@parhelia/localization 0.1.12789 → 0.1.12790

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.
Files changed (54) hide show
  1. package/dist/LocalizeItemDialog.d.ts.map +1 -1
  2. package/dist/LocalizeItemDialog.js +92 -34
  3. package/dist/LocalizeItemUtils.d.ts +1 -2
  4. package/dist/LocalizeItemUtils.d.ts.map +1 -1
  5. package/dist/LocalizeItemUtils.js +44 -12
  6. package/dist/api/discovery.d.ts +25 -0
  7. package/dist/api/discovery.d.ts.map +1 -1
  8. package/dist/api/discovery.js +87 -0
  9. package/dist/index.d.ts +8 -17
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +29 -30
  12. package/dist/services/translationService.d.ts +40 -9
  13. package/dist/services/translationService.d.ts.map +1 -1
  14. package/dist/services/translationService.js +30 -4
  15. package/dist/settings/TranslationServicesPanel.d.ts.map +1 -1
  16. package/dist/settings/TranslationServicesPanel.js +18 -36
  17. package/dist/sidebar/TranslationSidebar.d.ts.map +1 -1
  18. package/dist/sidebar/TranslationSidebar.js +4 -1
  19. package/dist/steps/ItemSelectionStep.d.ts +3 -0
  20. package/dist/steps/ItemSelectionStep.d.ts.map +1 -0
  21. package/dist/steps/ItemSelectionStep.js +23 -0
  22. package/dist/steps/ItemSelectionTree.d.ts +13 -0
  23. package/dist/steps/ItemSelectionTree.d.ts.map +1 -0
  24. package/dist/steps/ItemSelectionTree.js +326 -0
  25. package/dist/steps/MetadataInputStep.d.ts.map +1 -1
  26. package/dist/steps/MetadataInputStep.js +8 -1
  27. package/dist/steps/PromptCustomizationStep.d.ts +1 -1
  28. package/dist/steps/PromptCustomizationStep.d.ts.map +1 -1
  29. package/dist/steps/PromptCustomizationStep.js +161 -56
  30. package/dist/steps/ServiceLanguageSelectionStep.d.ts +6 -1
  31. package/dist/steps/ServiceLanguageSelectionStep.d.ts.map +1 -1
  32. package/dist/steps/ServiceLanguageSelectionStep.js +53 -163
  33. package/dist/steps/WizardStepShell.d.ts +17 -0
  34. package/dist/steps/WizardStepShell.d.ts.map +1 -0
  35. package/dist/steps/WizardStepShell.js +11 -0
  36. package/dist/steps/index.d.ts +1 -0
  37. package/dist/steps/index.d.ts.map +1 -1
  38. package/dist/steps/index.js +1 -0
  39. package/dist/steps/types.d.ts +17 -1
  40. package/dist/steps/types.d.ts.map +1 -1
  41. package/dist/translation-center/TranslationBatches.d.ts +2 -0
  42. package/dist/translation-center/TranslationBatches.d.ts.map +1 -0
  43. package/dist/translation-center/TranslationBatches.js +995 -0
  44. package/dist/translation-center/TranslationManagement.d.ts.map +1 -1
  45. package/dist/translation-center/TranslationManagement.js +6 -23
  46. package/dist/types.d.ts +1 -0
  47. package/dist/types.d.ts.map +1 -1
  48. package/package.json +1 -1
  49. package/dist/translation-center/BatchTranslationView.d.ts +0 -8
  50. package/dist/translation-center/BatchTranslationView.d.ts.map +0 -1
  51. package/dist/translation-center/BatchTranslationView.js +0 -890
  52. package/dist/translation-center/RecentTranslations.d.ts +0 -2
  53. package/dist/translation-center/RecentTranslations.d.ts.map +0 -1
  54. package/dist/translation-center/RecentTranslations.js +0 -309
@@ -1,15 +1,77 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useState, useEffect, useMemo, useRef, useCallback } from "react";
3
- export function PromptCustomizationStep({ stepIndex, isActive = true, data, setData, onStepCompleted, editContext, }) {
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ import { Input, Select, Textarea } from "@parhelia/core";
4
+ import { WizardStepShell } from "./WizardStepShell";
5
+ import { suggestBatchName } from "../api/discovery";
6
+ export function PromptCustomizationStep({ isActive = true, data, setData, onStepCompleted, editContext, }) {
4
7
  const [customPrompt, setCustomPrompt] = useState("");
5
8
  const [customizationType, setCustomizationType] = useState("extend");
6
- // Use refs to track current values without triggering re-renders
9
+ // Local mirror of the wizard's batch name so typing is responsive.
10
+ const [batchName, setBatchName] = useState(data.batchName ?? "");
11
+ const [isSuggesting, setIsSuggesting] = useState(false);
12
+ // Whether the user has manually edited the name. If so, we never overwrite
13
+ // it with a fresh AI suggestion.
14
+ const userTouchedNameRef = useRef(!!data.batchName);
15
+ const dataRef = useRef(data);
16
+ useEffect(() => {
17
+ dataRef.current = data;
18
+ }, [data]);
19
+ // Push name changes back into wizard data (debounced via simple equality).
20
+ useEffect(() => {
21
+ if ((dataRef.current.batchName ?? "") === batchName)
22
+ return;
23
+ setData({ ...dataRef.current, batchName });
24
+ }, [batchName, setData]);
25
+ // Auto-suggest a name when the step becomes active and the user hasn't
26
+ // already provided/edited one. Fires once per item+language combination so
27
+ // navigating away and back doesn't re-fetch.
28
+ const lastSuggestionKeyRef = useRef("");
29
+ const sessionId = editContext?.sessionId;
30
+ useEffect(() => {
31
+ if (!isActive)
32
+ return;
33
+ if (userTouchedNameRef.current)
34
+ return;
35
+ const itemIds = (data.selectionTreeItems && data.selectionTreeItems.length > 0
36
+ ? data.selectionTreeItems.map((s) => s.descriptor.id)
37
+ : data.items.map((i) => i.descriptor.id)).filter(Boolean);
38
+ const langs = [...data.targetLanguages].sort();
39
+ if (itemIds.length === 0 || langs.length === 0)
40
+ return;
41
+ const key = JSON.stringify({ itemIds: [...itemIds].sort(), langs });
42
+ if (lastSuggestionKeyRef.current === key)
43
+ return;
44
+ lastSuggestionKeyRef.current = key;
45
+ let cancelled = false;
46
+ setIsSuggesting(true);
47
+ void (async () => {
48
+ try {
49
+ const includeSubitems = !!(data.selectionTreeItems?.some((s) => s.includeSubitems));
50
+ const name = await suggestBatchName({ itemIds, targetLanguages: langs, includeSubitems }, sessionId);
51
+ if (cancelled || userTouchedNameRef.current)
52
+ return;
53
+ if (name)
54
+ setBatchName(name);
55
+ }
56
+ finally {
57
+ if (!cancelled)
58
+ setIsSuggesting(false);
59
+ }
60
+ })();
61
+ return () => {
62
+ cancelled = true;
63
+ };
64
+ }, [
65
+ isActive,
66
+ sessionId,
67
+ data.selectionTreeItems,
68
+ data.items,
69
+ data.targetLanguages,
70
+ ]);
7
71
  const customPromptRef = useRef(customPrompt);
8
72
  const customizationTypeRef = useRef(customizationType);
9
- // Track if we've initialized from parent data to prevent re-initialization during typing
10
73
  const hasInitializedRef = useRef(false);
11
74
  const lastProviderRef = useRef(data.translationProvider);
12
- // Debounce timer ref for parent updates
13
75
  const updateTimerRef = useRef(null);
14
76
  useEffect(() => {
15
77
  customPromptRef.current = customPrompt;
@@ -17,36 +79,32 @@ export function PromptCustomizationStep({ stepIndex, isActive = true, data, setD
17
79
  useEffect(() => {
18
80
  customizationTypeRef.current = customizationType;
19
81
  }, [customizationType]);
20
- // Get service-specific custom data for the selected provider
82
+ const selectedProvider = useMemo(() => data.translationProviders.find((p) => p.name === data.translationProvider), [data.translationProviders, data.translationProvider]);
21
83
  const serviceData = useMemo(() => {
22
84
  if (!data.serviceCustomData || !data.translationProvider)
23
85
  return null;
24
86
  return data.serviceCustomData.get(data.translationProvider);
25
87
  }, [data.serviceCustomData, data.translationProvider]);
26
88
  const enableCustomPrompt = serviceData?.enableCustomPrompt === true;
27
- // Get default prompt from provider settings (no fallback)
89
+ const supportsPromptCustomization = data.translationProvider === "OpenAI";
28
90
  const defaultPrompt = useMemo(() => {
29
- const provider = data.translationProviders.find(p => p.name === data.translationProvider);
30
- const prompt = provider?.defaultPrompt;
31
- // Return null if prompt is null, undefined, or empty string
91
+ const prompt = selectedProvider?.defaultPrompt;
32
92
  return prompt && prompt.trim() ? prompt : null;
33
- }, [data.translationProviders, data.translationProvider]);
34
- const hasDefaultPrompt = defaultPrompt != null && defaultPrompt.trim().length > 0;
35
- // Initialize from existing data - only on mount or provider change
93
+ }, [selectedProvider?.defaultPrompt]);
94
+ const hasDefaultPrompt = defaultPrompt != null && defaultPrompt.length > 0;
36
95
  useEffect(() => {
37
- // Reset initialization when provider changes
38
96
  if (lastProviderRef.current !== data.translationProvider) {
39
97
  hasInitializedRef.current = false;
40
98
  lastProviderRef.current = data.translationProvider;
41
99
  }
42
- // Skip if already initialized (prevents re-init during typing)
43
- if (hasInitializedRef.current) {
100
+ if (hasInitializedRef.current)
44
101
  return;
45
- }
46
- const nextCustomizationType = serviceData?.promptCustomizationType || "extend";
102
+ const nextCustomizationType = serviceData?.promptCustomizationType ||
103
+ "extend";
47
104
  let nextCustomPrompt = serviceData?.customPrompt || "";
48
- // If stored prompt already includes default (legacy), strip it for editing
49
- if (hasDefaultPrompt && nextCustomizationType === "extend" && nextCustomPrompt.startsWith(defaultPrompt || "")) {
105
+ if (hasDefaultPrompt &&
106
+ nextCustomizationType === "extend" &&
107
+ nextCustomPrompt.startsWith(defaultPrompt || "")) {
50
108
  nextCustomPrompt = nextCustomPrompt.slice((defaultPrompt || "").length);
51
109
  nextCustomPrompt = nextCustomPrompt.replace(/^\s*\n\s*\n?/, "");
52
110
  }
@@ -60,29 +118,70 @@ export function PromptCustomizationStep({ stepIndex, isActive = true, data, setD
60
118
  }
61
119
  hasInitializedRef.current = true;
62
120
  }, [
121
+ data.translationProvider,
122
+ defaultPrompt,
63
123
  enableCustomPrompt,
64
- serviceData,
65
124
  hasDefaultPrompt,
66
- defaultPrompt,
67
- data.translationProvider,
125
+ serviceData,
68
126
  ]);
69
- // Preview of final prompt
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 !== "OpenAI" || newProvider === "OpenAI") {
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
+ newServiceCustomData.set(data.translationProvider, {
151
+ enableCustomPrompt: true,
152
+ customPrompt: "",
153
+ promptCustomizationType: "extend",
154
+ });
155
+ }
156
+ else {
157
+ newServiceCustomData.delete(data.translationProvider);
158
+ }
159
+ setData({
160
+ ...data,
161
+ serviceCustomData: newServiceCustomData,
162
+ });
163
+ };
70
164
  const previewPrompt = useMemo(() => {
71
165
  if (!enableCustomPrompt || !customPrompt.trim()) {
72
166
  return defaultPrompt || "";
73
167
  }
74
- // If no default prompt, just show custom prompt
75
168
  if (!hasDefaultPrompt) {
76
169
  return customPrompt;
77
170
  }
78
- // If default prompt exists, show based on customization type
79
171
  if (customizationType === "replace") {
80
172
  return customPrompt;
81
173
  }
82
174
  return `${defaultPrompt}\n\n${customPrompt}`;
83
- }, [enableCustomPrompt, customPrompt, customizationType, defaultPrompt, hasDefaultPrompt]);
84
- // Debounced update to parent - prevents rapid state updates during typing
175
+ }, [
176
+ customPrompt,
177
+ customizationType,
178
+ defaultPrompt,
179
+ enableCustomPrompt,
180
+ hasDefaultPrompt,
181
+ ]);
85
182
  const updateParentData = useCallback(() => {
183
+ if (!data.translationProvider)
184
+ return;
86
185
  const trimmedCustomPrompt = customPromptRef.current.trim();
87
186
  const currentCustomizationType = customizationTypeRef.current;
88
187
  const newServiceCustomData = new Map(data.serviceCustomData || new Map());
@@ -95,33 +194,28 @@ export function PromptCustomizationStep({ stepIndex, isActive = true, data, setD
95
194
  };
96
195
  const isSame = currentServiceData?.enableCustomPrompt === true &&
97
196
  (currentServiceData.customPrompt || "") === trimmedCustomPrompt &&
98
- (currentServiceData.promptCustomizationType || "extend") === currentCustomizationType;
99
- if (isSame) {
197
+ (currentServiceData.promptCustomizationType || "extend") ===
198
+ currentCustomizationType;
199
+ if (isSame)
100
200
  return;
101
- }
102
201
  newServiceCustomData.set(data.translationProvider, nextServiceData);
103
202
  }
104
203
  else {
105
- if (!currentServiceData) {
204
+ if (!currentServiceData)
106
205
  return;
107
- }
108
206
  newServiceCustomData.delete(data.translationProvider);
109
207
  }
110
- const newData = {
208
+ setData({
111
209
  ...data,
112
210
  serviceCustomData: newServiceCustomData,
113
- };
114
- setData(newData);
115
- }, [enableCustomPrompt, data, setData]);
116
- // Update wizard data when settings change - debounced to prevent rapid fire
211
+ });
212
+ }, [data, enableCustomPrompt, setData]);
117
213
  useEffect(() => {
118
- if (!isActive)
214
+ if (!isActive || !supportsPromptCustomization)
119
215
  return;
120
- // Clear any pending update
121
216
  if (updateTimerRef.current) {
122
217
  clearTimeout(updateTimerRef.current);
123
218
  }
124
- // Debounce the update to parent (100ms delay)
125
219
  updateTimerRef.current = setTimeout(() => {
126
220
  updateParentData();
127
221
  }, 100);
@@ -131,24 +225,35 @@ export function PromptCustomizationStep({ stepIndex, isActive = true, data, setD
131
225
  }
132
226
  };
133
227
  }, [
134
- enableCustomPrompt,
135
228
  customPrompt,
136
229
  customizationType,
137
230
  isActive,
231
+ supportsPromptCustomization,
138
232
  updateParentData,
139
233
  ]);
140
- // Update completion status
141
- useEffect(() => {
142
- if (!isActive)
143
- return;
144
- // Step is always complete (it's optional)
145
- onStepCompleted(true);
146
- }, [isActive, onStepCompleted]);
147
- // Skip condition: hide step when checkbox is disabled
148
- // This is handled by skipCondition in wizard config
149
- return (_jsxs("div", { className: "p-6 space-y-6 h-full flex flex-col", "data-testid": "prompt-customization-step", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-xl font-semibold text-[var(--color-dark)] mb-2", children: "Customize Translation Prompt" }), _jsxs("p", { className: "text-sm text-[var(--color-gray-2)] mb-6", children: ["Optionally customize the prompt used for translation. This allows you to provide specific instructions or context for the translation service.", _jsx("br", {}), _jsx("span", { className: "text-xs text-[var(--color-gray-2)] mt-1 block", children: "Note: Your custom prompt will be appended to the system instructions that ensure proper translation structure." })] })] }), _jsxs("div", { className: "space-y-6 flex-1", children: [!enableCustomPrompt && (_jsx("div", { className: "border border-[var(--color-gray-3)] rounded-lg p-4 bg-[var(--color-gray-5)]", children: _jsx("p", { className: "text-sm text-[var(--color-gray-2)]", children: "Enable \"Customize translation prompt\" in the previous step to customize the prompt." }) })), enableCustomPrompt && (_jsxs(_Fragment, { children: [hasDefaultPrompt && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "bg-background rounded-lg border border-[var(--color-gray-3)] p-4", children: [_jsx("h3", { className: "text-sm font-medium text-[var(--color-dark)] mb-2", children: "Default Prompt" }), _jsx("div", { className: "border border-[var(--color-gray-3)] rounded-md p-3 bg-[var(--color-gray-5)]", children: _jsx("pre", { className: "text-xs text-[var(--color-gray-1)] whitespace-pre-wrap font-mono", children: defaultPrompt }) })] }), _jsxs("div", { className: "bg-background rounded-lg border border-[var(--color-gray-3)] p-4", children: [_jsx("h3", { className: "text-sm font-medium text-[var(--color-dark)] mb-3", children: "Customization Type" }), _jsxs("div", { className: "space-y-3", children: [_jsxs("label", { className: "flex items-center cursor-pointer py-1.5 px-2 rounded-md hover:bg-[var(--color-gray-5)] transition-colors", children: [_jsx("input", { type: "radio", name: "customizationType", value: "extend", checked: customizationType === "extend", onChange: () => setCustomizationType("extend"), className: "h-4 w-4 text-[#9650fb] focus:ring-[#9650fb] border-[var(--color-gray-3)] accent-[#9650fb]", "data-testid": "customization-type-extend" }), _jsx("span", { className: "ml-2 text-sm text-[var(--color-dark)]", children: "Extend (append to default)" })] }), _jsxs("label", { className: "flex items-center cursor-pointer py-1.5 px-2 rounded-md hover:bg-[var(--color-gray-5)] transition-colors", children: [_jsx("input", { type: "radio", name: "customizationType", value: "replace", checked: customizationType === "replace", onChange: () => setCustomizationType("replace"), className: "h-4 w-4 text-[#9650fb] focus:ring-[#9650fb] border-[var(--color-gray-3)] accent-[#9650fb]", "data-testid": "customization-type-replace" }), _jsx("span", { className: "ml-2 text-sm text-[var(--color-dark)]", children: "Replace (use custom prompt only)" })] })] })] })] })), _jsxs("div", { className: "bg-background rounded-lg border border-[var(--color-gray-3)] p-4", children: [_jsx("h3", { className: "text-sm font-medium text-[var(--color-dark)] mb-2", children: "Custom Prompt" }), _jsx("textarea", { value: customPrompt, onChange: (e) => setCustomPrompt(e.target.value), className: "w-full px-3 py-2.5 border border-[var(--color-gray-3)] rounded-md bg-[var(--color-gray-5)] text-[var(--color-dark)] focus:outline-none focus:ring-2 focus:ring-[#9650fb] focus:border-[#9650fb] text-sm font-mono transition-colors", rows: 6, placeholder: "Enter your custom prompt instructions here...", "data-testid": "custom-prompt-textarea" }), _jsx("p", { className: "text-xs text-[var(--color-gray-2)] mt-2", children: hasDefaultPrompt
150
- ? customizationType === "extend"
151
- ? "This will be appended to the default prompt. The final prompt (default + custom) will then be appended to the system instructions on the backend."
152
- : "This will replace the default prompt. The custom prompt will then be appended to the system instructions on the backend."
153
- : "Your custom prompt will be appended to the system instructions on the backend." })] }), hasDefaultPrompt && _jsxs("div", { className: "bg-background rounded-lg border border-[var(--color-gray-3)] p-4", children: [_jsx("h3", { className: "text-sm font-medium text-[var(--color-dark)] mb-2", children: "Preview" }), _jsx("div", { className: "border border-[var(--color-gray-3)] rounded-md p-3 bg-[var(--color-gray-5)] max-h-64 overflow-y-auto", children: _jsx("pre", { className: "text-xs text-[var(--color-gray-1)] whitespace-pre-wrap font-mono", children: previewPrompt }) }), _jsx("p", { className: "text-xs text-[var(--color-gray-2)] mt-2", children: "This is how the final prompt will look when sent to the translation service." })] })] }))] })] }));
234
+ return (_jsx(WizardStepShell, { fillHeight: true, testId: "prompt-customization-step", children: _jsxs("div", { className: "mx-auto flex min-h-0 w-full max-w-3xl flex-1 flex-col gap-6 overflow-y-auto", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-baseline justify-between", children: [_jsx("label", { htmlFor: "translation-batch-name", className: "text-[11px] font-bold tracking-wider text-gray-500 uppercase", children: "Name" }), isSuggesting && (_jsx("span", { className: "text-muted-foreground text-[10px]", children: "Suggesting\u2026" }))] }), _jsx("p", { className: "text-muted-foreground mt-1 text-xs", children: "A short label for this translation batch." }), _jsx(Input, { id: "translation-batch-name", type: "text", value: batchName, placeholder: isSuggesting ? "Generating a name…" : "Translation batch", onChange: (e) => {
235
+ userTouchedNameRef.current = true;
236
+ setBatchName(e.target.value);
237
+ }, className: "mt-2", "data-testid": "translation-batch-name-input" })] }), _jsxs("div", { children: [_jsx("label", { className: "text-[11px] font-bold tracking-wider text-gray-500 uppercase", children: "Translation provider" }), _jsx("p", { className: "text-muted-foreground mt-1 text-xs", children: "\"Create Versions\" will create new language versions without automatic translation." }), _jsx(Select, { value: data.translationProvider || "", onValueChange: (value) => handleProviderChange({
238
+ target: { value },
239
+ }), options: data.translationProviders.map((provider) => ({
240
+ value: provider.name,
241
+ label: provider.displayName || provider.name,
242
+ })), placeholder: "Select a provider\u2026", size: "sm", className: "mt-2 w-full", "data-testid": "translation-provider-select" })] }), supportsPromptCustomization && (_jsxs(_Fragment, { children: [_jsxs("label", { className: "-mb-2 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-gray-300 text-[#9650fb] accent-[#9650fb] focus:ring-[#9650fb]", "data-testid": "enable-custom-prompt-checkbox" }), _jsxs("div", { className: "min-w-0", children: [_jsx("span", { className: "block text-[13px] font-semibold text-gray-900", children: "Customize translation prompt" }), _jsx("span", { className: "text-muted-foreground mt-0.5 block text-xs", children: "Override or extend the provider's default instructions." })] })] }), enableCustomPrompt && (_jsxs(_Fragment, { children: [hasDefaultPrompt && (_jsxs(_Fragment, { children: [_jsxs("div", { children: [_jsx("span", { className: "text-[11px] font-bold tracking-wider text-gray-500 uppercase", children: "Default prompt" }), _jsx("pre", { className: "mt-2 max-h-40 overflow-y-auto rounded-md bg-gray-50/70 p-3 font-mono text-xs leading-relaxed whitespace-pre-wrap text-gray-700", children: defaultPrompt })] }), _jsxs("div", { children: [_jsx("span", { className: "text-[11px] font-bold tracking-wider text-gray-500 uppercase", children: "Customization type" }), _jsx("div", { className: "mt-2 grid gap-2 sm:grid-cols-2", children: [
243
+ {
244
+ value: "extend",
245
+ title: "Extend",
246
+ hint: "Append to the default prompt.",
247
+ },
248
+ {
249
+ value: "replace",
250
+ title: "Replace",
251
+ hint: "Use the custom prompt only.",
252
+ },
253
+ ].map((opt) => {
254
+ const selected = customizationType === opt.value;
255
+ return (_jsxs("label", { className: `group flex cursor-pointer items-start gap-2.5 rounded-lg p-2.5 transition-colors ${selected
256
+ ? "bg-primary/5"
257
+ : "hover:bg-gray-50"}`, children: [_jsx("input", { type: "radio", name: "customizationType", value: opt.value, checked: selected, onChange: () => setCustomizationType(opt.value), className: "mt-0.5 h-3.5 w-3.5 border-gray-300 text-[#9650fb] accent-[#9650fb] focus:ring-[#9650fb]", "data-testid": `customization-type-${opt.value}` }), _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-xs font-semibold text-gray-900", children: opt.title }), _jsx("div", { className: "text-muted-foreground mt-0.5 text-[11px]", children: opt.hint })] })] }, opt.value));
258
+ }) })] })] })), _jsxs("div", { children: [_jsx("span", { className: "text-[11px] font-bold tracking-wider text-gray-500 uppercase", children: "Custom prompt" }), _jsx(Textarea, { value: customPrompt, onChange: (e) => setCustomPrompt(e.target.value), className: "mt-2 font-mono", rows: 6, placeholder: "Enter your custom prompt instructions here\u2026", "data-testid": "custom-prompt-textarea" })] }), hasDefaultPrompt && (_jsxs("div", { children: [_jsx("span", { className: "text-[11px] font-bold tracking-wider text-gray-500 uppercase", children: "Preview" }), _jsx("pre", { className: "mt-2 max-h-52 overflow-y-auto rounded-md bg-gray-50/70 p-3 font-mono text-xs leading-relaxed whitespace-pre-wrap text-gray-700", children: previewPrompt })] }))] }))] }))] }) }));
154
259
  }
@@ -1,3 +1,8 @@
1
1
  import { TranslationStepProps } from "./types";
2
- export declare function ServiceLanguageSelectionStep({ stepIndex, isActive, data, setData, onStepCompleted, editContext, setFooterActions, requestClose }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
2
+ /**
3
+ * Language-only step. Item selection lives in its own step
4
+ * (see ItemSelectionStep). The legacy name is kept so existing imports
5
+ * keep working.
6
+ */
7
+ export declare function ServiceLanguageSelectionStep({ isActive, data, setData, onStepCompleted, editContext, }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
3
8
  //# sourceMappingURL=ServiceLanguageSelectionStep.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceLanguageSelectionStep.d.ts","sourceRoot":"","sources":["../../src/steps/ServiceLanguageSelectionStep.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,SAAS,CAAC;AAkBtE,wBAAgB,4BAA4B,CAAC,EAAE,SAAS,EAAE,QAAe,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,EAAE,oBAAoB,2CAuZ7K"}
1
+ {"version":3,"file":"ServiceLanguageSelectionStep.d.ts","sourceRoot":"","sources":["../../src/steps/ServiceLanguageSelectionStep.tsx"],"names":[],"mappings":"AACA,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,2CA4OtB"}