@jingyi0605/codingns4dsh 0.2.1-beta.8 → 0.2.1-beta.9

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 (223) hide show
  1. package/README.en.md +3 -3
  2. package/README.md +7 -3
  3. package/data/build/dist/bootstrap/dsh-peer-host-preboot-shim.d.ts.map +1 -1
  4. package/data/build/dist/bootstrap/dsh-peer-host-preboot-shim.js +26 -1
  5. package/data/build/dist/bootstrap/dsh-peer-host-preboot-shim.js.map +1 -1
  6. package/data/build/dist/client/account-bar.d.ts.map +1 -1
  7. package/data/build/dist/client/account-bar.js +33 -21
  8. package/data/build/dist/client/account-bar.js.map +1 -1
  9. package/data/build/dist/client/avatar/desktop-bridge.js +1 -1
  10. package/data/build/dist/client/avatar/desktop-bridge.js.map +1 -1
  11. package/data/build/dist/client/avatar/desktop-view.d.ts +3 -1
  12. package/data/build/dist/client/avatar/desktop-view.d.ts.map +1 -1
  13. package/data/build/dist/client/avatar/desktop-view.js +31 -12
  14. package/data/build/dist/client/avatar/desktop-view.js.map +1 -1
  15. package/data/build/dist/client/avatar/notification-bubble.d.ts +7 -1
  16. package/data/build/dist/client/avatar/notification-bubble.d.ts.map +1 -1
  17. package/data/build/dist/client/avatar/notification-bubble.js +68 -33
  18. package/data/build/dist/client/avatar/notification-bubble.js.map +1 -1
  19. package/data/build/dist/client/bundle.js +1626 -303
  20. package/data/build/dist/client/bundle.js.map +1 -1
  21. package/data/build/dist/client/cli-slots.d.ts.map +1 -1
  22. package/data/build/dist/client/cli-slots.js +26 -2
  23. package/data/build/dist/client/cli-slots.js.map +1 -1
  24. package/data/build/dist/client/client.assistant-notifications.js.map +1 -1
  25. package/data/build/dist/client/client.delegate-command.js +6 -6
  26. package/data/build/dist/client/client.delegate-command.js.map +1 -1
  27. package/data/build/dist/client/client.native-workspace-store.js +39 -3
  28. package/data/build/dist/client/client.native-workspace-store.js.map +1 -1
  29. package/data/build/dist/client/client.session-translation.js +54 -0
  30. package/data/build/dist/client/client.session-translation.js.map +1 -0
  31. package/data/build/dist/client/client.transport.js +86 -2
  32. package/data/build/dist/client/client.transport.js.map +1 -1
  33. package/data/build/dist/client/desktop-assistant.js +217 -61
  34. package/data/build/dist/client/features/assistant-notification-store.d.ts +1 -1
  35. package/data/build/dist/client/features/assistant-notification-store.d.ts.map +1 -1
  36. package/data/build/dist/client/features/assistant-notification-store.js +1 -1
  37. package/data/build/dist/client/features/assistant-notification-store.js.map +1 -1
  38. package/data/build/dist/client/features/global-voice-assistant.d.ts.map +1 -1
  39. package/data/build/dist/client/features/global-voice-assistant.js +12 -6
  40. package/data/build/dist/client/features/global-voice-assistant.js.map +1 -1
  41. package/data/build/dist/client/features/index.d.ts +1 -0
  42. package/data/build/dist/client/features/index.d.ts.map +1 -1
  43. package/data/build/dist/client/features/index.js +3 -0
  44. package/data/build/dist/client/features/index.js.map +1 -1
  45. package/data/build/dist/client/features/peer-host.d.ts.map +1 -1
  46. package/data/build/dist/client/features/peer-host.js +99 -17
  47. package/data/build/dist/client/features/peer-host.js.map +1 -1
  48. package/data/build/dist/client/features/session-translation.d.ts +3 -0
  49. package/data/build/dist/client/features/session-translation.d.ts.map +1 -0
  50. package/data/build/dist/client/features/session-translation.js +116 -0
  51. package/data/build/dist/client/features/session-translation.js.map +1 -0
  52. package/data/build/dist/client/index.d.ts.map +1 -1
  53. package/data/build/dist/client/index.js +6 -1
  54. package/data/build/dist/client/index.js.map +1 -1
  55. package/data/build/dist/client/locales/panels.d.ts.map +1 -1
  56. package/data/build/dist/client/locales/panels.js +62 -0
  57. package/data/build/dist/client/locales/panels.js.map +1 -1
  58. package/data/build/dist/client/session-file-links.d.ts +11 -0
  59. package/data/build/dist/client/session-file-links.d.ts.map +1 -0
  60. package/data/build/dist/client/{peer-host-file-links.js → session-file-links.js} +13 -3
  61. package/data/build/dist/client/session-file-links.js.map +1 -0
  62. package/data/build/dist/client/session-translation-chat.d.ts +6 -0
  63. package/data/build/dist/client/session-translation-chat.d.ts.map +1 -0
  64. package/data/build/dist/client/session-translation-chat.js +686 -0
  65. package/data/build/dist/client/session-translation-chat.js.map +1 -0
  66. package/data/build/dist/client/workspace-session-visibility-dom.d.ts.map +1 -1
  67. package/data/build/dist/client/workspace-session-visibility-dom.js +23 -7
  68. package/data/build/dist/client/workspace-session-visibility-dom.js.map +1 -1
  69. package/data/build/dist/dsh-capabilities/client/assistant-session-navigation-adapter.d.ts +1 -1
  70. package/data/build/dist/dsh-capabilities/client/assistant-session-navigation-adapter.d.ts.map +1 -1
  71. package/data/build/dist/dsh-capabilities/client/assistant-session-navigation-adapter.js +28 -2
  72. package/data/build/dist/dsh-capabilities/client/assistant-session-navigation-adapter.js.map +1 -1
  73. package/data/build/dist/host/cli-adapters/acp-cli-driver.d.ts +21 -0
  74. package/data/build/dist/host/cli-adapters/acp-cli-driver.d.ts.map +1 -1
  75. package/data/build/dist/host/cli-adapters/acp-cli-driver.js +100 -3
  76. package/data/build/dist/host/cli-adapters/acp-cli-driver.js.map +1 -1
  77. package/data/build/dist/host/cli-adapters/codex-driver.d.ts +10 -1
  78. package/data/build/dist/host/cli-adapters/codex-driver.d.ts.map +1 -1
  79. package/data/build/dist/host/cli-adapters/codex-driver.js +73 -14
  80. package/data/build/dist/host/cli-adapters/codex-driver.js.map +1 -1
  81. package/data/build/dist/host/cli-adapters/command-code-acp-loader.d.ts +8 -1
  82. package/data/build/dist/host/cli-adapters/command-code-acp-loader.d.ts.map +1 -1
  83. package/data/build/dist/host/cli-adapters/command-code-acp-loader.js +45 -2
  84. package/data/build/dist/host/cli-adapters/command-code-acp-loader.js.map +1 -1
  85. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +8 -2
  86. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts.map +1 -1
  87. package/data/build/dist/host/cli-adapters/command-code-driver.js +32 -3
  88. package/data/build/dist/host/cli-adapters/command-code-driver.js.map +1 -1
  89. package/data/build/dist/host/cli-adapters/command-code-history.d.ts +27 -0
  90. package/data/build/dist/host/cli-adapters/command-code-history.d.ts.map +1 -1
  91. package/data/build/dist/host/cli-adapters/command-code-history.js +131 -1
  92. package/data/build/dist/host/cli-adapters/command-code-history.js.map +1 -1
  93. package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.js +9 -9
  94. package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.js.map +1 -1
  95. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +11 -0
  96. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts.map +1 -1
  97. package/data/build/dist/host/cli-adapters/dsh-message-projector.js +79 -7
  98. package/data/build/dist/host/cli-adapters/dsh-message-projector.js.map +1 -1
  99. package/data/build/dist/host/cli-adapters/feature.d.ts.map +1 -1
  100. package/data/build/dist/host/cli-adapters/feature.js +286 -19
  101. package/data/build/dist/host/cli-adapters/feature.js.map +1 -1
  102. package/data/build/dist/host/cli-adapters/grok-driver.d.ts.map +1 -1
  103. package/data/build/dist/host/cli-adapters/grok-driver.js +108 -47
  104. package/data/build/dist/host/cli-adapters/grok-driver.js.map +1 -1
  105. package/data/build/dist/host/cli-adapters/legacy-session-settings.d.ts.map +1 -1
  106. package/data/build/dist/host/cli-adapters/legacy-session-settings.js +7 -2
  107. package/data/build/dist/host/cli-adapters/legacy-session-settings.js.map +1 -1
  108. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +4 -2
  109. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts.map +1 -1
  110. package/data/build/dist/host/cli-adapters/opencode-driver.js +78 -31
  111. package/data/build/dist/host/cli-adapters/opencode-driver.js.map +1 -1
  112. package/data/build/dist/host/cli-adapters/opencode-v2-protocol.d.ts +1 -1
  113. package/data/build/dist/host/cli-adapters/opencode-v2-protocol.d.ts.map +1 -1
  114. package/data/build/dist/host/cli-adapters/opencode-v2-protocol.js +63 -6
  115. package/data/build/dist/host/cli-adapters/opencode-v2-protocol.js.map +1 -1
  116. package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +5 -1
  117. package/data/build/dist/host/cli-adapters/provider-subscription.d.ts.map +1 -1
  118. package/data/build/dist/host/cli-adapters/provider-subscription.js +90 -25
  119. package/data/build/dist/host/cli-adapters/provider-subscription.js.map +1 -1
  120. package/data/build/dist/host/cli-adapters/registry.d.ts.map +1 -1
  121. package/data/build/dist/host/cli-adapters/registry.js +22 -5
  122. package/data/build/dist/host/cli-adapters/registry.js.map +1 -1
  123. package/data/build/dist/host/cli-adapters/session-store.d.ts +1 -0
  124. package/data/build/dist/host/cli-adapters/session-store.d.ts.map +1 -1
  125. package/data/build/dist/host/cli-adapters/session-store.js +3 -0
  126. package/data/build/dist/host/cli-adapters/session-store.js.map +1 -1
  127. package/data/build/dist/host/cli-bridge/bridge-server.js +2 -2
  128. package/data/build/dist/host/cli-bridge/bridge-server.js.map +1 -1
  129. package/data/build/dist/host/cli-bridge/command-code-mod.js +21 -21
  130. package/data/build/dist/host/cli-bridge/command-code-mod.js.map +1 -1
  131. package/data/build/dist/host/cli-bridge/dispatch.js +19 -19
  132. package/data/build/dist/host/cli-bridge/dispatch.js.map +1 -1
  133. package/data/build/dist/host/cli-bridge/injections.d.ts +32 -0
  134. package/data/build/dist/host/cli-bridge/injections.d.ts.map +1 -1
  135. package/data/build/dist/host/cli-bridge/injections.js +137 -1
  136. package/data/build/dist/host/cli-bridge/injections.js.map +1 -1
  137. package/data/build/dist/host/cli-bridge/mcp-stdio-entry.js +20 -20
  138. package/data/build/dist/host/cli-bridge/mcp-stdio-entry.js.map +1 -1
  139. package/data/build/dist/host/desktop-assistant/controller.d.ts +1 -0
  140. package/data/build/dist/host/desktop-assistant/controller.d.ts.map +1 -1
  141. package/data/build/dist/host/desktop-assistant/controller.js +21 -3
  142. package/data/build/dist/host/desktop-assistant/controller.js.map +1 -1
  143. package/data/build/dist/host/desktop-assistant/mac-agent.d.ts.map +1 -1
  144. package/data/build/dist/host/desktop-assistant/mac-agent.js +15 -3
  145. package/data/build/dist/host/desktop-assistant/mac-agent.js.map +1 -1
  146. package/data/build/dist/host/desktop-assistant/win-agent.d.ts.map +1 -1
  147. package/data/build/dist/host/desktop-assistant/win-agent.js +21 -3
  148. package/data/build/dist/host/desktop-assistant/win-agent.js.map +1 -1
  149. package/data/build/dist/host/features/assistant-notifications.d.ts.map +1 -1
  150. package/data/build/dist/host/features/assistant-notifications.js +12 -4
  151. package/data/build/dist/host/features/assistant-notifications.js.map +1 -1
  152. package/data/build/dist/host/features/index.d.ts +1 -0
  153. package/data/build/dist/host/features/index.d.ts.map +1 -1
  154. package/data/build/dist/host/features/index.js +3 -0
  155. package/data/build/dist/host/features/index.js.map +1 -1
  156. package/data/build/dist/host/features/peer-host.d.ts.map +1 -1
  157. package/data/build/dist/host/features/peer-host.js +11 -4
  158. package/data/build/dist/host/features/peer-host.js.map +1 -1
  159. package/data/build/dist/host/features/session-translation-service.d.ts +31 -0
  160. package/data/build/dist/host/features/session-translation-service.d.ts.map +1 -0
  161. package/data/build/dist/host/features/session-translation-service.js +451 -0
  162. package/data/build/dist/host/features/session-translation-service.js.map +1 -0
  163. package/data/build/dist/host/features/session-translation.d.ts +5 -0
  164. package/data/build/dist/host/features/session-translation.d.ts.map +1 -0
  165. package/data/build/dist/host/features/session-translation.js +37 -0
  166. package/data/build/dist/host/features/session-translation.js.map +1 -0
  167. package/data/build/dist/host/features/types.d.ts +3 -0
  168. package/data/build/dist/host/features/types.d.ts.map +1 -1
  169. package/data/build/dist/host/image-artifact-store.d.ts +21 -0
  170. package/data/build/dist/host/image-artifact-store.d.ts.map +1 -0
  171. package/data/build/dist/host/image-artifact-store.js +136 -0
  172. package/data/build/dist/host/image-artifact-store.js.map +1 -0
  173. package/data/build/dist/host/index.d.ts.map +1 -1
  174. package/data/build/dist/host/index.js +3 -0
  175. package/data/build/dist/host/index.js.map +1 -1
  176. package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.d.ts +1 -1
  177. package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.d.ts.map +1 -1
  178. package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.js +2 -0
  179. package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.js.map +1 -1
  180. package/data/build/dist/host/native-session-bridge.d.ts +16 -1
  181. package/data/build/dist/host/native-session-bridge.d.ts.map +1 -1
  182. package/data/build/dist/host/native-session-bridge.js +47 -0
  183. package/data/build/dist/host/native-session-bridge.js.map +1 -1
  184. package/data/build/dist/host/rpc.js +3 -3
  185. package/data/build/dist/host/rpc.js.map +1 -1
  186. package/data/build/dist/host/session-changes.d.ts.map +1 -1
  187. package/data/build/dist/host/session-changes.js +196 -13
  188. package/data/build/dist/host/session-changes.js.map +1 -1
  189. package/data/build/dist/host/settings.d.ts.map +1 -1
  190. package/data/build/dist/host/settings.js +8 -0
  191. package/data/build/dist/host/settings.js.map +1 -1
  192. package/data/build/dist/shared/assistant-notifications.d.ts +2 -2
  193. package/data/build/dist/shared/assistant-notifications.d.ts.map +1 -1
  194. package/data/build/dist/shared/contracts/cli-adapter.d.ts +25 -2
  195. package/data/build/dist/shared/contracts/cli-adapter.d.ts.map +1 -1
  196. package/data/build/dist/shared/contracts/config.d.ts +6 -0
  197. package/data/build/dist/shared/contracts/config.d.ts.map +1 -1
  198. package/data/build/dist/shared/contracts/config.js +3 -0
  199. package/data/build/dist/shared/contracts/config.js.map +1 -1
  200. package/data/build/dist/shared/contracts/session-translation.d.ts +58 -0
  201. package/data/build/dist/shared/contracts/session-translation.d.ts.map +1 -0
  202. package/data/build/dist/shared/contracts/session-translation.js +23 -0
  203. package/data/build/dist/shared/contracts/session-translation.js.map +1 -0
  204. package/data/build/dist/shared/contracts/version.d.ts +1 -1
  205. package/data/build/dist/shared/contracts/version.js +1 -1
  206. package/data/build/dist/shared/delegation-carrier.js +6 -6
  207. package/data/build/dist/shared/delegation-carrier.js.map +1 -1
  208. package/data/build/dist/shared/desktop-assistant.d.ts +10 -4
  209. package/data/build/dist/shared/desktop-assistant.d.ts.map +1 -1
  210. package/data/build/dist/shared/desktop-assistant.js +28 -8
  211. package/data/build/dist/shared/desktop-assistant.js.map +1 -1
  212. package/data/build/dist/shared/index.d.ts +2 -1
  213. package/data/build/dist/shared/index.d.ts.map +1 -1
  214. package/data/build/dist/shared/index.js +2 -1
  215. package/data/build/dist/shared/index.js.map +1 -1
  216. package/package.json +2 -2
  217. package/profile/README.md +2 -2
  218. package/profile/package.json +2 -2
  219. package/profile/version.json +1 -1
  220. package/version.json +1 -1
  221. package/data/build/dist/client/peer-host-file-links.d.ts +0 -8
  222. package/data/build/dist/client/peer-host-file-links.d.ts.map +0 -1
  223. package/data/build/dist/client/peer-host-file-links.js.map +0 -1
@@ -0,0 +1,686 @@
1
+ import { createElement, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { DisclosureRow, FileTypeIcon, IconCheckOutlineRegular, IconCopyOutlineRegular, IconThinkOutlineRegular, JsonBlock, MarkdownText, TextShimmer, fileExtension, fileSizeText, projectUserText, writeClipboard, } from '@deepseek-ai/dsh-client-ui-primitives';
4
+ import { useDismissOnOutsidePointer } from './popup-dismiss.js';
5
+ import { dshThemeColor } from './theme.js';
6
+ import { uiFontSize } from './font-scale.js';
7
+ /**
8
+ * 把翻译状态和查看原文的入口放回对应消息块,不占用 AI 回复底部的统一操作栏。
9
+ */
10
+ function TranslationAction(props) {
11
+ const source = props.record === undefined ? readEventSource(props.services, props.sessionId) : undefined;
12
+ const [, refresh] = useState(0);
13
+ const [open, setOpen] = useState(false);
14
+ const rootRef = useRef(null);
15
+ const buttonRef = useRef(null);
16
+ const popupRef = useRef(null);
17
+ const [popupPosition, setPopupPosition] = useState(null);
18
+ useEffect(() => {
19
+ if (source === undefined)
20
+ return undefined;
21
+ return source.subscribe(() => refresh((value) => value + 1));
22
+ }, [source]);
23
+ useDismissOnOutsidePointer(rootRef, open, () => setOpen(false), { portal: popupRef });
24
+ useLayoutEffect(() => {
25
+ if (!open || typeof window === 'undefined') {
26
+ setPopupPosition(null);
27
+ return undefined;
28
+ }
29
+ const updatePosition = () => {
30
+ const button = buttonRef.current;
31
+ if (button === null)
32
+ return;
33
+ const rect = button.getBoundingClientRect();
34
+ const edge = 12;
35
+ const gap = 8;
36
+ const width = Math.max(1, Math.min(420, window.innerWidth - edge * 2));
37
+ const availableAbove = Math.max(0, rect.top - gap - edge);
38
+ const availableBelow = Math.max(0, window.innerHeight - rect.bottom - gap - edge);
39
+ const preferredHeight = Math.min(440, window.innerHeight * 0.65);
40
+ const placement = availableAbove >= preferredHeight
41
+ || (availableBelow < preferredHeight && availableAbove > availableBelow)
42
+ ? 'above'
43
+ : 'below';
44
+ const availableHeight = placement === 'above' ? availableAbove : availableBelow;
45
+ const maxHeight = Math.min(preferredHeight, availableHeight);
46
+ const desiredLeft = props.popupAlign === 'end' ? rect.right - width : rect.left;
47
+ const maxLeft = Math.max(edge, window.innerWidth - edge - width);
48
+ setPopupPosition({
49
+ left: Math.min(maxLeft, Math.max(edge, desiredLeft)),
50
+ top: placement === 'above' ? rect.top - gap : rect.bottom + gap,
51
+ width,
52
+ maxHeight,
53
+ placement,
54
+ });
55
+ };
56
+ updatePosition();
57
+ window.addEventListener('resize', updatePosition);
58
+ window.addEventListener('scroll', updatePosition, true);
59
+ return () => {
60
+ window.removeEventListener('resize', updatePosition);
61
+ window.removeEventListener('scroll', updatePosition, true);
62
+ };
63
+ }, [open, props.popupAlign]);
64
+ const record = props.record ?? (source === undefined || props.lookupText === undefined
65
+ ? undefined
66
+ : findTranslationRecordForText(source.getSnapshot(), props.lookupText, props.outputKind, props.turn, props.step));
67
+ if (record === undefined)
68
+ return null;
69
+ const failed = record.failed === true;
70
+ const alreadyTargetLanguage = record.alreadyTargetLanguage === true;
71
+ const statusText = failed
72
+ ? props.translationT('translation.statusFailed')
73
+ : alreadyTargetLanguage
74
+ ? props.translationT('translation.statusNotNeeded')
75
+ : props.translationT('translation.statusTranslated');
76
+ const model = formatModel(record.model, props.translationT);
77
+ const detailsId = `codingns-translation-details-${safeDomId(props.messageId)}`;
78
+ const details = open && popupPosition !== null ? createElement('div', {
79
+ ref: popupRef,
80
+ role: 'dialog',
81
+ 'aria-label': props.translationT('translation.detailsTitle'),
82
+ id: detailsId,
83
+ style: {
84
+ position: 'fixed',
85
+ zIndex: 2147483647,
86
+ left: popupPosition.left,
87
+ top: popupPosition.top,
88
+ width: popupPosition.width,
89
+ maxHeight: popupPosition.maxHeight,
90
+ boxSizing: 'border-box',
91
+ ...(popupPosition.placement === 'above' ? { transform: 'translateY(-100%)' } : {}),
92
+ overflow: 'auto',
93
+ padding: '10px 12px',
94
+ border: `1px solid ${dshThemeColor.border}`,
95
+ borderRadius: 8,
96
+ color: dshThemeColor.labelPrimary,
97
+ background: dshThemeColor.menuBackground,
98
+ boxShadow: '0 8px 24px color-mix(in srgb, CanvasText 16%, transparent)',
99
+ fontSize: uiFontSize(12),
100
+ lineHeight: 1.45,
101
+ },
102
+ }, 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;
103
+ const renderedDetails = details === null
104
+ ? null
105
+ : typeof document === 'undefined' ? details : createPortal(details, document.body);
106
+ return createElement('div', {
107
+ ref: rootRef,
108
+ style: {
109
+ position: 'relative',
110
+ display: 'inline-flex',
111
+ alignItems: 'center',
112
+ verticalAlign: 'middle',
113
+ ...(props.inline === true ? { marginInlineStart: 4 } : {}),
114
+ },
115
+ 'data-codingns-translation-action': props.messageId,
116
+ }, createElement('button', {
117
+ ref: buttonRef,
118
+ type: 'button',
119
+ title: `${statusText} · ${props.translationT('translation.viewDetails')}`,
120
+ 'aria-label': `${statusText} · ${props.translationT('translation.viewDetails')}`,
121
+ 'aria-expanded': open,
122
+ 'aria-controls': detailsId,
123
+ onClick: () => setOpen((value) => !value),
124
+ style: translationButtonStyle,
125
+ }, createElement(TranslationGlyph), createElement('span', {
126
+ 'aria-hidden': true,
127
+ style: {
128
+ position: 'absolute',
129
+ top: 1,
130
+ right: 1,
131
+ display: 'grid',
132
+ placeItems: 'center',
133
+ width: 10,
134
+ height: 10,
135
+ zIndex: 1,
136
+ boxSizing: 'border-box',
137
+ border: '1.5px solid var(--dsw-alias-bg-base, Canvas)',
138
+ borderRadius: '50%',
139
+ color: '#fff',
140
+ background: failed ? dshThemeColor.error : alreadyTargetLanguage ? dshThemeColor.accent : dshThemeColor.success,
141
+ fontSize: 6,
142
+ fontWeight: 700,
143
+ lineHeight: 1,
144
+ },
145
+ }, failed ? '!' : '✓')), renderedDetails);
146
+ }
147
+ function TranslationGlyph() {
148
+ return createElement('svg', {
149
+ viewBox: '0 0 24 24',
150
+ width: 16,
151
+ height: 16,
152
+ fill: 'none',
153
+ stroke: 'currentColor',
154
+ strokeWidth: 1.5,
155
+ strokeLinecap: 'round',
156
+ strokeLinejoin: 'round',
157
+ 'aria-hidden': true,
158
+ }, 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' }));
159
+ }
160
+ const messageActionButtonStyle = {
161
+ position: 'relative',
162
+ display: 'inline-flex',
163
+ alignItems: 'center',
164
+ justifyContent: 'center',
165
+ width: 28,
166
+ height: 28,
167
+ padding: 0,
168
+ border: 0,
169
+ borderRadius: 'var(--dsw-radius-sm, 6px)',
170
+ color: dshThemeColor.labelTertiary,
171
+ background: 'transparent',
172
+ cursor: 'pointer',
173
+ };
174
+ const translationButtonStyle = {
175
+ ...messageActionButtonStyle,
176
+ transition: 'background-color 100ms ease, color 100ms ease',
177
+ };
178
+ function DetailBlock({ label, value }) {
179
+ 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));
180
+ }
181
+ /** 按原生 user 节点的结构渲染用户消息,并把翻译按钮放在复制按钮前。 */
182
+ function UserMessageNodeView(props) {
183
+ const data = props.node.data;
184
+ const parts = splitUserContent(data.content);
185
+ const record = readInputRecord({ source: data.source });
186
+ // DSH 自带消息保持原样;只接管确实带有 CodingNS 翻译记录的用户消息。
187
+ if (record === undefined)
188
+ return null;
189
+ const translationRecord = inputRecordForAction(record);
190
+ const labels = Array.isArray(data.referenceLabels) ? data.referenceLabels.filter((label) => typeof label === 'string') : [];
191
+ const skillNames = Array.isArray(data.skillNames) ? data.skillNames.filter((name) => typeof name === 'string') : [];
192
+ const showBubble = parts.text !== '' || parts.rest.length > 0;
193
+ const images = parts.attachments.filter((attachment) => attachment.type === 'image');
194
+ const compactImages = images.length > 1;
195
+ const messageId = `user-${String(data.seq ?? props.node.id ?? 'message')}`;
196
+ const chatT = props.t;
197
+ return createElement('div', {
198
+ 'data-chat-flow-kind': 'user',
199
+ style: { display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 6 },
200
+ }, createElement('div', {
201
+ style: {
202
+ display: 'flex',
203
+ flexDirection: 'column',
204
+ alignItems: 'flex-end',
205
+ gap: 8,
206
+ minWidth: 0,
207
+ maxWidth: 'min(calc(var(--dsh-chat-content-width, 748px) * .702), 82%)',
208
+ },
209
+ }, parts.attachments.length > 0 ? createElement('div', {
210
+ 'data-message-attachments': true,
211
+ style: { display: 'flex', flexWrap: 'wrap', justifyContent: 'flex-end', gap: 8, maxWidth: '100%' },
212
+ }, ...parts.attachments.map((attachment, index) => attachment.type === 'image'
213
+ ? createElement('div', { key: `image-${index}` }, props.renderMessageImages({ images: [{ attachment: attachment.attachment }], align: 'end', compact: compactImages }))
214
+ : createFileAttachment(attachment.file, index))) : null, showBubble ? createElement('div', {
215
+ style: {
216
+ boxSizing: 'border-box',
217
+ maxWidth: '100%',
218
+ padding: '10px 16px',
219
+ borderRadius: 'var(--dsw-radius-xl, 18px)',
220
+ color: dshThemeColor.labelPrimary,
221
+ background: 'var(--dsw-specific-bubble, var(--dsw-alias-interactive-bg-hover, rgba(127,127,127,.08)))',
222
+ fontSize: 'var(--dsh-content-font-size, 14px)',
223
+ lineHeight: 'calc(22px + var(--dsh-content-font-delta, 0px))',
224
+ whiteSpace: 'pre-wrap',
225
+ wordBreak: 'break-word',
226
+ },
227
+ }, projectUserText(parts.text, labels, skillNames, 'skill', typeof props.openFile === 'function' && typeof props.openSkill === 'function'
228
+ ? { openFile: props.openFile, openSkill: props.openSkill }
229
+ : undefined), ...parts.rest.map((block, index) => createElement(JsonBlock, {
230
+ key: `extra-${index}`,
231
+ label: chatT('message.extraBlock'),
232
+ payload: block,
233
+ truncatedLabel: (total) => chatT('json.truncated', { total }),
234
+ }))) : null), createElement('div', {
235
+ style: {
236
+ display: 'flex',
237
+ alignItems: 'center',
238
+ alignSelf: 'flex-end',
239
+ height: 'calc(28px + var(--dsh-content-font-delta, 0px))',
240
+ gap: 8,
241
+ color: dshThemeColor.labelTertiary,
242
+ },
243
+ }, typeof data.time === 'number' ? createElement('span', {
244
+ style: { paddingRight: 12, color: dshThemeColor.labelTertiary, fontSize: 'var(--dsh-content-font-size-secondary, 13px)', whiteSpace: 'nowrap' },
245
+ }, formatMessageTime(data.time)) : null, translationRecord === undefined ? null : createElement(TranslationAction, {
246
+ messageId,
247
+ sessionId: props.sessionId,
248
+ services: props.services,
249
+ translationT: props.translationT,
250
+ popupAlign: 'end',
251
+ record: translationRecord,
252
+ }), createElement(CopyAction, { text: parts.text, t: chatT })));
253
+ }
254
+ function createFileAttachment(file, index) {
255
+ const name = typeof file.name === 'string' ? file.name : '';
256
+ const bytes = typeof file.bytes === 'number' ? file.bytes : 0;
257
+ return createElement('span', {
258
+ key: `file-${index}`,
259
+ title: name,
260
+ style: {
261
+ display: 'inline-flex',
262
+ alignItems: 'center',
263
+ minWidth: 0,
264
+ maxWidth: '100%',
265
+ gap: 8,
266
+ padding: '8px 10px',
267
+ border: `1px solid ${dshThemeColor.border}`,
268
+ borderRadius: 8,
269
+ color: dshThemeColor.labelPrimary,
270
+ background: dshThemeColor.surfaceSubtle,
271
+ },
272
+ }, createElement(FileTypeIcon, { path: name, style: { flex: 'none' } }), createElement('span', { style: { display: 'flex', flexDirection: 'column', minWidth: 0, textAlign: 'left' } }, createElement('span', { style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, name), createElement('span', { style: { color: dshThemeColor.labelSecondary, fontSize: 11 } }, [fileExtension(name).toUpperCase().slice(0, 8), fileSizeText(bytes)].filter(Boolean).join(' '))));
273
+ }
274
+ function CopyAction({ text, t: chatT }) {
275
+ const [copied, setCopied] = useState(false);
276
+ const timer = useRef(undefined);
277
+ useEffect(() => () => {
278
+ if (timer.current !== undefined)
279
+ clearTimeout(timer.current);
280
+ }, []);
281
+ return createElement('button', {
282
+ type: 'button',
283
+ title: copied ? chatT('copied') : chatT('copy'),
284
+ 'aria-label': copied ? chatT('copied') : chatT('copy'),
285
+ onClick: () => {
286
+ void writeClipboard(text).then((success) => {
287
+ if (!success)
288
+ return;
289
+ setCopied(true);
290
+ if (timer.current !== undefined)
291
+ clearTimeout(timer.current);
292
+ timer.current = setTimeout(() => {
293
+ timer.current = undefined;
294
+ setCopied(false);
295
+ }, 1000);
296
+ });
297
+ },
298
+ style: messageActionButtonStyle,
299
+ }, createElement(copied ? IconCheckOutlineRegular : IconCopyOutlineRegular));
300
+ }
301
+ function splitUserContent(content) {
302
+ const text = [];
303
+ const attachments = [];
304
+ const rest = [];
305
+ if (typeof content === 'string')
306
+ return { text: content, attachments, rest };
307
+ if (!Array.isArray(content))
308
+ return { text: '', attachments, rest };
309
+ for (const value of content) {
310
+ const block = asRecord(value);
311
+ if (block?.type === 'text' && typeof block.text === 'string')
312
+ text.push(block.text);
313
+ else if (block?.type === 'image' && block.attachment !== undefined)
314
+ attachments.push({ type: 'image', attachment: block.attachment });
315
+ else if (block?.type === 'file' && asRecord(block.attachment) !== undefined)
316
+ attachments.push({ type: 'file', file: asRecord(block.attachment) });
317
+ else
318
+ rest.push(value);
319
+ }
320
+ return { text: text.join(''), attachments, rest };
321
+ }
322
+ function formatMessageTime(time) {
323
+ const date = new Date(time);
324
+ const now = new Date();
325
+ const sameDay = date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate();
326
+ const options = sameDay
327
+ ? { hour: '2-digit', minute: '2-digit' }
328
+ : { ...(date.getFullYear() === now.getFullYear() ? {} : { year: 'numeric' }), month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' };
329
+ return new Intl.DateTimeFormat(undefined, options).format(date);
330
+ }
331
+ /** DSH assistant-step renderer: keep each translated text/reasoning block's source action adjacent to that block. */
332
+ function AssistantTranslationNodeView(props) {
333
+ const data = props.node.data;
334
+ const blocks = Array.isArray(data.blocks) ? data.blocks : [];
335
+ const streaming = data.status === 'running';
336
+ const chatT = props.t;
337
+ const markdownLabels = useMemo(() => createMarkdownLabels(chatT), [chatT]);
338
+ const pathImages = useMemo(() => ({ resolve: resolveMarkdownImagePath }), []);
339
+ const eventSnapshot = readEventSource(props.services, props.sessionId)?.getSnapshot();
340
+ const hasTranslation = blocks.some((block) => {
341
+ if (block.kind !== 'reasoning' && block.kind !== 'text')
342
+ return false;
343
+ const outputKind = block.kind === 'reasoning' ? 'reasoning' : 'assistant';
344
+ const embedded = readRecord(block);
345
+ if (embedded !== undefined && (embedded.outputKind === undefined || embedded.outputKind === outputKind))
346
+ return true;
347
+ return eventSnapshot !== undefined && findTranslationRecordForText(eventSnapshot, block.text, outputKind, data.turn, data.step) !== undefined;
348
+ });
349
+ const turn = props.node.location?.kind === 'turn' || props.node.location?.kind === 'step' ? props.node.location.turn : undefined;
350
+ const tail = typeof props.useTurnData === 'function' ? props.useTurnData('turn-tail') : undefined;
351
+ const finalNode = data.finalNode;
352
+ const fileOwner = turn?.status === 'closed' && finalNode !== undefined && tail?.closing?.finalNode?.seq === finalNode.seq
353
+ ? { turn, seq: finalNode.seq, openFile: (path) => { void props.openFile(path); } }
354
+ : undefined;
355
+ const mentions = fileOwner === undefined || typeof props.fileMentions !== 'function' ? undefined : props.fileMentions(fileOwner);
356
+ const turnProcess = props.turnProcess;
357
+ const reasoningHidden = turnProcess !== undefined
358
+ && turnProcess.foldable === true
359
+ && turnProcess.spec?.answerStep === data.step
360
+ && turnProcess.spec?.inlineReasoning === true
361
+ && turnProcess.open !== true;
362
+ const rendered = [];
363
+ const last = blocks.length - 1;
364
+ const messageId = typeof finalNode?.messageId === 'string' ? finalNode.messageId : `turn-${data.turn}-step-${data.step}`;
365
+ if (!hasTranslation || !(streaming || data.status === 'interrupted' || blocks.some((block) => block.kind !== 'tool-call')))
366
+ return null;
367
+ for (let index = 0; index < blocks.length; index += 1) {
368
+ const block = blocks[index];
369
+ if (block === undefined)
370
+ continue;
371
+ if (props.groupPart === 'reasoning' && block.kind !== 'reasoning')
372
+ continue;
373
+ if (props.groupPart === 'response' && block.kind === 'reasoning')
374
+ continue;
375
+ const running = streaming && index === last;
376
+ if (block.kind === 'text') {
377
+ const translationRecord = readRecord(block);
378
+ rendered.push(createElement('div', {
379
+ key: `text-${index}`,
380
+ 'data-codingns-translation-inline': true,
381
+ style: { minWidth: 0 },
382
+ }, createElement(MarkdownText, { text: block.text, streaming, labels: markdownLabels, fileMentions: mentions, pathImages }), createElement(TranslationAction, {
383
+ messageId: `${messageId}-text-${index}`,
384
+ sessionId: props.sessionId,
385
+ services: props.services,
386
+ translationT: props.translationT,
387
+ lookupText: block.text,
388
+ outputKind: 'assistant',
389
+ turn: data.turn,
390
+ step: data.step,
391
+ inline: true,
392
+ ...(translationRecord === undefined ? {} : { record: translationRecord }),
393
+ })));
394
+ continue;
395
+ }
396
+ if (block.kind === 'reasoning') {
397
+ rendered.push(createElement(AssistantReasoningBlock, {
398
+ key: `reasoning-${index}`,
399
+ messageId: `${messageId}-reasoning-${index}`,
400
+ sessionId: props.sessionId,
401
+ services: props.services,
402
+ translationT: props.translationT,
403
+ chatT,
404
+ text: block.text,
405
+ turn: data.turn,
406
+ step: data.step,
407
+ running,
408
+ hidden: reasoningHidden,
409
+ reveal: () => turnProcess?.setOpen?.(true),
410
+ useGroupAction: props.useGroupAction,
411
+ renderFactorySlot: props.renderFactorySlot,
412
+ markdownLabels,
413
+ pathImages,
414
+ mentions,
415
+ }));
416
+ continue;
417
+ }
418
+ if (block.kind === 'image') {
419
+ const imageGroup = [block];
420
+ while (index + 1 < blocks.length && blocks[index + 1]?.kind === 'image') {
421
+ const next = blocks[index + 1];
422
+ if (next?.kind !== 'image')
423
+ break;
424
+ imageGroup.push(next);
425
+ index += 1;
426
+ }
427
+ rendered.push(createElement('div', { key: `images-${index}`, style: { alignSelf: 'flex-start' } }, props.renderMessageImages({ images: imageGroup.map((image) => ({ attachment: image.attachment })), align: 'start' })));
428
+ continue;
429
+ }
430
+ if (block.kind === 'other') {
431
+ rendered.push(createElement(JsonBlock, {
432
+ key: `other-${index}`,
433
+ label: chatT('message.unknownBlock'),
434
+ payload: block.block,
435
+ truncatedLabel: (total) => chatT('json.truncated', { total }),
436
+ }));
437
+ }
438
+ }
439
+ return createElement('div', {
440
+ 'data-streaming': streaming || undefined,
441
+ style: { display: 'flex', flexDirection: 'column', gap: 16, minWidth: 0, color: dshThemeColor.labelPrimary, fontSize: 'var(--dsh-content-font-size, 14px)', lineHeight: 'calc(24px + var(--dsh-content-font-delta, 0px))' },
442
+ }, ...rendered, data.status === 'interrupted' ? createElement('span', {
443
+ style: { alignSelf: 'flex-start', padding: '0 6px', borderRadius: 'var(--dsw-radius-sm, 4px)', color: dshThemeColor.labelTertiary, background: dshThemeColor.surfaceSubtle, fontSize: 11, lineHeight: '18px' },
444
+ }, chatT('message.stopped')) : null);
445
+ }
446
+ function AssistantReasoningBlock(props) {
447
+ const [expanded, setExpanded] = useState(false);
448
+ const groupRef = props.useGroupAction?.(props.hidden, props.reveal);
449
+ const summary = props.text.split('\n').map((line) => line.replace(/[`*_#>]/gu, '').trim()).find(Boolean) ?? '';
450
+ const markdown = props.renderFactorySlot === undefined
451
+ ? createElement(MarkdownText, { text: props.text, streaming: props.running, labels: props.markdownLabels, fileMentions: props.mentions, pathImages: props.pathImages, variant: 'compact' })
452
+ : props.renderFactorySlot('conversation.chat.reasoning.content', { text: props.text, running: props.running });
453
+ return createElement('div', {
454
+ ref: groupRef,
455
+ hidden: props.hidden,
456
+ 'data-turn-process-inline': props.hidden || undefined,
457
+ }, createElement(DisclosureRow, {
458
+ icon: createElement(IconThinkOutlineRegular, { size: 14 }),
459
+ title: props.chatT('message.think'),
460
+ open: expanded,
461
+ expandable: true,
462
+ expandOnRowClick: true,
463
+ running: props.running,
464
+ onToggle: () => setExpanded((value) => !value),
465
+ collapsedContent: createElement('span', {
466
+ style: { minWidth: 0, overflow: 'hidden', color: dshThemeColor.labelSecondary, fontSize: 'var(--dsh-content-font-size-secondary, 13px)', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },
467
+ }, createElement(TextShimmer, { active: props.running, children: summary })),
468
+ }, expanded ? createElement('div', {
469
+ 'data-codingns-translation-inline': true,
470
+ style: { paddingTop: 8 },
471
+ }, markdown, createElement(TranslationAction, {
472
+ messageId: props.messageId,
473
+ sessionId: props.sessionId,
474
+ services: props.services,
475
+ translationT: props.translationT,
476
+ lookupText: props.text,
477
+ outputKind: 'reasoning',
478
+ ...(props.turn === undefined ? {} : { turn: props.turn }),
479
+ ...(props.step === undefined ? {} : { step: props.step }),
480
+ inline: true,
481
+ })) : null));
482
+ }
483
+ function createMarkdownLabels(chatT) {
484
+ return {
485
+ code: {
486
+ copyLabel: chatT('copy'),
487
+ copiedLabel: chatT('copied'),
488
+ toolbarLabels: {
489
+ codeLabel: chatT('codeBlock.title'),
490
+ wrapLabel: chatT('codeBlock.wrap'),
491
+ unwrapLabel: chatT('codeBlock.unwrap'),
492
+ },
493
+ },
494
+ footnotes: chatT('markdown.footnotes'),
495
+ };
496
+ }
497
+ function resolveMarkdownImagePath(value) {
498
+ if (typeof document === 'undefined' || !value.startsWith('/') || /^[/\\]{2}/u.test(value) || /[\u0000-\u001f\u007f]/u.test(value))
499
+ return undefined;
500
+ const base = document.baseURI;
501
+ if (!/^https?:/u.test(base) && !base.startsWith('dsh-app://app/'))
502
+ return undefined;
503
+ try {
504
+ return new URL(`api/file?path=${encodeURIComponent(value)}`, base).href;
505
+ }
506
+ catch {
507
+ return undefined;
508
+ }
509
+ }
510
+ /**
511
+ * 注册两个有实际翻译记录的消息渲染器;DSH 的用户气泡内容仍由统一主题令牌控制。
512
+ */
513
+ export function registerSessionTranslationChatUi(services) {
514
+ const slots = services.slots;
515
+ if (slots?.inject === undefined || slots.register === undefined)
516
+ return () => undefined;
517
+ const removeStyles = installSessionTranslationChatStyles();
518
+ const translationT = services.locale.bind('codingns');
519
+ const removeUserRenderer = slots.inject('conversation.chat.node', () => slots.register({
520
+ name: 'conversation.chat.node',
521
+ key: 'user',
522
+ priority: -100,
523
+ locale: 'chat',
524
+ inject: (sessionId) => ({ services, sessionId, translationT }),
525
+ }, UserMessageNodeView));
526
+ const removeAssistantRenderer = slots.inject('conversation.chat.node', () => slots.register({
527
+ name: 'conversation.chat.node',
528
+ key: 'assistant-step',
529
+ priority: -100,
530
+ locale: 'chat',
531
+ inject: (sessionId) => ({ services, sessionId, translationT }),
532
+ }, AssistantTranslationNodeView));
533
+ return () => {
534
+ removeAssistantRenderer();
535
+ removeUserRenderer();
536
+ removeStyles();
537
+ };
538
+ }
539
+ function installSessionTranslationChatStyles() {
540
+ if (typeof document === 'undefined')
541
+ return () => undefined;
542
+ const styleId = 'codingns-session-translation-chat';
543
+ if (document.head.querySelector(`style[data-plugin-css="${styleId}"]`) !== null)
544
+ return () => undefined;
545
+ const style = document.createElement('style');
546
+ style.dataset.plugin = 'codingns4dsh';
547
+ style.dataset.pluginCss = styleId;
548
+ style.textContent = `
549
+ [data-codingns-translation-inline] > div:not([data-codingns-translation-action]){display:contents}
550
+ [data-codingns-translation-inline] > div:not([data-codingns-translation-action]) > p:last-child{display:inline;margin:0}
551
+ `;
552
+ document.head.appendChild(style);
553
+ return () => style.remove();
554
+ }
555
+ function readEventSource(services, sessionId) {
556
+ const sessions = services.sessions;
557
+ try {
558
+ return sessions?.binding?.(sessionId)?.eventSource;
559
+ }
560
+ catch {
561
+ return undefined;
562
+ }
563
+ }
564
+ function findTranslationRecordForText(snapshot, text, outputKind, turn, step) {
565
+ const entries = snapshot.entries ?? [];
566
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
567
+ const event = readEntryEvent(entries[index]);
568
+ if (event === undefined || !event.type.startsWith('assistant/'))
569
+ continue;
570
+ const eventData = asRecord(event.data);
571
+ if (turn !== undefined && typeof eventData?.turn === 'number' && eventData.turn !== turn)
572
+ continue;
573
+ if (step !== undefined && typeof eventData?.step === 'number' && eventData.step !== step)
574
+ continue;
575
+ const candidates = [];
576
+ collectRecordCandidates(event.data, candidates, new Set());
577
+ for (const candidate of candidates) {
578
+ const record = readRecord(candidate);
579
+ if (record === undefined || record.translated !== text)
580
+ continue;
581
+ if (outputKind !== undefined && record.outputKind !== undefined && record.outputKind !== outputKind)
582
+ continue;
583
+ return record;
584
+ }
585
+ }
586
+ return undefined;
587
+ }
588
+ function collectRecordCandidates(value, candidates, seen, depth = 0) {
589
+ if (Array.isArray(value)) {
590
+ for (const item of value)
591
+ collectRecordCandidates(item, candidates, seen, depth + 1);
592
+ return;
593
+ }
594
+ const record = asRecord(value);
595
+ if (record === undefined || seen.has(record) || depth > 7)
596
+ return;
597
+ seen.add(record);
598
+ if (readRecord(record) !== undefined || asRecord(record.codingnsTranslation) !== undefined)
599
+ candidates.push(record);
600
+ for (const key of ['codingnsTranslation', 'source', 'message', 'content', 'stream', 'chunk', 'block', 'data']) {
601
+ if (record[key] !== undefined)
602
+ collectRecordCandidates(record[key], candidates, seen, depth + 1);
603
+ }
604
+ }
605
+ function readEntryEvent(value) {
606
+ const record = asRecord(value);
607
+ const event = asRecord(record?.event);
608
+ return event ?? (typeof record?.type === 'string' ? record : undefined);
609
+ }
610
+ function readRecord(value) {
611
+ const record = asRecord(value);
612
+ const candidate = asRecord(record?.codingnsTranslation) ?? record;
613
+ if (candidate?.original === undefined || candidate.translated === undefined)
614
+ return undefined;
615
+ if (typeof candidate.original !== 'string' || typeof candidate.translated !== 'string')
616
+ return undefined;
617
+ const targetLanguage = readLanguage(candidate.targetLanguage);
618
+ if (targetLanguage === undefined)
619
+ return undefined;
620
+ const model = readModel(candidate.model);
621
+ const outputKind = candidate.outputKind === 'assistant' || candidate.outputKind === 'reasoning' ? candidate.outputKind : undefined;
622
+ return {
623
+ original: candidate.original,
624
+ translated: candidate.translated,
625
+ ...(candidate.sourceLanguage === 'zh' || candidate.sourceLanguage === 'en' || candidate.sourceLanguage === 'fr' || candidate.sourceLanguage === 'de' || candidate.sourceLanguage === 'ja' ? { sourceLanguage: candidate.sourceLanguage } : {}),
626
+ targetLanguage,
627
+ bilingual: candidate.bilingual === true,
628
+ ...(outputKind === undefined ? {} : { outputKind }),
629
+ ...(candidate.alreadyTargetLanguage === true ? { alreadyTargetLanguage: true } : {}),
630
+ ...(model === undefined ? {} : { model }),
631
+ ...(candidate.failed === true ? { failed: true } : {}),
632
+ };
633
+ }
634
+ function readModel(value) {
635
+ const record = asRecord(value);
636
+ if (typeof record?.provider !== 'string' || typeof record.model !== 'string' || record.provider.trim() === '' || record.model.trim() === '')
637
+ return undefined;
638
+ return { provider: record.provider, model: record.model };
639
+ }
640
+ function readInputRecord(value) {
641
+ const message = asRecord(value);
642
+ const source = asRecord(message?.source);
643
+ const candidate = asRecord(source?.codingnsTranslationInput);
644
+ if (candidate?.original === undefined || candidate.translated === undefined)
645
+ return undefined;
646
+ if (typeof candidate.original !== 'string' || typeof candidate.translated !== 'string')
647
+ return undefined;
648
+ const targetLanguage = candidate.targetLanguage;
649
+ if (targetLanguage !== 'en' && targetLanguage !== 'fr' && targetLanguage !== 'de' && targetLanguage !== 'ja')
650
+ return undefined;
651
+ const model = readModel(candidate.model);
652
+ return {
653
+ original: candidate.original,
654
+ translated: candidate.translated,
655
+ ...(candidate.sourceLanguage === 'zh' || candidate.sourceLanguage === 'en' || candidate.sourceLanguage === 'fr' || candidate.sourceLanguage === 'de' || candidate.sourceLanguage === 'ja' ? { sourceLanguage: candidate.sourceLanguage } : {}),
656
+ targetLanguage,
657
+ ...(candidate.alreadyTargetLanguage === true ? { alreadyTargetLanguage: true } : {}),
658
+ ...(model === undefined ? {} : { model }),
659
+ ...(candidate.failed === true ? { failed: true } : {}),
660
+ };
661
+ }
662
+ function inputRecordForAction(record) {
663
+ return {
664
+ original: record.original,
665
+ translated: record.translated,
666
+ ...(record.sourceLanguage === undefined ? {} : { sourceLanguage: record.sourceLanguage }),
667
+ targetLanguage: record.targetLanguage,
668
+ bilingual: false,
669
+ ...(record.alreadyTargetLanguage === true ? { alreadyTargetLanguage: true } : {}),
670
+ ...(record.model === undefined ? {} : { model: record.model }),
671
+ ...(record.failed === true ? { failed: true } : {}),
672
+ };
673
+ }
674
+ function readLanguage(value) {
675
+ return value === 'zh' || value === 'en' || value === 'fr' || value === 'de' || value === 'ja' ? value : undefined;
676
+ }
677
+ function formatModel(model, t) {
678
+ return model === undefined ? t('translation.modelUnknown') : `${model.provider} / ${model.model}`;
679
+ }
680
+ function safeDomId(value) {
681
+ return encodeURIComponent(value).replace(/%/gu, '-');
682
+ }
683
+ function asRecord(value) {
684
+ return typeof value === 'object' && value !== null && !Array.isArray(value) ? value : undefined;
685
+ }
686
+ //# sourceMappingURL=session-translation-chat.js.map