@jenkin-a/jeditor 1.0.0 → 1.0.3
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/Demo/cdn.html +24 -0
- package/Demo/img.png +0 -0
- package/README.en.md +388 -0
- package/README.md +390 -0
- package/dist/jeditor.css +1 -1
- package/dist/jeditor.es.js +7851 -366
- package/dist/jeditor.iife.js +366 -0
- package/dist/jeditor.umd.js +366 -5
- package/package.json +25 -9
package/README.md
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# JEditor
|
|
2
|
+
|
|
3
|
+
Language: [English](README.en.md) | 中文
|
|
4
|
+
|
|
5
|
+
当前版本:`v1.0.2`
|
|
6
|
+
|
|
7
|
+
JEditor 是一个面向中文内容创作、静态片段编写与 Source HTML 编辑的 Web 富文本编辑器。它最初基于 Tiptap 构建,但当前版本已经不再只是一个“工具栏 + Tiptap 的简单封装”,而是在此基础上逐步演进为一个支持:
|
|
8
|
+
|
|
9
|
+
- 可视化文档编辑
|
|
10
|
+
- Source HTML 编辑
|
|
11
|
+
- HTML 保活与回写
|
|
12
|
+
- 浏览器 CDN 直连
|
|
13
|
+
|
|
14
|
+
的混合编辑架构。
|
|
15
|
+
|
|
16
|
+

|
|
17
|
+
|
|
18
|
+
## 项目目标
|
|
19
|
+
|
|
20
|
+
JEditor 的目标不是单纯复刻一个传统富文本编辑器,而是解决一个更难的问题:
|
|
21
|
+
|
|
22
|
+
`让用户既能像写文档一样编辑内容,又能尽可能保留和操作原始 HTML。`
|
|
23
|
+
|
|
24
|
+
这也是 JEditor 与常规富文本工具的核心差异:
|
|
25
|
+
|
|
26
|
+
- 普通编辑器更强调“结构化内容”
|
|
27
|
+
- JEditor 同时强调“结构化编辑体验”和“HTML 存活能力”
|
|
28
|
+
|
|
29
|
+
## 当前能力
|
|
30
|
+
|
|
31
|
+
当前版本已经具备以下核心能力:
|
|
32
|
+
|
|
33
|
+
- 双工具栏富文本编辑体验
|
|
34
|
+
- 原生 HTML 容器启动
|
|
35
|
+
- `textarea` 启动与自动回写
|
|
36
|
+
- ESM / UMD / IIFE 三种构建产物
|
|
37
|
+
- CDN 直接接入,无需 npm
|
|
38
|
+
- Source 按钮切换源码编辑
|
|
39
|
+
- 左侧源码、右侧高保真预览
|
|
40
|
+
- 完整 HTML 文档保留
|
|
41
|
+
- fragment HTML 预处理与回写
|
|
42
|
+
- Raw HTML Block 占位与保活
|
|
43
|
+
- 基础富文本能力:
|
|
44
|
+
- 撤销 / 重做
|
|
45
|
+
- 格式刷
|
|
46
|
+
- 清除格式
|
|
47
|
+
- 标题 / 正文
|
|
48
|
+
- 字体 / 字号
|
|
49
|
+
- 粗体 / 斜体 / 下划线 / 删除线
|
|
50
|
+
- 颜色
|
|
51
|
+
- 引用
|
|
52
|
+
- inline code
|
|
53
|
+
- code block
|
|
54
|
+
- 列表
|
|
55
|
+
- 链接
|
|
56
|
+
- 表格
|
|
57
|
+
- 图片
|
|
58
|
+
- Callout
|
|
59
|
+
- 全屏
|
|
60
|
+
|
|
61
|
+
## 架构概览
|
|
62
|
+
|
|
63
|
+
JEditor 当前采用的是一套“Source HTML 为唯一真相”的混合架构:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
Source HTML
|
|
67
|
+
↓
|
|
68
|
+
Parser / Preprocess
|
|
69
|
+
↓
|
|
70
|
+
Projection
|
|
71
|
+
↓
|
|
72
|
+
Visual Editor (Tiptap)
|
|
73
|
+
↓
|
|
74
|
+
restoreRawHTML()
|
|
75
|
+
↓
|
|
76
|
+
Output HTML
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
可以把它理解为三层:
|
|
80
|
+
|
|
81
|
+
1. Source Layer
|
|
82
|
+
- 保存原始 HTML
|
|
83
|
+
- 完整文档模式下保持源码权威
|
|
84
|
+
- 负责高保真预览
|
|
85
|
+
|
|
86
|
+
2. Projection Layer
|
|
87
|
+
- 在 `setContent` 前预处理 HTML
|
|
88
|
+
- 把可识别节点交给 schema
|
|
89
|
+
- 把未知节点包成 `RawHtmlIsland`
|
|
90
|
+
|
|
91
|
+
3. Visual Layer
|
|
92
|
+
- 由 Tiptap 承担结构化编辑能力
|
|
93
|
+
- 工具栏、插件、命令、选区逻辑都工作在这一层
|
|
94
|
+
|
|
95
|
+
## 三种编辑状态
|
|
96
|
+
|
|
97
|
+
### 1. Visual Mode
|
|
98
|
+
|
|
99
|
+
默认模式。适合写正文、排版、插入表格、Callout、图片、代码块等结构化内容。
|
|
100
|
+
|
|
101
|
+
这一层主要依赖 Tiptap 提供:
|
|
102
|
+
|
|
103
|
+
- 选区管理
|
|
104
|
+
- 命令链
|
|
105
|
+
- schema
|
|
106
|
+
- 历史记录
|
|
107
|
+
- 节点与 mark 扩展
|
|
108
|
+
|
|
109
|
+
### 2. Source Mode
|
|
110
|
+
|
|
111
|
+
点击右侧 `Source` 按钮进入。
|
|
112
|
+
|
|
113
|
+
当前实现为:
|
|
114
|
+
|
|
115
|
+
- 左侧:源码编辑区 `textarea`
|
|
116
|
+
- 右侧:高保真 iframe 预览
|
|
117
|
+
|
|
118
|
+
当内容是完整 HTML 文档时,JEditor 不会强制把它重新喂回可视化编辑器,从而避免:
|
|
119
|
+
|
|
120
|
+
- `<!DOCTYPE html>` 丢失
|
|
121
|
+
- `<head>` 丢失
|
|
122
|
+
- `<style>` 丢失
|
|
123
|
+
- `<script>` 丢失
|
|
124
|
+
- 文档结构被 Tiptap 规范化
|
|
125
|
+
|
|
126
|
+
### 3. Hybrid / Preservation Flow
|
|
127
|
+
|
|
128
|
+
这是 JEditor 目前最关键的一层。
|
|
129
|
+
|
|
130
|
+
对于 fragment HTML,JEditor 会在 `setContent()` 之前先做预处理:
|
|
131
|
+
|
|
132
|
+
- 支持的节点:正常解析进入编辑器
|
|
133
|
+
- 不支持的节点:包成 `raw-html` 占位节点
|
|
134
|
+
|
|
135
|
+
导出时再通过 `restoreRawHTML()` 还原成原始 HTML。
|
|
136
|
+
|
|
137
|
+
这一步的目标很明确:
|
|
138
|
+
|
|
139
|
+
`未知 HTML 不一定能编辑,但不能被删除。`
|
|
140
|
+
|
|
141
|
+
## 目录结构
|
|
142
|
+
|
|
143
|
+
核心源码主要在 `src`:
|
|
144
|
+
|
|
145
|
+
- `src/jeditor.js`
|
|
146
|
+
- 编辑器总入口
|
|
147
|
+
- 负责模式切换、Source 控制、HTML 同步、初始化
|
|
148
|
+
|
|
149
|
+
- `src/editor/index.js`
|
|
150
|
+
- 创建 Tiptap Editor 实例
|
|
151
|
+
|
|
152
|
+
- `src/core/plugin-manager.js`
|
|
153
|
+
- 统一注册和管理插件
|
|
154
|
+
|
|
155
|
+
- `src/core/config.js`
|
|
156
|
+
- 默认配置与用户配置合并
|
|
157
|
+
|
|
158
|
+
- `src/core/html-preservation.js`
|
|
159
|
+
- `preprocessHTML`
|
|
160
|
+
- `restoreRawHTML`
|
|
161
|
+
- HTML 保活入口
|
|
162
|
+
|
|
163
|
+
- `src/toolbar/ui.js`
|
|
164
|
+
- 工具栏 DOM 生成、下拉菜单、状态同步
|
|
165
|
+
|
|
166
|
+
- `src/plugins`
|
|
167
|
+
- 所有工具栏能力与命令实现
|
|
168
|
+
|
|
169
|
+
- `src/extensions`
|
|
170
|
+
- 自定义 schema / mark / node 扩展
|
|
171
|
+
|
|
172
|
+
- `src/styles/editor.css`
|
|
173
|
+
- 编辑器内置样式
|
|
174
|
+
|
|
175
|
+
## HTML 保活机制
|
|
176
|
+
|
|
177
|
+
这一部分是 JEditor 当前最重要的架构能力。
|
|
178
|
+
|
|
179
|
+
### 1. preprocessHTML
|
|
180
|
+
|
|
181
|
+
在 `setContent()` 前执行。
|
|
182
|
+
|
|
183
|
+
职责:
|
|
184
|
+
|
|
185
|
+
- 遍历输入 HTML
|
|
186
|
+
- 判断节点是否属于当前 schema 可接受范围
|
|
187
|
+
- 对未知节点生成占位节点
|
|
188
|
+
|
|
189
|
+
例如:
|
|
190
|
+
|
|
191
|
+
```html
|
|
192
|
+
<section style="display:flex">
|
|
193
|
+
<article>...</article>
|
|
194
|
+
</section>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
如果当前 schema 不能完整表达这类结构,就不会直接丢弃,而是转换为:
|
|
198
|
+
|
|
199
|
+
```html
|
|
200
|
+
<raw-html data-raw-html="...encoded html..."></raw-html>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### 2. RawHtmlIsland
|
|
204
|
+
|
|
205
|
+
`RawHtmlIsland` 是一个不可拆的 block node。
|
|
206
|
+
|
|
207
|
+
它的职责不是编辑 HTML,而是“保留 HTML”。
|
|
208
|
+
|
|
209
|
+
在视觉上,它会显示为一个明确的块,而不是空节点,例如:
|
|
210
|
+
|
|
211
|
+
- Raw HTML Block
|
|
212
|
+
- HTML preserved
|
|
213
|
+
|
|
214
|
+
并支持:
|
|
215
|
+
|
|
216
|
+
- 双击跳转 Source
|
|
217
|
+
- 复制原始 HTML
|
|
218
|
+
- 删除该块
|
|
219
|
+
|
|
220
|
+
对应实现:
|
|
221
|
+
|
|
222
|
+
- `src/extensions/raw-html-island.js`
|
|
223
|
+
|
|
224
|
+
### 3. restoreRawHTML
|
|
225
|
+
|
|
226
|
+
在 `getHTML()` 时执行。
|
|
227
|
+
|
|
228
|
+
职责:
|
|
229
|
+
|
|
230
|
+
- 扫描编辑器输出
|
|
231
|
+
- 找到 `raw-html[data-raw-html]`
|
|
232
|
+
- 将其还原为保存的原始 `outerHTML`
|
|
233
|
+
|
|
234
|
+
因此即便某些节点在可视化层无法编辑,导出时仍然可以完整回到原始 HTML。
|
|
235
|
+
|
|
236
|
+
## Schema 扩展方向
|
|
237
|
+
|
|
238
|
+
当前已经开始接入第一阶段的 schema 扩展:
|
|
239
|
+
|
|
240
|
+
- `GenericDiv`
|
|
241
|
+
- `GlobalStyle`
|
|
242
|
+
- `RawHtmlIsland`
|
|
243
|
+
|
|
244
|
+
目标是逐步让 JEditor 支持更多“宽松但可保留”的 HTML 语法,而不是一开始就被严格 schema 过滤掉。
|
|
245
|
+
|
|
246
|
+
后续会继续推进:
|
|
247
|
+
|
|
248
|
+
- `GenericSpan`
|
|
249
|
+
- 更细粒度的 inline fallback
|
|
250
|
+
- 更完整的未知标签保留策略
|
|
251
|
+
|
|
252
|
+
## 插件体系
|
|
253
|
+
|
|
254
|
+
JEditor 使用插件驱动工具栏和命令。
|
|
255
|
+
|
|
256
|
+
每个插件通常包含以下能力:
|
|
257
|
+
|
|
258
|
+
- `name`
|
|
259
|
+
- `toolbar`
|
|
260
|
+
- `tiptapExtension`
|
|
261
|
+
- `command`
|
|
262
|
+
- `isActive`
|
|
263
|
+
- `renderPopover`
|
|
264
|
+
- `init / destroy`
|
|
265
|
+
|
|
266
|
+
这意味着:
|
|
267
|
+
|
|
268
|
+
- UI 与命令可以解耦
|
|
269
|
+
- 工具栏项可以按配置组合
|
|
270
|
+
- 扩展新按钮时无需改动编辑器核心
|
|
271
|
+
|
|
272
|
+
内置插件入口:
|
|
273
|
+
|
|
274
|
+
- `src/plugins/index.js`
|
|
275
|
+
|
|
276
|
+
插件管理器:
|
|
277
|
+
|
|
278
|
+
- `src/core/plugin-manager.js`
|
|
279
|
+
|
|
280
|
+
## 安装与开发
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
npm install
|
|
284
|
+
npm run dev
|
|
285
|
+
npm run build
|
|
286
|
+
npm run preview
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
构建产物:
|
|
290
|
+
|
|
291
|
+
- `dist/jeditor.es.js`
|
|
292
|
+
- `dist/jeditor.umd.js`
|
|
293
|
+
- `dist/jeditor.iife.js`
|
|
294
|
+
- `dist/jeditor.css`
|
|
295
|
+
|
|
296
|
+
## ESM 用法
|
|
297
|
+
|
|
298
|
+
```js
|
|
299
|
+
import '@jenkin-a/jeditor/dist/jeditor.css'
|
|
300
|
+
import { JEditor } from '@jenkin-a/jeditor'
|
|
301
|
+
|
|
302
|
+
const editor = JEditor.create('#editor', {
|
|
303
|
+
placeholder: '开始你的创作...',
|
|
304
|
+
})
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
也可以直接从 HTML 字符串创建:
|
|
308
|
+
|
|
309
|
+
```js
|
|
310
|
+
const editor = JEditor.fromHTML('#editor', '<h2>Hello</h2><p>World</p>')
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## CDN / 无 npm 用法
|
|
314
|
+
|
|
315
|
+
```html
|
|
316
|
+
<link rel="stylesheet" href="https://unpkg.com/@jenkin-a/jeditor/dist/jeditor.css">
|
|
317
|
+
<script src="https://unpkg.com/feather-icons"></script>
|
|
318
|
+
<script src="https://unpkg.com/@jenkin-a/jeditor/dist/jeditor.iife.js"></script>
|
|
319
|
+
|
|
320
|
+
<div id="editor">
|
|
321
|
+
<h2>你好,JEditor</h2>
|
|
322
|
+
<p>这里的原生 HTML 会在启动时被解析。</p>
|
|
323
|
+
</div>
|
|
324
|
+
|
|
325
|
+
<script>
|
|
326
|
+
const editor = window.JEditor.create('#editor', {
|
|
327
|
+
placeholder: '开始你的创作...',
|
|
328
|
+
})
|
|
329
|
+
</script>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
如果需要锁定版本:
|
|
333
|
+
|
|
334
|
+
```html
|
|
335
|
+
<script src="https://unpkg.com/@jenkin-a/jeditor@1.0.2/dist/jeditor.iife.js"></script>
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## API
|
|
339
|
+
|
|
340
|
+
```js
|
|
341
|
+
editor.getHTML()
|
|
342
|
+
editor.getJSON()
|
|
343
|
+
editor.getText()
|
|
344
|
+
editor.setContent('<p>Hello</p>')
|
|
345
|
+
editor.importHTML('<p>Hello</p>')
|
|
346
|
+
editor.focus()
|
|
347
|
+
editor.destroy()
|
|
348
|
+
editor.toggleSourceMode()
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## 适用场景
|
|
352
|
+
|
|
353
|
+
JEditor 适合以下场景:
|
|
354
|
+
|
|
355
|
+
- 企业知识库编辑器
|
|
356
|
+
- SOP / 公告 / 文档创作台
|
|
357
|
+
- 支持 Source HTML 的 CMS 编辑器
|
|
358
|
+
- 需要可视化 + HTML 混合编辑的后台系统
|
|
359
|
+
- 需要 CDN 直连接入的轻量编辑器
|
|
360
|
+
|
|
361
|
+
## 当前边界
|
|
362
|
+
|
|
363
|
+
虽然当前版本已经非常强,但它还在持续演进中。
|
|
364
|
+
|
|
365
|
+
目前仍存在一些边界:
|
|
366
|
+
|
|
367
|
+
- 部分 UI 细节仍需继续打磨
|
|
368
|
+
- 个别工具栏交互仍可继续优化
|
|
369
|
+
- 复杂未知 inline HTML 的保活能力还不是最终形态
|
|
370
|
+
- Source Mode 与 Hybrid Mode 还会继续深化
|
|
371
|
+
|
|
372
|
+
## 下一阶段方向
|
|
373
|
+
|
|
374
|
+
下一步的重点已经明确:
|
|
375
|
+
|
|
376
|
+
1. 扩展 schema
|
|
377
|
+
- `GenericSpan`
|
|
378
|
+
- 更宽松的 style / attr 保留
|
|
379
|
+
|
|
380
|
+
2. 完善 Raw HTML 保活
|
|
381
|
+
- 更细粒度 fallback
|
|
382
|
+
- 更好的导入 / 导出还原
|
|
383
|
+
|
|
384
|
+
3. 强化 Source / Visual 协同
|
|
385
|
+
- 更稳定的双向切换
|
|
386
|
+
- 更完整的 Hybrid 编辑体验
|
|
387
|
+
|
|
388
|
+
## License
|
|
389
|
+
|
|
390
|
+
MIT
|
package/dist/jeditor.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
:root{--je-blue:#0052d9;--je-blue-active:#e6f0ff;--je-divider:#e0e0e0;--je-hover:#eee;--je-radius:4px}.je-container{-webkit-font-smoothing:antialiased;background:#fff;border:1px solid #e6e6e6;border-radius:4px;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,PingFang SC,Microsoft YaHei,sans-serif;display:flex;overflow:hidden}.je-toolbar-row{flex-shrink:0;align-items:center;gap:1px;padding:0 12px;display:flex}.je-toolbar-row--primary{background:#fff;height:44px}.je-toolbar-row--secondary{background:#f3f4f6;border-radius:6px;height:42px;margin:0 8px 4px}.je-spacer{flex:1}.v-divider{background-color:var(--je-divider);flex-shrink:0;width:1px;height:16px;margin:0 6px}.tool-btn,.tool-btn-text,.tool-btn-arrow-down{border-radius:var(--je-radius);color:#444;cursor:pointer;background:0 0;border:none;flex-shrink:0;justify-content:center;align-items:center;gap:2px;line-height:1;transition:background-color .1s;display:inline-flex}.tool-btn{width:28px;height:28px;padding:0;font-size:13px}.tool-btn:hover{background-color:var(--je-hover)}.tool-btn-text{white-space:nowrap;height:28px;padding:0 6px;font-size:13px}.tool-btn-text:hover{background-color:var(--je-hover)}.tool-btn-arrow-down{color:#999;width:16px;height:28px;padding:0}.tool-btn-arrow-down:hover{background-color:var(--je-hover)}.tool-btn svg,.tool-btn-text svg,.tool-btn-arrow-down svg{flex-shrink:0;width:20px;height:20px}.tool-btn-text svg,.tool-btn-arrow-down svg{width:14px;height:14px}.je-color-group{flex-shrink:0;align-items:center;display:inline-flex}.je-color-inner{flex-direction:column;align-items:center;gap:1px;line-height:1;display:flex}.je-color-char{font-size:14px;font-weight:600}.je-color-bar{border-radius:1px;width:16px;height:3px}.tool-btn.is-active{background-color:var(--je-blue-active);color:var(--je-blue)}.tool-btn.is-disabled{opacity:.35;pointer-events:none;cursor:default}.je-editor-area{flex-direction:column;flex:1;display:flex}.je-editor-area .tiptap{cursor:text;color:#333;outline:none;flex:1;min-height:700px;padding:32px 60px;font-size:14px;line-height:1.7}.je-editor-area .tiptap h1{margin-bottom:.5em;font-size:2em;font-weight:700}.je-editor-area .tiptap h2{margin-bottom:.5em;font-size:1.5em;font-weight:700}.je-editor-area .tiptap h3{margin-bottom:.5em;font-size:1.25em;font-weight:700}.je-editor-area .tiptap p{margin-bottom:.8em;line-height:1.7}.je-editor-area .tiptap ul{margin-bottom:.8em;padding-left:1.5em;list-style-type:disc}.je-editor-area .tiptap ol{margin-bottom:.8em;padding-left:1.5em;list-style-type:decimal}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-thumb{background:#ddd;border-radius:10px}.image-node-wrapper{cursor:default;-webkit-user-select:none;user-select:none;line-height:0;display:inline-block;position:relative}.image-node-wrapper img{border:2px solid #0000;border-radius:2px;max-width:100%;transition:border-color .15s;display:block}.image-node-wrapper.is-selected img{border-color:var(--je-blue)}.image-resize-handle{background:var(--je-blue);z-index:10;border-radius:50%;width:10px;height:10px;display:none;position:absolute}.image-node-wrapper.is-selected .image-resize-handle{display:block}.image-resize-handle[data-corner=tl]{cursor:nwse-resize;top:-5px;left:-5px}.image-resize-handle[data-corner=tr]{cursor:nesw-resize;top:-5px;right:-5px}.image-resize-handle[data-corner=bl]{cursor:nesw-resize;bottom:-5px;left:-5px}.image-resize-handle[data-corner=br]{cursor:nwse-resize;bottom:-5px;right:-5px}
|
|
1
|
+
:root{--je-blue:#0052d9;--je-blue-active:#e6f0ff;--je-divider:#e0e0e0;--je-hover:#eee;--je-radius:4px}.je-container{-webkit-font-smoothing:antialiased;background:#fff;border:1px solid #e6e6e6;border-radius:4px;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,PingFang SC,Microsoft YaHei,sans-serif;display:flex;overflow:visible}.je-toolbar-row{z-index:40;flex-shrink:0;align-items:center;gap:1px;padding:0 12px;display:flex;position:sticky}.je-toolbar-row--primary{background:#fff;height:44px;top:0}.je-toolbar-row--secondary{background:#f3f4f6;border-radius:6px;height:42px;margin:0 8px 4px;position:sticky;top:44px}.je-toolbar-row--secondary:after{content:"";pointer-events:none;opacity:0;background:#eef0f2;height:1px;transition:opacity .12s;position:absolute;bottom:-5px;left:-8px;right:-8px}.je-container.is-scrolled .je-toolbar-row--secondary:after{opacity:1}.je-spacer{flex:1}.v-divider{background-color:var(--je-divider);flex-shrink:0;width:1px;height:16px;margin:0 6px}.tool-btn,.tool-btn-text,.tool-btn-arrow-down{border-radius:var(--je-radius);color:#444;cursor:pointer;background:0 0;border:none;flex-shrink:0;justify-content:center;align-items:center;gap:2px;line-height:1;transition:background-color .1s;display:inline-flex}.tool-btn{width:28px;height:28px;padding:0;font-size:13px}.tool-btn:hover{background-color:var(--je-hover)}.tool-btn.is-muted,.tool-btn-text.is-muted,.je-color-group.is-muted{color:#9ca3af;opacity:1;pointer-events:none}.tool-btn-text{white-space:nowrap;height:28px;padding:0 6px;font-size:13px}.tool-btn-text:hover{background-color:var(--je-hover)}.tool-btn-text--icon{gap:4px}.tool-btn-arrow-down{color:#999;width:16px;height:28px;padding:0}.tool-btn-arrow-down:hover{background-color:var(--je-hover)}.tool-btn svg,.tool-btn-text svg,.tool-btn-arrow-down svg{flex-shrink:0;width:20px;height:20px}.tool-btn-text svg,.tool-btn-arrow-down svg{width:14px;height:14px}.je-color-group{flex-shrink:0;align-items:center;display:inline-flex}.je-color-inner{justify-content:center;align-items:center;line-height:1;display:flex}.je-color-chip{background:0 0;border-radius:6px;justify-content:center;align-items:center;width:20px;height:20px;padding:0;display:inline-flex}.je-color-char{color:currentColor;font-size:14px;font-weight:600}.tool-btn.is-active,.tool-btn-text.is-active,.je-color-group.is-active{background-color:var(--je-blue-active);color:var(--je-blue)}.tool-btn.is-disabled,.tool-btn-text.is-disabled,.je-color-group.is-disabled{opacity:.35;pointer-events:none;cursor:default}.je-container.is-source-mode .je-toolbar-row .is-disabled{opacity:.35}.tool-btn.is-muted:hover,.tool-btn-text.is-muted:hover{background:0 0}.is-overflow-hidden{display:none!important}.je-editor-area,.je-editor-visual{flex-direction:column;flex:1;display:flex}.je-document-preview{background:#fff;border:none;width:100%;min-height:700px;display:none}.je-document-preview.is-active{display:block}.je-source-pane{box-sizing:border-box;background:linear-gradient(#f8fafc 0%,#fff 100%);flex:1;grid-template-columns:minmax(320px,1fr) minmax(320px,1fr);gap:16px;min-height:700px;padding:20px 24px 24px;display:none}.je-source-pane.is-active{display:grid}.je-source-textarea{color:#0f172a;resize:none;box-sizing:border-box;white-space:pre;background:#fff;border:1px solid #e5e7eb;border-radius:14px;outline:none;width:100%;min-height:100%;padding:18px 20px;font:13px/1.7 JetBrains Mono,SFMono-Regular,Consolas,monospace}.je-source-textarea:focus{border-color:#bfdbfe;box-shadow:0 0 0 4px #bfdbfe59}.je-source-preview{box-sizing:border-box;background:#fff;border:1px solid #e5e7eb;border-radius:14px;width:100%;min-height:100%}.je-editor-area .tiptap{cursor:text;color:#333;outline:none;flex:1;min-height:700px;padding:32px 60px;font-size:14px;line-height:1.7}.je-editor-area .tiptap h1{margin-bottom:.5em;font-size:2em;font-weight:700}.je-editor-area .tiptap h2{margin-bottom:.5em;font-size:1.5em;font-weight:700}.je-editor-area .tiptap h3{margin-bottom:.5em;font-size:1.25em;font-weight:700}.je-editor-area .tiptap p{margin-bottom:.8em;line-height:1.7}.je-editor-area .tiptap blockquote{color:#111827;background:#f3f4f6;border-left:3px solid #374151;margin:.8em 0;padding:8px 16px 8px 18px}.je-editor-area .tiptap ul{margin-bottom:.8em;padding-left:1.7em;list-style-type:disc}.je-editor-area .tiptap ul li::marker{color:#1c81d9}.je-editor-area .tiptap ol{counter-reset:je-ol;margin-bottom:.8em;padding-left:3.2em;list-style-type:none}.je-editor-area .tiptap ol>li{counter-increment:je-ol;position:relative}.je-editor-area .tiptap ol>li:before{content:counter(je-ol) ".";color:#1c81d9;white-space:nowrap;text-align:right;font-variant-numeric:tabular-nums;width:2.6em;position:absolute;left:-3.2em}.je-editor-area .tiptap ol ol{counter-reset:je-sub-ol;padding-left:3.8em}.je-editor-area .tiptap ol ol>li{counter-increment:je-sub-ol}.je-editor-area .tiptap ol ol>li:before{content:counter(je-ol) "." counter(je-sub-ol);width:3.2em;left:-3.8em}.je-editor-area .tiptap ol ol ol{counter-reset:je-sub-sub-ol;padding-left:4.8em}.je-editor-area .tiptap ol ol ol>li{counter-increment:je-sub-sub-ol}.je-editor-area .tiptap ol ol ol>li:before{content:counter(je-ol) "." counter(je-sub-ol) "." counter(je-sub-sub-ol);width:4.2em;left:-4.8em}.je-editor-area .tiptap li>p{margin-bottom:.35em}.je-editor-area .tiptap hr{background:#e5e7eb;border:none;height:1px;margin:1.1em 0}.je-editor-area .tiptap code{color:#c2410c;background:#f7f9fc;border:1px solid #e8edf3;border-radius:6px;padding:.14em .42em;font-family:JetBrains Mono,SFMono-Regular,Consolas,monospace;font-size:.92em;font-weight:700}.je-editor-area .tiptap a{color:#2563eb;text-underline-offset:2px;text-decoration:underline;text-decoration-thickness:1px}.je-editor-area .tiptap .tableWrapper{width:fit-content;max-width:100%;margin:1em 0;position:relative}.je-editor-area .tiptap table{border-collapse:collapse;table-layout:fixed;background:#fff;width:auto;margin:1em 0}.je-editor-area .tiptap th,.je-editor-area .tiptap td{vertical-align:top;border:1px solid #e5e7eb;width:8px;min-width:8px;height:4px;padding:4px 8px}.je-editor-area .tiptap th{color:#374151;background:#f8fafc;font-weight:600}.je-modal-overlay{z-index:1400;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);background:#0f172a2e;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.je-modal{background:#fff;border:1px solid #e8edf3;border-radius:14px;width:min(430px,100vw - 32px);padding:22px 24px 20px;box-shadow:0 24px 60px #0f172a29}.je-modal-title{color:#111827;margin-bottom:18px;font-size:18px;font-weight:700}.je-modal-field{grid-template-columns:44px 1fr;align-items:center;gap:10px;margin-bottom:14px;display:grid}.je-modal-label{color:#374151;font-size:14px}.je-modal-input{color:#111827;background:#f9fbfd;border:1px solid #dbe3ec;border-radius:8px;outline:none;height:36px;padding:0 12px;font-size:14px}.je-modal-input::placeholder{color:#9ca3af}.je-modal-input:focus{background:#fff;border-color:#9ec5fe;box-shadow:0 0 0 3px #93c5fd2e}.je-modal-actions{justify-content:flex-end;gap:10px;margin-top:24px;display:flex}.je-modal-btn{color:#374151;cursor:pointer;background:#fff;border:1px solid #e3e8ef;border-radius:8px;min-width:70px;height:34px;padding:0 14px;font-size:14px;transition:background-color .12s,border-color .12s,color .12s}.je-modal-btn:hover{background:#f8fafc;border-color:#d4dde8}.je-modal-btn.is-primary{color:#3b82f6;background:#ddebff;border-color:#cfe1f8}.je-modal-btn.is-primary:hover{background:#d3e6ff;border-color:#bfd6f6}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-thumb{background:#ddd;border-radius:10px}.image-node-wrapper{cursor:default;-webkit-user-select:none;user-select:none;line-height:0;display:inline-block;position:relative}.image-node-wrapper img{border:2px solid #0000;border-radius:2px;max-width:100%;transition:border-color .15s;display:block}.image-node-wrapper.is-selected img{border-color:var(--je-blue)}.image-resize-handle{background:var(--je-blue);z-index:10;border-radius:50%;width:10px;height:10px;display:none;position:absolute}.image-node-wrapper.is-selected .image-resize-handle{display:block}.image-resize-handle[data-corner=tl]{cursor:nwse-resize;top:-5px;left:-5px}.image-resize-handle[data-corner=tr]{cursor:nesw-resize;top:-5px;right:-5px}.image-resize-handle[data-corner=bl]{cursor:nesw-resize;bottom:-5px;left:-5px}.image-resize-handle[data-corner=br]{cursor:nwse-resize;bottom:-5px;right:-5px}.je-code-button{color:#1d4ed8;font-family:Courier New,monospace;font-weight:700}.je-popover{z-index:1000;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);background:#fffffff5;border:1px solid #e5e7eb;border-radius:14px;min-width:180px;padding:10px;position:absolute;box-shadow:0 10px 28px #0f172a1a}.je-popover-list{flex-direction:column;gap:4px;display:flex}.je-popover-list--scroll{max-height:260px;overflow-y:auto}.je-popover-list--font-family{max-height:312px}.je-popover--font-family{min-width:144px;max-width:180px}.je-popover--font-size{min-width:126px;max-width:150px}.je-popover-item{color:#374151;cursor:pointer;text-align:left;background:0 0;border:none;border-radius:10px;align-items:center;gap:8px;width:100%;padding:8px 10px;font-size:13px;display:flex}.je-popover-item svg{flex-shrink:0;width:16px;height:16px}.je-popover-item:hover{background:#f3f4f6}.je-popover-color{width:fit-content;min-width:unset;max-width:calc(100vw - 32px)}.je-color-section+.je-color-section{margin-top:10px}.je-color-section-title{color:#9ca3af;margin-bottom:8px;font-size:12px;font-weight:500}.je-color-grid{grid-template-columns:repeat(6,30px);justify-content:start;gap:6px;display:grid}.je-color-grid--recent{grid-template-columns:repeat(6,30px)}.je-color-swatch{cursor:pointer;color:#6b7280;background:#fff;border:1px solid #d1d5db;border-radius:8px;justify-content:center;align-items:center;width:30px;height:30px;transition:transform .12s,border-color .12s,box-shadow .12s;display:inline-flex}.je-color-swatch:hover{border-color:#93c5fd;transform:translateY(-1px);box-shadow:0 0 0 3px #3b82f624}.je-color-swatch.is-empty{opacity:.28;cursor:default}.je-color-swatch--filled{border-color:#d1d5db}.je-color-swatch--text{background:#fff;padding:0}.je-color-swatch--custom,.je-color-swatch--clear,.je-color-swatch--combo{background:#fff}.je-color-swatch--custom{position:relative;overflow:hidden}.je-style-preview{background:0 0;border-radius:5px;justify-content:center;align-items:center;width:18px;height:18px;font-size:15px;font-weight:700;display:inline-flex}.je-style-preview.is-empty{opacity:.45;box-shadow:inset 0 0 0 1px #d1d5dbe6}.je-style-preview.is-clear{color:#9ca3af}.je-color-custom-panel{background:#fafbfc;border:1px solid #eef2f7;border-radius:10px;flex-direction:column;gap:8px;margin-top:10px;padding:10px;display:flex}.je-color-custom-panel.is-hidden{display:none}.je-color-custom-title{color:#9ca3af;font-size:12px;font-weight:500}.je-color-picker{cursor:pointer;background:#fff;border:1px solid #edf2f7;border-radius:8px;width:100%;height:34px;padding:2px}.je-color-actions{gap:6px;display:flex}.je-action-btn{color:#667085;cursor:pointer;background:#fff;border:1px solid #e8edf3;border-radius:8px;flex:1;height:30px;padding:0 10px;font-size:12px;transition:background-color .12s,border-color .12s,color .12s}.je-action-btn:hover{background:#f8fafc;border-color:#dde5ee}.je-action-btn.is-primary{color:#4b5563;background:#eef3f8;border-color:#dbe5f0}.je-action-btn.is-primary:hover{background:#e4ebf3;border-color:#ced8e4}.je-callout-item{color:#374151;cursor:pointer;text-align:left;background:0 0;border:none;border-radius:10px;grid-template-columns:30px 1fr;align-items:center;gap:10px;width:100%;padding:8px 10px;display:grid}.je-callout-item:hover{background:#f3f4f6}.je-callout-badge{border-radius:8px;justify-content:center;align-items:center;width:30px;height:30px;font-size:14px;font-weight:700;display:inline-flex}.je-callout-content{flex-direction:column;gap:2px;display:flex}.je-callout-label{color:#111827;font-size:13px;font-weight:600}.je-callout-short{color:#9ca3af;font-size:12px}.je-editor-area .tiptap .je-callout-wrapper{box-sizing:border-box;width:auto;margin:12px 0;position:relative}.je-editor-area .tiptap .je-callout{background:var(--je-callout-bg,#faf8ff);width:auto;color:var(--je-callout-color,#9333ea);box-sizing:border-box;border:1px solid #0000;border-radius:14px;margin:0;padding:14px 16px;position:relative}.je-editor-area .tiptap .je-callout-header{-webkit-user-select:none;user-select:none;align-items:center;gap:10px;margin-bottom:10px;display:flex}.je-editor-area .tiptap .je-callout-drag-handle{width:24px;height:24px;color:inherit;cursor:grab;opacity:0;pointer-events:none;background:0 0;border:none;border-radius:8px;flex-shrink:0;justify-content:center;align-items:center;transition:opacity .18s,background-color .12s;display:inline-flex}.je-editor-area .tiptap .je-callout-wrapper:hover .je-callout-drag-handle,.je-editor-area .tiptap .je-callout-wrapper.is-handle-visible .je-callout-drag-handle,.je-editor-area .tiptap .je-callout-wrapper.ProseMirror-selectednode .je-callout-drag-handle,.je-editor-area .tiptap .je-callout-drag-handle:focus-visible{opacity:1;pointer-events:auto}.je-editor-area .tiptap .je-callout-drag-handle:hover,.je-editor-area .tiptap .je-callout-drag-handle:focus-visible{background:#ffffff80;outline:none}.je-editor-area .tiptap .je-callout-drag-handle svg{width:14px;height:14px}.je-editor-area .tiptap .je-callout-wrapper.ProseMirror-selectednode .je-callout{border-color:var(--je-callout-color,#9333ea);box-shadow:0 0 0 2px color-mix(in srgb, var(--je-callout-color,#9333ea) 12%, transparent)}.je-editor-area .tiptap .je-callout-title-btn{color:inherit;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;padding:0;font-size:14px;font-weight:700;display:inline-flex}.je-editor-area .tiptap .je-callout-title-btn svg,.je-editor-area .tiptap .je-callout-icon svg{width:18px;height:18px}.je-editor-area .tiptap .je-callout-body>:last-child{margin-bottom:0}.je-editor-area .tiptap .je-callout-body{color:inherit;min-height:24px}.je-callout-type-popover{z-index:1001;min-width:220px;position:absolute}.je-insert-popover{min-width:160px;padding:8px}.je-insert-item{color:#374151;cursor:pointer;text-align:left;background:0 0;border:none;border-radius:10px;justify-content:space-between;align-items:center;width:100%;min-height:34px;padding:0 10px;display:flex}.je-insert-item:hover{background:#f3f6fa}.je-insert-item-main{align-items:center;gap:8px;display:inline-flex}.je-insert-item-icon,.je-insert-item-arrow{color:#6b7280;justify-content:center;align-items:center;display:inline-flex}.je-insert-item-icon svg{width:16px;height:16px}.je-insert-table-panel{flex-direction:column;align-items:center;width:fit-content;min-width:216px;padding:10px;display:flex}.je-insert-table-title{color:#6b7280;width:196px;margin-bottom:10px;font-size:13px}.je-insert-table-grid{grid-template-columns:repeat(10,16px);gap:4px;width:196px;display:grid}.je-insert-table-cell{cursor:pointer;background:#f8fafc;border:1px solid #dbe3ec;width:16px;height:16px}.je-insert-table-cell.is-active{background:#dbeafe;border-color:#93c5fd}.je-insert-table-info{color:#6b7280;width:196px;margin-top:10px;font-size:13px}.je-insert-table-custom{border-top:1px solid #eef2f7;grid-template-columns:minmax(0,1fr) minmax(0,1fr) 48px;gap:6px;width:196px;margin-top:12px;padding-top:10px;display:grid}.je-insert-table-custom-field{color:#6b7280;flex-direction:column;gap:4px;font-size:12px;display:flex}.je-insert-table-number{box-sizing:border-box;background:#fff;border:1px solid #dbe3ec;border-radius:8px;outline:none;width:100%;height:30px;padding:0 8px}.je-insert-table-confirm{color:#4b5563;cursor:pointer;box-sizing:border-box;background:#eef3f8;border:1px solid #dbe5f0;border-radius:8px;align-self:end;width:100%;height:30px;padding:0}.je-table-wrap{width:fit-content;max-width:100%;position:relative}.je-table-move-handle,.je-table-resize-handle{z-index:3;opacity:0;transition:opacity .12s,background-color .12s;position:absolute}.je-table-wrap:hover .je-table-move-handle,.je-table-wrap:hover .je-table-resize-handle,.je-table-wrap.ProseMirror-selectednode .je-table-move-handle,.je-table-wrap.ProseMirror-selectednode .je-table-resize-handle{opacity:1}.je-table-move-handle{color:#6b7280;cursor:grab;background:#fff;border:1px solid #dbe3ec;border-radius:6px;justify-content:center;align-items:center;width:20px;height:20px;display:inline-flex;top:-10px;left:-10px}.je-table-move-handle svg{width:12px;height:12px}.je-table-resize-handle{cursor:nwse-resize;background:#cbd5e1;border-radius:4px;width:14px;height:14px;bottom:-8px;right:-8px}.je-code-block-wrap{background:#fff;border:1px solid #d0d7de;border-radius:12px;width:100%;margin:1em 0;position:relative;overflow:hidden}.je-code-block-header{background:#f6f8fa;align-items:center;gap:8px;min-height:34px;padding:0 12px;display:flex}.je-code-block-select{color:#6b7280;appearance:none;background:0 0;border:none;border-radius:0;outline:none;height:24px;padding:0 18px 0 0;font-size:12px}.je-code-block-select option{color:#111827}.je-code-block-header:after{content:"";pointer-events:none;border-bottom:1.5px solid #9ca3af;border-right:1.5px solid #9ca3af;width:10px;height:10px;margin-left:-12px;transform:rotate(45deg)scale(.6)}.je-code-block-spacer{flex:1}.je-code-block-copy{color:#6b7280;opacity:0;pointer-events:none;cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;width:20px;height:20px;padding:0;transition:opacity .12s,background-color .12s,color .12s;display:inline-flex}.je-code-block-copy:hover{color:#374151;background:#eaeef2}.je-code-block-copy.is-copied{color:#1d4ed8}.je-code-block-copy svg{width:14px;height:14px}.je-code-block-wrap:hover .je-code-block-copy,.je-code-block-wrap:focus-within .je-code-block-copy{opacity:1;pointer-events:auto}.je-code-block-wrap pre{background:0 0;margin:0;padding:12px 16px 16px;overflow-x:auto}.je-code-block-wrap pre code{color:#24292e;box-shadow:none;background:0 0;border:none;padding:0;font-size:13px;font-weight:400;display:block}.je-code-block-wrap pre code.je-code-block{box-shadow:none;background:0 0;border:none}.je-code-block-wrap .hljs-comment,.je-code-block-wrap .hljs-quote{color:#6a737d;font-style:italic}.je-code-block-wrap .hljs-keyword,.je-code-block-wrap .hljs-doctag,.je-code-block-wrap .hljs-selector-tag,.je-code-block-wrap .hljs-literal,.je-code-block-wrap .hljs-type{color:#d73a49}.je-code-block-wrap .hljs-string,.je-code-block-wrap .hljs-attr,.je-code-block-wrap .hljs-template-tag{color:#032f62}.je-code-block-wrap .hljs-number,.je-code-block-wrap .hljs-built_in,.je-code-block-wrap .hljs-title.class_,.je-code-block-wrap .hljs-symbol{color:#005cc5}.je-code-block-wrap .hljs-function,.je-code-block-wrap .hljs-title.function_{color:#6f42c1}.je-link-popover{z-index:1100;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);background:#fffffffa;border:1px solid #e5e7eb;border-radius:10px;grid-template-columns:1fr auto;align-items:center;width:268px;min-height:36px;padding:0 8px;display:grid;position:absolute;box-shadow:0 10px 28px #0f172a1f}.je-link-popover__main{color:#2563eb;white-space:nowrap;text-overflow:ellipsis;align-items:center;font-size:13px;text-decoration:none;display:inline-flex;overflow:hidden}.je-link-popover__actions{align-items:center;gap:4px;margin-left:8px;display:inline-flex}.je-link-popover__action{color:#6b7280;cursor:pointer;background:#fff;border:1px solid #e5e7eb;border-radius:8px;width:28px;height:28px}.je-link-popover__action:hover{color:#374151;background:#f8fafc}.raw-html-island{background:linear-gradient(#f8fafc 0%,#fff 100%);border:1px solid #e5e7eb;border-radius:14px;margin:12px 0;overflow:hidden}.raw-html-island__header{color:#475569;letter-spacing:.02em;text-transform:uppercase;border-bottom:1px solid #eef2f7;justify-content:space-between;align-items:center;gap:12px;padding:10px 14px;font-size:12px;font-weight:700;display:flex}.raw-html-island__actions{align-items:center;gap:6px;display:inline-flex}.raw-html-island__action{color:#64748b;cursor:pointer;background:#fff;border:1px solid #e6ebf1;border-radius:8px;height:28px;padding:0 10px;font-size:12px;transition:background-color .12s,border-color .12s,color .12s}.raw-html-island__action:hover{color:#475569;background:#f8fafc;border-color:#d6dee8}.raw-html-island__action.is-danger{color:#b91c1c}.raw-html-island__body{color:#0f172a;white-space:pre-wrap;word-break:break-word;margin:0;padding:14px;font:12px/1.7 JetBrains Mono,SFMono-Regular,Consolas,monospace}.raw-html-island.is-editing .raw-html-island__body{display:none}.raw-html-island__editor{background:#fff;border-top:1px solid #eef2f7;padding:14px}.raw-html-island__editor.is-hidden{display:none}.raw-html-island__textarea{color:#0f172a;resize:vertical;box-sizing:border-box;white-space:pre;background:#f8fafc;border:1px solid #e5e7eb;border-radius:12px;outline:none;width:100%;min-height:220px;padding:12px 14px;font:12px/1.7 JetBrains Mono,SFMono-Regular,Consolas,monospace}.raw-html-island__textarea:focus{background:#fff;border-color:#bfdbfe;box-shadow:0 0 0 4px #bfdbfe47}.raw-html-island__editor-actions{justify-content:flex-end;gap:8px;margin-top:10px;display:flex}.raw-html-island__action.is-primary{color:#4b5563;background:#eef3f8;border-color:#dbe5f0}.raw-html-island__action.is-primary:hover{background:#e4ebf3;border-color:#ced8e4}@media (width<=980px){.je-source-pane.is-active{grid-template-columns:1fr;min-height:auto}.je-source-textarea,.je-source-preview{min-height:360px}}pre code.hljs{padding:1em;display:block;overflow-x:auto}code.hljs{padding:3px 5px}.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#005cc5}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-comment,.hljs-code,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}
|
|
2
2
|
/*$vite$:1*/
|