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,60 @@
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
@@ -0,0 +1,52 @@
1
+ /**
2
+ * 背景元素
3
+ */
4
+
5
+ const paper = require('paper')
6
+
7
+ /**
8
+ * 添加背景
9
+ */
10
+ function addBackground(project, canvas, args) {
11
+ const { color, gradient } = args
12
+
13
+ if (gradient) {
14
+ const { type, colors, direction } = gradient
15
+ const paperColors = colors.map(c => new paper.Color(c))
16
+
17
+ if (type === 'linear') {
18
+ const angle = (direction || 45) * Math.PI / 180
19
+ const diagonal = Math.sqrt(canvas.width ** 2 + canvas.height ** 2)
20
+ const start = new paper.Point(
21
+ canvas.width / 2 - Math.cos(angle) * diagonal / 2,
22
+ canvas.height / 2 - Math.sin(angle) * diagonal / 2
23
+ )
24
+ const stop = new paper.Point(
25
+ canvas.width / 2 + Math.cos(angle) * diagonal / 2,
26
+ canvas.height / 2 + Math.sin(angle) * diagonal / 2
27
+ )
28
+ project.activeLayer.fillColor = new paper.Color({
29
+ gradient: { stops: paperColors },
30
+ origin: start,
31
+ destination: stop,
32
+ })
33
+ } else {
34
+ // radial
35
+ const center = new paper.Point(canvas.width / 2, canvas.height / 2)
36
+ const radius = Math.max(canvas.width, canvas.height) / 2
37
+ project.activeLayer.fillColor = new paper.Color({
38
+ gradient: { stops: paperColors },
39
+ origin: center,
40
+ destination: center.add(new paper.Point(radius, 0)),
41
+ })
42
+ }
43
+ } else if (color) {
44
+ project.activeLayer.fillColor = new paper.Color(color)
45
+ } else {
46
+ throw new Error('Must provide color or gradient')
47
+ }
48
+
49
+ return { success: true, message: 'Background added' }
50
+ }
51
+
52
+ module.exports = addBackground
@@ -0,0 +1,31 @@
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
@@ -0,0 +1,71 @@
1
+ /**
2
+ * 图片元素
3
+ */
4
+
5
+ const paper = require('paper')
6
+ const fs = require('fs')
7
+ const path = require('path')
8
+
9
+ /**
10
+ * 添加图片(支持本地路径、URL、Base64)
11
+ */
12
+ function addImage(project, args) {
13
+ const { src, x, y, width, height, opacity } = args
14
+
15
+ try {
16
+ // 转换为绝对路径
17
+ let absolutePath = src
18
+ if (!path.isAbsolute(absolutePath)) {
19
+ absolutePath = path.join(process.cwd(), absolutePath)
20
+ }
21
+
22
+ // 检查文件是否存在
23
+ if (!fs.existsSync(absolutePath)) {
24
+ return { success: false, error: `文件不存在: ${absolutePath}` }
25
+ }
26
+
27
+ // 读取文件并转为 Base64
28
+ const buffer = fs.readFileSync(absolutePath)
29
+ const ext = path.extname(absolutePath).toLowerCase()
30
+ const mimeTypes = {
31
+ '.png': 'image/png',
32
+ '.jpg': 'image/jpeg',
33
+ '.jpeg': 'image/jpeg',
34
+ '.gif': 'image/gif',
35
+ '.webp': 'image/webp',
36
+ '.bmp': 'image/bmp'
37
+ }
38
+ const mimeType = mimeTypes[ext] || 'image/png'
39
+ const imageUrl = `data:${mimeType};base64,${buffer.toString('base64')}`
40
+
41
+ const raster = new paper.Raster(imageUrl)
42
+
43
+ // 等待图片加载完成
44
+ raster.onLoad = () => {
45
+ if (width && height) {
46
+ raster.bounds = new paper.Rectangle(x, y, width, height)
47
+ } else if (width) {
48
+ const scale = width / raster.width
49
+ raster.bounds = new paper.Rectangle(x, y, width, raster.height * scale)
50
+ } else if (height) {
51
+ const scale = height / raster.height
52
+ raster.bounds = new paper.Rectangle(x, y, raster.width * scale, height)
53
+ } else {
54
+ raster.position = new paper.Point(x, y)
55
+ }
56
+
57
+ if (opacity !== undefined) raster.opacity = opacity
58
+ }
59
+
60
+ return {
61
+ success: true,
62
+ id: raster.id,
63
+ type: 'image',
64
+ path: absolutePath,
65
+ }
66
+ } catch (err) {
67
+ return { success: false, error: `Failed to load image: ${err.message}` }
68
+ }
69
+ }
70
+
71
+ module.exports = addImage
@@ -0,0 +1,26 @@
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 addBackground = require('./background')
13
+ const { addSVG, exportSVG } = require('./svg')
14
+
15
+ module.exports = {
16
+ addRectangle,
17
+ addCircle,
18
+ addLine,
19
+ addPolygon,
20
+ addImage,
21
+ addText,
22
+ addArtText,
23
+ addBackground,
24
+ addSVG,
25
+ exportSVG,
26
+ }
@@ -0,0 +1,23 @@
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
@@ -0,0 +1,32 @@
1
+ /**
2
+ * 多边形元素
3
+ */
4
+
5
+ const paper = require('paper')
6
+
7
+ /**
8
+ * 添加多边形
9
+ */
10
+ function addPolygon(project, args) {
11
+ const {
12
+ cx, cy, radius, sides,
13
+ fill, stroke, strokeWidth, opacity,
14
+ } = args
15
+
16
+ const polygon = new paper.Path.RegularPolygon({
17
+ center: [cx, cy],
18
+ radius: radius,
19
+ sides: sides,
20
+ })
21
+
22
+ if (fill) polygon.fillColor = new paper.Color(fill)
23
+ if (stroke) {
24
+ polygon.strokeColor = new paper.Color(stroke)
25
+ polygon.strokeWidth = strokeWidth || 1
26
+ }
27
+ if (opacity !== undefined) polygon.opacity = opacity
28
+
29
+ return { success: true, id: polygon.id, type: 'polygon' }
30
+ }
31
+
32
+ module.exports = addPolygon
@@ -0,0 +1,32 @@
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
@@ -0,0 +1,92 @@
1
+ /**
2
+ * SVG 元素
3
+ */
4
+
5
+ const paper = require('paper')
6
+ const fs = require('fs')
7
+
8
+ /**
9
+ * 添加 SVG
10
+ *
11
+ * @param {Object} project - Paper.js 项目
12
+ * @param {Object} args - 组件参数
13
+ * @param {string} args.src - SVG 文件路径或 SVG 字符串
14
+ * @param {number} args.x - X坐标
15
+ * @param {number} args.y - Y坐标
16
+ * @param {number} args.width - 宽度
17
+ * @param {number} args.height - 高度
18
+ * @param {number} args.opacity - 透明度 0-1
19
+ */
20
+ function addSVG(project, args) {
21
+ const { src, x, y, width, height, opacity } = args
22
+
23
+ let svgContent = src
24
+
25
+ // 如果是文件路径,读取文件内容
26
+ if (!src.startsWith('<') && !src.startsWith('<?xml')) {
27
+ try {
28
+ svgContent = fs.readFileSync(src, 'utf8')
29
+ } catch (e) {
30
+ return { success: false, error: `Failed to read SVG file: ${e.message}` }
31
+ }
32
+ }
33
+
34
+ // 导入 SVG
35
+ const svg = paper.project.importSVG(svgContent)
36
+
37
+ if (!svg) {
38
+ return { success: false, error: 'Failed to import SVG' }
39
+ }
40
+
41
+ // 设置位置
42
+ svg.position = new paper.Point(x, y)
43
+
44
+ // 设置尺寸
45
+ if (width && height) {
46
+ svg.bounds.width = width
47
+ svg.bounds.height = height
48
+ } else if (width) {
49
+ svg.scale(width / svg.bounds.width)
50
+ } else if (height) {
51
+ svg.scale(height / svg.bounds.height)
52
+ }
53
+
54
+ // 设置透明度
55
+ if (opacity !== undefined) {
56
+ svg.opacity = opacity
57
+ }
58
+
59
+ return {
60
+ success: true,
61
+ id: svg.id,
62
+ type: 'svg',
63
+ width: svg.bounds.width,
64
+ height: svg.bounds.height,
65
+ }
66
+ }
67
+
68
+ /**
69
+ * 创建 SVG 导出
70
+ *
71
+ * @param {Object} project - Paper.js 项目
72
+ * @param {string} filename - 文件名
73
+ * @param {string} outputDir - 输出目录
74
+ */
75
+ function exportSVG(project, filename, outputDir = '.') {
76
+ const svg = project.exportSVG({
77
+ asString: true,
78
+ bounds: 'content',
79
+ })
80
+
81
+ const filepath = require('path').join(outputDir, `${filename}.svg`)
82
+ fs.writeFileSync(filepath, svg)
83
+
84
+ return {
85
+ success: true,
86
+ filepath,
87
+ filename: `${filename}.svg`,
88
+ size: Buffer.byteLength(svg, 'utf8'),
89
+ }
90
+ }
91
+
92
+ module.exports = { addSVG, exportSVG }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 文字元素
3
+ */
4
+
5
+ const paper = require('paper')
6
+ const { validateFont, getDefaultFont } = require('../fonts')
7
+
8
+ /**
9
+ * 添加文字
10
+ */
11
+ function addText(project, args) {
12
+ const {
13
+ text,
14
+ x, y,
15
+ fontSize, fontFamily, color,
16
+ align,
17
+ shadow,
18
+ } = args
19
+
20
+ const textItem = new paper.PointText({
21
+ point: [x, y],
22
+ content: text,
23
+ fontSize: fontSize || 48,
24
+ fontFamily: validateFont(fontFamily) || getDefaultFont(),
25
+ fillColor: new paper.Color(color || '#ffffff'),
26
+ justification: align || 'left',
27
+ })
28
+
29
+ if (shadow) {
30
+ textItem.shadowColor = new paper.Color(shadow.color)
31
+ textItem.shadowBlur = shadow.blur || 5
32
+ textItem.shadowOffset = new paper.Point(shadow.offsetX || 2, shadow.offsetY || 2)
33
+ }
34
+
35
+ return { success: true, id: textItem.id, type: 'text' }
36
+ }
37
+
38
+ module.exports = addText
@@ -0,0 +1,118 @@
1
+ /**
2
+ * 字体管理模块
3
+ */
4
+
5
+ const path = require('path')
6
+ const fs = require('fs')
7
+ const { registerFont: registerFontFn } = require('canvas')
8
+
9
+ // 已注册的字体
10
+ const registeredFonts = new Map()
11
+
12
+ // 系统字体路径
13
+ const systemFonts = [
14
+ { path: 'C:\\Windows\\Fonts\\msyh.ttc', family: 'Microsoft YaHei' },
15
+ { path: 'C:\\Windows\\Fonts\\msyhbd.ttc', family: 'Microsoft YaHei Bold', weight: 'bold' },
16
+ { path: 'C:\\Windows\\Fonts\\simhei.ttf', family: 'SimHei' },
17
+ { path: 'C:\\Windows\\Fonts\\simsun.ttc', family: 'SimSun' },
18
+ { path: 'C:\\Windows\\Fonts\\Arial.ttf', family: 'Arial' },
19
+ { path: 'C:\\Windows\\Fonts\\Times New Roman.ttf', family: 'Times New Roman' },
20
+ { path: 'C:\\Windows\\Fonts\\Consolas.ttf', family: 'Consolas' },
21
+ { path: 'C:\\Windows\\Fonts\\Georgia.ttf', family: 'Georgia' },
22
+ ]
23
+
24
+ // 默认字体
25
+ let defaultFontFamily = 'sans-serif'
26
+
27
+ /**
28
+ * 注册字体文件
29
+ */
30
+ function registerFontFile(fontPath, fontFamily, options = {}) {
31
+ if (registeredFonts.has(fontFamily)) {
32
+ return true
33
+ }
34
+
35
+ try {
36
+ if (!fs.existsSync(fontPath)) {
37
+ return false
38
+ }
39
+
40
+ registerFontFn(fontPath, {
41
+ family: fontFamily,
42
+ weight: options.weight || 'normal',
43
+ style: options.style || 'normal',
44
+ })
45
+
46
+ registeredFonts.set(fontFamily, {
47
+ path: fontPath,
48
+ ...options,
49
+ })
50
+
51
+ return true
52
+ } catch (e) {
53
+ console.log(`[poster] 注册字体失败: ${fontFamily}`, e.message)
54
+ return false
55
+ }
56
+ }
57
+
58
+ /**
59
+ * 初始化字体
60
+ */
61
+ function initFonts() {
62
+ // 优先尝试注册系统字体
63
+ for (const font of systemFonts) {
64
+ if (registerFontFile(font.path, font.family, { weight: font.weight })) {
65
+ if (font.weight !== 'bold') {
66
+ defaultFontFamily = font.family
67
+ console.log(`[poster] 已注册系统字体: ${font.family}`)
68
+ break
69
+ }
70
+ }
71
+ }
72
+
73
+ // 确保有默认字体
74
+ if (!registeredFonts.has(defaultFontFamily)) {
75
+ registeredFonts.set('sans-serif', { path: null })
76
+ console.log('[poster] 使用默认字体: sans-serif')
77
+ }
78
+ }
79
+
80
+ /**
81
+ * 验证字体是否可用
82
+ */
83
+ function validateFont(fontFamily) {
84
+ if (!fontFamily) return defaultFontFamily
85
+ if (registeredFonts.has(fontFamily)) return fontFamily
86
+
87
+ const lower = fontFamily.toLowerCase()
88
+ for (const [name] of registeredFonts) {
89
+ if (name.toLowerCase() === lower) return name
90
+ }
91
+
92
+ //console.log(`[poster] 字体 "${fontFamily}" 未找到,使用默认字体: ${defaultFontFamily}`)
93
+ return defaultFontFamily
94
+ }
95
+
96
+ /**
97
+ * 获取已注册字体列表
98
+ */
99
+ function getRegisteredFonts() {
100
+ return Array.from(registeredFonts.keys())
101
+ }
102
+
103
+ /**
104
+ * 获取默认字体
105
+ */
106
+ function getDefaultFont() {
107
+ return defaultFontFamily
108
+ }
109
+
110
+ // 初始化
111
+ initFonts()
112
+
113
+ module.exports = {
114
+ registerFontFile,
115
+ validateFont,
116
+ getRegisteredFonts,
117
+ getDefaultFont,
118
+ }