@purposeinplay/payload-ai-translate 0.1.0
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 +21 -0
- package/README.md +714 -0
- package/dist/alerts-collection.d.ts +21 -0
- package/dist/alerts-collection.js +159 -0
- package/dist/api.d.ts +4 -0
- package/dist/api.js +918 -0
- package/dist/bulk-translate-batches-collection.d.ts +29 -0
- package/dist/bulk-translate-batches-collection.js +404 -0
- package/dist/bulk-translate-units-collection.d.ts +35 -0
- package/dist/bulk-translate-units-collection.js +310 -0
- package/dist/client/estimated-cost-cell.d.ts +6 -0
- package/dist/client/estimated-cost-cell.js +12 -0
- package/dist/client/excluded-fields-field.d.ts +45 -0
- package/dist/client/excluded-fields-field.js +553 -0
- package/dist/client/field-translate-button.d.ts +6 -0
- package/dist/client/field-translate-button.js +199 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +6 -0
- package/dist/client/lib/use-global-kill-switches.d.ts +20 -0
- package/dist/client/lib/use-global-kill-switches.js +58 -0
- package/dist/client/translate-button.d.ts +2 -0
- package/dist/client/translate-button.js +228 -0
- package/dist/client/translate-modal.d.ts +16 -0
- package/dist/client/translate-modal.js +549 -0
- package/dist/client/translation-progress.d.ts +10 -0
- package/dist/client/translation-progress.js +297 -0
- package/dist/components/TranslationNavGroup.d.ts +45 -0
- package/dist/components/TranslationNavGroup.js +104 -0
- package/dist/defaults.d.ts +11 -0
- package/dist/defaults.js +16 -0
- package/dist/endpoints/client-config.d.ts +44 -0
- package/dist/endpoints/client-config.js +145 -0
- package/dist/endpoints/estimate.d.ts +5 -0
- package/dist/endpoints/estimate.js +237 -0
- package/dist/endpoints/progress.d.ts +2 -0
- package/dist/endpoints/progress.js +314 -0
- package/dist/endpoints/translate.d.ts +11 -0
- package/dist/endpoints/translate.js +376 -0
- package/dist/endpoints/translation-hub/_helpers.d.ts +140 -0
- package/dist/endpoints/translation-hub/_helpers.js +297 -0
- package/dist/endpoints/translation-hub/active.d.ts +21 -0
- package/dist/endpoints/translation-hub/active.js +220 -0
- package/dist/endpoints/translation-hub/cancel.d.ts +22 -0
- package/dist/endpoints/translation-hub/cancel.js +233 -0
- package/dist/endpoints/translation-hub/enqueue.d.ts +70 -0
- package/dist/endpoints/translation-hub/enqueue.js +529 -0
- package/dist/endpoints/translation-hub/failures.d.ts +12 -0
- package/dist/endpoints/translation-hub/failures.js +67 -0
- package/dist/endpoints/translation-hub/force-reset.d.ts +20 -0
- package/dist/endpoints/translation-hub/force-reset.js +144 -0
- package/dist/endpoints/translation-hub/index.d.ts +21 -0
- package/dist/endpoints/translation-hub/index.js +20 -0
- package/dist/endpoints/translation-hub/list.d.ts +40 -0
- package/dist/endpoints/translation-hub/list.js +182 -0
- package/dist/endpoints/translation-hub/preflight.d.ts +19 -0
- package/dist/endpoints/translation-hub/preflight.js +141 -0
- package/dist/endpoints/translation-hub/retry-failed.d.ts +38 -0
- package/dist/endpoints/translation-hub/retry-failed.js +235 -0
- package/dist/endpoints/translation-hub/revert.d.ts +88 -0
- package/dist/endpoints/translation-hub/revert.js +405 -0
- package/dist/endpoints/translation-hub/status.d.ts +45 -0
- package/dist/endpoints/translation-hub/status.js +391 -0
- package/dist/endpoints/translation-hub/usage-summary.d.ts +114 -0
- package/dist/endpoints/translation-hub/usage-summary.js +481 -0
- package/dist/exports/client.d.ts +6 -0
- package/dist/exports/client.js +6 -0
- package/dist/exports/components.d.ts +6 -0
- package/dist/exports/components.js +5 -0
- package/dist/exports/index.d.ts +8 -0
- package/dist/exports/index.js +7 -0
- package/dist/exports/providers.d.ts +9 -0
- package/dist/exports/providers.js +5 -0
- package/dist/exports/views-client.d.ts +23 -0
- package/dist/exports/views-client.js +22 -0
- package/dist/exports/views.d.ts +30 -0
- package/dist/exports/views.js +29 -0
- package/dist/hooks/after-change-global.d.ts +4 -0
- package/dist/hooks/after-change-global.js +109 -0
- package/dist/hooks/after-change.d.ts +16 -0
- package/dist/hooks/after-change.js +205 -0
- package/dist/hooks/after-delete.d.ts +30 -0
- package/dist/hooks/after-delete.js +95 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/jobs-collection.d.ts +17 -0
- package/dist/jobs-collection.js +139 -0
- package/dist/lexical/classifier.d.ts +3 -0
- package/dist/lexical/classifier.js +108 -0
- package/dist/lexical/deserializer.d.ts +4 -0
- package/dist/lexical/deserializer.js +263 -0
- package/dist/lexical/placeholder-integrity.d.ts +6 -0
- package/dist/lexical/placeholder-integrity.js +21 -0
- package/dist/lexical/placeholders.d.ts +21 -0
- package/dist/lexical/placeholders.js +117 -0
- package/dist/lexical/serializer.d.ts +21 -0
- package/dist/lexical/serializer.js +233 -0
- package/dist/lexical/types.d.ts +32 -0
- package/dist/lexical/types.js +1 -0
- package/dist/lib/auth-diagnostics.d.ts +14 -0
- package/dist/lib/auth-diagnostics.js +19 -0
- package/dist/lib/batch-counts.d.ts +58 -0
- package/dist/lib/batch-counts.js +105 -0
- package/dist/lib/bulk-translate-migrations.d.ts +92 -0
- package/dist/lib/bulk-translate-migrations.js +153 -0
- package/dist/lib/coalescing-queue.d.ts +38 -0
- package/dist/lib/coalescing-queue.js +69 -0
- package/dist/lib/content-extractor.d.ts +16 -0
- package/dist/lib/content-extractor.js +410 -0
- package/dist/lib/content-hash.d.ts +1 -0
- package/dist/lib/content-hash.js +19 -0
- package/dist/lib/content-patcher.d.ts +15 -0
- package/dist/lib/content-patcher.js +293 -0
- package/dist/lib/cost-guards.d.ts +2 -0
- package/dist/lib/cost-guards.js +18 -0
- package/dist/lib/daily-spend-cap.d.ts +58 -0
- package/dist/lib/daily-spend-cap.js +233 -0
- package/dist/lib/effective-locales.d.ts +181 -0
- package/dist/lib/effective-locales.js +302 -0
- package/dist/lib/error-messages.d.ts +245 -0
- package/dist/lib/error-messages.js +626 -0
- package/dist/lib/events.d.ts +39 -0
- package/dist/lib/events.js +146 -0
- package/dist/lib/exclude-fields.d.ts +3 -0
- package/dist/lib/exclude-fields.js +64 -0
- package/dist/lib/field-breadcrumb.d.ts +31 -0
- package/dist/lib/field-breadcrumb.js +227 -0
- package/dist/lib/field-diff.d.ts +1 -0
- package/dist/lib/field-diff.js +25 -0
- package/dist/lib/field-empty.d.ts +2 -0
- package/dist/lib/field-empty.js +68 -0
- package/dist/lib/field-resolver.d.ts +3 -0
- package/dist/lib/field-resolver.js +164 -0
- package/dist/lib/group-soft-skips.d.ts +39 -0
- package/dist/lib/group-soft-skips.js +45 -0
- package/dist/lib/locale-merge.d.ts +44 -0
- package/dist/lib/locale-merge.js +357 -0
- package/dist/lib/locale-row-check.d.ts +30 -0
- package/dist/lib/locale-row-check.js +64 -0
- package/dist/lib/logger.d.ts +74 -0
- package/dist/lib/logger.js +97 -0
- package/dist/lib/manual-edit-guard.d.ts +128 -0
- package/dist/lib/manual-edit-guard.js +393 -0
- package/dist/lib/output-validation.d.ts +48 -0
- package/dist/lib/output-validation.js +148 -0
- package/dist/lib/payload-read.d.ts +16 -0
- package/dist/lib/payload-read.js +51 -0
- package/dist/lib/per-doc-claim.d.ts +90 -0
- package/dist/lib/per-doc-claim.js +140 -0
- package/dist/lib/per-doc-lock.d.ts +94 -0
- package/dist/lib/per-doc-lock.js +119 -0
- package/dist/lib/persist-usage.d.ts +91 -0
- package/dist/lib/persist-usage.js +116 -0
- package/dist/lib/progress-store.d.ts +103 -0
- package/dist/lib/progress-store.js +314 -0
- package/dist/lib/rate-limiter.d.ts +3 -0
- package/dist/lib/rate-limiter.js +53 -0
- package/dist/lib/snapshot-select.d.ts +43 -0
- package/dist/lib/snapshot-select.js +108 -0
- package/dist/lib/translate-prompt.d.ts +31 -0
- package/dist/lib/translate-prompt.js +66 -0
- package/dist/lib/translation-token-bucket.d.ts +57 -0
- package/dist/lib/translation-token-bucket.js +365 -0
- package/dist/lib/truncate-source-value.d.ts +1 -0
- package/dist/lib/truncate-source-value.js +27 -0
- package/dist/manual-edit-collection.d.ts +22 -0
- package/dist/manual-edit-collection.js +124 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.js +934 -0
- package/dist/providers/ai-sdk-adapter.d.ts +35 -0
- package/dist/providers/ai-sdk-adapter.js +100 -0
- package/dist/providers/anthropic.d.ts +31 -0
- package/dist/providers/anthropic.js +66 -0
- package/dist/providers/custom.d.ts +36 -0
- package/dist/providers/custom.js +24 -0
- package/dist/providers/gemini.d.ts +20 -0
- package/dist/providers/gemini.js +48 -0
- package/dist/providers/mock.d.ts +2 -0
- package/dist/providers/mock.js +29 -0
- package/dist/providers/openai.d.ts +28 -0
- package/dist/providers/openai.js +69 -0
- package/dist/settings-global.d.ts +74 -0
- package/dist/settings-global.js +216 -0
- package/dist/tasks/bulk-translate-coordinator.d.ts +115 -0
- package/dist/tasks/bulk-translate-coordinator.js +708 -0
- package/dist/tasks/bulk-translate-doc-task.d.ts +142 -0
- package/dist/tasks/bulk-translate-doc-task.js +1000 -0
- package/dist/tasks/bulk-translate-janitor.d.ts +87 -0
- package/dist/tasks/bulk-translate-janitor.js +311 -0
- package/dist/tasks/translate-job-task.d.ts +51 -0
- package/dist/tasks/translate-job-task.js +154 -0
- package/dist/translate.d.ts +113 -0
- package/dist/translate.js +911 -0
- package/dist/translation-daily-spend-collection.d.ts +24 -0
- package/dist/translation-daily-spend-collection.js +133 -0
- package/dist/translation-rate-limits-collection.d.ts +30 -0
- package/dist/translation-rate-limits-collection.js +144 -0
- package/dist/types.d.ts +672 -0
- package/dist/types.js +1 -0
- package/dist/usage-collection.d.ts +14 -0
- package/dist/usage-collection.js +377 -0
- package/dist/views/BulkRunsHub/BatchRow.d.ts +32 -0
- package/dist/views/BulkRunsHub/BatchRow.js +1222 -0
- package/dist/views/BulkRunsHub/BucketRow.d.ts +62 -0
- package/dist/views/BulkRunsHub/BucketRow.js +982 -0
- package/dist/views/BulkRunsHub/BulkRunsHub.client.d.ts +18 -0
- package/dist/views/BulkRunsHub/BulkRunsHub.client.js +331 -0
- package/dist/views/BulkRunsHub/EmptyState.d.ts +6 -0
- package/dist/views/BulkRunsHub/EmptyState.js +64 -0
- package/dist/views/BulkRunsHub/FilterBar.d.ts +16 -0
- package/dist/views/BulkRunsHub/FilterBar.js +284 -0
- package/dist/views/BulkRunsHub/InFlightBanner.d.ts +14 -0
- package/dist/views/BulkRunsHub/InFlightBanner.js +59 -0
- package/dist/views/BulkRunsHub/StatusBadge.d.ts +64 -0
- package/dist/views/BulkRunsHub/StatusBadge.js +248 -0
- package/dist/views/BulkRunsHub/SummaryStrip.d.ts +22 -0
- package/dist/views/BulkRunsHub/SummaryStrip.js +249 -0
- package/dist/views/BulkRunsHub/bucket-grouping.d.ts +200 -0
- package/dist/views/BulkRunsHub/bucket-grouping.js +344 -0
- package/dist/views/BulkRunsHub/bucketFailureSummary.d.ts +9 -0
- package/dist/views/BulkRunsHub/bucketFailureSummary.js +36 -0
- package/dist/views/BulkRunsHub/dedupedStatusFetch.d.ts +5 -0
- package/dist/views/BulkRunsHub/dedupedStatusFetch.js +45 -0
- package/dist/views/BulkRunsHub/index.d.ts +17 -0
- package/dist/views/BulkRunsHub/index.js +80 -0
- package/dist/views/BulkRunsHub/urlFilters.d.ts +14 -0
- package/dist/views/BulkRunsHub/urlFilters.js +50 -0
- package/dist/views/BulkRunsHub/useBulkRunsList.d.ts +26 -0
- package/dist/views/BulkRunsHub/useBulkRunsList.js +204 -0
- package/dist/views/BulkRunsHub/useUrlFilters.d.ts +10 -0
- package/dist/views/BulkRunsHub/useUrlFilters.js +88 -0
- package/dist/views/TranslationHub/ActiveJobs.d.ts +6 -0
- package/dist/views/TranslationHub/ActiveJobs.js +320 -0
- package/dist/views/TranslationHub/AdvancedPanel.d.ts +17 -0
- package/dist/views/TranslationHub/AdvancedPanel.js +996 -0
- package/dist/views/TranslationHub/AlertBanner.d.ts +6 -0
- package/dist/views/TranslationHub/AlertBanner.js +568 -0
- package/dist/views/TranslationHub/AuditPanel.d.ts +6 -0
- package/dist/views/TranslationHub/AuditPanel.helpers.d.ts +44 -0
- package/dist/views/TranslationHub/AuditPanel.helpers.js +71 -0
- package/dist/views/TranslationHub/AuditPanel.js +1367 -0
- package/dist/views/TranslationHub/BulkTranslate.types.d.ts +242 -0
- package/dist/views/TranslationHub/BulkTranslate.types.js +36 -0
- package/dist/views/TranslationHub/BulkTranslateFailureDrawer.d.ts +19 -0
- package/dist/views/TranslationHub/BulkTranslateFailureDrawer.js +332 -0
- package/dist/views/TranslationHub/BulkTranslateMonitor.d.ts +28 -0
- package/dist/views/TranslationHub/BulkTranslateMonitor.js +305 -0
- package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.d.ts +3 -0
- package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.js +42 -0
- package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.d.ts +26 -0
- package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.js +95 -0
- package/dist/views/TranslationHub/BulkTranslatePreflightModal.d.ts +22 -0
- package/dist/views/TranslationHub/BulkTranslatePreflightModal.js +879 -0
- package/dist/views/TranslationHub/BulkTranslateTerminalCard.d.ts +29 -0
- package/dist/views/TranslationHub/BulkTranslateTerminalCard.js +445 -0
- package/dist/views/TranslationHub/BulkTranslateTrigger.d.ts +66 -0
- package/dist/views/TranslationHub/BulkTranslateTrigger.js +161 -0
- package/dist/views/TranslationHub/EditorRecentRunsPanel.d.ts +33 -0
- package/dist/views/TranslationHub/EditorRecentRunsPanel.js +290 -0
- package/dist/views/TranslationHub/Hub.client.d.ts +74 -0
- package/dist/views/TranslationHub/Hub.client.js +357 -0
- package/dist/views/TranslationHub/ModelCombobox.d.ts +14 -0
- package/dist/views/TranslationHub/ModelCombobox.js +415 -0
- package/dist/views/TranslationHub/PerCollectionConfig.d.ts +10 -0
- package/dist/views/TranslationHub/PerCollectionConfig.helpers.d.ts +16 -0
- package/dist/views/TranslationHub/PerCollectionConfig.helpers.js +19 -0
- package/dist/views/TranslationHub/PerCollectionConfig.js +759 -0
- package/dist/views/TranslationHub/SettingsRail.d.ts +11 -0
- package/dist/views/TranslationHub/SettingsRail.js +382 -0
- package/dist/views/TranslationHub/StatusStrip.d.ts +6 -0
- package/dist/views/TranslationHub/StatusStrip.js +451 -0
- package/dist/views/TranslationHub/UsageTable.d.ts +6 -0
- package/dist/views/TranslationHub/UsageTable.helpers.d.ts +69 -0
- package/dist/views/TranslationHub/UsageTable.helpers.js +49 -0
- package/dist/views/TranslationHub/UsageTable.js +1240 -0
- package/dist/views/TranslationHub/alertGrouping.d.ts +70 -0
- package/dist/views/TranslationHub/alertGrouping.js +99 -0
- package/dist/views/TranslationHub/index.d.ts +20 -0
- package/dist/views/TranslationHub/index.js +109 -0
- package/dist/views/TranslationHub/tabNavigation.d.ts +53 -0
- package/dist/views/TranslationHub/tabNavigation.js +74 -0
- package/dist/views/TranslationHub/terminalBannerVisibility.d.ts +33 -0
- package/dist/views/TranslationHub/terminalBannerVisibility.js +124 -0
- package/dist/views/TranslationHub/useBulkTranslateActive.d.ts +49 -0
- package/dist/views/TranslationHub/useBulkTranslateActive.js +251 -0
- package/dist/views/TranslationHub/useFocusTrap.d.ts +6 -0
- package/dist/views/TranslationHub/useFocusTrap.js +81 -0
- package/dist/views/TranslationHub/useTranslationHubUsageSummary.d.ts +77 -0
- package/dist/views/TranslationHub/useTranslationHubUsageSummary.js +267 -0
- package/dist/views/shared/EditorError.d.ts +97 -0
- package/dist/views/shared/EditorError.js +205 -0
- package/dist/views/shared/ModelCell.d.ts +18 -0
- package/dist/views/shared/ModelCell.js +31 -0
- package/dist/views/shared/docHref.d.ts +16 -0
- package/dist/views/shared/docHref.js +26 -0
- package/dist/views/shared/fetch-error-body.d.ts +25 -0
- package/dist/views/shared/fetch-error-body.js +42 -0
- package/dist/views/shared/filterPillStyle.d.ts +35 -0
- package/dist/views/shared/filterPillStyle.js +40 -0
- package/dist/views/shared/format.d.ts +75 -0
- package/dist/views/shared/format.js +131 -0
- package/package.json +141 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { toast, useConfig, useDocumentInfo, useField, useLocale } from '@payloadcms/ui';
|
|
4
|
+
import { formatAdminURL } from 'payload/shared';
|
|
5
|
+
import { Component, useState } from 'react';
|
|
6
|
+
import { useGlobalKillSwitches } from './lib/use-global-kill-switches.js';
|
|
7
|
+
// Error boundary — if useDocumentInfo throws on create view, render nothing
|
|
8
|
+
let FieldTranslateButtonBoundary = class FieldTranslateButtonBoundary extends Component {
|
|
9
|
+
constructor(props){
|
|
10
|
+
super(props);
|
|
11
|
+
this.state = {
|
|
12
|
+
hasError: false
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
static getDerivedStateFromError() {
|
|
16
|
+
return {
|
|
17
|
+
hasError: true
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
render() {
|
|
21
|
+
if (this.state.hasError) return null;
|
|
22
|
+
return this.props.children;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
function FieldTranslateButtonInner({ path }) {
|
|
26
|
+
const [translating, setTranslating] = useState(false);
|
|
27
|
+
// Inline error message rendered next to the button. Toast notifications
|
|
28
|
+
// can be missed when the consumer's admin layout doesn't mount a
|
|
29
|
+
// `<Toaster />` correctly (Sonner version mismatch, plugin queue
|
|
30
|
+
// orphaned by RootProvider position, etc.). The inline indicator gives
|
|
31
|
+
// the user feedback even in those cases. Cleared when the user clicks
|
|
32
|
+
// again or a translation succeeds.
|
|
33
|
+
const [errorMsg, setErrorMsg] = useState(null);
|
|
34
|
+
const documentInfo = useDocumentInfo();
|
|
35
|
+
const { config } = useConfig();
|
|
36
|
+
const locale = useLocale();
|
|
37
|
+
// Hook into the surrounding field's form state so we can write the
|
|
38
|
+
// translated value back into the input without making the user reload.
|
|
39
|
+
const { setValue } = useField({
|
|
40
|
+
path
|
|
41
|
+
});
|
|
42
|
+
// Plugin-wide manual-translate kill switch (v1.2.8). Called here at
|
|
43
|
+
// the top of the component (before any early returns) to satisfy
|
|
44
|
+
// rules-of-hooks. The actual hide check happens further down.
|
|
45
|
+
const killSwitches = useGlobalKillSwitches();
|
|
46
|
+
const globalSlug = documentInfo.globalSlug;
|
|
47
|
+
const id = documentInfo.id;
|
|
48
|
+
const collectionSlug = documentInfo.collectionSlug ?? documentInfo.slug;
|
|
49
|
+
const defaultLocale = config.localization ? config.localization.defaultLocale : 'en';
|
|
50
|
+
const currentLocale = typeof locale === 'string' ? locale : locale?.code;
|
|
51
|
+
if (!currentLocale || currentLocale === defaultLocale) return null;
|
|
52
|
+
// Hide on manual-translate kill switch off (see top-of-component
|
|
53
|
+
// hook call for rules-of-hooks rationale). Optimistic while loading
|
|
54
|
+
// (`null`) — server-side endpoint gate is the real authority.
|
|
55
|
+
if (killSwitches && !killSwitches.manualEnabled) return null;
|
|
56
|
+
// Resolve doc context: globals don't have a numeric id, just a slug
|
|
57
|
+
let pathPrefix = null;
|
|
58
|
+
let bodyForGlobal = false;
|
|
59
|
+
if (globalSlug) {
|
|
60
|
+
pathPrefix = `/globals/${globalSlug}`;
|
|
61
|
+
bodyForGlobal = true;
|
|
62
|
+
} else if (id && collectionSlug) {
|
|
63
|
+
pathPrefix = `/${collectionSlug}`;
|
|
64
|
+
} else {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const handleTranslate = async ()=>{
|
|
68
|
+
if (translating) return;
|
|
69
|
+
setTranslating(true);
|
|
70
|
+
setErrorMsg(null);
|
|
71
|
+
try {
|
|
72
|
+
const { routes } = config;
|
|
73
|
+
const apiRoute = routes?.api ?? '/api';
|
|
74
|
+
const body = {
|
|
75
|
+
targetLocales: [
|
|
76
|
+
currentLocale
|
|
77
|
+
],
|
|
78
|
+
fields: [
|
|
79
|
+
path
|
|
80
|
+
],
|
|
81
|
+
// Per-field translate writes only the field the user clicked.
|
|
82
|
+
// The form patches the value into the local form state via
|
|
83
|
+
// `setValue` regardless — the locale write is a server-side
|
|
84
|
+
// side effect that should match the user's narrow intent.
|
|
85
|
+
writeMode: 'minimal'
|
|
86
|
+
};
|
|
87
|
+
if (!bodyForGlobal) body.id = id;
|
|
88
|
+
const apiPath = `${pathPrefix}/ai-translate`;
|
|
89
|
+
const res = await fetch(formatAdminURL({
|
|
90
|
+
apiRoute,
|
|
91
|
+
path: apiPath
|
|
92
|
+
}), {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
credentials: 'include',
|
|
95
|
+
headers: {
|
|
96
|
+
'Content-Type': 'application/json'
|
|
97
|
+
},
|
|
98
|
+
body: JSON.stringify(body)
|
|
99
|
+
});
|
|
100
|
+
if (!res.ok) {
|
|
101
|
+
const errorData = await res.json().catch(()=>({}));
|
|
102
|
+
throw new Error(errorData.error ?? `HTTP ${res.status}`);
|
|
103
|
+
}
|
|
104
|
+
const data = await res.json();
|
|
105
|
+
const result = data.results[0];
|
|
106
|
+
const failCount = result?.failed?.length ?? 0;
|
|
107
|
+
if (failCount === 0) {
|
|
108
|
+
// Patch the form state with the new value when the API returns it
|
|
109
|
+
// (plain text/textarea only — richText/blocks need a doc reload).
|
|
110
|
+
const successEntry = result?.succeeded?.find((s)=>s.fieldPath === path);
|
|
111
|
+
if (typeof successEntry?.translatedText === 'string') {
|
|
112
|
+
const newValue = successEntry.translatedText;
|
|
113
|
+
// 1) Update Payload's form state via the field-level setValue.
|
|
114
|
+
// This is the "right" path but doesn't always trigger the
|
|
115
|
+
// sibling input's re-render — Payload's text input is
|
|
116
|
+
// React-Compiler-memoized and may not observe a dispatch
|
|
117
|
+
// that originates from a different `useField` registration.
|
|
118
|
+
setValue(newValue);
|
|
119
|
+
// 2) Belt-and-suspenders: drive the DOM input directly and fire
|
|
120
|
+
// an `input` event the way a real keystroke would. This goes
|
|
121
|
+
// through the input's own onChange → its own setValue, which
|
|
122
|
+
// is the codepath Payload's memoization tracks.
|
|
123
|
+
// Runs in the next microtask so we don't fight React's
|
|
124
|
+
// render of step 1.
|
|
125
|
+
queueMicrotask(()=>{
|
|
126
|
+
const inputId = `field-${path.replace(/\./g, '__')}`;
|
|
127
|
+
const el = document.getElementById(inputId);
|
|
128
|
+
if (!el || el.value === newValue) return;
|
|
129
|
+
const proto = el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
|
|
130
|
+
const setter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
|
|
131
|
+
if (!setter) return;
|
|
132
|
+
setter.call(el, newValue);
|
|
133
|
+
el.dispatchEvent(new Event('input', {
|
|
134
|
+
bubbles: true
|
|
135
|
+
}));
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
toast.success(`Translated "${path}" to ${currentLocale}`);
|
|
139
|
+
} else {
|
|
140
|
+
const reason = result?.failed?.[0]?.error;
|
|
141
|
+
const msg = reason ? `Failed to translate "${path}": ${reason.slice(0, 200)}` : `Failed to translate "${path}"`;
|
|
142
|
+
toast.error(msg);
|
|
143
|
+
setErrorMsg(reason ?? 'Translation failed');
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
const msg = error instanceof Error ? error.message : 'Translation failed';
|
|
147
|
+
toast.error(msg);
|
|
148
|
+
setErrorMsg(msg);
|
|
149
|
+
} finally{
|
|
150
|
+
setTranslating(false);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
154
|
+
children: [
|
|
155
|
+
/*#__PURE__*/ _jsx("button", {
|
|
156
|
+
// Without an explicit type the browser defaults to "submit" inside the
|
|
157
|
+
// Payload edit `<form>`, so a click would save the document (bumping
|
|
158
|
+
// the version + showing "Updated successfully") in addition to firing
|
|
159
|
+
// the translation. Force "button" to keep it side-effect-free.
|
|
160
|
+
type: "button",
|
|
161
|
+
onClick: handleTranslate,
|
|
162
|
+
disabled: translating,
|
|
163
|
+
title: `Translate this field to ${currentLocale}`,
|
|
164
|
+
style: {
|
|
165
|
+
display: 'inline-flex',
|
|
166
|
+
alignItems: 'center',
|
|
167
|
+
gap: '4px',
|
|
168
|
+
padding: '4px 8px',
|
|
169
|
+
marginTop: '4px',
|
|
170
|
+
backgroundColor: 'transparent',
|
|
171
|
+
border: '1px solid var(--theme-elevation-300)',
|
|
172
|
+
borderRadius: '3px',
|
|
173
|
+
color: 'var(--theme-elevation-500)',
|
|
174
|
+
cursor: translating ? 'wait' : 'pointer',
|
|
175
|
+
fontSize: '12px',
|
|
176
|
+
opacity: translating ? 0.6 : 1
|
|
177
|
+
},
|
|
178
|
+
children: translating ? 'Translating...' : 'Translate'
|
|
179
|
+
}),
|
|
180
|
+
errorMsg && /*#__PURE__*/ _jsx("div", {
|
|
181
|
+
role: "alert",
|
|
182
|
+
style: {
|
|
183
|
+
marginTop: '4px',
|
|
184
|
+
color: 'var(--theme-error-500)',
|
|
185
|
+
fontSize: '12px',
|
|
186
|
+
maxWidth: '100%'
|
|
187
|
+
},
|
|
188
|
+
children: errorMsg
|
|
189
|
+
})
|
|
190
|
+
]
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
export function FieldTranslateButton(props) {
|
|
194
|
+
return /*#__PURE__*/ _jsx(FieldTranslateButtonBoundary, {
|
|
195
|
+
children: /*#__PURE__*/ _jsx(FieldTranslateButtonInner, {
|
|
196
|
+
...props
|
|
197
|
+
})
|
|
198
|
+
});
|
|
199
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { EstimatedCostCell } from './estimated-cost-cell.js';
|
|
2
|
+
export { ExcludedFieldsField } from './excluded-fields-field.js';
|
|
3
|
+
export { FieldTranslateButton } from './field-translate-button.js';
|
|
4
|
+
export { TranslateButton } from './translate-button.js';
|
|
5
|
+
export { TranslateModal } from './translate-modal.js';
|
|
6
|
+
export { TranslationProgress } from './translation-progress.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { EstimatedCostCell } from './estimated-cost-cell.js';
|
|
2
|
+
export { ExcludedFieldsField } from './excluded-fields-field.js';
|
|
3
|
+
export { FieldTranslateButton } from './field-translate-button.js';
|
|
4
|
+
export { TranslateButton } from './translate-button.js';
|
|
5
|
+
export { TranslateModal } from './translate-modal.js';
|
|
6
|
+
export { TranslationProgress } from './translation-progress.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side view of the three plugin-wide kill switches (v1.2.8).
|
|
3
|
+
* Fetched from `/api/ai-translate/client-config` on mount; the response
|
|
4
|
+
* is cached at module scope with a 15-second TTL so multiple buttons
|
|
5
|
+
* mounting on the same admin view collapse to one network call.
|
|
6
|
+
*
|
|
7
|
+
* Optimistic default: components that consume this hook should treat
|
|
8
|
+
* `null` (still loading) as "show normally". On a slow connection an
|
|
9
|
+
* admin user briefly sees the affordance before it hides — accepted
|
|
10
|
+
* trade-off since the server-side gates are the real source of truth
|
|
11
|
+
* (Translate endpoint returns 403, Enqueue endpoint returns 403).
|
|
12
|
+
*/
|
|
13
|
+
export type GlobalKillSwitchesClient = {
|
|
14
|
+
autoEnabled: boolean;
|
|
15
|
+
manualEnabled: boolean;
|
|
16
|
+
bulkEnabled: boolean;
|
|
17
|
+
};
|
|
18
|
+
/** @internal test helper */
|
|
19
|
+
export declare function _resetGlobalKillSwitchesCache(): void;
|
|
20
|
+
export declare function useGlobalKillSwitches(): GlobalKillSwitchesClient | null;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useConfig } from '@payloadcms/ui';
|
|
3
|
+
import { formatAdminURL } from 'payload/shared';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
const TTL_MS = 15_000;
|
|
6
|
+
const cache = new Map();
|
|
7
|
+
const DEFAULT_OPEN = {
|
|
8
|
+
autoEnabled: true,
|
|
9
|
+
manualEnabled: true,
|
|
10
|
+
bulkEnabled: true
|
|
11
|
+
};
|
|
12
|
+
function fetchKillSwitches(url) {
|
|
13
|
+
return fetch(url, {
|
|
14
|
+
credentials: 'include'
|
|
15
|
+
}).then((res)=>res.ok ? res.json() : null).then((data)=>{
|
|
16
|
+
const raw = data?.globalKillSwitches;
|
|
17
|
+
if (!raw) return DEFAULT_OPEN;
|
|
18
|
+
return {
|
|
19
|
+
autoEnabled: raw.autoEnabled !== false,
|
|
20
|
+
manualEnabled: raw.manualEnabled !== false,
|
|
21
|
+
bulkEnabled: raw.bulkEnabled !== false
|
|
22
|
+
};
|
|
23
|
+
}).catch(()=>DEFAULT_OPEN);
|
|
24
|
+
}
|
|
25
|
+
/** @internal test helper */ export function _resetGlobalKillSwitchesCache() {
|
|
26
|
+
cache.clear();
|
|
27
|
+
}
|
|
28
|
+
export function useGlobalKillSwitches() {
|
|
29
|
+
const { config } = useConfig();
|
|
30
|
+
const apiRoute = config.routes?.api ?? '/api';
|
|
31
|
+
const url = formatAdminURL({
|
|
32
|
+
apiRoute,
|
|
33
|
+
path: '/ai-translate/client-config',
|
|
34
|
+
serverURL: ''
|
|
35
|
+
});
|
|
36
|
+
const [switches, setSwitches] = useState(null);
|
|
37
|
+
useEffect(()=>{
|
|
38
|
+
let cancelled = false;
|
|
39
|
+
const now = Date.now();
|
|
40
|
+
const existing = cache.get(url);
|
|
41
|
+
if (!existing || existing.expiresAt <= now) {
|
|
42
|
+
cache.set(url, {
|
|
43
|
+
promise: fetchKillSwitches(url),
|
|
44
|
+
expiresAt: now + TTL_MS
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const entry = cache.get(url);
|
|
48
|
+
entry?.promise.then((s)=>{
|
|
49
|
+
if (!cancelled) setSwitches(s);
|
|
50
|
+
});
|
|
51
|
+
return ()=>{
|
|
52
|
+
cancelled = true;
|
|
53
|
+
};
|
|
54
|
+
}, [
|
|
55
|
+
url
|
|
56
|
+
]);
|
|
57
|
+
return switches;
|
|
58
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useConfig, useDocumentInfo, useFormProcessing } from '@payloadcms/ui';
|
|
4
|
+
import { formatAdminURL } from 'payload/shared';
|
|
5
|
+
import React, { useEffect, useState } from 'react';
|
|
6
|
+
import { useGlobalKillSwitches } from './lib/use-global-kill-switches.js';
|
|
7
|
+
import { TranslateModal } from './translate-modal.js';
|
|
8
|
+
import { TranslationProgress } from './translation-progress.js';
|
|
9
|
+
function resolveDocContext(info) {
|
|
10
|
+
const globalSlug = info.globalSlug;
|
|
11
|
+
if (globalSlug) {
|
|
12
|
+
return {
|
|
13
|
+
kind: 'global',
|
|
14
|
+
slug: globalSlug,
|
|
15
|
+
pathPrefix: `/globals/${globalSlug}`,
|
|
16
|
+
pollKey: globalSlug
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const id = info.id;
|
|
20
|
+
const collectionSlug = info.collectionSlug ?? info.slug;
|
|
21
|
+
if (id && collectionSlug) {
|
|
22
|
+
return {
|
|
23
|
+
kind: 'collection',
|
|
24
|
+
slug: collectionSlug,
|
|
25
|
+
id,
|
|
26
|
+
pathPrefix: `/${collectionSlug}`,
|
|
27
|
+
pollKey: String(id)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const SUBMIT_FEEDBACK_KEYFRAMES = `
|
|
33
|
+
@keyframes ai-translate-pre-indeterminate {
|
|
34
|
+
0% { left: -35%; right: 100%; }
|
|
35
|
+
60% { left: 100%; right: -35%; }
|
|
36
|
+
100% { left: 100%; right: -35%; }
|
|
37
|
+
}
|
|
38
|
+
`;
|
|
39
|
+
/**
|
|
40
|
+
* Optimistic "Saving…" indicator shown while Payload's form is
|
|
41
|
+
* submitting (or background-autosaving) AND we haven't yet picked up
|
|
42
|
+
* an active translation job.
|
|
43
|
+
*
|
|
44
|
+
* Why: Payload's publish flow round-trips through several
|
|
45
|
+
* POST/PATCH calls (autosave debounce + server actions + final
|
|
46
|
+
* publish) which can take 5-9s wall-clock before the after-change
|
|
47
|
+
* hook fires + `createJob` runs. Without this, the user clicks
|
|
48
|
+
* Publish and stares at unchanged UI for that whole window, then
|
|
49
|
+
* suddenly the progress bar appears mid-translation. With this,
|
|
50
|
+
* they get feedback within ~50ms of click.
|
|
51
|
+
*
|
|
52
|
+
* Wording is neutral ("Saving…") because this also fires on plain
|
|
53
|
+
* draft saves and autosave debounces, where no translation will run.
|
|
54
|
+
* Naming a translation here would be misleading; the real progress
|
|
55
|
+
* bar takes over the moment a job is registered server-side.
|
|
56
|
+
*
|
|
57
|
+
* Visually matches the indeterminate state of `TranslationProgress`
|
|
58
|
+
* so the transition into the real bar feels continuous.
|
|
59
|
+
*/ function SubmitFeedback({ formProcessing, showWhileTranslating }) {
|
|
60
|
+
if (!formProcessing || !showWhileTranslating) return null;
|
|
61
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
62
|
+
style: {
|
|
63
|
+
marginTop: '8px',
|
|
64
|
+
padding: '12px 16px',
|
|
65
|
+
backgroundColor: 'var(--theme-elevation-50)',
|
|
66
|
+
border: '1px solid var(--theme-elevation-200)',
|
|
67
|
+
borderRadius: '4px',
|
|
68
|
+
fontSize: '13px',
|
|
69
|
+
color: 'var(--theme-text)'
|
|
70
|
+
},
|
|
71
|
+
role: "status",
|
|
72
|
+
"aria-live": "polite",
|
|
73
|
+
children: [
|
|
74
|
+
/*#__PURE__*/ _jsx("style", {
|
|
75
|
+
children: SUBMIT_FEEDBACK_KEYFRAMES
|
|
76
|
+
}),
|
|
77
|
+
/*#__PURE__*/ _jsx("div", {
|
|
78
|
+
style: {
|
|
79
|
+
marginBottom: '8px',
|
|
80
|
+
fontWeight: 500
|
|
81
|
+
},
|
|
82
|
+
children: "Saving…"
|
|
83
|
+
}),
|
|
84
|
+
/*#__PURE__*/ _jsx("div", {
|
|
85
|
+
style: {
|
|
86
|
+
position: 'relative',
|
|
87
|
+
width: '100%',
|
|
88
|
+
height: '6px',
|
|
89
|
+
backgroundColor: 'var(--theme-elevation-200)',
|
|
90
|
+
borderRadius: '3px',
|
|
91
|
+
overflow: 'hidden'
|
|
92
|
+
},
|
|
93
|
+
children: /*#__PURE__*/ _jsx("div", {
|
|
94
|
+
style: {
|
|
95
|
+
position: 'absolute',
|
|
96
|
+
top: 0,
|
|
97
|
+
bottom: 0,
|
|
98
|
+
borderRadius: '3px',
|
|
99
|
+
backgroundColor: 'var(--theme-success-500)',
|
|
100
|
+
backgroundImage: 'linear-gradient(90deg, color-mix(in srgb, var(--theme-success-500) 70%, white) 0%, var(--theme-success-500) 50%, color-mix(in srgb, var(--theme-success-500) 70%, white) 100%)',
|
|
101
|
+
animation: 'ai-translate-pre-indeterminate 1.6s ease-in-out infinite'
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
]
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
export function TranslateButton() {
|
|
109
|
+
const [modalOpen, setModalOpen] = useState(false);
|
|
110
|
+
const [activeJobId, setActiveJobId] = useState(null);
|
|
111
|
+
const documentInfo = useDocumentInfo();
|
|
112
|
+
const { config } = useConfig();
|
|
113
|
+
// Only react to the real submit (Save / Publish click).
|
|
114
|
+
// `useFormBackgroundProcessing` also fires for every autosave
|
|
115
|
+
// debounce, which on a versioned collection with
|
|
116
|
+
// `autosave.interval: 400` happens on every keystroke — and the
|
|
117
|
+
// optimistic indicator below looks identical to the translation
|
|
118
|
+
// progress bar, so editors read "Saving…" on draft autosave as
|
|
119
|
+
// "translating now, did I just spend tokens?". Trade the ~5-9s
|
|
120
|
+
// optimistic head-start on Publish click for that confusion-free
|
|
121
|
+
// signal: the bar only appears when a translation is actually
|
|
122
|
+
// imminent (real submit) or running (`activeJobId`).
|
|
123
|
+
const formProcessing = useFormProcessing();
|
|
124
|
+
const isAnyFormActivity = formProcessing;
|
|
125
|
+
// Plugin-wide manual-translate kill switch (v1.2.8). Called at top
|
|
126
|
+
// level for rules-of-hooks; the `return null` below combines with
|
|
127
|
+
// the docCtx check.
|
|
128
|
+
const killSwitches = useGlobalKillSwitches();
|
|
129
|
+
const docCtx = resolveDocContext(documentInfo);
|
|
130
|
+
const apiRoute = config.routes?.api ?? '/api';
|
|
131
|
+
// Open ONE long-lived EventSource on mount. The server holds the
|
|
132
|
+
// connection open and pushes a 'progress' event the moment a
|
|
133
|
+
// translation job is created for this doc — typically within ~50ms of
|
|
134
|
+
// an after-change hook firing. No client polling, no race-the-server
|
|
135
|
+
// burst timers. Replaces the previous setInterval + form-submit burst
|
|
136
|
+
// that produced ~10s of dead-air UX between Publish click and
|
|
137
|
+
// progress bar.
|
|
138
|
+
//
|
|
139
|
+
// Effect deps are PRIMITIVES (`pollKey`, `pathPrefix`, `apiRoute`),
|
|
140
|
+
// not the full `docCtx` object — `resolveDocContext` returns a new
|
|
141
|
+
// object reference each render, which would tear down + recreate the
|
|
142
|
+
// EventSource constantly and defeat the whole point.
|
|
143
|
+
const pollKey = docCtx?.pollKey;
|
|
144
|
+
const pathPrefix = docCtx?.pathPrefix;
|
|
145
|
+
useEffect(()=>{
|
|
146
|
+
if (!pollKey || !pathPrefix) return;
|
|
147
|
+
if (typeof EventSource === 'undefined') return;
|
|
148
|
+
const path = `${pathPrefix}/ai-translate/progress?docId=${encodeURIComponent(pollKey)}`;
|
|
149
|
+
const url = formatAdminURL({
|
|
150
|
+
apiRoute,
|
|
151
|
+
path
|
|
152
|
+
});
|
|
153
|
+
const es = new EventSource(url, {
|
|
154
|
+
withCredentials: true
|
|
155
|
+
});
|
|
156
|
+
function handleProgress(e) {
|
|
157
|
+
try {
|
|
158
|
+
const data = JSON.parse(e.data);
|
|
159
|
+
if (data.status === 'running' && data.jobId) {
|
|
160
|
+
setActiveJobId(data.jobId);
|
|
161
|
+
}
|
|
162
|
+
} catch {
|
|
163
|
+
// malformed event — ignore
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
es.addEventListener('progress', handleProgress);
|
|
167
|
+
// EventSource auto-reconnects when the server closes; the
|
|
168
|
+
// `retry: 30000` directive paces idle reconnects.
|
|
169
|
+
return ()=>{
|
|
170
|
+
es.removeEventListener('progress', handleProgress);
|
|
171
|
+
es.close();
|
|
172
|
+
};
|
|
173
|
+
}, [
|
|
174
|
+
pollKey,
|
|
175
|
+
pathPrefix,
|
|
176
|
+
apiRoute
|
|
177
|
+
]);
|
|
178
|
+
if (!docCtx) return null;
|
|
179
|
+
// Hide on manual-translate kill switch off. Optimistic while loading.
|
|
180
|
+
if (killSwitches && !killSwitches.manualEnabled) return null;
|
|
181
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
182
|
+
style: {
|
|
183
|
+
marginTop: '16px'
|
|
184
|
+
},
|
|
185
|
+
children: [
|
|
186
|
+
/*#__PURE__*/ _jsx("button", {
|
|
187
|
+
type: "button",
|
|
188
|
+
onClick: ()=>setModalOpen(true),
|
|
189
|
+
disabled: !!activeJobId,
|
|
190
|
+
style: {
|
|
191
|
+
width: '100%',
|
|
192
|
+
padding: '10px 16px',
|
|
193
|
+
backgroundColor: activeJobId ? 'var(--theme-elevation-50)' : 'var(--theme-elevation-100)',
|
|
194
|
+
border: '1px solid var(--theme-elevation-300)',
|
|
195
|
+
borderRadius: '4px',
|
|
196
|
+
color: 'var(--theme-text)',
|
|
197
|
+
cursor: activeJobId ? 'default' : 'pointer',
|
|
198
|
+
fontSize: '14px',
|
|
199
|
+
fontWeight: 500,
|
|
200
|
+
opacity: activeJobId ? 0.7 : 1
|
|
201
|
+
},
|
|
202
|
+
children: activeJobId ? 'Translating...' : 'Translate...'
|
|
203
|
+
}),
|
|
204
|
+
/*#__PURE__*/ _jsx(SubmitFeedback, {
|
|
205
|
+
formProcessing: isAnyFormActivity,
|
|
206
|
+
showWhileTranslating: !activeJobId
|
|
207
|
+
}),
|
|
208
|
+
activeJobId && /*#__PURE__*/ _jsx("div", {
|
|
209
|
+
style: {
|
|
210
|
+
marginTop: '8px'
|
|
211
|
+
},
|
|
212
|
+
children: /*#__PURE__*/ _jsx(TranslationProgress, {
|
|
213
|
+
jobId: activeJobId,
|
|
214
|
+
collectionSlug: docCtx.kind === 'collection' ? docCtx.slug : undefined,
|
|
215
|
+
globalSlug: docCtx.kind === 'global' ? docCtx.slug : undefined,
|
|
216
|
+
onComplete: ()=>setActiveJobId(null)
|
|
217
|
+
})
|
|
218
|
+
}),
|
|
219
|
+
modalOpen && /*#__PURE__*/ _jsx(TranslateModal, {
|
|
220
|
+
docId: docCtx.kind === 'collection' ? docCtx.id : docCtx.slug,
|
|
221
|
+
collectionSlug: docCtx.kind === 'collection' ? docCtx.slug : undefined,
|
|
222
|
+
globalSlug: docCtx.kind === 'global' ? docCtx.slug : undefined,
|
|
223
|
+
onClose: ()=>setModalOpen(false),
|
|
224
|
+
onJobStarted: setActiveJobId
|
|
225
|
+
})
|
|
226
|
+
]
|
|
227
|
+
});
|
|
228
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type TranslateModalProps = {
|
|
3
|
+
docId: string | number;
|
|
4
|
+
collectionSlug?: string;
|
|
5
|
+
globalSlug?: string;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
/**
|
|
8
|
+
* Called when the translate POST returns a `jobId` (async mode).
|
|
9
|
+
* The parent TranslateButton uses this to hand off the active job to its
|
|
10
|
+
* sidebar progress widget so the modal can close immediately rather than
|
|
11
|
+
* trapping the user behind a duplicate progress UI.
|
|
12
|
+
*/
|
|
13
|
+
onJobStarted?: (jobId: string) => void;
|
|
14
|
+
};
|
|
15
|
+
export declare function TranslateModal({ docId, collectionSlug, globalSlug, onClose, onJobStarted, }: TranslateModalProps): React.ReactPortal | null;
|
|
16
|
+
export {};
|