diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx index 690c6790..3c653b1b 100644 --- a/apps/x/apps/renderer/src/App.tsx +++ b/apps/x/apps/renderer/src/App.tsx @@ -587,7 +587,7 @@ type ViewState = | { type: 'live-notes' } | { type: 'email' } | { type: 'workspace'; path?: string } - | { type: 'knowledge-view' } + | { type: 'knowledge-view'; folderPath?: string } | { type: 'chat-history' } | { type: 'home' } @@ -597,6 +597,7 @@ function viewStatesEqual(a: ViewState, b: ViewState): boolean { if (a.type === 'file' && b.type === 'file') return a.path === b.path if (a.type === 'task' && b.type === 'task') return a.name === b.name if (a.type === 'workspace' && b.type === 'workspace') return (a.path ?? '') === (b.path ?? '') + if (a.type === 'knowledge-view' && b.type === 'knowledge-view') return (a.folderPath ?? '') === (b.folderPath ?? '') return true // both graph } @@ -644,8 +645,10 @@ function parseDeepLink(input: string): ViewState | null { const path = params.get('path') return { type: 'workspace', path: path ?? undefined } } - case 'knowledge-view': - return { type: 'knowledge-view' } + case 'knowledge-view': { + const folderPath = params.get('folderPath') + return { type: 'knowledge-view', folderPath: folderPath ?? undefined } + } case 'chat-history': return { type: 'chat-history' } case 'home': @@ -762,6 +765,9 @@ function App() { const [isWorkspaceOpen, setIsWorkspaceOpen] = useState(false) const [workspaceInitialPath, setWorkspaceInitialPath] = useState(null) const [isKnowledgeViewOpen, setIsKnowledgeViewOpen] = useState(false) + // Folder being browsed inside the knowledge view (null = root overview). + // Lives in ViewState so folder drill-down participates in back/forward history. + const [knowledgeViewFolderPath, setKnowledgeViewFolderPath] = useState(null) const [isChatHistoryOpen, setIsChatHistoryOpen] = useState(false) // Default landing view: Home in the middle with the chat docked on the right. const [isHomeOpen, setIsHomeOpen] = useState(true) @@ -3463,13 +3469,13 @@ function App() { if (isLiveNotesOpen) return { type: 'live-notes' } if (isSuggestedTopicsOpen) return { type: 'suggested-topics' } if (isWorkspaceOpen) return { type: 'workspace', path: workspaceInitialPath ?? undefined } - if (isKnowledgeViewOpen) return { type: 'knowledge-view' } + if (isKnowledgeViewOpen) return { type: 'knowledge-view', folderPath: knowledgeViewFolderPath ?? undefined } if (isChatHistoryOpen) return { type: 'chat-history' } if (isHomeOpen) return { type: 'home' } if (selectedPath) return { type: 'file', path: selectedPath } if (isGraphOpen) return { type: 'graph' } return { type: 'chat', runId } - }, [selectedBackgroundTask, isEmailOpen, isMeetingsOpen, isLiveNotesOpen, isBgTasksOpen, isSuggestedTopicsOpen, selectedPath, isGraphOpen, isWorkspaceOpen, isKnowledgeViewOpen, isChatHistoryOpen, isHomeOpen, workspaceInitialPath, runId]) + }, [selectedBackgroundTask, isEmailOpen, isMeetingsOpen, isLiveNotesOpen, isBgTasksOpen, isSuggestedTopicsOpen, selectedPath, isGraphOpen, isWorkspaceOpen, isKnowledgeViewOpen, knowledgeViewFolderPath, isChatHistoryOpen, isHomeOpen, workspaceInitialPath, runId]) const appendUnique = useCallback((stack: ViewState[], entry: ViewState) => { const last = stack[stack.length - 1] @@ -3809,6 +3815,7 @@ function App() { setIsEmailOpen(false) setIsWorkspaceOpen(false) setIsKnowledgeViewOpen(true) + setKnowledgeViewFolderPath(view.folderPath ?? null) setIsChatHistoryOpen(false) setIsHomeOpen(false) ensureKnowledgeViewFileTab() @@ -5553,6 +5560,8 @@ function App() { revealInFileManager: knowledgeActions.revealInFileManager, onOpenInNewTab: knowledgeActions.onOpenInNewTab, }} + folderPath={knowledgeViewFolderPath} + onNavigateFolder={(path) => { void navigateToView({ type: 'knowledge-view', folderPath: path ?? undefined }) }} onOpenNote={(path) => navigateToFile(path)} onOpenGraph={() => knowledgeActions.openGraph()} onOpenSearch={() => { setSearchDefaultScope('knowledge'); setIsSearchOpen(true) }} diff --git a/apps/x/apps/renderer/src/components/knowledge-view.tsx b/apps/x/apps/renderer/src/components/knowledge-view.tsx index c7674fb4..e7ebe780 100644 --- a/apps/x/apps/renderer/src/components/knowledge-view.tsx +++ b/apps/x/apps/renderer/src/components/knowledge-view.tsx @@ -49,6 +49,10 @@ export type KnowledgeViewActions = { type KnowledgeViewProps = { tree: TreeNode[] actions: KnowledgeViewActions + // Folder currently being browsed (null = root overview). Controlled by the + // app so drill-down participates in the global back/forward history. + folderPath: string | null + onNavigateFolder: (path: string | null) => void onOpenNote: (path: string) => void onOpenGraph: () => void onOpenSearch: () => void @@ -141,14 +145,14 @@ function displayName(node: TreeNode): string { export function KnowledgeView({ tree, actions, + folderPath, + onNavigateFolder, onOpenNote, onOpenGraph, onOpenSearch, onOpenBases, onVoiceNoteCreated, }: KnowledgeViewProps) { - // null = root (folder overview); otherwise the path of the folder being browsed. - const [folderPath, setFolderPath] = useState(null) const [renameTarget, setRenameTarget] = useState(null) const topLevel = useMemo( @@ -170,7 +174,7 @@ export function KnowledgeView({ [topLevel], ) - const openFolder = useCallback((path: string) => setFolderPath(path), []) + const openFolder = useCallback((path: string) => onNavigateFolder(path), [onNavigateFolder]) // When the open folder no longer exists (deleted/renamed externally), fall // back to the root overview rather than holding a dangling drill-down. @@ -210,7 +214,7 @@ export function KnowledgeView({ renameTarget={renameTarget} onRequestRename={setRenameTarget} onClearRename={() => setRenameTarget(null)} - onNavigate={setFolderPath} + onNavigate={onNavigateFolder} onOpenFolder={openFolder} onOpenNote={onOpenNote} />