@jingyi0605/codingns4dsh 0.2.1-beta.4 → 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 (459) hide show
  1. package/README.en.md +134 -159
  2. package/README.md +128 -261
  3. package/assets/provider-icons/doubao.png +0 -0
  4. package/data/build/dist/client/account-bar.d.ts +1 -1
  5. package/data/build/dist/client/account-bar.d.ts.map +1 -1
  6. package/data/build/dist/client/account-bar.js +22 -14
  7. package/data/build/dist/client/account-bar.js.map +1 -1
  8. package/data/build/dist/client/adapter-catalog-watch.d.ts +8 -0
  9. package/data/build/dist/client/adapter-catalog-watch.d.ts.map +1 -0
  10. package/data/build/dist/client/adapter-catalog-watch.js +79 -0
  11. package/data/build/dist/client/adapter-catalog-watch.js.map +1 -0
  12. package/data/build/dist/client/avatar/catalog-panel.d.ts +1 -1
  13. package/data/build/dist/client/avatar/catalog-panel.d.ts.map +1 -1
  14. package/data/build/dist/client/avatar/catalog-panel.js +13 -11
  15. package/data/build/dist/client/avatar/catalog-panel.js.map +1 -1
  16. package/data/build/dist/client/avatar/engine.d.ts.map +1 -1
  17. package/data/build/dist/client/avatar/engine.js +26 -7
  18. package/data/build/dist/client/avatar/engine.js.map +1 -1
  19. package/data/build/dist/client/avatar/live2d.d.ts +2 -0
  20. package/data/build/dist/client/avatar/live2d.d.ts.map +1 -1
  21. package/data/build/dist/client/avatar/live2d.js +16 -6
  22. package/data/build/dist/client/avatar/live2d.js.map +1 -1
  23. package/data/build/dist/client/avatar/manager.d.ts.map +1 -1
  24. package/data/build/dist/client/avatar/manager.js +8 -2
  25. package/data/build/dist/client/avatar/manager.js.map +1 -1
  26. package/data/build/dist/client/avatar/slot.d.ts.map +1 -1
  27. package/data/build/dist/client/avatar/slot.js +4 -1
  28. package/data/build/dist/client/avatar/slot.js.map +1 -1
  29. package/data/build/dist/client/bundle.js +48459 -98385
  30. package/data/build/dist/client/bundle.js.map +1 -1
  31. package/data/build/dist/client/cli-catalog.d.ts +5 -0
  32. package/data/build/dist/client/cli-catalog.d.ts.map +1 -1
  33. package/data/build/dist/client/cli-catalog.js +15 -0
  34. package/data/build/dist/client/cli-catalog.js.map +1 -1
  35. package/data/build/dist/client/cli-slots.d.ts.map +1 -1
  36. package/data/build/dist/client/cli-slots.js +7 -45
  37. package/data/build/dist/client/cli-slots.js.map +1 -1
  38. package/data/build/dist/client/client.assistant-configuration-view.js +4526 -0
  39. package/data/build/dist/client/client.assistant-configuration-view.js.map +1 -0
  40. package/data/build/dist/client/client.assistant-debug-workbench.js +764 -0
  41. package/data/build/dist/client/client.assistant-debug-workbench.js.map +1 -0
  42. package/data/build/dist/client/client.assistant-prompt-editor.js +76 -0
  43. package/data/build/dist/client/client.assistant-prompt-editor.js.map +1 -0
  44. package/data/build/dist/client/client.assistant-prompts.js +44 -0
  45. package/data/build/dist/client/client.assistant-prompts.js.map +1 -0
  46. package/data/build/dist/client/client.assistant-voice-sessions.js +144 -0
  47. package/data/build/dist/client/client.assistant-voice-sessions.js.map +1 -0
  48. package/data/build/dist/client/client.assistant-workbench.js +2929 -0
  49. package/data/build/dist/client/client.assistant-workbench.js.map +1 -0
  50. package/data/build/dist/client/client.delegate-command.js +1315 -0
  51. package/data/build/dist/client/client.delegate-command.js.map +1 -0
  52. package/data/build/dist/client/client.editor-engine.js +32486 -0
  53. package/data/build/dist/client/client.editor-engine.js.map +1 -0
  54. package/data/build/dist/client/client.manager.js +3454 -0
  55. package/data/build/dist/client/client.manager.js.map +1 -0
  56. package/data/build/dist/client/client.model-catalog-cache.js +543 -0
  57. package/data/build/dist/client/client.model-catalog-cache.js.map +1 -0
  58. package/data/build/dist/client/client.native-workspace-store.js +689 -0
  59. package/data/build/dist/client/client.native-workspace-store.js.map +1 -0
  60. package/data/build/dist/client/client.primitives-adapter.js +333 -0
  61. package/data/build/dist/client/client.primitives-adapter.js.map +1 -0
  62. package/data/build/dist/client/client.react-dom.js +7286 -0
  63. package/data/build/dist/client/client.react-dom.js.map +1 -0
  64. package/data/build/dist/client/client.rolldown-runtime.js +59 -0
  65. package/data/build/dist/client/client.theme.js +386 -0
  66. package/data/build/dist/client/client.theme.js.map +1 -0
  67. package/data/build/dist/client/client.transport.js +3323 -0
  68. package/data/build/dist/client/client.transport.js.map +1 -0
  69. package/data/build/dist/client/client.xterm-engine.js +13064 -0
  70. package/data/build/dist/client/client.xterm-engine.js.map +1 -0
  71. package/data/build/dist/client/debug/ui.d.ts.map +1 -1
  72. package/data/build/dist/client/debug/ui.js +14 -5
  73. package/data/build/dist/client/debug/ui.js.map +1 -1
  74. package/data/build/dist/client/desktop-assistant.js +19 -5
  75. package/data/build/dist/client/editor-engine.d.ts +4 -0
  76. package/data/build/dist/client/editor-engine.d.ts.map +1 -0
  77. package/data/build/dist/client/editor-engine.js +50 -0
  78. package/data/build/dist/client/editor-engine.js.map +1 -0
  79. package/data/build/dist/client/engine-loader.d.ts +5 -0
  80. package/data/build/dist/client/engine-loader.d.ts.map +1 -0
  81. package/data/build/dist/client/engine-loader.js +35 -0
  82. package/data/build/dist/client/engine-loader.js.map +1 -0
  83. package/data/build/dist/client/features/assistant-configuration-loader.d.ts +14 -0
  84. package/data/build/dist/client/features/assistant-configuration-loader.d.ts.map +1 -0
  85. package/data/build/dist/client/features/assistant-configuration-loader.js +13 -0
  86. package/data/build/dist/client/features/assistant-configuration-loader.js.map +1 -0
  87. package/data/build/dist/client/features/assistant-configuration-session.d.ts.map +1 -1
  88. package/data/build/dist/client/features/assistant-configuration-session.js +13 -4
  89. package/data/build/dist/client/features/assistant-configuration-session.js.map +1 -1
  90. package/data/build/dist/client/features/assistant-configuration-view.d.ts +66 -0
  91. package/data/build/dist/client/features/assistant-configuration-view.d.ts.map +1 -0
  92. package/data/build/dist/client/features/assistant-configuration-view.js +133 -0
  93. package/data/build/dist/client/features/assistant-configuration-view.js.map +1 -0
  94. package/data/build/dist/client/features/assistant-debug-workbench.d.ts +4 -11
  95. package/data/build/dist/client/features/assistant-debug-workbench.d.ts.map +1 -1
  96. package/data/build/dist/client/features/assistant-debug-workbench.js +41 -68
  97. package/data/build/dist/client/features/assistant-debug-workbench.js.map +1 -1
  98. package/data/build/dist/client/features/assistant-display-store.d.ts +57 -0
  99. package/data/build/dist/client/features/assistant-display-store.d.ts.map +1 -0
  100. package/data/build/dist/client/features/assistant-display-store.js +189 -0
  101. package/data/build/dist/client/features/assistant-display-store.js.map +1 -0
  102. package/data/build/dist/client/features/assistant-prompt-editor.d.ts +12 -0
  103. package/data/build/dist/client/features/assistant-prompt-editor.d.ts.map +1 -0
  104. package/data/build/dist/client/features/assistant-prompt-editor.js +14 -0
  105. package/data/build/dist/client/features/assistant-prompt-editor.js.map +1 -0
  106. package/data/build/dist/client/features/assistant-view-loader.d.ts +17 -0
  107. package/data/build/dist/client/features/assistant-view-loader.d.ts.map +1 -0
  108. package/data/build/dist/client/features/assistant-view-loader.js +49 -0
  109. package/data/build/dist/client/features/assistant-view-loader.js.map +1 -0
  110. package/data/build/dist/client/features/assistant-workbench.d.ts +1 -60
  111. package/data/build/dist/client/features/assistant-workbench.d.ts.map +1 -1
  112. package/data/build/dist/client/features/assistant-workbench.js +41 -169
  113. package/data/build/dist/client/features/assistant-workbench.js.map +1 -1
  114. package/data/build/dist/client/features/cli-adapters.d.ts.map +1 -1
  115. package/data/build/dist/client/features/cli-adapters.js +44 -13
  116. package/data/build/dist/client/features/cli-adapters.js.map +1 -1
  117. package/data/build/dist/client/features/global-voice-assistant.d.ts.map +1 -1
  118. package/data/build/dist/client/features/global-voice-assistant.js +33 -27
  119. package/data/build/dist/client/features/global-voice-assistant.js.map +1 -1
  120. package/data/build/dist/client/features/peer-host.d.ts +2 -1
  121. package/data/build/dist/client/features/peer-host.d.ts.map +1 -1
  122. package/data/build/dist/client/features/peer-host.js +124 -58
  123. package/data/build/dist/client/features/peer-host.js.map +1 -1
  124. package/data/build/dist/client/features/workspace-session-enhancement.d.ts.map +1 -1
  125. package/data/build/dist/client/features/workspace-session-enhancement.js +37 -10
  126. package/data/build/dist/client/features/workspace-session-enhancement.js.map +1 -1
  127. package/data/build/dist/client/file-management-dom.d.ts +3 -1
  128. package/data/build/dist/client/file-management-dom.d.ts.map +1 -1
  129. package/data/build/dist/client/file-management-dom.js +85 -79
  130. package/data/build/dist/client/file-management-dom.js.map +1 -1
  131. package/data/build/dist/client/git-management.d.ts +1 -1
  132. package/data/build/dist/client/git-management.d.ts.map +1 -1
  133. package/data/build/dist/client/git-management.js +46 -27
  134. package/data/build/dist/client/git-management.js.map +1 -1
  135. package/data/build/dist/client/locale.d.ts.map +1 -1
  136. package/data/build/dist/client/locale.js +10 -0
  137. package/data/build/dist/client/locale.js.map +1 -1
  138. package/data/build/dist/client/locales/assistantAvatar.d.ts +1 -1
  139. package/data/build/dist/client/locales/assistantAvatar.js +2 -2
  140. package/data/build/dist/client/locales/assistantAvatar.js.map +1 -1
  141. package/data/build/dist/client/locales/fileWorkspace.d.ts.map +1 -1
  142. package/data/build/dist/client/locales/fileWorkspace.js +2 -0
  143. package/data/build/dist/client/locales/fileWorkspace.js.map +1 -1
  144. package/data/build/dist/client/locales/peerHost.d.ts.map +1 -1
  145. package/data/build/dist/client/locales/peerHost.js +4 -0
  146. package/data/build/dist/client/locales/peerHost.js.map +1 -1
  147. package/data/build/dist/client/locales/peerHostWorkspace.js +2 -2
  148. package/data/build/dist/client/locales/peerHostWorkspace.js.map +1 -1
  149. package/data/build/dist/client/locales/usage.d.ts.map +1 -1
  150. package/data/build/dist/client/locales/usage.js +4 -0
  151. package/data/build/dist/client/locales/usage.js.map +1 -1
  152. package/data/build/dist/client/model-catalog-cache.d.ts.map +1 -1
  153. package/data/build/dist/client/model-catalog-cache.js +9 -2
  154. package/data/build/dist/client/model-catalog-cache.js.map +1 -1
  155. package/data/build/dist/client/peer-host-management-api.d.ts +2 -2
  156. package/data/build/dist/client/peer-host-management-api.d.ts.map +1 -1
  157. package/data/build/dist/client/peer-host-management-api.js +8 -5
  158. package/data/build/dist/client/peer-host-management-api.js.map +1 -1
  159. package/data/build/dist/client/provider-icon-assets.js +5 -39
  160. package/data/build/dist/client/provider-icon-assets.js.map +1 -1
  161. package/data/build/dist/client/provider-icon-bridge.d.ts +6 -0
  162. package/data/build/dist/client/provider-icon-bridge.d.ts.map +1 -0
  163. package/data/build/dist/client/provider-icon-bridge.js +65 -0
  164. package/data/build/dist/client/provider-icon-bridge.js.map +1 -0
  165. package/data/build/dist/client/provider-icons.d.ts +1 -0
  166. package/data/build/dist/client/provider-icons.d.ts.map +1 -1
  167. package/data/build/dist/client/provider-icons.js +2 -1
  168. package/data/build/dist/client/provider-icons.js.map +1 -1
  169. package/data/build/dist/client/remote-web-context.d.ts +2 -0
  170. package/data/build/dist/client/remote-web-context.d.ts.map +1 -1
  171. package/data/build/dist/client/remote-web-context.js +50 -2
  172. package/data/build/dist/client/remote-web-context.js.map +1 -1
  173. package/data/build/dist/client/rpc-call.d.ts +4 -0
  174. package/data/build/dist/client/rpc-call.d.ts.map +1 -0
  175. package/data/build/dist/client/rpc-call.js +23 -0
  176. package/data/build/dist/client/rpc-call.js.map +1 -0
  177. package/data/build/dist/client/serial-polling.d.ts +15 -0
  178. package/data/build/dist/client/serial-polling.d.ts.map +1 -0
  179. package/data/build/dist/client/serial-polling.js +48 -0
  180. package/data/build/dist/client/serial-polling.js.map +1 -0
  181. package/data/build/dist/client/session-adapter-cache.d.ts.map +1 -1
  182. package/data/build/dist/client/session-adapter-cache.js +11 -1
  183. package/data/build/dist/client/session-adapter-cache.js.map +1 -1
  184. package/data/build/dist/client/settings-section.js +8 -2
  185. package/data/build/dist/client/settings-section.js.map +1 -1
  186. package/data/build/dist/client/sherpa-voice-adapter.d.ts.map +1 -1
  187. package/data/build/dist/client/sherpa-voice-adapter.js +8 -8
  188. package/data/build/dist/client/sherpa-voice-adapter.js.map +1 -1
  189. package/data/build/dist/client/subscription-slot.d.ts +11 -0
  190. package/data/build/dist/client/subscription-slot.d.ts.map +1 -1
  191. package/data/build/dist/client/subscription-slot.js +42 -15
  192. package/data/build/dist/client/subscription-slot.js.map +1 -1
  193. package/data/build/dist/client/terminal/xterm-engine.d.ts +4 -0
  194. package/data/build/dist/client/terminal/xterm-engine.d.ts.map +1 -0
  195. package/data/build/dist/client/terminal/xterm-engine.js +5 -0
  196. package/data/build/dist/client/terminal/xterm-engine.js.map +1 -0
  197. package/data/build/dist/client/terminal/xterm-view.d.ts.map +1 -1
  198. package/data/build/dist/client/terminal/xterm-view.js +33 -25
  199. package/data/build/dist/client/terminal/xterm-view.js.map +1 -1
  200. package/data/build/dist/host/avatar/temporary-previews.d.ts +3 -1
  201. package/data/build/dist/host/avatar/temporary-previews.d.ts.map +1 -1
  202. package/data/build/dist/host/avatar/temporary-previews.js +7 -2
  203. package/data/build/dist/host/avatar/temporary-previews.js.map +1 -1
  204. package/data/build/dist/host/cli-adapters/acp-cli-driver.d.ts +1 -1
  205. package/data/build/dist/host/cli-adapters/acp-cli-driver.d.ts.map +1 -1
  206. package/data/build/dist/host/cli-adapters/acp-cli-driver.js +3 -10
  207. package/data/build/dist/host/cli-adapters/acp-cli-driver.js.map +1 -1
  208. package/data/build/dist/host/cli-adapters/antigravity-driver.d.ts.map +1 -1
  209. package/data/build/dist/host/cli-adapters/antigravity-driver.js +2 -2
  210. package/data/build/dist/host/cli-adapters/antigravity-driver.js.map +1 -1
  211. package/data/build/dist/host/cli-adapters/antigravity-subscription.d.ts.map +1 -1
  212. package/data/build/dist/host/cli-adapters/antigravity-subscription.js +10 -10
  213. package/data/build/dist/host/cli-adapters/antigravity-subscription.js.map +1 -1
  214. package/data/build/dist/host/cli-adapters/binary-detection.d.ts +19 -0
  215. package/data/build/dist/host/cli-adapters/binary-detection.d.ts.map +1 -0
  216. package/data/build/dist/host/cli-adapters/binary-detection.js +71 -0
  217. package/data/build/dist/host/cli-adapters/binary-detection.js.map +1 -0
  218. package/data/build/dist/host/cli-adapters/claude-driver.d.ts +2 -0
  219. package/data/build/dist/host/cli-adapters/claude-driver.d.ts.map +1 -1
  220. package/data/build/dist/host/cli-adapters/claude-driver.js +11 -5
  221. package/data/build/dist/host/cli-adapters/claude-driver.js.map +1 -1
  222. package/data/build/dist/host/cli-adapters/claude-model-options.d.ts.map +1 -1
  223. package/data/build/dist/host/cli-adapters/claude-model-options.js +2 -1
  224. package/data/build/dist/host/cli-adapters/claude-model-options.js.map +1 -1
  225. package/data/build/dist/host/cli-adapters/claude-process.d.ts +7 -0
  226. package/data/build/dist/host/cli-adapters/claude-process.d.ts.map +1 -0
  227. package/data/build/dist/host/cli-adapters/claude-process.js +59 -0
  228. package/data/build/dist/host/cli-adapters/claude-process.js.map +1 -0
  229. package/data/build/dist/host/cli-adapters/codebuddy-driver.d.ts +3 -0
  230. package/data/build/dist/host/cli-adapters/codebuddy-driver.d.ts.map +1 -1
  231. package/data/build/dist/host/cli-adapters/codebuddy-driver.js +26 -12
  232. package/data/build/dist/host/cli-adapters/codebuddy-driver.js.map +1 -1
  233. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +0 -1
  234. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts.map +1 -1
  235. package/data/build/dist/host/cli-adapters/command-code-driver.js +10 -32
  236. package/data/build/dist/host/cli-adapters/command-code-driver.js.map +1 -1
  237. package/data/build/dist/host/cli-adapters/cursor-driver.d.ts.map +1 -1
  238. package/data/build/dist/host/cli-adapters/cursor-driver.js +4 -3
  239. package/data/build/dist/host/cli-adapters/cursor-driver.js.map +1 -1
  240. package/data/build/dist/host/cli-adapters/delegate-dispatch.js +1 -1
  241. package/data/build/dist/host/cli-adapters/delegate-dispatch.js.map +1 -1
  242. package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.d.ts +2 -0
  243. package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.d.ts.map +1 -1
  244. package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.js +5 -0
  245. package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.js.map +1 -1
  246. package/data/build/dist/host/cli-adapters/desktop-app-runtime.d.ts.map +1 -1
  247. package/data/build/dist/host/cli-adapters/desktop-app-runtime.js +2 -4
  248. package/data/build/dist/host/cli-adapters/desktop-app-runtime.js.map +1 -1
  249. package/data/build/dist/host/cli-adapters/doubao-app.d.ts +27 -0
  250. package/data/build/dist/host/cli-adapters/doubao-app.d.ts.map +1 -0
  251. package/data/build/dist/host/cli-adapters/doubao-app.js +110 -0
  252. package/data/build/dist/host/cli-adapters/doubao-app.js.map +1 -0
  253. package/data/build/dist/host/cli-adapters/doubao-artifact-store.d.ts +16 -0
  254. package/data/build/dist/host/cli-adapters/doubao-artifact-store.d.ts.map +1 -0
  255. package/data/build/dist/host/cli-adapters/doubao-artifact-store.js +142 -0
  256. package/data/build/dist/host/cli-adapters/doubao-artifact-store.js.map +1 -0
  257. package/data/build/dist/host/cli-adapters/doubao-artifacts.d.ts +16 -0
  258. package/data/build/dist/host/cli-adapters/doubao-artifacts.d.ts.map +1 -0
  259. package/data/build/dist/host/cli-adapters/doubao-artifacts.js +34 -0
  260. package/data/build/dist/host/cli-adapters/doubao-artifacts.js.map +1 -0
  261. package/data/build/dist/host/cli-adapters/doubao-cdp.d.ts +49 -0
  262. package/data/build/dist/host/cli-adapters/doubao-cdp.d.ts.map +1 -0
  263. package/data/build/dist/host/cli-adapters/doubao-cdp.js +177 -0
  264. package/data/build/dist/host/cli-adapters/doubao-cdp.js.map +1 -0
  265. package/data/build/dist/host/cli-adapters/doubao-driver.d.ts +33 -0
  266. package/data/build/dist/host/cli-adapters/doubao-driver.d.ts.map +1 -0
  267. package/data/build/dist/host/cli-adapters/doubao-driver.js +291 -0
  268. package/data/build/dist/host/cli-adapters/doubao-driver.js.map +1 -0
  269. package/data/build/dist/host/cli-adapters/doubao-events.d.ts +21 -0
  270. package/data/build/dist/host/cli-adapters/doubao-events.d.ts.map +1 -0
  271. package/data/build/dist/host/cli-adapters/doubao-events.js +126 -0
  272. package/data/build/dist/host/cli-adapters/doubao-events.js.map +1 -0
  273. package/data/build/dist/host/cli-adapters/doubao-runtime.d.ts +6 -0
  274. package/data/build/dist/host/cli-adapters/doubao-runtime.d.ts.map +1 -0
  275. package/data/build/dist/host/cli-adapters/doubao-runtime.js +176 -0
  276. package/data/build/dist/host/cli-adapters/doubao-runtime.js.map +1 -0
  277. package/data/build/dist/host/cli-adapters/doubao-tools.d.ts +8 -0
  278. package/data/build/dist/host/cli-adapters/doubao-tools.d.ts.map +1 -0
  279. package/data/build/dist/host/cli-adapters/doubao-tools.js +139 -0
  280. package/data/build/dist/host/cli-adapters/doubao-tools.js.map +1 -0
  281. package/data/build/dist/host/cli-adapters/driver.d.ts +4 -3
  282. package/data/build/dist/host/cli-adapters/driver.d.ts.map +1 -1
  283. package/data/build/dist/host/cli-adapters/feature.d.ts.map +1 -1
  284. package/data/build/dist/host/cli-adapters/feature.js +30 -36
  285. package/data/build/dist/host/cli-adapters/feature.js.map +1 -1
  286. package/data/build/dist/host/cli-adapters/grok-driver.d.ts.map +1 -1
  287. package/data/build/dist/host/cli-adapters/grok-driver.js +2 -1
  288. package/data/build/dist/host/cli-adapters/grok-driver.js.map +1 -1
  289. package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts.map +1 -1
  290. package/data/build/dist/host/cli-adapters/json-rpc-process.js +3 -1
  291. package/data/build/dist/host/cli-adapters/json-rpc-process.js.map +1 -1
  292. package/data/build/dist/host/cli-adapters/kimi-driver.d.ts.map +1 -1
  293. package/data/build/dist/host/cli-adapters/kimi-driver.js +4 -3
  294. package/data/build/dist/host/cli-adapters/kimi-driver.js.map +1 -1
  295. package/data/build/dist/host/cli-adapters/legacy-session-adapter.d.ts +2 -0
  296. package/data/build/dist/host/cli-adapters/legacy-session-adapter.d.ts.map +1 -1
  297. package/data/build/dist/host/cli-adapters/legacy-session-adapter.js +11 -0
  298. package/data/build/dist/host/cli-adapters/legacy-session-adapter.js.map +1 -1
  299. package/data/build/dist/host/cli-adapters/log-files.d.ts +3 -0
  300. package/data/build/dist/host/cli-adapters/log-files.d.ts.map +1 -0
  301. package/data/build/dist/host/cli-adapters/log-files.js +32 -0
  302. package/data/build/dist/host/cli-adapters/log-files.js.map +1 -0
  303. package/data/build/dist/host/cli-adapters/model-catalog.d.ts +2 -0
  304. package/data/build/dist/host/cli-adapters/model-catalog.d.ts.map +1 -1
  305. package/data/build/dist/host/cli-adapters/model-catalog.js +10 -0
  306. package/data/build/dist/host/cli-adapters/model-catalog.js.map +1 -1
  307. package/data/build/dist/host/cli-adapters/native-team-subagent.js +1 -1
  308. package/data/build/dist/host/cli-adapters/native-team-subagent.js.map +1 -1
  309. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +11 -0
  310. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts.map +1 -1
  311. package/data/build/dist/host/cli-adapters/opencode-driver.js +132 -49
  312. package/data/build/dist/host/cli-adapters/opencode-driver.js.map +1 -1
  313. package/data/build/dist/host/cli-adapters/opencode-v2-protocol.d.ts +29 -0
  314. package/data/build/dist/host/cli-adapters/opencode-v2-protocol.d.ts.map +1 -0
  315. package/data/build/dist/host/cli-adapters/opencode-v2-protocol.js +471 -0
  316. package/data/build/dist/host/cli-adapters/opencode-v2-protocol.js.map +1 -0
  317. package/data/build/dist/host/cli-adapters/pi-driver.d.ts.map +1 -1
  318. package/data/build/dist/host/cli-adapters/pi-driver.js +2 -1
  319. package/data/build/dist/host/cli-adapters/pi-driver.js.map +1 -1
  320. package/data/build/dist/host/cli-adapters/process-utils.d.ts +12 -4
  321. package/data/build/dist/host/cli-adapters/process-utils.d.ts.map +1 -1
  322. package/data/build/dist/host/cli-adapters/process-utils.js +151 -17
  323. package/data/build/dist/host/cli-adapters/process-utils.js.map +1 -1
  324. package/data/build/dist/host/cli-adapters/qoder-driver.d.ts.map +1 -1
  325. package/data/build/dist/host/cli-adapters/qoder-driver.js +63 -100
  326. package/data/build/dist/host/cli-adapters/qoder-driver.js.map +1 -1
  327. package/data/build/dist/host/cli-adapters/qoder-subscription.d.ts +6 -1
  328. package/data/build/dist/host/cli-adapters/qoder-subscription.d.ts.map +1 -1
  329. package/data/build/dist/host/cli-adapters/qoder-subscription.js +37 -10
  330. package/data/build/dist/host/cli-adapters/qoder-subscription.js.map +1 -1
  331. package/data/build/dist/host/cli-adapters/registry.d.ts +13 -21
  332. package/data/build/dist/host/cli-adapters/registry.d.ts.map +1 -1
  333. package/data/build/dist/host/cli-adapters/registry.js +102 -131
  334. package/data/build/dist/host/cli-adapters/registry.js.map +1 -1
  335. package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts +2 -10
  336. package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts.map +1 -1
  337. package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +1 -28
  338. package/data/build/dist/host/cli-adapters/rpc-driver-utils.js.map +1 -1
  339. package/data/build/dist/host/cli-adapters/session-store.d.ts +5 -1
  340. package/data/build/dist/host/cli-adapters/session-store.d.ts.map +1 -1
  341. package/data/build/dist/host/cli-adapters/session-store.js +68 -11
  342. package/data/build/dist/host/cli-adapters/session-store.js.map +1 -1
  343. package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +3 -3
  344. package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts.map +1 -1
  345. package/data/build/dist/host/cli-adapters/standard-stream-driver.js +14 -38
  346. package/data/build/dist/host/cli-adapters/standard-stream-driver.js.map +1 -1
  347. package/data/build/dist/host/cli-adapters/windows-command-environment.d.ts +12 -0
  348. package/data/build/dist/host/cli-adapters/windows-command-environment.d.ts.map +1 -0
  349. package/data/build/dist/host/cli-adapters/windows-command-environment.js +53 -0
  350. package/data/build/dist/host/cli-adapters/windows-command-environment.js.map +1 -0
  351. package/data/build/dist/host/cli-bridge/dispatch.js +1 -1
  352. package/data/build/dist/host/cli-bridge/dispatch.js.map +1 -1
  353. package/data/build/dist/host/debug.d.ts.map +1 -1
  354. package/data/build/dist/host/debug.js +5 -4
  355. package/data/build/dist/host/debug.js.map +1 -1
  356. package/data/build/dist/host/dsh-runtime-version.d.ts +1 -1
  357. package/data/build/dist/host/dsh-runtime-version.d.ts.map +1 -1
  358. package/data/build/dist/host/dsh-runtime-version.js +22 -17
  359. package/data/build/dist/host/dsh-runtime-version.js.map +1 -1
  360. package/data/build/dist/host/features/assistant-avatar-runtime.d.ts.map +1 -1
  361. package/data/build/dist/host/features/assistant-avatar-runtime.js +43 -19
  362. package/data/build/dist/host/features/assistant-avatar-runtime.js.map +1 -1
  363. package/data/build/dist/host/features/assistant-background.d.ts +44 -0
  364. package/data/build/dist/host/features/assistant-background.d.ts.map +1 -0
  365. package/data/build/dist/host/features/assistant-background.js +116 -0
  366. package/data/build/dist/host/features/assistant-background.js.map +1 -0
  367. package/data/build/dist/host/features/assistant-index-updates.d.ts +5 -0
  368. package/data/build/dist/host/features/assistant-index-updates.d.ts.map +1 -1
  369. package/data/build/dist/host/features/assistant-index-updates.js +29 -6
  370. package/data/build/dist/host/features/assistant-index-updates.js.map +1 -1
  371. package/data/build/dist/host/features/assistant-source-cache.d.ts +12 -0
  372. package/data/build/dist/host/features/assistant-source-cache.d.ts.map +1 -0
  373. package/data/build/dist/host/features/assistant-source-cache.js +58 -0
  374. package/data/build/dist/host/features/assistant-source-cache.js.map +1 -0
  375. package/data/build/dist/host/features/global-voice-coordinator.d.ts.map +1 -1
  376. package/data/build/dist/host/features/global-voice-coordinator.js +15 -10
  377. package/data/build/dist/host/features/global-voice-coordinator.js.map +1 -1
  378. package/data/build/dist/host/features/global-voice-rpc.d.ts.map +1 -1
  379. package/data/build/dist/host/features/global-voice-rpc.js +288 -114
  380. package/data/build/dist/host/features/global-voice-rpc.js.map +1 -1
  381. package/data/build/dist/host/features/peer-host.d.ts.map +1 -1
  382. package/data/build/dist/host/features/peer-host.js +61 -21
  383. package/data/build/dist/host/features/peer-host.js.map +1 -1
  384. package/data/build/dist/host/features/sherpa-voice-worker.d.ts +18 -0
  385. package/data/build/dist/host/features/sherpa-voice-worker.d.ts.map +1 -0
  386. package/data/build/dist/host/features/sherpa-voice-worker.js +40 -0
  387. package/data/build/dist/host/features/sherpa-voice-worker.js.map +1 -0
  388. package/data/build/dist/host/features/sherpa-worker-runtime.d.ts +41 -0
  389. package/data/build/dist/host/features/sherpa-worker-runtime.d.ts.map +1 -0
  390. package/data/build/dist/host/features/sherpa-worker-runtime.js +200 -0
  391. package/data/build/dist/host/features/sherpa-worker-runtime.js.map +1 -0
  392. package/data/build/dist/host/features/types.d.ts +6 -4
  393. package/data/build/dist/host/features/types.d.ts.map +1 -1
  394. package/data/build/dist/host/index.d.ts +1 -1
  395. package/data/build/dist/host/index.d.ts.map +1 -1
  396. package/data/build/dist/host/index.js +31 -21
  397. package/data/build/dist/host/index.js.map +1 -1
  398. package/data/build/dist/host/lan-access-dsh.d.ts.map +1 -1
  399. package/data/build/dist/host/lan-access-dsh.js +5 -22
  400. package/data/build/dist/host/lan-access-dsh.js.map +1 -1
  401. package/data/build/dist/host/modules/peer-host/host-api-proxy-service.d.ts +6 -46
  402. package/data/build/dist/host/modules/peer-host/host-api-proxy-service.d.ts.map +1 -1
  403. package/data/build/dist/host/modules/peer-host/host-api-proxy-service.js +48 -28
  404. package/data/build/dist/host/modules/peer-host/host-api-proxy-service.js.map +1 -1
  405. package/data/build/dist/host/modules/peer-host/peer-host-diagnostics.d.ts.map +1 -1
  406. package/data/build/dist/host/modules/peer-host/peer-host-diagnostics.js +1 -0
  407. package/data/build/dist/host/modules/peer-host/peer-host-diagnostics.js.map +1 -1
  408. package/data/build/dist/host/modules/peer-host/peer-host-handshake.d.ts.map +1 -1
  409. package/data/build/dist/host/modules/peer-host/peer-host-handshake.js +4 -1
  410. package/data/build/dist/host/modules/peer-host/peer-host-handshake.js.map +1 -1
  411. package/data/build/dist/host/modules/peer-host/peer-host-session.d.ts +7 -1
  412. package/data/build/dist/host/modules/peer-host/peer-host-session.d.ts.map +1 -1
  413. package/data/build/dist/host/modules/peer-host/peer-host-session.js +40 -13
  414. package/data/build/dist/host/modules/peer-host/peer-host-session.js.map +1 -1
  415. package/data/build/dist/host/provider-icon-assets.d.ts +5 -0
  416. package/data/build/dist/host/provider-icon-assets.d.ts.map +1 -0
  417. package/data/build/dist/host/provider-icon-assets.js +61 -0
  418. package/data/build/dist/host/provider-icon-assets.js.map +1 -0
  419. package/data/build/dist/host/rpc.js +2 -2
  420. package/data/build/dist/host/rpc.js.map +1 -1
  421. package/data/build/dist/host/session-migration-repair.d.ts +4 -0
  422. package/data/build/dist/host/session-migration-repair.d.ts.map +1 -1
  423. package/data/build/dist/host/session-migration-repair.js +75 -2
  424. package/data/build/dist/host/session-migration-repair.js.map +1 -1
  425. package/data/build/dist/host/terminal/backends/tmux-backend.d.ts +1 -1
  426. package/data/build/dist/host/terminal/backends/tmux-backend.d.ts.map +1 -1
  427. package/data/build/dist/host/terminal/backends/tmux-backend.js +15 -14
  428. package/data/build/dist/host/terminal/backends/tmux-backend.js.map +1 -1
  429. package/data/build/dist/shared/assistant-avatar-engine.d.ts +5 -0
  430. package/data/build/dist/shared/assistant-avatar-engine.d.ts.map +1 -1
  431. package/data/build/dist/shared/assistant-avatar-engine.js +14 -0
  432. package/data/build/dist/shared/assistant-avatar-engine.js.map +1 -1
  433. package/data/build/dist/shared/contracts/cli-adapter.d.ts +6 -0
  434. package/data/build/dist/shared/contracts/cli-adapter.d.ts.map +1 -1
  435. package/data/build/dist/shared/contracts/errors.d.ts +1 -0
  436. package/data/build/dist/shared/contracts/errors.d.ts.map +1 -1
  437. package/data/build/dist/shared/contracts/errors.js +1 -0
  438. package/data/build/dist/shared/contracts/errors.js.map +1 -1
  439. package/data/build/dist/shared/contracts/peer-host.d.ts +1 -0
  440. package/data/build/dist/shared/contracts/peer-host.d.ts.map +1 -1
  441. package/data/build/dist/shared/contracts/peer-host.js +1 -0
  442. package/data/build/dist/shared/contracts/peer-host.js.map +1 -1
  443. package/data/build/dist/shared/contracts/version.d.ts +3 -1
  444. package/data/build/dist/shared/contracts/version.d.ts.map +1 -1
  445. package/data/build/dist/shared/contracts/version.js +20 -7
  446. package/data/build/dist/shared/contracts/version.js.map +1 -1
  447. package/data/build/dist/shared/peer-host-http-routes.d.ts +62 -0
  448. package/data/build/dist/shared/peer-host-http-routes.d.ts.map +1 -0
  449. package/data/build/dist/shared/peer-host-http-routes.js +31 -0
  450. package/data/build/dist/shared/peer-host-http-routes.js.map +1 -0
  451. package/data/build/dist/shared/provider-icon-resources.d.ts +4 -0
  452. package/data/build/dist/shared/provider-icon-resources.d.ts.map +1 -0
  453. package/data/build/dist/shared/provider-icon-resources.js +24 -0
  454. package/data/build/dist/shared/provider-icon-resources.js.map +1 -0
  455. package/package.json +2 -2
  456. package/profile/README.md +2 -2
  457. package/profile/package.json +2 -2
  458. package/profile/version.json +1 -1
  459. package/version.json +1 -1
@@ -0,0 +1,4526 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@jingyi0605/codingns4dsh",
3
+ chunk: "client.assistant-configuration-view.js",
4
+ factory: (require) => {
5
+ var module = { exports: {} };
6
+ var exports = module.exports;
7
+ const require_rolldown_runtime = require("@jingyi0605/codingns4dsh/client.rolldown-runtime.js");
8
+ const require_transport = require("@jingyi0605/codingns4dsh/client.transport.js");
9
+ const require_assistant_prompts = require("@jingyi0605/codingns4dsh/client.assistant-prompts.js");
10
+ const require_theme = require("@jingyi0605/codingns4dsh/client.theme.js");
11
+ const require_manager = require("@jingyi0605/codingns4dsh/client.manager.js");
12
+ const require_primitives_adapter = require("@jingyi0605/codingns4dsh/client.primitives-adapter.js");
13
+ const require_react_dom$1 = require("@jingyi0605/codingns4dsh/client.react-dom.js");
14
+ const require_assistant_prompt_editor = require("@jingyi0605/codingns4dsh/client.assistant-prompt-editor.js");
15
+ const require_assistant_workbench = require("@jingyi0605/codingns4dsh/client.assistant-workbench.js");
16
+ let react = require("react");
17
+ react = require_rolldown_runtime.__toESM(react, 1);
18
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
19
+ //#region node_modules/.pnpm/tslib@2.8.1/node_modules/tslib/tslib.es6.mjs
20
+ /******************************************************************************
21
+ Copyright (c) Microsoft Corporation.
22
+
23
+ Permission to use, copy, modify, and/or distribute this software for any
24
+ purpose with or without fee is hereby granted.
25
+
26
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
27
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
28
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
29
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
30
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
31
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
32
+ PERFORMANCE OF THIS SOFTWARE.
33
+ ***************************************************************************** */
34
+ var extendStatics = function(d, b) {
35
+ extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d, b) {
36
+ d.__proto__ = b;
37
+ } || function(d, b) {
38
+ for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p];
39
+ };
40
+ return extendStatics(d, b);
41
+ };
42
+ function __extends(d, b) {
43
+ if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
44
+ extendStatics(d, b);
45
+ function __() {
46
+ this.constructor = d;
47
+ }
48
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
49
+ }
50
+ var __assign = function() {
51
+ __assign = Object.assign || function __assign(t) {
52
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
53
+ s = arguments[i];
54
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
55
+ }
56
+ return t;
57
+ };
58
+ return __assign.apply(this, arguments);
59
+ };
60
+ //#endregion
61
+ //#region node_modules/.pnpm/normalize-wheel@1.0.1/node_modules/normalize-wheel/src/UserAgent_DEPRECATED.js
62
+ var require_UserAgent_DEPRECATED = /* @__PURE__ */ require_rolldown_runtime.__commonJSMin(((exports, module) => {
63
+ /**
64
+ * Copyright 2004-present Facebook. All Rights Reserved.
65
+ *
66
+ * @providesModule UserAgent_DEPRECATED
67
+ */
68
+ /**
69
+ * Provides entirely client-side User Agent and OS detection. You should prefer
70
+ * the non-deprecated UserAgent module when possible, which exposes our
71
+ * authoritative server-side PHP-based detection to the client.
72
+ *
73
+ * Usage is straightforward:
74
+ *
75
+ * if (UserAgent_DEPRECATED.ie()) {
76
+ * // IE
77
+ * }
78
+ *
79
+ * You can also do version checks:
80
+ *
81
+ * if (UserAgent_DEPRECATED.ie() >= 7) {
82
+ * // IE7 or better
83
+ * }
84
+ *
85
+ * The browser functions will return NaN if the browser does not match, so
86
+ * you can also do version compares the other way:
87
+ *
88
+ * if (UserAgent_DEPRECATED.ie() < 7) {
89
+ * // IE6 or worse
90
+ * }
91
+ *
92
+ * Note that the version is a float and may include a minor version number,
93
+ * so you should always use range operators to perform comparisons, not
94
+ * strict equality.
95
+ *
96
+ * **Note:** You should **strongly** prefer capability detection to browser
97
+ * version detection where it's reasonable:
98
+ *
99
+ * http://www.quirksmode.org/js/support.html
100
+ *
101
+ * Further, we have a large number of mature wrapper functions and classes
102
+ * which abstract away many browser irregularities. Check the documentation,
103
+ * grep for things, or ask on javascript@lists.facebook.com before writing yet
104
+ * another copy of "event || window.event".
105
+ *
106
+ */
107
+ var _populated = false;
108
+ var _ie;
109
+ var _firefox;
110
+ var _opera;
111
+ var _webkit;
112
+ var _chrome;
113
+ var _ie_real_version;
114
+ var _osx;
115
+ var _windows;
116
+ var _linux;
117
+ var _android;
118
+ var _win64;
119
+ var _iphone;
120
+ var _ipad;
121
+ var _native;
122
+ var _mobile;
123
+ function _populate() {
124
+ if (_populated) return;
125
+ _populated = true;
126
+ var uas = navigator.userAgent;
127
+ var agent = /(?:MSIE.(\d+\.\d+))|(?:(?:Firefox|GranParadiso|Iceweasel).(\d+\.\d+))|(?:Opera(?:.+Version.|.)(\d+\.\d+))|(?:AppleWebKit.(\d+(?:\.\d+)?))|(?:Trident\/\d+\.\d+.*rv:(\d+\.\d+))/.exec(uas);
128
+ var os = /(Mac OS X)|(Windows)|(Linux)/.exec(uas);
129
+ _iphone = /\b(iPhone|iP[ao]d)/.exec(uas);
130
+ _ipad = /\b(iP[ao]d)/.exec(uas);
131
+ _android = /Android/i.exec(uas);
132
+ _native = /FBAN\/\w+;/i.exec(uas);
133
+ _mobile = /Mobile/i.exec(uas);
134
+ _win64 = !!/Win64/.exec(uas);
135
+ if (agent) {
136
+ _ie = agent[1] ? parseFloat(agent[1]) : agent[5] ? parseFloat(agent[5]) : NaN;
137
+ if (_ie && document && document.documentMode) _ie = document.documentMode;
138
+ var trident = /(?:Trident\/(\d+.\d+))/.exec(uas);
139
+ _ie_real_version = trident ? parseFloat(trident[1]) + 4 : _ie;
140
+ _firefox = agent[2] ? parseFloat(agent[2]) : NaN;
141
+ _opera = agent[3] ? parseFloat(agent[3]) : NaN;
142
+ _webkit = agent[4] ? parseFloat(agent[4]) : NaN;
143
+ if (_webkit) {
144
+ agent = /(?:Chrome\/(\d+\.\d+))/.exec(uas);
145
+ _chrome = agent && agent[1] ? parseFloat(agent[1]) : NaN;
146
+ } else _chrome = NaN;
147
+ } else _ie = _firefox = _opera = _chrome = _webkit = NaN;
148
+ if (os) {
149
+ if (os[1]) {
150
+ var ver = /(?:Mac OS X (\d+(?:[._]\d+)?))/.exec(uas);
151
+ _osx = ver ? parseFloat(ver[1].replace("_", ".")) : true;
152
+ } else _osx = false;
153
+ _windows = !!os[2];
154
+ _linux = !!os[3];
155
+ } else _osx = _windows = _linux = false;
156
+ }
157
+ var UserAgent_DEPRECATED = {
158
+ /**
159
+ * Check if the UA is Internet Explorer.
160
+ *
161
+ *
162
+ * @return float|NaN Version number (if match) or NaN.
163
+ */
164
+ ie: function() {
165
+ return _populate() || _ie;
166
+ },
167
+ /**
168
+ * Check if we're in Internet Explorer compatibility mode.
169
+ *
170
+ * @return bool true if in compatibility mode, false if
171
+ * not compatibility mode or not ie
172
+ */
173
+ ieCompatibilityMode: function() {
174
+ return _populate() || _ie_real_version > _ie;
175
+ },
176
+ /**
177
+ * Whether the browser is 64-bit IE. Really, this is kind of weak sauce; we
178
+ * only need this because Skype can't handle 64-bit IE yet. We need to remove
179
+ * this when we don't need it -- tracked by #601957.
180
+ */
181
+ ie64: function() {
182
+ return UserAgent_DEPRECATED.ie() && _win64;
183
+ },
184
+ /**
185
+ * Check if the UA is Firefox.
186
+ *
187
+ *
188
+ * @return float|NaN Version number (if match) or NaN.
189
+ */
190
+ firefox: function() {
191
+ return _populate() || _firefox;
192
+ },
193
+ /**
194
+ * Check if the UA is Opera.
195
+ *
196
+ *
197
+ * @return float|NaN Version number (if match) or NaN.
198
+ */
199
+ opera: function() {
200
+ return _populate() || _opera;
201
+ },
202
+ /**
203
+ * Check if the UA is WebKit.
204
+ *
205
+ *
206
+ * @return float|NaN Version number (if match) or NaN.
207
+ */
208
+ webkit: function() {
209
+ return _populate() || _webkit;
210
+ },
211
+ /**
212
+ * For Push
213
+ * WILL BE REMOVED VERY SOON. Use UserAgent_DEPRECATED.webkit
214
+ */
215
+ safari: function() {
216
+ return UserAgent_DEPRECATED.webkit();
217
+ },
218
+ /**
219
+ * Check if the UA is a Chrome browser.
220
+ *
221
+ *
222
+ * @return float|NaN Version number (if match) or NaN.
223
+ */
224
+ chrome: function() {
225
+ return _populate() || _chrome;
226
+ },
227
+ /**
228
+ * Check if the user is running Windows.
229
+ *
230
+ * @return bool `true' if the user's OS is Windows.
231
+ */
232
+ windows: function() {
233
+ return _populate() || _windows;
234
+ },
235
+ /**
236
+ * Check if the user is running Mac OS X.
237
+ *
238
+ * @return float|bool Returns a float if a version number is detected,
239
+ * otherwise true/false.
240
+ */
241
+ osx: function() {
242
+ return _populate() || _osx;
243
+ },
244
+ /**
245
+ * Check if the user is running Linux.
246
+ *
247
+ * @return bool `true' if the user's OS is some flavor of Linux.
248
+ */
249
+ linux: function() {
250
+ return _populate() || _linux;
251
+ },
252
+ /**
253
+ * Check if the user is running on an iPhone or iPod platform.
254
+ *
255
+ * @return bool `true' if the user is running some flavor of the
256
+ * iPhone OS.
257
+ */
258
+ iphone: function() {
259
+ return _populate() || _iphone;
260
+ },
261
+ mobile: function() {
262
+ return _populate() || _iphone || _ipad || _android || _mobile;
263
+ },
264
+ nativeApp: function() {
265
+ return _populate() || _native;
266
+ },
267
+ android: function() {
268
+ return _populate() || _android;
269
+ },
270
+ ipad: function() {
271
+ return _populate() || _ipad;
272
+ }
273
+ };
274
+ module.exports = UserAgent_DEPRECATED;
275
+ }));
276
+ //#endregion
277
+ //#region node_modules/.pnpm/normalize-wheel@1.0.1/node_modules/normalize-wheel/src/ExecutionEnvironment.js
278
+ /**
279
+ * Copyright (c) 2015, Facebook, Inc.
280
+ * All rights reserved.
281
+ *
282
+ * This source code is licensed under the BSD-style license found in the
283
+ * LICENSE file in the root directory of this source tree. An additional grant
284
+ * of patent rights can be found in the PATENTS file in the same directory.
285
+ *
286
+ * @providesModule ExecutionEnvironment
287
+ */
288
+ var require_ExecutionEnvironment = /* @__PURE__ */ require_rolldown_runtime.__commonJSMin(((exports, module) => {
289
+ var canUseDOM = !!(typeof window !== "undefined" && window.document && window.document.createElement);
290
+ module.exports = {
291
+ canUseDOM,
292
+ canUseWorkers: typeof Worker !== "undefined",
293
+ canUseEventListeners: canUseDOM && !!(window.addEventListener || window.attachEvent),
294
+ canUseViewport: canUseDOM && !!window.screen,
295
+ isInWorker: !canUseDOM
296
+ };
297
+ }));
298
+ //#endregion
299
+ //#region node_modules/.pnpm/normalize-wheel@1.0.1/node_modules/normalize-wheel/src/isEventSupported.js
300
+ /**
301
+ * Copyright 2013-2015, Facebook, Inc.
302
+ * All rights reserved.
303
+ *
304
+ * This source code is licensed under the BSD-style license found in the
305
+ * LICENSE file in the root directory of this source tree. An additional grant
306
+ * of patent rights can be found in the PATENTS file in the same directory.
307
+ *
308
+ * @providesModule isEventSupported
309
+ */
310
+ var require_isEventSupported = /* @__PURE__ */ require_rolldown_runtime.__commonJSMin(((exports, module) => {
311
+ var ExecutionEnvironment = require_ExecutionEnvironment();
312
+ var useHasFeature;
313
+ if (ExecutionEnvironment.canUseDOM) useHasFeature = document.implementation && document.implementation.hasFeature && document.implementation.hasFeature("", "") !== true;
314
+ /**
315
+ * Checks if an event is supported in the current execution environment.
316
+ *
317
+ * NOTE: This will not work correctly for non-generic events such as `change`,
318
+ * `reset`, `load`, `error`, and `select`.
319
+ *
320
+ * Borrows from Modernizr.
321
+ *
322
+ * @param {string} eventNameSuffix Event name, e.g. "click".
323
+ * @param {?boolean} capture Check if the capture phase is supported.
324
+ * @return {boolean} True if the event is supported.
325
+ * @internal
326
+ * @license Modernizr 3.0.0pre (Custom Build) | MIT
327
+ */
328
+ function isEventSupported(eventNameSuffix, capture) {
329
+ if (!ExecutionEnvironment.canUseDOM || capture && !("addEventListener" in document)) return false;
330
+ var eventName = "on" + eventNameSuffix;
331
+ var isSupported = eventName in document;
332
+ if (!isSupported) {
333
+ var element = document.createElement("div");
334
+ element.setAttribute(eventName, "return;");
335
+ isSupported = typeof element[eventName] === "function";
336
+ }
337
+ if (!isSupported && useHasFeature && eventNameSuffix === "wheel") isSupported = document.implementation.hasFeature("Events.wheel", "3.0");
338
+ return isSupported;
339
+ }
340
+ module.exports = isEventSupported;
341
+ }));
342
+ //#endregion
343
+ //#region node_modules/.pnpm/normalize-wheel@1.0.1/node_modules/normalize-wheel/src/normalizeWheel.js
344
+ /**
345
+ * Copyright (c) 2015, Facebook, Inc.
346
+ * All rights reserved.
347
+ *
348
+ * This source code is licensed under the BSD-style license found in the
349
+ * LICENSE file in the root directory of this source tree. An additional grant
350
+ * of patent rights can be found in the PATENTS file in the same directory.
351
+ *
352
+ * @providesModule normalizeWheel
353
+ * @typechecks
354
+ */
355
+ var require_normalizeWheel = /* @__PURE__ */ require_rolldown_runtime.__commonJSMin(((exports, module) => {
356
+ var UserAgent_DEPRECATED = require_UserAgent_DEPRECATED();
357
+ var isEventSupported = require_isEventSupported();
358
+ var PIXEL_STEP = 10;
359
+ var LINE_HEIGHT = 40;
360
+ var PAGE_HEIGHT = 800;
361
+ /**
362
+ * Mouse wheel (and 2-finger trackpad) support on the web sucks. It is
363
+ * complicated, thus this doc is long and (hopefully) detailed enough to answer
364
+ * your questions.
365
+ *
366
+ * If you need to react to the mouse wheel in a predictable way, this code is
367
+ * like your bestest friend. * hugs *
368
+ *
369
+ * As of today, there are 4 DOM event types you can listen to:
370
+ *
371
+ * 'wheel' -- Chrome(31+), FF(17+), IE(9+)
372
+ * 'mousewheel' -- Chrome, IE(6+), Opera, Safari
373
+ * 'MozMousePixelScroll' -- FF(3.5 only!) (2010-2013) -- don't bother!
374
+ * 'DOMMouseScroll' -- FF(0.9.7+) since 2003
375
+ *
376
+ * So what to do? The is the best:
377
+ *
378
+ * normalizeWheel.getEventType();
379
+ *
380
+ * In your event callback, use this code to get sane interpretation of the
381
+ * deltas. This code will return an object with properties:
382
+ *
383
+ * spinX -- normalized spin speed (use for zoom) - x plane
384
+ * spinY -- " - y plane
385
+ * pixelX -- normalized distance (to pixels) - x plane
386
+ * pixelY -- " - y plane
387
+ *
388
+ * Wheel values are provided by the browser assuming you are using the wheel to
389
+ * scroll a web page by a number of lines or pixels (or pages). Values can vary
390
+ * significantly on different platforms and browsers, forgetting that you can
391
+ * scroll at different speeds. Some devices (like trackpads) emit more events
392
+ * at smaller increments with fine granularity, and some emit massive jumps with
393
+ * linear speed or acceleration.
394
+ *
395
+ * This code does its best to normalize the deltas for you:
396
+ *
397
+ * - spin is trying to normalize how far the wheel was spun (or trackpad
398
+ * dragged). This is super useful for zoom support where you want to
399
+ * throw away the chunky scroll steps on the PC and make those equal to
400
+ * the slow and smooth tiny steps on the Mac. Key data: This code tries to
401
+ * resolve a single slow step on a wheel to 1.
402
+ *
403
+ * - pixel is normalizing the desired scroll delta in pixel units. You'll
404
+ * get the crazy differences between browsers, but at least it'll be in
405
+ * pixels!
406
+ *
407
+ * - positive value indicates scrolling DOWN/RIGHT, negative UP/LEFT. This
408
+ * should translate to positive value zooming IN, negative zooming OUT.
409
+ * This matches the newer 'wheel' event.
410
+ *
411
+ * Why are there spinX, spinY (or pixels)?
412
+ *
413
+ * - spinX is a 2-finger side drag on the trackpad, and a shift + wheel turn
414
+ * with a mouse. It results in side-scrolling in the browser by default.
415
+ *
416
+ * - spinY is what you expect -- it's the classic axis of a mouse wheel.
417
+ *
418
+ * - I dropped spinZ/pixelZ. It is supported by the DOM 3 'wheel' event and
419
+ * probably is by browsers in conjunction with fancy 3D controllers .. but
420
+ * you know.
421
+ *
422
+ * Implementation info:
423
+ *
424
+ * Examples of 'wheel' event if you scroll slowly (down) by one step with an
425
+ * average mouse:
426
+ *
427
+ * OS X + Chrome (mouse) - 4 pixel delta (wheelDelta -120)
428
+ * OS X + Safari (mouse) - N/A pixel delta (wheelDelta -12)
429
+ * OS X + Firefox (mouse) - 0.1 line delta (wheelDelta N/A)
430
+ * Win8 + Chrome (mouse) - 100 pixel delta (wheelDelta -120)
431
+ * Win8 + Firefox (mouse) - 3 line delta (wheelDelta -120)
432
+ *
433
+ * On the trackpad:
434
+ *
435
+ * OS X + Chrome (trackpad) - 2 pixel delta (wheelDelta -6)
436
+ * OS X + Firefox (trackpad) - 1 pixel delta (wheelDelta N/A)
437
+ *
438
+ * On other/older browsers.. it's more complicated as there can be multiple and
439
+ * also missing delta values.
440
+ *
441
+ * The 'wheel' event is more standard:
442
+ *
443
+ * http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents
444
+ *
445
+ * The basics is that it includes a unit, deltaMode (pixels, lines, pages), and
446
+ * deltaX, deltaY and deltaZ. Some browsers provide other values to maintain
447
+ * backward compatibility with older events. Those other values help us
448
+ * better normalize spin speed. Example of what the browsers provide:
449
+ *
450
+ * | event.wheelDelta | event.detail
451
+ * ------------------+------------------+--------------
452
+ * Safari v5/OS X | -120 | 0
453
+ * Safari v5/Win7 | -120 | 0
454
+ * Chrome v17/OS X | -120 | 0
455
+ * Chrome v17/Win7 | -120 | 0
456
+ * IE9/Win7 | -120 | undefined
457
+ * Firefox v4/OS X | undefined | 1
458
+ * Firefox v4/Win7 | undefined | 3
459
+ *
460
+ */
461
+ function normalizeWheel(event) {
462
+ var sX = 0, sY = 0, pX = 0, pY = 0;
463
+ if ("detail" in event) sY = event.detail;
464
+ if ("wheelDelta" in event) sY = -event.wheelDelta / 120;
465
+ if ("wheelDeltaY" in event) sY = -event.wheelDeltaY / 120;
466
+ if ("wheelDeltaX" in event) sX = -event.wheelDeltaX / 120;
467
+ if ("axis" in event && event.axis === event.HORIZONTAL_AXIS) {
468
+ sX = sY;
469
+ sY = 0;
470
+ }
471
+ pX = sX * PIXEL_STEP;
472
+ pY = sY * PIXEL_STEP;
473
+ if ("deltaY" in event) pY = event.deltaY;
474
+ if ("deltaX" in event) pX = event.deltaX;
475
+ if ((pX || pY) && event.deltaMode) {
476
+ if (event.deltaMode == 1) {
477
+ pX *= LINE_HEIGHT;
478
+ pY *= LINE_HEIGHT;
479
+ } else {
480
+ pX *= PAGE_HEIGHT;
481
+ pY *= PAGE_HEIGHT;
482
+ }
483
+ }
484
+ if (pX && !sX) sX = pX < 1 ? -1 : 1;
485
+ if (pY && !sY) sY = pY < 1 ? -1 : 1;
486
+ return {
487
+ spinX: sX,
488
+ spinY: sY,
489
+ pixelX: pX,
490
+ pixelY: pY
491
+ };
492
+ }
493
+ /**
494
+ * The best combination if you prefer spinX + spinY normalization. It favors
495
+ * the older DOMMouseScroll for Firefox, as FF does not include wheelDelta with
496
+ * 'wheel' event, making spin speed determination impossible.
497
+ */
498
+ normalizeWheel.getEventType = function() {
499
+ return UserAgent_DEPRECATED.firefox() ? "DOMMouseScroll" : isEventSupported("wheel") ? "wheel" : "mousewheel";
500
+ };
501
+ module.exports = normalizeWheel;
502
+ }));
503
+ //#endregion
504
+ //#region node_modules/.pnpm/normalize-wheel@1.0.1/node_modules/normalize-wheel/index.js
505
+ var require_normalize_wheel = /* @__PURE__ */ require_rolldown_runtime.__commonJSMin(((exports, module) => {
506
+ module.exports = require_normalizeWheel();
507
+ }));
508
+ //#endregion
509
+ //#region node_modules/.pnpm/react-easy-crop@5.5.6_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/react-easy-crop/index.module.mjs
510
+ var import_normalize_wheel = /* @__PURE__ */ require_rolldown_runtime.__toESM(require_normalize_wheel(), 1);
511
+ /**
512
+ * Compute the dimension of the crop area based on media size,
513
+ * aspect ratio and optionally rotation
514
+ */
515
+ function getCropSize(mediaWidth, mediaHeight, containerWidth, containerHeight, aspect, rotation) {
516
+ if (rotation === void 0) rotation = 0;
517
+ var _a = rotateSize(mediaWidth, mediaHeight, rotation), width = _a.width, height = _a.height;
518
+ var fittingWidth = Math.min(width, containerWidth);
519
+ var fittingHeight = Math.min(height, containerHeight);
520
+ if (fittingWidth > fittingHeight * aspect) return {
521
+ width: fittingHeight * aspect,
522
+ height: fittingHeight
523
+ };
524
+ return {
525
+ width: fittingWidth,
526
+ height: fittingWidth / aspect
527
+ };
528
+ }
529
+ /**
530
+ * Compute media zoom.
531
+ * We fit the media into the container with "max-width: 100%; max-height: 100%;"
532
+ */
533
+ function getMediaZoom(mediaSize) {
534
+ return mediaSize.width > mediaSize.height ? mediaSize.width / mediaSize.naturalWidth : mediaSize.height / mediaSize.naturalHeight;
535
+ }
536
+ /**
537
+ * Ensure a new media position stays in the crop area.
538
+ */
539
+ function restrictPosition(position, mediaSize, cropSize, zoom, rotation) {
540
+ if (rotation === void 0) rotation = 0;
541
+ var _a = rotateSize(mediaSize.width, mediaSize.height, rotation), width = _a.width, height = _a.height;
542
+ return {
543
+ x: restrictPositionCoord(position.x, width, cropSize.width, zoom),
544
+ y: restrictPositionCoord(position.y, height, cropSize.height, zoom)
545
+ };
546
+ }
547
+ function restrictPositionCoord(position, mediaSize, cropSize, zoom) {
548
+ var maxPosition = mediaSize * zoom / 2 - cropSize / 2;
549
+ return clamp(position, -maxPosition, maxPosition);
550
+ }
551
+ function getDistanceBetweenPoints(pointA, pointB) {
552
+ return Math.sqrt(Math.pow(pointA.y - pointB.y, 2) + Math.pow(pointA.x - pointB.x, 2));
553
+ }
554
+ function getRotationBetweenPoints(pointA, pointB) {
555
+ return Math.atan2(pointB.y - pointA.y, pointB.x - pointA.x) * 180 / Math.PI;
556
+ }
557
+ /**
558
+ * Compute the output cropped area of the media in percentages and pixels.
559
+ * x/y are the top-left coordinates on the src media
560
+ */
561
+ function computeCroppedArea(crop, mediaSize, cropSize, aspect, zoom, rotation, restrictPosition) {
562
+ if (rotation === void 0) rotation = 0;
563
+ if (restrictPosition === void 0) restrictPosition = true;
564
+ var limitAreaFn = restrictPosition ? limitArea : noOp;
565
+ var mediaBBoxSize = rotateSize(mediaSize.width, mediaSize.height, rotation);
566
+ var mediaNaturalBBoxSize = rotateSize(mediaSize.naturalWidth, mediaSize.naturalHeight, rotation);
567
+ var croppedAreaPercentages = {
568
+ x: limitAreaFn(100, ((mediaBBoxSize.width - cropSize.width / zoom) / 2 - crop.x / zoom) / mediaBBoxSize.width * 100),
569
+ y: limitAreaFn(100, ((mediaBBoxSize.height - cropSize.height / zoom) / 2 - crop.y / zoom) / mediaBBoxSize.height * 100),
570
+ width: limitAreaFn(100, cropSize.width / mediaBBoxSize.width * 100 / zoom),
571
+ height: limitAreaFn(100, cropSize.height / mediaBBoxSize.height * 100 / zoom)
572
+ };
573
+ var widthInPixels = Math.round(limitAreaFn(mediaNaturalBBoxSize.width, croppedAreaPercentages.width * mediaNaturalBBoxSize.width / 100));
574
+ var heightInPixels = Math.round(limitAreaFn(mediaNaturalBBoxSize.height, croppedAreaPercentages.height * mediaNaturalBBoxSize.height / 100));
575
+ var sizePixels = mediaNaturalBBoxSize.width >= mediaNaturalBBoxSize.height * aspect ? {
576
+ width: Math.round(heightInPixels * aspect),
577
+ height: heightInPixels
578
+ } : {
579
+ width: widthInPixels,
580
+ height: Math.round(widthInPixels / aspect)
581
+ };
582
+ return {
583
+ croppedAreaPercentages,
584
+ croppedAreaPixels: __assign(__assign({}, sizePixels), {
585
+ x: Math.round(limitAreaFn(mediaNaturalBBoxSize.width - sizePixels.width, croppedAreaPercentages.x * mediaNaturalBBoxSize.width / 100)),
586
+ y: Math.round(limitAreaFn(mediaNaturalBBoxSize.height - sizePixels.height, croppedAreaPercentages.y * mediaNaturalBBoxSize.height / 100))
587
+ })
588
+ };
589
+ }
590
+ /**
591
+ * Ensure the returned value is between 0 and max
592
+ */
593
+ function limitArea(max, value) {
594
+ return Math.min(max, Math.max(0, value));
595
+ }
596
+ function noOp(_max, value) {
597
+ return value;
598
+ }
599
+ /**
600
+ * Compute crop and zoom from the croppedAreaPercentages.
601
+ */
602
+ function getInitialCropFromCroppedAreaPercentages(croppedAreaPercentages, mediaSize, rotation, cropSize, minZoom, maxZoom) {
603
+ var mediaBBoxSize = rotateSize(mediaSize.width, mediaSize.height, rotation);
604
+ var zoom = clamp(cropSize.width / mediaBBoxSize.width * (100 / croppedAreaPercentages.width), minZoom, maxZoom);
605
+ return {
606
+ crop: {
607
+ x: zoom * mediaBBoxSize.width / 2 - cropSize.width / 2 - mediaBBoxSize.width * zoom * (croppedAreaPercentages.x / 100),
608
+ y: zoom * mediaBBoxSize.height / 2 - cropSize.height / 2 - mediaBBoxSize.height * zoom * (croppedAreaPercentages.y / 100)
609
+ },
610
+ zoom
611
+ };
612
+ }
613
+ /**
614
+ * Compute zoom from the croppedAreaPixels
615
+ */
616
+ function getZoomFromCroppedAreaPixels(croppedAreaPixels, mediaSize, cropSize) {
617
+ var mediaZoom = getMediaZoom(mediaSize);
618
+ return cropSize.height > cropSize.width ? cropSize.height / (croppedAreaPixels.height * mediaZoom) : cropSize.width / (croppedAreaPixels.width * mediaZoom);
619
+ }
620
+ /**
621
+ * Compute crop and zoom from the croppedAreaPixels
622
+ */
623
+ function getInitialCropFromCroppedAreaPixels(croppedAreaPixels, mediaSize, rotation, cropSize, minZoom, maxZoom) {
624
+ if (rotation === void 0) rotation = 0;
625
+ var mediaNaturalBBoxSize = rotateSize(mediaSize.naturalWidth, mediaSize.naturalHeight, rotation);
626
+ var zoom = clamp(getZoomFromCroppedAreaPixels(croppedAreaPixels, mediaSize, cropSize), minZoom, maxZoom);
627
+ var cropZoom = cropSize.height > cropSize.width ? cropSize.height / croppedAreaPixels.height : cropSize.width / croppedAreaPixels.width;
628
+ return {
629
+ crop: {
630
+ x: ((mediaNaturalBBoxSize.width - croppedAreaPixels.width) / 2 - croppedAreaPixels.x) * cropZoom,
631
+ y: ((mediaNaturalBBoxSize.height - croppedAreaPixels.height) / 2 - croppedAreaPixels.y) * cropZoom
632
+ },
633
+ zoom
634
+ };
635
+ }
636
+ /**
637
+ * Return the point that is the center of point a and b
638
+ */
639
+ function getCenter(a, b) {
640
+ return {
641
+ x: (b.x + a.x) / 2,
642
+ y: (b.y + a.y) / 2
643
+ };
644
+ }
645
+ function getRadianAngle(degreeValue) {
646
+ return degreeValue * Math.PI / 180;
647
+ }
648
+ /**
649
+ * Returns the new bounding area of a rotated rectangle.
650
+ */
651
+ function rotateSize(width, height, rotation) {
652
+ var rotRad = getRadianAngle(rotation);
653
+ return {
654
+ width: Math.abs(Math.cos(rotRad) * width) + Math.abs(Math.sin(rotRad) * height),
655
+ height: Math.abs(Math.sin(rotRad) * width) + Math.abs(Math.cos(rotRad) * height)
656
+ };
657
+ }
658
+ /**
659
+ * Clamp value between min and max
660
+ */
661
+ function clamp(value, min, max) {
662
+ return Math.min(Math.max(value, min), max);
663
+ }
664
+ /**
665
+ * Combine multiple class names into a single string.
666
+ */
667
+ function classNames() {
668
+ var args = [];
669
+ for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
670
+ return args.filter(function(value) {
671
+ if (typeof value === "string" && value.length > 0) return true;
672
+ return false;
673
+ }).join(" ").trim();
674
+ }
675
+ var css_248z = ".reactEasyCrop_Container {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n overflow: hidden;\n user-select: none;\n touch-action: none;\n cursor: move;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.reactEasyCrop_Image,\n.reactEasyCrop_Video {\n will-change: transform; /* this improves performances and prevent painting issues on iOS Chrome */\n}\n\n.reactEasyCrop_Contain {\n max-width: 100%;\n max-height: 100%;\n margin: auto;\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n}\n.reactEasyCrop_Cover_Horizontal {\n width: 100%;\n height: auto;\n}\n.reactEasyCrop_Cover_Vertical {\n width: auto;\n height: 100%;\n}\n\n.reactEasyCrop_CropArea {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n border: 1px solid rgba(255, 255, 255, 0.5);\n box-sizing: border-box;\n box-shadow: 0 0 0 9999em;\n color: rgba(0, 0, 0, 0.5);\n overflow: hidden;\n}\n\n.reactEasyCrop_CropAreaRound {\n border-radius: 50%;\n}\n\n.reactEasyCrop_CropAreaGrid::before {\n content: ' ';\n box-sizing: border-box;\n position: absolute;\n border: 1px solid rgba(255, 255, 255, 0.5);\n top: 0;\n bottom: 0;\n left: 33.33%;\n right: 33.33%;\n border-top: 0;\n border-bottom: 0;\n}\n\n.reactEasyCrop_CropAreaGrid::after {\n content: ' ';\n box-sizing: border-box;\n position: absolute;\n border: 1px solid rgba(255, 255, 255, 0.5);\n top: 33.33%;\n bottom: 33.33%;\n left: 0;\n right: 0;\n border-left: 0;\n border-right: 0;\n}\n";
676
+ var MIN_ZOOM = 1;
677
+ var MAX_ZOOM = 3;
678
+ var KEYBOARD_STEP = 1;
679
+ //#endregion
680
+ //#region src/client/avatar/portrait-editor.ts
681
+ const Cropper = function(_super) {
682
+ __extends(Cropper, _super);
683
+ function Cropper() {
684
+ var _this = _super !== null && _super.apply(this, arguments) || this;
685
+ _this.cropperRef = react.createRef();
686
+ _this.imageRef = react.createRef();
687
+ _this.videoRef = react.createRef();
688
+ _this.containerPosition = {
689
+ x: 0,
690
+ y: 0
691
+ };
692
+ _this.containerRef = null;
693
+ _this.styleRef = null;
694
+ _this.containerRect = null;
695
+ _this.mediaSize = {
696
+ width: 0,
697
+ height: 0,
698
+ naturalWidth: 0,
699
+ naturalHeight: 0
700
+ };
701
+ _this.dragStartPosition = {
702
+ x: 0,
703
+ y: 0
704
+ };
705
+ _this.dragStartCrop = {
706
+ x: 0,
707
+ y: 0
708
+ };
709
+ _this.gestureZoomStart = 0;
710
+ _this.gestureRotationStart = 0;
711
+ _this.isTouching = false;
712
+ _this.lastPinchDistance = 0;
713
+ _this.lastPinchRotation = 0;
714
+ _this.rafDragTimeout = null;
715
+ _this.rafPinchTimeout = null;
716
+ _this.wheelTimer = null;
717
+ _this.currentDoc = typeof document !== "undefined" ? document : null;
718
+ _this.currentWindow = typeof window !== "undefined" ? window : null;
719
+ _this.resizeObserver = null;
720
+ _this.previousCropSize = null;
721
+ _this.isInitialized = false;
722
+ _this.state = {
723
+ cropSize: null,
724
+ hasWheelJustStarted: false,
725
+ mediaObjectFit: void 0
726
+ };
727
+ _this.initResizeObserver = function() {
728
+ if (typeof window.ResizeObserver === "undefined" || !_this.containerRef) return;
729
+ var isFirstResize = true;
730
+ _this.resizeObserver = new window.ResizeObserver(function(entries) {
731
+ if (isFirstResize) {
732
+ isFirstResize = false;
733
+ return;
734
+ }
735
+ _this.computeSizes();
736
+ });
737
+ _this.resizeObserver.observe(_this.containerRef);
738
+ };
739
+ _this.preventZoomSafari = function(e) {
740
+ return e.preventDefault();
741
+ };
742
+ _this.cleanEvents = function() {
743
+ if (!_this.currentDoc) return;
744
+ _this.currentDoc.removeEventListener("mousemove", _this.onMouseMove);
745
+ _this.currentDoc.removeEventListener("mouseup", _this.onDragStopped);
746
+ _this.currentDoc.removeEventListener("touchmove", _this.onTouchMove);
747
+ _this.currentDoc.removeEventListener("touchend", _this.onDragStopped);
748
+ _this.currentDoc.removeEventListener("gesturechange", _this.onGestureChange);
749
+ _this.currentDoc.removeEventListener("gestureend", _this.onGestureEnd);
750
+ _this.currentDoc.removeEventListener("scroll", _this.onScroll);
751
+ };
752
+ _this.clearScrollEvent = function() {
753
+ if (_this.containerRef) _this.containerRef.removeEventListener("wheel", _this.onWheel);
754
+ if (_this.wheelTimer) clearTimeout(_this.wheelTimer);
755
+ };
756
+ _this.onMediaLoad = function() {
757
+ var cropSize = _this.computeSizes();
758
+ if (cropSize) {
759
+ _this.previousCropSize = cropSize;
760
+ _this.emitCropData();
761
+ _this.setInitialCrop(cropSize);
762
+ _this.isInitialized = true;
763
+ }
764
+ if (_this.props.onMediaLoaded) _this.props.onMediaLoaded(_this.mediaSize);
765
+ };
766
+ _this.setInitialCrop = function(cropSize) {
767
+ if (_this.props.initialCroppedAreaPercentages) {
768
+ var _a = getInitialCropFromCroppedAreaPercentages(_this.props.initialCroppedAreaPercentages, _this.mediaSize, _this.props.rotation, cropSize, _this.props.minZoom, _this.props.maxZoom), crop = _a.crop, zoom = _a.zoom;
769
+ _this.props.onCropChange(crop);
770
+ _this.props.onZoomChange && _this.props.onZoomChange(zoom);
771
+ } else if (_this.props.initialCroppedAreaPixels) {
772
+ var _b = getInitialCropFromCroppedAreaPixels(_this.props.initialCroppedAreaPixels, _this.mediaSize, _this.props.rotation, cropSize, _this.props.minZoom, _this.props.maxZoom), crop = _b.crop, zoom = _b.zoom;
773
+ _this.props.onCropChange(crop);
774
+ _this.props.onZoomChange && _this.props.onZoomChange(zoom);
775
+ }
776
+ };
777
+ _this.computeSizes = function() {
778
+ var _a, _b, _c, _d, _e, _f;
779
+ var mediaRef = _this.imageRef.current || _this.videoRef.current;
780
+ if (mediaRef && _this.containerRef) {
781
+ _this.containerRect = _this.containerRef.getBoundingClientRect();
782
+ _this.saveContainerPosition();
783
+ var containerAspect = _this.containerRect.width / _this.containerRect.height;
784
+ var naturalWidth = ((_a = _this.imageRef.current) === null || _a === void 0 ? void 0 : _a.naturalWidth) || ((_b = _this.videoRef.current) === null || _b === void 0 ? void 0 : _b.videoWidth) || 0;
785
+ var naturalHeight = ((_c = _this.imageRef.current) === null || _c === void 0 ? void 0 : _c.naturalHeight) || ((_d = _this.videoRef.current) === null || _d === void 0 ? void 0 : _d.videoHeight) || 0;
786
+ var isMediaScaledDown = mediaRef.offsetWidth < naturalWidth || mediaRef.offsetHeight < naturalHeight;
787
+ var mediaAspect = naturalWidth / naturalHeight;
788
+ var renderedMediaSize = void 0;
789
+ if (isMediaScaledDown) switch (_this.state.mediaObjectFit) {
790
+ default:
791
+ case "contain":
792
+ renderedMediaSize = containerAspect > mediaAspect ? {
793
+ width: _this.containerRect.height * mediaAspect,
794
+ height: _this.containerRect.height
795
+ } : {
796
+ width: _this.containerRect.width,
797
+ height: _this.containerRect.width / mediaAspect
798
+ };
799
+ break;
800
+ case "horizontal-cover":
801
+ renderedMediaSize = {
802
+ width: _this.containerRect.width,
803
+ height: _this.containerRect.width / mediaAspect
804
+ };
805
+ break;
806
+ case "vertical-cover": renderedMediaSize = {
807
+ width: _this.containerRect.height * mediaAspect,
808
+ height: _this.containerRect.height
809
+ };
810
+ }
811
+ else renderedMediaSize = {
812
+ width: mediaRef.offsetWidth,
813
+ height: mediaRef.offsetHeight
814
+ };
815
+ _this.mediaSize = __assign(__assign({}, renderedMediaSize), {
816
+ naturalWidth,
817
+ naturalHeight
818
+ });
819
+ if (_this.props.setMediaSize) _this.props.setMediaSize(_this.mediaSize);
820
+ var cropSize = _this.props.cropSize ? _this.props.cropSize : getCropSize(_this.mediaSize.width, _this.mediaSize.height, _this.containerRect.width, _this.containerRect.height, _this.props.aspect, _this.props.rotation);
821
+ if (((_e = _this.state.cropSize) === null || _e === void 0 ? void 0 : _e.height) !== cropSize.height || ((_f = _this.state.cropSize) === null || _f === void 0 ? void 0 : _f.width) !== cropSize.width) _this.props.onCropSizeChange && _this.props.onCropSizeChange(cropSize);
822
+ _this.setState({ cropSize }, _this.recomputeCropPosition);
823
+ if (_this.props.setCropSize) _this.props.setCropSize(cropSize);
824
+ return cropSize;
825
+ }
826
+ };
827
+ _this.saveContainerPosition = function() {
828
+ if (_this.containerRef) {
829
+ var bounds = _this.containerRef.getBoundingClientRect();
830
+ _this.containerPosition = {
831
+ x: bounds.left,
832
+ y: bounds.top
833
+ };
834
+ }
835
+ };
836
+ _this.onMouseDown = function(e) {
837
+ if (!_this.currentDoc) return;
838
+ e.preventDefault();
839
+ _this.currentDoc.addEventListener("mousemove", _this.onMouseMove);
840
+ _this.currentDoc.addEventListener("mouseup", _this.onDragStopped);
841
+ _this.saveContainerPosition();
842
+ _this.onDragStart(Cropper.getMousePoint(e));
843
+ };
844
+ _this.onMouseMove = function(e) {
845
+ return _this.onDrag(Cropper.getMousePoint(e));
846
+ };
847
+ _this.onScroll = function(e) {
848
+ if (!_this.currentDoc) return;
849
+ e.preventDefault();
850
+ _this.saveContainerPosition();
851
+ };
852
+ _this.onTouchStart = function(e) {
853
+ if (!_this.currentDoc) return;
854
+ _this.isTouching = true;
855
+ if (_this.props.onTouchRequest && !_this.props.onTouchRequest(e)) return;
856
+ _this.currentDoc.addEventListener("touchmove", _this.onTouchMove, { passive: false });
857
+ _this.currentDoc.addEventListener("touchend", _this.onDragStopped);
858
+ _this.saveContainerPosition();
859
+ if (e.touches.length === 2) _this.onPinchStart(e);
860
+ else if (e.touches.length === 1) _this.onDragStart(Cropper.getTouchPoint(e.touches[0]));
861
+ };
862
+ _this.onTouchMove = function(e) {
863
+ e.preventDefault();
864
+ if (e.touches.length === 2) _this.onPinchMove(e);
865
+ else if (e.touches.length === 1) _this.onDrag(Cropper.getTouchPoint(e.touches[0]));
866
+ };
867
+ _this.onGestureStart = function(e) {
868
+ if (!_this.currentDoc) return;
869
+ e.preventDefault();
870
+ _this.currentDoc.addEventListener("gesturechange", _this.onGestureChange);
871
+ _this.currentDoc.addEventListener("gestureend", _this.onGestureEnd);
872
+ _this.gestureZoomStart = _this.props.zoom;
873
+ _this.gestureRotationStart = _this.props.rotation;
874
+ };
875
+ _this.onGestureChange = function(e) {
876
+ e.preventDefault();
877
+ if (_this.isTouching) return;
878
+ var point = Cropper.getMousePoint(e);
879
+ var newZoom = _this.gestureZoomStart - 1 + e.scale;
880
+ _this.setNewZoom(newZoom, point, { shouldUpdatePosition: true });
881
+ if (_this.props.onRotationChange) {
882
+ var newRotation = _this.gestureRotationStart + e.rotation;
883
+ _this.props.onRotationChange(newRotation);
884
+ }
885
+ };
886
+ _this.onGestureEnd = function(e) {
887
+ _this.cleanEvents();
888
+ };
889
+ _this.onDragStart = function(_a) {
890
+ var _b, _c;
891
+ _this.dragStartPosition = {
892
+ x: _a.x,
893
+ y: _a.y
894
+ };
895
+ _this.dragStartCrop = __assign({}, _this.props.crop);
896
+ (_c = (_b = _this.props).onInteractionStart) === null || _c === void 0 || _c.call(_b);
897
+ };
898
+ _this.onDrag = function(_a) {
899
+ var x = _a.x, y = _a.y;
900
+ if (!_this.currentWindow) return;
901
+ if (_this.rafDragTimeout) _this.currentWindow.cancelAnimationFrame(_this.rafDragTimeout);
902
+ _this.rafDragTimeout = _this.currentWindow.requestAnimationFrame(function() {
903
+ if (!_this.state.cropSize) return;
904
+ if (x === void 0 || y === void 0) return;
905
+ var offsetX = x - _this.dragStartPosition.x;
906
+ var offsetY = y - _this.dragStartPosition.y;
907
+ var requestedPosition = {
908
+ x: _this.dragStartCrop.x + offsetX,
909
+ y: _this.dragStartCrop.y + offsetY
910
+ };
911
+ var newPosition = _this.props.restrictPosition ? restrictPosition(requestedPosition, _this.mediaSize, _this.state.cropSize, _this.props.zoom, _this.props.rotation) : requestedPosition;
912
+ _this.props.onCropChange(newPosition);
913
+ });
914
+ };
915
+ _this.onDragStopped = function() {
916
+ var _a, _b;
917
+ _this.isTouching = false;
918
+ _this.cleanEvents();
919
+ _this.emitCropData();
920
+ (_b = (_a = _this.props).onInteractionEnd) === null || _b === void 0 || _b.call(_a);
921
+ };
922
+ _this.onWheel = function(e) {
923
+ if (!_this.currentWindow) return;
924
+ if (_this.props.onWheelRequest && !_this.props.onWheelRequest(e)) return;
925
+ e.preventDefault();
926
+ var point = Cropper.getMousePoint(e);
927
+ var pixelY = (0, import_normalize_wheel.default)(e).pixelY;
928
+ var newZoom = _this.props.zoom - pixelY * _this.props.zoomSpeed / 200;
929
+ _this.setNewZoom(newZoom, point, { shouldUpdatePosition: true });
930
+ if (!_this.state.hasWheelJustStarted) _this.setState({ hasWheelJustStarted: true }, function() {
931
+ var _a, _b;
932
+ return (_b = (_a = _this.props).onInteractionStart) === null || _b === void 0 ? void 0 : _b.call(_a);
933
+ });
934
+ if (_this.wheelTimer) clearTimeout(_this.wheelTimer);
935
+ _this.wheelTimer = _this.currentWindow.setTimeout(function() {
936
+ return _this.setState({ hasWheelJustStarted: false }, function() {
937
+ var _a, _b;
938
+ return (_b = (_a = _this.props).onInteractionEnd) === null || _b === void 0 ? void 0 : _b.call(_a);
939
+ });
940
+ }, 250);
941
+ };
942
+ _this.getPointOnContainer = function(_a, containerTopLeft) {
943
+ var x = _a.x, y = _a.y;
944
+ if (!_this.containerRect) throw new Error("The Cropper is not mounted");
945
+ return {
946
+ x: _this.containerRect.width / 2 - (x - containerTopLeft.x),
947
+ y: _this.containerRect.height / 2 - (y - containerTopLeft.y)
948
+ };
949
+ };
950
+ _this.getPointOnMedia = function(_a) {
951
+ var x = _a.x, y = _a.y;
952
+ var _b = _this.props, crop = _b.crop, zoom = _b.zoom;
953
+ return {
954
+ x: (x + crop.x) / zoom,
955
+ y: (y + crop.y) / zoom
956
+ };
957
+ };
958
+ _this.setNewZoom = function(zoom, point, _a) {
959
+ var _c = (_a === void 0 ? {} : _a).shouldUpdatePosition, shouldUpdatePosition = _c === void 0 ? true : _c;
960
+ if (!_this.state.cropSize || !_this.props.onZoomChange) return;
961
+ var newZoom = clamp(zoom, _this.props.minZoom, _this.props.maxZoom);
962
+ if (shouldUpdatePosition) {
963
+ var zoomPoint = _this.getPointOnContainer(point, _this.containerPosition);
964
+ var zoomTarget = _this.getPointOnMedia(zoomPoint);
965
+ var requestedPosition = {
966
+ x: zoomTarget.x * newZoom - zoomPoint.x,
967
+ y: zoomTarget.y * newZoom - zoomPoint.y
968
+ };
969
+ var newPosition = _this.props.restrictPosition ? restrictPosition(requestedPosition, _this.mediaSize, _this.state.cropSize, newZoom, _this.props.rotation) : requestedPosition;
970
+ _this.props.onCropChange(newPosition);
971
+ }
972
+ _this.props.onZoomChange(newZoom);
973
+ };
974
+ _this.getCropData = function() {
975
+ if (!_this.state.cropSize) return null;
976
+ return computeCroppedArea(_this.props.restrictPosition ? restrictPosition(_this.props.crop, _this.mediaSize, _this.state.cropSize, _this.props.zoom, _this.props.rotation) : _this.props.crop, _this.mediaSize, _this.state.cropSize, _this.getAspect(), _this.props.zoom, _this.props.rotation, _this.props.restrictPosition);
977
+ };
978
+ _this.emitCropData = function() {
979
+ var cropData = _this.getCropData();
980
+ if (!cropData) return;
981
+ var croppedAreaPercentages = cropData.croppedAreaPercentages, croppedAreaPixels = cropData.croppedAreaPixels;
982
+ if (_this.props.onCropComplete) _this.props.onCropComplete(croppedAreaPercentages, croppedAreaPixels);
983
+ if (_this.props.onCropAreaChange) _this.props.onCropAreaChange(croppedAreaPercentages, croppedAreaPixels);
984
+ };
985
+ _this.emitCropAreaChange = function() {
986
+ var cropData = _this.getCropData();
987
+ if (!cropData) return;
988
+ var croppedAreaPercentages = cropData.croppedAreaPercentages, croppedAreaPixels = cropData.croppedAreaPixels;
989
+ if (_this.props.onCropAreaChange) _this.props.onCropAreaChange(croppedAreaPercentages, croppedAreaPixels);
990
+ };
991
+ _this.recomputeCropPosition = function() {
992
+ var _a, _b;
993
+ if (!_this.state.cropSize) return;
994
+ var adjustedCrop = _this.props.crop;
995
+ if (_this.isInitialized && ((_a = _this.previousCropSize) === null || _a === void 0 ? void 0 : _a.width) && ((_b = _this.previousCropSize) === null || _b === void 0 ? void 0 : _b.height)) {
996
+ if (Math.abs(_this.previousCropSize.width - _this.state.cropSize.width) > 1e-6 || Math.abs(_this.previousCropSize.height - _this.state.cropSize.height) > 1e-6) {
997
+ var scaleX = _this.state.cropSize.width / _this.previousCropSize.width;
998
+ var scaleY = _this.state.cropSize.height / _this.previousCropSize.height;
999
+ adjustedCrop = {
1000
+ x: _this.props.crop.x * scaleX,
1001
+ y: _this.props.crop.y * scaleY
1002
+ };
1003
+ }
1004
+ }
1005
+ var newPosition = _this.props.restrictPosition ? restrictPosition(adjustedCrop, _this.mediaSize, _this.state.cropSize, _this.props.zoom, _this.props.rotation) : adjustedCrop;
1006
+ _this.previousCropSize = _this.state.cropSize;
1007
+ _this.props.onCropChange(newPosition);
1008
+ _this.emitCropData();
1009
+ };
1010
+ _this.onKeyDown = function(event) {
1011
+ var _a, _b;
1012
+ var _c = _this.props, crop = _c.crop, onCropChange = _c.onCropChange, keyboardStep = _c.keyboardStep, zoom = _c.zoom, rotation = _c.rotation;
1013
+ var step = keyboardStep;
1014
+ if (!_this.state.cropSize) return;
1015
+ if (event.shiftKey) step *= .2;
1016
+ var newCrop = __assign({}, crop);
1017
+ switch (event.key) {
1018
+ case "ArrowUp":
1019
+ newCrop.y -= step;
1020
+ event.preventDefault();
1021
+ break;
1022
+ case "ArrowDown":
1023
+ newCrop.y += step;
1024
+ event.preventDefault();
1025
+ break;
1026
+ case "ArrowLeft":
1027
+ newCrop.x -= step;
1028
+ event.preventDefault();
1029
+ break;
1030
+ case "ArrowRight":
1031
+ newCrop.x += step;
1032
+ event.preventDefault();
1033
+ break;
1034
+ default: return;
1035
+ }
1036
+ if (_this.props.restrictPosition) newCrop = restrictPosition(newCrop, _this.mediaSize, _this.state.cropSize, zoom, rotation);
1037
+ if (!event.repeat) (_b = (_a = _this.props).onInteractionStart) === null || _b === void 0 || _b.call(_a);
1038
+ onCropChange(newCrop);
1039
+ };
1040
+ _this.onKeyUp = function(event) {
1041
+ var _a, _b;
1042
+ switch (event.key) {
1043
+ case "ArrowUp":
1044
+ case "ArrowDown":
1045
+ case "ArrowLeft":
1046
+ case "ArrowRight":
1047
+ event.preventDefault();
1048
+ break;
1049
+ default: return;
1050
+ }
1051
+ _this.emitCropData();
1052
+ (_b = (_a = _this.props).onInteractionEnd) === null || _b === void 0 || _b.call(_a);
1053
+ };
1054
+ return _this;
1055
+ }
1056
+ Cropper.prototype.componentDidMount = function() {
1057
+ if (!this.currentDoc || !this.currentWindow) return;
1058
+ if (this.containerRef) {
1059
+ if (this.containerRef.ownerDocument) this.currentDoc = this.containerRef.ownerDocument;
1060
+ if (this.currentDoc.defaultView) this.currentWindow = this.currentDoc.defaultView;
1061
+ this.initResizeObserver();
1062
+ if (typeof window.ResizeObserver === "undefined") this.currentWindow.addEventListener("resize", this.computeSizes);
1063
+ this.props.zoomWithScroll && this.containerRef.addEventListener("wheel", this.onWheel, { passive: false });
1064
+ this.containerRef.addEventListener("gesturestart", this.onGestureStart);
1065
+ }
1066
+ this.currentDoc.addEventListener("scroll", this.onScroll);
1067
+ if (!this.props.disableAutomaticStylesInjection) {
1068
+ this.styleRef = this.currentDoc.createElement("style");
1069
+ this.styleRef.setAttribute("type", "text/css");
1070
+ if (this.props.nonce) this.styleRef.setAttribute("nonce", this.props.nonce);
1071
+ this.styleRef.innerHTML = css_248z;
1072
+ this.currentDoc.head.appendChild(this.styleRef);
1073
+ }
1074
+ if (this.imageRef.current && this.imageRef.current.complete) this.onMediaLoad();
1075
+ if (this.props.setImageRef) this.props.setImageRef(this.imageRef);
1076
+ if (this.props.setVideoRef) this.props.setVideoRef(this.videoRef);
1077
+ if (this.props.setCropperRef) this.props.setCropperRef(this.cropperRef);
1078
+ };
1079
+ Cropper.prototype.componentWillUnmount = function() {
1080
+ var _a, _b;
1081
+ if (!this.currentDoc || !this.currentWindow) return;
1082
+ if (typeof window.ResizeObserver === "undefined") this.currentWindow.removeEventListener("resize", this.computeSizes);
1083
+ (_a = this.resizeObserver) === null || _a === void 0 || _a.disconnect();
1084
+ if (this.containerRef) this.containerRef.removeEventListener("gesturestart", this.preventZoomSafari);
1085
+ if (this.styleRef) (_b = this.styleRef.parentNode) === null || _b === void 0 || _b.removeChild(this.styleRef);
1086
+ this.cleanEvents();
1087
+ this.props.zoomWithScroll && this.clearScrollEvent();
1088
+ };
1089
+ Cropper.prototype.componentDidUpdate = function(prevProps) {
1090
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1091
+ if (prevProps.rotation !== this.props.rotation) {
1092
+ this.computeSizes();
1093
+ this.recomputeCropPosition();
1094
+ } else if (prevProps.aspect !== this.props.aspect) this.computeSizes();
1095
+ else if (prevProps.objectFit !== this.props.objectFit) this.computeSizes();
1096
+ else if (prevProps.zoom !== this.props.zoom) this.recomputeCropPosition();
1097
+ else if (((_a = prevProps.cropSize) === null || _a === void 0 ? void 0 : _a.height) !== ((_b = this.props.cropSize) === null || _b === void 0 ? void 0 : _b.height) || ((_c = prevProps.cropSize) === null || _c === void 0 ? void 0 : _c.width) !== ((_d = this.props.cropSize) === null || _d === void 0 ? void 0 : _d.width)) this.computeSizes();
1098
+ else if (((_e = prevProps.crop) === null || _e === void 0 ? void 0 : _e.x) !== ((_f = this.props.crop) === null || _f === void 0 ? void 0 : _f.x) || ((_g = prevProps.crop) === null || _g === void 0 ? void 0 : _g.y) !== ((_h = this.props.crop) === null || _h === void 0 ? void 0 : _h.y)) this.emitCropAreaChange();
1099
+ if (prevProps.zoomWithScroll !== this.props.zoomWithScroll && this.containerRef) this.props.zoomWithScroll ? this.containerRef.addEventListener("wheel", this.onWheel, { passive: false }) : this.clearScrollEvent();
1100
+ if (prevProps.video !== this.props.video) (_j = this.videoRef.current) === null || _j === void 0 || _j.load();
1101
+ var objectFit = this.getObjectFit();
1102
+ if (objectFit !== this.state.mediaObjectFit) this.setState({ mediaObjectFit: objectFit }, this.computeSizes);
1103
+ };
1104
+ Cropper.prototype.getAspect = function() {
1105
+ var _a = this.props, cropSize = _a.cropSize, aspect = _a.aspect;
1106
+ if (cropSize) return cropSize.width / cropSize.height;
1107
+ return aspect;
1108
+ };
1109
+ Cropper.prototype.getObjectFit = function() {
1110
+ var _a, _b, _c, _d;
1111
+ if (this.props.objectFit === "cover") {
1112
+ if ((this.imageRef.current || this.videoRef.current) && this.containerRef) {
1113
+ this.containerRect = this.containerRef.getBoundingClientRect();
1114
+ var containerAspect = this.containerRect.width / this.containerRect.height;
1115
+ return (((_a = this.imageRef.current) === null || _a === void 0 ? void 0 : _a.naturalWidth) || ((_b = this.videoRef.current) === null || _b === void 0 ? void 0 : _b.videoWidth) || 0) / (((_c = this.imageRef.current) === null || _c === void 0 ? void 0 : _c.naturalHeight) || ((_d = this.videoRef.current) === null || _d === void 0 ? void 0 : _d.videoHeight) || 0) < containerAspect ? "horizontal-cover" : "vertical-cover";
1116
+ }
1117
+ return "horizontal-cover";
1118
+ }
1119
+ return this.props.objectFit;
1120
+ };
1121
+ Cropper.prototype.onPinchStart = function(e) {
1122
+ var pointA = Cropper.getTouchPoint(e.touches[0]);
1123
+ var pointB = Cropper.getTouchPoint(e.touches[1]);
1124
+ this.lastPinchDistance = getDistanceBetweenPoints(pointA, pointB);
1125
+ this.lastPinchRotation = getRotationBetweenPoints(pointA, pointB);
1126
+ this.onDragStart(getCenter(pointA, pointB));
1127
+ };
1128
+ Cropper.prototype.onPinchMove = function(e) {
1129
+ var _this = this;
1130
+ if (!this.currentDoc || !this.currentWindow) return;
1131
+ var pointA = Cropper.getTouchPoint(e.touches[0]);
1132
+ var pointB = Cropper.getTouchPoint(e.touches[1]);
1133
+ var center = getCenter(pointA, pointB);
1134
+ this.onDrag(center);
1135
+ if (this.rafPinchTimeout) this.currentWindow.cancelAnimationFrame(this.rafPinchTimeout);
1136
+ this.rafPinchTimeout = this.currentWindow.requestAnimationFrame(function() {
1137
+ var distance = getDistanceBetweenPoints(pointA, pointB);
1138
+ var newZoom = _this.props.zoom * (distance / _this.lastPinchDistance);
1139
+ _this.setNewZoom(newZoom, center, { shouldUpdatePosition: false });
1140
+ _this.lastPinchDistance = distance;
1141
+ var rotation = getRotationBetweenPoints(pointA, pointB);
1142
+ var newRotation = _this.props.rotation + (rotation - _this.lastPinchRotation);
1143
+ _this.props.onRotationChange && _this.props.onRotationChange(newRotation);
1144
+ _this.lastPinchRotation = rotation;
1145
+ });
1146
+ };
1147
+ Cropper.prototype.render = function() {
1148
+ var _this = this;
1149
+ var _a;
1150
+ var _b = this.props, image = _b.image, video = _b.video, mediaProps = _b.mediaProps, cropperProps = _b.cropperProps, transform = _b.transform, _c = _b.crop, x = _c.x, y = _c.y, rotation = _b.rotation, zoom = _b.zoom, cropShape = _b.cropShape, showGrid = _b.showGrid, roundCropAreaPixels = _b.roundCropAreaPixels, _d = _b.style, containerStyle = _d.containerStyle, cropAreaStyle = _d.cropAreaStyle, mediaStyle = _d.mediaStyle, _e = _b.classes, containerClassName = _e.containerClassName, cropAreaClassName = _e.cropAreaClassName, mediaClassName = _e.mediaClassName;
1151
+ var objectFit = (_a = this.state.mediaObjectFit) !== null && _a !== void 0 ? _a : this.getObjectFit();
1152
+ return react.createElement("div", {
1153
+ onMouseDown: this.onMouseDown,
1154
+ onTouchStart: this.onTouchStart,
1155
+ ref: function ref(el) {
1156
+ return _this.containerRef = el;
1157
+ },
1158
+ "data-testid": "container",
1159
+ style: containerStyle,
1160
+ className: classNames("reactEasyCrop_Container", containerClassName)
1161
+ }, image ? react.createElement("img", __assign({
1162
+ alt: "",
1163
+ className: classNames("reactEasyCrop_Image", objectFit === "contain" && "reactEasyCrop_Contain", objectFit === "horizontal-cover" && "reactEasyCrop_Cover_Horizontal", objectFit === "vertical-cover" && "reactEasyCrop_Cover_Vertical", mediaClassName)
1164
+ }, mediaProps, {
1165
+ src: image,
1166
+ ref: this.imageRef,
1167
+ style: __assign(__assign({}, mediaStyle), { transform: transform || "translate(".concat(x, "px, ").concat(y, "px) rotate(").concat(rotation, "deg) scale(").concat(zoom, ")") }),
1168
+ onLoad: this.onMediaLoad
1169
+ })) : video && react.createElement("video", __assign({
1170
+ autoPlay: true,
1171
+ playsInline: true,
1172
+ loop: true,
1173
+ muted: true,
1174
+ className: classNames("reactEasyCrop_Video", objectFit === "contain" && "reactEasyCrop_Contain", objectFit === "horizontal-cover" && "reactEasyCrop_Cover_Horizontal", objectFit === "vertical-cover" && "reactEasyCrop_Cover_Vertical", mediaClassName)
1175
+ }, mediaProps, {
1176
+ ref: this.videoRef,
1177
+ onLoadedMetadata: this.onMediaLoad,
1178
+ style: __assign(__assign({}, mediaStyle), { transform: transform || "translate(".concat(x, "px, ").concat(y, "px) rotate(").concat(rotation, "deg) scale(").concat(zoom, ")") }),
1179
+ controls: false
1180
+ }), (Array.isArray(video) ? video : [{ src: video }]).map(function(item) {
1181
+ return react.createElement("source", __assign({ key: item.src }, item));
1182
+ })), this.state.cropSize && react.createElement("div", __assign({
1183
+ ref: this.cropperRef,
1184
+ style: __assign(__assign({}, cropAreaStyle), {
1185
+ width: roundCropAreaPixels ? Math.round(this.state.cropSize.width) : this.state.cropSize.width,
1186
+ height: roundCropAreaPixels ? Math.round(this.state.cropSize.height) : this.state.cropSize.height
1187
+ }),
1188
+ tabIndex: 0,
1189
+ onKeyDown: this.onKeyDown,
1190
+ onKeyUp: this.onKeyUp,
1191
+ "data-testid": "cropper",
1192
+ className: classNames("reactEasyCrop_CropArea", cropShape === "round" && "reactEasyCrop_CropAreaRound", showGrid && "reactEasyCrop_CropAreaGrid", cropAreaClassName)
1193
+ }, cropperProps)));
1194
+ };
1195
+ Cropper.defaultProps = {
1196
+ zoom: 1,
1197
+ rotation: 0,
1198
+ aspect: 4 / 3,
1199
+ maxZoom: MAX_ZOOM,
1200
+ minZoom: MIN_ZOOM,
1201
+ cropShape: "rect",
1202
+ objectFit: "contain",
1203
+ showGrid: true,
1204
+ style: {},
1205
+ classes: {},
1206
+ mediaProps: {},
1207
+ cropperProps: {},
1208
+ zoomSpeed: 1,
1209
+ restrictPosition: true,
1210
+ zoomWithScroll: true,
1211
+ keyboardStep: KEYBOARD_STEP
1212
+ };
1213
+ Cropper.getMousePoint = function(e) {
1214
+ return {
1215
+ x: Number(e.clientX),
1216
+ y: Number(e.clientY)
1217
+ };
1218
+ };
1219
+ Cropper.getTouchPoint = function(touch) {
1220
+ return {
1221
+ x: Number(touch.clientX),
1222
+ y: Number(touch.clientY)
1223
+ };
1224
+ };
1225
+ return Cropper;
1226
+ }(react.Component);
1227
+ /** 与名称同一行的头像设置;资源或渲染器变化时销毁未确认的裁剪会话。 */
1228
+ function AssistantAvatarPortraitEditor(props) {
1229
+ const registry = require_manager.getAssistantAvatarRegistry(props.services), id = require_manager.resolveAssistantAvatarAsset(props.model, "dialog").renderer;
1230
+ const read = (0, react.useCallback)(() => registry.get(id), [registry, id]);
1231
+ const renderer = (0, react.useSyncExternalStore)(registry.subscribe, read, read);
1232
+ return (0, react.createElement)(PortraitEditorContent, {
1233
+ ...props,
1234
+ renderer,
1235
+ key: require_manager.assistantAvatarPortraitKey(props.model, renderer)
1236
+ });
1237
+ }
1238
+ function PortraitEditorContent({ services, model, renderer, disabled, t }) {
1239
+ const service = require_manager.getAssistantAvatarPortraitService(services);
1240
+ const [open, setOpen] = (0, react.useState)(false), [pending, setPending] = (0, react.useState)(false), [error, setError] = (0, react.useState)("");
1241
+ const [source, setSource] = (0, react.useState)();
1242
+ const [area, setArea] = (0, react.useState)(), [crop, setCrop] = (0, react.useState)({
1243
+ x: 0,
1244
+ y: 0
1245
+ }), [zoom, setZoom] = (0, react.useState)(1);
1246
+ const [percentages, setPercentages] = (0, react.useState)();
1247
+ const session = (0, react.useRef)();
1248
+ const release = (0, react.useCallback)(() => {
1249
+ session.current?.controller.abort();
1250
+ if (session.current?.url !== void 0) URL.revokeObjectURL(session.current.url);
1251
+ session.current = void 0;
1252
+ }, []);
1253
+ const close = (0, react.useCallback)(() => {
1254
+ release();
1255
+ setOpen(false);
1256
+ setSource(void 0);
1257
+ setArea(void 0);
1258
+ setPercentages(void 0);
1259
+ setPending(false);
1260
+ setError("");
1261
+ }, [release]);
1262
+ (0, react.useEffect)(() => release, [release]);
1263
+ (0, react.useEffect)(() => {
1264
+ if (disabled) close();
1265
+ }, [disabled, close]);
1266
+ const edit = async () => {
1267
+ if (disabled || pending) return;
1268
+ close();
1269
+ setOpen(true);
1270
+ setPending(true);
1271
+ setCrop({
1272
+ x: 0,
1273
+ y: 0
1274
+ });
1275
+ setZoom(1);
1276
+ const active = { controller: new AbortController() };
1277
+ session.current = active;
1278
+ try {
1279
+ const preview = await service.getSource(model, renderer, active.controller.signal);
1280
+ if (preview === void 0) throw new Error("avatar_portrait_source_unavailable");
1281
+ const initial = preview.cropAreaPercentages ?? await require_manager.initialAssistantAvatarPortraitArea(preview, active.controller.signal);
1282
+ if (active.controller.signal.aborted || session.current !== active) return;
1283
+ active.url = URL.createObjectURL(preview.blob);
1284
+ setSource({
1285
+ preview,
1286
+ url: active.url,
1287
+ ...initial === void 0 ? {} : { initial }
1288
+ });
1289
+ } catch {
1290
+ if (!active.controller.signal.aborted) setError(t("avatar.portraitSourceFailed"));
1291
+ } finally {
1292
+ if (session.current === active) setPending(false);
1293
+ }
1294
+ };
1295
+ const save = async () => {
1296
+ const active = session.current;
1297
+ if (disabled || pending || source === void 0 || area === void 0 || active === void 0) return;
1298
+ setPending(true);
1299
+ setError("");
1300
+ try {
1301
+ const preview = await require_manager.cropAssistantAvatarPortrait(source.preview, area, active.controller.signal);
1302
+ if (preview === void 0) throw new Error("avatar_portrait_crop_failed");
1303
+ await service.setPortrait(model, renderer, {
1304
+ ...preview,
1305
+ ...percentages === void 0 ? {} : { cropAreaPercentages: percentages }
1306
+ }, active.controller.signal);
1307
+ if (session.current === active) close();
1308
+ } catch {
1309
+ if (!active.controller.signal.aborted) setError(t("avatar.portraitSaveFailed"));
1310
+ } finally {
1311
+ if (session.current === active) setPending(false);
1312
+ }
1313
+ };
1314
+ const changedArea = (0, react.useCallback)((percent, pixels) => {
1315
+ setArea(pixels);
1316
+ setPercentages(percent);
1317
+ }, []);
1318
+ const locked = disabled || pending && source !== void 0;
1319
+ return (0, react.createElement)("div", {
1320
+ "data-codingns-portrait-settings": model.id,
1321
+ style: {
1322
+ ...require_assistant_workbench.assistantSettingTextStyle,
1323
+ display: "contents"
1324
+ }
1325
+ }, (0, react.createElement)("div", {
1326
+ "data-codingns-portrait-controls": true,
1327
+ style: {
1328
+ display: "grid",
1329
+ gap: 6,
1330
+ minWidth: 0
1331
+ }
1332
+ }, (0, react.createElement)("span", null, t("avatar.portraitTitle")), (0, react.createElement)("div", { style: {
1333
+ display: "flex",
1334
+ alignItems: "center",
1335
+ flexWrap: "wrap",
1336
+ gap: 12
1337
+ } }, (0, react.createElement)(require_manager.AssistantAvatarPortrait, {
1338
+ services,
1339
+ model,
1340
+ size: 44
1341
+ }), (0, react.createElement)("button", {
1342
+ type: "button",
1343
+ disabled: disabled || pending,
1344
+ "aria-expanded": open,
1345
+ "data-codingns-portrait-edit": true,
1346
+ style: require_theme.dshSettingsButtonStyle,
1347
+ onClick: () => {
1348
+ edit();
1349
+ }
1350
+ }, t("avatar.portraitEdit")))), (0, react.createElement)("p", { style: {
1351
+ ...require_theme.dshSettingsHelpStyle,
1352
+ margin: 0,
1353
+ gridColumn: "1 / -1"
1354
+ } }, t(services.configurationDraft ? "avatar.portraitDraftHint" : "avatar.portraitLocalHint")), !open ? null : (0, react.createElement)("div", {
1355
+ "data-codingns-portrait-editor": true,
1356
+ "aria-busy": pending,
1357
+ style: {
1358
+ display: "grid",
1359
+ gap: 12,
1360
+ padding: 12,
1361
+ gridColumn: "1 / -1",
1362
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
1363
+ borderRadius: 10,
1364
+ minWidth: 0
1365
+ }
1366
+ }, source === void 0 ? null : (0, react.createElement)("div", {
1367
+ "data-codingns-portrait-cropper": true,
1368
+ style: {
1369
+ position: "relative",
1370
+ height: 270,
1371
+ width: "100%",
1372
+ minWidth: 0,
1373
+ overflow: "hidden",
1374
+ borderRadius: 8,
1375
+ background: require_theme.dshThemeColor.surfaceSubtle,
1376
+ pointerEvents: locked ? "none" : "auto"
1377
+ }
1378
+ }, (0, react.createElement)(Cropper, {
1379
+ image: source.url,
1380
+ crop,
1381
+ zoom,
1382
+ aspect: 1,
1383
+ cropShape: "round",
1384
+ showGrid: false,
1385
+ minZoom: 1,
1386
+ maxZoom: 8,
1387
+ zoomWithScroll: false,
1388
+ onCropChange: (next) => {
1389
+ if (!locked) setCrop(next);
1390
+ },
1391
+ onZoomChange: (next) => {
1392
+ if (!locked) setZoom(next);
1393
+ },
1394
+ onCropAreaChange: changedArea,
1395
+ ...source.initial === void 0 ? {} : { initialCroppedAreaPercentages: source.initial },
1396
+ cropperProps: {
1397
+ "aria-label": t("avatar.portraitCropArea"),
1398
+ "aria-disabled": locked,
1399
+ tabIndex: locked ? -1 : 0
1400
+ }
1401
+ })), source === void 0 ? null : (0, react.createElement)("label", { style: {
1402
+ display: "grid",
1403
+ gap: 6
1404
+ } }, t("avatar.portraitZoom"), (0, react.createElement)("input", {
1405
+ type: "range",
1406
+ min: 1,
1407
+ max: 8,
1408
+ step: .01,
1409
+ value: zoom,
1410
+ disabled: locked,
1411
+ "data-codingns-portrait-zoom": true,
1412
+ onChange: (event) => setZoom(Number(event.currentTarget.value)),
1413
+ style: {
1414
+ width: "100%",
1415
+ margin: 0
1416
+ }
1417
+ })), source === void 0 || area === void 0 ? null : (0, react.createElement)("div", { style: {
1418
+ display: "flex",
1419
+ alignItems: "center",
1420
+ gap: 10
1421
+ } }, (0, react.createElement)("span", {
1422
+ "data-codingns-portrait-crop-preview": true,
1423
+ "aria-hidden": true,
1424
+ style: {
1425
+ position: "relative",
1426
+ overflow: "hidden",
1427
+ borderRadius: "50%",
1428
+ width: 56,
1429
+ height: 56,
1430
+ flex: "0 0 56px"
1431
+ }
1432
+ }, (0, react.createElement)("img", {
1433
+ src: source.url,
1434
+ alt: "",
1435
+ draggable: false,
1436
+ style: {
1437
+ position: "absolute",
1438
+ maxWidth: "none",
1439
+ width: source.preview.width / area.width * 56,
1440
+ height: source.preview.height / area.height * 56,
1441
+ left: -area.x / area.width * 56,
1442
+ top: -area.y / area.height * 56
1443
+ }
1444
+ })), (0, react.createElement)("span", { style: require_theme.dshSettingsHelpStyle }, t("avatar.portraitDragHint"))), !pending ? null : (0, react.createElement)("span", {
1445
+ role: "status",
1446
+ style: require_theme.dshSettingsHelpStyle
1447
+ }, t(source === void 0 ? "avatar.portraitLoading" : "avatar.portraitSaving")), error === "" ? null : (0, react.createElement)("span", {
1448
+ role: "alert",
1449
+ style: {
1450
+ ...require_theme.dshSettingsHelpStyle,
1451
+ color: require_theme.dshThemeColor.error
1452
+ }
1453
+ }, error), (0, react.createElement)("div", { style: {
1454
+ display: "flex",
1455
+ flexWrap: "wrap",
1456
+ justifyContent: "flex-end",
1457
+ gap: 8
1458
+ } }, (0, react.createElement)("button", {
1459
+ type: "button",
1460
+ disabled: locked,
1461
+ style: require_theme.dshSettingsButtonStyle,
1462
+ onClick: close
1463
+ }, t("avatar.portraitCancel")), (0, react.createElement)("button", {
1464
+ type: "button",
1465
+ disabled: disabled || pending || area === void 0,
1466
+ style: require_theme.dshSettingsPrimaryButtonStyle,
1467
+ "data-codingns-portrait-save": true,
1468
+ onClick: () => {
1469
+ save();
1470
+ }
1471
+ }, t(services.configurationDraft ? "avatar.portraitConfirm" : "avatar.portraitSave")))));
1472
+ }
1473
+ //#endregion
1474
+ //#region src/client/avatar/catalog-picker.ts
1475
+ var import_react_dom = require_react_dom$1.require_react_dom();
1476
+ /** 合并展示选项,沿用安装后的模型 ID 去重,不把目录元数据写进设置清单。 */
1477
+ function assistantAvatarChoices(appearance, entries, t, includeLegacy = true) {
1478
+ const choices = /* @__PURE__ */ new Map();
1479
+ for (const model of require_manager.listAssistantAvatars(appearance, includeLegacy)) {
1480
+ const builtinLabel = require_manager.builtinAssistantAvatarLabelKey(model.id);
1481
+ const labelKey = builtinLabel ?? (appearance.models.some((registered) => registered.id === model.id) ? void 0 : require_manager.getAssistantAvatarPreset(model.id)?.labelKey);
1482
+ const name = labelKey === void 0 ? model.name : t(labelKey);
1483
+ choices.set(model.id, {
1484
+ id: model.id,
1485
+ label: name,
1486
+ thirdParty: builtinLabel === void 0
1487
+ });
1488
+ }
1489
+ for (const entry of entries) {
1490
+ const id = `catalog-${entry.id}`;
1491
+ if (!choices.has(id)) choices.set(id, {
1492
+ id,
1493
+ label: entry.name,
1494
+ thirdParty: true,
1495
+ catalog: entry
1496
+ });
1497
+ }
1498
+ return [...choices.values()];
1499
+ }
1500
+ /** 内置形象只有名称,第三方形象在名称旁显示独立彩色标签。 */
1501
+ function AssistantAvatarChoiceLabel({ choice, t }) {
1502
+ return (0, react.createElement)("span", { style: {
1503
+ display: "flex",
1504
+ alignItems: "center",
1505
+ gap: 8,
1506
+ minWidth: 0
1507
+ } }, (0, react.createElement)("span", { style: {
1508
+ overflow: "hidden",
1509
+ textOverflow: "ellipsis",
1510
+ whiteSpace: "nowrap"
1511
+ } }, choice.label), !choice.thirdParty ? null : (0, react.createElement)("span", {
1512
+ "data-codingns-avatar-third-party-badge": true,
1513
+ style: {
1514
+ flexShrink: 0,
1515
+ padding: "1px 6px",
1516
+ borderRadius: 4,
1517
+ background: "#2563eb",
1518
+ color: "#fff",
1519
+ fontSize: 11,
1520
+ fontWeight: 500,
1521
+ lineHeight: "18px",
1522
+ whiteSpace: "nowrap"
1523
+ }
1524
+ }, t("avatar.thirdPartyBadge")));
1525
+ }
1526
+ /** 一个选择入口同时服务内置、已安装和待安装形象。 */
1527
+ function AssistantAvatarPicker(props) {
1528
+ const [open, setOpen] = (0, react.useState)(false);
1529
+ const id = (0, react.useId)();
1530
+ (0, react.useEffect)(() => {
1531
+ if (props.disabled) setOpen(false);
1532
+ }, [props.disabled]);
1533
+ return (0, react.createElement)(AssistantAvatarPickerView, {
1534
+ ...props,
1535
+ id,
1536
+ open,
1537
+ onOpenChange: setOpen
1538
+ });
1539
+ }
1540
+ /** 展开状态与展示分离;菜单行为由 DSH 原语负责,业务只处理有效选择。 */
1541
+ function AssistantAvatarPickerView({ choices, value, disabled, t, onChoose, label = t("avatar.selected"), id, open, onOpenChange }) {
1542
+ const selected = choices.find((choice) => choice.id === value);
1543
+ const expanded = open && !disabled;
1544
+ const name = selected === void 0 ? value : selected.thirdParty ? t("avatar.externalName", { name: selected.label }) : selected.label;
1545
+ const anchor = (0, react.createElement)("button", {
1546
+ id,
1547
+ type: "button",
1548
+ disabled,
1549
+ "aria-label": `${label}: ${name}`,
1550
+ "aria-haspopup": "menu",
1551
+ "aria-expanded": expanded,
1552
+ "data-codingns-avatar-list": true,
1553
+ "data-codingns-avatar-selected": value,
1554
+ style: {
1555
+ ...require_theme.dshSettingsFieldStyle,
1556
+ display: "flex",
1557
+ alignItems: "center",
1558
+ justifyContent: "space-between",
1559
+ gap: 8,
1560
+ textAlign: "left",
1561
+ cursor: disabled ? "default" : "pointer"
1562
+ },
1563
+ onClick: () => {
1564
+ if (!disabled) onOpenChange(!expanded);
1565
+ },
1566
+ onKeyDown: (event) => {
1567
+ if (disabled || expanded || event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
1568
+ event.preventDefault();
1569
+ onOpenChange(true);
1570
+ }
1571
+ }, selected === void 0 ? name : (0, react.createElement)(AssistantAvatarChoiceLabel, {
1572
+ choice: selected,
1573
+ t
1574
+ }), (0, react.createElement)("span", {
1575
+ "aria-hidden": true,
1576
+ style: {
1577
+ display: "flex",
1578
+ flexShrink: 0
1579
+ }
1580
+ }, (0, react.createElement)(require_primitives_adapter.resolveChevronDownIcon(), { size: 14 })));
1581
+ return (0, react.createElement)("label", {
1582
+ htmlFor: id,
1583
+ style: require_assistant_workbench.assistantSettingFieldStyle
1584
+ }, (0, react.createElement)("span", null, label), (0, react.createElement)("style", null, ".codingns4dsh-avatar-picker{width:100%;min-width:0}.codingns4dsh-avatar-menu{z-index:10002;width:min(360px,calc(100vw - 24px));max-width:calc(100vw - 24px)}"), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
1585
+ open: expanded,
1586
+ anchor,
1587
+ portal: true,
1588
+ autoFocus: true,
1589
+ className: "codingns4dsh-avatar-picker",
1590
+ listClassName: "codingns4dsh-avatar-menu",
1591
+ selectedId: value,
1592
+ items: choices.map((choice) => ({
1593
+ id: choice.id,
1594
+ label: (0, react.createElement)(AssistantAvatarChoiceLabel, {
1595
+ choice,
1596
+ t
1597
+ }),
1598
+ disabled: disabled || choice.disabled === true
1599
+ })),
1600
+ onClose: () => onOpenChange(false),
1601
+ onSelect: (choiceId) => {
1602
+ const choice = choices.find((item) => item.id === choiceId);
1603
+ if (disabled || choice === void 0 || choice.disabled) return;
1604
+ onOpenChange(false);
1605
+ onChoose(choice);
1606
+ }
1607
+ }));
1608
+ }
1609
+ //#endregion
1610
+ //#region src/client/avatar/engine.ts
1611
+ /**
1612
+ * 引擎只在用户选择 Live2D 形象时准备:先探测 Host 已就绪的引擎,
1613
+ * 未安装则确认许可后自动下载安装;安装失败不改变当前形象。
1614
+ */
1615
+ function useAssistantAvatarEngine(options) {
1616
+ const { services, manager, appearance, disabled, notify } = options;
1617
+ const t = require_transport.useCodingNsTranslator(services.locale);
1618
+ const [status, setStatus] = (0, react.useState)();
1619
+ const [installing, setInstalling] = (0, react.useState)(false);
1620
+ const [cancelled, setCancelled] = (0, react.useState)(false);
1621
+ const [error, setError] = (0, react.useState)("");
1622
+ const [consentOpen, setConsentOpen] = (0, react.useState)(false);
1623
+ const [agreed, setAgreed] = (0, react.useState)(false);
1624
+ const installer = (0, react.useRef)();
1625
+ const probe = (0, react.useRef)();
1626
+ const pending = (0, react.useRef)();
1627
+ const mounted = (0, react.useRef)(true);
1628
+ (0, react.useEffect)(() => {
1629
+ mounted.current = true;
1630
+ return () => {
1631
+ mounted.current = false;
1632
+ probe.current?.abort();
1633
+ installer.current?.abort();
1634
+ };
1635
+ }, []);
1636
+ (0, react.useEffect)(() => {
1637
+ const controller = new AbortController();
1638
+ manager.engineStatus(controller.signal).then((value) => {
1639
+ if (!controller.signal.aborted && mounted.current) setStatus(value);
1640
+ }).catch(() => void 0);
1641
+ return () => controller.abort();
1642
+ }, [manager]);
1643
+ const runInstall = async (onReady) => {
1644
+ if (disabled || !mounted.current || installer.current !== void 0) return false;
1645
+ const controller = new AbortController();
1646
+ installer.current = controller;
1647
+ pending.current = onReady;
1648
+ setInstalling(true);
1649
+ setError("");
1650
+ setCancelled(false);
1651
+ try {
1652
+ const next = await manager.installEngine(controller.signal);
1653
+ if (controller.signal.aborted) return false;
1654
+ setStatus(next);
1655
+ await onReady();
1656
+ pending.current = void 0;
1657
+ notify?.({
1658
+ kind: "success",
1659
+ message: t("avatar.engineInstalled")
1660
+ });
1661
+ return true;
1662
+ } catch (failure) {
1663
+ if (!controller.signal.aborted && mounted.current) setError(errorMessage$2(failure));
1664
+ return false;
1665
+ } finally {
1666
+ if (installer.current === controller) installer.current = void 0;
1667
+ if (mounted.current) setInstalling(false);
1668
+ }
1669
+ };
1670
+ const ensure = async (onReady) => {
1671
+ if (disabled || installing || !mounted.current || installer.current !== void 0) return false;
1672
+ probe.current?.abort();
1673
+ const controller = new AbortController();
1674
+ probe.current = controller;
1675
+ setError("");
1676
+ let current = status;
1677
+ try {
1678
+ current = await manager.engineStatus(controller.signal);
1679
+ } catch {} finally {
1680
+ if (probe.current === controller) probe.current = void 0;
1681
+ }
1682
+ if (controller.signal.aborted || !mounted.current) return false;
1683
+ if (current !== void 0) setStatus(current);
1684
+ if (current?.installed === true) {
1685
+ await onReady();
1686
+ return true;
1687
+ }
1688
+ if (!require_manager.hasAssistantAvatarEngineConsent(appearance.engineConsent)) {
1689
+ pending.current = onReady;
1690
+ setAgreed(false);
1691
+ setConsentOpen(true);
1692
+ return false;
1693
+ }
1694
+ return await runInstall(onReady);
1695
+ };
1696
+ return {
1697
+ status,
1698
+ installing,
1699
+ cancelled,
1700
+ error,
1701
+ consentOpen,
1702
+ agreed,
1703
+ ensure,
1704
+ setAgreed: (value) => {
1705
+ setAgreed(value);
1706
+ },
1707
+ acceptConsent: async () => {
1708
+ try {
1709
+ await manager.setEngineEnabled(true);
1710
+ if (!mounted.current) return;
1711
+ setConsentOpen(false);
1712
+ setAgreed(false);
1713
+ const next = pending.current;
1714
+ if (next !== void 0) await runInstall(next);
1715
+ } catch (failure) {
1716
+ if (mounted.current) setError(errorMessage$2(failure));
1717
+ }
1718
+ },
1719
+ cancelConsent: () => {
1720
+ pending.current = void 0;
1721
+ setAgreed(false);
1722
+ setConsentOpen(false);
1723
+ },
1724
+ cancelInstall: () => {
1725
+ if (installer.current !== void 0) {
1726
+ setCancelled(true);
1727
+ installer.current.abort();
1728
+ }
1729
+ },
1730
+ retry: () => {
1731
+ const next = pending.current;
1732
+ if (next !== void 0) runInstall(next);
1733
+ }
1734
+ };
1735
+ }
1736
+ /** 许可确认使用原生模态;未同意时不产生任何下载或设置写入。 */
1737
+ function AssistantAvatarEngineDialog({ controller, t }) {
1738
+ const ref = (0, react.useRef)(null);
1739
+ const id = (0, react.useId)();
1740
+ const open = controller.consentOpen;
1741
+ (0, react.useEffect)(() => {
1742
+ if (!open) return;
1743
+ const opener = document.activeElement;
1744
+ const dialog = ref.current;
1745
+ dialog?.showModal();
1746
+ return () => {
1747
+ if (dialog?.open) dialog.close();
1748
+ if (opener?.isConnected) opener.focus();
1749
+ };
1750
+ }, [open]);
1751
+ if (!open) return null;
1752
+ return (0, react.createElement)("dialog", {
1753
+ ref,
1754
+ "aria-labelledby": id,
1755
+ "data-codingns-avatar-engine-dialog": true,
1756
+ onCancel: (event) => {
1757
+ event.preventDefault();
1758
+ controller.cancelConsent();
1759
+ },
1760
+ onKeyDown: (event) => {
1761
+ if (event.key === "Escape") {
1762
+ event.preventDefault();
1763
+ event.stopPropagation();
1764
+ controller.cancelConsent();
1765
+ }
1766
+ },
1767
+ style: {
1768
+ maxWidth: "min(480px, calc(100vw - 32px))",
1769
+ maxHeight: "calc(100dvh - 32px)",
1770
+ overflowY: "auto",
1771
+ boxSizing: "border-box",
1772
+ padding: 24,
1773
+ borderRadius: 16,
1774
+ margin: "auto",
1775
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
1776
+ background: require_theme.dshThemeColor.pageBackground,
1777
+ color: require_theme.dshThemeColor.labelPrimary,
1778
+ boxShadow: require_theme.dshThemeColor.subtleShadow
1779
+ }
1780
+ }, (0, react.createElement)("strong", {
1781
+ id,
1782
+ style: { fontSize: 16 }
1783
+ }, t("avatar.engineTerms", { version: require_manager.ASSISTANT_AVATAR_ENGINE_CONSENT_VERSION })), (0, react.createElement)("div", { style: {
1784
+ display: "grid",
1785
+ gap: 16,
1786
+ marginTop: 14
1787
+ } }, (0, react.createElement)("p", { style: {
1788
+ ...require_theme.dshSettingsHelpStyle,
1789
+ margin: 0,
1790
+ lineHeight: 1.7
1791
+ } }, t("avatar.engineTermsContent")), (0, react.createElement)("label", { style: require_assistant_workbench.assistantSettingCheckboxStyle }, (0, react.createElement)("input", {
1792
+ type: "checkbox",
1793
+ checked: controller.agreed,
1794
+ onChange: (event) => controller.setAgreed(event.currentTarget.checked)
1795
+ }), t("avatar.engineAgree")), (0, react.createElement)("div", { style: {
1796
+ display: "flex",
1797
+ justifyContent: "flex-end",
1798
+ gap: 8
1799
+ } }, (0, react.createElement)("button", {
1800
+ type: "button",
1801
+ autoFocus: true,
1802
+ style: require_theme.dshSettingsButtonStyle,
1803
+ onClick: controller.cancelConsent
1804
+ }, t("avatar.cancel")), (0, react.createElement)("button", {
1805
+ type: "button",
1806
+ disabled: !controller.agreed,
1807
+ style: require_theme.dshSettingsPrimaryButtonStyle,
1808
+ onClick: () => {
1809
+ if (controller.agreed) controller.acceptConsent();
1810
+ }
1811
+ }, t("avatar.engineConfirm")))));
1812
+ }
1813
+ /** 安装中显示不确定进度与取消;失败显示原因和重试,不改变当前形象。 */
1814
+ function AssistantAvatarEngineProgress({ controller, t }) {
1815
+ if (controller.installing) return (0, react.createElement)("div", {
1816
+ role: "status",
1817
+ "data-codingns-avatar-engine-progress": true,
1818
+ style: {
1819
+ display: "grid",
1820
+ gap: 8
1821
+ }
1822
+ }, (0, react.createElement)("span", null, t("avatar.engineInstalling")), (0, react.createElement)("progress", {
1823
+ "aria-label": t("avatar.engineInstalling"),
1824
+ style: { width: "100%" }
1825
+ }), (0, react.createElement)("button", {
1826
+ type: "button",
1827
+ style: {
1828
+ ...require_theme.dshSettingsButtonStyle,
1829
+ justifySelf: "start"
1830
+ },
1831
+ onClick: controller.cancelInstall
1832
+ }, t("avatar.engineCancel")));
1833
+ if (controller.error === "") {
1834
+ if (!controller.cancelled) return null;
1835
+ return (0, react.createElement)("div", {
1836
+ role: "status",
1837
+ "data-codingns-avatar-engine-cancelled": true,
1838
+ style: {
1839
+ display: "grid",
1840
+ gap: 8
1841
+ }
1842
+ }, (0, react.createElement)("span", null, t("avatar.engineCancelled")), (0, react.createElement)("button", {
1843
+ type: "button",
1844
+ style: {
1845
+ ...require_theme.dshSettingsButtonStyle,
1846
+ justifySelf: "start"
1847
+ },
1848
+ onClick: controller.retry
1849
+ }, t("avatar.engineRetry")));
1850
+ }
1851
+ return (0, react.createElement)("div", {
1852
+ role: "alert",
1853
+ "data-codingns-avatar-engine-error": true,
1854
+ style: {
1855
+ display: "grid",
1856
+ gap: 8,
1857
+ color: require_theme.dshThemeColor.error,
1858
+ fontSize: 13
1859
+ }
1860
+ }, (0, react.createElement)("span", { style: { overflowWrap: "anywhere" } }, t("avatar.engineInstallFailed", { reason: controller.error })), (0, react.createElement)("button", {
1861
+ type: "button",
1862
+ style: {
1863
+ ...require_theme.dshSettingsButtonStyle,
1864
+ justifySelf: "start",
1865
+ color: require_theme.dshThemeColor.error
1866
+ },
1867
+ onClick: controller.retry
1868
+ }, t("avatar.engineRetry")));
1869
+ }
1870
+ function errorMessage$2(error) {
1871
+ return error instanceof Error ? error.message : String(error);
1872
+ }
1873
+ //#endregion
1874
+ //#region src/client/avatar/temporary-preview.ts
1875
+ /** 挂载即启动预览;离开后隔离所有回调,避免旧下载覆盖新形象或关闭后的界面。 */
1876
+ function startAssistantAvatarTemporaryPreview(manager, entry, observer) {
1877
+ let active = true;
1878
+ const session = new AssistantAvatarTemporaryPreviewSession(manager, (error) => {
1879
+ if (active) observer.onError(error);
1880
+ });
1881
+ session.load(entry).then((preview) => {
1882
+ if (active) observer.onLoaded(preview);
1883
+ }).catch((error) => {
1884
+ if (active) observer.onError(error);
1885
+ }).finally(() => {
1886
+ if (active) observer.onSettled();
1887
+ });
1888
+ return { dispose: async () => {
1889
+ active = false;
1890
+ await session.dispose();
1891
+ } };
1892
+ }
1893
+ /** 客户端拥有一个临时预览租约,取消先释放,迟到答复再次释放,绝不登记到设置。 */
1894
+ var AssistantAvatarTemporaryPreviewSession = class {
1895
+ manager;
1896
+ onExpired;
1897
+ lease;
1898
+ controller = new AbortController();
1899
+ disposed = false;
1900
+ timer;
1901
+ constructor(manager, onExpired = () => {}, lease = crypto.randomUUID()) {
1902
+ this.manager = manager;
1903
+ this.onExpired = onExpired;
1904
+ this.lease = lease;
1905
+ }
1906
+ async load(entry) {
1907
+ this.controller.signal.throwIfAborted();
1908
+ try {
1909
+ this.timer = setInterval(() => {
1910
+ this.manager.keepPreview(this.lease).catch((error) => {
1911
+ if (this.disposed) return;
1912
+ this.onExpired(error);
1913
+ this.dispose().catch(() => void 0);
1914
+ });
1915
+ }, 6e4);
1916
+ const preview = await this.manager.previewCatalog(entry.id, entry.revision, this.lease, this.controller.signal);
1917
+ if (this.disposed) {
1918
+ await this.manager.releasePreview(this.lease);
1919
+ this.controller.signal.throwIfAborted();
1920
+ }
1921
+ return preview;
1922
+ } catch (error) {
1923
+ await this.dispose().catch(() => void 0);
1924
+ throw error;
1925
+ }
1926
+ }
1927
+ async dispose() {
1928
+ if (this.disposed) return;
1929
+ this.disposed = true;
1930
+ this.controller.abort();
1931
+ if (this.timer !== void 0) clearInterval(this.timer);
1932
+ await this.manager.releasePreview(this.lease);
1933
+ }
1934
+ };
1935
+ //#endregion
1936
+ //#region src/client/avatar/catalog-panel.ts
1937
+ const buttonStyle = {
1938
+ ...require_theme.dshSettingsButtonStyle,
1939
+ justifySelf: "start"
1940
+ };
1941
+ const helpStyle$1 = {
1942
+ ...require_theme.dshSettingsHelpStyle,
1943
+ margin: 0
1944
+ };
1945
+ const checkStyle = require_assistant_workbench.assistantSettingCheckboxStyle;
1946
+ /** 目录与已登记角色共用选择入口,协议和素材安装仍分别确认。 */
1947
+ function AssistantAvatarCatalogPanel(props) {
1948
+ const { services, manager, appearance, disabled, notify, active = true, previewTarget, onPreviewChange } = props;
1949
+ const t = require_transport.useCodingNsTranslator(services.locale);
1950
+ const [agreed, setAgreed] = (0, react.useState)(false);
1951
+ const [agreementOpen, setAgreementOpen] = (0, react.useState)(false);
1952
+ const [pending, setPending] = (0, react.useState)(false);
1953
+ const [installing, setInstalling] = (0, react.useState)(false);
1954
+ const [preview, setPreview] = (0, react.useState)();
1955
+ const [error, setError] = (0, react.useState)("");
1956
+ const accepted = require_manager.hasAssistantAvatarConsent(appearance.thirdPartyConsent);
1957
+ const consent = accepted ? `${appearance.thirdPartyConsent.version}:${appearance.thirdPartyConsent.acceptedAt}` : "";
1958
+ const catalog = useAssistantAvatarCatalog(manager, consent);
1959
+ const engine = useAssistantAvatarEngine({
1960
+ services,
1961
+ manager,
1962
+ appearance,
1963
+ disabled,
1964
+ notify
1965
+ });
1966
+ const current = require_manager.selectedAssistantAvatar(appearance);
1967
+ const choices = assistantAvatarChoices(appearance, catalog.entries, t);
1968
+ const selected = preview?.consent === consent ? catalog.entries.find((entry) => entry.id === preview.id) : void 0;
1969
+ (0, react.useEffect)(() => {
1970
+ onPreviewChange?.(active ? selected : void 0);
1971
+ return () => onPreviewChange?.(void 0);
1972
+ }, [
1973
+ active,
1974
+ selected,
1975
+ onPreviewChange
1976
+ ]);
1977
+ const busy = disabled || pending || installing || engine.installing;
1978
+ const choose = async (choice) => {
1979
+ if (busy) return;
1980
+ setError("");
1981
+ setPending(true);
1982
+ try {
1983
+ const select = async () => {
1984
+ if (choice.catalog !== void 0) {
1985
+ const id = choice.catalog.id;
1986
+ setPreview((current) => ({
1987
+ id,
1988
+ consent,
1989
+ selection: (current?.selection ?? 0) + 1
1990
+ }));
1991
+ return;
1992
+ }
1993
+ setPreview(void 0);
1994
+ await manager.select(choice.id);
1995
+ if (!services.configurationDraft) notify({
1996
+ kind: "success",
1997
+ message: t("avatar.saved")
1998
+ });
1999
+ };
2000
+ if (choice.catalog === void 0 ? manager.list().find((model) => model.id === choice.id)?.renderer === "live2d" : ["dsh-live2d-pet", "cubism-model"].includes(choice.catalog.format)) await engine.ensure(select);
2001
+ else await select();
2002
+ } catch (failure) {
2003
+ setError(errorMessage$1(failure));
2004
+ notify({
2005
+ kind: "error",
2006
+ message: errorMessage$1(failure)
2007
+ });
2008
+ } finally {
2009
+ setPending(false);
2010
+ }
2011
+ };
2012
+ const changeConsent = async (enabled) => {
2013
+ if (busy || enabled && !agreed) return;
2014
+ setPending(true);
2015
+ setError("");
2016
+ try {
2017
+ await manager.setThirdPartyEnabled(enabled);
2018
+ setAgreed(false);
2019
+ setAgreementOpen(false);
2020
+ } catch (failure) {
2021
+ setError(errorMessage$1(failure));
2022
+ notify({
2023
+ kind: "error",
2024
+ message: errorMessage$1(failure)
2025
+ });
2026
+ } finally {
2027
+ setPending(false);
2028
+ }
2029
+ };
2030
+ const previewNode = selected === void 0 || !active ? null : (0, react.createElement)(AssistantAvatarCatalogPreview, {
2031
+ ...props,
2032
+ selected,
2033
+ disabled: disabled || pending,
2034
+ key: `${consent}:${selected.id}:${selected.revision}:${preview?.selection}`,
2035
+ onPending: setInstalling,
2036
+ onInstalled: () => setPreview(void 0)
2037
+ });
2038
+ return (0, react.createElement)("section", {
2039
+ "data-codingns-third-party-avatars": true,
2040
+ "aria-label": t("avatar.thirdPartyTitle"),
2041
+ style: {
2042
+ ...require_assistant_workbench.assistantSettingTextStyle,
2043
+ display: "grid",
2044
+ gap: 20,
2045
+ ...previewNode !== null && !previewTarget ? { gridTemplateColumns: "repeat(auto-fit,minmax(min(100%,260px),1fr))" } : {}
2046
+ }
2047
+ }, (0, react.createElement)("div", { style: {
2048
+ display: "grid",
2049
+ gap: 12,
2050
+ alignContent: "start"
2051
+ } }, (0, react.createElement)(AssistantAvatarPicker, {
2052
+ choices,
2053
+ value: selected === void 0 ? appearance.selectedId : `catalog-${selected.id}`,
2054
+ disabled: busy,
2055
+ t,
2056
+ onChoose: (choice) => {
2057
+ choose(choice);
2058
+ }
2059
+ }), current.renderer !== "live2d" || engine.status?.installed !== false ? null : (0, react.createElement)("div", {
2060
+ "data-codingns-avatar-engine-missing": true,
2061
+ style: {
2062
+ display: "grid",
2063
+ gap: 8
2064
+ }
2065
+ }, (0, react.createElement)("p", { style: helpStyle$1 }, t("avatar.engineMissing")), (0, react.createElement)("button", {
2066
+ type: "button",
2067
+ disabled: busy,
2068
+ style: buttonStyle,
2069
+ onClick: () => {
2070
+ engine.ensure(() => void 0);
2071
+ }
2072
+ }, t("avatar.engineInstall"))), (0, react.createElement)(AssistantAvatarEngineProgress, {
2073
+ controller: engine,
2074
+ t
2075
+ }), (0, react.createElement)(AssistantAvatarThirdPartyToggle, {
2076
+ accepted,
2077
+ disabled: busy,
2078
+ t,
2079
+ onRequest: () => {
2080
+ setAgreed(false);
2081
+ setAgreementOpen(true);
2082
+ },
2083
+ onDisable: () => {
2084
+ changeConsent(false);
2085
+ }
2086
+ }), !agreementOpen ? null : (0, react.createElement)(AssistantAvatarConsentDialog, {
2087
+ agreed,
2088
+ disabled: busy,
2089
+ t,
2090
+ onChange: setAgreed,
2091
+ onAccept: () => {
2092
+ changeConsent(true);
2093
+ },
2094
+ onCancel: () => {
2095
+ if (!pending) {
2096
+ setAgreementOpen(false);
2097
+ setAgreed(false);
2098
+ }
2099
+ }
2100
+ }), !catalog.loading ? null : (0, react.createElement)("div", {
2101
+ role: "status",
2102
+ style: helpStyle$1
2103
+ }, t("avatar.catalogLoading")), !accepted || catalog.loading || catalog.entries.length > 0 || catalog.error ? null : (0, react.createElement)("p", { style: helpStyle$1 }, t("avatar.catalogEmpty")), !catalog.error ? null : (0, react.createElement)("div", {
2104
+ role: "alert",
2105
+ style: {
2106
+ display: "grid",
2107
+ gap: 8,
2108
+ color: require_theme.dshThemeColor.error,
2109
+ fontSize: 13
2110
+ }
2111
+ }, catalog.error, (0, react.createElement)("button", {
2112
+ type: "button",
2113
+ disabled: busy || catalog.loading,
2114
+ style: buttonStyle,
2115
+ onClick: catalog.reload
2116
+ }, t("avatar.catalogRetry"))), !error ? null : (0, react.createElement)("div", {
2117
+ role: "alert",
2118
+ style: {
2119
+ color: require_theme.dshThemeColor.error,
2120
+ fontSize: 13
2121
+ }
2122
+ }, error), (0, react.createElement)(AssistantAvatarEngineDialog, {
2123
+ controller: engine,
2124
+ t
2125
+ })), previewNode === null ? null : (0, react.createElement)(AssistantAvatarCatalogPreviewRegion, {
2126
+ target: previewTarget,
2127
+ children: previewNode
2128
+ }));
2129
+ }
2130
+ /** 预览 DOM 归右侧目标所有,独立设置入口则提供同级侧栏。 */
2131
+ function AssistantAvatarCatalogPreviewRegion({ target, children }) {
2132
+ return target ? (0, import_react_dom.createPortal)(children, target) : (0, react.createElement)("aside", { "data-codingns-avatar-preview-region": true }, children);
2133
+ }
2134
+ /** 仅有效协议允许读取元数据;协议变化和卸载会取消旧请求并隔离迟到答复。 */
2135
+ function useAssistantAvatarCatalog(manager, consent) {
2136
+ const [retry, setRetry] = (0, react.useState)(0);
2137
+ const [catalog, setCatalog] = (0, react.useState)({
2138
+ consent: "",
2139
+ entries: [],
2140
+ loading: false,
2141
+ error: ""
2142
+ });
2143
+ (0, react.useEffect)(() => {
2144
+ if (!consent) return;
2145
+ const controller = new AbortController();
2146
+ setCatalog({
2147
+ consent,
2148
+ entries: [],
2149
+ loading: true,
2150
+ error: ""
2151
+ });
2152
+ manager.getCatalog(controller.signal).then((entries) => {
2153
+ if (!controller.signal.aborted) setCatalog({
2154
+ consent,
2155
+ entries,
2156
+ loading: false,
2157
+ error: ""
2158
+ });
2159
+ }).catch((failure) => {
2160
+ if (!controller.signal.aborted) setCatalog({
2161
+ consent,
2162
+ entries: [],
2163
+ loading: false,
2164
+ error: errorMessage$1(failure)
2165
+ });
2166
+ });
2167
+ return () => controller.abort();
2168
+ }, [
2169
+ manager,
2170
+ consent,
2171
+ retry
2172
+ ]);
2173
+ const current = consent !== "" && catalog.consent === consent;
2174
+ return {
2175
+ entries: current ? catalog.entries : [],
2176
+ loading: consent !== "" && (!current || catalog.loading),
2177
+ error: current ? catalog.error : "",
2178
+ reload: () => setRetry((value) => value + 1)
2179
+ };
2180
+ }
2181
+ /** 勾选只请求展示协议,明确同意后立即加载目录;取消不会产生任何设置写入。 */
2182
+ function AssistantAvatarThirdPartyToggle({ accepted, disabled, t, onRequest, onDisable }) {
2183
+ return (0, react.createElement)("label", { style: require_assistant_workbench.assistantSettingSwitchStyle }, (0, react.createElement)("span", null, t("avatar.thirdPartyEnable")), (0, react.createElement)("input", {
2184
+ type: "checkbox",
2185
+ role: "switch",
2186
+ "aria-label": t("avatar.thirdPartyEnable"),
2187
+ checked: accepted,
2188
+ disabled,
2189
+ "data-codingns-third-party-enabled": true,
2190
+ onChange: (event) => {
2191
+ if (!disabled) {
2192
+ if (event.currentTarget.checked) onRequest();
2193
+ else onDisable();
2194
+ }
2195
+ }
2196
+ }));
2197
+ }
2198
+ /** 原生模态对话框限制背景焦点,关闭后恢复到触发协议的复选框。 */
2199
+ function AssistantAvatarConsentDialog(props) {
2200
+ const ref = (0, react.useRef)(null);
2201
+ const id = (0, react.useId)();
2202
+ (0, react.useEffect)(() => {
2203
+ const opener = document.activeElement;
2204
+ const dialog = ref.current;
2205
+ dialog?.showModal();
2206
+ return () => {
2207
+ if (dialog?.open) dialog.close();
2208
+ if (opener?.isConnected) opener.focus();
2209
+ };
2210
+ }, []);
2211
+ return (0, react.createElement)("dialog", {
2212
+ ref,
2213
+ "aria-labelledby": id,
2214
+ "data-codingns-avatar-consent-dialog": true,
2215
+ onCancel: (event) => {
2216
+ event.preventDefault();
2217
+ props.onCancel();
2218
+ },
2219
+ onKeyDown: (event) => {
2220
+ if (event.key === "Escape") {
2221
+ event.preventDefault();
2222
+ event.stopPropagation();
2223
+ props.onCancel();
2224
+ }
2225
+ },
2226
+ style: {
2227
+ maxWidth: "min(480px, calc(100vw - 32px))",
2228
+ maxHeight: "calc(100dvh - 32px)",
2229
+ overflowY: "auto",
2230
+ boxSizing: "border-box",
2231
+ padding: 24,
2232
+ borderRadius: 16,
2233
+ margin: "auto",
2234
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
2235
+ background: require_theme.dshThemeColor.pageBackground,
2236
+ color: require_theme.dshThemeColor.labelPrimary,
2237
+ boxShadow: require_theme.dshThemeColor.subtleShadow
2238
+ }
2239
+ }, (0, react.createElement)("strong", {
2240
+ id,
2241
+ style: { fontSize: 16 }
2242
+ }, props.t("avatar.thirdPartyTerms", { version: require_manager.ASSISTANT_AVATAR_CONSENT_VERSION })), (0, react.createElement)(AssistantAvatarConsentPrompt, props));
2243
+ }
2244
+ function AssistantAvatarConsentPrompt({ agreed, disabled, t, onChange, onAccept, onCancel }) {
2245
+ return (0, react.createElement)("div", { style: {
2246
+ display: "grid",
2247
+ gap: 16,
2248
+ marginTop: 14
2249
+ } }, (0, react.createElement)("p", { style: {
2250
+ ...helpStyle$1,
2251
+ lineHeight: 1.7
2252
+ } }, t("avatar.thirdPartyTermsContent")), (0, react.createElement)("label", { style: checkStyle }, (0, react.createElement)("input", {
2253
+ type: "checkbox",
2254
+ checked: agreed,
2255
+ disabled,
2256
+ onChange: (event) => {
2257
+ if (!disabled) onChange(event.currentTarget.checked);
2258
+ }
2259
+ }), t("avatar.thirdPartyAgree")), (0, react.createElement)("div", { style: {
2260
+ display: "flex",
2261
+ justifyContent: "flex-end",
2262
+ gap: 8
2263
+ } }, (0, react.createElement)("button", {
2264
+ type: "button",
2265
+ autoFocus: true,
2266
+ disabled,
2267
+ style: require_theme.dshSettingsButtonStyle,
2268
+ onClick: onCancel
2269
+ }, t("avatar.cancel")), (0, react.createElement)("button", {
2270
+ type: "button",
2271
+ disabled: disabled || !agreed,
2272
+ style: require_theme.dshSettingsPrimaryButtonStyle,
2273
+ onClick: () => {
2274
+ if (!disabled && agreed) onAccept();
2275
+ }
2276
+ }, t("avatar.thirdPartyConfirm"))));
2277
+ }
2278
+ function AssistantAvatarCatalogPreview({ services, manager, appearance, disabled, notify, selected, onPending, onInstalled }) {
2279
+ const t = require_transport.useCodingNsTranslator(services.locale);
2280
+ const [licenseAccepted, setLicenseAccepted] = (0, react.useState)(false);
2281
+ const [temporary, setTemporary] = (0, react.useState)();
2282
+ const [previewLoading, setPreviewLoading] = (0, react.useState)(!disabled);
2283
+ const [pending, setPending] = (0, react.useState)(false);
2284
+ const [error, setError] = (0, react.useState)("");
2285
+ const installer = (0, react.useRef)();
2286
+ (0, react.useEffect)(() => () => {
2287
+ installer.current?.abort();
2288
+ onPending(false);
2289
+ }, [manager, onPending]);
2290
+ (0, react.useEffect)(() => {
2291
+ setTemporary(void 0);
2292
+ setPreviewLoading(!disabled);
2293
+ setError("");
2294
+ if (disabled) return;
2295
+ const preview = startAssistantAvatarTemporaryPreview(manager, {
2296
+ id: selected.id,
2297
+ revision: selected.revision
2298
+ }, {
2299
+ onLoaded: setTemporary,
2300
+ onError: (failure) => {
2301
+ setTemporary(void 0);
2302
+ setPreviewLoading(false);
2303
+ setError(errorMessage$1(failure));
2304
+ },
2305
+ onSettled: () => setPreviewLoading(false)
2306
+ });
2307
+ return () => {
2308
+ preview.dispose().catch(() => void 0);
2309
+ };
2310
+ }, [
2311
+ manager,
2312
+ disabled,
2313
+ selected.id,
2314
+ selected.revision
2315
+ ]);
2316
+ const installed = appearance.models.some((model) => model.id === `catalog-${selected.id}` && model.package?.installationId !== void 0);
2317
+ const full = appearance.models.length >= 21 && !installed;
2318
+ const install = async () => {
2319
+ if (disabled || pending || previewLoading || !licenseAccepted || full) return;
2320
+ const controller = new AbortController();
2321
+ installer.current = controller;
2322
+ setPending(true);
2323
+ onPending(true);
2324
+ setError("");
2325
+ try {
2326
+ await manager.installCatalog(selected.id, selected.revision, licenseAccepted, controller.signal, temporary?.lease);
2327
+ if (!controller.signal.aborted) {
2328
+ setLicenseAccepted(false);
2329
+ if (!services.configurationDraft) notify({
2330
+ kind: "success",
2331
+ message: t("avatar.saved")
2332
+ });
2333
+ onInstalled();
2334
+ }
2335
+ } catch (failure) {
2336
+ if (!controller.signal.aborted) {
2337
+ setError(errorMessage$1(failure));
2338
+ notify({
2339
+ kind: "error",
2340
+ message: errorMessage$1(failure)
2341
+ });
2342
+ }
2343
+ } finally {
2344
+ if (installer.current === controller) installer.current = void 0;
2345
+ if (!controller.signal.aborted) {
2346
+ setPending(false);
2347
+ onPending(false);
2348
+ }
2349
+ }
2350
+ };
2351
+ return (0, react.createElement)("div", {
2352
+ style: {
2353
+ display: "grid",
2354
+ gap: 12
2355
+ },
2356
+ "data-codingns-avatar-catalog-preview": true
2357
+ }, (0, react.createElement)("div", {
2358
+ style: {
2359
+ display: "grid",
2360
+ gap: 10
2361
+ },
2362
+ "data-codingns-avatar-catalog-selection": selected.id
2363
+ }, (0, react.createElement)("div", {
2364
+ "data-codingns-avatar-temporary-preview": true,
2365
+ "aria-busy": previewLoading || pending,
2366
+ style: {
2367
+ minHeight: 182,
2368
+ display: "grid",
2369
+ justifyItems: "center",
2370
+ alignItems: "center"
2371
+ }
2372
+ }, temporary === void 0 ? previewLoading || pending ? (0, react.createElement)("div", { style: {
2373
+ position: "relative",
2374
+ width: 168,
2375
+ height: 182,
2376
+ maxWidth: "100%"
2377
+ } }, (0, react.createElement)(require_manager.AssistantAvatarLoading, {
2378
+ progress: { phase: "resources" },
2379
+ size: 168,
2380
+ t,
2381
+ animationOnly: true
2382
+ })) : (0, react.createElement)("p", {
2383
+ role: "status",
2384
+ style: {
2385
+ ...helpStyle$1,
2386
+ textAlign: "center"
2387
+ }
2388
+ }, t(error ? "avatar.temporaryLoadFailed" : "avatar.temporaryUnavailable")) : (0, react.createElement)(require_manager.AssistantAvatarSlot, {
2389
+ services,
2390
+ model: temporary.model,
2391
+ state: "idle",
2392
+ surface: "dialog",
2393
+ size: 168,
2394
+ transient: true,
2395
+ showDiagnostics: false
2396
+ })), (0, react.createElement)("p", { style: helpStyle$1 }, t("avatar.catalogPreviewHint")), (0, react.createElement)("strong", null, selected.name), (0, react.createElement)("p", { style: helpStyle$1 }, selected.description), (0, react.createElement)("div", { style: helpStyle$1 }, t("avatar.packageAuthor", { author: selected.author })), (0, react.createElement)("div", { style: helpStyle$1 }, t("avatar.packageLicense", { license: selected.license })), (0, react.createElement)("p", { style: helpStyle$1 }, selected.remarks), (0, react.createElement)("div", { style: helpStyle$1 }, t("avatar.catalogSize", {
2397
+ size: `${(selected.bytes / 1024 / 1024).toFixed(2)} MB`,
2398
+ files: selected.files
2399
+ })), (0, react.createElement)("div", { style: {
2400
+ display: "flex",
2401
+ flexWrap: "wrap",
2402
+ gap: 14,
2403
+ fontSize: 13
2404
+ } }, (0, react.createElement)("a", {
2405
+ href: selected.repositoryUrl,
2406
+ target: "_blank",
2407
+ rel: "noopener noreferrer"
2408
+ }, t("avatar.packageSource")), (0, react.createElement)("a", {
2409
+ href: selected.licenseUrl,
2410
+ target: "_blank",
2411
+ rel: "noopener noreferrer"
2412
+ }, t("avatar.catalogLicenseLink")), (0, react.createElement)("a", {
2413
+ href: selected.homepage,
2414
+ target: "_blank",
2415
+ rel: "noopener noreferrer"
2416
+ }, t("avatar.packageHomepage"))), selected.format === "codex-pet" ? null : (0, react.createElement)("p", { style: helpStyle$1 }, t("avatar.live2dDependencyHint")), installed ? (0, react.createElement)("span", { style: {
2417
+ color: require_theme.dshThemeColor.success,
2418
+ fontSize: 13
2419
+ } }, t("avatar.catalogInstalled")) : null, (0, react.createElement)("label", { style: checkStyle }, (0, react.createElement)("input", {
2420
+ type: "checkbox",
2421
+ checked: licenseAccepted,
2422
+ disabled: disabled || pending,
2423
+ onChange: (event) => setLicenseAccepted(event.currentTarget.checked)
2424
+ }), t("avatar.catalogLicenseAgree")), (0, react.createElement)("button", {
2425
+ type: "button",
2426
+ disabled: disabled || pending || previewLoading || !licenseAccepted || full,
2427
+ style: buttonStyle,
2428
+ onClick: () => {
2429
+ install();
2430
+ }
2431
+ }, t("avatar.temporaryUse"))), !error ? null : (0, react.createElement)("div", {
2432
+ role: "alert",
2433
+ style: {
2434
+ color: require_theme.dshThemeColor.error,
2435
+ fontSize: 13
2436
+ }
2437
+ }, error));
2438
+ }
2439
+ function errorMessage$1(error) {
2440
+ return error instanceof Error ? error.message : String(error);
2441
+ }
2442
+ //#endregion
2443
+ //#region src/client/avatar/settings-panel.ts
2444
+ /** 对话内复用设置页表单;订阅同一存储并把保存反馈留在当前窗口。 */
2445
+ function AssistantAppearanceEditor({ services, enabled, ...previewProps }) {
2446
+ const store = services.settings;
2447
+ const subscribe = (0, react.useCallback)((listener) => store.subscribe(listener), [store]);
2448
+ const getSnapshot = (0, react.useCallback)(() => store.getSnapshot(), [store]);
2449
+ const snapshot = (0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
2450
+ const [notice, setNotice] = (0, react.useState)();
2451
+ return (0, react.createElement)("div", {
2452
+ "data-codingns-avatar-settings-editor": true,
2453
+ style: {
2454
+ display: "grid",
2455
+ gap: 10
2456
+ }
2457
+ }, (0, react.createElement)(AssistantAppearancePanel, {
2458
+ services,
2459
+ enabled,
2460
+ snapshot,
2461
+ notify: setNotice,
2462
+ ...previewProps
2463
+ }), notice?.kind !== "success" ? null : (0, react.createElement)("div", {
2464
+ role: "status",
2465
+ style: {
2466
+ fontSize: 13,
2467
+ color: require_theme.dshThemeColor.success
2468
+ }
2469
+ }, notice.message));
2470
+ }
2471
+ /** 保存只写 appearance 子字段,不覆盖其他窗口刚改过的语音或受管范围。 */
2472
+ function AssistantAppearancePanel({ services, enabled, snapshot, notify, ...previewProps }) {
2473
+ const t = require_transport.useCodingNsTranslator(services.locale);
2474
+ const manager = require_manager.getAssistantAvatarManager(services);
2475
+ const appearance = (0, react.useSyncExternalStore)(manager.subscribe, manager.getSnapshot, manager.getSnapshot);
2476
+ const selected = require_manager.selectedAssistantAvatar(appearance);
2477
+ const registry = require_manager.getAssistantAvatarRegistry(services);
2478
+ const adapters = (0, react.useSyncExternalStore)(manager.adapters.subscribe, manager.adapters.getSnapshot, manager.adapters.getSnapshot);
2479
+ const renderers = (0, react.useSyncExternalStore)(registry.subscribe, registry.getSnapshot, registry.getSnapshot);
2480
+ const [pending, setPending] = (0, react.useState)(false);
2481
+ const [name, setName] = (0, react.useState)("");
2482
+ const [source, setSource] = (0, react.useState)("");
2483
+ const [renderer, setRenderer] = (0, react.useState)("image");
2484
+ const [version, setVersion] = (0, react.useState)(2);
2485
+ const [error, setError] = (0, react.useState)("");
2486
+ const [manifestUrl, setManifestUrl] = (0, react.useState)("");
2487
+ const [adapterId, setAdapterId] = (0, react.useState)("auto");
2488
+ const [candidates, setCandidates] = (0, react.useState)([]);
2489
+ const [candidateUrl, setCandidateUrl] = (0, react.useState)("");
2490
+ const [addMode, setAddMode] = (0, react.useState)("package");
2491
+ const [customSize, setCustomSize] = (0, react.useState)(false);
2492
+ const importer = (0, react.useRef)();
2493
+ (0, react.useEffect)(() => () => importer.current?.abort(), [manager]);
2494
+ const selectedAdapterId = adapters.some((adapter) => adapter.id === adapterId) ? adapterId : "auto";
2495
+ const selectedRenderer = renderers.some((item) => item.id === renderer && item.id !== "builtin") ? renderer : "image";
2496
+ const disabled = !enabled || pending || snapshot.status !== "ready" || !snapshot.writable;
2497
+ const engine = useAssistantAvatarEngine({
2498
+ services,
2499
+ manager,
2500
+ appearance,
2501
+ disabled,
2502
+ notify
2503
+ });
2504
+ const save = async (action, message = t("avatar.saved")) => {
2505
+ if (disabled) return false;
2506
+ setPending(true);
2507
+ setError("");
2508
+ try {
2509
+ await action();
2510
+ if (!services.configurationDraft) notify({
2511
+ kind: "success",
2512
+ message
2513
+ });
2514
+ return true;
2515
+ } catch (failure) {
2516
+ if (failure instanceof Error && failure.name === "AbortError") {
2517
+ notify({
2518
+ kind: "info",
2519
+ message: t("avatar.cancelled")
2520
+ });
2521
+ return false;
2522
+ }
2523
+ const message = failure instanceof Error ? failure.message : String(failure);
2524
+ setError(message);
2525
+ notify({
2526
+ kind: "error",
2527
+ message
2528
+ });
2529
+ return false;
2530
+ } finally {
2531
+ setPending(false);
2532
+ }
2533
+ };
2534
+ const add = async () => {
2535
+ if (!require_manager.isAssistantAvatarSource(source.trim())) {
2536
+ setError(t("avatar.invalidSource"));
2537
+ return;
2538
+ }
2539
+ const model = {
2540
+ id: `avatar-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`,
2541
+ name: name.trim() || t("avatar.customName"),
2542
+ renderer: selectedRenderer,
2543
+ source: source.trim(),
2544
+ spriteVersion: version
2545
+ };
2546
+ if (selectedRenderer === "live2d" ? await engine.ensure(async () => {
2547
+ await manager.add(model);
2548
+ if (!services.configurationDraft) notify({
2549
+ kind: "success",
2550
+ message: t("avatar.saved")
2551
+ });
2552
+ }) : await save(() => manager.add(model))) {
2553
+ setName("");
2554
+ setSource("");
2555
+ }
2556
+ };
2557
+ const importPackage = async () => {
2558
+ if (!require_manager.isAssistantAvatarSource(manifestUrl.trim())) {
2559
+ setError(t("avatar.invalidSource"));
2560
+ return;
2561
+ }
2562
+ const controller = new AbortController();
2563
+ importer.current = controller;
2564
+ const accepted = await save(async () => {
2565
+ const discovered = await manager.discover(manifestUrl.trim(), controller.signal);
2566
+ setCandidates(discovered);
2567
+ setCandidateUrl(discovered[0]?.url ?? "");
2568
+ }, t("avatar.discovered"));
2569
+ if (importer.current === controller) importer.current = void 0;
2570
+ if (!accepted) {
2571
+ setCandidates([]);
2572
+ setCandidateUrl("");
2573
+ }
2574
+ };
2575
+ const installPackage = async (url) => {
2576
+ const controller = new AbortController();
2577
+ importer.current = controller;
2578
+ const accepted = await save(() => manager.installPackage(url, selectedAdapterId, controller.signal));
2579
+ if (importer.current === controller) importer.current = void 0;
2580
+ if (accepted) {
2581
+ setManifestUrl("");
2582
+ setCandidates([]);
2583
+ setCandidateUrl("");
2584
+ }
2585
+ };
2586
+ const field = (label, input) => (0, react.createElement)("label", { style: require_assistant_workbench.assistantSettingFieldStyle }, (0, react.createElement)("span", null, label), input);
2587
+ const toggle = (key, label) => (0, react.createElement)("label", { style: require_assistant_workbench.assistantSettingSwitchStyle }, (0, react.createElement)("span", null, label), (0, react.createElement)("input", {
2588
+ type: "checkbox",
2589
+ role: "switch",
2590
+ "aria-label": label,
2591
+ checked: appearance[key],
2592
+ disabled,
2593
+ onChange: (event) => {
2594
+ const checked = event.currentTarget.checked;
2595
+ save(() => manager.configure({ [key]: checked }));
2596
+ }
2597
+ }));
2598
+ const sizeField = (key, min, max, label) => field(label, (0, react.createElement)("input", {
2599
+ key: appearance[key],
2600
+ type: "number",
2601
+ min,
2602
+ max,
2603
+ step: 1,
2604
+ defaultValue: appearance[key],
2605
+ disabled,
2606
+ "aria-label": label,
2607
+ style: require_theme.dshSettingsFieldStyle,
2608
+ onBlur: (event) => {
2609
+ const value = Number(event.currentTarget.value);
2610
+ if (Number.isInteger(value) && value >= min && value <= max && value !== appearance[key]) save(() => manager.configure({ [key]: value }));
2611
+ else event.currentTarget.value = String(appearance[key]);
2612
+ }
2613
+ }));
2614
+ return (0, react.createElement)("section", {
2615
+ "aria-label": t("avatar.settingsTitle"),
2616
+ style: {
2617
+ ...require_assistant_workbench.assistantSettingTextStyle,
2618
+ display: "grid",
2619
+ gap: 12,
2620
+ borderTop: `1px solid ${require_theme.dshThemeColor.border}`,
2621
+ paddingTop: 14
2622
+ }
2623
+ }, (0, react.createElement)("strong", { style: { fontSize: 14 } }, t("avatar.settingsTitle")), (0, react.createElement)("p", { style: {
2624
+ ...require_theme.dshSettingsHelpStyle,
2625
+ margin: 0
2626
+ } }, t("avatar.settingsDescription")), (0, react.createElement)(AssistantAvatarCatalogPanel, {
2627
+ services,
2628
+ manager,
2629
+ appearance,
2630
+ disabled,
2631
+ notify,
2632
+ ...previewProps
2633
+ }), (0, react.createElement)("p", { style: {
2634
+ ...require_theme.dshSettingsHelpStyle,
2635
+ margin: 0
2636
+ } }, t("avatar.externalHint")), selected.renderer !== "live2d" ? null : (0, react.createElement)("p", { style: {
2637
+ ...require_theme.dshSettingsHelpStyle,
2638
+ margin: 0
2639
+ } }, t("avatar.live2dDependencyHint")), selected.package === void 0 ? null : (0, react.createElement)("div", {
2640
+ "data-codingns-avatar-package-info": true,
2641
+ style: {
2642
+ ...require_theme.dshSettingsHelpStyle,
2643
+ overflowWrap: "anywhere"
2644
+ }
2645
+ }, (0, react.createElement)("div", null, t("avatar.packageAuthor", { author: selected.package.author || t("avatar.notDeclared") })), (0, react.createElement)("div", null, t("avatar.packageLicense", { license: selected.package.license || t("avatar.notDeclared") })), selected.package.manifestUrl === void 0 ? null : (0, react.createElement)("a", {
2646
+ href: selected.package.manifestUrl,
2647
+ target: "_blank",
2648
+ rel: "noopener noreferrer"
2649
+ }, t("avatar.packageSource")), selected.package.homepage === void 0 ? null : (0, react.createElement)("div", null, (0, react.createElement)("a", {
2650
+ href: selected.package.homepage,
2651
+ target: "_blank",
2652
+ rel: "noopener noreferrer"
2653
+ }, t("avatar.packageHomepage")))), toggle("floatingEnabled", t("avatar.floatingEnabled")), (0, react.createElement)(AssistantFloatingSizeControl, {
2654
+ size: appearance.floatingSize,
2655
+ custom: customSize,
2656
+ disabled,
2657
+ t,
2658
+ onPreset: (preset) => {
2659
+ setCustomSize(preset === "custom");
2660
+ if (preset !== "custom") save(() => manager.configure({ floatingSize: preset === "mini" ? 72 : 144 }));
2661
+ },
2662
+ onSize: (floatingSize) => {
2663
+ save(() => manager.configure({ floatingSize }));
2664
+ }
2665
+ }), toggle("dialogEnabled", t("avatar.dialogEnabled")), sizeField("dialogSize", 120, 480, t("avatar.dialogSize")), require_manager.getBuiltinAssistantAvatar(appearance.selectedId) !== void 0 ? null : (0, react.createElement)("button", {
2666
+ type: "button",
2667
+ disabled,
2668
+ style: {
2669
+ ...require_theme.dshSettingsButtonStyle,
2670
+ justifySelf: "start"
2671
+ },
2672
+ onClick: () => {
2673
+ const id = appearance.selectedId;
2674
+ save(() => manager.remove(id));
2675
+ }
2676
+ }, t("avatar.remove")), selected.package?.manifestUrl === void 0 || selected.package.installationId !== void 0 ? null : (0, react.createElement)("button", {
2677
+ type: "button",
2678
+ disabled,
2679
+ style: {
2680
+ ...require_theme.dshSettingsButtonStyle,
2681
+ justifySelf: "start"
2682
+ },
2683
+ onClick: () => {
2684
+ installPackage(selected.package.manifestUrl);
2685
+ }
2686
+ }, t("avatar.installCurrent")), (0, react.createElement)("details", { "data-codingns-avatar-add": true }, (0, react.createElement)("summary", { style: {
2687
+ cursor: "pointer",
2688
+ fontSize: 13
2689
+ } }, t("avatar.add")), field(t("avatar.addMode"), (0, react.createElement)("select", {
2690
+ value: addMode,
2691
+ disabled,
2692
+ style: {
2693
+ ...require_theme.dshSettingsFieldStyle,
2694
+ marginTop: 12
2695
+ },
2696
+ "data-codingns-avatar-add-mode": true,
2697
+ onChange: (event) => setAddMode(event.currentTarget.value === "custom" ? "custom" : "package")
2698
+ }, (0, react.createElement)("option", { value: "package" }, t("avatar.importPackage")), (0, react.createElement)("option", { value: "custom" }, t("avatar.customMaterial")))), addMode !== "package" ? null : (0, react.createElement)("div", {
2699
+ "data-codingns-avatar-package-import": true,
2700
+ style: {
2701
+ display: "grid",
2702
+ gap: 12,
2703
+ marginTop: 12
2704
+ }
2705
+ }, field(t("avatar.adapter"), (0, react.createElement)("select", {
2706
+ value: selectedAdapterId,
2707
+ disabled,
2708
+ style: require_theme.dshSettingsFieldStyle,
2709
+ onChange: (event) => setAdapterId(event.currentTarget.value)
2710
+ }, (0, react.createElement)("option", { value: "auto" }, t("avatar.adapter.auto")), ...adapters.map((adapter) => (0, react.createElement)("option", {
2711
+ key: adapter.id,
2712
+ value: adapter.id
2713
+ }, adapter.labelKey === void 0 ? adapter.name ?? adapter.id : t(adapter.labelKey))))), field(t("avatar.manifestUrl"), (0, react.createElement)("input", {
2714
+ value: manifestUrl,
2715
+ disabled,
2716
+ maxLength: 2048,
2717
+ style: require_theme.dshSettingsFieldStyle,
2718
+ onChange: (event) => {
2719
+ setManifestUrl(event.currentTarget.value);
2720
+ setCandidates([]);
2721
+ setCandidateUrl("");
2722
+ }
2723
+ })), (0, react.createElement)("p", { style: {
2724
+ ...require_theme.dshSettingsHelpStyle,
2725
+ margin: 0
2726
+ } }, t("avatar.importHint")), (0, react.createElement)("button", {
2727
+ type: "button",
2728
+ disabled,
2729
+ style: {
2730
+ ...require_theme.dshSettingsButtonStyle,
2731
+ justifySelf: "start"
2732
+ },
2733
+ onClick: () => {
2734
+ importPackage();
2735
+ }
2736
+ }, pending && importer.current !== void 0 ? t("avatar.importing") : t("avatar.discover")), candidates.length === 0 ? null : field(t("avatar.installCandidate"), (0, react.createElement)("select", {
2737
+ value: candidateUrl,
2738
+ disabled,
2739
+ style: require_theme.dshSettingsFieldStyle,
2740
+ onChange: (event) => setCandidateUrl(event.currentTarget.value)
2741
+ }, ...candidates.map((candidate) => (0, react.createElement)("option", {
2742
+ key: candidate.url,
2743
+ value: candidate.url
2744
+ }, candidate.name)))), candidates.length === 0 ? null : (0, react.createElement)("button", {
2745
+ type: "button",
2746
+ disabled: disabled || appearance.models.length >= 21,
2747
+ style: {
2748
+ ...require_theme.dshSettingsButtonStyle,
2749
+ justifySelf: "start"
2750
+ },
2751
+ onClick: () => {
2752
+ installPackage(candidateUrl);
2753
+ }
2754
+ }, t("avatar.importUse")), !pending || importer.current === void 0 ? null : (0, react.createElement)("div", {
2755
+ role: "status",
2756
+ style: {
2757
+ display: "grid",
2758
+ gap: 8
2759
+ }
2760
+ }, (0, react.createElement)("span", null, t("avatar.installProgress")), (0, react.createElement)("progress", {
2761
+ "aria-label": t("avatar.installProgress"),
2762
+ style: { width: "100%" }
2763
+ }), (0, react.createElement)("button", {
2764
+ type: "button",
2765
+ style: {
2766
+ ...require_theme.dshSettingsButtonStyle,
2767
+ justifySelf: "start"
2768
+ },
2769
+ onClick: () => importer.current?.abort()
2770
+ }, t("avatar.cancel")))), addMode !== "custom" ? null : (0, react.createElement)("div", {
2771
+ "data-codingns-avatar-custom-add": true,
2772
+ style: {
2773
+ display: "grid",
2774
+ gap: 12,
2775
+ marginTop: 12
2776
+ }
2777
+ }, field(t("avatar.name"), (0, react.createElement)("input", {
2778
+ value: name,
2779
+ disabled,
2780
+ maxLength: 80,
2781
+ style: require_theme.dshSettingsFieldStyle,
2782
+ onChange: (event) => setName(event.currentTarget.value)
2783
+ })), field(t("avatar.type"), (0, react.createElement)("select", {
2784
+ value: selectedRenderer,
2785
+ disabled,
2786
+ style: require_theme.dshSettingsFieldStyle,
2787
+ onChange: (event) => setRenderer(event.currentTarget.value)
2788
+ }, ...renderers.filter((item) => item.id !== "builtin").map((item) => (0, react.createElement)("option", {
2789
+ key: item.id,
2790
+ value: item.id
2791
+ }, item.labelKey === void 0 ? item.name ?? item.id : t(item.labelKey))))), field(t("avatar.source"), (0, react.createElement)("input", {
2792
+ value: source,
2793
+ disabled,
2794
+ maxLength: 2048,
2795
+ type: "text",
2796
+ style: require_theme.dshSettingsFieldStyle,
2797
+ onChange: (event) => setSource(event.currentTarget.value)
2798
+ })), (0, react.createElement)("p", { style: {
2799
+ ...require_theme.dshSettingsHelpStyle,
2800
+ margin: 0
2801
+ } }, t([
2802
+ "image",
2803
+ "spritesheet",
2804
+ "live2d"
2805
+ ].includes(selectedRenderer) ? `avatar.sourceHint.${selectedRenderer}` : "avatar.sourceHint.custom")), selectedRenderer !== "live2d" ? null : (0, react.createElement)("p", { style: {
2806
+ ...require_theme.dshSettingsHelpStyle,
2807
+ margin: 0
2808
+ } }, t("avatar.live2dDependencyHint")), selectedRenderer !== "spritesheet" ? null : field(t("avatar.spriteVersion"), (0, react.createElement)("select", {
2809
+ value: version,
2810
+ disabled,
2811
+ style: require_theme.dshSettingsFieldStyle,
2812
+ onChange: (event) => setVersion(event.currentTarget.value === "1" ? 1 : 2)
2813
+ }, (0, react.createElement)("option", { value: 1 }, "v1 · 1536×1872"), (0, react.createElement)("option", { value: 2 }, "v2 · 1536×2288"))), (0, react.createElement)("button", {
2814
+ type: "button",
2815
+ disabled: disabled || appearance.models.length >= 21,
2816
+ onClick: () => {
2817
+ add();
2818
+ },
2819
+ style: {
2820
+ ...require_theme.dshSettingsButtonStyle,
2821
+ justifySelf: "start"
2822
+ }
2823
+ }, t("avatar.addUse")))), (0, react.createElement)(AssistantAvatarEngineProgress, {
2824
+ controller: engine,
2825
+ t
2826
+ }), error === "" ? null : (0, react.createElement)("div", {
2827
+ role: "alert",
2828
+ style: {
2829
+ color: require_theme.dshThemeColor.error,
2830
+ fontSize: 13
2831
+ }
2832
+ }, error), (0, react.createElement)(AssistantAvatarEngineDialog, {
2833
+ controller: engine,
2834
+ t
2835
+ }));
2836
+ }
2837
+ /** 以数值判断旧配置的档位;选择自定义时才展开输入框,不增加持久字段。 */
2838
+ function AssistantFloatingSizeControl({ size, custom, disabled, t, onPreset, onSize }) {
2839
+ const preset = custom ? "custom" : size === 72 ? "mini" : size === 144 ? "standard" : "custom";
2840
+ const label = t("avatar.floatingSize");
2841
+ return (0, react.createElement)("div", {
2842
+ "data-codingns-avatar-floating-size": true,
2843
+ style: {
2844
+ display: "grid",
2845
+ gap: 8
2846
+ }
2847
+ }, (0, react.createElement)("label", { style: require_assistant_workbench.assistantSettingFieldStyle }, label, (0, react.createElement)("select", {
2848
+ value: preset,
2849
+ disabled,
2850
+ "aria-label": label,
2851
+ style: require_theme.dshSettingsFieldStyle,
2852
+ onChange: (event) => {
2853
+ const next = event.currentTarget.value;
2854
+ if (next === "mini" || next === "standard" || next === "custom") onPreset(next);
2855
+ }
2856
+ }, (0, react.createElement)("option", { value: "mini" }, t("avatar.size.mini")), (0, react.createElement)("option", { value: "standard" }, t("avatar.size.standard")), (0, react.createElement)("option", { value: "custom" }, t("avatar.size.custom")))), preset !== "custom" ? null : (0, react.createElement)("label", { style: require_assistant_workbench.assistantSettingFieldStyle }, t("avatar.customSize"), (0, react.createElement)("input", {
2857
+ key: size,
2858
+ type: "number",
2859
+ min: 72,
2860
+ max: 320,
2861
+ step: 1,
2862
+ defaultValue: size,
2863
+ disabled,
2864
+ "aria-label": t("avatar.customSize"),
2865
+ style: require_theme.dshSettingsFieldStyle,
2866
+ onBlur: (event) => {
2867
+ const value = Number(event.currentTarget.value);
2868
+ if (!disabled && Number.isInteger(value) && value >= 72 && value <= 320 && value !== size) onSize(value);
2869
+ else event.currentTarget.value = String(size);
2870
+ }
2871
+ })));
2872
+ }
2873
+ //#endregion
2874
+ //#region src/client/features/assistant-voice-settings.ts
2875
+ /** 设置页和对话窗口共用独立音色编辑器,不与形象包或识别模型混合。 */
2876
+ function AssistantVoiceSettings({ services, enabled = true, active = true, embedded = false }) {
2877
+ const t = require_transport.useCodingNsTranslator(services.locale);
2878
+ const [snapshot, setSnapshot] = (0, react.useState)();
2879
+ const [selectedId, setSelectedId] = (0, react.useState)("moss:Junhao");
2880
+ const [parameters, setParameters] = (0, react.useState)({ ...require_manager.DEFAULT_ASSISTANT_TTS_PARAMETERS });
2881
+ const [siteId, setSiteId] = (0, react.useState)("kyutai");
2882
+ const [source, setSource] = (0, react.useState)("");
2883
+ const [name, setName] = (0, react.useState)("");
2884
+ const [language, setLanguage] = (0, react.useState)("unknown");
2885
+ const [gender, setGender] = (0, react.useState)("unknown");
2886
+ const [busy, setBusy] = (0, react.useState)();
2887
+ const [error, setError] = (0, react.useState)("");
2888
+ const [writable, setWritable] = (0, react.useState)(() => services.settings.getSnapshot().writable);
2889
+ const alive = (0, react.useRef)(false);
2890
+ const pending = (0, react.useRef)(false);
2891
+ const reading = (0, react.useRef)(false);
2892
+ const abort = (0, react.useRef)();
2893
+ const player = (0, react.useRef)();
2894
+ if (player.current === void 0) player.current = new require_react_dom$1.MossVoiceOutput();
2895
+ (0, react.useEffect)(() => {
2896
+ if (!active) player.current?.cancel();
2897
+ }, [active]);
2898
+ const voices = snapshot?.voices ?? require_manager.MOSS_BUILTIN_VOICES;
2899
+ const selected = voices.find((voice) => voice.id === selectedId) ?? voices[0];
2900
+ const site = require_manager.ASSISTANT_VOICE_SITES.find((site) => site.id === siteId) ?? require_manager.ASSISTANT_VOICE_SITES[0];
2901
+ const disabled = !enabled || !writable || busy !== void 0 || snapshot?.status.busy === true;
2902
+ const ready = snapshot?.status.ready === true;
2903
+ const backend = snapshot?.settings.backend ?? services.settings.getSnapshot().value?.assistant?.tts?.backend ?? "browser";
2904
+ const moss = backend === "moss-onnx";
2905
+ const refresh = (0, react.useCallback)(async () => {
2906
+ if (reading.current) return;
2907
+ reading.current = true;
2908
+ try {
2909
+ const result = await services.rpc.call(require_transport.CODINGNS_RPC_CHANNEL, "assistant/tts/catalog", {}, abort.current?.signal);
2910
+ if (!result.ok) throw new Error(result.error.message);
2911
+ if (alive.current) setSnapshot(result.value);
2912
+ } catch (error) {
2913
+ if (alive.current) setError(message$2(error));
2914
+ } finally {
2915
+ reading.current = false;
2916
+ }
2917
+ }, [services.rpc]);
2918
+ (0, react.useEffect)(() => {
2919
+ alive.current = true;
2920
+ abort.current = new AbortController();
2921
+ refresh();
2922
+ const changed = () => {
2923
+ setWritable(services.settings.getSnapshot().writable);
2924
+ refresh();
2925
+ };
2926
+ const dispose = services.settings.subscribe(changed);
2927
+ window.addEventListener("focus", changed);
2928
+ return () => {
2929
+ alive.current = false;
2930
+ abort.current?.abort();
2931
+ player.current?.dispose();
2932
+ dispose();
2933
+ window.removeEventListener("focus", changed);
2934
+ };
2935
+ }, [refresh, services.settings]);
2936
+ (0, react.useEffect)(() => {
2937
+ if (snapshot !== void 0) setSelectedId(snapshot.settings.selectedId);
2938
+ }, [snapshot?.settings.selectedId]);
2939
+ (0, react.useEffect)(() => {
2940
+ if (snapshot !== void 0) setParameters(services.configurationDraft ? {
2941
+ ...require_manager.DEFAULT_ASSISTANT_TTS_PARAMETERS,
2942
+ ...snapshot.settings.parameters
2943
+ } : require_manager.readAssistantTtsParameters(snapshot.settings.parameters));
2944
+ }, [
2945
+ snapshot?.settings.parameters?.rate,
2946
+ snapshot?.settings.parameters?.volume,
2947
+ snapshot?.settings.parameters?.segmentPauseMs,
2948
+ snapshot?.settings.parameters?.chunkTokens,
2949
+ snapshot?.settings.parameters?.seed
2950
+ ]);
2951
+ (0, react.useEffect)(() => {
2952
+ if (busy !== "setup" && snapshot?.status.busy !== true) return void 0;
2953
+ const timer = setInterval(() => {
2954
+ refresh();
2955
+ }, 1e3);
2956
+ return () => clearInterval(timer);
2957
+ }, [
2958
+ busy,
2959
+ snapshot?.status.busy,
2960
+ refresh
2961
+ ]);
2962
+ (0, react.useEffect)(() => {
2963
+ if (!enabled || !moss) player.current?.cancel();
2964
+ }, [enabled, moss]);
2965
+ const run = async (action, value) => {
2966
+ if (pending.current || disabled) return;
2967
+ pending.current = true;
2968
+ setBusy(action);
2969
+ setError("");
2970
+ player.current?.cancel();
2971
+ try {
2972
+ const result = await services.rpc.call(require_transport.CODINGNS_RPC_CHANNEL, `assistant/tts/${action}`, value, abort.current?.signal);
2973
+ if (!result.ok) throw new Error(result.error.message);
2974
+ if (alive.current) {
2975
+ setSnapshot(result.value);
2976
+ if (action === "import") {
2977
+ setSource("");
2978
+ setName("");
2979
+ }
2980
+ }
2981
+ if (services.settings.reload !== void 0) await services.settings.reload();
2982
+ else await services.settings.load?.();
2983
+ } catch (error) {
2984
+ if (alive.current) setError(message$2(error));
2985
+ } finally {
2986
+ pending.current = false;
2987
+ if (alive.current) {
2988
+ setBusy(void 0);
2989
+ await refresh();
2990
+ }
2991
+ }
2992
+ };
2993
+ const preview = async () => {
2994
+ if (pending.current || disabled || !active || !moss || !ready) return;
2995
+ pending.current = true;
2996
+ setBusy("preview");
2997
+ setError("");
2998
+ try {
2999
+ await player.current.speak(t("tts.defaultText"), selected.id, void 0, require_manager.validateAssistantTtsParameters(parameters));
3000
+ } catch (error) {
3001
+ if (alive.current) setError(message$2(error));
3002
+ } finally {
3003
+ pending.current = false;
3004
+ if (alive.current) setBusy(void 0);
3005
+ }
3006
+ };
3007
+ const button = (label, action, locked = false) => (0, react.createElement)("button", {
3008
+ type: "button",
3009
+ disabled: disabled || locked,
3010
+ style: require_theme.dshSettingsButtonStyle,
3011
+ onClick: action
3012
+ }, label);
3013
+ const field = (label, content) => (0, react.createElement)("label", { style: require_assistant_workbench.assistantSettingFieldStyle }, (0, react.createElement)("span", null, label), content);
3014
+ const input = (value, change, maxLength = 2048) => (0, react.createElement)("input", {
3015
+ value,
3016
+ disabled,
3017
+ maxLength,
3018
+ style: require_theme.dshSettingsFieldStyle,
3019
+ onChange: (event) => change(event.currentTarget.value)
3020
+ });
3021
+ const options = (value, change, values) => (0, react.createElement)("select", {
3022
+ value,
3023
+ disabled,
3024
+ style: require_theme.dshSettingsFieldStyle,
3025
+ onChange: (event) => change(event.currentTarget.value)
3026
+ }, ...values.map((item) => (0, react.createElement)("option", {
3027
+ key: item,
3028
+ value: item
3029
+ }, t(`tts.${item}`))));
3030
+ const number = (key, scale = 1, slider = false) => {
3031
+ const limits = require_manager.ASSISTANT_TTS_PARAMETER_LIMITS[key];
3032
+ const value = parameters[key];
3033
+ const change = (event) => {
3034
+ const raw = event.currentTarget.value;
3035
+ const next = {
3036
+ ...parameters,
3037
+ [key]: raw === "" ? key === "seed" ? null : NaN : Number(raw) / scale
3038
+ };
3039
+ setParameters(next);
3040
+ if (services.configurationDraft) services.settings.mutate([{
3041
+ op: "set",
3042
+ path: [
3043
+ "assistant",
3044
+ "tts",
3045
+ "parameters"
3046
+ ],
3047
+ value: next
3048
+ }]).catch((cause) => setError(message$2(cause)));
3049
+ };
3050
+ const props = {
3051
+ disabled,
3052
+ min: limits.min * scale,
3053
+ max: limits.max * scale,
3054
+ step: limits.step * scale,
3055
+ onChange: change
3056
+ };
3057
+ return field(t(`tts.${key}`), (0, react.createElement)("div", { style: {
3058
+ display: "grid",
3059
+ gridTemplateColumns: slider ? "minmax(0, 1fr) 90px" : "1fr",
3060
+ gap: 10,
3061
+ alignItems: "center"
3062
+ } }, slider ? (0, react.createElement)("input", {
3063
+ ...props,
3064
+ type: "range",
3065
+ "aria-label": t(`tts.${key}`),
3066
+ value: value === null || !Number.isFinite(value) ? require_manager.DEFAULT_ASSISTANT_TTS_PARAMETERS[key] * scale : Number((value * scale).toFixed(4))
3067
+ }) : null, (0, react.createElement)("input", {
3068
+ ...props,
3069
+ type: "number",
3070
+ value: value === null || !Number.isFinite(value) ? "" : Number((value * scale).toFixed(4)),
3071
+ placeholder: key === "seed" ? t("tts.autoSeed") : void 0,
3072
+ "aria-label": t(`tts.${key}`),
3073
+ style: require_theme.dshSettingsFieldStyle,
3074
+ "data-codingns-tts-parameter": key
3075
+ })));
3076
+ };
3077
+ return (0, react.createElement)("section", {
3078
+ "aria-label": t("tts.title"),
3079
+ "data-codingns-voice-settings": true,
3080
+ style: {
3081
+ ...require_assistant_workbench.assistantSettingTextStyle,
3082
+ display: "grid",
3083
+ gap: 12,
3084
+ ...embedded ? {} : {
3085
+ borderTop: `1px solid ${require_theme.dshThemeColor.border}`,
3086
+ paddingTop: 14
3087
+ }
3088
+ }
3089
+ }, (0, react.createElement)("strong", null, t("tts.title")), moss ? (0, react.createElement)("p", { style: {
3090
+ ...require_theme.dshSettingsHelpStyle,
3091
+ margin: 0
3092
+ } }, t("tts.description")) : null, field(t("tts.backend"), (0, react.createElement)("select", {
3093
+ value: backend,
3094
+ disabled: disabled || snapshot === void 0,
3095
+ style: require_theme.dshSettingsFieldStyle,
3096
+ onChange: (event) => {
3097
+ run("select", {
3098
+ id: selectedId,
3099
+ backend: event.currentTarget.value
3100
+ });
3101
+ }
3102
+ }, (0, react.createElement)("option", { value: "browser" }, t("tts.browser")), (0, react.createElement)("option", {
3103
+ value: "moss-onnx",
3104
+ disabled: !ready
3105
+ }, t("tts.moss")))), (0, react.createElement)("div", { style: {
3106
+ display: "flex",
3107
+ flexWrap: "wrap",
3108
+ gap: 8
3109
+ } }, button(t(services.configurationDraft ? "tts.prepare" : "tts.setup"), () => {
3110
+ run("setup", {});
3111
+ }), moss ? button(t("tts.refresh"), () => {
3112
+ refresh();
3113
+ }) : null), !moss && !snapshot?.status.busy && busy !== "setup" ? null : (0, react.createElement)("div", {
3114
+ role: "status",
3115
+ style: require_theme.dshSettingsHelpStyle
3116
+ }, snapshot?.status.busy ? snapshot.status.phase : busy === "setup" ? t("tts.busy") : ready ? t("tts.ready") : t("tts.notReady")), snapshot?.status.busy ? (0, react.createElement)("progress", {
3117
+ max: snapshot.status.totalBytes ?? void 0,
3118
+ value: snapshot.status.totalBytes === null ? void 0 : snapshot.status.downloadedBytes,
3119
+ style: { width: "100%" },
3120
+ "aria-label": snapshot.status.phase
3121
+ }) : null, moss ? (0, react.createElement)("div", {
3122
+ "data-codingns-moss-voices": true,
3123
+ style: {
3124
+ display: "grid",
3125
+ gap: 12
3126
+ }
3127
+ }, field(t("tts.selected"), (0, react.createElement)("select", {
3128
+ value: selectedId,
3129
+ disabled,
3130
+ style: require_theme.dshSettingsFieldStyle,
3131
+ "data-codingns-voice-list": true,
3132
+ onChange: (event) => {
3133
+ const id = event.currentTarget.value;
3134
+ player.current?.cancel();
3135
+ setSelectedId(id);
3136
+ if (services.configurationDraft) services.settings.mutate([{
3137
+ op: "set",
3138
+ path: [
3139
+ "assistant",
3140
+ "tts",
3141
+ "selectedId"
3142
+ ],
3143
+ value: id
3144
+ }]).catch((cause) => setError(message$2(cause)));
3145
+ }
3146
+ }, ...voices.map((voice) => (0, react.createElement)("option", {
3147
+ key: voice.id,
3148
+ value: voice.id
3149
+ }, voiceLabel(voice, t))))), (0, react.createElement)("div", { style: {
3150
+ ...require_theme.dshSettingsHelpStyle,
3151
+ overflowWrap: "anywhere"
3152
+ } }, t("tts.license", { license: selected.license }), " · ", (0, react.createElement)("a", {
3153
+ href: selected.source,
3154
+ target: "_blank",
3155
+ rel: "noopener noreferrer"
3156
+ }, t("tts.sourceLink"))), (0, react.createElement)("div", { style: {
3157
+ display: "flex",
3158
+ flexWrap: "wrap",
3159
+ gap: 8
3160
+ } }, services.configurationDraft ? null : button(t("tts.apply"), () => {
3161
+ run("select", { id: selectedId });
3162
+ }, !ready), button(t("tts.preview"), () => {
3163
+ preview();
3164
+ }, !ready), busy !== "preview" ? null : (0, react.createElement)("button", {
3165
+ type: "button",
3166
+ style: require_theme.dshSettingsButtonStyle,
3167
+ onClick: () => player.current?.cancel()
3168
+ }, t("tts.stop")), selected.kind !== "reference" ? null : button(t("tts.remove"), () => {
3169
+ run("remove", { id: selected.id });
3170
+ }))) : null, (0, react.createElement)("div", {
3171
+ "data-codingns-tts-controls": true,
3172
+ style: {
3173
+ display: "grid",
3174
+ gap: 12
3175
+ }
3176
+ }, (0, react.createElement)("strong", null, t("tts.playbackSettings")), number("rate", 1, true), number("volume", 100, true), moss ? (0, react.createElement)("p", { style: {
3177
+ ...require_theme.dshSettingsHelpStyle,
3178
+ margin: 0
3179
+ } }, t("tts.rateHint")) : null, moss ? (0, react.createElement)("details", null, (0, react.createElement)("summary", { style: { cursor: "pointer" } }, t("tts.advanced")), (0, react.createElement)("div", { style: {
3180
+ display: "grid",
3181
+ gap: 12,
3182
+ marginTop: 12
3183
+ } }, (0, react.createElement)("p", { style: {
3184
+ ...require_theme.dshSettingsHelpStyle,
3185
+ margin: 0
3186
+ } }, t("tts.advancedHint")), number("segmentPauseMs"), number("chunkTokens"), number("seed"), (0, react.createElement)("p", { style: {
3187
+ ...require_theme.dshSettingsHelpStyle,
3188
+ margin: 0
3189
+ } }, t("tts.seedHint")))) : null, moss ? (0, react.createElement)("p", { style: {
3190
+ ...require_theme.dshSettingsHelpStyle,
3191
+ margin: 0
3192
+ } }, t("tts.parametersHint")) : null, (0, react.createElement)("div", { style: {
3193
+ display: "flex",
3194
+ flexWrap: "wrap",
3195
+ gap: 8
3196
+ } }, services.configurationDraft ? null : button(t("tts.saveParameters"), () => {
3197
+ run("configure", { parameters });
3198
+ }, snapshot === void 0), button(t(services.configurationDraft ? "tts.resetDraftParameters" : "tts.resetParameters"), () => {
3199
+ const next = moss ? { ...require_manager.DEFAULT_ASSISTANT_TTS_PARAMETERS } : {
3200
+ ...parameters,
3201
+ rate: require_manager.DEFAULT_ASSISTANT_TTS_PARAMETERS.rate,
3202
+ volume: require_manager.DEFAULT_ASSISTANT_TTS_PARAMETERS.volume
3203
+ };
3204
+ setParameters(next);
3205
+ if (services.configurationDraft) services.settings.mutate([{
3206
+ op: "set",
3207
+ path: [
3208
+ "assistant",
3209
+ "tts",
3210
+ "parameters"
3211
+ ],
3212
+ value: next
3213
+ }]).catch((cause) => setError(message$2(cause)));
3214
+ else run("configure", { parameters: next });
3215
+ }, snapshot === void 0))), moss ? (0, react.createElement)("details", { "data-codingns-voice-import": true }, (0, react.createElement)("summary", { style: { cursor: "pointer" } }, t(services.configurationDraft ? "tts.importDraft" : "tts.import")), (0, react.createElement)("div", { style: {
3216
+ display: "grid",
3217
+ gap: 12,
3218
+ marginTop: 12
3219
+ } }, field(t("tts.site"), (0, react.createElement)("select", {
3220
+ value: siteId,
3221
+ disabled,
3222
+ style: require_theme.dshSettingsFieldStyle,
3223
+ onChange: (event) => setSiteId(event.currentTarget.value)
3224
+ }, ...require_manager.ASSISTANT_VOICE_SITES.map((site) => (0, react.createElement)("option", {
3225
+ key: site.id,
3226
+ value: site.id
3227
+ }, site.name)))), (0, react.createElement)("a", {
3228
+ href: site.url,
3229
+ target: "_blank",
3230
+ rel: "noopener noreferrer",
3231
+ "aria-disabled": !enabled,
3232
+ onClick: (event) => {
3233
+ if (!enabled) event.preventDefault();
3234
+ },
3235
+ style: {
3236
+ ...require_theme.dshSettingsButtonStyle,
3237
+ justifySelf: "start",
3238
+ textDecoration: "none"
3239
+ }
3240
+ }, t("tts.openSite")), (0, react.createElement)("a", {
3241
+ href: siteId === "aishell" ? "https://huggingface.co/datasets/AISHELL/AISHELL-3/tree/main/train/wav" : "https://huggingface.co/kyutai/tts-voices/tree/main/voice-donations",
3242
+ target: "_blank",
3243
+ rel: "noopener noreferrer",
3244
+ "aria-disabled": !enabled,
3245
+ onClick: (event) => {
3246
+ if (!enabled) event.preventDefault();
3247
+ }
3248
+ }, t("tts.openRecordings")), (0, react.createElement)("p", { style: {
3249
+ ...require_theme.dshSettingsHelpStyle,
3250
+ margin: 0
3251
+ } }, t(siteId === "aishell" ? "tts.aishellHint" : "tts.kyutaiHint")), field(t("tts.source"), input(source, setSource)), field(t("tts.name"), input(name, setName, 80)), field(t("tts.language"), options(language, setLanguage, [
3252
+ "unknown",
3253
+ "zh",
3254
+ "en",
3255
+ "ja",
3256
+ "fr"
3257
+ ])), field(t("tts.gender"), options(gender, setGender, [
3258
+ "unknown",
3259
+ "male",
3260
+ "female"
3261
+ ])), (0, react.createElement)("p", { style: {
3262
+ ...require_theme.dshSettingsHelpStyle,
3263
+ margin: 0
3264
+ } }, t("tts.sourceHint")), button(t(services.configurationDraft ? "tts.importDraft" : "tts.import"), () => {
3265
+ const reference = siteId === "aishell" && !source.includes(":") ? `aishell:${source}` : source;
3266
+ run("import", {
3267
+ source: reference,
3268
+ name,
3269
+ language,
3270
+ gender
3271
+ });
3272
+ }, !ready || source.trim() === ""))) : null, error || snapshot?.status.error ? (0, react.createElement)("div", {
3273
+ role: "alert",
3274
+ style: {
3275
+ color: require_theme.dshThemeColor.error,
3276
+ fontSize: 13,
3277
+ overflowWrap: "anywhere"
3278
+ }
3279
+ }, error || snapshot?.status.error) : null);
3280
+ }
3281
+ function voiceLabel(voice, t) {
3282
+ const language = [
3283
+ "zh",
3284
+ "en",
3285
+ "ja",
3286
+ "fr",
3287
+ "unknown"
3288
+ ].includes(voice.language) ? t(`tts.${voice.language}`) : voice.language;
3289
+ return `${voice.name} · ${language} · ${t(`tts.${voice.gender}`)}`;
3290
+ }
3291
+ function message$2(error) {
3292
+ return error instanceof Error ? error.message : String(error);
3293
+ }
3294
+ //#endregion
3295
+ //#region src/client/features/assistant-voice-settings-group.ts
3296
+ /** 两组独立收起,保留子组件实例,避免关闭卡片丢失草稿或中断下载。 */
3297
+ function AssistantVoiceSettingsGroup(props) {
3298
+ const [expanded, setExpanded] = (0, react.useState)(false);
3299
+ return (0, react.createElement)(AssistantVoiceSettingsGroupView, {
3300
+ ...props,
3301
+ expanded,
3302
+ onToggle: setExpanded
3303
+ });
3304
+ }
3305
+ /** 原生折叠语义支持键盘操作;内容保留,隐藏后通过 active 停止试听和闲置查询。 */
3306
+ function AssistantVoiceSettingsGroupView({ kind, hint, active, disabled, expanded, t, renderContent, onToggle }) {
3307
+ return (0, react.createElement)("details", {
3308
+ open: expanded,
3309
+ "data-codingns-voice-settings-group": kind,
3310
+ onToggle: (event) => onToggle(event.currentTarget.open),
3311
+ style: {
3312
+ minWidth: 0,
3313
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
3314
+ borderRadius: 14,
3315
+ color: require_theme.dshThemeColor.labelPrimary,
3316
+ background: require_theme.dshThemeColor.cardBackground
3317
+ }
3318
+ }, (0, react.createElement)("summary", { style: {
3319
+ display: "flex",
3320
+ alignItems: "center",
3321
+ gap: 12,
3322
+ padding: "16px 18px",
3323
+ minWidth: 0,
3324
+ cursor: "pointer",
3325
+ listStyle: "none",
3326
+ borderRadius: expanded ? "14px 14px 0 0" : 14
3327
+ } }, (0, react.createElement)("span", {
3328
+ "aria-hidden": true,
3329
+ style: {
3330
+ width: 36,
3331
+ height: 36,
3332
+ flexShrink: 0,
3333
+ display: "flex",
3334
+ alignItems: "center",
3335
+ justifyContent: "center",
3336
+ borderRadius: 10,
3337
+ color: require_theme.dshThemeColor.accent,
3338
+ background: require_theme.dshThemeColor.surfaceSubtle
3339
+ }
3340
+ }, groupIcon(kind)), (0, react.createElement)("span", { style: {
3341
+ display: "grid",
3342
+ gap: 4,
3343
+ flex: "1 1 auto",
3344
+ minWidth: 0
3345
+ } }, (0, react.createElement)("strong", { style: {
3346
+ fontSize: 14,
3347
+ fontWeight: 600,
3348
+ lineHeight: 1.4
3349
+ } }, t(`voice.settings.${kind}`)), (0, react.createElement)("span", {
3350
+ title: hint,
3351
+ style: {
3352
+ fontSize: 12,
3353
+ lineHeight: 1.5,
3354
+ color: require_theme.dshThemeColor.labelSecondary,
3355
+ whiteSpace: "nowrap",
3356
+ overflow: "hidden",
3357
+ textOverflow: "ellipsis"
3358
+ }
3359
+ }, hint)), (0, react.createElement)("svg", {
3360
+ "aria-hidden": true,
3361
+ width: 18,
3362
+ height: 18,
3363
+ viewBox: "0 0 24 24",
3364
+ fill: "none",
3365
+ stroke: "currentColor",
3366
+ strokeWidth: 1.8,
3367
+ style: {
3368
+ flexShrink: 0,
3369
+ color: require_theme.dshThemeColor.labelSecondary,
3370
+ transform: expanded ? "rotate(180deg)" : "none"
3371
+ }
3372
+ }, (0, react.createElement)("path", {
3373
+ d: "m6 9 6 6 6-6",
3374
+ strokeLinecap: "round",
3375
+ strokeLinejoin: "round"
3376
+ }))), (0, react.createElement)("fieldset", {
3377
+ disabled,
3378
+ style: {
3379
+ display: "grid",
3380
+ gap: 18,
3381
+ margin: 0,
3382
+ padding: 18,
3383
+ minWidth: 0,
3384
+ border: 0,
3385
+ borderTop: `1px solid ${require_theme.dshThemeColor.border}`
3386
+ }
3387
+ }, (0, react.createElement)("style", null, groupCss), renderContent(active && expanded)));
3388
+ }
3389
+ function groupIcon(kind) {
3390
+ return (0, react.createElement)("svg", {
3391
+ width: 20,
3392
+ height: 20,
3393
+ viewBox: "0 0 24 24",
3394
+ fill: "none",
3395
+ stroke: "currentColor",
3396
+ strokeWidth: 1.6,
3397
+ strokeLinecap: "round",
3398
+ strokeLinejoin: "round"
3399
+ }, kind === "input" ? [(0, react.createElement)("rect", {
3400
+ key: "mic",
3401
+ x: 9,
3402
+ y: 2,
3403
+ width: 6,
3404
+ height: 12,
3405
+ rx: 3
3406
+ }), (0, react.createElement)("path", {
3407
+ key: "stand",
3408
+ d: "M5 10v2a7 7 0 0 0 14 0v-2M12 19v3M8 22h8"
3409
+ })] : [(0, react.createElement)("path", {
3410
+ key: "speaker",
3411
+ d: "m11 4-6 5H2v6h3l6 5V4Z"
3412
+ }), (0, react.createElement)("path", {
3413
+ key: "sound",
3414
+ d: "M15 8a6 6 0 0 1 0 8m3-11a10 10 0 0 1 0 14"
3415
+ })]);
3416
+ }
3417
+ const groupCss = `
3418
+ [data-codingns-voice-settings-group]>summary::-webkit-details-marker{display:none}
3419
+ [data-codingns-voice-settings-group]>summary:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.06))}
3420
+ [data-codingns-voice-settings-group]>summary:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#1677ff);outline-offset:2px}
3421
+ `;
3422
+ //#endregion
3423
+ //#region src/client/features/assistant-voice-initialization.ts
3424
+ /** 打开面板只检查缓存,用户点击启用后才安装;首次不展示技术参数或要求填写字段。 */
3425
+ function AssistantVoiceInitializationPanel({ services, enabled, active, inputSettings, outputSettings }) {
3426
+ const t = require_transport.useCodingNsTranslator(services.locale);
3427
+ const [snapshot, setSnapshot] = (0, react.useState)();
3428
+ const [pending, setPending] = (0, react.useState)(false);
3429
+ const [error, setError] = (0, react.useState)("");
3430
+ const [speaking, setSpeaking] = (0, react.useState)(false);
3431
+ const [writable, setWritable] = (0, react.useState)(() => services.settings.getSnapshot().writable);
3432
+ const mounted = (0, react.useRef)(false);
3433
+ const submitting = (0, react.useRef)(false);
3434
+ const reading = (0, react.useRef)(false);
3435
+ const revision = (0, react.useRef)(0);
3436
+ const abort = (0, react.useRef)();
3437
+ const player = (0, react.useRef)();
3438
+ if (player.current === void 0) player.current = new require_react_dom$1.MossVoiceOutput();
3439
+ const refresh = (0, react.useCallback)(async () => {
3440
+ if (reading.current) return;
3441
+ reading.current = true;
3442
+ const current = revision.current;
3443
+ try {
3444
+ const result = await services.rpc.call(require_transport.CODINGNS_RPC_CHANNEL, "assistant/voice/initialization", {}, abort.current?.signal);
3445
+ if (!result.ok) throw new Error(result.error.message);
3446
+ if (mounted.current && current === revision.current) {
3447
+ setSnapshot(result.value);
3448
+ setError("");
3449
+ }
3450
+ } catch (cause) {
3451
+ if (mounted.current && current === revision.current) setError(message$1(cause));
3452
+ } finally {
3453
+ reading.current = false;
3454
+ }
3455
+ }, [services.rpc]);
3456
+ (0, react.useEffect)(() => {
3457
+ mounted.current = true;
3458
+ abort.current = new AbortController();
3459
+ const dispose = services.settings.subscribe(() => {
3460
+ setWritable(services.settings.getSnapshot().writable);
3461
+ refresh();
3462
+ });
3463
+ return () => {
3464
+ mounted.current = false;
3465
+ revision.current++;
3466
+ abort.current?.abort();
3467
+ player.current?.dispose();
3468
+ dispose();
3469
+ };
3470
+ }, [services.settings, refresh]);
3471
+ (0, react.useEffect)(() => {
3472
+ if (!active && !pending && !snapshot?.busy) return void 0;
3473
+ refresh();
3474
+ const timer = setInterval(() => {
3475
+ refresh();
3476
+ }, pending || snapshot?.busy ? 1e3 : 5e3);
3477
+ return () => clearInterval(timer);
3478
+ }, [
3479
+ active,
3480
+ pending,
3481
+ snapshot?.busy,
3482
+ refresh
3483
+ ]);
3484
+ (0, react.useEffect)(() => {
3485
+ if (!active || !enabled) {
3486
+ player.current?.cancel();
3487
+ setSpeaking(false);
3488
+ }
3489
+ }, [active, enabled]);
3490
+ const initialize = async () => {
3491
+ if (submitting.current || !enabled || !writable || snapshot === void 0 || snapshot.busy) return;
3492
+ submitting.current = true;
3493
+ revision.current++;
3494
+ setPending(true);
3495
+ setError("");
3496
+ player.current?.cancel();
3497
+ setSpeaking(false);
3498
+ try {
3499
+ const result = await services.rpc.call(require_transport.CODINGNS_RPC_CHANNEL, "assistant/voice/initialize", {}, abort.current?.signal);
3500
+ if (!result.ok) throw new Error(result.error.message);
3501
+ revision.current++;
3502
+ if (mounted.current) setSnapshot(result.value);
3503
+ if (services.settings.reload !== void 0) await services.settings.reload();
3504
+ else await services.settings.load?.();
3505
+ } catch (cause) {
3506
+ if (mounted.current) setError(message$1(cause));
3507
+ } finally {
3508
+ submitting.current = false;
3509
+ if (mounted.current) setPending(false);
3510
+ }
3511
+ };
3512
+ const listen = async () => {
3513
+ if (speaking) {
3514
+ player.current?.cancel();
3515
+ setSpeaking(false);
3516
+ return;
3517
+ }
3518
+ if (!enabled || !writable || !snapshot?.ready) return;
3519
+ setSpeaking(true);
3520
+ setError("");
3521
+ try {
3522
+ await player.current.speak(t("tts.defaultText"), snapshot.tts.settings.selectedId, void 0, snapshot.tts.settings.parameters);
3523
+ } catch (cause) {
3524
+ if (mounted.current) setError(message$1(cause));
3525
+ } finally {
3526
+ if (mounted.current) setSpeaking(false);
3527
+ }
3528
+ };
3529
+ return (0, react.createElement)("div", { style: {
3530
+ display: "grid",
3531
+ gap: 18
3532
+ } }, (0, react.createElement)(AssistantVoiceInitializationView, {
3533
+ snapshot,
3534
+ pending,
3535
+ error,
3536
+ disabled: !enabled || !writable,
3537
+ speaking,
3538
+ draft: services.configurationDraft === true,
3539
+ t,
3540
+ onInitialize: () => {
3541
+ initialize();
3542
+ },
3543
+ onListen: () => {
3544
+ listen();
3545
+ },
3546
+ onRefresh: () => {
3547
+ refresh();
3548
+ }
3549
+ }), (0, react.createElement)(AssistantVoiceSettingsGroup, {
3550
+ kind: "input",
3551
+ hint: snapshot?.modelLabel ?? t("voice.settings.inputDescription"),
3552
+ active,
3553
+ disabled: !enabled || !writable || pending || snapshot?.busy === true,
3554
+ t,
3555
+ renderContent: inputSettings
3556
+ }), (0, react.createElement)(AssistantVoiceSettingsGroup, {
3557
+ kind: "output",
3558
+ hint: snapshot?.tts.settings.backend === "browser" ? t("tts.browser") : snapshot?.tts.voices.find((voice) => voice.id === snapshot.tts.settings.selectedId)?.name ?? t("voice.settings.outputDescription"),
3559
+ active,
3560
+ disabled: !enabled || !writable || pending || snapshot?.busy === true,
3561
+ t,
3562
+ renderContent: outputSettings
3563
+ }));
3564
+ }
3565
+ /** 纯展示层覆盖未配置、准备中、失败与完成四态,重试仍只有一个入口。 */
3566
+ function AssistantVoiceInitializationView({ snapshot, pending, error, disabled, speaking, draft = false, t, onInitialize, onListen, onRefresh }) {
3567
+ const busy = pending || snapshot?.busy === true;
3568
+ const ready = snapshot?.ready === true;
3569
+ const failure = error || snapshot?.error;
3570
+ const voice = snapshot?.tts.voices.find((voice) => voice.id === snapshot.tts.settings.selectedId);
3571
+ const label = busy ? t(snapshot?.phase === "speech" || snapshot?.tts.status.busy ? "tts.wizardSpeech" : "tts.wizardRecognition") : ready ? t(draft ? "tts.wizardDraftReady" : "tts.wizardReady") : snapshot === void 0 ? t("tts.wizardChecking") : t("tts.wizardDescription");
3572
+ const progress = snapshot?.progress;
3573
+ const value = busy && progress?.totalBytes != null && progress.totalBytes > 0 ? Math.min(progress.downloadedBytes, progress.totalBytes) : void 0;
3574
+ return (0, react.createElement)("section", {
3575
+ "data-codingns-voice-initialization": ready ? "ready" : busy ? "preparing" : failure ? "failed" : "initial",
3576
+ "aria-label": t("tts.wizardTitle"),
3577
+ style: {
3578
+ display: "grid",
3579
+ gap: 12,
3580
+ padding: 18,
3581
+ borderRadius: 12,
3582
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
3583
+ background: require_theme.dshThemeColor.surfaceSubtle
3584
+ }
3585
+ }, (0, react.createElement)("strong", null, t(ready ? "tts.wizardReady" : "tts.wizardTitle")), (0, react.createElement)("p", {
3586
+ role: "status",
3587
+ "aria-live": "polite",
3588
+ style: {
3589
+ ...require_theme.dshSettingsHelpStyle,
3590
+ margin: 0
3591
+ }
3592
+ }, label), ready ? null : (0, react.createElement)("div", { style: {
3593
+ display: "grid",
3594
+ gap: 5,
3595
+ fontSize: 13,
3596
+ overflowWrap: "anywhere"
3597
+ } }, (0, react.createElement)("span", null, t("tts.wizardModel", { model: snapshot?.modelId === "custom" ? t("voice.models.custom") : snapshot?.modelLabel ?? t("tts.wizardDefault") })), (0, react.createElement)("span", null, t("tts.wizardVoice", { voice: voice?.name ?? "Junhao" }))), busy ? (0, react.createElement)("div", { style: {
3598
+ display: "grid",
3599
+ gap: 6
3600
+ } }, (0, react.createElement)("progress", {
3601
+ max: progress?.totalBytes ?? 1,
3602
+ ...value === void 0 ? {} : { value },
3603
+ "aria-label": label,
3604
+ style: { width: "100%" }
3605
+ }), snapshot?.phase !== "speech" || progress === null || progress === void 0 ? null : (0, react.createElement)("small", { style: require_theme.dshSettingsHelpStyle }, progress.label)) : null, ready ? null : (0, react.createElement)("p", { style: {
3606
+ ...require_theme.dshSettingsHelpStyle,
3607
+ margin: 0
3608
+ } }, t("tts.wizardHint")), (0, react.createElement)("div", { style: {
3609
+ display: "flex",
3610
+ flexWrap: "wrap",
3611
+ gap: 8
3612
+ } }, (0, react.createElement)("button", {
3613
+ type: "button",
3614
+ disabled: disabled || busy || snapshot === void 0,
3615
+ onClick: ready ? onListen : onInitialize,
3616
+ style: require_theme.dshSettingsPrimaryButtonStyle
3617
+ }, t(ready ? speaking ? "tts.stop" : "tts.wizardListen" : busy ? "tts.wizardPreparing" : failure ? "tts.wizardRetry" : draft ? "tts.wizardPrepareResources" : "tts.wizardEnable")), snapshot !== void 0 && !failure ? null : (0, react.createElement)("button", {
3618
+ type: "button",
3619
+ disabled: busy,
3620
+ onClick: onRefresh,
3621
+ style: require_theme.dshSettingsButtonStyle
3622
+ }, t("tts.refresh"))), failure ? (0, react.createElement)("div", {
3623
+ role: "alert",
3624
+ style: {
3625
+ color: require_theme.dshThemeColor.error,
3626
+ fontSize: 13,
3627
+ overflowWrap: "anywhere"
3628
+ }
3629
+ }, failure) : null);
3630
+ }
3631
+ function message$1(cause) {
3632
+ return cause instanceof Error ? cause.message : String(cause);
3633
+ }
3634
+ //#endregion
3635
+ //#region src/client/voice-model-setup-progress.ts
3636
+ /** 复用页面 RPC 查询下载状态;每次查询返回后再安排下一次,避免请求积压。 */
3637
+ function watchVoiceModelSetupProgress(rpc, request, onProgress) {
3638
+ let active = true;
3639
+ let timer;
3640
+ const controller = new AbortController();
3641
+ const poll = async () => {
3642
+ try {
3643
+ const result = await rpc.call(require_transport.CODINGNS_RPC_CHANNEL, "assistant/voice/setup-progress", request, controller.signal);
3644
+ const progress = result.ok ? result.value : null;
3645
+ if (active && progress != null && progress.modelId === request.modelId) onProgress(progress);
3646
+ } catch {}
3647
+ if (active) timer = setTimeout(() => void poll(), 300);
3648
+ };
3649
+ poll();
3650
+ return () => {
3651
+ active = false;
3652
+ clearTimeout(timer);
3653
+ controller.abort();
3654
+ };
3655
+ }
3656
+ //#endregion
3657
+ //#region src/client/features/voice-initialization-dialog.ts
3658
+ /** 识别模型独立展示,不依赖 MOSS 初始化或高级声音设置的展开状态。 */
3659
+ function VoiceModelManagerPanel({ services, value, enabled, active }) {
3660
+ return (0, react.createElement)(VoiceModelManager, {
3661
+ services,
3662
+ value,
3663
+ enabled,
3664
+ active
3665
+ });
3666
+ }
3667
+ /** 选中的卡片与 Host 当前配置分开保存,操作完成后重新读取文件事实。 */
3668
+ function VoiceModelManager({ services, value, enabled = true, active = true, onClose }) {
3669
+ const t = require_transport.useCodingNsTranslator(services.locale);
3670
+ const [modelId, setModelId] = (0, react.useState)(() => require_assistant_workbench.findAssistantVoiceModel(value.modelId ?? "")?.id ?? "sherpa-onnx-streaming-zh-en");
3671
+ const [snapshot, setSnapshot] = (0, react.useState)();
3672
+ const [refreshing, setRefreshing] = (0, react.useState)(true);
3673
+ const [busy, setBusy] = (0, react.useState)();
3674
+ const [progress, setProgress] = (0, react.useState)();
3675
+ const [error, setError] = (0, react.useState)();
3676
+ const [loadError, setLoadError] = (0, react.useState)();
3677
+ const [success, setSuccess] = (0, react.useState)();
3678
+ const mounted = (0, react.useRef)(true);
3679
+ const readAbort = (0, react.useRef)();
3680
+ const stopProgress = (0, react.useRef)();
3681
+ const pending = (0, react.useRef)(false);
3682
+ const [settings, setSettings] = (0, react.useState)(() => services.settings.getSnapshot());
3683
+ const refresh = (0, react.useCallback)(async () => {
3684
+ readAbort.current?.abort();
3685
+ const controller = new AbortController();
3686
+ readAbort.current = controller;
3687
+ setRefreshing(true);
3688
+ try {
3689
+ const result = await services.rpc.call(require_transport.CODINGNS_RPC_CHANNEL, "assistant/voice/models", {}, controller.signal);
3690
+ if (!mounted.current || controller.signal.aborted) return;
3691
+ if (!result.ok) throw new Error(result.error.message);
3692
+ setSnapshot(result.value);
3693
+ setLoadError(void 0);
3694
+ } catch (cause) {
3695
+ if (mounted.current && !controller.signal.aborted) setLoadError(errorMessage(cause));
3696
+ } finally {
3697
+ if (mounted.current && !controller.signal.aborted) setRefreshing(false);
3698
+ }
3699
+ }, [services.rpc]);
3700
+ (0, react.useEffect)(() => {
3701
+ mounted.current = true;
3702
+ return () => {
3703
+ mounted.current = false;
3704
+ readAbort.current?.abort();
3705
+ stopProgress.current?.();
3706
+ };
3707
+ }, [services.rpc]);
3708
+ (0, react.useEffect)(() => {
3709
+ if (active) refresh();
3710
+ const onFocus = () => {
3711
+ if (active) refresh();
3712
+ };
3713
+ window.addEventListener("focus", onFocus);
3714
+ const unsubscribe = services.settings.subscribe(() => {
3715
+ setSettings(services.settings.getSnapshot());
3716
+ if (active || pending.current) refresh();
3717
+ });
3718
+ return () => {
3719
+ window.removeEventListener("focus", onFocus);
3720
+ unsubscribe();
3721
+ };
3722
+ }, [
3723
+ active,
3724
+ refresh,
3725
+ services.settings
3726
+ ]);
3727
+ (0, react.useEffect)(() => {
3728
+ const current = require_assistant_workbench.findAssistantVoiceModel((services.configurationDraft ? value.modelId : snapshot?.currentModelId) ?? "");
3729
+ if (current !== void 0) setModelId(current.id);
3730
+ }, [
3731
+ snapshot?.currentModelId,
3732
+ services.configurationDraft,
3733
+ value.modelId
3734
+ ]);
3735
+ (0, react.useEffect)(() => {
3736
+ if (!active || snapshot?.operation == null || busy !== void 0) return void 0;
3737
+ const timer = setTimeout(() => void refresh(), 1e3);
3738
+ return () => clearTimeout(timer);
3739
+ }, [
3740
+ active,
3741
+ snapshot,
3742
+ busy,
3743
+ refresh
3744
+ ]);
3745
+ (0, react.useEffect)(() => {
3746
+ if (onClose === void 0) return void 0;
3747
+ const onEscape = (event) => {
3748
+ if (event.key === "Escape" && !pending.current) onClose();
3749
+ };
3750
+ document.addEventListener("keydown", onEscape);
3751
+ return () => document.removeEventListener("keydown", onEscape);
3752
+ }, [onClose]);
3753
+ const run = async (kind) => {
3754
+ if (!active || !enabled || pending.current || snapshot === void 0 || snapshot.operation !== null) return;
3755
+ if (kind !== "verify" && (snapshot.runtimeRunning || !settings.writable || settings.status === "loading")) return;
3756
+ pending.current = true;
3757
+ setBusy(kind);
3758
+ setError(void 0);
3759
+ setSuccess(void 0);
3760
+ setProgress(void 0);
3761
+ try {
3762
+ const request = {
3763
+ modelId,
3764
+ requestId: `voice-model-${Date.now()}-${Math.random().toString(36).slice(2)}`,
3765
+ repair: kind === "repair"
3766
+ };
3767
+ if (kind !== "verify") stopProgress.current = watchVoiceModelSetupProgress(services.rpc, request, (next) => {
3768
+ if (mounted.current) setProgress(next);
3769
+ });
3770
+ const result = await services.rpc.call(require_transport.CODINGNS_RPC_CHANNEL, kind === "verify" ? "assistant/voice/model/verify" : "assistant/voice/setup", request);
3771
+ if (!mounted.current) return;
3772
+ if (!result.ok) throw new Error(result.error.message);
3773
+ if (kind !== "verify") {
3774
+ if (services.settings.reload !== void 0) await services.settings.reload();
3775
+ else await services.settings.load?.();
3776
+ }
3777
+ if (!mounted.current) return;
3778
+ setSuccess(t(kind === "verify" ? "voice.models.verified" : services.configurationDraft ? "voice.models.prepared" : "voice.models.applied"));
3779
+ } catch (cause) {
3780
+ if (mounted.current) setError(errorMessage(cause));
3781
+ } finally {
3782
+ stopProgress.current?.();
3783
+ stopProgress.current = void 0;
3784
+ pending.current = false;
3785
+ if (mounted.current) {
3786
+ setBusy(void 0);
3787
+ await refresh();
3788
+ }
3789
+ }
3790
+ };
3791
+ const choose = (id) => {
3792
+ setModelId(id);
3793
+ setError(void 0);
3794
+ setSuccess(void 0);
3795
+ setProgress(void 0);
3796
+ };
3797
+ const manager = (0, react.createElement)(VoiceModelManagerView, {
3798
+ snapshot,
3799
+ modelId,
3800
+ refreshing,
3801
+ busy,
3802
+ progress,
3803
+ error: error ?? loadError,
3804
+ success,
3805
+ enabled,
3806
+ writable: settings.writable && settings.status !== "loading",
3807
+ onSelect: choose,
3808
+ onRefresh: () => void refresh(),
3809
+ onRun: (kind) => void run(kind),
3810
+ ...onClose === void 0 ? {} : { onClose },
3811
+ t
3812
+ });
3813
+ if (onClose === void 0) return (0, react.createElement)("section", {
3814
+ "data-codingns-voice-model-manager": true,
3815
+ "aria-label": t("awb.asr"),
3816
+ "aria-busy": refreshing || busy !== void 0,
3817
+ style: {
3818
+ display: "grid",
3819
+ minWidth: 0
3820
+ }
3821
+ }, manager);
3822
+ return (0, react.createElement)("div", {
3823
+ role: "presentation",
3824
+ onPointerDown: () => {
3825
+ if (!pending.current) onClose();
3826
+ },
3827
+ style: {
3828
+ position: "fixed",
3829
+ inset: 0,
3830
+ zIndex: 1e4,
3831
+ display: "flex",
3832
+ alignItems: "center",
3833
+ justifyContent: "center",
3834
+ padding: 16,
3835
+ background: require_theme.dshThemeColor.overlay,
3836
+ boxSizing: "border-box"
3837
+ }
3838
+ }, (0, react.createElement)("div", {
3839
+ role: "dialog",
3840
+ "aria-modal": true,
3841
+ "aria-label": t("voice.setup.title"),
3842
+ "aria-busy": busy !== void 0,
3843
+ onPointerDown: (event) => event.stopPropagation(),
3844
+ style: {
3845
+ display: "flex",
3846
+ flexDirection: "column",
3847
+ width: "min(680px, 100%)",
3848
+ maxHeight: "min(780px, 100%)",
3849
+ color: require_theme.dshThemeColor.labelPrimary,
3850
+ background: require_theme.dshThemeColor.menuBackground,
3851
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
3852
+ borderRadius: 14,
3853
+ boxShadow: require_theme.dshThemeColor.prominentShadow,
3854
+ overflow: "hidden",
3855
+ boxSizing: "border-box"
3856
+ }
3857
+ }, manager));
3858
+ }
3859
+ /** 展示层保留下载、配置和验证三个维度,避免把选择动作显示成启用成功。 */
3860
+ function VoiceModelManagerView({ snapshot, modelId, refreshing, busy, progress, error, success, writable, enabled = true, onSelect, onRefresh, onRun, onClose, t }) {
3861
+ const current = require_assistant_workbench.findAssistantVoiceModel(snapshot?.currentModelId ?? "");
3862
+ const selected = snapshot?.models.find((model) => model.modelId === modelId);
3863
+ const locked = !enabled || busy !== void 0 || snapshot?.operation != null;
3864
+ const canApply = snapshot !== void 0 && !locked && writable && !snapshot.runtimeRunning;
3865
+ const alreadyUsed = selected?.current === true && selected.validation.state === "passed" && selected.state === "downloaded";
3866
+ const action = selected?.state === "partial" ? t("voice.models.completeDownload") : selected?.state === "downloaded" ? alreadyUsed ? t("voice.models.inUse") : t("voice.models.use") : t("voice.models.downloadUse");
3867
+ return (0, react.createElement)("div", { style: { display: "contents" } }, (0, react.createElement)("div", { style: {
3868
+ padding: onClose === void 0 ? "0 0 12px" : "20px 22px 16px",
3869
+ display: "grid",
3870
+ gap: 6
3871
+ } }, (0, react.createElement)("div", { style: {
3872
+ ...rowStyle,
3873
+ flexWrap: "wrap"
3874
+ } }, (0, react.createElement)("strong", { style: {
3875
+ fontSize: onClose === void 0 ? 14 : 19,
3876
+ lineHeight: 1.4
3877
+ } }, t(onClose === void 0 ? "awb.asr" : "voice.setup.title")), button(t(refreshing ? "voice.models.refreshing" : "voice.models.refresh"), refreshing || busy !== void 0, onRefresh)), (0, react.createElement)("span", { style: helpStyle }, t("voice.models.description")), onClose === void 0 ? (0, react.createElement)("span", { style: helpStyle }, t("voice.models.recognitionHint")) : null), (0, react.createElement)("div", { style: {
3878
+ ...onClose === void 0 ? {} : { overflowY: "auto" },
3879
+ minHeight: 0,
3880
+ padding: onClose === void 0 ? "0 0 16px" : "0 22px 18px",
3881
+ display: "grid",
3882
+ gap: 12
3883
+ } }, (0, react.createElement)("div", { style: {
3884
+ ...noticeStyle,
3885
+ display: "grid",
3886
+ gap: 5
3887
+ } }, (0, react.createElement)("span", { style: helpStyle }, t("voice.models.current")), (0, react.createElement)("strong", { style: {
3888
+ fontSize: 14,
3889
+ overflowWrap: "anywhere"
3890
+ } }, snapshot === void 0 ? t("voice.models.unknown") : current?.label ?? (snapshot.currentModelId ? t("voice.models.custom") : t("voice.models.none"))), snapshot === void 0 ? null : (0, react.createElement)("span", { style: helpStyle }, t(snapshot.runtimeRunning ? "voice.models.running" : snapshot.runtimeReady ? "voice.models.ready" : "voice.models.stopped"))), snapshot === void 0 && error === void 0 ? (0, react.createElement)("div", {
3891
+ role: "status",
3892
+ style: helpStyle
3893
+ }, t("voice.models.loading")) : null, (0, react.createElement)("div", {
3894
+ role: "radiogroup",
3895
+ "aria-label": t("voice.setup.model"),
3896
+ style: {
3897
+ display: "grid",
3898
+ gap: 10
3899
+ }
3900
+ }, ...require_assistant_workbench.ASSISTANT_VOICE_MODEL_CATALOG.map((model) => (0, react.createElement)(VoiceModelCard, {
3901
+ key: model.id,
3902
+ model,
3903
+ status: snapshot?.models.find((status) => status.modelId === model.id),
3904
+ selected: model.id === modelId,
3905
+ disabled: locked || snapshot === void 0,
3906
+ onSelect,
3907
+ t
3908
+ }))), busy === void 0 ? null : (0, react.createElement)("div", { style: noticeStyle }, (0, react.createElement)(VoiceModelSetupProgress, {
3909
+ progress: busy === "verify" ? {
3910
+ modelId,
3911
+ phase: "verifying",
3912
+ fileName: null,
3913
+ fileIndex: 0,
3914
+ fileCount: 0,
3915
+ downloadedBytes: 0,
3916
+ totalBytes: null
3917
+ } : progress,
3918
+ t
3919
+ })), snapshot?.operation == null || busy !== void 0 ? null : (0, react.createElement)("div", {
3920
+ role: "status",
3921
+ style: noticeStyle
3922
+ }, t("voice.models.otherOperation")), snapshot?.runtimeRunning ? (0, react.createElement)("div", { style: helpStyle }, t("voice.models.stopBeforeSwitch")) : null, success === void 0 ? null : (0, react.createElement)("div", {
3923
+ role: "status",
3924
+ style: {
3925
+ ...noticeStyle,
3926
+ color: require_theme.dshThemeColor.success
3927
+ }
3928
+ }, success), error === void 0 ? null : (0, react.createElement)("div", {
3929
+ role: "alert",
3930
+ style: {
3931
+ ...noticeStyle,
3932
+ color: require_theme.dshThemeColor.error,
3933
+ overflowWrap: "anywhere",
3934
+ whiteSpace: "pre-wrap"
3935
+ }
3936
+ }, error), (0, react.createElement)("span", { style: helpStyle }, t("voice.models.localHint"))), (0, react.createElement)("div", { style: {
3937
+ display: "flex",
3938
+ justifyContent: "flex-end",
3939
+ flexWrap: "wrap",
3940
+ gap: 8,
3941
+ padding: onClose === void 0 ? "14px 0 0" : "14px 22px",
3942
+ borderTop: `1px solid ${require_theme.dshThemeColor.border}`
3943
+ } }, onClose === void 0 ? null : button(t("voice.models.close"), busy !== void 0, onClose), selected?.state === "missing" || selected === void 0 ? null : button(t("voice.models.redownload"), !canApply, () => onRun("repair")), button(t("voice.models.verify"), locked || selected?.state !== "downloaded", () => onRun("verify")), button(action, !canApply || alreadyUsed || selected === void 0, () => onRun("setup"), true)));
3944
+ }
3945
+ /** 文件详情按需展开,模型下载状态与最近验证结果始终可见。 */
3946
+ function VoiceModelCard({ model, status, selected, disabled, onSelect, t }) {
3947
+ const state = status === void 0 ? t("voice.models.unknown") : t(status.state === "downloaded" ? "voice.models.downloaded" : status.state === "partial" ? "voice.models.partial" : "voice.models.missing");
3948
+ const validation = status?.validation.state === "passed" ? t("voice.models.passed") : status?.validation.state === "failed" ? t("voice.models.failed") : t("voice.models.unchecked");
3949
+ return (0, react.createElement)("div", { style: {
3950
+ border: `1px solid ${selected ? require_theme.dshThemeColor.accent : require_theme.dshThemeColor.border}`,
3951
+ borderRadius: 9,
3952
+ padding: "12px 14px",
3953
+ background: selected ? require_theme.dshThemeColor.surfaceSubtle : require_theme.dshThemeColor.menuBackground,
3954
+ display: "grid",
3955
+ gap: 8
3956
+ } }, (0, react.createElement)("label", { style: {
3957
+ ...require_assistant_workbench.assistantSettingCheckboxStyle,
3958
+ cursor: disabled ? "default" : "pointer"
3959
+ } }, (0, react.createElement)("input", {
3960
+ type: "radio",
3961
+ name: "voice-model",
3962
+ value: model.id,
3963
+ checked: selected,
3964
+ disabled,
3965
+ onChange: () => onSelect(model.id),
3966
+ style: {
3967
+ margin: "3px 0 0",
3968
+ accentColor: require_theme.dshThemeColor.accent,
3969
+ flexShrink: 0
3970
+ }
3971
+ }), (0, react.createElement)("span", { style: {
3972
+ display: "grid",
3973
+ gap: 5,
3974
+ minWidth: 0,
3975
+ flex: 1
3976
+ } }, (0, react.createElement)("span", { style: {
3977
+ ...require_assistant_workbench.assistantSettingTextStyle,
3978
+ overflowWrap: "anywhere"
3979
+ } }, model.label), (0, react.createElement)("span", { style: helpStyle }, model.description), (0, react.createElement)("span", { style: {
3980
+ display: "flex",
3981
+ flexWrap: "wrap",
3982
+ gap: 6
3983
+ } }, badge(state, status?.state === "downloaded" ? require_theme.dshThemeColor.success : require_theme.dshThemeColor.labelSecondary), status?.current ? badge(t("voice.models.inUse"), require_theme.dshThemeColor.accent) : null, status?.state === "downloaded" ? badge(validation, status.validation.state === "passed" ? require_theme.dshThemeColor.success : status.validation.state === "failed" ? require_theme.dshThemeColor.error : require_theme.dshThemeColor.labelSecondary) : null))), status === void 0 ? null : (0, react.createElement)("div", { style: {
3984
+ paddingLeft: 23,
3985
+ display: "grid",
3986
+ gap: 6
3987
+ } }, (0, react.createElement)("span", { style: helpStyle }, t("voice.models.fileCount", {
3988
+ count: status.files.filter((file) => file.present).length,
3989
+ total: status.files.length,
3990
+ size: formatDownloadSize(status.totalBytes)
3991
+ })), status.validation.checkedAt === null ? null : (0, react.createElement)("span", { style: helpStyle }, t("voice.models.checkedAt", { time: new Date(status.validation.checkedAt).toLocaleString() })), status.validation.error === null ? null : (0, react.createElement)("span", { style: {
3992
+ ...helpStyle,
3993
+ color: require_theme.dshThemeColor.error,
3994
+ overflowWrap: "anywhere"
3995
+ } }, status.validation.error), (0, react.createElement)("details", null, (0, react.createElement)("summary", { style: {
3996
+ ...helpStyle,
3997
+ cursor: "pointer"
3998
+ } }, t("voice.models.fileDetails")), (0, react.createElement)("div", { style: {
3999
+ display: "grid",
4000
+ gap: 9,
4001
+ marginTop: 9
4002
+ } }, ...status.files.map((file) => (0, react.createElement)("div", {
4003
+ key: file.name,
4004
+ style: {
4005
+ display: "grid",
4006
+ gap: 2
4007
+ }
4008
+ }, (0, react.createElement)("span", { style: {
4009
+ ...rowStyle,
4010
+ fontSize: 12
4011
+ } }, (0, react.createElement)("span", { style: { overflowWrap: "anywhere" } }, file.name), (0, react.createElement)("span", { style: {
4012
+ flexShrink: 0,
4013
+ color: file.present ? require_theme.dshThemeColor.labelSecondary : require_theme.dshThemeColor.error
4014
+ } }, file.present ? formatDownloadSize(file.bytes) : file.partialBytes > 0 ? t("voice.models.partialSize", { size: formatDownloadSize(file.partialBytes) }) : t("voice.models.fileMissing"))), (0, react.createElement)("span", { style: {
4015
+ ...helpStyle,
4016
+ fontSize: 11,
4017
+ overflowWrap: "anywhere",
4018
+ fontFamily: require_theme.dshThemeColor.codeFont
4019
+ } }, file.path)))))));
4020
+ }
4021
+ /** 已下载的字节进度与模型验证、配置阶段分别显示。 */
4022
+ function VoiceModelSetupProgress({ progress, t }) {
4023
+ const downloading = progress?.phase === "downloading";
4024
+ const percent = progress?.phase === "initializing" || progress?.phase === "completed" ? 100 : downloading && progress.totalBytes !== null && progress.totalBytes > 0 ? Math.min(100, Math.floor(progress.downloadedBytes / progress.totalBytes * 100)) : void 0;
4025
+ const label = downloading ? t("voice.setup.downloadFile", {
4026
+ index: progress.fileIndex,
4027
+ count: progress.fileCount
4028
+ }) : progress?.phase === "verifying" ? t("voice.models.verifying") : progress?.phase === "initializing" ? t("voice.setup.initializing") : progress?.phase === "completed" ? t("voice.setup.completed") : t("voice.setup.checking");
4029
+ const bytes = downloading ? progress.totalBytes === null ? t("voice.setup.downloadedSize", { size: formatDownloadSize(progress.downloadedBytes) }) : `${formatDownloadSize(progress.downloadedBytes)} / ${formatDownloadSize(progress.totalBytes)}` : void 0;
4030
+ return (0, react.createElement)("div", {
4031
+ role: "status",
4032
+ "aria-live": "polite",
4033
+ style: {
4034
+ display: "grid",
4035
+ gap: 7,
4036
+ color: require_theme.dshThemeColor.labelSecondary,
4037
+ fontSize: 13,
4038
+ lineHeight: 1.5
4039
+ }
4040
+ }, (0, react.createElement)("div", { style: rowStyle }, (0, react.createElement)("span", null, label), percent === void 0 ? null : (0, react.createElement)("span", { style: {
4041
+ flexShrink: 0,
4042
+ fontVariantNumeric: "tabular-nums"
4043
+ } }, downloading ? t("voice.setup.filePercent", { percent }) : "100%")), (0, react.createElement)("progress", {
4044
+ max: 100,
4045
+ ...percent === void 0 ? {} : { value: percent },
4046
+ "aria-label": downloading ? t("voice.setup.fileProgress") : label,
4047
+ style: {
4048
+ display: "block",
4049
+ width: "100%",
4050
+ height: 10,
4051
+ accentColor: require_theme.dshThemeColor.accent
4052
+ }
4053
+ }), downloading ? (0, react.createElement)("span", { style: { overflowWrap: "anywhere" } }, progress.fileName) : null, bytes === void 0 ? null : (0, react.createElement)("span", { style: { fontVariantNumeric: "tabular-nums" } }, bytes));
4054
+ }
4055
+ function formatDownloadSize(bytes) {
4056
+ if (bytes < 1024) return `${bytes} B`;
4057
+ if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
4058
+ return `${(bytes / 1048576).toFixed(1)} MB`;
4059
+ }
4060
+ function errorMessage(cause) {
4061
+ return cause instanceof Error ? cause.message : String(cause);
4062
+ }
4063
+ function button(label, disabled, onClick, primary = false) {
4064
+ return (0, react.createElement)("button", {
4065
+ type: "button",
4066
+ disabled,
4067
+ onClick,
4068
+ style: {
4069
+ ...primary ? require_theme.dshSettingsPrimaryButtonStyle : require_theme.dshSettingsButtonStyle,
4070
+ whiteSpace: "nowrap",
4071
+ flexShrink: 0,
4072
+ ...disabled ? {
4073
+ opacity: .5,
4074
+ cursor: "default"
4075
+ } : {}
4076
+ }
4077
+ }, label);
4078
+ }
4079
+ function badge(label, color) {
4080
+ return (0, react.createElement)("span", { style: {
4081
+ padding: "2px 7px",
4082
+ border: `1px solid ${require_theme.dshThemeColor.border}`,
4083
+ borderRadius: 5,
4084
+ fontSize: 11,
4085
+ lineHeight: 1.4,
4086
+ color
4087
+ } }, label);
4088
+ }
4089
+ const rowStyle = {
4090
+ display: "flex",
4091
+ justifyContent: "space-between",
4092
+ alignItems: "center",
4093
+ gap: 12
4094
+ };
4095
+ const helpStyle = {
4096
+ color: require_theme.dshThemeColor.labelSecondary,
4097
+ fontSize: 12,
4098
+ lineHeight: 1.55
4099
+ };
4100
+ const noticeStyle = {
4101
+ padding: "11px 13px",
4102
+ borderRadius: 8,
4103
+ background: require_theme.dshThemeColor.surfaceSubtle,
4104
+ fontSize: 13,
4105
+ lineHeight: 1.55
4106
+ };
4107
+ //#endregion
4108
+ //#region src/client/features/assistant-configuration-view.ts
4109
+ /** 创建与配置共用形象选择:选中 Live2D 时先确认引擎许可并自动安装,成功后才改草稿。 */
4110
+ function AssistantAvatarSelectionField({ services, appearance, avatarId, avatars, includeLegacy, disabled, t, onChange }) {
4111
+ const engine = useAssistantAvatarEngine({
4112
+ services,
4113
+ manager: require_manager.getAssistantAvatarManager(services),
4114
+ appearance,
4115
+ disabled
4116
+ });
4117
+ const choices = assistantAvatarChoices(appearance, [], t, includeLegacy);
4118
+ const avatar = avatars.find((model) => model.id === avatarId);
4119
+ const list = choices.some((choice) => choice.id === avatarId) ? choices : [{
4120
+ id: avatarId,
4121
+ label: t("awb.unavailableSelection", { id: avatarId }),
4122
+ thirdParty: false,
4123
+ disabled: true
4124
+ }, ...choices];
4125
+ const choose = (choice) => {
4126
+ if (disabled || !avatars.some((model) => model.id === choice.id)) return;
4127
+ if (avatars.find((item) => item.id === choice.id)?.renderer === "live2d") {
4128
+ engine.ensure(() => onChange({ avatarId: choice.id }));
4129
+ return;
4130
+ }
4131
+ onChange({ avatarId: choice.id });
4132
+ };
4133
+ return (0, react.createElement)("div", { style: {
4134
+ display: "grid",
4135
+ gap: 8
4136
+ } }, (0, react.createElement)(AssistantAvatarPicker, {
4137
+ label: t("awb.avatar"),
4138
+ value: avatarId,
4139
+ disabled: disabled || engine.installing,
4140
+ t,
4141
+ choices: list,
4142
+ onChoose: choose
4143
+ }), (0, react.createElement)(AssistantAvatarEngineProgress, {
4144
+ controller: engine,
4145
+ t
4146
+ }), (0, react.createElement)("p", { style: help }, t("avatar.externalHint")), avatar?.renderer !== "live2d" ? null : (0, react.createElement)("p", { style: help }, t("avatar.live2dDependencyHint")), (0, react.createElement)(AssistantAvatarEngineDialog, {
4147
+ controller: engine,
4148
+ t
4149
+ }));
4150
+ }
4151
+ function AssistantConfigurationFields({ draft, catalog, appearance, initializing = false, includeAvatar = true, services, t, disabled, onChange }) {
4152
+ const models = catalog?.models ?? [];
4153
+ const avatars = require_manager.listAssistantAvatars(appearance, !initializing);
4154
+ const avatar = avatars.find((model) => model.id === draft.avatarId);
4155
+ const avatarChoices = assistantAvatarChoices(appearance, [], t, !initializing);
4156
+ const input = (label, node) => (0, react.createElement)("label", { style: field }, (0, react.createElement)("span", null, label), node);
4157
+ const select = (label, value, change, options) => input(label, (0, react.createElement)("select", {
4158
+ value,
4159
+ disabled,
4160
+ style: identityFieldStyle,
4161
+ onChange: (event) => change(event.currentTarget.value)
4162
+ }, ...options));
4163
+ const missing = (id) => (0, react.createElement)("option", {
4164
+ key: id,
4165
+ value: id
4166
+ }, t("awb.unavailableSelection", { id }));
4167
+ return (0, react.createElement)("div", {
4168
+ "data-codingns-assistant-identity": true,
4169
+ style: {
4170
+ display: "grid",
4171
+ gap: 11
4172
+ }
4173
+ }, (0, react.createElement)("div", {
4174
+ "data-codingns-assistant-identity-row": true,
4175
+ style: {
4176
+ display: "grid",
4177
+ alignItems: "start",
4178
+ gridTemplateColumns: services === void 0 || avatar === void 0 ? "minmax(0, 1fr)" : "minmax(0, 1fr) minmax(0, 176px)",
4179
+ columnGap: 16,
4180
+ rowGap: 8,
4181
+ minWidth: 0
4182
+ }
4183
+ }, input(t("awb.name"), (0, react.createElement)("input", {
4184
+ value: draft.name,
4185
+ maxLength: 80,
4186
+ disabled,
4187
+ placeholder: t("awb.namePlaceholder"),
4188
+ style: identityFieldStyle,
4189
+ onChange: (event) => onChange({ name: event.currentTarget.value })
4190
+ })), services === void 0 || avatar === void 0 ? null : (0, react.createElement)(AssistantAvatarPortraitEditor, {
4191
+ services,
4192
+ model: avatar,
4193
+ disabled,
4194
+ t
4195
+ })), select(t("awb.model"), draft.modelKey, (modelKey) => onChange({ modelKey }), [
4196
+ (0, react.createElement)("option", {
4197
+ key: "",
4198
+ value: ""
4199
+ }, t("awb.defaultModel")),
4200
+ ...draft.modelKey && !models.some((model) => JSON.stringify([model.provider, model.model]) === draft.modelKey) ? [missing(draft.modelKey)] : [],
4201
+ ...models.map((model) => (0, react.createElement)("option", {
4202
+ key: JSON.stringify([model.provider, model.model]),
4203
+ value: JSON.stringify([model.provider, model.model])
4204
+ }, `${model.label} · ${model.provider}`))
4205
+ ]), catalog?.models.length === 0 ? (0, react.createElement)("p", { style: help }, t("awb.noModel"), ...catalog.errors.map((error) => (0, react.createElement)("span", {
4206
+ key: error,
4207
+ style: { display: "block" }
4208
+ }, error))) : null, input(t("awb.personality"), (0, react.createElement)("textarea", {
4209
+ value: draft.personality,
4210
+ maxLength: require_manager.ASSISTANT_PERSONALITY_MAX_CHARS,
4211
+ rows: 4,
4212
+ disabled,
4213
+ placeholder: t("awb.personalityPlaceholder"),
4214
+ style: {
4215
+ ...identityFieldStyle,
4216
+ minHeight: 96,
4217
+ maxHeight: 200,
4218
+ resize: "vertical",
4219
+ lineHeight: 1.65
4220
+ },
4221
+ onChange: (event) => onChange({ personality: event.currentTarget.value })
4222
+ })), !includeAvatar ? null : services === void 0 ? (0, react.createElement)("div", { style: {
4223
+ display: "grid",
4224
+ gap: 8
4225
+ } }, (0, react.createElement)(AssistantAvatarPicker, {
4226
+ label: t("awb.avatar"),
4227
+ value: draft.avatarId,
4228
+ disabled,
4229
+ t,
4230
+ choices: avatarChoices.some((choice) => choice.id === draft.avatarId) ? avatarChoices : [{
4231
+ id: draft.avatarId,
4232
+ label: t("awb.unavailableSelection", { id: draft.avatarId }),
4233
+ thirdParty: false,
4234
+ disabled: true
4235
+ }, ...avatarChoices],
4236
+ onChoose: (choice) => {
4237
+ if (avatars.some((avatar) => avatar.id === choice.id)) onChange({ avatarId: choice.id });
4238
+ }
4239
+ }), (0, react.createElement)("p", { style: help }, t("avatar.externalHint")), avatar?.renderer !== "live2d" ? null : (0, react.createElement)("p", { style: help }, t("avatar.live2dDependencyHint"))) : (0, react.createElement)(AssistantAvatarSelectionField, {
4240
+ services,
4241
+ appearance,
4242
+ avatarId: draft.avatarId,
4243
+ avatars,
4244
+ includeLegacy: !initializing,
4245
+ disabled,
4246
+ t,
4247
+ onChange
4248
+ }));
4249
+ }
4250
+ /** 创建后的各页只装配自己的字段;页面切换不持有或重置父级草稿。 */
4251
+ function AssistantConfigurationPage({ tab, active = true, services, value, draft, catalog, appearance, tts, workspaces, t, disabled, onChange, onDebug, onError, onReset, ...previewProps }) {
4252
+ switch (tab) {
4253
+ case "basic": return (0, react.createElement)(AssistantConfigurationFields, {
4254
+ draft,
4255
+ catalog,
4256
+ appearance,
4257
+ includeAvatar: false,
4258
+ services,
4259
+ t,
4260
+ disabled,
4261
+ onChange
4262
+ });
4263
+ case "appearance": return (0, react.createElement)("div", { style: {
4264
+ display: "grid",
4265
+ gap: 11
4266
+ } }, (0, react.createElement)(AssistantAppearanceEditor, {
4267
+ services,
4268
+ enabled: !disabled,
4269
+ active,
4270
+ ...previewProps
4271
+ }));
4272
+ case "voice": return (0, react.createElement)("div", { style: {
4273
+ display: "grid",
4274
+ gap: 11
4275
+ } }, (0, react.createElement)(AssistantVoiceInitializationPanel, {
4276
+ services,
4277
+ enabled: !disabled,
4278
+ active,
4279
+ inputSettings: (groupActive) => (0, react.createElement)("div", { style: {
4280
+ display: "grid",
4281
+ gap: 11
4282
+ } }, (0, react.createElement)(AssistantAudioDevice, {
4283
+ services,
4284
+ direction: "input",
4285
+ active: groupActive,
4286
+ disabled,
4287
+ t,
4288
+ onError
4289
+ }), (0, react.createElement)(VoiceModelManagerPanel, {
4290
+ services,
4291
+ value: value.voice,
4292
+ enabled: !disabled,
4293
+ active: groupActive
4294
+ })),
4295
+ outputSettings: (groupActive) => (0, react.createElement)("div", { style: {
4296
+ display: "grid",
4297
+ gap: 11
4298
+ } }, (0, react.createElement)(AssistantAudioDevice, {
4299
+ services,
4300
+ direction: "output",
4301
+ active: groupActive,
4302
+ disabled,
4303
+ t,
4304
+ onError
4305
+ }), (0, react.createElement)(AssistantVoiceSettings, {
4306
+ services,
4307
+ enabled: !disabled,
4308
+ active: groupActive,
4309
+ embedded: true
4310
+ }))
4311
+ }));
4312
+ case "more": return (0, react.createElement)("div", { style: {
4313
+ display: "grid",
4314
+ gap: 11
4315
+ } }, (0, react.createElement)(AssistantWorkspaceFields, {
4316
+ draft,
4317
+ workspaces,
4318
+ t,
4319
+ disabled,
4320
+ onChange
4321
+ }), (0, react.createElement)(AssistantAdvancedSettings, {
4322
+ services,
4323
+ value,
4324
+ disabled,
4325
+ t,
4326
+ onDebug,
4327
+ onError
4328
+ }), (0, react.createElement)("div", { style: {
4329
+ paddingTop: 16,
4330
+ borderTop: `1px solid ${require_theme.dshThemeColor.border}`
4331
+ } }, (0, react.createElement)("button", {
4332
+ type: "button",
4333
+ "data-codingns-assistant-reset": true,
4334
+ disabled: disabled || onReset === void 0,
4335
+ style: {
4336
+ ...require_theme.dshSettingsButtonStyle,
4337
+ color: require_theme.dshThemeColor.error
4338
+ },
4339
+ onClick: onReset
4340
+ }, t("awb.reset"))));
4341
+ }
4342
+ }
4343
+ /** 兼容原能力表单入口;实际配置页分别装配项目字段与声音字段。 */
4344
+ function AssistantCapabilityFields({ draft, tts, workspaces, t, disabled, onChange }) {
4345
+ return (0, react.createElement)("div", { style: {
4346
+ display: "grid",
4347
+ gap: 18
4348
+ } }, AssistantWorkspaceFields({
4349
+ draft,
4350
+ workspaces,
4351
+ t,
4352
+ disabled,
4353
+ onChange
4354
+ }), AssistantVoiceFields({
4355
+ draft,
4356
+ tts,
4357
+ t,
4358
+ disabled,
4359
+ onChange
4360
+ }));
4361
+ }
4362
+ /** 项目范围放到更多设置,保留暂不可用项目以避免编辑时丢失原有选择。 */
4363
+ function AssistantWorkspaceFields({ draft, workspaces, t, disabled, onChange }) {
4364
+ const knownWorkspaces = new Set(workspaces.map((workspace) => workspace.workspaceId));
4365
+ return (0, react.createElement)("fieldset", {
4366
+ "data-codingns-assistant-projects": true,
4367
+ style: {
4368
+ border: 0,
4369
+ padding: 0,
4370
+ margin: 0,
4371
+ minWidth: 0
4372
+ },
4373
+ disabled
4374
+ }, (0, react.createElement)("legend", { style: {
4375
+ fontSize: 14,
4376
+ fontWeight: 600,
4377
+ padding: 0,
4378
+ marginBottom: 8
4379
+ } }, t("awb.workspaces")), (0, react.createElement)("p", { style: help }, t("awb.scopeHint")), (0, react.createElement)("div", { style: {
4380
+ display: "grid",
4381
+ gap: 8,
4382
+ maxHeight: 180,
4383
+ overflowY: "auto",
4384
+ marginTop: 8
4385
+ } }, ...[...workspaces, ...draft.managedWorkspaceIds.filter((id) => !knownWorkspaces.has(id)).map((workspaceId) => ({
4386
+ workspaceId,
4387
+ title: t("awb.offlineWorkspace", { id: workspaceId })
4388
+ }))].map((workspace) => (0, react.createElement)("label", {
4389
+ key: workspace.workspaceId,
4390
+ style: require_assistant_workbench.assistantSettingCheckboxStyle
4391
+ }, (0, react.createElement)("input", {
4392
+ type: "checkbox",
4393
+ checked: draft.managedWorkspaceIds.includes(workspace.workspaceId),
4394
+ onChange: (event) => onChange({ managedWorkspaceIds: event.currentTarget.checked ? [...draft.managedWorkspaceIds, workspace.workspaceId] : draft.managedWorkspaceIds.filter((id) => id !== workspace.workspaceId) })
4395
+ }), workspace.title))));
4396
+ }
4397
+ /** 没有安装 MOSS 时只有浏览器声音可选,不因标签拆分而放宽可用性校验。 */
4398
+ function AssistantVoiceFields({ draft, tts, t, disabled, onChange }) {
4399
+ const mossReady = tts?.status.ready === true;
4400
+ const voices = mossReady ? tts.voices : [];
4401
+ return (0, react.createElement)("div", {
4402
+ "data-codingns-assistant-voice-selection": true,
4403
+ style: {
4404
+ display: "grid",
4405
+ gap: 10
4406
+ }
4407
+ }, (0, react.createElement)("label", { style: field }, (0, react.createElement)("span", null, t("awb.voice")), (0, react.createElement)("select", {
4408
+ value: draft.ttsBackend === "browser" || !mossReady ? "browser" : draft.voiceId,
4409
+ disabled,
4410
+ style: identityFieldStyle,
4411
+ onChange: (event) => {
4412
+ const voice = event.currentTarget.value;
4413
+ if (voice === "browser") onChange({ ttsBackend: "browser" });
4414
+ else if (mossReady && voices.some((item) => item.id === voice)) onChange({
4415
+ ttsBackend: "moss-onnx",
4416
+ voiceId: voice
4417
+ });
4418
+ }
4419
+ }, (0, react.createElement)("option", { value: "browser" }, t("awb.browserVoice")), ...voices.map((voice) => (0, react.createElement)("option", {
4420
+ key: voice.id,
4421
+ value: voice.id
4422
+ }, voice.name)))), mossReady ? null : (0, react.createElement)("p", { style: help }, t("awb.mossSetupHint")));
4423
+ }
4424
+ function AssistantAdvancedSettings({ services, value, disabled, t, onDebug, onError }) {
4425
+ const defaults = require_assistant_prompts.readAssistantPrompts(value.prompts);
4426
+ const prompts = services.configurationDraft ? {
4427
+ ...defaults,
4428
+ ...value.prompts
4429
+ } : defaults;
4430
+ const savePrompt = async (kind, text) => {
4431
+ try {
4432
+ if (!await services.settings.mutate([{
4433
+ op: "set",
4434
+ path: [
4435
+ "assistant",
4436
+ "prompts",
4437
+ kind
4438
+ ],
4439
+ value: text
4440
+ }])) throw new Error(t("settings.moduleWriteRejected"));
4441
+ } catch (cause) {
4442
+ onError(message(cause));
4443
+ throw cause;
4444
+ }
4445
+ };
4446
+ return (0, react.createElement)("div", { style: {
4447
+ display: "grid",
4448
+ gap: 14,
4449
+ marginTop: 14
4450
+ } }, (0, react.createElement)("p", { style: help }, t("awb.moreHint")), ...["index", "chat"].map((kind) => (0, react.createElement)(require_assistant_prompt_editor.AssistantPromptEditor, {
4451
+ key: kind,
4452
+ kind,
4453
+ value: prompts[kind],
4454
+ disabled,
4455
+ onSave: savePrompt,
4456
+ ...services.configurationDraft ? { onChange: savePrompt } : {},
4457
+ t
4458
+ })), (0, react.createElement)("button", {
4459
+ type: "button",
4460
+ disabled,
4461
+ style: require_theme.dshSettingsButtonStyle,
4462
+ onClick: onDebug
4463
+ }, t("assistant.debug.open")));
4464
+ }
4465
+ /** 设备按输入、输出分组枚举,折叠时保留选择但停止设备查询。 */
4466
+ function AssistantAudioDevice({ services, direction, active, disabled, t, onError }) {
4467
+ const adapter = require_manager.getGlobalVoiceAdapter(services);
4468
+ const output = direction === "output";
4469
+ const [devices, setDevices] = (0, react.useState)([]);
4470
+ const [selectedId, setSelectedId] = (0, react.useState)((output ? adapter?.outputDeviceId : adapter?.inputDeviceId) ?? "");
4471
+ (0, react.useEffect)(() => {
4472
+ if (!active || output && !adapter?.outputDeviceSupported) return void 0;
4473
+ let stopped = false;
4474
+ const refresh = () => {
4475
+ (output ? adapter?.enumerateOutputDevices() : adapter?.enumerateInputDevices())?.then((devices) => {
4476
+ if (!stopped) setDevices(devices);
4477
+ }).catch(() => void 0);
4478
+ };
4479
+ refresh();
4480
+ globalThis.navigator?.mediaDevices?.addEventListener?.("devicechange", refresh);
4481
+ return () => {
4482
+ stopped = true;
4483
+ globalThis.navigator?.mediaDevices?.removeEventListener?.("devicechange", refresh);
4484
+ };
4485
+ }, [
4486
+ active,
4487
+ adapter,
4488
+ output
4489
+ ]);
4490
+ if (output && !adapter?.outputDeviceSupported) return null;
4491
+ return (0, react.createElement)("label", { style: field }, t(output ? "voice.outputDevice" : "voice.inputDevice"), (0, react.createElement)("select", {
4492
+ disabled,
4493
+ value: selectedId,
4494
+ style: require_theme.dshSettingsFieldStyle,
4495
+ onChange: (event) => {
4496
+ const id = event.currentTarget.value;
4497
+ (output ? adapter?.selectOutputDevice(id) : adapter?.selectInputDevice(id))?.then(() => setSelectedId(id)).catch((cause) => onError(message(cause)));
4498
+ }
4499
+ }, (0, react.createElement)("option", { value: "" }, t(output ? "voice.defaultOutput" : "voice.defaultMicrophone")), ...devices.map((device) => (0, react.createElement)("option", {
4500
+ key: device.deviceId,
4501
+ value: device.deviceId
4502
+ }, device.label || t(output ? "voice.unnamedOutput" : "voice.unnamedMicrophone")))));
4503
+ }
4504
+ function message(error) {
4505
+ return error instanceof Error ? error.message : String(error);
4506
+ }
4507
+ const help = {
4508
+ ...require_theme.dshSettingsHelpStyle,
4509
+ margin: 0
4510
+ };
4511
+ const field = require_assistant_workbench.assistantSettingFieldStyle;
4512
+ const identityFieldStyle = {
4513
+ ...require_theme.dshSettingsFieldStyle,
4514
+ borderRadius: 8
4515
+ };
4516
+ //#endregion
4517
+ exports.AssistantCapabilityFields = AssistantCapabilityFields;
4518
+ exports.AssistantConfigurationFields = AssistantConfigurationFields;
4519
+ exports.AssistantConfigurationPage = AssistantConfigurationPage;
4520
+ exports.AssistantVoiceFields = AssistantVoiceFields;
4521
+ exports.AssistantWorkspaceFields = AssistantWorkspaceFields;
4522
+ return module.exports;
4523
+ }
4524
+ });
4525
+
4526
+ //# sourceMappingURL=client.assistant-configuration-view.js.map