@alexkroman1/aai-ui 1.8.2 → 1.9.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/_colors-DJUordGv.js +19 -0
- package/dist/_utils-5cs73OrA.js +16 -0
- package/dist/_utils.d.ts +4 -0
- package/dist/aai-logo-B8lDmsut.js +84 -0
- package/dist/audio.d.ts +9 -2
- package/dist/audio.js +41 -24
- package/dist/chat-view-C1XbqDk0.js +186 -0
- package/dist/components/_colors.d.ts +26 -0
- package/dist/components/aai-logo.d.ts +7 -6
- package/dist/components/button.d.ts +11 -13
- package/dist/components/button.js +7 -16
- package/dist/components/chat-view.d.ts +8 -19
- package/dist/components/chat-view.js +2 -98
- package/dist/components/controls.d.ts +1 -1
- package/dist/components/controls.js +2 -39
- package/dist/components/eyebrow.d.ts +12 -0
- package/dist/components/message-list.d.ts +1 -1
- package/dist/components/message-list.js +126 -56
- package/dist/components/sidebar-layout.d.ts +1 -1
- package/dist/components/start-screen.d.ts +4 -3
- package/dist/components/start-screen.js +20 -14
- package/dist/components/text-controls.d.ts +14 -0
- package/dist/components/tool-call-block.d.ts +5 -3
- package/dist/components/tool-call-block.js +1 -1
- package/dist/components/url-chips.d.ts +28 -0
- package/dist/context.d.ts +30 -4
- package/dist/context.js +86 -10
- package/dist/controls-BngrPbOC.js +135 -0
- package/dist/default-client/assets/audio-Cs-6t_Wd.js +1 -0
- package/dist/default-client/assets/capture-processor-C19oBn4L.js +105 -0
- package/dist/default-client/assets/index-Bf4ZTNcx.js +73 -0
- package/dist/default-client/assets/index-Bzlh9i7w.css +2 -0
- package/dist/default-client/assets/playback-processor-BtlzAH78.js +149 -0
- package/dist/default-client/index.html +3 -3
- package/dist/define-client.d.ts +2 -2
- package/dist/define-client.js +18 -24
- package/dist/eyebrow-C6ZFuiz6.js +27 -0
- package/dist/hooks.js +71 -42
- package/dist/index.d.ts +3 -1
- package/dist/index.js +6 -5
- package/dist/session-core-D3NDaySY.js +652 -0
- package/dist/session-core-messages.d.ts +50 -0
- package/dist/session-core-types.d.ts +147 -0
- package/dist/session-core-upload.d.ts +16 -0
- package/dist/session-core.d.ts +2 -68
- package/dist/session-core.js +1 -455
- package/dist/{tool-call-block-wby_jyoY.js → tool-call-block-7f1GTG-P.js} +33 -31
- package/dist/types.d.ts +29 -4
- package/dist/types.js +10 -1
- package/dist/worklets/capture-processor.d.ts +2 -0
- package/dist/worklets/capture-processor.js +80 -25
- package/dist/worklets/playback-processor.d.ts +2 -0
- package/dist/worklets/playback-processor.js +80 -29
- package/package.json +18 -18
- package/styles.css +30 -0
- package/dist/_react-test-utils.d.ts +0 -86
- package/dist/aai-logo-BqFv6JU6.js +0 -21
- package/dist/default-client/assets/audio-UowwhmYo.js +0 -1
- package/dist/default-client/assets/capture-processor-C26lSiVr.js +0 -53
- package/dist/default-client/assets/index-BZqVGR2o.css +0 -2
- package/dist/default-client/assets/index-YW9WUhbL.js +0 -49
- package/dist/default-client/assets/playback-processor-dSA8Im99.js +0 -101
|
@@ -1,99 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { t as
|
|
3
|
-
import { Controls } from "./controls.js";
|
|
4
|
-
import { MessageList } from "./message-list.js";
|
|
5
|
-
import clsx from "clsx";
|
|
6
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
-
//#region components/chat-view.tsx
|
|
8
|
-
/** @jsxImportSource react */
|
|
9
|
-
const STATE_COLORS = {
|
|
10
|
-
disconnected: "rgba(255,255,255,0.422)",
|
|
11
|
-
connecting: "rgba(255,255,255,0.422)",
|
|
12
|
-
ready: "#7fd88f",
|
|
13
|
-
listening: "#56b6c2",
|
|
14
|
-
thinking: "#f5a742",
|
|
15
|
-
speaking: "#e06c75",
|
|
16
|
-
error: "#e06c75"
|
|
17
|
-
};
|
|
18
|
-
/**
|
|
19
|
-
* The main chat interface for a voice agent session.
|
|
20
|
-
* Displays a header (with optional icon, title, and state indicator), an
|
|
21
|
-
* inline error banner, the {@link MessageList}, and session {@link Controls}.
|
|
22
|
-
*
|
|
23
|
-
* Must be rendered inside a {@link SessionProvider}.
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```tsx
|
|
27
|
-
* <StartScreen icon="🍕" title="Pizza Palace">
|
|
28
|
-
* <ChatView />
|
|
29
|
-
* </StartScreen>
|
|
30
|
-
* ```
|
|
31
|
-
*
|
|
32
|
-
* @example Pair with a sidebar
|
|
33
|
-
* ```tsx
|
|
34
|
-
* <SidebarLayout sidebar={<RecipeCard />}>
|
|
35
|
-
* <ChatView className="border-l" />
|
|
36
|
-
* </SidebarLayout>
|
|
37
|
-
* ```
|
|
38
|
-
*
|
|
39
|
-
* @example Custom header icon
|
|
40
|
-
* ```tsx
|
|
41
|
-
* <ChatView icon={<img src="/logo.svg" />} title="My Agent" />
|
|
42
|
-
* ```
|
|
43
|
-
*
|
|
44
|
-
* @param icon - Optional element rendered before the title in the header.
|
|
45
|
-
* @param title - Optional title string for the header.
|
|
46
|
-
* @param className - Additional CSS class names applied to the root element.
|
|
47
|
-
*
|
|
48
|
-
* @public
|
|
49
|
-
*/
|
|
50
|
-
function ChatView({ icon, title, className }) {
|
|
51
|
-
const session = useSession();
|
|
52
|
-
const theme = useTheme();
|
|
53
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
54
|
-
className: clsx("flex flex-col h-screen max-w-130 mx-auto font-aai text-sm", className),
|
|
55
|
-
style: {
|
|
56
|
-
background: theme.bg,
|
|
57
|
-
color: theme.text
|
|
58
|
-
},
|
|
59
|
-
children: [
|
|
60
|
-
/* @__PURE__ */ jsxs("div", {
|
|
61
|
-
className: "flex items-center gap-3 px-4 py-3 border-b shrink-0",
|
|
62
|
-
style: { borderColor: theme.border },
|
|
63
|
-
children: [
|
|
64
|
-
icon,
|
|
65
|
-
title ? /* @__PURE__ */ jsx("span", {
|
|
66
|
-
className: "text-sm font-semibold",
|
|
67
|
-
style: { color: theme.primary },
|
|
68
|
-
children: title
|
|
69
|
-
}) : !icon && /* @__PURE__ */ jsx(AaiLogo, {}),
|
|
70
|
-
/* @__PURE__ */ jsx("div", {
|
|
71
|
-
className: "ml-auto",
|
|
72
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
73
|
-
className: "inline-flex items-center justify-center gap-1.5 text-[13px] font-medium leading-[130%] capitalize",
|
|
74
|
-
style: { color: "rgba(255,255,255,0.284)" },
|
|
75
|
-
"data-state": session.state,
|
|
76
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
77
|
-
className: "w-2 h-2 rounded-full",
|
|
78
|
-
style: { background: STATE_COLORS[session.state] }
|
|
79
|
-
}), session.state]
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
]
|
|
83
|
-
}),
|
|
84
|
-
session.error && /* @__PURE__ */ jsx("div", {
|
|
85
|
-
className: "mx-4 mt-3 px-3 py-2 rounded-aai border text-[13px] leading-[130%]",
|
|
86
|
-
style: {
|
|
87
|
-
borderColor: "rgba(224,108,117,0.4)",
|
|
88
|
-
background: "rgba(224,108,117,0.08)",
|
|
89
|
-
color: "#e06c75"
|
|
90
|
-
},
|
|
91
|
-
children: session.error.message
|
|
92
|
-
}),
|
|
93
|
-
/* @__PURE__ */ jsx(MessageList, {}),
|
|
94
|
-
/* @__PURE__ */ jsx(Controls, {})
|
|
95
|
-
]
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
//#endregion
|
|
1
|
+
import "../context.js";
|
|
2
|
+
import { t as ChatView } from "../chat-view-C1XbqDk0.js";
|
|
99
3
|
export { ChatView };
|
|
@@ -1,40 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import clsx from "clsx";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
-
//#region components/controls.tsx
|
|
6
|
-
/** @jsxImportSource react */
|
|
7
|
-
/**
|
|
8
|
-
* Session control buttons: **Stop / Resume** and **New Conversation**.
|
|
9
|
-
*
|
|
10
|
-
* Reads session state from {@link useSession}. Must be rendered inside a
|
|
11
|
-
* {@link SessionProvider}.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```tsx
|
|
15
|
-
* <Controls className="justify-end" />
|
|
16
|
-
* ```
|
|
17
|
-
*
|
|
18
|
-
* @param className - Additional CSS class names applied to the container.
|
|
19
|
-
*
|
|
20
|
-
* @public
|
|
21
|
-
*/
|
|
22
|
-
function Controls({ className }) {
|
|
23
|
-
const { running, toggle, reset } = useSession();
|
|
24
|
-
const theme = useTheme();
|
|
25
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
26
|
-
className: clsx("flex gap-2 px-4 py-3 border-t shrink-0", className),
|
|
27
|
-
style: { borderColor: theme.border },
|
|
28
|
-
children: [/* @__PURE__ */ jsx(Button, {
|
|
29
|
-
variant: "secondary",
|
|
30
|
-
onClick: toggle,
|
|
31
|
-
children: running ? "Stop" : "Resume"
|
|
32
|
-
}), /* @__PURE__ */ jsx(Button, {
|
|
33
|
-
variant: "ghost",
|
|
34
|
-
onClick: reset,
|
|
35
|
-
children: "New Conversation"
|
|
36
|
-
})]
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
//#endregion
|
|
1
|
+
import "../context.js";
|
|
2
|
+
import { t as Controls } from "../controls-BngrPbOC.js";
|
|
40
3
|
export { Controls };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Eyebrow label — the design system's small outlined pill with ALL-CAPS,
|
|
4
|
+
* tracked text ("VOICE AGENT", "LISTENING", …). Used as a section label on
|
|
5
|
+
* the start screen and as the live status chip in the chat header.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export declare function Eyebrow({ children, className, style, ...rest }: {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
className?: string | undefined;
|
|
12
|
+
} & Omit<React.HTMLAttributes<HTMLSpanElement>, "className">): ReactNode;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useSession, useTheme } from "../context.js";
|
|
2
|
-
import {
|
|
2
|
+
import { a as primaryTint, i as TEXT_MUTED, r as TEXT_FAINT } from "../_colors-DJUordGv.js";
|
|
3
|
+
import { t as ToolCallBlock } from "../tool-call-block-7f1GTG-P.js";
|
|
3
4
|
import clsx from "clsx";
|
|
4
5
|
import { useEffect, useMemo, useRef } from "react";
|
|
5
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -17,47 +18,143 @@ const DOT_STYLES = [
|
|
|
17
18
|
function ThinkingDots() {
|
|
18
19
|
return /* @__PURE__ */ jsx("div", {
|
|
19
20
|
className: "flex items-center gap-2 text-sm font-medium min-h-5",
|
|
20
|
-
style: { color:
|
|
21
|
+
style: { color: TEXT_MUTED },
|
|
21
22
|
children: DOT_STYLES.map((style, i) => /* @__PURE__ */ jsx("div", {
|
|
22
23
|
className: "w-1.5 h-1.5 rounded-full",
|
|
23
24
|
style: {
|
|
24
25
|
...style,
|
|
25
|
-
background:
|
|
26
|
+
background: TEXT_MUTED
|
|
26
27
|
}
|
|
27
28
|
}, i))
|
|
28
29
|
});
|
|
29
30
|
}
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Right-aligned user bubble — a primary-tinted card (the design system's
|
|
33
|
+
* indigo-50 fill with an indigo-100 edge, derived from the theme primary so
|
|
34
|
+
* custom themes stay coherent). Shared by finalized messages and the live
|
|
35
|
+
* transcript.
|
|
36
|
+
*/
|
|
37
|
+
function UserBubble({ theme, color, children }) {
|
|
38
|
+
return /* @__PURE__ */ jsx("div", {
|
|
33
39
|
className: "flex flex-col w-full items-end",
|
|
34
40
|
children: /* @__PURE__ */ jsx("div", {
|
|
35
|
-
className: "max-w-[min(
|
|
41
|
+
className: "max-w-[min(78%,64ch)] border px-3.5 py-2.5 rounded-md whitespace-pre-wrap wrap-break-word text-[15px] font-normal leading-[22px]",
|
|
36
42
|
style: {
|
|
37
|
-
background:
|
|
38
|
-
borderColor: theme.
|
|
39
|
-
color
|
|
43
|
+
background: primaryTint(theme.primary, theme.surface, 7),
|
|
44
|
+
borderColor: primaryTint(theme.primary, theme.surface, 16),
|
|
45
|
+
color
|
|
40
46
|
},
|
|
41
|
-
children
|
|
47
|
+
children
|
|
42
48
|
})
|
|
43
49
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
}
|
|
51
|
+
/** Renders a single chat message: labeled agent prose, or a user bubble. */
|
|
52
|
+
function MessageBubble({ message, theme }) {
|
|
53
|
+
if (message.role === "user") return /* @__PURE__ */ jsx(UserBubble, {
|
|
54
|
+
theme,
|
|
55
|
+
color: theme.text,
|
|
47
56
|
children: message.content
|
|
48
57
|
});
|
|
58
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
59
|
+
className: "flex flex-col gap-1 max-w-[82%]",
|
|
60
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
61
|
+
className: "text-[10px] font-medium tracking-[1.2px] uppercase leading-none",
|
|
62
|
+
style: { color: TEXT_FAINT },
|
|
63
|
+
children: "Agent"
|
|
64
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
65
|
+
className: "whitespace-pre-wrap wrap-break-word text-[15px] font-normal leading-[23px]",
|
|
66
|
+
style: { color: theme.text },
|
|
67
|
+
children: message.content
|
|
68
|
+
})]
|
|
69
|
+
});
|
|
49
70
|
}
|
|
50
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Smooth-scroll to the anchor whenever the content version advances.
|
|
73
|
+
*
|
|
74
|
+
* The scroll runs inside `requestAnimationFrame` and is deduped per frame:
|
|
75
|
+
* several snapshot updates in one frame (transcript + message + tool call)
|
|
76
|
+
* trigger a single scroll after layout instead of one forced layout each.
|
|
77
|
+
*/
|
|
78
|
+
function useAutoScroll(contentVersion) {
|
|
51
79
|
const ref = useRef(null);
|
|
80
|
+
const scheduledRef = useRef(false);
|
|
52
81
|
useEffect(() => {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
82
|
+
if (contentVersion === 0 || scheduledRef.current) return;
|
|
83
|
+
scheduledRef.current = true;
|
|
84
|
+
requestAnimationFrame(() => {
|
|
85
|
+
scheduledRef.current = false;
|
|
86
|
+
ref.current?.scrollIntoView({
|
|
87
|
+
behavior: "smooth",
|
|
88
|
+
block: "end"
|
|
89
|
+
});
|
|
56
90
|
});
|
|
57
|
-
});
|
|
91
|
+
}, [contentVersion]);
|
|
58
92
|
return ref;
|
|
59
93
|
}
|
|
60
94
|
/**
|
|
95
|
+
* Interleave messages and tool calls into render items, ordered by insertion
|
|
96
|
+
* time. Each tool call renders immediately after its anchor message
|
|
97
|
+
* (`afterMessageId`); tool calls whose anchor slid out of the retained window
|
|
98
|
+
* (or that were inserted before any message existed) render first.
|
|
99
|
+
*/
|
|
100
|
+
function interleave(messages, toolCalls, renderMessage, renderToolCall) {
|
|
101
|
+
const items = [];
|
|
102
|
+
let tci = 0;
|
|
103
|
+
const pushToolCallsThrough = (maxAfterId) => {
|
|
104
|
+
let tc = toolCalls[tci];
|
|
105
|
+
while (tc && tc.afterMessageId <= maxAfterId) {
|
|
106
|
+
items.push(renderToolCall(tc));
|
|
107
|
+
tci++;
|
|
108
|
+
tc = toolCalls[tci];
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const firstMessage = messages[0];
|
|
112
|
+
if (firstMessage) pushToolCallsThrough(firstMessage.id - 1);
|
|
113
|
+
for (const msg of messages) {
|
|
114
|
+
items.push(renderMessage(msg));
|
|
115
|
+
pushToolCallsThrough(msg.id);
|
|
116
|
+
}
|
|
117
|
+
pushToolCallsThrough(Number.POSITIVE_INFINITY);
|
|
118
|
+
return items;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Build the interleaved row elements, reusing each row's element object across
|
|
122
|
+
* renders while its inputs are unchanged. Returning the identical element
|
|
123
|
+
* reference lets React bail out of re-rendering that row entirely — the same
|
|
124
|
+
* effect as wrapping the row components in `memo()` — so appending one message
|
|
125
|
+
* re-renders one row, not the whole capped list. Rows are keyed on stable ids
|
|
126
|
+
* (`ChatMessage.id`, `ToolCallInfo.callId`), which survive the sliding
|
|
127
|
+
* 200-message window; message and tool-call objects are referentially stable
|
|
128
|
+
* across snapshots, making the identity checks below sufficient.
|
|
129
|
+
*/
|
|
130
|
+
function useChatItems(messages, toolCalls, theme) {
|
|
131
|
+
const cacheRef = useRef(/* @__PURE__ */ new Map());
|
|
132
|
+
return useMemo(() => {
|
|
133
|
+
const prev = cacheRef.current;
|
|
134
|
+
const next = /* @__PURE__ */ new Map();
|
|
135
|
+
const rowFor = (key, data, make) => {
|
|
136
|
+
const hit = prev.get(key);
|
|
137
|
+
const element = hit && hit.data === data && hit.theme === theme ? hit.element : make();
|
|
138
|
+
next.set(key, {
|
|
139
|
+
data,
|
|
140
|
+
theme,
|
|
141
|
+
element
|
|
142
|
+
});
|
|
143
|
+
return element;
|
|
144
|
+
};
|
|
145
|
+
const items = interleave(messages, toolCalls, (msg) => rowFor(`m${msg.id}`, msg, () => /* @__PURE__ */ jsx(MessageBubble, {
|
|
146
|
+
message: msg,
|
|
147
|
+
theme
|
|
148
|
+
}, msg.id)), (tc) => rowFor(`t${tc.callId}`, tc, () => /* @__PURE__ */ jsx(ToolCallBlock, { toolCall: tc }, tc.callId)));
|
|
149
|
+
cacheRef.current = next;
|
|
150
|
+
return items;
|
|
151
|
+
}, [
|
|
152
|
+
messages,
|
|
153
|
+
toolCalls,
|
|
154
|
+
theme
|
|
155
|
+
]);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
61
158
|
* Scrollable list of all chat messages, tool-call blocks, live transcript,
|
|
62
159
|
* streaming agent utterance, and a thinking indicator.
|
|
63
160
|
*
|
|
@@ -78,7 +175,6 @@ function useAutoScroll() {
|
|
|
78
175
|
function MessageList({ className }) {
|
|
79
176
|
const session = useSession();
|
|
80
177
|
const theme = useTheme();
|
|
81
|
-
const scrollRef = useAutoScroll();
|
|
82
178
|
const showThinking = useMemo(() => {
|
|
83
179
|
if (session.state !== "thinking") return false;
|
|
84
180
|
const last = session.toolCalls.at(-1);
|
|
@@ -90,54 +186,28 @@ function MessageList({ className }) {
|
|
|
90
186
|
session.toolCalls,
|
|
91
187
|
session.messages
|
|
92
188
|
]);
|
|
93
|
-
const { messages, toolCalls } = session;
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
for (const [i, msg] of messages.entries()) {
|
|
97
|
-
items.push(/* @__PURE__ */ jsx(MessageBubble, {
|
|
98
|
-
message: msg,
|
|
99
|
-
theme
|
|
100
|
-
}, `msg-${i}`));
|
|
101
|
-
let tc = toolCalls[tci];
|
|
102
|
-
while (tc && tc.afterMessageIndex <= i) {
|
|
103
|
-
items.push(/* @__PURE__ */ jsx(ToolCallBlock, { toolCall: tc }, tc.callId));
|
|
104
|
-
tci++;
|
|
105
|
-
tc = toolCalls[tci];
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
let tc = toolCalls[tci];
|
|
109
|
-
while (tc) {
|
|
110
|
-
items.push(/* @__PURE__ */ jsx(ToolCallBlock, { toolCall: tc }, tc.callId));
|
|
111
|
-
tci++;
|
|
112
|
-
tc = toolCalls[tci];
|
|
113
|
-
}
|
|
114
|
-
const userTranscript = session.userTranscript;
|
|
189
|
+
const { messages, toolCalls, userTranscript, agentTranscript } = session;
|
|
190
|
+
const scrollRef = useAutoScroll(session.contentVersion);
|
|
191
|
+
const items = useChatItems(messages, toolCalls, theme);
|
|
115
192
|
return /* @__PURE__ */ jsx("div", {
|
|
116
193
|
role: "log",
|
|
117
194
|
className: clsx("flex-1 overflow-y-auto [scrollbar-width:none]", className),
|
|
118
195
|
style: { background: theme.surface },
|
|
119
196
|
children: /* @__PURE__ */ jsxs("div", {
|
|
120
|
-
className: "flex flex-col gap-4
|
|
197
|
+
className: "flex flex-col gap-4 p-7",
|
|
121
198
|
children: [
|
|
122
199
|
items,
|
|
123
|
-
|
|
200
|
+
agentTranscript && /* @__PURE__ */ jsx(MessageBubble, {
|
|
124
201
|
message: {
|
|
125
202
|
role: "assistant",
|
|
126
|
-
content:
|
|
203
|
+
content: agentTranscript
|
|
127
204
|
},
|
|
128
205
|
theme
|
|
129
206
|
}),
|
|
130
|
-
userTranscript !== null && /* @__PURE__ */ jsx(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
style: {
|
|
135
|
-
color: "rgba(255,255,255,0.284)",
|
|
136
|
-
background: "rgba(255,255,255,0.031)",
|
|
137
|
-
borderColor: theme.border
|
|
138
|
-
},
|
|
139
|
-
children: userTranscript || /* @__PURE__ */ jsx(ThinkingDots, {})
|
|
140
|
-
})
|
|
207
|
+
userTranscript !== null && /* @__PURE__ */ jsx(UserBubble, {
|
|
208
|
+
theme,
|
|
209
|
+
color: "#6F6A60",
|
|
210
|
+
children: userTranscript ? userTranscript : /* @__PURE__ */ jsx(ThinkingDots, {})
|
|
141
211
|
}),
|
|
142
212
|
showThinking && /* @__PURE__ */ jsx(ThinkingDots, {}),
|
|
143
213
|
/* @__PURE__ */ jsx("div", { ref: scrollRef })
|
|
@@ -18,4 +18,4 @@ export declare function SidebarLayout({ sidebar, children, sidebarWidth, sidebar
|
|
|
18
18
|
sidebarWidth?: string | undefined;
|
|
19
19
|
sidebarPosition?: "left" | "right" | undefined;
|
|
20
20
|
className?: string;
|
|
21
|
-
}): import("react
|
|
21
|
+
}): import("react").JSX.Element;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
/**
|
|
3
|
-
* A centered start screen
|
|
4
|
-
*
|
|
3
|
+
* A centered start screen: a white card on the cream page with the logo, an
|
|
4
|
+
* eyebrow label, a serif title, subtitle, and the start CTA. Renders
|
|
5
|
+
* `children` (the main app) once the session has started.
|
|
5
6
|
*
|
|
6
7
|
* @example
|
|
7
8
|
* ```tsx
|
|
@@ -23,4 +24,4 @@ export declare function StartScreen({ children, icon, title, subtitle, buttonTex
|
|
|
23
24
|
subtitle?: string | undefined;
|
|
24
25
|
buttonText?: string | undefined;
|
|
25
26
|
className?: string | undefined;
|
|
26
|
-
}): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean |
|
|
27
|
+
}): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useSessionCore, useSessionSelector, useTheme } from "../context.js";
|
|
2
2
|
import { Button } from "./button.js";
|
|
3
|
-
import
|
|
3
|
+
import "../_colors-DJUordGv.js";
|
|
4
|
+
import { t as AaiLogo } from "../aai-logo-B8lDmsut.js";
|
|
5
|
+
import { t as Eyebrow } from "../eyebrow-C6ZFuiz6.js";
|
|
4
6
|
import clsx from "clsx";
|
|
5
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
8
|
//#region components/start-screen.tsx
|
|
7
9
|
/** @jsxImportSource react */
|
|
8
10
|
/**
|
|
9
|
-
* A centered start screen
|
|
10
|
-
*
|
|
11
|
+
* A centered start screen: a white card on the cream page with the logo, an
|
|
12
|
+
* eyebrow label, a serif title, subtitle, and the start CTA. Renders
|
|
13
|
+
* `children` (the main app) once the session has started.
|
|
11
14
|
*
|
|
12
15
|
* @example
|
|
13
16
|
* ```tsx
|
|
@@ -22,34 +25,37 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
22
25
|
*
|
|
23
26
|
* @public
|
|
24
27
|
*/
|
|
25
|
-
function StartScreen({ children, icon, title, subtitle, buttonText = "Start", className }) {
|
|
26
|
-
const
|
|
28
|
+
function StartScreen({ children, icon, title, subtitle, buttonText = "Start Conversation", className }) {
|
|
29
|
+
const started = useSessionSelector((s) => s.started);
|
|
30
|
+
const { start } = useSessionCore();
|
|
27
31
|
const theme = useTheme();
|
|
28
32
|
if (started) return children;
|
|
29
33
|
return /* @__PURE__ */ jsx("div", {
|
|
30
34
|
className: clsx("flex items-center justify-center h-screen font-aai", className),
|
|
31
35
|
style: { background: theme.bg },
|
|
32
36
|
children: /* @__PURE__ */ jsxs("div", {
|
|
33
|
-
className: "flex flex-col items-center gap-
|
|
37
|
+
className: "flex flex-col items-center gap-5 border rounded-xl px-10 py-12 sm:px-16 sm:py-14 max-w-105 text-center",
|
|
34
38
|
style: {
|
|
35
39
|
background: theme.surface,
|
|
36
|
-
borderColor: theme.border
|
|
40
|
+
borderColor: theme.border,
|
|
41
|
+
boxShadow: "0 4px 12px -2px rgb(20 18 12 / 0.08)"
|
|
37
42
|
},
|
|
38
43
|
children: [
|
|
39
|
-
icon ??
|
|
44
|
+
icon ?? /* @__PURE__ */ jsx(AaiLogo, { size: 24 }),
|
|
45
|
+
/* @__PURE__ */ jsx(Eyebrow, { children: "Voice Agent" }),
|
|
40
46
|
title && /* @__PURE__ */ jsx("h1", {
|
|
41
|
-
className: "font-
|
|
42
|
-
style: { color: theme.
|
|
47
|
+
className: "font-aai-serif font-normal text-[32px] leading-[1.15] tracking-[-0.2px] m-0 text-balance",
|
|
48
|
+
style: { color: theme.text },
|
|
43
49
|
children: title
|
|
44
50
|
}),
|
|
45
51
|
subtitle && /* @__PURE__ */ jsx("p", {
|
|
46
|
-
className: "text-
|
|
47
|
-
style: { color: "
|
|
52
|
+
className: "text-[15px] leading-[22px] m-0 max-w-75",
|
|
53
|
+
style: { color: "#57534B" },
|
|
48
54
|
children: subtitle
|
|
49
55
|
}),
|
|
50
56
|
/* @__PURE__ */ jsx(Button, {
|
|
51
57
|
size: "lg",
|
|
52
|
-
className: "mt-2
|
|
58
|
+
className: "mt-2",
|
|
53
59
|
onClick: start,
|
|
54
60
|
children: buttonText
|
|
55
61
|
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Controls for a text-only session (`tts: none()`): a **record** toggle that
|
|
3
|
+
* streams the microphone while active, an **upload** button that transcribes
|
|
4
|
+
* an audio file, and **New Conversation**. Replies arrive as text in the
|
|
5
|
+
* {@link MessageList} — there is no playback side.
|
|
6
|
+
*
|
|
7
|
+
* Rendered automatically by {@link ChatView} when the server declares the
|
|
8
|
+
* session text-only; custom clients can compose it directly.
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export declare function TextControls({ className }: {
|
|
13
|
+
className?: string | undefined;
|
|
14
|
+
}): import("react").JSX.Element;
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { ToolCallInfo } from "../types.ts";
|
|
3
3
|
/**
|
|
4
|
-
* Renders a tool invocation
|
|
5
|
-
*
|
|
4
|
+
* Renders a tool invocation as the design system's console row: a small
|
|
5
|
+
* outlined "TOOL" chip (or the tool's configured icon), the tool name in
|
|
6
|
+
* mono, a truncated args preview, and a rotating chevron that expands the
|
|
7
|
+
* formatted result.
|
|
6
8
|
*
|
|
7
9
|
* Tool display is configured via `ToolConfigContext`. If no config is found
|
|
8
10
|
* for a tool name, the raw tool name is shown as the title.
|
|
9
11
|
*
|
|
10
12
|
* While the tool call is pending a shimmer animation is shown. Once
|
|
11
|
-
* complete, clicking the
|
|
13
|
+
* complete, clicking the row expands the formatted JSON result.
|
|
12
14
|
*
|
|
13
15
|
* @example
|
|
14
16
|
* ```tsx
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session's shareable UI URL.
|
|
3
|
+
*
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
export declare function UiUrlChip({ className }: {
|
|
7
|
+
className?: string | undefined;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
9
|
+
/**
|
|
10
|
+
* The session's programmatic WebSocket endpoint — the URL a script or backend
|
|
11
|
+
* can connect to directly instead of using this UI.
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
export declare function ApiUrlChip({ className }: {
|
|
16
|
+
className?: string | undefined;
|
|
17
|
+
}): import("react").JSX.Element;
|
|
18
|
+
/**
|
|
19
|
+
* The UI and API URLs side by side. They answer the same question — "how do I
|
|
20
|
+
* reach this agent?" — so they belong together and each needs its label to be
|
|
21
|
+
* told apart. Rendered by the default shell in every session mode (S2S,
|
|
22
|
+
* pipeline, text-only).
|
|
23
|
+
*
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
export declare function SessionUrlChips({ className }: {
|
|
27
|
+
className?: string | undefined;
|
|
28
|
+
}): import("react").JSX.Element;
|
package/dist/context.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { SessionCore, SessionSnapshot } from "./session-core.ts";
|
|
3
3
|
import type { ClientTheme } from "./types.ts";
|
|
4
|
-
declare const DEFAULT_THEME: Required<ClientTheme>;
|
|
5
4
|
export declare function SessionProvider({ value, children }: {
|
|
6
5
|
value: SessionCore;
|
|
7
6
|
children?: ReactNode;
|
|
@@ -13,11 +12,38 @@ export type Session = SessionSnapshot & {
|
|
|
13
12
|
reset(): void;
|
|
14
13
|
disconnect(): void;
|
|
15
14
|
toggle(): void;
|
|
15
|
+
startRecording(): void;
|
|
16
|
+
stopRecording(): void;
|
|
17
|
+
sendAudioFile(file: Blob): Promise<void>;
|
|
16
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* Return the raw {@link SessionCore} from context without subscribing to
|
|
21
|
+
* snapshot changes. Useful for accessing stable methods (`start`, `toggle`,
|
|
22
|
+
* `reset`, …) from components that select narrow state via
|
|
23
|
+
* {@link useSessionSelector}.
|
|
24
|
+
*
|
|
25
|
+
* Not part of the package's public export surface — internal to aai-ui
|
|
26
|
+
* components.
|
|
27
|
+
*/
|
|
28
|
+
export declare function useSessionCore(): SessionCore;
|
|
17
29
|
export declare function useSession(): Session;
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Subscribe to a narrow slice of the session snapshot.
|
|
32
|
+
*
|
|
33
|
+
* Unlike {@link useSession} — which re-renders the component on *every*
|
|
34
|
+
* snapshot change — this only triggers a re-render when the selected value
|
|
35
|
+
* changes (per `isEqual`, default `Object.is`). Use it for components that
|
|
36
|
+
* read a single field, e.g. `useSessionSelector((s) => s.running)`.
|
|
37
|
+
*
|
|
38
|
+
* The selector must be pure. It may run on every snapshot change, so keep it
|
|
39
|
+
* cheap; when it returns a derived object, pass a custom `isEqual` to avoid
|
|
40
|
+
* re-renders on referentially-new-but-equal results.
|
|
41
|
+
*
|
|
42
|
+
* @public
|
|
43
|
+
*/
|
|
44
|
+
export declare function useSessionSelector<T>(selector: (snapshot: SessionSnapshot) => T, isEqual?: (a: T, b: T) => boolean): T;
|
|
45
|
+
export declare function ThemeProvider({ value, children, }: {
|
|
46
|
+
value?: ClientTheme | undefined;
|
|
20
47
|
children?: ReactNode;
|
|
21
48
|
}): import("react").FunctionComponentElement<import("react").ProviderProps<Required<ClientTheme>>>;
|
|
22
49
|
export declare function useTheme(): Required<ClientTheme>;
|
|
23
|
-
export { DEFAULT_THEME };
|