foliko 1.1.1 → 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 (57) hide show
  1. package/.agent/data/weixin-media/2026-04-08/img_1775618677512.jpg +0 -0
  2. package/.agent/data/weixin-media/2026-04-08/img_1775619073340.jpg +0 -0
  3. package/.agent/data/weixin-media/2026-04-08/img_1775619097536.jpg +0 -0
  4. package/.agent/data/weixin-media/2026-04-08/img_1775619209388.jpg +0 -0
  5. package/.agent/plugins/poster-plugin/package.json +2 -1
  6. package/.agent/plugins/poster-plugin/src/canvas.js +70 -7
  7. package/.agent/plugins/poster-plugin/src/components/barcode.js +120 -0
  8. package/.agent/plugins/poster-plugin/src/components/bubble.js +153 -0
  9. package/.agent/plugins/poster-plugin/src/components/button.js +124 -0
  10. package/.agent/plugins/poster-plugin/src/components/cta.js +26 -24
  11. package/.agent/plugins/poster-plugin/src/components/featureGrid.js +22 -17
  12. package/.agent/plugins/poster-plugin/src/components/frame.js +230 -0
  13. package/.agent/plugins/poster-plugin/src/components/highlightText.js +144 -0
  14. package/.agent/plugins/poster-plugin/src/components/icon.js +94 -0
  15. package/.agent/plugins/poster-plugin/src/components/index.js +19 -0
  16. package/.agent/plugins/poster-plugin/src/components/listItem.js +6 -5
  17. package/.agent/plugins/poster-plugin/src/components/qrcode.js +74 -0
  18. package/.agent/plugins/poster-plugin/src/components/ribbon.js +193 -0
  19. package/.agent/plugins/poster-plugin/src/components/seal.js +146 -0
  20. package/.agent/plugins/poster-plugin/src/components/table.js +17 -9
  21. package/.agent/plugins/poster-plugin/src/components/tagCloud.js +24 -17
  22. package/.agent/plugins/poster-plugin/src/components/timeline.js +24 -12
  23. package/.agent/plugins/poster-plugin/src/composer.js +392 -150
  24. package/.agent/plugins/poster-plugin/src/elements/background.js +36 -4
  25. package/.agent/plugins/poster-plugin/src/elements/image.js +4 -47
  26. package/.agent/plugins/poster-plugin/src/elements/index.js +2 -0
  27. package/.agent/plugins/poster-plugin/src/elements/richText.js +230 -0
  28. package/.agent/plugins/poster-plugin/src/elements/svg.js +35 -19
  29. package/.agent/plugins/poster-plugin/src/index.js +430 -7
  30. package/.agent/plugins/poster-plugin/src/utils/imageLoader.js +84 -0
  31. package/.agent/plugins/poster-plugin/test-background.svg +1 -0
  32. package/.agent/plugins/poster-plugin/test-full-poster.svg +2 -0
  33. package/.agent/plugins/poster-plugin/test-image.png +0 -0
  34. package/.agent/sessions/cli_default.json +1089 -145
  35. package/.agent/sessions/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.json +8902 -0
  36. package/.claude/settings.local.json +6 -1
  37. package/output/beef-love-poster.png +0 -0
  38. package/output/international-news-daily.png +0 -0
  39. package/package.json +2 -1
  40. package/plugins/extension-executor-plugin.js +33 -32
  41. package/plugins/file-system-plugin.js +4 -19
  42. package/plugins/subagent-plugin.js +37 -14
  43. package/plugins/weixin-plugin.js +167 -47
  44. package/poster-test-2.png +0 -0
  45. package/skills/poster-guide/SKILL.md +497 -5
  46. package/src/core/agent-chat.js +141 -8
  47. package/src/core/agent.js +6 -3
  48. package/calc_tokens_weixin.js +0 -81
  49. package/foliko-creative-3.png +0 -0
  50. package/foliko-creative-4.png +0 -0
  51. package/foliko-creative-5.png +0 -0
  52. package/story-cover-book-v2.png +0 -0
  53. package/story-cover-japanese-1.png +0 -0
  54. package/story-cover-japanese-2.png +0 -0
  55. package/story-cover-japanese-3.png +0 -0
  56. package/story-cover-moran.png +0 -0
  57. package/undefined.png +0 -0
@@ -21,6 +21,7 @@ const {
21
21
  addImage,
22
22
  addText,
23
23
  addArtText,
24
+ addRichText,
24
25
  addBackground,
25
26
  addSVG,
26
27
  } = require('./elements')
@@ -51,6 +52,15 @@ const {
51
52
  createChart,
52
53
  createWatermark,
53
54
  createTable,
55
+ createButton,
56
+ createIcon,
57
+ createQRCode,
58
+ createFrame,
59
+ createBubble,
60
+ createRibbon,
61
+ createSeal,
62
+ createHighlightText,
63
+ createBarcode,
54
64
  } = require('./components')
55
65
 
56
66
  module.exports = function (Plugin) {
@@ -94,11 +104,13 @@ module.exports = function (Plugin) {
94
104
  preset: z.string().optional().describe('预设尺寸key'),
95
105
  width: z.number().optional().describe('自定义宽度'),
96
106
  height: z.number().optional().describe('自定义高度'),
97
- background: z.string().optional().describe('背景颜色'),
107
+ background: z.union([z.string(), z.object({
108
+ image: z.string()
109
+ })]).optional().describe('背景颜色或图片路径'),
98
110
  }),
99
111
  execute: async (args) => {
100
112
  try {
101
- const result = this._canvasManager.create(args)
113
+ const result = await this._canvasManager.create(args)
102
114
  return { success: true, ...result }
103
115
  } catch (err) {
104
116
  return { success: false, error: err.message }
@@ -146,7 +158,7 @@ module.exports = function (Plugin) {
146
158
  * 添加背景
147
159
  */
148
160
  add_poster_background: {
149
- description: '为画布添加纯色或渐变背景',
161
+ description: '为画布添加纯色、渐变或图片背景',
150
162
  inputSchema: z.object({
151
163
  color: z.string().optional().describe('纯色背景'),
152
164
  gradient: z.object({
@@ -154,13 +166,14 @@ module.exports = function (Plugin) {
154
166
  colors: z.array(z.string()).describe('颜色数组'),
155
167
  direction: z.number().optional().describe('方向角度,默认45'),
156
168
  }).optional(),
169
+ image: z.string().optional().describe('背景图片路径(本地路径或URL)'),
157
170
  }),
158
171
  execute: async (args) => {
159
172
  try {
160
173
  if (!this._canvasManager.isCreated()) {
161
174
  return { success: false, error: 'No canvas created' }
162
175
  }
163
- return addBackground(
176
+ return await addBackground(
164
177
  this._canvasManager.getProject(),
165
178
  this._canvasManager.getCanvas(),
166
179
  args
@@ -346,6 +359,82 @@ module.exports = function (Plugin) {
346
359
  },
347
360
  },
348
361
 
362
+ /**
363
+ * 添加富文本(支持旋转和多种样式)
364
+ */
365
+ add_poster_rich_text: {
366
+ description: '添加富文本,支持旋转、描边、渐变、阴影等多种样式',
367
+ inputSchema: z.object({
368
+ // 位置和尺寸
369
+ x: z.number().describe('X坐标'),
370
+ y: z.number().describe('Y坐标'),
371
+ width: z.number().optional().describe('文本区域宽度(用于自动换行)'),
372
+
373
+ // 文本内容
374
+ text: z.string().describe('文字内容'),
375
+
376
+ // 字体样式
377
+ fontSize: z.number().optional().describe('字体大小,默认48'),
378
+ fontFamily: z.string().optional().describe('字体名称'),
379
+ fontWeight: z.union([z.string(), z.number()]).optional().describe('字重 normal/bold 或 100-900'),
380
+ fontStyle: z.enum(['normal', 'italic', 'oblique']).optional().describe('字体风格'),
381
+ bold: z.boolean().optional().describe('粗体'),
382
+ italic: z.boolean().optional().describe('斜体'),
383
+
384
+ // 文字装饰
385
+ underline: z.boolean().optional().describe('下划线'),
386
+ strikethrough: z.boolean().optional().describe('删除线'),
387
+
388
+ // 颜色
389
+ color: z.string().optional().describe('文字颜色,默认#ffffff'),
390
+ backgroundColor: z.string().optional().describe('文字背景色'),
391
+ gradient: z.object({
392
+ colors: z.array(z.string()).describe('渐变颜色数组'),
393
+ direction: z.number().optional().describe('方向角度'),
394
+ }).optional().describe('渐变填充'),
395
+
396
+ // 描边
397
+ strokeColor: z.string().optional().describe('描边颜色'),
398
+ strokeWidth: z.number().optional().describe('描边宽度'),
399
+
400
+ // 阴影
401
+ shadow: z.object({
402
+ color: z.string().optional().describe('阴影颜色'),
403
+ blur: z.number().optional().describe('模糊度'),
404
+ offsetX: z.number().optional().describe('X偏移'),
405
+ offsetY: z.number().optional().describe('Y偏移'),
406
+ }).optional(),
407
+
408
+ // 间距
409
+ letterSpacing: z.number().optional().describe('字间距'),
410
+ lineSpacing: z.number().optional().describe('行间距增量'),
411
+ lineHeight: z.number().optional().describe('行高'),
412
+
413
+ // 对齐
414
+ align: z.enum(['left', 'center', 'right', 'justify']).optional().describe('对齐方式'),
415
+
416
+ // 变换
417
+ rotation: z.number().optional().describe('旋转角度(度)'),
418
+ scale: z.union([z.number(), z.object({
419
+ x: z.number().optional(),
420
+ y: z.number().optional()
421
+ })]).optional().describe('缩放'),
422
+
423
+ // 透明度
424
+ opacity: z.number().optional().describe('透明度 0-1,默认1'),
425
+ }),
426
+ execute: async (args) => {
427
+ try {
428
+ if (!this._canvasManager.isCreated()) {
429
+ return { success: false, error: 'No canvas created' }
430
+ }
431
+ return addRichText(this._canvasManager.getProject(), args)
432
+ } catch (err) {
433
+ return { success: false, error: err.message }
434
+ }
435
+ },
436
+ },
437
+
349
438
  /**
350
439
  * 添加图片
351
440
  */
@@ -364,7 +453,7 @@ module.exports = function (Plugin) {
364
453
  if (!this._canvasManager.isCreated()) {
365
454
  return { success: false, error: 'No canvas created' }
366
455
  }
367
- return addImage(this._canvasManager.getProject(), args)
456
+ return await addImage(this._canvasManager.getProject(), args)
368
457
  } catch (err) {
369
458
  return { success: false, error: err.message }
370
459
  }
@@ -1445,6 +1534,339 @@ module.exports = function (Plugin) {
1445
1534
  },
1446
1535
  },
1447
1536
 
1537
+ /**
1538
+ * 添加按钮
1539
+ */
1540
+ add_poster_button: {
1541
+ description: '添加按钮组件',
1542
+ inputSchema: z.object({
1543
+ x: z.number().describe('X坐标'),
1544
+ y: z.number().describe('Y坐标'),
1545
+ width: z.number().optional().describe('宽度,默认200'),
1546
+ height: z.number().optional().describe('高度,默认60'),
1547
+ text: z.string().optional().describe('按钮文字'),
1548
+ fontSize: z.number().optional().describe('字体大小'),
1549
+ color: z.string().optional().describe('文字颜色'),
1550
+ backgroundColor: z.string().optional().describe('背景色'),
1551
+ borderColor: z.string().optional().describe('边框颜色'),
1552
+ borderWidth: z.number().optional().describe('边框宽度'),
1553
+ radius: z.number().optional().describe('圆角'),
1554
+ shadow: z.object({
1555
+ color: z.string().optional(),
1556
+ blur: z.number().optional(),
1557
+ offsetX: z.number().optional(),
1558
+ offsetY: z.number().optional(),
1559
+ }).optional(),
1560
+ gradient: z.object({
1561
+ colors: z.array(z.string()).describe('渐变颜色数组'),
1562
+ }).optional(),
1563
+ opacity: z.number().optional().describe('透明度'),
1564
+ }),
1565
+ execute: async (args) => {
1566
+ try {
1567
+ if (!this._canvasManager.isCreated()) {
1568
+ return { success: false, error: 'No canvas created' }
1569
+ }
1570
+ return await createButton(
1571
+ this._canvasManager.getProject(),
1572
+ this._canvasManager.getCanvas(),
1573
+ args
1574
+ )
1575
+ } catch (err) {
1576
+ return { success: false, error: err.message }
1577
+ }
1578
+ },
1579
+ },
1580
+
1581
+ /**
1582
+ * 添加图标
1583
+ */
1584
+ add_poster_icon: {
1585
+ description: '添加图标(emoji或图片)',
1586
+ inputSchema: z.object({
1587
+ x: z.number().describe('X坐标'),
1588
+ y: z.number().describe('Y坐标'),
1589
+ size: z.number().optional().describe('图标大小,默认64'),
1590
+ icon: z.string().describe('图标内容(emoji或图片URL)'),
1591
+ color: z.string().optional().describe('颜色'),
1592
+ backgroundColor: z.string().optional().describe('背景色'),
1593
+ borderColor: z.string().optional().describe('边框颜色'),
1594
+ borderWidth: z.number().optional().describe('边框宽度'),
1595
+ radius: z.number().optional().describe('圆角'),
1596
+ shadow: z.object({
1597
+ color: z.string().optional(),
1598
+ blur: z.number().optional(),
1599
+ offsetX: z.number().optional(),
1600
+ offsetY: z.number().optional(),
1601
+ }).optional(),
1602
+ opacity: z.number().optional().describe('透明度'),
1603
+ }),
1604
+ execute: async (args) => {
1605
+ try {
1606
+ if (!this._canvasManager.isCreated()) {
1607
+ return { success: false, error: 'No canvas created' }
1608
+ }
1609
+ return await createIcon(
1610
+ this._canvasManager.getProject(),
1611
+ args
1612
+ )
1613
+ } catch (err) {
1614
+ return { success: false, error: err.message }
1615
+ }
1616
+ },
1617
+ },
1618
+
1619
+ /**
1620
+ * 添加二维码
1621
+ */
1622
+ // add_poster_qrcode: {
1623
+ // description: '添加二维码',
1624
+ // inputSchema: z.object({
1625
+ // x: z.number().describe('X坐标'),
1626
+ // y: z.number().describe('Y坐标'),
1627
+ // size: z.number().optional().describe('二维码大小,默认200'),
1628
+ // content: z.string().describe('二维码内容'),
1629
+ // color: z.string().optional().describe('前景色'),
1630
+ // backgroundColor: z.string().optional().describe('背景色'),
1631
+ // logo: z.string().optional().describe('中间logo图片路径'),
1632
+ // logoSize: z.number().optional().describe('logo大小'),
1633
+ // opacity: z.number().optional().describe('透明度'),
1634
+ // }),
1635
+ // execute: async (args) => {
1636
+ // try {
1637
+ // if (!this._canvasManager.isCreated()) {
1638
+ // return { success: false, error: 'No canvas created' }
1639
+ // }
1640
+ // return await createQRCode(
1641
+ // this._canvasManager.getProject(),
1642
+ // this._canvasManager.getCanvas(),
1643
+ // args
1644
+ // )
1645
+ // } catch (err) {
1646
+ // return { success: false, error: err.message }
1647
+ // }
1648
+ // },
1649
+ // },
1650
+
1651
+ /**
1652
+ * 添加装饰边框
1653
+ */
1654
+ add_poster_frame: {
1655
+ description: '添加装饰边框',
1656
+ inputSchema: z.object({
1657
+ x: z.number().describe('X坐标'),
1658
+ y: z.number().describe('Y坐标'),
1659
+ width: z.number().describe('宽度'),
1660
+ height: z.number().describe('高度'),
1661
+ style: z.enum(['simple', 'double', 'dashed', 'dotted', 'corner', 'vintage', 'modern', 'floral']).optional().describe('边框样式'),
1662
+ color: z.string().optional().describe('边框颜色'),
1663
+ borderWidth: z.number().optional().describe('边框宽度'),
1664
+ radius: z.number().optional().describe('圆角'),
1665
+ padding: z.number().optional().describe('内边距'),
1666
+ opacity: z.number().optional().describe('透明度'),
1667
+ }),
1668
+ execute: async (args) => {
1669
+ try {
1670
+ if (!this._canvasManager.isCreated()) {
1671
+ return { success: false, error: 'No canvas created' }
1672
+ }
1673
+ return await createFrame(
1674
+ this._canvasManager.getProject(),
1675
+ this._canvasManager.getCanvas(),
1676
+ args
1677
+ )
1678
+ } catch (err) {
1679
+ return { success: false, error: err.message }
1680
+ }
1681
+ },
1682
+ },
1683
+
1684
+ /**
1685
+ * 添加对话气泡
1686
+ */
1687
+ add_poster_bubble: {
1688
+ description: '添加对话气泡',
1689
+ inputSchema: z.object({
1690
+ x: z.number().describe('X坐标'),
1691
+ y: z.number().describe('Y坐标'),
1692
+ width: z.number().optional().describe('宽度,默认300'),
1693
+ height: z.number().optional().describe('高度,默认100'),
1694
+ text: z.string().describe('气泡文字'),
1695
+ fontSize: z.number().optional().describe('字体大小'),
1696
+ color: z.string().optional().describe('文字颜色'),
1697
+ backgroundColor: z.string().optional().describe('背景色'),
1698
+ borderColor: z.string().optional().describe('边框颜色'),
1699
+ borderWidth: z.number().optional().describe('边框宽度'),
1700
+ radius: z.number().optional().describe('圆角'),
1701
+ tailDirection: z.enum(['bottom', 'top', 'left', 'right']).optional().describe('尾巴方向'),
1702
+ tailPosition: z.enum(['left', 'center', 'right']).optional().describe('尾巴位置'),
1703
+ shadow: z.object({
1704
+ color: z.string().optional(),
1705
+ blur: z.number().optional(),
1706
+ offsetX: z.number().optional(),
1707
+ offsetY: z.number().optional(),
1708
+ }).optional(),
1709
+ opacity: z.number().optional().describe('透明度'),
1710
+ }),
1711
+ execute: async (args) => {
1712
+ try {
1713
+ if (!this._canvasManager.isCreated()) {
1714
+ return { success: false, error: 'No canvas created' }
1715
+ }
1716
+ return await createBubble(
1717
+ this._canvasManager.getProject(),
1718
+ this._canvasManager.getCanvas(),
1719
+ args
1720
+ )
1721
+ } catch (err) {
1722
+ return { success: false, error: err.message }
1723
+ }
1724
+ },
1725
+ },
1726
+
1727
+ /**
1728
+ * 添加丝带
1729
+ */
1730
+ add_poster_ribbon: {
1731
+ description: '添加丝带飘带',
1732
+ inputSchema: z.object({
1733
+ x: z.number().describe('X坐标'),
1734
+ y: z.number().describe('Y坐标'),
1735
+ width: z.number().optional().describe('宽度,默认300'),
1736
+ text: z.string().optional().describe('丝带文字'),
1737
+ fontSize: z.number().optional().describe('字体大小'),
1738
+ color: z.string().optional().describe('文字颜色'),
1739
+ backgroundColor: z.string().optional().describe('背景色'),
1740
+ borderColor: z.string().optional().describe('边框颜色'),
1741
+ borderWidth: z.number().optional().describe('边框宽度'),
1742
+ style: z.enum(['fold', 'diagonal', 'corner']).optional().describe('丝带样式'),
1743
+ shadow: z.object({
1744
+ color: z.string().optional(),
1745
+ blur: z.number().optional(),
1746
+ offsetX: z.number().optional(),
1747
+ offsetY: z.number().optional(),
1748
+ }).optional(),
1749
+ opacity: z.number().optional().describe('透明度'),
1750
+ }),
1751
+ execute: async (args) => {
1752
+ try {
1753
+ if (!this._canvasManager.isCreated()) {
1754
+ return { success: false, error: 'No canvas created' }
1755
+ }
1756
+ return await createRibbon(
1757
+ this._canvasManager.getProject(),
1758
+ this._canvasManager.getCanvas(),
1759
+ args
1760
+ )
1761
+ } catch (err) {
1762
+ return { success: false, error: err.message }
1763
+ }
1764
+ },
1765
+ },
1766
+
1767
+ /**
1768
+ * 添加印章
1769
+ */
1770
+ add_poster_seal: {
1771
+ description: '添加印章效果',
1772
+ inputSchema: z.object({
1773
+ x: z.number().describe('X坐标'),
1774
+ y: z.number().describe('Y坐标'),
1775
+ size: z.number().optional().describe('印章大小,默认100'),
1776
+ text: z.string().optional().describe('印章文字'),
1777
+ fontSize: z.number().optional().describe('字体大小'),
1778
+ color: z.string().optional().describe('印章颜色'),
1779
+ style: z.enum(['circle', 'square', 'star', 'hexagon']).optional().describe('印章形状'),
1780
+ borderWidth: z.number().optional().describe('边框宽度'),
1781
+ opacity: z.number().optional().describe('透明度'),
1782
+ }),
1783
+ execute: async (args) => {
1784
+ try {
1785
+ if (!this._canvasManager.isCreated()) {
1786
+ return { success: false, error: 'No canvas created' }
1787
+ }
1788
+ return await createSeal(
1789
+ this._canvasManager.getProject(),
1790
+ this._canvasManager.getCanvas(),
1791
+ args
1792
+ )
1793
+ } catch (err) {
1794
+ return { success: false, error: err.message }
1795
+ }
1796
+ },
1797
+ },
1798
+
1799
+ /**
1800
+ * 添加高亮文字
1801
+ */
1802
+ add_poster_highlight_text: {
1803
+ description: '添加高亮文字(荧光笔效果)',
1804
+ inputSchema: z.object({
1805
+ x: z.number().describe('X坐标'),
1806
+ y: z.number().describe('Y坐标'),
1807
+ text: z.string().describe('文字内容'),
1808
+ fontSize: z.number().optional().describe('字体大小'),
1809
+ color: z.string().optional().describe('文字颜色'),
1810
+ highlightColor: z.string().optional().describe('高亮颜色'),
1811
+ highlightStyle: z.enum(['marker', 'underline', 'background', 'stroke', 'neon']).optional().describe('高亮样式'),
1812
+ strokeWidth: z.number().optional().describe('描边宽度'),
1813
+ shadow: z.object({
1814
+ color: z.string().optional(),
1815
+ blur: z.number().optional(),
1816
+ offsetX: z.number().optional(),
1817
+ offsetY: z.number().optional(),
1818
+ }).optional(),
1819
+ opacity: z.number().optional().describe('透明度'),
1820
+ }),
1821
+ execute: async (args) => {
1822
+ try {
1823
+ if (!this._canvasManager.isCreated()) {
1824
+ return { success: false, error: 'No canvas created' }
1825
+ }
1826
+ return await createHighlightText(
1827
+ this._canvasManager.getProject(),
1828
+ this._canvasManager.getCanvas(),
1829
+ args
1830
+ )
1831
+ } catch (err) {
1832
+ return { success: false, error: err.message }
1833
+ }
1834
+ },
1835
+ },
1836
+
1837
+ /**
1838
+ * 添加条形码
1839
+ */
1840
+ // add_poster_barcode: {
1841
+ // description: '添加条形码',
1842
+ // inputSchema: z.object({
1843
+ // x: z.number().describe('X坐标'),
1844
+ // y: z.number().describe('Y坐标'),
1845
+ // width: z.number().optional().describe('宽度,默认300'),
1846
+ // height: z.number().optional().describe('高度,默认100'),
1847
+ // content: z.string().describe('条形码内容'),
1848
+ // color: z.string().optional().describe('条形码颜色'),
1849
+ // showText: z.boolean().optional().describe('是否显示文字'),
1850
+ // textColor: z.string().optional().describe('文字颜色'),
1851
+ // fontSize: z.number().optional().describe('字体大小'),
1852
+ // opacity: z.number().optional().describe('透明度'),
1853
+ // }),
1854
+ // execute: async (args) => {
1855
+ // try {
1856
+ // if (!this._canvasManager.isCreated()) {
1857
+ // return { success: false, error: 'No canvas created' }
1858
+ // }
1859
+ // return await createBarcode(
1860
+ // this._canvasManager.getProject(),
1861
+ // this._canvasManager.getCanvas(),
1862
+ // args
1863
+ // )
1864
+ // } catch (err) {
1865
+ // return { success: false, error: err.message }
1866
+ // }
1867
+ // },
1868
+ // },
1869
+
1448
1870
  // ==================== 组件化海报生成 ====================
1449
1871
 
1450
1872
  /**
@@ -1456,11 +1878,12 @@ module.exports = function (Plugin) {
1456
1878
  components: z.array(z.object({
1457
1879
  type: z.enum([
1458
1880
  'background', 'rectangle', 'circle', 'line', 'polygon',
1459
- 'text', 'artText', 'image', 'svg', 'imageFrame',
1881
+ 'text', 'artText', 'richText', 'image', 'svg', 'imageFrame',
1460
1882
  'columns', 'grid', 'star', 'arrow', 'progressCircle', 'chip', 'chart', 'watermark', 'table',
1461
1883
  'card', 'badge', 'cta', 'feature', 'featureGrid', 'divider',
1462
- 'avatar', 'progress', 'rating', 'quote', 'statCard',
1884
+ 'avatar', 'progress', 'rating', 'quote', 'statCard',
1463
1885
  'tagCloud', 'stepper', 'timeline', 'listItem', 'notification',
1886
+ 'button', 'icon', 'qrcode', 'frame', 'bubble', 'ribbon', 'seal', 'highlightText', 'barcode',
1464
1887
  ]).describe('组件类型'),
1465
1888
  })).describe('组件配置数组'),
1466
1889
  }),
@@ -0,0 +1,84 @@
1
+ /**
2
+ * 图片加载工具 - 使用 canvas loadImage 加载图片(仅支持本地路径)
3
+ */
4
+
5
+ const { loadImage } = require('canvas')
6
+ const paper = require('paper')
7
+ const fs = require('fs')
8
+ const path = require('path')
9
+
10
+ /**
11
+ * 解析图片源(仅支持本地路径)
12
+ * @param {string} src - 图片源
13
+ * @returns {Promise<{path: string, type: 'local'}>}
14
+ */
15
+ async function resolveImageSource(src) {
16
+ if (!src) {
17
+ throw new Error('Image source is required')
18
+ }
19
+
20
+ // 确保 src 是字符串
21
+ if (typeof src !== 'string') {
22
+ throw new Error('Image source must be a string')
23
+ }
24
+
25
+ // 本地路径
26
+ let absolutePath = src
27
+ if (!path.isAbsolute(absolutePath)) {
28
+ absolutePath = path.join(process.cwd(), absolutePath)
29
+ }
30
+
31
+ if (!fs.existsSync(absolutePath)) {
32
+ throw new Error(`图片文件不存在: ${absolutePath}`)
33
+ }
34
+
35
+ return { path: absolutePath, type: 'local' }
36
+ }
37
+
38
+ /**
39
+ * 加载图片并创建 Paper.js Raster
40
+ * @param {paper.Project} project - Paper.js 项目
41
+ * @param {string} src - 图片源
42
+ * @param {Object} bounds - 边界 {x, y, width?, height?}
43
+ * @param {number} opacity - 透明度
44
+ * @returns {Promise<{raster: paper.Raster, cleanup: function}>}
45
+ */
46
+ async function loadImageAsRaster(project, src, bounds = {}, opacity = 1) {
47
+ const { x = 0, y = 0, width, height } = bounds
48
+
49
+ // 解析图片源
50
+ const { path: imgPath } = await resolveImageSource(src)
51
+
52
+ // 使用 canvas loadImage 加载
53
+ const imageData = await loadImage(imgPath)
54
+
55
+ // 创建 Paper.js Raster
56
+ const raster = new paper.Raster(imageData)
57
+
58
+ // 添加到项目的活动层
59
+ if (project && project.activeLayer) {
60
+ project.activeLayer.addChild(raster)
61
+ }
62
+
63
+ // 设置位置和尺寸
64
+ if (width && height) {
65
+ raster.bounds = new paper.Rectangle(x, y, width, height)
66
+ } else if (width) {
67
+ const scale = width / raster.width
68
+ raster.bounds = new paper.Rectangle(x, y, width, raster.height * scale)
69
+ } else if (height) {
70
+ const scale = height / raster.height
71
+ raster.bounds = new paper.Rectangle(x, y, raster.width * scale, height)
72
+ } else {
73
+ raster.position = new paper.Point(x, y)
74
+ }
75
+
76
+ if (opacity !== undefined) raster.opacity = opacity
77
+
78
+ return { raster, cleanup: () => {} }
79
+ }
80
+
81
+ module.exports = {
82
+ loadImageAsRaster,
83
+ resolveImageSource,
84
+ }
@@ -0,0 +1 @@
1
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800" height="600" viewBox="0,0,800,600"><g fill="#ffffff" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="sans-serif" font-weight="normal" font-size="40" text-anchor="middle" style="mix-blend-mode: normal"><text x="400" y="300">背景图片测试</text></g></svg>