Open chat file links at the referenced line (#2309)

* fix(app): open chat file links at referenced lines

Pass chat file locations through the editable CodeMirror path so opening a reference selects and scrolls to its requested line. Cover the real assistant-link flow with a browser regression.

* fix(app): repeat chat file navigation

Track file navigation separately from stable tab identity so reopening the same path and line recenters both editable and read-only panes. Extend the browser regression to cover moving away and clicking the same link again.

* test(app): stabilize repeated file link navigation
This commit is contained in:
Mohamed Boudra
2026-07-21 21:34:20 +02:00
committed by GitHub
parent 7d10791bad
commit cb6d2f1459
8 changed files with 131 additions and 3 deletions
@@ -23,6 +23,7 @@ export interface BuildWorkspacePaneContentModelInput {
tab: WorkspaceTabDescriptor;
normalizedServerId: string;
normalizedWorkspaceId: string;
fileNavigationRevision?: number;
onOpenTab: (target: WorkspaceTabDescriptor["target"]) => void;
onCloseCurrentTab: () => void;
onRetargetCurrentTab: (target: WorkspaceTabDescriptor["target"]) => void;
@@ -34,6 +35,7 @@ export function buildWorkspacePaneContentModel({
tab,
normalizedServerId,
normalizedWorkspaceId,
fileNavigationRevision,
onOpenTab,
onCloseCurrentTab,
onRetargetCurrentTab,
@@ -51,6 +53,7 @@ export function buildWorkspacePaneContentModel({
workspaceId: normalizedWorkspaceId,
tabId: tab.tabId,
target: tab.target,
fileNavigationRevision,
openTab: onOpenTab,
closeCurrentTab: onCloseCurrentTab,
retargetCurrentTab: onRetargetCurrentTab,
@@ -85,6 +88,7 @@ export function WorkspacePaneContent({
workspaceId: paneContextValue.workspaceId,
tabId: paneContextValue.tabId,
target: paneContextValue.target,
fileNavigationRevision: paneContextValue.fileNavigationRevision,
openTab,
closeCurrentTab,
retargetCurrentTab,
@@ -97,6 +101,7 @@ export function WorkspacePaneContent({
openImportSheet,
openTab,
paneContextValue.serverId,
paneContextValue.fileNavigationRevision,
paneContextValue.tabId,
paneContextValue.target,
paneContextValue.workspaceId,
@@ -1773,6 +1773,17 @@ function WorkspaceScreenContent({
const openWorkspaceChildTabFocused = useWorkspaceLayoutStore(
(state) => state.openChildTabFocused,
);
// File targets stay identity-stable so the same path reuses its tab. Keep navigation
// requests separate so clicking an unchanged path:line can still recenter the pane.
const [fileNavigationRevisionByTabId, setFileNavigationRevisionByTabId] = useState<
Record<string, number>
>({});
const requestFileNavigation = useCallback((tabId: string) => {
setFileNavigationRevisionByTabId((current) => ({
...current,
[tabId]: (current[tabId] ?? 0) + 1,
}));
}, []);
const focusWorkspacePane = useWorkspaceLayoutStore((state) => state.focusPane);
const hasHydratedWorkspaces = useSessionStore(
(state) => state.sessions[normalizedServerId]?.hasHydratedWorkspaces ?? false,
@@ -2333,6 +2344,7 @@ function WorkspaceScreenContent({
? openWorkspaceChildTabFocused(persistenceKey, target, options.parentTabId)
: openWorkspaceTabFocused(persistenceKey, target);
if (tabId) {
requestFileNavigation(tabId);
navigateToTabId(tabId);
}
},
@@ -2342,6 +2354,7 @@ function WorkspaceScreenContent({
openWorkspaceChildTabFocused,
openWorkspaceTabFocused,
persistenceKey,
requestFileNavigation,
showMobileAgent,
],
);
@@ -2381,6 +2394,7 @@ function WorkspaceScreenContent({
? openWorkspaceChildTabFocused(persistenceKey, target, input.parentTabId)
: openWorkspaceTabFocused(persistenceKey, target);
if (tabId) {
requestFileNavigation(tabId);
navigateToTabId(tabId);
}
},
@@ -2392,6 +2406,7 @@ function WorkspaceScreenContent({
openWorkspaceChildTabFocused,
openWorkspaceTabFocused,
persistenceKey,
requestFileNavigation,
splitWorkspacePaneEmpty,
uiTabs,
workspaceLayout,
@@ -3211,6 +3226,7 @@ function WorkspaceScreenContent({
tab: input.tab,
normalizedServerId,
normalizedWorkspaceId,
fileNavigationRevision: fileNavigationRevisionByTabId[input.tab.tabId] ?? 0,
onOpenTab: (target) => {
if (!persistenceKey) {
return;
@@ -3245,6 +3261,7 @@ function WorkspaceScreenContent({
[
handleCloseTabById,
focusWorkspacePane,
fileNavigationRevisionByTabId,
handleOpenWorkspaceFileFromPane,
navigateToTabId,
normalizedServerId,