From bc73595018e50801be3487a5cc49b459219c3986 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 13 Apr 2026 16:03:37 +0700 Subject: [PATCH] Add WYSIWYG Markdown editor (EasyMDE) to wiki pages Reusable MarkdownEditor component using EasyMDE: - Toolbar: bold, italic, headings, quotes, lists, links, images, tables, code blocks, preview, side-by-side, fullscreen - Dark mode support with custom CSS overrides - Value syncs via bind:value for form submission - Applied to both wiki new and edit pages - Replaces the plain textarea + manual preview toggle Co-Authored-By: Claude Opus 4.6 (1M context) --- package-lock.json | 72 +++++++++++- package.json | 1 + src/lib/components/ui/MarkdownEditor.svelte | 111 ++++++++++++++++++ .../(app)/wiki/[slug]/edit/+page.svelte | 25 +--- src/routes/(app)/wiki/new/+page.svelte | 26 +--- 5 files changed, 189 insertions(+), 46 deletions(-) create mode 100644 src/lib/components/ui/MarkdownEditor.svelte diff --git a/package-lock.json b/package-lock.json index d2fa5f9..8e97d4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "date-fns": "^4.1.0", "dotenv": "^17.4.1", "drizzle-orm": "^0.38.4", + "easymde": "^2.20.0", "marked": "^18.0.0", "pg": "^8.13.1", "qrcode": "^1.5.4", @@ -2526,6 +2527,15 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/codemirror": { + "version": "5.60.17", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.17.tgz", + "integrity": "sha512-AZq2FIsUHVMlp7VSe2hTfl5w4pcUkoFkM3zVsRKsn1ca8CXRDYvnin04+HP2REkwsxemuHqvDofdlhUWNpbwfw==", + "license": "MIT", + "dependencies": { + "@types/tern": "*" + } + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -2537,7 +2547,12 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, + "license": "MIT" + }, + "node_modules/@types/marked": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.3.2.tgz", + "integrity": "sha512-a79Yc3TOk6dGdituy8hmTTJXjOkZ7zsFYV10L337ttq/rec8lRMDBpV7fL3uLx6TgbFCa5DU/h8FmIBQPSbU0w==", "license": "MIT" }, "node_modules/@types/node": { @@ -2579,6 +2594,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/tern": { + "version": "0.23.9", + "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", + "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -2719,6 +2743,21 @@ "node": ">=6" } }, + "node_modules/codemirror": { + "version": "5.65.21", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.21.tgz", + "integrity": "sha512-6teYk0bA0nR3QP0ihGMoxuKzpl5W80FpnHpBJpgy66NK3cZv5b/d/HY8PnRvfSsCG1MTfr92u2WUl+wT0E40mQ==", + "license": "MIT" + }, + "node_modules/codemirror-spell-checker": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz", + "integrity": "sha512-2Tl6n0v+GJRsC9K3MLCdLaMOmvWL0uukajNJseorZJsslaxZyZMgENocPU8R0DyoTAiKsyqiemSOZo7kjGV0LQ==", + "license": "MIT", + "dependencies": { + "typo-js": "*" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -3012,6 +3051,31 @@ } } }, + "node_modules/easymde": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.20.0.tgz", + "integrity": "sha512-V1Z5f92TfR42Na852OWnIZMbM7zotWQYTddNaLYZFVKj7APBbyZ3FYJ27gBw2grMW3R6Qdv9J8n5Ij7XRSIgXQ==", + "license": "MIT", + "dependencies": { + "@types/codemirror": "^5.60.10", + "@types/marked": "^4.0.7", + "codemirror": "^5.65.15", + "codemirror-spell-checker": "1.1.2", + "marked": "^4.1.0" + } + }, + "node_modules/easymde/node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -4867,6 +4931,12 @@ "node": ">=14.17" } }, + "node_modules/typo-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.3.1.tgz", + "integrity": "sha512-elJkpCL6Z77Ghw0Lv0lGnhBAjSTOQ5FhiVOCfOuxhaoTT2xtLVbqikYItK5HHchzPbHEUFAcjOH669T2ZzeCbg==", + "license": "BSD-3-Clause" + }, "node_modules/undici-types": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", diff --git a/package.json b/package.json index 745239b..03233a9 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "date-fns": "^4.1.0", "dotenv": "^17.4.1", "drizzle-orm": "^0.38.4", + "easymde": "^2.20.0", "marked": "^18.0.0", "pg": "^8.13.1", "qrcode": "^1.5.4", diff --git a/src/lib/components/ui/MarkdownEditor.svelte b/src/lib/components/ui/MarkdownEditor.svelte new file mode 100644 index 0000000..7c8006f --- /dev/null +++ b/src/lib/components/ui/MarkdownEditor.svelte @@ -0,0 +1,111 @@ + + +
+ + +
+ + diff --git a/src/routes/(app)/wiki/[slug]/edit/+page.svelte b/src/routes/(app)/wiki/[slug]/edit/+page.svelte index 51cefbd..21aacf6 100644 --- a/src/routes/(app)/wiki/[slug]/edit/+page.svelte +++ b/src/routes/(app)/wiki/[slug]/edit/+page.svelte @@ -1,14 +1,11 @@ @@ -54,24 +51,8 @@
-
- - -
- - {#if showPreview} -
- {@html renderedContent} -
- - {:else} - - {/if} + Content * (Markdown) +
diff --git a/src/routes/(app)/wiki/new/+page.svelte b/src/routes/(app)/wiki/new/+page.svelte index 99d277b..6f12f12 100644 --- a/src/routes/(app)/wiki/new/+page.svelte +++ b/src/routes/(app)/wiki/new/+page.svelte @@ -1,14 +1,11 @@ @@ -54,25 +51,8 @@
-
- - -
- - {#if showPreview} -
- {@html renderedContent} -
- - {:else} - - {/if} + Content * (Markdown) +