From a6c66f9777c496c3a711035a68078d1cd1eb5a0f Mon Sep 17 00:00:00 2001 From: Abhigyan Patwari Date: Sat, 4 Apr 2026 07:13:59 +0530 Subject: [PATCH] fix(web): scope all backend calls to the active repo, not always the first MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The root cause of four related issues: `repoRef.current` was never set during initial server connection — only `switchRepo()` set it. Every backend call (queries, search, grep, readFile) passed `undefined` as the repo parameter, so the server's `resolveRepo()` fell back to `repos[0]`. Changes: - Expose `setCurrentRepo` from useAppState and call it in `handleServerConnect` - Use `result.repoInfo.name` (canonical server name) instead of path parsing - Parse `?project=` URL param during auto-connect, pass it to `connectToServer` - Update URL with `?project=` on repo switch so F5/bookmarks work - Pass `projectName` as `repo` in CodeReferencesPanel's `readFile` calls Closes #139, closes #634, closes #635, closes #636 Co-Authored-By: Claude Opus 4.6 (1M context) --- gitnexus-web/src/App.tsx | 86 +++++++++++-------- .../src/components/CodeReferencesPanel.tsx | 5 +- gitnexus-web/src/hooks/useAppState.tsx | 11 +++ 3 files changed, 67 insertions(+), 35 deletions(-) diff --git a/gitnexus-web/src/App.tsx b/gitnexus-web/src/App.tsx index 7916d3268..3c626949c 100644 --- a/gitnexus-web/src/App.tsx +++ b/gitnexus-web/src/App.tsx @@ -45,17 +45,21 @@ const AppContent = () => { availableRepos, setAvailableRepos, switchRepo, + setCurrentRepo, } = useAppState(); const graphCanvasRef = useRef(null); const handleServerConnect = useCallback( async (result: ConnectResult): Promise => { - // Extract project name from repoPath + // Use the canonical repo name from the server response so all subsequent + // backend calls (queries, search, grep, readFile) scope to this repo. + const repoName = result.repoInfo.name; const repoPath = result.repoInfo.repoPath ?? result.repoInfo.path; - const parts = (repoPath || '').split('/').filter((p) => p && !p.startsWith('.')); - const projectName = parts[parts.length - 1] || parts[0] || 'server-project'; + const projectName = + repoName || repoPath?.split('/').filter(Boolean).pop() || 'server-project'; setProjectName(projectName); + setCurrentRepo(projectName); // Build KnowledgeGraph from server data for visualization const graph = createKnowledgeGraph(); @@ -80,10 +84,18 @@ const AppContent = () => { console.warn('Failed to initialize agent:', err); } }, - [setViewMode, setGraph, setProjectName, initializeAgent, startEmbeddingsWithFallback], + [ + setViewMode, + setGraph, + setProjectName, + setCurrentRepo, + initializeAgent, + startEmbeddingsWithFallback, + ], ); - // Auto-connect when ?server query param is present (bookmarkable shortcut) + // Auto-connect when ?server query param is present (bookmarkable shortcut). + // Also reads ?project= to connect to a specific repo. const autoConnectRan = useRef(false); useEffect(() => { if (autoConnectRan.current) return; @@ -91,7 +103,10 @@ const AppContent = () => { if (!params.has('server')) return; autoConnectRan.current = true; - // Clean the URL so a refresh won't re-trigger + const serverUrl = params.get('server') || window.location.origin; + const projectParam = params.get('project') || undefined; + + // Clean the URL so a refresh won't re-trigger auto-connect const cleanUrl = window.location.pathname + window.location.hash; window.history.replaceState(null, '', cleanUrl); @@ -103,36 +118,39 @@ const AppContent = () => { }); setViewMode('loading'); - const serverUrl = params.get('server') || window.location.origin; - const baseUrl = normalizeServerUrl(serverUrl); - connectToServer(serverUrl, (phase, downloaded, total) => { - if (phase === 'validating') { - setProgress({ - phase: 'extracting', - percent: 5, - message: 'Connecting to server...', - detail: 'Validating server', - }); - } else if (phase === 'downloading') { - const pct = total ? Math.round((downloaded / total) * 90) + 5 : 50; - const mb = (downloaded / (1024 * 1024)).toFixed(1); - setProgress({ - phase: 'extracting', - percent: pct, - message: 'Downloading graph...', - detail: `${mb} MB downloaded`, - }); - } else if (phase === 'extracting') { - setProgress({ - phase: 'extracting', - percent: 97, - message: 'Processing...', - detail: 'Extracting file contents', - }); - } - }) + connectToServer( + serverUrl, + (phase, downloaded, total) => { + if (phase === 'validating') { + setProgress({ + phase: 'extracting', + percent: 5, + message: 'Connecting to server...', + detail: 'Validating server', + }); + } else if (phase === 'downloading') { + const pct = total ? Math.round((downloaded / total) * 90) + 5 : 50; + const mb = (downloaded / (1024 * 1024)).toFixed(1); + setProgress({ + phase: 'extracting', + percent: pct, + message: 'Downloading graph...', + detail: `${mb} MB downloaded`, + }); + } else if (phase === 'extracting') { + setProgress({ + phase: 'extracting', + percent: 97, + message: 'Processing...', + detail: 'Extracting file contents', + }); + } + }, + undefined, + projectParam, + ) .then(async (result) => { await handleServerConnect(result); setProgress(null); diff --git a/gitnexus-web/src/components/CodeReferencesPanel.tsx b/gitnexus-web/src/components/CodeReferencesPanel.tsx index 6357d15ad..dfa75a533 100644 --- a/gitnexus-web/src/components/CodeReferencesPanel.tsx +++ b/gitnexus-web/src/components/CodeReferencesPanel.tsx @@ -54,6 +54,7 @@ export const CodeReferencesPanel = ({ onFocusNode }: CodeReferencesPanelProps) = clearCodeReferences, setSelectedNode, codeReferenceFocus, + projectName, } = useAppState(); const nodeById = useMemo(() => { @@ -223,10 +224,11 @@ export const CodeReferencesPanel = ({ onFocusNode }: CodeReferencesPanelProps) = const isWholeFile = selectedIsFile || startLine === undefined; const options = isWholeFile - ? undefined + ? { repo: projectName } : { startLine: Math.max(0, startLine - CONTEXT_LINES), endLine: (endLine ?? startLine) + CONTEXT_LINES, + repo: projectName, }; readFile(selectedFilePath, options) @@ -251,6 +253,7 @@ export const CodeReferencesPanel = ({ onFocusNode }: CodeReferencesPanelProps) = selectedNode?.properties?.startLine, selectedNode?.properties?.endLine, selectedIsFile, + projectName, ]); // Scroll to the selected node's startLine after content loads diff --git a/gitnexus-web/src/hooks/useAppState.tsx b/gitnexus-web/src/hooks/useAppState.tsx index 94c414761..2fa5218ba 100644 --- a/gitnexus-web/src/hooks/useAppState.tsx +++ b/gitnexus-web/src/hooks/useAppState.tsx @@ -145,6 +145,7 @@ interface AppState { availableRepos: BackendRepo[]; setAvailableRepos: (repos: BackendRepo[]) => void; switchRepo: (repoName: string) => Promise; + setCurrentRepo: (repoName: string) => void; // Worker API (shared across app) runQuery: (cypher: string) => Promise; @@ -456,6 +457,10 @@ const AppStateProviderInner = ({ children }: { children: ReactNode }) => { // Backend client — direct HTTP calls (no Worker/Comlink) const repoRef = useRef(undefined); + const setCurrentRepo = useCallback((repoName: string) => { + repoRef.current = repoName; + }, []); + const runQuery = useCallback(async (cypher: string): Promise => { return backendRunQuery(cypher, repoRef.current); }, []); @@ -1077,6 +1082,11 @@ const AppStateProviderInner = ({ children }: { children: ReactNode }) => { setProjectName(pName); repoRef.current = pName; + // Update URL so F5 / bookmarks open the correct repo + const url = new URL(window.location.href); + url.searchParams.set('project', pName); + window.history.replaceState(null, '', url.toString()); + const newGraph = createKnowledgeGraph(); for (const node of result.nodes) newGraph.addNode(node); for (const rel of result.relationships) newGraph.addRelationship(rel); @@ -1219,6 +1229,7 @@ const AppStateProviderInner = ({ children }: { children: ReactNode }) => { availableRepos, setAvailableRepos, switchRepo, + setCurrentRepo, runQuery, isDatabaseReady, // Embedding state and methods