@goodandready/dsh-voice 0.4.0 → 0.4.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/lib/client.js +52 -8
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -425,6 +425,7 @@ window.__ModuleLoader__.load({
|
|
|
425
425
|
'.dvs-save{background:var(--dsw-alias-brand-primary);color:#fff;border:none;border-radius:6px;padding:7px 14px;font-size:13px;cursor:pointer}' +
|
|
426
426
|
'.dvs-card{display:flex;flex-direction:column;gap:6px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:10px}' +
|
|
427
427
|
'.dvs-card input,.dvs-card select{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);border-radius:6px;padding:6px 8px;font-size:13px}' +
|
|
428
|
+
'.dvs-wait{font-size:13px;color:var(--dsw-alias-label-secondary);line-height:1.5;max-width:520px}' +
|
|
428
429
|
'.dvs-ok{font-size:12px;color:var(--dsw-alias-state-success-primary)}' +
|
|
429
430
|
'.dvs-bad{font-size:12px;color:var(--dsw-alias-state-error-primary)}'
|
|
430
431
|
const setCssId = 'dsh-voice/settings.module.css'
|
|
@@ -527,7 +528,13 @@ window.__ModuleLoader__.load({
|
|
|
527
528
|
|
|
528
529
|
function VoiceSection(props) {
|
|
529
530
|
const ctx = props.ctx
|
|
530
|
-
|
|
531
|
+
// На странице, открытой не с localhost, ядро отключает настройки целиком:
|
|
532
|
+
// общий вид документа не читается, каждый раздел получает "unavailable",
|
|
533
|
+
// запись выбрасывается. Сервер это ограничение не разделяет, и плагин
|
|
534
|
+
// dsh-lanmode поднимает ту же механику поверх тех же вызовов. Если он
|
|
535
|
+
// установлен — берём его службу, иначе ядровую: на loopback-странице она
|
|
536
|
+
// работает штатно.
|
|
537
|
+
const scope = ((ctx.get && ctx.get('lanSettings')) || ctx.settingsScope).bind({ namespace: NS })
|
|
531
538
|
const [snap, setSnap] = React.useState(null)
|
|
532
539
|
const [draft, setDraft] = React.useState(null)
|
|
533
540
|
const [saved, setSaved] = React.useState(false)
|
|
@@ -541,19 +548,56 @@ window.__ModuleLoader__.load({
|
|
|
541
548
|
return () => { alive = false; off() }
|
|
542
549
|
}, [])
|
|
543
550
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
551
|
+
// Снимок приходит со статусом, и он важнее самого значения.
|
|
552
|
+
// loading — ответа хоста ещё нет;
|
|
553
|
+
// unavailable — хост ответил, но наш namespace ему пока неизвестен:
|
|
554
|
+
// так бывает, если страницу открыли в первые секунды
|
|
555
|
+
// после старта, пока плагины ещё регистрируются;
|
|
556
|
+
// ready — значения на месте.
|
|
557
|
+
// При unavailable поле writable берётся из документа и остаётся true,
|
|
558
|
+
// поэтому без проверки статуса карточка рисует пустую, но с виду рабочую
|
|
559
|
+
// форму — именно так этот баг и выглядел.
|
|
560
|
+
const ready = !!snap && snap.status === 'ready'
|
|
561
|
+
const value = ready && snap.value ? snap.value : {}
|
|
562
|
+
const writable = ready && snap.writable !== false
|
|
563
|
+
|
|
564
|
+
// Зеркало настроек в браузере перечитывается только на запись значения и
|
|
565
|
+
// на переподключение; появление namespace таким сигналом не считается.
|
|
566
|
+
// Значит само оно не починится — просим перечитать, пока не готово.
|
|
567
|
+
// Зеркало общее, так что это чинит и остальные разделы настроек.
|
|
568
|
+
React.useEffect(() => {
|
|
569
|
+
if (ready) return undefined
|
|
570
|
+
let tries = 0
|
|
571
|
+
const timer = setInterval(() => {
|
|
572
|
+
if (tries >= 15) { clearInterval(timer); return }
|
|
573
|
+
tries += 1
|
|
574
|
+
try { ((ctx.get && ctx.get('lanSettings')) || ctx.settingsScope).describe().load() } catch (e) { /* сервис ещё не поднялся */ }
|
|
575
|
+
}, 1000)
|
|
576
|
+
return () => clearInterval(timer)
|
|
577
|
+
}, [ready])
|
|
578
|
+
|
|
579
|
+
// Черновик сеем только из готового снимка. Раньше он заполнялся из
|
|
580
|
+
// первого пришедшего, и пустой замораживался навсегда: следующий снимок
|
|
581
|
+
// уже не пересевал его, и карточка оставалась пустой до перезагрузки.
|
|
582
|
+
React.useEffect(() => { if (ready && draft === null) setDraft(JSON.parse(JSON.stringify(value))) }, [ready, draft, value])
|
|
547
583
|
// Клиент должен знать порог VAD и окно отмены — они живут в тех же настройках.
|
|
548
584
|
React.useEffect(() => {
|
|
549
|
-
if (!
|
|
585
|
+
if (!ready) return
|
|
550
586
|
voice.settings = {
|
|
551
587
|
vadSilenceMs: Number(value && value.dictation && value.dictation.vadSilenceMs) || 700,
|
|
552
588
|
autoSendMs: Number(value && value.message && value.message.autoSendMs) || 4000,
|
|
553
589
|
}
|
|
554
|
-
}, [
|
|
555
|
-
|
|
556
|
-
if (!
|
|
590
|
+
}, [ready, value])
|
|
591
|
+
|
|
592
|
+
if (!ready) {
|
|
593
|
+
const waiting = !snap || snap.status === 'loading'
|
|
594
|
+
return React.createElement('div', { className: 'dvs-wrap' },
|
|
595
|
+
React.createElement('div', { className: 'dvs-wait' }, waiting
|
|
596
|
+
? 'Загрузка настроек…'
|
|
597
|
+
: 'Харнесс ещё не объявил настройки плагина. Если он только что перезапустился, '
|
|
598
|
+
+ 'раздел появится сам через несколько секунд.'),
|
|
599
|
+
)
|
|
600
|
+
}
|
|
557
601
|
|
|
558
602
|
const setIn = (mode, key, v) => setDraft((d) => {
|
|
559
603
|
const next = JSON.parse(JSON.stringify(d || {}))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-voice",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Voice input for DeepSeek Harness: dictation chunked by pauses and voice messages, each with its own provider fallback chain (Deepgram, Groq, HuggingFace, local whisper.cpp, plus any OpenAI-compatible API of your own).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|