Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions scripts/taskToggleMemory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;/);
Expand Down
28 changes: 1 addition & 27 deletions src/lib/MarkdownViewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
17 changes: 16 additions & 1 deletion src/lib/sessions/documentSession.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}
Loading