@flowingspring/dsh-voco 0.3.6 → 0.3.8

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.
package/lib/client.js CHANGED
@@ -4,35 +4,63 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
- let react_jsx_runtime = require("react/jsx-runtime");
8
7
  let react = require("react");
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
9
  let react_dom_client = require("react-dom/client");
10
10
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
11
11
  let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
12
12
  //#region \0dsh-css:D:\QUAN\DevCode\Python\Project\dsh\dsh-voco\packages\ui-voice\src\client\VoiceControl.module.css.mjs
13
- const css$3 = ".TFZsua_controls{align-items:center;gap:2px;display:inline-flex}.TFZsua_button{width:28px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:8px;justify-content:center;align-items:center;padding:0;display:inline-flex}.TFZsua_button:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.TFZsua_active{color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-interactive-bg-hover-solid)}.TFZsua_button[data-muted=true]{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, currentColor 12%, transparent)}.TFZsua_interruptButton,.TFZsua_interruptButton:hover{color:var(--dsw-alias-state-error-primary)}.TFZsua_button[data-muted=true]:hover{background:color-mix(in srgb, currentColor 18%, transparent)}.TFZsua_button[data-state=speaking][data-muted=false]{animation:1.2s ease-in-out infinite TFZsua_voice-pulse}@keyframes TFZsua_voice-pulse{50%{opacity:.55;transform:scale(.94)}}@media (prefers-reduced-motion:reduce){.TFZsua_button[data-state=speaking][data-muted=false]{animation:none}}";
14
- const tagId$3 = "@flowingspring/dsh-voco/VoiceControl.module.css";
15
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
13
+ const css$4 = ".TFZsua_controls{align-items:center;gap:2px;display:inline-flex}.TFZsua_button{touch-action:manipulation;width:28px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:8px;justify-content:center;align-items:center;padding:0;display:inline-flex}.TFZsua_button:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.TFZsua_active{color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-interactive-bg-hover-solid)}.TFZsua_button[data-muted=true]{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, currentColor 12%, transparent)}.TFZsua_button[data-push-to-talk=true]{color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-interactive-bg-hover-solid);transform:scale(.94)}.TFZsua_interruptButton,.TFZsua_interruptButton:hover{color:var(--dsw-alias-state-error-primary)}.TFZsua_button[data-muted=true]:hover{background:color-mix(in srgb, currentColor 18%, transparent)}.TFZsua_button[data-state=speaking][data-muted=false]{animation:1.2s ease-in-out infinite TFZsua_voice-pulse}@keyframes TFZsua_voice-pulse{50%{opacity:.55;transform:scale(.94)}}@media (prefers-reduced-motion:reduce){.TFZsua_button[data-state=speaking][data-muted=false]{animation:none}}";
14
+ const tagId$4 = "@flowingspring/dsh-voco/VoiceControl.module.css";
15
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
16
16
  const tag = document.createElement("style");
17
17
  tag.dataset.plugin = "@flowingspring/dsh-voco";
18
- tag.dataset.pluginCss = tagId$3;
19
- tag.textContent = css$3;
18
+ tag.dataset.pluginCss = tagId$4;
19
+ tag.textContent = css$4;
20
20
  document.head.appendChild(tag);
21
21
  }
22
22
  var VoiceControl_module_css_default = {
23
- "interruptButton": "TFZsua_interruptButton",
24
- "voice-pulse": "TFZsua_voice-pulse",
25
23
  "controls": "TFZsua_controls",
26
24
  "button": "TFZsua_button",
27
- "active": "TFZsua_active"
25
+ "active": "TFZsua_active",
26
+ "interruptButton": "TFZsua_interruptButton",
27
+ "voice-pulse": "TFZsua_voice-pulse"
28
28
  };
29
29
  //#endregion
30
30
  //#region ../ui-voice/src/client/VoiceControl.tsx
31
31
  /** Start a fresh Voice Session from this Session's location or control the active connection. */
32
- function VoiceControl({ sessionId, useVoice, startVoice, retryVoice, setVoiceMuted, t, interruptResponse }) {
32
+ function VoiceControl({ sessionId, useVoice, startVoice, retryVoice, beginPushToTalk, endPushToTalk, setVoiceMuted, t, interruptResponse }) {
33
33
  const state = useVoice((snapshot) => snapshot.state);
34
34
  const activeSessionId = useVoice((snapshot) => snapshot.sessionId);
35
35
  const inputMuted = useVoice((snapshot) => snapshot.inputMuted);
36
+ const pushToTalkActive = useVoice((snapshot) => snapshot.pushToTalkActive);
37
+ const timer = (0, react.useRef)(void 0);
38
+ const pointerDown = (0, react.useRef)(false);
39
+ const longPress = (0, react.useRef)(false);
40
+ const suppressClick = (0, react.useRef)(false);
41
+ const clearTimer = () => {
42
+ if (timer.current !== void 0) {
43
+ window.clearTimeout(timer.current);
44
+ timer.current = void 0;
45
+ }
46
+ };
47
+ const releasePushToTalk = () => {
48
+ clearTimer();
49
+ pointerDown.current = false;
50
+ if (longPress.current) {
51
+ longPress.current = false;
52
+ endPushToTalk();
53
+ }
54
+ };
55
+ (0, react.useEffect)(() => {
56
+ window.addEventListener("blur", releasePushToTalk);
57
+ document.addEventListener("visibilitychange", releasePushToTalk);
58
+ return () => {
59
+ window.removeEventListener("blur", releasePushToTalk);
60
+ document.removeEventListener("visibilitychange", releasePushToTalk);
61
+ releasePushToTalk();
62
+ };
63
+ }, []);
36
64
  const toggle = async () => {
37
65
  if (state === "error" && activeSessionId !== void 0) {
38
66
  try {
@@ -55,6 +83,35 @@ window.__ModuleLoader__.load({
55
83
  interruptResponse();
56
84
  } catch {}
57
85
  };
86
+ const onPointerDown = (event) => {
87
+ if (event.pointerType === "mouse" && event.button !== 0) return;
88
+ pointerDown.current = true;
89
+ longPress.current = false;
90
+ suppressClick.current = false;
91
+ if (typeof event.currentTarget.setPointerCapture === "function") event.currentTarget.setPointerCapture(event.pointerId);
92
+ clearTimer();
93
+ timer.current = window.setTimeout(() => {
94
+ timer.current = void 0;
95
+ if (!pointerDown.current) return;
96
+ longPress.current = true;
97
+ suppressClick.current = true;
98
+ beginPushToTalk(sessionId);
99
+ }, 400);
100
+ };
101
+ const onPointerUp = (event) => {
102
+ releasePushToTalk();
103
+ if (event.currentTarget.hasPointerCapture?.(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
104
+ };
105
+ const onPointerCancel = () => {
106
+ releasePushToTalk();
107
+ };
108
+ const onClick = () => {
109
+ if (suppressClick.current) {
110
+ suppressClick.current = false;
111
+ return;
112
+ }
113
+ toggle();
114
+ };
58
115
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
59
116
  className: VoiceControl_module_css_default.controls,
60
117
  children: [state === "speaking" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -88,14 +145,17 @@ window.__ModuleLoader__.load({
88
145
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
89
146
  type: "button",
90
147
  className: `${VoiceControl_module_css_default.button} ${active ? VoiceControl_module_css_default.active : ""}`,
91
- "aria-label": label,
148
+ "aria-label": pushToTalkActive ? t("control.pushToTalk") : label,
92
149
  "aria-pressed": active ? inputMuted : void 0,
93
150
  title: label,
94
151
  "data-state": state,
95
152
  "data-muted": inputMuted,
96
- onClick: () => {
97
- toggle();
98
- },
153
+ "data-push-to-talk": pushToTalkActive,
154
+ onPointerDown,
155
+ onPointerUp,
156
+ onPointerCancel,
157
+ onLostPointerCapture: onPointerCancel,
158
+ onClick,
99
159
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
100
160
  viewBox: "0 0 16 16",
101
161
  width: "16",
@@ -153,33 +213,33 @@ window.__ModuleLoader__.load({
153
213
  }
154
214
  //#endregion
155
215
  //#region \0dsh-css:D:\QUAN\DevCode\Python\Project\dsh\dsh-voco\packages\ui-voice\src\client\VoiceNodeViews.module.css.mjs
156
- const css$2 = ".Kt2smW_utterance{flex-direction:column;align-items:flex-start;gap:6px;min-width:0;display:flex}.Kt2smW_utterance[data-role=user]{align-items:flex-end}.Kt2smW_bubble{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);max-width:min(525px,82%);color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;white-space:pre-wrap;border-radius:18px;padding:10px 16px;font-size:16px;line-height:24px}.Kt2smW_utterance[data-role=user] .Kt2smW_bubble{background:var(--dsw-specific-bubble);border-color:#0000}.Kt2smW_utterance[data-state=streaming] .Kt2smW_bubble{color:var(--dsw-alias-label-secondary)}.Kt2smW_meta,.Kt2smW_voiceBadge{align-items:center;display:inline-flex}.Kt2smW_meta{color:var(--dsw-alias-label-tertiary);gap:8px;padding:0 5px;font-size:10px;line-height:14px}.Kt2smW_voiceBadge{color:var(--dsw-alias-state-business-primary);letter-spacing:.04em;gap:4px;font-weight:600}.Kt2smW_miniWave{align-items:center;gap:1px;height:10px;display:inline-flex}.Kt2smW_miniWave i{background:currentColor;border-radius:1px;width:1.5px;height:4px}.Kt2smW_miniWave i:nth-child(2){height:8px}.Kt2smW_miniWave i:nth-child(3){height:6px}.Kt2smW_taskCard{box-sizing:border-box;width:fit-content;min-width:0;max-width:min(525px,100%)}.Kt2smW_taskSummary{min-width:0;max-width:100%;color:inherit;cursor:pointer;font:inherit;background:0 0;border:0;border-radius:6px;align-items:center;gap:7px;margin:-3px -5px;padding:3px 5px;display:inline-flex}.Kt2smW_taskSummary:hover{background:var(--dsw-alias-bg-layer-1)}.Kt2smW_taskDot{background:var(--dsw-alias-state-business-primary);border-radius:50%;flex:none;width:7px;height:7px}.Kt2smW_taskCard[data-status=completed] .Kt2smW_taskDot{background:var(--dsw-alias-state-success-primary)}.Kt2smW_taskCard[data-status=failed] .Kt2smW_taskDot{background:var(--dsw-alias-state-error-primary)}.Kt2smW_taskCard[data-status=cancelled] .Kt2smW_taskDot,.Kt2smW_taskCard[data-status=interrupted] .Kt2smW_taskDot{background:var(--dsw-alias-state-warn-primary)}.Kt2smW_taskTitle{min-width:0;color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;line-height:20px;overflow:hidden}.Kt2smW_taskStatus{color:var(--dsw-alias-label-tertiary);flex:none;font-size:11px;line-height:16px}.Kt2smW_taskStatus:before{content:\"·\";margin-right:7px}.Kt2smW_taskChevron{border-right:1px solid var(--dsw-alias-label-tertiary);border-bottom:1px solid var(--dsw-alias-label-tertiary);width:6px;height:6px;margin:0 2px 0 1px;transition:transform .12s;transform:rotate(-45deg)}.Kt2smW_taskCard[data-expanded=true] .Kt2smW_taskChevron{transform:rotate(45deg)}.Kt2smW_taskInput,.Kt2smW_taskUpdate{overflow-wrap:anywhere;margin:0;font-size:13px;line-height:20px}.Kt2smW_taskInput{color:var(--dsw-alias-label-primary)}.Kt2smW_taskUpdate{color:var(--dsw-alias-label-secondary);margin-top:4px}.Kt2smW_taskDetails{border-left:1px solid var(--dsw-alias-border-l2);margin:9px 0 0 3px;padding:2px 0 2px 16px}.Kt2smW_taskActions{align-items:center;gap:14px;margin-top:7px;display:flex}.Kt2smW_taskLink{color:var(--dsw-alias-state-business-primary);cursor:pointer;font:inherit;background:0 0;border:0;padding:0;font-size:12px;line-height:18px}.Kt2smW_taskLink:hover:not(:disabled){text-underline-position:from-font;text-decoration:underline}.Kt2smW_taskLink:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.Kt2smW_taskCancel{color:var(--dsw-alias-state-error-primary)}";
157
- const tagId$2 = "@flowingspring/dsh-voco/VoiceNodeViews.module.css";
158
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
216
+ const css$3 = ".Kt2smW_utterance{flex-direction:column;align-items:flex-start;gap:6px;min-width:0;display:flex}.Kt2smW_utterance[data-role=user]{align-items:flex-end}.Kt2smW_bubble{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);max-width:min(525px,82%);color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;white-space:pre-wrap;border-radius:18px;padding:10px 16px;font-size:16px;line-height:24px}.Kt2smW_utterance[data-role=user] .Kt2smW_bubble{background:var(--dsw-specific-bubble);border-color:#0000}.Kt2smW_utterance[data-state=streaming] .Kt2smW_bubble{color:var(--dsw-alias-label-secondary)}.Kt2smW_meta,.Kt2smW_voiceBadge{align-items:center;display:inline-flex}.Kt2smW_meta{color:var(--dsw-alias-label-tertiary);gap:8px;padding:0 5px;font-size:10px;line-height:14px}.Kt2smW_voiceBadge{color:var(--dsw-alias-state-business-primary);letter-spacing:.04em;gap:4px;font-weight:600}.Kt2smW_miniWave{align-items:center;gap:1px;height:10px;display:inline-flex}.Kt2smW_miniWave i{background:currentColor;border-radius:1px;width:1.5px;height:4px}.Kt2smW_miniWave i:nth-child(2){height:8px}.Kt2smW_miniWave i:nth-child(3){height:6px}.Kt2smW_taskCard{box-sizing:border-box;width:fit-content;min-width:0;max-width:min(525px,100%)}.Kt2smW_taskSummary{min-width:0;max-width:100%;color:inherit;cursor:pointer;font:inherit;background:0 0;border:0;border-radius:6px;align-items:center;gap:7px;margin:-3px -5px;padding:3px 5px;display:inline-flex}.Kt2smW_taskSummary:hover{background:var(--dsw-alias-bg-layer-1)}.Kt2smW_taskDot{background:var(--dsw-alias-state-business-primary);border-radius:50%;flex:none;width:7px;height:7px}.Kt2smW_taskCard[data-status=completed] .Kt2smW_taskDot{background:var(--dsw-alias-state-success-primary)}.Kt2smW_taskCard[data-status=failed] .Kt2smW_taskDot{background:var(--dsw-alias-state-error-primary)}.Kt2smW_taskCard[data-status=cancelled] .Kt2smW_taskDot,.Kt2smW_taskCard[data-status=interrupted] .Kt2smW_taskDot{background:var(--dsw-alias-state-warn-primary)}.Kt2smW_taskTitle{min-width:0;color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;line-height:20px;overflow:hidden}.Kt2smW_taskStatus{color:var(--dsw-alias-label-tertiary);flex:none;font-size:11px;line-height:16px}.Kt2smW_taskStatus:before{content:\"·\";margin-right:7px}.Kt2smW_taskChevron{border-right:1px solid var(--dsw-alias-label-tertiary);border-bottom:1px solid var(--dsw-alias-label-tertiary);width:6px;height:6px;margin:0 2px 0 1px;transition:transform .12s;transform:rotate(-45deg)}.Kt2smW_taskCard[data-expanded=true] .Kt2smW_taskChevron{transform:rotate(45deg)}.Kt2smW_taskInput,.Kt2smW_taskUpdate{overflow-wrap:anywhere;margin:0;font-size:13px;line-height:20px}.Kt2smW_taskInput{color:var(--dsw-alias-label-primary)}.Kt2smW_taskUpdate{color:var(--dsw-alias-label-secondary);margin-top:4px}.Kt2smW_taskDetails{border-left:1px solid var(--dsw-alias-border-l2);margin:9px 0 0 3px;padding:2px 0 2px 16px}.Kt2smW_taskActions{align-items:center;gap:14px;margin-top:7px;display:flex}.Kt2smW_taskLink{color:var(--dsw-alias-state-business-primary);cursor:pointer;font:inherit;background:0 0;border:0;padding:0;font-size:12px;line-height:18px}.Kt2smW_taskLink:hover:not(:disabled){text-underline-position:from-font;text-decoration:underline}.Kt2smW_taskLink:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.Kt2smW_taskCancel{color:var(--dsw-alias-state-error-primary)}";
217
+ const tagId$3 = "@flowingspring/dsh-voco/VoiceNodeViews.module.css";
218
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
159
219
  const tag = document.createElement("style");
160
220
  tag.dataset.plugin = "@flowingspring/dsh-voco";
161
- tag.dataset.pluginCss = tagId$2;
162
- tag.textContent = css$2;
221
+ tag.dataset.pluginCss = tagId$3;
222
+ tag.textContent = css$3;
163
223
  document.head.appendChild(tag);
164
224
  }
165
225
  var VoiceNodeViews_module_css_default = {
226
+ "taskSummary": "Kt2smW_taskSummary",
227
+ "taskTitle": "Kt2smW_taskTitle",
166
228
  "bubble": "Kt2smW_bubble",
167
- "taskDetails": "Kt2smW_taskDetails",
168
- "taskUpdate": "Kt2smW_taskUpdate",
169
- "voiceBadge": "Kt2smW_voiceBadge",
170
229
  "meta": "Kt2smW_meta",
230
+ "taskCancel": "Kt2smW_taskCancel",
231
+ "taskStatus": "Kt2smW_taskStatus",
232
+ "taskUpdate": "Kt2smW_taskUpdate",
171
233
  "utterance": "Kt2smW_utterance",
172
- "taskCard": "Kt2smW_taskCard",
173
234
  "taskInput": "Kt2smW_taskInput",
174
- "taskDot": "Kt2smW_taskDot",
175
- "taskChevron": "Kt2smW_taskChevron",
176
235
  "taskLink": "Kt2smW_taskLink",
177
- "taskTitle": "Kt2smW_taskTitle",
178
- "taskActions": "Kt2smW_taskActions",
236
+ "taskChevron": "Kt2smW_taskChevron",
179
237
  "miniWave": "Kt2smW_miniWave",
180
- "taskSummary": "Kt2smW_taskSummary",
181
- "taskStatus": "Kt2smW_taskStatus",
182
- "taskCancel": "Kt2smW_taskCancel"
238
+ "taskCard": "Kt2smW_taskCard",
239
+ "taskDot": "Kt2smW_taskDot",
240
+ "taskActions": "Kt2smW_taskActions",
241
+ "taskDetails": "Kt2smW_taskDetails",
242
+ "voiceBadge": "Kt2smW_voiceBadge"
183
243
  };
184
244
  //#endregion
185
245
  //#region ../ui-voice/src/client/VoiceNodeViews.tsx
@@ -307,21 +367,21 @@ window.__ModuleLoader__.load({
307
367
  }
308
368
  //#endregion
309
369
  //#region \0dsh-css:D:\QUAN\DevCode\Python\Project\dsh\dsh-voco\packages\ui-voice\src\client\VoiceOverlay.module.css.mjs
310
- const css$1 = ".GPPB2G_root{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-overlay);max-width:min(260px,100vw - 36px);min-height:36px;box-shadow:var(--dsw-shadow-lv2);color:var(--dsw-alias-label-primary);pointer-events:auto;border-radius:12px;align-items:center;gap:8px;padding:4px 6px 4px 12px;font-size:12px;line-height:18px;display:flex;position:absolute;bottom:18px;right:18px}.GPPB2G_wave{width:18px;height:18px;color:var(--dsw-alias-state-business-primary);flex:none;justify-content:center;align-items:center;gap:2px;display:inline-flex}.GPPB2G_wave i{background:currentColor;border-radius:1px;width:2px;height:5px;animation:.9s ease-in-out infinite alternate GPPB2G_voice-wave}.GPPB2G_wave i:nth-child(2){height:11px;animation-delay:-300ms}.GPPB2G_wave i:nth-child(3){height:8px;animation-delay:-600ms}.GPPB2G_wave i:nth-child(4){height:4px;animation-delay:-150ms}.GPPB2G_root[data-state=connecting] .GPPB2G_wave,.GPPB2G_root[data-state=error] .GPPB2G_wave{opacity:.55}.GPPB2G_root[data-state=error] .GPPB2G_wave{color:var(--dsw-alias-state-error-primary)}.GPPB2G_status{text-overflow:ellipsis;white-space:nowrap;flex:0 auto;min-width:0;overflow:hidden}.GPPB2G_root[data-muted=true] .GPPB2G_wave{color:var(--dsw-alias-state-error-primary);opacity:.65}.GPPB2G_root[data-muted=true] .GPPB2G_status{color:var(--dsw-alias-state-error-primary);font-weight:600}.GPPB2G_root[data-muted=true] .GPPB2G_wave i{animation:none}.GPPB2G_stop{min-height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border:0;border-radius:7px;flex:none;padding:0 8px}.GPPB2G_stop:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.GPPB2G_stop{color:var(--dsw-alias-state-error-primary)}@keyframes GPPB2G_voice-wave{to{transform:scaleY(.55)}}@media (prefers-reduced-motion:reduce){.GPPB2G_wave i{animation:none}}";
311
- const tagId$1 = "@flowingspring/dsh-voco/VoiceOverlay.module.css";
312
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
370
+ const css$2 = ".GPPB2G_root{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-overlay);max-width:min(260px,100vw - 36px);min-height:36px;box-shadow:var(--dsw-shadow-lv2);color:var(--dsw-alias-label-primary);pointer-events:auto;border-radius:12px;align-items:center;gap:8px;padding:4px 6px 4px 12px;font-size:12px;line-height:18px;display:flex;position:absolute;bottom:18px;right:18px}.GPPB2G_wave{width:18px;height:18px;color:var(--dsw-alias-state-business-primary);flex:none;justify-content:center;align-items:center;gap:2px;display:inline-flex}.GPPB2G_wave i{background:currentColor;border-radius:1px;width:2px;height:5px;animation:.9s ease-in-out infinite alternate GPPB2G_voice-wave}.GPPB2G_wave i:nth-child(2){height:11px;animation-delay:-300ms}.GPPB2G_wave i:nth-child(3){height:8px;animation-delay:-600ms}.GPPB2G_wave i:nth-child(4){height:4px;animation-delay:-150ms}.GPPB2G_root[data-state=connecting] .GPPB2G_wave,.GPPB2G_root[data-state=error] .GPPB2G_wave{opacity:.55}.GPPB2G_root[data-state=error] .GPPB2G_wave{color:var(--dsw-alias-state-error-primary)}.GPPB2G_status{text-overflow:ellipsis;white-space:nowrap;flex:0 auto;min-width:0;overflow:hidden}.GPPB2G_root[data-muted=true] .GPPB2G_wave{color:var(--dsw-alias-state-error-primary);opacity:.65}.GPPB2G_root[data-muted=true] .GPPB2G_status{color:var(--dsw-alias-state-error-primary);font-weight:600}.GPPB2G_root[data-muted=true] .GPPB2G_wave i{animation:none}.GPPB2G_stop{min-height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border:0;border-radius:7px;flex:none;padding:0 8px}.GPPB2G_stop:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.GPPB2G_stop{color:var(--dsw-alias-state-error-primary)}@keyframes GPPB2G_voice-wave{to{transform:scaleY(.55)}}@media (prefers-reduced-motion:reduce){.GPPB2G_wave i{animation:none}}";
371
+ const tagId$2 = "@flowingspring/dsh-voco/VoiceOverlay.module.css";
372
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
313
373
  const tag = document.createElement("style");
314
374
  tag.dataset.plugin = "@flowingspring/dsh-voco";
315
- tag.dataset.pluginCss = tagId$1;
316
- tag.textContent = css$1;
375
+ tag.dataset.pluginCss = tagId$2;
376
+ tag.textContent = css$2;
317
377
  document.head.appendChild(tag);
318
378
  }
319
379
  var VoiceOverlay_module_css_default = {
320
- "voice-wave": "GPPB2G_voice-wave",
380
+ "root": "GPPB2G_root",
321
381
  "wave": "GPPB2G_wave",
322
382
  "status": "GPPB2G_status",
323
- "root": "GPPB2G_root",
324
- "stop": "GPPB2G_stop"
383
+ "stop": "GPPB2G_stop",
384
+ "voice-wave": "GPPB2G_voice-wave"
325
385
  };
326
386
  //#endregion
327
387
  //#region ../ui-voice/src/client/VoiceOverlay.tsx
@@ -370,30 +430,30 @@ window.__ModuleLoader__.load({
370
430
  }
371
431
  //#endregion
372
432
  //#region \0dsh-css:D:\QUAN\DevCode\Python\Project\dsh\dsh-voco\packages\ui-voice\src\client\VoiceSessionMarkers.module.css.mjs
373
- const css = "._0brRca_slot{flex:none;justify-content:center;align-items:center;width:16px;height:20px;display:inline-flex}._0brRca_marker{width:16px;height:16px;color:var(--dsw-alias-state-business-primary);pointer-events:none;justify-content:center;align-items:center;display:inline-flex}._0brRca_marker svg{width:14px;height:14px;display:block}._0brRca_toggle{width:18px;height:18px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:4px;flex:none;justify-content:center;align-items:center;margin-left:1px;padding:0;font-size:16px;line-height:16px;display:inline-flex}._0brRca_toggle:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}._0brRca_children{flex-direction:column;gap:2px;margin:2px 0 2px 20px;display:flex}._0brRca_child{min-width:0;height:30px;color:var(--dsw-alias-label-secondary);cursor:pointer;text-align:left;font:inherit;background:0 0;border:0;border-radius:8px;align-items:center;gap:8px;padding:0 10px 0 12px;display:flex;position:relative}._0brRca_childMain{min-width:0;height:30px;color:inherit;cursor:pointer;text-align:left;font:inherit;background:0 0;border:0;flex:1;align-items:center;gap:8px;padding:0;display:flex}._0brRca_child:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}._0brRca_childMain:disabled{color:var(--dsw-alias-label-tertiary);cursor:not-allowed;opacity:.72}._0brRca_child[aria-current=page]{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-selected)}._0brRca_childDot{background:var(--dsw-alias-label-tertiary);border-radius:50%;flex:none;width:6px;height:6px}._0brRca_childDot[data-activity=running]{background:var(--dsw-alias-state-success-primary)}._0brRca_childLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}._0brRca_childTime{color:var(--dsw-alias-label-tertiary);flex:none;margin-left:auto;font-size:12px;line-height:20px}._0brRca_child[data-menu-open=true] ._0brRca_childTime,._0brRca_child:hover ._0brRca_childTime{display:none}._0brRca_childActions{flex:none;align-items:center;display:none;position:absolute;right:10px}._0brRca_child:hover ._0brRca_childActions,._0brRca_child[data-menu-open=true] ._0brRca_childActions{display:inline-flex}._0brRca_childMenuButton{width:16px;height:16px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:4px;justify-content:center;align-items:center;padding:0;display:inline-flex}._0brRca_childMenuButton:hover{color:var(--dsw-alias-label-primary)}._0brRca_deleteAction:not(:disabled){color:var(--dsw-alias-state-error-primary)}._0brRca_deleteStatus{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}._0brRca_deleteError{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}";
374
- const tagId = "@flowingspring/dsh-voco/VoiceSessionMarkers.module.css";
375
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
433
+ const css$1 = "._0brRca_slot{flex:none;justify-content:center;align-items:center;width:16px;height:20px;display:inline-flex}._0brRca_marker{width:16px;height:16px;color:var(--dsw-alias-state-business-primary);pointer-events:none;justify-content:center;align-items:center;display:inline-flex}._0brRca_marker svg{width:14px;height:14px;display:block}._0brRca_toggle{width:18px;height:18px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:4px;flex:none;justify-content:center;align-items:center;margin-left:1px;padding:0;font-size:16px;line-height:16px;display:inline-flex}._0brRca_toggle:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}._0brRca_children{flex-direction:column;gap:2px;margin:2px 0 2px 20px;display:flex}._0brRca_child{min-width:0;height:30px;color:var(--dsw-alias-label-secondary);cursor:pointer;text-align:left;font:inherit;background:0 0;border:0;border-radius:8px;align-items:center;gap:8px;padding:0 10px 0 12px;display:flex;position:relative}._0brRca_childMain{min-width:0;height:30px;color:inherit;cursor:pointer;text-align:left;font:inherit;background:0 0;border:0;flex:1;align-items:center;gap:8px;padding:0;display:flex}._0brRca_child:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}._0brRca_childMain:disabled{color:var(--dsw-alias-label-tertiary);cursor:not-allowed;opacity:.72}._0brRca_child[aria-current=page]{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-selected)}._0brRca_childDot{background:var(--dsw-alias-label-tertiary);border-radius:50%;flex:none;width:6px;height:6px}._0brRca_childDot[data-activity=running]{background:var(--dsw-alias-state-success-primary)}._0brRca_childLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}._0brRca_childTime{color:var(--dsw-alias-label-tertiary);flex:none;margin-left:auto;font-size:12px;line-height:20px}._0brRca_child[data-menu-open=true] ._0brRca_childTime,._0brRca_child:hover ._0brRca_childTime{display:none}._0brRca_childActions{flex:none;align-items:center;display:none;position:absolute;right:10px}._0brRca_child:hover ._0brRca_childActions,._0brRca_child[data-menu-open=true] ._0brRca_childActions{display:inline-flex}._0brRca_childMenuButton{width:16px;height:16px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:4px;justify-content:center;align-items:center;padding:0;display:inline-flex}._0brRca_childMenuButton:hover{color:var(--dsw-alias-label-primary)}._0brRca_deleteAction:not(:disabled){color:var(--dsw-alias-state-error-primary)}._0brRca_deleteStatus{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}._0brRca_deleteError{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}";
434
+ const tagId$1 = "@flowingspring/dsh-voco/VoiceSessionMarkers.module.css";
435
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
376
436
  const tag = document.createElement("style");
377
437
  tag.dataset.plugin = "@flowingspring/dsh-voco";
378
- tag.dataset.pluginCss = tagId;
379
- tag.textContent = css;
438
+ tag.dataset.pluginCss = tagId$1;
439
+ tag.textContent = css$1;
380
440
  document.head.appendChild(tag);
381
441
  }
382
442
  var VoiceSessionMarkers_module_css_default = {
383
- "childMenuButton": "_0brRca_childMenuButton",
384
- "deleteAction": "_0brRca_deleteAction",
385
- "child": "_0brRca_child",
386
- "childMain": "_0brRca_childMain",
443
+ "toggle": "_0brRca_toggle",
387
444
  "children": "_0brRca_children",
445
+ "marker": "_0brRca_marker",
446
+ "child": "_0brRca_child",
388
447
  "childLabel": "_0brRca_childLabel",
389
- "childTime": "_0brRca_childTime",
390
448
  "childActions": "_0brRca_childActions",
449
+ "childMenuButton": "_0brRca_childMenuButton",
391
450
  "deleteStatus": "_0brRca_deleteStatus",
451
+ "deleteAction": "_0brRca_deleteAction",
452
+ "childDot": "_0brRca_childDot",
453
+ "childTime": "_0brRca_childTime",
392
454
  "deleteError": "_0brRca_deleteError",
393
- "slot": "_0brRca_slot",
394
- "marker": "_0brRca_marker",
395
- "toggle": "_0brRca_toggle",
396
- "childDot": "_0brRca_childDot"
455
+ "childMain": "_0brRca_childMain",
456
+ "slot": "_0brRca_slot"
397
457
  };
398
458
  //#endregion
399
459
  //#region ../ui-voice/src/client/VoiceSessionMarkers.tsx
@@ -857,10 +917,162 @@ window.__ModuleLoader__.load({
857
917
  return marker;
858
918
  }
859
919
  //#endregion
920
+ //#region \0dsh-css:D:\QUAN\DevCode\Python\Project\dsh\dsh-voco\packages\ui-voice\src\client\VocoSettingsCard.module.css.mjs
921
+ const css = ".G5LlXq_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.G5LlXq_card:hover{border-color:var(--dsw-alias-label-dimmed)}.G5LlXq_cardOpen{border-color:var(--dsw-alias-label-dimmed);background:var(--dsw-alias-bg-layer-2)}.G5LlXq_header{appearance:none;width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.G5LlXq_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.G5LlXq_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.G5LlXq_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.G5LlXq_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.G5LlXq_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.G5LlXq_chevronOpen{transform:rotate(180deg)}.G5LlXq_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.G5LlXq_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.G5LlXq_pending,.G5LlXq_badge{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.G5LlXq_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.G5LlXq_fieldHead{align-items:center;gap:8px;display:flex}.G5LlXq_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.G5LlXq_badgeMuted{color:var(--dsw-alias-label-tertiary);white-space:nowrap;border-radius:999px;padding:1px 8px;font-size:11px;line-height:17px}.G5LlXq_input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.G5LlXq_input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.G5LlXq_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.G5LlXq_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}.G5LlXq_providerDetails{flex-direction:column;gap:4px;margin:6px 0 0;display:flex}.G5LlXq_providerDetail{grid-template-columns:88px minmax(0,1fr);gap:8px;font-size:12px;line-height:1.5;display:grid}.G5LlXq_providerDetail dt{color:var(--dsw-alias-label-tertiary)}.G5LlXq_providerDetail dd{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-secondary);margin:0}.G5LlXq_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.G5LlXq_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}.G5LlXq_discard,.G5LlXq_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.G5LlXq_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.G5LlXq_discard:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed);color:var(--dsw-alias-label-primary)}.G5LlXq_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.G5LlXq_discard:disabled,.G5LlXq_save:disabled{opacity:.4;cursor:default}.G5LlXq_discard:focus-visible,.G5LlXq_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}";
922
+ const tagId = "@flowingspring/dsh-voco/VocoSettingsCard.module.css";
923
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
924
+ const tag = document.createElement("style");
925
+ tag.dataset.plugin = "@flowingspring/dsh-voco";
926
+ tag.dataset.pluginCss = tagId;
927
+ tag.textContent = css;
928
+ document.head.appendChild(tag);
929
+ }
930
+ var VocoSettingsCard_module_css_default = {
931
+ "save": "G5LlXq_save",
932
+ "chevron": "G5LlXq_chevron",
933
+ "hint": "G5LlXq_hint",
934
+ "header": "G5LlXq_header",
935
+ "body": "G5LlXq_body",
936
+ "pending": "G5LlXq_pending",
937
+ "headText": "G5LlXq_headText",
938
+ "chevronOpen": "G5LlXq_chevronOpen",
939
+ "description": "G5LlXq_description",
940
+ "readOnly": "G5LlXq_readOnly",
941
+ "label": "G5LlXq_label",
942
+ "providerDetails": "G5LlXq_providerDetails",
943
+ "failed": "G5LlXq_failed",
944
+ "badgeMuted": "G5LlXq_badgeMuted",
945
+ "fieldHead": "G5LlXq_fieldHead",
946
+ "providerDetail": "G5LlXq_providerDetail",
947
+ "cardOpen": "G5LlXq_cardOpen",
948
+ "footer": "G5LlXq_footer",
949
+ "field": "G5LlXq_field",
950
+ "input": "G5LlXq_input",
951
+ "badge": "G5LlXq_badge",
952
+ "name": "G5LlXq_name",
953
+ "discard": "G5LlXq_discard",
954
+ "card": "G5LlXq_card"
955
+ };
956
+ //#endregion
957
+ //#region ../ui-voice/src/client/VocoSettingsCard.tsx
958
+ /** Official plugin-settings slot contribution for the Voco credential. */
959
+ function VocoSettingsCard({ useVocoSettings, editApiKey, save, discard, t }) {
960
+ const [open, setOpen] = (0, react.useState)(false);
961
+ const state = useVocoSettings((value) => value);
962
+ if (!state.available) return null;
963
+ const title = t("settings.title");
964
+ const disabled = !state.writable || !state.apiKeyWritable || state.saving;
965
+ const saveDisabled = !state.dirty || disabled;
966
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
967
+ className: `${VocoSettingsCard_module_css_default.card} ${open ? VocoSettingsCard_module_css_default.cardOpen : ""}`,
968
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
969
+ type: "button",
970
+ className: VocoSettingsCard_module_css_default.header,
971
+ "aria-expanded": open,
972
+ "aria-label": `${t(open ? "settings.collapse" : "settings.expand")}: ${title}`,
973
+ onClick: () => {
974
+ setOpen(!open);
975
+ },
976
+ children: [
977
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
978
+ className: VocoSettingsCard_module_css_default.headText,
979
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
980
+ className: VocoSettingsCard_module_css_default.name,
981
+ children: title
982
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
983
+ className: VocoSettingsCard_module_css_default.description,
984
+ children: t("settings.description")
985
+ })]
986
+ }),
987
+ state.dirty && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
988
+ className: VocoSettingsCard_module_css_default.pending,
989
+ children: t("settings.unsaved")
990
+ }),
991
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: `${VocoSettingsCard_module_css_default.chevron} ${open ? VocoSettingsCard_module_css_default.chevronOpen : ""}` })
992
+ ]
993
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
994
+ className: VocoSettingsCard_module_css_default.body,
995
+ children: [
996
+ !state.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
997
+ className: VocoSettingsCard_module_css_default.readOnly,
998
+ role: "status",
999
+ children: t("settings.readOnly")
1000
+ }),
1001
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1002
+ className: VocoSettingsCard_module_css_default.field,
1003
+ children: [
1004
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1005
+ className: VocoSettingsCard_module_css_default.fieldHead,
1006
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
1007
+ className: VocoSettingsCard_module_css_default.label,
1008
+ htmlFor: "voco-siliconflow-api-key",
1009
+ children: t("settings.apiKey")
1010
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1011
+ className: state.apiKeyConfigured ? VocoSettingsCard_module_css_default.badge : VocoSettingsCard_module_css_default.badgeMuted,
1012
+ children: t(state.apiKeyConfigured ? "settings.apiKeySet" : "settings.apiKeyUnset")
1013
+ })]
1014
+ }),
1015
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1016
+ id: "voco-siliconflow-api-key",
1017
+ className: VocoSettingsCard_module_css_default.input,
1018
+ type: "password",
1019
+ autoComplete: "off",
1020
+ value: state.apiKey,
1021
+ disabled,
1022
+ onChange: (event) => {
1023
+ editApiKey(event.target.value);
1024
+ }
1025
+ }),
1026
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1027
+ className: VocoSettingsCard_module_css_default.hint,
1028
+ children: t("settings.apiKeyHint")
1029
+ }),
1030
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
1031
+ className: VocoSettingsCard_module_css_default.providerDetails,
1032
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1033
+ className: VocoSettingsCard_module_css_default.providerDetail,
1034
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("settings.platform") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: t("settings.platformValue") })]
1035
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1036
+ className: VocoSettingsCard_module_css_default.providerDetail,
1037
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("settings.model") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: t("settings.modelValue") })]
1038
+ })]
1039
+ })
1040
+ ]
1041
+ }),
1042
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1043
+ className: VocoSettingsCard_module_css_default.footer,
1044
+ children: [
1045
+ state.failed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1046
+ className: VocoSettingsCard_module_css_default.failed,
1047
+ role: "status",
1048
+ children: t("settings.saveFailed")
1049
+ }),
1050
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1051
+ type: "button",
1052
+ className: VocoSettingsCard_module_css_default.discard,
1053
+ disabled: !state.dirty || state.saving,
1054
+ onClick: discard,
1055
+ children: t("settings.discard")
1056
+ }),
1057
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1058
+ type: "button",
1059
+ className: VocoSettingsCard_module_css_default.save,
1060
+ disabled: saveDisabled,
1061
+ onClick: save,
1062
+ children: t(state.saving ? "settings.saving" : "settings.save")
1063
+ })
1064
+ ]
1065
+ })
1066
+ ]
1067
+ })]
1068
+ });
1069
+ }
1070
+ //#endregion
860
1071
  //#region ../ui-voice/src/client/voice-controller.ts
861
1072
  const INITIAL_SNAPSHOT = {
862
1073
  state: "off",
863
1074
  inputMuted: false,
1075
+ pushToTalkActive: false,
864
1076
  textById: {}
865
1077
  };
866
1078
  /** Own the single browser microphone, provider WebSocket, playback queue, and live transcript feed. */
@@ -875,6 +1087,9 @@ window.__ModuleLoader__.load({
875
1087
  pendingText = [];
876
1088
  onConversationStarted;
877
1089
  conversationStarted = false;
1090
+ pushToTalkOrigin;
1091
+ pushToTalkPending = false;
1092
+ pushToTalkActive = false;
878
1093
  constructor(options = {}) {
879
1094
  this.onConversationStarted = options.onConversationStarted;
880
1095
  }
@@ -894,6 +1109,101 @@ window.__ModuleLoader__.load({
894
1109
  async start(sessionId) {
895
1110
  await this.open(sessionId, false);
896
1111
  }
1112
+ /** Begin a temporary manually committed utterance after a long press. */
1113
+ beginPushToTalk(sessionId) {
1114
+ if (this.pushToTalkPending || this.pushToTalkActive) return;
1115
+ const state = this.snapshot.state;
1116
+ if (state === "off") {
1117
+ this.pushToTalkOrigin = "off";
1118
+ this.pushToTalkPending = true;
1119
+ this.open(sessionId, true).catch(() => {});
1120
+ return;
1121
+ }
1122
+ if (state === "error") {
1123
+ const activeSessionId = this.snapshot.sessionId;
1124
+ if (activeSessionId === void 0) return;
1125
+ this.pushToTalkOrigin = this.snapshot.inputMuted ? "muted" : "hands-free";
1126
+ this.pushToTalkPending = true;
1127
+ this.open(activeSessionId, true).catch(() => {});
1128
+ return;
1129
+ }
1130
+ this.pushToTalkOrigin = this.snapshot.inputMuted ? "muted" : "hands-free";
1131
+ this.pushToTalkPending = true;
1132
+ if (state === "connecting") this.setInputMutedWithoutCommit(true);
1133
+ this.activatePushToTalk();
1134
+ }
1135
+ /** Finish or cancel the temporary manually committed utterance. */
1136
+ endPushToTalk() {
1137
+ if (!this.pushToTalkPending && !this.pushToTalkActive) return;
1138
+ if (!this.pushToTalkActive) {
1139
+ this.pushToTalkPending = false;
1140
+ this.restorePendingInputState();
1141
+ return;
1142
+ }
1143
+ const transport = this.transport;
1144
+ if (transport === void 0) {
1145
+ this.pushToTalkActive = false;
1146
+ this.pushToTalkPending = false;
1147
+ this.publish({
1148
+ ...this.snapshot,
1149
+ pushToTalkActive: false
1150
+ });
1151
+ return;
1152
+ }
1153
+ const origin = this.pushToTalkOrigin;
1154
+ const restoreMuted = origin === "off" || origin === "muted";
1155
+ try {
1156
+ transport.commitAudio();
1157
+ transport.setInputMuted(restoreMuted, false);
1158
+ } catch {
1159
+ this.handleUnexpectedClose();
1160
+ return;
1161
+ }
1162
+ this.pushToTalkActive = false;
1163
+ this.pushToTalkPending = false;
1164
+ this.pushToTalkOrigin = void 0;
1165
+ this.publish({
1166
+ ...this.snapshot,
1167
+ inputMuted: restoreMuted,
1168
+ pushToTalkActive: false
1169
+ });
1170
+ }
1171
+ activatePushToTalk() {
1172
+ if (!this.pushToTalkPending || this.pushToTalkActive || this.transport === void 0) return;
1173
+ try {
1174
+ this.transport.setInputMuted(false, false);
1175
+ this.transport.beginManualUtterance();
1176
+ } catch {
1177
+ this.handleUnexpectedClose();
1178
+ return;
1179
+ }
1180
+ this.pushToTalkActive = true;
1181
+ this.publish({
1182
+ ...this.snapshot,
1183
+ inputMuted: false,
1184
+ pushToTalkActive: true
1185
+ });
1186
+ }
1187
+ restorePendingInputState() {
1188
+ const origin = this.pushToTalkOrigin;
1189
+ if (origin === void 0) return;
1190
+ const restoreMuted = origin === "off" || origin === "muted";
1191
+ (this.transport ?? this.openingTransport)?.setInputMuted(restoreMuted, false);
1192
+ this.pushToTalkOrigin = void 0;
1193
+ this.publish({
1194
+ ...this.snapshot,
1195
+ inputMuted: restoreMuted,
1196
+ pushToTalkActive: false
1197
+ });
1198
+ }
1199
+ setInputMutedWithoutCommit(muted) {
1200
+ this.openingTransport?.setInputMuted(muted, false);
1201
+ this.transport?.setInputMuted(muted, false);
1202
+ if (this.snapshot.inputMuted !== muted) this.publish({
1203
+ ...this.snapshot,
1204
+ inputMuted: muted
1205
+ });
1206
+ }
897
1207
  async open(sessionId, inputMuted) {
898
1208
  const generation = ++this.generation;
899
1209
  this.pendingText.splice(0);
@@ -909,6 +1219,7 @@ window.__ModuleLoader__.load({
909
1219
  this.publish({
910
1220
  state: "connecting",
911
1221
  inputMuted,
1222
+ pushToTalkActive: false,
912
1223
  sessionId,
913
1224
  textById: {}
914
1225
  });
@@ -939,7 +1250,8 @@ window.__ModuleLoader__.load({
939
1250
  this.openingTransport = void 0;
940
1251
  this.transport = transport;
941
1252
  for (const text of this.pendingText.splice(0)) transport.submitText(text);
942
- this.setState("listening");
1253
+ if (this.pushToTalkPending) this.activatePushToTalk();
1254
+ if (this.snapshot.state !== "error") this.setState("listening");
943
1255
  } catch (cause) {
944
1256
  if (generation !== this.generation) return;
945
1257
  this.setState("error");
@@ -995,6 +1307,9 @@ window.__ModuleLoader__.load({
995
1307
  this.openingAbort = void 0;
996
1308
  this.publish(INITIAL_SNAPSHOT);
997
1309
  this.pendingText.splice(0);
1310
+ this.pushToTalkPending = false;
1311
+ this.pushToTalkActive = false;
1312
+ this.pushToTalkOrigin = void 0;
998
1313
  await this.closeTransport();
999
1314
  }
1000
1315
  async closeTransport() {
@@ -1014,6 +1329,13 @@ window.__ModuleLoader__.load({
1014
1329
  }
1015
1330
  handleUnexpectedClose() {
1016
1331
  this.openingAbort?.abort();
1332
+ this.pushToTalkPending = false;
1333
+ this.pushToTalkActive = false;
1334
+ this.pushToTalkOrigin = void 0;
1335
+ if (this.snapshot.pushToTalkActive) this.publish({
1336
+ ...this.snapshot,
1337
+ pushToTalkActive: false
1338
+ });
1017
1339
  this.setState("error");
1018
1340
  this.closeTransport().catch((cause) => {
1019
1341
  console.error("voice transport cleanup failed:", cause);
@@ -1144,6 +1466,14 @@ window.__ModuleLoader__.load({
1144
1466
  const chunks = [];
1145
1467
  const playing = /* @__PURE__ */ new Set();
1146
1468
  const transport = {
1469
+ beginManualUtterance: () => {
1470
+ if (socket.readyState !== WebSocket.OPEN) throw new Error("voice transport is not open");
1471
+ socket.send(JSON.stringify({ type: "audio.push-to-talk.start" }));
1472
+ },
1473
+ commitAudio: () => {
1474
+ if (socket.readyState !== WebSocket.OPEN) throw new Error("voice transport is not open");
1475
+ socket.send(JSON.stringify({ type: "audio.commit" }));
1476
+ },
1147
1477
  cancelTask: (taskId) => {
1148
1478
  if (socket.readyState !== WebSocket.OPEN) throw new Error("voice transport is not open");
1149
1479
  socket.send(JSON.stringify({
@@ -1156,12 +1486,12 @@ window.__ModuleLoader__.load({
1156
1486
  socket.send(JSON.stringify({ type: "response.interrupt" }));
1157
1487
  stopPlayback();
1158
1488
  },
1159
- setInputMuted: (muted) => {
1489
+ setInputMuted: (muted, commit = true) => {
1160
1490
  if (inputMuted === muted) return;
1161
1491
  inputMuted = muted;
1162
1492
  chunks.splice(0);
1163
1493
  for (const track of stream?.getTracks() ?? []) track.enabled = !muted;
1164
- if (muted && !stopped && socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ type: "audio.commit" }));
1494
+ if (muted && commit && !stopped && socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ type: "audio.commit" }));
1165
1495
  },
1166
1496
  submitText: (text) => {
1167
1497
  if (socket.readyState !== WebSocket.OPEN) throw new Error("voice transport is not open");
@@ -1841,6 +2171,7 @@ registerProcessor('dsh-pcm-capture', DshPcmCaptureProcessor)
1841
2171
  "control.interrupt": "打断播放",
1842
2172
  "control.mute": "静音麦克风",
1843
2173
  "control.unmute": "解除静音",
2174
+ "control.pushToTalk": "按住说话中,松开发送",
1844
2175
  "overlay.connecting": "正在连接语音",
1845
2176
  "overlay.listening": "正在聆听",
1846
2177
  "overlay.speaking": "正在回复",
@@ -1862,7 +2193,25 @@ registerProcessor('dsh-pcm-capture', DshPcmCaptureProcessor)
1862
2193
  "task.expand": "展开后台处理详情",
1863
2194
  "task.collapse": "收起后台处理详情",
1864
2195
  "task.cancel": "取消任务",
1865
- "task.open": "查看后台详情"
2196
+ "task.open": "查看后台详情",
2197
+ "settings.title": "语音助手(Voco)",
2198
+ "settings.description": "语音识别供应商配置。",
2199
+ "settings.expand": "展开设置",
2200
+ "settings.collapse": "收起设置",
2201
+ "settings.unsaved": "未保存",
2202
+ "settings.readOnly": "本部署的设置为只读。",
2203
+ "settings.apiKey": "API Key",
2204
+ "settings.apiKeyHint": "安全保存到 DSH 凭据库。留空表示保持当前密钥。",
2205
+ "settings.platform": "API 平台",
2206
+ "settings.platformValue": "硅基流动",
2207
+ "settings.model": "语音识别模型",
2208
+ "settings.modelValue": "XingChenAGI/XingChenASR-V3.2-Ultra(免费)",
2209
+ "settings.apiKeySet": "已配置密钥。",
2210
+ "settings.apiKeyUnset": "未配置密钥。",
2211
+ "settings.save": "保存",
2212
+ "settings.saving": "保存中…",
2213
+ "settings.discard": "放弃修改",
2214
+ "settings.saveFailed": "密钥保存失败,已保留供你重试。"
1866
2215
  };
1867
2216
  /** English dictionary, checked complete against the zh key set. */
1868
2217
  const en = {
@@ -1872,6 +2221,7 @@ registerProcessor('dsh-pcm-capture', DshPcmCaptureProcessor)
1872
2221
  "control.interrupt": "Stop playback",
1873
2222
  "control.mute": "Mute microphone",
1874
2223
  "control.unmute": "Unmute microphone",
2224
+ "control.pushToTalk": "Push to talk, release to send",
1875
2225
  "overlay.connecting": "Connecting voice",
1876
2226
  "overlay.listening": "Listening",
1877
2227
  "overlay.speaking": "Playing response",
@@ -1893,7 +2243,127 @@ registerProcessor('dsh-pcm-capture', DshPcmCaptureProcessor)
1893
2243
  "task.expand": "Show background task details",
1894
2244
  "task.collapse": "Hide background task details",
1895
2245
  "task.cancel": "Cancel task",
1896
- "task.open": "View background details"
2246
+ "task.open": "View background details",
2247
+ "settings.title": "Voice Assistant (Voco)",
2248
+ "settings.description": "Speech recognition provider configuration.",
2249
+ "settings.expand": "Show settings",
2250
+ "settings.collapse": "Hide settings",
2251
+ "settings.unsaved": "Unsaved",
2252
+ "settings.readOnly": "This deployment stores settings read-only.",
2253
+ "settings.apiKey": "API key",
2254
+ "settings.apiKeyHint": "Stored securely in DSH credentials. Leave blank to keep the current key.",
2255
+ "settings.platform": "API platform",
2256
+ "settings.platformValue": "SiliconFlow",
2257
+ "settings.model": "Speech model",
2258
+ "settings.modelValue": "XingChenAGI/XingChenASR-V3.2-Ultra (free)",
2259
+ "settings.apiKeySet": "A key is configured.",
2260
+ "settings.apiKeyUnset": "No key is configured.",
2261
+ "settings.save": "Save",
2262
+ "settings.saving": "Saving…",
2263
+ "settings.discard": "Discard",
2264
+ "settings.saveFailed": "The key could not be saved and was kept for you to retry."
2265
+ };
2266
+ //#endregion
2267
+ //#region ../ui-voice/src/client/voco-settings-controller.ts
2268
+ const VOCO_SETTINGS_NAMESPACE = "voice-local";
2269
+ const SILICONFLOW_API_KEY_REF = "SILICONFLOW_API_KEY";
2270
+ /** Bridges the voice-local namespace and the write-only DSH credentials API. */
2271
+ var VocoSettingsController = class {
2272
+ scope;
2273
+ api;
2274
+ store;
2275
+ draft = "";
2276
+ saving = false;
2277
+ failed = false;
2278
+ credential = {
2279
+ configured: false,
2280
+ writable: true
2281
+ };
2282
+ constructor(scope, api) {
2283
+ this.scope = scope;
2284
+ this.api = api;
2285
+ this.store = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)(this.projection());
2286
+ scope.subscribe(() => {
2287
+ this.publish();
2288
+ });
2289
+ this.readCredential();
2290
+ }
2291
+ refreshCredential(ref) {
2292
+ if (ref === "SILICONFLOW_API_KEY") this.readCredential();
2293
+ }
2294
+ inject() {
2295
+ return {
2296
+ hooks: { vocoSettings: this.store },
2297
+ editApiKey: (value) => {
2298
+ this.draft = value;
2299
+ this.failed = false;
2300
+ this.publish();
2301
+ },
2302
+ save: () => {
2303
+ this.save();
2304
+ },
2305
+ discard: () => {
2306
+ this.draft = "";
2307
+ this.failed = false;
2308
+ this.publish();
2309
+ }
2310
+ };
2311
+ }
2312
+ projection() {
2313
+ const snapshot = this.scope.getSnapshot();
2314
+ return {
2315
+ available: snapshot.status === "ready",
2316
+ writable: snapshot.writable,
2317
+ dirty: this.draft.trim() !== "",
2318
+ saving: this.saving,
2319
+ failed: this.failed,
2320
+ apiKey: this.draft,
2321
+ apiKeyConfigured: this.credential.configured,
2322
+ apiKeyWritable: this.credential.writable
2323
+ };
2324
+ }
2325
+ publish() {
2326
+ this.store.set(this.projection());
2327
+ }
2328
+ async readCredential() {
2329
+ let response;
2330
+ try {
2331
+ response = await this.api.credentials.describe({ refs: [SILICONFLOW_API_KEY_REF] });
2332
+ } catch {
2333
+ return;
2334
+ }
2335
+ if (!response.result.ok) return;
2336
+ const view = response.result.value.credentials[SILICONFLOW_API_KEY_REF];
2337
+ this.credential = {
2338
+ configured: view?.configured ?? false,
2339
+ writable: view?.writable ?? true
2340
+ };
2341
+ this.publish();
2342
+ }
2343
+ async save() {
2344
+ const value = this.draft.trim();
2345
+ if (value === "" || this.saving || !this.scope.getSnapshot().writable || !this.credential.writable) return;
2346
+ this.saving = true;
2347
+ this.failed = false;
2348
+ this.publish();
2349
+ try {
2350
+ if (!(await this.api.credentials.set({
2351
+ ref: "SILICONFLOW_API_KEY",
2352
+ value
2353
+ })).result.ok) {
2354
+ this.failed = true;
2355
+ return;
2356
+ }
2357
+ await this.readCredential();
2358
+ if (this.credential.configured) this.draft = "";
2359
+ else this.failed = true;
2360
+ } catch {
2361
+ this.failed = true;
2362
+ } finally {
2363
+ this.saving = false;
2364
+ this.publish();
2365
+ }
2366
+ }
1897
2367
  };
1898
2368
  //#endregion
1899
2369
  //#region ../ui-voice/src/client/index.ts
@@ -1904,7 +2374,9 @@ registerProcessor('dsh-pcm-capture', DshPcmCaptureProcessor)
1904
2374
  "sessions",
1905
2375
  "workspaces",
1906
2376
  "locale",
1907
- "remote"
2377
+ "remote",
2378
+ "connection",
2379
+ "settingsScope"
1908
2380
  ];
1909
2381
  /**
1910
2382
  * The Harness model-selection UI intentionally hides addressed children. Voice
@@ -1949,6 +2421,7 @@ registerProcessor('dsh-pcm-capture', DshPcmCaptureProcessor)
1949
2421
  }
1950
2422
  /** Mount root transport controls and durable Voice conversation renderers. @param ctx - browser context. */
1951
2423
  function apply(ctx) {
2424
+ const vocoSettings = new VocoSettingsController(ctx.settingsScope.bind({ namespace: VOCO_SETTINGS_NAMESPACE }), ctx.get("connection").api);
1952
2425
  const controller = new VoiceController({ onConversationStarted: () => {
1953
2426
  refreshSessions(ctx);
1954
2427
  } });
@@ -1973,6 +2446,12 @@ registerProcessor('dsh-pcm-capture', DshPcmCaptureProcessor)
1973
2446
  const sessionId = controller.getSnapshot().sessionId;
1974
2447
  if (sessionId !== void 0) history.record(sessionId);
1975
2448
  },
2449
+ beginPushToTalk: (sessionId) => {
2450
+ controller.beginPushToTalk(sessionId);
2451
+ },
2452
+ endPushToTalk: () => {
2453
+ controller.endPushToTalk();
2454
+ },
1976
2455
  interruptResponse: () => {
1977
2456
  controller.interruptResponse();
1978
2457
  },
@@ -2053,6 +2532,9 @@ registerProcessor('dsh-pcm-capture', DshPcmCaptureProcessor)
2053
2532
  zh,
2054
2533
  en
2055
2534
  }), "ui-voice: dictionaries");
2535
+ ctx.effect(() => ctx.remote.$on("credentials/reference-updated", (ref) => {
2536
+ vocoSettings.refreshCredential(ref);
2537
+ }), "ui-voice: credential invalidations");
2056
2538
  ctx.effect(() => async () => {
2057
2539
  await controller.stop();
2058
2540
  }, "ui-voice: transport teardown");
@@ -2098,6 +2580,12 @@ registerProcessor('dsh-pcm-capture', DshPcmCaptureProcessor)
2098
2580
  locale: NS,
2099
2581
  inject: delegationInjected
2100
2582
  }, VoiceDelegationView));
2583
+ ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
2584
+ name: "settings.plugin.item",
2585
+ key: VOCO_SETTINGS_NAMESPACE,
2586
+ locale: NS,
2587
+ inject: () => vocoSettings.inject()
2588
+ }, VocoSettingsCard));
2101
2589
  }
2102
2590
  /** Archive-manager archives one requested ID; Voice treats that state as
2103
2591
  * inherited by every descendant without mutating the third-party archive set. */