@alpaca-editor/core 1.0.4027 → 1.0.4031
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/dist/components/ActionButton.js +2 -2
- package/dist/components/ActionButton.js.map +1 -1
- package/dist/components/SimpleLanguageSelector.js +3 -1
- package/dist/components/SimpleLanguageSelector.js.map +1 -1
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/config/config.js +1 -1
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +1 -1
- package/dist/editor/ContextMenu.js +0 -1
- package/dist/editor/ContextMenu.js.map +1 -1
- package/dist/editor/Editor.js +9 -3
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/FieldListField.js +16 -24
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/ImageEditButton.js +1 -1
- package/dist/editor/ImageEditButton.js.map +1 -1
- package/dist/editor/ImageEditor.js +1 -1
- package/dist/editor/ImageEditor.js.map +1 -1
- package/dist/editor/MainLayout.js +2 -2
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/Terminal.js +1 -1
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/Titlebar.js +0 -1
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/ai/AgentCostDisplay.d.ts +26 -0
- package/dist/editor/ai/AgentCostDisplay.js +65 -0
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -0
- package/dist/editor/ai/Agents.js +59 -15
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiPromptPopover.d.ts +7 -0
- package/dist/editor/ai/AiPromptPopover.js +111 -0
- package/dist/editor/ai/AiPromptPopover.js.map +1 -0
- package/dist/editor/ai/AiResponseMessage.d.ts +1 -0
- package/dist/editor/ai/AiResponseMessage.js +101 -23
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/AiTerminal.d.ts +15 -1
- package/dist/editor/ai/AiTerminal.js +379 -48
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/ai/editorAiContext.d.ts +0 -1
- package/dist/editor/ai/editorAiContext.js +0 -2
- package/dist/editor/ai/editorAiContext.js.map +1 -1
- package/dist/editor/client/EditorClient.d.ts +3 -2
- package/dist/editor/client/EditorClient.js +326 -68
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +6 -4
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/fieldModificationStore.d.ts +19 -0
- package/dist/editor/client/fieldModificationStore.js +125 -0
- package/dist/editor/client/fieldModificationStore.js.map +1 -0
- package/dist/editor/client/itemsRepository.d.ts +1 -1
- package/dist/editor/client/itemsRepository.js +38 -28
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/operations.d.ts +1 -0
- package/dist/editor/client/operations.js +39 -31
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +5 -3
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/component-designer/aiContext.js +0 -2
- package/dist/editor/component-designer/aiContext.js.map +1 -1
- package/dist/editor/field-types/DropLinkEditor.js +1 -1
- package/dist/editor/field-types/DropLinkEditor.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +5 -7
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js +5 -7
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +5 -7
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/hooks/useEditorSettings.d.ts +17 -0
- package/dist/editor/hooks/useEditorSettings.js +61 -0
- package/dist/editor/hooks/useEditorSettings.js.map +1 -0
- package/dist/editor/menubar/ItemActionsMenu.js +2 -2
- package/dist/editor/menubar/ItemActionsMenu.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +1 -1
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/InsertControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/InsertControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/ViewportControls.js +1 -1
- package/dist/editor/menubar/toolbar-sections/ViewportControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldEditedIndicators.js +4 -3
- package/dist/editor/page-editor-chrome/FieldEditedIndicators.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +9 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +0 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +1 -1
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +9 -8
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +7 -1
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/pageViewContext.js +40 -6
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
- package/dist/editor/reviews/Comment.js +7 -6
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +84 -12
- package/dist/editor/services/agentService.js +256 -15
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +17 -3
- package/dist/editor/services/aiService.js +5 -3
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/contextService.js +0 -1
- package/dist/editor/services/contextService.js.map +1 -1
- package/dist/editor/services/systemService.d.ts +2 -1
- package/dist/editor/services/systemService.js +3 -0
- package/dist/editor/services/systemService.js.map +1 -1
- package/dist/editor/sidebar/ComponentPalette.js +1 -1
- package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
- package/dist/editor/sidebar/EditHistory.js +2 -2
- package/dist/editor/sidebar/EditHistory.js.map +1 -1
- package/dist/editor/sidebar/GraphQL.d.ts +1 -0
- package/dist/editor/sidebar/GraphQL.js +8 -2
- package/dist/editor/sidebar/GraphQL.js.map +1 -1
- package/dist/editor/sidebar/MainContentTree.js +1 -1
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/sidebar/SEOInfo.js +1 -1
- package/dist/editor/sidebar/SEOInfo.js.map +1 -1
- package/dist/editor/sidebar/ViewSelector.d.ts +4 -1
- package/dist/editor/sidebar/ViewSelector.js +64 -48
- package/dist/editor/sidebar/ViewSelector.js.map +1 -1
- package/dist/editor/ui/PerfectTree.js +2 -11
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/ui/SimpleIconButton.d.ts +2 -0
- package/dist/editor/ui/SimpleIconButton.js +8 -4
- package/dist/editor/ui/SimpleIconButton.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/page-wizard/steps/CollectStep.js +1 -1
- package/dist/page-wizard/steps/CollectStep.js.map +1 -1
- package/dist/page-wizard/steps/StructureStep.js +1 -1
- package/dist/page-wizard/steps/StructureStep.js.map +1 -1
- package/dist/page-wizard/steps/TranslateStep.js +233 -18
- package/dist/page-wizard/steps/TranslateStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/RecentPages.js +1 -13
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/splash-screen/SplashScreen.js +1 -1
- package/dist/splash-screen/SplashScreen.js.map +1 -1
- package/dist/styles.css +88 -3
- package/dist/types.d.ts +6 -0
- package/package.json +2 -2
- package/src/components/ActionButton.tsx +3 -2
- package/src/components/SimpleLanguageSelector.tsx +6 -1
- package/src/config/config.tsx +1 -1
- package/src/config/types.ts +1 -1
- package/src/editor/ContextMenu.tsx +0 -3
- package/src/editor/Editor.tsx +11 -3
- package/src/editor/FieldListField.tsx +22 -31
- package/src/editor/ImageEditButton.tsx +1 -0
- package/src/editor/ImageEditor.tsx +1 -0
- package/src/editor/MainLayout.tsx +2 -2
- package/src/editor/Terminal.tsx +1 -1
- package/src/editor/Titlebar.tsx +0 -2
- package/src/editor/ai/AgentCostDisplay.tsx +237 -0
- package/src/editor/ai/Agents.tsx +69 -20
- package/src/editor/ai/AiPromptPopover.tsx +209 -0
- package/src/editor/ai/AiResponseMessage.tsx +201 -60
- package/src/editor/ai/AiTerminal.tsx +502 -71
- package/src/editor/ai/editorAiContext.ts +0 -3
- package/src/editor/client/EditorClient.tsx +409 -117
- package/src/editor/client/editContext.ts +7 -5
- package/src/editor/client/fieldModificationStore.ts +196 -0
- package/src/editor/client/itemsRepository.ts +41 -31
- package/src/editor/client/operations.ts +95 -76
- package/src/editor/commands/componentCommands.tsx +9 -3
- package/src/editor/commands/itemCommands.tsx +0 -1
- package/src/editor/component-designer/aiContext.ts +0 -2
- package/src/editor/field-types/DropLinkEditor.tsx +1 -1
- package/src/editor/field-types/MultiLineText.tsx +9 -9
- package/src/editor/field-types/RichTextEditorComponent.tsx +8 -8
- package/src/editor/field-types/SingleLineText.tsx +9 -9
- package/src/editor/hooks/useEditorSettings.ts +68 -0
- package/src/editor/menubar/ItemActionsMenu.tsx +3 -2
- package/src/editor/menubar/PageSelector.tsx +1 -1
- package/src/editor/menubar/toolbar-sections/EditControls.tsx +1 -0
- package/src/editor/menubar/toolbar-sections/InsertControls.tsx +1 -0
- package/src/editor/menubar/toolbar-sections/UtilityControls.tsx +2 -0
- package/src/editor/menubar/toolbar-sections/ViewportControls.tsx +2 -0
- package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +4 -3
- package/src/editor/page-editor-chrome/FrameMenu.tsx +10 -1
- package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +0 -1
- package/src/editor/page-viewer/EditorForm.tsx +1 -0
- package/src/editor/page-viewer/PageViewer.tsx +9 -8
- package/src/editor/page-viewer/PageViewerFrame.tsx +7 -1
- package/src/editor/page-viewer/pageViewContext.ts +40 -5
- package/src/editor/reviews/Comment.tsx +7 -7
- package/src/editor/services/agentService.ts +405 -31
- package/src/editor/services/aiService.ts +22 -5
- package/src/editor/services/contextService.ts +0 -1
- package/src/editor/services/systemService.ts +7 -1
- package/src/editor/sidebar/ComponentPalette.tsx +4 -1
- package/src/editor/sidebar/EditHistory.tsx +2 -0
- package/src/editor/sidebar/GraphQL.tsx +19 -7
- package/src/editor/sidebar/MainContentTree.tsx +1 -1
- package/src/editor/sidebar/SEOInfo.tsx +1 -1
- package/src/editor/sidebar/ViewSelector.tsx +80 -64
- package/src/editor/ui/PerfectTree.tsx +2 -18
- package/src/editor/ui/SimpleIconButton.tsx +56 -38
- package/src/index.ts +4 -2
- package/src/page-wizard/steps/CollectStep.tsx +0 -2
- package/src/page-wizard/steps/StructureStep.tsx +3 -0
- package/src/page-wizard/steps/TranslateStep.tsx +473 -62
- package/src/revision.ts +2 -2
- package/src/splash-screen/RecentPages.tsx +0 -14
- package/src/splash-screen/SplashScreen.tsx +3 -2
- package/src/types.ts +7 -0
- package/dist/editor/ai/AiPopup.d.ts +0 -10
- package/dist/editor/ai/AiPopup.js +0 -23
- package/dist/editor/ai/AiPopup.js.map +0 -1
- package/dist/editor/ai/AiToolCall.d.ts +0 -5
- package/dist/editor/ai/AiToolCall.js +0 -28
- package/dist/editor/ai/AiToolCall.js.map +0 -1
- package/src/editor/ai/AiPopup.tsx +0 -59
- package/src/editor/ai/AiToolCall.tsx +0 -71
|
@@ -33,6 +33,8 @@ interface TranslatableFieldsResponse {
|
|
|
33
33
|
interface TranslatedField {
|
|
34
34
|
fieldId: string;
|
|
35
35
|
translatedValue: string;
|
|
36
|
+
isComplete?: boolean;
|
|
37
|
+
missingSuggestions?: string;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
interface TranslatedFieldsResponse {
|
|
@@ -56,6 +58,10 @@ export function TranslateStep({
|
|
|
56
58
|
const [isLoading, setIsLoading] = useState(false);
|
|
57
59
|
const [isTranslating, setIsTranslating] = useState(false);
|
|
58
60
|
const [isDetectingLanguage, setIsDetectingLanguage] = useState(false);
|
|
61
|
+
const [translatingFields, setTranslatingFields] = useState<Set<string>>(
|
|
62
|
+
new Set(),
|
|
63
|
+
);
|
|
64
|
+
const [checkingFields, setCheckingFields] = useState<Set<string>>(new Set());
|
|
59
65
|
const [targetLanguage, setTargetLanguage] = useState<Language | null>(
|
|
60
66
|
data.targetLanguage || null,
|
|
61
67
|
);
|
|
@@ -116,7 +122,7 @@ export function TranslateStep({
|
|
|
116
122
|
}, [currentItem, editContext]);
|
|
117
123
|
|
|
118
124
|
const fetchComponentNames = useCallback(
|
|
119
|
-
async (itemIds: string[]) => {
|
|
125
|
+
async (itemIds: string[], sourceLanguageParam: string) => {
|
|
120
126
|
if (!editContext || itemIds.length === 0) return;
|
|
121
127
|
|
|
122
128
|
const names: Record<string, string> = {};
|
|
@@ -125,7 +131,7 @@ export function TranslateStep({
|
|
|
125
131
|
try {
|
|
126
132
|
const item = await editContext.itemsRepository.getItem({
|
|
127
133
|
id: itemId,
|
|
128
|
-
language:
|
|
134
|
+
language: sourceLanguageParam,
|
|
129
135
|
version: 0,
|
|
130
136
|
});
|
|
131
137
|
|
|
@@ -142,69 +148,104 @@ export function TranslateStep({
|
|
|
142
148
|
|
|
143
149
|
setComponentNames(names);
|
|
144
150
|
},
|
|
145
|
-
[editContext
|
|
151
|
+
[editContext],
|
|
146
152
|
);
|
|
147
153
|
|
|
148
|
-
const fetchTranslatableFields = useCallback(
|
|
149
|
-
|
|
154
|
+
const fetchTranslatableFields = useCallback(
|
|
155
|
+
async (sourceLanguageParam: string) => {
|
|
156
|
+
if (!currentItem || !editContext) return;
|
|
150
157
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
setIsLoading(true);
|
|
159
|
+
try {
|
|
160
|
+
const response = await fetch(
|
|
161
|
+
"/alpaca/editor/page-wizard/getTranslatableFields",
|
|
162
|
+
{
|
|
163
|
+
method: "POST",
|
|
164
|
+
headers: {
|
|
165
|
+
"Content-Type": "application/json",
|
|
166
|
+
},
|
|
167
|
+
body: JSON.stringify({
|
|
168
|
+
id: currentItem.id,
|
|
169
|
+
language: sourceLanguageParam,
|
|
170
|
+
version: currentItem.version,
|
|
171
|
+
}),
|
|
159
172
|
},
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (response.ok) {
|
|
176
|
+
const fieldsData: TranslatableFieldsResponse = await response.json();
|
|
177
|
+
setTranslatableFields(fieldsData);
|
|
178
|
+
|
|
179
|
+
// Initialize translated fields structure
|
|
180
|
+
const initialTranslatedFields: TranslatedFieldsResponse = {};
|
|
181
|
+
Object.keys(fieldsData).forEach((itemId) => {
|
|
182
|
+
if (fieldsData[itemId]) {
|
|
183
|
+
initialTranslatedFields[itemId] = fieldsData[itemId].map(
|
|
184
|
+
(field) => ({
|
|
185
|
+
fieldId: field.fieldId,
|
|
186
|
+
translatedValue: "",
|
|
187
|
+
isComplete: undefined,
|
|
188
|
+
missingSuggestions: undefined,
|
|
189
|
+
}),
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
setTranslatedFields(initialTranslatedFields);
|
|
167
194
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
// Initialize translated fields structure
|
|
173
|
-
const initialTranslatedFields: TranslatedFieldsResponse = {};
|
|
174
|
-
Object.keys(fieldsData).forEach((itemId) => {
|
|
175
|
-
if (fieldsData[itemId]) {
|
|
176
|
-
initialTranslatedFields[itemId] = fieldsData[itemId].map(
|
|
177
|
-
(field) => ({
|
|
178
|
-
fieldId: field.fieldId,
|
|
179
|
-
translatedValue: "",
|
|
180
|
-
}),
|
|
181
|
-
);
|
|
195
|
+
// Fetch component names for all item IDs
|
|
196
|
+
const itemIds = Object.keys(fieldsData);
|
|
197
|
+
if (itemIds.length > 0) {
|
|
198
|
+
fetchComponentNames(itemIds, sourceLanguageParam);
|
|
182
199
|
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
// Fetch component names for all item IDs
|
|
187
|
-
const itemIds = Object.keys(fieldsData);
|
|
188
|
-
if (itemIds.length > 0) {
|
|
189
|
-
fetchComponentNames(itemIds);
|
|
200
|
+
} else {
|
|
201
|
+
console.error("Failed to fetch translatable fields");
|
|
190
202
|
}
|
|
191
|
-
}
|
|
192
|
-
console.error("
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.error("Error fetching translatable fields:", error);
|
|
205
|
+
} finally {
|
|
206
|
+
setIsLoading(false);
|
|
193
207
|
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
208
|
+
},
|
|
209
|
+
[currentItem, editContext, fetchComponentNames],
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Track the last source language we fetched for to prevent unnecessary re-fetching
|
|
213
|
+
const [lastFetchedSourceLanguage, setLastFetchedSourceLanguage] =
|
|
214
|
+
useState<string>("");
|
|
200
215
|
|
|
201
216
|
useEffect(() => {
|
|
202
217
|
fetchAvailableLanguages();
|
|
203
218
|
}, [fetchAvailableLanguages]);
|
|
204
219
|
|
|
220
|
+
// Reset the last fetched source language when the current item changes
|
|
205
221
|
useEffect(() => {
|
|
206
|
-
|
|
207
|
-
}, [
|
|
222
|
+
setLastFetchedSourceLanguage("");
|
|
223
|
+
}, [currentItem]);
|
|
224
|
+
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
// Only fetch translatable fields if:
|
|
227
|
+
// 1. We have a source language
|
|
228
|
+
// 2. It's different from the last one we fetched for
|
|
229
|
+
// 3. We're not currently publishing (to avoid conflicts)
|
|
230
|
+
// 4. We're not currently loading or translating
|
|
231
|
+
if (
|
|
232
|
+
sourceLanguage &&
|
|
233
|
+
sourceLanguage !== lastFetchedSourceLanguage &&
|
|
234
|
+
!isPublishing &&
|
|
235
|
+
!isLoading &&
|
|
236
|
+
!isTranslating
|
|
237
|
+
) {
|
|
238
|
+
fetchTranslatableFields(sourceLanguage);
|
|
239
|
+
setLastFetchedSourceLanguage(sourceLanguage);
|
|
240
|
+
}
|
|
241
|
+
}, [
|
|
242
|
+
fetchTranslatableFields,
|
|
243
|
+
sourceLanguage,
|
|
244
|
+
lastFetchedSourceLanguage,
|
|
245
|
+
isPublishing,
|
|
246
|
+
isLoading,
|
|
247
|
+
isTranslating,
|
|
248
|
+
]);
|
|
208
249
|
|
|
209
250
|
const detectLanguage = async () => {
|
|
210
251
|
if (!editContext) {
|
|
@@ -231,7 +272,7 @@ ${documentContent.substring(0, 1000)}`;
|
|
|
231
272
|
},
|
|
232
273
|
],
|
|
233
274
|
{ editContext, createAiContext: createWizardAiContext },
|
|
234
|
-
{ model: "gpt-
|
|
275
|
+
{ model: "gpt-4.1" },
|
|
235
276
|
{ signal: abortController.signal },
|
|
236
277
|
(response: any) => {
|
|
237
278
|
try {
|
|
@@ -282,12 +323,25 @@ ${documentContent.substring(0, 1000)}`;
|
|
|
282
323
|
}
|
|
283
324
|
};
|
|
284
325
|
|
|
285
|
-
//
|
|
326
|
+
// Keep track of the last document content we detected for
|
|
327
|
+
const [lastDetectedDocument, setLastDetectedDocument] = useState<string>("");
|
|
328
|
+
|
|
329
|
+
// Auto-detect language when document content is available or changes
|
|
286
330
|
useEffect(() => {
|
|
287
|
-
if (
|
|
331
|
+
if (
|
|
332
|
+
documentContent &&
|
|
333
|
+
!isDetectingLanguage &&
|
|
334
|
+
documentContent !== lastDetectedDocument
|
|
335
|
+
) {
|
|
336
|
+
// Reset detected language when document changes, then detect again
|
|
337
|
+
if (detectedLanguageName && lastDetectedDocument) {
|
|
338
|
+
setDetectedLanguageName("");
|
|
339
|
+
setTargetLanguage(null);
|
|
340
|
+
}
|
|
341
|
+
setLastDetectedDocument(documentContent);
|
|
288
342
|
detectLanguage();
|
|
289
343
|
}
|
|
290
|
-
}, [
|
|
344
|
+
}, [documentContent]);
|
|
291
345
|
|
|
292
346
|
const handleTranslate = async () => {
|
|
293
347
|
if (!targetLanguage?.languageCode) {
|
|
@@ -349,7 +403,7 @@ Return your response as a JSON object in this exact format:
|
|
|
349
403
|
},
|
|
350
404
|
],
|
|
351
405
|
{ editContext, createAiContext: createWizardAiContext },
|
|
352
|
-
{ model: "gpt-
|
|
406
|
+
{ model: "gpt-4.1", stream: false },
|
|
353
407
|
{ signal: abortController.signal },
|
|
354
408
|
);
|
|
355
409
|
|
|
@@ -377,6 +431,9 @@ Return your response as a JSON object in this exact format:
|
|
|
377
431
|
) {
|
|
378
432
|
updatedTranslatedFields[itemId][fieldIndex].translatedValue =
|
|
379
433
|
translatedValue || "";
|
|
434
|
+
updatedTranslatedFields[itemId][
|
|
435
|
+
fieldIndex
|
|
436
|
+
].missingSuggestions = undefined;
|
|
380
437
|
}
|
|
381
438
|
}
|
|
382
439
|
});
|
|
@@ -385,6 +442,25 @@ Return your response as a JSON object in this exact format:
|
|
|
385
442
|
console.log(
|
|
386
443
|
`Translation complete: ${translations.length} fields translated`,
|
|
387
444
|
);
|
|
445
|
+
|
|
446
|
+
// Check translation completeness for all translated fields
|
|
447
|
+
translations.forEach((translation: any) => {
|
|
448
|
+
const { itemId, fieldId, translatedValue } = translation;
|
|
449
|
+
if (translatedValue && translatedValue.trim()) {
|
|
450
|
+
const originalField = fieldsForTranslation.find(
|
|
451
|
+
(f) => f.itemId === itemId && f.fieldId === fieldId,
|
|
452
|
+
);
|
|
453
|
+
if (originalField) {
|
|
454
|
+
checkTranslationCompleteness(
|
|
455
|
+
itemId,
|
|
456
|
+
fieldId,
|
|
457
|
+
originalField.originalValue,
|
|
458
|
+
translatedValue,
|
|
459
|
+
originalField.fieldName,
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
});
|
|
388
464
|
} catch (parseError) {
|
|
389
465
|
console.error("Failed to parse AI response:", parseError);
|
|
390
466
|
}
|
|
@@ -397,6 +473,216 @@ Return your response as a JSON object in this exact format:
|
|
|
397
473
|
}
|
|
398
474
|
};
|
|
399
475
|
|
|
476
|
+
const handleSingleFieldTranslation = async (
|
|
477
|
+
itemId: string,
|
|
478
|
+
fieldId: string,
|
|
479
|
+
field: TranslatableField,
|
|
480
|
+
) => {
|
|
481
|
+
if (!targetLanguage?.languageCode || !editContext) {
|
|
482
|
+
console.warn("Missing target language or edit context");
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const fieldKey = `${itemId}-${fieldId}`;
|
|
487
|
+
setTranslatingFields((prev) => new Set(prev).add(fieldKey));
|
|
488
|
+
|
|
489
|
+
try {
|
|
490
|
+
// Create a prompt for this specific field
|
|
491
|
+
const prompt = `You are a translation assistant. You have been given a document in ${targetLanguage.name} and a single field that needs to be translated from ${sourceLanguage} to ${targetLanguage.name}.
|
|
492
|
+
|
|
493
|
+
Your task is to:
|
|
494
|
+
1. Find content from the provided document that matches this field
|
|
495
|
+
2. Return ONLY the translated text from the document - do not change, modify, or create any new text
|
|
496
|
+
3. If you cannot find a suitable translation in the document for this field, return an empty string
|
|
497
|
+
|
|
498
|
+
Document content:
|
|
499
|
+
${documentContent}
|
|
500
|
+
|
|
501
|
+
Field to translate:
|
|
502
|
+
- Field Name: ${field.fieldName}
|
|
503
|
+
- Value (${sourceLanguage}): ${field.value}
|
|
504
|
+
|
|
505
|
+
Return your response as a JSON object in this exact format:
|
|
506
|
+
{
|
|
507
|
+
"translatedValue": "exact text from document or empty string if no match found"
|
|
508
|
+
}`;
|
|
509
|
+
|
|
510
|
+
const abortController = new AbortController();
|
|
511
|
+
|
|
512
|
+
const result = await executePrompt(
|
|
513
|
+
[
|
|
514
|
+
{
|
|
515
|
+
content: prompt,
|
|
516
|
+
role: "user",
|
|
517
|
+
name: "user",
|
|
518
|
+
id: crypto.randomUUID(),
|
|
519
|
+
},
|
|
520
|
+
],
|
|
521
|
+
{ editContext, createAiContext: createWizardAiContext },
|
|
522
|
+
{ model: "gpt-4.1", stream: false },
|
|
523
|
+
{ signal: abortController.signal },
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
if (result && result.messages && result.messages.length > 0) {
|
|
527
|
+
const lastMessage = result.messages[result.messages.length - 1];
|
|
528
|
+
if (lastMessage && lastMessage.content) {
|
|
529
|
+
try {
|
|
530
|
+
const parsedResponse = JSON.parse(lastMessage.content);
|
|
531
|
+
const translatedValue = parsedResponse.translatedValue || "";
|
|
532
|
+
|
|
533
|
+
// Update this specific field's translation
|
|
534
|
+
setTranslatedFields((prev) => ({
|
|
535
|
+
...prev,
|
|
536
|
+
[itemId]:
|
|
537
|
+
prev[itemId]?.map((f) =>
|
|
538
|
+
f.fieldId === fieldId
|
|
539
|
+
? { ...f, translatedValue, missingSuggestions: undefined }
|
|
540
|
+
: f,
|
|
541
|
+
) || [],
|
|
542
|
+
}));
|
|
543
|
+
|
|
544
|
+
console.log(
|
|
545
|
+
`Single field translation complete for ${field.fieldName}`,
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
// Check translation completeness after translation
|
|
549
|
+
if (translatedValue.trim()) {
|
|
550
|
+
checkTranslationCompleteness(
|
|
551
|
+
itemId,
|
|
552
|
+
fieldId,
|
|
553
|
+
field.value,
|
|
554
|
+
translatedValue,
|
|
555
|
+
field.fieldName,
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
} catch (parseError) {
|
|
559
|
+
console.error("Failed to parse AI response:", parseError);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
} catch (error) {
|
|
564
|
+
console.error("Single field translation error:", error);
|
|
565
|
+
} finally {
|
|
566
|
+
setTranslatingFields((prev) => {
|
|
567
|
+
const newSet = new Set(prev);
|
|
568
|
+
newSet.delete(fieldKey);
|
|
569
|
+
return newSet;
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
const checkTranslationCompleteness = async (
|
|
575
|
+
itemId: string,
|
|
576
|
+
fieldId: string,
|
|
577
|
+
originalText: string,
|
|
578
|
+
translatedText: string,
|
|
579
|
+
fieldName: string,
|
|
580
|
+
) => {
|
|
581
|
+
if (!editContext || !translatedText.trim()) {
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const fieldKey = `${itemId}-${fieldId}`;
|
|
586
|
+
setCheckingFields((prev) => new Set(prev).add(fieldKey));
|
|
587
|
+
|
|
588
|
+
try {
|
|
589
|
+
const prompt = `You are a translation quality checker. Your task is to evaluate if a translation is complete and accurate.
|
|
590
|
+
|
|
591
|
+
Original text (${sourceLanguage}):
|
|
592
|
+
${originalText}
|
|
593
|
+
|
|
594
|
+
Translated text (${targetLanguage?.name || "target language"}):
|
|
595
|
+
${translatedText}
|
|
596
|
+
|
|
597
|
+
Field context: ${fieldName}
|
|
598
|
+
|
|
599
|
+
Is the translation complete (no missing content)? Do not assess the quality of the translation, just if it is complete. Only report missing sentences missing completely.
|
|
600
|
+
Do not report missing words, phrases, or content that is present in the original text. We just want to make sure we copied all content from the translation document.
|
|
601
|
+
|
|
602
|
+
If the translation is incomplete, identify specific content, phrases, or meaning that exists in the original but is missing in the translation, and suggest the exact text that should be added to make the translation complete.
|
|
603
|
+
|
|
604
|
+
Return your response as a JSON object in this exact format:
|
|
605
|
+
{
|
|
606
|
+
"isComplete": true/false,
|
|
607
|
+
"reason": "Brief explanation of your assessment",
|
|
608
|
+
"missingSuggestions": "If isComplete is false, provide specific suggestions for what needs to be added to complete the translation. Include the actual text that should be added. If isComplete is true, leave this empty."
|
|
609
|
+
}`;
|
|
610
|
+
|
|
611
|
+
const abortController = new AbortController();
|
|
612
|
+
|
|
613
|
+
const result = await executePrompt(
|
|
614
|
+
[
|
|
615
|
+
{
|
|
616
|
+
content: prompt,
|
|
617
|
+
role: "user",
|
|
618
|
+
name: "user",
|
|
619
|
+
id: crypto.randomUUID(),
|
|
620
|
+
},
|
|
621
|
+
],
|
|
622
|
+
{ editContext, createAiContext: createWizardAiContext },
|
|
623
|
+
{ model: "gpt-4.1", stream: false },
|
|
624
|
+
{ signal: abortController.signal },
|
|
625
|
+
);
|
|
626
|
+
|
|
627
|
+
if (result && result.messages && result.messages.length > 0) {
|
|
628
|
+
const lastMessage = result.messages[result.messages.length - 1];
|
|
629
|
+
if (lastMessage && lastMessage.content) {
|
|
630
|
+
try {
|
|
631
|
+
const parsedResponse = JSON.parse(lastMessage.content);
|
|
632
|
+
const isComplete = parsedResponse.isComplete || false;
|
|
633
|
+
const missingSuggestions = parsedResponse.missingSuggestions || "";
|
|
634
|
+
|
|
635
|
+
// Update the field's completeness status and missing suggestions
|
|
636
|
+
setTranslatedFields((prev) => ({
|
|
637
|
+
...prev,
|
|
638
|
+
[itemId]:
|
|
639
|
+
prev[itemId]?.map((f) =>
|
|
640
|
+
f.fieldId === fieldId
|
|
641
|
+
? {
|
|
642
|
+
...f,
|
|
643
|
+
isComplete,
|
|
644
|
+
missingSuggestions: isComplete
|
|
645
|
+
? undefined
|
|
646
|
+
: missingSuggestions,
|
|
647
|
+
}
|
|
648
|
+
: f,
|
|
649
|
+
) || [],
|
|
650
|
+
}));
|
|
651
|
+
|
|
652
|
+
console.log(
|
|
653
|
+
`Translation completeness check for ${fieldName}: ${isComplete ? "Complete" : "Incomplete"} - ${parsedResponse.reason}`,
|
|
654
|
+
);
|
|
655
|
+
|
|
656
|
+
if (!isComplete && missingSuggestions) {
|
|
657
|
+
console.log(
|
|
658
|
+
`Missing content suggestions for ${fieldName}: ${missingSuggestions}`,
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
return isComplete;
|
|
663
|
+
} catch (parseError) {
|
|
664
|
+
console.error(
|
|
665
|
+
"Failed to parse completeness check response:",
|
|
666
|
+
parseError,
|
|
667
|
+
);
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
return false;
|
|
674
|
+
} catch (error) {
|
|
675
|
+
console.error("Translation completeness check error:", error);
|
|
676
|
+
return false;
|
|
677
|
+
} finally {
|
|
678
|
+
setCheckingFields((prev) => {
|
|
679
|
+
const newSet = new Set(prev);
|
|
680
|
+
newSet.delete(fieldKey);
|
|
681
|
+
return newSet;
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
|
|
400
686
|
const handleFieldChange = (
|
|
401
687
|
itemId: string,
|
|
402
688
|
fieldId: string,
|
|
@@ -407,7 +693,12 @@ Return your response as a JSON object in this exact format:
|
|
|
407
693
|
[itemId]:
|
|
408
694
|
prev[itemId]?.map((field) =>
|
|
409
695
|
field.fieldId === fieldId
|
|
410
|
-
? {
|
|
696
|
+
? {
|
|
697
|
+
...field,
|
|
698
|
+
translatedValue: value,
|
|
699
|
+
isComplete: undefined,
|
|
700
|
+
missingSuggestions: undefined,
|
|
701
|
+
}
|
|
411
702
|
: field,
|
|
412
703
|
) || [],
|
|
413
704
|
}));
|
|
@@ -640,8 +931,27 @@ Return your response as a JSON object in this exact format:
|
|
|
640
931
|
icon={<Upload className="h-5 w-5" />}
|
|
641
932
|
>
|
|
642
933
|
{isLoading ? (
|
|
643
|
-
<div className="py-8 text-center">
|
|
644
|
-
|
|
934
|
+
<div className="py-8 text-center">
|
|
935
|
+
<div className="flex items-center justify-center gap-2">
|
|
936
|
+
<RefreshCw className="h-4 w-4 animate-spin" />
|
|
937
|
+
Loading translatable fields...
|
|
938
|
+
</div>
|
|
939
|
+
</div>
|
|
940
|
+
) : isTranslating ? (
|
|
941
|
+
<div className="py-8 text-center">
|
|
942
|
+
<div className="flex items-center justify-center gap-2">
|
|
943
|
+
<RefreshCw className="h-4 w-4 animate-spin" />
|
|
944
|
+
Translating fields...
|
|
945
|
+
</div>
|
|
946
|
+
</div>
|
|
947
|
+
) : isPublishing ? (
|
|
948
|
+
<div className="py-8 text-center">
|
|
949
|
+
<div className="flex items-center justify-center gap-2">
|
|
950
|
+
<RefreshCw className="h-4 w-4 animate-spin" />
|
|
951
|
+
Publishing translations...
|
|
952
|
+
</div>
|
|
953
|
+
</div>
|
|
954
|
+
) : translatedCount > 0 ? (
|
|
645
955
|
<div className="space-y-6">
|
|
646
956
|
{Object.entries(translatableFields).map(([itemId, fields]) => (
|
|
647
957
|
<div key={itemId} className="rounded-lg p-4">
|
|
@@ -667,9 +977,104 @@ Return your response as a JSON object in this exact format:
|
|
|
667
977
|
</div>
|
|
668
978
|
</div>
|
|
669
979
|
<div className="flex flex-col">
|
|
670
|
-
<
|
|
671
|
-
|
|
672
|
-
|
|
980
|
+
<div className="mb-1 flex items-center justify-between">
|
|
981
|
+
<div className="flex items-center gap-2">
|
|
982
|
+
<label className="block text-xs font-medium">
|
|
983
|
+
{field.fieldName}
|
|
984
|
+
</label>
|
|
985
|
+
{translatedField?.translatedValue && (
|
|
986
|
+
<div className="flex items-center">
|
|
987
|
+
{checkingFields.has(
|
|
988
|
+
`${itemId}-${field.fieldId}`,
|
|
989
|
+
) ? (
|
|
990
|
+
<RefreshCw className="h-3 w-3 animate-spin text-gray-500" />
|
|
991
|
+
) : translatedField.isComplete === true ? (
|
|
992
|
+
<span title="Translation is complete">
|
|
993
|
+
<CheckCircle className="h-3 w-3 text-green-600" />
|
|
994
|
+
</span>
|
|
995
|
+
) : translatedField.isComplete === false ? (
|
|
996
|
+
<span title="Translation may be incomplete">
|
|
997
|
+
<AlertCircle className="h-3 w-3 text-red-600" />
|
|
998
|
+
</span>
|
|
999
|
+
) : (
|
|
1000
|
+
<div className="h-3 w-3" />
|
|
1001
|
+
)}
|
|
1002
|
+
</div>
|
|
1003
|
+
)}
|
|
1004
|
+
</div>
|
|
1005
|
+
<div className="flex items-center gap-1">
|
|
1006
|
+
{translatedField?.translatedValue && (
|
|
1007
|
+
<button
|
|
1008
|
+
type="button"
|
|
1009
|
+
onClick={() =>
|
|
1010
|
+
checkTranslationCompleteness(
|
|
1011
|
+
itemId,
|
|
1012
|
+
field.fieldId,
|
|
1013
|
+
field.value,
|
|
1014
|
+
translatedField.translatedValue,
|
|
1015
|
+
field.fieldName,
|
|
1016
|
+
)
|
|
1017
|
+
}
|
|
1018
|
+
disabled={
|
|
1019
|
+
checkingFields.has(
|
|
1020
|
+
`${itemId}-${field.fieldId}`,
|
|
1021
|
+
) || !translatedField.translatedValue.trim()
|
|
1022
|
+
}
|
|
1023
|
+
className="flex items-center gap-1 rounded px-2 py-1 text-xs text-gray-600 hover:bg-gray-50 hover:text-gray-800 disabled:cursor-not-allowed disabled:text-gray-400 disabled:hover:bg-transparent"
|
|
1024
|
+
title="Check translation completeness"
|
|
1025
|
+
>
|
|
1026
|
+
{checkingFields.has(
|
|
1027
|
+
`${itemId}-${field.fieldId}`,
|
|
1028
|
+
) ? (
|
|
1029
|
+
<RefreshCw className="h-3 w-3 animate-spin" />
|
|
1030
|
+
) : (
|
|
1031
|
+
<CheckCircle className="h-3 w-3" />
|
|
1032
|
+
)}
|
|
1033
|
+
Check
|
|
1034
|
+
</button>
|
|
1035
|
+
)}
|
|
1036
|
+
<button
|
|
1037
|
+
type="button"
|
|
1038
|
+
onClick={() =>
|
|
1039
|
+
handleSingleFieldTranslation(
|
|
1040
|
+
itemId,
|
|
1041
|
+
field.fieldId,
|
|
1042
|
+
field,
|
|
1043
|
+
)
|
|
1044
|
+
}
|
|
1045
|
+
disabled={
|
|
1046
|
+
!targetLanguage?.languageCode ||
|
|
1047
|
+
translatingFields.has(
|
|
1048
|
+
`${itemId}-${field.fieldId}`,
|
|
1049
|
+
) ||
|
|
1050
|
+
isTranslating
|
|
1051
|
+
}
|
|
1052
|
+
className="flex items-center gap-1 rounded px-2 py-1 text-xs text-blue-600 hover:bg-blue-50 hover:text-blue-800 disabled:cursor-not-allowed disabled:text-gray-400 disabled:hover:bg-transparent"
|
|
1053
|
+
title="Retranslate this field"
|
|
1054
|
+
>
|
|
1055
|
+
{translatingFields.has(
|
|
1056
|
+
`${itemId}-${field.fieldId}`,
|
|
1057
|
+
) ? (
|
|
1058
|
+
<RefreshCw className="h-3 w-3 animate-spin" />
|
|
1059
|
+
) : (
|
|
1060
|
+
<Wand2 className="h-3 w-3" />
|
|
1061
|
+
)}
|
|
1062
|
+
Retranslate
|
|
1063
|
+
</button>
|
|
1064
|
+
</div>
|
|
1065
|
+
</div>
|
|
1066
|
+
{translatedField?.isComplete === false &&
|
|
1067
|
+
translatedField?.missingSuggestions && (
|
|
1068
|
+
<div className="mb-2 rounded border border-orange-200 bg-orange-50 p-2">
|
|
1069
|
+
<div className="mb-1 flex items-center gap-1 text-xs font-medium text-orange-800">
|
|
1070
|
+
<Sparkles className="h-3 w-3" />
|
|
1071
|
+
AI Suggestions for Missing Content:
|
|
1072
|
+
</div>
|
|
1073
|
+
<p className="text-xs text-orange-700 select-text">
|
|
1074
|
+
{translatedField.missingSuggestions}
|
|
1075
|
+
</p>
|
|
1076
|
+
</div>
|
|
1077
|
+
)}
|
|
673
1078
|
<InputTextarea
|
|
674
1079
|
value={translatedField?.translatedValue || ""}
|
|
675
1080
|
onChange={(e) =>
|
|
@@ -691,6 +1096,12 @@ Return your response as a JSON object in this exact format:
|
|
|
691
1096
|
</div>
|
|
692
1097
|
))}
|
|
693
1098
|
</div>
|
|
1099
|
+
) : (
|
|
1100
|
+
<div className="py-8 text-center text-sm text-gray-500">
|
|
1101
|
+
{Object.keys(translatableFields).length > 0
|
|
1102
|
+
? "Click 'Translate Fields' to start the translation process."
|
|
1103
|
+
: "No translatable fields found."}
|
|
1104
|
+
</div>
|
|
694
1105
|
)}
|
|
695
1106
|
</WizardBox>
|
|
696
1107
|
|
package/src/revision.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = "1.0.
|
|
2
|
-
export const buildDate = "2025-08-
|
|
1
|
+
export const version = "1.0.4031";
|
|
2
|
+
export const buildDate = "2025-08-06 09:06:10";
|