@ctrl-ui/react 0.9.5 → 0.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +2 -2
  2. package/dist/action-bar.js +16 -8
  3. package/dist/activity.d.ts +1 -1
  4. package/dist/activity.js +3 -1
  5. package/dist/audio-visualizer.js +1 -1
  6. package/dist/blocks/agent-run-clock.d.ts +5 -0
  7. package/dist/blocks/agent-run-clock.js +52 -0
  8. package/dist/blocks/agent-run-sidebar.d.ts +13 -0
  9. package/dist/blocks/agent-run-sidebar.js +100 -0
  10. package/dist/blocks/agent-run-status.d.ts +32 -0
  11. package/dist/blocks/agent-run-status.js +27 -0
  12. package/dist/blocks/agent-run.d.ts +80 -0
  13. package/dist/blocks/agent-run.js +254 -0
  14. package/dist/blocks/coding-agent.d.ts +1 -1
  15. package/dist/blocks/design-canvas-data.d.ts +1 -1
  16. package/dist/blocks/design-canvas.d.ts +2 -1
  17. package/dist/blocks/design-canvas.js +15 -8
  18. package/dist/chat-message.js +33 -39
  19. package/dist/knob-contracts/infinite-canvas-knobs.d.ts +1 -1
  20. package/dist/knob-contracts/infinite-canvas-knobs.js +1 -7
  21. package/dist/knob-contracts/swap-knobs.d.ts +4 -0
  22. package/dist/knob-contracts/swap-knobs.js +9 -0
  23. package/dist/knob-contracts/timeline-knobs.d.ts +1 -1
  24. package/dist/knob-contracts/timeline-knobs.js +1 -0
  25. package/dist/knob-contracts/tool-approval-knobs.d.ts +4 -0
  26. package/dist/knob-contracts/tool-approval-knobs.js +32 -0
  27. package/dist/lib/infinite-canvas-camera-store.d.ts +65 -0
  28. package/dist/lib/infinite-canvas-camera-store.js +233 -0
  29. package/dist/lib/infinite-canvas-camera.d.ts +33 -0
  30. package/dist/lib/infinite-canvas-camera.js +98 -0
  31. package/dist/lib/infinite-canvas-navigation.d.ts +19 -0
  32. package/dist/lib/infinite-canvas-navigation.js +395 -0
  33. package/dist/markdown-editor/bubble-menu.d.ts +6 -0
  34. package/dist/markdown-editor/bubble-menu.js +45 -0
  35. package/dist/markdown-editor/toolbar.d.ts +17 -1
  36. package/dist/markdown-editor/toolbar.js +103 -81
  37. package/dist/styles/index.css +10 -8
  38. package/dist/styles/recipes/activity.css +8 -0
  39. package/dist/styles/recipes/button-group.css +12 -6
  40. package/dist/styles/recipes/button.css +12 -2
  41. package/dist/styles/recipes/chat-message-edit.css +6 -21
  42. package/dist/styles/recipes/infinite-canvas.css +17 -57
  43. package/dist/styles/recipes/markdown-editor.css +12 -0
  44. package/dist/styles/recipes/swap.css +143 -0
  45. package/dist/styles/recipes/timeline.css +11 -0
  46. package/dist/styles/recipes/tool-approval.css +267 -0
  47. package/dist/styles/recipes/track-highlight.css +19 -6
  48. package/dist/styles/theme.css +6 -5
  49. package/dist/styles/type-scale.css +1 -1
  50. package/dist/tool-approval.d.ts +49 -0
  51. package/dist/tool-approval.js +260 -0
  52. package/dist/ui/code.js +13 -3
  53. package/dist/ui/command.js +1 -0
  54. package/dist/ui/infinite-canvas.d.ts +19 -22
  55. package/dist/ui/infinite-canvas.js +264 -325
  56. package/dist/ui/rich-tooltip.d.ts +2 -1
  57. package/dist/ui/rich-tooltip.js +32 -29
  58. package/dist/ui/swap.d.ts +14 -0
  59. package/dist/ui/swap.js +69 -0
  60. package/dist/ui/timeline.d.ts +1 -1
  61. package/dist/ui/timeline.js +1 -1
  62. package/dist/user-ask.js +1 -1
  63. package/lint/no-raw-type-classes.grit +2 -2
  64. package/package.json +2 -2
package/README.md CHANGED
@@ -47,10 +47,10 @@ const twMerge = extendTailwindMerge({ extend: { classGroups: { "font-size": [{ t
47
47
 
48
48
  Controls size their own text: pick the control's `size`, or set its `--cui-*-font-size` knob in your theme, rather than wrapping the label in a `text-*` span.
49
49
 
50
- With Biome, the shipped GritQL plugin warns on type rungs in `className`:
50
+ With Biome, the shipped GritQL plugin errors on text sizes in `className`: the type rungs, Tailwind's `text-xs`…`text-9xl`, and arbitrary `text-[13px]`. Adopt it folder by folder through `overrides` while you migrate:
51
51
 
52
52
  ```json
53
- { "plugins": ["./node_modules/@ctrl-ui/react/lint/no-raw-type-classes.grit"] }
53
+ { "overrides": [{ "includes": ["src/features/**"], "plugins": ["./node_modules/@ctrl-ui/react/lint/no-raw-type-classes.grit"] }] }
54
54
  ```
55
55
 
56
56
  ## Customize
@@ -3,6 +3,7 @@ import { useCopyToClipboard } from "./hooks/use-copy-to-clipboard.js";
3
3
  import { cn } from "./lib/cn.js";
4
4
  import { Button } from "./ui/button.js";
5
5
  import { LiveStatus } from "./ui/live-status.js";
6
+ import { Swap, SwapItem } from "./ui/swap.js";
6
7
  import { Toolbar } from "@base-ui/react/toolbar";
7
8
  import { createContext, useContext } from "react";
8
9
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -66,21 +67,28 @@ function ActionBarCopy({ value, children = "Copy", copiedChildren = "Copied", co
66
67
  onCopyError?.(error);
67
68
  }
68
69
  }
70
+ const copied = status === "copied";
69
71
  let copyStatus = "";
70
- if (status === "copied") copyStatus = copiedAriaLabel;
72
+ if (copied) copyStatus = copiedAriaLabel;
71
73
  else if (status === "failed") copyStatus = copyFailedLabel;
72
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ActionBarItem, {
74
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(ActionBarItem, {
73
75
  disabled: disabled ?? !copyValue,
74
76
  onClick: handleClick,
75
77
  ...props,
76
- children: status === "copied" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
77
- "aria-hidden": "true",
78
- className: "contents",
79
- children: copiedChildren
80
- }), /* @__PURE__ */ jsx("span", {
78
+ children: [/* @__PURE__ */ jsxs(Swap, {
79
+ value: copied ? "copied" : "idle",
80
+ children: [/* @__PURE__ */ jsx(SwapItem, {
81
+ value: "idle",
82
+ children
83
+ }), /* @__PURE__ */ jsx(SwapItem, {
84
+ value: "copied",
85
+ "aria-hidden": "true",
86
+ children: copiedChildren
87
+ })]
88
+ }), copied ? /* @__PURE__ */ jsx("span", {
81
89
  className: "sr-only",
82
90
  children
83
- })] }) : children
91
+ }) : null]
84
92
  }), /* @__PURE__ */ jsx(LiveStatus, { message: copyStatus })] });
85
93
  }
86
94
  function ActionBarEdit({ value, children = "Edit", disabled, onClick, ...props }) {
@@ -4,7 +4,7 @@ import { ScrollAreaProps } from "./ui/scroll-area.js";
4
4
  import { CSSProperties, ComponentProps, ReactNode } from "react";
5
5
  import { ChevronRight } from "lucide-react";
6
6
  //#region src/activity.d.ts
7
- export type ActivityState = "pending" | "running" | "success" | "error";
7
+ export type ActivityState = "pending" | "running" | "waiting" | "success" | "error";
8
8
  export type ActivityKind = "default" | "tool" | "reasoning" | "signal";
9
9
  export type ActivityDetailFormat = "text" | "code";
10
10
  export type ActivityProps = Omit<CollapsibleProps, "children" | "style"> & {
package/dist/activity.js CHANGED
@@ -5,17 +5,19 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./ui/collap
5
5
  import { ScrollArea } from "./ui/scroll-area.js";
6
6
  import { createContext, useContext } from "react";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
- import { Check, ChevronRight, CircleAlert, CircleDashed, LoaderCircle } from "lucide-react";
8
+ import { Check, ChevronRight, CircleAlert, CircleDashed, CirclePause, LoaderCircle } from "lucide-react";
9
9
  //#region src/activity.tsx
10
10
  const activityStatusLabels = {
11
11
  pending: "Pending",
12
12
  running: "Running",
13
+ waiting: "Waiting",
13
14
  success: "Complete",
14
15
  error: "Failed"
15
16
  };
16
17
  const activityStatusIcons = {
17
18
  pending: CircleDashed,
18
19
  running: LoaderCircle,
20
+ waiting: CirclePause,
19
21
  success: Check,
20
22
  error: CircleAlert
21
23
  };
@@ -61,7 +61,7 @@ function AudioVisualizer({ levels, points, active = true, className, style, ...p
61
61
  const visibleLevel = MIN_VISIBLE_LEVEL + perceptualLevel * .96;
62
62
  const levelStyle = {
63
63
  "--_audio-visualizer-level-opacity": `${.48 + perceptualLevel * .52}`,
64
- clipPath: `inset(${(1 - visibleLevel) * 50}% 0 round var(--cui-audio-visualizer-bar-radius))`
64
+ clipPath: `inset(${((1 - visibleLevel) * 50).toFixed(3)}% 0 round var(--cui-audio-visualizer-bar-radius))`
65
65
  };
66
66
  return /* @__PURE__ */ jsx("span", {
67
67
  ref: key === "bar-0" ? firstBarRef : void 0,
@@ -0,0 +1,5 @@
1
+ //#region src/blocks/agent-run-clock.d.ts
2
+ export declare function useRunClock(active: boolean): number | null;
3
+ export declare function formatElapsed(ms: number): string;
4
+ export declare function elapsedIsoDuration(ms: number): string;
5
+ //#endregion
@@ -0,0 +1,52 @@
1
+ import { useSyncExternalStore } from "react";
2
+ //#region src/blocks/agent-run-clock.ts
3
+ const TICK_MS = 1e3;
4
+ let now = null;
5
+ const listeners = /* @__PURE__ */ new Set();
6
+ let interval;
7
+ function tick() {
8
+ if (document.hidden) return;
9
+ now = Date.now();
10
+ for (const listener of listeners) listener();
11
+ }
12
+ function onVisibilityChange() {
13
+ if (!document.hidden) tick();
14
+ }
15
+ function subscribeToRunClock(listener) {
16
+ listeners.add(listener);
17
+ if (listeners.size === 1) {
18
+ now = Date.now();
19
+ interval = setInterval(tick, TICK_MS);
20
+ document.addEventListener("visibilitychange", onVisibilityChange);
21
+ }
22
+ return () => {
23
+ listeners.delete(listener);
24
+ if (listeners.size > 0) return;
25
+ clearInterval(interval);
26
+ interval = void 0;
27
+ document.removeEventListener("visibilitychange", onVisibilityChange);
28
+ };
29
+ }
30
+ const subscribeToNothing = () => () => {};
31
+ function useRunClock(active) {
32
+ return useSyncExternalStore(active ? subscribeToRunClock : subscribeToNothing, () => now, () => null);
33
+ }
34
+ function splitElapsed(ms) {
35
+ const totalSeconds = Math.floor(Math.max(0, ms) / 1e3);
36
+ return {
37
+ hours: Math.floor(totalSeconds / 3600),
38
+ minutes: Math.floor(totalSeconds % 3600 / 60),
39
+ seconds: totalSeconds % 60
40
+ };
41
+ }
42
+ const pad2 = (value) => String(value).padStart(2, "0");
43
+ function formatElapsed(ms) {
44
+ const { hours, minutes, seconds } = splitElapsed(ms);
45
+ return hours > 0 ? `${hours}:${pad2(minutes)}:${pad2(seconds)}` : `${minutes}:${pad2(seconds)}`;
46
+ }
47
+ function elapsedIsoDuration(ms) {
48
+ const { hours, minutes, seconds } = splitElapsed(ms);
49
+ return `PT${hours ? `${hours}H` : ""}${minutes ? `${minutes}M` : ""}${seconds}S`;
50
+ }
51
+ //#endregion
52
+ export { elapsedIsoDuration, formatElapsed, useRunClock };
@@ -0,0 +1,13 @@
1
+ import { AgentRunSummary } from "./agent-run-status.js";
2
+ import { ReactNode } from "react";
3
+ //#region src/blocks/agent-run-sidebar.d.ts
4
+ export declare function AgentRunSidebar({ appName, brand, runs, activeRunId, sidebarFooter, onRunSelect, onNewRun }: {
5
+ appName: string;
6
+ brand?: ReactNode;
7
+ runs: readonly AgentRunSummary[];
8
+ activeRunId?: string;
9
+ sidebarFooter?: ReactNode;
10
+ onRunSelect?: (run: AgentRunSummary) => void;
11
+ onNewRun?: () => void;
12
+ }): import("react").JSX.Element;
13
+ //#endregion
@@ -0,0 +1,100 @@
1
+ "use client";
2
+ import { Text } from "../ui/typography.js";
3
+ import { useSidebar } from "../ui/sidebar-provider.js";
4
+ import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "../ui/sidebar-menu.js";
5
+ import { Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarRail } from "../ui/sidebar.js";
6
+ import { agentRunStatusLabels } from "./agent-run-status.js";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { BotIcon, Check, CircleAlert, CircleDashed, CirclePause, CircleSlash, LoaderCircle, PlusIcon } from "lucide-react";
9
+ //#region src/blocks/agent-run-sidebar.tsx
10
+ const runStatusIcons = {
11
+ running: /* @__PURE__ */ jsx(LoaderCircle, {
12
+ className: "size-3.5 shrink-0 motion-safe:animate-spin",
13
+ "aria-hidden": "true"
14
+ }),
15
+ waiting: /* @__PURE__ */ jsx(CirclePause, {
16
+ className: "size-3.5 shrink-0 text-warning-text",
17
+ "aria-hidden": "true"
18
+ }),
19
+ success: /* @__PURE__ */ jsx(Check, {
20
+ className: "size-3.5 shrink-0 text-success-text",
21
+ "aria-hidden": "true"
22
+ }),
23
+ error: /* @__PURE__ */ jsx(CircleAlert, {
24
+ className: "size-3.5 shrink-0 text-destructive-text",
25
+ "aria-hidden": "true"
26
+ }),
27
+ cancelled: /* @__PURE__ */ jsx(CircleSlash, {
28
+ className: "size-3.5 shrink-0 text-muted-foreground",
29
+ "aria-hidden": "true"
30
+ }),
31
+ pending: /* @__PURE__ */ jsx(CircleDashed, {
32
+ className: "size-3.5 shrink-0 text-muted-foreground",
33
+ "aria-hidden": "true"
34
+ })
35
+ };
36
+ function AgentRunSidebar({ appName, brand, runs, activeRunId, sidebarFooter, onRunSelect, onNewRun }) {
37
+ const { setOpenMobile } = useSidebar();
38
+ return /* @__PURE__ */ jsxs(Sidebar, {
39
+ collapsible: "offcanvas",
40
+ children: [
41
+ /* @__PURE__ */ jsxs(SidebarHeader, {
42
+ className: "gap-1.5 border-b border-sidebar-border px-2 pb-2 pt-3",
43
+ children: [/* @__PURE__ */ jsxs("div", {
44
+ className: "flex min-w-0 items-center gap-2 px-1 py-1",
45
+ children: [brand ?? /* @__PURE__ */ jsx("span", {
46
+ className: "flex size-6 shrink-0 items-center justify-center rounded-md bg-foreground text-background",
47
+ children: /* @__PURE__ */ jsx(BotIcon, {
48
+ className: "size-3.5",
49
+ "aria-hidden": "true"
50
+ })
51
+ }), /* @__PURE__ */ jsx(Text, {
52
+ size: "label",
53
+ weight: "semibold",
54
+ className: "truncate",
55
+ title: appName,
56
+ children: appName
57
+ })]
58
+ }), onNewRun ? /* @__PURE__ */ jsx(SidebarMenu, { children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(SidebarMenuButton, {
59
+ size: "sm",
60
+ onClick: () => {
61
+ onNewRun();
62
+ setOpenMobile(false);
63
+ },
64
+ children: [/* @__PURE__ */ jsx(PlusIcon, {}), /* @__PURE__ */ jsx("span", { children: "New run" })]
65
+ }) }) }) : null]
66
+ }),
67
+ /* @__PURE__ */ jsx(SidebarContent, { children: /* @__PURE__ */ jsxs(SidebarGroup, {
68
+ className: "gap-1 pt-1",
69
+ children: [/* @__PURE__ */ jsx(SidebarGroupLabel, { children: "Runs" }), runs.length === 0 ? /* @__PURE__ */ jsx(Text, {
70
+ size: "caption",
71
+ tone: "muted",
72
+ className: "px-2",
73
+ children: "No runs yet"
74
+ }) : /* @__PURE__ */ jsx(SidebarMenu, { children: runs.map((run) => /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(SidebarMenuButton, {
75
+ size: "sm",
76
+ isActive: run.id === activeRunId,
77
+ onClick: () => {
78
+ onRunSelect?.(run);
79
+ setOpenMobile(false);
80
+ },
81
+ children: [
82
+ runStatusIcons[run.status],
83
+ /* @__PURE__ */ jsx("span", {
84
+ className: "truncate",
85
+ children: run.title
86
+ }),
87
+ /* @__PURE__ */ jsxs("span", {
88
+ className: "sr-only",
89
+ children: [", ", agentRunStatusLabels[run.status]]
90
+ })
91
+ ]
92
+ }) }, run.id)) })]
93
+ }) }),
94
+ sidebarFooter ? /* @__PURE__ */ jsx(SidebarFooter, { children: sidebarFooter }) : null,
95
+ /* @__PURE__ */ jsx(SidebarRail, {})
96
+ ]
97
+ });
98
+ }
99
+ //#endregion
100
+ export { AgentRunSidebar };
@@ -0,0 +1,32 @@
1
+ //#region src/blocks/agent-run-status.d.ts
2
+ export type AgentRunStatus = "pending" | "running" | "waiting" | "success" | "error" | "cancelled";
3
+ export type AgentRunSummary = {
4
+ id: string;
5
+ title: string;
6
+ status: AgentRunStatus;
7
+ };
8
+ export declare const agentRunStatusLabels: {
9
+ pending: string;
10
+ running: string;
11
+ waiting: string;
12
+ success: string;
13
+ error: string;
14
+ cancelled: string;
15
+ };
16
+ export declare const agentRunStatusColors: {
17
+ pending: "neutral";
18
+ running: "blue";
19
+ waiting: "yellow";
20
+ success: "green";
21
+ error: "red";
22
+ cancelled: "neutral";
23
+ };
24
+ export declare const agentRunStepLabels: {
25
+ neutral: string;
26
+ pending: string;
27
+ running: string;
28
+ waiting: string;
29
+ success: string;
30
+ error: string;
31
+ };
32
+ //#endregion
@@ -0,0 +1,27 @@
1
+ //#region src/blocks/agent-run-status.ts
2
+ const agentRunStatusLabels = {
3
+ pending: "Queued",
4
+ running: "Running",
5
+ waiting: "Needs approval",
6
+ success: "Done",
7
+ error: "Failed",
8
+ cancelled: "Cancelled"
9
+ };
10
+ const agentRunStatusColors = {
11
+ pending: "neutral",
12
+ running: "blue",
13
+ waiting: "yellow",
14
+ success: "green",
15
+ error: "red",
16
+ cancelled: "neutral"
17
+ };
18
+ const agentRunStepLabels = {
19
+ neutral: "Step",
20
+ pending: "Pending",
21
+ running: "Running",
22
+ waiting: "Waiting for approval",
23
+ success: "Done",
24
+ error: "Failed"
25
+ };
26
+ //#endregion
27
+ export { agentRunStatusColors, agentRunStatusLabels, agentRunStepLabels };
@@ -0,0 +1,80 @@
1
+ import { ActivityProps } from "../activity.js";
2
+ import { AgentRunStatus, AgentRunSummary } from "./agent-run-status.js";
3
+ import { Card } from "../ui/card.js";
4
+ import { SidebarProviderProps } from "../ui/sidebar-provider.js";
5
+ import "../ui/sidebar.js";
6
+ import { TimelineItemProps } from "../ui/timeline.js";
7
+ import { ComponentProps, ReactNode } from "react";
8
+ //#region src/blocks/agent-run.d.ts
9
+ type AgentRunLayout = "viewport" | "contained";
10
+ export type AgentRunBlockProps = Omit<ComponentProps<"div">, "children"> & Pick<SidebarProviderProps, "persistOpen" | "keyboardShortcut"> & {
11
+ appName?: string;
12
+ brand?: ReactNode;
13
+ runs: readonly AgentRunSummary[];
14
+ activeRunId?: string;
15
+ onRunSelect?: (run: AgentRunSummary) => void;
16
+ onNewRun?: () => void;
17
+ sidebarFooter?: ReactNode;
18
+ children: ReactNode;
19
+ layout?: AgentRunLayout;
20
+ };
21
+ export declare function AgentRunBlock({ appName, brand, runs, activeRunId, onRunSelect, onNewRun, sidebarFooter, children, layout, className, style, ...props }: AgentRunBlockProps): import("react").JSX.Element;
22
+ export type AgentRunElapsedProps = Omit<ComponentProps<"time">, "dateTime" | "children"> & {
23
+ startedAt: number;
24
+ endedAt?: number;
25
+ };
26
+ export declare function AgentRunElapsed({ startedAt, endedAt, className, ...props }: AgentRunElapsedProps): import("react").JSX.Element;
27
+ export type AgentRunHeaderProps = Omit<ComponentProps<"header">, "title"> & {
28
+ title: string;
29
+ status: AgentRunStatus;
30
+ startedAt?: number;
31
+ endedAt?: number;
32
+ actions?: ReactNode;
33
+ };
34
+ export declare function AgentRunHeader({ title, status, startedAt, endedAt, actions, className, ...props }: AgentRunHeaderProps): import("react").JSX.Element;
35
+ export type AgentRunBodyProps = ComponentProps<"div"> & {
36
+ aside?: ReactNode;
37
+ };
38
+ export declare function AgentRunBody({ aside, className, children, ...props }: AgentRunBodyProps): import("react").JSX.Element;
39
+ export type AgentRunPlanProps = Omit<ComponentProps<"section">, "title"> & {
40
+ title?: ReactNode;
41
+ description?: ReactNode;
42
+ };
43
+ export declare function AgentRunPlan({ title, description, className, children, ...props }: AgentRunPlanProps): import("react").JSX.Element;
44
+ export type AgentRunStepProps = Omit<TimelineItemProps, "title"> & {
45
+ title: ReactNode;
46
+ detail?: ReactNode;
47
+ startedAt?: number;
48
+ endedAt?: number;
49
+ actions?: ReactNode;
50
+ };
51
+ export declare function AgentRunStep({ title, detail, startedAt, endedAt, actions, state, children, ...props }: AgentRunStepProps): import("react").JSX.Element;
52
+ export type AgentRunToolCallProps = Omit<ActivityProps, "kind" | "name" | "children"> & {
53
+ name: string;
54
+ summary?: ReactNode;
55
+ icon?: ReactNode;
56
+ input?: unknown;
57
+ output?: unknown;
58
+ errorText?: string;
59
+ startedAt?: number;
60
+ endedAt?: number;
61
+ };
62
+ export declare function AgentRunToolCall({ name, summary, icon, input, output, errorText, startedAt, endedAt, ...props }: AgentRunToolCallProps): import("react").JSX.Element;
63
+ export type AgentRunAsideProps = ComponentProps<"aside">;
64
+ export declare function AgentRunAside({ className, ...props }: AgentRunAsideProps): import("react").JSX.Element;
65
+ export type AgentRunSectionProps = Omit<ComponentProps<typeof Card>, "title"> & {
66
+ title: ReactNode;
67
+ actions?: ReactNode;
68
+ };
69
+ export declare function AgentRunSection({ title, actions, children, ...props }: AgentRunSectionProps): import("react").JSX.Element;
70
+ export type AgentRunDetail = {
71
+ label: string;
72
+ value: ReactNode;
73
+ };
74
+ export type AgentRunDetailsProps = Omit<AgentRunSectionProps, "title" | "children"> & {
75
+ title?: ReactNode;
76
+ items: readonly AgentRunDetail[];
77
+ };
78
+ export declare function AgentRunDetails({ title, items, ...props }: AgentRunDetailsProps): import("react").JSX.Element | null;
79
+ //#endregion
80
+ export type { AgentRunStatus, AgentRunSummary };
@@ -0,0 +1,254 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { LiveStatus } from "../ui/live-status.js";
4
+ import { Activity, ActivityContent, ActivityDetail, ActivityDetailContent, ActivityDetailLabel, ActivityIcon, ActivityRow, ActivityStatus, ActivityTitle, ActivityTrigger } from "../activity.js";
5
+ import { Heading, Text } from "../ui/typography.js";
6
+ import { Badge } from "../ui/badge.js";
7
+ import { elapsedIsoDuration, formatElapsed, useRunClock } from "./agent-run-clock.js";
8
+ import { SidebarProvider } from "../ui/sidebar-provider.js";
9
+ import { SidebarInset, SidebarTrigger } from "../ui/sidebar.js";
10
+ import { agentRunStatusColors, agentRunStatusLabels, agentRunStepLabels } from "./agent-run-status.js";
11
+ import { AgentRunSidebar } from "./agent-run-sidebar.js";
12
+ import { Card, CardAction, CardContent, CardHeader } from "../ui/card.js";
13
+ import { Timeline, TimelineContent, TimelineDescription, TimelineIndicator, TimelineItem, TimelineMeta, TimelineSeparator, TimelineTitle } from "../ui/timeline.js";
14
+ import { createContext, useContext, useId } from "react";
15
+ import { jsx, jsxs } from "react/jsx-runtime";
16
+ import { Check, Circle, CircleAlert, CircleDashed, CirclePause, LoaderCircle } from "lucide-react";
17
+ //#region src/blocks/agent-run.tsx
18
+ const AgentRunLayoutContext = createContext("viewport");
19
+ function useHeadingLevels() {
20
+ return useContext(AgentRunLayoutContext) === "contained" ? {
21
+ title: 2,
22
+ section: 3
23
+ } : {
24
+ title: 1,
25
+ section: 2
26
+ };
27
+ }
28
+ function AgentRunBlock({ appName = "Agents", brand, runs, activeRunId, onRunSelect, onNewRun, sidebarFooter, children, layout = "viewport", className, style, ...props }) {
29
+ const providerStyle = {
30
+ "--sidebar-width": "16rem",
31
+ ...style
32
+ };
33
+ return /* @__PURE__ */ jsxs(SidebarProvider, {
34
+ layout,
35
+ className: cn("bg-background text-foreground", layout === "viewport" && "h-svh overflow-hidden", className),
36
+ style: providerStyle,
37
+ ...props,
38
+ children: [/* @__PURE__ */ jsx(AgentRunSidebar, {
39
+ appName,
40
+ brand,
41
+ runs,
42
+ activeRunId,
43
+ sidebarFooter,
44
+ onRunSelect,
45
+ onNewRun
46
+ }), /* @__PURE__ */ jsx(SidebarInset, {
47
+ className: "@container/agent-run flex h-full min-h-0 min-w-0 flex-col overflow-hidden",
48
+ children: /* @__PURE__ */ jsx(AgentRunLayoutContext.Provider, {
49
+ value: layout,
50
+ children
51
+ })
52
+ })]
53
+ });
54
+ }
55
+ function AgentRunElapsed({ startedAt, endedAt, className, ...props }) {
56
+ const now = useRunClock(endedAt === void 0);
57
+ const end = endedAt ?? now;
58
+ const ms = end === null ? null : Math.max(0, end - startedAt);
59
+ return /* @__PURE__ */ jsx("time", {
60
+ dateTime: ms === null ? void 0 : elapsedIsoDuration(ms),
61
+ className: cn("text-label tabular-nums text-muted-foreground", className),
62
+ ...props,
63
+ children: ms === null ? "" : formatElapsed(ms)
64
+ });
65
+ }
66
+ function AgentRunHeader({ title, status, startedAt, endedAt, actions, className, ...props }) {
67
+ const levels = useHeadingLevels();
68
+ return /* @__PURE__ */ jsxs("header", {
69
+ className: cn("flex h-12 shrink-0 items-center gap-2 border-b border-border/70 px-3", className),
70
+ ...props,
71
+ children: [
72
+ /* @__PURE__ */ jsx(SidebarTrigger, {
73
+ size: "xs",
74
+ className: "lg:hidden"
75
+ }),
76
+ /* @__PURE__ */ jsx(Heading, {
77
+ level: levels.title,
78
+ size: "label",
79
+ weight: "medium",
80
+ className: "min-w-0 truncate",
81
+ title,
82
+ children: title
83
+ }),
84
+ /* @__PURE__ */ jsx(Badge, {
85
+ size: "sm",
86
+ color: agentRunStatusColors[status],
87
+ children: agentRunStatusLabels[status]
88
+ }),
89
+ startedAt === void 0 ? null : /* @__PURE__ */ jsx(AgentRunElapsed, {
90
+ startedAt,
91
+ endedAt,
92
+ className: "text-body"
93
+ }),
94
+ /* @__PURE__ */ jsx("div", {
95
+ className: "ms-auto flex shrink-0 items-center gap-1",
96
+ children: actions
97
+ }),
98
+ /* @__PURE__ */ jsx(LiveStatus, { message: `${title}: ${agentRunStatusLabels[status]}` })
99
+ ]
100
+ });
101
+ }
102
+ function AgentRunBody({ aside, className, children, ...props }) {
103
+ return /* @__PURE__ */ jsxs("div", {
104
+ className: cn("grid min-h-0 flex-1 content-start overflow-y-auto @3xl/agent-run:grid-cols-[minmax(0,1fr)_minmax(18rem,22rem)] @3xl/agent-run:content-stretch @3xl/agent-run:overflow-hidden", className),
105
+ ...props,
106
+ children: [/* @__PURE__ */ jsx("div", {
107
+ className: "min-w-0 px-4 py-5 @lg/agent-run:px-6 @3xl/agent-run:overflow-y-auto",
108
+ children
109
+ }), aside]
110
+ });
111
+ }
112
+ function AgentRunPlan({ title = "Plan", description, className, children, ...props }) {
113
+ const levels = useHeadingLevels();
114
+ const headingId = useId();
115
+ return /* @__PURE__ */ jsxs("section", {
116
+ "aria-labelledby": headingId,
117
+ className: cn("min-w-0", className),
118
+ ...props,
119
+ children: [
120
+ /* @__PURE__ */ jsx(Heading, {
121
+ id: headingId,
122
+ level: levels.section,
123
+ size: "label",
124
+ weight: "medium",
125
+ children: title
126
+ }),
127
+ description ? /* @__PURE__ */ jsx(Text, {
128
+ size: "caption",
129
+ tone: "muted",
130
+ children: description
131
+ }) : null,
132
+ /* @__PURE__ */ jsx(Timeline, {
133
+ className: "mt-3",
134
+ children
135
+ })
136
+ ]
137
+ });
138
+ }
139
+ const stepIcons = {
140
+ neutral: /* @__PURE__ */ jsx(Circle, { "aria-hidden": "true" }),
141
+ pending: /* @__PURE__ */ jsx(CircleDashed, { "aria-hidden": "true" }),
142
+ running: /* @__PURE__ */ jsx(LoaderCircle, {
143
+ className: "motion-safe:animate-spin",
144
+ "aria-hidden": "true"
145
+ }),
146
+ waiting: /* @__PURE__ */ jsx(CirclePause, { "aria-hidden": "true" }),
147
+ success: /* @__PURE__ */ jsx(Check, { "aria-hidden": "true" }),
148
+ error: /* @__PURE__ */ jsx(CircleAlert, { "aria-hidden": "true" })
149
+ };
150
+ function AgentRunStep({ title, detail, startedAt, endedAt, actions, state = "pending", children, ...props }) {
151
+ const hasMeta = startedAt !== void 0 || actions !== void 0;
152
+ return /* @__PURE__ */ jsxs(TimelineItem, {
153
+ state,
154
+ ...props,
155
+ children: [
156
+ /* @__PURE__ */ jsx(TimelineIndicator, {
157
+ "aria-label": agentRunStepLabels[state],
158
+ children: stepIcons[state]
159
+ }),
160
+ /* @__PURE__ */ jsx(TimelineSeparator, {}),
161
+ /* @__PURE__ */ jsxs(TimelineContent, { children: [
162
+ /* @__PURE__ */ jsx(TimelineTitle, { children: title }),
163
+ detail ? /* @__PURE__ */ jsx(TimelineDescription, { children: detail }) : null,
164
+ hasMeta ? /* @__PURE__ */ jsxs(TimelineMeta, { children: [startedAt === void 0 ? null : /* @__PURE__ */ jsx(AgentRunElapsed, {
165
+ startedAt,
166
+ endedAt
167
+ }), actions] }) : null,
168
+ children ? /* @__PURE__ */ jsx("div", {
169
+ className: "mt-2 grid gap-2",
170
+ children
171
+ }) : null
172
+ ] })
173
+ ]
174
+ });
175
+ }
176
+ function formatToolValue(value) {
177
+ return typeof value === "string" ? value : JSON.stringify(value, null, 2);
178
+ }
179
+ function AgentRunToolCall({ name, summary, icon, input, output, errorText, startedAt, endedAt, ...props }) {
180
+ const hasDetails = input !== void 0 || output !== void 0 || errorText !== void 0;
181
+ const Row = hasDetails ? ActivityTrigger : ActivityRow;
182
+ return /* @__PURE__ */ jsxs(Activity, {
183
+ kind: "tool",
184
+ name,
185
+ ...props,
186
+ children: [/* @__PURE__ */ jsxs(Row, { children: [
187
+ icon ? /* @__PURE__ */ jsx(ActivityIcon, { children: icon }) : /* @__PURE__ */ jsx(ActivityIcon, {}),
188
+ /* @__PURE__ */ jsxs(ActivityTitle, { children: [name, summary ? /* @__PURE__ */ jsxs("span", {
189
+ className: "text-muted-foreground",
190
+ children: [" · ", summary]
191
+ }) : null] }),
192
+ startedAt === void 0 ? null : /* @__PURE__ */ jsx(AgentRunElapsed, {
193
+ startedAt,
194
+ endedAt,
195
+ className: "ms-2"
196
+ }),
197
+ /* @__PURE__ */ jsx(ActivityStatus, { className: "sr-only" })
198
+ ] }), hasDetails ? /* @__PURE__ */ jsxs(ActivityContent, { children: [
199
+ input === void 0 ? null : /* @__PURE__ */ jsxs(ActivityDetail, { children: [/* @__PURE__ */ jsx(ActivityDetailLabel, { children: "Input" }), /* @__PURE__ */ jsx(ActivityDetailContent, {
200
+ format: "code",
201
+ children: formatToolValue(input)
202
+ })] }),
203
+ output === void 0 ? null : /* @__PURE__ */ jsxs(ActivityDetail, { children: [/* @__PURE__ */ jsx(ActivityDetailLabel, { children: "Output" }), /* @__PURE__ */ jsx(ActivityDetailContent, {
204
+ format: "code",
205
+ children: formatToolValue(output)
206
+ })] }),
207
+ errorText === void 0 ? null : /* @__PURE__ */ jsxs(ActivityDetail, { children: [/* @__PURE__ */ jsx(ActivityDetailLabel, { children: "Error" }), /* @__PURE__ */ jsx(ActivityDetailContent, {
208
+ className: "text-destructive-text",
209
+ children: errorText
210
+ })] })
211
+ ] }) : null]
212
+ });
213
+ }
214
+ function AgentRunAside({ className, ...props }) {
215
+ return /* @__PURE__ */ jsx("aside", {
216
+ "aria-label": "Run output",
217
+ className: cn("grid content-start gap-4 border-t border-border/70 px-4 py-5 @lg/agent-run:px-6 @3xl/agent-run:overflow-y-auto @3xl/agent-run:border-t-0 @3xl/agent-run:border-s", className),
218
+ ...props
219
+ });
220
+ }
221
+ function AgentRunSection({ title, actions, children, ...props }) {
222
+ const levels = useHeadingLevels();
223
+ return /* @__PURE__ */ jsxs(Card, {
224
+ ...props,
225
+ children: [/* @__PURE__ */ jsxs(CardHeader, { children: [/* @__PURE__ */ jsx(Heading, {
226
+ level: levels.section,
227
+ size: "label",
228
+ weight: "medium",
229
+ children: title
230
+ }), actions ? /* @__PURE__ */ jsx(CardAction, { children: actions }) : null] }), /* @__PURE__ */ jsx(CardContent, { children })]
231
+ });
232
+ }
233
+ function AgentRunDetails({ title = "Details", items, ...props }) {
234
+ if (items.length === 0) return null;
235
+ return /* @__PURE__ */ jsx(AgentRunSection, {
236
+ title,
237
+ ...props,
238
+ children: /* @__PURE__ */ jsx("dl", {
239
+ className: "grid grid-cols-[auto_minmax(0,1fr)] gap-x-4 gap-y-2 text-body",
240
+ children: items.map((item) => /* @__PURE__ */ jsxs("div", {
241
+ className: "contents",
242
+ children: [/* @__PURE__ */ jsx("dt", {
243
+ className: "text-muted-foreground",
244
+ children: item.label
245
+ }), /* @__PURE__ */ jsx("dd", {
246
+ className: "min-w-0 break-words",
247
+ children: item.value
248
+ })]
249
+ }, item.label))
250
+ })
251
+ });
252
+ }
253
+ //#endregion
254
+ export { AgentRunAside, AgentRunBlock, AgentRunBody, AgentRunDetails, AgentRunElapsed, AgentRunHeader, AgentRunPlan, AgentRunSection, AgentRunStep, AgentRunToolCall };