2026-04-12 13:14:18 +02:00
|
|
|
import { useCallback, useMemo, useState } from 'react'
|
2026-04-13 19:37:59 +02:00
|
|
|
import type { AiAgentId } from '../lib/aiAgents'
|
|
|
|
|
import { useCliAiAgent, type AgentFileCallbacks } from '../hooks/useCliAiAgent'
|
2026-04-12 13:14:18 +02:00
|
|
|
import type { VaultEntry } from '../types'
|
|
|
|
|
import {
|
|
|
|
|
type NoteListItem,
|
|
|
|
|
type NoteReference,
|
|
|
|
|
} from '../utils/ai-context'
|
|
|
|
|
import { useAiPanelContextSnapshot } from './useAiPanelContextSnapshot'
|
|
|
|
|
|
|
|
|
|
interface UseAiPanelControllerArgs {
|
|
|
|
|
vaultPath: string
|
2026-04-13 19:37:59 +02:00
|
|
|
defaultAiAgent: AiAgentId
|
|
|
|
|
defaultAiAgentReady: boolean
|
2026-04-12 13:14:18 +02:00
|
|
|
activeEntry?: VaultEntry | null
|
|
|
|
|
activeNoteContent?: string | null
|
|
|
|
|
entries?: VaultEntry[]
|
|
|
|
|
openTabs?: VaultEntry[]
|
|
|
|
|
noteList?: NoteListItem[]
|
|
|
|
|
noteListFilter?: { type: string | null; query: string }
|
|
|
|
|
onOpenNote?: (path: string) => void
|
|
|
|
|
onFileCreated?: (relativePath: string) => void
|
|
|
|
|
onFileModified?: (relativePath: string) => void
|
|
|
|
|
onVaultChanged?: () => void
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 10:28:48 +02:00
|
|
|
export interface AiPanelController {
|
|
|
|
|
agent: ReturnType<typeof useCliAiAgent>
|
|
|
|
|
input: string
|
|
|
|
|
setInput: React.Dispatch<React.SetStateAction<string>>
|
|
|
|
|
linkedEntries: ReturnType<typeof useAiPanelContextSnapshot>['linkedEntries']
|
|
|
|
|
hasContext: boolean
|
|
|
|
|
isActive: boolean
|
|
|
|
|
handleSend: (text: string, references: NoteReference[]) => void
|
|
|
|
|
handleNavigateWikilink: (target: string) => void
|
|
|
|
|
handleNewChat: () => void
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-12 13:14:18 +02:00
|
|
|
export function useAiPanelController({
|
|
|
|
|
vaultPath,
|
2026-04-13 19:37:59 +02:00
|
|
|
defaultAiAgent,
|
|
|
|
|
defaultAiAgentReady,
|
2026-04-12 13:14:18 +02:00
|
|
|
activeEntry,
|
|
|
|
|
activeNoteContent,
|
|
|
|
|
entries,
|
|
|
|
|
openTabs,
|
|
|
|
|
noteList,
|
|
|
|
|
noteListFilter,
|
|
|
|
|
onOpenNote,
|
|
|
|
|
onFileCreated,
|
|
|
|
|
onFileModified,
|
|
|
|
|
onVaultChanged,
|
2026-04-20 10:28:48 +02:00
|
|
|
}: UseAiPanelControllerArgs): AiPanelController {
|
2026-04-12 13:14:18 +02:00
|
|
|
const [input, setInput] = useState('')
|
|
|
|
|
const { linkedEntries, contextPrompt } = useAiPanelContextSnapshot({
|
|
|
|
|
activeEntry,
|
|
|
|
|
activeNoteContent,
|
|
|
|
|
entries,
|
|
|
|
|
input,
|
|
|
|
|
openTabs,
|
|
|
|
|
noteList,
|
|
|
|
|
noteListFilter,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const fileCallbacks = useMemo<AgentFileCallbacks>(() => ({
|
|
|
|
|
onFileCreated,
|
|
|
|
|
onFileModified,
|
|
|
|
|
onVaultChanged,
|
|
|
|
|
}), [onFileCreated, onFileModified, onVaultChanged])
|
|
|
|
|
|
2026-04-13 19:37:59 +02:00
|
|
|
const agent = useCliAiAgent(vaultPath, contextPrompt, fileCallbacks, {
|
|
|
|
|
agent: defaultAiAgent,
|
|
|
|
|
agentReady: defaultAiAgentReady,
|
|
|
|
|
})
|
2026-04-12 13:14:18 +02:00
|
|
|
const hasContext = !!activeEntry
|
|
|
|
|
const isActive = agent.status === 'thinking' || agent.status === 'tool-executing'
|
|
|
|
|
|
|
|
|
|
const handleSend = useCallback((text: string, references: NoteReference[]) => {
|
|
|
|
|
if (!text.trim() || isActive) return
|
|
|
|
|
agent.sendMessage(text, references)
|
|
|
|
|
setInput('')
|
|
|
|
|
}, [agent, isActive])
|
|
|
|
|
|
|
|
|
|
const handleNavigateWikilink = useCallback((target: string) => {
|
|
|
|
|
onOpenNote?.(target)
|
|
|
|
|
}, [onOpenNote])
|
|
|
|
|
|
2026-04-20 10:28:48 +02:00
|
|
|
const handleNewChat = useCallback(() => {
|
|
|
|
|
agent.clearConversation()
|
|
|
|
|
setInput('')
|
|
|
|
|
}, [agent])
|
|
|
|
|
|
2026-04-12 13:14:18 +02:00
|
|
|
return {
|
|
|
|
|
agent,
|
|
|
|
|
input,
|
|
|
|
|
setInput,
|
|
|
|
|
linkedEntries,
|
|
|
|
|
hasContext,
|
|
|
|
|
isActive,
|
|
|
|
|
handleSend,
|
|
|
|
|
handleNavigateWikilink,
|
2026-04-20 10:28:48 +02:00
|
|
|
handleNewChat,
|
2026-04-12 13:14:18 +02:00
|
|
|
}
|
|
|
|
|
}
|