mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-06-03 19:25:19 +02:00
elevate folder navigation to app view state
This commit is contained in:
parent
373d1ee92b
commit
78c5ad2e6f
2 changed files with 22 additions and 9 deletions
|
|
@ -587,7 +587,7 @@ type ViewState =
|
||||||
| { type: 'live-notes' }
|
| { type: 'live-notes' }
|
||||||
| { type: 'email' }
|
| { type: 'email' }
|
||||||
| { type: 'workspace'; path?: string }
|
| { type: 'workspace'; path?: string }
|
||||||
| { type: 'knowledge-view' }
|
| { type: 'knowledge-view'; folderPath?: string }
|
||||||
| { type: 'chat-history' }
|
| { type: 'chat-history' }
|
||||||
| { type: 'home' }
|
| { 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 === 'file' && b.type === 'file') return a.path === b.path
|
||||||
if (a.type === 'task' && b.type === 'task') return a.name === b.name
|
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 === '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
|
return true // both graph
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -644,8 +645,10 @@ function parseDeepLink(input: string): ViewState | null {
|
||||||
const path = params.get('path')
|
const path = params.get('path')
|
||||||
return { type: 'workspace', path: path ?? undefined }
|
return { type: 'workspace', path: path ?? undefined }
|
||||||
}
|
}
|
||||||
case 'knowledge-view':
|
case 'knowledge-view': {
|
||||||
return { type: 'knowledge-view' }
|
const folderPath = params.get('folderPath')
|
||||||
|
return { type: 'knowledge-view', folderPath: folderPath ?? undefined }
|
||||||
|
}
|
||||||
case 'chat-history':
|
case 'chat-history':
|
||||||
return { type: 'chat-history' }
|
return { type: 'chat-history' }
|
||||||
case 'home':
|
case 'home':
|
||||||
|
|
@ -762,6 +765,9 @@ function App() {
|
||||||
const [isWorkspaceOpen, setIsWorkspaceOpen] = useState(false)
|
const [isWorkspaceOpen, setIsWorkspaceOpen] = useState(false)
|
||||||
const [workspaceInitialPath, setWorkspaceInitialPath] = useState<string | null>(null)
|
const [workspaceInitialPath, setWorkspaceInitialPath] = useState<string | null>(null)
|
||||||
const [isKnowledgeViewOpen, setIsKnowledgeViewOpen] = useState(false)
|
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<string | null>(null)
|
||||||
const [isChatHistoryOpen, setIsChatHistoryOpen] = useState(false)
|
const [isChatHistoryOpen, setIsChatHistoryOpen] = useState(false)
|
||||||
// Default landing view: Home in the middle with the chat docked on the right.
|
// Default landing view: Home in the middle with the chat docked on the right.
|
||||||
const [isHomeOpen, setIsHomeOpen] = useState(true)
|
const [isHomeOpen, setIsHomeOpen] = useState(true)
|
||||||
|
|
@ -3463,13 +3469,13 @@ function App() {
|
||||||
if (isLiveNotesOpen) return { type: 'live-notes' }
|
if (isLiveNotesOpen) return { type: 'live-notes' }
|
||||||
if (isSuggestedTopicsOpen) return { type: 'suggested-topics' }
|
if (isSuggestedTopicsOpen) return { type: 'suggested-topics' }
|
||||||
if (isWorkspaceOpen) return { type: 'workspace', path: workspaceInitialPath ?? undefined }
|
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 (isChatHistoryOpen) return { type: 'chat-history' }
|
||||||
if (isHomeOpen) return { type: 'home' }
|
if (isHomeOpen) return { type: 'home' }
|
||||||
if (selectedPath) return { type: 'file', path: selectedPath }
|
if (selectedPath) return { type: 'file', path: selectedPath }
|
||||||
if (isGraphOpen) return { type: 'graph' }
|
if (isGraphOpen) return { type: 'graph' }
|
||||||
return { type: 'chat', runId }
|
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 appendUnique = useCallback((stack: ViewState[], entry: ViewState) => {
|
||||||
const last = stack[stack.length - 1]
|
const last = stack[stack.length - 1]
|
||||||
|
|
@ -3809,6 +3815,7 @@ function App() {
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
setIsKnowledgeViewOpen(true)
|
setIsKnowledgeViewOpen(true)
|
||||||
|
setKnowledgeViewFolderPath(view.folderPath ?? null)
|
||||||
setIsChatHistoryOpen(false)
|
setIsChatHistoryOpen(false)
|
||||||
setIsHomeOpen(false)
|
setIsHomeOpen(false)
|
||||||
ensureKnowledgeViewFileTab()
|
ensureKnowledgeViewFileTab()
|
||||||
|
|
@ -5553,6 +5560,8 @@ function App() {
|
||||||
revealInFileManager: knowledgeActions.revealInFileManager,
|
revealInFileManager: knowledgeActions.revealInFileManager,
|
||||||
onOpenInNewTab: knowledgeActions.onOpenInNewTab,
|
onOpenInNewTab: knowledgeActions.onOpenInNewTab,
|
||||||
}}
|
}}
|
||||||
|
folderPath={knowledgeViewFolderPath}
|
||||||
|
onNavigateFolder={(path) => { void navigateToView({ type: 'knowledge-view', folderPath: path ?? undefined }) }}
|
||||||
onOpenNote={(path) => navigateToFile(path)}
|
onOpenNote={(path) => navigateToFile(path)}
|
||||||
onOpenGraph={() => knowledgeActions.openGraph()}
|
onOpenGraph={() => knowledgeActions.openGraph()}
|
||||||
onOpenSearch={() => { setSearchDefaultScope('knowledge'); setIsSearchOpen(true) }}
|
onOpenSearch={() => { setSearchDefaultScope('knowledge'); setIsSearchOpen(true) }}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,10 @@ export type KnowledgeViewActions = {
|
||||||
type KnowledgeViewProps = {
|
type KnowledgeViewProps = {
|
||||||
tree: TreeNode[]
|
tree: TreeNode[]
|
||||||
actions: KnowledgeViewActions
|
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
|
onOpenNote: (path: string) => void
|
||||||
onOpenGraph: () => void
|
onOpenGraph: () => void
|
||||||
onOpenSearch: () => void
|
onOpenSearch: () => void
|
||||||
|
|
@ -141,14 +145,14 @@ function displayName(node: TreeNode): string {
|
||||||
export function KnowledgeView({
|
export function KnowledgeView({
|
||||||
tree,
|
tree,
|
||||||
actions,
|
actions,
|
||||||
|
folderPath,
|
||||||
|
onNavigateFolder,
|
||||||
onOpenNote,
|
onOpenNote,
|
||||||
onOpenGraph,
|
onOpenGraph,
|
||||||
onOpenSearch,
|
onOpenSearch,
|
||||||
onOpenBases,
|
onOpenBases,
|
||||||
onVoiceNoteCreated,
|
onVoiceNoteCreated,
|
||||||
}: KnowledgeViewProps) {
|
}: KnowledgeViewProps) {
|
||||||
// null = root (folder overview); otherwise the path of the folder being browsed.
|
|
||||||
const [folderPath, setFolderPath] = useState<string | null>(null)
|
|
||||||
const [renameTarget, setRenameTarget] = useState<string | null>(null)
|
const [renameTarget, setRenameTarget] = useState<string | null>(null)
|
||||||
|
|
||||||
const topLevel = useMemo(
|
const topLevel = useMemo(
|
||||||
|
|
@ -170,7 +174,7 @@ export function KnowledgeView({
|
||||||
[topLevel],
|
[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
|
// When the open folder no longer exists (deleted/renamed externally), fall
|
||||||
// back to the root overview rather than holding a dangling drill-down.
|
// back to the root overview rather than holding a dangling drill-down.
|
||||||
|
|
@ -210,7 +214,7 @@ export function KnowledgeView({
|
||||||
renameTarget={renameTarget}
|
renameTarget={renameTarget}
|
||||||
onRequestRename={setRenameTarget}
|
onRequestRename={setRenameTarget}
|
||||||
onClearRename={() => setRenameTarget(null)}
|
onClearRename={() => setRenameTarget(null)}
|
||||||
onNavigate={setFolderPath}
|
onNavigate={onNavigateFolder}
|
||||||
onOpenFolder={openFolder}
|
onOpenFolder={openFolder}
|
||||||
onOpenNote={onOpenNote}
|
onOpenNote={onOpenNote}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue