@nywqs/vue-markdown-editor 0.1.0 → 0.1.2

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 CHANGED
@@ -1,221 +1,255 @@
1
- # @nywqs/vue-markdown-editor
2
-
3
- 一个基于 Vue 3 Markdown 编辑器组件,内置实时预览、工具栏快捷操作、主题切换、行号显示等特性,开箱即用,适合快速集成到后台管理、文档平台等场景。
4
-
5
- > 本组件通过 `marked` 渲染 Markdown,并内置常用工具栏按钮和键盘快捷键。
6
-
7
- ---
8
-
9
- ## 安装
10
-
11
- ```bash
12
- npm install @nywqs/vue-markdown-editor
13
- # 或
14
- yarn add @nywqs/vue-markdown-editor
15
- # 或
16
- pnpm add @nywqs/vue-markdown-editor
17
- ```
18
-
19
- ---
20
-
21
- ## 快速上手
22
-
23
- ### 全局注册组件
24
-
25
- ```ts
26
- import { createApp } from 'vue'
27
- import App from './App.vue'
28
- import MarkdownEditor from '@nywqs/vue-markdown-editor'
29
- import '@nywqs/vue-markdown-editor/dist-lib/wqs_editor.css'
30
-
31
- const app = createApp(App)
32
-
33
- // 作为全局组件使用
34
- app.component('MarkdownEditor', MarkdownEditor)
35
-
36
- app.mount('#app')
37
- ```
38
-
39
- ### 基本用法
40
-
41
- ```vue
42
- <template>
43
- <div style="height: 600px;">
44
- <MarkdownEditor v-model="content" />
45
- </div>
46
- </template>
47
-
48
- <script setup lang="ts">
49
- import { ref } from 'vue'
50
- import MarkdownEditor from '@nywqs/vue-markdown-editor'
51
- import '@nywqs/vue-markdown-editor/dist-lib/wqs_editor.css'
52
-
53
- const content = ref('# Hello Markdown Editor')
54
- </script>
55
- ```
56
-
57
- ---
58
-
59
- ## 组件属性(Props)
60
-
61
- `MarkdownEditor` 主要支持以下 props:
62
-
63
- | Prop | 类型 | 默认值 | 说明 |
64
- |-------------------|------------------------------|--------------|------|
65
- | `modelValue` | `string` | `''` | 当前 Markdown 文本内容,支持 `v-model` |
66
- | `theme` | `'dark' \| 'light'` | `'dark'` | 初始主题,支持深色 / 浅色 |
67
- | `locale` | `string` | `'zh-CN'` | 语言,用于工具栏文案,目前内置 `zh-CN` / `en-US` |
68
- | `toolbarStyle` | `'text' \| 'icon' \| 'both'` | `'text'` | 工具栏按钮展示样式:文字 / 图标 / 图标+文字 |
69
- | `iconPreset` | `'builtin' \| 'xicons'` | `'xicons'` | 图标来源:内置字符图标或 `@vicons/ionicons5` 图标 |
70
- | `readOnly` | `boolean` | `false` | 只读模式,禁用编辑 |
71
- | `showLineNumbers` | `boolean` | `true` | 是否显示行号 |
72
- | `showToolbar` | `boolean` | `true` | 是否显示顶部工具栏 |
73
- | `showPreview` | `boolean` | `true` | 是否显示右侧预览区域(可拖拽分隔条调节宽度) |
74
- | `showFooter` | `boolean` | `true` | 是否显示底部状态栏 |
75
- | `autofocus` | `boolean` | `false` | 是否自动聚焦到编辑区 |
76
- | `placeholder` | `string` | `''` | 编辑区占位文本 |
77
- | `footerText` | `string` | `''` | 底部左侧自定义文本,不设置时显示默认版权文案 |
78
- | `toolbarItems` | `string[]` | 见下方默认值 | 工具栏按钮配置,可自定义顺序和显示项 |
79
-
80
- ### `toolbarItems` 默认值
81
-
82
- 默认工具栏包含主题切换、标题、加粗、斜体、列表、引用、代码块、分割线、链接、图片:
83
-
84
- ```ts
85
- [
86
- 'theme-toggle',
87
- 'divider',
88
- 'h1',
89
- 'h2',
90
- 'divider',
91
- 'bold',
92
- 'italic',
93
- 'divider',
94
- 'unordered-list',
95
- 'ordered-list',
96
- 'blockquote',
97
- 'code-block',
98
- 'horizontal-rule',
99
- 'divider',
100
- 'link',
101
- 'image',
102
- ]
103
- ```
104
-
105
- ---
106
-
107
- ## 事件(Emits)
108
-
109
- 组件会触发以下事件:
110
-
111
- | 事件名 | 参数 | 说明 |
112
- |---------------------|------------------------------|------|
113
- | `update:modelValue` | `(value: string)` | `v-model` 双向绑定事件 |
114
- | `update:theme` | `(theme: 'dark' \| 'light')` | 通过工具栏切换主题时触发 |
115
- | `change` | `(value: string)` | 内容变更时触发 |
116
- | `save` | `()` | 预留保存事件(如需要可在内部扩展) |
117
- | `focus` | `(event: FocusEvent)` | 编辑区获得焦点 |
118
- | `blur` | `(event: FocusEvent)` | 编辑区失去焦点 |
119
-
120
- ---
121
-
122
- ## 插槽(Slots)
123
-
124
- ### `toolbar` 自定义工具栏
125
-
126
- 你可以完全接管工具栏渲染,组件会提供一组操作方法和工具函数:
127
-
128
- ```vue
129
- <MarkdownEditor v-model="content">
130
- <template #toolbar="scope">
131
- <!-- scope 中包含以下属性/方法:
132
- wrapSelection, toggleHeading, insertUnorderedList, insertOrderedList,
133
- insertBlockquote, insertCodeBlock, insertHorizontalRule,
134
- insertLink, insertImage, toggleTheme, handleToolbarClick,
135
- xiconComponents
136
- -->
137
- <button @click="scope.toggleHeading(1)">H1</button>
138
- <button @click="scope.toggleHeading(2)">H2</button>
139
- <button @click="scope.wrapSelection('**', '**')">Bold</button>
140
- </template>
141
- </MarkdownEditor>
142
- ```
143
-
144
- ### `preview` 自定义预览区域
145
-
146
- 如果你想用自己的预览组件,可以使用 `preview` 插槽。组件会把解析后的 `tokens` 传入:
147
-
148
- ```vue
149
- <MarkdownEditor v-model="content">
150
- <template #preview="{ tokens }">
151
- <!-- 自定义预览渲染 -->
152
- <pre>{{ tokens }}</pre>
153
- </template>
154
- </MarkdownEditor>
155
- ```
156
-
157
- ---
158
-
159
- ## 在表单场景中使用
160
-
161
- 例如配合表单一起提交:
162
-
163
- ```vue
164
- <script setup lang="ts">
165
- import { ref } from 'vue'
166
- import MarkdownEditor from '@nywqs/vue-markdown-editor'
167
- import '@nywqs/vue-markdown-editor/dist-lib/wqs_editor.css'
168
-
169
- const form = ref({
170
- title: '',
171
- content: '',
172
- })
173
-
174
- const handleSubmit = () => {
175
- console.log('提交内容:', form.value)
176
- }
177
- </script>
178
-
179
- <template>
180
- <form @submit.prevent="handleSubmit">
181
- <input v-model="form.title" placeholder="标题" />
182
-
183
- <div style="height: 500px; margin-top: 16px;">
184
- <MarkdownEditor v-model="form.content" />
185
- </div>
186
-
187
- <button type="submit" style="margin-top: 16px;">提交</button>
188
- </form>
189
- </template>
190
- ```
191
-
192
- ---
193
-
194
- ## 开发与构建
195
-
196
- 本仓库使用 Vite + Vue 3 进行开发和构建。
197
-
198
- 本地开发:
199
-
200
- ```bash
201
- npm install
202
- npm run dev
203
- ```
204
-
205
- 构建库:
206
-
207
- ```bash
208
- npm run build:lib
209
- ```
210
-
211
- 类型检查:
212
-
213
- ```bash
214
- npm run typecheck
215
- ```
216
-
217
- ---
218
-
219
- ## License
220
-
221
- MIT
1
+ # @nywqs/vue-markdown-editor
2
+
3
+ 一个基于 Vue 3 Canvas 的高性能 Markdown 编辑器组件,采用混合渲染架构(Canvas + DOM),支持大文件编辑、实时语法高亮、智能预测渲染等特性。
4
+
5
+ ## 核心特性
6
+
7
+ - 🚀 **高性能渲染**:Canvas + DOM 混合渲染,支持大文件流畅编辑
8
+ - 🎨 **实时语法高亮**:支持 Markdown 语法高亮和主题切换
9
+ - 📝 **丰富的编辑功能**:工具栏快捷操作、键盘快捷键、搜索替换
10
+ - 🔌 **插件系统**:支持表格、待办列表、数学公式、流程图等扩展
11
+ - 💡 **智能优化**:视口裁剪、虚拟滚动、增量渲染、离屏缓存
12
+ - 🎯 **实时预览**:支持 Markdown 预览面板,双向滚动同步
13
+
14
+ ---
15
+
16
+ ## 📦 安装
17
+
18
+ ```bash
19
+ npm install @nywqs/vue-markdown-editor
20
+ # 或
21
+ yarn add @nywqs/vue-markdown-editor
22
+ # 或
23
+ pnpm add @nywqs/vue-markdown-editor
24
+ ```
25
+
26
+ ---
27
+
28
+ ## 🚀 快速上手
29
+
30
+ ### 全局注册组件
31
+
32
+ ```ts
33
+ import { createApp } from 'vue'
34
+ import App from './App.vue'
35
+ import { CanvasEditor } from '@nywqs/vue-markdown-editor'
36
+ import '@nywqs/vue-markdown-editor/dist-lib/wqs_editor.css'
37
+
38
+ const app = createApp(App)
39
+
40
+ // 作为全局组件使用
41
+ app.component('CanvasEditor', CanvasEditor)
42
+
43
+ app.mount('#app')
44
+ ```
45
+
46
+ ### 基本用法
47
+
48
+ ```vue
49
+ <template>
50
+ <div style="height: 600px;">
51
+ <CanvasEditor
52
+ v-model="content"
53
+ :theme="theme"
54
+ :show-line-numbers="true"
55
+ :show-toolbar="true"
56
+ />
57
+ </div>
58
+ </template>
59
+
60
+ <script setup lang="ts">
61
+ import { ref } from 'vue'
62
+ import { CanvasEditor } from '@nywqs/vue-markdown-editor'
63
+ import '@nywqs/vue-markdown-editor/dist-lib/wqs_editor.css'
64
+
65
+ const content = ref('# Hello Canvas Editor\n\n这是一个高性能的 Markdown 编辑器')
66
+ const theme = ref('light')
67
+ </script>
68
+ ```
69
+
70
+ ---
71
+
72
+ ## 📖 组件属性(Props)
73
+
74
+ `CanvasEditor` 主要支持以下 props:
75
+
76
+ | Prop | 类型 | 默认值 | 说明 |
77
+ |-------------------------|---------------------|-----------|------|
78
+ | `modelValue` | `string` | `''` | 当前 Markdown 文本内容,支持 `v-model` |
79
+ | `theme` | `'light' \| 'dark'` | `'light'` | 编辑器主题 |
80
+ | `enableSyntaxHighlight` | `boolean` | `true` | 是否启用语法高亮 |
81
+ | `fontSize` | `number` | `15` | 字体大小(像素) |
82
+ | `lineHeight` | `number` | `26` | 行高(像素) |
83
+ | `showLineNumbers` | `boolean` | `true` | 是否显示行号 |
84
+ | `showToolbar` | `boolean` | `true` | 是否显示工具栏 |
85
+ | `scrollPercentage` | `number` | `0` | 外部控制的滚动位置(百分比,0-1) |
86
+ | `isSyncing` | `boolean` | `false` | 是否正在同步滚动(避免循环) |
87
+
88
+ ### `toolbarItems` 默认值
89
+
90
+ 默认工具栏包含主题切换、标题、加粗、斜体、列表、引用、代码块、分割线、链接、图片:
91
+
92
+ ```ts
93
+ [
94
+ 'theme-toggle',
95
+ 'divider',
96
+ 'h1',
97
+ 'h2',
98
+ 'divider',
99
+ 'bold',
100
+ 'italic',
101
+ 'divider',
102
+ 'unordered-list',
103
+ 'ordered-list',
104
+ 'blockquote',
105
+ 'code-block',
106
+ 'horizontal-rule',
107
+ 'divider',
108
+ 'link',
109
+ 'image',
110
+ ]
111
+ ```
112
+
113
+ ---
114
+
115
+ ## 事件(Emits)
116
+
117
+ 组件会触发以下事件:
118
+
119
+ | 事件名 | 参数 | 说明 |
120
+ |---------------------------|--------------------|------|
121
+ | `update:modelValue` | `(value: string)` | `v-model` 双向绑定事件 |
122
+ | `update:scrollPercentage` | `(value: number)` | 滚动位置变化时触发 |
123
+ | `scroll` | `(value: number)` | 滚动事件 |
124
+
125
+ ---
126
+
127
+ ## 🎨 主题和样式
128
+
129
+ 编辑器支持 `light` 和 `dark` 两种内置主题,可以通过 `theme` 属性切换:
130
+
131
+ ```vue
132
+ <CanvasEditor v-model="content" theme="dark" />
133
+ ```
134
+
135
+ 你也可以通过 CSS 变量自定义样式(见源码 `style.css`)。
136
+
137
+ ---
138
+
139
+ ## 🔧 配合预览面板使用
140
+
141
+ 如果需要实时预览,可以配合 `MarkdownPreviewPanel` 组件使用:
142
+
143
+ ```vue
144
+ <script setup lang="ts">
145
+ import { ref } from 'vue'
146
+ import { CanvasEditor, MarkdownPreviewPanel } from '@nywqs/vue-markdown-editor'
147
+ import '@nywqs/vue-markdown-editor/dist-lib/wqs_editor.css'
148
+
149
+ const content = ref('# Hello\n\n这是预览内容')
150
+ const theme = ref('light')
151
+ const editorScrollPercentage = ref(0)
152
+ const previewScrollPercentage = ref(0)
153
+ const isSyncingEditor = ref(false)
154
+ const isSyncingPreview = ref(false)
155
+
156
+ const handleEditorScroll = (percentage) => {
157
+ if (isSyncingPreview.value) return
158
+ isSyncingEditor.value = true
159
+ previewScrollPercentage.value = percentage
160
+ setTimeout(() => { isSyncingEditor.value = false }, 100)
161
+ }
162
+
163
+ const handlePreviewScroll = (percentage) => {
164
+ if (isSyncingEditor.value) return
165
+ isSyncingPreview.value = true
166
+ editorScrollPercentage.value = percentage
167
+ setTimeout(() => { isSyncingPreview.value = false }, 100)
168
+ }
169
+ </script>
170
+
171
+ <template>
172
+ <div style="display: flex; height: 600px;">
173
+ <div style="flex: 1;">
174
+ <CanvasEditor
175
+ v-model="content"
176
+ :theme="theme"
177
+ :scroll-percentage="editorScrollPercentage"
178
+ :is-syncing="isSyncingEditor"
179
+ @scroll="handleEditorScroll"
180
+ />
181
+ </div>
182
+ <div style="flex: 1;">
183
+ <MarkdownPreviewPanel
184
+ :content="content"
185
+ :theme="theme"
186
+ :scroll-percentage="previewScrollPercentage"
187
+ :is-syncing="isSyncingPreview"
188
+ @scroll="handlePreviewScroll"
189
+ />
190
+ </div>
191
+ </div>
192
+ </template>
193
+ ```
194
+
195
+ > **注意**:`MarkdownPreviewPanel` 同时支持 `content` 和 `modelValue` 属性,推荐使用 `content`。
196
+
197
+ ---
198
+
199
+ ## 🔌 插件系统
200
+
201
+ 编辑器内置了丰富的插件:
202
+
203
+ - **TablePlugin**:表格插入和编辑(Ctrl+Shift+T)
204
+ - **TodoListPlugin**:待办列表支持
205
+ - **MathPlugin**:数学公式支持(Ctrl+M)
206
+ - **MermaidPlugin**:流程图支持(Ctrl+Shift+D)
207
+ - **AutoCompletePlugin**:自动补全
208
+ - **SyntaxCheckerPlugin**:语法检查
209
+
210
+ 插件会自动激活,无需额外配置。
211
+
212
+ ---
213
+
214
+ ## ⚙️ 性能优化
215
+
216
+ 编辑器采用了多项性能优化技术:
217
+
218
+ 1. **视口裁剪**:只渲染可见区域,大文件性能提升 10-40 倍
219
+ 2. **虚拟滚动**:缓冲区机制,避免滚动闪烁
220
+ 3. **增量渲染**:只重绘变更的行,编辑时性能提升 3-5 倍
221
+ 4. **离屏缓存**:静态内容(行号、背景)缓存到离屏 Canvas
222
+ 5. **智能调度**:防抖优化 + 立即渲染混合策略
223
+
224
+ 这些优化让编辑器可以流畅处理大型文档(10000+ 行)。
225
+
226
+ ---
227
+
228
+ ## 🛠️ 开发与构建
229
+
230
+ 本仓库使用 Vite + Vue 3 进行开发和构建。
231
+
232
+ 本地开发:
233
+
234
+ ```bash
235
+ npm install
236
+ npm run dev
237
+ ```
238
+
239
+ 构建库:
240
+
241
+ ```bash
242
+ npm run build:lib
243
+ ```
244
+
245
+ 类型检查:
246
+
247
+ ```bash
248
+ npm run typecheck
249
+ ```
250
+
251
+ ---
252
+
253
+ ## License
254
+
255
+ MIT
@@ -0,0 +1 @@
1
+ /* empty css */
@@ -1,2 +1,2 @@
1
- pre code.hljs{padding:1em;display:block;overflow-x:auto}code.hljs{padding:3px 5px}.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#79c0ff}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-comment,.hljs-code,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}
1
+ .canvas-toolbar[data-v-46e06aed]{background:#f5f5f5;border-bottom:1px solid #ddd;flex-wrap:wrap;align-items:center;gap:4px;padding:8px 12px;display:flex}.toolbar-btn[data-v-46e06aed]{cursor:pointer;color:#666;white-space:nowrap;background:#fff;border:1px solid #ddd;border-radius:4px;justify-content:center;align-items:center;min-width:32px;height:32px;padding:0 8px;font-size:16px;transition:all .2s;display:flex}.toolbar-btn .icon[data-v-46e06aed]{justify-content:center;align-items:center;width:18px;height:18px;display:flex}.toolbar-btn[data-v-46e06aed]:hover{color:#333;background:#e9e9e9;border-color:#999;transform:translateY(-1px);box-shadow:0 2px 4px #0000001a}.toolbar-btn[data-v-46e06aed]:active{box-shadow:none;transform:translateY(0)}.toolbar-btn.divider[data-v-46e06aed]{cursor:default;background:#ddd;border:none;width:1px;min-width:1px;height:20px;margin:0 4px;padding:0}.toolbar-btn.divider[data-v-46e06aed]:hover{box-shadow:none;background:#ddd;transform:none}.search-panel[data-v-46382544]{z-index:1000;background:#fff;border:1px solid #e0e0e0;border-radius:8px;min-width:420px;padding:16px;position:absolute;top:8px;right:8px;box-shadow:0 4px 16px #0000001f}.search-row[data-v-46382544],.replace-row[data-v-46382544]{align-items:center;gap:8px;margin-bottom:10px;display:flex}.search-input[data-v-46382544]{border:1px solid #d0d0d0;border-radius:6px;outline:none;flex:1;padding:8px 12px;font-size:13px;transition:all .2s}.search-input[data-v-46382544]:focus{border-color:#4a90e2;box-shadow:0 0 0 3px #4a90e21a}.search-count[data-v-46382544]{color:#666;text-align:center;background:#f5f5f5;border-radius:4px;min-width:45px;padding:4px 8px;font-size:12px;font-weight:500}.search-btn[data-v-46382544],.action-btn[data-v-46382544]{cursor:pointer;color:#555;background:#fff;border:1px solid #d0d0d0;border-radius:6px;padding:8px 12px;font-size:13px;transition:all .2s}.search-btn[data-v-46382544]:hover,.action-btn[data-v-46382544]:hover{color:#4a90e2;background:#f8f8f8;border-color:#4a90e2}.search-btn[data-v-46382544]:active,.action-btn[data-v-46382544]:active{background:#e8e8e8}.close-btn[data-v-46382544]{color:#999;font-weight:700}.close-btn[data-v-46382544]:hover{color:#f44;background:#fff5f5;border-color:#f44}.action-btn[data-v-46382544]{color:#fff;background:#4a90e2;border-color:#4a90e2;padding:8px 16px;font-weight:500}.action-btn[data-v-46382544]:hover{color:#fff;background:#357abd;border-color:#357abd}.search-options[data-v-46382544]{border-top:1px solid #f0f0f0;gap:20px;padding-top:12px;display:flex}.search-options label[data-v-46382544]{color:#666;cursor:pointer;-webkit-user-select:none;user-select:none;align-items:center;gap:6px;font-size:12px;transition:color .2s;display:flex}.search-options label[data-v-46382544]:hover{color:#4a90e2}.search-options input[type=checkbox][data-v-46382544]{cursor:pointer;width:14px;height:14px}.canvas-editor-container[data-v-ddb360e3]{flex-direction:column;width:100%;height:100%;display:flex}.canvas-editor[data-v-ddb360e3]{flex:1;width:100%;position:relative;overflow:hidden}canvas[data-v-ddb360e3]{cursor:text;z-index:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-smooth:always;text-rendering:optimizeLegibility;display:block;position:absolute;top:0;left:0}.preview-panel[data-v-cef068b8]{background:#fff;width:100%;height:100%;transition:background-color .3s;overflow:hidden auto}.preview-panel[data-v-cef068b8]::-webkit-scrollbar{width:8px}.preview-panel[data-v-cef068b8]::-webkit-scrollbar-track{background:#f1f1f1}.preview-panel[data-v-cef068b8]::-webkit-scrollbar-thumb{background:#888;border-radius:4px}.preview-panel[data-v-cef068b8]::-webkit-scrollbar-thumb:hover{background:#555}.preview-content[data-v-cef068b8]{color:#333;max-width:900px;margin:0 auto;padding:20px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:16px;line-height:1.6}.preview-panel[data-theme=dark][data-v-cef068b8]{background:#1e1e1e}.preview-panel[data-theme=dark] .preview-content[data-v-cef068b8]{color:#ccc}.preview-content[data-v-cef068b8] h1,.preview-content[data-v-cef068b8] h2,.preview-content[data-v-cef068b8] h3,.preview-content[data-v-cef068b8] h4,.preview-content[data-v-cef068b8] h5,.preview-content[data-v-cef068b8] h6{margin-top:24px;margin-bottom:16px;font-weight:600;line-height:1.25}.preview-content[data-v-cef068b8] h1{border-bottom:1px solid #eaecef;padding-bottom:.3em;font-size:2em}.preview-content[data-v-cef068b8] h2{border-bottom:1px solid #eaecef;padding-bottom:.3em;font-size:1.5em}.preview-content[data-v-cef068b8] h3{font-size:1.25em}.preview-content[data-v-cef068b8] h4{font-size:1em}.preview-content[data-v-cef068b8] h5{font-size:.875em}.preview-content[data-v-cef068b8] h6{color:#6a737d;font-size:.85em}.preview-content[data-v-cef068b8] p{margin-top:0;margin-bottom:16px}.preview-content[data-v-cef068b8] a{color:#0366d6;text-decoration:none}.preview-content[data-v-cef068b8] a:hover{text-decoration:underline}.preview-content[data-v-cef068b8] strong{font-weight:600}.preview-content[data-v-cef068b8] em{font-style:italic}.preview-content[data-v-cef068b8] code{background-color:#1b1f230d;border-radius:3px;margin:0;padding:.2em .4em;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:85%}.preview-content[data-v-cef068b8] pre{background-color:#f6f8fa;border-radius:6px;margin-bottom:16px;padding:16px;font-size:85%;line-height:1.45;overflow:auto}.preview-content[data-v-cef068b8] pre code{line-height:inherit;word-wrap:normal;background-color:#0000;border:0;margin:0;padding:0;display:inline;overflow:visible}.preview-content[data-v-cef068b8] blockquote{color:#6a737d;border-left:.25em solid #dfe2e5;margin:0 0 16px;padding:0 1em}.preview-content[data-v-cef068b8] ul,.preview-content[data-v-cef068b8] ol{margin-top:0;margin-bottom:16px;padding-left:2em}.preview-content[data-v-cef068b8] li{margin-bottom:.25em}.preview-content[data-v-cef068b8] li>p{margin-bottom:0}.preview-content[data-v-cef068b8] img{box-sizing:content-box;background-color:#fff;max-width:100%}.preview-content[data-v-cef068b8] hr{background-color:#e1e4e8;border:0;height:.25em;margin:24px 0;padding:0}.preview-content[data-v-cef068b8] table{border-spacing:0;border-collapse:collapse;width:100%;margin-bottom:16px;display:block;overflow:auto}.preview-content[data-v-cef068b8] table th{background-color:#f6f8fa;border:1px solid #dfe2e5;padding:6px 13px;font-weight:600}.preview-content[data-v-cef068b8] table td{border:1px solid #dfe2e5;padding:6px 13px}.preview-content[data-v-cef068b8] table tr{background-color:#fff;border-top:1px solid #c6cbd1}.preview-content[data-v-cef068b8] table tr:nth-child(2n){background-color:#f6f8fa}.preview-content[data-v-cef068b8] input[type=checkbox]{margin-right:.5em}.preview-panel[data-theme=dark] .preview-content[data-v-cef068b8] code{background-color:#6e768166}.preview-panel[data-theme=dark] .preview-content[data-v-cef068b8] pre{background-color:#2d2d2d}.preview-panel[data-theme=dark] .preview-content[data-v-cef068b8] table th{background-color:#2d2d2d;border-color:#444}.preview-panel[data-theme=dark] .preview-content[data-v-cef068b8] table td{border-color:#444}.preview-panel[data-theme=dark] .preview-content[data-v-cef068b8] table tr{background-color:#1e1e1e;border-color:#444}.preview-panel[data-theme=dark] .preview-content[data-v-cef068b8] table tr:nth-child(2n){background-color:#252525}.preview-panel[data-theme=dark] .preview-content[data-v-cef068b8] blockquote{color:#8b949e;border-left-color:#3b3b3b}.preview-panel[data-theme=dark] .preview-content[data-v-cef068b8] hr{background-color:#3b3b3b}.preview-panel[data-theme=dark] .preview-content[data-v-cef068b8] h1,.preview-panel[data-theme=dark] .preview-content[data-v-cef068b8] h2{border-bottom-color:#3b3b3b}pre code.hljs{padding:1em;display:block;overflow-x:auto}code.hljs{padding:3px 5px}.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#79c0ff}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-comment,.hljs-code,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}.editor-config[data-v-2ff40722]{z-index:1000;background:#fff;border-radius:8px;width:600px;max-height:80vh;position:fixed;top:50%;left:50%;overflow:hidden;transform:translate(-50%,-50%);box-shadow:0 4px 24px #0003}.config-header[data-v-2ff40722]{background:#f9f9f9;border-bottom:1px solid #e5e5e5;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.config-header h3[data-v-2ff40722]{margin:0;font-size:18px;font-weight:600}.close-btn[data-v-2ff40722]{cursor:pointer;color:#666;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;width:32px;height:32px;font-size:24px;transition:all .2s;display:flex}.close-btn[data-v-2ff40722]:hover{color:#333;background:#e5e5e5}.config-body[data-v-2ff40722]{max-height:calc(80vh - 140px);padding:20px;overflow-y:auto}.config-section[data-v-2ff40722]{margin-bottom:24px}.config-section h4[data-v-2ff40722]{color:#333;margin:0 0 12px;font-size:16px;font-weight:600}.hint[data-v-2ff40722]{color:#999;margin:0 0 12px;font-size:13px}.config-item[data-v-2ff40722]{align-items:center;gap:12px;margin-bottom:12px;display:flex}.config-item label[data-v-2ff40722]{color:#666;min-width:80px;font-size:14px}.config-item select[data-v-2ff40722],.config-item input[type=number][data-v-2ff40722]{border:1px solid #ddd;border-radius:4px;flex:1;padding:6px 10px;font-size:14px}.config-item input[type=checkbox][data-v-2ff40722]{cursor:pointer;margin:0}.unit[data-v-2ff40722]{color:#999;font-size:12px}.color-grid[data-v-2ff40722]{grid-template-columns:repeat(2,1fr);gap:12px;display:grid}.color-item[data-v-2ff40722]{background:#fafafa;border:1px solid #e5e5e5;border-radius:4px;align-items:center;gap:8px;padding:8px;display:flex}.color-item label[data-v-2ff40722]{color:#666;flex:1;font-size:13px}.color-item input[type=color][data-v-2ff40722]{cursor:pointer;border:none;border-radius:4px;width:36px;height:36px}.color-value[data-v-2ff40722]{color:#999;font-family:monospace;font-size:12px}.config-footer[data-v-2ff40722]{background:#f9f9f9;border-top:1px solid #e5e5e5;justify-content:flex-end;gap:8px;padding:16px 20px;display:flex}.btn[data-v-2ff40722]{cursor:pointer;border:1px solid #ddd;border-radius:4px;padding:8px 16px;font-size:14px;transition:all .2s}.btn-secondary[data-v-2ff40722]{color:#666;background:#fff}.btn-secondary[data-v-2ff40722]:hover{background:#f5f5f5}.btn-primary[data-v-2ff40722]{color:#fff;background:#4a90e2;border-color:#4a90e2}.btn-primary[data-v-2ff40722]:hover{background:#357abd}
2
2
  /*$vite$:1*/