@alexkroman1/aai-ui 0.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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/dist/_components/app.d.ts +5 -0
  3. package/dist/_components/app.js +23 -0
  4. package/dist/_components/button.d.ts +11 -0
  5. package/dist/_components/button.js +22 -0
  6. package/dist/_components/chat-view.d.ts +5 -0
  7. package/dist/_components/chat-view.js +37 -0
  8. package/dist/_components/controls.d.ts +4 -0
  9. package/dist/_components/controls.js +23 -0
  10. package/dist/_components/error-banner.d.ts +7 -0
  11. package/dist/_components/error-banner.js +13 -0
  12. package/dist/_components/message-bubble.d.ts +7 -0
  13. package/dist/_components/message-bubble.js +19 -0
  14. package/dist/_components/message-list.d.ts +4 -0
  15. package/dist/_components/message-list.js +59 -0
  16. package/dist/_components/sidebar-layout.d.ts +21 -0
  17. package/dist/_components/sidebar-layout.js +36 -0
  18. package/dist/_components/start-screen.d.ts +26 -0
  19. package/dist/_components/start-screen.js +51 -0
  20. package/dist/_components/state-indicator.d.ts +7 -0
  21. package/dist/_components/state-indicator.js +15 -0
  22. package/dist/_components/thinking-indicator.d.ts +5 -0
  23. package/dist/_components/thinking-indicator.js +22 -0
  24. package/dist/_components/tool-call-block.d.ts +7 -0
  25. package/dist/_components/tool-call-block.js +96 -0
  26. package/dist/_components/tool-icons.d.ts +17 -0
  27. package/dist/_components/tool-icons.js +128 -0
  28. package/dist/_components/transcript.d.ts +7 -0
  29. package/dist/_components/transcript.js +17 -0
  30. package/dist/_jsdom-setup.d.ts +0 -0
  31. package/dist/audio.d.ts +45 -0
  32. package/dist/audio.js +111 -0
  33. package/dist/components.d.ts +31 -0
  34. package/dist/components.js +17 -0
  35. package/dist/index.d.ts +20 -0
  36. package/dist/index.js +19 -0
  37. package/dist/mount-context.d.ts +33 -0
  38. package/dist/mount-context.js +15 -0
  39. package/dist/mount.d.ts +51 -0
  40. package/dist/mount.js +70 -0
  41. package/dist/session.d.ts +100 -0
  42. package/dist/session.js +359 -0
  43. package/dist/signals.d.ts +89 -0
  44. package/dist/signals.js +138 -0
  45. package/dist/tsdown.config.d.ts +2 -0
  46. package/dist/types.d.ts +75 -0
  47. package/dist/types.js +5 -0
  48. package/dist/worklets/capture-processor.d.ts +2 -0
  49. package/dist/worklets/capture-processor.js +57 -0
  50. package/dist/worklets/playback-processor.d.ts +2 -0
  51. package/dist/worklets/playback-processor.js +105 -0
  52. package/package.json +56 -0
  53. package/styles.css +74 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,5 @@
1
+ import type * as preact from "preact";
2
+ /** @public */
3
+ export declare function App({ className }: {
4
+ className?: string;
5
+ }): preact.JSX.Element;
@@ -0,0 +1,23 @@
1
+ import { useMountConfig } from "../mount-context.js";
2
+ import { ChatView } from "./chat-view.js";
3
+ import { StartScreen } from "./start-screen.js";
4
+ import { jsx } from "preact/jsx-runtime";
5
+ //#region _components/app.tsx
6
+ function AnsiLogo() {
7
+ return /* @__PURE__ */ jsx("pre", {
8
+ class: "font-aai-mono text-lg leading-[1.1] font-bold text-aai-primary m-0",
9
+ children: "▄▀█ ▄▀█ █\n█▀█ █▀█ █"
10
+ });
11
+ }
12
+ /** @public */
13
+ function App({ className }) {
14
+ const { title } = useMountConfig();
15
+ return /* @__PURE__ */ jsx(StartScreen, {
16
+ icon: title ? void 0 : /* @__PURE__ */ jsx(AnsiLogo, {}),
17
+ title,
18
+ className,
19
+ children: /* @__PURE__ */ jsx(ChatView, {})
20
+ });
21
+ }
22
+ //#endregion
23
+ export { App };
@@ -0,0 +1,11 @@
1
+ import type * as preact from "preact";
2
+ type ButtonVariant = "default" | "secondary" | "ghost";
3
+ type ButtonSize = "default" | "lg";
4
+ /** @public */
5
+ export declare function Button({ variant, size, className, children, ...rest }: {
6
+ variant?: ButtonVariant;
7
+ size?: ButtonSize;
8
+ className?: string;
9
+ children?: preact.ComponentChildren;
10
+ } & Omit<preact.JSX.HTMLAttributes<HTMLButtonElement>, "className">): preact.JSX.Element;
11
+ export {};
@@ -0,0 +1,22 @@
1
+ import clsx from "clsx";
2
+ import { jsx } from "preact/jsx-runtime";
3
+ //#region _components/button.tsx
4
+ /** @public */
5
+ function Button({ variant = "default", size = "default", className, children, ...rest }) {
6
+ return /* @__PURE__ */ jsx("button", {
7
+ type: "button",
8
+ style: size === "lg" ? {
9
+ display: "grid",
10
+ placeItems: "center",
11
+ appearance: "none",
12
+ margin: 0,
13
+ padding: "12px 32px",
14
+ lineHeight: 1
15
+ } : void 0,
16
+ class: clsx(size !== "lg" && "flex items-center justify-center appearance-none m-0 h-8 px-3 py-1.5 w-fit leading-none", "rounded-aai text-sm font-medium cursor-pointer border outline-none", variant === "secondary" && "bg-aai-surface-hover text-aai-text-secondary border-aai-border", variant === "ghost" && "bg-transparent text-aai-text-secondary border-aai-border", variant === "default" && "bg-aai-primary text-white border-transparent", className),
17
+ ...rest,
18
+ children
19
+ });
20
+ }
21
+ //#endregion
22
+ export { Button };
@@ -0,0 +1,5 @@
1
+ import type * as preact from "preact";
2
+ /** @public */
3
+ export declare function ChatView({ className }: {
4
+ className?: string;
5
+ }): preact.JSX.Element;
@@ -0,0 +1,37 @@
1
+ import { useMountConfig } from "../mount-context.js";
2
+ import { useSession } from "../signals.js";
3
+ import { Controls } from "./controls.js";
4
+ import { ErrorBanner } from "./error-banner.js";
5
+ import { MessageList } from "./message-list.js";
6
+ import { StateIndicator } from "./state-indicator.js";
7
+ import clsx from "clsx";
8
+ import { jsx, jsxs } from "preact/jsx-runtime";
9
+ //#region _components/chat-view.tsx
10
+ /** @public */
11
+ function ChatView({ className }) {
12
+ const { session } = useSession();
13
+ const { title } = useMountConfig();
14
+ return /* @__PURE__ */ jsxs("div", {
15
+ class: clsx("flex flex-col h-screen max-w-130 mx-auto bg-aai-bg text-aai-text font-aai text-sm", className),
16
+ children: [
17
+ /* @__PURE__ */ jsxs("div", {
18
+ class: "flex items-center gap-3 px-4 py-3 border-b border-aai-border shrink-0",
19
+ children: [title ? /* @__PURE__ */ jsx("span", {
20
+ class: "text-sm font-semibold text-aai-primary",
21
+ children: title
22
+ }) : /* @__PURE__ */ jsx("pre", {
23
+ class: "font-aai-mono text-[10px] leading-[1.1] font-bold text-aai-primary m-0",
24
+ children: "▄▀█ ▄▀█ █\n█▀█ █▀█ █"
25
+ }), /* @__PURE__ */ jsx("div", {
26
+ class: "ml-auto",
27
+ children: /* @__PURE__ */ jsx(StateIndicator, { state: session.state })
28
+ })]
29
+ }),
30
+ /* @__PURE__ */ jsx(ErrorBanner, { error: session.error }),
31
+ /* @__PURE__ */ jsx(MessageList, {}),
32
+ /* @__PURE__ */ jsx(Controls, {})
33
+ ]
34
+ });
35
+ }
36
+ //#endregion
37
+ export { ChatView };
@@ -0,0 +1,4 @@
1
+ /** @public */
2
+ export declare function Controls({ className }: {
3
+ className?: string;
4
+ }): import("preact").JSX.Element;
@@ -0,0 +1,23 @@
1
+ import { useSession } from "../signals.js";
2
+ import { Button } from "./button.js";
3
+ import clsx from "clsx";
4
+ import { jsx, jsxs } from "preact/jsx-runtime";
5
+ //#region _components/controls.tsx
6
+ /** @public */
7
+ function Controls({ className }) {
8
+ const { running, toggle, reset } = useSession();
9
+ return /* @__PURE__ */ jsxs("div", {
10
+ class: clsx("flex gap-2 px-4 py-3 border-t border-aai-border shrink-0", className),
11
+ children: [/* @__PURE__ */ jsx(Button, {
12
+ variant: "secondary",
13
+ onClick: toggle,
14
+ children: running.value ? "Stop" : "Resume"
15
+ }), /* @__PURE__ */ jsx(Button, {
16
+ variant: "ghost",
17
+ onClick: reset,
18
+ children: "New Conversation"
19
+ })]
20
+ });
21
+ }
22
+ //#endregion
23
+ export { Controls };
@@ -0,0 +1,7 @@
1
+ import type * as preact from "preact";
2
+ import type { Reactive, SessionError } from "../types.ts";
3
+ /** @public */
4
+ export declare function ErrorBanner({ error, className, }: {
5
+ error: Reactive<SessionError | null>;
6
+ className?: string;
7
+ }): preact.JSX.Element | null;
@@ -0,0 +1,13 @@
1
+ import clsx from "clsx";
2
+ import { jsx } from "preact/jsx-runtime";
3
+ //#region _components/error-banner.tsx
4
+ /** @public */
5
+ function ErrorBanner({ error, className }) {
6
+ if (!error.value) return null;
7
+ return /* @__PURE__ */ jsx("div", {
8
+ class: clsx("mx-4 mt-3 px-3 py-2 rounded-aai border border-aai-error/40 bg-aai-error/8 text-[13px] leading-[130%] text-aai-error", className),
9
+ children: error.value.message
10
+ });
11
+ }
12
+ //#endregion
13
+ export { ErrorBanner };
@@ -0,0 +1,7 @@
1
+ import type * as preact from "preact";
2
+ import type { Message } from "../types.ts";
3
+ /** @public */
4
+ export declare function MessageBubble({ message, className, }: {
5
+ message: Message;
6
+ className?: string;
7
+ }): preact.JSX.Element;
@@ -0,0 +1,19 @@
1
+ import clsx from "clsx";
2
+ import { jsx } from "preact/jsx-runtime";
3
+ //#region _components/message-bubble.tsx
4
+ /** @public */
5
+ function MessageBubble({ message, className }) {
6
+ if (message.role === "user") return /* @__PURE__ */ jsx("div", {
7
+ class: clsx("flex flex-col w-full items-end", className),
8
+ children: /* @__PURE__ */ jsx("div", {
9
+ class: "max-w-[min(82%,64ch)] bg-aai-surface-faint border border-aai-border px-3 py-2 rounded-aai whitespace-pre-wrap wrap-break-word text-sm font-normal leading-[150%] text-aai-text",
10
+ children: message.content
11
+ })
12
+ });
13
+ return /* @__PURE__ */ jsx("div", {
14
+ class: clsx("whitespace-pre-wrap wrap-break-word text-sm font-normal leading-[150%] text-aai-text", className),
15
+ children: message.content
16
+ });
17
+ }
18
+ //#endregion
19
+ export { MessageBubble };
@@ -0,0 +1,4 @@
1
+ /** @public */
2
+ export declare function MessageList({ className }: {
3
+ className?: string;
4
+ }): import("preact").JSX.Element;
@@ -0,0 +1,59 @@
1
+ import { useAutoScroll, useSession } from "../signals.js";
2
+ import { MessageBubble } from "./message-bubble.js";
3
+ import { ThinkingIndicator } from "./thinking-indicator.js";
4
+ import { ToolCallBlock } from "./tool-call-block.js";
5
+ import { Transcript } from "./transcript.js";
6
+ import clsx from "clsx";
7
+ import { computed } from "@preact/signals";
8
+ import { jsx, jsxs } from "preact/jsx-runtime";
9
+ //#region _components/message-list.tsx
10
+ /** @public */
11
+ function MessageList({ className }) {
12
+ const { session } = useSession();
13
+ const scrollRef = useAutoScroll();
14
+ const showThinking = computed(() => {
15
+ if (session.state.value !== "thinking") return false;
16
+ const last = session.toolCalls.value.at(-1);
17
+ if (last?.status === "pending") return false;
18
+ const lastMsg = session.messages.value.at(-1);
19
+ return !lastMsg || lastMsg.role === "user" || Boolean(last);
20
+ });
21
+ const messages = session.messages.value;
22
+ const toolCalls = session.toolCalls.value;
23
+ const items = [];
24
+ let tci = 0;
25
+ for (const [i, msg] of messages.entries()) {
26
+ items.push(/* @__PURE__ */ jsx(MessageBubble, { message: msg }, `msg-${i}`));
27
+ let tc = toolCalls[tci];
28
+ while (tc && tc.afterMessageIndex <= i) {
29
+ items.push(/* @__PURE__ */ jsx(ToolCallBlock, { toolCall: tc }, tc.toolCallId));
30
+ tci++;
31
+ tc = toolCalls[tci];
32
+ }
33
+ }
34
+ let tc = toolCalls[tci];
35
+ while (tc) {
36
+ items.push(/* @__PURE__ */ jsx(ToolCallBlock, { toolCall: tc }, tc.toolCallId));
37
+ tci++;
38
+ tc = toolCalls[tci];
39
+ }
40
+ return /* @__PURE__ */ jsx("div", {
41
+ role: "log",
42
+ class: clsx("flex-1 overflow-y-auto [scrollbar-width:none] bg-aai-surface", className),
43
+ children: /* @__PURE__ */ jsxs("div", {
44
+ class: "flex flex-col gap-4.5 p-4",
45
+ children: [
46
+ items,
47
+ session.agentUtterance.value && /* @__PURE__ */ jsx(MessageBubble, { message: {
48
+ role: "assistant",
49
+ content: session.agentUtterance.value
50
+ } }),
51
+ /* @__PURE__ */ jsx(Transcript, { userUtterance: session.userUtterance }),
52
+ showThinking.value && /* @__PURE__ */ jsx(ThinkingIndicator, {}),
53
+ /* @__PURE__ */ jsx("div", { ref: scrollRef })
54
+ ]
55
+ })
56
+ });
57
+ }
58
+ //#endregion
59
+ export { MessageList };
@@ -0,0 +1,21 @@
1
+ import type { ComponentChildren } from "preact";
2
+ /**
3
+ * A two-column layout with a fixed-width sidebar and a flexible main area.
4
+ * Commonly used to pair a custom sidebar (cart, dashboard) with `<ChatView />`.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * <SidebarLayout sidebar={<OrderPanel />}>
9
+ * <ChatView />
10
+ * </SidebarLayout>
11
+ * ```
12
+ *
13
+ * @public
14
+ */
15
+ export declare function SidebarLayout({ sidebar, children, width, side, className, }: {
16
+ sidebar: ComponentChildren;
17
+ children: ComponentChildren;
18
+ width?: string | undefined;
19
+ side?: "left" | "right" | undefined;
20
+ className?: string;
21
+ }): import("preact").JSX.Element;
@@ -0,0 +1,36 @@
1
+ import clsx from "clsx";
2
+ import { jsx, jsxs } from "preact/jsx-runtime";
3
+ //#region _components/sidebar-layout.tsx
4
+ /**
5
+ * A two-column layout with a fixed-width sidebar and a flexible main area.
6
+ * Commonly used to pair a custom sidebar (cart, dashboard) with `<ChatView />`.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * <SidebarLayout sidebar={<OrderPanel />}>
11
+ * <ChatView />
12
+ * </SidebarLayout>
13
+ * ```
14
+ *
15
+ * @public
16
+ */
17
+ function SidebarLayout({ sidebar, children, width = "20rem", side = "left", className }) {
18
+ const sidebarEl = /* @__PURE__ */ jsx("div", {
19
+ class: clsx("flex-shrink-0 flex flex-col overflow-y-auto", side === "left" ? "border-r border-aai-border" : "border-l border-aai-border"),
20
+ style: { width },
21
+ children: sidebar
22
+ });
23
+ return /* @__PURE__ */ jsxs("div", {
24
+ class: clsx("flex h-screen bg-aai-bg", className),
25
+ children: [
26
+ side === "left" && sidebarEl,
27
+ /* @__PURE__ */ jsx("div", {
28
+ class: "flex-1 min-w-0",
29
+ children
30
+ }),
31
+ side === "right" && sidebarEl
32
+ ]
33
+ });
34
+ }
35
+ //#endregion
36
+ export { SidebarLayout };
@@ -0,0 +1,26 @@
1
+ import type { ComponentChildren } from "preact";
2
+ /**
3
+ * A centered start screen with icon, title, subtitle, and a start button.
4
+ * Renders `children` (the main app) once the session has started.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * function MyAgent() {
9
+ * return (
10
+ * <StartScreen icon="🍕" title="Pizza Palace" subtitle="Voice-powered ordering">
11
+ * <ChatView />
12
+ * </StartScreen>
13
+ * );
14
+ * }
15
+ * ```
16
+ *
17
+ * @public
18
+ */
19
+ export declare function StartScreen({ children, icon, title, subtitle, buttonText, className, }: {
20
+ children: ComponentChildren;
21
+ icon?: ComponentChildren | undefined;
22
+ title?: string | undefined;
23
+ subtitle?: string | undefined;
24
+ buttonText?: string | undefined;
25
+ className?: string | undefined;
26
+ }): import("preact").JSX.Element;
@@ -0,0 +1,51 @@
1
+ import { useSession } from "../signals.js";
2
+ import { Button } from "./button.js";
3
+ import clsx from "clsx";
4
+ import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
5
+ //#region _components/start-screen.tsx
6
+ /**
7
+ * A centered start screen with icon, title, subtitle, and a start button.
8
+ * Renders `children` (the main app) once the session has started.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * function MyAgent() {
13
+ * return (
14
+ * <StartScreen icon="🍕" title="Pizza Palace" subtitle="Voice-powered ordering">
15
+ * <ChatView />
16
+ * </StartScreen>
17
+ * );
18
+ * }
19
+ * ```
20
+ *
21
+ * @public
22
+ */
23
+ function StartScreen({ children, icon, title, subtitle, buttonText = "Start", className }) {
24
+ const { started, start } = useSession();
25
+ if (started.value) return /* @__PURE__ */ jsx(Fragment, { children });
26
+ return /* @__PURE__ */ jsx("div", {
27
+ class: clsx("flex items-center justify-center h-screen bg-aai-bg font-aai", className),
28
+ children: /* @__PURE__ */ jsxs("div", {
29
+ class: "flex flex-col items-center gap-4 bg-aai-surface border border-aai-border rounded-lg px-12 py-10 max-w-sm text-center",
30
+ children: [
31
+ icon,
32
+ title && /* @__PURE__ */ jsx("h1", {
33
+ class: "font-semibold text-aai-primary m-0",
34
+ children: title
35
+ }),
36
+ subtitle && /* @__PURE__ */ jsx("p", {
37
+ class: "text-sm text-aai-text-muted m-0",
38
+ children: subtitle
39
+ }),
40
+ /* @__PURE__ */ jsx(Button, {
41
+ size: "lg",
42
+ className: "mt-2 w-full",
43
+ onClick: start,
44
+ children: buttonText
45
+ })
46
+ ]
47
+ })
48
+ });
49
+ }
50
+ //#endregion
51
+ export { StartScreen };
@@ -0,0 +1,7 @@
1
+ import type * as preact from "preact";
2
+ import type { AgentState, Reactive } from "../types.ts";
3
+ /** @public */
4
+ export declare function StateIndicator({ state, className, }: {
5
+ state: Reactive<AgentState>;
6
+ className?: string;
7
+ }): preact.JSX.Element;
@@ -0,0 +1,15 @@
1
+ import clsx from "clsx";
2
+ import { jsx, jsxs } from "preact/jsx-runtime";
3
+ //#region _components/state-indicator.tsx
4
+ /** @public */
5
+ function StateIndicator({ state, className }) {
6
+ return /* @__PURE__ */ jsxs("div", {
7
+ class: clsx("inline-flex items-center justify-center gap-1.5 text-[13px] font-medium leading-[130%] text-aai-text-muted capitalize", className),
8
+ children: [/* @__PURE__ */ jsx("div", {
9
+ "data-state": state.value,
10
+ class: "w-2 h-2 rounded-full data-[state=disconnected]:bg-aai-state-disconnected data-[state=connecting]:bg-aai-state-connecting data-[state=ready]:bg-aai-state-ready data-[state=listening]:bg-aai-state-listening data-[state=thinking]:bg-aai-state-thinking data-[state=speaking]:bg-aai-state-speaking data-[state=error]:bg-aai-state-error"
11
+ }), state.value]
12
+ });
13
+ }
14
+ //#endregion
15
+ export { StateIndicator };
@@ -0,0 +1,5 @@
1
+ import type * as preact from "preact";
2
+ /** @public */
3
+ export declare function ThinkingIndicator({ className }: {
4
+ className?: string;
5
+ }): preact.JSX.Element;
@@ -0,0 +1,22 @@
1
+ import clsx from "clsx";
2
+ import { jsx } from "preact/jsx-runtime";
3
+ //#region _components/thinking-indicator.tsx
4
+ /** @public */
5
+ function ThinkingIndicator({ className }) {
6
+ return /* @__PURE__ */ jsx("div", {
7
+ class: clsx("flex items-center gap-2 text-aai-text-dim text-sm font-medium min-h-5", className),
8
+ children: [
9
+ 0,
10
+ .16,
11
+ .32
12
+ ].map((delay) => /* @__PURE__ */ jsx("div", {
13
+ class: "w-1.5 h-1.5 rounded-full bg-aai-text-dim",
14
+ style: {
15
+ animation: "aai-bounce 1.4s infinite ease-in-out both",
16
+ animationDelay: `${delay}s`
17
+ }
18
+ }, delay))
19
+ });
20
+ }
21
+ //#endregion
22
+ export { ThinkingIndicator };
@@ -0,0 +1,7 @@
1
+ import type * as preact from "preact";
2
+ import type { ToolCallInfo } from "../types.ts";
3
+ /** @public */
4
+ export declare function ToolCallBlock({ toolCall, className, }: {
5
+ toolCall: ToolCallInfo;
6
+ className?: string;
7
+ }): preact.JSX.Element;
@@ -0,0 +1,96 @@
1
+ import { BoltIcon, ChatBubbleIcon, DownloadIcon, ExternalLinkIcon, SearchIcon, TerminalIcon } from "./tool-icons.js";
2
+ import { useMemo, useState } from "preact/hooks";
3
+ import clsx from "clsx";
4
+ import { jsx, jsxs } from "preact/jsx-runtime";
5
+ //#region _components/tool-call-block.tsx
6
+ const argField = (key) => (args) => String(args[key] ?? "");
7
+ const TOOL_CONFIG = {
8
+ web_search: {
9
+ Icon: SearchIcon,
10
+ title: "Web Search",
11
+ subtitle: argField("query")
12
+ },
13
+ visit_webpage: {
14
+ Icon: ExternalLinkIcon,
15
+ title: "Visit Page",
16
+ subtitle: argField("url")
17
+ },
18
+ run_code: {
19
+ Icon: TerminalIcon,
20
+ title: "Run Code",
21
+ subtitle: (args) => {
22
+ const firstLine = String(args.code ?? "").split("\n")[0] ?? "";
23
+ return firstLine.length > 80 ? `${firstLine.slice(0, 80)}...` : firstLine;
24
+ }
25
+ },
26
+ fetch_json: {
27
+ Icon: DownloadIcon,
28
+ title: "Fetch JSON",
29
+ subtitle: argField("url")
30
+ },
31
+ user_input: {
32
+ Icon: ChatBubbleIcon,
33
+ title: "Asking User",
34
+ subtitle: argField("question")
35
+ }
36
+ };
37
+ const DEFAULT_CONFIG = {
38
+ Icon: BoltIcon,
39
+ title: "",
40
+ subtitle: (args) => {
41
+ const summary = JSON.stringify(args);
42
+ return summary.length > 80 ? `${summary.slice(0, 80)}...` : summary;
43
+ }
44
+ };
45
+ function formatResult(result) {
46
+ try {
47
+ return JSON.stringify(JSON.parse(result), null, 2);
48
+ } catch {
49
+ return result;
50
+ }
51
+ }
52
+ /** @public */
53
+ function ToolCallBlock({ toolCall, className }) {
54
+ const [isOpen, setOpen] = useState(false);
55
+ const config = TOOL_CONFIG[toolCall.toolName] ?? DEFAULT_CONFIG;
56
+ const isPending = toolCall.status === "pending";
57
+ const title = config.title || toolCall.toolName;
58
+ const canExpand = !isPending && !!toolCall.result;
59
+ const formatted = useMemo(() => toolCall.result ? formatResult(toolCall.result) : "", [toolCall.result]);
60
+ return /* @__PURE__ */ jsxs("div", {
61
+ class: clsx("flex flex-col", className),
62
+ children: [/* @__PURE__ */ jsxs("button", {
63
+ type: "button",
64
+ "aria-expanded": canExpand ? isOpen : void 0,
65
+ disabled: isPending,
66
+ class: clsx("flex items-center gap-2 px-3 py-2 rounded-aai border border-aai-border bg-aai-surface-faint select-none text-left w-full", canExpand && "cursor-pointer"),
67
+ onClick: () => canExpand && setOpen(!isOpen),
68
+ children: [
69
+ /* @__PURE__ */ jsx(config.Icon, { class: "w-4 h-4 text-aai-text-dim shrink-0" }),
70
+ /* @__PURE__ */ jsx("span", {
71
+ class: clsx("text-sm font-medium text-aai-text", isPending && "tool-shimmer"),
72
+ children: title
73
+ }),
74
+ /* @__PURE__ */ jsx("span", {
75
+ class: "text-sm text-aai-text-dim truncate flex-1 min-w-0",
76
+ children: config.subtitle(toolCall.args)
77
+ }),
78
+ canExpand && /* @__PURE__ */ jsx("span", {
79
+ class: "text-xs text-aai-text-dim shrink-0",
80
+ children: isOpen ? "▾" : "▸"
81
+ })
82
+ ]
83
+ }), isOpen && /* @__PURE__ */ jsxs("div", {
84
+ class: "border-x border-b border-aai-border rounded-b-aai bg-aai-surface max-h-64 overflow-auto",
85
+ children: [toolCall.toolName === "run_code" && toolCall.args.code && /* @__PURE__ */ jsx("pre", {
86
+ class: "text-xs text-aai-text p-2 whitespace-pre-wrap border-b border-aai-border font-mono",
87
+ children: String(toolCall.args.code)
88
+ }), formatted && /* @__PURE__ */ jsx("pre", {
89
+ class: "text-xs text-aai-text-dim p-2 whitespace-pre-wrap",
90
+ children: formatted
91
+ })]
92
+ })]
93
+ });
94
+ }
95
+ //#endregion
96
+ export { ToolCallBlock };
@@ -0,0 +1,17 @@
1
+ import type * as preact from "preact";
2
+ type IconProps = {
3
+ class?: string;
4
+ };
5
+ /** Magnifying glass icon for web_search. */
6
+ export declare function SearchIcon(props: IconProps): preact.JSX.Element;
7
+ /** External link icon for visit_webpage. */
8
+ export declare function ExternalLinkIcon(props: IconProps): preact.JSX.Element;
9
+ /** Terminal icon for run_code. */
10
+ export declare function TerminalIcon(props: IconProps): preact.JSX.Element;
11
+ /** Download icon for fetch_json. */
12
+ export declare function DownloadIcon(props: IconProps): preact.JSX.Element;
13
+ /** Chat bubble icon for user_input. */
14
+ export declare function ChatBubbleIcon(props: IconProps): preact.JSX.Element;
15
+ /** Bolt/lightning icon for default/unknown tools. */
16
+ export declare function BoltIcon(props: IconProps): preact.JSX.Element;
17
+ export {};