diff --git a/scripts/taskToggleMemory.test.ts b/scripts/taskToggleMemory.test.ts index e2d559f..cbd9bf2 100644 --- a/scripts/taskToggleMemory.test.ts +++ b/scripts/taskToggleMemory.test.ts @@ -2,10 +2,10 @@ import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import test from 'node:test'; -const viewer = readFileSync(new URL('../src/lib/MarkdownViewer.svelte', import.meta.url), 'utf8'); +const session = readFileSync(new URL('../src/lib/sessions/documentSession.svelte.ts', import.meta.url), 'utf8'); test('preview task toggles transform the active in-memory buffer before saving', () => { - const taskToggle = viewer.match(/async function toggleTaskCheckbox[\s\S]*?\n\t}\n\n\n\n\tfunction saveRecentFile/); + const taskToggle = session.match(/async function toggleTaskCheckbox[\s\S]*?\n\t}\n\n\treturn/); assert.ok(taskToggle); assert.doesNotMatch(taskToggle[0], /read_file_content/); assert.match(taskToggle[0], /const raw = tab\.rawContent;/); diff --git a/src/lib/MarkdownViewer.svelte b/src/lib/MarkdownViewer.svelte index c686379..98172c6 100644 --- a/src/lib/MarkdownViewer.svelte +++ b/src/lib/MarkdownViewer.svelte @@ -1469,37 +1469,11 @@ import { createDocumentSession, type LoadMarkdownOptions } from './sessions/docu } async function toggleTaskCheckbox(checkbox: HTMLInputElement) { - const tab = tabManager.activeTab; - if (!tab || !tab.path) return; - const raw = tab.rawContent; - - // find which task item this is by counting checkboxes in DOM const allBoxes = Array.from(markdownBody?.querySelectorAll('[data-task-checkbox]') || []); const index = allBoxes.indexOf(checkbox); if (index === -1) return; - - // checkbox.checked is still the OLD state (e.preventDefault blocked the toggle) const nowChecked = !checkbox.checked; - - // replace the nth [ ] or [x] in the raw markdown - let count = 0; - const updated = raw.replace(/^(\s*[-*+] )\[( |x|X)\]/gm, (match, prefix) => { - if (count === index) { - count++; - return `${prefix}[${nowChecked ? 'x' : ' '}]`; - } - count++; - return match; - }); - - if (updated === raw) return; - - // Update the single in-memory source before saving. saveContent keeps the - // tab dirty if its write fails, so a disk error cannot discard this edit. - tabManager.updateTabRawContent(tab.id, updated); - await saveContent(tab.id); - - // update DOM optimistically + if (!(await documentSession.toggleTaskCheckbox(index, nowChecked))) return; checkbox.checked = nowChecked; const li = checkbox.closest('li'); if (li) { diff --git a/src/lib/sessions/documentSession.svelte.ts b/src/lib/sessions/documentSession.svelte.ts index 45d51ae..fe453ec 100644 --- a/src/lib/sessions/documentSession.svelte.ts +++ b/src/lib/sessions/documentSession.svelte.ts @@ -192,5 +192,20 @@ export function createDocumentSession(options: DocumentSessionOptions) { } } - return { loadMarkdown, saveContent, saveContentAs }; + async function toggleTaskCheckbox(index: number, nowChecked: boolean) { + const tab = tabManager.activeTab; + if (!tab || !tab.path) return false; + const raw = tab.rawContent; + let count = 0; + const updated = raw.replace(/^(\s*[-*+] )\[( |x|X)\]/gm, (match, prefix) => { + if (count++ === index) return `${prefix}[${nowChecked ? 'x' : ' '}]`; + return match; + }); + if (updated === raw) return false; + tabManager.updateTabRawContent(tab.id, updated); + await saveContent(tab.id); + return true; + } + + return { loadMarkdown, saveContent, saveContentAs, toggleTaskCheckbox }; }