@jingyi0605/codingns4dsh 0.2.1-beta.7 → 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 (301) hide show
  1. package/README.en.md +3 -3
  2. package/README.md +9 -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 +6 -2
  12. package/data/build/dist/client/avatar/desktop-view.d.ts.map +1 -1
  13. package/data/build/dist/client/avatar/desktop-view.js +48 -13
  14. package/data/build/dist/client/avatar/desktop-view.js.map +1 -1
  15. package/data/build/dist/client/avatar/floating.d.ts +5 -2
  16. package/data/build/dist/client/avatar/floating.d.ts.map +1 -1
  17. package/data/build/dist/client/avatar/floating.js +16 -5
  18. package/data/build/dist/client/avatar/floating.js.map +1 -1
  19. package/data/build/dist/client/avatar/notification-bubble.d.ts +24 -5
  20. package/data/build/dist/client/avatar/notification-bubble.d.ts.map +1 -1
  21. package/data/build/dist/client/avatar/notification-bubble.js +253 -66
  22. package/data/build/dist/client/avatar/notification-bubble.js.map +1 -1
  23. package/data/build/dist/client/avatar/notification-surface.d.ts +16 -0
  24. package/data/build/dist/client/avatar/notification-surface.d.ts.map +1 -0
  25. package/data/build/dist/client/avatar/notification-surface.js +54 -0
  26. package/data/build/dist/client/avatar/notification-surface.js.map +1 -0
  27. package/data/build/dist/client/avatar/reaction-badge.d.ts.map +1 -1
  28. package/data/build/dist/client/avatar/reaction-badge.js +11 -2
  29. package/data/build/dist/client/avatar/reaction-badge.js.map +1 -1
  30. package/data/build/dist/client/bundle.js +2628 -566
  31. package/data/build/dist/client/bundle.js.map +1 -1
  32. package/data/build/dist/client/cli-slots.d.ts.map +1 -1
  33. package/data/build/dist/client/cli-slots.js +26 -2
  34. package/data/build/dist/client/cli-slots.js.map +1 -1
  35. package/data/build/dist/client/client.assistant-notifications.js.map +1 -1
  36. package/data/build/dist/client/client.assistant-workbench.js +235 -109
  37. package/data/build/dist/client/client.assistant-workbench.js.map +1 -1
  38. package/data/build/dist/client/client.delegate-command.js +6 -6
  39. package/data/build/dist/client/client.delegate-command.js.map +1 -1
  40. package/data/build/dist/client/client.manager.js +46 -9
  41. package/data/build/dist/client/client.manager.js.map +1 -1
  42. package/data/build/dist/client/client.native-workspace-store.js +39 -3
  43. package/data/build/dist/client/client.native-workspace-store.js.map +1 -1
  44. package/data/build/dist/client/client.session-translation.js +54 -0
  45. package/data/build/dist/client/client.session-translation.js.map +1 -0
  46. package/data/build/dist/client/client.theme.js +2 -0
  47. package/data/build/dist/client/client.theme.js.map +1 -1
  48. package/data/build/dist/client/client.transport.js +123 -11
  49. package/data/build/dist/client/client.transport.js.map +1 -1
  50. package/data/build/dist/client/desktop-assistant.js +57927 -387
  51. package/data/build/dist/client/features/assistant-conversation-message.d.ts +2 -1
  52. package/data/build/dist/client/features/assistant-conversation-message.d.ts.map +1 -1
  53. package/data/build/dist/client/features/assistant-conversation-message.js +59 -24
  54. package/data/build/dist/client/features/assistant-conversation-message.js.map +1 -1
  55. package/data/build/dist/client/features/assistant-notification-store.d.ts +8 -2
  56. package/data/build/dist/client/features/assistant-notification-store.d.ts.map +1 -1
  57. package/data/build/dist/client/features/assistant-notification-store.js +45 -1
  58. package/data/build/dist/client/features/assistant-notification-store.js.map +1 -1
  59. package/data/build/dist/client/features/assistant-record-style.js +11 -4
  60. package/data/build/dist/client/features/assistant-record-style.js.map +1 -1
  61. package/data/build/dist/client/features/assistant-workbench.d.ts +4 -0
  62. package/data/build/dist/client/features/assistant-workbench.d.ts.map +1 -1
  63. package/data/build/dist/client/features/assistant-workbench.js +27 -1
  64. package/data/build/dist/client/features/assistant-workbench.js.map +1 -1
  65. package/data/build/dist/client/features/global-voice-assistant.d.ts.map +1 -1
  66. package/data/build/dist/client/features/global-voice-assistant.js +46 -8
  67. package/data/build/dist/client/features/global-voice-assistant.js.map +1 -1
  68. package/data/build/dist/client/features/index.d.ts +1 -0
  69. package/data/build/dist/client/features/index.d.ts.map +1 -1
  70. package/data/build/dist/client/features/index.js +3 -0
  71. package/data/build/dist/client/features/index.js.map +1 -1
  72. package/data/build/dist/client/features/peer-host.d.ts.map +1 -1
  73. package/data/build/dist/client/features/peer-host.js +130 -17
  74. package/data/build/dist/client/features/peer-host.js.map +1 -1
  75. package/data/build/dist/client/features/session-translation.d.ts +3 -0
  76. package/data/build/dist/client/features/session-translation.d.ts.map +1 -0
  77. package/data/build/dist/client/features/session-translation.js +116 -0
  78. package/data/build/dist/client/features/session-translation.js.map +1 -0
  79. package/data/build/dist/client/index.d.ts.map +1 -1
  80. package/data/build/dist/client/index.js +6 -1
  81. package/data/build/dist/client/index.js.map +1 -1
  82. package/data/build/dist/client/locales/assistantWorkbench.d.ts +17 -3
  83. package/data/build/dist/client/locales/assistantWorkbench.d.ts.map +1 -1
  84. package/data/build/dist/client/locales/assistantWorkbench.js +15 -11
  85. package/data/build/dist/client/locales/assistantWorkbench.js.map +1 -1
  86. package/data/build/dist/client/locales/panels.d.ts.map +1 -1
  87. package/data/build/dist/client/locales/panels.js +62 -0
  88. package/data/build/dist/client/locales/panels.js.map +1 -1
  89. package/data/build/dist/client/peer-host-native-projection.d.ts +4 -0
  90. package/data/build/dist/client/peer-host-native-projection.d.ts.map +1 -1
  91. package/data/build/dist/client/peer-host-native-projection.js +40 -1
  92. package/data/build/dist/client/peer-host-native-projection.js.map +1 -1
  93. package/data/build/dist/client/peer-host-remote-events.d.ts +14 -0
  94. package/data/build/dist/client/peer-host-remote-events.d.ts.map +1 -1
  95. package/data/build/dist/client/peer-host-remote-events.js +92 -3
  96. package/data/build/dist/client/peer-host-remote-events.js.map +1 -1
  97. package/data/build/dist/client/session-file-links.d.ts +11 -0
  98. package/data/build/dist/client/session-file-links.d.ts.map +1 -0
  99. package/data/build/dist/client/{peer-host-file-links.js → session-file-links.js} +13 -3
  100. package/data/build/dist/client/session-file-links.js.map +1 -0
  101. package/data/build/dist/client/session-translation-chat.d.ts +6 -0
  102. package/data/build/dist/client/session-translation-chat.d.ts.map +1 -0
  103. package/data/build/dist/client/session-translation-chat.js +686 -0
  104. package/data/build/dist/client/session-translation-chat.js.map +1 -0
  105. package/data/build/dist/client/style.css +2561 -0
  106. package/data/build/dist/client/subscription-slot.d.ts.map +1 -1
  107. package/data/build/dist/client/subscription-slot.js +5 -1
  108. package/data/build/dist/client/subscription-slot.js.map +1 -1
  109. package/data/build/dist/client/theme.d.ts +2 -0
  110. package/data/build/dist/client/theme.d.ts.map +1 -1
  111. package/data/build/dist/client/theme.js +3 -0
  112. package/data/build/dist/client/theme.js.map +1 -1
  113. package/data/build/dist/client/workspace-session-visibility-dom.d.ts.map +1 -1
  114. package/data/build/dist/client/workspace-session-visibility-dom.js +43 -15
  115. package/data/build/dist/client/workspace-session-visibility-dom.js.map +1 -1
  116. package/data/build/dist/dsh-capabilities/client/assistant-session-navigation-adapter.d.ts +9 -1
  117. package/data/build/dist/dsh-capabilities/client/assistant-session-navigation-adapter.d.ts.map +1 -1
  118. package/data/build/dist/dsh-capabilities/client/assistant-session-navigation-adapter.js +65 -0
  119. package/data/build/dist/dsh-capabilities/client/assistant-session-navigation-adapter.js.map +1 -1
  120. package/data/build/dist/host/cli-adapters/acp-cli-driver.d.ts +21 -0
  121. package/data/build/dist/host/cli-adapters/acp-cli-driver.d.ts.map +1 -1
  122. package/data/build/dist/host/cli-adapters/acp-cli-driver.js +100 -3
  123. package/data/build/dist/host/cli-adapters/acp-cli-driver.js.map +1 -1
  124. package/data/build/dist/host/cli-adapters/codex-driver.d.ts +11 -1
  125. package/data/build/dist/host/cli-adapters/codex-driver.d.ts.map +1 -1
  126. package/data/build/dist/host/cli-adapters/codex-driver.js +80 -14
  127. package/data/build/dist/host/cli-adapters/codex-driver.js.map +1 -1
  128. package/data/build/dist/host/cli-adapters/codex-file-citations.d.ts +13 -0
  129. package/data/build/dist/host/cli-adapters/codex-file-citations.d.ts.map +1 -0
  130. package/data/build/dist/host/cli-adapters/codex-file-citations.js +271 -0
  131. package/data/build/dist/host/cli-adapters/codex-file-citations.js.map +1 -0
  132. package/data/build/dist/host/cli-adapters/command-code-acp-loader.d.ts +8 -1
  133. package/data/build/dist/host/cli-adapters/command-code-acp-loader.d.ts.map +1 -1
  134. package/data/build/dist/host/cli-adapters/command-code-acp-loader.js +45 -2
  135. package/data/build/dist/host/cli-adapters/command-code-acp-loader.js.map +1 -1
  136. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +8 -2
  137. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts.map +1 -1
  138. package/data/build/dist/host/cli-adapters/command-code-driver.js +32 -3
  139. package/data/build/dist/host/cli-adapters/command-code-driver.js.map +1 -1
  140. package/data/build/dist/host/cli-adapters/command-code-history.d.ts +27 -0
  141. package/data/build/dist/host/cli-adapters/command-code-history.d.ts.map +1 -1
  142. package/data/build/dist/host/cli-adapters/command-code-history.js +131 -1
  143. package/data/build/dist/host/cli-adapters/command-code-history.js.map +1 -1
  144. package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.js +9 -9
  145. package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.js.map +1 -1
  146. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +13 -1
  147. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts.map +1 -1
  148. package/data/build/dist/host/cli-adapters/dsh-message-projector.js +95 -12
  149. package/data/build/dist/host/cli-adapters/dsh-message-projector.js.map +1 -1
  150. package/data/build/dist/host/cli-adapters/feature.d.ts.map +1 -1
  151. package/data/build/dist/host/cli-adapters/feature.js +287 -19
  152. package/data/build/dist/host/cli-adapters/feature.js.map +1 -1
  153. package/data/build/dist/host/cli-adapters/grok-driver.d.ts.map +1 -1
  154. package/data/build/dist/host/cli-adapters/grok-driver.js +108 -47
  155. package/data/build/dist/host/cli-adapters/grok-driver.js.map +1 -1
  156. package/data/build/dist/host/cli-adapters/legacy-session-settings.d.ts.map +1 -1
  157. package/data/build/dist/host/cli-adapters/legacy-session-settings.js +7 -2
  158. package/data/build/dist/host/cli-adapters/legacy-session-settings.js.map +1 -1
  159. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +4 -2
  160. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts.map +1 -1
  161. package/data/build/dist/host/cli-adapters/opencode-driver.js +78 -31
  162. package/data/build/dist/host/cli-adapters/opencode-driver.js.map +1 -1
  163. package/data/build/dist/host/cli-adapters/opencode-v2-protocol.d.ts +1 -1
  164. package/data/build/dist/host/cli-adapters/opencode-v2-protocol.d.ts.map +1 -1
  165. package/data/build/dist/host/cli-adapters/opencode-v2-protocol.js +83 -9
  166. package/data/build/dist/host/cli-adapters/opencode-v2-protocol.js.map +1 -1
  167. package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +5 -1
  168. package/data/build/dist/host/cli-adapters/provider-subscription.d.ts.map +1 -1
  169. package/data/build/dist/host/cli-adapters/provider-subscription.js +90 -25
  170. package/data/build/dist/host/cli-adapters/provider-subscription.js.map +1 -1
  171. package/data/build/dist/host/cli-adapters/registry.d.ts.map +1 -1
  172. package/data/build/dist/host/cli-adapters/registry.js +22 -5
  173. package/data/build/dist/host/cli-adapters/registry.js.map +1 -1
  174. package/data/build/dist/host/cli-adapters/session-store.d.ts +1 -0
  175. package/data/build/dist/host/cli-adapters/session-store.d.ts.map +1 -1
  176. package/data/build/dist/host/cli-adapters/session-store.js +3 -0
  177. package/data/build/dist/host/cli-adapters/session-store.js.map +1 -1
  178. package/data/build/dist/host/cli-bridge/bridge-server.js +2 -2
  179. package/data/build/dist/host/cli-bridge/bridge-server.js.map +1 -1
  180. package/data/build/dist/host/cli-bridge/command-code-mod.js +21 -21
  181. package/data/build/dist/host/cli-bridge/command-code-mod.js.map +1 -1
  182. package/data/build/dist/host/cli-bridge/dispatch.js +19 -19
  183. package/data/build/dist/host/cli-bridge/dispatch.js.map +1 -1
  184. package/data/build/dist/host/cli-bridge/injections.d.ts +32 -0
  185. package/data/build/dist/host/cli-bridge/injections.d.ts.map +1 -1
  186. package/data/build/dist/host/cli-bridge/injections.js +137 -1
  187. package/data/build/dist/host/cli-bridge/injections.js.map +1 -1
  188. package/data/build/dist/host/cli-bridge/mcp-stdio-entry.js +20 -20
  189. package/data/build/dist/host/cli-bridge/mcp-stdio-entry.js.map +1 -1
  190. package/data/build/dist/host/desktop-assistant/controller.d.ts +3 -0
  191. package/data/build/dist/host/desktop-assistant/controller.d.ts.map +1 -1
  192. package/data/build/dist/host/desktop-assistant/controller.js +49 -4
  193. package/data/build/dist/host/desktop-assistant/controller.js.map +1 -1
  194. package/data/build/dist/host/desktop-assistant/mac-agent.d.ts.map +1 -1
  195. package/data/build/dist/host/desktop-assistant/mac-agent.js +15 -3
  196. package/data/build/dist/host/desktop-assistant/mac-agent.js.map +1 -1
  197. package/data/build/dist/host/desktop-assistant/win-agent.d.ts.map +1 -1
  198. package/data/build/dist/host/desktop-assistant/win-agent.js +21 -3
  199. package/data/build/dist/host/desktop-assistant/win-agent.js.map +1 -1
  200. package/data/build/dist/host/features/assistant-management-tools.d.ts +2 -1
  201. package/data/build/dist/host/features/assistant-management-tools.d.ts.map +1 -1
  202. package/data/build/dist/host/features/assistant-management-tools.js +10 -3
  203. package/data/build/dist/host/features/assistant-management-tools.js.map +1 -1
  204. package/data/build/dist/host/features/assistant-notification-events.js +1 -1
  205. package/data/build/dist/host/features/assistant-notification-events.js.map +1 -1
  206. package/data/build/dist/host/features/assistant-notifications.d.ts +8 -1
  207. package/data/build/dist/host/features/assistant-notifications.d.ts.map +1 -1
  208. package/data/build/dist/host/features/assistant-notifications.js +114 -12
  209. package/data/build/dist/host/features/assistant-notifications.js.map +1 -1
  210. package/data/build/dist/host/features/global-voice-rpc.d.ts +2 -2
  211. package/data/build/dist/host/features/global-voice-rpc.d.ts.map +1 -1
  212. package/data/build/dist/host/features/global-voice-rpc.js +89 -2
  213. package/data/build/dist/host/features/global-voice-rpc.js.map +1 -1
  214. package/data/build/dist/host/features/index.d.ts +1 -0
  215. package/data/build/dist/host/features/index.d.ts.map +1 -1
  216. package/data/build/dist/host/features/index.js +3 -0
  217. package/data/build/dist/host/features/index.js.map +1 -1
  218. package/data/build/dist/host/features/peer-host.d.ts.map +1 -1
  219. package/data/build/dist/host/features/peer-host.js +41 -5
  220. package/data/build/dist/host/features/peer-host.js.map +1 -1
  221. package/data/build/dist/host/features/session-translation-service.d.ts +31 -0
  222. package/data/build/dist/host/features/session-translation-service.d.ts.map +1 -0
  223. package/data/build/dist/host/features/session-translation-service.js +451 -0
  224. package/data/build/dist/host/features/session-translation-service.js.map +1 -0
  225. package/data/build/dist/host/features/session-translation.d.ts +5 -0
  226. package/data/build/dist/host/features/session-translation.d.ts.map +1 -0
  227. package/data/build/dist/host/features/session-translation.js +37 -0
  228. package/data/build/dist/host/features/session-translation.js.map +1 -0
  229. package/data/build/dist/host/features/types.d.ts +6 -1
  230. package/data/build/dist/host/features/types.d.ts.map +1 -1
  231. package/data/build/dist/host/image-artifact-store.d.ts +21 -0
  232. package/data/build/dist/host/image-artifact-store.d.ts.map +1 -0
  233. package/data/build/dist/host/image-artifact-store.js +136 -0
  234. package/data/build/dist/host/image-artifact-store.js.map +1 -0
  235. package/data/build/dist/host/index.d.ts.map +1 -1
  236. package/data/build/dist/host/index.js +3 -0
  237. package/data/build/dist/host/index.js.map +1 -1
  238. package/data/build/dist/host/modules/peer-host/peer-host-native-dispatch.d.ts +7 -1
  239. package/data/build/dist/host/modules/peer-host/peer-host-native-dispatch.d.ts.map +1 -1
  240. package/data/build/dist/host/modules/peer-host/peer-host-native-dispatch.js +23 -4
  241. package/data/build/dist/host/modules/peer-host/peer-host-native-dispatch.js.map +1 -1
  242. package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.d.ts +1 -1
  243. package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.d.ts.map +1 -1
  244. package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.js +2 -0
  245. package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.js.map +1 -1
  246. package/data/build/dist/host/native-session-bridge.d.ts +16 -1
  247. package/data/build/dist/host/native-session-bridge.d.ts.map +1 -1
  248. package/data/build/dist/host/native-session-bridge.js +69 -9
  249. package/data/build/dist/host/native-session-bridge.js.map +1 -1
  250. package/data/build/dist/host/rpc.js +4 -4
  251. package/data/build/dist/host/rpc.js.map +1 -1
  252. package/data/build/dist/host/session-changes.d.ts.map +1 -1
  253. package/data/build/dist/host/session-changes.js +196 -13
  254. package/data/build/dist/host/session-changes.js.map +1 -1
  255. package/data/build/dist/host/settings.d.ts.map +1 -1
  256. package/data/build/dist/host/settings.js +8 -0
  257. package/data/build/dist/host/settings.js.map +1 -1
  258. package/data/build/dist/shared/assistant-message-timeline.d.ts +11 -0
  259. package/data/build/dist/shared/assistant-message-timeline.d.ts.map +1 -1
  260. package/data/build/dist/shared/assistant-message-timeline.js +19 -0
  261. package/data/build/dist/shared/assistant-message-timeline.js.map +1 -1
  262. package/data/build/dist/shared/assistant-notifications.d.ts +35 -1
  263. package/data/build/dist/shared/assistant-notifications.d.ts.map +1 -1
  264. package/data/build/dist/shared/assistant-notifications.js +8 -0
  265. package/data/build/dist/shared/assistant-notifications.js.map +1 -1
  266. package/data/build/dist/shared/assistant-session-messages.d.ts +13 -0
  267. package/data/build/dist/shared/assistant-session-messages.d.ts.map +1 -0
  268. package/data/build/dist/shared/assistant-session-messages.js +155 -0
  269. package/data/build/dist/shared/assistant-session-messages.js.map +1 -0
  270. package/data/build/dist/shared/contracts/assistant.d.ts +13 -0
  271. package/data/build/dist/shared/contracts/assistant.d.ts.map +1 -1
  272. package/data/build/dist/shared/contracts/cli-adapter.d.ts +25 -2
  273. package/data/build/dist/shared/contracts/cli-adapter.d.ts.map +1 -1
  274. package/data/build/dist/shared/contracts/config.d.ts +6 -0
  275. package/data/build/dist/shared/contracts/config.d.ts.map +1 -1
  276. package/data/build/dist/shared/contracts/config.js +3 -0
  277. package/data/build/dist/shared/contracts/config.js.map +1 -1
  278. package/data/build/dist/shared/contracts/session-translation.d.ts +58 -0
  279. package/data/build/dist/shared/contracts/session-translation.d.ts.map +1 -0
  280. package/data/build/dist/shared/contracts/session-translation.js +23 -0
  281. package/data/build/dist/shared/contracts/session-translation.js.map +1 -0
  282. package/data/build/dist/shared/contracts/version.d.ts +1 -1
  283. package/data/build/dist/shared/contracts/version.js +1 -1
  284. package/data/build/dist/shared/delegation-carrier.js +6 -6
  285. package/data/build/dist/shared/delegation-carrier.js.map +1 -1
  286. package/data/build/dist/shared/desktop-assistant.d.ts +10 -3
  287. package/data/build/dist/shared/desktop-assistant.d.ts.map +1 -1
  288. package/data/build/dist/shared/desktop-assistant.js +46 -14
  289. package/data/build/dist/shared/desktop-assistant.js.map +1 -1
  290. package/data/build/dist/shared/index.d.ts +2 -1
  291. package/data/build/dist/shared/index.d.ts.map +1 -1
  292. package/data/build/dist/shared/index.js +2 -1
  293. package/data/build/dist/shared/index.js.map +1 -1
  294. package/package.json +17 -15
  295. package/profile/README.md +2 -2
  296. package/profile/package.json +2 -2
  297. package/profile/version.json +1 -1
  298. package/version.json +1 -1
  299. package/data/build/dist/client/peer-host-file-links.d.ts +0 -8
  300. package/data/build/dist/client/peer-host-file-links.d.ts.map +0 -1
  301. package/data/build/dist/client/peer-host-file-links.js.map +0 -1
@@ -1569,6 +1569,28 @@ window.__ModuleLoader__.load({
1569
1569
  });
1570
1570
  return parts;
1571
1571
  }
1572
+ /** 将同一正文位置连续发生的工具归入一个 DSH 风格 step;正文仍保持原有穿插位置。 */
1573
+ function assistantMessageStepTimeline(text, calls = []) {
1574
+ const parts = assistantMessageTimeline(text, calls);
1575
+ const grouped = [];
1576
+ for (const part of parts) {
1577
+ if (part.kind === "text") {
1578
+ grouped.push(part);
1579
+ continue;
1580
+ }
1581
+ const previous = grouped.at(-1);
1582
+ if (previous?.kind === "tool-step") grouped[grouped.length - 1] = {
1583
+ ...previous,
1584
+ calls: [...previous.calls, part.call]
1585
+ };
1586
+ else grouped.push({
1587
+ kind: "tool-step",
1588
+ key: `tool-step-${part.call.id}`,
1589
+ calls: [part.call]
1590
+ });
1591
+ }
1592
+ return grouped;
1593
+ }
1572
1594
  //#endregion
1573
1595
  //#region src/client/features/assistant-record-style.ts
1574
1596
  /** 工具记录和通话记录共享交互反馈,选择器只作用于这两类组件。 */
@@ -1594,14 +1616,21 @@ window.__ModuleLoader__.load({
1594
1616
  }, (0, react.createElement)("path", { d: "m9 5 7 7-7 7" }));
1595
1617
  }
1596
1618
  const recordCss = `
1597
- .codingns-assistant-tool-summary{list-style:none;cursor:pointer;border-radius:9px}
1619
+ .codingns-assistant-tool-summary{list-style:none;cursor:pointer}
1598
1620
  .codingns-assistant-tool-summary::-webkit-details-marker{display:none}
1599
- .codingns-assistant-tool-summary:hover,.codingns-assistant-record-button:hover{background:${require_theme.dshThemeColor.hoverBackground}!important}
1600
- .codingns-assistant-tool-summary:active,.codingns-assistant-record-button:active{background:${require_theme.dshThemeColor.activeBackground}!important}
1621
+ .codingns-assistant-tool-summary:hover,.codingns-assistant-record-button:hover{color:${require_theme.dshThemeColor.labelPrimary}!important}
1622
+ .codingns-assistant-tool-summary:active,.codingns-assistant-record-button:active{color:${require_theme.dshThemeColor.labelPrimary}!important}
1601
1623
  .codingns-assistant-tool-summary:focus-visible,.codingns-assistant-record-button:focus-visible{outline:2px solid ${require_theme.dshThemeColor.accent};outline-offset:2px}
1602
- .codingns-assistant-tool-details[open]>.codingns-assistant-tool-summary{border-radius:9px 9px 0 0}
1603
1624
  .codingns-assistant-tool-details[open] .codingns-assistant-tool-name{white-space:normal!important;overflow-wrap:anywhere}
1604
1625
  .codingns-assistant-tool-details[open]>.codingns-assistant-tool-summary .codingns-assistant-record-chevron{transform:rotate(90deg)}
1626
+ .codingns-assistant-tool-step-summary{list-style:none;cursor:pointer}
1627
+ .codingns-assistant-tool-step-summary::-webkit-details-marker{display:none}
1628
+ .codingns-assistant-tool-step-summary:hover{color:${require_theme.dshThemeColor.labelPrimary}!important}
1629
+ .codingns-assistant-tool-step-summary:active{color:${require_theme.dshThemeColor.labelPrimary}!important}
1630
+ .codingns-assistant-tool-step-summary:focus-visible{outline:2px solid ${require_theme.dshThemeColor.accent};outline-offset:2px}
1631
+ .codingns-assistant-tool-step-details[open]>.codingns-assistant-tool-step-summary .codingns-assistant-step-chevron{transform:rotate(180deg)}
1632
+ .codingns-assistant-tool-step-details[open] .codingns-assistant-tool-step-body{padding-inline:4px}
1633
+ .codingns-assistant-tool-step-details[open] .codingns-assistant-tool-step-preview{white-space:normal;overflow-wrap:anywhere}
1605
1634
  .codingns-assistant-voice-dialog[open]{display:flex;flex-direction:column}
1606
1635
  .codingns-assistant-voice-dialog::backdrop{background:${require_theme.dshThemeColor.overlay}}
1607
1636
  `;
@@ -1660,9 +1689,9 @@ window.__ModuleLoader__.load({
1660
1689
  }
1661
1690
  /** 文字聊天、实时字幕与通话历史共用同一时间线,不额外生成工具汇总区。 */
1662
1691
  function AssistantMessageContentView({ text, calls, t }) {
1663
- return (0, react.createElement)(react.Fragment, null, ...assistantMessageTimeline(text, calls).map((part) => part.kind === "tool" ? (0, react.createElement)(AssistantToolCallsView, {
1692
+ return (0, react.createElement)(react.Fragment, null, ...assistantMessageStepTimeline(text, calls).map((part) => part.kind === "tool-step" ? (0, react.createElement)(AssistantToolStepView, {
1664
1693
  key: part.key,
1665
- calls: [part.call],
1694
+ calls: part.calls,
1666
1695
  t
1667
1696
  }) : (0, react.createElement)("div", {
1668
1697
  key: part.key,
@@ -1674,14 +1703,15 @@ window.__ModuleLoader__.load({
1674
1703
  }, part.text)));
1675
1704
  }
1676
1705
  /** 工具信息单独展示,不拼入消息正文,字幕与语音合成都只使用原始 text。 */
1677
- function AssistantToolCallsView({ calls, t }) {
1706
+ function AssistantToolCallsView({ calls, t, compact = false }) {
1678
1707
  if (!calls?.length) return null;
1679
1708
  return (0, react.createElement)("div", {
1680
1709
  "data-codingns-assistant-tools": true,
1710
+ "data-codingns-assistant-tools-compact": compact,
1681
1711
  style: {
1682
1712
  display: "grid",
1683
- gap: 6,
1684
- marginBlock: 6,
1713
+ gap: 2,
1714
+ marginBlock: compact ? 0 : 6,
1685
1715
  textAlign: "left",
1686
1716
  whiteSpace: "normal",
1687
1717
  minWidth: 0
@@ -1689,26 +1719,113 @@ window.__ModuleLoader__.load({
1689
1719
  }, (0, react.createElement)(AssistantRecordStyle), ...calls.map((call) => (0, react.createElement)(AssistantToolCallView, {
1690
1720
  key: call.id,
1691
1721
  call,
1692
- t
1722
+ t,
1723
+ compact
1693
1724
  })));
1694
1725
  }
1726
+ /** 一个 step 只显示摘要;展开后才加载其中的工具调用明细。 */
1727
+ function AssistantToolStepView({ calls, t }) {
1728
+ if (calls.length === 0) return null;
1729
+ const running = calls.some((call) => call.state === "running");
1730
+ const failed = calls.some((call) => call.state === "failed");
1731
+ const status = running ? "running" : failed ? "failed" : calls.every((call) => call.state === "cancelled") ? "cancelled" : "completed";
1732
+ const latestCall = calls[calls.length - 1];
1733
+ const latestArguments = assistantToolArgumentPreview(latestCall, t);
1734
+ const summary = calls.map((call) => assistantToolStepPreview(call, t)).filter((value, index, all) => all.indexOf(value) === index).join(t("awb.call.separator"));
1735
+ return (0, react.createElement)("div", {
1736
+ "data-codingns-assistant-tool-step": true,
1737
+ style: {
1738
+ marginBlock: 6,
1739
+ textAlign: "left",
1740
+ whiteSpace: "normal",
1741
+ minWidth: 0,
1742
+ color: require_theme.dshThemeColor.labelSecondary,
1743
+ fontSize: require_theme.uiFontSize(13),
1744
+ lineHeight: 1.6
1745
+ }
1746
+ }, (0, react.createElement)(AssistantRecordStyle), (0, react.createElement)("details", { className: "codingns-assistant-tool-step-details" }, (0, react.createElement)("summary", {
1747
+ className: "codingns-assistant-tool-step-summary",
1748
+ title: t("awb.call.stepDetails"),
1749
+ "aria-label": `${t("awb.call.step")} · ${summary} · ${t(`awb.call.tool.${status}`)}`,
1750
+ style: {
1751
+ display: "flex",
1752
+ alignItems: "center",
1753
+ gap: 8,
1754
+ minWidth: 0,
1755
+ padding: "2px 0",
1756
+ color: require_theme.dshThemeColor.labelSecondary
1757
+ }
1758
+ }, (0, react.createElement)(AssistantStepChevron), (0, react.createElement)("span", {
1759
+ className: "codingns-assistant-tool-step-preview",
1760
+ "data-latest-tool": latestCall.name,
1761
+ title: latestCall.name,
1762
+ style: {
1763
+ minWidth: 0,
1764
+ overflow: "hidden",
1765
+ textOverflow: "ellipsis",
1766
+ whiteSpace: "nowrap"
1767
+ }
1768
+ }, summary, " · ", (0, react.createElement)("code", { style: {
1769
+ color: require_theme.dshThemeColor.labelPrimary,
1770
+ fontFamily: require_theme.dshThemeColor.codeFont,
1771
+ fontSize: "0.92em"
1772
+ } }, latestCall.name), latestArguments ? ` · ${latestArguments}` : "")), (0, react.createElement)("div", {
1773
+ className: "codingns-assistant-tool-step-body",
1774
+ style: {
1775
+ display: "grid",
1776
+ gap: 1,
1777
+ padding: "4px 0 5px 21px"
1778
+ }
1779
+ }, (0, react.createElement)(AssistantToolCallsView, {
1780
+ calls,
1781
+ t,
1782
+ compact: true
1783
+ }))));
1784
+ }
1785
+ function assistantToolStepPreview(call, t) {
1786
+ if (call.name.includes("read") || call.kind === "attachment") return t("awb.call.stepRead");
1787
+ if (call.name.includes("search") || call.kind === "web-search") return t("awb.call.stepSearch");
1788
+ if (call.name.includes("workspace") || call.kind === "workspace") return t("awb.call.stepWorkspace");
1789
+ return t("awb.call.stepTool");
1790
+ }
1791
+ function assistantToolArgumentPreview(call, t) {
1792
+ try {
1793
+ const args = JSON.parse(call.arguments);
1794
+ if (args === null || typeof args !== "object" || Array.isArray(args)) return "";
1795
+ const record = args;
1796
+ for (const key of [
1797
+ "query",
1798
+ "queries",
1799
+ "text",
1800
+ "url",
1801
+ "fileName",
1802
+ "filename",
1803
+ "file",
1804
+ "title",
1805
+ "message"
1806
+ ]) {
1807
+ const value = record[key];
1808
+ const preview = typeof value === "string" ? value : Array.isArray(value) ? value.filter((item) => typeof item === "string").join(t("awb.call.separator")) : "";
1809
+ if (preview.trim()) return preview.trim().slice(0, 120);
1810
+ }
1811
+ } catch {}
1812
+ return "";
1813
+ }
1695
1814
  /** 整个标题栏就是展开入口;实时状态更新不替换 details,保留用户的展开状态。 */
1696
- function AssistantToolCallView({ call, t }) {
1815
+ function AssistantToolCallView({ call, t, compact = false }) {
1697
1816
  const label = t(call.kind === "web-search" ? "awb.call.webSearch" : call.kind === "attachment" ? "awb.call.attachment" : "awb.call.workspace");
1698
1817
  const status = t(`awb.call.tool.${call.state}`);
1699
- const color = call.state === "failed" ? require_theme.dshThemeColor.error : call.state === "completed" ? require_theme.dshThemeColor.success : call.state === "running" ? require_theme.dshThemeColor.accent : require_theme.dshThemeColor.labelSecondary;
1700
- const startedAt = new Date(call.startedAt);
1701
- return (0, react.createElement)("article", {
1818
+ const argumentPreview = compact ? assistantToolArgumentPreview(call, t) : "";
1819
+ const rowColor = call.state === "failed" ? require_theme.dshThemeColor.error : require_theme.dshThemeColor.labelSecondary;
1820
+ return (0, react.createElement)("div", {
1821
+ "data-codingns-assistant-tool-row": call.name,
1702
1822
  "data-codingns-assistant-tool": call.name,
1703
1823
  "data-state": call.state,
1704
1824
  style: {
1705
- borderRadius: 10,
1706
- border: `1px solid ${require_theme.dshThemeColor.border}`,
1707
- background: require_theme.dshThemeColor.surfaceSubtle,
1708
- color: require_theme.dshThemeColor.labelPrimary,
1709
- fontSize: require_theme.uiFontSize(12),
1710
- lineHeight: 1.4,
1711
- minWidth: 0
1825
+ minWidth: 0,
1826
+ color: rowColor,
1827
+ fontSize: require_theme.uiFontSize(13),
1828
+ lineHeight: 1.65
1712
1829
  }
1713
1830
  }, (0, react.createElement)("details", { className: "codingns-assistant-tool-details" }, (0, react.createElement)("summary", {
1714
1831
  className: "codingns-assistant-tool-summary",
@@ -1718,98 +1835,53 @@ window.__ModuleLoader__.load({
1718
1835
  display: "flex",
1719
1836
  alignItems: "center",
1720
1837
  gap: 8,
1721
- padding: "8px 10px",
1722
- minWidth: 0
1838
+ minWidth: 0,
1839
+ padding: "1px 0"
1723
1840
  }
1724
- }, (0, react.createElement)("span", { style: {
1725
- display: "grid",
1726
- placeItems: "center",
1727
- flexShrink: 0,
1728
- width: 28,
1729
- height: 28,
1730
- borderRadius: 8,
1731
- color,
1732
- background: `color-mix(in srgb, ${color} 9%, transparent)`
1733
- } }, (0, react.createElement)(AssistantToolIcon, { kind: call.kind })), (0, react.createElement)("span", { style: {
1734
- flex: "1 1 auto",
1735
- minWidth: 0,
1736
- display: "grid",
1737
- gap: 1
1738
- } }, (0, react.createElement)("strong", { style: {
1739
- fontSize: require_theme.uiFontSize(12),
1740
- fontWeight: 600
1741
- } }, label), (0, react.createElement)("span", {
1742
- className: "codingns-assistant-tool-name",
1841
+ }, (0, react.createElement)(AssistantRecordChevron), (0, react.createElement)("span", { style: { flexShrink: 0 } }, label), (0, react.createElement)("span", { style: {
1842
+ color: require_theme.dshThemeColor.labelTertiary,
1843
+ flexShrink: 0
1844
+ } }, "·"), (0, react.createElement)("span", {
1743
1845
  title: call.name,
1744
1846
  style: {
1745
- color: require_theme.dshThemeColor.labelSecondary,
1746
- fontFamily: require_theme.dshThemeColor.codeFont,
1747
- fontSize: require_theme.uiFontSize(11),
1847
+ minWidth: 0,
1748
1848
  overflow: "hidden",
1749
1849
  textOverflow: "ellipsis",
1750
- whiteSpace: "nowrap"
1751
- }
1752
- }, call.name)), (0, react.createElement)("span", { style: {
1753
- display: "grid",
1754
- justifyItems: "end",
1755
- gap: 3,
1756
- flexShrink: 0
1757
- } }, (0, react.createElement)("span", {
1758
- role: "status",
1759
- style: {
1760
- display: "inline-flex",
1761
- alignItems: "center",
1762
- gap: 4,
1763
- color,
1764
- padding: "1px 6px",
1765
- borderRadius: 5,
1766
- background: `color-mix(in srgb, ${color} 9%, transparent)`,
1767
- fontSize: require_theme.uiFontSize(11),
1768
- whiteSpace: "nowrap"
1850
+ whiteSpace: "nowrap",
1851
+ fontFamily: require_theme.dshThemeColor.codeFont
1769
1852
  }
1770
- }, (0, react.createElement)("span", {
1771
- "aria-hidden": true,
1853
+ }, call.name, argumentPreview ? ` · ${argumentPreview}` : "")), (0, react.createElement)("div", {
1854
+ className: "codingns-assistant-tool-body",
1772
1855
  style: {
1773
- width: 4,
1774
- height: 4,
1775
- borderRadius: "50%",
1776
- background: "currentColor"
1856
+ display: "grid",
1857
+ gap: 0,
1858
+ margin: "2px 0 4px 22px",
1859
+ border: `0.5px solid ${require_theme.dshThemeColor.border}`,
1860
+ borderRadius: 6,
1861
+ overflow: "hidden",
1862
+ minWidth: 0
1777
1863
  }
1778
- }), status), (0, react.createElement)("time", {
1779
- dateTime: Number.isNaN(startedAt.valueOf()) ? void 0 : startedAt.toISOString(),
1780
- title: startedAt.toLocaleString(),
1864
+ }, ...[[t("awb.call.arguments"), call.arguments], [t("awb.call.result"), call.result]].map(([heading, value], index) => (0, react.createElement)("div", {
1865
+ key: heading,
1781
1866
  style: {
1782
- color: require_theme.dshThemeColor.labelTertiary,
1783
- fontSize: require_theme.uiFontSize(10),
1784
- fontVariantNumeric: "tabular-nums",
1785
- whiteSpace: "nowrap"
1867
+ display: "grid",
1868
+ gridTemplateColumns: "max-content minmax(0, 1fr)",
1869
+ gap: 12,
1870
+ alignItems: "baseline",
1871
+ padding: "7px 10px",
1872
+ minWidth: 0,
1873
+ ...index === 0 ? {} : { borderTop: `0.5px solid ${require_theme.dshThemeColor.border}` }
1786
1874
  }
1787
- }, startedAt.toLocaleTimeString([], {
1788
- hour: "2-digit",
1789
- minute: "2-digit",
1790
- second: "2-digit",
1791
- hour12: false
1792
- }))), (0, react.createElement)(AssistantRecordChevron)), (0, react.createElement)("div", { style: {
1793
- display: "grid",
1794
- gap: 8,
1795
- padding: "9px 10px 10px",
1796
- borderTop: `1px solid ${require_theme.dshThemeColor.border}`,
1797
- minWidth: 0
1798
- } }, ...[[t("awb.call.arguments"), call.arguments], [t("awb.call.result"), call.result]].map(([heading, value]) => (0, react.createElement)("div", {
1799
- key: heading,
1800
- style: { minWidth: 0 }
1801
1875
  }, (0, react.createElement)("small", { style: {
1802
- color: require_theme.dshThemeColor.labelSecondary,
1876
+ color: require_theme.dshThemeColor.labelTertiary,
1803
1877
  fontSize: require_theme.uiFontSize(11),
1804
- fontWeight: 500
1878
+ whiteSpace: "nowrap"
1805
1879
  } }, heading), (0, react.createElement)("pre", {
1806
1880
  tabIndex: 0,
1807
1881
  "aria-label": heading,
1808
1882
  style: {
1809
- margin: "4px 0 0",
1810
- padding: "6px 8px",
1811
- borderRadius: 6,
1812
- background: require_theme.dshThemeColor.cardBackground,
1883
+ margin: 0,
1884
+ color: require_theme.dshThemeColor.labelSecondary,
1813
1885
  maxHeight: 160,
1814
1886
  overflowY: "auto",
1815
1887
  overscrollBehavior: "contain",
@@ -1822,18 +1894,23 @@ window.__ModuleLoader__.load({
1822
1894
  }
1823
1895
  }, value || "—"))))));
1824
1896
  }
1825
- function AssistantToolIcon({ kind }) {
1897
+ function AssistantStepChevron() {
1826
1898
  return (0, react.createElement)("svg", {
1827
- width: 16,
1828
- height: 16,
1899
+ className: "codingns-assistant-step-chevron",
1900
+ width: 14,
1901
+ height: 14,
1829
1902
  viewBox: "0 0 24 24",
1830
1903
  fill: "none",
1831
1904
  stroke: "currentColor",
1832
- strokeWidth: 1.7,
1905
+ strokeWidth: 1.8,
1833
1906
  strokeLinecap: "round",
1834
1907
  strokeLinejoin: "round",
1835
- "aria-hidden": true
1836
- }, (0, react.createElement)("path", { d: kind === "web-search" ? "M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0ZM3 12h18M12 3a17 17 0 0 1 0 18 17 17 0 0 1 0-18Z" : kind === "attachment" ? "M14 3H6v18h12V7l-4-4Zm0 0v5h4M9 12h6m-6 4h6" : "M3 6h7l2 2h9v12H3V6Zm0 4h18" }));
1908
+ "aria-hidden": true,
1909
+ style: {
1910
+ flexShrink: 0,
1911
+ color: require_theme.dshThemeColor.labelTertiary
1912
+ }
1913
+ }, (0, react.createElement)("path", { d: "m6 9 6 6 6-6" }));
1837
1914
  }
1838
1915
  //#endregion
1839
1916
  //#region src/client/features/assistant-realtime-call.ts
@@ -2968,12 +3045,7 @@ window.__ModuleLoader__.load({
2968
3045
  flexDirection: "column",
2969
3046
  gap: configuring ? 11 : 16
2970
3047
  }
2971
- }, !loaded ? (0, react.createElement)("div", {
2972
- role: "status",
2973
- style: require_theme.dshSettingsHelpStyle
2974
- }, t("awb.loading"), " ", button(t("awb.retry"), () => {
2975
- refresh().catch((cause) => setError(message(cause)));
2976
- })) : null, noticeText ? (0, react.createElement)("div", {
3048
+ }, !loaded ? (0, react.createElement)(AssistantLoadingView, { t }) : null, noticeText ? (0, react.createElement)("div", {
2977
3049
  role: "alert",
2978
3050
  style: {
2979
3051
  color: require_theme.dshThemeColor.error,
@@ -3176,6 +3248,19 @@ window.__ModuleLoader__.load({
3176
3248
  model
3177
3249
  }));
3178
3250
  }
3251
+ /** 首次读取期间使用居中的动态状态,避免把正常等待误显示成失败或重试入口。 */
3252
+ function AssistantLoadingView({ t }) {
3253
+ return (0, react.createElement)("div", {
3254
+ "data-codingns-assistant-loading": true,
3255
+ role: "status",
3256
+ "aria-live": "polite",
3257
+ "aria-label": t("awb.loading"),
3258
+ style: assistantLoadingStyle
3259
+ }, (0, react.createElement)("style", null, assistantLoadingCss), (0, react.createElement)("div", {
3260
+ "aria-hidden": true,
3261
+ style: assistantLoadingOrbStyle
3262
+ }, (0, react.createElement)("span", { className: "codingns-assistant-loading-ring" }), (0, react.createElement)("span", { className: "codingns-assistant-loading-core" }), (0, react.createElement)("span", { className: "codingns-assistant-loading-dot codingns-assistant-loading-dot-one" }), (0, react.createElement)("span", { className: "codingns-assistant-loading-dot codingns-assistant-loading-dot-two" }), (0, react.createElement)("span", { className: "codingns-assistant-loading-dot codingns-assistant-loading-dot-three" })), (0, react.createElement)("div", { style: assistantLoadingLabelStyle }, t("awb.loadingHint")));
3263
+ }
3179
3264
  /** 内层菜单先消费 Escape,外层窗口只处理尚未消费的关闭按键。 */
3180
3265
  function handleAssistantWorkbenchEscape(event, onClose) {
3181
3266
  if (event.key !== "Escape" || event.defaultPrevented) return;
@@ -3210,6 +3295,47 @@ window.__ModuleLoader__.load({
3210
3295
  gap: 8,
3211
3296
  justifyItems: "center"
3212
3297
  };
3298
+ const assistantLoadingStyle = {
3299
+ flex: "1 1 auto",
3300
+ minHeight: "min(420px, 45vh)",
3301
+ display: "flex",
3302
+ flexDirection: "column",
3303
+ alignItems: "center",
3304
+ justifyContent: "center",
3305
+ gap: 18,
3306
+ color: require_theme.dshThemeColor.labelSecondary
3307
+ };
3308
+ const assistantLoadingOrbStyle = {
3309
+ position: "relative",
3310
+ width: 76,
3311
+ height: 76,
3312
+ borderRadius: "50%",
3313
+ background: `radial-gradient(circle at 35% 28%, ${require_theme.dshThemeColor.accent}33, transparent 58%)`,
3314
+ boxShadow: `0 0 0 1px ${require_theme.dshThemeColor.border}, 0 0 42px ${require_theme.dshThemeColor.accent}24`
3315
+ };
3316
+ const assistantLoadingLabelStyle = {
3317
+ fontSize: require_theme.uiFontSize(13),
3318
+ letterSpacing: "0.04em"
3319
+ };
3320
+ const assistantLoadingCss = `
3321
+ [data-codingns-assistant-loading] .codingns-assistant-loading-ring,
3322
+ [data-codingns-assistant-loading] .codingns-assistant-loading-core,
3323
+ [data-codingns-assistant-loading] .codingns-assistant-loading-dot{position:absolute;display:block;border-radius:50%}
3324
+ [data-codingns-assistant-loading] .codingns-assistant-loading-ring{inset:7px;border:1px solid ${require_theme.dshThemeColor.accent}88;border-top-color:transparent;border-left-color:${require_theme.dshThemeColor.accent};animation:codingns-assistant-loading-spin 2.8s linear infinite}
3325
+ [data-codingns-assistant-loading] .codingns-assistant-loading-core{inset:22px;background:${require_theme.dshThemeColor.accent};box-shadow:0 0 18px ${require_theme.dshThemeColor.accent}aa;animation:codingns-assistant-loading-breathe 1.8s ease-in-out infinite}
3326
+ [data-codingns-assistant-loading] .codingns-assistant-loading-dot{width:6px;height:6px;background:${require_theme.dshThemeColor.labelPrimary};box-shadow:0 0 10px ${require_theme.dshThemeColor.accent};animation:codingns-assistant-loading-pulse 1.8s ease-in-out infinite}
3327
+ [data-codingns-assistant-loading] .codingns-assistant-loading-dot-one{top:4px;left:35px}
3328
+ [data-codingns-assistant-loading] .codingns-assistant-loading-dot-two{right:7px;bottom:20px;animation-delay:.22s}
3329
+ [data-codingns-assistant-loading] .codingns-assistant-loading-dot-three{left:7px;bottom:20px;animation-delay:.44s}
3330
+ @keyframes codingns-assistant-loading-spin{to{transform:rotate(360deg)}}
3331
+ @keyframes codingns-assistant-loading-breathe{0%,100%{transform:scale(.82);opacity:.72}50%{transform:scale(1.08);opacity:1}}
3332
+ @keyframes codingns-assistant-loading-pulse{0%,100%{transform:scale(.65);opacity:.36}50%{transform:scale(1.15);opacity:1}}
3333
+ @media (prefers-reduced-motion:reduce){
3334
+ [data-codingns-assistant-loading] .codingns-assistant-loading-ring,
3335
+ [data-codingns-assistant-loading] .codingns-assistant-loading-core,
3336
+ [data-codingns-assistant-loading] .codingns-assistant-loading-dot{animation:none}
3337
+ }
3338
+ `;
3213
3339
  const backdrop = {
3214
3340
  position: "fixed",
3215
3341
  inset: 0,
@@ -4236,6 +4362,14 @@ window.__ModuleLoader__.load({
4236
4362
  question: t("awb.notifications.question"),
4237
4363
  approval: t("awb.notifications.approval")
4238
4364
  };
4365
+ const icon = reaction === "question" ? (0, react.createElement)("span", {
4366
+ "data-codingns-avatar-reaction-glyph": "speech",
4367
+ style: { display: "inline-flex" }
4368
+ }, speechIcon()) : {
4369
+ success: "✓",
4370
+ concerned: "!",
4371
+ approval: "…"
4372
+ }[reaction];
4239
4373
  return (0, react.createElement)("span", {
4240
4374
  role: "status",
4241
4375
  "aria-label": labels[reaction],
@@ -4243,21 +4377,50 @@ window.__ModuleLoader__.load({
4243
4377
  "data-codingns-avatar-reaction": reaction,
4244
4378
  style: {
4245
4379
  position: "absolute",
4246
- right: 0,
4380
+ left: "50%",
4247
4381
  top: 0,
4248
- padding: "2px 7px",
4249
- borderRadius: 12,
4382
+ transform: "translateX(-50%)",
4383
+ width: 30,
4384
+ height: 26,
4385
+ boxSizing: "border-box",
4386
+ display: "grid",
4387
+ placeItems: "center",
4388
+ padding: 0,
4389
+ borderRadius: 11,
4250
4390
  background: "#fff",
4251
4391
  color: "#242424",
4252
4392
  border: "1px solid #b8c3d3",
4393
+ lineHeight: 1,
4253
4394
  pointerEvents: "none"
4254
4395
  }
4255
- }, {
4256
- success: "✓",
4257
- concerned: "!",
4258
- question: "?",
4259
- approval: "…"
4260
- }[reaction]);
4396
+ }, icon);
4397
+ }
4398
+ /** 提问状态使用发言气泡:圆角轮廓、右下尾巴和三个空心圆点。 */
4399
+ function speechIcon() {
4400
+ return (0, react.createElement)("svg", {
4401
+ width: 20,
4402
+ height: 20,
4403
+ viewBox: "0 0 24 24",
4404
+ fill: "none",
4405
+ stroke: "currentColor",
4406
+ strokeWidth: 1.9,
4407
+ strokeLinecap: "round",
4408
+ strokeLinejoin: "round",
4409
+ "aria-hidden": true,
4410
+ focusable: false
4411
+ }, (0, react.createElement)("path", { d: "M12 3H9a7 7 0 0 0-7 7v2a7 7 0 0 0 7 7h5.5l3.5 2.5.8-4.45A7 7 0 0 0 21 12v-2a7 7 0 0 0-7-7h-2Z" }), (0, react.createElement)("circle", {
4412
+ cx: 7.5,
4413
+ cy: 11,
4414
+ r: 1.45
4415
+ }), (0, react.createElement)("circle", {
4416
+ cx: 12,
4417
+ cy: 11,
4418
+ r: 1.45
4419
+ }), (0, react.createElement)("circle", {
4420
+ cx: 16.5,
4421
+ cy: 11,
4422
+ r: 1.45
4423
+ }));
4261
4424
  }
4262
4425
  //#endregion
4263
4426
  //#region src/client/avatar/spritesheet.ts
@@ -7392,6 +7555,7 @@ window.__ModuleLoader__.load({
7392
7555
  var module = { exports: {} };
7393
7556
  var exports = module.exports;
7394
7557
  const require_assistant_notifications = require("@jingyi0605/codingns4dsh/client.assistant-notifications.js");
7558
+ const require_session_translation = require("@jingyi0605/codingns4dsh/client.session-translation.js");
7395
7559
  //#region src/shared/contracts/version.ts
7396
7560
  /** 当前经过完整验证的 DSH 版本;源文件由根目录 version.json 同步。 */
7397
7561
  const DSH_VERSION = "0.2.1-alpha.2";
@@ -7425,7 +7589,7 @@ window.__ModuleLoader__.load({
7425
7589
  return /^0\.1\.5(?:-|$)/u.test(version);
7426
7590
  }
7427
7591
  /** Codingns4DSH 插件自身的 npm 版本。 */
7428
- const CODINGNS_VERSION = "0.2.1-beta.7";
7592
+ const CODINGNS_VERSION = "0.2.1-beta.9";
7429
7593
  /** 判断宿主版本是否落在当前插件声明的 DSH 兼容范围内;范围省略上界时不校验上限。 */
7430
7594
  function isDshVersionCompatible(version) {
7431
7595
  const match = /^>=([^ ]+)(?: <=([^ ]+))?$/u.exec(DSH_COMPATIBILITY);
@@ -7670,8 +7834,10 @@ window.__ModuleLoader__.load({
7670
7834
  const CODINGNS_FILE_MANAGEMENT_FIELD = "fileManagement";
7671
7835
  const CODINGNS_MOBILE_ACCESS_FIELD = "mobileAccess";
7672
7836
  const CODINGNS_SUBSCRIPTION_USAGE_FIELD = "subscriptionUsage";
7837
+ const CODINGNS_SESSION_TRANSLATION_FIELD = "sessionTranslation";
7673
7838
  const CODINGNS_ASSISTANT_FIELD = "assistant";
7674
- const DEFAULT_CODINGNS_CONTROL_BASE_URLS = ["https://channel.codingns.com:1443"];
7839
+ const DEFAULT_CODINGNS_CONTROL_BASE_URL = "https://channel.codingns.com:1443";
7840
+ const DEFAULT_CODINGNS_CONTROL_BASE_URLS = [DEFAULT_CODINGNS_CONTROL_BASE_URL];
7675
7841
  /** 控制站的网页登录地址,用于注册 Codingns4DSH 账号。 */
7676
7842
  const CODINGNS_CONTROL_STATION_URL = "https://channel.codingns.com:1443";
7677
7843
  /** 独立 H5 登录页面地址;登录控制站后可从设置页复制给其他设备。 */
@@ -7705,7 +7871,28 @@ window.__ModuleLoader__.load({
7705
7871
  quickPhrases: DEFAULT_QUICK_PHRASES.map((phrase) => ({ ...phrase })),
7706
7872
  quickPhrasesSeeded: true
7707
7873
  };
7708
- [...DEFAULT_CODINGNS_CONTROL_BASE_URLS];
7874
+ const DEFAULT_CODINGNS_SETTINGS = {
7875
+ controlBaseUrl: DEFAULT_CODINGNS_CONTROL_BASE_URL,
7876
+ controlBaseUrls: [...DEFAULT_CODINGNS_CONTROL_BASE_URLS],
7877
+ modules: {},
7878
+ agentAdapters: {},
7879
+ agentAdapterPreferences: {},
7880
+ subagentBridge: DEFAULT_SUBAGENT_BRIDGE_SETTINGS,
7881
+ sessionTranslation: require_session_translation.DEFAULT_SESSION_TRANSLATION_SETTINGS,
7882
+ terminalEnhancement: DEFAULT_TERMINAL_ENHANCEMENT_SETTINGS,
7883
+ workspaceSessionEnhancement: DEFAULT_WORKSPACE_SESSION_ENHANCEMENT_SETTINGS,
7884
+ fileManagement: DEFAULT_FILE_MANAGEMENT_SETTINGS,
7885
+ mobileAccess: DEFAULT_MOBILE_ACCESS_SETTINGS,
7886
+ subscriptionUsage: DEFAULT_SUBSCRIPTION_USAGE_SETTINGS,
7887
+ assistant: DEFAULT_ASSISTANT_SETTINGS,
7888
+ lanAccessDsh: {
7889
+ autoStart: false,
7890
+ listenHost: "0.0.0.0",
7891
+ listenPort: 13080,
7892
+ dshPort: 0,
7893
+ pwa: DEFAULT_LAN_ACCESS_DSH_PWA_SETTINGS
7894
+ }
7895
+ };
7709
7896
  function captureRestartFeatureStates(descriptors, settings, dshVersion) {
7710
7897
  const states = {};
7711
7898
  for (const descriptor of descriptors) if (descriptor.activation === "restart") states[descriptor.name] = isFeatureDshVersionCompatible(descriptor, dshVersion) && isFeatureEnabled(descriptor, settings);
@@ -7872,6 +8059,12 @@ window.__ModuleLoader__.load({
7872
8059
  return CODINGNS_MODULES_FIELD;
7873
8060
  }
7874
8061
  });
8062
+ Object.defineProperty(exports, "CODINGNS_SESSION_TRANSLATION_FIELD", {
8063
+ enumerable: true,
8064
+ get: function() {
8065
+ return CODINGNS_SESSION_TRANSLATION_FIELD;
8066
+ }
8067
+ });
7875
8068
  Object.defineProperty(exports, "CODINGNS_SETTINGS_ENTRY_IDS", {
7876
8069
  enumerable: true,
7877
8070
  get: function() {
@@ -7926,6 +8119,12 @@ window.__ModuleLoader__.load({
7926
8119
  return DEFAULT_CODINGNS_CONTROL_BASE_URLS;
7927
8120
  }
7928
8121
  });
8122
+ Object.defineProperty(exports, "DEFAULT_CODINGNS_SETTINGS", {
8123
+ enumerable: true,
8124
+ get: function() {
8125
+ return DEFAULT_CODINGNS_SETTINGS;
8126
+ }
8127
+ });
7929
8128
  Object.defineProperty(exports, "DEFAULT_FILE_MANAGEMENT_SETTINGS", {
7930
8129
  enumerable: true,
7931
8130
  get: function() {
@@ -16031,6 +16230,59 @@ window.__ModuleLoader__.load({
16031
16230
  }
16032
16231
  });
16033
16232
 
16233
+ window.__ModuleLoader__.load({
16234
+ id: "@jingyi0605/codingns4dsh",
16235
+ chunk: "client.session-translation.js",
16236
+ factory: () => { const require = __codingnsOwnerRequire;
16237
+ var module = { exports: {} };
16238
+ var exports = module.exports;
16239
+ //#region src/shared/contracts/session-translation.ts
16240
+ const SESSION_TRANSLATION_ADAPTER_IDS = [
16241
+ "command-code",
16242
+ "claude-code",
16243
+ "kimi",
16244
+ "gemini",
16245
+ "pi",
16246
+ "codex",
16247
+ "opencode",
16248
+ "grok",
16249
+ "mcode",
16250
+ "zcode",
16251
+ "codebuddy",
16252
+ "workbuddy",
16253
+ "cursor-cli",
16254
+ "kiro-cli",
16255
+ "qoder",
16256
+ "qoder-cn",
16257
+ "antigravity",
16258
+ "doubao"
16259
+ ];
16260
+ const DEFAULT_SESSION_TRANSLATION_SETTINGS = {
16261
+ inputLanguage: "en",
16262
+ outputLanguage: "zh",
16263
+ bilingual: false
16264
+ };
16265
+ /** 所有内置外部 Agent 均可在会话模型选择器中开启翻译;DSH 原生 Agent 不在其中。 */
16266
+ function isSessionTranslationAdapter(adapterId) {
16267
+ return SESSION_TRANSLATION_ADAPTER_IDS.includes(adapterId);
16268
+ }
16269
+ //#endregion
16270
+ Object.defineProperty(exports, "DEFAULT_SESSION_TRANSLATION_SETTINGS", {
16271
+ enumerable: true,
16272
+ get: function() {
16273
+ return DEFAULT_SESSION_TRANSLATION_SETTINGS;
16274
+ }
16275
+ });
16276
+ Object.defineProperty(exports, "isSessionTranslationAdapter", {
16277
+ enumerable: true,
16278
+ get: function() {
16279
+ return isSessionTranslationAdapter;
16280
+ }
16281
+ });
16282
+ return module.exports;
16283
+ }
16284
+ });
16285
+
16034
16286
  window.__ModuleLoader__.load({
16035
16287
  id: "@jingyi0605/codingns4dsh",
16036
16288
  chunk: "client.theme.js",
@@ -16092,6 +16344,8 @@ window.__ModuleLoader__.load({
16092
16344
  primaryForeground: "var(--dsw-alias-label-primary-foreground, #fff)",
16093
16345
  success: "var(--dsw-alias-state-success-primary, #16803c)",
16094
16346
  error: "var(--dsw-alias-state-error-primary, #b42318)",
16347
+ warning: "var(--dsw-alias-state-warning-primary, #d99a00)",
16348
+ warningForeground: "#3d2c00",
16095
16349
  fieldBorder: "var(--dsw-alias-border-l4, #d9d9d9)",
16096
16350
  menuBorder: "var(--dsw-alias-border-l3, #d9d9d9)",
16097
16351
  switchThumb: "var(--dsw-static-neutral-00, #fff)",
@@ -17652,11 +17906,53 @@ window.__ModuleLoader__.load({
17652
17906
  "cli.thinkingLevelTitle": "Thinking level ({model})",
17653
17907
  "cli.adapterEnabled": "{name} enabled",
17654
17908
  "cli.adapterDisabled": "{name} disabled",
17909
+ "cli.translationConversation": "Translate conversation",
17910
+ "cli.translationConversationToggle": "Toggle conversation translation",
17655
17911
  "lan.fieldListenPort": "listen port",
17656
17912
  "lan.fieldDshPort": "DSH local port",
17657
17913
  "lan.fieldMustBeNumber": "{field} must be a number",
17658
17914
  "lan.fieldMustBeInteger": "{field} must be an integer from {minimum} to 65535",
17659
- "workspace.sessionSettingsSaved": "Workspace session settings saved"
17915
+ "workspace.sessionSettingsSaved": "Workspace session settings saved",
17916
+ "feature.sessionTranslation.label": "Session translation",
17917
+ "feature.sessionTranslation.description": "Translate selected external Agent requests and replies while keeping tools, commands, paths, and original history unchanged.",
17918
+ "translation.inputTitle": "Translate requests before sending",
17919
+ "translation.inputLanguage": "Request language",
17920
+ "translation.outputTitle": "Translate visible replies",
17921
+ "translation.outputLanguage": "Reply language",
17922
+ "translation.modelTitle": "Translation model",
17923
+ "translation.modelFollowDefault": "Use the default DSH model",
17924
+ "translation.modelUnavailable": "Saved model is unavailable",
17925
+ "translation.modelLoading": "Loading available DSH models…",
17926
+ "translation.modelEmpty": "No DSH text models are available.",
17927
+ "translation.modelHelp": "DeepSeek official and configured third-party models are available here. Translation requests always disable thinking.",
17928
+ "translation.detailsTitle": "Translation details",
17929
+ "translation.viewDetails": "View translation details",
17930
+ "translation.statusTranslated": "Translated",
17931
+ "translation.statusFailed": "Translation failed; original kept",
17932
+ "translation.statusNotNeeded": "Already in the target language; translation skipped",
17933
+ "translation.detectedLanguage": "Detected language",
17934
+ "translation.modelNotUsed": "Not used; translation was unnecessary",
17935
+ "translation.originalText": "Original text",
17936
+ "translation.translatedText": "Translated / sent text",
17937
+ "translation.modelUsed": "Model used",
17938
+ "translation.modelUnknown": "Model information unavailable",
17939
+ "translation.modelOfficial": "DeepSeek official",
17940
+ "translation.modelThirdParty": "Third-party",
17941
+ "translation.saveRejected": "The translation settings could not be saved.",
17942
+ "translation.saved": "Translation settings saved",
17943
+ "translation.language.zh": "简体中文",
17944
+ "translation.language.en": "English",
17945
+ "translation.language.fr": "Français",
17946
+ "translation.language.de": "Deutsch",
17947
+ "translation.language.ja": "日本語",
17948
+ "translation.adapter.claude": "Claude Code",
17949
+ "translation.adapter.codex": "Codex",
17950
+ "translation.adapter.pi": "Pi",
17951
+ "translation.adapter.opencode": "OpenCode",
17952
+ "translation.adapter.antigravity": "Antigravity",
17953
+ "translation.adapter.grok": "Grok",
17954
+ "translation.adapter.cursor": "Cursor",
17955
+ "translation.adapter.kiro": "Kiro"
17660
17956
  };
17661
17957
  const zh$9 = {
17662
17958
  "common.listSeparator": "、",
@@ -17665,11 +17961,53 @@ window.__ModuleLoader__.load({
17665
17961
  "cli.thinkingLevelTitle": "思考等级({model})",
17666
17962
  "cli.adapterEnabled": "已启用 {name}",
17667
17963
  "cli.adapterDisabled": "已停用 {name}",
17964
+ "cli.translationConversation": "翻译对话",
17965
+ "cli.translationConversationToggle": "切换对话翻译",
17668
17966
  "lan.fieldListenPort": "监听端口",
17669
17967
  "lan.fieldDshPort": "DSH 本地端口",
17670
17968
  "lan.fieldMustBeNumber": "{field} 必须是数字",
17671
17969
  "lan.fieldMustBeInteger": "{field} 必须是 {minimum} 到 65535 的整数",
17672
- "workspace.sessionSettingsSaved": "工作区会话设置已保存"
17970
+ "workspace.sessionSettingsSaved": "工作区会话设置已保存",
17971
+ "feature.sessionTranslation.label": "会话翻译",
17972
+ "feature.sessionTranslation.description": "为选定的外部 Agent 翻译请求和回复,同时保留工具、命令、路径与原始历史。",
17973
+ "translation.inputTitle": "发送前翻译请求",
17974
+ "translation.inputLanguage": "请求目标语言",
17975
+ "translation.outputTitle": "翻译界面回复",
17976
+ "translation.outputLanguage": "回复语言",
17977
+ "translation.modelTitle": "翻译模型",
17978
+ "translation.modelFollowDefault": "跟随 DSH 默认模型",
17979
+ "translation.modelUnavailable": "已保存的模型当前不可用",
17980
+ "translation.modelLoading": "正在加载可用的 DSH 模型…",
17981
+ "translation.modelEmpty": "当前没有可用的 DSH 文本模型。",
17982
+ "translation.modelHelp": "这里可以选择 DeepSeek 官方模型或已配置的第三方模型;翻译请求始终关闭思考。",
17983
+ "translation.detailsTitle": "翻译详情",
17984
+ "translation.viewDetails": "查看翻译详情",
17985
+ "translation.statusTranslated": "已翻译",
17986
+ "translation.statusFailed": "翻译失败,已保留原文",
17987
+ "translation.statusNotNeeded": "原文已是目标语言,无需翻译",
17988
+ "translation.detectedLanguage": "检测到的语言",
17989
+ "translation.modelNotUsed": "未调用,原文无需翻译",
17990
+ "translation.originalText": "原文",
17991
+ "translation.translatedText": "译文 / 实际发送文本",
17992
+ "translation.modelUsed": "实际翻译模型",
17993
+ "translation.modelUnknown": "暂无模型信息",
17994
+ "translation.modelOfficial": "DeepSeek 官方",
17995
+ "translation.modelThirdParty": "第三方",
17996
+ "translation.saveRejected": "会话翻译设置未能保存。",
17997
+ "translation.saved": "会话翻译设置已保存",
17998
+ "translation.language.zh": "简体中文",
17999
+ "translation.language.en": "英语",
18000
+ "translation.language.fr": "法语",
18001
+ "translation.language.de": "德语",
18002
+ "translation.language.ja": "日语",
18003
+ "translation.adapter.claude": "Claude Code",
18004
+ "translation.adapter.codex": "Codex",
18005
+ "translation.adapter.pi": "Pi",
18006
+ "translation.adapter.opencode": "OpenCode",
18007
+ "translation.adapter.antigravity": "Antigravity",
18008
+ "translation.adapter.grok": "Grok",
18009
+ "translation.adapter.cursor": "Cursor",
18010
+ "translation.adapter.kiro": "Kiro"
17673
18011
  };
17674
18012
  //#endregion
17675
18013
  //#region src/client/locales/peerHost.ts
@@ -18622,7 +18960,9 @@ window.__ModuleLoader__.load({
18622
18960
  "awb.notifications.question": "需要回答",
18623
18961
  "awb.notifications.approval": "等待审批",
18624
18962
  "awb.notifications.questionTag": "问题",
18625
- "awb.notifications.approvalTag": "权限请求",
18963
+ "awb.notifications.approvalTag": "权限申请",
18964
+ "awb.notifications.requestPrefix": "有",
18965
+ "awb.notifications.requestSuffix": "需要你来处理",
18626
18966
  "awb.notifications.autoClose": "通知自动关闭",
18627
18967
  "awb.notifications.autoCloseHint": "启用后,失败、提问与审批的气泡在设定时间后自动收起;完成提示始终固定 10 秒关闭。未读记录仍会保留。",
18628
18968
  "awb.notifications.autoCloseDuration": "自动关闭时间",
@@ -18630,13 +18970,16 @@ window.__ModuleLoader__.load({
18630
18970
  "awb.notifications.seconds": "秒",
18631
18971
  "awb.notifications.open": "查看会话",
18632
18972
  "awb.notifications.dismiss": "收起提醒",
18973
+ "awb.notifications.collapse": "收起通知",
18974
+ "awb.notifications.expand": "展开通知",
18633
18975
  "awb.notifications.list": "提醒列表",
18976
+ "awb.notifications.clearAll": "一键清理",
18977
+ "awb.notifications.clearAllLabel": "一键清理全部通知",
18634
18978
  "awb.notifications.unread": "{count} 条未读",
18635
18979
  "awb.notifications.pending": "{count} 项待处理",
18980
+ "awb.notifications.unreadFilterLabel": "筛选未读通知:{count} 条未读",
18981
+ "awb.notifications.pendingFilterLabel": "筛选待处理通知:{count} 项待处理",
18636
18982
  "awb.notifications.empty": "暂无提醒",
18637
- "awb.notifications.next": "下一页",
18638
- "awb.notifications.previous": "上一页",
18639
- "awb.notifications.closeList": "关闭提醒列表",
18640
18983
  "awb.notifications.disconnected": "远端暂不可达",
18641
18984
  "awb.notifications.expired": "提醒已失效",
18642
18985
  "awb.notifications.actionUnavailable": "通知操作暂不可用,请重试",
@@ -18681,6 +19024,14 @@ window.__ModuleLoader__.load({
18681
19024
  "awb.call.arguments": "调用参数",
18682
19025
  "awb.call.result": "返回结果",
18683
19026
  "awb.call.toolDetails": "查看参数与结果",
19027
+ "awb.call.step": "执行步骤",
19028
+ "awb.call.stepDetails": "展开查看步骤中的工具调用",
19029
+ "awb.call.separator": "、",
19030
+ "awb.call.toolCount": "{count} 次调用",
19031
+ "awb.call.stepRead": "已读取文件",
19032
+ "awb.call.stepSearch": "已搜索网页",
19033
+ "awb.call.stepWorkspace": "已调用工具",
19034
+ "awb.call.stepTool": "已执行操作",
18684
19035
  "awb.preparationUnsupported": "当前 Host 不支持仅准备资源,请更新 Host 后再操作。",
18685
19036
  "awb.title": "智能助理",
18686
19037
  "awb.create": "创建助理",
@@ -18750,7 +19101,8 @@ window.__ModuleLoader__.load({
18750
19101
  "awb.resetFinalConfirm": "确定要重置助理吗?助理配置、对话和派生索引将被清空,无法从此界面撤销。原始项目会话和下载素材会保留。",
18751
19102
  "awb.confirm": "确认",
18752
19103
  "awb.busy": "处理中…",
18753
- "awb.loading": "正在读取助理…",
19104
+ "awb.loading": "正在准备助理",
19105
+ "awb.loadingHint": "正在准备你的对话…",
18754
19106
  "awb.retry": "重试",
18755
19107
  "awb.summary": "历史交流摘要",
18756
19108
  "awb.saved": "配置已保存",
@@ -18777,6 +19129,8 @@ window.__ModuleLoader__.load({
18777
19129
  "awb.notifications.approval": "Approval needed",
18778
19130
  "awb.notifications.questionTag": "Question",
18779
19131
  "awb.notifications.approvalTag": "Permission request",
19132
+ "awb.notifications.requestPrefix": "There is a",
19133
+ "awb.notifications.requestSuffix": "waiting for your response",
18780
19134
  "awb.notifications.autoClose": "Auto-close notices",
18781
19135
  "awb.notifications.autoCloseHint": "When enabled, failure, question and approval notices collapse after the selected time; completion notices always close after 10 seconds. Unread records remain available.",
18782
19136
  "awb.notifications.autoCloseDuration": "Auto-close time",
@@ -18784,13 +19138,16 @@ window.__ModuleLoader__.load({
18784
19138
  "awb.notifications.seconds": " seconds",
18785
19139
  "awb.notifications.open": "View session",
18786
19140
  "awb.notifications.dismiss": "Collapse notice",
19141
+ "awb.notifications.collapse": "Collapse notifications",
19142
+ "awb.notifications.expand": "Expand notifications",
18787
19143
  "awb.notifications.list": "Notice list",
19144
+ "awb.notifications.clearAll": "Clear all",
19145
+ "awb.notifications.clearAllLabel": "Clear all notifications",
18788
19146
  "awb.notifications.unread": "{count} unread",
18789
19147
  "awb.notifications.pending": "{count} pending",
19148
+ "awb.notifications.unreadFilterLabel": "Filter unread notifications: {count} unread",
19149
+ "awb.notifications.pendingFilterLabel": "Filter pending notifications: {count} pending",
18790
19150
  "awb.notifications.empty": "No notices",
18791
- "awb.notifications.next": "Next page",
18792
- "awb.notifications.previous": "Previous page",
18793
- "awb.notifications.closeList": "Close notice list",
18794
19151
  "awb.notifications.disconnected": "Remote host unavailable",
18795
19152
  "awb.notifications.expired": "Notice expired",
18796
19153
  "awb.notifications.actionUnavailable": "Notice action is unavailable. Please try again.",
@@ -18835,6 +19192,14 @@ window.__ModuleLoader__.load({
18835
19192
  "awb.call.arguments": "Arguments",
18836
19193
  "awb.call.result": "Result",
18837
19194
  "awb.call.toolDetails": "View arguments and result",
19195
+ "awb.call.step": "Execution step",
19196
+ "awb.call.stepDetails": "Expand to view the tools in this step",
19197
+ "awb.call.separator": ", ",
19198
+ "awb.call.toolCount": "{count} calls",
19199
+ "awb.call.stepRead": "Read files",
19200
+ "awb.call.stepSearch": "Searched the web",
19201
+ "awb.call.stepWorkspace": "Called tools",
19202
+ "awb.call.stepTool": "Completed an action",
18838
19203
  "awb.preparationUnsupported": "This Host does not support preparing resources without applying settings. Update the Host before proceeding.",
18839
19204
  "awb.title": "Assistant",
18840
19205
  "awb.create": "Create assistant",
@@ -18904,7 +19269,8 @@ window.__ModuleLoader__.load({
18904
19269
  "awb.resetFinalConfirm": "Reset your assistant? Settings, conversation, and derived indexes will be cleared and cannot be restored from this page. Original project sessions and downloaded assets are kept.",
18905
19270
  "awb.confirm": "Confirm",
18906
19271
  "awb.busy": "Working…",
18907
- "awb.loading": "Reading assistant…",
19272
+ "awb.loading": "Preparing assistant",
19273
+ "awb.loadingHint": "Preparing your conversation…",
18908
19274
  "awb.retry": "Retry",
18909
19275
  "awb.summary": "Conversation summary",
18910
19276
  "awb.saved": "Configuration saved",
@@ -20061,6 +20427,7 @@ window.__ModuleLoader__.load({
20061
20427
  const require_transport = require("@jingyi0605/codingns4dsh/client.transport.js");
20062
20428
  const require_model_catalog_cache = require("@jingyi0605/codingns4dsh/client.model-catalog-cache.js");
20063
20429
  const require_assistant_notifications = require("@jingyi0605/codingns4dsh/client.assistant-notifications.js");
20430
+ const require_session_translation = require("@jingyi0605/codingns4dsh/client.session-translation.js");
20064
20431
  const require_theme = require("@jingyi0605/codingns4dsh/client.theme.js");
20065
20432
  const require_react_dom$1 = require("@jingyi0605/codingns4dsh/client.react-dom.js");
20066
20433
  const require_manager = require("@jingyi0605/codingns4dsh/client.manager.js");
@@ -27448,7 +27815,7 @@ window.__ModuleLoader__.load({
27448
27815
  const method = channel === "/codingns" ? endpoint : channel === "/api" ? endpoint.replace(/^codingns\//u, "") : "";
27449
27816
  if (peerHostId === null) {
27450
27817
  const input = CATALOG_ENDPOINTS.has(method) ? {
27451
- ...asRecord$6(payload),
27818
+ ...asRecord$8(payload),
27452
27819
  catalogHostId: "local"
27453
27820
  } : payload;
27454
27821
  return rpc.call(channel, endpoint, input, signal);
@@ -27472,18 +27839,18 @@ window.__ModuleLoader__.load({
27472
27839
  });
27473
27840
  let envelope = null;
27474
27841
  try {
27475
- envelope = asRecord$6(JSON.parse(response.body));
27842
+ envelope = asRecord$8(JSON.parse(response.body));
27476
27843
  } catch {}
27477
27844
  if (response.status < 200 || response.status >= 300) {
27478
- const error = asRecord$6(envelope?.error);
27845
+ const error = asRecord$8(envelope?.error);
27479
27846
  return failure$1(typeof error?.code === "string" ? error.code : "PEER_HOST_PROXY_UNREACHABLE", typeof error?.message === "string" ? error.message : require_transport.resolveCodingNsTranslator(locale)("cli.peerHostRpcFailed", { status: response.status }));
27480
27847
  }
27481
- const result = asRecord$6(envelope?.result);
27848
+ const result = asRecord$8(envelope?.result);
27482
27849
  if (result?.ok === true && Object.hasOwn(result, "value")) return {
27483
27850
  ok: true,
27484
27851
  value: result.value
27485
27852
  };
27486
- const error = asRecord$6(result?.error);
27853
+ const error = asRecord$8(result?.error);
27487
27854
  if (result?.ok === false && typeof error?.code === "string" && typeof error.message === "string") return failure$1(error.code, error.message);
27488
27855
  return failure$1("PEER_HOST_RESPONSE_INVALID", require_transport.resolveCodingNsTranslator(locale)("cli.peerHostRpcInvalid"));
27489
27856
  } };
@@ -27502,7 +27869,7 @@ window.__ModuleLoader__.load({
27502
27869
  }
27503
27870
  };
27504
27871
  }
27505
- function asRecord$6(value) {
27872
+ function asRecord$8(value) {
27506
27873
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
27507
27874
  }
27508
27875
  //#endregion
@@ -28344,7 +28711,7 @@ window.__ModuleLoader__.load({
28344
28711
  }, hostStatus === null ? t("terminal.readingStatus") : t("terminal.currentStatus", {
28345
28712
  status: hostStatus.effectiveEnabled ? t("terminal.enhanced") : t("terminal.basic"),
28346
28713
  platform: platformLabel(hostStatus.platform, t)
28347
- })), (0, react.createElement)(Field, {
28714
+ })), (0, react.createElement)(Field$1, {
28348
28715
  label: t("terminal.newDefault"),
28349
28716
  help: t("terminal.defaultProfileHelp")
28350
28717
  }, (0, react.createElement)("select", {
@@ -28359,7 +28726,7 @@ window.__ModuleLoader__.load({
28359
28726
  key: profile.profileId,
28360
28727
  value: profile.profileId,
28361
28728
  disabled: !profile.available
28362
- }, profile.label))), hostStatus?.fallbackReason === void 0 ? null : (0, react.createElement)("small", { style: helpStyle }, hostStatus.fallbackReason)), (0, react.createElement)(Field, {
28729
+ }, profile.label))), hostStatus?.fallbackReason === void 0 ? null : (0, react.createElement)("small", { style: helpStyle }, hostStatus.fallbackReason)), (0, react.createElement)(Field$1, {
28363
28730
  label: t("terminal.bindingScope"),
28364
28731
  help: t("terminal.workspaceBindingHelp")
28365
28732
  }, (0, react.createElement)("select", {
@@ -28398,7 +28765,7 @@ window.__ModuleLoader__.load({
28398
28765
  value: appearance.cursorColor,
28399
28766
  disabled,
28400
28767
  onChange: (cursorColor) => updateAppearance({ cursorColor })
28401
- }), (0, react.createElement)(Field, { label: t("terminal.cursorBlink") }, (0, react.createElement)("label", { style: {
28768
+ }), (0, react.createElement)(Field$1, { label: t("terminal.cursorBlink") }, (0, react.createElement)("label", { style: {
28402
28769
  display: "flex",
28403
28770
  alignItems: "center",
28404
28771
  justifyContent: "space-between",
@@ -28426,7 +28793,7 @@ window.__ModuleLoader__.load({
28426
28793
  step: .1,
28427
28794
  disabled,
28428
28795
  onChange: (lineHeight) => updateAppearance({ lineHeight })
28429
- }), (0, react.createElement)(Field, { label: t("terminal.cursorShape") }, (0, react.createElement)("select", {
28796
+ }), (0, react.createElement)(Field$1, { label: t("terminal.cursorShape") }, (0, react.createElement)("select", {
28430
28797
  value: appearance.cursorStyle,
28431
28798
  disabled,
28432
28799
  onChange: (event) => updateAppearance({ cursorStyle: parseCursorStyle(event.currentTarget.value) }),
@@ -28441,7 +28808,7 @@ window.__ModuleLoader__.load({
28441
28808
  onChange: (scrollback) => updateAppearance({ scrollback })
28442
28809
  })));
28443
28810
  }
28444
- function Field({ label, help, children }) {
28811
+ function Field$1({ label, help, children }) {
28445
28812
  return (0, react.createElement)("div", { style: {
28446
28813
  display: "flex",
28447
28814
  flexDirection: "column",
@@ -28457,7 +28824,7 @@ window.__ModuleLoader__.load({
28457
28824
  }, "ⓘ");
28458
28825
  }
28459
28826
  function ColorField({ label, value, disabled, onChange }) {
28460
- return (0, react.createElement)(Field, { label }, (0, react.createElement)("div", { style: rowStyle$2 }, (0, react.createElement)("input", {
28827
+ return (0, react.createElement)(Field$1, { label }, (0, react.createElement)("div", { style: rowStyle$2 }, (0, react.createElement)("input", {
28461
28828
  type: "color",
28462
28829
  value,
28463
28830
  disabled,
@@ -28470,7 +28837,7 @@ window.__ModuleLoader__.load({
28470
28837
  }), (0, react.createElement)("code", { style: { flex: 1 } }, value)));
28471
28838
  }
28472
28839
  function NumberField({ label, value, min, max, step, disabled, onChange }) {
28473
- return (0, react.createElement)(Field, { label }, (0, react.createElement)("div", { style: rowStyle$2 }, (0, react.createElement)("input", {
28840
+ return (0, react.createElement)(Field$1, { label }, (0, react.createElement)("div", { style: rowStyle$2 }, (0, react.createElement)("input", {
28474
28841
  type: "number",
28475
28842
  value,
28476
28843
  min,
@@ -28930,11 +29297,11 @@ window.__ModuleLoader__.load({
28930
29297
  const iterator = (await api.follow())[Symbol.asyncIterator]();
28931
29298
  const first = await iterator.next();
28932
29299
  await iterator.return?.();
28933
- const value = asRecord$5(asRecord$5(first.value)?.value);
29300
+ const value = asRecord$7(asRecord$7(first.value)?.value);
28934
29301
  const records = Array.isArray(value?.items) ? value.items : [];
28935
29302
  const globalArchived = readStringArray(value?.archivedSessionIds);
28936
29303
  return records.flatMap((item) => {
28937
- const record = asRecord$5(item);
29304
+ const record = asRecord$7(item);
28938
29305
  const workspaceId = readString$1(record?.workspaceId);
28939
29306
  if (workspaceId === void 0) return [];
28940
29307
  const path = readString$1(record?.path);
@@ -28950,12 +29317,12 @@ window.__ModuleLoader__.load({
28950
29317
  }
28951
29318
  async function readSessionList(api) {
28952
29319
  if (api?.list === void 0) return [];
28953
- const result = asRecord$5(unwrapRemoteValue$1(await api.list({})));
29320
+ const result = asRecord$7(unwrapRemoteValue$1(await api.list({})));
28954
29321
  return (Array.isArray(result?.items) ? result.items : []).flatMap((item) => {
28955
- const record = asRecord$5(item);
29322
+ const record = asRecord$7(item);
28956
29323
  const sessionId = readString$1(record?.sessionId);
28957
29324
  if (sessionId === void 0) return [];
28958
- const titleValue = asRecord$5(asRecord$5(record?.projections)?.values)?.title;
29325
+ const titleValue = asRecord$7(asRecord$7(record?.projections)?.values)?.title;
28959
29326
  const cwd = readString$1(record?.cwd);
28960
29327
  const workspaceIdValue = readString$1(record?.workspaceId);
28961
29328
  const archivedAt = typeof record?.archivedAt === "number" && Number.isFinite(record.archivedAt) ? record.archivedAt : 0;
@@ -29027,7 +29394,7 @@ window.__ModuleLoader__.load({
29027
29394
  "value",
29028
29395
  "data"
29029
29396
  ]) {
29030
- const workspace = asRecord$5(value[key]);
29397
+ const workspace = asRecord$7(value[key]);
29031
29398
  const id = readString$1(workspace?.workspaceId) ?? readString$1(workspace?.workspaceID) ?? readString$1(workspace?.id);
29032
29399
  if (id !== void 0) return id;
29033
29400
  }
@@ -29414,8 +29781,8 @@ window.__ModuleLoader__.load({
29414
29781
  }
29415
29782
  function normalizeRemote(value) {
29416
29783
  if (!isRecord$5(value)) return {};
29417
- const workspace = asRecord$5(readRemoteProperty(value, "workspace")) ?? asRecord$5(readRemoteProperty(value, "workspaceController"));
29418
- const session = asRecord$5(readRemoteProperty(value, "session")) ?? asRecord$5(readRemoteProperty(value, "sessionController"));
29784
+ const workspace = asRecord$7(readRemoteProperty(value, "workspace")) ?? asRecord$7(readRemoteProperty(value, "workspaceController"));
29785
+ const session = asRecord$7(readRemoteProperty(value, "session")) ?? asRecord$7(readRemoteProperty(value, "sessionController"));
29419
29786
  return {
29420
29787
  ...workspace ? { workspace } : {},
29421
29788
  ...session ? { session } : {}
@@ -29438,12 +29805,12 @@ window.__ModuleLoader__.load({
29438
29805
  return result === void 0 ? [] : [result];
29439
29806
  }) : [];
29440
29807
  }
29441
- function asRecord$5(value) {
29808
+ function asRecord$7(value) {
29442
29809
  return isRecord$5(value) ? value : void 0;
29443
29810
  }
29444
29811
  /** DSH Remote 的直接调用返回 RemoteResult;测试桩和旧宿主可能直接返回 value。 */
29445
29812
  function unwrapRemoteValue$1(value) {
29446
- const record = asRecord$5(value);
29813
+ const record = asRecord$7(value);
29447
29814
  if (record === void 0 || typeof record.ok !== "boolean") return value;
29448
29815
  return record.ok ? record.value : void 0;
29449
29816
  }
@@ -29462,8 +29829,8 @@ window.__ModuleLoader__.load({
29462
29829
  const WORKSPACE_SESSION_HIDDEN_FILTER_CHECK_ATTRIBUTE = "data-codingns-hidden-workspace-filter-check";
29463
29830
  const WORKSPACE_SESSION_HIDDEN_FILTER_BOUND_ATTRIBUTE = "data-codingns-hidden-workspace-filter-bound";
29464
29831
  const MENU_ATTRIBUTE$1 = "aria-haspopup";
29465
- const WORKSPACE_MENU_PATTERN = /(?:更多|菜单|选项|more|menu|option)/iu;
29466
- const WORKSPACE_FILTER_PATTERN = /(?:视图选项|筛选选项|view\s*options|filter\s*options)/iu;
29832
+ const WORKSPACE_MENU_PATTERN = /(?:更多|菜单|选项|操作|more|menu|option|action)/iu;
29833
+ const WORKSPACE_FILTER_PATTERN = /(?:视图选项|筛选选项|筛选会话|分组方式|排序方式|view\s*options?|filter\s*(?:options?|sessions?)|group\s*by|sort\s*by)/iu;
29467
29834
  /**
29468
29835
  * 给原生工作区菜单增加隐藏动作,并通过工作区筛选菜单控制恢复列表。
29469
29836
  *
@@ -29536,6 +29903,7 @@ window.__ModuleLoader__.load({
29536
29903
  workspaceIds.add(workspaceId);
29537
29904
  bindWorkspaceMenuTriggers(header, workspaceId, () => {
29538
29905
  activeMenuWorkspaceId = workspaceId;
29906
+ activeFilterTrigger = void 0;
29539
29907
  activeMenuKind = "workspace";
29540
29908
  menuContextPending = true;
29541
29909
  scheduleScan();
@@ -29546,6 +29914,7 @@ window.__ModuleLoader__.load({
29546
29914
  bindWorkspaceFilterTriggers(dom, (trigger) => {
29547
29915
  if (suppressFilterTrigger) return;
29548
29916
  activeFilterTrigger = trigger;
29917
+ activeMenuWorkspaceId = void 0;
29549
29918
  activeMenuKind = "filter";
29550
29919
  menuContextPending = true;
29551
29920
  scheduleScan();
@@ -29553,8 +29922,14 @@ window.__ModuleLoader__.load({
29553
29922
  });
29554
29923
  const menus = findWorkspaceMenus(dom);
29555
29924
  knownNativeMenus = new Set(menus);
29925
+ const menuContextFresh = menuContextPending;
29556
29926
  if (menus.length > 0) menuContextPending = false;
29557
- if (activeMenuKind === "filter") injectFilterMenuAction(dom, selectFilterMenus(menus, menuWorkspaceIds, activeMenuWorkspaceId), showHiddenWorkspaces, () => {
29927
+ const currentMenu = [...menus].reverse().find(isVisibleMenu);
29928
+ const currentMenuKind = currentMenu === void 0 ? activeMenuKind : isWorkspaceFilterMenu(currentMenu) ? "filter" : "workspace";
29929
+ const filterContextFresh = menuContextFresh && activeMenuKind === "filter";
29930
+ const filterMenus = currentMenuKind === "filter" ? selectFilterMenus(menus, menuWorkspaceIds, activeMenuWorkspaceId, filterContextFresh) : [];
29931
+ const currentMenuOwnsFilterAction = currentMenu !== void 0 && currentMenu.querySelector(`[data-codingns-hidden-workspace-filter]`) !== null;
29932
+ if (currentMenuKind === "filter" && (filterContextFresh || currentMenuOwnsFilterAction)) injectFilterMenuAction(dom, filterMenus, showHiddenWorkspaces, () => {
29558
29933
  showHiddenWorkspaces = !showHiddenWorkspaces;
29559
29934
  scheduleScan();
29560
29935
  }, (menu) => closeFilterMenu(activeFilterTrigger, menu, () => {
@@ -29563,10 +29938,11 @@ window.__ModuleLoader__.load({
29563
29938
  suppressFilterTrigger = false;
29564
29939
  };
29565
29940
  }), t);
29566
- else injectWorkspaceMenuActions(selectWorkspaceMenus(menus), dom, activeMenuWorkspaceId, workspaceIds, menuWorkspaceIds, (workspaceId) => updateVisibility(workspaceId, true), t);
29941
+ else injectWorkspaceMenuActions(selectWorkspaceMenus(menus, menuContextFresh, menuWorkspaceIds), dom, menuContextFresh ? activeMenuWorkspaceId : void 0, workspaceIds, menuWorkspaceIds, (workspaceId) => updateVisibility(workspaceId, true), t);
29567
29942
  if (menus.length === 0 && !menuContextPending) {
29568
29943
  activeMenuWorkspaceId = void 0;
29569
29944
  activeMenuKind = void 0;
29945
+ activeFilterTrigger = void 0;
29570
29946
  }
29571
29947
  if (showHiddenWorkspaces) injectHiddenWorkspaceFooter(dom, headers, workspaces, hiddenWorkspaceIds, (workspaceId) => updateVisibility(workspaceId, false), t);
29572
29948
  } finally {
@@ -29674,25 +30050,26 @@ window.__ModuleLoader__.load({
29674
30050
  let current = item.parentElement;
29675
30051
  for (let depth = 0; depth < 8 && current !== null; depth += 1, current = current.parentElement) counts.set(current, (counts.get(current) ?? 0) + 1);
29676
30052
  }
29677
- const candidates = [...counts.keys()].filter((element) => (counts.get(element) ?? 0) >= 3 && /(?:分组方式|排序方式|筛选会话|group(?:ing)?|sort|filter)/iu.test(element.textContent ?? ""));
30053
+ const candidates = [...counts.keys()].filter((element) => (counts.get(element) ?? 0) >= 3 && isWorkspaceFilterMenu(element));
29678
30054
  return candidates.filter((element) => !candidates.some((other) => other !== element && element.contains(other)));
29679
30055
  }
29680
30056
  /**
29681
30057
  * 菜单 Portal 在关闭时可能短暂保留旧节点。先过滤不可见节点,再按菜单内容
29682
30058
  * 区分筛选菜单和工作区操作菜单,避免旧筛选菜单抢走当前工作区菜单的注入目标。
29683
30059
  */
29684
- function selectFilterMenus(menus, menuWorkspaceIds, activeWorkspaceId) {
30060
+ function selectFilterMenus(menus, menuWorkspaceIds, activeWorkspaceId, allowUnidentified) {
29685
30061
  const visible = menus.filter(isVisibleMenu);
29686
- const candidates = (visible.length > 0 ? visible : menus).filter(isWorkspaceFilterMenu);
29687
- if (candidates.length > 0) return [candidates[candidates.length - 1]];
29688
- const fallback = (visible.length > 0 ? visible : menus).filter((menu) => menuWorkspaceIds.get(menu) === void 0 && (activeWorkspaceId === void 0 || resolveWorkspaceId(menu) !== activeWorkspaceId) && menu.querySelector(`[data-codingns-hidden-workspace-menu]`) === null);
30062
+ const candidates = visible.filter(isWorkspaceFilterMenu);
30063
+ const owned = candidates.filter((menu) => menu.querySelector(`[${WORKSPACE_SESSION_HIDDEN_FILTER_ATTRIBUTE}]`) !== null);
30064
+ if (owned.length > 0) return [owned[owned.length - 1]];
30065
+ if (allowUnidentified && candidates.length > 0) return [candidates[candidates.length - 1]];
30066
+ if (!allowUnidentified) return [];
30067
+ const fallback = visible.filter((menu) => menuWorkspaceIds.get(menu) === void 0 && (activeWorkspaceId === void 0 || resolveWorkspaceId(menu) !== activeWorkspaceId) && menu.querySelector(`[data-codingns-hidden-workspace-menu]`) === null);
29689
30068
  return fallback.length > 0 ? [fallback[fallback.length - 1]] : [];
29690
30069
  }
29691
- function selectWorkspaceMenus(menus) {
29692
- const visible = menus.filter(isVisibleMenu);
29693
- const candidates = (visible.length > 0 ? visible : menus).filter((menu) => !isWorkspaceFilterMenu(menu));
29694
- const available = candidates.length > 0 ? candidates : visible.length > 0 ? visible : menus;
29695
- return available.length > 0 ? [available[available.length - 1]] : [];
30070
+ function selectWorkspaceMenus(menus, allowUnidentified, menuWorkspaceIds) {
30071
+ const candidates = menus.filter(isVisibleMenu).filter((menu) => !isWorkspaceFilterMenu(menu) && (allowUnidentified || resolveWorkspaceId(menu) !== void 0 || menuWorkspaceIds.get(menu) !== void 0));
30072
+ return candidates.length > 0 ? [candidates[candidates.length - 1]] : [];
29696
30073
  }
29697
30074
  function isVisibleMenu(menu) {
29698
30075
  if (menu.hasAttribute("hidden") || menu.getAttribute("aria-hidden") === "true" || menu.getAttribute("data-state") === "closed") return false;
@@ -29700,7 +30077,8 @@ window.__ModuleLoader__.load({
29700
30077
  return style.display !== "none" && style.visibility !== "hidden";
29701
30078
  }
29702
30079
  function isWorkspaceFilterMenu(element) {
29703
- return /(?:分组方式|排序方式|筛选会话|group(?:ing)?|sort(?:ing)?|filter(?:\s+sessions?)?)/iu.test(element.textContent ?? "");
30080
+ const label = `${element.textContent ?? ""} ${element.getAttribute("aria-label") ?? ""} ${element.getAttribute("title") ?? ""}`;
30081
+ return WORKSPACE_FILTER_PATTERN.test(label);
29704
30082
  }
29705
30083
  function injectWorkspaceMenuActions(menus, dom, activeWorkspaceId, workspaceIds, menuWorkspaceIds, onHide, t) {
29706
30084
  for (const menu of menus) {
@@ -30550,13 +30928,14 @@ window.__ModuleLoader__.load({
30550
30928
  setProviderId(selection.providerId ?? null);
30551
30929
  }
30552
30930
  const intervalMins = props.getRefreshIntervalMins?.() ?? require_native_workspace_store.DEFAULT_SUBSCRIPTION_USAGE_SETTINGS.refreshIntervalMins;
30553
- const cacheKey = `${adapterId}|${selection.providerId ?? ""}|${selection.modelId ?? ""}`;
30931
+ const cacheKey = `${adapterId}|${selection.providerId ?? ""}|${selection.modelId ?? ""}|${sessionId}`;
30554
30932
  const cached = subscriptionUsageCache.get(cacheKey);
30555
30933
  if (cached !== void 0 && cached.usage.provider?.capability !== "new-api" && isSubscriptionUsageFresh(cached.capturedAt, Date.now(), intervalMins)) {
30556
30934
  if (active) setUsage(cached.usage);
30557
30935
  return;
30558
30936
  }
30559
30937
  const next = await loadUsage(props.rpc, cacheKey, {
30938
+ sessionId,
30560
30939
  adapterId,
30561
30940
  ...selection.providerId ? { providerId: selection.providerId } : {},
30562
30941
  ...selection.modelId ? { modelId: selection.modelId } : {}
@@ -30609,6 +30988,7 @@ window.__ModuleLoader__.load({
30609
30988
  (async () => {
30610
30989
  try {
30611
30990
  const result = await require_model_catalog_cache.callCliRpc(props.rpc, "subscription/reset", {
30991
+ sessionId: props.sessionId?.trim() ?? "",
30612
30992
  adapterId,
30613
30993
  ...providerId === null ? {} : { providerId }
30614
30994
  });
@@ -32953,7 +33333,8 @@ window.__ModuleLoader__.load({
32953
33333
  adapterId: next.adapterId,
32954
33334
  ...next.modelId ? { modelId: next.modelId } : {},
32955
33335
  ...next.effortId ? { effortId: next.effortId } : {},
32956
- ...next.serviceTierId ? { serviceTierId: next.serviceTierId } : {}
33336
+ ...next.serviceTierId ? { serviceTierId: next.serviceTierId } : {},
33337
+ ...next.translationEnabled === void 0 ? {} : { translationEnabled: next.translationEnabled }
32957
33338
  };
32958
33339
  selections.set(sessionId, normalized);
32959
33340
  selectionLastUsed.set(sessionId, Date.now());
@@ -33321,6 +33702,7 @@ window.__ModuleLoader__.load({
33321
33702
  adapterId,
33322
33703
  modelId: model.id,
33323
33704
  effortId: effort ?? "default",
33705
+ translationEnabled: currentSelection.translationEnabled === true,
33324
33706
  ...tier ? { serviceTierId: tier } : {}
33325
33707
  });
33326
33708
  };
@@ -33370,6 +33752,7 @@ window.__ModuleLoader__.load({
33370
33752
  adapterId: selection.adapterId,
33371
33753
  modelId: next.id,
33372
33754
  effortId: nextEffort ?? "default",
33755
+ translationEnabled: selection.translationEnabled === true,
33373
33756
  ...nextTier ? { serviceTierId: nextTier } : {}
33374
33757
  });
33375
33758
  setOpen(false);
@@ -33382,6 +33765,7 @@ window.__ModuleLoader__.load({
33382
33765
  adapterId: selection.adapterId,
33383
33766
  modelId: model.id,
33384
33767
  effortId: effort,
33768
+ translationEnabled: selection.translationEnabled === true,
33385
33769
  ...tier ? { serviceTierId: tier } : {}
33386
33770
  });
33387
33771
  setOpen(false);
@@ -33398,7 +33782,19 @@ window.__ModuleLoader__.load({
33398
33782
  adapterId: selection.adapterId,
33399
33783
  modelId: model.id,
33400
33784
  ...selection.effortId ? { effortId: selection.effortId } : {},
33401
- serviceTierId
33785
+ serviceTierId,
33786
+ translationEnabled: selection.translationEnabled === true
33787
+ });
33788
+ };
33789
+ const translationAvailable = require_session_translation.isSessionTranslationAdapter(selection.adapterId);
33790
+ const toggleTranslation = (next) => {
33791
+ if (model === void 0) return;
33792
+ update({
33793
+ adapterId: selection.adapterId,
33794
+ modelId: model.id,
33795
+ ...selection.effortId ? { effortId: selection.effortId } : {},
33796
+ ...selection.serviceTierId ? { serviceTierId: selection.serviceTierId } : {},
33797
+ translationEnabled: next
33402
33798
  });
33403
33799
  };
33404
33800
  const menu = loading ? [(0, react.createElement)("div", {
@@ -33411,6 +33807,15 @@ window.__ModuleLoader__.load({
33411
33807
  "aria-hidden": true,
33412
33808
  style: modelSpinnerStyle
33413
33809
  }), (0, react.createElement)("span", void 0, t("cli.loadingModel")))] : pane === "root" ? [
33810
+ ...translationAvailable ? [(0, react.createElement)("div", {
33811
+ key: "translation",
33812
+ className: "codingns4dsh-translation-toggle",
33813
+ style: serviceTierRowStyle
33814
+ }, (0, react.createElement)("span", { style: serviceTierLabelStyle }, t("cli.translationConversation")), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
33815
+ checked: selection.translationEnabled === true,
33816
+ onChange: toggleTranslation,
33817
+ label: t("cli.translationConversationToggle")
33818
+ }))] : [],
33414
33819
  ...serviceTierAvailable ? [(0, react.createElement)("div", {
33415
33820
  key: "service-tier",
33416
33821
  className: "codingns4dsh-service-tier",
@@ -33602,6 +34007,7 @@ window.__ModuleLoader__.load({
33602
34007
  padding: "0 10px",
33603
34008
  display: "flex",
33604
34009
  alignItems: "center",
34010
+ justifyContent: "space-between",
33605
34011
  gap: 8,
33606
34012
  fontSize: require_theme.uiFontSize(14),
33607
34013
  lineHeight: "22px"
@@ -38816,10 +39222,10 @@ ${scope} [data-dockkit-strip-tabs] {
38816
39222
  }));
38817
39223
  }
38818
39224
  function readSessionIds(value) {
38819
- const record = asRecord$4(value);
39225
+ const record = asRecord$6(value);
38820
39226
  if (record === void 0) return [];
38821
39227
  const result = /* @__PURE__ */ new Set();
38822
- const byId = asRecord$4(record.byId);
39228
+ const byId = asRecord$6(record.byId);
38823
39229
  if (byId !== void 0) {
38824
39230
  for (const sessionId of Object.keys(byId)) if (sessionId.trim() !== "") result.add(sessionId);
38825
39231
  }
@@ -38835,7 +39241,7 @@ ${scope} [data-dockkit-strip-tabs] {
38835
39241
  const items = record[key];
38836
39242
  if (!Array.isArray(items)) continue;
38837
39243
  for (const item of items) {
38838
- const itemRecord = asRecord$4(item);
39244
+ const itemRecord = asRecord$6(item);
38839
39245
  const candidate = itemRecord?.sessionId ?? itemRecord?.id;
38840
39246
  if (typeof candidate === "string" && candidate.trim() !== "") result.add(candidate);
38841
39247
  }
@@ -38876,10 +39282,10 @@ ${scope} [data-dockkit-strip-tabs] {
38876
39282
  const iterator = (await api.follow())[Symbol.asyncIterator]();
38877
39283
  const first = await iterator.next();
38878
39284
  await iterator.return?.();
38879
- const unwrapped = asRecord$4(unwrapRemoteValue(first.value));
38880
- const value = Array.isArray(unwrapped?.items) ? unwrapped : asRecord$4(unwrapped?.value);
39285
+ const unwrapped = asRecord$6(unwrapRemoteValue(first.value));
39286
+ const value = Array.isArray(unwrapped?.items) ? unwrapped : asRecord$6(unwrapped?.value);
38881
39287
  return (Array.isArray(value?.items) ? value.items : []).flatMap((item) => {
38882
- const record = asRecord$4(item);
39288
+ const record = asRecord$6(item);
38883
39289
  const workspaceId = record?.workspaceId;
38884
39290
  if (typeof workspaceId !== "string" || workspaceId.trim() === "") return [];
38885
39291
  const rawSessionIds = record?.sessionIds;
@@ -38893,16 +39299,16 @@ ${scope} [data-dockkit-strip-tabs] {
38893
39299
  });
38894
39300
  }
38895
39301
  async function readCurrentSession(api, sessionId, uiContext) {
38896
- const store = asRecord$4(require_native_workspace_store.readNativeService(uiContext, "sessions", "list"));
39302
+ const store = asRecord$6(require_native_workspace_store.readNativeService(uiContext, "sessions", "list"));
38897
39303
  if (typeof store?.getSnapshot === "function") {
38898
- const snapshot = asRecord$4(store.getSnapshot());
38899
- const byId = asRecord$4(snapshot?.byId);
38900
- if (byId !== void 0) return sessionItem(asRecord$4(byId[sessionId]));
38901
- return sessionItem(asRecord$4((Array.isArray(snapshot?.items) ? snapshot.items : []).find((item) => asRecord$4(item)?.sessionId === sessionId)));
39304
+ const snapshot = asRecord$6(store.getSnapshot());
39305
+ const byId = asRecord$6(snapshot?.byId);
39306
+ if (byId !== void 0) return sessionItem(asRecord$6(byId[sessionId]));
39307
+ return sessionItem(asRecord$6((Array.isArray(snapshot?.items) ? snapshot.items : []).find((item) => asRecord$6(item)?.sessionId === sessionId)));
38902
39308
  }
38903
39309
  if (api?.list === void 0) return void 0;
38904
39310
  const result = await readLegacySessionList(api);
38905
- return sessionItem(asRecord$4((Array.isArray(result?.items) ? result.items : []).find((item) => asRecord$4(item)?.sessionId === sessionId)));
39311
+ return sessionItem(asRecord$6((Array.isArray(result?.items) ? result.items : []).find((item) => asRecord$6(item)?.sessionId === sessionId)));
38906
39312
  }
38907
39313
  function sessionItem(session) {
38908
39314
  if (session === void 0) return void 0;
@@ -38917,7 +39323,7 @@ ${scope} [data-dockkit-strip-tabs] {
38917
39323
  async function readLegacySessionList(api) {
38918
39324
  const previous = legacySessionLists.get(api);
38919
39325
  if (previous !== void 0 && Date.now() < previous.expiresAt) return previous.promise;
38920
- const promise = Promise.resolve().then(() => api.list({})).then((value) => asRecord$4(unwrapRemoteValue(value)));
39326
+ const promise = Promise.resolve().then(() => api.list({})).then((value) => asRecord$6(unwrapRemoteValue(value)));
38921
39327
  const entry = {
38922
39328
  promise,
38923
39329
  expiresAt: Infinity
@@ -38941,11 +39347,11 @@ ${scope} [data-dockkit-strip-tabs] {
38941
39347
  const result = value.replaceAll("\\", "/").replace(/\/+/u, "/").replace(/\/+$/u, "") || "/";
38942
39348
  return /^[A-Za-z]:\//u.test(result) ? result.toLowerCase() : result;
38943
39349
  }
38944
- function asRecord$4(value) {
39350
+ function asRecord$6(value) {
38945
39351
  return typeof value === "object" && value !== null ? value : void 0;
38946
39352
  }
38947
39353
  function unwrapRemoteValue(value) {
38948
- const record = asRecord$4(value);
39354
+ const record = asRecord$6(value);
38949
39355
  if (record === void 0 || typeof record.ok !== "boolean") return value;
38950
39356
  return record.ok ? record.value : void 0;
38951
39357
  }
@@ -40682,7 +41088,7 @@ ${scope} [data-dockkit-strip-tabs] {
40682
41088
  function registerSessionChangedFilesView(ctx, rpc, remote, locale) {
40683
41089
  const runtime = globalThis;
40684
41090
  if (runtime.__CODINGNS4DSH_SESSION_CHANGED_FILES_VIEW__ !== void 0) return void 0;
40685
- const slots = asRecord$3(ctx)?.slots;
41091
+ const slots = asRecord$5(ctx)?.slots;
40686
41092
  if (typeof slots?.inject !== "function" || typeof slots.register !== "function") return void 0;
40687
41093
  if (isSlotIdRegistered(slots, "conversation.view", "codingns4dsh/session-changed-files")) return void 0;
40688
41094
  if (isSlotIdRegistered(slots, "conversation.session.header.actions", `codingns4dsh/session-changed-files/counter`)) return void 0;
@@ -40997,7 +41403,7 @@ ${scope} [data-dockkit-strip-tabs] {
40997
41403
  function fileIcon(name) {
40998
41404
  return name.includes(".") ? "·" : "□";
40999
41405
  }
41000
- function asRecord$3(value) {
41406
+ function asRecord$5(value) {
41001
41407
  return typeof value === "object" && value !== null ? value : void 0;
41002
41408
  }
41003
41409
  async function call(rpc, endpoint, payload) {
@@ -41326,8 +41732,39 @@ ${scope} [data-dockkit-strip-tabs] {
41326
41732
  },
41327
41733
  settingsPanel: FileManagementPanel
41328
41734
  };
41735
+ //#endregion
41736
+ //#region src/client/settings-anchor.ts
41737
+ /**
41738
+ * 设置区锚点解析。
41739
+ *
41740
+ * 账户入口(登录/注销、PeerHost 管理)是命令式挂到 DSH 侧栏底部的,必须先找到
41741
+ * 一个稳定的落点。历史上只认原生齿轮按钮 `button[aria-label="设置"]`,它在
41742
+ * DSH Desktop 下**根本不会渲染**:
41743
+ *
41744
+ * 1. Desktop 的 preload 无条件暴露 `dshDesktop`
41745
+ * (`exposeInMainWorld("dshDesktop", …)`),
41746
+ * 2. `@deepseek-ai/dsh-client-ui-settings-account` 的 `apply` 见到该全局就注册
41747
+ * `settings.launcher` 单槽位(账户菜单),
41748
+ * 3. `@deepseek-ai/dsh-client-ui-settings-general` 渲染
41749
+ * `renderSlot("settings.launcher", …, { fallback: 齿轮按钮 })`,
41750
+ * 而单槽位一旦有条目就只渲染条目,fallback 不进入 DOM。
41751
+ *
41752
+ * 结果就是入口锚点为空、`scan()` 直接返回,左下角既没有账户图标也进不去
41753
+ * PeerHost 管理。这里把槽位出口 `[data-slot="settings.launcher"]` 作为兜底锚点:
41754
+ * 它由 DSH 槽位契约提供(`dsh-client-ui-renderer` 的 `SlotOutlet` 始终输出
41755
+ * `data-slot="<slotKey>"`,渲染 fallback 时也输出),与具体按钮实现无关,
41756
+ * 因此 Web 与 Desktop 都成立。
41757
+ */
41758
+ /**
41759
+ * 原生设置齿轮按钮;Web 浏览器下 `settings.launcher` 无条目,由它作为 fallback 渲染。
41760
+ *
41761
+ * DSH 的 `aria-label` 来自 locale 词典(`dsh-client-ui-settings-general` 的 `trigger`
41762
+ * 键:zh 为「设置」、en 为 `Settings`),因此选择器必须同时覆盖 DSH 0.2.0-rc.2 支持
41763
+ * 的两种语言,不能把中文标签当作唯一的语言无关契约。其它语言走槽位出口兜底。
41764
+ */
41765
+ const SETTINGS_BUTTON_SELECTOR = "button[aria-label=\"设置\"], button[aria-label=\"Settings\"]";
41329
41766
  /** 锚点候选按优先级排列:原生齿轮优先,槽位出口兜底。 */
41330
- const SETTINGS_ANCHOR_SELECTORS = ["button[aria-label=\"设置\"], button[aria-label=\"Settings\"]", "[data-slot=\"settings.launcher\"]"];
41767
+ const SETTINGS_ANCHOR_SELECTORS = [SETTINGS_BUTTON_SELECTOR, "[data-slot=\"settings.launcher\"]"];
41331
41768
  /**
41332
41769
  * 按优先级挑选设置区锚点。
41333
41770
  *
@@ -41444,17 +41881,18 @@ ${scope} [data-dockkit-strip-tabs] {
41444
41881
  observeDom = true;
41445
41882
  return;
41446
41883
  }
41447
- const match = resolveSettingsAnchor((selector) => root.querySelector(selector));
41448
- if (match === null) {
41449
- observeDom = true;
41450
- return;
41451
- }
41452
- const container = settingsAnchorContainer(match);
41884
+ const existing = root.querySelector(`button[${ACCOUNT_ATTRIBUTE}]`);
41885
+ let match = existing?.parentElement === null || existing?.parentElement === void 0 ? null : matchForAccountContainer(existing.parentElement);
41886
+ let container = match === null ? null : settingsAnchorContainer(match);
41453
41887
  if (container === null) {
41888
+ match = resolveSettingsAnchor((selector) => root.querySelector(selector));
41889
+ container = match === null ? null : settingsAnchorContainer(match);
41890
+ }
41891
+ if (match === null || container === null) {
41454
41892
  observeDom = true;
41455
41893
  return;
41456
41894
  }
41457
- let button = container.querySelector(`button[${ACCOUNT_ATTRIBUTE}]`);
41895
+ let button = existing ?? container.querySelector(`button[${ACCOUNT_ATTRIBUTE}]`);
41458
41896
  if (button === null) {
41459
41897
  button = createAccountButton(root, isDesktopPage());
41460
41898
  button.addEventListener("click", (event) => {
@@ -41463,8 +41901,13 @@ ${scope} [data-dockkit-strip-tabs] {
41463
41901
  toggleMenu(button);
41464
41902
  });
41465
41903
  }
41466
- if (match.kind === "launcher-slot") container.append(button);
41467
- else if (button.parentElement !== container) container.insertBefore(button, container.firstChild);
41904
+ if (button.parentElement !== container) {
41905
+ if (match.kind === "launcher-slot") container.append(button);
41906
+ else container.insertBefore(button, container.firstChild);
41907
+ }
41908
+ root.querySelectorAll(`button[${ACCOUNT_ATTRIBUTE}]`).forEach((node) => {
41909
+ if (node !== button) node.remove();
41910
+ });
41468
41911
  observeDom = false;
41469
41912
  observer?.disconnect();
41470
41913
  container.dataset.codingnsAccountRow = "true";
@@ -41481,12 +41924,9 @@ ${scope} [data-dockkit-strip-tabs] {
41481
41924
  };
41482
41925
  const updateAccountLayout = (container, anchor, button, kind) => {
41483
41926
  if (kind === "launcher-slot") {
41484
- const row = container.parentElement;
41485
- if (row === null) return;
41486
41927
  const mode = "desktop";
41487
41928
  if (button.dataset.codingnsWide === mode) return;
41488
41929
  button.dataset.codingnsWide = mode;
41489
- row.style.flexWrap = "nowrap";
41490
41930
  button.style.order = "2";
41491
41931
  button.style.marginLeft = "auto";
41492
41932
  return;
@@ -41528,9 +41968,8 @@ ${scope} [data-dockkit-strip-tabs] {
41528
41968
  };
41529
41969
  const observerCallback = () => {
41530
41970
  if (disposed || rendering || renderQueued) return;
41531
- const match = resolveSettingsAnchor((selector) => root.querySelector(selector));
41532
41971
  const button = root.querySelector(`button[${ACCOUNT_ATTRIBUTE}]`);
41533
- if (match !== null && button !== null && settingsAnchorContainer(match) === button.parentElement) return;
41972
+ if (button !== null && button.parentElement !== null && matchForAccountContainer(button.parentElement) !== null) return;
41534
41973
  renderQueued = true;
41535
41974
  queueMicrotask(() => {
41536
41975
  renderQueued = false;
@@ -41987,6 +42426,17 @@ ${scope} [data-dockkit-strip-tabs] {
41987
42426
  function isWide(parent, settings) {
41988
42427
  return parent.clientWidth > 96 || getComputedStyle(settings).display !== "none" && settings.getBoundingClientRect().width > 70;
41989
42428
  }
42429
+ function matchForAccountContainer(container) {
42430
+ if (container.matches("[data-slot=\"settings.launcher\"]")) return {
42431
+ kind: "launcher-slot",
42432
+ node: container
42433
+ };
42434
+ const settings = container.querySelector(SETTINGS_BUTTON_SELECTOR);
42435
+ return settings === null || settings.parentElement !== container ? null : {
42436
+ kind: "settings-button",
42437
+ node: settings
42438
+ };
42439
+ }
41990
42440
  function formatPercent(value) {
41991
42441
  return `${Math.round(value)}%`;
41992
42442
  }
@@ -42998,6 +43448,7 @@ ${scope} [data-dockkit-strip-tabs] {
42998
43448
  "codingnsTerminal/shells",
42999
43449
  "codingnsTerminal/write",
43000
43450
  "codingnsAssistantNotifications/sourceStream",
43451
+ "fileUploads/upload",
43001
43452
  "fileReferences/list",
43002
43453
  "skills/list"
43003
43454
  ]);
@@ -43059,6 +43510,8 @@ ${scope} [data-dockkit-strip-tabs] {
43059
43510
  let localHostId;
43060
43511
  let hasAggregate = false;
43061
43512
  let sessionWorkspaceIds = /* @__PURE__ */ new Map();
43513
+ const sessionRunningOverrides = /* @__PURE__ */ new Map();
43514
+ const completedSessionTransitions = /* @__PURE__ */ new Set();
43062
43515
  const listeners = /* @__PURE__ */ new Set();
43063
43516
  return {
43064
43517
  hasAggregate: () => hasAggregate,
@@ -43066,6 +43519,11 @@ ${scope} [data-dockkit-strip-tabs] {
43066
43519
  const next = projectAggregate(results);
43067
43520
  const nextWorkspaceIds = new Set(next.workspaces.map((workspace) => workspace.workspaceId));
43068
43521
  const nextSessionIds = new Set(next.sessions.map((session) => session.sessionId));
43522
+ for (const candidate of next.sessions) {
43523
+ const previous = sessions.find((session) => session.sessionId === candidate.sessionId);
43524
+ if (previous?.running !== true || candidate.running !== false) continue;
43525
+ if (sessionRunningOverrides.get(candidate.sessionId) !== true || candidate.updatedAt > previous.updatedAt) completedSessionTransitions.add(candidate.sessionId);
43526
+ }
43069
43527
  const retainedSessions = [...next.sessions];
43070
43528
  const retainedSessionWorkspaceIds = new Map(next.sessionWorkspaceIds);
43071
43529
  const retainedWorkspaces = next.workspaces.map((workspace) => ({
@@ -43094,7 +43552,7 @@ ${scope} [data-dockkit-strip-tabs] {
43094
43552
  const projectedNext = {
43095
43553
  ...next,
43096
43554
  workspaces: retainedWorkspaces,
43097
- sessions: retainedSessions,
43555
+ sessions: retainedSessions.map((session) => applyRunningOverride(session, sessionRunningOverrides)),
43098
43556
  sessionWorkspaceIds: retainedSessionWorkspaceIds
43099
43557
  };
43100
43558
  const nextLocalHostId = projectedNext.localHostId ?? localHostId;
@@ -43109,6 +43567,23 @@ ${scope} [data-dockkit-strip-tabs] {
43109
43567
  for (const listener of [...listeners]) listener();
43110
43568
  return true;
43111
43569
  },
43570
+ setSessionRunning(sessionId, running) {
43571
+ if (sessionRunningOverrides.get(sessionId) === running) return false;
43572
+ sessionRunningOverrides.set(sessionId, running);
43573
+ const current = sessions.find((session) => session.sessionId === sessionId);
43574
+ if (current === void 0 || current.running === running) return false;
43575
+ sessions = sessions.map((session) => session.sessionId === sessionId ? {
43576
+ ...session,
43577
+ running
43578
+ } : session);
43579
+ for (const listener of [...listeners]) listener();
43580
+ return true;
43581
+ },
43582
+ consumeCompletedSessionTransitions() {
43583
+ const result = [...completedSessionTransitions];
43584
+ completedSessionTransitions.clear();
43585
+ return result;
43586
+ },
43112
43587
  setWorkspaceOrder(nextOrderedWorkspaceIds) {
43113
43588
  const next = normalizeWorkspaceOrder(nextOrderedWorkspaceIds);
43114
43589
  if (sameIds(orderedWorkspaceIds, next)) return false;
@@ -43215,6 +43690,13 @@ ${scope} [data-dockkit-strip-tabs] {
43215
43690
  ...session.parentSessionId === void 0 ? {} : { parentSessionId: require_primitives_adapter.createVirtualSessionId(session.scope.targetHostId ?? session.scope.hostId, session.parentSessionId) }
43216
43691
  };
43217
43692
  }
43693
+ function applyRunningOverride(session, overrides) {
43694
+ const running = overrides.get(session.sessionId);
43695
+ return running === void 0 || running === session.running ? session : {
43696
+ ...session,
43697
+ running
43698
+ };
43699
+ }
43218
43700
  function sameWorkspaces(previous, next) {
43219
43701
  if (previous.length !== next.length) return false;
43220
43702
  return previous.every((workspace, index) => {
@@ -43335,12 +43817,12 @@ ${scope} [data-dockkit-strip-tabs] {
43335
43817
  const refreshStates = /* @__PURE__ */ new WeakMap();
43336
43818
  /** 合并并按 Host 侧顺序排列工作区;快照形状不符时原样返回,注入失败不影响本机数据。 */
43337
43819
  function mergeWorkspaceSnapshot(snapshot, virtual, orderedWorkspaceIds, localHostId, hasAggregate) {
43338
- const record = asRecord$2(snapshot);
43820
+ const record = asRecord$4(snapshot);
43339
43821
  if (record === null || !Array.isArray(record.items)) return snapshot;
43340
43822
  const baseItems = record.items;
43341
43823
  const byId = new Map(virtual.map((workspace) => [workspace.workspaceId, workspace]));
43342
43824
  const items = hasAggregate ? baseItems.flatMap((item) => {
43343
- const workspace = asRecord$2(item);
43825
+ const workspace = asRecord$4(item);
43344
43826
  const id = workspace?.workspaceId;
43345
43827
  const ref = typeof id === "string" ? require_primitives_adapter.parseVirtualWorkspaceId(id) : null;
43346
43828
  if (ref === null || ref.hostId === localHostId || ref.targetHostId === null) return [item];
@@ -43351,7 +43833,7 @@ ${scope} [data-dockkit-strip-tabs] {
43351
43833
  }];
43352
43834
  }) : baseItems;
43353
43835
  const known = new Set(items.flatMap((item) => {
43354
- const workspaceId = asRecord$2(item)?.workspaceId;
43836
+ const workspaceId = asRecord$4(item)?.workspaceId;
43355
43837
  if (typeof workspaceId !== "string") return [];
43356
43838
  const normalized = normalizeWorkspaceId(workspaceId, localHostId);
43357
43839
  return normalized === workspaceId ? [workspaceId] : [workspaceId, normalized];
@@ -43370,7 +43852,7 @@ ${scope} [data-dockkit-strip-tabs] {
43370
43852
  function normalizeRemoteWorkspaceDisplayPaths(items) {
43371
43853
  let changed = false;
43372
43854
  const normalized = items.map((item) => {
43373
- const record = asRecord$2(item);
43855
+ const record = asRecord$4(item);
43374
43856
  const workspaceId = record?.workspaceId;
43375
43857
  if (record === null || typeof workspaceId !== "string" || require_primitives_adapter.parseVirtualWorkspaceId(workspaceId) === null) return item;
43376
43858
  const displayPath = createPeerHostWorkspaceDisplayPath(workspaceId);
@@ -43427,18 +43909,18 @@ ${scope} [data-dockkit-strip-tabs] {
43427
43909
  };
43428
43910
  }
43429
43911
  function mergeBlankSessionCwds(snapshot, workspaceSnapshot) {
43430
- const state = asRecord$2(snapshot);
43912
+ const state = asRecord$4(snapshot);
43431
43913
  const byId = state?.byId;
43432
- const workspaceState = asRecord$2(workspaceSnapshot);
43914
+ const workspaceState = asRecord$4(workspaceSnapshot);
43433
43915
  const items = Array.isArray(workspaceState?.items) ? workspaceState.items : [];
43434
43916
  if (state === null || !isRecord$4(byId) || items.length === 0) return snapshot;
43435
43917
  let changed = false;
43436
43918
  const nextById = { ...byId };
43437
43919
  for (const [id, value] of Object.entries(byId)) {
43438
- const session = asRecord$2(value);
43920
+ const session = asRecord$4(value);
43439
43921
  if (session === null || session.blank !== true) continue;
43440
- const displayPath = asRecord$2(items.find((item) => {
43441
- const record = asRecord$2(item);
43922
+ const displayPath = asRecord$4(items.find((item) => {
43923
+ const record = asRecord$4(item);
43442
43924
  return Array.isArray(record?.sessionIds) && record.sessionIds.includes(id);
43443
43925
  }))?.path;
43444
43926
  if (typeof displayPath !== "string" || displayPath === "" || session.cwd === displayPath) continue;
@@ -43531,18 +44013,18 @@ ${scope} [data-dockkit-strip-tabs] {
43531
44013
  function orderWorkspaceItems(items, orderedWorkspaceIds, localHostId) {
43532
44014
  if (orderedWorkspaceIds.length === 0 || items.length < 2) return items;
43533
44015
  if (localHostId === void 0 && items.some((item) => {
43534
- const workspaceId = asRecord$2(item)?.workspaceId;
44016
+ const workspaceId = asRecord$4(item)?.workspaceId;
43535
44017
  return typeof workspaceId === "string" && require_primitives_adapter.parseVirtualWorkspaceId(workspaceId) === null;
43536
44018
  })) return items;
43537
44019
  const hasLocalItem = localHostId !== void 0 && items.some((item) => {
43538
- const workspaceId = asRecord$2(item)?.workspaceId;
44020
+ const workspaceId = asRecord$4(item)?.workspaceId;
43539
44021
  return typeof workspaceId === "string" && require_primitives_adapter.parseVirtualWorkspaceId(workspaceId) === null;
43540
44022
  });
43541
44023
  const hasLocalOrder = localHostId !== void 0 && orderedWorkspaceIds.some((id) => require_primitives_adapter.parseVirtualWorkspaceId(id)?.hostId === localHostId);
43542
44024
  if (hasLocalItem && !hasLocalOrder) return items;
43543
44025
  const ranks = new Map(orderedWorkspaceIds.map((id, index) => [id, index]));
43544
44026
  const entries = items.map((item, index) => {
43545
- const workspaceId = asRecord$2(item)?.workspaceId;
44027
+ const workspaceId = asRecord$4(item)?.workspaceId;
43546
44028
  return {
43547
44029
  item,
43548
44030
  index,
@@ -43580,7 +44062,7 @@ ${scope} [data-dockkit-strip-tabs] {
43580
44062
  }
43581
44063
  return merged.length === original.length && merged.every((id, index) => id === original[index]) ? void 0 : merged;
43582
44064
  }
43583
- function asRecord$2(value) {
44065
+ function asRecord$4(value) {
43584
44066
  return isRecord$4(value) ? value : null;
43585
44067
  }
43586
44068
  function isRecord$4(value) {
@@ -44400,6 +44882,8 @@ ${scope} [data-dockkit-strip-tabs] {
44400
44882
  peers = /* @__PURE__ */ new Map();
44401
44883
  routes = /* @__PURE__ */ new Map();
44402
44884
  generations = /* @__PURE__ */ new Set();
44885
+ /** 事件早于聚合摘要到达时暂存,待会话进入可见作用域后重放。 */
44886
+ pendingSessionStatuses = /* @__PURE__ */ new Map();
44403
44887
  sequence = 0;
44404
44888
  instanceId = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;
44405
44889
  constructor(options) {
@@ -44408,8 +44892,35 @@ ${scope} [data-dockkit-strip-tabs] {
44408
44892
  setPeers(scopes) {
44409
44893
  this.peers.clear();
44410
44894
  for (const scope of scopes) if (scope.targetHostId !== null) this.peers.set(scope.targetHostId, scope);
44895
+ for (const [key, pending] of this.pendingSessionStatuses) if (pending.scope.targetHostId === null || !this.peers.has(pending.scope.targetHostId)) this.pendingSessionStatuses.delete(key);
44411
44896
  for (const generation of this.generations) this.sync(generation);
44412
44897
  }
44898
+ /** 聚合摘要建立会话作用域后调用,补发此前因作用域未知而暂存的状态事件。 */
44899
+ replayPendingSessionStatuses() {
44900
+ for (const generation of this.generations) {
44901
+ if (generation.clientId === void 0 || generation.controller.signal.aborted) continue;
44902
+ this.replayPendingSessionStatusesForGeneration(generation);
44903
+ }
44904
+ }
44905
+ /**
44906
+ * 摘要从 running 变为 idle、但事件流没有覆盖到这次变化时,补投递完成边沿。
44907
+ * 调用方必须只对确认发生过 true -> false 的会话调用,不能把普通 idle 摘要当未读。
44908
+ */
44909
+ replayCompletedSessionStatus(sessionId, scope) {
44910
+ const frame = {
44911
+ type: "emit",
44912
+ event: "api-session/status",
44913
+ args: [sessionId, false]
44914
+ };
44915
+ let delivered = false;
44916
+ for (const generation of this.generations) {
44917
+ if (generation.clientId === void 0 || generation.controller.signal.aborted || !this.options.accepts(sessionId, scope)) continue;
44918
+ generation.sessionStatuses.delete(sessionId);
44919
+ this.deliverSessionStatus(generation, scope, sessionId, false, frame);
44920
+ delivered = true;
44921
+ }
44922
+ if (!delivered) this.rememberPendingSessionStatus(scope, sessionId, frame);
44923
+ }
44413
44924
  ownsResult(payload) {
44414
44925
  const eventId = record$1(record$1(payload)?.args)?.eventId;
44415
44926
  return typeof eventId === "string" && eventId.startsWith(EVENT_PREFIX);
@@ -44435,6 +44946,7 @@ ${scope} [data-dockkit-strip-tabs] {
44435
44946
  controller: new AbortController(),
44436
44947
  queue: new EventQueue(),
44437
44948
  workers: /* @__PURE__ */ new Map(),
44949
+ sessionStatuses: /* @__PURE__ */ new Map(),
44438
44950
  prepared: false
44439
44951
  };
44440
44952
  const abort = () => {
@@ -44479,6 +44991,7 @@ ${scope} [data-dockkit-strip-tabs] {
44479
44991
  for (const worker of generation.workers.values()) this.stop(generation, worker);
44480
44992
  }
44481
44993
  this.routes.clear();
44994
+ this.pendingSessionStatuses.clear();
44482
44995
  }
44483
44996
  sync(generation) {
44484
44997
  if (generation.clientId === void 0 || generation.controller.signal.aborted) return;
@@ -44512,6 +45025,7 @@ ${scope} [data-dockkit-strip-tabs] {
44512
45025
  if (frame?.type !== "ready" || typeof frame.clientId !== "string") throw new Error("远端事件流缺少 ready 帧");
44513
45026
  clientId = frame.clientId;
44514
45027
  attempt = 0;
45028
+ this.replayPendingSessionStatusesForGeneration(generation);
44515
45029
  if (!generation.prepared) {
44516
45030
  generation.prepared = true;
44517
45031
  try {
@@ -44532,7 +45046,12 @@ ${scope} [data-dockkit-strip-tabs] {
44532
45046
  async deliver(generation, worker, scope, clientId, frame, signal) {
44533
45047
  if (frame?.type === "emit" && frame.event === "api-session/status") {
44534
45048
  const args = frame.args;
44535
- if (Array.isArray(args) && typeof args[0] === "string" && typeof args[1] === "boolean" && this.options.accepts(args[0], scope)) generation.queue.push(frame);
45049
+ if (!Array.isArray(args) || typeof args[0] !== "string" || typeof args[1] !== "boolean") return;
45050
+ if (!this.options.accepts(args[0], scope)) {
45051
+ this.rememberPendingSessionStatus(scope, args[0], frame);
45052
+ return;
45053
+ }
45054
+ this.deliverSessionStatus(generation, scope, args[0], args[1], frame);
44536
45055
  return;
44537
45056
  }
44538
45057
  if (frame?.type === "cancel" && typeof frame.eventId === "string") {
@@ -44566,6 +45085,42 @@ ${scope} [data-dockkit-strip-tabs] {
44566
45085
  eventId: id
44567
45086
  });
44568
45087
  }
45088
+ deliverSessionStatus(generation, scope, sessionId, running, frame) {
45089
+ const previous = generation.sessionStatuses.get(sessionId);
45090
+ if (!running && previous !== true) generation.queue.push({
45091
+ ...record$1(frame),
45092
+ args: [sessionId, true]
45093
+ });
45094
+ this.options.onSessionStatus?.(sessionId, running, scope);
45095
+ generation.queue.push(frame);
45096
+ generation.sessionStatuses.set(sessionId, running);
45097
+ }
45098
+ rememberPendingSessionStatus(scope, sessionId, frame) {
45099
+ if (scope.targetHostId === null) return;
45100
+ const key = `${scope.targetHostId}:${sessionId}`;
45101
+ const pending = this.pendingSessionStatuses.get(key) ?? {
45102
+ scope,
45103
+ sessionId,
45104
+ frames: []
45105
+ };
45106
+ const previousArgs = record$1(pending.frames.at(-1))?.args;
45107
+ const args = record$1(frame)?.args;
45108
+ if (Array.isArray(previousArgs) && Array.isArray(args) && previousArgs[1] === args[1]) return;
45109
+ pending.frames.push(frame);
45110
+ if (pending.frames.length > 8) pending.frames.shift();
45111
+ this.pendingSessionStatuses.set(key, pending);
45112
+ }
45113
+ replayPendingSessionStatusesForGeneration(generation) {
45114
+ for (const pending of [...this.pendingSessionStatuses.values()]) {
45115
+ if (!this.options.accepts(pending.sessionId, pending.scope)) continue;
45116
+ this.pendingSessionStatuses.delete(`${pending.scope.targetHostId}:${pending.sessionId}`);
45117
+ for (const frame of pending.frames) {
45118
+ const args = record$1(frame)?.args;
45119
+ if (!Array.isArray(args) || typeof args[1] !== "boolean") continue;
45120
+ this.deliverSessionStatus(generation, pending.scope, pending.sessionId, args[1], frame);
45121
+ }
45122
+ }
45123
+ }
44569
45124
  cancel(generation, worker, eventId, id) {
44570
45125
  worker.pending.delete(eventId);
44571
45126
  this.routes.delete(id);
@@ -44622,110 +45177,6 @@ ${scope} [data-dockkit-strip-tabs] {
44622
45177
  });
44623
45178
  }
44624
45179
  //#endregion
44625
- //#region node_modules/.pnpm/@deepseek-ai+dsh-util-workspace-path@0.2.1-alpha.2_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
44626
- /**
44627
- * The `dsh-resource://file/…` address grammar: how a file is named across the
44628
- * Sidebar and the resource model, built and parsed without touching a
44629
- * filesystem.
44630
- * @module
44631
- */
44632
- /** The scheme and type every file address opens with. */
44633
- const FILE_ADDRESS_PREFIX = "dsh-resource://file/";
44634
- /** Component-encode one id or path segment, keeping `:` literal for drive letters. */
44635
- function encodeSegment(segment) {
44636
- return encodeURIComponent(segment).replace(/%3A/gi, ":");
44637
- }
44638
- /** Encode a `/`-separated path segment by segment. */
44639
- function encodePath(path) {
44640
- return path.split("/").map(encodeSegment).join("/");
44641
- }
44642
- /** Whether a decoded first path segment is a Windows drive (`C:`). */
44643
- function isDriveSegment(segment) {
44644
- return segment !== void 0 && /^[A-Za-z]:$/.test(segment);
44645
- }
44646
- /**
44647
- * Build the address of a file read through one Session.
44648
- * @param sessionId - the Session whose Host workspace resolves the path.
44649
- * @param path - absolute or workspace-relative path; backslashes are normalized to `/`, and leading `./` prefixes are dropped.
44650
- * @returns the `dsh-resource://file/session/<sessionId>/<path>` address.
44651
- */
44652
- function sessionFileAddress(sessionId, path) {
44653
- const normalized = path.replace(/\\/g, "/").replace(/^(?:\.\/)+/, "");
44654
- return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encodePath(normalized)}`;
44655
- }
44656
- /**
44657
- * Read a file address back into its parts without resolving `.` or `..`.
44658
- * Query and fragment suffixes are ignored; encoded path segments are decoded.
44659
- * @param address - a candidate address.
44660
- * @returns the parts, or `undefined` when the string is not a `dsh-resource://file/` URI in a known scope with a path, or a segment is not validly encoded.
44661
- */
44662
- function parseFileAddress(address) {
44663
- try {
44664
- if (!address.startsWith(FILE_ADDRESS_PREFIX)) return void 0;
44665
- const end = address.search(/[?#]/);
44666
- const [scope, ...rest] = address.slice(20, end === -1 ? void 0 : end).split("/");
44667
- if (scope === "session") {
44668
- const [id, ...segments] = rest;
44669
- if (id === void 0 || id === "" || segments.length === 0) return void 0;
44670
- return {
44671
- scope,
44672
- sessionId: decodeURIComponent(id),
44673
- path: segments.map(decodeURIComponent).join("/")
44674
- };
44675
- }
44676
- if (scope === "absolute") {
44677
- const unc = rest[0] === "" && rest.length > 1;
44678
- const segments = (unc ? rest.slice(1) : rest).map(decodeURIComponent);
44679
- if (segments.length === 0 || segments[0] === "") return void 0;
44680
- if (unc) return {
44681
- scope,
44682
- path: `//${segments.join("/")}`
44683
- };
44684
- return {
44685
- scope,
44686
- path: isDriveSegment(segments[0]) ? segments.join("/") : `/${segments.join("/")}`
44687
- };
44688
- }
44689
- return;
44690
- } catch {
44691
- return;
44692
- }
44693
- }
44694
- //#endregion
44695
- //#region src/client/peer-host-file-links.ts
44696
- /**
44697
- * 在原生资源入口规范远端链接,保留资源里的虚拟会话身份和导航参数。
44698
- * 右栏文件树、消息与预览内链接共用这个入口;不扫描消息正文或当前选择。
44699
- */
44700
- function installPeerHostFileLinkRouting(uiContext, workspacePathForSession) {
44701
- const sidebar = uiContext.get("sidebarRight");
44702
- if (typeof sidebar?.openResource !== "function") return void 0;
44703
- const originalOpen = sidebar.openResource;
44704
- const originalOpenIn = sidebar.openResourceIn;
44705
- const resolve = (address, options) => {
44706
- const file = parseFileAddress(address);
44707
- if (file?.scope !== "session") return [address, options];
44708
- const location = normalizePeerHostFileLocation(file.path, workspacePathForSession(file.sessionId));
44709
- if (location.path === file.path) return [address, options];
44710
- const suffix = address.search(/[?#]/u);
44711
- return [sessionFileAddress(file.sessionId, location.path) + (suffix < 0 ? "" : address.slice(suffix)), location.line === void 0 ? options : {
44712
- ...options,
44713
- params: {
44714
- line: location.line,
44715
- ...options?.params
44716
- }
44717
- }];
44718
- };
44719
- const open = (address, options) => originalOpen.call(sidebar, ...resolve(address, options));
44720
- const openIn = originalOpenIn === void 0 ? void 0 : (sessionId, address, options) => originalOpenIn.call(sidebar, sessionId, ...resolve(address, options));
44721
- sidebar.openResource = open;
44722
- if (openIn !== void 0) sidebar.openResourceIn = openIn;
44723
- return () => {
44724
- if (sidebar.openResource === open) sidebar.openResource = originalOpen;
44725
- if (openIn !== void 0 && sidebar.openResourceIn === openIn) sidebar.openResourceIn = originalOpenIn;
44726
- };
44727
- }
44728
- //#endregion
44729
45180
  //#region src/shared/session-reference.ts
44730
45181
  /**
44731
45182
  * 生成 DSH 原生会话引用使用的 canonical URI。
@@ -44832,15 +45283,15 @@ ${scope} [data-dockkit-strip-tabs] {
44832
45283
  async function readRemoteCandidates(value) {
44833
45284
  const result = await value;
44834
45285
  if (Array.isArray(result)) return result.flatMap(asRemoteCandidate);
44835
- const record = asRecord$1(result);
45286
+ const record = asRecord$3(result);
44836
45287
  if (record?.ok === true && Array.isArray(record.value)) return record.value.flatMap(asRemoteCandidate);
44837
- const nested = asRecord$1(record?.ok === true ? record.value : void 0);
45288
+ const nested = asRecord$3(record?.ok === true ? record.value : void 0);
44838
45289
  if (Array.isArray(nested?.candidates)) return nested.candidates.flatMap(asRemoteCandidate);
44839
45290
  if (Array.isArray(record?.candidates)) return record.candidates.flatMap(asRemoteCandidate);
44840
45291
  return [];
44841
45292
  }
44842
45293
  function asRemoteCandidate(value) {
44843
- const record = asRecord$1(value);
45294
+ const record = asRecord$3(value);
44844
45295
  if (record === null || typeof record.sessionId !== "string" || typeof record.mention !== "string" || typeof record.label !== "string") return [];
44845
45296
  return [{
44846
45297
  sessionId: record.sessionId,
@@ -44874,14 +45325,14 @@ ${scope} [data-dockkit-strip-tabs] {
44874
45325
  value: formatSessionReferenceMention(title, session.sessionId)
44875
45326
  };
44876
45327
  }
44877
- function asRecord$1(value) {
45328
+ function asRecord$3(value) {
44878
45329
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
44879
45330
  }
44880
45331
  function readSelectedRemoteHost(service, sessionId) {
44881
45332
  const selection = service?.selection?.getSnapshot();
44882
- const record = asRecord$1(selection);
45333
+ const record = asRecord$3(selection);
44883
45334
  if (record === null) return void 0;
44884
- const selectedSessionId = typeof record.sessionId === "string" ? record.sessionId : asRecord$1(record.subagentAddress)?.parentSessionId;
45335
+ const selectedSessionId = typeof record.sessionId === "string" ? record.sessionId : asRecord$3(record.subagentAddress)?.parentSessionId;
44885
45336
  if (selectedSessionId !== void 0 && selectedSessionId !== sessionId) return void 0;
44886
45337
  const workspaceId = record.workspaceId;
44887
45338
  if (typeof workspaceId !== "string") return void 0;
@@ -44984,10 +45435,6 @@ ${scope} [data-dockkit-strip-tabs] {
44984
45435
  scope.effect(() => transport.watchNavigation(), "codingns4dsh: PeerHost model catalog navigation");
44985
45436
  });
44986
45437
  if (navigation !== void 0) context.resources.add(() => navigation.dispose());
44987
- const fileLinks = context.services.uiContext?.inject(["sidebarRight"], (scope) => {
44988
- scope.effect(() => installPeerHostFileLinkRouting(scope, (sessionId) => projection.sessions().find((session) => session.sessionId === sessionId)?.cwd) ?? (() => void 0), "codingns4dsh: PeerHost file links");
44989
- });
44990
- if (fileLinks !== void 0) context.resources.add(() => fileLinks.dispose());
44991
45438
  if (shim.getMode?.() === "desktop") reconnectNativeEvents(context.services.uiContext);
44992
45439
  }
44993
45440
  const panel = startPeerHostManagementPanel({
@@ -45126,9 +45573,9 @@ ${scope} [data-dockkit-strip-tabs] {
45126
45573
  return (await readPageRpcResponse(response)).result;
45127
45574
  };
45128
45575
  const codingNsCall = async (endpoint, payload, signal) => {
45129
- const result = asRecord(await request("/codingns", endpoint, payload, signal));
45576
+ const result = asRecord$2(await request("/codingns", endpoint, payload, signal));
45130
45577
  if (result?.ok === true) return result.value;
45131
- const error = asRecord(result?.error);
45578
+ const error = asRecord$2(result?.error);
45132
45579
  const failure = new Error(typeof error?.message === "string" ? error.message : `CodingNS RPC 失败: ${endpoint}`);
45133
45580
  if (typeof error?.code === "string") failure.code = error.code;
45134
45581
  throw failure;
@@ -45141,6 +45588,17 @@ ${scope} [data-dockkit-strip-tabs] {
45141
45588
  const scope = scopes.get(virtualSessionId);
45142
45589
  return scope?.targetHostId === parsed.hostId ? scope : void 0;
45143
45590
  };
45591
+ const scopeForFileUpload = (url) => {
45592
+ const virtualSessionId = url.searchParams.get("sessionId");
45593
+ if (virtualSessionId === null) return void 0;
45594
+ const parsed = require_primitives_adapter.parseVirtualSessionId(virtualSessionId);
45595
+ if (parsed === null) return void 0;
45596
+ const scope = scopes.get(virtualSessionId);
45597
+ return scope?.targetHostId === parsed.hostId && scope.sessionId === parsed.sessionId ? {
45598
+ virtualSessionId,
45599
+ scope
45600
+ } : void 0;
45601
+ };
45144
45602
  const sessionExportPath = (url, scope) => {
45145
45603
  const parsed = require_primitives_adapter.parseVirtualSessionId(url.searchParams.get("sessionId") ?? "");
45146
45604
  if (parsed === null || scope.targetHostId !== parsed.hostId) throw new Error("远程 Session 导出作用域不匹配");
@@ -45152,16 +45610,64 @@ ${scope} [data-dockkit-strip-tabs] {
45152
45610
  const raw = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
45153
45611
  return new URL(raw, typeof document === "undefined" ? "http://codingns.invalid" : document.baseURI);
45154
45612
  };
45613
+ const remoteFileUploadFetch = async (url, request, init, virtualSessionId, scope) => {
45614
+ const signal = init?.signal ?? request?.signal;
45615
+ signal?.throwIfAborted();
45616
+ const bodyRequest = new Request(request ?? url.href, init);
45617
+ const bytes = new Uint8Array(await bodyRequest.arrayBuffer());
45618
+ const name = url.searchParams.get("name");
45619
+ const payload = { args: {
45620
+ agentId: virtualSessionId,
45621
+ request: {
45622
+ data: encodeUploadBytesBase64(bytes),
45623
+ ...name === null ? {} : { name }
45624
+ }
45625
+ } };
45626
+ try {
45627
+ return jsonResponse({
45628
+ ok: true,
45629
+ value: decodeNativeResponseBytes(await codingNsCall("peerHost/native", {
45630
+ method: "fileUploads/upload",
45631
+ payload,
45632
+ scope
45633
+ }, signal))
45634
+ });
45635
+ } catch (error) {
45636
+ signal?.throwIfAborted();
45637
+ const code = error.code;
45638
+ return jsonResponse({
45639
+ ok: false,
45640
+ error: {
45641
+ code: typeof code === "string" ? code : "gateway/internal",
45642
+ message: error instanceof Error ? error.message : String(error),
45643
+ details: {}
45644
+ }
45645
+ });
45646
+ }
45647
+ };
45155
45648
  const remoteSessionExportFetch = async (input, init) => {
45156
45649
  if (fetchImpl === void 0) throw new Error("当前页面没有 fetch");
45157
45650
  const request = typeof Request !== "undefined" && input instanceof Request ? input : void 0;
45158
45651
  const method = (init?.method ?? request?.method ?? "GET").toUpperCase();
45159
45652
  const url = readFetchUrl(input);
45653
+ if (url.pathname === "/api/session/uploadFileBinary" && method === "POST") {
45654
+ const parsed = require_primitives_adapter.parseVirtualSessionId(url.searchParams.get("sessionId") ?? "");
45655
+ const uploadScope = scopeForFileUpload(url);
45656
+ if (uploadScope !== void 0 && uploadScope.scope.targetHostId !== null) return remoteFileUploadFetch(url, request, init, uploadScope.virtualSessionId, uploadScope.scope);
45657
+ if (parsed !== null) return jsonResponse({
45658
+ ok: false,
45659
+ error: {
45660
+ code: "PEER_HOST_SCOPE_MISMATCH",
45661
+ message: t("peerHost.sessionUnavailable"),
45662
+ details: {}
45663
+ }
45664
+ }, 409);
45665
+ }
45160
45666
  if (url.pathname !== "/api/session.export" || method !== "GET" && method !== "HEAD") return fetchImpl(input, init);
45161
45667
  const scope = scopeForSessionExport(url);
45162
45668
  if (scope === void 0 || scope.targetHostId === null) return fetchImpl(input, init);
45163
45669
  const headers = init?.headers === void 0 && request === void 0 ? void 0 : Object.fromEntries(new Headers(init?.headers ?? request?.headers).entries());
45164
- const response = asRecord(await codingNsCall("peerHost/request", {
45670
+ const response = asRecord$2(await codingNsCall("peerHost/request", {
45165
45671
  peerHostId: scope.targetHostId,
45166
45672
  scope,
45167
45673
  path: sessionExportPath(url, scope),
@@ -45193,7 +45699,7 @@ ${scope} [data-dockkit-strip-tabs] {
45193
45699
  }
45194
45700
  return;
45195
45701
  }
45196
- const object = asRecord(value);
45702
+ const object = asRecord$2(value);
45197
45703
  if (object === null) return void 0;
45198
45704
  for (const item of Object.values(object)) {
45199
45705
  const found = findScope(item);
@@ -45203,8 +45709,8 @@ ${scope} [data-dockkit-strip-tabs] {
45203
45709
  const scopeForNativeRequest = (method, value) => {
45204
45710
  if (method === "userQuestions/answer" || method === "userQuestions/attachWait") return findScope(questionAgentId(value));
45205
45711
  if (method.startsWith("messageFeedback/") || method === "sessionFeedback/record") {
45206
- const input = asRecord(value);
45207
- const request = asRecord(asRecord(input?.args)?.request ?? input?.request);
45712
+ const input = asRecord$2(value);
45713
+ const request = asRecord$2(asRecord$2(input?.args)?.request ?? input?.request);
45208
45714
  return findScope(request?.sessionId);
45209
45715
  }
45210
45716
  const direct = findScope(value);
@@ -45212,9 +45718,9 @@ ${scope} [data-dockkit-strip-tabs] {
45212
45718
  return method === "session/modelCatalog" && !containsResourceId(value) ? navigationScope() : void 0;
45213
45719
  };
45214
45720
  const scopeForPluginRequest = (endpoint, value) => {
45215
- if ((endpoint === "cli/catalog" || endpoint === "cli/models" || endpoint === "cli/catalog/refresh") && asRecord(value)?.catalogHostId === "local") return void 0;
45721
+ if ((endpoint === "cli/catalog" || endpoint === "cli/models" || endpoint === "cli/catalog/refresh") && asRecord$2(value)?.catalogHostId === "local") return void 0;
45216
45722
  if (endpoint.startsWith("fileManagement/")) {
45217
- const input = asRecord(value);
45723
+ const input = asRecord$2(value);
45218
45724
  const workspace = findScope(input?.workspaceId);
45219
45725
  const session = findScope(input?.sessionId);
45220
45726
  if (hasVirtualResourceScope(value)) {
@@ -45225,7 +45731,7 @@ ${scope} [data-dockkit-strip-tabs] {
45225
45731
  return workspace ?? session;
45226
45732
  }
45227
45733
  if (isDebugPluginEndpoint(endpoint)) {
45228
- const input = asRecord(value);
45734
+ const input = asRecord$2(value);
45229
45735
  return findScope(input?.workspaceId) ?? findScope(input?.sessionId);
45230
45736
  }
45231
45737
  const direct = findScope(value);
@@ -45238,8 +45744,8 @@ ${scope} [data-dockkit-strip-tabs] {
45238
45744
  if (selection !== void 0) {
45239
45745
  const direct = findScope(selection);
45240
45746
  if (direct !== void 0) return direct;
45241
- const selected = asRecord(selection);
45242
- const sessionId = selected?.sessionId ?? asRecord(selected?.subagentAddress)?.parentSessionId;
45747
+ const selected = asRecord$2(selection);
45748
+ const sessionId = selected?.sessionId ?? asRecord$2(selected?.subagentAddress)?.parentSessionId;
45243
45749
  const session = typeof sessionId === "string" ? require_primitives_adapter.parseVirtualSessionId(sessionId) : null;
45244
45750
  const workspace = typeof selected?.workspaceId === "string" ? require_primitives_adapter.parseVirtualWorkspaceId(selected.workspaceId) : null;
45245
45751
  const hostId = session?.hostId ?? workspace?.targetHostId;
@@ -45263,7 +45769,7 @@ ${scope} [data-dockkit-strip-tabs] {
45263
45769
  };
45264
45770
  const containsResourceId = (value) => {
45265
45771
  if (Array.isArray(value)) return value.some(containsResourceId);
45266
- const record = asRecord(value);
45772
+ const record = asRecord$2(value);
45267
45773
  if (record === null) return false;
45268
45774
  for (const [key, child] of Object.entries(record)) {
45269
45775
  if ((key === "workspaceId" || key === "sessionId" || key === "parentSessionId") && typeof child === "string" && child !== "") return true;
@@ -45275,14 +45781,14 @@ ${scope} [data-dockkit-strip-tabs] {
45275
45781
  const containsVirtualResourceId = (value) => {
45276
45782
  if (typeof value === "string") return require_primitives_adapter.parseVirtualSessionId(value) !== null || require_primitives_adapter.parseVirtualWorkspaceId(value) !== null;
45277
45783
  if (Array.isArray(value)) return value.some(containsVirtualResourceId);
45278
- const record = asRecord(value);
45784
+ const record = asRecord$2(value);
45279
45785
  return record !== null && Object.values(record).some(containsVirtualResourceId);
45280
45786
  };
45281
45787
  const hasUnresolvedVirtualNativeScope = (method, value) => {
45282
45788
  if (method === "userQuestions/answer" || method === "userQuestions/attachWait") return false;
45283
45789
  if (method.startsWith("messageFeedback/") || method === "sessionFeedback/record") {
45284
- const input = asRecord(value);
45285
- const request = asRecord(asRecord(input?.args)?.request ?? input?.request);
45790
+ const input = asRecord$2(value);
45791
+ const request = asRecord$2(asRecord$2(input?.args)?.request ?? input?.request);
45286
45792
  return typeof request?.sessionId === "string" && require_primitives_adapter.parseVirtualSessionId(request.sessionId) !== null;
45287
45793
  }
45288
45794
  return containsVirtualResourceId(value);
@@ -45293,13 +45799,13 @@ ${scope} [data-dockkit-strip-tabs] {
45293
45799
  return (key === "sessionId" || key === "parentSessionId" || key === "childSessionId") && parsed !== null && parsed.hostId === (scope.targetHostId ?? scope.hostId) ? parsed.sessionId : value;
45294
45800
  }
45295
45801
  if (Array.isArray(value)) return value.map((item) => rewriteCliPayload(item, scope, key));
45296
- const record = asRecord(value);
45802
+ const record = asRecord$2(value);
45297
45803
  if (record === null) return value;
45298
45804
  return Object.fromEntries(Object.entries(record).map(([childKey, child]) => [childKey, rewriteCliPayload(child, scope, childKey)]));
45299
45805
  };
45300
45806
  const remotePluginRpc = async (scope, endpoint, payload, signal) => {
45301
45807
  if (scope.targetHostId === null) throw new Error("PeerHost 插件请求缺少目标 Host");
45302
- const response = asRecord(await codingNsCall("peerHost/request", {
45808
+ const response = asRecord$2(await codingNsCall("peerHost/request", {
45303
45809
  peerHostId: scope.targetHostId,
45304
45810
  scope,
45305
45811
  path: `/api/codingns/${endpoint}`,
@@ -45308,7 +45814,7 @@ ${scope} [data-dockkit-strip-tabs] {
45308
45814
  rpcId: createRequestId(),
45309
45815
  method: endpoint,
45310
45816
  payload: endpoint.startsWith("git/") ? {
45311
- ...asRecord(payload),
45817
+ ...asRecord$2(payload),
45312
45818
  workspaceId: scope.workspaceId
45313
45819
  } : endpoint.startsWith("cli/") ? rewriteCliPayload(payload, scope) : rewritePluginResourcePayload(payload, scope)
45314
45820
  })
@@ -45318,7 +45824,7 @@ ${scope} [data-dockkit-strip-tabs] {
45318
45824
  if (status < 200 || status >= 300) {
45319
45825
  let error = null;
45320
45826
  try {
45321
- error = asRecord(asRecord(JSON.parse(body))?.error);
45827
+ error = asRecord$2(asRecord$2(JSON.parse(body))?.error);
45322
45828
  } catch {}
45323
45829
  if (typeof error?.code === "string" && typeof error.message === "string") return {
45324
45830
  ok: false,
@@ -45330,21 +45836,21 @@ ${scope} [data-dockkit-strip-tabs] {
45330
45836
  throw new Error(`远端 Host 插件 RPC 失败: HTTP ${status}`);
45331
45837
  }
45332
45838
  try {
45333
- const envelope = asRecord(JSON.parse(body));
45839
+ const envelope = asRecord$2(JSON.parse(body));
45334
45840
  return endpoint.startsWith("git/") ? projectGitWorkspaceIds(envelope?.result, scope.workspaceId, require_primitives_adapter.createVirtualWorkspaceId(scope.targetHostId, scope.workspaceId)) : envelope?.result;
45335
45841
  } catch {
45336
45842
  throw new Error("远端 Host 插件 RPC 响应不是 JSON");
45337
45843
  }
45338
45844
  };
45339
45845
  const mergeRemoteAdapterMap = async (localResult, signal) => {
45340
- const localEnvelope = asRecord(localResult);
45846
+ const localEnvelope = asRecord$2(localResult);
45341
45847
  if (localEnvelope?.ok !== true || !Array.isArray(localEnvelope.value)) return localResult;
45342
45848
  const rows = [...localEnvelope.value];
45343
45849
  for (const [targetHostId, scope] of remoteHostScopes) try {
45344
- const remoteEnvelope = asRecord(await remotePluginRpc(scope, "cli/session/adapter-map", {}, signal));
45850
+ const remoteEnvelope = asRecord$2(await remotePluginRpc(scope, "cli/session/adapter-map", {}, signal));
45345
45851
  if (remoteEnvelope?.ok !== true || !Array.isArray(remoteEnvelope.value)) continue;
45346
45852
  for (const raw of remoteEnvelope.value) {
45347
- const row = asRecord(raw);
45853
+ const row = asRecord$2(raw);
45348
45854
  const sessionId = typeof row?.sessionId === "string" ? row.sessionId : "";
45349
45855
  const adapterId = typeof row?.adapterId === "string" ? row.adapterId : "";
45350
45856
  if (sessionId !== "" && adapterId !== "") rows.push({
@@ -45359,7 +45865,7 @@ ${scope} [data-dockkit-strip-tabs] {
45359
45865
  };
45360
45866
  };
45361
45867
  const openRemoteStream = (method, payload, scope, signal) => (async function* () {
45362
- const opened = asRecord(await codingNsCall("peerHost/nativeStream", {
45868
+ const opened = asRecord$2(await codingNsCall("peerHost/nativeStream", {
45363
45869
  method,
45364
45870
  payload,
45365
45871
  scope
@@ -45368,7 +45874,7 @@ ${scope} [data-dockkit-strip-tabs] {
45368
45874
  if (streamId === "") throw new Error("PeerHost 原生 Remote 未返回 streamId");
45369
45875
  try {
45370
45876
  while (!(signal?.aborted ?? false)) {
45371
- const next = asRecord(await codingNsCall("peerHost/nativeStreamNext", {
45877
+ const next = asRecord$2(await codingNsCall("peerHost/nativeStreamNext", {
45372
45878
  streamId,
45373
45879
  scope
45374
45880
  }, signal));
@@ -45392,6 +45898,9 @@ ${scope} [data-dockkit-strip-tabs] {
45392
45898
  scope
45393
45899
  }, signal),
45394
45900
  accepts: (agentId, scope) => scopes.get(agentId)?.targetHostId === scope.targetHostId,
45901
+ onSessionStatus: (sessionId, running) => {
45902
+ projection.setSessionRunning(sessionId, running);
45903
+ },
45395
45904
  prepare: async () => {
45396
45905
  if (nativeSessionPrepared) return;
45397
45906
  nativeSessionPrepared = true;
@@ -45407,7 +45916,7 @@ ${scope} [data-dockkit-strip-tabs] {
45407
45916
  return pending;
45408
45917
  };
45409
45918
  const rememberCreatedSession = (scope, value) => {
45410
- const sessionId = asRecord(value)?.sessionId;
45919
+ const sessionId = asRecord$2(value)?.sessionId;
45411
45920
  if (typeof sessionId !== "string") return;
45412
45921
  const parsed = require_primitives_adapter.parseVirtualSessionId(sessionId);
45413
45922
  if (parsed === null) return;
@@ -45437,17 +45946,22 @@ ${scope} [data-dockkit-strip-tabs] {
45437
45946
  };
45438
45947
  /** 父会话目录早于聚合到达时,复用新建会话的临时作用域与清理机制。 */
45439
45948
  const rememberSubagentCatalog = (scope, value) => {
45440
- const catalog = asRecord(asRecord(asRecord(value)?.projections)?.values)?.subagentCatalog;
45441
- if (!Array.isArray(catalog)) return;
45949
+ const catalog = asRecord$2(asRecord$2(asRecord$2(value)?.projections)?.values)?.subagentCatalog;
45950
+ if (!Array.isArray(catalog)) return false;
45951
+ let changed = false;
45442
45952
  for (const child of catalog) {
45443
- const id = asRecord(child)?.id;
45444
- if (typeof id === "string" && !scopes.has(id)) rememberCreatedSession(scope, { sessionId: id });
45953
+ const id = asRecord$2(child)?.id;
45954
+ if (typeof id === "string" && !scopes.has(id)) {
45955
+ rememberCreatedSession(scope, { sessionId: id });
45956
+ changed = true;
45957
+ }
45445
45958
  }
45959
+ return changed;
45446
45960
  };
45447
45961
  return {
45448
45962
  hooks: {
45449
45963
  rpc: async ({ method, payload, signal }) => {
45450
- const value = asRecord(payload);
45964
+ const value = asRecord$2(payload);
45451
45965
  const channel = typeof value?.channel === "string" ? value.channel : "/codingns";
45452
45966
  const body = value?.payload;
45453
45967
  if (channel === "/api" && method === "$events/result" && events.ownsResult(body)) try {
@@ -45482,7 +45996,7 @@ ${scope} [data-dockkit-strip-tabs] {
45482
45996
  message: t("peerHost.debugWorkspaceUnavailable")
45483
45997
  }
45484
45998
  };
45485
- const workspaceId = asRecord(body)?.workspaceId;
45999
+ const workspaceId = asRecord$2(body)?.workspaceId;
45486
46000
  const scope = endpoint.startsWith("git/") ? findScope(workspaceId) : scopeForPluginRequest(endpoint, body);
45487
46001
  if (endpoint.startsWith("git/") && typeof workspaceId === "string" && require_primitives_adapter.parseVirtualWorkspaceId(workspaceId) !== null && scope === void 0) return {
45488
46002
  ok: false,
@@ -45525,9 +46039,17 @@ ${scope} [data-dockkit-strip-tabs] {
45525
46039
  payload: body,
45526
46040
  scope
45527
46041
  }, signal));
45528
- if (method === "session/create" || method === "session/fork") {
45529
- rememberCreatedSession(scope, created);
45530
- Promise.resolve(requestPeerHostAggregateRefresh()).catch(() => void 0);
46042
+ const createsSession = method === "session/create" || method === "session/fork";
46043
+ const refreshesNativeList = method === "session/rename" || method === "workspace/archiveSession" || method === "workspace/unarchiveSession";
46044
+ if (createsSession) rememberCreatedSession(scope, created);
46045
+ if (createsSession || refreshesNativeList) {
46046
+ let aggregateRefresh;
46047
+ try {
46048
+ aggregateRefresh = requestPeerHostAggregateRefresh();
46049
+ } catch {
46050
+ aggregateRefresh = void 0;
46051
+ }
46052
+ if (refreshesNativeList) Promise.resolve(aggregateRefresh).then(() => refreshPeerHostNativeSessions(uiContext)).catch(() => void 0);
45531
46053
  }
45532
46054
  return {
45533
46055
  ok: true,
@@ -45549,7 +46071,7 @@ ${scope} [data-dockkit-strip-tabs] {
45549
46071
  return result;
45550
46072
  },
45551
46073
  openStream: ({ method, payload, signal }) => {
45552
- const value = asRecord(payload);
46074
+ const value = asRecord$2(payload);
45553
46075
  const channel = typeof value?.channel === "string" ? value.channel : "/api";
45554
46076
  const body = value?.payload;
45555
46077
  if (channel === "/api" && method === "$events") return events.open((localSignal) => openDshGatewayStream(method, body, localSignal), signal);
@@ -45565,16 +46087,13 @@ ${scope} [data-dockkit-strip-tabs] {
45565
46087
  const stream = openRemoteStream(method, body, scope, signal);
45566
46088
  if (method !== "session/follow") return stream;
45567
46089
  return (async function* () {
45568
- let emitted = false;
45569
46090
  try {
45570
46091
  for await (const chunk of stream) {
45571
- emitted = true;
45572
- rememberSubagentCatalog(scope, chunk);
45573
- scheduleAggregateRefresh();
46092
+ if (rememberSubagentCatalog(scope, chunk)) scheduleAggregateRefresh();
45574
46093
  yield chunk;
45575
46094
  }
45576
46095
  } finally {
45577
- if (!emitted) scheduleAggregateRefresh();
46096
+ if (!(signal?.aborted ?? false)) scheduleAggregateRefresh();
45578
46097
  }
45579
46098
  })();
45580
46099
  }
@@ -45608,7 +46127,7 @@ ${scope} [data-dockkit-strip-tabs] {
45608
46127
  return scope === void 0 ? hasVirtualResourceScope(value) : scope.targetHostId !== null;
45609
46128
  }
45610
46129
  if (method?.startsWith("git/") || method?.startsWith("codingns/git/")) {
45611
- const workspaceId = asRecord(value)?.workspaceId;
46130
+ const workspaceId = asRecord$2(value)?.workspaceId;
45612
46131
  return typeof workspaceId === "string" && require_primitives_adapter.parseVirtualWorkspaceId(workspaceId) !== null;
45613
46132
  }
45614
46133
  const cli = method === void 0 ? void 0 : method === "cli/catalog" || method === "cli/models" ? method : method.startsWith("codingns/cli/") ? method.slice(9) : void 0;
@@ -45681,6 +46200,13 @@ ${scope} [data-dockkit-strip-tabs] {
45681
46200
  }
45682
46201
  refreshModelCatalogForScope(navigationScope());
45683
46202
  const changed = projection.setAggregate(aggregate, orderedWorkspaceIds);
46203
+ for (const sessionId of projection.consumeCompletedSessionTransitions()) {
46204
+ const scope = scopes.get(sessionId);
46205
+ if (scope === void 0) continue;
46206
+ projection.setSessionRunning(sessionId, false);
46207
+ events.replayCompletedSessionStatus(sessionId, scope);
46208
+ }
46209
+ events.replayPendingSessionStatuses();
45684
46210
  const readyHosts = new Set(aggregate.filter((host) => host.availability === "ready" && host.workspaces.length > 0).map((host) => host.targetHostId));
45685
46211
  events.setPeers([...remoteHostScopes.values()].filter((scope) => readyHosts.has(scope.targetHostId)));
45686
46212
  return changed;
@@ -45693,8 +46219,8 @@ ${scope} [data-dockkit-strip-tabs] {
45693
46219
  return endpoint.startsWith("cli/") || endpoint.startsWith("git/") || endpoint.startsWith("debug/") || endpoint.startsWith("fileManagement/") || endpoint === "terminal/status" ? endpoint : void 0;
45694
46220
  }
45695
46221
  function questionAgentId(value) {
45696
- const input = asRecord(value);
45697
- return asRecord(input?.args)?.agentId ?? input?.agentId;
46222
+ const input = asRecord$2(value);
46223
+ return asRecord$2(input?.args)?.agentId ?? input?.agentId;
45698
46224
  }
45699
46225
  function hasVirtualQuestionAgent(method, value) {
45700
46226
  if (method !== "userQuestions/answer" && method !== "userQuestions/attachWait") return false;
@@ -45705,19 +46231,19 @@ ${scope} [data-dockkit-strip-tabs] {
45705
46231
  return endpoint.startsWith("debug/") || endpoint === "terminal/status";
45706
46232
  }
45707
46233
  function hasVirtualResourceScope(value) {
45708
- const input = asRecord(value);
46234
+ const input = asRecord$2(value);
45709
46235
  return typeof input?.workspaceId === "string" && require_primitives_adapter.parseVirtualWorkspaceId(input.workspaceId) !== null || typeof input?.sessionId === "string" && require_primitives_adapter.parseVirtualSessionId(input.sessionId) !== null;
45710
46236
  }
45711
46237
  /** 只投影响应中的工作区标识;Diff、提交正文和文件路径均保持原文。 */
45712
46238
  function projectGitWorkspaceIds(value, workspaceId, virtualWorkspaceId) {
45713
46239
  if (Array.isArray(value)) return value.map((item) => projectGitWorkspaceIds(item, workspaceId, virtualWorkspaceId));
45714
- const record = asRecord(value);
46240
+ const record = asRecord$2(value);
45715
46241
  if (record === null) return value;
45716
46242
  return Object.fromEntries(Object.entries(record).map(([key, child]) => [key, key === "workspaceId" && child === workspaceId ? virtualWorkspaceId : projectGitWorkspaceIds(child, workspaceId, virtualWorkspaceId)]));
45717
46243
  }
45718
46244
  /** 只还原调试与文件请求的顶层资源标识,配置、文件正文和路径必须原样保存。 */
45719
46245
  function rewritePluginResourcePayload(value, scope) {
45720
- const input = asRecord(value);
46246
+ const input = asRecord$2(value);
45721
46247
  if (input === null) return value;
45722
46248
  const result = { ...input };
45723
46249
  const workspace = typeof input.workspaceId === "string" ? require_primitives_adapter.parseVirtualWorkspaceId(input.workspaceId) : null;
@@ -45818,7 +46344,7 @@ ${scope} [data-dockkit-strip-tabs] {
45818
46344
  /** 只借用原生导航数据,不修改选择策略,也不从后台保留会话推断前台。 */
45819
46345
  function readNativeNavigationStore(uiContext) {
45820
46346
  try {
45821
- const store = asRecord(asRecord(uiContext?.get("uiWorkspace"))?.selection);
46347
+ const store = asRecord$2(asRecord$2(uiContext?.get("uiWorkspace"))?.selection);
45822
46348
  if (typeof store?.getSnapshot !== "function" || typeof store.subscribe !== "function") return void 0;
45823
46349
  return store;
45824
46350
  } catch {
@@ -45827,14 +46353,14 @@ ${scope} [data-dockkit-strip-tabs] {
45827
46353
  }
45828
46354
  /** 把虚拟会话摘要并入 session/list 结果;形状不符时保持本机结果不变。 */
45829
46355
  function mergeSessionListResult(result, projection) {
45830
- const envelope = asRecord(result);
46356
+ const envelope = asRecord$2(result);
45831
46357
  if (envelope?.ok !== true) return result;
45832
- const value = asRecord(envelope.value);
46358
+ const value = asRecord$2(envelope.value);
45833
46359
  if (value === null || !Array.isArray(value.items)) return result;
45834
46360
  const virtual = projection.sessions();
45835
46361
  if (virtual.length === 0) return result;
45836
46362
  const known = new Set(value.items.flatMap((item) => {
45837
- const id = asRecord(item)?.sessionId;
46363
+ const id = asRecord$2(item)?.sessionId;
45838
46364
  return typeof id === "string" ? [id] : [];
45839
46365
  }));
45840
46366
  for (const session of virtual) if (session.adapterId !== void 0) publishSessionAdapter(session.sessionId, session.adapterId);
@@ -45850,7 +46376,7 @@ ${scope} [data-dockkit-strip-tabs] {
45850
46376
  function createRequestId() {
45851
46377
  return typeof globalThis.crypto?.randomUUID === "function" ? globalThis.crypto.randomUUID() : `${Date.now()}-${Math.random()}`;
45852
46378
  }
45853
- function asRecord(value) {
46379
+ function asRecord$2(value) {
45854
46380
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
45855
46381
  }
45856
46382
  /**
@@ -45869,8 +46395,8 @@ ${scope} [data-dockkit-strip-tabs] {
45869
46395
  }
45870
46396
  const metadata = fields.get("metadata");
45871
46397
  if (typeof metadata !== "string") throw new TypeError("connection: invalid binary response fields");
45872
- const envelope = asRecord(JSON.parse(metadata));
45873
- const result = asRecord(envelope?.result);
46398
+ const envelope = asRecord$2(JSON.parse(metadata));
46399
+ const result = asRecord$2(envelope?.result);
45874
46400
  if (envelope === null || result === null) throw new TypeError("connection: invalid server-response envelope");
45875
46401
  fields.delete("metadata");
45876
46402
  if (result.ok !== true) {
@@ -45881,7 +46407,7 @@ ${scope} [data-dockkit-strip-tabs] {
45881
46407
  if (!Array.isArray(attachments) || attachments.length === 0) throw new TypeError("connection: invalid binary response result");
45882
46408
  const root = { value: result.value };
45883
46409
  for (const rawAttachment of attachments) {
45884
- const attachment = asRecord(rawAttachment);
46410
+ const attachment = asRecord$2(rawAttachment);
45885
46411
  const path = attachment?.path;
45886
46412
  const part = attachment?.part;
45887
46413
  if (attachment?.codec !== "bytes" || typeof part !== "string" || !Array.isArray(path)) throw new TypeError("connection: invalid binary response attachment");
@@ -45926,8 +46452,8 @@ ${scope} [data-dockkit-strip-tabs] {
45926
46452
  function refreshDshModelCatalog(uiContext) {
45927
46453
  if (uiContext === void 0) return;
45928
46454
  try {
45929
- const service = asRecord(uiContext.get("modelDirectories"));
45930
- const catalog = service === null ? null : asRecord(service.catalog);
46455
+ const service = asRecord$2(uiContext.get("modelDirectories"));
46456
+ const catalog = service === null ? null : asRecord$2(service.catalog);
45931
46457
  if (catalog === null) return;
45932
46458
  const resetGeneration = catalog.resetGeneration;
45933
46459
  const refresh = catalog.refresh;
@@ -46109,6 +46635,20 @@ ${scope} [data-dockkit-strip-tabs] {
46109
46635
  for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
46110
46636
  return bytes.buffer;
46111
46637
  }
46638
+ function encodeUploadBytesBase64(bytes) {
46639
+ let binary = "";
46640
+ for (const byte of bytes) binary += String.fromCharCode(byte);
46641
+ if (typeof btoa === "function") return btoa(binary);
46642
+ const nodeBuffer = globalThis.Buffer;
46643
+ if (nodeBuffer !== void 0) return nodeBuffer.from(bytes).toString("base64");
46644
+ throw new Error("当前运行时不支持 Base64 编码");
46645
+ }
46646
+ function jsonResponse(value, status = 200) {
46647
+ return new Response(JSON.stringify(value), {
46648
+ status,
46649
+ headers: { "content-type": "application/json; charset=utf-8" }
46650
+ });
46651
+ }
46112
46652
  /** PeerHost 启用状态说明;安装动作发生在启动页 preboot 阶段。 */
46113
46653
  function PeerHostPanel({ services, enabled }) {
46114
46654
  const t = require_transport.useCodingNsTranslator(services.locale);
@@ -47917,15 +48457,40 @@ ${scope} [data-dockkit-strip-tabs] {
47917
48457
  }
47918
48458
  //#endregion
47919
48459
  //#region src/client/avatar/notification-bubble.ts
47920
- function AssistantNotificationBubble({ frame, t, error, onOpen, onDismiss, onPresented, autoClose = false, autoCloseSeconds = 30, onPage, onExpandedChange }) {
48460
+ /** 返回形象中心在通知面板坐标系中的投影;仅供旧调用方和布局测试使用。 */
48461
+ function assistantNotificationTailStyle(avatarCenter, regionStart, regionSize, side = "bottom") {
48462
+ if (side === "left" || side === "right") return {};
48463
+ const margin = 36;
48464
+ const target = avatarCenter - regionStart;
48465
+ const rootTarget = target - Math.sign(target - regionSize / 2) * 10;
48466
+ const center = Math.max(margin, Math.min(Math.max(margin, regionSize - margin), rootTarget));
48467
+ const bottomAngle = Math.max(-20, Math.min(20, -Math.atan2(target - center, 30) * 180 / Math.PI));
48468
+ const angle = side === "top" ? -bottomAngle : bottomAngle;
48469
+ return {
48470
+ "--codingns-assistant-tail-offset": `${Math.round(center)}px`,
48471
+ "--codingns-assistant-tail-target": `${Math.round(target)}px`,
48472
+ "--codingns-assistant-tail-angle": `${Math.round(angle * 10) / 10}deg`
48473
+ };
48474
+ }
48475
+ /** 实际面板边缘使用的目标投影,允许目标落在圆角外侧后由轮廓算法自然收敛。 */
48476
+ function assistantNotificationTailTarget(avatarCenter, regionStart) {
48477
+ return avatarCenter - regionStart;
48478
+ }
48479
+ function isPendingNotice(notice) {
48480
+ return (notice.kind === "question" || notice.kind === "approval") && notice.lifecycle === "active";
48481
+ }
48482
+ function AssistantNotificationBubble({ frame, t, error, onOpen, onDismiss, onAutoClose, onPresented, autoClose = false, autoCloseSeconds = 30, onExpandedChange, onCollapsedChange, onClearAll, onSizeChange, tailSide = "bottom", tailTarget, tailStyle }) {
47921
48483
  const [expanded, setExpanded] = (0, react.useState)(false);
47922
- const [cursors, setCursors] = (0, react.useState)([void 0]);
48484
+ const [collapsed, setCollapsed] = (0, react.useState)(false);
48485
+ const [listFilter, setListFilter] = (0, react.useState)("all");
48486
+ const [processing, setProcessing] = (0, react.useState)(false);
47923
48487
  const bubble = (0, react.useRef)(null);
48488
+ const shellBox = (0, react.useRef)(null);
47924
48489
  const autoCloseTimer = (0, react.useRef)(void 0);
47925
48490
  const snapshot = "items" in frame ? frame : void 0;
47926
48491
  const primary = snapshot === void 0 ? frame : snapshot.primary;
47927
- const items = snapshot?.items.filter((notice) => !notice.read || (notice.kind === "question" || notice.kind === "approval") && notice.lifecycle === "active") ?? [];
47928
- const attentionItems = snapshot === void 0 ? primary === void 0 || primary === null ? [] : [primary] : [...snapshot.primary === null || snapshot.primary.presentation === "collapsed" ? [] : [snapshot.primary], ...items.filter((notice) => notice.presentation !== "collapsed" && (!notice.read || (notice.kind === "question" || notice.kind === "approval") && notice.lifecycle === "active") && notice.noticeId !== snapshot.primary?.noticeId)];
48492
+ const items = snapshot === void 0 ? [] : [...snapshot.primary !== null && !snapshot.items.some((notice) => notice.noticeId === snapshot.primary.noticeId) ? [snapshot.primary] : [], ...snapshot.items];
48493
+ const attentionItems = snapshot === void 0 ? primary === void 0 || primary === null ? [] : [primary] : [...snapshot.primary === null || snapshot.primary.presentation === "collapsed" ? [] : [snapshot.primary], ...items.filter((notice) => notice.presentation !== "collapsed" && (!notice.read || isPendingNotice(notice)) && notice.noticeId !== snapshot.primary?.noticeId)];
47929
48494
  const multiple = attentionItems.length > 1 || snapshot !== void 0 && Math.max(snapshot.unreadCount, snapshot.pendingCount) > 1;
47930
48495
  const mainNotice = attentionItems[0] ?? (snapshot === void 0 ? primary ?? void 0 : void 0);
47931
48496
  const presented = attentionItems[0] ?? (snapshot === void 0 ? primary ?? void 0 : void 0);
@@ -47934,8 +48499,18 @@ ${scope} [data-dockkit-strip-tabs] {
47934
48499
  onExpandedChange?.(expanded);
47935
48500
  }, [expanded, onExpandedChange]);
47936
48501
  (0, react.useEffect)(() => {
47937
- setCursors([void 0]);
48502
+ setCollapsed(false);
48503
+ }, [frame.generation]);
48504
+ (0, react.useEffect)(() => {
48505
+ setListFilter("all");
47938
48506
  }, [frame.generation, snapshot?.reset]);
48507
+ (0, react.useEffect)(() => {
48508
+ const node = shellBox.current;
48509
+ if (onSizeChange === void 0 || node === null || typeof ResizeObserver === "undefined") return;
48510
+ const observer = new ResizeObserver(() => onSizeChange(Math.ceil(node.getBoundingClientRect().height)));
48511
+ observer.observe(node);
48512
+ return () => observer.disconnect();
48513
+ }, [onSizeChange]);
47939
48514
  (0, react.useEffect)(() => {
47940
48515
  if (presented === void 0 || presented === null) return;
47941
48516
  const node = bubble.current;
@@ -47947,7 +48522,8 @@ ${scope} [data-dockkit-strip-tabs] {
47947
48522
  const connectionGeneration = "connectionGeneration" in presented ? presented.connectionGeneration : void 0;
47948
48523
  autoCloseTimer.current = setTimeout(() => {
47949
48524
  autoCloseTimer.current = void 0;
47950
- onDismiss(presented.noticeId, frame.generation, connectionGeneration);
48525
+ if (onAutoClose !== void 0) onAutoClose(presented.noticeId, frame.generation, connectionGeneration);
48526
+ else onDismiss(presented.noticeId, frame.generation, connectionGeneration);
47951
48527
  }, autoCloseSeconds * 1e3);
47952
48528
  };
47953
48529
  const confirm = () => {
@@ -47983,26 +48559,62 @@ ${scope} [data-dockkit-strip-tabs] {
47983
48559
  presented?.connectionGeneration,
47984
48560
  createdAt,
47985
48561
  frame.generation,
48562
+ expanded,
48563
+ collapsed,
47986
48564
  autoClose,
47987
48565
  autoCloseSeconds,
47988
48566
  onPresented,
47989
- onDismiss
48567
+ onDismiss,
48568
+ onAutoClose
47990
48569
  ]);
47991
- const page = (next) => {
47992
- const updated = next ? [...cursors, snapshot.cursor] : cursors.slice(0, -1);
47993
- setCursors(updated);
47994
- onPage?.(updated.at(-1));
47995
- };
47996
- const row = (notice, variant = "list") => (0, react.createElement)("section", {
48570
+ const clearableCount = snapshot === void 0 ? 0 : [...snapshot.items, ...snapshot.primary === null ? [] : [snapshot.primary]].filter((notice) => !notice.read && !isPendingNotice(notice)).length;
48571
+ const showClearAll = snapshot !== void 0 && clearableCount > 0 && onClearAll !== void 0;
48572
+ const hasNotificationCounts = snapshot !== void 0 && (snapshot.unreadCount > 0 || snapshot.pendingCount > 0);
48573
+ const showCollapseButton = !collapsed && (mainNotice !== void 0 || hasNotificationCounts);
48574
+ const showExpandButton = collapsed && (mainNotice !== void 0 || hasNotificationCounts);
48575
+ const showTopRow = showCollapseButton || showExpandButton || showClearAll || hasNotificationCounts;
48576
+ const showBubble = !collapsed && (!expanded || snapshot === void 0) && mainNotice !== void 0;
48577
+ const showFilterTags = snapshot !== void 0 && hasNotificationCounts && (multiple || items.length > 1);
48578
+ const listItems = listFilter === "unread" ? items.filter((notice) => !notice.read && !isPendingNotice(notice)) : listFilter === "pending" ? items.filter((notice) => isPendingNotice(notice)) : items;
48579
+ const tail = tailSide !== "top" && (showBubble || expanded && snapshot !== void 0) ? (0, react.createElement)("span", {
48580
+ "aria-hidden": true,
48581
+ "data-codingns-notification-tail": tailSide,
48582
+ style: {
48583
+ ...tailBox(tailSide),
48584
+ ...tailStyle
48585
+ }
48586
+ }, (0, react.createElement)("svg", {
48587
+ width: 56,
48588
+ height: 36,
48589
+ viewBox: `0 0 56 36`,
48590
+ "aria-hidden": true,
48591
+ style: tailSvg(tailSide)
48592
+ }, (0, react.createElement)("path", {
48593
+ d: `${notificationTailCurve} Z`,
48594
+ fill: notificationTheme.surface
48595
+ }))) : null;
48596
+ const toggleFilter = (filter) => {
48597
+ setCollapsed(false);
48598
+ onCollapsedChange?.(false);
48599
+ setListFilter(listFilter === filter ? "all" : filter);
48600
+ setExpanded(true);
48601
+ };
48602
+ const requestLine = (notice, compact = false) => {
48603
+ const approval = notice.kind === "approval";
48604
+ const tagStyle = compact ? approval ? compactApprovalTag : compactRequestTag : approval ? approvalInlineTag : requestInlineTag;
48605
+ return (0, react.createElement)("span", { style: requestPrompt }, t("awb.notifications.requestPrefix"), (0, react.createElement)("span", { style: tagStyle }, approval ? t("awb.notifications.approvalTag") : t("awb.notifications.questionTag")), t("awb.notifications.requestSuffix"));
48606
+ };
48607
+ const row = (notice, variant) => (0, react.createElement)("section", {
47997
48608
  key: notice.noticeId,
47998
48609
  "data-codingns-notice-id": notice.noticeId,
47999
48610
  "data-codingns-notice-kind": notice.kind,
48000
- style: variant === "primary" ? primaryCard : variant === "compact" ? compactCard : listRow
48001
- }, (0, react.createElement)("div", { style: cardHeading }, (0, react.createElement)("span", {
48611
+ style: variant === "primary" ? primaryCard : compactCard
48612
+ }, (0, react.createElement)("div", { style: cardBody }, (0, react.createElement)("div", { style: cardHeading }, (0, react.createElement)("span", {
48002
48613
  "aria-hidden": true,
48003
48614
  style: {
48004
48615
  ...kindMark,
48005
- background: noticeKindColor[notice.kind]
48616
+ background: noticeKindColor[notice.kind],
48617
+ ...noticeKindForeground[notice.kind] === void 0 ? {} : { color: noticeKindForeground[notice.kind] }
48006
48618
  }
48007
48619
  }, icons[notice.kind]), (0, react.createElement)("div", {
48008
48620
  style: {
@@ -48010,9 +48622,9 @@ ${scope} [data-dockkit-strip-tabs] {
48010
48622
  flex: "1 1 auto"
48011
48623
  },
48012
48624
  title: `${notice.hostLabel} · ${notice.workspaceLabel} · ${notice.sessionTitle}`
48013
- }, (0, react.createElement)("div", { style: cardMeta }, `${notice.hostLabel} · ${notice.workspaceLabel}`), (0, react.createElement)("div", { style: cardTitle }, notice.sessionTitle))), variant === "compact" ? (0, react.createElement)("span", { style: notice.kind === "question" || notice.kind === "approval" ? requestTag : compactStatus }, notice.kind === "question" ? t("awb.notifications.questionTag") : notice.kind === "approval" ? t("awb.notifications.approvalTag") : notificationStatus(notice, t)) : null, variant !== "compact" ? (0, react.createElement)("div", { style: messageText }, (0, react.createElement)("span", { "aria-hidden": true }, icons[notice.kind] + " "), notice.kind === "question" || notice.kind === "approval" ? (0, react.createElement)("span", { style: requestInlineTag }, notice.kind === "question" ? t("awb.notifications.questionTag") : t("awb.notifications.approvalTag")) : null, notice.text, "errorExcerpt" in notice && notice.errorExcerpt ? (0, react.createElement)("div", null, notice.errorExcerpt) : null) : null, notice.availability !== "ready" ? (0, react.createElement)("span", { role: "status" }, t(notice.availability === "disconnected" ? "awb.notifications.disconnected" : "awb.notifications.expired")) : null, (0, react.createElement)("div", { style: actionRow }, (0, react.createElement)("button", {
48625
+ }, (0, react.createElement)("div", { style: cardMeta }, `${notice.hostLabel} · ${notice.workspaceLabel}`), (0, react.createElement)("div", { style: cardTitle }, notice.sessionTitle))), variant === "compact" ? notice.kind === "question" || notice.kind === "approval" ? (0, react.createElement)("div", { style: compactPrompt }, requestLine(notice, true)) : (0, react.createElement)("span", { style: compactStatus }, notificationStatus(notice, t)) : null, variant !== "compact" ? (0, react.createElement)("div", { style: messageText }, notice.kind === "question" || notice.kind === "approval" ? requestLine(notice) : (0, react.createElement)("span", null, (0, react.createElement)("span", { "aria-hidden": true }, icons[notice.kind] + " "), notice.text), "errorExcerpt" in notice && notice.errorExcerpt ? (0, react.createElement)("div", null, notice.errorExcerpt) : null) : null, notice.availability !== "ready" ? (0, react.createElement)("span", { role: "status" }, t(notice.availability === "disconnected" ? "awb.notifications.disconnected" : "awb.notifications.expired")) : null), (0, react.createElement)("div", { style: actionRow }, (0, react.createElement)("button", {
48014
48626
  type: "button",
48015
- style: variant === "compact" ? compactButton : button,
48627
+ style: notificationActionButton,
48016
48628
  disabled: notice.availability !== "ready",
48017
48629
  "aria-label": [
48018
48630
  t("awb.notifications.open"),
@@ -48020,94 +48632,107 @@ ${scope} [data-dockkit-strip-tabs] {
48020
48632
  notice.workspaceLabel,
48021
48633
  notice.sessionTitle
48022
48634
  ].join(" · "),
48635
+ title: t("awb.notifications.open"),
48636
+ "data-codingns-notification-action": "open",
48023
48637
  onClick: () => onOpen(notice.noticeId, frame.generation, "connectionGeneration" in notice ? notice.connectionGeneration : void 0)
48024
- }, t("awb.notifications.open")), (0, react.createElement)("button", {
48638
+ }, notificationActionIcon("open")), (0, react.createElement)("button", {
48025
48639
  type: "button",
48026
- style: variant === "compact" ? compactButton : button,
48640
+ style: notificationActionButton,
48027
48641
  "aria-label": [
48028
48642
  t("awb.notifications.dismiss"),
48029
48643
  notice.hostLabel,
48030
48644
  notice.workspaceLabel,
48031
48645
  notice.sessionTitle
48032
48646
  ].join(" · "),
48647
+ title: t("awb.notifications.dismiss"),
48648
+ "data-codingns-notification-action": "dismiss",
48033
48649
  onClick: () => {
48034
48650
  clearTimeout(autoCloseTimer.current);
48035
48651
  autoCloseTimer.current = void 0;
48036
48652
  onDismiss(notice.noticeId, frame.generation, "connectionGeneration" in notice ? notice.connectionGeneration : void 0);
48037
48653
  }
48038
- }, t("awb.notifications.dismiss"))));
48654
+ }, notificationActionIcon("dismiss"))));
48655
+ const capabilityNotices = collapsed ? [] : snapshot?.capabilities?.filter((capability) => capability.reason && (!capability.completed || !capability.error || !capability.requests || !capability.resolve || !capability.navigation)).map((capability) => (0, react.createElement)("div", {
48656
+ key: capability.hostId,
48657
+ role: "status",
48658
+ style: panel
48659
+ }, capability.reason)) ?? [];
48039
48660
  return (0, react.createElement)("div", {
48661
+ ref: shellBox,
48040
48662
  "data-codingns-assistant-notifications": true,
48041
48663
  style: shell,
48042
48664
  onPointerDown: (event) => event.stopPropagation(),
48043
48665
  onClick: (event) => event.stopPropagation()
48044
- }, snapshot && multiple ? (0, react.createElement)("button", {
48666
+ }, (0, react.createElement)("style", null, notificationBubbleCss), tail, showTopRow ? (0, react.createElement)("div", {
48667
+ "data-codingns-notification-toolbar": true,
48668
+ style: topRow
48669
+ }, showCollapseButton || showExpandButton ? (0, react.createElement)("button", {
48045
48670
  type: "button",
48046
- style: summaryButton,
48047
- "aria-expanded": expanded,
48048
- "aria-label": [
48049
- t("awb.notifications.list"),
48050
- t("awb.notifications.unread", { count: snapshot.unreadCount }),
48051
- t("awb.notifications.pending", { count: snapshot.pendingCount })
48052
- ].join(" · "),
48671
+ style: toolbarIconButton,
48672
+ "aria-expanded": !collapsed,
48673
+ "aria-label": t(collapsed ? "awb.notifications.expand" : "awb.notifications.collapse"),
48674
+ title: t(collapsed ? "awb.notifications.expand" : "awb.notifications.collapse"),
48053
48675
  onClick: () => {
48054
- setExpanded(!expanded);
48055
- if (!expanded) {
48056
- setCursors([void 0]);
48057
- onPage?.();
48058
- }
48676
+ const nextCollapsed = !collapsed;
48677
+ setCollapsed(nextCollapsed);
48678
+ setExpanded(!nextCollapsed);
48679
+ onCollapsedChange?.(nextCollapsed);
48059
48680
  }
48060
- }, `${t("awb.notifications.unread", { count: snapshot.unreadCount })} · ${t("awb.notifications.pending", { count: snapshot.pendingCount })}`) : null, (!expanded || snapshot === void 0) && mainNotice !== void 0 ? (0, react.createElement)("div", {
48681
+ }, notificationToolbarIcon(collapsed ? "expand" : "collapse")) : null, !collapsed && showClearAll ? (0, react.createElement)("button", {
48682
+ type: "button",
48683
+ style: processing ? clearAllButtonBusy : clearAllButton,
48684
+ disabled: processing,
48685
+ "aria-label": t("awb.notifications.clearAllLabel"),
48686
+ title: t("awb.notifications.clearAllLabel"),
48687
+ onClick: () => {
48688
+ if (processing || snapshot === void 0 || onClearAll === void 0) return;
48689
+ setProcessing(true);
48690
+ Promise.resolve(onClearAll(snapshot.generation)).catch(() => void 0).finally(() => setProcessing(false));
48691
+ }
48692
+ }, notificationToolbarIcon("clear")) : null, showFilterTags ? [snapshot.unreadCount > 0 ? (0, react.createElement)("button", {
48693
+ key: "unread-filter",
48694
+ type: "button",
48695
+ style: listFilter === "unread" ? filterButtonActive : filterButton,
48696
+ "data-codingns-notification-filter": true,
48697
+ "aria-expanded": expanded,
48698
+ "aria-pressed": listFilter === "unread",
48699
+ "aria-label": t("awb.notifications.unreadFilterLabel", { count: snapshot.unreadCount }),
48700
+ title: t("awb.notifications.unreadFilterLabel", { count: snapshot.unreadCount }),
48701
+ onClick: () => toggleFilter("unread")
48702
+ }, notificationToolbarIcon("unread"), (0, react.createElement)("span", { style: notificationCountBadge }, formatNotificationCount(snapshot.unreadCount))) : null, snapshot.pendingCount > 0 ? (0, react.createElement)("button", {
48703
+ key: "pending-filter",
48704
+ type: "button",
48705
+ style: listFilter === "pending" ? filterButtonActive : filterButton,
48706
+ "data-codingns-notification-filter": true,
48707
+ "aria-expanded": expanded,
48708
+ "aria-pressed": listFilter === "pending",
48709
+ "aria-label": t("awb.notifications.pendingFilterLabel", { count: snapshot.pendingCount }),
48710
+ title: t("awb.notifications.pendingFilterLabel", { count: snapshot.pendingCount }),
48711
+ onClick: () => toggleFilter("pending")
48712
+ }, notificationToolbarIcon("pending"), (0, react.createElement)("span", { style: notificationPendingBadge }, formatNotificationCount(snapshot.pendingCount))) : null] : null) : null, showBubble ? (0, react.createElement)("div", {
48061
48713
  ref: bubble,
48062
48714
  role: "status",
48063
48715
  "aria-live": "polite",
48064
48716
  style: bubblePanel
48065
- }, (0, react.createElement)("span", {
48066
- "aria-hidden": true,
48067
- style: bubbleTail
48068
- }, (0, react.createElement)("svg", {
48069
- width: 36,
48070
- height: 22,
48071
- viewBox: "0 0 36 22",
48072
- fill: require_theme.dshThemeColor.menuBackground,
48073
- stroke: require_theme.dshThemeColor.accent,
48074
- strokeWidth: 1.5,
48075
- strokeLinecap: "round",
48076
- strokeLinejoin: "round",
48077
- style: { display: "block" }
48078
- }, (0, react.createElement)("path", { d: "M2 1 C7 2 12 8 18 19 C19 21 21 21 22 19 C27 9 31 3 34 1" }))), (0, react.createElement)("div", { style: bubbleContent }, multiple ? attentionItems.map((notice) => row(notice, "compact")) : row(mainNotice, "primary"))) : null, expanded && snapshot ? (0, react.createElement)("div", {
48717
+ }, (0, react.createElement)("div", {
48718
+ "data-codingns-notification-scroll": true,
48719
+ style: bubbleContent
48720
+ }, multiple ? attentionItems.map((notice) => row(notice, "compact")) : row(mainNotice, "primary"))) : null, !collapsed && expanded && snapshot ? (0, react.createElement)("div", {
48721
+ ref: bubble,
48079
48722
  role: "region",
48080
48723
  "aria-label": t("awb.notifications.list"),
48081
- style: panel
48082
- }, (0, react.createElement)("button", {
48083
- type: "button",
48084
- style: button,
48085
- onClick: () => setExpanded(false)
48086
- }, t("awb.notifications.closeList")), (0, react.createElement)("div", { style: {
48087
- maxHeight: 180,
48088
- overflowY: "auto",
48089
- overscrollBehavior: "contain"
48090
- } }, ...items.map((notice) => row(notice, "list"))), items.length === 0 ? (0, react.createElement)("p", null, t("awb.notifications.empty")) : null, onPage ? (0, react.createElement)("div", { style: {
48091
- display: "flex",
48092
- gap: 6
48093
- } }, (0, react.createElement)("button", {
48094
- type: "button",
48095
- style: button,
48096
- disabled: cursors.length <= 1,
48097
- onClick: () => page(false)
48098
- }, t("awb.notifications.previous")), (0, react.createElement)("button", {
48099
- type: "button",
48100
- style: button,
48101
- disabled: !snapshot.cursor,
48102
- onClick: () => page(true)
48103
- }, t("awb.notifications.next"))) : null) : null, error ? (0, react.createElement)("div", {
48724
+ "data-codingns-notification-scroll": true,
48725
+ style: bubblePanel
48726
+ }, (0, react.createElement)("div", {
48727
+ "data-codingns-notification-scroll": true,
48728
+ style: {
48729
+ ...bubbleContent,
48730
+ maxHeight: 180
48731
+ }
48732
+ }, ...listItems.map((notice) => row(notice, "compact"))), listItems.length === 0 ? (0, react.createElement)("p", null, t("awb.notifications.empty")) : null) : null, !collapsed && error ? (0, react.createElement)("div", {
48104
48733
  role: "alert",
48105
48734
  style: panel
48106
- }, error) : null, ...snapshot?.capabilities?.filter((capability) => capability.reason && (!capability.completed || !capability.error || !capability.requests || !capability.resolve || !capability.navigation)).map((capability) => (0, react.createElement)("div", {
48107
- key: capability.hostId,
48108
- role: "status",
48109
- style: panel
48110
- }, capability.reason)) ?? []);
48735
+ }, error) : null, ...capabilityNotices);
48111
48736
  }
48112
48737
  /** 可见 DOM 与视口相交后才确认首展;后台挂载和零面积节点不消耗提醒时长。 */
48113
48738
  function assistantNotificationIsVisible(node) {
@@ -48142,140 +48767,242 @@ ${scope} [data-dockkit-strip-tabs] {
48142
48767
  /** 网页气泡作为形象的兄弟区域定位;字幕优先保留原侧,通知避让到另一侧或形象旁。 */
48143
48768
  function floatingAssistantNotificationLayout(x, y, width, height, viewportWidth, viewportHeight, captionAbove) {
48144
48769
  const bubbleWidth = Math.max(0, Math.min(320, viewportWidth - 24));
48145
- const aboveSpace = Math.max(0, y - 22), belowSpace = Math.max(0, viewportHeight - y - height - 22);
48770
+ const clearance = 30;
48771
+ const aboveSpace = Math.max(0, y - 22 - clearance), belowSpace = Math.max(0, viewportHeight - y - height - 22 - clearance);
48146
48772
  const above = captionAbove === void 0 ? aboveSpace >= belowSpace : !captionAbove;
48147
48773
  const space = above ? aboveSpace : belowSpace;
48148
48774
  const left = Math.max(12 - x, Math.min((width - bubbleWidth) / 2, viewportWidth - x - bubbleWidth - 12));
48149
48775
  if (captionAbove !== void 0 && space < 72) {
48150
48776
  const leftSpace = Math.max(0, x - 22), rightSpace = Math.max(0, viewportWidth - x - width - 22);
48151
48777
  const sideWidth = Math.min(320, Math.max(leftSpace, rightSpace));
48152
- if (sideWidth >= 150) return {
48153
- left: leftSpace >= rightSpace ? -sideWidth - 10 : width + 10,
48154
- top: 0,
48155
- width: sideWidth,
48156
- maxHeight: Math.min(276, height, Math.max(0, viewportHeight - y - 12))
48157
- };
48778
+ if (sideWidth >= 150) {
48779
+ const onLeft = leftSpace >= rightSpace;
48780
+ return {
48781
+ style: {
48782
+ left: onLeft ? -sideWidth - 10 : width + 10,
48783
+ top: 0,
48784
+ width: sideWidth,
48785
+ maxHeight: Math.min(276, height, Math.max(0, viewportHeight - y - 12))
48786
+ },
48787
+ tail: onLeft ? "right" : "left"
48788
+ };
48789
+ }
48158
48790
  const stackedAbove = captionAbove;
48159
48791
  const stackedSpace = (stackedAbove ? aboveSpace : belowSpace) - 190;
48160
48792
  if (stackedSpace >= 36) return {
48161
- left,
48162
- width: bubbleWidth,
48163
- maxHeight: Math.min(276, stackedSpace),
48164
- ...stackedAbove ? { bottom: height + 200 } : { top: height + 200 }
48793
+ style: {
48794
+ left,
48795
+ width: bubbleWidth,
48796
+ maxHeight: Math.min(276, stackedSpace),
48797
+ ...stackedAbove ? { bottom: height + 200 } : { top: height + 200 }
48798
+ },
48799
+ tail: stackedAbove ? "bottom" : "top"
48165
48800
  };
48166
48801
  }
48167
48802
  return {
48168
- left,
48169
- width: bubbleWidth,
48170
- maxHeight: Math.min(276, space),
48171
- ...above ? { bottom: height + 10 } : { top: height + 10 }
48803
+ style: {
48804
+ left,
48805
+ width: bubbleWidth,
48806
+ maxHeight: Math.min(276, space),
48807
+ ...above ? { bottom: height + clearance } : { top: height + clearance }
48808
+ },
48809
+ tail: above ? "bottom" : "top"
48172
48810
  };
48173
48811
  }
48174
- /** 将气泡尾巴指向头像中心;侧置气泡也不能固定在自己的左边角。 */
48175
- function assistantNotificationTailStyle(avatarCenter, regionLeft, regionWidth) {
48176
- const min = 20, max = Math.max(min, regionWidth - 20);
48177
- const center = Math.max(min, Math.min(max, avatarCenter - regionLeft));
48178
- return { "--codingns-assistant-tail-left": `${Math.round(center)}px` };
48179
- }
48180
48812
  const icons = {
48181
48813
  completed: "✓",
48182
48814
  error: "!",
48183
48815
  question: "?",
48184
- approval: "◇"
48816
+ approval: "!"
48185
48817
  };
48186
48818
  const noticeKindColor = {
48187
48819
  completed: require_theme.dshThemeColor.success,
48188
48820
  error: require_theme.dshThemeColor.error,
48189
48821
  question: require_theme.dshThemeColor.accent,
48190
- approval: require_theme.dshThemeColor.accent
48822
+ approval: require_theme.dshThemeColor.warning
48191
48823
  };
48824
+ /** 黄色填充上的图标与标签改用深色前景,避免白字对比度不足。 */
48825
+ const noticeKindForeground = { approval: require_theme.dshThemeColor.warningForeground };
48192
48826
  function notificationStatus(notice, t) {
48193
48827
  if (notice.kind === "completed") return t("awb.notifications.completed");
48194
48828
  if (notice.kind === "error") return t("awb.notifications.error");
48195
48829
  if (notice.kind === "question") return t("awb.notifications.question");
48196
48830
  return t("awb.notifications.approval");
48197
48831
  }
48832
+ /** 独立伴随页没有 DSH 主题变量,回退色固定为夜间灰;宿主页面仍可通过变量覆盖。 */
48833
+ const notificationTheme = {
48834
+ labelPrimary: "var(--dsw-alias-label-primary, #f1f2f4)",
48835
+ labelSecondary: "var(--dsw-alias-label-secondary, #b7bac1)",
48836
+ border: "var(--dsw-alias-border-l2, #5b5e66)",
48837
+ surface: "var(--dsw-alias-bg-layer-3, #303237)",
48838
+ card: "var(--dsw-alias-bg-layer-1, #3b3d43)",
48839
+ button: "var(--dsw-alias-button-elevated-fill, #44474e)",
48840
+ shadow: "var(--dsw-elevation-prominent, 0 12px 36px rgba(0, 0, 0, 0.42))"
48841
+ };
48198
48842
  const button = {
48199
48843
  ...require_theme.dshSettingsButtonStyle,
48200
48844
  minHeight: 36,
48201
48845
  touchAction: "manipulation",
48202
48846
  whiteSpace: "normal",
48203
48847
  borderRadius: 999,
48204
- fontWeight: 650
48848
+ fontWeight: 650,
48849
+ color: notificationTheme.labelPrimary,
48850
+ background: notificationTheme.button,
48851
+ borderColor: notificationTheme.border
48205
48852
  };
48853
+ /** 外壳按实际内容收缩,避免原生为形象预留的高度变成大片不透明背景。 */
48206
48854
  const shell = {
48207
- width: "min(320px, calc(100vw - 24px))",
48208
- maxWidth: "100%",
48855
+ position: "relative",
48856
+ isolation: "isolate",
48857
+ width: "100%",
48858
+ maxWidth: 320,
48859
+ height: "auto",
48860
+ maxHeight: "var(--codingns-notification-max-height, min(276px, calc(100vh - 24px)))",
48861
+ minHeight: 0,
48209
48862
  display: "flex",
48210
48863
  flexDirection: "column",
48211
48864
  gap: 6,
48865
+ padding: 10,
48866
+ boxSizing: "border-box",
48212
48867
  pointerEvents: "auto",
48213
- color: require_theme.dshThemeColor.labelPrimary,
48214
- fontSize: require_theme.uiFontSize(13)
48868
+ color: notificationTheme.labelPrimary,
48869
+ fontSize: require_theme.uiFontSize(13),
48870
+ background: notificationTheme.surface,
48871
+ border: `1px solid ${notificationTheme.border}`,
48872
+ borderRadius: "24px 24px 24px 8px",
48873
+ boxShadow: notificationTheme.shadow
48215
48874
  };
48216
48875
  const panel = {
48217
- padding: 12,
48218
- background: require_theme.dshThemeColor.menuBackground,
48219
- border: `1px solid ${require_theme.dshThemeColor.border}`,
48876
+ position: "relative",
48877
+ zIndex: 1,
48878
+ padding: 10,
48879
+ background: notificationTheme.surface,
48880
+ border: `1px solid ${notificationTheme.border}`,
48220
48881
  borderRadius: 18,
48221
- boxShadow: require_theme.dshThemeColor.prominentShadow,
48882
+ boxShadow: notificationTheme.shadow,
48222
48883
  minWidth: 0,
48223
48884
  boxSizing: "border-box",
48224
- maxHeight: "min(276px, calc(100vh - 24px))",
48885
+ maxHeight: "var(--codingns-notification-max-height, min(276px, calc(100vh - 24px)))",
48225
48886
  overflowY: "auto"
48226
48887
  };
48227
48888
  const bubblePanel = {
48228
- ...panel,
48229
48889
  position: "relative",
48230
- padding: 10,
48890
+ display: "flex",
48891
+ flexDirection: "column",
48892
+ flex: "0 1 auto",
48893
+ minWidth: 0,
48894
+ minHeight: 0,
48895
+ padding: 0,
48231
48896
  overflow: "visible",
48232
- borderRadius: "24px 24px 24px 8px",
48233
- borderColor: require_theme.dshThemeColor.accent,
48234
- background: require_theme.dshThemeColor.menuBackground,
48235
- boxShadow: require_theme.dshThemeColor.prominentShadow
48897
+ border: "none",
48898
+ borderRadius: 0,
48899
+ boxShadow: "none",
48900
+ maxHeight: "var(--codingns-notification-max-height, min(276px, calc(100vh - 24px)))",
48901
+ background: "transparent",
48902
+ outline: "none"
48236
48903
  };
48237
48904
  const bubbleContent = {
48238
48905
  position: "relative",
48239
48906
  zIndex: 1,
48240
48907
  display: "grid",
48908
+ gridAutoRows: "max-content",
48909
+ alignContent: "start",
48241
48910
  gap: 8,
48242
- maxHeight: "min(252px, calc(100vh - 48px))",
48911
+ minHeight: 0,
48912
+ flex: "0 1 auto",
48913
+ maxHeight: "var(--codingns-notification-max-height, min(276px, calc(100vh - 24px)))",
48243
48914
  overflowY: "auto",
48244
48915
  overscrollBehavior: "contain",
48245
- padding: "2px 2px 4px"
48246
- };
48247
- const bubbleTail = {
48248
- position: "absolute",
48249
- left: "var(--codingns-assistant-tail-left, 50%)",
48250
- bottom: -21,
48251
- width: 36,
48252
- height: 22,
48253
- marginLeft: -18,
48254
- zIndex: 0,
48255
- pointerEvents: "none"
48916
+ boxSizing: "border-box",
48917
+ padding: 0
48256
48918
  };
48919
+ const notificationTailCurve = "M1 0 C12 0 20 8 23 18 C24 24 25 30 28 36 C31 30 32 24 33 18 C36 8 44 0 55 0";
48920
+ function tailBox(side) {
48921
+ const base = {
48922
+ position: "absolute",
48923
+ zIndex: 0,
48924
+ pointerEvents: "none"
48925
+ };
48926
+ const renderHeight = 36;
48927
+ if (side === "top") return {
48928
+ ...base,
48929
+ top: -24,
48930
+ left: "var(--codingns-assistant-tail-offset, 50%)",
48931
+ width: 56,
48932
+ height: renderHeight,
48933
+ marginLeft: -28
48934
+ };
48935
+ if (side === "left") return {
48936
+ ...base,
48937
+ left: -24,
48938
+ top: "var(--codingns-assistant-tail-offset, 50%)",
48939
+ width: renderHeight,
48940
+ height: 56,
48941
+ marginTop: -28
48942
+ };
48943
+ if (side === "right") return {
48944
+ ...base,
48945
+ right: -24,
48946
+ top: "var(--codingns-assistant-tail-offset, 50%)",
48947
+ width: renderHeight,
48948
+ height: 56,
48949
+ marginTop: -28
48950
+ };
48951
+ return {
48952
+ ...base,
48953
+ bottom: -24,
48954
+ left: "var(--codingns-assistant-tail-offset, 50%)",
48955
+ width: 56,
48956
+ height: renderHeight,
48957
+ marginLeft: -28
48958
+ };
48959
+ }
48960
+ function tailSvg(side) {
48961
+ const angle = "var(--codingns-assistant-tail-angle, 0deg)";
48962
+ if (side === "bottom") return {
48963
+ display: "block",
48964
+ transformOrigin: "50% 0%",
48965
+ transform: `rotate(${angle})`
48966
+ };
48967
+ if (side === "top") return {
48968
+ display: "block",
48969
+ transformOrigin: "50% 50%",
48970
+ transform: `rotate(180deg) rotate(${angle})`
48971
+ };
48972
+ return {
48973
+ display: "block",
48974
+ position: "absolute",
48975
+ left: "50%",
48976
+ top: "50%",
48977
+ transform: `translate(-50%, -50%) rotate(${side === "left" ? 90 : -90}deg)`
48978
+ };
48979
+ }
48980
+ const notificationBubbleCss = "[data-codingns-notification-scroll]{scrollbar-width:none;-ms-overflow-style:none}[data-codingns-notification-scroll]::-webkit-scrollbar{display:none;width:0;height:0}[data-codingns-notification-filter], [data-codingns-notification-filter]:focus, [data-codingns-notification-filter]:focus-visible{border:1px solid var(--dsw-alias-border-l2,#5b5e66)!important;outline:none!important;box-shadow:none!important}";
48257
48981
  const primaryCard = {
48258
48982
  display: "grid",
48983
+ gridTemplateColumns: "minmax(0, 1fr) auto",
48984
+ alignItems: "center",
48259
48985
  gap: 9,
48260
48986
  padding: 5,
48261
48987
  minWidth: 0
48262
48988
  };
48263
48989
  const compactCard = {
48264
48990
  display: "grid",
48991
+ gridTemplateColumns: "minmax(0, 1fr) auto",
48992
+ alignItems: "center",
48265
48993
  gap: 7,
48266
48994
  padding: "10px 11px",
48267
48995
  minWidth: 0,
48268
- background: require_theme.dshThemeColor.cardBackground,
48269
- border: `1px solid ${require_theme.dshThemeColor.border}`,
48996
+ background: notificationTheme.card,
48997
+ border: `1px solid ${notificationTheme.border}`,
48270
48998
  borderRadius: 15,
48271
- boxShadow: require_theme.dshThemeColor.subtleShadow
48999
+ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.24)"
48272
49000
  };
48273
- const listRow = {
49001
+ const cardBody = {
48274
49002
  display: "grid",
48275
- gap: 7,
48276
- padding: "10px 0",
49003
+ gap: 8,
48277
49004
  minWidth: 0,
48278
- borderBottom: `1px solid ${require_theme.dshThemeColor.border}`
49005
+ minInlineSize: 0
48279
49006
  };
48280
49007
  const cardHeading = {
48281
49008
  display: "flex",
@@ -48284,7 +49011,7 @@ ${scope} [data-dockkit-strip-tabs] {
48284
49011
  minWidth: 0
48285
49012
  };
48286
49013
  const cardMeta = {
48287
- color: require_theme.dshThemeColor.labelSecondary,
49014
+ color: notificationTheme.labelSecondary,
48288
49015
  fontSize: require_theme.uiFontSize(11),
48289
49016
  lineHeight: 1.3,
48290
49017
  overflowWrap: "anywhere"
@@ -48302,43 +49029,104 @@ ${scope} [data-dockkit-strip-tabs] {
48302
49029
  width: 24,
48303
49030
  height: 24,
48304
49031
  borderRadius: "50%",
48305
- color: require_theme.dshThemeColor.primaryForeground,
49032
+ color: "#fff",
48306
49033
  fontSize: require_theme.uiFontSize(13),
48307
49034
  fontWeight: 800,
48308
- boxShadow: require_theme.dshThemeColor.subtleShadow
49035
+ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.28)"
48309
49036
  };
48310
49037
  const messageText = {
48311
- color: require_theme.dshThemeColor.labelPrimary,
49038
+ color: notificationTheme.labelPrimary,
48312
49039
  whiteSpace: "pre-wrap",
48313
49040
  overflowWrap: "anywhere",
48314
49041
  lineHeight: 1.5,
48315
49042
  maxHeight: 120,
48316
49043
  overflowY: "auto"
48317
49044
  };
48318
- const summaryButton = {
49045
+ const topRowButton = {
48319
49046
  ...button,
49047
+ height: 28,
49048
+ minHeight: 28,
49049
+ padding: "2px 10px",
49050
+ fontSize: require_theme.uiFontSize(12)
49051
+ };
49052
+ const toolbarIconButton = {
49053
+ ...topRowButton,
49054
+ width: 28,
49055
+ minWidth: 28,
49056
+ padding: 4,
49057
+ display: "inline-grid",
49058
+ placeItems: "center"
49059
+ };
49060
+ const filterButton = {
49061
+ ...topRowButton,
48320
49062
  alignSelf: "end",
48321
- padding: "8px 14px",
48322
- background: require_theme.dshThemeColor.menuBackground,
48323
- boxShadow: require_theme.dshThemeColor.subtleShadow
49063
+ display: "inline-flex",
49064
+ alignItems: "center",
49065
+ justifyContent: "center",
49066
+ gap: 4,
49067
+ minWidth: 46,
49068
+ padding: "2px 7px",
49069
+ background: notificationTheme.button,
49070
+ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.24)"
48324
49071
  };
48325
- const requestTag = {
48326
- justifySelf: "start",
48327
- padding: "4px 9px",
48328
- borderRadius: 999,
48329
- color: require_theme.dshThemeColor.primaryForeground,
49072
+ const filterButtonActive = {
49073
+ ...filterButton,
48330
49074
  background: require_theme.dshThemeColor.accent,
48331
- fontSize: require_theme.uiFontSize(11),
48332
- lineHeight: 1.2,
48333
- fontWeight: 750,
48334
- letterSpacing: "0.02em",
48335
- boxShadow: require_theme.dshThemeColor.subtleShadow
49075
+ borderColor: require_theme.dshThemeColor.accent,
49076
+ color: require_theme.dshThemeColor.primaryForeground
49077
+ };
49078
+ const notificationCountBadge = {
49079
+ display: "inline-grid",
49080
+ placeItems: "center",
49081
+ minWidth: 16,
49082
+ height: 16,
49083
+ padding: "0 4px",
49084
+ boxSizing: "border-box",
49085
+ borderRadius: 8,
49086
+ color: notificationTheme.labelPrimary,
49087
+ background: notificationTheme.card,
49088
+ fontSize: require_theme.uiFontSize(10),
49089
+ lineHeight: 1,
49090
+ fontWeight: 750
49091
+ };
49092
+ const notificationPendingBadge = {
49093
+ ...notificationCountBadge,
49094
+ color: require_theme.dshThemeColor.warningForeground,
49095
+ background: require_theme.dshThemeColor.warning
49096
+ };
49097
+ const topRow = {
49098
+ display: "flex",
49099
+ width: "100%",
49100
+ flexShrink: 0,
49101
+ alignItems: "center",
49102
+ justifyContent: "center",
49103
+ flexWrap: "nowrap",
49104
+ gap: 6
49105
+ };
49106
+ const clearAllButton = {
49107
+ ...toolbarIconButton,
49108
+ alignSelf: "end",
49109
+ background: notificationTheme.button,
49110
+ borderColor: notificationTheme.border,
49111
+ color: notificationTheme.labelPrimary,
49112
+ borderRadius: 10,
49113
+ boxShadow: "none"
49114
+ };
49115
+ const clearAllButtonBusy = {
49116
+ ...clearAllButton,
49117
+ opacity: .6,
49118
+ cursor: "default"
49119
+ };
49120
+ const requestPrompt = {
49121
+ display: "inline-flex",
49122
+ alignItems: "center",
49123
+ flexWrap: "wrap",
49124
+ gap: 6,
49125
+ lineHeight: 1.5
48336
49126
  };
48337
49127
  const requestInlineTag = {
48338
49128
  display: "inline-flex",
48339
49129
  alignItems: "center",
48340
- verticalAlign: "middle",
48341
- margin: "0 6px 2px 2px",
48342
49130
  padding: "3px 8px",
48343
49131
  borderRadius: 999,
48344
49132
  color: require_theme.dshThemeColor.primaryForeground,
@@ -48349,23 +49137,114 @@ ${scope} [data-dockkit-strip-tabs] {
48349
49137
  letterSpacing: "0.02em",
48350
49138
  boxShadow: require_theme.dshThemeColor.subtleShadow
48351
49139
  };
49140
+ const compactPrompt = {
49141
+ fontSize: require_theme.uiFontSize(12),
49142
+ lineHeight: 1.4,
49143
+ color: notificationTheme.labelPrimary
49144
+ };
49145
+ const compactRequestTag = {
49146
+ ...requestInlineTag,
49147
+ padding: "2px 7px",
49148
+ fontSize: require_theme.uiFontSize(10)
49149
+ };
49150
+ const approvalInlineTag = {
49151
+ ...requestInlineTag,
49152
+ background: require_theme.dshThemeColor.warning,
49153
+ color: require_theme.dshThemeColor.warningForeground
49154
+ };
49155
+ const compactApprovalTag = {
49156
+ ...compactRequestTag,
49157
+ background: require_theme.dshThemeColor.warning,
49158
+ color: require_theme.dshThemeColor.warningForeground
49159
+ };
48352
49160
  const compactStatus = {
48353
- color: require_theme.dshThemeColor.labelSecondary,
49161
+ color: notificationTheme.labelSecondary,
48354
49162
  fontSize: require_theme.uiFontSize(11),
48355
49163
  lineHeight: 1.3,
48356
49164
  paddingLeft: 32
48357
49165
  };
48358
49166
  const actionRow = {
48359
49167
  display: "flex",
48360
- flexWrap: "wrap",
48361
- gap: 7
49168
+ flexDirection: "column",
49169
+ alignItems: "stretch",
49170
+ justifyContent: "center",
49171
+ gap: 6,
49172
+ flex: "0 0 auto"
48362
49173
  };
48363
- const compactButton = {
49174
+ const notificationActionButton = {
48364
49175
  ...button,
48365
- minHeight: 30,
48366
- padding: "6px 10px",
49176
+ width: 28,
49177
+ height: 28,
49178
+ minHeight: 28,
49179
+ padding: 0,
49180
+ display: "inline-grid",
49181
+ placeItems: "center",
49182
+ borderRadius: 10,
48367
49183
  fontSize: require_theme.uiFontSize(12)
48368
49184
  };
49185
+ /** 操作按钮只保留图标,文字语义通过 aria-label 和 title 传达,避免气泡底部堆叠长文案。 */
49186
+ function notificationActionIcon(action) {
49187
+ if (action === "dismiss") return (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineMedium, { size: 16 });
49188
+ return (0, react.createElement)("svg", {
49189
+ width: 16,
49190
+ height: 16,
49191
+ viewBox: "0 0 24 24",
49192
+ fill: "none",
49193
+ stroke: "currentColor",
49194
+ strokeWidth: 2,
49195
+ strokeLinecap: "round",
49196
+ strokeLinejoin: "round",
49197
+ "aria-hidden": true,
49198
+ focusable: false
49199
+ }, (0, react.createElement)("path", { d: "M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z" }), (0, react.createElement)("circle", {
49200
+ cx: 12,
49201
+ cy: 12,
49202
+ r: 2.5
49203
+ }));
49204
+ }
49205
+ function notificationToolbarIcon(icon) {
49206
+ if (icon === "clear") return (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineMedium, { size: 16 });
49207
+ if (icon === "pending") return (0, react.createElement)("svg", {
49208
+ width: 16,
49209
+ height: 16,
49210
+ viewBox: "0 0 24 24",
49211
+ fill: "none",
49212
+ stroke: "currentColor",
49213
+ strokeWidth: 1.8,
49214
+ strokeLinecap: "round",
49215
+ strokeLinejoin: "round",
49216
+ "aria-hidden": true,
49217
+ focusable: false
49218
+ }, [
49219
+ (0, react.createElement)("path", {
49220
+ key: "hand",
49221
+ d: "M9 14V6.5a1.5 1.5 0 0 1 3 0v4a1.5 1.5 0 0 1 3 0v1a1.5 1.5 0 0 1 3 0v1a1.5 1.5 0 0 1 3 0v4c0 4-2.5 6-6.5 6h-1c-2.2 0-3.6-.8-5.1-2.3L4.2 15a1.7 1.7 0 0 1 2.4-2.4L9 15Z"
49222
+ }),
49223
+ (0, react.createElement)("path", {
49224
+ key: "fingers",
49225
+ d: "M12 10.5v3M15 11.5v3M18 12.5V15"
49226
+ }),
49227
+ (0, react.createElement)("path", {
49228
+ key: "tap",
49229
+ d: "M6.7 8.9a4.5 4.5 0 1 1 8.2-1.6"
49230
+ })
49231
+ ]);
49232
+ return (0, react.createElement)("svg", {
49233
+ width: 16,
49234
+ height: 16,
49235
+ viewBox: "0 0 24 24",
49236
+ fill: "none",
49237
+ stroke: "currentColor",
49238
+ strokeWidth: 1.8,
49239
+ strokeLinecap: "round",
49240
+ strokeLinejoin: "round",
49241
+ "aria-hidden": true,
49242
+ focusable: false
49243
+ }, (0, react.createElement)("path", { d: icon === "collapse" ? "M6 9l6 6 6-6" : icon === "expand" ? "M6 15l6-6 6 6" : icon === "unread" ? "M6 10a6 6 0 0 1 12 0c0 5 2 5 2 7H4c0-2 2-2 2-7Zm3 10h6" : "" }));
49244
+ }
49245
+ function formatNotificationCount(count) {
49246
+ return count > 99 ? "99+" : String(count);
49247
+ }
48369
49248
  //#endregion
48370
49249
  //#region src/client/avatar/floating.ts
48371
49250
  const POSITION_KEY = "codingns-assistant-avatar-position";
@@ -48482,11 +49361,15 @@ ${scope} [data-dockkit-strip-tabs] {
48482
49361
  };
48483
49362
  const captionLayout = floatingCallCaptionLayout(position.x, position.y, size, height, viewport.width, viewport.height);
48484
49363
  const notificationLayout = notification === void 0 ? void 0 : floatingAssistantNotificationLayout(position.x, position.y, size, height, viewport.width, viewport.height, call ? captionLayout.above : void 0);
49364
+ const notificationTailStyle = notificationLayout === void 0 ? {} : assistantNotificationTailStyle(size / 2, Number(notificationLayout.style.left ?? 0), Number(notificationLayout.style.width ?? size), notificationLayout.tail);
48485
49365
  const notificationStyle = notificationLayout === void 0 ? void 0 : {
48486
49366
  position: "absolute",
48487
- ...notificationLayout,
48488
- ...assistantNotificationTailStyle(size / 2, Number(notificationLayout.left ?? 0), Number(notificationLayout.width ?? size))
49367
+ ...notificationLayout.style,
49368
+ maxHeight: void 0,
49369
+ "--codingns-notification-max-height": `${Number(notificationLayout.style.maxHeight ?? 276)}px`,
49370
+ ...notificationTailStyle
48489
49371
  };
49372
+ const notificationNode = typeof notification === "function" ? notification(notificationLayout?.tail ?? "bottom", notificationLayout === void 0 ? void 0 : notificationLayout.tail === "left" || notificationLayout.tail === "right" ? assistantNotificationTailTarget(height / 2, Number(notificationLayout.style.top ?? 0)) : assistantNotificationTailTarget(size / 2, Number(notificationLayout.style.left ?? 0)), notificationTailStyle) : notification;
48490
49373
  return (0, react.createElement)("div", {
48491
49374
  ...kind === "avatar" ? { "data-codingns-floating-avatar": true } : { "data-codingns-floating-call": true },
48492
49375
  style: {
@@ -48544,7 +49427,7 @@ ${scope} [data-dockkit-strip-tabs] {
48544
49427
  t,
48545
49428
  ...captionLayout,
48546
49429
  onOpen
48547
- }) : null, notification && notificationStyle ? (0, react.createElement)("div", {
49430
+ }) : null, notificationNode && notificationStyle ? (0, react.createElement)("div", {
48548
49431
  "data-codingns-floating-notification-region": true,
48549
49432
  style: {
48550
49433
  ...notificationStyle,
@@ -48554,7 +49437,7 @@ ${scope} [data-dockkit-strip-tabs] {
48554
49437
  touchAction: "pan-y",
48555
49438
  userSelect: "text"
48556
49439
  }
48557
- }, notification) : null);
49440
+ }, notificationNode) : null);
48558
49441
  }
48559
49442
  function readPosition(width, height, positionKey) {
48560
49443
  if (typeof window === "undefined") return {
@@ -48687,6 +49570,10 @@ ${scope} [data-dockkit-strip-tabs] {
48687
49570
  ...record.unchanged === void 0 ? {} : { unchanged: record.unchanged }
48688
49571
  };
48689
49572
  }
49573
+ /** 空快照只代表通知中心已经返回结果,不应被当成需要挂载气泡的通知。 */
49574
+ function assistantNotificationSnapshotHasNotifications(snapshot) {
49575
+ return snapshot.primary !== null || snapshot.unreadCount > 0 || snapshot.pendingCount > 0 || snapshot.capabilities.some((entry) => Boolean(entry.reason) && (!entry.completed || !entry.error || !entry.requests || !entry.resolve || !entry.navigation));
49576
+ }
48690
49577
  //#endregion
48691
49578
  //#region src/client/avatar/desktop-bridge.ts
48692
49579
  /** 普通浏览器即使连接到 Desktop 的 Host,也只能使用页面内形象。 */
@@ -48757,7 +49644,7 @@ ${scope} [data-dockkit-strip-tabs] {
48757
49644
  for (const event of status.noticeEvents) {
48758
49645
  if (disposed) return;
48759
49646
  if (event.ownerId !== ownerId || event.generation !== status.generation || !Number.isSafeInteger(event.sequence) || event.sequence <= (noticeAck?.sequence ?? 0) || typeof event.noticeId !== "string" || !Number.isSafeInteger(event.noticeGeneration)) continue;
48760
- const handler = event.type === "notice-presented" ? hooks.onNoticePresented : event.type === "notice-action" && (event.action === "open" || event.action === "dismiss") ? hooks.onNoticeAction : void 0;
49647
+ const handler = event.type === "notice-presented" ? hooks.onNoticePresented : event.type === "notice-action" && (event.action === "open" || event.action === "collapse" || event.action === "dismiss" || event.action === "dismiss-all") ? hooks.onNoticeAction : void 0;
48761
49648
  if (!handler) break;
48762
49649
  try {
48763
49650
  await handler(event);
@@ -48911,6 +49798,8 @@ ${scope} [data-dockkit-strip-tabs] {
48911
49798
  presented = /* @__PURE__ */ new Set();
48912
49799
  opens = /* @__PURE__ */ new Map();
48913
49800
  knownNotices = /* @__PURE__ */ new Map();
49801
+ /** 最近一次前台选择;串行队列执行前再次校验,避免旧选择晚到时误读新会话。 */
49802
+ sessionSelectionKey;
48914
49803
  constructor(rpc, navigate, intervalMs = 750, now = Date.now) {
48915
49804
  this.rpc = rpc;
48916
49805
  this.navigate = navigate;
@@ -48944,6 +49833,7 @@ ${scope} [data-dockkit-strip-tabs] {
48944
49833
  this.presented.clear();
48945
49834
  this.opens.clear();
48946
49835
  this.knownNotices.clear();
49836
+ this.sessionSelectionKey = void 0;
48947
49837
  this.publish({ loading: enabled });
48948
49838
  if (enabled) {
48949
49839
  if (typeof this.rpc.open === "function") {
@@ -49104,6 +49994,52 @@ ${scope} [data-dockkit-strip-tabs] {
49104
49994
  });
49105
49995
  await this.refresh(true);
49106
49996
  }
49997
+ /** 一键清理只清空当前代次的气泡展示;DSH 侧未读、待处理与请求身份都由 Host 原样保留。 */
49998
+ async clearAll(generation) {
49999
+ const epoch = this.epoch;
50000
+ this.requireFrame(generation);
50001
+ await this.enqueue(async (signal) => {
50002
+ await this.call("clear-all", { generation }, signal);
50003
+ this.assertCurrent(epoch, signal);
50004
+ }, epoch).catch((error) => {
50005
+ if (epoch === this.epoch && this.enabled && !this.disposed) this.publish({
50006
+ ...this.snapshot,
50007
+ error: message(error),
50008
+ errorNoticeId: void 0,
50009
+ errorGeneration: void 0
50010
+ });
50011
+ throw error;
50012
+ });
50013
+ await this.refresh(true);
50014
+ }
50015
+ /** 当前会话一旦被 DSH 选中即提交会话级已读;空选择只取消尚未发出的旧选择。 */
50016
+ markSessionRead(target) {
50017
+ const key = target === void 0 ? void 0 : JSON.stringify([
50018
+ target.hostId,
50019
+ target.workspaceId,
50020
+ target.sessionId
50021
+ ]);
50022
+ this.sessionSelectionKey = key;
50023
+ if (target === void 0 || !this.enabled || this.disposed) return Promise.resolve();
50024
+ if (this.snapshot.frame === void 0) return this.refresh(true).then(() => this.sessionSelectionKey === key ? this.markSessionRead(target) : void 0);
50025
+ const epoch = this.epoch;
50026
+ return this.enqueue(async (signal) => {
50027
+ if (this.sessionSelectionKey !== key) return;
50028
+ const frame = this.snapshot.frame;
50029
+ if (frame === void 0) return;
50030
+ await this.call("read-session", {
50031
+ generation: frame.generation,
50032
+ ...target
50033
+ }, signal);
50034
+ this.assertCurrent(epoch, signal);
50035
+ }, epoch).then(() => this.refresh(true)).catch((error) => {
50036
+ if (epoch === this.epoch && this.enabled && !this.disposed) this.publish({
50037
+ ...this.snapshot,
50038
+ error: message(error)
50039
+ });
50040
+ throw error;
50041
+ });
50042
+ }
49107
50043
  /** 查看由 Host 返回可信目标,公开导航成功后才标记已读。 */
49108
50044
  open(noticeId, generation, expectedConnectionGeneration) {
49109
50045
  try {
@@ -49169,6 +50105,7 @@ ${scope} [data-dockkit-strip-tabs] {
49169
50105
  this.presented.clear();
49170
50106
  this.opens.clear();
49171
50107
  this.knownNotices.clear();
50108
+ this.sessionSelectionKey = void 0;
49172
50109
  this.listeners.clear();
49173
50110
  this.snapshot = { loading: false };
49174
50111
  }
@@ -49320,6 +50257,52 @@ ${scope} [data-dockkit-strip-tabs] {
49320
50257
  }
49321
50258
  //#endregion
49322
50259
  //#region src/dsh-capabilities/client/assistant-session-navigation-adapter.ts
50260
+ /** 读取 DSH 的唯一前台会话选择源;没有该能力的旧宿主不猜测当前会话。 */
50261
+ function readAssistantSessionSelectionStore(context) {
50262
+ try {
50263
+ const selection = (context?.get?.("uiWorkspace"))?.selection;
50264
+ if (typeof selection !== "object" || selection === null) return void 0;
50265
+ const store = selection;
50266
+ return typeof store.getSnapshot === "function" && typeof store.subscribe === "function" ? store : void 0;
50267
+ } catch {
50268
+ return;
50269
+ }
50270
+ }
50271
+ /** 将本地/远端虚拟选择归一化为 Host 可匹配的会话三元组。 */
50272
+ function normalizeAssistantSessionSelection(value, context) {
50273
+ const record = asRecord$1(value);
50274
+ if (record === void 0) return void 0;
50275
+ const rawSessionId = text$1(record.sessionId) ?? text$1(asRecord$1(record.subagentAddress)?.parentSessionId);
50276
+ const rawWorkspaceId = text$1(record.workspaceId) ?? text$1(asRecord$1(record.workspace)?.workspaceId) ?? (rawSessionId === void 0 ? void 0 : readSessionWorkspaceId(context, rawSessionId));
50277
+ if (rawSessionId === void 0 || rawWorkspaceId === void 0) return void 0;
50278
+ const session = require_primitives_adapter.parseVirtualSessionId(rawSessionId);
50279
+ const workspace = require_primitives_adapter.parseVirtualWorkspaceId(rawWorkspaceId);
50280
+ if (session !== null && workspace !== null && session.hostId !== workspace.hostId) return void 0;
50281
+ const hostId = session?.hostId ?? workspace?.targetHostId ?? "local";
50282
+ const workspaceId = workspace?.workspaceId ?? (hostId !== "local" && rawWorkspaceId.startsWith(`${hostId}:`) ? rawWorkspaceId.slice(hostId.length + 1) : rawWorkspaceId);
50283
+ const sessionId = session?.sessionId ?? rawSessionId;
50284
+ return {
50285
+ hostId: hostId === "local" ? "local" : hostId,
50286
+ workspaceId,
50287
+ sessionId
50288
+ };
50289
+ }
50290
+ /** 从原生 Workspace 列表按成员关系反查当前会话,保留远端虚拟 ID。 */
50291
+ function readSessionWorkspaceId(context, sessionId) {
50292
+ try {
50293
+ const snapshot = require_native_workspace_store.readNativeWorkspaceSnapshot(context);
50294
+ const exact = snapshot?.items.find((workspace) => workspace.sessionIds.includes(sessionId));
50295
+ if (exact !== void 0) return exact.workspaceId;
50296
+ const parsed = require_primitives_adapter.parseVirtualSessionId(sessionId);
50297
+ if (parsed === null) return void 0;
50298
+ return (snapshot?.items.find((workspace) => workspace.sessionIds.some((candidate) => {
50299
+ const value = require_primitives_adapter.parseVirtualSessionId(candidate);
50300
+ return value?.hostId === parsed.hostId && value.sessionId === parsed.sessionId;
50301
+ })))?.workspaceId;
50302
+ } catch {
50303
+ return;
50304
+ }
50305
+ }
49323
50306
  /** Host 负责目标与连接代次校验,本适配器只把可信目标交给原生导航。 */
49324
50307
  async function openAssistantNotificationSession(services, target, signal) {
49325
50308
  signal?.throwIfAborted();
@@ -49356,6 +50339,12 @@ ${scope} [data-dockkit-strip-tabs] {
49356
50339
  return;
49357
50340
  }
49358
50341
  }
50342
+ function asRecord$1(value) {
50343
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
50344
+ }
50345
+ function text$1(value) {
50346
+ return typeof value === "string" && value.trim() !== "" ? value : void 0;
50347
+ }
49359
50348
  //#endregion
49360
50349
  //#region src/client/features/global-voice-assistant.ts
49361
50350
  const GLOBAL_ASSISTANT_BUTTON_SIZE = 38;
@@ -49483,18 +50472,43 @@ ${scope} [data-dockkit-strip-tabs] {
49483
50472
  notificationsEnabled,
49484
50473
  notificationKey
49485
50474
  ]);
50475
+ (0, react.useEffect)(() => {
50476
+ const context = services.uiContext;
50477
+ if (context === void 0) return void 0;
50478
+ const attach = (scope) => {
50479
+ const selection = readAssistantSessionSelectionStore(scope);
50480
+ if (selection === void 0) return void 0;
50481
+ const sync = () => {
50482
+ notifications.markSessionRead(normalizeAssistantSessionSelection(selection.getSnapshot(), scope)).catch(() => void 0);
50483
+ };
50484
+ sync();
50485
+ return selection.subscribe(sync);
50486
+ };
50487
+ if (typeof context.inject === "function") {
50488
+ const fiber = context.inject(["uiWorkspace"], (scope) => {
50489
+ const unsubscribe = attach(scope);
50490
+ if (unsubscribe === void 0) return;
50491
+ scope.effect(() => unsubscribe, "codingns4dsh: assistant notification session read");
50492
+ });
50493
+ return () => {
50494
+ fiber.dispose();
50495
+ };
50496
+ }
50497
+ return attach(context);
50498
+ }, [notifications, services.uiContext]);
49486
50499
  const presentNotice = (0, react.useCallback)((noticeId, generation, kind) => {
49487
50500
  notifications.acknowledge(noticeId, generation, "presented", kind).catch(() => void 0);
49488
50501
  }, [notifications]);
49489
50502
  const dismissNotice = (0, react.useCallback)((noticeId, generation, connectionGeneration) => {
49490
50503
  notifications.acknowledge(noticeId, generation, "dismiss", void 0, connectionGeneration).catch(() => void 0);
49491
50504
  }, [notifications]);
50505
+ const collapseNotice = (0, react.useCallback)((noticeId, generation, connectionGeneration) => {
50506
+ notifications.acknowledge(noticeId, generation, "collapse", void 0, connectionGeneration).catch(() => void 0);
50507
+ }, [notifications]);
49492
50508
  const openNotice = (0, react.useCallback)((noticeId, generation, connectionGeneration) => {
49493
50509
  notifications.open(noticeId, generation, connectionGeneration).catch(() => void 0);
49494
50510
  }, [notifications]);
49495
- const pageNotices = (0, react.useCallback)((cursor) => {
49496
- notifications.page(cursor);
49497
- }, [notifications]);
50511
+ const clearAllNotices = (0, react.useCallback)((generation) => notifications.clearAll(generation), [notifications]);
49498
50512
  (0, react.useEffect)(() => {
49499
50513
  if (adapter !== void 0) {
49500
50514
  setSnapshot({
@@ -49763,7 +50777,7 @@ ${scope} [data-dockkit-strip-tabs] {
49763
50777
  userText: partialText || liveUserText,
49764
50778
  assistantText: liveAssistantText
49765
50779
  } : void 0;
49766
- const noticeFrame = notificationsEnabled ? notificationSnapshot.frame : void 0;
50780
+ const noticeFrame = notificationsEnabled && notificationSnapshot.frame !== void 0 && assistantNotificationSnapshotHasNotifications(notificationSnapshot.frame) ? notificationSnapshot.frame : void 0;
49767
50781
  const reaction = assistantNotificationReaction(noticeFrame);
49768
50782
  const desktopNotification = desktopAssistantNotification(noticeFrame, notificationSnapshot.errorNoticeId === noticeFrame?.primary?.noticeId ? notificationSnapshot.error : void 0);
49769
50783
  const desktopNotificationSnapshot = readDesktopAssistantNotificationSnapshot(noticeFrame);
@@ -49781,7 +50795,9 @@ ${scope} [data-dockkit-strip-tabs] {
49781
50795
  onNoticePresented: (event) => notifications.acknowledge(event.noticeId, event.noticeGeneration, "presented", event.noticeKind, event.connectionGeneration),
49782
50796
  onNoticeAction: async (event) => {
49783
50797
  await notifications.refresh(true);
49784
- return event.action === "open" ? notifications.open(event.noticeId, event.noticeGeneration, event.connectionGeneration) : notifications.acknowledge(event.noticeId, event.noticeGeneration, "dismiss", void 0, event.connectionGeneration);
50798
+ if (event.action === "dismiss-all") return notifications.clearAll(event.noticeGeneration);
50799
+ if (event.action === "open") return notifications.open(event.noticeId, event.noticeGeneration, event.connectionGeneration);
50800
+ return notifications.acknowledge(event.noticeId, event.noticeGeneration, event.action === "collapse" ? "collapse" : "dismiss", void 0, event.connectionGeneration);
49785
50801
  }
49786
50802
  });
49787
50803
  const nativeFloating = floatingEnabled && desktopAvatar.native;
@@ -49793,7 +50809,7 @@ ${scope} [data-dockkit-strip-tabs] {
49793
50809
  borderRadius: 12,
49794
50810
  overflowWrap: "anywhere"
49795
50811
  }
49796
- }, notificationSnapshot.error) : void 0 : (0, react.createElement)(AssistantNotificationBubble, {
50812
+ }, notificationSnapshot.error) : void 0 : (tail, tailTarget, tailStyle) => (0, react.createElement)(AssistantNotificationBubble, {
49797
50813
  frame: noticeFrame,
49798
50814
  t,
49799
50815
  error: notificationSnapshot.error,
@@ -49801,8 +50817,12 @@ ${scope} [data-dockkit-strip-tabs] {
49801
50817
  autoCloseSeconds: notificationSettings.autoCloseSeconds,
49802
50818
  onPresented: presentNotice,
49803
50819
  onDismiss: dismissNotice,
50820
+ onAutoClose: collapseNotice,
49804
50821
  onOpen: openNotice,
49805
- onPage: pageNotices
50822
+ onClearAll: clearAllNotices,
50823
+ tailSide: tail,
50824
+ tailTarget,
50825
+ tailStyle
49806
50826
  });
49807
50827
  return (0, react.createElement)("div", {
49808
50828
  "data-codingns-global-voice": "true",
@@ -50950,6 +51970,928 @@ ${scope} [data-dockkit-strip-tabs] {
50950
51970
  }
50951
51971
  };
50952
51972
  //#endregion
51973
+ //#region src/client/session-translation-chat.ts
51974
+ /**
51975
+ * 把翻译状态和查看原文的入口放回对应消息块,不占用 AI 回复底部的统一操作栏。
51976
+ */
51977
+ function TranslationAction(props) {
51978
+ const source = props.record === void 0 ? readEventSource(props.services, props.sessionId) : void 0;
51979
+ const [, refresh] = (0, react.useState)(0);
51980
+ const [open, setOpen] = (0, react.useState)(false);
51981
+ const rootRef = (0, react.useRef)(null);
51982
+ const buttonRef = (0, react.useRef)(null);
51983
+ const popupRef = (0, react.useRef)(null);
51984
+ const [popupPosition, setPopupPosition] = (0, react.useState)(null);
51985
+ (0, react.useEffect)(() => {
51986
+ if (source === void 0) return void 0;
51987
+ return source.subscribe(() => refresh((value) => value + 1));
51988
+ }, [source]);
51989
+ useDismissOnOutsidePointer(rootRef, open, () => setOpen(false), { portal: popupRef });
51990
+ (0, react.useLayoutEffect)(() => {
51991
+ if (!open || typeof window === "undefined") {
51992
+ setPopupPosition(null);
51993
+ return;
51994
+ }
51995
+ const updatePosition = () => {
51996
+ const button = buttonRef.current;
51997
+ if (button === null) return;
51998
+ const rect = button.getBoundingClientRect();
51999
+ const edge = 12;
52000
+ const gap = 8;
52001
+ const width = Math.max(1, Math.min(420, window.innerWidth - 24));
52002
+ const availableAbove = Math.max(0, rect.top - gap - edge);
52003
+ const availableBelow = Math.max(0, window.innerHeight - rect.bottom - gap - edge);
52004
+ const preferredHeight = Math.min(440, window.innerHeight * .65);
52005
+ const placement = availableAbove >= preferredHeight || availableBelow < preferredHeight && availableAbove > availableBelow ? "above" : "below";
52006
+ const maxHeight = Math.min(preferredHeight, placement === "above" ? availableAbove : availableBelow);
52007
+ const desiredLeft = props.popupAlign === "end" ? rect.right - width : rect.left;
52008
+ const maxLeft = Math.max(edge, window.innerWidth - edge - width);
52009
+ setPopupPosition({
52010
+ left: Math.min(maxLeft, Math.max(edge, desiredLeft)),
52011
+ top: placement === "above" ? rect.top - gap : rect.bottom + gap,
52012
+ width,
52013
+ maxHeight,
52014
+ placement
52015
+ });
52016
+ };
52017
+ updatePosition();
52018
+ window.addEventListener("resize", updatePosition);
52019
+ window.addEventListener("scroll", updatePosition, true);
52020
+ return () => {
52021
+ window.removeEventListener("resize", updatePosition);
52022
+ window.removeEventListener("scroll", updatePosition, true);
52023
+ };
52024
+ }, [open, props.popupAlign]);
52025
+ const record = props.record ?? (source === void 0 || props.lookupText === void 0 ? void 0 : findTranslationRecordForText(source.getSnapshot(), props.lookupText, props.outputKind, props.turn, props.step));
52026
+ if (record === void 0) return null;
52027
+ const failed = record.failed === true;
52028
+ const alreadyTargetLanguage = record.alreadyTargetLanguage === true;
52029
+ const statusText = failed ? props.translationT("translation.statusFailed") : alreadyTargetLanguage ? props.translationT("translation.statusNotNeeded") : props.translationT("translation.statusTranslated");
52030
+ const model = formatModel(record.model, props.translationT);
52031
+ const detailsId = `codingns-translation-details-${safeDomId(props.messageId)}`;
52032
+ const details = open && popupPosition !== null ? (0, react.createElement)("div", {
52033
+ ref: popupRef,
52034
+ role: "dialog",
52035
+ "aria-label": props.translationT("translation.detailsTitle"),
52036
+ id: detailsId,
52037
+ style: {
52038
+ position: "fixed",
52039
+ zIndex: 2147483647,
52040
+ left: popupPosition.left,
52041
+ top: popupPosition.top,
52042
+ width: popupPosition.width,
52043
+ maxHeight: popupPosition.maxHeight,
52044
+ boxSizing: "border-box",
52045
+ ...popupPosition.placement === "above" ? { transform: "translateY(-100%)" } : {},
52046
+ overflow: "auto",
52047
+ padding: "10px 12px",
52048
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
52049
+ borderRadius: 8,
52050
+ color: require_theme.dshThemeColor.labelPrimary,
52051
+ background: require_theme.dshThemeColor.menuBackground,
52052
+ boxShadow: "0 8px 24px color-mix(in srgb, CanvasText 16%, transparent)",
52053
+ fontSize: require_theme.uiFontSize(12),
52054
+ lineHeight: 1.45
52055
+ }
52056
+ }, (0, react.createElement)("div", { style: {
52057
+ display: "flex",
52058
+ justifyContent: "space-between",
52059
+ gap: 10,
52060
+ fontWeight: 600
52061
+ } }, (0, react.createElement)("span", void 0, props.translationT("translation.detailsTitle")), (0, react.createElement)("span", { style: {
52062
+ color: failed ? require_theme.dshThemeColor.error : alreadyTargetLanguage ? require_theme.dshThemeColor.accent : require_theme.dshThemeColor.success,
52063
+ fontWeight: 400
52064
+ } }, statusText)), record.sourceLanguage === void 0 ? null : (0, react.createElement)("div", { style: {
52065
+ marginTop: 8,
52066
+ color: require_theme.dshThemeColor.labelSecondary
52067
+ } }, `${props.translationT("translation.detectedLanguage")}: ${props.translationT(`translation.language.${record.sourceLanguage}`)}`), (0, react.createElement)(DetailBlock, {
52068
+ label: props.translationT("translation.originalText"),
52069
+ value: record.original
52070
+ }), (0, react.createElement)(DetailBlock, {
52071
+ label: props.translationT("translation.translatedText"),
52072
+ value: record.translated
52073
+ }), (0, react.createElement)("div", { style: {
52074
+ marginTop: 8,
52075
+ color: require_theme.dshThemeColor.labelSecondary
52076
+ } }, `${props.translationT("translation.modelUsed")}: ${alreadyTargetLanguage ? props.translationT("translation.modelNotUsed") : model}`)) : null;
52077
+ const renderedDetails = details === null ? null : typeof document === "undefined" ? details : (0, import_react_dom.createPortal)(details, document.body);
52078
+ return (0, react.createElement)("div", {
52079
+ ref: rootRef,
52080
+ style: {
52081
+ position: "relative",
52082
+ display: "inline-flex",
52083
+ alignItems: "center",
52084
+ verticalAlign: "middle",
52085
+ ...props.inline === true ? { marginInlineStart: 4 } : {}
52086
+ },
52087
+ "data-codingns-translation-action": props.messageId
52088
+ }, (0, react.createElement)("button", {
52089
+ ref: buttonRef,
52090
+ type: "button",
52091
+ title: `${statusText} · ${props.translationT("translation.viewDetails")}`,
52092
+ "aria-label": `${statusText} · ${props.translationT("translation.viewDetails")}`,
52093
+ "aria-expanded": open,
52094
+ "aria-controls": detailsId,
52095
+ onClick: () => setOpen((value) => !value),
52096
+ style: translationButtonStyle
52097
+ }, (0, react.createElement)(TranslationGlyph), (0, react.createElement)("span", {
52098
+ "aria-hidden": true,
52099
+ style: {
52100
+ position: "absolute",
52101
+ top: 1,
52102
+ right: 1,
52103
+ display: "grid",
52104
+ placeItems: "center",
52105
+ width: 10,
52106
+ height: 10,
52107
+ zIndex: 1,
52108
+ boxSizing: "border-box",
52109
+ border: "1.5px solid var(--dsw-alias-bg-base, Canvas)",
52110
+ borderRadius: "50%",
52111
+ color: "#fff",
52112
+ background: failed ? require_theme.dshThemeColor.error : alreadyTargetLanguage ? require_theme.dshThemeColor.accent : require_theme.dshThemeColor.success,
52113
+ fontSize: 6,
52114
+ fontWeight: 700,
52115
+ lineHeight: 1
52116
+ }
52117
+ }, failed ? "!" : "✓")), renderedDetails);
52118
+ }
52119
+ function TranslationGlyph() {
52120
+ return (0, react.createElement)("svg", {
52121
+ viewBox: "0 0 24 24",
52122
+ width: 16,
52123
+ height: 16,
52124
+ fill: "none",
52125
+ stroke: "currentColor",
52126
+ strokeWidth: 1.5,
52127
+ strokeLinecap: "round",
52128
+ strokeLinejoin: "round",
52129
+ "aria-hidden": true
52130
+ }, (0, react.createElement)("path", { d: "M3.5 5.5h10M8.5 3.5v2M12.5 5.5c0 4.4-3.3 7.2-8 9" }), (0, react.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" }));
52131
+ }
52132
+ const messageActionButtonStyle = {
52133
+ position: "relative",
52134
+ display: "inline-flex",
52135
+ alignItems: "center",
52136
+ justifyContent: "center",
52137
+ width: 28,
52138
+ height: 28,
52139
+ padding: 0,
52140
+ border: 0,
52141
+ borderRadius: "var(--dsw-radius-sm, 6px)",
52142
+ color: require_theme.dshThemeColor.labelTertiary,
52143
+ background: "transparent",
52144
+ cursor: "pointer"
52145
+ };
52146
+ const translationButtonStyle = {
52147
+ ...messageActionButtonStyle,
52148
+ transition: "background-color 100ms ease, color 100ms ease"
52149
+ };
52150
+ function DetailBlock({ label, value }) {
52151
+ return (0, react.createElement)("div", { style: { marginTop: 8 } }, (0, react.createElement)("div", { style: {
52152
+ marginBottom: 3,
52153
+ color: require_theme.dshThemeColor.labelSecondary,
52154
+ fontWeight: 600
52155
+ } }, label), (0, react.createElement)("pre", { style: {
52156
+ margin: 0,
52157
+ whiteSpace: "pre-wrap",
52158
+ overflowWrap: "anywhere",
52159
+ fontFamily: "inherit"
52160
+ } }, value));
52161
+ }
52162
+ /** 按原生 user 节点的结构渲染用户消息,并把翻译按钮放在复制按钮前。 */
52163
+ function UserMessageNodeView(props) {
52164
+ const data = props.node.data;
52165
+ const parts = splitUserContent(data.content);
52166
+ const record = readInputRecord({ source: data.source });
52167
+ if (record === void 0) return null;
52168
+ const translationRecord = inputRecordForAction(record);
52169
+ const labels = Array.isArray(data.referenceLabels) ? data.referenceLabels.filter((label) => typeof label === "string") : [];
52170
+ const skillNames = Array.isArray(data.skillNames) ? data.skillNames.filter((name) => typeof name === "string") : [];
52171
+ const showBubble = parts.text !== "" || parts.rest.length > 0;
52172
+ const compactImages = parts.attachments.filter((attachment) => attachment.type === "image").length > 1;
52173
+ const messageId = `user-${String(data.seq ?? props.node.id ?? "message")}`;
52174
+ const chatT = props.t;
52175
+ return (0, react.createElement)("div", {
52176
+ "data-chat-flow-kind": "user",
52177
+ style: {
52178
+ display: "flex",
52179
+ flexDirection: "column",
52180
+ alignItems: "flex-end",
52181
+ gap: 6
52182
+ }
52183
+ }, (0, react.createElement)("div", { style: {
52184
+ display: "flex",
52185
+ flexDirection: "column",
52186
+ alignItems: "flex-end",
52187
+ gap: 8,
52188
+ minWidth: 0,
52189
+ maxWidth: "min(calc(var(--dsh-chat-content-width, 748px) * .702), 82%)"
52190
+ } }, parts.attachments.length > 0 ? (0, react.createElement)("div", {
52191
+ "data-message-attachments": true,
52192
+ style: {
52193
+ display: "flex",
52194
+ flexWrap: "wrap",
52195
+ justifyContent: "flex-end",
52196
+ gap: 8,
52197
+ maxWidth: "100%"
52198
+ }
52199
+ }, ...parts.attachments.map((attachment, index) => attachment.type === "image" ? (0, react.createElement)("div", { key: `image-${index}` }, props.renderMessageImages({
52200
+ images: [{ attachment: attachment.attachment }],
52201
+ align: "end",
52202
+ compact: compactImages
52203
+ })) : createFileAttachment(attachment.file, index))) : null, showBubble ? (0, react.createElement)("div", { style: {
52204
+ boxSizing: "border-box",
52205
+ maxWidth: "100%",
52206
+ padding: "10px 16px",
52207
+ borderRadius: "var(--dsw-radius-xl, 18px)",
52208
+ color: require_theme.dshThemeColor.labelPrimary,
52209
+ background: "var(--dsw-specific-bubble, var(--dsw-alias-interactive-bg-hover, rgba(127,127,127,.08)))",
52210
+ fontSize: "var(--dsh-content-font-size, 14px)",
52211
+ lineHeight: "calc(22px + var(--dsh-content-font-delta, 0px))",
52212
+ whiteSpace: "pre-wrap",
52213
+ wordBreak: "break-word"
52214
+ } }, (0, _deepseek_ai_dsh_client_ui_primitives.projectUserText)(parts.text, labels, skillNames, "skill", typeof props.openFile === "function" && typeof props.openSkill === "function" ? {
52215
+ openFile: props.openFile,
52216
+ openSkill: props.openSkill
52217
+ } : void 0), ...parts.rest.map((block, index) => (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.JsonBlock, {
52218
+ key: `extra-${index}`,
52219
+ label: chatT("message.extraBlock"),
52220
+ payload: block,
52221
+ truncatedLabel: (total) => chatT("json.truncated", { total })
52222
+ }))) : null), (0, react.createElement)("div", { style: {
52223
+ display: "flex",
52224
+ alignItems: "center",
52225
+ alignSelf: "flex-end",
52226
+ height: "calc(28px + var(--dsh-content-font-delta, 0px))",
52227
+ gap: 8,
52228
+ color: require_theme.dshThemeColor.labelTertiary
52229
+ } }, typeof data.time === "number" ? (0, react.createElement)("span", { style: {
52230
+ paddingRight: 12,
52231
+ color: require_theme.dshThemeColor.labelTertiary,
52232
+ fontSize: "var(--dsh-content-font-size-secondary, 13px)",
52233
+ whiteSpace: "nowrap"
52234
+ } }, formatMessageTime(data.time)) : null, translationRecord === void 0 ? null : (0, react.createElement)(TranslationAction, {
52235
+ messageId,
52236
+ sessionId: props.sessionId,
52237
+ services: props.services,
52238
+ translationT: props.translationT,
52239
+ popupAlign: "end",
52240
+ record: translationRecord
52241
+ }), (0, react.createElement)(CopyAction, {
52242
+ text: parts.text,
52243
+ t: chatT
52244
+ })));
52245
+ }
52246
+ function createFileAttachment(file, index) {
52247
+ const name = typeof file.name === "string" ? file.name : "";
52248
+ const bytes = typeof file.bytes === "number" ? file.bytes : 0;
52249
+ return (0, react.createElement)("span", {
52250
+ key: `file-${index}`,
52251
+ title: name,
52252
+ style: {
52253
+ display: "inline-flex",
52254
+ alignItems: "center",
52255
+ minWidth: 0,
52256
+ maxWidth: "100%",
52257
+ gap: 8,
52258
+ padding: "8px 10px",
52259
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
52260
+ borderRadius: 8,
52261
+ color: require_theme.dshThemeColor.labelPrimary,
52262
+ background: require_theme.dshThemeColor.surfaceSubtle
52263
+ }
52264
+ }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
52265
+ path: name,
52266
+ style: { flex: "none" }
52267
+ }), (0, react.createElement)("span", { style: {
52268
+ display: "flex",
52269
+ flexDirection: "column",
52270
+ minWidth: 0,
52271
+ textAlign: "left"
52272
+ } }, (0, react.createElement)("span", { style: {
52273
+ overflow: "hidden",
52274
+ textOverflow: "ellipsis",
52275
+ whiteSpace: "nowrap"
52276
+ } }, name), (0, react.createElement)("span", { style: {
52277
+ color: require_theme.dshThemeColor.labelSecondary,
52278
+ fontSize: 11
52279
+ } }, [(0, _deepseek_ai_dsh_client_ui_primitives.fileExtension)(name).toUpperCase().slice(0, 8), (0, _deepseek_ai_dsh_client_ui_primitives.fileSizeText)(bytes)].filter(Boolean).join(" "))));
52280
+ }
52281
+ function CopyAction({ text, t: chatT }) {
52282
+ const [copied, setCopied] = (0, react.useState)(false);
52283
+ const timer = (0, react.useRef)(void 0);
52284
+ (0, react.useEffect)(() => () => {
52285
+ if (timer.current !== void 0) clearTimeout(timer.current);
52286
+ }, []);
52287
+ return (0, react.createElement)("button", {
52288
+ type: "button",
52289
+ title: copied ? chatT("copied") : chatT("copy"),
52290
+ "aria-label": copied ? chatT("copied") : chatT("copy"),
52291
+ onClick: () => {
52292
+ (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(text).then((success) => {
52293
+ if (!success) return;
52294
+ setCopied(true);
52295
+ if (timer.current !== void 0) clearTimeout(timer.current);
52296
+ timer.current = setTimeout(() => {
52297
+ timer.current = void 0;
52298
+ setCopied(false);
52299
+ }, 1e3);
52300
+ });
52301
+ },
52302
+ style: messageActionButtonStyle
52303
+ }, (0, react.createElement)(copied ? _deepseek_ai_dsh_client_ui_primitives.IconCheckOutlineRegular : _deepseek_ai_dsh_client_ui_primitives.IconCopyOutlineRegular));
52304
+ }
52305
+ function splitUserContent(content) {
52306
+ const text = [];
52307
+ const attachments = [];
52308
+ const rest = [];
52309
+ if (typeof content === "string") return {
52310
+ text: content,
52311
+ attachments,
52312
+ rest
52313
+ };
52314
+ if (!Array.isArray(content)) return {
52315
+ text: "",
52316
+ attachments,
52317
+ rest
52318
+ };
52319
+ for (const value of content) {
52320
+ const block = asRecord(value);
52321
+ if (block?.type === "text" && typeof block.text === "string") text.push(block.text);
52322
+ else if (block?.type === "image" && block.attachment !== void 0) attachments.push({
52323
+ type: "image",
52324
+ attachment: block.attachment
52325
+ });
52326
+ else if (block?.type === "file" && asRecord(block.attachment) !== void 0) attachments.push({
52327
+ type: "file",
52328
+ file: asRecord(block.attachment)
52329
+ });
52330
+ else rest.push(value);
52331
+ }
52332
+ return {
52333
+ text: text.join(""),
52334
+ attachments,
52335
+ rest
52336
+ };
52337
+ }
52338
+ function formatMessageTime(time) {
52339
+ const date = new Date(time);
52340
+ const now = /* @__PURE__ */ new Date();
52341
+ const options = date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate() ? {
52342
+ hour: "2-digit",
52343
+ minute: "2-digit"
52344
+ } : {
52345
+ ...date.getFullYear() === now.getFullYear() ? {} : { year: "numeric" },
52346
+ month: "2-digit",
52347
+ day: "2-digit",
52348
+ hour: "2-digit",
52349
+ minute: "2-digit"
52350
+ };
52351
+ return new Intl.DateTimeFormat(void 0, options).format(date);
52352
+ }
52353
+ /** DSH assistant-step renderer: keep each translated text/reasoning block's source action adjacent to that block. */
52354
+ function AssistantTranslationNodeView(props) {
52355
+ const data = props.node.data;
52356
+ const blocks = Array.isArray(data.blocks) ? data.blocks : [];
52357
+ const streaming = data.status === "running";
52358
+ const chatT = props.t;
52359
+ const markdownLabels = (0, react.useMemo)(() => createMarkdownLabels(chatT), [chatT]);
52360
+ const pathImages = (0, react.useMemo)(() => ({ resolve: resolveMarkdownImagePath }), []);
52361
+ const eventSnapshot = readEventSource(props.services, props.sessionId)?.getSnapshot();
52362
+ const hasTranslation = blocks.some((block) => {
52363
+ if (block.kind !== "reasoning" && block.kind !== "text") return false;
52364
+ const outputKind = block.kind === "reasoning" ? "reasoning" : "assistant";
52365
+ const embedded = readRecord(block);
52366
+ if (embedded !== void 0 && (embedded.outputKind === void 0 || embedded.outputKind === outputKind)) return true;
52367
+ return eventSnapshot !== void 0 && findTranslationRecordForText(eventSnapshot, block.text, outputKind, data.turn, data.step) !== void 0;
52368
+ });
52369
+ const turn = props.node.location?.kind === "turn" || props.node.location?.kind === "step" ? props.node.location.turn : void 0;
52370
+ const tail = typeof props.useTurnData === "function" ? props.useTurnData("turn-tail") : void 0;
52371
+ const finalNode = data.finalNode;
52372
+ const fileOwner = turn?.status === "closed" && finalNode !== void 0 && tail?.closing?.finalNode?.seq === finalNode.seq ? {
52373
+ turn,
52374
+ seq: finalNode.seq,
52375
+ openFile: (path) => {
52376
+ props.openFile(path);
52377
+ }
52378
+ } : void 0;
52379
+ const mentions = fileOwner === void 0 || typeof props.fileMentions !== "function" ? void 0 : props.fileMentions(fileOwner);
52380
+ const turnProcess = props.turnProcess;
52381
+ const reasoningHidden = turnProcess !== void 0 && turnProcess.foldable === true && turnProcess.spec?.answerStep === data.step && turnProcess.spec?.inlineReasoning === true && turnProcess.open !== true;
52382
+ const rendered = [];
52383
+ const last = blocks.length - 1;
52384
+ const messageId = typeof finalNode?.messageId === "string" ? finalNode.messageId : `turn-${data.turn}-step-${data.step}`;
52385
+ if (!hasTranslation || !(streaming || data.status === "interrupted" || blocks.some((block) => block.kind !== "tool-call"))) return null;
52386
+ for (let index = 0; index < blocks.length; index += 1) {
52387
+ const block = blocks[index];
52388
+ if (block === void 0) continue;
52389
+ if (props.groupPart === "reasoning" && block.kind !== "reasoning") continue;
52390
+ if (props.groupPart === "response" && block.kind === "reasoning") continue;
52391
+ const running = streaming && index === last;
52392
+ if (block.kind === "text") {
52393
+ const translationRecord = readRecord(block);
52394
+ rendered.push((0, react.createElement)("div", {
52395
+ key: `text-${index}`,
52396
+ "data-codingns-translation-inline": true,
52397
+ style: { minWidth: 0 }
52398
+ }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
52399
+ text: block.text,
52400
+ streaming,
52401
+ labels: markdownLabels,
52402
+ fileMentions: mentions,
52403
+ pathImages
52404
+ }), (0, react.createElement)(TranslationAction, {
52405
+ messageId: `${messageId}-text-${index}`,
52406
+ sessionId: props.sessionId,
52407
+ services: props.services,
52408
+ translationT: props.translationT,
52409
+ lookupText: block.text,
52410
+ outputKind: "assistant",
52411
+ turn: data.turn,
52412
+ step: data.step,
52413
+ inline: true,
52414
+ ...translationRecord === void 0 ? {} : { record: translationRecord }
52415
+ })));
52416
+ continue;
52417
+ }
52418
+ if (block.kind === "reasoning") {
52419
+ rendered.push((0, react.createElement)(AssistantReasoningBlock, {
52420
+ key: `reasoning-${index}`,
52421
+ messageId: `${messageId}-reasoning-${index}`,
52422
+ sessionId: props.sessionId,
52423
+ services: props.services,
52424
+ translationT: props.translationT,
52425
+ chatT,
52426
+ text: block.text,
52427
+ turn: data.turn,
52428
+ step: data.step,
52429
+ running,
52430
+ hidden: reasoningHidden,
52431
+ reveal: () => turnProcess?.setOpen?.(true),
52432
+ useGroupAction: props.useGroupAction,
52433
+ renderFactorySlot: props.renderFactorySlot,
52434
+ markdownLabels,
52435
+ pathImages,
52436
+ mentions
52437
+ }));
52438
+ continue;
52439
+ }
52440
+ if (block.kind === "image") {
52441
+ const imageGroup = [block];
52442
+ while (index + 1 < blocks.length && blocks[index + 1]?.kind === "image") {
52443
+ const next = blocks[index + 1];
52444
+ if (next?.kind !== "image") break;
52445
+ imageGroup.push(next);
52446
+ index += 1;
52447
+ }
52448
+ rendered.push((0, react.createElement)("div", {
52449
+ key: `images-${index}`,
52450
+ style: { alignSelf: "flex-start" }
52451
+ }, props.renderMessageImages({
52452
+ images: imageGroup.map((image) => ({ attachment: image.attachment })),
52453
+ align: "start"
52454
+ })));
52455
+ continue;
52456
+ }
52457
+ if (block.kind === "other") rendered.push((0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.JsonBlock, {
52458
+ key: `other-${index}`,
52459
+ label: chatT("message.unknownBlock"),
52460
+ payload: block.block,
52461
+ truncatedLabel: (total) => chatT("json.truncated", { total })
52462
+ }));
52463
+ }
52464
+ return (0, react.createElement)("div", {
52465
+ "data-streaming": streaming || void 0,
52466
+ style: {
52467
+ display: "flex",
52468
+ flexDirection: "column",
52469
+ gap: 16,
52470
+ minWidth: 0,
52471
+ color: require_theme.dshThemeColor.labelPrimary,
52472
+ fontSize: "var(--dsh-content-font-size, 14px)",
52473
+ lineHeight: "calc(24px + var(--dsh-content-font-delta, 0px))"
52474
+ }
52475
+ }, ...rendered, data.status === "interrupted" ? (0, react.createElement)("span", { style: {
52476
+ alignSelf: "flex-start",
52477
+ padding: "0 6px",
52478
+ borderRadius: "var(--dsw-radius-sm, 4px)",
52479
+ color: require_theme.dshThemeColor.labelTertiary,
52480
+ background: require_theme.dshThemeColor.surfaceSubtle,
52481
+ fontSize: 11,
52482
+ lineHeight: "18px"
52483
+ } }, chatT("message.stopped")) : null);
52484
+ }
52485
+ function AssistantReasoningBlock(props) {
52486
+ const [expanded, setExpanded] = (0, react.useState)(false);
52487
+ const groupRef = props.useGroupAction?.(props.hidden, props.reveal);
52488
+ const summary = props.text.split("\n").map((line) => line.replace(/[`*_#>]/gu, "").trim()).find(Boolean) ?? "";
52489
+ const markdown = props.renderFactorySlot === void 0 ? (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
52490
+ text: props.text,
52491
+ streaming: props.running,
52492
+ labels: props.markdownLabels,
52493
+ fileMentions: props.mentions,
52494
+ pathImages: props.pathImages,
52495
+ variant: "compact"
52496
+ }) : props.renderFactorySlot("conversation.chat.reasoning.content", {
52497
+ text: props.text,
52498
+ running: props.running
52499
+ });
52500
+ return (0, react.createElement)("div", {
52501
+ ref: groupRef,
52502
+ hidden: props.hidden,
52503
+ "data-turn-process-inline": props.hidden || void 0
52504
+ }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
52505
+ icon: (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconThinkOutlineRegular, { size: 14 }),
52506
+ title: props.chatT("message.think"),
52507
+ open: expanded,
52508
+ expandable: true,
52509
+ expandOnRowClick: true,
52510
+ running: props.running,
52511
+ onToggle: () => setExpanded((value) => !value),
52512
+ collapsedContent: (0, react.createElement)("span", { style: {
52513
+ minWidth: 0,
52514
+ overflow: "hidden",
52515
+ color: require_theme.dshThemeColor.labelSecondary,
52516
+ fontSize: "var(--dsh-content-font-size-secondary, 13px)",
52517
+ textOverflow: "ellipsis",
52518
+ whiteSpace: "nowrap"
52519
+ } }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.TextShimmer, {
52520
+ active: props.running,
52521
+ children: summary
52522
+ }))
52523
+ }, expanded ? (0, react.createElement)("div", {
52524
+ "data-codingns-translation-inline": true,
52525
+ style: { paddingTop: 8 }
52526
+ }, markdown, (0, react.createElement)(TranslationAction, {
52527
+ messageId: props.messageId,
52528
+ sessionId: props.sessionId,
52529
+ services: props.services,
52530
+ translationT: props.translationT,
52531
+ lookupText: props.text,
52532
+ outputKind: "reasoning",
52533
+ ...props.turn === void 0 ? {} : { turn: props.turn },
52534
+ ...props.step === void 0 ? {} : { step: props.step },
52535
+ inline: true
52536
+ })) : null));
52537
+ }
52538
+ function createMarkdownLabels(chatT) {
52539
+ return {
52540
+ code: {
52541
+ copyLabel: chatT("copy"),
52542
+ copiedLabel: chatT("copied"),
52543
+ toolbarLabels: {
52544
+ codeLabel: chatT("codeBlock.title"),
52545
+ wrapLabel: chatT("codeBlock.wrap"),
52546
+ unwrapLabel: chatT("codeBlock.unwrap")
52547
+ }
52548
+ },
52549
+ footnotes: chatT("markdown.footnotes")
52550
+ };
52551
+ }
52552
+ function resolveMarkdownImagePath(value) {
52553
+ if (typeof document === "undefined" || !value.startsWith("/") || /^[/\\]{2}/u.test(value) || /[\u0000-\u001f\u007f]/u.test(value)) return void 0;
52554
+ const base = document.baseURI;
52555
+ if (!/^https?:/u.test(base) && !base.startsWith("dsh-app://app/")) return void 0;
52556
+ try {
52557
+ return new URL(`api/file?path=${encodeURIComponent(value)}`, base).href;
52558
+ } catch {
52559
+ return;
52560
+ }
52561
+ }
52562
+ /**
52563
+ * 注册两个有实际翻译记录的消息渲染器;DSH 的用户气泡内容仍由统一主题令牌控制。
52564
+ */
52565
+ function registerSessionTranslationChatUi(services) {
52566
+ const slots = services.slots;
52567
+ if (slots?.inject === void 0 || slots.register === void 0) return () => void 0;
52568
+ const removeStyles = installSessionTranslationChatStyles();
52569
+ const translationT = services.locale.bind("codingns");
52570
+ const removeUserRenderer = slots.inject("conversation.chat.node", () => slots.register({
52571
+ name: "conversation.chat.node",
52572
+ key: "user",
52573
+ priority: -100,
52574
+ locale: "chat",
52575
+ inject: (sessionId) => ({
52576
+ services,
52577
+ sessionId,
52578
+ translationT
52579
+ })
52580
+ }, UserMessageNodeView));
52581
+ const removeAssistantRenderer = slots.inject("conversation.chat.node", () => slots.register({
52582
+ name: "conversation.chat.node",
52583
+ key: "assistant-step",
52584
+ priority: -100,
52585
+ locale: "chat",
52586
+ inject: (sessionId) => ({
52587
+ services,
52588
+ sessionId,
52589
+ translationT
52590
+ })
52591
+ }, AssistantTranslationNodeView));
52592
+ return () => {
52593
+ removeAssistantRenderer();
52594
+ removeUserRenderer();
52595
+ removeStyles();
52596
+ };
52597
+ }
52598
+ function installSessionTranslationChatStyles() {
52599
+ if (typeof document === "undefined") return () => void 0;
52600
+ const styleId = "codingns-session-translation-chat";
52601
+ if (document.head.querySelector(`style[data-plugin-css="${styleId}"]`) !== null) return () => void 0;
52602
+ const style = document.createElement("style");
52603
+ style.dataset.plugin = "codingns4dsh";
52604
+ style.dataset.pluginCss = styleId;
52605
+ style.textContent = `
52606
+ [data-codingns-translation-inline] > div:not([data-codingns-translation-action]){display:contents}
52607
+ [data-codingns-translation-inline] > div:not([data-codingns-translation-action]) > p:last-child{display:inline;margin:0}
52608
+ `;
52609
+ document.head.appendChild(style);
52610
+ return () => style.remove();
52611
+ }
52612
+ function readEventSource(services, sessionId) {
52613
+ const sessions = services.sessions;
52614
+ try {
52615
+ return sessions?.binding?.(sessionId)?.eventSource;
52616
+ } catch {
52617
+ return;
52618
+ }
52619
+ }
52620
+ function findTranslationRecordForText(snapshot, text, outputKind, turn, step) {
52621
+ const entries = snapshot.entries ?? [];
52622
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
52623
+ const event = readEntryEvent(entries[index]);
52624
+ if (event === void 0 || !event.type.startsWith("assistant/")) continue;
52625
+ const eventData = asRecord(event.data);
52626
+ if (turn !== void 0 && typeof eventData?.turn === "number" && eventData.turn !== turn) continue;
52627
+ if (step !== void 0 && typeof eventData?.step === "number" && eventData.step !== step) continue;
52628
+ const candidates = [];
52629
+ collectRecordCandidates(event.data, candidates, /* @__PURE__ */ new Set());
52630
+ for (const candidate of candidates) {
52631
+ const record = readRecord(candidate);
52632
+ if (record === void 0 || record.translated !== text) continue;
52633
+ if (outputKind !== void 0 && record.outputKind !== void 0 && record.outputKind !== outputKind) continue;
52634
+ return record;
52635
+ }
52636
+ }
52637
+ }
52638
+ function collectRecordCandidates(value, candidates, seen, depth = 0) {
52639
+ if (Array.isArray(value)) {
52640
+ for (const item of value) collectRecordCandidates(item, candidates, seen, depth + 1);
52641
+ return;
52642
+ }
52643
+ const record = asRecord(value);
52644
+ if (record === void 0 || seen.has(record) || depth > 7) return;
52645
+ seen.add(record);
52646
+ if (readRecord(record) !== void 0 || asRecord(record.codingnsTranslation) !== void 0) candidates.push(record);
52647
+ for (const key of [
52648
+ "codingnsTranslation",
52649
+ "source",
52650
+ "message",
52651
+ "content",
52652
+ "stream",
52653
+ "chunk",
52654
+ "block",
52655
+ "data"
52656
+ ]) if (record[key] !== void 0) collectRecordCandidates(record[key], candidates, seen, depth + 1);
52657
+ }
52658
+ function readEntryEvent(value) {
52659
+ const record = asRecord(value);
52660
+ return asRecord(record?.event) ?? (typeof record?.type === "string" ? record : void 0);
52661
+ }
52662
+ function readRecord(value) {
52663
+ const record = asRecord(value);
52664
+ const candidate = asRecord(record?.codingnsTranslation) ?? record;
52665
+ if (candidate?.original === void 0 || candidate.translated === void 0) return void 0;
52666
+ if (typeof candidate.original !== "string" || typeof candidate.translated !== "string") return void 0;
52667
+ const targetLanguage = readLanguage(candidate.targetLanguage);
52668
+ if (targetLanguage === void 0) return void 0;
52669
+ const model = readModel(candidate.model);
52670
+ const outputKind = candidate.outputKind === "assistant" || candidate.outputKind === "reasoning" ? candidate.outputKind : void 0;
52671
+ return {
52672
+ original: candidate.original,
52673
+ translated: candidate.translated,
52674
+ ...candidate.sourceLanguage === "zh" || candidate.sourceLanguage === "en" || candidate.sourceLanguage === "fr" || candidate.sourceLanguage === "de" || candidate.sourceLanguage === "ja" ? { sourceLanguage: candidate.sourceLanguage } : {},
52675
+ targetLanguage,
52676
+ bilingual: candidate.bilingual === true,
52677
+ ...outputKind === void 0 ? {} : { outputKind },
52678
+ ...candidate.alreadyTargetLanguage === true ? { alreadyTargetLanguage: true } : {},
52679
+ ...model === void 0 ? {} : { model },
52680
+ ...candidate.failed === true ? { failed: true } : {}
52681
+ };
52682
+ }
52683
+ function readModel(value) {
52684
+ const record = asRecord(value);
52685
+ if (typeof record?.provider !== "string" || typeof record.model !== "string" || record.provider.trim() === "" || record.model.trim() === "") return void 0;
52686
+ return {
52687
+ provider: record.provider,
52688
+ model: record.model
52689
+ };
52690
+ }
52691
+ function readInputRecord(value) {
52692
+ const candidate = asRecord(asRecord(asRecord(value)?.source)?.codingnsTranslationInput);
52693
+ if (candidate?.original === void 0 || candidate.translated === void 0) return void 0;
52694
+ if (typeof candidate.original !== "string" || typeof candidate.translated !== "string") return void 0;
52695
+ const targetLanguage = candidate.targetLanguage;
52696
+ if (targetLanguage !== "en" && targetLanguage !== "fr" && targetLanguage !== "de" && targetLanguage !== "ja") return void 0;
52697
+ const model = readModel(candidate.model);
52698
+ return {
52699
+ original: candidate.original,
52700
+ translated: candidate.translated,
52701
+ ...candidate.sourceLanguage === "zh" || candidate.sourceLanguage === "en" || candidate.sourceLanguage === "fr" || candidate.sourceLanguage === "de" || candidate.sourceLanguage === "ja" ? { sourceLanguage: candidate.sourceLanguage } : {},
52702
+ targetLanguage,
52703
+ ...candidate.alreadyTargetLanguage === true ? { alreadyTargetLanguage: true } : {},
52704
+ ...model === void 0 ? {} : { model },
52705
+ ...candidate.failed === true ? { failed: true } : {}
52706
+ };
52707
+ }
52708
+ function inputRecordForAction(record) {
52709
+ return {
52710
+ original: record.original,
52711
+ translated: record.translated,
52712
+ ...record.sourceLanguage === void 0 ? {} : { sourceLanguage: record.sourceLanguage },
52713
+ targetLanguage: record.targetLanguage,
52714
+ bilingual: false,
52715
+ ...record.alreadyTargetLanguage === true ? { alreadyTargetLanguage: true } : {},
52716
+ ...record.model === void 0 ? {} : { model: record.model },
52717
+ ...record.failed === true ? { failed: true } : {}
52718
+ };
52719
+ }
52720
+ function readLanguage(value) {
52721
+ return value === "zh" || value === "en" || value === "fr" || value === "de" || value === "ja" ? value : void 0;
52722
+ }
52723
+ function formatModel(model, t) {
52724
+ return model === void 0 ? t("translation.modelUnknown") : `${model.provider} / ${model.model}`;
52725
+ }
52726
+ function safeDomId(value) {
52727
+ return encodeURIComponent(value).replace(/%/gu, "-");
52728
+ }
52729
+ function asRecord(value) {
52730
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
52731
+ }
52732
+ //#endregion
52733
+ //#region src/client/features/session-translation.ts
52734
+ const languageLabelKeys = {
52735
+ zh: "translation.language.zh",
52736
+ en: "translation.language.en",
52737
+ fr: "translation.language.fr",
52738
+ de: "translation.language.de",
52739
+ ja: "translation.language.ja"
52740
+ };
52741
+ const sessionTranslationFeature = {
52742
+ descriptor: {
52743
+ name: "sessionTranslation",
52744
+ version: "0.1.0",
52745
+ enabledByDefault: false,
52746
+ dependencies: ["cliAdapters"],
52747
+ runtime: "client",
52748
+ ui: {
52749
+ label: "Session translation",
52750
+ description: "Translate selected external Agent requests and replies without changing tools, commands, paths, or original history.",
52751
+ labelKey: "feature.sessionTranslation.label",
52752
+ descriptionKey: "feature.sessionTranslation.description",
52753
+ order: 32,
52754
+ defaultOpen: true
52755
+ }
52756
+ },
52757
+ start: (context) => {
52758
+ return registerSessionTranslationChatUi(context.services);
52759
+ },
52760
+ settingsPanel: SessionTranslationPanel
52761
+ };
52762
+ function SessionTranslationPanel({ services, enabled, snapshot, notify }) {
52763
+ const t = require_transport.useCodingNsTranslator(services.locale);
52764
+ const value = snapshot.value?.sessionTranslation ?? require_native_workspace_store.DEFAULT_CODINGNS_SETTINGS.sessionTranslation ?? require_session_translation.DEFAULT_SESSION_TRANSLATION_SETTINGS;
52765
+ const disabled = !enabled || snapshot.status === "loading" || !snapshot.writable;
52766
+ const [catalog, setCatalog] = (0, react.useState)(null);
52767
+ (0, react.useEffect)(() => {
52768
+ let active = true;
52769
+ require_model_catalog_cache.callCliRpc(services.rpc, "translation/models", {}, AbortSignal.timeout(1e4)).then((value) => {
52770
+ if (active) setCatalog(value);
52771
+ }).catch(() => void 0);
52772
+ return () => {
52773
+ active = false;
52774
+ };
52775
+ }, [services.rpc]);
52776
+ const save = (patch) => {
52777
+ const next = Object.fromEntries(Object.entries({
52778
+ ...value,
52779
+ ...patch
52780
+ }).filter(([, item]) => item !== void 0));
52781
+ services.settings.set(require_native_workspace_store.CODINGNS_SESSION_TRANSLATION_FIELD, next).then((written) => {
52782
+ if (!written) throw new Error(t("translation.saveRejected"));
52783
+ notify({
52784
+ kind: "success",
52785
+ message: t("translation.saved")
52786
+ });
52787
+ }).catch((error) => notify({
52788
+ kind: "error",
52789
+ message: error instanceof Error ? error.message : String(error)
52790
+ }));
52791
+ };
52792
+ return (0, react.createElement)("div", {
52793
+ "aria-disabled": disabled,
52794
+ style: {
52795
+ ...require_theme.dshFormRootStyle,
52796
+ display: "flex",
52797
+ flexDirection: "column",
52798
+ gap: 0,
52799
+ opacity: disabled ? .55 : 1,
52800
+ pointerEvents: disabled ? "none" : "auto"
52801
+ }
52802
+ }, (0, react.createElement)("div", { style: {
52803
+ ...require_theme.dshSettingsSectionHeaderStyle,
52804
+ marginTop: 0
52805
+ } }, (0, react.createElement)("span", { style: require_theme.dshSettingsFieldLabelStyle }, t("translation.inputTitle"))), (0, react.createElement)(Field, { label: t("translation.inputLanguage") }, (0, react.createElement)("select", {
52806
+ value: value.inputLanguage,
52807
+ disabled,
52808
+ style: require_theme.dshSettingsFieldStyle,
52809
+ onChange: (event) => save({ inputLanguage: readInputLanguage(event.currentTarget.value) })
52810
+ }, ...[
52811
+ "en",
52812
+ "fr",
52813
+ "de",
52814
+ "ja"
52815
+ ].map((language) => (0, react.createElement)("option", {
52816
+ key: language,
52817
+ value: language
52818
+ }, t(languageLabelKeys[language]))))), (0, react.createElement)("div", { style: {
52819
+ ...require_theme.dshSettingsSectionHeaderStyle,
52820
+ marginTop: 12
52821
+ } }, (0, react.createElement)("span", { style: require_theme.dshSettingsFieldLabelStyle }, t("translation.outputTitle"))), (0, react.createElement)(Field, { label: t("translation.outputLanguage") }, (0, react.createElement)("select", {
52822
+ value: value.outputLanguage,
52823
+ disabled,
52824
+ style: require_theme.dshSettingsFieldStyle,
52825
+ onChange: (event) => save({ outputLanguage: readOutputLanguage(event.currentTarget.value) })
52826
+ }, ...[
52827
+ "zh",
52828
+ "en",
52829
+ "fr",
52830
+ "de",
52831
+ "ja"
52832
+ ].map((language) => (0, react.createElement)("option", {
52833
+ key: language,
52834
+ value: language
52835
+ }, t(languageLabelKeys[language]))))), (0, react.createElement)("div", { style: {
52836
+ ...require_theme.dshSettingsSectionHeaderStyle,
52837
+ marginTop: 12
52838
+ } }, (0, react.createElement)("span", { style: require_theme.dshSettingsFieldLabelStyle }, t("translation.modelTitle"))), (0, react.createElement)(ModelField, {
52839
+ value,
52840
+ catalog,
52841
+ disabled,
52842
+ t,
52843
+ onChange: (model) => save({ translationModel: model })
52844
+ }));
52845
+ }
52846
+ function Field({ label, children }) {
52847
+ return (0, react.createElement)("label", { style: {
52848
+ ...require_theme.dshSettingsListRowStyle,
52849
+ alignItems: "flex-start",
52850
+ flexDirection: "column",
52851
+ gap: 6
52852
+ } }, (0, react.createElement)("span", { style: require_theme.dshSettingsFieldLabelStyle }, label), children ?? null);
52853
+ }
52854
+ function ModelField({ value, catalog, disabled, t, onChange }) {
52855
+ const selectedKey = value.translationModel === void 0 ? "" : JSON.stringify([value.translationModel.provider, value.translationModel.model]);
52856
+ const models = catalog?.models ?? [];
52857
+ const hasSelected = selectedKey !== "" && models.some((model) => JSON.stringify([model.provider, model.model]) === selectedKey);
52858
+ return (0, react.createElement)("div", { style: {
52859
+ width: "100%",
52860
+ display: "grid",
52861
+ gap: 6
52862
+ } }, (0, react.createElement)("select", {
52863
+ value: selectedKey,
52864
+ disabled,
52865
+ style: require_theme.dshSettingsFieldStyle,
52866
+ onChange: (event) => onChange(readTranslationModel(event.currentTarget.value))
52867
+ }, (0, react.createElement)("option", { value: "" }, t("translation.modelFollowDefault")), !hasSelected && selectedKey !== "" && (0, react.createElement)("option", { value: selectedKey }, t("translation.modelUnavailable")), ...models.map((model) => (0, react.createElement)("option", {
52868
+ key: JSON.stringify([model.provider, model.model]),
52869
+ value: JSON.stringify([model.provider, model.model])
52870
+ }, `${isOfficialProvider(model.provider) ? t("translation.modelOfficial") : t("translation.modelThirdParty")} · ${model.label}`))), catalog === null ? (0, react.createElement)("span", { style: require_theme.dshSettingsHelpStyle }, t("translation.modelLoading")) : models.length === 0 ? (0, react.createElement)("span", { style: require_theme.dshSettingsHelpStyle }, t("translation.modelEmpty")) : (0, react.createElement)("span", { style: require_theme.dshSettingsHelpStyle }, t("translation.modelHelp")));
52871
+ }
52872
+ function readTranslationModel(value) {
52873
+ if (value === "") return void 0;
52874
+ try {
52875
+ const parsed = JSON.parse(value);
52876
+ if (!Array.isArray(parsed) || typeof parsed[0] !== "string" || typeof parsed[1] !== "string" || parsed[0].trim() === "" || parsed[1].trim() === "") return void 0;
52877
+ return {
52878
+ provider: parsed[0],
52879
+ model: parsed[1]
52880
+ };
52881
+ } catch {
52882
+ return;
52883
+ }
52884
+ }
52885
+ function isOfficialProvider(provider) {
52886
+ return /^(?:deepseek(?:-official)?|official-deepseek)$/iu.test(provider.trim());
52887
+ }
52888
+ function readInputLanguage(value) {
52889
+ return value === "fr" || value === "de" || value === "ja" ? value : "en";
52890
+ }
52891
+ function readOutputLanguage(value) {
52892
+ return value === "en" || value === "fr" || value === "de" || value === "ja" ? value : "zh";
52893
+ }
52894
+ //#endregion
50953
52895
  //#region src/features/host-router.ts
50954
52896
  /** 统一管理当前 Host/PeerHost 的完整作用域和 generation。 */
50955
52897
  var HostRouter = class {
@@ -51179,7 +53121,8 @@ ${scope} [data-dockkit-strip-tabs] {
51179
53121
  fileManagementFeature,
51180
53122
  peerHostFeature,
51181
53123
  globalVoiceAssistantFeature,
51182
- subagentCollapsedLineageFeature
53124
+ subagentCollapsedLineageFeature,
53125
+ sessionTranslationFeature
51183
53126
  ];
51184
53127
  /**
51185
53128
  * 汇总需要在设置页显示的模块,按 ui.order 升序。
@@ -58317,6 +60260,118 @@ ${scope} [data-dockkit-strip-tabs] {
58317
60260
  };
58318
60261
  }
58319
60262
  //#endregion
60263
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-util-workspace-path@0.2.1-alpha.2_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
60264
+ /**
60265
+ * The `dsh-resource://file/…` address grammar: how a file is named across the
60266
+ * Sidebar and the resource model, built and parsed without touching a
60267
+ * filesystem.
60268
+ * @module
60269
+ */
60270
+ /** The scheme and type every file address opens with. */
60271
+ const FILE_ADDRESS_PREFIX = "dsh-resource://file/";
60272
+ /** Component-encode one id or path segment, keeping `:` literal for drive letters. */
60273
+ function encodeSegment(segment) {
60274
+ return encodeURIComponent(segment).replace(/%3A/gi, ":");
60275
+ }
60276
+ /** Encode a `/`-separated path segment by segment. */
60277
+ function encodePath(path) {
60278
+ return path.split("/").map(encodeSegment).join("/");
60279
+ }
60280
+ /** Whether a decoded first path segment is a Windows drive (`C:`). */
60281
+ function isDriveSegment(segment) {
60282
+ return segment !== void 0 && /^[A-Za-z]:$/.test(segment);
60283
+ }
60284
+ /**
60285
+ * Build the address of a file read through one Session.
60286
+ * @param sessionId - the Session whose Host workspace resolves the path.
60287
+ * @param path - absolute or workspace-relative path; backslashes are normalized to `/`, and leading `./` prefixes are dropped.
60288
+ * @returns the `dsh-resource://file/session/<sessionId>/<path>` address.
60289
+ */
60290
+ function sessionFileAddress(sessionId, path) {
60291
+ const normalized = path.replace(/\\/g, "/").replace(/^(?:\.\/)+/, "");
60292
+ return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encodePath(normalized)}`;
60293
+ }
60294
+ /**
60295
+ * Read a file address back into its parts without resolving `.` or `..`.
60296
+ * Query and fragment suffixes are ignored; encoded path segments are decoded.
60297
+ * @param address - a candidate address.
60298
+ * @returns the parts, or `undefined` when the string is not a `dsh-resource://file/` URI in a known scope with a path, or a segment is not validly encoded.
60299
+ */
60300
+ function parseFileAddress(address) {
60301
+ try {
60302
+ if (!address.startsWith(FILE_ADDRESS_PREFIX)) return void 0;
60303
+ const end = address.search(/[?#]/);
60304
+ const [scope, ...rest] = address.slice(20, end === -1 ? void 0 : end).split("/");
60305
+ if (scope === "session") {
60306
+ const [id, ...segments] = rest;
60307
+ if (id === void 0 || id === "" || segments.length === 0) return void 0;
60308
+ return {
60309
+ scope,
60310
+ sessionId: decodeURIComponent(id),
60311
+ path: segments.map(decodeURIComponent).join("/")
60312
+ };
60313
+ }
60314
+ if (scope === "absolute") {
60315
+ const unc = rest[0] === "" && rest.length > 1;
60316
+ const segments = (unc ? rest.slice(1) : rest).map(decodeURIComponent);
60317
+ if (segments.length === 0 || segments[0] === "") return void 0;
60318
+ if (unc) return {
60319
+ scope,
60320
+ path: `//${segments.join("/")}`
60321
+ };
60322
+ return {
60323
+ scope,
60324
+ path: isDriveSegment(segments[0]) ? segments.join("/") : `/${segments.join("/")}`
60325
+ };
60326
+ }
60327
+ return;
60328
+ } catch {
60329
+ return;
60330
+ }
60331
+ }
60332
+ //#endregion
60333
+ //#region src/client/session-file-links.ts
60334
+ /**
60335
+ * 在原生资源入口规范本地及远端链接,保留资源里的会话身份和导航参数。
60336
+ * 右栏文件树、消息与预览内链接共用这个入口;不扫描消息正文或当前选择。
60337
+ */
60338
+ function installSessionFileLinkRouting(uiContext, workspacePathForSession) {
60339
+ const sidebar = uiContext.get("sidebarRight");
60340
+ if (typeof sidebar?.openResource !== "function") return void 0;
60341
+ const originalOpen = sidebar.openResource;
60342
+ const originalOpenIn = sidebar.openResourceIn;
60343
+ const resolve = (address, options) => {
60344
+ const file = parseFileAddress(address);
60345
+ if (file?.scope !== "session") return [address, options];
60346
+ const location = normalizePeerHostFileLocation(file.path, workspacePathForSession(file.sessionId));
60347
+ if (location.path === file.path) return [address, options];
60348
+ const suffix = address.search(/[?#]/u);
60349
+ return [sessionFileAddress(file.sessionId, location.path) + (suffix < 0 ? "" : address.slice(suffix)), location.line === void 0 ? options : {
60350
+ ...options,
60351
+ params: {
60352
+ line: location.line,
60353
+ ...options?.params
60354
+ }
60355
+ }];
60356
+ };
60357
+ const open = (address, options) => originalOpen.call(sidebar, ...resolve(address, options));
60358
+ const openIn = originalOpenIn === void 0 ? void 0 : (sessionId, address, options) => originalOpenIn.call(sidebar, sessionId, ...resolve(address, options));
60359
+ sidebar.openResource = open;
60360
+ if (openIn !== void 0) sidebar.openResourceIn = openIn;
60361
+ return () => {
60362
+ if (sidebar.openResource === open) sidebar.openResource = originalOpen;
60363
+ if (openIn !== void 0 && sidebar.openResourceIn === openIn) sidebar.openResourceIn = originalOpenIn;
60364
+ };
60365
+ }
60366
+ /** 独立于 PeerHost 开关,按资源所属会话的最新目录识别路径平台。 */
60367
+ function installNativeSessionFileLinkRouting(uiContext) {
60368
+ const sessions = require_native_workspace_store.readNativeService(uiContext, "sessions", "list");
60369
+ return installSessionFileLinkRouting(uiContext, (sessionId) => {
60370
+ const workspace = require_native_workspace_store.readNativeWorkspaceSnapshot(uiContext)?.items.find((item) => item.sessionIds.includes(sessionId));
60371
+ return workspace?.workspacePath ?? sessions?.getSnapshot().byId?.[sessionId]?.cwd ?? workspace?.path;
60372
+ });
60373
+ }
60374
+ //#endregion
58320
60375
  //#region node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/util.js
58321
60376
  function getEnumValues(entries) {
58322
60377
  const numericValues = Object.values(entries).filter((v) => typeof v === "number");
@@ -63938,6 +65993,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
63938
65993
  require_model_catalog_cache.debugInfo("codingns4dsh: client apply entered", { dshVersion });
63939
65994
  ensureCryptoRandomUUID();
63940
65995
  ctx.effect(() => require_transport.registerCodingNsLocale(ctx), "codingns4dsh: client dictionaries");
65996
+ ctx.inject([
65997
+ "sidebarRight",
65998
+ "sessions",
65999
+ "workspaces"
66000
+ ], (scope) => {
66001
+ scope.effect(() => installNativeSessionFileLinkRouting(scope) ?? (() => void 0), "codingns4dsh: session file links");
66002
+ });
63941
66003
  ctx.inject([
63942
66004
  "slots",
63943
66005
  "connection",
@@ -64194,5 +66256,5 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
64194
66256
  });
64195
66257
 
64196
66258
  })();
64197
- // codingns-client-revision: 4d7925ece707008d1f018624dd2c7c8bef7c24acabe9f9379b923e4f10e1fd26
66259
+ // codingns-client-revision: 357fbcae0ed92ae51010b9952fb1be86dedea3232628e2c830758032a3e1c1ee
64198
66260
  //# sourceMappingURL=bundle.js.map