@aigne/doc-smith 0.9.9-beta → 0.9.9-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/agents/create/aggregate-document-structure.mjs +21 -0
- package/agents/create/analyze-diagram-type-llm.yaml +1 -2
- package/agents/create/analyze-diagram-type.mjs +160 -2
- package/agents/create/generate-diagram-image.yaml +31 -0
- package/agents/create/generate-structure.yaml +1 -12
- package/agents/create/replace-d2-with-image.mjs +12 -27
- package/agents/create/utils/merge-document-structures.mjs +9 -3
- package/agents/localize/index.yaml +4 -0
- package/agents/localize/save-doc-translation-or-skip.mjs +18 -0
- package/agents/localize/set-review-content.mjs +58 -0
- package/agents/localize/translate-diagram.yaml +62 -0
- package/agents/localize/translate-document-wrapper.mjs +34 -0
- package/agents/localize/translate-multilingual.yaml +15 -9
- package/agents/localize/translate-or-skip-diagram.mjs +52 -0
- package/agents/publish/translate-meta.mjs +58 -6
- package/agents/update/generate-diagram.yaml +25 -8
- package/agents/update/index.yaml +1 -8
- package/agents/update/save-and-translate-document.mjs +5 -1
- package/agents/update/update-single/update-single-document-detail.mjs +52 -10
- package/agents/utils/analyze-feedback-intent.mjs +197 -80
- package/agents/utils/check-detail-result.mjs +14 -1
- package/agents/utils/choose-docs.mjs +3 -43
- package/agents/utils/save-doc-translation.mjs +2 -33
- package/agents/utils/save-doc.mjs +3 -37
- package/aigne.yaml +2 -2
- package/package.json +1 -1
- package/prompts/detail/diagram/generate-image-user.md +49 -0
- package/utils/d2-utils.mjs +10 -3
- package/utils/delete-diagram-images.mjs +3 -3
- package/utils/diagram-version-utils.mjs +14 -0
- package/utils/image-compress.mjs +1 -1
- package/utils/sync-diagram-to-translations.mjs +3 -3
- package/utils/translate-diagram-images.mjs +790 -0
- package/agents/update/check-sync-image-flag.mjs +0 -55
- package/agents/update/sync-images-and-exit.mjs +0 -148
|
@@ -1,88 +1,108 @@
|
|
|
1
1
|
import { AIAgent } from "@aigne/core";
|
|
2
2
|
import z from "zod";
|
|
3
|
+
import { diagramImageWithPathRegex } from "../../utils/d2-utils.mjs";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Detect existing diagram in document content using regex
|
|
7
|
+
* Uses generic regex patterns to detect diagram images inserted by the system
|
|
8
|
+
* @param {string} documentContent - Document content to analyze
|
|
9
|
+
* @returns {Object|null} - Diagram info if found, null otherwise
|
|
10
|
+
*/
|
|
11
|
+
function detectExistingDiagram(documentContent) {
|
|
12
|
+
if (!documentContent || typeof documentContent !== "string") {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Use generic regex to detect diagram image blocks with special markers
|
|
17
|
+
// Format: <!-- DIAGRAM_IMAGE_START:type:aspectRatio --><!-- DIAGRAM_IMAGE_END -->
|
|
18
|
+
const pathMatches = Array.from(documentContent.matchAll(diagramImageWithPathRegex));
|
|
19
|
+
|
|
20
|
+
if (pathMatches.length > 0) {
|
|
21
|
+
// Currently each document has only one diagram, so use the first one
|
|
22
|
+
const firstPathMatch = pathMatches[0];
|
|
23
|
+
const imagePath = firstPathMatch[4]; // Path is in capture group 4 (groups: 1=type, 2=aspectRatio, 3=timestamp, 4=path)
|
|
24
|
+
const fullMatch = firstPathMatch[0];
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
path: imagePath,
|
|
28
|
+
index: 0,
|
|
29
|
+
markdown: fullMatch,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
3
35
|
|
|
4
36
|
/**
|
|
5
37
|
* Analyze user feedback to determine the intent type for document updates
|
|
6
|
-
*
|
|
7
|
-
*
|
|
38
|
+
* Supports two modes:
|
|
39
|
+
* - Global mode (no documentContent): Only analyzes feedback to determine intentType
|
|
40
|
+
* - Single document mode (with documentContent): Detects diagrams and provides full analysis
|
|
41
|
+
* Returns: { intentType, diagramInfo, generationMode, changes }
|
|
8
42
|
*/
|
|
9
|
-
export default async function analyzeFeedbackIntent(
|
|
43
|
+
export default async function analyzeFeedbackIntent(
|
|
44
|
+
{ feedback, shouldUpdateDiagrams, documentContent },
|
|
45
|
+
options,
|
|
46
|
+
) {
|
|
47
|
+
// Check if documentContent is available (single document mode vs global mode)
|
|
48
|
+
const hasDocumentContent =
|
|
49
|
+
documentContent && typeof documentContent === "string" && documentContent.trim();
|
|
50
|
+
|
|
10
51
|
// Check if feedback exists and is not empty
|
|
11
52
|
// If feedback is empty and --diagram flag is set, default to updateDiagram
|
|
12
|
-
// Otherwise return null
|
|
13
53
|
if (!feedback || typeof feedback !== "string" || !feedback.trim()) {
|
|
14
54
|
// If --diagram flag is set, default to updateDiagram (user wants to update diagrams)
|
|
15
55
|
if (shouldUpdateDiagrams) {
|
|
16
|
-
|
|
56
|
+
const diagramInfo = hasDocumentContent ? detectExistingDiagram(documentContent) : null;
|
|
57
|
+
return {
|
|
58
|
+
intentType: "updateDiagram",
|
|
59
|
+
diagramInfo,
|
|
60
|
+
generationMode: diagramInfo ? "image-to-image" : "add-new",
|
|
61
|
+
changes: [],
|
|
62
|
+
};
|
|
17
63
|
}
|
|
18
|
-
|
|
64
|
+
// No feedback and no flag - return minimal structure
|
|
65
|
+
const diagramInfo = hasDocumentContent ? detectExistingDiagram(documentContent) : null;
|
|
66
|
+
return {
|
|
67
|
+
intentType: null,
|
|
68
|
+
diagramInfo,
|
|
69
|
+
generationMode: null,
|
|
70
|
+
changes: [],
|
|
71
|
+
};
|
|
19
72
|
}
|
|
20
73
|
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
// The --diagram flag should only be a hint, not override explicit user commands
|
|
74
|
+
// Step 1: Detect existing diagram only if documentContent is available (single document mode)
|
|
75
|
+
const diagramInfo = hasDocumentContent ? detectExistingDiagram(documentContent) : null;
|
|
24
76
|
|
|
25
|
-
|
|
26
|
-
|
|
77
|
+
// Step 2: Build instructions with diagram context (only in single document mode)
|
|
78
|
+
let diagramContextSection = "";
|
|
79
|
+
if (hasDocumentContent) {
|
|
80
|
+
if (diagramInfo) {
|
|
81
|
+
diagramContextSection = `The document contains an existing diagram: ${diagramInfo.path}`;
|
|
82
|
+
} else {
|
|
83
|
+
diagramContextSection = `The document does not contain any diagram images.`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
27
86
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
87
|
+
const instructions = `Analyze the user feedback and classify the intent:
|
|
88
|
+
|
|
89
|
+
**Intent Types:**
|
|
90
|
+
- addDiagram: User wants to add/create a new diagram/image/chart
|
|
91
|
+
- deleteDiagram: User wants to remove/delete an existing diagram/image/chart
|
|
31
92
|
- updateDiagram: User wants to modify/update an existing diagram/image/chart
|
|
32
|
-
- updateDocument: User wants to update
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
- "Remove image from page 3"
|
|
46
|
-
- "Delete the chart showing the flow"
|
|
47
|
-
|
|
48
|
-
**addDiagram** - Use this when user wants to add a new diagram:
|
|
49
|
-
- Keywords: add, create, insert, 添加, 创建, 插入
|
|
50
|
-
- Combined with: diagram, image, picture, chart, graph, 图表, 图片, 图
|
|
51
|
-
- Examples:
|
|
52
|
-
- "Add a diagram showing the architecture"
|
|
53
|
-
- "Create a flow chart"
|
|
54
|
-
- "添加一个架构图"
|
|
55
|
-
|
|
56
|
-
**updateDiagram** - Use this when user wants to modify an existing diagram:
|
|
57
|
-
- Keywords: update, modify, change, improve, 更新, 修改, 改进
|
|
58
|
-
- Combined with: diagram, image, picture, chart, graph, 图表, 图片, 图
|
|
59
|
-
- Examples:
|
|
60
|
-
- "Update the diagram to show the new process"
|
|
61
|
-
- "Modify the chart to include more details"
|
|
62
|
-
- "更新架构图"
|
|
63
|
-
|
|
64
|
-
**updateDocument** - Use this for all other content modifications:
|
|
65
|
-
- Text changes, section updates, content improvements
|
|
66
|
-
- No mention of diagrams/images/charts
|
|
67
|
-
- Examples:
|
|
68
|
-
- "Update the introduction section"
|
|
69
|
-
- "Fix the typo in paragraph 2"
|
|
70
|
-
- "Improve the explanation"
|
|
71
|
-
</intent_classification_rules>
|
|
72
|
-
|
|
73
|
-
<user_feedback>
|
|
74
|
-
{{feedback}}
|
|
75
|
-
</user_feedback>
|
|
76
|
-
|
|
77
|
-
<analysis_guidelines>
|
|
78
|
-
1. Pay close attention to action verbs (remove, delete, add, update, etc.)
|
|
79
|
-
2. Identify the target object (diagram, image, chart, or general content)
|
|
80
|
-
3. If feedback mentions removing/deleting a diagram/image/chart → deleteDiagram
|
|
81
|
-
4. If feedback mentions adding a diagram/image/chart → addDiagram
|
|
82
|
-
5. If feedback mentions updating a diagram/image/chart → updateDiagram
|
|
83
|
-
6. If feedback is about general content without diagram references → updateDocument
|
|
84
|
-
7. When in doubt, prioritize the most explicit action mentioned in the feedback
|
|
85
|
-
</analysis_guidelines>`;
|
|
93
|
+
- updateDocument: User wants to update text/content (not diagram-related)
|
|
94
|
+
|
|
95
|
+
${diagramContextSection ? `**Context:** ${diagramContextSection}` : ""}
|
|
96
|
+
|
|
97
|
+
**User Feedback:**
|
|
98
|
+
${feedback.trim()}
|
|
99
|
+
|
|
100
|
+
**For updateDiagram, determine generationMode:**
|
|
101
|
+
- "image-to-image": Diagram exists AND user describes changes (default when diagram exists)
|
|
102
|
+
- "text-only": User explicitly requests regenerating from text only
|
|
103
|
+
- "add-new": No existing diagram
|
|
104
|
+
|
|
105
|
+
Analyze the feedback and return the intent type, generationMode (if applicable), and any specific changes mentioned.`;
|
|
86
106
|
|
|
87
107
|
try {
|
|
88
108
|
const analyzeUpdateFeedbackIntentAgent = AIAgent.from({
|
|
@@ -93,6 +113,14 @@ You must analyze the user's feedback and classify it into one of the following i
|
|
|
93
113
|
instructions,
|
|
94
114
|
inputSchema: z.object({
|
|
95
115
|
feedback: z.string().describe("User feedback for content modifications"),
|
|
116
|
+
diagramInfo: z
|
|
117
|
+
.object({
|
|
118
|
+
path: z.string(),
|
|
119
|
+
index: z.number(),
|
|
120
|
+
markdown: z.string(),
|
|
121
|
+
})
|
|
122
|
+
.nullable()
|
|
123
|
+
.describe("Existing diagram information if found, null otherwise"),
|
|
96
124
|
}),
|
|
97
125
|
outputSchema: z.object({
|
|
98
126
|
intentType: z
|
|
@@ -100,36 +128,125 @@ You must analyze the user's feedback and classify it into one of the following i
|
|
|
100
128
|
.describe(
|
|
101
129
|
"The primary type of user intention: one of addDiagram, deleteDiagram, updateDiagram, updateDocument",
|
|
102
130
|
),
|
|
131
|
+
generationMode: z
|
|
132
|
+
.enum(["image-to-image", "text-only", "add-new", "remove-image"])
|
|
133
|
+
.optional()
|
|
134
|
+
.describe(
|
|
135
|
+
"Generation mode for diagram operations. Only relevant for addDiagram/updateDiagram. 'image-to-image' for modifying existing, 'text-only' for regenerating from text, 'add-new' for creating new.",
|
|
136
|
+
),
|
|
137
|
+
changes: z
|
|
138
|
+
.array(z.string())
|
|
139
|
+
.optional()
|
|
140
|
+
.describe(
|
|
141
|
+
"Specific changes mentioned in feedback (e.g., ['add node: database', 'modify style: modern']). Only relevant for updateDiagram.",
|
|
142
|
+
),
|
|
103
143
|
}),
|
|
104
144
|
});
|
|
105
145
|
|
|
106
|
-
const
|
|
146
|
+
const llmInput = {
|
|
107
147
|
feedback: feedback.trim(),
|
|
108
|
-
|
|
148
|
+
diagramInfo,
|
|
149
|
+
};
|
|
150
|
+
const llmResult = await options.context.invoke(analyzeUpdateFeedbackIntentAgent, llmInput);
|
|
151
|
+
|
|
152
|
+
// Handle case where LLM returns null, undefined, or invalid result
|
|
153
|
+
if (!llmResult || typeof llmResult !== "object") {
|
|
154
|
+
throw new Error(`LLM returned invalid result: ${JSON.stringify(llmResult)}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let { intentType, generationMode, changes } = llmResult;
|
|
158
|
+
|
|
159
|
+
// If LLM returned null/undefined intentType, try to infer from feedback content
|
|
160
|
+
if (!intentType) {
|
|
161
|
+
const feedbackLower = feedback.toLowerCase();
|
|
162
|
+
const hasDiagramTerms =
|
|
163
|
+
feedbackLower.includes("图") ||
|
|
164
|
+
feedbackLower.includes("diagram") ||
|
|
165
|
+
feedbackLower.includes("chart") ||
|
|
166
|
+
feedbackLower.includes("节点") ||
|
|
167
|
+
feedbackLower.includes("node");
|
|
168
|
+
|
|
169
|
+
if (hasDiagramTerms && diagramInfo) {
|
|
170
|
+
intentType = "updateDiagram";
|
|
171
|
+
} else if (hasDiagramTerms && !diagramInfo) {
|
|
172
|
+
intentType = "addDiagram";
|
|
173
|
+
} else {
|
|
174
|
+
intentType = "updateDocument";
|
|
175
|
+
}
|
|
176
|
+
}
|
|
109
177
|
|
|
110
178
|
// If --diagram flag is set and user didn't explicitly request delete/add,
|
|
111
179
|
// default to updateDiagram (for backward compatibility)
|
|
112
|
-
// But if user explicitly requested delete/add, respect that intent
|
|
113
180
|
if (
|
|
114
181
|
shouldUpdateDiagrams &&
|
|
115
182
|
intentType &&
|
|
116
183
|
!["deleteDiagram", "addDiagram"].includes(intentType)
|
|
117
184
|
) {
|
|
118
|
-
|
|
185
|
+
intentType = "updateDiagram";
|
|
119
186
|
}
|
|
120
187
|
|
|
121
|
-
|
|
188
|
+
// Set default generationMode based on intentType and diagramInfo if not provided by LLM
|
|
189
|
+
if (!generationMode) {
|
|
190
|
+
if (intentType === "deleteDiagram") {
|
|
191
|
+
generationMode = "remove-image";
|
|
192
|
+
} else if (intentType === "addDiagram") {
|
|
193
|
+
generationMode = "add-new";
|
|
194
|
+
} else if (intentType === "updateDiagram") {
|
|
195
|
+
generationMode = diagramInfo ? "image-to-image" : "add-new";
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Ensure we always have a valid intentType at this point
|
|
200
|
+
if (!intentType) {
|
|
201
|
+
intentType = "updateDocument";
|
|
202
|
+
generationMode = generationMode || null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
intentType,
|
|
207
|
+
diagramInfo,
|
|
208
|
+
generationMode,
|
|
209
|
+
changes: changes || [],
|
|
210
|
+
};
|
|
122
211
|
} catch (error) {
|
|
212
|
+
console.warn(`[analyzeFeedbackIntent] Failed to analyze feedback intent: ${error.message}`);
|
|
213
|
+
|
|
123
214
|
// If analysis fails and --diagram flag is set, default to updateDiagram
|
|
124
|
-
// Otherwise return null to fall back to default document update flow
|
|
125
215
|
if (shouldUpdateDiagrams) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
216
|
+
return {
|
|
217
|
+
intentType: "updateDiagram",
|
|
218
|
+
diagramInfo,
|
|
219
|
+
generationMode: diagramInfo ? "image-to-image" : "add-new",
|
|
220
|
+
changes: [],
|
|
221
|
+
};
|
|
130
222
|
}
|
|
131
|
-
|
|
132
|
-
|
|
223
|
+
|
|
224
|
+
// Fallback: try to infer intent from feedback content
|
|
225
|
+
const feedbackLower = feedback.toLowerCase();
|
|
226
|
+
const hasDiagramTerms =
|
|
227
|
+
feedbackLower.includes("图") ||
|
|
228
|
+
feedbackLower.includes("diagram") ||
|
|
229
|
+
feedbackLower.includes("chart") ||
|
|
230
|
+
feedbackLower.includes("节点") ||
|
|
231
|
+
feedbackLower.includes("node");
|
|
232
|
+
|
|
233
|
+
let fallbackIntentType = "updateDocument";
|
|
234
|
+
let fallbackGenerationMode = null;
|
|
235
|
+
|
|
236
|
+
if (hasDiagramTerms && diagramInfo) {
|
|
237
|
+
fallbackIntentType = "updateDiagram";
|
|
238
|
+
fallbackGenerationMode = "image-to-image";
|
|
239
|
+
} else if (hasDiagramTerms && !diagramInfo) {
|
|
240
|
+
fallbackIntentType = "addDiagram";
|
|
241
|
+
fallbackGenerationMode = "add-new";
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
intentType: fallbackIntentType,
|
|
246
|
+
diagramInfo,
|
|
247
|
+
generationMode: fallbackGenerationMode,
|
|
248
|
+
changes: [],
|
|
249
|
+
};
|
|
133
250
|
}
|
|
134
251
|
}
|
|
135
252
|
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import { buildAllowedLinksFromStructure } from "../../utils/docs-finder-utils.mjs";
|
|
2
2
|
import { checkMarkdown } from "../../utils/markdown-checker.mjs";
|
|
3
3
|
|
|
4
|
-
export default async function checkDetailResult({
|
|
4
|
+
export default async function checkDetailResult({
|
|
5
|
+
documentStructure,
|
|
6
|
+
reviewContent,
|
|
7
|
+
docsDir,
|
|
8
|
+
isApproved: preApproved,
|
|
9
|
+
}) {
|
|
10
|
+
// If already approved (e.g., --diagram mode), skip validation
|
|
11
|
+
if (preApproved === true) {
|
|
12
|
+
return {
|
|
13
|
+
isApproved: true,
|
|
14
|
+
detailFeedback: "",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
5
18
|
if (!reviewContent || reviewContent.trim() === "") {
|
|
6
19
|
return {
|
|
7
20
|
isApproved: false,
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
} from "../../utils/docs-finder-utils.mjs";
|
|
10
10
|
import {
|
|
11
11
|
hasDiagramContent,
|
|
12
|
-
hasBananaImages,
|
|
13
12
|
getDiagramTypeLabels,
|
|
14
13
|
formatDiagramTypeSuffix,
|
|
15
14
|
} from "../../utils/check-document-has-diagram.mjs";
|
|
@@ -37,7 +36,6 @@ export default async function chooseDocs(
|
|
|
37
36
|
action,
|
|
38
37
|
shouldUpdateDiagrams = false,
|
|
39
38
|
shouldAutoSelectDiagrams = false,
|
|
40
|
-
shouldSyncImages = false,
|
|
41
39
|
},
|
|
42
40
|
options,
|
|
43
41
|
) {
|
|
@@ -59,41 +57,8 @@ export default async function chooseDocs(
|
|
|
59
57
|
);
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
// If --diagram-sync flag is set, filter documents by banana images only
|
|
63
|
-
if (shouldSyncImages) {
|
|
64
|
-
debug("🔄 Filtering documents with banana images...");
|
|
65
|
-
|
|
66
|
-
// Read content for all files and filter by banana images only
|
|
67
|
-
const filesWithImages = [];
|
|
68
|
-
for (const fileName of mainLanguageFiles) {
|
|
69
|
-
const content = await readFileContent(docsDir, fileName);
|
|
70
|
-
if (content && hasBananaImages(content)) {
|
|
71
|
-
filesWithImages.push(fileName);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (filesWithImages.length === 0) {
|
|
76
|
-
debug("ℹ️ No documents found with banana images (DIAGRAM_IMAGE_START markers).");
|
|
77
|
-
return {
|
|
78
|
-
selectedDocs: [],
|
|
79
|
-
feedback: "",
|
|
80
|
-
selectedPaths: [],
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
debug(`✅ Found ${filesWithImages.length} document(s) with banana images.`);
|
|
85
|
-
debug("📋 Auto-selecting all documents with banana images...");
|
|
86
|
-
// Show diagram types for each document
|
|
87
|
-
for (const file of filesWithImages) {
|
|
88
|
-
const content = await readFileContent(docsDir, file);
|
|
89
|
-
const diagramLabels = content ? getDiagramTypeLabels(content) : [];
|
|
90
|
-
const diagramSuffix = formatDiagramTypeSuffix(diagramLabels);
|
|
91
|
-
debug(` • ${file}${diagramSuffix}`);
|
|
92
|
-
}
|
|
93
|
-
selectedFiles = filesWithImages;
|
|
94
|
-
}
|
|
95
60
|
// If --diagram flag is set, filter documents by diagram content
|
|
96
|
-
|
|
61
|
+
if (shouldUpdateDiagrams) {
|
|
97
62
|
debug("🔄 Filtering documents with diagram content...");
|
|
98
63
|
|
|
99
64
|
// Read content for all files and filter by diagram content
|
|
@@ -257,14 +222,9 @@ export default async function chooseDocs(
|
|
|
257
222
|
}
|
|
258
223
|
|
|
259
224
|
// Prompt for feedback if not provided
|
|
260
|
-
// Skip feedback prompt if --diagram
|
|
225
|
+
// Skip feedback prompt if --diagram or --diagram-all flag is set
|
|
261
226
|
let userFeedback = feedback;
|
|
262
|
-
if (
|
|
263
|
-
!userFeedback &&
|
|
264
|
-
(requiredFeedback || foundItems?.length > 1) &&
|
|
265
|
-
!shouldUpdateDiagrams &&
|
|
266
|
-
!shouldSyncImages
|
|
267
|
-
) {
|
|
227
|
+
if (!userFeedback && (requiredFeedback || foundItems?.length > 1) && !shouldUpdateDiagrams) {
|
|
268
228
|
const feedbackMessage = getFeedbackMessage(docAction);
|
|
269
229
|
|
|
270
230
|
userFeedback = await options.prompts.input({
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import { saveDocTranslation as _saveDocTranslation } from "../../utils/utils.mjs";
|
|
2
|
-
import { readFileContent } from "../../utils/docs-finder-utils.mjs";
|
|
3
|
-
import { getFileName } from "../../utils/utils.mjs";
|
|
4
|
-
import { debug } from "../../utils/debug.mjs";
|
|
5
|
-
import { syncDiagramToTranslations } from "../../utils/sync-diagram-to-translations.mjs";
|
|
6
2
|
|
|
7
3
|
export default async function saveDocTranslation({
|
|
8
4
|
path,
|
|
@@ -11,7 +7,6 @@ export default async function saveDocTranslation({
|
|
|
11
7
|
language,
|
|
12
8
|
labels,
|
|
13
9
|
isShowMessage = false,
|
|
14
|
-
locale,
|
|
15
10
|
}) {
|
|
16
11
|
await _saveDocTranslation({
|
|
17
12
|
path,
|
|
@@ -21,34 +16,8 @@ export default async function saveDocTranslation({
|
|
|
21
16
|
labels,
|
|
22
17
|
});
|
|
23
18
|
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
if (path && docsDir && locale) {
|
|
27
|
-
try {
|
|
28
|
-
// Read main document content (it should already be saved)
|
|
29
|
-
const mainFileName = getFileName(path, locale);
|
|
30
|
-
const mainContent = await readFileContent(docsDir, mainFileName);
|
|
31
|
-
|
|
32
|
-
if (mainContent) {
|
|
33
|
-
const syncResult = await syncDiagramToTranslations(
|
|
34
|
-
mainContent,
|
|
35
|
-
path,
|
|
36
|
-
docsDir,
|
|
37
|
-
locale,
|
|
38
|
-
"sync",
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
if (syncResult.updated > 0) {
|
|
42
|
-
debug(
|
|
43
|
-
`✅ Synced diagram images to ${syncResult.updated} translation file(s) after translation`,
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
} catch (error) {
|
|
48
|
-
// Don't fail the translation if sync fails
|
|
49
|
-
debug(`⚠️ Failed to sync diagram images after translation: ${error.message}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
19
|
+
// Note: Diagram image translation is now handled by translate-diagram-images.mjs
|
|
20
|
+
// in the translate workflow, which compares timestamps and only translates when needed
|
|
52
21
|
|
|
53
22
|
if (isShowMessage) {
|
|
54
23
|
const message = `✅ Translation completed successfully.`;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { shutdownMermaidWorkerPool } from "../../utils/mermaid-worker-pool.mjs";
|
|
2
2
|
import { saveDoc as _saveDoc } from "../../utils/utils.mjs";
|
|
3
|
-
import { debug } from "../../utils/debug.mjs";
|
|
4
3
|
|
|
5
4
|
export default async function saveDoc({
|
|
6
5
|
path,
|
|
@@ -22,42 +21,9 @@ export default async function saveDoc({
|
|
|
22
21
|
locale,
|
|
23
22
|
});
|
|
24
23
|
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
docsDir &&
|
|
29
|
-
path &&
|
|
30
|
-
intentType &&
|
|
31
|
-
["addDiagram", "updateDiagram", "deleteDiagram"].includes(intentType)
|
|
32
|
-
) {
|
|
33
|
-
try {
|
|
34
|
-
const { syncDiagramToTranslations } = await import(
|
|
35
|
-
"../../utils/sync-diagram-to-translations.mjs"
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
// Determine operation type for sync
|
|
39
|
-
// deleteDiagram -> "delete" (process even if 0 diagrams)
|
|
40
|
-
// addDiagram/updateDiagram -> "update" (skip if 0 diagrams)
|
|
41
|
-
const operationType = intentType === "deleteDiagram" ? "delete" : "update";
|
|
42
|
-
|
|
43
|
-
const syncResult = await syncDiagramToTranslations(
|
|
44
|
-
content,
|
|
45
|
-
path,
|
|
46
|
-
docsDir,
|
|
47
|
-
locale || "en",
|
|
48
|
-
operationType,
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
if (syncResult.updated > 0) {
|
|
52
|
-
debug(
|
|
53
|
-
`✅ Synced diagram changes to ${syncResult.updated} translation file(s) for ${intentType}`,
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
} catch (error) {
|
|
57
|
-
// Don't fail the operation if sync fails
|
|
58
|
-
debug(`⚠️ Failed to sync diagram to translations: ${error.message}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
24
|
+
// Note: Diagram image translation is handled separately during translate operation
|
|
25
|
+
// In update/add operations, we only update the main document with timestamp
|
|
26
|
+
// Translation documents will be updated during translate operation when timestamps differ
|
|
61
27
|
|
|
62
28
|
if (isShowMessage) {
|
|
63
29
|
// Shutdown mermaid worker pool to ensure clean exit
|
package/aigne.yaml
CHANGED
|
@@ -80,6 +80,7 @@ agents:
|
|
|
80
80
|
- ./agents/utils/feedback-refiner.yaml
|
|
81
81
|
- ./agents/utils/analyze-structure-feedback-intent.yaml
|
|
82
82
|
- ./agents/utils/analyze-document-feedback-intent.yaml
|
|
83
|
+
- ./agents/utils/analyze-feedback-intent.mjs
|
|
83
84
|
|
|
84
85
|
- ./agents/utils/document-title-streamline.yaml
|
|
85
86
|
- ./agents/utils/streamline-document-titles-if-needed.mjs
|
|
@@ -112,8 +113,7 @@ agents:
|
|
|
112
113
|
- ./agents/update/generate-diagram.yaml
|
|
113
114
|
- ./agents/update/check-generate-diagram.mjs
|
|
114
115
|
- ./agents/update/pre-check-generate-diagram.yaml
|
|
115
|
-
- ./agents/
|
|
116
|
-
- ./agents/update/sync-images-and-exit.mjs
|
|
116
|
+
- ./agents/localize/translate-diagram.yaml
|
|
117
117
|
cli:
|
|
118
118
|
chat: ./agents/chat/index.mjs
|
|
119
119
|
agents:
|
package/package.json
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
1
|
+
{% if useImageToImage and existingImage %}
|
|
2
|
+
# Image-to-Image Generation Mode
|
|
3
|
+
|
|
4
|
+
Your task is to **update an existing diagram** based on the current document content and user feedback.
|
|
5
|
+
|
|
6
|
+
**CRITICAL INSTRUCTIONS:**
|
|
7
|
+
1. **Use the existing image as the primary reference** - maintain its overall structure, layout, and visual style
|
|
8
|
+
2. **Analyze the document content** to understand what changes are needed
|
|
9
|
+
3. **Apply user feedback** to modify the diagram appropriately
|
|
10
|
+
4. **Maintain visual consistency** - keep the same style, color scheme, and general layout unless explicitly requested to change
|
|
11
|
+
5. **Make maximum changes** where needed - update content, add/remove elements, adjust relationships based on the document
|
|
12
|
+
6. **Preserve what works** - keep elements that are still accurate and relevant
|
|
13
|
+
|
|
14
|
+
**Task Parameters:**
|
|
15
|
+
- **Diagram Type:** {{ diagramType }}
|
|
16
|
+
- **Visual Style:** {{ diagramStyle }} (maintain consistency with existing image unless feedback requests change)
|
|
17
|
+
- **Aspect Ratio:** {{ aspectRatio }}
|
|
18
|
+
- **Language:** English
|
|
19
|
+
|
|
20
|
+
**Existing Diagram:**
|
|
21
|
+
[The existing diagram image is provided as input to the model]
|
|
22
|
+
|
|
23
|
+
**Document Content:**
|
|
24
|
+
{% if documentSummary %}
|
|
25
|
+
{{ documentSummary }}
|
|
26
|
+
{% else %}
|
|
27
|
+
{{ documentContent }}
|
|
28
|
+
{% endif %}
|
|
29
|
+
|
|
30
|
+
**User Feedback:**
|
|
31
|
+
{{ feedback if feedback else "Update the diagram to match the current document content." }}
|
|
32
|
+
|
|
33
|
+
**Your responsibilities:**
|
|
34
|
+
1. Analyze the existing diagram structure, style, and layout
|
|
35
|
+
2. Review the document content to identify what needs to be updated
|
|
36
|
+
3. Apply user feedback to modify the diagram
|
|
37
|
+
4. Maintain visual consistency with the original design where appropriate
|
|
38
|
+
5. Update the diagram to accurately reflect the current document content
|
|
39
|
+
6. Make necessary changes while preserving the overall visual style and structure
|
|
40
|
+
|
|
41
|
+
**Style Consistency:**
|
|
42
|
+
- Keep the same visual style as the existing image unless user feedback explicitly requests a style change
|
|
43
|
+
- Maintain color scheme, node shapes, and layout patterns
|
|
44
|
+
- Preserve the overall aesthetic and design language
|
|
45
|
+
|
|
46
|
+
{% else %}
|
|
47
|
+
# Standard Text-to-Image Generation Mode
|
|
48
|
+
|
|
1
49
|
Your task is to create a professional diagram image based on the document content below.
|
|
2
50
|
|
|
3
51
|
Please follow **all global rules, styles, aspect ratio logic, and diagram-type rules** defined in the system prompt.
|
|
@@ -30,3 +78,4 @@ Now analyze the following document content to understand what should be drawn:
|
|
|
30
78
|
{% endif %}
|
|
31
79
|
|
|
32
80
|
(Use this content to determine node structure, relationships, and flow.)
|
|
81
|
+
{% endif %}
|
package/utils/d2-utils.mjs
CHANGED
|
@@ -12,15 +12,22 @@ export const DIAGRAM_PLACEHOLDER = "DIAGRAM_PLACEHOLDER";
|
|
|
12
12
|
|
|
13
13
|
// Diagram image regex patterns for reuse across the codebase
|
|
14
14
|
// Pattern 1: Match only the start marker (for checking existence)
|
|
15
|
-
export const diagramImageStartRegex =
|
|
15
|
+
export const diagramImageStartRegex =
|
|
16
|
+
/<!--\s*DIAGRAM_IMAGE_START:([A-Za-z0-9_-]+):(\d+:\d+)(:\d+)?\s*-->/g;
|
|
16
17
|
|
|
17
18
|
// Pattern 2: Match full diagram image block without capturing image path (for finding/replacing)
|
|
19
|
+
// Supports both old format (without aspectRatio) and new format (with aspectRatio)
|
|
18
20
|
export const diagramImageBlockRegex =
|
|
19
|
-
/<!--\s*DIAGRAM_IMAGE_START:[
|
|
21
|
+
/<!--\s*DIAGRAM_IMAGE_START:([A-Za-z0-9_-]+)(?::(\d+:\d+))?(:\d+)?\s*-->\s*[\s\S]*?<!--\s*DIAGRAM_IMAGE_END\s*-->/g;
|
|
20
22
|
|
|
21
23
|
// Pattern 3: Match full diagram image block with image path capture (for extracting paths)
|
|
24
|
+
// Compatible with old format (without timestamp): <!-- DIAGRAM_IMAGE_START:type:aspectRatio -->
|
|
22
25
|
export const diagramImageWithPathRegex =
|
|
23
|
-
/<!--\s*DIAGRAM_IMAGE_START:[
|
|
26
|
+
/<!--\s*DIAGRAM_IMAGE_START:([A-Za-z0-9_-]+):(\d+:\d+)(:\d+)?\s*-->\s*!\[[^\]]*\]\(([^)]+)\)\s*<!--\s*DIAGRAM_IMAGE_END\s*-->/g;
|
|
27
|
+
|
|
28
|
+
// Pattern 4: Match full diagram image block with all details (type, aspectRatio, timestamp, path, altText)
|
|
29
|
+
export const diagramImageFullRegex =
|
|
30
|
+
/<!--\s*DIAGRAM_IMAGE_START:([A-Za-z0-9_-]+):(\d+:\d+)(:\d+)?\s*-->\s*!\[([^\]]*)\]\(([^)]+)\)\s*<!--\s*DIAGRAM_IMAGE_END\s*-->/g;
|
|
24
31
|
|
|
25
32
|
export async function ensureTmpDir() {
|
|
26
33
|
const tmpDir = path.join(DOC_SMITH_DIR, TMP_DIR);
|
|
@@ -2,6 +2,7 @@ import { unlink } from "node:fs/promises";
|
|
|
2
2
|
import { join, dirname, normalize } from "node:path";
|
|
3
3
|
import fs from "fs-extra";
|
|
4
4
|
import { debug } from "./debug.mjs";
|
|
5
|
+
import { diagramImageWithPathRegex } from "./d2-utils.mjs";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Extract image file paths from markdown content
|
|
@@ -19,11 +20,10 @@ export async function extractDiagramImagePaths(content, path, docsDir) {
|
|
|
19
20
|
const imagePaths = [];
|
|
20
21
|
|
|
21
22
|
// Pattern to match: <!-- DIAGRAM_IMAGE_START:... --><!-- DIAGRAM_IMAGE_END -->
|
|
22
|
-
const
|
|
23
|
-
const matches = Array.from(content.matchAll(diagramImageWithPathRegex));
|
|
23
|
+
const matches = Array.from((content || "").matchAll(diagramImageWithPathRegex));
|
|
24
24
|
|
|
25
25
|
for (const match of matches) {
|
|
26
|
-
const imagePath = match[
|
|
26
|
+
const imagePath = match[4] || ""; // Path is in capture group 4 (groups: 1=type, 2=aspectRatio, 3=timestamp, 4=path)
|
|
27
27
|
|
|
28
28
|
// Resolve absolute path
|
|
29
29
|
// If imagePath is relative, resolve from document location
|