foliko 1.0.87 → 1.1.0

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 (104) hide show
  1. package/.agent/data/default.json +3 -108
  2. package/.agent/data/plugins-state.json +34 -1
  3. package/.agent/mcp_config.json +0 -1
  4. package/.agent/memory/core.md +1 -0
  5. package/.agent/memory/project/mnn93ogy-ypjn27.md +9 -0
  6. package/.agent/memory/project/mnn98fqy-5nhc1u.md +25 -0
  7. package/.agent/memory/user/mnm67t9m-x8rekk.md +9 -0
  8. package/.agent/memory/user/mnn5mmqh-w6aktx.md +11 -0
  9. package/.agent/memory/user/mnnbfhhn-dk1bd1.md +22 -0
  10. package/.agent/plugins/__pycache__/file_writer.cpython-312.pyc +0 -0
  11. package/.agent/plugins/poster-plugin/README.md +304 -0
  12. package/.agent/plugins/poster-plugin/fonts/PatuaOne-Regular.ttf +0 -0
  13. package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221.ttf +0 -0
  14. 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
  15. package/.agent/plugins/poster-plugin/index.js +13 -0
  16. package/.agent/plugins/poster-plugin/package.json +28 -0
  17. package/.agent/plugins/poster-plugin/src/canvas.js +161 -0
  18. package/.agent/plugins/poster-plugin/src/components/arrow.js +84 -0
  19. package/.agent/plugins/poster-plugin/src/components/avatar.js +71 -0
  20. package/.agent/plugins/poster-plugin/src/components/badge.js +85 -0
  21. package/.agent/plugins/poster-plugin/src/components/card.js +88 -0
  22. package/.agent/plugins/poster-plugin/src/components/chart.js +127 -0
  23. package/.agent/plugins/poster-plugin/src/components/chip.js +88 -0
  24. package/.agent/plugins/poster-plugin/src/components/columns.js +107 -0
  25. package/.agent/plugins/poster-plugin/src/components/cta.js +85 -0
  26. package/.agent/plugins/poster-plugin/src/components/divider.js +55 -0
  27. package/.agent/plugins/poster-plugin/src/components/feature.js +85 -0
  28. package/.agent/plugins/poster-plugin/src/components/featureGrid.js +112 -0
  29. package/.agent/plugins/poster-plugin/src/components/grid.js +118 -0
  30. package/.agent/plugins/poster-plugin/src/components/imageFrame.js +155 -0
  31. package/.agent/plugins/poster-plugin/src/components/index.js +62 -0
  32. package/.agent/plugins/poster-plugin/src/components/listItem.js +146 -0
  33. package/.agent/plugins/poster-plugin/src/components/notification.js +123 -0
  34. package/.agent/plugins/poster-plugin/src/components/progress.js +79 -0
  35. package/.agent/plugins/poster-plugin/src/components/progressCircle.js +117 -0
  36. package/.agent/plugins/poster-plugin/src/components/quote.js +97 -0
  37. package/.agent/plugins/poster-plugin/src/components/rating.js +85 -0
  38. package/.agent/plugins/poster-plugin/src/components/star.js +70 -0
  39. package/.agent/plugins/poster-plugin/src/components/statCard.js +105 -0
  40. package/.agent/plugins/poster-plugin/src/components/stepper.js +118 -0
  41. package/.agent/plugins/poster-plugin/src/components/table.js +159 -0
  42. package/.agent/plugins/poster-plugin/src/components/tagCloud.js +78 -0
  43. package/.agent/plugins/poster-plugin/src/components/timeline.js +105 -0
  44. package/.agent/plugins/poster-plugin/src/components/watermark.js +52 -0
  45. package/.agent/plugins/poster-plugin/src/composer.js +1904 -0
  46. package/.agent/plugins/poster-plugin/src/elements/artText.js +60 -0
  47. package/.agent/plugins/poster-plugin/src/elements/background.js +52 -0
  48. package/.agent/plugins/poster-plugin/src/elements/circle.js +31 -0
  49. package/.agent/plugins/poster-plugin/src/elements/image.js +71 -0
  50. package/.agent/plugins/poster-plugin/src/elements/index.js +26 -0
  51. package/.agent/plugins/poster-plugin/src/elements/line.js +23 -0
  52. package/.agent/plugins/poster-plugin/src/elements/polygon.js +32 -0
  53. package/.agent/plugins/poster-plugin/src/elements/rectangle.js +32 -0
  54. package/.agent/plugins/poster-plugin/src/elements/svg.js +92 -0
  55. package/.agent/plugins/poster-plugin/src/elements/text.js +38 -0
  56. package/.agent/plugins/poster-plugin/src/fonts.js +118 -0
  57. package/.agent/plugins/poster-plugin/src/index.js +1659 -0
  58. package/.agent/plugins/poster-plugin/src/presets.js +36 -0
  59. package/.agent/plugins/poster-plugin/src/templates/business.js +60 -0
  60. package/.agent/plugins/poster-plugin/src/templates/gradient.js +64 -0
  61. package/.agent/plugins/poster-plugin/src/templates/index.js +43 -0
  62. package/.agent/plugins/poster-plugin/src/templates/modern.js +69 -0
  63. package/.agent/plugins/poster-plugin/src/templates/simple.js +58 -0
  64. package/.agent/plugins/poster-plugin/src/templates/social.js +62 -0
  65. package/.agent/plugins/poster-plugin/src/templates/tech.js +84 -0
  66. package/.agent/sessions/cli_default.json +24265 -0
  67. package/.agent/weixin.json +6 -0
  68. package/.claude/settings.local.json +5 -8
  69. package/CLAUDE.md +144 -108
  70. package/docs/CONTEXT_DESIGN.md +1596 -0
  71. package/examples/test-concurrent-chat.js +60 -60
  72. package/output/beef-love-poster.png +0 -0
  73. package/package.json +2 -2
  74. package/plugins/default-plugins.js +2 -1
  75. package/plugins/extension-executor-plugin.js +11 -0
  76. package/plugins/memory-plugin.js +984 -0
  77. package/plugins/session-plugin.js +57 -1
  78. package/plugins/weixin-plugin.js +24 -22
  79. package/skills/poster-guide/SKILL.md +743 -0
  80. package/skills/python-plugin-dev/SKILL.md +238 -238
  81. package/skills/skill-guide/SKILL.md +130 -108
  82. package/src/capabilities/skill-manager.js +99 -0
  83. package/src/core/agent-chat.js +538 -138
  84. package/src/core/agent-context.js +188 -0
  85. package/src/core/agent.js +6 -2
  86. package/src/core/context-manager.js +283 -0
  87. package/src/core/framework.js +264 -3
  88. package/src/core/plugin-manager.js +79 -2
  89. package/src/core/request-context.js +98 -0
  90. package/src/core/session-context.js +341 -0
  91. package/src/core/session-storage.js +274 -0
  92. package/src/executors/mcp-executor.js +2 -2
  93. package/src/utils/index.js +239 -67
  94. package/src/utils/plugin-helpers.js +17 -0
  95. package//346/265/267/346/212/245/346/217/222/344/273/266.md +621 -0
  96. package/.agent/plugins/__pycache__/test_plugin.cpython-312.pyc +0 -0
  97. package/.agent/plugins/temp-repo/LICENSE +0 -201
  98. package/.agent/plugins/temp-repo/puppeteer-plugin/README.md +0 -147
  99. package/.agent/plugins/temp-repo/puppeteer-plugin/index.js +0 -1418
  100. package/.agent/plugins/temp-repo/puppeteer-plugin/package.json +0 -9
  101. package/.agent/plugins/test_plugin.py +0 -304
  102. package/examples/test-chat-debug.js +0 -102
  103. package/examples/test-chat-result.js +0 -76
  104. package/examples/test-chat-stream-diff.js +0 -63
@@ -0,0 +1,161 @@
1
+ /**
2
+ * 画布管理模块
3
+ */
4
+
5
+ const paper = require('paper')
6
+ const PRESETS = require('./presets')
7
+
8
+ /**
9
+ * 画布管理器类
10
+ */
11
+ class CanvasManager {
12
+ constructor() {
13
+ this._canvas = null
14
+ this._project = null
15
+ this._paper = null
16
+ this._width = 0
17
+ this._height = 0
18
+ }
19
+
20
+ /**
21
+ * 创建画布
22
+ */
23
+ create({ preset, width, height, background }) {
24
+ let w, h
25
+
26
+ if (preset) {
27
+ const presetConfig = PRESETS[preset]
28
+ if (!presetConfig) {
29
+ throw new Error(`Unknown preset: ${preset}. Available: ${Object.keys(PRESETS).join(', ')}`)
30
+ }
31
+ w = presetConfig.width
32
+ h = presetConfig.height
33
+ } else if (width && height) {
34
+ w = width
35
+ h = height
36
+ } else {
37
+ throw new Error('Must provide preset or width/height')
38
+ }
39
+
40
+ this._canvas = paper.createCanvas(w, h)
41
+ this._project = new paper.Project(this._canvas)
42
+ // 确保活动层存在
43
+ if (!paper.project || !paper.project.activeLayer) {
44
+ paper.setup(this._canvas)
45
+ }
46
+ this._paper = paper
47
+ this._width = w
48
+ this._height = h
49
+
50
+ // 添加背景
51
+ if (background) {
52
+ const bg = new paper.Path.Rectangle({
53
+ point: [0, 0],
54
+ size: [w, h],
55
+ fillColor: background,
56
+ })
57
+ bg.sendToBack()
58
+ }
59
+
60
+ return {
61
+ width: w,
62
+ height: h,
63
+ preset: preset || 'custom',
64
+ background: background || null,
65
+ }
66
+ }
67
+
68
+ /**
69
+ * 获取画布
70
+ */
71
+ getCanvas() {
72
+ return this._canvas
73
+ }
74
+
75
+ /**
76
+ * 获取 Paper.js 项目
77
+ */
78
+ getProject() {
79
+ return this._project
80
+ }
81
+
82
+ /**
83
+ * 获取画布尺寸
84
+ */
85
+ getSize() {
86
+ return { width: this._width, height: this._height }
87
+ }
88
+
89
+ /**
90
+ * 获取中心点
91
+ */
92
+ getCenter() {
93
+ return { x: this._width / 2, y: this._height / 2 }
94
+ }
95
+
96
+ /**
97
+ * 清除画布
98
+ */
99
+ clear() {
100
+ if (this._project) {
101
+ this._project.activeLayer.removeChildren()
102
+ }
103
+ }
104
+
105
+ /**
106
+ * 获取元素数量
107
+ */
108
+ getElementCount() {
109
+ return this._project ? this._project.activeLayer.children.length : 0
110
+ }
111
+
112
+ /**
113
+ * 导出为 Buffer
114
+ */
115
+ toBuffer(format = 'png', quality) {
116
+ if (!this._canvas) {
117
+ throw new Error('No canvas created')
118
+ }
119
+
120
+ this._project.view.update()
121
+ this._project.view.draw()
122
+
123
+ const mimeType = format === 'jpg' ? 'image/jpeg' : 'image/png'
124
+ return this._canvas.toBuffer(mimeType, quality)
125
+ }
126
+
127
+ /**
128
+ * 导出为 Base64
129
+ */
130
+ toBase64(format = 'png', quality) {
131
+ if (!this._canvas) {
132
+ throw new Error('No canvas created')
133
+ }
134
+
135
+ this._project.view.update()
136
+ this._project.view.draw()
137
+
138
+ const mimeType = format === 'jpg' ? 'image/jpeg' : 'image/png'
139
+ return this._canvas.toDataURL(mimeType, quality)
140
+ }
141
+
142
+ /**
143
+ * 检查画布是否已创建
144
+ */
145
+ isCreated() {
146
+ return this._canvas !== null
147
+ }
148
+
149
+ /**
150
+ * 重置画布
151
+ */
152
+ reset() {
153
+ this._canvas = null
154
+ this._project = null
155
+ this._paper = null
156
+ this._width = 0
157
+ this._height = 0
158
+ }
159
+ }
160
+
161
+ module.exports = CanvasManager
@@ -0,0 +1,84 @@
1
+ /**
2
+ * 箭头组件
3
+ */
4
+
5
+ const paper = require('paper')
6
+
7
+ /**
8
+ * 创建箭头组件
9
+ */
10
+ function createArrow(project, canvas, params) {
11
+ const {
12
+ x1, y1, x2, y2,
13
+ color = '#333333',
14
+ strokeWidth = 2,
15
+ headSize = 12,
16
+ style = 'solid',
17
+ direction = 'end'
18
+ } = params
19
+
20
+ const elements = []
21
+
22
+ // 创建主线
23
+ const line = new paper.Path.Line({
24
+ from: [x1, y1],
25
+ to: [x2, y2]
26
+ })
27
+ line.strokeColor = new paper.Color(color)
28
+ line.strokeWidth = strokeWidth
29
+ if (style === 'dashed') {
30
+ line.dashArray = [10, 5]
31
+ }
32
+
33
+ if (project && project.activeLayer) {
34
+ project.activeLayer.addChild(line)
35
+ }
36
+ elements.push({ type: 'line', id: line.id })
37
+
38
+ // 创建箭头头部
39
+ function createArrowHead(x, y, angle) {
40
+ const path = new paper.Path()
41
+ const angle1 = angle + Math.PI * 0.8
42
+ const angle2 = angle - Math.PI * 0.8
43
+
44
+ path.moveTo(x, y)
45
+ path.lineTo(
46
+ x + headSize * Math.cos(angle1),
47
+ y + headSize * Math.sin(angle1)
48
+ )
49
+ path.moveTo(x, y)
50
+ path.lineTo(
51
+ x + headSize * Math.cos(angle2),
52
+ y + headSize * Math.sin(angle2)
53
+ )
54
+
55
+ path.strokeColor = new paper.Color(color)
56
+ path.strokeWidth = strokeWidth
57
+ path.strokeCap = 'round'
58
+
59
+ if (project && project.activeLayer) {
60
+ project.activeLayer.addChild(path)
61
+ }
62
+ return path
63
+ }
64
+
65
+ const angle = Math.atan2(y2 - y1, x2 - x1)
66
+
67
+ if (direction === 'end' || direction === 'both') {
68
+ const head = createArrowHead(x2, y2, angle)
69
+ elements.push({ type: 'path', id: head.id })
70
+ }
71
+
72
+ if (direction === 'start' || direction === 'both') {
73
+ const head = createArrowHead(x1, y1, angle + Math.PI)
74
+ elements.push({ type: 'path', id: head.id })
75
+ }
76
+
77
+ return {
78
+ success: true,
79
+ elements,
80
+ type: 'arrow'
81
+ }
82
+ }
83
+
84
+ module.exports = createArrow
@@ -0,0 +1,71 @@
1
+ /**
2
+ * 头像组件
3
+ */
4
+
5
+ const paper = require('paper')
6
+
7
+ /**
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.size - 头像大小
16
+ * @param {string} args.src - 图片路径(可选,不提供则显示首字母)
17
+ * @param {string} args.initials - 首字母(无图片时显示)
18
+ * @param {string} args.background - 背景色
19
+ * @param {string} args.border - 边框颜色
20
+ * @param {number} args.borderWidth - 边框宽度
21
+ * @param {string} args.color - 文字颜色
22
+ */
23
+ function createAvatar(project, canvas, args) {
24
+ const {
25
+ x, y,
26
+ size = 80,
27
+ src,
28
+ initials,
29
+ background = '#6366f1',
30
+ border,
31
+ borderWidth = 0,
32
+ color = '#ffffff',
33
+ } = args
34
+
35
+ const elements = []
36
+ const radius = size / 2
37
+
38
+ // 绘制圆形背景
39
+ const circle = new paper.Path.Circle({
40
+ center: [x, y],
41
+ radius: radius,
42
+ })
43
+ circle.fillColor = new paper.Color(background)
44
+
45
+ if (border) {
46
+ circle.strokeColor = new paper.Color(border)
47
+ circle.strokeWidth = borderWidth
48
+ }
49
+
50
+ elements.push({ type: 'circle', id: circle.id })
51
+
52
+ // 如果没有图片,显示首字母
53
+ if (!src && initials) {
54
+ const text = new paper.PointText({
55
+ point: [x, y + size / 6],
56
+ content: initials.charAt(0).toUpperCase(),
57
+ fontSize: size * 0.4,
58
+ fillColor: new paper.Color(color),
59
+ justification: 'center',
60
+ })
61
+ elements.push({ type: 'text', id: text.id })
62
+ }
63
+
64
+ return {
65
+ success: true,
66
+ elements,
67
+ size,
68
+ }
69
+ }
70
+
71
+ module.exports = createAvatar
@@ -0,0 +1,85 @@
1
+ /**
2
+ * 徽章/标签组件
3
+ */
4
+
5
+ const paper = require('paper')
6
+
7
+ /**
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 {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 {string} args.align - 对齐方式: left, center, right
21
+ * @param {number} args.padding - 内边距
22
+ * @param {number} args.radius - 圆角半径
23
+ */
24
+ function createBadge(project, canvas, args) {
25
+ const {
26
+ x, y,
27
+ text,
28
+ background = '#007bff',
29
+ color = '#ffffff',
30
+ border,
31
+ fontSize = 18,
32
+ align = 'center',
33
+ padding = 15,
34
+ radius = 4,
35
+ } = args
36
+
37
+ const elements = []
38
+
39
+ // 计算文字宽度(估算)
40
+ const textWidth = text.length * fontSize * 0.6
41
+ const badgeWidth = textWidth + padding * 2
42
+ const badgeHeight = fontSize + padding * 2
43
+
44
+ // 计算X位置
45
+ let badgeX = x
46
+ if (align === 'center') {
47
+ badgeX = x - badgeWidth / 2
48
+ } else if (align === 'right') {
49
+ badgeX = x - badgeWidth
50
+ }
51
+
52
+ // 绘制徽章背景
53
+ const badge = new paper.Path.Rectangle({
54
+ point: [badgeX, y],
55
+ size: [badgeWidth, badgeHeight],
56
+ radius: radius,
57
+ })
58
+ badge.fillColor = new paper.Color(background)
59
+
60
+ if (border) {
61
+ badge.strokeColor = new paper.Color(border)
62
+ badge.strokeWidth = 1
63
+ }
64
+
65
+ elements.push({ type: 'rectangle', id: badge.id })
66
+
67
+ // 绘制文字
68
+ const badgeText = new paper.PointText({
69
+ point: [badgeX + badgeWidth / 2, y + badgeHeight / 2 + fontSize / 3],
70
+ content: text,
71
+ fontSize: fontSize,
72
+ fillColor: new paper.Color(color),
73
+ justification: 'center',
74
+ })
75
+ elements.push({ type: 'text', id: badgeText.id })
76
+
77
+ return {
78
+ success: true,
79
+ elements,
80
+ width: badgeWidth,
81
+ height: badgeHeight,
82
+ }
83
+ }
84
+
85
+ module.exports = createBadge
@@ -0,0 +1,88 @@
1
+ /**
2
+ * 卡片组件
3
+ */
4
+
5
+ const paper = require('paper')
6
+
7
+ /**
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.width - 卡片宽度
16
+ * @param {number} args.height - 卡片高度
17
+ * @param {string} args.background - 背景色
18
+ * @param {string} args.border - 边框颜色
19
+ * @param {number} args.borderWidth - 边框宽度
20
+ * @param {number} args.radius - 圆角半径
21
+ * @param {string} args.title - 标题文字
22
+ * @param {number} args.titleSize - 标题字体大小
23
+ * @param {string} args.titleColor - 标题颜色
24
+ * @param {string} args.subtitle - 副标题
25
+ * @param {number} args.subtitleSize - 副标题字体大小
26
+ * @param {string} args.subtitleColor - 副标题颜色
27
+ * @param {number} args.padding - 内边距
28
+ */
29
+ function createCard(project, canvas, args) {
30
+ const {
31
+ x, y, width, height,
32
+ background, border, borderWidth, radius,
33
+ title, titleSize, titleColor,
34
+ subtitle, subtitleSize, subtitleColor,
35
+ padding = 20,
36
+ } = args
37
+
38
+ const elements = []
39
+
40
+ // 绘制卡片背景
41
+ const card = new paper.Path.Rectangle({
42
+ point: [x, y],
43
+ size: [width, height],
44
+ radius: radius || 0,
45
+ })
46
+
47
+ if (background) {
48
+ card.fillColor = new paper.Color(background)
49
+ } else {
50
+ card.fillColor = new paper.Color('#ffffff')
51
+ }
52
+
53
+ if (border) {
54
+ card.strokeColor = new paper.Color(border)
55
+ card.strokeWidth = borderWidth || 1
56
+ }
57
+
58
+ elements.push({ type: 'rectangle', id: card.id })
59
+
60
+ // 绘制标题
61
+ if (title) {
62
+ const titleText = new paper.PointText({
63
+ point: [x + padding, y + padding + (titleSize || 24)],
64
+ content: title,
65
+ fontSize: titleSize || 24,
66
+ fillColor: new paper.Color(titleColor || '#000000'),
67
+ justification: 'left',
68
+ })
69
+ elements.push({ type: 'text', id: titleText.id })
70
+ }
71
+
72
+ // 绘制副标题
73
+ if (subtitle) {
74
+ const titleHeight = title ? (titleSize || 24) + padding : padding
75
+ const subtitleText = new paper.PointText({
76
+ point: [x + padding, y + titleHeight + (subtitleSize || 16) + 10],
77
+ content: subtitle,
78
+ fontSize: subtitleSize || 16,
79
+ fillColor: new paper.Color(subtitleColor || '#666666'),
80
+ justification: 'left',
81
+ })
82
+ elements.push({ type: 'text', id: subtitleText.id })
83
+ }
84
+
85
+ return { success: true, elements }
86
+ }
87
+
88
+ module.exports = createCard
@@ -0,0 +1,127 @@
1
+ /**
2
+ * 图表组件
3
+ */
4
+
5
+ const paper = require('paper')
6
+
7
+ /**
8
+ * 创建图表组件
9
+ */
10
+ function createChart(project, canvas, params) {
11
+ const {
12
+ type = 'bar',
13
+ x,
14
+ y,
15
+ width,
16
+ height,
17
+ data = [],
18
+ barColor = '#3b82f6',
19
+ showLabels = true,
20
+ showValues = true,
21
+ barGap = 4
22
+ } = params
23
+
24
+ const elements = []
25
+
26
+ if (type === 'bar' && data.length > 0) {
27
+ const maxValue = Math.max(...data.map(d => d.value))
28
+ const barCount = data.length
29
+ const totalGap = barGap * (barCount - 1)
30
+ const barWidth = (width - totalGap) / barCount
31
+ const labelHeight = showLabels ? 24 : 0
32
+ const valueHeight = showValues ? 20 : 0
33
+ const chartHeight = height - labelHeight - valueHeight - 10
34
+
35
+ data.forEach((item, index) => {
36
+ const barHeight = (item.value / maxValue) * chartHeight
37
+ const barX = x + index * (barWidth + barGap)
38
+ const barY = y + height - labelHeight - valueHeight - barHeight - 5
39
+ const color = item.color || barColor
40
+
41
+ const bar = new paper.Path.Rectangle({
42
+ point: [barX, barY],
43
+ size: [barWidth, barHeight],
44
+ radius: [4, 4, 0, 0]
45
+ })
46
+ bar.fillColor = new paper.Color(color)
47
+ if (project && project.activeLayer) {
48
+ project.activeLayer.addChild(bar)
49
+ }
50
+ elements.push({ type: 'path', id: bar.id })
51
+
52
+ if (showValues) {
53
+ const valueText = new paper.PointText({
54
+ point: [barX + barWidth / 2, barY - 8],
55
+ content: String(item.value),
56
+ fontSize: 12,
57
+ fillColor: new paper.Color('#666666'),
58
+ justification: 'center'
59
+ })
60
+ if (project && project.activeLayer) {
61
+ project.activeLayer.addChild(valueText)
62
+ }
63
+ elements.push({ type: 'text', id: valueText.id })
64
+ }
65
+
66
+ if (showLabels) {
67
+ const labelText = new paper.PointText({
68
+ point: [barX + barWidth / 2, y + height - 8],
69
+ content: item.label || '',
70
+ fontSize: 11,
71
+ fillColor: new paper.Color('#333333'),
72
+ justification: 'center'
73
+ })
74
+ if (project && project.activeLayer) {
75
+ project.activeLayer.addChild(labelText)
76
+ }
77
+ elements.push({ type: 'text', id: labelText.id })
78
+ }
79
+ })
80
+ } else if (type === 'pie' && data.length > 0) {
81
+ const cx = x + width / 2
82
+ const cy = y + height / 2
83
+ const radius = Math.min(width, height) / 2 - 10
84
+ const total = data.reduce((sum, d) => sum + d.value, 0)
85
+ let currentAngle = -90
86
+ const colors = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#06b6d4', '#84cc16']
87
+
88
+ data.forEach((item, index) => {
89
+ const percentage = item.value / total
90
+ const endAngle = currentAngle + percentage * 360
91
+ const path = new paper.Path()
92
+ path.moveTo(cx, cy)
93
+ path.arc([cx, cy], radius, currentAngle * Math.PI / 180, endAngle * Math.PI / 180)
94
+ path.closePath()
95
+ path.fillColor = new paper.Color(item.color || colors[index % colors.length])
96
+ if (project && project.activeLayer) {
97
+ project.activeLayer.addChild(path)
98
+ }
99
+ elements.push({ type: 'path', id: path.id })
100
+
101
+ if (showLabels && percentage > 0.05) {
102
+ const midAngle = (currentAngle + endAngle) / 2
103
+ const midRad = midAngle * Math.PI / 180
104
+ const labelX = cx + radius * 0.7 * Math.cos(midRad)
105
+ const labelY = cy + radius * 0.7 * Math.sin(midRad)
106
+ const labelText = new paper.PointText({
107
+ point: [labelX, labelY + 4],
108
+ content: `${Math.round(percentage * 100)}%`,
109
+ fontSize: 11,
110
+ fillColor: new paper.Color('#ffffff'),
111
+ justification: 'center',
112
+ fontWeight: 'bold'
113
+ })
114
+ if (project && project.activeLayer) {
115
+ project.activeLayer.addChild(labelText)
116
+ }
117
+ elements.push({ type: 'text', id: labelText.id })
118
+ }
119
+
120
+ currentAngle = endAngle
121
+ })
122
+ }
123
+
124
+ return { success: true, elements, type: 'chart' }
125
+ }
126
+
127
+ module.exports = createChart
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Chip 标签组件
3
+ */
4
+
5
+ const paper = require('paper')
6
+
7
+ /**
8
+ * 创建 Chip 标签组件
9
+ */
10
+ function createChip(project, canvas, params) {
11
+ const {
12
+ x,
13
+ y,
14
+ text,
15
+ background = '#e0e0e0',
16
+ color = '#333333',
17
+ borderColor,
18
+ fontSize = 12,
19
+ padding = 12,
20
+ radius = 16,
21
+ icon
22
+ } = params
23
+
24
+ const elements = []
25
+
26
+ // 计算尺寸
27
+ const textWidth = text.length * fontSize * 0.6
28
+ const iconWidth = icon ? fontSize : 0
29
+ const totalWidth = padding * 2 + textWidth + iconWidth + 4
30
+ const height = fontSize + padding * 2
31
+ const rectX = x - totalWidth / 2
32
+ const rectY = y - height / 2
33
+
34
+ // 绘制背景
35
+ const bg = new paper.Path.Rectangle({
36
+ point: [rectX, rectY],
37
+ size: [totalWidth, height],
38
+ radius: radius
39
+ })
40
+ bg.fillColor = new paper.Color(background)
41
+ if (borderColor) {
42
+ bg.strokeColor = new paper.Color(borderColor)
43
+ bg.strokeWidth = 1
44
+ }
45
+ if (project && project.activeLayer) {
46
+ project.activeLayer.addChild(bg)
47
+ }
48
+ elements.push({ type: 'path', id: bg.id })
49
+
50
+ // 绘制图标
51
+ if (icon) {
52
+ const iconText = new paper.PointText({
53
+ point: [rectX + padding + iconWidth / 2, y + fontSize / 3],
54
+ content: icon,
55
+ fontSize: fontSize + 2,
56
+ fillColor: new paper.Color(color),
57
+ justification: 'center'
58
+ })
59
+ if (project && project.activeLayer) {
60
+ project.activeLayer.addChild(iconText)
61
+ }
62
+ elements.push({ type: 'text', id: iconText.id })
63
+ }
64
+
65
+ // 绘制文字
66
+ const textX = icon ? rectX + padding + iconWidth + 4 + textWidth / 2 : x
67
+ const label = new paper.PointText({
68
+ point: [textX, y + fontSize / 3],
69
+ content: text,
70
+ fontSize: fontSize,
71
+ fillColor: new paper.Color(color),
72
+ justification: 'center'
73
+ })
74
+ if (project && project.activeLayer) {
75
+ project.activeLayer.addChild(label)
76
+ }
77
+ elements.push({ type: 'text', id: label.id })
78
+
79
+ return {
80
+ success: true,
81
+ elements,
82
+ width: totalWidth,
83
+ height,
84
+ type: 'chip'
85
+ }
86
+ }
87
+
88
+ module.exports = createChip