@alexkroman1/aai-ui 6.5.0 → 6.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{_utils-CsqbIVGI.js → _utils-B6498_bm.js} +13 -1
- package/dist/_utils.d.ts +10 -0
- package/dist/_workflow-files.d.ts +16 -0
- package/dist/audio.js +15 -7
- package/dist/{chat-view-CK61bWWx.js → chat-view-ByQFf94G.js} +2 -2
- package/dist/components/button.js +30 -25
- package/dist/components/chat-view.js +1 -1
- package/dist/components/controls.js +1 -1
- package/dist/components/message-list.js +1 -1
- package/dist/components/tool-call-block.js +1 -1
- package/dist/{controls-Cy_YVfsa.js → controls-CjG91QJ4.js} +1 -1
- package/dist/default-client/assets/audio-CoVe8N48.js +1 -0
- package/dist/default-client/assets/index-CepvxbNU.js +293 -0
- package/dist/default-client/index.html +1 -1
- package/dist/define-client.js +5 -5
- package/dist/hooks.js +4 -3
- package/dist/index.js +69 -20
- package/dist/{message-list-BwA3rdPi.js → message-list-CpPV7dGx.js} +1 -1
- package/dist/{session-core-ClKdVgRU.js → session-core-CAfYmUbg.js} +33 -23
- package/dist/session-core-reconnect.d.ts +6 -6
- package/dist/session-core.js +1 -1
- package/dist/{tool-call-block-CrLN7xlI.js → tool-call-block-D6pTEPrT.js} +5 -4
- package/dist/worklets/capture-processor.js +3 -4
- package/dist/worklets/playback-processor.js +3 -4
- package/package.json +2 -2
- package/dist/default-client/assets/audio-fO7SVU64.js +0 -1
- package/dist/default-client/assets/index-aXDcAQ6M.js +0 -293
|
@@ -18,5 +18,17 @@ function pageBaseUrl() {
|
|
|
18
18
|
if (typeof location === "undefined") return "";
|
|
19
19
|
return location.origin + location.pathname;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Set the document title, when there is one to set and a document to set it on.
|
|
23
|
+
*
|
|
24
|
+
* Both mounts do this and neither may clobber a title the page's own HTML
|
|
25
|
+
* declared — `client()`'s custom-component tier because there is no shell header
|
|
26
|
+
* to show the name in, `page()` because there is no shell at all. One copy, for
|
|
27
|
+
* the reason `resolveContainer` and `mountRoot` are shared: the rule is the
|
|
28
|
+
* same, so the two mounts must not be able to disagree about it.
|
|
29
|
+
*/
|
|
30
|
+
function setPageTitle(name) {
|
|
31
|
+
if (name && typeof document !== "undefined") document.title = name;
|
|
32
|
+
}
|
|
21
33
|
//#endregion
|
|
22
|
-
export {
|
|
34
|
+
export { tryParseJSON as i, setPageTitle as n, truncate as r, pageBaseUrl as t };
|
package/dist/_utils.d.ts
CHANGED
|
@@ -8,3 +8,13 @@ export declare function truncate(s: string, max?: number): string;
|
|
|
8
8
|
* shareable-URL chip can never disagree about which agent the page is.
|
|
9
9
|
*/
|
|
10
10
|
export declare function pageBaseUrl(): string;
|
|
11
|
+
/**
|
|
12
|
+
* Set the document title, when there is one to set and a document to set it on.
|
|
13
|
+
*
|
|
14
|
+
* Both mounts do this and neither may clobber a title the page's own HTML
|
|
15
|
+
* declared — `client()`'s custom-component tier because there is no shell header
|
|
16
|
+
* to show the name in, `page()` because there is no shell at all. One copy, for
|
|
17
|
+
* the reason `resolveContainer` and `mountRoot` are shared: the rule is the
|
|
18
|
+
* same, so the two mounts must not be able to disagree about it.
|
|
19
|
+
*/
|
|
20
|
+
export declare function setPageTitle(name: string | undefined): void;
|
|
@@ -16,3 +16,19 @@
|
|
|
16
16
|
* into ids would corrupt it silently.
|
|
17
17
|
*/
|
|
18
18
|
export declare function filesOf(value: unknown): File[];
|
|
19
|
+
/**
|
|
20
|
+
* The input properties still carrying a `File` — i.e. the ones that CANNOT survive
|
|
21
|
+
* being sent.
|
|
22
|
+
*
|
|
23
|
+
* A run input is JSON, and `JSON.stringify(new File(…))` is `{}` — no `toJSON`, no
|
|
24
|
+
* own enumerable properties. So a File left in a payload does not fail to send: it
|
|
25
|
+
* arrives as an empty object, and the workflow rejects it against whatever its own
|
|
26
|
+
* schema says the property should be. Measured in production as
|
|
27
|
+
* `Invalid input for workflow "transcribe": recording: Invalid input` — a message
|
|
28
|
+
* about a type, on a form where the user had picked a perfectly good file.
|
|
29
|
+
*
|
|
30
|
+
* Exported beside {@link filesOf} because it is the same question asked at the
|
|
31
|
+
* other end: that one decides which fields to UPLOAD, this one checks that none
|
|
32
|
+
* were missed. Both hooks are the callers.
|
|
33
|
+
*/
|
|
34
|
+
export declare function fileFields(input: unknown): string[];
|
package/dist/audio.js
CHANGED
|
@@ -12,6 +12,19 @@ function assertGranted(granted, requested, side) {
|
|
|
12
12
|
throw new Error(`Browser refused the ${side} sample rate: asked for ${requested} Hz, got ${granted} Hz`);
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
+
* The error a dead worklet processor reports.
|
|
16
|
+
*
|
|
17
|
+
* A processor exception permanently kills the node — no further messages or
|
|
18
|
+
* audio will ever arrive — so both sides log it and hand it on. One spelling of
|
|
19
|
+
* that, because the pair differed only in the word "capture"/"playback" and in
|
|
20
|
+
* what the playback side has to settle afterwards.
|
|
21
|
+
*/
|
|
22
|
+
function workletCrash(side) {
|
|
23
|
+
const err = /* @__PURE__ */ new Error(`Audio ${side} worklet crashed`);
|
|
24
|
+
console.error("[aai-ui]", err.message);
|
|
25
|
+
return err;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
15
28
|
* Release a microphone that was (or later gets) granted while another init
|
|
16
29
|
* step failed; if `getUserMedia` itself rejected, this is a no-op. Without
|
|
17
30
|
* it, a mic granted after a failed init keeps the browser's recording
|
|
@@ -116,11 +129,7 @@ async function createVoiceIO(opts) {
|
|
|
116
129
|
const mic = capCtx.createMediaStreamSource(stream);
|
|
117
130
|
const capture = createCaptureNode(capCtx, onMicData, onMicSilent);
|
|
118
131
|
mic.connect(capture.node);
|
|
119
|
-
capture.node.onprocessorerror = () =>
|
|
120
|
-
const err = /* @__PURE__ */ new Error("Audio capture worklet crashed");
|
|
121
|
-
console.error("[aai-ui]", err.message);
|
|
122
|
-
onError?.(err);
|
|
123
|
-
};
|
|
132
|
+
capture.node.onprocessorerror = () => onError?.(workletCrash("capture"));
|
|
124
133
|
capture.start();
|
|
125
134
|
let playNode = null;
|
|
126
135
|
let onPlaybackStop = null;
|
|
@@ -157,8 +166,7 @@ async function createVoiceIO(opts) {
|
|
|
157
166
|
else if (e.data.event === "progress") onPlaybackProgress?.(e.data.bufferedMs);
|
|
158
167
|
};
|
|
159
168
|
node.onprocessorerror = () => {
|
|
160
|
-
const err =
|
|
161
|
-
console.error("[aai-ui]", err.message);
|
|
169
|
+
const err = workletCrash("playback");
|
|
162
170
|
settlePendingStop();
|
|
163
171
|
onError?.(err);
|
|
164
172
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { t as MessageList } from "./message-list-
|
|
1
|
+
import { t as MessageList } from "./message-list-CpPV7dGx.js";
|
|
2
2
|
import { useSessionSelector, useTheme } from "./context.js";
|
|
3
3
|
import { n as THINKING_COLOR, r as inkTint, t as ERROR_COLOR } from "./_colors-CcAi2FOU.js";
|
|
4
4
|
import { t as AaiLogo } from "./aai-logo-9xRBGVFl.js";
|
|
5
5
|
import { t as Eyebrow } from "./eyebrow-C6ZFuiz6.js";
|
|
6
|
-
import { t as Controls } from "./controls-
|
|
6
|
+
import { t as Controls } from "./controls-CjG91QJ4.js";
|
|
7
7
|
import clsx from "clsx";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
//#region components/console-shell.tsx
|
|
@@ -4,6 +4,35 @@ import clsx from "clsx";
|
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
//#region components/button.tsx
|
|
6
6
|
/** @jsxImportSource react */
|
|
7
|
+
/** One variant's rest and hover colors, derived from the theme. */
|
|
8
|
+
function variantColors(variant, theme) {
|
|
9
|
+
switch (variant) {
|
|
10
|
+
case "default": return {
|
|
11
|
+
bg: theme.primary,
|
|
12
|
+
fg: theme.surface,
|
|
13
|
+
border: "transparent",
|
|
14
|
+
hoverBg: inkTint(theme.text, theme.primary, 14),
|
|
15
|
+
hoverFg: theme.surface,
|
|
16
|
+
hoverBorder: "transparent"
|
|
17
|
+
};
|
|
18
|
+
case "secondary": return {
|
|
19
|
+
bg: "transparent",
|
|
20
|
+
fg: theme.primary,
|
|
21
|
+
border: theme.primary,
|
|
22
|
+
hoverBg: primaryTint(theme.primary, theme.surface, 8),
|
|
23
|
+
hoverFg: theme.primary,
|
|
24
|
+
hoverBorder: theme.primary
|
|
25
|
+
};
|
|
26
|
+
default: return {
|
|
27
|
+
bg: theme.surface,
|
|
28
|
+
fg: theme.text,
|
|
29
|
+
border: theme.border,
|
|
30
|
+
hoverBg: inkTint(theme.text, theme.surface, 5),
|
|
31
|
+
hoverFg: theme.text,
|
|
32
|
+
hoverBorder: theme.border
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
7
36
|
/**
|
|
8
37
|
* A styled button with variant and size presets.
|
|
9
38
|
*
|
|
@@ -33,31 +62,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
33
62
|
*/
|
|
34
63
|
function Button({ variant = "default", size = "default", className, children, style, ...rest }) {
|
|
35
64
|
const theme = useTheme();
|
|
36
|
-
|
|
37
|
-
if (variant === "default") colors = {
|
|
38
|
-
bg: theme.primary,
|
|
39
|
-
fg: theme.surface,
|
|
40
|
-
border: "transparent",
|
|
41
|
-
hoverBg: inkTint(theme.text, theme.primary, 14),
|
|
42
|
-
hoverFg: theme.surface,
|
|
43
|
-
hoverBorder: "transparent"
|
|
44
|
-
};
|
|
45
|
-
else if (variant === "secondary") colors = {
|
|
46
|
-
bg: "transparent",
|
|
47
|
-
fg: theme.primary,
|
|
48
|
-
border: theme.primary,
|
|
49
|
-
hoverBg: primaryTint(theme.primary, theme.surface, 8),
|
|
50
|
-
hoverFg: theme.primary,
|
|
51
|
-
hoverBorder: theme.primary
|
|
52
|
-
};
|
|
53
|
-
else colors = {
|
|
54
|
-
bg: theme.surface,
|
|
55
|
-
fg: theme.text,
|
|
56
|
-
border: theme.border,
|
|
57
|
-
hoverBg: inkTint(theme.text, theme.surface, 5),
|
|
58
|
-
hoverFg: theme.text,
|
|
59
|
-
hoverBorder: theme.border
|
|
60
|
-
};
|
|
65
|
+
const colors = variantColors(variant, theme);
|
|
61
66
|
return /* @__PURE__ */ jsx("button", {
|
|
62
67
|
type: "button",
|
|
63
68
|
style: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useSessionCore, useSessionSelector, useTheme } from "./context.js";
|
|
2
2
|
import { r as inkTint } from "./_colors-CcAi2FOU.js";
|
|
3
3
|
import { Button } from "./components/button.js";
|
|
4
|
-
import { t as pageBaseUrl } from "./_utils-
|
|
4
|
+
import { t as pageBaseUrl } from "./_utils-B6498_bm.js";
|
|
5
5
|
import clsx from "clsx";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { memo, useEffect, useRef, useState } from "react";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{o as e,s as t}from"./client-audio-constants-Ck0IJO4c.js";var n={echoCancellation:!0,noiseSuppression:!1,autoGainControl:!1,voiceIsolation:!1};function r(e,t,n){if(e!==t)throw Error(`Browser refused the ${n} sample rate: asked for ${t} Hz, got ${e} Hz`)}function i(e){let t=Error(`Audio ${e} worklet crashed`);return console.error(`[aai-ui]`,t.message),t}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(){let{promise:e,resolve:t}=Promise.withResolvers(),n=setTimeout(t,250);return i=()=>{clearTimeout(n),t()},r.port.postMessage({event:`stop`}),e}}}async function s(s){let{sttSampleRate:c,ttsSampleRate:l,captureWorkletSrc:u,playbackWorkletSrc:d,onMicData:f,onError:p,onPlaybackStats:m,onPlaybackProgress:h,onMicSilent:g}=s,_=new AudioContext({sampleRate:l,latencyHint:`playback`}),v=c===l,y=v?_:new AudioContext({sampleRate:c,latencyHint:`interactive`});async function b(){let e=v?[_]:[_,y];await Promise.all(e.map(e=>e.close().catch(e=>{console.warn(`AudioContext close failed:`,e)})))}let x=navigator.mediaDevices.getUserMedia({audio:{deviceId:{ideal:`default`},...n}}),S;try{[S]=await Promise.all([x,_.resume(),y.resume(),y.audioWorklet.addModule(u),_.audioWorklet.addModule(d)]),r(y.sampleRate,c,`capture`),r(_.sampleRate,l,`playback`)}catch(e){throw a(x),await b(),e}let C=y.createMediaStreamSource(S),w=o(y,f,g);C.connect(w.node),w.node.onprocessorerror=()=>p?.(i(`capture`)),w.start();let T=null,E=null,D=0,O=null,k=new AbortController;function A(){E?.(),E=null,O=null}function j(e){e.stats&&e.stats.concealedSamples>0&&m?.(e.stats),e.reason!==`interrupt`&&(O===null||e.turn===O)&&A()}function M(){if(T)return T;let e=new AudioWorkletNode(_,`playback-processor`);return e.connect(_.destination),e.port.onmessage=e=>{e.data.event===`stop`?j(e.data):e.data.event===`progress`&&h?.(e.data.bufferedMs)},e.onprocessorerror=()=>{let e=i(`playback`);A(),p?.(e)},T=e,e}let N={enqueue(e){k.signal.aborted||e.byteLength!==0&&M().port.postMessage({event:`write`,buffer:new Uint8Array(e)},[e])},done(){if(!T)return Promise.resolve();let n=++D;if(T.port.postMessage({event:`done`,turn:n}),_.state!==`running`)return O=null,Promise.resolve();let{promise:r,resolve:i}=Promise.withResolvers();E?.();let a=()=>{clearInterval(o),clearTimeout(s),E===a&&(E=null,O=null),i()},o=setInterval(()=>{_.state!==`running`&&a()},t),s=setTimeout(a,e);return E=a,O=n,r},flush(){T&&(A(),T.port.postMessage({event:`interrupt`}))},async close(){if(!k.signal.aborted){k.abort(),await w.stop(),C.disconnect(),w.node.disconnect(),T&&T.disconnect();for(let e of S.getTracks())e.stop();await b()}},async[Symbol.asyncDispose](){await N.close()}};return N}export{s as createVoiceIO};
|