foliko 1.1.7 → 1.1.8
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/.shared/ui-ux-pro-max/data/charts.csv +26 -0
- package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
- package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
- package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
- package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
- package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
- package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
- package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
- package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/search.py +106 -0
- package/.agent/ARCHITECTURE.md +288 -0
- package/.agent/agents/ambient-agent.md +57 -0
- package/.agent/agents/debugger.md +55 -0
- package/.agent/agents/email-assistant.md +49 -0
- package/.agent/agents/file-manager.md +42 -0
- package/.agent/agents/python-developer.md +60 -0
- package/.agent/agents/scheduler.md +59 -0
- package/.agent/agents/web-developer.md +45 -0
- package/.agent/data/default.json +412 -3
- package/.agent/data/plugins-state.json +172 -173
- package/.agent/data/puppeteer-sessions/undefined.json +6 -0
- package/.agent/data/weixin-media/2026-04-08/img_1775618677512.jpg +0 -0
- package/.agent/data/weixin-media/2026-04-08/img_1775619073340.jpg +0 -0
- package/.agent/data/weixin-media/2026-04-08/img_1775619097536.jpg +0 -0
- package/.agent/data/weixin-media/2026-04-08/img_1775619209388.jpg +0 -0
- package/.agent/mcp_config.json +11 -3
- package/.agent/memory/feedback/mnrdvj5i-ca3dkd.md +9 -0
- package/.agent/memory/feedback/mnre365e-7s4zax.md +9 -0
- package/.agent/memory/feedback/mnre36jn-nkfgmp.md +9 -0
- package/.agent/memory/feedback/mnre3805-kjiq6h.md +9 -0
- package/.agent/memory/feedback/mnsf66kp-b10rcd.md +9 -0
- package/.agent/memory/feedback/mnsi3sz8-p5g2cw.md +9 -0
- package/.agent/memory/feedback/mnsibe47-sv2ni1.md +9 -0
- package/.agent/memory/feedback/mnsic89w-nn228o.md +9 -0
- package/.agent/memory/feedback/mnsj1xe9-x83ba0.md +9 -0
- package/.agent/memory/feedback/mnsj21iv-wnwelx.md +9 -0
- package/.agent/memory/feedback/mnsj2g4a-cog7a2.md +9 -0
- package/.agent/memory/feedback/mnsj4js7-lktjp6.md +9 -0
- package/.agent/memory/feedback/mnsj5d4y-uglwvp.md +9 -0
- package/.agent/memory/feedback/mnslkuo9-uous66.md +24 -0
- package/.agent/memory/feedback/mnsm3vq0-megoil.md +9 -0
- package/.agent/memory/feedback/mnsnn5x2-sxcihd.md +9 -0
- package/.agent/memory/feedback/mnsnq17s-nabrn9.md +9 -0
- package/.agent/memory/feedback/mnsnybet-wz7rn3.md +9 -0
- package/.agent/memory/feedback/mnsrw0s7-7s9e30.md +9 -0
- package/.agent/memory/feedback/mnu5hpnd-tlm16q.md +9 -0
- package/.agent/memory/feedback/mnu60uqe-xuoxp4.md +9 -0
- package/.agent/memory/project/mnqx54u5-loqtoe.md +9 -0
- package/.agent/memory/project/mnqx84cv-mx6dmd.md +9 -0
- package/.agent/memory/project/mnsacuyr-hgtk5n.md +20 -0
- package/.agent/memory/project/mnu5hy2x-bjsg7u.md +9 -0
- package/.agent/memory/reference/mnre3cww-penbo1.md +9 -0
- package/.agent/memory/reference/mns9wn48-luerua.md +14 -0
- package/.agent/memory/reference/mns9yz5c-thc2s0.md +16 -0
- package/.agent/memory/reference/mnsfy4um-910f1o.md +23 -0
- package/.agent/memory/reference/mnsg37dp-lmfj18.md +32 -0
- package/.agent/memory/reference/mnsll60q-0j911u.md +36 -0
- package/.agent/memory/reference/mnsmlb5y-nej31u.md +16 -0
- package/.agent/memory/reference/mnssle72-yrot96.md +9 -0
- package/.agent/memory/user/mnsfuon6-l416q1.md +21 -0
- package/.agent/memory/user/mnsg9kut-95m7rf.md +20 -0
- package/.agent/memory/user/mnu2eo1v-yy6fhe.md +9 -0
- package/.agent/memory/user/mnu2etuo-8u8jk8.md +9 -0
- package/.agent/plugins/poster-plugin/fonts/NotoColorEmoji-Regular.ttf +0 -0
- package/.agent/plugins/poster-plugin/fonts/Symbola_hint.ttf +0 -0
- package/.agent/plugins/poster-plugin/package.json +3 -3
- package/.agent/plugins/poster-plugin/src/canvas.js +8 -0
- package/.agent/plugins/poster-plugin/src/components/barcode.js +5 -2
- package/.agent/plugins/poster-plugin/src/components/bubble.js +3 -2
- package/.agent/plugins/poster-plugin/src/components/button.js +74 -30
- package/.agent/plugins/poster-plugin/src/components/columns.js +97 -83
- package/.agent/plugins/poster-plugin/src/components/grid.js +104 -92
- package/.agent/plugins/poster-plugin/src/components/highlightText.js +2 -1
- package/.agent/plugins/poster-plugin/src/components/imageFrame.js +143 -93
- package/.agent/plugins/poster-plugin/src/components/quote.js +85 -13
- package/.agent/plugins/poster-plugin/src/components/ribbon.js +13 -9
- package/.agent/plugins/poster-plugin/src/components/seal.js +5 -3
- package/.agent/plugins/poster-plugin/src/components/star.js +3 -3
- package/.agent/plugins/poster-plugin/src/components/table.js +1 -1
- package/.agent/plugins/poster-plugin/src/components/watermark.js +4 -2
- package/.agent/plugins/poster-plugin/src/composer.js +181 -13
- package/.agent/plugins/poster-plugin/src/elements/artText.js +16 -7
- package/.agent/plugins/poster-plugin/src/elements/background.js +20 -5
- package/.agent/plugins/poster-plugin/src/elements/richText.js +160 -107
- package/.agent/plugins/poster-plugin/src/elements/text.js +48 -45
- package/.agent/plugins/poster-plugin/src/fonts.js +556 -125
- package/.agent/plugins/poster-plugin/src/index.js +46 -1
- package/.agent/plugins/poster-plugin/yarn.lock +186 -356
- package/.agent/plugins/puppeteer-plugin/README.md +147 -0
- package/.agent/plugins/puppeteer-plugin/index.js +1422 -0
- package/.agent/plugins/puppeteer-plugin/package.json +9 -0
- package/.agent/plugins.json +5 -11
- package/.agent/rules/GEMINI.md +273 -0
- package/.agent/rules/allow-rule.md +77 -0
- package/.agent/rules/log-rule.md +83 -0
- package/.agent/rules/security-rule.md +93 -0
- package/.agent/scripts/auto_preview.py +148 -0
- package/.agent/scripts/checklist.py +217 -0
- package/.agent/scripts/session_manager.py +120 -0
- package/.agent/scripts/verify_all.py +327 -0
- package/.agent/sessions/cli_default.json +122 -689
- package/.agent/skills/api-patterns/SKILL.md +81 -0
- package/.agent/skills/api-patterns/api-style.md +42 -0
- package/.agent/skills/api-patterns/auth.md +24 -0
- package/.agent/skills/api-patterns/documentation.md +26 -0
- package/.agent/skills/api-patterns/graphql.md +41 -0
- package/.agent/skills/api-patterns/rate-limiting.md +31 -0
- package/.agent/skills/api-patterns/response.md +37 -0
- package/.agent/skills/api-patterns/rest.md +40 -0
- package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
- package/.agent/skills/api-patterns/security-testing.md +122 -0
- package/.agent/skills/api-patterns/trpc.md +41 -0
- package/.agent/skills/api-patterns/versioning.md +22 -0
- package/.agent/skills/app-builder/SKILL.md +75 -0
- package/.agent/skills/app-builder/agent-coordination.md +71 -0
- package/.agent/skills/app-builder/feature-building.md +53 -0
- package/.agent/skills/app-builder/project-detection.md +34 -0
- package/.agent/skills/app-builder/scaffolding.md +118 -0
- package/.agent/skills/app-builder/tech-stack.md +40 -0
- package/.agent/skills/app-builder/templates/SKILL.md +39 -0
- package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
- package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
- package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
- package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
- package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
- package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
- package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
- package/.agent/skills/architecture/SKILL.md +55 -0
- package/.agent/skills/architecture/context-discovery.md +43 -0
- package/.agent/skills/architecture/examples.md +94 -0
- package/.agent/skills/architecture/pattern-selection.md +68 -0
- package/.agent/skills/architecture/patterns-reference.md +50 -0
- package/.agent/skills/architecture/trade-off-analysis.md +77 -0
- package/.agent/skills/clean-code/SKILL.md +201 -0
- package/.agent/skills/doc.md +177 -0
- package/.agent/skills/frontend-design/SKILL.md +418 -0
- package/.agent/skills/frontend-design/animation-guide.md +331 -0
- package/.agent/skills/frontend-design/color-system.md +311 -0
- package/.agent/skills/frontend-design/decision-trees.md +418 -0
- package/.agent/skills/frontend-design/motion-graphics.md +306 -0
- package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
- package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
- package/.agent/skills/frontend-design/typography-system.md +345 -0
- package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
- package/.agent/skills/frontend-design/visual-effects.md +383 -0
- package/.agent/skills/i18n-localization/SKILL.md +154 -0
- package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
- package/.agent/skills/mcp-builder/SKILL.md +176 -0
- package/.agent/skills/poster-design/SKILL.md +385 -0
- package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
- package/.agent/workflows/brainstorm.md +113 -0
- package/.agent/workflows/create.md +59 -0
- package/.agent/workflows/debug.md +103 -0
- package/.agent/workflows/deploy.md +176 -0
- package/.agent/workflows/enhance.md +63 -0
- package/.agent/workflows/orchestrate.md +237 -0
- package/.agent/workflows/plan.md +89 -0
- package/.agent/workflows/preview.md +81 -0
- package/.agent/workflows/simple-test.md +42 -0
- package/.agent/workflows/status.md +86 -0
- package/.agent/workflows/structured-orchestrate.md +180 -0
- package/.agent/workflows/test.md +144 -0
- package/.agent/workflows/ui-ux-pro-max.md +296 -0
- package/.claude/settings.local.json +1 -2
- package/.env.example +56 -56
- package/README.md +441 -441
- package/output/foliko-poster.png +0 -0
- package/outputs/emoji-font-showcase.png +0 -0
- package/outputs/foliko-muji-style.png +0 -0
- package/package.json +3 -2
- package/plugins/default-plugins.js +2 -1
- package/plugins/extension-executor-plugin.js +24 -1
- package/plugins/feishu-plugin.js +2 -2
- package/plugins/telegram-plugin.js +2 -2
- package/plugins/weixin-plugin.js +2 -2
- package/skills/find-skills/AGENTS.md +162 -162
- package/skills/find-skills/SKILL.md +133 -133
- package/src/core/agent.js +117 -29
- package/src/executors/mcp-executor.js +282 -26
- package/system.md +719 -570
- package/.agent/agents/code-assistant.json +0 -17
- package/.agent/agents/email-assistant.json +0 -14
- package/.agent/agents/file-assistant.json +0 -18
- package/.agent/agents/orchestrator-demo.md +0 -53
- package/.agent/agents/orchestrator.json +0 -7
- package/.agent/agents/system-assistant.json +0 -15
- package/.agent/agents/web-assistant.json +0 -12
- package/.agent/data/email/processed-emails.json +0 -1
- package/.agent/data/scheduler/tasks.json +0 -1
- package/.agent/data/web/web-config.json +0 -5
- package/.agent/memory/feedback/mnt7jrlt-d67qs7.md +0 -15
- package/.agent/memory/feedback/mnt88ja3-al4fuy.md +0 -9
- package/.agent/package.json +0 -8
- package/.agent/plugins/__pycache__/file_writer.cpython-312.pyc +0 -0
- package/.agent/plugins/daytona/README.md +0 -89
- package/.agent/plugins/daytona/index.js +0 -377
- package/.agent/plugins/daytona/package.json +0 -12
- package/.agent/plugins/marknative/README.md +0 -134
- package/.agent/plugins/marknative/fonts/SegoeUI Emoji.ttf +0 -0
- package/.agent/plugins/marknative/fonts.zip +0 -0
- package/.agent/plugins/marknative/index.js +0 -256
- package/.agent/plugins/marknative/package.json +0 -12
- package/.agent/plugins/poster-plugin/emojis/rocket.png +0 -1
- 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/system-info/index.js +0 -387
- package/.agent/plugins/system-info/package.json +0 -4
- package/.agent/plugins/system-info/test.js +0 -40
- package/.agent/plugins/test-plugin.py +0 -108
- package/.agent/python-scripts/test_sample.py +0 -24
- package/.agent/skills/agent-browser/SKILL.md +0 -311
- package/.agent/skills/agent-browser/TEST_PLAN.md +0 -200
- package/.agent/skills/sysinfo/SKILL.md +0 -38
- package/.agent/skills/sysinfo/system-info.sh +0 -130
- package/.agent/skills/workflow/SKILL.md +0 -324
- package/.agent/test-agent.js +0 -35
- package/.agent/weixin.json +0 -6
- package/.agent/workflows/email-digest.json +0 -50
- package/.agent/workflows/file-backup.json +0 -21
- package/.agent/workflows/get-ip-notify.json +0 -32
- package/.agent/workflows/news-aggregator.json +0 -93
- package/.agent/workflows/news-dashboard-v2.json +0 -94
- package/.agent/workflows/notification-batch.json +0 -32
- package/output/zen_silence.png +0 -0
|
@@ -16,6 +16,7 @@ const createRibbon = require('./components/ribbon')
|
|
|
16
16
|
const createSeal = require('./components/seal')
|
|
17
17
|
const createHighlightText = require('./components/highlightText')
|
|
18
18
|
const createBarcode = require('./components/barcode')
|
|
19
|
+
const createQuote = require('./components/quote')
|
|
19
20
|
const { loadImageAsRaster } = require('./utils/imageLoader')
|
|
20
21
|
|
|
21
22
|
// 组件包装函数
|
|
@@ -46,6 +47,9 @@ async function createHighlightTextComponent(project, canvas, args) {
|
|
|
46
47
|
async function createBarcodeComponent(project, canvas, args) {
|
|
47
48
|
return await createBarcode(project, args)
|
|
48
49
|
}
|
|
50
|
+
async function createQuoteComponentWrapper(project, canvas, args) {
|
|
51
|
+
return await createQuote(project, canvas, args)
|
|
52
|
+
}
|
|
49
53
|
|
|
50
54
|
/**
|
|
51
55
|
* 辅助函数:将元素添加到活跃层
|
|
@@ -178,6 +182,8 @@ async function createComponent(project, canvas, config) {
|
|
|
178
182
|
return createTextElement(project, args)
|
|
179
183
|
case 'artText':
|
|
180
184
|
return await createArtTextElement(project, args)
|
|
185
|
+
case 'richText':
|
|
186
|
+
return await createRichTextElement(project, args)
|
|
181
187
|
case 'image':
|
|
182
188
|
return await createImageElement(project, args)
|
|
183
189
|
case 'svg':
|
|
@@ -225,7 +231,7 @@ async function createComponent(project, canvas, config) {
|
|
|
225
231
|
case 'rating':
|
|
226
232
|
return await createRatingComponent(project, canvas, args)
|
|
227
233
|
case 'quote':
|
|
228
|
-
return await
|
|
234
|
+
return await createQuoteComponentWrapper(project, canvas, args)
|
|
229
235
|
case 'statCard':
|
|
230
236
|
return await createStatCardComponent(project, canvas, args)
|
|
231
237
|
case 'tagCloud':
|
|
@@ -452,15 +458,38 @@ function createPolygonElement(project, { cx, cy, radius, sides, fill, stroke, st
|
|
|
452
458
|
}
|
|
453
459
|
|
|
454
460
|
function createTextElement(project, { text, x, y, fontSize, fontFamily, color, align, shadow }) {
|
|
455
|
-
const { validateFont,
|
|
461
|
+
const { validateFont, getDefaultFontFamily, getFontFallbackChain } = require('./fonts')
|
|
462
|
+
|
|
463
|
+
const fontSizeVal = fontSize || 48
|
|
464
|
+
const textColor = color || '#ffffff'
|
|
465
|
+
const alignment = align || 'left'
|
|
466
|
+
|
|
467
|
+
// 计算文字宽度用于居中/右对齐
|
|
468
|
+
let offsetX = 0
|
|
469
|
+
if (alignment === 'center' || alignment === 'right') {
|
|
470
|
+
// 估算文字宽度:中文约 1.0 倍字体大小,英文约 0.5 倍
|
|
471
|
+
const chineseChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length
|
|
472
|
+
const otherChars = text.length - chineseChars
|
|
473
|
+
const textWidth = chineseChars * fontSizeVal * 1.0 + otherChars * fontSizeVal * 0.5
|
|
474
|
+
|
|
475
|
+
if (alignment === 'center') {
|
|
476
|
+
offsetX = -textWidth / 2
|
|
477
|
+
} else if (alignment === 'right') {
|
|
478
|
+
offsetX = -textWidth
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// 获取字体链(支持 @napi-rs/canvas 字体回退)
|
|
483
|
+
const chain = getFontFallbackChain(fontFamily, text)
|
|
484
|
+
const finalFontFamily = chain.length === 1 ? chain[0] : chain.join(', ')
|
|
456
485
|
|
|
457
486
|
const textItem = new paper.PointText({
|
|
458
|
-
point: [x, y],
|
|
487
|
+
point: [x + offsetX, y],
|
|
459
488
|
content: text,
|
|
460
|
-
fontSize:
|
|
461
|
-
fontFamily:
|
|
462
|
-
fillColor: new paper.Color(
|
|
463
|
-
justification:
|
|
489
|
+
fontSize: fontSizeVal,
|
|
490
|
+
fontFamily: finalFontFamily,
|
|
491
|
+
fillColor: new paper.Color(textColor),
|
|
492
|
+
justification: alignment,
|
|
464
493
|
})
|
|
465
494
|
|
|
466
495
|
if (shadow) {
|
|
@@ -477,13 +506,17 @@ function createTextElement(project, { text, x, y, fontSize, fontFamily, color, a
|
|
|
477
506
|
}
|
|
478
507
|
|
|
479
508
|
function createArtTextElement(project, { text, x, y, fontSize, fontFamily, gradient, strokeColor, strokeWidth, shadow }) {
|
|
480
|
-
const {
|
|
509
|
+
const { getFontFallbackChain } = require('./fonts')
|
|
510
|
+
|
|
511
|
+
// 获取字体链
|
|
512
|
+
const chain = getFontFallbackChain(fontFamily, text)
|
|
513
|
+
const finalFont = chain.length === 1 ? chain[0] : chain.join(', ')
|
|
481
514
|
|
|
482
515
|
const textItem = new paper.PointText({
|
|
483
516
|
point: [x, y],
|
|
484
517
|
content: text,
|
|
485
518
|
fontSize: fontSize || 120,
|
|
486
|
-
fontFamily:
|
|
519
|
+
fontFamily: finalFont,
|
|
487
520
|
fillColor: gradient ? new paper.Color(gradient.colors[0]) : new paper.Color('#ffffff'),
|
|
488
521
|
justification: 'center',
|
|
489
522
|
})
|
|
@@ -529,6 +562,135 @@ async function createImageElement(project, { src, x = 0, y = 0, width, height, o
|
|
|
529
562
|
}
|
|
530
563
|
}
|
|
531
564
|
|
|
565
|
+
/**
|
|
566
|
+
* 创建富文本元素(支持多行文本和自动换行)
|
|
567
|
+
*/
|
|
568
|
+
function createRichTextElement(project, {
|
|
569
|
+
x = 0,
|
|
570
|
+
y = 0,
|
|
571
|
+
width,
|
|
572
|
+
text = '',
|
|
573
|
+
fontSize = 48,
|
|
574
|
+
fontFamily = 'sans-serif',
|
|
575
|
+
color = '#ffffff',
|
|
576
|
+
align = 'left',
|
|
577
|
+
lineHeight = fontSize * 1.4,
|
|
578
|
+
letterSpacing = 0,
|
|
579
|
+
opacity = 1,
|
|
580
|
+
rotation = 0,
|
|
581
|
+
shadow = null,
|
|
582
|
+
}) {
|
|
583
|
+
const { getFontFallbackChain } = require('./fonts')
|
|
584
|
+
|
|
585
|
+
// 获取字体链
|
|
586
|
+
const chain = getFontFallbackChain(fontFamily, text)
|
|
587
|
+
const finalFontFamily = chain.length === 1 ? chain[0] : chain.join(', ')
|
|
588
|
+
|
|
589
|
+
// 估算字符宽度(中文约等于 fontSize,英文约等于 fontSize * 0.5)
|
|
590
|
+
const getCharWidth = (char) => {
|
|
591
|
+
return /[\u4e00-\u9fa5]/.test(char) ? fontSize : fontSize * 0.5
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// 计算文本宽度
|
|
595
|
+
const calcTextWidth = (str) => {
|
|
596
|
+
let width = 0
|
|
597
|
+
for (const char of str) {
|
|
598
|
+
width += getCharWidth(char)
|
|
599
|
+
}
|
|
600
|
+
return width + (str.length - 1) * letterSpacing
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// 处理自动换行
|
|
604
|
+
const lines = []
|
|
605
|
+
if (width) {
|
|
606
|
+
const paragraphs = text.split('\n')
|
|
607
|
+
for (const paragraph of paragraphs) {
|
|
608
|
+
if (!paragraph) {
|
|
609
|
+
lines.push('')
|
|
610
|
+
continue
|
|
611
|
+
}
|
|
612
|
+
let currentLine = ''
|
|
613
|
+
let currentWidth = 0
|
|
614
|
+
for (const char of paragraph) {
|
|
615
|
+
const charWidth = getCharWidth(char) + letterSpacing
|
|
616
|
+
if (currentWidth + charWidth > width && currentLine) {
|
|
617
|
+
lines.push(currentLine)
|
|
618
|
+
currentLine = char
|
|
619
|
+
currentWidth = charWidth
|
|
620
|
+
} else {
|
|
621
|
+
currentLine += char
|
|
622
|
+
currentWidth += charWidth
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (currentLine) {
|
|
626
|
+
lines.push(currentLine)
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
} else {
|
|
630
|
+
lines.push(text)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// 创建文本元素组
|
|
634
|
+
const group = new paper.Group()
|
|
635
|
+
const textItems = []
|
|
636
|
+
|
|
637
|
+
for (let i = 0; i < lines.length; i++) {
|
|
638
|
+
const line = lines[i]
|
|
639
|
+
const lineY = y + i * lineHeight
|
|
640
|
+
|
|
641
|
+
// 计算对齐偏移
|
|
642
|
+
let offsetX = 0
|
|
643
|
+
if (align === 'center' && width) {
|
|
644
|
+
offsetX = (width - calcTextWidth(line)) / 2
|
|
645
|
+
} else if (align === 'right' && width) {
|
|
646
|
+
offsetX = width - calcTextWidth(line)
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const textItem = new paper.PointText({
|
|
650
|
+
point: [x + offsetX, lineY + fontSize],
|
|
651
|
+
content: line,
|
|
652
|
+
fontSize: fontSize,
|
|
653
|
+
fontFamily: finalFontFamily,
|
|
654
|
+
fillColor: new paper.Color(color),
|
|
655
|
+
justification: align,
|
|
656
|
+
})
|
|
657
|
+
|
|
658
|
+
if (letterSpacing !== 0) {
|
|
659
|
+
textItem.letterSpacing = letterSpacing
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (opacity !== 1) {
|
|
663
|
+
textItem.opacity = opacity
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (rotation !== 0) {
|
|
667
|
+
textItem.rotate(rotation, new paper.Point(x, y))
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (shadow) {
|
|
671
|
+
textItem.shadowColor = new paper.Color(shadow.color || 'rgba(0,0,0,0.5)')
|
|
672
|
+
textItem.shadowBlur = shadow.blur || 5
|
|
673
|
+
textItem.shadowOffset = new paper.Point(shadow.offsetX || 2, shadow.offsetY || 2)
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
textItems.push(textItem)
|
|
677
|
+
group.addChild(textItem)
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// 添加到项目
|
|
681
|
+
if (project && project.activeLayer) {
|
|
682
|
+
project.activeLayer.addChild(group)
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
return {
|
|
686
|
+
success: true,
|
|
687
|
+
id: group.id,
|
|
688
|
+
type: 'richText',
|
|
689
|
+
lines: lines.length,
|
|
690
|
+
height: lines.length * lineHeight,
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
532
694
|
const fs = require('fs')
|
|
533
695
|
|
|
534
696
|
async function createSVGElement(project, { src, x = 0, y = 0, width, height, opacity = 1 }) {
|
|
@@ -1777,15 +1939,15 @@ function createGridComponent(project, canvas, {
|
|
|
1777
1939
|
* 创建星形组件
|
|
1778
1940
|
*/
|
|
1779
1941
|
function createStarComponent(project, canvas, {
|
|
1780
|
-
cx, cy, points = 5, innerRadius
|
|
1942
|
+
cx, cy, points = 5, innerRadius, outerRadius,
|
|
1781
1943
|
fill, stroke, strokeWidth = 1, opacity = 1, rotation = 0
|
|
1782
1944
|
}) {
|
|
1783
|
-
const
|
|
1945
|
+
const actualInnerRadius = innerRadius || outerRadius * 0.4
|
|
1784
1946
|
const path = new paper.Path()
|
|
1785
1947
|
const angleStep = Math.PI / points
|
|
1786
1948
|
|
|
1787
1949
|
for (let i = 0; i < points * 2; i++) {
|
|
1788
|
-
const radius = i % 2 === 0 ? outerRadius :
|
|
1950
|
+
const radius = i % 2 === 0 ? outerRadius : actualInnerRadius
|
|
1789
1951
|
const angle = i * angleStep - Math.PI / 2 + (rotation * Math.PI / 180)
|
|
1790
1952
|
const x = cx + radius * Math.cos(angle)
|
|
1791
1953
|
const y = cy + radius * Math.sin(angle)
|
|
@@ -2042,11 +2204,17 @@ function createWatermarkComponent(project, canvas, {
|
|
|
2042
2204
|
text, cx, cy, color = 'rgba(0,0,0,0.1)', fontSize = 48,
|
|
2043
2205
|
fontFamily = 'sans-serif', opacity = 0.1, rotation = 0, align = 'center'
|
|
2044
2206
|
}) {
|
|
2207
|
+
const { getFontFallbackChain } = require('./fonts')
|
|
2208
|
+
|
|
2209
|
+
// 获取字体链
|
|
2210
|
+
const chain = getFontFallbackChain(fontFamily, text)
|
|
2211
|
+
const finalFont = chain.length === 1 ? chain[0] : chain.join(', ')
|
|
2212
|
+
|
|
2045
2213
|
const label = new paper.PointText({
|
|
2046
2214
|
point: [cx, cy],
|
|
2047
2215
|
content: text,
|
|
2048
2216
|
fontSize: fontSize,
|
|
2049
|
-
fontFamily:
|
|
2217
|
+
fontFamily: finalFont,
|
|
2050
2218
|
fillColor: new paper.Color(color),
|
|
2051
2219
|
justification: align,
|
|
2052
2220
|
opacity: opacity
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 艺术文字元素
|
|
2
|
+
* 艺术文字元素 - 使用 @napi-rs/canvas 原生字体 API
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const paper = require('paper')
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
validateFont,
|
|
8
|
+
getDefaultFontFamily,
|
|
9
|
+
getFontFallbackChain
|
|
10
|
+
} = require('../fonts')
|
|
7
11
|
|
|
8
12
|
/**
|
|
9
13
|
* 添加艺术文字
|
|
@@ -20,18 +24,23 @@ function addArtText(project, args) {
|
|
|
20
24
|
shadow,
|
|
21
25
|
} = args
|
|
22
26
|
|
|
27
|
+
// 获取字体
|
|
28
|
+
const baseFont = validateFont(fontFamily) || getDefaultFontFamily()
|
|
29
|
+
const chain = getFontFallbackChain(fontFamily || baseFont, text)
|
|
30
|
+
const font = chain.length === 1 ? chain[0] : chain.join(', ')
|
|
31
|
+
|
|
23
32
|
const textItem = new paper.PointText({
|
|
24
33
|
point: [x, y],
|
|
25
34
|
content: text,
|
|
26
35
|
fontSize: fontSize || 120,
|
|
27
|
-
fontFamily:
|
|
36
|
+
fontFamily: font,
|
|
28
37
|
fillColor: gradient
|
|
29
38
|
? new paper.Color(gradient.colors[0])
|
|
30
39
|
: new paper.Color('#ffffff'),
|
|
31
40
|
justification: 'center',
|
|
32
41
|
})
|
|
33
42
|
|
|
34
|
-
//
|
|
43
|
+
// 渐变填充
|
|
35
44
|
if (gradient && gradient.colors.length > 0) {
|
|
36
45
|
const colors = gradient.colors.map(c => new paper.Color(c))
|
|
37
46
|
textItem.fillColor = new paper.Color({
|
|
@@ -41,15 +50,15 @@ function addArtText(project, args) {
|
|
|
41
50
|
})
|
|
42
51
|
}
|
|
43
52
|
|
|
44
|
-
//
|
|
53
|
+
// 描边
|
|
45
54
|
if (strokeColor) {
|
|
46
55
|
textItem.strokeColor = new paper.Color(strokeColor)
|
|
47
56
|
textItem.strokeWidth = strokeWidth || 2
|
|
48
57
|
}
|
|
49
58
|
|
|
50
|
-
//
|
|
59
|
+
// 阴影
|
|
51
60
|
if (shadow) {
|
|
52
|
-
textItem.shadowColor = new paper.Color(shadow.color)
|
|
61
|
+
textItem.shadowColor = new paper.Color(shadow.color || '#000000')
|
|
53
62
|
textItem.shadowBlur = shadow.blur || 10
|
|
54
63
|
textItem.shadowOffset = new paper.Point(shadow.offsetX || 3, shadow.offsetY || 3)
|
|
55
64
|
}
|
|
@@ -46,6 +46,7 @@ async function addBackground(project, canvas, args) {
|
|
|
46
46
|
const { type, colors, direction } = gradient
|
|
47
47
|
const paperColors = colors.map(c => new paper.Color(c))
|
|
48
48
|
|
|
49
|
+
let gradientConfig
|
|
49
50
|
if (type === 'linear') {
|
|
50
51
|
const angle = (direction || 45) * Math.PI / 180
|
|
51
52
|
const diagonal = Math.sqrt(canvas.width ** 2 + canvas.height ** 2)
|
|
@@ -57,23 +58,37 @@ async function addBackground(project, canvas, args) {
|
|
|
57
58
|
canvas.width / 2 + Math.cos(angle) * diagonal / 2,
|
|
58
59
|
canvas.height / 2 + Math.sin(angle) * diagonal / 2
|
|
59
60
|
)
|
|
60
|
-
|
|
61
|
+
gradientConfig = {
|
|
61
62
|
gradient: { stops: paperColors },
|
|
62
63
|
origin: start,
|
|
63
64
|
destination: stop,
|
|
64
|
-
}
|
|
65
|
+
}
|
|
65
66
|
} else {
|
|
66
67
|
// radial
|
|
67
68
|
const center = new paper.Point(canvas.width / 2, canvas.height / 2)
|
|
68
69
|
const radius = Math.max(canvas.width, canvas.height) / 2
|
|
69
|
-
|
|
70
|
+
gradientConfig = {
|
|
70
71
|
gradient: { stops: paperColors },
|
|
71
72
|
origin: center,
|
|
72
73
|
destination: center.add(new paper.Point(radius, 0)),
|
|
73
|
-
}
|
|
74
|
+
}
|
|
74
75
|
}
|
|
76
|
+
|
|
77
|
+
// 创建矩形背景元素(确保导出时能正确渲染渐变)
|
|
78
|
+
const bg = new paper.Path.Rectangle({
|
|
79
|
+
point: [0, 0],
|
|
80
|
+
size: [canvas.width, canvas.height],
|
|
81
|
+
fillColor: new paper.Color(gradientConfig),
|
|
82
|
+
})
|
|
83
|
+
bg.sendToBack()
|
|
75
84
|
} else if (color) {
|
|
76
|
-
|
|
85
|
+
// 创建矩形背景元素(确保导出时能正确渲染)
|
|
86
|
+
const bg = new paper.Path.Rectangle({
|
|
87
|
+
point: [0, 0],
|
|
88
|
+
size: [canvas.width, canvas.height],
|
|
89
|
+
fillColor: new paper.Color(color),
|
|
90
|
+
})
|
|
91
|
+
bg.sendToBack()
|
|
77
92
|
} else {
|
|
78
93
|
throw new Error('Must provide color, gradient, or image')
|
|
79
94
|
}
|