@jvs-milkdown/crepe 1.2.5 → 1.2.7

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 (130) hide show
  1. package/lib/cjs/builder.js +2 -0
  2. package/lib/cjs/builder.js.map +1 -1
  3. package/lib/cjs/feature/block-edit/index.js +6 -2
  4. package/lib/cjs/feature/block-edit/index.js.map +1 -1
  5. package/lib/cjs/feature/code-mirror/index.js +2 -0
  6. package/lib/cjs/feature/code-mirror/index.js.map +1 -1
  7. package/lib/cjs/feature/cursor/index.js +2 -0
  8. package/lib/cjs/feature/cursor/index.js.map +1 -1
  9. package/lib/cjs/feature/image-block/index.js +3 -4
  10. package/lib/cjs/feature/image-block/index.js.map +1 -1
  11. package/lib/cjs/feature/latex/index.js +2 -0
  12. package/lib/cjs/feature/latex/index.js.map +1 -1
  13. package/lib/cjs/feature/link-tooltip/index.js +2 -0
  14. package/lib/cjs/feature/link-tooltip/index.js.map +1 -1
  15. package/lib/cjs/feature/list-item/index.js +2 -0
  16. package/lib/cjs/feature/list-item/index.js.map +1 -1
  17. package/lib/cjs/feature/placeholder/index.js +4 -1
  18. package/lib/cjs/feature/placeholder/index.js.map +1 -1
  19. package/lib/cjs/feature/table/index.js +2 -0
  20. package/lib/cjs/feature/table/index.js.map +1 -1
  21. package/lib/cjs/feature/toolbar/index.js +242 -7
  22. package/lib/cjs/feature/toolbar/index.js.map +1 -1
  23. package/lib/cjs/index.js +1238 -962
  24. package/lib/cjs/index.js.map +1 -1
  25. package/lib/esm/builder.js +2 -0
  26. package/lib/esm/builder.js.map +1 -1
  27. package/lib/esm/feature/block-edit/index.js +6 -2
  28. package/lib/esm/feature/block-edit/index.js.map +1 -1
  29. package/lib/esm/feature/code-mirror/index.js +2 -0
  30. package/lib/esm/feature/code-mirror/index.js.map +1 -1
  31. package/lib/esm/feature/cursor/index.js +2 -0
  32. package/lib/esm/feature/cursor/index.js.map +1 -1
  33. package/lib/esm/feature/image-block/index.js +3 -4
  34. package/lib/esm/feature/image-block/index.js.map +1 -1
  35. package/lib/esm/feature/latex/index.js +2 -0
  36. package/lib/esm/feature/latex/index.js.map +1 -1
  37. package/lib/esm/feature/link-tooltip/index.js +2 -0
  38. package/lib/esm/feature/link-tooltip/index.js.map +1 -1
  39. package/lib/esm/feature/list-item/index.js +2 -0
  40. package/lib/esm/feature/list-item/index.js.map +1 -1
  41. package/lib/esm/feature/placeholder/index.js +4 -1
  42. package/lib/esm/feature/placeholder/index.js.map +1 -1
  43. package/lib/esm/feature/table/index.js +2 -0
  44. package/lib/esm/feature/table/index.js.map +1 -1
  45. package/lib/esm/feature/toolbar/index.js +242 -7
  46. package/lib/esm/feature/toolbar/index.js.map +1 -1
  47. package/lib/esm/index.js +1251 -975
  48. package/lib/esm/index.js.map +1 -1
  49. package/lib/theme/common/table.css +11 -7
  50. package/lib/theme/common/toolbar.css +6 -6
  51. package/lib/tsconfig.tsbuildinfo +1 -1
  52. package/lib/types/core/locale.d.ts +2 -0
  53. package/lib/types/core/locale.d.ts.map +1 -1
  54. package/lib/types/feature/attachment/view/components/attachment-block.d.ts.map +1 -1
  55. package/lib/types/feature/attachment/view/index.d.ts.map +1 -1
  56. package/lib/types/feature/block-edit/handle/component.d.ts.map +1 -1
  57. package/lib/types/feature/block-edit/handle/index.d.ts.map +1 -1
  58. package/lib/types/feature/block-edit/index.d.ts.map +1 -1
  59. package/lib/types/feature/block-edit/menu/api.d.ts.map +1 -1
  60. package/lib/types/feature/block-edit/menu/component.d.ts.map +1 -1
  61. package/lib/types/feature/block-edit/menu/index.d.ts.map +1 -1
  62. package/lib/types/feature/fixed-toolbar/component.d.ts.map +1 -1
  63. package/lib/types/feature/fixed-toolbar/config.d.ts +9 -1
  64. package/lib/types/feature/fixed-toolbar/config.d.ts.map +1 -1
  65. package/lib/types/feature/fixed-toolbar/document-header.d.ts.map +1 -1
  66. package/lib/types/feature/fixed-toolbar/index.d.ts.map +1 -1
  67. package/lib/types/feature/fixed-toolbar/menu-bar.d.ts.map +1 -1
  68. package/lib/types/feature/fixed-toolbar/outline-panel.d.ts.map +1 -1
  69. package/lib/types/feature/fixed-toolbar/shortcut-help-modal.d.ts.map +1 -1
  70. package/lib/types/feature/fixed-toolbar/view-menu-state.d.ts.map +1 -1
  71. package/lib/types/feature/index.d.ts +1 -1
  72. package/lib/types/feature/index.d.ts.map +1 -1
  73. package/lib/types/feature/latex/inline-tooltip/component.d.ts.map +1 -1
  74. package/lib/types/feature/latex/inline-tooltip/view.d.ts.map +1 -1
  75. package/lib/types/feature/loader.d.ts.map +1 -1
  76. package/lib/types/feature/placeholder/index.d.ts.map +1 -1
  77. package/lib/types/feature/toolbar/color.d.ts +1 -1
  78. package/lib/types/feature/toolbar/color.d.ts.map +1 -1
  79. package/lib/types/feature/toolbar/component.d.ts.map +1 -1
  80. package/lib/types/feature/toolbar/font.d.ts +1 -1
  81. package/lib/types/feature/toolbar/highlight-mark.d.ts +1 -1
  82. package/lib/types/feature/toolbar/index.d.ts.map +1 -1
  83. package/lib/types/feature/toolbar/underline.d.ts +1 -1
  84. package/lib/types/icons/crop.d.ts +1 -1
  85. package/lib/types/icons/crop.d.ts.map +1 -1
  86. package/lib/types/icons/format-painter.d.ts +2 -0
  87. package/lib/types/icons/format-painter.d.ts.map +1 -0
  88. package/lib/types/icons/index.d.ts +1 -0
  89. package/lib/types/icons/index.d.ts.map +1 -1
  90. package/package.json +4 -4
  91. package/src/core/locale.ts +2 -0
  92. package/src/feature/attachment/view/components/attachment-block.tsx +12 -7
  93. package/src/feature/attachment/view/index.ts +1 -0
  94. package/src/feature/block-edit/handle/component.tsx +5 -4
  95. package/src/feature/block-edit/handle/index.ts +1 -0
  96. package/src/feature/block-edit/index.ts +4 -1
  97. package/src/feature/block-edit/menu/api.ts +6 -1
  98. package/src/feature/block-edit/menu/component.tsx +49 -46
  99. package/src/feature/block-edit/menu/index.ts +1 -0
  100. package/src/feature/fixed-toolbar/component.tsx +3 -2
  101. package/src/feature/fixed-toolbar/config.ts +198 -146
  102. package/src/feature/fixed-toolbar/document-header.tsx +22 -12
  103. package/src/feature/fixed-toolbar/index.ts +2 -1
  104. package/src/feature/fixed-toolbar/menu-bar.tsx +15 -10
  105. package/src/feature/fixed-toolbar/outline-panel.tsx +17 -16
  106. package/src/feature/fixed-toolbar/shortcut-help-modal.tsx +3 -2
  107. package/src/feature/fixed-toolbar/view-menu-state.ts +1 -0
  108. package/src/feature/index.ts +1 -1
  109. package/src/feature/latex/inline-tooltip/component.tsx +3 -2
  110. package/src/feature/latex/inline-tooltip/view.ts +1 -0
  111. package/src/feature/loader.ts +1 -1
  112. package/src/feature/placeholder/index.ts +2 -1
  113. package/src/feature/toolbar/color.ts +77 -73
  114. package/src/feature/toolbar/component.tsx +321 -35
  115. package/src/feature/toolbar/index.ts +1 -0
  116. package/src/icons/align-center.ts +1 -1
  117. package/src/icons/bold.ts +1 -1
  118. package/src/icons/bullet-list.ts +1 -1
  119. package/src/icons/crop.ts +1 -4
  120. package/src/icons/divider.ts +1 -1
  121. package/src/icons/format-painter.ts +5 -0
  122. package/src/icons/h2.ts +1 -1
  123. package/src/icons/index.ts +1 -0
  124. package/src/icons/ordered-list.ts +1 -1
  125. package/src/icons/quote.ts +1 -1
  126. package/src/icons/strikethrough.ts +1 -1
  127. package/src/icons/text.ts +0 -1
  128. package/src/theme/common/table.css +11 -7
  129. package/src/theme/common/toolbar.css +6 -6
  130. package/src/feature/table/test.ts +0 -9
@@ -1,4 +1,5 @@
1
1
  import type { Ctx } from '@jvs-milkdown/kit/ctx'
2
+ import type { Mark } from '@jvs-milkdown/kit/prose/model'
2
3
 
3
4
  import { Icon } from '@jvs-milkdown/kit/component'
4
5
  import {
@@ -25,6 +26,7 @@ import {
25
26
  selectedRect,
26
27
  } from '@jvs-milkdown/kit/prose/tables'
27
28
  import clsx from 'clsx'
29
+ // @ts-ignore
28
30
  import {
29
31
  defineComponent,
30
32
  type Ref,
@@ -89,6 +91,7 @@ import {
89
91
  moreIcon,
90
92
  mergeCellIcon,
91
93
  splitCellIcon,
94
+ formatPainterIcon,
92
95
  } from '../../icons'
93
96
  import { keepAlive } from '../../utils/keep-alive'
94
97
  import { menuAPI } from '../block-edit/menu'
@@ -141,6 +144,17 @@ type ToolbarProps = {
141
144
  isFixed?: boolean
142
145
  }
143
146
 
147
+ interface FormatPainterState {
148
+ marks: Mark[]
149
+ align?: string | null
150
+ isPersistent: boolean
151
+ }
152
+
153
+ const formatPainterStates = new WeakMap<any, Ref<FormatPainterState | null>>()
154
+ const formatPainterHandlers = new WeakMap<any, (ev: MouseEvent) => void>()
155
+ const formatPainterIgnoreNext = new WeakMap<any, { value: boolean }>()
156
+ const cursorStyleEls = new WeakMap<any, HTMLStyleElement>()
157
+
144
158
  export const Toolbar = defineComponent<ToolbarProps>({
145
159
  props: {
146
160
  ctx: { type: Object as any, required: true },
@@ -150,7 +164,7 @@ export const Toolbar = defineComponent<ToolbarProps>({
150
164
  config: { type: Object as any, required: false },
151
165
  isFixed: { type: Boolean, required: false },
152
166
  },
153
- setup(props) {
167
+ setup(props: any) {
154
168
  const { ctx, config, isFixed } = props
155
169
 
156
170
  const showBlockMenu = ref(false)
@@ -178,6 +192,234 @@ export const Toolbar = defineComponent<ToolbarProps>({
178
192
  const tableHoverIndices = ref({ r: 0, c: 0 })
179
193
  let hideTableTimer: ReturnType<typeof setTimeout> | null = null
180
194
 
195
+ // --- Format Painter ---
196
+ let formatPainterState = formatPainterStates.get(ctx)
197
+ if (!formatPainterState) {
198
+ formatPainterState = ref<FormatPainterState | null>(null)
199
+ formatPainterStates.set(ctx, formatPainterState)
200
+ }
201
+
202
+ let formatPainterIgnore = formatPainterIgnoreNext.get(ctx)
203
+ if (!formatPainterIgnore) {
204
+ formatPainterIgnore = { value: false }
205
+ formatPainterIgnoreNext.set(ctx, formatPainterIgnore)
206
+ }
207
+
208
+ let formatPainterHandler = formatPainterHandlers.get(ctx)
209
+ if (!formatPainterHandler) {
210
+ formatPainterHandler = (ev: MouseEvent) => {
211
+ if (!formatPainterState!.value) return
212
+
213
+ if (formatPainterIgnore!.value) {
214
+ formatPainterIgnore!.value = false
215
+ return
216
+ }
217
+
218
+ const currentView = ctx.get(editorViewCtx)
219
+
220
+ // Ignore if clicked on the toolbar itself
221
+ const targetElement =
222
+ ev.target instanceof Element
223
+ ? ev.target
224
+ : (ev.target as Node).parentElement
225
+ if (
226
+ targetElement &&
227
+ targetElement.closest('.milkdown-toolbar, .milkdown-fixed-toolbar')
228
+ ) {
229
+ return
230
+ }
231
+
232
+ // Delay to let ProseMirror update its internal state.selection based on the new DOM selection
233
+ setTimeout(() => {
234
+ if (!formatPainterState!.value) return
235
+
236
+ const { state: curState, dispatch } = currentView
237
+ const { selection: curSelection, tr } = curState
238
+ const {
239
+ marks: savedMarks,
240
+ align: savedAlign,
241
+ isPersistent,
242
+ } = formatPainterState!.value
243
+
244
+ let applied = false
245
+ if (!curSelection.empty) {
246
+ savedMarks.forEach((m: any) => {
247
+ tr.addMark(curSelection.from, curSelection.to, m)
248
+ })
249
+ if (savedAlign) {
250
+ curState.doc.nodesBetween(
251
+ curSelection.from,
252
+ curSelection.to,
253
+ (node: any, pos: number) => {
254
+ if (
255
+ node.type.name === 'paragraph' ||
256
+ node.type.name === 'heading'
257
+ ) {
258
+ tr.setNodeMarkup(pos, null, {
259
+ ...node.attrs,
260
+ align: savedAlign,
261
+ })
262
+ }
263
+ }
264
+ )
265
+ }
266
+ dispatch(tr)
267
+ applied = true
268
+ } else {
269
+ if (savedMarks.length > 0) {
270
+ const $pos = curSelection.$from
271
+ if ($pos.parent.isTextblock) {
272
+ const text = $pos.parent.textContent
273
+ const offset = $pos.parentOffset
274
+ let start = offset
275
+ let end = offset
276
+ const isWordChar = (char: string) =>
277
+ !/[\s,.\-!?;:()[\]{}"'“”‘’,。!?;:()【】《》、\n\r]/.test(
278
+ char
279
+ )
280
+ while (start > 0 && isWordChar(text[start - 1])) start--
281
+ while (end < text.length && isWordChar(text[end])) end++
282
+
283
+ if (start < end) {
284
+ savedMarks.forEach((m: any) => {
285
+ tr.addMark($pos.start() + start, $pos.start() + end, m)
286
+ })
287
+ }
288
+ }
289
+ }
290
+
291
+ if (savedAlign) {
292
+ const pos = curSelection.$from.before(curSelection.$from.depth)
293
+ const node = curState.doc.nodeAt(pos)
294
+ if (
295
+ node &&
296
+ (node.type.name === 'paragraph' || node.type.name === 'heading')
297
+ ) {
298
+ tr.setNodeMarkup(pos, null, {
299
+ ...node.attrs,
300
+ align: savedAlign,
301
+ })
302
+ }
303
+ }
304
+ tr.setStoredMarks(savedMarks)
305
+ dispatch(tr)
306
+ applied = true
307
+ }
308
+
309
+ if (applied && !isPersistent) {
310
+ formatPainterState!.value = null
311
+ document.removeEventListener('pointerup', formatPainterHandler!)
312
+ }
313
+ }, 50)
314
+ }
315
+ formatPainterHandlers.set(ctx, formatPainterHandler)
316
+ }
317
+
318
+ const toggleFormatPainter = (isPersistent: boolean) => {
319
+ const view = ctx.get(editorViewCtx)
320
+ const { state } = view
321
+ const { selection } = state
322
+
323
+ let marks: Mark[] = []
324
+ if (selection.empty) {
325
+ marks = state.storedMarks
326
+ ? [...state.storedMarks]
327
+ : [...selection.$from.marks()]
328
+ } else {
329
+ let foundMarks: Mark[] | null = null
330
+ state.doc.nodesBetween(selection.from, selection.to, (node: any) => {
331
+ if (foundMarks) return false
332
+ if (node.isInline) {
333
+ foundMarks = [...node.marks]
334
+ return false
335
+ }
336
+ return undefined
337
+ })
338
+ marks = foundMarks || []
339
+ }
340
+
341
+ let align: string | null = null
342
+ let foundAlign: string | null = null
343
+ state.doc.nodesBetween(selection.from, selection.to, (node: any) => {
344
+ if (foundAlign) return false
345
+ if (
346
+ node.isBlock &&
347
+ (node.type.name === 'paragraph' || node.type.name === 'heading')
348
+ ) {
349
+ foundAlign = node.attrs.align || null
350
+ return false
351
+ }
352
+ return undefined
353
+ })
354
+
355
+ if (!foundAlign) {
356
+ const parent = selection.$from.parent
357
+ if (
358
+ parent &&
359
+ (parent.type.name === 'paragraph' || parent.type.name === 'heading')
360
+ ) {
361
+ foundAlign = parent.attrs.align || null
362
+ }
363
+ }
364
+ align = foundAlign
365
+
366
+ formatPainterState!.value = { marks, align, isPersistent }
367
+ document.removeEventListener('pointerup', formatPainterHandler!)
368
+
369
+ setTimeout(() => {
370
+ document.addEventListener('pointerup', formatPainterHandler!)
371
+ }, 0)
372
+ }
373
+
374
+ const handleFormatPainterClick = (e: MouseEvent) => {
375
+ e.preventDefault()
376
+ e.stopPropagation()
377
+ if (formatPainterState!.value) {
378
+ formatPainterState!.value = null
379
+ document.removeEventListener('pointerup', formatPainterHandler!)
380
+ } else {
381
+ formatPainterIgnore!.value = true
382
+ toggleFormatPainter(false)
383
+ }
384
+ }
385
+
386
+ const handleFormatPainterDblClick = (e: MouseEvent) => {
387
+ e.preventDefault()
388
+ e.stopPropagation()
389
+ toggleFormatPainter(true)
390
+ }
391
+
392
+ watch(
393
+ () => formatPainterState!.value,
394
+ (newVal: any) => {
395
+ if (newVal) {
396
+ let cursorStyleEl = cursorStyleEls.get(ctx)
397
+ if (!cursorStyleEl) {
398
+ cursorStyleEl = document.createElement('style')
399
+ // We must replace currentColor with a valid color for data URI (e.g., black or #363B4C)
400
+ const encodedSvg = encodeURIComponent(
401
+ formatPainterIcon.replace(/currentColor/g, '#363B4C')
402
+ )
403
+ cursorStyleEl.innerHTML = `.milkdown .ProseMirror, .milkdown .ProseMirror * { cursor: url("data:image/svg+xml;utf8,${encodedSvg}") 0 16, auto !important; }`
404
+ document.head.appendChild(cursorStyleEl)
405
+ cursorStyleEls.set(ctx, cursorStyleEl)
406
+ }
407
+ } else {
408
+ const cursorStyleEl = cursorStyleEls.get(ctx)
409
+ if (cursorStyleEl) {
410
+ cursorStyleEl.remove()
411
+ cursorStyleEls.delete(ctx)
412
+ }
413
+ }
414
+ }
415
+ )
416
+
417
+ onUnmounted(() => {
418
+ // Clean up pointerup listener only if it's the last toolbar instance?
419
+ // Actually, keep it simple, let it be cleaned up when format painter is turned off.
420
+ // But if the component unmounts, maybe we should remove the style to be safe.
421
+ })
422
+
181
423
  // --- Overflow menu (fixed toolbar only) ---
182
424
  const toolbarContainerRef = ref<HTMLElement | null>(null)
183
425
  const overflowVisibleCount = ref(Infinity)
@@ -624,7 +866,7 @@ export const Toolbar = defineComponent<ToolbarProps>({
624
866
  })
625
867
  modified = true
626
868
  } else {
627
- state.doc.nodesBetween(from, to, (node, pos) => {
869
+ state.doc.nodesBetween(from, to, (node: any, pos: number) => {
628
870
  if (
629
871
  node.isBlock &&
630
872
  (node.type.name === 'paragraph' || node.type.name === 'heading')
@@ -651,7 +893,7 @@ export const Toolbar = defineComponent<ToolbarProps>({
651
893
  const tr = state.tr
652
894
  let modified = false
653
895
 
654
- state.doc.nodesBetween(from, to, (node, pos) => {
896
+ state.doc.nodesBetween(from, to, (node: any, pos: number) => {
655
897
  if (
656
898
  node.isBlock &&
657
899
  (node.type.name === 'paragraph' || node.type.name === 'heading')
@@ -913,21 +1155,21 @@ export const Toolbar = defineComponent<ToolbarProps>({
913
1155
  borderRadius: '4px',
914
1156
  margin: '2px 8px',
915
1157
  }}
916
- onPointerdown={(e) => {
1158
+ onPointerdown={(e: PointerEvent) => {
917
1159
  e.preventDefault()
918
1160
  e.stopPropagation()
919
1161
  }}
920
- onPointerup={(e) => {
1162
+ onPointerup={(e: PointerEvent) => {
921
1163
  e.preventDefault()
922
1164
  e.stopPropagation()
923
1165
  if (!disabled) onClick()
924
1166
  }}
925
- onMouseenter={(e) => {
1167
+ onMouseenter={(e: MouseEvent) => {
926
1168
  if (!disabled)
927
1169
  (e.currentTarget as HTMLElement).style.backgroundColor =
928
1170
  'var(--crepe-color-hover)'
929
1171
  }}
930
- onMouseleave={(e) => {
1172
+ onMouseleave={(e: MouseEvent) => {
931
1173
  ;(e.currentTarget as HTMLElement).style.backgroundColor =
932
1174
  'transparent'
933
1175
  }}
@@ -972,10 +1214,10 @@ export const Toolbar = defineComponent<ToolbarProps>({
972
1214
  return () => {
973
1215
  // Find the formatting items
974
1216
  const formatItems: ToolbarItem[] = []
975
- toolbarGroupInfo.value.forEach((group) => {
1217
+ toolbarGroupInfo.value.forEach((group: any) => {
976
1218
  if (group.items) {
977
1219
  // We only want inline format items: bold, italic, strikethrough, underline, link, code
978
- group.items.forEach((item) => {
1220
+ group.items.forEach((item: any) => {
979
1221
  // Add logic if it's formatting or inline. We know exactly what's inside.
980
1222
  // But actually we injected underline in functionGroup and formattingGroup.
981
1223
  if (
@@ -999,7 +1241,7 @@ export const Toolbar = defineComponent<ToolbarProps>({
999
1241
 
1000
1242
  // Ensure proper formatting tools are visible
1001
1243
  const nonHeadingGroups = toolbarGroupInfo.value.filter(
1002
- (group) => (group as any).key !== 'heading'
1244
+ (group: any) => (group as any).key !== 'heading'
1003
1245
  )
1004
1246
 
1005
1247
  const isSectionOverflowed = (idx: number) =>
@@ -1026,7 +1268,7 @@ export const Toolbar = defineComponent<ToolbarProps>({
1026
1268
  position: 'relative',
1027
1269
  display: isSectionOverflowed(0) ? 'none' : 'flex',
1028
1270
  alignItems: 'center',
1029
- padding: '0 8px',
1271
+ padding: '0 6px',
1030
1272
  flexShrink: 0,
1031
1273
  }}
1032
1274
  >
@@ -1041,6 +1283,50 @@ export const Toolbar = defineComponent<ToolbarProps>({
1041
1283
  </span>
1042
1284
  </div>
1043
1285
 
1286
+ {/* Format Painter */}
1287
+ <div
1288
+ style={{
1289
+ position: 'relative',
1290
+ display: isSectionOverflowed(0) ? 'none' : 'flex',
1291
+ flexShrink: 0,
1292
+ }}
1293
+ >
1294
+ <button
1295
+ type="button"
1296
+ class={clsx(
1297
+ 'toolbar-item',
1298
+ formatPainterState!.value && 'active'
1299
+ )}
1300
+ onPointerdown={(e: PointerEvent) => {
1301
+ e.preventDefault()
1302
+ handleFormatPainterClick(e as any)
1303
+ }}
1304
+ onDblclick={handleFormatPainterDblClick}
1305
+ title={i18n(ctx, 'toolbar.formatPainter') || 'Format Painter'}
1306
+ style={{
1307
+ display: 'flex',
1308
+ alignItems: 'center',
1309
+ justifyContent: 'center',
1310
+ backgroundColor: formatPainterState!.value
1311
+ ? 'var(--crepe-color-selected, var(--crepe-color-hover))'
1312
+ : undefined,
1313
+ }}
1314
+ >
1315
+ <span
1316
+ style={
1317
+ {
1318
+ display: 'inline-flex',
1319
+ alignItems: 'center',
1320
+ '--toolbar-icon-color': formatPainterState!.value
1321
+ ? 'var(--crepe-color-primary)'
1322
+ : '#363B4C',
1323
+ } as any
1324
+ }
1325
+ innerHTML={formatPainterIcon}
1326
+ />
1327
+ </button>
1328
+ </div>
1329
+
1044
1330
  {/* Section 1: Divider */}
1045
1331
  <div
1046
1332
  class="divider"
@@ -1061,7 +1347,7 @@ export const Toolbar = defineComponent<ToolbarProps>({
1061
1347
  position: 'relative',
1062
1348
  display: isSectionOverflowed(2) ? 'none' : 'flex',
1063
1349
  alignItems: 'center',
1064
- padding: '0 8px',
1350
+ padding: '0 6px',
1065
1351
  minWidth: '50px',
1066
1352
  flexShrink: 0,
1067
1353
  }}
@@ -1107,7 +1393,7 @@ export const Toolbar = defineComponent<ToolbarProps>({
1107
1393
  position: 'relative',
1108
1394
  display: isSectionOverflowed(3) ? 'none' : 'flex',
1109
1395
  alignItems: 'center',
1110
- padding: '0 8px',
1396
+ padding: '0 6px',
1111
1397
  minWidth: '40px',
1112
1398
  flexShrink: 0,
1113
1399
  }}
@@ -1150,7 +1436,7 @@ export const Toolbar = defineComponent<ToolbarProps>({
1150
1436
  position: 'relative',
1151
1437
  display: isSectionOverflowed(5) ? 'none' : 'flex',
1152
1438
  alignItems: 'center',
1153
- padding: '0 8px',
1439
+ padding: '0 6px',
1154
1440
  flexShrink: 0,
1155
1441
  }}
1156
1442
  >
@@ -1212,8 +1498,8 @@ export const Toolbar = defineComponent<ToolbarProps>({
1212
1498
  {/* Formatting Tools (sections 7+) */}
1213
1499
  {(() => {
1214
1500
  let sectionIdx = 7
1215
- return nonHeadingGroups.map((group, groupIndex) => {
1216
- const items = group.items.map((item) => {
1501
+ return nonHeadingGroups.map((group: any, groupIndex: number) => {
1502
+ const items = group.items.map((item: any) => {
1217
1503
  const idx = sectionIdx
1218
1504
  sectionIdx++
1219
1505
  const isTable = item.icon === tableIcon
@@ -1234,7 +1520,7 @@ export const Toolbar = defineComponent<ToolbarProps>({
1234
1520
  'toolbar-item',
1235
1521
  ctx && checkActive(item.active) && 'active'
1236
1522
  )}
1237
- onPointerdown={(e) => {
1523
+ onPointerdown={(e: PointerEvent) => {
1238
1524
  if (isTable) {
1239
1525
  e.preventDefault()
1240
1526
  e.stopPropagation()
@@ -1313,7 +1599,7 @@ export const Toolbar = defineComponent<ToolbarProps>({
1313
1599
  flexShrink: 0,
1314
1600
  cursor: 'pointer',
1315
1601
  }}
1316
- onClick={(e) => {
1602
+ onClick={(e: MouseEvent) => {
1317
1603
  e.preventDefault()
1318
1604
  e.stopPropagation()
1319
1605
  if (showOverflowMenu.value) {
@@ -1374,8 +1660,8 @@ export const Toolbar = defineComponent<ToolbarProps>({
1374
1660
  }}
1375
1661
  />
1376
1662
  {blockGroups.value.groups
1377
- .flatMap((group) => group.items)
1378
- .filter((item) => {
1663
+ .flatMap((group: any) => group.items)
1664
+ .filter((item: any) => {
1379
1665
  const key = (item as any).key
1380
1666
  if (isFixed) {
1381
1667
  return [
@@ -1403,7 +1689,7 @@ export const Toolbar = defineComponent<ToolbarProps>({
1403
1689
  'code',
1404
1690
  ].includes(key)
1405
1691
  })
1406
- .map((item) => {
1692
+ .map((item: any) => {
1407
1693
  const isActive =
1408
1694
  activeBlockItem.value === item.icon ||
1409
1695
  (activeBlockItem.value === null && item.icon === textIcon)
@@ -1839,25 +2125,25 @@ export const Toolbar = defineComponent<ToolbarProps>({
1839
2125
  margin: '2px 8px',
1840
2126
  opacity: supported ? 1 : 0.45,
1841
2127
  }}
1842
- onPointerdown={(e) => {
2128
+ onPointerdown={(e: PointerEvent) => {
1843
2129
  e.preventDefault()
1844
2130
  e.stopPropagation()
1845
2131
  }}
1846
- onPointerup={(e) => {
2132
+ onPointerup={(e: PointerEvent) => {
1847
2133
  e.preventDefault()
1848
2134
  e.stopPropagation()
1849
2135
  if (supported) {
1850
2136
  setFontFamily(font.value)
1851
2137
  }
1852
2138
  }}
1853
- onMouseenter={(e) => {
2139
+ onMouseenter={(e: MouseEvent) => {
1854
2140
  if (supported) {
1855
2141
  ;(
1856
2142
  e.currentTarget as HTMLElement
1857
2143
  ).style.backgroundColor = 'var(--crepe-color-hover)'
1858
2144
  }
1859
2145
  }}
1860
- onMouseleave={(e) => {
2146
+ onMouseleave={(e: MouseEvent) => {
1861
2147
  ;(e.currentTarget as HTMLElement).style.backgroundColor =
1862
2148
  'transparent'
1863
2149
  }}
@@ -1951,20 +2237,20 @@ export const Toolbar = defineComponent<ToolbarProps>({
1951
2237
  borderRadius: '4px',
1952
2238
  margin: '2px 8px',
1953
2239
  }}
1954
- onPointerdown={(e) => {
2240
+ onPointerdown={(e: PointerEvent) => {
1955
2241
  e.preventDefault()
1956
2242
  e.stopPropagation()
1957
2243
  }}
1958
- onPointerup={(e) => {
2244
+ onPointerup={(e: PointerEvent) => {
1959
2245
  e.preventDefault()
1960
2246
  e.stopPropagation()
1961
2247
  setFontSize(size.value)
1962
2248
  }}
1963
- onMouseenter={(e) => {
2249
+ onMouseenter={(e: MouseEvent) => {
1964
2250
  ;(e.currentTarget as HTMLElement).style.backgroundColor =
1965
2251
  'var(--crepe-color-hover)'
1966
2252
  }}
1967
- onMouseleave={(e) => {
2253
+ onMouseleave={(e: MouseEvent) => {
1968
2254
  ;(e.currentTarget as HTMLElement).style.backgroundColor =
1969
2255
  'transparent'
1970
2256
  }}
@@ -2070,11 +2356,11 @@ export const Toolbar = defineComponent<ToolbarProps>({
2070
2356
  onPointerenter={() => {
2071
2357
  tableHoverIndices.value = { r: r + 1, c: c + 1 }
2072
2358
  }}
2073
- onPointerdown={(e) => {
2359
+ onPointerdown={(e: PointerEvent) => {
2074
2360
  e.preventDefault()
2075
2361
  e.stopPropagation()
2076
2362
  }}
2077
- onPointerup={(e) => {
2363
+ onPointerup={(e: PointerEvent) => {
2078
2364
  e.preventDefault()
2079
2365
  e.stopPropagation()
2080
2366
  runTableInsert(r + 1, c + 1)
@@ -2137,7 +2423,7 @@ export const Toolbar = defineComponent<ToolbarProps>({
2137
2423
  maxWidth: '360px',
2138
2424
  width: 'max-content',
2139
2425
  }}
2140
- onClick={(e) => e.stopPropagation()}
2426
+ onClick={(e: MouseEvent) => e.stopPropagation()}
2141
2427
  >
2142
2428
  {(() => {
2143
2429
  const cutoff = overflowVisibleCount.value
@@ -2156,11 +2442,11 @@ export const Toolbar = defineComponent<ToolbarProps>({
2156
2442
  type="button"
2157
2443
  class={clsx('toolbar-item', isActive && 'active')}
2158
2444
  title={title}
2159
- onPointerdown={(e) => {
2445
+ onPointerdown={(e: PointerEvent) => {
2160
2446
  e.preventDefault()
2161
2447
  e.stopPropagation()
2162
2448
  }}
2163
- onClick={(e) => {
2449
+ onClick={(e: MouseEvent) => {
2164
2450
  e.preventDefault()
2165
2451
  e.stopPropagation()
2166
2452
  if (onClick) onClick(e)
@@ -2283,7 +2569,7 @@ export const Toolbar = defineComponent<ToolbarProps>({
2283
2569
  ctx ? checkActive(item.active) : false,
2284
2570
  isTable
2285
2571
  ? undefined
2286
- : (e) => {
2572
+ : (_e: MouseEvent) => {
2287
2573
  item.onRun(ctx)
2288
2574
  showOverflowMenu.value = false
2289
2575
  },
@@ -12,6 +12,7 @@ import {
12
12
  tooltipFactory,
13
13
  } from '@jvs-milkdown/kit/plugin/tooltip'
14
14
  import { TextSelection, AllSelection } from '@jvs-milkdown/kit/prose/state'
15
+ // @ts-ignore
15
16
  import { createApp, ref, shallowRef, type App, type ShallowRef } from 'vue'
16
17
 
17
18
  import type { GroupBuilder } from '../../utils'
@@ -12,4 +12,4 @@ export const alignCenterIconActive = `
12
12
  height="24"
13
13
  viewBox="0 0 1024 1024">
14
14
  <path d="M0 0m0 0l1024 0q0 0 0 0l0 1024q0 0 0 0l-1024 0q0 0 0 0l0-1024q0 0 0 0Z" fill="#1E6FFF" fill-opacity="0" p-id="52154"></path><path d="M128 192m46.464 0l675.072 0q46.464 0 46.464 46.464l0 0q0 46.464-46.464 46.464l-675.072 0q-46.464 0-46.464-46.464l0 0q0-46.464 46.464-46.464Z" fill="#1E6FFF" p-id="52155"></path><path d="M288 497.536c0 25.664 20.8 46.464 46.464 46.464h355.072a46.464 46.464 0 0 0 0-92.928H334.464c-25.664 0-46.464 20.8-46.464 46.464zM224 750.464c0 25.6 20.8 46.464 46.464 46.464h483.072a46.464 46.464 0 0 0 0-92.928H270.464c-25.664 0-46.464 20.8-46.464 46.464z" fill="#1E6FFF" p-id="52156"></path></svg>
15
- `
15
+ `
package/src/icons/bold.ts CHANGED
@@ -9,4 +9,4 @@ export const boldIconActive = `
9
9
  height="24">
10
10
  <path d="M0 0m0 0l1024 0q0 0 0 0l0 1024q0 0 0 0l-1024 0q0 0 0 0l0-1024q0 0 0 0Z" fill="#1E6FFF" fill-opacity="0" p-id="50045"></path><path d="M340.032 468.032H544c62.592 0 113.28-49.28 113.28-110.08 0-60.672-50.688-109.952-113.28-109.952H340.032v220.032z m453.248 197.952c0 109.376-91.392 198.016-204.032 198.016H249.28v-704H544c112.64 0 204.032 88.64 204.032 198.016 0 52.992-21.632 101.12-56.512 136.64 60.8 34.24 101.76 98.176 101.76 171.328zM340.032 555.968v220.032h249.216c62.592 0 113.28-49.28 113.28-110.016 0-60.736-50.688-110.016-113.28-110.016H340.032z" fill="#1E6FFF" p-id="50046"></path>
11
11
  </svg>
12
- `
12
+ `
@@ -6,4 +6,4 @@ export const bulletListIcon = `
6
6
  export const bulletListIconActive = `
7
7
  <svg viewBox="0 0 1024 1024" width="24" height="24">
8
8
  <path d="M375.104 150.592h501.312a45.184 45.184 0 1 1 0 90.368H375.04a45.184 45.184 0 1 1 0-90.368M170.112 128h1.024a67.776 67.776 0 1 1 0 135.552h-1.024a67.776 67.776 0 1 1 0-135.552m0 316.224h1.024a67.776 67.776 0 0 1 0 135.552h-1.024a67.776 67.776 0 0 1 0-135.552m0 316.224h1.024a67.776 67.776 0 1 1 0 135.552h-1.024a67.776 67.776 0 1 1 0-135.552m204.992-293.632h501.312a45.184 45.184 0 1 1 0 90.368H375.04a45.184 45.184 0 1 1 0-90.368m0 316.224h501.312a45.184 45.184 0 1 1 0 90.368H375.04a45.184 45.184 0 1 1 0-90.368" fill="#1E6FFF" p-id="53364"></path></svg>
9
- `
9
+ `
package/src/icons/crop.ts CHANGED
@@ -1,6 +1,3 @@
1
1
  export const cropIcon = `
2
- <svg viewBox="0 0 1024 1024" height="24px" width="24px">
3
- <path d="M192 128v128H128v64h192V128h-128z m0 0h64v128h-64V128z m512 0v128h192v64H640V128h64z m-512 640H128v-64h192v192h-128v-128z m0 128h64v-128h-64v128z m512 0v-128h192v64h-128v128h-64z m0 0h64v-128h-64v128z" fill="currentColor"/>
4
- <path d="M256 256h512v512H256z" fill="none" stroke="currentColor" stroke-width="64"/>
5
- </svg>
2
+ <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7429" width="24px" height="24px"><path d="M279.296 46.592a46.592 46.592 0 1 0-93.1328 0v139.5712H46.592a46.592 46.592 0 1 0 0 93.1328h139.6224v512c0 25.7024 20.8384 46.5408 46.592 46.5408h512v139.6224a46.592 46.592 0 0 0 93.0816 0v-139.6224h139.6224a46.592 46.592 0 1 0 0-93.1328H279.296V46.592z m139.6224 139.5712a46.592 46.592 0 1 0 0 93.1328h325.7856v325.7856a46.592 46.592 0 0 0 93.1328 0V232.704a46.592 46.592 0 0 0-46.592-46.5408H418.9696z" fill="currentColor" p-id="7430"></path></svg>
6
3
  `
@@ -6,4 +6,4 @@ export const dividerIcon = `
6
6
  export const dividerIconActive = `
7
7
  <svg viewBox="0 0 1024 1024" width="24" height="24">
8
8
  <path d="M888.384 560H135.68c-23.808 0-39.68-19.2-39.68-48s15.872-48 39.68-48H888.32c23.808 0 39.68 19.2 39.68 48s-15.872 48-39.68 48zM571.52 896H452.608c-23.808 0-39.68-19.2-39.68-48s15.872-48 39.68-48h118.848c23.744 0 39.68 19.2 39.68 48s-15.936 48-39.68 48z m316.928 0h-118.848c-23.744 0-39.616-19.2-39.616-48s15.872-48 39.68-48h118.784c23.808 0 39.68 19.2 39.68 48s-15.872 48-39.68 48zM571.52 224H452.608c-23.808 0-39.68-19.2-39.68-48S428.8 128 452.672 128h118.848c23.744 0 39.68 19.2 39.68 48s-15.936 48-39.68 48zM254.528 896H135.68c-23.808 0-39.68-19.2-39.68-48s15.872-48 39.68-48h118.848c23.744 0 39.616 19.2 39.616 48s-15.872 48-39.68 48z m0-672H135.68c-23.808 0-39.68-19.2-39.68-48S111.872 128 135.68 128h118.848c23.744 0 39.616 19.2 39.616 48s-15.872 48-39.68 48z m633.856 0h-118.848c-23.744 0-39.616-19.2-39.616-48s15.872-48 39.68-48h118.784c23.808 0 39.68 19.2 39.68 48s-15.872 48-39.68 48z" fill="#1E6FFF" p-id="53064"></path></svg>
9
- `
9
+ `
@@ -0,0 +1,5 @@
1
+ export const formatPainterIcon = `
2
+ <svg viewBox="0 0 1024 1024" version="1.1" width="20" height="20">
3
+ <path d="M454.698667 302.506667H323.84q-23.04 0-41.6 13.824-18.517333 13.824-25.130667 35.968l-31.36 104.704q-10.112 33.621333 10.794667 61.738666 11.52 15.445333 27.221333 22.442667 0.853333 46.165333-0.810666 105.6-1.408 51.84-58.368 138.496-23.338667 35.413333-3.413334 72.96 20.053333 37.76 62.677334 37.76h477.994666q39.509333 0 69.077334-26.325333 29.738667-26.453333 34.090666-65.962667 10.24-93.738667 10.24-155.946667 0-57.088-8.618666-103.68 20.949333-6.058667 35.285333-25.344 20.864-28.117333 10.837333-61.738666l-31.36-104.704q-6.656-22.144-25.173333-35.968-18.517333-13.781333-41.6-13.781334h-130.858667V197.802667q0-28.885333-20.437333-49.365334Q622.933333 128 594.090667 128H524.373333q-28.842667 0-49.237333 20.48-20.437333 20.437333-20.437333 49.322667v104.746666z m68.992 41.728q0.682667-3.370667 0.682666-6.784V197.802667h69.717334V337.493333q0 3.413333 0.64 6.826667 0.682667 3.370667 2.005333 6.528 1.28 3.2 3.2 6.058667 1.92 2.816 4.352 5.248 2.389333 2.474667 5.248 4.352 2.901333 1.92 6.058667 3.242666 3.157333 1.322667 6.528 1.962667 3.328 0.682667 6.826666 0.682667h165.674667l31.36 104.746666H292.48l31.36-104.746666h165.717333q3.413333 0 6.826667-0.64 3.328-0.682667 6.528-2.005334 3.157333-1.322667 5.973333-3.242666 2.858667-1.877333 5.290667-4.352 2.432-2.432 4.309333-5.248 1.92-2.901333 3.242667-6.058667 1.322667-3.157333 1.962667-6.570667z m252.501333 202.666666q9.429333 43.648 9.429333 100.864 0 58.368-9.813333 148.352-1.450667 12.757333-11.093333 21.376-9.813333 8.704-22.826667 8.704h-100.309333q22.058667-81.493333 22.058666-174.592h-69.674666q0 94.421333-24.96 174.592h-121.514667q41.941333-81.450667 41.941333-174.592H419.754667q0 94.378667-52.992 174.592H263.808q-0.725333 0-1.109333-0.725333-0.469333-0.981333 0.042666-1.749333 67.968-103.253333 69.888-175.018667 1.536-56.533333 0.938667-101.802667H776.106667z" fill="currentColor" p-id="11997"></path>
4
+ </svg>
5
+ `
package/src/icons/h2.ts CHANGED
@@ -7,4 +7,4 @@ export const h2Icon = `
7
7
  export const h2IconActive = `
8
8
  <svg viewBox="0 0 1024 1024" width="24"
9
9
  height="24"><path d="M768 426.688c-47.104 0-85.312 38.208-85.312 85.312v21.312a42.688 42.688 0 1 1-85.376 0V512A170.688 170.688 0 0 1 768 341.312h7.36a163.328 163.328 0 0 1 115.456 278.912L743.04 768H896a42.688 42.688 0 0 1 0 85.312h-256a42.688 42.688 0 0 1-30.144-72.832l220.672-220.672a78.016 78.016 0 0 0-55.168-133.12H768z m-640-256c23.552 0 42.688 19.072 42.688 42.624v256h256v-256a42.688 42.688 0 1 1 85.312 0v597.376a42.688 42.688 0 1 1-85.312 0v-256h-256v256a42.688 42.688 0 1 1-85.376 0V213.312c0-23.552 19.136-42.624 42.688-42.624z" fill="#1E6FFF" p-id="50643"></path></svg>
10
- `
10
+ `
@@ -52,3 +52,4 @@ export * from './keyboard'
52
52
  export * from './word-wrap'
53
53
  export * from './crop'
54
54
  export * from './border'
55
+ export * from './format-painter'
@@ -5,4 +5,4 @@ export const orderedListIcon = `
5
5
  `
6
6
  export const orderedListIconActive = `
7
7
  <svg viewBox="0 0 1024 1024" width="24" height="24"><path d="M133.184 376.448a56.064 56.064 0 0 1 42.176 17.92 61.184 61.184 0 0 1 16.32 42.752 61.056 61.056 0 0 1-11.84 36.864l-39.872 53.888h27.328c11.776 0 21.696 7.872 23.872 18.56l0.512 4.608c0 12.8-10.88 23.168-24.32 23.168H100.352a23.04 23.04 0 0 1-23.552-22.4v-9.344c0-3.84 1.28-7.616 3.584-10.752l61.76-83.264a41.216 41.216 0 0 0 1.536-2.432l1.728-3.264c0.448-1.92 0.64-3.84 0.64-5.824a16.832 16.832 0 0 0-3.264-10.752c-1.92-2.24-4.224-3.264-7.936-3.392a9.984 9.984 0 0 0-7.808 3.2c-1.92 1.984-3.328 4.672-4.736 10.112l-1.792 3.84A18.432 18.432 0 0 1 105.088 448l-12.672-0.128a16.32 16.32 0 0 1-14.976-18.176l1.344-7.552a63.36 63.36 0 0 1 14.4-27.904 53.44 53.44 0 0 1 40-17.792z m10.24 308.864c20.608 0.192 37.632 6.272 50.432 18.304a56.704 56.704 0 0 1 13.44 65.792 44.288 44.288 0 0 1-8.32 11.264l-0.64 0.448 0.896 0.704a62.016 62.016 0 0 1 5.12 5.632l4.032 6.208c4.224 8 6.4 16.832 6.4 25.856a58.304 58.304 0 0 1-20.8 46.208c-13.184 11.456-30.08 17.28-50.24 17.408-15.872 0-30.144-4.608-42.432-13.632a61.44 61.44 0 0 1-21.952-30.912l-0.512-2.624-0.128-1.856a17.28 17.28 0 0 1 17.664-16.768h19.84l10.048 6.016 2.112 4.16 2.752 3.648 2.88 2.752c2.56 1.92 6.272 3.072 11.52 3.072A19.52 19.52 0 0 0 159.232 832c3.2-2.56 4.864-6.4 4.992-11.712-0.128-5.952-1.92-9.92-5.12-12.608a21.568 21.568 0 0 0-14.336-4.48l-4.608-0.448a21.888 21.888 0 0 1-18.432-21.248v-1.664a20.48 20.48 0 0 1 20.928-19.904c6.976 0 11.776-1.472 14.976-4.352a11.584 11.584 0 0 0 4.288-9.472 13.312 13.312 0 0 0-4.48-10.24 20.096 20.096 0 0 0-13.184-4.352 17.28 17.28 0 0 0-10.176 3.2c-2.112 1.408-3.712 3.392-5.76 7.68l-2.048 3.328a19.52 19.52 0 0 1-14.976 6.72l-19.392-0.256a15.424 15.424 0 0 1-12.736-18.816l2.048-6.784a60.544 60.544 0 0 1 17.984-25.152c12.224-10.432 27.008-15.872 44.032-16.128h0.064zM148.928 89.6c10.816 0 19.52 8.384 19.52 18.688v153.408c0 14.208-12.032 25.664-26.88 25.664a26.304 26.304 0 0 1-26.944-25.6V145.664l-8.192 4.8a20.16 20.16 0 0 1-6.592 2.368l-3.52 0.32a19.072 19.072 0 0 1-19.584-18.624v-11.328c0-7.04 4.224-13.568 10.88-16.704l31.36-14.976a20.288 20.288 0 0 1 8.768-2.048h21.184z m746.816 54.848c28.352 0 51.392 19.712 51.392 44.096 0 24.32-23.04 44.16-51.392 44.16H330.24c-28.352 0-51.392-19.84-51.392-44.16 0-24.384 23.04-44.16 51.392-44.16h565.504z m0 303.36c28.352 0 51.392 19.776 51.392 44.16 0 24.32-23.04 44.096-51.392 44.096H330.24c-28.352 0-51.392-19.84-51.392-44.16 0-24.32 23.04-44.096 51.392-44.096h565.504z m0 303.424c28.352 0 51.392 19.712 51.392 44.16 0 24.32-23.04 44.096-51.392 44.096H330.24c-28.352 0-51.392-19.84-51.392-44.16 0-24.32 23.04-44.096 51.392-44.096h565.504z" fill="#1E6FFF" p-id="52914"></path></svg>
8
- `
8
+ `
@@ -7,4 +7,4 @@ export const quoteIconActive = `
7
7
  <svg viewBox="0 0 1024 1024" width="24" height="24">
8
8
  <path d="M169.856 776.832c-48.064-51.2-73.856-108.608-73.856-201.728 0-153.6 100.864-292.288 251.008-368.768a37.696 37.696 0 0 1 48.64 14.08 42.24 42.24 0 0 1-13.44 57.92C258.368 359.104 231.936 456.256 220.8 519.488c25.088-12.992 57.856-17.536 90.048-14.528 84.16 7.808 150.528 77.12 150.528 163.2A163.52 163.52 0 0 1 298.048 832c-50.048 0-97.92-22.976-128.192-55.168z m466.56 0c-48-51.2-73.856-108.608-73.856-201.728 0-153.6 100.864-292.288 251.072-368.768a37.696 37.696 0 0 1 48.64 14.08 42.24 42.24 0 0 1-13.44 57.92c-123.84 80.768-150.272 177.92-161.408 241.152 25.024-12.992 57.856-17.536 89.984-14.528 84.224 7.808 150.592 77.12 150.592 163.2A163.52 163.52 0 0 1 764.672 832c-50.112 0-97.92-22.976-128.256-55.168z" fill="#1E6FFF" p-id="52764"></path>
9
9
  </svg>
10
- `
10
+ `