fix: render empty task list items as checkboxes in note previews

The task-list markdown rule only matched a marker followed by a space
("[ ] text"). markdown-it trims trailing whitespace from inline content,
so an item with no text arrived as exactly "[ ]" and rendered as literal
brackets in the card preview, while the editor showed a checkbox.

Match the marker followed by a space or end-of-content instead, and strip
only what was matched so "[ ]x" glued to text stays literal.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Frederic Cabassut
2026-09-07 19:13:53 +02:00
committed by Fred Cabassut
co-authored by Claude Fable 5.1
parent 6bdcc3c214
commit 082b8632c6
2 changed files with 26 additions and 3 deletions
+19
View File
@@ -95,6 +95,25 @@ describe('renderMarkdown', () => {
expect(html).toContain('disabled'); expect(html).toContain('disabled');
expect(html).toContain('class="task-checkbox"'); expect(html).toContain('class="task-checkbox"');
}); });
it('should render an empty task item as a checkbox, not literal brackets', () => {
const html = renderMarkdown('- [ ] Train tickets\n- [ ]');
expect(html).not.toContain('[ ]');
expect(html.match(/type="checkbox"/g)).toHaveLength(2);
});
it('should render an empty checked task item as a checked checkbox', () => {
const html = renderMarkdown('- [x]');
expect(html).not.toContain('[x]');
expect(html).toContain('<input type="checkbox" checked disabled class="task-checkbox">');
expect(html).toContain('class="task-list"');
});
it('should not treat brackets glued to text as a task marker', () => {
const html = renderMarkdown('- [ ]x');
expect(html).not.toContain('type="checkbox"');
expect(html).toContain('[ ]x');
});
}); });
describe('stripMarkdown', () => { describe('stripMarkdown', () => {
+7 -3
View File
@@ -33,12 +33,16 @@ md.core.ruler.after('inline', 'task-lists', (state) => {
for (let i = 0; i < tokens.length; i++) { for (let i = 0; i < tokens.length; i++) {
if (tokens[i].type !== 'inline') continue; if (tokens[i].type !== 'inline') continue;
const content = tokens[i].content; const content = tokens[i].content;
if (content.startsWith('[ ] ') || content.startsWith('[x] ')) { // markdown-it trims trailing whitespace from inline content, so an item
const checked = content.startsWith('[x] '); // with no text after the marker arrives as exactly "[ ]" — match the
// marker followed by a space or end-of-content.
const marker = /^\[( |x)\](?: |$)/.exec(content);
if (marker) {
const checked = marker[1] === 'x';
const checkbox = checked const checkbox = checked
? '<input type="checkbox" checked disabled class="task-checkbox" /> ' ? '<input type="checkbox" checked disabled class="task-checkbox" /> '
: '<input type="checkbox" disabled class="task-checkbox" /> '; : '<input type="checkbox" disabled class="task-checkbox" /> ';
tokens[i].content = content.slice(4); tokens[i].content = content.slice(marker[0].length);
tokens[i].children = md.parseInline(tokens[i].content, state.env)[0].children; tokens[i].children = md.parseInline(tokens[i].content, state.env)[0].children;
// Prepend checkbox // Prepend checkbox
const token = new state.Token('html_inline', '', 0); const token = new state.Token('html_inline', '', 0);