@grandaniel/vue-markdown-editor 1.2.0 → 1.3.0-dev.c5fc9b8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +226 -141
  2. package/dist/components/MarkdownEditor/Factory/MarkdownNodeFactory.d.ts +7 -0
  3. package/dist/components/MarkdownEditor/MarkdownComponentRegistry.d.ts +5 -1
  4. package/dist/components/MarkdownEditor/MarkdownEditorModule.vue.d.ts +2 -2
  5. package/dist/components/MarkdownEditor/Modules/MarkdownModuleBlockquote.vue.d.ts +20 -0
  6. package/dist/components/MarkdownEditor/Modules/MarkdownModuleCodeBlock.vue.d.ts +66 -0
  7. package/dist/components/MarkdownEditor/Modules/MarkdownModuleCodeBlockState.d.ts +5 -0
  8. package/dist/components/MarkdownEditor/Modules/MarkdownModuleFile.vue.d.ts +2 -2
  9. package/dist/components/MarkdownEditor/Modules/MarkdownModuleHeadline1.vue.d.ts +2 -2
  10. package/dist/components/MarkdownEditor/Modules/MarkdownModuleHeadline2.vue.d.ts +2 -2
  11. package/dist/components/MarkdownEditor/Modules/MarkdownModuleHeadline3.vue.d.ts +2 -2
  12. package/dist/components/MarkdownEditor/Modules/MarkdownModuleHr.vue.d.ts +14 -0
  13. package/dist/components/MarkdownEditor/Modules/MarkdownModuleHrState.d.ts +3 -0
  14. package/dist/components/MarkdownEditor/Modules/MarkdownModuleOrderedList.vue.d.ts +14 -0
  15. package/dist/components/MarkdownEditor/Modules/MarkdownModuleParagraph.vue.d.ts +2 -2
  16. package/dist/components/MarkdownEditor/Modules/MarkdownModuleTable.vue.d.ts +14 -0
  17. package/dist/components/MarkdownEditor/Modules/MarkdownModuleTableState.d.ts +5 -0
  18. package/dist/components/MarkdownEditor/Types/MarkdownAstNode.d.ts +4 -0
  19. package/dist/components/MarkdownEditor/Types/MarkdownAstNodeType.d.ts +7 -2
  20. package/dist/components/MarkdownEditor/index.d.ts +7 -1
  21. package/dist/components/MarkdownRenderer/Composable/useMarkdownRenderer.d.ts +21 -1143
  22. package/dist/components/MarkdownRenderer/MarkdownRenderer.vue.d.ts +12 -1141
  23. package/dist/components/MarkdownRenderer/RenderModules/MarkdownModuleBlockquoteRender.vue.d.ts +7 -0
  24. package/dist/components/MarkdownRenderer/RenderModules/MarkdownModuleCodeBlockRender.vue.d.ts +7 -0
  25. package/dist/components/MarkdownRenderer/RenderModules/MarkdownModuleHrRender.vue.d.ts +6 -0
  26. package/dist/components/MarkdownRenderer/RenderModules/MarkdownModuleOrderedListRender.vue.d.ts +7 -0
  27. package/dist/components/MarkdownRenderer/RenderModules/MarkdownModuleTableRender.vue.d.ts +7 -0
  28. package/dist/index.d.ts +1 -0
  29. package/dist/markdown.d.ts +20 -0
  30. package/dist/vue-markdown-editor.css +1 -1
  31. package/dist/vue-markdown-editor.mjs +15883 -10355
  32. package/package.json +5 -6
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  **@grandaniel/vue-markdown-editor** provides two complementary packages:
11
11
 
12
- - **Editor** — A rich block-editing experience where every Markdown element — headlines, paragraphs, lists, images — is its own independently editable, draggable block. Built for content-first workflows: write, reorder, and format with keyboard shortcuts and an always-visible drag handle.
12
+ - **Editor** — A rich block-editing experience where every Markdown element — headlines, bullet & numbered lists, tables, blockquotes, code blocks, dividers, images, and files — is its own independently editable, draggable block. Built for content-first workflows: write, reorder, and format with keyboard shortcuts and an always-visible drag handle.
13
13
 
14
14
  - **Renderer** — A lightweight, SSR-safe component that renders Markdown strings to semantic HTML. Uses the same `remark-parse` pipeline as the editor, so custom module blocks render identically. Fully customizable: override any default render component with your own Vue components.
15
15
 
@@ -40,10 +40,10 @@ Images are first-class citizens: paste an image to auto-upload, then edit its **
40
40
 
41
41
  ## Who uses it
42
42
 
43
- | Project | How |
44
- |---|---|
45
- | **[heartbeat.systems](https://heartbeat.systems)** | Admin utility — content editors manage help articles, release notes, and in-app documentation through the block editor. |
46
- | **[markdownstud.io](https://markdownstud.io)** | AI writing assistant — the editor serves as the primary composition surface where users draft, review, and polish AI-generated content. |
43
+ | Project | How |
44
+ | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
45
+ | **[heartbeat.systems](https://heartbeat.systems)** | Admin utility — content editors manage help articles, release notes, and in-app documentation through the block editor. |
46
+ | **[markdownstud.io](https://markdownstud.io)** | AI writing assistant — the editor serves as the primary composition surface where users draft, review, and polish AI-generated content. |
47
47
 
48
48
  ---
49
49
 
@@ -54,12 +54,17 @@ npm install @grandaniel/vue-markdown-editor
54
54
  ```
55
55
 
56
56
  > **Peer dependency:** Vue `^3.5.0`
57
- > **Node:** `>=22`
57
+ > **Node:** `>=22`
58
58
 
59
59
  Import the components **and** the stylesheet:
60
60
 
61
61
  ```ts
62
- import { MarkdownEditor, MarkdownRenderer, useMarkdownEditor, useMarkdownRenderer } from "@grandaniel/vue-markdown-editor";
62
+ import {
63
+ MarkdownEditor,
64
+ MarkdownRenderer,
65
+ useMarkdownEditor,
66
+ useMarkdownRenderer,
67
+ } from "@grandaniel/vue-markdown-editor";
63
68
  import "@grandaniel/vue-markdown-editor/style.css";
64
69
  ```
65
70
 
@@ -86,10 +91,7 @@ const focusedNode = ref<MarkdownAstNode | null>(null);
86
91
  </script>
87
92
 
88
93
  <template>
89
- <MarkdownEditor
90
- :editor="editor"
91
- v-model:focused-node="focusedNode"
92
- />
94
+ <MarkdownEditor :editor="editor" v-model:focused-node="focusedNode" />
93
95
  </template>
94
96
  ```
95
97
 
@@ -112,13 +114,13 @@ editor.markdownContent.value = "## New heading\n\nFresh content.";
112
114
 
113
115
  ### Keyboard Shortcuts
114
116
 
115
- | Key | Action |
116
- |---|---|
117
- | <kbd>↑</kbd> / <kbd>↓</kbd> | Move focus between blocks |
118
- | <kbd>Enter</kbd> | Split current block → insert new paragraph below |
119
- | <kbd>Backspace</kbd> (empty block) | Delete the block, focus moves up |
120
- | <kbd>Delete</kbd> (empty block) | Delete the block, focus stays at same index |
121
- | Click blank area | Append a new empty paragraph at the bottom |
117
+ | Key | Action |
118
+ | ---------------------------------- | ------------------------------------------------ |
119
+ | <kbd>↑</kbd> / <kbd>↓</kbd> | Move focus between blocks |
120
+ | <kbd>Enter</kbd> | Split current block → insert new paragraph below |
121
+ | <kbd>Backspace</kbd> (empty block) | Delete the block, focus moves up |
122
+ | <kbd>Delete</kbd> (empty block) | Delete the block, focus stays at same index |
123
+ | Click blank area | Append a new empty paragraph at the bottom |
122
124
 
123
125
  #### Auto type‑detection
124
126
 
@@ -126,6 +128,69 @@ Type `# `, `## `, or `### ` at the start of a paragraph and the block auto‑con
126
128
 
127
129
  ---
128
130
 
131
+ ### Block modules & serialized Markdown
132
+
133
+ The editor splits Markdown into **block modules** — every module is its own draggable, editable block:
134
+
135
+ | Module | Markdown | Edits |
136
+ | ------------- | ---------------------------- | -------------------------------------------- |
137
+ | Paragraph | plain text | Rich text (bold, italic, links, inline code) |
138
+ | Heading | `# ` / `## ` / `### ` | Single-line text |
139
+ | Bullet list | `- item` | Multiple rich-text items |
140
+ | Numbered list | `1. item` | Multiple rich-text items |
141
+ | Blockquote | `> line` | Rich-text quote, one `>` per line |
142
+ | Code block | fenced code `lang…` | Monospace code + optional language |
143
+ | Divider (HR) | `---` | Static horizontal rule |
144
+ | Table | GFM pipe table | Cell-by-cell grid (headers + rows) |
145
+ | Image | `"""MarkdownModuleImage…"""` | src / alt / caption via a modal |
146
+ | File | `"""MarkdownModuleFile…"""` | Downloadable attachment |
147
+
148
+ Most blocks are parsed from **standard Markdown** — paste a Markdown document and it is split into blocks automatically. Headings also convert on the fly: type `# `, `## `, or `### ` at the start of a paragraph, or type `> `, `1. `, a code fence, or `---` for the matching non-heading block.
149
+
150
+ The special `"""…"""` blocks carry extra metadata and are used for images and files. Here is the exact Markdown the editor accepts and produces for every module:
151
+
152
+ ````markdown
153
+ # Heading 1
154
+
155
+ ## Heading 2
156
+
157
+ ### Heading 3
158
+
159
+ A **paragraph** with _inline_ markup and a [link](https://example.com).
160
+
161
+ - Bullet item one
162
+ - Bullet item two
163
+
164
+ 1. Numbered item one
165
+ 2. Numbered item two
166
+
167
+ > A blockquote line
168
+ > Another blockquote line
169
+
170
+ ```ts
171
+ const port = 25565;
172
+ ```
173
+
174
+ ---
175
+
176
+ | Module | Kind |
177
+ | ------ | ------------- |
178
+ | Table | Grid of cells |
179
+
180
+ """MarkdownModuleImage
181
+ src: https://example.com/photo.jpg
182
+ alt: A scenic view
183
+ caption: Photo caption
184
+ """
185
+
186
+ """MarkdownModuleFile
187
+ url: https://example.com/file.pdf
188
+ fileName: file.pdf
189
+ fileSize: 1024
190
+ mimeType: application/pdf
191
+ """
192
+ ````
193
+
129
194
  ### Image Upload
130
195
 
131
196
  Images are first-class blocks with **src**, **alt text**, and **caption** fields. Right‑click any image → **Edit Attributes** to open the editing modal.
@@ -147,10 +212,7 @@ async function uploadImage(file: File): Promise<string> {
147
212
  </script>
148
213
 
149
214
  <template>
150
- <MarkdownEditor
151
- :editor="editor"
152
- :image-upload-function="uploadImage"
153
- />
215
+ <MarkdownEditor :editor="editor" :image-upload-function="uploadImage" />
154
216
  </template>
155
217
  ```
156
218
 
@@ -174,34 +236,34 @@ caption: Photo taken during the 2026 summit
174
236
 
175
237
  Returns a reactive editor instance:
176
238
 
177
- | Member | Type | Description |
178
- |---|---|---|
179
- | `markdownContent` | `Ref<string>` | Reactive raw Markdown. Read to serialize, write to load content. |
180
- | `markdownNodes` | `Ref<MarkdownAstNode[]>` | Reactive array of AST nodes. |
181
- | `deleteNode(index)` | `(index: number) => void` | Remove the node at `index`. |
182
- | `addBlankNode(index?)` | `(index?: number) => number` | Insert an empty paragraph at `index` (or end). Returns the new index. |
183
- | `addNodeWithType(index, type, content?)` | `(index: number, type: MarkdownNodeType, content?: string) => number` | Insert a typed node. Returns the new index. |
184
- | `replaceNodeType(node, newType)` | `(node: MarkdownAstNode, type: MarkdownNodeType) => { newNode, index } \| null` | Convert between block types (e.g. paragraph → heading). |
185
- | `moveNode(from, to)` | `(fromIndex: number, toIndex: number) => void` | Programmatically reorder a block. |
239
+ | Member | Type | Description |
240
+ | ---------------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
241
+ | `markdownContent` | `Ref<string>` | Reactive raw Markdown. Read to serialize, write to load content. |
242
+ | `markdownNodes` | `Ref<MarkdownAstNode[]>` | Reactive array of AST nodes. |
243
+ | `deleteNode(index)` | `(index: number) => void` | Remove the node at `index`. |
244
+ | `addBlankNode(index?)` | `(index?: number) => number` | Insert an empty paragraph at `index` (or end). Returns the new index. |
245
+ | `addNodeWithType(index, type, content?)` | `(index: number, type: MarkdownNodeType, content?: string) => number` | Insert a typed node. Returns the new index. |
246
+ | `replaceNodeType(node, newType)` | `(node: MarkdownAstNode, type: MarkdownNodeType) => { newNode, index } \| null` | Convert between block types (e.g. paragraph → heading). |
247
+ | `moveNode(from, to)` | `(fromIndex: number, toIndex: number) => void` | Programmatically reorder a block. |
186
248
 
187
249
  #### `MarkdownEditor` props
188
250
 
189
- | Prop | Type | Required | Description |
190
- |---|---|---|---|
191
- | `editor` | `MarkdownEditorInstance` | ✓ | Instance from `useMarkdownEditor()`. |
192
- | `focusedNode` | `MarkdownAstNode \| null` | — | For `v-model:focused-node` tracking. |
193
- | `imageUploadFunction` | `(file: File) => Promise<string>` | — | Async callback for paste‑to‑upload. |
251
+ | Prop | Type | Required | Description |
252
+ | --------------------- | --------------------------------- | -------- | ------------------------------------ |
253
+ | `editor` | `MarkdownEditorInstance` | ✓ | Instance from `useMarkdownEditor()`. |
254
+ | `focusedNode` | `MarkdownAstNode \| null` | — | For `v-model:focused-node` tracking. |
255
+ | `imageUploadFunction` | `(file: File) => Promise<string>` | — | Async callback for paste‑to‑upload. |
194
256
 
195
257
  #### `MarkdownEditor` emits
196
258
 
197
- | Event | Payload | Description |
198
- |---|---|---|
259
+ | Event | Payload | Description |
260
+ | --------------------- | ------------------------- | -------------------------------------- |
199
261
  | `update:focused-node` | `MarkdownAstNode \| null` | Fires when focus moves to a new block. |
200
262
 
201
263
  #### `MarkdownEditor` slots
202
264
 
203
- | Slot | Description |
204
- |---|---|
265
+ | Slot | Description |
266
+ | ---------------- | --------------------------------------------------------------------------- |
205
267
  | `after-controls` | Injected inside every block, after the drag‑handle / add / delete controls. |
206
268
 
207
269
  ---
@@ -222,8 +284,20 @@ const markdown = `# Hello World
222
284
 
223
285
  This is a **paragraph** with *inline* formatting.
224
286
 
225
- - List item 1
226
- - List item 2
287
+ - Bullet item one
288
+ - Bullet item two
289
+
290
+ 1. Numbered item one
291
+ 2. Numbered item two
292
+
293
+ > A short blockquote.
294
+
295
+ | Feature | Status |
296
+ | --- | --- |
297
+ | Tables | Rendered |
298
+ | Code | Fenced |
299
+
300
+ ---
227
301
 
228
302
  """MarkdownModuleImage
229
303
  src: https://example.com/photo.jpg
@@ -244,7 +318,11 @@ Bind the editor's reactive `markdownContent` to the renderer:
244
318
 
245
319
  ```vue
246
320
  <script setup lang="ts">
247
- import { MarkdownEditor, MarkdownRenderer, useMarkdownEditor } from "@grandaniel/vue-markdown-editor";
321
+ import {
322
+ MarkdownEditor,
323
+ MarkdownRenderer,
324
+ useMarkdownEditor,
325
+ } from "@grandaniel/vue-markdown-editor";
248
326
 
249
327
  const editor = useMarkdownEditor("# Start writing…");
250
328
  </script>
@@ -290,10 +368,7 @@ caption: My custom caption
290
368
  </script>
291
369
 
292
370
  <template>
293
- <MarkdownRenderer
294
- :markdown="markdown"
295
- :renderer="renderer"
296
- />
371
+ <MarkdownRenderer :markdown="markdown" :renderer="renderer" />
297
372
  </template>
298
373
  ```
299
374
 
@@ -317,15 +392,20 @@ defineProps<{ state: MarkdownModuleImageState }>();
317
392
 
318
393
  #### Available node types and their state classes
319
394
 
320
- | Node Type | State Class | Prop Interface |
321
- |---|---|---|
322
- | `MarkdownAstNodeType.PARAGRAPH` | `MarkdownModuleTextState` | `{ text: string }` |
323
- | `MarkdownAstNodeType.HEADLINE1` | `MarkdownModuleTextState` | `{ text: string }` |
324
- | `MarkdownAstNodeType.HEADLINE2` | `MarkdownModuleTextState` | `{ text: string }` |
325
- | `MarkdownAstNodeType.HEADLINE3` | `MarkdownModuleTextState` | `{ text: string }` |
326
- | `MarkdownAstNodeType.LIST` | `MarkdownModuleListState` | `{ items: MarkdownModuleTextState[] }` |
327
- | `MarkdownAstNodeType.IMAGE` | `MarkdownModuleImageState` | `{ src: string; alt: string; caption: string }` |
328
- | `MarkdownAstNodeType.FILE` | `MarkdownModuleFileState` | `{ url: string; fileName: string; fileSize: number; mimeType: string; uploadError: string }` |
395
+ | Node Type | State Class | Prop Interface |
396
+ | ---------------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------- |
397
+ | `MarkdownAstNodeType.PARAGRAPH` | `MarkdownModuleTextState` | `{ text: string }` |
398
+ | `MarkdownAstNodeType.HEADLINE1` | `MarkdownModuleTextState` | `{ text: string }` |
399
+ | `MarkdownAstNodeType.HEADLINE2` | `MarkdownModuleTextState` | `{ text: string }` |
400
+ | `MarkdownAstNodeType.HEADLINE3` | `MarkdownModuleTextState` | `{ text: string }` |
401
+ | `MarkdownAstNodeType.LIST` | `MarkdownModuleListState` | `{ items: MarkdownModuleTextState[] }` |
402
+ | `MarkdownAstNodeType.ORDERED_LIST` | `MarkdownModuleListState` | `{ items: MarkdownModuleTextState[] }` |
403
+ | `MarkdownAstNodeType.BLOCKQUOTE` | `MarkdownModuleTextState` | `{ text: string }` |
404
+ | `MarkdownAstNodeType.CODE_BLOCK` | `MarkdownModuleCodeBlockState` | `{ code: string; language: string }` |
405
+ | `MarkdownAstNodeType.HR` | `MarkdownModuleHrState` | `{}` |
406
+ | `MarkdownAstNodeType.TABLE` | `MarkdownModuleTableState` | `{ headers: string[]; rows: string[][] }` |
407
+ | `MarkdownAstNodeType.IMAGE` | `MarkdownModuleImageState` | `{ src: string; alt: string; caption: string }` |
408
+ | `MarkdownAstNodeType.FILE` | `MarkdownModuleFileState` | `{ url: string; fileName: string; fileSize: number; mimeType: string; uploadError: string }` |
329
409
 
330
410
  ---
331
411
 
@@ -335,38 +415,43 @@ defineProps<{ state: MarkdownModuleImageState }>();
335
415
 
336
416
  Returns a reactive renderer instance with a customizable component registry:
337
417
 
338
- | Member | Type | Description |
339
- |---|---|---|
340
- | `componentRegistry` | `Reactive<{ [K in MarkdownNodeType]: RenderComponent<RenderStateMap[K]> }>` | Reactive map of node types to their current render components. |
418
+ | Member | Type | Description |
419
+ | ------------------------------------ | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
420
+ | `componentRegistry` | `Reactive<{ [K in MarkdownNodeType]: RenderComponent<RenderStateMap[K]> }>` | Reactive map of node types to their current render components. |
341
421
  | `overrideComponent(type, component)` | `<K extends MarkdownNodeType>(type: K, component: RenderComponent<RenderStateMap[K]>) => void` | Replace the render component for a given node type. Strictly typed — the component must accept the correct `state` prop. |
342
422
 
343
423
  #### `MarkdownRenderer` props
344
424
 
345
- | Prop | Type | Required | Description |
346
- |---|---|---|---|
347
- | `markdown` | `string` | ✓ | Raw Markdown string to render as HTML. |
348
- | `renderer` | `MarkdownRendererInstance` | — | Instance from `useMarkdownRenderer()`. Optional — when omitted, the built-in default render components are used. Provide this to supply custom render components via `overrideComponent()`. |
425
+ | Prop | Type | Required | Description |
426
+ | ---------- | -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
427
+ | `markdown` | `string` | ✓ | Raw Markdown string to render as HTML. |
428
+ | `renderer` | `MarkdownRendererInstance` | — | Instance from `useMarkdownRenderer()`. Optional — when omitted, the built-in default render components are used. Provide this to supply custom render components via `overrideComponent()`. |
349
429
 
350
430
  ---
351
431
 
352
432
  ## Exported Types & Utilities
353
433
 
354
- | Export | Kind |
355
- |---|---|
356
- | `MarkdownEditorInstance` | Type — return type of `useMarkdownEditor()`. |
357
- | `MarkdownRendererInstance` | Type — return type of `useMarkdownRenderer()`. |
358
- | `MarkdownAstNode` | Class — AST node with `id`, `type`, `componentState`, `editingState`. |
359
- | `MarkdownAstNodeType` | Enum — `PARAGRAPH`, `HEADLINE1`, `HEADLINE2`, `HEADLINE3`, `IMAGE`, `LIST`, `FILE`. |
360
- | `ImageNode` | Type alias — `MarkdownAstNode<MarkdownModuleImageState>`. |
361
- | `TextNode` | Type alias — `MarkdownAstNode<MarkdownModuleTextState>`. |
362
- | `FileNode` | Type alias — `MarkdownAstNode<MarkdownModuleFileState>`. |
363
- | `TextishNodeType` | Type — union of `PARAGRAPH \| HEADLINE1 \| HEADLINE2 \| HEADLINE3 \| LIST`. |
364
- | `isTextNodeState(node)` | Type guard for text‑based nodes. |
365
- | `isTextNodeType(type)` | Type guard for text‑based node types. |
366
- | `RenderComponent<TState>` | Type a Vue component that accepts `{ state: TState }` as props. |
367
- | `RenderStateMap` | Interface maps each `MarkdownNodeType` to its state class for strict typing. |
368
- | `MarkdownModuleImageState` | Classstate for image nodes (`src`, `alt`, `caption`). |
369
- | `MarkdownModuleFileState` | Classstate for file nodes (`url`, `fileName`, `fileSize`, `mimeType`, `uploadError`). |
434
+ | Export | Kind |
435
+ | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
436
+ | `MarkdownEditorInstance` | Type — return type of `useMarkdownEditor()`. |
437
+ | `MarkdownRendererInstance` | Type — return type of `useMarkdownRenderer()`. |
438
+ | `MarkdownAstNode` | Class — AST node with `id`, `type`, `componentState`, `editingState`. |
439
+ | `MarkdownAstNodeType` | Enum — `PARAGRAPH`, `HEADLINE1`, `HEADLINE2`, `HEADLINE3`, `IMAGE`, `LIST`, `FILE`, `BLOCKQUOTE`, `CODE_BLOCK`, `HR`, `TABLE`, `ORDERED_LIST`. |
440
+ | `ImageNode` | Type alias — `MarkdownAstNode<MarkdownModuleImageState>`. |
441
+ | `TextNode` | Type alias — `MarkdownAstNode<MarkdownModuleTextState>`. |
442
+ | `FileNode` | Type alias — `MarkdownAstNode<MarkdownModuleFileState>`. |
443
+ | `CodeBlockNode` | Type alias — `MarkdownAstNode<MarkdownModuleCodeBlockState>`. |
444
+ | `TableNode` | Type alias `MarkdownAstNode<MarkdownModuleTableState>`. |
445
+ | `TextishNodeType` | Type union of `PARAGRAPH \| HEADLINE1 \| HEADLINE2 \| HEADLINE3 \| LIST \| ORDERED_LIST \| BLOCKQUOTE`. |
446
+ | `isTextNodeState(node)` | Type guard for text‑based nodes. |
447
+ | `isTextNodeType(type)` | Type guard for text‑based node types. |
448
+ | `RenderComponent<TState>` | Typea Vue component that accepts `{ state: TState }` as props. |
449
+ | `RenderStateMap` | Interfacemaps each `MarkdownNodeType` to its state class for strict typing. |
450
+ | `MarkdownModuleImageState` | Class — state for image nodes (`src`, `alt`, `caption`). |
451
+ | `MarkdownModuleFileState` | Class — state for file nodes (`url`, `fileName`, `fileSize`, `mimeType`, `uploadError`). |
452
+ | `MarkdownModuleCodeBlockState` | Class — state for code blocks (`code`, `language`). |
453
+ | `MarkdownModuleTableState` | Class — state for tables (`headers`, `rows`). |
454
+ | `MarkdownModuleHrState` | Class — state for dividers (empty). |
370
455
 
371
456
  ---
372
457
 
@@ -376,32 +461,32 @@ All components use scoped SCSS. To override styles, use **global CSS** with high
376
461
 
377
462
  ### CSS class reference
378
463
 
379
- | Class | Applies to |
380
- |---|---|
381
- | `.markdown-editor` | Root editor container |
382
- | `.markdown-editor-module` | Individual block wrapper — `.is-focused` when active |
383
- | `.markdown-editor-module-controls` | Left control bar (drag handle + add/delete buttons) |
384
- | `.markdown-editor-module-content` | Content area inside a block |
385
- | `.markdown-editor-module-content-focused` | Content area when the block is focused |
386
- | `.markdown-editor-focus-controls` | Row containing drag‑handle, delete, and add buttons |
387
- | `.drag-handle` | SortableJS drag handle (⠿) |
388
- | `.focus-control-btn` | Delete / Add buttons in the control bar |
389
- | `.markdown-editor-context-menu` | Floating block context menu (`z-index: 1000`) |
390
- | `.markdown-editor-context-menu-block-item` | Full‑width context menu button |
391
- | `.markdown-editor-context-menu-inline-item` | Inline toolbar button (`.is-active` when toggled) |
392
- | `.markdown-editor-modal-overlay` | Modal backdrop (`z-index: 9999`) |
393
- | `.markdown-editor-modal` | Modal container |
394
- | `.markdown-editor-modal-header` | Modal title bar |
395
- | `.markdown-editor-modal-title` | Modal heading text |
396
- | `.markdown-editor-modal-close` | Close (✕) button |
397
- | `.markdown-editor-modal-body` | Modal content area |
398
- | `.markdown-editor-modal-footer` | Modal action bar |
399
- | `.markdown-editor-modal-button` | Base modal button |
400
- | `.markdown-editor-modal-button-primary` | Primary (Save) button — blue |
401
- | `.markdown-editor-modal-button-secondary` | Secondary (Cancel) button — gray |
402
- | `.markdown-module-image` | Image block wrapper |
403
- | `.markdown-module-image-form` | Image edit form inside the modal |
404
- | `.markdown-module-image-form-field` | Form field group (label + input) |
464
+ | Class | Applies to |
465
+ | ------------------------------------------- | ---------------------------------------------------- |
466
+ | `.markdown-editor` | Root editor container |
467
+ | `.markdown-editor-module` | Individual block wrapper — `.is-focused` when active |
468
+ | `.markdown-editor-module-controls` | Left control bar (drag handle + add/delete buttons) |
469
+ | `.markdown-editor-module-content` | Content area inside a block |
470
+ | `.markdown-editor-module-content-focused` | Content area when the block is focused |
471
+ | `.markdown-editor-focus-controls` | Row containing drag‑handle, delete, and add buttons |
472
+ | `.drag-handle` | SortableJS drag handle (⠿) |
473
+ | `.focus-control-btn` | Delete / Add buttons in the control bar |
474
+ | `.markdown-editor-context-menu` | Floating block context menu (`z-index: 1000`) |
475
+ | `.markdown-editor-context-menu-block-item` | Full‑width context menu button |
476
+ | `.markdown-editor-context-menu-inline-item` | Inline toolbar button (`.is-active` when toggled) |
477
+ | `.markdown-editor-modal-overlay` | Modal backdrop (`z-index: 9999`) |
478
+ | `.markdown-editor-modal` | Modal container |
479
+ | `.markdown-editor-modal-header` | Modal title bar |
480
+ | `.markdown-editor-modal-title` | Modal heading text |
481
+ | `.markdown-editor-modal-close` | Close (✕) button |
482
+ | `.markdown-editor-modal-body` | Modal content area |
483
+ | `.markdown-editor-modal-footer` | Modal action bar |
484
+ | `.markdown-editor-modal-button` | Base modal button |
485
+ | `.markdown-editor-modal-button-primary` | Primary (Save) button — blue |
486
+ | `.markdown-editor-modal-button-secondary` | Secondary (Cancel) button — gray |
487
+ | `.markdown-module-image` | Image block wrapper |
488
+ | `.markdown-module-image-form` | Image edit form inside the modal |
489
+ | `.markdown-module-image-form-field` | Form field group (label + input) |
405
490
 
406
491
  ### Styling TipTap content
407
492
 
@@ -436,12 +521,12 @@ Each text‑based block hosts its own TinyMCE‑style TipTap editor. Target `.ti
436
521
 
437
522
  We take package integrity seriously.
438
523
 
439
- | Measure | Status |
440
- |---|---|
524
+ | Measure | Status |
525
+ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
441
526
  | **npm package provenance** | ✅ Enabled — every publish includes [provenance attestations](https://docs.npmjs.com/generating-provenance-statements) via GitHub Actions and Sigstore. |
442
- | **CI/CD** | ✅ GitHub Actions runs `npm ci` → `npm test` → `npm run build` → publish on every push to `dev` and `main`. |
443
- | **Prerelease tags** | ✅ Non‑main branches publish with a `dev` dist‑tag (e.g. `1.1.3-dev.abc1234`). |
444
- | **Dependabot** | 🔜 Planned — automated dependency update PRs will be enabled via `.github/dependabot.yml`. |
527
+ | **CI/CD** | ✅ GitHub Actions runs `npm ci` → `npm test` → `npm run build` → publish on every push to `dev` and `main`. |
528
+ | **Prerelease tags** | ✅ Non‑main branches publish with a `dev` dist‑tag (e.g. `1.1.3-dev.abc1234`). |
529
+ | **Dependabot** | 🔜 Planned — automated dependency update PRs will be enabled via `.github/dependabot.yml`. |
445
530
 
446
531
  To verify provenance locally:
447
532
 
@@ -495,32 +580,32 @@ All components use scoped SCSS. To override styles, use **global CSS** with high
495
580
 
496
581
  ### CSS class reference
497
582
 
498
- | Class | Applies to |
499
- |---|---|
500
- | `.markdown-editor` | Root editor container |
501
- | `.markdown-editor-module` | Individual block wrapper — `.is-focused` when active |
502
- | `.markdown-editor-module-controls` | Left control bar (drag handle + add/delete buttons) |
503
- | `.markdown-editor-module-content` | Content area inside a block |
504
- | `.markdown-editor-module-content-focused` | Content area when the block is focused |
505
- | `.markdown-editor-focus-controls` | Row containing drag‑handle, delete, and add buttons |
506
- | `.drag-handle` | SortableJS drag handle (⠿) |
507
- | `.focus-control-btn` | Delete / Add buttons in the control bar |
508
- | `.markdown-editor-context-menu` | Floating block context menu (`z-index: 1000`) |
509
- | `.markdown-editor-context-menu-block-item` | Full‑width context menu button |
510
- | `.markdown-editor-context-menu-inline-item` | Inline toolbar button (`.is-active` when toggled) |
511
- | `.markdown-editor-modal-overlay` | Modal backdrop (`z-index: 9999`) |
512
- | `.markdown-editor-modal` | Modal container |
513
- | `.markdown-editor-modal-header` | Modal title bar |
514
- | `.markdown-editor-modal-title` | Modal heading text |
515
- | `.markdown-editor-modal-close` | Close (✕) button |
516
- | `.markdown-editor-modal-body` | Modal content area |
517
- | `.markdown-editor-modal-footer` | Modal action bar |
518
- | `.markdown-editor-modal-button` | Base modal button |
519
- | `.markdown-editor-modal-button-primary` | Primary (Save) button — blue |
520
- | `.markdown-editor-modal-button-secondary` | Secondary (Cancel) button — gray |
521
- | `.markdown-module-image` | Image block wrapper |
522
- | `.markdown-module-image-form` | Image edit form inside the modal |
523
- | `.markdown-module-image-form-field` | Form field group (label + input) |
583
+ | Class | Applies to |
584
+ | ------------------------------------------- | ---------------------------------------------------- |
585
+ | `.markdown-editor` | Root editor container |
586
+ | `.markdown-editor-module` | Individual block wrapper — `.is-focused` when active |
587
+ | `.markdown-editor-module-controls` | Left control bar (drag handle + add/delete buttons) |
588
+ | `.markdown-editor-module-content` | Content area inside a block |
589
+ | `.markdown-editor-module-content-focused` | Content area when the block is focused |
590
+ | `.markdown-editor-focus-controls` | Row containing drag‑handle, delete, and add buttons |
591
+ | `.drag-handle` | SortableJS drag handle (⠿) |
592
+ | `.focus-control-btn` | Delete / Add buttons in the control bar |
593
+ | `.markdown-editor-context-menu` | Floating block context menu (`z-index: 1000`) |
594
+ | `.markdown-editor-context-menu-block-item` | Full‑width context menu button |
595
+ | `.markdown-editor-context-menu-inline-item` | Inline toolbar button (`.is-active` when toggled) |
596
+ | `.markdown-editor-modal-overlay` | Modal backdrop (`z-index: 9999`) |
597
+ | `.markdown-editor-modal` | Modal container |
598
+ | `.markdown-editor-modal-header` | Modal title bar |
599
+ | `.markdown-editor-modal-title` | Modal heading text |
600
+ | `.markdown-editor-modal-close` | Close (✕) button |
601
+ | `.markdown-editor-modal-body` | Modal content area |
602
+ | `.markdown-editor-modal-footer` | Modal action bar |
603
+ | `.markdown-editor-modal-button` | Base modal button |
604
+ | `.markdown-editor-modal-button-primary` | Primary (Save) button — blue |
605
+ | `.markdown-editor-modal-button-secondary` | Secondary (Cancel) button — gray |
606
+ | `.markdown-module-image` | Image block wrapper |
607
+ | `.markdown-module-image-form` | Image edit form inside the modal |
608
+ | `.markdown-module-image-form-field` | Form field group (label + input) |
524
609
 
525
610
  ### Styling TipTap content
526
611
 
@@ -555,12 +640,12 @@ Each text‑based block hosts its own TinyMCE‑style TipTap editor. Target `.ti
555
640
 
556
641
  We take package integrity seriously.
557
642
 
558
- | Measure | Status |
559
- |---|---|
643
+ | Measure | Status |
644
+ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
560
645
  | **npm package provenance** | ✅ Enabled — every publish includes [provenance attestations](https://docs.npmjs.com/generating-provenance-statements) via GitHub Actions and Sigstore. |
561
- | **CI/CD** | ✅ GitHub Actions runs `npm ci` → `npm test` → `npm run build` → publish on every push to `dev` and `main`. |
562
- | **Prerelease tags** | ✅ Non‑main branches publish with a `dev` dist‑tag (e.g. `1.1.3-dev.abc1234`). |
563
- | **Dependabot** | 🔜 Planned — automated dependency update PRs will be enabled via `.github/dependabot.yml`. |
646
+ | **CI/CD** | ✅ GitHub Actions runs `npm ci` → `npm test` → `npm run build` → publish on every push to `dev` and `main`. |
647
+ | **Prerelease tags** | ✅ Non‑main branches publish with a `dev` dist‑tag (e.g. `1.1.3-dev.abc1234`). |
648
+ | **Dependabot** | 🔜 Planned — automated dependency update PRs will be enabled via `.github/dependabot.yml`. |
564
649
 
565
650
  To verify provenance locally:
566
651
 
@@ -1,6 +1,8 @@
1
+ import type MarkdownModuleCodeBlockState from "../Modules/MarkdownModuleCodeBlockState";
1
2
  import type MarkdownModuleFileState from "../Modules/MarkdownModuleFileState";
2
3
  import type MarkdownModuleImageState from "../Modules/MarkdownModuleImageState";
3
4
  import type MarkdownModuleListState from "../Modules/MarkdownModuleListState";
5
+ import type MarkdownModuleTableState from "../Modules/MarkdownModuleTableState";
4
6
  import MarkdownModuleTextState from "../Modules/MarkdownModuleTextState";
5
7
  import { MarkdownAstNode } from "../Types/MarkdownAstNode";
6
8
  import { type TextishNodeType } from "../Types/MarkdownAstNodeType";
@@ -9,6 +11,11 @@ declare class MarkdownNodeFactory {
9
11
  createTextNode(type: TextishNodeType, text: string): MarkdownAstNode<MarkdownModuleTextState>;
10
12
  createImageNode(src: string, alt: string, caption?: string): MarkdownAstNode<MarkdownModuleImageState>;
11
13
  createListNode(items: string[]): MarkdownAstNode<MarkdownModuleListState>;
14
+ createOrderedListNode(items: string[]): MarkdownAstNode<MarkdownModuleListState>;
15
+ createBlockquoteNode(text: string): MarkdownAstNode<MarkdownModuleTextState>;
16
+ createCodeBlockNode(code: string, language?: string): MarkdownAstNode<MarkdownModuleCodeBlockState>;
17
+ createHrNode(): MarkdownAstNode;
18
+ createTableNode(headers: string[], rows: string[][]): MarkdownAstNode<MarkdownModuleTableState>;
12
19
  createBlankParagraph(): MarkdownAstNode<MarkdownModuleTextState>;
13
20
  createFileNode(url: string, fileName: string, fileSize: number, mimeType: string, uploadError?: string): MarkdownAstNode<MarkdownModuleFileState>;
14
21
  }
@@ -1,13 +1,17 @@
1
1
  import type { Component } from "vue";
2
+ import MarkdownModuleCodeBlockState from "./Modules/MarkdownModuleCodeBlockState";
2
3
  import MarkdownModuleFileState from "./Modules/MarkdownModuleFileState";
4
+ import MarkdownModuleHrState from "./Modules/MarkdownModuleHrState";
3
5
  import MarkdownModuleImageState from "./Modules/MarkdownModuleImageState";
4
6
  import MarkdownModuleListState from "./Modules/MarkdownModuleListState";
7
+ import MarkdownModuleTableState from "./Modules/MarkdownModuleTableState";
5
8
  import MarkdownModuleTextState from "./Modules/MarkdownModuleTextState";
6
9
  import type { MarkdownAstNode } from "./Types/MarkdownAstNode";
7
10
  import MarkdownNodeType from "./Types/MarkdownAstNodeType";
11
+ type StateTypeConstructor = typeof MarkdownModuleTextState | typeof MarkdownModuleImageState | typeof MarkdownModuleListState | typeof MarkdownModuleFileState | typeof MarkdownModuleCodeBlockState | typeof MarkdownModuleHrState | typeof MarkdownModuleTableState;
8
12
  type MarkdownComponentRegistryEntry = {
9
13
  component: Component;
10
- stateType: typeof MarkdownModuleTextState | typeof MarkdownModuleImageState | typeof MarkdownModuleListState | typeof MarkdownModuleFileState;
14
+ stateType: StateTypeConstructor;
11
15
  };
12
16
  declare const registry: Record<MarkdownNodeType, MarkdownComponentRegistryEntry>;
13
17
  export declare function isTextNodeState(state: MarkdownAstNode): state is MarkdownAstNode & {
@@ -16,9 +16,9 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
16
16
  required: true;
17
17
  };
18
18
  }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
19
- focus: () => any;
20
19
  "update:cursor-position": (args_0: number) => any;
21
20
  "change-type": (args_0: MarkdownAstNode<object>, args_1: number) => any;
21
+ focus: () => any;
22
22
  "update:node": (args_0: Record<string, unknown>) => any;
23
23
  }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
24
24
  node: {
@@ -30,9 +30,9 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
30
30
  required: true;
31
31
  };
32
32
  }>> & Readonly<{
33
- onFocus?: (() => any) | undefined;
34
33
  "onUpdate:cursor-position"?: ((args_0: number) => any) | undefined;
35
34
  "onChange-type"?: ((args_0: MarkdownAstNode<object>, args_1: number) => any) | undefined;
35
+ onFocus?: (() => any) | undefined;
36
36
  "onUpdate:node"?: ((args_0: Record<string, unknown>) => any) | undefined;
37
37
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
38
38
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -0,0 +1,20 @@
1
+ import type MarkdownModuleTextState from "./MarkdownModuleTextState";
2
+ type __VLS_ModelProps = {
3
+ modelValue: MarkdownModuleTextState;
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {
6
+ editor: import("vue").ShallowRef<import("@tiptap/vue-3").Editor | undefined, import("@tiptap/vue-3").Editor | undefined>;
7
+ focus: () => void;
8
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
+ "update:model-value": (componentState: MarkdownModuleTextState) => any;
10
+ "update:cursor-position": (cursorPosition: number) => any;
11
+ "change-type": (newType: import("..").MarkdownAstNodeType) => any;
12
+ "update:modelValue": (value: MarkdownModuleTextState) => any;
13
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
14
+ "onUpdate:model-value"?: ((componentState: MarkdownModuleTextState) => any) | undefined;
15
+ "onUpdate:cursor-position"?: ((cursorPosition: number) => any) | undefined;
16
+ "onChange-type"?: ((newType: import("..").MarkdownAstNodeType) => any) | undefined;
17
+ "onUpdate:modelValue"?: ((value: MarkdownModuleTextState) => any) | undefined;
18
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
+ declare const _default: typeof __VLS_export;
20
+ export default _default;