Compare commits
2 Commits
6a66c7867a
...
c5a5a4e55b
| Author | SHA1 | Date | |
|---|---|---|---|
| c5a5a4e55b | |||
| 87e594be52 |
@@ -172,9 +172,38 @@ MarkdownEditor.vue
|
|||||||
- **parseBlocks** 每次 modelValue 变化时调用,包含 N 次 `processSync`(N = 块数)
|
- **parseBlocks** 每次 modelValue 变化时调用,包含 N 次 `processSync`(N = 块数)
|
||||||
- 典型文档(50 块)约需 50-100ms,在可接受范围内
|
- 典型文档(50 块)约需 50-100ms,在可接受范围内
|
||||||
- 后续可优化为增量更新(只重新渲染变化的块)
|
- 后续可优化为增量更新(只重新渲染变化的块)
|
||||||
- **textarea auto-resize** 通过 `scrollHeight` 动态调整高度,无额外 DOM 操作
|
- **CodeMirror 实例数** 完整编辑模式保持一个实例;混合模式只为当前活动块创建实例
|
||||||
- **双栏滚动同步** 使用比例计算,避免频繁的绝对位置计算
|
- **双栏滚动同步** 使用比例计算,避免频繁的绝对位置计算
|
||||||
|
|
||||||
|
## 代码编辑体验
|
||||||
|
|
||||||
|
Markdown 源码编辑由 CodeMirror 6 提供,完整编辑模式与混合模式的活动块复用
|
||||||
|
`MarkdownCodeEditor.vue`。组件继续通过 `update:modelValue` 传递纯 Markdown 字符串,
|
||||||
|
不会改变外层的保存和文件切换数据流。
|
||||||
|
|
||||||
|
### 编辑能力
|
||||||
|
|
||||||
|
- Markdown 语法着色,以及围栏代码块内部的语言着色
|
||||||
|
- 括号、引号自动闭合,列表续行,Tab/Shift+Tab 缩进
|
||||||
|
- 输入三个反引号或波浪号后自动打开语言提示
|
||||||
|
- 语言提示支持继续输入筛选、方向键选择、Enter 确认和 Ctrl+Space 手动触发
|
||||||
|
- 围栏语言确认后按 Enter 自动补齐结束围栏,并将光标放在代码区域
|
||||||
|
- 最近使用的五种语言通过 localStorage 排在提示列表前面
|
||||||
|
|
||||||
|
语言名称、别名和 Shiki 映射集中定义在 `src/lib/markdownLanguages.ts`。
|
||||||
|
无法识别的围栏语言按纯文本处理,不阻断 Markdown 渲染或编辑。
|
||||||
|
|
||||||
|
### 预览高亮
|
||||||
|
|
||||||
|
预览仍先经过 unified 和 rehype-sanitize。安全过滤完成后,可信的 Shiki 高亮器
|
||||||
|
替换 `<pre><code>` 节点,因此不会放宽 Markdown 原始 HTML 的安全策略。
|
||||||
|
|
||||||
|
Shiki 核心和语言语法均按需加载:只有文档包含代码块时才初始化高亮器,只有某种
|
||||||
|
语言第一次出现在预览中时才加载对应语法。高亮器和已加载语言会在应用生命周期内
|
||||||
|
复用。Shiki 与 CodeMirror 共用 `--code-*` 语义色变量,每套应用主题同时定义界面色
|
||||||
|
和代码色;CodeMirror 围栏内容还复用 Shiki 的 token 划分,确保混合模式点击前后逐词
|
||||||
|
颜色一致。切换主题时预览与活动编辑器会即时使用新颜色,无需重新解析文档。
|
||||||
|
|
||||||
## 样式体系
|
## 样式体系
|
||||||
|
|
||||||
| 层级 | 设计 |
|
| 层级 | 设计 |
|
||||||
@@ -284,8 +313,8 @@ MarkdownEditor.vue
|
|||||||
|
|
||||||
paste 事件同时绑定在两种模式的 textarea 上:
|
paste 事件同时绑定在两种模式的 textarea 上:
|
||||||
|
|
||||||
- **edit 模式**:`@paste="onEditorPaste"` — 操作 `props.modelValue` + `editorTextareaRef`
|
- **edit 模式**:`@paste="onEditorPaste"` — 按 CodeMirror 当前选区替换 `modelValue`
|
||||||
- **mixed 模式**:`@paste="onBlockPaste"` — 操作 `editingSource` + `blockTextareaRef`(包含空文档和 block 编辑两种场景)
|
- **mixed 模式**:`@paste="onBlockPaste"` — 按活动块 CodeMirror 选区替换 `editingSource`
|
||||||
|
|
||||||
粘贴后的 markdown 语法统一为:
|
粘贴后的 markdown 语法统一为:
|
||||||
```markdown
|
```markdown
|
||||||
|
|||||||
8734
package-lock.json
generated
Normal file
8734
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -10,6 +10,15 @@
|
|||||||
"tauri": "tauri"
|
"tauri": "tauri"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.20.3",
|
||||||
|
"@codemirror/commands": "^6.10.4",
|
||||||
|
"@codemirror/lang-markdown": "6.4.0",
|
||||||
|
"@codemirror/language": "^6.12.4",
|
||||||
|
"@codemirror/language-data": "^6.5.2",
|
||||||
|
"@codemirror/search": "^6.7.1",
|
||||||
|
"@codemirror/state": "^6.7.1",
|
||||||
|
"@codemirror/view": "^6.43.6",
|
||||||
|
"@lezer/highlight": "^1.2.3",
|
||||||
"@lucide/vue": "^1.23.0",
|
"@lucide/vue": "^1.23.0",
|
||||||
"@tauri-apps/api": "^2",
|
"@tauri-apps/api": "^2",
|
||||||
"@tauri-apps/plugin-dialog": "^2.7.1",
|
"@tauri-apps/plugin-dialog": "^2.7.1",
|
||||||
@@ -25,6 +34,7 @@
|
|||||||
"remark-rehype": "^11.1.2",
|
"remark-rehype": "^11.1.2",
|
||||||
"remixicon": "^4.9.1",
|
"remixicon": "^4.9.1",
|
||||||
"shadcn-vue": "^2.7.4",
|
"shadcn-vue": "^2.7.4",
|
||||||
|
"shiki": "^4.3.1",
|
||||||
"tailwind-merge": "^3.6.0",
|
"tailwind-merge": "^3.6.0",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
"unified": "^11.0.0",
|
"unified": "^11.0.0",
|
||||||
|
|||||||
666
pnpm-lock.yaml
generated
666
pnpm-lock.yaml
generated
@@ -4,10 +4,40 @@ settings:
|
|||||||
autoInstallPeers: true
|
autoInstallPeers: true
|
||||||
excludeLinksFromLockfile: false
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
overrides:
|
||||||
|
'@codemirror/lang-markdown': 6.4.0
|
||||||
|
|
||||||
importers:
|
importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@codemirror/autocomplete':
|
||||||
|
specifier: ^6.20.3
|
||||||
|
version: 6.20.3
|
||||||
|
'@codemirror/commands':
|
||||||
|
specifier: ^6.10.4
|
||||||
|
version: 6.10.4
|
||||||
|
'@codemirror/lang-markdown':
|
||||||
|
specifier: 6.4.0
|
||||||
|
version: 6.4.0
|
||||||
|
'@codemirror/language':
|
||||||
|
specifier: ^6.12.4
|
||||||
|
version: 6.12.4
|
||||||
|
'@codemirror/language-data':
|
||||||
|
specifier: ^6.5.2
|
||||||
|
version: 6.5.2
|
||||||
|
'@codemirror/search':
|
||||||
|
specifier: ^6.7.1
|
||||||
|
version: 6.7.1
|
||||||
|
'@codemirror/state':
|
||||||
|
specifier: ^6.7.1
|
||||||
|
version: 6.7.1
|
||||||
|
'@codemirror/view':
|
||||||
|
specifier: ^6.43.6
|
||||||
|
version: 6.43.6
|
||||||
|
'@lezer/highlight':
|
||||||
|
specifier: ^1.2.3
|
||||||
|
version: 1.2.3
|
||||||
'@lucide/vue':
|
'@lucide/vue':
|
||||||
specifier: ^1.23.0
|
specifier: ^1.23.0
|
||||||
version: 1.23.0(vue@3.5.39(typescript@5.6.3))
|
version: 1.23.0(vue@3.5.39(typescript@5.6.3))
|
||||||
@@ -53,6 +83,9 @@ importers:
|
|||||||
shadcn-vue:
|
shadcn-vue:
|
||||||
specifier: ^2.7.4
|
specifier: ^2.7.4
|
||||||
version: 2.7.4(eslint@10.6.0(jiti@2.7.0))(vue@3.5.39(typescript@5.6.3))
|
version: 2.7.4(eslint@10.6.0(jiti@2.7.0))(vue@3.5.39(typescript@5.6.3))
|
||||||
|
shiki:
|
||||||
|
specifier: ^4.3.1
|
||||||
|
version: 4.3.1
|
||||||
tailwind-merge:
|
tailwind-merge:
|
||||||
specifier: ^3.6.0
|
specifier: ^3.6.0
|
||||||
version: 3.6.0
|
version: 3.6.0
|
||||||
@@ -224,6 +257,96 @@ packages:
|
|||||||
resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
|
resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@codemirror/autocomplete@6.20.3':
|
||||||
|
resolution: {integrity: sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==}
|
||||||
|
|
||||||
|
'@codemirror/commands@6.10.4':
|
||||||
|
resolution: {integrity: sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg==}
|
||||||
|
|
||||||
|
'@codemirror/lang-angular@0.1.4':
|
||||||
|
resolution: {integrity: sha512-oap+gsltb/fzdlTQWD6BFF4bSLKcDnlxDsLdePiJpCVNKWXSTAbiiQeYI3UmES+BLAdkmIC1WjyztC1pi/bX4g==}
|
||||||
|
|
||||||
|
'@codemirror/lang-cpp@6.0.3':
|
||||||
|
resolution: {integrity: sha512-URM26M3vunFFn9/sm6rzqrBzDgfWuDixp85uTY49wKudToc2jTHUrKIGGKs+QWND+YLofNNZpxcNGRynFJfvgA==}
|
||||||
|
|
||||||
|
'@codemirror/lang-css@6.3.1':
|
||||||
|
resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==}
|
||||||
|
|
||||||
|
'@codemirror/lang-go@6.0.1':
|
||||||
|
resolution: {integrity: sha512-7fNvbyNylvqCphW9HD6WFnRpcDjr+KXX/FgqXy5H5ZS0eC5edDljukm/yNgYkwTsgp2busdod50AOTIy6Jikfg==}
|
||||||
|
|
||||||
|
'@codemirror/lang-html@6.4.11':
|
||||||
|
resolution: {integrity: sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==}
|
||||||
|
|
||||||
|
'@codemirror/lang-java@6.0.2':
|
||||||
|
resolution: {integrity: sha512-m5Nt1mQ/cznJY7tMfQTJchmrjdjQ71IDs+55d1GAa8DGaB8JXWsVCkVT284C3RTASaY43YknrK2X3hPO/J3MOQ==}
|
||||||
|
|
||||||
|
'@codemirror/lang-javascript@6.2.5':
|
||||||
|
resolution: {integrity: sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==}
|
||||||
|
|
||||||
|
'@codemirror/lang-jinja@6.0.1':
|
||||||
|
resolution: {integrity: sha512-P5kyHLObzjtbGj16h+hyvZTxJhSjBEeSx4wMjbnAf3b0uwTy2+F0zGjMZL4PQOm/mh2eGZ5xUDVZXgwP783Nsw==}
|
||||||
|
|
||||||
|
'@codemirror/lang-json@6.0.2':
|
||||||
|
resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==}
|
||||||
|
|
||||||
|
'@codemirror/lang-less@6.0.2':
|
||||||
|
resolution: {integrity: sha512-EYdQTG22V+KUUk8Qq582g7FMnCZeEHsyuOJisHRft/mQ+ZSZ2w51NupvDUHiqtsOy7It5cHLPGfHQLpMh9bqpQ==}
|
||||||
|
|
||||||
|
'@codemirror/lang-liquid@6.3.2':
|
||||||
|
resolution: {integrity: sha512-6PDVU3ZnfeYyz1at1E/ttorErZvZFXXt1OPhtfe1EZJ2V2iDFa0CwPqPgG5F7NXN0yONGoBogKmFAafKTqlwIw==}
|
||||||
|
|
||||||
|
'@codemirror/lang-markdown@6.4.0':
|
||||||
|
resolution: {integrity: sha512-ZeArR54seh4laFbUTVy0ZmQgO+C/cxxlW4jEoQMhL3HALScBpZBeZcLzrQmJsTEx4is9GzOe0bFAke2B1KZqeA==}
|
||||||
|
|
||||||
|
'@codemirror/lang-php@6.0.2':
|
||||||
|
resolution: {integrity: sha512-ZKy2v1n8Fc8oEXj0Th0PUMXzQJ0AIR6TaZU+PbDHExFwdu+guzOA4jmCHS1Nz4vbFezwD7LyBdDnddSJeScMCA==}
|
||||||
|
|
||||||
|
'@codemirror/lang-python@6.2.1':
|
||||||
|
resolution: {integrity: sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==}
|
||||||
|
|
||||||
|
'@codemirror/lang-rust@6.0.2':
|
||||||
|
resolution: {integrity: sha512-EZaGjCUegtiU7kSMvOfEZpaCReowEf3yNidYu7+vfuGTm9ow4mthAparY5hisJqOHmJowVH3Upu+eJlUji6qqA==}
|
||||||
|
|
||||||
|
'@codemirror/lang-sass@6.0.2':
|
||||||
|
resolution: {integrity: sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q==}
|
||||||
|
|
||||||
|
'@codemirror/lang-sql@6.10.0':
|
||||||
|
resolution: {integrity: sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==}
|
||||||
|
|
||||||
|
'@codemirror/lang-vue@0.1.3':
|
||||||
|
resolution: {integrity: sha512-QSKdtYTDRhEHCfo5zOShzxCmqKJvgGrZwDQSdbvCRJ5pRLWBS7pD/8e/tH44aVQT6FKm0t6RVNoSUWHOI5vNug==}
|
||||||
|
|
||||||
|
'@codemirror/lang-wast@6.0.2':
|
||||||
|
resolution: {integrity: sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q==}
|
||||||
|
|
||||||
|
'@codemirror/lang-xml@6.1.0':
|
||||||
|
resolution: {integrity: sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==}
|
||||||
|
|
||||||
|
'@codemirror/lang-yaml@6.1.3':
|
||||||
|
resolution: {integrity: sha512-AZ8DJBuXGVHybpBQhmZtgew5//4hv3tdkXnr3vDmOUMJRuB6vn/uuwtmTOTlqEaQFg3hQSVeA90NmvIQyUV6FQ==}
|
||||||
|
|
||||||
|
'@codemirror/language-data@6.5.2':
|
||||||
|
resolution: {integrity: sha512-CPkWBKrNS8stYbEU5kwBwTf3JB1kghlbh4FSAwzGW2TEscdeHHH4FGysREW86Mqnj3Qn09s0/6Ea/TutmoTobg==}
|
||||||
|
|
||||||
|
'@codemirror/language@6.12.4':
|
||||||
|
resolution: {integrity: sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==}
|
||||||
|
|
||||||
|
'@codemirror/legacy-modes@6.5.3':
|
||||||
|
resolution: {integrity: sha512-xCsmIzH78MyWkib9jlPaaun57XNkfbMIhagfaZVd0iLTqlpw3jXaIcbZm72MTmmn64eTZpBVNjbyYh+QXnxRsg==}
|
||||||
|
|
||||||
|
'@codemirror/lint@6.9.7':
|
||||||
|
resolution: {integrity: sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==}
|
||||||
|
|
||||||
|
'@codemirror/search@6.7.1':
|
||||||
|
resolution: {integrity: sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==}
|
||||||
|
|
||||||
|
'@codemirror/state@6.7.1':
|
||||||
|
resolution: {integrity: sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A==}
|
||||||
|
|
||||||
|
'@codemirror/view@6.43.6':
|
||||||
|
resolution: {integrity: sha512-EVunGSYN1wz1p75WY1s3Xg7t3i8Yol0kGZGizNdX9BUFgMFILYVe8/u6EVpo7Ff5PwbZuILb4QAq7IZoKzIEQA==}
|
||||||
|
|
||||||
'@dotenvx/dotenvx@1.75.1':
|
'@dotenvx/dotenvx@1.75.1':
|
||||||
resolution: {integrity: sha512-/BITOC9dmS/edY2zQwZNicQ059O6RKabtQfyEafV0nGtfYRNHYy1DIPiYVcov40+tob9hfmBnbR963dS+EQ1DQ==}
|
resolution: {integrity: sha512-/BITOC9dmS/edY2zQwZNicQ059O6RKabtQfyEafV0nGtfYRNHYy1DIPiYVcov40+tob9hfmBnbR963dS+EQ1DQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -485,11 +608,65 @@ packages:
|
|||||||
'@jridgewell/trace-mapping@0.3.31':
|
'@jridgewell/trace-mapping@0.3.31':
|
||||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||||
|
|
||||||
|
'@lezer/common@1.5.2':
|
||||||
|
resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==}
|
||||||
|
|
||||||
|
'@lezer/cpp@1.1.6':
|
||||||
|
resolution: {integrity: sha512-vh9gWWJOXFVY8HBHK3Twzq8MgwG2iN4GSyzBP9sCGTe37P15x2R14VaBQk0VA0ezTRN1KHYBBsHhvpGZ2Xy/pA==}
|
||||||
|
|
||||||
|
'@lezer/css@1.3.4':
|
||||||
|
resolution: {integrity: sha512-N+tn9tej2hPvyKgHEApMOQfHczDJCwxrRFS3SPn9QjYN+uwHvEDnCgKRrb3mxDYxRS8sKMM8fhC3+lc04Abz5Q==}
|
||||||
|
|
||||||
|
'@lezer/go@1.0.1':
|
||||||
|
resolution: {integrity: sha512-xToRsYxwsgJNHTgNdStpcvmbVuKxTapV0dM0wey1geMMRc9aggoVyKgzYp41D2/vVOx+Ii4hmE206kvxIXBVXQ==}
|
||||||
|
|
||||||
|
'@lezer/highlight@1.2.3':
|
||||||
|
resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==}
|
||||||
|
|
||||||
|
'@lezer/html@1.3.13':
|
||||||
|
resolution: {integrity: sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==}
|
||||||
|
|
||||||
|
'@lezer/java@1.1.3':
|
||||||
|
resolution: {integrity: sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw==}
|
||||||
|
|
||||||
|
'@lezer/javascript@1.5.4':
|
||||||
|
resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==}
|
||||||
|
|
||||||
|
'@lezer/json@1.0.3':
|
||||||
|
resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==}
|
||||||
|
|
||||||
|
'@lezer/lr@1.4.10':
|
||||||
|
resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==}
|
||||||
|
|
||||||
|
'@lezer/markdown@1.7.1':
|
||||||
|
resolution: {integrity: sha512-MEBZeFSBxgteUjEC3Wxg2Dwld5/JxRKG267L3bMFdibm8KjqSdiJYBeFw1Nt1CM8+zKMpSIEHblY8FD9z38sJQ==}
|
||||||
|
|
||||||
|
'@lezer/php@1.0.5':
|
||||||
|
resolution: {integrity: sha512-W7asp9DhM6q0W6DYNwIkLSKOvxlXRrif+UXBMxzsJUuqmhE7oVU+gS3THO4S/Puh7Xzgm858UNaFi6dxTP8dJA==}
|
||||||
|
|
||||||
|
'@lezer/python@1.1.19':
|
||||||
|
resolution: {integrity: sha512-MhQIURHRytsNzP/YXnqpYKW6la6voAH3kyplTOOiCdjyFY6cWWGFVmYVdHIPrElqSDf4iCDktQCockB9FxuhzQ==}
|
||||||
|
|
||||||
|
'@lezer/rust@1.0.2':
|
||||||
|
resolution: {integrity: sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==}
|
||||||
|
|
||||||
|
'@lezer/sass@1.1.0':
|
||||||
|
resolution: {integrity: sha512-3mMGdCTUZ/84ArHOuXWQr37pnf7f+Nw9ycPUeKX+wu19b7pSMcZGLbaXwvD2APMBDOGxPmpK/O6S1v1EvLoqgQ==}
|
||||||
|
|
||||||
|
'@lezer/xml@1.0.6':
|
||||||
|
resolution: {integrity: sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==}
|
||||||
|
|
||||||
|
'@lezer/yaml@1.0.4':
|
||||||
|
resolution: {integrity: sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw==}
|
||||||
|
|
||||||
'@lucide/vue@1.23.0':
|
'@lucide/vue@1.23.0':
|
||||||
resolution: {integrity: sha512-9SIYeY5K+R1iv8F8JUKMGSL7Pck/86BJ8djtZz/lnYsoHtKFUj1H2z6+PvKnC/8blZ8tqTDeDXAoTKobuX80hg==}
|
resolution: {integrity: sha512-9SIYeY5K+R1iv8F8JUKMGSL7Pck/86BJ8djtZz/lnYsoHtKFUj1H2z6+PvKnC/8blZ8tqTDeDXAoTKobuX80hg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: '>=3.0.1'
|
vue: '>=3.0.1'
|
||||||
|
|
||||||
|
'@marijn/find-cluster-break@1.0.3':
|
||||||
|
resolution: {integrity: sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==}
|
||||||
|
|
||||||
'@modelcontextprotocol/sdk@1.29.0':
|
'@modelcontextprotocol/sdk@1.29.0':
|
||||||
resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==}
|
resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -654,6 +831,37 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
|
'@shikijs/core@4.3.1':
|
||||||
|
resolution: {integrity: sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/engine-javascript@4.3.1':
|
||||||
|
resolution: {integrity: sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/engine-oniguruma@4.3.1':
|
||||||
|
resolution: {integrity: sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/langs@4.3.1':
|
||||||
|
resolution: {integrity: sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/primitive@4.3.1':
|
||||||
|
resolution: {integrity: sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/themes@4.3.1':
|
||||||
|
resolution: {integrity: sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/types@4.3.1':
|
||||||
|
resolution: {integrity: sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
'@shikijs/vscode-textmate@10.0.2':
|
||||||
|
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
|
||||||
|
|
||||||
'@swc/helpers@0.5.23':
|
'@swc/helpers@0.5.23':
|
||||||
resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==}
|
resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==}
|
||||||
|
|
||||||
@@ -859,6 +1067,9 @@ packages:
|
|||||||
'@types/hast@3.0.4':
|
'@types/hast@3.0.4':
|
||||||
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
|
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
|
||||||
|
|
||||||
|
'@types/hast@3.0.5':
|
||||||
|
resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==}
|
||||||
|
|
||||||
'@types/json-schema@7.0.15':
|
'@types/json-schema@7.0.15':
|
||||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||||
|
|
||||||
@@ -1210,6 +1421,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==}
|
resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
|
|
||||||
|
crelt@1.0.7:
|
||||||
|
resolution: {integrity: sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==}
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -2223,6 +2437,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
oniguruma-parser@0.12.2:
|
||||||
|
resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==}
|
||||||
|
|
||||||
|
oniguruma-to-es@4.3.6:
|
||||||
|
resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==}
|
||||||
|
|
||||||
open@10.2.0:
|
open@10.2.0:
|
||||||
resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
|
resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -2410,6 +2630,15 @@ packages:
|
|||||||
resolution: {integrity: sha512-CkfWKhQiYsMQYaWUkHdERXUxT2jJLBoa5y7zFv3dUAE7Ly5oU/0hsqrENyEfrCL03pDsQYbnoz17Cbagx/c2OA==}
|
resolution: {integrity: sha512-CkfWKhQiYsMQYaWUkHdERXUxT2jJLBoa5y7zFv3dUAE7Ly5oU/0hsqrENyEfrCL03pDsQYbnoz17Cbagx/c2OA==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
|
|
||||||
|
regex-recursion@6.0.2:
|
||||||
|
resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
|
||||||
|
|
||||||
|
regex-utilities@2.3.0:
|
||||||
|
resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
|
||||||
|
|
||||||
|
regex@6.1.0:
|
||||||
|
resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==}
|
||||||
|
|
||||||
rehype-sanitize@6.0.0:
|
rehype-sanitize@6.0.0:
|
||||||
resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==}
|
resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==}
|
||||||
|
|
||||||
@@ -2514,6 +2743,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
shiki@4.3.1:
|
||||||
|
resolution: {integrity: sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
side-channel-list@1.0.1:
|
side-channel-list@1.0.1:
|
||||||
resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==}
|
resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -2606,6 +2839,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
|
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
style-mod@4.1.3:
|
||||||
|
resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==}
|
||||||
|
|
||||||
stylus@0.57.0:
|
stylus@0.57.0:
|
||||||
resolution: {integrity: sha512-yOI6G8WYfr0q8v8rRvE91wbxFU+rJPo760Va4MF6K0I6BZjO4r+xSynkvyPBP9tV1CIEUeRsiidjIs2rzb1CnQ==}
|
resolution: {integrity: sha512-yOI6G8WYfr0q8v8rRvE91wbxFU+rJPo760Va4MF6K0I6BZjO4r+xSynkvyPBP9tV1CIEUeRsiidjIs2rzb1CnQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -2826,6 +3062,9 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
w3c-keyname@2.2.8:
|
||||||
|
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
|
||||||
|
|
||||||
web-worker@1.5.0:
|
web-worker@1.5.0:
|
||||||
resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==}
|
resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==}
|
||||||
|
|
||||||
@@ -3070,6 +3309,257 @@ snapshots:
|
|||||||
'@babel/helper-string-parser': 7.29.7
|
'@babel/helper-string-parser': 7.29.7
|
||||||
'@babel/helper-validator-identifier': 7.29.7
|
'@babel/helper-validator-identifier': 7.29.7
|
||||||
|
|
||||||
|
'@codemirror/autocomplete@6.20.3':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@codemirror/view': 6.43.6
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
|
||||||
|
'@codemirror/commands@6.10.4':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@codemirror/view': 6.43.6
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
|
||||||
|
'@codemirror/lang-angular@0.1.4':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/lang-html': 6.4.11
|
||||||
|
'@codemirror/lang-javascript': 6.2.5
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@codemirror/lang-cpp@6.0.3':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@lezer/cpp': 1.1.6
|
||||||
|
|
||||||
|
'@codemirror/lang-css@6.3.1':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/autocomplete': 6.20.3
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/css': 1.3.4
|
||||||
|
|
||||||
|
'@codemirror/lang-go@6.0.1':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/autocomplete': 6.20.3
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/go': 1.0.1
|
||||||
|
|
||||||
|
'@codemirror/lang-html@6.4.11':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/autocomplete': 6.20.3
|
||||||
|
'@codemirror/lang-css': 6.3.1
|
||||||
|
'@codemirror/lang-javascript': 6.2.5
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@codemirror/view': 6.43.6
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/css': 1.3.4
|
||||||
|
'@lezer/html': 1.3.13
|
||||||
|
|
||||||
|
'@codemirror/lang-java@6.0.2':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@lezer/java': 1.1.3
|
||||||
|
|
||||||
|
'@codemirror/lang-javascript@6.2.5':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/autocomplete': 6.20.3
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/lint': 6.9.7
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@codemirror/view': 6.43.6
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/javascript': 1.5.4
|
||||||
|
|
||||||
|
'@codemirror/lang-jinja@6.0.1':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/autocomplete': 6.20.3
|
||||||
|
'@codemirror/lang-html': 6.4.11
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@codemirror/view': 6.43.6
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@codemirror/lang-json@6.0.2':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@lezer/json': 1.0.3
|
||||||
|
|
||||||
|
'@codemirror/lang-less@6.0.2':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/lang-css': 6.3.1
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@codemirror/lang-liquid@6.3.2':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/autocomplete': 6.20.3
|
||||||
|
'@codemirror/lang-html': 6.4.11
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@codemirror/view': 6.43.6
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@codemirror/lang-markdown@6.4.0':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/autocomplete': 6.20.3
|
||||||
|
'@codemirror/lang-html': 6.4.11
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@codemirror/view': 6.43.6
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/markdown': 1.7.1
|
||||||
|
|
||||||
|
'@codemirror/lang-php@6.0.2':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/lang-html': 6.4.11
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/php': 1.0.5
|
||||||
|
|
||||||
|
'@codemirror/lang-python@6.2.1':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/autocomplete': 6.20.3
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/python': 1.1.19
|
||||||
|
|
||||||
|
'@codemirror/lang-rust@6.0.2':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@lezer/rust': 1.0.2
|
||||||
|
|
||||||
|
'@codemirror/lang-sass@6.0.2':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/lang-css': 6.3.1
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/sass': 1.1.0
|
||||||
|
|
||||||
|
'@codemirror/lang-sql@6.10.0':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/autocomplete': 6.20.3
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@codemirror/lang-vue@0.1.3':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/lang-html': 6.4.11
|
||||||
|
'@codemirror/lang-javascript': 6.2.5
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@codemirror/lang-wast@6.0.2':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@codemirror/lang-xml@6.1.0':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/autocomplete': 6.20.3
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@codemirror/view': 6.43.6
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/xml': 1.0.6
|
||||||
|
|
||||||
|
'@codemirror/lang-yaml@6.1.3':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/autocomplete': 6.20.3
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
'@lezer/yaml': 1.0.4
|
||||||
|
|
||||||
|
'@codemirror/language-data@6.5.2':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/lang-angular': 0.1.4
|
||||||
|
'@codemirror/lang-cpp': 6.0.3
|
||||||
|
'@codemirror/lang-css': 6.3.1
|
||||||
|
'@codemirror/lang-go': 6.0.1
|
||||||
|
'@codemirror/lang-html': 6.4.11
|
||||||
|
'@codemirror/lang-java': 6.0.2
|
||||||
|
'@codemirror/lang-javascript': 6.2.5
|
||||||
|
'@codemirror/lang-jinja': 6.0.1
|
||||||
|
'@codemirror/lang-json': 6.0.2
|
||||||
|
'@codemirror/lang-less': 6.0.2
|
||||||
|
'@codemirror/lang-liquid': 6.3.2
|
||||||
|
'@codemirror/lang-markdown': 6.4.0
|
||||||
|
'@codemirror/lang-php': 6.0.2
|
||||||
|
'@codemirror/lang-python': 6.2.1
|
||||||
|
'@codemirror/lang-rust': 6.0.2
|
||||||
|
'@codemirror/lang-sass': 6.0.2
|
||||||
|
'@codemirror/lang-sql': 6.10.0
|
||||||
|
'@codemirror/lang-vue': 0.1.3
|
||||||
|
'@codemirror/lang-wast': 6.0.2
|
||||||
|
'@codemirror/lang-xml': 6.1.0
|
||||||
|
'@codemirror/lang-yaml': 6.1.3
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
'@codemirror/legacy-modes': 6.5.3
|
||||||
|
|
||||||
|
'@codemirror/language@6.12.4':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@codemirror/view': 6.43.6
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
style-mod: 4.1.3
|
||||||
|
|
||||||
|
'@codemirror/legacy-modes@6.5.3':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/language': 6.12.4
|
||||||
|
|
||||||
|
'@codemirror/lint@6.9.7':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@codemirror/view': 6.43.6
|
||||||
|
crelt: 1.0.7
|
||||||
|
|
||||||
|
'@codemirror/search@6.7.1':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
'@codemirror/view': 6.43.6
|
||||||
|
crelt: 1.0.7
|
||||||
|
|
||||||
|
'@codemirror/state@6.7.1':
|
||||||
|
dependencies:
|
||||||
|
'@marijn/find-cluster-break': 1.0.3
|
||||||
|
|
||||||
|
'@codemirror/view@6.43.6':
|
||||||
|
dependencies:
|
||||||
|
'@codemirror/state': 6.7.1
|
||||||
|
crelt: 1.0.7
|
||||||
|
style-mod: 4.1.3
|
||||||
|
w3c-keyname: 2.2.8
|
||||||
|
|
||||||
'@dotenvx/dotenvx@1.75.1':
|
'@dotenvx/dotenvx@1.75.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@dotenvx/primitives': 0.8.0
|
'@dotenvx/primitives': 0.8.0
|
||||||
@@ -3277,10 +3767,105 @@ snapshots:
|
|||||||
'@jridgewell/resolve-uri': 3.1.2
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
|
||||||
|
'@lezer/common@1.5.2': {}
|
||||||
|
|
||||||
|
'@lezer/cpp@1.1.6':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@lezer/css@1.3.4':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@lezer/go@1.0.1':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@lezer/highlight@1.2.3':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
|
||||||
|
'@lezer/html@1.3.13':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@lezer/java@1.1.3':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@lezer/javascript@1.5.4':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@lezer/json@1.0.3':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@lezer/lr@1.4.10':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
|
||||||
|
'@lezer/markdown@1.7.1':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
|
||||||
|
'@lezer/php@1.0.5':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@lezer/python@1.1.19':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@lezer/rust@1.0.2':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@lezer/sass@1.1.0':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@lezer/xml@1.0.6':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
|
'@lezer/yaml@1.0.4':
|
||||||
|
dependencies:
|
||||||
|
'@lezer/common': 1.5.2
|
||||||
|
'@lezer/highlight': 1.2.3
|
||||||
|
'@lezer/lr': 1.4.10
|
||||||
|
|
||||||
'@lucide/vue@1.23.0(vue@3.5.39(typescript@5.6.3))':
|
'@lucide/vue@1.23.0(vue@3.5.39(typescript@5.6.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
vue: 3.5.39(typescript@5.6.3)
|
vue: 3.5.39(typescript@5.6.3)
|
||||||
|
|
||||||
|
'@marijn/find-cluster-break@1.0.3': {}
|
||||||
|
|
||||||
'@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)':
|
'@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@hono/node-server': 1.19.14(hono@4.12.28)
|
'@hono/node-server': 1.19.14(hono@4.12.28)
|
||||||
@@ -3393,6 +3978,46 @@ snapshots:
|
|||||||
'@rollup/rollup-win32-x64-msvc@4.62.2':
|
'@rollup/rollup-win32-x64-msvc@4.62.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@shikijs/core@4.3.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/primitive': 4.3.1
|
||||||
|
'@shikijs/types': 4.3.1
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
'@types/hast': 3.0.5
|
||||||
|
hast-util-to-html: 9.0.5
|
||||||
|
|
||||||
|
'@shikijs/engine-javascript@4.3.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 4.3.1
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
oniguruma-to-es: 4.3.6
|
||||||
|
|
||||||
|
'@shikijs/engine-oniguruma@4.3.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 4.3.1
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
|
||||||
|
'@shikijs/langs@4.3.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 4.3.1
|
||||||
|
|
||||||
|
'@shikijs/primitive@4.3.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 4.3.1
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
'@types/hast': 3.0.5
|
||||||
|
|
||||||
|
'@shikijs/themes@4.3.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 4.3.1
|
||||||
|
|
||||||
|
'@shikijs/types@4.3.1':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
'@types/hast': 3.0.5
|
||||||
|
|
||||||
|
'@shikijs/vscode-textmate@10.0.2': {}
|
||||||
|
|
||||||
'@swc/helpers@0.5.23':
|
'@swc/helpers@0.5.23':
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
@@ -3547,6 +4172,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 3.0.3
|
'@types/unist': 3.0.3
|
||||||
|
|
||||||
|
'@types/hast@3.0.5':
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
|
||||||
'@types/json-schema@7.0.15': {}
|
'@types/json-schema@7.0.15': {}
|
||||||
|
|
||||||
'@types/mdast@4.0.4':
|
'@types/mdast@4.0.4':
|
||||||
@@ -3907,6 +4536,8 @@ snapshots:
|
|||||||
object-assign: 4.1.1
|
object-assign: 4.1.1
|
||||||
vary: 1.1.2
|
vary: 1.1.2
|
||||||
|
|
||||||
|
crelt@1.0.7: {}
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
path-key: 3.1.1
|
path-key: 3.1.1
|
||||||
@@ -4406,7 +5037,7 @@ snapshots:
|
|||||||
|
|
||||||
hast-util-whitespace@3.0.0:
|
hast-util-whitespace@3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.5
|
||||||
|
|
||||||
he@1.2.0: {}
|
he@1.2.0: {}
|
||||||
|
|
||||||
@@ -5048,6 +5679,14 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mimic-function: 5.0.1
|
mimic-function: 5.0.1
|
||||||
|
|
||||||
|
oniguruma-parser@0.12.2: {}
|
||||||
|
|
||||||
|
oniguruma-to-es@4.3.6:
|
||||||
|
dependencies:
|
||||||
|
oniguruma-parser: 0.12.2
|
||||||
|
regex: 6.1.0
|
||||||
|
regex-recursion: 6.0.2
|
||||||
|
|
||||||
open@10.2.0:
|
open@10.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
default-browser: 5.5.0
|
default-browser: 5.5.0
|
||||||
@@ -5235,6 +5874,16 @@ snapshots:
|
|||||||
tiny-invariant: 1.3.3
|
tiny-invariant: 1.3.3
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
regex-recursion@6.0.2:
|
||||||
|
dependencies:
|
||||||
|
regex-utilities: 2.3.0
|
||||||
|
|
||||||
|
regex-utilities@2.3.0: {}
|
||||||
|
|
||||||
|
regex@6.1.0:
|
||||||
|
dependencies:
|
||||||
|
regex-utilities: 2.3.0
|
||||||
|
|
||||||
rehype-sanitize@6.0.0:
|
rehype-sanitize@6.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
@@ -5449,6 +6098,17 @@ snapshots:
|
|||||||
|
|
||||||
shebang-regex@3.0.0: {}
|
shebang-regex@3.0.0: {}
|
||||||
|
|
||||||
|
shiki@4.3.1:
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/core': 4.3.1
|
||||||
|
'@shikijs/engine-javascript': 4.3.1
|
||||||
|
'@shikijs/engine-oniguruma': 4.3.1
|
||||||
|
'@shikijs/langs': 4.3.1
|
||||||
|
'@shikijs/themes': 4.3.1
|
||||||
|
'@shikijs/types': 4.3.1
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
'@types/hast': 3.0.5
|
||||||
|
|
||||||
side-channel-list@1.0.1:
|
side-channel-list@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
@@ -5551,6 +6211,8 @@ snapshots:
|
|||||||
|
|
||||||
strip-final-newline@2.0.0: {}
|
strip-final-newline@2.0.0: {}
|
||||||
|
|
||||||
|
style-mod@4.1.3: {}
|
||||||
|
|
||||||
stylus@0.57.0:
|
stylus@0.57.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
css: 3.0.0
|
css: 3.0.0
|
||||||
@@ -5771,6 +6433,8 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.6.3
|
typescript: 5.6.3
|
||||||
|
|
||||||
|
w3c-keyname@2.2.8: {}
|
||||||
|
|
||||||
web-worker@1.5.0: {}
|
web-worker@1.5.0: {}
|
||||||
|
|
||||||
which@2.0.1:
|
which@2.0.1:
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
allowBuilds:
|
allowBuilds:
|
||||||
esbuild: true
|
esbuild: true
|
||||||
vue-demi: true
|
vue-demi: true
|
||||||
|
overrides:
|
||||||
|
'@codemirror/lang-markdown': 6.4.0
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
onBeforeUnmount,
|
onBeforeUnmount,
|
||||||
ref,
|
ref,
|
||||||
watch,
|
watch,
|
||||||
type ComponentPublicInstance,
|
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import { unified, type Plugin, type Transformer } from "unified";
|
import { unified, type Plugin, type Transformer } from "unified";
|
||||||
import remarkParse from "remark-parse";
|
import remarkParse from "remark-parse";
|
||||||
@@ -14,6 +13,9 @@ import remarkRehype from "remark-rehype";
|
|||||||
import rehypeStringify from "rehype-stringify";
|
import rehypeStringify from "rehype-stringify";
|
||||||
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
|
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
|
||||||
import { convertFileSrc, invoke, isTauri } from "@tauri-apps/api/core";
|
import { convertFileSrc, invoke, isTauri } from "@tauri-apps/api/core";
|
||||||
|
import MarkdownCodeEditor from "./MarkdownCodeEditor.vue";
|
||||||
|
import { highlightCodeToHtml } from "@/lib/codeHighlight";
|
||||||
|
import { resolveMarkdownCodeLanguage } from "@/lib/markdownLanguages";
|
||||||
|
|
||||||
type DocumentMode = "preview" | "edit" | "mixed";
|
type DocumentMode = "preview" | "edit" | "mixed";
|
||||||
type ClipboardMediaKind = "image" | "video";
|
type ClipboardMediaKind = "image" | "video";
|
||||||
@@ -29,6 +31,7 @@ const VIDEO_MIME_TYPES: Record<string, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface MarkdownAstNode {
|
interface MarkdownAstNode {
|
||||||
|
type?: string;
|
||||||
position?: {
|
position?: {
|
||||||
start?: { offset?: number };
|
start?: { offset?: number };
|
||||||
end?: { offset?: number };
|
end?: { offset?: number };
|
||||||
@@ -51,12 +54,20 @@ interface MarkdownTree {
|
|||||||
|
|
||||||
interface MarkdownBlock {
|
interface MarkdownBlock {
|
||||||
id: string;
|
id: string;
|
||||||
|
type: string;
|
||||||
source: string;
|
source: string;
|
||||||
html: string;
|
html: string;
|
||||||
startOffset: number;
|
startOffset: number;
|
||||||
endOffset: number;
|
endOffset: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface MarkdownCodeEditorApi {
|
||||||
|
focus: () => void;
|
||||||
|
focusAtEnd: () => void;
|
||||||
|
replaceRange: (text: string, from?: number, to?: number) => void;
|
||||||
|
scrollToLine: (lineNumber: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
modelValue?: string;
|
modelValue?: string;
|
||||||
@@ -230,9 +241,9 @@ const renderedHtml = ref("");
|
|||||||
const renderError = ref(false);
|
const renderError = ref(false);
|
||||||
const markdownBlocks = ref<MarkdownBlock[]>([]);
|
const markdownBlocks = ref<MarkdownBlock[]>([]);
|
||||||
|
|
||||||
const editorTextareaRef = ref<HTMLTextAreaElement>();
|
const editorCodeRef = ref<MarkdownCodeEditorApi>();
|
||||||
const scrollRef = ref<HTMLDivElement>();
|
const scrollRef = ref<HTMLDivElement>();
|
||||||
const blockTextareaRef = ref<HTMLTextAreaElement | null>(null);
|
const blockCodeRef = ref<MarkdownCodeEditorApi>();
|
||||||
|
|
||||||
const EMPTY_BLOCK_ID = "__empty__";
|
const EMPTY_BLOCK_ID = "__empty__";
|
||||||
const editingBlockId = ref<string | null>(null);
|
const editingBlockId = ref<string | null>(null);
|
||||||
@@ -245,8 +256,8 @@ const pendingLocalModelUpdate = ref(false);
|
|||||||
const isEditingEmptyDocument = computed(
|
const isEditingEmptyDocument = computed(
|
||||||
() => editingBlockId.value === EMPTY_BLOCK_ID,
|
() => editingBlockId.value === EMPTY_BLOCK_ID,
|
||||||
);
|
);
|
||||||
let textareaResizeFrame: number | null = null;
|
|
||||||
let modeScrollRestoreFrame: number | null = null;
|
let modeScrollRestoreFrame: number | null = null;
|
||||||
|
let renderRequestId = 0;
|
||||||
|
|
||||||
interface ScrollSnapshot {
|
interface ScrollSnapshot {
|
||||||
ratio: number;
|
ratio: number;
|
||||||
@@ -276,7 +287,6 @@ function restoreScrollSnapshot(snapshot: ScrollSnapshot | null) {
|
|||||||
|
|
||||||
function restoreScrollAfterModeChange(
|
function restoreScrollAfterModeChange(
|
||||||
snapshot: ScrollSnapshot | null,
|
snapshot: ScrollSnapshot | null,
|
||||||
mode: DocumentMode,
|
|
||||||
) {
|
) {
|
||||||
if (!snapshot) return;
|
if (!snapshot) return;
|
||||||
|
|
||||||
@@ -286,17 +296,10 @@ function restoreScrollAfterModeChange(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void nextTick(() => {
|
void nextTick(() => {
|
||||||
if (mode === "edit" && editorTextareaRef.value) {
|
|
||||||
resizeTextarea(editorTextareaRef.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreScrollSnapshot(snapshot);
|
restoreScrollSnapshot(snapshot);
|
||||||
|
|
||||||
modeScrollRestoreFrame = requestAnimationFrame(() => {
|
modeScrollRestoreFrame = requestAnimationFrame(() => {
|
||||||
modeScrollRestoreFrame = null;
|
modeScrollRestoreFrame = null;
|
||||||
if (mode === "edit" && editorTextareaRef.value) {
|
|
||||||
resizeTextarea(editorTextareaRef.value);
|
|
||||||
}
|
|
||||||
restoreScrollSnapshot(snapshot);
|
restoreScrollSnapshot(snapshot);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -304,21 +307,62 @@ function restoreScrollAfterModeChange(
|
|||||||
|
|
||||||
function syncRenderedState(md: string, mode = activeDocumentMode.value) {
|
function syncRenderedState(md: string, mode = activeDocumentMode.value) {
|
||||||
if (mode === "preview") {
|
if (mode === "preview") {
|
||||||
renderFullHtml(md);
|
void renderFullHtml(md);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode === "mixed") {
|
if (mode === "mixed") {
|
||||||
parseBlocks(md);
|
void parseBlocks(md);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderFullHtml(md: string) {
|
async function highlightCodeBlocks(html: string) {
|
||||||
|
if (!html.includes("<pre><code")) return html;
|
||||||
|
|
||||||
|
const document = new DOMParser().parseFromString(html, "text/html");
|
||||||
|
const codeBlocks = Array.from(document.querySelectorAll("pre > code"));
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
codeBlocks.map(async (code) => {
|
||||||
|
const languageClass = Array.from(code.classList).find((className) =>
|
||||||
|
className.startsWith("language-"),
|
||||||
|
);
|
||||||
|
const requestedLanguage = languageClass?.slice("language-".length) || "text";
|
||||||
|
const language = resolveMarkdownCodeLanguage(requestedLanguage)?.shiki || "text";
|
||||||
|
code.parentElement?.setAttribute("data-language", requestedLanguage);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const highlighted = await highlightCodeToHtml(code.textContent || "", language);
|
||||||
|
if (!highlighted) return;
|
||||||
|
const fragment = new DOMParser().parseFromString(highlighted, "text/html");
|
||||||
|
const highlightedPre = fragment.body.firstElementChild;
|
||||||
|
if (highlightedPre) {
|
||||||
|
highlightedPre.setAttribute("data-language", requestedLanguage);
|
||||||
|
code.parentElement?.replaceWith(highlightedPre);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
code.parentElement?.setAttribute("data-language", requestedLanguage);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return document.body.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderMarkdownHtml(md: string) {
|
||||||
|
const file = md2html.processSync(md);
|
||||||
|
return highlightCodeBlocks(String(file.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderFullHtml(md: string) {
|
||||||
|
const requestId = ++renderRequestId;
|
||||||
try {
|
try {
|
||||||
const file = md2html.processSync(md);
|
const html = await renderMarkdownHtml(md);
|
||||||
renderedHtml.value = String(file.value);
|
if (requestId !== renderRequestId || activeDocumentMode.value !== "preview") return;
|
||||||
|
renderedHtml.value = html;
|
||||||
renderError.value = false;
|
renderError.value = false;
|
||||||
} catch {
|
} catch {
|
||||||
|
if (requestId !== renderRequestId) return;
|
||||||
renderedHtml.value = "";
|
renderedHtml.value = "";
|
||||||
renderError.value = true;
|
renderError.value = true;
|
||||||
}
|
}
|
||||||
@@ -333,16 +377,16 @@ function escapeHtml(value: string) {
|
|||||||
.replace(/'/g, "'");
|
.replace(/'/g, "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderBlockHtml(source: string) {
|
async function renderBlockHtml(source: string) {
|
||||||
try {
|
try {
|
||||||
const file = md2html.processSync(source);
|
return await renderMarkdownHtml(source);
|
||||||
return String(file.value);
|
|
||||||
} catch {
|
} catch {
|
||||||
return `<pre><code>${escapeHtml(source)}</code></pre>`;
|
return `<pre><code>${escapeHtml(source)}</code></pre>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseBlocks(md: string) {
|
async function parseBlocks(md: string) {
|
||||||
|
const requestId = ++renderRequestId;
|
||||||
if (!md.trim()) {
|
if (!md.trim()) {
|
||||||
markdownBlocks.value = [];
|
markdownBlocks.value = [];
|
||||||
renderError.value = false;
|
renderError.value = false;
|
||||||
@@ -351,8 +395,8 @@ function parseBlocks(md: string) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const tree = mdParser.parse(md) as { children?: MarkdownAstNode[] };
|
const tree = mdParser.parse(md) as { children?: MarkdownAstNode[] };
|
||||||
const blocks = (tree.children ?? [])
|
const blockCandidates = (tree.children ?? [])
|
||||||
.map((node, index): MarkdownBlock | null => {
|
.map((node, index): Omit<MarkdownBlock, "html"> | null => {
|
||||||
const startOffset = node.position?.start?.offset;
|
const startOffset = node.position?.start?.offset;
|
||||||
const endOffset = node.position?.end?.offset;
|
const endOffset = node.position?.end?.offset;
|
||||||
|
|
||||||
@@ -367,22 +411,33 @@ function parseBlocks(md: string) {
|
|||||||
const source = md.slice(startOffset, endOffset);
|
const source = md.slice(startOffset, endOffset);
|
||||||
return {
|
return {
|
||||||
id: `${index}-${startOffset}-${endOffset}`,
|
id: `${index}-${startOffset}-${endOffset}`,
|
||||||
|
type: node.type || "unknown",
|
||||||
source,
|
source,
|
||||||
html: renderBlockHtml(source),
|
|
||||||
startOffset,
|
startOffset,
|
||||||
endOffset,
|
endOffset,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter((block): block is MarkdownBlock => block !== null);
|
.filter((block): block is Omit<MarkdownBlock, "html"> => block !== null);
|
||||||
|
|
||||||
|
const blocks = await Promise.all(
|
||||||
|
blockCandidates.map(async (block) => ({
|
||||||
|
...block,
|
||||||
|
html: await renderBlockHtml(block.source),
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (requestId !== renderRequestId || activeDocumentMode.value !== "mixed") return;
|
||||||
markdownBlocks.value = blocks;
|
markdownBlocks.value = blocks;
|
||||||
renderError.value = false;
|
renderError.value = false;
|
||||||
} catch {
|
} catch {
|
||||||
|
const html = await renderBlockHtml(md);
|
||||||
|
if (requestId !== renderRequestId || activeDocumentMode.value !== "mixed") return;
|
||||||
markdownBlocks.value = [
|
markdownBlocks.value = [
|
||||||
{
|
{
|
||||||
id: "fallback-0",
|
id: "fallback-0",
|
||||||
|
type: "unknown",
|
||||||
source: md,
|
source: md,
|
||||||
html: renderBlockHtml(md),
|
html,
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: md.length,
|
endOffset: md.length,
|
||||||
},
|
},
|
||||||
@@ -407,7 +462,6 @@ watch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
scheduleEditorTextareaResize();
|
|
||||||
// 外部切换文件时重置滚动位置;本组件内编辑时保留当前位置。
|
// 外部切换文件时重置滚动位置;本组件内编辑时保留当前位置。
|
||||||
if (shouldResetScroll && scrollRef.value) scrollRef.value.scrollTop = 0;
|
if (shouldResetScroll && scrollRef.value) scrollRef.value.scrollTop = 0;
|
||||||
});
|
});
|
||||||
@@ -415,40 +469,11 @@ watch(
|
|||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
// ── Editor textarea handlers ───────────────────────────────────────
|
// ── Editor handlers ────────────────────────────────────────────────
|
||||||
|
|
||||||
function setBlockTextareaRef(el: Element | ComponentPublicInstance | null) {
|
|
||||||
blockTextareaRef.value = el instanceof HTMLTextAreaElement ? el : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resizeTextarea(ta: HTMLTextAreaElement) {
|
|
||||||
ta.style.height = "auto";
|
|
||||||
ta.style.height = `${ta.scrollHeight}px`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function scheduleTextareaResize(ta: HTMLTextAreaElement | undefined | null) {
|
|
||||||
if (!ta) return;
|
|
||||||
if (textareaResizeFrame !== null) {
|
|
||||||
cancelAnimationFrame(textareaResizeFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
textareaResizeFrame = requestAnimationFrame(() => {
|
|
||||||
textareaResizeFrame = null;
|
|
||||||
if (ta.isConnected) resizeTextarea(ta);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function scheduleEditorTextareaResize() {
|
|
||||||
scheduleTextareaResize(editorTextareaRef.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function focusBlockEditor() {
|
function focusBlockEditor() {
|
||||||
void nextTick(() => {
|
void nextTick(() => {
|
||||||
const ta = blockTextareaRef.value;
|
blockCodeRef.value?.focusAtEnd();
|
||||||
if (!ta) return;
|
|
||||||
resizeTextarea(ta);
|
|
||||||
ta.focus();
|
|
||||||
ta.selectionStart = ta.selectionEnd = ta.value.length;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,54 +486,6 @@ function applyFullEditorValue(value: string) {
|
|||||||
emitModelValue(value);
|
emitModelValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEditorInput(e: Event) {
|
|
||||||
const ta = e.target as HTMLTextAreaElement;
|
|
||||||
const val = ta.value;
|
|
||||||
applyFullEditorValue(val);
|
|
||||||
scheduleTextareaResize(ta);
|
|
||||||
}
|
|
||||||
|
|
||||||
function tryAutoPairForValue(
|
|
||||||
ta: HTMLTextAreaElement,
|
|
||||||
key: string,
|
|
||||||
value: string,
|
|
||||||
onChange: (nextValue: string) => void,
|
|
||||||
): boolean {
|
|
||||||
const pairs: Record<string, string> = {
|
|
||||||
"(": ")",
|
|
||||||
"[": "]",
|
|
||||||
"{": "}",
|
|
||||||
'"': '"',
|
|
||||||
"'": "'",
|
|
||||||
"`": "`",
|
|
||||||
};
|
|
||||||
const close = pairs[key];
|
|
||||||
if (!close) return false;
|
|
||||||
|
|
||||||
const start = ta.selectionStart;
|
|
||||||
const end = ta.selectionEnd;
|
|
||||||
const selected = value.substring(start, end);
|
|
||||||
const newVal =
|
|
||||||
value.substring(0, start) +
|
|
||||||
key +
|
|
||||||
selected +
|
|
||||||
close +
|
|
||||||
value.substring(end);
|
|
||||||
|
|
||||||
onChange(newVal);
|
|
||||||
|
|
||||||
void nextTick(() => {
|
|
||||||
ta.selectionStart = start + 1;
|
|
||||||
ta.selectionEnd = start + 1 + selected.length;
|
|
||||||
ta.focus();
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tryAutoPair(ta: HTMLTextAreaElement, key: string): boolean {
|
|
||||||
return tryAutoPairForValue(ta, key, props.modelValue, applyFullEditorValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isSaveShortcut(e: KeyboardEvent) {
|
function isSaveShortcut(e: KeyboardEvent) {
|
||||||
return (e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "s";
|
return (e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "s";
|
||||||
}
|
}
|
||||||
@@ -526,36 +503,6 @@ function onRootKeydown(e: KeyboardEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEditorKeydown(e: KeyboardEvent) {
|
|
||||||
if (isSaveShortcut(e)) {
|
|
||||||
requestSave(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ta = e.target as HTMLTextAreaElement;
|
|
||||||
|
|
||||||
if (e.key === "Tab") {
|
|
||||||
e.preventDefault();
|
|
||||||
const start = ta.selectionStart;
|
|
||||||
const end = ta.selectionEnd;
|
|
||||||
const newVal =
|
|
||||||
props.modelValue.substring(0, start) +
|
|
||||||
" " +
|
|
||||||
props.modelValue.substring(end);
|
|
||||||
applyFullEditorValue(newVal);
|
|
||||||
void nextTick(() => {
|
|
||||||
ta.selectionStart = ta.selectionEnd = start + 2;
|
|
||||||
ta.focus();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tryAutoPair(ta, e.key)) {
|
|
||||||
e.preventDefault();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Mixed mode block editing ───────────────────────────────────────
|
// ── Mixed mode block editing ───────────────────────────────────────
|
||||||
|
|
||||||
function startBlockEdit(block: MarkdownBlock) {
|
function startBlockEdit(block: MarkdownBlock) {
|
||||||
@@ -615,52 +562,6 @@ function finalizeBlockEdit(): string | undefined {
|
|||||||
return nextValue;
|
return nextValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onBlockInput(e: Event) {
|
|
||||||
const ta = e.target as HTMLTextAreaElement;
|
|
||||||
editingSource.value = ta.value;
|
|
||||||
scheduleTextareaResize(ta);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onBlockKeydown(e: KeyboardEvent) {
|
|
||||||
const ta = e.target as HTMLTextAreaElement;
|
|
||||||
|
|
||||||
if (isSaveShortcut(e)) {
|
|
||||||
requestSave(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.key === "Escape") {
|
|
||||||
e.preventDefault();
|
|
||||||
cancelBlockEdit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.key === "Tab") {
|
|
||||||
e.preventDefault();
|
|
||||||
const start = ta.selectionStart;
|
|
||||||
const end = ta.selectionEnd;
|
|
||||||
editingSource.value =
|
|
||||||
editingSource.value.substring(0, start) +
|
|
||||||
" " +
|
|
||||||
editingSource.value.substring(end);
|
|
||||||
void nextTick(() => {
|
|
||||||
ta.selectionStart = ta.selectionEnd = start + 2;
|
|
||||||
scheduleTextareaResize(ta);
|
|
||||||
ta.focus();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
tryAutoPairForValue(ta, e.key, editingSource.value, (nextValue) => {
|
|
||||||
editingSource.value = nextValue;
|
|
||||||
void nextTick(() => scheduleTextareaResize(ta));
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function flushPendingChanges(): string | undefined {
|
function flushPendingChanges(): string | undefined {
|
||||||
if (!editingBlockId.value) return undefined;
|
if (!editingBlockId.value) return undefined;
|
||||||
return finalizeBlockEdit();
|
return finalizeBlockEdit();
|
||||||
@@ -675,17 +576,10 @@ watch(activeDocumentMode, (mode, oldMode) => {
|
|||||||
|
|
||||||
syncRenderedState(committedValue ?? props.modelValue, mode);
|
syncRenderedState(committedValue ?? props.modelValue, mode);
|
||||||
|
|
||||||
if (mode === "edit") {
|
restoreScrollAfterModeChange(scrollSnapshot);
|
||||||
void nextTick(scheduleEditorTextareaResize);
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreScrollAfterModeChange(scrollSnapshot, mode);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (textareaResizeFrame !== null) {
|
|
||||||
cancelAnimationFrame(textareaResizeFrame);
|
|
||||||
}
|
|
||||||
if (modeScrollRestoreFrame !== null) {
|
if (modeScrollRestoreFrame !== null) {
|
||||||
cancelAnimationFrame(modeScrollRestoreFrame);
|
cancelAnimationFrame(modeScrollRestoreFrame);
|
||||||
}
|
}
|
||||||
@@ -948,47 +842,38 @@ async function processPaste(
|
|||||||
applyValue(newValue, cursorPos);
|
applyValue(newValue, cursorPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onEditorPaste(e: ClipboardEvent) {
|
async function onEditorPaste(payload: {
|
||||||
const ta = editorTextareaRef.value;
|
event: ClipboardEvent;
|
||||||
if (!ta) return;
|
value: string;
|
||||||
|
from: number;
|
||||||
|
to: number;
|
||||||
|
}) {
|
||||||
await processPaste(
|
await processPaste(
|
||||||
e,
|
payload.event,
|
||||||
props.modelValue,
|
payload.value,
|
||||||
ta.selectionStart,
|
payload.from,
|
||||||
ta.selectionEnd,
|
payload.to,
|
||||||
(newValue, cursorPos) => {
|
(newValue, cursorPos) => {
|
||||||
applyFullEditorValue(newValue);
|
const insertedText = newValue.slice(payload.from, cursorPos);
|
||||||
void nextTick(() => {
|
editorCodeRef.value?.replaceRange(insertedText, payload.from, payload.to);
|
||||||
const ta = editorTextareaRef.value;
|
|
||||||
if (ta) {
|
|
||||||
ta.selectionStart = ta.selectionEnd = cursorPos;
|
|
||||||
ta.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onBlockPaste(e: ClipboardEvent) {
|
async function onBlockPaste(payload: {
|
||||||
const ta = blockTextareaRef.value;
|
event: ClipboardEvent;
|
||||||
if (!ta) return;
|
value: string;
|
||||||
|
from: number;
|
||||||
|
to: number;
|
||||||
|
}) {
|
||||||
await processPaste(
|
await processPaste(
|
||||||
e,
|
payload.event,
|
||||||
editingSource.value,
|
payload.value,
|
||||||
ta.selectionStart,
|
payload.from,
|
||||||
ta.selectionEnd,
|
payload.to,
|
||||||
(newValue, cursorPos) => {
|
(newValue, cursorPos) => {
|
||||||
editingSource.value = newValue;
|
const insertedText = newValue.slice(payload.from, cursorPos);
|
||||||
void nextTick(() => {
|
blockCodeRef.value?.replaceRange(insertedText, payload.from, payload.to);
|
||||||
const ta = blockTextareaRef.value;
|
|
||||||
if (ta) {
|
|
||||||
ta.selectionStart = ta.selectionEnd = cursorPos;
|
|
||||||
ta.focus();
|
|
||||||
scheduleTextareaResize(ta);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -997,19 +882,7 @@ function scrollToLine(lineNumber: number) {
|
|||||||
if (!scrollRef.value) return;
|
if (!scrollRef.value) return;
|
||||||
|
|
||||||
if (activeDocumentMode.value === "edit") {
|
if (activeDocumentMode.value === "edit") {
|
||||||
const ta = editorTextareaRef.value;
|
editorCodeRef.value?.scrollToLine(lineNumber);
|
||||||
if (ta) {
|
|
||||||
const lineHeight = 24;
|
|
||||||
const targetScroll = (lineNumber - 1) * lineHeight;
|
|
||||||
ta.scrollTop = Math.max(0, targetScroll - 80);
|
|
||||||
ta.focus();
|
|
||||||
const lines = ta.value.split("\n");
|
|
||||||
let pos = 0;
|
|
||||||
for (let i = 0; i < Math.min(lineNumber - 1, lines.length); i++) {
|
|
||||||
pos += lines[i].length + 1;
|
|
||||||
}
|
|
||||||
ta.setSelectionRange(pos, pos);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const previewEl = scrollRef.value.querySelector(".markdown-preview") as HTMLElement;
|
const previewEl = scrollRef.value.querySelector(".markdown-preview") as HTMLElement;
|
||||||
if (previewEl) {
|
if (previewEl) {
|
||||||
@@ -1052,18 +925,14 @@ defineExpose({
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<!-- Editor mode -->
|
<!-- Editor mode -->
|
||||||
<textarea
|
<MarkdownCodeEditor
|
||||||
v-if="activeDocumentMode === 'edit'"
|
v-if="activeDocumentMode === 'edit'"
|
||||||
ref="editorTextareaRef"
|
ref="editorCodeRef"
|
||||||
:value="props.modelValue"
|
:model-value="props.modelValue"
|
||||||
:placeholder="props.placeholder"
|
:placeholder="props.placeholder"
|
||||||
class="
|
min-height="60vh"
|
||||||
markdown-source-editor
|
class="markdown-source-editor w-full font-serif leading-relaxed"
|
||||||
w-full min-h-[60vh] resize-none overflow-hidden bg-transparent outline-none
|
@update:model-value="applyFullEditorValue"
|
||||||
font-serif leading-relaxed
|
|
||||||
"
|
|
||||||
@input="onEditorInput"
|
|
||||||
@keydown="onEditorKeydown"
|
|
||||||
@paste="onEditorPaste"
|
@paste="onEditorPaste"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -1072,7 +941,7 @@ defineExpose({
|
|||||||
<div v-if="renderError" class="text-red-500 text-sm">
|
<div v-if="renderError" class="text-red-500 text-sm">
|
||||||
⚠ Markdown 解析错误
|
⚠ Markdown 解析错误
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="markdown-preview py-2" v-html="renderedHtml" />
|
<div v-else class="markdown-preview" v-html="renderedHtml" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mixed mode -->
|
<!-- Mixed mode -->
|
||||||
@@ -1080,15 +949,16 @@ defineExpose({
|
|||||||
<div v-if="renderError" class="text-red-500 text-sm">
|
<div v-if="renderError" class="text-red-500 text-sm">
|
||||||
⚠ Markdown 解析错误
|
⚠ Markdown 解析错误
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
<MarkdownCodeEditor
|
||||||
v-else-if="isEditingEmptyDocument"
|
v-else-if="isEditingEmptyDocument"
|
||||||
:ref="setBlockTextareaRef"
|
ref="blockCodeRef"
|
||||||
:value="editingSource"
|
:model-value="editingSource"
|
||||||
:placeholder="props.placeholder"
|
:placeholder="props.placeholder"
|
||||||
rows="1"
|
autofocus
|
||||||
|
min-height="12rem"
|
||||||
class="markdown-block-editor"
|
class="markdown-block-editor"
|
||||||
@input="onBlockInput"
|
@update:model-value="editingSource = $event"
|
||||||
@keydown="onBlockKeydown"
|
@escape="cancelBlockEdit"
|
||||||
@blur="finalizeBlockEdit"
|
@blur="finalizeBlockEdit"
|
||||||
@paste="onBlockPaste"
|
@paste="onBlockPaste"
|
||||||
/>
|
/>
|
||||||
@@ -1110,14 +980,17 @@ defineExpose({
|
|||||||
class="markdown-block"
|
class="markdown-block"
|
||||||
:class="{ 'markdown-block-editing': editingBlockId === block.id }"
|
:class="{ 'markdown-block-editing': editingBlockId === block.id }"
|
||||||
>
|
>
|
||||||
<textarea
|
<MarkdownCodeEditor
|
||||||
v-if="editingBlockId === block.id"
|
v-if="editingBlockId === block.id"
|
||||||
:ref="setBlockTextareaRef"
|
ref="blockCodeRef"
|
||||||
:value="editingSource"
|
:model-value="editingSource"
|
||||||
rows="1"
|
autofocus
|
||||||
class="markdown-block-editor"
|
class="markdown-block-editor"
|
||||||
@input="onBlockInput"
|
:class="{
|
||||||
@keydown="onBlockKeydown"
|
'markdown-block-editor-soft-wrap': block.type === 'paragraph',
|
||||||
|
}"
|
||||||
|
@update:model-value="editingSource = $event"
|
||||||
|
@escape="cancelBlockEdit"
|
||||||
@blur="finalizeBlockEdit"
|
@blur="finalizeBlockEdit"
|
||||||
@paste="onBlockPaste"
|
@paste="onBlockPaste"
|
||||||
/>
|
/>
|
||||||
@@ -1157,14 +1030,9 @@ defineExpose({
|
|||||||
|
|
||||||
.markdown-source-editor {
|
.markdown-source-editor {
|
||||||
display: block;
|
display: block;
|
||||||
height: auto;
|
|
||||||
color: var(--app-text);
|
color: var(--app-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-source-editor::placeholder {
|
|
||||||
color: var(--app-subtle);
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-mixed > .markdown-block:first-child .markdown-block-preview > *:first-child {
|
.markdown-mixed > .markdown-block:first-child .markdown-block-preview > *:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
@@ -1209,10 +1077,30 @@ defineExpose({
|
|||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
margin: 0.75em 0;
|
margin: 0.75em 0;
|
||||||
}
|
}
|
||||||
|
.markdown-preview pre.shiki {
|
||||||
|
position: relative;
|
||||||
|
background-color: var(--code-background) !important;
|
||||||
|
color: var(--code-foreground);
|
||||||
|
border: 1px solid var(--code-border);
|
||||||
|
}
|
||||||
|
.markdown-preview pre.shiki[data-language]::before {
|
||||||
|
content: attr(data-language);
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.65rem;
|
||||||
|
color: var(--app-muted);
|
||||||
|
font-family: "Geist", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1;
|
||||||
|
text-transform: lowercase;
|
||||||
|
}
|
||||||
|
.markdown-preview pre.shiki span {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
.markdown-preview pre code {
|
.markdown-preview pre code {
|
||||||
background: none;
|
background: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: var(--app-text);
|
color: var(--code-foreground);
|
||||||
font-size: 0.875em;
|
font-size: 0.875em;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
word-break: normal;
|
word-break: normal;
|
||||||
@@ -1315,20 +1203,28 @@ defineExpose({
|
|||||||
.markdown-block-editor {
|
.markdown-block-editor {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: calc((1em * 1.6) + 1.5rem + 2px);
|
min-height: 1.625em;
|
||||||
resize: none;
|
resize: none;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
border: 1px solid var(--app-accent-muted);
|
border: 0;
|
||||||
border-radius: 0.375rem;
|
border-radius: 0;
|
||||||
background-color: var(--app-surface);
|
background: transparent;
|
||||||
color: var(--app-text);
|
color: var(--app-text);
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
font: inherit;
|
||||||
font-size: 0.92em;
|
line-height: inherit;
|
||||||
line-height: 1.6;
|
margin: 0;
|
||||||
margin: 0.375rem 0;
|
padding: 0;
|
||||||
padding: 0.75rem 0.875rem;
|
|
||||||
outline: none;
|
outline: none;
|
||||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--app-accent) 12%, transparent);
|
box-shadow: none;
|
||||||
|
caret-color: var(--app-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-block-editor .cm-tooltip {
|
||||||
|
font-size: calc(0.875rem * var(--editor-zoom, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-block-editor-soft-wrap {
|
||||||
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-block-editor::placeholder,
|
.markdown-block-editor::placeholder,
|
||||||
|
|||||||
@@ -122,14 +122,45 @@ const documentModeOptions: Array<{
|
|||||||
const themeOptions: Array<{
|
const themeOptions: Array<{
|
||||||
value: AppTheme;
|
value: AppTheme;
|
||||||
label: string;
|
label: string;
|
||||||
swatch: string;
|
description: string;
|
||||||
|
swatches: [string, string, string];
|
||||||
}> = [
|
}> = [
|
||||||
{ value: "system", label: "跟随系统", swatch: "linear-gradient(135deg, #ffffff 0 50%, #17191d 50% 100%)" },
|
{
|
||||||
{ value: "warm", label: "柔和暖色", swatch: "#bf6a3b" },
|
value: "system",
|
||||||
{ value: "white", label: "白色", swatch: "#2563eb" },
|
label: "跟随系统",
|
||||||
{ value: "black", label: "黑色", swatch: "#8ab4ff" },
|
description: "随操作系统在浅色与深色之间切换",
|
||||||
{ value: "gruvbox", label: "Gruvbox", swatch: "#fe8019" },
|
swatches: ["#ffffff", "#101113", "#7c9cff"],
|
||||||
{ value: "gray", label: "灰色", swatch: "#4f6f93" },
|
},
|
||||||
|
{
|
||||||
|
value: "warm",
|
||||||
|
label: "柔和暖色",
|
||||||
|
description: "暖白纸张、陶土强调色",
|
||||||
|
swatches: ["#faf9f6", "#bf6a3b", "#7a55d9"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "white",
|
||||||
|
label: "明亮白色",
|
||||||
|
description: "纯白背景、清晰蓝色强调",
|
||||||
|
swatches: ["#ffffff", "#2563eb", "#8250df"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "gray",
|
||||||
|
label: "中性灰色",
|
||||||
|
description: "低饱和界面、沉静蓝灰强调",
|
||||||
|
swatches: ["#f3f4f6", "#4f6f93", "#7c5c99"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "black",
|
||||||
|
label: "深色",
|
||||||
|
description: "近黑背景、冷调高对比代码色",
|
||||||
|
swatches: ["#101113", "#8ab4ff", "#c58af9"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "gruvbox",
|
||||||
|
label: "Gruvbox",
|
||||||
|
description: "复古深色、暖橙与青绿色代码色",
|
||||||
|
swatches: ["#282828", "#fe8019", "#8ec07c"],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function loadBooleanPref(key: string, fallback: boolean) {
|
function loadBooleanPref(key: string, fallback: boolean) {
|
||||||
@@ -239,15 +270,6 @@ function onDefaultDocumentModeChange(event: Event) {
|
|||||||
emit("updateDefaultDocumentMode", mode);
|
emit("updateDefaultDocumentMode", mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onThemeChange(event: Event) {
|
|
||||||
const theme = (event.target as HTMLSelectElement).value as AppTheme;
|
|
||||||
emit("updateAppTheme", theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetTheme() {
|
|
||||||
emit("updateAppTheme", "warm");
|
|
||||||
}
|
|
||||||
|
|
||||||
function setSyncStatus(message: string, isError = false) {
|
function setSyncStatus(message: string, isError = false) {
|
||||||
if (isError) {
|
if (isError) {
|
||||||
syncError.value = message;
|
syncError.value = message;
|
||||||
@@ -690,51 +712,35 @@ onBeforeUnmount(() => {
|
|||||||
<section class="settings-about-card" aria-label="外观设置">
|
<section class="settings-about-card" aria-label="外观设置">
|
||||||
<div class="settings-row settings-row-top">
|
<div class="settings-row settings-row-top">
|
||||||
<div class="settings-copy">
|
<div class="settings-copy">
|
||||||
<h3>基础颜色</h3>
|
<h3>外观主题</h3>
|
||||||
<p>设置 {{ APP_NAME }} 的基础颜色。</p>
|
<p>主题同时应用于界面、Markdown 预览和代码编辑。</p>
|
||||||
</div>
|
</div>
|
||||||
<select
|
|
||||||
class="settings-select"
|
|
||||||
aria-label="基础颜色"
|
|
||||||
:value="appTheme"
|
|
||||||
@change="onThemeChange"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="option in themeOptions"
|
|
||||||
:key="option.value"
|
|
||||||
:value="option.value"
|
|
||||||
>
|
|
||||||
{{ option.label }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings-row">
|
<div class="theme-option-grid" role="radiogroup" aria-label="外观主题">
|
||||||
<div class="settings-copy">
|
|
||||||
<h3>主题色</h3>
|
|
||||||
<p>为 {{ APP_NAME }} 选择一个主题色。主题色将影响选中、链接等元素的颜色。</p>
|
|
||||||
</div>
|
|
||||||
<div class="theme-swatch-group" aria-label="主题色">
|
|
||||||
<button
|
<button
|
||||||
class="theme-reset-btn"
|
v-for="option in themeOptions"
|
||||||
type="button"
|
|
||||||
title="重置主题色"
|
|
||||||
@click="resetTheme"
|
|
||||||
>
|
|
||||||
<i class="ri-reset-left-line text-lg"></i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-for="option in themeOptions.filter((item) => item.value !== 'system')"
|
|
||||||
:key="option.value"
|
:key="option.value"
|
||||||
class="theme-swatch"
|
class="theme-option"
|
||||||
:class="{ 'theme-swatch-active': appTheme === option.value }"
|
:class="{ 'theme-option-active': appTheme === option.value }"
|
||||||
type="button"
|
type="button"
|
||||||
:title="option.label"
|
role="radio"
|
||||||
:aria-label="option.label"
|
:aria-checked="appTheme === option.value"
|
||||||
:style="{ background: option.swatch }"
|
|
||||||
@click="emit('updateAppTheme', option.value)"
|
@click="emit('updateAppTheme', option.value)"
|
||||||
></button>
|
>
|
||||||
</div>
|
<span class="theme-option-preview" aria-hidden="true">
|
||||||
|
<span
|
||||||
|
v-for="(swatch, index) in option.swatches"
|
||||||
|
:key="swatch"
|
||||||
|
:style="{ background: swatch, flex: index === 0 ? 2 : 1 }"
|
||||||
|
></span>
|
||||||
|
</span>
|
||||||
|
<span class="theme-option-copy">
|
||||||
|
<strong>{{ option.label }}</strong>
|
||||||
|
<small>{{ option.description }}</small>
|
||||||
|
</span>
|
||||||
|
<i v-if="appTheme === option.value" class="ri-check-line"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
@@ -1165,46 +1171,70 @@ onBeforeUnmount(() => {
|
|||||||
color: #dc2626;
|
color: #dc2626;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-swatch-group {
|
.theme-option-grid {
|
||||||
display: inline-flex;
|
display: grid;
|
||||||
align-items: center;
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
padding: 4px 20px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-reset-btn,
|
.theme-option {
|
||||||
.theme-swatch {
|
display: grid;
|
||||||
display: inline-flex;
|
grid-template-columns: 48px minmax(0, 1fr) 18px;
|
||||||
|
gap: 11px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
min-height: 64px;
|
||||||
border: none;
|
padding: 10px;
|
||||||
cursor: pointer;
|
border: 1px solid var(--app-border-strong);
|
||||||
}
|
border-radius: 7px;
|
||||||
|
background: var(--app-surface);
|
||||||
.theme-reset-btn {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius: 6px;
|
|
||||||
background: transparent;
|
|
||||||
color: var(--app-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-reset-btn:hover {
|
|
||||||
background: var(--app-hover);
|
|
||||||
color: var(--app-text);
|
color: var(--app-text);
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.15s ease, background-color 0.15s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-swatch {
|
.theme-option:hover {
|
||||||
width: 22px;
|
background: var(--app-surface-muted);
|
||||||
height: 22px;
|
|
||||||
border-radius: 999px;
|
|
||||||
box-shadow:
|
|
||||||
0 0 0 1px color-mix(in srgb, var(--app-text) 18%, transparent),
|
|
||||||
0 2px 5px color-mix(in srgb, var(--app-shadow) 70%, transparent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-swatch-active {
|
.theme-option-active {
|
||||||
outline: 2px solid var(--app-accent);
|
border-color: var(--app-accent);
|
||||||
outline-offset: 3px;
|
background: var(--app-active-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-option-preview {
|
||||||
|
display: flex;
|
||||||
|
width: 48px;
|
||||||
|
height: 38px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid color-mix(in srgb, var(--app-text) 18%, transparent);
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 2px 5px color-mix(in srgb, var(--app-shadow) 55%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-option-copy {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-option-copy strong {
|
||||||
|
color: var(--app-text-strong);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-option-copy small {
|
||||||
|
color: var(--app-muted);
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-option > i {
|
||||||
|
color: var(--app-accent);
|
||||||
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-empty-page {
|
.settings-empty-page {
|
||||||
|
|||||||
@@ -44,6 +44,21 @@
|
|||||||
--app-overlay: rgba(56, 52, 46, 0.22);
|
--app-overlay: rgba(56, 52, 46, 0.22);
|
||||||
--app-toast-bg: rgba(56, 52, 46, 0.8);
|
--app-toast-bg: rgba(56, 52, 46, 0.8);
|
||||||
--app-shadow: rgba(56, 52, 46, 0.16);
|
--app-shadow: rgba(56, 52, 46, 0.16);
|
||||||
|
--code-background: #f4f1ea;
|
||||||
|
--code-foreground: #38342e;
|
||||||
|
--code-border: #e0dbcf;
|
||||||
|
--code-token-comment: #8c877d;
|
||||||
|
--code-token-constant: #8c4f2b;
|
||||||
|
--code-token-keyword: #7651a8;
|
||||||
|
--code-token-parameter: #9f4f28;
|
||||||
|
--code-token-function: #315f78;
|
||||||
|
--code-token-string: #6f641f;
|
||||||
|
--code-token-string-expression: #6f641f;
|
||||||
|
--code-token-punctuation: #6e685f;
|
||||||
|
--code-token-link: #6342bd;
|
||||||
|
--code-token-inserted: #3d7a4d;
|
||||||
|
--code-token-deleted: #b34242;
|
||||||
|
--code-token-changed: #9a691f;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root[data-theme="white"] {
|
:root[data-theme="white"] {
|
||||||
@@ -74,6 +89,21 @@
|
|||||||
--app-overlay: rgba(17, 24, 39, 0.2);
|
--app-overlay: rgba(17, 24, 39, 0.2);
|
||||||
--app-toast-bg: rgba(17, 24, 39, 0.82);
|
--app-toast-bg: rgba(17, 24, 39, 0.82);
|
||||||
--app-shadow: rgba(17, 24, 39, 0.15);
|
--app-shadow: rgba(17, 24, 39, 0.15);
|
||||||
|
--code-background: #f4f6f8;
|
||||||
|
--code-foreground: #24292f;
|
||||||
|
--code-border: #d7dbe0;
|
||||||
|
--code-token-comment: #6e7781;
|
||||||
|
--code-token-constant: #953800;
|
||||||
|
--code-token-keyword: #8250df;
|
||||||
|
--code-token-parameter: #953800;
|
||||||
|
--code-token-function: #0550ae;
|
||||||
|
--code-token-string: #0a3069;
|
||||||
|
--code-token-string-expression: #0a3069;
|
||||||
|
--code-token-punctuation: #57606a;
|
||||||
|
--code-token-link: #0969da;
|
||||||
|
--code-token-inserted: #1a7f37;
|
||||||
|
--code-token-deleted: #cf222e;
|
||||||
|
--code-token-changed: #9a6700;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root[data-theme="gray"] {
|
:root[data-theme="gray"] {
|
||||||
@@ -104,6 +134,21 @@
|
|||||||
--app-overlay: rgba(31, 35, 40, 0.24);
|
--app-overlay: rgba(31, 35, 40, 0.24);
|
||||||
--app-toast-bg: rgba(31, 35, 40, 0.82);
|
--app-toast-bg: rgba(31, 35, 40, 0.82);
|
||||||
--app-shadow: rgba(31, 35, 40, 0.14);
|
--app-shadow: rgba(31, 35, 40, 0.14);
|
||||||
|
--code-background: #eceff2;
|
||||||
|
--code-foreground: #2f3337;
|
||||||
|
--code-border: #cfd5dc;
|
||||||
|
--code-token-comment: #737a82;
|
||||||
|
--code-token-constant: #8b572a;
|
||||||
|
--code-token-keyword: #7c5c99;
|
||||||
|
--code-token-parameter: #8b572a;
|
||||||
|
--code-token-function: #3b668c;
|
||||||
|
--code-token-string: #537238;
|
||||||
|
--code-token-string-expression: #537238;
|
||||||
|
--code-token-punctuation: #656c74;
|
||||||
|
--code-token-link: #3b668c;
|
||||||
|
--code-token-inserted: #4e7a52;
|
||||||
|
--code-token-deleted: #a94a4a;
|
||||||
|
--code-token-changed: #8b6b2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root[data-theme="black"] {
|
:root[data-theme="black"] {
|
||||||
@@ -134,6 +179,21 @@
|
|||||||
--app-overlay: rgba(0, 0, 0, 0.48);
|
--app-overlay: rgba(0, 0, 0, 0.48);
|
||||||
--app-toast-bg: rgba(0, 0, 0, 0.82);
|
--app-toast-bg: rgba(0, 0, 0, 0.82);
|
||||||
--app-shadow: rgba(0, 0, 0, 0.36);
|
--app-shadow: rgba(0, 0, 0, 0.36);
|
||||||
|
--code-background: #1b1e23;
|
||||||
|
--code-foreground: #e7e9ed;
|
||||||
|
--code-border: #383d45;
|
||||||
|
--code-token-comment: #8b949e;
|
||||||
|
--code-token-constant: #ffa657;
|
||||||
|
--code-token-keyword: #ff7b72;
|
||||||
|
--code-token-parameter: #ffa657;
|
||||||
|
--code-token-function: #d2a8ff;
|
||||||
|
--code-token-string: #a5d6ff;
|
||||||
|
--code-token-string-expression: #a5d6ff;
|
||||||
|
--code-token-punctuation: #b1bac4;
|
||||||
|
--code-token-link: #79c0ff;
|
||||||
|
--code-token-inserted: #7ee787;
|
||||||
|
--code-token-deleted: #ff7b72;
|
||||||
|
--code-token-changed: #e3b341;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root[data-theme="gruvbox"] {
|
:root[data-theme="gruvbox"] {
|
||||||
@@ -164,6 +224,21 @@
|
|||||||
--app-overlay: rgba(29, 32, 33, 0.52);
|
--app-overlay: rgba(29, 32, 33, 0.52);
|
||||||
--app-toast-bg: rgba(29, 32, 33, 0.9);
|
--app-toast-bg: rgba(29, 32, 33, 0.9);
|
||||||
--app-shadow: rgba(0, 0, 0, 0.38);
|
--app-shadow: rgba(0, 0, 0, 0.38);
|
||||||
|
--code-background: #1d2021;
|
||||||
|
--code-foreground: #ebdbb2;
|
||||||
|
--code-border: #504945;
|
||||||
|
--code-token-comment: #928374;
|
||||||
|
--code-token-constant: #d3869b;
|
||||||
|
--code-token-keyword: #fb4934;
|
||||||
|
--code-token-parameter: #fe8019;
|
||||||
|
--code-token-function: #fabd2f;
|
||||||
|
--code-token-string: #b8bb26;
|
||||||
|
--code-token-string-expression: #b8bb26;
|
||||||
|
--code-token-punctuation: #bdae93;
|
||||||
|
--code-token-link: #83a598;
|
||||||
|
--code-token-inserted: #b8bb26;
|
||||||
|
--code-token-deleted: #fb4934;
|
||||||
|
--code-token-changed: #fabd2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
@@ -195,6 +270,21 @@
|
|||||||
--app-overlay: rgba(0, 0, 0, 0.48);
|
--app-overlay: rgba(0, 0, 0, 0.48);
|
||||||
--app-toast-bg: rgba(0, 0, 0, 0.82);
|
--app-toast-bg: rgba(0, 0, 0, 0.82);
|
||||||
--app-shadow: rgba(0, 0, 0, 0.36);
|
--app-shadow: rgba(0, 0, 0, 0.36);
|
||||||
|
--code-background: #1b1e23;
|
||||||
|
--code-foreground: #e7e9ed;
|
||||||
|
--code-border: #383d45;
|
||||||
|
--code-token-comment: #8b949e;
|
||||||
|
--code-token-constant: #ffa657;
|
||||||
|
--code-token-keyword: #ff7b72;
|
||||||
|
--code-token-parameter: #ffa657;
|
||||||
|
--code-token-function: #d2a8ff;
|
||||||
|
--code-token-string: #a5d6ff;
|
||||||
|
--code-token-string-expression: #a5d6ff;
|
||||||
|
--code-token-punctuation: #b1bac4;
|
||||||
|
--code-token-link: #79c0ff;
|
||||||
|
--code-token-inserted: #7ee787;
|
||||||
|
--code-token-deleted: #ff7b72;
|
||||||
|
--code-token-changed: #e3b341;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,4 +464,4 @@ body {
|
|||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
@apply font-sans;
|
@apply font-sans;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user