@lancc/cmark 0.0.0-stage → 1.2.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 (202) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +21 -0
  3. package/README.md +217 -2
  4. package/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  5. package/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  6. package/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  7. package/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  8. package/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  9. package/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  10. package/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  11. package/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  12. package/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  13. package/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  14. package/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  15. package/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  16. package/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  17. package/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  18. package/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  19. package/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  20. package/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  21. package/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  22. package/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  23. package/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  24. package/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  25. package/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  26. package/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  27. package/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  28. package/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  29. package/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  30. package/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  31. package/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  32. package/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  33. package/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  34. package/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  35. package/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  36. package/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  37. package/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  38. package/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  39. package/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  40. package/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  41. package/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  42. package/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  43. package/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  44. package/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  45. package/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  46. package/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  47. package/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  48. package/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  49. package/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  50. package/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  51. package/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  52. package/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  53. package/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  54. package/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  55. package/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  56. package/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  57. package/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  58. package/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  59. package/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  60. package/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  61. package/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  62. package/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  63. package/assets/abnfDiagram-WEYOIA3C-BgXsTF7a.js +1 -0
  64. package/assets/architectureDiagram-ZHVYKT53-BsCWa_qC.js +36 -0
  65. package/assets/blockDiagram-RJAE7QA3-wbIxEiYN.js +140 -0
  66. package/assets/c4Diagram-XDPPPE54-5zdyxFvA.js +38 -0
  67. package/assets/chunk-2E7WHVMD-F85Lca-i.js +88 -0
  68. package/assets/chunk-2Q5K7J3B-CjpAZ0OI.js +1 -0
  69. package/assets/chunk-4EMNKR2T-C8Kbh83c.js +1 -0
  70. package/assets/chunk-55XCU76K-BINcFiLN.js +1 -0
  71. package/assets/chunk-5VM5RSS4-Diy5h2LS.js +15 -0
  72. package/assets/chunk-JWPE2WC7-BXnjDdh1.js +1 -0
  73. package/assets/chunk-VQ5W62QI-BM7ZZo1I.js +1 -0
  74. package/assets/chunk-XXDRQBXY-CW8CjB1w.js +1 -0
  75. package/assets/chunk-ZCHMQSOR-C7ATz1L1.js +2 -0
  76. package/assets/classDiagram-v2-4MYWR3UA-CMoqNk-b.js +217 -0
  77. package/assets/cose-bilkent-JH36ORCC-6rqR5iJk.js +1 -0
  78. package/assets/cynefin-EF2NZ3EQ-DypLIJxl.js +141 -0
  79. package/assets/cynefinDiagram-WF24UDRP-8b2CEBlR.js +62 -0
  80. package/assets/cytoscape.esm-CjX6Hyah.js +331 -0
  81. package/assets/dagre-NK4GIZVX-Dv6vuUbR.js +4 -0
  82. package/assets/diagram-CBK3MWTC-DJgMDeYd.js +24 -0
  83. package/assets/diagram-I5XIIV2D-BB9cMoRW.js +24 -0
  84. package/assets/diagram-ODIL2UCW-BVcRtsdy.js +30 -0
  85. package/assets/diagram-PFBEJUXE-BbGSOtJx.js +3 -0
  86. package/assets/diagram-RJA3I4VV-CzkY_8Fo.js +41 -0
  87. package/assets/diagram-TYUNFSNR-YTl5QdfL.js +200 -0
  88. package/assets/ebnfDiagram-V4P3TTPT-COVjPAjE.js +1 -0
  89. package/assets/echarts-BAPDkhe3.js +61 -0
  90. package/assets/elk-QONTBCTQ-DHsjY8Z_.js +27 -0
  91. package/assets/erDiagram-KYQIGQAA-CUAbTUl1.js +99 -0
  92. package/assets/flowDiagram-IUGLLWBU-CWsiqTQC.js +213 -0
  93. package/assets/ganttDiagram-R7GPNTJY-DetBW0Wm.js +292 -0
  94. package/assets/gitGraphDiagram-PDYL7XHY-ByrdgJmw.js +106 -0
  95. package/assets/graph-D8UmR1vK.js +1 -0
  96. package/assets/index-B17NCAw4.js +758 -0
  97. package/assets/index-eB_q_VA3.css +1 -0
  98. package/assets/infoDiagram-MVDRKAX5-BM48fjhU.js +2 -0
  99. package/assets/ishikawaDiagram-HPEHV2GE-DtHYywHZ.js +70 -0
  100. package/assets/journeyDiagram-BOSHW7O7-DJ1LiRh3.js +139 -0
  101. package/assets/kanban-definition-FNSA4HC5-DPPt8pwJ.js +89 -0
  102. package/assets/katex-CQL9J12G.js +257 -0
  103. package/assets/markdown-DVehI1Sg.js +314 -0
  104. package/assets/mermaid-C_q9pug0.js +312 -0
  105. package/assets/mindmap-definition-6OY57UG3-zbqYOEY6.js +98 -0
  106. package/assets/pegDiagram-ECIUJNBX-e7QG7TMq.js +1 -0
  107. package/assets/pieDiagram-GNMPY74T-Duf6YP0N.js +39 -0
  108. package/assets/quadrantDiagram-D57K4MC3-B2eYoUUy.js +7 -0
  109. package/assets/railroadDiagram-Y72NRBL7-BWCzPcI_.js +1 -0
  110. package/assets/requirementDiagram-EQYSW5RA-HhPMEqB1.js +84 -0
  111. package/assets/sankeyDiagram-SY24JHO5-CMUTwFBB.js +40 -0
  112. package/assets/sequenceDiagram-BLEHNHQU-C9wR2ll7.js +169 -0
  113. package/assets/sizeCapture-INFHLROL-DXfz37S6.js +1 -0
  114. package/assets/stateDiagram-v2-A6IESKWZ-D_gtonhE.js +291 -0
  115. package/assets/swimlanes-B3ENRKFQ-DdJOuuMV.js +1 -0
  116. package/assets/swimlanesDiagram-AYQRV4AK-BWt3HxHV.js +8 -0
  117. package/assets/timeline-definition-JXYKGLGB-DQsdwJe-.js +120 -0
  118. package/assets/usecaseDiagram-WPOLBNNX-DTRyFq7d.js +370 -0
  119. package/assets/vennDiagram-Q3Y3IDCW-dihYszm0.js +34 -0
  120. package/assets/wardleyDiagram-IVAIDKWA-BoPpgYfK.js +78 -0
  121. package/assets/xychartDiagram-4URNHDU4-DoLjYxeB.js +7 -0
  122. package/export-file/github-markdown.css +550 -0
  123. package/export-file/index.html +33 -0
  124. package/fonts/KaTeX_AMS-Regular.ttf +0 -0
  125. package/fonts/KaTeX_AMS-Regular.woff +0 -0
  126. package/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  127. package/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  128. package/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  129. package/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  130. package/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  131. package/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  132. package/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  133. package/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  134. package/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  135. package/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  136. package/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  137. package/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  138. package/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  139. package/fonts/KaTeX_Main-Bold.ttf +0 -0
  140. package/fonts/KaTeX_Main-Bold.woff +0 -0
  141. package/fonts/KaTeX_Main-Bold.woff2 +0 -0
  142. package/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  143. package/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  144. package/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  145. package/fonts/KaTeX_Main-Italic.ttf +0 -0
  146. package/fonts/KaTeX_Main-Italic.woff +0 -0
  147. package/fonts/KaTeX_Main-Italic.woff2 +0 -0
  148. package/fonts/KaTeX_Main-Regular.ttf +0 -0
  149. package/fonts/KaTeX_Main-Regular.woff +0 -0
  150. package/fonts/KaTeX_Main-Regular.woff2 +0 -0
  151. package/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  152. package/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  153. package/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  154. package/fonts/KaTeX_Math-Italic.ttf +0 -0
  155. package/fonts/KaTeX_Math-Italic.woff +0 -0
  156. package/fonts/KaTeX_Math-Italic.woff2 +0 -0
  157. package/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  158. package/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  159. package/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  160. package/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  161. package/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  162. package/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  163. package/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  164. package/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  165. package/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  166. package/fonts/KaTeX_Script-Regular.ttf +0 -0
  167. package/fonts/KaTeX_Script-Regular.woff +0 -0
  168. package/fonts/KaTeX_Script-Regular.woff2 +0 -0
  169. package/fonts/KaTeX_Size1-Regular.ttf +0 -0
  170. package/fonts/KaTeX_Size1-Regular.woff +0 -0
  171. package/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  172. package/fonts/KaTeX_Size2-Regular.ttf +0 -0
  173. package/fonts/KaTeX_Size2-Regular.woff +0 -0
  174. package/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  175. package/fonts/KaTeX_Size3-Regular.ttf +0 -0
  176. package/fonts/KaTeX_Size3-Regular.woff +0 -0
  177. package/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  178. package/fonts/KaTeX_Size4-Regular.ttf +0 -0
  179. package/fonts/KaTeX_Size4-Regular.woff +0 -0
  180. package/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  181. package/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  182. package/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  183. package/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  184. package/index.html +15 -0
  185. package/katex.min.css +1 -0
  186. package/logo.png +0 -0
  187. package/package.json +31 -4
  188. package/plugin.json +31 -0
  189. package/preload/branch-bar.js +201 -0
  190. package/preload/export-css.js +59 -0
  191. package/preload/git-panel.js +1366 -0
  192. package/preload/git-service.js +1463 -0
  193. package/preload/host.js +130 -0
  194. package/preload/ndb.js +1407 -0
  195. package/preload/package.json +3 -0
  196. package/preload/remote-setup.js +392 -0
  197. package/preload/remote-wizard.js +328 -0
  198. package/preload/rename-note.js +150 -0
  199. package/preload/services.js +423 -0
  200. package/preload/settings-panel.js +959 -0
  201. package/preload/ws-prefs.js +55 -0
  202. package/preload.js +1870 -0
@@ -0,0 +1,423 @@
1
+ /**
2
+ * services.js —— 导出、附件、剪贴板、文件杂项
3
+ *
4
+ * V1.1.0 新写。行为对齐 V1.0.0(用户已习惯的交互),实现全部按功能规格重写:
5
+ * - 导出 md:内容 + 附件一起落到目标目录
6
+ * - 导出 html:包一层带样式的文档,公式用 KaTeX 的 CSS
7
+ * - 导出 pdf / 图片:开一个隐藏窗口渲染 HTML 再抓图/打印
8
+ * - 图片附件:按内容 md5 命名,同一张图多处引用只存一份
9
+ */
10
+ const fs = require('fs')
11
+ const path = require('path')
12
+ const os = require('os')
13
+ const crypto = require('crypto')
14
+
15
+ const IMAGE_EXT = {
16
+ 'image/png': '.png',
17
+ 'image/jpeg': '.jpg',
18
+ 'image/jpg': '.jpg',
19
+ 'image/gif': '.gif',
20
+ 'image/webp': '.webp',
21
+ 'image/bmp': '.bmp',
22
+ 'image/svg+xml': '.svg',
23
+ 'image/x-icon': '.ico',
24
+ 'image/vnd.microsoft.icon': '.ico',
25
+ 'image/tiff': '.tiff',
26
+ }
27
+ const EXT_IS_IMAGE = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg', '.ico', '.tiff']
28
+ const MAX_IMAGE_BYTES = 10 * 1024 * 1024
29
+
30
+ function md5(buf) {
31
+ return crypto.createHash('md5').update(buf).digest('hex')
32
+ }
33
+
34
+ function safeName(name, fallback) {
35
+ const cleaned = String(name || '')
36
+ .replace(/[\\/:*?"<>|]/g, '')
37
+ .trim()
38
+ return cleaned || String(fallback || Date.now())
39
+ }
40
+
41
+ function ensureDir(dir) {
42
+ fs.mkdirSync(dir, { recursive: true })
43
+ return dir
44
+ }
45
+
46
+
47
+ const { exportDocCss } = require('./export-css')
48
+ function attachmentCacheDir(win) {
49
+ const base = path.join(win.ztools.getPath('temp') || os.tmpdir(), 'cmark.attachments')
50
+ return ensureDir(base)
51
+ }
52
+
53
+ module.exports = function installServices(win) {
54
+ const svc = {
55
+ /* ---------------- 首次使用示例 ---------------- */
56
+ getFirstMDFileContent() {
57
+ return [
58
+ '# 欢迎使用 CMark',
59
+ '',
60
+ '这是一个基于 ZTools 的本地 Markdown 笔记插件。',
61
+ '',
62
+ '- 笔记就是磁盘上的 `.md` 文件,用你习惯的编辑器也能打开',
63
+ '- 支持 git:提交、推送、拉取,改动能同步到远端仓库',
64
+ '- 支持表格、代码块、公式、流程图、图表',
65
+ '',
66
+ '在左侧右键新建笔记,或把已有的 `.md` 拖进目录。',
67
+ '',
68
+ ].join('\n')
69
+ },
70
+
71
+ /* ---------------- 剪贴板 ---------------- */
72
+ readClipboardData() {
73
+ try {
74
+ const { clipboard } = require('electron')
75
+ return { text: clipboard.readText(), html: clipboard.readHTML() }
76
+ } catch (e) {
77
+ return { text: '', html: '' }
78
+ }
79
+ },
80
+
81
+ readClipboardHtml() {
82
+ try {
83
+ return require('electron').clipboard.readHTML()
84
+ } catch (e) {
85
+ return ''
86
+ }
87
+ },
88
+
89
+ writeClipboardData(data) {
90
+ try {
91
+ if (!data) return
92
+ const clip = require('electron').clipboard
93
+ if (data.text != null) clip.writeText(String(data.text))
94
+ if (data.html) clip.writeHTML(String(data.html))
95
+ } catch (e) {}
96
+ },
97
+
98
+ copyHtml(html) {
99
+ try {
100
+ require('electron').clipboard.writeHTML(String(html || ''))
101
+ } catch (e) {}
102
+ },
103
+
104
+ /* ---------------- 目录 / 文件 ---------------- */
105
+ makeFolder(dir, name) {
106
+ const p = path.join(dir, safeName(name, '新建文件夹'))
107
+ ensureDir(p)
108
+ return p
109
+ },
110
+
111
+ getFileBaseName(p) {
112
+ return path.basename(p || '')
113
+ },
114
+
115
+ getFileContent(p) {
116
+ try {
117
+ return fs.readFileSync(p, 'utf-8')
118
+ } catch (e) {
119
+ return ''
120
+ }
121
+ },
122
+
123
+ fileUrlToPath(u) {
124
+ try {
125
+ return require('url').fileURLToPath(u)
126
+ } catch (e) {
127
+ return u
128
+ }
129
+ },
130
+
131
+ shellOpenPath(p) {
132
+ setTimeout(() => {
133
+ try {
134
+ if (!fs.existsSync(p)) {
135
+ win.ztools.showNotification('路径不存在:' + p)
136
+ return
137
+ }
138
+ win.ztools.shellOpenPath(p)
139
+ } catch (e) {}
140
+ })
141
+ },
142
+
143
+ /** 把 md 里的相对图片路径解析成绝对路径(存在才返回) */
144
+ convertMdFileImageURLToAbsolutePath(mdPath, rel) {
145
+ const abs = path.join(path.dirname(mdPath), rel)
146
+ return fs.existsSync(abs) ? abs : null
147
+ },
148
+
149
+ /* ---------------- 图片 ---------------- */
150
+ /**
151
+ * 解析一个图片来源,返回 {digest, name, data(Buffer), contentType}
152
+ * 支持:data:base64、本地文件路径
153
+ */
154
+ getImageData(src) {
155
+ if (!src) throw new Error('图片为空')
156
+ const dataUrl = /^data:(image\/[a-z.+-]+);base64,/i.exec(src)
157
+ if (dataUrl) {
158
+ const buf = Buffer.from(src.slice(dataUrl[0].length), 'base64')
159
+ const type = dataUrl[1].toLowerCase()
160
+ if (buf.length > MAX_IMAGE_BYTES) throw new Error('图片大小超过 10 M')
161
+ return { digest: md5(buf), name: 'pasted-image', data: buf, contentType: type }
162
+ }
163
+
164
+ if (!fs.existsSync(src)) throw new Error('图片文件不存在')
165
+ const ext = path.extname(src).toLowerCase()
166
+ if (!EXT_IS_IMAGE.includes(ext)) throw new Error('不是支持的图片格式')
167
+ const st = fs.statSync(src)
168
+ if (st.size > MAX_IMAGE_BYTES) throw new Error('图片大小超过 10 M')
169
+ const buf = fs.readFileSync(src)
170
+ const type = 'image/' + ext.replace('.', '').replace('jpg', 'jpeg')
171
+ return { digest: md5(buf), name: path.basename(src), data: buf, contentType: type }
172
+ },
173
+
174
+ /** 从剪贴板取图(截图或复制的图片文件) */
175
+ getClipboardImage() {
176
+ // 优先:资源管理器里复制的图片文件
177
+ try {
178
+ const files = win.ztools.getCopyedFiles && win.ztools.getCopyedFiles()
179
+ if (files && files.length) {
180
+ if (files.length > 1) throw new Error('存在多个文件,请一次粘贴一个图片文件')
181
+ return svc.getImageData(files[0].path)
182
+ }
183
+ } catch (e) {
184
+ if (e && e.message) throw e
185
+ }
186
+
187
+ // 其次:剪贴板里的位图(截图)
188
+ try {
189
+ const { clipboard } = require('electron')
190
+ const formats = clipboard.availableFormats()
191
+ if (!formats || !formats.length) return null
192
+ const hasImage = formats.some((f) => f.startsWith('image/'))
193
+ if (!hasImage) return null
194
+ const img = clipboard.readImage()
195
+ if (!img || img.isEmpty()) return null
196
+ const buf = img.toPNG()
197
+ if (buf.length > MAX_IMAGE_BYTES) throw new Error('图片大小超过 10 M')
198
+ return { digest: md5(buf), name: '截图', data: buf, contentType: 'image/png' }
199
+ } catch (e) {
200
+ if (e && e.message) throw e
201
+ return null
202
+ }
203
+ },
204
+
205
+ /** 附件落盘到临时缓存,返回可给 <img src> 用的绝对路径 */
206
+ getAttachmentTempPath(digest) {
207
+ if (!digest) return null
208
+ const dir = attachmentCacheDir(win)
209
+ let ext = ''
210
+ try {
211
+ const meta = win.__NDB && win.__NDB.get && win.__NDB.get('attachment/' + digest)
212
+ if (meta && meta._attachments && meta._attachments[0]) {
213
+ ext = IMAGE_EXT[meta._attachments[0].content_type] || ''
214
+ }
215
+ } catch (e) {}
216
+
217
+ const target = path.join(dir, digest + ext)
218
+ if (fs.existsSync(target)) return target
219
+
220
+ try {
221
+ const buf = win.__NDB && win.__NDB.getAttachment && win.__NDB.getAttachment('attachment/' + digest)
222
+ if (!buf) return null
223
+ fs.writeFileSync(target, Buffer.isBuffer(buf) ? buf : Buffer.from(buf))
224
+ return target
225
+ } catch (e) {
226
+ return null
227
+ }
228
+ },
229
+
230
+ /** 把 data URL 或远端图片存成本地文件(用于渲染外链图片) */
231
+ async saveImgToFileBySrc(src) {
232
+ if (!src) return null
233
+ let buf = null
234
+ let ext = ''
235
+ const dataUrl = /^data:(image\/[a-z.+-]+);base64,/i.exec(src)
236
+ if (dataUrl) {
237
+ ext = IMAGE_EXT[dataUrl[1].toLowerCase()] || ''
238
+ if (!ext) return null
239
+ buf = Buffer.from(src.slice(dataUrl[0].length), 'base64')
240
+ } else if (/^https?:\/\//i.test(src)) {
241
+ try {
242
+ const res = await fetch(src)
243
+ const type = (res.headers.get('content-type') || '').split(';')[0].toLowerCase()
244
+ ext = IMAGE_EXT[type] || ''
245
+ if (!ext) return null
246
+ buf = Buffer.from(await res.arrayBuffer())
247
+ } catch (e) {
248
+ return null
249
+ }
250
+ } else {
251
+ return null
252
+ }
253
+ if (!buf) return null
254
+ const dir = attachmentCacheDir(win)
255
+ const target = path.join(dir, md5(buf) + ext)
256
+ if (!fs.existsSync(target)) {
257
+ try {
258
+ fs.writeFileSync(target, buf)
259
+ } catch (e) {
260
+ return null
261
+ }
262
+ }
263
+ return target
264
+ },
265
+
266
+ /* ---------------- 导出 ---------------- */
267
+ _exportDir: '',
268
+
269
+ /** 导出 Markdown:正文 + 附件一起复制 */
270
+ writeMarkdownFile(dir, name, content, attachmentPaths) {
271
+ const base = safeName(name, Date.now())
272
+ let targetDir = dir
273
+ if (!targetDir) {
274
+ targetDir = win.ztools.showSaveDialog({
275
+ defaultPath: path.join(win.ztools.getPath('downloads') || '', base),
276
+ })
277
+ if (!targetDir) return null
278
+ ensureDir(targetDir)
279
+ }
280
+ for (const a of attachmentPaths || []) {
281
+ try {
282
+ fs.copyFileSync(a, path.join(targetDir, path.basename(a)))
283
+ } catch (e) {}
284
+ }
285
+ const file = path.join(targetDir, base + '.md')
286
+ fs.writeFileSync(file, content, 'utf8')
287
+ return file
288
+ },
289
+
290
+ /** 导出 HTML:套一层文档壳,样式内联 */
291
+ writeHtmlFile(dir, name, html, attachmentPaths, withKatex, selfContained, theme) {
292
+ const base = safeName(name, Date.now())
293
+ const katexCss = withKatex
294
+ ? '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css">'
295
+ : ''
296
+ const doc = [
297
+ '<!doctype html>',
298
+ '<html lang="zh-CN">',
299
+ '<head>',
300
+ '<meta charset="utf-8"/>',
301
+ '<meta name="viewport" content="width=device-width,initial-scale=1"/>',
302
+ '<title>' + base + '</title>',
303
+ katexCss,
304
+ '<style>',
305
+ exportDocCss(theme),
306
+ '</style>',
307
+ '</head>',
308
+ '<body><article class="markdown-body">',
309
+ String(html || ''),
310
+ '</article></body></html>',
311
+ ].join('\n')
312
+
313
+ let targetDir = dir
314
+ if (!targetDir) {
315
+ const picked = win.ztools.showSaveDialog({
316
+ defaultPath: path.join(win.ztools.getPath('downloads') || '', base),
317
+ })
318
+ if (!picked) return null
319
+ targetDir = ensureDir(picked)
320
+ }
321
+ for (const a of attachmentPaths || []) {
322
+ try {
323
+ fs.copyFileSync(a, path.join(targetDir, path.basename(a)))
324
+ } catch (e) {}
325
+ }
326
+ const file = path.join(targetDir, base + '.html')
327
+ fs.writeFileSync(file, doc, 'utf8')
328
+ return file
329
+ },
330
+
331
+ /**
332
+ * 导出 PDF / 图片:开隐藏窗口渲染 HTML,再抓图或打印
333
+ * type: 'pdf' | 'image'
334
+ */
335
+ exportFile(type, html, extraCss, cb, outDir, theme) {
336
+ const done = typeof cb === 'function' ? cb : () => {}
337
+ if (!win.ztools.createBrowserWindow) {
338
+ done(new Error('当前平台不支持静默渲染,请改用导出 HTML'))
339
+ return
340
+ }
341
+ const shell = [
342
+ '<!doctype html><html><head><meta charset="utf-8"/>',
343
+ '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css">',
344
+ '<style>',
345
+ exportDocCss(theme),
346
+ extraCss || '',
347
+ '</style></head><body><article class="markdown-body"></article></body></html>',
348
+ ].join('\n')
349
+
350
+ const tmpHtml = path.join(attachmentCacheDir(win), 'export-' + Date.now() + '.html')
351
+ fs.writeFileSync(tmpHtml, shell, 'utf8')
352
+
353
+ let bw
354
+ try {
355
+ bw = win.ztools.createBrowserWindow(
356
+ 'file://' + tmpHtml.replace(/\\/g, '/'),
357
+ { width: 980, height: 1200, show: false },
358
+ async () => {
359
+ try {
360
+ await bw.webContents.executeJavaScript(
361
+ 'document.querySelector(".markdown-body").innerHTML = ' + JSON.stringify(String(html || ''))
362
+ )
363
+ // 等图片解码,最多 3 秒
364
+ await bw.webContents.executeJavaScript(
365
+ 'Promise.all(Array.from(document.images).map(i => i.complete ? 1 : new Promise(r => { i.onload = i.onerror = r })))'
366
+ )
367
+
368
+ let payload
369
+ if (type === 'image') {
370
+ const h = await bw.webContents.executeJavaScript('document.body.scrollHeight')
371
+ bw.setContentSize(980, Math.min(h + 30, 20000))
372
+ const img = await bw.webContents.capturePage()
373
+ payload = img.toPNG()
374
+ } else {
375
+ payload = await bw.webContents.printToPDF({
376
+ printBackground: true,
377
+ pageSize: 'A4',
378
+ landscape: false,
379
+ marginsType: 0,
380
+ })
381
+ }
382
+
383
+ const dir = outDir || win.ztools.getPath('temp')
384
+ const ext = type === 'image' ? '.png' : '.pdf'
385
+ const out = path.join(dir, 'cmark-export-' + Date.now() + ext)
386
+ fs.writeFileSync(out, payload)
387
+ try {
388
+ bw.destroy()
389
+ } catch (e) {}
390
+ done(null, out)
391
+ } catch (e) {
392
+ try {
393
+ bw.destroy()
394
+ } catch (e2) {}
395
+ done(e)
396
+ }
397
+ }
398
+ )
399
+ } catch (e) {
400
+ done(e)
401
+ }
402
+ },
403
+
404
+ /** 把导出的临时文件"另存为"到用户选的位置 */
405
+ dialogSaveExportFile(tempPath, name, ext) {
406
+ const base = safeName(name, Date.now())
407
+ const realExt = ext === 'image' ? 'png' : ext
408
+ const target = win.ztools.showSaveDialog({
409
+ defaultPath: path.join(win.ztools.getPath('downloads') || '', base + '.' + realExt),
410
+ filters: [{ name: realExt.toUpperCase(), extensions: [realExt] }],
411
+ })
412
+ if (!target) return null
413
+ try {
414
+ fs.copyFileSync(tempPath, target)
415
+ return target
416
+ } catch (e) {
417
+ return null
418
+ }
419
+ },
420
+ }
421
+
422
+ return svc
423
+ }