zengen 0.1.34 → 0.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/.github/workflows/bump-version.yml +112 -0
  2. package/.github/workflows/ci.yml +2 -2
  3. package/.github/workflows/pages.yml +1 -7
  4. package/.zen/meta.json +57 -0
  5. package/.zen/translations.json +51 -0
  6. package/dist/ai-client.d.ts +34 -0
  7. package/dist/ai-client.d.ts.map +1 -0
  8. package/dist/ai-client.js +180 -0
  9. package/dist/ai-client.js.map +1 -0
  10. package/dist/ai-processor.d.ts +51 -0
  11. package/dist/ai-processor.d.ts.map +1 -0
  12. package/dist/ai-processor.js +215 -0
  13. package/dist/ai-processor.js.map +1 -0
  14. package/dist/ai-service.d.ts +79 -0
  15. package/dist/ai-service.d.ts.map +1 -0
  16. package/dist/ai-service.js +257 -0
  17. package/dist/ai-service.js.map +1 -0
  18. package/dist/builder.d.ts +26 -2
  19. package/dist/builder.d.ts.map +1 -1
  20. package/dist/builder.js +420 -9
  21. package/dist/builder.js.map +1 -1
  22. package/dist/cli.js +45 -3
  23. package/dist/cli.js.map +1 -1
  24. package/dist/gitignore.d.ts +2 -1
  25. package/dist/gitignore.d.ts.map +1 -1
  26. package/dist/gitignore.js +21 -3
  27. package/dist/gitignore.js.map +1 -1
  28. package/dist/gitignore.test.js +82 -17
  29. package/dist/gitignore.test.js.map +1 -1
  30. package/dist/markdown.d.ts +6 -1
  31. package/dist/markdown.d.ts.map +1 -1
  32. package/dist/markdown.js +31 -9
  33. package/dist/markdown.js.map +1 -1
  34. package/dist/navigation.js +5 -5
  35. package/dist/navigation.js.map +1 -1
  36. package/dist/scanner.d.ts +26 -0
  37. package/dist/scanner.d.ts.map +1 -0
  38. package/dist/scanner.js +190 -0
  39. package/dist/scanner.js.map +1 -0
  40. package/dist/template.d.ts +6 -2
  41. package/dist/template.d.ts.map +1 -1
  42. package/dist/template.js +58 -9
  43. package/dist/template.js.map +1 -1
  44. package/dist/translation-service.d.ts +72 -0
  45. package/dist/translation-service.d.ts.map +1 -0
  46. package/dist/translation-service.js +291 -0
  47. package/dist/translation-service.js.map +1 -0
  48. package/dist/types.d.ts +35 -4
  49. package/dist/types.d.ts.map +1 -1
  50. package/docs/advanced-usage.md +39 -0
  51. package/docs/getting-started.md +26 -0
  52. package/docs/guides/best-practices.md +0 -113
  53. package/docs/guides/config.md +0 -233
  54. package/package.json +3 -2
  55. package/src/ai-client.ts +227 -0
  56. package/src/ai-processor.ts +243 -0
  57. package/src/ai-service.ts +281 -0
  58. package/src/builder.ts +543 -10
  59. package/src/cli.ts +49 -3
  60. package/src/gitignore.test.ts +82 -17
  61. package/src/gitignore.ts +23 -3
  62. package/src/markdown.ts +39 -11
  63. package/src/navigation.ts +5 -5
  64. package/src/scanner.ts +180 -0
  65. package/src/template.ts +69 -9
  66. package/src/translation-service.ts +350 -0
  67. package/src/types.ts +39 -3
  68. package/test-multilang.js +44 -0
  69. package/docs/ci/github-ci-cd.md +0 -127
  70. package/docs/guides/api.md +0 -277
@@ -19,9 +19,6 @@ npx zengen build --watch --serve
19
19
  # 自定义端口
20
20
  npx zengen build --watch --serve --port 8080
21
21
 
22
- # 使用配置文件
23
- npx zengen build --config zen.config.json
24
-
25
22
  # 清理输出目录
26
23
  npx zengen build --clean
27
24
 
@@ -49,238 +46,8 @@ npx zengen
49
46
  | `--serve` | `-s` | 启动开发服务器(需要 `--watch`) | `false` |
50
47
  | `--port` | `-p` | 开发服务器端口 | `3000` |
51
48
  | `--host` | | 开发服务器主机 | `localhost` |
52
- | `--template` | `-t` | 自定义模板文件路径 | 内置模板 |
53
- | `--config` | `-c` | 配置文件路径 | 无 |
54
49
  | `--verbose` | `-v` | 显示详细日志 | `false` |
55
50
  | `--clean` | | 清理输出目录 | `false` |
56
51
  | `--base-url` | | 站点基础 URL | 无 |
57
52
  | `--help` | `-h` | 显示帮助信息 | 无 |
58
53
 
59
- ## 配置文件
60
-
61
- ### 配置文件格式
62
-
63
- 在项目根目录创建 `zen.config.json` 文件:
64
-
65
- ```json
66
- {
67
- "template": "./custom-template.html",
68
- "baseUrl": "/my-docs",
69
- "i18n": {
70
- "sourceLang": "zh-CN",
71
- "targetLangs": ["en-US", "ja-JP"],
72
- "apiKey": "your-openai-api-key"
73
- },
74
- "includePattern": "**/*.md",
75
- "excludePattern": "**/_*.md"
76
- }
77
- ```
78
-
79
- ### 配置项说明
80
-
81
- #### `template`
82
-
83
- - **类型**: `string`
84
- - **描述**: 自定义模板文件路径
85
- - **示例**: `"./templates/custom.html"`
86
-
87
- #### `baseUrl`
88
-
89
- - **类型**: `string`
90
- - **描述**: 站点基础 URL,用于生成绝对路径
91
- - **示例**: `"/docs"`, `"/my-project"`
92
-
93
- #### `i18n`
94
-
95
- - **类型**: `object`
96
- - **描述**: 多语言翻译配置
97
- - **字段**:
98
- - `sourceLang`: 源语言代码(如 `"zh-CN"`)
99
- - `targetLangs`: 目标语言代码数组(如 `["en-US", "ja-JP"]`)
100
- - `apiKey`: OpenAI API 密钥(可选,从环境变量读取)
101
-
102
- #### `includePattern`
103
-
104
- - **类型**: `string`
105
- - **描述**: 包含的文件模式(glob 语法)
106
- - **默认**: `"**/*.md"`
107
- - **示例**: `"**/*.{md,markdown}"`
108
-
109
- #### `excludePattern`
110
-
111
- - **类型**: `string`
112
- - **描述**: 排除的文件模式(glob 语法)
113
- - **默认**: 无
114
- - **示例**: `"**/_*.md"`, `"**/node_modules/**"`
115
-
116
- ## 模板配置
117
-
118
- ### 默认模板
119
-
120
- ZEN 使用内置的极简模板,包含:
121
-
122
- - 响应式布局
123
- - 代码高亮(使用 highlight.js)
124
- - 导航菜单
125
- - 深色/浅色主题切换
126
-
127
- ### 自定义模板
128
-
129
- 创建自定义模板文件 `custom-template.html`:
130
-
131
- ```html
132
- <!DOCTYPE html>
133
- <html lang="zh-CN">
134
- <head>
135
- <meta charset="UTF-8" />
136
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
137
- <title>{{title}}</title>
138
- <link rel="stylesheet" href="/styles.css" />
139
- </head>
140
- <body>
141
- <nav class="sidebar">{{navigation}}</nav>
142
- <main class="content">{{content}}</main>
143
- <footer>
144
- <p>由 ZEN 生成</p>
145
- </footer>
146
- </body>
147
- </html>
148
- ```
149
-
150
- **可用模板变量:**
151
-
152
- | 变量 | 描述 |
153
- | ----------------- | ------------------------- |
154
- | `{{title}}` | 页面标题 |
155
- | `{{content}}` | 转换后的 HTML 内容 |
156
- | `{{navigation}}` | 导航菜单 HTML |
157
- | `{{metadata}}` | 页面元数据(JSON 字符串) |
158
- | `{{currentPath}}` | 当前页面路径 |
159
-
160
- ### 模板位置
161
-
162
- 1. **命令行指定**: `--template ./my-template.html`
163
- 2. **配置文件指定**: `"template": "./my-template.html"`
164
- 3. **默认模板**: 内置模板
165
-
166
- ## 多语言配置
167
-
168
- ### 基本配置
169
-
170
- ```json
171
- {
172
- "i18n": {
173
- "sourceLang": "zh-CN",
174
- "targetLangs": ["en-US", "ja-JP"],
175
- "apiKey": "sk-..."
176
- }
177
- }
178
- ```
179
-
180
- ### 环境变量
181
-
182
- 也可以使用环境变量配置 API 密钥:
183
-
184
- ```bash
185
- export OPENAI_API_KEY="sk-..."
186
- npx zengen build
187
- ```
188
-
189
- ### 翻译文件结构
190
-
191
- ```
192
- docs/
193
- ├── index.md # 源文件(中文)
194
- ├── index.en-US.md # 英文翻译(自动生成)
195
- └── index.ja-JP.md # 日文翻译(自动生成)
196
- ```
197
-
198
- ### 翻译流程
199
-
200
- 1. **首次构建**: 翻译所有内容
201
- 2. **增量更新**: 只翻译新增或修改的内容
202
- 3. **手动更新**: 可以直接编辑翻译文件
203
-
204
- ## 文件处理配置
205
-
206
- ### 文件过滤
207
-
208
- ```json
209
- {
210
- "includePattern": "**/*.{md,markdown}",
211
- "excludePattern": "**/{_*,node_modules}/**"
212
- }
213
- ```
214
-
215
- ### 元数据提取
216
-
217
- 在 Markdown 文件顶部添加 YAML frontmatter:
218
-
219
- ```yaml
220
- ---
221
- title: 页面标题
222
- description: 页面描述
223
- author: 作者名
224
- date: 2024-01-01
225
- tags: [文档, 教程]
226
- ---
227
- ```
228
-
229
- ### 自定义处理器
230
-
231
- 通过 API 使用自定义处理器,配置文件不支持直接配置处理器。
232
-
233
- ## 开发服务器配置
234
-
235
- ### 基本配置
236
-
237
- ```bash
238
- # 启动开发服务器
239
- npx zengen build --watch --serve
240
-
241
- # 自定义端口和主机
242
- npx zengen build --watch --serve --port 8080 --host 0.0.0.0
243
- ```
244
-
245
- ### 服务器特性
246
-
247
- - 自动重载:文件变化时自动刷新浏览器
248
- - 静态文件服务:提供构建的文档站点
249
- - 实时预览:即时查看修改效果
250
-
251
- ## 环境配置
252
-
253
- ### Node.js 版本
254
-
255
- - **最低版本**: Node.js 16.x
256
- - **推荐版本**: Node.js 18.x 或更高
257
-
258
- ### 内存要求
259
-
260
- - 小型项目:~100MB
261
- - 大型项目:~500MB(包含翻译)
262
-
263
- ### 网络要求
264
-
265
- - 使用 AI 翻译时需要网络连接
266
- - 纯本地构建无需网络
267
-
268
- ## 最佳实践
269
-
270
- ### 配置文件管理
271
-
272
- 1. **版本控制**: 将 `zen.config.json` 加入版本控制
273
- 2. **环境变量**: 敏感信息(如 API 密钥)使用环境变量
274
- 3. **模板分离**: 自定义模板单独存放
275
-
276
- ### 性能优化
277
-
278
- 1. **增量构建**: 使用 `--watch` 模式开发
279
- 2. **文件组织**: 合理组织文档结构
280
- 3. **缓存利用**: ZEN 会自动缓存处理结果
281
-
282
- ### 错误处理
283
-
284
- 1. **详细日志**: 使用 `--verbose` 查看详细错误信息
285
- 2. **配置验证**: ZEN 会自动验证配置
286
- 3. **错误恢复**: 构建失败时会保留上次成功的结果
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zengen",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "description": "ZEN - A minimalist Markdown documentation site builder",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -25,7 +25,7 @@
25
25
  "documentation",
26
26
  "cli"
27
27
  ],
28
- "author": "ZEN Team",
28
+ "author": "zccz14",
29
29
  "license": "ISC",
30
30
  "repository": {
31
31
  "type": "git",
@@ -54,6 +54,7 @@
54
54
  "dependencies": {
55
55
  "chokidar": "^5.0.0",
56
56
  "clipanion": "^4.0.0-rc.4",
57
+ "dotenv": "^16.4.7",
57
58
  "express": "^4.21.2",
58
59
  "highlight.js": "^11.11.1",
59
60
  "marked": "^17.0.1",
@@ -0,0 +1,227 @@
1
+ import { AIMetadata } from './types';
2
+ import { AIService, AIConfig } from './ai-service';
3
+
4
+ /**
5
+ * OpenAI 兼容 API 响应接口
6
+ */
7
+ interface OpenAIResponse {
8
+ id: string;
9
+ object: string;
10
+ created: number;
11
+ model: string;
12
+ choices: Array<{
13
+ index: number;
14
+ message: {
15
+ role: string;
16
+ content: string;
17
+ };
18
+ finish_reason: string;
19
+ }>;
20
+ usage: {
21
+ prompt_tokens: number;
22
+ completion_tokens: number;
23
+ total_tokens: number;
24
+ };
25
+ }
26
+
27
+ /**
28
+ * AI 客户端类 - 使用 fetch 调用 OpenAI 兼容 API
29
+ */
30
+ export class AIClient {
31
+ private aiService: AIService;
32
+
33
+ constructor(aiService: AIService) {
34
+ this.aiService = aiService;
35
+ }
36
+
37
+ /**
38
+ * 调用 AI 模型提取文档 metadata
39
+ */
40
+ async extractMetadata(content: string, filePath: string): Promise<AIMetadata | null> {
41
+ const config = this.aiService.getConfig();
42
+
43
+ if (!config.enabled || !config.apiKey) {
44
+ console.log(`⚠️ AI feature is disabled or API key not configured for: ${filePath}`);
45
+ return null;
46
+ }
47
+
48
+ try {
49
+ console.log(`🤖 Extracting AI metadata for: ${filePath}`);
50
+
51
+ const prompt = this.buildMetadataPrompt(content);
52
+ const response = await this.callOpenAIAPI(prompt, config);
53
+
54
+ if (!response) {
55
+ console.warn(`⚠️ Failed to extract metadata for: ${filePath}`);
56
+ return null;
57
+ }
58
+
59
+ const metadata = this.parseMetadataResponse(response.choices[0].message.content);
60
+
61
+ // 添加 tokens 使用情况
62
+ metadata.tokens_used = {
63
+ prompt: response.usage.prompt_tokens,
64
+ completion: response.usage.completion_tokens,
65
+ total: response.usage.total_tokens,
66
+ };
67
+
68
+ // 打印 tokens 使用情况
69
+ this.aiService.logTokenUsage(filePath, metadata.tokens_used);
70
+
71
+ return metadata;
72
+ } catch (error) {
73
+ console.error(`❌ Failed to extract AI metadata for ${filePath}:`, error);
74
+ return null;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * 构建提取 metadata 的 prompt
80
+ */
81
+ private buildMetadataPrompt(content: string): string {
82
+ // 限制内容长度以避免 token 超限
83
+ const maxContentLength = 8000;
84
+ const truncatedContent =
85
+ content.length > maxContentLength
86
+ ? content.substring(0, maxContentLength) + '... [内容已截断]'
87
+ : content;
88
+
89
+ return `请分析以下文档内容,提取以下信息并返回 JSON 格式:
90
+
91
+ 文档内容:
92
+ """
93
+ ${truncatedContent}
94
+ """
95
+
96
+ 请提取:
97
+ 1. title: 文档的标题(简洁明了,不超过 20 个字)
98
+ 2. summary: 文档摘要(控制在 100 字以内,概括主要内容)
99
+ 3. tags: 关键词列表(3-8 个关键词,使用中文或英文)
100
+ 4. inferred_date: 文档中隐含的创建日期(如果有的话,格式:YYYY-MM-DD,没有就留空字符串)
101
+ 5. inferred_lang: 文档使用的语言代码(例如:zh-Hans 表示简体中文,en-US 表示美式英语)
102
+
103
+ 请严格按照以下 JSON 格式返回,不要包含任何其他文本:
104
+ {
105
+ "title": "文档标题",
106
+ "summary": "文档摘要...",
107
+ "tags": ["关键词1", "关键词2", "关键词3"],
108
+ "inferred_date": "2023-01-01",
109
+ "inferred_lang": "zh-Hans"
110
+ }`;
111
+ }
112
+
113
+ /**
114
+ * 调用 OpenAI 兼容 API
115
+ */
116
+ private async callOpenAIAPI(prompt: string, config: AIConfig): Promise<OpenAIResponse | null> {
117
+ try {
118
+ const response = await fetch(`${config.baseUrl}/chat/completions`, {
119
+ method: 'POST',
120
+ headers: {
121
+ 'Content-Type': 'application/json',
122
+ Authorization: `Bearer ${config.apiKey}`,
123
+ },
124
+ body: JSON.stringify({
125
+ model: config.model,
126
+ messages: [
127
+ {
128
+ role: 'system',
129
+ content:
130
+ '你是一个专业的文档分析助手,擅长从文档中提取结构化信息。请严格按照要求的 JSON 格式返回结果。',
131
+ },
132
+ {
133
+ role: 'user',
134
+ content: prompt,
135
+ },
136
+ ],
137
+ temperature: config.temperature,
138
+ max_tokens: config.maxTokens,
139
+ response_format: { type: 'json_object' },
140
+ }),
141
+ });
142
+
143
+ if (!response.ok) {
144
+ const errorText = await response.text();
145
+ console.error(`❌ OpenAI API error (${response.status}):`, errorText);
146
+ return null;
147
+ }
148
+
149
+ const data: OpenAIResponse = await response.json();
150
+ return data;
151
+ } catch (error) {
152
+ console.error('❌ Failed to call OpenAI API:', error);
153
+ return null;
154
+ }
155
+ }
156
+
157
+ /**
158
+ * 解析 AI 返回的 metadata
159
+ */
160
+ private parseMetadataResponse(responseContent: string): AIMetadata {
161
+ try {
162
+ const metadata = JSON.parse(responseContent);
163
+
164
+ // 验证和清理数据
165
+ return {
166
+ title: metadata.title?.trim() || '未命名文档',
167
+ summary: metadata.summary?.trim() || '',
168
+ tags: Array.isArray(metadata.tags)
169
+ ? metadata.tags.map((tag: string) => tag.trim()).filter(Boolean)
170
+ : [],
171
+ inferred_date: metadata.inferred_date?.trim() || undefined,
172
+ inferred_lang: metadata.inferred_lang?.trim() || 'zh-Hans',
173
+ };
174
+ } catch (error) {
175
+ console.error('❌ Failed to parse AI response:', error, 'Response:', responseContent);
176
+
177
+ // 返回默认值
178
+ return {
179
+ title: '解析失败',
180
+ summary: 'AI 响应解析失败',
181
+ tags: ['error'],
182
+ inferred_lang: 'zh-Hans',
183
+ };
184
+ }
185
+ }
186
+
187
+ /**
188
+ * 批量处理文件
189
+ */
190
+ async processFiles(
191
+ files: Array<{ content: string; path: string; hash: string }>
192
+ ): Promise<Map<string, AIMetadata>> {
193
+ const results = new Map<string, AIMetadata>();
194
+ const config = this.aiService.getConfig();
195
+
196
+ if (!config.enabled || !config.apiKey) {
197
+ console.log('⚠️ AI feature is disabled or API key not configured');
198
+ return results;
199
+ }
200
+
201
+ console.log(`🤖 Processing ${files.length} files with AI...`);
202
+
203
+ for (const file of files) {
204
+ try {
205
+ // 检查缓存
206
+ const cachedMetadata = await this.aiService.getCachedMetadata(file.hash, file.path);
207
+ if (cachedMetadata) {
208
+ results.set(file.path, cachedMetadata);
209
+ continue;
210
+ }
211
+
212
+ // 调用 AI 提取 metadata
213
+ const metadata = await this.extractMetadata(file.content, file.path);
214
+ if (metadata) {
215
+ results.set(file.path, metadata);
216
+
217
+ // 缓存结果
218
+ await this.aiService.cacheMetadata(file.hash, file.path, metadata);
219
+ }
220
+ } catch (error) {
221
+ console.error(`❌ Failed to process file ${file.path}:`, error);
222
+ }
223
+ }
224
+
225
+ return results;
226
+ }
227
+ }