@goodandready/dsh-tts 0.4.1 → 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/breaker.js +64 -0
- package/lib/cache.js +1 -1
- package/lib/client-src/10-styles.js +8 -8
- package/lib/client-src/20-player.js +57 -19
- package/lib/client-src/30-locales.js +36 -0
- package/lib/client-src/40-editors.js +30 -50
- package/lib/client-src/50-settings.js +86 -29
- package/lib/client-src/60-card-dock.js +50 -44
- package/lib/client.js +267 -150
- package/lib/index.js +27 -4
- package/lib/local.js +5 -7
- package/lib/providers/constants.js +61 -0
- package/lib/providers.js +3 -61
- package/lib/routes.js +15 -8
- package/lib/stream-hub.js +61 -9
- package/lib/text.js +3 -3
- package/package.json +1 -1
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
React.createElement('button', {
|
|
48
48
|
type: 'button', className: 'dts-link', style: { flex: 1, textAlign: 'left', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },
|
|
49
49
|
title: text,
|
|
50
|
-
onClick: () => playStandalone(text),
|
|
50
|
+
onClick: () => playStandalone(text), title: text,
|
|
51
51
|
}, text.slice(0, 80)),
|
|
52
52
|
React.createElement('button', {
|
|
53
53
|
type: 'button', className: 'dts-link', title: '★',
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
)
|
|
57
57
|
return React.createElement('div', { style: { position: 'relative', display: 'inline-flex', gap: '4px', alignItems: 'center' } },
|
|
58
58
|
React.createElement('button', {
|
|
59
|
-
type: 'button', className: 'dts-link', title: '
|
|
59
|
+
type: 'button', className: 'dts-link', title: t('recent') || 'Recent',
|
|
60
60
|
onClick: () => setShowList((v) => !v),
|
|
61
61
|
}, '☰'),
|
|
62
62
|
showList ? React.createElement('div', {
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
lists.favs.map((text) => row(text, true)),
|
|
68
68
|
React.createElement('div', { className: 'dts-h' }, '⟳'),
|
|
69
69
|
lists.items.filter((x) => !lists.favs.includes(x)).map((text) => row(text, false)),
|
|
70
|
-
!lists.items.length ? React.createElement('span', { className: 'dts-sub' }, '
|
|
70
|
+
!lists.items.length ? React.createElement('span', { className: 'dts-sub' }, t('recentEmpty') || 'No recent speech yet') : null,
|
|
71
71
|
) : null,
|
|
72
72
|
React.createElement('button', {
|
|
73
73
|
type: 'button',
|
|
@@ -85,11 +85,10 @@
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
-
*
|
|
88
|
+
* Fallback translator for the dock.
|
|
89
89
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* принципе, и привязка — единственный способ дать ей язык.
|
|
90
|
+
* Bound at registration for the page lifetime. The core draws the slot label;
|
|
91
|
+
* props.t never reaches it, so the binder is the only way to give it a language.
|
|
93
92
|
*/
|
|
94
93
|
let fallbackDockText = (key) => key
|
|
95
94
|
|
|
@@ -124,7 +123,7 @@
|
|
|
124
123
|
ctx.effect(() => {
|
|
125
124
|
const undo = [addLocale('en', en), addLocale('ru', ru)]
|
|
126
125
|
return () => { for (const off of undo) off() }
|
|
127
|
-
}, 'dsh-tts:
|
|
126
|
+
}, 'dsh-tts: locales')
|
|
128
127
|
// The sidebar draws the section label, not our component: props.t never
|
|
129
128
|
// reaches it, so bind the translator to the namespace ourselves.
|
|
130
129
|
const t = ctx.locale.bind(NS)
|
|
@@ -168,7 +167,7 @@
|
|
|
168
167
|
exports.apply = function apply(ctx) {
|
|
169
168
|
registerSettings(ctx)
|
|
170
169
|
registerSpeaker(ctx)
|
|
171
|
-
ctx.effect(() => listenForVoice(), 'dsh-tts:
|
|
170
|
+
ctx.effect(() => listenForVoice(), 'dsh-tts: barge-in on user speech')
|
|
172
171
|
ctx.effect(() => {
|
|
173
172
|
if (typeof window === 'undefined') return () => {}
|
|
174
173
|
const onKey = (e) => {
|
|
@@ -178,47 +177,54 @@
|
|
|
178
177
|
}
|
|
179
178
|
window.addEventListener('keydown', onKey)
|
|
180
179
|
return () => window.removeEventListener('keydown', onKey)
|
|
181
|
-
}, 'dsh-tts:
|
|
180
|
+
}, 'dsh-tts: hotkeys')
|
|
182
181
|
ctx.effect(() => {
|
|
183
182
|
if (typeof window === 'undefined' || typeof EventSource === 'undefined') return () => {}
|
|
184
183
|
let es = null
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
184
|
+
let closed = false
|
|
185
|
+
let attempt = 0
|
|
186
|
+
let retryTimer = null
|
|
187
|
+
const onItem = (e) => {
|
|
188
|
+
try {
|
|
189
|
+
const item = JSON.parse(e.data)
|
|
190
|
+
if (!item || !item.id) return
|
|
191
|
+
if (seenIds.has(item.id)) return
|
|
192
|
+
seenIds.add(item.id)
|
|
193
|
+
if (seenIds.size > 500) {
|
|
194
|
+
const first = seenIds.values().next().value
|
|
195
|
+
seenIds.delete(first)
|
|
196
|
+
}
|
|
197
|
+
player.queue.push(item)
|
|
198
|
+
drainQueue()
|
|
199
|
+
} catch { /* ignore malformed SSE frame */ }
|
|
200
|
+
}
|
|
201
|
+
const connect = () => {
|
|
202
|
+
if (closed) return
|
|
203
|
+
try {
|
|
204
|
+
es = new EventSource('/dsh-tts/stream')
|
|
205
|
+
es.addEventListener('open', () => { attempt = 0 })
|
|
206
|
+
es.addEventListener('utterance', onItem)
|
|
207
|
+
es.addEventListener('chime', onItem)
|
|
208
|
+
es.onerror = () => {
|
|
209
|
+
try { es.close() } catch { /* already closed */ }
|
|
210
|
+
if (closed) return
|
|
211
|
+
attempt += 1
|
|
212
|
+
const delay = Math.min(15000, 500 * Math.pow(2, Math.min(attempt, 5)))
|
|
213
|
+
retryTimer = setTimeout(connect, delay)
|
|
214
|
+
}
|
|
215
|
+
} catch { /* EventSource constructor failed — poll remains */ }
|
|
216
|
+
}
|
|
217
|
+
connect()
|
|
218
|
+
return () => {
|
|
219
|
+
closed = true
|
|
220
|
+
if (retryTimer) clearTimeout(retryTimer)
|
|
221
|
+
if (es) { try { es.close() } catch { /* already closed */ } }
|
|
222
|
+
}
|
|
223
|
+
}, 'dsh-tts: SSE audio stream')
|
|
218
224
|
ctx.effect(() => {
|
|
219
225
|
const timer = setInterval(pollPending, 1000)
|
|
220
226
|
return () => clearInterval(timer)
|
|
221
|
-
}, 'dsh-tts:
|
|
227
|
+
}, 'dsh-tts: poll pending audio')
|
|
222
228
|
}
|
|
223
229
|
return module.exports
|
|
224
230
|
},
|