@aigne/doc-smith 0.9.10 → 0.9.11-beta

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 (308) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +189 -219
  3. package/README.zh.md +270 -0
  4. package/agents/bash-executor/index.mjs +347 -0
  5. package/agents/clear/ai/intent.md +142 -0
  6. package/agents/clear/choose-contents.mjs +13 -65
  7. package/agents/clear/clear-auth-tokens.mjs +17 -21
  8. package/agents/clear/clear-deployment-config.mjs +33 -24
  9. package/agents/clear/index.yaml +1 -9
  10. package/agents/content-checker/ai/intent.md +209 -0
  11. package/agents/content-checker/clean-invalid-docs.mjs +254 -0
  12. package/agents/content-checker/index.mjs +191 -0
  13. package/agents/content-checker/validate-content.mjs +983 -0
  14. package/agents/generate-images/generate-image.yaml +75 -0
  15. package/agents/generate-images/generate-summary.mjs +213 -0
  16. package/agents/generate-images/index.yaml +39 -0
  17. package/agents/generate-images/prepare-generation.mjs +286 -0
  18. package/agents/generate-images/prepare-image-generation.mjs +130 -0
  19. package/{prompts/detail/diagram/generate-image-system.md → agents/generate-images/prompts/system.md} +22 -56
  20. package/agents/generate-images/prompts/user.md +85 -0
  21. package/agents/generate-images/save-image-result.mjs +247 -0
  22. package/agents/generate-images/scan-image-slots.mjs +247 -0
  23. package/agents/localize/index.yaml +19 -42
  24. package/{prompts/translate → agents/localize/prompts}/translate-document.md +0 -139
  25. package/agents/localize/translate-documents/generate-summary.mjs +163 -0
  26. package/agents/localize/translate-documents/load-glossary.mjs +52 -0
  27. package/agents/localize/translate-documents/prepare-translation.mjs +249 -0
  28. package/agents/localize/translate-documents/save-translation.mjs +171 -0
  29. package/agents/localize/translate-documents/translate-document-to-language.mjs +209 -0
  30. package/agents/localize/translate-documents/translate-document.yaml +23 -0
  31. package/agents/localize/translate-documents/translate-to-languages.yaml +10 -0
  32. package/agents/localize/translate-images/check-image-translation.mjs +225 -0
  33. package/agents/localize/translate-images/detect-text/detect-and-update-shared.mjs +148 -0
  34. package/agents/localize/translate-images/detect-text/detect-image-text.yaml +44 -0
  35. package/agents/localize/translate-images/detect-text/detect-images-text.yaml +21 -0
  36. package/agents/localize/translate-images/detect-text/prompts/detect-image-text-system.md +43 -0
  37. package/agents/localize/translate-images/detect-text/prompts/detect-image-text-user.md +14 -0
  38. package/agents/localize/translate-images/detect-text/save-text-detection.mjs +105 -0
  39. package/agents/localize/translate-images/prepare-image-input.mjs +124 -0
  40. package/agents/localize/translate-images/save-image-translation.mjs +172 -0
  41. package/agents/localize/translate-images/scan-doc-images.mjs +165 -0
  42. package/agents/localize/translate-images/translate-doc-images.yaml +24 -0
  43. package/agents/localize/{translate-diagram.yaml → translate-images/translate-image.yaml} +25 -14
  44. package/agents/publish/ai/intent.md +182 -0
  45. package/agents/publish/check.mjs +107 -0
  46. package/agents/publish/index.yaml +9 -14
  47. package/agents/publish/publish-docs.mjs +81 -61
  48. package/agents/publish/translate-meta.mjs +79 -58
  49. package/agents/save-document/index.mjs +260 -0
  50. package/agents/structure-checker/index.mjs +307 -0
  51. package/agents/structure-checker/validate-structure.mjs +477 -0
  52. package/agents/update-image/analyze-feedback.yaml +37 -0
  53. package/agents/update-image/index.yaml +78 -0
  54. package/agents/update-image/load-existing-image.mjs +211 -0
  55. package/agents/update-image/prompts/analyze-feedback-system.md +43 -0
  56. package/agents/update-image/prompts/analyze-feedback-user.md +15 -0
  57. package/aigne.yaml +26 -139
  58. package/package.json +16 -48
  59. package/scripts/README.md +90 -0
  60. package/scripts/install.sh +86 -0
  61. package/scripts/uninstall.sh +52 -0
  62. package/skills/doc-smith/SKILL.md +285 -0
  63. package/skills/doc-smith/ai/intent/sources-improve.md +290 -0
  64. package/skills/doc-smith/references/changeset-guide.md +171 -0
  65. package/skills/doc-smith/references/document-content-guide.md +214 -0
  66. package/skills/doc-smith/references/document-structure-schema.md +138 -0
  67. package/skills/doc-smith/references/patch-guide.md +96 -0
  68. package/skills/doc-smith/references/structure-confirmation-guide.md +133 -0
  69. package/skills/doc-smith/references/structure-planning-guide.md +149 -0
  70. package/skills/doc-smith/references/update-workflow.md +108 -0
  71. package/skills/doc-smith/references/user-intent-guide.md +175 -0
  72. package/skills/doc-smith/references/workspace-initialization.md +376 -0
  73. package/skills/doc-smith-docs-detail/SKILL.md +356 -0
  74. package/skills/doc-smith-docs-detail/ai/intent.md +271 -0
  75. package/skills-entry/doc-smith/ai/intent.md +260 -0
  76. package/skills-entry/doc-smith/index.mjs +66 -0
  77. package/skills-entry/doc-smith/prompt.md +57 -0
  78. package/skills-entry/doc-smith/utils.mjs +27 -0
  79. package/skills-entry/doc-smith-docs-detail/batch.yaml +56 -0
  80. package/skills-entry/doc-smith-docs-detail/index.mjs +95 -0
  81. package/skills-entry/doc-smith-docs-detail/prompt.md +64 -0
  82. package/utils/afs-factory.mjs +183 -0
  83. package/utils/agent-constants.mjs +97 -0
  84. package/utils/{auth-utils.mjs → auth.mjs} +6 -9
  85. package/{agents/utils/update-branding.mjs → utils/branding.mjs} +3 -4
  86. package/utils/config.mjs +261 -0
  87. package/utils/constants.mjs +32 -0
  88. package/utils/deploy.mjs +3 -3
  89. package/utils/docs-converter.mjs +454 -0
  90. package/utils/docs.mjs +212 -0
  91. package/utils/document-paths.mjs +172 -0
  92. package/utils/files.mjs +74 -0
  93. package/utils/git.mjs +65 -0
  94. package/utils/{blocklet.mjs → http.mjs} +18 -0
  95. package/utils/image-slots.mjs +57 -0
  96. package/utils/image-utils.mjs +114 -0
  97. package/utils/project.mjs +95 -0
  98. package/utils/sources-path-resolver.mjs +76 -0
  99. package/utils/{upload-files.mjs → upload.mjs} +3 -3
  100. package/utils/workspace.mjs +371 -0
  101. package/agents/chat/chat-system.md +0 -38
  102. package/agents/chat/index.mjs +0 -59
  103. package/agents/chat/skills/generate-document.yaml +0 -15
  104. package/agents/chat/skills/list-documents.mjs +0 -15
  105. package/agents/chat/skills/update-document.yaml +0 -24
  106. package/agents/clear/clear-document-config.mjs +0 -36
  107. package/agents/clear/clear-document-structure.mjs +0 -102
  108. package/agents/clear/clear-generated-docs.mjs +0 -142
  109. package/agents/clear/clear-media-description.mjs +0 -129
  110. package/agents/create/aggregate-document-structure.mjs +0 -21
  111. package/agents/create/analyze-diagram-type-llm.yaml +0 -159
  112. package/agents/create/analyze-diagram-type.mjs +0 -455
  113. package/agents/create/check-document-structure.yaml +0 -30
  114. package/agents/create/check-need-generate-structure.mjs +0 -138
  115. package/agents/create/document-structure-tools/add-document.mjs +0 -85
  116. package/agents/create/document-structure-tools/delete-document.mjs +0 -116
  117. package/agents/create/document-structure-tools/move-document.mjs +0 -109
  118. package/agents/create/document-structure-tools/update-document.mjs +0 -84
  119. package/agents/create/generate-diagram-image.yaml +0 -91
  120. package/agents/create/generate-structure.yaml +0 -106
  121. package/agents/create/index.yaml +0 -45
  122. package/agents/create/refine-document-structure.yaml +0 -12
  123. package/agents/create/replace-d2-with-image.mjs +0 -610
  124. package/agents/create/update-document-structure.yaml +0 -54
  125. package/agents/create/user-add-document/add-documents-to-structure.mjs +0 -90
  126. package/agents/create/user-add-document/find-documents-to-add-links.yaml +0 -47
  127. package/agents/create/user-add-document/index.yaml +0 -46
  128. package/agents/create/user-add-document/prepare-documents-to-translate.mjs +0 -22
  129. package/agents/create/user-add-document/print-add-document-summary.mjs +0 -63
  130. package/agents/create/user-add-document/review-documents-with-new-links.mjs +0 -110
  131. package/agents/create/user-remove-document/find-documents-with-invalid-links.mjs +0 -78
  132. package/agents/create/user-remove-document/index.yaml +0 -40
  133. package/agents/create/user-remove-document/prepare-documents-to-translate.mjs +0 -22
  134. package/agents/create/user-remove-document/print-remove-document-summary.mjs +0 -53
  135. package/agents/create/user-remove-document/remove-documents-from-structure.mjs +0 -99
  136. package/agents/create/user-remove-document/review-documents-with-invalid-links.mjs +0 -115
  137. package/agents/create/user-review-document-structure.mjs +0 -139
  138. package/agents/create/utils/init-current-content.mjs +0 -34
  139. package/agents/create/utils/merge-document-structures.mjs +0 -36
  140. package/agents/evaluate/code-snippet.mjs +0 -97
  141. package/agents/evaluate/document-structure.yaml +0 -67
  142. package/agents/evaluate/document.yaml +0 -82
  143. package/agents/evaluate/generate-report.mjs +0 -85
  144. package/agents/evaluate/index.yaml +0 -46
  145. package/agents/history/index.yaml +0 -6
  146. package/agents/history/view.mjs +0 -78
  147. package/agents/init/check.mjs +0 -16
  148. package/agents/init/index.mjs +0 -643
  149. package/agents/init/validate.mjs +0 -16
  150. package/agents/localize/choose-language.mjs +0 -107
  151. package/agents/localize/record-translation-history.mjs +0 -23
  152. package/agents/localize/save-doc-translation-or-skip.mjs +0 -18
  153. package/agents/localize/set-review-content.mjs +0 -58
  154. package/agents/localize/translate-document-wrapper.mjs +0 -34
  155. package/agents/localize/translate-document.yaml +0 -24
  156. package/agents/localize/translate-multilingual.yaml +0 -57
  157. package/agents/localize/translate-or-skip-diagram.mjs +0 -52
  158. package/agents/media/batch-generate-media-description.yaml +0 -46
  159. package/agents/media/generate-media-description.yaml +0 -50
  160. package/agents/media/load-media-description.mjs +0 -454
  161. package/agents/prefs/index.mjs +0 -203
  162. package/agents/schema/document-structure-item.yaml +0 -26
  163. package/agents/schema/document-structure-refine-item.yaml +0 -23
  164. package/agents/schema/document-structure.yaml +0 -29
  165. package/agents/update/batch-generate-document.yaml +0 -27
  166. package/agents/update/batch-update-document.yaml +0 -7
  167. package/agents/update/check-diagram-flag.mjs +0 -116
  168. package/agents/update/check-document.mjs +0 -162
  169. package/agents/update/check-generate-diagram.mjs +0 -106
  170. package/agents/update/check-update-is-single.mjs +0 -53
  171. package/agents/update/document-tools/update-document-content.mjs +0 -303
  172. package/agents/update/generate-diagram.yaml +0 -80
  173. package/agents/update/generate-document.yaml +0 -70
  174. package/agents/update/handle-document-update.yaml +0 -103
  175. package/agents/update/index.yaml +0 -69
  176. package/agents/update/pre-check-generate-diagram.yaml +0 -44
  177. package/agents/update/save-and-translate-document.mjs +0 -80
  178. package/agents/update/update-document-detail.yaml +0 -71
  179. package/agents/update/update-single/update-single-document-detail.mjs +0 -322
  180. package/agents/update/update-single-document.yaml +0 -7
  181. package/agents/update/user-review-document.mjs +0 -272
  182. package/agents/utils/action-success.mjs +0 -16
  183. package/agents/utils/analyze-document-feedback-intent.yaml +0 -32
  184. package/agents/utils/analyze-feedback-intent.mjs +0 -253
  185. package/agents/utils/analyze-structure-feedback-intent.yaml +0 -29
  186. package/agents/utils/check-detail-result.mjs +0 -51
  187. package/agents/utils/check-feedback-refiner.mjs +0 -81
  188. package/agents/utils/choose-docs.mjs +0 -251
  189. package/agents/utils/document-icon-generate.yaml +0 -52
  190. package/agents/utils/document-title-streamline.yaml +0 -48
  191. package/agents/utils/ensure-document-icons.mjs +0 -129
  192. package/agents/utils/exit.mjs +0 -6
  193. package/agents/utils/feedback-refiner.yaml +0 -50
  194. package/agents/utils/find-item-by-path.mjs +0 -114
  195. package/agents/utils/find-user-preferences-by-path.mjs +0 -37
  196. package/agents/utils/format-document-structure.mjs +0 -35
  197. package/agents/utils/generate-document-or-skip.mjs +0 -41
  198. package/agents/utils/handle-diagram-operations.mjs +0 -263
  199. package/agents/utils/load-all-document-content.mjs +0 -30
  200. package/agents/utils/load-document-all-content.mjs +0 -96
  201. package/agents/utils/load-sources.mjs +0 -405
  202. package/agents/utils/map-reasoning-effort-level.mjs +0 -15
  203. package/agents/utils/post-generate.mjs +0 -133
  204. package/agents/utils/read-current-document-content.mjs +0 -46
  205. package/agents/utils/save-doc-translation.mjs +0 -30
  206. package/agents/utils/save-doc.mjs +0 -54
  207. package/agents/utils/save-output.mjs +0 -26
  208. package/agents/utils/save-sidebar.mjs +0 -38
  209. package/agents/utils/skip-if-content-exists.mjs +0 -27
  210. package/agents/utils/streamline-document-titles-if-needed.mjs +0 -88
  211. package/agents/utils/transform-detail-data-sources.mjs +0 -45
  212. package/assets/report-template/report.html +0 -198
  213. package/docs-mcp/analyze-content-relevance.yaml +0 -50
  214. package/docs-mcp/analyze-docs-relevance.yaml +0 -59
  215. package/docs-mcp/docs-search.yaml +0 -42
  216. package/docs-mcp/get-docs-detail.mjs +0 -41
  217. package/docs-mcp/get-docs-structure.mjs +0 -16
  218. package/docs-mcp/read-doc-content.mjs +0 -119
  219. package/prompts/common/document/content-rules-core.md +0 -20
  220. package/prompts/common/document/markdown-syntax-rules.md +0 -65
  221. package/prompts/common/document/media-file-list-usage-rules.md +0 -18
  222. package/prompts/common/document/openapi-usage-rules.md +0 -189
  223. package/prompts/common/document/role-and-personality.md +0 -16
  224. package/prompts/common/document/user-preferences.md +0 -9
  225. package/prompts/common/document-structure/conflict-resolution-guidance.md +0 -16
  226. package/prompts/common/document-structure/document-icon-generate.md +0 -116
  227. package/prompts/common/document-structure/document-structure-rules.md +0 -43
  228. package/prompts/common/document-structure/document-title-streamline.md +0 -86
  229. package/prompts/common/document-structure/glossary.md +0 -7
  230. package/prompts/common/document-structure/intj-traits.md +0 -5
  231. package/prompts/common/document-structure/openapi-usage-rules.md +0 -28
  232. package/prompts/common/document-structure/output-constraints.md +0 -18
  233. package/prompts/common/document-structure/user-locale-rules.md +0 -10
  234. package/prompts/common/document-structure/user-preferences.md +0 -9
  235. package/prompts/detail/custom/admonition-usage-rules.md +0 -94
  236. package/prompts/detail/custom/code-block-usage-rules.md +0 -163
  237. package/prompts/detail/custom/custom-components/x-card-usage-rules.md +0 -63
  238. package/prompts/detail/custom/custom-components/x-cards-usage-rules.md +0 -83
  239. package/prompts/detail/custom/custom-components/x-field-desc-usage-rules.md +0 -120
  240. package/prompts/detail/custom/custom-components/x-field-group-usage-rules.md +0 -80
  241. package/prompts/detail/custom/custom-components/x-field-usage-rules.md +0 -189
  242. package/prompts/detail/custom/custom-components-usage-rules.md +0 -18
  243. package/prompts/detail/diagram/generate-image-user.md +0 -81
  244. package/prompts/detail/diagram/guide.md +0 -29
  245. package/prompts/detail/diagram/official-examples.md +0 -712
  246. package/prompts/detail/diagram/pre-check.md +0 -23
  247. package/prompts/detail/diagram/role-and-personality.md +0 -2
  248. package/prompts/detail/diagram/rules.md +0 -46
  249. package/prompts/detail/diagram/system-prompt.md +0 -1139
  250. package/prompts/detail/diagram/user-prompt.md +0 -43
  251. package/prompts/detail/generate/detail-example.md +0 -457
  252. package/prompts/detail/generate/document-rules.md +0 -45
  253. package/prompts/detail/generate/system-prompt.md +0 -61
  254. package/prompts/detail/generate/user-prompt.md +0 -99
  255. package/prompts/detail/jsx/rules.md +0 -6
  256. package/prompts/detail/update/system-prompt.md +0 -121
  257. package/prompts/detail/update/user-prompt.md +0 -41
  258. package/prompts/evaluate/document-structure.md +0 -93
  259. package/prompts/evaluate/document.md +0 -149
  260. package/prompts/media/media-description/system-prompt.md +0 -43
  261. package/prompts/media/media-description/user-prompt.md +0 -17
  262. package/prompts/structure/check-document-structure.md +0 -93
  263. package/prompts/structure/document-rules.md +0 -21
  264. package/prompts/structure/find-documents-to-add-links.md +0 -52
  265. package/prompts/structure/generate/system-prompt.md +0 -13
  266. package/prompts/structure/generate/user-prompt.md +0 -137
  267. package/prompts/structure/review/structure-review-system.md +0 -81
  268. package/prompts/structure/structure-example.md +0 -89
  269. package/prompts/structure/structure-getting-started.md +0 -10
  270. package/prompts/structure/update/system-prompt.md +0 -93
  271. package/prompts/structure/update/user-prompt.md +0 -43
  272. package/prompts/translate/admonition.md +0 -20
  273. package/prompts/translate/code-block.md +0 -33
  274. package/prompts/utils/analyze-document-feedback-intent.md +0 -54
  275. package/prompts/utils/analyze-structure-feedback-intent.md +0 -43
  276. package/prompts/utils/feedback-refiner.md +0 -105
  277. package/types/document-schema.mjs +0 -55
  278. package/types/document-structure-schema.mjs +0 -261
  279. package/utils/check-document-has-diagram.mjs +0 -95
  280. package/utils/conflict-detector.mjs +0 -149
  281. package/utils/constants/index.mjs +0 -620
  282. package/utils/constants/linter.mjs +0 -102
  283. package/utils/d2-utils.mjs +0 -205
  284. package/utils/debug.mjs +0 -3
  285. package/utils/delete-diagram-images.mjs +0 -99
  286. package/utils/diagram-version-utils.mjs +0 -14
  287. package/utils/docs-finder-utils.mjs +0 -548
  288. package/utils/evaluate/report-utils.mjs +0 -132
  289. package/utils/extract-api.mjs +0 -32
  290. package/utils/file-utils.mjs +0 -960
  291. package/utils/history-utils.mjs +0 -203
  292. package/utils/icon-map.mjs +0 -26
  293. package/utils/image-compress.mjs +0 -154
  294. package/utils/kroki-utils.mjs +0 -173
  295. package/utils/linter/index.mjs +0 -50
  296. package/utils/load-config.mjs +0 -78
  297. package/utils/markdown/index.mjs +0 -26
  298. package/utils/markdown-checker.mjs +0 -694
  299. package/utils/mermaid-validator.mjs +0 -140
  300. package/utils/mermaid-worker-pool.mjs +0 -250
  301. package/utils/mermaid-worker.mjs +0 -233
  302. package/utils/openapi/index.mjs +0 -28
  303. package/utils/preferences-utils.mjs +0 -175
  304. package/utils/request.mjs +0 -10
  305. package/utils/sync-diagram-to-translations.mjs +0 -272
  306. package/utils/translate-diagram-images.mjs +0 -807
  307. package/utils/utils.mjs +0 -1354
  308. /package/{prompts/translate → agents/localize/prompts}/glossary.md +0 -0
@@ -1,807 +0,0 @@
1
- import { readFileContent } from "./docs-finder-utils.mjs";
2
- import { debug } from "./debug.mjs";
3
- import path from "node:path";
4
- import fs from "fs-extra";
5
- import { copyFile } from "node:fs/promises";
6
- import { diagramImageFullRegex } from "./d2-utils.mjs";
7
- import { calculateImageTimestamp } from "./diagram-version-utils.mjs";
8
- import { getFileName } from "./utils.mjs";
9
- import { compressImage } from "./image-compress.mjs";
10
- import { pathExists } from "./file-utils.mjs";
11
-
12
- // Constants
13
- const DEFAULT_DIAGRAM_TYPE = "architecture";
14
- const DEFAULT_ASPECT_RATIO = "16:9";
15
- const DEFAULT_ALT_TEXT = "Diagram";
16
- const DEFAULT_IMAGE_QUALITY = 85;
17
- const DEFAULT_IMAGE_SIZE = "1K";
18
- const DEFAULT_MIME_TYPE = "image/jpeg";
19
-
20
- /**
21
- * Find translation files for a document, filtered by selected languages
22
- * @param {string} docPath - Document path (e.g., "/guides/getting-started")
23
- * @param {string} docsDir - Documentation directory
24
- * @param {string} locale - Main language locale (e.g., "en")
25
- * @param {Array<string>} selectedLanguages - Array of selected language codes to translate (e.g., ["zh", "ja"])
26
- * @returns {Promise<Array<{language: string, fileName: string}>>} - Array of translation file info
27
- */
28
- async function findTranslationFiles(docPath, docsDir, locale, selectedLanguages = null) {
29
- // Convert path to flat filename format
30
- const flatName = docPath.replace(/^\//, "").replace(/\//g, "-");
31
-
32
- try {
33
- const files = await fs.readdir(docsDir);
34
- const translationFiles = [];
35
- const mainFileName = locale === "en" ? `${flatName}.md` : `${flatName}.${locale}.md`;
36
-
37
- // Filter files to find translation files matching the pattern
38
- for (const file of files) {
39
- if (!file.endsWith(".md")) continue;
40
- if (file === mainFileName) continue; // Skip main language file
41
-
42
- // Case 1: File without language suffix (xxx.md) - this is English translation when main language is not English
43
- if (file === `${flatName}.md` && locale !== "en") {
44
- translationFiles.push({
45
- language: "en",
46
- fileName: file,
47
- });
48
- continue;
49
- }
50
-
51
- // Case 2: File with language suffix (xxx.{lang}.md) - all other translations
52
- if (file.startsWith(`${flatName}.`) && file.match(/\.\w+(-\w+)?\.md$/)) {
53
- const langMatch = file.match(/\.(\w+(-\w+)?)\.md$/);
54
- if (langMatch && langMatch[1] !== locale) {
55
- const fileLanguage = langMatch[1];
56
- // If selectedLanguages is provided, only include files for selected languages
57
- if (selectedLanguages === null || selectedLanguages.includes(fileLanguage)) {
58
- translationFiles.push({
59
- language: fileLanguage,
60
- fileName: file,
61
- });
62
- }
63
- }
64
- }
65
- }
66
-
67
- return translationFiles;
68
- } catch (error) {
69
- debug(`⚠️ Could not read translation files from ${docsDir}: ${error.message}`);
70
- return [];
71
- }
72
- }
73
-
74
- /**
75
- * Extract diagram images with timestamp from content
76
- * Supports both new format (with timestamp) and old format (without timestamp)
77
- * Uses a single regex to extract all information in one pass
78
- * @param {string} content - Document content
79
- * @returns {Array<{type: string, aspectRatio: string, timestamp: string|null, path: string, altText: string, fullMatch: string, index: number}>} - Array of diagram image info
80
- */
81
- function extractDiagramImagesWithTimestamp(content) {
82
- const images = [];
83
-
84
- // Use unified regex to match both old and new formats in one pass
85
- // Captures: type, aspectRatio, optional timestamp, altText, path, fullMatch
86
- const matches = Array.from((content || "").matchAll(diagramImageFullRegex));
87
- for (const match of matches) {
88
- images.push({
89
- type: match[1] || DEFAULT_DIAGRAM_TYPE, // Diagram type (e.g., "architecture", "guide")
90
- aspectRatio: match[2] || DEFAULT_ASPECT_RATIO, // Aspect ratio (e.g., "16:9", "4:3")
91
- timestamp: (match[3] || "").replace(/^:/, ""), // Timestamp without leading colon (null for old format)
92
- altText: match[4] || DEFAULT_ALT_TEXT, // Alt text from markdown
93
- path: match[5] || "", // Image path
94
- fullMatch: match[0] || "", // Full matched block
95
- index: match.index || 0, // Position in document
96
- });
97
- }
98
-
99
- // Sort by position in document
100
- images.sort((a, b) => a.index - b.index);
101
-
102
- return images;
103
- }
104
-
105
- /**
106
- * Convert diagram info to mediaFile format for image generation
107
- * @param {Object} diagramInfo - Diagram info with path
108
- * @param {string} docPath - Document path
109
- * @param {string} docsDir - Documentation directory
110
- * @returns {Promise<Object|null>} - MediaFile object or null if file doesn't exist
111
- */
112
- async function convertDiagramInfoToMediaFile(diagramInfo, docPath, docsDir) {
113
- if (!diagramInfo || !diagramInfo.path) {
114
- return null;
115
- }
116
-
117
- try {
118
- const imagePath = diagramInfo.path;
119
-
120
- // Resolve absolute path
121
- let absolutePath;
122
- if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) {
123
- // Remote URL, cannot convert to local file
124
- return null;
125
- } else if (path.isAbsolute(imagePath)) {
126
- absolutePath = imagePath;
127
- } else {
128
- // Relative path resolution:
129
- // - If path starts with "../", it's relative to the document directory
130
- // - Otherwise, it's relative to docsDir (e.g., "assets/diagram/...")
131
- if (imagePath.startsWith("../")) {
132
- // Relative to document directory
133
- const docDir = path.dirname(docPath);
134
- const imageRelativePath = path.join(docDir, imagePath).replace(/\\/g, "/");
135
- absolutePath = path.join(process.cwd(), docsDir, imageRelativePath);
136
- } else {
137
- // Relative to docsDir (most common case: "assets/diagram/...")
138
- absolutePath = path.join(process.cwd(), docsDir, imagePath);
139
- }
140
- }
141
-
142
- // Normalize path
143
- const normalizedPath = path.normalize(absolutePath);
144
-
145
- // Check if file exists
146
- if (!(await fs.pathExists(normalizedPath))) {
147
- return null;
148
- }
149
-
150
- // Get file extension for mimeType detection
151
- const ext = path.extname(normalizedPath).toLowerCase();
152
- let mimeType = DEFAULT_MIME_TYPE;
153
- if (ext === ".png") mimeType = "image/png";
154
- else if (ext === ".gif") mimeType = "image/gif";
155
- else if (ext === ".webp") mimeType = "image/webp";
156
-
157
- return {
158
- type: "local",
159
- path: normalizedPath,
160
- filename: path.basename(normalizedPath),
161
- mimeType,
162
- };
163
- } catch (error) {
164
- debug(`Failed to convert diagram info to mediaFile: ${error.message}`);
165
- return null;
166
- }
167
- }
168
-
169
- /**
170
- * Generate translated image filename with language suffix
171
- * @param {string} originalPath - Original image path (e.g., "assets/diagram/overview-diagram-0.jpg")
172
- * @param {string} language - Target language code (e.g., "zh")
173
- * @returns {string} - Translated image path (e.g., "assets/diagram/overview-diagram-0.zh.jpg")
174
- */
175
- function generateTranslatedImagePath(originalPath, language) {
176
- const pathParts = originalPath.split(".");
177
- if (pathParts.length < 2) {
178
- // No extension, just append language
179
- return `${originalPath}.${language}`;
180
- }
181
- // Insert language before extension
182
- const ext = pathParts.pop();
183
- const base = pathParts.join(".");
184
- return `${base}.${language}.${ext}`;
185
- }
186
-
187
- /**
188
- * Compress and copy generated image to target location
189
- * Tries compression first, falls back to copying if compression fails
190
- * @param {Object} generatedImage - Generated image object with path
191
- * @param {string} targetPath - Target absolute path for the image
192
- * @param {string} fileName - Translation file name for error reporting
193
- * @returns {Promise<void>}
194
- */
195
- async function compressAndCopyImage(generatedImage, targetPath, fileName = null) {
196
- try {
197
- const compressedPath = await compressImage(generatedImage.path, {
198
- quality: DEFAULT_IMAGE_QUALITY,
199
- outputPath: targetPath,
200
- });
201
-
202
- if (compressedPath === generatedImage.path) {
203
- // Compression failed, copy original
204
- await copyFile(generatedImage.path, targetPath);
205
- }
206
- } catch (error) {
207
- console.error(`copy original image ${fileName} to ${targetPath}`);
208
- debug(`copy original image ${fileName} to ${targetPath}`, error);
209
- await copyFile(generatedImage.path, targetPath);
210
- }
211
- }
212
-
213
- async function ensureImageTimestamp(imageInfo, docPath, docsDir) {
214
- if (imageInfo.timestamp) {
215
- return imageInfo.timestamp;
216
- }
217
-
218
- const existingImage = await convertDiagramInfoToMediaFile(
219
- { path: imageInfo.path },
220
- docPath,
221
- docsDir,
222
- );
223
-
224
- return existingImage
225
- ? await calculateImageTimestamp(existingImage.path)
226
- : Math.floor(Date.now() / 1000).toString();
227
- }
228
-
229
- function createImageMarkdown(diagramType, aspectRatio, timestamp, altText, imagePath) {
230
- const type = diagramType || DEFAULT_DIAGRAM_TYPE;
231
- const ratio = aspectRatio || DEFAULT_ASPECT_RATIO;
232
- const alt = altText || DEFAULT_ALT_TEXT;
233
- const timestampPart = timestamp ? `:${timestamp}` : "";
234
- return `<!-- DIAGRAM_IMAGE_START:${type}:${ratio}${timestampPart} -->\n![${alt}](${imagePath})\n<!-- DIAGRAM_IMAGE_END -->`;
235
- }
236
-
237
- const processedDocuments = new Set();
238
-
239
- /**
240
- * Translate diagram images for translation documents
241
- * Only translates images when timestamp differs between main and translation documents
242
- * If main document images don't have timestamp (old format), generates timestamps and updates both main and translation documents
243
- * @param {string} mainContent - Main document content (with timestamp in markers)
244
- * @param {string} docPath - Document path
245
- * @param {string} docsDir - Documentation directory
246
- * @param {string} locale - Main language locale
247
- * @param {Object} options - Options object with context for invoking agents
248
- * @param {Array<string>} selectedLanguages - Selected languages to translate
249
- * @returns {Promise<{updated: number, skipped: number, errors: Array, mainContentUpdated: string|null}>} - Translation result with updated main content
250
- */
251
- export async function translateDiagramImages(
252
- mainContent,
253
- docPath,
254
- docsDir,
255
- locale = "en",
256
- options = {},
257
- selectedLanguages = null,
258
- ) {
259
- // Avoid duplicate processing when called multiple times in iterate_on context
260
- const documentKey = `${docPath}:${docsDir}:${locale}`;
261
- if (processedDocuments.has(documentKey)) {
262
- debug(`⏭️ Diagram images for ${docPath} already processed, skipping`);
263
- return { updated: 0, skipped: 0, errors: [] };
264
- }
265
- processedDocuments.add(documentKey);
266
-
267
- const result = {
268
- updated: 0,
269
- skipped: 0,
270
- errors: [],
271
- mainContentUpdated: null, // Will contain updated main content if timestamps were added
272
- };
273
-
274
- try {
275
- // Find translation files, filtered by selected languages
276
- const translationFiles = await findTranslationFiles(
277
- docPath,
278
- docsDir,
279
- locale,
280
- selectedLanguages,
281
- );
282
-
283
- if (translationFiles.length === 0) {
284
- debug("ℹ️ No translation files found, skipping image translation");
285
- return result;
286
- }
287
-
288
- const mainImages = extractDiagramImagesWithTimestamp(mainContent);
289
- let updatedMainContent = mainContent;
290
- let mainContentNeedsUpdate = false;
291
-
292
- if (mainImages.length === 0) {
293
- debug("ℹ️ No diagram images in main content, skipping translation");
294
- return result;
295
- }
296
-
297
- for (let i = 0; i < mainImages.length; i++) {
298
- const mainImage = mainImages[i];
299
- if (!mainImage.timestamp) {
300
- const newTimestamp = await ensureImageTimestamp(mainImage, docPath, docsDir);
301
- mainImages[i] = { ...mainImage, timestamp: newTimestamp };
302
- updatedMainContent = updatedMainContent.replace(
303
- mainImage.fullMatch,
304
- createImageMarkdown(
305
- mainImage.type,
306
- mainImage.aspectRatio,
307
- newTimestamp,
308
- mainImage.altText,
309
- mainImage.path,
310
- ),
311
- );
312
- mainContentNeedsUpdate = true;
313
- }
314
- }
315
-
316
- if (mainContentNeedsUpdate) {
317
- const mainFileName = getFileName(docPath, locale);
318
- await fs.writeFile(path.join(docsDir, mainFileName), updatedMainContent, "utf8");
319
- result.mainContentUpdated = updatedMainContent;
320
- debug(`✅ Updated main document with timestamps: ${mainFileName}`);
321
- }
322
- for (const { language, fileName } of translationFiles) {
323
- try {
324
- const translationFilePath = path.join(docsDir, fileName);
325
-
326
- // Check if translation file exists before reading to avoid unnecessary warnings
327
- const fileExists = await pathExists(translationFilePath);
328
- if (!fileExists) {
329
- debug(`ℹ️ Translation file does not exist yet: ${fileName} (skipping)`);
330
- result.skipped++;
331
- continue;
332
- }
333
-
334
- const translationContent = await readFileContent(docsDir, fileName);
335
-
336
- if (translationContent === null || translationContent === undefined) {
337
- debug(`⚠️ Could not read translation file: ${fileName}`);
338
- result.skipped++;
339
- continue;
340
- }
341
-
342
- const translationImages = extractDiagramImagesWithTimestamp(translationContent);
343
- let hasChanges = false;
344
- let updatedContent = translationContent;
345
- for (let i = 0; i < mainImages.length; i++) {
346
- const mainImage = mainImages[i];
347
- const translationImage = translationImages[i];
348
-
349
- const translationImagePath = translationImage?.path || "";
350
- const hasLanguageSuffix =
351
- translationImagePath.includes(`.${language}.`) ||
352
- translationImagePath.endsWith(`.${language}`);
353
-
354
- const needsTranslation =
355
- mainImage &&
356
- (!translationImage ||
357
- !hasLanguageSuffix ||
358
- !translationImage.timestamp ||
359
- translationImage.timestamp !== mainImage.timestamp);
360
-
361
- if (!needsTranslation) {
362
- continue;
363
- }
364
-
365
- const existingImage = await convertDiagramInfoToMediaFile(
366
- { path: mainImage.path },
367
- docPath,
368
- docsDir,
369
- );
370
-
371
- if (!existingImage) {
372
- debug(
373
- `⏭️ Main image not found: ${mainImage.path}, skipping translation for ${language}`,
374
- );
375
- continue;
376
- }
377
-
378
- try {
379
- const translateDiagramAgent = options.context?.agents?.["translateDiagram"];
380
- if (!translateDiagramAgent) {
381
- debug(`⚠️ translateDiagram agent not found, skipping translation`);
382
- result.errors.push({
383
- file: fileName,
384
- imageIndex: i,
385
- error: "translateDiagram agent not found",
386
- });
387
- continue;
388
- }
389
-
390
- // Call translateDiagram agent with translation parameters
391
- const imageResult = await options.context.invoke(translateDiagramAgent, {
392
- existingImage: [existingImage], // Pass the main document image for translation
393
- ratio: mainImage.aspectRatio || DEFAULT_ASPECT_RATIO, // Aspect ratio from main image
394
- size: DEFAULT_IMAGE_SIZE, // Image clarity/size
395
- locale: language, // Target language code for translation
396
- });
397
-
398
- let generatedImage = null;
399
- if (
400
- imageResult?.images &&
401
- Array.isArray(imageResult.images) &&
402
- imageResult.images.length > 0
403
- ) {
404
- generatedImage = imageResult.images[0];
405
- } else if (imageResult?.image || imageResult?.imageUrl || imageResult?.path) {
406
- generatedImage = {
407
- path: imageResult.image || imageResult.imageUrl || imageResult.path,
408
- filename: path.basename(
409
- imageResult.image || imageResult.imageUrl || imageResult.path,
410
- ),
411
- mimeType: imageResult.mimeType || DEFAULT_MIME_TYPE,
412
- type: "local",
413
- };
414
- }
415
-
416
- if (!generatedImage) {
417
- debug(`⚠️ No image generated for ${language} diagram ${i}`);
418
- result.errors.push({
419
- file: fileName,
420
- imageIndex: i,
421
- error: "No image generated from agent",
422
- });
423
- continue;
424
- }
425
-
426
- const translatedImagePath = generateTranslatedImagePath(mainImage.path, language);
427
- const translatedImageAbsolutePath = path.join(
428
- process.cwd(),
429
- docsDir,
430
- translatedImagePath,
431
- );
432
- await fs.ensureDir(path.dirname(translatedImageAbsolutePath));
433
- await compressAndCopyImage(generatedImage, translatedImageAbsolutePath, fileName);
434
-
435
- const altText = translationImage?.altText || mainImage.altText || DEFAULT_ALT_TEXT;
436
- const newImageMarkdown = createImageMarkdown(
437
- mainImage.type,
438
- mainImage.aspectRatio,
439
- mainImage.timestamp,
440
- altText,
441
- translatedImagePath,
442
- );
443
-
444
- if (translationImage) {
445
- updatedContent = updatedContent.replace(translationImage.fullMatch, newImageMarkdown);
446
- } else {
447
- const lastImageIndex =
448
- translationImages.length > 0
449
- ? translationImages[translationImages.length - 1].index +
450
- translationImages[translationImages.length - 1].fullMatch.length
451
- : updatedContent.length;
452
- updatedContent =
453
- updatedContent.slice(0, lastImageIndex) +
454
- "\n\n" +
455
- newImageMarkdown +
456
- "\n\n" +
457
- updatedContent.slice(lastImageIndex);
458
- }
459
-
460
- hasChanges = true;
461
- } catch (error) {
462
- console.error(`❌ Error translating diagram image ${fileName} for ${language}`);
463
- debug(`❌ Error translating diagram image ${fileName} for ${language}`, error);
464
- result.errors.push({
465
- file: fileName,
466
- imageIndex: i,
467
- error: error.message,
468
- });
469
- }
470
- }
471
-
472
- if (hasChanges) {
473
- await fs.writeFile(translationFilePath, updatedContent, "utf8");
474
- result.updated++;
475
- } else {
476
- result.skipped++;
477
- }
478
- } catch (error) {
479
- debug(`❌ Error processing translation file ${fileName}: ${error.message}`);
480
- result.errors.push({
481
- file: fileName,
482
- error: error.message,
483
- });
484
- }
485
- }
486
-
487
- return result;
488
- } finally {
489
- // Clear processed documents after completion (even on error) to allow re-processing if needed
490
- processedDocuments.delete(documentKey);
491
- }
492
- }
493
-
494
- /**
495
- * Cache diagram images for translation (before document translation)
496
- * This function checks if images need translation and caches the translated image info
497
- * @param {string} mainContent - Main document content
498
- * @param {string} translationContent - Current translation document content (may be empty for new translations)
499
- * @param {string} docPath - Document path
500
- * @param {string} docsDir - Documentation directory
501
- * @param {string} locale - Main language locale
502
- * @param {string} language - Target language code
503
- * @param {Object} options - Options object with context for invoking agents
504
- * @param {boolean} shouldTranslateDiagramsOnly - Whether to translate diagrams only (from --diagram flag)
505
- * @returns {Promise<Array<{originalMatch: string, translatedMarkdown: string, index: number}>|null>} - Cached image info or null
506
- */
507
- export async function cacheDiagramImagesForTranslation(
508
- mainContent,
509
- translationContent,
510
- docPath,
511
- docsDir,
512
- locale = "en",
513
- language,
514
- options = {},
515
- shouldTranslateDiagramsOnly = false,
516
- ) {
517
- try {
518
- const mainImages = extractDiagramImagesWithTimestamp(mainContent);
519
- let updatedMainContent = mainContent;
520
- let mainContentNeedsUpdate = false;
521
-
522
- if (mainImages.length === 0) {
523
- debug("ℹ️ No diagram images in main content");
524
- return null;
525
- }
526
-
527
- for (let i = 0; i < mainImages.length; i++) {
528
- const mainImage = mainImages[i];
529
- if (!mainImage.timestamp) {
530
- const newTimestamp = await ensureImageTimestamp(mainImage, docPath, docsDir);
531
- mainImages[i] = { ...mainImage, timestamp: newTimestamp };
532
- updatedMainContent = updatedMainContent.replace(
533
- mainImage.fullMatch,
534
- createImageMarkdown(
535
- mainImage.type,
536
- mainImage.aspectRatio,
537
- newTimestamp,
538
- mainImage.altText,
539
- mainImage.path,
540
- ),
541
- );
542
- mainContentNeedsUpdate = true;
543
- }
544
- }
545
-
546
- if (mainContentNeedsUpdate) {
547
- const mainFileName = getFileName(docPath, locale);
548
- await fs.writeFile(path.join(docsDir, mainFileName), updatedMainContent, "utf8");
549
- debug(`✅ Updated main document with timestamps: ${mainFileName}`);
550
- }
551
-
552
- const translationImages = translationContent
553
- ? extractDiagramImagesWithTimestamp(translationContent)
554
- : [];
555
-
556
- const cachedImages = [];
557
-
558
- for (let i = 0; i < mainImages.length; i++) {
559
- const mainImage = mainImages[i];
560
- const translationImage = translationImages[i];
561
-
562
- let needsTranslation = false;
563
-
564
- if (shouldTranslateDiagramsOnly) {
565
- // When --diagram flag is set, always regenerate translation images regardless of existing images
566
- needsTranslation = true;
567
- debug(
568
- `🔄 --diagram flag set: forcing regeneration of translation for ${language} diagram ${i}`,
569
- );
570
- } else {
571
- // Normal mode: check if translation is needed based on timestamp and language suffix
572
- const translationImagePath = translationImage?.path || "";
573
- const hasLanguageSuffix =
574
- translationImagePath.includes(`.${language}.`) ||
575
- translationImagePath.endsWith(`.${language}`);
576
- needsTranslation =
577
- !translationImage ||
578
- !hasLanguageSuffix ||
579
- !translationImage.timestamp ||
580
- translationImage.timestamp !== mainImage.timestamp;
581
- }
582
-
583
- if (!needsTranslation) {
584
- // No translation needed, but we should cache the existing translation image
585
- // to ensure it's preserved in the final document (this applies to both normal and --diagram mode)
586
- if (translationImage) {
587
- cachedImages.push({
588
- originalMatch: translationImage.fullMatch,
589
- translatedMarkdown: translationImage.fullMatch, // Keep existing markdown
590
- index: translationImage.index,
591
- mainImageIndex: mainImage.index,
592
- });
593
- debug(
594
- `💾 Cached existing diagram image for ${language} diagram ${i} (timestamp matches, no translation needed)`,
595
- );
596
- }
597
- continue;
598
- }
599
-
600
- const existingImage = await convertDiagramInfoToMediaFile(
601
- { path: mainImage.path },
602
- docPath,
603
- docsDir,
604
- );
605
-
606
- if (!existingImage) {
607
- debug(`⏭️ Main image not found: ${mainImage.path}, skipping translation for ${language}`);
608
- continue;
609
- }
610
-
611
- try {
612
- const translateDiagramAgent = options.context?.agents?.["translateDiagram"];
613
- if (!translateDiagramAgent) {
614
- debug(`⚠️ translateDiagram agent not found, skipping translation`);
615
- continue;
616
- }
617
-
618
- const imageResult = await options.context.invoke(translateDiagramAgent, {
619
- existingImage: [existingImage],
620
- ratio: mainImage.aspectRatio || DEFAULT_ASPECT_RATIO,
621
- size: DEFAULT_IMAGE_SIZE,
622
- locale: language,
623
- });
624
-
625
- let generatedImage = null;
626
- if (
627
- imageResult?.images &&
628
- Array.isArray(imageResult.images) &&
629
- imageResult.images.length > 0
630
- ) {
631
- generatedImage = imageResult.images[0];
632
- } else if (imageResult?.image || imageResult?.imageUrl || imageResult?.path) {
633
- generatedImage = {
634
- path: imageResult.image || imageResult.imageUrl || imageResult.path,
635
- filename: path.basename(imageResult.image || imageResult.imageUrl || imageResult.path),
636
- mimeType: imageResult.mimeType || DEFAULT_MIME_TYPE,
637
- type: "local",
638
- };
639
- }
640
-
641
- if (!generatedImage) {
642
- debug(`⚠️ No image generated for ${language} diagram ${i}`);
643
- continue;
644
- }
645
-
646
- const translatedImagePath = generateTranslatedImagePath(mainImage.path, language);
647
- const translatedImageAbsolutePath = path.join(process.cwd(), docsDir, translatedImagePath);
648
- await fs.ensureDir(path.dirname(translatedImageAbsolutePath));
649
- const translationFileName = getFileName(docPath, language);
650
- await compressAndCopyImage(
651
- generatedImage,
652
- translatedImageAbsolutePath,
653
- translationFileName,
654
- );
655
-
656
- const altText = translationImage?.altText || mainImage.altText || DEFAULT_ALT_TEXT;
657
- const newImageMarkdown = createImageMarkdown(
658
- mainImage.type,
659
- mainImage.aspectRatio,
660
- mainImage.timestamp,
661
- altText,
662
- translatedImagePath,
663
- );
664
-
665
- cachedImages.push({
666
- originalMatch: translationImage?.fullMatch || null,
667
- translatedMarkdown: newImageMarkdown,
668
- index: translationImage?.index || mainImage.index,
669
- mainImageIndex: mainImage.index,
670
- });
671
- } catch (error) {
672
- const translationFileName = getFileName(docPath, language);
673
- console.error(`❌ Error translating diagram image ${translationFileName} for ${language}`);
674
- debug(`❌ Error translating diagram image ${translationFileName} for ${language}`, error);
675
- // Continue processing other images even if one fails
676
- }
677
- }
678
-
679
- return cachedImages.length > 0 ? cachedImages : null;
680
- } catch (error) {
681
- debug(`❌ Error caching diagram images: ${error.message}`);
682
- return null;
683
- }
684
- }
685
-
686
- /**
687
- * Agent wrapper for caching diagram images during translation workflow
688
- * This agent is called BEFORE translate-document-wrapper.mjs to cache image info
689
- * @param {Object} input - Input parameters
690
- * @param {string} input.path - Document path
691
- * @param {string} input.docsDir - Documentation directory
692
- * @param {string} input.locale - Main language locale
693
- * @param {boolean} input.shouldTranslateDiagramsOnly - Whether to translate diagrams only (from --diagram flag)
694
- * @param {string} input.language - Current language being translated
695
- * @param {Object} options - Options with context for invoking agents
696
- * @returns {Promise<Object>} - Result object with cached image info
697
- */
698
- export default async function translateDiagramImagesAgent(input, options) {
699
- // Extract parameters from input
700
- const docPath = input.path;
701
- const docsDir = input.docsDir;
702
- const locale = input.locale || "en";
703
- const currentLanguage = input.language;
704
- const shouldTranslateDiagramsOnly = input.shouldTranslateDiagramsOnly || false;
705
-
706
- if (!docPath || !docsDir || !currentLanguage) {
707
- debug(
708
- "⚠️ Missing required parameters for diagram image translation (path, docsDir, or language)",
709
- );
710
- debug(` - path: ${docPath}`);
711
- debug(` - docsDir: ${docsDir}`);
712
- debug(` - language: ${currentLanguage}`);
713
- return { ...input, cachedDiagramImages: null };
714
- }
715
-
716
- try {
717
- // Read main document content
718
- const mainFileName = getFileName(docPath, locale);
719
- const mainContent = await readFileContent(docsDir, mainFileName);
720
-
721
- if (!mainContent) {
722
- debug(`⚠️ Could not read main document: ${mainFileName}`);
723
- return { ...input, cachedDiagramImages: null };
724
- }
725
-
726
- // Read current translation file content (if exists) to check timestamps
727
- const translationFileName = getFileName(docPath, currentLanguage);
728
- const translationFilePath = path.join(docsDir, translationFileName);
729
-
730
- // Check if translation file exists before reading to avoid unnecessary warnings
731
- const translationFileExists = await pathExists(translationFilePath);
732
- const translationContent = translationFileExists
733
- ? await readFileContent(docsDir, translationFileName)
734
- : null;
735
-
736
- // Cache diagram images for translation
737
- // This function will:
738
- // 1. If --diagram flag is set, always translate images
739
- // 2. Otherwise, check if main document image timestamps match translation document timestamps
740
- // 3. Cache the image info that needs to be inserted into translated document
741
- const cachedImages = await cacheDiagramImagesForTranslation(
742
- mainContent,
743
- translationContent || "",
744
- docPath,
745
- docsDir,
746
- locale,
747
- currentLanguage,
748
- options,
749
- shouldTranslateDiagramsOnly,
750
- );
751
-
752
- if (cachedImages && cachedImages.length > 0) {
753
- // Check if any images were actually translated (not just cached existing ones)
754
- const translatedCount = cachedImages.filter(
755
- (img) => img.translatedMarkdown !== img.originalMatch,
756
- ).length;
757
- if (translatedCount > 0) {
758
- debug(
759
- `✅ Cached ${cachedImages.length} diagram image(s) for ${currentLanguage} (${translatedCount} translated)`,
760
- );
761
- } else {
762
- debug(
763
- `ℹ️ Cached ${cachedImages.length} existing diagram image(s) for ${currentLanguage} (no translation needed)`,
764
- );
765
- }
766
- } else {
767
- debug(`ℹ️ No diagram images found or cached for ${currentLanguage}`);
768
- }
769
-
770
- // In --diagram mode:
771
- // - If translation document exists: use existing translation content (skip document translation, only replace images)
772
- // - If translation document doesn't exist: allow document translation first, then replace images
773
- let finalTranslation = input.translation;
774
- let finalIsApproved = input.isApproved;
775
-
776
- if (shouldTranslateDiagramsOnly) {
777
- if (translationContent) {
778
- // Translation document exists: use existing content, skip document translation
779
- finalTranslation = translationContent;
780
- finalIsApproved = true; // Skip document translation
781
- debug(
782
- `ℹ️ --diagram mode: using existing translation content for ${currentLanguage} (will only replace diagram images)`,
783
- );
784
- } else {
785
- // Translation document doesn't exist: allow document translation first
786
- finalTranslation = undefined; // Let translate-document-wrapper.mjs handle translation
787
- finalIsApproved = false; // Allow document translation
788
- debug(
789
- `ℹ️ --diagram mode: translation document not found for ${currentLanguage}, will translate document first, then replace diagram images`,
790
- );
791
- }
792
- }
793
-
794
- return {
795
- ...input,
796
- translation: finalTranslation,
797
- isApproved: finalIsApproved,
798
- cachedDiagramImages: cachedImages || null,
799
- };
800
- } catch (error) {
801
- // Don't fail the translation if image translation fails
802
- debug(`⚠️ Failed to cache diagram images: ${error.message}`);
803
- return { ...input, cachedDiagramImages: null };
804
- }
805
- }
806
-
807
- translateDiagramImagesAgent.task_render_mode = "hide";