foliko 1.1.7 → 1.1.9

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 (113) hide show
  1. package/.agent/agents/poster-expert.md +228 -0
  2. package/.agent/data/default.json +10 -0
  3. package/.agent/data/plugins-state.json +1 -1
  4. package/.agent/mcp_config.json +0 -6
  5. package/.agent/plugins/test-plugin.py +15 -0
  6. package/.agent/plugins/test_nested_plugin.py +85 -0
  7. package/.agent/sessions/cli_default.json +83 -803
  8. package/.agent/sessions/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.json +189 -0
  9. package/.claude/settings.local.json +8 -1
  10. package/cli/src/ui/chat-ui.js +32 -21
  11. package/foliko_poster.png +0 -0
  12. package/output/business_poster_final.png +0 -0
  13. package/output/emoji_test_v2.png +0 -0
  14. package/output/foliko-ai-launch-poster.png +0 -0
  15. package/output/foliko-poster.png +0 -0
  16. package/output/muji_style_poster.png +0 -0
  17. package/output/new_product_launch.png +0 -0
  18. package/output/news_poster_625yi_subsidy.png +0 -0
  19. package/output/tech_future_2026.png +0 -0
  20. package/output/tech_future_poster.png +0 -0
  21. package/package.json +2 -2
  22. package/plugins/default-plugins.js +60 -7
  23. package/plugins/extension-executor-plugin.js +32 -22
  24. package/plugins/feishu-plugin.js +2 -2
  25. package/plugins/python-plugin-loader.js +461 -40
  26. package/plugins/python-plugin-loader.js.bak +856 -0
  27. package/plugins/subagent-plugin.js +0 -121
  28. package/plugins/telegram-plugin.js +2 -2
  29. package/plugins/weixin-plugin.js +40 -31
  30. package/poster.png +0 -0
  31. package/skills/foliko-dev/SKILL.md +6 -0
  32. package/skills/python-plugin-dev/SKILL.md +124 -2
  33. package/src/core/agent-chat.js +220 -281
  34. package/src/core/agent.js +127 -29
  35. package/src/executors/mcp-executor.js +282 -26
  36. package/src/utils/plugin-helpers.js +22 -4
  37. package/system.md +362 -109
  38. package/test-scan.js +18 -0
  39. package/.agent/memory/feedback/mnt7jrlt-d67qs7.md +0 -15
  40. package/.agent/memory/feedback/mnt88ja3-al4fuy.md +0 -9
  41. package/.agent/plugins/poster-plugin/README.md +0 -304
  42. package/.agent/plugins/poster-plugin/emojis/rocket.png +0 -1
  43. package/.agent/plugins/poster-plugin/fonts/PatuaOne-Regular.ttf +0 -0
  44. package/.agent/plugins/poster-plugin/fonts/SegoeUI Emoji.ttf +0 -0
  45. package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221.ttf +0 -0
  46. package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221/347/262/227/344/275/223.ttf +0 -0
  47. package/.agent/plugins/poster-plugin/index.js +0 -13
  48. package/.agent/plugins/poster-plugin/package.json +0 -29
  49. package/.agent/plugins/poster-plugin/src/canvas.js +0 -224
  50. package/.agent/plugins/poster-plugin/src/components/arrow.js +0 -84
  51. package/.agent/plugins/poster-plugin/src/components/avatar.js +0 -71
  52. package/.agent/plugins/poster-plugin/src/components/badge.js +0 -85
  53. package/.agent/plugins/poster-plugin/src/components/barcode.js +0 -120
  54. package/.agent/plugins/poster-plugin/src/components/bubble.js +0 -153
  55. package/.agent/plugins/poster-plugin/src/components/button.js +0 -124
  56. package/.agent/plugins/poster-plugin/src/components/card.js +0 -88
  57. package/.agent/plugins/poster-plugin/src/components/chart.js +0 -127
  58. package/.agent/plugins/poster-plugin/src/components/chip.js +0 -88
  59. package/.agent/plugins/poster-plugin/src/components/columns.js +0 -107
  60. package/.agent/plugins/poster-plugin/src/components/cta.js +0 -87
  61. package/.agent/plugins/poster-plugin/src/components/divider.js +0 -55
  62. package/.agent/plugins/poster-plugin/src/components/feature.js +0 -85
  63. package/.agent/plugins/poster-plugin/src/components/featureGrid.js +0 -117
  64. package/.agent/plugins/poster-plugin/src/components/frame.js +0 -230
  65. package/.agent/plugins/poster-plugin/src/components/grid.js +0 -118
  66. package/.agent/plugins/poster-plugin/src/components/highlightText.js +0 -144
  67. package/.agent/plugins/poster-plugin/src/components/icon.js +0 -94
  68. package/.agent/plugins/poster-plugin/src/components/imageFrame.js +0 -155
  69. package/.agent/plugins/poster-plugin/src/components/index.js +0 -81
  70. package/.agent/plugins/poster-plugin/src/components/listItem.js +0 -147
  71. package/.agent/plugins/poster-plugin/src/components/notification.js +0 -123
  72. package/.agent/plugins/poster-plugin/src/components/progress.js +0 -79
  73. package/.agent/plugins/poster-plugin/src/components/progressCircle.js +0 -117
  74. package/.agent/plugins/poster-plugin/src/components/qrcode.js +0 -74
  75. package/.agent/plugins/poster-plugin/src/components/quote.js +0 -97
  76. package/.agent/plugins/poster-plugin/src/components/rating.js +0 -85
  77. package/.agent/plugins/poster-plugin/src/components/ribbon.js +0 -193
  78. package/.agent/plugins/poster-plugin/src/components/seal.js +0 -146
  79. package/.agent/plugins/poster-plugin/src/components/star.js +0 -70
  80. package/.agent/plugins/poster-plugin/src/components/statCard.js +0 -105
  81. package/.agent/plugins/poster-plugin/src/components/stepper.js +0 -118
  82. package/.agent/plugins/poster-plugin/src/components/table.js +0 -167
  83. package/.agent/plugins/poster-plugin/src/components/tagCloud.js +0 -85
  84. package/.agent/plugins/poster-plugin/src/components/timeline.js +0 -117
  85. package/.agent/plugins/poster-plugin/src/components/watermark.js +0 -52
  86. package/.agent/plugins/poster-plugin/src/composer.js +0 -2146
  87. package/.agent/plugins/poster-plugin/src/elements/artText.js +0 -60
  88. package/.agent/plugins/poster-plugin/src/elements/background.js +0 -84
  89. package/.agent/plugins/poster-plugin/src/elements/circle.js +0 -31
  90. package/.agent/plugins/poster-plugin/src/elements/image.js +0 -28
  91. package/.agent/plugins/poster-plugin/src/elements/index.js +0 -28
  92. package/.agent/plugins/poster-plugin/src/elements/line.js +0 -23
  93. package/.agent/plugins/poster-plugin/src/elements/polygon.js +0 -63
  94. package/.agent/plugins/poster-plugin/src/elements/rectangle.js +0 -32
  95. package/.agent/plugins/poster-plugin/src/elements/richText.js +0 -230
  96. package/.agent/plugins/poster-plugin/src/elements/svg.js +0 -108
  97. package/.agent/plugins/poster-plugin/src/elements/text.js +0 -109
  98. package/.agent/plugins/poster-plugin/src/fonts.js +0 -243
  99. package/.agent/plugins/poster-plugin/src/index.js +0 -2081
  100. package/.agent/plugins/poster-plugin/src/presets.js +0 -36
  101. package/.agent/plugins/poster-plugin/src/templates/business.js +0 -60
  102. package/.agent/plugins/poster-plugin/src/templates/gradient.js +0 -64
  103. package/.agent/plugins/poster-plugin/src/templates/index.js +0 -43
  104. package/.agent/plugins/poster-plugin/src/templates/modern.js +0 -69
  105. package/.agent/plugins/poster-plugin/src/templates/simple.js +0 -58
  106. package/.agent/plugins/poster-plugin/src/templates/social.js +0 -62
  107. package/.agent/plugins/poster-plugin/src/templates/tech.js +0 -84
  108. package/.agent/plugins/poster-plugin/src/utils/imageLoader.js +0 -84
  109. package/.agent/plugins/poster-plugin/test-background.svg +0 -1
  110. package/.agent/plugins/poster-plugin/test-full-poster.svg +0 -2
  111. package/.agent/plugins/poster-plugin/test-image.png +0 -0
  112. package/.agent/plugins/poster-plugin/yarn.lock +0 -1007
  113. package/output/zen_silence.png +0 -0
@@ -1,60 +0,0 @@
1
- /**
2
- * 艺术文字元素
3
- */
4
-
5
- const paper = require('paper')
6
- const { validateFont, getDefaultFont } = require('../fonts')
7
-
8
- /**
9
- * 添加艺术文字
10
- */
11
- function addArtText(project, args) {
12
- const {
13
- text,
14
- x, y,
15
- fontSize,
16
- fontFamily,
17
- gradient,
18
- strokeColor,
19
- strokeWidth,
20
- shadow,
21
- } = args
22
-
23
- const textItem = new paper.PointText({
24
- point: [x, y],
25
- content: text,
26
- fontSize: fontSize || 120,
27
- fontFamily: validateFont(fontFamily) || getDefaultFont(),
28
- fillColor: gradient
29
- ? new paper.Color(gradient.colors[0])
30
- : new paper.Color('#ffffff'),
31
- justification: 'center',
32
- })
33
-
34
- // 应用渐变
35
- if (gradient && gradient.colors.length > 0) {
36
- const colors = gradient.colors.map(c => new paper.Color(c))
37
- textItem.fillColor = new paper.Color({
38
- gradient: { stops: colors },
39
- origin: textItem.bounds.topLeft,
40
- destination: textItem.bounds.topRight,
41
- })
42
- }
43
-
44
- // 应用描边
45
- if (strokeColor) {
46
- textItem.strokeColor = new paper.Color(strokeColor)
47
- textItem.strokeWidth = strokeWidth || 2
48
- }
49
-
50
- // 应用阴影
51
- if (shadow) {
52
- textItem.shadowColor = new paper.Color(shadow.color)
53
- textItem.shadowBlur = shadow.blur || 10
54
- textItem.shadowOffset = new paper.Point(shadow.offsetX || 3, shadow.offsetY || 3)
55
- }
56
-
57
- return { success: true, id: textItem.id, type: 'artText' }
58
- }
59
-
60
- module.exports = addArtText
@@ -1,84 +0,0 @@
1
- /**
2
- * 背景元素
3
- */
4
-
5
- const paper = require('paper')
6
- const { loadImageAsRaster } = require('../utils/imageLoader')
7
-
8
- /**
9
- * 添加背景(支持纯色、渐变或图片)
10
- */
11
- async function addBackground(project, canvas, args) {
12
- const { color, gradient, image } = args
13
-
14
- if (image) {
15
- const { raster } = await loadImageAsRaster(project, image, { x: 0, y: 0 })
16
-
17
- raster.onLoad = () => {
18
- // 计算缩放比例,使图片覆盖整个画布(cover 模式)
19
- const canvasRatio = canvas.width / canvas.height
20
- const imageRatio = raster.width / raster.height
21
-
22
- let scaledWidth, scaledHeight, offsetX, offsetY
23
-
24
- if (imageRatio > canvasRatio) {
25
- // 图片更宽,以高度为基准缩放
26
- scaledHeight = canvas.height
27
- scaledWidth = raster.width * (canvas.height / raster.height)
28
- offsetX = (canvas.width - scaledWidth) / 2
29
- offsetY = 0
30
- } else {
31
- // 图片更高,以宽度为基准缩放
32
- scaledWidth = canvas.width
33
- scaledHeight = raster.height * (canvas.width / raster.width)
34
- offsetX = 0
35
- offsetY = (canvas.height - scaledHeight) / 2
36
- }
37
-
38
- raster.bounds = new paper.Rectangle(offsetX, offsetY, scaledWidth, scaledHeight)
39
- raster.sendToBack()
40
- }
41
-
42
- return { success: true, message: 'Background image added' }
43
- }
44
-
45
- if (gradient) {
46
- const { type, colors, direction } = gradient
47
- const paperColors = colors.map(c => new paper.Color(c))
48
-
49
- if (type === 'linear') {
50
- const angle = (direction || 45) * Math.PI / 180
51
- const diagonal = Math.sqrt(canvas.width ** 2 + canvas.height ** 2)
52
- const start = new paper.Point(
53
- canvas.width / 2 - Math.cos(angle) * diagonal / 2,
54
- canvas.height / 2 - Math.sin(angle) * diagonal / 2
55
- )
56
- const stop = new paper.Point(
57
- canvas.width / 2 + Math.cos(angle) * diagonal / 2,
58
- canvas.height / 2 + Math.sin(angle) * diagonal / 2
59
- )
60
- project.activeLayer.fillColor = new paper.Color({
61
- gradient: { stops: paperColors },
62
- origin: start,
63
- destination: stop,
64
- })
65
- } else {
66
- // radial
67
- const center = new paper.Point(canvas.width / 2, canvas.height / 2)
68
- const radius = Math.max(canvas.width, canvas.height) / 2
69
- project.activeLayer.fillColor = new paper.Color({
70
- gradient: { stops: paperColors },
71
- origin: center,
72
- destination: center.add(new paper.Point(radius, 0)),
73
- })
74
- }
75
- } else if (color) {
76
- project.activeLayer.fillColor = new paper.Color(color)
77
- } else {
78
- throw new Error('Must provide color, gradient, or image')
79
- }
80
-
81
- return { success: true, message: 'Background added' }
82
- }
83
-
84
- module.exports = addBackground
@@ -1,31 +0,0 @@
1
- /**
2
- * 圆形/椭圆元素
3
- */
4
-
5
- const paper = require('paper')
6
-
7
- /**
8
- * 添加圆形或椭圆
9
- */
10
- function addCircle(project, args) {
11
- const {
12
- cx, cy, rx, ry,
13
- fill, stroke, strokeWidth, opacity,
14
- } = args
15
-
16
- const circle = new paper.Path.Ellipse({
17
- center: [cx, cy],
18
- radius: [rx, ry || rx],
19
- })
20
-
21
- if (fill) circle.fillColor = new paper.Color(fill)
22
- if (stroke) {
23
- circle.strokeColor = new paper.Color(stroke)
24
- circle.strokeWidth = strokeWidth || 1
25
- }
26
- if (opacity !== undefined) circle.opacity = opacity
27
-
28
- return { success: true, id: circle.id, type: 'circle' }
29
- }
30
-
31
- module.exports = addCircle
@@ -1,28 +0,0 @@
1
- /**
2
- * 图片元素
3
- */
4
-
5
- const paper = require('paper')
6
- const { loadImageAsRaster } = require('../utils/imageLoader')
7
-
8
- /**
9
- * 添加图片(支持本地路径、URL、Base64)
10
- */
11
- async function addImage(project, args) {
12
- const { src, x, y, width, height, opacity } = args
13
-
14
- try {
15
- const { raster } = await loadImageAsRaster(project, src, { x, y, width, height }, opacity)
16
-
17
- return {
18
- success: true,
19
- id: raster.id,
20
- type: 'image',
21
- path: src,
22
- }
23
- } catch (err) {
24
- return { success: false, error: `Failed to load image: ${err.message}` }
25
- }
26
- }
27
-
28
- module.exports = addImage
@@ -1,28 +0,0 @@
1
- /**
2
- * 基础元素模块导出
3
- */
4
-
5
- const addRectangle = require('./rectangle')
6
- const addCircle = require('./circle')
7
- const addLine = require('./line')
8
- const addPolygon = require('./polygon')
9
- const addImage = require('./image')
10
- const addText = require('./text')
11
- const addArtText = require('./artText')
12
- const addRichText = require('./richText')
13
- const addBackground = require('./background')
14
- const { addSVG, exportSVG } = require('./svg')
15
-
16
- module.exports = {
17
- addRectangle,
18
- addCircle,
19
- addLine,
20
- addPolygon,
21
- addImage,
22
- addText,
23
- addArtText,
24
- addRichText,
25
- addBackground,
26
- addSVG,
27
- exportSVG,
28
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * 线条元素
3
- */
4
-
5
- const paper = require('paper')
6
-
7
- /**
8
- * 添加线条
9
- */
10
- function addLine(project, args) {
11
- const { x1, y1, x2, y2, stroke, strokeWidth } = args
12
-
13
- const line = new paper.Path.Line({
14
- from: [x1, y1],
15
- to: [x2, y2],
16
- strokeColor: new paper.Color(stroke || '#ffffff'),
17
- strokeWidth: strokeWidth || 2,
18
- })
19
-
20
- return { success: true, id: line.id, type: 'line' }
21
- }
22
-
23
- module.exports = addLine
@@ -1,63 +0,0 @@
1
- /**
2
- * 多边形元素
3
- */
4
-
5
- const paper = require('paper')
6
-
7
- /**
8
- * 添加多边形
9
- * 支持两种模式:
10
- * 1. 规则多边形: cx, cy, radius, sides
11
- * 2. 自定义多边形: points 数组 [[x1,y1], [x2,y2], ...]
12
- */
13
- function addPolygon(project, args) {
14
- const {
15
- cx, cy, radius, sides,
16
- points,
17
- fill, stroke, strokeWidth, opacity,
18
- x = 0, y = 0,
19
- } = args
20
-
21
- let polygon
22
-
23
- if (points && Array.isArray(points)) {
24
- // 使用自定义点创建多边形
25
- // 每个点应该是 [relativeX, relativeY],相对于 x, y 偏移
26
- const pathData = points.map((pt, i) => {
27
- const px = (Array.isArray(pt) ? pt[0] : pt) + x
28
- const py = (Array.isArray(pt) ? pt[1] : 0) + y
29
- return i === 0 ? [px, py] : [px, py]
30
- })
31
-
32
- polygon = new paper.Path({
33
- segments: pathData,
34
- closed: true,
35
- })
36
- } else if (cx !== undefined && cy !== undefined) {
37
- // 使用中心点和半径创建规则多边形
38
- polygon = new paper.Path.RegularPolygon({
39
- center: [cx, cy],
40
- radius: radius || 100,
41
- sides: sides || 6,
42
- })
43
- } else {
44
- throw new Error('Polygon requires either points array or cx/cy coordinates')
45
- }
46
-
47
- if (fill) {
48
- polygon.fillColor = new paper.Color(fill)
49
- } else {
50
- polygon.fillColor = null
51
- }
52
-
53
- if (stroke) {
54
- polygon.strokeColor = new paper.Color(stroke)
55
- polygon.strokeWidth = strokeWidth || 1
56
- }
57
-
58
- if (opacity !== undefined) polygon.opacity = opacity
59
-
60
- return { success: true, id: polygon.id, type: 'polygon' }
61
- }
62
-
63
- module.exports = addPolygon
@@ -1,32 +0,0 @@
1
- /**
2
- * 矩形元素
3
- */
4
-
5
- const paper = require('paper')
6
-
7
- /**
8
- * 添加矩形
9
- */
10
- function addRectangle(project, args) {
11
- const {
12
- x, y, width, height,
13
- fill, stroke, strokeWidth, radius, opacity,
14
- } = args
15
-
16
- const rect = new paper.Path.Rectangle({
17
- point: [x, y],
18
- size: [width, height],
19
- radius: radius || 0,
20
- })
21
-
22
- if (fill) rect.fillColor = new paper.Color(fill)
23
- if (stroke) {
24
- rect.strokeColor = new paper.Color(stroke)
25
- rect.strokeWidth = strokeWidth || 1
26
- }
27
- if (opacity !== undefined) rect.opacity = opacity
28
-
29
- return { success: true, id: rect.id, type: 'rectangle' }
30
- }
31
-
32
- module.exports = addRectangle
@@ -1,230 +0,0 @@
1
- /**
2
- * 富文本组件 - 支持旋转和多种文本样式
3
- */
4
-
5
- const paper = require('paper')
6
- const { validateFont, getDefaultFont, getRegisteredFonts } = require('../fonts')
7
-
8
- /**
9
- * 检测文本是否包含 emoji
10
- */
11
- function containsEmoji(text) {
12
- if (!text || typeof text !== 'string') return false
13
- const emojiRanges = [
14
- /\u{1F600}-\u{1F64F}/u, /\u{1F300}-\u{1F5FF}/u, /\u{1F680}-\u{1F6FF}/u,
15
- /\u{1F700}-\u{1F77F}/u, /\u{1F780}-\u{1F7FF}/u, /\u{1F800}-\u{1F8FF}/u,
16
- /\u{1F900}-\u{1F9FF}/u, /\u{1FA00}-\u{1FA6F}/u, /\u{1FA70}-\u{1FAFF}/u,
17
- /\u{2600}-\u{26FF}/u, /\u{2700}-\u{27BF}/u,
18
- ]
19
- for (const range of emojiRanges) {
20
- if (range.test(text)) return true
21
- }
22
- return /[\u{1F300}-\u{1F9FF}]/u.test(text)
23
- }
24
-
25
- /**
26
- * 获取适合的字体(考虑 emoji)
27
- */
28
- function getFontForText(requestedFont, text) {
29
- const baseFont = validateFont(requestedFont) || getDefaultFont()
30
- if (containsEmoji(text)) {
31
- const registeredFonts = getRegisteredFonts()
32
- const emojiFont = registeredFonts.find(f => {
33
- if (!f) return false
34
- const lower = f.toLowerCase()
35
- return lower.includes('color emoji') || lower.includes('noto emoji') ||
36
- lower.includes('segoe ui emoji') || lower.includes('symbola') || lower.includes('emoji')
37
- })
38
- if (emojiFont) return emojiFont
39
- }
40
- return baseFont
41
- }
42
-
43
- /**
44
- * 添加富文本
45
- * @param {paper.Project} project - Paper.js 项目
46
- * @param {Object} args - 参数
47
- * @returns {Object} 结果
48
- */
49
- function addRichText(project, args) {
50
- const {
51
- // 位置和尺寸
52
- x = 0,
53
- y = 0,
54
- width, // 文本区域宽度(用于自动换行)
55
-
56
- // 文本内容
57
- text = '',
58
-
59
- // 字体样式
60
- fontSize = 48,
61
- fontFamily,
62
- fontWeight, // normal, bold, 100-900
63
- fontStyle, // normal, italic, oblique
64
- italic = false,
65
- bold = false,
66
-
67
- // 文字装饰
68
- underline = false,
69
- strikethrough = false,
70
-
71
- // 颜色
72
- color = '#ffffff',
73
- backgroundColor, // 文字背景色
74
- gradient, // { colors: ['#fff', '#000'], direction: 0 }
75
-
76
- // 描边
77
- strokeColor,
78
- strokeWidth = 1,
79
-
80
- // 阴影
81
- shadow, // { color: '#000', blur: 5, offsetX: 2, offsetY: 2 }
82
-
83
- // 间距
84
- letterSpacing = 0,
85
- lineSpacing = 0, // 行间距增量
86
- lineHeight, // 行高
87
-
88
- // 对齐
89
- align = 'left', // left, center, right, justify
90
-
91
- // 变换
92
- rotation = 0, // 旋转角度(度)
93
- scale,
94
-
95
- // 透明度
96
- opacity = 1,
97
-
98
- // 换行
99
- wrap = true, // 是否自动换行
100
- } = args
101
-
102
- const font = getFontForText(fontFamily, text)
103
- const effectiveFontWeight = bold ? 'bold' : (fontWeight || 'normal')
104
- const effectiveFontStyle = italic ? 'italic' : (fontStyle || 'normal')
105
-
106
- // 创建文本项
107
- const textItem = new paper.PointText({
108
- point: [x, y + fontSize],
109
- content: text,
110
- fontSize,
111
- fontFamily: font,
112
- fontWeight: effectiveFontWeight,
113
- fontStyle: effectiveFontStyle,
114
- fillColor: new paper.Color(color),
115
- justification: align,
116
- })
117
-
118
- // 字母间距
119
- if (letterSpacing !== 0) {
120
- textItem.letterSpacing = letterSpacing
121
- }
122
-
123
- // 行高
124
- if (lineHeight) {
125
- textItem.leading = lineHeight
126
- } else if (lineSpacing !== 0) {
127
- textItem.leading = fontSize + lineSpacing
128
- }
129
-
130
- // 下划线和删除线 - 使用字符样式
131
- if (underline || strikethrough) {
132
- textItem.decorations = []
133
- if (underline) textItem.decorations.push('underline')
134
- if (strikethrough) textItem.decorations.push('strikethrough')
135
- }
136
-
137
- // 渐变填充
138
- if (gradient && gradient.colors && gradient.colors.length > 0) {
139
- const colors = gradient.colors.map(c => new paper.Color(c))
140
- const bounds = textItem.bounds
141
- if (gradient.direction !== undefined) {
142
- // 自定义方向
143
- const angle = gradient.direction * Math.PI / 180
144
- const diagonal = Math.sqrt(bounds.width ** 2 + bounds.height ** 2)
145
- textItem.fillColor = new paper.Color({
146
- gradient: { stops: colors },
147
- origin: new paper.Point(
148
- bounds.center.x - Math.cos(angle) * diagonal / 2,
149
- bounds.center.y - Math.sin(angle) * diagonal / 2
150
- ),
151
- destination: new paper.Point(
152
- bounds.center.x + Math.cos(angle) * diagonal / 2,
153
- bounds.center.y + Math.sin(angle) * diagonal / 2
154
- ),
155
- })
156
- } else {
157
- // 水平渐变
158
- textItem.fillColor = new paper.Color({
159
- gradient: { stops: colors },
160
- origin: bounds.topLeft,
161
- destination: bounds.topRight,
162
- })
163
- }
164
- }
165
-
166
- // 背景色 - 创建背景矩形
167
- let bgRect = null
168
- if (backgroundColor) {
169
- const padding = 10
170
- bgRect = new paper.Path.Rectangle({
171
- point: [textItem.bounds.left - padding, textItem.bounds.top - padding],
172
- size: [textItem.bounds.width + padding * 2, textItem.bounds.height + padding * 2],
173
- fillColor: new paper.Color(backgroundColor),
174
- radius: 4,
175
- })
176
- bgRect.sendToBack()
177
- }
178
-
179
- // 描边
180
- if (strokeColor) {
181
- textItem.strokeColor = new paper.Color(strokeColor)
182
- textItem.strokeWidth = strokeWidth
183
- }
184
-
185
- // 阴影
186
- if (shadow) {
187
- textItem.shadowColor = new paper.Color(shadow.color || '#000000')
188
- textItem.shadowBlur = shadow.blur || 5
189
- textItem.shadowOffset = new paper.Point(shadow.offsetX || 2, shadow.offsetY || 2)
190
- }
191
-
192
- // 透明度
193
- if (opacity !== 1) {
194
- textItem.opacity = opacity
195
- if (bgRect) bgRect.opacity = opacity
196
- }
197
-
198
- // 旋转
199
- if (rotation !== 0) {
200
- textItem.rotate(rotation, new paper.Point(x, y + fontSize))
201
- if (bgRect) bgRect.rotate(rotation, bgRect.bounds.center)
202
- }
203
-
204
- // 缩放
205
- if (scale !== undefined) {
206
- if (typeof scale === 'number') {
207
- textItem.scale(scale, scale, new paper.Point(x, y + fontSize))
208
- if (bgRect) bgRect.scale(scale, scale, bgRect.bounds.center)
209
- } else if (typeof scale === 'object' && (scale.x !== undefined || scale.y !== undefined)) {
210
- const sx = scale.x || 1
211
- const sy = scale.y || sx
212
- textItem.scale(sx, sy, new paper.Point(x, y + fontSize))
213
- if (bgRect) bgRect.scale(sx, sy, bgRect.bounds.center)
214
- }
215
- }
216
-
217
- return {
218
- success: true,
219
- id: textItem.id,
220
- type: 'richText',
221
- bounds: {
222
- x: textItem.bounds.x,
223
- y: textItem.bounds.y,
224
- width: textItem.bounds.width,
225
- height: textItem.bounds.height,
226
- }
227
- }
228
- }
229
-
230
- module.exports = addRichText
@@ -1,108 +0,0 @@
1
- /**
2
- * SVG 元素
3
- */
4
-
5
- const paper = require('paper')
6
- const fs = require('fs')
7
- const path = require('path')
8
-
9
- /**
10
- * 添加 SVG
11
- */
12
- async function addSVG(project, args) {
13
- const { src, x = 0, y = 0, width, height, opacity = 1 } = args
14
-
15
- // 确保 src 是字符串
16
- if (typeof src !== 'string') {
17
- return { success: false, error: 'SVG source must be a string' }
18
- }
19
-
20
- if (!src) {
21
- return { success: false, error: 'SVG source is required' }
22
- }
23
-
24
- let svgContent = src
25
-
26
- // 如果是文件路径,读取文件内容
27
- if (!src.startsWith('<') && !src.startsWith('<?xml')) {
28
- try {
29
- let filePath = src
30
- if (!path.isAbsolute(filePath)) {
31
- filePath = path.join(process.cwd(), filePath)
32
- }
33
- svgContent = fs.readFileSync(filePath, 'utf8')
34
- } catch (e) {
35
- return { success: false, error: `Failed to read SVG file: ${e.message}` }
36
- }
37
- }
38
-
39
- // 导入 SVG 到指定项目
40
- let svg
41
- try {
42
- svg = project.importSVG(svgContent)
43
- } catch (e) {
44
- return { success: false, error: `Failed to import SVG: ${e.message}` }
45
- }
46
-
47
- if (!svg) {
48
- return { success: false, error: 'Failed to import SVG' }
49
- }
50
-
51
- // 确保 SVG 添加到活动层
52
- if (project && project.activeLayer && svg.parent !== project.activeLayer) {
53
- project.activeLayer.addChild(svg)
54
- }
55
-
56
- // 设置位置
57
- svg.position = new paper.Point(x, y)
58
-
59
- // 设置尺寸
60
- if (width && height) {
61
- const scaleX = width / svg.bounds.width
62
- const scaleY = height / svg.bounds.height
63
- svg.scale(Math.min(scaleX, scaleY), svg.bounds.center)
64
- } else if (width) {
65
- svg.scale(width / svg.bounds.width, svg.bounds.center)
66
- } else if (height) {
67
- svg.scale(height / svg.bounds.height, svg.bounds.center)
68
- }
69
-
70
- // 设置透明度
71
- if (opacity !== undefined) {
72
- svg.opacity = opacity
73
- }
74
-
75
- return {
76
- success: true,
77
- id: svg.id,
78
- type: 'svg',
79
- width: svg.bounds.width,
80
- height: svg.bounds.height,
81
- }
82
- }
83
-
84
- /**
85
- * 创建 SVG 导出
86
- *
87
- * @param {Object} project - Paper.js 项目
88
- * @param {string} filename - 文件名
89
- * @param {string} outputDir - 输出目录
90
- */
91
- function exportSVG(project, filename, outputDir = '.') {
92
- const svg = project.exportSVG({
93
- asString: true,
94
- bounds: 'content',
95
- })
96
-
97
- const filepath = require('path').join(outputDir, `${filename}.svg`)
98
- fs.writeFileSync(filepath, svg)
99
-
100
- return {
101
- success: true,
102
- filepath,
103
- filename: `${filename}.svg`,
104
- size: Buffer.byteLength(svg, 'utf8'),
105
- }
106
- }
107
-
108
- module.exports = { addSVG, exportSVG }