@ocai/app-sdk-ui 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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;
@@ -1,5 +1,5 @@
1
1
  import { LucideIcon } from 'lucide-react';
2
- import { ReactElement } from 'react';
2
+ import { ReactElement, ReactNode } from 'react';
3
3
  import { AiActivity } from './activity-labels';
4
4
  import { TracePart } from './segments';
5
5
  export declare function humanizeToolName(type: string): string;
@@ -10,7 +10,9 @@ 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;
14
+ renderStepDetail?: (part: TracePart) => ReactNode;
13
15
  defaultOpen?: boolean;
14
16
  className?: string;
15
17
  };
16
- export declare const MessageTrace: ({ parts, isLoading, messageId, activity, getToolLabel, getToolIcon, defaultOpen, className, }: MessageTraceProps) => ReactElement;
18
+ export declare const MessageTrace: import('react').MemoExoticComponent<({ parts, isLoading, messageId, activity, getToolLabel, getToolIcon, getActivityOverride, renderStepDetail, 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 {};
@@ -4,10 +4,16 @@ export type SourceItem = {
4
4
  url: string;
5
5
  title?: string;
6
6
  };
7
+ export type SourceListProps = {
8
+ sources: readonly SourceItem[];
9
+ getFaviconUrl?: (url: string) => string | null;
10
+ className?: string;
11
+ };
12
+ export declare const SourceList: ({ sources, getFaviconUrl, className, }: SourceListProps) => ReactElement | null;
7
13
  export type SourcesProps = Omit<ComponentProps<typeof Collapsible>, "children"> & {
8
14
  sources: readonly SourceItem[];
9
- label?: (count: number) => string;
15
+ getLabel?: (count: number) => string;
10
16
  getFaviconUrl?: (url: string) => string | null;
11
17
  };
12
- export declare const Sources: ({ sources, label, getFaviconUrl, className, ...props }: SourcesProps) => ReactElement | null;
18
+ export declare const Sources: ({ sources, getLabel, getFaviconUrl, className, ...props }: SourcesProps) => ReactElement | null;
13
19
  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
- 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';
11
+ export { Sources, SourceList, citationClassName, type SourceItem, type SourcesProps, type SourceListProps, } from './chat/sources';
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 C, Tooltip as w } from "radix-ui";
10
+ import { cva as T } from "class-variance-authority";
11
11
  //#region src/lib/cn.ts
12
- function C(...e) {
12
+ function E(...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 te = 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: E("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: te },
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: E("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: E("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: E("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: E(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: E("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: E(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: E("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: E("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: E("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: E("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: E("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 = T("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 }) {
287
- return /* @__PURE__ */ a(r ? te.Root : "button", {
285
+ function K({ className: e, variant: t = "default", size: n = "default", asChild: r = !1, ...i }) {
286
+ return /* @__PURE__ */ a(r ? C.Root : "button", {
288
287
  "data-slot": "button",
289
288
  "data-variant": t,
290
289
  "data-size": n,
291
- className: C(we({
290
+ className: E(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(w.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(w.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(w.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(w.Portal, { children: /* @__PURE__ */ o(w.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: E("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(w.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: E("-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: E("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"
@@ -389,114 +487,166 @@ var Te = ({ className: e, children: t, ...n }) => /* @__PURE__ */ a("div", {
389
487
  done: "Refined"
390
488
  },
391
489
  {
392
- active: "Working",
393
- done: "Done"
490
+ active: "Musing",
491
+ done: "Mused"
492
+ },
493
+ {
494
+ active: "Scurrying",
495
+ done: "Scurried"
496
+ },
497
+ {
498
+ active: "Crunching",
499
+ done: "Crunched"
394
500
  }
395
- ], De = {
396
- waiting: K,
397
- reasoning: K,
501
+ ], Fe = {
502
+ waiting: Pe,
503
+ reasoning: Pe,
398
504
  tools: [{
399
505
  active: "Working",
400
506
  done: "Worked"
401
507
  }]
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`;
508
+ };
509
+ function Z({ kind: e, active: t, seed: n, elapsedSeconds: r, verb: i }) {
510
+ let a;
511
+ if (i !== void 0) a = i;
512
+ else {
513
+ let r = Fe[e], i = r[Math.abs(n) % r.length];
514
+ a = t ? i.active : i.done;
515
+ }
516
+ return {
517
+ verb: a,
518
+ elapsedSeconds: r > 0 ? r : 0
519
+ };
520
+ }
521
+ function Ie(e) {
522
+ let { verb: t, elapsedSeconds: n } = Z(e);
523
+ return n <= 0 ? t : `${t} for ${n}s`;
406
524
  }
407
- function J(e, t) {
525
+ function Le(e, t) {
408
526
  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);
527
+ 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
528
  if (n === Infinity) return 0;
411
- let a = i ? t ?? r : r;
529
+ let a = i && Number.isFinite(t) ? t : r;
412
530
  return a === -Infinity ? 0 : Math.max(0, Math.floor((a - n) / 1e3));
413
531
  }
414
- function Y(e) {
532
+ function Re(e) {
415
533
  let t = 0;
416
534
  for (let n = 0; n < e.length; n += 1) t = t * 31 + e.charCodeAt(n) | 0;
417
535
  return t;
418
536
  }
419
537
  //#endregion
420
538
  //#region src/chat/message-trace.tsx
421
- function X(e) {
539
+ function Q(e) {
422
540
  let t = e.replace(/^tool-/, "").replace(/[_-]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim();
423
541
  return t.charAt(0).toUpperCase() + t.slice(1);
424
542
  }
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({
543
+ var ze = _(({ parts: e, isLoading: t, messageId: n, activity: r, getToolLabel: i, getToolIcon: s, getActivityOverride: c, renderStepDetail: l, defaultOpen: u = !1, className: d }) => {
544
+ let f = e.filter((e) => e.type === "reasoning").map((e) => String(e.text ?? "")).filter((e) => e.trim()).join("\n\n"), p = f.trim().length > 0, m = [], h = !1, g;
545
+ for (let t of e) if (t.type.startsWith("tool-")) {
546
+ h = !0;
547
+ let e = String(t.state ?? "");
548
+ e === "output-available" || e === "output-error" || e === "output-denied" || (g = t.type), m.push({
549
+ kind: "tool",
550
+ label: i?.(t.type) ?? Q(t.type),
551
+ state: e,
552
+ toolType: t.type,
553
+ part: t
554
+ });
555
+ } else t.type === "text" && String(t.text ?? "").trim() && m.push({
433
556
  kind: "note",
434
557
  text: String(t.text)
435
558
  });
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",
559
+ h && !t && m.push({ kind: "done" });
560
+ let _ = e.some((e) => Number.isFinite(e.startedAtMs)) ? Le(e, r?.nowMs) : r?.elapsedSeconds ?? 0, v = r?.seed ?? Re(n), y = t ? _ : 0, b = t ? c?.(g) : void 0, S = {
561
+ kind: p ? "reasoning" : "tools",
439
562
  active: t,
440
- seed: h,
441
- elapsedSeconds: t ? m : 0
442
- }), _ = d || f.length > 0, [v, y] = b(c);
563
+ seed: v,
564
+ elapsedSeconds: y,
565
+ verb: b
566
+ }, C = Ie(S), { elapsedSeconds: w } = Z(S), T = p || m.length > 0, te = T && t && w === 0, [D, O] = x(u), k = ee();
443
567
  return /* @__PURE__ */ o("div", {
444
- className: C("not-prose flex flex-col", l),
568
+ className: E("not-prose flex flex-col", d),
445
569
  children: [/* @__PURE__ */ a("button", {
446
570
  type: "button",
447
571
  "data-testid": "message-reasoning",
448
- onClick: () => y((e) => !e),
449
- className: "flex items-center gap-1 self-start text-sm text-muted-foreground transition-colors hover:text-foreground",
572
+ onClick: () => O((e) => !e),
573
+ "aria-expanded": D,
574
+ "aria-controls": T ? k : void 0,
575
+ className: "flex items-center gap-1 self-start text-[13px] leading-5 text-muted-foreground transition-colors hover:text-foreground",
450
576
  children: /* @__PURE__ */ o("span", {
451
- className: C(t && "ocui-shimmer"),
452
- children: [g, _ ? "..." : ""]
577
+ className: E(t && "ocui-shimmer"),
578
+ children: [C, te ? "..." : ""]
453
579
  })
454
- }), v && _ && /* @__PURE__ */ o("div", {
580
+ }), D && T && /* @__PURE__ */ o("div", {
581
+ id: k,
455
582
  className: "mt-2 flex flex-col gap-2",
456
- children: [d && /* @__PURE__ */ a(ie, {
583
+ children: [p && /* @__PURE__ */ a(ne, {
457
584
  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", {
585
+ children: f
586
+ }), m.length > 0 && /* @__PURE__ */ a("div", {
460
587
  className: "flex flex-col",
461
- children: f.map((e, t) => /* @__PURE__ */ a(Ae, {
588
+ children: m.map((e, t) => /* @__PURE__ */ a(Be, {
462
589
  node: e,
463
590
  isFirst: t === 0,
464
- isLast: t === f.length - 1,
465
- getToolIcon: s
591
+ isLast: t === m.length - 1,
592
+ getToolIcon: s,
593
+ renderStepDetail: l
466
594
  }, `step-${t}`))
467
595
  })]
468
596
  })]
469
597
  });
470
- };
471
- function Ae({ node: e, isFirst: t, isLast: n, getToolIcon: r }) {
598
+ });
599
+ ze.displayName = "MessageTrace";
600
+ function Be({ node: e, isFirst: t, isLast: n, getToolIcon: r, renderStepDetail: i }) {
601
+ let s = e.kind === "tool" && i ? i(e.part) : null, l = s != null, [u, d] = x(!1), f = ee();
472
602
  return /* @__PURE__ */ o("div", {
473
- className: "flex items-stretch gap-2.5",
603
+ className: "flex flex-col",
474
604
  children: [/* @__PURE__ */ o("div", {
475
- className: "flex w-4 flex-col items-center",
476
- children: [
477
- /* @__PURE__ */ a("div", { className: C("w-px flex-1", t ? "bg-transparent" : "bg-border") }),
478
- /* @__PURE__ */ a(je, {
479
- node: e,
480
- getToolIcon: r
481
- }),
482
- /* @__PURE__ */ a("div", { className: C("w-px flex-1", n ? "bg-transparent" : "bg-border") })
483
- ]
484
- }), /* @__PURE__ */ a("div", {
485
- className: "min-w-0 py-1 text-sm leading-5",
486
- children: e.kind === "tool" ? /* @__PURE__ */ a("span", {
487
- className: "text-muted-foreground",
488
- children: e.label
489
- }) : e.kind === "note" ? /* @__PURE__ */ a("span", {
490
- className: "text-muted-foreground/80",
491
- children: e.text
492
- }) : /* @__PURE__ */ a("span", {
493
- className: "text-muted-foreground",
494
- children: "Done"
495
- })
605
+ className: "flex items-stretch gap-2.5",
606
+ children: [/* @__PURE__ */ o("div", {
607
+ className: "flex w-4 flex-col items-center",
608
+ children: [
609
+ /* @__PURE__ */ a("div", { className: E("w-px flex-1", t ? "bg-transparent" : "bg-border") }),
610
+ /* @__PURE__ */ a(Ve, {
611
+ node: e,
612
+ getToolIcon: r
613
+ }),
614
+ /* @__PURE__ */ a("div", { className: E("w-px flex-1", n ? "bg-transparent" : "bg-border") })
615
+ ]
616
+ }), /* @__PURE__ */ a("div", {
617
+ className: "min-w-0 py-1 text-sm leading-5",
618
+ children: e.kind === "tool" ? l ? /* @__PURE__ */ o("button", {
619
+ type: "button",
620
+ onClick: () => d((e) => !e),
621
+ "aria-expanded": u,
622
+ "aria-controls": f,
623
+ className: "group inline-flex items-center gap-1 text-sm text-muted-foreground transition-colors hover:text-foreground",
624
+ children: [/* @__PURE__ */ a("span", { children: e.label }), /* @__PURE__ */ a(c, { className: E("size-2.5 transition-[transform,opacity]", u ? "rotate-180 opacity-100" : "opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100") })]
625
+ }) : /* @__PURE__ */ a("span", {
626
+ className: "text-muted-foreground",
627
+ children: e.label
628
+ }) : e.kind === "note" ? /* @__PURE__ */ a("span", {
629
+ className: "text-muted-foreground/80",
630
+ children: e.text
631
+ }) : /* @__PURE__ */ a("span", {
632
+ className: "text-muted-foreground",
633
+ children: "Done"
634
+ })
635
+ })]
636
+ }), l && u && /* @__PURE__ */ o("div", {
637
+ className: "flex items-stretch gap-2.5",
638
+ children: [/* @__PURE__ */ a("div", {
639
+ className: "flex w-4 flex-col items-center",
640
+ children: /* @__PURE__ */ a("div", { className: E("w-px flex-1", n ? "bg-transparent" : "bg-border") })
641
+ }), /* @__PURE__ */ a("div", {
642
+ id: f,
643
+ className: "min-w-0 flex-1 pb-1",
644
+ children: s
645
+ })]
496
646
  })]
497
647
  });
498
648
  }
499
- function je({ node: e, getToolIcon: t }) {
649
+ function Ve({ node: e, getToolIcon: t }) {
500
650
  let n = "flex items-center justify-center bg-background py-0.5";
501
651
  return e.kind === "note" ? /* @__PURE__ */ a("div", {
502
652
  className: n,
@@ -509,54 +659,16 @@ function je({ node: e, getToolIcon: t }) {
509
659
  children: /* @__PURE__ */ a(t?.(e.toolType) ?? s, { className: "size-3.5 text-muted-foreground" })
510
660
  }) : /* @__PURE__ */ a("div", {
511
661
  className: n,
512
- children: /* @__PURE__ */ a(f, { className: "size-3.5 animate-spin text-muted-foreground" })
662
+ children: /* @__PURE__ */ a(f, { className: "size-3.5 animate-spin text-muted-foreground motion-reduce:animate-none" })
513
663
  }) : /* @__PURE__ */ a("div", {
514
664
  className: n,
515
665
  children: /* @__PURE__ */ a(l, { className: "size-4 text-muted-foreground" })
516
666
  });
517
667
  }
518
668
  //#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
669
  //#region src/chat/use-activity-clock.ts
558
- function Pe(e, t) {
559
- let n = ee(null), [r, i] = b(() => ({
670
+ function He(e, t) {
671
+ let n = b(null), [r, i] = x(() => ({
560
672
  seed: t,
561
673
  elapsedSeconds: 0,
562
674
  nowMs: Date.now()
@@ -597,21 +709,21 @@ function Pe(e, t) {
597
709
  }
598
710
  //#endregion
599
711
  //#region src/chat/sources.tsx
600
- function Fe(e) {
601
- return `Used ${e} source${e === 1 ? "" : "s"}`;
712
+ function Ue(e) {
713
+ return `Searched ${e} source${e === 1 ? "" : "s"}`;
602
714
  }
603
- function Q(e) {
715
+ function We(e) {
604
716
  try {
605
717
  return new URL(e).hostname;
606
718
  } catch {
607
719
  return e;
608
720
  }
609
721
  }
610
- function Ie(e) {
611
- return `https://www.google.com/s2/favicons?domain=${Q(e)}&sz=32`;
722
+ function Ge(e) {
723
+ return `https://www.google.com/s2/favicons?domain=${We(e)}&sz=32`;
612
724
  }
613
- function Le({ url: e, getFaviconUrl: t }) {
614
- let n = t(e), [r, i] = b(!1);
725
+ function Ke({ url: e, getFaviconUrl: t }) {
726
+ let n = t(e), [r, i] = x(!1);
615
727
  return !n || r ? /* @__PURE__ */ a(d, { className: "size-3.5 shrink-0 text-muted-foreground" }) : /* @__PURE__ */ a("img", {
616
728
  src: n,
617
729
  alt: "",
@@ -619,92 +731,65 @@ function Le({ url: e, getFaviconUrl: t }) {
619
731
  onError: () => i(!0)
620
732
  });
621
733
  }
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),
734
+ var $ = ({ sources: e, getFaviconUrl: t = Ge, className: n }) => e.length === 0 ? null : /* @__PURE__ */ a("div", {
735
+ className: E("flex flex-col gap-0.5", n),
736
+ children: e.map((e, n) => {
737
+ let r = We(e.url), i = e.title || r;
738
+ return /* @__PURE__ */ o("a", {
739
+ href: e.url,
740
+ target: "_blank",
741
+ rel: "noreferrer",
742
+ 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",
743
+ children: [
744
+ /* @__PURE__ */ a(Ke, {
745
+ getFaviconUrl: t,
746
+ url: e.url
747
+ }),
748
+ /* @__PURE__ */ a("span", {
749
+ className: "truncate",
750
+ children: i
751
+ }),
752
+ i !== r && /* @__PURE__ */ a("span", {
753
+ className: "shrink-0 truncate text-xs text-muted-foreground/70",
754
+ children: r
755
+ })
756
+ ]
757
+ }, `${e.url}-${n}`);
758
+ })
759
+ }), qe = ({ sources: e, getLabel: t = Ue, getFaviconUrl: n = Ge, className: r, ...i }) => e.length === 0 ? null : /* @__PURE__ */ o(k, {
760
+ className: E("not-prose", r),
624
761
  ...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",
762
+ children: [/* @__PURE__ */ o(A, {
763
+ className: "group flex items-center gap-1 self-start text-[13px] leading-5 text-muted-foreground transition-colors hover:text-foreground",
627
764
  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, {
629
- 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
- children: e.map((e, t) => {
631
- let r = Q(e.url), i = e.title || r;
632
- return /* @__PURE__ */ o("a", {
633
- href: e.url,
634
- target: "_blank",
635
- rel: "noreferrer",
636
- 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
- children: [
638
- /* @__PURE__ */ a(Le, {
639
- getFaviconUrl: n,
640
- url: e.url
641
- }),
642
- /* @__PURE__ */ a("span", {
643
- className: "truncate",
644
- children: i
645
- }),
646
- i !== r && /* @__PURE__ */ a("span", {
647
- className: "shrink-0 truncate text-xs text-muted-foreground/70",
648
- children: r
649
- })
650
- ]
651
- }, `${e.url}-${t}`);
765
+ }), /* @__PURE__ */ a(j, {
766
+ className: "mt-1 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",
767
+ children: /* @__PURE__ */ a($, {
768
+ sources: e,
769
+ getFaviconUrl: n
652
770
  })
653
771
  })]
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, {
772
+ }), Je = "ocui-cite", Ye = ({ pressed: e, onPressedChange: t, label: n = "Search the web", tooltip: r, className: i, variant: s, size: c = "icon", onClick: l, ...u }) => {
773
+ let f = s ?? (e ? "default" : "ghost"), p = /* @__PURE__ */ a(K, {
774
+ ...u,
689
775
  "aria-label": n,
690
776
  "aria-pressed": e,
691
- className: C(i),
692
- onClick: (n) => {
693
- l?.(n), t(!e);
694
- },
777
+ className: E(i),
695
778
  size: c,
696
779
  type: "button",
697
780
  variant: f,
698
- ...u,
781
+ onClick: (n) => {
782
+ l?.(n), t(!e);
783
+ },
699
784
  children: /* @__PURE__ */ a(d, {})
700
785
  });
701
- return r ? /* @__PURE__ */ a(H, {
786
+ return r ? /* @__PURE__ */ a(q, {
702
787
  delayDuration: 300,
703
- children: /* @__PURE__ */ o(U, { children: [/* @__PURE__ */ a(W, {
788
+ children: /* @__PURE__ */ o(J, { children: [/* @__PURE__ */ a(Y, {
704
789
  asChild: !0,
705
790
  children: p
706
- }), /* @__PURE__ */ a(G, { children: /* @__PURE__ */ a("p", { children: r }) })] })
791
+ }), /* @__PURE__ */ a(X, { children: /* @__PURE__ */ a("p", { children: r }) })] })
707
792
  }) : p;
708
793
  };
709
794
  //#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 };
795
+ 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, $ as SourceList, qe as Sources, pe as Tool, ve as ToolContent, _e as ToolHeader, ye as ToolInput, be as ToolOutput, Ye as WebSearchToggle, Te as buildSegments, Je as citationClassName, Ne as deriveTurnView, Me as extractWebSearchSources, Ie as getAiActivityLabel, Z as getAiActivityLabelParts, Q as humanizeToolName, De as isWebSearchError, Re as labelSeedFromString, F as reasoningTextClassName, ke as stripCitationParens, Le 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.4.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
  }