Repository navigation
Clicking a link in agent chat output navigates the WebView away, destroying the chat page #43
Description
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:
- WebView2 fires a
NavigationStartedevent for the link URL. - No handler cancels or redirects it.
- 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:
-
Subscribe to
NavigationStarted. When a navigation is triggered that is not the initial shell load (i.e. the target URI is notabout:blankandHtmlShellis 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 }); } }
-
Alternatively (or additionally), intercept link clicks in the shell JavaScript before they trigger navigation: attach a
clicklistener ondocumentthat callse.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, postopenUrlto hostPhantom.Workspaces.Agent.Gui/Controls/AgentChatOutputControl.axaml.cs— handleopenUrlmessage from browser, open URL viaProcess.Start- Possibly
Phantom.Workspaces.Gui.Styles/Controls/ControllableWebViewControl.cs— addNavigationStartedguard as defence-in-depth
Related
- WebView reload/refresh wipes all chat content (DOM never repopulated) #39 — WebView reload wipes content (link navigation triggers the same symptom)
Activity
JoshuaRowePhantom commentedon Jun 26, 2026
Fixed: link clicks now open a new Phantom.Workspaces browser tab via AgentViewModel.OpenUrlHandler. JS intercepts clicks and posts openUrl to host.
Fix #43: link clicks open a Phantom.Workspaces browser tab
Fix #43: use WebViewModel for link navigation instead of placeholder …
JoshuaRowePhantom commentedon Jun 30, 2026
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.