@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.
@@ -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;AAqCvB,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,uBAAuB,GAAG,WAAW,CAAC,uBAAuB,CAAC,2CAugBtE"}
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-6 py-4", ...props, children: children }));
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 prompt-customization step checks serviceCustomData and translationProvider
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 PromptCustomizationStep's
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 h-[85vh] max-h-[900px] min-h-0 w-[90vw] max-w-5xl flex-col overflow-hidden md:min-h-[700px]", "data-testid": "translation-wizard-dialog", onPointerDownOutside: (e) => e.preventDefault(), onEscapeKeyDown: (e) => e.preventDefault(), "aria-describedby": "translation-wizard-description", style: {
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: "outline", 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 ? ((() => {
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,CA0I5B;AAGD,eAAO,MAAM,mBAAmB,GAC9B,eAAe,MAAM,EAAE,EACvB,mBAAmB,iBAAiB,EAAE,EACtC,WAAW,MAAM,EACjB,MAAM,QAAQ,EACd,qBAAqB,MAAM,+BAkC5B,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 provider = wizardData.translationProviders.find((p) => p.name === wizardData.translationProvider);
90
- const defaultPrompt = provider?.defaultPrompt && provider.defaultPrompt.trim()
91
- ? provider.defaultPrompt
92
- : null;
93
- const customizationType = serviceData.promptCustomizationType === "replace"
94
- ? "replace"
95
- : "extend";
96
- const rawCustomPrompt = (serviceData.customPrompt || "").trim();
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)
@@ -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;AAepF,OAAO,EACL,mBAAmB,EAGpB,MAAM,gBAAgB,CAAC;AAUxB,QAAA,MAAM,wCAAwC;;;;;;;;;;;;;;CAuB7C,CAAC;AAEF,QAAA,MAAM,4CAA4C;;;;;;;;CAiBjD,CAAC;AAkCF,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,uBAyHpC"}
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 { PromptCustomizationStep } from "./steps/PromptCustomizationStep";
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: PromptCustomizationStep,
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: PromptCustomizationStep,
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: false,
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 translate sidebar in the Editor workspace
154
+ // Register localization sidebars
137
155
  const sidebars = configuration.editor?.sidebars || [];
138
156
  const hasTranslateSidebar = sidebars.some((s) => s.id === "translate");
139
- if (!hasTranslateSidebar) {
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: [...sidebars, translateSidebar],
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;AAkEF,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,OAAO,EACP,WAAW,EACX,QAAe,EACf,sBAAsB,EACtB,MAAY,GACb,EAAE,sBAAsB,2CA4VxB"}
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,3 @@
1
+ import { TranslationStepProps } from "./types";
2
+ export declare function ProviderStep({ isActive, data, setData, onStepCompleted, editContext, }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
3
+ //# sourceMappingURL=ProviderStep.d.ts.map
@@ -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,2CAiRtB"}
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: "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) => {
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 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) => {
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-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
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 }), _jsx("span", { className: "shrink-0 font-mono text-[10px] tracking-wider text-neutral-grey-50 ", children: lang.code })] }, lang.code));
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,2CAiBtB"}
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"}