Files
crumbs-original/src/lib/utils/markdown.ts
T
Frederic CabassutandClaude Opus 4.6 b42dedfd53 feat: add task list button to rich text editor toolbar
Register TaskList and TaskItem TipTap extensions, add "Task list" option
to the List dropdown in the formatting toolbar, and consolidate task list
CSS between the editor and markdown-it preview into shared styles in
app.css.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 23:57:29 +01:00

56 lines
1.6 KiB
TypeScript

import MarkdownIt from 'markdown-it';
const md = new MarkdownIt({
html: false,
linkify: true,
typographer: true,
breaks: true
});
// Add task list support
md.core.ruler.after('inline', 'task-lists', (state) => {
const tokens = state.tokens;
for (let i = 0; i < tokens.length; i++) {
if (tokens[i].type !== 'inline') continue;
const content = tokens[i].content;
if (content.startsWith('[ ] ') || content.startsWith('[x] ')) {
const checked = content.startsWith('[x] ');
const checkbox = checked
? '<input type="checkbox" checked disabled class="task-checkbox" /> '
: '<input type="checkbox" disabled class="task-checkbox" /> ';
tokens[i].content = content.slice(4);
tokens[i].children = md.parseInline(tokens[i].content, state.env)[0].children;
// Prepend checkbox
const token = new state.Token('html_inline', '', 0);
token.content = checkbox;
tokens[i].children!.unshift(token);
// Add task-list class to parent <ul> (once)
for (let j = i - 1; j >= 0; j--) {
if (tokens[j].type === 'bullet_list_open') {
const cls = tokens[j].attrGet('class') ?? '';
if (!cls.includes('task-list')) {
tokens[j].attrJoin('class', 'task-list');
}
break;
}
}
}
}
});
export function renderMarkdown(content: string): string {
return md.render(content);
}
export function stripMarkdown(content: string): string {
// Simple strip for preview - remove markdown syntax
return content
.replace(/```[\s\S]*?```/g, '')
.replace(/`[^`]*`/g, '')
.replace(/!\[.*?\]\(.*?\)/g, '')
.replace(/\[([^\]]*)\]\(.*?\)/g, '$1')
.replace(/[#*_~\[\]]/g, '')
.trim();
}