diff --git a/src/components/AiPanel.tsx b/src/components/AiPanel.tsx index 3871a633..9dd99f52 100644 --- a/src/components/AiPanel.tsx +++ b/src/components/AiPanel.tsx @@ -16,6 +16,8 @@ interface AiPanelProps { onVaultChanged?: () => void vaultPath: string activeEntry?: VaultEntry | null + /** Direct content of the active note from the editor tab. */ + activeNoteContent?: string | null entries?: VaultEntry[] allContent?: Record openTabs?: VaultEntry[] @@ -110,7 +112,7 @@ function MessageHistory({ messages, isActive, onOpenNote, onNavigateWikilink, ha ) } -export function AiPanel({ onClose, onOpenNote, onFileCreated, onFileModified, onVaultChanged, vaultPath, activeEntry, entries, allContent, openTabs, noteList, noteListFilter }: AiPanelProps) { +export function AiPanel({ onClose, onOpenNote, onFileCreated, onFileModified, onVaultChanged, vaultPath, activeEntry, activeNoteContent, entries, allContent, openTabs, noteList, noteListFilter }: AiPanelProps) { const [input, setInput] = useState('') const [pendingRefs, setPendingRefs] = useState([]) const inputRef = useRef(null) @@ -122,17 +124,18 @@ export function AiPanel({ onClose, onOpenNote, onFileCreated, onFileModified, on }, [activeEntry, entries]) const contextPrompt = useMemo(() => { - if (!activeEntry || !allContent || !entries) return undefined + if (!activeEntry || !entries) return undefined return buildContextSnapshot({ activeEntry, - allContent, + allContent: allContent ?? {}, + activeNoteContent: activeNoteContent ?? undefined, openTabs, noteList, noteListFilter, entries, references: pendingRefs.length > 0 ? pendingRefs : undefined, }) - }, [activeEntry, allContent, openTabs, noteList, noteListFilter, entries, pendingRefs]) + }, [activeEntry, activeNoteContent, allContent, openTabs, noteList, noteListFilter, entries, pendingRefs]) const fileCallbacks = useMemo(() => ({ onFileCreated, diff --git a/src/components/EditorRightPanel.tsx b/src/components/EditorRightPanel.tsx index 3108c1bb..fbb927d4 100644 --- a/src/components/EditorRightPanel.tsx +++ b/src/components/EditorRightPanel.tsx @@ -51,6 +51,7 @@ export function EditorRightPanel({ onVaultChanged={onVaultChanged} vaultPath={vaultPath} activeEntry={inspectorEntry} + activeNoteContent={inspectorContent} entries={entries} allContent={allContent} openTabs={openTabs} diff --git a/src/utils/ai-context.test.ts b/src/utils/ai-context.test.ts index ba876107..a317fe3a 100644 --- a/src/utils/ai-context.test.ts +++ b/src/utils/ai-context.test.ts @@ -331,6 +331,35 @@ describe('buildContextSnapshot', () => { expect(json.noteList).toBeUndefined() }) + it('uses activeNoteContent for body when allContent is empty (Tauri mode)', () => { + const emptyAllContent: Record = {} + const result = buildContextSnapshot({ + activeEntry: active, + allContent: emptyAllContent, + entries, + activeNoteContent: '---\ntitle: Alpha\n---\n\n# Alpha\nProject content from tab.', + }) + const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0]) + expect(json.activeNote.body).toContain('Project content from tab.') + }) + + it('prefers activeNoteContent over allContent when both present', () => { + const result = buildContextSnapshot({ + activeEntry: active, + allContent, + entries, + activeNoteContent: 'Fresh editor content', + }) + const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0]) + expect(json.activeNote.body).toBe('Fresh editor content') + }) + + it('falls back to allContent when activeNoteContent is undefined', () => { + const result = buildContextSnapshot({ activeEntry: active, allContent, entries }) + const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0]) + expect(json.activeNote.body).toBe('# Alpha\nProject content.') + }) + it('includes wikilink instruction in preamble', () => { const result = buildContextSnapshot({ activeEntry: active, allContent, entries }) expect(result).toContain('[[Note Title]]') diff --git a/src/utils/ai-context.ts b/src/utils/ai-context.ts index 24c19604..f14ed22b 100644 --- a/src/utils/ai-context.ts +++ b/src/utils/ai-context.ts @@ -72,6 +72,8 @@ export interface NoteListItem { export interface ContextSnapshotParams { activeEntry: VaultEntry allContent: Record + /** Direct content of the active note from the editor tab (most reliable source). */ + activeNoteContent?: string openTabs?: VaultEntry[] noteList?: NoteListItem[] noteListFilter?: { type: string | null; query: string } @@ -94,7 +96,7 @@ const MAX_NOTE_LIST_ITEMS = 100 /** Build a structured context snapshot as a system prompt for Claude. */ export function buildContextSnapshot(params: ContextSnapshotParams): string { - const { activeEntry, allContent, openTabs, noteList, noteListFilter, entries, references } = params + const { activeEntry, allContent, activeNoteContent, openTabs, noteList, noteListFilter, entries, references } = params const snapshot: Record = { activeNote: { @@ -102,7 +104,7 @@ export function buildContextSnapshot(params: ContextSnapshotParams): string { title: activeEntry.title, type: activeEntry.isA ?? 'Note', frontmatter: entryFrontmatter(activeEntry), - body: allContent[activeEntry.path] ?? '', + body: activeNoteContent ?? allContent[activeEntry.path] ?? '', }, }