@kaitify/core 0.0.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/examples/App.vue +342 -0
  4. package/examples/content.js +1 -0
  5. package/examples/main.ts +4 -0
  6. package/examples/test.html +23 -0
  7. package/lib/extensions/Extension.d.ts +172 -0
  8. package/lib/extensions/align/index.d.ts +10 -0
  9. package/lib/extensions/attachment/index.d.ts +29 -0
  10. package/lib/extensions/back-color/index.d.ts +9 -0
  11. package/lib/extensions/blockquote/index.d.ts +12 -0
  12. package/lib/extensions/bold/index.d.ts +9 -0
  13. package/lib/extensions/code/index.d.ts +12 -0
  14. package/lib/extensions/code-block/hljs.d.ts +12 -0
  15. package/lib/extensions/code-block/index.d.ts +15 -0
  16. package/lib/extensions/color/index.d.ts +9 -0
  17. package/lib/extensions/font-family/index.d.ts +9 -0
  18. package/lib/extensions/font-size/index.d.ts +9 -0
  19. package/lib/extensions/heading/index.d.ts +13 -0
  20. package/lib/extensions/history/index.d.ts +10 -0
  21. package/lib/extensions/horizontal/index.d.ts +7 -0
  22. package/lib/extensions/image/index.d.ts +26 -0
  23. package/lib/extensions/indent/index.d.ts +8 -0
  24. package/lib/extensions/index.d.ts +29 -0
  25. package/lib/extensions/italic/index.d.ts +9 -0
  26. package/lib/extensions/line-height/index.d.ts +9 -0
  27. package/lib/extensions/link/index.d.ts +27 -0
  28. package/lib/extensions/list/index.d.ts +18 -0
  29. package/lib/extensions/math/index.d.ts +11 -0
  30. package/lib/extensions/strikethrough/index.d.ts +9 -0
  31. package/lib/extensions/subscript/index.d.ts +9 -0
  32. package/lib/extensions/superscript/index.d.ts +9 -0
  33. package/lib/extensions/table/index.d.ts +21 -0
  34. package/lib/extensions/task/index.d.ts +12 -0
  35. package/lib/extensions/text/index.d.ts +14 -0
  36. package/lib/extensions/underline/index.d.ts +9 -0
  37. package/lib/extensions/video/index.d.ts +27 -0
  38. package/lib/index.d.ts +3 -0
  39. package/lib/kaitify-core.es.js +38337 -0
  40. package/lib/kaitify-core.umd.js +2 -0
  41. package/lib/model/Editor.d.ts +504 -0
  42. package/lib/model/History.d.ts +42 -0
  43. package/lib/model/KNode.d.ts +258 -0
  44. package/lib/model/Selection.d.ts +29 -0
  45. package/lib/model/config/dom-observe.d.ts +10 -0
  46. package/lib/model/config/event-handler.d.ts +33 -0
  47. package/lib/model/config/format-patch.d.ts +25 -0
  48. package/lib/model/config/format-rules.d.ts +37 -0
  49. package/lib/model/config/function.d.ts +84 -0
  50. package/lib/model/index.d.ts +6 -0
  51. package/lib/tools/index.d.ts +49 -0
  52. package/lib/view/index.d.ts +21 -0
  53. package/lib/view/js-render/dom-patch.d.ts +65 -0
  54. package/lib/view/js-render/index.d.ts +5 -0
  55. package/package.json +52 -0
  56. package/src/css/style.less +56 -0
  57. package/src/css/var.less +45 -0
  58. package/src/extensions/Extension.ts +200 -0
  59. package/src/extensions/align/index.ts +115 -0
  60. package/src/extensions/attachment/icon.svg +1 -0
  61. package/src/extensions/attachment/index.ts +293 -0
  62. package/src/extensions/attachment/style.less +25 -0
  63. package/src/extensions/back-color/index.ts +56 -0
  64. package/src/extensions/blockquote/index.ts +144 -0
  65. package/src/extensions/blockquote/style.less +16 -0
  66. package/src/extensions/bold/index.ts +77 -0
  67. package/src/extensions/code/index.ts +295 -0
  68. package/src/extensions/code/style.less +14 -0
  69. package/src/extensions/code-block/hljs.less +183 -0
  70. package/src/extensions/code-block/hljs.ts +95 -0
  71. package/src/extensions/code-block/index.ts +308 -0
  72. package/src/extensions/code-block/style.less +20 -0
  73. package/src/extensions/color/index.ts +56 -0
  74. package/src/extensions/font-family/index.ts +80 -0
  75. package/src/extensions/font-size/index.ts +56 -0
  76. package/src/extensions/heading/index.ts +164 -0
  77. package/src/extensions/heading/style.less +42 -0
  78. package/src/extensions/history/index.ts +96 -0
  79. package/src/extensions/horizontal/index.ts +45 -0
  80. package/src/extensions/horizontal/style.less +13 -0
  81. package/src/extensions/image/index.ts +242 -0
  82. package/src/extensions/image/style.less +8 -0
  83. package/src/extensions/indent/index.ts +98 -0
  84. package/src/extensions/index.ts +29 -0
  85. package/src/extensions/italic/index.ts +77 -0
  86. package/src/extensions/line-height/index.ts +113 -0
  87. package/src/extensions/link/index.ts +184 -0
  88. package/src/extensions/link/style.less +19 -0
  89. package/src/extensions/list/index.ts +410 -0
  90. package/src/extensions/list/style.less +19 -0
  91. package/src/extensions/math/index.ts +233 -0
  92. package/src/extensions/math/style.less +21 -0
  93. package/src/extensions/strikethrough/index.ts +78 -0
  94. package/src/extensions/subscript/index.ts +77 -0
  95. package/src/extensions/superscript/index.ts +77 -0
  96. package/src/extensions/table/index.ts +1148 -0
  97. package/src/extensions/table/style.less +71 -0
  98. package/src/extensions/task/index.ts +243 -0
  99. package/src/extensions/task/style.less +59 -0
  100. package/src/extensions/text/index.ts +359 -0
  101. package/src/extensions/underline/index.ts +78 -0
  102. package/src/extensions/video/index.ts +273 -0
  103. package/src/extensions/video/style.less +8 -0
  104. package/src/index.ts +9 -0
  105. package/src/model/Editor.ts +1963 -0
  106. package/src/model/History.ts +115 -0
  107. package/src/model/KNode.ts +677 -0
  108. package/src/model/Selection.ts +39 -0
  109. package/src/model/config/dom-observe.ts +184 -0
  110. package/src/model/config/event-handler.ts +237 -0
  111. package/src/model/config/format-patch.ts +215 -0
  112. package/src/model/config/format-rules.ts +218 -0
  113. package/src/model/config/function.ts +1018 -0
  114. package/src/model/index.ts +6 -0
  115. package/src/tools/index.ts +156 -0
  116. package/src/view/index.ts +46 -0
  117. package/src/view/js-render/dom-patch.ts +324 -0
  118. package/src/view/js-render/index.ts +210 -0
  119. package/vite-env.d.ts +2 -0
@@ -0,0 +1,410 @@
1
+ import { Editor, KNode, KNodeStylesType } from '@/model'
2
+ import { getSelectionBlockNodes } from '@/model/config/function'
3
+ import { Extension } from '../Extension'
4
+ import './style.less'
5
+
6
+ export type OrderedListType = 'decimal' | 'lower-alpha' | 'upper-alpha' | 'lower-roman' | 'upper-roman' | 'lower-greek' | 'cjk-ideographic'
7
+
8
+ export type UnorderListType = 'disc' | 'circle' | 'square'
9
+
10
+ export type ListOptionsType = {
11
+ ordered?: boolean
12
+ listType?: OrderedListType | UnorderListType
13
+ }
14
+
15
+ declare module '../../model' {
16
+ interface EditorCommandsType {
17
+ getList?: (options: ListOptionsType) => KNode | null
18
+ hasList?: (options: ListOptionsType) => boolean
19
+ allList?: (options: ListOptionsType) => boolean
20
+ setList?: (options: ListOptionsType) => Promise<void>
21
+ unsetList?: (options: ListOptionsType) => Promise<void>
22
+ }
23
+ }
24
+
25
+ /**
26
+ * 块节点转为列表
27
+ */
28
+ const toList = (editor: Editor, node: KNode, ordered?: boolean, listType?: OrderedListType | UnorderListType) => {
29
+ if (!node.isBlock()) {
30
+ return
31
+ }
32
+ //是列表项节点
33
+ if (node.isMatch({ tag: 'li' })) {
34
+ //如果是和当前要转的列表类型一致则不处理
35
+ if (listType) {
36
+ if (node.parent!.isMatch({ tag: ordered ? 'ol' : 'ul', styles: { listStyleType: listType } })) {
37
+ return
38
+ }
39
+ } else {
40
+ if (node.parent!.isMatch({ tag: ordered ? 'ol' : 'ul' })) {
41
+ return
42
+ }
43
+ }
44
+
45
+ //获取列表节点
46
+ const listNode = node.parent!
47
+ //获取当前块节点在列表项节点里的序列
48
+ const index = listNode.children!.findIndex(item => item.isEqual(node))
49
+ //复制当前块节点
50
+ const newNode = node.clone(false)
51
+ //将块节点的子节点都给复制的块节点
52
+ node.children!.forEach((item, index) => {
53
+ editor.addNode(item, newNode, index)
54
+ })
55
+ node.children = []
56
+ //创建新的列表节点
57
+ const newListNode = KNode.create({
58
+ type: 'block',
59
+ tag: ordered ? 'ol' : 'ul',
60
+ children: []
61
+ })
62
+ if (listType) {
63
+ newListNode.styles = {
64
+ listStyleType: listType
65
+ }
66
+ }
67
+ //将复制的块节点给新的列表节点
68
+ editor.addNode(newNode, newListNode)
69
+ //该列表项节点是原列表节点的第一个子节点
70
+ if (index == 0) {
71
+ editor.addNodeBefore(newListNode, listNode)
72
+ }
73
+ //该列表项节点是原列表节点的最后一个子节点
74
+ else if (index == listNode.children!.length - 1) {
75
+ editor.addNodeAfter(newListNode, listNode)
76
+ }
77
+ //该列表项节点在原列表节点的中间
78
+ else {
79
+ //复制原列表节点
80
+ const sList = listNode.clone(false)
81
+ //截取原节点的前半部分数据给复制的列表节点
82
+ const sListItems = listNode.children!.splice(0, index)
83
+ sListItems.forEach((item, index) => {
84
+ editor.addNode(item, sList, index)
85
+ })
86
+ editor.addNodeBefore(newListNode, listNode)
87
+ editor.addNodeBefore(sList, newListNode)
88
+ }
89
+ }
90
+ //是固定的块节点或者内嵌套的块节点
91
+ else if (node.fixed || node.nested) {
92
+ //创建列表节点
93
+ const listNode = KNode.create({
94
+ type: 'block',
95
+ tag: ordered ? 'ol' : 'ul',
96
+ children: [
97
+ {
98
+ type: 'block',
99
+ tag: 'li',
100
+ nested: true,
101
+ children: []
102
+ }
103
+ ]
104
+ })
105
+ if (listType) {
106
+ listNode.styles = {
107
+ listStyleType: listType
108
+ }
109
+ }
110
+ //将块节点的子节点给列表节点的列表项节点
111
+ node.children!.forEach((item, index) => {
112
+ editor.addNode(item, listNode.children![0], index)
113
+ })
114
+ //将列表节点添加到块节点下
115
+ listNode.parent = node
116
+ node.children = [listNode]
117
+ }
118
+ //非固定块节点
119
+ else {
120
+ //将块节点转为列表节点
121
+ editor.toParagraph(node)
122
+ node.tag = ordered ? 'ol' : 'ul'
123
+ if (listType) {
124
+ node.styles = {
125
+ listStyleType: listType
126
+ }
127
+ }
128
+ //创建列表项节点
129
+ const listItem = KNode.create({
130
+ type: 'block',
131
+ tag: 'li',
132
+ nested: true
133
+ })
134
+ //将列表节点的子节点都给列表项节点
135
+ node.children!.forEach((item, index) => {
136
+ editor.addNode(item, listItem, index)
137
+ })
138
+ //将列表项节点作为列表节点的子节点
139
+ node.children = [listItem]
140
+ listItem.parent = node
141
+ }
142
+ }
143
+
144
+ /**
145
+ * 取消当前列表项块节点的列表设置
146
+ */
147
+ const ListItemToParagraph = (editor: Editor, node: KNode) => {
148
+ if (!node.isBlock()) {
149
+ return
150
+ }
151
+ if (!node.isMatch({ tag: 'li' })) {
152
+ return
153
+ }
154
+ //获取列表节点
155
+ const listNode = node.parent!
156
+ //列表项在列表节点中的序列
157
+ const index = listNode.children!.findIndex(item => item.isEqual(node))
158
+ //在列表节点的第一个位置
159
+ if (index == 0) {
160
+ editor.addNodeBefore(node, listNode)
161
+ listNode.children!.splice(index, 1)
162
+ }
163
+ //在列表节点的最后一个位置
164
+ else if (index == listNode.children!.length - 1) {
165
+ editor.addNodeAfter(node, listNode)
166
+ listNode.children!.splice(index, 1)
167
+ }
168
+ //在列表节点的中间位置
169
+ else {
170
+ //复制原列表节点
171
+ const sList = listNode.clone(false)
172
+ //截取原节点的前半部分数据给复制的列表节点
173
+ const sListItems = listNode.children!.splice(0, index)
174
+ sListItems.forEach((item, index) => {
175
+ editor.addNode(item, sList, index)
176
+ })
177
+ editor.addNodeBefore(node, listNode)
178
+ listNode.children!.splice(0, 1)
179
+ editor.addNodeBefore(sList, node)
180
+ }
181
+ editor.toParagraph(node)
182
+ }
183
+
184
+ /**
185
+ * 节点合并处理
186
+ */
187
+ const listMergeHandler = ({ editor, node }: { editor: Editor; node: KNode }) => {
188
+ //节点是有序列表
189
+ if (node.isMatch({ tag: 'ol' })) {
190
+ //前一个兄弟节点
191
+ const previousNode = node.getPrevious(node.parent ? node.parent.children! : editor.stackNodes)
192
+ //前一个兄弟节点是有序列表则将当前节点的子节点都给前一个节点
193
+ if (previousNode && previousNode.isMatch({ tag: 'ol' }) && previousNode.isEqualMarks(node) && previousNode.isEqualStyles(node)) {
194
+ const nodes = node.children!.map(item => {
195
+ item.parent = previousNode
196
+ return item
197
+ })
198
+ previousNode.children!.push(...nodes)
199
+ node.children = []
200
+ const nextNode = node.getNext(node.parent ? node.parent.children! : editor.stackNodes)
201
+ //如果此时后一个节点存在
202
+ if (nextNode) {
203
+ listMergeHandler({ editor, node: nextNode })
204
+ }
205
+ }
206
+ }
207
+ //节点是无序列表
208
+ if (node.isMatch({ tag: 'ul' })) {
209
+ //前一个兄弟节点
210
+ const previousNode = node.getPrevious(node.parent ? node.parent.children! : editor.stackNodes)
211
+ //前一个兄弟节点是无序列表则将当前节点的子节点都给前一个节点
212
+ if (previousNode && previousNode.isMatch({ tag: 'ul' }) && previousNode.isEqualMarks(node) && previousNode.isEqualStyles(node)) {
213
+ const nodes = node.children!.map(item => {
214
+ item.parent = previousNode
215
+ return item
216
+ })
217
+ previousNode.children!.push(...nodes)
218
+ node.children = []
219
+ const nextNode = node.getNext(node.parent ? node.parent.children! : editor.stackNodes)
220
+ //如果此时后一个节点存在
221
+ if (nextNode) {
222
+ listMergeHandler({ editor, node: nextNode })
223
+ }
224
+ }
225
+ }
226
+ }
227
+
228
+ export const ListExtension = () =>
229
+ Extension.create({
230
+ name: 'list',
231
+ extraKeepTags: ['ul', 'ol', 'li'],
232
+ domParseNodeCallback(node) {
233
+ if (node.isMatch({ tag: 'ul' }) || node.isMatch({ tag: 'ol' })) {
234
+ node.type = 'block'
235
+ }
236
+ if (node.isMatch({ tag: 'li' })) {
237
+ node.type = 'block'
238
+ node.nested = true
239
+ }
240
+ return node
241
+ },
242
+ formatRules: [
243
+ //列表处理
244
+ ({ node }) => {
245
+ if (node.isMatch({ tag: 'ol' }) || node.isMatch({ tag: 'ul' })) {
246
+ //必须是块节点
247
+ node.type = 'block'
248
+ //固定设置序标在内侧
249
+ if (node.hasStyles()) {
250
+ node.styles!.listStylePosition = 'inside'
251
+ } else {
252
+ node.styles = {
253
+ listStylePosition: 'inside'
254
+ }
255
+ }
256
+ }
257
+ },
258
+ //列表项处理
259
+ ({ editor, node }) => {
260
+ if (node.isMatch({ tag: 'li' })) {
261
+ //必须是内嵌块节点
262
+ node.type = 'block'
263
+ node.nested = true
264
+ //如果li节点无父节点或者父节点不是有序列表也不是无序列表,则默认加入到无序列表中
265
+ if (!node.parent || !(node.parent.isMatch({ tag: 'ol' }) || node.parent.isMatch({ tag: 'ul' }))) {
266
+ //设为内嵌块节点
267
+ node.nested = true
268
+ //创建列表节点
269
+ const listNode = KNode.create({
270
+ type: 'block',
271
+ tag: 'ul'
272
+ })
273
+ //获取父节点
274
+ const parentNode = node.parent
275
+ //在父节点中的位置序列
276
+ const index = parentNode ? parentNode.children!.findIndex(item => item.isEqual(node)) : editor.stackNodes.findIndex(item => item.isEqual(node))
277
+ //从父节点中移除
278
+ parentNode ? parentNode.children!.splice(index, 1, listNode) : editor.stackNodes.splice(index, 1, listNode)
279
+ //加入到列表节点中
280
+ editor.addNode(node, listNode)
281
+ }
282
+ }
283
+ },
284
+ //列表合并处理
285
+ listMergeHandler
286
+ ],
287
+ pasteKeepStyles(node) {
288
+ const styles: KNodeStylesType = {}
289
+ //保留序标类型样式
290
+ if ((node.isMatch({ tag: 'ol' }) || node.isMatch({ tag: 'ul' })) && node.hasStyles()) {
291
+ if (node.styles!.hasOwnProperty('listStyleType')) styles.listStyleType = node.styles!['listStyleType']
292
+ }
293
+ return styles
294
+ },
295
+ onDetachMentBlockFromParentCallback(node) {
296
+ //父节点存在并且是列表节点
297
+ if (node.parent && (node.parent.isMatch({ tag: 'ol' }) || node.parent.isMatch({ tag: 'ul' }))) {
298
+ //将该节点转为列表项节点
299
+ node.tag = 'li'
300
+ node.marks = {}
301
+ node.styles = {}
302
+ node.fixed = false
303
+ node.nested = true
304
+ node.locked = false
305
+ node.namespace = ''
306
+ return false
307
+ }
308
+ return true
309
+ },
310
+ addCommands() {
311
+ /**
312
+ * 获取光标所在的有序列表或者无序列表,如果光标不在一个有序列表或者无序列表内,返回null
313
+ */
314
+ const getList = (options: ListOptionsType) => {
315
+ if (options.listType) {
316
+ return this.getMatchNodeBySelection({
317
+ tag: options.ordered ? 'ol' : 'ul',
318
+ styles: {
319
+ listStyleType: options.listType
320
+ }
321
+ })
322
+ }
323
+ return this.getMatchNodeBySelection({ tag: options.ordered ? 'ol' : 'ul' })
324
+ }
325
+
326
+ /**
327
+ * 判断光标范围内是否有有序列表或者无序列表
328
+ */
329
+ const hasList = (options: ListOptionsType) => {
330
+ if (options.listType) {
331
+ return this.isSelectionNodesSomeMatch({
332
+ tag: options.ordered ? 'ol' : 'ul',
333
+ styles: {
334
+ listStyleType: options.listType
335
+ }
336
+ })
337
+ }
338
+ return this.isSelectionNodesSomeMatch({ tag: options.ordered ? 'ol' : 'ul' })
339
+ }
340
+
341
+ /**
342
+ * 判断光标范围内是否都是有序列表或者无序列表
343
+ */
344
+ const allList = (options: ListOptionsType) => {
345
+ if (options.listType) {
346
+ return this.isSelectionNodesAllMatch({
347
+ tag: options.ordered ? 'ol' : 'ul',
348
+ styles: {
349
+ listStyleType: options.listType
350
+ }
351
+ })
352
+ }
353
+ return this.isSelectionNodesAllMatch({ tag: options.ordered ? 'ol' : 'ul' })
354
+ }
355
+
356
+ /**
357
+ * 设置有序列表或者无序列表
358
+ */
359
+ const setList = async (options: ListOptionsType) => {
360
+ if (allList(options)) {
361
+ return
362
+ }
363
+ //起点和终点在一起
364
+ if (this.selection.collapsed()) {
365
+ const blockNode = this.selection.start!.node.getBlock()
366
+ toList(this, blockNode, options.ordered, options.listType)
367
+ }
368
+ //起点和终点不在一起
369
+ else {
370
+ const blockNodes = getSelectionBlockNodes.apply(this)
371
+ blockNodes.forEach(item => {
372
+ toList(this, item, options.ordered, options.listType)
373
+ })
374
+ }
375
+ await this.updateView()
376
+ }
377
+
378
+ /**
379
+ * 取消有序列表或者无序列表
380
+ */
381
+ const unsetList = async (options: ListOptionsType) => {
382
+ if (!allList(options)) {
383
+ return
384
+ }
385
+ //起点和终点在一起
386
+ if (this.selection.collapsed()) {
387
+ const blockNode = this.selection.start!.node.getBlock()
388
+ const matchNode = blockNode.getMatchNode({ tag: 'li' })
389
+ if (matchNode) ListItemToParagraph(this, matchNode)
390
+ }
391
+ //起点和终点不在一起
392
+ else {
393
+ const blockNodes = getSelectionBlockNodes.apply(this)
394
+ blockNodes.forEach(item => {
395
+ const matchNode = item.getMatchNode({ tag: 'li' })
396
+ if (matchNode) ListItemToParagraph(this, matchNode)
397
+ })
398
+ }
399
+ await this.updateView()
400
+ }
401
+
402
+ return {
403
+ getList,
404
+ hasList,
405
+ allList,
406
+ setList,
407
+ unsetList
408
+ }
409
+ }
410
+ })
@@ -0,0 +1,19 @@
1
+ .Kaitify {
2
+ ol,
3
+ ul {
4
+ margin: 0 0 var(--kaitify-large-margin) 0;
5
+ padding: 0;
6
+
7
+ &:last-child {
8
+ margin-bottom: 0 !important;
9
+ }
10
+
11
+ li {
12
+ margin: 0 0 var(--kaitify-large-margin) 0;
13
+
14
+ &:last-child {
15
+ margin-bottom: 0;
16
+ }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,233 @@
1
+ import KaTex from 'katex'
2
+ import { common as DapCommon } from 'dap-util'
3
+ import { KNode, KNodeMarksType, KNodeStylesType } from '@/model'
4
+ import { Extension } from '../Extension'
5
+ import 'katex/dist/katex.css'
6
+ import './style.less'
7
+
8
+ declare module '../../model' {
9
+ interface EditorCommandsType {
10
+ getMath?: () => KNode | null
11
+ hasMath?: () => boolean
12
+ setMath?: (value: string) => Promise<void>
13
+ updateMath?: (value: string) => Promise<void>
14
+ }
15
+ }
16
+
17
+ export const MathExtension = () =>
18
+ Extension.create({
19
+ name: 'math',
20
+ domParseNodeCallback(node) {
21
+ if (
22
+ node.isMatch({
23
+ tag: 'span',
24
+ marks: {
25
+ 'kaitify-math': true
26
+ }
27
+ })
28
+ ) {
29
+ //锁定节点防止合并
30
+ node.locked = true
31
+ //设为行内
32
+ node.type = 'inline'
33
+ //处理子孙节点
34
+ KNode.flat(node.children!).forEach(item => {
35
+ //锁定节点防止合并
36
+ item.locked = true
37
+ //非文本节点
38
+ if (!item.isText()) {
39
+ //有子节点转为行内
40
+ if (item.hasChildren()) {
41
+ item.type = 'inline'
42
+ }
43
+ //无子节点转为闭合
44
+ else {
45
+ item.type = 'closed'
46
+ }
47
+ }
48
+ })
49
+ }
50
+ return node
51
+ },
52
+ pasteKeepMarks(node) {
53
+ const marks: KNodeMarksType = {}
54
+ //数学公式内的标记全部保留
55
+ if (
56
+ !!node.getMatchNode({
57
+ tag: 'span',
58
+ marks: {
59
+ 'kaitify-math': true
60
+ }
61
+ })
62
+ ) {
63
+ Object.assign(marks, DapCommon.clone(node.marks!))
64
+ }
65
+ return marks
66
+ },
67
+ pasteKeepStyles(node) {
68
+ const styles: KNodeStylesType = {}
69
+ //数学公式内的样式全部保留
70
+ if (
71
+ !!node.getMatchNode({
72
+ tag: 'span',
73
+ marks: {
74
+ 'kaitify-math': true
75
+ }
76
+ })
77
+ ) {
78
+ Object.assign(styles, DapCommon.clone(node.styles!))
79
+ }
80
+ return styles
81
+ },
82
+ beforePatchNodeToFormat(node) {
83
+ const mathNode = node.getMatchNode({
84
+ tag: 'span',
85
+ marks: {
86
+ 'kaitify-math': true
87
+ }
88
+ })
89
+ if (mathNode) {
90
+ return mathNode
91
+ }
92
+ return node
93
+ },
94
+ formatRules: [
95
+ ({ editor, node }) => {
96
+ if (
97
+ !node.isEmpty() &&
98
+ node.isMatch({
99
+ tag: 'span',
100
+ marks: {
101
+ 'kaitify-math': true
102
+ }
103
+ })
104
+ ) {
105
+ //公式节点必须锁定
106
+ node.locked = true
107
+ //公式节点必须是行内
108
+ node.type = 'inline'
109
+ //保持对子孙节点的处理
110
+ KNode.flat(node.children!).forEach(item => {
111
+ //锁定节点防止合并
112
+ item.locked = true
113
+ //非文本节点
114
+ if (!item.isText()) {
115
+ //有子节点转为行内
116
+ if (item.hasChildren()) {
117
+ item.type = 'inline'
118
+ }
119
+ //无子节点转为闭合
120
+ else {
121
+ item.type = 'closed'
122
+ }
123
+ }
124
+ })
125
+ //没有不可编辑标记的话需要设置
126
+ if (node.marks!['contenteditable'] != 'false') {
127
+ node.marks!['contenteditable'] = 'false'
128
+ }
129
+ //两侧设置空白元素
130
+ const previousNode = node.getPrevious(node.parent ? node.parent!.children! : editor.stackNodes)
131
+ const nextNode = node.getNext(node.parent ? node.parent!.children! : editor.stackNodes)
132
+ //前一个节点不存在或者不是零宽度空白文本节点
133
+ if (!previousNode || !previousNode.isZeroWidthText()) {
134
+ const zeroWidthText = KNode.createZeroWidthText()
135
+ editor.addNodeBefore(zeroWidthText, node)
136
+ }
137
+ //后一个节点不存在或者不是零宽度空白文本节点
138
+ if (!nextNode || !nextNode.isZeroWidthText()) {
139
+ const zeroWidthText = KNode.createZeroWidthText()
140
+ editor.addNodeAfter(zeroWidthText, node)
141
+ }
142
+ //重置光标
143
+ if (editor.isSelectionInNode(node, 'start')) {
144
+ const newTextNode = node.getNext(node.parent ? node.parent!.children! : editor.stackNodes)
145
+ if (newTextNode) editor.setSelectionBefore(newTextNode, 'start')
146
+ }
147
+ if (editor.isSelectionInNode(node, 'end')) {
148
+ const newTextNode = node.getNext(node.parent ? node.parent!.children! : editor.stackNodes)
149
+ if (newTextNode) editor.setSelectionBefore(newTextNode, 'end')
150
+ }
151
+ }
152
+ }
153
+ ],
154
+ addCommands() {
155
+ /**
156
+ * 获取光标所在的数学公式节点,如果光标不在一个数学公式节点内,返回null
157
+ */
158
+ const getMath = () => {
159
+ return this.getMatchNodeBySelection({
160
+ tag: 'span',
161
+ marks: {
162
+ 'kaitify-math': true
163
+ }
164
+ })
165
+ }
166
+
167
+ /**
168
+ * 判断光标范围内是否有数学公式节点
169
+ */
170
+ const hasMath = () => {
171
+ return this.isSelectionNodesSomeMatch({
172
+ tag: 'span',
173
+ marks: {
174
+ 'kaitify-math': true
175
+ }
176
+ })
177
+ }
178
+
179
+ /**
180
+ * 插入数学公式
181
+ */
182
+ const setMath = async (value: string) => {
183
+ if (!value || !this.selection.focused() || hasMath()) {
184
+ return
185
+ }
186
+ const mathHtml = KaTex.renderToString(value, {
187
+ output: 'html',
188
+ throwOnError: true
189
+ })
190
+ //设置最终的html内容
191
+ const html = `<span kaitify-math="${value}" contenteditable="false">${mathHtml}</span>`
192
+ //html内容转为节点数组
193
+ const nodes = this.htmlParseNode(html)
194
+ //插入节点
195
+ this.insertNode(nodes[0])
196
+ await this.updateView()
197
+ }
198
+
199
+ /**
200
+ * 更新数学公式
201
+ */
202
+ const updateMath = async (value: string) => {
203
+ if (!value || !this.selection.focused()) {
204
+ return
205
+ }
206
+ const mathNode = getMath()
207
+ if (!mathNode) {
208
+ return
209
+ }
210
+ const mathHtml = KaTex.renderToString(value, {
211
+ output: 'html',
212
+ throwOnError: true
213
+ })
214
+ //设置最终的html内容
215
+ const html = `<span kaitify-math="${value}" contenteditable="false">${mathHtml}</span>`
216
+ //html内容转为节点数组
217
+ const nodes = this.htmlParseNode(html)
218
+ //替换掉原来的数学公式
219
+ nodes[0].parent = mathNode.parent
220
+ const index = mathNode.parent ? mathNode.parent.children!.findIndex(item => item.isEqual(mathNode)) : this.stackNodes.findIndex(item => item.isEqual(mathNode))
221
+ mathNode.parent ? mathNode.parent.children!.splice(index, 1, nodes[0]) : this.stackNodes.splice(index, 1, nodes[0])
222
+ this.setSelectionAfter(nodes[0])
223
+ await this.updateView()
224
+ }
225
+
226
+ return {
227
+ getMath,
228
+ hasMath,
229
+ setMath,
230
+ updateMath
231
+ }
232
+ }
233
+ })
@@ -0,0 +1,21 @@
1
+ .Kaitify {
2
+ span[kaitify-math] {
3
+ display: inline-block;
4
+ padding: var(--kaitify-small-padding) var(--kaitify-padding);
5
+ border-radius: var(--kaitify-border-radius);
6
+ margin: 0 var(--kaitify-sides-between);
7
+ transition: all 300ms;
8
+ max-width: 100%;
9
+ background: var(--kaitify-lightest-theme);
10
+
11
+ &:hover {
12
+ cursor: pointer !important;
13
+ }
14
+
15
+ .katex,
16
+ math {
17
+ width: 100%;
18
+ overflow: hidden;
19
+ }
20
+ }
21
+ }