foliko 1.1.0 → 1.1.2

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 (214) 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 +173 -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_updated.json +12 -0
  46. package/.agent/plugins/poster-plugin/fonts/NotoColorEmoji-Regular.ttf +0 -0
  47. package/.agent/plugins/poster-plugin/package.json +2 -1
  48. package/.agent/plugins/poster-plugin/src/canvas.js +70 -7
  49. package/.agent/plugins/poster-plugin/src/components/barcode.js +120 -0
  50. package/.agent/plugins/poster-plugin/src/components/bubble.js +153 -0
  51. package/.agent/plugins/poster-plugin/src/components/button.js +124 -0
  52. package/.agent/plugins/poster-plugin/src/components/cta.js +26 -24
  53. package/.agent/plugins/poster-plugin/src/components/featureGrid.js +22 -17
  54. package/.agent/plugins/poster-plugin/src/components/frame.js +230 -0
  55. package/.agent/plugins/poster-plugin/src/components/highlightText.js +144 -0
  56. package/.agent/plugins/poster-plugin/src/components/icon.js +94 -0
  57. package/.agent/plugins/poster-plugin/src/components/index.js +19 -0
  58. package/.agent/plugins/poster-plugin/src/components/listItem.js +6 -5
  59. package/.agent/plugins/poster-plugin/src/components/qrcode.js +74 -0
  60. package/.agent/plugins/poster-plugin/src/components/ribbon.js +193 -0
  61. package/.agent/plugins/poster-plugin/src/components/seal.js +146 -0
  62. package/.agent/plugins/poster-plugin/src/components/table.js +17 -9
  63. package/.agent/plugins/poster-plugin/src/components/tagCloud.js +24 -17
  64. package/.agent/plugins/poster-plugin/src/components/timeline.js +24 -12
  65. package/.agent/plugins/poster-plugin/src/composer.js +392 -150
  66. package/.agent/plugins/poster-plugin/src/elements/background.js +36 -4
  67. package/.agent/plugins/poster-plugin/src/elements/image.js +4 -47
  68. package/.agent/plugins/poster-plugin/src/elements/index.js +2 -0
  69. package/.agent/plugins/poster-plugin/src/elements/polygon.js +37 -6
  70. package/.agent/plugins/poster-plugin/src/elements/richText.js +230 -0
  71. package/.agent/plugins/poster-plugin/src/elements/svg.js +35 -19
  72. package/.agent/plugins/poster-plugin/src/elements/text.js +71 -2
  73. package/.agent/plugins/poster-plugin/src/fonts.js +123 -8
  74. package/.agent/plugins/poster-plugin/src/index.js +445 -23
  75. package/.agent/plugins/poster-plugin/src/utils/imageLoader.js +84 -0
  76. package/.agent/plugins/poster-plugin/test-background.svg +1 -0
  77. package/.agent/plugins/poster-plugin/test-full-poster.svg +2 -0
  78. package/.agent/plugins/poster-plugin/test-image.png +0 -0
  79. package/.agent/plugins/puppeteer-plugin/README.md +147 -0
  80. package/.agent/plugins/puppeteer-plugin/index.js +1418 -0
  81. package/.agent/plugins/puppeteer-plugin/package.json +9 -0
  82. package/.agent/plugins.json +5 -11
  83. package/.agent/rules/GEMINI.md +273 -0
  84. package/.agent/rules/allow-rule.md +77 -0
  85. package/.agent/rules/log-rule.md +83 -0
  86. package/.agent/rules/security-rule.md +93 -0
  87. package/.agent/scripts/auto_preview.py +148 -0
  88. package/.agent/scripts/checklist.py +217 -0
  89. package/.agent/scripts/session_manager.py +120 -0
  90. package/.agent/scripts/verify_all.py +327 -0
  91. package/.agent/sessions/cli_default.json +678 -23580
  92. package/.agent/sessions/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.json +11097 -0
  93. package/.agent/skills/api-patterns/SKILL.md +81 -0
  94. package/.agent/skills/api-patterns/api-style.md +42 -0
  95. package/.agent/skills/api-patterns/auth.md +24 -0
  96. package/.agent/skills/api-patterns/documentation.md +26 -0
  97. package/.agent/skills/api-patterns/graphql.md +41 -0
  98. package/.agent/skills/api-patterns/rate-limiting.md +31 -0
  99. package/.agent/skills/api-patterns/response.md +37 -0
  100. package/.agent/skills/api-patterns/rest.md +40 -0
  101. package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
  102. package/.agent/skills/api-patterns/security-testing.md +122 -0
  103. package/.agent/skills/api-patterns/trpc.md +41 -0
  104. package/.agent/skills/api-patterns/versioning.md +22 -0
  105. package/.agent/skills/app-builder/SKILL.md +75 -0
  106. package/.agent/skills/app-builder/agent-coordination.md +71 -0
  107. package/.agent/skills/app-builder/feature-building.md +53 -0
  108. package/.agent/skills/app-builder/project-detection.md +34 -0
  109. package/.agent/skills/app-builder/scaffolding.md +118 -0
  110. package/.agent/skills/app-builder/tech-stack.md +40 -0
  111. package/.agent/skills/app-builder/templates/SKILL.md +39 -0
  112. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
  113. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
  114. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
  115. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
  116. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
  117. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
  118. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
  119. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
  120. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
  121. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
  122. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
  123. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
  124. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
  125. package/.agent/skills/architecture/SKILL.md +55 -0
  126. package/.agent/skills/architecture/context-discovery.md +43 -0
  127. package/.agent/skills/architecture/examples.md +94 -0
  128. package/.agent/skills/architecture/pattern-selection.md +68 -0
  129. package/.agent/skills/architecture/patterns-reference.md +50 -0
  130. package/.agent/skills/architecture/trade-off-analysis.md +77 -0
  131. package/.agent/skills/clean-code/SKILL.md +201 -0
  132. package/.agent/skills/doc.md +177 -0
  133. package/.agent/skills/frontend-design/SKILL.md +418 -0
  134. package/.agent/skills/frontend-design/animation-guide.md +331 -0
  135. package/.agent/skills/frontend-design/color-system.md +311 -0
  136. package/.agent/skills/frontend-design/decision-trees.md +418 -0
  137. package/.agent/skills/frontend-design/motion-graphics.md +306 -0
  138. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
  139. package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
  140. package/.agent/skills/frontend-design/typography-system.md +345 -0
  141. package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
  142. package/.agent/skills/frontend-design/visual-effects.md +383 -0
  143. package/.agent/skills/i18n-localization/SKILL.md +154 -0
  144. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
  145. package/.agent/skills/mcp-builder/SKILL.md +176 -0
  146. package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
  147. package/.agent/workflows/brainstorm.md +113 -0
  148. package/.agent/workflows/create.md +59 -0
  149. package/.agent/workflows/debug.md +103 -0
  150. package/.agent/workflows/deploy.md +176 -0
  151. package/.agent/workflows/enhance.md +63 -0
  152. package/.agent/workflows/orchestrate.md +237 -0
  153. package/.agent/workflows/plan.md +89 -0
  154. package/.agent/workflows/preview.md +81 -0
  155. package/.agent/workflows/simple-test.md +42 -0
  156. package/.agent/workflows/status.md +86 -0
  157. package/.agent/workflows/structured-orchestrate.md +180 -0
  158. package/.agent/workflows/test.md +144 -0
  159. package/.agent/workflows/ui-ux-pro-max.md +296 -0
  160. package/.claude/settings.local.json +21 -1
  161. package/.env.example +56 -56
  162. package/README.md +441 -441
  163. package/cli/src/commands/chat.js +2 -1
  164. package/output/international-news-daily.png +0 -0
  165. package/package.json +2 -1
  166. package/plugins/extension-executor-plugin.js +91 -12
  167. package/plugins/file-system-plugin.js +4 -19
  168. package/plugins/subagent-plugin.js +37 -14
  169. package/plugins/weixin-plugin.js +168 -40
  170. package/poster-test-2.png +0 -0
  171. package/skills/find-skills/AGENTS.md +162 -162
  172. package/skills/find-skills/SKILL.md +133 -133
  173. package/skills/poster-guide/SKILL.md +1435 -627
  174. package/src/core/agent-chat.js +223 -11
  175. package/src/core/agent.js +6 -3
  176. package/.agent/agents/code-assistant.json +0 -14
  177. package/.agent/agents/email-assistant.json +0 -14
  178. package/.agent/agents/file-assistant.json +0 -15
  179. package/.agent/agents/system-assistant.json +0 -15
  180. package/.agent/agents/web-assistant.json +0 -12
  181. package/.agent/data/ambient/goals.json +0 -50
  182. package/.agent/data/ambient/memories.json +0 -7
  183. package/.agent/data/scheduler/tasks.json +0 -1
  184. package/.agent/memory/core.md +0 -1
  185. package/.agent/memory/project/mnn93ogy-ypjn27.md +0 -9
  186. package/.agent/memory/project/mnn98fqy-5nhc1u.md +0 -25
  187. package/.agent/memory/user/mnm67t9m-x8rekk.md +0 -9
  188. package/.agent/memory/user/mnn5mmqh-w6aktx.md +0 -11
  189. package/.agent/memory/user/mnnbfhhn-dk1bd1.md +0 -22
  190. package/.agent/package.json +0 -8
  191. package/.agent/plugins/__pycache__/file_writer.cpython-312.pyc +0 -0
  192. package/.agent/plugins/daytona/README.md +0 -89
  193. package/.agent/plugins/daytona/index.js +0 -377
  194. package/.agent/plugins/daytona/package.json +0 -12
  195. package/.agent/plugins/marknative/README.md +0 -134
  196. package/.agent/plugins/marknative/index.js +0 -228
  197. package/.agent/plugins/marknative/package.json +0 -12
  198. package/.agent/plugins/marknative/update-readme.js +0 -134
  199. package/.agent/plugins/system-info/index.js +0 -387
  200. package/.agent/plugins/system-info/package.json +0 -4
  201. package/.agent/plugins/system-info/test.js +0 -40
  202. package/.agent/python-scripts/test_sample.py +0 -24
  203. package/.agent/skills/agent-browser/SKILL.md +0 -311
  204. package/.agent/skills/agent-browser/TEST_PLAN.md +0 -200
  205. package/.agent/skills/sysinfo/SKILL.md +0 -38
  206. package/.agent/skills/sysinfo/system-info.sh +0 -130
  207. package/.agent/skills/workflow/SKILL.md +0 -324
  208. package/.agent/weixin.json +0 -6
  209. package/.agent/workflows/email-digest.json +0 -50
  210. package/.agent/workflows/file-backup.json +0 -21
  211. package/.agent/workflows/get-ip-notify.json +0 -32
  212. package/.agent/workflows/news-aggregator.json +0 -93
  213. package/.agent/workflows/news-dashboard-v2.json +0 -94
  214. package/.agent/workflows/notification-batch.json +0 -32
@@ -0,0 +1,120 @@
1
+ /**
2
+ * 条形码组件 - 静态图片
3
+ */
4
+
5
+ const paper = require('paper')
6
+ const { loadImageAsRaster, downloadImage } = require('../utils/imageLoader')
7
+
8
+ /**
9
+ * 创建条形码
10
+ */
11
+ async function createBarcode(project, args) {
12
+ const {
13
+ x = 0,
14
+ y = 0,
15
+ width = 300,
16
+ height = 100,
17
+ content = '1234567890',
18
+ showText = true,
19
+ textColor = '#000000',
20
+ fontSize = 16,
21
+ opacity = 1,
22
+ } = args
23
+
24
+ // 内容太短或像URL,回退到简单条形码
25
+ if (content.length < 8 || content.startsWith('http')) {
26
+ return createSimpleBarcode(project, { x, y, width, height, content, showText, textColor, fontSize, opacity })
27
+ }
28
+
29
+ try {
30
+ const barcodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${width}x${height}&data=${encodeURIComponent(content)}&format=png`
31
+ const { raster } = await loadImageAsRaster(project, barcodeUrl, { x, y, width, height }, opacity)
32
+ const items = [raster]
33
+
34
+ // 文字
35
+ if (showText) {
36
+ const textItem = new paper.PointText({
37
+ point: [x + width / 2, y + height + fontSize + 5],
38
+ content: content,
39
+ fontSize,
40
+ fontFamily: 'monospace',
41
+ fillColor: new paper.Color(textColor),
42
+ justification: 'center',
43
+ })
44
+ if (opacity !== 1) textItem.opacity = opacity
45
+ items.push(textItem)
46
+ }
47
+
48
+ return {
49
+ success: true,
50
+ type: 'barcode',
51
+ items: items.map(i => i.id),
52
+ }
53
+ } catch (err) {
54
+ // 回退到简单条形码
55
+ return createSimpleBarcode(project, { x, y, width, height, content, showText, textColor, fontSize, opacity })
56
+ }
57
+ }
58
+
59
+ /**
60
+ * 创建简单的条形码(使用线条绘制)
61
+ */
62
+ function createSimpleBarcode(project, args) {
63
+ const {
64
+ x = 0,
65
+ y = 0,
66
+ width = 300,
67
+ height = 80,
68
+ content = '123456',
69
+ color = '#000000',
70
+ showText = true,
71
+ textColor = '#000000',
72
+ fontSize = 14,
73
+ opacity = 1,
74
+ } = args
75
+
76
+ const items = []
77
+ const barHeight = showText ? height - 25 : height
78
+ const barWidth = width / (content.length * 15)
79
+
80
+ // 生成简单的条形图案
81
+ for (let i = 0; i < content.length; i++) {
82
+ const charCode = content.charCodeAt(i)
83
+ const density = (charCode % 3) + 1
84
+
85
+ for (let d = 0; d < density; d++) {
86
+ const isWide = (charCode + d) % 2 === 0
87
+ const barW = isWide ? barWidth * 2 : barWidth
88
+
89
+ const bar = new paper.Path.Rectangle({
90
+ point: [x + (i * density + d) * barWidth, y],
91
+ size: [barW * 0.8, barHeight],
92
+ fillColor: new paper.Color(color),
93
+ })
94
+ if (opacity !== 1) bar.opacity = opacity
95
+ items.push(bar)
96
+ }
97
+ }
98
+
99
+ // 文字
100
+ if (showText) {
101
+ const textItem = new paper.PointText({
102
+ point: [x + width / 2, y + barHeight + fontSize + 3],
103
+ content: content,
104
+ fontSize,
105
+ fontFamily: 'monospace',
106
+ fillColor: new paper.Color(textColor),
107
+ justification: 'center',
108
+ })
109
+ if (opacity !== 1) textItem.opacity = opacity
110
+ items.push(textItem)
111
+ }
112
+
113
+ return {
114
+ success: true,
115
+ type: 'barcode',
116
+ items: items.map(i => i.id),
117
+ }
118
+ }
119
+
120
+ module.exports = createBarcode
@@ -0,0 +1,153 @@
1
+ /**
2
+ * 对话气泡组件
3
+ */
4
+
5
+ const paper = require('paper')
6
+
7
+ /**
8
+ * 创建对话气泡
9
+ */
10
+ function createBubble(project, args) {
11
+ const {
12
+ x = 0,
13
+ y = 0,
14
+ width = 300,
15
+ height = 100,
16
+ text = '',
17
+ fontSize = 24,
18
+ fontFamily,
19
+ color = '#000000',
20
+ backgroundColor = '#ffffff',
21
+ borderColor,
22
+ borderWidth = 1,
23
+ radius = 20,
24
+ tailDirection = 'bottom', // bottom, top, left, right
25
+ tailPosition = 'left', // left, center, right
26
+ shadow,
27
+ opacity = 1,
28
+ } = args
29
+
30
+ const items = []
31
+
32
+ // 气泡主体
33
+ const bubbleX = tailDirection === 'left' ? x + 15 : x
34
+ const bubbleY = tailDirection === 'top' ? y + 15 : y
35
+ const bubbleWidth = tailDirection === 'left' ? width - 15 : width
36
+ const bubbleHeight = tailDirection === 'top' ? height - 15 : height
37
+
38
+ const bgOpts = {
39
+ point: [bubbleX, bubbleY],
40
+ size: [bubbleWidth, bubbleHeight],
41
+ radius: radius,
42
+ }
43
+
44
+ if (borderColor) {
45
+ bgOpts.strokeColor = new paper.Color(borderColor)
46
+ bgOpts.strokeWidth = borderWidth
47
+ }
48
+
49
+ const bg = new paper.Path.Rectangle(bgOpts)
50
+ bg.fillColor = new paper.Color(backgroundColor)
51
+
52
+ if (shadow) {
53
+ bg.shadowColor = new paper.Color(shadow.color || '#000000')
54
+ bg.shadowBlur = shadow.blur || 10
55
+ bg.shadowOffset = new paper.Point(shadow.offsetX || 3, shadow.offsetY || 3)
56
+ }
57
+
58
+ if (opacity !== 1) bg.opacity = opacity
59
+ items.push(bg)
60
+
61
+ // 气泡尾巴
62
+ const tailSize = 20
63
+ let tailX, tailY, tailRotation
64
+
65
+ // 计算尾巴位置
66
+ switch (tailDirection) {
67
+ case 'bottom':
68
+ tailY = bubbleY + bubbleHeight - 5
69
+ if (tailPosition === 'left') tailX = bubbleX + 30
70
+ else if (tailPosition === 'right') tailX = bubbleX + bubbleWidth - 30
71
+ else tailX = bubbleX + bubbleWidth / 2
72
+ tailRotation = 0
73
+ break
74
+ case 'top':
75
+ tailY = bubbleY + 5
76
+ if (tailPosition === 'left') tailX = bubbleX + 30
77
+ else if (tailPosition === 'right') tailX = bubbleX + bubbleWidth - 30
78
+ else tailX = bubbleX + bubbleWidth / 2
79
+ tailRotation = 180
80
+ break
81
+ case 'left':
82
+ tailX = bubbleX + 5
83
+ if (tailPosition === 'left') tailY = bubbleY + 30
84
+ else if (tailPosition === 'right') tailY = bubbleY + bubbleHeight - 30
85
+ else tailY = bubbleY + bubbleHeight / 2
86
+ tailRotation = 90
87
+ break
88
+ case 'right':
89
+ tailX = bubbleX + bubbleWidth - 5
90
+ if (tailPosition === 'left') tailY = bubbleY + 30
91
+ else if (tailPosition === 'right') tailY = bubbleY + bubbleHeight - 30
92
+ else tailY = bubbleY + bubbleHeight / 2
93
+ tailRotation = 270
94
+ break
95
+ }
96
+
97
+ const tail = new paper.Path()
98
+ tail.add(new paper.Point(tailX - tailSize, tailY))
99
+ tail.add(new paper.Point(tailX, tailY + tailSize * 0.7))
100
+ tail.add(new paper.Point(tailX, tailY - tailSize * 0.7))
101
+ tail.closed = true
102
+ tail.fillColor = new paper.Color(backgroundColor)
103
+ if (borderColor) {
104
+ tail.strokeColor = new paper.Color(borderColor)
105
+ tail.strokeWidth = borderWidth
106
+ }
107
+ tail.rotate(tailRotation, new paper.Point(tailX, tailY))
108
+
109
+ if (opacity !== 1) tail.opacity = opacity
110
+ items.push(tail)
111
+
112
+ // 文字
113
+ const padding = 25
114
+ const textItem = new paper.PointText({
115
+ point: [bubbleX + padding, bubbleY + bubbleHeight / 2 + fontSize / 3],
116
+ content: text,
117
+ fontSize,
118
+ fontFamily: fontFamily || 'sans-serif',
119
+ fillColor: new paper.Color(color),
120
+ justification: tailPosition === 'left' ? 'left' : tailPosition === 'right' ? 'right' : 'center',
121
+ })
122
+
123
+ // 限制文字宽度
124
+ const maxTextWidth = bubbleWidth - padding * 2
125
+ if (textItem.bounds.width > maxTextWidth) {
126
+ textItem.justification = 'left'
127
+ const charsPerLine = Math.floor((text.length * maxTextWidth) / textItem.bounds.width)
128
+ // 简单换行处理
129
+ let lines = []
130
+ let currentLine = ''
131
+ for (const char of text) {
132
+ currentLine += char
133
+ const testText = new paper.PointText({ content: currentLine, fontSize, fontFamily: fontFamily || 'sans-serif' })
134
+ if (testText.bounds.width > maxTextWidth) {
135
+ lines.push(currentLine.slice(0, -1))
136
+ currentLine = char
137
+ }
138
+ }
139
+ if (currentLine) lines.push(currentLine)
140
+ textItem.content = lines.join('\n')
141
+ }
142
+
143
+ if (opacity !== 1) textItem.opacity = opacity
144
+ items.push(textItem)
145
+
146
+ return {
147
+ success: true,
148
+ type: 'bubble',
149
+ items: items.map(i => i.id),
150
+ }
151
+ }
152
+
153
+ module.exports = createBubble
@@ -0,0 +1,124 @@
1
+ /**
2
+ * 按钮组件
3
+ */
4
+
5
+ const paper = require('paper')
6
+ const { loadImageAsRaster } = require('../utils/imageLoader')
7
+
8
+ /**
9
+ * 创建按钮
10
+ */
11
+ async function createButton(project, args) {
12
+ const {
13
+ x = 0,
14
+ y = 0,
15
+ width = 200,
16
+ height = 60,
17
+ text = '按钮',
18
+ fontSize = 24,
19
+ fontFamily,
20
+ color = '#ffffff',
21
+ backgroundColor = '#3b82f6',
22
+ borderColor,
23
+ borderWidth = 0,
24
+ radius = 8,
25
+ shadow,
26
+ gradient,
27
+ icon,
28
+ iconPosition = 'left',
29
+ opacity = 1,
30
+ } = args
31
+
32
+ // 创建按钮背景
33
+ const bgOptions = {
34
+ point: [x, y],
35
+ size: [width, height],
36
+ radius: radius,
37
+ }
38
+
39
+ if (borderColor) {
40
+ bgOptions.strokeColor = new paper.Color(borderColor)
41
+ bgOptions.strokeWidth = borderWidth
42
+ }
43
+
44
+ const bg = new paper.Path.Rectangle(bgOptions)
45
+
46
+ // 渐变或纯色填充
47
+ if (gradient && gradient.colors && gradient.colors.length > 0) {
48
+ const colors = gradient.colors.map(c => new paper.Color(c))
49
+ bg.fillColor = new paper.Color({
50
+ gradient: { stops: colors },
51
+ origin: bg.bounds.topLeft,
52
+ destination: bg.bounds.bottomLeft,
53
+ })
54
+ } else {
55
+ bg.fillColor = new paper.Color(backgroundColor)
56
+ }
57
+
58
+ // 阴影
59
+ if (shadow) {
60
+ bg.shadowColor = new paper.Color(shadow.color || '#000000')
61
+ bg.shadowBlur = shadow.blur || 10
62
+ bg.shadowOffset = new paper.Point(shadow.offsetX || 0, shadow.offsetY || 4)
63
+ }
64
+
65
+ if (opacity !== 1) {
66
+ bg.opacity = opacity
67
+ }
68
+
69
+ const items = [bg]
70
+ let textX = x + width / 2
71
+
72
+ // 添加图标
73
+ if (icon) {
74
+ const iconSize = Math.min(height * 0.5, 40)
75
+ const iconX = iconPosition === 'left' ? x + 20 : x + width - 40 - iconSize
76
+
77
+ if (icon.startsWith('http') || icon.startsWith('data:')) {
78
+ // URL图片图标
79
+ const { raster } = await loadImageAsRaster(project, icon, {
80
+ x: iconX,
81
+ y: y + (height - iconSize) / 2,
82
+ width: iconSize,
83
+ height: iconSize
84
+ }, opacity)
85
+ items.push(raster)
86
+ } else {
87
+ // Emoji 或文本图标
88
+ const iconText = new paper.PointText({
89
+ point: [iconX + iconSize / 2, y + height / 2 + fontSize / 3],
90
+ content: icon,
91
+ fontSize: iconSize,
92
+ justification: 'center',
93
+ })
94
+ if (opacity !== 1) iconText.opacity = opacity
95
+ items.push(iconText)
96
+ }
97
+
98
+ textX = iconPosition === 'left' ? x + width / 2 + 25 : x + width / 2 - 25
99
+ }
100
+
101
+ // 添加文字
102
+ const textItem = new paper.PointText({
103
+ point: [textX, y + height / 2 + fontSize / 3],
104
+ content: text,
105
+ fontSize,
106
+ fontFamily: fontFamily || 'sans-serif',
107
+ fillColor: new paper.Color(color),
108
+ justification: 'center',
109
+ })
110
+
111
+ if (opacity !== 1) {
112
+ textItem.opacity = opacity
113
+ }
114
+
115
+ items.push(textItem)
116
+
117
+ return {
118
+ success: true,
119
+ type: 'button',
120
+ items: items.map(i => i.id),
121
+ }
122
+ }
123
+
124
+ module.exports = createButton
@@ -6,25 +6,11 @@ const paper = require('paper')
6
6
 
7
7
  /**
8
8
  * 创建 CTA 按钮
9
- *
10
- * @param {Object} project - Paper.js 项目
11
- * @param {Object} canvas - 画布对象
12
- * @param {Object} args - 组件参数
13
- * @param {number} args.x - X坐标(居中)
14
- * @param {number} args.y - Y坐标
15
- * @param {string} args.text - 按钮文字
16
- * @param {string} args.background - 背景色
17
- * @param {string} args.color - 文字颜色
18
- * @param {string} args.border - 边框颜色
19
- * @param {number} args.fontSize - 字体大小
20
- * @param {number} args.padding - 内边距
21
- * @param {number} args.radius - 圆角半径
22
- * @param {Object} args.shadow - 阴影设置
23
9
  */
24
10
  function createCTA(project, canvas, args) {
25
11
  const {
26
- x, y,
27
- text,
12
+ x = 0, y = 0,
13
+ text = '',
28
14
  background = '#007bff',
29
15
  color = '#ffffff',
30
16
  border,
@@ -32,13 +18,18 @@ function createCTA(project, canvas, args) {
32
18
  padding = 25,
33
19
  radius = 8,
34
20
  shadow,
21
+ width: customWidth,
35
22
  } = args
36
23
 
37
24
  const elements = []
38
25
 
39
- // 计算按钮尺寸
40
- const textWidth = text.length * fontSize * 0.7
41
- const btnWidth = textWidth + padding * 2
26
+ // 确保 text 是字符串
27
+ const textStr = String(text || '')
28
+ // 使用更准确的字符宽度估算:中文约1.0,英文约0.5
29
+ const chineseChars = (textStr.match(/[\u4e00-\u9fa5]/g) || []).length
30
+ const otherChars = textStr.length - chineseChars
31
+ const textWidth = chineseChars * fontSize * 1.0 + otherChars * fontSize * 0.5
32
+ const btnWidth = customWidth || (textWidth + padding * 2)
42
33
  const btnHeight = fontSize + padding * 2
43
34
 
44
35
  const btnX = x - btnWidth / 2
@@ -56,22 +47,32 @@ function createCTA(project, canvas, args) {
56
47
  button.strokeWidth = 1
57
48
  }
58
49
 
59
- if (shadow) {
60
- button.shadowColor = new paper.Color(shadow.color || 'rgba(0,0,0,0.3)')
61
- button.shadowBlur = shadow.blur || 10
62
- button.shadowOffset = new paper.Point(shadow.offsetX || 0, shadow.offsetY || 4)
50
+ if (shadow && typeof shadow === 'object') {
51
+ try {
52
+ if (shadow.color) button.shadowColor = new paper.Color(shadow.color)
53
+ button.shadowBlur = shadow.blur || 10
54
+ button.shadowOffset = new paper.Point(shadow.offsetX || 0, shadow.offsetY || 4)
55
+ } catch (e) {
56
+ // 忽略阴影错误
57
+ }
63
58
  }
64
59
 
60
+ if (project && project.activeLayer) {
61
+ project.activeLayer.addChild(button)
62
+ }
65
63
  elements.push({ type: 'rectangle', id: button.id })
66
64
 
67
65
  // 绘制文字
68
66
  const buttonText = new paper.PointText({
69
67
  point: [x, y + btnHeight / 2 + fontSize / 3],
70
- content: text,
68
+ content: textStr,
71
69
  fontSize: fontSize,
72
70
  fillColor: new paper.Color(color),
73
71
  justification: 'center',
74
72
  })
73
+ if (project && project.activeLayer) {
74
+ project.activeLayer.addChild(buttonText)
75
+ }
75
76
  elements.push({ type: 'text', id: buttonText.id })
76
77
 
77
78
  return {
@@ -79,6 +80,7 @@ function createCTA(project, canvas, args) {
79
80
  elements,
80
81
  width: btnWidth,
81
82
  height: btnHeight,
83
+ type: 'cta',
82
84
  }
83
85
  }
84
86
 
@@ -6,20 +6,6 @@ const paper = require('paper')
6
6
 
7
7
  /**
8
8
  * 创建特性网格
9
- *
10
- * @param {Object} project - Paper.js 项目
11
- * @param {Object} canvas - 画布对象
12
- * @param {Object} args - 组件参数
13
- * @param {number} args.x - X坐标
14
- * @param {number} args.y - Y坐标
15
- * @param {number} args.columns - 列数
16
- * @param {number} args.itemWidth - 每个特性宽度
17
- * @param {number} args.itemHeight - 每个特性高度
18
- * @param {number} args.gap - 间距
19
- * @param {Array} args.items - 特性数组 [{icon, title, description}]
20
- * @param {string} args.background - 背景色
21
- * @param {string} args.borderColor - 边框颜色
22
- * @param {number} args.radius - 圆角半径
23
9
  */
24
10
  function createFeatureGrid(project, canvas, args) {
25
11
  const {
@@ -35,7 +21,13 @@ function createFeatureGrid(project, canvas, args) {
35
21
  } = args
36
22
 
37
23
  const elements = []
38
- const rows = Math.ceil(items.length / columns)
24
+
25
+ // 确保 items 是数组
26
+ if (!Array.isArray(items)) {
27
+ items = []
28
+ }
29
+
30
+ const rows = items.length > 0 ? Math.ceil(items.length / columns) : 0
39
31
 
40
32
  for (let i = 0; i < items.length; i++) {
41
33
  const item = items[i]
@@ -55,6 +47,9 @@ function createFeatureGrid(project, canvas, args) {
55
47
  bg.strokeColor = new paper.Color(borderColor)
56
48
  bg.strokeWidth = 0.5
57
49
  bg.opacity = 0.8
50
+ if (project && project.activeLayer) {
51
+ project.activeLayer.addChild(bg)
52
+ }
58
53
  elements.push({ type: 'rectangle', id: bg.id })
59
54
 
60
55
  const padding = 15
@@ -69,6 +64,9 @@ function createFeatureGrid(project, canvas, args) {
69
64
  fillColor: new paper.Color(item.iconColor || '#00ff88'),
70
65
  justification: 'left',
71
66
  })
67
+ if (project && project.activeLayer) {
68
+ project.activeLayer.addChild(iconText)
69
+ }
72
70
  elements.push({ type: 'text', id: iconText.id })
73
71
  itemYOffset += 35
74
72
  }
@@ -82,6 +80,9 @@ function createFeatureGrid(project, canvas, args) {
82
80
  fillColor: new paper.Color(item.titleColor || '#ffffff'),
83
81
  justification: 'left',
84
82
  })
83
+ if (project && project.activeLayer) {
84
+ project.activeLayer.addChild(titleText)
85
+ }
85
86
  elements.push({ type: 'text', id: titleText.id })
86
87
  itemYOffset += 22
87
88
  }
@@ -95,6 +96,9 @@ function createFeatureGrid(project, canvas, args) {
95
96
  fillColor: new paper.Color(item.descColor || '#888888'),
96
97
  justification: 'left',
97
98
  })
99
+ if (project && project.activeLayer) {
100
+ project.activeLayer.addChild(descText)
101
+ }
98
102
  elements.push({ type: 'text', id: descText.id })
99
103
  }
100
104
  }
@@ -102,10 +106,11 @@ function createFeatureGrid(project, canvas, args) {
102
106
  return {
103
107
  success: true,
104
108
  elements,
105
- width: columns * itemWidth + (columns - 1) * gap,
106
- height: rows * itemHeight + (rows - 1) * gap,
109
+ width: columns * itemWidth + Math.max(0, columns - 1) * gap,
110
+ height: rows * itemHeight + Math.max(0, rows - 1) * gap,
107
111
  rows,
108
112
  cols: columns,
113
+ type: 'featureGrid',
109
114
  }
110
115
  }
111
116