@alexkroman1/aai-ui 0.9.2 → 0.10.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/_components/app.d.ts +19 -1
- package/dist/_components/app.js +19 -1
- package/dist/_components/button.d.ts +43 -4
- package/dist/_components/button.js +33 -9
- package/dist/_components/chat-view.d.ts +25 -1
- package/dist/_components/chat-view.js +25 -1
- package/dist/_components/controls.d.ts +15 -1
- package/dist/_components/controls.js +15 -1
- package/dist/_components/error-banner.d.ts +15 -1
- package/dist/_components/error-banner.js +15 -1
- package/dist/_components/message-bubble.d.ts +19 -3
- package/dist/_components/message-bubble.js +17 -1
- package/dist/_components/message-list.d.ts +18 -1
- package/dist/_components/message-list.js +20 -3
- package/dist/_components/state-indicator.d.ts +23 -1
- package/dist/_components/state-indicator.js +23 -1
- package/dist/_components/thinking-indicator.d.ts +12 -1
- package/dist/_components/thinking-indicator.js +23 -11
- package/dist/_components/tool-call-block.d.ts +21 -1
- package/dist/_components/tool-call-block.js +33 -11
- package/dist/_components/transcript.d.ts +19 -1
- package/dist/_components/transcript.js +19 -1
- package/dist/_sqlite-stub.d.ts +3 -0
- package/dist/audio.js +13 -3
- package/dist/client-handler.d.ts +43 -0
- package/dist/index.d.ts +23 -6
- package/dist/index.js +3 -4
- package/dist/mount.d.ts +4 -0
- package/dist/mount.js +7 -4
- package/dist/session-D0RdWlWH.js +451 -0
- package/dist/session.d.ts +5 -36
- package/dist/session.js +1 -358
- package/dist/signals.d.ts +48 -0
- package/dist/signals.js +84 -25
- package/dist/types.d.ts +19 -5
- package/package.json +12 -10
- package/styles.css +2 -3
- package/dist/components.d.ts +0 -31
- package/dist/components.js +0 -17
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import type * as preact from "preact";
|
|
2
2
|
import type { Reactive } from "../types.ts";
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Live speech-to-text transcript shown while the user is speaking.
|
|
5
|
+
* Returns `null` when there is no active utterance.
|
|
6
|
+
*
|
|
7
|
+
* Displays a {@link ThinkingIndicator} while waiting for the first
|
|
8
|
+
* transcription text (i.e., `userUtterance` is `""`).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const { session } = useSession();
|
|
13
|
+
* <Transcript userUtterance={session.userUtterance} />
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* @param userUtterance - Reactive string (`null` when idle, `""` when
|
|
17
|
+
* listening but no text yet, or the transcript text).
|
|
18
|
+
* @param className - Additional CSS class names.
|
|
19
|
+
*
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
4
22
|
export declare function Transcript({ userUtterance, className, }: {
|
|
5
23
|
userUtterance: Reactive<string | null>;
|
|
6
24
|
className?: string;
|
|
@@ -2,7 +2,25 @@ import { ThinkingIndicator } from "./thinking-indicator.js";
|
|
|
2
2
|
import clsx from "clsx";
|
|
3
3
|
import { jsx } from "preact/jsx-runtime";
|
|
4
4
|
//#region _components/transcript.tsx
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Live speech-to-text transcript shown while the user is speaking.
|
|
7
|
+
* Returns `null` when there is no active utterance.
|
|
8
|
+
*
|
|
9
|
+
* Displays a {@link ThinkingIndicator} while waiting for the first
|
|
10
|
+
* transcription text (i.e., `userUtterance` is `""`).
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* const { session } = useSession();
|
|
15
|
+
* <Transcript userUtterance={session.userUtterance} />
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @param userUtterance - Reactive string (`null` when idle, `""` when
|
|
19
|
+
* listening but no text yet, or the transcript text).
|
|
20
|
+
* @param className - Additional CSS class names.
|
|
21
|
+
*
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
6
24
|
function Transcript({ userUtterance, className }) {
|
|
7
25
|
if (userUtterance.value === null) return null;
|
|
8
26
|
return /* @__PURE__ */ jsx("div", {
|
package/dist/audio.js
CHANGED
|
@@ -29,7 +29,9 @@ async function createVoiceIO(opts) {
|
|
|
29
29
|
await Promise.all([ctx.audioWorklet.addModule(captureWorkletSrc), ctx.audioWorklet.addModule(playbackWorkletSrc)]);
|
|
30
30
|
} catch (err) {
|
|
31
31
|
for (const t of stream.getTracks()) t.stop();
|
|
32
|
-
await ctx.close().catch(() => {
|
|
32
|
+
await ctx.close().catch((err) => {
|
|
33
|
+
console.warn("AudioContext close failed:", err);
|
|
34
|
+
});
|
|
33
35
|
throw err;
|
|
34
36
|
}
|
|
35
37
|
const mic = ctx.createMediaStreamSource(stream);
|
|
@@ -43,7 +45,9 @@ async function createVoiceIO(opts) {
|
|
|
43
45
|
});
|
|
44
46
|
mic.connect(capNode);
|
|
45
47
|
const chunkSizeBytes = Math.floor(sttSampleRate * MIC_BUFFER_SECONDS) * 2;
|
|
46
|
-
const
|
|
48
|
+
const capBufA = new Uint8Array(chunkSizeBytes * 2);
|
|
49
|
+
const capBufB = new Uint8Array(chunkSizeBytes * 2);
|
|
50
|
+
let capBuf = capBufA;
|
|
47
51
|
let capOffset = 0;
|
|
48
52
|
capNode.port.postMessage({ event: "start" });
|
|
49
53
|
capNode.port.onmessage = (e) => {
|
|
@@ -52,8 +56,11 @@ async function createVoiceIO(opts) {
|
|
|
52
56
|
capBuf.set(chunk, capOffset);
|
|
53
57
|
capOffset += chunk.byteLength;
|
|
54
58
|
if (capOffset >= chunkSizeBytes) {
|
|
55
|
-
|
|
59
|
+
const sendBuf = capBuf;
|
|
60
|
+
const filled = capOffset;
|
|
61
|
+
capBuf = capBuf === capBufA ? capBufB : capBufA;
|
|
56
62
|
capOffset = 0;
|
|
63
|
+
onMicData(sendBuf.buffer.slice(0, filled));
|
|
57
64
|
}
|
|
58
65
|
};
|
|
59
66
|
let playNode = null;
|
|
@@ -98,6 +105,9 @@ async function createVoiceIO(opts) {
|
|
|
98
105
|
if (lifecycle.signal.aborted) return;
|
|
99
106
|
lifecycle.abort();
|
|
100
107
|
capNode.port.postMessage({ event: "stop" });
|
|
108
|
+
mic.disconnect();
|
|
109
|
+
capNode.disconnect();
|
|
110
|
+
if (playNode) playNode.disconnect();
|
|
101
111
|
for (const t of stream.getTracks()) t.stop();
|
|
102
112
|
await ctx.close().catch(() => {});
|
|
103
113
|
},
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ReadyConfig } from "@alexkroman1/aai/protocol";
|
|
2
|
+
import type { VoiceIO } from "./audio.ts";
|
|
3
|
+
import type { AgentState, ChatMessage, Reactive, SessionError, ToolCallInfo } from "./types.ts";
|
|
4
|
+
type BatchFn = (fn: () => void) => void;
|
|
5
|
+
/**
|
|
6
|
+
* Handles server→client messages and updates reactive Preact signals
|
|
7
|
+
* accordingly (state transitions, transcripts, messages, audio playback).
|
|
8
|
+
*
|
|
9
|
+
* @internal Exported for testing only.
|
|
10
|
+
*/
|
|
11
|
+
export declare class ClientHandler {
|
|
12
|
+
#private;
|
|
13
|
+
constructor(opts: {
|
|
14
|
+
state: Reactive<AgentState>;
|
|
15
|
+
messages: Reactive<ChatMessage[]>;
|
|
16
|
+
toolCalls: Reactive<ToolCallInfo[]>;
|
|
17
|
+
userUtterance: Reactive<string | null>;
|
|
18
|
+
agentUtterance: Reactive<string | null>;
|
|
19
|
+
error: Reactive<SessionError | null>;
|
|
20
|
+
voiceIO: () => VoiceIO | null;
|
|
21
|
+
batch: BatchFn;
|
|
22
|
+
});
|
|
23
|
+
/** Single entry point for all server→client session events. */
|
|
24
|
+
event(e: import("@alexkroman1/aai/protocol").ClientEvent): void;
|
|
25
|
+
/** Enqueue a PCM16 audio chunk for playback. Transitions state to `"speaking"` on the first chunk. */
|
|
26
|
+
playAudioChunk(chunk: Uint8Array): void;
|
|
27
|
+
/**
|
|
28
|
+
* Signal that the server has finished sending audio for this turn.
|
|
29
|
+
* Waits for the audio queue to drain, then transitions state to `"listening"`.
|
|
30
|
+
* Uses the `#generation` counter to discard stale completions from interrupted turns.
|
|
31
|
+
*/
|
|
32
|
+
playAudioDone(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Dispatch an incoming WebSocket message (text or binary).
|
|
35
|
+
*
|
|
36
|
+
* Returns the parsed config if the message is a `config` message,
|
|
37
|
+
* otherwise `null`.
|
|
38
|
+
*/
|
|
39
|
+
handleMessage(data: string | ArrayBuffer): (ReadyConfig & {
|
|
40
|
+
sessionId?: string;
|
|
41
|
+
}) | null;
|
|
42
|
+
}
|
|
43
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
* Browser client library for AAI voice agents.
|
|
3
3
|
*
|
|
4
4
|
* Provides WebSocket session management, audio capture/playback,
|
|
5
|
-
* and Preact UI components. For narrower
|
|
6
|
-
*
|
|
7
|
-
* - `@aai/ui/session` — WebSocket session only (no Preact dependency)
|
|
8
|
-
* - `@aai/ui/components` — Preact components, mount helpers, and signals
|
|
5
|
+
* and Preact UI components. For a narrower import without the Preact
|
|
6
|
+
* dependency, use `@aai/ui/session`.
|
|
9
7
|
*
|
|
10
8
|
* @example
|
|
11
9
|
* ```tsx
|
|
@@ -14,7 +12,26 @@
|
|
|
14
12
|
* mount(App, { target: "#app" });
|
|
15
13
|
* ```
|
|
16
14
|
*/
|
|
17
|
-
export
|
|
15
|
+
export { App } from "./_components/app.tsx";
|
|
16
|
+
export type { ButtonSize, ButtonVariant } from "./_components/button.tsx";
|
|
17
|
+
export { Button } from "./_components/button.tsx";
|
|
18
|
+
export { ChatView } from "./_components/chat-view.tsx";
|
|
19
|
+
export { Controls } from "./_components/controls.tsx";
|
|
20
|
+
export { ErrorBanner } from "./_components/error-banner.tsx";
|
|
21
|
+
export { MessageBubble } from "./_components/message-bubble.tsx";
|
|
22
|
+
export { MessageList } from "./_components/message-list.tsx";
|
|
23
|
+
export { SidebarLayout } from "./_components/sidebar-layout.tsx";
|
|
24
|
+
export { StartScreen } from "./_components/start-screen.tsx";
|
|
25
|
+
export { StateIndicator } from "./_components/state-indicator.tsx";
|
|
26
|
+
export { ThinkingIndicator } from "./_components/thinking-indicator.tsx";
|
|
27
|
+
export { ToolCallBlock } from "./_components/tool-call-block.tsx";
|
|
28
|
+
export { Transcript } from "./_components/transcript.tsx";
|
|
29
|
+
export type { MountHandle, MountOptions } from "./mount.tsx";
|
|
30
|
+
export { mount } from "./mount.tsx";
|
|
31
|
+
export type { MountConfig, MountTheme } from "./mount-context.ts";
|
|
32
|
+
export { useMountConfig } from "./mount-context.ts";
|
|
18
33
|
export type { VoiceSession } from "./session.ts";
|
|
19
34
|
export { createVoiceSession } from "./session.ts";
|
|
20
|
-
export type {
|
|
35
|
+
export type { SessionSignals } from "./signals.ts";
|
|
36
|
+
export { createSessionControls, SessionProvider, useAutoScroll, useSession, useToolCallStart, useToolCallUpdate, useToolResult, } from "./signals.ts";
|
|
37
|
+
export type { AgentState, ChatMessage, Reactive, SessionError, SessionErrorCode, ToolCallInfo, VoiceSessionOptions, } from "./types.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useMountConfig } from "./mount-context.js";
|
|
2
|
-
import { SessionProvider, createSessionControls, useAutoScroll, useSession, useToolResult } from "./signals.js";
|
|
2
|
+
import { SessionProvider, createSessionControls, useAutoScroll, useSession, useToolCallStart, useToolCallUpdate, useToolResult } from "./signals.js";
|
|
3
3
|
import { Button } from "./_components/button.js";
|
|
4
4
|
import { Controls } from "./_components/controls.js";
|
|
5
5
|
import { ErrorBanner } from "./_components/error-banner.js";
|
|
@@ -13,7 +13,6 @@ import { ChatView } from "./_components/chat-view.js";
|
|
|
13
13
|
import { StartScreen } from "./_components/start-screen.js";
|
|
14
14
|
import { App } from "./_components/app.js";
|
|
15
15
|
import { SidebarLayout } from "./_components/sidebar-layout.js";
|
|
16
|
-
import { createVoiceSession } from "./session.js";
|
|
16
|
+
import { t as createVoiceSession } from "./session-D0RdWlWH.js";
|
|
17
17
|
import { mount } from "./mount.js";
|
|
18
|
-
|
|
19
|
-
export { App, Button, ChatView, Controls, ErrorBanner, MessageBubble, MessageList, SessionProvider, SidebarLayout, StartScreen, StateIndicator, ThinkingIndicator, ToolCallBlock, Transcript, createSessionControls, createVoiceSession, mount, useAutoScroll, useMountConfig, useSession, useToolResult };
|
|
18
|
+
export { App, Button, ChatView, Controls, ErrorBanner, MessageBubble, MessageList, SessionProvider, SidebarLayout, StartScreen, StateIndicator, ThinkingIndicator, ToolCallBlock, Transcript, createSessionControls, createVoiceSession, mount, useAutoScroll, useMountConfig, useSession, useToolCallStart, useToolCallUpdate, useToolResult };
|
package/dist/mount.d.ts
CHANGED
|
@@ -16,6 +16,10 @@ export type MountOptions = {
|
|
|
16
16
|
title?: string;
|
|
17
17
|
/** Theme color overrides. */
|
|
18
18
|
theme?: MountTheme;
|
|
19
|
+
/** Called when the server sends a session ID. Store it for reconnection. */
|
|
20
|
+
onSessionId?: ((sessionId: string) => void) | undefined;
|
|
21
|
+
/** Session ID from a previous connection for resuming persisted state. */
|
|
22
|
+
resumeSessionId?: string | undefined;
|
|
19
23
|
};
|
|
20
24
|
/**
|
|
21
25
|
* Handle returned by {@link mount} for cleanup.
|
package/dist/mount.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { MountConfigProvider } from "./mount-context.js";
|
|
2
2
|
import { SessionProvider, createSessionControls } from "./signals.js";
|
|
3
|
-
import { createVoiceSession } from "./session.js";
|
|
3
|
+
import { t as createVoiceSession } from "./session-D0RdWlWH.js";
|
|
4
4
|
import { render } from "preact";
|
|
5
5
|
import { batch, signal } from "@preact/signals";
|
|
6
6
|
import { jsx } from "preact/jsx-runtime";
|
|
7
7
|
//#region mount.tsx
|
|
8
8
|
function resolveContainer(target = "#app") {
|
|
9
|
-
|
|
9
|
+
if (typeof target !== "string") return target;
|
|
10
|
+
const el = document.querySelector(target);
|
|
10
11
|
if (!el) throw new Error(`Element not found: ${target}`);
|
|
11
12
|
return el;
|
|
12
13
|
}
|
|
@@ -28,8 +29,10 @@ function mount(Component, options) {
|
|
|
28
29
|
const container = resolveContainer(options?.target);
|
|
29
30
|
const session = createVoiceSession({
|
|
30
31
|
platformUrl: options?.platformUrl ?? globalThis.location.origin + globalThis.location.pathname,
|
|
31
|
-
signal,
|
|
32
|
-
batch
|
|
32
|
+
reactiveFactory: signal,
|
|
33
|
+
batch,
|
|
34
|
+
onSessionId: options?.onSessionId,
|
|
35
|
+
resumeSessionId: options?.resumeSessionId
|
|
33
36
|
});
|
|
34
37
|
const signals = createSessionControls(session);
|
|
35
38
|
const mountConfig = {
|