Skip to content

Clicking a link in agent chat output navigates the WebView away, destroying the chat page #43

Closed

Description

@JoshuaRowePhantom

Describe the bug

Clicking any hyperlink rendered in the agent chat output (e.g. a URL from a UriContent block, or a link inside a Markdown-rendered text response after #37) causes the underlying WebView to navigate to that URL, replacing the chat shell page with the link target. All chat content is lost and the output control goes blank.

Root cause

ControllableWebViewControl (which hosts the chat shell) subscribes to NavigationCompleted but does not handle NavigationStarted or NewWindowRequested. There is no guard that intercepts link-click navigations and redirects them.

When the user clicks a link in the page:

  1. WebView2 fires a NavigationStarted event for the link URL.
  2. No handler cancels or redirects it.
  3. The WebView navigates away from the chat shell — clearing the hostBridge, destroying the DOM, and losing all chat content (reproducing WebView reload/refresh wipes all chat content (DOM never repopulated) #39 on every link click).

ConfiguredWebView (used for the general-purpose browser tab) does handle NewWindowRequested via reflection, but ControllableWebViewControl has no equivalent and is a different control.

Expected behaviour

Clicking a link in the chat output should not navigate the WebView. Instead it should open the URL in the system default browser (or, if the link is an internal entity reference, dispatch to the host via the JS bridge). The chat shell must remain loaded and intact.

Suggested fix

In ControllableWebViewControl:

  1. Subscribe to NavigationStarted. When a navigation is triggered that is not the initial shell load (i.e. the target URI is not about:blank and HtmlShell is not being freshly assigned), cancel the navigation and open the URL externally:

    this.NavigationStarted += this.OnNavigationStarted;
    private void OnNavigationStarted(object? sender, NavigationStartedEventArgs e)
    {
        if (this.isShellLoaded && e.Uri is { } uri && uri.Scheme is "http" or "https")
        {
            e.Cancel = true;
            System.Diagnostics.Process.Start(new ProcessStartInfo(uri.ToString()) { UseShellExecute = true });
        }
    }
  2. Alternatively (or additionally), intercept link clicks in the shell JavaScript before they trigger navigation: attach a click listener on document that calls e.preventDefault() for anchor elements and posts a { type: "openUrl", url: "..." } message to the host, letting the host decide how to open it.

The JS-side approach is more portable and avoids dependence on WebView2-specific event API shape (the NavigationStarted event args type is not yet publicly typed in Avalonia.Controls.WebView). It also works for <form> submissions and programmatic window.location changes.

Affected files (expected)

  • Phantom.Workspaces.Agent.Gui/Assets/chat-output-shell.html — intercept clicks on <a> elements, post openUrl to host
  • Phantom.Workspaces.Agent.Gui/Controls/AgentChatOutputControl.axaml.cs — handle openUrl message from browser, open URL via Process.Start
  • Possibly Phantom.Workspaces.Gui.Styles/Controls/ControllableWebViewControl.cs — add NavigationStarted guard as defence-in-depth

Related

Activity

  1. JoshuaRowePhantom commented on Jun 26, 2026

    @JoshuaRowePhantom
    OwnerAuthor

    Fixed: link clicks now open a new Phantom.Workspaces browser tab via AgentViewModel.OpenUrlHandler. JS intercepts clicks and posts openUrl to host.

  2. added 2 commits that reference this issue on Jun 26, 2026
    ad9c7ae
    e6f8a9d
  3. JoshuaRowePhantom commented on Jun 30, 2026

    @JoshuaRowePhantom
    OwnerAuthor

    Verification passed

    Checked: Commits ad9c7ae and e6f8a9d on the \ eatures\ branch; files \chat-output-shell.html, \AgentChatOutputControl.axaml.cs, \AgentViewModel.cs, \OpenAgentSessionShortcutHandler.cs, and \AgentChatOutputControlTests.cs.

    Result: Implementation found, all verification criteria satisfied, tests present and green.

    • JS click interceptor in \chat-output-shell.html\ intercepts <a>\ clicks and posts \openUrl\ to host via \postToHost.
    • \AgentChatOutputControl\ handles \openUrl\ messages, fires \UrlNavigationRequested, and calls \AgentViewModel.OpenUrlHandler.
    • Workspace layer (\OpenAgentSessionShortcutHandler) sets \OpenUrlHandler\ to open a \WebViewModel\ browser tab.
    • Standalone Agent.Gui app sets \OpenUrlHandler\ to \Process.Start\ (external browser).
    • \AgentChatOutputControlTests\ covers: HTML anchor-click interceptor present, \openUrl\ message raises \UrlNavigationRequested, empty URL does not raise event. No skipped tests, no unresolved TODOs, no timing-dependent tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingverifiedIssue has been verified

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions