mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-21 21:31:12 +02:00
remove faulty undo/redo and replaced with back/forward buttons
This commit is contained in:
parent
4d2fc01f88
commit
d05344861a
3 changed files with 97 additions and 17 deletions
|
|
@ -382,6 +382,8 @@ function ChatInputWithMentions({
|
||||||
function App() {
|
function App() {
|
||||||
// File browser state (for Knowledge section)
|
// File browser state (for Knowledge section)
|
||||||
const [selectedPath, setSelectedPath] = useState<string | null>(null)
|
const [selectedPath, setSelectedPath] = useState<string | null>(null)
|
||||||
|
const [fileHistoryBack, setFileHistoryBack] = useState<string[]>([])
|
||||||
|
const [fileHistoryForward, setFileHistoryForward] = useState<string[]>([])
|
||||||
const [fileContent, setFileContent] = useState<string>('')
|
const [fileContent, setFileContent] = useState<string>('')
|
||||||
const [editorContent, setEditorContent] = useState<string>('')
|
const [editorContent, setEditorContent] = useState<string>('')
|
||||||
const [tree, setTree] = useState<TreeNode[]>([])
|
const [tree, setTree] = useState<TreeNode[]>([])
|
||||||
|
|
@ -1075,6 +1077,52 @@ function App() {
|
||||||
setIsGraphOpen(false)
|
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
|
// Handle image upload for the markdown editor
|
||||||
const handleImageUpload = useCallback(async (file: File): Promise<string | null> => {
|
const handleImageUpload = useCallback(async (file: File): Promise<string | null> => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -1128,7 +1176,7 @@ function App() {
|
||||||
|
|
||||||
const toggleExpand = (path: string, kind: 'file' | 'dir') => {
|
const toggleExpand = (path: string, kind: 'file' | 'dir') => {
|
||||||
if (kind === 'file') {
|
if (kind === 'file') {
|
||||||
setSelectedPath(path)
|
navigateToFile(path)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -1312,9 +1360,9 @@ function App() {
|
||||||
const openWikiLink = useCallback(async (wikiPath: string) => {
|
const openWikiLink = useCallback(async (wikiPath: string) => {
|
||||||
const resolvedPath = await ensureWikiFile(wikiPath)
|
const resolvedPath = await ensureWikiFile(wikiPath)
|
||||||
if (resolvedPath) {
|
if (resolvedPath) {
|
||||||
setSelectedPath(resolvedPath)
|
navigateToFile(resolvedPath)
|
||||||
}
|
}
|
||||||
}, [ensureWikiFile, setSelectedPath])
|
}, [ensureWikiFile, navigateToFile])
|
||||||
|
|
||||||
const wikiLinkConfig = React.useMemo(() => ({
|
const wikiLinkConfig = React.useMemo(() => ({
|
||||||
files: knowledgeFiles,
|
files: knowledgeFiles,
|
||||||
|
|
@ -1616,7 +1664,7 @@ function App() {
|
||||||
error={graphStatus === 'error' ? (graphError ?? 'Failed to build graph') : null}
|
error={graphStatus === 'error' ? (graphError ?? 'Failed to build graph') : null}
|
||||||
onSelectNode={(path) => {
|
onSelectNode={(path) => {
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setSelectedPath(path)
|
navigateToFile(path)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1629,6 +1677,10 @@ function App() {
|
||||||
placeholder="Start writing..."
|
placeholder="Start writing..."
|
||||||
wikiLinks={wikiLinkConfig}
|
wikiLinks={wikiLinkConfig}
|
||||||
onImageUpload={handleImageUpload}
|
onImageUpload={handleImageUpload}
|
||||||
|
onNavigateBack={navigateBack}
|
||||||
|
onNavigateForward={navigateForward}
|
||||||
|
canNavigateBack={canNavigateBack}
|
||||||
|
canNavigateForward={canNavigateForward}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ import {
|
||||||
MinusIcon,
|
MinusIcon,
|
||||||
LinkIcon,
|
LinkIcon,
|
||||||
CodeSquareIcon,
|
CodeSquareIcon,
|
||||||
Undo2Icon,
|
ChevronLeftIcon,
|
||||||
Redo2Icon,
|
ChevronRightIcon,
|
||||||
ExternalLinkIcon,
|
ExternalLinkIcon,
|
||||||
Trash2Icon,
|
Trash2Icon,
|
||||||
ImageIcon,
|
ImageIcon,
|
||||||
|
|
@ -33,9 +33,21 @@ interface EditorToolbarProps {
|
||||||
editor: Editor | null
|
editor: Editor | null
|
||||||
onSelectionHighlight?: (range: { from: number; to: number } | null) => void
|
onSelectionHighlight?: (range: { from: number; to: number } | null) => void
|
||||||
onImageUpload?: (file: File) => Promise<void> | void
|
onImageUpload?: (file: File) => Promise<void> | 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 [linkUrl, setLinkUrl] = useState('')
|
||||||
const [isLinkPopoverOpen, setIsLinkPopoverOpen] = useState(false)
|
const [isLinkPopoverOpen, setIsLinkPopoverOpen] = useState(false)
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
@ -105,24 +117,24 @@ export function EditorToolbar({ editor, onSelectionHighlight, onImageUpload }: E
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="editor-toolbar">
|
<div className="editor-toolbar">
|
||||||
{/* Undo / Redo */}
|
{/* Back / Forward Navigation */}
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon-sm"
|
size="icon-sm"
|
||||||
onClick={() => editor.chain().focus().undo().run()}
|
onClick={onNavigateBack}
|
||||||
disabled={!editor.can().undo()}
|
disabled={!canNavigateBack}
|
||||||
title="Undo (Ctrl+Z)"
|
title="Go back"
|
||||||
>
|
>
|
||||||
<Undo2Icon className="size-4" />
|
<ChevronLeftIcon className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon-sm"
|
size="icon-sm"
|
||||||
onClick={() => editor.chain().focus().redo().run()}
|
onClick={onNavigateForward}
|
||||||
disabled={!editor.can().redo()}
|
disabled={!canNavigateForward}
|
||||||
title="Redo (Ctrl+Shift+Z)"
|
title="Go forward"
|
||||||
>
|
>
|
||||||
<Redo2Icon className="size-4" />
|
<ChevronRightIcon className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<div className="separator" />
|
<div className="separator" />
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,10 @@ interface MarkdownEditorProps {
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
wikiLinks?: WikiLinkConfig
|
wikiLinks?: WikiLinkConfig
|
||||||
onImageUpload?: (file: File) => Promise<string | null>
|
onImageUpload?: (file: File) => Promise<string | null>
|
||||||
|
onNavigateBack?: () => void
|
||||||
|
onNavigateForward?: () => void
|
||||||
|
canNavigateBack?: boolean
|
||||||
|
canNavigateForward?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type WikiLinkMatch = {
|
type WikiLinkMatch = {
|
||||||
|
|
@ -78,6 +82,10 @@ export function MarkdownEditor({
|
||||||
placeholder = 'Start writing...',
|
placeholder = 'Start writing...',
|
||||||
wikiLinks,
|
wikiLinks,
|
||||||
onImageUpload,
|
onImageUpload,
|
||||||
|
onNavigateBack,
|
||||||
|
onNavigateForward,
|
||||||
|
canNavigateBack,
|
||||||
|
canNavigateForward,
|
||||||
}: MarkdownEditorProps) {
|
}: MarkdownEditorProps) {
|
||||||
const isInternalUpdate = useRef(false)
|
const isInternalUpdate = useRef(false)
|
||||||
const wrapperRef = useRef<HTMLDivElement>(null)
|
const wrapperRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
@ -318,7 +326,15 @@ export function MarkdownEditor({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tiptap-editor" onKeyDown={handleKeyDown}>
|
<div className="tiptap-editor" onKeyDown={handleKeyDown}>
|
||||||
<EditorToolbar editor={editor} onSelectionHighlight={setSelectionHighlight} onImageUpload={handleImageUploadWithPlaceholder} />
|
<EditorToolbar
|
||||||
|
editor={editor}
|
||||||
|
onSelectionHighlight={setSelectionHighlight}
|
||||||
|
onImageUpload={handleImageUploadWithPlaceholder}
|
||||||
|
onNavigateBack={onNavigateBack}
|
||||||
|
onNavigateForward={onNavigateForward}
|
||||||
|
canNavigateBack={canNavigateBack}
|
||||||
|
canNavigateForward={canNavigateForward}
|
||||||
|
/>
|
||||||
<div className="editor-content-wrapper" ref={wrapperRef} onScroll={handleScroll}>
|
<div className="editor-content-wrapper" ref={wrapperRef} onScroll={handleScroll}>
|
||||||
<EditorContent editor={editor} />
|
<EditorContent editor={editor} />
|
||||||
{wikiLinks ? (
|
{wikiLinks ? (
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue