@jingyi0605/codingns4dsh 0.2.1-beta.10 → 0.2.1-beta.11

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.
Files changed (145) hide show
  1. package/README.en.md +3 -3
  2. package/README.md +6 -4
  3. package/data/build/dist/client/bundle.js +8958 -7380
  4. package/data/build/dist/client/bundle.js.map +1 -1
  5. package/data/build/dist/client/cli-slots.d.ts +11 -2
  6. package/data/build/dist/client/cli-slots.d.ts.map +1 -1
  7. package/data/build/dist/client/cli-slots.js +67 -16
  8. package/data/build/dist/client/cli-slots.js.map +1 -1
  9. package/data/build/dist/client/client.assistant-configuration-view.js +8 -7
  10. package/data/build/dist/client/client.assistant-configuration-view.js.map +1 -1
  11. package/data/build/dist/client/client.moss-voice-output.js +581 -0
  12. package/data/build/dist/client/client.moss-voice-output.js.map +1 -0
  13. package/data/build/dist/client/client.native-workspace-store.js +1 -1
  14. package/data/build/dist/client/client.native-workspace-store.js.map +1 -1
  15. package/data/build/dist/client/client.primitives-adapter.js.map +1 -1
  16. package/data/build/dist/client/client.react-dom.js +0 -570
  17. package/data/build/dist/client/client.react-dom.js.map +1 -1
  18. package/data/build/dist/client/client.session-translation.js +97 -2
  19. package/data/build/dist/client/client.session-translation.js.map +1 -1
  20. package/data/build/dist/client/client.transport.js +57 -11
  21. package/data/build/dist/client/client.transport.js.map +1 -1
  22. package/data/build/dist/client/desktop-assistant.js +57 -11
  23. package/data/build/dist/client/features/cli-adapters.d.ts.map +1 -1
  24. package/data/build/dist/client/features/cli-adapters.js +3 -1
  25. package/data/build/dist/client/features/cli-adapters.js.map +1 -1
  26. package/data/build/dist/client/features/session-translation.d.ts.map +1 -1
  27. package/data/build/dist/client/features/session-translation.js +43 -48
  28. package/data/build/dist/client/features/session-translation.js.map +1 -1
  29. package/data/build/dist/client/locales/panels.d.ts.map +1 -1
  30. package/data/build/dist/client/locales/panels.js +57 -11
  31. package/data/build/dist/client/locales/panels.js.map +1 -1
  32. package/data/build/dist/client/native-model-translation-menu.d.ts +6 -0
  33. package/data/build/dist/client/native-model-translation-menu.d.ts.map +1 -0
  34. package/data/build/dist/client/native-model-translation-menu.js +57 -0
  35. package/data/build/dist/client/native-model-translation-menu.js.map +1 -0
  36. package/data/build/dist/client/native-translation-default.d.ts +6 -0
  37. package/data/build/dist/client/native-translation-default.d.ts.map +1 -0
  38. package/data/build/dist/client/native-translation-default.js +30 -0
  39. package/data/build/dist/client/native-translation-default.js.map +1 -0
  40. package/data/build/dist/client/session-translation-chat.d.ts +15 -0
  41. package/data/build/dist/client/session-translation-chat.d.ts.map +1 -1
  42. package/data/build/dist/client/session-translation-chat.js +109 -41
  43. package/data/build/dist/client/session-translation-chat.js.map +1 -1
  44. package/data/build/dist/client/session-translation-interactions.d.ts +31 -0
  45. package/data/build/dist/client/session-translation-interactions.d.ts.map +1 -0
  46. package/data/build/dist/client/session-translation-interactions.js +158 -0
  47. package/data/build/dist/client/session-translation-interactions.js.map +1 -0
  48. package/data/build/dist/client/session-translation-model-pool.d.ts +16 -0
  49. package/data/build/dist/client/session-translation-model-pool.d.ts.map +1 -0
  50. package/data/build/dist/client/session-translation-model-pool.js +246 -0
  51. package/data/build/dist/client/session-translation-model-pool.js.map +1 -0
  52. package/data/build/dist/client/session-translation-pending-dom.d.ts +8 -0
  53. package/data/build/dist/client/session-translation-pending-dom.d.ts.map +1 -0
  54. package/data/build/dist/client/session-translation-pending-dom.js +58 -0
  55. package/data/build/dist/client/session-translation-pending-dom.js.map +1 -0
  56. package/data/build/dist/dsh-capabilities/client/interaction-presentation-adapter.d.ts +30 -0
  57. package/data/build/dist/dsh-capabilities/client/interaction-presentation-adapter.d.ts.map +1 -0
  58. package/data/build/dist/dsh-capabilities/client/interaction-presentation-adapter.js +102 -0
  59. package/data/build/dist/dsh-capabilities/client/interaction-presentation-adapter.js.map +1 -0
  60. package/data/build/dist/dsh-capabilities/client/interaction-text-dom.d.ts +12 -0
  61. package/data/build/dist/dsh-capabilities/client/interaction-text-dom.d.ts.map +1 -0
  62. package/data/build/dist/dsh-capabilities/client/interaction-text-dom.js +181 -0
  63. package/data/build/dist/dsh-capabilities/client/interaction-text-dom.js.map +1 -0
  64. package/data/build/dist/dsh-capabilities/client/primitives-adapter.d.ts +1 -0
  65. package/data/build/dist/dsh-capabilities/client/primitives-adapter.d.ts.map +1 -1
  66. package/data/build/dist/dsh-capabilities/client/primitives-adapter.js +1 -0
  67. package/data/build/dist/dsh-capabilities/client/primitives-adapter.js.map +1 -1
  68. package/data/build/dist/dsh-capabilities/client/question-history-adapter.d.ts +20 -0
  69. package/data/build/dist/dsh-capabilities/client/question-history-adapter.d.ts.map +1 -0
  70. package/data/build/dist/dsh-capabilities/client/question-history-adapter.js +65 -0
  71. package/data/build/dist/dsh-capabilities/client/question-history-adapter.js.map +1 -0
  72. package/data/build/dist/dsh-capabilities/client/slot-presentation-mirror.d.ts +30 -0
  73. package/data/build/dist/dsh-capabilities/client/slot-presentation-mirror.d.ts.map +1 -0
  74. package/data/build/dist/dsh-capabilities/client/slot-presentation-mirror.js +62 -0
  75. package/data/build/dist/dsh-capabilities/client/slot-presentation-mirror.js.map +1 -0
  76. package/data/build/dist/dsh-capabilities/matrix.d.ts.map +1 -1
  77. package/data/build/dist/dsh-capabilities/matrix.js +1 -0
  78. package/data/build/dist/dsh-capabilities/matrix.js.map +1 -1
  79. package/data/build/dist/dsh-capabilities/routes.d.ts.map +1 -1
  80. package/data/build/dist/dsh-capabilities/routes.js +3 -0
  81. package/data/build/dist/dsh-capabilities/routes.js.map +1 -1
  82. package/data/build/dist/dsh-capabilities/types.d.ts +1 -1
  83. package/data/build/dist/dsh-capabilities/types.d.ts.map +1 -1
  84. package/data/build/dist/dsh-capabilities/types.js.map +1 -1
  85. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts.map +1 -1
  86. package/data/build/dist/host/cli-adapters/dsh-message-projector.js +17 -8
  87. package/data/build/dist/host/cli-adapters/dsh-message-projector.js.map +1 -1
  88. package/data/build/dist/host/cli-adapters/feature.d.ts.map +1 -1
  89. package/data/build/dist/host/cli-adapters/feature.js +252 -39
  90. package/data/build/dist/host/cli-adapters/feature.js.map +1 -1
  91. package/data/build/dist/host/cli-adapters/native-subagent-dispatch.js +2 -2
  92. package/data/build/dist/host/cli-adapters/native-subagent-dispatch.js.map +1 -1
  93. package/data/build/dist/host/cli-adapters/registry.d.ts.map +1 -1
  94. package/data/build/dist/host/cli-adapters/registry.js +2 -0
  95. package/data/build/dist/host/cli-adapters/registry.js.map +1 -1
  96. package/data/build/dist/host/features/interaction-translation.d.ts +9 -0
  97. package/data/build/dist/host/features/interaction-translation.d.ts.map +1 -0
  98. package/data/build/dist/host/features/interaction-translation.js +168 -0
  99. package/data/build/dist/host/features/interaction-translation.js.map +1 -0
  100. package/data/build/dist/host/features/session-translation-protection.d.ts +11 -0
  101. package/data/build/dist/host/features/session-translation-protection.d.ts.map +1 -0
  102. package/data/build/dist/host/features/session-translation-protection.js +165 -0
  103. package/data/build/dist/host/features/session-translation-protection.js.map +1 -0
  104. package/data/build/dist/host/features/session-translation-runtime.d.ts +16 -0
  105. package/data/build/dist/host/features/session-translation-runtime.d.ts.map +1 -0
  106. package/data/build/dist/host/features/session-translation-runtime.js +85 -0
  107. package/data/build/dist/host/features/session-translation-runtime.js.map +1 -0
  108. package/data/build/dist/host/features/session-translation-service.d.ts +10 -3
  109. package/data/build/dist/host/features/session-translation-service.d.ts.map +1 -1
  110. package/data/build/dist/host/features/session-translation-service.js +159 -197
  111. package/data/build/dist/host/features/session-translation-service.js.map +1 -1
  112. package/data/build/dist/host/features/session-translation.js +1 -1
  113. package/data/build/dist/host/features/session-translation.js.map +1 -1
  114. package/data/build/dist/host/native-session-bridge.js +2 -2
  115. package/data/build/dist/host/native-session-bridge.js.map +1 -1
  116. package/data/build/dist/host/settings.d.ts.map +1 -1
  117. package/data/build/dist/host/settings.js +6 -2
  118. package/data/build/dist/host/settings.js.map +1 -1
  119. package/data/build/dist/shared/contracts/interaction-presentation.d.ts +32 -0
  120. package/data/build/dist/shared/contracts/interaction-presentation.d.ts.map +1 -0
  121. package/data/build/dist/shared/contracts/interaction-presentation.js +2 -0
  122. package/data/build/dist/shared/contracts/interaction-presentation.js.map +1 -0
  123. package/data/build/dist/shared/contracts/interaction-translation.d.ts +53 -0
  124. package/data/build/dist/shared/contracts/interaction-translation.d.ts.map +1 -0
  125. package/data/build/dist/shared/contracts/interaction-translation.js +44 -0
  126. package/data/build/dist/shared/contracts/interaction-translation.js.map +1 -0
  127. package/data/build/dist/shared/contracts/session-translation.d.ts +62 -7
  128. package/data/build/dist/shared/contracts/session-translation.d.ts.map +1 -1
  129. package/data/build/dist/shared/contracts/session-translation.js +122 -2
  130. package/data/build/dist/shared/contracts/session-translation.js.map +1 -1
  131. package/data/build/dist/shared/contracts/version.d.ts +1 -1
  132. package/data/build/dist/shared/contracts/version.js +1 -1
  133. package/data/build/dist/shared/index.d.ts +1 -1
  134. package/data/build/dist/shared/index.d.ts.map +1 -1
  135. package/data/build/dist/shared/index.js +1 -1
  136. package/data/build/dist/shared/index.js.map +1 -1
  137. package/data/build/dist/shared/translation-indicator.d.ts +26 -0
  138. package/data/build/dist/shared/translation-indicator.d.ts.map +1 -0
  139. package/data/build/dist/shared/translation-indicator.js +29 -0
  140. package/data/build/dist/shared/translation-indicator.js.map +1 -0
  141. package/package.json +1 -1
  142. package/profile/README.md +2 -2
  143. package/profile/package.json +2 -2
  144. package/profile/version.json +1 -1
  145. package/version.json +1 -1
@@ -0,0 +1,6 @@
1
+ /**
2
+ * DSH 模型菜单尚无扩展插槽,通过原生触发器的 aria-controls 定位当前会话菜单。
3
+ * 只添加翻译行的 Portal 容器,原生模型选择、思考强度和菜单交互继续由 DSH 管理。
4
+ */
5
+ export declare function watchNativeModelMenu(anchor: HTMLElement, onTarget: (target: HTMLElement | null) => void): () => void;
6
+ //# sourceMappingURL=native-model-translation-menu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-model-translation-menu.d.ts","sourceRoot":"","sources":["../../../../src/client/native-model-translation-menu.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI,CA+CpH"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * DSH 模型菜单尚无扩展插槽,通过原生触发器的 aria-controls 定位当前会话菜单。
3
+ * 只添加翻译行的 Portal 容器,原生模型选择、思考强度和菜单交互继续由 DSH 管理。
4
+ */
5
+ export function watchNativeModelMenu(anchor, onTarget) {
6
+ const document = anchor.ownerDocument;
7
+ const view = document.defaultView;
8
+ const Observer = view?.MutationObserver;
9
+ if (Observer === undefined || document.body === null)
10
+ return () => undefined;
11
+ let target = null;
12
+ let trigger = null;
13
+ const resize = () => {
14
+ if (view !== null)
15
+ view.dispatchEvent(new view.Event('resize'));
16
+ };
17
+ const sizeObserver = view?.ResizeObserver === undefined ? undefined : new view.ResizeObserver(resize);
18
+ const closeOnEscape = (event) => {
19
+ if (event.key !== 'Escape')
20
+ return;
21
+ event.preventDefault();
22
+ event.stopPropagation();
23
+ trigger?.click();
24
+ };
25
+ const scan = () => {
26
+ trigger = anchor.closest('[data-composer-card]')?.querySelector('[data-slot="conversation.input.model"] button[aria-haspopup="menu"]') ?? null;
27
+ const menuId = trigger?.getAttribute('aria-controls');
28
+ const menu = menuId === undefined || menuId === null ? null : document.getElementById(menuId);
29
+ // 模型列表和思考档位子菜单不放翻译行,避免干扰原生键盘导航与搜索。
30
+ const rootMenu = trigger?.getAttribute('aria-expanded') === 'true'
31
+ && menu?.getAttribute('role') === 'menu'
32
+ && menu.querySelector(':scope > [role="menuitem"]') !== null
33
+ ? menu : null;
34
+ if (target !== null && target.parentElement === rootMenu && rootMenu !== null)
35
+ return;
36
+ sizeObserver?.disconnect();
37
+ target?.remove();
38
+ target = rootMenu === null ? null : document.createElement('div');
39
+ if (target !== null) {
40
+ target.dataset.codingnsNativeTranslation = '';
41
+ target.addEventListener('keydown', closeOnEscape);
42
+ rootMenu.prepend(target);
43
+ sizeObserver?.observe(target);
44
+ }
45
+ onTarget(target);
46
+ };
47
+ const observer = new Observer(scan);
48
+ observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['aria-controls', 'aria-expanded', 'role'] });
49
+ scan();
50
+ return () => {
51
+ observer.disconnect();
52
+ sizeObserver?.disconnect();
53
+ target?.remove();
54
+ resize();
55
+ };
56
+ }
57
+ //# sourceMappingURL=native-model-translation-menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-model-translation-menu.js","sourceRoot":"","sources":["../../../../src/client/native-model-translation-menu.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare function setNativeTranslationDefaultReader(next: (() => boolean) | undefined): void;
2
+ /** undefined 表示功能模块未启用,不应向原生模型菜单添加开关。 */
3
+ export declare function readNativeTranslationDefault(): boolean | undefined;
4
+ export declare function subscribeNativeTranslationDefault(listener: () => void): () => void;
5
+ export declare function notifyNativeTranslationDefaultChanged(): void;
6
+ //# sourceMappingURL=native-translation-default.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-translation-default.d.ts","sourceRoot":"","sources":["../../../../src/client/native-translation-default.ts"],"names":[],"mappings":"AASA,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,CAGzF;AAED,yCAAyC;AACzC,wBAAgB,4BAA4B,IAAI,OAAO,GAAG,SAAS,CAMlE;AAED,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAGlF;AAED,wBAAgB,qCAAqC,IAAI,IAAI,CAE5D"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * DSH 原生会话翻译开关的默认值读取器。
3
+ *
4
+ * 会话翻译功能模块在启动时注册读取器,模型菜单订阅变化;两个功能模块
5
+ * (`cliAdapters` 注册槽位、`sessionTranslation` 决定默认值)不互相引用。
6
+ */
7
+ let reader;
8
+ const listeners = new Set();
9
+ export function setNativeTranslationDefaultReader(next) {
10
+ reader = next;
11
+ notifyNativeTranslationDefaultChanged();
12
+ }
13
+ /** undefined 表示功能模块未启用,不应向原生模型菜单添加开关。 */
14
+ export function readNativeTranslationDefault() {
15
+ try {
16
+ return reader === undefined ? undefined : reader() === true;
17
+ }
18
+ catch {
19
+ return undefined;
20
+ }
21
+ }
22
+ export function subscribeNativeTranslationDefault(listener) {
23
+ listeners.add(listener);
24
+ return () => { listeners.delete(listener); };
25
+ }
26
+ export function notifyNativeTranslationDefaultChanged() {
27
+ for (const listener of listeners)
28
+ listener();
29
+ }
30
+ //# sourceMappingURL=native-translation-default.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-translation-default.js","sourceRoot":"","sources":["../../../../src/client/native-translation-default.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,IAAI,MAAmC,CAAA;AACvC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAA;AAEvC,MAAM,UAAU,iCAAiC,CAAC,IAAiC;IACjF,MAAM,GAAG,IAAI,CAAA;IACb,qCAAqC,EAAE,CAAA;AACzC,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,4BAA4B;IAC1C,IAAI,CAAC;QACH,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,IAAI,CAAA;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iCAAiC,CAAC,QAAoB;IACpE,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACvB,OAAO,GAAG,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA,CAAC,CAAC,CAAA;AAC7C,CAAC;AAED,MAAM,UAAU,qCAAqC;IACnD,KAAK,MAAM,QAAQ,IAAI,SAAS;QAAE,QAAQ,EAAE,CAAA;AAC9C,CAAC"}
@@ -3,6 +3,17 @@ import type { MarkdownLabels } from '@deepseek-ai/dsh-client-ui-primitives';
3
3
  import type { CodingNsClientServices } from './features/types.js';
4
4
  import type { CodingNsTranslator } from './locale.js';
5
5
  import type { SessionTranslationRecord } from '../shared/contracts/session-translation.js';
6
+ interface TranslationActionProps {
7
+ readonly messageId: string;
8
+ readonly messageRole?: 'user' | 'assistant';
9
+ readonly services: CodingNsClientServices;
10
+ readonly translationT: CodingNsTranslator;
11
+ /** Absent only for a pending submission in a translation-enabled session. */
12
+ readonly record?: SessionTranslationRecord;
13
+ readonly original?: string;
14
+ readonly popupAlign?: 'start' | 'end';
15
+ readonly inline?: boolean;
16
+ }
6
17
  interface MarkdownTranslationLabels extends MarkdownLabels {
7
18
  readonly code: MarkdownLabels['code'] & {
8
19
  readonly toolbarLabels: NonNullable<MarkdownLabels['code']['toolbarLabels']>;
@@ -15,6 +26,10 @@ interface MarkdownTranslationLabels extends MarkdownLabels {
15
26
  type TranslationFactorySlot = (name: string, props: Record<string, unknown>, options?: {
16
27
  readonly fallback?: ReactNode;
17
28
  }) => ReactNode;
29
+ /**
30
+ * 把翻译状态和查看原文的入口放回对应消息块,不占用 AI 回复底部的统一操作栏。
31
+ */
32
+ export declare function TranslationAction(props: TranslationActionProps): ReactElement | null;
18
33
  interface AssistantReasoningContentProps {
19
34
  readonly messageId: string;
20
35
  readonly services: CodingNsClientServices;
@@ -1 +1 @@
1
- {"version":3,"file":"session-translation-chat.d.ts","sourceRoot":"","sources":["../../../../src/client/session-translation-chat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAiBnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AAI3E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AACjE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,KAAK,EAAoH,wBAAwB,EAAE,MAAM,4CAA4C,CAAA;AAyB5M,UAAU,yBAA0B,SAAQ,cAAc;IACxD,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG;QACtC,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAA;KAC7E,CAAA;CACF;AAED;;;GAGG;AACH,KAAK,sBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,KAAK,SAAS,CAAA;AAqiBtI,UAAU,8BAA8B;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAA;IACzC,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAA;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,8BAA8B;IAC9B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,wBAAwB,CAAA;IACrD,QAAQ,CAAC,cAAc,EAAE,yBAAyB,CAAA;IAClD,QAAQ,CAAC,UAAU,EAAE;QAAE,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,CAAA;IACnE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAC3B,6CAA6C;IAC7C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,sBAAsB,CAAA;CACpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,8BAA8B,GAAG,YAAY,CAkB7F;AA4BD;;GAEG;AACH,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM,IAAI,CA2B7F"}
1
+ {"version":3,"file":"session-translation-chat.d.ts","sourceRoot":"","sources":["../../../../src/client/session-translation-chat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAiBnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AAO3E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AACjE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,KAAK,EAAoH,wBAAwB,EAAE,MAAM,4CAA4C,CAAA;AAgB5M,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAA;IAC3C,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAA;IACzC,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAA;IACzC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,wBAAwB,CAAA;IAC1C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,UAAU,yBAA0B,SAAQ,cAAc;IACxD,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG;QACtC,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAA;KAC7E,CAAA;CACF;AAED;;;GAGG;AACH,KAAK,sBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,KAAK,SAAS,CAAA;AAQtI;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,YAAY,GAAG,IAAI,CA0IpF;AA+cD,UAAU,8BAA8B;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAA;IACzC,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAA;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,8BAA8B;IAC9B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,wBAAwB,CAAA;IACrD,QAAQ,CAAC,cAAc,EAAE,yBAAyB,CAAA;IAClD,QAAQ,CAAC,UAAU,EAAE;QAAE,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,CAAA;IACnE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAC3B,6CAA6C;IAC7C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,sBAAsB,CAAA;CACpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,8BAA8B,GAAG,YAAY,CAoB7F;AA4BD;;GAEG;AACH,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM,IAAI,CAiC7F"}
@@ -1,13 +1,17 @@
1
- import { createElement, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
1
+ import { createElement, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from 'react';
2
2
  import { createPortal } from 'react-dom';
3
3
  import { DisclosureRow, FileTypeIcon, IconCheckOutlineRegular, IconCopyOutlineRegular, IconThinkOutlineRegular, JsonBlock, MarkdownText, TextShimmer, fileExtension, fileSizeText, projectUserText, writeClipboard, } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { useDismissOnOutsidePointer } from './popup-dismiss.js';
5
5
  import { dshThemeColor } from './theme.js';
6
+ import { TRANSLATION_GLYPH_PATHS, TRANSLATION_GLYPH_SIZE, TRANSLATION_PENDING_BADGE_STYLE, TRANSLATION_STATUS_BADGE_STYLE } from '../shared/translation-indicator.js';
6
7
  import { uiFontSize } from './font-scale.js';
8
+ import { useCliSessionTranslationEnabled } from './cli-slots.js';
9
+ import { watchPendingTranslationTargets } from './session-translation-pending-dom.js';
10
+ import { readSessionTranslationMetrics } from '../shared/contracts/session-translation.js';
7
11
  /**
8
12
  * 把翻译状态和查看原文的入口放回对应消息块,不占用 AI 回复底部的统一操作栏。
9
13
  */
10
- function TranslationAction(props) {
14
+ export function TranslationAction(props) {
11
15
  const [open, setOpen] = useState(false);
12
16
  const rootRef = useRef(null);
13
17
  const buttonRef = useRef(null);
@@ -55,14 +59,17 @@ function TranslationAction(props) {
55
59
  };
56
60
  }, [open, props.popupAlign]);
57
61
  const record = props.record;
58
- const failed = record.failed === true;
59
- const alreadyTargetLanguage = record.alreadyTargetLanguage === true;
60
- const statusText = failed
61
- ? props.translationT('translation.statusFailed')
62
- : alreadyTargetLanguage
63
- ? props.translationT('translation.statusNotNeeded')
64
- : props.translationT('translation.statusTranslated');
65
- const model = formatModel(record.model, props.translationT);
62
+ const pending = record === undefined;
63
+ const failed = record?.failed === true;
64
+ const alreadyTargetLanguage = record?.alreadyTargetLanguage === true;
65
+ const statusText = pending
66
+ ? props.translationT('translation.statusTranslating')
67
+ : failed
68
+ ? props.translationT('translation.statusFailed')
69
+ : alreadyTargetLanguage
70
+ ? props.translationT('translation.statusNotNeeded')
71
+ : props.translationT('translation.statusTranslated');
72
+ const statusColor = pending ? dshThemeColor.accent : failed ? dshThemeColor.error : alreadyTargetLanguage ? dshThemeColor.accent : dshThemeColor.success;
66
73
  const detailsId = `codingns-translation-details-${safeDomId(props.messageId)}`;
67
74
  const details = open && popupPosition !== null ? createElement('div', {
68
75
  ref: popupRef,
@@ -88,7 +95,13 @@ function TranslationAction(props) {
88
95
  fontSize: uiFontSize(12),
89
96
  lineHeight: 1.45,
90
97
  },
91
- }, createElement('div', { style: { display: 'flex', justifyContent: 'space-between', gap: 10, fontWeight: 600 } }, createElement('span', undefined, props.translationT('translation.detailsTitle')), createElement('span', { style: { color: failed ? dshThemeColor.error : alreadyTargetLanguage ? dshThemeColor.accent : dshThemeColor.success, fontWeight: 400 } }, statusText)), record.sourceLanguage === undefined ? null : createElement('div', { style: { marginTop: 8, color: dshThemeColor.labelSecondary } }, `${props.translationT('translation.detectedLanguage')}: ${props.translationT(`translation.language.${record.sourceLanguage}`)}`), createElement(DetailBlock, { label: props.translationT('translation.originalText'), value: record.original }), createElement(DetailBlock, { label: props.translationT('translation.translatedText'), value: record.translated }), createElement('div', { style: { marginTop: 8, color: dshThemeColor.labelSecondary } }, `${props.translationT('translation.modelUsed')}: ${alreadyTargetLanguage ? props.translationT('translation.modelNotUsed') : model}`)) : null;
98
+ }, createElement('div', { style: { display: 'flex', justifyContent: 'space-between', gap: 10, fontWeight: 600 } }, createElement('span', undefined, props.translationT('translation.detailsTitle')), createElement('span', { role: 'status', 'aria-live': 'polite', style: { color: statusColor, fontWeight: 400 } }, statusText)), record?.sourceLanguage === undefined ? null : createElement('div', { style: { marginTop: 8, color: dshThemeColor.labelSecondary } }, `${props.translationT('translation.detectedLanguage')}: ${props.translationT(`translation.language.${record.sourceLanguage}`)}`), createElement(DetailBlock, { kind: 'original', translationT: props.translationT, value: record?.original ?? props.original ?? '' }), pending ? createElement('progress', { 'aria-label': statusText, style: { display: 'block', width: '100%', height: 4, marginTop: 8, accentColor: dshThemeColor.accent } }) : null, pending
99
+ ? createElement('div', { role: 'status', 'aria-live': 'polite', style: { marginTop: 8, color: dshThemeColor.labelSecondary } }, props.translationT('translation.progressHelp'))
100
+ : createElement(DetailBlock, { kind: props.messageRole === 'user' ? 'sent' : 'translated', translationT: props.translationT, value: record.translated }), pending ? null : createElement('div', { style: { marginTop: 8, color: dshThemeColor.labelSecondary } }, [
101
+ `${props.translationT('translation.modelUsed')}: ${alreadyTargetLanguage ? props.translationT('translation.modelNotUsed') : formatModel(record.model, props.translationT)}`,
102
+ ...(record.durationMs === undefined ? [] : [`${(record.durationMs / 1000).toFixed(2)} s`]),
103
+ ...(record.totalTokens === undefined ? [] : [`${record.totalTokens} tok`]),
104
+ ].join(' · '))) : null;
92
105
  const renderedDetails = details === null
93
106
  ? null
94
107
  : typeof document === 'undefined' ? details : createPortal(details, document.body);
@@ -113,38 +126,47 @@ function TranslationAction(props) {
113
126
  style: translationButtonStyle,
114
127
  }, createElement(TranslationGlyph), createElement('span', {
115
128
  'aria-hidden': true,
129
+ 'data-codingns-translation-pending': pending || undefined,
116
130
  style: {
117
- position: 'absolute',
118
- top: 1,
119
- right: 1,
120
- display: 'grid',
121
- placeItems: 'center',
122
- width: 10,
123
- height: 10,
124
- zIndex: 1,
125
- boxSizing: 'border-box',
126
- border: '1.5px solid var(--dsw-alias-bg-base, Canvas)',
127
- borderRadius: '50%',
128
- color: '#fff',
129
- background: failed ? dshThemeColor.error : alreadyTargetLanguage ? dshThemeColor.accent : dshThemeColor.success,
130
- fontSize: 6,
131
- fontWeight: 700,
132
- lineHeight: 1,
131
+ ...TRANSLATION_STATUS_BADGE_STYLE,
132
+ ...(pending ? TRANSLATION_PENDING_BADGE_STYLE : { background: statusColor }),
133
133
  },
134
- }, failed ? '!' : '✓')), renderedDetails);
134
+ }, pending ? null : failed ? '!' : '✓')), renderedDetails);
135
+ }
136
+ function PendingTranslationActions(props) {
137
+ const anchorRef = useRef(null);
138
+ const [targets, setTargets] = useState([]);
139
+ const enabled = useCliSessionTranslationEnabled(props.sessionId, props.services.rpc);
140
+ const subagent = props.useSession?.((state) => state.subagent);
141
+ const eligible = enabled && subagent == null && !props.sessionId?.startsWith('codingns-assistant-');
142
+ useLayoutEffect(() => {
143
+ setTargets([]);
144
+ if (!eligible || anchorRef.current === null)
145
+ return undefined;
146
+ return watchPendingTranslationTargets(anchorRef.current, setTargets);
147
+ }, [eligible, props.sessionId]);
148
+ return createElement('span', { ref: anchorRef, style: { display: 'none' } }, ...targets.map((target) => createPortal(createElement(PendingTranslationAction, {
149
+ services: props.services,
150
+ translationT: props.translationT,
151
+ original: target.original,
152
+ }), target.container, String(target.key))));
153
+ }
154
+ function PendingTranslationAction(props) {
155
+ const id = useId();
156
+ return createElement(TranslationAction, { ...props, messageId: `pending-${id}`, messageRole: 'user', popupAlign: 'end' });
135
157
  }
136
158
  function TranslationGlyph() {
137
159
  return createElement('svg', {
138
160
  viewBox: '0 0 24 24',
139
- width: 16,
140
- height: 16,
161
+ width: TRANSLATION_GLYPH_SIZE,
162
+ height: TRANSLATION_GLYPH_SIZE,
141
163
  fill: 'none',
142
164
  stroke: 'currentColor',
143
165
  strokeWidth: 1.5,
144
166
  strokeLinecap: 'round',
145
167
  strokeLinejoin: 'round',
146
168
  'aria-hidden': true,
147
- }, createElement('path', { d: 'M3.5 5.5h10M8.5 3.5v2M12.5 5.5c0 4.4-3.3 7.2-8 9' }), createElement('path', { d: 'M5.5 8.5c1.4 2 3.4 3.7 6 4.8M14.5 19.5l3-8 3 8M15.7 17h3.6' }));
169
+ }, ...TRANSLATION_GLYPH_PATHS.map(d => createElement('path', { key: d, d })));
148
170
  }
149
171
  const messageActionButtonStyle = {
150
172
  position: 'relative',
@@ -164,8 +186,36 @@ const translationButtonStyle = {
164
186
  ...messageActionButtonStyle,
165
187
  transition: 'background-color 100ms ease, color 100ms ease',
166
188
  };
167
- function DetailBlock({ label, value }) {
168
- return createElement('div', { style: { marginTop: 8 } }, createElement('div', { style: { marginBottom: 3, color: dshThemeColor.labelSecondary, fontWeight: 600 } }, label), createElement('pre', { style: { margin: 0, whiteSpace: 'pre-wrap', overflowWrap: 'anywhere', fontFamily: 'inherit' } }, value));
189
+ const translationDetailTypes = {
190
+ original: { labelKey: 'translation.originalText', color: dshThemeColor.labelSecondary },
191
+ translated: { labelKey: 'translation.translatedText', color: dshThemeColor.accent },
192
+ sent: { labelKey: 'translation.actualSentText', color: dshThemeColor.success },
193
+ };
194
+ function DetailBlock({ kind, translationT, value }) {
195
+ const { labelKey, color } = translationDetailTypes[kind];
196
+ return createElement('div', { style: { marginTop: 8 } }, createElement('div', {
197
+ style: {
198
+ display: 'inline-block',
199
+ maxWidth: '100%',
200
+ boxSizing: 'border-box',
201
+ marginBottom: 6,
202
+ padding: '2px 8px',
203
+ border: `1px solid color-mix(in srgb, ${color} 30%, transparent)`,
204
+ borderRadius: 'var(--dsw-radius-sm, 6px)',
205
+ color,
206
+ background: `color-mix(in srgb, ${color} 12%, transparent)`,
207
+ fontWeight: 600,
208
+ overflowWrap: 'anywhere',
209
+ },
210
+ }, translationT(labelKey)), createElement('pre', {
211
+ style: {
212
+ margin: 0,
213
+ color: `color-mix(in srgb, ${color} 45%, ${dshThemeColor.labelPrimary})`,
214
+ whiteSpace: 'pre-wrap',
215
+ overflowWrap: 'anywhere',
216
+ fontFamily: 'inherit',
217
+ },
218
+ }, value));
169
219
  }
170
220
  /** 按原生 user 节点的结构渲染用户消息;翻译入口只在确实有记录时追加。 */
171
221
  function UserMessageNodeView(props) {
@@ -238,6 +288,7 @@ function UserMessageNodeView(props) {
238
288
  style: { paddingRight: 12, color: dshThemeColor.labelTertiary, fontSize: 'var(--dsh-content-font-size-secondary, 13px)', whiteSpace: 'nowrap' },
239
289
  }, formatMessageTime(data.time)) : null, translationRecord === undefined ? null : createElement(TranslationAction, {
240
290
  messageId,
291
+ messageRole: 'user',
241
292
  services: props.services,
242
293
  translationT: props.translationT,
243
294
  popupAlign: 'end',
@@ -321,7 +372,7 @@ function formatMessageTime(time) {
321
372
  : { ...(date.getFullYear() === now.getFullYear() ? {} : { year: 'numeric' }), month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' };
322
373
  return new Intl.DateTimeFormat(undefined, options).format(date);
323
374
  }
324
- /** DSH assistant-step renderer: keep each translated text/reasoning block's source action adjacent to that block. */
375
+ /** DSH assistant-step renderer: keep each translation action adjacent to its source block. */
325
376
  function AssistantTranslationNodeView(props) {
326
377
  const data = props.node.data;
327
378
  const blocks = Array.isArray(data.blocks) ? data.blocks : [];
@@ -375,12 +426,14 @@ function AssistantTranslationNodeView(props) {
375
426
  const translationRecord = readRecord(block) ?? (hasTranslation && eventSnapshot !== undefined
376
427
  ? findTranslationRecordForText(eventSnapshot, block.text, 'assistant', data.turn, data.step)
377
428
  : undefined);
429
+ const displayText = translationRecord?.translated ?? block.text;
378
430
  rendered.push(createElement('div', {
379
431
  key: `text-${index}`,
380
432
  'data-codingns-translation-inline': true,
381
433
  style: { minWidth: 0 },
382
- }, createElement(MarkdownText, { text: block.text, streaming, labels: markdownLabels, fileMentions: mentions, pathImages }), translationRecord === undefined ? null : createElement(TranslationAction, {
434
+ }, createElement(MarkdownText, { text: displayText, streaming, labels: markdownLabels, fileMentions: mentions, pathImages }), translationRecord === undefined ? null : createElement(TranslationAction, {
383
435
  messageId: `${messageId}-text-${index}`,
436
+ messageRole: 'assistant',
384
437
  services: props.services,
385
438
  translationT: props.translationT,
386
439
  record: translationRecord,
@@ -442,7 +495,8 @@ function AssistantTranslationNodeView(props) {
442
495
  function AssistantReasoningBlock(props) {
443
496
  const [expanded, setExpanded] = useState(false);
444
497
  const groupRef = props.useGroupAction?.(props.hidden, props.reveal);
445
- const summary = props.text.split('\n').map((line) => line.replace(/[`*_#>]/gu, '').trim()).find(Boolean) ?? '';
498
+ const displayText = props.translationRecord?.translated ?? props.text;
499
+ const summary = displayText.split('\n').map((line) => line.replace(/[`*_#>]/gu, '').trim()).find(Boolean) ?? '';
446
500
  return createElement('div', {
447
501
  ref: groupRef,
448
502
  hidden: props.hidden,
@@ -483,15 +537,17 @@ function AssistantReasoningBlock(props) {
483
537
  * 工厂存在时才允许表现层覆盖。
484
538
  */
485
539
  export function AssistantReasoningContent(props) {
486
- const markdown = createElement(MarkdownText, { text: props.text, streaming: props.running, labels: props.markdownLabels, fileMentions: props.mentions, pathImages: props.pathImages, variant: 'compact' });
540
+ const displayText = props.translationRecord?.translated ?? props.text;
541
+ const markdown = createElement(MarkdownText, { text: displayText, streaming: props.running, labels: props.markdownLabels, fileMentions: props.mentions, pathImages: props.pathImages, variant: 'compact' });
487
542
  const content = props.renderFactorySlot === undefined
488
543
  ? markdown
489
- : props.renderFactorySlot('conversation.chat.reasoning.content', { text: props.text, running: props.running }, { fallback: markdown });
544
+ : props.renderFactorySlot('conversation.chat.reasoning.content', { text: displayText, running: props.running }, { fallback: markdown });
490
545
  return createElement('div', {
491
546
  'data-codingns-translation-inline': true,
492
547
  style: { paddingTop: 8 },
493
548
  }, content, props.translationRecord === undefined ? null : createElement(TranslationAction, {
494
549
  messageId: props.messageId,
550
+ messageRole: 'assistant',
495
551
  services: props.services,
496
552
  translationT: props.translationT,
497
553
  record: props.translationRecord,
@@ -534,6 +590,11 @@ export function registerSessionTranslationChatUi(services) {
534
590
  return () => undefined;
535
591
  const removeStyles = installSessionTranslationChatStyles();
536
592
  const translationT = services.locale.bind('codingns');
593
+ const removePendingActions = slots.inject('conversation.input.left', () => slots.register({
594
+ name: 'conversation.input.left',
595
+ id: 'codingns-translation-pending',
596
+ inject: (sessionId) => ({ services, sessionId, translationT }),
597
+ }, PendingTranslationActions));
537
598
  const removeUserRenderer = slots.inject('conversation.chat.node', () => slots.register({
538
599
  name: 'conversation.chat.node',
539
600
  key: 'user',
@@ -549,6 +610,7 @@ export function registerSessionTranslationChatUi(services) {
549
610
  inject: (sessionId) => ({ services, sessionId, translationT }),
550
611
  }, AssistantTranslationNodeView));
551
612
  return () => {
613
+ removePendingActions();
552
614
  removeAssistantRenderer();
553
615
  removeUserRenderer();
554
616
  removeStyles();
@@ -564,6 +626,9 @@ function installSessionTranslationChatStyles() {
564
626
  style.dataset.plugin = 'codingns4dsh';
565
627
  style.dataset.pluginCss = styleId;
566
628
  style.textContent = `
629
+ @keyframes codingns-translation-spin{to{transform:rotate(360deg)}}
630
+ [data-codingns-translation-pending]{animation:codingns-translation-spin .8s linear infinite}
631
+ @media(prefers-reduced-motion:reduce){[data-codingns-translation-pending]{animation-duration:1.6s}}
567
632
  [data-codingns-translation-inline] > div:not([data-codingns-translation-action]){display:contents}
568
633
  [data-codingns-translation-inline] > div:not([data-codingns-translation-action]) > p:last-child{display:inline;margin:0}
569
634
  `;
@@ -594,7 +659,7 @@ function findTranslationRecordForText(snapshot, text, outputKind, turn, step) {
594
659
  collectRecordCandidates(event.data, candidates, new Set());
595
660
  for (const candidate of candidates) {
596
661
  const record = readRecord(candidate);
597
- if (record === undefined || record.translated !== text)
662
+ if (record === undefined || record.original !== text && record.translated !== text)
598
663
  continue;
599
664
  if (outputKind !== undefined && record.outputKind !== undefined && record.outputKind !== outputKind)
600
665
  continue;
@@ -647,6 +712,7 @@ function readRecord(value) {
647
712
  ...(candidate.alreadyTargetLanguage === true ? { alreadyTargetLanguage: true } : {}),
648
713
  ...(model === undefined ? {} : { model }),
649
714
  ...(candidate.failed === true ? { failed: true } : {}),
715
+ ...readSessionTranslationMetrics(candidate),
650
716
  };
651
717
  }
652
718
  function readModel(value) {
@@ -663,8 +729,8 @@ function readInputRecord(value) {
663
729
  return undefined;
664
730
  if (typeof candidate.original !== 'string' || typeof candidate.translated !== 'string')
665
731
  return undefined;
666
- const targetLanguage = candidate.targetLanguage;
667
- if (targetLanguage !== 'en' && targetLanguage !== 'fr' && targetLanguage !== 'de' && targetLanguage !== 'ja')
732
+ const targetLanguage = readLanguage(candidate.targetLanguage);
733
+ if (targetLanguage === undefined)
668
734
  return undefined;
669
735
  const model = readModel(candidate.model);
670
736
  return {
@@ -675,6 +741,7 @@ function readInputRecord(value) {
675
741
  ...(candidate.alreadyTargetLanguage === true ? { alreadyTargetLanguage: true } : {}),
676
742
  ...(model === undefined ? {} : { model }),
677
743
  ...(candidate.failed === true ? { failed: true } : {}),
744
+ ...readSessionTranslationMetrics(candidate),
678
745
  };
679
746
  }
680
747
  function inputRecordForAction(record) {
@@ -687,6 +754,7 @@ function inputRecordForAction(record) {
687
754
  ...(record.alreadyTargetLanguage === true ? { alreadyTargetLanguage: true } : {}),
688
755
  ...(record.model === undefined ? {} : { model: record.model }),
689
756
  ...(record.failed === true ? { failed: true } : {}),
757
+ ...readSessionTranslationMetrics(record),
690
758
  };
691
759
  }
692
760
  function readLanguage(value) {