@parhelia/localization 0.1.12910 → 0.1.12912
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/LocalizeItemDialog.d.ts.map +1 -1
- package/dist/LocalizeItemDialog.js +17 -9
- package/dist/LocalizeItemUtils.d.ts.map +1 -1
- package/dist/LocalizeItemUtils.js +8 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -9
- package/dist/steps/ItemSelectionStep.js +1 -1
- package/dist/steps/ItemSelectionTree.d.ts.map +1 -1
- package/dist/steps/ItemSelectionTree.js +6 -1
- package/dist/steps/ProviderStep.d.ts +3 -0
- package/dist/steps/ProviderStep.d.ts.map +1 -0
- package/dist/steps/ProviderStep.js +223 -0
- package/dist/steps/ServiceLanguageSelectionStep.d.ts.map +1 -1
- package/dist/steps/ServiceLanguageSelectionStep.js +11 -6
- package/dist/steps/WizardStepShell.d.ts +3 -1
- package/dist/steps/WizardStepShell.d.ts.map +1 -1
- package/dist/steps/WizardStepShell.js +4 -2
- package/dist/translation-center/TranslationBatches.d.ts +2 -0
- package/dist/translation-center/TranslationBatches.d.ts.map +1 -1
- package/dist/translation-center/TranslationBatches.js +541 -234
- package/dist/translation-center/TranslationsTitlebar.d.ts.map +1 -1
- package/dist/translation-center/TranslationsTitlebar.js +4 -13
- package/package.json +1 -1
- package/dist/steps/PromptCustomizationStep.d.ts +0 -3
- package/dist/steps/PromptCustomizationStep.d.ts.map +0 -1
- package/dist/steps/PromptCustomizationStep.js +0 -257
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalizeItemDialog.d.ts","sourceRoot":"","sources":["../src/LocalizeItemDialog.tsx"],"names":[],"mappings":"AA8BA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EAExB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"LocalizeItemDialog.d.ts","sourceRoot":"","sources":["../src/LocalizeItemDialog.tsx"],"names":[],"mappings":"AA8BA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EAExB,MAAM,eAAe,CAAC;AAmEvB,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,uBAAuB,GAAG,WAAW,CAAC,uBAAuB,CAAC,2CA4gBtE"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Dialog, DialogContent, StyledDialogTitle, Button, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@parhelia/core";
|
|
2
|
+
import { Dialog, DialogContent, StyledDialogTitle, Button, cn, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@parhelia/core";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
|
|
4
4
|
import { AlertTriangle as LucideAlertTriangle, ArrowRight as LucideArrowRight, Globe as LucideGlobe, } from "lucide-react";
|
|
5
5
|
import { useTranslationWizard } from "./hooks/useTranslationWizard";
|
|
@@ -12,9 +12,17 @@ const LARGE_BATCH_WARNING_THRESHOLD = 100;
|
|
|
12
12
|
const getNextButtonLabel = (step) => step?.nextButtonLabel || step?.name || "Next";
|
|
13
13
|
// Note: DialogButtons is an internal component that might need to be added to core exports
|
|
14
14
|
// For now, we'll implement it inline
|
|
15
|
-
const DialogButtons = ({ children, ...props }) => (_jsx("div", { className: "mt-auto flex shrink-0 items-center gap-3 border-t border-border-default bg-white px-
|
|
15
|
+
const DialogButtons = ({ children, ...props }) => (_jsx("div", { className: "mt-auto flex shrink-0 items-center gap-3 border-t border-border-default bg-white px-4 py-4 md:px-6", ...props, children: children }));
|
|
16
|
+
const StepIndicatorDots = ({ steps, currentStepIndex, }) => {
|
|
17
|
+
if (steps.length <= 1)
|
|
18
|
+
return null;
|
|
19
|
+
return (_jsx("div", { className: "pointer-events-none absolute top-full left-1/2 mt-8 flex -translate-x-1/2 justify-center gap-3", "aria-hidden": "true", "data-testid": "translation-wizard-step-dots", children: steps.map((step, index) => (_jsx("div", { className: cn("h-1.5 rounded-full transition-all duration-300", index === currentStepIndex
|
|
20
|
+
? "w-8 bg-white"
|
|
21
|
+
: "w-1.5 bg-neutral-grey-10") }, step.id ?? step.name ?? index))) }));
|
|
22
|
+
};
|
|
16
23
|
export function LocalizeItemDialog(props) {
|
|
17
24
|
const editContext = props.editContext;
|
|
25
|
+
const isMobile = editContext?.isMobile ?? false;
|
|
18
26
|
const configuration = editContext.configuration.translationWizard;
|
|
19
27
|
const [currentStepIndex, setCurrentStepIndex] = useState(0);
|
|
20
28
|
const [stepCompleted, setStepCompleted] = useState(-1);
|
|
@@ -53,7 +61,7 @@ export function LocalizeItemDialog(props) {
|
|
|
53
61
|
}, [props.onClose]);
|
|
54
62
|
// Memoize activeSteps to prevent unnecessary recalculations and new object references
|
|
55
63
|
// This prevents infinite loops when wizardData changes but skip conditions don't
|
|
56
|
-
// The skip condition for
|
|
64
|
+
// The skip condition for the provider step checks serviceCustomData and translationProvider
|
|
57
65
|
const activeSteps = useMemo(() => {
|
|
58
66
|
const steps = configuration.steps.filter((step) => !step.skipCondition || !step.skipCondition(wizardData));
|
|
59
67
|
return steps;
|
|
@@ -233,7 +241,7 @@ export function LocalizeItemDialog(props) {
|
|
|
233
241
|
itemsCount: newData.items?.length || 0,
|
|
234
242
|
serviceCustomData: serviceCustomDataKey,
|
|
235
243
|
metadata: metadataKey,
|
|
236
|
-
// batchName must be part of the key — otherwise the
|
|
244
|
+
// batchName must be part of the key — otherwise the ProviderStep's
|
|
237
245
|
// setData({...prev, batchName}) call sees an "unchanged" key here and the
|
|
238
246
|
// update is silently dropped, so the user-entered name never reaches submit.
|
|
239
247
|
batchName: newData.batchName ?? "",
|
|
@@ -279,9 +287,9 @@ export function LocalizeItemDialog(props) {
|
|
|
279
287
|
if (!open) {
|
|
280
288
|
props.onClose?.(null);
|
|
281
289
|
}
|
|
282
|
-
}, children: [_jsxs(DialogContent, { className: "flex
|
|
283
|
-
width: "min(90vw, 1280px)",
|
|
284
|
-
height: "min(85vh, 900px)",
|
|
290
|
+
}, children: [_jsxs(DialogContent, { className: "flex max-h-[900px] min-h-0 max-w-5xl flex-col overflow-visible md:min-h-[700px]", "data-testid": "translation-wizard-dialog", onPointerDownOutside: (e) => e.preventDefault(), onEscapeKeyDown: (e) => e.preventDefault(), "aria-describedby": "translation-wizard-description", style: {
|
|
291
|
+
width: isMobile ? "calc(100vw - 24px)" : "min(90vw, 1280px)",
|
|
292
|
+
height: isMobile ? "min(92dvh, 900px)" : "min(85vh, 900px)",
|
|
285
293
|
}, children: [_jsx(StyledDialogTitle, { icon: _jsx(GlobeIcon, { strokeWidth: 1.5 }), title: `Translate · ${currentStep?.name ?? ""}`, subtitle: currentStep?.description ||
|
|
286
294
|
"Configure and start translation for your content" }), _jsx("div", { id: "translation-wizard-description", className: "sr-only", children: currentStep?.description ||
|
|
287
295
|
"Configure and start translation for your content." }), _jsxs("div", { className: "flex flex-1 flex-col min-h-0", children: [_jsx("div", { className: "flex-1 overflow-y-auto min-h-0 ", "data-testid": "translation-wizard-step-content", children: _jsx("div", { className: "h-full relative", children: activeSteps.map((step, index) => {
|
|
@@ -290,12 +298,12 @@ export function LocalizeItemDialog(props) {
|
|
|
290
298
|
if (!StepComponent)
|
|
291
299
|
return null;
|
|
292
300
|
return (_jsx("div", { className: "h-full", style: { display: isActive ? "block" : "none" }, "aria-hidden": !isActive, "data-testid": `step-content-${step.id}`, children: _jsx(StepComponent, { stepIndex: index, isActive: isActive, data: wizardData, setData: setWizardDataStable, editContext: editContext, onStepCompleted: (completed) => handleStepCompleted(completed, index), setBeforeNextCallback: isActive ? setBeforeNextCallbackStable : undefined, setFooterActions: isActive ? provideFooterActions : undefined, requestClose: isActive ? requestCloseCb : undefined }) }, step.id));
|
|
293
|
-
}) }) }), _jsxs(DialogButtons, { "data-testid": "translation-wizard-dialog-buttons", children: [_jsx(Button, { onClick: () => props.onClose?.(null), variant: "
|
|
301
|
+
}) }) }), _jsxs(DialogButtons, { "data-testid": "translation-wizard-dialog-buttons", children: [_jsx(Button, { onClick: () => props.onClose?.(null), variant: "ghost", size: "lg", className: "w-auto min-w-24 flex-none", "data-testid": "translation-wizard-cancel-button", children: "Cancel" }), _jsxs("div", { className: "ml-auto flex min-w-0 flex-wrap items-center justify-end gap-3", children: [currentStepIndex > 0 && (_jsx(Button, { onClick: handlePrevious, variant: "outline", size: "lg", className: "w-auto min-w-32 flex-none", "data-testid": "translation-wizard-previous-button", children: "Previous" })), footerActions.map((a) => (_jsx(Button, { onClick: a.onClick, disabled: !!a.disabled, variant: "default", size: "lg", className: "w-auto min-w-32 flex-none", "data-testid": `translation-wizard-footer-action-${a.key}`, children: a.label }, a.key))), _jsx(Button, { onClick: handleNext, disabled: !canProceed || isSubmitting, variant: "default", size: "lg", className: "w-auto min-w-40 flex-none gap-2", "data-testid": "translation-wizard-next-button", children: isSubmitting ? ("Starting...") : isLastStep ? ((() => {
|
|
294
302
|
const { totalTranslations, isStreaming } = calculateTranslationCounts(wizardData);
|
|
295
303
|
if (totalTranslations <= 0)
|
|
296
304
|
return "Start Translation";
|
|
297
305
|
return `Start Translation (${totalTranslations}${isStreaming ? "+" : ""})`;
|
|
298
|
-
})()) : (_jsxs(_Fragment, { children: [getNextButtonLabel(activeSteps[currentStepIndex + 1]), _jsx(ArrowRightIcon, { className: "h-4 w-4", strokeWidth: 2 })] })) })] })] })] })] }), _jsx(AlertDialog, { open: showLargeBatchWarning, onOpenChange: setShowLargeBatchWarning, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsxs(AlertDialogTitle, { className: "flex items-center gap-2", children: [_jsx(AlertTriangleIcon, { className: "h-5 w-5 text-feedback-orange" }), "Large Translation Batch"] }), _jsx(AlertDialogDescription, { children: (() => {
|
|
306
|
+
})()) : (_jsxs(_Fragment, { children: [getNextButtonLabel(activeSteps[currentStepIndex + 1]), _jsx(ArrowRightIcon, { className: "h-4 w-4", strokeWidth: 2 })] })) })] })] })] }), _jsx(StepIndicatorDots, { steps: activeSteps, currentStepIndex: currentStepIndex })] }), _jsx(AlertDialog, { open: showLargeBatchWarning, onOpenChange: setShowLargeBatchWarning, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsxs(AlertDialogTitle, { className: "flex items-center gap-2", children: [_jsx(AlertTriangleIcon, { className: "h-5 w-5 text-feedback-orange" }), "Large Translation Batch"] }), _jsx(AlertDialogDescription, { children: (() => {
|
|
299
307
|
const { itemCount, languageCount, totalTranslations } = calculateTranslationCounts(wizardData);
|
|
300
308
|
return (_jsxs(_Fragment, { children: ["You are about to start", " ", _jsxs("strong", { children: [totalTranslations, " translations"] }), " (", itemCount, " items \u00D7 ", languageCount, " languages). Large batches may take a significant amount of time to process.", _jsx("br", {}), _jsx("br", {}), "Are you sure you want to continue?"] }));
|
|
301
309
|
})() })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: isSubmitting, children: "Cancel" }), _jsx(AlertDialogAction, { onClick: handleLargeBatchConfirm, disabled: isSubmitting, children: isSubmitting ? "Starting..." : "Continue" })] })] }) })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalizeItemUtils.d.ts","sourceRoot":"","sources":["../src/LocalizeItemUtils.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAsDjD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAIF,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,qBAAqB,EACjC,WAAW,EAAE,eAAe,GAC3B,OAAO,CAAC,iBAAiB,CAAC,
|
|
1
|
+
{"version":3,"file":"LocalizeItemUtils.d.ts","sourceRoot":"","sources":["../src/LocalizeItemUtils.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAsDjD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAIF,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,qBAAqB,EACjC,WAAW,EAAE,eAAe,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAwH5B;AAGD,eAAO,MAAM,mBAAmB,GAC9B,eAAe,MAAM,EAAE,EACvB,mBAAmB,iBAAiB,EAAE,EACtC,WAAW,MAAM,EACjB,MAAM,QAAQ,EACd,qBAAqB,MAAM,+BAkC5B,CAAC"}
|
|
@@ -86,29 +86,15 @@ export async function performDefaultTranslation(wizardData, editContext) {
|
|
|
86
86
|
if (wizardData.serviceCustomData && wizardData.serviceCustomData.size > 0) {
|
|
87
87
|
const serviceData = wizardData.serviceCustomData.get(wizardData.translationProvider);
|
|
88
88
|
if (serviceData && serviceData.enableCustomPrompt) {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
let finalCustomPrompt = "";
|
|
98
|
-
if (rawCustomPrompt) {
|
|
99
|
-
if (defaultPrompt && customizationType === "extend") {
|
|
100
|
-
finalCustomPrompt = `${defaultPrompt}\n\n${rawCustomPrompt}`;
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
finalCustomPrompt = rawCustomPrompt;
|
|
104
|
-
}
|
|
89
|
+
const customPrompt = (serviceData.customPrompt || "").trim();
|
|
90
|
+
if (customPrompt) {
|
|
91
|
+
metadataObj.serviceCustomData = {
|
|
92
|
+
[wizardData.translationProvider]: {
|
|
93
|
+
enableCustomPrompt: serviceData.enableCustomPrompt,
|
|
94
|
+
customPrompt,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
105
97
|
}
|
|
106
|
-
metadataObj.serviceCustomData = {
|
|
107
|
-
[wizardData.translationProvider]: {
|
|
108
|
-
enableCustomPrompt: serviceData.enableCustomPrompt,
|
|
109
|
-
customPrompt: finalCustomPrompt,
|
|
110
|
-
},
|
|
111
|
-
};
|
|
112
98
|
}
|
|
113
99
|
}
|
|
114
100
|
// Serialize metadata to JSON string (or undefined if empty)
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AAoBpF,OAAO,EACL,mBAAmB,EAGpB,MAAM,gBAAgB,CAAC;AAWxB,QAAA,MAAM,wCAAwC;;;;;;;;;;;;;;CAuB7C,CAAC;AAEF,QAAA,MAAM,4CAA4C;;;;;;;;CAiBjD,CAAC;AAoDF,OAAO,EACL,wCAAwC,EACxC,4CAA4C,GAC7C,CAAC;AACF,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,YAAY,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEhF,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,mBAAmB,EAClC,OAAO,CAAC,EAAE,yBAAyB,uBAmIpC"}
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { ItemSelectionStep } from "./steps/ItemSelectionStep";
|
|
3
3
|
import { ServiceLanguageSelectionStep } from "./steps/ServiceLanguageSelectionStep";
|
|
4
|
-
import {
|
|
4
|
+
import { ProviderStep } from "./steps/ProviderStep";
|
|
5
5
|
import { localizeItemCommand } from "./LocalizeItemCommand";
|
|
6
6
|
import { TranslationSidebar } from "./sidebar/TranslationSidebar";
|
|
7
7
|
import { TranslationManagement } from "./translation-center/TranslationManagement";
|
|
8
8
|
import { TranslationsTitlebar } from "./translation-center/TranslationsTitlebar";
|
|
9
|
-
import { TranslationBatches } from "./translation-center/TranslationBatches";
|
|
9
|
+
import { TranslationBatches, TranslationBatchFiltersSidebar, TRANSLATION_BATCH_FILTERS_SIDEBAR_ID, } from "./translation-center/TranslationBatches";
|
|
10
10
|
import { TranslationServicesPanel } from "./settings/TranslationServicesPanel";
|
|
11
|
-
import { Languages as LucideLanguages, LayoutGrid as LucideLayoutGrid, Globe as LucideGlobe, } from "lucide-react";
|
|
11
|
+
import { Languages as LucideLanguages, LayoutGrid as LucideLayoutGrid, Globe as LucideGlobe, ListChecks as LucideListChecks, } from "lucide-react";
|
|
12
12
|
const LanguagesIcon = LucideLanguages;
|
|
13
13
|
const LayoutGridIcon = LucideLayoutGrid;
|
|
14
14
|
const GlobeIcon = LucideGlobe;
|
|
15
|
+
const ListChecksIcon = LucideListChecks;
|
|
15
16
|
const DEFAULT_TRANSLATION_WIZARD_CONFIGURATION = {
|
|
16
17
|
steps: [
|
|
17
18
|
{
|
|
@@ -32,7 +33,7 @@ const DEFAULT_TRANSLATION_WIZARD_CONFIGURATION = {
|
|
|
32
33
|
name: "Provider",
|
|
33
34
|
nextButtonLabel: "Select Provider",
|
|
34
35
|
description: "Choose the translation provider and provider settings.",
|
|
35
|
-
component:
|
|
36
|
+
component: ProviderStep,
|
|
36
37
|
},
|
|
37
38
|
],
|
|
38
39
|
};
|
|
@@ -50,7 +51,7 @@ const SINGLE_ITEM_TRANSLATION_WIZARD_CONFIGURATION = {
|
|
|
50
51
|
name: "Provider",
|
|
51
52
|
nextButtonLabel: "Select Provider",
|
|
52
53
|
description: "Choose the translation provider and provider settings.",
|
|
53
|
-
component:
|
|
54
|
+
component: ProviderStep,
|
|
54
55
|
},
|
|
55
56
|
],
|
|
56
57
|
};
|
|
@@ -72,6 +73,22 @@ const translateSidebar = {
|
|
|
72
73
|
},
|
|
73
74
|
],
|
|
74
75
|
};
|
|
76
|
+
const translationBatchFiltersSidebar = {
|
|
77
|
+
id: TRANSLATION_BATCH_FILTERS_SIDEBAR_ID,
|
|
78
|
+
title: "Translation Batches",
|
|
79
|
+
icon: _jsx(ListChecksIcon, { strokeWidth: 1 }),
|
|
80
|
+
position: "left",
|
|
81
|
+
sortOrder: 10,
|
|
82
|
+
defaultWidth: 320,
|
|
83
|
+
panels: [
|
|
84
|
+
{
|
|
85
|
+
name: "translation-batch-filters",
|
|
86
|
+
title: "Translation Batches",
|
|
87
|
+
content: _jsx(TranslationBatchFiltersSidebar, {}),
|
|
88
|
+
initialSize: 100,
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
75
92
|
/**
|
|
76
93
|
* Translation Management workspace definition
|
|
77
94
|
*/
|
|
@@ -81,7 +98,8 @@ const translationManagementWorkspace = {
|
|
|
81
98
|
icon: _jsx(GlobeIcon, { strokeWidth: 1 }),
|
|
82
99
|
component: _jsx(TranslationManagement, {}),
|
|
83
100
|
titlebar: _jsx(TranslationsTitlebar, {}),
|
|
84
|
-
supportsSidebars:
|
|
101
|
+
supportsSidebars: true,
|
|
102
|
+
defaultSidebars: [TRANSLATION_BATCH_FILTERS_SIDEBAR_ID],
|
|
85
103
|
sortOrder: 30,
|
|
86
104
|
};
|
|
87
105
|
export { DEFAULT_TRANSLATION_WIZARD_CONFIGURATION, SINGLE_ITEM_TRANSLATION_WIZARD_CONFIGURATION, };
|
|
@@ -133,13 +151,21 @@ export function configureLocalization(configuration, options) {
|
|
|
133
151
|
if (!hasLocalizeCommand) {
|
|
134
152
|
configuration.commands.allItemCommands.push(localizeItemCommand);
|
|
135
153
|
}
|
|
136
|
-
// Register
|
|
154
|
+
// Register localization sidebars
|
|
137
155
|
const sidebars = configuration.editor?.sidebars || [];
|
|
138
156
|
const hasTranslateSidebar = sidebars.some((s) => s.id === "translate");
|
|
139
|
-
|
|
157
|
+
const hasTranslationBatchFiltersSidebar = sidebars.some((s) => s.id === TRANSLATION_BATCH_FILTERS_SIDEBAR_ID);
|
|
158
|
+
if (!hasTranslateSidebar || !hasTranslationBatchFiltersSidebar) {
|
|
159
|
+
const nextSidebars = [...sidebars];
|
|
160
|
+
if (!hasTranslateSidebar) {
|
|
161
|
+
nextSidebars.push(translateSidebar);
|
|
162
|
+
}
|
|
163
|
+
if (!hasTranslationBatchFiltersSidebar) {
|
|
164
|
+
nextSidebars.push(translationBatchFiltersSidebar);
|
|
165
|
+
}
|
|
140
166
|
configuration.editor = {
|
|
141
167
|
...configuration.editor,
|
|
142
|
-
sidebars:
|
|
168
|
+
sidebars: nextSidebars,
|
|
143
169
|
};
|
|
144
170
|
}
|
|
145
171
|
// Add Translation Management workspace if enabled
|
|
@@ -20,5 +20,5 @@ export function ItemSelectionStep({ isActive = true, data, setData, onStepComple
|
|
|
20
20
|
onStepCompletedRef.current(completed);
|
|
21
21
|
}
|
|
22
22
|
}, [isActive, itemSelectionValid, multiItemEnabled]);
|
|
23
|
-
return (_jsx(WizardStepShell, { fillHeight: true, testId: "item-selection-step", children: _jsx(ItemSelectionTree, { data: data, setData: setData, editContext: editContext, isActive: isActive, onSelectionValidChange: setItemSelectionValid, height: "100%" }) }));
|
|
23
|
+
return (_jsx(WizardStepShell, { fillHeight: true, noPadding: true, testId: "item-selection-step", children: _jsx(ItemSelectionTree, { data: data, setData: setData, editContext: editContext, isActive: isActive, onSelectionValidChange: setItemSelectionValid, height: "100%" }) }));
|
|
24
24
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ItemSelectionTree.d.ts","sourceRoot":"","sources":["../../src/steps/ItemSelectionTree.tsx"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,eAAe,EAMrB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAGrD,KAAK,sBAAsB,GAAG;IAC5B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,OAAO,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC/C,WAAW,EAAE,eAAe,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B,CAAC;
|
|
1
|
+
{"version":3,"file":"ItemSelectionTree.d.ts","sourceRoot":"","sources":["../../src/steps/ItemSelectionTree.tsx"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,eAAe,EAMrB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAGrD,KAAK,sBAAsB,GAAG;IAC5B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,OAAO,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC/C,WAAW,EAAE,eAAe,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B,CAAC;AAsEF,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,OAAO,EACP,WAAW,EACX,QAAe,EACf,sBAAsB,EACtB,MAAY,GACb,EAAE,sBAAsB,2CAiWxB"}
|
|
@@ -49,6 +49,8 @@ function fullItemToWithSubtree(item, includeSubitems) {
|
|
|
49
49
|
name: item.descriptor?.name ?? item.name,
|
|
50
50
|
displayName: item.descriptor?.displayName ?? item.displayName,
|
|
51
51
|
path: item.descriptor?.path ?? item.path,
|
|
52
|
+
icon: item.descriptor?.icon ??
|
|
53
|
+
item.icon,
|
|
52
54
|
},
|
|
53
55
|
includeSubitems,
|
|
54
56
|
};
|
|
@@ -263,6 +265,7 @@ export function ItemSelectionTree({ data, setData, editContext, isActive = true,
|
|
|
263
265
|
for (const node of source) {
|
|
264
266
|
if (!node.id || existing.has(node.id))
|
|
265
267
|
continue;
|
|
268
|
+
const nodeWithIcon = node;
|
|
266
269
|
cacheTreeNode(node);
|
|
267
270
|
next.push({
|
|
268
271
|
descriptor: {
|
|
@@ -272,6 +275,7 @@ export function ItemSelectionTree({ data, setData, editContext, isActive = true,
|
|
|
272
275
|
name: node.name,
|
|
273
276
|
displayName: node.displayName,
|
|
274
277
|
path: node.path,
|
|
278
|
+
icon: nodeWithIcon.icon,
|
|
275
279
|
},
|
|
276
280
|
includeSubitems: false,
|
|
277
281
|
});
|
|
@@ -297,6 +301,7 @@ export function ItemSelectionTree({ data, setData, editContext, isActive = true,
|
|
|
297
301
|
name: item.name,
|
|
298
302
|
displayName: item.displayName,
|
|
299
303
|
path: item.path,
|
|
304
|
+
icon: item.icon,
|
|
300
305
|
},
|
|
301
306
|
includeSubitems: false,
|
|
302
307
|
},
|
|
@@ -323,5 +328,5 @@ export function ItemSelectionTree({ data, setData, editContext, isActive = true,
|
|
|
323
328
|
setItems(next);
|
|
324
329
|
commitToWizard(next);
|
|
325
330
|
}, [items, commitToWizard]);
|
|
326
|
-
return (_jsx(ItemCollectionEditor, { items: items, selectedInTree: selectedInTree, onSelectedInTreeChange: setSelectedInTree, selectedFromList: selectedFromList, onSelectedFromListChange: setSelectedFromList, onAddToList: handleAddToList, onRemoveFromList: handleRemoveFromList, onAddItem: handleAddItem, onRemoveItem: handleRemoveItem, onToggleSubitems: handleToggleSubitems, language: language, rootItemIds: [SITECORE_ROOT], selectedItemsLabel: "Items to Translate", emptyMessage: "No items selected", emptyHint: "Browse or search to add items", height: height, subitemCounts: subitemCounts, expandIdPath: expandIdPath, localStorageKey: "translation-wizard.itemCollectionSplitter" }));
|
|
331
|
+
return (_jsx(ItemCollectionEditor, { items: items, selectedInTree: selectedInTree, onSelectedInTreeChange: setSelectedInTree, selectedFromList: selectedFromList, onSelectedFromListChange: setSelectedFromList, onAddToList: handleAddToList, onRemoveFromList: handleRemoveFromList, onAddItem: handleAddItem, onRemoveItem: handleRemoveItem, onToggleSubitems: handleToggleSubitems, language: language, rootItemIds: [SITECORE_ROOT], selectedItemsLabel: "Items to Translate", emptyMessage: "No items selected", emptyHint: "Browse or search to add items", height: height, subitemCounts: subitemCounts, expandIdPath: expandIdPath, localStorageKey: "translation-wizard.itemCollectionSplitter", treeSearchContainerClassName: "border-b-0 p-0 pb-3", frameless: true }));
|
|
327
332
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProviderStep.d.ts","sourceRoot":"","sources":["../../src/steps/ProviderStep.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,SAAS,CAAC;AAQtE,wBAAgB,YAAY,CAAC,EAC3B,QAAe,EACf,IAAI,EACJ,OAAO,EACP,eAAe,EACf,WAAW,GACZ,EAAE,oBAAoB,2CAqXtB"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { Button, Input, Label, Select, Spinner, Textarea, } from "@parhelia/core";
|
|
4
|
+
import { RotateCcw } from "lucide-react";
|
|
5
|
+
import { WizardStepShell } from "./WizardStepShell";
|
|
6
|
+
import { suggestBatchName } from "../api/discovery";
|
|
7
|
+
const PROMPT_CUSTOMIZATION_TYPE = "replace";
|
|
8
|
+
const ResetIcon = (props) => React.createElement(RotateCcw, props);
|
|
9
|
+
export function ProviderStep({ isActive = true, data, setData, onStepCompleted, editContext, }) {
|
|
10
|
+
const [customPrompt, setCustomPrompt] = useState("");
|
|
11
|
+
// Local mirror of the wizard's batch name so typing is responsive.
|
|
12
|
+
const [batchName, setBatchName] = useState(data.batchName ?? "");
|
|
13
|
+
const [isSuggesting, setIsSuggesting] = useState(false);
|
|
14
|
+
// Whether the user has manually edited the name. If so, we never overwrite
|
|
15
|
+
// it with a fresh AI suggestion.
|
|
16
|
+
const userTouchedNameRef = useRef(!!data.batchName);
|
|
17
|
+
const dataRef = useRef(data);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
dataRef.current = data;
|
|
20
|
+
}, [data]);
|
|
21
|
+
// Push name changes back into wizard data (debounced via simple equality).
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if ((dataRef.current.batchName ?? "") === batchName)
|
|
24
|
+
return;
|
|
25
|
+
setData({ ...dataRef.current, batchName });
|
|
26
|
+
}, [batchName, setData]);
|
|
27
|
+
// Auto-suggest a name when the step becomes active and the user hasn't
|
|
28
|
+
// already provided/edited one. Fires once per item+language combination so
|
|
29
|
+
// navigating away and back doesn't re-fetch.
|
|
30
|
+
const lastSuggestionKeyRef = useRef("");
|
|
31
|
+
const sessionId = editContext?.sessionId;
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!isActive)
|
|
34
|
+
return;
|
|
35
|
+
if (userTouchedNameRef.current)
|
|
36
|
+
return;
|
|
37
|
+
const itemIds = (data.selectionTreeItems && data.selectionTreeItems.length > 0
|
|
38
|
+
? data.selectionTreeItems.map((s) => s.descriptor.id)
|
|
39
|
+
: data.items.map((i) => i.descriptor.id)).filter(Boolean);
|
|
40
|
+
const langs = [...data.targetLanguages].sort();
|
|
41
|
+
if (itemIds.length === 0 || langs.length === 0)
|
|
42
|
+
return;
|
|
43
|
+
const key = JSON.stringify({ itemIds: [...itemIds].sort(), langs });
|
|
44
|
+
if (lastSuggestionKeyRef.current === key)
|
|
45
|
+
return;
|
|
46
|
+
lastSuggestionKeyRef.current = key;
|
|
47
|
+
let cancelled = false;
|
|
48
|
+
setIsSuggesting(true);
|
|
49
|
+
void (async () => {
|
|
50
|
+
try {
|
|
51
|
+
const includeSubitems = !!data.selectionTreeItems?.some((s) => s.includeSubitems);
|
|
52
|
+
const name = await suggestBatchName({ itemIds, targetLanguages: langs, includeSubitems }, sessionId);
|
|
53
|
+
if (cancelled || userTouchedNameRef.current)
|
|
54
|
+
return;
|
|
55
|
+
if (name)
|
|
56
|
+
setBatchName(name);
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
if (!cancelled)
|
|
60
|
+
setIsSuggesting(false);
|
|
61
|
+
}
|
|
62
|
+
})();
|
|
63
|
+
return () => {
|
|
64
|
+
cancelled = true;
|
|
65
|
+
};
|
|
66
|
+
}, [
|
|
67
|
+
isActive,
|
|
68
|
+
sessionId,
|
|
69
|
+
data.selectionTreeItems,
|
|
70
|
+
data.items,
|
|
71
|
+
data.targetLanguages,
|
|
72
|
+
]);
|
|
73
|
+
const customPromptRef = useRef(customPrompt);
|
|
74
|
+
const hasInitializedRef = useRef(false);
|
|
75
|
+
const lastProviderRef = useRef(data.translationProvider);
|
|
76
|
+
const updateTimerRef = useRef(null);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
customPromptRef.current = customPrompt;
|
|
79
|
+
}, [customPrompt]);
|
|
80
|
+
const selectedProvider = useMemo(() => data.translationProviders.find((p) => p.name === data.translationProvider), [data.translationProviders, data.translationProvider]);
|
|
81
|
+
const serviceData = useMemo(() => {
|
|
82
|
+
if (!data.serviceCustomData || !data.translationProvider)
|
|
83
|
+
return null;
|
|
84
|
+
return data.serviceCustomData.get(data.translationProvider);
|
|
85
|
+
}, [data.serviceCustomData, data.translationProvider]);
|
|
86
|
+
const enableCustomPrompt = serviceData?.enableCustomPrompt === true;
|
|
87
|
+
const supportsPromptCustomization = data.translationProvider === "AI";
|
|
88
|
+
const isCreateLanguageVersionsOnlyProvider = data.translationProvider === "Create Language Versions Only" ||
|
|
89
|
+
selectedProvider?.displayName === "Create Language Versions Only";
|
|
90
|
+
const defaultPrompt = useMemo(() => {
|
|
91
|
+
const prompt = selectedProvider?.defaultPrompt;
|
|
92
|
+
return prompt && prompt.trim() ? prompt : null;
|
|
93
|
+
}, [selectedProvider?.defaultPrompt]);
|
|
94
|
+
const hasDefaultPrompt = defaultPrompt != null && defaultPrompt.length > 0;
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (lastProviderRef.current !== data.translationProvider) {
|
|
97
|
+
hasInitializedRef.current = false;
|
|
98
|
+
lastProviderRef.current = data.translationProvider;
|
|
99
|
+
}
|
|
100
|
+
if (hasInitializedRef.current)
|
|
101
|
+
return;
|
|
102
|
+
if (supportsPromptCustomization && !selectedProvider)
|
|
103
|
+
return;
|
|
104
|
+
let nextCustomPrompt = serviceData?.customPrompt || "";
|
|
105
|
+
if (hasDefaultPrompt &&
|
|
106
|
+
nextCustomPrompt.trim() &&
|
|
107
|
+
(serviceData?.promptCustomizationType || "extend") === "extend" &&
|
|
108
|
+
!nextCustomPrompt.startsWith(defaultPrompt || "")) {
|
|
109
|
+
nextCustomPrompt = `${defaultPrompt}\n\n${nextCustomPrompt}`;
|
|
110
|
+
}
|
|
111
|
+
if (enableCustomPrompt && serviceData) {
|
|
112
|
+
setCustomPrompt(nextCustomPrompt || defaultPrompt || "");
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
setCustomPrompt(defaultPrompt || "");
|
|
116
|
+
}
|
|
117
|
+
hasInitializedRef.current = true;
|
|
118
|
+
}, [
|
|
119
|
+
data.translationProvider,
|
|
120
|
+
defaultPrompt,
|
|
121
|
+
enableCustomPrompt,
|
|
122
|
+
hasDefaultPrompt,
|
|
123
|
+
selectedProvider,
|
|
124
|
+
serviceData,
|
|
125
|
+
supportsPromptCustomization,
|
|
126
|
+
]);
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (!isActive)
|
|
129
|
+
return;
|
|
130
|
+
onStepCompleted(!!data.translationProvider);
|
|
131
|
+
}, [data.translationProvider, isActive, onStepCompleted]);
|
|
132
|
+
const handleProviderChange = (e) => {
|
|
133
|
+
const newProvider = e.target.value;
|
|
134
|
+
const newServiceCustomData = new Map();
|
|
135
|
+
data.serviceCustomData?.forEach((value, key) => {
|
|
136
|
+
if (key !== "AI" || newProvider === "AI") {
|
|
137
|
+
newServiceCustomData.set(key, value);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
const newData = {
|
|
141
|
+
...data,
|
|
142
|
+
translationProvider: newProvider,
|
|
143
|
+
serviceCustomData: newServiceCustomData,
|
|
144
|
+
};
|
|
145
|
+
setData(newData);
|
|
146
|
+
};
|
|
147
|
+
const handleCustomPromptToggle = (enabled) => {
|
|
148
|
+
const newServiceCustomData = new Map(data.serviceCustomData || new Map());
|
|
149
|
+
if (enabled) {
|
|
150
|
+
const prompt = defaultPrompt || "";
|
|
151
|
+
setCustomPrompt(prompt);
|
|
152
|
+
newServiceCustomData.set(data.translationProvider, {
|
|
153
|
+
enableCustomPrompt: true,
|
|
154
|
+
customPrompt: prompt.trim(),
|
|
155
|
+
promptCustomizationType: PROMPT_CUSTOMIZATION_TYPE,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
newServiceCustomData.delete(data.translationProvider);
|
|
160
|
+
}
|
|
161
|
+
setData({
|
|
162
|
+
...data,
|
|
163
|
+
serviceCustomData: newServiceCustomData,
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
const handleResetPrompt = () => {
|
|
167
|
+
setCustomPrompt(defaultPrompt || "");
|
|
168
|
+
};
|
|
169
|
+
const updateParentData = useCallback(() => {
|
|
170
|
+
if (!data.translationProvider)
|
|
171
|
+
return;
|
|
172
|
+
const trimmedCustomPrompt = customPromptRef.current.trim();
|
|
173
|
+
const newServiceCustomData = new Map(data.serviceCustomData || new Map());
|
|
174
|
+
const currentServiceData = data.serviceCustomData?.get(data.translationProvider);
|
|
175
|
+
if (enableCustomPrompt) {
|
|
176
|
+
const nextServiceData = {
|
|
177
|
+
enableCustomPrompt: true,
|
|
178
|
+
customPrompt: trimmedCustomPrompt,
|
|
179
|
+
promptCustomizationType: PROMPT_CUSTOMIZATION_TYPE,
|
|
180
|
+
};
|
|
181
|
+
const isSame = currentServiceData?.enableCustomPrompt === true &&
|
|
182
|
+
(currentServiceData.customPrompt || "") === trimmedCustomPrompt &&
|
|
183
|
+
currentServiceData.promptCustomizationType ===
|
|
184
|
+
PROMPT_CUSTOMIZATION_TYPE;
|
|
185
|
+
if (isSame)
|
|
186
|
+
return;
|
|
187
|
+
newServiceCustomData.set(data.translationProvider, nextServiceData);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
if (!currentServiceData)
|
|
191
|
+
return;
|
|
192
|
+
newServiceCustomData.delete(data.translationProvider);
|
|
193
|
+
}
|
|
194
|
+
setData({
|
|
195
|
+
...data,
|
|
196
|
+
serviceCustomData: newServiceCustomData,
|
|
197
|
+
});
|
|
198
|
+
}, [data, enableCustomPrompt, setData]);
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
if (!isActive || !supportsPromptCustomization)
|
|
201
|
+
return;
|
|
202
|
+
if (updateTimerRef.current) {
|
|
203
|
+
clearTimeout(updateTimerRef.current);
|
|
204
|
+
}
|
|
205
|
+
updateTimerRef.current = setTimeout(() => {
|
|
206
|
+
updateParentData();
|
|
207
|
+
}, 100);
|
|
208
|
+
return () => {
|
|
209
|
+
if (updateTimerRef.current) {
|
|
210
|
+
clearTimeout(updateTimerRef.current);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}, [customPrompt, isActive, supportsPromptCustomization, updateParentData]);
|
|
214
|
+
return (_jsx(WizardStepShell, { fillHeight: true, noPadding: true, testId: "prompt-customization-step", children: _jsx("div", { className: "flex min-h-0 w-full flex-1 overflow-y-auto", children: _jsxs("section", { className: "grid min-h-full w-full flex-1 grid-cols-2", children: [_jsxs("div", { className: "min-w-0 px-6 py-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { htmlFor: "translation-batch-name", className: "text-neutral-grey-50", children: "Translation batch name" }), isSuggesting && (_jsx("div", { role: "status", "aria-label": "Suggesting name", className: "text-muted-foreground flex h-3.5 w-3.5 items-center justify-center", children: _jsx(Spinner, { size: "xs" }) }))] }), _jsx(Input, { id: "translation-batch-name", type: "text", value: batchName, placeholder: "A short label for this translation batch.", onChange: (e) => {
|
|
215
|
+
userTouchedNameRef.current = true;
|
|
216
|
+
setBatchName(e.target.value);
|
|
217
|
+
}, className: "mt-2", "data-testid": "translation-batch-name-input" })] }), _jsxs("div", { className: "border-border-default min-w-0 space-y-4 border-l px-6 py-6", children: [_jsxs("div", { children: [_jsx("div", { className: "flex items-center justify-between", children: _jsx(Label, { htmlFor: "translation-provider-select", className: "text-neutral-grey-50", children: "Translation provider" }) }), _jsx(Select, { id: "translation-provider-select", value: data.translationProvider || "", onValueChange: (value) => handleProviderChange({
|
|
218
|
+
target: { value },
|
|
219
|
+
}), options: data.translationProviders.map((provider) => ({
|
|
220
|
+
value: provider.name,
|
|
221
|
+
label: provider.displayName || provider.name,
|
|
222
|
+
})), placeholder: "Select a provider...", size: "sm", className: "mt-2 w-full", "data-testid": "translation-provider-select" }), isCreateLanguageVersionsOnlyProvider && (_jsx("p", { className: "text-muted-foreground mt-2 text-xs", children: "\"Create Language Versions Only\" will create new language versions without automatic translation." }))] }), supportsPromptCustomization && (_jsxs("div", { children: [_jsxs(Label, { className: "flex cursor-pointer items-start gap-2.5", children: [_jsx("input", { type: "checkbox", checked: enableCustomPrompt, onChange: (e) => handleCustomPromptToggle(e.target.checked), className: "mt-0.5 h-3.5 w-3.5 rounded border-border-default text-[var(--color-highlight-100)] accent-[var(--color-highlight-100)] focus:ring-[var(--color-highlight-100)]", "data-testid": "enable-custom-prompt-checkbox" }), _jsxs("div", { className: "min-w-0", children: [_jsx("span", { className: "block text-[13px] font-medium text-neutral-grey-100", children: "Customize translation prompt (optional)" }), _jsx("span", { className: "text-muted-foreground mt-0.5 block text-xs", children: "Edit the provider prompt before starting translation." })] })] }), enableCustomPrompt && (_jsxs("div", { className: "mt-4 space-y-2", children: [_jsx(Textarea, { id: "custom-prompt-textarea", value: customPrompt, onChange: (e) => setCustomPrompt(e.target.value), className: "text-xs", rows: 8, placeholder: "Enter prompt instructions here...", "data-testid": "custom-prompt-textarea" }), hasDefaultPrompt && (_jsxs(Button, { type: "button", size: "sm", variant: "outline", onClick: handleResetPrompt, disabled: customPrompt === (defaultPrompt || ""), className: "gap-1.5", title: "Reset to the default prompt", "data-testid": "reset-custom-prompt-button", children: [_jsx(ResetIcon, { className: "size-3.5", strokeWidth: 1.5, "aria-hidden": "true" }), "Reset to default"] }))] }))] }))] })] }) }) }));
|
|
223
|
+
}
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,2CA+RtB"}
|
|
@@ -78,6 +78,10 @@ export function ServiceLanguageSelectionStep({ isActive = true, data, setData, o
|
|
|
78
78
|
const languageDataLang = data.languageData.get(code);
|
|
79
79
|
const sourceLanguage = languageDataLang?.translationStatus?.sourceLanguage ||
|
|
80
80
|
itemSourceLanguage;
|
|
81
|
+
const sourceLanguageName = editContextLanguageMap.get(sourceLanguage)?.name ||
|
|
82
|
+
data.languageData.get(sourceLanguage)?.name ||
|
|
83
|
+
siteLanguageMap.get(sourceLanguage)?.name ||
|
|
84
|
+
sourceLanguage;
|
|
81
85
|
return {
|
|
82
86
|
code,
|
|
83
87
|
name: editContextLang?.name ||
|
|
@@ -86,6 +90,7 @@ export function ServiceLanguageSelectionStep({ isActive = true, data, setData, o
|
|
|
86
90
|
code,
|
|
87
91
|
icon: editContextLang?.icon || siteLanguage?.icon,
|
|
88
92
|
sourceLanguage,
|
|
93
|
+
sourceLanguageName,
|
|
89
94
|
};
|
|
90
95
|
})
|
|
91
96
|
.filter((lang) => lang.sourceLanguage !== lang.code);
|
|
@@ -152,17 +157,17 @@ export function ServiceLanguageSelectionStep({ isActive = true, data, setData, o
|
|
|
152
157
|
return allLanguages.some((lang) => languageSelection[lang.code]);
|
|
153
158
|
}, [allLanguages, languageSelection]);
|
|
154
159
|
const selectedCount = data.targetLanguages.length;
|
|
155
|
-
return (_jsx(WizardStepShell, { fillHeight: true, testId: "language-selection-step", children: _jsxs("div", { className: "
|
|
160
|
+
return (_jsx(WizardStepShell, { fillHeight: true, noPadding: true, testId: "language-selection-step", children: _jsxs("div", { className: "flex min-h-0 w-full flex-1 flex-col overflow-hidden p-6", children: [_jsxs("div", { className: "flex shrink-0 items-start justify-between gap-2 pb-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: "Select 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
161
|
if (input) {
|
|
157
162
|
input.indeterminate =
|
|
158
163
|
areSomeLanguagesSelected && !areAllLanguagesSelected;
|
|
159
164
|
}
|
|
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
|
|
165
|
+
}, 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 pt-2", children: allLanguages.length === 0 ? (_jsx("div", { className: "grid gap-2.5 sm:grid-cols-2", children: [...Array(6)].map((_, i) => (_jsxs("div", { className: "flex animate-pulse items-center gap-2 rounded-[8px] 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 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-2.5 sm:grid-cols-2", children: allLanguages.map((lang) => {
|
|
161
166
|
const checked = !!languageSelection[lang.code];
|
|
162
|
-
return (_jsxs("label", { className: `group flex cursor-pointer items-center gap-2.5 rounded-
|
|
163
|
-
? "border-primary/30
|
|
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
|
|
167
|
+
return (_jsxs("label", { className: `group flex cursor-pointer items-center gap-2.5 rounded-[8px] border px-2 py-1.5 transition-all ${checked
|
|
168
|
+
? "border-primary/30 ring-primary/10 ring-1"
|
|
169
|
+
: "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 object-cover" })) : (_jsx("span", { className: "inline-block h-4 w-5 shrink-0 bg-neutral-grey-5" })), _jsx("span", { className: `min-w-0 flex-1 truncate text-[13px] ${checked
|
|
165
170
|
? "font-medium text-neutral-grey-100"
|
|
166
|
-
: "text-neutral-grey-100"}`, children: lang.name }),
|
|
171
|
+
: "text-neutral-grey-100"}`, children: lang.name }), _jsxs("span", { className: "ml-auto flex max-w-[9rem] shrink-0 flex-col items-end leading-tight", children: [_jsx("span", { className: "font-mono text-[10px] tracking-wider text-neutral-grey-50", children: lang.code }), _jsxs("span", { className: "max-w-full truncate text-[10px] text-neutral-grey-50", title: `Source language: ${lang.sourceLanguageName}`, children: ["from ", lang.sourceLanguageName] })] })] }, lang.code));
|
|
167
172
|
}) })) })] }) }));
|
|
168
173
|
}
|
|
@@ -4,6 +4,8 @@ type WizardStepShellProps = {
|
|
|
4
4
|
meta?: ReactNode;
|
|
5
5
|
/** When true, the body section flex-grows and content can overflow internally. */
|
|
6
6
|
fillHeight?: boolean;
|
|
7
|
+
/** Removes the shell padding so a step can draw flush dividers or bands. */
|
|
8
|
+
noPadding?: boolean;
|
|
7
9
|
testId?: string;
|
|
8
10
|
children: ReactNode;
|
|
9
11
|
};
|
|
@@ -12,6 +14,6 @@ type WizardStepShellProps = {
|
|
|
12
14
|
* live in the wizard's stepper bar, so this just gives a consistent
|
|
13
15
|
* padding + optional meta row + flex/scroll behavior for the content.
|
|
14
16
|
*/
|
|
15
|
-
export declare function WizardStepShell({ meta, fillHeight, testId, children, }: WizardStepShellProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare function WizardStepShell({ meta, fillHeight, noPadding, testId, children, }: WizardStepShellProps): import("react/jsx-runtime").JSX.Element;
|
|
16
18
|
export {};
|
|
17
19
|
//# sourceMappingURL=WizardStepShell.d.ts.map
|
|
@@ -1 +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,
|
|
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,4EAA4E;IAC5E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,UAAkB,EAClB,SAAiB,EACjB,MAAM,EACN,QAAQ,GACT,EAAE,oBAAoB,2CAwBtB"}
|