@parhelia/localization 0.1.11008 → 0.1.11049
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -33
- package/dist/LocalizeItemDialog.d.ts.map +1 -1
- package/dist/LocalizeItemDialog.js +88 -21
- package/dist/LocalizeItemUtils.d.ts.map +1 -1
- package/dist/LocalizeItemUtils.js +13 -5
- package/dist/steps/MetadataInputStep.d.ts +1 -1
- package/dist/steps/MetadataInputStep.d.ts.map +1 -1
- package/dist/steps/MetadataInputStep.js +5 -2
- package/dist/steps/ServiceLanguageSelectionStep.d.ts +1 -1
- package/dist/steps/ServiceLanguageSelectionStep.d.ts.map +1 -1
- package/dist/steps/ServiceLanguageSelectionStep.js +82 -25
- package/dist/steps/SubitemDiscoveryStep.d.ts +1 -1
- package/dist/steps/SubitemDiscoveryStep.d.ts.map +1 -1
- package/dist/steps/SubitemDiscoveryStep.js +113 -190
- package/dist/steps/types.d.ts +2 -0
- package/dist/steps/types.d.ts.map +1 -1
- package/dist/translation-center/BatchTranslationView.d.ts.map +1 -1
- package/dist/translation-center/BatchTranslationView.js +131 -75
- package/dist/translation-center/RecentTranslations.d.ts.map +1 -1
- package/dist/translation-center/RecentTranslations.js +78 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
# @parhelia/localization
|
|
2
|
-
|
|
3
|
-
Localization and translation features for Parhelia visual editor.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @parhelia/localization
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
12
|
-
|
|
13
|
-
```typescript
|
|
14
|
-
import { LocalizeItemDialog, TranslationSidebar } from '@parhelia/localization';
|
|
15
|
-
import '@parhelia/localization/styles.css';
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Features
|
|
19
|
-
|
|
20
|
-
- Translation wizard
|
|
21
|
-
- Localization setup
|
|
22
|
-
- Translation center
|
|
23
|
-
- Multi-language content management
|
|
24
|
-
- Version creation for localized content
|
|
25
|
-
|
|
26
|
-
## License
|
|
27
|
-
|
|
28
|
-
See LICENSE file for details.
|
|
29
|
-
|
|
30
|
-
## Documentation
|
|
31
|
-
|
|
32
|
-
For complete documentation, visit [https://parhelia.ai](https://parhelia.ai)
|
|
33
|
-
|
|
1
|
+
# @parhelia/localization
|
|
2
|
+
|
|
3
|
+
Localization and translation features for Parhelia visual editor.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @parhelia/localization
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { LocalizeItemDialog, TranslationSidebar } from '@parhelia/localization';
|
|
15
|
+
import '@parhelia/localization/styles.css';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- Translation wizard
|
|
21
|
+
- Localization setup
|
|
22
|
+
- Translation center
|
|
23
|
+
- Multi-language content management
|
|
24
|
+
- Version creation for localized content
|
|
25
|
+
|
|
26
|
+
## License
|
|
27
|
+
|
|
28
|
+
See LICENSE file for details.
|
|
29
|
+
|
|
30
|
+
## Documentation
|
|
31
|
+
|
|
32
|
+
For complete documentation, visit [https://parhelia.ai](https://parhelia.ai)
|
|
33
|
+
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalizeItemDialog.d.ts","sourceRoot":"","sources":["../src/LocalizeItemDialog.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAI7C,OAAO,EACL,uBAAuB,EACvB,uBAAuB,
|
|
1
|
+
{"version":3,"file":"LocalizeItemDialog.d.ts","sourceRoot":"","sources":["../src/LocalizeItemDialog.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAI7C,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EAExB,MAAM,eAAe,CAAC;AAiBvB,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,uBAAuB,GAAG,WAAW,CAAC,uBAAuB,CAAC,2CAmVtE"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, Button, cn, } from "@parhelia/core";
|
|
3
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { ChevronRight as LucideChevronRight } from "lucide-react";
|
|
5
5
|
import { useTranslationWizard } from "./hooks/useTranslationWizard";
|
|
6
6
|
import { performDefaultTranslation, generateBatchId } from "./LocalizeItemUtils";
|
|
@@ -12,12 +12,15 @@ export function LocalizeItemDialog(props) {
|
|
|
12
12
|
const editContext = props.editContext;
|
|
13
13
|
const configuration = editContext.configuration.translationWizard;
|
|
14
14
|
const [currentStepIndex, setCurrentStepIndex] = useState(0);
|
|
15
|
-
const [
|
|
15
|
+
const [stepCompleted, setStepCompleted] = useState(-1);
|
|
16
16
|
const [beforeNextCallback, setBeforeNextCallback] = useState(null);
|
|
17
17
|
const { wizardData, setWizardData } = useTranslationWizard(props);
|
|
18
18
|
// Keep a ref to the latest wizard data to avoid stale closure issues
|
|
19
19
|
const latestWizardDataRef = useRef(wizardData);
|
|
20
20
|
latestWizardDataRef.current = wizardData;
|
|
21
|
+
// Log render cycles to track infinite loops
|
|
22
|
+
const renderCountRef = useRef(0);
|
|
23
|
+
renderCountRef.current += 1;
|
|
21
24
|
// Clear footer actions when step changes to avoid stale actions from previous steps
|
|
22
25
|
const [footerActions, setFooterActions] = useState([]);
|
|
23
26
|
useEffect(() => {
|
|
@@ -36,11 +39,40 @@ export function LocalizeItemDialog(props) {
|
|
|
36
39
|
const requestCloseCb = useCallback((result) => {
|
|
37
40
|
props.onClose?.(result);
|
|
38
41
|
}, [props.onClose]);
|
|
39
|
-
//
|
|
40
|
-
|
|
42
|
+
// Memoize activeSteps to prevent unnecessary recalculations and new object references
|
|
43
|
+
// This prevents infinite loops when wizardData changes but skip conditions don't
|
|
44
|
+
// The skip condition for subitem-discovery step only checks includeSubitems
|
|
45
|
+
const activeSteps = useMemo(() => {
|
|
46
|
+
const steps = configuration.steps.filter((step) => !step.skipCondition || !step.skipCondition(wizardData));
|
|
47
|
+
return steps;
|
|
48
|
+
}, [configuration.steps, wizardData.includeSubitems]); // Only depend on what affects skip conditions
|
|
41
49
|
const currentStep = activeSteps[currentStepIndex];
|
|
50
|
+
// Refs for stable callbacks - updated during render to avoid dependency issues
|
|
51
|
+
const currentStepIdRef = useRef(currentStep?.id);
|
|
52
|
+
const currentStepIndexRef = useRef(currentStepIndex);
|
|
53
|
+
const lastSetWizardDataRef = useRef('');
|
|
54
|
+
// Track step changes for logging
|
|
55
|
+
if (currentStepIdRef.current !== currentStep?.id) {
|
|
56
|
+
currentStepIdRef.current = currentStep?.id;
|
|
57
|
+
}
|
|
58
|
+
currentStepIndexRef.current = currentStepIndex;
|
|
59
|
+
// Ref to track beforeNextCallback for switchStep
|
|
60
|
+
const beforeNextCallbackRef = useRef(null);
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
beforeNextCallbackRef.current = beforeNextCallback;
|
|
63
|
+
}, [beforeNextCallback]);
|
|
64
|
+
// Switch step helper that clears callbacks (similar to page wizard)
|
|
65
|
+
const switchStep = useCallback((index) => {
|
|
66
|
+
beforeNextCallbackRef.current = null; // Clear callback when switching steps
|
|
67
|
+
setBeforeNextCallback(null);
|
|
68
|
+
setCurrentStepIndex(index);
|
|
69
|
+
}, []);
|
|
70
|
+
// Stable callback setter to avoid infinite loops in child components
|
|
71
|
+
const setBeforeNextCallbackStable = useCallback((cb) => {
|
|
72
|
+
setBeforeNextCallback(() => cb);
|
|
73
|
+
}, []); // No dependencies - use ref to access current step index
|
|
42
74
|
const isLastStep = currentStepIndex === activeSteps.length - 1;
|
|
43
|
-
const canProceed =
|
|
75
|
+
const canProceed = stepCompleted >= currentStepIndex;
|
|
44
76
|
const handleNext = async () => {
|
|
45
77
|
if (beforeNextCallback && typeof beforeNextCallback === 'function') {
|
|
46
78
|
const canProceed = await beforeNextCallback();
|
|
@@ -56,14 +88,12 @@ export function LocalizeItemDialog(props) {
|
|
|
56
88
|
await handleFinish();
|
|
57
89
|
}
|
|
58
90
|
else {
|
|
59
|
-
|
|
91
|
+
switchStep(currentStepIndex + 1);
|
|
60
92
|
}
|
|
61
93
|
};
|
|
62
94
|
const handlePrevious = () => {
|
|
63
95
|
if (currentStepIndex > 0) {
|
|
64
|
-
|
|
65
|
-
setBeforeNextCallback(null);
|
|
66
|
-
setCurrentStepIndex(currentStepIndex - 1);
|
|
96
|
+
switchStep(currentStepIndex - 1);
|
|
67
97
|
}
|
|
68
98
|
};
|
|
69
99
|
const handleFinish = useCallback(async () => {
|
|
@@ -79,29 +109,62 @@ export function LocalizeItemDialog(props) {
|
|
|
79
109
|
try {
|
|
80
110
|
const batchId = generateBatchId();
|
|
81
111
|
const translationResult = await performDefaultTranslation(currentWizardData, editContext, batchId);
|
|
82
|
-
console.log("Translation completed successfully:", translationResult);
|
|
83
112
|
// Include batchId in result for navigation to translation management
|
|
84
113
|
const resultWithBatch = { ...result, batchId };
|
|
85
114
|
// Close dialog and let parent component handle navigation to translation management
|
|
86
115
|
props.onClose?.(resultWithBatch);
|
|
87
116
|
}
|
|
88
117
|
catch (error) {
|
|
89
|
-
|
|
90
|
-
|
|
118
|
+
// Handle specific error types
|
|
119
|
+
if (error.name === 'NoTranslationsNeededError') {
|
|
120
|
+
// Show user-friendly message and close dialog
|
|
121
|
+
alert('No translations needed. All selected target languages match the source languages of the items.');
|
|
122
|
+
props.onClose?.(result);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// For other errors, still close the dialog
|
|
126
|
+
console.error('Translation failed:', error);
|
|
127
|
+
alert('Translation failed. Please check the console for details.');
|
|
91
128
|
props.onClose?.(result);
|
|
92
129
|
}
|
|
93
130
|
}
|
|
94
131
|
catch (error) {
|
|
95
|
-
console.error("Error in handleFinish:", error);
|
|
96
132
|
props.onClose?.(null);
|
|
97
133
|
}
|
|
98
134
|
}, [editContext, props]);
|
|
99
|
-
|
|
100
|
-
|
|
135
|
+
// Stable setData wrapper to prevent infinite loops
|
|
136
|
+
// Use refs to access current values without creating dependencies
|
|
137
|
+
const setWizardDataStable = useCallback((newData) => {
|
|
138
|
+
// Create a stable key from the data to detect actual changes
|
|
139
|
+
// Only compare the fields that matter for re-renders
|
|
140
|
+
const dataKey = JSON.stringify({
|
|
141
|
+
translationProvider: newData.translationProvider,
|
|
142
|
+
targetLanguages: [...newData.targetLanguages].sort(),
|
|
143
|
+
includeSubitems: newData.includeSubitems,
|
|
144
|
+
discoveredItemsCount: newData.discoveredItems?.length || 0,
|
|
145
|
+
itemsCount: newData.items?.length || 0,
|
|
146
|
+
});
|
|
147
|
+
// Skip if data hasn't actually changed
|
|
148
|
+
if (lastSetWizardDataRef.current === dataKey) {
|
|
101
149
|
return;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
150
|
+
}
|
|
151
|
+
lastSetWizardDataRef.current = dataKey;
|
|
152
|
+
setWizardData(newData);
|
|
153
|
+
}, []); // No dependencies - use refs to access current values
|
|
154
|
+
// Simplified step completion handler - steps pass their stepIndex via closure
|
|
155
|
+
// Since steps are always mounted, we can use the step index directly from the map
|
|
156
|
+
const handleStepCompleted = useCallback((completed, stepIndex) => {
|
|
157
|
+
if (completed) {
|
|
158
|
+
setStepCompleted((prev) => {
|
|
159
|
+
const newValue = Math.max(prev, stepIndex);
|
|
160
|
+
return newValue;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
const newValue = Math.max(-1, stepIndex - 1);
|
|
165
|
+
setStepCompleted(newValue);
|
|
166
|
+
}
|
|
167
|
+
}, []);
|
|
105
168
|
return (_jsx(Dialog, { open: true, onOpenChange: (open) => {
|
|
106
169
|
// Only close when explicitly requested (e.g., close button clicked)
|
|
107
170
|
if (!open) {
|
|
@@ -117,7 +180,11 @@ export function LocalizeItemDialog(props) {
|
|
|
117
180
|
? "text-blue-600 font-medium"
|
|
118
181
|
: currentStepIndex > index
|
|
119
182
|
? "text-blue-600"
|
|
120
|
-
: "text-gray-400"), "data-testid": `step-indicator-label-${step.id}`, children: step.name }), index < activeSteps.length - 1 && (_jsx(ChevronRightIcon, { className: "w-4 h-4 text-gray-400 mx-2" }))] }, step.id))) }) }), _jsx("div", { className: "flex-1 overflow-y-auto min-h-0", "data-testid": "translation-wizard-step-content", children:
|
|
121
|
-
|
|
122
|
-
|
|
183
|
+
: "text-gray-400"), "data-testid": `step-indicator-label-${step.id}`, children: step.name }), index < activeSteps.length - 1 && (_jsx(ChevronRightIcon, { className: "w-4 h-4 text-gray-400 mx-2" }))] }, step.id))) }) }), _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) => {
|
|
184
|
+
const StepComponent = step.component;
|
|
185
|
+
const isActive = index === currentStepIndex;
|
|
186
|
+
if (!StepComponent)
|
|
187
|
+
return null;
|
|
188
|
+
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));
|
|
189
|
+
}) }) }), _jsxs(DialogButtons, { "data-testid": "translation-wizard-dialog-buttons", children: [_jsx(Button, { onClick: () => props.onClose?.(null), variant: "outline", size: "default", "data-testid": "translation-wizard-cancel-button", children: "Cancel" }), currentStepIndex > 0 && (_jsx(Button, { onClick: handlePrevious, variant: "outline", size: "default", "data-testid": "translation-wizard-previous-button", children: "Previous" })), footerActions.map((a) => (_jsx(Button, { onClick: a.onClick, disabled: !!a.disabled, variant: "default", size: "default", "data-testid": `translation-wizard-footer-action-${a.key}`, children: a.label }, a.key))), _jsx(Button, { onClick: handleNext, disabled: !canProceed, variant: "default", size: "default", "data-testid": "translation-wizard-next-button", children: isLastStep ? "Start Translation" : "Next" })] })] })] }) }));
|
|
123
190
|
}
|
|
@@ -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,
|
|
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,CAgF5B;AAGD,eAAO,MAAM,mBAAmB,GAAU,eAAe,MAAM,EAAE,EAAE,mBAAmB,iBAAiB,EAAE,EAAE,WAAW,MAAM,EAAE,MAAM,QAAQ,EAAE,qBAAqB,MAAM,+BA0BxK,CAAC"}
|
|
@@ -15,15 +15,17 @@ export async function performDefaultTranslation(wizardData, editContext, predefi
|
|
|
15
15
|
for (const item of itemsToTranslate) {
|
|
16
16
|
for (const language of wizardData.targetLanguages) {
|
|
17
17
|
const languageInfo = wizardData.languageData.get(language);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
// Determine source language for this target language from translation status
|
|
19
|
+
// The UI already filters out languages where sourceLanguage === targetLanguage,
|
|
20
|
+
// so we can trust that if a language is selected, it has a valid source
|
|
21
|
+
const sourceLanguage = languageInfo?.translationStatus?.sourceLanguage || "en";
|
|
22
|
+
// Skip if target language equals source language (already filtered in UI, but double-check here)
|
|
23
|
+
if (language === sourceLanguage)
|
|
22
24
|
continue;
|
|
23
25
|
const translationItem = {
|
|
24
26
|
itemId: item.descriptor.id,
|
|
25
27
|
targetLanguage: language,
|
|
26
|
-
sourceLanguage:
|
|
28
|
+
sourceLanguage: sourceLanguage,
|
|
27
29
|
};
|
|
28
30
|
// Get metadata for this specific item and language
|
|
29
31
|
const itemLanguageMetadata = wizardData.itemMetadata.get(item.descriptor.id)?.get(language);
|
|
@@ -33,6 +35,12 @@ export async function performDefaultTranslation(wizardData, editContext, predefi
|
|
|
33
35
|
translationRequests.push(translationItem);
|
|
34
36
|
}
|
|
35
37
|
}
|
|
38
|
+
// Validate that we have translation requests to process
|
|
39
|
+
if (translationRequests.length === 0) {
|
|
40
|
+
const error = new Error('No translations needed. All selected target languages match the source languages of the items.');
|
|
41
|
+
error.name = 'NoTranslationsNeededError';
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
36
44
|
try {
|
|
37
45
|
const batchResult = await requestBatchTranslation({
|
|
38
46
|
sessionId: editContext.sessionId,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { TranslationStepProps } from "./types";
|
|
2
2
|
/** Example Step for metadata input testing */
|
|
3
|
-
export declare function MetadataInputStep({ data, setData, onStepCompleted, }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export declare function MetadataInputStep({ stepIndex, isActive, data, setData, onStepCompleted, }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
//# sourceMappingURL=MetadataInputStep.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MetadataInputStep.d.ts","sourceRoot":"","sources":["../../src/steps/MetadataInputStep.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE/C,8CAA8C;AAC9C,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,OAAO,EACP,eAAe,GAChB,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"MetadataInputStep.d.ts","sourceRoot":"","sources":["../../src/steps/MetadataInputStep.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE/C,8CAA8C;AAC9C,wBAAgB,iBAAiB,CAAC,EAChC,SAAS,EACT,QAAe,EACf,IAAI,EACJ,OAAO,EACP,eAAe,GAChB,EAAE,oBAAoB,2CA4EtB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect } from "react";
|
|
3
3
|
/** Example Step for metadata input testing */
|
|
4
|
-
export function MetadataInputStep({ data, setData, onStepCompleted, }) {
|
|
4
|
+
export function MetadataInputStep({ stepIndex, isActive = true, data, setData, onStepCompleted, }) {
|
|
5
5
|
// Convert Map<string, Map<string, string>> to a flat object for local state management
|
|
6
6
|
const [itemMetadata, setItemMetadata] = useState(() => {
|
|
7
7
|
const result = {};
|
|
@@ -30,9 +30,12 @@ export function MetadataInputStep({ data, setData, onStepCompleted, }) {
|
|
|
30
30
|
};
|
|
31
31
|
// Data is saved automatically on change
|
|
32
32
|
useEffect(() => {
|
|
33
|
+
// Only update completion when this step is active
|
|
34
|
+
if (!isActive)
|
|
35
|
+
return;
|
|
33
36
|
// Mark step as completed since metadata is optional
|
|
34
37
|
onStepCompleted(true);
|
|
35
|
-
}, [onStepCompleted]);
|
|
38
|
+
}, [isActive, onStepCompleted]); // Check when active state changes
|
|
36
39
|
// No footer actions needed since data is saved automatically
|
|
37
40
|
return (_jsxs("div", { className: "flex flex-col gap-4 p-4", "data-testid": "metadata-input-step", children: [_jsx("h3", { className: "text-lg font-medium", children: "Enter Metadata Per Item Per Language" }), _jsx("p", { className: "text-sm text-gray-600", children: "Provide custom metadata for each item and language combination (optional)." }), _jsx("div", { className: "max-h-64 overflow-y-auto space-y-4", "data-testid": "metadata-input-container", children: allItems.map((item) => (_jsxs("div", { className: "border rounded p-3", "data-testid": `metadata-item-${item.id}`, children: [_jsx("label", { className: "block text-sm font-medium mb-2", "data-testid": `metadata-item-label-${item.id}`, children: item.name || item.id }), data.targetLanguages?.map((language) => (_jsxs("div", { className: "mb-2", "data-testid": `metadata-field-${item.id}-${language}`, children: [_jsxs("label", { className: "block text-xs font-medium mb-1 text-gray-600", "data-testid": `metadata-language-label-${item.id}-${language}`, children: [language.toUpperCase(), ":"] }), _jsx("textarea", { className: "w-full border rounded p-2 text-sm", rows: 2, value: itemMetadata[item.id]?.[language] || "", onChange: (e) => handleMetadataChange(item.id, language, e.target.value), placeholder: `Enter metadata for ${language}...`, "data-testid": `metadata-textarea-${item.id}-${language}` })] }, `${item.id}-${language}`)))] }, item.id))) })] }));
|
|
38
41
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { TranslationStepProps } from "./types";
|
|
2
|
-
export declare function ServiceLanguageSelectionStep({ data, setData, onStepCompleted, editContext, setFooterActions, requestClose }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function ServiceLanguageSelectionStep({ stepIndex, isActive, data, setData, onStepCompleted, editContext, setFooterActions, requestClose }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
//# 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;AAGtE,wBAAgB,4BAA4B,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"ServiceLanguageSelectionStep.d.ts","sourceRoot":"","sources":["../../src/steps/ServiceLanguageSelectionStep.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,SAAS,CAAC;AAGtE,wBAAgB,4BAA4B,CAAC,EAAE,SAAS,EAAE,QAAe,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,EAAE,oBAAoB,2CAyU7K"}
|
|
@@ -1,44 +1,88 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
// Version creation now offered via VersionOnlyTranslationProvider. No custom button here.
|
|
4
|
-
export function ServiceLanguageSelectionStep({ data, setData, onStepCompleted, editContext, setFooterActions, requestClose }) {
|
|
4
|
+
export function ServiceLanguageSelectionStep({ stepIndex, isActive = true, data, setData, onStepCompleted, editContext, setFooterActions, requestClose }) {
|
|
5
5
|
const [languageSelection, setLanguageSelection] = useState({});
|
|
6
6
|
const dataRef = useRef(data);
|
|
7
7
|
useEffect(() => { dataRef.current = data; }, [data]);
|
|
8
8
|
// Check if multi-item translation is enabled
|
|
9
9
|
const multiItemEnabled = editContext?.configuration?.localization?.multiItem !== false;
|
|
10
|
-
// Use
|
|
10
|
+
// Use ref to track onStepCompleted to avoid dependency issues
|
|
11
11
|
const onStepCompletedRef = useRef(onStepCompleted);
|
|
12
12
|
useEffect(() => {
|
|
13
13
|
onStepCompletedRef.current = onStepCompleted;
|
|
14
14
|
}, [onStepCompleted]);
|
|
15
|
+
// Track last completion state to prevent unnecessary calls
|
|
16
|
+
const lastCompletionStateRef = useRef(null);
|
|
17
|
+
// Call completion check when component mounts or when returning to this step
|
|
18
|
+
// Also check when data changes (but only if we're actually the active step)
|
|
15
19
|
useEffect(() => {
|
|
20
|
+
// Only update completion when this step is active
|
|
21
|
+
if (!isActive)
|
|
22
|
+
return;
|
|
16
23
|
const hasProvider = !!data.translationProvider;
|
|
17
24
|
const hasLanguages = data.targetLanguages.length > 0;
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
const isCompleted = hasProvider && hasLanguages;
|
|
26
|
+
// Only call if completion state actually changed
|
|
27
|
+
if (lastCompletionStateRef.current !== isCompleted) {
|
|
28
|
+
lastCompletionStateRef.current = isCompleted;
|
|
29
|
+
onStepCompletedRef.current(isCompleted);
|
|
30
|
+
}
|
|
31
|
+
}, [isActive, data.translationProvider, data.targetLanguages.length]);
|
|
20
32
|
// 1) Derive provider + available languages (lightweight, in-memory)
|
|
21
33
|
const provider = useMemo(() => data.translationProviders.find(p => p.name === data.translationProvider), [data.translationProviders, data.translationProvider]);
|
|
22
|
-
|
|
34
|
+
// Use editContext.itemLanguages (like TranslationSidebar) to get all languages including source language
|
|
35
|
+
// This is not item-specific - it shows all available languages for the site
|
|
36
|
+
const editContextLanguages = useMemo(() => editContext?.itemLanguages || [], [editContext?.itemLanguages]);
|
|
37
|
+
// Get language codes from editContext languages, or fallback to languageData keys
|
|
38
|
+
const siteLanguageCodes = useMemo(() => {
|
|
39
|
+
if (editContextLanguages.length > 0) {
|
|
40
|
+
return editContextLanguages.map(lang => lang.languageCode);
|
|
41
|
+
}
|
|
42
|
+
return Array.from(data.languageData.keys());
|
|
43
|
+
}, [editContextLanguages, data.languageData]);
|
|
23
44
|
const availableLanguageCodes = useMemo(() => (provider?.supportedLanguages?.length ? provider.supportedLanguages : siteLanguageCodes), [provider?.supportedLanguages, siteLanguageCodes]);
|
|
45
|
+
// Determine the source language from the items being translated
|
|
46
|
+
// Use the first item's language as the source language (all items should have the same source language)
|
|
47
|
+
const itemSourceLanguage = useMemo(() => {
|
|
48
|
+
return data.items[0]?.descriptor.language || editContext?.contentEditorItem?.descriptor.language || "en";
|
|
49
|
+
}, [data.items, editContext?.contentEditorItem]);
|
|
24
50
|
const allLanguages = useMemo(() => {
|
|
51
|
+
// Create a map of language codes to Language objects from editContext for quick lookup
|
|
52
|
+
const editContextLanguageMap = new Map(editContextLanguages.map(lang => [lang.languageCode, lang]));
|
|
25
53
|
const arr = availableLanguageCodes
|
|
26
|
-
.map(code =>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
54
|
+
.map(code => {
|
|
55
|
+
// Prefer language info from editContext (like TranslationSidebar), fallback to languageData
|
|
56
|
+
const editContextLang = editContextLanguageMap.get(code);
|
|
57
|
+
const languageDataLang = data.languageData.get(code);
|
|
58
|
+
// Determine source language: prefer from translationStatus, fallback to item's source language
|
|
59
|
+
const sourceLanguage = languageDataLang?.translationStatus?.sourceLanguage || itemSourceLanguage;
|
|
60
|
+
return {
|
|
61
|
+
code,
|
|
62
|
+
name: editContextLang?.name || languageDataLang?.name || code,
|
|
63
|
+
hasVersions: (editContextLang?.versions || 0) > 0 || (languageDataLang?.items.length || 0) > 0,
|
|
64
|
+
translationStatus: languageDataLang?.translationStatus,
|
|
65
|
+
sourceLanguage, // Add source language to the language object
|
|
66
|
+
};
|
|
67
|
+
})
|
|
68
|
+
// Filter out languages where sourceLanguage equals targetLanguage
|
|
69
|
+
.filter(lang => lang.sourceLanguage !== lang.code);
|
|
35
70
|
arr.sort((a, b) => a.name.localeCompare(b.name));
|
|
36
71
|
return arr;
|
|
37
|
-
}, [availableLanguageCodes, data.languageData]);
|
|
72
|
+
}, [availableLanguageCodes, editContextLanguages, data.languageData, itemSourceLanguage]);
|
|
73
|
+
// Track last processed targetLanguages to prevent unnecessary updates
|
|
74
|
+
const lastProcessedTargetLanguagesRef = useRef('');
|
|
38
75
|
// Rehydrate UI selection from saved wizard data when returning to this step
|
|
39
76
|
useEffect(() => {
|
|
40
77
|
if (!allLanguages || allLanguages.length === 0)
|
|
41
78
|
return;
|
|
79
|
+
// Create a stable key from targetLanguages to detect actual changes
|
|
80
|
+
const targetLanguagesKey = JSON.stringify([...data.targetLanguages].sort());
|
|
81
|
+
// Skip if we've already processed this exact set of target languages
|
|
82
|
+
if (lastProcessedTargetLanguagesRef.current === targetLanguagesKey) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
lastProcessedTargetLanguagesRef.current = targetLanguagesKey;
|
|
42
86
|
const initialSelection = {};
|
|
43
87
|
// Mark as selected any language that's in our saved targetLanguages array
|
|
44
88
|
for (const langCode of data.targetLanguages) {
|
|
@@ -49,20 +93,32 @@ export function ServiceLanguageSelectionStep({ data, setData, onStepCompleted, e
|
|
|
49
93
|
}
|
|
50
94
|
setLanguageSelection(initialSelection);
|
|
51
95
|
}, [allLanguages, data.targetLanguages]);
|
|
96
|
+
// Track last set targetLanguages to prevent unnecessary setData calls
|
|
97
|
+
const lastSetTargetLanguagesRef = useRef('');
|
|
52
98
|
// Update wizard data when language selection changes
|
|
53
99
|
useEffect(() => {
|
|
54
100
|
const selectedLanguages = Object.entries(languageSelection)
|
|
55
101
|
.filter(([, isSelected]) => isSelected)
|
|
56
102
|
.map(([code]) => code);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
103
|
+
const selectedLanguagesKey = JSON.stringify([...selectedLanguages].sort());
|
|
104
|
+
const currentTargetLanguagesKey = JSON.stringify([...data.targetLanguages].sort());
|
|
105
|
+
// Only update if the selection has actually changed AND we haven't already set this value
|
|
106
|
+
if (selectedLanguagesKey === currentTargetLanguagesKey) {
|
|
107
|
+
// Selection matches current data, no update needed
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (lastSetTargetLanguagesRef.current === selectedLanguagesKey) {
|
|
111
|
+
// We've already set this value, skip to prevent loops
|
|
112
|
+
return;
|
|
64
113
|
}
|
|
65
|
-
|
|
114
|
+
lastSetTargetLanguagesRef.current = selectedLanguagesKey;
|
|
115
|
+
const newData = {
|
|
116
|
+
...dataRef.current,
|
|
117
|
+
targetLanguages: selectedLanguages
|
|
118
|
+
};
|
|
119
|
+
setData(newData);
|
|
120
|
+
// Completion will be updated by the useEffect that watches data.targetLanguages.length
|
|
121
|
+
}, [languageSelection, setData, data.targetLanguages]);
|
|
66
122
|
const handleProviderChange = (e) => {
|
|
67
123
|
const newProvider = e.target.value;
|
|
68
124
|
const newData = {
|
|
@@ -74,6 +130,7 @@ export function ServiceLanguageSelectionStep({ data, setData, onStepCompleted, e
|
|
|
74
130
|
setData(newData);
|
|
75
131
|
// Clear UI selection too
|
|
76
132
|
setLanguageSelection({});
|
|
133
|
+
// Completion will be updated by the useEffect that watches data.translationProvider
|
|
77
134
|
};
|
|
78
135
|
const handleLanguageToggle = (langCode) => {
|
|
79
136
|
setLanguageSelection(prev => ({ ...prev, [langCode]: !prev[langCode] }));
|
|
@@ -110,11 +167,11 @@ export function ServiceLanguageSelectionStep({ data, setData, onStepCompleted, e
|
|
|
110
167
|
const areSomeLanguagesSelected = useMemo(() => {
|
|
111
168
|
return allLanguages.some(lang => languageSelection[lang.code]);
|
|
112
169
|
}, [allLanguages, languageSelection]);
|
|
113
|
-
return (_jsxs("div", { className: "p-6 space-y-6 h-full flex flex-col", "data-testid": "service-language-selection-step", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-xl font-semibold mb-2", children: "Configure Translation" }), _jsx("p", { className: "text-sm text-gray-600 mb-6", children: "Select translation provider and target languages for your content." })] }), _jsxs("div", { className: "space-y-6 flex-1", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-sm font-medium text-gray-900 mb-2", children: "Translation Provider" }), _jsx("p", { className: "text-xs text-gray-600 mb-3", children: "Choose how to translate your content. \"Create Versions\" will create new language versions without automatic translation." }), _jsxs("select", { value: data.translationProvider || "", onChange: handleProviderChange, className: "block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm", "data-testid": "translation-provider-select", children: [_jsx("option", { value: "", disabled: true, children: "Select a provider..." }), data.translationProviders.map((provider) => (_jsx("option", { value: provider.name, children: provider.displayName }, provider.name)))] })] }), multiItemEnabled && (_jsxs("div", { children: [_jsxs("label", { className: "flex items-center", children: [_jsx("input", { type: "checkbox", checked: data.includeSubitems, onChange: handleSubitemsToggle, className: "h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded", "data-testid": "include-subitems-checkbox" }), _jsx("span", { className: "ml-2 text-sm text-gray-900", children: "Include subitems" })] }), _jsx("p", { className: "text-xs text-gray-500 mt-1 ml-6", children: "Also translate any child components and nested content within this item." })] })), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center justify-between mb-2", children: [_jsx("h3", { className: "text-sm font-medium text-gray-900", children: "Target Languages" }), allLanguages.length > 1 && (_jsxs("label", { className: "flex items-center cursor-pointer", children: [_jsx("input", { type: "checkbox", checked: areAllLanguagesSelected, ref: (input) => {
|
|
170
|
+
return (_jsxs("div", { className: "p-6 space-y-6 h-full flex flex-col", "data-testid": "service-language-selection-step", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-xl font-semibold mb-2", children: "Configure Translation" }), _jsx("p", { className: "text-sm text-gray-600 mb-6", children: "Select translation provider and target languages for your content." })] }), _jsxs("div", { className: "space-y-6 flex-1", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-sm font-medium text-gray-900 mb-2", children: "Translation Provider" }), _jsx("p", { className: "text-xs text-gray-600 mb-3", children: "Choose how to translate your content. \"Create Versions\" will create new language versions without automatic translation." }), _jsxs("select", { value: data.translationProvider || "", onChange: handleProviderChange, className: "block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm", "data-testid": "translation-provider-select", children: [_jsx("option", { value: "", disabled: true, children: "Select a provider..." }), data.translationProviders.map((provider) => (_jsx("option", { value: provider.name, children: provider.displayName || provider.name }, provider.name)))] })] }), multiItemEnabled && (_jsxs("div", { children: [_jsxs("label", { className: "flex items-center", children: [_jsx("input", { type: "checkbox", checked: data.includeSubitems, onChange: handleSubitemsToggle, className: "h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded", "data-testid": "include-subitems-checkbox" }), _jsx("span", { className: "ml-2 text-sm text-gray-900", children: "Include subitems" })] }), _jsx("p", { className: "text-xs text-gray-500 mt-1 ml-6", children: "Also translate any child components and nested content within this item." })] })), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center justify-between mb-2", children: [_jsx("h3", { className: "text-sm font-medium text-gray-900", children: "Target Languages" }), allLanguages.length > 1 && (_jsxs("label", { className: "flex items-center cursor-pointer", children: [_jsx("input", { type: "checkbox", checked: areAllLanguagesSelected, ref: (input) => {
|
|
114
171
|
if (input) {
|
|
115
172
|
input.indeterminate = areSomeLanguagesSelected && !areAllLanguagesSelected;
|
|
116
173
|
}
|
|
117
174
|
}, onChange: handleSelectAllLanguages, className: "h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded", "data-testid": "select-all-languages-checkbox" }), _jsx("span", { className: "ml-2 text-xs text-gray-600", children: "Select All" })] }))] }), _jsx("p", { className: "text-xs text-gray-600 mb-3", children: "Select the languages you want to translate this content into." }), _jsx("div", { className: "border border-gray-200 rounded-md min-h-[200px] max-h-64 overflow-y-auto", children: data.translationProviders.length === 0 || allLanguages.length === 0 ? (
|
|
118
175
|
// Loading skeleton
|
|
119
|
-
_jsx("div", { className: "p-3 space-y-2", children: [...Array(4)].map((_, i) => (_jsxs("div", { className: "flex items-center animate-pulse", children: [_jsx("div", { className: "h-4 w-4 bg-gray-200 rounded" }), _jsx("div", { className: "ml-2 h-4 bg-gray-200 rounded w-32" }), _jsx("div", { className: "ml-auto h-4 bg-gray-200 rounded w-16" })] }, i))) })) : (_jsx("div", { className: "p-3 grid grid-cols-1 gap-2", children: allLanguages.map((lang) => (_jsxs("label", { className: "flex items-center", children: [_jsx("input", { type: "checkbox", checked: languageSelection[lang.code] || false, onChange: () => handleLanguageToggle(lang.code), className: "h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded", "data-testid": `language-checkbox-${lang.code}` }), _jsxs("span", { className: "ml-2 text-sm text-gray-900", children: [lang.name, " (", lang.code, ")"] }), !lang.hasVersions && (_jsx("span", { className: "ml-auto text-xs text-orange-600 bg-orange-100 px-2 py-1 rounded", children: "No versions" }))] }, lang.code))) })) })] })] })] }));
|
|
176
|
+
_jsx("div", { className: "p-3 space-y-2", children: [...Array(4)].map((_, i) => (_jsxs("div", { className: "flex items-center animate-pulse", children: [_jsx("div", { className: "h-4 w-4 bg-gray-200 rounded" }), _jsx("div", { className: "ml-2 h-4 bg-gray-200 rounded w-32" }), _jsx("div", { className: "ml-auto h-4 bg-gray-200 rounded w-16" })] }, i))) })) : (_jsx("div", { className: "p-3 grid grid-cols-1 gap-2", children: allLanguages.map((lang) => (_jsxs("label", { className: "flex items-center", children: [_jsx("input", { type: "checkbox", checked: languageSelection[lang.code] || false, onChange: () => handleLanguageToggle(lang.code), className: "h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded", "data-testid": `language-checkbox-${lang.code}` }), _jsxs("span", { className: "ml-2 text-sm text-gray-900 flex items-center gap-2", children: [_jsxs("span", { children: [lang.name, " (", lang.code, ")"] }), lang.sourceLanguage && (_jsxs("span", { className: "text-xs text-gray-500", children: ["from: ", lang.sourceLanguage] }))] }), !lang.hasVersions && (_jsx("span", { className: "ml-auto text-xs text-orange-600 bg-orange-100 px-2 py-1 rounded", children: "No versions" }))] }, lang.code))) })) })] })] })] }));
|
|
120
177
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { TranslationStepProps } from "./types";
|
|
2
|
-
export declare function SubitemDiscoveryStep({ data, setData, editContext, onStepCompleted, setBeforeNextCallback, setFooterActions, requestClose }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function SubitemDiscoveryStep({ stepIndex, isActive, data, setData, editContext, onStepCompleted, setBeforeNextCallback, setFooterActions, requestClose }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=SubitemDiscoveryStep.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubitemDiscoveryStep.d.ts","sourceRoot":"","sources":["../../src/steps/SubitemDiscoveryStep.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"SubitemDiscoveryStep.d.ts","sourceRoot":"","sources":["../../src/steps/SubitemDiscoveryStep.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAW/C,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,QAAe,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,YAAY,EAAE,EAAE,oBAAoB,2CAqb5L"}
|