@jingyi0605/codingns4dsh 0.2.1-beta.5 → 0.2.1-beta.6

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 +112 -161
  2. package/README.md +106 -263
  3. package/assets/provider-icons/doubao.png +0 -0
  4. package/data/build/dist/client/avatar/catalog-panel.d.ts +1 -1
  5. package/data/build/dist/client/avatar/catalog-panel.d.ts.map +1 -1
  6. package/data/build/dist/client/avatar/catalog-panel.js +13 -11
  7. package/data/build/dist/client/avatar/catalog-panel.js.map +1 -1
  8. package/data/build/dist/client/avatar/engine.d.ts.map +1 -1
  9. package/data/build/dist/client/avatar/engine.js +26 -7
  10. package/data/build/dist/client/avatar/engine.js.map +1 -1
  11. package/data/build/dist/client/avatar/manager.d.ts.map +1 -1
  12. package/data/build/dist/client/avatar/manager.js +5 -1
  13. package/data/build/dist/client/avatar/manager.js.map +1 -1
  14. package/data/build/dist/client/avatar/slot.d.ts.map +1 -1
  15. package/data/build/dist/client/avatar/slot.js +4 -1
  16. package/data/build/dist/client/avatar/slot.js.map +1 -1
  17. package/data/build/dist/client/bundle.js +49155 -99509
  18. package/data/build/dist/client/bundle.js.map +1 -1
  19. package/data/build/dist/client/cli-catalog.d.ts +2 -0
  20. package/data/build/dist/client/cli-catalog.d.ts.map +1 -1
  21. package/data/build/dist/client/cli-catalog.js +8 -0
  22. package/data/build/dist/client/cli-catalog.js.map +1 -1
  23. package/data/build/dist/client/cli-slots.js +2 -2
  24. package/data/build/dist/client/cli-slots.js.map +1 -1
  25. package/data/build/dist/client/client.assistant-configuration-view.js +4526 -0
  26. package/data/build/dist/client/client.assistant-configuration-view.js.map +1 -0
  27. package/data/build/dist/client/client.assistant-debug-workbench.js +764 -0
  28. package/data/build/dist/client/client.assistant-debug-workbench.js.map +1 -0
  29. package/data/build/dist/client/client.assistant-prompt-editor.js +76 -0
  30. package/data/build/dist/client/client.assistant-prompt-editor.js.map +1 -0
  31. package/data/build/dist/client/client.assistant-prompts.js +44 -0
  32. package/data/build/dist/client/client.assistant-prompts.js.map +1 -0
  33. package/data/build/dist/client/client.assistant-voice-sessions.js +144 -0
  34. package/data/build/dist/client/client.assistant-voice-sessions.js.map +1 -0
  35. package/data/build/dist/client/client.assistant-workbench.js +2929 -0
  36. package/data/build/dist/client/client.assistant-workbench.js.map +1 -0
  37. package/data/build/dist/client/client.delegate-command.js +1315 -0
  38. package/data/build/dist/client/client.delegate-command.js.map +1 -0
  39. package/data/build/dist/client/client.editor-engine.js +32486 -0
  40. package/data/build/dist/client/client.editor-engine.js.map +1 -0
  41. package/data/build/dist/client/client.manager.js +3454 -0
  42. package/data/build/dist/client/client.manager.js.map +1 -0
  43. package/data/build/dist/client/client.model-catalog-cache.js +543 -0
  44. package/data/build/dist/client/client.model-catalog-cache.js.map +1 -0
  45. package/data/build/dist/client/client.native-workspace-store.js +689 -0
  46. package/data/build/dist/client/client.native-workspace-store.js.map +1 -0
  47. package/data/build/dist/client/client.primitives-adapter.js +333 -0
  48. package/data/build/dist/client/client.primitives-adapter.js.map +1 -0
  49. package/data/build/dist/client/client.react-dom.js +7286 -0
  50. package/data/build/dist/client/client.react-dom.js.map +1 -0
  51. package/data/build/dist/client/client.rolldown-runtime.js +59 -0
  52. package/data/build/dist/client/client.theme.js +386 -0
  53. package/data/build/dist/client/client.theme.js.map +1 -0
  54. package/data/build/dist/client/client.transport.js +3323 -0
  55. package/data/build/dist/client/client.transport.js.map +1 -0
  56. package/data/build/dist/client/client.xterm-engine.js +13064 -0
  57. package/data/build/dist/client/client.xterm-engine.js.map +1 -0
  58. package/data/build/dist/client/editor-engine.d.ts +4 -0
  59. package/data/build/dist/client/editor-engine.d.ts.map +1 -0
  60. package/data/build/dist/client/editor-engine.js +50 -0
  61. package/data/build/dist/client/editor-engine.js.map +1 -0
  62. package/data/build/dist/client/engine-loader.d.ts +5 -0
  63. package/data/build/dist/client/engine-loader.d.ts.map +1 -0
  64. package/data/build/dist/client/engine-loader.js +35 -0
  65. package/data/build/dist/client/engine-loader.js.map +1 -0
  66. package/data/build/dist/client/features/assistant-configuration-loader.d.ts +14 -0
  67. package/data/build/dist/client/features/assistant-configuration-loader.d.ts.map +1 -0
  68. package/data/build/dist/client/features/assistant-configuration-loader.js +13 -0
  69. package/data/build/dist/client/features/assistant-configuration-loader.js.map +1 -0
  70. package/data/build/dist/client/features/assistant-configuration-session.d.ts.map +1 -1
  71. package/data/build/dist/client/features/assistant-configuration-session.js +13 -4
  72. package/data/build/dist/client/features/assistant-configuration-session.js.map +1 -1
  73. package/data/build/dist/client/features/assistant-configuration-view.d.ts +66 -0
  74. package/data/build/dist/client/features/assistant-configuration-view.d.ts.map +1 -0
  75. package/data/build/dist/client/features/assistant-configuration-view.js +133 -0
  76. package/data/build/dist/client/features/assistant-configuration-view.js.map +1 -0
  77. package/data/build/dist/client/features/assistant-debug-workbench.d.ts +4 -11
  78. package/data/build/dist/client/features/assistant-debug-workbench.d.ts.map +1 -1
  79. package/data/build/dist/client/features/assistant-debug-workbench.js +41 -68
  80. package/data/build/dist/client/features/assistant-debug-workbench.js.map +1 -1
  81. package/data/build/dist/client/features/assistant-display-store.d.ts +57 -0
  82. package/data/build/dist/client/features/assistant-display-store.d.ts.map +1 -0
  83. package/data/build/dist/client/features/assistant-display-store.js +189 -0
  84. package/data/build/dist/client/features/assistant-display-store.js.map +1 -0
  85. package/data/build/dist/client/features/assistant-prompt-editor.d.ts +12 -0
  86. package/data/build/dist/client/features/assistant-prompt-editor.d.ts.map +1 -0
  87. package/data/build/dist/client/features/assistant-prompt-editor.js +14 -0
  88. package/data/build/dist/client/features/assistant-prompt-editor.js.map +1 -0
  89. package/data/build/dist/client/features/assistant-view-loader.d.ts +17 -0
  90. package/data/build/dist/client/features/assistant-view-loader.d.ts.map +1 -0
  91. package/data/build/dist/client/features/assistant-view-loader.js +49 -0
  92. package/data/build/dist/client/features/assistant-view-loader.js.map +1 -0
  93. package/data/build/dist/client/features/assistant-workbench.d.ts +1 -60
  94. package/data/build/dist/client/features/assistant-workbench.d.ts.map +1 -1
  95. package/data/build/dist/client/features/assistant-workbench.js +41 -169
  96. package/data/build/dist/client/features/assistant-workbench.js.map +1 -1
  97. package/data/build/dist/client/features/cli-adapters.js +3 -7
  98. package/data/build/dist/client/features/cli-adapters.js.map +1 -1
  99. package/data/build/dist/client/features/global-voice-assistant.d.ts.map +1 -1
  100. package/data/build/dist/client/features/global-voice-assistant.js +21 -20
  101. package/data/build/dist/client/features/global-voice-assistant.js.map +1 -1
  102. package/data/build/dist/client/features/peer-host.d.ts.map +1 -1
  103. package/data/build/dist/client/features/peer-host.js +14 -25
  104. package/data/build/dist/client/features/peer-host.js.map +1 -1
  105. package/data/build/dist/client/file-management-dom.d.ts +3 -1
  106. package/data/build/dist/client/file-management-dom.d.ts.map +1 -1
  107. package/data/build/dist/client/file-management-dom.js +85 -79
  108. package/data/build/dist/client/file-management-dom.js.map +1 -1
  109. package/data/build/dist/client/locales/assistantAvatar.d.ts +1 -1
  110. package/data/build/dist/client/locales/assistantAvatar.js +2 -2
  111. package/data/build/dist/client/locales/assistantAvatar.js.map +1 -1
  112. package/data/build/dist/client/locales/fileWorkspace.d.ts.map +1 -1
  113. package/data/build/dist/client/locales/fileWorkspace.js +2 -0
  114. package/data/build/dist/client/locales/fileWorkspace.js.map +1 -1
  115. package/data/build/dist/client/peer-host-management-api.d.ts +2 -2
  116. package/data/build/dist/client/peer-host-management-api.d.ts.map +1 -1
  117. package/data/build/dist/client/peer-host-management-api.js +8 -5
  118. package/data/build/dist/client/peer-host-management-api.js.map +1 -1
  119. package/data/build/dist/client/provider-icon-assets.js +5 -39
  120. package/data/build/dist/client/provider-icon-assets.js.map +1 -1
  121. package/data/build/dist/client/provider-icon-bridge.d.ts +6 -0
  122. package/data/build/dist/client/provider-icon-bridge.d.ts.map +1 -0
  123. package/data/build/dist/client/provider-icon-bridge.js +65 -0
  124. package/data/build/dist/client/provider-icon-bridge.js.map +1 -0
  125. package/data/build/dist/client/provider-icons.d.ts +1 -0
  126. package/data/build/dist/client/provider-icons.d.ts.map +1 -1
  127. package/data/build/dist/client/provider-icons.js +2 -1
  128. package/data/build/dist/client/provider-icons.js.map +1 -1
  129. package/data/build/dist/client/remote-web-context.d.ts +2 -0
  130. package/data/build/dist/client/remote-web-context.d.ts.map +1 -1
  131. package/data/build/dist/client/remote-web-context.js +50 -2
  132. package/data/build/dist/client/remote-web-context.js.map +1 -1
  133. package/data/build/dist/client/serial-polling.d.ts +3 -1
  134. package/data/build/dist/client/serial-polling.d.ts.map +1 -1
  135. package/data/build/dist/client/serial-polling.js +10 -3
  136. package/data/build/dist/client/serial-polling.js.map +1 -1
  137. package/data/build/dist/client/settings-section.js +8 -2
  138. package/data/build/dist/client/settings-section.js.map +1 -1
  139. package/data/build/dist/client/terminal/xterm-engine.d.ts +4 -0
  140. package/data/build/dist/client/terminal/xterm-engine.d.ts.map +1 -0
  141. package/data/build/dist/client/terminal/xterm-engine.js +5 -0
  142. package/data/build/dist/client/terminal/xterm-engine.js.map +1 -0
  143. package/data/build/dist/client/terminal/xterm-view.d.ts.map +1 -1
  144. package/data/build/dist/client/terminal/xterm-view.js +33 -25
  145. package/data/build/dist/client/terminal/xterm-view.js.map +1 -1
  146. package/data/build/dist/host/avatar/temporary-previews.d.ts +3 -1
  147. package/data/build/dist/host/avatar/temporary-previews.d.ts.map +1 -1
  148. package/data/build/dist/host/avatar/temporary-previews.js +7 -2
  149. package/data/build/dist/host/avatar/temporary-previews.js.map +1 -1
  150. package/data/build/dist/host/cli-adapters/antigravity-driver.d.ts.map +1 -1
  151. package/data/build/dist/host/cli-adapters/antigravity-driver.js +2 -2
  152. package/data/build/dist/host/cli-adapters/antigravity-driver.js.map +1 -1
  153. package/data/build/dist/host/cli-adapters/binary-detection.d.ts +19 -0
  154. package/data/build/dist/host/cli-adapters/binary-detection.d.ts.map +1 -0
  155. package/data/build/dist/host/cli-adapters/binary-detection.js +71 -0
  156. package/data/build/dist/host/cli-adapters/binary-detection.js.map +1 -0
  157. package/data/build/dist/host/cli-adapters/claude-driver.d.ts +2 -0
  158. package/data/build/dist/host/cli-adapters/claude-driver.d.ts.map +1 -1
  159. package/data/build/dist/host/cli-adapters/claude-driver.js +4 -0
  160. package/data/build/dist/host/cli-adapters/claude-driver.js.map +1 -1
  161. package/data/build/dist/host/cli-adapters/claude-model-options.d.ts.map +1 -1
  162. package/data/build/dist/host/cli-adapters/claude-model-options.js +2 -1
  163. package/data/build/dist/host/cli-adapters/claude-model-options.js.map +1 -1
  164. package/data/build/dist/host/cli-adapters/claude-process.d.ts +7 -0
  165. package/data/build/dist/host/cli-adapters/claude-process.d.ts.map +1 -0
  166. package/data/build/dist/host/cli-adapters/claude-process.js +59 -0
  167. package/data/build/dist/host/cli-adapters/claude-process.js.map +1 -0
  168. package/data/build/dist/host/cli-adapters/codebuddy-driver.d.ts +3 -0
  169. package/data/build/dist/host/cli-adapters/codebuddy-driver.d.ts.map +1 -1
  170. package/data/build/dist/host/cli-adapters/codebuddy-driver.js +18 -5
  171. package/data/build/dist/host/cli-adapters/codebuddy-driver.js.map +1 -1
  172. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +0 -1
  173. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts.map +1 -1
  174. package/data/build/dist/host/cli-adapters/command-code-driver.js +6 -29
  175. package/data/build/dist/host/cli-adapters/command-code-driver.js.map +1 -1
  176. package/data/build/dist/host/cli-adapters/doubao-app.d.ts +27 -0
  177. package/data/build/dist/host/cli-adapters/doubao-app.d.ts.map +1 -0
  178. package/data/build/dist/host/cli-adapters/doubao-app.js +110 -0
  179. package/data/build/dist/host/cli-adapters/doubao-app.js.map +1 -0
  180. package/data/build/dist/host/cli-adapters/doubao-artifact-store.d.ts +16 -0
  181. package/data/build/dist/host/cli-adapters/doubao-artifact-store.d.ts.map +1 -0
  182. package/data/build/dist/host/cli-adapters/doubao-artifact-store.js +142 -0
  183. package/data/build/dist/host/cli-adapters/doubao-artifact-store.js.map +1 -0
  184. package/data/build/dist/host/cli-adapters/doubao-artifacts.d.ts +16 -0
  185. package/data/build/dist/host/cli-adapters/doubao-artifacts.d.ts.map +1 -0
  186. package/data/build/dist/host/cli-adapters/doubao-artifacts.js +34 -0
  187. package/data/build/dist/host/cli-adapters/doubao-artifacts.js.map +1 -0
  188. package/data/build/dist/host/cli-adapters/doubao-cdp.d.ts +49 -0
  189. package/data/build/dist/host/cli-adapters/doubao-cdp.d.ts.map +1 -0
  190. package/data/build/dist/host/cli-adapters/doubao-cdp.js +177 -0
  191. package/data/build/dist/host/cli-adapters/doubao-cdp.js.map +1 -0
  192. package/data/build/dist/host/cli-adapters/doubao-driver.d.ts +33 -0
  193. package/data/build/dist/host/cli-adapters/doubao-driver.d.ts.map +1 -0
  194. package/data/build/dist/host/cli-adapters/doubao-driver.js +291 -0
  195. package/data/build/dist/host/cli-adapters/doubao-driver.js.map +1 -0
  196. package/data/build/dist/host/cli-adapters/doubao-events.d.ts +21 -0
  197. package/data/build/dist/host/cli-adapters/doubao-events.d.ts.map +1 -0
  198. package/data/build/dist/host/cli-adapters/doubao-events.js +126 -0
  199. package/data/build/dist/host/cli-adapters/doubao-events.js.map +1 -0
  200. package/data/build/dist/host/cli-adapters/doubao-runtime.d.ts +6 -0
  201. package/data/build/dist/host/cli-adapters/doubao-runtime.d.ts.map +1 -0
  202. package/data/build/dist/host/cli-adapters/doubao-runtime.js +176 -0
  203. package/data/build/dist/host/cli-adapters/doubao-runtime.js.map +1 -0
  204. package/data/build/dist/host/cli-adapters/doubao-tools.d.ts +8 -0
  205. package/data/build/dist/host/cli-adapters/doubao-tools.d.ts.map +1 -0
  206. package/data/build/dist/host/cli-adapters/doubao-tools.js +139 -0
  207. package/data/build/dist/host/cli-adapters/doubao-tools.js.map +1 -0
  208. package/data/build/dist/host/cli-adapters/driver.d.ts +3 -2
  209. package/data/build/dist/host/cli-adapters/driver.d.ts.map +1 -1
  210. package/data/build/dist/host/cli-adapters/feature.d.ts.map +1 -1
  211. package/data/build/dist/host/cli-adapters/feature.js +2 -0
  212. package/data/build/dist/host/cli-adapters/feature.js.map +1 -1
  213. package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts.map +1 -1
  214. package/data/build/dist/host/cli-adapters/json-rpc-process.js +3 -1
  215. package/data/build/dist/host/cli-adapters/json-rpc-process.js.map +1 -1
  216. package/data/build/dist/host/cli-adapters/legacy-session-adapter.d.ts.map +1 -1
  217. package/data/build/dist/host/cli-adapters/legacy-session-adapter.js +1 -0
  218. package/data/build/dist/host/cli-adapters/legacy-session-adapter.js.map +1 -1
  219. package/data/build/dist/host/cli-adapters/model-catalog.d.ts +2 -0
  220. package/data/build/dist/host/cli-adapters/model-catalog.d.ts.map +1 -1
  221. package/data/build/dist/host/cli-adapters/model-catalog.js +10 -0
  222. package/data/build/dist/host/cli-adapters/model-catalog.js.map +1 -1
  223. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +1 -0
  224. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts.map +1 -1
  225. package/data/build/dist/host/cli-adapters/opencode-driver.js +9 -28
  226. package/data/build/dist/host/cli-adapters/opencode-driver.js.map +1 -1
  227. package/data/build/dist/host/cli-adapters/process-utils.d.ts +4 -2
  228. package/data/build/dist/host/cli-adapters/process-utils.d.ts.map +1 -1
  229. package/data/build/dist/host/cli-adapters/process-utils.js +64 -11
  230. package/data/build/dist/host/cli-adapters/process-utils.js.map +1 -1
  231. package/data/build/dist/host/cli-adapters/qoder-driver.d.ts.map +1 -1
  232. package/data/build/dist/host/cli-adapters/qoder-driver.js +13 -33
  233. package/data/build/dist/host/cli-adapters/qoder-driver.js.map +1 -1
  234. package/data/build/dist/host/cli-adapters/registry.d.ts.map +1 -1
  235. package/data/build/dist/host/cli-adapters/registry.js +31 -4
  236. package/data/build/dist/host/cli-adapters/registry.js.map +1 -1
  237. package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts +2 -11
  238. package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts.map +1 -1
  239. package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +1 -28
  240. package/data/build/dist/host/cli-adapters/rpc-driver-utils.js.map +1 -1
  241. package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +3 -3
  242. package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts.map +1 -1
  243. package/data/build/dist/host/cli-adapters/standard-stream-driver.js +12 -37
  244. package/data/build/dist/host/cli-adapters/standard-stream-driver.js.map +1 -1
  245. package/data/build/dist/host/cli-adapters/windows-command-environment.d.ts +12 -0
  246. package/data/build/dist/host/cli-adapters/windows-command-environment.d.ts.map +1 -0
  247. package/data/build/dist/host/cli-adapters/windows-command-environment.js +53 -0
  248. package/data/build/dist/host/cli-adapters/windows-command-environment.js.map +1 -0
  249. package/data/build/dist/host/features/assistant-avatar-runtime.d.ts.map +1 -1
  250. package/data/build/dist/host/features/assistant-avatar-runtime.js +23 -14
  251. package/data/build/dist/host/features/assistant-avatar-runtime.js.map +1 -1
  252. package/data/build/dist/host/features/assistant-background.d.ts +44 -0
  253. package/data/build/dist/host/features/assistant-background.d.ts.map +1 -0
  254. package/data/build/dist/host/features/assistant-background.js +116 -0
  255. package/data/build/dist/host/features/assistant-background.js.map +1 -0
  256. package/data/build/dist/host/features/assistant-index-updates.d.ts +5 -0
  257. package/data/build/dist/host/features/assistant-index-updates.d.ts.map +1 -1
  258. package/data/build/dist/host/features/assistant-index-updates.js +29 -6
  259. package/data/build/dist/host/features/assistant-index-updates.js.map +1 -1
  260. package/data/build/dist/host/features/assistant-source-cache.d.ts +2 -2
  261. package/data/build/dist/host/features/assistant-source-cache.d.ts.map +1 -1
  262. package/data/build/dist/host/features/assistant-source-cache.js +4 -2
  263. package/data/build/dist/host/features/assistant-source-cache.js.map +1 -1
  264. package/data/build/dist/host/features/global-voice-rpc.d.ts.map +1 -1
  265. package/data/build/dist/host/features/global-voice-rpc.js +269 -110
  266. package/data/build/dist/host/features/global-voice-rpc.js.map +1 -1
  267. package/data/build/dist/host/features/peer-host.d.ts.map +1 -1
  268. package/data/build/dist/host/features/peer-host.js +61 -21
  269. package/data/build/dist/host/features/peer-host.js.map +1 -1
  270. package/data/build/dist/host/features/types.d.ts +6 -4
  271. package/data/build/dist/host/features/types.d.ts.map +1 -1
  272. package/data/build/dist/host/index.d.ts.map +1 -1
  273. package/data/build/dist/host/index.js +29 -19
  274. package/data/build/dist/host/index.js.map +1 -1
  275. package/data/build/dist/host/provider-icon-assets.d.ts +5 -0
  276. package/data/build/dist/host/provider-icon-assets.d.ts.map +1 -0
  277. package/data/build/dist/host/provider-icon-assets.js +61 -0
  278. package/data/build/dist/host/provider-icon-assets.js.map +1 -0
  279. package/data/build/dist/host/rpc.js +1 -1
  280. package/data/build/dist/host/rpc.js.map +1 -1
  281. package/data/build/dist/host/session-migration-repair.d.ts +4 -0
  282. package/data/build/dist/host/session-migration-repair.d.ts.map +1 -1
  283. package/data/build/dist/host/session-migration-repair.js +75 -2
  284. package/data/build/dist/host/session-migration-repair.js.map +1 -1
  285. package/data/build/dist/shared/assistant-avatar-engine.d.ts +2 -0
  286. package/data/build/dist/shared/assistant-avatar-engine.d.ts.map +1 -1
  287. package/data/build/dist/shared/assistant-avatar-engine.js +11 -1
  288. package/data/build/dist/shared/assistant-avatar-engine.js.map +1 -1
  289. package/data/build/dist/shared/contracts/cli-adapter.d.ts +3 -0
  290. package/data/build/dist/shared/contracts/cli-adapter.d.ts.map +1 -1
  291. package/data/build/dist/shared/contracts/version.d.ts +1 -1
  292. package/data/build/dist/shared/contracts/version.js +1 -1
  293. package/data/build/dist/shared/provider-icon-resources.d.ts +4 -0
  294. package/data/build/dist/shared/provider-icon-resources.d.ts.map +1 -0
  295. package/data/build/dist/shared/provider-icon-resources.js +24 -0
  296. package/data/build/dist/shared/provider-icon-resources.js.map +1 -0
  297. package/package.json +2 -2
  298. package/profile/README.md +2 -2
  299. package/profile/package.json +2 -2
  300. package/profile/version.json +1 -1
  301. package/version.json +1 -1
@@ -0,0 +1,2929 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@jingyi0605/codingns4dsh",
3
+ chunk: "client.assistant-workbench.js",
4
+ factory: (require) => {
5
+ var module = { exports: {} };
6
+ var exports = module.exports;
7
+ const require_native_workspace_store = require("@jingyi0605/codingns4dsh/client.native-workspace-store.js");
8
+ const require_transport = require("@jingyi0605/codingns4dsh/client.transport.js");
9
+ const require_theme = require("@jingyi0605/codingns4dsh/client.theme.js");
10
+ const require_manager = require("@jingyi0605/codingns4dsh/client.manager.js");
11
+ const require_primitives_adapter = require("@jingyi0605/codingns4dsh/client.primitives-adapter.js");
12
+ const require_assistant_voice_sessions = require("@jingyi0605/codingns4dsh/client.assistant-voice-sessions.js");
13
+ let react = require("react");
14
+ //#region src/shared/voice-models.ts
15
+ const STREAMING_FILES = [
16
+ {
17
+ setting: "asrEncoder",
18
+ name: "encoder-epoch-99-avg-1.int8.onnx"
19
+ },
20
+ {
21
+ setting: "asrDecoder",
22
+ name: "decoder-epoch-99-avg-1.int8.onnx"
23
+ },
24
+ {
25
+ setting: "asrJoiner",
26
+ name: "joiner-epoch-99-avg-1.int8.onnx"
27
+ },
28
+ {
29
+ setting: "asrTokens",
30
+ name: "tokens.txt"
31
+ }
32
+ ];
33
+ /** 编程交流默认识别中英混合语句;显式保存的模型选择继续保留。 */
34
+ const DEFAULT_VOICE_MODEL_ID = "sherpa-onnx-streaming-zh-en";
35
+ /** 只暴露支持实时输入的推荐模型;模型地址由 Host 根据此白名单解析。 */
36
+ const ASSISTANT_VOICE_MODEL_CATALOG = [
37
+ {
38
+ id: DEFAULT_VOICE_MODEL_ID,
39
+ label: "中英双语实时模型(默认)",
40
+ description: "本地运行,约 200 MB,同时识别中文和英文,适合编程交流。",
41
+ repository: "csukuangfj/sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20",
42
+ files: STREAMING_FILES
43
+ },
44
+ {
45
+ id: "sherpa-onnx-streaming-zh-large-2025-06-30",
46
+ label: "中文 Zipformer Large(2025-06-30)",
47
+ description: "本地中文实时识别,模型文件约 168 MB。",
48
+ repository: "csukuangfj/sherpa-onnx-streaming-zipformer-zh-int8-2025-06-30",
49
+ files: [
50
+ {
51
+ setting: "asrEncoder",
52
+ name: "encoder.int8.onnx"
53
+ },
54
+ {
55
+ setting: "asrDecoder",
56
+ name: "decoder.onnx"
57
+ },
58
+ {
59
+ setting: "asrJoiner",
60
+ name: "joiner.int8.onnx"
61
+ },
62
+ {
63
+ setting: "asrTokens",
64
+ name: "tokens.txt"
65
+ }
66
+ ]
67
+ },
68
+ {
69
+ id: "sherpa-onnx-streaming-zh-14m",
70
+ label: "中文轻量实时模型(14M)",
71
+ description: "本地运行,约 27 MB,适合中文语音对话。",
72
+ repository: "csukuangfj/sherpa-onnx-streaming-zipformer-zh-14M-2023-02-23",
73
+ files: STREAMING_FILES
74
+ }
75
+ ];
76
+ function findAssistantVoiceModel(modelId) {
77
+ return ASSISTANT_VOICE_MODEL_CATALOG.find((model) => model.id === modelId);
78
+ }
79
+ //#endregion
80
+ //#region src/client/assistant-settings-styles.ts
81
+ /** 助理各页共用选项排版,避免开关、许可复选框和字段标签分别继承宿主字号。 */
82
+ const assistantSettingTextStyle = {
83
+ fontSize: 13,
84
+ fontWeight: 400,
85
+ lineHeight: 1.5
86
+ };
87
+ const assistantSettingFieldStyle = {
88
+ ...assistantSettingTextStyle,
89
+ display: "grid",
90
+ gap: 6,
91
+ minWidth: 0
92
+ };
93
+ const assistantSettingCheckboxStyle = {
94
+ ...assistantSettingTextStyle,
95
+ display: "flex",
96
+ alignItems: "start",
97
+ gap: 8
98
+ };
99
+ const assistantSettingSwitchStyle = {
100
+ ...assistantSettingTextStyle,
101
+ display: "flex",
102
+ alignItems: "center",
103
+ justifyContent: "space-between",
104
+ gap: 12
105
+ };
106
+ //#endregion
107
+ //#region src/client/features/assistant-display-store.ts
108
+ /** 展示专用快照:按完成时间串行调度,隐藏暂停;绝不承载通话心跳或后台租约。 */
109
+ var AssistantDisplayResource = class {
110
+ read;
111
+ interval;
112
+ snapshot = {};
113
+ listeners = /* @__PURE__ */ new Set();
114
+ timer;
115
+ pending;
116
+ queued;
117
+ controller;
118
+ failures = 0;
119
+ updatedAt = 0;
120
+ forceNext = false;
121
+ generation = 0;
122
+ dom;
123
+ constructor(read, interval, dom) {
124
+ this.read = read;
125
+ this.interval = interval;
126
+ this.dom = dom ?? (typeof document === "undefined" ? void 0 : document);
127
+ }
128
+ getSnapshot = () => this.snapshot;
129
+ get observed() {
130
+ return this.listeners.size > 0;
131
+ }
132
+ get active() {
133
+ return this.observed || this.pending !== void 0;
134
+ }
135
+ subscribe = (listener) => {
136
+ this.listeners.add(listener);
137
+ if (this.listeners.size === 1) {
138
+ this.dom?.addEventListener("visibilitychange", this.visible);
139
+ this.visible();
140
+ }
141
+ return () => {
142
+ this.listeners.delete(listener);
143
+ if (this.listeners.size !== 0) return;
144
+ this.generation++;
145
+ this.forceNext = false;
146
+ clearTimeout(this.timer);
147
+ this.controller?.abort();
148
+ this.dom?.removeEventListener("visibilitychange", this.visible);
149
+ };
150
+ };
151
+ visible = () => {
152
+ clearTimeout(this.timer);
153
+ if (this.dom?.visibilityState === "hidden") {
154
+ this.controller?.abort();
155
+ return;
156
+ }
157
+ this.refresh({ afterPending: this.controller?.signal.aborted === true });
158
+ };
159
+ refresh(options = {}) {
160
+ this.forceNext ||= options.force === true;
161
+ if (this.dom?.visibilityState === "hidden") return Promise.resolve(this.snapshot.value);
162
+ if (this.pending) {
163
+ if (options.afterPending || options.force) {
164
+ const generation = this.generation;
165
+ this.queued ??= this.pending.then(() => {
166
+ this.queued = void 0;
167
+ return generation === this.generation ? this.refresh() : this.snapshot.value;
168
+ });
169
+ return this.queued;
170
+ }
171
+ return this.pending;
172
+ }
173
+ if (!this.forceNext && options.maxAgeMs && this.snapshot.error === void 0 && this.snapshot.value !== void 0 && Date.now() - this.updatedAt < options.maxAgeMs) return Promise.resolve(this.snapshot.value);
174
+ clearTimeout(this.timer);
175
+ const controller = new AbortController();
176
+ this.controller = controller;
177
+ const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(1e4)]);
178
+ const force = this.forceNext;
179
+ this.forceNext = false;
180
+ this.pending = abortable(() => this.read(signal, force), signal).then((value) => {
181
+ signal.throwIfAborted();
182
+ this.failures = 0;
183
+ this.updatedAt = Date.now();
184
+ this.snapshot = { value };
185
+ this.emit();
186
+ return value;
187
+ }).catch((error) => {
188
+ if (!controller.signal.aborted) {
189
+ this.failures++;
190
+ this.snapshot = {
191
+ ...this.snapshot,
192
+ error
193
+ };
194
+ this.emit();
195
+ }
196
+ }).finally(() => {
197
+ this.pending = void 0;
198
+ if (!this.listeners.size || this.dom?.visibilityState === "hidden") return;
199
+ const delay = Math.min(this.interval(this.snapshot.value) * 2 ** Math.min(this.failures, 4), 6e4);
200
+ this.timer = setTimeout(() => {
201
+ this.refresh();
202
+ }, delay);
203
+ });
204
+ return this.pending;
205
+ }
206
+ emit() {
207
+ for (const listener of this.listeners) listener();
208
+ }
209
+ };
210
+ /** 取消时结束等待;底层 Promise 仍由处理器接住,避免迟到拒绝成为未处理异常。 */
211
+ function abortable(read, signal) {
212
+ return new Promise((resolve, reject) => {
213
+ const aborted = () => reject(signal.reason);
214
+ if (signal.aborted) {
215
+ aborted();
216
+ return;
217
+ }
218
+ signal.addEventListener("abort", aborted, { once: true });
219
+ Promise.resolve().then(() => {
220
+ signal.throwIfAborted();
221
+ return read();
222
+ }).then(resolve, reject).finally(() => signal.removeEventListener("abort", aborted));
223
+ });
224
+ }
225
+ const stores = /* @__PURE__ */ new WeakMap();
226
+ const legacyStatus = /* @__PURE__ */ new WeakSet();
227
+ function assistantDisplayScope(ids) {
228
+ return JSON.stringify([...new Set(ids)].sort());
229
+ }
230
+ function getAssistantDisplayStore(rpc, scope) {
231
+ let scopes = stores.get(rpc);
232
+ if (!scopes) {
233
+ scopes = /* @__PURE__ */ new Map();
234
+ stores.set(rpc, scopes);
235
+ }
236
+ let store = scopes.get(scope);
237
+ if (!store) {
238
+ store = new AssistantDisplayStore(rpc);
239
+ scopes.set(scope, store);
240
+ }
241
+ scopes.delete(scope);
242
+ scopes.set(scope, store);
243
+ for (const [key, cached] of scopes) {
244
+ if (scopes.size <= 8) break;
245
+ if (key !== scope && !cached.active) scopes.delete(key);
246
+ }
247
+ return store;
248
+ }
249
+ var AssistantDisplayStore = class {
250
+ rpc;
251
+ chats = /* @__PURE__ */ new Map();
252
+ status;
253
+ debug;
254
+ lifecycle;
255
+ get active() {
256
+ return this.status.active || this.debug.active || this.lifecycle.active || [...this.chats.values()].some((resource) => resource.active);
257
+ }
258
+ constructor(rpc, dom) {
259
+ this.rpc = rpc;
260
+ this.debug = new AssistantDisplayResource((signal, force) => this.call("assistant/debug", force ? { refresh: true } : {}, signal), () => legacyStatus.has(rpc) ? 3e4 : 3e3, dom);
261
+ this.status = new AssistantDisplayResource(async (signal, force) => {
262
+ if (!legacyStatus.has(rpc)) try {
263
+ return await this.call("assistant/status", {}, signal);
264
+ } catch (error) {
265
+ if (!unknownStatus(error)) throw error;
266
+ legacyStatus.add(rpc);
267
+ }
268
+ const value = await this.debug.refresh({
269
+ maxAgeMs: 3e4,
270
+ ...force ? { force: true } : {}
271
+ });
272
+ if (!value) throw this.debug.getSnapshot().error ?? /* @__PURE__ */ new Error("Assistant debug snapshot unavailable");
273
+ return {
274
+ revision: value.index?.generation ?? 0,
275
+ capturedAt: value.capturedAt,
276
+ indexState: value.indexState,
277
+ indexedAt: value.indexedAt,
278
+ workspaces: value.workspaces
279
+ };
280
+ }, () => legacyStatus.has(rpc) ? 3e4 : 3e3, dom);
281
+ this.lifecycle = new AssistantDisplayResource((signal) => this.call("assistant/lifecycle/read", {}, signal), (value) => value?.conversation.active?.state === "running" || value?.conversation.voiceSessions?.some((session) => session.endedAt === null) ? 600 : 1500, dom);
282
+ }
283
+ chat(requestId) {
284
+ for (const [id, cached] of this.chats) if (id !== requestId && !cached.observed) this.chats.delete(id);
285
+ let resource = this.chats.get(requestId);
286
+ if (!resource) {
287
+ resource = new AssistantDisplayResource((signal) => this.call("assistant/chat/read", { requestId }, signal), () => 600);
288
+ this.chats.set(requestId, resource);
289
+ }
290
+ return resource;
291
+ }
292
+ async call(endpoint, payload, signal) {
293
+ const result = await this.rpc.call(require_transport.CODINGNS_RPC_CHANNEL, endpoint, payload, signal);
294
+ if (!result.ok) throw Object.assign(new Error(result.error.message), { code: result.error.code });
295
+ return result.value;
296
+ }
297
+ };
298
+ function unknownStatus(error) {
299
+ const code = error?.code ?? "";
300
+ const message = error instanceof Error ? error.message : String(error);
301
+ return /UNKNOWN_(?:ACTION|ENDPOINT|METHOD)|METHOD_NOT_FOUND|RPC_NOT_FOUND|UNSUPPORTED_ACTION/iu.test(code) || /(?:unknown|unsupported|not found).*(?:status|action|endpoint)|(?:未知|不支持|未注册).*(?:status|操作|动作|接口)/iu.test(message);
302
+ }
303
+ //#endregion
304
+ //#region src/client/features/assistant-configuration-tabs.ts
305
+ const tabs = [
306
+ {
307
+ id: "basic",
308
+ label: "awb.tab.basic"
309
+ },
310
+ {
311
+ id: "appearance",
312
+ label: "awb.tab.appearance"
313
+ },
314
+ {
315
+ id: "voice",
316
+ label: "awb.tab.voice"
317
+ },
318
+ {
319
+ id: "more",
320
+ label: "awb.more"
321
+ }
322
+ ];
323
+ /** 延迟挂载尚未访问的页,访问后保留实例,避免切换丢失资源表单或中断安装。 */
324
+ function AssistantConfigurationTabs({ active, visited, panels, preview, t, onChange }) {
325
+ const id = (0, react.useId)();
326
+ return (0, react.createElement)("div", {
327
+ "data-codingns-configuration-tabs": true,
328
+ style: {
329
+ display: "grid",
330
+ gap: 11,
331
+ minWidth: 0
332
+ }
333
+ }, (0, react.createElement)(AssistantConfigurationTabBar, {
334
+ id,
335
+ active,
336
+ t,
337
+ onChange
338
+ }), (0, react.createElement)("div", { style: {
339
+ display: "flex",
340
+ flexWrap: "wrap",
341
+ alignItems: "stretch",
342
+ gap: 22,
343
+ minWidth: 0
344
+ } }, (0, react.createElement)("div", { style: {
345
+ flex: "1 1 330px",
346
+ minWidth: 0
347
+ } }, ...tabs.map((tab) => (0, react.createElement)("section", {
348
+ key: tab.id,
349
+ id: `${id}-panel-${tab.id}`,
350
+ role: "tabpanel",
351
+ "aria-labelledby": `${id}-tab-${tab.id}`,
352
+ hidden: tab.id !== active,
353
+ tabIndex: 0,
354
+ "data-codingns-configuration-page": tab.id,
355
+ style: {
356
+ display: tab.id === active ? "grid" : "none",
357
+ gap: 11,
358
+ minWidth: 0
359
+ }
360
+ }, tab.id === active || visited.includes(tab.id) ? panels[tab.id] : null))), (0, react.createElement)("div", {
361
+ hidden: active === "more",
362
+ "data-codingns-configuration-preview": true,
363
+ style: {
364
+ display: active === "more" ? "none" : "flex",
365
+ flex: "1 1 290px",
366
+ minWidth: 0
367
+ }
368
+ }, preview)));
369
+ }
370
+ /** 标签可用方向键、Home 和 End 切换,只有当前标签进入顺序焦点。 */
371
+ function AssistantConfigurationTabBar({ id, active, t, onChange }) {
372
+ const navigate = (event, index) => {
373
+ let next;
374
+ if (event.key === "ArrowRight") next = (index + 1) % tabs.length;
375
+ else if (event.key === "ArrowLeft") next = (index + tabs.length - 1) % tabs.length;
376
+ else if (event.key === "Home") next = 0;
377
+ else if (event.key === "End") next = tabs.length - 1;
378
+ else return;
379
+ event.preventDefault();
380
+ onChange(tabs[next].id);
381
+ event.currentTarget.parentElement?.querySelectorAll("[role=\"tab\"]")[next]?.focus();
382
+ };
383
+ return (0, react.createElement)("div", {
384
+ role: "tablist",
385
+ "aria-label": t("awb.configure"),
386
+ style: {
387
+ position: "sticky",
388
+ top: 0,
389
+ zIndex: 2,
390
+ display: "flex",
391
+ gap: 4,
392
+ padding: 4,
393
+ borderRadius: 12,
394
+ background: require_theme.dshThemeColor.cardBackground,
395
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
396
+ overflowX: "auto",
397
+ minWidth: 0
398
+ }
399
+ }, ...tabs.map((tab, index) => (0, react.createElement)("button", {
400
+ key: tab.id,
401
+ type: "button",
402
+ role: "tab",
403
+ id: `${id}-tab-${tab.id}`,
404
+ "aria-controls": `${id}-panel-${tab.id}`,
405
+ "aria-selected": active === tab.id,
406
+ tabIndex: active === tab.id ? 0 : -1,
407
+ onClick: () => onChange(tab.id),
408
+ onKeyDown: (event) => navigate(event, index),
409
+ style: {
410
+ flex: "1 0 auto",
411
+ minHeight: 36,
412
+ border: 0,
413
+ borderRadius: 8,
414
+ padding: "7px 12px",
415
+ whiteSpace: "nowrap",
416
+ cursor: "pointer",
417
+ font: "inherit",
418
+ fontSize: 13,
419
+ fontWeight: active === tab.id ? 600 : 400,
420
+ color: active === tab.id ? require_theme.dshThemeColor.labelPrimary : require_theme.dshThemeColor.labelSecondary,
421
+ background: active === tab.id ? require_theme.dshThemeColor.pageBackground : "transparent",
422
+ boxShadow: active === tab.id ? require_theme.dshThemeColor.subtleShadow : "none"
423
+ }
424
+ }, t(tab.label))));
425
+ }
426
+ const ASSISTANT_IMAGE_MEDIA_TYPES = [
427
+ "image/png",
428
+ "image/jpeg",
429
+ "image/webp",
430
+ "image/gif"
431
+ ];
432
+ //#endregion
433
+ //#region src/shared/assistant-commands.ts
434
+ /** 清理指令是固定的协议标识,中文别名不随界面语言变化。 */
435
+ const ASSISTANT_CLEAR_COMMANDS = [
436
+ "/clear",
437
+ "/清理",
438
+ "/reset",
439
+ "/重置"
440
+ ];
441
+ //#endregion
442
+ //#region src/client/features/assistant-composer-input.ts
443
+ /** 只匹配完整指令,避免把路径、带参数文本或普通问题误当成破坏性操作。 */
444
+ function readAssistantComposerCommand(text) {
445
+ return ASSISTANT_CLEAR_COMMANDS.includes(text.trim().toLowerCase()) ? "clear" : void 0;
446
+ }
447
+ function isAssistantClearCommandSuggestion(text) {
448
+ const prefix = text.trim().toLowerCase();
449
+ return prefix.startsWith("/") && ASSISTANT_CLEAR_COMMANDS.some((command) => command.startsWith(prefix));
450
+ }
451
+ /** 先归零再测量,删除内容也能缩回单行;超过视口上限后仅内部滚动。 */
452
+ function resizeAssistantComposerTextarea(input) {
453
+ const style = input.ownerDocument.defaultView?.getComputedStyle(input);
454
+ const minimum = Number.parseFloat(style?.minHeight ?? "") || 38;
455
+ const maximum = Number.parseFloat(style?.maxHeight ?? "") || 120;
456
+ input.style.height = "0px";
457
+ const height = input.scrollHeight;
458
+ input.style.height = `${Math.min(maximum, Math.max(minimum, height))}px`;
459
+ input.style.overflowY = height > maximum ? "auto" : "hidden";
460
+ }
461
+ function validateAssistantFiles(files) {
462
+ if (files.length > 6) throw new Error("awb.attachments.countError");
463
+ if (files.some((file) => file.size > 10485760) || files.reduce((sum, file) => sum + file.size, 0) > 20971520) throw new Error("awb.attachments.sizeError");
464
+ if (files.some((file) => file.type.startsWith("image/") && !ASSISTANT_IMAGE_MEDIA_TYPES.some((type) => type === file.type))) throw new Error("awb.attachments.typeError");
465
+ }
466
+ /** 附件在点击发送时才编码;选择、删除或取消草稿不会在 Host 留下文件。 */
467
+ async function encodeAssistantFiles(files) {
468
+ validateAssistantFiles(files);
469
+ return await Promise.all(files.map(async (file) => {
470
+ const bytes = new Uint8Array(await file.arrayBuffer());
471
+ let binary = "";
472
+ for (let offset = 0; offset < bytes.length; offset += 8192) binary += String.fromCharCode(...bytes.subarray(offset, offset + 8192));
473
+ return {
474
+ name: file.name,
475
+ mediaType: file.type,
476
+ data: btoa(binary)
477
+ };
478
+ }));
479
+ }
480
+ //#endregion
481
+ //#region src/client/features/assistant-workbench-controls.ts
482
+ /** 活动状态优先;只有正在构建索引才显示认知更新,等待或缺少结果不代表正在执行。 */
483
+ function resolveAssistantWorkbenchStatus({ running, working, voiceActive, voiceState, hasProjects, indexState }) {
484
+ if (running || voiceActive && voiceState === "thinking") return "thinking";
485
+ if (working || voiceActive) return "working";
486
+ if (hasProjects && indexState === "building") return "updating";
487
+ return "idle";
488
+ }
489
+ function AssistantStatusBadge({ status, t }) {
490
+ return (0, react.createElement)("span", {
491
+ role: "status",
492
+ "aria-live": "polite",
493
+ "data-codingns-assistant-status": status,
494
+ style: {
495
+ display: "inline-flex",
496
+ alignItems: "center",
497
+ gap: 5,
498
+ padding: "3px 8px",
499
+ borderRadius: 999,
500
+ fontSize: 11,
501
+ lineHeight: 1.4,
502
+ whiteSpace: "nowrap",
503
+ color: status === "idle" ? require_theme.dshThemeColor.labelSecondary : require_theme.dshThemeColor.accent,
504
+ background: require_theme.dshThemeColor.surfaceSubtle,
505
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
506
+ flexShrink: 0
507
+ }
508
+ }, (0, react.createElement)("span", {
509
+ "aria-hidden": true,
510
+ style: {
511
+ width: 5,
512
+ height: 5,
513
+ borderRadius: "50%",
514
+ background: "currentColor"
515
+ }
516
+ }), t(`awb.state.${status}`));
517
+ }
518
+ function AssistantComposer(props) {
519
+ const textarea = (0, react.useRef)(null);
520
+ const picker = (0, react.useRef)(null);
521
+ (0, react.useEffect)(() => {
522
+ if (textarea.current !== null) resizeAssistantComposerTextarea(textarea.current);
523
+ }, [props.value, props.disabled]);
524
+ (0, react.useEffect)(() => {
525
+ const input = textarea.current;
526
+ if (input === null) return void 0;
527
+ const resize = () => resizeAssistantComposerTextarea(input);
528
+ let width = input.clientWidth;
529
+ const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(() => {
530
+ if (input.clientWidth !== width) {
531
+ width = input.clientWidth;
532
+ resize();
533
+ }
534
+ });
535
+ observer?.observe(input);
536
+ window.addEventListener("resize", resize);
537
+ window.visualViewport?.addEventListener("resize", resize);
538
+ return () => {
539
+ observer?.disconnect();
540
+ window.removeEventListener("resize", resize);
541
+ window.visualViewport?.removeEventListener("resize", resize);
542
+ };
543
+ }, []);
544
+ return (0, react.createElement)("div", null, (0, react.createElement)(AssistantControlsStyle), (0, react.createElement)("input", {
545
+ ref: picker,
546
+ type: "file",
547
+ multiple: true,
548
+ hidden: true,
549
+ "aria-label": props.t("awb.attachments.add"),
550
+ onChange: (event) => {
551
+ const files = Array.from(event.currentTarget.files ?? []);
552
+ event.currentTarget.value = "";
553
+ if (!props.disabled) props.onFiles?.(files);
554
+ }
555
+ }), (0, react.createElement)(AssistantComposerView, {
556
+ ...props,
557
+ textareaRef: textarea,
558
+ onAttach: props.onFiles === void 0 ? void 0 : () => picker.current?.click()
559
+ }));
560
+ }
561
+ /** 事件与布局保持独立,自动高度和文件选择器只由外层绑定浏览器生命周期。 */
562
+ function AssistantComposerView({ t, value, disabled, onChange, onSend, label = "awb.send", running = false, stopping = false, onStop, voiceActive = false, voiceDisabled = true, onVoice, files = [], onRemoveFile, onFiles, onAttach, textareaRef }) {
563
+ const send = () => {
564
+ if (!disabled && !running && (value.trim() || files.length > 0)) onSend();
565
+ };
566
+ const voiceLabel = t(voiceActive ? "voice.dialog.stop" : "voice.dialog.start");
567
+ const sendDisabled = running ? stopping || onStop === void 0 : disabled || !value.trim() && files.length === 0;
568
+ return (0, react.createElement)("form", {
569
+ "data-codingns-assistant-composer": true,
570
+ style: {
571
+ display: "flex",
572
+ flexDirection: "column",
573
+ gap: 8,
574
+ padding: "10px 12px",
575
+ borderRadius: 30,
576
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
577
+ background: require_theme.dshThemeColor.surfaceSubtle,
578
+ minWidth: 0,
579
+ outline: "none",
580
+ boxShadow: "none"
581
+ },
582
+ onSubmit: (event) => {
583
+ event.preventDefault();
584
+ send();
585
+ }
586
+ }, files.length === 0 ? null : (0, react.createElement)("div", {
587
+ "data-codingns-assistant-attachments": true,
588
+ style: {
589
+ display: "flex",
590
+ flexWrap: "wrap",
591
+ gap: 6,
592
+ padding: "4px 8px",
593
+ maxHeight: 100,
594
+ overflowY: "auto"
595
+ }
596
+ }, ...files.map((file, index) => (0, react.createElement)("span", {
597
+ key: `${index}:${file.name}`,
598
+ style: {
599
+ display: "inline-flex",
600
+ alignItems: "center",
601
+ gap: 4,
602
+ maxWidth: "100%",
603
+ padding: "3px 8px",
604
+ borderRadius: 10,
605
+ background: require_theme.dshThemeColor.buttonBackground,
606
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
607
+ fontSize: 12
608
+ }
609
+ }, (0, react.createElement)("span", {
610
+ title: file.name,
611
+ style: {
612
+ overflow: "hidden",
613
+ textOverflow: "ellipsis",
614
+ whiteSpace: "nowrap"
615
+ }
616
+ }, file.name), (0, react.createElement)(AssistantIconButton, {
617
+ icon: "close",
618
+ label: t("awb.attachments.remove", { name: file.name }),
619
+ disabled,
620
+ size: 24,
621
+ onClick: () => onRemoveFile?.(index)
622
+ })))), (0, react.createElement)("div", { style: {
623
+ display: "flex",
624
+ alignItems: "flex-end",
625
+ gap: 8,
626
+ minWidth: 0
627
+ } }, onAttach === void 0 ? null : (0, react.createElement)(AssistantIconButton, {
628
+ icon: "add",
629
+ label: t("awb.attachments.add"),
630
+ disabled,
631
+ size: 38,
632
+ onClick: onAttach
633
+ }), (0, react.createElement)("textarea", {
634
+ ref: textareaRef,
635
+ value,
636
+ disabled,
637
+ maxLength: 8e3,
638
+ rows: 1,
639
+ "aria-label": t("awb.placeholder"),
640
+ placeholder: t("awb.placeholder"),
641
+ style: {
642
+ flex: "1 1 auto",
643
+ width: "100%",
644
+ minWidth: 0,
645
+ minHeight: 38,
646
+ maxHeight: "min(120px, 20dvh)",
647
+ padding: "9px 0",
648
+ boxSizing: "border-box",
649
+ resize: "none",
650
+ overflowY: "hidden",
651
+ border: 0,
652
+ outline: "none",
653
+ boxShadow: "none",
654
+ borderRadius: 0,
655
+ background: "transparent",
656
+ color: require_theme.dshThemeColor.labelPrimary,
657
+ font: "inherit",
658
+ fontSize: 14,
659
+ lineHeight: "20px"
660
+ },
661
+ onChange: (event) => onChange(event.currentTarget.value),
662
+ onInput: (event) => resizeAssistantComposerTextarea(event.currentTarget),
663
+ onPaste: (event) => {
664
+ if (!disabled && onFiles !== void 0 && event.clipboardData.files.length > 0) {
665
+ event.preventDefault();
666
+ onFiles(Array.from(event.clipboardData.files));
667
+ }
668
+ },
669
+ onKeyDown: (event) => {
670
+ if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
671
+ event.preventDefault();
672
+ send();
673
+ }
674
+ }
675
+ }), onVoice === void 0 ? null : (0, react.createElement)("button", {
676
+ type: "button",
677
+ disabled: voiceDisabled,
678
+ "aria-label": voiceLabel,
679
+ className: "codingns-assistant-icon-button",
680
+ "aria-pressed": voiceActive,
681
+ title: voiceDisabled && !voiceActive ? t("awb.voiceUnavailable") : voiceLabel,
682
+ style: {
683
+ ...iconButtonStyle,
684
+ color: voiceActive ? require_theme.dshThemeColor.accent : require_theme.dshThemeColor.labelPrimary,
685
+ opacity: voiceDisabled ? .4 : 1,
686
+ cursor: voiceDisabled ? "default" : "pointer"
687
+ },
688
+ onClick: () => {
689
+ if (!voiceDisabled) onVoice();
690
+ }
691
+ }, (0, react.createElement)(require_primitives_adapter.resolveAssistantControlIcon("phone"), { size: 20 })), (0, react.createElement)("button", {
692
+ type: running ? "button" : "submit",
693
+ "aria-label": t(running ? "awb.stop" : label),
694
+ title: t(running ? "awb.stop" : label),
695
+ disabled: sendDisabled,
696
+ onClick: running ? () => {
697
+ if (!stopping) onStop?.();
698
+ } : void 0,
699
+ style: {
700
+ ...iconButtonStyle,
701
+ color: require_theme.dshThemeColor.switchThumb,
702
+ background: require_theme.dshThemeColor.accent,
703
+ opacity: sendDisabled ? .4 : 1,
704
+ cursor: sendDisabled ? "default" : "pointer"
705
+ }
706
+ }, (0, react.createElement)(require_primitives_adapter.resolveAssistantControlIcon(running ? "stop" : "send"), { size: 20 }))), !isAssistantClearCommandSuggestion(value) ? null : (0, react.createElement)("button", {
707
+ type: "button",
708
+ disabled,
709
+ "data-codingns-assistant-command": "clear",
710
+ style: {
711
+ ...require_theme.dshSettingsButtonStyle,
712
+ textAlign: "left",
713
+ margin: "0 6px 2px",
714
+ border: 0,
715
+ borderRadius: 10
716
+ },
717
+ onClick: () => {
718
+ if (!disabled) onChange("/clear");
719
+ }
720
+ }, (0, react.createElement)("strong", { style: { marginRight: 8 } }, "/clear"), t("awb.command.clear")));
721
+ }
722
+ function AssistantIconButton({ icon, label, disabled = false, size = 32, onClick }) {
723
+ return (0, react.createElement)("button", {
724
+ type: "button",
725
+ disabled,
726
+ title: label,
727
+ "aria-label": label,
728
+ className: "codingns-assistant-icon-button",
729
+ style: {
730
+ ...iconButtonStyle,
731
+ width: size,
732
+ height: size,
733
+ borderRadius: size === 38 ? "50%" : 8,
734
+ color: require_theme.dshThemeColor.labelSecondary,
735
+ opacity: disabled ? .4 : 1,
736
+ cursor: disabled ? "default" : "pointer"
737
+ },
738
+ onClick: () => {
739
+ if (!disabled) onClick();
740
+ }
741
+ }, (0, react.createElement)(require_primitives_adapter.resolveAssistantControlIcon(icon), { size: size < 30 ? 14 : 20 }));
742
+ }
743
+ /** 第一轮重置确认只推进阶段,第二轮才允许执行,清理仍只需一次确认。 */
744
+ function confirmAssistantMaintenance(stage, next, execute) {
745
+ if (stage === "reset-first") next("reset-final");
746
+ else execute(stage === "clear" ? "clear" : "reset");
747
+ }
748
+ function AssistantMaintenanceDialog(props) {
749
+ const ref = (0, react.useRef)(null);
750
+ const id = (0, react.useId)();
751
+ (0, react.useEffect)(() => {
752
+ const opener = document.activeElement;
753
+ const dialog = ref.current;
754
+ dialog?.showModal();
755
+ return () => {
756
+ if (dialog?.open) dialog.close();
757
+ if (opener?.isConnected) opener.focus();
758
+ };
759
+ }, []);
760
+ return (0, react.createElement)("dialog", {
761
+ ref,
762
+ "aria-labelledby": id,
763
+ "data-codingns-assistant-confirmation": props.stage,
764
+ onCancel: (event) => {
765
+ event.preventDefault();
766
+ if (!props.disabled) props.onCancel();
767
+ },
768
+ onKeyDown: (event) => {
769
+ if (event.key === "Escape") {
770
+ event.preventDefault();
771
+ event.stopPropagation();
772
+ if (!props.disabled) props.onCancel();
773
+ }
774
+ },
775
+ style: {
776
+ padding: 11,
777
+ borderRadius: 16,
778
+ maxWidth: "min(440px, calc(100vw - 32px))",
779
+ maxHeight: "calc(100dvh - 32px)",
780
+ margin: "auto",
781
+ boxSizing: "border-box",
782
+ overflowY: "auto",
783
+ background: require_theme.dshThemeColor.pageBackground,
784
+ color: require_theme.dshThemeColor.labelPrimary,
785
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
786
+ boxShadow: require_theme.dshThemeColor.prominentShadow
787
+ }
788
+ }, (0, react.createElement)("strong", {
789
+ id,
790
+ style: { fontSize: 17 }
791
+ }, props.t(props.stage === "clear" ? "awb.clear" : props.stage === "reset-first" ? "awb.reset" : "awb.resetFinalTitle")), (0, react.createElement)(AssistantMaintenanceConfirmationView, props));
792
+ }
793
+ function AssistantMaintenanceConfirmationView({ stage, t, disabled, error, onConfirm, onCancel }) {
794
+ return (0, react.createElement)("div", { style: {
795
+ display: "grid",
796
+ gap: 16,
797
+ marginTop: 14,
798
+ fontSize: 13,
799
+ lineHeight: 1.6
800
+ } }, (0, react.createElement)("p", { style: {
801
+ margin: 0,
802
+ color: require_theme.dshThemeColor.labelSecondary
803
+ } }, t(stage === "clear" ? "awb.clearConfirm" : stage === "reset-first" ? "awb.resetConfirm" : "awb.resetFinalConfirm")), !error ? null : (0, react.createElement)("p", {
804
+ role: "alert",
805
+ style: {
806
+ margin: 0,
807
+ color: require_theme.dshThemeColor.error,
808
+ overflowWrap: "anywhere"
809
+ }
810
+ }, error), (0, react.createElement)("div", { style: {
811
+ display: "flex",
812
+ gap: 8,
813
+ justifyContent: "flex-end"
814
+ } }, (0, react.createElement)("button", {
815
+ type: "button",
816
+ autoFocus: true,
817
+ disabled,
818
+ style: require_theme.dshSettingsButtonStyle,
819
+ onClick: () => {
820
+ if (!disabled) onCancel();
821
+ }
822
+ }, t("awb.cancel")), (0, react.createElement)("button", {
823
+ type: "button",
824
+ disabled,
825
+ style: require_theme.dshSettingsPrimaryButtonStyle,
826
+ onClick: () => {
827
+ if (!disabled) onConfirm();
828
+ }
829
+ }, t(stage === "reset-first" ? "awb.resetContinue" : stage === "reset-final" ? "awb.resetFinalAction" : "awb.confirm"))));
830
+ }
831
+ const iconButtonStyle = {
832
+ display: "inline-flex",
833
+ alignItems: "center",
834
+ justifyContent: "center",
835
+ flexShrink: 0,
836
+ width: 38,
837
+ height: 38,
838
+ padding: 0,
839
+ border: 0,
840
+ borderRadius: "50%",
841
+ background: "transparent",
842
+ cursor: "pointer"
843
+ };
844
+ /** 创建与配置页也复用标题按钮的悬停样式,不依赖对话输入栏挂载。 */
845
+ function AssistantControlsStyle() {
846
+ return (0, react.createElement)("style", null, assistantComposerCss);
847
+ }
848
+ const assistantComposerCss = `
849
+ [data-codingns-assistant-composer] textarea,[data-codingns-assistant-composer] textarea:focus,[data-codingns-assistant-composer] textarea:focus-visible{outline:none!important;box-shadow:none!important;border:0!important;resize:none!important}
850
+ .codingns-assistant-icon-button:not(:disabled):hover{background:var(--dsw-alias-interactive-bg-hover)!important}
851
+ .codingns-assistant-icon-button:not(:disabled):active{background:var(--dsw-alias-interactive-bg-active)!important}
852
+ `;
853
+ //#endregion
854
+ //#region src/client/features/assistant-configuration-session.ts
855
+ const resourceActions = /* @__PURE__ */ new Set([
856
+ "assistant/tts/setup",
857
+ "assistant/tts/import",
858
+ "assistant/voice/setup",
859
+ "assistant/voice/initialize",
860
+ "avatar/install",
861
+ "avatar/installCatalog",
862
+ "avatar/installEngine"
863
+ ]);
864
+ /** 配置窗口的唯一草稿存储;普通写入只修改内存,正式运行仍读原服务。 */
865
+ var AssistantConfigurationSession = class {
866
+ original;
867
+ services;
868
+ snapshot;
869
+ listeners = /* @__PURE__ */ new Set();
870
+ changes = /* @__PURE__ */ new Map();
871
+ portraits = /* @__PURE__ */ new Map();
872
+ portraitValues = /* @__PURE__ */ new Map();
873
+ removedPortraits = /* @__PURE__ */ new Set();
874
+ removals = /* @__PURE__ */ new Map();
875
+ devices = /* @__PURE__ */ new Map();
876
+ releaseAdapter;
877
+ preparationSupport;
878
+ preparations = 0;
879
+ lifetime = new AbortController();
880
+ localRevision = 0;
881
+ constructor(original) {
882
+ this.original = original;
883
+ this.snapshot = {
884
+ ...original.settings.getSnapshot(),
885
+ value: structuredClone(original.settings.getSnapshot().value),
886
+ revision: this.localRevision,
887
+ preparing: false
888
+ };
889
+ this.services = {
890
+ ...original,
891
+ configurationDraft: true,
892
+ settings: this,
893
+ rpc: { call: (channel, endpoint, payload, signal) => this.call(channel, endpoint, payload, signal) }
894
+ };
895
+ require_manager.bindAssistantAvatarRegistry(this.services, require_manager.getAssistantAvatarRegistry(original));
896
+ require_manager.bindAssistantAvatarPortraitService(this.services, new require_manager.AssistantAvatarPortraitService(void 0, void 0, void 0, void 0, void 0, {
897
+ read: async (key, signal) => this.portraitValues.get(key) ?? await require_manager.assistantAvatarPortraitOverrideStore.read(key, signal),
898
+ write: async (key, id, _surface, preview, signal) => {
899
+ signal?.throwIfAborted();
900
+ const model = require_manager.getAssistantAvatarManager(this.services).list().find((item) => item.id === id);
901
+ if (model === void 0) return false;
902
+ this.portraitValues.set(key, preview);
903
+ this.portraits.set(key, {
904
+ model,
905
+ renderer: require_manager.getAssistantAvatarRegistry(this.services).get(model.surfaces?.dialog?.renderer ?? model.renderer),
906
+ preview
907
+ });
908
+ return true;
909
+ },
910
+ removeModel: async (id) => {
911
+ this.removedPortraits.add(id);
912
+ for (const [key, item] of this.portraits) if (item.model.id === id) {
913
+ this.portraits.delete(key);
914
+ this.portraitValues.delete(key);
915
+ }
916
+ }
917
+ }));
918
+ require_manager.bindAssistantAvatarManager(this.services, new require_manager.AssistantAvatarManager(this, require_manager.getAssistantAvatarManager(original).adapters, void 0, void 0, this.services.rpc, (id) => {
919
+ this.removedPortraits.add(id);
920
+ }));
921
+ const adapter = require_manager.getGlobalVoiceAdapter(original);
922
+ if (adapter !== void 0) this.releaseAdapter = require_manager.registerGlobalVoiceAdapter(this.services, new Proxy(adapter, { get: (target, key) => {
923
+ if (key === "selectInputDevice" || key === "selectOutputDevice") return async (id) => {
924
+ this.requireWritable();
925
+ this.devices.set(key === "selectInputDevice" ? "input" : "output", id);
926
+ };
927
+ if (key === "inputDeviceId" || key === "outputDeviceId") return this.devices.get(key === "inputDeviceId" ? "input" : "output") ?? Reflect.get(target, key);
928
+ const value = Reflect.get(target, key);
929
+ return typeof value === "function" ? value.bind(target) : value;
930
+ } }));
931
+ }
932
+ getSnapshot = () => this.snapshot;
933
+ subscribe = (listener) => {
934
+ this.listeners.add(listener);
935
+ return () => this.listeners.delete(listener);
936
+ };
937
+ /** 权威快照变化仅更新未编辑字段,后台状态刷新不能抹掉模型等草稿。 */
938
+ sync() {
939
+ const fresh = this.original.settings.getSnapshot();
940
+ let value = structuredClone(fresh.value);
941
+ if (value !== void 0) for (const operation of this.changes.values()) value = apply(value, operation);
942
+ this.publish({
943
+ ...fresh,
944
+ value,
945
+ revision: this.localRevision
946
+ });
947
+ }
948
+ reset() {
949
+ this.lifetime.abort();
950
+ this.lifetime = new AbortController();
951
+ this.preparationSupport = void 0;
952
+ this.changes.clear();
953
+ this.portraits.clear();
954
+ this.portraitValues.clear();
955
+ this.removedPortraits.clear();
956
+ this.removals.clear();
957
+ this.devices.clear();
958
+ require_manager.getAssistantAvatarPortraitService(this.services).dispose();
959
+ this.localRevision++;
960
+ this.sync();
961
+ }
962
+ async mutate(operations, expectedRevision) {
963
+ this.requireWritable();
964
+ if (expectedRevision !== void 0 && expectedRevision !== this.localRevision) throw new Error("settings namespace \"assistant-draft\" changed since it was read (expected revision)");
965
+ for (const operation of operations) if (operation.path[0] !== "assistant" || operation.path.length < 2 || operation.path.length > 4 || operation.path.some((key) => !/^[A-Za-z][A-Za-z0-9_-]*$/u.test(key) || ["constructor", "prototype"].includes(key))) throw new Error("配置草稿只允许修改助理字段");
966
+ let value = this.snapshot.value;
967
+ for (const operation of operations) {
968
+ value = apply(value, operation);
969
+ const path = operation.path.join(".");
970
+ for (const key of this.changes.keys()) if (key === path || key.startsWith(path + ".")) this.changes.delete(key);
971
+ this.changes.set(path, structuredClone(operation));
972
+ }
973
+ this.localRevision++;
974
+ this.publish({
975
+ ...this.snapshot,
976
+ value,
977
+ revision: this.localRevision
978
+ });
979
+ return true;
980
+ }
981
+ async set(field, value) {
982
+ return this.mutate([{
983
+ op: "set",
984
+ path: field.split("."),
985
+ value
986
+ }]);
987
+ }
988
+ async unset(field) {
989
+ return this.mutate([{
990
+ op: "unset",
991
+ path: field.split(".")
992
+ }]);
993
+ }
994
+ async load() {
995
+ this.sync();
996
+ }
997
+ async reload() {
998
+ await this.original.settings.reload?.();
999
+ this.sync();
1000
+ }
1001
+ /** 身份字段由生命周期接口校验,其余字段同一请求合并保存。 */
1002
+ configurationPatch() {
1003
+ return [...this.changes.values()].filter((operation) => [
1004
+ "appearance",
1005
+ "voice",
1006
+ "tts",
1007
+ "prompts"
1008
+ ].includes(operation.path[1])).map((operation) => ({
1009
+ ...operation,
1010
+ path: operation.path.slice(1)
1011
+ }));
1012
+ }
1013
+ async commitLocal(signal) {
1014
+ for (const item of this.portraits.values()) await require_manager.getAssistantAvatarPortraitService(this.original).setPortrait(item.model, item.renderer, item.preview, signal);
1015
+ const adapter = require_manager.getGlobalVoiceAdapter(this.original);
1016
+ for (const [direction, id] of this.devices) {
1017
+ signal.throwIfAborted();
1018
+ await (direction === "input" ? adapter?.selectInputDevice(id) : adapter?.selectOutputDevice(id));
1019
+ }
1020
+ for (const id of this.removedPortraits) require_manager.getAssistantAvatarPortraitService(this.original).removeModel(id);
1021
+ for (const { endpoint, payload } of this.removals.values()) {
1022
+ const result = await this.original.rpc.call(require_transport.CODINGNS_RPC_CHANNEL, endpoint, payload, signal);
1023
+ if (!result.ok) throw new Error(result.error.message);
1024
+ }
1025
+ }
1026
+ dispose() {
1027
+ this.lifetime.abort();
1028
+ this.releaseAdapter?.();
1029
+ require_manager.getAssistantAvatarPortraitService(this.services).dispose();
1030
+ this.listeners.clear();
1031
+ }
1032
+ requireWritable() {
1033
+ if (!this.original.settings.getSnapshot().writable || !this.snapshot.writable || this.snapshot.status !== "ready" || this.snapshot.value === void 0) throw new Error("当前助理配置不可写");
1034
+ }
1035
+ publish(next) {
1036
+ const snapshot = {
1037
+ ...next,
1038
+ preparing: this.preparations > 0
1039
+ };
1040
+ if (require_manager.sameSettingsValue(this.snapshot, snapshot)) return;
1041
+ this.snapshot = snapshot;
1042
+ for (const listener of this.listeners) listener();
1043
+ }
1044
+ async call(channel, endpoint, payload, signal) {
1045
+ if (endpoint === "avatar/releasePreview") return this.original.rpc.call(channel, endpoint, payload, signal);
1046
+ const ownedSignal = AbortSignal.any([this.lifetime.signal, ...signal === void 0 ? [] : [signal]]);
1047
+ const preparing = resourceActions.has(endpoint);
1048
+ if (preparing) {
1049
+ this.preparations++;
1050
+ this.publish(this.snapshot);
1051
+ }
1052
+ try {
1053
+ return await this.dispatch(channel, endpoint, payload, ownedSignal);
1054
+ } finally {
1055
+ if (preparing) {
1056
+ this.preparations--;
1057
+ this.publish(this.snapshot);
1058
+ }
1059
+ }
1060
+ }
1061
+ async dispatch(channel, endpoint, payload, signal) {
1062
+ signal?.throwIfAborted();
1063
+ const value = payload;
1064
+ if (endpoint === "avatar/installEngine") this.requireWritable();
1065
+ if (endpoint === "assistant/index/configure") return {
1066
+ ok: true,
1067
+ value: { configured: true }
1068
+ };
1069
+ if ([
1070
+ "avatar/catalog",
1071
+ "avatar/previewCatalog",
1072
+ "avatar/keepPreview",
1073
+ "avatar/installCatalog"
1074
+ ].includes(endpoint)) {
1075
+ const thirdPartyConsent = this.snapshot.value?.assistant.appearance?.thirdPartyConsent;
1076
+ if (!require_manager.hasAssistantAvatarConsent(thirdPartyConsent)) return {
1077
+ ok: false,
1078
+ error: {
1079
+ code: "CODINGNS_AVATAR_CONSENT_REQUIRED",
1080
+ message: require_transport.codingNsTranslator(this.original.locale)("avatar.thirdPartyConsentRequired")
1081
+ }
1082
+ };
1083
+ payload = {
1084
+ ...value,
1085
+ thirdPartyConsent
1086
+ };
1087
+ }
1088
+ if (endpoint === "avatar/remove") {
1089
+ this.requireWritable();
1090
+ this.removals.set(endpoint + ":" + String(value?.id), {
1091
+ endpoint,
1092
+ payload
1093
+ });
1094
+ return {
1095
+ ok: true,
1096
+ value: void 0
1097
+ };
1098
+ }
1099
+ if ([
1100
+ "assistant/tts/select",
1101
+ "assistant/tts/configure",
1102
+ "assistant/tts/remove"
1103
+ ].includes(endpoint)) {
1104
+ const tts = require_manager.readAssistantTtsSettings(this.snapshot.value.assistant.tts);
1105
+ const next = endpoint.endsWith("/configure") ? {
1106
+ ...tts,
1107
+ parameters: require_manager.validateAssistantTtsParameters(value?.parameters, tts.parameters)
1108
+ } : endpoint.endsWith("/remove") ? {
1109
+ ...tts,
1110
+ voices: tts.voices.filter((voice) => voice.id !== value?.id),
1111
+ selectedId: tts.selectedId === value?.id ? "moss:Junhao" : tts.selectedId
1112
+ } : {
1113
+ ...tts,
1114
+ selectedId: value?.id ?? tts.selectedId,
1115
+ backend: value?.backend === "browser" ? "browser" : "moss-onnx"
1116
+ };
1117
+ await this.set("assistant.tts", next);
1118
+ return this.call(channel, "assistant/tts/catalog", {}, signal);
1119
+ }
1120
+ const preparing = [
1121
+ "assistant/tts/setup",
1122
+ "assistant/tts/import",
1123
+ "assistant/voice/setup",
1124
+ "assistant/voice/initialize"
1125
+ ].includes(endpoint);
1126
+ if (preparing) {
1127
+ this.requireWritable();
1128
+ this.preparationSupport ??= this.original.rpc.call(channel, "assistant/configuration/capabilities", {}, signal).then((response) => response.ok && response.value?.prepareOnly === true);
1129
+ if (!await this.preparationSupport) return {
1130
+ ok: false,
1131
+ error: {
1132
+ code: "UNSUPPORTED",
1133
+ message: require_transport.codingNsTranslator(this.original.locale)("awb.preparationUnsupported")
1134
+ }
1135
+ };
1136
+ signal?.throwIfAborted();
1137
+ }
1138
+ const preparedModelId = endpoint === "assistant/voice/initialize" ? findAssistantVoiceModel(this.snapshot.value.assistant.voice.modelId ?? "")?.id : void 0;
1139
+ const result = await this.original.rpc.call(channel, endpoint, preparing ? {
1140
+ ...value,
1141
+ ...preparedModelId === void 0 ? {} : { modelId: preparedModelId },
1142
+ prepareOnly: true
1143
+ } : payload, signal);
1144
+ signal.throwIfAborted();
1145
+ if (!result.ok) return result;
1146
+ if (preparing) {
1147
+ const prepared = result.value;
1148
+ if (endpoint === "assistant/voice/setup" && prepared.voice !== void 0) await this.set("assistant.voice", prepared.voice);
1149
+ if (endpoint === "assistant/voice/initialize") {
1150
+ if (prepared.voice !== void 0) await this.set("assistant.voice", prepared.voice);
1151
+ await this.set("assistant.tts", {
1152
+ ...require_manager.readAssistantTtsSettings(this.snapshot.value.assistant.tts),
1153
+ backend: "moss-onnx"
1154
+ });
1155
+ }
1156
+ if (endpoint === "assistant/tts/setup") await this.set("assistant.tts", {
1157
+ ...require_manager.readAssistantTtsSettings(this.snapshot.value.assistant.tts),
1158
+ backend: "moss-onnx"
1159
+ });
1160
+ if (endpoint === "assistant/tts/import") {
1161
+ const current = require_manager.readAssistantTtsSettings(this.snapshot.value.assistant.tts), incoming = prepared.settings;
1162
+ const voices = new Map([...current.voices, ...incoming.voices].map((voice) => [voice.id, voice]));
1163
+ await this.set("assistant.tts", {
1164
+ ...current,
1165
+ ...incoming,
1166
+ voices: [...voices.values()]
1167
+ });
1168
+ }
1169
+ }
1170
+ if (endpoint.startsWith("assistant/tts/") && result.value?.settings !== void 0) {
1171
+ const raw = this.snapshot.value.assistant.tts;
1172
+ const settings = {
1173
+ ...require_manager.readAssistantTtsSettings(raw),
1174
+ ...raw?.parameters === void 0 ? {} : { parameters: raw.parameters }
1175
+ };
1176
+ return {
1177
+ ok: true,
1178
+ value: {
1179
+ ...result.value,
1180
+ settings,
1181
+ voices: [...require_manager.MOSS_BUILTIN_VOICES, ...settings.voices]
1182
+ }
1183
+ };
1184
+ }
1185
+ if (endpoint === "assistant/voice/initialization" || endpoint === "assistant/voice/initialize") {
1186
+ const snapshot = result.value, settings = require_manager.readAssistantTtsSettings(this.snapshot.value.assistant.tts), voice = this.snapshot.value.assistant.voice;
1187
+ const model = findAssistantVoiceModel(voice.modelId ?? "");
1188
+ return {
1189
+ ok: true,
1190
+ value: {
1191
+ ...snapshot,
1192
+ modelId: voice.modelId || snapshot.modelId,
1193
+ modelLabel: model?.label ?? snapshot.modelLabel,
1194
+ ready: voice.initialized && snapshot.tts.status.ready && settings.backend === "moss-onnx",
1195
+ tts: {
1196
+ ...snapshot.tts,
1197
+ settings,
1198
+ voices: [...require_manager.MOSS_BUILTIN_VOICES, ...settings.voices]
1199
+ }
1200
+ }
1201
+ };
1202
+ }
1203
+ return result;
1204
+ }
1205
+ };
1206
+ function apply(value, operation) {
1207
+ const next = structuredClone(value), path = operation.path;
1208
+ let target = next;
1209
+ for (const key of path.slice(0, -1)) target = target[key] ??= {};
1210
+ if (operation.op === "unset") delete target[path.at(-1)];
1211
+ else target[path.at(-1)] = structuredClone(operation.value);
1212
+ return next;
1213
+ }
1214
+ //#endregion
1215
+ //#region src/shared/assistant-message-timeline.ts
1216
+ /** 按首次执行位置穿插工具,状态更新不会改变位置;不改写原始正文或播报内容。 */
1217
+ function assistantMessageTimeline(text, calls = []) {
1218
+ const position = (call) => Number.isSafeInteger(call.textOffset) && call.textOffset >= 0 ? Math.min(call.textOffset, text.length) : 0;
1219
+ const ordered = [...calls].sort((left, right) => position(left) - position(right));
1220
+ const parts = [];
1221
+ let offset = 0;
1222
+ let textKey = "text-start";
1223
+ for (const call of ordered) {
1224
+ const next = position(call);
1225
+ if (next > offset) parts.push({
1226
+ kind: "text",
1227
+ key: textKey,
1228
+ text: text.slice(offset, next)
1229
+ });
1230
+ parts.push({
1231
+ kind: "tool",
1232
+ key: `tool-${call.id}`,
1233
+ call
1234
+ });
1235
+ offset = next;
1236
+ textKey = `text-after-${call.id}`;
1237
+ }
1238
+ if (offset < text.length) parts.push({
1239
+ kind: "text",
1240
+ key: textKey,
1241
+ text: text.slice(offset)
1242
+ });
1243
+ return parts;
1244
+ }
1245
+ //#endregion
1246
+ //#region src/client/features/assistant-record-style.ts
1247
+ /** 工具记录和通话记录共享交互反馈,选择器只作用于这两类组件。 */
1248
+ function AssistantRecordStyle() {
1249
+ return (0, react.createElement)("style", null, recordCss);
1250
+ }
1251
+ function AssistantRecordChevron() {
1252
+ return (0, react.createElement)("svg", {
1253
+ className: "codingns-assistant-record-chevron",
1254
+ width: 14,
1255
+ height: 14,
1256
+ viewBox: "0 0 24 24",
1257
+ fill: "none",
1258
+ stroke: "currentColor",
1259
+ strokeWidth: 1.8,
1260
+ strokeLinecap: "round",
1261
+ strokeLinejoin: "round",
1262
+ "aria-hidden": true,
1263
+ style: {
1264
+ flexShrink: 0,
1265
+ color: require_theme.dshThemeColor.labelTertiary
1266
+ }
1267
+ }, (0, react.createElement)("path", { d: "m9 5 7 7-7 7" }));
1268
+ }
1269
+ const recordCss = `
1270
+ .codingns-assistant-tool-summary{list-style:none;cursor:pointer;border-radius:9px}
1271
+ .codingns-assistant-tool-summary::-webkit-details-marker{display:none}
1272
+ .codingns-assistant-tool-summary:hover,.codingns-assistant-record-button:hover{background:${require_theme.dshThemeColor.hoverBackground}!important}
1273
+ .codingns-assistant-tool-summary:active,.codingns-assistant-record-button:active{background:${require_theme.dshThemeColor.activeBackground}!important}
1274
+ .codingns-assistant-tool-summary:focus-visible,.codingns-assistant-record-button:focus-visible{outline:2px solid ${require_theme.dshThemeColor.accent};outline-offset:2px}
1275
+ .codingns-assistant-tool-details[open]>.codingns-assistant-tool-summary{border-radius:9px 9px 0 0}
1276
+ .codingns-assistant-tool-details[open] .codingns-assistant-tool-name{white-space:normal!important;overflow-wrap:anywhere}
1277
+ .codingns-assistant-tool-details[open]>.codingns-assistant-tool-summary .codingns-assistant-record-chevron{transform:rotate(90deg)}
1278
+ .codingns-assistant-voice-dialog[open]{display:flex;flex-direction:column}
1279
+ .codingns-assistant-voice-dialog::backdrop{background:${require_theme.dshThemeColor.overlay}}
1280
+ `;
1281
+ //#endregion
1282
+ //#region src/client/features/assistant-conversation-message.ts
1283
+ /** 聊天和语音详情共用消息样式与静态头像,方向只由所在视图指定。 */
1284
+ function AssistantConversationMessageView({ message, name, t, services, model, side, streaming = false }) {
1285
+ return (0, react.createElement)("article", {
1286
+ "data-codingns-assistant-message": message.role,
1287
+ ...side === void 0 ? {} : { "data-codingns-message-side": side },
1288
+ ...streaming ? { role: "status" } : {},
1289
+ style: {
1290
+ padding: "11px 14px",
1291
+ borderRadius: 10,
1292
+ background: message.role === "user" ? require_theme.dshThemeColor.surfaceSubtle : "transparent",
1293
+ justifySelf: side === "left" ? "start" : side === "right" || message.role === "user" ? "end" : "stretch",
1294
+ maxWidth: side === void 0 ? "100%" : "88%",
1295
+ minWidth: 0,
1296
+ boxSizing: "border-box",
1297
+ fontSize: 14,
1298
+ lineHeight: 1.65,
1299
+ whiteSpace: "pre-wrap",
1300
+ overflowWrap: "anywhere"
1301
+ }
1302
+ }, (0, react.createElement)("div", { style: {
1303
+ display: "flex",
1304
+ alignItems: "center",
1305
+ justifyContent: side === "right" ? "flex-end" : "flex-start",
1306
+ gap: 7,
1307
+ marginBottom: 5
1308
+ } }, message.role !== "assistant" || services === void 0 || model === void 0 ? null : (0, react.createElement)(require_manager.AssistantAvatarPortrait, {
1309
+ services,
1310
+ model
1311
+ }), (0, react.createElement)("small", { style: { color: require_theme.dshThemeColor.labelSecondary } }, message.role === "user" ? t("voice.dialog.user") : name, message.interrupted ? " · " + t("awb.call.interrupted") : "")), (0, react.createElement)(AssistantMessageContentView, {
1312
+ text: message.text,
1313
+ calls: message.role === "assistant" ? message.toolCalls : void 0,
1314
+ t
1315
+ }), message.attachments?.length ? (0, react.createElement)("div", {
1316
+ "data-codingns-assistant-message-attachments": true,
1317
+ style: {
1318
+ display: "flex",
1319
+ flexWrap: "wrap",
1320
+ gap: 6,
1321
+ marginTop: 8
1322
+ }
1323
+ }, ...message.attachments.map((item) => (0, react.createElement)("span", {
1324
+ key: item.attachment.attachmentId,
1325
+ title: item.attachment.name,
1326
+ style: {
1327
+ fontSize: 12,
1328
+ padding: "3px 8px",
1329
+ borderRadius: 8,
1330
+ border: `1px solid ${require_theme.dshThemeColor.border}`
1331
+ }
1332
+ }, item.attachment.name || t("awb.attachments.unnamed")))) : null);
1333
+ }
1334
+ /** 文字聊天、实时字幕与通话历史共用同一时间线,不额外生成工具汇总区。 */
1335
+ function AssistantMessageContentView({ text, calls, t }) {
1336
+ return (0, react.createElement)(react.Fragment, null, ...assistantMessageTimeline(text, calls).map((part) => part.kind === "tool" ? (0, react.createElement)(AssistantToolCallsView, {
1337
+ key: part.key,
1338
+ calls: [part.call],
1339
+ t
1340
+ }) : (0, react.createElement)("div", {
1341
+ key: part.key,
1342
+ "data-codingns-assistant-text": true,
1343
+ style: {
1344
+ whiteSpace: "pre-wrap",
1345
+ overflowWrap: "anywhere"
1346
+ }
1347
+ }, part.text)));
1348
+ }
1349
+ /** 工具信息单独展示,不拼入消息正文,字幕与语音合成都只使用原始 text。 */
1350
+ function AssistantToolCallsView({ calls, t }) {
1351
+ if (!calls?.length) return null;
1352
+ return (0, react.createElement)("div", {
1353
+ "data-codingns-assistant-tools": true,
1354
+ style: {
1355
+ display: "grid",
1356
+ gap: 6,
1357
+ marginBlock: 6,
1358
+ textAlign: "left",
1359
+ whiteSpace: "normal",
1360
+ minWidth: 0
1361
+ }
1362
+ }, (0, react.createElement)(AssistantRecordStyle), ...calls.map((call) => (0, react.createElement)(AssistantToolCallView, {
1363
+ key: call.id,
1364
+ call,
1365
+ t
1366
+ })));
1367
+ }
1368
+ /** 整个标题栏就是展开入口;实时状态更新不替换 details,保留用户的展开状态。 */
1369
+ function AssistantToolCallView({ call, t }) {
1370
+ const label = t(call.kind === "web-search" ? "awb.call.webSearch" : call.kind === "attachment" ? "awb.call.attachment" : "awb.call.workspace");
1371
+ const status = t(`awb.call.tool.${call.state}`);
1372
+ 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;
1373
+ const startedAt = new Date(call.startedAt);
1374
+ return (0, react.createElement)("article", {
1375
+ "data-codingns-assistant-tool": call.name,
1376
+ "data-state": call.state,
1377
+ style: {
1378
+ borderRadius: 10,
1379
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
1380
+ background: require_theme.dshThemeColor.surfaceSubtle,
1381
+ color: require_theme.dshThemeColor.labelPrimary,
1382
+ fontSize: 12,
1383
+ lineHeight: 1.4,
1384
+ minWidth: 0
1385
+ }
1386
+ }, (0, react.createElement)("details", { className: "codingns-assistant-tool-details" }, (0, react.createElement)("summary", {
1387
+ className: "codingns-assistant-tool-summary",
1388
+ title: t("awb.call.toolDetails"),
1389
+ "aria-label": `${label} · ${call.name} · ${status} · ${t("awb.call.toolDetails")}`,
1390
+ style: {
1391
+ display: "flex",
1392
+ alignItems: "center",
1393
+ gap: 8,
1394
+ padding: "8px 10px",
1395
+ minWidth: 0
1396
+ }
1397
+ }, (0, react.createElement)("span", { style: {
1398
+ display: "grid",
1399
+ placeItems: "center",
1400
+ flexShrink: 0,
1401
+ width: 28,
1402
+ height: 28,
1403
+ borderRadius: 8,
1404
+ color,
1405
+ background: `color-mix(in srgb, ${color} 9%, transparent)`
1406
+ } }, (0, react.createElement)(AssistantToolIcon, { kind: call.kind })), (0, react.createElement)("span", { style: {
1407
+ flex: "1 1 auto",
1408
+ minWidth: 0,
1409
+ display: "grid",
1410
+ gap: 1
1411
+ } }, (0, react.createElement)("strong", { style: {
1412
+ fontSize: 12,
1413
+ fontWeight: 600
1414
+ } }, label), (0, react.createElement)("span", {
1415
+ className: "codingns-assistant-tool-name",
1416
+ title: call.name,
1417
+ style: {
1418
+ color: require_theme.dshThemeColor.labelSecondary,
1419
+ fontFamily: require_theme.dshThemeColor.codeFont,
1420
+ fontSize: 11,
1421
+ overflow: "hidden",
1422
+ textOverflow: "ellipsis",
1423
+ whiteSpace: "nowrap"
1424
+ }
1425
+ }, call.name)), (0, react.createElement)("span", { style: {
1426
+ display: "grid",
1427
+ justifyItems: "end",
1428
+ gap: 3,
1429
+ flexShrink: 0
1430
+ } }, (0, react.createElement)("span", {
1431
+ role: "status",
1432
+ style: {
1433
+ display: "inline-flex",
1434
+ alignItems: "center",
1435
+ gap: 4,
1436
+ color,
1437
+ padding: "1px 6px",
1438
+ borderRadius: 5,
1439
+ background: `color-mix(in srgb, ${color} 9%, transparent)`,
1440
+ fontSize: 11,
1441
+ whiteSpace: "nowrap"
1442
+ }
1443
+ }, (0, react.createElement)("span", {
1444
+ "aria-hidden": true,
1445
+ style: {
1446
+ width: 4,
1447
+ height: 4,
1448
+ borderRadius: "50%",
1449
+ background: "currentColor"
1450
+ }
1451
+ }), status), (0, react.createElement)("time", {
1452
+ dateTime: Number.isNaN(startedAt.valueOf()) ? void 0 : startedAt.toISOString(),
1453
+ title: startedAt.toLocaleString(),
1454
+ style: {
1455
+ color: require_theme.dshThemeColor.labelTertiary,
1456
+ fontSize: 10,
1457
+ fontVariantNumeric: "tabular-nums",
1458
+ whiteSpace: "nowrap"
1459
+ }
1460
+ }, startedAt.toLocaleTimeString([], {
1461
+ hour: "2-digit",
1462
+ minute: "2-digit",
1463
+ second: "2-digit",
1464
+ hour12: false
1465
+ }))), (0, react.createElement)(AssistantRecordChevron)), (0, react.createElement)("div", { style: {
1466
+ display: "grid",
1467
+ gap: 8,
1468
+ padding: "9px 10px 10px",
1469
+ borderTop: `1px solid ${require_theme.dshThemeColor.border}`,
1470
+ minWidth: 0
1471
+ } }, ...[[t("awb.call.arguments"), call.arguments], [t("awb.call.result"), call.result]].map(([heading, value]) => (0, react.createElement)("div", {
1472
+ key: heading,
1473
+ style: { minWidth: 0 }
1474
+ }, (0, react.createElement)("small", { style: {
1475
+ color: require_theme.dshThemeColor.labelSecondary,
1476
+ fontSize: 11,
1477
+ fontWeight: 500
1478
+ } }, heading), (0, react.createElement)("pre", {
1479
+ tabIndex: 0,
1480
+ "aria-label": heading,
1481
+ style: {
1482
+ margin: "4px 0 0",
1483
+ padding: "6px 8px",
1484
+ borderRadius: 6,
1485
+ background: require_theme.dshThemeColor.cardBackground,
1486
+ maxHeight: 160,
1487
+ overflowY: "auto",
1488
+ overscrollBehavior: "contain",
1489
+ scrollbarWidth: "thin",
1490
+ whiteSpace: "pre-wrap",
1491
+ overflowWrap: "anywhere",
1492
+ fontFamily: require_theme.dshThemeColor.codeFont,
1493
+ fontSize: 11,
1494
+ lineHeight: 1.5
1495
+ }
1496
+ }, value || "—"))))));
1497
+ }
1498
+ function AssistantToolIcon({ kind }) {
1499
+ return (0, react.createElement)("svg", {
1500
+ width: 16,
1501
+ height: 16,
1502
+ viewBox: "0 0 24 24",
1503
+ fill: "none",
1504
+ stroke: "currentColor",
1505
+ strokeWidth: 1.7,
1506
+ strokeLinecap: "round",
1507
+ strokeLinejoin: "round",
1508
+ "aria-hidden": true
1509
+ }, (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" }));
1510
+ }
1511
+ //#endregion
1512
+ //#region src/client/features/assistant-realtime-call.ts
1513
+ /** 通话页只控制当前 Client 的采集和播放设备,不重新初始化模型或申请第二份租约。 */
1514
+ function AssistantRealtimeCall(props) {
1515
+ const adapter = require_manager.getGlobalVoiceAdapter(props.services);
1516
+ const [microphoneMuted, setMicrophoneMuted] = (0, react.useState)(adapter?.isMicrophoneMuted ?? false);
1517
+ const [speakerMuted, setSpeakerMuted] = (0, react.useState)(adapter?.isSpeakerMuted ?? false);
1518
+ const [speakersOpen, setSpeakersOpen] = (0, react.useState)(false);
1519
+ const [devices, setDevices] = (0, react.useState)([]);
1520
+ const [outputId, setOutputId] = (0, react.useState)(adapter?.outputDeviceId ?? "");
1521
+ const [devicePending, setDevicePending] = (0, react.useState)(false);
1522
+ const [error, setError] = (0, react.useState)("");
1523
+ const [now, setNow] = (0, react.useState)(Date.now);
1524
+ const startedAt = (0, react.useRef)(Date.now());
1525
+ const menuId = (0, react.useId)();
1526
+ (0, react.useEffect)(() => {
1527
+ if (props.minimized) return void 0;
1528
+ setNow(Date.now());
1529
+ const timer = setInterval(() => setNow(Date.now()), 1e3);
1530
+ return () => clearInterval(timer);
1531
+ }, [props.minimized]);
1532
+ (0, react.useEffect)(() => {
1533
+ if (!speakersOpen || !adapter?.outputDeviceSupported) return void 0;
1534
+ let active = true;
1535
+ adapter.enumerateOutputDevices().then((items) => {
1536
+ if (active) setDevices(items);
1537
+ }).catch((cause) => {
1538
+ if (active) setError(String(cause));
1539
+ });
1540
+ return () => {
1541
+ active = false;
1542
+ };
1543
+ }, [adapter, speakersOpen]);
1544
+ const duration = require_assistant_voice_sessions.voiceSessionDuration(props.startedAt === void 0 ? props.session ?? {
1545
+ startedAt: startedAt.current,
1546
+ endedAt: null
1547
+ } : {
1548
+ startedAt: props.startedAt,
1549
+ endedAt: null
1550
+ }, now);
1551
+ const menu = !speakersOpen ? null : (0, react.createElement)("div", {
1552
+ id: menuId,
1553
+ role: "group",
1554
+ "aria-label": props.t("awb.call.speakerSettings"),
1555
+ style: {
1556
+ display: "grid",
1557
+ gap: 12,
1558
+ position: "absolute",
1559
+ bottom: 86,
1560
+ left: "50%",
1561
+ transform: "translateX(-50%)",
1562
+ zIndex: 2,
1563
+ width: "min(300px, calc(100vw - 80px))",
1564
+ padding: 16,
1565
+ boxSizing: "border-box",
1566
+ borderRadius: 14,
1567
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
1568
+ background: require_theme.dshThemeColor.menuBackground,
1569
+ boxShadow: require_theme.dshThemeColor.prominentShadow
1570
+ },
1571
+ onKeyDown: (event) => {
1572
+ if (event.key === "Escape") {
1573
+ event.preventDefault();
1574
+ event.stopPropagation();
1575
+ setSpeakersOpen(false);
1576
+ }
1577
+ }
1578
+ }, (0, react.createElement)("label", { style: {
1579
+ display: "flex",
1580
+ alignItems: "center",
1581
+ gap: 9,
1582
+ fontSize: 13
1583
+ } }, (0, react.createElement)("input", {
1584
+ type: "checkbox",
1585
+ checked: !speakerMuted,
1586
+ disabled: adapter?.setSpeakerMuted === void 0,
1587
+ onChange: () => {
1588
+ const muted = !speakerMuted;
1589
+ adapter?.setSpeakerMuted?.(muted);
1590
+ setSpeakerMuted(muted);
1591
+ }
1592
+ }), props.t("awb.call.playSound")), (0, react.createElement)("label", { style: {
1593
+ display: "grid",
1594
+ gap: 6,
1595
+ fontSize: 12
1596
+ } }, props.t("awb.call.outputDevice"), (0, react.createElement)("select", {
1597
+ value: adapter?.outputDeviceSupported ? outputId : "",
1598
+ disabled: !adapter?.outputDeviceSupported || devicePending,
1599
+ style: {
1600
+ ...require_theme.dshSettingsFieldStyle,
1601
+ width: "100%",
1602
+ minWidth: 0
1603
+ },
1604
+ onChange: (event) => {
1605
+ const id = event.currentTarget.value;
1606
+ setDevicePending(true);
1607
+ setError("");
1608
+ adapter?.selectOutputDevice(id).then(() => setOutputId(id)).catch((cause) => setError(String(cause))).finally(() => setDevicePending(false));
1609
+ }
1610
+ }, (0, react.createElement)("option", { value: "" }, props.t("awb.call.defaultSpeaker")), adapter?.outputDeviceSupported && outputId && !devices.some((device) => device.deviceId === outputId) ? (0, react.createElement)("option", { value: outputId }, props.t("awb.call.speaker")) : null, ...devices.filter((device) => device.deviceId !== "").map((device) => (0, react.createElement)("option", {
1611
+ key: device.deviceId,
1612
+ value: device.deviceId
1613
+ }, device.label || props.t("awb.call.speaker"))))), error ? (0, react.createElement)("div", {
1614
+ role: "alert",
1615
+ style: {
1616
+ fontSize: 12,
1617
+ color: require_theme.dshThemeColor.error,
1618
+ overflowWrap: "anywhere"
1619
+ }
1620
+ }, error) : null);
1621
+ return (0, react.createElement)(AssistantRealtimeCallView, {
1622
+ ...props,
1623
+ duration,
1624
+ microphoneMuted,
1625
+ speakerMuted,
1626
+ microphoneAvailable: adapter?.setMicrophoneMuted !== void 0,
1627
+ avatar: props.minimized ? null : (0, react.createElement)(require_manager.AssistantAvatarSlot, {
1628
+ services: props.services,
1629
+ model: props.model,
1630
+ state: require_manager.resolveAssistantAvatarState(props.state, props.pending),
1631
+ surface: "dialog",
1632
+ size: 256,
1633
+ showDiagnostics: false
1634
+ }),
1635
+ onMicrophone: () => {
1636
+ const muted = !microphoneMuted;
1637
+ adapter?.setMicrophoneMuted?.(muted);
1638
+ setMicrophoneMuted(muted);
1639
+ },
1640
+ onSpeaker: () => setSpeakersOpen((open) => !open),
1641
+ speakersOpen,
1642
+ menuId,
1643
+ menu
1644
+ });
1645
+ }
1646
+ /** 纯展示层可独立渲染明暗主题与移动端,不需要真实音频设备。 */
1647
+ function AssistantRealtimeCallView({ name, t, state, pending, duration, minimized = false, microphoneMuted, speakerMuted, microphoneAvailable = true, userText, assistantText, toolCalls, avatar, onMicrophone, onSpeaker, onHangup, onMinimize, speakersOpen = false, menuId, menu }) {
1648
+ const captions = (0, react.useRef)(null);
1649
+ const followCaptions = (0, react.useRef)(true);
1650
+ const toolState = toolCalls?.map((call) => `${call.id}:${call.state}`).join("|");
1651
+ (0, react.useEffect)(() => {
1652
+ if (minimized) return;
1653
+ const element = captions.current;
1654
+ if (assistantText === "") followCaptions.current = true;
1655
+ if (element !== null && followCaptions.current) element.scrollTop = element.scrollHeight;
1656
+ }, [
1657
+ userText,
1658
+ assistantText,
1659
+ toolState,
1660
+ minimized
1661
+ ]);
1662
+ const status = pending ? state === "disabled" ? "ending" : "connecting" : microphoneMuted ? "muted" : state === "speaking" ? "speaking" : state === "thinking" ? "thinking" : "listening";
1663
+ const control = (kind, label, action, pressed) => (0, react.createElement)("div", { style: {
1664
+ display: "grid",
1665
+ justifyItems: "center",
1666
+ gap: 8
1667
+ } }, (0, react.createElement)("button", {
1668
+ type: "button",
1669
+ "aria-label": label,
1670
+ title: label,
1671
+ "aria-pressed": pressed,
1672
+ ...kind === "speaker" ? {
1673
+ "aria-expanded": speakersOpen,
1674
+ "aria-controls": menuId
1675
+ } : {},
1676
+ disabled: kind === "microphone" && (pending || !microphoneAvailable),
1677
+ onClick: action,
1678
+ style: {
1679
+ ...require_theme.dshSettingsButtonStyle,
1680
+ display: "grid",
1681
+ placeItems: "center",
1682
+ width: 58,
1683
+ height: 58,
1684
+ padding: 0,
1685
+ borderRadius: "50%",
1686
+ ...kind === "hangup" ? {
1687
+ background: "#e64949",
1688
+ color: "#fff",
1689
+ borderColor: "#e64949"
1690
+ } : pressed ? {
1691
+ background: require_theme.dshThemeColor.labelPrimary,
1692
+ color: require_theme.dshThemeColor.pageBackground
1693
+ } : {}
1694
+ }
1695
+ }, (0, react.createElement)(CallIcon, {
1696
+ kind,
1697
+ muted: pressed === true
1698
+ })), (0, react.createElement)("span", { style: {
1699
+ fontSize: 12,
1700
+ color: require_theme.dshThemeColor.labelSecondary
1701
+ } }, kind === "microphone" ? t(microphoneMuted ? "awb.call.unmute" : "awb.call.microphone") : t(`awb.call.${kind}`)));
1702
+ return (0, react.createElement)("div", {
1703
+ "data-codingns-realtime-call": true,
1704
+ style: {
1705
+ display: "flex",
1706
+ flexDirection: "column",
1707
+ flex: "1 1 auto",
1708
+ minHeight: 0,
1709
+ textAlign: "center",
1710
+ padding: 11,
1711
+ gap: 10
1712
+ }
1713
+ }, (0, react.createElement)("style", null, ".codingns-call-halo{position:absolute;inset:10%;border-radius:50%;background:radial-gradient(circle,color-mix(in srgb,var(--dsw-alias-button-info-fill,#1677ff) 14%,transparent),transparent 70%);animation:codingns-call-breathe 3s ease-in-out infinite}.codingns-call-caption{overflow-wrap:anywhere;white-space:pre-wrap}[data-codingns-call-avatar] img{height:auto!important;max-height:32dvh;object-fit:contain}[data-codingns-call-avatar] canvas,[data-codingns-call-avatar] video{max-width:100%;max-height:32dvh;object-fit:contain}@keyframes codingns-call-breathe{50%{transform:scale(1.12);opacity:.6}}@media(prefers-reduced-motion:reduce){.codingns-call-halo{animation:none}}"), (0, react.createElement)("div", { style: {
1714
+ position: "relative",
1715
+ paddingInline: onMinimize ? 44 : 0,
1716
+ minHeight: 40,
1717
+ display: "grid",
1718
+ gap: 4
1719
+ } }, (0, react.createElement)("strong", { style: {
1720
+ fontSize: 18,
1721
+ overflowWrap: "anywhere"
1722
+ } }, name), (0, react.createElement)("span", { style: {
1723
+ fontSize: 12,
1724
+ color: require_theme.dshThemeColor.labelTertiary,
1725
+ fontVariantNumeric: "tabular-nums"
1726
+ } }, t("awb.call.title"), " · ", duration), onMinimize ? (0, react.createElement)("button", {
1727
+ type: "button",
1728
+ "aria-label": t("awb.call.minimize"),
1729
+ title: t("awb.call.minimize"),
1730
+ onClick: onMinimize,
1731
+ style: {
1732
+ ...require_theme.dshSettingsButtonStyle,
1733
+ position: "absolute",
1734
+ right: 0,
1735
+ top: 0,
1736
+ width: 40,
1737
+ height: 40,
1738
+ display: "grid",
1739
+ placeItems: "center",
1740
+ padding: 0,
1741
+ borderRadius: 12
1742
+ }
1743
+ }, (0, react.createElement)("svg", {
1744
+ width: 22,
1745
+ height: 22,
1746
+ viewBox: "0 0 24 24",
1747
+ fill: "none",
1748
+ stroke: "currentColor",
1749
+ strokeWidth: 1.8,
1750
+ strokeLinecap: "round",
1751
+ "aria-hidden": true
1752
+ }, (0, react.createElement)("path", { d: "M5 15h14M9 6l3 3 3-3" }))) : null), (0, react.createElement)("div", { style: {
1753
+ display: "grid",
1754
+ placeItems: "center",
1755
+ flex: "1 1 auto",
1756
+ minHeight: "min(160px, 24dvh)",
1757
+ position: "relative"
1758
+ } }, (0, react.createElement)("div", {
1759
+ className: "codingns-call-halo",
1760
+ "aria-hidden": true
1761
+ }), (0, react.createElement)("div", {
1762
+ "data-codingns-call-avatar": true,
1763
+ style: {
1764
+ position: "relative",
1765
+ width: "min(256px, 32dvh, 100%)",
1766
+ maxWidth: "100%",
1767
+ maxHeight: "32dvh",
1768
+ overflow: "hidden",
1769
+ display: "grid",
1770
+ placeItems: "center"
1771
+ }
1772
+ }, avatar)), (0, react.createElement)("div", {
1773
+ role: "status",
1774
+ style: {
1775
+ fontSize: 13,
1776
+ color: require_theme.dshThemeColor.labelSecondary
1777
+ }
1778
+ }, t(`awb.call.${status}`)), (0, react.createElement)("div", {
1779
+ ref: captions,
1780
+ "data-codingns-call-captions": true,
1781
+ "aria-label": t("awb.call.caption"),
1782
+ tabIndex: 0,
1783
+ onScroll: (event) => {
1784
+ const element = event.currentTarget;
1785
+ followCaptions.current = element.scrollHeight - element.scrollTop - element.clientHeight <= 24;
1786
+ },
1787
+ style: {
1788
+ minHeight: 90,
1789
+ maxHeight: "min(280px, 35dvh)",
1790
+ overflowY: "auto",
1791
+ overscrollBehavior: "contain",
1792
+ scrollbarWidth: "thin",
1793
+ display: "flex",
1794
+ flexDirection: "column",
1795
+ gap: 8,
1796
+ flexShrink: 0
1797
+ }
1798
+ }, userText && !microphoneMuted ? (0, react.createElement)("p", {
1799
+ className: "codingns-call-caption",
1800
+ style: {
1801
+ margin: 0,
1802
+ flexShrink: 0,
1803
+ fontSize: 13,
1804
+ lineHeight: 1.6,
1805
+ color: require_theme.dshThemeColor.labelTertiary
1806
+ }
1807
+ }, userText) : null, (0, react.createElement)("div", {
1808
+ "aria-live": "polite",
1809
+ style: {
1810
+ flexShrink: 0,
1811
+ display: "grid",
1812
+ gap: 8,
1813
+ fontSize: 16,
1814
+ lineHeight: 1.65,
1815
+ color: assistantText ? require_theme.dshThemeColor.labelPrimary : require_theme.dshThemeColor.labelTertiary
1816
+ }
1817
+ }, assistantText || toolCalls?.length ? (0, react.createElement)(AssistantMessageContentView, {
1818
+ text: assistantText,
1819
+ calls: toolCalls,
1820
+ t
1821
+ }) : !userText ? t("awb.call.waiting") : "")), (0, react.createElement)("div", { style: {
1822
+ display: "flex",
1823
+ justifyContent: "center",
1824
+ gap: "clamp(20px, 6vw, 44px)",
1825
+ padding: "12px 0 4px",
1826
+ position: "relative"
1827
+ } }, menu, control("speaker", t("awb.call.speakerSettings"), onSpeaker, speakerMuted), control("microphone", t(microphoneMuted ? "awb.call.unmute" : "awb.call.microphone"), onMicrophone, microphoneMuted), control("hangup", t("awb.call.hangup"), () => {
1828
+ onHangup();
1829
+ })));
1830
+ }
1831
+ function CallIcon({ kind, muted = false, size = 24 }) {
1832
+ return (0, react.createElement)("svg", {
1833
+ width: size,
1834
+ height: size,
1835
+ viewBox: "0 0 24 24",
1836
+ fill: "none",
1837
+ stroke: "currentColor",
1838
+ strokeWidth: 1.6,
1839
+ strokeLinecap: "round",
1840
+ strokeLinejoin: "round",
1841
+ "aria-hidden": true
1842
+ }, (0, react.createElement)("path", { d: kind === "speaker" ? "M11 5 6 9H3v6h3l5 4V5Zm4 3a5 5 0 0 1 0 8m3-11a9 9 0 0 1 0 14" : kind === "microphone" ? "M9 5a3 3 0 0 1 6 0v6a3 3 0 0 1-6 0V5Zm-3 5v1a6 6 0 0 0 12 0v-1M12 17v4m-4 0h8" : "M3 14v3h4v-3l2-1a15 15 0 0 1 6 0l2 1v3h4v-3c-2-5-16-5-18 0Z" }), muted ? (0, react.createElement)("path", { d: "M3 3 21 21" }) : null);
1843
+ }
1844
+ function AssistantVoiceSessionCard({ session, t, name, services, model }) {
1845
+ const [open, setOpen] = (0, react.useState)(false);
1846
+ const startedAt = new Date(session.startedAt);
1847
+ return (0, react.createElement)("div", { "data-codingns-voice-session-card": session.id }, (0, react.createElement)(AssistantRecordStyle), (0, react.createElement)("button", {
1848
+ type: "button",
1849
+ className: "codingns-assistant-record-button",
1850
+ "aria-haspopup": "dialog",
1851
+ onClick: () => setOpen(true),
1852
+ style: {
1853
+ ...require_theme.dshSettingsButtonStyle,
1854
+ textAlign: "left",
1855
+ width: "100%",
1856
+ display: "flex",
1857
+ alignItems: "center",
1858
+ gap: 10,
1859
+ padding: "10px 12px",
1860
+ borderRadius: 10,
1861
+ background: require_theme.dshThemeColor.surfaceSubtle,
1862
+ minWidth: 0
1863
+ }
1864
+ }, (0, react.createElement)("span", { style: {
1865
+ display: "grid",
1866
+ placeItems: "center",
1867
+ width: 32,
1868
+ height: 32,
1869
+ borderRadius: 9,
1870
+ flexShrink: 0,
1871
+ background: `color-mix(in srgb, ${require_theme.dshThemeColor.accent} 9%, transparent)`,
1872
+ color: require_theme.dshThemeColor.accent
1873
+ } }, (0, react.createElement)(CallIcon, {
1874
+ kind: "speaker",
1875
+ size: 18
1876
+ })), (0, react.createElement)("span", { style: {
1877
+ flex: "1 1 auto",
1878
+ display: "grid",
1879
+ gap: 3,
1880
+ minWidth: 0
1881
+ } }, (0, react.createElement)("strong", { style: {
1882
+ fontSize: 13,
1883
+ fontWeight: 600
1884
+ } }, t("awb.call.record")), (0, react.createElement)("span", { style: {
1885
+ fontSize: 11,
1886
+ color: require_theme.dshThemeColor.labelSecondary,
1887
+ overflow: "hidden",
1888
+ textOverflow: "ellipsis",
1889
+ whiteSpace: "nowrap"
1890
+ } }, t("awb.call.count", { count: require_assistant_voice_sessions.voiceSessionMessageCount(session) }), " · ", (0, react.createElement)("time", {
1891
+ dateTime: Number.isNaN(startedAt.valueOf()) ? void 0 : startedAt.toISOString(),
1892
+ title: startedAt.toLocaleString()
1893
+ }, startedAt.toLocaleString([], {
1894
+ month: "2-digit",
1895
+ day: "2-digit",
1896
+ hour: "2-digit",
1897
+ minute: "2-digit",
1898
+ hour12: false
1899
+ })))), (0, react.createElement)("span", {
1900
+ title: t("awb.call.duration"),
1901
+ "aria-label": `${t("awb.call.duration")} ${require_assistant_voice_sessions.voiceSessionDuration(session)}`,
1902
+ style: {
1903
+ padding: "3px 7px",
1904
+ borderRadius: 6,
1905
+ background: require_theme.dshThemeColor.cardBackground,
1906
+ color: require_theme.dshThemeColor.labelSecondary,
1907
+ fontSize: 11,
1908
+ fontVariantNumeric: "tabular-nums",
1909
+ whiteSpace: "nowrap",
1910
+ flexShrink: 0
1911
+ }
1912
+ }, require_assistant_voice_sessions.voiceSessionDuration(session)), (0, react.createElement)(AssistantRecordChevron)), open ? (0, react.createElement)(VoiceSessionModal, {
1913
+ session,
1914
+ t,
1915
+ name,
1916
+ services,
1917
+ model,
1918
+ onClose: () => setOpen(false)
1919
+ }) : null);
1920
+ }
1921
+ function VoiceSessionModal({ onClose, ...props }) {
1922
+ const dialog = (0, react.useRef)(null);
1923
+ const titleId = (0, react.useId)();
1924
+ (0, react.useEffect)(() => {
1925
+ dialog.current?.showModal();
1926
+ return () => dialog.current?.close();
1927
+ }, []);
1928
+ return (0, react.createElement)("dialog", {
1929
+ ref: dialog,
1930
+ className: "codingns-assistant-voice-dialog",
1931
+ "aria-labelledby": titleId,
1932
+ onCancel: (event) => {
1933
+ event.preventDefault();
1934
+ event.stopPropagation();
1935
+ onClose();
1936
+ },
1937
+ onKeyDown: (event) => {
1938
+ if (event.key === "Escape") {
1939
+ event.preventDefault();
1940
+ event.stopPropagation();
1941
+ onClose();
1942
+ }
1943
+ },
1944
+ style: {
1945
+ width: "min(680px, calc(100vw - 24px))",
1946
+ maxHeight: "calc(100dvh - 24px)",
1947
+ boxSizing: "border-box",
1948
+ padding: 11,
1949
+ borderRadius: 12,
1950
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
1951
+ background: require_theme.dshThemeColor.pageBackground,
1952
+ color: require_theme.dshThemeColor.labelPrimary,
1953
+ boxShadow: require_theme.dshThemeColor.prominentShadow,
1954
+ overflow: "hidden"
1955
+ }
1956
+ }, (0, react.createElement)(AssistantRecordStyle), (0, react.createElement)("div", { style: {
1957
+ display: "flex",
1958
+ alignItems: "center",
1959
+ gap: 10,
1960
+ marginBottom: 12,
1961
+ flexShrink: 0
1962
+ } }, (0, react.createElement)("strong", {
1963
+ id: titleId,
1964
+ style: {
1965
+ flex: 1,
1966
+ fontSize: 14
1967
+ }
1968
+ }, props.t("awb.call.details")), (0, react.createElement)("button", {
1969
+ type: "button",
1970
+ className: "codingns-assistant-record-button",
1971
+ autoFocus: true,
1972
+ onClick: onClose,
1973
+ title: props.t("awb.close"),
1974
+ "aria-label": props.t("awb.close"),
1975
+ style: {
1976
+ ...require_theme.dshSettingsButtonStyle,
1977
+ width: 32,
1978
+ height: 32,
1979
+ minHeight: 32,
1980
+ padding: 0,
1981
+ border: 0,
1982
+ borderRadius: 8,
1983
+ display: "grid",
1984
+ placeItems: "center",
1985
+ flexShrink: 0,
1986
+ color: require_theme.dshThemeColor.labelSecondary
1987
+ }
1988
+ }, (0, react.createElement)("svg", {
1989
+ width: 16,
1990
+ height: 16,
1991
+ viewBox: "0 0 24 24",
1992
+ fill: "none",
1993
+ stroke: "currentColor",
1994
+ strokeWidth: 1.8,
1995
+ strokeLinecap: "round",
1996
+ "aria-hidden": true
1997
+ }, (0, react.createElement)("path", { d: "m6 6 12 12M6 18 18 6" })))), (0, react.createElement)("div", { style: {
1998
+ flex: "1 1 auto",
1999
+ minHeight: 0,
2000
+ overflowY: "auto",
2001
+ overscrollBehavior: "contain",
2002
+ scrollbarWidth: "thin"
2003
+ } }, (0, react.createElement)(AssistantVoiceSessionDetailsView, props)));
2004
+ }
2005
+ function AssistantVoiceSessionDetailsView({ session, t, name, services, model }) {
2006
+ const date = (value) => value === null ? "—" : new Date(value).toLocaleString();
2007
+ const statistic = {
2008
+ display: "inline-flex",
2009
+ alignItems: "center",
2010
+ padding: "2px 7px",
2011
+ borderRadius: 5,
2012
+ background: require_theme.dshThemeColor.cardBackground,
2013
+ color: require_theme.dshThemeColor.labelPrimary,
2014
+ fontSize: 11,
2015
+ fontVariantNumeric: "tabular-nums"
2016
+ };
2017
+ return (0, react.createElement)("div", { style: {
2018
+ display: "grid",
2019
+ gap: 8,
2020
+ minWidth: 0
2021
+ } }, (0, react.createElement)("div", { style: {
2022
+ padding: "10px 12px",
2023
+ borderRadius: 9,
2024
+ background: require_theme.dshThemeColor.surfaceSubtle,
2025
+ display: "grid",
2026
+ gap: 8,
2027
+ color: require_theme.dshThemeColor.labelSecondary,
2028
+ fontSize: 11,
2029
+ lineHeight: 1.5,
2030
+ minWidth: 0
2031
+ } }, (0, react.createElement)("div", { style: {
2032
+ display: "flex",
2033
+ flexWrap: "wrap",
2034
+ gap: 6
2035
+ } }, (0, react.createElement)("span", { style: statistic }, t("awb.call.count", { count: require_assistant_voice_sessions.voiceSessionMessageCount(session) })), (0, react.createElement)("span", { style: statistic }, t("awb.call.duration"), " ", require_assistant_voice_sessions.voiceSessionDuration(session))), (0, react.createElement)("div", { style: {
2036
+ display: "grid",
2037
+ gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 200px), 1fr))",
2038
+ gap: "4px 16px",
2039
+ minWidth: 0
2040
+ } }, ...[[t("awb.call.startedAt"), session.startedAt], [t("awb.call.endedAt"), session.endedAt]].map(([label, value]) => (0, react.createElement)("span", {
2041
+ key: label,
2042
+ style: { overflowWrap: "anywhere" }
2043
+ }, label, " ", (0, react.createElement)("span", { style: {
2044
+ color: require_theme.dshThemeColor.labelPrimary,
2045
+ fontVariantNumeric: "tabular-nums"
2046
+ } }, date(value)))))), session.messages.length === 0 ? (0, react.createElement)("p", { style: {
2047
+ margin: "8px 0",
2048
+ fontSize: 13,
2049
+ color: require_theme.dshThemeColor.labelSecondary
2050
+ } }, t("awb.call.noMessages")) : null, ...session.messages.map((message) => (0, react.createElement)(AssistantConversationMessageView, {
2051
+ key: message.id,
2052
+ message,
2053
+ name,
2054
+ t,
2055
+ services,
2056
+ model,
2057
+ side: message.role === "user" ? "right" : "left"
2058
+ })));
2059
+ }
2060
+ //#endregion
2061
+ //#region src/client/features/assistant-configuration-loader.ts
2062
+ const configuration = require_assistant_voice_sessions.createAssistantViewLoader(() => require.async("./client.assistant-configuration-view.js"));
2063
+ const assistantConfigurationFieldsLoader = require_assistant_voice_sessions.createAssistantViewLoader(async () => (await configuration.load()).AssistantConfigurationFields);
2064
+ const assistantConfigurationPageLoader = require_assistant_voice_sessions.createAssistantViewLoader(async () => (await configuration.load()).AssistantConfigurationPage);
2065
+ function AssistantConfigurationFields(props) {
2066
+ return (0, react.createElement)(require_assistant_voice_sessions.AssistantLoadedView, {
2067
+ loader: assistantConfigurationFieldsLoader,
2068
+ viewProps: props,
2069
+ t: props.t
2070
+ });
2071
+ }
2072
+ function AssistantConfigurationPage(props) {
2073
+ return (0, react.createElement)(require_assistant_voice_sessions.AssistantLoadedView, {
2074
+ loader: assistantConfigurationPageLoader,
2075
+ viewProps: props,
2076
+ t: props.t
2077
+ });
2078
+ }
2079
+ //#endregion
2080
+ //#region src/client/features/assistant-workbench.ts
2081
+ const debugLoader = require_assistant_voice_sessions.createAssistantViewLoader(async () => (await require.async("./client.assistant-debug-workbench.js")).AssistantDebugDialog);
2082
+ const emptyConversation = () => ({
2083
+ revision: 0,
2084
+ summary: "",
2085
+ messages: [],
2086
+ pendingMessage: null,
2087
+ active: null,
2088
+ compressing: false,
2089
+ error: null
2090
+ });
2091
+ function readAssistantDraft(settings, mossReady) {
2092
+ const tts = require_manager.readAssistantTtsSettings(settings.tts);
2093
+ const profile = require_manager.readAssistantProfile(settings);
2094
+ const appearance = require_manager.normalizeAssistantAppearance(settings.appearance);
2095
+ const avatarId = require_manager.listAssistantAvatars(appearance, profile.initialized).some((model) => model.id === appearance.selectedId) ? appearance.selectedId : require_manager.BUILTIN_ASSISTANT_AVATAR.id;
2096
+ return {
2097
+ name: profile.name,
2098
+ personality: require_manager.readAssistantPersonality(profile),
2099
+ modelKey: settings.model === void 0 ? "" : JSON.stringify([settings.model.provider, settings.model.model]),
2100
+ managedWorkspaceIds: [...settings.managedWorkspaceIds],
2101
+ avatarId,
2102
+ voiceId: tts.selectedId,
2103
+ ttsBackend: !profile.initialized || mossReady === false ? "browser" : tts.backend
2104
+ };
2105
+ }
2106
+ function assistantDraftPayload(draft, initializing = false, includeVoice = true) {
2107
+ const model = draft.modelKey === "" ? null : JSON.parse(draft.modelKey);
2108
+ return {
2109
+ name: draft.name,
2110
+ model: model === null ? null : {
2111
+ provider: model[0],
2112
+ model: model[1]
2113
+ },
2114
+ personality: draft.personality,
2115
+ avatarId: draft.avatarId,
2116
+ ...initializing ? {} : {
2117
+ managedWorkspaceIds: draft.managedWorkspaceIds,
2118
+ ...includeVoice ? {
2119
+ voiceId: draft.voiceId,
2120
+ ttsBackend: draft.ttsBackend
2121
+ } : {}
2122
+ }
2123
+ };
2124
+ }
2125
+ /** 一位助理、一份连续对话;配置草稿、资源管理和正式聊天各自有明确的写入边界。 */
2126
+ function AssistantWorkbench(props) {
2127
+ const { services } = props;
2128
+ const t = require_transport.useCodingNsTranslator(services.locale);
2129
+ const [settings, setSettings] = (0, react.useState)(() => services.settings.getSnapshot());
2130
+ const value = settings.value?.assistant ?? require_native_workspace_store.DEFAULT_ASSISTANT_SETTINGS;
2131
+ const scope = assistantDisplayScope(value.managedWorkspaceIds);
2132
+ const display = (0, react.useMemo)(() => getAssistantDisplayStore(services.rpc, scope), [services.rpc, scope]);
2133
+ const [lifecycle, setLifecycle] = (0, react.useState)(() => ({
2134
+ profile: require_manager.readAssistantProfile(value),
2135
+ conversation: emptyConversation()
2136
+ }));
2137
+ const [loaded, setLoaded] = (0, react.useState)(false);
2138
+ const [view, setView] = (0, react.useState)(() => props.initialConfiguration || !lifecycle.profile.initialized ? "configuration" : "chat");
2139
+ const [configuration] = (0, react.useState)(() => new AssistantConfigurationSession(services));
2140
+ const configurationSnapshot = (0, react.useSyncExternalStore)(configuration.subscribe, configuration.getSnapshot, configuration.getSnapshot);
2141
+ const configurationValue = configurationSnapshot.value?.assistant ?? require_native_workspace_store.DEFAULT_ASSISTANT_SETTINGS;
2142
+ const draft = {
2143
+ ...readAssistantDraft(configurationValue),
2144
+ name: configurationValue.profile?.name ?? require_manager.readAssistantProfile(configurationValue).name,
2145
+ personality: configurationValue.profile?.personality ?? require_manager.readAssistantPersonality(require_manager.readAssistantProfile(configurationValue))
2146
+ };
2147
+ const [catalog, setCatalog] = (0, react.useState)();
2148
+ const [tts, setTts] = (0, react.useState)();
2149
+ const [workspaces, setWorkspaces] = (0, react.useState)([]);
2150
+ const [indexState, setIndexState] = (0, react.useState)();
2151
+ const [text, setText] = (0, react.useState)("");
2152
+ const [files, setFiles] = (0, react.useState)([]);
2153
+ const [busy, setBusy] = (0, react.useState)(false);
2154
+ const [error, setError] = (0, react.useState)("");
2155
+ const [notice, setNotice] = (0, react.useState)("");
2156
+ const [confirmation, setConfirmation] = (0, react.useState)();
2157
+ const [configurationTab, setConfigurationTab] = (0, react.useState)("basic");
2158
+ const [visitedTabs, setVisitedTabs] = (0, react.useState)(["basic"]);
2159
+ const [debugDialog, setDebugDialog] = (0, react.useState)(false);
2160
+ const [catalogPreview, setCatalogPreview] = (0, react.useState)();
2161
+ const [previewTarget, setPreviewTarget] = (0, react.useState)(null);
2162
+ const mounted = (0, react.useRef)(false);
2163
+ const abort = (0, react.useRef)();
2164
+ const revision = (0, react.useRef)(0);
2165
+ const ownedRequests = (0, react.useRef)(/* @__PURE__ */ new Set());
2166
+ const pendingOperation = (0, react.useRef)(false);
2167
+ const voiceProps = (0, react.useRef)(props);
2168
+ voiceProps.current = props;
2169
+ const messagesEnd = (0, react.useRef)(null);
2170
+ const configuring = view === "configuration" || !lifecycle.profile.initialized;
2171
+ const initializing = !lifecycle.profile.initialized;
2172
+ const calling = !configuring && (props.active || props.pending);
2173
+ const running = lifecycle.conversation.active?.state === "running";
2174
+ const locked = busy || lifecycle.conversation.compressing || props.pending;
2175
+ const writable = settings.writable && settings.status === "ready";
2176
+ const call = (0, react.useCallback)(async (endpoint, payload = {}, signal = abort.current?.signal) => {
2177
+ const result = await services.rpc.call(require_transport.CODINGNS_RPC_CHANNEL, endpoint, payload, signal);
2178
+ if (!result.ok) throw new Error(result.error.message);
2179
+ return result.value;
2180
+ }, [services.rpc]);
2181
+ const refresh = (0, react.useCallback)(async () => {
2182
+ const current = revision.current;
2183
+ const next = await display.lifecycle.refresh({ afterPending: true });
2184
+ if (next && mounted.current && current === revision.current && !pendingOperation.current) {
2185
+ setLifecycle(next);
2186
+ setLoaded(true);
2187
+ }
2188
+ return next;
2189
+ }, [display]);
2190
+ const cancelRequests = (0, react.useCallback)(() => {
2191
+ for (const requestId of ownedRequests.current) services.rpc.call(require_transport.CODINGNS_RPC_CHANNEL, "assistant/conversation/cancel", { requestId }).catch(() => void 0);
2192
+ ownedRequests.current.clear();
2193
+ }, [services.rpc]);
2194
+ const close = (0, react.useCallback)(() => {
2195
+ cancelRequests();
2196
+ Promise.resolve(voiceProps.current.onStop()).finally(() => voiceProps.current.onClose());
2197
+ }, [cancelRequests]);
2198
+ (0, react.useEffect)(() => {
2199
+ mounted.current = true;
2200
+ abort.current = new AbortController();
2201
+ return () => {
2202
+ mounted.current = false;
2203
+ abort.current?.abort();
2204
+ cancelRequests();
2205
+ };
2206
+ }, [cancelRequests]);
2207
+ (0, react.useEffect)(() => {
2208
+ if (props.minimized) return;
2209
+ const changed = () => {
2210
+ const snapshot = display.lifecycle.getSnapshot();
2211
+ if (snapshot.error) setError(message(snapshot.error));
2212
+ else if (snapshot.value && !pendingOperation.current) {
2213
+ setLifecycle(snapshot.value);
2214
+ setLoaded(true);
2215
+ }
2216
+ };
2217
+ changed();
2218
+ return display.lifecycle.subscribe(changed);
2219
+ }, [display, props.minimized]);
2220
+ (0, react.useEffect)(() => {
2221
+ const changed = () => {
2222
+ const next = services.settings.getSnapshot();
2223
+ setSettings(next);
2224
+ configuration.sync();
2225
+ };
2226
+ changed();
2227
+ const dispose = services.settings.subscribe(changed);
2228
+ call("assistant/chat/models").then((next) => {
2229
+ if (mounted.current) setCatalog(next);
2230
+ }).catch((cause) => {
2231
+ if (mounted.current) setError(message(cause));
2232
+ });
2233
+ return dispose;
2234
+ }, [
2235
+ services.settings,
2236
+ call,
2237
+ configuration
2238
+ ]);
2239
+ (0, react.useEffect)(() => () => configuration.dispose(), [configuration]);
2240
+ (0, react.useEffect)(() => {
2241
+ if (initializing) return void 0;
2242
+ let stopped = false;
2243
+ const refreshResources = () => {
2244
+ call("assistant/tts/catalog").then((next) => {
2245
+ if (stopped || !mounted.current) return;
2246
+ setTts(next);
2247
+ }).catch(() => {});
2248
+ };
2249
+ const nativeChanged = () => setWorkspaces(require_native_workspace_store.readNativeWorkspaceSnapshot(services.uiContext)?.items ?? []);
2250
+ refreshResources();
2251
+ nativeChanged();
2252
+ const dispose = services.settings.subscribe(refreshResources);
2253
+ const disposeNative = require_native_workspace_store.readNativeWorkspaceListStore(services.uiContext)?.subscribe(nativeChanged);
2254
+ const changed = () => {
2255
+ const snapshot = display.status.getSnapshot();
2256
+ const next = snapshot.value;
2257
+ if (next && !snapshot.error) {
2258
+ setIndexState(next.indexState);
2259
+ setWorkspaces(mergeWorkspaces(require_native_workspace_store.readNativeWorkspaceSnapshot(services.uiContext)?.items ?? [], next));
2260
+ } else setIndexState(void 0);
2261
+ };
2262
+ changed();
2263
+ const disposeStatus = props.minimized ? void 0 : display.status.subscribe(changed);
2264
+ return () => {
2265
+ stopped = true;
2266
+ disposeStatus?.();
2267
+ dispose();
2268
+ disposeNative?.();
2269
+ };
2270
+ }, [
2271
+ initializing,
2272
+ services.settings,
2273
+ services.uiContext,
2274
+ call,
2275
+ display,
2276
+ props.minimized
2277
+ ]);
2278
+ (0, react.useEffect)(() => {
2279
+ if (props.initialConfiguration) {
2280
+ setView("configuration");
2281
+ Promise.resolve(voiceProps.current.onStop());
2282
+ }
2283
+ }, [props.initialConfiguration]);
2284
+ (0, react.useEffect)(() => {
2285
+ if (props.minimized || debugDialog || confirmation !== void 0) return void 0;
2286
+ const escape = (event) => handleAssistantWorkbenchEscape(event, close);
2287
+ document.addEventListener("keydown", escape);
2288
+ return () => document.removeEventListener("keydown", escape);
2289
+ }, [
2290
+ props.minimized,
2291
+ debugDialog,
2292
+ confirmation,
2293
+ close
2294
+ ]);
2295
+ const toolState = lifecycle.conversation.active?.toolCalls?.map((call) => `${call.id}:${call.state}`).join("|");
2296
+ (0, react.useEffect)(() => {
2297
+ messagesEnd.current?.scrollIntoView?.({ block: "nearest" });
2298
+ }, [
2299
+ lifecycle.conversation.revision,
2300
+ lifecycle.conversation.active?.text,
2301
+ toolState,
2302
+ props.partialText
2303
+ ]);
2304
+ const run = async (operation) => {
2305
+ if (pendingOperation.current) return;
2306
+ pendingOperation.current = true;
2307
+ revision.current++;
2308
+ setBusy(true);
2309
+ setError("");
2310
+ setNotice("");
2311
+ try {
2312
+ await operation();
2313
+ } catch (cause) {
2314
+ if (mounted.current) setError(message(cause));
2315
+ } finally {
2316
+ pendingOperation.current = false;
2317
+ revision.current++;
2318
+ if (mounted.current) {
2319
+ setBusy(false);
2320
+ refresh().catch(() => void 0);
2321
+ }
2322
+ }
2323
+ };
2324
+ const reloadSettings = async () => {
2325
+ await (services.settings.reload?.() ?? services.settings.load?.());
2326
+ if (mounted.current) setSettings(services.settings.getSnapshot());
2327
+ };
2328
+ const changeDraft = (patch) => {
2329
+ const current = configuration.getSnapshot().value?.assistant ?? require_native_workspace_store.DEFAULT_ASSISTANT_SETTINGS;
2330
+ const operations = [];
2331
+ if (patch.name !== void 0 || patch.personality !== void 0) operations.push({
2332
+ op: "set",
2333
+ path: ["assistant", "profile"],
2334
+ value: {
2335
+ ...require_manager.readAssistantProfile(current),
2336
+ ...patch.name === void 0 ? {} : { name: patch.name },
2337
+ ...patch.personality === void 0 ? {} : { personality: patch.personality }
2338
+ }
2339
+ });
2340
+ if (patch.modelKey !== void 0) operations.push(patch.modelKey === "" ? {
2341
+ op: "unset",
2342
+ path: ["assistant", "model"]
2343
+ } : {
2344
+ op: "set",
2345
+ path: ["assistant", "model"],
2346
+ value: {
2347
+ provider: JSON.parse(patch.modelKey)[0],
2348
+ model: JSON.parse(patch.modelKey)[1]
2349
+ }
2350
+ });
2351
+ if (patch.managedWorkspaceIds !== void 0) operations.push({
2352
+ op: "set",
2353
+ path: ["assistant", "managedWorkspaceIds"],
2354
+ value: [...patch.managedWorkspaceIds]
2355
+ });
2356
+ if (patch.avatarId !== void 0) operations.push({
2357
+ op: "set",
2358
+ path: [
2359
+ "assistant",
2360
+ "appearance",
2361
+ "selectedId"
2362
+ ],
2363
+ value: patch.avatarId
2364
+ });
2365
+ if (patch.voiceId !== void 0 || patch.ttsBackend !== void 0) operations.push({
2366
+ op: "set",
2367
+ path: ["assistant", "tts"],
2368
+ value: {
2369
+ ...require_manager.readAssistantTtsSettings(current.tts),
2370
+ ...patch.voiceId === void 0 ? {} : { selectedId: patch.voiceId },
2371
+ ...patch.ttsBackend === void 0 ? {} : { backend: patch.ttsBackend }
2372
+ }
2373
+ });
2374
+ configuration.mutate(operations).catch((cause) => setError(message(cause)));
2375
+ };
2376
+ const resetConfigurationTabs = () => {
2377
+ setConfigurationTab("basic");
2378
+ setVisitedTabs(["basic"]);
2379
+ };
2380
+ const chooseConfigurationTab = (tab) => {
2381
+ if (tab === configurationTab) return;
2382
+ setConfirmation(void 0);
2383
+ setVisitedTabs((current) => current.includes(tab) ? current : [...current, tab]);
2384
+ setConfigurationTab(tab);
2385
+ };
2386
+ const addFiles = (added) => {
2387
+ try {
2388
+ const next = [...files, ...added];
2389
+ validateAssistantFiles(next);
2390
+ setFiles(next);
2391
+ setError("");
2392
+ } catch (cause) {
2393
+ setError(t(message(cause)));
2394
+ }
2395
+ };
2396
+ const requestClear = () => {
2397
+ setError("");
2398
+ setConfirmation("clear");
2399
+ };
2400
+ const send = async () => {
2401
+ const input = text.trim();
2402
+ if (!input && files.length === 0 || locked || !loaded || running) return;
2403
+ if (readAssistantComposerCommand(input) === "clear") {
2404
+ if (files.length > 0) {
2405
+ setError(t("awb.command.attachmentsError"));
2406
+ return;
2407
+ }
2408
+ requestClear();
2409
+ return;
2410
+ }
2411
+ await run(async () => {
2412
+ await voiceProps.current.onStop();
2413
+ const attachments = await encodeAssistantFiles(files);
2414
+ if (!mounted.current) return;
2415
+ const requestId = requestKey();
2416
+ ownedRequests.current.add(requestId);
2417
+ await call("assistant/conversation/start", {
2418
+ requestId,
2419
+ text: input || t("awb.attachments.prompt"),
2420
+ ...attachments.length === 0 ? {} : { attachments }
2421
+ });
2422
+ if (!mounted.current || !ownedRequests.current.has(requestId)) {
2423
+ services.rpc.call(require_transport.CODINGNS_RPC_CHANNEL, "assistant/conversation/cancel", { requestId });
2424
+ return;
2425
+ }
2426
+ setText("");
2427
+ setFiles([]);
2428
+ await refresh();
2429
+ });
2430
+ };
2431
+ (0, react.useEffect)(() => {
2432
+ const active = lifecycle.conversation.active;
2433
+ if (active !== null && active.state !== "running") ownedRequests.current.delete(active.requestId);
2434
+ }, [lifecycle.conversation.active?.requestId, lifecycle.conversation.active?.state]);
2435
+ (0, react.useEffect)(() => {
2436
+ if (!props.minimized && !props.active && !props.pending) display.lifecycle.refresh();
2437
+ }, [
2438
+ props.active,
2439
+ props.pending,
2440
+ props.minimized,
2441
+ display
2442
+ ]);
2443
+ const save = () => {
2444
+ run(async () => {
2445
+ cancelRequests();
2446
+ await voiceProps.current.onStop();
2447
+ const savingDraft = readAssistantDraft(configuration.getSnapshot().value.assistant);
2448
+ const next = await call("assistant/lifecycle/configure", {
2449
+ ...assistantDraftPayload(savingDraft, initializing),
2450
+ configurationPatch: configuration.configurationPatch()
2451
+ });
2452
+ await configuration.commitLocal(abort.current.signal);
2453
+ if (!mounted.current) return;
2454
+ setLifecycle(next);
2455
+ await reloadSettings();
2456
+ configuration.reset();
2457
+ setView("chat");
2458
+ resetConfigurationTabs();
2459
+ });
2460
+ };
2461
+ const maintain = (action) => {
2462
+ run(async () => {
2463
+ cancelRequests();
2464
+ await voiceProps.current.onStop();
2465
+ const next = await call(action === "reset" ? "assistant/lifecycle/reset" : `assistant/conversation/${action}`);
2466
+ if (!mounted.current) return;
2467
+ setLifecycle(next);
2468
+ setConfirmation(void 0);
2469
+ setText("");
2470
+ setFiles([]);
2471
+ if (action === "reset") {
2472
+ resetConfigurationTabs();
2473
+ setView("configuration");
2474
+ configuration.reset();
2475
+ } else setNotice(t("awb.cleared"));
2476
+ await reloadSettings();
2477
+ if (action === "reset") configuration.reset();
2478
+ });
2479
+ };
2480
+ const stopReply = () => {
2481
+ run(async () => {
2482
+ await voiceProps.current.onStop();
2483
+ const requestId = lifecycle.conversation.active?.requestId;
2484
+ if (requestId !== void 0) await call("assistant/conversation/cancel", { requestId });
2485
+ cancelRequests();
2486
+ await refresh();
2487
+ });
2488
+ };
2489
+ const switchView = () => {
2490
+ cancelRequests();
2491
+ Promise.resolve(voiceProps.current.onStop());
2492
+ configuration.reset();
2493
+ setConfirmation(void 0);
2494
+ setView(configuring ? "chat" : "configuration");
2495
+ resetConfigurationTabs();
2496
+ };
2497
+ const appearance = require_manager.normalizeAssistantAppearance(value.appearance);
2498
+ const previewAppearance = {
2499
+ ...require_manager.normalizeAssistantAppearance(configurationValue.appearance),
2500
+ selectedId: draft.avatarId
2501
+ };
2502
+ const avatarState = require_manager.resolveAssistantAvatarState(running ? "thinking" : props.state, props.pending);
2503
+ const button = (label, onClick, disabled = locked, primary = false) => (0, react.createElement)("button", {
2504
+ type: "button",
2505
+ disabled,
2506
+ onClick,
2507
+ style: primary ? require_theme.dshSettingsPrimaryButtonStyle : require_theme.dshSettingsButtonStyle
2508
+ }, label);
2509
+ const noticeText = error || lifecycle.conversation.error || (lifecycle.conversation.active?.state === "failed" ? lifecycle.conversation.active.error : null) || props.message;
2510
+ const status = resolveAssistantWorkbenchStatus({
2511
+ running,
2512
+ working: locked,
2513
+ voiceActive: props.active,
2514
+ voiceState: props.state,
2515
+ hasProjects: value.managedWorkspaceIds.length > 0,
2516
+ indexState
2517
+ });
2518
+ const configurationPreview = (0, react.createElement)("div", {
2519
+ "data-codingns-assistant-preview": true,
2520
+ style: {
2521
+ flex: "1 1 290px",
2522
+ minWidth: 0,
2523
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
2524
+ borderRadius: 18,
2525
+ background: require_theme.dshThemeColor.surfaceSubtle,
2526
+ padding: 11,
2527
+ boxSizing: "border-box",
2528
+ display: "grid",
2529
+ alignContent: "start",
2530
+ gap: 11
2531
+ }
2532
+ }, (0, react.createElement)("div", {
2533
+ ref: setPreviewTarget,
2534
+ "data-codingns-avatar-preview-region": true
2535
+ }, catalogPreview === void 0 ? (0, react.createElement)("div", { style: avatarStyle }, (0, react.createElement)(require_manager.AssistantAvatarSlot, {
2536
+ services: configuration.services,
2537
+ model: require_manager.selectedAssistantAvatar(previewAppearance),
2538
+ state: avatarState,
2539
+ surface: "dialog",
2540
+ size: 144,
2541
+ showDiagnostics: false
2542
+ }), (0, react.createElement)("strong", { style: {
2543
+ fontSize: 18,
2544
+ overflowWrap: "anywhere",
2545
+ textAlign: "center"
2546
+ } }, draft.name.trim() || t("awb.unnamed")), (0, react.createElement)("p", { style: {
2547
+ ...help,
2548
+ textAlign: "center",
2549
+ display: "-webkit-box",
2550
+ WebkitLineClamp: 2,
2551
+ WebkitBoxOrient: "vertical",
2552
+ overflow: "hidden",
2553
+ overflowWrap: "anywhere"
2554
+ } }, draft.personality.trim() || t("awb.personalityPreview"))) : null));
2555
+ const page = (tab) => (0, react.createElement)(AssistantConfigurationPage, {
2556
+ tab,
2557
+ active: tab === configurationTab,
2558
+ services: configuration.services,
2559
+ value: configurationValue,
2560
+ draft,
2561
+ catalog,
2562
+ appearance: previewAppearance,
2563
+ tts,
2564
+ workspaces,
2565
+ t,
2566
+ disabled: locked || !writable || !loaded,
2567
+ onChange: changeDraft,
2568
+ onDebug: () => setDebugDialog(true),
2569
+ onError: setError,
2570
+ onReset: () => {
2571
+ setError("");
2572
+ setConfirmation("reset-first");
2573
+ },
2574
+ previewTarget,
2575
+ onPreviewChange: setCatalogPreview
2576
+ });
2577
+ return (0, react.createElement)("div", {
2578
+ "data-codingns-workbench-minimized": props.minimized === true,
2579
+ hidden: props.minimized,
2580
+ style: {
2581
+ ...backdrop,
2582
+ ...props.minimized ? { display: "none" } : {}
2583
+ }
2584
+ }, configuring ? (0, react.createElement)(AssistantControlsStyle) : null, (0, react.createElement)("section", {
2585
+ role: "dialog",
2586
+ "aria-modal": !props.minimized,
2587
+ "aria-label": t("awb.title"),
2588
+ "data-codingns-assistant-workbench": configuring ? "configuration" : "chat",
2589
+ style: {
2590
+ ...dialog,
2591
+ ...!configuring ? {
2592
+ height: "min(740px, calc(100dvh - 24px))",
2593
+ minHeight: 0
2594
+ } : {}
2595
+ }
2596
+ }, calling ? null : (0, react.createElement)("header", { style: {
2597
+ ...row,
2598
+ flexShrink: 0,
2599
+ padding: 11,
2600
+ borderBottom: `1px solid ${require_theme.dshThemeColor.border}`
2601
+ } }, (0, react.createElement)("div", { style: {
2602
+ flex: "1 1 auto",
2603
+ minWidth: 0,
2604
+ display: "grid",
2605
+ gap: 5
2606
+ } }, (0, react.createElement)("div", {
2607
+ "data-codingns-assistant-heading": true,
2608
+ style: {
2609
+ ...row,
2610
+ gap: 8
2611
+ }
2612
+ }, (0, react.createElement)("strong", { style: {
2613
+ fontSize: 17,
2614
+ overflowWrap: "anywhere"
2615
+ } }, initializing ? t("awb.setupTitle") : lifecycle.profile.name), initializing ? null : (0, react.createElement)(AssistantStatusBadge, {
2616
+ status,
2617
+ t
2618
+ })), initializing ? (0, react.createElement)("span", { style: help }, t("awb.setupSubtitle")) : null), !configuring && lifecycle.profile.initialized ? (0, react.createElement)(AssistantIconButton, {
2619
+ icon: "clear",
2620
+ label: t("awb.clear"),
2621
+ onClick: requestClear,
2622
+ disabled: locked || !loaded
2623
+ }) : null, lifecycle.profile.initialized ? (0, react.createElement)(AssistantIconButton, {
2624
+ icon: configuring ? "chat" : "settings",
2625
+ label: t(configuring ? "awb.chat" : "awb.configure"),
2626
+ onClick: switchView,
2627
+ disabled: locked
2628
+ }) : null, (0, react.createElement)(AssistantIconButton, {
2629
+ icon: "close",
2630
+ label: t("awb.close"),
2631
+ onClick: close,
2632
+ disabled: busy
2633
+ })), (0, react.createElement)("div", {
2634
+ "data-codingns-assistant-scroll": true,
2635
+ style: {
2636
+ flex: "1 1 auto",
2637
+ padding: calling ? 0 : 11,
2638
+ overflowY: "auto",
2639
+ overscrollBehavior: "contain",
2640
+ minHeight: 0,
2641
+ display: "flex",
2642
+ flexDirection: "column",
2643
+ gap: configuring ? 11 : 16
2644
+ }
2645
+ }, !loaded ? (0, react.createElement)("div", {
2646
+ role: "status",
2647
+ style: require_theme.dshSettingsHelpStyle
2648
+ }, t("awb.loading"), " ", button(t("awb.retry"), () => {
2649
+ refresh().catch((cause) => setError(message(cause)));
2650
+ })) : null, noticeText ? (0, react.createElement)("div", {
2651
+ role: "alert",
2652
+ style: {
2653
+ color: require_theme.dshThemeColor.error,
2654
+ fontSize: 13,
2655
+ overflowWrap: "anywhere"
2656
+ }
2657
+ }, noticeText) : null, notice ? (0, react.createElement)("div", {
2658
+ role: "status",
2659
+ style: require_theme.dshSettingsHelpStyle
2660
+ }, notice) : null, calling ? (0, react.createElement)(AssistantRealtimeCall, {
2661
+ services,
2662
+ name: lifecycle.profile.name,
2663
+ model: require_manager.selectedAssistantAvatar(appearance),
2664
+ t,
2665
+ minimized: props.minimized,
2666
+ startedAt: props.callStartedAt,
2667
+ onMinimize: props.onMinimize,
2668
+ session: lifecycle.conversation.voiceSessions?.find((session) => session.endedAt === null),
2669
+ pending: props.pending,
2670
+ state: props.state,
2671
+ userText: props.partialText || props.liveUserText || "",
2672
+ assistantText: props.liveAssistantText || "",
2673
+ toolCalls: lifecycle.conversation.pendingMessage?.source === "voice" ? lifecycle.conversation.active?.toolCalls : lifecycle.conversation.voiceSessions?.find((session) => session.endedAt === null)?.messages.at(-1)?.toolCalls,
2674
+ onHangup: async () => {
2675
+ try {
2676
+ await props.onStop();
2677
+ await refresh();
2678
+ } catch (cause) {
2679
+ setError(message(cause));
2680
+ }
2681
+ }
2682
+ }) : configuring ? (0, react.createElement)("div", {
2683
+ "data-codingns-assistant-configuration": true,
2684
+ style: {
2685
+ ...assistantSettingTextStyle,
2686
+ display: "grid",
2687
+ gap: 11,
2688
+ minWidth: 0
2689
+ }
2690
+ }, initializing ? (0, react.createElement)("div", { style: {
2691
+ display: "flex",
2692
+ flexWrap: "wrap",
2693
+ alignItems: "stretch",
2694
+ gap: 22
2695
+ } }, (0, react.createElement)("div", { style: {
2696
+ flex: "1 1 330px",
2697
+ minWidth: 0
2698
+ } }, (0, react.createElement)(AssistantConfigurationFields, {
2699
+ draft,
2700
+ catalog,
2701
+ appearance: previewAppearance,
2702
+ initializing,
2703
+ services: configuration.services,
2704
+ t,
2705
+ disabled: locked || !writable || !loaded,
2706
+ onChange: changeDraft
2707
+ })), configurationPreview) : (0, react.createElement)(AssistantConfigurationTabs, {
2708
+ active: configurationTab,
2709
+ visited: visitedTabs,
2710
+ t,
2711
+ onChange: chooseConfigurationTab,
2712
+ panels: {
2713
+ basic: page("basic"),
2714
+ appearance: page("appearance"),
2715
+ voice: page("voice"),
2716
+ more: page("more")
2717
+ },
2718
+ preview: configurationPreview
2719
+ })) : (0, react.createElement)("div", {
2720
+ "data-codingns-assistant-chat": true,
2721
+ style: {
2722
+ display: "grid",
2723
+ gap: 16
2724
+ }
2725
+ }, !appearance.dialogEnabled ? null : (0, react.createElement)("div", { style: avatarStyle }, (0, react.createElement)(require_manager.AssistantAvatarSlot, {
2726
+ services,
2727
+ model: require_manager.selectedAssistantAvatar(appearance),
2728
+ state: avatarState,
2729
+ surface: "dialog",
2730
+ size: 120
2731
+ })), (0, react.createElement)(AssistantConversationView, {
2732
+ conversation: lifecycle.conversation,
2733
+ name: lifecycle.profile.name,
2734
+ t,
2735
+ services,
2736
+ model: require_manager.selectedAssistantAvatar(appearance)
2737
+ }), props.partialText ? (0, react.createElement)("div", {
2738
+ role: "status",
2739
+ style: help
2740
+ }, props.partialText) : null, (0, react.createElement)("div", { ref: messagesEnd }))), calling ? null : configuring ? (0, react.createElement)("footer", {
2741
+ "data-codingns-assistant-configuration-footer": true,
2742
+ style: {
2743
+ ...row,
2744
+ flexShrink: 0,
2745
+ justifyContent: initializing ? "space-between" : "flex-end",
2746
+ padding: 11,
2747
+ borderTop: `1px solid ${require_theme.dshThemeColor.border}`,
2748
+ background: require_theme.dshThemeColor.pageBackground
2749
+ }
2750
+ }, initializing ? (0, react.createElement)("p", { style: {
2751
+ ...help,
2752
+ flex: "1 1 220px"
2753
+ } }, t("awb.setupLater")) : null, (0, react.createElement)("div", { style: row }, initializing ? null : button(t("awb.cancel"), switchView), button(t(initializing ? "awb.createStart" : "awb.save"), save, locked || configurationSnapshot.preparing || !loaded || !writable || !draft.name.trim() || catalog === void 0 || catalog.models.length === 0 || draft.modelKey === "" && catalog.default === null, true))) : (0, react.createElement)("footer", {
2754
+ "data-codingns-assistant-composer-dock": true,
2755
+ style: {
2756
+ flexShrink: 0,
2757
+ padding: 11,
2758
+ borderTop: `1px solid ${require_theme.dshThemeColor.border}`,
2759
+ background: require_theme.dshThemeColor.pageBackground
2760
+ }
2761
+ }, (0, react.createElement)(AssistantComposer, {
2762
+ t,
2763
+ value: text,
2764
+ disabled: locked || running || !loaded,
2765
+ onChange: setText,
2766
+ onSend: () => {
2767
+ send();
2768
+ },
2769
+ files,
2770
+ onFiles: addFiles,
2771
+ onRemoveFile: (index) => setFiles((current) => current.filter((_, position) => position !== index)),
2772
+ running,
2773
+ stopping: busy,
2774
+ onStop: stopReply,
2775
+ voiceActive: props.active,
2776
+ voiceDisabled: busy || lifecycle.conversation.compressing || running || !loaded || !props.active && (props.pending || !props.realtimeAvailable),
2777
+ onVoice: () => {
2778
+ cancelRequests();
2779
+ if (props.active) props.onStop();
2780
+ else props.onStart();
2781
+ }
2782
+ }))), confirmation === void 0 ? null : (0, react.createElement)(AssistantMaintenanceDialog, {
2783
+ key: confirmation,
2784
+ stage: confirmation,
2785
+ t,
2786
+ disabled: locked || !loaded || confirmation !== "clear" && !writable,
2787
+ error,
2788
+ onCancel: () => setConfirmation(void 0),
2789
+ onConfirm: () => {
2790
+ if (!locked && loaded && (confirmation === "clear" || writable)) confirmAssistantMaintenance(confirmation, setConfirmation, maintain);
2791
+ }
2792
+ }), debugDialog ? (0, react.createElement)(require_assistant_voice_sessions.AssistantLoadedView, {
2793
+ loader: debugLoader,
2794
+ t,
2795
+ overlay: true,
2796
+ onClose: () => setDebugDialog(false),
2797
+ viewProps: {
2798
+ services: configuring ? configuration.services : services,
2799
+ displayRpc: services.rpc,
2800
+ onClose: () => setDebugDialog(false)
2801
+ }
2802
+ }) : null);
2803
+ }
2804
+ /** 正式消息以 Host 快照为准,语音识别与流式回复在同一消息流里显示。 */
2805
+ function AssistantConversationView({ conversation, name, t, services, model }) {
2806
+ const messages = [...conversation.messages, ...conversation.pendingMessage === null ? [] : [conversation.pendingMessage]];
2807
+ const timeline = require_assistant_voice_sessions.assistantConversationTimeline(conversation);
2808
+ const active = conversation.active;
2809
+ const streaming = active !== null && active.state !== "cancelled" && (active.text !== "" || Boolean(active.toolCalls?.length)) && !messages.some((message) => message.id === `${active.requestId}-assistant`);
2810
+ return (0, react.createElement)("div", {
2811
+ "data-codingns-assistant-messages": true,
2812
+ style: {
2813
+ display: "grid",
2814
+ gap: 12
2815
+ }
2816
+ }, conversation.summary ? (0, react.createElement)("details", null, (0, react.createElement)("summary", { style: help }, t("awb.summary")), (0, react.createElement)("p", { style: {
2817
+ ...help,
2818
+ whiteSpace: "pre-wrap"
2819
+ } }, conversation.summary)) : null, timeline.length === 0 && !streaming ? (0, react.createElement)("p", { style: {
2820
+ ...help,
2821
+ textAlign: "center",
2822
+ padding: "20px 0"
2823
+ } }, t("awb.empty")) : null, ...timeline.map((item) => {
2824
+ if (item.kind === "voice") return (0, react.createElement)(AssistantVoiceSessionCard, {
2825
+ key: item.session.id,
2826
+ session: item.session,
2827
+ t,
2828
+ name,
2829
+ services,
2830
+ model
2831
+ });
2832
+ const message = item.message;
2833
+ return (0, react.createElement)(AssistantConversationMessageView, {
2834
+ key: message.id,
2835
+ message,
2836
+ name,
2837
+ t,
2838
+ services,
2839
+ model
2840
+ });
2841
+ }), !streaming ? null : (0, react.createElement)(AssistantConversationMessageView, {
2842
+ key: `${active.requestId}-assistant`,
2843
+ streaming: true,
2844
+ message: {
2845
+ role: "assistant",
2846
+ text: active.text,
2847
+ ...active.toolCalls === void 0 ? {} : { toolCalls: active.toolCalls }
2848
+ },
2849
+ name,
2850
+ t,
2851
+ services,
2852
+ model
2853
+ }));
2854
+ }
2855
+ /** 内层菜单先消费 Escape,外层窗口只处理尚未消费的关闭按键。 */
2856
+ function handleAssistantWorkbenchEscape(event, onClose) {
2857
+ if (event.key !== "Escape" || event.defaultPrevented) return;
2858
+ event.stopImmediatePropagation();
2859
+ onClose();
2860
+ }
2861
+ function mergeWorkspaces(native, debug) {
2862
+ return [...native, ...debug.workspaces.filter((workspace) => !native.some((item) => item.workspaceId === workspace.workspaceId)).map((workspace) => ({
2863
+ workspaceId: workspace.workspaceId,
2864
+ title: workspace.name,
2865
+ sessionIds: []
2866
+ }))];
2867
+ }
2868
+ function message(error) {
2869
+ return error instanceof Error ? error.message : String(error);
2870
+ }
2871
+ function requestKey() {
2872
+ return `assistant-${globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}`;
2873
+ }
2874
+ const row = {
2875
+ display: "flex",
2876
+ alignItems: "center",
2877
+ flexWrap: "wrap",
2878
+ gap: 8
2879
+ };
2880
+ const help = {
2881
+ ...require_theme.dshSettingsHelpStyle,
2882
+ margin: 0
2883
+ };
2884
+ const avatarStyle = {
2885
+ display: "grid",
2886
+ gap: 8,
2887
+ justifyItems: "center"
2888
+ };
2889
+ const backdrop = {
2890
+ position: "fixed",
2891
+ inset: 0,
2892
+ zIndex: 1e4,
2893
+ background: require_theme.dshThemeColor.overlay,
2894
+ backdropFilter: "blur(6px)",
2895
+ display: "flex",
2896
+ alignItems: "center",
2897
+ justifyContent: "center",
2898
+ padding: 12,
2899
+ boxSizing: "border-box"
2900
+ };
2901
+ const dialog = {
2902
+ display: "flex",
2903
+ flexDirection: "column",
2904
+ width: "min(940px, 100%)",
2905
+ maxHeight: "calc(100dvh - 24px)",
2906
+ minHeight: 300,
2907
+ background: require_theme.dshThemeColor.pageBackground,
2908
+ color: require_theme.dshThemeColor.labelPrimary,
2909
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
2910
+ borderRadius: 20,
2911
+ boxShadow: "0 24px 80px rgba(0,0,0,.24)",
2912
+ overflow: "hidden"
2913
+ };
2914
+ //#endregion
2915
+ exports.ASSISTANT_VOICE_MODEL_CATALOG = ASSISTANT_VOICE_MODEL_CATALOG;
2916
+ exports.AssistantWorkbench = AssistantWorkbench;
2917
+ exports.DEFAULT_VOICE_MODEL_ID = DEFAULT_VOICE_MODEL_ID;
2918
+ exports.assistantDisplayScope = assistantDisplayScope;
2919
+ exports.assistantSettingCheckboxStyle = assistantSettingCheckboxStyle;
2920
+ exports.assistantSettingFieldStyle = assistantSettingFieldStyle;
2921
+ exports.assistantSettingSwitchStyle = assistantSettingSwitchStyle;
2922
+ exports.assistantSettingTextStyle = assistantSettingTextStyle;
2923
+ exports.findAssistantVoiceModel = findAssistantVoiceModel;
2924
+ exports.getAssistantDisplayStore = getAssistantDisplayStore;
2925
+ return module.exports;
2926
+ }
2927
+ });
2928
+
2929
+ //# sourceMappingURL=client.assistant-workbench.js.map