foliko 1.1.6 → 1.1.8

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 (307) hide show
  1. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
  2. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
  3. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
  4. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
  5. package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
  6. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
  7. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
  8. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  9. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  10. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  11. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  12. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  13. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  14. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  15. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
  16. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  17. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  18. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  19. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  20. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
  21. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
  22. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  23. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  24. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
  25. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  26. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  27. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
  28. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
  29. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +106 -0
  30. package/.agent/ARCHITECTURE.md +288 -0
  31. package/.agent/agents/ambient-agent.md +57 -0
  32. package/.agent/agents/debugger.md +55 -0
  33. package/.agent/agents/email-assistant.md +49 -0
  34. package/.agent/agents/file-manager.md +42 -0
  35. package/.agent/agents/python-developer.md +60 -0
  36. package/.agent/agents/scheduler.md +59 -0
  37. package/.agent/agents/web-developer.md +45 -0
  38. package/.agent/data/default.json +412 -3
  39. package/.agent/data/plugins-state.json +177 -174
  40. package/.agent/data/puppeteer-sessions/undefined.json +6 -0
  41. package/.agent/data/weixin-media/2026-04-08/img_1775618677512.jpg +0 -0
  42. package/.agent/data/weixin-media/2026-04-08/img_1775619073340.jpg +0 -0
  43. package/.agent/data/weixin-media/2026-04-08/img_1775619097536.jpg +0 -0
  44. package/.agent/data/weixin-media/2026-04-08/img_1775619209388.jpg +0 -0
  45. package/.agent/mcp_config.json +11 -3
  46. package/.agent/memory/feedback/mnrdvj5i-ca3dkd.md +9 -0
  47. package/.agent/memory/feedback/mnre365e-7s4zax.md +9 -0
  48. package/.agent/memory/feedback/mnre36jn-nkfgmp.md +9 -0
  49. package/.agent/memory/feedback/mnre3805-kjiq6h.md +9 -0
  50. package/.agent/memory/feedback/mnsf66kp-b10rcd.md +9 -0
  51. package/.agent/memory/feedback/mnsi3sz8-p5g2cw.md +9 -0
  52. package/.agent/memory/feedback/mnsibe47-sv2ni1.md +9 -0
  53. package/.agent/memory/feedback/mnsic89w-nn228o.md +9 -0
  54. package/.agent/memory/feedback/mnsj1xe9-x83ba0.md +9 -0
  55. package/.agent/memory/feedback/mnsj21iv-wnwelx.md +9 -0
  56. package/.agent/memory/feedback/mnsj2g4a-cog7a2.md +9 -0
  57. package/.agent/memory/feedback/mnsj4js7-lktjp6.md +9 -0
  58. package/.agent/memory/feedback/mnsj5d4y-uglwvp.md +9 -0
  59. package/.agent/memory/feedback/mnslkuo9-uous66.md +24 -0
  60. package/.agent/memory/feedback/mnsm3vq0-megoil.md +9 -0
  61. package/.agent/memory/feedback/mnsnn5x2-sxcihd.md +9 -0
  62. package/.agent/memory/feedback/mnsnq17s-nabrn9.md +9 -0
  63. package/.agent/memory/feedback/mnsnybet-wz7rn3.md +9 -0
  64. package/.agent/memory/feedback/mnsrw0s7-7s9e30.md +9 -0
  65. package/.agent/memory/feedback/mnu5hpnd-tlm16q.md +9 -0
  66. package/.agent/memory/feedback/mnu60uqe-xuoxp4.md +9 -0
  67. package/.agent/memory/project/mnqx54u5-loqtoe.md +9 -0
  68. package/.agent/memory/project/mnqx84cv-mx6dmd.md +9 -0
  69. package/.agent/memory/project/mnsacuyr-hgtk5n.md +20 -0
  70. package/.agent/memory/project/mnu5hy2x-bjsg7u.md +9 -0
  71. package/.agent/memory/reference/mnre3cww-penbo1.md +9 -0
  72. package/.agent/memory/reference/mns9wn48-luerua.md +14 -0
  73. package/.agent/memory/reference/mns9yz5c-thc2s0.md +16 -0
  74. package/.agent/memory/reference/mnsfy4um-910f1o.md +23 -0
  75. package/.agent/memory/reference/mnsg37dp-lmfj18.md +32 -0
  76. package/.agent/memory/reference/mnsll60q-0j911u.md +36 -0
  77. package/.agent/memory/reference/mnsmlb5y-nej31u.md +16 -0
  78. package/.agent/memory/reference/mnssle72-yrot96.md +9 -0
  79. package/.agent/memory/user/mnsfuon6-l416q1.md +21 -0
  80. package/.agent/memory/user/mnsg9kut-95m7rf.md +20 -0
  81. package/.agent/memory/user/mnu2eo1v-yy6fhe.md +9 -0
  82. package/.agent/memory/user/mnu2etuo-8u8jk8.md +9 -0
  83. package/.agent/plugins/poster-plugin/fonts/NotoColorEmoji-Regular.ttf +0 -0
  84. package/.agent/plugins/poster-plugin/fonts/Symbola_hint.ttf +0 -0
  85. package/.agent/plugins/poster-plugin/package.json +3 -3
  86. package/.agent/plugins/poster-plugin/src/canvas.js +8 -0
  87. package/.agent/plugins/poster-plugin/src/components/barcode.js +5 -2
  88. package/.agent/plugins/poster-plugin/src/components/bubble.js +3 -2
  89. package/.agent/plugins/poster-plugin/src/components/button.js +74 -30
  90. package/.agent/plugins/poster-plugin/src/components/columns.js +97 -83
  91. package/.agent/plugins/poster-plugin/src/components/grid.js +104 -92
  92. package/.agent/plugins/poster-plugin/src/components/highlightText.js +2 -1
  93. package/.agent/plugins/poster-plugin/src/components/imageFrame.js +143 -93
  94. package/.agent/plugins/poster-plugin/src/components/quote.js +85 -13
  95. package/.agent/plugins/poster-plugin/src/components/ribbon.js +13 -9
  96. package/.agent/plugins/poster-plugin/src/components/seal.js +5 -3
  97. package/.agent/plugins/poster-plugin/src/components/star.js +3 -3
  98. package/.agent/plugins/poster-plugin/src/components/table.js +1 -1
  99. package/.agent/plugins/poster-plugin/src/components/watermark.js +4 -2
  100. package/.agent/plugins/poster-plugin/src/composer.js +181 -13
  101. package/.agent/plugins/poster-plugin/src/elements/artText.js +16 -7
  102. package/.agent/plugins/poster-plugin/src/elements/background.js +20 -5
  103. package/.agent/plugins/poster-plugin/src/elements/richText.js +160 -107
  104. package/.agent/plugins/poster-plugin/src/elements/text.js +48 -45
  105. package/.agent/plugins/poster-plugin/src/fonts.js +556 -125
  106. package/.agent/plugins/poster-plugin/src/index.js +46 -1
  107. package/.agent/plugins/poster-plugin/yarn.lock +186 -356
  108. package/.agent/plugins/puppeteer-plugin/README.md +147 -0
  109. package/.agent/plugins/puppeteer-plugin/index.js +1422 -0
  110. package/.agent/plugins/puppeteer-plugin/package.json +9 -0
  111. package/.agent/plugins.json +5 -11
  112. package/.agent/rules/GEMINI.md +273 -0
  113. package/.agent/rules/allow-rule.md +77 -0
  114. package/.agent/rules/log-rule.md +83 -0
  115. package/.agent/rules/security-rule.md +93 -0
  116. package/.agent/scripts/auto_preview.py +148 -0
  117. package/.agent/scripts/checklist.py +217 -0
  118. package/.agent/scripts/session_manager.py +120 -0
  119. package/.agent/scripts/verify_all.py +327 -0
  120. package/.agent/sessions/cli_default.json +128 -5479
  121. package/.agent/skills/api-patterns/SKILL.md +81 -0
  122. package/.agent/skills/api-patterns/api-style.md +42 -0
  123. package/.agent/skills/api-patterns/auth.md +24 -0
  124. package/.agent/skills/api-patterns/documentation.md +26 -0
  125. package/.agent/skills/api-patterns/graphql.md +41 -0
  126. package/.agent/skills/api-patterns/rate-limiting.md +31 -0
  127. package/.agent/skills/api-patterns/response.md +37 -0
  128. package/.agent/skills/api-patterns/rest.md +40 -0
  129. package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
  130. package/.agent/skills/api-patterns/security-testing.md +122 -0
  131. package/.agent/skills/api-patterns/trpc.md +41 -0
  132. package/.agent/skills/api-patterns/versioning.md +22 -0
  133. package/.agent/skills/app-builder/SKILL.md +75 -0
  134. package/.agent/skills/app-builder/agent-coordination.md +71 -0
  135. package/.agent/skills/app-builder/feature-building.md +53 -0
  136. package/.agent/skills/app-builder/project-detection.md +34 -0
  137. package/.agent/skills/app-builder/scaffolding.md +118 -0
  138. package/.agent/skills/app-builder/tech-stack.md +40 -0
  139. package/.agent/skills/app-builder/templates/SKILL.md +39 -0
  140. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
  141. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
  142. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
  143. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
  144. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
  145. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
  146. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
  147. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
  148. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
  149. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
  150. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
  151. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
  152. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
  153. package/.agent/skills/architecture/SKILL.md +55 -0
  154. package/.agent/skills/architecture/context-discovery.md +43 -0
  155. package/.agent/skills/architecture/examples.md +94 -0
  156. package/.agent/skills/architecture/pattern-selection.md +68 -0
  157. package/.agent/skills/architecture/patterns-reference.md +50 -0
  158. package/.agent/skills/architecture/trade-off-analysis.md +77 -0
  159. package/.agent/skills/clean-code/SKILL.md +201 -0
  160. package/.agent/skills/doc.md +177 -0
  161. package/.agent/skills/frontend-design/SKILL.md +418 -0
  162. package/.agent/skills/frontend-design/animation-guide.md +331 -0
  163. package/.agent/skills/frontend-design/color-system.md +311 -0
  164. package/.agent/skills/frontend-design/decision-trees.md +418 -0
  165. package/.agent/skills/frontend-design/motion-graphics.md +306 -0
  166. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
  167. package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
  168. package/.agent/skills/frontend-design/typography-system.md +345 -0
  169. package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
  170. package/.agent/skills/frontend-design/visual-effects.md +383 -0
  171. package/.agent/skills/i18n-localization/SKILL.md +154 -0
  172. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
  173. package/.agent/skills/mcp-builder/SKILL.md +176 -0
  174. package/.agent/skills/poster-design/SKILL.md +385 -0
  175. package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
  176. package/.agent/workflows/brainstorm.md +113 -0
  177. package/.agent/workflows/create.md +59 -0
  178. package/.agent/workflows/debug.md +103 -0
  179. package/.agent/workflows/deploy.md +176 -0
  180. package/.agent/workflows/enhance.md +63 -0
  181. package/.agent/workflows/orchestrate.md +237 -0
  182. package/.agent/workflows/plan.md +89 -0
  183. package/.agent/workflows/preview.md +81 -0
  184. package/.agent/workflows/simple-test.md +42 -0
  185. package/.agent/workflows/status.md +86 -0
  186. package/.agent/workflows/structured-orchestrate.md +180 -0
  187. package/.agent/workflows/test.md +144 -0
  188. package/.agent/workflows/ui-ux-pro-max.md +296 -0
  189. package/.env.example +56 -56
  190. package/README.md +441 -441
  191. package/SPEC.md +735 -696
  192. package/output/foliko-poster.png +0 -0
  193. package/outputs/emoji-font-showcase.png +0 -0
  194. package/outputs/foliko-muji-style.png +0 -0
  195. package/package.json +3 -2
  196. package/plugins/ambient-agent/EventWatcher.js +33 -37
  197. package/plugins/ambient-agent/ExplorerLoop.js +338 -36
  198. package/plugins/ambient-agent/GoalManager.js +7 -3
  199. package/plugins/ambient-agent/StateStore.js +30 -1
  200. package/plugins/ambient-agent/constants.js +15 -1
  201. package/plugins/ambient-agent/index.js +26 -33
  202. package/plugins/coordinator-plugin.js +3 -3
  203. package/plugins/default-plugins.js +4 -3
  204. package/plugins/email/index.js +150 -36
  205. package/plugins/email/monitor.js +79 -5
  206. package/plugins/email/reply.js +15 -25
  207. package/plugins/extension-executor-plugin.js +184 -32
  208. package/plugins/feishu-plugin.js +2 -2
  209. package/plugins/file-system-plugin.js +57 -24
  210. package/plugins/memory-plugin.js +176 -64
  211. package/plugins/python-plugin-loader.js +79 -9
  212. package/plugins/scheduler-plugin.js +64 -24
  213. package/plugins/telegram-plugin.js +2 -2
  214. package/plugins/think-plugin.js +7 -2
  215. package/plugins/web-plugin.js +263 -4
  216. package/plugins/weixin-plugin.js +2 -2
  217. package/skills/ambient-agent/SKILL.md +342 -314
  218. package/skills/find-skills/AGENTS.md +162 -162
  219. package/skills/find-skills/SKILL.md +133 -133
  220. package/src/core/agent-chat.js +64 -9
  221. package/src/core/agent.js +216 -69
  222. package/src/core/tool-registry.js +5 -5
  223. package/src/executors/mcp-executor.js +453 -35
  224. package/src/utils/id.js +5 -0
  225. package/system.md +3629 -0
  226. package/.agent/agents/code-assistant.json +0 -17
  227. package/.agent/agents/email-assistant.json +0 -14
  228. package/.agent/agents/file-assistant.json +0 -18
  229. package/.agent/agents/orchestrator-demo.md +0 -53
  230. package/.agent/agents/orchestrator.json +0 -7
  231. package/.agent/agents/system-assistant.json +0 -15
  232. package/.agent/agents/web-assistant.json +0 -12
  233. package/.agent/data/ambient/goals.json +0 -50
  234. package/.agent/data/ambient/memories.json +0 -7
  235. package/.agent/data/scheduler/tasks.json +0 -1
  236. package/.agent/memory/core.md +0 -1
  237. package/.agent/memory/feedback/mnrsiuoc-e1ru74.md +0 -9
  238. package/.agent/memory/feedback/mnrt2mmz-98az6n.md +0 -9
  239. package/.agent/memory/feedback/mnrtqrhm-kxsicz.md +0 -9
  240. package/.agent/memory/feedback/mnrts8vg-i0ngzp.md +0 -15
  241. package/.agent/memory/feedback/mnrtt7jt-c0trb2.md +0 -9
  242. package/.agent/memory/feedback/mnruc2f0-5s52la.md +0 -16
  243. package/.agent/memory/feedback/mnrumbmx-63sa0v.md +0 -9
  244. package/.agent/memory/project/mnn93ogy-ypjn27.md +0 -9
  245. package/.agent/memory/project/mnn98fqy-5nhc1u.md +0 -25
  246. package/.agent/memory/project/mnrp7p5n-8enm2a.md +0 -31
  247. package/.agent/memory/project/mnrp9ifb-yynks0.md +0 -40
  248. package/.agent/memory/project/mnrpb3b8-f617s4.md +0 -25
  249. package/.agent/memory/project/mnrrmqgg-focprv.md +0 -9
  250. package/.agent/memory/project/mnrtykbh-6atsor.md +0 -9
  251. package/.agent/memory/project/mnru9jiu-kgau16.md +0 -35
  252. package/.agent/memory/reference/mnq3oenw-46haj6.md +0 -63
  253. package/.agent/memory/reference/mnq5qxm2-mjoooh.md +0 -116
  254. package/.agent/memory/reference/mnrnvpwo-rcqv9m.md +0 -52
  255. package/.agent/memory/reference/mnrovxvz-zy9xqm.md +0 -25
  256. package/.agent/memory/reference/mnroxabj-1b3930.md +0 -68
  257. package/.agent/memory/reference/mnrpjtlp-mnb9od.md +0 -35
  258. package/.agent/memory/reference/mnrps1x3-6b8xfm.md +0 -28
  259. package/.agent/memory/reference/mnrpt9ov-15er5w.md +0 -22
  260. package/.agent/memory/reference/mnrq82dn-y9tv9e.md +0 -50
  261. package/.agent/memory/reference/mnrqnr5v-v75drf.md +0 -34
  262. package/.agent/memory/reference/mnrrfzys-urudaf.md +0 -31
  263. package/.agent/memory/reference/mnrrocha-t0027n.md +0 -21
  264. package/.agent/memory/reference/mnrukklc-bxndsb.md +0 -35
  265. package/.agent/memory/user/mnm67t9m-x8rekk.md +0 -9
  266. package/.agent/memory/user/mnn5mmqh-w6aktx.md +0 -11
  267. package/.agent/memory/user/mnnbfhhn-dk1bd1.md +0 -22
  268. package/.agent/memory/user/mnrt39t8-8eosy0.md +0 -9
  269. package/.agent/package.json +0 -8
  270. package/.agent/plugins/__pycache__/file_writer.cpython-312.pyc +0 -0
  271. package/.agent/plugins/daytona/README.md +0 -89
  272. package/.agent/plugins/daytona/index.js +0 -377
  273. package/.agent/plugins/daytona/package.json +0 -12
  274. package/.agent/plugins/marknative/README.md +0 -134
  275. package/.agent/plugins/marknative/fonts/SegoeUI Emoji.ttf +0 -0
  276. package/.agent/plugins/marknative/fonts.zip +0 -0
  277. package/.agent/plugins/marknative/index.js +0 -256
  278. package/.agent/plugins/marknative/package.json +0 -12
  279. package/.agent/plugins/poster-plugin/emojis/rocket.png +0 -1
  280. package/.agent/plugins/poster-plugin/test-background.svg +0 -1
  281. package/.agent/plugins/poster-plugin/test-full-poster.svg +0 -2
  282. package/.agent/plugins/poster-plugin/test-image.png +0 -0
  283. package/.agent/plugins/system-info/index.js +0 -387
  284. package/.agent/plugins/system-info/package.json +0 -4
  285. package/.agent/plugins/system-info/test.js +0 -40
  286. package/.agent/python-scripts/test_sample.py +0 -24
  287. package/.agent/skills/agent-browser/SKILL.md +0 -311
  288. package/.agent/skills/agent-browser/TEST_PLAN.md +0 -200
  289. package/.agent/skills/sysinfo/SKILL.md +0 -38
  290. package/.agent/skills/sysinfo/system-info.sh +0 -130
  291. package/.agent/skills/workflow/SKILL.md +0 -324
  292. package/.agent/test-agent.js +0 -35
  293. package/.agent/weixin.json +0 -6
  294. package/.agent/workflows/email-digest.json +0 -50
  295. package/.agent/workflows/file-backup.json +0 -21
  296. package/.agent/workflows/get-ip-notify.json +0 -32
  297. package/.agent/workflows/news-aggregator.json +0 -93
  298. package/.agent/workflows/news-dashboard-v2.json +0 -94
  299. package/.agent/workflows/notification-batch.json +0 -32
  300. package/foliko-cloud-rising.png +0 -0
  301. package/foliko-dawn-of-ai.png +0 -0
  302. package/foliko-mindful-observation.png +0 -0
  303. package/foliko-stellar-dreams.png +0 -0
  304. package/foliko-zen-jing.png +0 -0
  305. package/foliko-zen-kong.png +0 -0
  306. package/foliko-zen-wu.png +0 -0
  307. package/zen_karesansui.png +0 -0
@@ -1,9 +1,15 @@
1
1
  /**
2
- * 富文本组件 - 支持旋转和多种文本样式
2
+ * 富文本组件 - 使用 @napi-rs/canvas 原生字体 API
3
+ * 支持自动换行
3
4
  */
4
5
 
5
6
  const paper = require('paper')
6
- const { validateFont, getDefaultFont, getRegisteredFonts } = require('../fonts')
7
+ const {
8
+ validateFont,
9
+ getDefaultFontFamily,
10
+ getFontFallbackChain,
11
+ isEmojiFont
12
+ } = require('../fonts')
7
13
 
8
14
  /**
9
15
  * 检测文本是否包含 emoji
@@ -11,138 +17,199 @@ const { validateFont, getDefaultFont, getRegisteredFonts } = require('../fonts')
11
17
  function containsEmoji(text) {
12
18
  if (!text || typeof text !== 'string') return false
13
19
  const emojiRanges = [
14
- /\u{1F600}-\u{1F64F}/u, /\u{1F300}-\u{1F5FF}/u, /\u{1F680}-\u{1F6FF}/u,
15
- /\u{1F700}-\u{1F77F}/u, /\u{1F780}-\u{1F7FF}/u, /\u{1F800}-\u{1F8FF}/u,
16
- /\u{1F900}-\u{1F9FF}/u, /\u{1FA00}-\u{1FA6F}/u, /\u{1FA70}-\u{1FAFF}/u,
17
- /\u{2600}-\u{26FF}/u, /\u{2700}-\u{27BF}/u,
20
+ /\u{1F600}-\u{1F64F}/u,
21
+ /\u{1F300}-\u{1F5FF}/u,
22
+ /\u{1F680}-\u{1F6FF}/u,
23
+ /\u{1F700}-\u{1F77F}/u,
24
+ /\u{1F780}-\u{1F7FF}/u,
25
+ /\u{1F800}-\u{1F8FF}/u,
26
+ /\u{1F900}-\u{1F9FF}/u,
27
+ /\u{1FA00}-\u{1FA6F}/u,
28
+ /\u{1FA70}-\u{1FAFF}/u,
29
+ /\u2600-\u26FF/u,
30
+ /\u2700-\u27BF/u,
18
31
  ]
19
32
  for (const range of emojiRanges) {
20
33
  if (range.test(text)) return true
21
34
  }
22
- return /[\u{1F300}-\u{1F9FF}]/u.test(text)
35
+ return false
23
36
  }
24
37
 
25
38
  /**
26
- * 获取适合的字体(考虑 emoji)
39
+ * 检测文本是否包含中文
40
+ */
41
+ function containsChinese(text) {
42
+ if (!text || typeof text !== 'string') return false
43
+ return /[\u4e00-\u9fff]/.test(text)
44
+ }
45
+
46
+ /**
47
+ * 获取适合的字体
27
48
  */
28
49
  function getFontForText(requestedFont, text) {
29
- const baseFont = validateFont(requestedFont) || getDefaultFont()
30
- if (containsEmoji(text)) {
31
- const registeredFonts = getRegisteredFonts()
32
- const emojiFont = registeredFonts.find(f => {
33
- if (!f) return false
34
- const lower = f.toLowerCase()
35
- return lower.includes('color emoji') || lower.includes('noto emoji') ||
36
- lower.includes('segoe ui emoji') || lower.includes('symbola') || lower.includes('emoji')
37
- })
38
- if (emojiFont) return emojiFont
50
+ const baseFont = validateFont(requestedFont) || getDefaultFontFamily()
51
+ const chain = getFontFallbackChain(requestedFont || baseFont, text)
52
+
53
+ if (chain.length === 1) {
54
+ return chain[0]
39
55
  }
40
- return baseFont
56
+
57
+ return chain.join(', ')
58
+ }
59
+
60
+ /**
61
+ * 手动实现文本自动换行
62
+ */
63
+ function wrapText(text, maxWidth, fontSize, font) {
64
+ if (!maxWidth || maxWidth <= 0) return [text]
65
+
66
+ const tempText = new paper.PointText({
67
+ fontSize,
68
+ fontFamily: font,
69
+ })
70
+
71
+ const lines = []
72
+ const paragraphs = text.split('\n')
73
+
74
+ for (const paragraph of paragraphs) {
75
+ let currentLine = ''
76
+
77
+ // 简单字符分割
78
+ const chars = [...paragraph]
79
+
80
+ for (const char of chars) {
81
+ const testLine = currentLine + char
82
+ tempText.content = testLine
83
+ const testWidth = tempText.bounds.width
84
+
85
+ if (testWidth > maxWidth && currentLine.length > 0) {
86
+ lines.push(currentLine)
87
+ currentLine = char
88
+ } else {
89
+ currentLine = testLine
90
+ }
91
+ }
92
+
93
+ if (currentLine.length > 0) {
94
+ lines.push(currentLine)
95
+ }
96
+ }
97
+
98
+ tempText.remove()
99
+ return lines
41
100
  }
42
101
 
43
102
  /**
44
103
  * 添加富文本
45
- * @param {paper.Project} project - Paper.js 项目
46
- * @param {Object} args - 参数
47
- * @returns {Object} 结果
48
104
  */
49
105
  function addRichText(project, args) {
50
106
  const {
51
- // 位置和尺寸
52
107
  x = 0,
53
108
  y = 0,
54
- width, // 文本区域宽度(用于自动换行)
109
+ width,
55
110
 
56
- // 文本内容
57
111
  text = '',
58
112
 
59
- // 字体样式
60
113
  fontSize = 48,
61
114
  fontFamily,
62
- fontWeight, // normal, bold, 100-900
63
- fontStyle, // normal, italic, oblique
115
+ fontWeight,
116
+ fontStyle,
64
117
  italic = false,
65
118
  bold = false,
66
119
 
67
- // 文字装饰
68
120
  underline = false,
69
121
  strikethrough = false,
70
122
 
71
- // 颜色
72
123
  color = '#ffffff',
73
- backgroundColor, // 文字背景色
74
- gradient, // { colors: ['#fff', '#000'], direction: 0 }
124
+ backgroundColor,
125
+ gradient,
75
126
 
76
- // 描边
77
127
  strokeColor,
78
128
  strokeWidth = 1,
79
129
 
80
- // 阴影
81
- shadow, // { color: '#000', blur: 5, offsetX: 2, offsetY: 2 }
130
+ shadow,
82
131
 
83
- // 间距
84
132
  letterSpacing = 0,
85
- lineSpacing = 0, // 行间距增量
86
- lineHeight, // 行高
133
+ lineSpacing = 0,
134
+ lineHeight,
87
135
 
88
- // 对齐
89
- align = 'left', // left, center, right, justify
136
+ align = 'left',
90
137
 
91
- // 变换
92
- rotation = 0, // 旋转角度(度)
138
+ rotation = 0,
93
139
  scale,
94
140
 
95
- // 透明度
96
141
  opacity = 1,
97
142
 
98
- // 换行
99
- wrap = true, // 是否自动换行
143
+ wrap = true,
100
144
  } = args
101
145
 
102
146
  const font = getFontForText(fontFamily, text)
103
147
  const effectiveFontWeight = bold ? 'bold' : (fontWeight || 'normal')
104
148
  const effectiveFontStyle = italic ? 'italic' : (fontStyle || 'normal')
105
149
 
106
- // 创建文本项
107
- const textItem = new paper.PointText({
108
- point: [x, y + fontSize],
109
- content: text,
110
- fontSize,
111
- fontFamily: font,
112
- fontWeight: effectiveFontWeight,
113
- fontStyle: effectiveFontStyle,
114
- fillColor: new paper.Color(color),
115
- justification: align,
116
- })
150
+ const actualLineHeight = lineHeight || (fontSize + lineSpacing)
117
151
 
118
- // 字母间距
119
- if (letterSpacing !== 0) {
120
- textItem.letterSpacing = letterSpacing
152
+ // 文本换行
153
+ let textLines = [text]
154
+ if (wrap && width && width > 0) {
155
+ textLines = wrapText(text, width - 10, fontSize, font)
121
156
  }
122
157
 
123
- // 行高
124
- if (lineHeight) {
125
- textItem.leading = lineHeight
126
- } else if (lineSpacing !== 0) {
127
- textItem.leading = fontSize + lineSpacing
128
- }
158
+ // 创建组
159
+ const group = new paper.Group()
129
160
 
130
- // 下划线和删除线 - 使用字符样式
131
- if (underline || strikethrough) {
132
- textItem.decorations = []
133
- if (underline) textItem.decorations.push('underline')
134
- if (strikethrough) textItem.decorations.push('strikethrough')
135
- }
161
+ // 逐行创建文本
162
+ textLines.forEach((lineText, index) => {
163
+ const lineY = y + fontSize + index * actualLineHeight
164
+
165
+ const textItem = new paper.PointText({
166
+ point: [x, lineY],
167
+ content: lineText,
168
+ fontSize,
169
+ fontFamily: font,
170
+ fontWeight: effectiveFontWeight,
171
+ fontStyle: effectiveFontStyle,
172
+ fillColor: new paper.Color(color),
173
+ justification: align,
174
+ })
175
+
176
+ if (letterSpacing !== 0) {
177
+ textItem.letterSpacing = letterSpacing
178
+ }
179
+
180
+ if (underline || strikethrough) {
181
+ textItem.decorations = []
182
+ if (underline) textItem.decorations.push('underline')
183
+ if (strikethrough) textItem.decorations.push('strikethrough')
184
+ }
185
+
186
+ if (strokeColor) {
187
+ textItem.strokeColor = new paper.Color(strokeColor)
188
+ textItem.strokeWidth = strokeWidth
189
+ }
190
+
191
+ if (shadow) {
192
+ textItem.shadowColor = new paper.Color(shadow.color || '#000000')
193
+ textItem.shadowBlur = shadow.blur || 5
194
+ textItem.shadowOffset = new paper.Point(shadow.offsetX || 2, shadow.offsetY || 2)
195
+ }
196
+
197
+ if (opacity !== 1) {
198
+ textItem.opacity = opacity
199
+ }
200
+
201
+ group.addChild(textItem)
202
+ })
136
203
 
137
204
  // 渐变填充
138
205
  if (gradient && gradient.colors && gradient.colors.length > 0) {
139
206
  const colors = gradient.colors.map(c => new paper.Color(c))
140
- const bounds = textItem.bounds
207
+ const bounds = group.bounds
208
+
141
209
  if (gradient.direction !== undefined) {
142
- // 自定义方向
143
210
  const angle = gradient.direction * Math.PI / 180
144
211
  const diagonal = Math.sqrt(bounds.width ** 2 + bounds.height ** 2)
145
- textItem.fillColor = new paper.Color({
212
+ group.fillColor = new paper.Color({
146
213
  gradient: { stops: colors },
147
214
  origin: new paper.Point(
148
215
  bounds.center.x - Math.cos(angle) * diagonal / 2,
@@ -154,75 +221,61 @@ function addRichText(project, args) {
154
221
  ),
155
222
  })
156
223
  } else {
157
- // 水平渐变
158
- textItem.fillColor = new paper.Color({
224
+ group.fillColor = new paper.Color({
159
225
  gradient: { stops: colors },
160
226
  origin: bounds.topLeft,
161
227
  destination: bounds.topRight,
162
228
  })
163
229
  }
230
+
231
+ group.children.forEach(child => {
232
+ if (child.fillColor) {
233
+ child.fillColor = null
234
+ }
235
+ })
164
236
  }
165
237
 
166
- // 背景色 - 创建背景矩形
238
+ // 背景色
167
239
  let bgRect = null
168
240
  if (backgroundColor) {
169
241
  const padding = 10
170
242
  bgRect = new paper.Path.Rectangle({
171
- point: [textItem.bounds.left - padding, textItem.bounds.top - padding],
172
- size: [textItem.bounds.width + padding * 2, textItem.bounds.height + padding * 2],
243
+ point: [group.bounds.left - padding, group.bounds.top - padding],
244
+ size: [group.bounds.width + padding * 2, group.bounds.height + padding * 2],
173
245
  fillColor: new paper.Color(backgroundColor),
174
246
  radius: 4,
175
247
  })
176
248
  bgRect.sendToBack()
177
249
  }
178
250
 
179
- // 描边
180
- if (strokeColor) {
181
- textItem.strokeColor = new paper.Color(strokeColor)
182
- textItem.strokeWidth = strokeWidth
183
- }
184
-
185
- // 阴影
186
- if (shadow) {
187
- textItem.shadowColor = new paper.Color(shadow.color || '#000000')
188
- textItem.shadowBlur = shadow.blur || 5
189
- textItem.shadowOffset = new paper.Point(shadow.offsetX || 2, shadow.offsetY || 2)
190
- }
191
-
192
- // 透明度
193
- if (opacity !== 1) {
194
- textItem.opacity = opacity
195
- if (bgRect) bgRect.opacity = opacity
196
- }
197
-
198
251
  // 旋转
199
252
  if (rotation !== 0) {
200
- textItem.rotate(rotation, new paper.Point(x, y + fontSize))
253
+ group.rotate(rotation, new paper.Point(x, y + fontSize))
201
254
  if (bgRect) bgRect.rotate(rotation, bgRect.bounds.center)
202
255
  }
203
256
 
204
257
  // 缩放
205
258
  if (scale !== undefined) {
206
259
  if (typeof scale === 'number') {
207
- textItem.scale(scale, scale, new paper.Point(x, y + fontSize))
260
+ group.scale(scale, scale, new paper.Point(x, y + fontSize))
208
261
  if (bgRect) bgRect.scale(scale, scale, bgRect.bounds.center)
209
- } else if (typeof scale === 'object' && (scale.x !== undefined || scale.y !== undefined)) {
262
+ } else if (typeof scale === 'object') {
210
263
  const sx = scale.x || 1
211
264
  const sy = scale.y || sx
212
- textItem.scale(sx, sy, new paper.Point(x, y + fontSize))
265
+ group.scale(sx, sy, new paper.Point(x, y + fontSize))
213
266
  if (bgRect) bgRect.scale(sx, sy, bgRect.bounds.center)
214
267
  }
215
268
  }
216
269
 
217
270
  return {
218
271
  success: true,
219
- id: textItem.id,
272
+ id: group.id,
220
273
  type: 'richText',
221
274
  bounds: {
222
- x: textItem.bounds.x,
223
- y: textItem.bounds.y,
224
- width: textItem.bounds.width,
225
- height: textItem.bounds.height,
275
+ x: group.bounds.x,
276
+ y: group.bounds.y,
277
+ width: group.bounds.width,
278
+ height: group.bounds.height,
226
279
  }
227
280
  }
228
281
  }
@@ -1,17 +1,23 @@
1
1
  /**
2
- * 文字元素
2
+ * 文字元素 - 使用 @napi-rs/canvas 原生字体 API
3
3
  */
4
4
 
5
5
  const paper = require('paper')
6
- const { validateFont, getDefaultFont, getRegisteredFonts } = require('../fonts')
6
+ const {
7
+ validateFont,
8
+ getDefaultFontFamily,
9
+ getFontFallbackChain,
10
+ matchFont,
11
+ isEmojiFont
12
+ } = require('../fonts')
7
13
 
8
14
  /**
9
15
  * 检测文本是否包含 emoji
10
16
  */
11
17
  function containsEmoji(text) {
12
18
  if (!text || typeof text !== 'string') return false
13
-
14
- // Emoji Unicode 范围
19
+
20
+ // 使用 Unicode 属性转义检测 emoji
15
21
  const emojiRanges = [
16
22
  /\u{1F600}-\u{1F64F}/u, // 表情符号
17
23
  /\u{1F300}-\u{1F5FF}/u, // 符号和图片
@@ -22,56 +28,43 @@ function containsEmoji(text) {
22
28
  /\u{1F900}-\u{1F9FF}/u, // 表情符号补充
23
29
  /\u{1FA00}-\u{1FA6F}/u, // 棋牌符号
24
30
  /\u{1FA70}-\u{1FAFF}/u, // 装饰符号
25
- /\u{2600}-\u{26FF}/u, // 杂项符号
26
- /\u{2700}-\u{27BF}/u, // 装饰符号
27
- /[\u{1F000}-\u{1F02F}]/u, // 麻将牌
31
+ /\u2600-\u26FF/u, // 杂项符号
32
+ /\u2700-\u27BF/u, // 装饰符号
28
33
  ]
29
34
 
30
35
  for (const range of emojiRanges) {
31
- if (range.test(text)) {
32
- return true
33
- }
36
+ if (range.test(text)) return true
34
37
  }
35
38
 
36
- // 简单检测:包含常见 emoji 字符
37
- const simpleEmoji = /[\u{1F300}-\u{1F9FF}]/u
38
- return simpleEmoji.test(text)
39
+ return false
39
40
  }
40
41
 
41
42
  /**
42
- * 获取适合的字体(考虑 emoji)
43
+ * 检测文本是否包含中文
44
+ */
45
+ function containsChinese(text) {
46
+ if (!text || typeof text !== 'string') return false
47
+ return /[\u4e00-\u9fff]/.test(text)
48
+ }
49
+
50
+ /**
51
+ * 获取适合的字体(考虑 emoji 和中文)
43
52
  */
44
53
  function getFontForText(requestedFont, text) {
45
- const baseFont = validateFont(requestedFont) || getDefaultFont()
46
-
47
- // 如果文本包含 emoji,尝试使用支持 emoji 的字体
48
- if (containsEmoji(text)) {
49
- const registeredFonts = getRegisteredFonts()
50
-
51
- // 优先查找专门的 emoji 字体(多种命名方式)
52
- const emojiFont = registeredFonts.find(f => {
53
- if (!f) return false
54
- const lower = f.toLowerCase()
55
- return (
56
- lower.includes('color emoji') || // Noto Color Emoji, Apple Color Emoji
57
- lower.includes('noto emoji') || // Noto Emoji
58
- lower.includes('segoe ui emoji') || // Windows Segoe UI Emoji
59
- lower.includes('segoe ui symbol') || // Windows Segoe UI Symbol
60
- lower.includes('segui') || // Windows Segoe UI 开头
61
- lower.includes('symbola') || // Symbola
62
- lower.includes('emoji') // 任何包含 emoji 的字体
63
- )
64
- })
65
-
66
- if (emojiFont) {
67
- console.log(`[poster] 检测到 emoji,使用字体: ${emojiFont}`)
68
- return emojiFont
69
- } else {
70
- console.log(`[poster] 检测到 emoji,但未找到专用 emoji 字体,已注册字体: ${registeredFonts.join(', ')}`)
71
- }
54
+ // 获取验证后的字体
55
+ const baseFont = validateFont(requestedFont) || getDefaultFontFamily()
56
+
57
+ // 获取字体 fallback 链
58
+ const chain = getFontFallbackChain(requestedFont || baseFont, text)
59
+
60
+ // 对于 @napi-rs/canvas,逗号分隔的字体链应该能正常工作
61
+ // 如果只有一个字体,就直接返回
62
+ if (chain.length === 1) {
63
+ return chain[0]
72
64
  }
73
65
 
74
- return baseFont
66
+ // 返回字体链(逗号分隔)
67
+ return chain.join(', ')
75
68
  }
76
69
 
77
70
  /**
@@ -81,13 +74,17 @@ function addText(project, args) {
81
74
  const {
82
75
  text,
83
76
  x, y,
84
- fontSize, fontFamily, color,
77
+ fontSize,
78
+ fontFamily,
79
+ color,
85
80
  align,
86
81
  shadow,
87
82
  } = args
88
83
 
84
+ // 获取适合的字体
89
85
  const font = getFontForText(fontFamily, text)
90
86
 
87
+ // 创建文本元素
91
88
  const textItem = new paper.PointText({
92
89
  point: [x, y],
93
90
  content: text,
@@ -97,13 +94,19 @@ function addText(project, args) {
97
94
  justification: align || 'left',
98
95
  })
99
96
 
97
+ // 添加阴影
100
98
  if (shadow) {
101
- textItem.shadowColor = new paper.Color(shadow.color)
99
+ textItem.shadowColor = new paper.Color(shadow.color || '#000000')
102
100
  textItem.shadowBlur = shadow.blur || 5
103
101
  textItem.shadowOffset = new paper.Point(shadow.offsetX || 2, shadow.offsetY || 2)
104
102
  }
105
103
 
106
- return { success: true, id: textItem.id, type: 'text' }
104
+ return {
105
+ success: true,
106
+ id: textItem.id,
107
+ type: 'text',
108
+ font: font
109
+ }
107
110
  }
108
111
 
109
112
  module.exports = addText