@parhelia/localization 0.1.12902 → 0.1.12903
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/dist/LocalizeItemCommand.d.ts +7 -1
- package/dist/LocalizeItemCommand.d.ts.map +1 -1
- package/dist/LocalizeItemCommand.js +29 -18
- package/dist/LocalizeItemDialog.js +1 -1
- package/dist/steps/ServiceLanguageSelectionStep.d.ts.map +1 -1
- package/dist/steps/ServiceLanguageSelectionStep.js +39 -5
- package/dist/translation-center/TranslationBatches.d.ts.map +1 -1
- package/dist/translation-center/TranslationBatches.js +22 -2
- package/dist/translation-center/TranslationsTitlebar.d.ts.map +1 -1
- package/dist/translation-center/TranslationsTitlebar.js +13 -4
- package/dist/translationEvents.d.ts +6 -0
- package/dist/translationEvents.d.ts.map +1 -0
- package/dist/translationEvents.js +4 -0
- package/package.json +1 -1
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import { Command, CommandContext, CommandData, FullItem } from "@parhelia/core";
|
|
1
|
+
import { Command, CommandContext, CommandData, EditContextType, FullItem } from "@parhelia/core";
|
|
2
|
+
import { TranslationDialogResult } from "./steps/types";
|
|
2
3
|
export type LocalizeItemCommandData = CommandData & {
|
|
3
4
|
items: FullItem[];
|
|
4
5
|
};
|
|
5
6
|
export type LocalizeItemCommandContext = CommandContext<LocalizeItemCommandData>;
|
|
6
7
|
export type LocalizeItemCommand = Command<LocalizeItemCommandData>;
|
|
8
|
+
export declare function openLocalizeItemDialog({ editContext, openDialog, items, }: {
|
|
9
|
+
editContext: EditContextType;
|
|
10
|
+
openDialog?: EditContextType["openDialog"];
|
|
11
|
+
items: FullItem[];
|
|
12
|
+
}): Promise<TranslationDialogResult | null>;
|
|
7
13
|
export declare const localizeItemCommand: LocalizeItemCommand;
|
|
8
14
|
//# sourceMappingURL=LocalizeItemCommand.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalizeItemCommand.d.ts","sourceRoot":"","sources":["../src/LocalizeItemCommand.tsx"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"LocalizeItemCommand.d.ts","sourceRoot":"","sources":["../src/LocalizeItemCommand.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,OAAO,EACP,cAAc,EACd,WAAW,EACX,eAAe,EACf,QAAQ,EACT,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAEL,uBAAuB,EACxB,MAAM,eAAe,CAAC;AAQvB,MAAM,MAAM,uBAAuB,GAAG,WAAW,GAAG;IAClD,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GACpC,cAAc,CAAC,uBAAuB,CAAC,CAAC;AAC1C,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAEnE,wBAAsB,sBAAsB,CAAC,EAC3C,WAAW,EACX,UAAmC,EACnC,KAAK,GACN,EAAE;IACD,WAAW,EAAE,eAAe,CAAC;IAC7B,UAAU,CAAC,EAAE,eAAe,CAAC,YAAY,CAAC,CAAC;IAC3C,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,2CAiCA;AAED,eAAO,MAAM,mBAAmB,EAAE,mBA6BjC,CAAC"}
|
|
@@ -2,9 +2,35 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { Globe as LucideGlobe } from "lucide-react";
|
|
4
4
|
import { LocalizeItemDialog } from "./LocalizeItemDialog";
|
|
5
|
+
import { dispatchTranslationBatchStarted } from "./translationEvents";
|
|
5
6
|
// Icon wrapper to avoid React 19 JSX type issues with forwardRef components.
|
|
6
7
|
// Use React.createElement so we don't call the forwardRef objects as functions.
|
|
7
8
|
const GlobeIcon = (props) => React.createElement(LucideGlobe, props);
|
|
9
|
+
export async function openLocalizeItemDialog({ editContext, openDialog = editContext.openDialog, items, }) {
|
|
10
|
+
const result = await openDialog(LocalizeItemDialog, {
|
|
11
|
+
items,
|
|
12
|
+
editContext,
|
|
13
|
+
});
|
|
14
|
+
// If translation was started (result contains batchId), navigate to the batch view
|
|
15
|
+
if (result?.batchId) {
|
|
16
|
+
const translationManagementEnabled = editContext?.configuration?.localization
|
|
17
|
+
?.translationManagement !== false;
|
|
18
|
+
if (translationManagementEnabled) {
|
|
19
|
+
// Synchronous URL update to avoid race with workspace URL sync
|
|
20
|
+
const params = new URLSearchParams(window.location.search);
|
|
21
|
+
params.set("workspace", "translation-management");
|
|
22
|
+
params.set("batchId", result.batchId);
|
|
23
|
+
const newUrl = `${window.location.pathname}?${params.toString()}`;
|
|
24
|
+
window.history.pushState(null, "", newUrl);
|
|
25
|
+
// Now switch the workspace; URL already contains batchId
|
|
26
|
+
editContext.switchWorkspace("translation-management");
|
|
27
|
+
window.setTimeout(() => {
|
|
28
|
+
dispatchTranslationBatchStarted(result.batchId);
|
|
29
|
+
}, 0);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
8
34
|
export const localizeItemCommand = {
|
|
9
35
|
id: "localizeItem",
|
|
10
36
|
label: "Translate",
|
|
@@ -26,25 +52,10 @@ export const localizeItemCommand = {
|
|
|
26
52
|
if (!items || !items?.length) {
|
|
27
53
|
return;
|
|
28
54
|
}
|
|
29
|
-
|
|
30
|
-
items: items,
|
|
55
|
+
return openLocalizeItemDialog({
|
|
31
56
|
editContext: context.editContext,
|
|
57
|
+
openDialog: context.openDialog,
|
|
58
|
+
items,
|
|
32
59
|
});
|
|
33
|
-
// If translation was started (result contains batchId), navigate to the batch view
|
|
34
|
-
if (result?.batchId) {
|
|
35
|
-
const translationManagementEnabled = context.editContext?.configuration?.localization
|
|
36
|
-
?.translationManagement !== false;
|
|
37
|
-
if (translationManagementEnabled) {
|
|
38
|
-
// Synchronous URL update to avoid race with workspace URL sync
|
|
39
|
-
const params = new URLSearchParams(window.location.search);
|
|
40
|
-
params.set("workspace", "translation-management");
|
|
41
|
-
params.set("batchId", result.batchId);
|
|
42
|
-
const newUrl = `${window.location.pathname}?${params.toString()}`;
|
|
43
|
-
window.history.pushState(null, "", newUrl);
|
|
44
|
-
// Now switch the workspace; URL already contains batchId
|
|
45
|
-
context.editContext.switchWorkspace("translation-management");
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return result;
|
|
49
60
|
},
|
|
50
61
|
};
|
|
@@ -12,7 +12,7 @@ const LARGE_BATCH_WARNING_THRESHOLD = 100;
|
|
|
12
12
|
const getNextButtonLabel = (step) => step?.nextButtonLabel || step?.name || "Next";
|
|
13
13
|
// Note: DialogButtons is an internal component that might need to be added to core exports
|
|
14
14
|
// For now, we'll implement it inline
|
|
15
|
-
const DialogButtons = ({ children, ...props }) => (_jsx("div", { className: "mt-auto flex shrink-0 items-center gap-3 border-t border-border-default bg-
|
|
15
|
+
const DialogButtons = ({ children, ...props }) => (_jsx("div", { className: "mt-auto flex shrink-0 items-center gap-3 border-t border-border-default bg-white px-6 py-4", ...props, children: children }));
|
|
16
16
|
export function LocalizeItemDialog(props) {
|
|
17
17
|
const editContext = props.editContext;
|
|
18
18
|
const configuration = editContext.configuration.translationWizard;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServiceLanguageSelectionStep.d.ts","sourceRoot":"","sources":["../../src/steps/ServiceLanguageSelectionStep.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,SAAS,CAAC;AAGtE;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,EAC3C,QAAe,EACf,IAAI,EACJ,OAAO,EACP,eAAe,EACf,WAAW,GACZ,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"ServiceLanguageSelectionStep.d.ts","sourceRoot":"","sources":["../../src/steps/ServiceLanguageSelectionStep.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,SAAS,CAAC;AAGtE;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,EAC3C,QAAe,EACf,IAAI,EACJ,OAAO,EACP,eAAe,EACf,WAAW,GACZ,EAAE,oBAAoB,2CAiRtB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { CountBadge } from "@parhelia/core";
|
|
3
|
+
import { CountBadge, getLanguages } from "@parhelia/core";
|
|
4
4
|
import { WizardStepShell } from "./WizardStepShell";
|
|
5
5
|
/**
|
|
6
6
|
* Language-only step. Item selection lives in its own step
|
|
@@ -28,12 +28,40 @@ export function ServiceLanguageSelectionStep({ isActive = true, data, setData, o
|
|
|
28
28
|
}
|
|
29
29
|
}, [isActive, data.targetLanguages.length]);
|
|
30
30
|
const editContextLanguages = useMemo(() => editContext?.itemLanguages || [], [editContext?.itemLanguages]);
|
|
31
|
+
const [siteLanguages, setSiteLanguages] = useState([]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (editContextLanguages.length > 0 || data.languageData.size > 0)
|
|
34
|
+
return;
|
|
35
|
+
let cancelled = false;
|
|
36
|
+
const loadSiteLanguages = async () => {
|
|
37
|
+
try {
|
|
38
|
+
const res = await getLanguages();
|
|
39
|
+
const languages = (res?.data ?? res ?? []);
|
|
40
|
+
if (!cancelled && Array.isArray(languages)) {
|
|
41
|
+
setSiteLanguages(languages);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error("Failed to load languages:", error);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
loadSiteLanguages();
|
|
49
|
+
return () => {
|
|
50
|
+
cancelled = true;
|
|
51
|
+
};
|
|
52
|
+
}, [editContextLanguages.length, data.languageData.size]);
|
|
31
53
|
const siteLanguageCodes = useMemo(() => {
|
|
32
54
|
if (editContextLanguages.length > 0) {
|
|
33
55
|
return editContextLanguages.map((lang) => lang.languageCode);
|
|
34
56
|
}
|
|
57
|
+
if (data.languageData.size > 0) {
|
|
58
|
+
return Array.from(data.languageData.keys());
|
|
59
|
+
}
|
|
60
|
+
if (siteLanguages.length > 0) {
|
|
61
|
+
return siteLanguages.map((lang) => lang.languageCode);
|
|
62
|
+
}
|
|
35
63
|
return Array.from(data.languageData.keys());
|
|
36
|
-
}, [editContextLanguages, data.languageData]);
|
|
64
|
+
}, [editContextLanguages, data.languageData, siteLanguages]);
|
|
37
65
|
const itemSourceLanguage = useMemo(() => {
|
|
38
66
|
return (data.items[0]?.descriptor.language ||
|
|
39
67
|
editContext?.item?.descriptor.language ||
|
|
@@ -42,16 +70,21 @@ export function ServiceLanguageSelectionStep({ isActive = true, data, setData, o
|
|
|
42
70
|
}, [data.items, editContext?.item, editContext?.currentItemDescriptor]);
|
|
43
71
|
const allLanguages = useMemo(() => {
|
|
44
72
|
const editContextLanguageMap = new Map(editContextLanguages.map((lang) => [lang.languageCode, lang]));
|
|
73
|
+
const siteLanguageMap = new Map(siteLanguages.map((lang) => [lang.languageCode, lang]));
|
|
45
74
|
const arr = siteLanguageCodes
|
|
46
75
|
.map((code) => {
|
|
47
76
|
const editContextLang = editContextLanguageMap.get(code);
|
|
77
|
+
const siteLanguage = siteLanguageMap.get(code);
|
|
48
78
|
const languageDataLang = data.languageData.get(code);
|
|
49
79
|
const sourceLanguage = languageDataLang?.translationStatus?.sourceLanguage ||
|
|
50
80
|
itemSourceLanguage;
|
|
51
81
|
return {
|
|
52
82
|
code,
|
|
53
|
-
name: editContextLang?.name ||
|
|
54
|
-
|
|
83
|
+
name: editContextLang?.name ||
|
|
84
|
+
languageDataLang?.name ||
|
|
85
|
+
siteLanguage?.name ||
|
|
86
|
+
code,
|
|
87
|
+
icon: editContextLang?.icon || siteLanguage?.icon,
|
|
55
88
|
sourceLanguage,
|
|
56
89
|
};
|
|
57
90
|
})
|
|
@@ -61,6 +94,7 @@ export function ServiceLanguageSelectionStep({ isActive = true, data, setData, o
|
|
|
61
94
|
}, [
|
|
62
95
|
siteLanguageCodes,
|
|
63
96
|
editContextLanguages,
|
|
97
|
+
siteLanguages,
|
|
64
98
|
data.languageData,
|
|
65
99
|
itemSourceLanguage,
|
|
66
100
|
]);
|
|
@@ -118,7 +152,7 @@ export function ServiceLanguageSelectionStep({ isActive = true, data, setData, o
|
|
|
118
152
|
return allLanguages.some((lang) => languageSelection[lang.code]);
|
|
119
153
|
}, [allLanguages, languageSelection]);
|
|
120
154
|
const selectedCount = data.targetLanguages.length;
|
|
121
|
-
return (_jsx(WizardStepShell, { fillHeight: true, testId: "language-selection-step", children: _jsxs("div", { className: "mx-auto flex min-h-0 w-full max-w-3xl flex-1 flex-col overflow-hidden rounded-xl border border-border-default bg-white", children: [_jsxs("div", { className: "flex h-[37px] shrink-0 items-center justify-between gap-2 border-b border-border-default bg-neutral-grey-5/50 px-3", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "text-xs font-medium tracking-wider text-neutral-grey-50 ", children: "Target Languages" }), selectedCount > 0 &&
|
|
155
|
+
return (_jsx(WizardStepShell, { fillHeight: true, testId: "language-selection-step", children: _jsxs("div", { className: "mx-auto flex min-h-0 w-full max-w-3xl flex-1 flex-col overflow-hidden rounded-xl border border-border-default bg-white", children: [_jsxs("div", { className: "flex h-[37px] shrink-0 items-center justify-between gap-2 border-b border-border-default bg-neutral-grey-5/50 px-3", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "text-xs font-medium tracking-wider text-neutral-grey-50 ", children: "Target Languages" }), selectedCount > 0 && _jsx(CountBadge, { children: selectedCount })] }), allLanguages.length > 1 && (_jsxs("label", { className: "flex cursor-pointer items-center gap-1.5 text-[11px] font-medium text-neutral-grey-50 transition-colors hover:text-neutral-grey-100", children: [_jsx("input", { type: "checkbox", checked: areAllLanguagesSelected, ref: (input) => {
|
|
122
156
|
if (input) {
|
|
123
157
|
input.indeterminate =
|
|
124
158
|
areSomeLanguagesSelected && !areAllLanguagesSelected;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TranslationBatches.d.ts","sourceRoot":"","sources":["../../src/translation-center/TranslationBatches.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TranslationBatches.d.ts","sourceRoot":"","sources":["../../src/translation-center/TranslationBatches.tsx"],"names":[],"mappings":"AAwPA,wBAAgB,kBAAkB,4CA6uCjC"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState, useMemo } from "react";
|
|
3
3
|
import React from "react";
|
|
4
|
-
import { Button, Select, Input, UserPicker, Popover, PopoverContent, PopoverTrigger, ContentTree, fetchItemStubs, searchUsers as searchBackendUsers, useEditContext, getLanguages, SimpleIconButton, DeleteIcon, } from "@parhelia/core";
|
|
4
|
+
import { Button, Badge, Select, Input, UserPicker, Popover, PopoverContent, PopoverTrigger, ContentTree, fetchItemStubs, searchUsers as searchBackendUsers, useEditContext, getLanguages, SimpleIconButton, DeleteIcon, } from "@parhelia/core";
|
|
5
5
|
import { listBatches, getTranslationProviders, listBatchTranslationJobs, retryBatchTranslation, abortBatch, deleteBatch, } from "../services/translationService";
|
|
6
|
+
import { TRANSLATION_BATCH_STARTED_EVENT, } from "../translationEvents";
|
|
6
7
|
import { useDebouncedCallback } from "use-debounce";
|
|
7
8
|
import { X, ChevronDown, Languages, Loader2, Calendar, CheckCircle2, User as UserIcon2, Cloud, Globe, FileText, FolderTree, ExternalLink, Check, AlertCircle, CircleStop, Hourglass, Copy, Sparkles, RotateCcw, } from "lucide-react";
|
|
8
9
|
import { toast } from "sonner";
|
|
@@ -437,6 +438,25 @@ export function TranslationBatches() {
|
|
|
437
438
|
useEffect(() => {
|
|
438
439
|
loadBatchJobsRef.current = loadBatchJobs;
|
|
439
440
|
}, [loadBatchJobs]);
|
|
441
|
+
useEffect(() => {
|
|
442
|
+
const handleTranslationBatchStarted = (event) => {
|
|
443
|
+
const detail = event
|
|
444
|
+
.detail;
|
|
445
|
+
const batchId = normalizeGuid(detail?.batchId);
|
|
446
|
+
void (async () => {
|
|
447
|
+
await loadRecentBatches(0, false, effectiveFilters);
|
|
448
|
+
if (!batchId)
|
|
449
|
+
return;
|
|
450
|
+
setExpandedBatchId(batchId);
|
|
451
|
+
setExpandedItems(new Set());
|
|
452
|
+
await loadBatchJobs(batchId);
|
|
453
|
+
})();
|
|
454
|
+
};
|
|
455
|
+
window.addEventListener(TRANSLATION_BATCH_STARTED_EVENT, handleTranslationBatchStarted);
|
|
456
|
+
return () => {
|
|
457
|
+
window.removeEventListener(TRANSLATION_BATCH_STARTED_EVENT, handleTranslationBatchStarted);
|
|
458
|
+
};
|
|
459
|
+
}, [effectiveFilters, loadBatchJobs, loadRecentBatches]);
|
|
440
460
|
const toggleBatch = useCallback((batchId) => {
|
|
441
461
|
setExpandedBatchId((prev) => {
|
|
442
462
|
const next = prev === batchId ? null : batchId;
|
|
@@ -1146,7 +1166,7 @@ function LanguageJobChip({ job, language, batchStartedAtUtc, onOpen, isRetrying
|
|
|
1146
1166
|
const emptyFieldText = statistics && statistics.emptyFieldCount > 0
|
|
1147
1167
|
? `${formatCount(statistics.emptyFieldCount)} empty`
|
|
1148
1168
|
: null;
|
|
1149
|
-
return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: `inline-flex items-center gap-1.5 rounded-md border badge-pad-sm text-[12px] transition-colors cursor-pointer ${chipCls}`, children: [language?.icon ? (_jsx("img", { src: language.icon, alt: langName, className: "h-3.5 shrink-0" })) : null, _jsx("span", { className: "font-mono tracking-tight", children: job.targetLanguage }), jobInProgress && (_jsx(LoaderIcon, { className: "h-3 w-3 shrink-0 animate-spin" })), jobPending && (_jsx(QueuedIcon, { className: "h-3 w-3 shrink-0", strokeWidth: 1.75, "aria-label": "Queued" })), jobError && _jsx(AlertCircleIcon, { className: "h-3 w-3 shrink-0" }), !jobInProgress && !jobError && !jobPending && !jobAborted && (_jsx(CheckIcon, { className: "h-3 w-3 shrink-0 text-feedback-green" }))] }) }), _jsxs(PopoverContent, { align: "start", sideOffset: 6, className: "w-[22rem] max-w-[calc(100vw-2rem)] p-0", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-border-default px-3 py-2", children: [language?.icon ? (_jsx("img", { src: language.icon, alt: langName, className: "h-4 shrink-0" })) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-[13px] font-medium text-neutral-grey-100 truncate", children: langName }), _jsxs("div", { className: "text-[11px] text-neutral-grey-50 font-mono truncate", children: [job.targetLanguage, job.sourceLanguage ? ` ← ${job.sourceLanguage}` : ""] })] }), _jsxs(
|
|
1169
|
+
return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: `inline-flex items-center gap-1.5 rounded-md border badge-pad-sm text-[12px] transition-colors cursor-pointer ${chipCls}`, children: [language?.icon ? (_jsx("img", { src: language.icon, alt: langName, className: "h-3.5 shrink-0" })) : null, _jsx("span", { className: "font-mono tracking-tight", children: job.targetLanguage }), jobInProgress && (_jsx(LoaderIcon, { className: "h-3 w-3 shrink-0 animate-spin" })), jobPending && (_jsx(QueuedIcon, { className: "h-3 w-3 shrink-0", strokeWidth: 1.75, "aria-label": "Queued" })), jobError && _jsx(AlertCircleIcon, { className: "h-3 w-3 shrink-0" }), !jobInProgress && !jobError && !jobPending && !jobAborted && (_jsx(CheckIcon, { className: "h-3 w-3 shrink-0 text-feedback-green" }))] }) }), _jsxs(PopoverContent, { align: "start", sideOffset: 6, className: "w-[22rem] max-w-[calc(100vw-2rem)] p-0", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-border-default px-3 py-2", children: [language?.icon ? (_jsx("img", { src: language.icon, alt: langName, className: "h-4 shrink-0" })) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-[13px] font-medium text-neutral-grey-100 truncate", children: langName }), _jsxs("div", { className: "text-[11px] text-neutral-grey-50 font-mono truncate", children: [job.targetLanguage, job.sourceLanguage ? ` ← ${job.sourceLanguage}` : ""] })] }), _jsxs(Badge, { size: "sm", className: statusBadgeCls, children: [jobInProgress && (_jsx(LoaderIcon, { className: "h-2.5 w-2.5 animate-spin" })), jobStatus || "Unknown"] })] }), _jsxs("dl", { className: "divide-y divide-border-default/60", children: [claimedAtValid && (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: "Claimed" }), _jsx("dd", { className: "text-[12px] text-neutral-grey-100", children: claimedAt.toLocaleString() })] })), timestampValid && (jobInProgress || jobPending) && (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: "Updated" }), _jsx("dd", { className: "text-[12px] text-neutral-grey-100", children: timestamp.toLocaleString() })] })), durationMs != null && (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: jobInProgress || jobPending ? "Running" : "Duration" }), _jsxs("dd", { className: "text-[12px] tabular-nums text-neutral-grey-100", title: durationApproximated
|
|
1150
1170
|
? "Approximate — measured from batch start"
|
|
1151
1171
|
: undefined, children: [durationApproximated ? "~" : "", formatDuration(durationMs)] })] })), statistics && (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: "Fields" }), _jsxs("dd", { className: "text-[12px] tabular-nums text-neutral-grey-100", children: [fieldText, emptyFieldText ? (_jsxs("span", { className: "ml-1.5 text-neutral-grey-50", children: ["(", emptyFieldText, ")"] })) : null] })] })), statistics && (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: "Text" }), _jsxs("dd", { className: "text-[12px] tabular-nums text-neutral-grey-100", children: [formatCount(statistics.sourceCharacterCount), " chars"] })] })), job.totalCost != null && (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: "Cost" }), _jsx("dd", { className: "text-[12px] tabular-nums text-neutral-grey-100", children: formatUsdCost(job.totalCost) })] })), lastHeartbeatValid && (jobInProgress || jobPending) && (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: "Heartbeat" }), _jsx("dd", { className: "text-[12px] text-neutral-grey-100", children: lastHeartbeat.toLocaleString() })] })), attemptCount > 1 && (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: "Attempts" }), _jsx("dd", { className: "text-[12px] tabular-nums text-neutral-grey-100", children: attemptCount })] })), job.hash && (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: "Hash" }), _jsx("dd", { className: "text-[11px] font-mono text-neutral-grey-100 break-all", children: job.hash })] })), job.message && (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: jobError ? "Error" : jobAborted ? "Aborted" : "Message" }), _jsx("dd", { className: `text-[12px] break-words ${jobError ? "text-feedback-red" : jobAborted ? "text-feedback-orange" : "text-neutral-grey-100"}`, children: job.message })] })), job.batchId && (_jsxs("div", { className: "flex items-baseline gap-3 px-3 py-2", children: [_jsx("dt", { className: "w-20 shrink-0 text-[10px] tracking-wide text-neutral-grey-50", children: "Batch" }), _jsx("dd", { className: "text-[11px] font-mono text-neutral-grey-50 break-all", children: job.batchId })] }))] }), _jsxs("div", { className: "flex justify-end gap-2 border-t border-border-default px-3 py-2", children: [jobError && onRetry && (_jsxs(Button, { size: "sm", variant: "outline", disabled: isRetrying, onClick: (e) => {
|
|
1152
1172
|
e.stopPropagation();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TranslationsTitlebar.d.ts","sourceRoot":"","sources":["../../src/translation-center/TranslationsTitlebar.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TranslationsTitlebar.d.ts","sourceRoot":"","sources":["../../src/translation-center/TranslationsTitlebar.tsx"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,oBAAoB,mDA8CnC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { SimpleIconButton, useEditContext } from "@parhelia/core";
|
|
3
|
-
import { SquarePen } from "lucide-react";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, SimpleIconButton, useEditContext } from "@parhelia/core";
|
|
3
|
+
import { Globe, SquarePen } from "lucide-react";
|
|
4
|
+
import { openLocalizeItemDialog } from "../LocalizeItemCommand";
|
|
4
5
|
/**
|
|
5
6
|
* TranslationsTitlebar - Titlebar content for the Translation Management workspace.
|
|
6
7
|
* Shows desktop actions for the Translation Management workspace.
|
|
@@ -12,5 +13,13 @@ export function TranslationsTitlebar() {
|
|
|
12
13
|
if (editContext.isMobile)
|
|
13
14
|
return null;
|
|
14
15
|
const { showAgentsWorkspaceEditor, setShowAgentsWorkspaceEditor } = editContext;
|
|
15
|
-
|
|
16
|
+
const multiItemEnabled = editContext.configuration?.localization?.multiItem !== false;
|
|
17
|
+
const currentItem = editContext.item;
|
|
18
|
+
const canTranslate = multiItemEnabled || !!currentItem;
|
|
19
|
+
return (_jsxs("div", { className: "flex w-full items-center justify-between gap-3 pr-2", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [_jsx("div", { "aria-hidden": "true", className: "border-border-default h-7 shrink-0 border-r" }), _jsxs(Button, { size: "sm", title: canTranslate ? "Translate" : "Open an item to translate", "data-testid": "translations-titlebar-translate-button", disabled: !canTranslate, onClick: () => {
|
|
20
|
+
void openLocalizeItemDialog({
|
|
21
|
+
editContext,
|
|
22
|
+
items: currentItem ? [currentItem] : [],
|
|
23
|
+
});
|
|
24
|
+
}, children: [_jsx(Globe, { className: "h-4 w-4", strokeWidth: 1.5 }), "Translate"] })] }), _jsx(SimpleIconButton, { icon: _jsx(SquarePen, { className: "h-5 w-5", strokeWidth: 1 }), label: showAgentsWorkspaceEditor ? "Hide Editor" : "Show Editor", size: "large", "data-testid": "translations-editor-panel-toggle", selected: showAgentsWorkspaceEditor, onClick: () => setShowAgentsWorkspaceEditor(!showAgentsWorkspaceEditor) })] }));
|
|
16
25
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const TRANSLATION_BATCH_STARTED_EVENT = "parhelia:translation-batch-started";
|
|
2
|
+
export type TranslationBatchStartedEventDetail = {
|
|
3
|
+
batchId: string;
|
|
4
|
+
};
|
|
5
|
+
export declare function dispatchTranslationBatchStarted(batchId: string): void;
|
|
6
|
+
//# sourceMappingURL=translationEvents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translationEvents.d.ts","sourceRoot":"","sources":["../src/translationEvents.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,+BAA+B,uCACN,CAAC;AAEvC,MAAM,MAAM,kCAAkC,GAAG;IAC/C,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,MAAM,QAO9D"}
|