foliko 1.1.7 → 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 (250) 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 +172 -173
  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 +122 -689
  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/.claude/settings.local.json +1 -2
  190. package/.env.example +56 -56
  191. package/README.md +441 -441
  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/default-plugins.js +2 -1
  197. package/plugins/extension-executor-plugin.js +24 -1
  198. package/plugins/feishu-plugin.js +2 -2
  199. package/plugins/telegram-plugin.js +2 -2
  200. package/plugins/weixin-plugin.js +2 -2
  201. package/skills/find-skills/AGENTS.md +162 -162
  202. package/skills/find-skills/SKILL.md +133 -133
  203. package/src/core/agent.js +117 -29
  204. package/src/executors/mcp-executor.js +282 -26
  205. package/system.md +719 -570
  206. package/.agent/agents/code-assistant.json +0 -17
  207. package/.agent/agents/email-assistant.json +0 -14
  208. package/.agent/agents/file-assistant.json +0 -18
  209. package/.agent/agents/orchestrator-demo.md +0 -53
  210. package/.agent/agents/orchestrator.json +0 -7
  211. package/.agent/agents/system-assistant.json +0 -15
  212. package/.agent/agents/web-assistant.json +0 -12
  213. package/.agent/data/email/processed-emails.json +0 -1
  214. package/.agent/data/scheduler/tasks.json +0 -1
  215. package/.agent/data/web/web-config.json +0 -5
  216. package/.agent/memory/feedback/mnt7jrlt-d67qs7.md +0 -15
  217. package/.agent/memory/feedback/mnt88ja3-al4fuy.md +0 -9
  218. package/.agent/package.json +0 -8
  219. package/.agent/plugins/__pycache__/file_writer.cpython-312.pyc +0 -0
  220. package/.agent/plugins/daytona/README.md +0 -89
  221. package/.agent/plugins/daytona/index.js +0 -377
  222. package/.agent/plugins/daytona/package.json +0 -12
  223. package/.agent/plugins/marknative/README.md +0 -134
  224. package/.agent/plugins/marknative/fonts/SegoeUI Emoji.ttf +0 -0
  225. package/.agent/plugins/marknative/fonts.zip +0 -0
  226. package/.agent/plugins/marknative/index.js +0 -256
  227. package/.agent/plugins/marknative/package.json +0 -12
  228. package/.agent/plugins/poster-plugin/emojis/rocket.png +0 -1
  229. package/.agent/plugins/poster-plugin/test-background.svg +0 -1
  230. package/.agent/plugins/poster-plugin/test-full-poster.svg +0 -2
  231. package/.agent/plugins/poster-plugin/test-image.png +0 -0
  232. package/.agent/plugins/system-info/index.js +0 -387
  233. package/.agent/plugins/system-info/package.json +0 -4
  234. package/.agent/plugins/system-info/test.js +0 -40
  235. package/.agent/plugins/test-plugin.py +0 -108
  236. package/.agent/python-scripts/test_sample.py +0 -24
  237. package/.agent/skills/agent-browser/SKILL.md +0 -311
  238. package/.agent/skills/agent-browser/TEST_PLAN.md +0 -200
  239. package/.agent/skills/sysinfo/SKILL.md +0 -38
  240. package/.agent/skills/sysinfo/system-info.sh +0 -130
  241. package/.agent/skills/workflow/SKILL.md +0 -324
  242. package/.agent/test-agent.js +0 -35
  243. package/.agent/weixin.json +0 -6
  244. package/.agent/workflows/email-digest.json +0 -50
  245. package/.agent/workflows/file-backup.json +0 -21
  246. package/.agent/workflows/get-ip-notify.json +0 -32
  247. package/.agent/workflows/news-aggregator.json +0 -93
  248. package/.agent/workflows/news-dashboard-v2.json +0 -94
  249. package/.agent/workflows/notification-batch.json +0 -32
  250. package/output/zen_silence.png +0 -0
@@ -16,6 +16,7 @@ const createRibbon = require('./components/ribbon')
16
16
  const createSeal = require('./components/seal')
17
17
  const createHighlightText = require('./components/highlightText')
18
18
  const createBarcode = require('./components/barcode')
19
+ const createQuote = require('./components/quote')
19
20
  const { loadImageAsRaster } = require('./utils/imageLoader')
20
21
 
21
22
  // 组件包装函数
@@ -46,6 +47,9 @@ async function createHighlightTextComponent(project, canvas, args) {
46
47
  async function createBarcodeComponent(project, canvas, args) {
47
48
  return await createBarcode(project, args)
48
49
  }
50
+ async function createQuoteComponentWrapper(project, canvas, args) {
51
+ return await createQuote(project, canvas, args)
52
+ }
49
53
 
50
54
  /**
51
55
  * 辅助函数:将元素添加到活跃层
@@ -178,6 +182,8 @@ async function createComponent(project, canvas, config) {
178
182
  return createTextElement(project, args)
179
183
  case 'artText':
180
184
  return await createArtTextElement(project, args)
185
+ case 'richText':
186
+ return await createRichTextElement(project, args)
181
187
  case 'image':
182
188
  return await createImageElement(project, args)
183
189
  case 'svg':
@@ -225,7 +231,7 @@ async function createComponent(project, canvas, config) {
225
231
  case 'rating':
226
232
  return await createRatingComponent(project, canvas, args)
227
233
  case 'quote':
228
- return await createQuoteComponent(project, canvas, args)
234
+ return await createQuoteComponentWrapper(project, canvas, args)
229
235
  case 'statCard':
230
236
  return await createStatCardComponent(project, canvas, args)
231
237
  case 'tagCloud':
@@ -452,15 +458,38 @@ function createPolygonElement(project, { cx, cy, radius, sides, fill, stroke, st
452
458
  }
453
459
 
454
460
  function createTextElement(project, { text, x, y, fontSize, fontFamily, color, align, shadow }) {
455
- const { validateFont, getDefaultFont } = require('./fonts')
461
+ const { validateFont, getDefaultFontFamily, getFontFallbackChain } = require('./fonts')
462
+
463
+ const fontSizeVal = fontSize || 48
464
+ const textColor = color || '#ffffff'
465
+ const alignment = align || 'left'
466
+
467
+ // 计算文字宽度用于居中/右对齐
468
+ let offsetX = 0
469
+ if (alignment === 'center' || alignment === 'right') {
470
+ // 估算文字宽度:中文约 1.0 倍字体大小,英文约 0.5 倍
471
+ const chineseChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length
472
+ const otherChars = text.length - chineseChars
473
+ const textWidth = chineseChars * fontSizeVal * 1.0 + otherChars * fontSizeVal * 0.5
474
+
475
+ if (alignment === 'center') {
476
+ offsetX = -textWidth / 2
477
+ } else if (alignment === 'right') {
478
+ offsetX = -textWidth
479
+ }
480
+ }
481
+
482
+ // 获取字体链(支持 @napi-rs/canvas 字体回退)
483
+ const chain = getFontFallbackChain(fontFamily, text)
484
+ const finalFontFamily = chain.length === 1 ? chain[0] : chain.join(', ')
456
485
 
457
486
  const textItem = new paper.PointText({
458
- point: [x, y],
487
+ point: [x + offsetX, y],
459
488
  content: text,
460
- fontSize: fontSize || 48,
461
- fontFamily: validateFont(fontFamily) || getDefaultFont(),
462
- fillColor: new paper.Color(color || '#ffffff'),
463
- justification: align || 'left',
489
+ fontSize: fontSizeVal,
490
+ fontFamily: finalFontFamily,
491
+ fillColor: new paper.Color(textColor),
492
+ justification: alignment,
464
493
  })
465
494
 
466
495
  if (shadow) {
@@ -477,13 +506,17 @@ function createTextElement(project, { text, x, y, fontSize, fontFamily, color, a
477
506
  }
478
507
 
479
508
  function createArtTextElement(project, { text, x, y, fontSize, fontFamily, gradient, strokeColor, strokeWidth, shadow }) {
480
- const { validateFont, getDefaultFont } = require('./fonts')
509
+ const { getFontFallbackChain } = require('./fonts')
510
+
511
+ // 获取字体链
512
+ const chain = getFontFallbackChain(fontFamily, text)
513
+ const finalFont = chain.length === 1 ? chain[0] : chain.join(', ')
481
514
 
482
515
  const textItem = new paper.PointText({
483
516
  point: [x, y],
484
517
  content: text,
485
518
  fontSize: fontSize || 120,
486
- fontFamily: validateFont(fontFamily) || getDefaultFont(),
519
+ fontFamily: finalFont,
487
520
  fillColor: gradient ? new paper.Color(gradient.colors[0]) : new paper.Color('#ffffff'),
488
521
  justification: 'center',
489
522
  })
@@ -529,6 +562,135 @@ async function createImageElement(project, { src, x = 0, y = 0, width, height, o
529
562
  }
530
563
  }
531
564
 
565
+ /**
566
+ * 创建富文本元素(支持多行文本和自动换行)
567
+ */
568
+ function createRichTextElement(project, {
569
+ x = 0,
570
+ y = 0,
571
+ width,
572
+ text = '',
573
+ fontSize = 48,
574
+ fontFamily = 'sans-serif',
575
+ color = '#ffffff',
576
+ align = 'left',
577
+ lineHeight = fontSize * 1.4,
578
+ letterSpacing = 0,
579
+ opacity = 1,
580
+ rotation = 0,
581
+ shadow = null,
582
+ }) {
583
+ const { getFontFallbackChain } = require('./fonts')
584
+
585
+ // 获取字体链
586
+ const chain = getFontFallbackChain(fontFamily, text)
587
+ const finalFontFamily = chain.length === 1 ? chain[0] : chain.join(', ')
588
+
589
+ // 估算字符宽度(中文约等于 fontSize,英文约等于 fontSize * 0.5)
590
+ const getCharWidth = (char) => {
591
+ return /[\u4e00-\u9fa5]/.test(char) ? fontSize : fontSize * 0.5
592
+ }
593
+
594
+ // 计算文本宽度
595
+ const calcTextWidth = (str) => {
596
+ let width = 0
597
+ for (const char of str) {
598
+ width += getCharWidth(char)
599
+ }
600
+ return width + (str.length - 1) * letterSpacing
601
+ }
602
+
603
+ // 处理自动换行
604
+ const lines = []
605
+ if (width) {
606
+ const paragraphs = text.split('\n')
607
+ for (const paragraph of paragraphs) {
608
+ if (!paragraph) {
609
+ lines.push('')
610
+ continue
611
+ }
612
+ let currentLine = ''
613
+ let currentWidth = 0
614
+ for (const char of paragraph) {
615
+ const charWidth = getCharWidth(char) + letterSpacing
616
+ if (currentWidth + charWidth > width && currentLine) {
617
+ lines.push(currentLine)
618
+ currentLine = char
619
+ currentWidth = charWidth
620
+ } else {
621
+ currentLine += char
622
+ currentWidth += charWidth
623
+ }
624
+ }
625
+ if (currentLine) {
626
+ lines.push(currentLine)
627
+ }
628
+ }
629
+ } else {
630
+ lines.push(text)
631
+ }
632
+
633
+ // 创建文本元素组
634
+ const group = new paper.Group()
635
+ const textItems = []
636
+
637
+ for (let i = 0; i < lines.length; i++) {
638
+ const line = lines[i]
639
+ const lineY = y + i * lineHeight
640
+
641
+ // 计算对齐偏移
642
+ let offsetX = 0
643
+ if (align === 'center' && width) {
644
+ offsetX = (width - calcTextWidth(line)) / 2
645
+ } else if (align === 'right' && width) {
646
+ offsetX = width - calcTextWidth(line)
647
+ }
648
+
649
+ const textItem = new paper.PointText({
650
+ point: [x + offsetX, lineY + fontSize],
651
+ content: line,
652
+ fontSize: fontSize,
653
+ fontFamily: finalFontFamily,
654
+ fillColor: new paper.Color(color),
655
+ justification: align,
656
+ })
657
+
658
+ if (letterSpacing !== 0) {
659
+ textItem.letterSpacing = letterSpacing
660
+ }
661
+
662
+ if (opacity !== 1) {
663
+ textItem.opacity = opacity
664
+ }
665
+
666
+ if (rotation !== 0) {
667
+ textItem.rotate(rotation, new paper.Point(x, y))
668
+ }
669
+
670
+ if (shadow) {
671
+ textItem.shadowColor = new paper.Color(shadow.color || 'rgba(0,0,0,0.5)')
672
+ textItem.shadowBlur = shadow.blur || 5
673
+ textItem.shadowOffset = new paper.Point(shadow.offsetX || 2, shadow.offsetY || 2)
674
+ }
675
+
676
+ textItems.push(textItem)
677
+ group.addChild(textItem)
678
+ }
679
+
680
+ // 添加到项目
681
+ if (project && project.activeLayer) {
682
+ project.activeLayer.addChild(group)
683
+ }
684
+
685
+ return {
686
+ success: true,
687
+ id: group.id,
688
+ type: 'richText',
689
+ lines: lines.length,
690
+ height: lines.length * lineHeight,
691
+ }
692
+ }
693
+
532
694
  const fs = require('fs')
533
695
 
534
696
  async function createSVGElement(project, { src, x = 0, y = 0, width, height, opacity = 1 }) {
@@ -1777,15 +1939,15 @@ function createGridComponent(project, canvas, {
1777
1939
  * 创建星形组件
1778
1940
  */
1779
1941
  function createStarComponent(project, canvas, {
1780
- cx, cy, points = 5, innerRadius: providedInnerRadius, outerRadius,
1942
+ cx, cy, points = 5, innerRadius, outerRadius,
1781
1943
  fill, stroke, strokeWidth = 1, opacity = 1, rotation = 0
1782
1944
  }) {
1783
- const innerRadius = providedInnerRadius || outerRadius * 0.4
1945
+ const actualInnerRadius = innerRadius || outerRadius * 0.4
1784
1946
  const path = new paper.Path()
1785
1947
  const angleStep = Math.PI / points
1786
1948
 
1787
1949
  for (let i = 0; i < points * 2; i++) {
1788
- const radius = i % 2 === 0 ? outerRadius : innerRadius
1950
+ const radius = i % 2 === 0 ? outerRadius : actualInnerRadius
1789
1951
  const angle = i * angleStep - Math.PI / 2 + (rotation * Math.PI / 180)
1790
1952
  const x = cx + radius * Math.cos(angle)
1791
1953
  const y = cy + radius * Math.sin(angle)
@@ -2042,11 +2204,17 @@ function createWatermarkComponent(project, canvas, {
2042
2204
  text, cx, cy, color = 'rgba(0,0,0,0.1)', fontSize = 48,
2043
2205
  fontFamily = 'sans-serif', opacity = 0.1, rotation = 0, align = 'center'
2044
2206
  }) {
2207
+ const { getFontFallbackChain } = require('./fonts')
2208
+
2209
+ // 获取字体链
2210
+ const chain = getFontFallbackChain(fontFamily, text)
2211
+ const finalFont = chain.length === 1 ? chain[0] : chain.join(', ')
2212
+
2045
2213
  const label = new paper.PointText({
2046
2214
  point: [cx, cy],
2047
2215
  content: text,
2048
2216
  fontSize: fontSize,
2049
- fontFamily: fontFamily,
2217
+ fontFamily: finalFont,
2050
2218
  fillColor: new paper.Color(color),
2051
2219
  justification: align,
2052
2220
  opacity: opacity
@@ -1,9 +1,13 @@
1
1
  /**
2
- * 艺术文字元素
2
+ * 艺术文字元素 - 使用 @napi-rs/canvas 原生字体 API
3
3
  */
4
4
 
5
5
  const paper = require('paper')
6
- const { validateFont, getDefaultFont } = require('../fonts')
6
+ const {
7
+ validateFont,
8
+ getDefaultFontFamily,
9
+ getFontFallbackChain
10
+ } = require('../fonts')
7
11
 
8
12
  /**
9
13
  * 添加艺术文字
@@ -20,18 +24,23 @@ function addArtText(project, args) {
20
24
  shadow,
21
25
  } = args
22
26
 
27
+ // 获取字体
28
+ const baseFont = validateFont(fontFamily) || getDefaultFontFamily()
29
+ const chain = getFontFallbackChain(fontFamily || baseFont, text)
30
+ const font = chain.length === 1 ? chain[0] : chain.join(', ')
31
+
23
32
  const textItem = new paper.PointText({
24
33
  point: [x, y],
25
34
  content: text,
26
35
  fontSize: fontSize || 120,
27
- fontFamily: validateFont(fontFamily) || getDefaultFont(),
36
+ fontFamily: font,
28
37
  fillColor: gradient
29
38
  ? new paper.Color(gradient.colors[0])
30
39
  : new paper.Color('#ffffff'),
31
40
  justification: 'center',
32
41
  })
33
42
 
34
- // 应用渐变
43
+ // 渐变填充
35
44
  if (gradient && gradient.colors.length > 0) {
36
45
  const colors = gradient.colors.map(c => new paper.Color(c))
37
46
  textItem.fillColor = new paper.Color({
@@ -41,15 +50,15 @@ function addArtText(project, args) {
41
50
  })
42
51
  }
43
52
 
44
- // 应用描边
53
+ // 描边
45
54
  if (strokeColor) {
46
55
  textItem.strokeColor = new paper.Color(strokeColor)
47
56
  textItem.strokeWidth = strokeWidth || 2
48
57
  }
49
58
 
50
- // 应用阴影
59
+ // 阴影
51
60
  if (shadow) {
52
- textItem.shadowColor = new paper.Color(shadow.color)
61
+ textItem.shadowColor = new paper.Color(shadow.color || '#000000')
53
62
  textItem.shadowBlur = shadow.blur || 10
54
63
  textItem.shadowOffset = new paper.Point(shadow.offsetX || 3, shadow.offsetY || 3)
55
64
  }
@@ -46,6 +46,7 @@ async function addBackground(project, canvas, args) {
46
46
  const { type, colors, direction } = gradient
47
47
  const paperColors = colors.map(c => new paper.Color(c))
48
48
 
49
+ let gradientConfig
49
50
  if (type === 'linear') {
50
51
  const angle = (direction || 45) * Math.PI / 180
51
52
  const diagonal = Math.sqrt(canvas.width ** 2 + canvas.height ** 2)
@@ -57,23 +58,37 @@ async function addBackground(project, canvas, args) {
57
58
  canvas.width / 2 + Math.cos(angle) * diagonal / 2,
58
59
  canvas.height / 2 + Math.sin(angle) * diagonal / 2
59
60
  )
60
- project.activeLayer.fillColor = new paper.Color({
61
+ gradientConfig = {
61
62
  gradient: { stops: paperColors },
62
63
  origin: start,
63
64
  destination: stop,
64
- })
65
+ }
65
66
  } else {
66
67
  // radial
67
68
  const center = new paper.Point(canvas.width / 2, canvas.height / 2)
68
69
  const radius = Math.max(canvas.width, canvas.height) / 2
69
- project.activeLayer.fillColor = new paper.Color({
70
+ gradientConfig = {
70
71
  gradient: { stops: paperColors },
71
72
  origin: center,
72
73
  destination: center.add(new paper.Point(radius, 0)),
73
- })
74
+ }
74
75
  }
76
+
77
+ // 创建矩形背景元素(确保导出时能正确渲染渐变)
78
+ const bg = new paper.Path.Rectangle({
79
+ point: [0, 0],
80
+ size: [canvas.width, canvas.height],
81
+ fillColor: new paper.Color(gradientConfig),
82
+ })
83
+ bg.sendToBack()
75
84
  } else if (color) {
76
- project.activeLayer.fillColor = new paper.Color(color)
85
+ // 创建矩形背景元素(确保导出时能正确渲染)
86
+ const bg = new paper.Path.Rectangle({
87
+ point: [0, 0],
88
+ size: [canvas.width, canvas.height],
89
+ fillColor: new paper.Color(color),
90
+ })
91
+ bg.sendToBack()
77
92
  } else {
78
93
  throw new Error('Must provide color, gradient, or image')
79
94
  }