diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx index 884e4e95..d1a6f1fe 100644 --- a/apps/x/apps/renderer/src/App.tsx +++ b/apps/x/apps/renderer/src/App.tsx @@ -382,6 +382,8 @@ function ChatInputWithMentions({ function App() { // File browser state (for Knowledge section) const [selectedPath, setSelectedPath] = useState(null) + const [fileHistoryBack, setFileHistoryBack] = useState([]) + const [fileHistoryForward, setFileHistoryForward] = useState([]) const [fileContent, setFileContent] = useState('') const [editorContent, setEditorContent] = useState('') const [tree, setTree] = useState([]) @@ -1075,6 +1077,52 @@ function App() { setIsGraphOpen(false) }, []) + // File navigation with history tracking + const navigateToFile = useCallback((path: string | null) => { + if (path === selectedPath) return + + // Push current path to back history (if we have one) + if (selectedPath) { + setFileHistoryBack(prev => [...prev, selectedPath]) + } + // Clear forward history when navigating to a new file + setFileHistoryForward([]) + setSelectedPath(path) + }, [selectedPath]) + + const navigateBack = useCallback(() => { + if (fileHistoryBack.length === 0) return + + const newBack = [...fileHistoryBack] + const previousPath = newBack.pop()! + + // Push current path to forward history + if (selectedPath) { + setFileHistoryForward(prev => [...prev, selectedPath]) + } + + setFileHistoryBack(newBack) + setSelectedPath(previousPath) + }, [fileHistoryBack, selectedPath]) + + const navigateForward = useCallback(() => { + if (fileHistoryForward.length === 0) return + + const newForward = [...fileHistoryForward] + const nextPath = newForward.pop()! + + // Push current path to back history + if (selectedPath) { + setFileHistoryBack(prev => [...prev, selectedPath]) + } + + setFileHistoryForward(newForward) + setSelectedPath(nextPath) + }, [fileHistoryForward, selectedPath]) + + const canNavigateBack = fileHistoryBack.length > 0 + const canNavigateForward = fileHistoryForward.length > 0 + // Handle image upload for the markdown editor const handleImageUpload = useCallback(async (file: File): Promise => { try { @@ -1128,7 +1176,7 @@ function App() { const toggleExpand = (path: string, kind: 'file' | 'dir') => { if (kind === 'file') { - setSelectedPath(path) + navigateToFile(path) setIsGraphOpen(false) return } @@ -1312,9 +1360,9 @@ function App() { const openWikiLink = useCallback(async (wikiPath: string) => { const resolvedPath = await ensureWikiFile(wikiPath) if (resolvedPath) { - setSelectedPath(resolvedPath) + navigateToFile(resolvedPath) } - }, [ensureWikiFile, setSelectedPath]) + }, [ensureWikiFile, navigateToFile]) const wikiLinkConfig = React.useMemo(() => ({ files: knowledgeFiles, @@ -1616,7 +1664,7 @@ function App() { error={graphStatus === 'error' ? (graphError ?? 'Failed to build graph') : null} onSelectNode={(path) => { setIsGraphOpen(false) - setSelectedPath(path) + navigateToFile(path) }} /> @@ -1629,6 +1677,10 @@ function App() { placeholder="Start writing..." wikiLinks={wikiLinkConfig} onImageUpload={handleImageUpload} + onNavigateBack={navigateBack} + onNavigateForward={navigateForward} + canNavigateBack={canNavigateBack} + canNavigateForward={canNavigateForward} /> ) : ( diff --git a/apps/x/apps/renderer/src/components/editor-toolbar.tsx b/apps/x/apps/renderer/src/components/editor-toolbar.tsx index 48b16156..95853e5c 100644 --- a/apps/x/apps/renderer/src/components/editor-toolbar.tsx +++ b/apps/x/apps/renderer/src/components/editor-toolbar.tsx @@ -22,8 +22,8 @@ import { MinusIcon, LinkIcon, CodeSquareIcon, - Undo2Icon, - Redo2Icon, + ChevronLeftIcon, + ChevronRightIcon, ExternalLinkIcon, Trash2Icon, ImageIcon, @@ -33,9 +33,21 @@ interface EditorToolbarProps { editor: Editor | null onSelectionHighlight?: (range: { from: number; to: number } | null) => void onImageUpload?: (file: File) => Promise | void + onNavigateBack?: () => void + onNavigateForward?: () => void + canNavigateBack?: boolean + canNavigateForward?: boolean } -export function EditorToolbar({ editor, onSelectionHighlight, onImageUpload }: EditorToolbarProps) { +export function EditorToolbar({ + editor, + onSelectionHighlight, + onImageUpload, + onNavigateBack, + onNavigateForward, + canNavigateBack, + canNavigateForward, +}: EditorToolbarProps) { const [linkUrl, setLinkUrl] = useState('') const [isLinkPopoverOpen, setIsLinkPopoverOpen] = useState(false) const fileInputRef = useRef(null) @@ -105,24 +117,24 @@ export function EditorToolbar({ editor, onSelectionHighlight, onImageUpload }: E return (
- {/* Undo / Redo */} + {/* Back / Forward Navigation */}
diff --git a/apps/x/apps/renderer/src/components/markdown-editor.tsx b/apps/x/apps/renderer/src/components/markdown-editor.tsx index 1bda92a9..a65f262a 100644 --- a/apps/x/apps/renderer/src/components/markdown-editor.tsx +++ b/apps/x/apps/renderer/src/components/markdown-editor.tsx @@ -30,6 +30,10 @@ interface MarkdownEditorProps { placeholder?: string wikiLinks?: WikiLinkConfig onImageUpload?: (file: File) => Promise + onNavigateBack?: () => void + onNavigateForward?: () => void + canNavigateBack?: boolean + canNavigateForward?: boolean } type WikiLinkMatch = { @@ -78,6 +82,10 @@ export function MarkdownEditor({ placeholder = 'Start writing...', wikiLinks, onImageUpload, + onNavigateBack, + onNavigateForward, + canNavigateBack, + canNavigateForward, }: MarkdownEditorProps) { const isInternalUpdate = useRef(false) const wrapperRef = useRef(null) @@ -318,7 +326,15 @@ export function MarkdownEditor({ return (
- +
{wikiLinks ? (