@parhelia/localization 0.1.12907 → 0.1.12909

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/LocalizeItemCommand.d.ts +7 -1
  2. package/dist/LocalizeItemCommand.d.ts.map +1 -1
  3. package/dist/LocalizeItemCommand.js +31 -18
  4. package/dist/LocalizeItemDialog.d.ts.map +1 -1
  5. package/dist/LocalizeItemDialog.js +97 -35
  6. package/dist/LocalizeItemUtils.d.ts +1 -2
  7. package/dist/LocalizeItemUtils.d.ts.map +1 -1
  8. package/dist/LocalizeItemUtils.js +78 -36
  9. package/dist/api/discovery.d.ts +25 -0
  10. package/dist/api/discovery.d.ts.map +1 -1
  11. package/dist/api/discovery.js +106 -2
  12. package/dist/hooks/useTranslationWizard.d.ts.map +1 -1
  13. package/dist/hooks/useTranslationWizard.js +3 -3
  14. package/dist/index.d.ts +10 -11
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +36 -32
  17. package/dist/services/translationService.d.ts +41 -10
  18. package/dist/services/translationService.d.ts.map +1 -1
  19. package/dist/services/translationService.js +48 -6
  20. package/dist/settings/TranslationServicesPanel.d.ts.map +1 -1
  21. package/dist/settings/TranslationServicesPanel.js +21 -36
  22. package/dist/setup/LocalizationSetupStep.d.ts.map +1 -1
  23. package/dist/setup/LocalizationSetupStep.js +29 -18
  24. package/dist/sidebar/TranslationSidebar.d.ts.map +1 -1
  25. package/dist/sidebar/TranslationSidebar.js +20 -10
  26. package/dist/steps/ItemSelectionStep.d.ts +3 -0
  27. package/dist/steps/ItemSelectionStep.d.ts.map +1 -0
  28. package/dist/steps/ItemSelectionStep.js +24 -0
  29. package/dist/steps/ItemSelectionTree.d.ts +13 -0
  30. package/dist/steps/ItemSelectionTree.d.ts.map +1 -0
  31. package/dist/steps/ItemSelectionTree.js +327 -0
  32. package/dist/steps/PromptCustomizationStep.d.ts +1 -1
  33. package/dist/steps/PromptCustomizationStep.d.ts.map +1 -1
  34. package/dist/steps/PromptCustomizationStep.js +159 -56
  35. package/dist/steps/ServiceLanguageSelectionStep.d.ts +6 -1
  36. package/dist/steps/ServiceLanguageSelectionStep.d.ts.map +1 -1
  37. package/dist/steps/ServiceLanguageSelectionStep.js +92 -165
  38. package/dist/steps/WizardStepShell.d.ts +17 -0
  39. package/dist/steps/WizardStepShell.d.ts.map +1 -0
  40. package/dist/steps/WizardStepShell.js +11 -0
  41. package/dist/steps/types.d.ts +17 -1
  42. package/dist/steps/types.d.ts.map +1 -1
  43. package/dist/translation-center/TranslationBatches.d.ts +2 -0
  44. package/dist/translation-center/TranslationBatches.d.ts.map +1 -0
  45. package/dist/translation-center/TranslationBatches.js +1180 -0
  46. package/dist/translation-center/TranslationManagement.d.ts.map +1 -1
  47. package/dist/translation-center/TranslationManagement.js +25 -15
  48. package/dist/translation-center/TranslationsTitlebar.d.ts +6 -0
  49. package/dist/translation-center/TranslationsTitlebar.d.ts.map +1 -0
  50. package/dist/translation-center/TranslationsTitlebar.js +25 -0
  51. package/dist/translationEvents.d.ts +6 -0
  52. package/dist/translationEvents.d.ts.map +1 -0
  53. package/dist/translationEvents.js +4 -0
  54. package/dist/types.d.ts +1 -0
  55. package/dist/types.d.ts.map +1 -1
  56. package/package.json +1 -1
  57. package/dist/constants.d.ts +0 -15
  58. package/dist/constants.d.ts.map +0 -1
  59. package/dist/constants.js +0 -21
  60. package/dist/steps/MetadataInputStep.d.ts +0 -4
  61. package/dist/steps/MetadataInputStep.d.ts.map +0 -1
  62. package/dist/steps/MetadataInputStep.js +0 -41
  63. package/dist/steps/SubitemDiscoveryStep.d.ts +0 -3
  64. package/dist/steps/SubitemDiscoveryStep.d.ts.map +0 -1
  65. package/dist/steps/SubitemDiscoveryStep.js +0 -313
  66. package/dist/steps/index.d.ts +0 -5
  67. package/dist/steps/index.d.ts.map +0 -1
  68. package/dist/steps/index.js +0 -4
  69. package/dist/translation-center/BatchTranslationView.d.ts +0 -8
  70. package/dist/translation-center/BatchTranslationView.d.ts.map +0 -1
  71. package/dist/translation-center/BatchTranslationView.js +0 -870
  72. package/dist/translation-center/RecentTranslations.d.ts +0 -2
  73. package/dist/translation-center/RecentTranslations.d.ts.map +0 -1
  74. package/dist/translation-center/RecentTranslations.js +0 -309
  75. package/dist/utils/createVersions.d.ts +0 -14
  76. package/dist/utils/createVersions.d.ts.map +0 -1
  77. package/dist/utils/createVersions.js +0 -26
@@ -1,8 +1,14 @@
1
- import { Command, CommandContext, CommandData, FullItem } from "@parhelia/core";
1
+ import { Command, CommandContext, CommandData, EditContextType, FullItem } from "@parhelia/core";
2
+ import { TranslationDialogResult } from "./steps/types";
2
3
  export type LocalizeItemCommandData = CommandData & {
3
4
  items: FullItem[];
4
5
  };
5
6
  export type LocalizeItemCommandContext = CommandContext<LocalizeItemCommandData>;
6
7
  export type LocalizeItemCommand = Command<LocalizeItemCommandData>;
8
+ export declare function openLocalizeItemDialog({ editContext, openDialog, items, }: {
9
+ editContext: EditContextType;
10
+ openDialog?: EditContextType["openDialog"];
11
+ items: FullItem[];
12
+ }): Promise<TranslationDialogResult | null>;
7
13
  export declare const localizeItemCommand: LocalizeItemCommand;
8
14
  //# sourceMappingURL=LocalizeItemCommand.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LocalizeItemCommand.d.ts","sourceRoot":"","sources":["../src/LocalizeItemCommand.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAUhF,MAAM,MAAM,uBAAuB,GAAG,WAAW,GAAG;IAClD,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,cAAc,CAAC,uBAAuB,CAAC,CAAC;AACjF,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAEnE,eAAO,MAAM,mBAAmB,EAAE,mBAgDjC,CAAC"}
1
+ {"version":3,"file":"LocalizeItemCommand.d.ts","sourceRoot":"","sources":["../src/LocalizeItemCommand.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,OAAO,EACP,cAAc,EACd,WAAW,EACX,eAAe,EACf,QAAQ,EACT,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAEL,uBAAuB,EACxB,MAAM,eAAe,CAAC;AAQvB,MAAM,MAAM,uBAAuB,GAAG,WAAW,GAAG;IAClD,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GACpC,cAAc,CAAC,uBAAuB,CAAC,CAAC;AAC1C,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAEnE,wBAAsB,sBAAsB,CAAC,EAC3C,WAAW,EACX,UAAmC,EACnC,KAAK,GACN,EAAE;IACD,WAAW,EAAE,eAAe,CAAC;IAC7B,UAAU,CAAC,EAAE,eAAe,CAAC,YAAY,CAAC,CAAC;IAC3C,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,2CAiCA;AAED,eAAO,MAAM,mBAAmB,EAAE,mBA6BjC,CAAC"}
@@ -2,9 +2,35 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from "react";
3
3
  import { Globe as LucideGlobe } from "lucide-react";
4
4
  import { LocalizeItemDialog } from "./LocalizeItemDialog";
5
+ import { dispatchTranslationBatchStarted } from "./translationEvents";
5
6
  // Icon wrapper to avoid React 19 JSX type issues with forwardRef components.
6
7
  // Use React.createElement so we don't call the forwardRef objects as functions.
7
8
  const GlobeIcon = (props) => React.createElement(LucideGlobe, props);
9
+ export async function openLocalizeItemDialog({ editContext, openDialog = editContext.openDialog, items, }) {
10
+ const result = await openDialog(LocalizeItemDialog, {
11
+ items,
12
+ editContext,
13
+ });
14
+ // If translation was started (result contains batchId), navigate to the batch view
15
+ if (result?.batchId) {
16
+ const translationManagementEnabled = editContext?.configuration?.localization
17
+ ?.translationManagement !== false;
18
+ if (translationManagementEnabled) {
19
+ // Synchronous URL update to avoid race with workspace URL sync
20
+ const params = new URLSearchParams(window.location.search);
21
+ params.set("workspace", "translation-management");
22
+ params.set("batchId", result.batchId);
23
+ const newUrl = `${window.location.pathname}?${params.toString()}`;
24
+ window.history.pushState(null, "", newUrl);
25
+ // Now switch the workspace; URL already contains batchId
26
+ editContext.switchWorkspace("translation-management");
27
+ window.setTimeout(() => {
28
+ dispatchTranslationBatchStarted(result.batchId);
29
+ }, 0);
30
+ }
31
+ }
32
+ return result;
33
+ }
8
34
  export const localizeItemCommand = {
9
35
  id: "localizeItem",
10
36
  label: "Translate",
@@ -14,7 +40,8 @@ export const localizeItemCommand = {
14
40
  return true;
15
41
  }
16
42
  // Check if multi-item is disabled and multiple items are selected
17
- const multiItemEnabled = context.editContext?.configuration?.localization?.multiItem !== false;
43
+ const multiItemEnabled = context.editContext?.configuration?.localization?.multiItem !==
44
+ false;
18
45
  if (!multiItemEnabled && context.data.items.length > 1) {
19
46
  return true; // Disable command when multiple items selected in single-item mode
20
47
  }
@@ -25,24 +52,10 @@ export const localizeItemCommand = {
25
52
  if (!items || !items?.length) {
26
53
  return;
27
54
  }
28
- const result = await context.openDialog(LocalizeItemDialog, {
29
- items: items,
55
+ return openLocalizeItemDialog({
30
56
  editContext: context.editContext,
57
+ openDialog: context.openDialog,
58
+ items,
31
59
  });
32
- // If translation was started (result contains batchId), navigate to the batch view
33
- if (result?.batchId) {
34
- const translationManagementEnabled = context.editContext?.configuration?.localization?.translationManagement !== false;
35
- if (translationManagementEnabled) {
36
- // Synchronous URL update to avoid race with workspace URL sync
37
- const params = new URLSearchParams(window.location.search);
38
- params.set("workspace", "translation-management");
39
- params.set("batchId", result.batchId);
40
- const newUrl = `${window.location.pathname}?${params.toString()}`;
41
- window.history.pushState(null, "", newUrl);
42
- // Now switch the workspace; URL already contains batchId
43
- context.editContext.switchWorkspace("translation-management");
44
- }
45
- }
46
- return result;
47
60
  },
48
61
  };
@@ -1 +1 @@
1
- {"version":3,"file":"LocalizeItemDialog.d.ts","sourceRoot":"","sources":["../src/LocalizeItemDialog.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EAExB,MAAM,eAAe,CAAC;AAuBvB,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,uBAAuB,GAAG,WAAW,CAAC,uBAAuB,CAAC,2CAybtE"}
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,15 +1,18 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Dialog, DialogContent, DialogHeader, DialogTitle, Button, cn, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@parhelia/core";
3
- import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
4
- import { AlertTriangle as LucideAlertTriangle } from "lucide-react";
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";
3
+ import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
4
+ import { AlertTriangle as LucideAlertTriangle, ArrowRight as LucideArrowRight, Globe as LucideGlobe, } from "lucide-react";
5
5
  import { useTranslationWizard } from "./hooks/useTranslationWizard";
6
- import { performDefaultTranslation, generateBatchId } from "./LocalizeItemUtils";
6
+ import { performDefaultTranslation, } from "./LocalizeItemUtils";
7
7
  const AlertTriangleIcon = LucideAlertTriangle;
8
+ const GlobeIcon = LucideGlobe;
9
+ const ArrowRightIcon = LucideArrowRight;
8
10
  // Threshold for warning about large batch translations
9
11
  const LARGE_BATCH_WARNING_THRESHOLD = 100;
12
+ const getNextButtonLabel = (step) => step?.nextButtonLabel || step?.name || "Next";
10
13
  // Note: DialogButtons is an internal component that might need to be added to core exports
11
14
  // For now, we'll implement it inline
12
- const DialogButtons = ({ children, ...props }) => (_jsx("div", { className: "mt-auto flex shrink-0 items-center gap-3 border-t border-gray-3 bg-background 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-6 py-4", ...props, children: children }));
13
16
  export function LocalizeItemDialog(props) {
14
17
  const editContext = props.editContext;
15
18
  const configuration = editContext.configuration.translationWizard;
@@ -33,7 +36,12 @@ export function LocalizeItemDialog(props) {
33
36
  // Provide stable callbacks to steps to avoid update loops
34
37
  const lastActionsSigRef = useRef("");
35
38
  const provideFooterActions = useCallback((actions) => {
36
- const normalized = (actions || []).map(a => ({ key: a.key, label: a.label, disabled: !!a.disabled, signature: a.signature || "" }));
39
+ const normalized = (actions || []).map((a) => ({
40
+ key: a.key,
41
+ label: a.label,
42
+ disabled: !!a.disabled,
43
+ signature: a.signature || "",
44
+ }));
37
45
  const signature = JSON.stringify(normalized);
38
46
  if (signature === lastActionsSigRef.current)
39
47
  return;
@@ -45,7 +53,6 @@ export function LocalizeItemDialog(props) {
45
53
  }, [props.onClose]);
46
54
  // Memoize activeSteps to prevent unnecessary recalculations and new object references
47
55
  // This prevents infinite loops when wizardData changes but skip conditions don't
48
- // The skip condition for subitem-discovery step checks includeSubitems
49
56
  // The skip condition for prompt-customization step checks serviceCustomData and translationProvider
50
57
  const activeSteps = useMemo(() => {
51
58
  const steps = configuration.steps.filter((step) => !step.skipCondition || !step.skipCondition(wizardData));
@@ -54,13 +61,13 @@ export function LocalizeItemDialog(props) {
54
61
  configuration.steps,
55
62
  wizardData.includeSubitems,
56
63
  wizardData.serviceCustomData,
57
- wizardData.translationProvider
64
+ wizardData.translationProvider,
58
65
  ]); // Depend on what affects skip conditions
59
66
  const currentStep = activeSteps[currentStepIndex];
60
67
  // Refs for stable callbacks - updated during render to avoid dependency issues
61
68
  const currentStepIdRef = useRef(currentStep?.id);
62
69
  const currentStepIndexRef = useRef(currentStepIndex);
63
- const lastSetWizardDataRef = useRef('');
70
+ const lastSetWizardDataRef = useRef("");
64
71
  // Track step changes for logging
65
72
  if (currentStepIdRef.current !== currentStep?.id) {
66
73
  currentStepIdRef.current = currentStep?.id;
@@ -84,7 +91,7 @@ export function LocalizeItemDialog(props) {
84
91
  const isLastStep = currentStepIndex === activeSteps.length - 1;
85
92
  const canProceed = stepCompleted >= currentStepIndex;
86
93
  const handleNext = async () => {
87
- if (beforeNextCallback && typeof beforeNextCallback === 'function') {
94
+ if (beforeNextCallback && typeof beforeNextCallback === "function") {
88
95
  const canProceed = await beforeNextCallback();
89
96
  if (!canProceed)
90
97
  return;
@@ -106,13 +113,40 @@ export function LocalizeItemDialog(props) {
106
113
  switchStep(currentStepIndex - 1);
107
114
  }
108
115
  };
109
- // Calculate translation counts for large batch warning
116
+ // Calculate translation counts for the Start Translation button and
117
+ // the large-batch warning. Sums each selected item plus its streamed
118
+ // subitem count (when includeSubitems is on). `isStreaming` is true
119
+ // while any selected-and-flagged item is still being counted, so the
120
+ // UI can append a "+" to indicate the total is not yet final.
110
121
  const calculateTranslationCounts = useCallback((data) => {
111
- const itemsToTranslate = data.includeSubitems ? data.discoveredItems : data.items;
112
- const itemCount = itemsToTranslate.length;
113
122
  const languageCount = data.targetLanguages.length;
123
+ const subitemCounts = data.subitemCounts ?? {};
124
+ let itemCount = 0;
125
+ let isStreaming = false;
126
+ if (data.selectionTreeItems && data.selectionTreeItems.length > 0) {
127
+ for (const entry of data.selectionTreeItems) {
128
+ itemCount += 1;
129
+ if (!entry.includeSubitems)
130
+ continue;
131
+ const c = subitemCounts[entry.descriptor.id];
132
+ if (c === undefined || c === "loading") {
133
+ isStreaming = true;
134
+ }
135
+ else if (c !== "error") {
136
+ itemCount += c.count;
137
+ if (!c.complete)
138
+ isStreaming = true;
139
+ }
140
+ }
141
+ }
142
+ else {
143
+ const baseItems = data.discoveredItems && data.discoveredItems.length > 0
144
+ ? data.discoveredItems
145
+ : data.items;
146
+ itemCount = baseItems.length;
147
+ }
114
148
  const totalTranslations = itemCount * languageCount;
115
- return { itemCount, languageCount, totalTranslations };
149
+ return { itemCount, languageCount, totalTranslations, isStreaming };
116
150
  }, []);
117
151
  const executeTranslation = useCallback(async () => {
118
152
  setIsSubmitting(true);
@@ -124,33 +158,36 @@ export function LocalizeItemDialog(props) {
124
158
  targetLanguages: currentWizardData.targetLanguages,
125
159
  includeSubitems: currentWizardData.includeSubitems,
126
160
  discoveredItems: currentWizardData.discoveredItems,
161
+ selectionTreeItems: currentWizardData.selectionTreeItems,
127
162
  };
128
163
  try {
129
- const batchId = generateBatchId();
130
- const translationResult = await performDefaultTranslation(currentWizardData, editContext, batchId);
164
+ const translationResult = await performDefaultTranslation(currentWizardData, editContext);
131
165
  // Include batchId in result for navigation to translation management
132
- const resultWithBatch = { ...result, batchId };
166
+ const resultWithBatch = {
167
+ ...result,
168
+ batchId: translationResult.batchId,
169
+ };
133
170
  // Close dialog and let parent component handle navigation to translation management
134
171
  props.onClose?.(resultWithBatch);
135
172
  }
136
173
  catch (error) {
137
174
  // Handle specific error types
138
- if (error.name === 'NoTranslationsNeededError') {
175
+ if (error.name === "NoTranslationsNeededError") {
139
176
  // Show user-friendly message via toast and close dialog
140
- editContext.showToast?.('No translations needed. All selected target languages match the source languages of the items.');
177
+ editContext.showToast?.("No translations needed. All selected target languages match the source languages of the items.");
141
178
  props.onClose?.(result);
142
179
  return;
143
180
  }
144
181
  // For other errors, show toast and stay in dialog
145
- console.error('Translation failed:', error);
146
- const errorMessage = error.message || 'Translation request failed. Please try again.';
182
+ console.error("Translation failed:", error);
183
+ const errorMessage = error.message || "Translation request failed. Please try again.";
147
184
  editContext.showToast?.(errorMessage);
148
185
  // Don't close the dialog - let user try again or cancel
149
186
  }
150
187
  }
151
188
  catch (error) {
152
- console.error('Unexpected error in translation:', error);
153
- editContext.showToast?.('An unexpected error occurred. Please try again.');
189
+ console.error("Unexpected error in translation:", error);
190
+ editContext.showToast?.("An unexpected error occurred. Please try again.");
154
191
  // Don't close the dialog on error
155
192
  }
156
193
  finally {
@@ -181,18 +218,40 @@ export function LocalizeItemDialog(props) {
181
218
  : null;
182
219
  // Serialize metadata for comparison (supports both string and object formats)
183
220
  const metadataKey = newData.metadata
184
- ? (typeof newData.metadata === 'string'
221
+ ? typeof newData.metadata === "string"
185
222
  ? newData.metadata
186
- : JSON.stringify(newData.metadata))
223
+ : JSON.stringify(newData.metadata)
187
224
  : null;
188
225
  const dataKey = JSON.stringify({
189
226
  translationProvider: newData.translationProvider,
190
227
  targetLanguages: [...newData.targetLanguages].sort(),
191
228
  includeSubitems: newData.includeSubitems,
192
- discoveredItemsCount: newData.discoveredItems?.length || 0,
229
+ discoveredItemIds: (newData.discoveredItems || [])
230
+ .map((item) => item?.descriptor?.id || item?.id)
231
+ .filter(Boolean)
232
+ .sort(),
193
233
  itemsCount: newData.items?.length || 0,
194
234
  serviceCustomData: serviceCustomDataKey,
195
235
  metadata: metadataKey,
236
+ // batchName must be part of the key — otherwise the PromptCustomizationStep's
237
+ // setData({...prev, batchName}) call sees an "unchanged" key here and the
238
+ // update is silently dropped, so the user-entered name never reaches submit.
239
+ batchName: newData.batchName ?? "",
240
+ // subitemCounts must be part of the key — otherwise live count
241
+ // updates from the subitem-count stream are silently dropped and
242
+ // the Start Translation button can't reflect them.
243
+ subitemCounts: newData.subitemCounts
244
+ ? Object.keys(newData.subitemCounts)
245
+ .sort()
246
+ .map((id) => {
247
+ const c = newData.subitemCounts[id];
248
+ if (c === undefined)
249
+ return [id, "missing"];
250
+ if (c === "loading" || c === "error")
251
+ return [id, c];
252
+ return [id, c.count, c.complete];
253
+ })
254
+ : null,
196
255
  });
197
256
  // Skip if data hasn't actually changed
198
257
  if (lastSetWizardDataRef.current === dataKey) {
@@ -223,18 +282,21 @@ export function LocalizeItemDialog(props) {
223
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: {
224
283
  width: "min(90vw, 1280px)",
225
284
  height: "min(85vh, 900px)",
226
- }, children: [_jsx(DialogHeader, { className: "border-b border-gray-3 bg-background px-6 py-4 pr-14", children: _jsx(DialogTitle, { "data-testid": "translation-wizard-title", children: "Translate" }) }), _jsx("div", { id: "translation-wizard-description", className: "sr-only", children: currentStep?.description || "Configure and start translation for your content." }), _jsx("div", { className: "border-b border-gray-3 bg-background px-6 py-3 pr-14", children: _jsx("div", { "data-testid": "translation-wizard-step-navigation", children: _jsx("div", { className: "flex min-w-0 flex-nowrap items-center overflow-x-auto", children: activeSteps.map((step, index) => {
227
- const isCurrent = currentStepIndex === index;
228
- const isCompleted = currentStepIndex > index;
229
- return (_jsxs(Fragment, { children: [_jsxs("div", { className: "flex shrink-0 items-center gap-2", "data-testid": `step-indicator-${step.id}`, "aria-current": isCurrent ? "step" : undefined, children: [_jsx("div", { className: cn("flex h-7 w-7 shrink-0 items-center justify-center rounded-full border text-sm font-medium transition-colors", isCurrent && "border-theme-secondary bg-theme-secondary-light text-theme-secondary", isCompleted && "border-theme-secondary bg-theme-secondary text-white", !isCurrent && !isCompleted && "border-gray-3 bg-background text-gray-2"), "data-testid": `step-indicator-circle-${step.id}`, "aria-label": step.name, children: isCompleted ? "✓" : index + 1 }), _jsx("span", { className: cn("whitespace-nowrap text-sm transition-colors", isCurrent && "font-medium text-theme-secondary", isCompleted && "text-theme-secondary", !isCurrent && !isCompleted && "text-gray-2"), "data-testid": `step-indicator-label-${step.id}`, children: step.name })] }), index < activeSteps.length - 1 && (_jsx("div", { className: cn("mx-3 h-px w-8 shrink-0 transition-colors", currentStepIndex > index ? "bg-theme-secondary" : "bg-gray-3") }))] }, step.id));
230
- }) }) }) }), _jsxs("div", { className: "flex flex-1 flex-col min-h-0", children: [_jsx("div", { className: "flex-1 overflow-y-auto min-h-0 bg-[var(--color-gray-5)]", "data-testid": "translation-wizard-step-content", children: _jsx("div", { className: "h-full relative", children: activeSteps.map((step, index) => {
285
+ }, children: [_jsx(StyledDialogTitle, { icon: _jsx(GlobeIcon, { strokeWidth: 1.5 }), title: `Translate · ${currentStep?.name ?? ""}`, subtitle: currentStep?.description ||
286
+ "Configure and start translation for your content" }), _jsx("div", { id: "translation-wizard-description", className: "sr-only", children: currentStep?.description ||
287
+ "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) => {
231
288
  const StepComponent = step.component;
232
289
  const isActive = index === currentStepIndex;
233
290
  if (!StepComponent)
234
291
  return null;
235
- 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));
236
- }) }) }), _jsxs(DialogButtons, { "data-testid": "translation-wizard-dialog-buttons", children: [_jsx(Button, { onClick: () => props.onClose?.(null), variant: "outline", size: "default", 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: "default", 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: "default", 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: "default", className: "w-auto min-w-40 flex-none", "data-testid": "translation-wizard-next-button", children: isSubmitting ? "Starting..." : isLastStep ? "Start Translation" : "Next" })] })] })] })] }), _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-amber-500" }), "Large Translation Batch"] }), _jsx(AlertDialogDescription, { children: (() => {
292
+ 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 ? ((() => {
294
+ const { totalTranslations, isStreaming } = calculateTranslationCounts(wizardData);
295
+ if (totalTranslations <= 0)
296
+ return "Start Translation";
297
+ 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: (() => {
237
299
  const { itemCount, languageCount, totalTranslations } = calculateTranslationCounts(wizardData);
238
- 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?"] }));
300
+ 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?"] }));
239
301
  })() })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: isSubmitting, children: "Cancel" }), _jsx(AlertDialogAction, { onClick: handleLargeBatchConfirm, disabled: isSubmitting, children: isSubmitting ? "Starting..." : "Continue" })] })] }) })] }));
240
302
  }
@@ -2,7 +2,6 @@ import { TranslationStatus } from "./types";
2
2
  import { FullItem } from "@parhelia/core";
3
3
  import { TranslationWizardData } from "./steps/types";
4
4
  import { EditContextType } from "@parhelia/core";
5
- export declare function generateBatchId(): string;
6
5
  export type StartedTranslation = {
7
6
  itemId: string;
8
7
  targetLanguage: string;
@@ -12,6 +11,6 @@ export type TranslationResult = {
12
11
  started: StartedTranslation[];
13
12
  batchId?: string;
14
13
  };
15
- export declare function performDefaultTranslation(wizardData: TranslationWizardData, editContext: EditContextType, predefinedBatchId?: string): Promise<TranslationResult>;
14
+ export declare function performDefaultTranslation(wizardData: TranslationWizardData, editContext: EditContextType): Promise<TranslationResult>;
16
15
  export declare const defaultTranslateAll: (languageCodes: string[], translationStatus: TranslationStatus[], sessionId: string, item: FullItem, translationProvider: string) => Promise<TranslationResult>;
17
16
  //# sourceMappingURL=LocalizeItemUtils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LocalizeItemUtils.d.ts","sourceRoot":"","sources":["../src/LocalizeItemUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAC,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAIjD,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,MAAM,MAAM,kBAAkB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5F,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,EAC5B,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,iBAAiB,CAAC,CAyH5B;AAGD,eAAO,MAAM,mBAAmB,GAAU,eAAe,MAAM,EAAE,EAAE,mBAAmB,iBAAiB,EAAE,EAAE,WAAW,MAAM,EAAE,MAAM,QAAQ,EAAE,qBAAqB,MAAM,+BA0BxK,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,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,15 +1,43 @@
1
- import { requestBatchTranslation } from "./services/translationService";
2
- // Shared utility for generating unique batch IDs
3
- export function generateBatchId() {
4
- return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}-${performance.now().toString(36).substring(2, 8)}`;
1
+ import { requestBatchTranslation, } from "./services/translationService";
2
+ import { discoverItemsTree, flattenSelectableItemsFromBackendTrees, } from "./api/discovery";
3
+ /**
4
+ * Resolve the final list of item IDs to translate, expanding subitems for
5
+ * any selection-tree item whose includeSubitems flag is true.
6
+ *
7
+ * Selection priority:
8
+ * 1. `selectionTreeItems` (new model) - per-item flags drive expansion
9
+ * 2. `discoveredItems` (legacy fallback) - used as-is
10
+ * 3. `items` (initial wizard input) - used as-is
11
+ */
12
+ async function resolveItemIdsToTranslate(wizardData, sessionId) {
13
+ if (wizardData.selectionTreeItems &&
14
+ wizardData.selectionTreeItems.length > 0) {
15
+ const directIds = [];
16
+ const rootsToExpand = [];
17
+ for (const entry of wizardData.selectionTreeItems) {
18
+ if (entry.includeSubitems) {
19
+ rootsToExpand.push(entry.descriptor.id);
20
+ }
21
+ else {
22
+ directIds.push(entry.descriptor.id);
23
+ }
24
+ }
25
+ const expandedIds = [];
26
+ if (rootsToExpand.length > 0) {
27
+ const language = wizardData.selectionTreeItems[0]?.descriptor.language;
28
+ const tree = await discoverItemsTree({ rootItemIds: rootsToExpand, language }, sessionId);
29
+ expandedIds.push(...flattenSelectableItemsFromBackendTrees(tree.trees).map((n) => n.id));
30
+ }
31
+ return Array.from(new Set([...directIds, ...expandedIds]));
32
+ }
33
+ const fallback = wizardData.discoveredItems && wizardData.discoveredItems.length > 0
34
+ ? wizardData.discoveredItems
35
+ : wizardData.items;
36
+ return fallback.map((item) => item.descriptor.id);
5
37
  }
6
38
  // Unified translation function that handles both single and batch translations
7
39
  // Now always uses batch logic for traceability in recentTranslation view
8
- export async function performDefaultTranslation(wizardData, editContext, predefinedBatchId) {
9
- const batchId = predefinedBatchId || generateBatchId();
10
- const itemsToTranslate = wizardData.includeSubitems
11
- ? wizardData.discoveredItems
12
- : wizardData.items;
40
+ export async function performDefaultTranslation(wizardData, editContext) {
13
41
  // Build language mappings (source -> target) from the wizard data
14
42
  // The source language is consistent for each target language across all items
15
43
  const languageMappings = [];
@@ -24,19 +52,19 @@ export async function performDefaultTranslation(wizardData, editContext, predefi
24
52
  targetLanguage: language,
25
53
  });
26
54
  }
27
- // Extract item IDs
28
- const itemIds = itemsToTranslate.map(item => item.descriptor.id);
55
+ // Resolve item IDs, expanding subitems per-item-flag where applicable.
56
+ const itemIds = await resolveItemIdsToTranslate(wizardData, editContext.sessionId);
29
57
  // Validate that we have translation requests to process
30
58
  if (languageMappings.length === 0 || itemIds.length === 0) {
31
- const error = new Error('No translations needed. All selected target languages match the source languages of the items.');
32
- error.name = 'NoTranslationsNeededError';
59
+ const error = new Error("No translations needed. All selected target languages match the source languages of the items.");
60
+ error.name = "NoTranslationsNeededError";
33
61
  throw error;
34
62
  }
35
63
  // Prepare batch metadata including service-specific custom data
36
64
  // Start with wizardData.metadata as an object
37
65
  let metadataObj = {};
38
66
  if (wizardData.metadata) {
39
- if (typeof wizardData.metadata === 'string') {
67
+ if (typeof wizardData.metadata === "string") {
40
68
  try {
41
69
  metadataObj = JSON.parse(wizardData.metadata);
42
70
  }
@@ -44,19 +72,27 @@ export async function performDefaultTranslation(wizardData, editContext, predefi
44
72
  // Invalid JSON, ignore
45
73
  }
46
74
  }
47
- else if (typeof wizardData.metadata === 'object') {
75
+ else if (typeof wizardData.metadata === "object") {
48
76
  metadataObj = { ...wizardData.metadata };
49
77
  }
50
78
  }
51
- // Merge service-specific custom data (e.g., custom prompts for OpenAI)
79
+ // Embed the user/AI-provided batch name so it surfaces in Recent
80
+ // Translations and the batch detail view.
81
+ const trimmedName = wizardData.batchName?.trim();
82
+ if (trimmedName) {
83
+ metadataObj.name = trimmedName;
84
+ }
85
+ // Merge service-specific custom data (e.g., custom prompts for AI providers)
52
86
  if (wizardData.serviceCustomData && wizardData.serviceCustomData.size > 0) {
53
87
  const serviceData = wizardData.serviceCustomData.get(wizardData.translationProvider);
54
88
  if (serviceData && serviceData.enableCustomPrompt) {
55
- const provider = wizardData.translationProviders.find(p => p.name === wizardData.translationProvider);
89
+ const provider = wizardData.translationProviders.find((p) => p.name === wizardData.translationProvider);
56
90
  const defaultPrompt = provider?.defaultPrompt && provider.defaultPrompt.trim()
57
91
  ? provider.defaultPrompt
58
92
  : null;
59
- const customizationType = serviceData.promptCustomizationType === "replace" ? "replace" : "extend";
93
+ const customizationType = serviceData.promptCustomizationType === "replace"
94
+ ? "replace"
95
+ : "extend";
60
96
  const rawCustomPrompt = (serviceData.customPrompt || "").trim();
61
97
  let finalCustomPrompt = "";
62
98
  if (rawCustomPrompt) {
@@ -70,34 +106,37 @@ export async function performDefaultTranslation(wizardData, editContext, predefi
70
106
  metadataObj.serviceCustomData = {
71
107
  [wizardData.translationProvider]: {
72
108
  enableCustomPrompt: serviceData.enableCustomPrompt,
73
- customPrompt: finalCustomPrompt
74
- }
109
+ customPrompt: finalCustomPrompt,
110
+ },
75
111
  };
76
112
  }
77
113
  }
78
114
  // Serialize metadata to JSON string (or undefined if empty)
79
- const batchMetadata = Object.keys(metadataObj).length > 0 ? JSON.stringify(metadataObj) : undefined;
115
+ const batchMetadata = Object.keys(metadataObj).length > 0
116
+ ? JSON.stringify(metadataObj)
117
+ : undefined;
80
118
  const batchResult = await requestBatchTranslation({
81
119
  sessionId: editContext.sessionId,
82
- batchId,
83
120
  provider: wizardData.translationProvider,
84
121
  batchMetadata,
85
122
  languageMappings,
86
123
  itemIds,
87
124
  });
88
125
  if (batchResult.type === "error") {
89
- console.error('Translation request failed:', batchResult);
126
+ console.error("Translation request failed:", batchResult);
90
127
  // Throw an error so the caller can handle it (e.g., show a toast)
91
- const errorMessage = batchResult.details || batchResult.summary || 'Translation request failed';
128
+ const errorMessage = batchResult.details ||
129
+ batchResult.summary ||
130
+ "Translation request failed";
92
131
  const error = new Error(errorMessage);
93
- error.name = 'TranslationRequestError';
132
+ error.name = "TranslationRequestError";
94
133
  throw error;
95
134
  }
96
135
  const batchResponse = batchResult.data;
97
136
  if (!batchResponse) {
98
- console.error('Batch response is undefined');
99
- const error = new Error('Server returned an empty response');
100
- error.name = 'TranslationRequestError';
137
+ console.error("Batch response is undefined");
138
+ const error = new Error("Server returned an empty response");
139
+ error.name = "TranslationRequestError";
101
140
  throw error;
102
141
  }
103
142
  const started = batchResponse.started?.map((result) => ({
@@ -106,9 +145,9 @@ export async function performDefaultTranslation(wizardData, editContext, predefi
106
145
  jobId: result.jobId,
107
146
  })) || [];
108
147
  if (batchResponse.skipped && batchResponse.skipped.length > 0) {
109
- console.warn('Some translations were skipped:', batchResponse.skipped);
148
+ console.warn("Some translations were skipped:", batchResponse.skipped);
110
149
  }
111
- return { started, batchId };
150
+ return { started, batchId: batchResponse.id };
112
151
  }
113
152
  // Legacy function for backward compatibility
114
153
  export const defaultTranslateAll = async (languageCodes, translationStatus, sessionId, item, translationProvider) => {
@@ -116,21 +155,24 @@ export const defaultTranslateAll = async (languageCodes, translationStatus, sess
116
155
  const wizardData = {
117
156
  items: [item],
118
157
  targetLanguages: languageCodes,
119
- languageData: new Map(languageCodes.map(lang => {
120
- const status = translationStatus?.find(x => x.targetLanguage === lang);
121
- return [lang, {
158
+ languageData: new Map(languageCodes.map((lang) => {
159
+ const status = translationStatus?.find((x) => x.targetLanguage === lang);
160
+ return [
161
+ lang,
162
+ {
122
163
  name: lang,
123
164
  languageCode: lang,
124
165
  items: [item.descriptor],
125
- translationStatus: status
126
- }];
166
+ translationStatus: status,
167
+ },
168
+ ];
127
169
  })),
128
170
  translationProvider,
129
171
  includeSubitems: false,
130
172
  discoveredItems: [],
131
173
  itemMetadata: new Map(),
132
174
  metadata: null,
133
- translationProviders: []
175
+ translationProviders: [],
134
176
  };
135
177
  const editContext = { sessionId };
136
178
  const result = await performDefaultTranslation(wizardData, editContext);
@@ -1,3 +1,8 @@
1
+ export declare function suggestBatchName(req: {
2
+ itemIds: string[];
3
+ targetLanguages: string[];
4
+ includeSubitems?: boolean;
5
+ }, sessionId?: string): Promise<string | null>;
1
6
  export type DiscoveredItem = {
2
7
  id: string;
3
8
  name: string;
@@ -31,4 +36,24 @@ export type DiscoverItemsTreeResponse = {
31
36
  export declare function discoverItemsTree(req: DiscoverItemsTreeRequest, sessionId?: string): Promise<DiscoverItemsTreeResponse>;
32
37
  export declare function convertBackendTreeToTreeNodes(trees: BackendTreeNode[]): TreeNode[];
33
38
  export declare function flattenSelectableItemsFromBackendTrees(trees: BackendTreeNode[]): DiscoveredItem[];
39
+ export type SubitemCountUpdate = {
40
+ count: number;
41
+ complete: boolean;
42
+ };
43
+ /**
44
+ * Streams the running subitem count for a given root item from the
45
+ * backend. The endpoint returns newline-delimited JSON; each parsed line
46
+ * triggers `onProgress` so the UI can show the count climbing. Filtered
47
+ * server-side via IItemExportFilter + excluded templates.
48
+ *
49
+ * Pass an `AbortSignal` to cancel mid-stream (e.g. user toggles the
50
+ * subitems flag off again).
51
+ */
52
+ export declare function streamSubitemCount(req: {
53
+ itemId: string;
54
+ language?: string;
55
+ }, onProgress: (update: SubitemCountUpdate) => void, options?: {
56
+ sessionId?: string;
57
+ signal?: AbortSignal;
58
+ }): Promise<number>;
34
59
  //# sourceMappingURL=discovery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/api/discovery.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC;AAInG,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,wBAAwB,EAC7B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,yBAAyB,CAAC,CAWpC;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,QAAQ,EAAE,CAQlF;AAED,wBAAgB,sCAAsC,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,cAAc,EAAE,CAQjG"}
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/api/discovery.ts"],"names":[],"mappings":"AAEA,wBAAsB,gBAAgB,CACpC,GAAG,EAAE;IACH,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,EACD,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB,CAAC;AAIF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,wBAAwB,EAC7B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,yBAAyB,CAAC,CAYpC;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,eAAe,EAAE,GACvB,QAAQ,EAAE,CAgBZ;AAED,wBAAgB,sCAAsC,CACpD,KAAK,EAAE,eAAe,EAAE,GACvB,cAAc,EAAE,CAclB;AAED,MAAM,MAAM,kBAAkB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtE;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EAC1C,UAAU,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,EAChD,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GACrD,OAAO,CAAC,MAAM,CAAC,CAsEjB"}