@parhelia/localization 0.1.12907 → 0.1.12909
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/LocalizeItemCommand.d.ts +7 -1
- package/dist/LocalizeItemCommand.d.ts.map +1 -1
- package/dist/LocalizeItemCommand.js +31 -18
- package/dist/LocalizeItemDialog.d.ts.map +1 -1
- package/dist/LocalizeItemDialog.js +97 -35
- package/dist/LocalizeItemUtils.d.ts +1 -2
- package/dist/LocalizeItemUtils.d.ts.map +1 -1
- package/dist/LocalizeItemUtils.js +78 -36
- package/dist/api/discovery.d.ts +25 -0
- package/dist/api/discovery.d.ts.map +1 -1
- package/dist/api/discovery.js +106 -2
- package/dist/hooks/useTranslationWizard.d.ts.map +1 -1
- package/dist/hooks/useTranslationWizard.js +3 -3
- package/dist/index.d.ts +10 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -32
- package/dist/services/translationService.d.ts +41 -10
- package/dist/services/translationService.d.ts.map +1 -1
- package/dist/services/translationService.js +48 -6
- package/dist/settings/TranslationServicesPanel.d.ts.map +1 -1
- package/dist/settings/TranslationServicesPanel.js +21 -36
- package/dist/setup/LocalizationSetupStep.d.ts.map +1 -1
- package/dist/setup/LocalizationSetupStep.js +29 -18
- package/dist/sidebar/TranslationSidebar.d.ts.map +1 -1
- package/dist/sidebar/TranslationSidebar.js +20 -10
- package/dist/steps/ItemSelectionStep.d.ts +3 -0
- package/dist/steps/ItemSelectionStep.d.ts.map +1 -0
- package/dist/steps/ItemSelectionStep.js +24 -0
- package/dist/steps/ItemSelectionTree.d.ts +13 -0
- package/dist/steps/ItemSelectionTree.d.ts.map +1 -0
- package/dist/steps/ItemSelectionTree.js +327 -0
- package/dist/steps/PromptCustomizationStep.d.ts +1 -1
- package/dist/steps/PromptCustomizationStep.d.ts.map +1 -1
- package/dist/steps/PromptCustomizationStep.js +159 -56
- package/dist/steps/ServiceLanguageSelectionStep.d.ts +6 -1
- package/dist/steps/ServiceLanguageSelectionStep.d.ts.map +1 -1
- package/dist/steps/ServiceLanguageSelectionStep.js +92 -165
- package/dist/steps/WizardStepShell.d.ts +17 -0
- package/dist/steps/WizardStepShell.d.ts.map +1 -0
- package/dist/steps/WizardStepShell.js +11 -0
- package/dist/steps/types.d.ts +17 -1
- package/dist/steps/types.d.ts.map +1 -1
- package/dist/translation-center/TranslationBatches.d.ts +2 -0
- package/dist/translation-center/TranslationBatches.d.ts.map +1 -0
- package/dist/translation-center/TranslationBatches.js +1180 -0
- package/dist/translation-center/TranslationManagement.d.ts.map +1 -1
- package/dist/translation-center/TranslationManagement.js +25 -15
- package/dist/translation-center/TranslationsTitlebar.d.ts +6 -0
- package/dist/translation-center/TranslationsTitlebar.d.ts.map +1 -0
- package/dist/translation-center/TranslationsTitlebar.js +25 -0
- package/dist/translationEvents.d.ts +6 -0
- package/dist/translationEvents.d.ts.map +1 -0
- package/dist/translationEvents.js +4 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/constants.d.ts +0 -15
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js +0 -21
- package/dist/steps/MetadataInputStep.d.ts +0 -4
- package/dist/steps/MetadataInputStep.d.ts.map +0 -1
- package/dist/steps/MetadataInputStep.js +0 -41
- package/dist/steps/SubitemDiscoveryStep.d.ts +0 -3
- package/dist/steps/SubitemDiscoveryStep.d.ts.map +0 -1
- package/dist/steps/SubitemDiscoveryStep.js +0 -313
- package/dist/steps/index.d.ts +0 -5
- package/dist/steps/index.d.ts.map +0 -1
- package/dist/steps/index.js +0 -4
- package/dist/translation-center/BatchTranslationView.d.ts +0 -8
- package/dist/translation-center/BatchTranslationView.d.ts.map +0 -1
- package/dist/translation-center/BatchTranslationView.js +0 -870
- package/dist/translation-center/RecentTranslations.d.ts +0 -2
- package/dist/translation-center/RecentTranslations.d.ts.map +0 -1
- package/dist/translation-center/RecentTranslations.js +0 -309
- package/dist/utils/createVersions.d.ts +0 -14
- package/dist/utils/createVersions.d.ts.map +0 -1
- package/dist/utils/createVersions.js +0 -26
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import { TranslationStepProps } from "./types";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Language-only step. Item selection lives in its own step
|
|
4
|
+
* (see ItemSelectionStep). The legacy name is kept so existing imports
|
|
5
|
+
* keep working.
|
|
6
|
+
*/
|
|
7
|
+
export declare function ServiceLanguageSelectionStep({ isActive, data, setData, onStepCompleted, editContext, }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
|
|
3
8
|
//# sourceMappingURL=ServiceLanguageSelectionStep.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServiceLanguageSelectionStep.d.ts","sourceRoot":"","sources":["../../src/steps/ServiceLanguageSelectionStep.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ServiceLanguageSelectionStep.d.ts","sourceRoot":"","sources":["../../src/steps/ServiceLanguageSelectionStep.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,SAAS,CAAC;AAGtE;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,EAC3C,QAAe,EACf,IAAI,EACJ,OAAO,EACP,eAAe,EACf,WAAW,GACZ,EAAE,oBAAoB,2CAiRtB"}
|
|
@@ -1,63 +1,67 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return "text-purple-800 bg-purple-100";
|
|
12
|
-
case "Not started":
|
|
13
|
-
return "text-[var(--color-gray-2)] bg-[var(--color-gray-4)]";
|
|
14
|
-
default:
|
|
15
|
-
return "text-[var(--color-gray-2)] bg-[var(--color-gray-4)]";
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
export function ServiceLanguageSelectionStep({ stepIndex, isActive = true, data, setData, onStepCompleted, editContext, setFooterActions, requestClose }) {
|
|
3
|
+
import { CountBadge, getLanguages } from "@parhelia/core";
|
|
4
|
+
import { WizardStepShell } from "./WizardStepShell";
|
|
5
|
+
/**
|
|
6
|
+
* Language-only step. Item selection lives in its own step
|
|
7
|
+
* (see ItemSelectionStep). The legacy name is kept so existing imports
|
|
8
|
+
* keep working.
|
|
9
|
+
*/
|
|
10
|
+
export function ServiceLanguageSelectionStep({ isActive = true, data, setData, onStepCompleted, editContext, }) {
|
|
19
11
|
const [languageSelection, setLanguageSelection] = useState({});
|
|
20
12
|
const dataRef = useRef(data);
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// Use ref to track onStepCompleted to avoid dependency issues
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
dataRef.current = data;
|
|
15
|
+
}, [data]);
|
|
25
16
|
const onStepCompletedRef = useRef(onStepCompleted);
|
|
26
17
|
useEffect(() => {
|
|
27
18
|
onStepCompletedRef.current = onStepCompleted;
|
|
28
19
|
}, [onStepCompleted]);
|
|
29
|
-
// Track last completion state to prevent unnecessary calls
|
|
30
20
|
const lastCompletionStateRef = useRef(null);
|
|
31
|
-
// Call completion check when component mounts or when returning to this step
|
|
32
|
-
// Also check when data changes (but only if we're actually the active step)
|
|
33
21
|
useEffect(() => {
|
|
34
|
-
// Only update completion when this step is active
|
|
35
22
|
if (!isActive)
|
|
36
23
|
return;
|
|
37
|
-
const
|
|
38
|
-
const hasLanguages = data.targetLanguages.length > 0;
|
|
39
|
-
const isCompleted = hasProvider && hasLanguages;
|
|
40
|
-
// Only call if completion state actually changed
|
|
24
|
+
const isCompleted = data.targetLanguages.length > 0;
|
|
41
25
|
if (lastCompletionStateRef.current !== isCompleted) {
|
|
42
26
|
lastCompletionStateRef.current = isCompleted;
|
|
43
27
|
onStepCompletedRef.current(isCompleted);
|
|
44
28
|
}
|
|
45
|
-
}, [isActive, data.
|
|
46
|
-
// 1) Derive provider + available languages (lightweight, in-memory)
|
|
47
|
-
const provider = useMemo(() => data.translationProviders.find(p => p.name === data.translationProvider), [data.translationProviders, data.translationProvider]);
|
|
48
|
-
// Use editContext.itemLanguages (like TranslationSidebar) to get all languages including source language
|
|
49
|
-
// This is not item-specific - it shows all available languages for the site
|
|
29
|
+
}, [isActive, data.targetLanguages.length]);
|
|
50
30
|
const editContextLanguages = useMemo(() => editContext?.itemLanguages || [], [editContext?.itemLanguages]);
|
|
51
|
-
|
|
31
|
+
const [siteLanguages, setSiteLanguages] = useState([]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (editContextLanguages.length > 0 || data.languageData.size > 0)
|
|
34
|
+
return;
|
|
35
|
+
let cancelled = false;
|
|
36
|
+
const loadSiteLanguages = async () => {
|
|
37
|
+
try {
|
|
38
|
+
const res = await getLanguages();
|
|
39
|
+
const languages = (res?.data ?? res ?? []);
|
|
40
|
+
if (!cancelled && Array.isArray(languages)) {
|
|
41
|
+
setSiteLanguages(languages);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error("Failed to load languages:", error);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
loadSiteLanguages();
|
|
49
|
+
return () => {
|
|
50
|
+
cancelled = true;
|
|
51
|
+
};
|
|
52
|
+
}, [editContextLanguages.length, data.languageData.size]);
|
|
52
53
|
const siteLanguageCodes = useMemo(() => {
|
|
53
54
|
if (editContextLanguages.length > 0) {
|
|
54
|
-
return editContextLanguages.map(lang => lang.languageCode);
|
|
55
|
+
return editContextLanguages.map((lang) => lang.languageCode);
|
|
56
|
+
}
|
|
57
|
+
if (data.languageData.size > 0) {
|
|
58
|
+
return Array.from(data.languageData.keys());
|
|
59
|
+
}
|
|
60
|
+
if (siteLanguages.length > 0) {
|
|
61
|
+
return siteLanguages.map((lang) => lang.languageCode);
|
|
55
62
|
}
|
|
56
63
|
return Array.from(data.languageData.keys());
|
|
57
|
-
}, [editContextLanguages, data.languageData]);
|
|
58
|
-
const availableLanguageCodes = useMemo(() => (provider?.supportedLanguages?.length ? provider.supportedLanguages : siteLanguageCodes), [provider?.supportedLanguages, siteLanguageCodes]);
|
|
59
|
-
// Determine the source language from the items being translated
|
|
60
|
-
// Use the first item's language as the source language (all items should have the same source language)
|
|
64
|
+
}, [editContextLanguages, data.languageData, siteLanguages]);
|
|
61
65
|
const itemSourceLanguage = useMemo(() => {
|
|
62
66
|
return (data.items[0]?.descriptor.language ||
|
|
63
67
|
editContext?.item?.descriptor.language ||
|
|
@@ -65,177 +69,100 @@ export function ServiceLanguageSelectionStep({ stepIndex, isActive = true, data,
|
|
|
65
69
|
"en");
|
|
66
70
|
}, [data.items, editContext?.item, editContext?.currentItemDescriptor]);
|
|
67
71
|
const allLanguages = useMemo(() => {
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
const arr =
|
|
71
|
-
.map(code => {
|
|
72
|
-
// Prefer language info from editContext (like TranslationSidebar), fallback to languageData
|
|
72
|
+
const editContextLanguageMap = new Map(editContextLanguages.map((lang) => [lang.languageCode, lang]));
|
|
73
|
+
const siteLanguageMap = new Map(siteLanguages.map((lang) => [lang.languageCode, lang]));
|
|
74
|
+
const arr = siteLanguageCodes
|
|
75
|
+
.map((code) => {
|
|
73
76
|
const editContextLang = editContextLanguageMap.get(code);
|
|
77
|
+
const siteLanguage = siteLanguageMap.get(code);
|
|
74
78
|
const languageDataLang = data.languageData.get(code);
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
const sourceLanguage = languageDataLang?.translationStatus?.sourceLanguage ||
|
|
80
|
+
itemSourceLanguage;
|
|
77
81
|
return {
|
|
78
82
|
code,
|
|
79
|
-
name: editContextLang?.name ||
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
name: editContextLang?.name ||
|
|
84
|
+
languageDataLang?.name ||
|
|
85
|
+
siteLanguage?.name ||
|
|
86
|
+
code,
|
|
87
|
+
icon: editContextLang?.icon || siteLanguage?.icon,
|
|
88
|
+
sourceLanguage,
|
|
83
89
|
};
|
|
84
90
|
})
|
|
85
|
-
|
|
86
|
-
.filter(lang => lang.sourceLanguage !== lang.code);
|
|
91
|
+
.filter((lang) => lang.sourceLanguage !== lang.code);
|
|
87
92
|
arr.sort((a, b) => a.name.localeCompare(b.name));
|
|
88
93
|
return arr;
|
|
89
|
-
}, [
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
}, [
|
|
95
|
+
siteLanguageCodes,
|
|
96
|
+
editContextLanguages,
|
|
97
|
+
siteLanguages,
|
|
98
|
+
data.languageData,
|
|
99
|
+
itemSourceLanguage,
|
|
100
|
+
]);
|
|
101
|
+
const lastProcessedTargetLanguagesRef = useRef("");
|
|
93
102
|
useEffect(() => {
|
|
94
103
|
if (!allLanguages || allLanguages.length === 0)
|
|
95
104
|
return;
|
|
96
|
-
// Create a stable key from targetLanguages to detect actual changes
|
|
97
105
|
const targetLanguagesKey = JSON.stringify([...data.targetLanguages].sort());
|
|
98
|
-
|
|
99
|
-
if (lastProcessedTargetLanguagesRef.current === targetLanguagesKey) {
|
|
106
|
+
if (lastProcessedTargetLanguagesRef.current === targetLanguagesKey)
|
|
100
107
|
return;
|
|
101
|
-
}
|
|
102
108
|
lastProcessedTargetLanguagesRef.current = targetLanguagesKey;
|
|
103
109
|
const initialSelection = {};
|
|
104
|
-
// Mark as selected any language that's in our saved targetLanguages array
|
|
105
110
|
for (const langCode of data.targetLanguages) {
|
|
106
|
-
const lang = allLanguages.find(l => l.code === langCode);
|
|
107
|
-
if (lang)
|
|
111
|
+
const lang = allLanguages.find((l) => l.code === langCode);
|
|
112
|
+
if (lang)
|
|
108
113
|
initialSelection[langCode] = true;
|
|
109
|
-
}
|
|
110
114
|
}
|
|
111
115
|
setLanguageSelection(initialSelection);
|
|
112
116
|
}, [allLanguages, data.targetLanguages]);
|
|
113
|
-
|
|
114
|
-
const lastSetTargetLanguagesRef = useRef('');
|
|
115
|
-
// Update wizard data when language selection changes
|
|
117
|
+
const lastSetTargetLanguagesRef = useRef("");
|
|
116
118
|
useEffect(() => {
|
|
117
119
|
const selectedLanguages = Object.entries(languageSelection)
|
|
118
120
|
.filter(([, isSelected]) => isSelected)
|
|
119
121
|
.map(([code]) => code);
|
|
120
122
|
const selectedLanguagesKey = JSON.stringify([...selectedLanguages].sort());
|
|
121
123
|
const currentTargetLanguagesKey = JSON.stringify([...data.targetLanguages].sort());
|
|
122
|
-
|
|
123
|
-
if (selectedLanguagesKey === currentTargetLanguagesKey) {
|
|
124
|
-
// Selection matches current data, no update needed
|
|
124
|
+
if (selectedLanguagesKey === currentTargetLanguagesKey)
|
|
125
125
|
return;
|
|
126
|
-
|
|
127
|
-
if (lastSetTargetLanguagesRef.current === selectedLanguagesKey) {
|
|
128
|
-
// We've already set this value, skip to prevent loops
|
|
126
|
+
if (lastSetTargetLanguagesRef.current === selectedLanguagesKey)
|
|
129
127
|
return;
|
|
130
|
-
}
|
|
131
128
|
lastSetTargetLanguagesRef.current = selectedLanguagesKey;
|
|
132
129
|
const newData = {
|
|
133
130
|
...dataRef.current,
|
|
134
|
-
targetLanguages: selectedLanguages
|
|
131
|
+
targetLanguages: selectedLanguages,
|
|
135
132
|
};
|
|
136
133
|
setData(newData);
|
|
137
|
-
// Completion will be updated by the useEffect that watches data.targetLanguages.length
|
|
138
134
|
}, [languageSelection, setData, data.targetLanguages]);
|
|
139
|
-
const handleProviderChange = (e) => {
|
|
140
|
-
const newProvider = e.target.value;
|
|
141
|
-
// Always create a new Map instance to ensure React detects the change
|
|
142
|
-
const newServiceCustomData = new Map();
|
|
143
|
-
if (data.serviceCustomData) {
|
|
144
|
-
data.serviceCustomData.forEach((value, key) => {
|
|
145
|
-
// Only preserve custom data if not switching away from OpenAI
|
|
146
|
-
if (key !== "OpenAI" || newProvider === "OpenAI") {
|
|
147
|
-
newServiceCustomData.set(key, value);
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
const newData = {
|
|
152
|
-
...data,
|
|
153
|
-
translationProvider: newProvider,
|
|
154
|
-
serviceCustomData: newServiceCustomData,
|
|
155
|
-
// Clear target languages when provider changes since language availability might change
|
|
156
|
-
targetLanguages: []
|
|
157
|
-
};
|
|
158
|
-
setData(newData);
|
|
159
|
-
// Clear UI selection too
|
|
160
|
-
setLanguageSelection({});
|
|
161
|
-
// Completion will be updated by the useEffect that watches data.translationProvider
|
|
162
|
-
};
|
|
163
135
|
const handleLanguageToggle = (langCode) => {
|
|
164
|
-
setLanguageSelection(prev => ({ ...prev, [langCode]: !prev[langCode] }));
|
|
165
|
-
};
|
|
166
|
-
const handleSubitemsToggle = () => {
|
|
167
|
-
const newData = {
|
|
168
|
-
...data,
|
|
169
|
-
includeSubitems: !data.includeSubitems
|
|
170
|
-
};
|
|
171
|
-
setData(newData);
|
|
136
|
+
setLanguageSelection((prev) => ({ ...prev, [langCode]: !prev[langCode] }));
|
|
172
137
|
};
|
|
173
138
|
const handleSelectAllLanguages = () => {
|
|
174
|
-
const allSelected = allLanguages.every(lang => languageSelection[lang.code]);
|
|
139
|
+
const allSelected = allLanguages.every((lang) => languageSelection[lang.code]);
|
|
175
140
|
const newSelection = {};
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
newSelection[lang.code] = false;
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
// Select all
|
|
184
|
-
allLanguages.forEach(lang => {
|
|
185
|
-
newSelection[lang.code] = true;
|
|
186
|
-
});
|
|
187
|
-
}
|
|
141
|
+
allLanguages.forEach((lang) => {
|
|
142
|
+
newSelection[lang.code] = !allSelected;
|
|
143
|
+
});
|
|
188
144
|
setLanguageSelection(newSelection);
|
|
189
145
|
};
|
|
190
146
|
const areAllLanguagesSelected = useMemo(() => {
|
|
191
147
|
if (allLanguages.length === 0)
|
|
192
148
|
return false;
|
|
193
|
-
return allLanguages.every(lang => languageSelection[lang.code]);
|
|
149
|
+
return allLanguages.every((lang) => languageSelection[lang.code]);
|
|
194
150
|
}, [allLanguages, languageSelection]);
|
|
195
151
|
const areSomeLanguagesSelected = useMemo(() => {
|
|
196
|
-
return allLanguages.some(lang => languageSelection[lang.code]);
|
|
152
|
+
return allLanguages.some((lang) => languageSelection[lang.code]);
|
|
197
153
|
}, [allLanguages, languageSelection]);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const serviceData = data.serviceCustomData?.get("OpenAI");
|
|
204
|
-
return serviceData?.enableCustomPrompt === true;
|
|
205
|
-
}, [data.translationProvider, data.serviceCustomData]);
|
|
206
|
-
return (_jsx("div", { className: "p-6 space-y-6 h-full flex flex-col", "data-testid": "service-language-selection-step", children: _jsxs("div", { className: "space-y-6 flex-1", children: [_jsxs("div", { className: "bg-background rounded-lg border border-[var(--color-gray-3)] p-4", children: [_jsx("h3", { className: "text-sm font-medium text-[var(--color-dark)] mb-2", children: "Translation Provider" }), _jsx("p", { className: "text-xs text-[var(--color-gray-2)] mb-3", children: "Choose how to translate your content. \"Create Versions\" will create new language versions without automatic translation." }), _jsxs("select", { value: data.translationProvider || "", onChange: handleProviderChange, className: "block w-full px-3 py-2 border border-[var(--color-gray-3)] rounded-md bg-[var(--color-gray-5)] text-[var(--color-dark)] text-sm focus:outline-none focus:ring-2 focus:ring-[#9650fb] focus:border-[#9650fb] transition-colors", "data-testid": "translation-provider-select", children: [_jsx("option", { value: "", disabled: true, children: "Select a provider..." }), data.translationProviders.map((provider) => (_jsx("option", { value: provider.name, children: provider.displayName || provider.name }, provider.name)))] })] }), multiItemEnabled && (_jsxs("div", { className: "bg-background rounded-lg border border-[var(--color-gray-3)] p-4", children: [_jsxs("label", { className: "flex items-center cursor-pointer", children: [_jsx("input", { type: "checkbox", checked: data.includeSubitems, onChange: handleSubitemsToggle, className: "h-4 w-4 text-[#9650fb] focus:ring-[#9650fb] border-[var(--color-gray-3)] rounded accent-[#9650fb]", "data-testid": "include-subitems-checkbox" }), _jsx("span", { className: "ml-2 text-sm text-[var(--color-dark)] font-medium", children: "Include subitems" })] }), _jsx("p", { className: "text-xs text-[var(--color-gray-2)] mt-1.5 ml-6", children: "Also translate any child components and nested content within this item." })] })), data.translationProvider === "OpenAI" && (_jsxs("div", { className: "bg-background rounded-lg border border-[var(--color-gray-3)] p-4", children: [_jsxs("label", { className: "flex items-center cursor-pointer", children: [_jsx("input", { type: "checkbox", checked: isPromptCustomizationEnabled, onChange: (e) => {
|
|
207
|
-
const isChecked = e.target.checked;
|
|
208
|
-
// Always create a new Map instance to ensure React detects the change
|
|
209
|
-
const newServiceCustomData = new Map();
|
|
210
|
-
if (data.serviceCustomData) {
|
|
211
|
-
data.serviceCustomData.forEach((value, key) => {
|
|
212
|
-
newServiceCustomData.set(key, value);
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
if (isChecked) {
|
|
216
|
-
newServiceCustomData.set("OpenAI", {
|
|
217
|
-
enableCustomPrompt: true,
|
|
218
|
-
customPrompt: "",
|
|
219
|
-
promptCustomizationType: "extend",
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
// Remove the service data when unchecked
|
|
224
|
-
newServiceCustomData.delete("OpenAI");
|
|
154
|
+
const selectedCount = data.targetLanguages.length;
|
|
155
|
+
return (_jsx(WizardStepShell, { fillHeight: true, testId: "language-selection-step", children: _jsxs("div", { className: "mx-auto flex min-h-0 w-full max-w-3xl flex-1 flex-col overflow-hidden rounded-xl border border-border-default bg-white", children: [_jsxs("div", { className: "flex h-[37px] shrink-0 items-center justify-between gap-2 border-b border-border-default bg-neutral-grey-5/50 px-3", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "text-xs font-medium tracking-wider text-neutral-grey-50 ", children: "Target Languages" }), selectedCount > 0 && _jsx(CountBadge, { children: selectedCount })] }), allLanguages.length > 1 && (_jsxs("label", { className: "flex cursor-pointer items-center gap-1.5 text-[11px] font-medium text-neutral-grey-50 transition-colors hover:text-neutral-grey-100", children: [_jsx("input", { type: "checkbox", checked: areAllLanguagesSelected, ref: (input) => {
|
|
156
|
+
if (input) {
|
|
157
|
+
input.indeterminate =
|
|
158
|
+
areSomeLanguagesSelected && !areAllLanguagesSelected;
|
|
225
159
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
}, onChange: handleSelectAllLanguages, className: "h-4 w-4 text-[#9650fb] focus:ring-[#9650fb] border-[var(--color-gray-3)] rounded accent-[#9650fb]", "data-testid": "select-all-languages-checkbox" }), _jsx("span", { className: "ml-2 text-xs text-[var(--color-gray-2)]", children: "Select All" })] }))] }), _jsx("p", { className: "text-xs text-[var(--color-gray-2)] mb-3", children: "Select the languages you want to translate this content into." }), _jsx("div", { className: "border border-[var(--color-gray-3)] rounded-lg min-h-[200px] max-h-64 overflow-y-auto bg-[var(--color-gray-5)]", children: data.translationProviders.length === 0 || allLanguages.length === 0 ? (
|
|
236
|
-
// Loading skeleton
|
|
237
|
-
_jsx("div", { className: "p-3 space-y-2", children: [...Array(4)].map((_, i) => (_jsxs("div", { className: "flex items-center animate-pulse", children: [_jsx("div", { className: "h-4 w-4 bg-[var(--color-gray-3)] rounded" }), _jsx("div", { className: "ml-2 h-4 bg-[var(--color-gray-3)] rounded w-32" }), _jsx("div", { className: "ml-auto h-4 bg-[var(--color-gray-3)] rounded w-16" })] }, i))) })) : (_jsx("div", { className: "p-3 grid grid-cols-1 gap-2", children: allLanguages.map((lang) => {
|
|
238
|
-
const statusLabel = lang.translationStatus?.status || "Not started";
|
|
239
|
-
return (_jsxs("label", { className: "flex items-start gap-2 py-1.5 px-2 rounded-md hover:bg-[var(--color-gray-4)] transition-colors cursor-pointer", children: [_jsx("input", { type: "checkbox", checked: languageSelection[lang.code] || false, onChange: () => handleLanguageToggle(lang.code), className: "mt-0.5 h-4 w-4 shrink-0 text-[#9650fb] focus:ring-[#9650fb] border-[var(--color-gray-3)] rounded accent-[#9650fb]", "data-testid": `language-checkbox-${lang.code}` }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "text-sm text-[var(--color-dark)]", children: [lang.name, " (", lang.code, ")"] }), _jsxs("div", { className: "mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-[var(--color-gray-2)]", children: [lang.sourceLanguage && _jsxs("span", { children: ["from: ", lang.sourceLanguage] }), !lang.hasVersions && (_jsx("span", { className: "text-2xs text-orange-600 bg-orange-100 px-2 py-1 rounded-full font-medium", children: "No versions" })), lang.hasVersions && (_jsx("span", { title: lang.translationStatus?.message, className: `text-2xs px-2 py-1 rounded-full font-medium ${getTranslationStatusBadgeClass(statusLabel)}`, "data-testid": `translation-status-badge-${lang.code}`, children: statusLabel }))] })] })] }, lang.code));
|
|
240
|
-
}) })) })] })] }) }));
|
|
160
|
+
}, onChange: handleSelectAllLanguages, className: "h-3 w-3 rounded border-border-default text-[var(--color-highlight-100)] accent-[var(--color-highlight-100)] focus:ring-[var(--color-highlight-100)]", "data-testid": "select-all-languages-checkbox" }), "Select all"] }))] }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto p-2", children: allLanguages.length === 0 ? (_jsx("div", { className: "grid gap-2 p-1 sm:grid-cols-2", children: [...Array(6)].map((_, i) => (_jsxs("div", { className: "flex animate-pulse items-center gap-2 rounded-lg border border-border-default px-3 py-2", children: [_jsx("div", { className: "h-3.5 w-3.5 rounded bg-neutral-grey-10" }), _jsx("div", { className: "h-4 w-4 rounded-sm bg-neutral-grey-10" }), _jsx("div", { className: "h-3.5 w-24 rounded bg-neutral-grey-10" }), _jsx("div", { className: "ml-auto h-3 w-10 rounded bg-neutral-grey-5" })] }, i))) })) : (_jsx("div", { className: "grid gap-1.5 sm:grid-cols-2", children: allLanguages.map((lang) => {
|
|
161
|
+
const checked = !!languageSelection[lang.code];
|
|
162
|
+
return (_jsxs("label", { className: `group flex cursor-pointer items-center gap-2.5 rounded-lg border px-2 py-1.5 transition-all ${checked
|
|
163
|
+
? "border-primary/30 bg-primary/5 ring-primary/10 ring-1"
|
|
164
|
+
: "border-border-default hover:border-border-default hover:bg-neutral-grey-5"}`, children: [_jsx("input", { type: "checkbox", checked: checked, onChange: () => handleLanguageToggle(lang.code), className: "h-3.5 w-3.5 shrink-0 rounded border-border-default text-[var(--color-highlight-100)] accent-[var(--color-highlight-100)] focus:ring-[var(--color-highlight-100)]", "data-testid": `language-checkbox-${lang.code}` }), lang.icon ? (_jsx("img", { src: lang.icon, alt: "", "aria-hidden": "true", className: "h-4 w-5 shrink-0 rounded-sm object-cover" })) : (_jsx("span", { className: "inline-block h-4 w-5 shrink-0 rounded-sm bg-neutral-grey-5" })), _jsx("span", { className: `min-w-0 flex-1 truncate text-[13px] ${checked
|
|
165
|
+
? "font-medium text-neutral-grey-100"
|
|
166
|
+
: "text-neutral-grey-100"}`, children: lang.name }), _jsx("span", { className: "shrink-0 font-mono text-[10px] tracking-wider text-neutral-grey-50 ", children: lang.code })] }, lang.code));
|
|
167
|
+
}) })) })] }) }));
|
|
241
168
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
type WizardStepShellProps = {
|
|
3
|
+
/** Right-aligned content above the body (badges, counts, actions). */
|
|
4
|
+
meta?: ReactNode;
|
|
5
|
+
/** When true, the body section flex-grows and content can overflow internally. */
|
|
6
|
+
fillHeight?: boolean;
|
|
7
|
+
testId?: string;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Body-only scaffold for translation-wizard steps. Step title/description
|
|
12
|
+
* live in the wizard's stepper bar, so this just gives a consistent
|
|
13
|
+
* padding + optional meta row + flex/scroll behavior for the content.
|
|
14
|
+
*/
|
|
15
|
+
export declare function WizardStepShell({ meta, fillHeight, testId, children, }: WizardStepShellProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=WizardStepShell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WizardStepShell.d.ts","sourceRoot":"","sources":["../../src/steps/WizardStepShell.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,KAAK,oBAAoB,GAAG;IAC1B,sEAAsE;IACtE,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,kFAAkF;IAClF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,UAAkB,EAClB,MAAM,EACN,QAAQ,GACT,EAAE,oBAAoB,2CAiBtB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Body-only scaffold for translation-wizard steps. Step title/description
|
|
4
|
+
* live in the wizard's stepper bar, so this just gives a consistent
|
|
5
|
+
* padding + optional meta row + flex/scroll behavior for the content.
|
|
6
|
+
*/
|
|
7
|
+
export function WizardStepShell({ meta, fillHeight = false, testId, children, }) {
|
|
8
|
+
return (_jsxs("div", { className: "flex h-full flex-col gap-4 p-6", "data-testid": testId, children: [meta && (_jsx("div", { className: "flex items-center justify-end gap-2", children: meta })), _jsx("div", { className: fillHeight
|
|
9
|
+
? "flex min-h-0 flex-1 flex-col gap-4"
|
|
10
|
+
: "flex flex-col gap-5", children: children })] }));
|
|
11
|
+
}
|
package/dist/steps/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FullItem, ItemDescriptor, EditContextType } from "@parhelia/core";
|
|
1
|
+
import { FullItem, ItemDescriptor, EditContextType, ItemWithSubtree, SubitemCountState } from "@parhelia/core";
|
|
2
2
|
export type TranslationProviderInfo = {
|
|
3
3
|
name: string;
|
|
4
4
|
displayName: string;
|
|
@@ -13,6 +13,7 @@ export type TranslationStatus = {
|
|
|
13
13
|
hash?: string;
|
|
14
14
|
message?: string;
|
|
15
15
|
batchId?: string;
|
|
16
|
+
totalCost?: number | null;
|
|
16
17
|
};
|
|
17
18
|
export type LanguageData = {
|
|
18
19
|
name: string;
|
|
@@ -25,6 +26,8 @@ export type TranslationDialogResult = {
|
|
|
25
26
|
targetLanguages: string[];
|
|
26
27
|
includeSubitems: boolean;
|
|
27
28
|
discoveredItems: FullItem[];
|
|
29
|
+
selectionTreeItems?: ItemWithSubtree[];
|
|
30
|
+
batchName?: string;
|
|
28
31
|
metadata?: any;
|
|
29
32
|
batchId?: string;
|
|
30
33
|
};
|
|
@@ -38,8 +41,21 @@ export type TranslationWizardData = {
|
|
|
38
41
|
items: FullItem[];
|
|
39
42
|
targetLanguages: string[];
|
|
40
43
|
translationProvider: string;
|
|
44
|
+
/** Legacy flag: true when ANY item in selectionTreeItems has includeSubitems on. */
|
|
41
45
|
includeSubitems: boolean;
|
|
46
|
+
/** Flattened list of items to translate (root selections + expanded subtrees). */
|
|
42
47
|
discoveredItems: FullItem[];
|
|
48
|
+
/** User's tree selection with per-item subitem flags. Authoritative for the UI. */
|
|
49
|
+
selectionTreeItems?: ItemWithSubtree[];
|
|
50
|
+
/**
|
|
51
|
+
* Live subitem counts streamed from the backend, keyed by item id.
|
|
52
|
+
* Populated by ItemSelectionTree so later steps (e.g. the Start
|
|
53
|
+
* Translation button) can show a running total with a "+" while
|
|
54
|
+
* streams are still in flight.
|
|
55
|
+
*/
|
|
56
|
+
subitemCounts?: Record<string, SubitemCountState>;
|
|
57
|
+
/** User-editable batch name (AI-suggested initially). */
|
|
58
|
+
batchName?: string;
|
|
43
59
|
languageData: Map<string, LanguageData>;
|
|
44
60
|
translationProviders: TranslationProviderInfo[];
|
|
45
61
|
itemMetadata: Map<string, Map<string, string>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/steps/types.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/steps/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TranslationBatches.d.ts","sourceRoot":"","sources":["../../src/translation-center/TranslationBatches.tsx"],"names":[],"mappings":"AAwPA,wBAAgB,kBAAkB,4CA6uCjC"}
|