diff --git a/src/hooks/useEditorSave.test.ts b/src/hooks/useEditorSave.test.ts index 59674ae5..897de224 100644 --- a/src/hooks/useEditorSave.test.ts +++ b/src/hooks/useEditorSave.test.ts @@ -171,6 +171,22 @@ describe('useEditorSave', () => { // (We'll check via the next handleSave call) }) + it('handleContentChange syncs content to tab state immediately', () => { + const { result } = renderSaveHook() + + act(() => { + result.current.handleContentChange('/test/note.md', '---\ntitle: T\n---\n\n# T\n\nLive edits') + }) + + // setTabs must be called on every content change (not just on save) + // so that consumers like the AI panel see current editor content + expect(setTabs).toHaveBeenCalled() + const updater = setTabs.mock.calls[0][0] + const tabs = [{ entry: { path: '/test/note.md' }, content: 'stale' }] + const updated = updater(tabs) + expect(updated[0].content).toBe('---\ntitle: T\n---\n\n# T\n\nLive edits') + }) + it('save updates tab content with edited body, not original (regression)', async () => { const { result } = renderSaveHook() diff --git a/src/hooks/useEditorSave.ts b/src/hooks/useEditorSave.ts index a179e1ac..066781ea 100644 --- a/src/hooks/useEditorSave.ts +++ b/src/hooks/useEditorSave.ts @@ -66,10 +66,14 @@ export function useEditorSave({ updateVaultContent, setTabs, setToastMessage, on } }, [flushPending, setToastMessage, onAfterSave, saveNote, onNotePersisted]) - /** Called by Editor onChange — buffers the latest content without saving */ + /** Called by Editor onChange — buffers the latest content and syncs tab state + * so consumers (e.g. AI panel) always see current editor content. */ const handleContentChange = useCallback((path: string, content: string) => { pendingContentRef.current = { path, content } - }, []) + setTabs((prev: Tab[]) => + prev.map((t) => t.entry.path === path ? { ...t, content } : t) + ) + }, [setTabs]) /** Save pending content for a specific path (used before rename) */ const savePendingForPath = useCallback(