diff --git a/._MARKDOWN_EDITOR_REWRITE_PLAN.md b/._MARKDOWN_EDITOR_REWRITE_PLAN.md new file mode 100755 index 00000000..3d095653 Binary files /dev/null and b/._MARKDOWN_EDITOR_REWRITE_PLAN.md differ diff --git a/._MARKDOWN_EDITOR_UX_FINDINGS.md b/._MARKDOWN_EDITOR_UX_FINDINGS.md new file mode 100755 index 00000000..3d095653 Binary files /dev/null and b/._MARKDOWN_EDITOR_UX_FINDINGS.md differ diff --git a/._tailwind.config.js b/._tailwind.config.js new file mode 100755 index 00000000..3d095653 Binary files /dev/null and b/._tailwind.config.js differ diff --git a/MARKDOWN_EDITOR_REWRITE_PLAN.md b/MARKDOWN_EDITOR_REWRITE_PLAN.md new file mode 100755 index 00000000..4201446b --- /dev/null +++ b/MARKDOWN_EDITOR_REWRITE_PLAN.md @@ -0,0 +1,46 @@ +# Markdown Editor - Architecture Rewrite Plan + +## Context & Motivation +The current Markdown Editor violates the core UX philosophy of the Dewe.Dev suite: `[input] program -> [process/edit] human -> [output] program`. + +Currently, users input Markdown and edit raw Markdown in a CodeMirror instance. To align with the rest of the suite (Object Editor, Table Editor, Invoice Editor), the tool must provide a **WYSIWYG (What You See Is What You Get) Rich Text Editor**. The user should interact with human-readable text visually, while the system seamlessly translates it back to raw Markdown/HTML for export. + +## Phase 1: Dependency Updates & Tiptap Integration +To achieve a robust WYSIWYG experience that translates perfectly to/from Markdown, we will use **Tiptap** (a headless wrapper around ProseMirror). + +**Dependencies to Install:** +* `@tiptap/react` +* `@tiptap/starter-kit` (Core formatting) +* `@tiptap/extension-link` (Hyperlinks) +* `@tiptap/extension-image` (Image support) +* `@tiptap/extension-table` (Table support) +* `@tiptap/extension-task-list` & `@tiptap/extension-task-item` +* `tiptap-markdown` (Crucial: Handles native parsing/serializing of markdown to the Tiptap state) + +**Dependencies to Remove:** +* `marked` (Tiptap handles parsing now) +* `dompurify` (Tiptap handles sanitization) +* Custom CodeMirror Markdown implementations inside `MarkdownEditor.js` (We will keep CodeMirror only for the final "Export Code" view, if needed). + +## Phase 2: Component Restructuring +Create a new component: `RichMarkdownEditor.js`. +This component will replace the split-view CodeMirror setup. + +**Features of `RichMarkdownEditor.js`:** +1. **Floating/Sticky Toolbar:** Similar to Google Docs or Notion. Bold, Italic, H1-H3, Lists, Blockquotes, Code Blocks. +2. **Interactive Editor Content:** The actual prose area where users type naturally. +3. **Two-way Binding:** When the Tiptap state updates, it immediately serializes the state to a hidden Markdown string, which is saved to `localStorage` (for data-loss prevention) and readied for export. + +## Phase 3: UX Flow Adjustments (Addressing Previous Findings) +* **Input Flow:** User pastes Markdown -> Tiptap parses it via `tiptap-markdown` -> User sees rendered Rich Text immediately (Fixes UX Gap #3 from previous findings). +* **View Modes:** Remove the complex "Split View" vs "Editor Only" logic. The primary view is *always* the Rich Text Editor. +* **File Uploads:** Restrict the `` to only accept `.md` and `.txt` files. Remove UI mentions of `.html` or `.docx` until a dedicated parser (like mammoth.js) is explicitly implemented (Fixes UX Gap #2). + +## Phase 4: Export Engine +The Export card will now feature: +1. **Raw Markdown:** A read-only CodeMirror block displaying the exact Markdown output generated by Tiptap, with "Copy" and "Download" buttons. +2. **HTML:** Tiptap's `editor.getHTML()` output. +3. **PDF Export:** Retain `html2pdf.js`, but explicitly inject CSS print rules (`break-inside: avoid`, `white-space: pre-wrap`) targeting Tiptap's code block classes to prevent page overflow (Fixes UX Gap #4). + +## Summary +By executing this rewrite, the Markdown Editor will transition from a basic code validator into a premium document translation hub, perfectly aligning with the product's overarching vision. diff --git a/MARKDOWN_EDITOR_UX_FINDINGS.md b/MARKDOWN_EDITOR_UX_FINDINGS.md new file mode 100755 index 00000000..d928799e --- /dev/null +++ b/MARKDOWN_EDITOR_UX_FINDINGS.md @@ -0,0 +1,21 @@ +# Markdown Editor UX Findings & Proposed Improvements + +## 1. Core Goal Misalignment (The Primary UX Defect) +**Current State:** The user inputs markdown (via paste, URL, file, or typing), and the primary editing experience happens in a raw text editor (CodeMirror) where they edit markdown syntax. The "rendered" version is read-only (Preview). +**The Gap:** As a developer tool, the goal is often to translate system language (markdown) into human language (rich text) *and vice-versa*. Humans expect a WYSIWYG (What You See Is What You Get) document editor experience. +**The Proposed Fix:** The "Preview" should actually be an interactive, editable Rich Text interface. A user should be able to input raw markdown, see the rich text, *edit the rich text visually like a Word document*, and then export the result back to raw markdown. + +## 2. File Import Format Limitations +**Current State:** The UI implies users can open files. +**The Gap:** `.html` and `.docx` imports require external libraries (`turndown`, `mammoth.js`) which are not yet fully implemented according to the Phase 2 roadmap. +**The Proposed Fix:** Restrict the `` to `.md` and `.txt` until the conversion logic is built, preventing users from loading raw binary/HTML into the text editor. + +## 3. View Mode Toggles & Workflow +**Current State:** Defaults to 'split' on desktop and 'editor' on mobile. After importing data, it jumps to the raw editor. +**The Gap:** Similar to the Object Editor, jumping straight to raw code isn't ideal. +**The Proposed Fix:** After data is provided, default to the (newly proposed) Rich Text / Preview mode first. + +## 4. PDF Export Styling +**Current State:** Uses `html2pdf.js` for export. +**The Gap:** Long code blocks in markdown often overflow the page boundaries when converted to PDF because CSS print media rules for `white-space: pre-wrap` or `break-inside` are missing. +**The Proposed Fix:** Inject print-specific CSS rules before triggering the PDF export to ensure code blocks wrap gracefully. diff --git a/package-lock.json b/package-lock.json index cd14637e..0560e4a6 100755 --- a/package-lock.json +++ b/package-lock.json @@ -22,9 +22,21 @@ "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", + "@tailwindcss/typography": "^0.5.20", "@testing-library/jest-dom": "^6.8.0", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", + "@tiptap/extension-code-block-lowlight": "^3.26.1", + "@tiptap/extension-image": "^3.26.1", + "@tiptap/extension-link": "^3.26.1", + "@tiptap/extension-table": "^3.26.1", + "@tiptap/extension-table-cell": "^3.26.1", + "@tiptap/extension-table-header": "^3.26.1", + "@tiptap/extension-table-row": "^3.26.1", + "@tiptap/extension-task-item": "^3.26.1", + "@tiptap/extension-task-list": "^3.26.1", + "@tiptap/react": "^3.26.1", + "@tiptap/starter-kit": "^3.26.1", "@uiw/react-codemirror": "^4.25.1", "codemirror": "^6.0.2", "diff-match-patch": "^1.0.5", @@ -35,6 +47,7 @@ "js-beautify": "^1.15.4", "jspdf": "^3.0.3", "jspdf-autotable": "^5.0.2", + "lowlight": "^3.3.0", "lucide-react": "^0.540.0", "marked": "^16.4.1", "marked-emoji": "^2.0.1", @@ -48,6 +61,8 @@ "reactflow": "^11.11.4", "serialize-javascript": "^6.0.0", "serve": "^14.2.4", + "tailwindcss-typography": "^3.1.0", + "tiptap-markdown": "^0.9.0", "turndown": "^7.2.1", "web-vitals": "^2.1.4" }, @@ -62,7 +77,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -2828,12 +2842,6 @@ "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", "license": "MIT" }, - "node_modules/@codemirror/view/node_modules/w3c-keyname": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", - "license": "MIT" - }, "node_modules/@csstools/normalize.css": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.1.1.tgz", @@ -2926,6 +2934,34 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT", + "optional": true + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -5788,7 +5824,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -5802,7 +5837,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -5816,7 +5850,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -5826,7 +5859,6 @@ "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -5836,7 +5868,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -5847,7 +5878,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -9175,6 +9205,18 @@ "node": ">= 6" } }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.20.tgz", + "integrity": "sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >=4.0.0 || insiders" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", @@ -9238,16 +9280,6 @@ "dequal": "^2.0.3" } }, - "node_modules/@testing-library/dom/node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/@testing-library/dom/node_modules/dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", @@ -9395,6 +9427,544 @@ "@testing-library/dom": ">=7.21.4" } }, + "node_modules/@tiptap/core": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.26.1.tgz", + "integrity": "sha512-TX9PyPqBoix0qDLjtok/bddtdSy54QhzLVha405C07V+WySOpH3s/pWYkywehZQY0SQtcrcY4MNSCeQjCbA28A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "3.26.1" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.26.1.tgz", + "integrity": "sha512-WaKjKmUaadgvZDDBk9JOn/oidlOFr6booqJIWHGL5S0aUUTKHS19oGfKQq/l9Z1y1niaRePk0Y4fy/jxCnfKPA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.26.1.tgz", + "integrity": "sha512-VIlF2sAiV6K009pcIDotfY8mvsPaq90dxeG9Q0ZIqfMD958TUCqjHw4MGYZf0/FgP12xksBfmcR7W312xgUf9Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.26.1.tgz", + "integrity": "sha512-Y3R9wFKP/U9M04JG+0PM/yW3OV+MSbUp6YBKQWZmUu8x6y7TbcNvDsaJ6QEFZt5aRMS6qH1ksYPTOz47JdjcfA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1", + "@tiptap/pm": "3.26.1" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.26.1.tgz", + "integrity": "sha512-JB6bEJJHxXNAXEXTIAN3/j70p1ARHdeMfhzshGZswWKUWtDibTCrspIp7p1VNeiuVtJ/HB6PpFkGi7yWtQ3RTg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.26.1" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.26.1.tgz", + "integrity": "sha512-t9/VR5k3rGPyhcGau9YvVgaAQ+nP9R9WzS996bQQ7GIrMOTSXb0FWwoQFBiYl83V6VA16Tlj/oScC7SFlA8lvA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.26.1.tgz", + "integrity": "sha512-NY7SYqcrqDVYTSWyaNGdSfCims6pOHoRQ2Rh4DEFb/rb8gLVkqbLZhcHzQCVfinlPqgV3xWF6cYMORwmnlBkXQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1", + "@tiptap/pm": "3.26.1" + } + }, + "node_modules/@tiptap/extension-code-block-lowlight": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-3.26.1.tgz", + "integrity": "sha512-DToQR8rJs/KeTU0KqCOdufkx8ujpFSWHZURUI8ajcX4z4nja8Q28OTvHflRQidsbF6i7Ps/3ga3odurXIpgyjw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1", + "@tiptap/extension-code-block": "3.26.1", + "@tiptap/pm": "3.26.1", + "highlight.js": "^11", + "lowlight": "^2 || ^3" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.26.1.tgz", + "integrity": "sha512-6W2vZjvi0Mv+4xEtwMDGhWwo7FotWR6eKfmntmduvehWevFpMxOKcTtyotjLigfZv738y50YWmvbaPuAPJG3BA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.26.1.tgz", + "integrity": "sha512-eVq3BvFIa3YD+pBIlj1i72vYEixlegGVKHnSYiVF2ovkQOSAH9sca7pkq6WgV1sMTCyWCU8e+WznTqtydvHUWA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "3.26.1" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.26.1.tgz", + "integrity": "sha512-xn0g4m/q2bjG+hULPwp6Aqb/6wpzUtc65jOhgJsG/S3Ey3kLJGUvZBuhozwNFu8FcugxM1fMUpNhkJkodCCGFw==", + "license": "MIT", + "optional": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@floating-ui/dom": "^1.0.0", + "@tiptap/core": "3.26.1", + "@tiptap/pm": "3.26.1" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.26.1.tgz", + "integrity": "sha512-BWW1yMQQA4TbEU0LLK+4cd9ebLTuZG5KjHwFMBRD/bGiRW9V1gTWFsCqThBbczcANoQiZK9pn5/4Ad/rGM3HUg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "3.26.1" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.26.1.tgz", + "integrity": "sha512-gzNb1e/fK6HN+ko1axsrasjK7F1q0Bnm0G4ZY/0eq7pV7s1wZuwoCiGbvUx/9LCFKRV6+94FTqlb0A3NbYN36g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.26.1.tgz", + "integrity": "sha512-eRlv9XxzUL8FobKAiF1WjP35CT2QpbcxxeyYFF7BmGEONvKI7r5g7JGwyGli4Cvclh70h8w6JuoXSmGUVEU65A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.26.1.tgz", + "integrity": "sha512-l9lPZYeSmY90y/2GkQcKaICFD5Atr8sx2SzJGkQzpNC9tRxZXyAHnfJE3OjBkspuGzjWIN0DimxBj4ibz58sKw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1", + "@tiptap/pm": "3.26.1" + } + }, + "node_modules/@tiptap/extension-image": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-3.26.1.tgz", + "integrity": "sha512-IjoT+kRK4a1sTImvUz257yfk5l9kMxXxfxCfix5AUKdiWyn8SGUjJZapLICcZVY05UDqXmwsBvBK9lHkKX5ERg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.26.1.tgz", + "integrity": "sha512-cLKYvOLToWEkJkAPspgIZ/PYDzAxacLm1VWcAq1tO1QDQCDe2Kw+y/zsGlyYEq/aKsAgpp4JNopBwAXRXxt2/A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.26.1.tgz", + "integrity": "sha512-aLLGLgikuhLFHRbjfUC6D4gRg+NUty4uhW7YkyVl8AxxPME47dPbCOX4H6uLCjEZcn3WnfNuCTr6HCTl0KEmGA==", + "license": "MIT", + "dependencies": { + "linkifyjs": "^4.3.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1", + "@tiptap/pm": "3.26.1" + } + }, + "node_modules/@tiptap/extension-list": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.26.1.tgz", + "integrity": "sha512-06nOjnyXpzMO8Ys5k3IbYsDsKib1mv2OtaxBYX1/1uvRyOKwUX5tqDLb/qigic0LIANNL73lkNC8Z8XPeG4Tkg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1", + "@tiptap/pm": "3.26.1" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.26.1.tgz", + "integrity": "sha512-5gLXJUiP763NA6i4HgrtcwUDXPP8820hsaBQyF1Y1VsXNi02uW9FVLe3RZK8jF0NZUNh9CqD0gogYJCbKOUU8A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.26.1" + } + }, + "node_modules/@tiptap/extension-list-keymap": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.26.1.tgz", + "integrity": "sha512-EReSayePO6SIxtRbxx+7KfBQreWHvoZmMb3O/RemfT8W6J0hCG5N/Rh8Z12+YZOnCDRXJ4RzFpAikYka3E54jQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.26.1" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.26.1.tgz", + "integrity": "sha512-LeFPeFwb7ylkQVuuaHj+niu7WhWHpjDOi1GKZJE/ohOa2lgt7P221HMqhUzPiDlXOExN72oWTNmXUlT0ymCTkw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.26.1" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.26.1.tgz", + "integrity": "sha512-OkBeYUNM3eTzjm3z6IcC3NHryOX8g3eGNI86P/B+tFoFQSRuzLsKZU50ARCfIiLLg812NjcqujeJ1eX3BKDZrw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.26.1.tgz", + "integrity": "sha512-7hmQ2mBsA+75GRrJIKYxb+10H23mblEQSGGsv9Ptl7JLaGmj+8sv2HGQGSUT9QBiBVprxaYTqyWFXQC9akfLWg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1" + } + }, + "node_modules/@tiptap/extension-table": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-3.26.1.tgz", + "integrity": "sha512-epxUhc5ecxsH39lzNejc2WxFPXAXWGs9g2ofKDrIaoSlZlfFHf89/sEGSz048a46E5Sb+fYCtzUvRUUx+aG4xw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1", + "@tiptap/pm": "3.26.1" + } + }, + "node_modules/@tiptap/extension-table-cell": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-3.26.1.tgz", + "integrity": "sha512-eCGgHrzIUPHZpz/z3F4O8yk+SM/HBcLVvAWTHl8P+4/GC2+6oVFH+9ixBDIMKiJugSOnuOY8uLm30+Ld/MtyTw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-table": "3.26.1" + } + }, + "node_modules/@tiptap/extension-table-header": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-3.26.1.tgz", + "integrity": "sha512-idVDYdhVpTL4hnzuf/MbE74HHjqqqIRCVwzfbTy/d5JnTnJ1LXpJZKz2oFWNOk5NaAq0kPhkwkz5lSBUgd2DbQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-table": "3.26.1" + } + }, + "node_modules/@tiptap/extension-table-row": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-3.26.1.tgz", + "integrity": "sha512-zAr7bQcUHoBpeysvbzxW8JchMduUn0wGwA2UeEgoE1K+gep74wRHs9LE8NRd70hARbZLzgUMRXcpT+W1pdoMMw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-table": "3.26.1" + } + }, + "node_modules/@tiptap/extension-task-item": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-task-item/-/extension-task-item-3.26.1.tgz", + "integrity": "sha512-VjwGkI7MJIswrfkArO4yWS9eAJq2KrCVsNIH96yUGkKve4cHrpDHfbCboLaj8453N98AGVtgY9GdVnAL6ypSAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.26.1" + } + }, + "node_modules/@tiptap/extension-task-list": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-task-list/-/extension-task-list-3.26.1.tgz", + "integrity": "sha512-d5eO6Ae6WqSZHd0lt16snD8u2PlZIjmKuvDQBjPYvzl5MUOmplcuOyaAsaphBJK5tr4u9pP9XDNVGB7Xjp0gHQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.26.1" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.26.1.tgz", + "integrity": "sha512-Gocui5WvcCCJJIX17gdOVCSdYi5H4fDwaR0qkMAUZPq5kJCdrfl+vNpt8BTt53Bk+/QumiUW21fhQ184w7RoeQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.26.1.tgz", + "integrity": "sha512-HUHtQ+DRWDM0opW7Nk3YQwrLzw876hMU7cr1X/ZTG+8Bp+AKHihlwU+bqrPgG5St0mqASyUEhHQ/vK5PlnUYOQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1" + } + }, + "node_modules/@tiptap/extensions": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.26.1.tgz", + "integrity": "sha512-PmRaoe6bebTgz/ZQrjmzwZMST1d9js9ZTiKnUXeXl3Fm+V5U/c3TbbKDfqmL63qPQdjtShDMHi9tYuv+c77OFQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1", + "@tiptap/pm": "3.26.1" + } + }, + "node_modules/@tiptap/pm": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.26.1.tgz", + "integrity": "sha512-48cJQRbvr9Ux0+IgM1BR5vOLU5hkC+n+uerdQy2JjrIRKpYE/huU8fQFm6PoRppoKYfilklzb29elsQ+n2TA+g==", + "license": "MIT", + "dependencies": { + "prosemirror-changeset": "^2.3.0", + "prosemirror-commands": "^1.6.2", + "prosemirror-dropcursor": "^1.8.1", + "prosemirror-gapcursor": "^1.3.2", + "prosemirror-history": "^1.4.1", + "prosemirror-inputrules": "^1.4.0", + "prosemirror-keymap": "^1.2.3", + "prosemirror-model": "^1.25.7", + "prosemirror-schema-list": "^1.5.0", + "prosemirror-state": "^1.4.4", + "prosemirror-tables": "^1.8.0", + "prosemirror-transform": "^1.12.0", + "prosemirror-view": "^1.41.8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tiptap/react": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.26.1.tgz", + "integrity": "sha512-Gl7AhTJM7pjQ2WFwdIwD736oQeqUcw3GVaXYmCKtwTSO3F9PszLgeKEp6DvM+CmctTNYhu/apRfzkH3vU0h0uA==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "fast-equals": "^5.3.3", + "use-sync-external-store": "^1.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "optionalDependencies": { + "@tiptap/extension-bubble-menu": "^3.26.1", + "@tiptap/extension-floating-menu": "^3.26.1" + }, + "peerDependencies": { + "@tiptap/core": "3.26.1", + "@tiptap/pm": "3.26.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tiptap/starter-kit": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.26.1.tgz", + "integrity": "sha512-A0zsvwGU9exLND34F8e8KqUXFSfs835tNN+VC+ZT3yNeaO/WXnlh/Cgal1F6pHHbcxy7RV2CRwJU5S3cWLPxrA==", + "license": "MIT", + "dependencies": { + "@tiptap/core": "^3.26.1", + "@tiptap/extension-blockquote": "^3.26.1", + "@tiptap/extension-bold": "^3.26.1", + "@tiptap/extension-bullet-list": "^3.26.1", + "@tiptap/extension-code": "^3.26.1", + "@tiptap/extension-code-block": "^3.26.1", + "@tiptap/extension-document": "^3.26.1", + "@tiptap/extension-dropcursor": "^3.26.1", + "@tiptap/extension-gapcursor": "^3.26.1", + "@tiptap/extension-hard-break": "^3.26.1", + "@tiptap/extension-heading": "^3.26.1", + "@tiptap/extension-horizontal-rule": "^3.26.1", + "@tiptap/extension-italic": "^3.26.1", + "@tiptap/extension-link": "^3.26.1", + "@tiptap/extension-list": "^3.26.1", + "@tiptap/extension-list-item": "^3.26.1", + "@tiptap/extension-list-keymap": "^3.26.1", + "@tiptap/extension-ordered-list": "^3.26.1", + "@tiptap/extension-paragraph": "^3.26.1", + "@tiptap/extension-strike": "^3.26.1", + "@tiptap/extension-text": "^3.26.1", + "@tiptap/extension-underline": "^3.26.1", + "@tiptap/extensions": "^3.26.1", + "@tiptap/pm": "^3.26.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -9576,6 +10146,15 @@ "@types/send": "*" } }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/http-errors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", @@ -9603,6 +10182,28 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/linkify-it": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", + "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "13.0.9", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.9.tgz", + "integrity": "sha512-1XPwR0+MgXLWfTn9gCsZ55AHOKW1WN+P9vr0PaQh5aerR9LLQXUbjfEAFhjmEmyoYFWAyuN2Mqkn40MZ4ukjBw==", + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^3", + "@types/mdurl": "^1" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", + "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", + "license": "MIT" + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -9642,6 +10243,26 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -9719,6 +10340,18 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -10207,7 +10840,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, "license": "MIT" }, "node_modules/arch": { @@ -10236,6 +10868,12 @@ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", "license": "MIT" }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, "node_modules/array.prototype.reduce": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.8.tgz", @@ -13987,7 +14625,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -14246,17 +14883,10 @@ "node": ">= 6" } }, - "node_modules/cheerio/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -14281,7 +14911,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -14295,7 +14924,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -14308,7 +14936,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -14321,7 +14948,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -14334,7 +14960,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -14347,7 +14972,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -14360,7 +14984,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -14370,7 +14993,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -14383,7 +15005,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -14393,7 +15014,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -14406,7 +15026,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -14419,7 +15038,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -14975,19 +15593,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/css-minimizer-webpack-plugin/node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/css-minimizer-webpack-plugin/node_modules/cssnano": { "version": "5.1.15", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", @@ -15745,13 +16350,6 @@ "node": ">=10.13.0" } }, - "node_modules/css-minimizer-webpack-plugin/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, "node_modules/css-minimizer-webpack-plugin/node_modules/yaml": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", @@ -15797,6 +16395,18 @@ "node": ">=0.10.0" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/cssstyle": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", @@ -15817,6 +16427,13 @@ "dev": true, "license": "MIT" }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT", + "peer": true + }, "node_modules/d3-color": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", @@ -15887,6 +16504,15 @@ "node": ">= 10" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/destroy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", @@ -15900,11 +16526,23 @@ "dev": true, "license": "MIT" }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true, "license": "Apache-2.0" }, "node_modules/diff-match-patch": { @@ -15917,7 +16555,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true, "license": "MIT" }, "node_modules/dns-packet": { @@ -16140,6 +16777,18 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-array-method-boxes-properly": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", @@ -19416,13 +20065,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, "node_modules/eslint/node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -20744,11 +21386,19 @@ "node": ">= 0.8" } }, + "node_modules/fast-equals": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", + "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -20765,7 +21415,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -20775,7 +21424,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -20788,7 +21436,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -20801,7 +21448,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -20814,7 +21460,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -20824,7 +21469,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -20837,7 +21481,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -20847,7 +21490,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -20861,7 +21503,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -20874,7 +21515,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -21181,7 +21821,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -21714,12 +22353,6 @@ "util-deprecate": "^1.0.2" } }, - "node_modules/highland/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, "node_modules/highlight.js": { "version": "11.11.1", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", @@ -23437,7 +24070,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -27478,7 +28110,6 @@ "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, "license": "MIT", "bin": { "jiti": "bin/jiti.js" @@ -27750,6 +28381,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/linkify-it": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz", + "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/linkifyjs": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.3.tgz", + "integrity": "sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==", + "license": "MIT" + }, "node_modules/loader-runner": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", @@ -27795,6 +28451,21 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, + "node_modules/lowlight": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz", + "integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "highlight.js": "~11.11.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lucide-react": { "version": "0.540.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.540.0.tgz", @@ -27850,6 +28521,39 @@ "tmpl": "1.0.5" } }, + "node_modules/markdown-it": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz", + "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.1", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-task-lists": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz", + "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==", + "license": "ISC" + }, "node_modules/marked": { "version": "16.4.2", "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", @@ -27871,6 +28575,12 @@ "marked": ">=4 <19" } }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -28114,7 +28824,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0", @@ -28126,7 +28835,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -29466,7 +30174,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -30712,6 +31419,12 @@ "wrappy": "1" } }, + "node_modules/orderedmap": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", + "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", + "license": "MIT" + }, "node_modules/p-retry": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", @@ -30751,7 +31464,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, "license": "MIT" }, "node_modules/pend": { @@ -30770,7 +31482,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -30780,7 +31491,6 @@ "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -30819,7 +31529,6 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, "license": "MIT", "dependencies": { "postcss-value-parser": "^4.0.0", @@ -30837,7 +31546,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -31004,19 +31712,6 @@ "postcss": "^8.1.0" } }, - "node_modules/postcss-modules-local-by-default/node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-modules-local-by-default/node_modules/icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", @@ -31044,13 +31739,6 @@ "node": ">=4" } }, - "node_modules/postcss-modules-local-by-default/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, "node_modules/postcss-modules-scope": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", @@ -31067,19 +31755,6 @@ "postcss": "^8.1.0" } }, - "node_modules/postcss-modules-scope/node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz", @@ -31094,18 +31769,10 @@ "node": ">=4" } }, - "node_modules/postcss-modules-scope/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, "node_modules/postcss-nested": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -31127,24 +31794,10 @@ "postcss": "^8.2.14" } }, - "node_modules/postcss-nested/node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-nested/node_modules/postcss-selector-parser": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", - "dev": true, "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -31154,13 +31807,6 @@ "node": ">=4" } }, - "node_modules/postcss-nested/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, "node_modules/postcss-normalize": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", @@ -31628,19 +32274,6 @@ ], "license": "CC0-1.0" }, - "node_modules/postcss-preset-env/node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-preset-env/node_modules/postcss-attribute-case-insensitive": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", @@ -32148,25 +32781,29 @@ "node": ">=4" } }, - "node_modules/postcss-preset-env/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, "license": "MIT" }, "node_modules/postcss/node_modules/nanoid": { "version": "3.3.12", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", - "dev": true, "funding": [ { "type": "github", @@ -32185,7 +32822,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -32270,6 +32906,178 @@ "dev": true, "license": "MIT" }, + "node_modules/prosemirror-changeset": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.4.1.tgz", + "integrity": "sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==", + "license": "MIT", + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz", + "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.10.2" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz", + "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.1.tgz", + "integrity": "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-history": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz", + "integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz", + "integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz", + "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-markdown": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.4.tgz", + "integrity": "sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw==", + "license": "MIT", + "dependencies": { + "@types/markdown-it": "^14.0.0", + "markdown-it": "^14.0.0", + "prosemirror-model": "^1.25.0" + } + }, + "node_modules/prosemirror-markdown/node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "license": "MIT" + }, + "node_modules/prosemirror-markdown/node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/prosemirror-markdown/node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "license": "MIT" + }, + "node_modules/prosemirror-model": { + "version": "1.25.8", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.8.tgz", + "integrity": "sha512-BswA4BLSFEiORV6Vjj/yZBXDbos1zTEnhyeSSgT8psGFhstQS7UJ8/WOLiDos9Byaee27+tml0/DuMNxYR84zg==", + "license": "MIT", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz", + "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.7.3" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", + "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" + } + }, + "node_modules/prosemirror-tables": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.5.tgz", + "integrity": "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.2.3", + "prosemirror-model": "^1.25.4", + "prosemirror-state": "^1.4.4", + "prosemirror-transform": "^1.10.5", + "prosemirror-view": "^1.41.4" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.12.0.tgz", + "integrity": "sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.41.9", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.9.tgz", + "integrity": "sha512-clTunTX+eaLbr87L1V1QPheRlEQJyTlL3gXe9x3jQIk3rL0RVWxviDGz8tFaydwIVm+hKhYCyr+R/zBtWr9s6A==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.25.8", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, "node_modules/psl": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", @@ -32293,6 +33101,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/puppeteer": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.20.0.tgz", @@ -32580,12 +33397,6 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "license": "MIT" }, - "node_modules/puppeteer/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, "node_modules/puppeteer/node_modules/ws": { "version": "6.2.4", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.4.tgz", @@ -32628,7 +33439,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -34164,7 +34974,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, "license": "MIT", "dependencies": { "pify": "^2.3.0" @@ -35518,7 +36327,6 @@ "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -35659,7 +36467,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -35669,7 +36476,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -35679,7 +36485,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -35692,7 +36497,6 @@ "version": "2.16.2", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", - "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.3" @@ -35792,6 +36596,12 @@ "node": ">=8" } }, + "node_modules/rope-sequence": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", + "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", + "license": "MIT" + }, "node_modules/sanitize.css": { "dev": true }, @@ -36991,13 +37801,6 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/spdy-transport/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, "node_modules/spdy/node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -37208,7 +38011,6 @@ "version": "3.35.1", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", @@ -37231,7 +38033,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -37242,7 +38043,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -37252,14 +38052,12 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/sucrase/node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -37270,7 +38068,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -37280,14 +38077,12 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, "license": "MIT" }, "node_modules/sucrase/node_modules/pirates": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -37324,7 +38119,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -37337,7 +38131,6 @@ "version": "3.4.19", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", - "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -37371,11 +38164,20 @@ "node": ">=14.0.0" } }, + "node_modules/tailwindcss-typography": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tailwindcss-typography/-/tailwindcss-typography-3.1.0.tgz", + "integrity": "sha512-uqovHMt9orPs1kl2U+bidlcO25tmUGJjjluQdI67yRnOy/IlqKe6tYcHHAsQPhehlk3r11/T3CE7Uc0PSUJcNg==", + "deprecated": "Use @tailwindcss/typography", + "license": "ISC", + "dependencies": { + "lodash": "^4.17.15" + } + }, "node_modules/tailwindcss/node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -37384,24 +38186,10 @@ "node": ">=8" } }, - "node_modules/tailwindcss/node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/tailwindcss/node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -37414,7 +38202,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -37427,7 +38214,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -37437,7 +38223,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -37450,7 +38235,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, "license": "MIT", "engines": { "node": ">=14" @@ -37463,7 +38247,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -37477,7 +38260,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -37487,7 +38269,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -37500,7 +38281,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", - "dev": true, "funding": [ { "type": "opencollective", @@ -37543,7 +38323,6 @@ "version": "6.1.4", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", - "dev": true, "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -37557,7 +38336,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -37566,18 +38344,10 @@ "node": ">=8.0" } }, - "node_modules/tailwindcss/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, "node_modules/tailwindcss/node_modules/yaml": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "dev": true, "license": "ISC", "optional": true, "peer": true, @@ -37849,7 +38619,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0" @@ -37859,7 +38628,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, "license": "MIT", "dependencies": { "thenify": ">= 3.1.0 < 4" @@ -37879,7 +38647,6 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -37896,7 +38663,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -37914,7 +38680,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -37923,6 +38688,24 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tiptap-markdown": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/tiptap-markdown/-/tiptap-markdown-0.9.0.tgz", + "integrity": "sha512-dKLQ9iiuGNgrlGVjrNauF/UBzWu4LYOx5pkD0jNkmQt/GOwfCJsBuzZTsf1jZ204ANHOm572mZ9PYvGh1S7tpQ==", + "license": "MIT", + "workspaces": [ + "example" + ], + "dependencies": { + "@types/markdown-it": "^13.0.7", + "markdown-it": "^14.1.0", + "markdown-it-task-lists": "^2.1.1", + "prosemirror-markdown": "^1.11.1" + }, + "peerDependencies": { + "@tiptap/core": "^3.0.1" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -37983,7 +38766,6 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, "license": "Apache-2.0" }, "node_modules/tslib": { @@ -38039,9 +38821,9 @@ } }, "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -38050,9 +38832,15 @@ "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=4.2.0" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", @@ -38164,6 +38952,12 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/util.promisify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", @@ -39430,6 +40224,12 @@ "node": ">= 12" } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, "node_modules/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", diff --git a/package.json b/package.json index 13e3fa7c..1e3f60d0 100755 --- a/package.json +++ b/package.json @@ -18,9 +18,21 @@ "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", + "@tailwindcss/typography": "^0.5.20", "@testing-library/jest-dom": "^6.8.0", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", + "@tiptap/extension-code-block-lowlight": "^3.26.1", + "@tiptap/extension-image": "^3.26.1", + "@tiptap/extension-link": "^3.26.1", + "@tiptap/extension-table": "^3.26.1", + "@tiptap/extension-table-cell": "^3.26.1", + "@tiptap/extension-table-header": "^3.26.1", + "@tiptap/extension-table-row": "^3.26.1", + "@tiptap/extension-task-item": "^3.26.1", + "@tiptap/extension-task-list": "^3.26.1", + "@tiptap/react": "^3.26.1", + "@tiptap/starter-kit": "^3.26.1", "@uiw/react-codemirror": "^4.25.1", "codemirror": "^6.0.2", "diff-match-patch": "^1.0.5", @@ -31,6 +43,7 @@ "js-beautify": "^1.15.4", "jspdf": "^3.0.3", "jspdf-autotable": "^5.0.2", + "lowlight": "^3.3.0", "lucide-react": "^0.540.0", "marked": "^16.4.1", "marked-emoji": "^2.0.1", @@ -44,6 +57,8 @@ "reactflow": "^11.11.4", "serialize-javascript": "^6.0.0", "serve": "^14.2.4", + "tailwindcss-typography": "^3.1.0", + "tiptap-markdown": "^0.9.0", "turndown": "^7.2.1", "web-vitals": "^2.1.4" }, diff --git a/src/components/._CodeBlockComponent.js b/src/components/._CodeBlockComponent.js new file mode 100755 index 00000000..3d095653 Binary files /dev/null and b/src/components/._CodeBlockComponent.js differ diff --git a/src/components/._RichMarkdownEditor.js b/src/components/._RichMarkdownEditor.js new file mode 100755 index 00000000..3d095653 Binary files /dev/null and b/src/components/._RichMarkdownEditor.js differ diff --git a/src/components/CodeBlockComponent.js b/src/components/CodeBlockComponent.js new file mode 100755 index 00000000..89e1d92d --- /dev/null +++ b/src/components/CodeBlockComponent.js @@ -0,0 +1,41 @@ +import React from "react"; +import { NodeViewWrapper, NodeViewContent } from "@tiptap/react"; +import { Copy } from "lucide-react"; + +const CodeBlockComponent = ({ node, updateAttributes, extension }) => { + const handleCopy = () => { + navigator.clipboard.writeText(node.textContent).then(() => { + // Optional: Add visual feedback for copy + }); + }; + + return ( + +
+ +
+
+        
+      
+
+ ); +}; + +export default CodeBlockComponent; diff --git a/src/components/RichMarkdownEditor.js b/src/components/RichMarkdownEditor.js new file mode 100755 index 00000000..6870acae --- /dev/null +++ b/src/components/RichMarkdownEditor.js @@ -0,0 +1,289 @@ +import React, { useEffect, useCallback } from "react"; +import { useEditor, EditorContent, ReactNodeViewRenderer } from "@tiptap/react"; +import StarterKit from "@tiptap/starter-kit"; +import Link from "@tiptap/extension-link"; +import Image from "@tiptap/extension-image"; +import { Table } from "@tiptap/extension-table"; +import { TableRow } from "@tiptap/extension-table-row"; +import { TableHeader } from "@tiptap/extension-table-header"; +import { TableCell } from "@tiptap/extension-table-cell"; +import { TaskList } from "@tiptap/extension-task-list"; +import { TaskItem } from "@tiptap/extension-task-item"; +import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight"; +import { common, createLowlight } from "lowlight"; +import CodeBlockComponent from "./CodeBlockComponent"; +import { Markdown } from "tiptap-markdown"; +import { + Bold, + Italic, + Strikethrough, + Code, + Heading1, + Heading2, + Heading3, + List, + ListOrdered, + CheckSquare, + Quote, + Link2, + Image as ImageIcon, + Table as TableIcon, + Minus, +} from "lucide-react"; + +// Set up lowlight for syntax highlighting in Tiptap +const lowlight = createLowlight(common); + +const MenuBar = ({ editor }) => { + if (!editor) return null; + + return ( +
+
+ + + + + +
+ + + + + +
+ + + + + +
+ + + + + +
+ + + + +
+
+ ); +}; + +const RichMarkdownEditor = ({ + initialContent, + onChange, + className = "", + height = "600px", + isFullscreen = false, +}) => { + const editor = useEditor({ + extensions: [ + StarterKit.configure({ + codeBlock: false, // We'll use our own codeblock extension + }), + CodeBlockLowlight.extend({ + addNodeView() { + return ReactNodeViewRenderer(CodeBlockComponent); + }, + }).configure({ + lowlight, + }), + Link.configure({ + openOnClick: false, + }), + Image, + Table.configure({ + resizable: true, + }), + TableRow, + TableHeader, + TableCell, + TaskList, + TaskItem.configure({ + nested: true, + }), + Markdown.configure({ + html: true, + tightLists: true, + tightListClass: "tight", + bulletListMarker: "-", + linkify: true, + breaks: false, + }), + ], + content: initialContent, + onUpdate: ({ editor }) => { + // Serialize back to markdown and send to parent + const markdownOutput = editor.storage.markdown.getMarkdown(); + const htmlOutput = editor.getHTML(); + onChange(markdownOutput, htmlOutput); + }, + editorProps: { + attributes: { + class: + "prose prose-sm sm:prose dark:prose-invert prose-blue focus:outline-none w-full max-w-none", + }, + }, + }); + + // Update editor content when initialContent prop completely changes from outside (e.g. loading a template) + useEffect(() => { + if (editor && initialContent !== undefined) { + const currentMarkdown = editor.storage.markdown.getMarkdown(); + if (initialContent !== currentMarkdown) { + editor.commands.setContent(initialContent); + } + } + }, [editor, initialContent]); + + return ( +
+ +
+
+ +
+
+
+ ); +}; + +export default RichMarkdownEditor; diff --git a/src/pages/._MarkdownEditor.js b/src/pages/._MarkdownEditor.js new file mode 100755 index 00000000..3d095653 Binary files /dev/null and b/src/pages/._MarkdownEditor.js differ diff --git a/src/pages/MarkdownEditor.js b/src/pages/MarkdownEditor.js index 90852990..dae9a6de 100755 --- a/src/pages/MarkdownEditor.js +++ b/src/pages/MarkdownEditor.js @@ -1,136 +1,148 @@ -import React, { useState, useRef, useEffect } from 'react'; -import { - FileText, Plus, Upload, Download, Globe, AlertTriangle, Edit3, - Eye, EyeOff, Columns, Type, Maximize2, Minimize2, - Bold, Italic, Underline, List, ListOrdered, Link2, Code, Table, Minus, - Heading, Quote, CheckSquare, ChevronUp, ChevronDown, FileDown -} from 'lucide-react'; -import ToolLayout from '../components/ToolLayout'; -import CodeMirrorEditor from '../components/CodeMirrorEditor'; -import SEO from '../components/SEO'; -import RelatedTools from '../components/RelatedTools'; -import { marked } from 'marked'; -import { markedEmoji } from 'marked-emoji'; -import DOMPurify from 'dompurify'; -import hljs from 'highlight.js'; -import html2pdf from 'html2pdf.js'; -import 'highlight.js/styles/github-dark.css'; -import '../styles/markdown-preview.css'; +import React, { useState, useRef, useEffect } from "react"; +import { + FileText, + Plus, + Upload, + Download, + AlertTriangle, + Edit3, + Eye, + Pencil, + EyeOff, + Maximize2, + Minimize2, + FileDown, + Globe, + ChevronUp, + ChevronDown, + Code, + Type, +} from "lucide-react"; +import ToolLayout from "../components/ToolLayout"; +import RichMarkdownEditor from "../components/RichMarkdownEditor"; +import CodeMirrorEditor from "../components/CodeMirrorEditor"; +import SEO from "../components/SEO"; +import RelatedTools from "../components/RelatedTools"; +import hljs from "highlight.js"; +import html2pdf from "html2pdf.js"; +import { marked } from "marked"; +import DOMPurify from "dompurify"; +import "highlight.js/styles/github-dark.css"; +import "../styles/markdown-preview.css"; const MarkdownEditor = () => { - const [markdownText, setMarkdownText] = useState(''); + const [markdownText, setMarkdownText] = useState(""); // Sync markdown data to localStorage for navigation guard useEffect(() => { - localStorage.setItem('markdownEditorData', markdownText); + localStorage.setItem("markdownEditorData", markdownText); }, [markdownText]); // State management following ObjectEditor pattern - const [activeTab, setActiveTab] = useState('create'); - const [inputText, setInputText] = useState(''); - const [error, setError] = useState(''); - // Default to 'editor' on mobile, 'split' on desktop - const [viewMode, setViewMode] = useState(() => { - return window.innerWidth < 1024 ? 'editor' : 'split'; - }); + const [activeTab, setActiveTab] = useState("create"); + const [inputText, setInputText] = useState(""); + const [error, setError] = useState(""); + const [viewMode, setViewMode] = useState("preview"); const [isFullscreen, setIsFullscreen] = useState(false); - const [fetchUrl, setFetchUrl] = useState(''); + const [fetchUrl, setFetchUrl] = useState(""); const [fetching, setFetching] = useState(false); const [showHeadingDropdown, setShowHeadingDropdown] = useState(false); const [createNewCompleted, setCreateNewCompleted] = useState(false); const [showInputChangeModal, setShowInputChangeModal] = useState(false); const [pendingTabChange, setPendingTabChange] = useState(null); const fileInputRef = useRef(null); + const [htmlText, setHtmlText] = useState(""); const [pasteCollapsed, setPasteCollapsed] = useState(false); + + // Handle rich editor updates + const handleEditorChange = (markdown, html) => { + setMarkdownText(markdown); + setHtmlText(html); + }; const [pasteDataSummary, setPasteDataSummary] = useState(null); const [urlDataSummary, setUrlDataSummary] = useState(null); const [fileDataSummary, setFileDataSummary] = useState(null); const [usageTipsExpanded, setUsageTipsExpanded] = useState(false); - // Configure marked with custom renderer for code blocks - useEffect(() => { - const renderer = new marked.Renderer(); - - // Custom code block renderer with header and copy button - renderer.code = function(token) { - // In marked v4+, parameters come as an object - const codeString = String(token.text || token || ''); - const language = token.lang || ''; - const normalizedLang = language ? language.toLowerCase().trim() : ''; - - let highlightedCode = codeString; - - // Apply syntax highlighting - if (normalizedLang && hljs.getLanguage(normalizedLang)) { - try { - const result = hljs.highlight(codeString, { language: normalizedLang }); - highlightedCode = result.value; - } catch (e) { - highlightedCode = codeString; - } - } else { - try { - const result = hljs.highlightAuto(codeString); - highlightedCode = result.value; - } catch (e) { - highlightedCode = codeString; - } - } - - const displayLang = normalizedLang || 'text'; - // Create a unique ID for this code block - const blockId = 'code-' + Math.random().toString(36).substr(2, 9); - - return ` -
-
- ${displayLang} - -
-
${highlightedCode}
-
- `; - }; - - marked.setOptions({ - gfm: true, - breaks: true, - renderer: renderer - }); - - // Enable GFM extensions including task lists - marked.use({ - gfm: true, - breaks: true - }); - - // Enable emoji support - marked.use(markedEmoji({ - emojis: {}, // Uses default emoji set - unicode: true // Use unicode emojis - })); - }, []); - - // Parse markdown to HTML with custom underline support - const parseMarkdown = (markdown) => { + // Fallback markdown parser if htmlText is empty (e.g., initial load) + const parseMarkdownFallback = (markdown) => { try { - // Convert __text__ to text before parsing (custom underline syntax) - // But preserve __ at start of line (which is for bold in some contexts) - let processed = markdown.replace(/(?$1'); - - const html = marked.parse(processed || ''); - return DOMPurify.sanitize(html, { - ADD_TAGS: ['u', 'button', 'input'], - ADD_ATTR: ['data-code-id', 'title', 'id', 'type', 'checked', 'disabled'] + // Create custom marked renderer to wrap code blocks with the header/copy UI + const renderer = new marked.Renderer(); + + renderer.code = function (token) { + const codeString = String(token.text || token || ""); + const language = token.lang || ""; + const normalizedLang = language + ? language.toLowerCase().trim() + : "text"; + + let highlightedCode = codeString; + + // Apply syntax highlighting + if (normalizedLang !== "text" && hljs.getLanguage(normalizedLang)) { + try { + const result = hljs.highlight(codeString, { + language: normalizedLang, + }); + highlightedCode = result.value; + } catch (e) { + highlightedCode = codeString; + } + } else { + try { + const result = hljs.highlightAuto(codeString); + highlightedCode = result.value; + } catch (e) { + highlightedCode = codeString; + } + } + + const displayLang = normalizedLang || "text"; + const blockId = "code-" + Math.random().toString(36).substr(2, 9); + + return ` +
+
+ ${displayLang} + +
+
${highlightedCode}
+
+ `; + }; + + const html = marked.parse(markdown || "", { renderer }); + return DOMPurify.sanitize(html, { + ADD_TAGS: [ + "button", + "svg", + "path", + "line", + "polyline", + "rect", + "circle", + "ellipse", + "polygon", + ], + ADD_ATTR: [ + "data-code-id", + "stroke", + "stroke-width", + "stroke-linecap", + "stroke-linejoin", + "fill", + "viewBox", + ], }); } catch (e) { - return '

Error parsing markdown

'; + return "

Error parsing markdown

"; } }; @@ -141,119 +153,49 @@ const MarkdownEditor = () => { } const words = markdownText.trim().split(/\s+/).length; const characters = markdownText.length; - const lines = markdownText.split('\n').length; + const lines = markdownText.split("\n").length; const readingTime = Math.ceil(words / 200); return { words, characters, lines, readingTime }; }; const stats = calculateStats(); - // Add event delegation for copy buttons and close dropdown on outside click useEffect(() => { const handleClick = (e) => { - // Handle copy button clicks - const button = e.target.closest('.code-block-copy'); + // Handle copy button clicks in Read mode + const button = e.target.closest(".code-block-copy"); if (button) { - const codeId = button.getAttribute('data-code-id'); + const codeId = button.getAttribute("data-code-id"); const codeElement = document.getElementById(codeId); - + if (codeElement) { const code = codeElement.textContent; - navigator.clipboard.writeText(code).then(() => { - const originalText = button.textContent; - button.textContent = 'Copied!'; - setTimeout(() => { - button.textContent = originalText; - }, 2000); - }).catch(err => { - // Failed to copy - }); + navigator.clipboard + .writeText(code) + .then(() => { + const originalText = button.innerHTML; + button.innerHTML = "Copied!"; + setTimeout(() => { + button.innerHTML = originalText; + }, 2000); + }) + .catch((err) => { + // Failed to copy + }); } return; } // Close heading dropdown if clicking outside - if (showHeadingDropdown && !e.target.closest('.relative')) { + if (showHeadingDropdown && !e.target.closest(".relative")) { setShowHeadingDropdown(false); } }; - document.addEventListener('click', handleClick); - return () => document.removeEventListener('click', handleClick); + document.addEventListener("click", handleClick); + return () => document.removeEventListener("click", handleClick); }, [showHeadingDropdown]); - // Toolbar formatting functions - proper CodeMirror integration with toggle support - const insertMarkdown = (before, after = '', placeholder = 'text', skipPlaceholder = false) => { - // Get CodeMirror view from the DOM - const editorElement = document.querySelector('.cm-editor'); - if (!editorElement) return; - - const view = editorElement.cmView?.view; - if (!view) return; - - const state = view.state; - const selection = state.selection.main; - - // Get selected text or use placeholder (unless skipPlaceholder is true) - const selectedText = state.doc.sliceString(selection.from, selection.to) || (skipPlaceholder ? '' : placeholder); - - // Check if text is already formatted (toggle support) - const beforeLen = before.length; - const afterLen = after.length; - const expandedFrom = Math.max(0, selection.from - beforeLen); - const expandedTo = Math.min(state.doc.length, selection.to + afterLen); - const expandedText = state.doc.sliceString(expandedFrom, expandedTo); - - // Check if already formatted - const isFormatted = expandedText.startsWith(before) && expandedText.endsWith(after); - - if (isFormatted && selectedText) { - // Remove formatting - view.dispatch({ - changes: { from: expandedFrom, to: expandedTo, insert: selectedText }, - selection: { - anchor: expandedFrom, - head: expandedFrom + selectedText.length - } - }); - } else { - // Add formatting - const formatted = `${before}${selectedText}${after}`; - view.dispatch({ - changes: { from: selection.from, to: selection.to, insert: formatted }, - selection: { - anchor: selection.from + before.length, - head: selection.from + before.length + selectedText.length - } - }); - } - - // Focus back to editor - view.focus(); - }; - - // Heading insertion helper - const insertHeading = (level) => { - const prefix = '#'.repeat(level) + ' '; - insertMarkdown(prefix, '', 'Heading'); - setShowHeadingDropdown(false); - }; - - const toolbarButtons = [ - { icon: Heading, label: 'Heading', action: () => setShowHeadingDropdown(!showHeadingDropdown), isDropdown: true, group: 'formatter' }, - { icon: Bold, label: 'Bold', action: () => insertMarkdown('**', '**', 'bold text'), group: 'formatter' }, - { icon: Italic, label: 'Italic', action: () => insertMarkdown('*', '*', 'italic text'), group: 'formatter' }, - { icon: Underline, label: 'Underline', action: () => insertMarkdown('__', '__', 'underlined text'), group: 'formatter' }, - { icon: Quote, label: 'Quote', action: () => insertMarkdown('> ', '', 'quote'), group: 'formatter' }, - { icon: Code, label: 'Code Block', action: () => insertMarkdown('\n```\n', '\n```\n', 'code'), group: 'formatter' }, - { icon: Link2, label: 'Link', action: () => insertMarkdown('[', '](url)', 'link text'), group: 'formatter' }, - { icon: List, label: 'Bullet List', action: () => insertMarkdown('- ', '', 'list item'), group: 'list' }, - { icon: ListOrdered, label: 'Numbered List', action: () => insertMarkdown('1. ', '', 'list item'), group: 'list' }, - { icon: CheckSquare, label: 'Task List', action: () => insertMarkdown('- [ ] ', '', 'task'), group: 'list' }, - { icon: Minus, label: 'Divider', action: () => insertMarkdown('\n---\n', '', '', true), group: 'element' }, - { icon: Table, label: 'Table', action: () => insertMarkdown('\n| Header 1 | Header 2 |\n|----------|----------|\n| Cell 1 | Cell 2 |', '', '', true), group: 'element' }, - ]; - // Sample markdown const sampleMarkdown = `# Welcome to Markdown Editor @@ -380,8 +322,8 @@ MIT License - see LICENSE file for details blog: `# Blog Post Title -**Published:** ${new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })} -**Author:** Your Name +**Published:** ${new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" })} +**Author:** Your Name **Tags:** #tag1 #tag2 #tag3 --- @@ -516,8 +458,8 @@ For support, email support@example.com or visit our [Help Center](https://help.e meeting: `# Meeting Notes -**Date:** ${new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })} -**Time:** [Start Time] - [End Time] +**Date:** ${new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" })} +**Time:** [Start Time] - [End Time] **Location:** [Location/Video Call Link] ## Attendees @@ -563,7 +505,7 @@ For support, email support@example.com or visit our [Help Center](https://help.e | Task 3 | Person C | 2025-01-10 | ✅ Complete | ## Next Meeting -**Date:** [Next Meeting Date] +**Date:** [Next Meeting Date] **Agenda Items:** - Follow up on action items - [Other topics] @@ -599,7 +541,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Security - Security improvements -## [1.0.0] - ${new Date().toISOString().split('T')[0]} +## [1.0.0] - ${new Date().toISOString().split("T")[0]} ### Added - Initial release @@ -641,10 +583,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [Unreleased]: https://github.com/username/repo/compare/v1.0.0...HEAD [1.0.0]: https://github.com/username/repo/compare/v0.2.0...v1.0.0 [0.2.0]: https://github.com/username/repo/compare/v0.1.0...v0.2.0 -[0.1.0]: https://github.com/username/repo/releases/tag/v0.1.0` +[0.1.0]: https://github.com/username/repo/releases/tag/v0.1.0`, }; - const [selectedTemplate, setSelectedTemplate] = useState('sample'); + const [selectedTemplate, setSelectedTemplate] = useState("sample"); // Helper function to check if user has data that would be lost const hasUserData = () => { @@ -660,7 +602,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 // Handle tab change with confirmation if data exists const handleTabChange = (newTab) => { - if (newTab === 'create' && activeTab !== 'create') { + if (newTab === "create" && activeTab !== "create") { if (hasModifiedData()) { setPendingTabChange(newTab); setShowInputChangeModal(true); @@ -673,7 +615,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 setShowInputChangeModal(true); } else { setActiveTab(newTab); - if (newTab === 'create' && createNewCompleted) { + if (newTab === "create" && createNewCompleted) { setCreateNewCompleted(false); } } @@ -681,9 +623,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 // Clear all data function const clearAllData = () => { - setMarkdownText(''); - setInputText(''); - setError(''); + setMarkdownText(""); + setHtmlText(""); // Also clear HTML text to force re-render + setInputText(""); + setError(""); setCreateNewCompleted(false); setPasteCollapsed(false); setPasteDataSummary(null); @@ -693,22 +636,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 // Confirm input method change and clear data const confirmInputChange = () => { - if (pendingTabChange === 'create_empty') { + if (pendingTabChange === "create_empty") { clearAllData(); - setMarkdownText(''); + setMarkdownText(""); + setHtmlText(""); setCreateNewCompleted(true); - } else if (pendingTabChange === 'create_sample') { + } else if (pendingTabChange === "create_sample") { clearAllData(); setMarkdownText(templates[selectedTemplate]); + setHtmlText(""); setCreateNewCompleted(true); } else { clearAllData(); setActiveTab(pendingTabChange); - if (pendingTabChange === 'create') { + if (pendingTabChange === "create") { setCreateNewCompleted(false); } } - + setShowInputChangeModal(false); setPendingTabChange(null); }; @@ -722,18 +667,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 // Handle Parse Markdown button click const handleParseMarkdown = () => { if (!inputText.trim()) { - setError('Please enter some markdown text'); + setError("Please enter some markdown text"); setPasteCollapsed(false); return; } setMarkdownText(inputText); - setError(''); + setHtmlText(""); // Clear html text to force the fallback to parse the new markdown immediately + setError(""); setCreateNewCompleted(true); setPasteDataSummary({ - format: 'Markdown', + format: "Markdown", size: inputText.length, - lines: inputText.split('\n').length + lines: inputText.split("\n").length, }); setPasteCollapsed(true); }; @@ -747,10 +693,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 reader.onload = (e) => { let content = e.target.result; - let format = 'Markdown'; + let format = "Markdown"; // Detect file type and convert if needed - if (file.name.endsWith('.html') || file.name.endsWith('.htm')) { + if (file.name.endsWith(".html") || file.name.endsWith(".htm")) { // Extract content from HTML body if it's a full HTML document const bodyMatch = content.match(/]*>([\s\S]*)<\/body>/i); if (bodyMatch) { @@ -758,28 +704,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 } // Convert HTML to markdown (basic conversion) content = htmlToMarkdown(content); - format = 'HTML (converted)'; - } else if (file.name.endsWith('.txt')) { + format = "HTML (converted)"; + } else if (file.name.endsWith(".txt")) { // Plain text - treat as markdown-ready content - format = 'Plain Text'; + format = "Plain Text"; } setMarkdownText(content); - setActiveTab('open'); + setHtmlText(""); // Clear html text to force the fallback to parse the new markdown immediately + setActiveTab("open"); setCreateNewCompleted(true); - setError(''); + setError(""); // Update file data summary setFileDataSummary({ format: format, size: content.length, - lines: content.split('\n').length, - filename: file.name + lines: content.split("\n").length, + filename: file.name, }); }; reader.onerror = () => { - setError('Failed to read file'); + setError("Failed to read file"); }; reader.readAsText(file); @@ -788,21 +735,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 // Fetch markdown from URL const handleFetchFromURL = async () => { if (!fetchUrl.trim()) { - setError('Please enter a URL'); + setError("Please enter a URL"); return; } setFetching(true); - setError(''); + setError(""); try { let urlToFetch = fetchUrl.trim(); - + // Convert GitHub URLs to raw URLs - if (urlToFetch.includes('github.com') && !urlToFetch.includes('raw.githubusercontent.com')) { + if ( + urlToFetch.includes("github.com") && + !urlToFetch.includes("raw.githubusercontent.com") + ) { urlToFetch = urlToFetch - .replace('github.com', 'raw.githubusercontent.com') - .replace('/blob/', '/'); + .replace("github.com", "raw.githubusercontent.com") + .replace("/blob/", "/"); } // Try direct fetch first @@ -811,7 +761,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 response = await fetch(urlToFetch); } catch (corsError) { // If CORS error, try with CORS proxy - response = await fetch(`https://api.allorigins.win/raw?url=${encodeURIComponent(urlToFetch)}`); + response = await fetch( + `https://api.allorigins.win/raw?url=${encodeURIComponent(urlToFetch)}`, + ); } if (!response.ok) { @@ -819,33 +771,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 } const text = await response.text(); - + if (!text || text.trim().length === 0) { - throw new Error('URL returned empty content'); + throw new Error("URL returned empty content"); } // Detect format - let format = 'Markdown'; - if (urlToFetch.endsWith('.html') || urlToFetch.endsWith('.htm')) { - format = 'HTML (converted)'; + let format = "Markdown"; + if (urlToFetch.endsWith(".html") || urlToFetch.endsWith(".htm")) { + format = "HTML (converted)"; const converted = htmlToMarkdown(text); setMarkdownText(converted); + setHtmlText(""); // Clear html text to force the fallback to parse the new markdown immediately } else { setMarkdownText(text); + setHtmlText(""); // Clear html text to force the fallback to parse the new markdown immediately } - setActiveTab('url'); + setActiveTab("url"); setCreateNewCompleted(true); - setError(''); + setError(""); // Update URL data summary setUrlDataSummary({ format: format, size: text.length, - lines: text.split('\n').length, - url: urlToFetch + lines: text.split("\n").length, + url: urlToFetch, }); - } catch (err) { setError(`Failed to fetch from URL: ${err.message}`); } finally { @@ -856,146 +809,188 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 // HTML to Markdown converter - designed to reverse our exact export format const htmlToMarkdown = (html) => { // Create a temporary DOM element to parse HTML properly - const tempDiv = document.createElement('div'); + const tempDiv = document.createElement("div"); tempDiv.innerHTML = html; - + // Remove script and style tags - tempDiv.querySelectorAll('script, style').forEach(el => el.remove()); - + tempDiv.querySelectorAll("script, style").forEach((el) => el.remove()); + // Process our custom code-block-wrapper structure - tempDiv.querySelectorAll('.code-block-wrapper').forEach(wrapper => { - const langSpan = wrapper.querySelector('.code-block-language'); - const codeElement = wrapper.querySelector('code'); - + tempDiv.querySelectorAll(".code-block-wrapper").forEach((wrapper) => { + const langSpan = wrapper.querySelector(".code-block-language"); + const codeElement = wrapper.querySelector("code"); + if (codeElement) { - const language = langSpan ? langSpan.textContent.trim() : ''; + const language = langSpan ? langSpan.textContent.trim() : ""; // Get the text content directly (this preserves the actual code) let code = codeElement.textContent; - + // Create markdown code block - const codeBlock = document.createTextNode(`\n\`\`\`${language}\n${code}\n\`\`\`\n\n`); + const codeBlock = document.createTextNode( + `\n\`\`\`${language}\n${code}\n\`\`\`\n\n`, + ); wrapper.parentNode.replaceChild(codeBlock, wrapper); } }); - + // Handle regular code blocks (without our wrapper) - tempDiv.querySelectorAll('pre > code').forEach(codeElement => { + tempDiv.querySelectorAll("pre > code").forEach((codeElement) => { const pre = codeElement.parentElement; const classMatch = codeElement.className.match(/language-(\w+)/); - const language = classMatch ? classMatch[1] : ''; + const language = classMatch ? classMatch[1] : ""; let code = codeElement.textContent; - + // Create markdown code block - const codeBlock = document.createTextNode(`\n\`\`\`${language}\n${code}\n\`\`\`\n\n`); + const codeBlock = document.createTextNode( + `\n\`\`\`${language}\n${code}\n\`\`\`\n\n`, + ); pre.parentNode.replaceChild(codeBlock, pre); }); - + // Get the processed HTML let markdown = tempDiv.innerHTML; - + // Headers - markdown = markdown.replace(/]*>([\s\S]*?)<\/h1>/gi, '# $1\n\n'); - markdown = markdown.replace(/]*>([\s\S]*?)<\/h2>/gi, '## $1\n\n'); - markdown = markdown.replace(/]*>([\s\S]*?)<\/h3>/gi, '### $1\n\n'); - markdown = markdown.replace(/]*>([\s\S]*?)<\/h4>/gi, '#### $1\n\n'); - markdown = markdown.replace(/]*>([\s\S]*?)<\/h5>/gi, '##### $1\n\n'); - markdown = markdown.replace(/]*>([\s\S]*?)<\/h6>/gi, '###### $1\n\n'); - + markdown = markdown.replace(/]*>([\s\S]*?)<\/h1>/gi, "# $1\n\n"); + markdown = markdown.replace(/]*>([\s\S]*?)<\/h2>/gi, "## $1\n\n"); + markdown = markdown.replace(/]*>([\s\S]*?)<\/h3>/gi, "### $1\n\n"); + markdown = markdown.replace(/]*>([\s\S]*?)<\/h4>/gi, "#### $1\n\n"); + markdown = markdown.replace(/]*>([\s\S]*?)<\/h5>/gi, "##### $1\n\n"); + markdown = markdown.replace(/]*>([\s\S]*?)<\/h6>/gi, "###### $1\n\n"); + // Bold and Italic - markdown = markdown.replace(/]*>([\s\S]*?)<\/strong>/gi, '**$1**'); - markdown = markdown.replace(/]*>([\s\S]*?)<\/b>/gi, '**$1**'); - markdown = markdown.replace(/]*>([\s\S]*?)<\/em>/gi, '*$1*'); - markdown = markdown.replace(/]*>([\s\S]*?)<\/i>/gi, '*$1*'); - markdown = markdown.replace(/]*>([\s\S]*?)<\/u>/gi, '__$1__'); - + markdown = markdown.replace( + /]*>([\s\S]*?)<\/strong>/gi, + "**$1**", + ); + markdown = markdown.replace(/]*>([\s\S]*?)<\/b>/gi, "**$1**"); + markdown = markdown.replace(/]*>([\s\S]*?)<\/em>/gi, "*$1*"); + markdown = markdown.replace(/]*>([\s\S]*?)<\/i>/gi, "*$1*"); + markdown = markdown.replace(/]*>([\s\S]*?)<\/u>/gi, "__$1__"); + // Links - markdown = markdown.replace(/]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi, '[$2]($1)'); - + markdown = markdown.replace( + /]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi, + "[$2]($1)", + ); + // Images - markdown = markdown.replace(/]*src="([^"]*)"[^>]*alt="([^"]*)"[^>]*>/gi, '![$2]($1)'); - markdown = markdown.replace(/]*alt="([^"]*)"[^>]*src="([^"]*)"[^>]*>/gi, '![$1]($2)'); - + markdown = markdown.replace( + /]*src="([^"]*)"[^>]*alt="([^"]*)"[^>]*>/gi, + "![$2]($1)", + ); + markdown = markdown.replace( + /]*alt="([^"]*)"[^>]*src="([^"]*)"[^>]*>/gi, + "![$1]($2)", + ); + // Inline code (must come after code blocks) - markdown = markdown.replace(/]*>([\s\S]*?)<\/code>/gi, '`$1`'); - + markdown = markdown.replace(/]*>([\s\S]*?)<\/code>/gi, "`$1`"); + // Tables - markdown = markdown.replace(/]*>([\s\S]*?)<\/table>/gi, (match, content) => { - let tableMarkdown = '\n'; - const rows = content.match(/]*>([\s\S]*?)<\/tr>/gi) || []; - - rows.forEach((row, index) => { - const cells = row.match(/]*>([\s\S]*?)<\/t[hd]>/gi) || []; - const cellContents = cells.map(cell => cell.replace(/<[^>]+>/g, '').trim()); - tableMarkdown += '| ' + cellContents.join(' | ') + ' |\n'; - - // Add separator after header row - if (index === 0) { - tableMarkdown += '| ' + cellContents.map(() => '---').join(' | ') + ' |\n'; - } - }); - - return tableMarkdown + '\n'; - }); - + markdown = markdown.replace( + /]*>([\s\S]*?)<\/table>/gi, + (match, content) => { + let tableMarkdown = "\n"; + const rows = content.match(/]*>([\s\S]*?)<\/tr>/gi) || []; + + rows.forEach((row, index) => { + const cells = row.match(/]*>([\s\S]*?)<\/t[hd]>/gi) || []; + const cellContents = cells.map((cell) => + cell.replace(/<[^>]+>/g, "").trim(), + ); + tableMarkdown += "| " + cellContents.join(" | ") + " |\n"; + + // Add separator after header row + if (index === 0) { + tableMarkdown += + "| " + cellContents.map(() => "---").join(" | ") + " |\n"; + } + }); + + return tableMarkdown + "\n"; + }, + ); + // Lists - handle nested lists - markdown = markdown.replace(/]*>([\s\S]*?)<\/li>/gi, (match, content) => { - content = content.trim(); - return '- ' + content + '\n'; - }); - markdown = markdown.replace(/]*>([\s\S]*?)<\/ul>/gi, '$1\n'); - markdown = markdown.replace(/]*>([\s\S]*?)<\/ol>/gi, '$1\n'); - + markdown = markdown.replace( + /]*>([\s\S]*?)<\/li>/gi, + (match, content) => { + content = content.trim(); + return "- " + content + "\n"; + }, + ); + markdown = markdown.replace(/]*>([\s\S]*?)<\/ul>/gi, "$1\n"); + markdown = markdown.replace(/]*>([\s\S]*?)<\/ol>/gi, "$1\n"); + // Blockquotes - markdown = markdown.replace(/]*>([\s\S]*?)<\/blockquote>/gi, (match, content) => { - // Remove HTML tags from content first - content = content.replace(/<[^>]+>/g, ''); - return '\n' + content.split('\n').filter(line => line.trim()).map(line => '> ' + line.trim()).join('\n') + '\n\n'; - }); - + markdown = markdown.replace( + /]*>([\s\S]*?)<\/blockquote>/gi, + (match, content) => { + // Remove HTML tags from content first + content = content.replace(/<[^>]+>/g, ""); + return ( + "\n" + + content + .split("\n") + .filter((line) => line.trim()) + .map((line) => "> " + line.trim()) + .join("\n") + + "\n\n" + ); + }, + ); + // Paragraphs and breaks - markdown = markdown.replace(/]*>([\s\S]*?)<\/p>/gi, (match, content) => { - content = content.trim(); - return content + '\n\n'; - }); - markdown = markdown.replace(//gi, '\n'); - markdown = markdown.replace(//gi, '\n---\n\n'); - + markdown = markdown.replace( + /]*>([\s\S]*?)<\/p>/gi, + (match, content) => { + content = content.trim(); + return content + "\n\n"; + }, + ); + markdown = markdown.replace(//gi, "\n"); + markdown = markdown.replace(//gi, "\n---\n\n"); + // Remove remaining HTML tags - markdown = markdown.replace(/<[^>]+>/g, ''); - + markdown = markdown.replace(/<[^>]+>/g, ""); + // Decode HTML entities (that might still be in non-code content) - markdown = markdown.replace(/</g, '<'); - markdown = markdown.replace(/>/g, '>'); - markdown = markdown.replace(/&/g, '&'); + markdown = markdown.replace(/</g, "<"); + markdown = markdown.replace(/>/g, ">"); + markdown = markdown.replace(/&/g, "&"); markdown = markdown.replace(/"/g, '"'); markdown = markdown.replace(/'/g, "'"); - markdown = markdown.replace(/ /g, ' '); - + markdown = markdown.replace(/ /g, " "); + // Clean up extra whitespace gently // Remove multiple consecutive blank lines (3+ becomes 2) - markdown = markdown.replace(/\n{3,}/g, '\n\n'); - + markdown = markdown.replace(/\n{3,}/g, "\n\n"); + // Remove leading spaces from each line (but preserve code block indentation) let inCodeBlock = false; - markdown = markdown.split('\n').map(line => { - // Check if this line starts or ends a code block - if (line.trim().startsWith('```')) { - inCodeBlock = !inCodeBlock; - return line.trimStart(); // Trim the ``` line itself - } - - // If we're inside a code block, preserve indentation - if (inCodeBlock) { - return line; - } - - // Outside code blocks, remove leading spaces - return line.trimStart(); - }).join('\n'); - + markdown = markdown + .split("\n") + .map((line) => { + // Check if this line starts or ends a code block + if (line.trim().startsWith("```")) { + inCodeBlock = !inCodeBlock; + return line.trimStart(); // Trim the ``` line itself + } + + // If we're inside a code block, preserve indentation + if (inCodeBlock) { + return line; + } + + // Outside code blocks, remove leading spaces + return line.trimStart(); + }) + .join("\n"); + // Remove leading/trailing whitespace from the entire document markdown = markdown.trim(); - + return markdown; }; @@ -1015,18 +1010,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 // Export handlers const handleExportMarkdown = () => { if (!markdownText.trim()) { - setError('No content to export'); + setError("No content to export"); return; } - downloadFile(markdownText, 'document.md', 'text/markdown'); + downloadFile(markdownText, "document.md", "text/markdown"); }; const handleExportHTML = () => { if (!markdownText.trim()) { - setError('No content to export'); + setError("No content to export"); return; } - const html = parseMarkdown(markdownText); + + // Determine HTML to use: if htmlText is empty, fall back to marked + const html = htmlText || parseMarkdownFallback(markdownText); const fullHTML = ` @@ -1036,15 +1033,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 -${html} + ${html} `; - downloadFile(fullHTML, 'document.html', 'text/html'); + downloadFile(fullHTML, "document.html", "text/html"); }; const handleExportHTMLContent = () => { if (!markdownText.trim()) { - setError('No content to export'); + setError("No content to export"); return; } - let html = parseMarkdown(markdownText); - - // Clean up the HTML content by removing code-block wrappers - const tempDiv = document.createElement('div'); + const html = htmlText || parseMarkdownFallback(markdownText); + + // Clean up the HTML content by removing code-block wrappers and headers + const tempDiv = document.createElement("div"); tempDiv.innerHTML = html; - - // Remove code-block-header and code-block-wrapper elements - tempDiv.querySelectorAll('.code-block-header').forEach(el => el.remove()); - tempDiv.querySelectorAll('.code-block-wrapper').forEach(wrapper => { + + // Remove code-block-header + tempDiv.querySelectorAll(".code-block-header").forEach((el) => el.remove()); + + // Unwrap code-block-wrapper + tempDiv.querySelectorAll(".code-block-wrapper").forEach((wrapper) => { while (wrapper.firstChild) { wrapper.parentNode.insertBefore(wrapper.firstChild, wrapper); } wrapper.remove(); }); - - html = tempDiv.innerHTML; - downloadFile(html, 'content.html', 'text/html'); + + // Remove React-specific classes and styles from pre tags to keep it generic + tempDiv.querySelectorAll("pre").forEach((pre) => { + pre.removeAttribute("class"); + pre.removeAttribute("style"); + }); + + const cleanHtml = tempDiv.innerHTML; + downloadFile(cleanHtml, "content.html", "text/html"); }; const handleExportPlainText = () => { if (!markdownText.trim()) { - setError('No content to export'); + setError("No content to export"); return; } - // Export as markdown-ready plain text (keep markdown syntax) - // This allows users to copy/paste and re-import without losing formatting - downloadFile(markdownText, 'document.txt', 'text/plain'); + + // Strip markdown formatting using a simple regex to get plain text + const plainText = markdownText + .replace(/[#*`_~>[\]]/g, "") // remove markdown characters + .replace(/!\[.*?\]\([^)]+\)/g, "") // remove images + .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") // remove links but keep text + .trim(); + + downloadFile(plainText, "document.txt", "text/plain"); }; const handleCopyToClipboard = () => { if (!markdownText.trim()) { - setError('No content to copy'); + setError("No content to copy"); return; } - navigator.clipboard.writeText(markdownText).then(() => { - // Show success feedback (you can add a toast notification here) - setError(''); // Clear any errors - }).catch(() => { - setError('Failed to copy to clipboard'); - }); + + // Attempt to copy the markdown text + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard + .writeText(markdownText) + .then(() => { + // Provide visual feedback by temporarily changing the button state + const copyBtn = document.getElementById("copy-export-btn"); + if (copyBtn) { + const originalText = copyBtn.innerHTML; + copyBtn.innerHTML = `Copied!To clipboard`; + setTimeout(() => { + copyBtn.innerHTML = originalText; + }, 2000); + } + setError(""); + }) + .catch((err) => { + console.error("Clipboard API failed:", err); + fallbackCopyTextToClipboard(markdownText); + }); + } else { + fallbackCopyTextToClipboard(markdownText); + } + }; + + // Fallback copy mechanism + const fallbackCopyTextToClipboard = (text) => { + const textArea = document.createElement("textarea"); + textArea.value = text; + + // Avoid scrolling to bottom + textArea.style.top = "0"; + textArea.style.left = "0"; + textArea.style.position = "fixed"; + + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + try { + document.execCommand("copy"); + const copyBtn = document.getElementById("copy-export-btn"); + if (copyBtn) { + const originalText = copyBtn.innerHTML; + copyBtn.innerHTML = `Copied!To clipboard`; + setTimeout(() => { + copyBtn.innerHTML = originalText; + }, 2000); + } + setError(""); + } catch (err) { + setError("Failed to copy to clipboard"); + } + + document.body.removeChild(textArea); }; const handleExportPDF = () => { if (!markdownText.trim()) { - setError('No content to export'); + setError("No content to export"); return; } - // Create the content element with rendered markdown - const element = document.createElement('div'); - element.innerHTML = parseMarkdown(markdownText); - - // Remove code block headers (language + copy button) - const codeHeaders = element.querySelectorAll('.code-block-header'); - codeHeaders.forEach(header => header.remove()); - - // Remove copy buttons from code blocks - const copyButtons = element.querySelectorAll('button[title="Copy code"]'); - copyButtons.forEach(btn => btn.remove()); - + // Determine HTML to use: if htmlText is empty, fall back to marked + const sourceHtml = htmlText || parseMarkdownFallback(markdownText); + + // Create the content element with rendered html + const element = document.createElement("div"); + element.innerHTML = sourceHtml; + // Add comprehensive PDF styles inline - const styleEl = document.createElement('style'); + const styleEl = document.createElement("style"); styleEl.textContent = ` .pdf-content { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; @@ -1454,44 +1475,49 @@ ${html} margin-right: 8px; } `; - - element.className = 'pdf-content'; - + + element.className = "pdf-content"; + // Create wrapper with styles - const wrapper = document.createElement('div'); + const wrapper = document.createElement("div"); wrapper.appendChild(styleEl); wrapper.appendChild(element); const opt = { margin: [15, 15, 15, 15], - filename: 'document.pdf', - image: { type: 'jpeg', quality: 0.98 }, - html2canvas: { - scale: 2, + filename: "document.pdf", + image: { type: "jpeg", quality: 0.98 }, + html2canvas: { + scale: 2, useCORS: true, logging: false, letterRendering: true, - backgroundColor: '#ffffff' + backgroundColor: "#ffffff", }, - jsPDF: { - unit: 'mm', - format: 'a4', - orientation: 'portrait', - compress: true + jsPDF: { + unit: "mm", + format: "a4", + orientation: "portrait", + compress: true, }, - pagebreak: { mode: ['avoid-all', 'css', 'legacy'] } + pagebreak: { mode: ["avoid-all", "css", "legacy"] }, }; - html2pdf().set(opt).from(wrapper).save().then(() => { - setError(''); - }).catch((err) => { - setError('Failed to generate PDF'); - }); + html2pdf() + .set(opt) + .from(wrapper) + .save() + .then(() => { + setError(""); + }) + .catch((err) => { + setError("Failed to generate PDF"); + }); }; return ( <> - - {/* Input Section - Always visible */} -
-
-

Get Started

-
+ {/* Input Section - Always visible */} +
+
+

+ Get Started +

+
{/* Tab Navigation */}
{/* Tab Content */} - {(activeTab !== 'create' || !createNewCompleted) && ( + {(activeTab !== "create" || !createNewCompleted) && (
{/* Create New Tab Content */} - {activeTab === 'create' && ( + {activeTab === "create" && ( <> {!createNewCompleted ? (
@@ -1575,16 +1603,16 @@ ${html} Choose how you'd like to begin writing

- +
- +
- +

- 💡 Tip: You can always import markdown later using the URL, Paste, or Open tabs. + 💡 Tip: You can always import + markdown later using the URL, Paste, or Open tabs.

@@ -1649,9 +1690,12 @@ ${html}
- ✓ Document ready: {markdownText ? `${stats.words} words, ${stats.characters} characters, ${stats.lines} lines` : 'Empty document'} + ✓ Document ready:{" "} + {markdownText + ? `${stats.words} words, ${stats.characters} characters, ${stats.lines} lines` + : "Empty document"} -
)} - + {!urlDataSummary && (
@@ -1690,7 +1736,12 @@ ${html} type="url" value={fetchUrl} onChange={(e) => setFetchUrl(e.target.value)} - onKeyPress={(e) => e.key === 'Enter' && !fetching && fetchUrl.trim() && handleFetchFromURL()} + onKeyPress={(e) => + e.key === "Enter" && + !fetching && + fetchUrl.trim() && + handleFetchFromURL() + } placeholder="https://raw.githubusercontent.com/user/repo/main/README.md" className="tool-input w-full" disabled={fetching} @@ -1701,11 +1752,12 @@ ${html} disabled={fetching || !fetchUrl.trim()} className="bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white font-medium px-4 py-2 rounded-md transition-colors flex items-center whitespace-nowrap" > - {fetching ? 'Fetching...' : 'Fetch Data'} + {fetching ? "Fetching..." : "Fetch Data"}

- Enter a URL to a markdown file (GitHub raw, Gist, Pastebin, etc.) + Enter a URL to a markdown file (GitHub raw, Gist, + Pastebin, etc.)

)} @@ -1713,14 +1765,16 @@ ${html} )} {/* Paste Tab Content */} - {activeTab === 'paste' && ( - pasteCollapsed ? ( + {activeTab === "paste" && + (pasteCollapsed ? (
- ✓ Markdown loaded: {pasteDataSummary.format} ({pasteDataSummary.size.toLocaleString()} chars, {pasteDataSummary.lines} lines) + ✓ Markdown loaded: {pasteDataSummary.format} ( + {pasteDataSummary.size.toLocaleString()} chars,{" "} + {pasteDataSummary.lines} lines) -
- ) - )} + ))} {/* Open Tab Content */} - {activeTab === 'open' && ( - fileDataSummary ? ( + {activeTab === "open" && + (fileDataSummary ? (
- ✓ File loaded: {fileDataSummary.format} ({fileDataSummary.size.toLocaleString()} chars, {fileDataSummary.lines} lines) - {fileDataSummary.filename} + ✓ File loaded: {fileDataSummary.format} ( + {fileDataSummary.size.toLocaleString()} chars,{" "} + {fileDataSummary.lines} lines) -{" "} + {fileDataSummary.filename} -
- ) - )} + ))}
)} -
+
- {/* Input Method Change Confirmation Modal */} - {showInputChangeModal && ( - - )} + {/* Input Method Change Confirmation Modal */} + {showInputChangeModal && ( + + )} - {/* Editor Section */} - {(activeTab !== 'create' || createNewCompleted) && ( -
- {/* Editor Header - Sticky */} -
-
-
-

- - Markdown Editor -

- - {/* Statistics */} -
- {stats.words} words - - {stats.characters} chars - - {stats.lines} lines - - {stats.readingTime} min read + {/* Editor Section */} + {(activeTab !== "create" || createNewCompleted) && ( +
+ {/* Editor Header - Sticky */} +
+
+
+

+ + Markdown Editor +

+ + {/* Statistics */} +
+ {stats.words} words + + {stats.characters} chars + + {stats.lines} lines + + {stats.readingTime} min read +
-
- - {/* View Mode Controls */} -
-
+ + {/* View Mode Controls */} +
+
+ + + +
- {/* Hide split button on mobile (< lg) */} - -
- -
-
- {/* Markdown Toolbar */} - {(viewMode === 'editor' || viewMode === 'split') && ( -
-
- {toolbarButtons.map((btn, idx) => { - const Icon = btn.icon; - const prevGroup = idx > 0 ? toolbarButtons[idx - 1].group : null; - const showSeparator = idx > 0 && btn.group !== prevGroup; - - return ( - - {/* Group Separator */} - {showSeparator && ( -
- )} - -
- - {/* Tooltip */} -
- {btn.label} -
- - {/* Heading Dropdown */} - {btn.isDropdown && showHeadingDropdown && ( -
- {[1, 2, 3, 4, 5, 6].map(level => ( - - ))} -
- )} -
- - ); - })} -
-
- )} - - {/* Editor Content */} -
- {/* Markdown Editor */} - {(viewMode === 'editor' || viewMode === 'split') && ( -
-
- -
-
- )} - - {/* Preview */} - {(viewMode === 'preview' || viewMode === 'split') && ( -
-
- {markdownText ? ( -
- ) : ( -
-
- -

Preview will appear here

-
+ {/* Editor Content */} +
+
+ {viewMode === "preview" ? ( +
+
+
- )} -
+
+ ) : viewMode === "editor" ? ( + + ) : ( +
+ { + setMarkdownText(newMarkdown); + setHtmlText(""); // Clear html text so switching to preview re-renders it accurately + }} + language="markdown" + placeholder="Write markdown here..." + maxLines={999} + showToggle={false} + height={isFullscreen ? "calc(100vh - 60px)" : "600px"} + className="w-full h-full" + /> +
+ )}
- )} -
-
- )} - - {/* Export Section */} - {markdownText && ( -
-
-

- - Export Options -

-

- Download your markdown in different formats -

-
- -
-
- {/* Export as Markdown */} - - - {/* Export as PDF */} - - - {/* Export as Full HTML */} - - - {/* Export as HTML Content Only */} - - - {/* Export as Plain Text */} - - - {/* Copy to Clipboard */} - -
- - {/* Export Info */} -
-
- -
-

Export Information

-
    -
  • Markdown: Original markdown with all formatting (.md file)
  • -
  • PDF: Professional PDF document with formatted content - perfect for sharing and printing!
  • -
  • Full HTML: Complete standalone HTML page with GitHub Dark theme, syntax highlighting (Highlight.js), and working copy buttons - ready to use!
  • -
  • HTML Content: Body content only, ready to paste into web pages
  • -
  • Plain Text: Markdown-ready text file, keeps all syntax (.txt file)
  • -
  • Copy: Copy markdown to clipboard for pasting
  • -
-
-
-
-
-
- )} - - {/* Usage Tips */} -
-
setUsageTipsExpanded(!usageTipsExpanded)} - className="px-4 py-3 cursor-pointer hover:bg-blue-100 dark:hover:bg-blue-900/30 transition-colors flex items-center justify-between" - > -

- 💡 Usage Tips -

- {usageTipsExpanded ? : } -
- - {usageTipsExpanded && ( -
- {/* Input Methods */} -
-

📝 Input Methods:

-
    -
  • Create New: Start with empty editor or load sample markdown
  • -
  • URL: Fetch markdown from GitHub, Gist, or any public URL
  • -
  • Paste: Paste markdown, HTML (auto-converts), or plain text
  • -
  • Open: Load .md, .txt, or .html files from your computer
  • -
-
- - {/* Editing Features */} -
-

✏️ Editing Features

-
    -
  • Live Preview: See your markdown rendered in real-time
  • -
  • Syntax Highlighting: Code blocks with automatic language detection
  • -
  • View Modes: Switch between Split, Editor Only, Preview Only, or Fullscreen
  • -
  • Toolbar: Quick formatting buttons for headers, bold, italic, links, code, and more
  • -
  • Statistics: Track word count, character count, lines, and reading time
  • -
  • GitHub Flavored Markdown: Full GFM support including tables and task lists
  • -
-
- - {/* Markdown Syntax */} -
-

📖 Markdown Syntax

-
    -
  • Headers: # H1, ## H2, ### H3, etc.
  • -
  • Bold: **bold text** or __bold text__
  • -
  • Italic: *italic text* or _italic text_
  • -
  • Underline: __underlined text__ (custom syntax)
  • -
  • Code: `inline code` or ```language for code blocks
  • -
  • Links: [text](url)
  • -
  • Images: ![alt text](url)
  • -
  • Lists: - or * for unordered, 1. for ordered
  • -
  • Tables: {'|'} Header {'|'} Header {'|'} with {'|'} --- {'|'} --- {'|'} separator
  • -
  • Blockquotes: {'>'} quoted text
  • -
-
- - {/* Export Options */} -
-

📤 Export Options

-
    -
  • Markdown (.md): Standard markdown format
  • -
  • Full HTML: Standalone page with styling and working copy buttons
  • -
  • HTML Content: Just the body content for embedding
  • -
  • Plain Text (.txt): Markdown-ready text file
  • -
  • Copy: Copy to clipboard for quick sharing
  • -
-
- - {/* Data Privacy */} -
-

💾 Data Privacy

-
    -
  • 100% Client-Side: All processing happens in your browser
  • -
  • No Server Upload: Your markdown never leaves your device
  • -
  • No Tracking: We don't store or track your content
  • -
  • Privacy-First: Safe for confidential documents
  • -
)} -
- {/* Related Tools */} - - + {/* Export Section */} + {markdownText && ( +
+
+

+ + Export Options +

+

+ Download your markdown in different formats +

+
+ +
+
+ {/* Export as Markdown */} + + + {/* Export as PDF */} + + + {/* Export as Full HTML */} + + + {/* Export as HTML Content Only */} + + + {/* Export as Plain Text */} + + + {/* Copy to Clipboard */} + +
+ + {/* Export Info */} +
+
+ +
+

Export Information

+
    +
  • + Markdown: Original markdown with all + formatting (.md file) +
  • +
  • + PDF: Professional PDF document with + formatted content - perfect for sharing and printing! +
  • +
  • + Full HTML: Complete standalone HTML + page with GitHub Dark theme, syntax highlighting + (Highlight.js), and working copy buttons - ready to use! +
  • +
  • + HTML Content: Body content only, ready + to paste into web pages +
  • +
  • + Plain Text: Markdown-ready text file, + keeps all syntax (.txt file) +
  • +
  • + Copy: Copy markdown to clipboard for + pasting +
  • +
+
+
+
+
+
+ )} + + {/* Usage Tips */} +
+
setUsageTipsExpanded(!usageTipsExpanded)} + className="px-4 py-3 cursor-pointer hover:bg-blue-100 dark:hover:bg-blue-900/30 transition-colors flex items-center justify-between" + > +

+ 💡 Usage Tips +

+ {usageTipsExpanded ? ( + + ) : ( + + )} +
+ + {usageTipsExpanded && ( +
+ {/* Input Methods */} +
+

📝 Input Methods:

+
    +
  • + Create New: Start with empty editor or load + sample markdown +
  • +
  • + URL: Fetch markdown from GitHub, Gist, or + any public URL +
  • +
  • + Paste: Paste markdown, HTML + (auto-converts), or plain text +
  • +
  • + Open: Load .md, .txt, or .html files from + your computer +
  • +
+
+ + {/* Editing Features */} +
+

+ ✏️ Editing Features +

+
    +
  • + Live Preview: See your markdown rendered in + real-time +
  • +
  • + Syntax Highlighting: Code blocks with + automatic language detection +
  • +
  • + View Modes: Switch between Split, Editor + Only, Preview Only, or Fullscreen +
  • +
  • + Toolbar: Quick formatting buttons for + headers, bold, italic, links, code, and more +
  • +
  • + Statistics: Track word count, character + count, lines, and reading time +
  • +
  • + GitHub Flavored Markdown: Full GFM support + including tables and task lists +
  • +
+
+ + {/* Markdown Syntax */} +
+

+ 📖 Markdown Syntax +

+
    +
  • + Headers: # H1, ## H2, ### H3, etc. +
  • +
  • + Bold: **bold text** or __bold text__ +
  • +
  • + Italic: *italic text* or _italic text_ +
  • +
  • + Underline: __underlined text__ (custom + syntax) +
  • +
  • + Code: `inline code` or ```language for code + blocks +
  • +
  • + Links: [text](url) +
  • +
  • + Images: ![alt text](url) +
  • +
  • + Lists: - or * for unordered, 1. for ordered +
  • +
  • + Tables: {"|"} Header {"|"} Header {"|"}{" "} + with {"|"} --- {"|"} --- {"|"} separator +
  • +
  • + Blockquotes: {">"} quoted text +
  • +
+
+ + {/* Export Options */} +
+

+ 📤 Export Options +

+
    +
  • + Markdown (.md): Standard markdown format +
  • +
  • + Full HTML: Standalone page with styling and + working copy buttons +
  • +
  • + HTML Content: Just the body content for + embedding +
  • +
  • + Plain Text (.txt): Markdown-ready text file +
  • +
  • + Copy: Copy to clipboard for quick sharing +
  • +
+
+ + {/* Data Privacy */} +
+

+ 💾 Data Privacy +

+
    +
  • + 100% Client-Side: All processing happens in + your browser +
  • +
  • + No Server Upload: Your markdown never + leaves your device +
  • +
  • + No Tracking: We don't store or track your + content +
  • +
  • + Privacy-First: Safe for confidential + documents +
  • +
+
+
+ )} +
+ + {/* Related Tools */} + + ); }; // Input Method Change Confirmation Modal Component -const InputChangeConfirmationModal = ({ markdownText, stats, currentMethod, newMethod, onConfirm, onCancel }) => { +const InputChangeConfirmationModal = ({ + markdownText, + stats, + currentMethod, + newMethod, + onConfirm, + onCancel, +}) => { const getMethodName = (method) => { switch (method) { - case 'create': return 'Create New'; - case 'create_empty': return 'Start Empty'; - case 'create_sample': return 'Load Sample'; - case 'url': return 'URL Import'; - case 'paste': return 'Paste Data'; - case 'open': return 'File Upload'; - default: return method; + case "create": + return "Create New"; + case "create_empty": + return "Start Empty"; + case "create_sample": + return "Load Sample"; + case "url": + return "URL Import"; + case "paste": + return "Paste Data"; + case "open": + return "File Upload"; + default: + return method; } }; @@ -2213,13 +2369,20 @@ const InputChangeConfirmationModal = ({ markdownText, stats, currentMethod, newM {/* Content */}

- {(newMethod === 'create_empty' || newMethod === 'create_sample') ? ( - <>Using {getMethodName(newMethod)} will clear all current markdown. + {newMethod === "create_empty" || newMethod === "create_sample" ? ( + <> + Using {getMethodName(newMethod)} will clear all + current markdown. + ) : ( - <>Switching from {getMethodName(currentMethod)} to {getMethodName(newMethod)} will clear all current markdown. + <> + Switching from {getMethodName(currentMethod)}{" "} + to {getMethodName(newMethod)} will clear all + current markdown. + )}

- +

This will permanently delete: @@ -2235,7 +2398,8 @@ const InputChangeConfirmationModal = ({ markdownText, stats, currentMethod, newM

- Tip: Consider downloading your current markdown before switching methods to avoid losing your work. + Tip: Consider downloading your current markdown + before switching methods to avoid losing your work.

diff --git a/src/styles/._markdown-preview.css b/src/styles/._markdown-preview.css new file mode 100755 index 00000000..3d095653 Binary files /dev/null and b/src/styles/._markdown-preview.css differ diff --git a/src/styles/markdown-preview.css b/src/styles/markdown-preview.css index 2a4fd5ba..4d22df9c 100755 --- a/src/styles/markdown-preview.css +++ b/src/styles/markdown-preview.css @@ -1,23 +1,25 @@ /* GitHub-style Markdown Preview Styling */ .markdown-preview { - color: #24292f; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif; - font-size: 16px; - line-height: 1.6; - word-wrap: break-word; - overflow-wrap: break-word; - max-width: 100%; - word-break: break-word; + color: #24292f; + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, + Arial, sans-serif; + font-size: 16px; + line-height: 1.6; + word-wrap: break-word; + overflow-wrap: break-word; + max-width: 100%; + word-break: break-word; } /* Ensure all child elements respect container width */ .markdown-preview * { - max-width: 100%; - box-sizing: border-box; + max-width: 100%; + box-sizing: border-box; } .dark .markdown-preview { - color: #c9d1d9; + color: #c9d1d9; } .markdown-preview h1, @@ -26,254 +28,256 @@ .markdown-preview h4, .markdown-preview h5, .markdown-preview h6 { - margin-top: 24px; - margin-bottom: 16px; - font-weight: 600; - line-height: 1.25; + margin-top: 24px; + margin-bottom: 16px; + font-weight: 600; + line-height: 1.25; } .markdown-preview h1 { - font-size: 2em; - border-bottom: 1px solid #d0d7de; - padding-bottom: 0.3em; + font-size: 2em; + border-bottom: 1px solid #d0d7de; + padding-bottom: 0.3em; } .dark .markdown-preview h1 { - border-bottom-color: #21262d; + border-bottom-color: #21262d; } .markdown-preview h2 { - font-size: 1.5em; - border-bottom: 1px solid #d0d7de; - padding-bottom: 0.3em; + font-size: 1.5em; + border-bottom: 1px solid #d0d7de; + padding-bottom: 0.3em; } .dark .markdown-preview h2 { - border-bottom-color: #21262d; + border-bottom-color: #21262d; } .markdown-preview h3 { - font-size: 1.25em; + font-size: 1.25em; } .markdown-preview h4 { - font-size: 1em; + font-size: 1em; } .markdown-preview h5 { - font-size: 0.875em; + font-size: 0.875em; } .markdown-preview h6 { - font-size: 0.85em; - color: #57606a; + font-size: 0.85em; + color: #57606a; } .dark .markdown-preview h6 { - color: #8b949e; + color: #8b949e; } .markdown-preview p { - margin-top: 0; - margin-bottom: 16px; + margin-top: 0; + margin-bottom: 16px; } /* Inline code - with background */ .markdown-preview code { - padding: 0.2em 0.4em; - margin: 0; - font-size: 85%; - background-color: rgba(175, 184, 193, 0.2); - border-radius: 6px; - font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; + padding: 0.2em 0.4em; + margin: 0; + font-size: 85%; + background-color: rgba(175, 184, 193, 0.2); + border-radius: 6px; + font-family: + ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, + "Liberation Mono", monospace; } .dark .markdown-preview code { - background-color: rgba(110, 118, 129, 0.4); + background-color: rgba(110, 118, 129, 0.4); } /* Code block wrapper with header */ .markdown-preview .code-block-wrapper { - margin-bottom: 16px; - border-radius: 8px; - overflow: hidden; - border: 1px solid #d0d7de; - background-color: #f6f8fa; + margin-bottom: 16px; + border-radius: 8px; + overflow: hidden; + border: 1px solid #d0d7de; + background-color: #f6f8fa; } .dark .markdown-preview .code-block-wrapper { - border-color: #30363d; - background-color: #0d1117; + border-color: #30363d; + background-color: #0d1117; } /* Code block header */ .markdown-preview .code-block-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 4px 10px; - background-color: #f6f8fa; - border-bottom: 1px solid #d0d7de; - font-size: 12px; + display: flex; + justify-content: space-between; + align-items: center; + padding: 4px 10px; + background-color: #f6f8fa; + border-bottom: 1px solid #d0d7de; + font-size: 12px; } .dark .markdown-preview .code-block-header { - background-color: #161b22; - border-bottom-color: #30363d; + background-color: #161b22; + border-bottom-color: #30363d; } /* Language label */ .markdown-preview .code-block-language { - font-weight: 600; - color: #57606a; - text-transform: uppercase; - font-size: 10px; - letter-spacing: 0.5px; + font-weight: 600; + color: #57606a; + text-transform: uppercase; + font-size: 10px; + letter-spacing: 0.5px; } .dark .markdown-preview .code-block-language { - color: #8b949e; + color: #8b949e; } /* Copy button */ .markdown-preview .code-block-copy { - padding: 2px 6px; - background-color: transparent; - border: 1px solid #d0d7de; - border-radius: 6px; - color: #24292f; - font-size: 10px; - cursor: pointer; - transition: all 0.2s; - font-weight: 500; + padding: 2px 6px; + background-color: transparent; + border: 1px solid #d0d7de; + border-radius: 6px; + color: #24292f; + font-size: 10px; + cursor: pointer; + transition: all 0.2s; + font-weight: 500; } .markdown-preview .code-block-copy:hover { - background-color: #f3f4f6; - border-color: #1f2328; + background-color: #f3f4f6; + border-color: #1f2328; } .dark .markdown-preview .code-block-copy { - color: #c9d1d9; - border-color: #30363d; + color: #c9d1d9; + border-color: #30363d; } .dark .markdown-preview .code-block-copy:hover { - background-color: #21262d; - border-color: #8b949e; + background-color: #21262d; + border-color: #8b949e; } /* Code blocks - with background */ .markdown-preview .code-block-wrapper pre { - padding: 16px; - overflow: auto; - font-size: 85%; - line-height: 1.45; - background-color: #0d1117; - margin: 0; - border-radius: 0; + padding: 16px; + overflow: auto; + font-size: 85%; + line-height: 1.45; + background-color: #0d1117; + margin: 0; + border-radius: 0; } /* Legacy pre blocks (without wrapper) */ .markdown-preview pre:not(.code-block-wrapper pre) { - padding: 16px; - overflow: auto; - font-size: 85%; - line-height: 1.45; - background-color: #afb8c133; - border-radius: 6px; - margin-bottom: 16px; + padding: 16px; + overflow: auto; + font-size: 85%; + line-height: 1.45; + background-color: #afb8c133; + border-radius: 6px; + margin-bottom: 16px; } .dark .markdown-preview pre:not(.code-block-wrapper pre) { - background-color: rgba(110, 118, 129, 0.4); + background-color: rgba(110, 118, 129, 0.4); } /* Code inside pre blocks - NO background (transparent) */ .markdown-preview pre code { - display: inline; - max-width: auto; - padding: 0; - margin: 0; - overflow: visible; - line-height: inherit; - word-wrap: normal; - background-color: transparent !important; - border: 0; - border-radius: 0; + display: inline; + max-width: auto; + padding: 0; + margin: 0; + overflow: visible; + line-height: inherit; + word-wrap: normal; + background-color: transparent !important; + border: 0; + border-radius: 0; } /* Preserve highlight.js syntax highlighting colors */ .markdown-preview pre code.hljs { - background: transparent !important; - padding: 0 !important; + background: transparent !important; + padding: 0 !important; } .markdown-preview table { - border-spacing: 0; - border-collapse: collapse; - display: block; - width: 100%; - max-width: 100%; - overflow-x: auto; - margin-bottom: 16px; + border-spacing: 0; + border-collapse: collapse; + display: block; + width: 100%; + max-width: 100%; + overflow-x: auto; + margin-bottom: 16px; } .markdown-preview table tr { - background-color: #ffffff; - border-top: 1px solid #d0d7de; + background-color: #ffffff; + border-top: 1px solid #d0d7de; } .dark .markdown-preview table tr { - background-color: #0d1117; - border-top-color: #21262d; + background-color: #0d1117; + border-top-color: #21262d; } .markdown-preview table tr:nth-child(2n) { - background-color: #f6f8fa; + background-color: #f6f8fa; } .dark .markdown-preview table tr:nth-child(2n) { - background-color: #161b22; + background-color: #161b22; } .markdown-preview table th, .markdown-preview table td { - padding: 6px 13px; - border: 1px solid #d0d7de; + padding: 6px 13px; + border: 1px solid #d0d7de; } .dark .markdown-preview table th, .dark .markdown-preview table td { - border-color: #21262d; + border-color: #21262d; } .markdown-preview table th { - font-weight: 600; - background-color: #f6f8fa; + font-weight: 600; + background-color: #f6f8fa; } .dark .markdown-preview table th { - background-color: #161b22; + background-color: #161b22; } .markdown-preview blockquote { - padding: 0 1em; - color: #57606a; - border-left: 0.25em solid #d0d7de; - margin: 0 0 16px 0; + padding: 0 1em; + color: #57606a; + border-left: 0.25em solid #d0d7de; + margin: 0 0 16px 0; } .dark .markdown-preview blockquote { - color: #8b949e; - border-left-color: #3b434b; + color: #8b949e; + border-left-color: #3b434b; } .markdown-preview ul, .markdown-preview ol { - padding-left: 2em; - margin-top: 0; - margin-bottom: 16px; + padding-left: 2em; + margin-top: 0; + margin-bottom: 16px; } /* Nested lists */ @@ -281,100 +285,192 @@ .markdown-preview ul ol, .markdown-preview ol ul, .markdown-preview ol ol { - margin-top: 0.25em; - margin-bottom: 0.25em; + margin-top: 0.25em; + margin-bottom: 0.25em; } /* List items */ .markdown-preview li { - margin-bottom: 0.25em; - line-height: 1.6; + margin-bottom: 0.25em; + line-height: 1.6; } .markdown-preview li + li { - margin-top: 0.25em; + margin-top: 0.25em; } /* Better bullet points */ .markdown-preview ul > li { - list-style-type: disc; + list-style-type: disc; } .markdown-preview ul ul > li { - list-style-type: circle; + list-style-type: circle; } .markdown-preview ul ul ul > li { - list-style-type: square; + list-style-type: square; } /* Ordered list styling */ .markdown-preview ol > li { - list-style-type: decimal; + list-style-type: decimal; } .markdown-preview ol ol > li { - list-style-type: lower-alpha; + list-style-type: lower-alpha; } .markdown-preview ol ol ol > li { - list-style-type: lower-roman; + list-style-type: lower-roman; } /* List item content spacing */ .markdown-preview li > p { - margin-top: 0.5em; - margin-bottom: 0.5em; + margin-top: 0.5em; + margin-bottom: 0.5em; } .markdown-preview li > p:first-child { - margin-top: 0; + margin-top: 0; } .markdown-preview li > p:last-child { - margin-bottom: 0; + margin-bottom: 0; } .markdown-preview hr { - height: 0.25em; - padding: 0; - margin: 24px 0; - background-color: #d0d7de; - border: 0; + height: 0.25em; + padding: 0; + margin: 24px 0; + background-color: #d0d7de; + border: 0; } .dark .markdown-preview hr { - background-color: #21262d; + background-color: #21262d; } .markdown-preview a { - color: #0969da; - text-decoration: none; + color: #0969da; + text-decoration: none; } .dark .markdown-preview a { - color: #58a6ff; + color: #58a6ff; } .markdown-preview a:hover { - text-decoration: underline; + text-decoration: underline; } .markdown-preview strong { - font-weight: 600; + font-weight: 600; } .markdown-preview em { - font-style: italic; + font-style: italic; } .markdown-preview u { - text-decoration: underline; + text-decoration: underline; } .markdown-preview img { - max-width: 100%; - height: auto; - border-radius: 6px; - margin: 16px 0; + max-width: 100%; + height: auto; + border-radius: 6px; + margin: 16px 0; +} + +/* Tiptap specific styling overrides to match prose */ +.tiptap p.is-editor-empty:first-child::before { + color: #adb5bd; + content: attr(data-placeholder); + float: left; + height: 0; + pointer-events: none; +} + +.tiptap { + outline: none; +} + +.tiptap ul[data-type="taskList"] { + list-style: none; + padding: 0; +} + +.tiptap ul[data-type="taskList"] li { + display: flex; + align-items: flex-start; + margin-top: 0; + margin-bottom: 0; +} + +.tiptap ul[data-type="taskList"] li > label { + flex: 0 0 auto; + margin-right: 0.5rem; + user-select: none; + margin-top: 0.2rem; +} + +.tiptap ul[data-type="taskList"] li > div { + flex: 1 1 auto; + margin: 0; +} + +.tiptap ul[data-type="taskList"] li > div > p { + margin: 0; +} + +.tiptap p { + margin-top: 0; + margin-bottom: 0.65em; +} + +/* Printing logic for PDF export */ +@media print { + .tiptap pre, + .markdown-preview pre { + white-space: pre-wrap !important; + word-wrap: break-word !important; + break-inside: avoid !important; + } + + .code-block-header { + display: none !important; + } +} + +/* Custom Node Views (Code Block) */ +.tiptap .code-block-wrapper { + margin-bottom: 0.65em; + border-radius: 6px; + background-color: #0d1117; + overflow: hidden; +} + +.tiptap .code-block-wrapper pre { + margin: 0 !important; + padding: 1rem; + border-radius: 0 0 6px 6px; + background: transparent; +} + +/* Markdown Content Wrapper Padding Strategies */ +.markdown-content-wrapper.is-normal.is-read-mode > .prose { + padding-bottom: 3rem; /* 48px */ +} + +.markdown-content-wrapper.is-fullscreen.is-read-mode > .prose { + padding-bottom: 4rem; /* 128px */ +} + +.markdown-content-wrapper.is-normal.is-edit-mode > div { + padding-bottom: 3rem; /* 48px */ +} + +.markdown-content-wrapper.is-fullscreen.is-edit-mode > div { + padding-bottom: 6rem; /* 128px */ } diff --git a/tailwind.config.js b/tailwind.config.js index 9fd42706..94651e61 100755 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,34 +1,165 @@ /** @type {import('tailwindcss').Config} */ module.exports = { - content: [ - "./src/**/*.{js,jsx,ts,tsx}", - ], - darkMode: 'class', // Enable manual dark mode control via class + content: ["./src/**/*.{js,jsx,ts,tsx}"], + darkMode: "class", // Enable manual dark mode control via class theme: { extend: { colors: { primary: { - 50: '#f0f9ff', - 100: '#e0f2fe', - 200: '#bae6fd', - 300: '#7dd3fc', - 400: '#38bdf8', - 500: '#0ea5e9', - 600: '#0284c7', - 700: '#0369a1', - 800: '#075985', - 900: '#0c4a6e', - } + 50: "#f0f9ff", + 100: "#e0f2fe", + 200: "#bae6fd", + 300: "#7dd3fc", + 400: "#38bdf8", + 500: "#0ea5e9", + 600: "#0284c7", + 700: "#0369a1", + 800: "#075985", + 900: "#0c4a6e", + }, }, fontFamily: { - mono: ['JetBrains Mono', 'Monaco', 'Cascadia Code', 'Segoe UI Mono', 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', 'Fira Code', 'Droid Sans Mono', 'Courier New', 'monospace'], + mono: [ + "JetBrains Mono", + "Monaco", + "Cascadia Code", + "Segoe UI Mono", + "Roboto Mono", + "Oxygen Mono", + "Ubuntu Monospace", + "Source Code Pro", + "Fira Code", + "Droid Sans Mono", + "Courier New", + "monospace", + ], }, - maxWidth: { - '1/4': '25%', - '1/2': '50%', - '3/4': '75%', - } + typography: (theme) => ({ + DEFAULT: { + css: { + "--tw-prose-body": "#24292f", + "--tw-prose-headings": "#24292f", + "--tw-prose-lead": "#57606a", + "--tw-prose-links": "#0969da", + "--tw-prose-bold": "#24292f", + "--tw-prose-counters": "#57606a", + "--tw-prose-bullets": "#d0d7de", + "--tw-prose-hr": "#d0d7de", + "--tw-prose-quotes": "#57606a", + "--tw-prose-quote-borders": "#d0d7de", + "--tw-prose-captions": "#57606a", + "--tw-prose-code": "#24292f", + "--tw-prose-pre-code": "#24292f", + "--tw-prose-pre-bg": "#f6f8fa", + "--tw-prose-th-borders": "#d0d7de", + "--tw-prose-td-borders": "#d0d7de", + + // Invert colors for dark mode + "--tw-prose-invert-body": "#c9d1d9", + "--tw-prose-invert-headings": "#c9d1d9", + "--tw-prose-invert-lead": "#8b949e", + "--tw-prose-invert-links": "#58a6ff", + "--tw-prose-invert-bold": "#c9d1d9", + "--tw-prose-invert-counters": "#8b949e", + "--tw-prose-invert-bullets": "#30363d", + "--tw-prose-invert-hr": "#21262d", + "--tw-prose-invert-quotes": "#8b949e", + "--tw-prose-invert-quote-borders": "#30363d", + "--tw-prose-invert-captions": "#8b949e", + "--tw-prose-invert-code": "#c9d1d9", + "--tw-prose-invert-pre-code": "#c9d1d9", + "--tw-prose-invert-pre-bg": "#161b22", + "--tw-prose-invert-th-borders": "#30363d", + "--tw-prose-invert-td-borders": "#30363d", + + // Adjust margins and sizes (Standardizing to GitHub Markdown / Modern defaults) + maxWidth: "none", + lineHeight: "1.4", + p: { + marginTop: "0", + marginBottom: "0.65em", + }, + "h1, h2, h3, h4, h5, h6": { + marginTop: "1em", + marginBottom: "0.65em", + fontWeight: "600", + lineHeight: "1.2", + }, + h1: { + fontSize: "2em", + paddingBottom: "0.2em", + borderBottomWidth: "1px", + }, + h2: { + fontSize: "1.5em", + paddingBottom: "0.2em", + borderBottomWidth: "1px", + }, + h3: { fontSize: "1.25em" }, + h4: { fontSize: "1em" }, + h5: { fontSize: "0.875em" }, + h6: { fontSize: "0.85em", color: "var(--tw-prose-lead)" }, + "ul, ol": { + marginTop: "0", + marginBottom: "0.65em", + paddingLeft: "1.5em", + }, + li: { + marginTop: "0.15em", + marginBottom: "0.15em", + }, + "li > p": { + marginTop: "0", + marginBottom: "0", + }, + blockquote: { + marginTop: "0", + marginBottom: "0.65em", + paddingLeft: "1em", + fontStyle: "normal", + borderLeftWidth: "4px", + }, + pre: { + marginTop: "0", + marginBottom: "0.65em", + padding: "0.75em", + borderRadius: "6px", + }, + code: { + backgroundColor: "rgba(175, 184, 193, 0.2)", + padding: "0.2em 0.4em", + borderRadius: "6px", + fontWeight: "inherit", + }, + "code::before": { content: '""' }, + "code::after": { content: '""' }, + "pre code": { + backgroundColor: "transparent", + padding: "0", + }, + table: { + marginTop: "0", + marginBottom: "0.65em", + }, + "thead th": { + padding: "0.4em 0.75em", + borderWidth: "1px", + }, + "tbody td": { + padding: "0.4em 0.75em", + borderWidth: "1px", + }, + hr: { + marginTop: "1em", + marginBottom: "1em", + height: "0.25em", + borderWidth: "0", + backgroundColor: "var(--tw-prose-hr)", + }, + }, + }, + }), }, }, - plugins: [], -} + plugins: [require("@tailwindcss/typography")], +};