From d5a9f9ce0011571dd5ce3441595543d4a96539fd Mon Sep 17 00:00:00 2001 From: beo3000 Date: Fri, 27 Feb 2026 22:14:20 +0100 Subject: [PATCH] upd md editor --- ka-note/client/package.json | 1 + ka-note/client/src/app.css | 115 ++++++++++++++++++ .../src/lib/components/MarkdownEditor.svelte | 29 +++++ ka-note/client/src/lib/editor/editor.css | 45 +++++++ .../src/lib/editor/tiptapSlashCommand.ts | 107 ++++++++++++---- .../client/src/lib/utils/renderMarkdown.ts | 44 ++++++- ka-note/package-lock.json | 10 ++ ka-note/server/ka-note.db-shm | Bin 32768 -> 32768 bytes ka-note/server/ka-note.db-wal | Bin 4008792 -> 4194192 bytes 9 files changed, 329 insertions(+), 22 deletions(-) diff --git a/ka-note/client/package.json b/ka-note/client/package.json index 58353bb..6999f93 100644 --- a/ka-note/client/package.json +++ b/ka-note/client/package.json @@ -24,6 +24,7 @@ "@tiptap/starter-kit": "^3.20.0", "dexie": "^4.0.11", "dompurify": "^3.3.1", + "highlight.js": "^11.11.1", "marked": "^17.0.3", "tiptap-markdown": "^0.9.0" }, diff --git a/ka-note/client/src/app.css b/ka-note/client/src/app.css index a03a4f6..f4d11ed 100644 --- a/ka-note/client/src/app.css +++ b/ka-note/client/src/app.css @@ -2,6 +2,8 @@ @tailwind components; @tailwind utilities; +@import 'highlight.js/styles/github-dark-dimmed.css'; + :root { --scope-color: #555566; /* updated dynamically by layout */ --bg-color: #1a1a22; @@ -79,6 +81,87 @@ ul.tree-list li { margin-bottom: 0; } +/* Callout blocks */ +.callout { + border-radius: 6px; + padding: 0.6rem 0.9rem; + margin: 0.75rem 0; + border-left: 4px solid; +} + +.callout-title { + font-weight: 600; + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 0.25rem; +} + +.callout-body > :first-child { margin-top: 0; } +.callout-body > :last-child { margin-bottom: 0; } + +.callout-note { background: #1a2a3a; border-color: var(--accent); } +.callout-note .callout-title { color: var(--accent); } +.callout-info { background: #1a2a3a; border-color: var(--info); } +.callout-info .callout-title { color: var(--info); } +.callout-tip { background: #1a2e22; border-color: var(--success); } +.callout-tip .callout-title { color: var(--success); } +.callout-success { background: #1a2e22; border-color: var(--success); } +.callout-success .callout-title { color: var(--success); } +.callout-warning { background: #2e2510; border-color: var(--warning); } +.callout-warning .callout-title { color: var(--warning); } +.callout-danger { background: #2e1515; border-color: var(--danger); } +.callout-danger .callout-title { color: var(--danger); } +.callout-caution { background: #2e1515; border-color: var(--danger); } +.callout-caution .callout-title { color: var(--danger); } + +/* Tables */ +.markdown-content .table-wrapper { + overflow-x: auto; + margin: 0.75rem 0; +} + +.markdown-content table { + border-collapse: collapse; + width: 100%; + font-size: 0.875rem; +} + +.markdown-content th { + background: #2a2a38; + color: #e0e0e0; + font-weight: 600; + text-align: left; + padding: 0.4rem 0.75rem; + border: 1px solid var(--border-color); +} + +.markdown-content td { + padding: 0.35rem 0.75rem; + border: 1px solid var(--border-color); + vertical-align: top; +} + +.markdown-content tbody tr:nth-child(even) td { + background: #22222e; +} + +/* Typography tweaks */ +.markdown-content blockquote { + border-left: 3px solid var(--accent); + padding-left: 0.75rem; + color: #aaa; + font-style: italic; + margin-left: 0; +} + +.markdown-content hr { + border: none; + height: 1px; + background: linear-gradient(to right, transparent, var(--border-color), transparent); + margin: 1.25rem 0; +} + /* Tiptap Editor */ .ka-editor-wrapper { @apply rounded border border-[#444] bg-bg font-mono text-white; @@ -122,6 +205,38 @@ ul.tree-list li { padding-left: 20px; } +/* Table styles in editor */ +.ka-editor-wrapper .ProseMirror table { + border-collapse: collapse; + width: 100%; + font-size: 0.875rem; + margin: 0.5rem 0; + overflow-x: auto; + display: block; +} + +.ka-editor-wrapper .ProseMirror th, +.ka-editor-wrapper .ProseMirror td { + border: 1px solid #444; + padding: 0.3rem 0.6rem; + vertical-align: top; + min-width: 60px; + position: relative; +} + +.ka-editor-wrapper .ProseMirror th { + background: #2a2a38; + font-weight: 600; +} + +.ka-editor-wrapper .ProseMirror .selectedCell::after { + content: ''; + position: absolute; + inset: 0; + background: rgba(74, 158, 255, 0.15); + pointer-events: none; +} + /* Rating indicators on @NAME tags */ .person-ref.rating-1 { border-bottom: 3px solid #d9534f !important; diff --git a/ka-note/client/src/lib/components/MarkdownEditor.svelte b/ka-note/client/src/lib/components/MarkdownEditor.svelte index e51f4b0..3ec2574 100644 --- a/ka-note/client/src/lib/components/MarkdownEditor.svelte +++ b/ka-note/client/src/lib/components/MarkdownEditor.svelte @@ -28,6 +28,7 @@ let element: HTMLDivElement; let bubbleMenuEl: HTMLDivElement; + let tableMenuEl: HTMLDivElement; let editor: Editor | null = null; let skipNextUpdate = false; let lastExternalContent = $state(content); @@ -103,10 +104,17 @@ if (!view.hasFocus()) return false; if (from === to) return false; if (e.isActive('image')) return false; + if (e.isActive('table')) return false; const { empty } = e.state.selection; return !empty; }, }), + BubbleMenu.configure({ + element: tableMenuEl, + pluginKey: 'tableMenu', + tippyOptions: { duration: 100, placement: 'top-start', hideOnClick: false }, + shouldShow: ({ editor: e }) => e.isActive('table'), + }), ], content: initialContent, editorProps: { @@ -163,6 +171,27 @@
e.stopPropagation()}>
+
+ Zeile + + + + Spalte + + + + +
'); +} + +// --- Callout blocks post-processing --- + +const CALLOUT_ICONS: Record = { + note: '📝', info: 'ℹ️', tip: '💡', success: '✅', + warning: '⚠️', danger: '🚨', caution: '⚠️' +}; + +function processCallouts(html: string): string { + return html.replace( + /
\s*

\[!(NOTE|INFO|TIP|SUCCESS|WARNING|DANGER|CAUTION)\](.*?)<\/p>([\s\S]*?)<\/blockquote>/gi, + (_, type: string, title: string, body: string) => { + const t = type.toLowerCase(); + const icon = CALLOUT_ICONS[t] ?? ''; + const label = title.trim() || type.toUpperCase(); + const content = body.trim(); + return `

` + + `
${icon} ${label}
` + + (content ? `
${content}
` : '') + + `
`; + } + ); +} + // --- Collapsible list post-processing --- function addCollapsibleToggles(html: string): string { @@ -161,6 +201,8 @@ export function renderMarkdown(text: string): string { // tiptap-markdown escapes [ as \[ — restore [[WikiLinks]] before parsing const unescaped = text.replace(/\\\[\\\[(.+?)\\\]\\\]/g, '[[$1]]'); const raw = markedInstance.parse(unescaped) as string; - const sanitized = DOMPurify.sanitize(raw, PURIFY_CONFIG); + const withTables = wrapTables(raw); + const withCallouts = processCallouts(withTables); + const sanitized = DOMPurify.sanitize(withCallouts, PURIFY_CONFIG); return addCollapsibleToggles(sanitized); } diff --git a/ka-note/package-lock.json b/ka-note/package-lock.json index 5a0c85f..4a7e8dd 100644 --- a/ka-note/package-lock.json +++ b/ka-note/package-lock.json @@ -35,6 +35,7 @@ "@tiptap/starter-kit": "^3.20.0", "dexie": "^4.0.11", "dompurify": "^3.3.1", + "highlight.js": "^11.11.1", "marked": "^17.0.3", "tiptap-markdown": "^0.9.0" }, @@ -6595,6 +6596,15 @@ "he": "bin/he" } }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/hono": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.0.tgz", diff --git a/ka-note/server/ka-note.db-shm b/ka-note/server/ka-note.db-shm index 04d9b0c6d5c8ddcef479fb13ead29cb9b35af920..430e1cf5cea9522afa0f09e8a002f92548281a87 100644 GIT binary patch literal 32768 zcmeI5S5p*06on6$;Nd6jh1jgVDJnUmNZ{<_#!N+Re z%E$QsGsboQtqm?lCZk_IBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsJ77lG+`qE|!RbQ6Gqh`@RjVh0*=);vj~o8_D$0S^Rb;Hifdoc1SyKty5#im($W z%`;*Axfi`V6PSr-orn0n2MGjWK2~BQ5|N2wRAUzo;goqUQV%xa+O1~cxoflQ5WEi- zU==nY30WvX4R+%&PUF1}t+}9w*?8fCl#ECMRs`nYrPVOUx)2zNYhA{C9~g>@zMIg& zCV{zVwOQ%5vQcPuTV%)Q4aY0TXH*k#w0Z8W8ZWx&L13PF2D2W$=-tH_^F(O9dt`Us z5$KDBSdGm{Mm9=Oi#<4kGx*@0i7Qr*HP1qKMO4>>48|gSbWK`cf`*!>+56(aTHOfr z!(z1Q)|8h0j5F`-(6Ti)Ws^XEEJ3u*T6dL=H*c$PS7xWD9e^mOM^uz=0`4kuqKz&D zwjc#Lx-_G89}{s;>;9bH+)}JTEK-q+a%{yu98(au40ZUVz{mL(lgxYaROcLqOR6m$ z>bDGQ5r;J7p#s~mAIBBs+>4`VSK#A(i^=A#kE(Nq;Ie8}pNdC*XkrAe`oXn#EmvS25|Duc uRAD;~;)IfR2IIUEADY-95N6)ZZKskoDhLe01r;{*uti`RT5Q$zM$tdQ8ncrC literal 32768 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