diff --git a/package-lock.json b/package-lock.json index 95843c6bb6..2b2db70b24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,8 @@ "@tiptap/extension-table-cell": "^2.12.0", "@tiptap/extension-table-header": "^2.12.0", "@tiptap/extension-table-row": "^2.12.0", + "@tiptap/extension-task-item": "^2.25.0", + "@tiptap/extension-task-list": "^2.25.0", "@tiptap/extension-typography": "^2.10.0", "@tiptap/pm": "^2.11.7", "@tiptap/starter-kit": "^2.10.0", @@ -3247,6 +3249,33 @@ "@tiptap/core": "^2.7.0" } }, + "node_modules/@tiptap/extension-task-item": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-task-item/-/extension-task-item-2.25.0.tgz", + "integrity": "sha512-8F7Z7jbsyGrPLHQCn+n39zdqIgxwR1kJ1nL5ZwhEW3ZhJgkFF0WMJSv36mwIJwL08p8um/c6g72AYB/e8CD7eA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-task-list": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-task-list/-/extension-task-list-2.25.0.tgz", + "integrity": "sha512-2mASqp8MJ0dyc1OK6c8P7m/zwoVDv8PV+XsRR9O3tpIz/zjUVrOl0W4IndjUPBMa7cpJX8fGj8iC3DaRNpSMcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, "node_modules/@tiptap/extension-text": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.10.0.tgz", diff --git a/package.json b/package.json index dc29d03138..4bc2fadd65 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,8 @@ "@tiptap/extension-table-cell": "^2.12.0", "@tiptap/extension-table-header": "^2.12.0", "@tiptap/extension-table-row": "^2.12.0", + "@tiptap/extension-task-item": "^2.25.0", + "@tiptap/extension-task-list": "^2.25.0", "@tiptap/extension-typography": "^2.10.0", "@tiptap/pm": "^2.11.7", "@tiptap/starter-kit": "^2.10.0", diff --git a/src/app.css b/src/app.css index 045d75c34e..1bb282a7ea 100644 --- a/src/app.css +++ b/src/app.css @@ -330,6 +330,39 @@ input[type='number'] { @apply line-clamp-1 absolute; } +.tiptap ul[data-type='taskList'] { + list-style: none; + margin-left: 0; + padding: 0; + + li { + align-items: center; + + display: flex; + + > label { + flex: 0 0 auto; + margin-right: 0.5rem; + user-select: none; + display: flex; + } + + > div { + flex: 1 1 auto; + + align-items: center; + } + } + + input[type='checkbox'] { + cursor: pointer; + } + + ul[data-type='taskList'] { + margin: 0; + } +} + @media (prefers-color-scheme: dark) { .ProseMirror p.is-editor-empty:first-child::before { color: #757575; diff --git a/src/lib/components/common/RichTextInput.svelte b/src/lib/components/common/RichTextInput.svelte index 1da84766f2..a21286b51c 100644 --- a/src/lib/components/common/RichTextInput.svelte +++ b/src/lib/components/common/RichTextInput.svelte @@ -11,6 +11,18 @@ // Use turndown-plugin-gfm for proper GFM table support turndownService.use(gfm); + turndownService.addRule('taskListItems', { + filter: (node) => + node.nodeName === 'LI' && + (node.getAttribute('data-checked') === 'true' || + node.getAttribute('data-checked') === 'false'), + replacement: function (content, node) { + const checked = node.getAttribute('data-checked') === 'true'; + content = content.replace(/^\s+/, ''); + return `- [${checked ? 'x' : ' '}] ${content}\n`; + } + }); + import { onMount, onDestroy, tick } from 'svelte'; import { createEventDispatcher } from 'svelte'; @@ -27,6 +39,9 @@ import TableHeader from '@tiptap/extension-table-header'; import TableCell from '@tiptap/extension-table-cell'; + import TaskItem from '@tiptap/extension-task-item'; + import TaskList from '@tiptap/extension-task-list'; + import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'; import Placeholder from '@tiptap/extension-placeholder'; import StarterKit from '@tiptap/starter-kit'; @@ -437,6 +452,10 @@ TableRow, TableHeader, TableCell, + TaskList, + TaskItem.configure({ + nested: true + }), ...(autocomplete ? [ AIAutocompletion.configure({ @@ -464,6 +483,8 @@ const htmlValue = editor.getHTML(); const jsonValue = editor.getJSON(); + + console.log(htmlValue, jsonValue); let mdValue = turndownService .turndown( htmlValue