@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,18 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self' data: https:; img-src 'self' data: blob: https: http: file:; connect-src 'self';">
7
+ <title>ColaMD</title>
8
+ </head>
9
+ <body>
10
+ <div id="titlebar">
11
+ <div id="agent-dot"></div>
12
+ <button id="slides-btn" title="Open as Slides">&#9654;</button>
13
+ </div>
14
+ <div id="editor"></div>
15
+ <textarea id="source-editor" spellcheck="false"></textarea>
16
+ <script type="module" src="./main.ts"></script>
17
+ </body>
18
+ </html>
@@ -0,0 +1,303 @@
1
+ import { createEditor, getMarkdown, getHTML, setMarkdown, togglePluginMode } from './editor/editor'
2
+ import { applyTheme, loadSavedTheme } from './themes/theme-manager'
3
+ import { getAllPlugins, togglePlugin, findPluginBySelector, findExportCapabilities } from './editor/plugins'
4
+ import './themes/base.css'
5
+
6
+ const pluginModules = import.meta.glob<{ default?: unknown }>('./editor/plugins/*-plugin.ts', { eager: true })
7
+ Object.keys(pluginModules)
8
+
9
+ function isSlidesContent(content: string): boolean {
10
+ return /^---\s*\n[\s\S]*?(kicker|chip):/m.test(content)
11
+ }
12
+
13
+ let sourceModeActive = false
14
+ const editorEl = () => document.getElementById('editor') as HTMLElement
15
+ const sourceEl = () => document.getElementById('source-editor') as HTMLTextAreaElement
16
+ const slidesBtnEl = () => document.getElementById('slides-btn') as HTMLButtonElement
17
+
18
+ function enterSourceMode(content: string): void {
19
+ sourceModeActive = true
20
+ editorEl().classList.add('hidden')
21
+ const ta = sourceEl()
22
+ ta.classList.add('visible')
23
+ ta.value = content
24
+ slidesBtnEl().classList.add('visible')
25
+ }
26
+
27
+ function exitSourceMode(): void {
28
+ sourceModeActive = false
29
+ editorEl().classList.remove('hidden')
30
+ sourceEl().classList.remove('visible')
31
+ slidesBtnEl().classList.remove('visible')
32
+ }
33
+
34
+ function setContent(content: string): void {
35
+ if (isSlidesContent(content)) {
36
+ enterSourceMode(content)
37
+ } else {
38
+ exitSourceMode()
39
+ setMarkdown(content)
40
+ }
41
+ }
42
+
43
+ function getContent(): string {
44
+ if (sourceModeActive) return sourceEl().value
45
+ return getMarkdown()
46
+ }
47
+
48
+ async function init(): Promise<void> {
49
+ const api = window.electronAPI
50
+ const savedTheme = loadSavedTheme()
51
+ applyTheme(savedTheme)
52
+
53
+ if (savedTheme.startsWith('custom:')) {
54
+ const fileName = savedTheme.slice(7)
55
+ const css = await api.loadThemeCSS(fileName)
56
+ if (css) applyTheme(savedTheme, css)
57
+ }
58
+
59
+ await createEditor('editor')
60
+
61
+ // Run plugin init hooks (mermaid initialize, etc.)
62
+ for (const p of getAllPlugins()) p.onInit?.()
63
+
64
+ // Send plugin list to main process for menu
65
+ api.registerPlugins(getAllPlugins().map((p) => ({ id: p.id, name: p.name, enabled: p.enabled })))
66
+
67
+ // Handle plugin toggle from menu
68
+ api.onMenuTogglePlugin((id) => {
69
+ togglePlugin(id)
70
+ const p = getAllPlugins().find((x) => x.id === id)
71
+ if (p) {
72
+ const mode = p.enabled ? 'rendered' : 'raw'
73
+ togglePluginMode(p.nodeTypes, mode)
74
+ api.syncPluginState(p.id, p.enabled)
75
+ }
76
+ })
77
+
78
+ // Slides button — open as slides
79
+ slidesBtnEl().addEventListener('click', () => api.openAsSlides(getContent()))
80
+
81
+ api.onMenuOpen(async () => {
82
+ const result = await api.openFile()
83
+ if (result) setContent(result.content)
84
+ })
85
+
86
+ function syncRawEdits(): void {
87
+ document.querySelectorAll('.math-block-raw, .math-inline-raw, .mermaid-source').forEach((el) => {
88
+ if ((el as HTMLElement).matches(':focus')) (el as HTMLElement).blur()
89
+ })
90
+ }
91
+
92
+ function restoreRenderedMode(): void {
93
+ for (const p of getAllPlugins()) {
94
+ if (!p.enabled) {
95
+ p.enabled = true
96
+ togglePluginMode(p.nodeTypes, 'rendered')
97
+ api.syncPluginState(p.id, true)
98
+ }
99
+ }
100
+ }
101
+
102
+ api.onMenuSave(async () => {
103
+ syncRawEdits()
104
+ const ok = await api.saveFile(getContent())
105
+ if (ok) restoreRenderedMode()
106
+ })
107
+ api.onMenuSaveAs(async () => {
108
+ syncRawEdits()
109
+ const ok = await api.saveFileAs(getContent())
110
+ if (ok) restoreRenderedMode()
111
+ })
112
+ api.onMenuExportPDF(async () => {
113
+ syncRawEdits()
114
+ restoreRenderedMode()
115
+ await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)))
116
+ await api.exportPDF()
117
+ })
118
+ api.onMenuExportHTML(() => {
119
+ const s = getComputedStyle(document.body)
120
+ const v = (name: string) => s.getPropertyValue(name).trim()
121
+ const bgColor = v('--bg-color')
122
+ const textColor = v('--text-color')
123
+ const textMuted = v('--text-muted')
124
+ const borderColor = v('--border-color')
125
+ const linkColor = v('--link-color')
126
+ const codeBg = v('--code-bg')
127
+ const codeBlockBg = v('--code-block-bg')
128
+ const codeBlockText = v('--code-block-text') || textColor
129
+ const blockquoteBorder = v('--blockquote-border')
130
+ const blockquoteBg = v('--blockquote-bg') || 'transparent'
131
+ const tableHeaderBg = v('--table-header-bg')
132
+ const selectionBg = v('--selection-bg')
133
+
134
+ const editor = document.querySelector('#editor .ProseMirror')
135
+ const fontFamily = editor ? getComputedStyle(editor).fontFamily : '-apple-system,BlinkMacSystemFont,sans-serif'
136
+
137
+ const getElColor = (selector: string, fallback: string): string => {
138
+ const el = document.querySelector(`#editor .ProseMirror ${selector}`)
139
+ return el ? getComputedStyle(el).color : fallback
140
+ }
141
+ const strongColor = getElColor('strong', textColor)
142
+ const codeColor = getElColor('code', textColor)
143
+
144
+ const html = `<!DOCTYPE html>
145
+ <html><head><meta charset="utf-8"><title>ColaMD Export</title>
146
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.46/dist/katex.min.css">
147
+ <style>
148
+ body{max-width:780px;margin:40px auto;padding:20px;font-family:${fontFamily};line-height:1.75;background:${bgColor};color:${textColor}}
149
+ h1{font-size:2em;font-weight:700;border-bottom:1px solid ${borderColor};padding-bottom:.3em}
150
+ h2{font-size:1.5em;font-weight:600;border-bottom:1px solid ${borderColor};padding-bottom:.25em}
151
+ h3{font-size:1.25em;font-weight:600}
152
+ strong{color:${strongColor}}
153
+ a{color:${linkColor};text-decoration:none}
154
+ code{background:${codeBg};color:${codeColor};padding:2px 6px;border-radius:3px;font-size:.875em;font-family:'SF Mono','Fira Code',Menlo,monospace}
155
+ pre{background:${codeBlockBg};color:${codeBlockText};padding:16px;border-radius:6px;overflow-x:auto;margin:1em 0}
156
+ pre code{background:none;padding:0;color:inherit}
157
+ blockquote{border-left:4px solid ${blockquoteBorder};background:${blockquoteBg};padding-left:16px;margin:1em 0;color:${textMuted}}
158
+ table{border-collapse:collapse;width:100%;margin:1em 0}
159
+ th,td{border:1px solid ${borderColor};padding:8px 12px}
160
+ th{background:${tableHeaderBg};font-weight:600}
161
+ hr{border:none;border-top:2px solid ${borderColor};margin:2em 0}
162
+ img{max-width:100%}
163
+ ::selection{background:${selectionBg}}
164
+ .math-inline{display:inline;padding:2px 4px;border-radius:3px;background:${codeBg}}
165
+ .math-block{display:block;padding:16px;margin:1em 0;border-radius:6px;background:${codeBlockBg};text-align:center;overflow-x:auto}
166
+ .mermaid-block{display:block;padding:16px;margin:1em 0;border-radius:6px;background:${codeBlockBg};border:1px solid ${borderColor}}
167
+ .mermaid-preview{display:flex;justify-content:center;align-items:center}
168
+ .mermaid-preview svg{max-width:100%;height:auto}
169
+ .mermaid-preview svg .label text,.mermaid-preview svg .nodeLabel text,.mermaid-preview svg .state-title,.mermaid-preview svg .state-description,.mermaid-preview svg .pieTitleText,.mermaid-preview svg .titleText{transform:translateY(-2px)}.mermaid-preview svg .nodeLabel,.mermaid-preview svg .edgeLabel{display:inline-block;position:relative;top:-2px}
170
+ </style>
171
+ </head><body>${getHTML()}</body></html>`
172
+ api.exportHTML(html)
173
+ })
174
+
175
+ api.onNewFile(() => { exitSourceMode(); setMarkdown('') })
176
+ api.onFileOpened((data) => setContent(data.content))
177
+ api.onFileChanged((content) => {
178
+ if (sourceModeActive) {
179
+ sourceEl().value = content
180
+ } else {
181
+ setMarkdown(content)
182
+ }
183
+ })
184
+ api.onSetTheme((theme) => {
185
+ applyTheme(theme)
186
+ // Notify all plugins of theme change
187
+ for (const p of getAllPlugins()) p.onThemeChange?.(theme)
188
+ // Force re-render all enabled plugin nodes so they pick up the new theme
189
+ for (const p of getAllPlugins()) {
190
+ if (p.enabled) {
191
+ togglePluginMode(p.nodeTypes, 'raw')
192
+ requestAnimationFrame(() => togglePluginMode(p.nodeTypes, 'rendered'))
193
+ }
194
+ }
195
+ })
196
+ api.onSetCustomCSS((css) => {
197
+ const theme = loadSavedTheme()
198
+ applyTheme(theme, css)
199
+ })
200
+
201
+ api.onMenuNewSlides(async () => {
202
+ await api.newSlides()
203
+ })
204
+
205
+ api.onNewSlidesContent((content) => {
206
+ enterSourceMode(content)
207
+ })
208
+
209
+ api.onMenuOpenAsSlides(async () => {
210
+ await api.openAsSlides(getContent())
211
+ })
212
+
213
+ api.onMenuExportSlides(async () => {
214
+ await api.exportSlides(getContent())
215
+ })
216
+
217
+ api.onMenuImportTheme(async () => {
218
+ const result = await api.loadCustomTheme()
219
+ if (result) applyTheme(`custom:${result.name}`, result.css)
220
+ })
221
+
222
+ const agentDot = document.getElementById('agent-dot')
223
+ api.onAgentActivity((state) => {
224
+ if (agentDot) agentDot.className = state === 'idle' ? '' : state
225
+ })
226
+
227
+ // --- Export via plugin capabilities (context menu) ---
228
+ let exportTarget: HTMLElement | null = null
229
+ let exportCapability: import('./editor/plugins').ExportCapability | null = null
230
+ let contextMenu: HTMLDivElement | null = null
231
+
232
+ function getContextMenu(): HTMLDivElement {
233
+ if (!contextMenu) {
234
+ contextMenu = document.createElement('div')
235
+ contextMenu.id = 'cola-context-menu'
236
+ document.body.appendChild(contextMenu)
237
+ }
238
+ return contextMenu
239
+ }
240
+
241
+ function hideContextMenu(): void {
242
+ if (contextMenu) contextMenu.style.display = 'none'
243
+ }
244
+
245
+ async function executeExport(cap: import('./editor/plugins').ExportCapability): Promise<void> {
246
+ const target = exportTarget
247
+ if (!target) return
248
+ hideContextMenu()
249
+ try {
250
+ const dataUrl = await cap.execute(target)
251
+ if (!dataUrl) return
252
+ await api.exportFile(dataUrl, cap.defaultName)
253
+ } catch (err) {
254
+ console.error('Export failed:', err)
255
+ }
256
+ }
257
+
258
+ document.addEventListener('contextmenu', (e) => {
259
+ if ((e.target as Element).matches('textarea, input, [contenteditable="true"]')) return
260
+ const target = (e.target as Element).closest('.mermaid-block, .math-block') as HTMLElement | null
261
+ if (!target) return
262
+ const capabilities = findExportCapabilities(target.className)
263
+ if (!capabilities.length) return
264
+ e.preventDefault()
265
+ exportTarget = target
266
+
267
+ const menu = getContextMenu()
268
+ menu.innerHTML = capabilities.map((cap) =>
269
+ `<div class="cola-menu-item" data-export="${cap.label}">${cap.label}</div>`
270
+ ).join('')
271
+ menu.style.left = e.clientX + 'px'
272
+ menu.style.top = e.clientY + 'px'
273
+ menu.style.display = 'block'
274
+
275
+ menu.querySelectorAll('.cola-menu-item').forEach((item) => {
276
+ item.addEventListener('click', (ev) => {
277
+ ev.preventDefault()
278
+ ev.stopPropagation()
279
+ const cap = capabilities.find((c) => c.label === (ev.target as HTMLElement).dataset.export)
280
+ if (cap) executeExport(cap)
281
+ })
282
+ })
283
+ })
284
+
285
+ document.addEventListener('mousedown', (e) => {
286
+ if (contextMenu && !contextMenu.contains(e.target as Node)) {
287
+ hideContextMenu()
288
+ }
289
+ }, true)
290
+
291
+ document.addEventListener('dragover', (e) => e.preventDefault())
292
+ document.addEventListener('drop', async (e) => {
293
+ e.preventDefault()
294
+ const file = e.dataTransfer?.files[0]
295
+ if (!file) return
296
+ const filePath = api.getPathForFile(file)
297
+ if (!filePath) return
298
+ const result = await api.openFilePath(filePath)
299
+ if (result) setContent(result.content)
300
+ })
301
+ }
302
+
303
+ init().catch((e) => console.error('ColaMD init failed:', e))