@parhelia/localization 0.1.12565 → 0.1.12570

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 (28) hide show
  1. package/dist/LocalizeItemDialog.d.ts.map +1 -1
  2. package/dist/LocalizeItemDialog.js +17 -13
  3. package/dist/api/discovery.d.ts +4 -1
  4. package/dist/api/discovery.d.ts.map +1 -1
  5. package/dist/api/discovery.js +4 -3
  6. package/dist/hooks/useTranslationWizard.d.ts.map +1 -1
  7. package/dist/hooks/useTranslationWizard.js +2 -3
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +28 -13
  10. package/dist/services/translationService.d.ts +7 -48
  11. package/dist/services/translationService.d.ts.map +1 -1
  12. package/dist/services/translationService.js +0 -3
  13. package/dist/settings/TranslationServicesPanel.d.ts.map +1 -1
  14. package/dist/settings/TranslationServicesPanel.js +42 -111
  15. package/dist/setup/LocalizationSetupStep.d.ts.map +1 -1
  16. package/dist/setup/LocalizationSetupStep.js +7 -8
  17. package/dist/steps/ServiceLanguageSelectionStep.d.ts.map +1 -1
  18. package/dist/steps/ServiceLanguageSelectionStep.js +1 -18
  19. package/dist/steps/SubitemDiscoveryStep.d.ts.map +1 -1
  20. package/dist/steps/SubitemDiscoveryStep.js +181 -95
  21. package/dist/translation-center/BatchTranslationView.d.ts +1 -1
  22. package/dist/translation-center/BatchTranslationView.d.ts.map +1 -1
  23. package/dist/translation-center/BatchTranslationView.js +96 -354
  24. package/dist/translation-center/RecentTranslations.d.ts.map +1 -1
  25. package/dist/translation-center/RecentTranslations.js +13 -20
  26. package/dist/translation-center/TranslationManagement.d.ts.map +1 -1
  27. package/dist/translation-center/TranslationManagement.js +4 -2
  28. package/package.json +11 -10
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useEffect, useState, useRef, useMemo, useCallback } from "react";
3
3
  import { Button, PerfectTree } from "@parhelia/core";
4
4
  import { convertFullItemToStub, convertStubToFullItem } from "@parhelia/core";
5
- import { discoverItemsTree, convertBackendTreeToTreeNodes, flattenSelectableItemsFromBackendTrees } from "../api/discovery";
5
+ import { discoverItemsTree, convertBackendTreeToTreeNodes, flattenPagesFromBackendTrees } from "../api/discovery";
6
6
  // We need to implement a basic Spinner component since it's not in core
7
7
  const Spinner = ({ ...props }) => (_jsx("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2 border-[#9650fb]", ...props }));
8
8
  export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData, editContext, onStepCompleted, setBeforeNextCallback, setFooterActions, requestClose }) {
@@ -15,6 +15,7 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
15
15
  const [userHasInteracted, setUserHasInteracted] = useState(false);
16
16
  const [discoveredCount, setDiscoveredCount] = useState(0);
17
17
  const [totalItemsCount, setTotalItemsCount] = useState(0);
18
+ const [translatableItemsCount, setTranslatableItemsCount] = useState(0);
18
19
  const [treeInitialized, setTreeInitialized] = useState(false);
19
20
  const [expandedIds, setExpandedIds] = useState(new Set());
20
21
  // Refs for async safety
@@ -46,19 +47,26 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
46
47
  const nodes = buildTreeNodes(stubs);
47
48
  setTreeNodes(nodes);
48
49
  setExpandedIds(new Set(nodes.map(n => n.key)));
50
+ // Count total items and translatable items in the tree
49
51
  const countItems = (nodeList) => {
50
52
  let total = 0;
53
+ let translatable = 0;
51
54
  const walk = (nodeList) => {
52
55
  nodeList.forEach(node => {
53
56
  total++;
57
+ if (node?.data?.isTranslatable === true) {
58
+ translatable++;
59
+ }
54
60
  if (node.children)
55
61
  walk(node.children);
56
62
  });
57
63
  };
58
64
  walk(nodeList);
59
- return total;
65
+ return { total, translatable };
60
66
  };
61
- setTotalItemsCount(countItems(nodes));
67
+ const counts = countItems(nodes);
68
+ setTotalItemsCount(counts.total);
69
+ setTranslatableItemsCount(counts.translatable);
62
70
  setDiscoveryComplete(true);
63
71
  setTreeInitialized(true);
64
72
  return;
@@ -79,11 +87,11 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
79
87
  }, editContext.sessionId ?? undefined);
80
88
  if (!isMountedRef.current)
81
89
  return;
82
- const items = flattenSelectableItemsFromBackendTrees(resp.trees);
90
+ const pages = flattenPagesFromBackendTrees(resp.trees);
83
91
  const language = editContext?.item?.language ??
84
92
  editContext?.currentItemDescriptor?.language ??
85
93
  "en";
86
- const stubs = items.map(x => ({
94
+ const stubs = pages.map(x => ({
87
95
  id: x.id,
88
96
  name: x.name,
89
97
  path: x.path,
@@ -106,19 +114,26 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
106
114
  const nodes = convertBackendTreeToTreeNodes(resp.trees);
107
115
  setTreeNodes(nodes);
108
116
  setExpandedIds(new Set(nodes.map(n => n.key)));
117
+ // Count total items and translatable items in the tree
109
118
  const countItems = (nodeList) => {
110
119
  let total = 0;
120
+ let translatable = 0;
111
121
  const walk = (nodeList) => {
112
122
  nodeList.forEach(node => {
113
123
  total++;
124
+ if (node?.data?.isTranslatable === true) {
125
+ translatable++;
126
+ }
114
127
  if (node.children)
115
128
  walk(node.children);
116
129
  });
117
130
  };
118
131
  walk(nodeList);
119
- return total;
132
+ return { total, translatable };
120
133
  };
121
- setTotalItemsCount(countItems(nodes));
134
+ const counts = countItems(nodes);
135
+ setTotalItemsCount(counts.total);
136
+ setTranslatableItemsCount(counts.translatable);
122
137
  setDiscoveryComplete(true);
123
138
  setTreeInitialized(true);
124
139
  }
@@ -143,19 +158,19 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
143
158
  initialSelection = new Set(data.discoveredItems.map(item => item.descriptor.id));
144
159
  }
145
160
  else {
146
- // Default: select all selectable items in the tree
147
- const allSelectableIds = new Set();
161
+ // Default: select all translatable items in the tree
162
+ const allTranslatableIds = new Set();
148
163
  const walk = (nodes) => {
149
164
  nodes.forEach(node => {
150
- if (node?.data?.isFolder !== true) {
151
- allSelectableIds.add(node.key);
165
+ if (node?.data?.isTranslatable) {
166
+ allTranslatableIds.add(node.key);
152
167
  }
153
168
  if (node.children)
154
169
  walk(node.children);
155
170
  });
156
171
  };
157
172
  walk(treeNodes);
158
- initialSelection = allSelectableIds;
173
+ initialSelection = allTranslatableIds;
159
174
  }
160
175
  setSelectedItemIds(initialSelection);
161
176
  }, [isActive, discoveryComplete, treeNodes, data.discoveredItems, userHasInteracted]);
@@ -180,7 +195,7 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
180
195
  const buildNode = (item) => ({
181
196
  key: item.id,
182
197
  label: item.name || item.id,
183
- data: { id: item.id, name: item.name, path: item.path, isFolder: item.hasLayout !== true, hasChildren: item.hasChildren },
198
+ data: { id: item.id, name: item.name, path: item.path, hasChildren: item.hasChildren },
184
199
  children: (childrenMap.get(item.id) || []).map(buildNode)
185
200
  });
186
201
  const originalItemIds = new Set(data.items.map(item => item.descriptor.id));
@@ -190,6 +205,8 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
190
205
  // PerfectTree integrations
191
206
  const expandedKeys = useMemo(() => Array.from(expandedIds), [expandedIds]);
192
207
  const selectedKeys = useMemo(() => Array.from(selectedItemIds), [selectedItemIds]);
208
+ // Memoize base item IDs for checkbox rendering
209
+ const baseItemIdsForCheckbox = useMemo(() => new Set(data.items.map(item => item.descriptor.id)), [data.items]);
193
210
  const getDescendantIds = (nodeKey) => {
194
211
  const findNode = (nodes) => {
195
212
  for (const node of nodes) {
@@ -217,98 +234,167 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
217
234
  traverse(node.children);
218
235
  return descendants;
219
236
  };
237
+ const getDescendantTranslatableIds = (nodeKey) => {
238
+ const findNode = (nodes) => {
239
+ for (const node of nodes) {
240
+ if (node.key === nodeKey)
241
+ return node;
242
+ if (node.children) {
243
+ const found = findNode(node.children);
244
+ if (found)
245
+ return found;
246
+ }
247
+ }
248
+ return null;
249
+ };
250
+ const root = findNode(treeNodes);
251
+ if (!root?.children)
252
+ return [];
253
+ const out = [];
254
+ const walk = (children) => {
255
+ children.forEach(child => {
256
+ const isTranslatable = child?.data?.isTranslatable === true;
257
+ if (isTranslatable)
258
+ out.push(child.key);
259
+ if (child.children)
260
+ walk(child.children);
261
+ });
262
+ };
263
+ walk(root.children);
264
+ return out;
265
+ };
220
266
  const handleCancel = () => {
221
267
  // Simplified cancel - just stop discovery
222
268
  inFlightRef.current = false;
223
269
  setIsDiscovering(false);
224
270
  setDiscoveryComplete(true);
225
271
  };
226
- return (_jsxs("div", { className: "flex min-h-0 flex-col gap-4 p-6", "data-testid": "subitem-discovery-step", children: [_jsxs("div", { className: "mb-2 shrink-0", children: [_jsx("h3", { className: "text-lg font-semibold text-[var(--color-dark)] mb-1", children: "Discover Subitems" }), _jsx("p", { className: "text-sm text-[var(--color-gray-2)]", children: "Scanning for subitems to include in translation..." })] }), _jsxs("div", { className: "flex w-full min-w-0 shrink-0 flex-col rounded-lg border border-[var(--color-gray-3)] bg-background p-4 shadow-sm", "data-testid": "discovery-status-container", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [isDiscovering && _jsx(Spinner, { "data-testid": "discovery-spinner" }), _jsx("span", { className: "font-medium text-[var(--color-dark)]", "data-testid": "discovery-status-text", children: isDiscovering ? 'Discovering subitems...' :
272
+ return (_jsxs("div", { className: "flex flex-col gap-4 p-6 h-full", "data-testid": "subitem-discovery-step", children: [_jsxs("div", { className: "mb-2", children: [_jsx("h3", { className: "text-lg font-semibold text-[var(--color-dark)] mb-1", children: "Discover Subitems" }), _jsx("p", { className: "text-sm text-[var(--color-gray-2)]", children: "Scanning for subitems to include in translation..." })] }), _jsxs("div", { className: "border border-[var(--color-gray-3)] rounded-lg p-4 bg-background min-h-[120px] shadow-sm", "data-testid": "discovery-status-container", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [isDiscovering && _jsx(Spinner, { "data-testid": "discovery-spinner" }), _jsx("span", { className: "font-medium text-[var(--color-dark)]", "data-testid": "discovery-status-text", children: isDiscovering ? 'Discovering subitems...' :
227
273
  discoveryComplete ? 'Discovery Complete' :
228
- 'Ready to discover' })] }), isDiscovering && (_jsx(Button, { size: "sm", variant: "outline", onClick: handleCancel, "data-testid": "discovery-cancel-button", children: "Cancel" }))] }), _jsx("div", { className: "text-sm text-[var(--color-gray-2)] mb-3", "data-testid": "discovery-summary", children: discoveredCount > 0 ? (_jsxs("p", { "data-testid": "discovery-total-count", children: [_jsxs("span", { className: "font-medium text-[#9650fb]", children: [totalItemsCount, " total items found"] }), _jsx("br", {}), _jsxs("span", { className: "text-xs text-[var(--color-gray-2)]", children: ["Original items: ", data.items.length, " | Subitems discovered: ", Math.max(0, totalItemsCount - data.items.length)] }), isDiscovering && (_jsxs(_Fragment, { children: [_jsx("br", {}), _jsx("span", { className: "text-xs text-[var(--color-gray-2)] italic", children: "Item tree will appear when discovery completes" })] }))] })) : (_jsx("p", { className: "text-[var(--color-gray-2)]", children: "No items discovered yet." })) }), _jsx("div", { className: "mt-4 flex flex-col", children: !isDiscovering && discoveryComplete && treeInitialized && allDiscoveredItems.length > 0 && treeNodes.length > 0 ? (_jsxs("div", { className: "flex w-full min-w-0 flex-col", "data-testid": "item-selection-section", children: [_jsxs("div", { className: "mb-3 flex flex-col gap-2 border-t border-[var(--color-gray-3)] pt-4", children: [_jsx("h3", { className: "text-lg font-semibold text-[var(--color-dark)]", children: "Select Items to Translate" }), _jsx("span", { className: "text-sm text-[var(--color-gray-2)]", "data-testid": "selection-summary-header", children: selectedItemIds.size > 0 ? _jsxs(_Fragment, { children: [_jsx("span", { className: "font-medium text-[#9650fb]", children: selectedItemIds.size }), " selected"] }) : "No items selected" }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => {
229
- if (!isActive)
230
- return;
231
- const allIds = new Set();
232
- const walk = (nodes) => {
233
- nodes.forEach(node => {
234
- allIds.add(node.key);
235
- if (node.children)
236
- walk(node.children);
237
- });
238
- };
239
- walk(treeNodes);
240
- setUserHasInteracted(true);
241
- setSelectedItemIds(allIds);
242
- onStepCompleted(allIds.size > 0);
243
- }, "data-testid": "select-all-items-button", children: "Select All" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
244
- if (!isActive)
245
- return;
246
- setUserHasInteracted(true);
247
- setSelectedItemIds(new Set());
248
- onStepCompleted(false);
249
- }, "data-testid": "select-none-items-button", children: "Select None" })] })] }), _jsx("div", { className: "text-xs text-[var(--color-gray-2)] mb-3", "data-testid": "selection-summary", children: _jsx("span", { children: "Tip: Hold Shift and click a checkbox to toggle all descendants." }) }), treeNodes.length > 0 && (_jsx("div", { className: "h-80 w-full min-w-0 shrink-0 overflow-auto rounded-lg border border-[var(--color-gray-3)] bg-[var(--color-gray-5)]", "data-testid": "item-tree-view", children: _jsx(PerfectTree, { nodes: treeNodes, expandedKeys: expandedKeys, selectedKeys: selectedKeys, onToggleExpand: (key) => {
250
- setExpandedIds(prev => {
251
- const next = new Set(prev);
252
- if (next.has(key))
253
- next.delete(key);
254
- else
255
- next.add(key);
256
- return next;
257
- });
258
- }, onSelect: (key, event) => {
259
- if (!isActive)
260
- return; // Don't handle selection when inactive
261
- setUserHasInteracted(true);
262
- const targetNode = key;
263
- const shift = event?.shiftKey === true;
264
- const next = new Set(selectedItemIds);
265
- const findNode = (nodes) => {
266
- for (const n of nodes) {
267
- if (n.key === targetNode)
268
- return n;
269
- if (n.children) {
270
- const f = findNode(n.children);
271
- if (f)
272
- return f;
273
- }
274
- }
275
- return null;
276
- };
277
- const n = findNode(treeNodes);
278
- if (!n)
279
- return;
280
- const ids = shift ? [key, ...getDescendantIds(key)] : [key];
281
- if (!next.has(key))
282
- ids.forEach((id) => next.add(id));
283
- else
284
- ids.forEach((id) => next.delete(id));
285
- setSelectedItemIds(next);
286
- // Update completion when user changes selection
287
- onStepCompleted(next.size > 0);
288
- }, renderNode: (node) => {
289
- const isChecked = selectedItemIds.has(node.key);
290
- const icon = node?.data?.icon;
291
- return (_jsxs("div", { className: "flex items-center gap-2", children: [icon && (_jsx("img", { src: icon, alt: "", className: "w-4 h-4" })), _jsx("input", { type: "checkbox", checked: isChecked, "data-testid": `tree-item-checkbox-${node.key}`, onMouseDown: (e) => {
292
- shiftToggleRef.current = e.shiftKey;
293
- }, onChange: (e) => {
274
+ 'Ready to discover' })] }), isDiscovering && (_jsx(Button, { size: "sm", variant: "outline", onClick: handleCancel, "data-testid": "discovery-cancel-button", children: "Cancel" }))] }), _jsx("div", { className: "text-sm text-[var(--color-gray-2)] mb-3", "data-testid": "discovery-summary", children: discoveredCount > 0 ? (_jsxs("p", { "data-testid": "discovery-total-count", children: [_jsxs("span", { className: "font-medium text-[#9650fb]", children: [totalItemsCount, " total items found"] }), totalItemsCount !== translatableItemsCount && (_jsxs(_Fragment, { children: [_jsx("br", {}), _jsxs("span", { className: "text-xs text-[var(--color-gray-2)]", children: ["Translatable items: ", translatableItemsCount, " | Non-translatable: ", totalItemsCount - translatableItemsCount] })] })), _jsx("br", {}), _jsxs("span", { className: "text-xs text-[var(--color-gray-2)]", children: ["Original items: ", data.items.length, " | Subitems discovered: ", Math.max(0, totalItemsCount - data.items.length)] }), isDiscovering && (_jsxs(_Fragment, { children: [_jsx("br", {}), _jsx("span", { className: "text-xs text-[var(--color-gray-2)] italic", children: "Item tree will appear when discovery completes" })] }))] })) : (_jsx("p", { className: "text-[var(--color-gray-2)]", children: "No items discovered yet." })) }), _jsx("div", { className: "mt-4 min-h-[400px]", children: !isDiscovering && discoveryComplete && treeInitialized && allDiscoveredItems.length > 0 && treeNodes.length > 0 ? (_jsx("div", { children: _jsxs("div", { className: "border-t border-[var(--color-gray-3)] pt-4", "data-testid": "item-selection-section", children: [_jsxs("div", { className: "flex items-center justify-between mb-3", children: [_jsx("h3", { className: "text-lg font-semibold text-[var(--color-dark)]", children: "Select Items to Translate" }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx("span", { className: "text-sm text-[var(--color-gray-2)]", "data-testid": "selection-summary-header", children: selectedItemIds.size > 0 ? _jsxs(_Fragment, { children: [_jsx("span", { className: "font-medium text-[#9650fb]", children: selectedItemIds.size }), " selected"] }) : "No items selected" }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => {
294
275
  if (!isActive)
295
276
  return;
277
+ // Only select translatable items
278
+ const translatableIds = new Set();
279
+ const walk = (nodes) => {
280
+ nodes.forEach(node => {
281
+ if (node?.data?.isTranslatable === true) {
282
+ translatableIds.add(node.key);
283
+ }
284
+ if (node.children)
285
+ walk(node.children);
286
+ });
287
+ };
288
+ walk(treeNodes);
296
289
  setUserHasInteracted(true);
297
- const next = new Set(selectedItemIds);
298
- const withDesc = shiftToggleRef.current && (node.children?.length ?? 0) > 0;
299
- const ids = withDesc ? [node.key, ...getDescendantIds(node.key)] : [node.key];
300
- if (e.currentTarget.checked)
301
- ids.forEach((id) => next.add(id));
302
- else
303
- ids.forEach((id) => next.delete(id));
304
- setSelectedItemIds(next);
305
- shiftToggleRef.current = false;
306
- // Update completion when user changes selection
307
- onStepCompleted(next.size > 0);
308
- } }), _jsx("span", { children: node.label })] }));
309
- } }) }))] })) : isDiscovering ? (
290
+ setSelectedItemIds(translatableIds);
291
+ onStepCompleted(translatableIds.size > 0);
292
+ }, "data-testid": "select-all-items-button", children: "Select All" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
293
+ if (!isActive)
294
+ return;
295
+ setUserHasInteracted(true);
296
+ setSelectedItemIds(new Set());
297
+ onStepCompleted(false);
298
+ }, "data-testid": "select-none-items-button", children: "Select None" })] })] }), _jsx("div", { className: "text-xs text-[var(--color-gray-2)] mb-3", "data-testid": "selection-summary", children: _jsx("span", { children: "Tip: Hold Shift and click a checkbox to toggle all descendants." }) }), treeNodes.length > 0 && (_jsx("div", { className: "border border-[var(--color-gray-3)] rounded-lg h-80 overflow-auto bg-[var(--color-gray-5)]", "data-testid": "item-tree-view", children: _jsx(PerfectTree, { nodes: treeNodes, expandedKeys: expandedKeys, selectedKeys: selectedKeys, onToggleExpand: (key) => {
299
+ setExpandedIds(prev => {
300
+ const next = new Set(prev);
301
+ if (next.has(key))
302
+ next.delete(key);
303
+ else
304
+ next.add(key);
305
+ return next;
306
+ });
307
+ }, onSelect: (key, event) => {
308
+ if (!isActive)
309
+ return; // Don't handle selection when inactive
310
+ setUserHasInteracted(true);
311
+ const targetNode = key;
312
+ const shift = event?.shiftKey === true;
313
+ const next = new Set(selectedItemIds);
314
+ // Only select translatable items
315
+ const findNode = (nodes) => {
316
+ for (const n of nodes) {
317
+ if (n.key === targetNode)
318
+ return n;
319
+ if (n.children) {
320
+ const f = findNode(n.children);
321
+ if (f)
322
+ return f;
323
+ }
324
+ }
325
+ return null;
326
+ };
327
+ const n = findNode(treeNodes);
328
+ const isTranslatable = n?.data?.isTranslatable === true;
329
+ if (isTranslatable) {
330
+ const ids = shift ? [key, ...getDescendantIds(key)] : [key];
331
+ if (!next.has(key))
332
+ ids.forEach((id) => next.add(id));
333
+ else
334
+ ids.forEach((id) => next.delete(id));
335
+ }
336
+ else {
337
+ // Folder row click toggles all descendant translatable items
338
+ const translatableIds = getDescendantTranslatableIds(key);
339
+ const allSelected = translatableIds.length > 0 && translatableIds.every((id) => next.has(id));
340
+ if (allSelected)
341
+ translatableIds.forEach((id) => next.delete(id));
342
+ else
343
+ translatableIds.forEach((id) => next.add(id));
344
+ }
345
+ setSelectedItemIds(next);
346
+ // Update completion when user changes selection
347
+ onStepCompleted(next.size > 0);
348
+ }, renderNode: (node) => {
349
+ const isTranslatable = node?.data?.isTranslatable === true;
350
+ const translatableIds = !isTranslatable ? getDescendantTranslatableIds(node.key) : [];
351
+ const allSelected = !isTranslatable ? (translatableIds.length > 0 && translatableIds.every((id) => selectedItemIds.has(id))) : false;
352
+ const someSelected = !isTranslatable ? (translatableIds.some((id) => selectedItemIds.has(id)) && !allSelected) : false;
353
+ // Simple checkbox logic: if it's translatable, check direct selection; otherwise check descendant translatable items
354
+ // Base items are treated like any other selected item - if selected, show checked
355
+ const isChecked = isTranslatable ? selectedItemIds.has(node.key) : allSelected;
356
+ const icon = node?.data?.icon;
357
+ return (_jsxs("div", { className: `flex items-center gap-2 ${!isTranslatable ? 'opacity-70' : ''}`, children: [icon && (_jsx("img", { src: icon, alt: "", className: `w-4 h-4 ${!isTranslatable ? 'opacity-50' : ''}` })), _jsx("input", { type: "checkbox", checked: isChecked, disabled: !isTranslatable, "data-testid": `tree-item-checkbox-${node.key}`, ref: (el) => { if (el)
358
+ el.indeterminate = someSelected; }, onMouseDown: (e) => {
359
+ if (!isTranslatable)
360
+ return; // Don't handle mouse down for untranslatable items
361
+ shiftToggleRef.current = e.shiftKey;
362
+ }, onChange: (e) => {
363
+ if (!isActive || !isTranslatable)
364
+ return; // Don't handle changes when inactive or untranslatable
365
+ setUserHasInteracted(true);
366
+ const next = new Set(selectedItemIds);
367
+ if (isTranslatable) {
368
+ const withDesc = shiftToggleRef.current && (node.children?.length ?? 0) > 0;
369
+ const ids = withDesc ? [node.key, ...getDescendantIds(node.key)] : [node.key];
370
+ if (e.currentTarget.checked)
371
+ ids.forEach((id) => next.add(id));
372
+ else
373
+ ids.forEach((id) => next.delete(id));
374
+ }
375
+ else {
376
+ const ids = getDescendantTranslatableIds(node.key);
377
+ const allSel = ids.length > 0 && ids.every((id) => next.has(id));
378
+ if (e.currentTarget.checked && !allSel)
379
+ ids.forEach((id) => next.add(id));
380
+ else if (!e.currentTarget.checked && allSel)
381
+ ids.forEach((id) => next.delete(id));
382
+ else {
383
+ // Toggle based on current
384
+ if (allSel)
385
+ ids.forEach((id) => next.delete(id));
386
+ else
387
+ ids.forEach((id) => next.add(id));
388
+ }
389
+ }
390
+ setSelectedItemIds(next);
391
+ shiftToggleRef.current = false;
392
+ // Update completion when user changes selection
393
+ onStepCompleted(next.size > 0);
394
+ } }), _jsx("span", { className: `${!isTranslatable ? 'opacity-70 italic' : ''}`, children: node.label })] }));
395
+ } }) }))] }) })) : isDiscovering ? (
310
396
  // Loading skeleton for discovery results
311
- _jsxs("div", { className: "flex w-full min-w-0 flex-col", children: [_jsx("div", { className: "p-3 bg-[#f6eeff] border border-[#9650fb]/20 rounded-lg mb-4", children: _jsx("div", { className: "h-4 bg-[#9650fb]/20 rounded w-64 animate-pulse" }) }), _jsxs("div", { className: "border-t border-[var(--color-gray-3)] pt-4", children: [_jsxs("div", { className: "mb-3 flex flex-col gap-2", children: [_jsx("div", { className: "h-6 bg-[var(--color-gray-3)] rounded w-48 animate-pulse" }), _jsx("div", { className: "h-4 bg-[var(--color-gray-3)] rounded w-40 animate-pulse" }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx("div", { className: "h-8 bg-[var(--color-gray-3)] rounded w-20 animate-pulse" }), _jsx("div", { className: "h-8 bg-[var(--color-gray-3)] rounded w-20 animate-pulse" })] })] }), _jsx("div", { className: "h-4 bg-[var(--color-gray-3)] rounded w-64 mb-3 animate-pulse" }), _jsx("div", { className: "border border-[var(--color-gray-3)] rounded-lg h-80 shrink-0 overflow-auto bg-[var(--color-gray-5)]", children: _jsx("div", { className: "p-3 space-y-2", children: [...Array(8)].map((_, i) => (_jsxs("div", { className: "flex items-center gap-3 animate-pulse", children: [_jsx("div", { className: "h-4 w-4 bg-[var(--color-gray-3)] rounded" }), _jsx("div", { className: "h-4 bg-[var(--color-gray-3)] rounded w-48" })] }, i))) }) })] })] })) : (
397
+ _jsxs("div", { children: [_jsx("div", { className: "p-3 bg-[#f6eeff] border border-[#9650fb]/20 rounded-lg mb-4", children: _jsx("div", { className: "h-4 bg-[#9650fb]/20 rounded w-64 animate-pulse" }) }), _jsxs("div", { className: "border-t border-[var(--color-gray-3)] pt-4", children: [_jsxs("div", { className: "flex items-center justify-between mb-3", children: [_jsx("div", { className: "h-6 bg-[var(--color-gray-3)] rounded w-48 animate-pulse" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("div", { className: "h-8 bg-[var(--color-gray-3)] rounded w-20 animate-pulse" }), _jsx("div", { className: "h-8 bg-[var(--color-gray-3)] rounded w-20 animate-pulse" })] })] }), _jsx("div", { className: "h-4 bg-[var(--color-gray-3)] rounded w-64 mb-3 animate-pulse" }), _jsx("div", { className: "border border-[var(--color-gray-3)] rounded-lg h-80 overflow-auto bg-[var(--color-gray-5)]", children: _jsx("div", { className: "p-3 space-y-2", children: [...Array(8)].map((_, i) => (_jsxs("div", { className: "flex items-center gap-3 animate-pulse", children: [_jsx("div", { className: "h-4 w-4 bg-[var(--color-gray-3)] rounded" }), _jsx("div", { className: "h-4 bg-[var(--color-gray-3)] rounded w-48" })] }, i))) }) })] })] })) : (
312
398
  // Empty state
313
- _jsx("div", { className: "flex h-80 shrink-0 items-center justify-center overflow-auto rounded-lg border border-[var(--color-gray-3)] bg-[var(--color-gray-5)]", children: _jsxs("div", { className: "text-center text-[var(--color-gray-2)]", children: [_jsx("p", { className: "font-medium text-[var(--color-gray-1)]", children: "Ready to discover subitems" }), _jsx("p", { className: "text-sm mt-1", children: "Discovery will start automatically" })] }) })) })] })] }));
399
+ _jsx("div", { className: "flex items-center justify-center h-80 border border-[var(--color-gray-3)] rounded-lg bg-[var(--color-gray-5)]", children: _jsxs("div", { className: "text-center text-[var(--color-gray-2)]", children: [_jsx("p", { className: "font-medium text-[var(--color-gray-1)]", children: "Ready to discover subitems" }), _jsx("p", { className: "text-sm mt-1", children: "Discovery will start automatically" })] }) })) })] })] }));
314
400
  }
@@ -3,6 +3,6 @@ interface BatchTranslationViewProps {
3
3
  batchId: string;
4
4
  onBack?: () => void;
5
5
  }
6
- export declare function BatchTranslationView({ batchId, onBack, }: BatchTranslationViewProps): import("react/jsx-runtime").JSX.Element;
6
+ export declare function BatchTranslationView({ batchId, onBack }: BatchTranslationViewProps): import("react/jsx-runtime").JSX.Element;
7
7
  export {};
8
8
  //# sourceMappingURL=BatchTranslationView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BatchTranslationView.d.ts","sourceRoot":"","sources":["../../src/translation-center/BatchTranslationView.tsx"],"names":[],"mappings":"AA4CA,OAAO,qCAAqC,CAAC;AA4C7C,UAAU,yBAAyB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAwED,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,MAAM,GACP,EAAE,yBAAyB,2CAmoD3B"}
1
+ {"version":3,"file":"BatchTranslationView.d.ts","sourceRoot":"","sources":["../../src/translation-center/BatchTranslationView.tsx"],"names":[],"mappings":"AAyBA,OAAO,qCAAqC,CAAC;AAsB7C,UAAU,yBAAyB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAiCD,wBAAgB,oBAAoB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,yBAAyB,2CAy8BlF"}