@parhelia/localization 0.1.10745
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/LICENSE +8 -0
- package/README.md +29 -0
- package/dist/core/src/editor/ui/DragPreview.d.ts +15 -0
- package/dist/core/src/editor/ui/DragPreview.d.ts.map +1 -0
- package/dist/core/src/editor/ui/DragPreview.js +32 -0
- package/dist/core/src/editor/ui/PerfectTree.d.ts +79 -0
- package/dist/core/src/editor/ui/PerfectTree.d.ts.map +1 -0
- package/dist/core/src/editor/ui/PerfectTree.js +857 -0
- package/dist/localization/src/LocalizeItemCommand.d.ts +8 -0
- package/dist/localization/src/LocalizeItemCommand.d.ts.map +1 -0
- package/dist/localization/src/LocalizeItemCommand.js +44 -0
- package/dist/localization/src/LocalizeItemDialog.d.ts +4 -0
- package/dist/localization/src/LocalizeItemDialog.d.ts.map +1 -0
- package/dist/localization/src/LocalizeItemDialog.js +126 -0
- package/dist/localization/src/LocalizeItemUtils.d.ts +17 -0
- package/dist/localization/src/LocalizeItemUtils.d.ts.map +1 -0
- package/dist/localization/src/LocalizeItemUtils.js +93 -0
- package/dist/localization/src/api/discovery.d.ts +36 -0
- package/dist/localization/src/api/discovery.d.ts.map +1 -0
- package/dist/localization/src/api/discovery.js +29 -0
- package/dist/localization/src/constants.d.ts +15 -0
- package/dist/localization/src/constants.d.ts.map +1 -0
- package/dist/localization/src/constants.js +21 -0
- package/dist/localization/src/hooks/useTranslationWizard.d.ts +6 -0
- package/dist/localization/src/hooks/useTranslationWizard.d.ts.map +1 -0
- package/dist/localization/src/hooks/useTranslationWizard.js +78 -0
- package/dist/localization/src/index.d.ts +69 -0
- package/dist/localization/src/index.d.ts.map +1 -0
- package/dist/localization/src/index.js +152 -0
- package/dist/localization/src/services/translationService.d.ts +102 -0
- package/dist/localization/src/services/translationService.d.ts.map +1 -0
- package/dist/localization/src/services/translationService.js +37 -0
- package/dist/localization/src/setup/LocalizationSetupStep.d.ts +3 -0
- package/dist/localization/src/setup/LocalizationSetupStep.d.ts.map +1 -0
- package/dist/localization/src/setup/LocalizationSetupStep.js +108 -0
- package/dist/localization/src/sidebar/TranslationSidebar.d.ts +2 -0
- package/dist/localization/src/sidebar/TranslationSidebar.d.ts.map +1 -0
- package/dist/localization/src/sidebar/TranslationSidebar.js +93 -0
- package/dist/localization/src/steps/MetadataInputStep.d.ts +4 -0
- package/dist/localization/src/steps/MetadataInputStep.d.ts.map +1 -0
- package/dist/localization/src/steps/MetadataInputStep.js +38 -0
- package/dist/localization/src/steps/ServiceLanguageSelectionStep.d.ts +3 -0
- package/dist/localization/src/steps/ServiceLanguageSelectionStep.d.ts.map +1 -0
- package/dist/localization/src/steps/ServiceLanguageSelectionStep.js +91 -0
- package/dist/localization/src/steps/SubitemDiscoveryStep.d.ts +3 -0
- package/dist/localization/src/steps/SubitemDiscoveryStep.d.ts.map +1 -0
- package/dist/localization/src/steps/SubitemDiscoveryStep.js +391 -0
- package/dist/localization/src/steps/index.d.ts +5 -0
- package/dist/localization/src/steps/index.d.ts.map +1 -0
- package/dist/localization/src/steps/index.js +4 -0
- package/dist/localization/src/steps/types.d.ts +68 -0
- package/dist/localization/src/steps/types.d.ts.map +1 -0
- package/dist/localization/src/steps/types.js +1 -0
- package/dist/localization/src/translation-center/BatchTranslationView.d.ts +7 -0
- package/dist/localization/src/translation-center/BatchTranslationView.d.ts.map +1 -0
- package/dist/localization/src/translation-center/BatchTranslationView.js +487 -0
- package/dist/localization/src/translation-center/RecentTranslations.d.ts +2 -0
- package/dist/localization/src/translation-center/RecentTranslations.d.ts.map +1 -0
- package/dist/localization/src/translation-center/RecentTranslations.js +199 -0
- package/dist/localization/src/translation-center/TranslationManagement.d.ts +2 -0
- package/dist/localization/src/translation-center/TranslationManagement.d.ts.map +1 -0
- package/dist/localization/src/translation-center/TranslationManagement.js +25 -0
- package/dist/localization/src/types.d.ts +18 -0
- package/dist/localization/src/types.d.ts.map +1 -0
- package/dist/localization/src/types.js +1 -0
- package/dist/localization/src/utils/createVersions.d.ts +14 -0
- package/dist/localization/src/utils/createVersions.d.ts.map +1 -0
- package/dist/localization/src/utils/createVersions.js +26 -0
- package/package.json +47 -0
- package/styles.css +1 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Command, CommandContext, CommandData, FullItem } from "@parhelia/core";
|
|
2
|
+
export type LocalizeItemCommandData = CommandData & {
|
|
3
|
+
items: FullItem[];
|
|
4
|
+
};
|
|
5
|
+
export type LocalizeItemCommandContext = CommandContext<LocalizeItemCommandData>;
|
|
6
|
+
export type LocalizeItemCommand = Command<LocalizeItemCommandData>;
|
|
7
|
+
export declare const localizeItemCommand: LocalizeItemCommand;
|
|
8
|
+
//# sourceMappingURL=LocalizeItemCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LocalizeItemCommand.d.ts","sourceRoot":"","sources":["../../../src/LocalizeItemCommand.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAMhF,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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Globe } from "lucide-react";
|
|
3
|
+
import { LocalizeItemDialog } from "./LocalizeItemDialog";
|
|
4
|
+
export const localizeItemCommand = {
|
|
5
|
+
id: "localizeItem",
|
|
6
|
+
label: "Localize",
|
|
7
|
+
icon: _jsx(Globe, { strokeWidth: 1 }),
|
|
8
|
+
disabled: (context) => {
|
|
9
|
+
if (!context.data?.items || context.data.items.length === 0) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
// Check if multi-item is disabled and multiple items are selected
|
|
13
|
+
const multiItemEnabled = context.editContext?.configuration?.localization?.multiItem !== false;
|
|
14
|
+
if (!multiItemEnabled && context.data.items.length > 1) {
|
|
15
|
+
return true; // Disable command when multiple items selected in single-item mode
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
},
|
|
19
|
+
execute: async (context) => {
|
|
20
|
+
const items = context.data?.items;
|
|
21
|
+
if (!items || !items?.length) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const result = await context.openDialog(LocalizeItemDialog, {
|
|
25
|
+
items: items,
|
|
26
|
+
editContext: context.editContext,
|
|
27
|
+
});
|
|
28
|
+
// If translation was started (result contains batchId), navigate to the batch view
|
|
29
|
+
if (result?.batchId) {
|
|
30
|
+
const translationManagementEnabled = context.editContext?.configuration?.localization?.translationManagement !== false;
|
|
31
|
+
if (translationManagementEnabled) {
|
|
32
|
+
// Synchronous URL update to avoid race with view URL sync
|
|
33
|
+
const params = new URLSearchParams(window.location.search);
|
|
34
|
+
params.set("view", "translation-management");
|
|
35
|
+
params.set("batchId", result.batchId);
|
|
36
|
+
const newUrl = `${window.location.pathname}?${params.toString()}`;
|
|
37
|
+
window.history.pushState(null, "", newUrl);
|
|
38
|
+
// Now switch the view; URL already contains batchId
|
|
39
|
+
context.editContext.switchView("translation-management");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { DialogProps } from "@parhelia/core";
|
|
2
|
+
import { TranslationDialogResult, LocalizeItemDialogProps } from "./steps/types";
|
|
3
|
+
export declare function LocalizeItemDialog(props: LocalizeItemDialogProps & DialogProps<TranslationDialogResult>): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
//# sourceMappingURL=LocalizeItemDialog.d.ts.map
|
|
@@ -0,0 +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,EACxB,MAAM,eAAe,CAAC;AAYvB,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,uBAAuB,GAAG,WAAW,CAAC,uBAAuB,CAAC,2CA4PtE"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle, Button, cn } from "@parhelia/core";
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
+
import { ChevronRight } from "lucide-react";
|
|
5
|
+
import { useTranslationWizard } from "./hooks/useTranslationWizard";
|
|
6
|
+
import { performDefaultTranslation, generateBatchId } from "./LocalizeItemUtils";
|
|
7
|
+
// Note: DialogButtons is an internal component that might need to be added to core exports
|
|
8
|
+
// For now, we'll implement it inline
|
|
9
|
+
const DialogButtons = ({ children, ...props }) => (_jsx("div", { className: "flex justify-end gap-2 pt-4 border-t mt-auto px-6 pb-6", ...props, children: children }));
|
|
10
|
+
export function LocalizeItemDialog(props) {
|
|
11
|
+
const editContext = props.editContext;
|
|
12
|
+
const configuration = editContext.configuration.translationWizard;
|
|
13
|
+
const [currentStepIndex, setCurrentStepIndex] = useState(0);
|
|
14
|
+
const [completedByStepId, setCompletedByStepId] = useState({});
|
|
15
|
+
const [beforeNextCallback, setBeforeNextCallback] = useState(null);
|
|
16
|
+
const { wizardData, setWizardData } = useTranslationWizard(props);
|
|
17
|
+
// Keep a ref to the latest wizard data to avoid stale closure issues
|
|
18
|
+
const latestWizardDataRef = useRef(wizardData);
|
|
19
|
+
latestWizardDataRef.current = wizardData;
|
|
20
|
+
// Clear footer actions when step changes to avoid stale actions from previous steps
|
|
21
|
+
const [footerActions, setFooterActions] = useState([]);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
setFooterActions([]);
|
|
24
|
+
}, [currentStepIndex]);
|
|
25
|
+
// Provide stable callbacks to steps to avoid update loops
|
|
26
|
+
const lastActionsSigRef = useRef("");
|
|
27
|
+
const provideFooterActions = useCallback((actions) => {
|
|
28
|
+
const normalized = (actions || []).map(a => ({ key: a.key, label: a.label, disabled: !!a.disabled, signature: a.signature || "" }));
|
|
29
|
+
const signature = JSON.stringify(normalized);
|
|
30
|
+
if (signature === lastActionsSigRef.current)
|
|
31
|
+
return;
|
|
32
|
+
lastActionsSigRef.current = signature;
|
|
33
|
+
setFooterActions(actions || []);
|
|
34
|
+
}, []);
|
|
35
|
+
const requestCloseCb = useCallback((result) => {
|
|
36
|
+
props.onClose?.(result);
|
|
37
|
+
}, [props.onClose]);
|
|
38
|
+
// Filter steps based on skip conditions
|
|
39
|
+
const activeSteps = configuration.steps.filter((step) => !step.skipCondition || !step.skipCondition(wizardData));
|
|
40
|
+
const currentStep = activeSteps[currentStepIndex];
|
|
41
|
+
const isLastStep = currentStepIndex === activeSteps.length - 1;
|
|
42
|
+
const canProceed = !!(currentStep && completedByStepId[currentStep.id]);
|
|
43
|
+
const handleNext = async () => {
|
|
44
|
+
if (beforeNextCallback && typeof beforeNextCallback === 'function') {
|
|
45
|
+
const canProceed = await beforeNextCallback();
|
|
46
|
+
if (!canProceed)
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (currentStep?.beforeNext) {
|
|
50
|
+
const canProceed = await currentStep.beforeNext(wizardData, editContext);
|
|
51
|
+
if (!canProceed)
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (isLastStep) {
|
|
55
|
+
await handleFinish();
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
setCurrentStepIndex(currentStepIndex + 1);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const handlePrevious = () => {
|
|
62
|
+
if (currentStepIndex > 0) {
|
|
63
|
+
// Clear the callback when moving to previous step
|
|
64
|
+
setBeforeNextCallback(null);
|
|
65
|
+
setCurrentStepIndex(currentStepIndex - 1);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const handleFinish = useCallback(async () => {
|
|
69
|
+
try {
|
|
70
|
+
// Use the ref to get the absolute latest wizard data
|
|
71
|
+
const currentWizardData = latestWizardDataRef.current;
|
|
72
|
+
const result = {
|
|
73
|
+
translationProvider: currentWizardData.translationProvider,
|
|
74
|
+
targetLanguages: currentWizardData.targetLanguages,
|
|
75
|
+
includeSubitems: currentWizardData.includeSubitems,
|
|
76
|
+
discoveredItems: currentWizardData.discoveredItems,
|
|
77
|
+
};
|
|
78
|
+
if (currentWizardData.translationProvider === "Create Versions") {
|
|
79
|
+
props.onClose?.(result);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
try {
|
|
83
|
+
const batchId = generateBatchId();
|
|
84
|
+
const translationResult = await performDefaultTranslation(currentWizardData, editContext, batchId);
|
|
85
|
+
console.log("Translation completed successfully:", translationResult);
|
|
86
|
+
// Include batchId in result for navigation
|
|
87
|
+
const resultWithBatch = { ...result, batchId };
|
|
88
|
+
props.onClose?.(resultWithBatch);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
console.error("Translation failed:", error);
|
|
92
|
+
// Still close the dialog to show results
|
|
93
|
+
props.onClose?.(result);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.error("Error in handleFinish:", error);
|
|
99
|
+
props.onClose?.(null);
|
|
100
|
+
}
|
|
101
|
+
}, [editContext, props]);
|
|
102
|
+
const handleStepCompleted = useCallback((completed) => {
|
|
103
|
+
if (!currentStep)
|
|
104
|
+
return;
|
|
105
|
+
setCompletedByStepId(prev => ({ ...prev, [currentStep.id]: completed }));
|
|
106
|
+
}, [currentStep?.id]);
|
|
107
|
+
const StepComponent = currentStep?.component;
|
|
108
|
+
return (_jsx(Dialog, { open: true, onOpenChange: (open) => {
|
|
109
|
+
// Only close when explicitly requested (e.g., close button clicked)
|
|
110
|
+
if (!open) {
|
|
111
|
+
props.onClose?.(null);
|
|
112
|
+
}
|
|
113
|
+
}, children: _jsxs(DialogContent, { className: "w-[90vw] max-w-5xl h-[85vh] max-h-[900px] min-h-[700px] overflow-hidden flex flex-col", "data-testid": "translation-wizard-dialog", onPointerDownOutside: (e) => e.preventDefault(), onEscapeKeyDown: (e) => e.preventDefault(), "aria-describedby": "translation-wizard-description", style: {
|
|
114
|
+
width: 'min(90vw, 1280px)',
|
|
115
|
+
height: 'min(85vh, 900px)',
|
|
116
|
+
minHeight: '700px'
|
|
117
|
+
}, children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { "data-testid": "translation-wizard-title", children: "Translation Wizard" }), _jsx("div", { id: "translation-wizard-description", className: "sr-only", children: currentStep?.description || "Configure and start translation for your content." })] }), _jsxs("div", { className: "flex flex-1 flex-col min-h-0", children: [_jsx("div", { className: "border-b border-gray-300 bg-white px-4", "data-testid": "translation-wizard-step-navigation", children: _jsx("div", { className: "flex items-center gap-3 py-2", children: activeSteps.map((step, index) => (_jsxs("div", { className: "flex items-center gap-2", "data-testid": `step-indicator-${step.id}`, children: [_jsx("div", { className: cn("flex h-8 w-8 items-center justify-center rounded-full border text-sm font-medium", currentStepIndex === index &&
|
|
118
|
+
"border-blue-500 text-blue-500 bg-blue-50", currentStepIndex < index && "border-gray-300 text-gray-400", currentStepIndex > index &&
|
|
119
|
+
"bg-blue-500 text-white border-blue-500"), "data-testid": `step-indicator-circle-${step.id}`, children: currentStepIndex > index ? "✓" : index + 1 }), _jsx("span", { className: cn("text-sm", currentStepIndex === index
|
|
120
|
+
? "text-blue-600 font-medium"
|
|
121
|
+
: currentStepIndex > index
|
|
122
|
+
? "text-blue-600"
|
|
123
|
+
: "text-gray-400"), "data-testid": `step-indicator-label-${step.id}`, children: step.name }), index < activeSteps.length - 1 && (_jsx(ChevronRight, { 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: StepComponent && (_jsx("div", { className: "h-full", children: _jsx(StepComponent, { data: wizardData, setData: setWizardData, editContext: editContext, onStepCompleted: handleStepCompleted, setBeforeNextCallback: (cb) => {
|
|
124
|
+
setBeforeNextCallback((prev) => (prev === cb ? prev : cb));
|
|
125
|
+
}, setFooterActions: provideFooterActions, requestClose: requestCloseCb }) })) }), _jsxs(DialogButtons, { "data-testid": "translation-wizard-dialog-buttons", children: [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))), currentStepIndex > 0 && (_jsx(Button, { onClick: handlePrevious, variant: "outline", size: "default", "data-testid": "translation-wizard-previous-button", children: "Previous" })), _jsx(Button, { onClick: handleNext, disabled: !canProceed, variant: "default", size: "default", "data-testid": "translation-wizard-next-button", children: isLastStep ? "Start Translation" : "Next" }), _jsx(Button, { onClick: () => props.onClose?.(null), variant: "outline", size: "default", "data-testid": "translation-wizard-cancel-button", children: "Cancel" })] })] })] }) }));
|
|
126
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { TranslationStatus } from "./types";
|
|
2
|
+
import { FullItem } from "@parhelia/core";
|
|
3
|
+
import { TranslationWizardData } from "./steps/types";
|
|
4
|
+
import { EditContextType } from "@parhelia/core";
|
|
5
|
+
export declare function generateBatchId(): string;
|
|
6
|
+
export type StartedTranslation = {
|
|
7
|
+
itemId: string;
|
|
8
|
+
targetLanguage: string;
|
|
9
|
+
jobId?: string;
|
|
10
|
+
};
|
|
11
|
+
export type TranslationResult = {
|
|
12
|
+
started: StartedTranslation[];
|
|
13
|
+
batchId?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function performDefaultTranslation(wizardData: TranslationWizardData, editContext: EditContextType, predefinedBatchId?: string): Promise<TranslationResult>;
|
|
16
|
+
export declare const defaultTranslateAll: (languageCodes: string[], translationStatus: TranslationStatus[], sessionId: string, item: FullItem, translationProvider: string) => Promise<TranslationResult>;
|
|
17
|
+
//# sourceMappingURL=LocalizeItemUtils.d.ts.map
|
|
@@ -0,0 +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,CAqE5B;AAGD,eAAO,MAAM,mBAAmB,GAAU,eAAe,MAAM,EAAE,EAAE,mBAAmB,iBAAiB,EAAE,EAAE,WAAW,MAAM,EAAE,MAAM,QAAQ,EAAE,qBAAqB,MAAM,+BA0BxK,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
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)}`;
|
|
5
|
+
}
|
|
6
|
+
// Unified translation function that handles both single and batch translations
|
|
7
|
+
// 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;
|
|
13
|
+
// Prepare all translation requests
|
|
14
|
+
const translationRequests = [];
|
|
15
|
+
for (const item of itemsToTranslate) {
|
|
16
|
+
for (const language of wizardData.targetLanguages) {
|
|
17
|
+
const languageInfo = wizardData.languageData.get(language);
|
|
18
|
+
if (!languageInfo?.translationStatus)
|
|
19
|
+
continue;
|
|
20
|
+
const status = languageInfo.translationStatus;
|
|
21
|
+
if (status.sourceLanguage === language)
|
|
22
|
+
continue;
|
|
23
|
+
const translationItem = {
|
|
24
|
+
itemId: item.descriptor.id,
|
|
25
|
+
targetLanguage: language,
|
|
26
|
+
sourceLanguage: status.sourceLanguage || "en",
|
|
27
|
+
};
|
|
28
|
+
// Get metadata for this specific item and language
|
|
29
|
+
const itemLanguageMetadata = wizardData.itemMetadata.get(item.descriptor.id)?.get(language);
|
|
30
|
+
if (itemLanguageMetadata) {
|
|
31
|
+
translationItem.metadata = itemLanguageMetadata;
|
|
32
|
+
}
|
|
33
|
+
translationRequests.push(translationItem);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const batchResult = await requestBatchTranslation({
|
|
38
|
+
sessionId: editContext.sessionId,
|
|
39
|
+
batchId,
|
|
40
|
+
provider: wizardData.translationProvider,
|
|
41
|
+
batchMetadata: wizardData.metadata,
|
|
42
|
+
translations: translationRequests,
|
|
43
|
+
});
|
|
44
|
+
if (batchResult.type === "error") {
|
|
45
|
+
console.error('Translation request failed:', batchResult);
|
|
46
|
+
return { started: [], batchId };
|
|
47
|
+
}
|
|
48
|
+
const batchResponse = batchResult.data;
|
|
49
|
+
if (!batchResponse) {
|
|
50
|
+
console.error('Batch response is undefined');
|
|
51
|
+
return { started: [], batchId };
|
|
52
|
+
}
|
|
53
|
+
const started = batchResponse.started?.map((result) => ({
|
|
54
|
+
itemId: result.itemId,
|
|
55
|
+
targetLanguage: result.targetLanguage,
|
|
56
|
+
jobId: result.jobId,
|
|
57
|
+
})) || [];
|
|
58
|
+
if (batchResponse.skipped && batchResponse.skipped.length > 0) {
|
|
59
|
+
console.warn('Some translations were skipped:', batchResponse.skipped);
|
|
60
|
+
}
|
|
61
|
+
return { started, batchId };
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error('Translation request failed:', error);
|
|
65
|
+
return { started: [], batchId };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Legacy function for backward compatibility
|
|
69
|
+
export const defaultTranslateAll = async (languageCodes, translationStatus, sessionId, item, translationProvider) => {
|
|
70
|
+
// Create a minimal wizardData-like object for the legacy function
|
|
71
|
+
const wizardData = {
|
|
72
|
+
items: [item],
|
|
73
|
+
targetLanguages: languageCodes,
|
|
74
|
+
languageData: new Map(languageCodes.map(lang => {
|
|
75
|
+
const status = translationStatus?.find(x => x.targetLanguage === lang);
|
|
76
|
+
return [lang, {
|
|
77
|
+
name: lang,
|
|
78
|
+
languageCode: lang,
|
|
79
|
+
items: [item.descriptor],
|
|
80
|
+
translationStatus: status
|
|
81
|
+
}];
|
|
82
|
+
})),
|
|
83
|
+
translationProvider,
|
|
84
|
+
includeSubitems: false,
|
|
85
|
+
discoveredItems: [],
|
|
86
|
+
itemMetadata: new Map(),
|
|
87
|
+
metadata: null,
|
|
88
|
+
translationProviders: []
|
|
89
|
+
};
|
|
90
|
+
const editContext = { sessionId };
|
|
91
|
+
const result = await performDefaultTranslation(wizardData, editContext);
|
|
92
|
+
return result;
|
|
93
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type DiscoveredItem = {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
path: string;
|
|
5
|
+
parentId?: string | null;
|
|
6
|
+
hasChildren: boolean;
|
|
7
|
+
hasLayout?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type TreeNode = {
|
|
10
|
+
key: string;
|
|
11
|
+
label: string;
|
|
12
|
+
data: DiscoveredItem;
|
|
13
|
+
children?: TreeNode[];
|
|
14
|
+
};
|
|
15
|
+
export type BackendTreeNode = {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
path: string;
|
|
19
|
+
hasLayout: boolean;
|
|
20
|
+
isFolder: boolean;
|
|
21
|
+
icon?: string;
|
|
22
|
+
children: BackendTreeNode[];
|
|
23
|
+
};
|
|
24
|
+
export type DiscoverItemsTreeRequest = {
|
|
25
|
+
rootItemIds: string[];
|
|
26
|
+
language?: string;
|
|
27
|
+
};
|
|
28
|
+
export type DiscoverItemsTreeResponse = {
|
|
29
|
+
trees: BackendTreeNode[];
|
|
30
|
+
totalIncluded: number;
|
|
31
|
+
isComplete: boolean;
|
|
32
|
+
};
|
|
33
|
+
export declare function discoverItemsTree(req: DiscoverItemsTreeRequest, sessionId?: string): Promise<DiscoverItemsTreeResponse>;
|
|
34
|
+
export declare function convertBackendTreeToTreeNodes(trees: BackendTreeNode[]): TreeNode[];
|
|
35
|
+
export declare function flattenPagesFromBackendTrees(trees: BackendTreeNode[]): DiscoveredItem[];
|
|
36
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +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;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,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,SAAS,EAAE,OAAO,CAAC;IACnB,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,4BAA4B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,cAAc,EAAE,CAQvF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { post } from "@parhelia/core";
|
|
2
|
+
export async function discoverItemsTree(req, sessionId) {
|
|
3
|
+
const { data, type, details } = await post("/parhelia/translation/discoveritemstree", {
|
|
4
|
+
RootItemIds: req.rootItemIds,
|
|
5
|
+
Language: req.language,
|
|
6
|
+
}, sessionId);
|
|
7
|
+
if (type !== "success" || !data)
|
|
8
|
+
throw new Error(details || "Failed to discover items tree");
|
|
9
|
+
return data;
|
|
10
|
+
}
|
|
11
|
+
export function convertBackendTreeToTreeNodes(trees) {
|
|
12
|
+
const convert = (n) => ({
|
|
13
|
+
key: n.id,
|
|
14
|
+
label: n.name,
|
|
15
|
+
data: { id: n.id, name: n.name, path: n.path, hasLayout: n.hasLayout, icon: n.icon, parentId: undefined, hasChildren: (n.children?.length ?? 0) > 0 },
|
|
16
|
+
children: n.children?.map(convert) || [],
|
|
17
|
+
});
|
|
18
|
+
return trees.map(convert);
|
|
19
|
+
}
|
|
20
|
+
export function flattenPagesFromBackendTrees(trees) {
|
|
21
|
+
const out = [];
|
|
22
|
+
const walk = (n) => {
|
|
23
|
+
if (n.hasLayout)
|
|
24
|
+
out.push({ id: n.id, name: n.name, path: n.path, hasChildren: (n.children?.length ?? 0) > 0, parentId: undefined });
|
|
25
|
+
n.children?.forEach(walk);
|
|
26
|
+
};
|
|
27
|
+
trees.forEach(walk);
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const DISCOVERY_CONFIRMATION_THRESHOLD = 100;
|
|
2
|
+
export declare const EXCLUDED_TEMPLATE_IDS: string[];
|
|
3
|
+
export declare const DIALOG_DIMENSIONS: {
|
|
4
|
+
width: string;
|
|
5
|
+
height: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const STEP_STATES: {
|
|
8
|
+
readonly COMPLETED: "completed";
|
|
9
|
+
readonly CURRENT: "current";
|
|
10
|
+
readonly PENDING: "pending";
|
|
11
|
+
};
|
|
12
|
+
export declare const STORAGE_KEYS: {
|
|
13
|
+
readonly TRANSLATION_PROVIDER: "editor.translationProvider";
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,gCAAgC,MAAM,CAAC;AAGpD,eAAO,MAAM,qBAAqB,UAEjC,CAAC;AAGF,eAAO,MAAM,iBAAiB;;;CAG7B,CAAC;AAGF,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAGX,eAAO,MAAM,YAAY;;CAEf,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Discovery configuration
|
|
2
|
+
export const DISCOVERY_CONFIRMATION_THRESHOLD = 100; // Pause every N subitems
|
|
3
|
+
// Template IDs to exclude from translation
|
|
4
|
+
export const EXCLUDED_TEMPLATE_IDS = [
|
|
5
|
+
"de8bfdac-5f9f-40c3-a680-2fce9997ae28" // System template ID
|
|
6
|
+
];
|
|
7
|
+
// Dialog dimensions
|
|
8
|
+
export const DIALOG_DIMENSIONS = {
|
|
9
|
+
width: "75vw",
|
|
10
|
+
height: "75vh"
|
|
11
|
+
};
|
|
12
|
+
// Step progress indicators
|
|
13
|
+
export const STEP_STATES = {
|
|
14
|
+
COMPLETED: "completed",
|
|
15
|
+
CURRENT: "current",
|
|
16
|
+
PENDING: "pending"
|
|
17
|
+
};
|
|
18
|
+
// Local storage keys
|
|
19
|
+
export const STORAGE_KEYS = {
|
|
20
|
+
TRANSLATION_PROVIDER: "editor.translationProvider"
|
|
21
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { TranslationWizardData, LocalizeItemDialogProps } from "../steps/types";
|
|
2
|
+
export declare function useTranslationWizard(props: LocalizeItemDialogProps): {
|
|
3
|
+
wizardData: TranslationWizardData;
|
|
4
|
+
setWizardData: import("react").Dispatch<import("react").SetStateAction<TranslationWizardData>>;
|
|
5
|
+
};
|
|
6
|
+
//# sourceMappingURL=useTranslationWizard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTranslationWizard.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTranslationWizard.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAMhF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,uBAAuB;;;EA8FlE"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
import { getLanguagesAndVersions } from "@parhelia/core";
|
|
3
|
+
import { getTranslationStatus, getTranslationProviders } from "../services/translationService";
|
|
4
|
+
export function useTranslationWizard(props) {
|
|
5
|
+
const [wizardData, setWizardData] = useState({
|
|
6
|
+
items: props.items,
|
|
7
|
+
targetLanguages: [],
|
|
8
|
+
translationProvider: "",
|
|
9
|
+
includeSubitems: false,
|
|
10
|
+
discoveredItems: [],
|
|
11
|
+
languageData: new Map(),
|
|
12
|
+
translationProviders: [],
|
|
13
|
+
itemMetadata: new Map(), // Initialize as Map<string, Map<string, string>>
|
|
14
|
+
metadata: null, // Initialize with test value
|
|
15
|
+
});
|
|
16
|
+
// Load initial data
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const loadInitialData = async () => {
|
|
19
|
+
const itemDescriptors = props.items.map((x) => x.descriptor);
|
|
20
|
+
// Load translation providers
|
|
21
|
+
const providersResult = await getTranslationProviders();
|
|
22
|
+
const providers = providersResult.data || [];
|
|
23
|
+
if (providers.length > 0) {
|
|
24
|
+
const savedProvider = localStorage.getItem("editor.translationProvider");
|
|
25
|
+
// Validate that saved provider exists in current providers by name
|
|
26
|
+
const validProvider = savedProvider && providers.find(p => p.name === savedProvider)
|
|
27
|
+
? savedProvider
|
|
28
|
+
: providers[0]?.name || "";
|
|
29
|
+
const defaultProvider = validProvider;
|
|
30
|
+
// Load languages for each item
|
|
31
|
+
const itemLanguages = await Promise.all(itemDescriptors.map(async (item) => {
|
|
32
|
+
const result = await getLanguagesAndVersions(item);
|
|
33
|
+
return result.data?.languages || [];
|
|
34
|
+
}));
|
|
35
|
+
// Load translation status for each item
|
|
36
|
+
const translationStatuses = await Promise.all(itemDescriptors.map(async (item) => {
|
|
37
|
+
const result = await getTranslationStatus(item.id);
|
|
38
|
+
return result.data || [];
|
|
39
|
+
}));
|
|
40
|
+
const languageMap = new Map();
|
|
41
|
+
itemLanguages.forEach((languages, itemIndex) => {
|
|
42
|
+
languages.forEach((language) => {
|
|
43
|
+
const languageData = languageMap.get(language.languageCode) || {
|
|
44
|
+
name: language.name,
|
|
45
|
+
languageCode: language.languageCode,
|
|
46
|
+
items: [],
|
|
47
|
+
};
|
|
48
|
+
if (language.versions > 0 && itemDescriptors[itemIndex]) {
|
|
49
|
+
languageData.items.push(itemDescriptors[itemIndex]);
|
|
50
|
+
}
|
|
51
|
+
const status = translationStatuses[itemIndex]?.find((s) => s.targetLanguage === language.languageCode);
|
|
52
|
+
if (status && !languageData.translationStatus) {
|
|
53
|
+
languageData.translationStatus = status;
|
|
54
|
+
}
|
|
55
|
+
languageMap.set(language.languageCode, languageData);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
setWizardData(prevData => ({
|
|
59
|
+
...prevData,
|
|
60
|
+
translationProviders: providers,
|
|
61
|
+
translationProvider: defaultProvider,
|
|
62
|
+
languageData: languageMap,
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
loadInitialData();
|
|
67
|
+
}, [props.items]);
|
|
68
|
+
// Save translation provider to localStorage when it changes
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (wizardData.translationProvider) {
|
|
71
|
+
localStorage.setItem("editor.translationProvider", wizardData.translationProvider);
|
|
72
|
+
}
|
|
73
|
+
}, [wizardData.translationProvider]);
|
|
74
|
+
return {
|
|
75
|
+
wizardData,
|
|
76
|
+
setWizardData,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ServiceLanguageSelectionStep } from "./steps/ServiceLanguageSelectionStep";
|
|
2
|
+
import { SubitemDiscoveryStep } from "./steps/SubitemDiscoveryStep";
|
|
3
|
+
import { TranslationWizardData } from "./steps/types";
|
|
4
|
+
import { EditorConfiguration } from "@parhelia/core";
|
|
5
|
+
declare const DEFAULT_TRANSLATION_WIZARD_CONFIGURATION: {
|
|
6
|
+
steps: ({
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
component: typeof ServiceLanguageSelectionStep;
|
|
11
|
+
skipCondition?: undefined;
|
|
12
|
+
} | {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
component: typeof SubitemDiscoveryStep;
|
|
17
|
+
skipCondition: (data: TranslationWizardData) => boolean;
|
|
18
|
+
})[];
|
|
19
|
+
};
|
|
20
|
+
declare const SINGLE_ITEM_TRANSLATION_WIZARD_CONFIGURATION: {
|
|
21
|
+
steps: {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
description: string;
|
|
25
|
+
component: typeof ServiceLanguageSelectionStep;
|
|
26
|
+
}[];
|
|
27
|
+
};
|
|
28
|
+
export { DEFAULT_TRANSLATION_WIZARD_CONFIGURATION, SINGLE_ITEM_TRANSLATION_WIZARD_CONFIGURATION };
|
|
29
|
+
export { LocalizeItemDialog } from "./LocalizeItemDialog";
|
|
30
|
+
export { localizeItemCommand } from "./LocalizeItemCommand";
|
|
31
|
+
export { TranslationManagement } from "./translation-center/TranslationManagement";
|
|
32
|
+
export { RecentTranslations } from "./translation-center/RecentTranslations";
|
|
33
|
+
export { BatchTranslationView } from "./translation-center/BatchTranslationView";
|
|
34
|
+
export { LocalizationSetupStep } from "./setup/LocalizationSetupStep";
|
|
35
|
+
export type { LocalizeItemCommand, LocalizeItemCommandContext, LocalizeItemCommandData } from "./LocalizeItemCommand";
|
|
36
|
+
export { defaultTranslateAll, generateBatchId, performDefaultTranslation, type StartedTranslation, type TranslationResult } from "./LocalizeItemUtils";
|
|
37
|
+
export * from "./steps/types";
|
|
38
|
+
export type { TranslationProviderInfo, TranslationStatus } from "./steps/types";
|
|
39
|
+
export type LocalizationConfigOptions = {
|
|
40
|
+
/**
|
|
41
|
+
* Enable multi-item translation (allows translating multiple items and their subitems).
|
|
42
|
+
* When false, restricts translation to single items only.
|
|
43
|
+
* @default true
|
|
44
|
+
*/
|
|
45
|
+
multiItem?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Enable Translation Management view for tracking and managing translation batches.
|
|
48
|
+
* @default true
|
|
49
|
+
*/
|
|
50
|
+
translationManagement?: boolean;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Configures the localization extension for the editor.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* // Full-featured (default): Multi-item translation with subitem discovery
|
|
57
|
+
* configureLocalization(configuration);
|
|
58
|
+
* // or explicitly:
|
|
59
|
+
* configureLocalization(configuration, { multiItem: true });
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* // Single-item only: Restricts translation to one item at a time
|
|
63
|
+
* configureLocalization(configuration, { multiItem: false });
|
|
64
|
+
*
|
|
65
|
+
* @param configuration - The editor configuration object
|
|
66
|
+
* @param options - Optional configuration for localization features
|
|
67
|
+
*/
|
|
68
|
+
export declare function configureLocalization(configuration: EditorConfiguration, options?: LocalizationConfigOptions): EditorConfiguration;
|
|
69
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAOtD,OAAO,EAAE,mBAAmB,EAAsB,MAAM,gBAAgB,CAAC;AAEzE,QAAA,MAAM,wCAAwC;;;;;;;;;;;;8BAalB,qBAAqB;;CAGhD,CAAC;AAEF,QAAA,MAAM,4CAA4C;;;;;;;CAUjD,CAAC;AAEF,OAAO,EAAE,wCAAwC,EAAE,4CAA4C,EAAE,CAAC;AAClG,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,yBAAyB,EACzB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,qBAAqB,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEhF,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,mBAAmB,EAClC,OAAO,CAAC,EAAE,yBAAyB,uBAyGpC"}
|