@ocai/app-sdk-ui 0.2.0 → 0.3.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.
@@ -4,10 +4,17 @@ export interface AiActivity {
4
4
  elapsedSeconds: number;
5
5
  nowMs: number;
6
6
  }
7
- export declare function getAiActivityLabel({ kind, active, seed, elapsedSeconds, }: Pick<AiActivity, "seed" | "elapsedSeconds"> & {
7
+ export interface ActivityLabelParts {
8
+ verb: string;
9
+ elapsedSeconds: number;
10
+ }
11
+ export interface GetAiActivityLabelArgs extends Pick<AiActivity, "seed" | "elapsedSeconds"> {
8
12
  kind: AiActivityKind;
9
13
  active: boolean;
10
- }): string;
14
+ verb?: string;
15
+ }
16
+ export declare function getAiActivityLabelParts({ kind, active, seed, elapsedSeconds, verb, }: GetAiActivityLabelArgs): ActivityLabelParts;
17
+ export declare function getAiActivityLabel(args: GetAiActivityLabelArgs): string;
11
18
  export declare function traceElapsedSeconds(parts: ReadonlyArray<{
12
19
  startedAtMs?: number;
13
20
  completedAtMs?: number;
@@ -10,7 +10,8 @@ export type MessageTraceProps = {
10
10
  activity?: AiActivity;
11
11
  getToolLabel?: (type: string) => string | undefined;
12
12
  getToolIcon?: (type: string) => LucideIcon | undefined;
13
+ getActivityOverride?: (runningToolType: string | undefined) => string | undefined;
13
14
  defaultOpen?: boolean;
14
15
  className?: string;
15
16
  };
16
- export declare const MessageTrace: ({ parts, isLoading, messageId, activity, getToolLabel, getToolIcon, defaultOpen, className, }: MessageTraceProps) => ReactElement;
17
+ export declare const MessageTrace: import('react').MemoExoticComponent<({ parts, isLoading, messageId, activity, getToolLabel, getToolIcon, getActivityOverride, defaultOpen, className, }: MessageTraceProps) => ReactElement>;
@@ -1,5 +1,4 @@
1
1
  import { ComponentProps } from 'react';
2
2
  import { Streamdown } from 'streamdown';
3
- type ResponseProps = ComponentProps<typeof Streamdown>;
3
+ export type ResponseProps = ComponentProps<typeof Streamdown>;
4
4
  export declare function Response({ className, ...props }: ResponseProps): import("react").JSX.Element;
5
- export {};
@@ -6,8 +6,8 @@ export type SourceItem = {
6
6
  };
7
7
  export type SourcesProps = Omit<ComponentProps<typeof Collapsible>, "children"> & {
8
8
  sources: readonly SourceItem[];
9
- label?: (count: number) => string;
9
+ getLabel?: (count: number) => string;
10
10
  getFaviconUrl?: (url: string) => string | null;
11
11
  };
12
- export declare const Sources: ({ sources, label, getFaviconUrl, className, ...props }: SourcesProps) => ReactElement | null;
12
+ export declare const Sources: ({ sources, getLabel, getFaviconUrl, className, ...props }: SourcesProps) => ReactElement | null;
13
13
  export declare const citationClassName = "ocui-cite";
@@ -1,5 +1,6 @@
1
1
  import { ReactElement } from 'react';
2
2
  export declare const hasRenderableValue: (v: unknown) => boolean;
3
+ export declare const capText: (value: string, max?: number) => string;
3
4
  export declare const ToolValue: ({ value }: {
4
5
  value: unknown;
5
6
  }) => ReactElement | null;
@@ -0,0 +1,21 @@
1
+ import { MessageSegment, TracePart } from './segments';
2
+ import { SourceItem } from './sources';
3
+ import { AiActivityKind } from './activity-labels';
4
+ export type TurnPhase = "waiting" | "reasoning" | "searching" | "tooling" | "answering" | "settled";
5
+ export interface TurnView {
6
+ phase: TurnPhase;
7
+ segments: MessageSegment[];
8
+ trace: {
9
+ parts: TracePart[];
10
+ isLoading: boolean;
11
+ } | null;
12
+ sources: SourceItem[];
13
+ activityKind: AiActivityKind;
14
+ hasFinalAnswer: boolean;
15
+ }
16
+ export interface DeriveTurnViewOptions {
17
+ isPresentationalTool?: (type: string) => boolean;
18
+ isSearchTool?: (type: string) => boolean;
19
+ messageLoading?: boolean;
20
+ }
21
+ export declare function deriveTurnView(parts: readonly TracePart[], options?: DeriveTurnViewOptions): TurnView;
@@ -25,6 +25,7 @@ export type WebSearchUIPart = ToolUIPart<{
25
25
  webSearch: WebSearchUITool;
26
26
  }>;
27
27
  export declare function isWebSearchError(output: unknown): output is WebSearchErrorOutput;
28
+ export declare function stripCitationParens(text: string): string;
28
29
  export declare function extractWebSearchSources(parts: ReadonlyArray<{
29
30
  type?: string;
30
31
  state?: string;
package/dist/chat.css CHANGED
@@ -115,15 +115,9 @@
115
115
  .ocui-md.ocui-cite [data-streamdown="link"] {
116
116
  @apply inline cursor-pointer rounded-sm bg-muted px-1.5 py-0.5 align-baseline text-xs font-normal text-muted-foreground no-underline transition-colors hover:bg-accent hover:text-accent-foreground;
117
117
  }
118
- /* External-link hint: a small superscript arrow, added via content so it
119
- never disturbs inline flow the way an <svg> child would. */
120
- .ocui-md.ocui-cite [data-streamdown="link"]::after {
121
- content: "↗";
122
- @apply ml-0.5 align-super text-[0.65em] text-muted-foreground/70;
123
- }
124
118
 
125
119
  /* Activity-label shimmer: while the model streams, the trigger label
126
- ("Thinking…", "Shaping for 4 seconds") sweeps a full-contrast highlight
120
+ ("Thinking…", "Shaping for 4s") sweeps a full-contrast highlight
127
121
  left→right across the muted label text, instead of pulsing the whole word.
128
122
  Codex-style (a moving highlight band clipped to the glyphs). Base is the
129
123
  muted label color; the highlight is the foreground seed. Applied by
package/dist/index.d.ts CHANGED
@@ -1,12 +1,13 @@
1
- export { Response } from './chat/response';
1
+ export { Response, type ResponseProps } from './chat/response';
2
2
  export { MessageContent, type MessageContentProps, type MessageContentVariant, } from './chat/message';
3
3
  export { Reasoning, ReasoningContent, ReasoningTrigger, reasoningTextClassName, type ReasoningProps, type ReasoningTriggerProps, } from './chat/reasoning';
4
4
  export { Tool, ToolContent, ToolHeader, ToolInput, ToolOutput, type ToolContentProps, type ToolHeaderProps, type ToolInputProps, type ToolOutputProps, type ToolProps, } from './chat/tool';
5
5
  export { Action, Actions, type ActionProps, type ActionsProps, } from './chat/actions';
6
+ export { deriveTurnView, type TurnView, type TurnPhase, type DeriveTurnViewOptions, } from './chat/turn-view';
6
7
  export { MessageTrace, humanizeToolName, type MessageTraceProps, } from './chat/message-trace';
7
8
  export { buildSegments, traceIsLoading, type BuildSegmentsOptions, type MessageSegment, type TracePart, } from './chat/segments';
8
- export { getAiActivityLabel, labelSeedFromString, traceElapsedSeconds, type AiActivity, type AiActivityKind, } from './chat/activity-labels';
9
+ export { getAiActivityLabel, getAiActivityLabelParts, labelSeedFromString, traceElapsedSeconds, type AiActivity, type AiActivityKind, type ActivityLabelParts, type GetAiActivityLabelArgs, } from './chat/activity-labels';
9
10
  export { useActivityClock } from './chat/use-activity-clock';
10
11
  export { Sources, citationClassName, type SourceItem, type SourcesProps, } from './chat/sources';
11
- export { extractWebSearchSources, isWebSearchError, type WebSearchErrorOutput, type WebSearchInput, type WebSearchOutput, type WebSearchResult, type WebSearchSuccessOutput, type WebSearchUIPart, type WebSearchUITool, } from './chat/web-search';
12
+ export { extractWebSearchSources, isWebSearchError, stripCitationParens, type WebSearchErrorOutput, type WebSearchInput, type WebSearchOutput, type WebSearchResult, type WebSearchSuccessOutput, type WebSearchUIPart, type WebSearchUITool, } from './chat/web-search';
12
13
  export { WebSearchToggle, type WebSearchToggleProps, } from './chat/web-search-toggle';
package/dist/index.js CHANGED
@@ -5,20 +5,20 @@ import { clsx as n } from "clsx";
5
5
  import { twMerge as r } from "tailwind-merge";
6
6
  import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
7
7
  import { CheckIcon as s, ChevronDownIcon as c, CircleCheckIcon as l, ClockIcon as u, GlobeIcon as d, Loader2Icon as f, WrenchIcon as p, XIcon as m } from "lucide-react";
8
- import { createContext as h, isValidElement as g, memo as _, useContext as v, useEffect as y, useRef as ee, useState as b } from "react";
9
- import { Collapsible as x, Slot as te, Tooltip as S } from "radix-ui";
10
- import { cva as ne } from "class-variance-authority";
8
+ import { createContext as h, isValidElement as g, memo as _, useContext as v, useEffect as y, useId as ee, useRef as b, useState as x } from "react";
9
+ import { Collapsible as S, Slot as te, Tooltip as C } from "radix-ui";
10
+ import { cva as w } from "class-variance-authority";
11
11
  //#region src/lib/cn.ts
12
- function C(...e) {
12
+ function T(...e) {
13
13
  return r(n(e));
14
14
  }
15
15
  //#endregion
16
16
  //#region src/chat/response.tsx
17
- var re = e({ singleDollarTextMath: !0 });
18
- function ie({ className: e, ...n }) {
17
+ var E = e({ singleDollarTextMath: !0 });
18
+ function ne({ className: e, ...n }) {
19
19
  return /* @__PURE__ */ a(t, {
20
- className: C("ocui-md size-full text-base font-normal leading-6 [&>*:first-child]:mt-0 [&>*:last-child]:mb-0 [&_code]:whitespace-pre-wrap [&_code]:wrap-break-word [&_pre]:max-w-full [&_pre]:overflow-x-auto", e),
21
- plugins: { math: re },
20
+ className: T("ocui-md size-full text-base font-normal leading-6 [&>*:first-child]:mt-0 [&>*:last-child]:mb-0 [&_code]:whitespace-pre-wrap [&_code]:wrap-break-word [&_pre]:max-w-full [&_pre]:overflow-x-auto", e),
21
+ plugins: { math: E },
22
22
  controls: {
23
23
  table: !1,
24
24
  code: { download: !1 }
@@ -28,74 +28,73 @@ function ie({ className: e, ...n }) {
28
28
  }
29
29
  //#endregion
30
30
  //#region src/chat/message.tsx
31
- var ae = {
31
+ var D = {
32
32
  user: "w-fit wrap-break-word rounded-md bg-card px-3 py-1.5 text-primary",
33
33
  assistant: "bg-transparent px-0 py-0 text-left text-foreground"
34
- }, oe = ({ children: e, className: t, variant: n = "user", ...r }) => /* @__PURE__ */ a("div", {
35
- className: C("flex flex-col gap-2 overflow-hidden text-base font-normal leading-6", ae[n], t),
34
+ }, O = ({ children: e, className: t, variant: n = "user", ...r }) => /* @__PURE__ */ a("div", {
35
+ className: T("flex flex-col gap-2 overflow-hidden text-base font-normal leading-6", D[n], t),
36
36
  ...r,
37
37
  children: e
38
38
  });
39
39
  //#endregion
40
40
  //#region src/internal/collapsible.tsx
41
- function w({ ...e }) {
42
- return /* @__PURE__ */ a(x.Root, {
41
+ function k({ ...e }) {
42
+ return /* @__PURE__ */ a(S.Root, {
43
43
  "data-slot": "collapsible",
44
44
  ...e
45
45
  });
46
46
  }
47
- function T({ ...e }) {
48
- return /* @__PURE__ */ a(x.CollapsibleTrigger, {
47
+ function A({ ...e }) {
48
+ return /* @__PURE__ */ a(S.CollapsibleTrigger, {
49
49
  "data-slot": "collapsible-trigger",
50
50
  ...e
51
51
  });
52
52
  }
53
- function E({ ...e }) {
54
- return /* @__PURE__ */ a(x.CollapsibleContent, {
53
+ function j({ ...e }) {
54
+ return /* @__PURE__ */ a(S.CollapsibleContent, {
55
55
  "data-slot": "collapsible-content",
56
56
  ...e
57
57
  });
58
58
  }
59
59
  //#endregion
60
60
  //#region src/chat/reasoning.tsx
61
- function se({ prop: e, defaultProp: t, onChange: n }) {
62
- let [r, i] = b(t), a = e !== void 0;
61
+ function re({ prop: e, defaultProp: t, onChange: n }) {
62
+ let [r, i] = x(t), a = e !== void 0;
63
63
  return [a ? e : r, (e) => {
64
64
  a || i(e), n?.(e);
65
65
  }];
66
66
  }
67
- var D = h(null), ce = () => {
68
- let e = v(D);
67
+ var M = h(null), ie = () => {
68
+ let e = v(M);
69
69
  if (!e) throw Error("Reasoning components must be used within Reasoning");
70
70
  return e;
71
- }, le = 500, O = _(({ className: e, isStreaming: t = !1, duration: n = 0, open: r, defaultOpen: i = !0, onOpenChange: o, children: s, ...c }) => {
72
- let [l, u] = se({
71
+ }, ae = 500, N = _(({ className: e, isStreaming: t = !1, duration: n = 0, open: r, defaultOpen: i = !0, onOpenChange: o, children: s, ...c }) => {
72
+ let [l, u] = re({
73
73
  prop: r,
74
74
  defaultProp: i,
75
75
  onChange: o
76
- }), [d, f] = b(!1);
77
- return y(() => {
76
+ }), [d, f] = x(!1), p = b(u);
77
+ return p.current = u, y(() => {
78
78
  if (i && !t && l && !d) {
79
79
  let e = setTimeout(() => {
80
- u(!1), f(!0);
81
- }, le);
80
+ p.current(!1), f(!0);
81
+ }, ae);
82
82
  return () => clearTimeout(e);
83
83
  }
84
84
  }, [
85
85
  t,
86
86
  l,
87
87
  i,
88
- u,
89
88
  d
90
- ]), /* @__PURE__ */ a(D.Provider, {
89
+ ]), /* @__PURE__ */ a(M.Provider, {
91
90
  value: {
92
91
  isStreaming: t,
93
92
  isOpen: l,
94
93
  setIsOpen: u,
95
94
  duration: n
96
95
  },
97
- children: /* @__PURE__ */ a(w, {
98
- className: C("not-prose", e),
96
+ children: /* @__PURE__ */ a(k, {
97
+ className: T("not-prose", e),
99
98
  onOpenChange: u,
100
99
  open: l,
101
100
  ...c,
@@ -103,51 +102,51 @@ var D = h(null), ce = () => {
103
102
  })
104
103
  });
105
104
  });
106
- function ue({ variant: e, isStreaming: t, duration: n }) {
105
+ function oe({ variant: e, isStreaming: t, duration: n }) {
107
106
  let r = e === "tools";
108
107
  return t ? r ? "Working…" : "Thinking…" : n > 0 ? r ? `Worked for ${n}s` : `Thought for ${n}s` : r ? "Worked" : "Thought";
109
108
  }
110
- var k = _(({ className: e, children: t, variant: n = "reasoning", label: r, ...s }) => {
111
- let { isStreaming: l, isOpen: u, duration: d } = ce(), f = r ?? ue({
109
+ var P = _(({ className: e, children: t, variant: n = "reasoning", label: r, ...s }) => {
110
+ let { isStreaming: l, isOpen: u, duration: d } = ie(), f = r ?? oe({
112
111
  variant: n,
113
112
  isStreaming: l,
114
113
  duration: d
115
114
  });
116
- return /* @__PURE__ */ a(T, {
117
- className: C("flex items-center gap-1 rounded-md px-1.5 py-0.5 text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground", e),
115
+ return /* @__PURE__ */ a(A, {
116
+ className: T("flex items-center gap-1 rounded-md px-1.5 py-0.5 text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground", e),
118
117
  ...s,
119
118
  children: t ?? /* @__PURE__ */ o(i, { children: [
120
119
  n === "tools" && /* @__PURE__ */ a(p, { className: "size-3" }),
121
120
  /* @__PURE__ */ a("span", {
122
- className: C(l && "ocui-shimmer"),
121
+ className: T(l && "ocui-shimmer"),
123
122
  children: f
124
123
  }),
125
- /* @__PURE__ */ a(c, { className: C("size-2.5 transition-transform", u ? "rotate-180" : "rotate-0") })
124
+ /* @__PURE__ */ a(c, { className: T("size-2.5 transition-transform", u ? "rotate-180" : "rotate-0") })
126
125
  ] })
127
126
  });
128
- }), A = "grid gap-1 text-sm text-muted-foreground/80 **:text-sm [&_code]:bg-transparent [&_code]:p-0 [&_code]:font-semibold [&_code]:text-foreground [&_li]:my-0 [&_ol]:my-1 [&_p]:my-0 [&_ul]:my-1", j = _(({ className: e, ...t }) => /* @__PURE__ */ a(E, {
129
- className: C(A, "mt-1.5", e),
127
+ }), F = "grid gap-1 text-sm text-muted-foreground/80 **:text-sm [&_code]:bg-transparent [&_code]:p-0 [&_code]:font-semibold [&_code]:text-foreground [&_li]:my-0 [&_ol]:my-1 [&_p]:my-0 [&_ul]:my-1", I = _(({ className: e, ...t }) => /* @__PURE__ */ a(j, {
128
+ className: T(F, "mt-1.5", e),
130
129
  ...t
131
130
  }));
132
- O.displayName = "Reasoning", k.displayName = "ReasoningTrigger", j.displayName = "ReasoningContent";
131
+ N.displayName = "Reasoning", P.displayName = "ReasoningTrigger", I.displayName = "ReasoningContent";
133
132
  //#endregion
134
133
  //#region src/chat/tool-value.tsx
135
- var de = 8, M = 240, N = 2e4, P = (e) => e === null || typeof e != "object" && typeof e != "function", F = (e) => {
134
+ var se = 8, L = 240, ce = 2e4, R = (e) => e === null || typeof e != "object" && typeof e != "function", z = (e) => {
136
135
  if (typeof e != "object" || !e) return !1;
137
136
  let t = Object.getPrototypeOf(e);
138
137
  return t === Object.prototype || t === null;
139
- }, I = (e) => Array.isArray(e) && e.every(P), fe = (e) => {
140
- if (!F(e)) return !1;
138
+ }, B = (e) => Array.isArray(e) && e.every(R), le = (e) => {
139
+ if (!z(e)) return !1;
141
140
  let t = Object.keys(e);
142
- return t.length === 0 || t.length > de ? !1 : t.every((t) => P(e[t]) || I(e[t]));
143
- }, L = (e) => g(e) ? !0 : e == null ? !1 : typeof e == "string" ? e.trim().length > 0 : P(e) ? !0 : Array.isArray(e) ? e.length > 0 : F(e) ? Object.keys(e).length > 0 : !0, pe = (e) => {
141
+ return t.length === 0 || t.length > se ? !1 : t.every((t) => R(e[t]) || B(e[t]));
142
+ }, V = (e) => g(e) ? !0 : e == null ? !1 : typeof e == "string" ? e.trim().length > 0 : R(e) ? !0 : Array.isArray(e) ? e.length > 0 : z(e) ? Object.keys(e).length > 0 : !0, H = (e, t = ce) => e.length > t ? `${e.slice(0, t)}\n… (truncated)` : e, ue = (e) => {
144
143
  try {
145
144
  let t = JSON.stringify(e, null, 2);
146
- return t === void 0 ? String(e) : t.length > N ? `${t.slice(0, N)}\n… (truncated)` : t;
145
+ return t === void 0 ? String(e) : H(t);
147
146
  } catch {
148
147
  return String(e);
149
148
  }
150
- }, R = ({ value: e }) => {
149
+ }, U = ({ value: e }) => {
151
150
  if (e == null) return /* @__PURE__ */ a("span", {
152
151
  className: "font-mono text-muted-foreground",
153
152
  children: "null"
@@ -160,13 +159,13 @@ var de = 8, M = 240, N = 2e4, P = (e) => e === null || typeof e != "object" && t
160
159
  className: "text-muted-foreground italic",
161
160
  children: "empty"
162
161
  });
163
- let t = e.length > M;
162
+ let t = e.length > L;
164
163
  return /* @__PURE__ */ a("span", {
165
164
  className: "text-foreground",
166
165
  title: t ? e : void 0,
167
- children: t ? `${e.slice(0, M)}…` : e
166
+ children: t ? `${e.slice(0, L)}…` : e
168
167
  });
169
- }, z = ({ value: e }) => e.length === 0 ? /* @__PURE__ */ a("span", {
168
+ }, W = ({ value: e }) => e.length === 0 ? /* @__PURE__ */ a("span", {
170
169
  className: "text-muted-foreground italic",
171
170
  children: "none"
172
171
  }) : /* @__PURE__ */ a("span", {
@@ -174,8 +173,8 @@ var de = 8, M = 240, N = 2e4, P = (e) => e === null || typeof e != "object" && t
174
173
  children: e.map((e, t) => /* @__PURE__ */ o("span", { children: [t > 0 && /* @__PURE__ */ a("span", {
175
174
  className: "px-1 text-muted-foreground/70",
176
175
  children: "·"
177
- }), /* @__PURE__ */ a(R, { value: e })] }, t))
178
- }), me = ({ data: e }) => /* @__PURE__ */ a("ul", {
176
+ }), /* @__PURE__ */ a(U, { value: e })] }, t))
177
+ }), de = ({ data: e }) => /* @__PURE__ */ a("ul", {
179
178
  className: "ocui-tool-kv grid min-w-0 grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1 text-xs",
180
179
  children: Object.entries(e).map(([e, t]) => /* @__PURE__ */ o("li", {
181
180
  className: "contents",
@@ -184,25 +183,25 @@ var de = 8, M = 240, N = 2e4, P = (e) => e === null || typeof e != "object" && t
184
183
  children: e
185
184
  }), /* @__PURE__ */ a("span", {
186
185
  className: "min-w-0 break-words whitespace-pre-wrap",
187
- children: I(t) ? /* @__PURE__ */ a(z, { value: t }) : /* @__PURE__ */ a(R, { value: t })
186
+ children: B(t) ? /* @__PURE__ */ a(W, { value: t }) : /* @__PURE__ */ a(U, { value: t })
188
187
  })]
189
188
  }, e))
190
- }), he = ({ value: e }) => /* @__PURE__ */ a("pre", {
189
+ }), fe = ({ value: e }) => /* @__PURE__ */ a("pre", {
191
190
  className: "ocui-tool-json overflow-x-auto rounded-md bg-muted/40 px-2.5 py-2 font-mono text-xs text-muted-foreground",
192
- children: pe(e)
193
- }), B = ({ value: e }) => g(e) ? /* @__PURE__ */ a(i, { children: e }) : e == null ? null : typeof e == "string" ? e.trim() === "" ? null : /* @__PURE__ */ a("div", {
191
+ children: ue(e)
192
+ }), G = ({ value: e }) => g(e) ? /* @__PURE__ */ a(i, { children: e }) : e == null ? null : typeof e == "string" ? e.trim() === "" ? null : /* @__PURE__ */ a("div", {
194
193
  className: "whitespace-pre-wrap break-words font-mono text-xs leading-relaxed text-muted-foreground",
195
- children: e
196
- }) : P(e) ? /* @__PURE__ */ a("div", {
194
+ children: H(e)
195
+ }) : R(e) ? /* @__PURE__ */ a("div", {
197
196
  className: "font-mono text-xs text-foreground",
198
197
  children: String(e)
199
- }) : fe(e) ? /* @__PURE__ */ a(me, { data: e }) : I(e) ? /* @__PURE__ */ a("div", {
198
+ }) : le(e) ? /* @__PURE__ */ a(de, { data: e }) : B(e) ? /* @__PURE__ */ a("div", {
200
199
  className: "text-xs",
201
- children: /* @__PURE__ */ a(z, { value: e })
202
- }) : /* @__PURE__ */ a(he, { value: e }), ge = ({ className: e, ...t }) => /* @__PURE__ */ a(w, {
203
- className: C("not-prose w-full rounded-md border border-border", e),
200
+ children: /* @__PURE__ */ a(W, { value: e })
201
+ }) : /* @__PURE__ */ a(fe, { value: e }), pe = ({ className: e, ...t }) => /* @__PURE__ */ a(k, {
202
+ className: T("not-prose w-full rounded-md border border-border", e),
204
203
  ...t
205
- }), _e = {
204
+ }), me = {
206
205
  "input-streaming": "Pending",
207
206
  "input-available": "Running",
208
207
  "approval-requested": "Pending",
@@ -210,19 +209,19 @@ var de = 8, M = 240, N = 2e4, P = (e) => e === null || typeof e != "object" && t
210
209
  "output-available": "Completed",
211
210
  "output-error": "Error",
212
211
  "output-denied": "Denied"
213
- }, ve = {
214
- "input-streaming": /* @__PURE__ */ a(f, { className: "size-3.5 animate-spin" }),
215
- "input-available": /* @__PURE__ */ a(f, { className: "size-3.5 animate-spin" }),
212
+ }, he = {
213
+ "input-streaming": /* @__PURE__ */ a(f, { className: "size-3.5 animate-spin motion-reduce:animate-none" }),
214
+ "input-available": /* @__PURE__ */ a(f, { className: "size-3.5 animate-spin motion-reduce:animate-none" }),
216
215
  "approval-requested": /* @__PURE__ */ a(u, { className: "size-3.5" }),
217
216
  "approval-responded": /* @__PURE__ */ a(s, { className: "size-3.5" }),
218
217
  "output-available": /* @__PURE__ */ a(s, { className: "size-3.5" }),
219
218
  "output-error": /* @__PURE__ */ a(m, { className: "size-3.5 text-destructive" }),
220
219
  "output-denied": /* @__PURE__ */ a(m, { className: "size-3.5 text-destructive" })
221
- }, ye = ({ state: e }) => /* @__PURE__ */ o("span", {
220
+ }, ge = ({ state: e }) => /* @__PURE__ */ o("span", {
222
221
  className: "flex shrink-0 items-center gap-1 text-xs text-muted-foreground",
223
- children: [ve[e], _e[e]]
224
- }), be = ({ className: e, type: t, state: n, title: r, ...i }) => /* @__PURE__ */ o(T, {
225
- className: C("group flex w-full min-w-0 items-center justify-between gap-2 rounded-md p-2.5 transition-colors hover:bg-muted/50", e),
222
+ children: [he[e], me[e]]
223
+ }), _e = ({ className: e, type: t, state: n, title: r, ...i }) => /* @__PURE__ */ o(A, {
224
+ className: T("group flex w-full min-w-0 items-center justify-between gap-2 rounded-md p-2.5 transition-colors hover:bg-muted/50", e),
226
225
  ...i,
227
226
  children: [/* @__PURE__ */ o("div", {
228
227
  className: "flex min-w-0 flex-1 items-center gap-2",
@@ -232,20 +231,20 @@ var de = 8, M = 240, N = 2e4, P = (e) => e === null || typeof e != "object" && t
232
231
  })]
233
232
  }), /* @__PURE__ */ o("div", {
234
233
  className: "flex shrink-0 items-center gap-2",
235
- children: [/* @__PURE__ */ a(ye, { state: n }), /* @__PURE__ */ a(c, { className: "size-3.5 text-muted-foreground transition-transform group-data-[state=open]:rotate-180" })]
234
+ children: [/* @__PURE__ */ a(ge, { state: n }), /* @__PURE__ */ a(c, { className: "size-3.5 text-muted-foreground transition-transform group-data-[state=open]:rotate-180" })]
236
235
  })]
237
- }), xe = ({ className: e, ...t }) => /* @__PURE__ */ a(E, {
238
- className: C("data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-hidden data-[state=closed]:animate-out data-[state=open]:animate-in", e),
236
+ }), ve = ({ className: e, ...t }) => /* @__PURE__ */ a(j, {
237
+ className: T("data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-hidden data-[state=closed]:animate-out data-[state=open]:animate-in", e),
239
238
  ...t
240
- }), Se = ({ className: e, input: t, ...n }) => L(t) ? /* @__PURE__ */ o("div", {
241
- className: C("space-y-1.5 overflow-hidden px-2.5 pb-2.5", e),
239
+ }), ye = ({ className: e, input: t, ...n }) => V(t) ? /* @__PURE__ */ o("div", {
240
+ className: T("space-y-1.5 overflow-hidden px-2.5 pb-2.5", e),
242
241
  ...n,
243
242
  children: [/* @__PURE__ */ a("h4", {
244
243
  className: "text-xs font-medium tracking-wide text-muted-foreground uppercase",
245
244
  children: "Parameters"
246
- }), /* @__PURE__ */ a(B, { value: t })]
247
- }) : null, Ce = ({ className: e, output: t, errorText: n, ...r }) => L(t) || n ? /* @__PURE__ */ o("div", {
248
- className: C("space-y-1.5 px-2.5 pb-2.5", e),
245
+ }), /* @__PURE__ */ a(G, { value: t })]
246
+ }) : null, be = ({ className: e, output: t, errorText: n, ...r }) => V(t) || n ? /* @__PURE__ */ o("div", {
247
+ className: T("space-y-1.5 px-2.5 pb-2.5", e),
249
248
  ...r,
250
249
  children: [/* @__PURE__ */ a("h4", {
251
250
  className: "text-xs font-medium tracking-wide text-muted-foreground uppercase",
@@ -255,9 +254,9 @@ var de = 8, M = 240, N = 2e4, P = (e) => e === null || typeof e != "object" && t
255
254
  children: n
256
255
  }) : /* @__PURE__ */ a("div", {
257
256
  className: "text-xs [&_table]:w-full",
258
- children: /* @__PURE__ */ a(B, { value: t })
257
+ children: /* @__PURE__ */ a(G, { value: t })
259
258
  })]
260
- }) : null, we = ne("group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
259
+ }) : null, xe = w("group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
261
260
  variants: {
262
261
  variant: {
263
262
  default: "bg-primary text-primary-foreground hover:bg-primary/90",
@@ -283,12 +282,12 @@ var de = 8, M = 240, N = 2e4, P = (e) => e === null || typeof e != "object" && t
283
282
  size: "default"
284
283
  }
285
284
  });
286
- function V({ className: e, variant: t = "default", size: n = "default", asChild: r = !1, ...i }) {
285
+ function K({ className: e, variant: t = "default", size: n = "default", asChild: r = !1, ...i }) {
287
286
  return /* @__PURE__ */ a(r ? te.Root : "button", {
288
287
  "data-slot": "button",
289
288
  "data-variant": t,
290
289
  "data-size": n,
291
- className: C(we({
290
+ className: T(xe({
292
291
  variant: t,
293
292
  size: n,
294
293
  className: e
@@ -298,43 +297,43 @@ function V({ className: e, variant: t = "default", size: n = "default", asChild:
298
297
  }
299
298
  //#endregion
300
299
  //#region src/internal/tooltip.tsx
301
- function H({ delayDuration: e = 0, ...t }) {
302
- return /* @__PURE__ */ a(S.Provider, {
300
+ function q({ delayDuration: e = 0, ...t }) {
301
+ return /* @__PURE__ */ a(C.Provider, {
303
302
  "data-slot": "tooltip-provider",
304
303
  delayDuration: e,
305
304
  ...t
306
305
  });
307
306
  }
308
- function U({ ...e }) {
309
- return /* @__PURE__ */ a(S.Root, {
307
+ function J({ ...e }) {
308
+ return /* @__PURE__ */ a(C.Root, {
310
309
  "data-slot": "tooltip",
311
310
  ...e
312
311
  });
313
312
  }
314
- function W({ ...e }) {
315
- return /* @__PURE__ */ a(S.Trigger, {
313
+ function Y({ ...e }) {
314
+ return /* @__PURE__ */ a(C.Trigger, {
316
315
  "data-slot": "tooltip-trigger",
317
316
  ...e
318
317
  });
319
318
  }
320
- function G({ className: e, sideOffset: t = 0, children: n, ...r }) {
321
- return /* @__PURE__ */ a(S.Portal, { children: /* @__PURE__ */ o(S.Content, {
319
+ function X({ className: e, sideOffset: t = 0, children: n, ...r }) {
320
+ return /* @__PURE__ */ a(C.Portal, { children: /* @__PURE__ */ o(C.Content, {
322
321
  "data-slot": "tooltip-content",
323
322
  sideOffset: t,
324
- className: C("z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md border border-border bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-[state=instant-open]:animate-in data-[state=instant-open]:fade-in-0 data-[state=instant-open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95", e),
323
+ className: T("z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md border border-border bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-[state=instant-open]:animate-in data-[state=instant-open]:fade-in-0 data-[state=instant-open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95", e),
325
324
  ...r,
326
- children: [n, /* @__PURE__ */ a(S.Arrow, { className: "z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] border-r border-b border-border bg-popover fill-popover" })]
325
+ children: [n, /* @__PURE__ */ a(C.Arrow, { className: "z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] border-r border-b border-border bg-popover fill-popover" })]
327
326
  }) });
328
327
  }
329
328
  //#endregion
330
329
  //#region src/chat/actions.tsx
331
- var Te = ({ className: e, children: t, ...n }) => /* @__PURE__ */ a("div", {
332
- className: C("flex items-center", e),
330
+ var Se = ({ className: e, children: t, ...n }) => /* @__PURE__ */ a("div", {
331
+ className: T("-ml-1.5 flex items-center", e),
333
332
  ...n,
334
333
  children: t
335
- }), Ee = ({ tooltip: e, children: t, label: n, className: r, variant: i = "ghost", size: s = "icon", ...c }) => {
336
- let l = /* @__PURE__ */ o(V, {
337
- className: C("relative", r),
334
+ }), Ce = ({ tooltip: e, children: t, label: n, className: r, variant: i = "ghost", size: s = "icon", ...c }) => {
335
+ let l = /* @__PURE__ */ o(K, {
336
+ className: T("relative", r),
338
337
  size: s,
339
338
  type: "button",
340
339
  variant: i,
@@ -344,14 +343,113 @@ var Te = ({ className: e, children: t, ...n }) => /* @__PURE__ */ a("div", {
344
343
  children: n || e
345
344
  })]
346
345
  });
347
- return e ? /* @__PURE__ */ a(H, {
346
+ return e ? /* @__PURE__ */ a(q, {
348
347
  delayDuration: 500,
349
- children: /* @__PURE__ */ o(U, { children: [/* @__PURE__ */ a(W, {
348
+ children: /* @__PURE__ */ o(J, { children: [/* @__PURE__ */ a(Y, {
350
349
  asChild: !0,
351
350
  children: l
352
- }), /* @__PURE__ */ a(G, { children: /* @__PURE__ */ a("p", { children: e }) })] })
351
+ }), /* @__PURE__ */ a(X, { children: /* @__PURE__ */ a("p", { children: e }) })] })
353
352
  }) : l;
354
- }, K = [
353
+ };
354
+ //#endregion
355
+ //#region src/chat/segments.ts
356
+ function we(e, t) {
357
+ return e.startsWith("tool-") && !t(e);
358
+ }
359
+ function Te(e, t) {
360
+ let n = t?.isPresentationalTool ?? (() => !1), r = -1;
361
+ e.forEach((e, t) => {
362
+ (e.type === "reasoning" || we(e.type, n)) && (r = t);
363
+ });
364
+ let i = [], a = null, o = null, s = (e) => {
365
+ o = null, a || (a = {
366
+ kind: "trace",
367
+ parts: []
368
+ }, i.push(a)), a.parts.push(e);
369
+ };
370
+ return e.forEach((e, t) => {
371
+ if (!(e.type === "step-start" || e.type === "step-finish")) if (e.type === "reasoning") {
372
+ let t = String(e.text ?? ""), n = e.state === "streaming";
373
+ if (!(t.trim() || n)) return;
374
+ s(e);
375
+ } else we(e.type, n) ? s(e) : e.type === "text" && t <= r ? String(e.text ?? "").trim() && s(e) : e.type === "file" ? (a = null, o || (o = {
376
+ kind: "images",
377
+ parts: [],
378
+ index: t
379
+ }, i.push(o)), o.parts.push(e)) : (a = null, o = null, i.push({
380
+ kind: "part",
381
+ part: e,
382
+ index: t
383
+ }));
384
+ }), i;
385
+ }
386
+ function Ee(e, t) {
387
+ return e.some((e) => {
388
+ let n = e.state;
389
+ return typeof n == "string" ? e.type === "reasoning" || e.type === "text" ? n === "streaming" : n === "input-streaming" || n === "input-available" : t;
390
+ });
391
+ }
392
+ //#endregion
393
+ //#region src/chat/web-search.ts
394
+ function De(e) {
395
+ return typeof e == "object" && !!e && "error" in e && typeof e.error == "string";
396
+ }
397
+ var Oe = /\(\s*(\[[^\]\n]*\]\((?:[^()\n]|\([^()\n]*\))*\))\s*\)/g;
398
+ function ke(e) {
399
+ return e.replace(Oe, "$1");
400
+ }
401
+ var Ae = "tool-webSearch";
402
+ function je(e) {
403
+ try {
404
+ let t = new URL(e);
405
+ return `${t.hostname.toLowerCase()}${t.pathname.replace(/\/$/, "")}${t.search}`;
406
+ } catch {
407
+ return e.replace(/#.*$/, "").replace(/\/$/, "");
408
+ }
409
+ }
410
+ function Me(e, t) {
411
+ let n = t?.toolType ?? Ae, r = /* @__PURE__ */ new Set(), i = [];
412
+ for (let t of e) {
413
+ if (t.type !== n || t.state !== "output-available" || De(t.output)) continue;
414
+ let e = t.output?.results;
415
+ if (Array.isArray(e)) for (let t of e) {
416
+ if (typeof t?.url != "string") continue;
417
+ let e = je(t.url);
418
+ r.has(e) || (r.add(e), i.push({
419
+ url: t.url,
420
+ title: typeof t.title == "string" ? t.title : void 0
421
+ }));
422
+ }
423
+ }
424
+ return i;
425
+ }
426
+ //#endregion
427
+ //#region src/chat/turn-view.ts
428
+ function Ne(e, t) {
429
+ let n = t?.isSearchTool ?? (() => !1), r = t?.messageLoading, i = Te(e, { isPresentationalTool: t?.isPresentationalTool }), a = i.flatMap((e) => e.kind === "trace" ? e.parts : []), o = i.filter((e) => e.kind === "part" && e.part.type === "text"), s = o.some((e) => String(e.part.text ?? "").trim() !== ""), c = s && o.some((e) => e.part.state === "streaming"), l = a.some((e) => e.type === "reasoning" && String(e.text ?? "").trim() !== ""), u = a.some((e) => e.type.startsWith("tool-")), d = i.length > 0, f = a.some((e) => e.type === "reasoning" && e.state === "streaming"), p;
430
+ for (let e of a) {
431
+ if (!e.type.startsWith("tool-")) continue;
432
+ let t = String(e.state ?? "");
433
+ t === "output-available" || t === "output-error" || t === "output-denied" || (p = e.type);
434
+ }
435
+ let m = a.length > 0 && Ee(a, r ?? !1), h = (r ?? !0) && !s, g = a.length > 0 ? {
436
+ parts: a,
437
+ isLoading: h
438
+ } : null, _;
439
+ _ = d ? h ? m && f ? "reasoning" : m && p && n(p) ? "searching" : m && p ? "tooling" : l ? "reasoning" : u ? "tooling" : "reasoning" : c ? "answering" : "settled" : h ? "waiting" : "settled";
440
+ let v = d ? l ? "reasoning" : "tools" : "waiting";
441
+ return {
442
+ phase: _,
443
+ segments: i,
444
+ trace: g,
445
+ sources: Me(e),
446
+ activityKind: v,
447
+ hasFinalAnswer: s
448
+ };
449
+ }
450
+ //#endregion
451
+ //#region src/chat/activity-labels.ts
452
+ var Pe = [
355
453
  {
356
454
  active: "Thinking",
357
455
  done: "Thought"
@@ -392,94 +490,115 @@ var Te = ({ className: e, children: t, ...n }) => /* @__PURE__ */ a("div", {
392
490
  active: "Working",
393
491
  done: "Done"
394
492
  }
395
- ], De = {
396
- waiting: K,
397
- reasoning: K,
493
+ ], Fe = {
494
+ waiting: Pe,
495
+ reasoning: Pe,
398
496
  tools: [{
399
497
  active: "Working",
400
498
  done: "Worked"
401
499
  }]
402
- }, Oe = 5;
403
- function q({ kind: e, active: t, seed: n, elapsedSeconds: r }) {
404
- let i = De[e], a = Math.floor(Math.max(0, r) / Oe), o = i[Math.abs(n + a) % i.length], s = t ? o.active : o.done;
405
- return r <= 0 ? s : `${s} for ${r}s`;
500
+ };
501
+ function Z({ kind: e, active: t, seed: n, elapsedSeconds: r, verb: i }) {
502
+ let a;
503
+ if (i !== void 0) a = i;
504
+ else {
505
+ let r = Fe[e], i = r[Math.abs(n) % r.length];
506
+ a = t ? i.active : i.done;
507
+ }
508
+ return {
509
+ verb: a,
510
+ elapsedSeconds: r > 0 ? r : 0
511
+ };
512
+ }
513
+ function Q(e) {
514
+ let { verb: t, elapsedSeconds: n } = Z(e);
515
+ return n <= 0 ? t : `${t} for ${n}s`;
406
516
  }
407
- function J(e, t) {
517
+ function Ie(e, t) {
408
518
  let n = Infinity, r = -Infinity, i = !1;
409
- for (let t of e) typeof t.startedAtMs == "number" && (n = Math.min(n, t.startedAtMs), typeof t.completedAtMs == "number" ? r = Math.max(r, t.completedAtMs) : i = !0);
519
+ for (let t of e) Number.isFinite(t.startedAtMs) && (n = Math.min(n, t.startedAtMs), Number.isFinite(t.completedAtMs) ? r = Math.max(r, t.completedAtMs) : i = !0);
410
520
  if (n === Infinity) return 0;
411
- let a = i ? t ?? r : r;
521
+ let a = i && Number.isFinite(t) ? t : r;
412
522
  return a === -Infinity ? 0 : Math.max(0, Math.floor((a - n) / 1e3));
413
523
  }
414
- function Y(e) {
524
+ function Le(e) {
415
525
  let t = 0;
416
526
  for (let n = 0; n < e.length; n += 1) t = t * 31 + e.charCodeAt(n) | 0;
417
527
  return t;
418
528
  }
419
529
  //#endregion
420
530
  //#region src/chat/message-trace.tsx
421
- function X(e) {
531
+ function Re(e) {
422
532
  let t = e.replace(/^tool-/, "").replace(/[_-]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim();
423
533
  return t.charAt(0).toUpperCase() + t.slice(1);
424
534
  }
425
- var ke = ({ parts: e, isLoading: t, messageId: n, activity: r, getToolLabel: i, getToolIcon: s, defaultOpen: c = !1, className: l }) => {
426
- let u = e.filter((e) => e.type === "reasoning").map((e) => String(e.text ?? "")).filter((e) => e.trim()).join("\n\n"), d = u.trim().length > 0, f = [], p = !1;
427
- for (let t of e) t.type.startsWith("tool-") ? (p = !0, f.push({
428
- kind: "tool",
429
- label: i?.(t.type) ?? X(t.type),
430
- state: String(t.state ?? ""),
431
- toolType: t.type
432
- })) : t.type === "text" && String(t.text ?? "").trim() && f.push({
535
+ var ze = _(({ parts: e, isLoading: t, messageId: n, activity: r, getToolLabel: i, getToolIcon: s, getActivityOverride: c, defaultOpen: l = !1, className: u }) => {
536
+ let d = e.filter((e) => e.type === "reasoning").map((e) => String(e.text ?? "")).filter((e) => e.trim()).join("\n\n"), f = d.trim().length > 0, p = [], m = !1, h;
537
+ for (let t of e) if (t.type.startsWith("tool-")) {
538
+ m = !0;
539
+ let e = String(t.state ?? "");
540
+ e === "output-available" || e === "output-error" || e === "output-denied" || (h = t.type), p.push({
541
+ kind: "tool",
542
+ label: i?.(t.type) ?? Re(t.type),
543
+ state: e,
544
+ toolType: t.type
545
+ });
546
+ } else t.type === "text" && String(t.text ?? "").trim() && p.push({
433
547
  kind: "note",
434
548
  text: String(t.text)
435
549
  });
436
- p && !t && f.push({ kind: "done" });
437
- let m = e.some((e) => typeof e.startedAtMs == "number") ? J(e, r?.nowMs) : r?.elapsedSeconds ?? 0, h = r?.seed ?? Y(n), g = q({
438
- kind: d ? "reasoning" : "tools",
550
+ m && !t && p.push({ kind: "done" });
551
+ let g = e.some((e) => Number.isFinite(e.startedAtMs)) ? Ie(e, r?.nowMs) : r?.elapsedSeconds ?? 0, _ = r?.seed ?? Le(n), v = t ? g : 0, y = t ? c?.(h) : void 0, b = {
552
+ kind: f ? "reasoning" : "tools",
439
553
  active: t,
440
- seed: h,
441
- elapsedSeconds: t ? m : 0
442
- }), _ = d || f.length > 0, [v, y] = b(c);
554
+ seed: _,
555
+ elapsedSeconds: v,
556
+ verb: y
557
+ }, S = Q(b), { elapsedSeconds: te } = Z(b), C = f || p.length > 0, w = C && t && te === 0, [E, D] = x(l), O = ee();
443
558
  return /* @__PURE__ */ o("div", {
444
- className: C("not-prose flex flex-col", l),
559
+ className: T("not-prose flex flex-col", u),
445
560
  children: [/* @__PURE__ */ a("button", {
446
561
  type: "button",
447
562
  "data-testid": "message-reasoning",
448
- onClick: () => y((e) => !e),
563
+ onClick: () => D((e) => !e),
564
+ "aria-expanded": E,
565
+ "aria-controls": C ? O : void 0,
449
566
  className: "flex items-center gap-1 self-start text-sm text-muted-foreground transition-colors hover:text-foreground",
450
567
  children: /* @__PURE__ */ o("span", {
451
- className: C(t && "ocui-shimmer"),
452
- children: [g, _ ? "..." : ""]
568
+ className: T(t && "ocui-shimmer"),
569
+ children: [S, w ? "..." : ""]
453
570
  })
454
- }), v && _ && /* @__PURE__ */ o("div", {
571
+ }), E && C && /* @__PURE__ */ o("div", {
572
+ id: O,
455
573
  className: "mt-2 flex flex-col gap-2",
456
- children: [d && /* @__PURE__ */ a(ie, {
574
+ children: [f && /* @__PURE__ */ a(ne, {
457
575
  className: "grid gap-1 text-sm text-muted-foreground/80 **:text-sm [&_code]:bg-transparent [&_code]:p-0 [&_code]:font-semibold [&_code]:text-foreground [&_li]:my-0 [&_ol]:my-1 [&_p]:my-0 [&_ul]:my-1",
458
- children: u
459
- }), f.length > 0 && /* @__PURE__ */ a("div", {
576
+ children: d
577
+ }), p.length > 0 && /* @__PURE__ */ a("div", {
460
578
  className: "flex flex-col",
461
- children: f.map((e, t) => /* @__PURE__ */ a(Ae, {
579
+ children: p.map((e, t) => /* @__PURE__ */ a(Be, {
462
580
  node: e,
463
581
  isFirst: t === 0,
464
- isLast: t === f.length - 1,
582
+ isLast: t === p.length - 1,
465
583
  getToolIcon: s
466
584
  }, `step-${t}`))
467
585
  })]
468
586
  })]
469
587
  });
470
- };
471
- function Ae({ node: e, isFirst: t, isLast: n, getToolIcon: r }) {
588
+ });
589
+ ze.displayName = "MessageTrace";
590
+ function Be({ node: e, isFirst: t, isLast: n, getToolIcon: r }) {
472
591
  return /* @__PURE__ */ o("div", {
473
592
  className: "flex items-stretch gap-2.5",
474
593
  children: [/* @__PURE__ */ o("div", {
475
594
  className: "flex w-4 flex-col items-center",
476
595
  children: [
477
- /* @__PURE__ */ a("div", { className: C("w-px flex-1", t ? "bg-transparent" : "bg-border") }),
478
- /* @__PURE__ */ a(je, {
596
+ /* @__PURE__ */ a("div", { className: T("w-px flex-1", t ? "bg-transparent" : "bg-border") }),
597
+ /* @__PURE__ */ a(Ve, {
479
598
  node: e,
480
599
  getToolIcon: r
481
600
  }),
482
- /* @__PURE__ */ a("div", { className: C("w-px flex-1", n ? "bg-transparent" : "bg-border") })
601
+ /* @__PURE__ */ a("div", { className: T("w-px flex-1", n ? "bg-transparent" : "bg-border") })
483
602
  ]
484
603
  }), /* @__PURE__ */ a("div", {
485
604
  className: "min-w-0 py-1 text-sm leading-5",
@@ -496,7 +615,7 @@ function Ae({ node: e, isFirst: t, isLast: n, getToolIcon: r }) {
496
615
  })]
497
616
  });
498
617
  }
499
- function je({ node: e, getToolIcon: t }) {
618
+ function Ve({ node: e, getToolIcon: t }) {
500
619
  let n = "flex items-center justify-center bg-background py-0.5";
501
620
  return e.kind === "note" ? /* @__PURE__ */ a("div", {
502
621
  className: n,
@@ -509,54 +628,16 @@ function je({ node: e, getToolIcon: t }) {
509
628
  children: /* @__PURE__ */ a(t?.(e.toolType) ?? s, { className: "size-3.5 text-muted-foreground" })
510
629
  }) : /* @__PURE__ */ a("div", {
511
630
  className: n,
512
- children: /* @__PURE__ */ a(f, { className: "size-3.5 animate-spin text-muted-foreground" })
631
+ children: /* @__PURE__ */ a(f, { className: "size-3.5 animate-spin text-muted-foreground motion-reduce:animate-none" })
513
632
  }) : /* @__PURE__ */ a("div", {
514
633
  className: n,
515
634
  children: /* @__PURE__ */ a(l, { className: "size-4 text-muted-foreground" })
516
635
  });
517
636
  }
518
637
  //#endregion
519
- //#region src/chat/segments.ts
520
- function Z(e, t) {
521
- return e.startsWith("tool-") && !t(e);
522
- }
523
- function Me(e, t) {
524
- let n = t?.isPresentationalTool ?? (() => !1), r = -1;
525
- e.forEach((e, t) => {
526
- (e.type === "reasoning" || Z(e.type, n)) && (r = t);
527
- });
528
- let i = [], a = null, o = null, s = (e) => {
529
- o = null, a || (a = {
530
- kind: "trace",
531
- parts: []
532
- }, i.push(a)), a.parts.push(e);
533
- };
534
- return e.forEach((e, t) => {
535
- if (!(e.type === "step-start" || e.type === "step-finish")) if (e.type === "reasoning") {
536
- let t = String(e.text ?? ""), n = e.state === "streaming";
537
- if (!(t.trim() || n)) return;
538
- s(e);
539
- } else Z(e.type, n) ? s(e) : e.type === "text" && t <= r ? String(e.text ?? "").trim() && s(e) : e.type === "file" ? (a = null, o || (o = {
540
- kind: "images",
541
- parts: [],
542
- index: t
543
- }, i.push(o)), o.parts.push(e)) : (a = null, o = null, i.push({
544
- kind: "part",
545
- part: e,
546
- index: t
547
- }));
548
- }), i;
549
- }
550
- function Ne(e, t) {
551
- return e.some((e) => {
552
- let n = e.state;
553
- return typeof n == "string" ? e.type === "reasoning" || e.type === "text" ? n === "streaming" : n === "input-streaming" || n === "input-available" : t;
554
- });
555
- }
556
- //#endregion
557
638
  //#region src/chat/use-activity-clock.ts
558
- function Pe(e, t) {
559
- let n = ee(null), [r, i] = b(() => ({
639
+ function He(e, t) {
640
+ let n = b(null), [r, i] = x(() => ({
560
641
  seed: t,
561
642
  elapsedSeconds: 0,
562
643
  nowMs: Date.now()
@@ -597,21 +678,21 @@ function Pe(e, t) {
597
678
  }
598
679
  //#endregion
599
680
  //#region src/chat/sources.tsx
600
- function Fe(e) {
601
- return `Used ${e} source${e === 1 ? "" : "s"}`;
681
+ function Ue(e) {
682
+ return `Searched ${e} source${e === 1 ? "" : "s"}`;
602
683
  }
603
- function Q(e) {
684
+ function $(e) {
604
685
  try {
605
686
  return new URL(e).hostname;
606
687
  } catch {
607
688
  return e;
608
689
  }
609
690
  }
610
- function Ie(e) {
611
- return `https://www.google.com/s2/favicons?domain=${Q(e)}&sz=32`;
691
+ function We(e) {
692
+ return `https://www.google.com/s2/favicons?domain=${$(e)}&sz=32`;
612
693
  }
613
- function Le({ url: e, getFaviconUrl: t }) {
614
- let n = t(e), [r, i] = b(!1);
694
+ function Ge({ url: e, getFaviconUrl: t }) {
695
+ let n = t(e), [r, i] = x(!1);
615
696
  return !n || r ? /* @__PURE__ */ a(d, { className: "size-3.5 shrink-0 text-muted-foreground" }) : /* @__PURE__ */ a("img", {
616
697
  src: n,
617
698
  alt: "",
@@ -619,23 +700,23 @@ function Le({ url: e, getFaviconUrl: t }) {
619
700
  onError: () => i(!0)
620
701
  });
621
702
  }
622
- var Re = ({ sources: e, label: t = Fe, getFaviconUrl: n = Ie, className: r, ...i }) => e.length === 0 ? null : /* @__PURE__ */ o(w, {
623
- className: C("not-prose", r),
703
+ var Ke = ({ sources: e, getLabel: t = Ue, getFaviconUrl: n = We, className: r, ...i }) => e.length === 0 ? null : /* @__PURE__ */ o(k, {
704
+ className: T("not-prose", r),
624
705
  ...i,
625
- children: [/* @__PURE__ */ o(T, {
626
- className: "group flex items-center gap-1 rounded-md px-1.5 py-0.5 text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
706
+ children: [/* @__PURE__ */ o(A, {
707
+ className: "group flex items-center gap-1 self-start text-sm text-muted-foreground transition-colors hover:text-foreground",
627
708
  children: [/* @__PURE__ */ a("span", { children: t(e.length) }), /* @__PURE__ */ a(c, { className: "size-2.5 transition-transform group-data-[state=open]:rotate-180" })]
628
- }), /* @__PURE__ */ a(E, {
709
+ }), /* @__PURE__ */ a(j, {
629
710
  className: "mt-1 flex flex-col gap-0.5 data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 data-[state=closed]:animate-out data-[state=open]:animate-in",
630
711
  children: e.map((e, t) => {
631
- let r = Q(e.url), i = e.title || r;
712
+ let r = $(e.url), i = e.title || r;
632
713
  return /* @__PURE__ */ o("a", {
633
714
  href: e.url,
634
715
  target: "_blank",
635
716
  rel: "noreferrer",
636
717
  className: "flex min-w-0 items-center gap-1.5 rounded-md px-1.5 py-1 text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
637
718
  children: [
638
- /* @__PURE__ */ a(Le, {
719
+ /* @__PURE__ */ a(Ge, {
639
720
  getFaviconUrl: n,
640
721
  url: e.url
641
722
  }),
@@ -651,60 +732,27 @@ var Re = ({ sources: e, label: t = Fe, getFaviconUrl: n = Ie, className: r, ...i
651
732
  }, `${e.url}-${t}`);
652
733
  })
653
734
  })]
654
- }), ze = "ocui-cite";
655
- //#endregion
656
- //#region src/chat/web-search.ts
657
- function $(e) {
658
- return typeof e == "object" && !!e && "error" in e && typeof e.error == "string";
659
- }
660
- var Be = "tool-webSearch";
661
- function Ve(e) {
662
- try {
663
- let t = new URL(e);
664
- return `${t.hostname.toLowerCase()}${t.pathname.replace(/\/$/, "")}${t.search}`;
665
- } catch {
666
- return e.replace(/#.*$/, "").replace(/\/$/, "");
667
- }
668
- }
669
- function He(e, t) {
670
- let n = t?.toolType ?? Be, r = /* @__PURE__ */ new Set(), i = [];
671
- for (let t of e) {
672
- if (t.type !== n || t.state !== "output-available" || $(t.output)) continue;
673
- let e = t.output?.results;
674
- if (Array.isArray(e)) for (let t of e) {
675
- if (typeof t?.url != "string") continue;
676
- let e = Ve(t.url);
677
- r.has(e) || (r.add(e), i.push({
678
- url: t.url,
679
- title: t.title
680
- }));
681
- }
682
- }
683
- return i;
684
- }
685
- //#endregion
686
- //#region src/chat/web-search-toggle.tsx
687
- var Ue = ({ pressed: e, onPressedChange: t, label: n = "Search the web", tooltip: r, className: i, variant: s, size: c = "icon", onClick: l, ...u }) => {
688
- let f = s ?? (e ? "default" : "ghost"), p = /* @__PURE__ */ a(V, {
735
+ }), qe = "ocui-cite", Je = ({ pressed: e, onPressedChange: t, label: n = "Search the web", tooltip: r, className: i, variant: s, size: c = "icon", onClick: l, ...u }) => {
736
+ let f = s ?? (e ? "default" : "ghost"), p = /* @__PURE__ */ a(K, {
737
+ ...u,
689
738
  "aria-label": n,
690
739
  "aria-pressed": e,
691
- className: C(i),
692
- onClick: (n) => {
693
- l?.(n), t(!e);
694
- },
740
+ className: T(i),
695
741
  size: c,
696
742
  type: "button",
697
743
  variant: f,
698
- ...u,
744
+ onClick: (n) => {
745
+ l?.(n), t(!e);
746
+ },
699
747
  children: /* @__PURE__ */ a(d, {})
700
748
  });
701
- return r ? /* @__PURE__ */ a(H, {
749
+ return r ? /* @__PURE__ */ a(q, {
702
750
  delayDuration: 300,
703
- children: /* @__PURE__ */ o(U, { children: [/* @__PURE__ */ a(W, {
751
+ children: /* @__PURE__ */ o(J, { children: [/* @__PURE__ */ a(Y, {
704
752
  asChild: !0,
705
753
  children: p
706
- }), /* @__PURE__ */ a(G, { children: /* @__PURE__ */ a("p", { children: r }) })] })
754
+ }), /* @__PURE__ */ a(X, { children: /* @__PURE__ */ a("p", { children: r }) })] })
707
755
  }) : p;
708
756
  };
709
757
  //#endregion
710
- export { Ee as Action, Te as Actions, oe as MessageContent, ke as MessageTrace, O as Reasoning, j as ReasoningContent, k as ReasoningTrigger, ie as Response, Re as Sources, ge as Tool, xe as ToolContent, be as ToolHeader, Se as ToolInput, Ce as ToolOutput, Ue as WebSearchToggle, Me as buildSegments, ze as citationClassName, He as extractWebSearchSources, q as getAiActivityLabel, X as humanizeToolName, $ as isWebSearchError, Y as labelSeedFromString, A as reasoningTextClassName, J as traceElapsedSeconds, Ne as traceIsLoading, Pe as useActivityClock };
758
+ export { Ce as Action, Se as Actions, O as MessageContent, ze as MessageTrace, N as Reasoning, I as ReasoningContent, P as ReasoningTrigger, ne as Response, Ke as Sources, pe as Tool, ve as ToolContent, _e as ToolHeader, ye as ToolInput, be as ToolOutput, Je as WebSearchToggle, Te as buildSegments, qe as citationClassName, Ne as deriveTurnView, Me as extractWebSearchSources, Q as getAiActivityLabel, Z as getAiActivityLabelParts, Re as humanizeToolName, De as isWebSearchError, Le as labelSeedFromString, F as reasoningTextClassName, ke as stripCitationParens, Ie as traceElapsedSeconds, Ee as traceIsLoading, He as useActivityClock };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ocai/app-sdk-ui",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Shared design tokens and AI-app UI primitives for OceanAI products.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -47,21 +47,26 @@
47
47
  },
48
48
  "devDependencies": {
49
49
  "@streamdown/math": "^1.0.2",
50
+ "@testing-library/dom": "^10.4.1",
51
+ "@testing-library/react": "^16.3.2",
50
52
  "@types/node": "^22.0.0",
51
53
  "@types/react": "^19.0.0",
52
54
  "@types/react-dom": "^19.0.0",
53
55
  "@vitejs/plugin-react": "^6.0.1",
54
56
  "ai": "^6.0.0",
57
+ "jsdom": "^26.1.0",
55
58
  "react": "^19.0.0",
56
59
  "react-dom": "^19.0.0",
57
60
  "streamdown": "^2.5.0",
58
61
  "typescript": "~5.6.0",
59
62
  "vite": "^8.0.16",
60
- "vite-plugin-dts": "^4.5.4"
63
+ "vite-plugin-dts": "^4.5.4",
64
+ "vitest": "^4.1.10"
61
65
  },
62
66
  "scripts": {
63
67
  "build": "vite build && cp src/tokens.css src/chat/chat.css dist/",
64
68
  "dev": "vite build --watch",
65
- "typecheck": "tsc --noEmit"
69
+ "typecheck": "tsc --noEmit",
70
+ "test": "vitest run"
66
71
  }
67
72
  }