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.
- package/.agent/agents/poster-expert.md +228 -0
- package/.agent/data/default.json +10 -0
- package/.agent/data/plugins-state.json +1 -1
- package/.agent/mcp_config.json +0 -6
- package/.agent/plugins/test-plugin.py +15 -0
- package/.agent/plugins/test_nested_plugin.py +85 -0
- package/.agent/sessions/cli_default.json +83 -803
- package/.agent/sessions/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.json +189 -0
- package/.claude/settings.local.json +8 -1
- package/cli/src/ui/chat-ui.js +32 -21
- package/foliko_poster.png +0 -0
- package/output/business_poster_final.png +0 -0
- package/output/emoji_test_v2.png +0 -0
- package/output/foliko-ai-launch-poster.png +0 -0
- package/output/foliko-poster.png +0 -0
- package/output/muji_style_poster.png +0 -0
- package/output/new_product_launch.png +0 -0
- package/output/news_poster_625yi_subsidy.png +0 -0
- package/output/tech_future_2026.png +0 -0
- package/output/tech_future_poster.png +0 -0
- package/package.json +2 -2
- package/plugins/default-plugins.js +60 -7
- package/plugins/extension-executor-plugin.js +32 -22
- package/plugins/feishu-plugin.js +2 -2
- package/plugins/python-plugin-loader.js +461 -40
- package/plugins/python-plugin-loader.js.bak +856 -0
- package/plugins/subagent-plugin.js +0 -121
- package/plugins/telegram-plugin.js +2 -2
- package/plugins/weixin-plugin.js +40 -31
- package/poster.png +0 -0
- package/skills/foliko-dev/SKILL.md +6 -0
- package/skills/python-plugin-dev/SKILL.md +124 -2
- package/src/core/agent-chat.js +220 -281
- package/src/core/agent.js +127 -29
- package/src/executors/mcp-executor.js +282 -26
- package/src/utils/plugin-helpers.js +22 -4
- package/system.md +362 -109
- package/test-scan.js +18 -0
- package/.agent/memory/feedback/mnt7jrlt-d67qs7.md +0 -15
- package/.agent/memory/feedback/mnt88ja3-al4fuy.md +0 -9
- package/.agent/plugins/poster-plugin/README.md +0 -304
- package/.agent/plugins/poster-plugin/emojis/rocket.png +0 -1
- package/.agent/plugins/poster-plugin/fonts/PatuaOne-Regular.ttf +0 -0
- package/.agent/plugins/poster-plugin/fonts/SegoeUI Emoji.ttf +0 -0
- package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221.ttf +0 -0
- 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
- package/.agent/plugins/poster-plugin/index.js +0 -13
- package/.agent/plugins/poster-plugin/package.json +0 -29
- package/.agent/plugins/poster-plugin/src/canvas.js +0 -224
- package/.agent/plugins/poster-plugin/src/components/arrow.js +0 -84
- package/.agent/plugins/poster-plugin/src/components/avatar.js +0 -71
- package/.agent/plugins/poster-plugin/src/components/badge.js +0 -85
- package/.agent/plugins/poster-plugin/src/components/barcode.js +0 -120
- package/.agent/plugins/poster-plugin/src/components/bubble.js +0 -153
- package/.agent/plugins/poster-plugin/src/components/button.js +0 -124
- package/.agent/plugins/poster-plugin/src/components/card.js +0 -88
- package/.agent/plugins/poster-plugin/src/components/chart.js +0 -127
- package/.agent/plugins/poster-plugin/src/components/chip.js +0 -88
- package/.agent/plugins/poster-plugin/src/components/columns.js +0 -107
- package/.agent/plugins/poster-plugin/src/components/cta.js +0 -87
- package/.agent/plugins/poster-plugin/src/components/divider.js +0 -55
- package/.agent/plugins/poster-plugin/src/components/feature.js +0 -85
- package/.agent/plugins/poster-plugin/src/components/featureGrid.js +0 -117
- package/.agent/plugins/poster-plugin/src/components/frame.js +0 -230
- package/.agent/plugins/poster-plugin/src/components/grid.js +0 -118
- package/.agent/plugins/poster-plugin/src/components/highlightText.js +0 -144
- package/.agent/plugins/poster-plugin/src/components/icon.js +0 -94
- package/.agent/plugins/poster-plugin/src/components/imageFrame.js +0 -155
- package/.agent/plugins/poster-plugin/src/components/index.js +0 -81
- package/.agent/plugins/poster-plugin/src/components/listItem.js +0 -147
- package/.agent/plugins/poster-plugin/src/components/notification.js +0 -123
- package/.agent/plugins/poster-plugin/src/components/progress.js +0 -79
- package/.agent/plugins/poster-plugin/src/components/progressCircle.js +0 -117
- package/.agent/plugins/poster-plugin/src/components/qrcode.js +0 -74
- package/.agent/plugins/poster-plugin/src/components/quote.js +0 -97
- package/.agent/plugins/poster-plugin/src/components/rating.js +0 -85
- package/.agent/plugins/poster-plugin/src/components/ribbon.js +0 -193
- package/.agent/plugins/poster-plugin/src/components/seal.js +0 -146
- package/.agent/plugins/poster-plugin/src/components/star.js +0 -70
- package/.agent/plugins/poster-plugin/src/components/statCard.js +0 -105
- package/.agent/plugins/poster-plugin/src/components/stepper.js +0 -118
- package/.agent/plugins/poster-plugin/src/components/table.js +0 -167
- package/.agent/plugins/poster-plugin/src/components/tagCloud.js +0 -85
- package/.agent/plugins/poster-plugin/src/components/timeline.js +0 -117
- package/.agent/plugins/poster-plugin/src/components/watermark.js +0 -52
- package/.agent/plugins/poster-plugin/src/composer.js +0 -2146
- package/.agent/plugins/poster-plugin/src/elements/artText.js +0 -60
- package/.agent/plugins/poster-plugin/src/elements/background.js +0 -84
- package/.agent/plugins/poster-plugin/src/elements/circle.js +0 -31
- package/.agent/plugins/poster-plugin/src/elements/image.js +0 -28
- package/.agent/plugins/poster-plugin/src/elements/index.js +0 -28
- package/.agent/plugins/poster-plugin/src/elements/line.js +0 -23
- package/.agent/plugins/poster-plugin/src/elements/polygon.js +0 -63
- package/.agent/plugins/poster-plugin/src/elements/rectangle.js +0 -32
- package/.agent/plugins/poster-plugin/src/elements/richText.js +0 -230
- package/.agent/plugins/poster-plugin/src/elements/svg.js +0 -108
- package/.agent/plugins/poster-plugin/src/elements/text.js +0 -109
- package/.agent/plugins/poster-plugin/src/fonts.js +0 -243
- package/.agent/plugins/poster-plugin/src/index.js +0 -2081
- package/.agent/plugins/poster-plugin/src/presets.js +0 -36
- package/.agent/plugins/poster-plugin/src/templates/business.js +0 -60
- package/.agent/plugins/poster-plugin/src/templates/gradient.js +0 -64
- package/.agent/plugins/poster-plugin/src/templates/index.js +0 -43
- package/.agent/plugins/poster-plugin/src/templates/modern.js +0 -69
- package/.agent/plugins/poster-plugin/src/templates/simple.js +0 -58
- package/.agent/plugins/poster-plugin/src/templates/social.js +0 -62
- package/.agent/plugins/poster-plugin/src/templates/tech.js +0 -84
- package/.agent/plugins/poster-plugin/src/utils/imageLoader.js +0 -84
- package/.agent/plugins/poster-plugin/test-background.svg +0 -1
- package/.agent/plugins/poster-plugin/test-full-poster.svg +0 -2
- package/.agent/plugins/poster-plugin/test-image.png +0 -0
- package/.agent/plugins/poster-plugin/yarn.lock +0 -1007
- package/output/zen_silence.png +0 -0
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 预设尺寸配置
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const PRESETS = {
|
|
6
|
-
// 海报
|
|
7
|
-
poster_a4: { width: 2480, height: 3508, name: 'A4海报 (300dpi)' },
|
|
8
|
-
poster_square: { width: 2000, height: 2000, name: '方形海报' },
|
|
9
|
-
poster_16_9: { width: 1920, height: 1080, name: '16:9海报' },
|
|
10
|
-
poster_4_3: { width: 1600, height: 1200, name: '4:3海报' },
|
|
11
|
-
poster_9_16: { width: 1080, height: 1920, name: '9:16竖版海报' },
|
|
12
|
-
|
|
13
|
-
// Banner
|
|
14
|
-
banner_1920x500: { width: 1920, height: 500, name: '网站Banner' },
|
|
15
|
-
banner_1200x400: { width: 1200, height: 400, name: '电商Banner' },
|
|
16
|
-
banner_750x300: { width: 750, height: 300, name: '移动端Banner' },
|
|
17
|
-
banner_468x60: { width: 468, height: 60, name: '小横幅' },
|
|
18
|
-
banner_twitter: { width: 1500, height: 500, name: 'Twitter封面' },
|
|
19
|
-
|
|
20
|
-
// 社交媒体
|
|
21
|
-
social_instagram: { width: 1080, height: 1080, name: 'Instagram正方形' },
|
|
22
|
-
social_story: { width: 1080, height: 1920, name: 'Instagram Story' },
|
|
23
|
-
social_facebook: { width: 1200, height: 630, name: 'Facebook封面' },
|
|
24
|
-
social_linkedin: { width: 1200, height: 627, name: 'LinkedIn封面' },
|
|
25
|
-
social_youtube: { width: 2560, height: 1440, name: 'YouTube封面' },
|
|
26
|
-
|
|
27
|
-
// 宣传图
|
|
28
|
-
promo_900x500: { width: 900, height: 500, name: '宣传图' },
|
|
29
|
-
promo_500x500: { width: 500, height: 500, name: '正方宣传图' },
|
|
30
|
-
promo_300x250: { width: 300, height: 250, name: '矩形广告' },
|
|
31
|
-
|
|
32
|
-
// 名片
|
|
33
|
-
card_standard: { width: 1058, height: 640, name: '标准名片 (300dpi)' },
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
module.exports = PRESETS
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Business(商务专业风格)模板
|
|
3
|
-
* 使用组件化方式实现
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { createFromConfig } = require('../composer')
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 应用商务风格模板
|
|
10
|
-
*/
|
|
11
|
-
async function applyBusinessTemplate(project, canvas, args) {
|
|
12
|
-
const { title, subtitle, background, accentColor } = args
|
|
13
|
-
const cx = canvas.width / 2
|
|
14
|
-
|
|
15
|
-
const bg = background || '#1e3a5f'
|
|
16
|
-
const accent = accentColor || '#c9a227'
|
|
17
|
-
|
|
18
|
-
const titleFontSize = Math.min(100, canvas.width / 12)
|
|
19
|
-
const subtitleFontSize = Math.min(36, canvas.width / 28)
|
|
20
|
-
|
|
21
|
-
const components = [
|
|
22
|
-
// 背景
|
|
23
|
-
{ type: 'background', color: bg },
|
|
24
|
-
|
|
25
|
-
// 顶部装饰线
|
|
26
|
-
{ type: 'line', x1: 100, y1: 150, x2: canvas.width - 100, y2: 150, stroke: accent, strokeWidth: 4 },
|
|
27
|
-
|
|
28
|
-
// 左侧装饰条
|
|
29
|
-
{ type: 'rectangle', x: 80, y: 150, width: 8, height: canvas.height - 300, fill: accent },
|
|
30
|
-
|
|
31
|
-
// 主标题
|
|
32
|
-
{
|
|
33
|
-
type: 'artText',
|
|
34
|
-
text: title,
|
|
35
|
-
x: cx,
|
|
36
|
-
y: canvas.height / 2 - 20,
|
|
37
|
-
fontSize: titleFontSize,
|
|
38
|
-
gradient: { colors: ['#ffffff', '#e0e0e0'] },
|
|
39
|
-
shadow: { color: 'rgba(0,0,0,0.3)', blur: 10 }
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
// 副标题
|
|
43
|
-
...(subtitle ? [{
|
|
44
|
-
type: 'text',
|
|
45
|
-
text: subtitle,
|
|
46
|
-
x: cx,
|
|
47
|
-
y: canvas.height / 2 + 60,
|
|
48
|
-
fontSize: subtitleFontSize,
|
|
49
|
-
color: '#cccccc',
|
|
50
|
-
align: 'center'
|
|
51
|
-
}] : []),
|
|
52
|
-
|
|
53
|
-
// 底部装饰线
|
|
54
|
-
{ type: 'line', x1: 100, y1: canvas.height - 150, x2: canvas.width - 100, y2: canvas.height - 150, stroke: accent, strokeWidth: 4 }
|
|
55
|
-
]
|
|
56
|
-
|
|
57
|
-
return await createFromConfig(project, canvas, { components })
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
module.exports = applyBusinessTemplate
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gradient(渐变风格)模板
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const { createFromConfig } = require('../composer')
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* 应用渐变风格模板
|
|
9
|
-
*/
|
|
10
|
-
async function applyGradientTemplate(project, canvas, args) {
|
|
11
|
-
const { title, subtitle, background, accentColor } = args
|
|
12
|
-
const cx = canvas.width / 2
|
|
13
|
-
const cy = canvas.height / 2
|
|
14
|
-
|
|
15
|
-
const bg = background || '#667eea'
|
|
16
|
-
const accent = accentColor || '#764ba2'
|
|
17
|
-
|
|
18
|
-
const titleFontSize = Math.min(120, canvas.width / 10)
|
|
19
|
-
|
|
20
|
-
const components = [
|
|
21
|
-
// 渐变背景
|
|
22
|
-
{
|
|
23
|
-
type: 'background',
|
|
24
|
-
gradient: {
|
|
25
|
-
type: 'radial',
|
|
26
|
-
colors: [bg, accent, '#1a1a2e']
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
// 装饰圆形
|
|
31
|
-
{ type: 'circle', cx: canvas.width * 0.2, cy: canvas.height * 0.2, rx: 100, fill: '#ffffff', opacity: 0.1 },
|
|
32
|
-
{ type: 'circle', cx: canvas.width * 0.8, cy: canvas.height * 0.7, rx: 150, fill: '#ffffff', opacity: 0.08 },
|
|
33
|
-
{ type: 'circle', cx: canvas.width * 0.5, cy: canvas.height * 0.9, rx: 200, fill: '#000000', opacity: 0.2 },
|
|
34
|
-
|
|
35
|
-
// 主标题
|
|
36
|
-
{
|
|
37
|
-
type: 'artText',
|
|
38
|
-
text: title,
|
|
39
|
-
x: cx,
|
|
40
|
-
y: cy - 20,
|
|
41
|
-
fontSize: titleFontSize,
|
|
42
|
-
gradient: { colors: ['#ffffff', '#e0e0e0'] },
|
|
43
|
-
shadow: { color: 'rgba(0,0,0,0.5)', blur: 20 }
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
// 副标题
|
|
47
|
-
...(subtitle ? [{
|
|
48
|
-
type: 'text',
|
|
49
|
-
text: subtitle,
|
|
50
|
-
x: cx,
|
|
51
|
-
y: cy + 80,
|
|
52
|
-
fontSize: 36,
|
|
53
|
-
color: '#ffffff',
|
|
54
|
-
align: 'center'
|
|
55
|
-
}] : []),
|
|
56
|
-
|
|
57
|
-
// 底部装饰线
|
|
58
|
-
{ type: 'line', x1: cx - 150, y1: cy + 150, x2: cx + 150, y2: cy + 150, stroke: '#ffffff', strokeWidth: 2, opacity: 0.5 }
|
|
59
|
-
]
|
|
60
|
-
|
|
61
|
-
return await createFromConfig(project, canvas, { components })
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
module.exports = applyGradientTemplate
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 模板模块导出
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const applyModernTemplate = require('./modern')
|
|
6
|
-
const applyBusinessTemplate = require('./business')
|
|
7
|
-
const applySocialTemplate = require('./social')
|
|
8
|
-
const applySimpleTemplate = require('./simple')
|
|
9
|
-
const applyTechTemplate = require('./tech')
|
|
10
|
-
const applyGradientTemplate = require('./gradient')
|
|
11
|
-
|
|
12
|
-
const TEMPLATES = {
|
|
13
|
-
modern: applyModernTemplate,
|
|
14
|
-
business: applyBusinessTemplate,
|
|
15
|
-
social: applySocialTemplate,
|
|
16
|
-
simple: applySimpleTemplate,
|
|
17
|
-
tech: applyTechTemplate,
|
|
18
|
-
gradient: applyGradientTemplate,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* 应用模板
|
|
23
|
-
*/
|
|
24
|
-
async function applyTemplate(project, canvas, templateName, args) {
|
|
25
|
-
const templateFn = TEMPLATES[templateName]
|
|
26
|
-
if (!templateFn) {
|
|
27
|
-
throw new Error(`Unknown template: ${templateName}. Available: ${Object.keys(TEMPLATES).join(', ')}`)
|
|
28
|
-
}
|
|
29
|
-
return await templateFn(project, canvas, args)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* 获取可用模板列表
|
|
34
|
-
*/
|
|
35
|
-
function getAvailableTemplates() {
|
|
36
|
-
return Object.keys(TEMPLATES)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
module.exports = {
|
|
40
|
-
applyTemplate,
|
|
41
|
-
getAvailableTemplates,
|
|
42
|
-
TEMPLATES,
|
|
43
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Modern(现代科技风格)模板
|
|
3
|
-
* 使用组件化方式实现
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { createFromConfig } = require('../composer')
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 应用现代风格模板
|
|
10
|
-
*/
|
|
11
|
-
async function applyModernTemplate(project, canvas, args) {
|
|
12
|
-
const { title, subtitle, background, accentColor } = args
|
|
13
|
-
const cx = canvas.width / 2
|
|
14
|
-
const cy = canvas.height / 2
|
|
15
|
-
|
|
16
|
-
const bg = background || '#1a1a2e'
|
|
17
|
-
const accent = accentColor || '#00d9ff'
|
|
18
|
-
|
|
19
|
-
// 计算装饰圆形位置
|
|
20
|
-
const circle1X = canvas.width * 0.85
|
|
21
|
-
const circle1Y = canvas.height * 0.15
|
|
22
|
-
const circle2X = canvas.width * 0.1
|
|
23
|
-
const circle2Y = canvas.height * 0.85
|
|
24
|
-
const circle3X = canvas.width * 0.9
|
|
25
|
-
const circle3Y = canvas.height * 0.8
|
|
26
|
-
|
|
27
|
-
// 计算字体大小
|
|
28
|
-
const titleFontSize = Math.min(120, canvas.width / 10)
|
|
29
|
-
const subtitleFontSize = Math.min(40, canvas.width / 25)
|
|
30
|
-
|
|
31
|
-
const components = [
|
|
32
|
-
// 背景
|
|
33
|
-
{ type: 'background', color: bg },
|
|
34
|
-
|
|
35
|
-
// 装饰圆形
|
|
36
|
-
{ type: 'circle', cx: circle1X, cy: circle1Y, rx: 200, fill: accent, opacity: 0.2 },
|
|
37
|
-
{ type: 'circle', cx: circle2X, cy: circle2Y, rx: 150, fill: accent, opacity: 0.15 },
|
|
38
|
-
{ type: 'circle', cx: circle3X, cy: circle3Y, rx: 100, fill: '#00ff88', opacity: 0.1 },
|
|
39
|
-
|
|
40
|
-
// 艺术标题
|
|
41
|
-
{
|
|
42
|
-
type: 'artText',
|
|
43
|
-
text: title,
|
|
44
|
-
x: cx,
|
|
45
|
-
y: cy - 30,
|
|
46
|
-
fontSize: titleFontSize,
|
|
47
|
-
gradient: { colors: [accent, '#00ff88'] },
|
|
48
|
-
shadow: { color: accent, blur: 20 }
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
// 副标题
|
|
52
|
-
...(subtitle ? [{
|
|
53
|
-
type: 'text',
|
|
54
|
-
text: subtitle,
|
|
55
|
-
x: cx,
|
|
56
|
-
y: cy + 80,
|
|
57
|
-
fontSize: subtitleFontSize,
|
|
58
|
-
color: '#aaaaaa',
|
|
59
|
-
align: 'center'
|
|
60
|
-
}] : []),
|
|
61
|
-
|
|
62
|
-
// 分隔线
|
|
63
|
-
{ type: 'line', x1: cx - 200, y1: cy + 50, x2: cx + 200, y2: cy + 50, stroke: accent, strokeWidth: 2 }
|
|
64
|
-
]
|
|
65
|
-
|
|
66
|
-
return await createFromConfig(project, canvas, { components })
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
module.exports = applyModernTemplate
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simple(简约留白风格)模板
|
|
3
|
-
* 使用组件化方式实现
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { createFromConfig } = require('../composer')
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 应用简约风格模板
|
|
10
|
-
*/
|
|
11
|
-
async function applySimpleTemplate(project, canvas, args) {
|
|
12
|
-
const { title, subtitle, background, accentColor } = args
|
|
13
|
-
const cx = canvas.width / 2
|
|
14
|
-
const cy = canvas.height / 2
|
|
15
|
-
|
|
16
|
-
const bg = background || '#f5f5f5'
|
|
17
|
-
const accent = accentColor || '#333333'
|
|
18
|
-
|
|
19
|
-
const titleFontSize = Math.min(100, canvas.width / 12)
|
|
20
|
-
const subtitleFontSize = Math.min(36, canvas.width / 28)
|
|
21
|
-
|
|
22
|
-
const components = [
|
|
23
|
-
// 背景
|
|
24
|
-
{ type: 'background', color: bg },
|
|
25
|
-
|
|
26
|
-
// 装饰线条
|
|
27
|
-
{ type: 'line', x1: cx - 100, y1: cy - 80, x2: cx + 100, y2: cy - 80, stroke: accent, strokeWidth: 1 },
|
|
28
|
-
|
|
29
|
-
// 主标题
|
|
30
|
-
{
|
|
31
|
-
type: 'artText',
|
|
32
|
-
text: title,
|
|
33
|
-
x: cx,
|
|
34
|
-
y: cy - 20,
|
|
35
|
-
fontSize: titleFontSize,
|
|
36
|
-
gradient: { colors: [accent, '#666666'] },
|
|
37
|
-
shadow: { color: 'rgba(0,0,0,0.1)', blur: 5 }
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
// 副标题
|
|
41
|
-
...(subtitle ? [{
|
|
42
|
-
type: 'text',
|
|
43
|
-
text: subtitle,
|
|
44
|
-
x: cx,
|
|
45
|
-
y: cy + 60,
|
|
46
|
-
fontSize: subtitleFontSize,
|
|
47
|
-
color: '#666666',
|
|
48
|
-
align: 'center'
|
|
49
|
-
}] : []),
|
|
50
|
-
|
|
51
|
-
// 装饰线条
|
|
52
|
-
{ type: 'line', x1: cx - 100, y1: cy + 100, x2: cx + 100, y2: cy + 100, stroke: accent, strokeWidth: 1 }
|
|
53
|
-
]
|
|
54
|
-
|
|
55
|
-
return await createFromConfig(project, canvas, { components })
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
module.exports = applySimpleTemplate
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Social(社交媒体风格)模板
|
|
3
|
-
* 使用组件化方式实现
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { createFromConfig } = require('../composer')
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 应用社交媒体风格模板
|
|
10
|
-
*/
|
|
11
|
-
async function applySocialTemplate(project, canvas, args) {
|
|
12
|
-
const { title, subtitle, background, accentColor } = args
|
|
13
|
-
const cx = canvas.width / 2
|
|
14
|
-
const cy = canvas.height / 2
|
|
15
|
-
|
|
16
|
-
const bg = background || '#2d3436'
|
|
17
|
-
const accent = accentColor || '#e84393'
|
|
18
|
-
|
|
19
|
-
const titleFontSize = Math.min(72, canvas.width / 14)
|
|
20
|
-
const subtitleFontSize = Math.min(32, canvas.width / 30)
|
|
21
|
-
const frameRadius = Math.min(250, canvas.width / 3)
|
|
22
|
-
|
|
23
|
-
const components = [
|
|
24
|
-
// 背景
|
|
25
|
-
{ type: 'background', color: bg },
|
|
26
|
-
|
|
27
|
-
// 中心圆形边框
|
|
28
|
-
{ type: 'circle', cx: cx, cy: cy - 50, rx: frameRadius, stroke: accent, strokeWidth: 6, fill: 'transparent' },
|
|
29
|
-
{ type: 'circle', cx: cx, cy: cy - 50, rx: frameRadius - 20, stroke: accent, strokeWidth: 1, opacity: 0.5, fill: 'transparent' },
|
|
30
|
-
|
|
31
|
-
// 主标题
|
|
32
|
-
{
|
|
33
|
-
type: 'artText',
|
|
34
|
-
text: title,
|
|
35
|
-
x: cx,
|
|
36
|
-
y: cy - 30,
|
|
37
|
-
fontSize: titleFontSize,
|
|
38
|
-
gradient: { colors: ['#ffffff', '#e0e0e0'] },
|
|
39
|
-
shadow: { color: accent, blur: 15 }
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
// 副标题
|
|
43
|
-
...(subtitle ? [{
|
|
44
|
-
type: 'text',
|
|
45
|
-
text: subtitle,
|
|
46
|
-
x: cx,
|
|
47
|
-
y: cy + 40,
|
|
48
|
-
fontSize: subtitleFontSize,
|
|
49
|
-
color: '#dddddd',
|
|
50
|
-
align: 'center'
|
|
51
|
-
}] : []),
|
|
52
|
-
|
|
53
|
-
// 底部装饰点
|
|
54
|
-
{ type: 'circle', cx: cx - 30, cy: cy + 120, rx: 5, fill: accent, opacity: 0.5 },
|
|
55
|
-
{ type: 'circle', cx: cx, cy: cy + 120, rx: 5, fill: accent, opacity: 0.7 },
|
|
56
|
-
{ type: 'circle', cx: cx + 30, cy: cy + 120, rx: 5, fill: accent, opacity: 0.9 }
|
|
57
|
-
]
|
|
58
|
-
|
|
59
|
-
return await createFromConfig(project, canvas, { components })
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
module.exports = applySocialTemplate
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tech(科技感)模板
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const { createFromConfig } = require('../composer')
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* 应用科技风格模板
|
|
9
|
-
*/
|
|
10
|
-
async function applyTechTemplate(project, canvas, args) {
|
|
11
|
-
const { title, subtitle, background, accentColor } = args
|
|
12
|
-
const cx = canvas.width / 2
|
|
13
|
-
const cy = canvas.height / 2
|
|
14
|
-
|
|
15
|
-
const bg = background || '#0a0a0a'
|
|
16
|
-
const accent = accentColor || '#00ff00'
|
|
17
|
-
|
|
18
|
-
const titleFontSize = Math.min(100, canvas.width / 12)
|
|
19
|
-
const subtitleFontSize = Math.min(32, canvas.width / 28)
|
|
20
|
-
|
|
21
|
-
// 计算网格位置
|
|
22
|
-
const gridItems = []
|
|
23
|
-
const cols = 5
|
|
24
|
-
const rows = 3
|
|
25
|
-
const gridWidth = canvas.width * 0.8
|
|
26
|
-
const gridHeight = canvas.height * 0.3
|
|
27
|
-
const cellWidth = gridWidth / cols
|
|
28
|
-
const cellHeight = gridHeight / rows
|
|
29
|
-
const gridX = (canvas.width - gridWidth) / 2
|
|
30
|
-
const gridY = canvas.height * 0.7
|
|
31
|
-
|
|
32
|
-
for (let i = 0; i < cols; i++) {
|
|
33
|
-
for (let j = 0; j < rows; j++) {
|
|
34
|
-
gridItems.push({
|
|
35
|
-
type: 'rectangle',
|
|
36
|
-
x: gridX + i * cellWidth,
|
|
37
|
-
y: gridY + j * cellHeight,
|
|
38
|
-
width: cellWidth - 10,
|
|
39
|
-
height: cellHeight - 10,
|
|
40
|
-
stroke: accent,
|
|
41
|
-
strokeWidth: 0.5,
|
|
42
|
-
opacity: 0.3
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const components = [
|
|
48
|
-
// 背景
|
|
49
|
-
{ type: 'background', color: bg },
|
|
50
|
-
|
|
51
|
-
// 顶部装饰线
|
|
52
|
-
{ type: 'line', x1: 0, y1: 50, x2: canvas.width * 0.3, y2: 50, stroke: accent, strokeWidth: 2 },
|
|
53
|
-
{ type: 'line', x1: canvas.width * 0.7, y1: 50, x2: canvas.width, y2: 50, stroke: accent, strokeWidth: 2 },
|
|
54
|
-
|
|
55
|
-
// 主标题(霓虹效果)
|
|
56
|
-
{
|
|
57
|
-
type: 'artText',
|
|
58
|
-
text: title,
|
|
59
|
-
x: cx,
|
|
60
|
-
y: cy,
|
|
61
|
-
fontSize: titleFontSize,
|
|
62
|
-
gradient: { colors: [accent, '#00ffff'] },
|
|
63
|
-
shadow: { color: accent, blur: 30 }
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
// 副标题
|
|
67
|
-
...(subtitle ? [{
|
|
68
|
-
type: 'text',
|
|
69
|
-
text: subtitle,
|
|
70
|
-
x: cx,
|
|
71
|
-
y: cy + 80,
|
|
72
|
-
fontSize: subtitleFontSize,
|
|
73
|
-
color: '#888888',
|
|
74
|
-
align: 'center'
|
|
75
|
-
}] : []),
|
|
76
|
-
|
|
77
|
-
// 网格装饰
|
|
78
|
-
...gridItems
|
|
79
|
-
]
|
|
80
|
-
|
|
81
|
-
return await createFromConfig(project, canvas, { components })
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
module.exports = applyTechTemplate
|
|
@@ -1,84 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
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>
|