@bytechain.cn/colamd 1.5.0

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 (157) hide show
  1. package/.github/workflows/release.yml +66 -0
  2. package/.trae/documents/fix-mermaid-colors-and-sankey.md +50 -0
  3. package/CLAUDE.md +87 -0
  4. package/LICENSE +21 -0
  5. package/README.md +540 -0
  6. package/README_CN.md +543 -0
  7. package/demo.md +486 -0
  8. package/dist/main/index.js +735 -0
  9. package/dist/preload/index.js +71 -0
  10. package/dist/renderer/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  11. package/dist/renderer/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  12. package/dist/renderer/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  13. package/dist/renderer/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  14. package/dist/renderer/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  15. package/dist/renderer/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  16. package/dist/renderer/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  17. package/dist/renderer/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  18. package/dist/renderer/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  19. package/dist/renderer/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  20. package/dist/renderer/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  21. package/dist/renderer/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  22. package/dist/renderer/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  23. package/dist/renderer/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  24. package/dist/renderer/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  25. package/dist/renderer/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  26. package/dist/renderer/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  27. package/dist/renderer/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  28. package/dist/renderer/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  29. package/dist/renderer/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  30. package/dist/renderer/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  31. package/dist/renderer/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  32. package/dist/renderer/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  33. package/dist/renderer/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  34. package/dist/renderer/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  35. package/dist/renderer/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  36. package/dist/renderer/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  37. package/dist/renderer/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  38. package/dist/renderer/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  39. package/dist/renderer/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  40. package/dist/renderer/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  41. package/dist/renderer/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  42. package/dist/renderer/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  43. package/dist/renderer/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  44. package/dist/renderer/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  45. package/dist/renderer/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  46. package/dist/renderer/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  47. package/dist/renderer/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  48. package/dist/renderer/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  49. package/dist/renderer/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  50. package/dist/renderer/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  51. package/dist/renderer/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  52. package/dist/renderer/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  53. package/dist/renderer/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  54. package/dist/renderer/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  55. package/dist/renderer/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  56. package/dist/renderer/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  57. package/dist/renderer/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  58. package/dist/renderer/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  59. package/dist/renderer/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  60. package/dist/renderer/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  61. package/dist/renderer/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  62. package/dist/renderer/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  63. package/dist/renderer/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  64. package/dist/renderer/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  65. package/dist/renderer/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  66. package/dist/renderer/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  67. package/dist/renderer/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  68. package/dist/renderer/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  69. package/dist/renderer/assets/arc-tTbbM8LO.js +131 -0
  70. package/dist/renderer/assets/architectureDiagram-3BPJPVTR-CEgYow6c.js +8720 -0
  71. package/dist/renderer/assets/blockDiagram-GPEHLZMM-LHyVtPwW.js +3825 -0
  72. package/dist/renderer/assets/c4Diagram-AAUBKEIU-C1P1eJrf.js +2482 -0
  73. package/dist/renderer/assets/channel-upve91Tq.js +7 -0
  74. package/dist/renderer/assets/chunk-2J33WTMH-lag2vhq9.js +24 -0
  75. package/dist/renderer/assets/chunk-4BX2VUAB-BXJ8Ggh-.js +16 -0
  76. package/dist/renderer/assets/chunk-55IACEB6-CiBpxRa1.js +13 -0
  77. package/dist/renderer/assets/chunk-727SXJPM-ODeKQFXC.js +2016 -0
  78. package/dist/renderer/assets/chunk-AQP2D5EJ-BK7xJolB.js +1953 -0
  79. package/dist/renderer/assets/chunk-FMBD7UC4-BxpCZPtz.js +19 -0
  80. package/dist/renderer/assets/chunk-ND2GUHAM-CqqaU9Ue.js +116 -0
  81. package/dist/renderer/assets/chunk-QZHKN3VN-Biq_K124.js +19 -0
  82. package/dist/renderer/assets/classDiagram-4FO5ZUOK-Cq95X99o.js +23 -0
  83. package/dist/renderer/assets/classDiagram-v2-Q7XG4LA2-Cq95X99o.js +23 -0
  84. package/dist/renderer/assets/cose-bilkent-S5V4N54A-XasiD0bu.js +4942 -0
  85. package/dist/renderer/assets/cytoscape.esm-CpHeHM5e.js +30269 -0
  86. package/dist/renderer/assets/dagre-BM42HDAG-Nq84Gfx4.js +705 -0
  87. package/dist/renderer/assets/defaultLocale-B2RvLBDe.js +206 -0
  88. package/dist/renderer/assets/diagram-2AECGRRQ-DwuB1GWt.js +301 -0
  89. package/dist/renderer/assets/diagram-5GNKFQAL-C2tgeI1h.js +169 -0
  90. package/dist/renderer/assets/diagram-KO2AKTUF-D5KzjNBc.js +632 -0
  91. package/dist/renderer/assets/diagram-LMA3HP47-C12xHS1c.js +212 -0
  92. package/dist/renderer/assets/diagram-OG6HWLK6-CnxI9oEa.js +851 -0
  93. package/dist/renderer/assets/erDiagram-TEJ5UH35-D_uPaKwn.js +1227 -0
  94. package/dist/renderer/assets/flowDiagram-I6XJVG4X-B6q_1-tE.js +2332 -0
  95. package/dist/renderer/assets/ganttDiagram-6RSMTGT7-CFo7ifF9.js +3720 -0
  96. package/dist/renderer/assets/gitGraphDiagram-PVQCEYII-WSexHTnq.js +1373 -0
  97. package/dist/renderer/assets/graph-DyX_9f6d.js +1988 -0
  98. package/dist/renderer/assets/index-DW7LS8C1.js +72292 -0
  99. package/dist/renderer/assets/index-dyHEFYvY.css +2184 -0
  100. package/dist/renderer/assets/infoDiagram-5YYISTIA-DaeJdLRq.js +31 -0
  101. package/dist/renderer/assets/init-ZxktEp_H.js +16 -0
  102. package/dist/renderer/assets/ishikawaDiagram-YF4QCWOH-DDCZc35f.js +967 -0
  103. package/dist/renderer/assets/journeyDiagram-JHISSGLW-BEdmpAgl.js +1255 -0
  104. package/dist/renderer/assets/kanban-definition-UN3LZRKU-BEFtQcFb.js +1052 -0
  105. package/dist/renderer/assets/layout-CAJgQHdw.js +2610 -0
  106. package/dist/renderer/assets/linear-B2ggJ8Am.js +340 -0
  107. package/dist/renderer/assets/mindmap-definition-RKZ34NQL-DSxVgHB5.js +1180 -0
  108. package/dist/renderer/assets/ordinal-DSZU4PqD.js +76 -0
  109. package/dist/renderer/assets/pieDiagram-4H26LBE5-CwYoJBuL.js +246 -0
  110. package/dist/renderer/assets/quadrantDiagram-W4KKPZXB-CST9Fvg9.js +1344 -0
  111. package/dist/renderer/assets/requirementDiagram-4Y6WPE33-DtrH52jS.js +1204 -0
  112. package/dist/renderer/assets/sankeyDiagram-5OEKKPKP-ca1tPzJ_.js +1274 -0
  113. package/dist/renderer/assets/sequenceDiagram-3UESZ5HK-Dfp1EJZ7.js +4514 -0
  114. package/dist/renderer/assets/stateDiagram-AJRCARHV-Bha2QoNB.js +450 -0
  115. package/dist/renderer/assets/stateDiagram-v2-BHNVJYJU-DWgFUYu1.js +21 -0
  116. package/dist/renderer/assets/timeline-definition-PNZ67QCA-C3h_-OTj.js +1596 -0
  117. package/dist/renderer/assets/vennDiagram-CIIHVFJN-DFzjSrZi.js +2486 -0
  118. package/dist/renderer/assets/wardley-L42UT6IY-Cx-VbqoS.js +30699 -0
  119. package/dist/renderer/assets/wardleyDiagram-YWT4CUSO-S2D9XqX6.js +975 -0
  120. package/dist/renderer/assets/xychartDiagram-2RQKCTM6-Cfxigbts.js +1932 -0
  121. package/dist/renderer/index.html +19 -0
  122. package/docs/agent-diff-view.md +48 -0
  123. package/electron-builder.yml +57 -0
  124. package/electron.vite.config.ts +30 -0
  125. package/package.json +40 -0
  126. package/resources/entitlements.mac.plist +12 -0
  127. package/resources/icon.icns +0 -0
  128. package/resources/icon.png +0 -0
  129. package/resources/icon.svg +23 -0
  130. package/resources/templates/slides/icon.png +0 -0
  131. package/resources/templates/slides/slides-template.md +74 -0
  132. package/resources/templates/slides/template.html +535 -0
  133. package/scripts/afterPack.js +13 -0
  134. package/src/main/index.ts +881 -0
  135. package/src/preload/index.ts +110 -0
  136. package/src/renderer/editor/editor.ts +204 -0
  137. package/src/renderer/editor/html-view.ts +15 -0
  138. package/src/renderer/editor/plugins/index.ts +76 -0
  139. package/src/renderer/editor/plugins/math-plugin.ts +297 -0
  140. package/src/renderer/editor/plugins/mermaid-plugin-custom.css +431 -0
  141. package/src/renderer/editor/plugins/mermaid-plugin-dark.css +428 -0
  142. package/src/renderer/editor/plugins/mermaid-plugin-elegant.css +443 -0
  143. package/src/renderer/editor/plugins/mermaid-plugin-newsprint.css +208 -0
  144. package/src/renderer/editor/plugins/mermaid-plugin.css +111 -0
  145. package/src/renderer/editor/plugins/mermaid-plugin.ts +679 -0
  146. package/src/renderer/env.d.ts +7 -0
  147. package/src/renderer/index.html +18 -0
  148. package/src/renderer/main.ts +303 -0
  149. package/src/renderer/themes/base.css +509 -0
  150. package/src/renderer/themes/theme-manager.ts +40 -0
  151. package/themes/README.md +280 -0
  152. package/themes/elegant.css +664 -0
  153. package/themes/guizang.css +732 -0
  154. package/tsconfig.json +14 -0
  155. package/tsconfig.main.json +11 -0
  156. package/tsconfig.preload.json +11 -0
  157. package/tsconfig.renderer.json +12 -0
@@ -0,0 +1,110 @@
1
+ import { contextBridge, ipcRenderer, webUtils } from 'electron'
2
+
3
+ export interface ElectronAPI {
4
+ openFile: () => Promise<{ path: string; content: string } | null>
5
+ openFilePath: (path: string) => Promise<{ path: string; content: string } | null>
6
+ saveFile: (content: string) => Promise<boolean>
7
+ saveFileAs: (content: string) => Promise<boolean>
8
+ exportPDF: () => Promise<boolean>
9
+ exportHTML: (html: string) => Promise<boolean>
10
+ newSlides: () => Promise<string | null>
11
+ openAsSlides: (content: string) => Promise<boolean>
12
+ loadCustomTheme: () => Promise<{ name: string; css: string } | null>
13
+ loadThemeCSS: (fileName: string) => Promise<string | null>
14
+ getPathForFile: (file: File) => string
15
+ openExternal: (url: string) => void
16
+ onFileChanged: (callback: (content: string) => void) => void
17
+ onNewFile: (callback: () => void) => void
18
+ onFileOpened: (callback: (data: { path: string; content: string }) => void) => void
19
+ onMenuOpen: (callback: () => void) => void
20
+ onMenuSave: (callback: () => void) => void
21
+ onMenuSaveAs: (callback: () => void) => void
22
+ onMenuExportPDF: (callback: () => void) => void
23
+ onMenuExportHTML: (callback: () => void) => void
24
+ onMenuNewSlides: (callback: () => void) => void
25
+ onMenuOpenAsSlides: (callback: () => void) => void
26
+ onNewSlidesContent: (callback: (content: string) => void) => void
27
+ onSetTheme: (callback: (theme: string) => void) => void
28
+ onSetCustomCSS: (callback: (css: string) => void) => void
29
+ exportSlides: (content: string) => Promise<boolean>
30
+ onMenuExportSlides: (callback: () => void) => void
31
+ onAgentActivity: (callback: (state: string) => void) => void
32
+ registerPlugins: (plugins: Array<{ id: string; name: string; enabled: boolean }>) => Promise<boolean>
33
+ syncPluginState: (id: string, enabled: boolean) => Promise<void>
34
+ onMenuTogglePlugin: (callback: (id: string) => void) => void
35
+ onMenuImportTheme: (callback: () => void) => void
36
+ exportFile: (dataUrl: string, defaultName: string) => Promise<boolean>
37
+ }
38
+
39
+ contextBridge.exposeInMainWorld('electronAPI', {
40
+ openFile: () => ipcRenderer.invoke('open-file'),
41
+ openFilePath: (path: string) => ipcRenderer.invoke('open-file-path', path),
42
+ saveFile: (content: string) => ipcRenderer.invoke('save-file', content),
43
+ saveFileAs: (content: string) => ipcRenderer.invoke('save-file-as', content),
44
+ exportPDF: () => ipcRenderer.invoke('export-pdf'),
45
+ exportHTML: (html: string) => ipcRenderer.invoke('export-html', html),
46
+ exportSlides: (content: string) => ipcRenderer.invoke('export-slides', content),
47
+ newSlides: () => ipcRenderer.invoke('new-slides'),
48
+ openAsSlides: (content: string) => ipcRenderer.invoke('open-as-slides', content),
49
+ loadCustomTheme: () => ipcRenderer.invoke('load-custom-theme'),
50
+ loadThemeCSS: (fileName: string) => ipcRenderer.invoke('load-theme-css', fileName),
51
+ getPathForFile: (file: File) => webUtils.getPathForFile(file),
52
+ openExternal: (url: string) => ipcRenderer.send('open-external', url),
53
+ onFileChanged: (callback: (content: string) => void) => {
54
+ ipcRenderer.on('file-changed', (_event, content) => callback(content))
55
+ },
56
+ onNewFile: (callback: () => void) => {
57
+ ipcRenderer.on('new-file', () => callback())
58
+ },
59
+ onFileOpened: (callback: (data: { path: string; content: string }) => void) => {
60
+ ipcRenderer.on('file-opened', (_event, data) => callback(data))
61
+ },
62
+ onMenuOpen: (callback: () => void) => {
63
+ ipcRenderer.on('menu-open', () => callback())
64
+ },
65
+ onMenuSave: (callback: () => void) => {
66
+ ipcRenderer.on('menu-save', () => callback())
67
+ },
68
+ onMenuSaveAs: (callback: () => void) => {
69
+ ipcRenderer.on('menu-save-as', () => callback())
70
+ },
71
+ onMenuExportPDF: (callback: () => void) => {
72
+ ipcRenderer.on('menu-export-pdf', () => callback())
73
+ },
74
+ onMenuExportHTML: (callback: () => void) => {
75
+ ipcRenderer.on('menu-export-html', () => callback())
76
+ },
77
+ onMenuNewSlides: (callback: () => void) => {
78
+ ipcRenderer.on('menu-new-slides', () => callback())
79
+ },
80
+ onMenuOpenAsSlides: (callback: () => void) => {
81
+ ipcRenderer.on('menu-open-as-slides', () => callback())
82
+ },
83
+ onNewSlidesContent: (callback: (content: string) => void) => {
84
+ ipcRenderer.on('new-slides-content', (_event, content) => callback(content))
85
+ },
86
+ onSetTheme: (callback: (theme: string) => void) => {
87
+ ipcRenderer.on('set-theme', (_event, theme) => callback(theme))
88
+ },
89
+ onSetCustomCSS: (callback: (css: string) => void) => {
90
+ ipcRenderer.on('set-custom-css', (_event, css) => callback(css))
91
+ },
92
+ onMenuImportTheme: (callback: () => void) => {
93
+ ipcRenderer.on('menu-import-theme', () => callback())
94
+ },
95
+ onMenuExportSlides: (callback: () => void) => {
96
+ ipcRenderer.on('menu-export-slides', () => callback())
97
+ },
98
+ onAgentActivity: (callback: (state: string) => void) => {
99
+ ipcRenderer.on('agent-activity', (_event, state) => callback(state))
100
+ },
101
+ registerPlugins: (plugins: Array<{ id: string; name: string; enabled: boolean }>) =>
102
+ ipcRenderer.invoke('register-plugins', plugins),
103
+ syncPluginState: (id: string, enabled: boolean) =>
104
+ ipcRenderer.invoke('sync-plugin-state', id, enabled),
105
+ onMenuTogglePlugin: (callback: (id: string) => void) => {
106
+ ipcRenderer.on('menu-toggle-plugin', (_event, id) => callback(id))
107
+ },
108
+ exportFile: (dataUrl: string, defaultName: string) =>
109
+ ipcRenderer.invoke('save-export-file', dataUrl, defaultName),
110
+ } satisfies ElectronAPI)
@@ -0,0 +1,204 @@
1
+ import { Editor, rootCtx, defaultValueCtx, editorViewCtx, serializerCtx, remarkPluginsCtx, nodeViewCtx, schemaCtx } from '@milkdown/kit/core'
2
+ import { DOMSerializer } from '@milkdown/kit/prose/model'
3
+ import { EditorView } from '@milkdown/kit/prose/view'
4
+ import remarkBreaks from 'remark-breaks'
5
+ import { commonmark } from '@milkdown/kit/preset/commonmark'
6
+ import { gfm } from '@milkdown/kit/preset/gfm'
7
+ import { history } from '@milkdown/kit/plugin/history'
8
+ import { listener, listenerCtx } from '@milkdown/kit/plugin/listener'
9
+ import { clipboard } from '@milkdown/kit/plugin/clipboard'
10
+ import { replaceAll } from '@milkdown/kit/utils'
11
+ import { htmlView } from './html-view'
12
+ import { getAllPluginModules } from './plugins'
13
+
14
+ import '@milkdown/kit/prose/view/style/prosemirror.css'
15
+
16
+ let editorInstance: Editor | null = null
17
+
18
+ const inlineStyles: Record<string, string> = {
19
+ 'h1': 'font-size:1.8em;font-weight:700;margin:1em 0 .5em;padding-bottom:.3em;border-bottom:1px solid #eee;',
20
+ 'h2': 'font-size:1.4em;font-weight:600;margin:1em 0 .5em;padding-bottom:.25em;border-bottom:1px solid #eee;',
21
+ 'h3': 'font-size:1.2em;font-weight:600;margin:.8em 0 .4em;',
22
+ 'h4': 'font-weight:600;margin:.8em 0 .4em;',
23
+ 'h5': 'font-weight:600;margin:.8em 0 .4em;',
24
+ 'h6': 'font-weight:600;margin:.8em 0 .4em;',
25
+ 'p': 'margin:.5em 0;line-height:1.75;',
26
+ 'strong': 'font-weight:600;',
27
+ 'a': 'color:#0969da;text-decoration:none;',
28
+ 'code': 'background:rgba(175,184,193,0.2);padding:2px 6px;border-radius:3px;font-size:.875em;font-family:Menlo,Monaco,monospace;',
29
+ 'pre': 'background:#f6f8fa;padding:16px;border-radius:6px;overflow-x:auto;margin:1em 0;',
30
+ 'blockquote': 'border-left:4px solid #ddd;padding-left:16px;margin:1em 0;color:#666;',
31
+ 'ul': 'padding-left:24px;margin:.5em 0;',
32
+ 'ol': 'padding-left:24px;margin:.5em 0;',
33
+ 'li': 'margin:.25em 0;',
34
+ 'table': 'border-collapse:collapse;width:100%;margin:1em 0;',
35
+ 'th': 'border:1px solid #ddd;padding:8px 12px;text-align:left;font-weight:600;background:#f6f8fa;',
36
+ 'td': 'border:1px solid #ddd;padding:8px 12px;text-align:left;',
37
+ 'hr': 'border:none;border-top:2px solid #ddd;margin:2em 0;',
38
+ 'img': 'max-width:100%;',
39
+ }
40
+
41
+ function enhanceClipboard(e: ClipboardEvent): void {
42
+ const html = e.clipboardData?.getData('text/html')
43
+ if (!html) return
44
+
45
+ const doc = new DOMParser().parseFromString(html, 'text/html')
46
+
47
+ for (const [tag, style] of Object.entries(inlineStyles)) {
48
+ doc.querySelectorAll(tag).forEach((el) => {
49
+ ;(el as HTMLElement).setAttribute('style', style)
50
+ })
51
+ }
52
+
53
+ doc.querySelectorAll('pre code').forEach((el) => {
54
+ ;(el as HTMLElement).setAttribute('style', 'background:none;padding:0;font-size:.875em;line-height:1.6;font-family:Menlo,Monaco,monospace;')
55
+ })
56
+
57
+ doc.querySelectorAll('.math-inline').forEach((el) => {
58
+ ;(el as HTMLElement).setAttribute('style', 'display:inline;padding:2px 4px;border-radius:3px;background:rgba(175,184,193,0.2);')
59
+ })
60
+ doc.querySelectorAll('.math-block').forEach((el) => {
61
+ ;(el as HTMLElement).setAttribute('style', 'display:block;padding:16px;margin:1em 0;border-radius:6px;background:#f6f8fa;text-align:center;overflow-x:auto;')
62
+ })
63
+
64
+ doc.querySelectorAll('.mermaid-block').forEach((el) => {
65
+ ;(el as HTMLElement).setAttribute('style', 'display:block;padding:16px;margin:1em 0;border-radius:6px;background:#f6f8fa;border:1px solid #d0d7de;')
66
+ })
67
+ doc.querySelectorAll('.mermaid-preview svg').forEach((el) => {
68
+ ;(el as HTMLElement).setAttribute('style', 'max-width:100%;height:auto;')
69
+ })
70
+
71
+ e.clipboardData?.setData('text/html', doc.body.innerHTML)
72
+ }
73
+
74
+ const defaultContent = `# Welcome to ColaMD\n\nStart typing here...\n`
75
+
76
+ export async function createEditor(
77
+ rootId: string,
78
+ onChange?: (markdown: string) => void
79
+ ): Promise<Editor> {
80
+ const root = document.getElementById(rootId)
81
+ if (!root) throw new Error(`Element #${rootId} not found`)
82
+
83
+ const pluginModules = getAllPluginModules()
84
+
85
+ let builder = Editor.make()
86
+ .config((ctx) => {
87
+ ctx.set(rootCtx, root)
88
+ ctx.set(defaultValueCtx, defaultContent)
89
+ ctx.set(remarkPluginsCtx, [
90
+ ...pluginModules.map((m) => m.info.remarkPlugin),
91
+ { plugin: remarkBreaks, options: undefined },
92
+ ])
93
+ if (onChange) {
94
+ ctx.get(listenerCtx).markdownUpdated((_ctx, markdown) => {
95
+ onChange(markdown)
96
+ })
97
+ }
98
+ })
99
+ .use(commonmark)
100
+ .use(gfm)
101
+ .use(history)
102
+ .use(listener)
103
+ .use(clipboard)
104
+ .use(htmlView)
105
+
106
+ for (const mod of pluginModules) {
107
+ for (const p of mod.milkdownPlugins) {
108
+ builder = builder.use(p)
109
+ }
110
+ }
111
+
112
+ editorInstance = await builder.create()
113
+
114
+ editorInstance.action((ctx) => {
115
+ const nvs = ctx.get(nodeViewCtx)
116
+ const schema = ctx.get(schemaCtx)
117
+ const fixed = nvs.map((nv: any, i: number) => {
118
+ if (nv[0] != null) return nv
119
+ const viewFn = nv[1]
120
+ const fallbackNames = ['math_inline', 'math_block', 'mermaid_block']
121
+ const name = fallbackNames[i - 1]
122
+ if (name && schema.nodes[name]) {
123
+ return [name, viewFn]
124
+ }
125
+ return nv
126
+ })
127
+ ctx.set(nodeViewCtx, fixed)
128
+
129
+ const oldView = ctx.get(editorViewCtx)
130
+ const rootEl = ctx.get(rootCtx)
131
+ const nodeViews = Object.fromEntries(fixed)
132
+ const newView = new EditorView(rootEl, {
133
+ state: oldView.state,
134
+ nodeViews,
135
+ dispatchTransaction: oldView.props.dispatchTransaction!,
136
+ })
137
+ oldView.destroy()
138
+ rootEl.appendChild(newView.dom)
139
+ ctx.set(editorViewCtx, newView)
140
+ })
141
+
142
+ root.addEventListener('copy', enhanceClipboard)
143
+ root.addEventListener('cut', enhanceClipboard)
144
+
145
+ root.addEventListener('click', (e) => {
146
+ if (!(e.metaKey || e.ctrlKey)) return
147
+ const link = (e.target as HTMLElement).closest('a')
148
+ if (!link) return
149
+ const href = link.getAttribute('href')
150
+ if (href) {
151
+ e.preventDefault()
152
+ window.electronAPI.openExternal(href)
153
+ }
154
+ })
155
+
156
+ return editorInstance
157
+ }
158
+
159
+ export function getMarkdown(): string {
160
+ if (!editorInstance) return ''
161
+ let markdown = ''
162
+ editorInstance.action((ctx) => {
163
+ const serializer = ctx.get(serializerCtx)
164
+ const view = ctx.get(editorViewCtx)
165
+ markdown = serializer(view.state.doc)
166
+ })
167
+ return markdown
168
+ }
169
+
170
+ export function getHTML(): string {
171
+ if (!editorInstance) return ''
172
+ let html = ''
173
+ editorInstance.action((ctx) => {
174
+ const view = ctx.get(editorViewCtx)
175
+ const div = document.createElement('div')
176
+ const fragment = DOMSerializer.fromSchema(view.state.schema).serializeFragment(view.state.doc.content)
177
+ div.appendChild(fragment)
178
+ html = div.innerHTML
179
+ })
180
+ return html
181
+ }
182
+
183
+ export function setMarkdown(content: string): void {
184
+ if (!editorInstance) return
185
+ editorInstance.action(replaceAll(content))
186
+ }
187
+
188
+ export function togglePluginMode(nodeTypes: string[], mode: 'rendered' | 'raw'): void {
189
+ if (!editorInstance) return
190
+ editorInstance.action((ctx) => {
191
+ const view = ctx.get(editorViewCtx)
192
+ const { state, dispatch } = view
193
+ const tr = state.tr
194
+ const nodeTypeSet = new Set(nodeTypes)
195
+
196
+ state.doc.descendants((node, pos) => {
197
+ if (nodeTypeSet.has(node.type.name)) {
198
+ tr.setNodeMarkup(pos, undefined, { ...node.attrs, mode })
199
+ }
200
+ })
201
+
202
+ dispatch(tr)
203
+ })
204
+ }
@@ -0,0 +1,15 @@
1
+ import { $view } from '@milkdown/kit/utils'
2
+ import { htmlSchema } from '@milkdown/kit/preset/commonmark'
3
+ import type { NodeViewConstructor } from '@milkdown/kit/prose/view'
4
+
5
+ export const htmlView = $view(htmlSchema.node, (): NodeViewConstructor => {
6
+ return (node) => {
7
+ const dom = document.createElement('span')
8
+ dom.classList.add('milkdown-html-inline')
9
+ dom.innerHTML = node.attrs.value as string
10
+ return {
11
+ dom,
12
+ stopEvent: () => true
13
+ }
14
+ }
15
+ })
@@ -0,0 +1,76 @@
1
+ import type { MilkdownPlugin } from '@milkdown/ctx'
2
+ import type { Plugin } from 'unified'
3
+
4
+ export interface ExportCapability {
5
+ label: string
6
+ defaultName: string
7
+ filter: { name: string; extensions: string[] }
8
+ execute: (element: HTMLElement) => Promise<string | null>
9
+ }
10
+
11
+ export interface RendererPlugin {
12
+ id: string
13
+ name: string
14
+ enabled: boolean
15
+ remarkPlugin: { plugin: Plugin; options?: any }
16
+ nodeTypes: string[]
17
+ onInit?: () => void
18
+ onThemeChange?: (theme: string) => void
19
+ exportCapabilities?: ExportCapability[]
20
+ }
21
+
22
+ export interface PluginModule {
23
+ info: RendererPlugin
24
+ milkdownPlugins: MilkdownPlugin[]
25
+ }
26
+
27
+ const modules: PluginModule[] = []
28
+
29
+ export function registerPluginModule(m: PluginModule): void {
30
+ modules.push(m)
31
+ }
32
+
33
+ export function getAllPluginModules(): PluginModule[] {
34
+ return modules
35
+ }
36
+
37
+ export function getAllPlugins(): RendererPlugin[] {
38
+ return modules.map(m => m.info)
39
+ }
40
+
41
+ export function getEnabledPlugins(): RendererPlugin[] {
42
+ return modules.filter(m => m.info.enabled).map(m => m.info)
43
+ }
44
+
45
+ export function getPlugin(id: string): RendererPlugin | undefined {
46
+ return modules.find(m => m.info.id === id)?.info
47
+ }
48
+
49
+ export function togglePlugin(id: string): void {
50
+ const m = modules.find(m => m.info.id === id)
51
+ if (m) m.info.enabled = !m.info.enabled
52
+ }
53
+
54
+ export function findPluginBySelector(selector: string): { plugin: RendererPlugin; capability: ExportCapability } | undefined {
55
+ for (const m of modules) {
56
+ if (!m.info.enabled || !m.info.exportCapabilities?.length) continue
57
+ for (const nodeType of m.info.nodeTypes) {
58
+ if (selector.includes(nodeType.replace(/_/g, '-'))) {
59
+ return { plugin: m.info, capability: m.info.exportCapabilities[0] }
60
+ }
61
+ }
62
+ }
63
+ return undefined
64
+ }
65
+
66
+ export function findExportCapabilities(selector: string): ExportCapability[] {
67
+ for (const m of modules) {
68
+ if (!m.info.enabled || !m.info.exportCapabilities?.length) continue
69
+ for (const nodeType of m.info.nodeTypes) {
70
+ if (selector.includes(nodeType.replace(/_/g, '-'))) {
71
+ return m.info.exportCapabilities
72
+ }
73
+ }
74
+ }
75
+ return []
76
+ }