dsh-live-voice 0.3.0 → 0.3.2
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/CHANGELOG.md +89 -1
- package/PLAN.md +25 -1
- package/README.md +4 -2
- package/docs/ARCHITECTURE.md +9 -16
- package/docs/CONFIGURATION.md +17 -3
- package/docs/VOICE-LIFECYCLE.md +5 -1
- package/lib/client.js +543 -326
- package/lib/server.js +158 -5
- package/package.json +2 -2
- package/src/app/AGENTS.md +7 -0
- package/src/app/ARCHITECTURE.md +10 -0
- package/src/app/client/apply.tsx +96 -40
- package/src/app/client/i18n/catalogs/base.ts +7 -0
- package/src/app/client/i18n/catalogs/en.ts +10 -0
- package/src/app/client/i18n/catalogs/es.ts +10 -0
- package/src/app/client/i18n/catalogs/fr.ts +10 -0
- package/src/app/client/i18n/catalogs/hi.ts +10 -0
- package/src/app/client/i18n/catalogs/pt-BR.ts +10 -0
- package/src/app/client/i18n/catalogs/zh.ts +9 -0
- package/src/app/server/apply.ts +4 -0
- package/src/app/server/registerRoutes.ts +40 -0
- package/src/modules/AGENTS.md +5 -0
- package/src/modules/conversation/ARCHITECTURE.md +7 -0
- package/src/modules/core/ARCHITECTURE.md +5 -0
- package/src/modules/core/coordinator.ts +8 -6
- package/src/modules/core/qwen/QwenHttpHost.ts +1 -1
- package/src/modules/core/qwen/QwenSettings.tsx +15 -14
- package/src/modules/core/settings.ts +26 -8
- package/src/modules/core/transcript.ts +5 -43
- package/src/modules/recognition/ARCHITECTURE.md +5 -0
- package/src/modules/recognition/engines/whisper/WhisperRecognitionEngine.ts +5 -3
- package/src/modules/recognition/engines/whisper/WhisperSettings.tsx +19 -20
- package/src/modules/settings/ARCHITECTURE.md +5 -0
- package/src/modules/settings/components/LiveVoiceSettings.tsx +11 -3
- package/src/modules/settings/models/settingsHost.ts +45 -0
- package/src/modules/settings/models/settingsStorage.ts +45 -10
- package/src/modules/settings/sections/conversation/ConversationSettingsSection.tsx +12 -6
- package/src/modules/settings/sections/recognition/RecognitionEngineSettings.tsx +2 -2
- package/src/modules/settings/sections/recognition/RecognitionFilterSettings.tsx +3 -3
- package/src/modules/settings/sections/recognition/RecognitionSettingsSection.tsx +7 -3
- package/src/modules/settings/sections/recognition/SilenceDetectionSettings.tsx +38 -8
- package/src/modules/settings/sections/recognition/VoiceCommandSettings.tsx +3 -3
- package/src/modules/settings/sections/speak/OutputFilterSettings.tsx +5 -5
- package/src/modules/settings/sections/speak/SpeakSettingsSection.tsx +7 -3
- package/src/modules/settings/sections/speak/SpeechAdvancedSettings.tsx +4 -4
- package/src/modules/settings/sections/speak/SpeechEngineSettings.tsx +2 -2
- package/src/modules/settings/services/releases.ts +10 -2
- package/src/modules/speak/ARCHITECTURE.md +5 -0
- package/src/shared/ARCHITECTURE.md +5 -0
- package/src/shared/design-system/forms/DraftField.tsx +79 -0
- package/src/shared/design-system/forms/NumberField.tsx +4 -10
- package/src/shared/design-system/forms/TextAreaField.tsx +4 -10
- package/src/shared/design-system/forms/TextField.tsx +4 -10
- package/src/shared/design-system/icons/icons.ts +4 -0
- package/src/shared/design-system/layout/SettingsSubcard.tsx +15 -3
- package/src/shared/design-system/layout/SettingsTabs.tsx +4 -2
- package/src/styles/index.ts +3 -3
package/lib/client.js
CHANGED
|
@@ -34,63 +34,34 @@ __export(apply_exports, {
|
|
|
34
34
|
inject: () => inject
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(apply_exports);
|
|
37
|
-
var
|
|
37
|
+
var import_react54 = __toESM(require("react"), 1);
|
|
38
38
|
var import_react_dom = require("react-dom");
|
|
39
39
|
|
|
40
40
|
// src/modules/core/transcript.ts
|
|
41
41
|
var TranscriptDraft = class {
|
|
42
|
-
constructor() {
|
|
43
|
-
this.reset();
|
|
44
|
-
}
|
|
45
42
|
reset() {
|
|
46
|
-
this.owned = null;
|
|
47
|
-
this.edited = false;
|
|
48
43
|
}
|
|
49
44
|
update(current, hypothesis, final = false) {
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
let base = current;
|
|
55
|
-
let at = current.length;
|
|
56
|
-
if (this.owned) {
|
|
57
|
-
const { start, text: text2, before: before2, after: after2 } = this.owned;
|
|
58
|
-
if (current === before2 + text2 + after2) {
|
|
59
|
-
base = before2 + after2;
|
|
60
|
-
at = start;
|
|
61
|
-
} else if (text2 && current.indexOf(text2) >= 0 && current.indexOf(text2) === current.lastIndexOf(text2)) {
|
|
62
|
-
at = current.indexOf(text2);
|
|
63
|
-
base = current.slice(0, at) + current.slice(at + text2.length);
|
|
64
|
-
} else {
|
|
65
|
-
this.owned = null;
|
|
66
|
-
this.edited = !final;
|
|
67
|
-
return current;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
const before = base.slice(0, at);
|
|
71
|
-
const after = base.slice(at);
|
|
72
|
-
const separator = hypothesis && before && !/\s$/.test(before) ? " " : "";
|
|
73
|
-
const text = separator + hypothesis;
|
|
74
|
-
const result = before + text + after;
|
|
75
|
-
this.owned = final ? null : { start: at, text, before, after };
|
|
76
|
-
return result;
|
|
45
|
+
if (!final || !hypothesis) return current;
|
|
46
|
+
const separator = current && !/\s$/.test(current) ? " " : "";
|
|
47
|
+
return current + separator + hypothesis;
|
|
77
48
|
}
|
|
78
49
|
};
|
|
79
50
|
|
|
80
51
|
// src/modules/core/settings.ts
|
|
81
52
|
var voiceDetectionPresets = Object.freeze({
|
|
82
53
|
short: Object.freeze({
|
|
83
|
-
silenceMs:
|
|
54
|
+
silenceMs: 500,
|
|
84
55
|
label: "Short",
|
|
85
56
|
description: "Send quickly after a short pause."
|
|
86
57
|
}),
|
|
87
58
|
natural: Object.freeze({
|
|
88
|
-
silenceMs:
|
|
59
|
+
silenceMs: 1e3,
|
|
89
60
|
label: "Natural",
|
|
90
61
|
description: "Allow normal pauses between phrases."
|
|
91
62
|
}),
|
|
92
63
|
long: Object.freeze({
|
|
93
|
-
silenceMs:
|
|
64
|
+
silenceMs: 2e3,
|
|
94
65
|
label: "Long",
|
|
95
66
|
description: "Wait through longer thinking pauses."
|
|
96
67
|
})
|
|
@@ -116,7 +87,8 @@ var defaultSettings = Object.freeze({
|
|
|
116
87
|
recognitionEngine: "browser",
|
|
117
88
|
recognitionProcessLocally: true,
|
|
118
89
|
recognitionAutoInstall: true,
|
|
119
|
-
voiceDetectionPreset: "
|
|
90
|
+
voiceDetectionPreset: "short",
|
|
91
|
+
voiceDetectionCustomSilenceMs: 1e3,
|
|
120
92
|
recognitionMaxUtteranceSeconds: 60,
|
|
121
93
|
microphoneEnabled: true,
|
|
122
94
|
holdToTalkEnabled: true,
|
|
@@ -150,6 +122,10 @@ var defaultSettings = Object.freeze({
|
|
|
150
122
|
segmentGapMs: 400
|
|
151
123
|
});
|
|
152
124
|
var normalizeCommandPhrases = (value, fallback) => typeof value === "string" && value.length <= 1e3 && !value.includes("\0") ? value.split(/[,\r\n]+/).map((phrase) => phrase.trim()).filter(Boolean).slice(0, 20).join(", ") : fallback;
|
|
125
|
+
var customSilenceMinMs = 100;
|
|
126
|
+
var customSilenceMaxMs = 1e4;
|
|
127
|
+
var normalizeCustomSilenceMs = (value) => Number.isInteger(value) && value >= customSilenceMinMs && value <= customSilenceMaxMs ? value : defaultSettings.voiceDetectionCustomSilenceMs;
|
|
128
|
+
var voiceDetectionSilenceMs = (settings) => settings.voiceDetectionPreset === "custom" ? normalizeCustomSilenceMs(settings.voiceDetectionCustomSilenceMs) : (voiceDetectionPresets[settings.voiceDetectionPreset] || voiceDetectionPresets[defaultSettings.voiceDetectionPreset]).silenceMs;
|
|
153
129
|
function normalizeSettings(value) {
|
|
154
130
|
const source = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
155
131
|
return {
|
|
@@ -157,7 +133,8 @@ function normalizeSettings(value) {
|
|
|
157
133
|
recognitionEngine: ["browser", "whisper-http", "qwen-http"].includes(source.recognitionEngine) ? source.recognitionEngine : defaultSettings.recognitionEngine,
|
|
158
134
|
recognitionProcessLocally: typeof source.recognitionProcessLocally === "boolean" ? source.recognitionProcessLocally : defaultSettings.recognitionProcessLocally,
|
|
159
135
|
recognitionAutoInstall: typeof source.recognitionAutoInstall === "boolean" ? source.recognitionAutoInstall : defaultSettings.recognitionAutoInstall,
|
|
160
|
-
voiceDetectionPreset: Object.hasOwn(voiceDetectionPresets, source.voiceDetectionPreset) ? source.voiceDetectionPreset : defaultSettings.voiceDetectionPreset,
|
|
136
|
+
voiceDetectionPreset: source.voiceDetectionPreset === "custom" || Object.hasOwn(voiceDetectionPresets, source.voiceDetectionPreset) ? source.voiceDetectionPreset : defaultSettings.voiceDetectionPreset,
|
|
137
|
+
voiceDetectionCustomSilenceMs: normalizeCustomSilenceMs(source.voiceDetectionCustomSilenceMs),
|
|
161
138
|
recognitionMaxUtteranceSeconds: Number.isInteger(source.recognitionMaxUtteranceSeconds) && source.recognitionMaxUtteranceSeconds >= 10 && source.recognitionMaxUtteranceSeconds <= 300 ? source.recognitionMaxUtteranceSeconds : defaultSettings.recognitionMaxUtteranceSeconds,
|
|
162
139
|
microphoneEnabled: typeof source.microphoneEnabled === "boolean" ? source.microphoneEnabled : defaultSettings.microphoneEnabled,
|
|
163
140
|
holdToTalkEnabled: typeof source.holdToTalkEnabled === "boolean" ? source.holdToTalkEnabled : defaultSettings.holdToTalkEnabled,
|
|
@@ -339,7 +316,8 @@ var VoiceCoordinator = class {
|
|
|
339
316
|
lang: this.snapshot.settings.recognitionLang,
|
|
340
317
|
processLocally: this.snapshot.settings.recognitionProcessLocally,
|
|
341
318
|
autoInstallLocalPack: this.snapshot.settings.recognitionAutoInstall,
|
|
342
|
-
voiceDetectionPreset: this.snapshot.settings.voiceDetectionPreset
|
|
319
|
+
voiceDetectionPreset: this.snapshot.settings.voiceDetectionPreset,
|
|
320
|
+
voiceDetectionCustomSilenceMs: this.snapshot.settings.voiceDetectionCustomSilenceMs
|
|
343
321
|
});
|
|
344
322
|
this.patch({ capabilities: { ...this.snapshot.capabilities, recognition: void 0 } });
|
|
345
323
|
}
|
|
@@ -361,7 +339,8 @@ var VoiceCoordinator = class {
|
|
|
361
339
|
lang: settings.recognitionLang,
|
|
362
340
|
processLocally: settings.recognitionProcessLocally,
|
|
363
341
|
autoInstallLocalPack: settings.recognitionAutoInstall,
|
|
364
|
-
voiceDetectionPreset: settings.voiceDetectionPreset
|
|
342
|
+
voiceDetectionPreset: settings.voiceDetectionPreset,
|
|
343
|
+
voiceDetectionCustomSilenceMs: settings.voiceDetectionCustomSilenceMs
|
|
365
344
|
});
|
|
366
345
|
this.patch({
|
|
367
346
|
settings,
|
|
@@ -454,7 +433,6 @@ var VoiceCoordinator = class {
|
|
|
454
433
|
}
|
|
455
434
|
muteListening() {
|
|
456
435
|
this.cancelAutoSend();
|
|
457
|
-
this.composer.setDraft(this.transcript.update(this.composer.getDraft(), "", true));
|
|
458
436
|
this.transcript.reset();
|
|
459
437
|
this.updateSettings({ microphoneEnabled: false });
|
|
460
438
|
if (!this.snapshot.settings.voiceCommandsEnabled) return this.stopListening();
|
|
@@ -636,7 +614,6 @@ var VoiceCoordinator = class {
|
|
|
636
614
|
clear: this.snapshot.settings.voiceCommandClear
|
|
637
615
|
});
|
|
638
616
|
if (command) {
|
|
639
|
-
this.composer.setDraft(this.transcript.update(this.composer.getDraft(), "", true));
|
|
640
617
|
this.transcript.reset();
|
|
641
618
|
this.cancelAutoSend();
|
|
642
619
|
this.patch({ recognizing: false });
|
|
@@ -660,13 +637,11 @@ var VoiceCoordinator = class {
|
|
|
660
637
|
return;
|
|
661
638
|
}
|
|
662
639
|
if (this.snapshot.muted) {
|
|
663
|
-
this.composer.setDraft(this.transcript.update(this.composer.getDraft(), "", true));
|
|
664
640
|
this.transcript.reset();
|
|
665
641
|
this.patch({ recognizing: false });
|
|
666
642
|
return;
|
|
667
643
|
}
|
|
668
644
|
if (this.snapshot.settings.recognitionFilterEnabled && !hasMinimumWords(final, this.snapshot.settings.recognitionMinimumWords)) {
|
|
669
|
-
this.composer.setDraft(this.transcript.update(this.composer.getDraft(), "", true));
|
|
670
645
|
this.transcript.reset();
|
|
671
646
|
this.patch({ recognizing: false });
|
|
672
647
|
return;
|
|
@@ -677,7 +652,6 @@ var VoiceCoordinator = class {
|
|
|
677
652
|
}
|
|
678
653
|
if (interim) {
|
|
679
654
|
this.cancelAutoSend();
|
|
680
|
-
this.composer.setDraft(this.transcript.update(this.composer.getDraft(), interim));
|
|
681
655
|
}
|
|
682
656
|
if (!interim && this.snapshot.recognizing)
|
|
683
657
|
this.assistantSpeechNotBefore = Date.now() + this.snapshot.settings.assistantSpeechDelaySeconds * 1e3;
|
|
@@ -745,7 +719,6 @@ var VoiceCoordinator = class {
|
|
|
745
719
|
});
|
|
746
720
|
}
|
|
747
721
|
async cancelDictation() {
|
|
748
|
-
this.composer.setDraft(this.transcript.update(this.composer.getDraft(), "", true));
|
|
749
722
|
await this.stopListening();
|
|
750
723
|
}
|
|
751
724
|
async endConversation() {
|
|
@@ -1233,7 +1206,7 @@ var VoiceOwnership = class {
|
|
|
1233
1206
|
};
|
|
1234
1207
|
|
|
1235
1208
|
// src/modules/recognition/components/RecognitionCapabilityStatus.tsx
|
|
1236
|
-
var
|
|
1209
|
+
var import_react20 = __toESM(require("react"), 1);
|
|
1237
1210
|
|
|
1238
1211
|
// src/app/client/i18n/catalogs/en.ts
|
|
1239
1212
|
var en = {
|
|
@@ -1324,6 +1297,8 @@ var en = {
|
|
|
1324
1297
|
"dsh-live-voice.recognition.planned.vote": "Coming Soon \u2014 vote on repo issues",
|
|
1325
1298
|
"dsh-live-voice.recognition.planned.voxtral": "Voxtral Realtime \u2014 Soon",
|
|
1326
1299
|
"dsh-live-voice.recognition.planned.webGpu": "Browser WebGPU Inference \u2014 Soon",
|
|
1300
|
+
"dsh-live-voice.recognition.presets.custom.description": "Choose your own silence duration.",
|
|
1301
|
+
"dsh-live-voice.recognition.presets.custom.label": "Custom",
|
|
1327
1302
|
"dsh-live-voice.recognition.presets.long.description": "Wait through longer thinking pauses.",
|
|
1328
1303
|
"dsh-live-voice.recognition.presets.long.label": "Long",
|
|
1329
1304
|
"dsh-live-voice.recognition.presets.natural.description": "Allow normal pauses between phrases.",
|
|
@@ -1336,6 +1311,8 @@ var en = {
|
|
|
1336
1311
|
"dsh-live-voice.recognition.qwen.endpointHelp": "HTTP API at the configured DSH host URL (default: http://127.0.0.1:8080/inference). Audio uses the authenticated DSH host transcription route.",
|
|
1337
1312
|
"dsh-live-voice.recognition.qwen.hostHelp": "Host-wide settings for the Qwen3 ASR + TTS server. Enter any HTTP or HTTPS base URL reachable from the DSH host. The browser accesses it through authenticated DSH routes.",
|
|
1338
1313
|
"dsh-live-voice.recognition.qwen.label": "Qwen3 ASR \u2014 HTTP API",
|
|
1314
|
+
"dsh-live-voice.recognition.silenceDetection.customHelp": "Enter a whole number from 100 to 10,000 ms. Saved when you leave the field. Short pauses may split speech; recognition adds its own latency.",
|
|
1315
|
+
"dsh-live-voice.recognition.silenceDetection.customLabel": "Custom pause (milliseconds)",
|
|
1339
1316
|
"dsh-live-voice.recognition.silenceDetection.duration": "Pause before sending: {milliseconds} ms",
|
|
1340
1317
|
"dsh-live-voice.recognition.silenceDetection.help": "Controls how long a pause must last before captured speech is sent for recognition.",
|
|
1341
1318
|
"dsh-live-voice.recognition.silenceDetection.label": "Silence detection",
|
|
@@ -1372,6 +1349,9 @@ var en = {
|
|
|
1372
1349
|
"dsh-live-voice.settings.delivery.toggle": "Automatic delivery mode",
|
|
1373
1350
|
"dsh-live-voice.settings.engine.refresh": "Refresh available engines",
|
|
1374
1351
|
"dsh-live-voice.settings.filters.title": "Filtering",
|
|
1352
|
+
"dsh-live-voice.settings.general.title": "General",
|
|
1353
|
+
"dsh-live-voice.settings.persistence.loadError": "Could not load Live Voice settings from the server. Reload to try again.",
|
|
1354
|
+
"dsh-live-voice.settings.persistence.saveError": "Could not save Live Voice settings on the server. Please try again.",
|
|
1375
1355
|
"dsh-live-voice.settings.tabs.conversation": "Conversation",
|
|
1376
1356
|
"dsh-live-voice.settings.tabs.recognition": "Speech recognition",
|
|
1377
1357
|
"dsh-live-voice.settings.tabs.speak": "Speech",
|
|
@@ -1540,6 +1520,8 @@ var es = {
|
|
|
1540
1520
|
"dsh-live-voice.recognition.planned.vote": "Pr\xF3ximamente \u2014 vota en las incidencias del repositorio",
|
|
1541
1521
|
"dsh-live-voice.recognition.planned.voxtral": "Voxtral Realtime \u2014 Pr\xF3ximamente",
|
|
1542
1522
|
"dsh-live-voice.recognition.planned.webGpu": "Inferencia WebGPU en el navegador \u2014 Pr\xF3ximamente",
|
|
1523
|
+
"dsh-live-voice.recognition.presets.custom.description": "Elige la duraci\xF3n del silencio.",
|
|
1524
|
+
"dsh-live-voice.recognition.presets.custom.label": "Personalizada",
|
|
1543
1525
|
"dsh-live-voice.recognition.presets.long.description": "Espera durante pausas de reflexi\xF3n m\xE1s largas.",
|
|
1544
1526
|
"dsh-live-voice.recognition.presets.long.label": "Larga",
|
|
1545
1527
|
"dsh-live-voice.recognition.presets.natural.description": "Permite pausas normales entre frases.",
|
|
@@ -1552,6 +1534,8 @@ var es = {
|
|
|
1552
1534
|
"dsh-live-voice.recognition.qwen.endpointHelp": "API HTTP en la URL configurada en el host de DSH (predeterminada: http://127.0.0.1:8080/inference). El audio utiliza la ruta de transcripci\xF3n autenticada del host de DSH.",
|
|
1553
1535
|
"dsh-live-voice.recognition.qwen.hostHelp": "Configuraci\xF3n para todo el host del servidor Qwen3 ASR + TTS. Introduce una URL base HTTP o HTTPS accesible desde el host de DSH. El navegador accede a ella mediante las rutas autenticadas de DSH.",
|
|
1554
1536
|
"dsh-live-voice.recognition.qwen.label": "Qwen3 ASR \u2014 API HTTP",
|
|
1537
|
+
"dsh-live-voice.recognition.silenceDetection.customHelp": "Introduce un n\xFAmero entero de 100 a 10.000 ms. Se guarda al salir del campo. Las pausas cortas pueden dividir el habla; el reconocimiento a\xF1ade su propia latencia.",
|
|
1538
|
+
"dsh-live-voice.recognition.silenceDetection.customLabel": "Pausa personalizada (milisegundos)",
|
|
1555
1539
|
"dsh-live-voice.recognition.silenceDetection.duration": "Pausa antes de enviar: {milliseconds} ms",
|
|
1556
1540
|
"dsh-live-voice.recognition.silenceDetection.help": "Determina cu\xE1nto debe durar una pausa antes de enviar la voz capturada para su reconocimiento.",
|
|
1557
1541
|
"dsh-live-voice.recognition.silenceDetection.label": "Detecci\xF3n de silencio",
|
|
@@ -1588,6 +1572,9 @@ var es = {
|
|
|
1588
1572
|
"dsh-live-voice.settings.delivery.toggle": "Modo de env\xEDo autom\xE1tico",
|
|
1589
1573
|
"dsh-live-voice.settings.engine.refresh": "Actualizar los motores disponibles",
|
|
1590
1574
|
"dsh-live-voice.settings.filters.title": "Filtrado",
|
|
1575
|
+
"dsh-live-voice.settings.general.title": "General",
|
|
1576
|
+
"dsh-live-voice.settings.persistence.loadError": "No se pudieron cargar los ajustes de Live Voice del servidor. Recarga para volver a intentarlo.",
|
|
1577
|
+
"dsh-live-voice.settings.persistence.saveError": "No se pudieron guardar los ajustes de Live Voice en el servidor. Int\xE9ntalo de nuevo.",
|
|
1591
1578
|
"dsh-live-voice.settings.tabs.conversation": "Conversaci\xF3n",
|
|
1592
1579
|
"dsh-live-voice.settings.tabs.recognition": "Reconocimiento de voz",
|
|
1593
1580
|
"dsh-live-voice.settings.tabs.speak": "S\xEDntesis de voz",
|
|
@@ -1756,6 +1743,8 @@ var fr = {
|
|
|
1756
1743
|
"dsh-live-voice.recognition.planned.vote": "Bient\xF4t disponible \u2014 votez dans les tickets du d\xE9p\xF4t",
|
|
1757
1744
|
"dsh-live-voice.recognition.planned.voxtral": "Voxtral Realtime \u2014 Bient\xF4t disponible",
|
|
1758
1745
|
"dsh-live-voice.recognition.planned.webGpu": "Inf\xE9rence WebGPU dans le navigateur \u2014 Bient\xF4t disponible",
|
|
1746
|
+
"dsh-live-voice.recognition.presets.custom.description": "Choisissez la dur\xE9e du silence.",
|
|
1747
|
+
"dsh-live-voice.recognition.presets.custom.label": "Personnalis\xE9e",
|
|
1759
1748
|
"dsh-live-voice.recognition.presets.long.description": "Attend pendant les pauses de r\xE9flexion plus longues.",
|
|
1760
1749
|
"dsh-live-voice.recognition.presets.long.label": "Longue",
|
|
1761
1750
|
"dsh-live-voice.recognition.presets.natural.description": "Autorise des pauses normales entre les phrases.",
|
|
@@ -1768,6 +1757,8 @@ var fr = {
|
|
|
1768
1757
|
"dsh-live-voice.recognition.qwen.endpointHelp": "API HTTP \xE0 l\u2019URL configur\xE9e sur l\u2019h\xF4te DSH (par d\xE9faut : http://127.0.0.1:8080/inference). Le son passe par la route de transcription authentifi\xE9e de l\u2019h\xF4te DSH.",
|
|
1769
1758
|
"dsh-live-voice.recognition.qwen.hostHelp": "Param\xE8tres communs \xE0 tout l\u2019h\xF4te pour le serveur Qwen3 ASR + TTS. Saisissez une URL de base HTTP ou HTTPS accessible depuis l\u2019h\xF4te DSH. Le navigateur y acc\xE8de via les routes authentifi\xE9es de DSH.",
|
|
1770
1759
|
"dsh-live-voice.recognition.qwen.label": "Qwen3 ASR \u2014 API HTTP",
|
|
1760
|
+
"dsh-live-voice.recognition.silenceDetection.customHelp": "Saisissez un entier de 100 \xE0 10 000 ms. Enregistr\xE9 en quittant le champ. Les pauses courtes peuvent couper la parole ; la reconnaissance ajoute sa propre latence.",
|
|
1761
|
+
"dsh-live-voice.recognition.silenceDetection.customLabel": "Pause personnalis\xE9e (millisecondes)",
|
|
1771
1762
|
"dsh-live-voice.recognition.silenceDetection.duration": "Pause avant l\u2019envoi : {milliseconds} ms",
|
|
1772
1763
|
"dsh-live-voice.recognition.silenceDetection.help": "D\xE9termine la dur\xE9e de pause n\xE9cessaire avant que la parole captur\xE9e soit envoy\xE9e pour reconnaissance.",
|
|
1773
1764
|
"dsh-live-voice.recognition.silenceDetection.label": "D\xE9tection du silence",
|
|
@@ -1804,6 +1795,9 @@ var fr = {
|
|
|
1804
1795
|
"dsh-live-voice.settings.delivery.toggle": "Mode d\u2019envoi automatique",
|
|
1805
1796
|
"dsh-live-voice.settings.engine.refresh": "Actualiser les moteurs disponibles",
|
|
1806
1797
|
"dsh-live-voice.settings.filters.title": "Filtrage",
|
|
1798
|
+
"dsh-live-voice.settings.general.title": "G\xE9n\xE9ral",
|
|
1799
|
+
"dsh-live-voice.settings.persistence.loadError": "Impossible de charger les param\xE8tres Live Voice depuis le serveur. Rechargez pour r\xE9essayer.",
|
|
1800
|
+
"dsh-live-voice.settings.persistence.saveError": "Impossible de sauvegarder les param\xE8tres Live Voice sur le serveur. R\xE9essayez.",
|
|
1807
1801
|
"dsh-live-voice.settings.tabs.conversation": "Conversation",
|
|
1808
1802
|
"dsh-live-voice.settings.tabs.recognition": "Reconnaissance vocale",
|
|
1809
1803
|
"dsh-live-voice.settings.tabs.speak": "Synth\xE8se vocale",
|
|
@@ -1972,6 +1966,8 @@ var hi = {
|
|
|
1972
1966
|
"dsh-live-voice.recognition.planned.vote": "\u091C\u0932\u094D\u0926 \u0906 \u0930\u0939\u093E \u0939\u0948 \u2014 \u0930\u093F\u092A\u0949\u091C\u093C\u093F\u091F\u0930\u0940 \u0915\u0947 \u0907\u0936\u094D\u092F\u0942 \u092E\u0947\u0902 \u0935\u094B\u091F \u0926\u0947\u0902",
|
|
1973
1967
|
"dsh-live-voice.recognition.planned.voxtral": "Voxtral Realtime \u2014 \u091C\u0932\u094D\u0926 \u0906 \u0930\u0939\u093E \u0939\u0948",
|
|
1974
1968
|
"dsh-live-voice.recognition.planned.webGpu": "\u092C\u094D\u0930\u093E\u0909\u091C\u093C\u0930 \u092E\u0947\u0902 WebGPU \u0907\u0928\u094D\u092B\u093C\u0930\u0947\u0902\u0938 \u2014 \u091C\u0932\u094D\u0926 \u0906 \u0930\u0939\u093E \u0939\u0948",
|
|
1969
|
+
"dsh-live-voice.recognition.presets.custom.description": "\u092E\u094C\u0928 \u0915\u0940 \u0905\u0935\u0927\u093F \u0938\u094D\u0935\u092F\u0902 \u091A\u0941\u0928\u0947\u0902\u0964",
|
|
1970
|
+
"dsh-live-voice.recognition.presets.custom.label": "\u0915\u0938\u094D\u091F\u092E",
|
|
1975
1971
|
"dsh-live-voice.recognition.presets.long.description": "\u0932\u0902\u092C\u0947 \u0938\u094B\u091A-\u0935\u093F\u091A\u093E\u0930 \u0935\u093E\u0932\u0947 \u0935\u093F\u0930\u093E\u092E\u094B\u0902 \u0924\u0915 \u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E \u0915\u0930\u0947\u0902\u0964",
|
|
1976
1972
|
"dsh-live-voice.recognition.presets.long.label": "\u0932\u0902\u092C\u093E",
|
|
1977
1973
|
"dsh-live-voice.recognition.presets.natural.description": "\u0935\u093E\u0915\u094D\u092F\u093E\u0902\u0936\u094B\u0902 \u0915\u0947 \u092C\u0940\u091A \u0938\u093E\u092E\u093E\u0928\u094D\u092F \u0935\u093F\u0930\u093E\u092E \u0915\u0940 \u0905\u0928\u0941\u092E\u0924\u093F \u0926\u0947\u0902\u0964",
|
|
@@ -1984,6 +1980,8 @@ var hi = {
|
|
|
1984
1980
|
"dsh-live-voice.recognition.qwen.endpointHelp": "HTTP API, \u0915\u0949\u0928\u094D\u092B\u093C\u093F\u0917\u0930 \u0915\u093F\u090F \u0917\u090F DSH \u0939\u094B\u0938\u094D\u091F URL \u092A\u0930 \u0939\u0948 (\u0921\u093F\u092B\u093C\u0949\u0932\u094D\u091F: http://127.0.0.1:8080/inference)\u0964 \u0911\u0921\u093F\u092F\u094B \u0915\u0947 \u0932\u093F\u090F \u092A\u094D\u0930\u092E\u093E\u0923\u0940\u0915\u0930\u0923 \u0935\u093E\u0932\u0947 DSH \u0939\u094B\u0938\u094D\u091F \u091F\u094D\u0930\u093E\u0902\u0938\u0915\u094D\u0930\u093F\u092A\u094D\u0936\u0928 \u0930\u0942\u091F \u0915\u093E \u0909\u092A\u092F\u094B\u0917 \u0939\u094B\u0924\u093E \u0939\u0948\u0964",
|
|
1985
1981
|
"dsh-live-voice.recognition.qwen.hostHelp": "Qwen3 ASR + TTS \u0938\u0930\u094D\u0935\u0930 \u0915\u0940 \u092A\u0942\u0930\u0947 \u0939\u094B\u0938\u094D\u091F \u092A\u0930 \u0932\u093E\u0917\u0942 \u0939\u094B\u0928\u0947 \u0935\u093E\u0932\u0940 \u0938\u0947\u091F\u093F\u0902\u0917\u094D\u0938\u0964 \u0915\u094B\u0908 \u092D\u0940 HTTP \u092F\u093E HTTPS \u092C\u0947\u0938 URL \u0926\u0930\u094D\u091C \u0915\u0930\u0947\u0902 \u091C\u093F\u0938 \u0924\u0915 DSH \u0939\u094B\u0938\u094D\u091F \u092A\u0939\u0941\u0901\u091A \u0938\u0915\u0947\u0964 \u092C\u094D\u0930\u093E\u0909\u091C\u093C\u0930 \u092A\u094D\u0930\u092E\u093E\u0923\u0940\u0915\u0930\u0923 \u0935\u093E\u0932\u0947 DSH \u0930\u0942\u091F\u094B\u0902 \u0915\u0947 \u091C\u093C\u0930\u093F\u090F \u0907\u0938\u0947 \u090F\u0915\u094D\u0938\u0947\u0938 \u0915\u0930\u0924\u093E \u0939\u0948\u0964",
|
|
1986
1982
|
"dsh-live-voice.recognition.qwen.label": "Qwen3 ASR \u2014 HTTP API",
|
|
1983
|
+
"dsh-live-voice.recognition.silenceDetection.customHelp": "100 \u0938\u0947 10,000 ms \u0924\u0915 \u092A\u0942\u0930\u094D\u0923\u093E\u0902\u0915 \u0926\u0930\u094D\u091C \u0915\u0930\u0947\u0902\u0964 \u092B\u093C\u0940\u0932\u094D\u0921 \u091B\u094B\u0921\u093C\u0928\u0947 \u092A\u0930 \u0938\u0939\u0947\u091C\u093E \u091C\u093E\u0924\u093E \u0939\u0948\u0964 \u091B\u094B\u091F\u0947 \u0935\u093F\u0930\u093E\u092E \u092C\u094B\u0932\u0928\u0947 \u0915\u094B \u0916\u0902\u0921\u094B\u0902 \u092E\u0947\u0902 \u092C\u093E\u0901\u091F \u0938\u0915\u0924\u0947 \u0939\u0948\u0902; \u092A\u0939\u091A\u093E\u0928 \u092E\u0947\u0902 \u0905\u0924\u093F\u0930\u093F\u0915\u094D\u0924 \u0938\u092E\u092F \u0932\u0917\u0924\u093E \u0939\u0948\u0964",
|
|
1984
|
+
"dsh-live-voice.recognition.silenceDetection.customLabel": "\u0915\u0938\u094D\u091F\u092E \u0935\u093F\u0930\u093E\u092E (\u092E\u093F\u0932\u0940\u0938\u0947\u0915\u0902\u0921)",
|
|
1987
1985
|
"dsh-live-voice.recognition.silenceDetection.duration": "\u092D\u0947\u091C\u0928\u0947 \u0938\u0947 \u092A\u0939\u0932\u0947 \u0915\u093E \u0935\u093F\u0930\u093E\u092E: {milliseconds} \u092E\u093F\u0932\u0940\u0938\u0947\u0915\u0902\u0921",
|
|
1988
1986
|
"dsh-live-voice.recognition.silenceDetection.help": "\u0924\u092F \u0915\u0930\u0924\u093E \u0939\u0948 \u0915\u093F \u0930\u093F\u0915\u0949\u0930\u094D\u0921 \u0915\u0940 \u0917\u0908 \u0906\u0935\u093E\u091C\u093C \u0915\u094B \u092A\u0939\u091A\u093E\u0928 \u0915\u0947 \u0932\u093F\u090F \u092D\u0947\u091C\u0928\u0947 \u0938\u0947 \u092A\u0939\u0932\u0947 \u0935\u093F\u0930\u093E\u092E \u0915\u093F\u0924\u0928\u0940 \u0926\u0947\u0930 \u0930\u0939\u0928\u093E \u091A\u093E\u0939\u093F\u090F\u0964",
|
|
1989
1987
|
"dsh-live-voice.recognition.silenceDetection.label": "\u092E\u094C\u0928 \u0915\u093E \u092A\u0924\u093E \u0932\u0917\u093E\u0928\u093E",
|
|
@@ -2020,6 +2018,9 @@ var hi = {
|
|
|
2020
2018
|
"dsh-live-voice.settings.delivery.toggle": "\u0938\u094D\u0935\u091A\u093E\u0932\u093F\u0924 \u092D\u0947\u091C\u0928\u0947 \u0915\u093E \u092E\u094B\u0921",
|
|
2021
2019
|
"dsh-live-voice.settings.engine.refresh": "\u0909\u092A\u0932\u092C\u094D\u0927 \u0907\u0902\u091C\u0928 \u0930\u0940\u092B\u093C\u094D\u0930\u0947\u0936 \u0915\u0930\u0947\u0902",
|
|
2022
2020
|
"dsh-live-voice.settings.filters.title": "\u092B\u093C\u093F\u0932\u094D\u091F\u0930\u093F\u0902\u0917",
|
|
2021
|
+
"dsh-live-voice.settings.general.title": "\u0938\u093E\u092E\u093E\u0928\u094D\u092F",
|
|
2022
|
+
"dsh-live-voice.settings.persistence.loadError": "\u0938\u0930\u094D\u0935\u0930 \u0938\u0947 Live Voice \u0938\u0947\u091F\u093F\u0902\u0917 \u0932\u094B\u0921 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u0940\u0902\u0964 \u092B\u093F\u0930 \u092A\u094D\u0930\u092F\u093E\u0938 \u0915\u0930\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F \u092A\u0947\u091C \u0930\u0940\u0932\u094B\u0921 \u0915\u0930\u0947\u0902\u0964",
|
|
2023
|
+
"dsh-live-voice.settings.persistence.saveError": "\u0938\u0930\u094D\u0935\u0930 \u092A\u0930 Live Voice \u0938\u0947\u091F\u093F\u0902\u0917 \u0938\u0947\u0935 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u0940\u0902\u0964 \u092B\u093F\u0930 \u092A\u094D\u0930\u092F\u093E\u0938 \u0915\u0930\u0947\u0902\u0964",
|
|
2023
2024
|
"dsh-live-voice.settings.tabs.conversation": "\u092C\u093E\u0924\u091A\u0940\u0924",
|
|
2024
2025
|
"dsh-live-voice.settings.tabs.recognition": "\u0935\u093E\u0915\u094D \u092A\u0939\u091A\u093E\u0928",
|
|
2025
2026
|
"dsh-live-voice.settings.tabs.speak": "\u0935\u093E\u091A\u0928",
|
|
@@ -2188,6 +2189,8 @@ var ptBR = {
|
|
|
2188
2189
|
"dsh-live-voice.recognition.planned.vote": "Em breve \u2014 vote nas issues do reposit\xF3rio",
|
|
2189
2190
|
"dsh-live-voice.recognition.planned.voxtral": "Voxtral Realtime \u2014 Em breve",
|
|
2190
2191
|
"dsh-live-voice.recognition.planned.webGpu": "Infer\xEAncia WebGPU no navegador \u2014 Em breve",
|
|
2192
|
+
"dsh-live-voice.recognition.presets.custom.description": "Escolha a dura\xE7\xE3o do sil\xEAncio.",
|
|
2193
|
+
"dsh-live-voice.recognition.presets.custom.label": "Personalizada",
|
|
2191
2194
|
"dsh-live-voice.recognition.presets.long.description": "Aguarda durante pausas de pensamento mais longas.",
|
|
2192
2195
|
"dsh-live-voice.recognition.presets.long.label": "Longa",
|
|
2193
2196
|
"dsh-live-voice.recognition.presets.natural.description": "Permite pausas normais entre frases.",
|
|
@@ -2200,6 +2203,8 @@ var ptBR = {
|
|
|
2200
2203
|
"dsh-live-voice.recognition.qwen.endpointHelp": "API HTTP na URL configurada do host DSH (padr\xE3o: http://127.0.0.1:8080/inference). O \xE1udio usa a rota de transcri\xE7\xE3o autenticada do host DSH.",
|
|
2201
2204
|
"dsh-live-voice.recognition.qwen.hostHelp": "Configura\xE7\xF5es de todo o host para o servidor Qwen3 ASR + TTS. Insira qualquer URL base HTTP ou HTTPS acess\xEDvel a partir do host DSH. O navegador acessa atrav\xE9s de rotas autenticadas do DSH.",
|
|
2202
2205
|
"dsh-live-voice.recognition.qwen.label": "Qwen3 ASR \u2014 API HTTP",
|
|
2206
|
+
"dsh-live-voice.recognition.silenceDetection.customHelp": "Insira um n\xFAmero inteiro de 100 a 10.000 ms. Salvo ao sair do campo. Pausas curtas podem dividir a fala; o reconhecimento acrescenta sua pr\xF3pria lat\xEAncia.",
|
|
2207
|
+
"dsh-live-voice.recognition.silenceDetection.customLabel": "Pausa personalizada (milissegundos)",
|
|
2203
2208
|
"dsh-live-voice.recognition.silenceDetection.duration": "Pausa antes de enviar: {milliseconds} ms",
|
|
2204
2209
|
"dsh-live-voice.recognition.silenceDetection.help": "Controla a dura\xE7\xE3o m\xEDnima da pausa antes de enviar a fala capturada para reconhecimento.",
|
|
2205
2210
|
"dsh-live-voice.recognition.silenceDetection.label": "Detec\xE7\xE3o de sil\xEAncio",
|
|
@@ -2236,6 +2241,9 @@ var ptBR = {
|
|
|
2236
2241
|
"dsh-live-voice.settings.delivery.toggle": "Modo de entrega autom\xE1tica",
|
|
2237
2242
|
"dsh-live-voice.settings.engine.refresh": "Atualizar mecanismos dispon\xEDveis",
|
|
2238
2243
|
"dsh-live-voice.settings.filters.title": "Filtragem",
|
|
2244
|
+
"dsh-live-voice.settings.general.title": "Gerais",
|
|
2245
|
+
"dsh-live-voice.settings.persistence.loadError": "N\xE3o foi poss\xEDvel carregar as configura\xE7\xF5es do Live Voice do servidor. Recarregue para tentar novamente.",
|
|
2246
|
+
"dsh-live-voice.settings.persistence.saveError": "N\xE3o foi poss\xEDvel salvar as configura\xE7\xF5es do Live Voice no servidor. Tente novamente.",
|
|
2239
2247
|
"dsh-live-voice.settings.tabs.conversation": "Conversa",
|
|
2240
2248
|
"dsh-live-voice.settings.tabs.recognition": "Reconhecimento de voz",
|
|
2241
2249
|
"dsh-live-voice.settings.tabs.speak": "Fala",
|
|
@@ -2404,6 +2412,8 @@ var zh = {
|
|
|
2404
2412
|
"dsh-live-voice.recognition.planned.vote": "\u5373\u5C06\u63A8\u51FA \u2014 \u8BF7\u5728\u4ED3\u5E93\u8BAE\u9898\u4E2D\u6295\u7968",
|
|
2405
2413
|
"dsh-live-voice.recognition.planned.voxtral": "Voxtral Realtime \u2014 \u5373\u5C06\u63A8\u51FA",
|
|
2406
2414
|
"dsh-live-voice.recognition.planned.webGpu": "\u6D4F\u89C8\u5668 WebGPU \u63A8\u7406 \u2014 \u5373\u5C06\u63A8\u51FA",
|
|
2415
|
+
"dsh-live-voice.recognition.presets.custom.description": "\u81EA\u884C\u9009\u62E9\u9759\u97F3\u65F6\u957F\u3002",
|
|
2416
|
+
"dsh-live-voice.recognition.presets.custom.label": "\u81EA\u5B9A\u4E49",
|
|
2407
2417
|
"dsh-live-voice.recognition.presets.long.description": "\u7B49\u5F85\u8F83\u957F\u7684\u601D\u8003\u505C\u987F\u3002",
|
|
2408
2418
|
"dsh-live-voice.recognition.presets.long.label": "\u957F",
|
|
2409
2419
|
"dsh-live-voice.recognition.presets.natural.description": "\u5141\u8BB8\u77ED\u8BED\u4E4B\u95F4\u51FA\u73B0\u81EA\u7136\u505C\u987F\u3002",
|
|
@@ -2416,6 +2426,8 @@ var zh = {
|
|
|
2416
2426
|
"dsh-live-voice.recognition.qwen.endpointHelp": "HTTP API \u4F4D\u4E8E\u914D\u7F6E\u7684 DSH \u4E3B\u673A URL\uFF08\u9ED8\u8BA4\uFF1Ahttp://127.0.0.1:8080/inference\uFF09\u3002\u97F3\u9891\u901A\u8FC7\u9700\u8981\u8EAB\u4EFD\u9A8C\u8BC1\u7684 DSH \u4E3B\u673A\u8F6C\u5199\u8DEF\u7531\u53D1\u9001\u3002",
|
|
2417
2427
|
"dsh-live-voice.recognition.qwen.hostHelp": "Qwen3 ASR + TTS \u670D\u52A1\u5668\u7684\u4E3B\u673A\u7EA7\u8BBE\u7F6E\u3002\u8BF7\u8F93\u5165 DSH \u4E3B\u673A\u53EF\u8BBF\u95EE\u7684\u4EFB\u610F HTTP \u6216 HTTPS \u57FA\u7840 URL\u3002\u6D4F\u89C8\u5668\u901A\u8FC7\u9700\u8981\u8EAB\u4EFD\u9A8C\u8BC1\u7684 DSH \u8DEF\u7531\u8BBF\u95EE\u8BE5\u670D\u52A1\u5668\u3002",
|
|
2418
2428
|
"dsh-live-voice.recognition.qwen.label": "Qwen3 ASR \u2014 HTTP API",
|
|
2429
|
+
"dsh-live-voice.recognition.silenceDetection.customHelp": "\u8BF7\u8F93\u5165 100 \u81F3 10,000 ms \u7684\u6574\u6570\u3002\u79BB\u5F00\u8F93\u5165\u6846\u65F6\u4FDD\u5B58\u3002\u77ED\u505C\u987F\u53EF\u80FD\u5207\u5206\u8BED\u97F3\uFF1B\u8BC6\u522B\u8FD8\u9700\u8981\u989D\u5916\u65F6\u95F4\u3002",
|
|
2430
|
+
"dsh-live-voice.recognition.silenceDetection.customLabel": "\u81EA\u5B9A\u4E49\u505C\u987F\uFF08\u6BEB\u79D2\uFF09",
|
|
2419
2431
|
"dsh-live-voice.recognition.silenceDetection.duration": "\u53D1\u9001\u524D\u505C\u987F\uFF1A{milliseconds} \u6BEB\u79D2",
|
|
2420
2432
|
"dsh-live-voice.recognition.silenceDetection.help": "\u8BBE\u7F6E\u505C\u987F\u6301\u7EED\u591A\u4E45\u540E\uFF0C\u5C06\u91C7\u96C6\u7684\u8BED\u97F3\u53D1\u9001\u8FDB\u884C\u8BC6\u522B\u3002",
|
|
2421
2433
|
"dsh-live-voice.recognition.silenceDetection.label": "\u9759\u97F3\u68C0\u6D4B",
|
|
@@ -2452,6 +2464,9 @@ var zh = {
|
|
|
2452
2464
|
"dsh-live-voice.settings.delivery.toggle": "\u81EA\u52A8\u53D1\u9001\u6A21\u5F0F",
|
|
2453
2465
|
"dsh-live-voice.settings.engine.refresh": "\u5237\u65B0\u53EF\u7528\u5F15\u64CE",
|
|
2454
2466
|
"dsh-live-voice.settings.filters.title": "\u8FC7\u6EE4",
|
|
2467
|
+
"dsh-live-voice.settings.general.title": "\u5E38\u89C4",
|
|
2468
|
+
"dsh-live-voice.settings.persistence.loadError": "\u65E0\u6CD5\u4ECE\u670D\u52A1\u5668\u52A0\u8F7D Live Voice \u8BBE\u7F6E\u3002\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5\u3002",
|
|
2469
|
+
"dsh-live-voice.settings.persistence.saveError": "\u65E0\u6CD5\u5728\u670D\u52A1\u5668\u4E0A\u4FDD\u5B58 Live Voice \u8BBE\u7F6E\u3002\u8BF7\u91CD\u8BD5\u3002",
|
|
2455
2470
|
"dsh-live-voice.settings.tabs.conversation": "\u5BF9\u8BDD",
|
|
2456
2471
|
"dsh-live-voice.settings.tabs.recognition": "\u8BED\u97F3\u8BC6\u522B",
|
|
2457
2472
|
"dsh-live-voice.settings.tabs.speak": "\u8BED\u97F3\u5408\u6210",
|
|
@@ -2741,6 +2756,10 @@ var iconPaths = {
|
|
|
2741
2756
|
skipNext: "M5 5l10 7-10 7V5M19 5v14",
|
|
2742
2757
|
send: "M3 11.5L21 3l-8.5 18-2-7.5L3 11.5zm7.5 2L21 3",
|
|
2743
2758
|
queue: "M5 6h14M5 12h10M5 18h6M18 15v6M15 18h6",
|
|
2759
|
+
settings: "M4 7h16M4 17h16M8 4v6M16 14v6",
|
|
2760
|
+
filter: "M3 5h18l-7 8v6l-4 2v-8L3 5z",
|
|
2761
|
+
link: "M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1.7 1.7M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1.7-1.7",
|
|
2762
|
+
chevron: "M6 9l6 6 6-6",
|
|
2744
2763
|
close: "M6 6l12 12M18 6 6 18"
|
|
2745
2764
|
};
|
|
2746
2765
|
|
|
@@ -2821,56 +2840,113 @@ function CheckboxField({
|
|
|
2821
2840
|
}
|
|
2822
2841
|
|
|
2823
2842
|
// src/shared/design-system/forms/NumberField.tsx
|
|
2843
|
+
var import_react12 = __toESM(require("react"), 1);
|
|
2844
|
+
|
|
2845
|
+
// src/shared/design-system/forms/DraftField.tsx
|
|
2824
2846
|
var import_react11 = __toESM(require("react"), 1);
|
|
2825
|
-
function
|
|
2826
|
-
|
|
2847
|
+
function DraftField({
|
|
2848
|
+
label,
|
|
2849
|
+
value,
|
|
2850
|
+
defaultValue,
|
|
2851
|
+
onCommit,
|
|
2852
|
+
multiline,
|
|
2853
|
+
onInput,
|
|
2854
|
+
onChange,
|
|
2855
|
+
onFocus,
|
|
2856
|
+
onBlur,
|
|
2857
|
+
...props
|
|
2858
|
+
}) {
|
|
2859
|
+
const saved = String(value ?? defaultValue ?? "");
|
|
2860
|
+
const [draft, setDraft] = import_react11.default.useState(saved);
|
|
2861
|
+
const editing = import_react11.default.useRef(false);
|
|
2862
|
+
const dirty = import_react11.default.useRef(false);
|
|
2863
|
+
import_react11.default.useEffect(() => {
|
|
2864
|
+
if (!editing.current) setDraft(saved);
|
|
2865
|
+
}, [saved]);
|
|
2866
|
+
const inputProps = {
|
|
2867
|
+
...props,
|
|
2868
|
+
value: draft,
|
|
2869
|
+
onFocus: (event) => {
|
|
2870
|
+
editing.current = true;
|
|
2871
|
+
onFocus?.(event);
|
|
2872
|
+
},
|
|
2873
|
+
onInput: (event) => {
|
|
2874
|
+
editing.current = true;
|
|
2875
|
+
dirty.current = true;
|
|
2876
|
+
setDraft(event.currentTarget.value);
|
|
2877
|
+
onInput?.(event);
|
|
2878
|
+
},
|
|
2879
|
+
onChange: (event) => {
|
|
2880
|
+
editing.current = true;
|
|
2881
|
+
dirty.current = true;
|
|
2882
|
+
setDraft(event.currentTarget.value);
|
|
2883
|
+
onChange?.(event);
|
|
2884
|
+
},
|
|
2885
|
+
onBlur: (event) => {
|
|
2886
|
+
editing.current = false;
|
|
2887
|
+
const raw = event.currentTarget.value;
|
|
2888
|
+
const changed = dirty.current;
|
|
2889
|
+
dirty.current = false;
|
|
2890
|
+
setDraft(saved);
|
|
2891
|
+
const valid = multiline || props.type !== "number" || raw.trim() !== "" && event.currentTarget.validity.valid;
|
|
2892
|
+
if (changed && valid) onCommit?.(raw);
|
|
2893
|
+
onBlur?.(event);
|
|
2894
|
+
}
|
|
2895
|
+
};
|
|
2896
|
+
return /* @__PURE__ */ import_react11.default.createElement("label", null, label, multiline ? /* @__PURE__ */ import_react11.default.createElement("textarea", { ...inputProps }) : /* @__PURE__ */ import_react11.default.createElement("input", { ...inputProps }));
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2899
|
+
// src/shared/design-system/forms/NumberField.tsx
|
|
2900
|
+
function NumberField(props) {
|
|
2901
|
+
return /* @__PURE__ */ import_react12.default.createElement(DraftField, { ...props, type: "number" });
|
|
2827
2902
|
}
|
|
2828
2903
|
|
|
2829
2904
|
// src/shared/design-system/forms/SelectField.tsx
|
|
2830
|
-
var
|
|
2905
|
+
var import_react13 = __toESM(require("react"), 1);
|
|
2831
2906
|
function SelectField({ label, options, ...props }) {
|
|
2832
|
-
return /* @__PURE__ */
|
|
2907
|
+
return /* @__PURE__ */ import_react13.default.createElement("label", null, label, /* @__PURE__ */ import_react13.default.createElement("select", { ...props }, options.map((option) => /* @__PURE__ */ import_react13.default.createElement("option", { key: option.value, value: option.value, disabled: option.disabled }, option.label))));
|
|
2833
2908
|
}
|
|
2834
2909
|
|
|
2835
2910
|
// src/shared/design-system/forms/TextAreaField.tsx
|
|
2836
|
-
var
|
|
2837
|
-
function TextAreaField(
|
|
2838
|
-
return /* @__PURE__ */
|
|
2911
|
+
var import_react14 = __toESM(require("react"), 1);
|
|
2912
|
+
function TextAreaField(props) {
|
|
2913
|
+
return /* @__PURE__ */ import_react14.default.createElement(DraftField, { ...props, multiline: true });
|
|
2839
2914
|
}
|
|
2840
2915
|
|
|
2841
2916
|
// src/shared/design-system/forms/TextField.tsx
|
|
2842
|
-
var
|
|
2843
|
-
function TextField(
|
|
2844
|
-
return /* @__PURE__ */
|
|
2917
|
+
var import_react15 = __toESM(require("react"), 1);
|
|
2918
|
+
function TextField(props) {
|
|
2919
|
+
return /* @__PURE__ */ import_react15.default.createElement(DraftField, { ...props, type: "text" });
|
|
2845
2920
|
}
|
|
2846
2921
|
|
|
2847
2922
|
// src/shared/design-system/layout/SettingsCard.tsx
|
|
2848
|
-
var
|
|
2923
|
+
var import_react16 = __toESM(require("react"), 1);
|
|
2849
2924
|
function SettingsCard({
|
|
2850
2925
|
children,
|
|
2851
2926
|
className = ""
|
|
2852
2927
|
}) {
|
|
2853
|
-
return /* @__PURE__ */
|
|
2928
|
+
return /* @__PURE__ */ import_react16.default.createElement("div", { className: ["dlv-settings-card", className].filter(Boolean).join(" ") }, children);
|
|
2854
2929
|
}
|
|
2855
2930
|
|
|
2856
2931
|
// src/shared/design-system/layout/SettingsSection.tsx
|
|
2857
|
-
var
|
|
2932
|
+
var import_react17 = __toESM(require("react"), 1);
|
|
2858
2933
|
|
|
2859
2934
|
// src/shared/design-system/layout/SettingsSubcard.tsx
|
|
2860
|
-
var
|
|
2935
|
+
var import_react18 = __toESM(require("react"), 1);
|
|
2861
2936
|
function SettingsSubcard({
|
|
2862
2937
|
title,
|
|
2863
2938
|
children,
|
|
2864
2939
|
open = false,
|
|
2865
|
-
ariaLabel
|
|
2940
|
+
ariaLabel,
|
|
2941
|
+
icon = "settings"
|
|
2866
2942
|
}) {
|
|
2867
|
-
return /* @__PURE__ */
|
|
2943
|
+
return /* @__PURE__ */ import_react18.default.createElement("details", { className: "dlv-settings-subcard", open }, /* @__PURE__ */ import_react18.default.createElement("summary", { "aria-label": ariaLabel }, /* @__PURE__ */ import_react18.default.createElement(Icon, { name: icon, className: "dlv-settings-subcard-icon" }), /* @__PURE__ */ import_react18.default.createElement("span", null, title), /* @__PURE__ */ import_react18.default.createElement(Icon, { name: "chevron", className: "dlv-settings-subcard-chevron" })), /* @__PURE__ */ import_react18.default.createElement("div", { className: "dlv-settings-subcard-body" }, children));
|
|
2868
2944
|
}
|
|
2869
2945
|
|
|
2870
2946
|
// src/shared/design-system/layout/SettingsTabs.tsx
|
|
2871
|
-
var
|
|
2947
|
+
var import_react19 = __toESM(require("react"), 1);
|
|
2872
2948
|
function SettingsTabs({ label, tabs, active, idPrefix, onChange }) {
|
|
2873
|
-
const refs =
|
|
2949
|
+
const refs = import_react19.default.useRef([]);
|
|
2874
2950
|
const activate = (index) => {
|
|
2875
2951
|
const tab = tabs[index];
|
|
2876
2952
|
if (!tab) return;
|
|
@@ -2887,7 +2963,7 @@ function SettingsTabs({ label, tabs, active, idPrefix, onChange }) {
|
|
|
2887
2963
|
event.preventDefault();
|
|
2888
2964
|
activate(next);
|
|
2889
2965
|
};
|
|
2890
|
-
return /* @__PURE__ */
|
|
2966
|
+
return /* @__PURE__ */ import_react19.default.createElement("div", { className: "dlv-settings-tabs", role: "tablist", "aria-label": label }, tabs.map((tab, index) => /* @__PURE__ */ import_react19.default.createElement(
|
|
2891
2967
|
"button",
|
|
2892
2968
|
{
|
|
2893
2969
|
key: tab.id,
|
|
@@ -2905,7 +2981,8 @@ function SettingsTabs({ label, tabs, active, idPrefix, onChange }) {
|
|
|
2905
2981
|
onClick: () => onChange(tab.id),
|
|
2906
2982
|
onKeyDown: (event) => onKeyDown(event, index)
|
|
2907
2983
|
},
|
|
2908
|
-
tab.
|
|
2984
|
+
tab.icon ? /* @__PURE__ */ import_react19.default.createElement(Icon, { name: tab.icon, className: "dlv-settings-tab-icon" }) : null,
|
|
2985
|
+
/* @__PURE__ */ import_react19.default.createElement("span", null, tab.label)
|
|
2909
2986
|
)));
|
|
2910
2987
|
}
|
|
2911
2988
|
|
|
@@ -3265,7 +3342,8 @@ var WhisperHttpRecognitionEngine = class {
|
|
|
3265
3342
|
constructor({
|
|
3266
3343
|
globals = globalThis,
|
|
3267
3344
|
meter,
|
|
3268
|
-
voiceDetectionPreset =
|
|
3345
|
+
voiceDetectionPreset = defaultSettings.voiceDetectionPreset,
|
|
3346
|
+
voiceDetectionCustomSilenceMs = defaultSettings.voiceDetectionCustomSilenceMs,
|
|
3269
3347
|
maxUtteranceSeconds = 60
|
|
3270
3348
|
} = {}) {
|
|
3271
3349
|
this.g = globals;
|
|
@@ -3273,11 +3351,12 @@ var WhisperHttpRecognitionEngine = class {
|
|
|
3273
3351
|
this.session = null;
|
|
3274
3352
|
this.lang = "pt-BR";
|
|
3275
3353
|
this.voiceDetectionPreset = voiceDetectionPreset;
|
|
3354
|
+
this.voiceDetectionCustomSilenceMs = voiceDetectionCustomSilenceMs;
|
|
3276
3355
|
this.maxUtteranceSeconds = maxUtteranceSeconds;
|
|
3277
3356
|
this.route = ROUTE;
|
|
3278
3357
|
}
|
|
3279
3358
|
get segmentation() {
|
|
3280
|
-
return
|
|
3359
|
+
return { silenceMs: voiceDetectionSilenceMs(this) };
|
|
3281
3360
|
}
|
|
3282
3361
|
async capability() {
|
|
3283
3362
|
try {
|
|
@@ -3775,16 +3854,16 @@ var QwenHttpSpeakingEngine = class extends HostAudioSpeakingEngine {
|
|
|
3775
3854
|
};
|
|
3776
3855
|
|
|
3777
3856
|
// src/modules/settings/components/createLiveVoiceSettings.tsx
|
|
3778
|
-
var
|
|
3857
|
+
var import_react46 = __toESM(require("react"), 1);
|
|
3779
3858
|
|
|
3780
3859
|
// src/modules/settings/components/LiveVoiceSettings.tsx
|
|
3781
|
-
var
|
|
3860
|
+
var import_react45 = __toESM(require("react"), 1);
|
|
3782
3861
|
|
|
3783
3862
|
// src/modules/settings/hooks/useAudioDevices.ts
|
|
3784
|
-
var
|
|
3863
|
+
var import_react21 = __toESM(require("react"), 1);
|
|
3785
3864
|
function useAudioDevices() {
|
|
3786
|
-
const [audioDevices, setAudioDevices] =
|
|
3787
|
-
|
|
3865
|
+
const [audioDevices, setAudioDevices] = import_react21.default.useState([]);
|
|
3866
|
+
import_react21.default.useEffect(() => {
|
|
3788
3867
|
let active = true;
|
|
3789
3868
|
const refresh = async () => {
|
|
3790
3869
|
try {
|
|
@@ -3814,17 +3893,17 @@ function createDeviceOptions(devices, kind, systemDefault, fallback) {
|
|
|
3814
3893
|
}
|
|
3815
3894
|
|
|
3816
3895
|
// src/modules/conversation/hooks/useConversationActions.ts
|
|
3817
|
-
var
|
|
3896
|
+
var import_react22 = __toESM(require("react"), 1);
|
|
3818
3897
|
function useConversationActions(controller) {
|
|
3819
|
-
const [error, setError] =
|
|
3820
|
-
const alive =
|
|
3821
|
-
|
|
3898
|
+
const [error, setError] = import_react22.default.useState("");
|
|
3899
|
+
const alive = import_react22.default.useRef(true);
|
|
3900
|
+
import_react22.default.useEffect(
|
|
3822
3901
|
() => () => {
|
|
3823
3902
|
alive.current = false;
|
|
3824
3903
|
},
|
|
3825
3904
|
[]
|
|
3826
3905
|
);
|
|
3827
|
-
const invoke =
|
|
3906
|
+
const invoke = import_react22.default.useCallback(
|
|
3828
3907
|
(name, ...args) => {
|
|
3829
3908
|
setError("");
|
|
3830
3909
|
try {
|
|
@@ -3837,27 +3916,27 @@ function useConversationActions(controller) {
|
|
|
3837
3916
|
},
|
|
3838
3917
|
[controller]
|
|
3839
3918
|
);
|
|
3840
|
-
const clearError =
|
|
3919
|
+
const clearError = import_react22.default.useCallback(() => setError(""), []);
|
|
3841
3920
|
return { invoke, error, clearError };
|
|
3842
3921
|
}
|
|
3843
3922
|
|
|
3844
3923
|
// src/modules/conversation/hooks/useConversationController.ts
|
|
3845
|
-
var
|
|
3924
|
+
var import_react23 = __toESM(require("react"), 1);
|
|
3846
3925
|
function useConversationController(controller) {
|
|
3847
|
-
const subscribe =
|
|
3926
|
+
const subscribe = import_react23.default.useCallback(
|
|
3848
3927
|
(listener) => controller.subscribe(listener),
|
|
3849
3928
|
[controller]
|
|
3850
3929
|
);
|
|
3851
|
-
const read =
|
|
3852
|
-
return
|
|
3930
|
+
const read = import_react23.default.useCallback(() => controller.getSnapshot(), [controller]);
|
|
3931
|
+
return import_react23.default.useSyncExternalStore(subscribe, read, read);
|
|
3853
3932
|
}
|
|
3854
3933
|
|
|
3855
3934
|
// src/modules/settings/hooks/useReleaseStatus.ts
|
|
3856
|
-
var
|
|
3935
|
+
var import_react24 = __toESM(require("react"), 1);
|
|
3857
3936
|
|
|
3858
3937
|
// src/modules/settings/services/releases.ts
|
|
3859
|
-
var CURRENT_VERSION = "0.3.
|
|
3860
|
-
var TESTED_DSH_VERSION = "0.
|
|
3938
|
+
var CURRENT_VERSION = "0.3.2";
|
|
3939
|
+
var TESTED_DSH_VERSION = "0.2.0-rc.2";
|
|
3861
3940
|
var REPOSITORY_URL = "https://github.com/victorwads/dsh-live-voice";
|
|
3862
3941
|
var RELEASES_URL = `${REPOSITORY_URL}/releases`;
|
|
3863
3942
|
var LATEST_RELEASE_API_URL = "https://api.github.com/repos/victorwads/dsh-live-voice/releases/latest";
|
|
@@ -3865,7 +3944,7 @@ var RELEASE_CHECK_STORAGE_KEY = "dsh-live-voice.latest-release-check";
|
|
|
3865
3944
|
var RELEASE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
3866
3945
|
var LIVE_VOICE_BADGE_URL = "https://cdn.simpleicons.org/npm/white";
|
|
3867
3946
|
var DSH_BADGE_URL = "https://cdn.simpleicons.org/deepseek/white";
|
|
3868
|
-
var TESTED_DSH_RELEASE_URL = `https://github.com/deepseek-ai/deepseek-harness/releases/tag/v${TESTED_DSH_VERSION}`;
|
|
3947
|
+
var TESTED_DSH_RELEASE_URL = `https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v${TESTED_DSH_VERSION}`;
|
|
3869
3948
|
function versionParts(version) {
|
|
3870
3949
|
const normalized = String(version || "").trim().replace(/^v/i, "").split("+", 1)[0];
|
|
3871
3950
|
const [core = "", prerelease = ""] = normalized.split("-", 2);
|
|
@@ -3917,9 +3996,16 @@ function writeCached(storage, value) {
|
|
|
3917
3996
|
} catch {
|
|
3918
3997
|
}
|
|
3919
3998
|
}
|
|
3999
|
+
var releaseMemory = /* @__PURE__ */ new Map();
|
|
4000
|
+
var memoryStorage = {
|
|
4001
|
+
getItem: (key) => releaseMemory.get(key) ?? null,
|
|
4002
|
+
setItem: (key, value) => {
|
|
4003
|
+
releaseMemory.set(key, value);
|
|
4004
|
+
}
|
|
4005
|
+
};
|
|
3920
4006
|
async function checkLatestRelease({
|
|
3921
4007
|
fetchImpl = globalThis.window?.fetch?.bind(globalThis.window),
|
|
3922
|
-
storage =
|
|
4008
|
+
storage = memoryStorage,
|
|
3923
4009
|
now = Date.now()
|
|
3924
4010
|
} = {}) {
|
|
3925
4011
|
const cached = readCached(storage, now);
|
|
@@ -3951,10 +4037,10 @@ function hasNewerRelease(release, currentVersion = CURRENT_VERSION) {
|
|
|
3951
4037
|
|
|
3952
4038
|
// src/modules/settings/hooks/useReleaseStatus.ts
|
|
3953
4039
|
function useReleaseStatus() {
|
|
3954
|
-
const [latestRelease, setLatestRelease] =
|
|
4040
|
+
const [latestRelease, setLatestRelease] = import_react24.default.useState(
|
|
3955
4041
|
null
|
|
3956
4042
|
);
|
|
3957
|
-
|
|
4043
|
+
import_react24.default.useEffect(() => {
|
|
3958
4044
|
let active = true;
|
|
3959
4045
|
void checkLatestRelease().then((result) => {
|
|
3960
4046
|
if (active) setLatestRelease(result.release);
|
|
@@ -3967,10 +4053,10 @@ function useReleaseStatus() {
|
|
|
3967
4053
|
}
|
|
3968
4054
|
|
|
3969
4055
|
// src/modules/settings/sections/conversation/ConversationSettingsSection.tsx
|
|
3970
|
-
var
|
|
4056
|
+
var import_react30 = __toESM(require("react"), 1);
|
|
3971
4057
|
|
|
3972
4058
|
// src/modules/settings/sections/conversation/DeliverySettings.tsx
|
|
3973
|
-
var
|
|
4059
|
+
var import_react25 = __toESM(require("react"), 1);
|
|
3974
4060
|
function DeliverySettings({
|
|
3975
4061
|
settings,
|
|
3976
4062
|
updateSettings
|
|
@@ -3978,7 +4064,7 @@ function DeliverySettings({
|
|
|
3978
4064
|
const { scoped } = useLanguage((ctx) => ctx.settings);
|
|
3979
4065
|
const mode = settings.sendingMode || "manual";
|
|
3980
4066
|
const seconds = [2, 3, 4, 5, 6, 8, 10];
|
|
3981
|
-
return /* @__PURE__ */
|
|
4067
|
+
return /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, /* @__PURE__ */ import_react25.default.createElement(
|
|
3982
4068
|
SelectField,
|
|
3983
4069
|
{
|
|
3984
4070
|
label: scoped.delivery.label(),
|
|
@@ -3990,7 +4076,7 @@ function DeliverySettings({
|
|
|
3990
4076
|
],
|
|
3991
4077
|
onChange: (event) => updateSettings({ sendingMode: event.target.value })
|
|
3992
4078
|
}
|
|
3993
|
-
), mode !== "manual" ? /* @__PURE__ */
|
|
4079
|
+
), mode !== "manual" ? /* @__PURE__ */ import_react25.default.createElement(
|
|
3994
4080
|
SelectField,
|
|
3995
4081
|
{
|
|
3996
4082
|
label: scoped.autoSend.delay(),
|
|
@@ -3998,17 +4084,17 @@ function DeliverySettings({
|
|
|
3998
4084
|
options: seconds.map((value) => ({ value: String(value), label: String(value) })),
|
|
3999
4085
|
onChange: (event) => updateSettings({ autoSendDelaySeconds: Number(event.target.value) })
|
|
4000
4086
|
}
|
|
4001
|
-
) : /* @__PURE__ */
|
|
4087
|
+
) : /* @__PURE__ */ import_react25.default.createElement("p", { className: "dlv-setting-description" }, scoped.delivery.manualLabel()));
|
|
4002
4088
|
}
|
|
4003
4089
|
|
|
4004
4090
|
// src/modules/settings/sections/conversation/HoldToTalkSettings.tsx
|
|
4005
|
-
var
|
|
4091
|
+
var import_react26 = __toESM(require("react"), 1);
|
|
4006
4092
|
function HoldToTalkSettings({
|
|
4007
4093
|
settings,
|
|
4008
4094
|
updateSettings
|
|
4009
4095
|
}) {
|
|
4010
4096
|
const { scoped: recognition } = useLanguage((ctx) => ctx.recognition);
|
|
4011
|
-
return /* @__PURE__ */
|
|
4097
|
+
return /* @__PURE__ */ import_react26.default.createElement(
|
|
4012
4098
|
CheckboxField,
|
|
4013
4099
|
{
|
|
4014
4100
|
label: recognition.holdToTalk.enabled(),
|
|
@@ -4020,14 +4106,14 @@ function HoldToTalkSettings({
|
|
|
4020
4106
|
}
|
|
4021
4107
|
|
|
4022
4108
|
// src/modules/settings/sections/conversation/VoiceModeSettings.tsx
|
|
4023
|
-
var
|
|
4109
|
+
var import_react27 = __toESM(require("react"), 1);
|
|
4024
4110
|
function VoiceModeSettings({
|
|
4025
4111
|
settings,
|
|
4026
4112
|
updateSettings
|
|
4027
4113
|
}) {
|
|
4028
4114
|
const { scoped: recognition } = useLanguage((ctx) => ctx.recognition);
|
|
4029
4115
|
const mode = settings.mode || "speaker";
|
|
4030
|
-
return /* @__PURE__ */
|
|
4116
|
+
return /* @__PURE__ */ import_react27.default.createElement(import_react27.default.Fragment, null, /* @__PURE__ */ import_react27.default.createElement(
|
|
4031
4117
|
SelectField,
|
|
4032
4118
|
{
|
|
4033
4119
|
label: recognition.mode.label(),
|
|
@@ -4038,16 +4124,16 @@ function VoiceModeSettings({
|
|
|
4038
4124
|
],
|
|
4039
4125
|
onChange: (event) => updateSettings({ mode: event.target.value })
|
|
4040
4126
|
}
|
|
4041
|
-
), /* @__PURE__ */
|
|
4127
|
+
), /* @__PURE__ */ import_react27.default.createElement("p", { className: "dlv-setting-description" }, mode === "headphones" ? recognition.headphoneMode.help() : recognition.speakerMode.help()));
|
|
4042
4128
|
}
|
|
4043
4129
|
|
|
4044
4130
|
// src/modules/settings/sections/conversation/ConversationDelaySettings.tsx
|
|
4045
|
-
var
|
|
4131
|
+
var import_react28 = __toESM(require("react"), 1);
|
|
4046
4132
|
var DELAYS = [1, 2, 3, 4, 5, 6, 8, 10];
|
|
4047
4133
|
function ConversationDelaySettings({ settings, updateSettings }) {
|
|
4048
4134
|
const { scoped: commons } = useLanguage((ctx) => ctx.commons);
|
|
4049
4135
|
const { scoped: speak } = useLanguage((ctx) => ctx.speak);
|
|
4050
|
-
return /* @__PURE__ */
|
|
4136
|
+
return /* @__PURE__ */ import_react28.default.createElement(import_react28.default.Fragment, null, /* @__PURE__ */ import_react28.default.createElement(
|
|
4051
4137
|
SelectField,
|
|
4052
4138
|
{
|
|
4053
4139
|
label: speak.responseDelay.label(),
|
|
@@ -4058,40 +4144,41 @@ function ConversationDelaySettings({ settings, updateSettings }) {
|
|
|
4058
4144
|
})),
|
|
4059
4145
|
onChange: (event) => updateSettings({ assistantSpeechDelaySeconds: Number(event.target.value) })
|
|
4060
4146
|
}
|
|
4061
|
-
), /* @__PURE__ */
|
|
4147
|
+
), /* @__PURE__ */ import_react28.default.createElement("p", { className: "dlv-setting-description" }, speak.responseDelay.help()));
|
|
4062
4148
|
}
|
|
4063
4149
|
|
|
4064
4150
|
// src/modules/settings/sections/speak/PlaybackPolicySettings.tsx
|
|
4065
|
-
var
|
|
4151
|
+
var import_react29 = __toESM(require("react"), 1);
|
|
4066
4152
|
function PlaybackPolicySettings({ settings, updateSettings }) {
|
|
4067
4153
|
const { scoped: speak } = useLanguage((ctx) => ctx.speak);
|
|
4068
|
-
return /* @__PURE__ */
|
|
4154
|
+
return /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, /* @__PURE__ */ import_react29.default.createElement(
|
|
4069
4155
|
CheckboxField,
|
|
4070
4156
|
{
|
|
4071
4157
|
label: speak.autoPlayback.label(),
|
|
4072
4158
|
checked: settings.announceAssistantMessages !== false,
|
|
4073
4159
|
onChange: (event) => updateSettings({ announceAssistantMessages: event.target.checked })
|
|
4074
4160
|
}
|
|
4075
|
-
), /* @__PURE__ */
|
|
4161
|
+
), /* @__PURE__ */ import_react29.default.createElement("p", null, speak.autoPlayback.help()), /* @__PURE__ */ import_react29.default.createElement(
|
|
4076
4162
|
CheckboxField,
|
|
4077
4163
|
{
|
|
4078
4164
|
label: speak.interruption.enabled(),
|
|
4079
4165
|
checked: Boolean(settings.interruptSpeechOnUserMessage),
|
|
4080
4166
|
onChange: (event) => updateSettings({ interruptSpeechOnUserMessage: event.target.checked })
|
|
4081
4167
|
}
|
|
4082
|
-
), /* @__PURE__ */
|
|
4168
|
+
), /* @__PURE__ */ import_react29.default.createElement("p", null, settings.interruptSpeechOnUserMessage ? speak.interruption.enabledHelp() : speak.interruption.disabledHelp()));
|
|
4083
4169
|
}
|
|
4084
4170
|
|
|
4085
4171
|
// src/modules/settings/sections/conversation/ConversationSettingsSection.tsx
|
|
4086
4172
|
function ConversationSettingsSection(props) {
|
|
4087
|
-
|
|
4173
|
+
const { scoped: settingsLanguage } = useLanguage((ctx) => ctx.settings);
|
|
4174
|
+
return /* @__PURE__ */ import_react30.default.createElement(SettingsCard, null, /* @__PURE__ */ import_react30.default.createElement("div", { className: "dlv-settings-card-body" }, /* @__PURE__ */ import_react30.default.createElement(SettingsSubcard, { title: settingsLanguage.general.title(), icon: "send" }, /* @__PURE__ */ import_react30.default.createElement("div", { className: "dlv-conversation-fields" }, /* @__PURE__ */ import_react30.default.createElement(DeliverySettings, { ...props }), /* @__PURE__ */ import_react30.default.createElement(HoldToTalkSettings, { ...props }), /* @__PURE__ */ import_react30.default.createElement(VoiceModeSettings, { ...props }), /* @__PURE__ */ import_react30.default.createElement(PlaybackPolicySettings, { ...props }), /* @__PURE__ */ import_react30.default.createElement(ConversationDelaySettings, { ...props })))));
|
|
4088
4175
|
}
|
|
4089
4176
|
|
|
4090
4177
|
// src/modules/settings/sections/recognition/RecognitionEngineSettings.tsx
|
|
4091
|
-
var
|
|
4178
|
+
var import_react33 = __toESM(require("react"), 1);
|
|
4092
4179
|
|
|
4093
4180
|
// src/modules/core/qwen/QwenSettings.tsx
|
|
4094
|
-
var
|
|
4181
|
+
var import_react31 = __toESM(require("react"), 1);
|
|
4095
4182
|
var BASE = "/api/dsh-live-voice/qwen";
|
|
4096
4183
|
var UNLOADED = "dsh-live-voice.speak.qwen.restartRequired";
|
|
4097
4184
|
async function qwenSettingsRequest(path, { method = "GET", config, signal } = {}, fetchImpl = globalThis.fetch) {
|
|
@@ -4120,15 +4207,15 @@ function QwenSettings({ controller }) {
|
|
|
4120
4207
|
const { scoped: commons } = useLanguage((ctx) => ctx.commons);
|
|
4121
4208
|
const { scoped: recognition } = useLanguage((ctx) => ctx.recognition);
|
|
4122
4209
|
const { scoped: speak } = useLanguage((ctx) => ctx.speak);
|
|
4123
|
-
const [draft, setDraft] =
|
|
4210
|
+
const [draft, setDraft] = import_react31.default.useState({
|
|
4124
4211
|
baseUrl: "http://127.0.0.1:8080/",
|
|
4125
4212
|
timeoutMs: 3e5
|
|
4126
4213
|
});
|
|
4127
|
-
const [busy, setBusy] =
|
|
4128
|
-
const [loaded, setLoaded] =
|
|
4129
|
-
const [error, setError] =
|
|
4130
|
-
const [message2, setMessage] =
|
|
4131
|
-
const active =
|
|
4214
|
+
const [busy, setBusy] = import_react31.default.useState(true);
|
|
4215
|
+
const [loaded, setLoaded] = import_react31.default.useState(false);
|
|
4216
|
+
const [error, setError] = import_react31.default.useState("");
|
|
4217
|
+
const [message2, setMessage] = import_react31.default.useState("");
|
|
4218
|
+
const active = import_react31.default.useRef(null);
|
|
4132
4219
|
const translateStatus = (value) => ({
|
|
4133
4220
|
"dsh-live-voice.commons.connection.unsaved": commons.connection.unsaved(),
|
|
4134
4221
|
"dsh-live-voice.speak.qwen.saved": speak.qwen.saved(),
|
|
@@ -4138,7 +4225,7 @@ function QwenSettings({ controller }) {
|
|
|
4138
4225
|
"dsh-live-voice.speak.qwen.requestFailed": speak.qwen.requestFailed(),
|
|
4139
4226
|
"dsh-live-voice.speak.qwen.healthFailed": speak.qwen.healthFailed()
|
|
4140
4227
|
})[value] ?? value;
|
|
4141
|
-
async function run(action) {
|
|
4228
|
+
async function run(action, config = draft) {
|
|
4142
4229
|
active.current?.abort();
|
|
4143
4230
|
const abort = new AbortController();
|
|
4144
4231
|
active.current = abort;
|
|
@@ -4156,7 +4243,7 @@ function QwenSettings({ controller }) {
|
|
|
4156
4243
|
await controller.endConversation?.();
|
|
4157
4244
|
const value = await qwenSettingsRequest("/config", {
|
|
4158
4245
|
method: "PUT",
|
|
4159
|
-
config: { ...
|
|
4246
|
+
config: { ...config, timeoutMs: Number(config.timeoutMs) },
|
|
4160
4247
|
signal: abort.signal
|
|
4161
4248
|
});
|
|
4162
4249
|
if (!abort.signal.aborted) {
|
|
@@ -4167,7 +4254,7 @@ function QwenSettings({ controller }) {
|
|
|
4167
4254
|
} else {
|
|
4168
4255
|
const value = await qwenSettingsRequest("/test", {
|
|
4169
4256
|
method: "POST",
|
|
4170
|
-
config: { ...
|
|
4257
|
+
config: { ...config, timeoutMs: Number(config.timeoutMs) },
|
|
4171
4258
|
signal: abort.signal
|
|
4172
4259
|
});
|
|
4173
4260
|
if (!abort.signal.aborted) {
|
|
@@ -4182,30 +4269,33 @@ function QwenSettings({ controller }) {
|
|
|
4182
4269
|
if (!abort.signal.aborted) setBusy(false);
|
|
4183
4270
|
}
|
|
4184
4271
|
}
|
|
4185
|
-
|
|
4272
|
+
import_react31.default.useEffect(() => {
|
|
4186
4273
|
void run("load");
|
|
4187
4274
|
return () => active.current?.abort();
|
|
4188
4275
|
}, []);
|
|
4189
|
-
const field = (label, key, type = "text") =>
|
|
4190
|
-
"
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4276
|
+
const field = (label, key, type = "text") => {
|
|
4277
|
+
const Field = type === "number" ? NumberField : TextField;
|
|
4278
|
+
return /* @__PURE__ */ import_react31.default.createElement(
|
|
4279
|
+
Field,
|
|
4280
|
+
{
|
|
4281
|
+
label,
|
|
4282
|
+
value: draft[key],
|
|
4283
|
+
disabled: busy || !loaded,
|
|
4284
|
+
autoComplete: "off",
|
|
4285
|
+
...type === "number" ? { min: 1e3, max: 6e5, step: 1 } : {},
|
|
4286
|
+
onCommit: (value) => {
|
|
4287
|
+
const config = { ...draft, [key]: value };
|
|
4288
|
+
setDraft(config);
|
|
4289
|
+
void run("save", config);
|
|
4290
|
+
}
|
|
4201
4291
|
}
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
return /* @__PURE__ */
|
|
4292
|
+
);
|
|
4293
|
+
};
|
|
4294
|
+
return /* @__PURE__ */ import_react31.default.createElement(import_react31.default.Fragment, null, /* @__PURE__ */ import_react31.default.createElement("p", null, recognition.qwen.hostHelp()), field(speak.qwen.endpoint(), "baseUrl"), field(commons.connection.timeout(), "timeoutMs", "number"), /* @__PURE__ */ import_react31.default.createElement("div", { className: "dlv-settings-actions" }, /* @__PURE__ */ import_react31.default.createElement("button", { type: "button", disabled: busy || !loaded, onClick: () => run("save") }, speak.qwen.save()), /* @__PURE__ */ import_react31.default.createElement("button", { type: "button", disabled: busy || !loaded, onClick: () => run("test") }, speak.qwen.test()), /* @__PURE__ */ import_react31.default.createElement("button", { type: "button", disabled: busy, onClick: () => run("load") }, commons.connection.reload())), busy && /* @__PURE__ */ import_react31.default.createElement("p", { role: "status" }, commons.connection.contactingHost()), message2 && /* @__PURE__ */ import_react31.default.createElement("p", { role: "status" }, translateStatus(message2)), error && /* @__PURE__ */ import_react31.default.createElement("p", { role: "alert" }, translateStatus(error)));
|
|
4205
4295
|
}
|
|
4206
4296
|
|
|
4207
4297
|
// src/modules/recognition/engines/whisper/WhisperSettings.tsx
|
|
4208
|
-
var
|
|
4298
|
+
var import_react32 = __toESM(require("react"), 1);
|
|
4209
4299
|
var BASE2 = "/api/dsh-live-voice/whisper";
|
|
4210
4300
|
var UNLOADED2 = "dsh-live-voice.recognition.whisper.restartRequired";
|
|
4211
4301
|
async function whisperSettingsRequest(path, { method = "GET", config, signal } = {}, fetchImpl = globalThis.fetch) {
|
|
@@ -4234,16 +4324,16 @@ function WhisperSettings({ controller }) {
|
|
|
4234
4324
|
const { scoped: commons } = useLanguage((ctx) => ctx.commons);
|
|
4235
4325
|
const { scoped: recognition } = useLanguage((ctx) => ctx.recognition);
|
|
4236
4326
|
const { scoped: settings } = useLanguage((ctx) => ctx.settings);
|
|
4237
|
-
const [draft, setDraft] =
|
|
4327
|
+
const [draft, setDraft] = import_react32.default.useState({
|
|
4238
4328
|
url: "http://127.0.0.1:8080/inference",
|
|
4239
4329
|
healthUrl: "/health",
|
|
4240
4330
|
timeoutMs: 3e4
|
|
4241
4331
|
});
|
|
4242
|
-
const [busy, setBusy] =
|
|
4243
|
-
const [loaded, setLoaded] =
|
|
4244
|
-
const [error, setError] =
|
|
4245
|
-
const [message2, setMessage] =
|
|
4246
|
-
const active =
|
|
4332
|
+
const [busy, setBusy] = import_react32.default.useState(true);
|
|
4333
|
+
const [loaded, setLoaded] = import_react32.default.useState(false);
|
|
4334
|
+
const [error, setError] = import_react32.default.useState("");
|
|
4335
|
+
const [message2, setMessage] = import_react32.default.useState("");
|
|
4336
|
+
const active = import_react32.default.useRef(null);
|
|
4247
4337
|
const translateStatus = (value) => ({
|
|
4248
4338
|
"dsh-live-voice.commons.connection.unsaved": commons.connection.unsaved(),
|
|
4249
4339
|
"dsh-live-voice.recognition.whisper.saved": recognition.whisper.saved(),
|
|
@@ -4253,7 +4343,7 @@ function WhisperSettings({ controller }) {
|
|
|
4253
4343
|
"dsh-live-voice.recognition.whisper.requestFailed": recognition.whisper.requestFailed(),
|
|
4254
4344
|
"dsh-live-voice.recognition.whisper.healthFailed": recognition.whisper.healthFailed()
|
|
4255
4345
|
})[value] ?? value;
|
|
4256
|
-
async function run(action) {
|
|
4346
|
+
async function run(action, config = draft) {
|
|
4257
4347
|
active.current?.abort();
|
|
4258
4348
|
const abort = new AbortController();
|
|
4259
4349
|
active.current = abort;
|
|
@@ -4271,7 +4361,7 @@ function WhisperSettings({ controller }) {
|
|
|
4271
4361
|
await controller.endConversation?.();
|
|
4272
4362
|
const value = await whisperSettingsRequest("/config", {
|
|
4273
4363
|
method: "PUT",
|
|
4274
|
-
config: { ...
|
|
4364
|
+
config: { ...config, timeoutMs: Number(config.timeoutMs) },
|
|
4275
4365
|
signal: abort.signal
|
|
4276
4366
|
});
|
|
4277
4367
|
if (!abort.signal.aborted) {
|
|
@@ -4282,7 +4372,7 @@ function WhisperSettings({ controller }) {
|
|
|
4282
4372
|
} else {
|
|
4283
4373
|
const value = await whisperSettingsRequest("/test", {
|
|
4284
4374
|
method: "POST",
|
|
4285
|
-
config: { ...
|
|
4375
|
+
config: { ...config, timeoutMs: Number(config.timeoutMs) },
|
|
4286
4376
|
signal: abort.signal
|
|
4287
4377
|
});
|
|
4288
4378
|
if (!abort.signal.aborted) {
|
|
@@ -4297,28 +4387,29 @@ function WhisperSettings({ controller }) {
|
|
|
4297
4387
|
if (!abort.signal.aborted) setBusy(false);
|
|
4298
4388
|
}
|
|
4299
4389
|
}
|
|
4300
|
-
|
|
4390
|
+
import_react32.default.useEffect(() => {
|
|
4301
4391
|
void run("load");
|
|
4302
4392
|
return () => active.current?.abort();
|
|
4303
4393
|
}, []);
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4394
|
+
const field = (label, key, type = "text") => {
|
|
4395
|
+
const Field = type === "number" ? NumberField : TextField;
|
|
4396
|
+
return /* @__PURE__ */ import_react32.default.createElement(
|
|
4397
|
+
Field,
|
|
4307
4398
|
{
|
|
4308
|
-
|
|
4399
|
+
label,
|
|
4309
4400
|
value: draft[key],
|
|
4310
4401
|
disabled: busy || !loaded,
|
|
4311
4402
|
autoComplete: "off",
|
|
4312
4403
|
...type === "number" ? { min: 100, max: 3e5, step: 1 } : {},
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4404
|
+
onCommit: (value) => {
|
|
4405
|
+
const config = { ...draft, [key]: value };
|
|
4406
|
+
setDraft(config);
|
|
4407
|
+
void run("save", config);
|
|
4317
4408
|
}
|
|
4318
4409
|
}
|
|
4319
|
-
)
|
|
4320
|
-
}
|
|
4321
|
-
return /* @__PURE__ */
|
|
4410
|
+
);
|
|
4411
|
+
};
|
|
4412
|
+
return /* @__PURE__ */ import_react32.default.createElement(import_react32.default.Fragment, null, /* @__PURE__ */ import_react32.default.createElement("p", null, settings.whisper.hostHelp()), field(commons.connection.endpoint(), "url"), field(commons.connection.healthEndpoint(), "healthUrl"), field(commons.connection.timeout(), "timeoutMs", "number"), /* @__PURE__ */ import_react32.default.createElement("div", { className: "dlv-settings-actions" }, /* @__PURE__ */ import_react32.default.createElement("button", { type: "button", disabled: busy || !loaded, onClick: () => run("save") }, recognition.whisper.save()), /* @__PURE__ */ import_react32.default.createElement("button", { type: "button", disabled: busy || !loaded, onClick: () => run("test") }, commons.connection.test()), /* @__PURE__ */ import_react32.default.createElement("button", { type: "button", disabled: busy, onClick: () => run("load") }, commons.connection.reload())), busy && /* @__PURE__ */ import_react32.default.createElement("p", { role: "status" }, commons.connection.contactingHost()), message2 && /* @__PURE__ */ import_react32.default.createElement("p", { role: "status" }, translateStatus(message2)), error && /* @__PURE__ */ import_react32.default.createElement("p", { role: "alert" }, translateStatus(error)));
|
|
4322
4413
|
}
|
|
4323
4414
|
|
|
4324
4415
|
// src/modules/settings/sections/recognition/RecognitionEngineSettings.tsx
|
|
@@ -4335,7 +4426,7 @@ function RecognitionEngineSettings({
|
|
|
4335
4426
|
const setEngine = (next) => updateSettings(
|
|
4336
4427
|
next === "browser" && settings.recognitionLang === "auto" ? { recognitionEngine: next, recognitionLang: "pt-BR" } : { recognitionEngine: next }
|
|
4337
4428
|
);
|
|
4338
|
-
return /* @__PURE__ */
|
|
4429
|
+
return /* @__PURE__ */ import_react33.default.createElement(import_react33.default.Fragment, null, /* @__PURE__ */ import_react33.default.createElement(
|
|
4339
4430
|
SelectField,
|
|
4340
4431
|
{
|
|
4341
4432
|
label: recognition.engine.label(),
|
|
@@ -4359,7 +4450,7 @@ function RecognitionEngineSettings({
|
|
|
4359
4450
|
],
|
|
4360
4451
|
onChange: (event) => setEngine(event.target.value)
|
|
4361
4452
|
}
|
|
4362
|
-
), /* @__PURE__ */
|
|
4453
|
+
), /* @__PURE__ */ import_react33.default.createElement(
|
|
4363
4454
|
SelectField,
|
|
4364
4455
|
{
|
|
4365
4456
|
label: recognition.microphone.device(),
|
|
@@ -4372,7 +4463,7 @@ function RecognitionEngineSettings({
|
|
|
4372
4463
|
),
|
|
4373
4464
|
onChange: (event) => updateSettings({ inputDeviceId: event.target.value })
|
|
4374
4465
|
}
|
|
4375
|
-
), /* @__PURE__ */
|
|
4466
|
+
), /* @__PURE__ */ import_react33.default.createElement(
|
|
4376
4467
|
SelectField,
|
|
4377
4468
|
{
|
|
4378
4469
|
label: recognition.language.label(),
|
|
@@ -4384,29 +4475,29 @@ function RecognitionEngineSettings({
|
|
|
4384
4475
|
],
|
|
4385
4476
|
onChange: (event) => updateSettings({ recognitionLang: event.target.value })
|
|
4386
4477
|
}
|
|
4387
|
-
), engine === "browser" && /* @__PURE__ */
|
|
4478
|
+
), engine === "browser" && /* @__PURE__ */ import_react33.default.createElement(import_react33.default.Fragment, null, /* @__PURE__ */ import_react33.default.createElement("p", null, recognition.browser.help()), /* @__PURE__ */ import_react33.default.createElement(
|
|
4388
4479
|
CheckboxField,
|
|
4389
4480
|
{
|
|
4390
4481
|
label: recognition.browser.localProcessing(),
|
|
4391
4482
|
checked: settings.recognitionProcessLocally !== false,
|
|
4392
4483
|
onChange: (event) => updateSettings({ recognitionProcessLocally: event.target.checked })
|
|
4393
4484
|
}
|
|
4394
|
-
), settings.recognitionProcessLocally !== false ? /* @__PURE__ */
|
|
4485
|
+
), settings.recognitionProcessLocally !== false ? /* @__PURE__ */ import_react33.default.createElement(
|
|
4395
4486
|
CheckboxField,
|
|
4396
4487
|
{
|
|
4397
4488
|
label: recognition.browser.autoInstallPack(),
|
|
4398
4489
|
checked: settings.recognitionAutoInstall !== false,
|
|
4399
4490
|
onChange: (event) => updateSettings({ recognitionAutoInstall: event.target.checked })
|
|
4400
4491
|
}
|
|
4401
|
-
) : /* @__PURE__ */
|
|
4492
|
+
) : /* @__PURE__ */ import_react33.default.createElement("p", { role: "status" }, recognition.browser.remoteServiceWarning())), engine === "whisper-http" && /* @__PURE__ */ import_react33.default.createElement(SettingsSubcard, { title: commons.connection.title(), icon: "link" }, /* @__PURE__ */ import_react33.default.createElement("p", null, recognition.whisper.captureHelp()), /* @__PURE__ */ import_react33.default.createElement(WhisperSettings, { controller })), engine === "qwen-http" && /* @__PURE__ */ import_react33.default.createElement(SettingsSubcard, { title: commons.connection.title(), icon: "link" }, /* @__PURE__ */ import_react33.default.createElement("p", null, recognition.qwen.captureHelp()), settings.engine !== "qwen-http" && /* @__PURE__ */ import_react33.default.createElement(QwenSettings, { controller })));
|
|
4402
4493
|
}
|
|
4403
4494
|
|
|
4404
4495
|
// src/modules/settings/sections/recognition/RecognitionFilterSettings.tsx
|
|
4405
|
-
var
|
|
4496
|
+
var import_react34 = __toESM(require("react"), 1);
|
|
4406
4497
|
function RecognitionFilterSettings({ settings, updateSettings }) {
|
|
4407
4498
|
const { scoped: recognition } = useLanguage((ctx) => ctx.recognition);
|
|
4408
4499
|
const enabled = settings.recognitionFilterEnabled !== false;
|
|
4409
|
-
return /* @__PURE__ */
|
|
4500
|
+
return /* @__PURE__ */ import_react34.default.createElement(SettingsSubcard, { title: recognition.minimumWords.label(), icon: "filter" }, /* @__PURE__ */ import_react34.default.createElement(
|
|
4410
4501
|
CheckboxField,
|
|
4411
4502
|
{
|
|
4412
4503
|
label: recognition.minimumWords.enabled(),
|
|
@@ -4414,7 +4505,7 @@ function RecognitionFilterSettings({ settings, updateSettings }) {
|
|
|
4414
4505
|
checked: enabled,
|
|
4415
4506
|
onChange: (event) => updateSettings({ recognitionFilterEnabled: event.target.checked })
|
|
4416
4507
|
}
|
|
4417
|
-
), /* @__PURE__ */
|
|
4508
|
+
), /* @__PURE__ */ import_react34.default.createElement(
|
|
4418
4509
|
NumberField,
|
|
4419
4510
|
{
|
|
4420
4511
|
label: recognition.minimumWords.label(),
|
|
@@ -4422,8 +4513,8 @@ function RecognitionFilterSettings({ settings, updateSettings }) {
|
|
|
4422
4513
|
max: 20,
|
|
4423
4514
|
value: settings.recognitionMinimumWords ?? 2,
|
|
4424
4515
|
disabled: !enabled,
|
|
4425
|
-
|
|
4426
|
-
const value = Number(
|
|
4516
|
+
onCommit: (raw) => {
|
|
4517
|
+
const value = Number(raw);
|
|
4427
4518
|
if (Number.isInteger(value) && value >= 1 && value <= 20)
|
|
4428
4519
|
updateSettings({ recognitionMinimumWords: value });
|
|
4429
4520
|
}
|
|
@@ -4432,23 +4523,24 @@ function RecognitionFilterSettings({ settings, updateSettings }) {
|
|
|
4432
4523
|
}
|
|
4433
4524
|
|
|
4434
4525
|
// src/modules/settings/sections/recognition/RecognitionSettingsSection.tsx
|
|
4435
|
-
var
|
|
4526
|
+
var import_react38 = __toESM(require("react"), 1);
|
|
4436
4527
|
|
|
4437
4528
|
// src/modules/settings/sections/recognition/SilenceDetectionSettings.tsx
|
|
4438
|
-
var
|
|
4529
|
+
var import_react35 = __toESM(require("react"), 1);
|
|
4439
4530
|
function SilenceDetectionSettings({ settings, updateSettings }) {
|
|
4440
4531
|
const { scoped: recognition } = useLanguage((ctx) => ctx.recognition);
|
|
4532
|
+
const savedCustomMs = normalizeCustomSilenceMs(settings.voiceDetectionCustomSilenceMs);
|
|
4441
4533
|
if (!usesPluginVoiceDetection(settings.recognitionEngine)) return null;
|
|
4442
|
-
const selected = settings.voiceDetectionPreset ||
|
|
4443
|
-
return /* @__PURE__ */
|
|
4534
|
+
const selected = settings.voiceDetectionPreset || defaultSettings.voiceDetectionPreset;
|
|
4535
|
+
return /* @__PURE__ */ import_react35.default.createElement(
|
|
4444
4536
|
SettingsSubcard,
|
|
4445
4537
|
{
|
|
4446
4538
|
title: recognition.silenceDetection.label(),
|
|
4447
4539
|
ariaLabel: recognition.silenceDetection.title(),
|
|
4448
|
-
|
|
4540
|
+
icon: "pause"
|
|
4449
4541
|
},
|
|
4450
|
-
/* @__PURE__ */
|
|
4451
|
-
/* @__PURE__ */
|
|
4542
|
+
/* @__PURE__ */ import_react35.default.createElement("p", null, recognition.silenceDetection.help()),
|
|
4543
|
+
/* @__PURE__ */ import_react35.default.createElement(
|
|
4452
4544
|
NumberField,
|
|
4453
4545
|
{
|
|
4454
4546
|
label: recognition.maxUtterance.label(),
|
|
@@ -4456,22 +4548,22 @@ function SilenceDetectionSettings({ settings, updateSettings }) {
|
|
|
4456
4548
|
max: 300,
|
|
4457
4549
|
step: 1,
|
|
4458
4550
|
value: settings.recognitionMaxUtteranceSeconds ?? 60,
|
|
4459
|
-
|
|
4460
|
-
const value = Number(
|
|
4551
|
+
onCommit: (raw) => {
|
|
4552
|
+
const value = Number(raw);
|
|
4461
4553
|
if (Number.isInteger(value) && value >= 10 && value <= 300)
|
|
4462
4554
|
updateSettings({ recognitionMaxUtteranceSeconds: value });
|
|
4463
4555
|
}
|
|
4464
4556
|
}
|
|
4465
4557
|
),
|
|
4466
|
-
/* @__PURE__ */
|
|
4467
|
-
/* @__PURE__ */
|
|
4558
|
+
/* @__PURE__ */ import_react35.default.createElement("small", null, recognition.maxUtterance.help()),
|
|
4559
|
+
/* @__PURE__ */ import_react35.default.createElement(
|
|
4468
4560
|
"div",
|
|
4469
4561
|
{
|
|
4470
4562
|
className: "dlv-preset-group",
|
|
4471
4563
|
role: "radiogroup",
|
|
4472
4564
|
"aria-label": recognition.silenceDetection.pauseLabel()
|
|
4473
4565
|
},
|
|
4474
|
-
Object.
|
|
4566
|
+
[...Object.keys(voiceDetectionPresets), "custom"].map((value) => /* @__PURE__ */ import_react35.default.createElement("label", { key: value, className: "dlv-preset" }, /* @__PURE__ */ import_react35.default.createElement(
|
|
4475
4567
|
"input",
|
|
4476
4568
|
{
|
|
4477
4569
|
type: "radio",
|
|
@@ -4480,16 +4572,31 @@ function SilenceDetectionSettings({ settings, updateSettings }) {
|
|
|
4480
4572
|
checked: selected === value,
|
|
4481
4573
|
onChange: () => updateSettings({ voiceDetectionPreset: value })
|
|
4482
4574
|
}
|
|
4483
|
-
), /* @__PURE__ */
|
|
4575
|
+
), /* @__PURE__ */ import_react35.default.createElement("span", null, /* @__PURE__ */ import_react35.default.createElement("strong", null, recognition.presets[value].label()), /* @__PURE__ */ import_react35.default.createElement("small", null, recognition.presets[value].description()))))
|
|
4484
4576
|
),
|
|
4485
|
-
/* @__PURE__ */
|
|
4486
|
-
|
|
4577
|
+
selected === "custom" && /* @__PURE__ */ import_react35.default.createElement(import_react35.default.Fragment, null, /* @__PURE__ */ import_react35.default.createElement(
|
|
4578
|
+
NumberField,
|
|
4579
|
+
{
|
|
4580
|
+
label: recognition.silenceDetection.customLabel(),
|
|
4581
|
+
min: customSilenceMinMs,
|
|
4582
|
+
max: customSilenceMaxMs,
|
|
4583
|
+
step: 1,
|
|
4584
|
+
value: savedCustomMs,
|
|
4585
|
+
onCommit: (draft) => {
|
|
4586
|
+
const value = Number(draft);
|
|
4587
|
+
if (draft.trim() && Number.isInteger(value) && value >= customSilenceMinMs && value <= customSilenceMaxMs)
|
|
4588
|
+
updateSettings({ voiceDetectionCustomSilenceMs: value });
|
|
4589
|
+
}
|
|
4590
|
+
}
|
|
4591
|
+
), /* @__PURE__ */ import_react35.default.createElement("small", null, recognition.silenceDetection.customHelp())),
|
|
4592
|
+
/* @__PURE__ */ import_react35.default.createElement("p", { className: "dlv-vad-summary" }, recognition.silenceDetection.duration({
|
|
4593
|
+
milliseconds: voiceDetectionSilenceMs(settings)
|
|
4487
4594
|
}))
|
|
4488
4595
|
);
|
|
4489
4596
|
}
|
|
4490
4597
|
|
|
4491
4598
|
// src/modules/settings/sections/recognition/VoiceCommandSettings.tsx
|
|
4492
|
-
var
|
|
4599
|
+
var import_react36 = __toESM(require("react"), 1);
|
|
4493
4600
|
var COMMANDS = [
|
|
4494
4601
|
["send", "voiceCommandSend", "send message, send it"],
|
|
4495
4602
|
["queue", "voiceCommandQueue", "queue message, queue it"],
|
|
@@ -4502,52 +4609,53 @@ var COMMANDS = [
|
|
|
4502
4609
|
function VoiceCommandSettings({ settings, updateSettings }) {
|
|
4503
4610
|
const { scoped: recognition } = useLanguage((ctx) => ctx.recognition);
|
|
4504
4611
|
const enabled = settings.voiceCommandsEnabled !== false;
|
|
4505
|
-
return /* @__PURE__ */
|
|
4612
|
+
return /* @__PURE__ */ import_react36.default.createElement(SettingsSubcard, { title: recognition.commands.title(), icon: "send" }, /* @__PURE__ */ import_react36.default.createElement(
|
|
4506
4613
|
CheckboxField,
|
|
4507
4614
|
{
|
|
4508
4615
|
label: recognition.commands.enabled(),
|
|
4509
4616
|
checked: enabled,
|
|
4510
4617
|
onChange: (event) => updateSettings({ voiceCommandsEnabled: event.target.checked })
|
|
4511
4618
|
}
|
|
4512
|
-
), /* @__PURE__ */
|
|
4619
|
+
), /* @__PURE__ */ import_react36.default.createElement("p", { className: "dlv-setting-description" }, recognition.voiceCommands.help()), COMMANDS.map(([label, key, fallback]) => /* @__PURE__ */ import_react36.default.createElement(
|
|
4513
4620
|
TextAreaField,
|
|
4514
4621
|
{
|
|
4515
4622
|
key,
|
|
4516
4623
|
rows: 2,
|
|
4517
4624
|
maxLength: 1e3,
|
|
4518
4625
|
label: recognition.commands[label](),
|
|
4519
|
-
value: settings[key]
|
|
4626
|
+
value: settings[key] ?? fallback,
|
|
4520
4627
|
disabled: !enabled,
|
|
4521
|
-
|
|
4628
|
+
onCommit: (value) => updateSettings({ [key]: value })
|
|
4522
4629
|
}
|
|
4523
4630
|
)));
|
|
4524
4631
|
}
|
|
4525
4632
|
|
|
4526
4633
|
// src/modules/settings/sections/recognition/RecognitionStatus.tsx
|
|
4527
|
-
var
|
|
4634
|
+
var import_react37 = __toESM(require("react"), 1);
|
|
4528
4635
|
function RecognitionStatus({ capabilities = {} }) {
|
|
4529
4636
|
const { scoped: recognition } = useLanguage((ctx) => ctx.recognition);
|
|
4530
|
-
return /* @__PURE__ */
|
|
4637
|
+
return /* @__PURE__ */ import_react37.default.createElement(import_react37.default.Fragment, null, capabilities.capture?.supported === false ? /* @__PURE__ */ import_react37.default.createElement("p", { role: "status" }, recognition.microphone.failure({ reason: capabilities.capture.reason || "" })) : capabilities.capture?.permission === "prompt" ? /* @__PURE__ */ import_react37.default.createElement("p", { role: "status" }, recognition.microphone.permissionHelp()) : null, capabilities.recognition?.supported === false && /* @__PURE__ */ import_react37.default.createElement("p", { role: "status" }, capabilities.recognition.reason));
|
|
4531
4638
|
}
|
|
4532
4639
|
|
|
4533
4640
|
// src/modules/settings/sections/recognition/RecognitionSettingsSection.tsx
|
|
4534
4641
|
function RecognitionSettingsSection(props) {
|
|
4535
|
-
|
|
4642
|
+
const { scoped: settingsLanguage } = useLanguage((ctx) => ctx.settings);
|
|
4643
|
+
return /* @__PURE__ */ import_react38.default.createElement(SettingsCard, null, /* @__PURE__ */ import_react38.default.createElement("div", { className: "dlv-settings-card-body" }, /* @__PURE__ */ import_react38.default.createElement(RecognitionStatus, { ...props }), /* @__PURE__ */ import_react38.default.createElement(SettingsSubcard, { title: settingsLanguage.general.title(), icon: "mic" }, /* @__PURE__ */ import_react38.default.createElement(RecognitionEngineSettings, { ...props })), /* @__PURE__ */ import_react38.default.createElement(VoiceCommandSettings, { ...props }), /* @__PURE__ */ import_react38.default.createElement(RecognitionFilterSettings, { ...props }), /* @__PURE__ */ import_react38.default.createElement(SilenceDetectionSettings, { ...props })));
|
|
4536
4644
|
}
|
|
4537
4645
|
|
|
4538
4646
|
// src/modules/settings/sections/speak/OutputFilterSettings.tsx
|
|
4539
|
-
var
|
|
4647
|
+
var import_react39 = __toESM(require("react"), 1);
|
|
4540
4648
|
function OutputFilterSettings({ settings, updateSettings }) {
|
|
4541
4649
|
const { scoped: speak } = useLanguage((ctx) => ctx.speak);
|
|
4542
4650
|
const enabled = settings.outputCodeFilterEnabled !== false;
|
|
4543
|
-
return /* @__PURE__ */
|
|
4651
|
+
return /* @__PURE__ */ import_react39.default.createElement(SettingsSubcard, { title: speak.filters.code.enabled(), icon: "filter" }, /* @__PURE__ */ import_react39.default.createElement(
|
|
4544
4652
|
CheckboxField,
|
|
4545
4653
|
{
|
|
4546
4654
|
label: speak.filters.code.enabled(),
|
|
4547
4655
|
checked: enabled,
|
|
4548
4656
|
onChange: (event) => updateSettings({ outputCodeFilterEnabled: event.target.checked })
|
|
4549
4657
|
}
|
|
4550
|
-
), /* @__PURE__ */
|
|
4658
|
+
), /* @__PURE__ */ import_react39.default.createElement(
|
|
4551
4659
|
NumberField,
|
|
4552
4660
|
{
|
|
4553
4661
|
label: speak.filters.code.maxLines(),
|
|
@@ -4555,34 +4663,34 @@ function OutputFilterSettings({ settings, updateSettings }) {
|
|
|
4555
4663
|
max: 100,
|
|
4556
4664
|
value: settings.outputCodeMaxLines ?? 5,
|
|
4557
4665
|
disabled: !enabled,
|
|
4558
|
-
|
|
4559
|
-
const value = Number(
|
|
4666
|
+
onCommit: (raw) => {
|
|
4667
|
+
const value = Number(raw);
|
|
4560
4668
|
if (Number.isInteger(value) && value >= 0 && value <= 100)
|
|
4561
4669
|
updateSettings({ outputCodeMaxLines: value });
|
|
4562
4670
|
}
|
|
4563
4671
|
}
|
|
4564
|
-
), /* @__PURE__ */
|
|
4672
|
+
), /* @__PURE__ */ import_react39.default.createElement(
|
|
4565
4673
|
TextField,
|
|
4566
4674
|
{
|
|
4567
4675
|
label: speak.filters.code.replacement(),
|
|
4568
|
-
|
|
4676
|
+
value: settings.outputCodeNotice ?? speak.filters.code.notice(),
|
|
4569
4677
|
disabled: !enabled,
|
|
4570
|
-
|
|
4678
|
+
onCommit: (value) => updateSettings({ outputCodeNotice: value })
|
|
4571
4679
|
}
|
|
4572
4680
|
));
|
|
4573
4681
|
}
|
|
4574
4682
|
|
|
4575
4683
|
// src/modules/settings/sections/speak/SpeakSettingsSection.tsx
|
|
4576
|
-
var
|
|
4684
|
+
var import_react42 = __toESM(require("react"), 1);
|
|
4577
4685
|
|
|
4578
4686
|
// src/modules/settings/sections/speak/SpeechEngineSettings.tsx
|
|
4579
|
-
var
|
|
4687
|
+
var import_react40 = __toESM(require("react"), 1);
|
|
4580
4688
|
function SpeechEngineSettings({ settings, capabilities, updateSettings }) {
|
|
4581
4689
|
const { scoped: commons } = useLanguage((ctx) => ctx.commons);
|
|
4582
4690
|
const { scoped: speak } = useLanguage((ctx) => ctx.speak);
|
|
4583
4691
|
const engine = settings.engine || "browser";
|
|
4584
4692
|
const browserVoices = capabilities?.browser?.voices || [];
|
|
4585
|
-
return /* @__PURE__ */
|
|
4693
|
+
return /* @__PURE__ */ import_react40.default.createElement(import_react40.default.Fragment, null, /* @__PURE__ */ import_react40.default.createElement(
|
|
4586
4694
|
SelectField,
|
|
4587
4695
|
{
|
|
4588
4696
|
label: speak.engine.label(),
|
|
@@ -4606,7 +4714,7 @@ function SpeechEngineSettings({ settings, capabilities, updateSettings }) {
|
|
|
4606
4714
|
],
|
|
4607
4715
|
onChange: (event) => updateSettings({ engine: event.target.value })
|
|
4608
4716
|
}
|
|
4609
|
-
), engine === "browser" && /* @__PURE__ */
|
|
4717
|
+
), engine === "browser" && /* @__PURE__ */ import_react40.default.createElement(
|
|
4610
4718
|
SelectField,
|
|
4611
4719
|
{
|
|
4612
4720
|
label: speak.browser.voice(),
|
|
@@ -4620,7 +4728,7 @@ function SpeechEngineSettings({ settings, capabilities, updateSettings }) {
|
|
|
4620
4728
|
],
|
|
4621
4729
|
onChange: (event) => updateSettings({ voice: event.target.value })
|
|
4622
4730
|
}
|
|
4623
|
-
), engine === "qwen-http" && /* @__PURE__ */
|
|
4731
|
+
), engine === "qwen-http" && /* @__PURE__ */ import_react40.default.createElement(
|
|
4624
4732
|
SelectField,
|
|
4625
4733
|
{
|
|
4626
4734
|
label: speak.qwen.voice(),
|
|
@@ -4628,7 +4736,7 @@ function SpeechEngineSettings({ settings, capabilities, updateSettings }) {
|
|
|
4628
4736
|
options: qwenVoices,
|
|
4629
4737
|
onChange: (event) => updateSettings({ voice: event.target.value })
|
|
4630
4738
|
}
|
|
4631
|
-
), /* @__PURE__ */
|
|
4739
|
+
), /* @__PURE__ */ import_react40.default.createElement(
|
|
4632
4740
|
NumberField,
|
|
4633
4741
|
{
|
|
4634
4742
|
label: speak.rate.label(),
|
|
@@ -4636,9 +4744,9 @@ function SpeechEngineSettings({ settings, capabilities, updateSettings }) {
|
|
|
4636
4744
|
max: 3,
|
|
4637
4745
|
step: 0.1,
|
|
4638
4746
|
value: settings.rate ?? 1,
|
|
4639
|
-
|
|
4747
|
+
onCommit: (value) => updateSettings({ rate: Number(value) })
|
|
4640
4748
|
}
|
|
4641
|
-
), /* @__PURE__ */
|
|
4749
|
+
), /* @__PURE__ */ import_react40.default.createElement(
|
|
4642
4750
|
NumberField,
|
|
4643
4751
|
{
|
|
4644
4752
|
label: speak.segmentGap.label(),
|
|
@@ -4646,13 +4754,13 @@ function SpeechEngineSettings({ settings, capabilities, updateSettings }) {
|
|
|
4646
4754
|
max: 2e3,
|
|
4647
4755
|
step: 50,
|
|
4648
4756
|
value: settings.segmentGapMs ?? 400,
|
|
4649
|
-
|
|
4757
|
+
onCommit: (value) => updateSettings({ segmentGapMs: Number(value) })
|
|
4650
4758
|
}
|
|
4651
|
-
), capabilities?.[engine === "qwen-http" ? "qwen" : engine]?.supported === false && /* @__PURE__ */
|
|
4759
|
+
), capabilities?.[engine === "qwen-http" ? "qwen" : engine]?.supported === false && /* @__PURE__ */ import_react40.default.createElement("p", { role: "status" }, capabilities?.[engine === "qwen-http" ? "qwen" : engine]?.reason || commons.engine.failure()));
|
|
4652
4760
|
}
|
|
4653
4761
|
|
|
4654
4762
|
// src/modules/settings/sections/speak/SpeechAdvancedSettings.tsx
|
|
4655
|
-
var
|
|
4763
|
+
var import_react41 = __toESM(require("react"), 1);
|
|
4656
4764
|
function SpeechAdvancedSettings({
|
|
4657
4765
|
controller,
|
|
4658
4766
|
settings,
|
|
@@ -4670,7 +4778,7 @@ function SpeechAdvancedSettings({
|
|
|
4670
4778
|
commons.systemDefault(),
|
|
4671
4779
|
speak.output.fallbackName()
|
|
4672
4780
|
);
|
|
4673
|
-
return /* @__PURE__ */
|
|
4781
|
+
return /* @__PURE__ */ import_react41.default.createElement(import_react41.default.Fragment, null, settings.engine !== "say" ? /* @__PURE__ */ import_react41.default.createElement(
|
|
4674
4782
|
SelectField,
|
|
4675
4783
|
{
|
|
4676
4784
|
label: speak.output.device(),
|
|
@@ -4678,44 +4786,45 @@ function SpeechAdvancedSettings({
|
|
|
4678
4786
|
options: outputOptions,
|
|
4679
4787
|
onChange: (event) => updateSettings({ outputDeviceId: event.target.value })
|
|
4680
4788
|
}
|
|
4681
|
-
) : /* @__PURE__ */
|
|
4789
|
+
) : /* @__PURE__ */ import_react41.default.createElement("p", { className: "dlv-setting-description" }, speak.macos.outputHelp()), settings.engine === "qwen-http" && /* @__PURE__ */ import_react41.default.createElement(SettingsSubcard, { title: speak.qwen.connection(), icon: "link" }, /* @__PURE__ */ import_react41.default.createElement(QwenSettings, { controller })), /* @__PURE__ */ import_react41.default.createElement(SettingsSubcard, { title: speak.agentContext.label(), icon: "settings" }, /* @__PURE__ */ import_react41.default.createElement(
|
|
4682
4790
|
CheckboxField,
|
|
4683
4791
|
{
|
|
4684
4792
|
label: speak.agentContext.enabled(),
|
|
4685
4793
|
checked: settings.agentVoiceContextEnabled !== false,
|
|
4686
4794
|
onChange: (event) => updateSettings({ agentVoiceContextEnabled: event.target.checked })
|
|
4687
4795
|
}
|
|
4688
|
-
), /* @__PURE__ */
|
|
4796
|
+
), /* @__PURE__ */ import_react41.default.createElement(
|
|
4689
4797
|
TextAreaField,
|
|
4690
4798
|
{
|
|
4691
4799
|
label: speak.agentContext.label(),
|
|
4692
|
-
value: settings.agentVoiceContext
|
|
4800
|
+
value: settings.agentVoiceContext ?? defaultAgentVoiceContext,
|
|
4693
4801
|
disabled: settings.agentVoiceContextEnabled === false,
|
|
4694
|
-
|
|
4802
|
+
onCommit: (value) => updateSettings({ agentVoiceContext: value })
|
|
4695
4803
|
}
|
|
4696
|
-
), /* @__PURE__ */
|
|
4804
|
+
), /* @__PURE__ */ import_react41.default.createElement(
|
|
4697
4805
|
"button",
|
|
4698
4806
|
{
|
|
4699
4807
|
type: "button",
|
|
4700
4808
|
onClick: () => updateSettings({ agentVoiceContext: defaultAgentVoiceContext })
|
|
4701
4809
|
},
|
|
4702
4810
|
speak.agentContext.restore()
|
|
4703
|
-
)), /* @__PURE__ */
|
|
4811
|
+
)), /* @__PURE__ */ import_react41.default.createElement("p", null, speak.engine.playbackHelp()), /* @__PURE__ */ import_react41.default.createElement("div", { className: "dlv-settings-actions" }, /* @__PURE__ */ import_react41.default.createElement("button", { type: "button", onClick: () => invoke("speak", speak.output.testPhrase()) }, speak.output.test()), speaking && /* @__PURE__ */ import_react41.default.createElement("button", { type: "button", onClick: () => invoke("stopSpeech") }, speak.output.stopTest()), /* @__PURE__ */ import_react41.default.createElement("button", { type: "button", onClick: () => invoke("refreshCapabilities") }, settingsLanguage.engine.refresh())));
|
|
4704
4812
|
}
|
|
4705
4813
|
|
|
4706
4814
|
// src/modules/settings/sections/speak/SpeakSettingsSection.tsx
|
|
4707
4815
|
function SpeakSettingsSection(props) {
|
|
4708
|
-
|
|
4816
|
+
const { scoped: settingsLanguage } = useLanguage((ctx) => ctx.settings);
|
|
4817
|
+
return /* @__PURE__ */ import_react42.default.createElement(SettingsCard, null, /* @__PURE__ */ import_react42.default.createElement("div", { className: "dlv-settings-card-body" }, /* @__PURE__ */ import_react42.default.createElement(SettingsSubcard, { title: settingsLanguage.general.title(), icon: "speaker" }, /* @__PURE__ */ import_react42.default.createElement(SpeechEngineSettings, { ...props }), /* @__PURE__ */ import_react42.default.createElement(SpeechAdvancedSettings, { ...props })), /* @__PURE__ */ import_react42.default.createElement(OutputFilterSettings, { ...props })));
|
|
4709
4818
|
}
|
|
4710
4819
|
|
|
4711
4820
|
// src/modules/settings/components/SettingsHeader.tsx
|
|
4712
|
-
var
|
|
4821
|
+
var import_react44 = __toESM(require("react"), 1);
|
|
4713
4822
|
|
|
4714
4823
|
// src/modules/settings/components/VersionBadges.tsx
|
|
4715
|
-
var
|
|
4824
|
+
var import_react43 = __toESM(require("react"), 1);
|
|
4716
4825
|
function VersionBadges() {
|
|
4717
4826
|
const { scoped: commons } = useLanguage((ctx) => ctx.commons);
|
|
4718
|
-
return /* @__PURE__ */
|
|
4827
|
+
return /* @__PURE__ */ import_react43.default.createElement("div", { className: "dlv-version-badges", "aria-label": commons.version.title() }, /* @__PURE__ */ import_react43.default.createElement(
|
|
4719
4828
|
"a",
|
|
4720
4829
|
{
|
|
4721
4830
|
className: "dlv-shields-badge",
|
|
@@ -4725,9 +4834,9 @@ function VersionBadges() {
|
|
|
4725
4834
|
"aria-label": commons.version.link({ version: CURRENT_VERSION }),
|
|
4726
4835
|
title: commons.version.label({ version: CURRENT_VERSION })
|
|
4727
4836
|
},
|
|
4728
|
-
/* @__PURE__ */
|
|
4729
|
-
/* @__PURE__ */
|
|
4730
|
-
), /* @__PURE__ */
|
|
4837
|
+
/* @__PURE__ */ import_react43.default.createElement("img", { src: LIVE_VOICE_BADGE_URL, alt: "" }),
|
|
4838
|
+
/* @__PURE__ */ import_react43.default.createElement("span", null, `v${CURRENT_VERSION}`)
|
|
4839
|
+
), /* @__PURE__ */ import_react43.default.createElement(
|
|
4731
4840
|
"a",
|
|
4732
4841
|
{
|
|
4733
4842
|
className: "dlv-shields-badge",
|
|
@@ -4737,8 +4846,8 @@ function VersionBadges() {
|
|
|
4737
4846
|
"aria-label": commons.version.compatibilityLink({ version: TESTED_DSH_VERSION }),
|
|
4738
4847
|
title: commons.version.compatibility({ version: TESTED_DSH_VERSION })
|
|
4739
4848
|
},
|
|
4740
|
-
/* @__PURE__ */
|
|
4741
|
-
/* @__PURE__ */
|
|
4849
|
+
/* @__PURE__ */ import_react43.default.createElement("img", { src: DSH_BADGE_URL, alt: "" }),
|
|
4850
|
+
/* @__PURE__ */ import_react43.default.createElement("span", null, `v${TESTED_DSH_VERSION}`)
|
|
4742
4851
|
));
|
|
4743
4852
|
}
|
|
4744
4853
|
|
|
@@ -4747,7 +4856,7 @@ function SettingsHeader({ onClose }) {
|
|
|
4747
4856
|
const { scoped: commons } = useLanguage((ctx) => ctx.commons);
|
|
4748
4857
|
const { scoped: settings } = useLanguage((ctx) => ctx.settings);
|
|
4749
4858
|
const { latestRelease, updateAvailable } = useReleaseStatus();
|
|
4750
|
-
return /* @__PURE__ */
|
|
4859
|
+
return /* @__PURE__ */ import_react44.default.createElement("div", { className: "dlv-settings-heading" }, /* @__PURE__ */ import_react44.default.createElement("h3", null, commons.pluginName()), /* @__PURE__ */ import_react44.default.createElement(VersionBadges, null), /* @__PURE__ */ import_react44.default.createElement("span", { className: "dlv-heading-divider", "aria-hidden": true }), updateAvailable && latestRelease ? /* @__PURE__ */ import_react44.default.createElement(import_react44.default.Fragment, null, /* @__PURE__ */ import_react44.default.createElement(
|
|
4751
4860
|
"a",
|
|
4752
4861
|
{
|
|
4753
4862
|
className: "dlv-version-badge dlv-update-badge",
|
|
@@ -4757,9 +4866,9 @@ function SettingsHeader({ onClose }) {
|
|
|
4757
4866
|
"aria-label": commons.update.link({ version: latestRelease.tag }),
|
|
4758
4867
|
title: commons.update.version({ version: latestRelease.tag })
|
|
4759
4868
|
},
|
|
4760
|
-
/* @__PURE__ */
|
|
4869
|
+
/* @__PURE__ */ import_react44.default.createElement("span", { className: "dlv-update-icon", "aria-hidden": true }, "\u2191"),
|
|
4761
4870
|
commons.update.label()
|
|
4762
|
-
), /* @__PURE__ */
|
|
4871
|
+
), /* @__PURE__ */ import_react44.default.createElement("span", { className: "dlv-heading-divider", "aria-hidden": true })) : null, /* @__PURE__ */ import_react44.default.createElement(
|
|
4763
4872
|
"a",
|
|
4764
4873
|
{
|
|
4765
4874
|
className: "dlv-version-badge dlv-star-badge",
|
|
@@ -4771,7 +4880,7 @@ function SettingsHeader({ onClose }) {
|
|
|
4771
4880
|
},
|
|
4772
4881
|
"\u2605 ",
|
|
4773
4882
|
commons.repository.starLabel()
|
|
4774
|
-
), onClose ? /* @__PURE__ */
|
|
4883
|
+
), onClose ? /* @__PURE__ */ import_react44.default.createElement(IconButton, { icon: "close", label: settings.close(), onClick: onClose }) : null);
|
|
4775
4884
|
}
|
|
4776
4885
|
|
|
4777
4886
|
// src/modules/settings/components/LiveVoiceSettings.tsx
|
|
@@ -4780,12 +4889,20 @@ function LiveVoiceSettings({ controller, onClose }) {
|
|
|
4780
4889
|
const { scoped: settingsLanguage } = useLanguage((ctx) => ctx.settings);
|
|
4781
4890
|
const state = useConversationController(controller);
|
|
4782
4891
|
const { invoke, error, clearError } = useConversationActions(controller);
|
|
4783
|
-
const [activeTab, setActiveTab] =
|
|
4784
|
-
const tabsId =
|
|
4892
|
+
const [activeTab, setActiveTab] = import_react45.default.useState("conversation");
|
|
4893
|
+
const tabsId = import_react45.default.useId();
|
|
4785
4894
|
const tabs = [
|
|
4786
|
-
{ id: "speech", label: settingsLanguage.tabs.speak() },
|
|
4787
|
-
{
|
|
4788
|
-
|
|
4895
|
+
{ id: "speech", label: settingsLanguage.tabs.speak(), icon: "speaker" },
|
|
4896
|
+
{
|
|
4897
|
+
id: "recognition",
|
|
4898
|
+
label: settingsLanguage.tabs.recognition(),
|
|
4899
|
+
icon: "mic"
|
|
4900
|
+
},
|
|
4901
|
+
{
|
|
4902
|
+
id: "conversation",
|
|
4903
|
+
label: settingsLanguage.tabs.conversation(),
|
|
4904
|
+
icon: "send"
|
|
4905
|
+
}
|
|
4789
4906
|
];
|
|
4790
4907
|
const sectionProps = {
|
|
4791
4908
|
controller,
|
|
@@ -4795,7 +4912,7 @@ function LiveVoiceSettings({ controller, onClose }) {
|
|
|
4795
4912
|
invoke,
|
|
4796
4913
|
updateSettings: (next) => invoke("updateSettings", next)
|
|
4797
4914
|
};
|
|
4798
|
-
return /* @__PURE__ */
|
|
4915
|
+
return /* @__PURE__ */ import_react45.default.createElement("section", { className: "dlv-settings", "aria-label": settingsLanguage.title() }, /* @__PURE__ */ import_react45.default.createElement(SettingsHeader, { onClose }), /* @__PURE__ */ import_react45.default.createElement(
|
|
4799
4916
|
SettingsTabs,
|
|
4800
4917
|
{
|
|
4801
4918
|
label: settingsLanguage.title(),
|
|
@@ -4804,7 +4921,7 @@ function LiveVoiceSettings({ controller, onClose }) {
|
|
|
4804
4921
|
idPrefix: tabsId,
|
|
4805
4922
|
onChange: setActiveTab
|
|
4806
4923
|
}
|
|
4807
|
-
), tabs.map((tab) => /* @__PURE__ */
|
|
4924
|
+
), tabs.map((tab) => /* @__PURE__ */ import_react45.default.createElement(
|
|
4808
4925
|
"div",
|
|
4809
4926
|
{
|
|
4810
4927
|
key: tab.id,
|
|
@@ -4814,8 +4931,8 @@ function LiveVoiceSettings({ controller, onClose }) {
|
|
|
4814
4931
|
"aria-labelledby": `${tabsId}-tab-${tab.id}`,
|
|
4815
4932
|
hidden: activeTab !== tab.id
|
|
4816
4933
|
},
|
|
4817
|
-
tab.id === "speech" ? /* @__PURE__ */
|
|
4818
|
-
)), /* @__PURE__ */
|
|
4934
|
+
tab.id === "speech" ? /* @__PURE__ */ import_react45.default.createElement(SpeakSettingsSection, { ...sectionProps }) : tab.id === "recognition" ? /* @__PURE__ */ import_react45.default.createElement(RecognitionSettingsSection, { ...sectionProps }) : /* @__PURE__ */ import_react45.default.createElement(ConversationSettingsSection, { ...sectionProps })
|
|
4935
|
+
)), /* @__PURE__ */ import_react45.default.createElement(
|
|
4819
4936
|
ErrorMessage,
|
|
4820
4937
|
{
|
|
4821
4938
|
error: error || state.error,
|
|
@@ -4832,17 +4949,64 @@ function LiveVoiceSettings({ controller, onClose }) {
|
|
|
4832
4949
|
// src/modules/settings/components/createLiveVoiceSettings.tsx
|
|
4833
4950
|
function createLiveVoiceSettings(..._legacyArguments) {
|
|
4834
4951
|
return function SettingsPanel({ controller, onClose }) {
|
|
4835
|
-
return /* @__PURE__ */
|
|
4952
|
+
return /* @__PURE__ */ import_react46.default.createElement(LiveVoiceSettings, { controller, onClose });
|
|
4953
|
+
};
|
|
4954
|
+
}
|
|
4955
|
+
|
|
4956
|
+
// src/modules/settings/models/settingsStorage.ts
|
|
4957
|
+
var SETTINGS_PATH = "/api/dsh-live-voice/settings";
|
|
4958
|
+
function createSettingsClient(fetchImpl = globalThis.fetch) {
|
|
4959
|
+
let settings = normalizeSettings({});
|
|
4960
|
+
let queue = Promise.resolve();
|
|
4961
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
4962
|
+
async function request(method, patch) {
|
|
4963
|
+
const response = await fetchImpl(SETTINGS_PATH, {
|
|
4964
|
+
method,
|
|
4965
|
+
credentials: "same-origin",
|
|
4966
|
+
cache: "no-store",
|
|
4967
|
+
...patch === void 0 ? {} : { headers: { "content-type": "application/json" }, body: JSON.stringify(patch) }
|
|
4968
|
+
});
|
|
4969
|
+
const body = await response.json();
|
|
4970
|
+
if (!response.ok || body?.ok !== true) throw new Error("settings-request-failed");
|
|
4971
|
+
settings = normalizeSettings(body.value);
|
|
4972
|
+
for (const listener of listeners) listener(settings);
|
|
4973
|
+
return settings;
|
|
4974
|
+
}
|
|
4975
|
+
const ready = request("GET");
|
|
4976
|
+
void ready.catch(() => {
|
|
4977
|
+
});
|
|
4978
|
+
return {
|
|
4979
|
+
ready,
|
|
4980
|
+
getSnapshot: () => settings,
|
|
4981
|
+
subscribe(listener) {
|
|
4982
|
+
listeners.add(listener);
|
|
4983
|
+
return () => {
|
|
4984
|
+
listeners.delete(listener);
|
|
4985
|
+
};
|
|
4986
|
+
},
|
|
4987
|
+
save(patch) {
|
|
4988
|
+
const operation = queue.then(async () => {
|
|
4989
|
+
await ready;
|
|
4990
|
+
return request("PUT", patch);
|
|
4991
|
+
});
|
|
4992
|
+
queue = operation.then(
|
|
4993
|
+
() => {
|
|
4994
|
+
},
|
|
4995
|
+
() => {
|
|
4996
|
+
}
|
|
4997
|
+
);
|
|
4998
|
+
return operation;
|
|
4999
|
+
}
|
|
4836
5000
|
};
|
|
4837
5001
|
}
|
|
4838
5002
|
|
|
4839
5003
|
// src/modules/conversation/components/ConversationControls.tsx
|
|
4840
|
-
var
|
|
5004
|
+
var import_react48 = __toESM(require("react"), 1);
|
|
4841
5005
|
|
|
4842
5006
|
// src/modules/conversation/components/MicrophoneButton.tsx
|
|
4843
|
-
var
|
|
5007
|
+
var import_react47 = __toESM(require("react"), 1);
|
|
4844
5008
|
function MicrophoneButton(props) {
|
|
4845
|
-
return /* @__PURE__ */
|
|
5009
|
+
return /* @__PURE__ */ import_react47.default.createElement(IconButton, { ...props, className: "dlv-mic", icon: "mic" });
|
|
4846
5010
|
}
|
|
4847
5011
|
|
|
4848
5012
|
// src/modules/conversation/components/ConversationControls.tsx
|
|
@@ -4859,14 +5023,14 @@ function ConversationControls({ controller }) {
|
|
|
4859
5023
|
const unavailable = capability?.supported === false || capture?.supported === false;
|
|
4860
5024
|
const reason = capture?.supported === false ? capture.reason : capability?.reason;
|
|
4861
5025
|
const label = pending ? recognition.microphone.checking() : unavailable ? reason || recognition.status.unavailable() : commons.conversation.start();
|
|
4862
|
-
return /* @__PURE__ */
|
|
5026
|
+
return /* @__PURE__ */ import_react48.default.createElement(import_react48.default.Fragment, null, /* @__PURE__ */ import_react48.default.createElement(
|
|
4863
5027
|
MicrophoneButton,
|
|
4864
5028
|
{
|
|
4865
5029
|
label,
|
|
4866
5030
|
disabled: pending,
|
|
4867
5031
|
onClick: () => invoke(unavailable ? "explainRecognition" : "startConversation")
|
|
4868
5032
|
}
|
|
4869
|
-
), /* @__PURE__ */
|
|
5033
|
+
), /* @__PURE__ */ import_react48.default.createElement(
|
|
4870
5034
|
ErrorMessage,
|
|
4871
5035
|
{
|
|
4872
5036
|
error,
|
|
@@ -4878,10 +5042,10 @@ function ConversationControls({ controller }) {
|
|
|
4878
5042
|
}
|
|
4879
5043
|
|
|
4880
5044
|
// src/modules/conversation/components/ConversationStatusBar.tsx
|
|
4881
|
-
var
|
|
5045
|
+
var import_react52 = __toESM(require("react"), 1);
|
|
4882
5046
|
|
|
4883
5047
|
// src/modules/conversation/components/DeliveryModeButton.tsx
|
|
4884
|
-
var
|
|
5048
|
+
var import_react49 = __toESM(require("react"), 1);
|
|
4885
5049
|
function nextDeliveryMode(mode) {
|
|
4886
5050
|
return mode === "queue" ? "steer" : mode === "steer" ? "manual" : "queue";
|
|
4887
5051
|
}
|
|
@@ -4894,7 +5058,7 @@ function DeliveryModeButton({
|
|
|
4894
5058
|
const description = mode === "steer" ? settings.delivery.steerDescription() : mode === "queue" ? commons.queue() : commons.manual();
|
|
4895
5059
|
const status = settings.delivery.status({ mode: description });
|
|
4896
5060
|
const visible = mode === "steer" ? commons.send() : mode === "queue" ? commons.delivery.queueBadge() : commons.toggle.offBadge();
|
|
4897
|
-
return /* @__PURE__ */
|
|
5061
|
+
return /* @__PURE__ */ import_react49.default.createElement(
|
|
4898
5062
|
IconButton,
|
|
4899
5063
|
{
|
|
4900
5064
|
className: "dlv-live-toggle",
|
|
@@ -4910,18 +5074,18 @@ function DeliveryModeButton({
|
|
|
4910
5074
|
}
|
|
4911
5075
|
|
|
4912
5076
|
// src/modules/conversation/components/PlaybackControls.tsx
|
|
4913
|
-
var
|
|
5077
|
+
var import_react50 = __toESM(require("react"), 1);
|
|
4914
5078
|
function PlaybackControls({ state, invoke }) {
|
|
4915
5079
|
const { scoped: speak } = useLanguage((ctx) => ctx.speak);
|
|
4916
5080
|
const capabilities = state.capabilities?.[state.settings?.engine] ?? {};
|
|
4917
|
-
return /* @__PURE__ */
|
|
5081
|
+
return /* @__PURE__ */ import_react50.default.createElement(import_react50.default.Fragment, null, state.speechSegmentsRemaining > 1 ? /* @__PURE__ */ import_react50.default.createElement(
|
|
4918
5082
|
IconButton,
|
|
4919
5083
|
{
|
|
4920
5084
|
label: speak.playback.next(),
|
|
4921
5085
|
icon: "skipNext",
|
|
4922
5086
|
onClick: () => invoke("skipSpeechSegment")
|
|
4923
5087
|
}
|
|
4924
|
-
) : null, state.speechSegmentsRemaining > 0 && capabilities.pause ? /* @__PURE__ */
|
|
5088
|
+
) : null, state.speechSegmentsRemaining > 0 && capabilities.pause ? /* @__PURE__ */ import_react50.default.createElement(
|
|
4925
5089
|
IconButton,
|
|
4926
5090
|
{
|
|
4927
5091
|
label: speak.playback.pause(),
|
|
@@ -4929,14 +5093,14 @@ function PlaybackControls({ state, invoke }) {
|
|
|
4929
5093
|
disabled: !state.speaking || state.paused,
|
|
4930
5094
|
onClick: () => invoke("pauseSpeech")
|
|
4931
5095
|
}
|
|
4932
|
-
) : null, state.paused && capabilities.resume ? /* @__PURE__ */
|
|
5096
|
+
) : null, state.paused && capabilities.resume ? /* @__PURE__ */ import_react50.default.createElement(
|
|
4933
5097
|
IconButton,
|
|
4934
5098
|
{
|
|
4935
5099
|
label: speak.playback.resume(),
|
|
4936
5100
|
icon: "play",
|
|
4937
5101
|
onClick: () => invoke("resumeSpeech")
|
|
4938
5102
|
}
|
|
4939
|
-
) : null, state.speechSegmentsRemaining > 0 ? /* @__PURE__ */
|
|
5103
|
+
) : null, state.speechSegmentsRemaining > 0 ? /* @__PURE__ */ import_react50.default.createElement(
|
|
4940
5104
|
IconButton,
|
|
4941
5105
|
{
|
|
4942
5106
|
label: speak.playback.stopAll(),
|
|
@@ -4947,10 +5111,10 @@ function PlaybackControls({ state, invoke }) {
|
|
|
4947
5111
|
}
|
|
4948
5112
|
|
|
4949
5113
|
// src/modules/conversation/components/Waveform.tsx
|
|
4950
|
-
var
|
|
5114
|
+
var import_react51 = __toESM(require("react"), 1);
|
|
4951
5115
|
function Waveform({ controller, enabled }) {
|
|
4952
|
-
const ref =
|
|
4953
|
-
|
|
5116
|
+
const ref = import_react51.default.useRef(null);
|
|
5117
|
+
import_react51.default.useEffect(() => {
|
|
4954
5118
|
const canvas = ref.current;
|
|
4955
5119
|
const context = canvas?.getContext("2d");
|
|
4956
5120
|
if (!canvas || !context) return;
|
|
@@ -5002,7 +5166,7 @@ function Waveform({ controller, enabled }) {
|
|
|
5002
5166
|
window.removeEventListener("resize", resize);
|
|
5003
5167
|
};
|
|
5004
5168
|
}, [controller, enabled]);
|
|
5005
|
-
return /* @__PURE__ */
|
|
5169
|
+
return /* @__PURE__ */ import_react51.default.createElement("canvas", { ref, className: "dlv-wave", "aria-hidden": true });
|
|
5006
5170
|
}
|
|
5007
5171
|
|
|
5008
5172
|
// src/modules/conversation/components/conversationStatus.ts
|
|
@@ -5033,8 +5197,8 @@ function ConversationStatusBar({
|
|
|
5033
5197
|
const { scoped: speak } = useLanguage((ctx) => ctx.speak);
|
|
5034
5198
|
const state = useConversationController(controller);
|
|
5035
5199
|
const { invoke, error, clearError } = useConversationActions(controller);
|
|
5036
|
-
const [now, setNow] =
|
|
5037
|
-
|
|
5200
|
+
const [now, setNow] = import_react52.default.useState(Date.now());
|
|
5201
|
+
import_react52.default.useEffect(() => {
|
|
5038
5202
|
if (!state.autoSendAt) return;
|
|
5039
5203
|
setNow(Date.now());
|
|
5040
5204
|
const timer = setInterval(() => setNow(Date.now()), 200);
|
|
@@ -5058,33 +5222,33 @@ function ConversationStatusBar({
|
|
|
5058
5222
|
}) : speak.autoPlayback.status({
|
|
5059
5223
|
state: autoPlayback ? commons.on() : commons.off()
|
|
5060
5224
|
});
|
|
5061
|
-
return /* @__PURE__ */
|
|
5225
|
+
return /* @__PURE__ */ import_react52.default.createElement(
|
|
5062
5226
|
"div",
|
|
5063
5227
|
{
|
|
5064
5228
|
className: overlay ? "dlv-bar-wrap dlv-question-overlay" : "dlv-bar-wrap",
|
|
5065
5229
|
style: overlay ? overlayStyle : void 0
|
|
5066
5230
|
},
|
|
5067
|
-
/* @__PURE__ */
|
|
5231
|
+
/* @__PURE__ */ import_react52.default.createElement("div", { className: "dlv-pill", role: "group", "aria-label": commons.controls.title() }, capture && !state.conversation ? /* @__PURE__ */ import_react52.default.createElement(
|
|
5068
5232
|
IconButton,
|
|
5069
5233
|
{
|
|
5070
5234
|
label: recognition.dictation.cancel(),
|
|
5071
5235
|
icon: "close",
|
|
5072
5236
|
onClick: () => invoke("cancelDictation")
|
|
5073
5237
|
}
|
|
5074
|
-
) : null, state.conversation ? /* @__PURE__ */
|
|
5238
|
+
) : null, state.conversation ? /* @__PURE__ */ import_react52.default.createElement(
|
|
5075
5239
|
IconButton,
|
|
5076
5240
|
{
|
|
5077
5241
|
label: commons.conversation.end(),
|
|
5078
5242
|
icon: "close",
|
|
5079
5243
|
onClick: () => invoke("endConversation")
|
|
5080
5244
|
}
|
|
5081
|
-
) : null, /* @__PURE__ */
|
|
5245
|
+
) : null, /* @__PURE__ */ import_react52.default.createElement(Waveform, { controller, enabled: Boolean(state.listening) }), /* @__PURE__ */ import_react52.default.createElement("span", { className: "dlv-status", role: "status", "aria-live": "polite" }, status), /* @__PURE__ */ import_react52.default.createElement(
|
|
5082
5246
|
DeliveryModeButton,
|
|
5083
5247
|
{
|
|
5084
5248
|
mode: state.settings.sendingMode || "manual",
|
|
5085
5249
|
onChange: (sendingMode) => invoke("updateSettings", { sendingMode })
|
|
5086
5250
|
}
|
|
5087
|
-
), /* @__PURE__ */
|
|
5251
|
+
), /* @__PURE__ */ import_react52.default.createElement(
|
|
5088
5252
|
IconButton,
|
|
5089
5253
|
{
|
|
5090
5254
|
className: `dlv-live-toggle${state.speechSegmentsRemaining > 0 ? " dlv-live-toggle-expanded" : ""}`,
|
|
@@ -5096,21 +5260,21 @@ function ConversationStatusBar({
|
|
|
5096
5260
|
"aria-checked": autoPlayback,
|
|
5097
5261
|
onClick: () => invoke("updateSettings", { announceAssistantMessages: !autoPlayback })
|
|
5098
5262
|
}
|
|
5099
|
-
), remaining ? /* @__PURE__ */
|
|
5263
|
+
), remaining ? /* @__PURE__ */ import_react52.default.createElement(
|
|
5100
5264
|
IconButton,
|
|
5101
5265
|
{
|
|
5102
5266
|
label: settings.autoSend.cancel(),
|
|
5103
5267
|
icon: "close",
|
|
5104
5268
|
onClick: () => invoke("cancelAutoSend")
|
|
5105
5269
|
}
|
|
5106
|
-
) : null, state.conversation && !capture ? /* @__PURE__ */
|
|
5270
|
+
) : null, state.conversation && !capture ? /* @__PURE__ */ import_react52.default.createElement(
|
|
5107
5271
|
IconButton,
|
|
5108
5272
|
{
|
|
5109
5273
|
label: recognition.microphone.takeControl(),
|
|
5110
5274
|
icon: "mic",
|
|
5111
5275
|
onClick: () => invoke("startConversation")
|
|
5112
5276
|
}
|
|
5113
|
-
) : null, capture ? /* @__PURE__ */
|
|
5277
|
+
) : null, capture ? /* @__PURE__ */ import_react52.default.createElement(
|
|
5114
5278
|
IconButton,
|
|
5115
5279
|
{
|
|
5116
5280
|
className: "dlv-live-toggle dlv-mic-state",
|
|
@@ -5124,8 +5288,8 @@ function ConversationStatusBar({
|
|
|
5124
5288
|
"data-muted": state.muted ? "true" : "false",
|
|
5125
5289
|
onClick: () => invoke(state.muted ? "resumeListeningInput" : "muteListening")
|
|
5126
5290
|
}
|
|
5127
|
-
) : null, /* @__PURE__ */
|
|
5128
|
-
/* @__PURE__ */
|
|
5291
|
+
) : null, /* @__PURE__ */ import_react52.default.createElement(PlaybackControls, { state, invoke })),
|
|
5292
|
+
/* @__PURE__ */ import_react52.default.createElement(
|
|
5129
5293
|
ErrorMessage,
|
|
5130
5294
|
{
|
|
5131
5295
|
error: error || state.error,
|
|
@@ -5141,7 +5305,7 @@ function ConversationStatusBar({
|
|
|
5141
5305
|
}
|
|
5142
5306
|
|
|
5143
5307
|
// src/modules/conversation/components/SpeakButton.tsx
|
|
5144
|
-
var
|
|
5308
|
+
var import_react53 = __toESM(require("react"), 1);
|
|
5145
5309
|
function SpeakButton({
|
|
5146
5310
|
active = false,
|
|
5147
5311
|
disabled = false,
|
|
@@ -5150,7 +5314,7 @@ function SpeakButton({
|
|
|
5150
5314
|
}) {
|
|
5151
5315
|
const { scoped: speak } = useLanguage((ctx) => ctx.speak);
|
|
5152
5316
|
const resolvedLabel = label ?? (active ? speak.playback.stop() : speak.playback.message());
|
|
5153
|
-
return /* @__PURE__ */
|
|
5317
|
+
return /* @__PURE__ */ import_react53.default.createElement(
|
|
5154
5318
|
IconButton,
|
|
5155
5319
|
{
|
|
5156
5320
|
className: "dlv-speaker",
|
|
@@ -5285,11 +5449,11 @@ var styles = `
|
|
|
5285
5449
|
.dlv-settings h3,.dlv-settings p{margin:0}
|
|
5286
5450
|
.dlv-settings-heading{display:flex;align-items:center;gap:8px;min-width:0}.dlv-settings-heading h3{white-space:nowrap}.dlv-heading-divider{height:1px;min-width:12px;flex:1;background:var(--dsw-alias-border-l1)}.dlv-settings-heading .dlv-star-badge{flex:none}
|
|
5287
5451
|
.dlv-version-badges{display:flex;flex:none;gap:5px;align-items:center}.dlv-shields-badge{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;height:20px;gap:4px;padding:3px 5px;border:1px solid var(--dsw-alias-border-l1);border-radius:5px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font-size:10px;font-weight:650;line-height:12px;text-decoration:none}.dlv-shields-badge img{display:block;width:12px;height:12px;max-width:100%}.dlv-shields-badge:focus-visible{border-radius:4px;outline:2px solid var(--dsw-alias-label-primary);outline-offset:3px}.dlv-version-badge{display:inline-flex;align-items:center;min-height:24px;box-sizing:border-box;padding:3px 8px;border:1px solid var(--dsw-alias-border-l1);border-radius:999px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font-size:11px;font-weight:650;line-height:16px;text-decoration:none}.dlv-version-badges a.dlv-version-badge:hover{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary)}.dlv-star-badge{padding:4px 10px;color:var(--dsw-alias-label-primary)}.dlv-update-icon{margin-right:4px;font-size:14px;line-height:1}.dlv-star-icon{margin-right:4px;color:#f59e0b;font-size:14px;line-height:1;text-shadow:0 0 8px rgba(245,158,11,.28)}.dlv-version-badges a.dlv-star-badge:hover{border-color:#f59e0b;background:rgba(245,158,11,.08)}.dlv-update-badge{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}
|
|
5288
|
-
.dlv-settings-tabs{border-bottom:.5px solid var(--dsw-alias-border-l2);display:flex;align-items:flex-end;gap:22px;overflow-x:auto}.dlv-settings-tab{position:relative;flex:none;padding:7px 1px 9px;border:0;background:transparent;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:
|
|
5452
|
+
.dlv-settings-tabs{border-bottom:.5px solid var(--dsw-alias-border-l2);display:flex;align-items:flex-end;gap:22px;overflow-x:auto}.dlv-settings-tab{position:relative;display:inline-flex;align-items:center;gap:6px;flex:none;padding:7px 1px 9px;border:0;background:transparent;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:16px;font-weight:600;line-height:22px;cursor:pointer}.dlv-settings-tab-icon{width:17px;height:17px;flex:none}.dlv-settings-tab:hover,.dlv-settings-tab[data-active=true]{color:var(--dsw-alias-label-primary)}.dlv-settings-tab[data-active=true]:after,.dlv-settings-tab:focus-visible:after{content:"";position:absolute;right:0;bottom:-1px;left:0;height:2px;border-radius:2px 2px 0 0;background:var(--dsw-alias-label-primary)}.dlv-settings-tab:focus-visible{border-radius:2px;outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;color:var(--dsw-alias-label-primary)}.dlv-settings-panel{min-width:0;padding-top:2px}.dlv-settings-panel[hidden]{display:none}.dlv-settings-group{margin:0;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;min-width:0;display:grid;gap:14px;padding:16px}.dlv-settings-group legend{padding:0 6px;font-weight:600;color:var(--dsw-alias-label-primary)}.dlv-settings-card-body{display:grid;gap:18px;padding:8px 0 12px}.dlv-settings-subcard{min-width:0;margin:8px 0 0}.dlv-settings-subcard>summary{cursor:pointer;list-style:none;display:flex;align-items:center;gap:9px;padding:10px 12px;border-bottom:1px solid var(--dsw-alias-border-l1);border-radius:6px 6px 0 0;background:color-mix(in srgb,var(--dsw-alias-bg-layer-2) 38%,transparent);color:var(--dsw-alias-label-secondary);font-size:14px;font-weight:500;line-height:20px}.dlv-settings-subcard>summary::-webkit-details-marker{display:none}.dlv-settings-subcard>summary:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:3px}.dlv-settings-subcard-icon{width:16px;height:16px;flex:none}.dlv-settings-subcard-chevron{width:16px;height:16px;margin-left:auto;flex:none;transition:transform .15s}.dlv-settings-subcard[open]>summary .dlv-settings-subcard-chevron{transform:rotate(180deg)}.dlv-settings-subcard-body{display:grid;gap:16px;padding:18px 0 6px}
|
|
5289
5453
|
.dlv-settings label{display:grid;gap:6px;font-size:14px}.dlv-settings label.dlv-check{display:flex;align-items:center;gap:8px}.dlv-settings label.dlv-check input{width:auto}
|
|
5290
5454
|
.dlv-settings :is(input,select,textarea){box-sizing:border-box;width:100%;padding:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:inherit;font:inherit}
|
|
5291
|
-
.dlv-settings small,.dlv-settings p{font-size:13px;color:var(--dsw-alias-label-secondary);line-height:1.5}
|
|
5292
|
-
.dlv-preset-group{display:grid;grid-template-columns:repeat(
|
|
5455
|
+
.dlv-settings small,.dlv-settings p{font-size:13px;color:var(--dsw-alias-label-secondary);line-height:1.5}.dlv-settings .dlv-setting-description{line-height:1.65;margin:3px 0 5px}.dlv-settings .dlv-conversation-fields{display:grid;gap:20px;padding:4px 0}
|
|
5456
|
+
.dlv-preset-group{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.dlv-settings label.dlv-preset{display:flex;align-items:flex-start;gap:8px;padding:10px;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;cursor:pointer}.dlv-settings label.dlv-preset:has(input:checked){border-color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1)}.dlv-settings label.dlv-preset input{width:auto;margin-top:3px}.dlv-preset span{display:grid;gap:3px}.dlv-vad-summary{font-weight:500}
|
|
5293
5457
|
.dlv-settings-actions{display:flex;flex-wrap:wrap;gap:8px}.dlv-settings-actions button{padding:8px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:inherit;cursor:pointer}.dlv-settings-actions button:disabled{opacity:.45;cursor:default}
|
|
5294
5458
|
@media(max-width:480px){.dlv-settings-heading{gap:6px}.dlv-heading-divider{min-width:4px}.dlv-settings-heading .dlv-star-badge{padding:3px 6px;font-size:10px}.dlv-preset-group{grid-template-columns:1fr}.dlv-pill{flex-wrap:wrap}.dlv-wave{flex-basis:90px}.dlv-status{flex-basis:100px}}
|
|
5295
5459
|
`;
|
|
@@ -5301,6 +5465,8 @@ function apply(ctx) {
|
|
|
5301
5465
|
const SettingsPanel = createLiveVoiceSettings(t, ctx.locale);
|
|
5302
5466
|
const controllers = /* @__PURE__ */ new Map();
|
|
5303
5467
|
const retiring = /* @__PURE__ */ new Set();
|
|
5468
|
+
const preferences = createSettingsClient();
|
|
5469
|
+
const settingsControllers = /* @__PURE__ */ new Set();
|
|
5304
5470
|
let disposed = false;
|
|
5305
5471
|
let voiceModeActive = false;
|
|
5306
5472
|
const publishVoiceContext = (entry, active = entry?.controller?.getSnapshot().conversation === true) => {
|
|
@@ -5327,16 +5493,19 @@ function apply(ctx) {
|
|
|
5327
5493
|
"recognitionProcessLocally",
|
|
5328
5494
|
"recognitionAutoInstall",
|
|
5329
5495
|
"voiceDetectionPreset",
|
|
5496
|
+
"voiceDetectionCustomSilenceMs",
|
|
5330
5497
|
"recognitionMaxUtteranceSeconds"
|
|
5331
5498
|
]);
|
|
5332
5499
|
const changesRecognition = (next) => Object.keys(next).some((key) => recognitionSettingKeys.has(key));
|
|
5333
5500
|
const recognitionFor = (settings, meter) => settings.recognitionEngine === "qwen-http" ? new QwenHttpRecognitionEngine({
|
|
5334
5501
|
meter,
|
|
5335
5502
|
voiceDetectionPreset: settings.voiceDetectionPreset,
|
|
5503
|
+
voiceDetectionCustomSilenceMs: settings.voiceDetectionCustomSilenceMs,
|
|
5336
5504
|
maxUtteranceSeconds: settings.recognitionMaxUtteranceSeconds
|
|
5337
5505
|
}) : settings.recognitionEngine === "whisper-http" ? new WhisperHttpRecognitionEngine({
|
|
5338
5506
|
meter,
|
|
5339
5507
|
voiceDetectionPreset: settings.voiceDetectionPreset,
|
|
5508
|
+
voiceDetectionCustomSilenceMs: settings.voiceDetectionCustomSilenceMs,
|
|
5340
5509
|
maxUtteranceSeconds: settings.recognitionMaxUtteranceSeconds
|
|
5341
5510
|
}) : new BrowserRecognitionEngine({
|
|
5342
5511
|
processLocally: settings.recognitionProcessLocally,
|
|
@@ -5346,6 +5515,38 @@ function apply(ctx) {
|
|
|
5346
5515
|
if (!disposed && !controller.disposed)
|
|
5347
5516
|
controller.patch({ error: error?.message || String(error) });
|
|
5348
5517
|
});
|
|
5518
|
+
async function savePreferences(controller, next) {
|
|
5519
|
+
try {
|
|
5520
|
+
return await preferences.save(next);
|
|
5521
|
+
} catch {
|
|
5522
|
+
const error = new Error(t("dsh-live-voice.settings.persistence.saveError"));
|
|
5523
|
+
if (!disposed && !controller.disposed) controller.patch({ error: error.message });
|
|
5524
|
+
throw error;
|
|
5525
|
+
}
|
|
5526
|
+
}
|
|
5527
|
+
const unsubscribePreferences = preferences.subscribe((settings) => {
|
|
5528
|
+
if (disposed) return;
|
|
5529
|
+
for (const entry of controllers.values()) {
|
|
5530
|
+
if (entry.closed) continue;
|
|
5531
|
+
const previous = entry.controller.getSnapshot().settings;
|
|
5532
|
+
if ([...recognitionSettingKeys].some((key) => previous[key] !== settings[key]))
|
|
5533
|
+
entry.controller.replaceRecognition(recognitionFor(settings, entry.controller.meter));
|
|
5534
|
+
entry.applySettings(settings);
|
|
5535
|
+
}
|
|
5536
|
+
for (const c of settingsControllers) {
|
|
5537
|
+
if (c.disposed) continue;
|
|
5538
|
+
const previous = c.getSnapshot().settings;
|
|
5539
|
+
if ([...recognitionSettingKeys].some((key) => previous[key] !== settings[key]))
|
|
5540
|
+
c.replaceRecognition(recognitionFor(settings, c.meter));
|
|
5541
|
+
c.applySavedSettings(settings);
|
|
5542
|
+
for (const engine of Object.values(c.engines)) engine.lang = settings.lang;
|
|
5543
|
+
run(c, c.refreshCapabilities());
|
|
5544
|
+
}
|
|
5545
|
+
});
|
|
5546
|
+
ctx.effect(
|
|
5547
|
+
() => () => unsubscribePreferences(),
|
|
5548
|
+
"dsh-live-voice: remove preferences subscription"
|
|
5549
|
+
);
|
|
5349
5550
|
function retire(entry) {
|
|
5350
5551
|
if (entry.closed) return;
|
|
5351
5552
|
entry.closed = true;
|
|
@@ -5374,6 +5575,10 @@ function apply(ctx) {
|
|
|
5374
5575
|
key,
|
|
5375
5576
|
draft: "",
|
|
5376
5577
|
pendingDraft: void 0,
|
|
5578
|
+
publishedDraft: void 0,
|
|
5579
|
+
publishedRevision: void 0,
|
|
5580
|
+
pendingRevision: void 0,
|
|
5581
|
+
staleDrafts: /* @__PURE__ */ new Set(),
|
|
5377
5582
|
composers: /* @__PURE__ */ new Map(),
|
|
5378
5583
|
refs: 0,
|
|
5379
5584
|
buttons: 0,
|
|
@@ -5381,14 +5586,7 @@ function apply(ctx) {
|
|
|
5381
5586
|
closed: false,
|
|
5382
5587
|
lastVoiceContextBody: null
|
|
5383
5588
|
};
|
|
5384
|
-
|
|
5385
|
-
try {
|
|
5386
|
-
settings = normalizeSettings(
|
|
5387
|
-
JSON.parse(localStorage.getItem("dsh-live-voice.settings") || "{}")
|
|
5388
|
-
);
|
|
5389
|
-
} catch {
|
|
5390
|
-
settings = normalizeSettings(null);
|
|
5391
|
-
}
|
|
5589
|
+
const settings = preferences.getSnapshot();
|
|
5392
5590
|
const engineBrowser = new BrowserSpeakingEngine({ lang: settings.lang || "pt-BR" });
|
|
5393
5591
|
const engineSay = new HostAudioSpeakingEngine({
|
|
5394
5592
|
endpoint: "/api/dsh-live-voice/say/speech",
|
|
@@ -5443,8 +5641,12 @@ function apply(ctx) {
|
|
|
5443
5641
|
if (disposed || entry.closed) return;
|
|
5444
5642
|
const owner = [...entry.composers.values()].at(-1);
|
|
5445
5643
|
if (!owner) return;
|
|
5644
|
+
if (entry.pendingDraft === void 0) entry.staleDrafts.clear();
|
|
5645
|
+
entry.staleDrafts.add(entry.draft);
|
|
5646
|
+
if (entry.publishedDraft !== void 0) entry.staleDrafts.add(entry.publishedDraft);
|
|
5446
5647
|
entry.draft = text;
|
|
5447
5648
|
entry.pendingDraft = text;
|
|
5649
|
+
entry.pendingRevision = entry.publishedRevision;
|
|
5448
5650
|
owner.actions.setDraft(text);
|
|
5449
5651
|
}
|
|
5450
5652
|
},
|
|
@@ -5517,14 +5719,7 @@ function apply(ctx) {
|
|
|
5517
5719
|
controller.updateSettings = (next) => {
|
|
5518
5720
|
if (disposed || entry.closed) return;
|
|
5519
5721
|
entry.applySettings(next);
|
|
5520
|
-
|
|
5521
|
-
try {
|
|
5522
|
-
localStorage.setItem("dsh-live-voice.settings", JSON.stringify(settings2));
|
|
5523
|
-
} catch {
|
|
5524
|
-
}
|
|
5525
|
-
for (const other of controllers.values()) {
|
|
5526
|
-
if (other !== entry && !other.closed) other.applySettings(settings2);
|
|
5527
|
-
}
|
|
5722
|
+
return savePreferences(controller, next);
|
|
5528
5723
|
};
|
|
5529
5724
|
for (const method of ["stopListening", "cancelDictation", "endConversation", "stopSpeech"]) {
|
|
5530
5725
|
const original = controller[method].bind(controller);
|
|
@@ -5549,7 +5744,12 @@ function apply(ctx) {
|
|
|
5549
5744
|
return ownership.run(
|
|
5550
5745
|
controller,
|
|
5551
5746
|
[...controllers.values()].map((other) => other.controller).concat([...retiring]),
|
|
5552
|
-
() => {
|
|
5747
|
+
async () => {
|
|
5748
|
+
try {
|
|
5749
|
+
await preferences.ready;
|
|
5750
|
+
} catch {
|
|
5751
|
+
throw new Error(t("dsh-live-voice.settings.persistence.loadError"));
|
|
5752
|
+
}
|
|
5553
5753
|
if (disposed || entry.closed || !entry.refs || request !== entry.request) return;
|
|
5554
5754
|
if (method !== "speak" && !entry.composers.size) return;
|
|
5555
5755
|
const result = original(...args);
|
|
@@ -5560,12 +5760,16 @@ function apply(ctx) {
|
|
|
5560
5760
|
};
|
|
5561
5761
|
}
|
|
5562
5762
|
controllers.set(key, entry);
|
|
5763
|
+
void preferences.ready.catch(() => {
|
|
5764
|
+
if (!entry.closed)
|
|
5765
|
+
controller.patch({ error: t("dsh-live-voice.settings.persistence.loadError") });
|
|
5766
|
+
});
|
|
5563
5767
|
run(controller, controller.refreshCapabilities());
|
|
5564
5768
|
return entry;
|
|
5565
5769
|
}
|
|
5566
5770
|
function useEntry(sessionId, kind) {
|
|
5567
|
-
const [entry, setEntry] =
|
|
5568
|
-
|
|
5771
|
+
const [entry, setEntry] = import_react54.default.useState(null);
|
|
5772
|
+
import_react54.default.useLayoutEffect(() => {
|
|
5569
5773
|
if (disposed) return;
|
|
5570
5774
|
const current = get(sessionId);
|
|
5571
5775
|
current.refs++;
|
|
@@ -5585,14 +5789,14 @@ function apply(ctx) {
|
|
|
5585
5789
|
function useComposer(entry, props) {
|
|
5586
5790
|
const subscribedInput = props.useInput?.((value) => value);
|
|
5587
5791
|
const input = subscribedInput ?? props.input;
|
|
5588
|
-
const token =
|
|
5589
|
-
|
|
5792
|
+
const token = import_react54.default.useRef({});
|
|
5793
|
+
import_react54.default.useLayoutEffect(() => {
|
|
5590
5794
|
if (!entry || entry.closed || disposed) return;
|
|
5591
5795
|
return () => {
|
|
5592
5796
|
entry.composers.delete(token.current);
|
|
5593
5797
|
};
|
|
5594
5798
|
}, [entry]);
|
|
5595
|
-
|
|
5799
|
+
import_react54.default.useLayoutEffect(() => {
|
|
5596
5800
|
if (!entry || entry.closed || disposed) return;
|
|
5597
5801
|
if (!input || typeof props.inputActions?.setDraft !== "function") return;
|
|
5598
5802
|
entry.composers.set(token.current, {
|
|
@@ -5618,13 +5822,17 @@ function apply(ctx) {
|
|
|
5618
5822
|
if (voiceModeActive && !entry.controller.getSnapshot().conversation)
|
|
5619
5823
|
run(entry.controller, entry.controller.startConversation());
|
|
5620
5824
|
}, [entry, input, props.inputActions]);
|
|
5621
|
-
|
|
5825
|
+
import_react54.default.useLayoutEffect(() => {
|
|
5622
5826
|
if (!entry || entry.closed || disposed || !input) return;
|
|
5623
5827
|
const published = typeof input.draft === "string" ? input.draft : "";
|
|
5828
|
+
entry.publishedDraft = published;
|
|
5829
|
+
entry.publishedRevision = input.draftRev;
|
|
5830
|
+
const newerRevision = Number.isInteger(input.draftRev) && Number.isInteger(entry.pendingRevision) && input.draftRev > entry.pendingRevision;
|
|
5624
5831
|
if (entry.pendingDraft === void 0) entry.draft = published;
|
|
5625
|
-
else if (published === entry.pendingDraft) {
|
|
5832
|
+
else if (published === entry.pendingDraft || newerRevision || !entry.staleDrafts.has(published)) {
|
|
5626
5833
|
entry.draft = published;
|
|
5627
5834
|
entry.pendingDraft = void 0;
|
|
5835
|
+
entry.staleDrafts.clear();
|
|
5628
5836
|
}
|
|
5629
5837
|
entry.controller.composerChanged(entry.draft);
|
|
5630
5838
|
});
|
|
@@ -5632,19 +5840,12 @@ function apply(ctx) {
|
|
|
5632
5840
|
function Buttons(props) {
|
|
5633
5841
|
const entry = useEntry(props.sessionId, "buttons");
|
|
5634
5842
|
useComposer(entry, props);
|
|
5635
|
-
return entry ? /* @__PURE__ */
|
|
5843
|
+
return entry ? /* @__PURE__ */ import_react54.default.createElement(ConversationControls, { controller: entry.controller }) : null;
|
|
5636
5844
|
}
|
|
5637
5845
|
function Settings() {
|
|
5638
|
-
const [controller, setController] =
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
try {
|
|
5642
|
-
settings = normalizeSettings(
|
|
5643
|
-
JSON.parse(localStorage.getItem("dsh-live-voice.settings") || "{}")
|
|
5644
|
-
);
|
|
5645
|
-
} catch {
|
|
5646
|
-
settings = normalizeSettings(null);
|
|
5647
|
-
}
|
|
5846
|
+
const [controller, setController] = import_react54.default.useState(null);
|
|
5847
|
+
import_react54.default.useEffect(() => {
|
|
5848
|
+
const settings = preferences.getSnapshot();
|
|
5648
5849
|
const browser = new BrowserSpeakingEngine({ lang: settings.lang });
|
|
5649
5850
|
const qwen = new QwenHttpSpeakingEngine({ lang: settings.lang });
|
|
5650
5851
|
const meter = new MicrophoneMeter();
|
|
@@ -5670,9 +5871,21 @@ function apply(ctx) {
|
|
|
5670
5871
|
c.speak = (...args) => ownership.run(
|
|
5671
5872
|
c,
|
|
5672
5873
|
[...controllers.values()].map((entry) => entry.controller).concat([...retiring]),
|
|
5673
|
-
() =>
|
|
5874
|
+
async () => {
|
|
5875
|
+
try {
|
|
5876
|
+
await preferences.ready;
|
|
5877
|
+
} catch {
|
|
5878
|
+
throw new Error(t("dsh-live-voice.settings.persistence.loadError"));
|
|
5879
|
+
}
|
|
5880
|
+
if (!disposed && !c.disposed) return speak(...args);
|
|
5881
|
+
}
|
|
5674
5882
|
);
|
|
5675
5883
|
const update = c.updateSettings.bind(c);
|
|
5884
|
+
c.applySavedSettings = update;
|
|
5885
|
+
settingsControllers.add(c);
|
|
5886
|
+
void preferences.ready.catch(
|
|
5887
|
+
() => c.patch({ error: t("dsh-live-voice.settings.persistence.loadError") })
|
|
5888
|
+
);
|
|
5676
5889
|
let settingsRevision = 0;
|
|
5677
5890
|
c.updateSettings = async (next) => {
|
|
5678
5891
|
const revision = ++settingsRevision;
|
|
@@ -5681,10 +5894,11 @@ function apply(ctx) {
|
|
|
5681
5894
|
qwen.lang = c.getSnapshot().settings.lang;
|
|
5682
5895
|
const settings2 = c.getSnapshot().settings;
|
|
5683
5896
|
if (changesRecognition(next)) c.replaceRecognition(recognitionFor(settings2, c.meter));
|
|
5684
|
-
|
|
5897
|
+
const saved = savePreferences(c, next);
|
|
5685
5898
|
run(c, c.refreshCapabilities());
|
|
5686
5899
|
const active = [...controllers.values()];
|
|
5687
|
-
await Promise.
|
|
5900
|
+
const [savedSettings] = await Promise.all([
|
|
5901
|
+
saved,
|
|
5688
5902
|
c.endConversation(),
|
|
5689
5903
|
...active.map((entry) => entry.controller.endConversation())
|
|
5690
5904
|
]);
|
|
@@ -5692,8 +5906,10 @@ function apply(ctx) {
|
|
|
5692
5906
|
for (const entry of controllers.values()) {
|
|
5693
5907
|
if (entry.closed) continue;
|
|
5694
5908
|
if (changesRecognition(next))
|
|
5695
|
-
entry.controller.replaceRecognition(
|
|
5696
|
-
|
|
5909
|
+
entry.controller.replaceRecognition(
|
|
5910
|
+
recognitionFor(savedSettings, entry.controller.meter)
|
|
5911
|
+
);
|
|
5912
|
+
entry.applySettings(savedSettings);
|
|
5697
5913
|
run(entry.controller, entry.controller.refreshCapabilities());
|
|
5698
5914
|
}
|
|
5699
5915
|
};
|
|
@@ -5703,24 +5919,25 @@ function apply(ctx) {
|
|
|
5703
5919
|
refresh();
|
|
5704
5920
|
return () => {
|
|
5705
5921
|
document.removeEventListener("dsh-live-voice:capabilitieschanged", refresh);
|
|
5922
|
+
settingsControllers.delete(c);
|
|
5706
5923
|
void c.dispose();
|
|
5707
5924
|
};
|
|
5708
5925
|
}, []);
|
|
5709
|
-
return controller ? /* @__PURE__ */
|
|
5926
|
+
return controller ? /* @__PURE__ */ import_react54.default.createElement(SettingsPanel, { controller }) : null;
|
|
5710
5927
|
}
|
|
5711
5928
|
registerSettingsSlot(ctx, Settings, () => t("dsh-live-voice.commons.pluginName"));
|
|
5712
5929
|
function Dock(props) {
|
|
5713
5930
|
const entry = useEntry(props.sessionId, "dock");
|
|
5714
5931
|
useComposer(entry, props);
|
|
5715
|
-
return entry ? /* @__PURE__ */
|
|
5932
|
+
return entry ? /* @__PURE__ */ import_react54.default.createElement(ConversationStatusBar, { controller: entry.controller }) : null;
|
|
5716
5933
|
}
|
|
5717
5934
|
function QuestionStatusView({ entry }) {
|
|
5718
|
-
const snapshot =
|
|
5935
|
+
const snapshot = import_react54.default.useSyncExternalStore(
|
|
5719
5936
|
entry.controller.subscribe,
|
|
5720
5937
|
entry.controller.getSnapshot
|
|
5721
5938
|
);
|
|
5722
|
-
const [overlayStyle, setOverlayStyle] =
|
|
5723
|
-
|
|
5939
|
+
const [overlayStyle, setOverlayStyle] = import_react54.default.useState();
|
|
5940
|
+
import_react54.default.useLayoutEffect(() => {
|
|
5724
5941
|
if (!snapshot.answeringQuestion) return;
|
|
5725
5942
|
const seat = document.querySelector("[data-composer-seat]");
|
|
5726
5943
|
if (!seat) return;
|
|
@@ -5742,7 +5959,7 @@ function apply(ctx) {
|
|
|
5742
5959
|
}, [snapshot.answeringQuestion]);
|
|
5743
5960
|
const target = typeof document === "undefined" ? null : document.body;
|
|
5744
5961
|
return snapshot.answeringQuestion && target && overlayStyle ? (0, import_react_dom.createPortal)(
|
|
5745
|
-
/* @__PURE__ */
|
|
5962
|
+
/* @__PURE__ */ import_react54.default.createElement(
|
|
5746
5963
|
ConversationStatusBar,
|
|
5747
5964
|
{
|
|
5748
5965
|
controller: entry.controller,
|
|
@@ -5756,19 +5973,19 @@ function apply(ctx) {
|
|
|
5756
5973
|
}
|
|
5757
5974
|
function QuestionStatus(props) {
|
|
5758
5975
|
const entry = useEntry(props.sessionId, "question-status");
|
|
5759
|
-
return entry ? /* @__PURE__ */
|
|
5976
|
+
return entry ? /* @__PURE__ */ import_react54.default.createElement(QuestionStatusView, { entry }) : null;
|
|
5760
5977
|
}
|
|
5761
5978
|
function ActionView({ entry, messageId }) {
|
|
5762
|
-
const snapshot =
|
|
5979
|
+
const snapshot = import_react54.default.useSyncExternalStore(
|
|
5763
5980
|
entry.controller.subscribe,
|
|
5764
5981
|
entry.controller.getSnapshot
|
|
5765
5982
|
);
|
|
5766
|
-
const chat =
|
|
5983
|
+
const chat = import_react54.default.useSyncExternalStore(entry.subscribeChat, entry.readChat);
|
|
5767
5984
|
const message2 = addressedTurn(assistantMessages(chat), messageId);
|
|
5768
5985
|
const capability = snapshot.capabilities[snapshot.settings.engine];
|
|
5769
5986
|
const active = snapshot.speaking && message2.id === snapshot.activeMessageId;
|
|
5770
5987
|
const unavailable = capability?.supported !== true;
|
|
5771
|
-
return /* @__PURE__ */
|
|
5988
|
+
return /* @__PURE__ */ import_react54.default.createElement(
|
|
5772
5989
|
SpeakButton,
|
|
5773
5990
|
{
|
|
5774
5991
|
active,
|
|
@@ -5783,7 +6000,7 @@ function apply(ctx) {
|
|
|
5783
6000
|
}
|
|
5784
6001
|
function Action(props) {
|
|
5785
6002
|
const entry = useEntry(props.sessionId, "action");
|
|
5786
|
-
return entry ? /* @__PURE__ */
|
|
6003
|
+
return entry ? /* @__PURE__ */ import_react54.default.createElement(ActionView, { entry, messageId: props.messageId }) : null;
|
|
5787
6004
|
}
|
|
5788
6005
|
ctx.effect(() => {
|
|
5789
6006
|
const style = document.createElement("style");
|