mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-12 21:02:17 +02:00
added chat history page
This commit is contained in:
parent
e678dd71a0
commit
f8100d353d
3 changed files with 388 additions and 103 deletions
|
|
@ -27,6 +27,7 @@ import { BgTasksView } from '@/components/bg-tasks-view';
|
||||||
import { EmailView } from '@/components/email-view';
|
import { EmailView } from '@/components/email-view';
|
||||||
import { WorkspaceView } from '@/components/workspace-view';
|
import { WorkspaceView } from '@/components/workspace-view';
|
||||||
import { KnowledgeView } from '@/components/knowledge-view';
|
import { KnowledgeView } from '@/components/knowledge-view';
|
||||||
|
import { ChatHistoryView } from '@/components/chat-history-view';
|
||||||
import { MeetingsView } from '@/components/meetings-view';
|
import { MeetingsView } from '@/components/meetings-view';
|
||||||
import { SidebarSectionProvider } from '@/contexts/sidebar-context';
|
import { SidebarSectionProvider } from '@/contexts/sidebar-context';
|
||||||
import {
|
import {
|
||||||
|
|
@ -187,6 +188,7 @@ const EMAIL_TAB_PATH = '__rowboat_email__'
|
||||||
const WORKSPACE_TAB_PATH = '__rowboat_workspace__'
|
const WORKSPACE_TAB_PATH = '__rowboat_workspace__'
|
||||||
const WORKSPACE_ROOT = 'knowledge/Workspace'
|
const WORKSPACE_ROOT = 'knowledge/Workspace'
|
||||||
const KNOWLEDGE_VIEW_TAB_PATH = '__rowboat_knowledge_view__'
|
const KNOWLEDGE_VIEW_TAB_PATH = '__rowboat_knowledge_view__'
|
||||||
|
const CHAT_HISTORY_TAB_PATH = '__rowboat_chat_history__'
|
||||||
const BASES_DEFAULT_TAB_PATH = '__rowboat_bases_default__'
|
const BASES_DEFAULT_TAB_PATH = '__rowboat_bases_default__'
|
||||||
|
|
||||||
const clampNumber = (value: number, min: number, max: number) =>
|
const clampNumber = (value: number, min: number, max: number) =>
|
||||||
|
|
@ -322,6 +324,7 @@ const isBgTasksTabPath = (path: string) => path === BG_TASKS_TAB_PATH
|
||||||
const isEmailTabPath = (path: string) => path === EMAIL_TAB_PATH
|
const isEmailTabPath = (path: string) => path === EMAIL_TAB_PATH
|
||||||
const isWorkspaceTabPath = (path: string) => path === WORKSPACE_TAB_PATH
|
const isWorkspaceTabPath = (path: string) => path === WORKSPACE_TAB_PATH
|
||||||
const isKnowledgeViewTabPath = (path: string) => path === KNOWLEDGE_VIEW_TAB_PATH
|
const isKnowledgeViewTabPath = (path: string) => path === KNOWLEDGE_VIEW_TAB_PATH
|
||||||
|
const isChatHistoryTabPath = (path: string) => path === CHAT_HISTORY_TAB_PATH
|
||||||
const isBaseFilePath = (path: string) => path.endsWith('.base') || path === BASES_DEFAULT_TAB_PATH
|
const isBaseFilePath = (path: string) => path.endsWith('.base') || path === BASES_DEFAULT_TAB_PATH
|
||||||
|
|
||||||
const getSuggestedTopicTargetFolder = (category?: string) => {
|
const getSuggestedTopicTargetFolder = (category?: string) => {
|
||||||
|
|
@ -574,6 +577,7 @@ type ViewState =
|
||||||
| { type: 'email' }
|
| { type: 'email' }
|
||||||
| { type: 'workspace'; path?: string }
|
| { type: 'workspace'; path?: string }
|
||||||
| { type: 'knowledge-view' }
|
| { type: 'knowledge-view' }
|
||||||
|
| { type: 'chat-history' }
|
||||||
|
|
||||||
function viewStatesEqual(a: ViewState, b: ViewState): boolean {
|
function viewStatesEqual(a: ViewState, b: ViewState): boolean {
|
||||||
if (a.type !== b.type) return false
|
if (a.type !== b.type) return false
|
||||||
|
|
@ -630,6 +634,8 @@ function parseDeepLink(input: string): ViewState | null {
|
||||||
}
|
}
|
||||||
case 'knowledge-view':
|
case 'knowledge-view':
|
||||||
return { type: 'knowledge-view' }
|
return { type: 'knowledge-view' }
|
||||||
|
case 'chat-history':
|
||||||
|
return { type: 'chat-history' }
|
||||||
default:
|
default:
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
@ -768,6 +774,7 @@ 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)
|
||||||
|
const [isChatHistoryOpen, setIsChatHistoryOpen] = useState(false)
|
||||||
const [expandedFrom, setExpandedFrom] = useState<{
|
const [expandedFrom, setExpandedFrom] = useState<{
|
||||||
path: string | null
|
path: string | null
|
||||||
graph: boolean
|
graph: boolean
|
||||||
|
|
@ -1106,6 +1113,7 @@ function App() {
|
||||||
if (isEmailTabPath(tab.path)) return 'Email'
|
if (isEmailTabPath(tab.path)) return 'Email'
|
||||||
if (isWorkspaceTabPath(tab.path)) return 'Workspace'
|
if (isWorkspaceTabPath(tab.path)) return 'Workspace'
|
||||||
if (isKnowledgeViewTabPath(tab.path)) return 'Knowledge'
|
if (isKnowledgeViewTabPath(tab.path)) return 'Knowledge'
|
||||||
|
if (isChatHistoryTabPath(tab.path)) return 'Chat history'
|
||||||
if (tab.path === BASES_DEFAULT_TAB_PATH) return 'Bases'
|
if (tab.path === BASES_DEFAULT_TAB_PATH) return 'Bases'
|
||||||
if (tab.path.endsWith('.base')) return tab.path.split('/').pop()?.replace(/\.base$/i, '') || 'Base'
|
if (tab.path.endsWith('.base')) return tab.path.split('/').pop()?.replace(/\.base$/i, '') || 'Base'
|
||||||
return tab.path.split('/').pop()?.replace(/\.md$/i, '') || tab.path
|
return tab.path.split('/').pop()?.replace(/\.md$/i, '') || tab.path
|
||||||
|
|
@ -2818,7 +2826,7 @@ function App() {
|
||||||
setActiveFileTabId(existingTab.id)
|
setActiveFileTabId(existingTab.id)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
setSelectedPath(path)
|
setSelectedPath(path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -2827,7 +2835,7 @@ function App() {
|
||||||
setActiveFileTabId(id)
|
setActiveFileTabId(id)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
setSelectedPath(path)
|
setSelectedPath(path)
|
||||||
}, [fileTabs, dismissBrowserOverlay])
|
}, [fileTabs, dismissBrowserOverlay])
|
||||||
|
|
||||||
|
|
@ -2846,14 +2854,14 @@ function App() {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsGraphOpen(true)
|
setIsGraphOpen(true)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (isSuggestedTopicsTabPath(tab.path)) {
|
if (isSuggestedTopicsTabPath(tab.path)) {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsSuggestedTopicsOpen(true)
|
setIsSuggestedTopicsOpen(true)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (isLiveNotesTabPath(tab.path)) {
|
if (isLiveNotesTabPath(tab.path)) {
|
||||||
|
|
@ -2865,6 +2873,7 @@ function App() {
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
setIsLiveNotesOpen(true)
|
setIsLiveNotesOpen(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -2877,6 +2886,7 @@ function App() {
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
setIsBgTasksOpen(true)
|
setIsBgTasksOpen(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -2890,6 +2900,7 @@ function App() {
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (isEmailTabPath(tab.path)) {
|
if (isEmailTabPath(tab.path)) {
|
||||||
|
|
@ -2901,6 +2912,7 @@ function App() {
|
||||||
setIsBgTasksOpen(false)
|
setIsBgTasksOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
setIsEmailOpen(true)
|
setIsEmailOpen(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -2913,6 +2925,7 @@ function App() {
|
||||||
setIsBgTasksOpen(false)
|
setIsBgTasksOpen(false)
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
setIsWorkspaceOpen(true)
|
setIsWorkspaceOpen(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -2925,18 +2938,32 @@ function App() {
|
||||||
setIsBgTasksOpen(false)
|
setIsBgTasksOpen(false)
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
setIsKnowledgeViewOpen(true)
|
setIsKnowledgeViewOpen(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (isChatHistoryTabPath(tab.path)) {
|
||||||
|
setSelectedPath(null)
|
||||||
|
setIsGraphOpen(false)
|
||||||
|
setIsSuggestedTopicsOpen(false)
|
||||||
|
setIsMeetingsOpen(false)
|
||||||
|
setIsLiveNotesOpen(false)
|
||||||
|
setIsBgTasksOpen(false)
|
||||||
|
setIsEmailOpen(false)
|
||||||
|
setIsWorkspaceOpen(false)
|
||||||
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
setSelectedPath(tab.path)
|
setSelectedPath(tab.path)
|
||||||
}, [fileTabs, isRightPaneMaximized, dismissBrowserOverlay])
|
}, [fileTabs, isRightPaneMaximized, dismissBrowserOverlay])
|
||||||
|
|
||||||
const closeFileTab = useCallback((tabId: string) => {
|
const closeFileTab = useCallback((tabId: string) => {
|
||||||
const closingTab = fileTabs.find(t => t.id === tabId)
|
const closingTab = fileTabs.find(t => t.id === tabId)
|
||||||
if (closingTab && !isGraphTabPath(closingTab.path) && !isSuggestedTopicsTabPath(closingTab.path) && !isLiveNotesTabPath(closingTab.path) && !isBgTasksTabPath(closingTab.path) && !isEmailTabPath(closingTab.path) && !isWorkspaceTabPath(closingTab.path) && !isKnowledgeViewTabPath(closingTab.path) && !isBaseFilePath(closingTab.path)) {
|
if (closingTab && !isGraphTabPath(closingTab.path) && !isSuggestedTopicsTabPath(closingTab.path) && !isLiveNotesTabPath(closingTab.path) && !isBgTasksTabPath(closingTab.path) && !isEmailTabPath(closingTab.path) && !isWorkspaceTabPath(closingTab.path) && !isKnowledgeViewTabPath(closingTab.path) && !isChatHistoryTabPath(closingTab.path) && !isBaseFilePath(closingTab.path)) {
|
||||||
removeEditorCacheForPath(closingTab.path)
|
removeEditorCacheForPath(closingTab.path)
|
||||||
initialContentByPathRef.current.delete(closingTab.path)
|
initialContentByPathRef.current.delete(closingTab.path)
|
||||||
untitledRenameReadyPathsRef.current.delete(closingTab.path)
|
untitledRenameReadyPathsRef.current.delete(closingTab.path)
|
||||||
|
|
@ -2959,7 +2986,7 @@ function App() {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
const idx = prev.findIndex(t => t.id === tabId)
|
const idx = prev.findIndex(t => t.id === tabId)
|
||||||
|
|
@ -2973,12 +3000,12 @@ function App() {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsGraphOpen(true)
|
setIsGraphOpen(true)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
} else if (isSuggestedTopicsTabPath(newActiveTab.path)) {
|
} else if (isSuggestedTopicsTabPath(newActiveTab.path)) {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsSuggestedTopicsOpen(true)
|
setIsSuggestedTopicsOpen(true)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
} else if (isMeetingsTabPath(newActiveTab.path)) {
|
} else if (isMeetingsTabPath(newActiveTab.path)) {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
|
|
@ -2989,6 +3016,7 @@ function App() {
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
} else if (isLiveNotesTabPath(newActiveTab.path)) {
|
} else if (isLiveNotesTabPath(newActiveTab.path)) {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
|
|
@ -2998,6 +3026,7 @@ function App() {
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
setIsLiveNotesOpen(true)
|
setIsLiveNotesOpen(true)
|
||||||
} else if (isBgTasksTabPath(newActiveTab.path)) {
|
} else if (isBgTasksTabPath(newActiveTab.path)) {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
|
|
@ -3009,6 +3038,7 @@ function App() {
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
} else if (isEmailTabPath(newActiveTab.path)) {
|
} else if (isEmailTabPath(newActiveTab.path)) {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
|
|
@ -3018,6 +3048,7 @@ function App() {
|
||||||
setIsBgTasksOpen(false)
|
setIsBgTasksOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
setIsEmailOpen(true)
|
setIsEmailOpen(true)
|
||||||
} else if (isWorkspaceTabPath(newActiveTab.path)) {
|
} else if (isWorkspaceTabPath(newActiveTab.path)) {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
|
|
@ -3028,6 +3059,7 @@ function App() {
|
||||||
setIsBgTasksOpen(false)
|
setIsBgTasksOpen(false)
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
setIsWorkspaceOpen(true)
|
setIsWorkspaceOpen(true)
|
||||||
} else if (isKnowledgeViewTabPath(newActiveTab.path)) {
|
} else if (isKnowledgeViewTabPath(newActiveTab.path)) {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
|
|
@ -3038,11 +3070,23 @@ function App() {
|
||||||
setIsBgTasksOpen(false)
|
setIsBgTasksOpen(false)
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
setIsKnowledgeViewOpen(true)
|
setIsKnowledgeViewOpen(true)
|
||||||
|
} else if (isChatHistoryTabPath(newActiveTab.path)) {
|
||||||
|
setSelectedPath(null)
|
||||||
|
setIsGraphOpen(false)
|
||||||
|
setIsSuggestedTopicsOpen(false)
|
||||||
|
setIsMeetingsOpen(false)
|
||||||
|
setIsLiveNotesOpen(false)
|
||||||
|
setIsBgTasksOpen(false)
|
||||||
|
setIsEmailOpen(false)
|
||||||
|
setIsWorkspaceOpen(false)
|
||||||
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(true)
|
||||||
} else {
|
} else {
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
setSelectedPath(newActiveTab.path)
|
setSelectedPath(newActiveTab.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3073,7 +3117,7 @@ function App() {
|
||||||
dismissBrowserOverlay()
|
dismissBrowserOverlay()
|
||||||
handleNewChat()
|
handleNewChat()
|
||||||
// Left-pane "new chat" should always open full chat view.
|
// Left-pane "new chat" should always open full chat view.
|
||||||
if (selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen) {
|
if (selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isChatHistoryOpen) {
|
||||||
setExpandedFrom({
|
setExpandedFrom({
|
||||||
path: selectedPath,
|
path: selectedPath,
|
||||||
graph: isGraphOpen,
|
graph: isGraphOpen,
|
||||||
|
|
@ -3090,8 +3134,8 @@ function App() {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
}, [chatTabs, activeChatTabId, dismissBrowserOverlay, handleNewChat, selectedPath, isGraphOpen, isSuggestedTopicsOpen, isMeetingsOpen, isLiveNotesOpen, isBgTasksOpen, isEmailOpen, isWorkspaceOpen, isKnowledgeViewOpen])
|
}, [chatTabs, activeChatTabId, dismissBrowserOverlay, handleNewChat, selectedPath, isGraphOpen, isSuggestedTopicsOpen, isMeetingsOpen, isLiveNotesOpen, isBgTasksOpen, isEmailOpen, isWorkspaceOpen, isKnowledgeViewOpen, isChatHistoryOpen])
|
||||||
|
|
||||||
// Sidebar variant: create/switch chat tab without leaving file/graph context.
|
// Sidebar variant: create/switch chat tab without leaving file/graph context.
|
||||||
const handleNewChatTabInSidebar = useCallback(() => {
|
const handleNewChatTabInSidebar = useCallback(() => {
|
||||||
|
|
@ -3223,7 +3267,7 @@ function App() {
|
||||||
|
|
||||||
const handleOpenFullScreenChat = useCallback(() => {
|
const handleOpenFullScreenChat = useCallback(() => {
|
||||||
// Remember where we came from so the close button can return
|
// Remember where we came from so the close button can return
|
||||||
if (selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen) {
|
if (selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isChatHistoryOpen) {
|
||||||
setExpandedFrom({
|
setExpandedFrom({
|
||||||
path: selectedPath,
|
path: selectedPath,
|
||||||
graph: isGraphOpen,
|
graph: isGraphOpen,
|
||||||
|
|
@ -3239,19 +3283,19 @@ function App() {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
}, [selectedPath, isGraphOpen, isSuggestedTopicsOpen, isMeetingsOpen, isLiveNotesOpen, isBgTasksOpen, isEmailOpen, isWorkspaceOpen, isKnowledgeViewOpen, dismissBrowserOverlay])
|
}, [selectedPath, isGraphOpen, isSuggestedTopicsOpen, isMeetingsOpen, isLiveNotesOpen, isBgTasksOpen, isEmailOpen, isWorkspaceOpen, isKnowledgeViewOpen, isChatHistoryOpen, dismissBrowserOverlay])
|
||||||
|
|
||||||
const handleCloseFullScreenChat = useCallback(() => {
|
const handleCloseFullScreenChat = useCallback(() => {
|
||||||
if (expandedFrom) {
|
if (expandedFrom) {
|
||||||
if (expandedFrom.graph) {
|
if (expandedFrom.graph) {
|
||||||
setIsGraphOpen(true)
|
setIsGraphOpen(true)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
} else if (expandedFrom.suggestedTopics) {
|
} else if (expandedFrom.suggestedTopics) {
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsSuggestedTopicsOpen(true)
|
setIsSuggestedTopicsOpen(true)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
} else if (expandedFrom.meetings) {
|
} else if (expandedFrom.meetings) {
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
|
|
@ -3283,7 +3327,7 @@ function App() {
|
||||||
} else if (expandedFrom.path) {
|
} else if (expandedFrom.path) {
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
setSelectedPath(expandedFrom.path)
|
setSelectedPath(expandedFrom.path)
|
||||||
}
|
}
|
||||||
setExpandedFrom(null)
|
setExpandedFrom(null)
|
||||||
|
|
@ -3299,10 +3343,11 @@ function App() {
|
||||||
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' }
|
||||||
|
if (isChatHistoryOpen) return { type: 'chat-history' }
|
||||||
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, workspaceInitialPath, runId])
|
}, [selectedBackgroundTask, isEmailOpen, isMeetingsOpen, isLiveNotesOpen, isBgTasksOpen, isSuggestedTopicsOpen, selectedPath, isGraphOpen, isWorkspaceOpen, isKnowledgeViewOpen, isChatHistoryOpen, 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]
|
||||||
|
|
@ -3425,6 +3470,17 @@ function App() {
|
||||||
setActiveFileTabId(id)
|
setActiveFileTabId(id)
|
||||||
}, [fileTabs])
|
}, [fileTabs])
|
||||||
|
|
||||||
|
const ensureChatHistoryFileTab = useCallback(() => {
|
||||||
|
const existing = fileTabs.find((tab) => isChatHistoryTabPath(tab.path))
|
||||||
|
if (existing) {
|
||||||
|
setActiveFileTabId(existing.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const id = newFileTabId()
|
||||||
|
setFileTabs((prev) => [...prev, { id, path: CHAT_HISTORY_TAB_PATH }])
|
||||||
|
setActiveFileTabId(id)
|
||||||
|
}, [fileTabs])
|
||||||
|
|
||||||
const openEmailView = useCallback(() => {
|
const openEmailView = useCallback(() => {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
|
|
@ -3447,7 +3503,7 @@ function App() {
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsBrowserOpen(false)
|
setIsBrowserOpen(false)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
setSelectedBackgroundTask(null)
|
setSelectedBackgroundTask(null)
|
||||||
setExpandedFrom(null)
|
setExpandedFrom(null)
|
||||||
setIsRightPaneMaximized(false)
|
setIsRightPaneMaximized(false)
|
||||||
|
|
@ -3481,7 +3537,7 @@ function App() {
|
||||||
// visible in the middle pane.
|
// visible in the middle pane.
|
||||||
setIsBrowserOpen(false)
|
setIsBrowserOpen(false)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
setExpandedFrom(null)
|
setExpandedFrom(null)
|
||||||
// Preserve split vs knowledge-max mode when navigating knowledge files.
|
// Preserve split vs knowledge-max mode when navigating knowledge files.
|
||||||
// Only exit chat-only maximize, because that would hide the selected file.
|
// Only exit chat-only maximize, because that would hide the selected file.
|
||||||
|
|
@ -3496,7 +3552,7 @@ function App() {
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsBrowserOpen(false)
|
setIsBrowserOpen(false)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
setExpandedFrom(null)
|
setExpandedFrom(null)
|
||||||
setIsGraphOpen(true)
|
setIsGraphOpen(true)
|
||||||
ensureGraphFileTab()
|
ensureGraphFileTab()
|
||||||
|
|
@ -3509,7 +3565,7 @@ function App() {
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
setIsBrowserOpen(false)
|
setIsBrowserOpen(false)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
setExpandedFrom(null)
|
setExpandedFrom(null)
|
||||||
setIsRightPaneMaximized(false)
|
setIsRightPaneMaximized(false)
|
||||||
setSelectedBackgroundTask(view.name)
|
setSelectedBackgroundTask(view.name)
|
||||||
|
|
@ -3522,7 +3578,7 @@ function App() {
|
||||||
setIsRightPaneMaximized(false)
|
setIsRightPaneMaximized(false)
|
||||||
setSelectedBackgroundTask(null)
|
setSelectedBackgroundTask(null)
|
||||||
setIsSuggestedTopicsOpen(true)
|
setIsSuggestedTopicsOpen(true)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
ensureSuggestedTopicsFileTab()
|
ensureSuggestedTopicsFileTab()
|
||||||
return
|
return
|
||||||
case 'meetings':
|
case 'meetings':
|
||||||
|
|
@ -3539,6 +3595,7 @@ function App() {
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
ensureMeetingsFileTab()
|
ensureMeetingsFileTab()
|
||||||
return
|
return
|
||||||
case 'live-notes':
|
case 'live-notes':
|
||||||
|
|
@ -3554,6 +3611,7 @@ function App() {
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
setIsLiveNotesOpen(true)
|
setIsLiveNotesOpen(true)
|
||||||
ensureLiveNotesFileTab()
|
ensureLiveNotesFileTab()
|
||||||
return
|
return
|
||||||
|
|
@ -3571,6 +3629,7 @@ function App() {
|
||||||
setIsEmailOpen(true)
|
setIsEmailOpen(true)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
ensureEmailFileTab()
|
ensureEmailFileTab()
|
||||||
return
|
return
|
||||||
case 'workspace':
|
case 'workspace':
|
||||||
|
|
@ -3587,6 +3646,7 @@ function App() {
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsWorkspaceOpen(true)
|
setIsWorkspaceOpen(true)
|
||||||
setIsKnowledgeViewOpen(false)
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
setWorkspaceInitialPath(view.path ?? null)
|
setWorkspaceInitialPath(view.path ?? null)
|
||||||
ensureWorkspaceFileTab()
|
ensureWorkspaceFileTab()
|
||||||
return
|
return
|
||||||
|
|
@ -3604,8 +3664,26 @@ function App() {
|
||||||
setIsEmailOpen(false)
|
setIsEmailOpen(false)
|
||||||
setIsWorkspaceOpen(false)
|
setIsWorkspaceOpen(false)
|
||||||
setIsKnowledgeViewOpen(true)
|
setIsKnowledgeViewOpen(true)
|
||||||
|
setIsChatHistoryOpen(false)
|
||||||
ensureKnowledgeViewFileTab()
|
ensureKnowledgeViewFileTab()
|
||||||
return
|
return
|
||||||
|
case 'chat-history':
|
||||||
|
setSelectedPath(null)
|
||||||
|
setIsGraphOpen(false)
|
||||||
|
setIsBrowserOpen(false)
|
||||||
|
setExpandedFrom(null)
|
||||||
|
setIsRightPaneMaximized(false)
|
||||||
|
setSelectedBackgroundTask(null)
|
||||||
|
setIsSuggestedTopicsOpen(false)
|
||||||
|
setIsMeetingsOpen(false)
|
||||||
|
setIsLiveNotesOpen(false)
|
||||||
|
setIsBgTasksOpen(false)
|
||||||
|
setIsEmailOpen(false)
|
||||||
|
setIsWorkspaceOpen(false)
|
||||||
|
setIsKnowledgeViewOpen(false)
|
||||||
|
setIsChatHistoryOpen(true)
|
||||||
|
ensureChatHistoryFileTab()
|
||||||
|
return
|
||||||
case 'chat':
|
case 'chat':
|
||||||
setSelectedPath(null)
|
setSelectedPath(null)
|
||||||
setIsGraphOpen(false)
|
setIsGraphOpen(false)
|
||||||
|
|
@ -3614,7 +3692,7 @@ function App() {
|
||||||
setIsRightPaneMaximized(false)
|
setIsRightPaneMaximized(false)
|
||||||
setSelectedBackgroundTask(null)
|
setSelectedBackgroundTask(null)
|
||||||
setIsSuggestedTopicsOpen(false)
|
setIsSuggestedTopicsOpen(false)
|
||||||
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false)
|
setIsMeetingsOpen(false); setIsLiveNotesOpen(false); setIsBgTasksOpen(false); setIsEmailOpen(false); setIsWorkspaceOpen(false); setIsKnowledgeViewOpen(false); setIsChatHistoryOpen(false)
|
||||||
if (view.runId) {
|
if (view.runId) {
|
||||||
await loadRun(view.runId)
|
await loadRun(view.runId)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -3622,7 +3700,7 @@ function App() {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}, [ensureEmailFileTab, ensureMeetingsFileTab, ensureLiveNotesFileTab, ensureFileTabForPath, ensureGraphFileTab, ensureSuggestedTopicsFileTab, ensureWorkspaceFileTab, ensureKnowledgeViewFileTab, handleNewChat, isRightPaneMaximized, loadRun])
|
}, [ensureEmailFileTab, ensureMeetingsFileTab, ensureLiveNotesFileTab, ensureFileTabForPath, ensureGraphFileTab, ensureSuggestedTopicsFileTab, ensureWorkspaceFileTab, ensureKnowledgeViewFileTab, ensureChatHistoryFileTab, handleNewChat, isRightPaneMaximized, loadRun])
|
||||||
|
|
||||||
const navigateToView = useCallback(async (nextView: ViewState) => {
|
const navigateToView = useCallback(async (nextView: ViewState) => {
|
||||||
const current = currentViewState
|
const current = currentViewState
|
||||||
|
|
@ -3944,7 +4022,7 @@ function App() {
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// Keyboard shortcut: Ctrl+L to toggle main chat view
|
// Keyboard shortcut: Ctrl+L to toggle main chat view
|
||||||
const isFullScreenChat = !selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !selectedBackgroundTask && !isBrowserOpen
|
const isFullScreenChat = !selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !isChatHistoryOpen && !selectedBackgroundTask && !isBrowserOpen
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === 'l') {
|
if ((e.ctrlKey || e.metaKey) && e.key === 'l') {
|
||||||
|
|
@ -4029,11 +4107,11 @@ function App() {
|
||||||
const handleTabKeyDown = (e: KeyboardEvent) => {
|
const handleTabKeyDown = (e: KeyboardEvent) => {
|
||||||
const mod = e.metaKey || e.ctrlKey
|
const mod = e.metaKey || e.ctrlKey
|
||||||
if (!mod) return
|
if (!mod) return
|
||||||
const rightPaneAvailable = Boolean((selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen) && isChatSidebarOpen)
|
const rightPaneAvailable = Boolean((selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isChatHistoryOpen) && isChatSidebarOpen)
|
||||||
const targetPane: ShortcutPane = rightPaneAvailable
|
const targetPane: ShortcutPane = rightPaneAvailable
|
||||||
? (isRightPaneMaximized ? 'right' : activeShortcutPane)
|
? (isRightPaneMaximized ? 'right' : activeShortcutPane)
|
||||||
: 'left'
|
: 'left'
|
||||||
const inFileView = targetPane === 'left' && Boolean(selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen)
|
const inFileView = targetPane === 'left' && Boolean(selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isChatHistoryOpen)
|
||||||
const selectedKnowledgePath = isGraphOpen
|
const selectedKnowledgePath = isGraphOpen
|
||||||
? GRAPH_TAB_PATH
|
? GRAPH_TAB_PATH
|
||||||
: isSuggestedTopicsOpen
|
: isSuggestedTopicsOpen
|
||||||
|
|
@ -4050,6 +4128,8 @@ function App() {
|
||||||
? WORKSPACE_TAB_PATH
|
? WORKSPACE_TAB_PATH
|
||||||
: isKnowledgeViewOpen
|
: isKnowledgeViewOpen
|
||||||
? KNOWLEDGE_VIEW_TAB_PATH
|
? KNOWLEDGE_VIEW_TAB_PATH
|
||||||
|
: isChatHistoryOpen
|
||||||
|
? CHAT_HISTORY_TAB_PATH
|
||||||
: selectedPath
|
: selectedPath
|
||||||
const targetFileTabId = activeFileTabId ?? (
|
const targetFileTabId = activeFileTabId ?? (
|
||||||
selectedKnowledgePath
|
selectedKnowledgePath
|
||||||
|
|
@ -4104,7 +4184,7 @@ function App() {
|
||||||
}
|
}
|
||||||
document.addEventListener('keydown', handleTabKeyDown)
|
document.addEventListener('keydown', handleTabKeyDown)
|
||||||
return () => document.removeEventListener('keydown', handleTabKeyDown)
|
return () => document.removeEventListener('keydown', handleTabKeyDown)
|
||||||
}, [selectedPath, isGraphOpen, isSuggestedTopicsOpen, isMeetingsOpen, isLiveNotesOpen, isBgTasksOpen, isEmailOpen, isWorkspaceOpen, isKnowledgeViewOpen, isChatSidebarOpen, isRightPaneMaximized, activeShortcutPane, chatTabs, fileTabs, activeChatTabId, activeFileTabId, closeChatTab, closeFileTab, switchChatTab, switchFileTab])
|
}, [selectedPath, isGraphOpen, isSuggestedTopicsOpen, isMeetingsOpen, isLiveNotesOpen, isBgTasksOpen, isEmailOpen, isWorkspaceOpen, isKnowledgeViewOpen, isChatHistoryOpen, isChatSidebarOpen, isRightPaneMaximized, activeShortcutPane, chatTabs, fileTabs, activeChatTabId, activeFileTabId, closeChatTab, closeFileTab, switchChatTab, switchFileTab])
|
||||||
|
|
||||||
const toggleExpand = (path: string, kind: 'file' | 'dir') => {
|
const toggleExpand = (path: string, kind: 'file' | 'dir') => {
|
||||||
if (kind === 'file') {
|
if (kind === 'file') {
|
||||||
|
|
@ -4129,7 +4209,7 @@ function App() {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
if (!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !selectedBackgroundTask) {
|
if (!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !isChatHistoryOpen && !selectedBackgroundTask) {
|
||||||
setIsChatSidebarOpen(false)
|
setIsChatSidebarOpen(false)
|
||||||
setIsRightPaneMaximized(false)
|
setIsRightPaneMaximized(false)
|
||||||
}
|
}
|
||||||
|
|
@ -4255,28 +4335,28 @@ function App() {
|
||||||
},
|
},
|
||||||
openGraph: () => {
|
openGraph: () => {
|
||||||
// From chat-only landing state, open graph directly in full knowledge view.
|
// From chat-only landing state, open graph directly in full knowledge view.
|
||||||
if (!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !selectedBackgroundTask) {
|
if (!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !isChatHistoryOpen && !selectedBackgroundTask) {
|
||||||
setIsChatSidebarOpen(false)
|
setIsChatSidebarOpen(false)
|
||||||
setIsRightPaneMaximized(false)
|
setIsRightPaneMaximized(false)
|
||||||
}
|
}
|
||||||
void navigateToView({ type: 'graph' })
|
void navigateToView({ type: 'graph' })
|
||||||
},
|
},
|
||||||
openBases: () => {
|
openBases: () => {
|
||||||
if (!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !selectedBackgroundTask) {
|
if (!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !isChatHistoryOpen && !selectedBackgroundTask) {
|
||||||
setIsChatSidebarOpen(false)
|
setIsChatSidebarOpen(false)
|
||||||
setIsRightPaneMaximized(false)
|
setIsRightPaneMaximized(false)
|
||||||
}
|
}
|
||||||
void navigateToView({ type: 'file', path: BASES_DEFAULT_TAB_PATH })
|
void navigateToView({ type: 'file', path: BASES_DEFAULT_TAB_PATH })
|
||||||
},
|
},
|
||||||
openWorkspaceAt: (path?: string) => {
|
openWorkspaceAt: (path?: string) => {
|
||||||
if (!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !selectedBackgroundTask) {
|
if (!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !isChatHistoryOpen && !selectedBackgroundTask) {
|
||||||
setIsChatSidebarOpen(false)
|
setIsChatSidebarOpen(false)
|
||||||
setIsRightPaneMaximized(false)
|
setIsRightPaneMaximized(false)
|
||||||
}
|
}
|
||||||
void navigateToView({ type: 'workspace', path })
|
void navigateToView({ type: 'workspace', path })
|
||||||
},
|
},
|
||||||
openKnowledgeView: () => {
|
openKnowledgeView: () => {
|
||||||
if (!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !selectedBackgroundTask) {
|
if (!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !isChatHistoryOpen && !selectedBackgroundTask) {
|
||||||
setIsChatSidebarOpen(false)
|
setIsChatSidebarOpen(false)
|
||||||
setIsRightPaneMaximized(false)
|
setIsRightPaneMaximized(false)
|
||||||
}
|
}
|
||||||
|
|
@ -4888,7 +4968,7 @@ function App() {
|
||||||
const selectedTask = selectedBackgroundTask
|
const selectedTask = selectedBackgroundTask
|
||||||
? backgroundTasks.find(t => t.name === selectedBackgroundTask)
|
? backgroundTasks.find(t => t.name === selectedBackgroundTask)
|
||||||
: null
|
: null
|
||||||
const isRightPaneContext = Boolean(selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isBrowserOpen)
|
const isRightPaneContext = Boolean(selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isChatHistoryOpen || isBrowserOpen)
|
||||||
const isRightPaneOnlyMode = isRightPaneContext && isChatSidebarOpen && isRightPaneMaximized
|
const isRightPaneOnlyMode = isRightPaneContext && isChatSidebarOpen && isRightPaneMaximized
|
||||||
const shouldCollapseLeftPane = isRightPaneOnlyMode
|
const shouldCollapseLeftPane = isRightPaneOnlyMode
|
||||||
const openMarkdownTabs = React.useMemo(() => {
|
const openMarkdownTabs = React.useMemo(() => {
|
||||||
|
|
@ -4905,7 +4985,7 @@ function App() {
|
||||||
return (
|
return (
|
||||||
<TooltipProvider delayDuration={0}>
|
<TooltipProvider delayDuration={0}>
|
||||||
<SidebarSectionProvider defaultSection="tasks" onSectionChange={(section) => {
|
<SidebarSectionProvider defaultSection="tasks" onSectionChange={(section) => {
|
||||||
if (section === 'knowledge' && !selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen) {
|
if (section === 'knowledge' && !selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !isChatHistoryOpen) {
|
||||||
void navigateToView({ type: 'file', path: BASES_DEFAULT_TAB_PATH })
|
void navigateToView({ type: 'file', path: BASES_DEFAULT_TAB_PATH })
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
|
|
@ -4928,7 +5008,7 @@ function App() {
|
||||||
onNewChat: handleNewChatTab,
|
onNewChat: handleNewChatTab,
|
||||||
onSelectRun: (runIdToLoad) => {
|
onSelectRun: (runIdToLoad) => {
|
||||||
cancelRecordingIfActive()
|
cancelRecordingIfActive()
|
||||||
if (selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isBrowserOpen) {
|
if (selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isChatHistoryOpen || isBrowserOpen) {
|
||||||
setIsChatSidebarOpen(true)
|
setIsChatSidebarOpen(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4939,7 +5019,7 @@ function App() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// In two-pane mode (file/graph/browser), keep the middle pane and just swap chat context in the right sidebar.
|
// In two-pane mode (file/graph/browser), keep the middle pane and just swap chat context in the right sidebar.
|
||||||
if (selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isBrowserOpen) {
|
if (selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isChatHistoryOpen || isBrowserOpen) {
|
||||||
setChatTabs(prev => prev.map(t => t.id === activeChatTabId ? { ...t, runId: runIdToLoad } : t))
|
setChatTabs(prev => prev.map(t => t.id === activeChatTabId ? { ...t, runId: runIdToLoad } : t))
|
||||||
loadRun(runIdToLoad)
|
loadRun(runIdToLoad)
|
||||||
return
|
return
|
||||||
|
|
@ -4963,14 +5043,14 @@ function App() {
|
||||||
} else {
|
} else {
|
||||||
// Only one tab, reset it to new chat
|
// Only one tab, reset it to new chat
|
||||||
setChatTabs([{ id: tabForRun.id, runId: null }])
|
setChatTabs([{ id: tabForRun.id, runId: null }])
|
||||||
if (selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isBrowserOpen) {
|
if (selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isChatHistoryOpen || isBrowserOpen) {
|
||||||
handleNewChat()
|
handleNewChat()
|
||||||
} else {
|
} else {
|
||||||
void navigateToView({ type: 'chat', runId: null })
|
void navigateToView({ type: 'chat', runId: null })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (runId === runIdToDelete) {
|
} else if (runId === runIdToDelete) {
|
||||||
if (selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isBrowserOpen) {
|
if (selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isChatHistoryOpen || isBrowserOpen) {
|
||||||
setChatTabs(prev => prev.map(t => t.id === activeChatTabId ? { ...t, runId: null } : t))
|
setChatTabs(prev => prev.map(t => t.id === activeChatTabId ? { ...t, runId: null } : t))
|
||||||
handleNewChat()
|
handleNewChat()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -4985,6 +5065,9 @@ function App() {
|
||||||
onSelectBackgroundTask: (taskName) => {
|
onSelectBackgroundTask: (taskName) => {
|
||||||
void navigateToView({ type: 'task', name: taskName })
|
void navigateToView({ type: 'task', name: taskName })
|
||||||
},
|
},
|
||||||
|
onOpenChatHistoryView: () => {
|
||||||
|
void navigateToView({ type: 'chat-history' })
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
backgroundTasks={backgroundTasks}
|
backgroundTasks={backgroundTasks}
|
||||||
selectedBackgroundTask={selectedBackgroundTask}
|
selectedBackgroundTask={selectedBackgroundTask}
|
||||||
|
|
@ -5018,7 +5101,7 @@ function App() {
|
||||||
canNavigateForward={canNavigateForward}
|
canNavigateForward={canNavigateForward}
|
||||||
collapsedLeftPaddingPx={collapsedLeftPaddingPx}
|
collapsedLeftPaddingPx={collapsedLeftPaddingPx}
|
||||||
>
|
>
|
||||||
{(selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen) && fileTabs.length >= 1 ? (
|
{(selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isChatHistoryOpen) && fileTabs.length >= 1 ? (
|
||||||
<TabBar
|
<TabBar
|
||||||
tabs={fileTabs}
|
tabs={fileTabs}
|
||||||
activeTabId={activeFileTabId ?? ''}
|
activeTabId={activeFileTabId ?? ''}
|
||||||
|
|
@ -5026,7 +5109,7 @@ function App() {
|
||||||
getTabId={(t) => t.id}
|
getTabId={(t) => t.id}
|
||||||
onSwitchTab={switchFileTab}
|
onSwitchTab={switchFileTab}
|
||||||
onCloseTab={closeFileTab}
|
onCloseTab={closeFileTab}
|
||||||
allowSingleTabClose={fileTabs.length === 1 && (isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || (selectedPath != null && isBaseFilePath(selectedPath)))}
|
allowSingleTabClose={fileTabs.length === 1 && (isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isChatHistoryOpen || (selectedPath != null && isBaseFilePath(selectedPath)))}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TabBar
|
<TabBar
|
||||||
|
|
@ -5079,7 +5162,7 @@ function App() {
|
||||||
<TooltipContent side="bottom">Version history</TooltipContent>
|
<TooltipContent side="bottom">Version history</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !selectedTask && !isBrowserOpen && (
|
{!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !isChatHistoryOpen && !selectedTask && !isBrowserOpen && (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<button
|
<button
|
||||||
|
|
@ -5094,7 +5177,7 @@ function App() {
|
||||||
<TooltipContent side="bottom">New chat tab</TooltipContent>
|
<TooltipContent side="bottom">New chat tab</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !isBrowserOpen && expandedFrom && (
|
{!selectedPath && !isGraphOpen && !isSuggestedTopicsOpen && !isMeetingsOpen && !isLiveNotesOpen && !isBgTasksOpen && !isEmailOpen && !isWorkspaceOpen && !isKnowledgeViewOpen && !isChatHistoryOpen && !isBrowserOpen && expandedFrom && (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<button
|
<button
|
||||||
|
|
@ -5109,7 +5192,7 @@ function App() {
|
||||||
<TooltipContent side="bottom">Restore two-pane view</TooltipContent>
|
<TooltipContent side="bottom">Restore two-pane view</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{(selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen) && (
|
{(selectedPath || isGraphOpen || isSuggestedTopicsOpen || isMeetingsOpen || isLiveNotesOpen || isBgTasksOpen || isEmailOpen || isWorkspaceOpen || isKnowledgeViewOpen || isChatHistoryOpen) && (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<button
|
<button
|
||||||
|
|
@ -5204,6 +5287,26 @@ function App() {
|
||||||
onVoiceNoteCreated={handleVoiceNoteCreated}
|
onVoiceNoteCreated={handleVoiceNoteCreated}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
) : isChatHistoryOpen ? (
|
||||||
|
<div className="flex-1 min-h-0 flex flex-col overflow-hidden">
|
||||||
|
<ChatHistoryView
|
||||||
|
runs={runs}
|
||||||
|
currentRunId={runId}
|
||||||
|
processingRunIds={processingRunIds}
|
||||||
|
onSelectRun={(rid) => void navigateToView({ type: 'chat', runId: rid })}
|
||||||
|
onOpenInNewTab={(rid) => openChatInNewTab(rid)}
|
||||||
|
onDeleteRun={async (rid) => {
|
||||||
|
try {
|
||||||
|
await window.ipc.invoke('runs:delete', { runId: rid })
|
||||||
|
await loadRuns()
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to delete run:', err)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onNewChat={handleNewChatTab}
|
||||||
|
onOpenSearch={() => setIsSearchOpen(true)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
) : selectedPath && isBaseFilePath(selectedPath) ? (
|
) : selectedPath && isBaseFilePath(selectedPath) ? (
|
||||||
<div className="flex-1 min-h-0 flex flex-col overflow-hidden">
|
<div className="flex-1 min-h-0 flex flex-col overflow-hidden">
|
||||||
<BasesView
|
<BasesView
|
||||||
|
|
|
||||||
177
apps/x/apps/renderer/src/components/chat-history-view.tsx
Normal file
177
apps/x/apps/renderer/src/components/chat-history-view.tsx
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
import { useCallback, useMemo, useState } from 'react'
|
||||||
|
import { ExternalLink, MessageSquare, SearchIcon, SquarePen, Trash2 } from 'lucide-react'
|
||||||
|
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import {
|
||||||
|
ContextMenu,
|
||||||
|
ContextMenuContent,
|
||||||
|
ContextMenuItem,
|
||||||
|
ContextMenuSeparator,
|
||||||
|
ContextMenuTrigger,
|
||||||
|
} from '@/components/ui/context-menu'
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog'
|
||||||
|
import { formatRelativeTime } from '@/lib/relative-time'
|
||||||
|
|
||||||
|
type Run = {
|
||||||
|
id: string
|
||||||
|
title?: string
|
||||||
|
createdAt: string
|
||||||
|
agentId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChatHistoryViewProps = {
|
||||||
|
runs: Run[]
|
||||||
|
currentRunId?: string | null
|
||||||
|
processingRunIds?: Set<string>
|
||||||
|
onSelectRun: (runId: string) => void
|
||||||
|
onOpenInNewTab?: (runId: string) => void
|
||||||
|
onDeleteRun: (runId: string) => Promise<void> | void
|
||||||
|
onNewChat?: () => void
|
||||||
|
onOpenSearch?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ChatHistoryView({
|
||||||
|
runs,
|
||||||
|
currentRunId,
|
||||||
|
processingRunIds,
|
||||||
|
onSelectRun,
|
||||||
|
onOpenInNewTab,
|
||||||
|
onDeleteRun,
|
||||||
|
onNewChat,
|
||||||
|
onOpenSearch,
|
||||||
|
}: ChatHistoryViewProps) {
|
||||||
|
const [pendingDeleteId, setPendingDeleteId] = useState<string | null>(null)
|
||||||
|
|
||||||
|
const sortedRuns = useMemo(() => {
|
||||||
|
return [...runs].sort((a, b) => {
|
||||||
|
const at = new Date(a.createdAt).getTime()
|
||||||
|
const bt = new Date(b.createdAt).getTime()
|
||||||
|
return (Number.isNaN(bt) ? 0 : bt) - (Number.isNaN(at) ? 0 : at)
|
||||||
|
})
|
||||||
|
}, [runs])
|
||||||
|
|
||||||
|
const handleConfirmDelete = useCallback(async () => {
|
||||||
|
if (!pendingDeleteId) return
|
||||||
|
const id = pendingDeleteId
|
||||||
|
setPendingDeleteId(null)
|
||||||
|
await onDeleteRun(id)
|
||||||
|
}, [pendingDeleteId, onDeleteRun])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-full flex-col overflow-hidden">
|
||||||
|
<div className="shrink-0 flex items-center justify-between gap-3 border-b border-border px-8 py-6">
|
||||||
|
<h1 className="text-2xl font-bold tracking-tight">Chat history</h1>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{onOpenSearch && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onOpenSearch}
|
||||||
|
className="inline-flex items-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-sm text-foreground transition-colors hover:bg-accent"
|
||||||
|
>
|
||||||
|
<SearchIcon className="size-4" />
|
||||||
|
<span>Search</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{onNewChat && (
|
||||||
|
<Button size="sm" onClick={onNewChat}>
|
||||||
|
<SquarePen className="size-4" />
|
||||||
|
New chat
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 overflow-y-auto">
|
||||||
|
<div className="min-w-[480px]">
|
||||||
|
<div className="sticky top-0 z-10 flex items-center border-b border-border bg-background px-6 py-2 text-xs font-medium text-muted-foreground">
|
||||||
|
<div className="flex-1">Title</div>
|
||||||
|
<div className="w-32 shrink-0">Created</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{sortedRuns.length === 0 ? (
|
||||||
|
<div className="px-6 py-8 text-sm text-muted-foreground">No chats yet.</div>
|
||||||
|
) : (
|
||||||
|
sortedRuns.map((run) => {
|
||||||
|
const isActive = currentRunId === run.id
|
||||||
|
const isProcessing = processingRunIds?.has(run.id)
|
||||||
|
return (
|
||||||
|
<ContextMenu key={run.id}>
|
||||||
|
<ContextMenuTrigger asChild>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={(e) => {
|
||||||
|
if (e.metaKey && onOpenInNewTab) {
|
||||||
|
onOpenInNewTab(run.id)
|
||||||
|
} else {
|
||||||
|
onSelectRun(run.id)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={[
|
||||||
|
'flex w-full items-center border-b border-border/60 px-6 py-1.5 text-left text-sm transition-colors hover:bg-accent',
|
||||||
|
isActive ? 'bg-accent/60' : '',
|
||||||
|
].join(' ')}
|
||||||
|
>
|
||||||
|
<div className="flex flex-1 items-center gap-2 min-w-0">
|
||||||
|
<MessageSquare className="size-4 shrink-0 text-muted-foreground" />
|
||||||
|
<span className="min-w-0 truncate">{run.title || '(Untitled chat)'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="w-32 shrink-0 text-xs text-muted-foreground tabular-nums">
|
||||||
|
{formatRelativeTime(run.createdAt)}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</ContextMenuTrigger>
|
||||||
|
<ContextMenuContent className="w-48">
|
||||||
|
{onOpenInNewTab && (
|
||||||
|
<>
|
||||||
|
<ContextMenuItem onClick={() => onOpenInNewTab(run.id)}>
|
||||||
|
<ExternalLink className="mr-2 size-4" />
|
||||||
|
Open in new tab
|
||||||
|
</ContextMenuItem>
|
||||||
|
<ContextMenuSeparator />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{!isProcessing && (
|
||||||
|
<ContextMenuItem
|
||||||
|
variant="destructive"
|
||||||
|
onClick={() => setPendingDeleteId(run.id)}
|
||||||
|
>
|
||||||
|
<Trash2 className="mr-2 size-4" />
|
||||||
|
Delete
|
||||||
|
</ContextMenuItem>
|
||||||
|
)}
|
||||||
|
</ContextMenuContent>
|
||||||
|
</ContextMenu>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Dialog open={!!pendingDeleteId} onOpenChange={(open) => { if (!open) setPendingDeleteId(null) }}>
|
||||||
|
<DialogContent showCloseButton={false} className="sm:max-w-sm">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Delete chat</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Are you sure you want to delete this chat?
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={() => setPendingDeleteId(null)}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button variant="destructive" onClick={() => void handleConfirmDelete()}>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -183,6 +183,7 @@ type TasksActions = {
|
||||||
onDeleteRun: (runId: string) => void
|
onDeleteRun: (runId: string) => void
|
||||||
onOpenInNewTab?: (runId: string) => void
|
onOpenInNewTab?: (runId: string) => void
|
||||||
onSelectBackgroundTask?: (taskName: string) => void
|
onSelectBackgroundTask?: (taskName: string) => void
|
||||||
|
onOpenChatHistoryView?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
type SidebarContentPanelProps = {
|
type SidebarContentPanelProps = {
|
||||||
|
|
@ -1194,62 +1195,66 @@ function TasksSection({
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{runs.length > 0 && (
|
<div className="px-3 py-1.5 mt-4 text-xs font-medium text-muted-foreground">
|
||||||
<>
|
Chat history
|
||||||
<div className="px-3 py-1.5 mt-4 text-xs font-medium text-muted-foreground">
|
</div>
|
||||||
Chat history
|
<SidebarMenu>
|
||||||
</div>
|
{runs.slice(0, 3).map((run) => (
|
||||||
<SidebarMenu>
|
<ContextMenu key={run.id}>
|
||||||
{runs.map((run) => (
|
<ContextMenuTrigger asChild>
|
||||||
<ContextMenu key={run.id}>
|
<SidebarMenuItem className="group/chat-item">
|
||||||
<ContextMenuTrigger asChild>
|
<SidebarMenuButton
|
||||||
<SidebarMenuItem className="group/chat-item">
|
isActive={currentRunId === run.id}
|
||||||
<SidebarMenuButton
|
onClick={(e) => {
|
||||||
isActive={currentRunId === run.id}
|
if (e.metaKey && actions?.onOpenInNewTab) {
|
||||||
onClick={(e) => {
|
actions.onOpenInNewTab(run.id)
|
||||||
if (e.metaKey && actions?.onOpenInNewTab) {
|
} else {
|
||||||
actions.onOpenInNewTab(run.id)
|
actions?.onSelectRun(run.id)
|
||||||
} else {
|
}
|
||||||
actions?.onSelectRun(run.id)
|
}}
|
||||||
}
|
>
|
||||||
}}
|
<div className="flex w-full items-center gap-2 min-w-0">
|
||||||
>
|
<span className="min-w-0 flex-1 truncate text-sm">{run.title || '(Untitled chat)'}</span>
|
||||||
<div className="flex w-full items-center gap-2 min-w-0">
|
{run.createdAt ? (
|
||||||
<span className="min-w-0 flex-1 truncate text-sm">{run.title || '(Untitled chat)'}</span>
|
<span className="shrink-0 text-[10px] text-muted-foreground">
|
||||||
{run.createdAt ? (
|
{formatRunTime(run.createdAt)}
|
||||||
<span className="shrink-0 text-[10px] text-muted-foreground">
|
</span>
|
||||||
{formatRunTime(run.createdAt)}
|
) : null}
|
||||||
</span>
|
</div>
|
||||||
) : null}
|
</SidebarMenuButton>
|
||||||
</div>
|
</SidebarMenuItem>
|
||||||
</SidebarMenuButton>
|
</ContextMenuTrigger>
|
||||||
</SidebarMenuItem>
|
<ContextMenuContent className="w-48">
|
||||||
</ContextMenuTrigger>
|
{actions?.onOpenInNewTab && (
|
||||||
<ContextMenuContent className="w-48">
|
<ContextMenuItem onClick={() => actions.onOpenInNewTab!(run.id)}>
|
||||||
{actions?.onOpenInNewTab && (
|
<ExternalLink className="mr-2 size-4" />
|
||||||
<ContextMenuItem onClick={() => actions.onOpenInNewTab!(run.id)}>
|
Open in new tab
|
||||||
<ExternalLink className="mr-2 size-4" />
|
</ContextMenuItem>
|
||||||
Open in new tab
|
)}
|
||||||
</ContextMenuItem>
|
{!processingRunIds?.has(run.id) && (
|
||||||
)}
|
<>
|
||||||
{!processingRunIds?.has(run.id) && (
|
{actions?.onOpenInNewTab && <ContextMenuSeparator />}
|
||||||
<>
|
<ContextMenuItem
|
||||||
{actions?.onOpenInNewTab && <ContextMenuSeparator />}
|
variant="destructive"
|
||||||
<ContextMenuItem
|
onClick={() => setPendingDeleteRunId(run.id)}
|
||||||
variant="destructive"
|
>
|
||||||
onClick={() => setPendingDeleteRunId(run.id)}
|
<Trash2 className="mr-2 size-4" />
|
||||||
>
|
Delete
|
||||||
<Trash2 className="mr-2 size-4" />
|
</ContextMenuItem>
|
||||||
Delete
|
</>
|
||||||
</ContextMenuItem>
|
)}
|
||||||
</>
|
</ContextMenuContent>
|
||||||
)}
|
</ContextMenu>
|
||||||
</ContextMenuContent>
|
))}
|
||||||
</ContextMenu>
|
{actions?.onOpenChatHistoryView && (
|
||||||
))}
|
<SidebarMenuItem>
|
||||||
</SidebarMenu>
|
<SidebarMenuButton onClick={() => actions.onOpenChatHistoryView?.()}>
|
||||||
</>
|
<ArrowUpRight className="size-4 shrink-0 text-muted-foreground" />
|
||||||
)}
|
<span className="text-muted-foreground">View all</span>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</SidebarMenuItem>
|
||||||
|
)}
|
||||||
|
</SidebarMenu>
|
||||||
</SidebarGroupContent>
|
</SidebarGroupContent>
|
||||||
|
|
||||||
{/* Delete confirmation dialog */}
|
{/* Delete confirmation dialog */}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue