@procore/ai-translations 0.2.0 → 0.3.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/dist/legacy/index.d.mts +54 -1
- package/dist/legacy/index.d.ts +54 -1
- package/dist/legacy/index.js +77 -10
- package/dist/legacy/index.mjs +69 -3
- package/dist/modern/index.d.mts +54 -1
- package/dist/modern/index.d.ts +54 -1
- package/dist/modern/index.js +74 -10
- package/dist/modern/index.mjs +66 -3
- package/package.json +5 -2
package/dist/legacy/index.d.mts
CHANGED
|
@@ -2,6 +2,20 @@ import * as _tanstack_react_query from '@tanstack/react-query';
|
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
3
|
import React, { ReactNode } from 'react';
|
|
4
4
|
|
|
5
|
+
interface TranslationProgress {
|
|
6
|
+
/**
|
|
7
|
+
* Progress percentage (0-100)
|
|
8
|
+
*/
|
|
9
|
+
progress: number;
|
|
10
|
+
/**
|
|
11
|
+
* Current number of strings translated
|
|
12
|
+
*/
|
|
13
|
+
current: number;
|
|
14
|
+
/**
|
|
15
|
+
* Total number of strings to translate
|
|
16
|
+
*/
|
|
17
|
+
total: number;
|
|
18
|
+
}
|
|
5
19
|
interface TranslationQueueEntry {
|
|
6
20
|
originalText: string;
|
|
7
21
|
isTranslated: boolean;
|
|
@@ -11,6 +25,26 @@ interface TranslationQueueEntry {
|
|
|
11
25
|
tool: string;
|
|
12
26
|
translationStrategy: 'frontend_translations' | 'backend_translations';
|
|
13
27
|
}
|
|
28
|
+
interface ModelDownloadProgress {
|
|
29
|
+
/** Bytes already downloaded */
|
|
30
|
+
loaded: number;
|
|
31
|
+
/** Total bytes to download */
|
|
32
|
+
total: number;
|
|
33
|
+
/** Download percentage (0-100) */
|
|
34
|
+
progress: number;
|
|
35
|
+
/** True once the model has finished downloading */
|
|
36
|
+
isComplete: boolean;
|
|
37
|
+
}
|
|
38
|
+
interface AITranslationContextValue {
|
|
39
|
+
ait: (text: string) => Promise<string>;
|
|
40
|
+
locale: string;
|
|
41
|
+
renderVersion?: number;
|
|
42
|
+
/** Batch-translation progress; `null` while the queue is idle. */
|
|
43
|
+
translationProgress: TranslationProgress | null;
|
|
44
|
+
/** Chrome AI model download progress; `null` until a download begins. */
|
|
45
|
+
modelDownloadProgress: ModelDownloadProgress | null;
|
|
46
|
+
tool: string;
|
|
47
|
+
}
|
|
14
48
|
|
|
15
49
|
interface ConfigurationResponse {
|
|
16
50
|
[key: string]: ToolConfig;
|
|
@@ -63,6 +97,25 @@ interface UseConfigOptions {
|
|
|
63
97
|
*/
|
|
64
98
|
declare function useConfig(toolName: string, options?: UseConfigOptions): _tanstack_react_query.UseQueryResult<ConfigurationResponse | null, unknown>;
|
|
65
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Returns the full AI-translation context value provided by the nearest
|
|
102
|
+
* `AITranslationProvider`.
|
|
103
|
+
*
|
|
104
|
+
* Use this hook when you need more than one value from the context at once.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* function MyComponent() {
|
|
109
|
+
* const { ait, locale, translationProgress, modelDownloadProgress } = useAITranslation();
|
|
110
|
+
*
|
|
111
|
+
* return <span>{locale}</span>;
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* @throws if called outside of an `AITranslationProvider`.
|
|
116
|
+
*/
|
|
117
|
+
declare function useAITranslation(): AITranslationContextValue;
|
|
118
|
+
|
|
66
119
|
interface AITranslationProviderProps {
|
|
67
120
|
children: ReactNode;
|
|
68
121
|
locale: string;
|
|
@@ -123,4 +176,4 @@ declare global {
|
|
|
123
176
|
var _BACKEND_AI_TRANSLATION_IN_PROGRESS_: boolean;
|
|
124
177
|
}
|
|
125
178
|
|
|
126
|
-
export { AITranslateText, type AITranslateTextProps, AITranslationProvider, AI_TRANSLATION_FEATURE_FLAG_KEY, type TranslatedIconProps, type UseConfigOptions, getAITranslationLDId, useConfig };
|
|
179
|
+
export { AITranslateText, type AITranslateTextProps, AITranslationProvider, AI_TRANSLATION_FEATURE_FLAG_KEY, type TranslatedIconProps, type UseConfigOptions, getAITranslationLDId, useAITranslation, useConfig };
|
package/dist/legacy/index.d.ts
CHANGED
|
@@ -2,6 +2,20 @@ import * as _tanstack_react_query from '@tanstack/react-query';
|
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
3
|
import React, { ReactNode } from 'react';
|
|
4
4
|
|
|
5
|
+
interface TranslationProgress {
|
|
6
|
+
/**
|
|
7
|
+
* Progress percentage (0-100)
|
|
8
|
+
*/
|
|
9
|
+
progress: number;
|
|
10
|
+
/**
|
|
11
|
+
* Current number of strings translated
|
|
12
|
+
*/
|
|
13
|
+
current: number;
|
|
14
|
+
/**
|
|
15
|
+
* Total number of strings to translate
|
|
16
|
+
*/
|
|
17
|
+
total: number;
|
|
18
|
+
}
|
|
5
19
|
interface TranslationQueueEntry {
|
|
6
20
|
originalText: string;
|
|
7
21
|
isTranslated: boolean;
|
|
@@ -11,6 +25,26 @@ interface TranslationQueueEntry {
|
|
|
11
25
|
tool: string;
|
|
12
26
|
translationStrategy: 'frontend_translations' | 'backend_translations';
|
|
13
27
|
}
|
|
28
|
+
interface ModelDownloadProgress {
|
|
29
|
+
/** Bytes already downloaded */
|
|
30
|
+
loaded: number;
|
|
31
|
+
/** Total bytes to download */
|
|
32
|
+
total: number;
|
|
33
|
+
/** Download percentage (0-100) */
|
|
34
|
+
progress: number;
|
|
35
|
+
/** True once the model has finished downloading */
|
|
36
|
+
isComplete: boolean;
|
|
37
|
+
}
|
|
38
|
+
interface AITranslationContextValue {
|
|
39
|
+
ait: (text: string) => Promise<string>;
|
|
40
|
+
locale: string;
|
|
41
|
+
renderVersion?: number;
|
|
42
|
+
/** Batch-translation progress; `null` while the queue is idle. */
|
|
43
|
+
translationProgress: TranslationProgress | null;
|
|
44
|
+
/** Chrome AI model download progress; `null` until a download begins. */
|
|
45
|
+
modelDownloadProgress: ModelDownloadProgress | null;
|
|
46
|
+
tool: string;
|
|
47
|
+
}
|
|
14
48
|
|
|
15
49
|
interface ConfigurationResponse {
|
|
16
50
|
[key: string]: ToolConfig;
|
|
@@ -63,6 +97,25 @@ interface UseConfigOptions {
|
|
|
63
97
|
*/
|
|
64
98
|
declare function useConfig(toolName: string, options?: UseConfigOptions): _tanstack_react_query.UseQueryResult<ConfigurationResponse | null, unknown>;
|
|
65
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Returns the full AI-translation context value provided by the nearest
|
|
102
|
+
* `AITranslationProvider`.
|
|
103
|
+
*
|
|
104
|
+
* Use this hook when you need more than one value from the context at once.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* function MyComponent() {
|
|
109
|
+
* const { ait, locale, translationProgress, modelDownloadProgress } = useAITranslation();
|
|
110
|
+
*
|
|
111
|
+
* return <span>{locale}</span>;
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* @throws if called outside of an `AITranslationProvider`.
|
|
116
|
+
*/
|
|
117
|
+
declare function useAITranslation(): AITranslationContextValue;
|
|
118
|
+
|
|
66
119
|
interface AITranslationProviderProps {
|
|
67
120
|
children: ReactNode;
|
|
68
121
|
locale: string;
|
|
@@ -123,4 +176,4 @@ declare global {
|
|
|
123
176
|
var _BACKEND_AI_TRANSLATION_IN_PROGRESS_: boolean;
|
|
124
177
|
}
|
|
125
178
|
|
|
126
|
-
export { AITranslateText, type AITranslateTextProps, AITranslationProvider, AI_TRANSLATION_FEATURE_FLAG_KEY, type TranslatedIconProps, type UseConfigOptions, getAITranslationLDId, useConfig };
|
|
179
|
+
export { AITranslateText, type AITranslateTextProps, AITranslationProvider, AI_TRANSLATION_FEATURE_FLAG_KEY, type TranslatedIconProps, type UseConfigOptions, getAITranslationLDId, useAITranslation, useConfig };
|
package/dist/legacy/index.js
CHANGED
|
@@ -26,6 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
AITranslationProvider: () => AITranslationProvider,
|
|
27
27
|
AI_TRANSLATION_FEATURE_FLAG_KEY: () => AI_TRANSLATION_FEATURE_FLAG_KEY,
|
|
28
28
|
getAITranslationLDId: () => getAITranslationLDId,
|
|
29
|
+
useAITranslation: () => useAITranslation,
|
|
29
30
|
useConfig: () => useConfig
|
|
30
31
|
});
|
|
31
32
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -212,6 +213,9 @@ function useConfig(toolName, options = {}) {
|
|
|
212
213
|
});
|
|
213
214
|
}
|
|
214
215
|
|
|
216
|
+
// src/hooks/useAITranslation.ts
|
|
217
|
+
var import_react2 = require("react");
|
|
218
|
+
|
|
215
219
|
// src/Provider.tsx
|
|
216
220
|
var import_react = require("react");
|
|
217
221
|
|
|
@@ -616,6 +620,7 @@ var import_web_sdk_events = require("@procore/web-sdk-events");
|
|
|
616
620
|
var TRANSLATION_COMPLETE_EVENT_NAME = "ai-translation-completed";
|
|
617
621
|
var RERENDER_EVENT_NAME = "ai-translations-component-rerender";
|
|
618
622
|
var TRANSLATION_PROGRESS_EVENT_NAME = "ai-translations-progress";
|
|
623
|
+
var MODEL_DOWNLOAD_PROGRESS_EVENT_NAME = "ai-translations-model-download-progress";
|
|
619
624
|
|
|
620
625
|
// src/utils/eventHandler.ts
|
|
621
626
|
var EventHandler = class {
|
|
@@ -682,6 +687,34 @@ var EventHandler = class {
|
|
|
682
687
|
}
|
|
683
688
|
);
|
|
684
689
|
}
|
|
690
|
+
/**
|
|
691
|
+
* Publishes a Chrome AI language-model download-progress event.
|
|
692
|
+
*
|
|
693
|
+
* Not tool-scoped — a Chrome language model is shared across the entire
|
|
694
|
+
* browser session, so all Provider instances receive it.
|
|
695
|
+
*/
|
|
696
|
+
publishModelDownloadProgressEvent(loaded, total, progress) {
|
|
697
|
+
this.aiTranslationEvents.publish(MODEL_DOWNLOAD_PROGRESS_EVENT_NAME, {
|
|
698
|
+
loaded,
|
|
699
|
+
total,
|
|
700
|
+
progress,
|
|
701
|
+
isComplete: loaded === total
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
subscribeToModelDownloadProgressEvent(callback) {
|
|
705
|
+
return this.aiTranslationEvents.subscribe(
|
|
706
|
+
MODEL_DOWNLOAD_PROGRESS_EVENT_NAME,
|
|
707
|
+
(detail) => {
|
|
708
|
+
var _a, _b, _c, _d;
|
|
709
|
+
return callback({
|
|
710
|
+
loaded: ((_a = detail.data) == null ? void 0 : _a.loaded) ?? 0,
|
|
711
|
+
total: ((_b = detail.data) == null ? void 0 : _b.total) ?? 0,
|
|
712
|
+
progress: ((_c = detail.data) == null ? void 0 : _c.progress) ?? 0,
|
|
713
|
+
isComplete: ((_d = detail.data) == null ? void 0 : _d.isComplete) ?? false
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
);
|
|
717
|
+
}
|
|
685
718
|
withEventName(eventName) {
|
|
686
719
|
return eventName + ":" + this.toolName;
|
|
687
720
|
}
|
|
@@ -974,6 +1007,13 @@ var ChromeLanguageDetector = class _ChromeLanguageDetector {
|
|
|
974
1007
|
};
|
|
975
1008
|
|
|
976
1009
|
// src/translators/frontend_translators/chrome/Translator.ts
|
|
1010
|
+
var _modelDownloadEventHandler = null;
|
|
1011
|
+
var getModelDownloadEventHandler = () => {
|
|
1012
|
+
if (!_modelDownloadEventHandler) {
|
|
1013
|
+
_modelDownloadEventHandler = new EventHandler("__chrome_model__");
|
|
1014
|
+
}
|
|
1015
|
+
return _modelDownloadEventHandler;
|
|
1016
|
+
};
|
|
977
1017
|
var _ChromeTranslator = class _ChromeTranslator {
|
|
978
1018
|
constructor(translator) {
|
|
979
1019
|
__publicField(this, "translator");
|
|
@@ -1013,7 +1053,12 @@ var _ChromeTranslator = class _ChromeTranslator {
|
|
|
1013
1053
|
"downloadprogress",
|
|
1014
1054
|
(e) => {
|
|
1015
1055
|
isDownloading = true;
|
|
1016
|
-
|
|
1056
|
+
const progress = e.total > 0 ? Math.round(e.loaded / e.total * 100) : 0;
|
|
1057
|
+
getModelDownloadEventHandler().publishModelDownloadProgressEvent(
|
|
1058
|
+
e.loaded,
|
|
1059
|
+
e.total,
|
|
1060
|
+
progress
|
|
1061
|
+
);
|
|
1017
1062
|
if (e.loaded === e.total) {
|
|
1018
1063
|
resolveReady();
|
|
1019
1064
|
}
|
|
@@ -1295,6 +1340,7 @@ function AITranslationInnerProvider(props) {
|
|
|
1295
1340
|
enableAIT = true
|
|
1296
1341
|
} = props;
|
|
1297
1342
|
const [translationProgress, setTranslationProgress] = (0, import_react.useState)(null);
|
|
1343
|
+
const [modelDownloadProgress, setModelDownloadProgress] = (0, import_react.useState)(null);
|
|
1298
1344
|
const [config2, setConfig] = (0, import_react.useState)(new Config());
|
|
1299
1345
|
const translator = (0, import_react.useRef)(new Translator2(config2));
|
|
1300
1346
|
const eventHandler = (0, import_react.useRef)(new EventHandler(tool));
|
|
@@ -1333,6 +1379,14 @@ function AITranslationInnerProvider(props) {
|
|
|
1333
1379
|
);
|
|
1334
1380
|
return () => unsubscribe();
|
|
1335
1381
|
}, []);
|
|
1382
|
+
(0, import_react.useEffect)(() => {
|
|
1383
|
+
const unsubscribe = eventHandler.current.subscribeToModelDownloadProgressEvent(
|
|
1384
|
+
(progress) => {
|
|
1385
|
+
setModelDownloadProgress(progress);
|
|
1386
|
+
}
|
|
1387
|
+
);
|
|
1388
|
+
return () => unsubscribe();
|
|
1389
|
+
}, []);
|
|
1336
1390
|
(0, import_react.useEffect)(() => {
|
|
1337
1391
|
if (isFetched && remoteConfig && remoteConfig[tool]) {
|
|
1338
1392
|
setConfig((prevConfig) => {
|
|
@@ -1357,6 +1411,7 @@ function AITranslationInnerProvider(props) {
|
|
|
1357
1411
|
locale,
|
|
1358
1412
|
renderVersion: renderVersionManager.getVersion(),
|
|
1359
1413
|
translationProgress,
|
|
1414
|
+
modelDownloadProgress,
|
|
1360
1415
|
tool
|
|
1361
1416
|
};
|
|
1362
1417
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AITranslationContext.Provider, { value: contextValue, children });
|
|
@@ -1366,11 +1421,22 @@ function AITranslationProvider(props) {
|
|
|
1366
1421
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_query2.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AITranslationInnerProvider, { ...props }) });
|
|
1367
1422
|
}
|
|
1368
1423
|
|
|
1424
|
+
// src/hooks/useAITranslation.ts
|
|
1425
|
+
function useAITranslation() {
|
|
1426
|
+
const ctx = (0, import_react2.useContext)(AITranslationContext);
|
|
1427
|
+
if (!ctx) {
|
|
1428
|
+
throw new Error(
|
|
1429
|
+
"useAITranslation must be used inside an AITranslationProvider"
|
|
1430
|
+
);
|
|
1431
|
+
}
|
|
1432
|
+
return ctx;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1369
1435
|
// src/components/AITranslateText.tsx
|
|
1370
|
-
var
|
|
1436
|
+
var import_react4 = require("react");
|
|
1371
1437
|
|
|
1372
1438
|
// src/components/TranslatedIcon.tsx
|
|
1373
|
-
var
|
|
1439
|
+
var import_react3 = require("react");
|
|
1374
1440
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1375
1441
|
var TranslatedIcon = ({
|
|
1376
1442
|
width = 14,
|
|
@@ -1406,11 +1472,11 @@ var AITranslateText = ({
|
|
|
1406
1472
|
showHighlight = false,
|
|
1407
1473
|
translatedIconProps
|
|
1408
1474
|
}) => {
|
|
1409
|
-
const context = (0,
|
|
1410
|
-
const [displayText, setDisplayText] = (0,
|
|
1411
|
-
const [showHighlightState, setShowHighlightState] = (0,
|
|
1412
|
-
const eventHandlerRef = (0,
|
|
1413
|
-
(0,
|
|
1475
|
+
const context = (0, import_react4.useContext)(AITranslationContext);
|
|
1476
|
+
const [displayText, setDisplayText] = (0, import_react4.useState)(text ?? "");
|
|
1477
|
+
const [showHighlightState, setShowHighlightState] = (0, import_react4.useState)(showHighlight);
|
|
1478
|
+
const eventHandlerRef = (0, import_react4.useRef)(null);
|
|
1479
|
+
(0, import_react4.useEffect)(() => {
|
|
1414
1480
|
if (!context) return;
|
|
1415
1481
|
if (!eventHandlerRef.current) {
|
|
1416
1482
|
eventHandlerRef.current = new EventHandler(context.tool);
|
|
@@ -1427,14 +1493,14 @@ var AITranslateText = ({
|
|
|
1427
1493
|
);
|
|
1428
1494
|
return () => unsubscribe();
|
|
1429
1495
|
}, [context, text]);
|
|
1430
|
-
const reset = (0,
|
|
1496
|
+
const reset = (0, import_react4.useCallback)(
|
|
1431
1497
|
(displayValue = text) => {
|
|
1432
1498
|
setDisplayText(displayValue);
|
|
1433
1499
|
setShowHighlightState(false);
|
|
1434
1500
|
},
|
|
1435
1501
|
[text]
|
|
1436
1502
|
);
|
|
1437
|
-
(0,
|
|
1503
|
+
(0, import_react4.useEffect)(() => {
|
|
1438
1504
|
if (text == null || text === "") {
|
|
1439
1505
|
reset(text ?? "");
|
|
1440
1506
|
return;
|
|
@@ -1484,5 +1550,6 @@ var getAITranslationLDId = (domain) => {
|
|
|
1484
1550
|
AITranslationProvider,
|
|
1485
1551
|
AI_TRANSLATION_FEATURE_FLAG_KEY,
|
|
1486
1552
|
getAITranslationLDId,
|
|
1553
|
+
useAITranslation,
|
|
1487
1554
|
useConfig
|
|
1488
1555
|
});
|
package/dist/legacy/index.mjs
CHANGED
|
@@ -184,6 +184,9 @@ function useConfig(toolName, options = {}) {
|
|
|
184
184
|
});
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
// src/hooks/useAITranslation.ts
|
|
188
|
+
import { useContext } from "react";
|
|
189
|
+
|
|
187
190
|
// src/Provider.tsx
|
|
188
191
|
import {
|
|
189
192
|
createContext,
|
|
@@ -594,6 +597,7 @@ import { SystemEvents } from "@procore/web-sdk-events";
|
|
|
594
597
|
var TRANSLATION_COMPLETE_EVENT_NAME = "ai-translation-completed";
|
|
595
598
|
var RERENDER_EVENT_NAME = "ai-translations-component-rerender";
|
|
596
599
|
var TRANSLATION_PROGRESS_EVENT_NAME = "ai-translations-progress";
|
|
600
|
+
var MODEL_DOWNLOAD_PROGRESS_EVENT_NAME = "ai-translations-model-download-progress";
|
|
597
601
|
|
|
598
602
|
// src/utils/eventHandler.ts
|
|
599
603
|
var EventHandler = class {
|
|
@@ -660,6 +664,34 @@ var EventHandler = class {
|
|
|
660
664
|
}
|
|
661
665
|
);
|
|
662
666
|
}
|
|
667
|
+
/**
|
|
668
|
+
* Publishes a Chrome AI language-model download-progress event.
|
|
669
|
+
*
|
|
670
|
+
* Not tool-scoped — a Chrome language model is shared across the entire
|
|
671
|
+
* browser session, so all Provider instances receive it.
|
|
672
|
+
*/
|
|
673
|
+
publishModelDownloadProgressEvent(loaded, total, progress) {
|
|
674
|
+
this.aiTranslationEvents.publish(MODEL_DOWNLOAD_PROGRESS_EVENT_NAME, {
|
|
675
|
+
loaded,
|
|
676
|
+
total,
|
|
677
|
+
progress,
|
|
678
|
+
isComplete: loaded === total
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
subscribeToModelDownloadProgressEvent(callback) {
|
|
682
|
+
return this.aiTranslationEvents.subscribe(
|
|
683
|
+
MODEL_DOWNLOAD_PROGRESS_EVENT_NAME,
|
|
684
|
+
(detail) => {
|
|
685
|
+
var _a, _b, _c, _d;
|
|
686
|
+
return callback({
|
|
687
|
+
loaded: ((_a = detail.data) == null ? void 0 : _a.loaded) ?? 0,
|
|
688
|
+
total: ((_b = detail.data) == null ? void 0 : _b.total) ?? 0,
|
|
689
|
+
progress: ((_c = detail.data) == null ? void 0 : _c.progress) ?? 0,
|
|
690
|
+
isComplete: ((_d = detail.data) == null ? void 0 : _d.isComplete) ?? false
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
);
|
|
694
|
+
}
|
|
663
695
|
withEventName(eventName) {
|
|
664
696
|
return eventName + ":" + this.toolName;
|
|
665
697
|
}
|
|
@@ -952,6 +984,13 @@ var ChromeLanguageDetector = class _ChromeLanguageDetector {
|
|
|
952
984
|
};
|
|
953
985
|
|
|
954
986
|
// src/translators/frontend_translators/chrome/Translator.ts
|
|
987
|
+
var _modelDownloadEventHandler = null;
|
|
988
|
+
var getModelDownloadEventHandler = () => {
|
|
989
|
+
if (!_modelDownloadEventHandler) {
|
|
990
|
+
_modelDownloadEventHandler = new EventHandler("__chrome_model__");
|
|
991
|
+
}
|
|
992
|
+
return _modelDownloadEventHandler;
|
|
993
|
+
};
|
|
955
994
|
var _ChromeTranslator = class _ChromeTranslator {
|
|
956
995
|
constructor(translator) {
|
|
957
996
|
__publicField(this, "translator");
|
|
@@ -991,7 +1030,12 @@ var _ChromeTranslator = class _ChromeTranslator {
|
|
|
991
1030
|
"downloadprogress",
|
|
992
1031
|
(e) => {
|
|
993
1032
|
isDownloading = true;
|
|
994
|
-
|
|
1033
|
+
const progress = e.total > 0 ? Math.round(e.loaded / e.total * 100) : 0;
|
|
1034
|
+
getModelDownloadEventHandler().publishModelDownloadProgressEvent(
|
|
1035
|
+
e.loaded,
|
|
1036
|
+
e.total,
|
|
1037
|
+
progress
|
|
1038
|
+
);
|
|
995
1039
|
if (e.loaded === e.total) {
|
|
996
1040
|
resolveReady();
|
|
997
1041
|
}
|
|
@@ -1273,6 +1317,7 @@ function AITranslationInnerProvider(props) {
|
|
|
1273
1317
|
enableAIT = true
|
|
1274
1318
|
} = props;
|
|
1275
1319
|
const [translationProgress, setTranslationProgress] = useState(null);
|
|
1320
|
+
const [modelDownloadProgress, setModelDownloadProgress] = useState(null);
|
|
1276
1321
|
const [config2, setConfig] = useState(new Config());
|
|
1277
1322
|
const translator = useRef(new Translator2(config2));
|
|
1278
1323
|
const eventHandler = useRef(new EventHandler(tool));
|
|
@@ -1311,6 +1356,14 @@ function AITranslationInnerProvider(props) {
|
|
|
1311
1356
|
);
|
|
1312
1357
|
return () => unsubscribe();
|
|
1313
1358
|
}, []);
|
|
1359
|
+
useEffect(() => {
|
|
1360
|
+
const unsubscribe = eventHandler.current.subscribeToModelDownloadProgressEvent(
|
|
1361
|
+
(progress) => {
|
|
1362
|
+
setModelDownloadProgress(progress);
|
|
1363
|
+
}
|
|
1364
|
+
);
|
|
1365
|
+
return () => unsubscribe();
|
|
1366
|
+
}, []);
|
|
1314
1367
|
useEffect(() => {
|
|
1315
1368
|
if (isFetched && remoteConfig && remoteConfig[tool]) {
|
|
1316
1369
|
setConfig((prevConfig) => {
|
|
@@ -1335,6 +1388,7 @@ function AITranslationInnerProvider(props) {
|
|
|
1335
1388
|
locale,
|
|
1336
1389
|
renderVersion: renderVersionManager.getVersion(),
|
|
1337
1390
|
translationProgress,
|
|
1391
|
+
modelDownloadProgress,
|
|
1338
1392
|
tool
|
|
1339
1393
|
};
|
|
1340
1394
|
return /* @__PURE__ */ jsx(AITranslationContext.Provider, { value: contextValue, children });
|
|
@@ -1344,11 +1398,22 @@ function AITranslationProvider(props) {
|
|
|
1344
1398
|
return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(AITranslationInnerProvider, { ...props }) });
|
|
1345
1399
|
}
|
|
1346
1400
|
|
|
1401
|
+
// src/hooks/useAITranslation.ts
|
|
1402
|
+
function useAITranslation() {
|
|
1403
|
+
const ctx = useContext(AITranslationContext);
|
|
1404
|
+
if (!ctx) {
|
|
1405
|
+
throw new Error(
|
|
1406
|
+
"useAITranslation must be used inside an AITranslationProvider"
|
|
1407
|
+
);
|
|
1408
|
+
}
|
|
1409
|
+
return ctx;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1347
1412
|
// src/components/AITranslateText.tsx
|
|
1348
1413
|
import {
|
|
1349
1414
|
useState as useState2,
|
|
1350
1415
|
useEffect as useEffect2,
|
|
1351
|
-
useContext,
|
|
1416
|
+
useContext as useContext2,
|
|
1352
1417
|
useCallback as useCallback2,
|
|
1353
1418
|
useRef as useRef2
|
|
1354
1419
|
} from "react";
|
|
@@ -1390,7 +1455,7 @@ var AITranslateText = ({
|
|
|
1390
1455
|
showHighlight = false,
|
|
1391
1456
|
translatedIconProps
|
|
1392
1457
|
}) => {
|
|
1393
|
-
const context =
|
|
1458
|
+
const context = useContext2(AITranslationContext);
|
|
1394
1459
|
const [displayText, setDisplayText] = useState2(text ?? "");
|
|
1395
1460
|
const [showHighlightState, setShowHighlightState] = useState2(showHighlight);
|
|
1396
1461
|
const eventHandlerRef = useRef2(null);
|
|
@@ -1467,5 +1532,6 @@ export {
|
|
|
1467
1532
|
AITranslationProvider,
|
|
1468
1533
|
AI_TRANSLATION_FEATURE_FLAG_KEY,
|
|
1469
1534
|
getAITranslationLDId,
|
|
1535
|
+
useAITranslation,
|
|
1470
1536
|
useConfig
|
|
1471
1537
|
};
|
package/dist/modern/index.d.mts
CHANGED
|
@@ -2,6 +2,20 @@ import * as _tanstack_react_query from '@tanstack/react-query';
|
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
3
|
import React, { ReactNode } from 'react';
|
|
4
4
|
|
|
5
|
+
interface TranslationProgress {
|
|
6
|
+
/**
|
|
7
|
+
* Progress percentage (0-100)
|
|
8
|
+
*/
|
|
9
|
+
progress: number;
|
|
10
|
+
/**
|
|
11
|
+
* Current number of strings translated
|
|
12
|
+
*/
|
|
13
|
+
current: number;
|
|
14
|
+
/**
|
|
15
|
+
* Total number of strings to translate
|
|
16
|
+
*/
|
|
17
|
+
total: number;
|
|
18
|
+
}
|
|
5
19
|
interface TranslationQueueEntry {
|
|
6
20
|
originalText: string;
|
|
7
21
|
isTranslated: boolean;
|
|
@@ -11,6 +25,26 @@ interface TranslationQueueEntry {
|
|
|
11
25
|
tool: string;
|
|
12
26
|
translationStrategy: 'frontend_translations' | 'backend_translations';
|
|
13
27
|
}
|
|
28
|
+
interface ModelDownloadProgress {
|
|
29
|
+
/** Bytes already downloaded */
|
|
30
|
+
loaded: number;
|
|
31
|
+
/** Total bytes to download */
|
|
32
|
+
total: number;
|
|
33
|
+
/** Download percentage (0-100) */
|
|
34
|
+
progress: number;
|
|
35
|
+
/** True once the model has finished downloading */
|
|
36
|
+
isComplete: boolean;
|
|
37
|
+
}
|
|
38
|
+
interface AITranslationContextValue {
|
|
39
|
+
ait: (text: string) => Promise<string>;
|
|
40
|
+
locale: string;
|
|
41
|
+
renderVersion?: number;
|
|
42
|
+
/** Batch-translation progress; `null` while the queue is idle. */
|
|
43
|
+
translationProgress: TranslationProgress | null;
|
|
44
|
+
/** Chrome AI model download progress; `null` until a download begins. */
|
|
45
|
+
modelDownloadProgress: ModelDownloadProgress | null;
|
|
46
|
+
tool: string;
|
|
47
|
+
}
|
|
14
48
|
|
|
15
49
|
interface ConfigurationResponse {
|
|
16
50
|
[key: string]: ToolConfig;
|
|
@@ -63,6 +97,25 @@ interface UseConfigOptions {
|
|
|
63
97
|
*/
|
|
64
98
|
declare function useConfig(toolName: string, options?: UseConfigOptions): _tanstack_react_query.UseQueryResult<ConfigurationResponse | null, unknown>;
|
|
65
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Returns the full AI-translation context value provided by the nearest
|
|
102
|
+
* `AITranslationProvider`.
|
|
103
|
+
*
|
|
104
|
+
* Use this hook when you need more than one value from the context at once.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* function MyComponent() {
|
|
109
|
+
* const { ait, locale, translationProgress, modelDownloadProgress } = useAITranslation();
|
|
110
|
+
*
|
|
111
|
+
* return <span>{locale}</span>;
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* @throws if called outside of an `AITranslationProvider`.
|
|
116
|
+
*/
|
|
117
|
+
declare function useAITranslation(): AITranslationContextValue;
|
|
118
|
+
|
|
66
119
|
interface AITranslationProviderProps {
|
|
67
120
|
children: ReactNode;
|
|
68
121
|
locale: string;
|
|
@@ -123,4 +176,4 @@ declare global {
|
|
|
123
176
|
var _BACKEND_AI_TRANSLATION_IN_PROGRESS_: boolean;
|
|
124
177
|
}
|
|
125
178
|
|
|
126
|
-
export { AITranslateText, type AITranslateTextProps, AITranslationProvider, AI_TRANSLATION_FEATURE_FLAG_KEY, type TranslatedIconProps, type UseConfigOptions, getAITranslationLDId, useConfig };
|
|
179
|
+
export { AITranslateText, type AITranslateTextProps, AITranslationProvider, AI_TRANSLATION_FEATURE_FLAG_KEY, type TranslatedIconProps, type UseConfigOptions, getAITranslationLDId, useAITranslation, useConfig };
|
package/dist/modern/index.d.ts
CHANGED
|
@@ -2,6 +2,20 @@ import * as _tanstack_react_query from '@tanstack/react-query';
|
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
3
|
import React, { ReactNode } from 'react';
|
|
4
4
|
|
|
5
|
+
interface TranslationProgress {
|
|
6
|
+
/**
|
|
7
|
+
* Progress percentage (0-100)
|
|
8
|
+
*/
|
|
9
|
+
progress: number;
|
|
10
|
+
/**
|
|
11
|
+
* Current number of strings translated
|
|
12
|
+
*/
|
|
13
|
+
current: number;
|
|
14
|
+
/**
|
|
15
|
+
* Total number of strings to translate
|
|
16
|
+
*/
|
|
17
|
+
total: number;
|
|
18
|
+
}
|
|
5
19
|
interface TranslationQueueEntry {
|
|
6
20
|
originalText: string;
|
|
7
21
|
isTranslated: boolean;
|
|
@@ -11,6 +25,26 @@ interface TranslationQueueEntry {
|
|
|
11
25
|
tool: string;
|
|
12
26
|
translationStrategy: 'frontend_translations' | 'backend_translations';
|
|
13
27
|
}
|
|
28
|
+
interface ModelDownloadProgress {
|
|
29
|
+
/** Bytes already downloaded */
|
|
30
|
+
loaded: number;
|
|
31
|
+
/** Total bytes to download */
|
|
32
|
+
total: number;
|
|
33
|
+
/** Download percentage (0-100) */
|
|
34
|
+
progress: number;
|
|
35
|
+
/** True once the model has finished downloading */
|
|
36
|
+
isComplete: boolean;
|
|
37
|
+
}
|
|
38
|
+
interface AITranslationContextValue {
|
|
39
|
+
ait: (text: string) => Promise<string>;
|
|
40
|
+
locale: string;
|
|
41
|
+
renderVersion?: number;
|
|
42
|
+
/** Batch-translation progress; `null` while the queue is idle. */
|
|
43
|
+
translationProgress: TranslationProgress | null;
|
|
44
|
+
/** Chrome AI model download progress; `null` until a download begins. */
|
|
45
|
+
modelDownloadProgress: ModelDownloadProgress | null;
|
|
46
|
+
tool: string;
|
|
47
|
+
}
|
|
14
48
|
|
|
15
49
|
interface ConfigurationResponse {
|
|
16
50
|
[key: string]: ToolConfig;
|
|
@@ -63,6 +97,25 @@ interface UseConfigOptions {
|
|
|
63
97
|
*/
|
|
64
98
|
declare function useConfig(toolName: string, options?: UseConfigOptions): _tanstack_react_query.UseQueryResult<ConfigurationResponse | null, unknown>;
|
|
65
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Returns the full AI-translation context value provided by the nearest
|
|
102
|
+
* `AITranslationProvider`.
|
|
103
|
+
*
|
|
104
|
+
* Use this hook when you need more than one value from the context at once.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* function MyComponent() {
|
|
109
|
+
* const { ait, locale, translationProgress, modelDownloadProgress } = useAITranslation();
|
|
110
|
+
*
|
|
111
|
+
* return <span>{locale}</span>;
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* @throws if called outside of an `AITranslationProvider`.
|
|
116
|
+
*/
|
|
117
|
+
declare function useAITranslation(): AITranslationContextValue;
|
|
118
|
+
|
|
66
119
|
interface AITranslationProviderProps {
|
|
67
120
|
children: ReactNode;
|
|
68
121
|
locale: string;
|
|
@@ -123,4 +176,4 @@ declare global {
|
|
|
123
176
|
var _BACKEND_AI_TRANSLATION_IN_PROGRESS_: boolean;
|
|
124
177
|
}
|
|
125
178
|
|
|
126
|
-
export { AITranslateText, type AITranslateTextProps, AITranslationProvider, AI_TRANSLATION_FEATURE_FLAG_KEY, type TranslatedIconProps, type UseConfigOptions, getAITranslationLDId, useConfig };
|
|
179
|
+
export { AITranslateText, type AITranslateTextProps, AITranslationProvider, AI_TRANSLATION_FEATURE_FLAG_KEY, type TranslatedIconProps, type UseConfigOptions, getAITranslationLDId, useAITranslation, useConfig };
|
package/dist/modern/index.js
CHANGED
|
@@ -24,6 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
AITranslationProvider: () => AITranslationProvider,
|
|
25
25
|
AI_TRANSLATION_FEATURE_FLAG_KEY: () => AI_TRANSLATION_FEATURE_FLAG_KEY,
|
|
26
26
|
getAITranslationLDId: () => getAITranslationLDId,
|
|
27
|
+
useAITranslation: () => useAITranslation,
|
|
27
28
|
useConfig: () => useConfig
|
|
28
29
|
});
|
|
29
30
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -208,6 +209,9 @@ function useConfig(toolName, options = {}) {
|
|
|
208
209
|
});
|
|
209
210
|
}
|
|
210
211
|
|
|
212
|
+
// src/hooks/useAITranslation.ts
|
|
213
|
+
var import_react2 = require("react");
|
|
214
|
+
|
|
211
215
|
// src/Provider.tsx
|
|
212
216
|
var import_react = require("react");
|
|
213
217
|
|
|
@@ -607,6 +611,7 @@ var import_web_sdk_events = require("@procore/web-sdk-events");
|
|
|
607
611
|
var TRANSLATION_COMPLETE_EVENT_NAME = "ai-translation-completed";
|
|
608
612
|
var RERENDER_EVENT_NAME = "ai-translations-component-rerender";
|
|
609
613
|
var TRANSLATION_PROGRESS_EVENT_NAME = "ai-translations-progress";
|
|
614
|
+
var MODEL_DOWNLOAD_PROGRESS_EVENT_NAME = "ai-translations-model-download-progress";
|
|
610
615
|
|
|
611
616
|
// src/utils/eventHandler.ts
|
|
612
617
|
var EventHandler = class {
|
|
@@ -666,6 +671,31 @@ var EventHandler = class {
|
|
|
666
671
|
)
|
|
667
672
|
);
|
|
668
673
|
}
|
|
674
|
+
/**
|
|
675
|
+
* Publishes a Chrome AI language-model download-progress event.
|
|
676
|
+
*
|
|
677
|
+
* Not tool-scoped — a Chrome language model is shared across the entire
|
|
678
|
+
* browser session, so all Provider instances receive it.
|
|
679
|
+
*/
|
|
680
|
+
publishModelDownloadProgressEvent(loaded, total, progress) {
|
|
681
|
+
this.aiTranslationEvents.publish(MODEL_DOWNLOAD_PROGRESS_EVENT_NAME, {
|
|
682
|
+
loaded,
|
|
683
|
+
total,
|
|
684
|
+
progress,
|
|
685
|
+
isComplete: loaded === total
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
subscribeToModelDownloadProgressEvent(callback) {
|
|
689
|
+
return this.aiTranslationEvents.subscribe(
|
|
690
|
+
MODEL_DOWNLOAD_PROGRESS_EVENT_NAME,
|
|
691
|
+
(detail) => callback({
|
|
692
|
+
loaded: detail.data?.loaded ?? 0,
|
|
693
|
+
total: detail.data?.total ?? 0,
|
|
694
|
+
progress: detail.data?.progress ?? 0,
|
|
695
|
+
isComplete: detail.data?.isComplete ?? false
|
|
696
|
+
})
|
|
697
|
+
);
|
|
698
|
+
}
|
|
669
699
|
withEventName(eventName) {
|
|
670
700
|
return eventName + ":" + this.toolName;
|
|
671
701
|
}
|
|
@@ -955,6 +985,13 @@ var ChromeLanguageDetector = class _ChromeLanguageDetector {
|
|
|
955
985
|
};
|
|
956
986
|
|
|
957
987
|
// src/translators/frontend_translators/chrome/Translator.ts
|
|
988
|
+
var _modelDownloadEventHandler = null;
|
|
989
|
+
var getModelDownloadEventHandler = () => {
|
|
990
|
+
if (!_modelDownloadEventHandler) {
|
|
991
|
+
_modelDownloadEventHandler = new EventHandler("__chrome_model__");
|
|
992
|
+
}
|
|
993
|
+
return _modelDownloadEventHandler;
|
|
994
|
+
};
|
|
958
995
|
var ChromeTranslator = class _ChromeTranslator {
|
|
959
996
|
translator;
|
|
960
997
|
constructor(translator) {
|
|
@@ -995,7 +1032,12 @@ var ChromeTranslator = class _ChromeTranslator {
|
|
|
995
1032
|
"downloadprogress",
|
|
996
1033
|
(e) => {
|
|
997
1034
|
isDownloading = true;
|
|
998
|
-
|
|
1035
|
+
const progress = e.total > 0 ? Math.round(e.loaded / e.total * 100) : 0;
|
|
1036
|
+
getModelDownloadEventHandler().publishModelDownloadProgressEvent(
|
|
1037
|
+
e.loaded,
|
|
1038
|
+
e.total,
|
|
1039
|
+
progress
|
|
1040
|
+
);
|
|
999
1041
|
if (e.loaded === e.total) {
|
|
1000
1042
|
resolveReady();
|
|
1001
1043
|
}
|
|
@@ -1274,6 +1316,7 @@ function AITranslationInnerProvider(props) {
|
|
|
1274
1316
|
enableAIT = true
|
|
1275
1317
|
} = props;
|
|
1276
1318
|
const [translationProgress, setTranslationProgress] = (0, import_react.useState)(null);
|
|
1319
|
+
const [modelDownloadProgress, setModelDownloadProgress] = (0, import_react.useState)(null);
|
|
1277
1320
|
const [config2, setConfig] = (0, import_react.useState)(new Config());
|
|
1278
1321
|
const translator = (0, import_react.useRef)(new Translator2(config2));
|
|
1279
1322
|
const eventHandler = (0, import_react.useRef)(new EventHandler(tool));
|
|
@@ -1312,6 +1355,14 @@ function AITranslationInnerProvider(props) {
|
|
|
1312
1355
|
);
|
|
1313
1356
|
return () => unsubscribe();
|
|
1314
1357
|
}, []);
|
|
1358
|
+
(0, import_react.useEffect)(() => {
|
|
1359
|
+
const unsubscribe = eventHandler.current.subscribeToModelDownloadProgressEvent(
|
|
1360
|
+
(progress) => {
|
|
1361
|
+
setModelDownloadProgress(progress);
|
|
1362
|
+
}
|
|
1363
|
+
);
|
|
1364
|
+
return () => unsubscribe();
|
|
1365
|
+
}, []);
|
|
1315
1366
|
(0, import_react.useEffect)(() => {
|
|
1316
1367
|
if (isFetched && remoteConfig && remoteConfig[tool]) {
|
|
1317
1368
|
setConfig((prevConfig) => {
|
|
@@ -1336,6 +1387,7 @@ function AITranslationInnerProvider(props) {
|
|
|
1336
1387
|
locale,
|
|
1337
1388
|
renderVersion: renderVersionManager.getVersion(),
|
|
1338
1389
|
translationProgress,
|
|
1390
|
+
modelDownloadProgress,
|
|
1339
1391
|
tool
|
|
1340
1392
|
};
|
|
1341
1393
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AITranslationContext.Provider, { value: contextValue, children });
|
|
@@ -1345,11 +1397,22 @@ function AITranslationProvider(props) {
|
|
|
1345
1397
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_query2.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AITranslationInnerProvider, { ...props }) });
|
|
1346
1398
|
}
|
|
1347
1399
|
|
|
1400
|
+
// src/hooks/useAITranslation.ts
|
|
1401
|
+
function useAITranslation() {
|
|
1402
|
+
const ctx = (0, import_react2.useContext)(AITranslationContext);
|
|
1403
|
+
if (!ctx) {
|
|
1404
|
+
throw new Error(
|
|
1405
|
+
"useAITranslation must be used inside an AITranslationProvider"
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1408
|
+
return ctx;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1348
1411
|
// src/components/AITranslateText.tsx
|
|
1349
|
-
var
|
|
1412
|
+
var import_react4 = require("react");
|
|
1350
1413
|
|
|
1351
1414
|
// src/components/TranslatedIcon.tsx
|
|
1352
|
-
var
|
|
1415
|
+
var import_react3 = require("react");
|
|
1353
1416
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1354
1417
|
var TranslatedIcon = ({
|
|
1355
1418
|
width = 14,
|
|
@@ -1385,11 +1448,11 @@ var AITranslateText = ({
|
|
|
1385
1448
|
showHighlight = false,
|
|
1386
1449
|
translatedIconProps
|
|
1387
1450
|
}) => {
|
|
1388
|
-
const context = (0,
|
|
1389
|
-
const [displayText, setDisplayText] = (0,
|
|
1390
|
-
const [showHighlightState, setShowHighlightState] = (0,
|
|
1391
|
-
const eventHandlerRef = (0,
|
|
1392
|
-
(0,
|
|
1451
|
+
const context = (0, import_react4.useContext)(AITranslationContext);
|
|
1452
|
+
const [displayText, setDisplayText] = (0, import_react4.useState)(text ?? "");
|
|
1453
|
+
const [showHighlightState, setShowHighlightState] = (0, import_react4.useState)(showHighlight);
|
|
1454
|
+
const eventHandlerRef = (0, import_react4.useRef)(null);
|
|
1455
|
+
(0, import_react4.useEffect)(() => {
|
|
1393
1456
|
if (!context) return;
|
|
1394
1457
|
if (!eventHandlerRef.current) {
|
|
1395
1458
|
eventHandlerRef.current = new EventHandler(context.tool);
|
|
@@ -1406,14 +1469,14 @@ var AITranslateText = ({
|
|
|
1406
1469
|
);
|
|
1407
1470
|
return () => unsubscribe();
|
|
1408
1471
|
}, [context, text]);
|
|
1409
|
-
const reset = (0,
|
|
1472
|
+
const reset = (0, import_react4.useCallback)(
|
|
1410
1473
|
(displayValue = text) => {
|
|
1411
1474
|
setDisplayText(displayValue);
|
|
1412
1475
|
setShowHighlightState(false);
|
|
1413
1476
|
},
|
|
1414
1477
|
[text]
|
|
1415
1478
|
);
|
|
1416
|
-
(0,
|
|
1479
|
+
(0, import_react4.useEffect)(() => {
|
|
1417
1480
|
if (text == null || text === "") {
|
|
1418
1481
|
reset(text ?? "");
|
|
1419
1482
|
return;
|
|
@@ -1463,5 +1526,6 @@ var getAITranslationLDId = (domain) => {
|
|
|
1463
1526
|
AITranslationProvider,
|
|
1464
1527
|
AI_TRANSLATION_FEATURE_FLAG_KEY,
|
|
1465
1528
|
getAITranslationLDId,
|
|
1529
|
+
useAITranslation,
|
|
1466
1530
|
useConfig
|
|
1467
1531
|
});
|
package/dist/modern/index.mjs
CHANGED
|
@@ -178,6 +178,9 @@ function useConfig(toolName, options = {}) {
|
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
// src/hooks/useAITranslation.ts
|
|
182
|
+
import { useContext } from "react";
|
|
183
|
+
|
|
181
184
|
// src/Provider.tsx
|
|
182
185
|
import {
|
|
183
186
|
createContext,
|
|
@@ -583,6 +586,7 @@ import { SystemEvents } from "@procore/web-sdk-events";
|
|
|
583
586
|
var TRANSLATION_COMPLETE_EVENT_NAME = "ai-translation-completed";
|
|
584
587
|
var RERENDER_EVENT_NAME = "ai-translations-component-rerender";
|
|
585
588
|
var TRANSLATION_PROGRESS_EVENT_NAME = "ai-translations-progress";
|
|
589
|
+
var MODEL_DOWNLOAD_PROGRESS_EVENT_NAME = "ai-translations-model-download-progress";
|
|
586
590
|
|
|
587
591
|
// src/utils/eventHandler.ts
|
|
588
592
|
var EventHandler = class {
|
|
@@ -642,6 +646,31 @@ var EventHandler = class {
|
|
|
642
646
|
)
|
|
643
647
|
);
|
|
644
648
|
}
|
|
649
|
+
/**
|
|
650
|
+
* Publishes a Chrome AI language-model download-progress event.
|
|
651
|
+
*
|
|
652
|
+
* Not tool-scoped — a Chrome language model is shared across the entire
|
|
653
|
+
* browser session, so all Provider instances receive it.
|
|
654
|
+
*/
|
|
655
|
+
publishModelDownloadProgressEvent(loaded, total, progress) {
|
|
656
|
+
this.aiTranslationEvents.publish(MODEL_DOWNLOAD_PROGRESS_EVENT_NAME, {
|
|
657
|
+
loaded,
|
|
658
|
+
total,
|
|
659
|
+
progress,
|
|
660
|
+
isComplete: loaded === total
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
subscribeToModelDownloadProgressEvent(callback) {
|
|
664
|
+
return this.aiTranslationEvents.subscribe(
|
|
665
|
+
MODEL_DOWNLOAD_PROGRESS_EVENT_NAME,
|
|
666
|
+
(detail) => callback({
|
|
667
|
+
loaded: detail.data?.loaded ?? 0,
|
|
668
|
+
total: detail.data?.total ?? 0,
|
|
669
|
+
progress: detail.data?.progress ?? 0,
|
|
670
|
+
isComplete: detail.data?.isComplete ?? false
|
|
671
|
+
})
|
|
672
|
+
);
|
|
673
|
+
}
|
|
645
674
|
withEventName(eventName) {
|
|
646
675
|
return eventName + ":" + this.toolName;
|
|
647
676
|
}
|
|
@@ -931,6 +960,13 @@ var ChromeLanguageDetector = class _ChromeLanguageDetector {
|
|
|
931
960
|
};
|
|
932
961
|
|
|
933
962
|
// src/translators/frontend_translators/chrome/Translator.ts
|
|
963
|
+
var _modelDownloadEventHandler = null;
|
|
964
|
+
var getModelDownloadEventHandler = () => {
|
|
965
|
+
if (!_modelDownloadEventHandler) {
|
|
966
|
+
_modelDownloadEventHandler = new EventHandler("__chrome_model__");
|
|
967
|
+
}
|
|
968
|
+
return _modelDownloadEventHandler;
|
|
969
|
+
};
|
|
934
970
|
var ChromeTranslator = class _ChromeTranslator {
|
|
935
971
|
translator;
|
|
936
972
|
constructor(translator) {
|
|
@@ -971,7 +1007,12 @@ var ChromeTranslator = class _ChromeTranslator {
|
|
|
971
1007
|
"downloadprogress",
|
|
972
1008
|
(e) => {
|
|
973
1009
|
isDownloading = true;
|
|
974
|
-
|
|
1010
|
+
const progress = e.total > 0 ? Math.round(e.loaded / e.total * 100) : 0;
|
|
1011
|
+
getModelDownloadEventHandler().publishModelDownloadProgressEvent(
|
|
1012
|
+
e.loaded,
|
|
1013
|
+
e.total,
|
|
1014
|
+
progress
|
|
1015
|
+
);
|
|
975
1016
|
if (e.loaded === e.total) {
|
|
976
1017
|
resolveReady();
|
|
977
1018
|
}
|
|
@@ -1250,6 +1291,7 @@ function AITranslationInnerProvider(props) {
|
|
|
1250
1291
|
enableAIT = true
|
|
1251
1292
|
} = props;
|
|
1252
1293
|
const [translationProgress, setTranslationProgress] = useState(null);
|
|
1294
|
+
const [modelDownloadProgress, setModelDownloadProgress] = useState(null);
|
|
1253
1295
|
const [config2, setConfig] = useState(new Config());
|
|
1254
1296
|
const translator = useRef(new Translator2(config2));
|
|
1255
1297
|
const eventHandler = useRef(new EventHandler(tool));
|
|
@@ -1288,6 +1330,14 @@ function AITranslationInnerProvider(props) {
|
|
|
1288
1330
|
);
|
|
1289
1331
|
return () => unsubscribe();
|
|
1290
1332
|
}, []);
|
|
1333
|
+
useEffect(() => {
|
|
1334
|
+
const unsubscribe = eventHandler.current.subscribeToModelDownloadProgressEvent(
|
|
1335
|
+
(progress) => {
|
|
1336
|
+
setModelDownloadProgress(progress);
|
|
1337
|
+
}
|
|
1338
|
+
);
|
|
1339
|
+
return () => unsubscribe();
|
|
1340
|
+
}, []);
|
|
1291
1341
|
useEffect(() => {
|
|
1292
1342
|
if (isFetched && remoteConfig && remoteConfig[tool]) {
|
|
1293
1343
|
setConfig((prevConfig) => {
|
|
@@ -1312,6 +1362,7 @@ function AITranslationInnerProvider(props) {
|
|
|
1312
1362
|
locale,
|
|
1313
1363
|
renderVersion: renderVersionManager.getVersion(),
|
|
1314
1364
|
translationProgress,
|
|
1365
|
+
modelDownloadProgress,
|
|
1315
1366
|
tool
|
|
1316
1367
|
};
|
|
1317
1368
|
return /* @__PURE__ */ jsx(AITranslationContext.Provider, { value: contextValue, children });
|
|
@@ -1321,11 +1372,22 @@ function AITranslationProvider(props) {
|
|
|
1321
1372
|
return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(AITranslationInnerProvider, { ...props }) });
|
|
1322
1373
|
}
|
|
1323
1374
|
|
|
1375
|
+
// src/hooks/useAITranslation.ts
|
|
1376
|
+
function useAITranslation() {
|
|
1377
|
+
const ctx = useContext(AITranslationContext);
|
|
1378
|
+
if (!ctx) {
|
|
1379
|
+
throw new Error(
|
|
1380
|
+
"useAITranslation must be used inside an AITranslationProvider"
|
|
1381
|
+
);
|
|
1382
|
+
}
|
|
1383
|
+
return ctx;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1324
1386
|
// src/components/AITranslateText.tsx
|
|
1325
1387
|
import {
|
|
1326
1388
|
useState as useState2,
|
|
1327
1389
|
useEffect as useEffect2,
|
|
1328
|
-
useContext,
|
|
1390
|
+
useContext as useContext2,
|
|
1329
1391
|
useCallback as useCallback2,
|
|
1330
1392
|
useRef as useRef2
|
|
1331
1393
|
} from "react";
|
|
@@ -1367,7 +1429,7 @@ var AITranslateText = ({
|
|
|
1367
1429
|
showHighlight = false,
|
|
1368
1430
|
translatedIconProps
|
|
1369
1431
|
}) => {
|
|
1370
|
-
const context =
|
|
1432
|
+
const context = useContext2(AITranslationContext);
|
|
1371
1433
|
const [displayText, setDisplayText] = useState2(text ?? "");
|
|
1372
1434
|
const [showHighlightState, setShowHighlightState] = useState2(showHighlight);
|
|
1373
1435
|
const eventHandlerRef = useRef2(null);
|
|
@@ -1444,5 +1506,6 @@ export {
|
|
|
1444
1506
|
AITranslationProvider,
|
|
1445
1507
|
AI_TRANSLATION_FEATURE_FLAG_KEY,
|
|
1446
1508
|
getAITranslationLDId,
|
|
1509
|
+
useAITranslation,
|
|
1447
1510
|
useConfig
|
|
1448
1511
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@procore/ai-translations",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Library that provides a solution to use AI to translate text into a language",
|
|
5
5
|
"main": "dist/legacy/index.js",
|
|
6
6
|
"types": "dist/legacy/index.d.ts",
|
|
@@ -49,6 +49,8 @@
|
|
|
49
49
|
"cypress:run:component": "cypress run --component --browser chrome ",
|
|
50
50
|
"cypress:run:headed": "cypress run --headed --browser chrome",
|
|
51
51
|
"test:cypress": "yarn cypress:run",
|
|
52
|
+
"test:integration": "hammer test src/__integration__",
|
|
53
|
+
"cypress:run:integration": "cypress run --component --browser chrome --spec 'cypress/component/integration/**'",
|
|
52
54
|
"storybook": "storybook dev -p 6006",
|
|
53
55
|
"build-storybook": "storybook build"
|
|
54
56
|
},
|
|
@@ -87,6 +89,7 @@
|
|
|
87
89
|
"@storybook/addon-essentials": "^7.6.20",
|
|
88
90
|
"@storybook/addon-a11y": "^7.6.20",
|
|
89
91
|
"@procore/storybook-addon": "^4.5.1",
|
|
90
|
-
"eslint-plugin-storybook": "^0.11.1"
|
|
92
|
+
"eslint-plugin-storybook": "^0.11.1",
|
|
93
|
+
"fake-indexeddb": "^6.0.0"
|
|
91
94
|
}
|
|
92
95
|
}
|