@grandaniel/vue-markdown-editor 1.2.0 → 1.3.0-dev.e600a13
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.
- package/README.md +226 -141
- package/dist/components/MarkdownEditor/Factory/MarkdownNodeFactory.d.ts +7 -0
- package/dist/components/MarkdownEditor/MarkdownComponentRegistry.d.ts +5 -1
- package/dist/components/MarkdownEditor/MarkdownEditorModule.vue.d.ts +2 -2
- package/dist/components/MarkdownEditor/Modules/MarkdownModuleBlockquote.vue.d.ts +20 -0
- package/dist/components/MarkdownEditor/Modules/MarkdownModuleCodeBlock.vue.d.ts +66 -0
- package/dist/components/MarkdownEditor/Modules/MarkdownModuleCodeBlockState.d.ts +5 -0
- package/dist/components/MarkdownEditor/Modules/MarkdownModuleFile.vue.d.ts +2 -2
- package/dist/components/MarkdownEditor/Modules/MarkdownModuleHeadline1.vue.d.ts +2 -2
- package/dist/components/MarkdownEditor/Modules/MarkdownModuleHeadline2.vue.d.ts +2 -2
- package/dist/components/MarkdownEditor/Modules/MarkdownModuleHeadline3.vue.d.ts +2 -2
- package/dist/components/MarkdownEditor/Modules/MarkdownModuleHr.vue.d.ts +14 -0
- package/dist/components/MarkdownEditor/Modules/MarkdownModuleHrState.d.ts +3 -0
- package/dist/components/MarkdownEditor/Modules/MarkdownModuleOrderedList.vue.d.ts +14 -0
- package/dist/components/MarkdownEditor/Modules/MarkdownModuleParagraph.vue.d.ts +2 -2
- package/dist/components/MarkdownEditor/Modules/MarkdownModuleTable.vue.d.ts +14 -0
- package/dist/components/MarkdownEditor/Modules/MarkdownModuleTableState.d.ts +5 -0
- package/dist/components/MarkdownEditor/Types/MarkdownAstNode.d.ts +4 -0
- package/dist/components/MarkdownEditor/Types/MarkdownAstNodeType.d.ts +7 -2
- package/dist/components/MarkdownEditor/index.d.ts +7 -1
- package/dist/components/MarkdownRenderer/Composable/useMarkdownRenderer.d.ts +822 -0
- package/dist/components/MarkdownRenderer/MarkdownRenderer.vue.d.ts +869 -54
- package/dist/components/MarkdownRenderer/RenderModules/MarkdownModuleBlockquoteRender.vue.d.ts +7 -0
- package/dist/components/MarkdownRenderer/RenderModules/MarkdownModuleCodeBlockRender.vue.d.ts +7 -0
- package/dist/components/MarkdownRenderer/RenderModules/MarkdownModuleHrRender.vue.d.ts +6 -0
- package/dist/components/MarkdownRenderer/RenderModules/MarkdownModuleOrderedListRender.vue.d.ts +7 -0
- package/dist/components/MarkdownRenderer/RenderModules/MarkdownModuleTableRender.vue.d.ts +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/markdown.d.ts +20 -0
- package/dist/vue-markdown-editor.css +1 -1
- package/dist/vue-markdown-editor.mjs +11369 -7964
- 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,
|
|
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
|
|
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)**
|
|
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 {
|
|
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
|
|
116
|
-
|
|
117
|
-
| <kbd>↑</kbd> / <kbd>↓</kbd>
|
|
118
|
-
| <kbd>Enter</kbd>
|
|
119
|
-
| <kbd>Backspace</kbd> (empty block) | Delete the block, focus moves up
|
|
120
|
-
| <kbd>Delete</kbd> (empty block)
|
|
121
|
-
| Click blank area
|
|
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
|
|
178
|
-
|
|
179
|
-
| `markdownContent`
|
|
180
|
-
| `markdownNodes`
|
|
181
|
-
| `deleteNode(index)`
|
|
182
|
-
| `addBlankNode(index?)`
|
|
183
|
-
| `addNodeWithType(index, type, content?)` | `(index: number, type: MarkdownNodeType, content?: string) => number`
|
|
184
|
-
| `replaceNodeType(node, newType)`
|
|
185
|
-
| `moveNode(from, to)`
|
|
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
|
|
190
|
-
|
|
191
|
-
| `editor`
|
|
192
|
-
| `focusedNode`
|
|
193
|
-
| `imageUploadFunction` | `(file: File) => Promise<string>` | —
|
|
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
|
|
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
|
|
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
|
-
-
|
|
226
|
-
-
|
|
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 {
|
|
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
|
|
321
|
-
|
|
322
|
-
| `MarkdownAstNodeType.PARAGRAPH`
|
|
323
|
-
| `MarkdownAstNodeType.HEADLINE1`
|
|
324
|
-
| `MarkdownAstNodeType.HEADLINE2`
|
|
325
|
-
| `MarkdownAstNodeType.HEADLINE3`
|
|
326
|
-
| `MarkdownAstNodeType.LIST`
|
|
327
|
-
| `MarkdownAstNodeType.
|
|
328
|
-
| `MarkdownAstNodeType.
|
|
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
|
|
339
|
-
|
|
340
|
-
| `componentRegistry`
|
|
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
|
|
346
|
-
|
|
347
|
-
| `markdown` | `string`
|
|
348
|
-
| `renderer` | `MarkdownRendererInstance` | —
|
|
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
|
|
355
|
-
|
|
356
|
-
| `MarkdownEditorInstance`
|
|
357
|
-
| `MarkdownRendererInstance`
|
|
358
|
-
| `MarkdownAstNode`
|
|
359
|
-
| `MarkdownAstNodeType`
|
|
360
|
-
| `ImageNode`
|
|
361
|
-
| `TextNode`
|
|
362
|
-
| `FileNode`
|
|
363
|
-
| `
|
|
364
|
-
| `
|
|
365
|
-
| `
|
|
366
|
-
| `
|
|
367
|
-
| `
|
|
368
|
-
| `
|
|
369
|
-
| `
|
|
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>` | Type — a Vue component that accepts `{ state: TState }` as props. |
|
|
449
|
+
| `RenderStateMap` | Interface — maps 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
|
|
380
|
-
|
|
381
|
-
| `.markdown-editor`
|
|
382
|
-
| `.markdown-editor-module`
|
|
383
|
-
| `.markdown-editor-module-controls`
|
|
384
|
-
| `.markdown-editor-module-content`
|
|
385
|
-
| `.markdown-editor-module-content-focused`
|
|
386
|
-
| `.markdown-editor-focus-controls`
|
|
387
|
-
| `.drag-handle`
|
|
388
|
-
| `.focus-control-btn`
|
|
389
|
-
| `.markdown-editor-context-menu`
|
|
390
|
-
| `.markdown-editor-context-menu-block-item`
|
|
391
|
-
| `.markdown-editor-context-menu-inline-item` | Inline toolbar button (`.is-active` when toggled)
|
|
392
|
-
| `.markdown-editor-modal-overlay`
|
|
393
|
-
| `.markdown-editor-modal`
|
|
394
|
-
| `.markdown-editor-modal-header`
|
|
395
|
-
| `.markdown-editor-modal-title`
|
|
396
|
-
| `.markdown-editor-modal-close`
|
|
397
|
-
| `.markdown-editor-modal-body`
|
|
398
|
-
| `.markdown-editor-modal-footer`
|
|
399
|
-
| `.markdown-editor-modal-button`
|
|
400
|
-
| `.markdown-editor-modal-button-primary`
|
|
401
|
-
| `.markdown-editor-modal-button-secondary`
|
|
402
|
-
| `.markdown-module-image`
|
|
403
|
-
| `.markdown-module-image-form`
|
|
404
|
-
| `.markdown-module-image-form-field`
|
|
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
|
|
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**
|
|
443
|
-
| **Prerelease tags**
|
|
444
|
-
| **Dependabot**
|
|
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
|
|
499
|
-
|
|
500
|
-
| `.markdown-editor`
|
|
501
|
-
| `.markdown-editor-module`
|
|
502
|
-
| `.markdown-editor-module-controls`
|
|
503
|
-
| `.markdown-editor-module-content`
|
|
504
|
-
| `.markdown-editor-module-content-focused`
|
|
505
|
-
| `.markdown-editor-focus-controls`
|
|
506
|
-
| `.drag-handle`
|
|
507
|
-
| `.focus-control-btn`
|
|
508
|
-
| `.markdown-editor-context-menu`
|
|
509
|
-
| `.markdown-editor-context-menu-block-item`
|
|
510
|
-
| `.markdown-editor-context-menu-inline-item` | Inline toolbar button (`.is-active` when toggled)
|
|
511
|
-
| `.markdown-editor-modal-overlay`
|
|
512
|
-
| `.markdown-editor-modal`
|
|
513
|
-
| `.markdown-editor-modal-header`
|
|
514
|
-
| `.markdown-editor-modal-title`
|
|
515
|
-
| `.markdown-editor-modal-close`
|
|
516
|
-
| `.markdown-editor-modal-body`
|
|
517
|
-
| `.markdown-editor-modal-footer`
|
|
518
|
-
| `.markdown-editor-modal-button`
|
|
519
|
-
| `.markdown-editor-modal-button-primary`
|
|
520
|
-
| `.markdown-editor-modal-button-secondary`
|
|
521
|
-
| `.markdown-module-image`
|
|
522
|
-
| `.markdown-module-image-form`
|
|
523
|
-
| `.markdown-module-image-form-field`
|
|
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
|
|
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**
|
|
562
|
-
| **Prerelease tags**
|
|
563
|
-
| **Dependabot**
|
|
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:
|
|
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;
|