fix: persist property-only frontmatter edits

This commit is contained in:
lucaronin
2026-05-23 18:12:58 +02:00
parent 2a3ecf7270
commit ae529f64cd
3 changed files with 44 additions and 1 deletions

View File

@@ -270,6 +270,7 @@ export interface FrontmatterOpOptions {
export interface FrontmatterApplyCallbacks {
updateTab: (path: VaultPath, content: MarkdownContent) => void
updateEntry: (path: VaultPath, patch: Partial<VaultEntry>) => void
cacheContent?: (path: VaultPath, content: MarkdownContent) => void
toast: (message: ToastMessage) => void
getEntry?: (path: VaultPath) => VaultEntry | undefined
onMissingNotePath?: (path: VaultPath, error: unknown) => void | Promise<void>
@@ -395,6 +396,7 @@ export async function runFrontmatterAndApply(request: FrontmatterRunRequest): Pr
const { op, path, key, value, callbacks, options } = request
try {
const newContent = await executeFrontmatterOp(op, path, key, value)
callbacks.cacheContent?.(path, newContent)
if (callbacks.shouldApply && !callbacks.shouldApply(path)) return undefined
callbacks.updateTab(path, newContent)
applyEntryPatch(path, callbacks, frontmatterToEntryPatch(op, key, value))

View File

@@ -7,6 +7,7 @@ import { RAPID_CREATE_NOTE_SETTLE_MS } from './useNoteCreation'
import { useNoteActions } from './useNoteActions'
import type { NoteActionsConfig } from './useNoteActions'
import { GITIGNORED_VISIBILITY_APPLIED_EVENT } from '../lib/gitignoredVisibilityEvents'
import { clearNoteContentCache, getCachedNoteContentEntry } from './noteContentCache'
vi.mock('@tauri-apps/api/core', () => ({ invoke: vi.fn() }))
vi.mock('../mock-tauri', () => ({
@@ -85,6 +86,7 @@ describe('useNoteActions hook', () => {
vi.clearAllMocks()
vi.mocked(invoke).mockReset()
vi.mocked(isTauri).mockReturnValue(false)
clearNoteContentCache()
vi.useRealTimers()
})
@@ -283,6 +285,44 @@ describe('useNoteActions hook', () => {
expect(setToastMessage).not.toHaveBeenCalled()
})
it('keeps property-only frontmatter writes in the note cache after a note switch wins the apply guard', async () => {
vi.mocked(isTauri).mockReturnValue(true)
const noteA = makeEntry({ path: '/vault/note-a.md', filename: 'note-a.md', title: 'Note A' })
const noteB = makeEntry({ path: '/vault/note-b.md', filename: 'note-b.md', title: 'Note B' })
const updatedContent = '---\nStatus: Done\n---\nBody'
let resolveFrontmatterWrite: ((content: string) => void) | null = null
vi.mocked(invoke).mockImplementation(() => new Promise((resolve) => {
resolveFrontmatterWrite = (content) => { resolve(content) }
}))
const { result } = renderHook(() => useNoteActions(makeConfig([noteA, noteB])))
act(() => {
result.current.handleSwitchTab(noteA.path)
})
let updatePromise: Promise<void> = Promise.resolve()
await act(async () => {
updatePromise = result.current.handleUpdateFrontmatter(
noteA.path,
'Status',
'Done',
{ requireActivePath: noteA.path },
)
await Promise.resolve()
})
act(() => {
result.current.handleSwitchTab(noteB.path)
})
await act(async () => {
resolveFrontmatterWrite?.(updatedContent)
await updatePromise
})
expect(updateEntry).not.toHaveBeenCalled()
expect(setToastMessage).not.toHaveBeenCalled()
expect(getCachedNoteContentEntry(noteA.path)?.value).toBe(updatedContent)
})
it('handleCreateNoteImmediate creates note with timestamp-based title', async () => {
const createdEntry = await createImmediateEntry()
expect(createdEntry.title).toBe('Untitled Note 1700000000')

View File

@@ -1,7 +1,7 @@
import { useCallback, useEffect, useRef, type MutableRefObject } from 'react'
import type { VaultEntry } from '../types'
import type { FrontmatterValue } from '../components/Inspector'
import { useTabManagement } from './useTabManagement'
import { cacheNoteContent, useTabManagement } from './useTabManagement'
import {
GITIGNORED_VISIBILITY_APPLIED_EVENT,
type GitignoredVisibilityAppliedEvent,
@@ -474,6 +474,7 @@ function useFrontmatterRunner({
key,
value,
callbacks: {
cacheContent: cacheNoteContent,
updateTab: updateTabContent,
updateEntry,
toast: setToastMessage,