infographic-gen 1.0.5 → 1.0.7

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/README.md CHANGED
@@ -14,6 +14,8 @@ Transform your ideas into beautiful, data-driven infographics in seconds using t
14
14
  - **AI-Powered Generation** — Uses OpenAI, DeepSeek, or other LLM providers via OpenAI SDK
15
15
  - **Professional Templates** — 60+ pre-designed AntV Infographic templates (lists, sequences, hierarchies, comparisons, charts, relations, etc.)
16
16
  - **Self-Correcting** — Automatically retries with error feedback if generation fails (up to 3 attempts)
17
+ - **File Context** — Read `.md`, `.docx`, `.pdf` files as context for AI-powered summarization and visualization
18
+ - **Direct DSL Rendering** — Render SVG directly from a saved DSL text file with `--from-dsl` (skip AI)
17
19
  - **DSL Export** — Optional `--dsl` flag to save raw syntax for debugging and fine-tuning
18
20
  - **Error Auto-Dump** — Automatically saves problematic DSL to `error-dump.txt` when rendering fails
19
21
  - **Persistent Config** — Save API keys and settings locally for repeat use
@@ -123,7 +125,50 @@ ig-gen "Compare React vs Vue" -o frameworks.svg --dsl frameworks.txt
123
125
  **Error Auto-Dump:**
124
126
  If rendering fails after 3 self-correction attempts, the problematic DSL is automatically saved to `error-dump.txt` for debugging.
125
127
 
126
- ### 4. Get Help
128
+ ### 4. Use Local Files as Context
129
+
130
+ You can pass a local file (`.md`, `.docx`, `.pdf`) to the AI as reference material. The AI will read the file content and use it to generate an infographic:
131
+
132
+ ```bash
133
+ # Summarize a Markdown report into a timeline infographic
134
+ ig-gen "把报告核心里程碑总结成时间轴" -f ./2024-report.md -o timeline.svg
135
+
136
+ # Visualize a Word document as a comparison chart
137
+ ig-gen "Extract key pros and cons" -f analysis.docx -o comparison.svg
138
+
139
+ # Generate infographic from PDF research paper
140
+ ig-gen "Summarize findings into a list infographic" -f paper.pdf -o findings.svg
141
+ ```
142
+
143
+ **Supported file formats:**
144
+
145
+ | Format | Library | Notes |
146
+ | ------- | ------------- | ------------------------------------- |
147
+ | `.md` | Built-in `fs` | Also supports `.txt`, `.csv`, `.json` |
148
+ | `.docx` | `mammoth` | Extracts plain text |
149
+ | `.pdf` | `pdf-parse` | Extracts text content |
150
+
151
+ > **Note:** Very large files are automatically truncated to stay within the model's context window. You can configure the max character limit via `ig-gen config set maxFileChars 50000`.
152
+
153
+ ### 5. Render from Saved DSL File
154
+
155
+ If you previously saved a DSL file (via `--dsl` or from `error-dump.txt`), you can render it directly without calling the AI:
156
+
157
+ ```bash
158
+ # Render a previously saved DSL file
159
+ ig-gen --from-dsl frameworks.txt -o frameworks.svg
160
+
161
+ # Re-render after manually editing the error dump
162
+ ig-gen --from-dsl error-dump.txt -o fixed.svg
163
+ ```
164
+
165
+ This is useful for:
166
+
167
+ - **Offline rendering** — No API key needed
168
+ - **Manual editing** — Fine-tune DSL syntax and re-render instantly
169
+ - **Debugging** — Fix problematic DSL from `error-dump.txt` and re-render
170
+
171
+ ### 6. Get Help
127
172
 
128
173
  ```bash
129
174
  ig-gen --help
@@ -205,7 +250,9 @@ infographic-gen/
205
250
  │ ├── config/index.ts # Config store (conf library)
206
251
  │ ├── utils/
207
252
  │ │ ├── logger.ts # Colored output to stderr
208
- │ │ └── spinner.ts # Ora spinner wrapper
253
+ │ │ ├── spinner.ts # Ora spinner wrapper
254
+ │ │ ├── i18n.ts # Internationalization (en/zh-CN)
255
+ │ │ └── file-reader.ts # File text extraction (md/docx/pdf)
209
256
  │ └── core/
210
257
  │ ├── prompts.ts # System prompts (60+ templates)
211
258
  │ ├── ai.ts # OpenAI integration + self-correction
@@ -248,6 +295,7 @@ Config is stored in:
248
295
  | `baseUrl` | string | `https://api.openai.com/v1` | LLM endpoint (optional) |
249
296
  | `modelName` | string | `gpt-4o` | Model to use (optional, defaults to gpt-4o) |
250
297
  | `defaultOutputDir` | string | `~/infographics` or `.` | Default output directory (optional, defaults to `.`) |
298
+ | `maxFileChars` | string | `30000` | Max characters to read from file context (optional) |
251
299
 
252
300
  Manage config via CLI:
253
301
 
package/README.zh-CN.md CHANGED
@@ -12,6 +12,8 @@
12
12
  - **AI 驱动生成** — 支持 OpenAI、DeepSeek 或其他兼容 OpenAI SDK 的 LLM 服务
13
13
  - **专业模板库** — 60+ 预设设计的 AntV Infographic 模板(列表、流程、层级、对比、图表、关系等)
14
14
  - **自动纠错** — 生成失败时自动重试并反馈错误信息(最多 3 次尝试)
15
+ - **文件上下文** — 读取 `.md`、`.docx`、`.pdf` 文件作为 AI 参考资料,实现一键总结可视化
16
+ - **DSL 直接渲染** — 通过 `--from-dsl` 从已保存的 DSL 文本文件直接渲染 SVG(跳过 AI)
15
17
  - **DSL 导出** — 可选的 `--dsl` 参数保存原始语法,用于调试和微调
16
18
  - **错误自动转储** — 渲染失败时自动保存有问题的 DSL 到 `error-dump.txt`
17
19
  - **多语言支持** — CLI 输出支持英文/中文切换(默认英文)
@@ -123,7 +125,50 @@ ig-gen "对比 React 和 Vue" -o frameworks.svg --dsl frameworks.txt
123
125
  **错误自动保存:**
124
126
  如果经过 3 次自我修正后渲染仍然失败,有问题的 DSL 会自动保存到 `error-dump.txt` 以便调试。
125
127
 
126
- ### 4. 切换界面语言
128
+ ### 4. 使用本地文件作为上下文
129
+
130
+ 可以通过 `-f` 参数传入本地文件(`.md`、`.docx`、`.pdf`),AI 会读取文件内容并以此为参考资料生成信息图:
131
+
132
+ ```bash
133
+ # 把 Markdown 报告总结成时间轴信息图
134
+ ig-gen "把报告核心里程碑总结成时间轴" -f ./2024-report.md -o timeline.svg
135
+
136
+ # 把 Word 文档可视化为对比图
137
+ ig-gen "提取主要优缺点" -f analysis.docx -o comparison.svg
138
+
139
+ # 从 PDF 研究报告生成信息图
140
+ ig-gen "将研究发现总结为列表信息图" -f paper.pdf -o findings.svg
141
+ ```
142
+
143
+ **支持的文件格式:**
144
+
145
+ | 格式 | 依赖库 | 说明 |
146
+ | ------- | ----------- | -------------------------------- |
147
+ | `.md` | 内置 `fs` | 同时支持 `.txt`、`.csv`、`.json` |
148
+ | `.docx` | `mammoth` | 提取纯文本 |
149
+ | `.pdf` | `pdf-parse` | 提取文本内容 |
150
+
151
+ > **注意:** 超大文件会自动截断以适应模型的上下文窗口。可通过 `ig-gen config set maxFileChars 50000` 配置最大字符数。
152
+
153
+ ### 5. 从 DSL 文件直接渲染
154
+
155
+ 如果你之前通过 `--dsl` 保存过 DSL 文件(或从 `error-dump.txt` 获得),可以直接渲染而不需要调用 AI:
156
+
157
+ ```bash
158
+ # 渲染之前保存的 DSL 文件
159
+ ig-gen --from-dsl frameworks.txt -o frameworks.svg
160
+
161
+ # 修改 error-dump.txt 后重新渲染
162
+ ig-gen --from-dsl error-dump.txt -o fixed.svg
163
+ ```
164
+
165
+ 适用于:
166
+
167
+ - **离线渲染** — 无需 API Key
168
+ - **手动编辑** — 微调 DSL 语法后立即重新渲染
169
+ - **调试** — 修正 `error-dump.txt` 中的问题语法并重新渲染
170
+
171
+ ### 6. 切换界面语言
127
172
 
128
173
  默认情况下,CLI 输出为英文。如需切换为中文:
129
174
 
@@ -138,7 +183,7 @@ ig-gen config set locale en
138
183
  ig-gen config get locale
139
184
  ```
140
185
 
141
- ### 5. 获取帮助
186
+ ### 7. 获取帮助
142
187
 
143
188
  ```bash
144
189
  ig-gen --help
@@ -222,7 +267,9 @@ infographic-gen/
222
267
  │ ├── config/index.ts # 配置存储(conf 库)
223
268
  │ ├── utils/
224
269
  │ │ ├── logger.ts # 彩色日志输出
225
- │ │ └── spinner.ts # 加载动画
270
+ │ │ ├── spinner.ts # 加载动画
271
+ │ │ ├── i18n.ts # 国际化(en/zh-CN)
272
+ │ │ └── file-reader.ts # 文件文本提取(md/docx/pdf)
226
273
  │ └── core/
227
274
  │ ├── prompts.ts # 系统提示词(60+ 模板)
228
275
  │ ├── ai.ts # OpenAI 集成 + 自动纠错
@@ -262,6 +309,7 @@ infographic-gen/
262
309
  | `modelName` | string | `gpt-4o` | 使用的模型(可选,默认 gpt-4o) |
263
310
  | `defaultOutputDir` | string | `~/infographics` 或 `.` | 默认输出目录(可选,默认当前目录) |
264
311
  | `locale` | string | `en` 或 `zh-CN` | CLI 界面语言(可选,默认 `en`) |
312
+ | `maxFileChars` | string | `30000` | 文件上下文最大字符数(可选) |
265
313
 
266
314
  ### 通过 CLI 管理配置
267
315
 
package/dist/index.js CHANGED
@@ -127,7 +127,8 @@ var CONFIG_KEYS = [
127
127
  "provider",
128
128
  "modelName",
129
129
  "defaultOutputDir",
130
- "locale"
130
+ "locale",
131
+ "maxFileChars"
131
132
  ];
132
133
  var CONFIG_LABELS = {
133
134
  apiKey: "API Key",
@@ -135,7 +136,8 @@ var CONFIG_LABELS = {
135
136
  provider: "Provider",
136
137
  modelName: "Model Name",
137
138
  defaultOutputDir: "Default Output Directory",
138
- locale: "Language"
139
+ locale: "Language",
140
+ maxFileChars: "Max File Content Characters"
139
141
  };
140
142
  var DEFAULTS = {
141
143
  apiKey: "",
@@ -143,7 +145,8 @@ var DEFAULTS = {
143
145
  provider: "openai",
144
146
  modelName: "gpt-4o",
145
147
  defaultOutputDir: ".",
146
- locale: "en"
148
+ locale: "en",
149
+ maxFileChars: "30000"
147
150
  };
148
151
  var store = new Conf({
149
152
  projectName: "infographic-gen",
@@ -173,7 +176,8 @@ function getAllConfig() {
173
176
  provider: getConfig("provider"),
174
177
  modelName: getConfig("modelName"),
175
178
  defaultOutputDir: getConfig("defaultOutputDir"),
176
- locale: getConfig("locale")
179
+ locale: getConfig("locale"),
180
+ maxFileChars: getConfig("maxFileChars")
177
181
  };
178
182
  }
179
183
  function isValidConfigKey(key) {
@@ -229,7 +233,7 @@ var translations = {
229
233
  // Generate command
230
234
  generateDesc: "Generate an SVG infographic from natural language (default command)",
231
235
  generatePromptArg: "Describe the infographic you want to create",
232
- generateOutputOpt: "Output SVG file path (default: infographic-output.svg in default output directory)",
236
+ generateOutputOpt: "Output SVG file path (default: infographic-YYYYMMDD-HHMMSS.svg in default output directory)",
233
237
  generateDslOpt: "Save raw DSL syntax to a text file (optional, for debugging or fine-tuning)",
234
238
  outputTarget: "Output target: {path}",
235
239
  callingAI: "Calling AI to generate infographic syntax...",
@@ -254,7 +258,21 @@ var translations = {
254
258
  // AI errors
255
259
  apiKeyNotConfigured: "API Key not configured. Please run: infographic-gen config set apiKey <YOUR_KEY>",
256
260
  llmEmptyContent: "LLM returned empty content",
257
- failedAfterRetries: "Failed to generate renderable infographic after {retries} retries. Last error: {error}"
261
+ failedAfterRetries: "Failed to generate renderable infographic after {retries} retries. Last error: {error}",
262
+ // File reader
263
+ unsupportedFileFormat: "Unsupported file format: {ext}. Currently supported: {supported}",
264
+ fileNotFound: "File not found: {path}",
265
+ fileTooLarge: "File too large ({size} MB). Maximum allowed size is 10 MB.",
266
+ parsingFile: "Parsing local file: {path}...",
267
+ fileParsed: "File parsed successfully!",
268
+ fileContentTruncated: "Warning: file content is too long and has been auto-truncated. Some information may be lost.",
269
+ generateFileOpt: "Provide a local file (.md, .txt, .pdf, .docx) as additional context",
270
+ generateFromDslOpt: "Render SVG directly from a DSL text file (skips AI generation)",
271
+ readingDslFile: "Reading DSL file: {path}...",
272
+ dslFileReadSuccess: "DSL file loaded!",
273
+ dslFileNotFound: "DSL file not found: {path}",
274
+ dslFileReadFailed: "Failed to read DSL file",
275
+ fromDslNoPromptNeeded: "Using --from-dsl mode: rendering directly from DSL file, no prompt needed."
258
276
  },
259
277
  "zh-CN": {
260
278
  // Main program
@@ -277,7 +295,7 @@ var translations = {
277
295
  // Generate command
278
296
  generateDesc: "\u6839\u636E\u81EA\u7136\u8BED\u8A00\u63CF\u8FF0\u751F\u6210 SVG \u4FE1\u606F\u56FE\uFF08\u9ED8\u8BA4\u547D\u4EE4\uFF09",
279
297
  generatePromptArg: "\u63CF\u8FF0\u4F60\u60F3\u8981\u751F\u6210\u7684\u4FE1\u606F\u56FE\u5185\u5BB9",
280
- generateOutputOpt: "\u8F93\u51FA SVG \u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4\uFF1A\u9ED8\u8BA4\u8F93\u51FA\u76EE\u5F55\u4E2D\u7684 infographic-output.svg\uFF09",
298
+ generateOutputOpt: "\u8F93\u51FA SVG \u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4\uFF1A\u9ED8\u8BA4\u8F93\u51FA\u76EE\u5F55\u4E2D\u7684 infographic-YYYYMMDD-HHMMSS.svg\uFF09",
281
299
  generateDslOpt: "\u5C06\u539F\u59CB DSL \u8BED\u6CD5\u4FDD\u5B58\u5230\u6307\u5B9A\u6587\u672C\u6587\u4EF6\uFF08\u53EF\u9009\uFF0C\u7528\u4E8E\u8C03\u8BD5\u6216\u5FAE\u8C03\uFF09",
282
300
  outputTarget: "\u76EE\u6807\u8F93\u51FA\uFF1A{path}",
283
301
  callingAI: "\u6B63\u5728\u8C03\u7528 AI \u751F\u6210\u4FE1\u606F\u56FE\u8BED\u6CD5...",
@@ -302,7 +320,21 @@ var translations = {
302
320
  // AI errors
303
321
  apiKeyNotConfigured: "\u5C1A\u672A\u914D\u7F6E API Key\u3002\u8BF7\u5148\u8FD0\u884C\uFF1Ainfographic-gen config set apiKey <YOUR_KEY>",
304
322
  llmEmptyContent: "LLM \u8FD4\u56DE\u4E86\u7A7A\u5185\u5BB9",
305
- failedAfterRetries: "\u5DF2\u91CD\u8BD5 {retries} \u6B21\u4ECD\u7136\u65E0\u6CD5\u751F\u6210\u53EF\u6E32\u67D3\u7684\u4FE1\u606F\u56FE\u3002\u6700\u540E\u4E00\u6B21\u9519\u8BEF\uFF1A{error}"
323
+ failedAfterRetries: "\u5DF2\u91CD\u8BD5 {retries} \u6B21\u4ECD\u7136\u65E0\u6CD5\u751F\u6210\u53EF\u6E32\u67D3\u7684\u4FE1\u606F\u56FE\u3002\u6700\u540E\u4E00\u6B21\u9519\u8BEF\uFF1A{error}",
324
+ // File reader
325
+ unsupportedFileFormat: "\u6682\u4E0D\u652F\u6301 {ext} \u683C\u5F0F\u7684\u6587\u4EF6\uFF0C\u76EE\u524D\u652F\u6301\uFF1A{supported}",
326
+ fileNotFound: "\u6587\u4EF6\u672A\u627E\u5230\uFF1A{path}",
327
+ fileTooLarge: "\u6587\u4EF6\u8FC7\u5927\uFF08{size} MB\uFF09\uFF0C\u6700\u5927\u5141\u8BB8 10 MB\u3002",
328
+ parsingFile: "\u6B63\u5728\u89E3\u6790\u672C\u5730\u6587\u4EF6\uFF1A{path}...",
329
+ fileParsed: "\u6587\u4EF6\u89E3\u6790\u6210\u529F\uFF01",
330
+ fileContentTruncated: "\u8B66\u544A\uFF1A\u6587\u4EF6\u5185\u5BB9\u8FC7\u957F\uFF0C\u5DF2\u81EA\u52A8\u622A\u65AD\uFF0C\u53EF\u80FD\u4F1A\u4E22\u5931\u90E8\u5206\u4FE1\u606F\u3002",
331
+ generateFileOpt: "\u63D0\u4F9B\u672C\u5730\u6587\u4EF6\uFF08.md, .txt, .pdf, .docx\uFF09\u4F5C\u4E3A\u989D\u5916\u4E0A\u4E0B\u6587",
332
+ generateFromDslOpt: "\u76F4\u63A5\u4ECE DSL \u6587\u672C\u6587\u4EF6\u6E32\u67D3 SVG\uFF08\u8DF3\u8FC7 AI \u751F\u6210\uFF09",
333
+ readingDslFile: "\u6B63\u5728\u8BFB\u53D6 DSL \u6587\u4EF6\uFF1A{path}...",
334
+ dslFileReadSuccess: "DSL \u6587\u4EF6\u52A0\u8F7D\u6210\u529F\uFF01",
335
+ dslFileNotFound: "DSL \u6587\u4EF6\u672A\u627E\u5230\uFF1A{path}",
336
+ dslFileReadFailed: "\u8BFB\u53D6 DSL \u6587\u4EF6\u5931\u8D25",
337
+ fromDslNoPromptNeeded: "\u4F7F\u7528 --from-dsl \u6A21\u5F0F\uFF1A\u76F4\u63A5\u4ECE DSL \u6587\u4EF6\u6E32\u67D3\uFF0C\u65E0\u9700\u63D0\u793A\u8BCD\u3002"
306
338
  }
307
339
  };
308
340
  function getLocale() {
@@ -377,7 +409,8 @@ function maskApiKey(key) {
377
409
 
378
410
  // src/commands/generate.ts
379
411
  init_esm_shims();
380
- import path3 from "path";
412
+ import path4 from "path";
413
+ import fs3 from "fs/promises";
381
414
 
382
415
  // src/core/ai.ts
383
416
  init_esm_shims();
@@ -439,22 +472,6 @@ theme dark
439
472
  - #F6BD16
440
473
  - #F08BB4
441
474
  \`\`\`
442
-
443
- \u2022 \u4F7F\u7528 \`theme.base.text.font-family\` \u6307\u5B9A\u5B57\u4F53\uFF0C\u5982\u624B\u5199\u98CE\u683C \`851tegakizatsu\`
444
- \u2022 \u4F7F\u7528 \`theme.stylize\` \u9009\u62E9\u5185\u7F6E\u98CE\u683C\u5E76\u4F20\u53C2\u3002\u5E38\u89C1\u98CE\u683C\uFF1A
445
- - \`rough\`\uFF1A\u624B\u7ED8\u6548\u679C
446
- - \`pattern\`\uFF1A\u56FE\u6848\u586B\u5145
447
- - \`linear-gradient\` / \`radial-gradient\`\uFF1A\u7EBF\u6027/\u5F84\u5411\u6E10\u53D8
448
-
449
- \u4F8B\u5982\uFF1A\u624B\u7ED8\u98CE\u683C\uFF08rough\uFF09
450
- \`\`\`
451
- infographic list-row-simple-horizontal-arrow
452
- theme
453
- stylize rough
454
- base
455
- text
456
- font-family 851tegakizatsu
457
- \`\`\`
458
475
  `.trim();
459
476
  var DATA_EXAMPLES = `
460
477
  ### \u6570\u636E\u8BED\u6CD5\u793A\u4F8B
@@ -701,7 +718,16 @@ ${FULL_EXAMPLE}
701
718
  2. **\u5FC5\u987B\u5C0A\u91CD\u7528\u6237\u8F93\u5165\u8BED\u8A00**\uFF1A\u5982\u679C\u7528\u6237\u4F7F\u7528\u4E2D\u6587\u63CF\u8FF0\u9700\u6C42\uFF0CDSL \u4E2D\u6240\u6709\u6587\u672C\u5185\u5BB9\uFF08title\u3001desc\u3001label \u7B49\uFF09\u4E5F\u5FC5\u987B\u4F7F\u7528\u4E2D\u6587\u3002
702
719
  3. **\u9009\u62E9\u6700\u5408\u9002\u7684\u6A21\u677F**\uFF1A\u6839\u636E\u7528\u6237\u9700\u6C42\u7684\u4FE1\u606F\u7ED3\u6784\uFF08\u5217\u8868\u3001\u6D41\u7A0B\u3001\u5BF9\u6BD4\u3001\u5C42\u7EA7\u7B49\uFF09\u9009\u62E9\u6700\u5339\u914D\u7684\u6A21\u677F\u3002
703
720
  4. **\u6570\u636E\u5B8C\u6574\u6027**\uFF1A\u786E\u4FDD\u751F\u6210\u7684\u6570\u636E\u7ED3\u6784\u5B8C\u6574\uFF0C\u5305\u542B title\u3001desc \u4EE5\u53CA\u4E0E\u6A21\u677F\u5339\u914D\u7684\u4E3B\u6570\u636E\u5B57\u6BB5\u3002
704
- 5. **\u914D\u8272\u548C\u4E3B\u9898**\uFF1A\u9664\u975E\u7528\u6237\u7279\u522B\u8981\u6C42\uFF0C\u5426\u5219\u4F7F\u7528\u4E00\u7EC4\u7F8E\u89C2\u4E14\u534F\u8C03\u7684\u9ED8\u8BA4\u914D\u8272\u3002
721
+ 5. **\u914D\u8272\u548C\u4E3B\u9898**\uFF1A\u9664\u975E\u7528\u6237\u7279\u522B\u8981\u6C42\uFF0C\u5426\u5219\u4F7F\u7528\u9ED8\u8BA4\u914D\u8272\u3002
722
+ `.trim();
723
+ var FILE_CONTEXT_PROMPT_TEMPLATE = `
724
+ \u4EE5\u4E0B\u662F\u63D0\u4F9B\u7684\u53C2\u8003\u8D44\u6599\uFF1A
725
+ <context>
726
+ {fileContent}
727
+ </context>
728
+
729
+ \u8BF7\u6839\u636E\u4E0A\u8FF0\u8D44\u6599\uFF0C\u5B8C\u6210\u4EE5\u4E0B\u7528\u6237\u9700\u6C42\uFF1A
730
+ {userPrompt}
705
731
  `.trim();
706
732
  var SELF_CORRECTION_PROMPT_TEMPLATE = `
707
733
  \u4E4B\u524D\u4F60\u751F\u6210\u7684 AntV Infographic DSL \u8BED\u6CD5\u5728\u6E32\u67D3\u65F6\u51FA\u9519\u4E86\u3002
@@ -781,16 +807,20 @@ function cleanDSL(raw) {
781
807
  }
782
808
  return cleaned;
783
809
  }
784
- async function generateInfographicDSL(userPrompt) {
810
+ async function generateInfographicDSL(userPrompt, fileContext) {
785
811
  const client = createClient();
786
812
  const { modelName } = getLLMConfig();
813
+ const finalPrompt = fileContext ? FILE_CONTEXT_PROMPT_TEMPLATE.replace(
814
+ "{fileContent}",
815
+ fileContext
816
+ ).replace("{userPrompt}", userPrompt) : userPrompt;
787
817
  const messages = [
788
818
  { role: "system", content: INFOGRAPHIC_CREATOR_SYSTEM_PROMPT },
789
- { role: "user", content: userPrompt }
819
+ { role: "user", content: finalPrompt }
790
820
  ];
791
821
  return callLLM(client, modelName, messages);
792
822
  }
793
- async function retryWithCorrection(originalSyntax, errorMessage, userPrompt, attempt) {
823
+ async function retryWithCorrection(originalSyntax, errorMessage, userPrompt, attempt, fileContext) {
794
824
  if (attempt > MAX_RETRIES) {
795
825
  throw new Error(
796
826
  t("failedAfterRetries", {
@@ -814,9 +844,13 @@ async function retryWithCorrection(originalSyntax, errorMessage, userPrompt, att
814
844
  "{error}",
815
845
  errorMessage
816
846
  ).replace("{syntax}", originalSyntax);
847
+ const originalUserPrompt = fileContext ? FILE_CONTEXT_PROMPT_TEMPLATE.replace(
848
+ "{fileContent}",
849
+ fileContext
850
+ ).replace("{userPrompt}", userPrompt) : userPrompt;
817
851
  const messages = [
818
852
  { role: "system", content: INFOGRAPHIC_CREATOR_SYSTEM_PROMPT },
819
- { role: "user", content: userPrompt },
853
+ { role: "user", content: originalUserPrompt },
820
854
  { role: "assistant", content: originalSyntax },
821
855
  { role: "user", content: correctionMessage }
822
856
  ];
@@ -828,6 +862,13 @@ init_esm_shims();
828
862
  import fs from "fs/promises";
829
863
  import path2 from "path";
830
864
  import { renderToString } from "@antv/infographic/ssr";
865
+ function generateDefaultFilename() {
866
+ const now = /* @__PURE__ */ new Date();
867
+ const pad = (n) => String(n).padStart(2, "0");
868
+ const date = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}`;
869
+ const time = `${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
870
+ return `infographic-${date}-${time}.svg`;
871
+ }
831
872
  async function renderDSLToSVG(syntax) {
832
873
  try {
833
874
  const svgString = await renderToString(syntax);
@@ -861,22 +902,121 @@ function resolveOutputPath(userOutput) {
861
902
  }
862
903
  return path2.resolve(defaultDir, withExt);
863
904
  }
864
- return path2.resolve(defaultDir, "infographic-output.svg");
905
+ return path2.resolve(defaultDir, generateDefaultFilename());
906
+ }
907
+
908
+ // src/utils/file-reader.ts
909
+ init_esm_shims();
910
+ import fs2 from "fs/promises";
911
+ import path3 from "path";
912
+ import mammoth from "mammoth";
913
+ import { PDFParse } from "pdf-parse";
914
+ var DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
915
+ var DEFAULT_MAX_FILE_CHARS = 3e4;
916
+ var SUPPORTED_EXTENSIONS = [".md", ".txt", ".json", ".csv", ".docx", ".pdf"];
917
+ function truncateText(text, maxChars) {
918
+ if (text.length <= maxChars) return text;
919
+ const truncated = text.slice(0, maxChars);
920
+ const lastNewline = truncated.lastIndexOf("\n");
921
+ return lastNewline > maxChars * 0.8 ? truncated.slice(0, lastNewline) : truncated;
922
+ }
923
+ async function extractTextFromFile(filePath) {
924
+ const resolvedPath = path3.resolve(filePath);
925
+ const ext = path3.extname(resolvedPath).toLowerCase();
926
+ if (!SUPPORTED_EXTENSIONS.includes(ext)) {
927
+ throw new Error(
928
+ t("unsupportedFileFormat", {
929
+ ext,
930
+ supported: SUPPORTED_EXTENSIONS.join(", ")
931
+ })
932
+ );
933
+ }
934
+ let stats;
935
+ try {
936
+ stats = await fs2.stat(resolvedPath);
937
+ } catch {
938
+ throw new Error(t("fileNotFound", { path: resolvedPath }));
939
+ }
940
+ if (stats.size > DEFAULT_MAX_FILE_SIZE) {
941
+ throw new Error(
942
+ t("fileTooLarge", {
943
+ size: (stats.size / 1024 / 1024).toFixed(1)
944
+ })
945
+ );
946
+ }
947
+ let rawText;
948
+ if (ext === ".md" || ext === ".txt" || ext === ".json" || ext === ".csv") {
949
+ rawText = await fs2.readFile(resolvedPath, "utf-8");
950
+ } else if (ext === ".docx") {
951
+ const buffer = await fs2.readFile(resolvedPath);
952
+ const result = await mammoth.extractRawText({ buffer });
953
+ rawText = result.value;
954
+ } else if (ext === ".pdf") {
955
+ const buffer = await fs2.readFile(resolvedPath);
956
+ const parser = new PDFParse({ data: buffer });
957
+ const result = await parser.getText();
958
+ rawText = result.text;
959
+ } else {
960
+ throw new Error(
961
+ t("unsupportedFileFormat", {
962
+ ext,
963
+ supported: SUPPORTED_EXTENSIONS.join(", ")
964
+ })
965
+ );
966
+ }
967
+ const maxChars = getMaxFileChars();
968
+ const truncated = rawText.length > maxChars;
969
+ const text = truncated ? truncateText(rawText, maxChars) : rawText;
970
+ return { text, truncated };
971
+ }
972
+ function getMaxFileChars() {
973
+ const configured = getConfig("maxFileChars");
974
+ if (configured) {
975
+ const parsed = parseInt(configured, 10);
976
+ if (!isNaN(parsed) && parsed > 0) return parsed;
977
+ }
978
+ return DEFAULT_MAX_FILE_CHARS;
865
979
  }
866
980
 
867
981
  // src/commands/generate.ts
868
982
  function registerGenerateCommand(program2) {
869
- program2.command("generate", { isDefault: true }).alias("g").description(t("generateDesc")).argument("<prompt>", t("generatePromptArg")).option("-o, --output <path>", t("generateOutputOpt")).option("--dsl <path>", t("generateDslOpt")).action(async (prompt, opts) => {
870
- await handleGenerate(prompt, opts.output, opts.dsl);
871
- });
983
+ program2.command("generate", { isDefault: true }).alias("g").description(t("generateDesc")).argument("[prompt]", t("generatePromptArg")).option("-o, --output <path>", t("generateOutputOpt")).option("--dsl <path>", t("generateDslOpt")).option("-f, --file <path>", t("generateFileOpt")).option("--from-dsl <path>", t("generateFromDslOpt")).action(
984
+ async (prompt, opts) => {
985
+ if (opts.fromDsl) {
986
+ await handleFromDsl(opts.fromDsl, opts.output);
987
+ return;
988
+ }
989
+ if (!prompt) {
990
+ error(t("generatePromptArg"));
991
+ process.exit(1);
992
+ }
993
+ await handleGenerate(prompt, opts.output, opts.dsl, opts.file);
994
+ }
995
+ );
872
996
  }
873
- async function handleGenerate(prompt, outputOption, dslOption) {
997
+ async function handleGenerate(prompt, outputOption, dslOption, fileOption) {
874
998
  const outputPath = resolveOutputPath(outputOption);
875
- info(t("outputTarget", { path: path3.resolve(outputPath) }));
999
+ let fileContext;
1000
+ if (fileOption) {
1001
+ startSpinner(t("parsingFile", { path: fileOption }));
1002
+ try {
1003
+ const { text, truncated } = await extractTextFromFile(fileOption);
1004
+ fileContext = text;
1005
+ succeedSpinner(t("fileParsed"));
1006
+ if (truncated) {
1007
+ warn(t("fileContentTruncated"));
1008
+ }
1009
+ } catch (err) {
1010
+ failSpinner(t("aiCallFailed"));
1011
+ error(err instanceof Error ? err.message : String(err));
1012
+ process.exit(1);
1013
+ }
1014
+ }
1015
+ info(t("outputTarget", { path: path4.resolve(outputPath) }));
876
1016
  startSpinner(t("callingAI"));
877
1017
  let syntax;
878
1018
  try {
879
- syntax = await generateInfographicDSL(prompt);
1019
+ syntax = await generateInfographicDSL(prompt, fileContext);
880
1020
  } catch (err) {
881
1021
  failSpinner(t("aiCallFailed"));
882
1022
  error(err instanceof Error ? err.message : String(err));
@@ -897,7 +1037,8 @@ async function handleGenerate(prompt, outputOption, dslOption) {
897
1037
  syntax,
898
1038
  lastError,
899
1039
  prompt,
900
- attempt + 1
1040
+ attempt + 1,
1041
+ fileContext
901
1042
  );
902
1043
  updateSpinner(t("reRenderingSVG"));
903
1044
  } catch (retryErr) {
@@ -917,7 +1058,7 @@ async function handleGenerate(prompt, outputOption, dslOption) {
917
1058
  const errorDumpPath = "error-dump.txt";
918
1059
  try {
919
1060
  await writeDSLFile(errorDumpPath, syntax);
920
- warn(t("dslAutoSaved", { path: path3.resolve(errorDumpPath) }));
1061
+ warn(t("dslAutoSaved", { path: path4.resolve(errorDumpPath) }));
921
1062
  dim(t("dslAutoSaveHint"));
922
1063
  } catch (writeErr) {
923
1064
  dim(t("dslForDebug"));
@@ -935,14 +1076,55 @@ async function handleGenerate(prompt, outputOption, dslOption) {
935
1076
  if (dslOption) {
936
1077
  try {
937
1078
  await writeDSLFile(dslOption, syntax);
938
- success(t("dslSaved", { path: path3.resolve(dslOption) }));
1079
+ success(t("dslSaved", { path: path4.resolve(dslOption) }));
939
1080
  } catch (err) {
940
1081
  warn(t("dslWriteFailed"));
941
1082
  warn(err instanceof Error ? err.message : String(err));
942
1083
  }
943
1084
  }
944
1085
  succeedSpinner(t("infographicGenerated"));
945
- success(t("svgSaved", { path: path3.resolve(outputPath) }));
1086
+ success(t("svgSaved", { path: path4.resolve(outputPath) }));
1087
+ dim(t("openInBrowser"));
1088
+ }
1089
+ async function handleFromDsl(dslPath, outputOption) {
1090
+ const outputPath = resolveOutputPath(outputOption);
1091
+ const resolvedDslPath = path4.resolve(dslPath);
1092
+ dim(t("fromDslNoPromptNeeded"));
1093
+ info(t("outputTarget", { path: path4.resolve(outputPath) }));
1094
+ startSpinner(t("readingDslFile", { path: dslPath }));
1095
+ let syntax;
1096
+ try {
1097
+ syntax = await fs3.readFile(resolvedDslPath, "utf-8");
1098
+ } catch {
1099
+ failSpinner(t("dslFileReadFailed"));
1100
+ error(t("dslFileNotFound", { path: resolvedDslPath }));
1101
+ process.exit(1);
1102
+ }
1103
+ syntax = syntax.trim();
1104
+ if (!syntax) {
1105
+ failSpinner(t("dslFileReadFailed"));
1106
+ error(t("llmEmptyContent"));
1107
+ process.exit(1);
1108
+ }
1109
+ succeedSpinner(t("dslFileReadSuccess"));
1110
+ startSpinner(t("renderingSVG"));
1111
+ let svgContent;
1112
+ try {
1113
+ svgContent = await renderDSLToSVG(syntax);
1114
+ } catch (err) {
1115
+ failSpinner(t("renderingFailed"));
1116
+ error(err instanceof Error ? err.message : String(err));
1117
+ process.exit(1);
1118
+ }
1119
+ try {
1120
+ await writeSVGFile(outputPath, svgContent);
1121
+ } catch (err) {
1122
+ failSpinner(t("fileWriteFailed"));
1123
+ error(err instanceof Error ? err.message : String(err));
1124
+ process.exit(1);
1125
+ }
1126
+ succeedSpinner(t("infographicGenerated"));
1127
+ success(t("svgSaved", { path: path4.resolve(outputPath) }));
946
1128
  dim(t("openInBrowser"));
947
1129
  }
948
1130
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../node_modules/tsup/assets/esm_shims.js","../node_modules/picocolors/picocolors.js","../src/index.ts","../src/commands/config.ts","../src/config/index.ts","../src/utils/logger.ts","../src/utils/i18n.ts","../src/commands/generate.ts","../src/core/ai.ts","../src/core/prompts.ts","../src/utils/spinner.ts","../src/core/render.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","let p = process || {}, argv = p.argv || [], env = p.env || {}\nlet isColorSupported =\n\t!(!!env.NO_COLOR || argv.includes(\"--no-color\")) &&\n\t(!!env.FORCE_COLOR || argv.includes(\"--color\") || p.platform === \"win32\" || ((p.stdout || {}).isTTY && env.TERM !== \"dumb\") || !!env.CI)\n\nlet formatter = (open, close, replace = open) =>\n\tinput => {\n\t\tlet string = \"\" + input, index = string.indexOf(close, open.length)\n\t\treturn ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close\n\t}\n\nlet replaceClose = (string, close, replace, index) => {\n\tlet result = \"\", cursor = 0\n\tdo {\n\t\tresult += string.substring(cursor, index) + replace\n\t\tcursor = index + close.length\n\t\tindex = string.indexOf(close, cursor)\n\t} while (~index)\n\treturn result + string.substring(cursor)\n}\n\nlet createColors = (enabled = isColorSupported) => {\n\tlet f = enabled ? formatter : () => String\n\treturn {\n\t\tisColorSupported: enabled,\n\t\treset: f(\"\\x1b[0m\", \"\\x1b[0m\"),\n\t\tbold: f(\"\\x1b[1m\", \"\\x1b[22m\", \"\\x1b[22m\\x1b[1m\"),\n\t\tdim: f(\"\\x1b[2m\", \"\\x1b[22m\", \"\\x1b[22m\\x1b[2m\"),\n\t\titalic: f(\"\\x1b[3m\", \"\\x1b[23m\"),\n\t\tunderline: f(\"\\x1b[4m\", \"\\x1b[24m\"),\n\t\tinverse: f(\"\\x1b[7m\", \"\\x1b[27m\"),\n\t\thidden: f(\"\\x1b[8m\", \"\\x1b[28m\"),\n\t\tstrikethrough: f(\"\\x1b[9m\", \"\\x1b[29m\"),\n\n\t\tblack: f(\"\\x1b[30m\", \"\\x1b[39m\"),\n\t\tred: f(\"\\x1b[31m\", \"\\x1b[39m\"),\n\t\tgreen: f(\"\\x1b[32m\", \"\\x1b[39m\"),\n\t\tyellow: f(\"\\x1b[33m\", \"\\x1b[39m\"),\n\t\tblue: f(\"\\x1b[34m\", \"\\x1b[39m\"),\n\t\tmagenta: f(\"\\x1b[35m\", \"\\x1b[39m\"),\n\t\tcyan: f(\"\\x1b[36m\", \"\\x1b[39m\"),\n\t\twhite: f(\"\\x1b[37m\", \"\\x1b[39m\"),\n\t\tgray: f(\"\\x1b[90m\", \"\\x1b[39m\"),\n\n\t\tbgBlack: f(\"\\x1b[40m\", \"\\x1b[49m\"),\n\t\tbgRed: f(\"\\x1b[41m\", \"\\x1b[49m\"),\n\t\tbgGreen: f(\"\\x1b[42m\", \"\\x1b[49m\"),\n\t\tbgYellow: f(\"\\x1b[43m\", \"\\x1b[49m\"),\n\t\tbgBlue: f(\"\\x1b[44m\", \"\\x1b[49m\"),\n\t\tbgMagenta: f(\"\\x1b[45m\", \"\\x1b[49m\"),\n\t\tbgCyan: f(\"\\x1b[46m\", \"\\x1b[49m\"),\n\t\tbgWhite: f(\"\\x1b[47m\", \"\\x1b[49m\"),\n\n\t\tblackBright: f(\"\\x1b[90m\", \"\\x1b[39m\"),\n\t\tredBright: f(\"\\x1b[91m\", \"\\x1b[39m\"),\n\t\tgreenBright: f(\"\\x1b[92m\", \"\\x1b[39m\"),\n\t\tyellowBright: f(\"\\x1b[93m\", \"\\x1b[39m\"),\n\t\tblueBright: f(\"\\x1b[94m\", \"\\x1b[39m\"),\n\t\tmagentaBright: f(\"\\x1b[95m\", \"\\x1b[39m\"),\n\t\tcyanBright: f(\"\\x1b[96m\", \"\\x1b[39m\"),\n\t\twhiteBright: f(\"\\x1b[97m\", \"\\x1b[39m\"),\n\n\t\tbgBlackBright: f(\"\\x1b[100m\", \"\\x1b[49m\"),\n\t\tbgRedBright: f(\"\\x1b[101m\", \"\\x1b[49m\"),\n\t\tbgGreenBright: f(\"\\x1b[102m\", \"\\x1b[49m\"),\n\t\tbgYellowBright: f(\"\\x1b[103m\", \"\\x1b[49m\"),\n\t\tbgBlueBright: f(\"\\x1b[104m\", \"\\x1b[49m\"),\n\t\tbgMagentaBright: f(\"\\x1b[105m\", \"\\x1b[49m\"),\n\t\tbgCyanBright: f(\"\\x1b[106m\", \"\\x1b[49m\"),\n\t\tbgWhiteBright: f(\"\\x1b[107m\", \"\\x1b[49m\"),\n\t}\n}\n\nmodule.exports = createColors()\nmodule.exports.createColors = createColors\n","import { Command } from \"commander\";\nimport { registerConfigCommand } from \"./commands/config.js\";\nimport { registerGenerateCommand } from \"./commands/generate.js\";\nimport { readFileSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { dirname, join } from \"node:path\";\nimport { t } from \"./utils/i18n.js\";\n\n// 从 package.json 中读取版本号\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst packageJsonPath = join(__dirname, \"../package.json\");\nconst packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\"));\nconst VERSION = packageJson.version;\n\nconst program = new Command();\n\nprogram\n .name(\"infographic-gen\")\n .description(t(\"programDesc\"))\n .version(VERSION, \"-v, --version\", t(\"versionFlag\"));\n\n// 注册子命令\nregisterConfigCommand(program);\nregisterGenerateCommand(program);\n\n// 解析命令行参数\nprogram.parse(process.argv);\n","import { Command } from \"commander\";\nimport {\n getAllConfig,\n getConfig,\n setConfig,\n deleteConfig,\n isValidConfigKey,\n getConfigPath,\n CONFIG_KEYS,\n CONFIG_LABELS,\n type ConfigKey,\n} from \"../config/index.js\";\nimport * as log from \"../utils/logger.js\";\nimport { t } from \"../utils/i18n.js\";\n\n/**\n * 注册 `config` 子命令,支持以下操作:\n * config set <key> <value> — 设置配置项\n * config get <key> — 查看单个配置项\n * config list — 列出所有配置\n * config delete <key> — 删除(重置)配置项\n * config path — 打印配置文件路径\n */\nexport function registerConfigCommand(program: Command): void {\n const configCmd = program\n .command(\"config\")\n .alias(\"c\")\n .description(\n \"Manage configuration (API Key, Base URL, Provider, Model, Output Directory)\",\n );\n\n // ---------- config set ----------\n configCmd\n .command(\"set <key> <value>\")\n .description(t(\"configSetDesc\"))\n .action((key: string, value: string) => {\n if (!isValidConfigKey(key)) {\n log.error(t(\"configInvalidKey\", { key, keys: CONFIG_KEYS.join(\", \") }));\n process.exit(1);\n }\n setConfig(key, value);\n log.success(t(\"configSetSuccess\", { label: CONFIG_LABELS[key] }));\n });\n\n // ---------- config get ----------\n configCmd\n .command(\"get <key>\")\n .description(t(\"configGetDesc\"))\n .action((key: string) => {\n if (!isValidConfigKey(key)) {\n log.error(t(\"configInvalidKey\", { key, keys: CONFIG_KEYS.join(\", \") }));\n process.exit(1);\n }\n const val = getConfig(key);\n if (!val) {\n log.warn(t(\"configNotSet\", { label: CONFIG_LABELS[key] }));\n } else {\n // Mask apiKey for security\n const display = key === \"apiKey\" ? maskApiKey(val) : val;\n log.label(CONFIG_LABELS[key], display);\n }\n });\n\n // ---------- config list ----------\n configCmd\n .command(\"list\")\n .description(t(\"configListDesc\"))\n .action(() => {\n const all = getAllConfig();\n log.info(t(\"configCurrent\"));\n for (const k of CONFIG_KEYS) {\n const val = all[k];\n const display =\n k === \"apiKey\" && val\n ? maskApiKey(val)\n : val || t(\"configNotSetValue\");\n log.label(CONFIG_LABELS[k], display);\n }\n log.dim(t(\"configFileLocation\", { path: getConfigPath() }));\n });\n\n // ---------- config delete ----------\n configCmd\n .command(\"delete <key>\")\n .description(t(\"configDeleteDesc\"))\n .action((key: string) => {\n if (!isValidConfigKey(key)) {\n log.error(t(\"configInvalidKey\", { key, keys: CONFIG_KEYS.join(\", \") }));\n process.exit(1);\n }\n deleteConfig(key as ConfigKey);\n log.success(\n t(\"configResetSuccess\", { label: CONFIG_LABELS[key as ConfigKey] }),\n );\n });\n\n // ---------- config path ----------\n configCmd\n .command(\"path\")\n .description(t(\"configPathDesc\"))\n .action(() => {\n console.log(getConfigPath());\n });\n}\n\n// ---- helpers ----\n\n/** 对 API Key 做脱敏处理:只显示前 4 位和后 4 位 */\nfunction maskApiKey(key: string): string {\n if (key.length <= 8) return \"****\";\n return key.slice(0, 4) + \"****\" + key.slice(-4);\n}\n","import Conf from \"conf\";\n\n/**\n * 用户可配置的 LLM 字段\n */\nexport interface LLMConfig {\n apiKey: string;\n baseUrl: string;\n provider: string;\n modelName: string;\n}\n\n/**\n * 配置 schema 的完整类型\n */\ninterface ConfigSchema {\n apiKey: string;\n baseUrl: string;\n provider: string;\n modelName: string;\n defaultOutputDir: string;\n locale: string;\n}\n\n/** 所有可配置的 key */\nexport const CONFIG_KEYS = [\n \"apiKey\",\n \"baseUrl\",\n \"provider\",\n \"modelName\",\n \"defaultOutputDir\",\n \"locale\",\n] as const;\nexport type ConfigKey = (typeof CONFIG_KEYS)[number];\n\n/** 配置项的人类可读标签 */\nexport const CONFIG_LABELS: Record<ConfigKey, string> = {\n apiKey: \"API Key\",\n baseUrl: \"Base URL\",\n provider: \"Provider\",\n modelName: \"Model Name\",\n defaultOutputDir: \"Default Output Directory\",\n locale: \"Language\",\n};\n\n/** 默认值 */\nconst DEFAULTS: ConfigSchema = {\n apiKey: \"\",\n baseUrl: \"https://api.openai.com/v1\",\n provider: \"openai\",\n modelName: \"gpt-4o\",\n defaultOutputDir: \".\",\n locale: \"en\",\n};\n\n/**\n * 基于 conf 的本地持久化配置管理器(单例)\n */\nconst store = new Conf<ConfigSchema>({\n projectName: \"infographic-gen\",\n defaults: DEFAULTS,\n});\n\n/** 读取单个配置值 */\nexport function getConfig<K extends ConfigKey>(key: K): string {\n return store.get(key) as string;\n}\n\n/** 写入单个配置值 */\nexport function setConfig<K extends ConfigKey>(key: K, value: string): void {\n store.set(key, value);\n}\n\n/** 删除单个配置值(恢复为默认) */\nexport function deleteConfig<K extends ConfigKey>(key: K): void {\n store.delete(key);\n}\n\n/** 读取完整 LLM 配置(用于创建 OpenAI 客户端) */\nexport function getLLMConfig(): LLMConfig {\n return {\n apiKey: getConfig(\"apiKey\"),\n baseUrl: getConfig(\"baseUrl\"),\n provider: getConfig(\"provider\"),\n modelName: getConfig(\"modelName\"),\n };\n}\n\n/** 返回所有配置项的 KV 对象(用于 `config list`) */\nexport function getAllConfig(): Record<ConfigKey, string> {\n return {\n apiKey: getConfig(\"apiKey\"),\n baseUrl: getConfig(\"baseUrl\"),\n provider: getConfig(\"provider\"),\n modelName: getConfig(\"modelName\"),\n defaultOutputDir: getConfig(\"defaultOutputDir\"),\n locale: getConfig(\"locale\"),\n };\n}\n\n/** 判断某个 key 是否为合法配置项 */\nexport function isValidConfigKey(key: string): key is ConfigKey {\n return CONFIG_KEYS.includes(key as ConfigKey);\n}\n\n/** 返回配置文件在磁盘上的路径(用于 debug) */\nexport function getConfigPath(): string {\n return store.path;\n}\n","import pc from \"picocolors\";\n\n/**\n * 统一的控制台日志工具,基于 picocolors 着色。\n * 所有输出走 stderr,stdout 留给管道场景。\n */\n\nexport function info(msg: string): void {\n console.error(pc.blue(\"ℹ\") + \" \" + msg);\n}\n\nexport function success(msg: string): void {\n console.error(pc.green(\"✔\") + \" \" + msg);\n}\n\nexport function warn(msg: string): void {\n console.error(pc.yellow(\"⚠\") + \" \" + pc.yellow(msg));\n}\n\nexport function error(msg: string): void {\n console.error(pc.red(\"✖\") + \" \" + pc.red(msg));\n}\n\nexport function dim(msg: string): void {\n console.error(pc.dim(msg));\n}\n\nexport function bold(msg: string): string {\n return pc.bold(msg);\n}\n\nexport function label(key: string, value: string): void {\n console.error(` ${pc.cyan(key)}: ${value}`);\n}\n","import { getConfig } from \"../config/index.js\";\n\nexport type Locale = \"en\" | \"zh-CN\";\n\n/**\n * 所有翻译文本的定义\n */\nconst translations = {\n en: {\n // Main program\n programDesc:\n \"AI-powered CLI to generate AntV Infographic SVGs from natural language prompts\",\n versionFlag: \"Display version number\",\n\n // Config command\n configDesc:\n \"Manage configuration (API Key, Base URL, Provider, Model, Output Directory)\",\n configSetDesc: \"Set a configuration value\",\n configGetDesc: \"Get a single configuration value\",\n configListDesc: \"List all configuration values\",\n configDeleteDesc: \"Delete a configuration value (reset to default)\",\n configPathDesc: \"Show configuration file path\",\n configInvalidKey: 'Invalid config key \"{key}\". Available: {keys}',\n configSetSuccess: \"{label} set successfully\",\n configNotSet: \"{label} not set\",\n configCurrent: \"Current configuration:\",\n configFileLocation: \" Config file location: {path}\",\n configResetSuccess: \"{label} reset to default\",\n configNotSetValue: \"(not set)\",\n\n // Generate command\n generateDesc:\n \"Generate an SVG infographic from natural language (default command)\",\n generatePromptArg: \"Describe the infographic you want to create\",\n generateOutputOpt:\n \"Output SVG file path (default: infographic-output.svg in default output directory)\",\n generateDslOpt:\n \"Save raw DSL syntax to a text file (optional, for debugging or fine-tuning)\",\n outputTarget: \"Output target: {path}\",\n callingAI: \"Calling AI to generate infographic syntax...\",\n renderingSVG: \"Rendering SVG...\",\n reRenderingSVG: \"Re-rendering SVG...\",\n aiCallFailed: \"AI call failed\",\n renderingFailed: \"Rendering failed\",\n renderFailedAfterRetries:\n \"Failed to generate renderable infographic after {retries} self-correction attempts.\",\n lastError: \"Last error: {error}\",\n dslAutoSaved: \"❌ Problematic DSL syntax auto-saved to: {path}\",\n dslAutoSaveHint:\n \"You can inspect this file to debug the LLM output or refine your prompt.\",\n dslForDebug: \"Generated DSL syntax (for debugging reference):\",\n fileWriteFailed: \"File write failed\",\n dslSaved: \"DSL syntax saved to: {path}\",\n dslWriteFailed:\n \"DSL file write failed (does not affect main functionality):\",\n infographicGenerated: \"Infographic generated!\",\n svgSaved: \"SVG saved to: {path}\",\n openInBrowser:\n \"You can open the SVG file directly in a browser to view it.\",\n selfCorrecting: \"AI self-correcting (attempt {attempt}/{max})...\",\n selfCorrectionFailed: \"Self-correction failed\",\n renderFailedRetrying:\n \"Render failed, auto-correcting (attempt {attempt}/{max})...\",\n\n // AI errors\n apiKeyNotConfigured:\n \"API Key not configured. Please run: infographic-gen config set apiKey <YOUR_KEY>\",\n llmEmptyContent: \"LLM returned empty content\",\n failedAfterRetries:\n \"Failed to generate renderable infographic after {retries} retries. Last error: {error}\",\n },\n \"zh-CN\": {\n // Main program\n programDesc: \"AI 驱动的信息图生成 CLI —— 输入自然语言,输出精美 SVG 信息图\",\n versionFlag: \"显示版本号\",\n\n // Config command\n configDesc: \"管理配置(API Key、Base URL、Provider、Model、输出目录)\",\n configSetDesc: \"设置配置项\",\n configGetDesc: \"查看单个配置项\",\n configListDesc: \"列出所有配置项\",\n configDeleteDesc: \"删除配置项(恢复默认值)\",\n configPathDesc: \"显示配置文件路径\",\n configInvalidKey: '无效的配置项 \"{key}\",可选项:{keys}',\n configSetSuccess: \"{label} 已设置\",\n configNotSet: \"{label} 尚未设置\",\n configCurrent: \"当前配置:\",\n configFileLocation: \" 配置文件位置: {path}\",\n configResetSuccess: \"{label} 已重置为默认值\",\n configNotSetValue: \"(未设置)\",\n\n // Generate command\n generateDesc: \"根据自然语言描述生成 SVG 信息图(默认命令)\",\n generatePromptArg: \"描述你想要生成的信息图内容\",\n generateOutputOpt:\n \"输出 SVG 文件路径(默认:默认输出目录中的 infographic-output.svg)\",\n generateDslOpt: \"将原始 DSL 语法保存到指定文本文件(可选,用于调试或微调)\",\n outputTarget: \"目标输出:{path}\",\n callingAI: \"正在调用 AI 生成信息图语法...\",\n renderingSVG: \"正在渲染 SVG...\",\n reRenderingSVG: \"正在重新渲染 SVG...\",\n aiCallFailed: \"AI 调用失败\",\n renderingFailed: \"渲染最终失败\",\n renderFailedAfterRetries:\n \"经过 {retries} 次自我修正仍无法生成可渲染的信息图。\",\n lastError: \"最后一次错误:{error}\",\n dslAutoSaved: \"❌ 有问题的 DSL 语法已自动保存至:{path}\",\n dslAutoSaveHint:\n \"你可以检查此文件以排查大模型生成的问题,或用于调试 Prompt。\",\n dslForDebug: \"生成的 DSL 语法如下(供 debug 参考):\",\n fileWriteFailed: \"文件写入失败\",\n dslSaved: \"DSL 语法已保存至:{path}\",\n dslWriteFailed: \"DSL 文件写入失败(不影响主要功能):\",\n infographicGenerated: \"信息图已生成!\",\n svgSaved: \"SVG 已保存至:{path}\",\n openInBrowser: \"可直接在浏览器中打开 SVG 文件查看效果。\",\n selfCorrecting: \"AI 正在自我修正(第 {attempt}/{max} 次)...\",\n selfCorrectionFailed: \"自我修正失败\",\n renderFailedRetrying: \"渲染失败,正在自动修正(第 {attempt}/{max} 次)...\",\n\n // AI errors\n apiKeyNotConfigured:\n \"尚未配置 API Key。请先运行:infographic-gen config set apiKey <YOUR_KEY>\",\n llmEmptyContent: \"LLM 返回了空内容\",\n failedAfterRetries:\n \"已重试 {retries} 次仍然无法生成可渲染的信息图。最后一次错误:{error}\",\n },\n} as const;\n\n/**\n * 获取当前配置的语言\n */\nexport function getLocale(): Locale {\n const locale = getConfig(\"locale\");\n return (locale === \"zh-CN\" ? \"zh-CN\" : \"en\") as Locale;\n}\n\n/**\n * 获取翻译文本\n * @param key 翻译键\n * @param params 插值参数\n */\nexport function t(\n key: keyof (typeof translations)[\"en\"],\n params?: Record<string, string | number>,\n): string {\n const locale = getLocale();\n let text: string = translations[locale][key];\n\n if (params) {\n Object.entries(params).forEach(([paramKey, value]) => {\n text = text.replace(`{${paramKey}}`, String(value));\n });\n }\n\n return text;\n}\n","import path from \"node:path\";\nimport { Command } from \"commander\";\nimport {\n generateInfographicDSL,\n retryWithCorrection,\n MAX_RETRIES,\n} from \"../core/ai.js\";\nimport {\n renderDSLToSVG,\n writeSVGFile,\n writeDSLFile,\n resolveOutputPath,\n} from \"../core/render.js\";\nimport * as log from \"../utils/logger.js\";\nimport {\n startSpinner,\n succeedSpinner,\n failSpinner,\n updateSpinner,\n} from \"../utils/spinner.js\";\nimport { t } from \"../utils/i18n.js\";\n\n/**\n * 注册 `generate` 子命令。\n *\n * 用法示例:\n * infographic-gen generate \"帮我画一个软件开发流程图\" -o result.svg\n * infographic-gen generate \"帮我画一个饼图\" -o chart.svg --dsl chart-syntax.txt\n */\nexport function registerGenerateCommand(program: Command): void {\n program\n .command(\"generate\", { isDefault: true })\n .alias(\"g\")\n .description(t(\"generateDesc\"))\n .argument(\"<prompt>\", t(\"generatePromptArg\"))\n .option(\"-o, --output <path>\", t(\"generateOutputOpt\"))\n .option(\"--dsl <path>\", t(\"generateDslOpt\"))\n .action(async (prompt: string, opts: { output?: string; dsl?: string }) => {\n await handleGenerate(prompt, opts.output, opts.dsl);\n });\n}\n\n/**\n * 生成命令的核心处理逻辑。\n *\n * 流程:\n * 1. 调用 LLM 生成 DSL\n * 2. 调用 SSR 渲染为 SVG\n * 3. 如果渲染失败,自动进入自我修正循环(最多 MAX_RETRIES 次)\n * 4. 写入 SVG 文件\n * 5. 如果用户指定了 --dsl,保存原始 DSL 语法\n */\nasync function handleGenerate(\n prompt: string,\n outputOption?: string,\n dslOption?: string,\n): Promise<void> {\n const outputPath = resolveOutputPath(outputOption);\n\n log.info(t(\"outputTarget\", { path: path.resolve(outputPath) }));\n startSpinner(t(\"callingAI\"));\n\n let syntax: string;\n try {\n syntax = await generateInfographicDSL(prompt);\n } catch (err) {\n failSpinner(t(\"aiCallFailed\"));\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n\n updateSpinner(t(\"renderingSVG\"));\n\n // ─── 渲染 + 自我修正循环 ───────────────────────────────────────────\n let svgContent: string | null = null;\n let lastError = \"\";\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n svgContent = await renderDSLToSVG(syntax);\n break; // Render successful, exit loop\n } catch (err) {\n lastError = err instanceof Error ? err.message : String(err);\n\n if (attempt < MAX_RETRIES) {\n // Retry available → self-correction\n try {\n syntax = await retryWithCorrection(\n syntax,\n lastError,\n prompt,\n attempt + 1,\n );\n updateSpinner(t(\"reRenderingSVG\"));\n } catch (retryErr) {\n failSpinner(t(\"selfCorrectionFailed\"));\n log.error(\n retryErr instanceof Error ? retryErr.message : String(retryErr),\n );\n process.exit(1);\n }\n }\n }\n }\n\n if (!svgContent) {\n failSpinner(t(\"renderingFailed\"));\n log.error(t(\"renderFailedAfterRetries\", { retries: String(MAX_RETRIES) }));\n log.error(t(\"lastError\", { error: lastError }));\n\n // Auto-save problematic DSL to error-dump.txt\n const errorDumpPath = \"error-dump.txt\";\n try {\n await writeDSLFile(errorDumpPath, syntax);\n log.warn(t(\"dslAutoSaved\", { path: path.resolve(errorDumpPath) }));\n log.dim(t(\"dslAutoSaveHint\"));\n } catch (writeErr) {\n log.dim(t(\"dslForDebug\"));\n console.error(syntax);\n }\n\n process.exit(1);\n }\n\n // ─── Render successful, write file ─────────────────────────────────────────────\n try {\n await writeSVGFile(outputPath, svgContent);\n } catch (err) {\n failSpinner(t(\"fileWriteFailed\"));\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n\n // ─── If user specified --dsl, save raw DSL syntax ───────────────────────\n if (dslOption) {\n try {\n await writeDSLFile(dslOption, syntax);\n log.success(t(\"dslSaved\", { path: path.resolve(dslOption) }));\n } catch (err) {\n log.warn(t(\"dslWriteFailed\"));\n log.warn(err instanceof Error ? err.message : String(err));\n }\n }\n\n succeedSpinner(t(\"infographicGenerated\"));\n log.success(t(\"svgSaved\", { path: path.resolve(outputPath) }));\n log.dim(t(\"openInBrowser\"));\n}\n","import OpenAI from \"openai\";\nimport { getLLMConfig } from \"../config/index.js\";\nimport {\n INFOGRAPHIC_CREATOR_SYSTEM_PROMPT,\n SELF_CORRECTION_PROMPT_TEMPLATE,\n} from \"./prompts.js\";\nimport * as log from \"../utils/logger.js\";\nimport { updateSpinner } from \"../utils/spinner.js\";\nimport { t } from \"../utils/i18n.js\";\n\n/** LLM 请求返回的结果 */\nexport interface LLMResult {\n /** 生成的 DSL 语法文本 */\n syntax: string;\n /** 总共重试了几次(0 = 一次成功) */\n attempts: number;\n}\n\n/** 最大自我修正重试次数 */\nconst MAX_RETRIES = 3;\n\n/**\n * 创建一个懒初始化的 OpenAI 客户端。\n * 通过覆盖 baseURL / apiKey 兼容 DeepSeek、阿里云百炼等平台。\n */\nfunction createClient(): OpenAI {\n const { apiKey, baseUrl } = getLLMConfig();\n\n if (!apiKey) {\n throw new Error(t(\"apiKeyNotConfigured\"));\n }\n\n return new OpenAI({\n apiKey,\n baseURL: baseUrl,\n });\n}\n\n/**\n * 向 LLM 发送请求,获取信息图 DSL。\n *\n * @param userPrompt 用户的自然语言描述\n * @returns LLM 返回的纯 DSL 文本\n */\nasync function callLLM(\n client: OpenAI,\n model: string,\n messages: OpenAI.ChatCompletionMessageParam[],\n): Promise<string> {\n const response = await client.chat.completions.create({\n model,\n messages,\n temperature: 0.7,\n max_tokens: 4096,\n });\n\n const content = response.choices[0]?.message?.content;\n if (!content) {\n throw new Error(t(\"llmEmptyContent\"));\n }\n\n return cleanDSL(content);\n}\n\n/**\n * 清理 LLM 返回的文本,去除可能的 Markdown 代码块标记等噪声。\n */\nfunction cleanDSL(raw: string): string {\n let cleaned = raw.trim();\n\n // 去掉可能包裹的 ```xxx ... ``` 代码块\n const codeBlockMatch = cleaned.match(/^```[\\w]*\\n?([\\s\\S]*?)```$/);\n if (codeBlockMatch) {\n cleaned = codeBlockMatch[1].trim();\n }\n\n // 确保以 infographic 开头\n const infographicIndex = cleaned.indexOf(\"infographic \");\n if (infographicIndex > 0) {\n cleaned = cleaned.slice(infographicIndex);\n }\n\n return cleaned;\n}\n\n/**\n * 带自我修正机制的 AI 生成入口。\n *\n * 流程:\n * 1. 将用户 Prompt + 系统提示词发给 LLM,获取 DSL。\n * 2. 调用方(generate 命令)会尝试渲染。如果渲染失败,调用 retryWithCorrection。\n * 3. 最多重试 MAX_RETRIES 次。\n *\n * @param userPrompt 用户输入的自然语言\n * @returns 生成的 DSL 语法\n */\nexport async function generateInfographicDSL(\n userPrompt: string,\n): Promise<string> {\n const client = createClient();\n const { modelName } = getLLMConfig();\n\n const messages: OpenAI.ChatCompletionMessageParam[] = [\n { role: \"system\", content: INFOGRAPHIC_CREATOR_SYSTEM_PROMPT },\n { role: \"user\", content: userPrompt },\n ];\n\n return callLLM(client, modelName, messages);\n}\n\n/**\n * 自我修正:当 DSL 渲染失败时,把错误信息反馈给 LLM 要求修正。\n *\n * @param originalSyntax 上一次生成的 DSL\n * @param errorMessage 渲染报错信息\n * @param userPrompt 用户原始需求\n * @param attempt 当前第几次重试(从 1 开始)\n * @returns 修正后的 DSL\n */\nexport async function retryWithCorrection(\n originalSyntax: string,\n errorMessage: string,\n userPrompt: string,\n attempt: number,\n): Promise<string> {\n if (attempt > MAX_RETRIES) {\n throw new Error(\n t(\"failedAfterRetries\", {\n retries: String(MAX_RETRIES),\n error: errorMessage,\n }),\n );\n }\n\n updateSpinner(\n t(\"selfCorrecting\", { attempt: String(attempt), max: String(MAX_RETRIES) }),\n );\n log.warn(\n t(\"renderFailedRetrying\", {\n attempt: String(attempt),\n max: String(MAX_RETRIES),\n }),\n );\n\n const client = createClient();\n const { modelName } = getLLMConfig();\n\n const correctionMessage = SELF_CORRECTION_PROMPT_TEMPLATE.replace(\n \"{error}\",\n errorMessage,\n ).replace(\"{syntax}\", originalSyntax);\n\n const messages: OpenAI.ChatCompletionMessageParam[] = [\n { role: \"system\", content: INFOGRAPHIC_CREATOR_SYSTEM_PROMPT },\n { role: \"user\", content: userPrompt },\n { role: \"assistant\", content: originalSyntax },\n { role: \"user\", content: correctionMessage },\n ];\n\n return callLLM(client, modelName, messages);\n}\n\n/** 导出最大重试次数常量供外部使用 */\nexport { MAX_RETRIES };\n","/**\n * ⭐ 硬编码的系统提示词常量。\n *\n * 直接内嵌在代码中,确保 CLI 开箱即用,无需读取本地磁盘文件。\n * 内容基于 @antv/infographic 的 .skills/infographic-creator/SKILL.md 整理。\n */\n\n// ─── 公共:AntV Infographic 语法规范 ────────────────────────────────\n\nconst SYNTAX_SPEC = `\n## AntV Infographic 语法\n\nAntV Infographic 语法是一种自定义的 DSL,用于描述信息图渲染配置。它使用缩进描述信息,具有较强鲁棒性,便于 AI 流式输出并渲染信息图。主要包含以下信息:\n\n1. template:用模板表达文字信息结构。\n2. data:信息图数据,包含 title、desc、数据项等。数据项通常包含 label、desc、icon 等字段。\n3. theme:主题包含 palette、font 等样式配置。\n\n例如:\n\\`\\`\\`\ninfographic list-row-horizontal-icon-arrow\ndata\n title Title\n desc Description\n lists\n - label Label\n value 12.5\n desc Explanation\n icon document text\ntheme\n palette #3b82f6 #8b5cf6 #f97316\n\\`\\`\\`\n\n### 语法规范\n\n• 第一行必须是 \\`infographic <template-name>\\`,模板从下方可用模板列表中选择。\n• 使用 \\`data\\` / \\`theme\\` 块,块内用两个空格缩进。\n• 键值对使用「键 空格 值」;数组使用 \\`-\\` 作为条目前缀。\n• icon 使用图标关键词(如 \\`star fill\\`)。\n• \\`data\\` 应包含 title/desc + 模板对应的主数据字段(不一定是 \\`items\\`)。\n• 主数据字段选择(只用一个,避免混用):\n - \\`list-*\\` → \\`lists\\`\n - \\`sequence-*\\` → \\`sequences\\`(可选 \\`order asc|desc\\`)\n - \\`compare-*\\` → \\`compares\\`(支持 \\`children\\` 分组对比),可包含多个对比项\n - \\`hierarchy-structure\\` → \\`items\\`(每一项对应一个独立层级,每一层级可以包含子项,最多可嵌套 3 层)\n - \\`hierarchy-*\\` → 单一 \\`root\\`(树结构,通过 \\`children\\` 嵌套)\n - \\`relation-*\\` → \\`nodes\\` + \\`relations\\`;简单关系图可省略 \\`nodes\\`,在 relations 中用箭头语法\n - \\`chart-*\\` → \\`values\\`(数值统计,可选 \\`category\\`)\n - 不确定时再用 \\`items\\` 兜底\n\n• \\`compare-binary-*\\` / \\`compare-hierarchy-left-right-*\\` 二元模板:必须两个根节点,所有对比项挂在这两个根节点的 children\n• \\`hierarchy-*\\`:使用单一 \\`root\\`,通过 \\`children\\` 嵌套(不要重复 \\`root\\`)\n• \\`theme\\` 用于自定义主题(palette、font 等)\n\n例如:暗色主题 + 自定义配色\n\\`\\`\\`\ninfographic list-row-simple-horizontal-arrow\ntheme dark\n palette\n - #61DDAA\n - #F6BD16\n - #F08BB4\n\\`\\`\\`\n\n• 使用 \\`theme.base.text.font-family\\` 指定字体,如手写风格 \\`851tegakizatsu\\`\n• 使用 \\`theme.stylize\\` 选择内置风格并传参。常见风格:\n - \\`rough\\`:手绘效果\n - \\`pattern\\`:图案填充\n - \\`linear-gradient\\` / \\`radial-gradient\\`:线性/径向渐变\n\n例如:手绘风格(rough)\n\\`\\`\\`\ninfographic list-row-simple-horizontal-arrow\ntheme\n stylize rough\n base\n text\n font-family 851tegakizatsu\n\\`\\`\\`\n`.trim();\n\n// ─── 数据语法示例 ───────────────────────────────────────────────────\n\nconst DATA_EXAMPLES = `\n### 数据语法示例\n\n• list-* 模版\n\\`\\`\\`\ninfographic list-grid-badge-card\ndata\n title Feature List\n lists\n - label Fast\n icon flash fast\n - label Secure\n icon secure shield check\n\\`\\`\\`\n\n• sequence-* 模版\n\\`\\`\\`\ninfographic sequence-steps-simple\ndata\n sequences\n - label Step 1\n - label Step 2\n - label Step 3\n order asc\n\\`\\`\\`\n\n• hierarchy-* 模版\n\\`\\`\\`\ninfographic hierarchy-structure\ndata\n root\n label Company\n children\n - label Dept A\n - label Dept B\n\\`\\`\\`\n\n• compare-* 模版\n\\`\\`\\`\ninfographic compare-swot\ndata\n compares\n - label Strengths\n children\n - label Strong brand\n - label Loyal users\n - label Weaknesses\n children\n - label High cost\n - label Slow release\n\\`\\`\\`\n\n四象限图\n\\`\\`\\`\ninfographic compare-quadrant-quarter-simple-card\ndata\n compares\n - label High Impact & Low Effort\n - label High Impact & High Effort\n - label Low Impact & Low Effort\n - label Low Impact & High Effort\n\\`\\`\\`\n\n• chart-* 模版\n\\`\\`\\`\ninfographic chart-column-simple\ndata\n values\n - label Visits\n value 1280\n - label Conversion\n value 12.4\n\\`\\`\\`\n\n• relation-* 模版\n边标签写法:A -label-> B 或 A -->|label| B\n\\`\\`\\`\ninfographic relation-dagre-flow-tb-simple-circle-node\ndata\n nodes\n - id A\n label Node A\n - id B\n label Node B\n relations\n A - approves -> B\n A -->|blocks| B\n\\`\\`\\`\n\n• 兜底 items 示例\n\\`\\`\\`\ninfographic list-row-horizontal-icon-arrow\ndata\n items\n - label Item A\n desc Description\n icon sun\n - label Item B\n desc Description\n icon moon\n\\`\\`\\`\n`.trim();\n\n// ─── 可用模板列表 ───────────────────────────────────────────────────\n\nconst AVAILABLE_TEMPLATES = `\n### 可用模板\n\n• chart-bar-plain-text\n• chart-column-simple\n• chart-line-plain-text\n• chart-pie-compact-card\n• chart-pie-donut-pill-badge\n• chart-pie-donut-plain-text\n• chart-pie-plain-text\n• chart-wordcloud\n• compare-binary-horizontal-badge-card-arrow\n• compare-binary-horizontal-simple-fold\n• compare-binary-horizontal-underline-text-vs\n• compare-hierarchy-left-right-circle-node-pill-badge\n• compare-quadrant-quarter-circular\n• compare-quadrant-quarter-simple-card\n• compare-swot\n• hierarchy-mindmap-branch-gradient-capsule-item\n• hierarchy-mindmap-level-gradient-compact-card\n• hierarchy-structure\n• hierarchy-tree-curved-line-rounded-rect-node\n• hierarchy-tree-tech-style-badge-card\n• hierarchy-tree-tech-style-capsule-item\n• list-column-done-list\n• list-column-simple-vertical-arrow\n• list-column-vertical-icon-arrow\n• list-grid-badge-card\n• list-grid-candy-card-lite\n• list-grid-ribbon-card\n• list-row-horizontal-icon-arrow\n• list-sector-plain-text\n• list-waterfall-badge-card\n• list-waterfall-compact-card\n• list-zigzag-down-compact-card\n• list-zigzag-down-simple\n• list-zigzag-up-compact-card\n• list-zigzag-up-simple\n• relation-dagre-flow-tb-animated-badge-card\n• relation-dagre-flow-tb-animated-simple-circle-node\n• relation-dagre-flow-tb-badge-card\n• relation-dagre-flow-tb-simple-circle-node\n• sequence-ascending-stairs-3d-underline-text\n• sequence-ascending-steps\n• sequence-circular-simple\n• sequence-color-snake-steps-horizontal-icon-line\n• sequence-cylinders-3d-simple\n• sequence-filter-mesh-simple\n• sequence-funnel-simple\n• sequence-horizontal-zigzag-underline-text\n• sequence-mountain-underline-text\n• sequence-pyramid-simple\n• sequence-roadmap-vertical-plain-text\n• sequence-roadmap-vertical-simple\n• sequence-snake-steps-compact-card\n• sequence-snake-steps-simple\n• sequence-snake-steps-underline-text\n• sequence-stairs-front-compact-card\n• sequence-stairs-front-pill-badge\n• sequence-timeline-rounded-rect-node\n• sequence-timeline-simple\n• sequence-zigzag-pucks-3d-simple\n• sequence-zigzag-steps-underline-text\n\n模板选择建议:\n• 严格顺序(流程/步骤/发展趋势)→ sequence-*\n - 时间线 → sequence-timeline-*\n - 阶梯图 → sequence-stairs-*\n - 路线图 → sequence-roadmap-vertical-*\n - 折线路径 → sequence-zigzag-*\n - 环形进度 → sequence-circular-simple\n - 彩色蛇形步骤 → sequence-color-snake-steps-*\n - 金字塔 → sequence-pyramid-simple\n• 观点列举 → list-row-* 或 list-column-*\n• 二元对比(利弊)→ compare-binary-*\n• SWOT → compare-swot\n• 层级结构(树图)→ hierarchy-tree-*\n• 数据图表 → chart-*\n• 象限分析 → quadrant-*\n• 网格列表(要点)→ list-grid-*\n• 关系展示 → relation-*\n• 词云 → chart-wordcloud\n• 思维导图 → hierarchy-mindmap-*\n`.trim();\n\n// ─── 完整示例 ───────────────────────────────────────────────────────\n\nconst FULL_EXAMPLE = `\n### 完整示例\n\n绘制互联网技术演进信息图:\n\\`\\`\\`\ninfographic list-row-horizontal-icon-arrow\ndata\n title Internet Technology Evolution\n desc From Web 1.0 to AI era, key milestones\n lists\n - time 1991\n label Web 1.0\n desc Tim Berners-Lee published the first website, opening the Internet era\n icon web\n - time 2004\n label Web 2.0\n desc Social media and user-generated content become mainstream\n icon account multiple\n - time 2007\n label Mobile\n desc iPhone released, smartphone changes the world\n icon cellphone\n - time 2015\n label Cloud Native\n desc Containerization and microservices architecture are widely used\n icon cloud\n - time 2020\n label Low Code\n desc Visual development lowers the technology threshold\n icon application brackets\n - time 2023\n label AI Large Model\n desc ChatGPT ignites the generative AI revolution\n icon brain\n\\`\\`\\`\n`.trim();\n\n// ─── 导出:信息图创建者系统提示词 ───────────────────────────────────\n\n/**\n * 信息图创建者的完整 System Prompt。\n * 用于指导 LLM 根据用户自然语言需求,生成合法的 AntV Infographic DSL 语法。\n */\nexport const INFOGRAPHIC_CREATOR_SYSTEM_PROMPT = `\n你是一位专业的信息图设计专家。你的任务是根据用户的需求,生成 AntV Infographic DSL 语法来创建精美的信息图。\n\n信息图(Infographic)将数据、信息与知识转化为可感知的视觉语言。它结合视觉设计与数据可视化,用直观符号压缩复杂信息,帮助受众快速理解并记住要点。\n\nInfographic = Information Structure + Visual Expression\n\n你需要严格遵守以下规范来生成 AntV Infographic 语法:\n\n${SYNTAX_SPEC}\n\n${DATA_EXAMPLES}\n\n${AVAILABLE_TEMPLATES}\n\n${FULL_EXAMPLE}\n\n## 重要约束\n\n1. **只输出纯 DSL 语法**:不要输出任何 Markdown 代码块标记(如 \\\\\\`\\\\\\`\\\\\\`)、解释性文字、JSON 或其他格式。直接输出以 \\`infographic <template-name>\\` 开头的 DSL 文本。\n2. **必须尊重用户输入语言**:如果用户使用中文描述需求,DSL 中所有文本内容(title、desc、label 等)也必须使用中文。\n3. **选择最合适的模板**:根据用户需求的信息结构(列表、流程、对比、层级等)选择最匹配的模板。\n4. **数据完整性**:确保生成的数据结构完整,包含 title、desc 以及与模板匹配的主数据字段。\n5. **配色和主题**:除非用户特别要求,否则使用一组美观且协调的默认配色。\n`.trim();\n\n/**\n * 自我修正提示词模板。\n * 当 SSR 渲染失败时,将报错信息注入后发给 LLM 进行修正。\n */\nexport const SELF_CORRECTION_PROMPT_TEMPLATE = `\n之前你生成的 AntV Infographic DSL 语法在渲染时出错了。\n\n报错信息:\n{error}\n\n之前生成的语法:\n{syntax}\n\n请修正语法并重新输出。要求:\n1. 只输出修正后的纯 DSL 语法,不要添加任何解释或 Markdown 格式。\n2. 确保第一行是 \\`infographic <template-name>\\`。\n3. 确保数据结构符合所选模板的规范。\n4. 修复导致错误的具体问题。\n`.trim();\n","import ora, { type Ora } from \"ora\";\n\n/**\n * 终端 Loading 动画封装。\n * 提供 start / succeed / fail / update 等便捷方法。\n */\n\nlet currentSpinner: Ora | null = null;\n\nexport function startSpinner(text: string): Ora {\n // 如果之前有未关闭的 spinner,先停掉\n if (currentSpinner?.isSpinning) {\n currentSpinner.stop();\n }\n currentSpinner = ora({ text, stream: process.stderr }).start();\n return currentSpinner;\n}\n\nexport function succeedSpinner(text?: string): void {\n currentSpinner?.succeed(text);\n currentSpinner = null;\n}\n\nexport function failSpinner(text?: string): void {\n currentSpinner?.fail(text);\n currentSpinner = null;\n}\n\nexport function updateSpinner(text: string): void {\n if (currentSpinner) {\n currentSpinner.text = text;\n }\n}\n\nexport function stopSpinner(): void {\n currentSpinner?.stop();\n currentSpinner = null;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { renderToString } from \"@antv/infographic/ssr\";\nimport { getConfig } from \"../config/index.js\";\n\n/**\n * 渲染结果\n */\nexport interface RenderResult {\n /** 生成的 SVG 字符串 */\n svg: string;\n}\n\n/**\n * 使用 @antv/infographic SSR 将 DSL 语法渲染为 SVG 字符串。\n *\n * @param syntax AntV Infographic DSL 语法文本\n * @returns SVG 字符串\n * @throws 渲染失败时抛出带有详细错误信息的 Error\n */\nexport async function renderDSLToSVG(syntax: string): Promise<string> {\n try {\n const svgString = await renderToString(syntax);\n if (!svgString || svgString.trim().length === 0) {\n throw new Error(\"renderToString 返回了空的 SVG 内容\");\n }\n return svgString;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`SSR 渲染失败: ${message}`);\n }\n}\n\n/**\n * 将 SVG 字符串写入指定路径。\n * 自动创建所需的中间目录。\n *\n * @param outputPath 输出文件路径\n * @param svgContent SVG 字符串内容\n */\nexport async function writeSVGFile(\n outputPath: string,\n svgContent: string,\n): Promise<void> {\n const absolutePath = path.resolve(outputPath);\n const dir = path.dirname(absolutePath);\n\n // 确保输出目录存在\n await fs.mkdir(dir, { recursive: true });\n await fs.writeFile(absolutePath, svgContent, \"utf-8\");\n}\n\n/**\n * 将 DSL 语法文本写入指定路径。\n * 自动创建所需的中间目录。\n *\n * @param outputPath 输出文件路径\n * @param dslContent DSL 语法字符串\n */\nexport async function writeDSLFile(\n outputPath: string,\n dslContent: string,\n): Promise<void> {\n const absolutePath = path.resolve(outputPath);\n const dir = path.dirname(absolutePath);\n\n // 确保输出目录存在\n await fs.mkdir(dir, { recursive: true });\n await fs.writeFile(absolutePath, dslContent, \"utf-8\");\n}\n\n/**\n * 根据用户输入的输出路径,补全默认值。\n * 支持配置默认输出目录,如果用户未指定,默认为 defaultOutputDir 下的 infographic-output.svg。\n *\n * @param userOutput 用户指定的输出路径(可选)\n * @returns 完整的输出路径(绝对路径或相对路径)\n */\nexport function resolveOutputPath(userOutput?: string): string {\n const defaultDir = getConfig(\"defaultOutputDir\") || \".\";\n\n if (userOutput) {\n // 用户指定了输出路径\n // 确保扩展名是 .svg\n const withExt = userOutput.endsWith(\".svg\")\n ? userOutput\n : userOutput + \".svg\";\n\n // 如果是绝对路径,直接返回;否则相对于默认目录\n if (path.isAbsolute(withExt)) {\n return withExt;\n }\n return path.resolve(defaultDir, withExt);\n }\n\n // 用户未指定,使用默认输出目录 + 默认文件名\n return path.resolve(defaultDir, \"infographic-output.svg\");\n}\n"],"mappings":"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","names":["pc","pc","program","path","path","path","program","path","fileURLToPath","__dirname","fileURLToPath"]}
1
+ {"version":3,"sources":["../node_modules/tsup/assets/esm_shims.js","../node_modules/picocolors/picocolors.js","../src/index.ts","../src/commands/config.ts","../src/config/index.ts","../src/utils/logger.ts","../src/utils/i18n.ts","../src/commands/generate.ts","../src/core/ai.ts","../src/core/prompts.ts","../src/utils/spinner.ts","../src/core/render.ts","../src/utils/file-reader.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","let p = process || {}, argv = p.argv || [], env = p.env || {}\nlet isColorSupported =\n\t!(!!env.NO_COLOR || argv.includes(\"--no-color\")) &&\n\t(!!env.FORCE_COLOR || argv.includes(\"--color\") || p.platform === \"win32\" || ((p.stdout || {}).isTTY && env.TERM !== \"dumb\") || !!env.CI)\n\nlet formatter = (open, close, replace = open) =>\n\tinput => {\n\t\tlet string = \"\" + input, index = string.indexOf(close, open.length)\n\t\treturn ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close\n\t}\n\nlet replaceClose = (string, close, replace, index) => {\n\tlet result = \"\", cursor = 0\n\tdo {\n\t\tresult += string.substring(cursor, index) + replace\n\t\tcursor = index + close.length\n\t\tindex = string.indexOf(close, cursor)\n\t} while (~index)\n\treturn result + string.substring(cursor)\n}\n\nlet createColors = (enabled = isColorSupported) => {\n\tlet f = enabled ? formatter : () => String\n\treturn {\n\t\tisColorSupported: enabled,\n\t\treset: f(\"\\x1b[0m\", \"\\x1b[0m\"),\n\t\tbold: f(\"\\x1b[1m\", \"\\x1b[22m\", \"\\x1b[22m\\x1b[1m\"),\n\t\tdim: f(\"\\x1b[2m\", \"\\x1b[22m\", \"\\x1b[22m\\x1b[2m\"),\n\t\titalic: f(\"\\x1b[3m\", \"\\x1b[23m\"),\n\t\tunderline: f(\"\\x1b[4m\", \"\\x1b[24m\"),\n\t\tinverse: f(\"\\x1b[7m\", \"\\x1b[27m\"),\n\t\thidden: f(\"\\x1b[8m\", \"\\x1b[28m\"),\n\t\tstrikethrough: f(\"\\x1b[9m\", \"\\x1b[29m\"),\n\n\t\tblack: f(\"\\x1b[30m\", \"\\x1b[39m\"),\n\t\tred: f(\"\\x1b[31m\", \"\\x1b[39m\"),\n\t\tgreen: f(\"\\x1b[32m\", \"\\x1b[39m\"),\n\t\tyellow: f(\"\\x1b[33m\", \"\\x1b[39m\"),\n\t\tblue: f(\"\\x1b[34m\", \"\\x1b[39m\"),\n\t\tmagenta: f(\"\\x1b[35m\", \"\\x1b[39m\"),\n\t\tcyan: f(\"\\x1b[36m\", \"\\x1b[39m\"),\n\t\twhite: f(\"\\x1b[37m\", \"\\x1b[39m\"),\n\t\tgray: f(\"\\x1b[90m\", \"\\x1b[39m\"),\n\n\t\tbgBlack: f(\"\\x1b[40m\", \"\\x1b[49m\"),\n\t\tbgRed: f(\"\\x1b[41m\", \"\\x1b[49m\"),\n\t\tbgGreen: f(\"\\x1b[42m\", \"\\x1b[49m\"),\n\t\tbgYellow: f(\"\\x1b[43m\", \"\\x1b[49m\"),\n\t\tbgBlue: f(\"\\x1b[44m\", \"\\x1b[49m\"),\n\t\tbgMagenta: f(\"\\x1b[45m\", \"\\x1b[49m\"),\n\t\tbgCyan: f(\"\\x1b[46m\", \"\\x1b[49m\"),\n\t\tbgWhite: f(\"\\x1b[47m\", \"\\x1b[49m\"),\n\n\t\tblackBright: f(\"\\x1b[90m\", \"\\x1b[39m\"),\n\t\tredBright: f(\"\\x1b[91m\", \"\\x1b[39m\"),\n\t\tgreenBright: f(\"\\x1b[92m\", \"\\x1b[39m\"),\n\t\tyellowBright: f(\"\\x1b[93m\", \"\\x1b[39m\"),\n\t\tblueBright: f(\"\\x1b[94m\", \"\\x1b[39m\"),\n\t\tmagentaBright: f(\"\\x1b[95m\", \"\\x1b[39m\"),\n\t\tcyanBright: f(\"\\x1b[96m\", \"\\x1b[39m\"),\n\t\twhiteBright: f(\"\\x1b[97m\", \"\\x1b[39m\"),\n\n\t\tbgBlackBright: f(\"\\x1b[100m\", \"\\x1b[49m\"),\n\t\tbgRedBright: f(\"\\x1b[101m\", \"\\x1b[49m\"),\n\t\tbgGreenBright: f(\"\\x1b[102m\", \"\\x1b[49m\"),\n\t\tbgYellowBright: f(\"\\x1b[103m\", \"\\x1b[49m\"),\n\t\tbgBlueBright: f(\"\\x1b[104m\", \"\\x1b[49m\"),\n\t\tbgMagentaBright: f(\"\\x1b[105m\", \"\\x1b[49m\"),\n\t\tbgCyanBright: f(\"\\x1b[106m\", \"\\x1b[49m\"),\n\t\tbgWhiteBright: f(\"\\x1b[107m\", \"\\x1b[49m\"),\n\t}\n}\n\nmodule.exports = createColors()\nmodule.exports.createColors = createColors\n","import { Command } from \"commander\";\nimport { registerConfigCommand } from \"./commands/config.js\";\nimport { registerGenerateCommand } from \"./commands/generate.js\";\nimport { readFileSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { dirname, join } from \"node:path\";\nimport { t } from \"./utils/i18n.js\";\n\n// 从 package.json 中读取版本号\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst packageJsonPath = join(__dirname, \"../package.json\");\nconst packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\"));\nconst VERSION = packageJson.version;\n\nconst program = new Command();\n\nprogram\n .name(\"infographic-gen\")\n .description(t(\"programDesc\"))\n .version(VERSION, \"-v, --version\", t(\"versionFlag\"));\n\n// 注册子命令\nregisterConfigCommand(program);\nregisterGenerateCommand(program);\n\n// 解析命令行参数\nprogram.parse(process.argv);\n","import { Command } from \"commander\";\nimport {\n getAllConfig,\n getConfig,\n setConfig,\n deleteConfig,\n isValidConfigKey,\n getConfigPath,\n CONFIG_KEYS,\n CONFIG_LABELS,\n type ConfigKey,\n} from \"../config/index.js\";\nimport * as log from \"../utils/logger.js\";\nimport { t } from \"../utils/i18n.js\";\n\n/**\n * 注册 `config` 子命令,支持以下操作:\n * config set <key> <value> — 设置配置项\n * config get <key> — 查看单个配置项\n * config list — 列出所有配置\n * config delete <key> — 删除(重置)配置项\n * config path — 打印配置文件路径\n */\nexport function registerConfigCommand(program: Command): void {\n const configCmd = program\n .command(\"config\")\n .alias(\"c\")\n .description(\n \"Manage configuration (API Key, Base URL, Provider, Model, Output Directory)\",\n );\n\n // ---------- config set ----------\n configCmd\n .command(\"set <key> <value>\")\n .description(t(\"configSetDesc\"))\n .action((key: string, value: string) => {\n if (!isValidConfigKey(key)) {\n log.error(t(\"configInvalidKey\", { key, keys: CONFIG_KEYS.join(\", \") }));\n process.exit(1);\n }\n setConfig(key, value);\n log.success(t(\"configSetSuccess\", { label: CONFIG_LABELS[key] }));\n });\n\n // ---------- config get ----------\n configCmd\n .command(\"get <key>\")\n .description(t(\"configGetDesc\"))\n .action((key: string) => {\n if (!isValidConfigKey(key)) {\n log.error(t(\"configInvalidKey\", { key, keys: CONFIG_KEYS.join(\", \") }));\n process.exit(1);\n }\n const val = getConfig(key);\n if (!val) {\n log.warn(t(\"configNotSet\", { label: CONFIG_LABELS[key] }));\n } else {\n // Mask apiKey for security\n const display = key === \"apiKey\" ? maskApiKey(val) : val;\n log.label(CONFIG_LABELS[key], display);\n }\n });\n\n // ---------- config list ----------\n configCmd\n .command(\"list\")\n .description(t(\"configListDesc\"))\n .action(() => {\n const all = getAllConfig();\n log.info(t(\"configCurrent\"));\n for (const k of CONFIG_KEYS) {\n const val = all[k];\n const display =\n k === \"apiKey\" && val\n ? maskApiKey(val)\n : val || t(\"configNotSetValue\");\n log.label(CONFIG_LABELS[k], display);\n }\n log.dim(t(\"configFileLocation\", { path: getConfigPath() }));\n });\n\n // ---------- config delete ----------\n configCmd\n .command(\"delete <key>\")\n .description(t(\"configDeleteDesc\"))\n .action((key: string) => {\n if (!isValidConfigKey(key)) {\n log.error(t(\"configInvalidKey\", { key, keys: CONFIG_KEYS.join(\", \") }));\n process.exit(1);\n }\n deleteConfig(key as ConfigKey);\n log.success(\n t(\"configResetSuccess\", { label: CONFIG_LABELS[key as ConfigKey] }),\n );\n });\n\n // ---------- config path ----------\n configCmd\n .command(\"path\")\n .description(t(\"configPathDesc\"))\n .action(() => {\n console.log(getConfigPath());\n });\n}\n\n// ---- helpers ----\n\n/** 对 API Key 做脱敏处理:只显示前 4 位和后 4 位 */\nfunction maskApiKey(key: string): string {\n if (key.length <= 8) return \"****\";\n return key.slice(0, 4) + \"****\" + key.slice(-4);\n}\n","import Conf from \"conf\";\n\n/**\n * 用户可配置的 LLM 字段\n */\nexport interface LLMConfig {\n apiKey: string;\n baseUrl: string;\n provider: string;\n modelName: string;\n}\n\n/**\n * 配置 schema 的完整类型\n */\ninterface ConfigSchema {\n apiKey: string;\n baseUrl: string;\n provider: string;\n modelName: string;\n defaultOutputDir: string;\n locale: string;\n maxFileChars: string;\n}\n\n/** 所有可配置的 key */\nexport const CONFIG_KEYS = [\n \"apiKey\",\n \"baseUrl\",\n \"provider\",\n \"modelName\",\n \"defaultOutputDir\",\n \"locale\",\n \"maxFileChars\",\n] as const;\nexport type ConfigKey = (typeof CONFIG_KEYS)[number];\n\n/** 配置项的人类可读标签 */\nexport const CONFIG_LABELS: Record<ConfigKey, string> = {\n apiKey: \"API Key\",\n baseUrl: \"Base URL\",\n provider: \"Provider\",\n modelName: \"Model Name\",\n defaultOutputDir: \"Default Output Directory\",\n locale: \"Language\",\n maxFileChars: \"Max File Content Characters\",\n};\n\n/** 默认值 */\nconst DEFAULTS: ConfigSchema = {\n apiKey: \"\",\n baseUrl: \"https://api.openai.com/v1\",\n provider: \"openai\",\n modelName: \"gpt-4o\",\n defaultOutputDir: \".\",\n locale: \"en\",\n maxFileChars: \"30000\",\n};\n\n/**\n * 基于 conf 的本地持久化配置管理器(单例)\n *\n * conf 默认将配置存储在系统标准目录(如 Windows 的 %APPDATA%/infographic-gen-nodejs/Config/),\n * 该路径不包含版本号,因此升级 npm 包版本不会影响已有配置。\n * defaults 仅在对应 key 不存在时生效,不会覆盖用户已设置的值。\n */\nconst store = new Conf<ConfigSchema>({\n projectName: \"infographic-gen\",\n defaults: DEFAULTS,\n});\n\n/** 读取单个配置值 */\nexport function getConfig<K extends ConfigKey>(key: K): string {\n return store.get(key) as string;\n}\n\n/** 写入单个配置值 */\nexport function setConfig<K extends ConfigKey>(key: K, value: string): void {\n store.set(key, value);\n}\n\n/** 删除单个配置值(恢复为默认) */\nexport function deleteConfig<K extends ConfigKey>(key: K): void {\n store.delete(key);\n}\n\n/** 读取完整 LLM 配置(用于创建 OpenAI 客户端) */\nexport function getLLMConfig(): LLMConfig {\n return {\n apiKey: getConfig(\"apiKey\"),\n baseUrl: getConfig(\"baseUrl\"),\n provider: getConfig(\"provider\"),\n modelName: getConfig(\"modelName\"),\n };\n}\n\n/** 返回所有配置项的 KV 对象(用于 `config list`) */\nexport function getAllConfig(): Record<ConfigKey, string> {\n return {\n apiKey: getConfig(\"apiKey\"),\n baseUrl: getConfig(\"baseUrl\"),\n provider: getConfig(\"provider\"),\n modelName: getConfig(\"modelName\"),\n defaultOutputDir: getConfig(\"defaultOutputDir\"),\n locale: getConfig(\"locale\"),\n maxFileChars: getConfig(\"maxFileChars\"),\n };\n}\n\n/** 判断某个 key 是否为合法配置项 */\nexport function isValidConfigKey(key: string): key is ConfigKey {\n return CONFIG_KEYS.includes(key as ConfigKey);\n}\n\n/** 返回配置文件在磁盘上的路径(用于 debug) */\nexport function getConfigPath(): string {\n return store.path;\n}\n","import pc from \"picocolors\";\n\n/**\n * 统一的控制台日志工具,基于 picocolors 着色。\n * 所有输出走 stderr,stdout 留给管道场景。\n */\n\nexport function info(msg: string): void {\n console.error(pc.blue(\"ℹ\") + \" \" + msg);\n}\n\nexport function success(msg: string): void {\n console.error(pc.green(\"✔\") + \" \" + msg);\n}\n\nexport function warn(msg: string): void {\n console.error(pc.yellow(\"⚠\") + \" \" + pc.yellow(msg));\n}\n\nexport function error(msg: string): void {\n console.error(pc.red(\"✖\") + \" \" + pc.red(msg));\n}\n\nexport function dim(msg: string): void {\n console.error(pc.dim(msg));\n}\n\nexport function bold(msg: string): string {\n return pc.bold(msg);\n}\n\nexport function label(key: string, value: string): void {\n console.error(` ${pc.cyan(key)}: ${value}`);\n}\n","import { getConfig } from \"../config/index.js\";\n\nexport type Locale = \"en\" | \"zh-CN\";\n\n/**\n * 所有翻译文本的定义\n */\nconst translations = {\n en: {\n // Main program\n programDesc:\n \"AI-powered CLI to generate AntV Infographic SVGs from natural language prompts\",\n versionFlag: \"Display version number\",\n\n // Config command\n configDesc:\n \"Manage configuration (API Key, Base URL, Provider, Model, Output Directory)\",\n configSetDesc: \"Set a configuration value\",\n configGetDesc: \"Get a single configuration value\",\n configListDesc: \"List all configuration values\",\n configDeleteDesc: \"Delete a configuration value (reset to default)\",\n configPathDesc: \"Show configuration file path\",\n configInvalidKey: 'Invalid config key \"{key}\". Available: {keys}',\n configSetSuccess: \"{label} set successfully\",\n configNotSet: \"{label} not set\",\n configCurrent: \"Current configuration:\",\n configFileLocation: \" Config file location: {path}\",\n configResetSuccess: \"{label} reset to default\",\n configNotSetValue: \"(not set)\",\n\n // Generate command\n generateDesc:\n \"Generate an SVG infographic from natural language (default command)\",\n generatePromptArg: \"Describe the infographic you want to create\",\n generateOutputOpt:\n \"Output SVG file path (default: infographic-YYYYMMDD-HHMMSS.svg in default output directory)\",\n generateDslOpt:\n \"Save raw DSL syntax to a text file (optional, for debugging or fine-tuning)\",\n outputTarget: \"Output target: {path}\",\n callingAI: \"Calling AI to generate infographic syntax...\",\n renderingSVG: \"Rendering SVG...\",\n reRenderingSVG: \"Re-rendering SVG...\",\n aiCallFailed: \"AI call failed\",\n renderingFailed: \"Rendering failed\",\n renderFailedAfterRetries:\n \"Failed to generate renderable infographic after {retries} self-correction attempts.\",\n lastError: \"Last error: {error}\",\n dslAutoSaved: \"❌ Problematic DSL syntax auto-saved to: {path}\",\n dslAutoSaveHint:\n \"You can inspect this file to debug the LLM output or refine your prompt.\",\n dslForDebug: \"Generated DSL syntax (for debugging reference):\",\n fileWriteFailed: \"File write failed\",\n dslSaved: \"DSL syntax saved to: {path}\",\n dslWriteFailed:\n \"DSL file write failed (does not affect main functionality):\",\n infographicGenerated: \"Infographic generated!\",\n svgSaved: \"SVG saved to: {path}\",\n openInBrowser:\n \"You can open the SVG file directly in a browser to view it.\",\n selfCorrecting: \"AI self-correcting (attempt {attempt}/{max})...\",\n selfCorrectionFailed: \"Self-correction failed\",\n renderFailedRetrying:\n \"Render failed, auto-correcting (attempt {attempt}/{max})...\",\n\n // AI errors\n apiKeyNotConfigured:\n \"API Key not configured. Please run: infographic-gen config set apiKey <YOUR_KEY>\",\n llmEmptyContent: \"LLM returned empty content\",\n failedAfterRetries:\n \"Failed to generate renderable infographic after {retries} retries. Last error: {error}\",\n\n // File reader\n unsupportedFileFormat:\n \"Unsupported file format: {ext}. Currently supported: {supported}\",\n fileNotFound: \"File not found: {path}\",\n fileTooLarge: \"File too large ({size} MB). Maximum allowed size is 10 MB.\",\n parsingFile: \"Parsing local file: {path}...\",\n fileParsed: \"File parsed successfully!\",\n fileContentTruncated:\n \"Warning: file content is too long and has been auto-truncated. Some information may be lost.\",\n generateFileOpt:\n \"Provide a local file (.md, .txt, .pdf, .docx) as additional context\",\n generateFromDslOpt:\n \"Render SVG directly from a DSL text file (skips AI generation)\",\n readingDslFile: \"Reading DSL file: {path}...\",\n dslFileReadSuccess: \"DSL file loaded!\",\n dslFileNotFound: \"DSL file not found: {path}\",\n dslFileReadFailed: \"Failed to read DSL file\",\n fromDslNoPromptNeeded:\n \"Using --from-dsl mode: rendering directly from DSL file, no prompt needed.\",\n },\n \"zh-CN\": {\n // Main program\n programDesc: \"AI 驱动的信息图生成 CLI —— 输入自然语言,输出精美 SVG 信息图\",\n versionFlag: \"显示版本号\",\n\n // Config command\n configDesc: \"管理配置(API Key、Base URL、Provider、Model、输出目录)\",\n configSetDesc: \"设置配置项\",\n configGetDesc: \"查看单个配置项\",\n configListDesc: \"列出所有配置项\",\n configDeleteDesc: \"删除配置项(恢复默认值)\",\n configPathDesc: \"显示配置文件路径\",\n configInvalidKey: '无效的配置项 \"{key}\",可选项:{keys}',\n configSetSuccess: \"{label} 已设置\",\n configNotSet: \"{label} 尚未设置\",\n configCurrent: \"当前配置:\",\n configFileLocation: \" 配置文件位置: {path}\",\n configResetSuccess: \"{label} 已重置为默认值\",\n configNotSetValue: \"(未设置)\",\n\n // Generate command\n generateDesc: \"根据自然语言描述生成 SVG 信息图(默认命令)\",\n generatePromptArg: \"描述你想要生成的信息图内容\",\n generateOutputOpt:\n \"输出 SVG 文件路径(默认:默认输出目录中的 infographic-YYYYMMDD-HHMMSS.svg)\",\n generateDslOpt: \"将原始 DSL 语法保存到指定文本文件(可选,用于调试或微调)\",\n outputTarget: \"目标输出:{path}\",\n callingAI: \"正在调用 AI 生成信息图语法...\",\n renderingSVG: \"正在渲染 SVG...\",\n reRenderingSVG: \"正在重新渲染 SVG...\",\n aiCallFailed: \"AI 调用失败\",\n renderingFailed: \"渲染最终失败\",\n renderFailedAfterRetries:\n \"经过 {retries} 次自我修正仍无法生成可渲染的信息图。\",\n lastError: \"最后一次错误:{error}\",\n dslAutoSaved: \"❌ 有问题的 DSL 语法已自动保存至:{path}\",\n dslAutoSaveHint:\n \"你可以检查此文件以排查大模型生成的问题,或用于调试 Prompt。\",\n dslForDebug: \"生成的 DSL 语法如下(供 debug 参考):\",\n fileWriteFailed: \"文件写入失败\",\n dslSaved: \"DSL 语法已保存至:{path}\",\n dslWriteFailed: \"DSL 文件写入失败(不影响主要功能):\",\n infographicGenerated: \"信息图已生成!\",\n svgSaved: \"SVG 已保存至:{path}\",\n openInBrowser: \"可直接在浏览器中打开 SVG 文件查看效果。\",\n selfCorrecting: \"AI 正在自我修正(第 {attempt}/{max} 次)...\",\n selfCorrectionFailed: \"自我修正失败\",\n renderFailedRetrying: \"渲染失败,正在自动修正(第 {attempt}/{max} 次)...\",\n\n // AI errors\n apiKeyNotConfigured:\n \"尚未配置 API Key。请先运行:infographic-gen config set apiKey <YOUR_KEY>\",\n llmEmptyContent: \"LLM 返回了空内容\",\n failedAfterRetries:\n \"已重试 {retries} 次仍然无法生成可渲染的信息图。最后一次错误:{error}\",\n\n // File reader\n unsupportedFileFormat: \"暂不支持 {ext} 格式的文件,目前支持:{supported}\",\n fileNotFound: \"文件未找到:{path}\",\n fileTooLarge: \"文件过大({size} MB),最大允许 10 MB。\",\n parsingFile: \"正在解析本地文件:{path}...\",\n fileParsed: \"文件解析成功!\",\n fileContentTruncated:\n \"警告:文件内容过长,已自动截断,可能会丢失部分信息。\",\n generateFileOpt: \"提供本地文件(.md, .txt, .pdf, .docx)作为额外上下文\",\n generateFromDslOpt: \"直接从 DSL 文本文件渲染 SVG(跳过 AI 生成)\",\n readingDslFile: \"正在读取 DSL 文件:{path}...\",\n dslFileReadSuccess: \"DSL 文件加载成功!\",\n dslFileNotFound: \"DSL 文件未找到:{path}\",\n dslFileReadFailed: \"读取 DSL 文件失败\",\n fromDslNoPromptNeeded:\n \"使用 --from-dsl 模式:直接从 DSL 文件渲染,无需提示词。\",\n },\n} as const;\n\n/**\n * 获取当前配置的语言\n */\nexport function getLocale(): Locale {\n const locale = getConfig(\"locale\");\n return (locale === \"zh-CN\" ? \"zh-CN\" : \"en\") as Locale;\n}\n\n/**\n * 获取翻译文本\n * @param key 翻译键\n * @param params 插值参数\n */\nexport function t(\n key: keyof (typeof translations)[\"en\"],\n params?: Record<string, string | number>,\n): string {\n const locale = getLocale();\n let text: string = translations[locale][key];\n\n if (params) {\n Object.entries(params).forEach(([paramKey, value]) => {\n text = text.replace(`{${paramKey}}`, String(value));\n });\n }\n\n return text;\n}\n","import path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport { Command } from \"commander\";\nimport {\n generateInfographicDSL,\n retryWithCorrection,\n MAX_RETRIES,\n} from \"../core/ai.js\";\nimport {\n renderDSLToSVG,\n writeSVGFile,\n writeDSLFile,\n resolveOutputPath,\n} from \"../core/render.js\";\nimport { extractTextFromFile } from \"../utils/file-reader.js\";\nimport * as log from \"../utils/logger.js\";\nimport {\n startSpinner,\n succeedSpinner,\n failSpinner,\n updateSpinner,\n} from \"../utils/spinner.js\";\nimport { t } from \"../utils/i18n.js\";\n\n/**\n * 注册 `generate` 子命令。\n *\n * 用法示例:\n * infographic-gen generate \"帮我画一个软件开发流程图\" -o result.svg\n * infographic-gen generate \"帮我画一个饼图\" -o chart.svg --dsl chart-syntax.txt\n * infographic-gen generate --from-dsl my-syntax.txt -o result.svg\n */\nexport function registerGenerateCommand(program: Command): void {\n program\n .command(\"generate\", { isDefault: true })\n .alias(\"g\")\n .description(t(\"generateDesc\"))\n .argument(\"[prompt]\", t(\"generatePromptArg\"))\n .option(\"-o, --output <path>\", t(\"generateOutputOpt\"))\n .option(\"--dsl <path>\", t(\"generateDslOpt\"))\n .option(\"-f, --file <path>\", t(\"generateFileOpt\"))\n .option(\"--from-dsl <path>\", t(\"generateFromDslOpt\"))\n .action(\n async (\n prompt: string | undefined,\n opts: {\n output?: string;\n dsl?: string;\n file?: string;\n fromDsl?: string;\n },\n ) => {\n // --from-dsl 模式:直接从 DSL 文件渲染,不需要 prompt\n if (opts.fromDsl) {\n await handleFromDsl(opts.fromDsl, opts.output);\n return;\n }\n\n // 正常模式:必须提供 prompt\n if (!prompt) {\n log.error(t(\"generatePromptArg\"));\n process.exit(1);\n }\n\n await handleGenerate(prompt, opts.output, opts.dsl, opts.file);\n },\n );\n}\n\n/**\n * 生成命令的核心处理逻辑。\n *\n * 流程:\n * 1. 调用 LLM 生成 DSL\n * 2. 调用 SSR 渲染为 SVG\n * 3. 如果渲染失败,自动进入自我修正循环(最多 MAX_RETRIES 次)\n * 4. 写入 SVG 文件\n * 5. 如果用户指定了 --dsl,保存原始 DSL 语法\n */\nasync function handleGenerate(\n prompt: string,\n outputOption?: string,\n dslOption?: string,\n fileOption?: string,\n): Promise<void> {\n const outputPath = resolveOutputPath(outputOption);\n\n // ─── 文件解析(可选)─────────────────────────────────────────\n let fileContext: string | undefined;\n\n if (fileOption) {\n startSpinner(t(\"parsingFile\", { path: fileOption }));\n try {\n const { text, truncated } = await extractTextFromFile(fileOption);\n fileContext = text;\n succeedSpinner(t(\"fileParsed\"));\n if (truncated) {\n log.warn(t(\"fileContentTruncated\"));\n }\n } catch (err) {\n failSpinner(t(\"aiCallFailed\"));\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n }\n\n log.info(t(\"outputTarget\", { path: path.resolve(outputPath) }));\n startSpinner(t(\"callingAI\"));\n\n let syntax: string;\n try {\n syntax = await generateInfographicDSL(prompt, fileContext);\n } catch (err) {\n failSpinner(t(\"aiCallFailed\"));\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n\n updateSpinner(t(\"renderingSVG\"));\n\n // ─── 渲染 + 自我修正循环 ───────────────────────────────────────────\n let svgContent: string | null = null;\n let lastError = \"\";\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n svgContent = await renderDSLToSVG(syntax);\n break; // Render successful, exit loop\n } catch (err) {\n lastError = err instanceof Error ? err.message : String(err);\n\n if (attempt < MAX_RETRIES) {\n // Retry available → self-correction\n try {\n syntax = await retryWithCorrection(\n syntax,\n lastError,\n prompt,\n attempt + 1,\n fileContext,\n );\n updateSpinner(t(\"reRenderingSVG\"));\n } catch (retryErr) {\n failSpinner(t(\"selfCorrectionFailed\"));\n log.error(\n retryErr instanceof Error ? retryErr.message : String(retryErr),\n );\n process.exit(1);\n }\n }\n }\n }\n\n if (!svgContent) {\n failSpinner(t(\"renderingFailed\"));\n log.error(t(\"renderFailedAfterRetries\", { retries: String(MAX_RETRIES) }));\n log.error(t(\"lastError\", { error: lastError }));\n\n // Auto-save problematic DSL to error-dump.txt\n const errorDumpPath = \"error-dump.txt\";\n try {\n await writeDSLFile(errorDumpPath, syntax);\n log.warn(t(\"dslAutoSaved\", { path: path.resolve(errorDumpPath) }));\n log.dim(t(\"dslAutoSaveHint\"));\n } catch (writeErr) {\n log.dim(t(\"dslForDebug\"));\n console.error(syntax);\n }\n\n process.exit(1);\n }\n\n // ─── Render successful, write file ─────────────────────────────────────────────\n try {\n await writeSVGFile(outputPath, svgContent);\n } catch (err) {\n failSpinner(t(\"fileWriteFailed\"));\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n\n // ─── If user specified --dsl, save raw DSL syntax ───────────────────────\n if (dslOption) {\n try {\n await writeDSLFile(dslOption, syntax);\n log.success(t(\"dslSaved\", { path: path.resolve(dslOption) }));\n } catch (err) {\n log.warn(t(\"dslWriteFailed\"));\n log.warn(err instanceof Error ? err.message : String(err));\n }\n }\n\n succeedSpinner(t(\"infographicGenerated\"));\n log.success(t(\"svgSaved\", { path: path.resolve(outputPath) }));\n log.dim(t(\"openInBrowser\"));\n}\n\n/**\n * 从 DSL 文件直接渲染 SVG 的处理逻辑。\n *\n * 跳过 AI 生成步骤,直接读取 DSL 文件并渲染。\n * 适用于:\n * - 之前通过 --dsl 保存的 DSL 语法文件\n * - 手动编写或微调过的 DSL 文件\n * - error-dump.txt 修正后重新渲染\n */\nasync function handleFromDsl(\n dslPath: string,\n outputOption?: string,\n): Promise<void> {\n const outputPath = resolveOutputPath(outputOption);\n const resolvedDslPath = path.resolve(dslPath);\n\n log.dim(t(\"fromDslNoPromptNeeded\"));\n log.info(t(\"outputTarget\", { path: path.resolve(outputPath) }));\n\n // ─── 读取 DSL 文件 ──────────────────────────────────────────────\n startSpinner(t(\"readingDslFile\", { path: dslPath }));\n\n let syntax: string;\n try {\n syntax = await fs.readFile(resolvedDslPath, \"utf-8\");\n } catch {\n failSpinner(t(\"dslFileReadFailed\"));\n log.error(t(\"dslFileNotFound\", { path: resolvedDslPath }));\n process.exit(1);\n }\n\n syntax = syntax.trim();\n if (!syntax) {\n failSpinner(t(\"dslFileReadFailed\"));\n log.error(t(\"llmEmptyContent\"));\n process.exit(1);\n }\n\n succeedSpinner(t(\"dslFileReadSuccess\"));\n\n // ─── 渲染 SVG ──────────────────────────────────────────────────\n startSpinner(t(\"renderingSVG\"));\n\n let svgContent: string;\n try {\n svgContent = await renderDSLToSVG(syntax);\n } catch (err) {\n failSpinner(t(\"renderingFailed\"));\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n\n // ─── 写入文件 ──────────────────────────────────────────────────\n try {\n await writeSVGFile(outputPath, svgContent);\n } catch (err) {\n failSpinner(t(\"fileWriteFailed\"));\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n\n succeedSpinner(t(\"infographicGenerated\"));\n log.success(t(\"svgSaved\", { path: path.resolve(outputPath) }));\n log.dim(t(\"openInBrowser\"));\n}\n","import OpenAI from \"openai\";\nimport { getLLMConfig } from \"../config/index.js\";\nimport {\n INFOGRAPHIC_CREATOR_SYSTEM_PROMPT,\n SELF_CORRECTION_PROMPT_TEMPLATE,\n FILE_CONTEXT_PROMPT_TEMPLATE,\n} from \"./prompts.js\";\nimport * as log from \"../utils/logger.js\";\nimport { updateSpinner } from \"../utils/spinner.js\";\nimport { t } from \"../utils/i18n.js\";\n\n/** LLM 请求返回的结果 */\nexport interface LLMResult {\n /** 生成的 DSL 语法文本 */\n syntax: string;\n /** 总共重试了几次(0 = 一次成功) */\n attempts: number;\n}\n\n/** 最大自我修正重试次数 */\nconst MAX_RETRIES = 3;\n\n/**\n * 创建一个懒初始化的 OpenAI 客户端。\n * 通过覆盖 baseURL / apiKey 兼容 DeepSeek、阿里云百炼等平台。\n */\nfunction createClient(): OpenAI {\n const { apiKey, baseUrl } = getLLMConfig();\n\n if (!apiKey) {\n throw new Error(t(\"apiKeyNotConfigured\"));\n }\n\n return new OpenAI({\n apiKey,\n baseURL: baseUrl,\n });\n}\n\n/**\n * 向 LLM 发送请求,获取信息图 DSL。\n *\n * @param userPrompt 用户的自然语言描述\n * @returns LLM 返回的纯 DSL 文本\n */\nasync function callLLM(\n client: OpenAI,\n model: string,\n messages: OpenAI.ChatCompletionMessageParam[],\n): Promise<string> {\n const response = await client.chat.completions.create({\n model,\n messages,\n temperature: 0.7,\n max_tokens: 4096,\n });\n\n const content = response.choices[0]?.message?.content;\n if (!content) {\n throw new Error(t(\"llmEmptyContent\"));\n }\n\n return cleanDSL(content);\n}\n\n/**\n * 清理 LLM 返回的文本,去除可能的 Markdown 代码块标记等噪声。\n */\nfunction cleanDSL(raw: string): string {\n let cleaned = raw.trim();\n\n // 去掉可能包裹的 ```xxx ... ``` 代码块\n const codeBlockMatch = cleaned.match(/^```[\\w]*\\n?([\\s\\S]*?)```$/);\n if (codeBlockMatch) {\n cleaned = codeBlockMatch[1].trim();\n }\n\n // 确保以 infographic 开头\n const infographicIndex = cleaned.indexOf(\"infographic \");\n if (infographicIndex > 0) {\n cleaned = cleaned.slice(infographicIndex);\n }\n\n return cleaned;\n}\n\n/**\n * 带自我修正机制的 AI 生成入口。\n *\n * 流程:\n * 1. 将用户 Prompt + 系统提示词发给 LLM,获取 DSL。\n * 2. 调用方(generate 命令)会尝试渲染。如果渲染失败,调用 retryWithCorrection。\n * 3. 最多重试 MAX_RETRIES 次。\n *\n * @param userPrompt 用户输入的自然语言\n * @param fileContext 可选的文件上下文(通过 -f 参数传入的文件内容)\n * @returns 生成的 DSL 语法\n */\nexport async function generateInfographicDSL(\n userPrompt: string,\n fileContext?: string,\n): Promise<string> {\n const client = createClient();\n const { modelName } = getLLMConfig();\n\n const finalPrompt = fileContext\n ? FILE_CONTEXT_PROMPT_TEMPLATE.replace(\n \"{fileContent}\",\n fileContext,\n ).replace(\"{userPrompt}\", userPrompt)\n : userPrompt;\n\n const messages: OpenAI.ChatCompletionMessageParam[] = [\n { role: \"system\", content: INFOGRAPHIC_CREATOR_SYSTEM_PROMPT },\n { role: \"user\", content: finalPrompt },\n ];\n\n return callLLM(client, modelName, messages);\n}\n\n/**\n * 自我修正:当 DSL 渲染失败时,把错误信息反馈给 LLM 要求修正。\n *\n * @param originalSyntax 上一次生成的 DSL\n * @param errorMessage 渲染报错信息\n * @param userPrompt 用户原始需求\n * @param attempt 当前第几次重试(从 1 开始)\n * @param fileContext 可选的文件上下文\n * @returns 修正后的 DSL\n */\nexport async function retryWithCorrection(\n originalSyntax: string,\n errorMessage: string,\n userPrompt: string,\n attempt: number,\n fileContext?: string,\n): Promise<string> {\n if (attempt > MAX_RETRIES) {\n throw new Error(\n t(\"failedAfterRetries\", {\n retries: String(MAX_RETRIES),\n error: errorMessage,\n }),\n );\n }\n\n updateSpinner(\n t(\"selfCorrecting\", { attempt: String(attempt), max: String(MAX_RETRIES) }),\n );\n log.warn(\n t(\"renderFailedRetrying\", {\n attempt: String(attempt),\n max: String(MAX_RETRIES),\n }),\n );\n\n const client = createClient();\n const { modelName } = getLLMConfig();\n\n const correctionMessage = SELF_CORRECTION_PROMPT_TEMPLATE.replace(\n \"{error}\",\n errorMessage,\n ).replace(\"{syntax}\", originalSyntax);\n\n // 如果有文件上下文,在重试时也保留,让 AI 不丢失参考资料\n const originalUserPrompt = fileContext\n ? FILE_CONTEXT_PROMPT_TEMPLATE.replace(\n \"{fileContent}\",\n fileContext,\n ).replace(\"{userPrompt}\", userPrompt)\n : userPrompt;\n\n const messages: OpenAI.ChatCompletionMessageParam[] = [\n { role: \"system\", content: INFOGRAPHIC_CREATOR_SYSTEM_PROMPT },\n { role: \"user\", content: originalUserPrompt },\n { role: \"assistant\", content: originalSyntax },\n { role: \"user\", content: correctionMessage },\n ];\n\n return callLLM(client, modelName, messages);\n}\n\n/** 导出最大重试次数常量供外部使用 */\nexport { MAX_RETRIES };\n","/**\n * ⭐ 硬编码的系统提示词常量。\n *\n * 直接内嵌在代码中,确保 CLI 开箱即用,无需读取本地磁盘文件。\n * 内容基于 @antv/infographic 的 .skills/infographic-creator/SKILL.md 整理。\n */\n\n// ─── 公共:AntV Infographic 语法规范 ────────────────────────────────\n\nconst SYNTAX_SPEC = `\n## AntV Infographic 语法\n\nAntV Infographic 语法是一种自定义的 DSL,用于描述信息图渲染配置。它使用缩进描述信息,具有较强鲁棒性,便于 AI 流式输出并渲染信息图。主要包含以下信息:\n\n1. template:用模板表达文字信息结构。\n2. data:信息图数据,包含 title、desc、数据项等。数据项通常包含 label、desc、icon 等字段。\n3. theme:主题包含 palette、font 等样式配置。\n\n例如:\n\\`\\`\\`\ninfographic list-row-horizontal-icon-arrow\ndata\n title Title\n desc Description\n lists\n - label Label\n value 12.5\n desc Explanation\n icon document text\ntheme\n palette #3b82f6 #8b5cf6 #f97316\n\\`\\`\\`\n\n### 语法规范\n\n• 第一行必须是 \\`infographic <template-name>\\`,模板从下方可用模板列表中选择。\n• 使用 \\`data\\` / \\`theme\\` 块,块内用两个空格缩进。\n• 键值对使用「键 空格 值」;数组使用 \\`-\\` 作为条目前缀。\n• icon 使用图标关键词(如 \\`star fill\\`)。\n• \\`data\\` 应包含 title/desc + 模板对应的主数据字段(不一定是 \\`items\\`)。\n• 主数据字段选择(只用一个,避免混用):\n - \\`list-*\\` → \\`lists\\`\n - \\`sequence-*\\` → \\`sequences\\`(可选 \\`order asc|desc\\`)\n - \\`compare-*\\` → \\`compares\\`(支持 \\`children\\` 分组对比),可包含多个对比项\n - \\`hierarchy-structure\\` → \\`items\\`(每一项对应一个独立层级,每一层级可以包含子项,最多可嵌套 3 层)\n - \\`hierarchy-*\\` → 单一 \\`root\\`(树结构,通过 \\`children\\` 嵌套)\n - \\`relation-*\\` → \\`nodes\\` + \\`relations\\`;简单关系图可省略 \\`nodes\\`,在 relations 中用箭头语法\n - \\`chart-*\\` → \\`values\\`(数值统计,可选 \\`category\\`)\n - 不确定时再用 \\`items\\` 兜底\n\n• \\`compare-binary-*\\` / \\`compare-hierarchy-left-right-*\\` 二元模板:必须两个根节点,所有对比项挂在这两个根节点的 children\n• \\`hierarchy-*\\`:使用单一 \\`root\\`,通过 \\`children\\` 嵌套(不要重复 \\`root\\`)\n• \\`theme\\` 用于自定义主题(palette、font 等)\n\n例如:暗色主题 + 自定义配色\n\\`\\`\\`\ninfographic list-row-simple-horizontal-arrow\ntheme dark\n palette\n - #61DDAA\n - #F6BD16\n - #F08BB4\n\\`\\`\\`\n`.trim();\n\n// ─── 数据语法示例 ───────────────────────────────────────────────────\n\nconst DATA_EXAMPLES = `\n### 数据语法示例\n\n• list-* 模版\n\\`\\`\\`\ninfographic list-grid-badge-card\ndata\n title Feature List\n lists\n - label Fast\n icon flash fast\n - label Secure\n icon secure shield check\n\\`\\`\\`\n\n• sequence-* 模版\n\\`\\`\\`\ninfographic sequence-steps-simple\ndata\n sequences\n - label Step 1\n - label Step 2\n - label Step 3\n order asc\n\\`\\`\\`\n\n• hierarchy-* 模版\n\\`\\`\\`\ninfographic hierarchy-structure\ndata\n root\n label Company\n children\n - label Dept A\n - label Dept B\n\\`\\`\\`\n\n• compare-* 模版\n\\`\\`\\`\ninfographic compare-swot\ndata\n compares\n - label Strengths\n children\n - label Strong brand\n - label Loyal users\n - label Weaknesses\n children\n - label High cost\n - label Slow release\n\\`\\`\\`\n\n四象限图\n\\`\\`\\`\ninfographic compare-quadrant-quarter-simple-card\ndata\n compares\n - label High Impact & Low Effort\n - label High Impact & High Effort\n - label Low Impact & Low Effort\n - label Low Impact & High Effort\n\\`\\`\\`\n\n• chart-* 模版\n\\`\\`\\`\ninfographic chart-column-simple\ndata\n values\n - label Visits\n value 1280\n - label Conversion\n value 12.4\n\\`\\`\\`\n\n• relation-* 模版\n边标签写法:A -label-> B 或 A -->|label| B\n\\`\\`\\`\ninfographic relation-dagre-flow-tb-simple-circle-node\ndata\n nodes\n - id A\n label Node A\n - id B\n label Node B\n relations\n A - approves -> B\n A -->|blocks| B\n\\`\\`\\`\n\n• 兜底 items 示例\n\\`\\`\\`\ninfographic list-row-horizontal-icon-arrow\ndata\n items\n - label Item A\n desc Description\n icon sun\n - label Item B\n desc Description\n icon moon\n\\`\\`\\`\n`.trim();\n\n// ─── 可用模板列表 ───────────────────────────────────────────────────\n\nconst AVAILABLE_TEMPLATES = `\n### 可用模板\n\n• chart-bar-plain-text\n• chart-column-simple\n• chart-line-plain-text\n• chart-pie-compact-card\n• chart-pie-donut-pill-badge\n• chart-pie-donut-plain-text\n• chart-pie-plain-text\n• chart-wordcloud\n• compare-binary-horizontal-badge-card-arrow\n• compare-binary-horizontal-simple-fold\n• compare-binary-horizontal-underline-text-vs\n• compare-hierarchy-left-right-circle-node-pill-badge\n• compare-quadrant-quarter-circular\n• compare-quadrant-quarter-simple-card\n• compare-swot\n• hierarchy-mindmap-branch-gradient-capsule-item\n• hierarchy-mindmap-level-gradient-compact-card\n• hierarchy-structure\n• hierarchy-tree-curved-line-rounded-rect-node\n• hierarchy-tree-tech-style-badge-card\n• hierarchy-tree-tech-style-capsule-item\n• list-column-done-list\n• list-column-simple-vertical-arrow\n• list-column-vertical-icon-arrow\n• list-grid-badge-card\n• list-grid-candy-card-lite\n• list-grid-ribbon-card\n• list-row-horizontal-icon-arrow\n• list-sector-plain-text\n• list-waterfall-badge-card\n• list-waterfall-compact-card\n• list-zigzag-down-compact-card\n• list-zigzag-down-simple\n• list-zigzag-up-compact-card\n• list-zigzag-up-simple\n• relation-dagre-flow-tb-animated-badge-card\n• relation-dagre-flow-tb-animated-simple-circle-node\n• relation-dagre-flow-tb-badge-card\n• relation-dagre-flow-tb-simple-circle-node\n• sequence-ascending-stairs-3d-underline-text\n• sequence-ascending-steps\n• sequence-circular-simple\n• sequence-color-snake-steps-horizontal-icon-line\n• sequence-cylinders-3d-simple\n• sequence-filter-mesh-simple\n• sequence-funnel-simple\n• sequence-horizontal-zigzag-underline-text\n• sequence-mountain-underline-text\n• sequence-pyramid-simple\n• sequence-roadmap-vertical-plain-text\n• sequence-roadmap-vertical-simple\n• sequence-snake-steps-compact-card\n• sequence-snake-steps-simple\n• sequence-snake-steps-underline-text\n• sequence-stairs-front-compact-card\n• sequence-stairs-front-pill-badge\n• sequence-timeline-rounded-rect-node\n• sequence-timeline-simple\n• sequence-zigzag-pucks-3d-simple\n• sequence-zigzag-steps-underline-text\n\n模板选择建议:\n• 严格顺序(流程/步骤/发展趋势)→ sequence-*\n - 时间线 → sequence-timeline-*\n - 阶梯图 → sequence-stairs-*\n - 路线图 → sequence-roadmap-vertical-*\n - 折线路径 → sequence-zigzag-*\n - 环形进度 → sequence-circular-simple\n - 彩色蛇形步骤 → sequence-color-snake-steps-*\n - 金字塔 → sequence-pyramid-simple\n• 观点列举 → list-row-* 或 list-column-*\n• 二元对比(利弊)→ compare-binary-*\n• SWOT → compare-swot\n• 层级结构(树图)→ hierarchy-tree-*\n• 数据图表 → chart-*\n• 象限分析 → quadrant-*\n• 网格列表(要点)→ list-grid-*\n• 关系展示 → relation-*\n• 词云 → chart-wordcloud\n• 思维导图 → hierarchy-mindmap-*\n`.trim();\n\n// ─── 完整示例 ───────────────────────────────────────────────────────\n\nconst FULL_EXAMPLE = `\n### 完整示例\n\n绘制互联网技术演进信息图:\n\\`\\`\\`\ninfographic list-row-horizontal-icon-arrow\ndata\n title Internet Technology Evolution\n desc From Web 1.0 to AI era, key milestones\n lists\n - time 1991\n label Web 1.0\n desc Tim Berners-Lee published the first website, opening the Internet era\n icon web\n - time 2004\n label Web 2.0\n desc Social media and user-generated content become mainstream\n icon account multiple\n - time 2007\n label Mobile\n desc iPhone released, smartphone changes the world\n icon cellphone\n - time 2015\n label Cloud Native\n desc Containerization and microservices architecture are widely used\n icon cloud\n - time 2020\n label Low Code\n desc Visual development lowers the technology threshold\n icon application brackets\n - time 2023\n label AI Large Model\n desc ChatGPT ignites the generative AI revolution\n icon brain\n\\`\\`\\`\n`.trim();\n\n// ─── 导出:信息图创建者系统提示词 ───────────────────────────────────\n\n/**\n * 信息图创建者的完整 System Prompt。\n * 用于指导 LLM 根据用户自然语言需求,生成合法的 AntV Infographic DSL 语法。\n */\nexport const INFOGRAPHIC_CREATOR_SYSTEM_PROMPT = `\n你是一位专业的信息图设计专家。你的任务是根据用户的需求,生成 AntV Infographic DSL 语法来创建精美的信息图。\n\n信息图(Infographic)将数据、信息与知识转化为可感知的视觉语言。它结合视觉设计与数据可视化,用直观符号压缩复杂信息,帮助受众快速理解并记住要点。\n\nInfographic = Information Structure + Visual Expression\n\n你需要严格遵守以下规范来生成 AntV Infographic 语法:\n\n${SYNTAX_SPEC}\n\n${DATA_EXAMPLES}\n\n${AVAILABLE_TEMPLATES}\n\n${FULL_EXAMPLE}\n\n## 重要约束\n\n1. **只输出纯 DSL 语法**:不要输出任何 Markdown 代码块标记(如 \\\\\\`\\\\\\`\\\\\\`)、解释性文字、JSON 或其他格式。直接输出以 \\`infographic <template-name>\\` 开头的 DSL 文本。\n2. **必须尊重用户输入语言**:如果用户使用中文描述需求,DSL 中所有文本内容(title、desc、label 等)也必须使用中文。\n3. **选择最合适的模板**:根据用户需求的信息结构(列表、流程、对比、层级等)选择最匹配的模板。\n4. **数据完整性**:确保生成的数据结构完整,包含 title、desc 以及与模板匹配的主数据字段。\n5. **配色和主题**:除非用户特别要求,否则使用默认配色。\n`.trim();\n\n/**\n * 自我修正提示词模板。\n * 当 SSR 渲染失败时,将报错信息注入后发给 LLM 进行修正。\n */\n/**\n * 文件上下文提示词模板。\n * 当用户通过 -f 参数传入本地文件时,将文件内容和用户指令组装为完整的 Prompt。\n */\nexport const FILE_CONTEXT_PROMPT_TEMPLATE = `\n以下是提供的参考资料:\n<context>\n{fileContent}\n</context>\n\n请根据上述资料,完成以下用户需求:\n{userPrompt}\n`.trim();\n\nexport const SELF_CORRECTION_PROMPT_TEMPLATE = `\n之前你生成的 AntV Infographic DSL 语法在渲染时出错了。\n\n报错信息:\n{error}\n\n之前生成的语法:\n{syntax}\n\n请修正语法并重新输出。要求:\n1. 只输出修正后的纯 DSL 语法,不要添加任何解释或 Markdown 格式。\n2. 确保第一行是 \\`infographic <template-name>\\`。\n3. 确保数据结构符合所选模板的规范。\n4. 修复导致错误的具体问题。\n`.trim();\n","import ora, { type Ora } from \"ora\";\n\n/**\n * 终端 Loading 动画封装。\n * 提供 start / succeed / fail / update 等便捷方法。\n */\n\nlet currentSpinner: Ora | null = null;\n\nexport function startSpinner(text: string): Ora {\n // 如果之前有未关闭的 spinner,先停掉\n if (currentSpinner?.isSpinning) {\n currentSpinner.stop();\n }\n currentSpinner = ora({ text, stream: process.stderr }).start();\n return currentSpinner;\n}\n\nexport function succeedSpinner(text?: string): void {\n currentSpinner?.succeed(text);\n currentSpinner = null;\n}\n\nexport function failSpinner(text?: string): void {\n currentSpinner?.fail(text);\n currentSpinner = null;\n}\n\nexport function updateSpinner(text: string): void {\n if (currentSpinner) {\n currentSpinner.text = text;\n }\n}\n\nexport function stopSpinner(): void {\n currentSpinner?.stop();\n currentSpinner = null;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { renderToString } from \"@antv/infographic/ssr\";\nimport { getConfig } from \"../config/index.js\";\n\n/**\n * 生成带时间戳的默认文件名。\n * 格式:infographic-YYYYMMDD-HHMMSS.svg\n * 例如:infographic-20231024-153022.svg\n */\nfunction generateDefaultFilename(): string {\n const now = new Date();\n const pad = (n: number) => String(n).padStart(2, \"0\");\n const date = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}`;\n const time = `${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;\n return `infographic-${date}-${time}.svg`;\n}\n\n/**\n * 渲染结果\n */\nexport interface RenderResult {\n /** 生成的 SVG 字符串 */\n svg: string;\n}\n\n/**\n * 使用 @antv/infographic SSR 将 DSL 语法渲染为 SVG 字符串。\n *\n * @param syntax AntV Infographic DSL 语法文本\n * @returns SVG 字符串\n * @throws 渲染失败时抛出带有详细错误信息的 Error\n */\nexport async function renderDSLToSVG(syntax: string): Promise<string> {\n try {\n const svgString = await renderToString(syntax);\n if (!svgString || svgString.trim().length === 0) {\n throw new Error(\"renderToString 返回了空的 SVG 内容\");\n }\n return svgString;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`SSR 渲染失败: ${message}`);\n }\n}\n\n/**\n * 将 SVG 字符串写入指定路径。\n * 自动创建所需的中间目录。\n *\n * @param outputPath 输出文件路径\n * @param svgContent SVG 字符串内容\n */\nexport async function writeSVGFile(\n outputPath: string,\n svgContent: string,\n): Promise<void> {\n const absolutePath = path.resolve(outputPath);\n const dir = path.dirname(absolutePath);\n\n // 确保输出目录存在\n await fs.mkdir(dir, { recursive: true });\n await fs.writeFile(absolutePath, svgContent, \"utf-8\");\n}\n\n/**\n * 将 DSL 语法文本写入指定路径。\n * 自动创建所需的中间目录。\n *\n * @param outputPath 输出文件路径\n * @param dslContent DSL 语法字符串\n */\nexport async function writeDSLFile(\n outputPath: string,\n dslContent: string,\n): Promise<void> {\n const absolutePath = path.resolve(outputPath);\n const dir = path.dirname(absolutePath);\n\n // 确保输出目录存在\n await fs.mkdir(dir, { recursive: true });\n await fs.writeFile(absolutePath, dslContent, \"utf-8\");\n}\n\n/**\n * 根据用户输入的输出路径,补全默认值。\n * 支持配置默认输出目录,如果用户未指定,默认为 defaultOutputDir 下的\n * infographic-YYYYMMDD-HHMMSS.svg(使用当前时间戳)。\n *\n * @param userOutput 用户指定的输出路径(可选)\n * @returns 完整的输出路径(绝对路径或相对路径)\n */\nexport function resolveOutputPath(userOutput?: string): string {\n const defaultDir = getConfig(\"defaultOutputDir\") || \".\";\n\n if (userOutput) {\n // 用户指定了输出路径\n // 确保扩展名是 .svg\n const withExt = userOutput.endsWith(\".svg\")\n ? userOutput\n : userOutput + \".svg\";\n\n // 如果是绝对路径,直接返回;否则相对于默认目录\n if (path.isAbsolute(withExt)) {\n return withExt;\n }\n return path.resolve(defaultDir, withExt);\n }\n\n // 用户未指定,使用默认输出目录 + 时间戳文件名\n return path.resolve(defaultDir, generateDefaultFilename());\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport mammoth from \"mammoth\";\nimport { PDFParse } from \"pdf-parse\";\nimport { t } from \"./i18n.js\";\nimport { getConfig } from \"../config/index.js\";\n\n/** 默认最大文件大小:10MB */\nconst DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;\n\n/** 默认最大文本字符数:30000 */\nconst DEFAULT_MAX_FILE_CHARS = 30000;\n\n/** 支持的文件扩展名 */\nconst SUPPORTED_EXTENSIONS = [\".md\", \".txt\", \".json\", \".csv\", \".docx\", \".pdf\"];\n\n/**\n * 按段落边界截断文本,保留结构完整性。\n */\nfunction truncateText(text: string, maxChars: number): string {\n if (text.length <= maxChars) return text;\n\n const truncated = text.slice(0, maxChars);\n // 退到最后一个换行符,避免截断在句子中间\n const lastNewline = truncated.lastIndexOf(\"\\n\");\n return lastNewline > maxChars * 0.8\n ? truncated.slice(0, lastNewline)\n : truncated;\n}\n\n/**\n * 根据文件后缀,智能提取文件中的纯文本。\n *\n * - 不调用 process.exit(),仅抛出异常,由上层处理。\n * - 自动检查文件大小,防止 DoS。\n * - 自动截断超长文本,返回 { text, truncated } 。\n *\n * @param filePath 文件路径(支持相对路径,内部会 resolve)\n * @returns 提取的纯文本和是否被截断的标志\n */\nexport async function extractTextFromFile(\n filePath: string,\n): Promise<{ text: string; truncated: boolean }> {\n const resolvedPath = path.resolve(filePath);\n const ext = path.extname(resolvedPath).toLowerCase();\n\n // 1. 检查扩展名是否支持\n if (!SUPPORTED_EXTENSIONS.includes(ext)) {\n throw new Error(\n t(\"unsupportedFileFormat\", {\n ext,\n supported: SUPPORTED_EXTENSIONS.join(\", \"),\n }),\n );\n }\n\n // 2. 检查文件是否存在 & 大小预检\n let stats: Awaited<ReturnType<typeof fs.stat>>;\n try {\n stats = await fs.stat(resolvedPath);\n } catch {\n throw new Error(t(\"fileNotFound\", { path: resolvedPath }));\n }\n\n if (stats.size > DEFAULT_MAX_FILE_SIZE) {\n throw new Error(\n t(\"fileTooLarge\", {\n size: (stats.size / 1024 / 1024).toFixed(1),\n }),\n );\n }\n\n // 3. 根据扩展名提取文本\n let rawText: string;\n\n if (ext === \".md\" || ext === \".txt\" || ext === \".json\" || ext === \".csv\") {\n rawText = await fs.readFile(resolvedPath, \"utf-8\");\n } else if (ext === \".docx\") {\n const buffer = await fs.readFile(resolvedPath);\n const result = await mammoth.extractRawText({ buffer });\n rawText = result.value;\n } else if (ext === \".pdf\") {\n const buffer = await fs.readFile(resolvedPath);\n const parser = new PDFParse({ data: buffer });\n const result = await parser.getText();\n rawText = result.text;\n } else {\n // 理论上不会走到这里,因为上面已经检查了扩展名\n throw new Error(\n t(\"unsupportedFileFormat\", {\n ext,\n supported: SUPPORTED_EXTENSIONS.join(\", \"),\n }),\n );\n }\n\n // 4. 截断超长内容\n const maxChars = getMaxFileChars();\n const truncated = rawText.length > maxChars;\n const text = truncated ? truncateText(rawText, maxChars) : rawText;\n\n return { text, truncated };\n}\n\n/**\n * 从配置中获取最大文件字符数,回退到默认值。\n */\nfunction getMaxFileChars(): number {\n const configured = getConfig(\"maxFileChars\");\n if (configured) {\n const parsed = parseInt(configured, 10);\n if (!isNaN(parsed) && parsed > 0) return parsed;\n }\n return DEFAULT_MAX_FILE_CHARS;\n}\n"],"mappings":"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","names":["pc","pc","program","path","fs","path","path","fs","path","path","fs","program","path","fs","fileURLToPath","__dirname","fileURLToPath"]}
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "infographic-gen",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "AI-powered CLI tool to generate AntV Infographic SVGs from natural language prompts",
5
5
  "license": "MIT",
6
- "author": "Your Name <your.email@example.com>",
6
+ "author": "xxMudCloudxx",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/yourusername/infographic-gen.git"
10
+ "url": "https://github.com/xxMudCloudxx/infographic-gen.git"
11
11
  },
12
- "homepage": "https://github.com/yourusername/infographic-gen#readme",
12
+ "homepage": "https://github.com/xxMudCloudxx/infographic-gen#readme",
13
13
  "bugs": {
14
- "url": "https://github.com/yourusername/infographic-gen/issues"
14
+ "url": "https://github.com/xxMudCloudxx/infographic-gen/issues"
15
15
  },
16
16
  "keywords": [
17
17
  "infographic",
@@ -47,12 +47,15 @@
47
47
  "@antv/infographic": "^0.2.14",
48
48
  "commander": "^13.1.0",
49
49
  "conf": "^13.1.0",
50
+ "mammoth": "^1.11.0",
50
51
  "openai": "^4.85.4",
51
52
  "ora": "^8.2.0",
53
+ "pdf-parse": "^2.4.5",
52
54
  "picocolors": "^1.1.1"
53
55
  },
54
56
  "devDependencies": {
55
57
  "@types/node": "^22.13.4",
58
+ "@types/pdf-parse": "^1.1.5",
56
59
  "tsup": "^8.4.0",
57
60
  "typescript": "^5.7.3",
58
61
  "vitest": "^4.0.18"