@braze/sanity-plugin-ai-translation 0.2.52 → 0.2.54
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/index.js +190 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getPublishedId, pathToString, useClient, defineType, defineField, definePlugin } from "sanity";
|
|
2
2
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { WarningOutlineIcon, SearchIcon, CheckmarkIcon, ErrorOutlineIcon, CircleIcon, ArrowRightIcon, ResetIcon, PanelRightIcon, TranslateIcon } from "@sanity/icons";
|
|
4
|
-
import { createContext, useReducer, useEffect, useContext, useMemo, useState, useCallback } from "react";
|
|
4
|
+
import { createContext, useReducer, useRef, useEffect, useContext, useMemo, useState, useCallback } from "react";
|
|
5
5
|
import defaults from "lodash.defaults";
|
|
6
6
|
import { Card, Stack, Flex, Text, Box, Heading, Button, Spinner, Inline, Checkbox, Grid, Radio, TextInput, Badge, Select, Label, Code, Tooltip } from "@sanity/ui";
|
|
7
7
|
import { toPlainText } from "@portabletext/react";
|
|
@@ -62,7 +62,7 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
62
62
|
error: error instanceof Error ? error.message : "Network error"
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
-
}, activeTranslations = /* @__PURE__ */ new Map(), registerActiveTranslation = (documentId, releaseId) => {
|
|
65
|
+
}, undraftId = (id) => id.replace(/^drafts\./, ""), activeTranslations = /* @__PURE__ */ new Map(), registerActiveTranslation = (documentId, releaseId) => {
|
|
66
66
|
const entry = {
|
|
67
67
|
state: {
|
|
68
68
|
documentId,
|
|
@@ -80,7 +80,7 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
80
80
|
}, updateActiveTranslationJob = (documentId, job) => {
|
|
81
81
|
const entry = activeTranslations.get(documentId);
|
|
82
82
|
if (entry) {
|
|
83
|
-
const jobKey = `${job._id}_${job.language}`, existingJob = entry.state.translationJobs.get(jobKey), jobWithDefinedValues = Object.fromEntries(
|
|
83
|
+
const jobKey = `${undraftId(job._id)}_${job.language}`, existingJob = entry.state.translationJobs.get(jobKey), jobWithDefinedValues = Object.fromEntries(
|
|
84
84
|
Object.entries(job).filter(([_, value]) => value !== void 0)
|
|
85
85
|
), mergedJob = existingJob ? { ...existingJob, ...jobWithDefinedValues } : jobWithDefinedValues;
|
|
86
86
|
entry.state.translationJobs.set(jobKey, mergedJob), notifySubscribers(entry);
|
|
@@ -114,6 +114,7 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
114
114
|
shouldTranslateReferences: !1,
|
|
115
115
|
maxReferenceDepth: 1,
|
|
116
116
|
references: [],
|
|
117
|
+
selectedReferenceIds: /* @__PURE__ */ new Set(),
|
|
117
118
|
translationMetadata: null,
|
|
118
119
|
translationMode: "sanity-only",
|
|
119
120
|
openaiValidated: !1,
|
|
@@ -167,8 +168,13 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
167
168
|
...context,
|
|
168
169
|
references: action.payload
|
|
169
170
|
};
|
|
171
|
+
case "SET_SELECTED_REFERENCE_IDS":
|
|
172
|
+
return {
|
|
173
|
+
...context,
|
|
174
|
+
selectedReferenceIds: action.payload
|
|
175
|
+
};
|
|
170
176
|
case "SET_TRANSLATION_JOB_STATUS": {
|
|
171
|
-
const jobKey = `${action.payload._id}_${action.payload.language}`, newTranslationJobs = new Map(context.translationJobs), existingJob = newTranslationJobs.get(jobKey), mergedJob = defaults(action.payload, existingJob);
|
|
177
|
+
const jobKey = `${undraftId(action.payload._id)}_${action.payload.language}`, newTranslationJobs = new Map(context.translationJobs), existingJob = newTranslationJobs.get(jobKey), mergedJob = defaults(action.payload, existingJob);
|
|
172
178
|
return newTranslationJobs.set(jobKey, mergedJob), {
|
|
173
179
|
...context,
|
|
174
180
|
translationJobs: newTranslationJobs
|
|
@@ -191,6 +197,7 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
191
197
|
...context,
|
|
192
198
|
overallStatus: "idle",
|
|
193
199
|
translationJobs: /* @__PURE__ */ new Map(),
|
|
200
|
+
selectedReferenceIds: /* @__PURE__ */ new Set(),
|
|
194
201
|
// Clear any release conflicts from previous run
|
|
195
202
|
releaseConfig: {
|
|
196
203
|
...context.releaseConfig,
|
|
@@ -218,6 +225,7 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
218
225
|
shouldTranslateReferences: !1,
|
|
219
226
|
maxReferenceDepth: 1,
|
|
220
227
|
references: [],
|
|
228
|
+
selectedReferenceIds: /* @__PURE__ */ new Set(),
|
|
221
229
|
// Restore jobs from active translation if available
|
|
222
230
|
translationJobs: activeTranslation?.translationJobs ?? /* @__PURE__ */ new Map(),
|
|
223
231
|
translationMode: config?.openai?.defaultMode || "sanity-only",
|
|
@@ -233,7 +241,7 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
233
241
|
jobCount: activeTranslation.translationJobs.size,
|
|
234
242
|
component: "TranslationContextProvider"
|
|
235
243
|
});
|
|
236
|
-
const [context, dispatch] = useReducer(translationReducer, initialContext);
|
|
244
|
+
const [context, dispatch] = useReducer(translationReducer, initialContext), lastProcessedRefsRef = useRef("");
|
|
237
245
|
return useEffect(() => {
|
|
238
246
|
const handleActiveTranslationUpdate = (state) => {
|
|
239
247
|
dispatch({
|
|
@@ -273,7 +281,28 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
273
281
|
});
|
|
274
282
|
}
|
|
275
283
|
})();
|
|
276
|
-
}, [config?.openai?.apiKey, config?.openai?.enabled, config?.openai?.model]),
|
|
284
|
+
}, [config?.openai?.apiKey, config?.openai?.enabled, config?.openai?.model]), useEffect(() => {
|
|
285
|
+
const currentRefsKey = context.references.map((ref) => ref._id).sort().join(",");
|
|
286
|
+
if (currentRefsKey !== lastProcessedRefsRef.current) {
|
|
287
|
+
if (context.references.length > 0) {
|
|
288
|
+
const currentReferenceIds = new Set(context.references.map((ref) => ref._id)), previousReferenceIds = new Set(
|
|
289
|
+
lastProcessedRefsRef.current ? lastProcessedRefsRef.current.split(",") : []
|
|
290
|
+
), hasNewReferences = Array.from(currentReferenceIds).some((id) => !previousReferenceIds.has(id)), currentSelection = context.selectedReferenceIds;
|
|
291
|
+
if (currentSelection.size === 0 || hasNewReferences) {
|
|
292
|
+
const newSelection = new Set(currentSelection);
|
|
293
|
+
currentReferenceIds.forEach((id) => {
|
|
294
|
+
newSelection.add(id);
|
|
295
|
+
});
|
|
296
|
+
const currentIds = Array.from(currentSelection).sort(), newIds = Array.from(newSelection).sort();
|
|
297
|
+
(currentIds.length !== newIds.length || !currentIds.every((id, i) => id === newIds[i])) && dispatch({
|
|
298
|
+
type: "SET_SELECTED_REFERENCE_IDS",
|
|
299
|
+
payload: newSelection
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
lastProcessedRefsRef.current = currentRefsKey;
|
|
304
|
+
}
|
|
305
|
+
}, [context.references, context.selectedReferenceIds]), devLog.debug("Translation context state initialized", {
|
|
277
306
|
documentId: document._id,
|
|
278
307
|
overallStatus: context.overallStatus,
|
|
279
308
|
selectedLanguages: context.selectedTargetLanguages,
|
|
@@ -290,7 +319,7 @@ const isLoggingEnabled = () => process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLAT
|
|
|
290
319
|
return context;
|
|
291
320
|
}, getExistingTranslationId = (translationMetadata, targetLanguage) => translationMetadata?.translations?.length && translationMetadata.translations.find(
|
|
292
321
|
(translation) => translation._key === targetLanguage
|
|
293
|
-
)?.value._ref || null,
|
|
322
|
+
)?.value._ref || null, TRANSLATION_METADATA_TYPE = "translation.metadata", getTranslationMetadata = async ({
|
|
294
323
|
client,
|
|
295
324
|
sourceDocument,
|
|
296
325
|
sourceLanguage
|
|
@@ -1253,7 +1282,9 @@ function restoreTransforms({
|
|
|
1253
1282
|
schemaType,
|
|
1254
1283
|
document: translatedDocument
|
|
1255
1284
|
};
|
|
1256
|
-
devLog.debug("Restoring transform for field", { fieldPath, schemaType })
|
|
1285
|
+
devLog.debug("Restoring transform for field", { fieldPath, schemaType });
|
|
1286
|
+
const restoredValue = transform.fromTranslatable(value, originalValue, context);
|
|
1287
|
+
restoredObj[key] = restoredValue;
|
|
1257
1288
|
} else
|
|
1258
1289
|
restoredObj[key] = restoreTransforms({
|
|
1259
1290
|
translatedDocument: value,
|
|
@@ -1491,9 +1522,9 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
1491
1522
|
}, modelPricing = pricing[model] || pricing["gpt-5"], cachedInputRate = model === "gpt-5.1" ? 0.125 / 1e6 : modelPricing.input;
|
|
1492
1523
|
return Math.max(0, tokens.prompt - tokens.cached) * modelPricing.input + tokens.cached * cachedInputRate + tokens.completion * modelPricing.output + tokens.reasoning * modelPricing.reasoning;
|
|
1493
1524
|
}, deserializeDocument = (originalDoc, reviewedText) => {
|
|
1494
|
-
const parsed = parseReviewFormat(reviewedText), result = cloneDeep(originalDoc);
|
|
1525
|
+
const parsed = parseReviewFormat(reviewedText, originalDoc), result = cloneDeep(originalDoc);
|
|
1495
1526
|
return applyTextChanges(result, parsed), result;
|
|
1496
|
-
}, parseReviewFormat = (text) => {
|
|
1527
|
+
}, parseReviewFormat = (text, originalDoc) => {
|
|
1497
1528
|
const root = [], stack = [], lines = text.split(`
|
|
1498
1529
|
`);
|
|
1499
1530
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -1526,8 +1557,16 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
1526
1557
|
}
|
|
1527
1558
|
const content = contentLines.join(`
|
|
1528
1559
|
`);
|
|
1529
|
-
let contentType
|
|
1530
|
-
|
|
1560
|
+
let contentType;
|
|
1561
|
+
try {
|
|
1562
|
+
const fieldValue = get(originalDoc, [fieldName]);
|
|
1563
|
+
if (fieldValue && typeof fieldValue == "object" && fieldValue !== null) {
|
|
1564
|
+
const fieldType = fieldValue._type;
|
|
1565
|
+
typeof fieldType == "string" && fieldType.startsWith("translatable.") && (contentType = fieldType);
|
|
1566
|
+
}
|
|
1567
|
+
} catch {
|
|
1568
|
+
}
|
|
1569
|
+
contentType || (content.includes("<block") || content.includes("<span") ? contentType = "translatable.richtext" : contentType = "translatable.vtt.captions");
|
|
1531
1570
|
const node = {
|
|
1532
1571
|
type: "field",
|
|
1533
1572
|
name: fieldName,
|
|
@@ -1564,7 +1603,7 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
1564
1603
|
parent && parent.children.push(node);
|
|
1565
1604
|
} else
|
|
1566
1605
|
root.push(node);
|
|
1567
|
-
else if (attributes.type
|
|
1606
|
+
else if (attributes.type && typeof attributes.type == "string" && attributes.type.startsWith("translatable.")) {
|
|
1568
1607
|
const contentLines = [];
|
|
1569
1608
|
for (i++; i < lines.length; ) {
|
|
1570
1609
|
const currentLine = lines[i];
|
|
@@ -1626,14 +1665,13 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
1626
1665
|
applyFieldChanges(doc, path, node);
|
|
1627
1666
|
}
|
|
1628
1667
|
}, applyFieldChanges = (doc, path, node) => {
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
fieldValue && typeof fieldValue == "object" && fieldValue._type === "translatable.vtt.captions" && node.text && set(doc, [...path, "translationText"], node.text);
|
|
1632
|
-
return;
|
|
1633
|
-
}
|
|
1634
|
-
if (node.attributes.type === "translatable.richtext") {
|
|
1668
|
+
const nodeType = node.attributes.type;
|
|
1669
|
+
if (typeof nodeType == "string" && nodeType.startsWith("translatable.")) {
|
|
1635
1670
|
const fieldValue = get(doc, path);
|
|
1636
|
-
fieldValue && typeof fieldValue == "object" && fieldValue
|
|
1671
|
+
if (fieldValue && typeof fieldValue == "object" && fieldValue !== null) {
|
|
1672
|
+
const fieldType = fieldValue._type;
|
|
1673
|
+
typeof fieldType == "string" && fieldType === nodeType && node.text && set(doc, [...path, "translationText"], node.text);
|
|
1674
|
+
}
|
|
1637
1675
|
return;
|
|
1638
1676
|
}
|
|
1639
1677
|
if (node.attributes.type === "array") {
|
|
@@ -1721,12 +1759,9 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
1721
1759
|
return;
|
|
1722
1760
|
}
|
|
1723
1761
|
if (isPlainObject(value)) {
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
}
|
|
1728
|
-
if (value._type === "translatable.richtext" && typeof value.translationText == "string") {
|
|
1729
|
-
lines.push(`${indentStr}--- ${fieldName} ---`), lines.push(value.translationText), lines.push(`${indentStr}--- /${fieldName} ---`);
|
|
1762
|
+
const valueObj = value, valueType = valueObj._type, translationText = valueObj.translationText;
|
|
1763
|
+
if (typeof valueType == "string" && valueType.startsWith("translatable.") && typeof translationText == "string") {
|
|
1764
|
+
lines.push(`${indentStr}--- ${fieldName} ---`), lines.push(translationText), lines.push(`${indentStr}--- /${fieldName} ---`);
|
|
1730
1765
|
return;
|
|
1731
1766
|
}
|
|
1732
1767
|
lines.push(`${indentStr}<field name="${fieldName}" type="object">`);
|
|
@@ -2656,10 +2691,11 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2656
2691
|
overallStatus,
|
|
2657
2692
|
dispatch,
|
|
2658
2693
|
references,
|
|
2694
|
+
selectedReferenceIds,
|
|
2659
2695
|
selectedTargetLanguages,
|
|
2660
2696
|
translationMode,
|
|
2661
2697
|
releaseConfig
|
|
2662
|
-
} = useTranslationContext(), client = useClient({ apiVersion: "vX" }), [conflictContext, setConflictContext] = useState(null), [isCheckingConflicts, setIsCheckingConflicts] = useState(!1);
|
|
2698
|
+
} = useTranslationContext(), client = useClient({ apiVersion: "vX" }), [conflictContext, setConflictContext] = useState(null), [isCheckingConflicts, setIsCheckingConflicts] = useState(!1), filteredReferences = references.filter((ref) => selectedReferenceIds.has(ref._id));
|
|
2663
2699
|
useEffect(() => {
|
|
2664
2700
|
releaseConfig.mode === "new" && conflictContext && setConflictContext(null);
|
|
2665
2701
|
}, [releaseConfig.mode, conflictContext]);
|
|
@@ -2672,7 +2708,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2672
2708
|
dispatch,
|
|
2673
2709
|
languageFieldName: config.languageFieldName,
|
|
2674
2710
|
schemaId: config.schemaId,
|
|
2675
|
-
references,
|
|
2711
|
+
references: filteredReferences,
|
|
2676
2712
|
maxConcurrentTranslationJobs: config.maxConcurrentTranslationJobs,
|
|
2677
2713
|
maxReferenceDepth: config.maxReferenceDepth,
|
|
2678
2714
|
translationMode,
|
|
@@ -2698,7 +2734,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2698
2734
|
config.maxReferenceDepth,
|
|
2699
2735
|
config.openai,
|
|
2700
2736
|
config.excludeFields,
|
|
2701
|
-
|
|
2737
|
+
filteredReferences,
|
|
2702
2738
|
translationMode,
|
|
2703
2739
|
releaseConfig
|
|
2704
2740
|
]
|
|
@@ -2716,7 +2752,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2716
2752
|
sourceDocument: document,
|
|
2717
2753
|
targetLanguages: selectedTargetLanguages,
|
|
2718
2754
|
languageFieldName: config.languageFieldName,
|
|
2719
|
-
references
|
|
2755
|
+
references: filteredReferences
|
|
2720
2756
|
});
|
|
2721
2757
|
if (setIsCheckingConflicts(!1), foundConflicts.length > 0) {
|
|
2722
2758
|
setConflictContext({
|
|
@@ -2727,7 +2763,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2727
2763
|
}
|
|
2728
2764
|
}
|
|
2729
2765
|
startTranslation(!1);
|
|
2730
|
-
}, [releaseConfig, client, document, selectedTargetLanguages, config.languageFieldName, startTranslation,
|
|
2766
|
+
}, [releaseConfig, client, document, selectedTargetLanguages, config.languageFieldName, startTranslation, filteredReferences]), handleCancelConflict = useCallback(() => {
|
|
2731
2767
|
setConflictContext(null);
|
|
2732
2768
|
}, []), handleReplaceConflict = useCallback(() => {
|
|
2733
2769
|
conflictContext && startTranslation(!0, conflictContext.releaseId);
|
|
@@ -3101,8 +3137,104 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3101
3137
|
});
|
|
3102
3138
|
}
|
|
3103
3139
|
return result;
|
|
3140
|
+
}, ReferenceSelectionPanel = () => {
|
|
3141
|
+
const { references, selectedReferenceIds, dispatch } = useTranslationContext(), [typeFilter, setTypeFilter] = useState("all"), documentTypes = useMemo(() => {
|
|
3142
|
+
const types = new Set(references.map((ref) => ref._type));
|
|
3143
|
+
return Array.from(types).sort();
|
|
3144
|
+
}, [references]), filteredReferences = useMemo(() => typeFilter === "all" ? references : references.filter((ref) => ref._type === typeFilter), [references, typeFilter]), selectedCount = useMemo(() => filteredReferences.filter((ref) => selectedReferenceIds.has(ref._id)).length, [filteredReferences, selectedReferenceIds]), handleToggleReference = (referenceId) => {
|
|
3145
|
+
const newSelection = new Set(selectedReferenceIds);
|
|
3146
|
+
newSelection.has(referenceId) ? newSelection.delete(referenceId) : newSelection.add(referenceId), dispatch({
|
|
3147
|
+
type: "SET_SELECTED_REFERENCE_IDS",
|
|
3148
|
+
payload: newSelection
|
|
3149
|
+
});
|
|
3150
|
+
}, handleSelectAll = () => {
|
|
3151
|
+
const newSelection = new Set(selectedReferenceIds);
|
|
3152
|
+
filteredReferences.forEach((ref) => {
|
|
3153
|
+
newSelection.add(ref._id);
|
|
3154
|
+
}), dispatch({
|
|
3155
|
+
type: "SET_SELECTED_REFERENCE_IDS",
|
|
3156
|
+
payload: newSelection
|
|
3157
|
+
});
|
|
3158
|
+
}, handleSelectNone = () => {
|
|
3159
|
+
const newSelection = new Set(selectedReferenceIds);
|
|
3160
|
+
filteredReferences.forEach((ref) => {
|
|
3161
|
+
newSelection.delete(ref._id);
|
|
3162
|
+
}), dispatch({
|
|
3163
|
+
type: "SET_SELECTED_REFERENCE_IDS",
|
|
3164
|
+
payload: newSelection
|
|
3165
|
+
});
|
|
3166
|
+
};
|
|
3167
|
+
return references.length === 0 ? null : /* @__PURE__ */ jsx(Card, { padding: 4, border: !0, muted: !0, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
3168
|
+
/* @__PURE__ */ jsxs(Flex, { justify: "space-between", align: "center", gap: 2, children: [
|
|
3169
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, align: "center", children: [
|
|
3170
|
+
/* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: "Select References" }),
|
|
3171
|
+
/* @__PURE__ */ jsxs(Text, { size: 1, muted: !0, children: [
|
|
3172
|
+
"(",
|
|
3173
|
+
selectedCount,
|
|
3174
|
+
" of ",
|
|
3175
|
+
filteredReferences.length,
|
|
3176
|
+
" selected)"
|
|
3177
|
+
] })
|
|
3178
|
+
] }),
|
|
3179
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
3180
|
+
/* @__PURE__ */ jsx(Button, { mode: "ghost", size: 1, onClick: handleSelectAll, text: "Select All" }),
|
|
3181
|
+
/* @__PURE__ */ jsx(Button, { mode: "ghost", size: 1, onClick: handleSelectNone, text: "Select None" })
|
|
3182
|
+
] })
|
|
3183
|
+
] }),
|
|
3184
|
+
documentTypes.length > 1 && /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
|
|
3185
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Filter by type:" }),
|
|
3186
|
+
/* @__PURE__ */ jsxs(
|
|
3187
|
+
Select,
|
|
3188
|
+
{
|
|
3189
|
+
value: typeFilter,
|
|
3190
|
+
onChange: (event) => setTypeFilter(event.target.value),
|
|
3191
|
+
style: { minWidth: "150px" },
|
|
3192
|
+
children: [
|
|
3193
|
+
/* @__PURE__ */ jsxs("option", { value: "all", children: [
|
|
3194
|
+
"All types (",
|
|
3195
|
+
references.length,
|
|
3196
|
+
")"
|
|
3197
|
+
] }),
|
|
3198
|
+
documentTypes.map((type) => {
|
|
3199
|
+
const count = references.filter((ref) => ref._type === type).length;
|
|
3200
|
+
return /* @__PURE__ */ jsxs("option", { value: type, children: [
|
|
3201
|
+
type,
|
|
3202
|
+
" (",
|
|
3203
|
+
count,
|
|
3204
|
+
")"
|
|
3205
|
+
] }, type);
|
|
3206
|
+
})
|
|
3207
|
+
]
|
|
3208
|
+
}
|
|
3209
|
+
)
|
|
3210
|
+
] }),
|
|
3211
|
+
/* @__PURE__ */ jsx(Box, { style: { maxHeight: "400px", overflowY: "auto" }, children: /* @__PURE__ */ jsx(Stack, { space: 2, children: filteredReferences.length === 0 ? /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "No references match the selected filter." }) : filteredReferences.map((reference) => {
|
|
3212
|
+
const isSelected = selectedReferenceIds.has(reference._id), displayTitle = reference.title || reference._id;
|
|
3213
|
+
return /* @__PURE__ */ jsx(Card, { padding: 3, border: !0, radius: 2, children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 3, children: [
|
|
3214
|
+
/* @__PURE__ */ jsx(
|
|
3215
|
+
Checkbox,
|
|
3216
|
+
{
|
|
3217
|
+
checked: isSelected,
|
|
3218
|
+
onChange: () => handleToggleReference(reference._id)
|
|
3219
|
+
}
|
|
3220
|
+
),
|
|
3221
|
+
/* @__PURE__ */ jsxs(Flex, { flex: 1, align: "center", gap: 2, children: [
|
|
3222
|
+
/* @__PURE__ */ jsx(Text, { size: 1, children: displayTitle }),
|
|
3223
|
+
/* @__PURE__ */ jsx(Badge, { tone: "primary", fontSize: 0, children: reference._type })
|
|
3224
|
+
] })
|
|
3225
|
+
] }) }, reference._id);
|
|
3226
|
+
}) }) })
|
|
3227
|
+
] }) });
|
|
3104
3228
|
}, ReferenceConfigPanel = () => {
|
|
3105
|
-
const {
|
|
3229
|
+
const {
|
|
3230
|
+
dispatch,
|
|
3231
|
+
document,
|
|
3232
|
+
shouldTranslateReferences,
|
|
3233
|
+
maxReferenceDepth,
|
|
3234
|
+
config,
|
|
3235
|
+
references,
|
|
3236
|
+
selectedReferenceIds
|
|
3237
|
+
} = useTranslationContext(), client = useClient({ apiVersion: "vX" }), [isLoadingReferences, setIsLoadingReferences] = useState(!1), [showSelectionPanel, setShowSelectionPanel] = useState(!1), maxDepth = config.maxReferenceDepth || 3;
|
|
3106
3238
|
useEffect(() => {
|
|
3107
3239
|
(async () => {
|
|
3108
3240
|
if (setIsLoadingReferences(!0), !shouldTranslateReferences) {
|
|
@@ -3172,7 +3304,24 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3172
3304
|
)
|
|
3173
3305
|
] }) : null
|
|
3174
3306
|
] }),
|
|
3175
|
-
shouldTranslateReferences ? /* @__PURE__ */
|
|
3307
|
+
shouldTranslateReferences ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3308
|
+
/* @__PURE__ */ jsxs(Flex, { padding: 2, justify: "space-between", align: "center", children: [
|
|
3309
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: isLoadingReferences ? "Loading references..." : references.length === 0 ? "No translatable references found." : (() => {
|
|
3310
|
+
const selectedCount = references.filter((ref) => selectedReferenceIds.has(ref._id)).length, totalCount = references.length;
|
|
3311
|
+
return selectedCount === totalCount ? `${totalCount} reference${totalCount === 1 ? "" : "s"} will be translated along with this document.` : `${selectedCount} of ${totalCount} reference${totalCount === 1 ? "" : "s"} will be translated along with this document.`;
|
|
3312
|
+
})() }),
|
|
3313
|
+
!isLoadingReferences && references.length > 0 && /* @__PURE__ */ jsx(
|
|
3314
|
+
Button,
|
|
3315
|
+
{
|
|
3316
|
+
mode: "ghost",
|
|
3317
|
+
size: 1,
|
|
3318
|
+
text: showSelectionPanel ? "Hide selection" : "Select specific references...",
|
|
3319
|
+
onClick: () => setShowSelectionPanel(!showSelectionPanel)
|
|
3320
|
+
}
|
|
3321
|
+
)
|
|
3322
|
+
] }),
|
|
3323
|
+
!isLoadingReferences && references.length > 0 && showSelectionPanel && /* @__PURE__ */ jsx(ReferenceSelectionPanel, {})
|
|
3324
|
+
] }) : null
|
|
3176
3325
|
] });
|
|
3177
3326
|
}, WriteSettingsPanel = () => /* @__PURE__ */ jsx(ReferenceConfigPanel, {}), TranslationConfigView = ({ setIsDialogOpen }) => /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
|
|
3178
3327
|
/* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
@@ -3984,7 +4133,14 @@ function parseSRTContent(content) {
|
|
|
3984
4133
|
contentPreview: content.substring(0, 100) + (content.length > 100 ? "..." : "")
|
|
3985
4134
|
}), !content || content.trim() === "")
|
|
3986
4135
|
return [];
|
|
3987
|
-
|
|
4136
|
+
let normalizedContent = content;
|
|
4137
|
+
content.includes("\\n") && !content.includes(`
|
|
4138
|
+
`) && (normalizedContent = content.replace(/\\n/g, `
|
|
4139
|
+
`), devLog.debug("Unescaped newlines in SRT content", {
|
|
4140
|
+
originalLength: content.length,
|
|
4141
|
+
normalizedLength: normalizedContent.length
|
|
4142
|
+
}));
|
|
4143
|
+
const cues = [], cueBlocks = normalizedContent.split(/\n\s*\n/);
|
|
3988
4144
|
for (const block of cueBlocks) {
|
|
3989
4145
|
const lines = block.trim().split(`
|
|
3990
4146
|
`);
|