zengen 0.1.36 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/.github/workflows/pages.yml +1 -1
  2. package/.zen/meta.json +128 -30
  3. package/.zen/src/en-US/01d04f7c17b4a541ead9d759d877b30b403e15b849182a49eb1f62bd29ecd18c.md +120 -0
  4. package/.zen/src/en-US/1b798c44a4f353e47296ca83d5905e37e6aba3e90bbd9bc3b3d34fc12059a2ca.md +75 -0
  5. package/.zen/src/en-US/1e96be58d76c60056b708eb5bd8b8b81d7b5845d9cfe0b879d85068a5f11df3a.md +189 -0
  6. package/.zen/src/en-US/5ec990146b35e00de2630559126ee07f7cdcddeb23b0e8cab3d85b4181353e26.md +53 -0
  7. package/.zen/src/en-US/6124ea88edec5bde737b26b21f71ecfeffe4e73151784856edf813ee231a4baa.md +11 -0
  8. package/.zen/src/en-US/80ae9bed74fc6348a7c1fe9f33e86b65f5d919169721f77bcf0e1bc29fbdb4f9.md +61 -0
  9. package/.zen/src/en-US/f0c2799126931ccd113a0c45b1e623870b0d4f4f400becf6dd877da8f1011517.md +41 -0
  10. package/.zen/src/en-US/fdfca9b960d0eaa8b2b96fe988ead7481d2c0b16f66ebc94fb477139b4178cdc.md +65 -0
  11. package/.zen/src/zh-Hans/01d04f7c17b4a541ead9d759d877b30b403e15b849182a49eb1f62bd29ecd18c.md +120 -0
  12. package/.zen/src/zh-Hans/1b798c44a4f353e47296ca83d5905e37e6aba3e90bbd9bc3b3d34fc12059a2ca.md +77 -0
  13. package/.zen/src/zh-Hans/1e96be58d76c60056b708eb5bd8b8b81d7b5845d9cfe0b879d85068a5f11df3a.md +189 -0
  14. package/.zen/src/zh-Hans/5ec990146b35e00de2630559126ee07f7cdcddeb23b0e8cab3d85b4181353e26.md +55 -0
  15. package/.zen/src/zh-Hans/6124ea88edec5bde737b26b21f71ecfeffe4e73151784856edf813ee231a4baa.md +1 -0
  16. package/.zen/src/zh-Hans/6ad8db715a1b60613fe934fefb29fa981ecad9b63145593accff144d73b44bde.md +175 -0
  17. package/.zen/src/zh-Hans/80ae9bed74fc6348a7c1fe9f33e86b65f5d919169721f77bcf0e1bc29fbdb4f9.md +63 -0
  18. package/.zen/src/zh-Hans/a1580f71c6c6c1ff4a314be72d410a8507af2f087d56360c7f5048d349c21953.md +48 -0
  19. package/.zen/src/zh-Hans/d49012f98c4367b34034063400e2f7826bf0615952210c82396920172d468e2c.md +107 -0
  20. package/.zen/src/zh-Hans/f0c2799126931ccd113a0c45b1e623870b0d4f4f400becf6dd877da8f1011517.md +41 -0
  21. package/.zen/src/zh-Hans/fdfca9b960d0eaa8b2b96fe988ead7481d2c0b16f66ebc94fb477139b4178cdc.md +65 -0
  22. package/assets/templates/default/layout.html +274 -0
  23. package/dist/ai/extractMetadataFromMarkdown.d.ts +8 -0
  24. package/dist/ai/extractMetadataFromMarkdown.d.ts.map +1 -0
  25. package/dist/ai/extractMetadataFromMarkdown.js +88 -0
  26. package/dist/ai/extractMetadataFromMarkdown.js.map +1 -0
  27. package/dist/ai/translateMarkdown.d.ts +8 -0
  28. package/dist/ai/translateMarkdown.d.ts.map +1 -0
  29. package/dist/ai/translateMarkdown.js +29 -0
  30. package/dist/ai/translateMarkdown.js.map +1 -0
  31. package/dist/build/pipeline.d.ts +6 -0
  32. package/dist/build/pipeline.d.ts.map +1 -0
  33. package/dist/build/pipeline.js +218 -0
  34. package/dist/build/pipeline.js.map +1 -0
  35. package/dist/cli.js +10 -118
  36. package/dist/cli.js.map +1 -1
  37. package/dist/findEntries.d.ts +10 -0
  38. package/dist/findEntries.d.ts.map +1 -0
  39. package/dist/findEntries.js +38 -0
  40. package/dist/findEntries.js.map +1 -0
  41. package/dist/index.d.ts +1 -32
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +1 -35
  44. package/dist/index.js.map +1 -1
  45. package/dist/metadata.d.ts +14 -0
  46. package/dist/metadata.d.ts.map +1 -0
  47. package/dist/metadata.js +78 -0
  48. package/dist/metadata.js.map +1 -0
  49. package/dist/paths.d.ts +6 -0
  50. package/dist/paths.d.ts.map +1 -0
  51. package/dist/paths.js +10 -0
  52. package/dist/paths.js.map +1 -0
  53. package/dist/process/extractMetadataByAI.d.ts +5 -0
  54. package/dist/process/extractMetadataByAI.d.ts.map +1 -0
  55. package/dist/process/extractMetadataByAI.js +31 -0
  56. package/dist/process/extractMetadataByAI.js.map +1 -0
  57. package/dist/process/template.d.ts +5 -0
  58. package/dist/process/template.d.ts.map +1 -0
  59. package/dist/process/template.js +188 -0
  60. package/dist/process/template.js.map +1 -0
  61. package/dist/scan/files.d.ts +7 -0
  62. package/dist/scan/files.d.ts.map +1 -0
  63. package/dist/scan/files.js +54 -0
  64. package/dist/scan/files.js.map +1 -0
  65. package/dist/services/openai.d.ts +41 -0
  66. package/dist/services/openai.d.ts.map +1 -0
  67. package/dist/services/openai.js +54 -0
  68. package/dist/services/openai.js.map +1 -0
  69. package/dist/types.d.ts +16 -67
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/utils/convertMarkdownToHtml.d.ts +7 -0
  72. package/dist/utils/convertMarkdownToHtml.d.ts.map +1 -0
  73. package/dist/utils/convertMarkdownToHtml.js +39 -0
  74. package/dist/utils/convertMarkdownToHtml.js.map +1 -0
  75. package/dist/utils/frontmatter.d.ts +6 -0
  76. package/dist/utils/frontmatter.d.ts.map +1 -0
  77. package/dist/utils/frontmatter.js +22 -0
  78. package/dist/utils/frontmatter.js.map +1 -0
  79. package/docs/deployment/github-pages.md +1 -2
  80. package/docs/guides/best-practices.md +4 -4
  81. package/docs/guides/config.md +0 -5
  82. package/package.json +4 -2
  83. package/src/ai/extractMetadataFromMarkdown.ts +95 -0
  84. package/src/ai/translateMarkdown.ts +29 -0
  85. package/src/build/pipeline.ts +211 -0
  86. package/src/cli.ts +10 -132
  87. package/src/findEntries.ts +37 -0
  88. package/src/index.ts +1 -40
  89. package/src/metadata.ts +44 -0
  90. package/src/paths.ts +7 -0
  91. package/src/process/extractMetadataByAI.ts +29 -0
  92. package/src/process/template.ts +201 -0
  93. package/src/scan/files.ts +17 -0
  94. package/src/services/openai.ts +92 -0
  95. package/src/types.ts +18 -72
  96. package/src/utils/convertMarkdownToHtml.ts +32 -0
  97. package/src/utils/frontmatter.ts +18 -0
  98. package/.zen/translations.json +0 -51
  99. package/dist/ai-client.d.ts +0 -34
  100. package/dist/ai-client.d.ts.map +0 -1
  101. package/dist/ai-client.js +0 -180
  102. package/dist/ai-client.js.map +0 -1
  103. package/dist/ai-processor.d.ts +0 -51
  104. package/dist/ai-processor.d.ts.map +0 -1
  105. package/dist/ai-processor.js +0 -215
  106. package/dist/ai-processor.js.map +0 -1
  107. package/dist/ai-service.d.ts +0 -79
  108. package/dist/ai-service.d.ts.map +0 -1
  109. package/dist/ai-service.js +0 -257
  110. package/dist/ai-service.js.map +0 -1
  111. package/dist/builder.d.ts +0 -70
  112. package/dist/builder.d.ts.map +0 -1
  113. package/dist/builder.js +0 -854
  114. package/dist/builder.js.map +0 -1
  115. package/dist/gitignore.d.ts +0 -41
  116. package/dist/gitignore.d.ts.map +0 -1
  117. package/dist/gitignore.js +0 -202
  118. package/dist/gitignore.js.map +0 -1
  119. package/dist/gitignore.test.d.ts +0 -2
  120. package/dist/gitignore.test.d.ts.map +0 -1
  121. package/dist/gitignore.test.js +0 -309
  122. package/dist/gitignore.test.js.map +0 -1
  123. package/dist/markdown.d.ts +0 -35
  124. package/dist/markdown.d.ts.map +0 -1
  125. package/dist/markdown.js +0 -221
  126. package/dist/markdown.js.map +0 -1
  127. package/dist/navigation.d.ts +0 -46
  128. package/dist/navigation.d.ts.map +0 -1
  129. package/dist/navigation.js +0 -196
  130. package/dist/navigation.js.map +0 -1
  131. package/dist/scanner.d.ts +0 -26
  132. package/dist/scanner.d.ts.map +0 -1
  133. package/dist/scanner.js +0 -190
  134. package/dist/scanner.js.map +0 -1
  135. package/dist/template.d.ts +0 -33
  136. package/dist/template.d.ts.map +0 -1
  137. package/dist/template.js +0 -434
  138. package/dist/template.js.map +0 -1
  139. package/dist/translation-service.d.ts +0 -72
  140. package/dist/translation-service.d.ts.map +0 -1
  141. package/dist/translation-service.js +0 -291
  142. package/dist/translation-service.js.map +0 -1
  143. package/src/ai-client.ts +0 -227
  144. package/src/ai-processor.ts +0 -243
  145. package/src/ai-service.ts +0 -281
  146. package/src/builder.ts +0 -991
  147. package/src/gitignore.test.ts +0 -318
  148. package/src/gitignore.ts +0 -193
  149. package/src/markdown.ts +0 -212
  150. package/src/navigation.ts +0 -237
  151. package/src/scanner.ts +0 -180
  152. package/src/template.ts +0 -425
  153. package/src/translation-service.ts +0 -350
@@ -0,0 +1,29 @@
1
+ import { readFile } from 'fs/promises';
2
+ import { extractMetadataFromMarkdown } from '../ai/extractMetadataFromMarkdown';
3
+ import { MetaData } from '../metadata';
4
+
5
+ /**
6
+ * 运行 AI 元数据提取
7
+ */
8
+ export async function extractMetadataByAI(): Promise<void> {
9
+ const { files } = MetaData;
10
+
11
+ if (MetaData.options.verbose) console.log(`🤖 Running AI metadata extraction...`);
12
+ console.log(`🤖 Processing ${files.length} files with AI...`);
13
+
14
+ for (const file of files) {
15
+ try {
16
+ if (file.metadata) {
17
+ console.info(`ℹ️ Skipping ${file.path}, already has metadata`);
18
+ continue;
19
+ }
20
+ const content = await readFile(file.path, 'utf-8');
21
+ file.metadata = await extractMetadataFromMarkdown(content);
22
+ console.log(`✅ Extracted AI metadata for ${file.path}`, file.metadata.tokens_used);
23
+ } catch (error) {
24
+ console.error(`⚠️ Failed to process file ${file.path}:`, error);
25
+ }
26
+ }
27
+
28
+ console.log(`✅ AI processing completed for ${files.length} files`);
29
+ }
@@ -0,0 +1,201 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+ import { MetaData } from '../metadata';
4
+ import { ZEN_DIST_DIR, ZEN_SRC_DIR } from '../paths';
5
+ import { MetaDataStore } from '../types';
6
+ import { convertMarkdownToHtml } from '../utils/convertMarkdownToHtml';
7
+ import { parseFrontmatter } from '../utils/frontmatter';
8
+
9
+ const langNames: Record<string, string> = {
10
+ 'zh-Hans': '简体中文',
11
+ 'en-US': 'English',
12
+ 'ja-JP': '日本語',
13
+ 'ko-KR': '한국어',
14
+ };
15
+ /**
16
+ * 生成语言切换器 HTML
17
+ * @param currentLang 当前语言
18
+ * @param availableLangs 可用语言列表
19
+ * @returns 语言切换器 HTML 字符串
20
+ */
21
+ function generateLanguageSwitcher(templateData: TemplateData): string {
22
+ const {
23
+ options: { langs = [], baseUrl = '/' },
24
+ } = MetaData;
25
+
26
+ const items = langs
27
+ .map(lang => {
28
+ const langName = langNames[lang] || lang;
29
+ const isCurrent = lang === templateData.lang;
30
+ const activeClass = isCurrent ? 'active' : '';
31
+
32
+ return `<li class="lang-item ${activeClass}">
33
+ <a href="${path.join(baseUrl, lang, templateData.file.hash + '.html')}" class="lang-link">${langName}</a>
34
+ </li>`;
35
+ })
36
+ .join('');
37
+
38
+ return `<div class="language-switcher">
39
+ <span class="lang-label">Language:</span>
40
+ <ul class="lang-list">${items}</ul>
41
+ </div>`;
42
+ }
43
+
44
+ /**
45
+ * 生成导航 HTML
46
+ * @param navigation 导航树
47
+ * @param currentPath 当前路径(可选,用于高亮当前页面)
48
+ * @returns 导航 HTML 字符串
49
+ */
50
+ async function generateNavigationHtml(data: TemplateData): Promise<string> {
51
+ const {
52
+ files,
53
+ options: { baseUrl = '/' },
54
+ } = MetaData;
55
+
56
+ const navigation = await Promise.all(
57
+ files.map(async file => {
58
+ const content = await fs.readFile(
59
+ path.join(ZEN_SRC_DIR, data.lang, file.hash + '.md'),
60
+ 'utf-8'
61
+ );
62
+ const { frontmatter } = parseFrontmatter(content);
63
+ const title = frontmatter.title || file.metadata?.title || file.path; // 优先使用提取的标题
64
+
65
+ return {
66
+ title,
67
+ path: path.join(baseUrl, data.lang, file.hash + '.html'),
68
+ isActive: data.file.hash === file.hash,
69
+ };
70
+ })
71
+ );
72
+ navigation.sort((a, b) => a.title.localeCompare(b.title));
73
+
74
+ return `<ul class="nav-list">${navigation
75
+ .map(item => {
76
+ const activeClass = item.isActive ? 'active' : '';
77
+
78
+ let html = `<li class="nav-item">`;
79
+ html += `<a href="${item.path}" class="nav-link ${activeClass}">${item.title}</a>`;
80
+
81
+ html += `</li>`;
82
+ return html;
83
+ })
84
+ .join('')}</ul>`;
85
+ }
86
+
87
+ interface TemplateData {
88
+ file: MetaDataStore['files'][0];
89
+ content: string;
90
+ lang: string;
91
+ }
92
+
93
+ /**
94
+ * 简单的模板变量替换
95
+ * @param template 模板字符串
96
+ * @param data 模板数据
97
+ * @returns 渲染后的 HTML 字符串
98
+ */
99
+ async function renderTemplate(template: string, data: TemplateData): Promise<string> {
100
+ const {
101
+ options: { langs = [] },
102
+ } = MetaData;
103
+ const markdownContent = data.content;
104
+ const { frontmatter, body } = parseFrontmatter(markdownContent);
105
+
106
+ const htmlContent = convertMarkdownToHtml(body);
107
+
108
+ let result = template;
109
+
110
+ // 替换导航
111
+ const navigationHtml = await generateNavigationHtml(data);
112
+ result = result.replace(/{{navigation}}/g, navigationHtml);
113
+
114
+ // 替换其他变量 - 使用全局替换
115
+ result = result.replace(/{{title}}/g, frontmatter.title || 'Untitled');
116
+ result = result.replace(/{{content}}/g, htmlContent);
117
+
118
+ // 替换元数据变量
119
+ if (frontmatter) {
120
+ result = result.replace(/{{summary}}/g, frontmatter.summary || '');
121
+ result = result.replace(/{{tags}}/g, frontmatter.tags?.join(', ') || '');
122
+ result = result.replace(/{{inferred_date}}/g, frontmatter.inferred_date || '');
123
+ result = result.replace(/{{inferred_lang}}/g, frontmatter.inferred_lang || '');
124
+ }
125
+
126
+ // 替换语言相关变量
127
+ result = result.replace(/{{lang}}/g, data.lang || '');
128
+ if (langs && langs.length > 1 && data.lang) {
129
+ const langSwitcher = generateLanguageSwitcher(data);
130
+ result = result.replace('{{language_switcher}}', langSwitcher);
131
+ }
132
+
133
+ return result;
134
+ }
135
+
136
+ const renderRedirectTemplate = async (from: string, to: string): Promise<void> => {
137
+ const {
138
+ options: { baseUrl = '/' },
139
+ } = MetaData;
140
+ const toURL = path.join(baseUrl, to);
141
+ const html = `<!DOCTYPE html>
142
+ <html lang="en">
143
+ <head>
144
+ <meta charset="UTF-8">
145
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
146
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
147
+ <meta http-equiv="refresh" content="0; url=${toURL}">
148
+ <title>Redirecting...</title>
149
+ </head>
150
+ <body>
151
+ <p>Redirecting to <a href="${toURL}">${toURL}</a></p>
152
+ </body>
153
+ </html>`;
154
+ const targetPath = path.join(ZEN_DIST_DIR, from);
155
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
156
+ await fs.writeFile(targetPath, html, 'utf-8');
157
+ };
158
+
159
+ /**
160
+ * 渲染模板并保存文件
161
+ */
162
+ export async function renderTemplates(): Promise<void> {
163
+ const {
164
+ files,
165
+ options: { langs, verbose },
166
+ } = MetaData;
167
+
168
+ if (verbose) console.log(`⚡ Processing files...`);
169
+ const layoutTemplate = await fs.readFile(
170
+ path.join(__dirname, '../../assets/templates/default/layout.html'),
171
+ 'utf-8'
172
+ );
173
+
174
+ for (const file of files) {
175
+ for (const lang of langs || []) {
176
+ console.info(`📄 Preparing file for language: ${file.path} [${file.hash}] [${lang}]`);
177
+ const targetPath = path.join(ZEN_DIST_DIR, lang, file.hash + '.html');
178
+ const content = await fs.readFile(path.join(ZEN_SRC_DIR, lang, file.hash + '.md'), 'utf-8');
179
+ try {
180
+ const html = await renderTemplate(layoutTemplate, {
181
+ file,
182
+ content,
183
+ lang,
184
+ });
185
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
186
+ await fs.writeFile(targetPath, html, 'utf-8');
187
+ if (verbose) console.log(`✅ Rendered: ${targetPath}`);
188
+ } catch (error) {
189
+ console.error(`❌ Failed to render ${file.path}:`, error);
190
+ }
191
+ }
192
+ }
193
+
194
+ for (const lang of langs || []) {
195
+ await renderRedirectTemplate(
196
+ path.join(lang, 'index.html'),
197
+ path.join(lang, files[0].hash + '.html')
198
+ );
199
+ }
200
+ await renderRedirectTemplate('index.html', path.join(langs?.[0] || 'en-US', 'index.html'));
201
+ }
@@ -0,0 +1,17 @@
1
+ import * as crypto from 'crypto';
2
+ import * as fs from 'fs/promises';
3
+
4
+ /**
5
+ * 计算文件内容的 SHA256 哈希值
6
+ * @param filePath 文件路径
7
+ * @returns 文件的哈希值,如果读取失败则返回空字符串
8
+ */
9
+ export async function calculateFileHash(filePath: string): Promise<string> {
10
+ try {
11
+ const content = await fs.readFile(filePath, 'utf-8');
12
+ return crypto.createHash('sha256').update(content).digest('hex');
13
+ } catch (error) {
14
+ console.warn(`⚠️ Failed to calculate hash for ${filePath}:`, error);
15
+ return '';
16
+ }
17
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * OpenAI 消息接口
3
+ */
4
+ export interface OpenAIMessage {
5
+ role: 'system' | 'user' | 'assistant';
6
+ content: string;
7
+ }
8
+
9
+ /**
10
+ * OpenAI 响应接口
11
+ */
12
+ export interface OpenAIResponse {
13
+ id: string;
14
+ object: string;
15
+ created: number;
16
+ model: string;
17
+ choices: Array<{
18
+ index: number;
19
+ message: {
20
+ role: string;
21
+ content: string;
22
+ };
23
+ finish_reason: string;
24
+ }>;
25
+ usage: {
26
+ prompt_tokens: number;
27
+ completion_tokens: number;
28
+ total_tokens: number;
29
+ };
30
+ }
31
+
32
+ /**
33
+ * 使用 OpenAI API 补全消息
34
+ * @param messages 消息数组
35
+ * @param options 可选配置
36
+ * @returns Promise<OpenAIResponse> 返回完整的OpenAI响应
37
+ */
38
+ export const completeMessages = async (
39
+ messages: OpenAIMessage[],
40
+ options?: {
41
+ response_format?: { type: 'json_object' | 'text' };
42
+ }
43
+ ): Promise<OpenAIResponse> => {
44
+ // 从环境变量读取配置
45
+ const apiKey = process.env.OPENAI_API_KEY || '';
46
+ const baseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
47
+ const model = process.env.OPENAI_MODEL || 'gpt-3.5-turbo';
48
+
49
+ if (!apiKey) {
50
+ throw new Error('OPENAI_API_KEY environment variable is not set');
51
+ }
52
+
53
+ try {
54
+ const requestBody: any = {
55
+ model,
56
+ messages,
57
+ temperature: 0, // 总是设置为 0,提取内容不需要随机性
58
+ // 不设置 max_tokens,让API自动决定
59
+ };
60
+
61
+ // 添加可选的response_format
62
+ if (options?.response_format) {
63
+ requestBody.response_format = options.response_format;
64
+ }
65
+
66
+ const response = await fetch(`${baseUrl}/chat/completions`, {
67
+ method: 'POST',
68
+ headers: {
69
+ 'Content-Type': 'application/json',
70
+ Authorization: `Bearer ${apiKey}`,
71
+ },
72
+ body: JSON.stringify(requestBody),
73
+ });
74
+
75
+ if (!response.ok) {
76
+ const errorText = await response.text();
77
+ throw new Error(`OpenAI API error (${response.status}): ${errorText}`);
78
+ }
79
+
80
+ const data: OpenAIResponse = await response.json();
81
+
82
+ // 验证响应
83
+ if (!data.choices?.[0]?.message?.content?.trim()) {
84
+ throw new Error('Empty response from OpenAI API');
85
+ }
86
+
87
+ return data;
88
+ } catch (error) {
89
+ console.error('❌ Failed to call OpenAI API:', error);
90
+ throw error;
91
+ }
92
+ };
package/src/types.ts CHANGED
@@ -1,34 +1,28 @@
1
+ /**
2
+ * 单个文件的元数据缓存项
3
+ */
4
+ export interface FileMetaData {
5
+ hash: string;
6
+ path: string;
7
+ metadata?: AIMetadata;
8
+ }
9
+
10
+ /**
11
+ * .zen/meta.json 文件结构
12
+ */
13
+ export interface MetaDataStore {
14
+ version: string;
15
+ options: BuildOptions;
16
+ files: FileMetaData[];
17
+ }
18
+
1
19
  export interface BuildOptions {
2
- srcDir: string;
3
- outDir: string;
4
20
  template?: string;
5
- watch?: boolean;
6
21
  verbose?: boolean;
7
- serve?: boolean;
8
- port?: number;
9
- host?: string;
10
22
  baseUrl?: string;
11
23
  langs?: string[]; // 目标语言数组
12
24
  }
13
25
 
14
- export interface ScannedFile {
15
- path: string; // 相对路径
16
- name: string;
17
- ext: string;
18
- hash?: string; // 文件内容的 sha256 hash
19
- }
20
-
21
- export interface FileInfo {
22
- path: string; // 相对路径
23
- name: string;
24
- ext: string;
25
- content: string;
26
- html?: string;
27
- metadata?: { title: string }; // 简化,只保留标题
28
- hash?: string; // 文件内容的 sha256 hash
29
- aiMetadata?: AIMetadata; // AI 提取的元数据
30
- }
31
-
32
26
  export interface AIMetadata {
33
27
  title: string; // AI 提取的标题
34
28
  summary: string; // AI 提取的摘要,控制在 100字以内
@@ -41,51 +35,3 @@ export interface AIMetadata {
41
35
  total: number;
42
36
  }; // tokens 使用情况
43
37
  }
44
-
45
- export interface NavigationItem {
46
- title: string;
47
- path: string;
48
- children?: NavigationItem[];
49
- }
50
-
51
- export interface TemplateData {
52
- title: string;
53
- content: string;
54
- navigation: NavigationItem[];
55
- metadata?: AIMetadata; // 使用完整的 AI 元数据
56
- currentPath?: string;
57
- lang?: string; // 当前语言
58
- availableLangs?: string[]; // 可用的语言列表
59
- }
60
-
61
- export interface MarkdownProcessor {
62
- beforeParse?(content: string, fileInfo: FileInfo): string | Promise<string>;
63
- afterParse?(html: string, fileInfo: FileInfo): string | Promise<string>;
64
- }
65
-
66
- export interface ZenConfig {
67
- srcDir?: string;
68
- outDir?: string;
69
- template?: string;
70
- baseUrl?: string;
71
- i18n?: {
72
- sourceLang: string;
73
- targetLangs: string[];
74
- apiKey?: string;
75
- };
76
- ai?: {
77
- enabled?: boolean;
78
- model?: string;
79
- temperature?: number;
80
- maxTokens?: number;
81
- };
82
- processors?: MarkdownProcessor[];
83
- includePattern?: string;
84
- excludePattern?: string;
85
- }
86
-
87
- export interface MultiLangBuildOptions extends BuildOptions {
88
- langs: string[]; // 必须指定目标语言
89
- useMetaData?: boolean; // 是否使用 meta.json 中的元数据
90
- filterOrphans?: boolean; // 是否过滤孤儿文件
91
- }
@@ -0,0 +1,32 @@
1
+ import { marked } from 'marked';
2
+ import hljs from 'highlight.js';
3
+
4
+ // 配置 marked 使用 highlight.js 进行代码高亮
5
+ marked.setOptions({
6
+ highlight: function (code: string, lang: string) {
7
+ if (lang && hljs.getLanguage(lang)) {
8
+ try {
9
+ return hljs.highlight(code, { language: lang }).value;
10
+ } catch (err) {
11
+ console.warn(`Failed to highlight code with language ${lang}:`, err);
12
+ }
13
+ }
14
+ return hljs.highlightAuto(code).value;
15
+ },
16
+ pedantic: false,
17
+ gfm: true,
18
+ breaks: false,
19
+ sanitize: false,
20
+ smartLists: true,
21
+ smartypants: false,
22
+ xhtml: false,
23
+ } as any);
24
+
25
+ /**
26
+ * 将 Markdown 内容转换为 HTML
27
+ * @param mdContent Markdown 内容字符串
28
+ * @returns 转换后的 HTML 字符串
29
+ */
30
+ export const convertMarkdownToHtml = (mdContent: string): string => {
31
+ return marked.parse(mdContent) as string;
32
+ };
@@ -0,0 +1,18 @@
1
+ import { parse, stringify } from 'yaml';
2
+
3
+ export const parseFrontmatter = (content: string): { frontmatter: any; body: string } => {
4
+ const frontmatterRegex = /^---\n([\s\S]*?)\n---/;
5
+ const match = content.match(frontmatterRegex);
6
+ if (match) {
7
+ const frontmatterContent = match[1];
8
+ const body = content.slice(match[0].length).trim();
9
+ return { frontmatter: parse(frontmatterContent.trim()), body };
10
+ }
11
+ return { frontmatter: {}, body: content };
12
+ };
13
+
14
+ export const updateFrontmatter = (content: string, newFrontmatter: any): string => {
15
+ const { body } = parseFrontmatter(content);
16
+ const frontmatterContent = `---\n${stringify(newFrontmatter)}---\n\n`;
17
+ return frontmatterContent + body;
18
+ };
@@ -1,51 +0,0 @@
1
- [
2
- {
3
- "sourceHash": "1b798c44a4f353e47296ca83d5905e37e6aba3e90bbd9bc3b3d34fc12059a2ca",
4
- "sourceLang": "zh-Hans",
5
- "targetLang": "en-US",
6
- "translatedContent": "# ZEN - A Minimalist Markdown Documentation Site Builder\n\n> 📖 **Reading Note**: This README is the Chinese version. The English version will be automatically generated by AI translation.\n\n## Project Motivation\n\n### Return to Content\n\nI enjoy contemplation, but I don't want to deal with complex build tools, fiddle with intricate documentation configurations, or navigate complicated structures.\n\n### Return to Native Language\n\nLife is short, I use AI for translation. Stay connected with the world.\n\n## Core Features\n\n1. **Static Site Generation**\n * Builds any folder containing Markdown files into a static HTML site.\n\n2. **Intelligent Navigation**\n * Generates a site map and navigation automatically, without requiring you to maintain the original directory structure of your Markdown source files.\n\n3. **Incremental i18n Translation**\n * Utilizes LLMs for incremental internationalization (i18n) translation. Write your Markdown in your native language, while your audience can be multilingual.\n\n## Design Philosophy\n\n* **Minimalism**: Minimal configuration, maximum flexibility.\n* **Content-First**: Focus on writing, not tool configuration.\n* **AI-Powered**: Leverage AI for translation and content organization.\n* **Cross-Language**: Supports multilingual content creation and presentation.\n\n## Quick Start\n\n### Recommended Usage\n\n**It is recommended to switch to the directory containing your Markdown files and start building directly with the following command:**\n\n```bash\nnpx zengen build\n```\n\n### Other Usages\n\n1. **Live Preview (Watch for file changes)**:\n\n```bash\nnpx zengen build --watch\n```\n\n2. **View more parameters or help**:\n\n```bash\nnpx zengen\n```\n\n**Note**: ZEN enforces using the current directory as the source directory and outputs to the `.zen/dist` directory. Specifying source and output directory parameters is no longer supported.\n\n---\n\n**ZEN** - Return documentation to its essence, return writing to tranquility.",
7
- "lastUpdated": "2026-01-05T14:12:28.056Z"
8
- },
9
- {
10
- "sourceHash": "5ec990146b35e00de2630559126ee07f7cdcddeb23b0e8cab3d85b4181353e26",
11
- "sourceLang": "zh-Hans",
12
- "targetLang": "en-US",
13
- "translatedContent": "# Advanced Usage\n\nIn-depth introduction to ZEN's advanced features and configuration options.\n\n## Custom Templates\n\nZEN supports custom HTML templates:\n\n```bash\nzengen build --src ./docs --out ./dist --template ./custom-template.html\n```\n\n## Configuration Options\n\nCan be configured in the `.zenrc` file:\n\n```json\n{\n \"srcDir\": \"./docs\",\n \"outDir\": \"./dist\",\n \"template\": \"./template.html\",\n \"baseUrl\": \"https://example.com\",\n \"i18n\": {\n \"sourceLang\": \"zh-Hans\",\n \"targetLangs\": [\"en-US\", \"ja-JP\"]\n }\n}\n```\n\n## Plugin System\n\nZEN supports plugin extensions:\n\n```typescript\ninterface MarkdownProcessor {\n beforeParse?(content: string, fileInfo: FileInfo): string | Promise<string>;\n afterParse?(html: string, fileInfo: FileInfo): string | Promise<string>;\n}\n```",
14
- "lastUpdated": "2026-01-05T14:12:35.392Z"
15
- },
16
- {
17
- "sourceHash": "d49012f98c4367b34034063400e2f7826bf0615952210c82396920172d468e2c",
18
- "sourceLang": "zh-Hans",
19
- "targetLang": "en-US",
20
- "translatedContent": "# GitHub Pages Deployment Configuration\n\nThis directory contains the GitHub Pages deployment configuration for the ZEN project documentation site.\n\n## Workflow\n\n### `pages.yml`\n\nThis workflow automatically builds the ZEN project documentation site and deploys it to GitHub Pages.\n\n**Triggers:**\n\n- Push to the `main` branch (when changes occur in `demo/src/`, `package.json`, or workflow files)\n- Pull Request targeting the `main` branch\n- Manual trigger\n\n**Workflow Steps:**\n\n1. **Checkout code**: Check out code from the remote branch to ensure code synchronization.\n2. **Set up Node.js**: Configure the Node.js 20.x environment.\n3. **Install dependencies**: Install project dependencies using `npm ci`.\n4. **Build zengen**: Build the local zengen package.\n5. **Install zengen**: Install the locally built zengen as a global tool.\n6. **Test zengen CLI**: Verify the CLI tool functions correctly.\n7. **Build documentation site**: Build the documentation using `cd demo/src && zengen build`, outputting to the `.zen/dist` directory.\n8. **Configure Pages**: Set up GitHub Pages.\n9. **Upload artifact**: Upload the built documentation site as a Pages artifact.\n10. **Deploy to GitHub Pages**: Automatically deploy to GitHub Pages.\n\n## Accessing the Documentation Site\n\nAfter successful deployment, the documentation site will be accessible at the following URL:\n\n```\nhttps://[username].github.io/[repository-name]/\n```\n\n## Custom Configuration\n\n### Custom Domain\n\nIf you need to use a custom domain, you can add a CNAME file after the build step:\n\n```yaml\n# Create CNAME file (if a custom domain is needed)\necho \"docs.example.com\" > docs-dist/CNAME\n```\n\n### Build Options\n\nThe currently used build command:\n\n```bash\ncd demo/src\nzengen build --clean --verbose\n```\n\nAvailable options:\n\n- `--clean`: Clean the output directory before building.\n- `--verbose`: Show verbose output.\n- `--watch`: Watch mode (not suitable for CI/CD).\n- `--template`: Specify a custom template file.\n- `--config`: Specify a configuration file.\n\n### Environment Variables\n\nThe workflow uses the following environment variables:\n\n- `GITHUB_TOKEN`: Automatically provided GitHub token.\n- `NODE_VERSION`: Node.js version (defaults to 20.x).\n\n## Troubleshooting\n\n### Build Failures\n\n1. **Check Node.js version**: Ensure a supported Node.js version is used.\n2. **Verify dependency installation**: Ensure `npm ci` executes successfully.\n3. **Check build output**: Review the verbose output of `zengen build`.\n4. **CLI output directory issue**: ZEN now enforces output to the `.zen/dist` directory and no longer supports the `--out` parameter.\n\n### Deployment Failures\n\n1. **Check permissions**: Ensure the workflow has correct write permissions for Pages.\n2. **Verify artifact**: Ensure the `.zen/dist` directory contains valid HTML files.\n3. **Review logs**: Check GitHub Actions logs for detailed error messages.\n\n### Documentation Not Updating\n\n1. **Check triggers**: Ensure files in the `demo/src/` directory were modified.\n2. **Wait for deployment completion**: GitHub Pages deployment may take a few minutes.\n3. **Clear browser cache**: The browser might be caching an old version.\n\n## Manual Trigger\n\nDeployment can be manually triggered via the GitHub Actions interface:\n\n1. Go to the repository's \"Actions\" tab.\n2. Select the \"Deploy to GitHub Pages\" workflow.\n3. Click the \"Run workflow\" button.\n4. Select the branch and run.\n\n## Related Files\n\n- `demo/src/`: Documentation source files (Markdown format).\n- `package.json`: Project configuration and dependencies.\n- `src/cli.ts`: zengen CLI tool implementation.\n- `src/builder.ts`: Documentation builder implementation.",
21
- "lastUpdated": "2026-01-05T14:13:01.694Z"
22
- },
23
- {
24
- "sourceHash": "f0c2799126931ccd113a0c45b1e623870b0d4f4f400becf6dd877da8f1011517",
25
- "sourceLang": "zh-Hans",
26
- "targetLang": "en-US",
27
- "translatedContent": "# Quick Start Guide\n\nThis document explains how to quickly get started with the ZEN documentation generator.\n\n## Installation\n\n```bash\nnpm install -g zengen\n```\n\n## Basic Usage\n\n1. Create a documentation directory\n2. Write Markdown files\n3. Run the build command\n\n```bash\nzengen build --src ./docs --out ./dist\n```\n\n## Features\n\n- Minimal configuration\n- Built-in responsive templates\n- AI-assisted metadata extraction\n- Multi-language support",
28
- "lastUpdated": "2026-01-05T14:13:05.710Z"
29
- },
30
- {
31
- "sourceHash": "6ad8db715a1b60613fe934fefb29fa981ecad9b63145593accff144d73b44bde",
32
- "sourceLang": "zh-Hans",
33
- "targetLang": "en-US",
34
- "translatedContent": "# Best Practices\n\nThis document introduces best practices for building documentation sites using ZEN.\n\n## Multilingual Management\n\n### Translation Strategy\n\n1. **Primary Language First**: Write the complete documentation in the native language first.\n2. **Incremental Translation**: Only translate modified parts after each update.\n3. **Terminology Consistency**: Create a glossary to maintain translation consistency.\n4. **Manual Review**: It is recommended to manually review AI translations.\n\n## Performance Optimization\n\n### Build Optimization\n\n1. **Incremental Builds**: Use `--watch` mode for development.\n2. **Cache Utilization**: ZEN automatically caches processing results.\n3. **Parallel Processing**: Automatically processes files in parallel on multi-core CPUs.\n\n### Development Workflow\n\n```bash\n# Watch for changes during development\ncd docs\nnpx zengen build --watch\n\n# Start development server\nnpx zengen build --watch --serve\n\n# Production build\nnpx zengen build --clean\n```\n\n## Deployment Strategy\n\n### CI/CD Integration\n\n#### GitHub Actions Example\n\n```yaml\nname: Build and Deploy Documentation\non:\n push:\n branches: [main]\n paths:\n - 'docs/**'\n - '.github/workflows/docs.yml'\n\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - name: Checkout repository\n uses: actions/checkout@v4\n\n - name: Setup Node.js\n uses: actions/setup-node@v4\n with:\n node-version: '20.x'\n\n - name: Build documentation\n run: |\n cd docs\n npx zengen build --clean --base-url /my-docs\n\n - name: Deploy to GitHub Pages\n uses: peaceiris/actions-gh-pages@v3\n with:\n github_token: ${{ secrets.GITHUB_TOKEN }}\n publish_dir: docs/.zen/dist\n```\n\n#### Custom Deployment Script\n\n```bash\n#!/bin/bash\n# deploy-docs.sh\n\n# Switch to the documentation directory\ncd docs\n\n# Clean and build\nnpx zengen build --clean\n\n# Sync to server\nrsync -avz .zen/dist/ user@server:/var/www/docs/\n\n# Clean cache\nrm -rf .zen/cache\n```\n\n### Cloud Deployment Options\n\n- **GitHub Pages**: Free hosting for documentation.\n- **Vercel**: Automatic deployment of static sites.\n- **Netlify**: Supports form handling and redirects.\n- **AWS S3 + CloudFront**: Enterprise-grade static hosting.\n\n## Maintenance Recommendations\n\n### Regular Updates\n\n1. **Content Review**: Check documentation accuracy monthly.\n2. **Link Checking**: Regularly check for broken links.\n3. **Performance Monitoring**: Monitor page load speed.\n4. **User Feedback**: Collect user feedback to improve documentation.\n\n### Version Control\n\n1. **Document Versioning**: Synchronize with software versions.\n2. **Change Log**: Record documentation update history.\n3. **Rollback Mechanism**: Support quick rollback to previous versions.\n\n## Frequently Asked Questions\n\n### Slow Build Speed\n\n**Solutions:**\n\n- Reduce unnecessary images and resources.\n- Use `--watch` mode for incremental development.\n- Split large documents into multiple smaller files.\n- Disable unnecessary processors.\n\n### Poor Translation Quality\n\n**Solutions:**\n\n- Provide context for AI translation.\n- Create a glossary to improve consistency.\n- Manually review critical content.\n- Adjust translation prompts.\n\n### Complex Navigation Structure\n\n**Solutions:**\n\n- Maintain a flat directory structure.\n- Use clear heading hierarchies.\n- Provide search functionality.\n- Use sidebar navigation appropriately.\n\n### High Memory Usage\n\n**Solutions:**\n\n- Reduce the number of files processed simultaneously.\n- Disable caching (not recommended).\n- Increase system memory.\n- Process large documents in batches.\n\n## Advanced Techniques\n\n### Custom Template Tips\n\n1. **Responsive Design**: Ensure templates display correctly on mobile devices.\n2. **Theme Switching**: Implement dark/light themes.\n3. **Syntax Highlighting**: Integrate highlight.js or other highlighting libraries.\n4. **Search Functionality**: Add client-side search.\n\n### Integrating Other Tools\n\n1. **Image Optimization**: Use sharp or imagemin to optimize images.\n2. **SEO Optimization**: Add meta tags and structured data.\n3. **Analytics Integration**: Integrate Google Analytics or Plausible.\n4. **CDN Acceleration**: Use a CDN to accelerate static resources.\n\n### Monitoring and Logging\n\n1. **Build Logs**: Use `--verbose` to view detailed logs.\n2. **Error Monitoring**: Set up error monitoring and alerts.\n3. **Performance Monitoring**: Monitor build time and resource usage.\n4. **User Analytics**: Analyze documentation usage.",
35
- "lastUpdated": "2026-01-05T14:13:35.974Z"
36
- },
37
- {
38
- "sourceHash": "a1580f71c6c6c1ff4a314be72d410a8507af2f087d56360c7f5048d349c21953",
39
- "sourceLang": "zh-Hans",
40
- "targetLang": "en-US",
41
- "translatedContent": "# Configuration Guide\n\nZEN's design philosophy is minimalism, so configuration is very simple.\n\n## Command Line Usage\n\n### Basic Commands\n\n```bash\n# Build documentation (recommended usage)\nnpx zengen build\n\n# Live preview (watch for file changes)\nnpx zengen build --watch\n\n# Start development server (requires --watch)\nnpx zengen build --watch --serve\n\n# Custom port\nnpx zengen build --watch --serve --port 8080\n\n# Clean output directory\nnpx zengen build --clean\n\n# Show verbose logs\nnpx zengen build --verbose\n\n# Set base URL\nnpx zengen build --base-url /my-docs\n\n# View help\nnpx zengen\n```\n\n**Important Notes:**\n\n- ZEN enforces the current directory as the source directory and outputs to the `.zen/dist` directory\n- Specifying source and output directories via command-line arguments is no longer supported\n- When using `--watch` mode, modifying files triggers automatic rebuilds\n\n### Command Line Options\n\n| Option | Short | Description | Default |\n| -------------- | ----- | --------------------------------------- | ----------- |\n| `--watch` | `-w` | Watch for file changes and auto-rebuild | `false` |\n| `--serve` | `-s` | Start dev server (requires `--watch`) | `false` |\n| `--port` | `-p` | Development server port | `3000` |\n| `--host` | | Development server host | `localhost` |\n| `--verbose` | `-v` | Show verbose logs | `false` |\n| `--clean` | | Clean output directory | `false` |\n| `--base-url` | | Site base URL | None |\n| `--help` | `-h` | Show help information | None |",
42
- "lastUpdated": "2026-01-05T14:13:50.036Z"
43
- },
44
- {
45
- "sourceHash": "fdfca9b960d0eaa8b2b96fe988ead7481d2c0b16f66ebc94fb477139b4178cdc",
46
- "sourceLang": "zh-Hans",
47
- "targetLang": "en-US",
48
- "translatedContent": "# ZEN Documentation Site Example\n\nWelcome to the ZEN documentation builder! This is a minimalist Markdown documentation site generator.\n\n## Features\n\n- **Minimal Configuration**: No complex configuration files required\n- **Content-First**: Focus on writing, not tool configuration\n- **Smart Navigation**: Automatically generates site maps and navigation\n- **Multi-Language Support**: Supports incremental i18n translation\n\n## Quick Start\n\n```bash\n# Build documentation using npx (recommended)\nnpx zengen build\n\n# Live preview (watch for file changes)\nnpx zengen build --watch\n\n# View more parameters or help\nnpx zengen\n```\n\n**Note**: ZEN enforces the use of the current directory as the source directory and outputs to the `.zen/dist` directory. Specifying source and output directory parameters is no longer supported.\n\n## Code Examples\n\n```javascript\n// This is a JavaScript example\nconst zen = require('zengen');\n\nasync function buildDocs() {\n await zen.build({\n // ZEN now enforces using the current directory as the source\n // Outputs to the .zen/dist directory\n });\n}\n```\n\n```python\n# This is a Python example\ndef hello_world():\n print(\"Hello from ZEN!\")\n```\n\n## Next Steps\n\n1. Read the [API Documentation](./api.md)\n2. Check the [Configuration Guide](./config.md)\n3. Learn about [Best Practices](./best-practices.md)",
49
- "lastUpdated": "2026-01-05T14:14:00.234Z"
50
- }
51
- ]
@@ -1,34 +0,0 @@
1
- import { AIMetadata } from './types';
2
- import { AIService } from './ai-service';
3
- /**
4
- * AI 客户端类 - 使用 fetch 调用 OpenAI 兼容 API
5
- */
6
- export declare class AIClient {
7
- private aiService;
8
- constructor(aiService: AIService);
9
- /**
10
- * 调用 AI 模型提取文档 metadata
11
- */
12
- extractMetadata(content: string, filePath: string): Promise<AIMetadata | null>;
13
- /**
14
- * 构建提取 metadata 的 prompt
15
- */
16
- private buildMetadataPrompt;
17
- /**
18
- * 调用 OpenAI 兼容 API
19
- */
20
- private callOpenAIAPI;
21
- /**
22
- * 解析 AI 返回的 metadata
23
- */
24
- private parseMetadataResponse;
25
- /**
26
- * 批量处理文件
27
- */
28
- processFiles(files: Array<{
29
- content: string;
30
- path: string;
31
- hash: string;
32
- }>): Promise<Map<string, AIMetadata>>;
33
- }
34
- //# sourceMappingURL=ai-client.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ai-client.d.ts","sourceRoot":"","sources":["../src/ai-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,SAAS,EAAY,MAAM,cAAc,CAAC;AAyBnD;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,SAAS,CAAY;gBAEjB,SAAS,EAAE,SAAS;IAIhC;;OAEG;IACG,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAsCpF;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAgC3B;;OAEG;YACW,aAAa;IAyC3B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA2B7B;;OAEG;IACG,YAAY,CAChB,KAAK,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,GAC5D,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAmCpC"}