Closes #3593 (MUL-2926). Renders - [ ] / - [x] as interactive checkboxes in the issue content editor, the way GitHub / Notion do.
Before, the read-only view (ReadonlyContent, react-markdown + remark-gfm) already rendered task-list checkboxes, but the editable Tiptap editor parsed - [ ] as a plain bullet with literal [ ] text. This PR brings the editable view to parity.
Changes
Register TaskList + a patched TaskItem in the shared extension factory (extensions/index.ts). Both ship their own markdown tokenizer / renderMarkdown, an input rule (typing [] / [x] ), and a checkbox NodeView. The taskList tokenizer is consulted before marked's built-in list tokenizer, so - [ ] becomes a task while a plain - still falls through to the existing bullet list — no conflict.
Patch TaskItem's keymap to reuse PatchedListItem's split → lift Enter chain, so double-Enter on an empty task item exits the list (same fix bullet lists already have). nested: true enables sub-tasks and nested markdown round-trips. The shared chain is factored into a small listItemKeymap helper.
Bubble menu: add a "Task list" entry to the list dropdown, with i18n strings for en / zh-Hans / ja / ko.
Styling (styles/prose.css): task lists are styled for both the editor (ul[data-type="taskList"]) and the read-only remark-gfm output (ul.contains-task-list), so both views match. Checkboxes use accent-color: var(--brand); completed items render muted.
Testing
pnpm --filter @multica/views run typecheck — clean
eslint on changed files — clean
pnpm --filter @multica/views test — 430 passed, including:
Neweditor/extensions/task-list-markdown.test.ts: round-trips - [ ] / - [x] (parse + serialize) through a real editor, confirms uppercase [X], confirms a plain bullet stays a bullet, and confirms a checkbox toggled in the editor serializes back to - [x].
New readonly case: - [x] renders a checked, disabled checkbox (checked state preserved).
Locale parity test passes with the new key.
Verified the produced DOM in jsdom for both modes to confirm the CSS selectors match (the editor's checkbox NodeView keeps data-checked on the <li>; readonly emits .task-list-item with the checked attribute).
Summary
Closes #3593 (MUL-2926). Renders
- [ ]/- [x]as interactive checkboxes in the issue content editor, the way GitHub / Notion do.Before, the read-only view (
ReadonlyContent, react-markdown + remark-gfm) already rendered task-list checkboxes, but the editable Tiptap editor parsed- [ ]as a plain bullet with literal[ ]text. This PR brings the editable view to parity.Changes
TaskList+ a patchedTaskItemin the shared extension factory (extensions/index.ts). Both ship their own markdown tokenizer /renderMarkdown, an input rule (typing[]/[x]), and a checkboxNodeView. ThetaskListtokenizer is consulted before marked's built-in list tokenizer, so- [ ]becomes a task while a plain-still falls through to the existing bullet list — no conflict.TaskItem's keymap to reusePatchedListItem'ssplit → liftEnter chain, so double-Enter on an empty task item exits the list (same fix bullet lists already have).nested: trueenables sub-tasks and nested markdown round-trips. The shared chain is factored into a smalllistItemKeymaphelper.en/zh-Hans/ja/ko.styles/prose.css): task lists are styled for both the editor (ul[data-type="taskList"]) and the read-only remark-gfm output (ul.contains-task-list), so both views match. Checkboxes useaccent-color: var(--brand); completed items render muted.Testing
pnpm --filter @multica/views run typecheck— cleaneslinton changed files — cleanpnpm --filter @multica/views test— 430 passed, including:editor/extensions/task-list-markdown.test.ts: round-trips- [ ]/- [x](parse + serialize) through a real editor, confirms uppercase[X], confirms a plain bullet stays a bullet, and confirms a checkbox toggled in the editor serializes back to- [x].- [x]renders a checked, disabled checkbox (checked state preserved).Verified the produced DOM in jsdom for both modes to confirm the CSS selectors match (the editor's checkbox NodeView keeps
data-checkedon the<li>; readonly emits.task-list-itemwith thecheckedattribute).