@alexkroman1/aai-ui 1.16.0 → 3.0.0
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/dist/_module-url-C_4gRVL0.js +15 -0
- package/dist/audio.d.ts +85 -11
- package/dist/audio.js +112 -83
- package/dist/chat-view-C1oJxsWz.js +129 -0
- package/dist/client-config.d.ts +6 -6
- package/dist/components/chat-view.d.ts +1 -8
- package/dist/components/chat-view.js +2 -2
- package/dist/components/console-shell.d.ts +37 -0
- package/dist/components/controls.js +1 -1
- package/dist/components/url-chips.d.ts +1 -4
- package/dist/context.d.ts +1 -1
- package/dist/context.js +1 -4
- package/dist/{controls-BbZcmnJf.js → controls-DV368uhb.js} +2 -5
- package/dist/default-client/assets/_module-url-BX0RuRU2.js +1 -0
- package/dist/default-client/assets/audio-BGHiiDY_.js +1 -0
- package/dist/default-client/assets/capture-processor-BLPsqnGl.js +90 -0
- package/dist/default-client/assets/{index-BunwIXSP.js → index-DXf7-M0Q.js} +156 -36
- package/dist/default-client/assets/index-Dv-Q5VRL.css +2 -0
- package/dist/default-client/assets/playback-processor-B_T_1KQP.js +278 -0
- package/dist/default-client/index.html +2 -2
- package/dist/define-client-Cyf1jqAl.js +150 -0
- package/dist/define-client.d.ts +0 -9
- package/dist/define-client.js +1 -1
- package/dist/index.d.ts +2 -5
- package/dist/index.js +7 -5
- package/dist/{session-core-B64kau_v.js → session-core-BM3WHeoY.js} +36 -128
- package/dist/session-core-messages.d.ts +0 -4
- package/dist/session-core-types.d.ts +1 -27
- package/dist/session-core.js +1 -1
- package/dist/types.d.ts +24 -1
- package/dist/types.js +32 -2
- package/dist/worklets/_module-url.d.ts +10 -0
- package/dist/worklets/capture-processor.d.ts +3 -3
- package/dist/worklets/capture-processor.js +35 -52
- package/dist/worklets/playback-processor.d.ts +3 -3
- package/dist/worklets/playback-processor.js +149 -26
- package/package.json +2 -2
- package/dist/chat-view-gi6FccZq.js +0 -193
- package/dist/components/sync-chat-view.d.ts +0 -18
- package/dist/components/text-controls.d.ts +0 -14
- package/dist/default-client/assets/audio-DNDZgEZp.js +0 -1
- package/dist/default-client/assets/capture-processor-UlKEKyIW.js +0 -108
- package/dist/default-client/assets/index-BogmeUln.css +0 -2
- package/dist/default-client/assets/playback-processor-C5HVRVbu.js +0 -156
- package/dist/define-client-DdpijqAu.js +0 -906
- package/dist/session-core-upload.d.ts +0 -16
- package/dist/sync-mic.d.ts +0 -93
- package/dist/sync-session.d.ts +0 -49
- package/dist/sync-vad.d.ts +0 -54
package/dist/context.js
CHANGED
|
@@ -36,10 +36,7 @@ function useSession() {
|
|
|
36
36
|
resetState: core.resetState,
|
|
37
37
|
reset: core.reset,
|
|
38
38
|
disconnect: core.disconnect,
|
|
39
|
-
toggle: core.toggle
|
|
40
|
-
startRecording: core.startRecording,
|
|
41
|
-
stopRecording: core.stopRecording,
|
|
42
|
-
sendAudioFile: core.sendAudioFile
|
|
39
|
+
toggle: core.toggle
|
|
43
40
|
}), [snapshot, core]);
|
|
44
41
|
}
|
|
45
42
|
/**
|
|
@@ -14,9 +14,6 @@ const COPIED_FEEDBACK_MS = 1500;
|
|
|
14
14
|
* The label is what makes a pair of these readable — on its own a bare URL
|
|
15
15
|
* leaves you guessing whether it's the page or the socket.
|
|
16
16
|
*
|
|
17
|
-
* Exported for the sync-transport shell, which labels its HTTP endpoint
|
|
18
|
-
* without a session snapshot to read from.
|
|
19
|
-
*
|
|
20
17
|
* @internal
|
|
21
18
|
*/
|
|
22
19
|
function UrlChip({ label, url, hint, testId, className }) {
|
|
@@ -91,7 +88,7 @@ function ApiUrlChip({ className }) {
|
|
|
91
88
|
* The UI and API URLs side by side. They answer the same question — "how do I
|
|
92
89
|
* reach this agent?" — so they belong together and each needs its label to be
|
|
93
90
|
* told apart. Rendered by the default shell in every session mode (S2S,
|
|
94
|
-
* pipeline
|
|
91
|
+
* pipeline).
|
|
95
92
|
*
|
|
96
93
|
* @public
|
|
97
94
|
*/
|
|
@@ -140,4 +137,4 @@ const Controls = memo(function Controls({ className }) {
|
|
|
140
137
|
});
|
|
141
138
|
});
|
|
142
139
|
//#endregion
|
|
143
|
-
export {
|
|
140
|
+
export { UiUrlChip as i, ApiUrlChip as n, SessionUrlChips as r, Controls as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){return URL.createObjectURL(new Blob([e],{type:`application/javascript`}))}export{e as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,o as t,t as n}from"./index-DXf7-M0Q.js";function r(e){let t=new Int16Array(e.length),n=0;for(let r of e){let e=Math.max(-1,Math.min(1,r));t[n++]=e<0?e*32768:e*32767}return t}function i(e,t,n){if(e!==t)throw Error(`Browser refused the ${n} sample rate: asked for ${t} Hz, got ${e} Hz`)}function a(e){e.then(e=>{for(let t of e.getTracks())t.stop()}).catch(()=>{})}function o(e,t,n){let r=new AudioWorkletNode(e,`capture-processor`,{channelCount:1,channelCountMode:`explicit`}),i=null;return r.port.onmessage=e=>{let r=e.data;r.event===`chunk`&&r.buffer?t(r.buffer):r.event===`silent`?n?.():r.event===`stopped`&&(i?.(),i=null)},{node:r,start(){r.port.postMessage({event:`start`})},stop(){return new Promise(e=>{let t=setTimeout(e,250);i=()=>{clearTimeout(t),e()},r.port.postMessage({event:`stop`})})}}}async function s(r){let{sttSampleRate:s,ttsSampleRate:c,captureWorkletSrc:l,playbackWorkletSrc:u,onMicData:d,onError:f,onPlaybackStats:p,onMicSilent:m}=r,h=new AudioContext({sampleRate:c,latencyHint:`playback`}),g=s===c,_=g?h:new AudioContext({sampleRate:s,latencyHint:`interactive`});async function v(){let e=g?[h]:[h,_];await Promise.all(e.map(e=>e.close().catch(e=>{console.warn(`AudioContext close failed:`,e)})))}let y=navigator.mediaDevices.getUserMedia({audio:{deviceId:{ideal:`default`},...n}}),b;try{[b]=await Promise.all([y,h.resume(),_.resume(),_.audioWorklet.addModule(l),h.audioWorklet.addModule(u)]),i(_.sampleRate,s,`capture`),i(h.sampleRate,c,`playback`)}catch(e){throw a(y),await v(),e}let x=_.createMediaStreamSource(b),S=o(_,d,m);x.connect(S.node),S.node.onprocessorerror=()=>{let e=Error(`Audio capture worklet crashed`);console.error(`[aai-ui]`,e.message),f?.(e)},S.start();let C=null,w=null,T=new AbortController;function E(){if(C)return C;let e=new AudioWorkletNode(h,`playback-processor`);return e.connect(h.destination),e.port.onmessage=e=>{if(e.data.event===`stop`){let t=e.data.stats;if(t&&t.concealedSamples>0&&p?.(t),e.data.reason===`interrupt`)return;w?.(),w=null}},e.onprocessorerror=()=>{let e=Error(`Audio playback worklet crashed`);console.error(`[aai-ui]`,e.message),w?.(),w=null,f?.(e)},C=e,e}let D={enqueue(e){T.signal.aborted||e.byteLength!==0&&E().port.postMessage({event:`write`,buffer:new Uint8Array(e)},[e])},done(){return!C||(C.port.postMessage({event:`done`}),h.state!==`running`)?Promise.resolve():new Promise(n=>{w?.();let r=()=>{clearInterval(i),clearTimeout(a),w===r&&(w=null),n()},i=setInterval(()=>{h.state!==`running`&&r()},t),a=setTimeout(r,e);w=r})},flush(){C&&(w?.(),w=null,C.port.postMessage({event:`interrupt`}))},async close(){if(!T.signal.aborted){T.abort(),await S.stop(),x.disconnect(),S.node.disconnect(),C&&C.disconnect();for(let e of b.getTracks())e.stop();await v()}},async[Symbol.asyncDispose](){await D.close()}};return D}export{i as assertGranted,o as createCaptureNode,s as createVoiceIO,r as floatToPcm16,a as releaseStreamOnFailure};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import{n as e,r as t}from"./index-DXf7-M0Q.js";import{t as n}from"./_module-url-BX0RuRU2.js";var r=n(`
|
|
2
|
+
class CaptureProcessor extends AudioWorkletProcessor {
|
|
3
|
+
constructor(options) {
|
|
4
|
+
super();
|
|
5
|
+
this.recording = false;
|
|
6
|
+
const opts = options.processorOptions || {};
|
|
7
|
+
// The context runs at the STT rate, so this is both the input and the
|
|
8
|
+
// output rate — there is nothing to convert.
|
|
9
|
+
this.rate = opts.sampleRate || sampleRate;
|
|
10
|
+
// Int16 accumulation buffer: flushed to the main thread as one transferred
|
|
11
|
+
// ArrayBuffer once ~bufferSeconds of samples are batched. Sized 2x the
|
|
12
|
+
// flush target so a whole render quantum always fits before flushing.
|
|
13
|
+
this.targetSamples = Math.max(1, Math.round(this.rate * (opts.bufferSeconds || ${e})));
|
|
14
|
+
this.pending = new Int16Array(this.targetSamples * 2);
|
|
15
|
+
this.pendingLen = 0;
|
|
16
|
+
// Dead-mic probe: samples left to inspect before concluding the device
|
|
17
|
+
// delivers nothing but digital silence. Only consumed while recording, so
|
|
18
|
+
// the cost disappears after the window (or after the first real sample).
|
|
19
|
+
this.probeSamplesLeft = Math.round(
|
|
20
|
+
(this.rate * (opts.silenceProbeMs ?? ${t})) / 1000,
|
|
21
|
+
);
|
|
22
|
+
this.port.onmessage = (e) => {
|
|
23
|
+
if (e.data.event === 'start') this.recording = true;
|
|
24
|
+
else if (e.data.event === 'stop') {
|
|
25
|
+
// Final flush so the tail of speech isn't dropped on close, then ack
|
|
26
|
+
// so the host knows the tail chunk (if any) has been posted and it is
|
|
27
|
+
// safe to tear the context down.
|
|
28
|
+
this.flush();
|
|
29
|
+
this.recording = false;
|
|
30
|
+
this.port.postMessage({ event: 'stopped' });
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Convert Float32 -> Int16 and append to the pending batch. Writes through
|
|
36
|
+
// an Int16Array directly (assignment truncates like DataView.setInt16).
|
|
37
|
+
accumulate(samples) {
|
|
38
|
+
let buf = this.pending;
|
|
39
|
+
if (this.pendingLen + samples.length > buf.length) {
|
|
40
|
+
// Defensive: only reachable if a render quantum outproduces the 1x
|
|
41
|
+
// headroom above the flush target (never with 128-sample quanta).
|
|
42
|
+
const grown = new Int16Array((this.pendingLen + samples.length) * 2);
|
|
43
|
+
grown.set(buf.subarray(0, this.pendingLen));
|
|
44
|
+
this.pending = grown;
|
|
45
|
+
buf = grown;
|
|
46
|
+
}
|
|
47
|
+
for (let i = 0; i < samples.length; i++) {
|
|
48
|
+
const s = Math.max(-1, Math.min(1, samples[i]));
|
|
49
|
+
buf[this.pendingLen++] = s < 0 ? s * 0x8000 : s * 0x7fff;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Post the batched samples as one transferred ArrayBuffer and reset.
|
|
54
|
+
flush() {
|
|
55
|
+
if (this.pendingLen === 0) return;
|
|
56
|
+
const buffer = this.pending.buffer.slice(0, this.pendingLen * 2);
|
|
57
|
+
this.pendingLen = 0;
|
|
58
|
+
this.port.postMessage({ event: 'chunk', buffer }, [buffer]);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Watch the first window of input for any nonzero sample. One is enough to
|
|
62
|
+
// prove the device is live — a real mic in a quiet room still carries a
|
|
63
|
+
// noise floor, so all-zeros means muted, wrong input, or no input at all.
|
|
64
|
+
probeForSilence(channel) {
|
|
65
|
+
for (let i = 0; i < channel.length; i++) {
|
|
66
|
+
if (channel[i] !== 0) {
|
|
67
|
+
this.probeSamplesLeft = 0;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
this.probeSamplesLeft -= channel.length;
|
|
72
|
+
if (this.probeSamplesLeft <= 0) {
|
|
73
|
+
this.port.postMessage({ event: 'silent' });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
process(inputs) {
|
|
78
|
+
const input = inputs[0];
|
|
79
|
+
if (!input || !input[0] || !this.recording) return true;
|
|
80
|
+
|
|
81
|
+
if (this.probeSamplesLeft > 0) this.probeForSilence(input[0]);
|
|
82
|
+
|
|
83
|
+
this.accumulate(input[0]);
|
|
84
|
+
if (this.pendingLen >= this.targetSamples) this.flush();
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
registerProcessor('capture-processor', CaptureProcessor);
|
|
90
|
+
`);export{r as default};
|