@alexkroman1/aai-ui 5.2.0 → 5.4.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/README.md +59 -0
- package/dist/{chat-view-C6N2oXjo.js → chat-view-CqRGHImR.js} +12 -6
- package/dist/client-config.d.ts +13 -2
- package/dist/components/button.d.ts +17 -11
- package/dist/components/button.js +9 -6
- package/dist/components/chat-view.d.ts +10 -4
- package/dist/components/chat-view.js +1 -1
- package/dist/components/controls.d.ts +6 -2
- package/dist/components/controls.js +1 -1
- package/dist/components/message-list.d.ts +6 -2
- package/dist/components/message-list.js +1 -1
- package/dist/components/sidebar-layout.d.ts +13 -3
- package/dist/components/sidebar-layout.js +13 -3
- package/dist/components/start-screen.d.ts +2 -0
- package/dist/components/start-screen.js +2 -0
- package/dist/components/tool-call-block.d.ts +1 -6
- package/dist/components/tool-call-block.js +1 -1
- package/dist/components/tool-config-context.d.ts +3 -3
- package/dist/components/url-chips.d.ts +3 -3
- package/dist/context.d.ts +59 -2
- package/dist/context.js +45 -0
- package/dist/{controls-Cqg7T4FN.js → controls-CoBO6sId.js} +9 -5
- package/dist/default-client/assets/{audio-C1U_foBG.js → audio-DXQj0wCf.js} +1 -1
- package/dist/default-client/assets/{capture-processor-dyyJArQs.js → capture-processor-DZg_Q6hY.js} +1 -1
- package/dist/default-client/assets/{index-CGZajs1V.js → index-DyY8OeVc.js} +25 -25
- package/dist/default-client/assets/{playback-processor-BO2TdYrv.js → playback-processor-DZlwpHHt.js} +1 -1
- package/dist/default-client/index.html +1 -1
- package/dist/define-client.d.ts +44 -9
- package/dist/define-client.js +22 -4
- package/dist/hooks.d.ts +51 -19
- package/dist/hooks.js +37 -7
- package/dist/index.d.ts +5 -5
- package/dist/index.js +6 -6
- package/dist/{message-list-CD9GxrlH.js → message-list-CdtrNi4w.js} +7 -3
- package/dist/{session-core-Bvga6tUS.js → session-core-DX2SIpdQ.js} +30 -4
- package/dist/session-core-types.d.ts +34 -9
- package/dist/session-core.d.ts +15 -0
- package/dist/session-core.js +1 -1
- package/dist/{tool-call-block-O9vLqoyU.js → tool-call-block-C--Bj04_.js} +4 -9
- package/dist/types.d.ts +23 -7
- package/dist/types.js +3 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @alexkroman1/aai-ui
|
|
2
|
+
|
|
3
|
+
The browser client for aai voice agents: React 19 components, hooks, and a
|
|
4
|
+
framework-agnostic session core (WebSocket + microphone + playback).
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
npm i @alexkroman1/aai-ui react react-dom
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Every agent gets this UI for free — `aai dev` and deployed agents serve a
|
|
11
|
+
default client built from this package. Install it directly when the agent
|
|
12
|
+
has its own `client.tsx`.
|
|
13
|
+
|
|
14
|
+
## A custom client
|
|
15
|
+
|
|
16
|
+
`client()` mounts the default chat shell with your sidebar, or replaces the
|
|
17
|
+
whole UI with a custom component:
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import "@alexkroman1/aai-ui/styles.css";
|
|
21
|
+
import { client, useAgentState, useTheme } from "@alexkroman1/aai-ui";
|
|
22
|
+
|
|
23
|
+
type OrderView = { items: string[]; total: string };
|
|
24
|
+
|
|
25
|
+
function OrderSidebar() {
|
|
26
|
+
const theme = useTheme();
|
|
27
|
+
// Server state projected by the agent's `syncState`, pushed after every
|
|
28
|
+
// tool call.
|
|
29
|
+
const order = useAgentState<OrderView>() ?? { items: [], total: "$0.00" };
|
|
30
|
+
return (
|
|
31
|
+
<div style={{ color: theme.text }}>
|
|
32
|
+
{order.items.map((item) => (
|
|
33
|
+
<div key={item}>{item}</div>
|
|
34
|
+
))}
|
|
35
|
+
<strong style={{ color: theme.primary }}>{order.total}</strong>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
client({ sidebar: <OrderSidebar /> });
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Hooks
|
|
44
|
+
|
|
45
|
+
Inside components rendered by `client()`:
|
|
46
|
+
|
|
47
|
+
- `useSession()` — connection state, transcript, `connect`/`disconnect`.
|
|
48
|
+
- `useAgentState<T>()` — the agent's `syncState` projection, live.
|
|
49
|
+
- `useToolResult(name, cb)` / `useToolCallStart(name, cb)` — observe tool
|
|
50
|
+
calls as they run (e.g. to render a card per result).
|
|
51
|
+
- `useEvent(name, cb)` — custom events the agent pushes with `ctx.send`.
|
|
52
|
+
- `useTheme()` — the resolved theme colors for custom components.
|
|
53
|
+
|
|
54
|
+
For a non-React integration, `createSessionCore()` exposes the same session
|
|
55
|
+
as a plain store with an immutable snapshot per change.
|
|
56
|
+
|
|
57
|
+
## Documentation
|
|
58
|
+
|
|
59
|
+
Full API reference: <https://alexkroman.github.io/agent/>
|
|
@@ -2,8 +2,8 @@ import { useSessionSelector, useTheme } from "./context.js";
|
|
|
2
2
|
import { a as THINKING_COLOR, r as TEXT_FAINT, t as ERROR_COLOR } from "./_colors-Bfh3-BVE.js";
|
|
3
3
|
import { t as AaiLogo } from "./aai-logo-B8lDmsut.js";
|
|
4
4
|
import { t as Eyebrow } from "./eyebrow-C6ZFuiz6.js";
|
|
5
|
-
import { t as Controls } from "./controls-
|
|
6
|
-
import { t as MessageList } from "./message-list-
|
|
5
|
+
import { t as Controls } from "./controls-CoBO6sId.js";
|
|
6
|
+
import { t as MessageList } from "./message-list-CdtrNi4w.js";
|
|
7
7
|
import clsx from "clsx";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
//#region components/console-shell.tsx
|
|
@@ -93,13 +93,19 @@ const PULSING_STATES = /* @__PURE__ */ new Set(["listening", "speaking"]);
|
|
|
93
93
|
* (logo + live-status eyebrow), the conversation on a raised white card,
|
|
94
94
|
* and the session controls beneath it.
|
|
95
95
|
*
|
|
96
|
-
* Must be rendered inside a
|
|
96
|
+
* Must be rendered inside a `SessionProvider`.
|
|
97
97
|
*
|
|
98
98
|
* @example
|
|
99
99
|
* ```tsx
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
100
|
+
* import { ChatView, StartScreen } from "@alexkroman1/aai-ui";
|
|
101
|
+
*
|
|
102
|
+
* function App() {
|
|
103
|
+
* return (
|
|
104
|
+
* <StartScreen icon="🍕" title="Pizza Palace">
|
|
105
|
+
* <ChatView />
|
|
106
|
+
* </StartScreen>
|
|
107
|
+
* );
|
|
108
|
+
* }
|
|
103
109
|
* ```
|
|
104
110
|
*
|
|
105
111
|
* @param icon - Optional element rendered in place of the logo in the header.
|
package/dist/client-config.d.ts
CHANGED
|
@@ -13,8 +13,13 @@
|
|
|
13
13
|
* named no sessionUrl" (`{}`). See its doc comment.
|
|
14
14
|
*/
|
|
15
15
|
import { type ClientConfigResponse } from "@alexkroman1/aai/protocol";
|
|
16
|
+
/** @internal Re-exported for the sibling modules; the SDK's `/protocol` subpath is the canonical home. */
|
|
16
17
|
export type { ClientConfigResponse } from "@alexkroman1/aai/protocol";
|
|
17
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a relative endpoint path against the agent's base URL.
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
18
23
|
export declare function buildAgentUrl(platformUrl: string, endpointPath: string): URL;
|
|
19
24
|
/**
|
|
20
25
|
* Fetch the agent's client config, reporting `null` when the lookup did not
|
|
@@ -29,7 +34,13 @@ export declare function buildAgentUrl(platformUrl: string, endpointPath: string)
|
|
|
29
34
|
* platform's `/:slug/websocket` — a WebSocket redirect browsers don't
|
|
30
35
|
* follow, so every retry failed with no re-brokering even after the agent
|
|
31
36
|
* recovered.
|
|
37
|
+
*
|
|
38
|
+
* @internal
|
|
32
39
|
*/
|
|
33
40
|
export declare function loadClientConfig(platformUrl: string, fetchFn?: typeof globalThis.fetch): Promise<ClientConfigResponse | null>;
|
|
34
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Fetch the agent's client config; any failure yields the agent default.
|
|
43
|
+
*
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
35
46
|
export declare function fetchClientConfig(platformUrl: string, fetchFn?: typeof globalThis.fetch): Promise<ClientConfigResponse>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
1
|
+
import type { ButtonHTMLAttributes, ReactNode } from "react";
|
|
2
2
|
/**
|
|
3
3
|
* Visual style of a {@link Button} (design-system "website refresh":
|
|
4
4
|
* rectangular, ALL-CAPS, tracked labels).
|
|
@@ -6,15 +6,19 @@ import type { ReactNode } from "react";
|
|
|
6
6
|
* - `"default"` — Primary filled button (indigo background).
|
|
7
7
|
* - `"secondary"` — Outlined primary (transparent background, primary border).
|
|
8
8
|
* - `"ghost"` — Raised neutral (surface background with hairline border).
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
9
11
|
*/
|
|
10
|
-
type ButtonVariant = "default" | "secondary" | "ghost";
|
|
12
|
+
export type ButtonVariant = "default" | "secondary" | "ghost";
|
|
11
13
|
/**
|
|
12
14
|
* Size preset for a {@link Button}.
|
|
13
15
|
*
|
|
14
16
|
* - `"default"` — Compact control (height 36 px).
|
|
15
17
|
* - `"lg"` — Primary CTA (height 44 px, generous padding).
|
|
18
|
+
*
|
|
19
|
+
* @public
|
|
16
20
|
*/
|
|
17
|
-
type ButtonSize = "default" | "lg";
|
|
21
|
+
export type ButtonSize = "default" | "lg";
|
|
18
22
|
/**
|
|
19
23
|
* A styled button with variant and size presets.
|
|
20
24
|
*
|
|
@@ -23,13 +27,16 @@ type ButtonSize = "default" | "lg";
|
|
|
23
27
|
*
|
|
24
28
|
* @example
|
|
25
29
|
* ```tsx
|
|
26
|
-
*
|
|
27
|
-
* Stop
|
|
28
|
-
* </Button>
|
|
30
|
+
* import { Button } from "@alexkroman1/aai-ui";
|
|
29
31
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
32
|
+
* function Actions({ onStop }: { onStop: () => void }) {
|
|
33
|
+
* return (
|
|
34
|
+
* <>
|
|
35
|
+
* <Button variant="secondary" onClick={onStop}>Stop</Button>
|
|
36
|
+
* <Button size="lg" className="w-full">Start Conversation</Button>
|
|
37
|
+
* </>
|
|
38
|
+
* );
|
|
39
|
+
* }
|
|
33
40
|
* ```
|
|
34
41
|
*
|
|
35
42
|
* @param variant - Visual style (`"default"` | `"secondary"` | `"ghost"`). Defaults to `"default"`.
|
|
@@ -44,5 +51,4 @@ export declare function Button({ variant, size, className, children, style, ...r
|
|
|
44
51
|
size?: ButtonSize;
|
|
45
52
|
className?: string;
|
|
46
53
|
children?: ReactNode;
|
|
47
|
-
} & Omit<
|
|
48
|
-
export {};
|
|
54
|
+
} & Omit<ButtonHTMLAttributes<HTMLButtonElement>, "className">): import("react").JSX.Element;
|
|
@@ -11,13 +11,16 @@ import { jsx } from "react/jsx-runtime";
|
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```tsx
|
|
14
|
-
*
|
|
15
|
-
* Stop
|
|
16
|
-
* </Button>
|
|
14
|
+
* import { Button } from "@alexkroman1/aai-ui";
|
|
17
15
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
16
|
+
* function Actions({ onStop }: { onStop: () => void }) {
|
|
17
|
+
* return (
|
|
18
|
+
* <>
|
|
19
|
+
* <Button variant="secondary" onClick={onStop}>Stop</Button>
|
|
20
|
+
* <Button size="lg" className="w-full">Start Conversation</Button>
|
|
21
|
+
* </>
|
|
22
|
+
* );
|
|
23
|
+
* }
|
|
21
24
|
* ```
|
|
22
25
|
*
|
|
23
26
|
* @param variant - Visual style (`"default"` | `"secondary"` | `"ghost"`). Defaults to `"default"`.
|
|
@@ -6,13 +6,19 @@ import type { ReactNode } from "react";
|
|
|
6
6
|
* (logo + live-status eyebrow), the conversation on a raised white card,
|
|
7
7
|
* and the session controls beneath it.
|
|
8
8
|
*
|
|
9
|
-
* Must be rendered inside a
|
|
9
|
+
* Must be rendered inside a `SessionProvider`.
|
|
10
10
|
*
|
|
11
11
|
* @example
|
|
12
12
|
* ```tsx
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* import { ChatView, StartScreen } from "@alexkroman1/aai-ui";
|
|
14
|
+
*
|
|
15
|
+
* function App() {
|
|
16
|
+
* return (
|
|
17
|
+
* <StartScreen icon="🍕" title="Pizza Palace">
|
|
18
|
+
* <ChatView />
|
|
19
|
+
* </StartScreen>
|
|
20
|
+
* );
|
|
21
|
+
* }
|
|
16
22
|
* ```
|
|
17
23
|
*
|
|
18
24
|
* @param icon - Optional element rendered in place of the logo in the header.
|
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
* Session control buttons: **Stop / Resume** and **New Conversation**.
|
|
3
3
|
*
|
|
4
4
|
* Reads session state from {@link useSession}. Must be rendered inside a
|
|
5
|
-
*
|
|
5
|
+
* `SessionProvider`.
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
8
|
* ```tsx
|
|
9
|
-
*
|
|
9
|
+
* import { Controls } from "@alexkroman1/aai-ui";
|
|
10
|
+
*
|
|
11
|
+
* function Footer() {
|
|
12
|
+
* return <Controls className="justify-end" />;
|
|
13
|
+
* }
|
|
10
14
|
* ```
|
|
11
15
|
*
|
|
12
16
|
* @param className - Additional CSS class names applied to the container.
|
|
@@ -5,11 +5,15 @@
|
|
|
5
5
|
* Messages and tool calls are interleaved in the correct order. The list
|
|
6
6
|
* auto-scrolls to the latest content.
|
|
7
7
|
*
|
|
8
|
-
* Must be rendered inside a
|
|
8
|
+
* Must be rendered inside a `SessionProvider`.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```tsx
|
|
12
|
-
*
|
|
12
|
+
* import { MessageList } from "@alexkroman1/aai-ui";
|
|
13
|
+
*
|
|
14
|
+
* function Conversation() {
|
|
15
|
+
* return <MessageList className="flex-1" />;
|
|
16
|
+
* }
|
|
13
17
|
* ```
|
|
14
18
|
*
|
|
15
19
|
* @param className - Additional CSS class names applied to the scroll container.
|
|
@@ -5,9 +5,19 @@ import type { ReactNode } from "react";
|
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
7
|
* ```tsx
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* import { ChatView, SidebarLayout } from "@alexkroman1/aai-ui";
|
|
9
|
+
*
|
|
10
|
+
* function OrderPanel() {
|
|
11
|
+
* return <div>Cart</div>;
|
|
12
|
+
* }
|
|
13
|
+
*
|
|
14
|
+
* function App() {
|
|
15
|
+
* return (
|
|
16
|
+
* <SidebarLayout sidebar={<OrderPanel />}>
|
|
17
|
+
* <ChatView />
|
|
18
|
+
* </SidebarLayout>
|
|
19
|
+
* );
|
|
20
|
+
* }
|
|
11
21
|
* ```
|
|
12
22
|
*
|
|
13
23
|
* @public
|
|
@@ -9,9 +9,19 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```tsx
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* import { ChatView, SidebarLayout } from "@alexkroman1/aai-ui";
|
|
13
|
+
*
|
|
14
|
+
* function OrderPanel() {
|
|
15
|
+
* return <div>Cart</div>;
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* function App() {
|
|
19
|
+
* return (
|
|
20
|
+
* <SidebarLayout sidebar={<OrderPanel />}>
|
|
21
|
+
* <ChatView />
|
|
22
|
+
* </SidebarLayout>
|
|
23
|
+
* );
|
|
24
|
+
* }
|
|
15
25
|
* ```
|
|
16
26
|
*
|
|
17
27
|
* @public
|
|
@@ -14,6 +14,8 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
16
|
* ```tsx
|
|
17
|
+
* import { ChatView, StartScreen } from "@alexkroman1/aai-ui";
|
|
18
|
+
*
|
|
17
19
|
* function MyAgent() {
|
|
18
20
|
* return (
|
|
19
21
|
* <StartScreen icon="🍕" title="Pizza Palace" subtitle="Voice-powered ordering">
|
|
@@ -16,15 +16,10 @@ import type { ToolCallInfo } from "../types.ts";
|
|
|
16
16
|
* snapshots and rows are keyed on the stable `callId`, so a list update only
|
|
17
17
|
* re-renders the rows whose tool call actually changed.
|
|
18
18
|
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```tsx
|
|
21
|
-
* <ToolCallBlock toolCall={toolCall} />
|
|
22
|
-
* ```
|
|
23
|
-
*
|
|
24
19
|
* @param toolCall - The tool call to render (see {@link ToolCallInfo}).
|
|
25
20
|
* @param className - Additional CSS class names.
|
|
26
21
|
*
|
|
27
|
-
* @
|
|
22
|
+
* @internal Not exported from the package — rendered by `MessageList`.
|
|
28
23
|
*/
|
|
29
24
|
export declare const ToolCallBlock: import("react").MemoExoticComponent<({ toolCall, className, }: {
|
|
30
25
|
toolCall: ToolCallInfo;
|
|
@@ -8,10 +8,10 @@ export type ToolDisplayConfig = Record<string, {
|
|
|
8
8
|
label?: string;
|
|
9
9
|
}>;
|
|
10
10
|
/**
|
|
11
|
-
* Context for tool display configuration.
|
|
12
|
-
*
|
|
11
|
+
* Context for tool display configuration. Installed by `client()` from
|
|
12
|
+
* `ClientConfig.tools`; the built-in components read it via `useToolConfig`.
|
|
13
13
|
*
|
|
14
|
-
* @
|
|
14
|
+
* @internal
|
|
15
15
|
*/
|
|
16
16
|
export declare const ToolConfigContext: import("react").Context<ToolDisplayConfig>;
|
|
17
17
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The session's shareable UI URL — the page this UI is served from, what
|
|
3
3
|
* you'd send someone to talk to the agent.
|
|
4
4
|
*
|
|
5
|
-
* @
|
|
5
|
+
* @internal
|
|
6
6
|
*/
|
|
7
7
|
export declare function UiUrlChip({ className }: {
|
|
8
8
|
className?: string | undefined;
|
|
@@ -11,7 +11,7 @@ export declare function UiUrlChip({ className }: {
|
|
|
11
11
|
* The session's programmatic WebSocket endpoint — the URL a script or backend
|
|
12
12
|
* can connect to directly instead of using this UI.
|
|
13
13
|
*
|
|
14
|
-
* @
|
|
14
|
+
* @internal
|
|
15
15
|
*/
|
|
16
16
|
export declare function ApiUrlChip({ className }: {
|
|
17
17
|
className?: string | undefined;
|
|
@@ -22,7 +22,7 @@ export declare function ApiUrlChip({ className }: {
|
|
|
22
22
|
* told apart. Rendered by the default shell in every session mode (S2S,
|
|
23
23
|
* pipeline).
|
|
24
24
|
*
|
|
25
|
-
* @
|
|
25
|
+
* @internal
|
|
26
26
|
*/
|
|
27
27
|
export declare function SessionUrlChips({ className }: {
|
|
28
28
|
className?: string | undefined;
|
package/dist/context.d.ts
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { SessionCore, SessionSnapshot } from "./session-core-types.ts";
|
|
3
3
|
import type { ClientTheme } from "./types.ts";
|
|
4
|
+
/**
|
|
5
|
+
* Provides the {@link SessionCore} the session hooks read. `client()`
|
|
6
|
+
* installs it automatically; a custom tree only needs it when bypassing
|
|
7
|
+
* `client()` and mounting React itself.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
4
11
|
export declare function SessionProvider({ value, children }: {
|
|
5
12
|
value: SessionCore;
|
|
6
13
|
children?: ReactNode;
|
|
7
14
|
}): import("react").FunctionComponentElement<import("react").ProviderProps<SessionCore | null>>;
|
|
8
|
-
/**
|
|
9
|
-
*
|
|
15
|
+
/**
|
|
16
|
+
* What {@link useSession} returns: the live {@link SessionSnapshot} fields
|
|
17
|
+
* (`state`, `messages`, `toolCalls`, `agentState`, live transcripts, `error`,
|
|
18
|
+
* `apiUrl`, `started`/`running`/`recording`, …) merged with the session's
|
|
19
|
+
* control methods (`start`, `toggle`, `reset`, `resetState`, `disconnect`,
|
|
20
|
+
* `cancel`).
|
|
21
|
+
*
|
|
22
|
+
* Note there is no text-send method — sessions are voice-only; the only
|
|
23
|
+
* client→server inputs are audio and the control methods above.
|
|
24
|
+
*
|
|
25
|
+
* Method signatures come from {@link SessionCore} — one source of truth.
|
|
26
|
+
*
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
10
29
|
export type Session = SessionSnapshot & Pick<SessionCore, "start" | "cancel" | "resetState" | "reset" | "disconnect" | "toggle">;
|
|
11
30
|
/**
|
|
12
31
|
* Return the raw {@link SessionCore} from context without subscribing to
|
|
@@ -18,6 +37,30 @@ export type Session = SessionSnapshot & Pick<SessionCore, "start" | "cancel" | "
|
|
|
18
37
|
* components.
|
|
19
38
|
*/
|
|
20
39
|
export declare function useSessionCore(): SessionCore;
|
|
40
|
+
/**
|
|
41
|
+
* Return the live {@link Session}: the current snapshot fields plus the
|
|
42
|
+
* control methods (`start`, `toggle`, `reset`, `resetState`, `disconnect`,
|
|
43
|
+
* `cancel`).
|
|
44
|
+
*
|
|
45
|
+
* Throws if used outside the provider `client()` installs (the error names
|
|
46
|
+
* `<SessionProvider>` — you only mount that yourself when bypassing
|
|
47
|
+
* `client()`). Re-renders the component on *every* snapshot change; for a
|
|
48
|
+
* component that reads one field, prefer {@link useSessionSelector} for a
|
|
49
|
+
* targeted subscription.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```tsx
|
|
53
|
+
* import { useSession } from "@alexkroman1/aai-ui";
|
|
54
|
+
*
|
|
55
|
+
* function Controls() {
|
|
56
|
+
* const session = useSession();
|
|
57
|
+
* if (!session.started) return <button onClick={session.start}>Start</button>;
|
|
58
|
+
* return <button onClick={session.toggle}>{session.running ? "Pause" : "Resume"}</button>;
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @public
|
|
63
|
+
*/
|
|
21
64
|
export declare function useSession(): Session;
|
|
22
65
|
/**
|
|
23
66
|
* Subscribe to a narrow slice of the session snapshot.
|
|
@@ -34,8 +77,22 @@ export declare function useSession(): Session;
|
|
|
34
77
|
* @public
|
|
35
78
|
*/
|
|
36
79
|
export declare function useSessionSelector<T>(selector: (snapshot: SessionSnapshot) => T, isEqual?: (a: T, b: T) => boolean): T;
|
|
80
|
+
/**
|
|
81
|
+
* Provides the theme the components read via `useTheme`. `client()` installs
|
|
82
|
+
* it automatically (from `ClientConfig.theme`); a custom tree only needs it
|
|
83
|
+
* when bypassing `client()` and mounting React itself.
|
|
84
|
+
*
|
|
85
|
+
* @internal
|
|
86
|
+
*/
|
|
37
87
|
export declare function ThemeProvider({ value, children, }: {
|
|
38
88
|
value?: ClientTheme | undefined;
|
|
39
89
|
children?: ReactNode;
|
|
40
90
|
}): import("react").FunctionComponentElement<import("react").ProviderProps<Required<ClientTheme>>>;
|
|
91
|
+
/**
|
|
92
|
+
* Read the resolved theme (every {@link ClientTheme} field filled with its
|
|
93
|
+
* default) from the nearest theme context. Returns the default theme when no
|
|
94
|
+
* provider is present, so components can call it unconditionally.
|
|
95
|
+
*
|
|
96
|
+
* @public
|
|
97
|
+
*/
|
|
41
98
|
export declare function useTheme(): Required<ClientTheme>;
|
package/dist/context.js
CHANGED
|
@@ -9,6 +9,13 @@ const DEFAULT_THEME = {
|
|
|
9
9
|
border: "#DCD7CC"
|
|
10
10
|
};
|
|
11
11
|
const SessionCtx = createContext(null);
|
|
12
|
+
/**
|
|
13
|
+
* Provides the {@link SessionCore} the session hooks read. `client()`
|
|
14
|
+
* installs it automatically; a custom tree only needs it when bypassing
|
|
15
|
+
* `client()` and mounting React itself.
|
|
16
|
+
*
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
12
19
|
function SessionProvider({ value, children }) {
|
|
13
20
|
return createElement(SessionCtx.Provider, { value }, children);
|
|
14
21
|
}
|
|
@@ -26,6 +33,30 @@ function useSessionCore() {
|
|
|
26
33
|
if (!core) throw new Error("Session hooks must be used within <SessionProvider>");
|
|
27
34
|
return core;
|
|
28
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Return the live {@link Session}: the current snapshot fields plus the
|
|
38
|
+
* control methods (`start`, `toggle`, `reset`, `resetState`, `disconnect`,
|
|
39
|
+
* `cancel`).
|
|
40
|
+
*
|
|
41
|
+
* Throws if used outside the provider `client()` installs (the error names
|
|
42
|
+
* `<SessionProvider>` — you only mount that yourself when bypassing
|
|
43
|
+
* `client()`). Re-renders the component on *every* snapshot change; for a
|
|
44
|
+
* component that reads one field, prefer {@link useSessionSelector} for a
|
|
45
|
+
* targeted subscription.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* import { useSession } from "@alexkroman1/aai-ui";
|
|
50
|
+
*
|
|
51
|
+
* function Controls() {
|
|
52
|
+
* const session = useSession();
|
|
53
|
+
* if (!session.started) return <button onClick={session.start}>Start</button>;
|
|
54
|
+
* return <button onClick={session.toggle}>{session.running ? "Pause" : "Resume"}</button>;
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @public
|
|
59
|
+
*/
|
|
29
60
|
function useSession() {
|
|
30
61
|
const core = useSessionCore();
|
|
31
62
|
const snapshot = useSyncExternalStore(core.subscribe, core.getSnapshot);
|
|
@@ -58,6 +89,13 @@ function useSessionSelector(selector, isEqual = Object.is) {
|
|
|
58
89
|
return useSyncExternalStoreWithSelector(core.subscribe, core.getSnapshot, core.getSnapshot, selector, isEqual);
|
|
59
90
|
}
|
|
60
91
|
const ThemeCtx = createContext(DEFAULT_THEME);
|
|
92
|
+
/**
|
|
93
|
+
* Provides the theme the components read via `useTheme`. `client()` installs
|
|
94
|
+
* it automatically (from `ClientConfig.theme`); a custom tree only needs it
|
|
95
|
+
* when bypassing `client()` and mounting React itself.
|
|
96
|
+
*
|
|
97
|
+
* @internal
|
|
98
|
+
*/
|
|
61
99
|
function ThemeProvider({ value, children }) {
|
|
62
100
|
const merged = useMemo(() => value ? {
|
|
63
101
|
...DEFAULT_THEME,
|
|
@@ -92,6 +130,13 @@ function usePageBackground(bg) {
|
|
|
92
130
|
};
|
|
93
131
|
}, [bg]);
|
|
94
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Read the resolved theme (every {@link ClientTheme} field filled with its
|
|
135
|
+
* default) from the nearest theme context. Returns the default theme when no
|
|
136
|
+
* provider is present, so components can call it unconditionally.
|
|
137
|
+
*
|
|
138
|
+
* @public
|
|
139
|
+
*/
|
|
95
140
|
function useTheme() {
|
|
96
141
|
return useContext(ThemeCtx);
|
|
97
142
|
}
|
|
@@ -55,7 +55,7 @@ function UrlChip({ label, url, hint, testId, className }) {
|
|
|
55
55
|
* The session's shareable UI URL — the page this UI is served from, what
|
|
56
56
|
* you'd send someone to talk to the agent.
|
|
57
57
|
*
|
|
58
|
-
* @
|
|
58
|
+
* @internal
|
|
59
59
|
*/
|
|
60
60
|
function UiUrlChip({ className }) {
|
|
61
61
|
return /* @__PURE__ */ jsx(UrlChip, {
|
|
@@ -70,7 +70,7 @@ function UiUrlChip({ className }) {
|
|
|
70
70
|
* The session's programmatic WebSocket endpoint — the URL a script or backend
|
|
71
71
|
* can connect to directly instead of using this UI.
|
|
72
72
|
*
|
|
73
|
-
* @
|
|
73
|
+
* @internal
|
|
74
74
|
*/
|
|
75
75
|
function ApiUrlChip({ className }) {
|
|
76
76
|
return /* @__PURE__ */ jsx(UrlChip, {
|
|
@@ -87,7 +87,7 @@ function ApiUrlChip({ className }) {
|
|
|
87
87
|
* told apart. Rendered by the default shell in every session mode (S2S,
|
|
88
88
|
* pipeline).
|
|
89
89
|
*
|
|
90
|
-
* @
|
|
90
|
+
* @internal
|
|
91
91
|
*/
|
|
92
92
|
function SessionUrlChips({ className }) {
|
|
93
93
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -102,11 +102,15 @@ function SessionUrlChips({ className }) {
|
|
|
102
102
|
* Session control buttons: **Stop / Resume** and **New Conversation**.
|
|
103
103
|
*
|
|
104
104
|
* Reads session state from {@link useSession}. Must be rendered inside a
|
|
105
|
-
*
|
|
105
|
+
* `SessionProvider`.
|
|
106
106
|
*
|
|
107
107
|
* @example
|
|
108
108
|
* ```tsx
|
|
109
|
-
*
|
|
109
|
+
* import { Controls } from "@alexkroman1/aai-ui";
|
|
110
|
+
*
|
|
111
|
+
* function Footer() {
|
|
112
|
+
* return <Controls className="justify-end" />;
|
|
113
|
+
* }
|
|
110
114
|
* ```
|
|
111
115
|
*
|
|
112
116
|
* @param className - Additional CSS class names applied to the container.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,o as t,t as n}from"./index-
|
|
1
|
+
import{a as e,o as t,t as n}from"./index-DyY8OeVc.js";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){e.then(e=>{for(let t of e.getTracks())t.stop()}).catch(()=>{})}function a(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 o(o){let{sttSampleRate:s,ttsSampleRate:c,captureWorkletSrc:l,playbackWorkletSrc:u,onMicData:d,onError:f,onPlaybackStats:p,onMicSilent:m}=o,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)]),r(_.sampleRate,s,`capture`),r(h.sampleRate,c,`playback`)}catch(e){throw i(y),await v(),e}let x=_.createMediaStreamSource(b),S=a(_,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{o as createVoiceIO};
|
package/dist/default-client/assets/{capture-processor-dyyJArQs.js → capture-processor-DZg_Q6hY.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{n as e,r as t}from"./index-
|
|
1
|
+
import{n as e,r as t}from"./index-DyY8OeVc.js";import{t as n}from"./_module-url-BX0RuRU2.js";var r=n(`
|
|
2
2
|
class CaptureProcessor extends AudioWorkletProcessor {
|
|
3
3
|
constructor(options) {
|
|
4
4
|
super();
|