xiaodao-editor 0.1.1
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 +306 -0
- package/dist/block-editor.js +23254 -0
- package/dist/block-editor.umd.cjs +26 -0
- package/dist/core/Editor.d.ts +83 -0
- package/dist/core/command/Command.d.ts +32 -0
- package/dist/core/command/InputRule.d.ts +21 -0
- package/dist/core/command/Keymap.d.ts +42 -0
- package/dist/core/command/SlashCommand.d.ts +24 -0
- package/dist/core/command/primitiveCommands.d.ts +97 -0
- package/dist/core/extension/Extension.d.ts +52 -0
- package/dist/core/extension/Registry.d.ts +46 -0
- package/dist/core/history/HistoryManager.d.ts +37 -0
- package/dist/core/ids.d.ts +5 -0
- package/dist/core/index.d.ts +35 -0
- package/dist/core/plugin/Plugin.d.ts +26 -0
- package/dist/core/schema/BlockSchema.d.ts +70 -0
- package/dist/core/schema/SchemaRegistry.d.ts +22 -0
- package/dist/core/selection/Selection.d.ts +25 -0
- package/dist/core/serialize/Serializer.d.ts +31 -0
- package/dist/core/state/EditorState.d.ts +26 -0
- package/dist/core/state/Step.d.ts +39 -0
- package/dist/core/state/Transaction.d.ts +52 -0
- package/dist/core/state/invert.d.ts +19 -0
- package/dist/core/state/store.d.ts +78 -0
- package/dist/core/types.d.ts +105 -0
- package/dist/extensions/BulletList.d.ts +2 -0
- package/dist/extensions/CodeBlock.d.ts +2 -0
- package/dist/extensions/Divider.d.ts +2 -0
- package/dist/extensions/Heading.d.ts +2 -0
- package/dist/extensions/History.d.ts +2 -0
- package/dist/extensions/Image.d.ts +34 -0
- package/dist/extensions/Keymap.d.ts +2 -0
- package/dist/extensions/OrderedList.d.ts +18 -0
- package/dist/extensions/Paragraph.d.ts +5 -0
- package/dist/extensions/Quote.d.ts +2 -0
- package/dist/extensions/Table.d.ts +74 -0
- package/dist/extensions/TableOfContents.d.ts +18 -0
- package/dist/extensions/TodoList.d.ts +2 -0
- package/dist/extensions/_commonAttrs.d.ts +84 -0
- package/dist/extensions/builtin.d.ts +15 -0
- package/dist/extensions/tableModel.d.ts +199 -0
- package/dist/i18n.d.ts +31 -0
- package/dist/index.d.ts +30 -0
- package/dist/style.css +1 -0
- package/dist/test-img.png +0 -0
- package/dist/view/BlockContent.vue.d.ts +21 -0
- package/dist/view/BlockEditor.vue.d.ts +67 -0
- package/dist/view/BlockHost.vue.d.ts +51 -0
- package/dist/view/BlockList.vue.d.ts +64 -0
- package/dist/view/clipboard.d.ts +11 -0
- package/dist/view/context.d.ts +116 -0
- package/dist/view/domSelection.d.ts +65 -0
- package/dist/view/imageUpload.d.ts +130 -0
- package/dist/view/inlineDom.d.ts +23 -0
- package/dist/view/keymapHandler.d.ts +7 -0
- package/dist/view/ui/BlockHandle.vue.d.ts +33 -0
- package/dist/view/ui/BlockSettingsMenu.vue.d.ts +32 -0
- package/dist/view/ui/CodeLangPicker.vue.d.ts +20 -0
- package/dist/view/ui/HoverToolbar.vue.d.ts +81 -0
- package/dist/view/ui/LinkPopover.vue.d.ts +62 -0
- package/dist/view/ui/MobileToolbar.vue.d.ts +29 -0
- package/dist/view/ui/NumberPicker.vue.d.ts +20 -0
- package/dist/view/ui/OrderedListMenu.vue.d.ts +37 -0
- package/dist/view/ui/PlusMenu.vue.d.ts +29 -0
- package/dist/view/ui/SafeHtml.vue.d.ts +8 -0
- package/dist/view/ui/icons.d.ts +51 -0
- package/dist/view/ui/inputRulesEngine.d.ts +33 -0
- package/dist/view/ui/popup.d.ts +75 -0
- package/dist/view/ui/useMenuDismiss.d.ts +9 -0
- package/dist/view/ui/useMenuScroll.d.ts +2 -0
- package/dist/view/urlUtils.d.ts +39 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# xiaodao-editor
|
|
2
|
+
|
|
3
|
+
Notion-style **block editor** for Vue 3 + TypeScript. Ships as a single
|
|
4
|
+
zero-runtime-dependency package: a framework-agnostic core plus a Vue view
|
|
5
|
+
layer. Every block type (paragraph, heading, list, code, …) is contributed
|
|
6
|
+
by an **extension**, so the core never switches on a block type.
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **11 built-in block types** — paragraph, h1–h6 (heading), bullet list,
|
|
13
|
+
ordered list, to-do, quote, code block, **image**, **divider**,
|
|
14
|
+
**table**, **table of contents** (13 extensions total including Keymap
|
|
15
|
+
and History behavior extensions)
|
|
16
|
+
- **Table block** — `attrs`-based N×M grid; default 120 px column widths,
|
|
17
|
+
new tables default to header row; row/column selection strips,
|
|
18
|
+
corner-handle to select the whole table; insert dots between rows/cols;
|
|
19
|
+
floating action bar with merge/split cells, **toggle header row** (sets
|
|
20
|
+
`attrs.headerRow`), and delete row/col/table; each cell uses its own
|
|
21
|
+
`contenteditable` with paragraph/heading/codeBlock cell types, rich
|
|
22
|
+
inline marks, cell background color, and alignment; Tab navigates
|
|
23
|
+
between cells, Enter exits edit (code-block cells: Enter inserts a
|
|
24
|
+
newline), Escape blurs; internal horizontal scrollbar à la Arco Design;
|
|
25
|
+
full-rect merge-cell selection expansion so you can never select half
|
|
26
|
+
of a merged cell.
|
|
27
|
+
- **Inline marks** — bold, italic, underline, strikethrough, inline code,
|
|
28
|
+
**link** (`Mod-K`, URL pasting, auto-link, popover with view/edit/copy/remove,
|
|
29
|
+
href sanitization to block `javascript:` / XSS), per-selection text color
|
|
30
|
+
and background color
|
|
31
|
+
- **Block-level attrs** — alignment (left/center/right/justify), text color,
|
|
32
|
+
background color, indentation (0–10); image additionally carries
|
|
33
|
+
`src`, `alt`, `title`, `width`, `height`, `caption`, `fileId`
|
|
34
|
+
- **Slash menu** — `/` opens a searchable command palette; input rules
|
|
35
|
+
(`# `, `> `, `[] `, ```` ``` ````) convert blocks on the fly; `/image`
|
|
36
|
+
opens the file picker
|
|
37
|
+
- **Block manipulation** — drag handle, hover toolbar, `+` insert button,
|
|
38
|
+
grip menu with duplicate / copy / cut / move up / move down / delete;
|
|
39
|
+
**real nesting** (Tab/Shift-Tab indent/outdent builds a parent–child tree;
|
|
40
|
+
drag-and-drop supports before/after sibling insert plus a **drop-into**
|
|
41
|
+
mode — pause over a block's center to nest under it as its first child);
|
|
42
|
+
duplicate clones the whole subtree; image additionally exposes replace /
|
|
43
|
+
remove / drag-resize corner handle with locked aspect ratio and editable
|
|
44
|
+
caption
|
|
45
|
+
- **Clipboard** — clean copy/cut/paste of HTML and plain text; multi-block
|
|
46
|
+
selection overlay; **HTML `<img>` / image-file paste + drag-and-drop
|
|
47
|
+
automatically create image blocks** and dispatch the upload; selecting text
|
|
48
|
+
and pasting a URL wraps it as a link
|
|
49
|
+
- **History** — undo/redo with typing grouping (`Mod-Z` / `Mod-Shift-Z`);
|
|
50
|
+
undo restores blocks but never resurrects transient upload state
|
|
51
|
+
- **i18n** — `zh-CN` (default) and `en-US` via the `locale` prop; zero-dep
|
|
52
|
+
translation module (no `vue-i18n`)
|
|
53
|
+
- **Theming** — `light` (default) and `dark` via the `theme` prop; CSS
|
|
54
|
+
variables for all design tokens
|
|
55
|
+
- **Accessible** — keyboard navigation throughout, ARIA roles on menus
|
|
56
|
+
- **Table of contents** — a live, non-editable block that renders a
|
|
57
|
+
hierarchical list of every heading in the document; stays in sync as
|
|
58
|
+
headings are added, removed, or edited; click an entry to jump to the
|
|
59
|
+
heading; insert via slash menu `/table of contents`
|
|
60
|
+
|
|
61
|
+
## Quick start
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
npm install xiaodao-editor
|
|
65
|
+
# or: pnpm add xiaodao-editor
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```vue
|
|
69
|
+
<script setup lang="ts">
|
|
70
|
+
import { ref } from 'vue'
|
|
71
|
+
import { BlockEditor } from 'xiaodao-editor'
|
|
72
|
+
import type { DocumentData } from 'xiaodao-editor'
|
|
73
|
+
import 'xiaodao-editor/style.css'
|
|
74
|
+
|
|
75
|
+
const doc = ref<DocumentData>({ blocks: [] })
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<template>
|
|
79
|
+
<BlockEditor v-model="doc" />
|
|
80
|
+
</template>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The editor ships with all 13 built-in extensions by default — no need to pass
|
|
84
|
+
`extensions` unless you want a custom set.
|
|
85
|
+
|
|
86
|
+
## Props
|
|
87
|
+
|
|
88
|
+
| Prop | Type | Default | Description |
|
|
89
|
+
| -------------- | -------------------------------------- | ------------------ | ----------------------------------------------------------------------------- |
|
|
90
|
+
| `modelValue` | `DocumentData` | `{ blocks: [] }` | The document JSON (two-way via `v-model`). |
|
|
91
|
+
| `extensions` | `readonly Extension[]` | `BuiltinExtensions`| Extensions to register. Override to add custom blocks or strip built-ins. |
|
|
92
|
+
| `editable` | `boolean` | `true` | Read-only mode when `false`. |
|
|
93
|
+
| `placeholder` | `string` | locale-aware | Placeholder for the first empty block. Defaults to a localized string. |
|
|
94
|
+
| `theme` | `'light' \| 'dark'` | `'light'` | Color theme. The class is applied to `.block-editor` and synced to `<body>`. |
|
|
95
|
+
| `locale` | `'zh-CN' \| 'en-US'` | `'zh-CN'` | UI language. Any non-empty value other than `'zh-CN'` ⇒ `'en-US'`. |
|
|
96
|
+
| `uploadImage` | `UploadImageHandler` | in-memory mock | Hook for image uploads. Signature: `(name, file, controller, onProgress) => Promise<ImageUploadResult>`. Consumers **must** provide this if they intend to persist documents (the default mock stores `blob:` URLs which are not serialisable). |
|
|
97
|
+
|
|
98
|
+
### Emits
|
|
99
|
+
|
|
100
|
+
| Event | Payload | When |
|
|
101
|
+
| ------------------------ | -------------- | ------------------------------------------------------------------------------------- |
|
|
102
|
+
| `update:modelValue` | `DocumentData` | Document changed (debounced on blur). |
|
|
103
|
+
| `cleanup:image-file` | `number` | `fileId` reference count dropped to 0 (last image block referencing it was removed or its src replaced). Payload is the `fileId`; 0 is never emitted. Consumer may reclaim cloud storage.
|
|
104
|
+
|
|
105
|
+
### Expose
|
|
106
|
+
|
|
107
|
+
| Member | Type | Description |
|
|
108
|
+
| -------- | -------- | ---------------------------------------- |
|
|
109
|
+
| `editor` | `Editor` | The framework-agnostic `Editor` instance.|
|
|
110
|
+
|
|
111
|
+
## Theming
|
|
112
|
+
|
|
113
|
+
All design tokens are CSS variables. Light values live under `:root`; dark
|
|
114
|
+
values are defined on `.block-editor.theme-dark` and `body.theme-dark` (the
|
|
115
|
+
latter so `<Teleport>`-ed popovers inherit them too).
|
|
116
|
+
|
|
117
|
+
```css
|
|
118
|
+
/* Override tokens in your app */
|
|
119
|
+
:root {
|
|
120
|
+
--be-accent: #6366f1;
|
|
121
|
+
--be-radius: 4px;
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The `.block-editor` element intentionally has **no background** — the host
|
|
126
|
+
page controls the editor's background so it blends into the surrounding UI.
|
|
127
|
+
Set it explicitly if needed:
|
|
128
|
+
|
|
129
|
+
```css
|
|
130
|
+
.block-editor {
|
|
131
|
+
background: var(--be-bg); /* or any color you want */
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Built-in extensions
|
|
136
|
+
|
|
137
|
+
`BuiltinExtensions` bundles these **13 extensions** (11 block types + 2 behavior extensions):
|
|
138
|
+
|
|
139
|
+
| Extension | Block type | Notes |
|
|
140
|
+
| --------------------- | --------------- | ---------------------------------------------------------------- |
|
|
141
|
+
| `ParagraphExtension` | `paragraph` | Default block type. |
|
|
142
|
+
| `HeadingExtension` | `heading` | h1–h6 via `attrs.level` (1–6). |
|
|
143
|
+
| `BulletListExtension` | `bulletList` | Unordered list. |
|
|
144
|
+
| `OrderedListExtension`| `orderedList` | Auto-numbered; `attrs.startNumber` for explicit override. |
|
|
145
|
+
| `TodoListExtension` | `todoList` | Checkbox via `attrs.checked`. |
|
|
146
|
+
| `QuoteExtension` | `quote` | Blockquote. No inline italic (disabled by schema). |
|
|
147
|
+
| `CodeBlockExtension` | `codeBlock` | `attrs.language`; isolating — Enter inserts a newline. |
|
|
148
|
+
| `ImageExtension` | `image` | `content: 'none'`; attrs `src/alt/title/width/height/caption/fileId`; serialize → HTML `<figure>`/`<img>` + Markdown ``; replace + drag-resize handle + editable caption; upload side-channel via `uploadImage` prop + `cleanup:image-file`. |
|
|
149
|
+
| `TableExtension` | `table` | `content: 'none'`; attrs `rows/cols/cells/colWidths/headerRow`; cell InlineSeq per cell with cellType/align/bgColor/rowspan/colspan; row/col selection strips + corner handle; floating toolbar with merge/split, **toggle header row**, delete row/col/table; row/col insert dots; full-rect selection expansion for merged cells. Default column width 120 px; new tables default to `headerRow: true`. |
|
|
150
|
+
| `DividerExtension` | `divider` | Isolating horizontal rule. |
|
|
151
|
+
| `TableOfContentsExtension` | `tableOfContents` | `content: 'none'`; empty attrs — the heading list is a **dynamic view** computed from the editor state on every render. Non-editable block (`editable: false`); collects all `heading` blocks in document order (table-cell headings excluded automatically); click an entry to scroll the heading into view. Serialize emits empty string (the real headings are exported by their own blocks). |
|
|
152
|
+
| `KeymapExtension` | — | Enter / Backspace / ArrowUp / ArrowDown bindings. |
|
|
153
|
+
| `HistoryExtension` | — | `Mod-Z` / `Mod-Shift-Z` / `Mod-Y` undo/redo keymap. |
|
|
154
|
+
|
|
155
|
+
To use a **custom subset**, pass `extensions` explicitly:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import {
|
|
159
|
+
ParagraphExtension, HeadingExtension,
|
|
160
|
+
KeymapExtension, HistoryExtension,
|
|
161
|
+
} from 'xiaodao-editor'
|
|
162
|
+
|
|
163
|
+
const extensions = [
|
|
164
|
+
ParagraphExtension, HeadingExtension,
|
|
165
|
+
KeymapExtension, HistoryExtension,
|
|
166
|
+
]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Document model
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
interface Block {
|
|
173
|
+
id: BlockId
|
|
174
|
+
type: BlockType
|
|
175
|
+
attrs: Attrs // e.g. { level: 2, align: 'center', color: 'red' }
|
|
176
|
+
content: InlineSeq // text runs with optional marks
|
|
177
|
+
children: BlockId[] // child block ids — real nesting:
|
|
178
|
+
// paragraph/heading + the 3 list kinds can be parents;
|
|
179
|
+
// any block type can be a child. `attrs.indent` is a
|
|
180
|
+
// derived mirror of the nesting depth.
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
interface DocumentData {
|
|
184
|
+
id?: string
|
|
185
|
+
blocks: BlockData[] // nested JSON; normalized on import
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Example document:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
const doc: DocumentData = {
|
|
193
|
+
blocks: [
|
|
194
|
+
{ type: 'heading', attrs: { level: 1 }, content: [{ type: 'text', text: 'Title' }] },
|
|
195
|
+
{ type: 'paragraph', content: [
|
|
196
|
+
{ type: 'text', text: 'Normal ' },
|
|
197
|
+
{ type: 'text', text: 'bold', marks: [{ type: 'bold' }] },
|
|
198
|
+
{ type: 'text', text: ' and a ' },
|
|
199
|
+
{ type: 'text', text: 'link', marks: [{ type: 'link', attrs: { href: 'https://example.com' } }] },
|
|
200
|
+
{ type: 'text', text: '.' },
|
|
201
|
+
]},
|
|
202
|
+
{ type: 'codeBlock', attrs: { language: 'ts' }, content: [{ type: 'text', text: 'const x = 1' }] },
|
|
203
|
+
{ type: 'image', attrs: {
|
|
204
|
+
src: 'https://cdn.example.com/hero.png', alt: 'Hero',
|
|
205
|
+
width: 1200, height: 630, caption: 'Fig. 1 — Architecture overview', fileId: 42,
|
|
206
|
+
}, content: [] },
|
|
207
|
+
{ type: 'divider' },
|
|
208
|
+
{ type: 'table', attrs: {
|
|
209
|
+
rows: 2, cols: 3,
|
|
210
|
+
headerRow: true,
|
|
211
|
+
colWidths: [120, 120, 120],
|
|
212
|
+
cells: [
|
|
213
|
+
[{ content: [{ type: 'text', text: 'A' }], rowspan: 1, colspan: 1, covered: false },
|
|
214
|
+
{ content: [{ type: 'text', text: 'B' }], rowspan: 1, colspan: 1, covered: false },
|
|
215
|
+
{ content: [{ type: 'text', text: 'C' }], rowspan: 1, colspan: 1, covered: false }],
|
|
216
|
+
[{ content: [{ type: 'text', text: '1' }], rowspan: 1, colspan: 1, covered: false },
|
|
217
|
+
{ content: [{ type: 'text', text: '2' }], rowspan: 1, colspan: 1, covered: false },
|
|
218
|
+
{ content: [{ type: 'text', text: '3' }], rowspan: 1, colspan: 1, covered: false }],
|
|
219
|
+
],
|
|
220
|
+
}, content: [] },
|
|
221
|
+
],
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Custom extensions
|
|
226
|
+
|
|
227
|
+
A block-type extension provides a `name`, a `schema` (block type, content kind,
|
|
228
|
+
and attrs with defaults + validators), and a `renderer` (a Vue component that
|
|
229
|
+
receives `block` and `placeholder` props). Extensions can also contribute
|
|
230
|
+
input rules, slash commands, keymap bindings, and Markdown/HTML
|
|
231
|
+
serialization. A minimal block-type extension provides a schema and a Vue
|
|
232
|
+
renderer:
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
import { defineComponent, h } from 'vue'
|
|
236
|
+
import type { Extension } from 'xiaodao-editor'
|
|
237
|
+
import { BlockContent } from 'xiaodao-editor'
|
|
238
|
+
|
|
239
|
+
const CalloutBlock = defineComponent({
|
|
240
|
+
props: ['block', 'placeholder'],
|
|
241
|
+
setup(props) {
|
|
242
|
+
return () => h(BlockContent, {
|
|
243
|
+
block: props.block,
|
|
244
|
+
placeholder: props.placeholder,
|
|
245
|
+
class: 'block-callout',
|
|
246
|
+
})
|
|
247
|
+
},
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
export const CalloutExtension: Extension = {
|
|
251
|
+
name: 'callout',
|
|
252
|
+
schema: {
|
|
253
|
+
type: 'callout',
|
|
254
|
+
content: 'text',
|
|
255
|
+
attrs: {
|
|
256
|
+
color: { default: 'default' },
|
|
257
|
+
bgColor: { default: 'yellow' },
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
renderer: { component: CalloutBlock },
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Register it alongside the built-ins:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
import { BuiltinExtensions, BlockEditor } from 'xiaodao-editor'
|
|
268
|
+
import { CalloutExtension } from './callout'
|
|
269
|
+
|
|
270
|
+
const extensions = [...BuiltinExtensions, CalloutExtension]
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Architecture
|
|
274
|
+
|
|
275
|
+
- **`src/core/`** — framework-agnostic engine (zero Vue imports, enforced by
|
|
276
|
+
ESLint). Owns the document model, transactions, history, commands, schema,
|
|
277
|
+
and extension registries.
|
|
278
|
+
- **`src/view/`** — Vue bridge: `BlockEditor.vue` (root), `BlockList`,
|
|
279
|
+
`BlockHost`, `BlockContent` (per-block `contenteditable`), and the UI
|
|
280
|
+
components (`BlockHandle`, `BlockSettingsMenu`, `HoverToolbar`, `PlusMenu`,
|
|
281
|
+
`OrderedListMenu`, `NumberPicker`, `CodeLangPicker`).
|
|
282
|
+
- **`src/extensions/`** — the 13 built-in extensions plus `_commonAttrs.ts`
|
|
283
|
+
(shared align/color/bgColor/indent specs and color presets, plus
|
|
284
|
+
`ImageExtension`'s upload-side-channel renderer logic). **Table** lives in
|
|
285
|
+
`Table.ts` (Vue renderer + command registrations) and `tableModel.ts` (pure
|
|
286
|
+
structural helpers: insert/remove row/col, merge/split cells, full-rect
|
|
287
|
+
selection expansion, header row toggle, column width helpers, HTML/Markdown
|
|
288
|
+
serialization, attrs validation/coercion). **Divider** lives in `Divider.ts`.
|
|
289
|
+
**Table of contents** lives in `TableOfContents.ts` (non-editable block that
|
|
290
|
+
renders a live heading list).
|
|
291
|
+
- **`src/i18n.ts`** — locale + theme module; provides `t(key)` via Vue's
|
|
292
|
+
provide/inject so popovers rendered through `<Teleport>` stay reactive.
|
|
293
|
+
|
|
294
|
+
## Development
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
pnpm install
|
|
298
|
+
pnpm dev # playground at http://localhost:5173
|
|
299
|
+
pnpm typecheck # vue-tsc --noEmit
|
|
300
|
+
pnpm build # vue-tsc --noEmit && vite build
|
|
301
|
+
pnpm lint # eslint --fix
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## License
|
|
305
|
+
|
|
306
|
+
MIT
|