foliko 1.1.8 → 1.1.9

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 (306) hide show
  1. package/.agent/agents/code-assistant.json +17 -0
  2. package/.agent/agents/email-assistant.json +14 -0
  3. package/.agent/agents/file-assistant.json +18 -0
  4. package/.agent/agents/orchestrator-demo.md +53 -0
  5. package/.agent/agents/orchestrator.json +7 -0
  6. package/.agent/agents/poster-expert.md +228 -0
  7. package/.agent/agents/system-assistant.json +15 -0
  8. package/.agent/agents/web-assistant.json +12 -0
  9. package/.agent/data/default.json +5 -404
  10. package/.agent/data/email/processed-emails.json +1 -0
  11. package/.agent/data/plugins-state.json +173 -172
  12. package/.agent/data/scheduler/tasks.json +1 -0
  13. package/.agent/data/web/web-config.json +5 -0
  14. package/.agent/mcp_config.json +0 -14
  15. package/.agent/package.json +8 -0
  16. package/.agent/plugins/__pycache__/file_writer.cpython-312.pyc +0 -0
  17. package/.agent/plugins/daytona/README.md +89 -0
  18. package/.agent/plugins/daytona/index.js +377 -0
  19. package/.agent/plugins/daytona/package.json +12 -0
  20. package/.agent/plugins/marknative/README.md +134 -0
  21. package/.agent/plugins/marknative/fonts.zip +0 -0
  22. package/.agent/plugins/marknative/index.js +256 -0
  23. package/.agent/plugins/marknative/package.json +12 -0
  24. package/.agent/plugins/system-info/index.js +387 -0
  25. package/.agent/plugins/system-info/package.json +4 -0
  26. package/.agent/plugins/system-info/test.js +40 -0
  27. package/.agent/plugins/test-plugin.py +123 -0
  28. package/.agent/plugins/test_nested_plugin.py +85 -0
  29. package/.agent/plugins.json +11 -5
  30. package/.agent/python-scripts/test_sample.py +24 -0
  31. package/.agent/sessions/cli_default.json +96 -249
  32. package/.agent/sessions/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.json +189 -0
  33. package/.agent/skills/agent-browser/SKILL.md +311 -0
  34. package/.agent/skills/agent-browser/TEST_PLAN.md +200 -0
  35. package/.agent/skills/sysinfo/SKILL.md +38 -0
  36. package/.agent/skills/sysinfo/system-info.sh +130 -0
  37. package/.agent/skills/workflow/SKILL.md +324 -0
  38. package/.agent/test-agent.js +35 -0
  39. package/.agent/weixin.json +6 -0
  40. package/.agent/workflows/email-digest.json +50 -0
  41. package/.agent/workflows/file-backup.json +21 -0
  42. package/.agent/workflows/get-ip-notify.json +32 -0
  43. package/.agent/workflows/news-aggregator.json +93 -0
  44. package/.agent/workflows/news-dashboard-v2.json +94 -0
  45. package/.agent/workflows/notification-batch.json +32 -0
  46. package/.claude/settings.local.json +9 -1
  47. package/.env.example +56 -56
  48. package/README.md +441 -441
  49. package/cli/src/ui/chat-ui.js +32 -21
  50. package/foliko_poster.png +0 -0
  51. package/output/business_poster_final.png +0 -0
  52. package/output/emoji_test_v2.png +0 -0
  53. package/output/foliko-ai-launch-poster.png +0 -0
  54. package/output/foliko-poster.png +0 -0
  55. package/output/muji_style_poster.png +0 -0
  56. package/output/new_product_launch.png +0 -0
  57. package/output/news_poster_625yi_subsidy.png +0 -0
  58. package/output/tech_future_2026.png +0 -0
  59. package/output/tech_future_poster.png +0 -0
  60. package/package.json +1 -2
  61. package/plugins/default-plugins.js +58 -6
  62. package/plugins/extension-executor-plugin.js +8 -21
  63. package/plugins/python-plugin-loader.js +461 -40
  64. package/plugins/python-plugin-loader.js.bak +856 -0
  65. package/plugins/subagent-plugin.js +0 -121
  66. package/plugins/weixin-plugin.js +38 -29
  67. package/poster.png +0 -0
  68. package/skills/find-skills/AGENTS.md +162 -162
  69. package/skills/find-skills/SKILL.md +133 -133
  70. package/skills/foliko-dev/SKILL.md +6 -0
  71. package/skills/python-plugin-dev/SKILL.md +124 -2
  72. package/src/core/agent-chat.js +220 -281
  73. package/src/core/agent.js +10 -0
  74. package/src/utils/plugin-helpers.js +22 -4
  75. package/system.md +1678 -1574
  76. package/test-scan.js +18 -0
  77. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +0 -26
  78. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +0 -97
  79. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +0 -101
  80. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +0 -31
  81. package/.agent/.shared/ui-ux-pro-max/data/products.csv +0 -97
  82. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +0 -24
  83. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +0 -45
  84. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  85. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  86. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
  87. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  88. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  89. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  90. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  91. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +0 -54
  92. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
  93. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  94. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  95. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  96. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +0 -59
  97. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +0 -58
  98. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +0 -101
  99. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  100. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +0 -31
  101. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  102. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  103. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +0 -258
  104. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +0 -1067
  105. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +0 -106
  106. package/.agent/ARCHITECTURE.md +0 -288
  107. package/.agent/agents/ambient-agent.md +0 -57
  108. package/.agent/agents/debugger.md +0 -55
  109. package/.agent/agents/email-assistant.md +0 -49
  110. package/.agent/agents/file-manager.md +0 -42
  111. package/.agent/agents/python-developer.md +0 -60
  112. package/.agent/agents/scheduler.md +0 -59
  113. package/.agent/agents/web-developer.md +0 -45
  114. package/.agent/data/puppeteer-sessions/undefined.json +0 -6
  115. package/.agent/data/weixin-media/2026-04-08/img_1775618677512.jpg +0 -0
  116. package/.agent/data/weixin-media/2026-04-08/img_1775619073340.jpg +0 -0
  117. package/.agent/data/weixin-media/2026-04-08/img_1775619097536.jpg +0 -0
  118. package/.agent/data/weixin-media/2026-04-08/img_1775619209388.jpg +0 -0
  119. package/.agent/memory/feedback/mnrdvj5i-ca3dkd.md +0 -9
  120. package/.agent/memory/feedback/mnre365e-7s4zax.md +0 -9
  121. package/.agent/memory/feedback/mnre36jn-nkfgmp.md +0 -9
  122. package/.agent/memory/feedback/mnre3805-kjiq6h.md +0 -9
  123. package/.agent/memory/feedback/mnsf66kp-b10rcd.md +0 -9
  124. package/.agent/memory/feedback/mnsi3sz8-p5g2cw.md +0 -9
  125. package/.agent/memory/feedback/mnsibe47-sv2ni1.md +0 -9
  126. package/.agent/memory/feedback/mnsic89w-nn228o.md +0 -9
  127. package/.agent/memory/feedback/mnsj1xe9-x83ba0.md +0 -9
  128. package/.agent/memory/feedback/mnsj21iv-wnwelx.md +0 -9
  129. package/.agent/memory/feedback/mnsj2g4a-cog7a2.md +0 -9
  130. package/.agent/memory/feedback/mnsj4js7-lktjp6.md +0 -9
  131. package/.agent/memory/feedback/mnsj5d4y-uglwvp.md +0 -9
  132. package/.agent/memory/feedback/mnslkuo9-uous66.md +0 -24
  133. package/.agent/memory/feedback/mnsm3vq0-megoil.md +0 -9
  134. package/.agent/memory/feedback/mnsnn5x2-sxcihd.md +0 -9
  135. package/.agent/memory/feedback/mnsnq17s-nabrn9.md +0 -9
  136. package/.agent/memory/feedback/mnsnybet-wz7rn3.md +0 -9
  137. package/.agent/memory/feedback/mnsrw0s7-7s9e30.md +0 -9
  138. package/.agent/memory/feedback/mnu5hpnd-tlm16q.md +0 -9
  139. package/.agent/memory/feedback/mnu60uqe-xuoxp4.md +0 -9
  140. package/.agent/memory/project/mnqx54u5-loqtoe.md +0 -9
  141. package/.agent/memory/project/mnqx84cv-mx6dmd.md +0 -9
  142. package/.agent/memory/project/mnsacuyr-hgtk5n.md +0 -20
  143. package/.agent/memory/project/mnu5hy2x-bjsg7u.md +0 -9
  144. package/.agent/memory/reference/mnre3cww-penbo1.md +0 -9
  145. package/.agent/memory/reference/mns9wn48-luerua.md +0 -14
  146. package/.agent/memory/reference/mns9yz5c-thc2s0.md +0 -16
  147. package/.agent/memory/reference/mnsfy4um-910f1o.md +0 -23
  148. package/.agent/memory/reference/mnsg37dp-lmfj18.md +0 -32
  149. package/.agent/memory/reference/mnsll60q-0j911u.md +0 -36
  150. package/.agent/memory/reference/mnsmlb5y-nej31u.md +0 -16
  151. package/.agent/memory/reference/mnssle72-yrot96.md +0 -9
  152. package/.agent/memory/user/mnsfuon6-l416q1.md +0 -21
  153. package/.agent/memory/user/mnsg9kut-95m7rf.md +0 -20
  154. package/.agent/memory/user/mnu2eo1v-yy6fhe.md +0 -9
  155. package/.agent/memory/user/mnu2etuo-8u8jk8.md +0 -9
  156. package/.agent/plugins/poster-plugin/README.md +0 -304
  157. package/.agent/plugins/poster-plugin/fonts/NotoColorEmoji-Regular.ttf +0 -0
  158. package/.agent/plugins/poster-plugin/fonts/PatuaOne-Regular.ttf +0 -0
  159. package/.agent/plugins/poster-plugin/fonts/Symbola_hint.ttf +0 -0
  160. package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221.ttf +0 -0
  161. package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221/347/262/227/344/275/223.ttf +0 -0
  162. package/.agent/plugins/poster-plugin/index.js +0 -13
  163. package/.agent/plugins/poster-plugin/package.json +0 -29
  164. package/.agent/plugins/poster-plugin/src/canvas.js +0 -232
  165. package/.agent/plugins/poster-plugin/src/components/arrow.js +0 -84
  166. package/.agent/plugins/poster-plugin/src/components/avatar.js +0 -71
  167. package/.agent/plugins/poster-plugin/src/components/badge.js +0 -85
  168. package/.agent/plugins/poster-plugin/src/components/barcode.js +0 -123
  169. package/.agent/plugins/poster-plugin/src/components/bubble.js +0 -154
  170. package/.agent/plugins/poster-plugin/src/components/button.js +0 -168
  171. package/.agent/plugins/poster-plugin/src/components/card.js +0 -88
  172. package/.agent/plugins/poster-plugin/src/components/chart.js +0 -127
  173. package/.agent/plugins/poster-plugin/src/components/chip.js +0 -88
  174. package/.agent/plugins/poster-plugin/src/components/columns.js +0 -121
  175. package/.agent/plugins/poster-plugin/src/components/cta.js +0 -87
  176. package/.agent/plugins/poster-plugin/src/components/divider.js +0 -55
  177. package/.agent/plugins/poster-plugin/src/components/feature.js +0 -85
  178. package/.agent/plugins/poster-plugin/src/components/featureGrid.js +0 -117
  179. package/.agent/plugins/poster-plugin/src/components/frame.js +0 -230
  180. package/.agent/plugins/poster-plugin/src/components/grid.js +0 -130
  181. package/.agent/plugins/poster-plugin/src/components/highlightText.js +0 -145
  182. package/.agent/plugins/poster-plugin/src/components/icon.js +0 -94
  183. package/.agent/plugins/poster-plugin/src/components/imageFrame.js +0 -205
  184. package/.agent/plugins/poster-plugin/src/components/index.js +0 -81
  185. package/.agent/plugins/poster-plugin/src/components/listItem.js +0 -147
  186. package/.agent/plugins/poster-plugin/src/components/notification.js +0 -123
  187. package/.agent/plugins/poster-plugin/src/components/progress.js +0 -79
  188. package/.agent/plugins/poster-plugin/src/components/progressCircle.js +0 -117
  189. package/.agent/plugins/poster-plugin/src/components/qrcode.js +0 -74
  190. package/.agent/plugins/poster-plugin/src/components/quote.js +0 -169
  191. package/.agent/plugins/poster-plugin/src/components/rating.js +0 -85
  192. package/.agent/plugins/poster-plugin/src/components/ribbon.js +0 -197
  193. package/.agent/plugins/poster-plugin/src/components/seal.js +0 -148
  194. package/.agent/plugins/poster-plugin/src/components/star.js +0 -70
  195. package/.agent/plugins/poster-plugin/src/components/statCard.js +0 -105
  196. package/.agent/plugins/poster-plugin/src/components/stepper.js +0 -118
  197. package/.agent/plugins/poster-plugin/src/components/table.js +0 -167
  198. package/.agent/plugins/poster-plugin/src/components/tagCloud.js +0 -85
  199. package/.agent/plugins/poster-plugin/src/components/timeline.js +0 -117
  200. package/.agent/plugins/poster-plugin/src/components/watermark.js +0 -54
  201. package/.agent/plugins/poster-plugin/src/composer.js +0 -2314
  202. package/.agent/plugins/poster-plugin/src/elements/artText.js +0 -69
  203. package/.agent/plugins/poster-plugin/src/elements/background.js +0 -99
  204. package/.agent/plugins/poster-plugin/src/elements/circle.js +0 -31
  205. package/.agent/plugins/poster-plugin/src/elements/image.js +0 -28
  206. package/.agent/plugins/poster-plugin/src/elements/index.js +0 -28
  207. package/.agent/plugins/poster-plugin/src/elements/line.js +0 -23
  208. package/.agent/plugins/poster-plugin/src/elements/polygon.js +0 -63
  209. package/.agent/plugins/poster-plugin/src/elements/rectangle.js +0 -32
  210. package/.agent/plugins/poster-plugin/src/elements/richText.js +0 -283
  211. package/.agent/plugins/poster-plugin/src/elements/svg.js +0 -108
  212. package/.agent/plugins/poster-plugin/src/elements/text.js +0 -112
  213. package/.agent/plugins/poster-plugin/src/fonts.js +0 -674
  214. package/.agent/plugins/poster-plugin/src/index.js +0 -2126
  215. package/.agent/plugins/poster-plugin/src/presets.js +0 -36
  216. package/.agent/plugins/poster-plugin/src/templates/business.js +0 -60
  217. package/.agent/plugins/poster-plugin/src/templates/gradient.js +0 -64
  218. package/.agent/plugins/poster-plugin/src/templates/index.js +0 -43
  219. package/.agent/plugins/poster-plugin/src/templates/modern.js +0 -69
  220. package/.agent/plugins/poster-plugin/src/templates/simple.js +0 -58
  221. package/.agent/plugins/poster-plugin/src/templates/social.js +0 -62
  222. package/.agent/plugins/poster-plugin/src/templates/tech.js +0 -84
  223. package/.agent/plugins/poster-plugin/src/utils/imageLoader.js +0 -84
  224. package/.agent/plugins/poster-plugin/yarn.lock +0 -837
  225. package/.agent/plugins/puppeteer-plugin/README.md +0 -147
  226. package/.agent/plugins/puppeteer-plugin/index.js +0 -1422
  227. package/.agent/plugins/puppeteer-plugin/package.json +0 -9
  228. package/.agent/rules/GEMINI.md +0 -273
  229. package/.agent/rules/allow-rule.md +0 -77
  230. package/.agent/rules/log-rule.md +0 -83
  231. package/.agent/rules/security-rule.md +0 -93
  232. package/.agent/scripts/auto_preview.py +0 -148
  233. package/.agent/scripts/checklist.py +0 -217
  234. package/.agent/scripts/session_manager.py +0 -120
  235. package/.agent/scripts/verify_all.py +0 -327
  236. package/.agent/skills/api-patterns/SKILL.md +0 -81
  237. package/.agent/skills/api-patterns/api-style.md +0 -42
  238. package/.agent/skills/api-patterns/auth.md +0 -24
  239. package/.agent/skills/api-patterns/documentation.md +0 -26
  240. package/.agent/skills/api-patterns/graphql.md +0 -41
  241. package/.agent/skills/api-patterns/rate-limiting.md +0 -31
  242. package/.agent/skills/api-patterns/response.md +0 -37
  243. package/.agent/skills/api-patterns/rest.md +0 -40
  244. package/.agent/skills/api-patterns/scripts/api_validator.py +0 -211
  245. package/.agent/skills/api-patterns/security-testing.md +0 -122
  246. package/.agent/skills/api-patterns/trpc.md +0 -41
  247. package/.agent/skills/api-patterns/versioning.md +0 -22
  248. package/.agent/skills/app-builder/SKILL.md +0 -75
  249. package/.agent/skills/app-builder/agent-coordination.md +0 -71
  250. package/.agent/skills/app-builder/feature-building.md +0 -53
  251. package/.agent/skills/app-builder/project-detection.md +0 -34
  252. package/.agent/skills/app-builder/scaffolding.md +0 -118
  253. package/.agent/skills/app-builder/tech-stack.md +0 -40
  254. package/.agent/skills/app-builder/templates/SKILL.md +0 -39
  255. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +0 -76
  256. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +0 -92
  257. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +0 -88
  258. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +0 -88
  259. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +0 -83
  260. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +0 -90
  261. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +0 -90
  262. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +0 -122
  263. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +0 -122
  264. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +0 -169
  265. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +0 -134
  266. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +0 -83
  267. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +0 -119
  268. package/.agent/skills/architecture/SKILL.md +0 -55
  269. package/.agent/skills/architecture/context-discovery.md +0 -43
  270. package/.agent/skills/architecture/examples.md +0 -94
  271. package/.agent/skills/architecture/pattern-selection.md +0 -68
  272. package/.agent/skills/architecture/patterns-reference.md +0 -50
  273. package/.agent/skills/architecture/trade-off-analysis.md +0 -77
  274. package/.agent/skills/clean-code/SKILL.md +0 -201
  275. package/.agent/skills/doc.md +0 -177
  276. package/.agent/skills/frontend-design/SKILL.md +0 -418
  277. package/.agent/skills/frontend-design/animation-guide.md +0 -331
  278. package/.agent/skills/frontend-design/color-system.md +0 -311
  279. package/.agent/skills/frontend-design/decision-trees.md +0 -418
  280. package/.agent/skills/frontend-design/motion-graphics.md +0 -306
  281. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +0 -183
  282. package/.agent/skills/frontend-design/scripts/ux_audit.py +0 -722
  283. package/.agent/skills/frontend-design/typography-system.md +0 -345
  284. package/.agent/skills/frontend-design/ux-psychology.md +0 -1116
  285. package/.agent/skills/frontend-design/visual-effects.md +0 -383
  286. package/.agent/skills/i18n-localization/SKILL.md +0 -154
  287. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +0 -241
  288. package/.agent/skills/mcp-builder/SKILL.md +0 -176
  289. package/.agent/skills/poster-design/SKILL.md +0 -385
  290. package/.agent/skills/web-design-guidelines/SKILL.md +0 -57
  291. package/.agent/workflows/brainstorm.md +0 -113
  292. package/.agent/workflows/create.md +0 -59
  293. package/.agent/workflows/debug.md +0 -103
  294. package/.agent/workflows/deploy.md +0 -176
  295. package/.agent/workflows/enhance.md +0 -63
  296. package/.agent/workflows/orchestrate.md +0 -237
  297. package/.agent/workflows/plan.md +0 -89
  298. package/.agent/workflows/preview.md +0 -81
  299. package/.agent/workflows/simple-test.md +0 -42
  300. package/.agent/workflows/status.md +0 -86
  301. package/.agent/workflows/structured-orchestrate.md +0 -180
  302. package/.agent/workflows/test.md +0 -144
  303. package/.agent/workflows/ui-ux-pro-max.md +0 -296
  304. package/outputs/emoji-font-showcase.png +0 -0
  305. package/outputs/foliko-muji-style.png +0 -0
  306. /package/.agent/plugins/{poster-plugin → marknative}/fonts/SegoeUI Emoji.ttf +0 -0
@@ -1,674 +0,0 @@
1
- /**
2
- * 字体管理模块 - 支持多字体 fallback
3
- * 使用 @napi-rs/canvas 原生 API 解决中文乱码问题
4
- */
5
-
6
- const path = require('path')
7
- const fs = require('fs')
8
-
9
- // 导入 @napi-rs/canvas 原生 API
10
- let GlobalFonts = null
11
- try {
12
- const canvas = require('@napi-rs/canvas')
13
- GlobalFonts = canvas.GlobalFonts
14
- } catch (e) {
15
- console.warn('[poster] @napi-rs/canvas 未安装,字体注册可能受限')
16
- }
17
-
18
- // 兼容旧的 canvas API(备用)
19
- let legacyRegisterFont = null
20
- try {
21
- legacyRegisterFont = require('canvas').registerFont
22
- } catch (e) {
23
- // ignore
24
- }
25
-
26
- // 已注册的字体
27
- const registeredFonts = new Map()
28
-
29
- // 字体信息缓存(用于返回详细列表)
30
- let fontInfoList = []
31
-
32
- // Emoji 字体名称映射(用于统一识别)
33
- const emojiFontMappings = {
34
- 'NotoColorEmoji': 'Noto Color Emoji',
35
- 'NotoColorEmoji-Regular': 'Noto Color Emoji',
36
- 'Noto Emoji': 'Noto Emoji',
37
- 'NotoEmoji-Regular': 'Noto Emoji',
38
- 'Apple Color Emoji': 'Apple Color Emoji',
39
- 'Segoe UI Emoji': 'Segoe UI Emoji',
40
- 'SegoeUI Emoji': 'Segoe UI Emoji',
41
- 'Symbola': 'Symbola',
42
- 'Symbola_hint': 'Symbola',
43
- }
44
-
45
- // 中文字体别名映射表 - 将请求的字体映射到实际可用的字体
46
- const chineseFontMappings = {
47
- // 黑体系列
48
- 'SimHei': 'Microsoft YaHei',
49
- '黑体': 'Microsoft YaHei',
50
- 'Hei': 'Microsoft YaHei',
51
- 'Microsoft YaHei': 'Microsoft YaHei',
52
- '微软雅黑': 'Microsoft YaHei',
53
- 'msyh': 'Microsoft YaHei',
54
- 'Source Han Sans CN': 'Source Han Sans CN',
55
- '思源黑体': 'Noto Sans CJK SC',
56
- 'Noto Sans CJK': 'Noto Sans CJK SC',
57
- 'Noto Sans CJK SC': 'Noto Sans CJK SC',
58
- 'Noto Sans': 'Noto Sans CJK SC',
59
-
60
- // 宋体系列
61
- 'SimSun': 'SimSun',
62
- '宋体': 'SimSun',
63
- 'Song': 'SimSun',
64
- 'SimSun_GB2312': 'SimSun',
65
- '方正宋体': 'Noto Serif CJK SC',
66
- 'Noto Serif CJK': 'Noto Serif CJK SC',
67
- 'Noto Serif CJK SC': 'Noto Serif CJK SC',
68
-
69
- // 楷体系列
70
- 'Kai': 'LXGW WenKai',
71
- '楷体': 'LXGW WenKai',
72
- 'Kaiti': 'LXGW WenKai',
73
- 'STKaiti': 'LXGW WenKai',
74
- '华文楷体': 'LXGW WenKai',
75
- '楷书': 'LXGW WenKai',
76
- 'LXGW WenKai': 'LXGW WenKai',
77
- 'LXGWWenKai': 'LXGW WenKai',
78
- 'LXGWWenKai-Regular': 'LXGW WenKai',
79
- '霞鹜文楷': 'LXGW WenKai',
80
-
81
- // 仿宋系列
82
- 'FangSong': 'SimSun',
83
- '仿宋': 'SimSun',
84
- 'FangSong_GB2312': 'SimSun',
85
- '方正仿宋': 'Noto Serif CJK SC',
86
-
87
- // 幼圆
88
- 'YouYuan': 'Microsoft YaHei',
89
- '幼圆': 'Microsoft YaHei',
90
- '圆体': 'Microsoft YaHei',
91
-
92
- // 华文系列
93
- 'STHeiti': 'Microsoft YaHei',
94
- '华文黑体': 'Microsoft YaHei',
95
- 'STSong': 'SimSun',
96
- '华文宋体': 'SimSun',
97
- 'STSongti': 'SimSun',
98
- '华文宋体_GB2312': 'SimSun',
99
- 'STZhongsong': 'Noto Serif CJK SC',
100
- '华文中宋': 'Noto Serif CJK SC',
101
- 'STCaiyun': 'Microsoft YaHei',
102
- '华文彩云': 'Microsoft YaHei',
103
- 'STXingkai': 'Microsoft YaHei',
104
- '华文行楷': 'Microsoft YaHei',
105
- 'STXinwei': 'Microsoft YaHei',
106
- '华文新魏': 'Microsoft YaHei',
107
- 'STLiti': 'Microsoft YaHei',
108
- '华文隶书': 'Microsoft YaHei',
109
-
110
- // 其他常见中文别名
111
- 'sans-serif': 'Microsoft YaHei',
112
- 'serif': 'SimSun',
113
- 'monospace': 'Consolas',
114
- 'PingFang': 'Microsoft YaHei',
115
- 'PingFang SC': 'Microsoft YaHei',
116
- 'Hiragino Sans GB': 'Microsoft YaHei',
117
- '冬青黑体': 'Microsoft YaHei',
118
- 'WenQuanYi': 'Microsoft YaHei',
119
- '文泉驿': 'Microsoft YaHei',
120
- 'WenQuanYi Micro Hei': 'Microsoft YaHei',
121
- 'WenQuanYi Zen Hei': 'Microsoft YaHei',
122
- 'AR PL': 'Microsoft YaHei',
123
- 'AR PL UKai': 'SimSun',
124
- }
125
-
126
- // 默认字体信息
127
- let defaultFont = {
128
- name: 'Microsoft YaHei',
129
- family: 'Microsoft YaHei',
130
- path: null,
131
- weight: 'normal',
132
- style: 'normal',
133
- isDefault: true,
134
- }
135
-
136
- /**
137
- * 使用 @napi-rs/canvas 原生 API 注册字体
138
- */
139
- function registerFontWithGlobalFonts(fontPath, fontFamily, options = {}) {
140
- if (!GlobalFonts) return false
141
-
142
- try {
143
- const absolutePath = path.resolve(fontPath)
144
-
145
- // 使用 registerFromPath 注册字体
146
- GlobalFonts.registerFromPath(absolutePath, fontFamily)
147
-
148
- return true
149
- } catch (e) {
150
- console.log(`[poster] GlobalFonts 注册字体失败: ${fontFamily}`, e.message)
151
- return false
152
- }
153
- }
154
-
155
- /**
156
- * 使用 legacy canvas API 注册字体(备用)
157
- */
158
- function registerFontWithLegacy(fontPath, fontFamily, options = {}) {
159
- if (!legacyRegisterFont) return false
160
-
161
- try {
162
- legacyRegisterFont(fontPath, {
163
- family: fontFamily,
164
- weight: options.weight || 'normal',
165
- style: options.style || 'normal',
166
- })
167
- return true
168
- } catch (e) {
169
- console.log(`[poster] Legacy 注册字体失败: ${fontFamily}`, e.message)
170
- return false
171
- }
172
- }
173
-
174
- /**
175
- * 注册字体文件 - 双 API 策略
176
- */
177
- function registerFontFile(fontPath, fontFamily, options = {}) {
178
- if (!fontFamily) return false
179
-
180
- // 避免重复注册
181
- if (registeredFonts.has(fontFamily)) {
182
- return true
183
- }
184
-
185
- try {
186
- if (!fs.existsSync(fontPath)) {
187
- return false
188
- }
189
-
190
- let success = false
191
-
192
- // 优先使用 @napi-rs/canvas 原生 API
193
- if (GlobalFonts) {
194
- success = registerFontWithGlobalFonts(fontPath, fontFamily, options)
195
- }
196
-
197
- // 同时使用 legacy API 保持兼容性(paper.js 可能需要)
198
- if (!success && legacyRegisterFont) {
199
- success = registerFontWithLegacy(fontPath, fontFamily, options)
200
- }
201
-
202
- if (success) {
203
- const fontInfo = {
204
- name: fontFamily,
205
- family: fontFamily,
206
- path: path.resolve(fontPath),
207
- weight: options.weight || 'normal',
208
- style: options.style || 'normal',
209
- isDefault: false,
210
- isEmoji: isEmojiFont(fontFamily),
211
- source: options.source || 'unknown',
212
- }
213
-
214
- registeredFonts.set(fontFamily, fontInfo)
215
- }
216
-
217
- return success
218
- } catch (e) {
219
- console.log(`[poster] 注册字体失败: ${fontFamily}`, e.message)
220
- return false
221
- }
222
- }
223
-
224
- /**
225
- * 判断是否为 Emoji 字体
226
- */
227
- function isEmojiFont(fontName) {
228
- if (!fontName) return false
229
- const lower = fontName.toLowerCase()
230
- return (
231
- lower.includes('emoji') ||
232
- lower.includes('color') ||
233
- lower.includes('symbola')
234
- )
235
- }
236
-
237
- /**
238
- * 获取插件字体目录
239
- */
240
- function getPluginFontsDir() {
241
- // 插件目录
242
- const pluginDir = path.join(__dirname, '..')
243
- const pluginFontsDir = path.join(pluginDir, 'fonts')
244
-
245
- // 也尝试从工作目录
246
- const cwdFontsDir = path.join(process.cwd(), '.agent', 'plugins', 'poster-plugin', 'fonts')
247
-
248
- if (fs.existsSync(pluginFontsDir)) {
249
- return pluginFontsDir
250
- }
251
- if (fs.existsSync(cwdFontsDir)) {
252
- return cwdFontsDir
253
- }
254
- return pluginFontsDir
255
- }
256
-
257
- /**
258
- * 获取系统字体路径
259
- */
260
- function getSystemFontPaths() {
261
- const isWin = process.platform === 'win32'
262
- const isMac = process.platform === 'darwin'
263
-
264
- const fonts = []
265
-
266
- if (isWin) {
267
- const winDir = 'C:\\Windows\\Fonts'
268
- fonts.push(
269
- // 中文首选 - 微软雅黑
270
- { path: `${winDir}\\msyh.ttc`, family: 'Microsoft YaHei', weight: 'normal' },
271
- { path: `${winDir}\\msyhbd.ttc`, family: 'Microsoft YaHei', weight: 'bold' },
272
- // 备选中文字体
273
- { path: `${winDir}\\simhei.ttf`, family: 'SimHei', weight: 'normal' },
274
- { path: `${winDir}\\simsun.ttc`, family: 'SimSun', weight: 'normal' },
275
- { path: `${winDir}\\simkai.ttf`, family: 'KaiTi', weight: 'normal' },
276
- // 常用西文字体
277
- { path: `${winDir}\\arial.ttf`, family: 'Arial', weight: 'normal' },
278
- { path: `${winDir}\\ariali.ttf`, family: 'Arial', weight: 'normal', style: 'italic' },
279
- { path: `${winDir}\\arialbi.ttf`, family: 'Arial', weight: 'bold', style: 'italic' },
280
- { path: `${winDir}\\arialbd.ttf`, family: 'Arial', weight: 'bold' },
281
- { path: `${winDir}\\times.ttf`, family: 'Times New Roman', weight: 'normal' },
282
- { path: `${winDir}\\timesi.ttf`, family: 'Times New Roman', weight: 'normal', style: 'italic' },
283
- { path: `${winDir}\\timesbi.ttf`, family: 'Times New Roman', weight: 'bold', style: 'italic' },
284
- { path: `${winDir}\\timesbd.ttf`, family: 'Times New Roman', weight: 'bold' },
285
- { path: `${winDir}\\consola.ttf`, family: 'Consolas', weight: 'normal' },
286
- { path: `${winDir}\\calibri.ttf`, family: 'Calibri', weight: 'normal' },
287
- // Emoji 字体
288
- { path: `${winDir}\\seguiemj.ttf`, family: 'Segoe UI Emoji', weight: 'normal' },
289
- )
290
- } else if (isMac) {
291
- fonts.push(
292
- // macOS 中文字体
293
- { path: '/System/Library/Fonts/PingFang.ttc', family: 'PingFang SC', weight: 'normal' },
294
- { path: '/System/Library/Fonts/STHeiti Light.ttc', family: 'Heiti SC', weight: 'normal' },
295
- { path: '/System/Library/Fonts/STHeiti Medium.ttc', family: 'Heiti SC', weight: 'bold' },
296
- // Emoji
297
- { path: '/System/Library/Fonts/Apple Color Emoji.ttc', family: 'Apple Color Emoji', weight: 'normal' },
298
- // 常用西文
299
- { path: '/System/Library/Fonts/Helvetica.ttc', family: 'Helvetica', weight: 'normal' },
300
- { path: '/System/Library/Fonts/Times.ttc', family: 'Times New Roman', weight: 'normal' },
301
- )
302
- } else {
303
- // Linux
304
- fonts.push(
305
- // 系统 CJK 字体
306
- { path: '/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc', family: 'Noto Sans CJK SC', weight: 'normal' },
307
- { path: '/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc', family: 'Noto Sans CJK SC', weight: 'bold' },
308
- { path: '/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc', family: 'Noto Serif CJK SC', weight: 'normal' },
309
- // 备选路径
310
- { path: '/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc', family: 'Noto Sans CJK SC', weight: 'normal' },
311
- { path: '/usr/share/fonts/truetype/chinese/NotoSansCJK-Regular.ttc', family: 'Noto Sans CJK SC', weight: 'normal' },
312
- // 西文字体
313
- { path: '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', family: 'DejaVu Sans', weight: 'normal' },
314
- { path: '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', family: 'DejaVu Sans', weight: 'bold' },
315
- // Emoji
316
- { path: '/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf', family: 'Noto Color Emoji', weight: 'normal' },
317
- { path: '/usr/share/fonts/truetype/noto/NotoEmoji-Regular.ttf', family: 'Noto Emoji', weight: 'normal' },
318
- )
319
- }
320
-
321
- return fonts
322
- }
323
-
324
- /**
325
- * 初始化字体
326
- */
327
- function initFonts() {
328
- // 清空之前的字体信息
329
- fontInfoList = []
330
- registeredFonts.clear()
331
-
332
- console.log('[poster] 初始化字体系统...')
333
- console.log(`[poster] 平台: ${process.platform}`)
334
-
335
- // 1. 优先加载插件自带的字体
336
- const pluginFontsDir = getPluginFontsDir()
337
- console.log(`[poster] 插件字体目录: ${pluginFontsDir}`)
338
-
339
- if (fs.existsSync(pluginFontsDir)) {
340
- const fontFiles = fs.readdirSync(pluginFontsDir)
341
-
342
- for (const file of fontFiles) {
343
- if (!file.endsWith('.ttf') && !file.endsWith('.otf') && !file.endsWith('.ttc')) {
344
- continue
345
- }
346
-
347
- const fontPath = path.join(pluginFontsDir, file)
348
- let fontName = path.basename(file, path.extname(file))
349
-
350
- // 检查是否为 Emoji 字体
351
- if (isEmojiFont(fontName)) {
352
- fontName = emojiFontMappings[fontName] || 'Noto Color Emoji'
353
- }
354
-
355
- // 特殊处理中文文件名
356
- if (fontName === '微软雅黑' || fontName === '微软雅黑粗体') {
357
- fontName = fontName.includes('粗体') ? 'Microsoft YaHei' : 'Microsoft YaHei'
358
- }
359
-
360
- const options = {
361
- source: 'plugin',
362
- weight: fontName.includes('Bold') || fontName.includes('粗体') ? 'bold' : 'normal',
363
- }
364
-
365
- if (registerFontFile(fontPath, fontName, options)) {
366
- console.log(`[poster] 已注册插件字体: ${fontName} (${file})`)
367
-
368
- // 微软雅黑设为默认
369
- if ((fontName === 'Microsoft YaHei' || fontName === '微软雅黑') && options.weight === 'normal') {
370
- defaultFont = {
371
- name: 'Microsoft YaHei',
372
- family: 'Microsoft YaHei',
373
- path: fontPath,
374
- weight: 'normal',
375
- style: 'normal',
376
- isDefault: true,
377
- source: 'plugin',
378
- }
379
- console.log(`[poster] 设为默认字体: Microsoft YaHei`)
380
- }
381
-
382
- fontInfoList.push({
383
- name: fontName,
384
- family: fontName,
385
- path: fontPath,
386
- source: 'plugin',
387
- isEmoji: isEmojiFont(fontName),
388
- })
389
- }
390
- }
391
- }
392
-
393
- // 2. 加载系统字体
394
- console.log('[poster] 加载系统字体...')
395
- const systemFonts = getSystemFontPaths()
396
-
397
- for (const font of systemFonts) {
398
- if (registerFontFile(font.path, font.family, { weight: font.weight, style: font.style, source: 'system' })) {
399
- if (!fontInfoList.find(f => f.name === font.family)) {
400
- fontInfoList.push({
401
- name: font.family,
402
- family: font.family,
403
- path: font.path,
404
- source: 'system',
405
- isEmoji: isEmojiFont(font.family),
406
- })
407
- }
408
- }
409
- }
410
-
411
- // 3. 如果没有默认字体,尝试从系统字体中选择
412
- if (!defaultFont.path) {
413
- // 优先选择支持中文的系统字体
414
- const chineseFontCandidates = ['Microsoft YaHei', 'SimHei', 'PingFang SC', 'Noto Sans CJK SC', 'Source Han Sans CN']
415
-
416
- for (const candidate of chineseFontCandidates) {
417
- if (registeredFonts.has(candidate)) {
418
- const info = registeredFonts.get(candidate)
419
- defaultFont = {
420
- name: candidate,
421
- family: candidate,
422
- path: info.path,
423
- weight: 'normal',
424
- style: 'normal',
425
- isDefault: true,
426
- source: 'system',
427
- }
428
- console.log(`[poster] 设为默认字体: ${candidate}`)
429
- break
430
- }
431
- }
432
- }
433
-
434
- // 4. 确保有回退字体
435
- const fallbackFonts = [
436
- { family: 'Arial', weight: 'normal', path: 'C:\\Windows\\Fonts\\arial.ttf' },
437
- { family: 'Helvetica', weight: 'normal', path: '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf' },
438
- ]
439
-
440
- for (const fb of fallbackFonts) {
441
- if (!registeredFonts.has(fb.family) && fs.existsSync(fb.path)) {
442
- registerFontFile(fb.path, fb.family, { weight: fb.weight, source: 'fallback' })
443
- }
444
- }
445
-
446
- // 5. 最终回退
447
- if (!registeredFonts.has('sans-serif')) {
448
- registeredFonts.set('sans-serif', {
449
- name: 'sans-serif',
450
- family: 'sans-serif',
451
- path: null,
452
- isDefault: false,
453
- source: 'fallback',
454
- })
455
- }
456
-
457
- if (!defaultFont.path) {
458
- defaultFont = {
459
- name: 'sans-serif',
460
- family: 'sans-serif',
461
- path: null,
462
- weight: 'normal',
463
- style: 'normal',
464
- isDefault: true,
465
- source: 'fallback',
466
- }
467
- }
468
-
469
- // 打印注册结果
470
- console.log(`[poster] 字体注册完成,共 ${registeredFonts.size} 个字体`)
471
- console.log(`[poster] 默认字体: ${defaultFont.name} (${defaultFont.source})`)
472
-
473
- // 打印中文字体列表
474
- const cjkFonts = Array.from(registeredFonts.keys()).filter(f =>
475
- f.includes('CJK') || f.includes('Han') || f.includes('YaHei') ||
476
- f.includes('Hei') || f.includes('Song') || f.includes('Kai') ||
477
- f.includes('Fang') || f.includes('Yuan') || f.includes('PingFang')
478
- )
479
- if (cjkFonts.length > 0) {
480
- console.log(`[poster] 已注册 CJK 字体: ${cjkFonts.join(', ')}`)
481
- }
482
-
483
- refreshFontList()
484
- }
485
-
486
- /**
487
- * 刷新字体列表缓存
488
- */
489
- function refreshFontList() {
490
- fontInfoList = []
491
- for (const [name, info] of registeredFonts) {
492
- fontInfoList.push({
493
- name: name,
494
- family: info.family || name,
495
- path: info.path,
496
- source: info.source || 'unknown',
497
- isEmoji: info.isEmoji || isEmojiFont(name),
498
- isDefault: info.isDefault || (name === defaultFont.name),
499
- })
500
- }
501
- }
502
-
503
- /**
504
- * 验证字体是否可用 - 支持字体别名映射
505
- */
506
- function validateFont(fontFamily) {
507
- if (!fontFamily) return defaultFont.name
508
-
509
- // 直接匹配
510
- if (registeredFonts.has(fontFamily)) return fontFamily
511
-
512
- // 别名映射
513
- const mapped = chineseFontMappings[fontFamily]
514
- if (mapped && registeredFonts.has(mapped)) {
515
- return mapped
516
- }
517
-
518
- // 大小写不敏感匹配
519
- const lower = fontFamily.toLowerCase()
520
- for (const [name] of registeredFonts) {
521
- if (name.toLowerCase() === lower) return name
522
- }
523
-
524
- // 如果请求的是中文,尝试返回中文字体
525
- if (/[\u4e00-\u9fff]/.test(fontFamily) || isCommonChineseFontName(fontFamily)) {
526
- const chineseFonts = ['Microsoft YaHei', 'PingFang SC', 'SimHei', 'Noto Sans CJK SC']
527
- for (const cf of chineseFonts) {
528
- if (registeredFonts.has(cf)) return cf
529
- }
530
- }
531
-
532
- return defaultFont.name
533
- }
534
-
535
- /**
536
- * 判断是否为常见中文字体名称
537
- */
538
- function isCommonChineseFontName(name) {
539
- if (!name) return false
540
- const lower = name.toLowerCase()
541
- const keywords = [
542
- 'hei', 'song', 'kai', 'fang', 'yuan', '仿', '宋', '楷', '黑', '圆',
543
- '微软', '思源', 'noto', 'source', 'wqy', '文泉', 'pingfang', 'hiragino'
544
- ]
545
- return keywords.some(kw => lower.includes(kw.toLowerCase()))
546
- }
547
-
548
- /**
549
- * 获取已注册字体列表
550
- */
551
- function listAllFonts() {
552
- refreshFontList()
553
-
554
- const regularFonts = fontInfoList.filter(f => !f.isEmoji)
555
- const emojiFonts = fontInfoList.filter(f => f.isEmoji)
556
-
557
- return {
558
- success: true,
559
- default: defaultFont,
560
- fonts: fontInfoList,
561
- regular: regularFonts.map(f => f.name),
562
- emoji: emojiFonts.map(f => f.name),
563
- total: fontInfoList.length,
564
- }
565
- }
566
-
567
- /**
568
- * 获取默认字体
569
- */
570
- function getDefaultFont() {
571
- return { ...defaultFont }
572
- }
573
-
574
- /**
575
- * 获取默认字体名称
576
- */
577
- function getDefaultFontFamily() {
578
- return defaultFont.name
579
- }
580
-
581
- /**
582
- * 获取字体 fallback 链
583
- * @napi-rs/canvas 支持逗号分隔的字体链
584
- */
585
- function getFontFallbackChain(primaryFont, text = '') {
586
- const chain = []
587
- const validated = validateFont(primaryFont || defaultFont.name)
588
-
589
- // 主字体
590
- if (validated && validated !== 'sans-serif') {
591
- chain.push(validated)
592
- }
593
-
594
- // 检查文本是否包含中文或 emoji
595
- const hasChinese = /[\u4e00-\u9fff]/.test(text || primaryFont || '')
596
- const hasEmoji = /[\u{1F300}-\u{1F9FF}]/u.test(text || '')
597
-
598
- // 中文字体优先级
599
- if (hasChinese) {
600
- const chineseFonts = ['Microsoft YaHei', 'PingFang SC', 'SimHei', 'Noto Sans CJK SC', 'SimSun']
601
- for (const cf of chineseFonts) {
602
- if (!chain.includes(cf) && registeredFonts.has(cf)) {
603
- chain.push(cf)
604
- }
605
- }
606
- }
607
-
608
- // Emoji 字体优先级
609
- if (hasEmoji) {
610
- const emojiFontList = ['Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', 'Noto Emoji', 'Symbola']
611
- for (const ef of emojiFontList) {
612
- if (!chain.includes(ef) && registeredFonts.has(ef)) {
613
- chain.unshift(ef) // emoji 字体放最前面
614
- }
615
- }
616
- }
617
-
618
- // 添加默认字体(如果不在链中)
619
- if (!chain.includes(defaultFont.name)) {
620
- chain.push(defaultFont.name)
621
- }
622
-
623
- // 添加通用字体族作为最终回退
624
- const genericFallbacks = ['sans-serif', 'serif', 'monospace']
625
- for (const fb of genericFallbacks) {
626
- if (!chain.includes(fb) && registeredFonts.has(fb)) {
627
- chain.push(fb)
628
- }
629
- }
630
-
631
- return chain
632
- }
633
-
634
- /**
635
- * 使用 @napi-rs/canvas 匹配字体(用于文本度量)
636
- */
637
- function matchFont(text, fontFamily) {
638
- if (!GlobalFonts) return fontFamily || defaultFont.name
639
-
640
- try {
641
- // GlobalFonts.matchFont 可以在没有精确字体时返回最接近的匹配
642
- const matched = GlobalFonts.matchFont(text, fontFamily)
643
- return matched || fontFamily || defaultFont.name
644
- } catch (e) {
645
- return fontFamily || defaultFont.name
646
- }
647
- }
648
-
649
- /**
650
- * 获取所有已注册的字体家族
651
- */
652
- function getFontFamilies() {
653
- if (GlobalFonts && GlobalFonts.families) {
654
- return GlobalFonts.families
655
- }
656
- return Array.from(registeredFonts.keys())
657
- }
658
-
659
- // 初始化
660
- initFonts()
661
-
662
- module.exports = {
663
- init: initFonts,
664
- registerFontFile,
665
- validateFont,
666
- listAllFonts,
667
- getDefaultFont,
668
- getDefaultFontFamily,
669
- getFontFallbackChain,
670
- getFontFamilies,
671
- matchFont,
672
- isEmojiFont,
673
- getRegisteredFonts: () => Array.from(registeredFonts.keys()),
674
- }