@deepseek-ai/dsh-experimental-client-ui-voice-input 0.1.7-alpha.1 → 0.1.7-rc.1

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/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/experimental/client-ui-voice-input/README.md
5
- README.md: 4d859f4d9837c249c564abfbba0682f1493502d9
6
- README.zh.md: 84a02e4f620feb35706de5b3bc10ec291f32c423
5
+ README.md: 764962ff496c9fdd91073c2aa391ebceeec23608
6
+ README.zh.md: 105cb641684eb2920c04294e74f1b5389f564c0c
package/README.md CHANGED
@@ -27,6 +27,8 @@ This optional browser plugin adds an outline microphone icon between the model s
27
27
 
28
28
  Enable the [voice input bundle](../voice-input-bundle/README.md) from Plugins. After cache inspection, missing models trigger a setup prompt with Go to setup and Later actions. Go to setup opens bundle details; Download and prepare starts installation there. Intact cached models need no prompt or repeated installation. Before preparation, local providers can show their estimated disk, memory and setup-time requirements. The preparation summary starts collapsed; expand it to inspect completed, current and pending steps. Once ready, click the microphone, allow access and click Stop to transcribe. Cancel or Escape discards capture. Recognition feedback stays inside the toolbar. Provider and language preferences are saved from bundle details through the Settings service. Browser microphone access requires HTTPS or loopback and operating-system permission.
29
29
 
30
+ Before downloading or retrying, **Model download source** offers Automatic and the origins advertised by the Host. SenseVoice normally offers Hugging Face and HF-Mirror; a fixed private deployment exposes only its configured origin. Manual selection uses only that source, while Automatic preserves the provider’s comparison and fallback policy. The choice is retained for retries in the current card, without changing deployment configuration or recognition preferences. Source controls are unavailable during preparation and hidden when resources are ready.
31
+
30
32
  -----
31
33
 
32
34
  <a id="understand-the-implementation"></a>
package/README.zh.md CHANGED
@@ -27,6 +27,8 @@ kind: "package-reference"
27
27
 
28
28
  在插件管理页启用[语音输入 Bundle](../voice-input-bundle/README.zh.md)。缓存检查后,缺少模型时弹出安装引导,可选择“前往安装”或“稍后”。“前往安装”打开 Bundle 详情,再点击“下载并准备”才开始安装。缓存完整时不弹引导,也无需重复安装。准备前,本地 Provider 可展示磁盘、内存与安装时间的估算。准备摘要默认折叠;展开后可查看已完成、当前和未开始的步骤。就绪后点击麦克风,允许访问,再点击停止以转写。取消或 Escape 丢弃录音。识别反馈留在工具栏内部。Provider 和语言偏好在 Bundle 详情中通过 Settings 服务保存。浏览器麦克风访问需要 HTTPS 或环回地址,以及操作系统权限。
29
29
 
30
+ 下载或重试前,**模型下载源**提供自动选择及 Host 公布的源。SenseVoice 默认提供 Hugging Face 和 HF-Mirror;固定私有部署仅显示配置的源。手动选择只使用指定源,自动选择沿用提供方的比较与回退策略。当前卡片中的重试会保留选择,不修改部署配置或识别偏好。准备期间不能改源,资源就绪后隐藏源选择器。
31
+
30
32
  -----
31
33
 
32
34
  <a id="understand-the-implementation"></a>
package/lib/client.js CHANGED
@@ -614,6 +614,7 @@ window.__ModuleLoader__.load({
614
614
  };
615
615
  const integer = /^-?\d+$/;
616
616
  const number$1 = /^-?\d+(?:\.\d+)?$/;
617
+ const _undefined$2 = /^undefined$/i;
617
618
  const lowercase = /^[^A-Z]*$/;
618
619
  const uppercase = /^[^a-z]*$/;
619
620
  //#endregion
@@ -1408,6 +1409,22 @@ window.__ModuleLoader__.load({
1408
1409
  $ZodCheckNumberFormat.init(inst, def);
1409
1410
  $ZodNumber.init(inst, def);
1410
1411
  });
1412
+ const $ZodUndefined = /*@__PURE__*/ $constructor("$ZodUndefined", (inst, def) => {
1413
+ $ZodType.init(inst, def);
1414
+ inst._zod.pattern = _undefined$2;
1415
+ inst._zod.values = new Set([void 0]);
1416
+ inst._zod.parse = (payload, _ctx) => {
1417
+ const input = payload.value;
1418
+ if (typeof input === "undefined") return payload;
1419
+ payload.issues.push({
1420
+ expected: "undefined",
1421
+ code: "invalid_type",
1422
+ input,
1423
+ inst
1424
+ });
1425
+ return payload;
1426
+ };
1427
+ });
1411
1428
  const $ZodUnknown = /*@__PURE__*/ $constructor("$ZodUnknown", (inst, def) => {
1412
1429
  $ZodType.init(inst, def);
1413
1430
  inst._zod.parse = (payload) => payload;
@@ -2472,6 +2489,13 @@ window.__ModuleLoader__.load({
2472
2489
  });
2473
2490
  }
2474
2491
  // @__NO_SIDE_EFFECTS__
2492
+ function _undefined$1(Class, params) {
2493
+ return new Class({
2494
+ type: "undefined",
2495
+ ...normalizeParams(params)
2496
+ });
2497
+ }
2498
+ // @__NO_SIDE_EFFECTS__
2475
2499
  function _unknown(Class) {
2476
2500
  return new Class({ type: "unknown" });
2477
2501
  }
@@ -3024,6 +3048,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
3024
3048
  else if (typeof maximum === "number") json.maximum = maximum;
3025
3049
  if (typeof multipleOf === "number") json.multipleOf = multipleOf;
3026
3050
  };
3051
+ const undefinedProcessor = (_schema, ctx, _json, _params) => {
3052
+ if (ctx.unrepresentable === "throw") throw new Error("Undefined cannot be represented in JSON Schema");
3053
+ };
3027
3054
  const voidProcessor = (_schema, ctx, _json, _params) => {
3028
3055
  if (ctx.unrepresentable === "throw") throw new Error("Void cannot be represented in JSON Schema");
3029
3056
  };
@@ -3676,6 +3703,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
3676
3703
  function int(params) {
3677
3704
  return /* @__PURE__ */ _int(ZodNumberFormat, params);
3678
3705
  }
3706
+ const ZodUndefined = /*@__PURE__*/ $constructor("ZodUndefined", (inst, def) => {
3707
+ $ZodUndefined.init(inst, def);
3708
+ ZodType.init(inst, def);
3709
+ inst._zod.processJSONSchema = (ctx, json, params) => undefinedProcessor(inst, ctx, json, params);
3710
+ });
3711
+ function _undefined(params) {
3712
+ return /* @__PURE__ */ _undefined$1(ZodUndefined, params);
3713
+ }
3679
3714
  const ZodUnknown = /*@__PURE__*/ $constructor("ZodUnknown", (inst, def) => {
3680
3715
  $ZodUnknown.init(inst, def);
3681
3716
  ZodType.init(inst, def);
@@ -4204,7 +4239,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4204
4239
  "expectedMemoryBytes": number().readonly(),
4205
4240
  "minimumMinutes": number().readonly(),
4206
4241
  "maximumMinutes": number().readonly()
4207
- }).readonly().optional()
4242
+ }).readonly().optional(),
4243
+ "downloadSources": array(string()).readonly().optional()
4208
4244
  })).readonly(),
4209
4245
  "selection": object({
4210
4246
  "providerId": intersection(string(), unknown()).readonly(),
@@ -4375,7 +4411,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4375
4411
  "expectedMemoryBytes": number().readonly(),
4376
4412
  "minimumMinutes": number().readonly(),
4377
4413
  "maximumMinutes": number().readonly()
4378
- }).readonly().optional()
4414
+ }).readonly().optional(),
4415
+ "downloadSources": array(string()).readonly().optional()
4379
4416
  })).readonly(),
4380
4417
  "selection": object({
4381
4418
  "providerId": intersection(string(), unknown()).readonly(),
@@ -4384,6 +4421,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4384
4421
  });
4385
4422
  let _deepseek_ai_dsh_experimental_api_speech_to_text_speech_prepare_parameter_0$schema$value;
4386
4423
  const _deepseek_ai_dsh_experimental_api_speech_to_text_speech_prepare_parameter_0$schema = () => _deepseek_ai_dsh_experimental_api_speech_to_text_speech_prepare_parameter_0$schema$value ??= intersection(string(), unknown());
4424
+ let _deepseek_ai_dsh_experimental_api_speech_to_text_speech_prepare_parameter_1$schema$value;
4425
+ const _deepseek_ai_dsh_experimental_api_speech_to_text_speech_prepare_parameter_1$schema = () => _deepseek_ai_dsh_experimental_api_speech_to_text_speech_prepare_parameter_1$schema$value ??= union([_undefined(), object({ "downloadSource": string().readonly().optional() })]);
4387
4426
  let _deepseek_ai_dsh_experimental_api_speech_to_text_speech_prepare_result$schema$value;
4388
4427
  const _deepseek_ai_dsh_experimental_api_speech_to_text_speech_prepare_result$schema = () => _deepseek_ai_dsh_experimental_api_speech_to_text_speech_prepare_result$schema$value ??= _void();
4389
4428
  let _deepseek_ai_dsh_experimental_api_speech_to_text_speech_transcribe_parameter_0$schema$value;
@@ -4424,7 +4463,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4424
4463
  },
4425
4464
  sourceLocation: {
4426
4465
  "file": "packages/experimental/api-speech-to-text/src/index.ts",
4427
- "line": 79,
4466
+ "line": 80,
4428
4467
  "column": 3
4429
4468
  }
4430
4469
  },
@@ -4508,6 +4547,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4508
4547
  typeSymbol: "@deepseek-ai/dsh-experimental-speech-to-text/types#SpeechProviderId",
4509
4548
  create: _deepseek_ai_dsh_experimental_api_speech_to_text_speech_prepare_parameter_0$schema
4510
4549
  }
4550
+ }, {
4551
+ name: "options",
4552
+ wire: "options",
4553
+ source: "json",
4554
+ acceptsUndefined: true,
4555
+ codec: {
4556
+ mode: "strict",
4557
+ typeSymbol: "@deepseek-ai/dsh-experimental-speech-to-text/types#SpeechPreparationOptions",
4558
+ create: _deepseek_ai_dsh_experimental_api_speech_to_text_speech_prepare_parameter_1$schema
4559
+ }
4511
4560
  }],
4512
4561
  result: {
4513
4562
  mode: "strict",
@@ -4516,7 +4565,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4516
4565
  },
4517
4566
  sourceLocation: {
4518
4567
  "file": "packages/experimental/api-speech-to-text/src/index.ts",
4519
- "line": 71,
4568
+ "line": 72,
4520
4569
  "column": 3
4521
4570
  }
4522
4571
  },
@@ -4544,7 +4593,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4544
4593
  },
4545
4594
  sourceLocation: {
4546
4595
  "file": "packages/experimental/api-speech-to-text/src/index.ts",
4547
- "line": 88,
4596
+ "line": 89,
4548
4597
  "column": 9
4549
4598
  }
4550
4599
  }
@@ -4753,7 +4802,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4753
4802
  };
4754
4803
  //#endregion
4755
4804
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/experimental/client-ui-voice-input/src/client/VoiceInput.module.css.mjs
4756
- const css = ".ddpbLW_trigger{flex:none;width:28px;padding:0}.ddpbLW_triggerAnchor{flex:none;display:inline-flex}.ddpbLW_captureRow{align-items:center;gap:12px;width:100%;min-width:0;min-height:34px;display:flex}.ddpbLW_roundButton{corner-shape:round;background:var(--dsw-specific-selector);border-radius:50%;flex:none;width:32px;height:32px;padding:0}.ddpbLW_roundButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.ddpbLW_waveform{width:0;min-width:24px;height:24px;color:var(--dsw-alias-label-secondary);flex:1;display:block}.ddpbLW_activityMessage{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:1;align-items:center;gap:8px;font-size:12px;display:flex;overflow:hidden}.ddpbLW_inlineAction{flex:none}.ddpbLW_preparation{border:.5px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;gap:10px;margin:12px 0;padding:14px;font-size:13px;display:flex}.ddpbLW_preparation p{margin:0}.ddpbLW_preparationActions{gap:8px;display:flex}.ddpbLW_preparationActions:empty{display:none}.ddpbLW_preparationError{color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;font-size:12px}.ddpbLW_downloadFailure{overflow-wrap:anywhere;gap:6px;display:grid}.ddpbLW_summaryRow{flex-wrap:wrap;gap:4px;height:auto;min-height:28px}.ddpbLW_summaryTitle{flex:1;min-width:0}.ddpbLW_metric{color:var(--dsw-alias-label-tertiary);font-size:12px}.ddpbLW_progress{width:100%;height:6px;accent-color:var(--dsw-alias-state-business-primary)}.ddpbLW_steps{flex-direction:column;gap:14px;margin:0;padding:16px 0 2px;list-style:none;display:flex}.ddpbLW_steps li{align-items:flex-start;gap:10px;line-height:18px;display:flex}.ddpbLW_steps li>:first-child{margin-top:1px}.ddpbLW_steps li[data-step-state=pending]{color:var(--dsw-alias-label-tertiary)}.ddpbLW_stepBody{flex:1;min-width:0}.ddpbLW_stepProgress{flex-direction:column;gap:6px;margin-top:6px;display:flex}.ddpbLW_stepProgress small{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;font-size:11px}.ddpbLW_preferences{gap:12px;margin:18px 0;font-size:13px;display:grid}.ddpbLW_preferences label{grid-template-columns:100px minmax(120px,280px);align-items:center;gap:12px;display:grid}.ddpbLW_preferences select{border:.5px solid var(--dsw-alias-border-l1);color:inherit;font:inherit;background:0 0;border-radius:6px;padding:6px 10px}.ddpbLW_preferences p{margin:0}.ddpbLW_settingsCard{border:.5px solid var(--dsw-alias-border-l1);border-radius:10px;padding:16px;list-style:none}.ddpbLW_settingsCard>p{color:var(--dsw-alias-label-secondary);margin:8px 0 0;font-size:13px}.ddpbLW_setupEstimate{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:8px;gap:10px;padding:12px;line-height:1.6;display:grid}.ddpbLW_setupEstimate dl{gap:6px;margin:0;display:grid}.ddpbLW_setupEstimate dl>div{grid-template-columns:72px minmax(0,1fr);gap:10px;display:grid}.ddpbLW_setupEstimate dt{color:var(--dsw-alias-label-tertiary)}.ddpbLW_setupEstimate dd{margin:0}.ddpbLW_setupEstimate small{color:var(--dsw-alias-label-tertiary);font-size:12px}";
4805
+ const css = ".ddpbLW_trigger{flex:none;width:28px;padding:0}.ddpbLW_triggerAnchor{flex:none;display:inline-flex}.ddpbLW_captureRow{align-items:center;gap:12px;width:100%;min-width:0;min-height:34px;display:flex}.ddpbLW_roundButton{corner-shape:round;background:var(--dsw-specific-selector);border-radius:50%;flex:none;width:32px;height:32px;padding:0}.ddpbLW_roundButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.ddpbLW_waveform{width:0;min-width:24px;height:24px;color:var(--dsw-alias-label-secondary);flex:1;display:block}.ddpbLW_activityMessage{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:1;align-items:center;gap:8px;font-size:12px;display:flex;overflow:hidden}.ddpbLW_inlineAction{flex:none}.ddpbLW_preparation{border:.5px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;gap:10px;margin:12px 0;padding:14px;font-size:13px;display:flex}.ddpbLW_preparation p{margin:0}.ddpbLW_preparationActions{gap:8px;display:flex}.ddpbLW_preparationActions:empty{display:none}.ddpbLW_preparationError{color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;font-size:12px}.ddpbLW_downloadFailure{overflow-wrap:anywhere;gap:6px;display:grid}.ddpbLW_summaryRow{flex-wrap:wrap;gap:4px;height:auto;min-height:28px}.ddpbLW_summaryTitle{flex:1;min-width:0}.ddpbLW_metric{color:var(--dsw-alias-label-tertiary);font-size:12px}.ddpbLW_progress{width:100%;height:6px;accent-color:var(--dsw-alias-state-business-primary)}.ddpbLW_steps{flex-direction:column;gap:14px;margin:0;padding:16px 0 2px;list-style:none;display:flex}.ddpbLW_steps li{align-items:flex-start;gap:10px;line-height:18px;display:flex}.ddpbLW_steps li>:first-child{margin-top:1px}.ddpbLW_steps li[data-step-state=pending]{color:var(--dsw-alias-label-tertiary)}.ddpbLW_stepBody{flex:1;min-width:0}.ddpbLW_stepProgress{flex-direction:column;gap:6px;margin-top:6px;display:flex}.ddpbLW_stepProgress small{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;font-size:11px}.ddpbLW_preferences{gap:12px;margin:18px 0;font-size:13px;display:grid}.ddpbLW_preferences label,.ddpbLW_sourceChoice label{grid-template-columns:100px minmax(120px,280px);align-items:center;gap:12px;display:grid}.ddpbLW_preferences select,.ddpbLW_sourceChoice select{border:.5px solid var(--dsw-alias-border-l1);color:inherit;font:inherit;background:0 0;border-radius:6px;padding:6px 10px}.ddpbLW_sourceChoice{gap:6px;display:grid}.ddpbLW_preferences p{margin:0}.ddpbLW_settingsCard{border:.5px solid var(--dsw-alias-border-l1);border-radius:10px;padding:16px;list-style:none}.ddpbLW_settingsCard>p{color:var(--dsw-alias-label-secondary);margin:8px 0 0;font-size:13px}.ddpbLW_setupEstimate{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:8px;gap:10px;padding:12px;line-height:1.6;display:grid}.ddpbLW_setupEstimate dl{gap:6px;margin:0;display:grid}.ddpbLW_setupEstimate dl>div{grid-template-columns:72px minmax(0,1fr);gap:10px;display:grid}.ddpbLW_setupEstimate dt{color:var(--dsw-alias-label-tertiary)}.ddpbLW_setupEstimate dd{margin:0}.ddpbLW_setupEstimate small{color:var(--dsw-alias-label-tertiary);font-size:12px}";
4757
4806
  const tagId = "@deepseek-ai/dsh-experimental-client-ui-voice-input/VoiceInput.module.css";
4758
4807
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
4759
4808
  const tag = document.createElement("style");
@@ -4776,6 +4825,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4776
4825
  "roundButton": "ddpbLW_roundButton",
4777
4826
  "settingsCard": "ddpbLW_settingsCard",
4778
4827
  "setupEstimate": "ddpbLW_setupEstimate",
4828
+ "sourceChoice": "ddpbLW_sourceChoice",
4779
4829
  "stepBody": "ddpbLW_stepBody",
4780
4830
  "stepProgress": "ddpbLW_stepProgress",
4781
4831
  "steps": "ddpbLW_steps",
@@ -5122,6 +5172,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5122
5172
  "downloadAdvice.storage": "请检查运行 DSH 的机器的剩余磁盘空间,以及模型目录的写入权限后重试。",
5123
5173
  "downloadAdvice.unknown": "请检查运行 DSH 的机器的网络、磁盘空间和模型目录权限后重试。",
5124
5174
  downloadSource: "下载来源:{source}",
5175
+ sourceChoice: "模型下载源",
5176
+ sourceAuto: "自动选择(推荐)",
5177
+ sourceHuggingFace: "Hugging Face",
5178
+ sourceMirror: "HF-Mirror(国内镜像)",
5179
+ sourceAutoHelp: "优先使用响应较快的源,下载失败时自动尝试其他源。",
5180
+ sourceManualHelp: "仅从所选源下载;已下载并通过校验的文件会复用。",
5125
5181
  downloadCode: "错误码:{code}",
5126
5182
  "reconnecting": "正在连接语音服务…",
5127
5183
  "preparation.unprepared": "首次使用需要下载本地识别模型。",
@@ -5153,7 +5209,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5153
5209
  requesting: "请允许使用麦克风…",
5154
5210
  recording: "正在录音…",
5155
5211
  interrupted: "录音中断,请重试。",
5156
- local: "音频在运行 DSH 的机器上识别。首次准备默认从 Hugging Face 下载模型,该机器需要能访问 Hugging Face 及其模型下载服务。如需代理,请在该机器上配置。",
5212
+ local: "音频在运行 DSH 的机器上识别。需要下载模型时,请确保该机器能访问所选下载源及其文件服务。如需代理,请在该机器上配置。",
5157
5213
  cloud: "音频将发送至所选云端服务。",
5158
5214
  empty: "未识别到语音",
5159
5215
  cancelled: "已取消语音输入。",
@@ -5217,6 +5273,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5217
5273
  "downloadAdvice.storage": "Check available disk space and write permission for the model directory on the machine running DSH, then retry.",
5218
5274
  "downloadAdvice.unknown": "Check the network, disk space and model directory permissions on the machine running DSH, then retry.",
5219
5275
  downloadSource: "Download source: {source}",
5276
+ sourceChoice: "Model download source",
5277
+ sourceAuto: "Automatic (recommended)",
5278
+ sourceHuggingFace: "Hugging Face",
5279
+ sourceMirror: "HF-Mirror (China mirror)",
5280
+ sourceAutoHelp: "Prefer the first responding source and try another if a download fails.",
5281
+ sourceManualHelp: "Download only from the selected source. Verified downloaded files are reused.",
5220
5282
  downloadCode: "Error code: {code}",
5221
5283
  "reconnecting": "Connecting to speech recognition…",
5222
5284
  "preparation.unprepared": "First use downloads the local recognition models.",
@@ -5248,7 +5310,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5248
5310
  requesting: "Allow microphone access to continue…",
5249
5311
  recording: "Recording…",
5250
5312
  interrupted: "Recording was interrupted. Please try again.",
5251
- local: "Audio is recognized on the machine running DSH. First setup downloads models from Hugging Face by default, so that machine must be able to access Hugging Face and its model download services. If a proxy is needed, configure it on that machine.",
5313
+ local: "Audio is recognized on the machine running DSH. If models need downloading, that machine must be able to reach the selected source and its file services. Configure a proxy on that machine if needed.",
5252
5314
  cloud: "Audio will be sent to the selected cloud service.",
5253
5315
  empty: "No speech recognized",
5254
5316
  cancelled: "Voice input cancelled.",
@@ -5367,6 +5429,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5367
5429
  function PreparationCard({ provider, connected, prepare, cancelPreparation, t }) {
5368
5430
  const state = provider.preparation;
5369
5431
  const [expanded, setExpanded] = (0, react.useState)(false), [now, setNow] = (0, react.useState)(Date.now), [error, setError] = (0, react.useState)("");
5432
+ const [source, setSource] = (0, react.useState)(""), [submitting, setSubmitting] = (0, react.useState)(false);
5433
+ const sources = provider.downloadSources ?? [];
5434
+ const [firstSource = ""] = sources;
5435
+ const selectedSource = sources.includes(source) ? source : sources.length === 1 ? firstSource : "";
5436
+ const canPrepare = [
5437
+ "unprepared",
5438
+ "cancelled",
5439
+ "failed"
5440
+ ].includes(state.phase);
5370
5441
  const current = state.steps?.find((step) => step.status === "running" || step.status === "failed" || step.status === "cancelled");
5371
5442
  const preparing = preparationTone(state) === "ongoing";
5372
5443
  const startedAt = current?.startedAt ?? ("startedAt" in state ? state.startedAt : void 0);
@@ -5381,10 +5452,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5381
5452
  }, [preparing, startedAt]);
5382
5453
  const run = async (action) => {
5383
5454
  setError("");
5455
+ setSubmitting(true);
5384
5456
  try {
5385
5457
  await action();
5386
5458
  } catch (failure) {
5387
5459
  setError(failure instanceof Error ? failure.message : String(failure));
5460
+ } finally {
5461
+ setSubmitting(false);
5388
5462
  }
5389
5463
  };
5390
5464
  const metric = state.phase === "downloading" ? byteText(state, t) : preparing && startedAt !== void 0 ? t("elapsed", { seconds: String(Math.max(0, Math.floor((now - startedAt) / 1e3))) }) : "";
@@ -5469,24 +5543,41 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5469
5543
  state,
5470
5544
  t
5471
5545
  }),
5546
+ canPrepare && sources.length > 0 && (0, react_jsx_runtime.jsxs)("div", {
5547
+ className: VoiceInput_module_css_default.sourceChoice,
5548
+ children: [(0, react_jsx_runtime.jsxs)("label", { children: [t("sourceChoice"), (0, react_jsx_runtime.jsxs)("select", {
5549
+ "aria-label": t("sourceChoice"),
5550
+ value: selectedSource,
5551
+ disabled: !connected || submitting || sources.length === 1,
5552
+ onChange: (event) => {
5553
+ setSource(event.target.value);
5554
+ },
5555
+ children: [sources.length > 1 && (0, react_jsx_runtime.jsx)("option", {
5556
+ value: "",
5557
+ children: t("sourceAuto")
5558
+ }), sources.map((origin) => (0, react_jsx_runtime.jsx)("option", {
5559
+ value: origin,
5560
+ children: origin === "https://huggingface.co" ? t("sourceHuggingFace") : origin === "https://hf-mirror.com" ? t("sourceMirror") : origin
5561
+ }, origin))]
5562
+ })] }), (0, react_jsx_runtime.jsx)("small", {
5563
+ className: VoiceInput_module_css_default.metric,
5564
+ children: t(selectedSource === "" ? "sourceAutoHelp" : "sourceManualHelp")
5565
+ })]
5566
+ }),
5472
5567
  (0, react_jsx_runtime.jsxs)("div", {
5473
5568
  className: VoiceInput_module_css_default.preparationActions,
5474
- children: [[
5475
- "unprepared",
5476
- "cancelled",
5477
- "failed"
5478
- ].includes(state.phase) && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5569
+ children: [canPrepare && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5479
5570
  variant: "outline",
5480
5571
  size: "sm",
5481
- disabled: !connected,
5572
+ disabled: !connected || submitting,
5482
5573
  onClick: () => {
5483
- run(() => prepare(provider.id));
5574
+ run(() => selectedSource === "" ? prepare(provider.id) : prepare(provider.id, { downloadSource: selectedSource }));
5484
5575
  },
5485
5576
  children: t(state.phase === "unprepared" ? "prepare" : "retryPrepare")
5486
5577
  }), preparing && state.phase !== "waking" && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5487
5578
  variant: "ghost",
5488
5579
  size: "sm",
5489
- disabled: !connected || state.phase === "cancelling",
5580
+ disabled: !connected || submitting || state.phase === "cancelling",
5490
5581
  onClick: () => {
5491
5582
  run(() => cancelPreparation(provider.id));
5492
5583
  },
@@ -5642,8 +5733,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5642
5733
  const result = await ctx.remote.speech.configure(patch);
5643
5734
  if (!result.ok) throw result.error;
5644
5735
  },
5645
- prepare: async (providerId) => {
5646
- const result = await ctx.remote.speech.prepare(providerId);
5736
+ prepare: async (providerId, options) => {
5737
+ const result = await ctx.remote.speech.prepare(providerId, options);
5647
5738
  if (!result.ok) throw result.error;
5648
5739
  },
5649
5740
  cancelPreparation: async (providerId) => {
@@ -1,6 +1,6 @@
1
1
  import type { HostObservable, InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
2
2
  import type { TranscriptionRequest } from '@deepseek-ai/dsh-experimental-api-speech-to-text/types';
3
- import type { SpeechProviderId, SpeechSelectionPatch, Transcript } from '@deepseek-ai/dsh-experimental-speech-to-text/types';
3
+ import type { SpeechPreparationOptions, SpeechProviderId, SpeechSelectionPatch, Transcript } from '@deepseek-ai/dsh-experimental-speech-to-text/types';
4
4
  import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
5
5
  import { type Recording } from './audio.ts';
6
6
  import type { SpeechReadiness } from './readiness.ts';
@@ -10,7 +10,7 @@ export interface VoiceInputActions {
10
10
  /** @returns one microphone operation owned by the plugin lifecycle. */
11
11
  createRecording: () => Recording;
12
12
  transcribe: (request: TranscriptionRequest, signal: AbortSignal) => Promise<RemoteResult<Transcript>>;
13
- prepare: (providerId: SpeechProviderId) => Promise<void>;
13
+ prepare: (providerId: SpeechProviderId, options?: SpeechPreparationOptions) => Promise<void>;
14
14
  cancelPreparation: (providerId: SpeechProviderId) => Promise<void>;
15
15
  configure: (patch: SpeechSelectionPatch) => Promise<void>;
16
16
  }
@@ -54,6 +54,12 @@ export declare const zh: {
54
54
  readonly 'downloadAdvice.storage': "请检查运行 DSH 的机器的剩余磁盘空间,以及模型目录的写入权限后重试。";
55
55
  readonly 'downloadAdvice.unknown': "请检查运行 DSH 的机器的网络、磁盘空间和模型目录权限后重试。";
56
56
  readonly downloadSource: "下载来源:{source}";
57
+ readonly sourceChoice: "模型下载源";
58
+ readonly sourceAuto: "自动选择(推荐)";
59
+ readonly sourceHuggingFace: "Hugging Face";
60
+ readonly sourceMirror: "HF-Mirror(国内镜像)";
61
+ readonly sourceAutoHelp: "优先使用响应较快的源,下载失败时自动尝试其他源。";
62
+ readonly sourceManualHelp: "仅从所选源下载;已下载并通过校验的文件会复用。";
57
63
  readonly downloadCode: "错误码:{code}";
58
64
  readonly reconnecting: "正在连接语音服务…";
59
65
  readonly 'preparation.unprepared': "首次使用需要下载本地识别模型。";
@@ -85,7 +91,7 @@ export declare const zh: {
85
91
  readonly requesting: "请允许使用麦克风…";
86
92
  readonly recording: "正在录音…";
87
93
  readonly interrupted: "录音中断,请重试。";
88
- readonly local: "音频在运行 DSH 的机器上识别。首次准备默认从 Hugging Face 下载模型,该机器需要能访问 Hugging Face 及其模型下载服务。如需代理,请在该机器上配置。";
94
+ readonly local: "音频在运行 DSH 的机器上识别。需要下载模型时,请确保该机器能访问所选下载源及其文件服务。如需代理,请在该机器上配置。";
89
95
  readonly cloud: "音频将发送至所选云端服务。";
90
96
  readonly empty: "未识别到语音";
91
97
  readonly cancelled: "已取消语音输入。";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-experimental-client-ui-voice-input",
3
3
  "description": "Record speech and insert editable text into the conversation draft",
4
- "version": "0.1.7-alpha.1",
4
+ "version": "0.1.7-rc.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -33,45 +33,45 @@
33
33
  "license": "MIT",
34
34
  "dependencies": {},
35
35
  "peerDependencies": {
36
- "@deepseek-ai/cordis": "^4.0.3",
37
- "@deepseek-ai/dsh-api-remotes": "^0.1.7-alpha.1",
38
- "@deepseek-ai/dsh-api-session-controller": "^0.1.7-alpha.1",
39
- "@deepseek-ai/dsh-client-locale": "^0.1.7-alpha.1",
40
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.7-alpha.1",
41
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-alpha.1",
42
- "@deepseek-ai/dsh-client-ui-session": "^0.1.7-alpha.1",
43
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-alpha.1",
44
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.7-alpha.1",
45
- "@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
46
- "@deepseek-ai/dsh-experimental-speech-to-text": "^0.1.7-alpha.1",
47
- "@deepseek-ai/dsh-api-gateway": "^0.1.7-alpha.1",
48
- "@deepseek-ai/dsh-client-store": "^0.1.7-alpha.1",
49
- "@deepseek-ai/dsh-typert-protocol": "^0.1.7-alpha.1",
50
- "@deepseek-ai/dsh-client-ui-plugin-manager": "^0.1.7-alpha.1",
51
- "@deepseek-ai/dsh-experimental-api-speech-to-text": "^0.1.7-alpha.1"
36
+ "@deepseek-ai/cordis": "~4.0.4",
37
+ "@deepseek-ai/dsh-api-remotes": "0.1.7-rc.1",
38
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.1",
39
+ "@deepseek-ai/dsh-client-locale": "0.1.7-rc.1",
40
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.1",
41
+ "@deepseek-ai/dsh-api-session-controller": "0.1.7-rc.1",
42
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.1",
43
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
44
+ "@deepseek-ai/dsh-client-ui-session": "0.1.7-rc.1",
45
+ "@deepseek-ai/dsh-session": "0.1.7-rc.1",
46
+ "@deepseek-ai/dsh-typert-protocol": "0.1.7-rc.1",
47
+ "@deepseek-ai/dsh-experimental-api-speech-to-text": "0.1.7-rc.1",
48
+ "@deepseek-ai/dsh-experimental-speech-to-text": "0.1.7-rc.1",
49
+ "@deepseek-ai/dsh-api-gateway": "0.1.7-rc.1",
50
+ "@deepseek-ai/dsh-client-ui-plugin-manager": "0.1.7-rc.1",
51
+ "@deepseek-ai/dsh-client-store": "0.1.7-rc.1"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/react": "~18.3.1",
55
55
  "react": "^18.2.0",
56
56
  "react-dom": "^18.2.0",
57
57
  "@testing-library/react": "^16.1.0",
58
- "@deepseek-ai/dsh-api-remotes": "^0.1.7-alpha.1",
59
- "@deepseek-ai/dsh-api-session-controller": "^0.1.7-alpha.1",
60
- "@deepseek-ai/cordis": "^4.0.3",
61
- "@deepseek-ai/dsh-client-locale": "^0.1.7-alpha.1",
62
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-alpha.1",
63
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.7-alpha.1",
64
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-alpha.1",
65
- "@deepseek-ai/dsh-client-ui-session": "^0.1.7-alpha.1",
66
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.7-alpha.1",
67
- "@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
68
- "@deepseek-ai/dsh-typert-protocol": "^0.1.7-alpha.1",
69
- "@deepseek-ai/dsh-experimental-api-speech-to-text": "^0.1.7-alpha.1",
70
- "@deepseek-ai/dsh-experimental-speech-to-text": "^0.1.7-alpha.1",
71
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.7-alpha.1",
72
- "@deepseek-ai/dsh-api-gateway": "^0.1.7-alpha.1",
73
- "@deepseek-ai/dsh-client-store": "^0.1.7-alpha.1",
74
- "@deepseek-ai/dsh-client-ui-plugin-manager": "^0.1.7-alpha.1"
58
+ "@deepseek-ai/cordis": "~4.0.4",
59
+ "@deepseek-ai/dsh-api-remotes": "0.1.7-rc.1",
60
+ "@deepseek-ai/dsh-api-session-controller": "0.1.7-rc.1",
61
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.1",
62
+ "@deepseek-ai/dsh-client-locale": "0.1.7-rc.1",
63
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.1",
64
+ "@deepseek-ai/dsh-client-ui-session": "0.1.7-rc.1",
65
+ "@deepseek-ai/dsh-session": "0.1.7-rc.1",
66
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
67
+ "@deepseek-ai/dsh-typert-protocol": "0.1.7-rc.1",
68
+ "@deepseek-ai/dsh-experimental-speech-to-text": "0.1.7-rc.1",
69
+ "@deepseek-ai/dsh-experimental-api-speech-to-text": "0.1.7-rc.1",
70
+ "@deepseek-ai/dsh-client-test-runtime": "0.1.7-rc.1",
71
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.1",
72
+ "@deepseek-ai/dsh-api-gateway": "0.1.7-rc.1",
73
+ "@deepseek-ai/dsh-client-store": "0.1.7-rc.1",
74
+ "@deepseek-ai/dsh-client-ui-plugin-manager": "0.1.7-rc.1"
75
75
  },
76
76
  "dsh": {
77
77
  "client": {