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
@@ -1,28 +1,19 @@
1
1
  /**
2
2
  * 图片框组件 - 带装饰边框的图片
3
+ * 使用 Paper.js API 实现
3
4
  */
4
- const createImageFrame = (ctx) => ({
5
- /**
6
- * @param {Object} params
7
- * @param {string} params.src - 图片路径或URL
8
- * @param {number} params.x - X坐标
9
- * @param {number} params.y - Y坐标
10
- * @param {number} params.width - 图片宽度
11
- * @param {number} params.height - 图片高度
12
- * @param {string} [params.borderColor='#ffffff'] - 边框颜色
13
- * @param {number} [params.borderWidth=3] - 边框宽度
14
- * @param {string} [params.outerColor='#1a1a2e'] - 外边框颜色
15
- * @param {number} [params.outerWidth=6] - 外边框宽度
16
- * @param {number} [params.shadowBlur=0] - 阴影模糊
17
- * @param {number} [params.shadowOffsetX=0] - 阴影X偏移
18
- * @param {number} [params.shadowOffsetY=0] - 阴影Y偏移
19
- * @param {string} [params.shadowColor='rgba(0,0,0,0.3)'] - 阴影颜色
20
- * @param {number} [params.radius=0] - 圆角半径
21
- * @param {string} [params.overlayColor] - 叠加颜色
22
- * @param {number} [params.overlayOpacity=0] - 叠加透明度
23
- * @param {string} [params.fit='cover'] - 图片填充方式: cover, contain, fill
24
- */
25
- async draw({
5
+
6
+ const paper = require('paper')
7
+
8
+ /**
9
+ * 创建图片框组件
10
+ *
11
+ * @param {Object} project - Paper.js 项目
12
+ * @param {Object} canvas - 画布对象
13
+ * @param {Object} params - 组件参数
14
+ */
15
+ function createImageFrame(project, canvas, params) {
16
+ const {
26
17
  src,
27
18
  x,
28
19
  y,
@@ -40,33 +31,77 @@ const createImageFrame = (ctx) => ({
40
31
  overlayColor,
41
32
  overlayOpacity = 0,
42
33
  fit = 'cover'
43
- }) {
44
- // 绘制外边框(装饰层)
45
- if (outerWidth > 0) {
46
- ctx.fillStyle = outerColor
47
- await _roundRect(ctx, x - outerWidth, y - outerWidth, width + outerWidth * 2, height + outerWidth * 2, radius + outerWidth)
48
- ctx.fill()
49
- }
34
+ } = params
35
+
36
+ const elements = []
50
37
 
51
- // 绘制内边框
52
- if (borderWidth > 0) {
53
- ctx.fillStyle = borderColor
54
- await _roundRect(ctx, x - borderWidth, y - borderWidth, width + borderWidth * 2, height + borderWidth * 2, radius + borderWidth)
55
- ctx.fill()
38
+ // 辅助函数:创建圆角矩形路径
39
+ function createRoundedRectPath(x, y, w, h, r) {
40
+ const rect = new paper.Path.Rectangle({
41
+ point: [x, y],
42
+ size: [w, h],
43
+ radius: r
44
+ })
45
+ return rect
46
+ }
47
+
48
+ // 绘制阴影
49
+ if (shadowBlur > 0) {
50
+ // 使用 Paper.js 的 shadow 功能
51
+ // 注意:Paper.js 对阴影支持有限,这里使用简单实现
52
+ }
53
+
54
+ // 绘制外边框(装饰层)
55
+ if (outerWidth > 0) {
56
+ const outerRect = createRoundedRectPath(
57
+ x - outerWidth,
58
+ y - outerWidth,
59
+ width + outerWidth * 2,
60
+ height + outerWidth * 2,
61
+ radius + outerWidth
62
+ )
63
+ outerRect.fillColor = new paper.Color(outerColor)
64
+ if (project && project.activeLayer) {
65
+ project.activeLayer.addChild(outerRect)
56
66
  }
67
+ elements.push({ type: 'path', id: outerRect.id })
68
+ }
57
69
 
58
- // 绘制阴影
59
- if (shadowBlur > 0) {
60
- ctx.shadowColor = shadowColor
61
- ctx.shadowBlur = shadowBlur
62
- ctx.shadowOffsetX = shadowOffsetX
63
- ctx.shadowOffsetY = shadowOffsetY
70
+ // 绘制内边框
71
+ if (borderWidth > 0) {
72
+ const borderRect = createRoundedRectPath(
73
+ x - borderWidth,
74
+ y - borderWidth,
75
+ width + borderWidth * 2,
76
+ height + borderWidth * 2,
77
+ radius + borderWidth
78
+ )
79
+ borderRect.fillColor = new paper.Color(borderColor)
80
+ if (project && project.activeLayer) {
81
+ project.activeLayer.addChild(borderRect)
64
82
  }
83
+ elements.push({ type: 'path', id: borderRect.id })
84
+ }
85
+
86
+ // 创建图片容器(裁剪区域)
87
+ const clipRect = createRoundedRectPath(x, y, width, height, radius)
88
+
89
+ // 创建裁剪组
90
+ const clipGroup = new paper.Group()
91
+ clipGroup.addChild(clipRect)
92
+
93
+ if (project && project.activeLayer) {
94
+ project.activeLayer.addChild(clipGroup)
95
+ }
96
+ elements.push({ type: 'group', id: clipGroup.id })
65
97
 
66
- // 加载并绘制图片
67
- const image = await _loadImage(src)
68
- const imgWidth = image.width
69
- const imgHeight = image.height
98
+ // 加载并添加图片
99
+ loadImageAsync(src).then((loadedRaster) => {
100
+ if (!loadedRaster) return
101
+
102
+ // loadedRaster 已经是 Raster 对象
103
+ const imgWidth = loadedRaster.width
104
+ const imgHeight = loadedRaster.height
70
105
  const imgRatio = imgWidth / imgHeight
71
106
  const boxRatio = width / height
72
107
 
@@ -94,61 +129,76 @@ const createImageFrame = (ctx) => ({
94
129
  }
95
130
  }
96
131
 
97
- // 裁剪并绘制图片
98
- ctx.save()
99
- await _roundRect(ctx, x, y, width, height, radius)
100
- ctx.clip()
101
- ctx.drawImage(image, drawX, drawY, drawW, drawH)
102
- ctx.restore()
103
-
104
- // 重置阴影
105
- ctx.shadowColor = 'transparent'
106
- ctx.shadowBlur = 0
107
- ctx.shadowOffsetX = 0
108
- ctx.shadowOffsetY = 0
109
-
110
- // 叠加颜色
111
- if (overlayColor && overlayOpacity > 0) {
112
- ctx.fillStyle = overlayColor
113
- ctx.globalAlpha = overlayOpacity
114
- await _roundRect(ctx, x, y, width, height, radius)
115
- ctx.fill()
116
- ctx.globalAlpha = 1
132
+ loadedRaster.bounds = new paper.Rectangle(drawX, drawY, drawW, drawH)
133
+
134
+ // 应用裁剪
135
+ loadedRaster.clipped = true
136
+ loadedRaster.clipMask = clipRect
137
+
138
+ if (project && project.activeLayer) {
139
+ // 将图片添加到裁剪组
140
+ clipGroup.addChild(loadedRaster)
117
141
  }
142
+ })
143
+
144
+ // 叠加颜色
145
+ if (overlayColor && overlayOpacity > 0) {
146
+ const overlayRect = createRoundedRectPath(x, y, width, height, radius)
147
+ overlayRect.fillColor = new paper.Color(overlayColor)
148
+ overlayRect.fillColor.alpha = overlayOpacity
149
+ if (project && project.activeLayer) {
150
+ project.activeLayer.addChild(overlayRect)
151
+ }
152
+ elements.push({ type: 'path', id: overlayRect.id })
153
+ }
154
+
155
+ return {
156
+ success: true,
157
+ elements,
158
+ width: width,
159
+ height: height,
160
+ type: 'imageFrame'
118
161
  }
119
- })
120
-
121
- // 辅助函数:圆角矩形
122
- async function _roundRect(ctx, x, y, width, height, radius) {
123
- const r = Math.min(radius, width / 2, height / 2)
124
- ctx.beginPath()
125
- ctx.moveTo(x + r, y)
126
- ctx.lineTo(x + width - r, y)
127
- ctx.quadraticCurveTo(x + width, y, x + width, y + r)
128
- ctx.lineTo(x + width, y + height - r)
129
- ctx.quadraticCurveTo(x + width, y + height, x + width - r, y + height)
130
- ctx.lineTo(x + r, y + height)
131
- ctx.quadraticCurveTo(x, y + height, x, y + height - r)
132
- ctx.lineTo(x, y + r)
133
- ctx.quadraticCurveTo(x, y, x + r, y)
134
- ctx.closePath()
135
162
  }
136
163
 
137
- // 辅助函数:加载图片
138
- async function _loadImage(src) {
139
- return new Promise((resolve, reject) => {
140
- const img = new Image()
141
- img.crossOrigin = 'anonymous'
142
- img.onload = () => resolve(img)
143
- img.onerror = (e) => {
144
- // 尝试 data URL 格式
145
- if (src.startsWith('data:')) {
146
- img.src = src
147
- } else {
148
- reject(new Error(`Failed to load image: ${src}`))
164
+ // 异步加载图片
165
+ function loadImageAsync(src) {
166
+ return new Promise((resolve) => {
167
+ try {
168
+ // 尝试作为 URL 加载
169
+ const raster = new paper.Raster(src)
170
+
171
+ raster.onLoad = () => {
172
+ // Paper.js Raster 本身就可以使用,不需要 .image
173
+ resolve(raster)
174
+ }
175
+
176
+ raster.onError = () => {
177
+ // 尝试作为本地文件
178
+ const fs = require('fs')
179
+ const path = require('path')
180
+
181
+ if (fs.existsSync(src)) {
182
+ const data = fs.readFileSync(src)
183
+ const base64 = data.toString('base64')
184
+ const ext = path.extname(src).slice(1).toLowerCase()
185
+ const mimeType = ext === 'jpg' ? 'jpeg' : ext
186
+ const dataUrl = `data:image/${mimeType};base64,${base64}`
187
+
188
+ const raster2 = new paper.Raster(dataUrl)
189
+ raster2.onLoad = () => {
190
+ resolve(raster2)
191
+ }
192
+ raster2.onError = () => {
193
+ resolve(null)
194
+ }
195
+ } else {
196
+ resolve(null)
197
+ }
149
198
  }
199
+ } catch (e) {
200
+ resolve(null)
150
201
  }
151
- img.src = src
152
202
  })
153
203
  }
154
204
 
@@ -1,8 +1,55 @@
1
1
  /**
2
- * 引用块组件
2
+ * 引用块组件 - 支持自动换行
3
3
  */
4
4
 
5
5
  const paper = require('paper')
6
+ const { getFontFallbackChain } = require('../fonts')
7
+
8
+ /**
9
+ * 手动实现文本自动换行
10
+ */
11
+ function wrapText(text, maxWidth, fontSize, font) {
12
+ if (!maxWidth || maxWidth <= 0) return [text]
13
+
14
+ const tempText = new paper.PointText({
15
+ fontSize,
16
+ fontFamily: font,
17
+ })
18
+
19
+ const lines = []
20
+ const paragraphs = text.split('\n')
21
+
22
+ for (let p = 0; p < paragraphs.length; p++) {
23
+ let currentLine = ''
24
+ const paragraph = paragraphs[p]
25
+ let i = 0
26
+
27
+ while (i < paragraph.length) {
28
+ // 尝试获取下一个字符
29
+ let char = paragraph[i]
30
+ let testLine = currentLine + char
31
+
32
+ tempText.content = testLine
33
+ let testWidth = tempText.bounds.width
34
+
35
+ // 如果加上下一个字符超过宽度
36
+ if (testWidth > maxWidth && currentLine.length > 0) {
37
+ lines.push(currentLine)
38
+ currentLine = char
39
+ } else {
40
+ currentLine = testLine
41
+ }
42
+ i++
43
+ }
44
+
45
+ if (currentLine.length > 0) {
46
+ lines.push(currentLine)
47
+ }
48
+ }
49
+
50
+ tempText.remove()
51
+ return lines
52
+ }
6
53
 
7
54
  /**
8
55
  * 创建引用块
@@ -38,14 +85,31 @@ function createQuote(project, canvas, args) {
38
85
  textColor = '#1e293b',
39
86
  authorColor = '#64748b',
40
87
  fontSize = 18,
88
+ fontFamily,
41
89
  } = args
42
90
 
91
+ // 获取字体回退链
92
+ const quoteFont = getFontFallbackChain(fontFamily, text || '').join(', ')
93
+
43
94
  const elements = []
95
+
96
+ // 计算文本换行
97
+ const textPadding = padding + 30 // 左边距 + 引号宽度
98
+ const maxTextWidth = width - textPadding - padding
99
+ const textLines = wrapText(text, maxTextWidth, fontSize, quoteFont)
100
+ const lineHeight = fontSize * 1.5
101
+ const textBlockHeight = textLines.length * lineHeight
102
+
103
+ // 计算背景高度
104
+ const authorHeight = author ? fontSize * 1.5 : 0
105
+ const totalHeight = padding * 2 + textBlockHeight + authorHeight
106
+ const minHeight = 80
107
+ const finalHeight = Math.max(totalHeight, minHeight)
44
108
 
45
109
  // 绘制背景
46
110
  const bg = new paper.Path.Rectangle({
47
111
  point: [x, y],
48
- size: [width, author ? 80 + fontSize * 2 : 40 + fontSize * 1.5],
112
+ size: [width, finalHeight],
49
113
  radius: radius,
50
114
  })
51
115
  bg.fillColor = new paper.Color(background)
@@ -54,7 +118,7 @@ function createQuote(project, canvas, args) {
54
118
  // 绘制左边框
55
119
  const border = new paper.Path.Rectangle({
56
120
  point: [x, y],
57
- size: [borderWidth, author ? 80 + fontSize * 2 : 40 + fontSize * 1.5],
121
+ size: [borderWidth, finalHeight],
58
122
  })
59
123
  border.fillColor = new paper.Color(borderColor)
60
124
  elements.push({ type: 'rectangle', id: border.id })
@@ -64,27 +128,35 @@ function createQuote(project, canvas, args) {
64
128
  point: [x + padding + 10, y + padding + fontSize],
65
129
  content: '"',
66
130
  fontSize: fontSize * 2,
131
+ fontFamily: quoteFont,
67
132
  fillColor: new paper.Color(borderColor),
68
133
  justification: 'left',
69
134
  })
70
135
  elements.push({ type: 'text', id: quoteMark.id })
71
136
 
72
- // 绘制引用文字
73
- const quoteText = new paper.PointText({
74
- point: [x + padding + 30, y + padding + fontSize * 1.5],
75
- content: text,
76
- fontSize: fontSize,
77
- fillColor: new paper.Color(textColor),
78
- justification: 'left',
137
+ // 绘制引用文字(多行)
138
+ textLines.forEach((line, index) => {
139
+ const lineY = y + padding + fontSize + index * lineHeight
140
+ const quoteText = new paper.PointText({
141
+ point: [x + padding + 30, lineY],
142
+ content: line,
143
+ fontSize: fontSize,
144
+ fontFamily: quoteFont,
145
+ fillColor: new paper.Color(textColor),
146
+ justification: 'left',
147
+ })
148
+ elements.push({ type: 'text', id: quoteText.id })
79
149
  })
80
- elements.push({ type: 'text', id: quoteText.id })
81
150
 
82
151
  // 绘制作者
83
152
  if (author) {
153
+ const authorY = y + padding + textBlockHeight + fontSize * 1.3
154
+ const authorFont = getFontFallbackChain(fontFamily, author).join(', ')
84
155
  const authorText = new paper.PointText({
85
- point: [x + padding, y + padding + fontSize * 2.5 + 10],
156
+ point: [x + padding, authorY],
86
157
  content: `— ${author}`,
87
- fontSize: fontSize * 0.8,
158
+ fontSize: fontSize * 0.85,
159
+ fontFamily: authorFont,
88
160
  fillColor: new paper.Color(authorColor),
89
161
  justification: 'left',
90
162
  })
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  const paper = require('paper')
6
+ const { getFontFallbackChain, validateFont } = require('../fonts')
6
7
 
7
8
  /**
8
9
  * 创建丝带
@@ -27,6 +28,9 @@ function createRibbon(project, args) {
27
28
 
28
29
  const items = []
29
30
 
31
+ // 获取字体回退链
32
+ const ribbonFont = getFontFallbackChain(fontFamily, text).join(', ')
33
+
30
34
  if (style === 'diagonal') {
31
35
  // 对角丝带
32
36
  const diagonalLength = Math.sqrt(width ** 2 + 60 ** 2)
@@ -59,7 +63,7 @@ function createRibbon(project, args) {
59
63
  point: [x + diagonalLength / 2, y + 60 / 2 + fontSize / 3],
60
64
  content: text,
61
65
  fontSize,
62
- fontFamily: fontFamily || 'sans-serif',
66
+ fontFamily: ribbonFont,
63
67
  fillColor: new paper.Color(color),
64
68
  justification: 'center',
65
69
  })
@@ -112,17 +116,17 @@ function createRibbon(project, args) {
112
116
  items.push(fold)
113
117
 
114
118
  // 文字
115
- const textItem = new paper.PointText({
119
+ const cornerTextItem = new paper.PointText({
116
120
  point: [x + ribbonWidth / 2, y + ribbonHeight / 2 + fontSize / 3],
117
121
  content: text,
118
122
  fontSize,
119
- fontFamily: fontFamily || 'sans-serif',
123
+ fontFamily: ribbonFont,
120
124
  fillColor: new paper.Color(color),
121
125
  justification: 'center',
122
126
  })
123
127
 
124
- if (opacity !== 1) textItem.opacity = opacity
125
- items.push(textItem)
128
+ if (opacity !== 1) cornerTextItem.opacity = opacity
129
+ items.push(cornerTextItem)
126
130
 
127
131
  } else {
128
132
  // 折叠丝带 (默认)
@@ -170,17 +174,17 @@ function createRibbon(project, args) {
170
174
  items.push(rightFold)
171
175
 
172
176
  // 文字
173
- const textItem = new paper.PointText({
177
+ const foldTextItem = new paper.PointText({
174
178
  point: [x + width / 2, y + ribbonHeight / 2 + fontSize / 3],
175
179
  content: text,
176
180
  fontSize,
177
- fontFamily: fontFamily || 'sans-serif',
181
+ fontFamily: ribbonFont,
178
182
  fillColor: new paper.Color(color),
179
183
  justification: 'center',
180
184
  })
181
185
 
182
- if (opacity !== 1) textItem.opacity = opacity
183
- items.push(textItem)
186
+ if (opacity !== 1) foldTextItem.opacity = opacity
187
+ items.push(foldTextItem)
184
188
  }
185
189
 
186
190
  return {
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  const paper = require('paper')
6
+ const { getFontFallbackChain, validateFont } = require('../fonts')
6
7
 
7
8
  /**
8
9
  * 创建印章
@@ -70,11 +71,12 @@ function createSeal(project, args) {
70
71
  items.push(innerBorder)
71
72
 
72
73
  // 文字
74
+ const mainFont = getFontFallbackChain(fontFamily, text).join(', ')
73
75
  const textItem = new paper.PointText({
74
76
  point: [centerX, centerY + fontSize / 3],
75
77
  content: text,
76
78
  fontSize,
77
- fontFamily: fontFamily || 'serif',
79
+ fontFamily: mainFont,
78
80
  fillColor: new paper.Color(color),
79
81
  justification: 'center',
80
82
  })
@@ -87,7 +89,7 @@ function createSeal(project, args) {
87
89
  point: [centerX, centerY - size / 4],
88
90
  content: '★',
89
91
  fontSize: 16,
90
- fontFamily: fontFamily || 'serif',
92
+ fontFamily: mainFont,
91
93
  fillColor: new paper.Color(color),
92
94
  justification: 'center',
93
95
  })
@@ -99,7 +101,7 @@ function createSeal(project, args) {
99
101
  point: [centerX, centerY + size / 3],
100
102
  content: '★ ★ ★',
101
103
  fontSize: 12,
102
- fontFamily: fontFamily || 'serif',
104
+ fontFamily: mainFont,
103
105
  fillColor: new paper.Color(color),
104
106
  justification: 'center',
105
107
  letterSpacing: 8,
@@ -15,7 +15,7 @@ function createStar(project, canvas, params) {
15
15
  cx,
16
16
  cy,
17
17
  points = 5,
18
- innerRadius: providedInnerRadius,
18
+ innerRadius,
19
19
  outerRadius,
20
20
  fill,
21
21
  stroke,
@@ -25,14 +25,14 @@ function createStar(project, canvas, params) {
25
25
  } = params
26
26
 
27
27
  // 计算内半径
28
- const innerRadius = providedInnerRadius || outerRadius * 0.4
28
+ const actualInnerRadius = innerRadius || outerRadius * 0.4
29
29
 
30
30
  // 创建星形路径
31
31
  const path = new paper.Path()
32
32
  const angleStep = Math.PI / points
33
33
 
34
34
  for (let i = 0; i < points * 2; i++) {
35
- const radius = i % 2 === 0 ? outerRadius : innerRadius
35
+ const radius = i % 2 === 0 ? outerRadius : actualInnerRadius
36
36
  const angle = i * angleStep - Math.PI / 2 + (rotation * Math.PI / 180)
37
37
  const x = cx + radius * Math.cos(angle)
38
38
  const y = cy + radius * Math.sin(angle)
@@ -125,7 +125,7 @@ function createTable(project, canvas, params) {
125
125
  // 行分隔线
126
126
  const rowLine = new paper.Path.Line({
127
127
  from: [x, rowY],
128
- to: [x + width, rowY]
128
+ to: [x + tableWidth, rowY]
129
129
  })
130
130
  rowLine.strokeColor = new paper.Color(borderColor)
131
131
  rowLine.strokeWidth = 0.5
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  const paper = require('paper')
6
+ const { getFontFallbackChain, validateFont } = require('../fonts')
6
7
 
7
8
  /**
8
9
  * 创建水印组件
@@ -14,7 +15,7 @@ function createWatermark(project, canvas, params) {
14
15
  cy,
15
16
  color = 'rgba(0,0,0,0.1)',
16
17
  fontSize = 48,
17
- fontFamily = 'sans-serif',
18
+ fontFamily = 'Microsoft YaHei',
18
19
  opacity = 0.1,
19
20
  rotation = 0,
20
21
  align = 'center'
@@ -22,11 +23,12 @@ function createWatermark(project, canvas, params) {
22
23
 
23
24
  const elements = []
24
25
 
26
+ const finalFont = getFontFallbackChain(fontFamily, text).join(', ')
25
27
  const label = new paper.PointText({
26
28
  point: [cx, cy],
27
29
  content: text,
28
30
  fontSize: fontSize,
29
- fontFamily: fontFamily,
31
+ fontFamily: finalFont,
30
32
  fillColor: new paper.Color(color),
31
33
  justification: align,
32
34
  opacity: opacity