@ctrl-ui/react 0.9.5 → 0.9.8

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 (165) hide show
  1. package/README.md +38 -2
  2. package/dist/action-bar.js +16 -8
  3. package/dist/activity.d.ts +4 -2
  4. package/dist/activity.js +5 -2
  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 +101 -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 +82 -0
  13. package/dist/blocks/agent-run.js +262 -0
  14. package/dist/blocks/coding-agent.d.ts +1 -1
  15. package/dist/blocks/coding-agent.js +50 -69
  16. package/dist/blocks/design-canvas-data.d.ts +1 -1
  17. package/dist/blocks/design-canvas-inspector.js +1 -1
  18. package/dist/blocks/design-canvas.d.ts +2 -1
  19. package/dist/blocks/design-canvas.js +56 -42
  20. package/dist/blocks/discussion.js +1 -1
  21. package/dist/blocks/file-explorer.js +66 -47
  22. package/dist/blocks/settings.js +37 -41
  23. package/dist/blocks/team-chat.js +2 -1
  24. package/dist/blocks/theme-toggle.d.ts +2 -1
  25. package/dist/blocks/theme-toggle.js +13 -4
  26. package/dist/chat-message.js +33 -39
  27. package/dist/hooks/use-mobile.d.ts +1 -1
  28. package/dist/hooks/use-mobile.js +1 -1
  29. package/dist/knob-contracts/badge-knobs.d.ts +1 -1
  30. package/dist/knob-contracts/badge-knobs.js +3 -1
  31. package/dist/knob-contracts/button-knobs.d.ts +1 -1
  32. package/dist/knob-contracts/button-knobs.js +1 -0
  33. package/dist/knob-contracts/calendar-knobs.d.ts +1 -1
  34. package/dist/knob-contracts/calendar-knobs.js +9 -1
  35. package/dist/knob-contracts/card-knobs.d.ts +1 -1
  36. package/dist/knob-contracts/card-knobs.js +5 -1
  37. package/dist/knob-contracts/choice-knobs.d.ts +1 -1
  38. package/dist/knob-contracts/choice-knobs.js +1 -0
  39. package/dist/knob-contracts/collapsible-knobs.d.ts +1 -1
  40. package/dist/knob-contracts/collapsible-knobs.js +5 -1
  41. package/dist/knob-contracts/emoji-picker-knobs.d.ts +1 -1
  42. package/dist/knob-contracts/emoji-picker-knobs.js +3 -1
  43. package/dist/knob-contracts/field-knobs.d.ts +1 -1
  44. package/dist/knob-contracts/field-knobs.js +17 -1
  45. package/dist/knob-contracts/infinite-canvas-knobs.d.ts +1 -1
  46. package/dist/knob-contracts/infinite-canvas-knobs.js +1 -7
  47. package/dist/knob-contracts/item-knobs.d.ts +1 -1
  48. package/dist/knob-contracts/item-knobs.js +5 -1
  49. package/dist/knob-contracts/kbd-knobs.d.ts +1 -1
  50. package/dist/knob-contracts/kbd-knobs.js +4 -1
  51. package/dist/knob-contracts/page-layout-knobs.d.ts +1 -1
  52. package/dist/knob-contracts/page-layout-knobs.js +1 -0
  53. package/dist/knob-contracts/pagination-knobs.d.ts +1 -1
  54. package/dist/knob-contracts/pagination-knobs.js +2 -1
  55. package/dist/knob-contracts/popup-knobs.d.ts +1 -1
  56. package/dist/knob-contracts/popup-knobs.js +9 -0
  57. package/dist/knob-contracts/range-knobs.d.ts +1 -1
  58. package/dist/knob-contracts/range-knobs.js +2 -0
  59. package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
  60. package/dist/knob-contracts/sidebar-knobs.js +2 -0
  61. package/dist/knob-contracts/swap-knobs.d.ts +4 -0
  62. package/dist/knob-contracts/swap-knobs.js +9 -0
  63. package/dist/knob-contracts/tabs-knobs.d.ts +1 -1
  64. package/dist/knob-contracts/tabs-knobs.js +3 -0
  65. package/dist/knob-contracts/timeline-knobs.d.ts +1 -1
  66. package/dist/knob-contracts/timeline-knobs.js +5 -1
  67. package/dist/knob-contracts/tool-approval-knobs.d.ts +4 -0
  68. package/dist/knob-contracts/tool-approval-knobs.js +32 -0
  69. package/dist/knob-contracts/tree-knobs.d.ts +1 -1
  70. package/dist/knob-contracts/tree-knobs.js +3 -1
  71. package/dist/lib/infinite-canvas-camera-store.d.ts +65 -0
  72. package/dist/lib/infinite-canvas-camera-store.js +233 -0
  73. package/dist/lib/infinite-canvas-camera.d.ts +33 -0
  74. package/dist/lib/infinite-canvas-camera.js +98 -0
  75. package/dist/lib/infinite-canvas-navigation.d.ts +19 -0
  76. package/dist/lib/infinite-canvas-navigation.js +395 -0
  77. package/dist/markdown-editor/bubble-menu.d.ts +6 -0
  78. package/dist/markdown-editor/bubble-menu.js +45 -0
  79. package/dist/markdown-editor/toolbar.d.ts +17 -1
  80. package/dist/markdown-editor/toolbar.js +103 -81
  81. package/dist/skin.d.ts +2 -0
  82. package/dist/styles/index.css +11 -10
  83. package/dist/styles/recipes/activity.css +12 -0
  84. package/dist/styles/recipes/avatar.css +1 -1
  85. package/dist/styles/recipes/badge.css +17 -0
  86. package/dist/styles/recipes/button-group.css +12 -6
  87. package/dist/styles/recipes/button.css +20 -3
  88. package/dist/styles/recipes/calendar.css +82 -6
  89. package/dist/styles/recipes/card.css +40 -5
  90. package/dist/styles/recipes/chat-message-edit.css +6 -21
  91. package/dist/styles/recipes/choice.css +9 -2
  92. package/dist/styles/recipes/collapsible.css +8 -1
  93. package/dist/styles/recipes/color-picker-values.css +4 -0
  94. package/dist/styles/recipes/emoji-picker.css +29 -8
  95. package/dist/styles/recipes/field-parts.css +80 -5
  96. package/dist/styles/recipes/field.css +113 -40
  97. package/dist/styles/recipes/infinite-canvas.css +17 -57
  98. package/dist/styles/recipes/item.css +33 -5
  99. package/dist/styles/recipes/kbd.css +26 -6
  100. package/dist/styles/recipes/markdown-editor.css +12 -0
  101. package/dist/styles/recipes/morphing-panel.css +1 -1
  102. package/dist/styles/recipes/page-layout.css +18 -2
  103. package/dist/styles/recipes/pagination.css +8 -0
  104. package/dist/styles/recipes/popup-controls.css +9 -0
  105. package/dist/styles/recipes/popup-drawer.css +1 -0
  106. package/dist/styles/recipes/popup-lists.css +55 -6
  107. package/dist/styles/recipes/popup-motion.css +7 -1
  108. package/dist/styles/recipes/popup.css +10 -0
  109. package/dist/styles/recipes/range.css +16 -2
  110. package/dist/styles/recipes/sidebar-mobile.css +1 -1
  111. package/dist/styles/recipes/sidebar-parts.css +17 -1
  112. package/dist/styles/recipes/sidebar.css +11 -0
  113. package/dist/styles/recipes/swap.css +143 -0
  114. package/dist/styles/recipes/tabs.css +41 -8
  115. package/dist/styles/recipes/timeline.css +36 -2
  116. package/dist/styles/recipes/tool-approval.css +267 -0
  117. package/dist/styles/recipes/track-highlight.css +19 -6
  118. package/dist/styles/recipes/tree.css +33 -0
  119. package/dist/styles/theme.css +17 -6
  120. package/dist/tool-approval.d.ts +49 -0
  121. package/dist/tool-approval.js +260 -0
  122. package/dist/ui/alert-dialog.js +1 -1
  123. package/dist/ui/badge.js +1 -1
  124. package/dist/ui/calendar.js +9 -4
  125. package/dist/ui/card.d.ts +3 -1
  126. package/dist/ui/card.js +2 -1
  127. package/dist/ui/code.js +13 -3
  128. package/dist/ui/command.js +1 -0
  129. package/dist/ui/dialog.js +1 -1
  130. package/dist/ui/dropdown-menu.d.ts +1 -1
  131. package/dist/ui/dropdown-menu.js +23 -6
  132. package/dist/ui/dropzone.js +10 -11
  133. package/dist/ui/field.d.ts +6 -2
  134. package/dist/ui/field.js +4 -2
  135. package/dist/ui/infinite-canvas.d.ts +19 -22
  136. package/dist/ui/infinite-canvas.js +264 -325
  137. package/dist/ui/morphing-panel.d.ts +5 -1
  138. package/dist/ui/morphing-panel.js +4 -2
  139. package/dist/ui/rich-tooltip.d.ts +2 -1
  140. package/dist/ui/rich-tooltip.js +32 -29
  141. package/dist/ui/select.js +2 -2
  142. package/dist/ui/sidebar-mobile.js +1 -1
  143. package/dist/ui/sidebar-provider.d.ts +3 -1
  144. package/dist/ui/sidebar-provider.js +3 -2
  145. package/dist/ui/sidebar-resize-rail.js +1 -1
  146. package/dist/ui/sidebar.d.ts +6 -5
  147. package/dist/ui/sidebar.js +10 -9
  148. package/dist/ui/swap.d.ts +14 -0
  149. package/dist/ui/swap.js +69 -0
  150. package/dist/ui/tabs.d.ts +1 -1
  151. package/dist/ui/tabs.js +2 -13
  152. package/dist/ui/timeline.d.ts +1 -1
  153. package/dist/ui/timeline.js +2 -2
  154. package/dist/ui/typography.d.ts +11 -5
  155. package/dist/ui/typography.js +18 -6
  156. package/dist/user-ask.js +1 -1
  157. package/lint/biome-preset.json +3 -0
  158. package/lint/class-policy.mjs +17 -0
  159. package/lint/eslint-plugin.mjs +87 -0
  160. package/lint/grit-rules.mjs +40 -0
  161. package/lint/no-component-style-classes.grit +16 -0
  162. package/lint/no-raw-type-classes.grit +3 -4
  163. package/lint/write-grit-rules.mjs +19 -0
  164. package/package.json +12 -3
  165. package/dist/styles/type-scale.css +0 -72
package/README.md CHANGED
@@ -47,10 +47,46 @@ 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
+ ## Lint rules
51
+
52
+ Two rules keep call sites honest, shipped for Biome, ESLint, and oxlint from one policy (`lint/class-policy.mjs`):
53
+
54
+ - `no-component-style-classes` errors when `className` on a component imported from `@ctrl-ui/react` (or a `components/control-ui` directory) sets what the component owns: colors, type, padding, borders, radius, shadows, state styles such as `hover:` and `data-[…]:`, or `!important`. Layout classes (`flex`, `gap-*`, widths, heights, margins, placement) stay allowed. Restyle through the component's props or its `--cui-*` knobs instead.
55
+ - `no-raw-type-classes` errors on text sizes in any `className`: the type rungs, Tailwind's `text-xs`…`text-9xl`, and arbitrary `text-[13px]`. It reaches beyond Control UI, so adopt it folder by folder while you migrate.
56
+
57
+ Biome, extending the shipped preset so rules added later arrive with the package update:
58
+
59
+ ```json
60
+ {
61
+ "extends": ["@ctrl-ui/react/biome"],
62
+ "overrides": [{ "includes": ["src/features/**"], "plugins": ["./node_modules/@ctrl-ui/react/lint/no-raw-type-classes.grit"] }]
63
+ }
64
+ ```
65
+
66
+ ESLint flat config:
67
+
68
+ ```js
69
+ import controlUi from "@ctrl-ui/react/eslint-plugin";
70
+
71
+ export default [{ plugins: { "control-ui": controlUi }, rules: { "control-ui/no-component-style-classes": "error" } }];
72
+ ```
73
+
74
+ oxlint, through its JS plugin support:
75
+
76
+ ```json
77
+ { "jsPlugins": ["@ctrl-ui/react/eslint-plugin"], "rules": { "control-ui/no-component-style-classes": "error" } }
78
+ ```
79
+
80
+ When your app imports components through its own wrapper package or folder, declare that path so the rule follows those imports too:
81
+
82
+ ```js
83
+ rules: { "control-ui/no-component-style-classes": ["error", { importSources: ["@acme/ui/components/"] }] }
84
+ ```
85
+
86
+ Biome's Grit plugins take no options, so the package ships `control-ui-grit` to write the rules with your paths. Run it on install so a package update regenerates them, gitignore `.control-ui`, and list `./.control-ui/no-component-style-classes.grit` under `plugins` in place of the preset:
51
87
 
52
88
  ```json
53
- { "plugins": ["./node_modules/@ctrl-ui/react/lint/no-raw-type-classes.grit"] }
89
+ { "scripts": { "postinstall": "control-ui-grit --import-source=@acme/ui/components/ --out=.control-ui" } }
54
90
  ```
55
91
 
56
92
  ## 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"> & {
@@ -45,8 +45,10 @@ export type ActivityDetailProps = ComponentProps<"div"> & {
45
45
  export declare function ActivityDetail({ className, ...props }: ActivityDetailProps): import("react").JSX.Element;
46
46
  export type ActivityDetailLabelProps = ActivityStyleProps<ComponentProps<"div">, ActivityKnobStyle>;
47
47
  export declare function ActivityDetailLabel({ className, ...props }: ActivityDetailLabelProps): import("react").JSX.Element;
48
+ export type ActivityDetailTone = "default" | "destructive";
48
49
  export type ActivityDetailContentProps = ActivityStyleProps<ComponentProps<"div">, ActivityKnobStyle> & {
49
50
  format?: ActivityDetailFormat;
51
+ tone?: ActivityDetailTone;
50
52
  };
51
- export declare function ActivityDetailContent({ format, className, ...props }: ActivityDetailContentProps): import("react").JSX.Element;
53
+ export declare function ActivityDetailContent({ format, tone, className, ...props }: ActivityDetailContentProps): import("react").JSX.Element;
52
54
  //#endregion
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
  };
@@ -166,12 +168,13 @@ function ActivityDetailLabel({ className, ...props }) {
166
168
  className: cn("uppercase", className)
167
169
  });
168
170
  }
169
- function ActivityDetailContent({ format = "text", className, ...props }) {
171
+ function ActivityDetailContent({ format = "text", tone = "default", className, ...props }) {
170
172
  return /* @__PURE__ */ jsx("div", {
171
173
  "data-control-ui": "activity",
172
174
  "data-control-family": "activity",
173
175
  "data-slot": "detail-content",
174
176
  "data-format": format,
177
+ "data-tone": tone,
175
178
  ...props,
176
179
  className: cn("min-w-0 whitespace-pre-wrap wrap-anywhere", format === "code" && "w-fit max-w-full", className)
177
180
  });
@@ -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,101 @@
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, CheckIcon, CircleAlertIcon, CircleDashedIcon, CirclePauseIcon, CircleSlashIcon, LoaderCircleIcon, PlusIcon } from "lucide-react";
9
+ //#region src/blocks/agent-run-sidebar.tsx
10
+ const runStatusIcons = {
11
+ running: /* @__PURE__ */ jsx(LoaderCircleIcon, {
12
+ className: "size-3.5 shrink-0 motion-safe:animate-spin",
13
+ "aria-hidden": "true"
14
+ }),
15
+ waiting: /* @__PURE__ */ jsx(CirclePauseIcon, {
16
+ className: "size-3.5 shrink-0 text-warning-text",
17
+ "aria-hidden": "true"
18
+ }),
19
+ success: /* @__PURE__ */ jsx(CheckIcon, {
20
+ className: "size-3.5 shrink-0 text-success-text",
21
+ "aria-hidden": "true"
22
+ }),
23
+ error: /* @__PURE__ */ jsx(CircleAlertIcon, {
24
+ className: "size-3.5 shrink-0 text-destructive-text",
25
+ "aria-hidden": "true"
26
+ }),
27
+ cancelled: /* @__PURE__ */ jsx(CircleSlashIcon, {
28
+ className: "size-3.5 shrink-0 text-muted-foreground",
29
+ "aria-hidden": "true"
30
+ }),
31
+ pending: /* @__PURE__ */ jsx(CircleDashedIcon, {
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
+ divider: true,
43
+ className: "gap-1.5",
44
+ children: [/* @__PURE__ */ jsxs("div", {
45
+ className: "flex min-w-0 items-center gap-2 px-1 py-1",
46
+ children: [brand ?? /* @__PURE__ */ jsx("span", {
47
+ className: "flex size-6 shrink-0 items-center justify-center rounded-md bg-foreground text-background",
48
+ children: /* @__PURE__ */ jsx(BotIcon, {
49
+ className: "size-3.5",
50
+ "aria-hidden": "true"
51
+ })
52
+ }), /* @__PURE__ */ jsx(Text, {
53
+ size: "label",
54
+ weight: "semibold",
55
+ className: "truncate",
56
+ title: appName,
57
+ children: appName
58
+ })]
59
+ }), onNewRun ? /* @__PURE__ */ jsx(SidebarMenu, { children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(SidebarMenuButton, {
60
+ size: "sm",
61
+ onClick: () => {
62
+ onNewRun();
63
+ setOpenMobile(false);
64
+ },
65
+ children: [/* @__PURE__ */ jsx(PlusIcon, {}), /* @__PURE__ */ jsx("span", { children: "New run" })]
66
+ }) }) }) : null]
67
+ }),
68
+ /* @__PURE__ */ jsx(SidebarContent, { children: /* @__PURE__ */ jsxs(SidebarGroup, {
69
+ className: "gap-1",
70
+ children: [/* @__PURE__ */ jsx(SidebarGroupLabel, { children: "Runs" }), runs.length === 0 ? /* @__PURE__ */ jsx(Text, {
71
+ size: "caption",
72
+ tone: "muted",
73
+ className: "mx-2",
74
+ children: "No runs yet"
75
+ }) : /* @__PURE__ */ jsx(SidebarMenu, { children: runs.map((run) => /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(SidebarMenuButton, {
76
+ size: "sm",
77
+ isActive: run.id === activeRunId,
78
+ onClick: () => {
79
+ onRunSelect?.(run);
80
+ setOpenMobile(false);
81
+ },
82
+ children: [
83
+ runStatusIcons[run.status],
84
+ /* @__PURE__ */ jsx("span", {
85
+ className: "truncate",
86
+ children: run.title
87
+ }),
88
+ /* @__PURE__ */ jsxs("span", {
89
+ className: "sr-only",
90
+ children: [", ", agentRunStatusLabels[run.status]]
91
+ })
92
+ ]
93
+ }) }, run.id)) })]
94
+ }) }),
95
+ sidebarFooter ? /* @__PURE__ */ jsx(SidebarFooter, { children: sidebarFooter }) : null,
96
+ /* @__PURE__ */ jsx(SidebarRail, {})
97
+ ]
98
+ });
99
+ }
100
+ //#endregion
101
+ 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,82 @@
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 { TypeSize } from "../ui/typography.js";
8
+ import { ComponentProps, ReactNode } from "react";
9
+ //#region src/blocks/agent-run.d.ts
10
+ type AgentRunLayout = "viewport" | "contained";
11
+ export type AgentRunBlockProps = Omit<ComponentProps<"div">, "children"> & Pick<SidebarProviderProps, "persistOpen" | "keyboardShortcut"> & {
12
+ appName?: string;
13
+ brand?: ReactNode;
14
+ runs: readonly AgentRunSummary[];
15
+ activeRunId?: string;
16
+ onRunSelect?: (run: AgentRunSummary) => void;
17
+ onNewRun?: () => void;
18
+ sidebarFooter?: ReactNode;
19
+ children: ReactNode;
20
+ layout?: AgentRunLayout;
21
+ };
22
+ export declare function AgentRunBlock({ appName, brand, runs, activeRunId, onRunSelect, onNewRun, sidebarFooter, children, layout, className, style, ...props }: AgentRunBlockProps): import("react").JSX.Element;
23
+ export type AgentRunElapsedProps = Omit<ComponentProps<"time">, "dateTime" | "children"> & {
24
+ startedAt: number;
25
+ endedAt?: number;
26
+ size?: TypeSize;
27
+ };
28
+ export declare function AgentRunElapsed({ startedAt, endedAt, size, className, ...props }: AgentRunElapsedProps): import("react").JSX.Element;
29
+ export type AgentRunHeaderProps = Omit<ComponentProps<"header">, "title"> & {
30
+ title: string;
31
+ status: AgentRunStatus;
32
+ startedAt?: number;
33
+ endedAt?: number;
34
+ actions?: ReactNode;
35
+ };
36
+ export declare function AgentRunHeader({ title, status, startedAt, endedAt, actions, className, ...props }: AgentRunHeaderProps): import("react").JSX.Element;
37
+ export type AgentRunBodyProps = ComponentProps<"div"> & {
38
+ aside?: ReactNode;
39
+ };
40
+ export declare function AgentRunBody({ aside, className, children, ...props }: AgentRunBodyProps): import("react").JSX.Element;
41
+ export type AgentRunPlanProps = Omit<ComponentProps<"section">, "title"> & {
42
+ title?: ReactNode;
43
+ description?: ReactNode;
44
+ };
45
+ export declare function AgentRunPlan({ title, description, className, children, ...props }: AgentRunPlanProps): import("react").JSX.Element;
46
+ export type AgentRunStepProps = Omit<TimelineItemProps, "title"> & {
47
+ title: ReactNode;
48
+ detail?: ReactNode;
49
+ startedAt?: number;
50
+ endedAt?: number;
51
+ actions?: ReactNode;
52
+ };
53
+ export declare function AgentRunStep({ title, detail, startedAt, endedAt, actions, state, children, ...props }: AgentRunStepProps): import("react").JSX.Element;
54
+ export type AgentRunToolCallProps = Omit<ActivityProps, "kind" | "name" | "children"> & {
55
+ name: string;
56
+ summary?: ReactNode;
57
+ icon?: ReactNode;
58
+ input?: unknown;
59
+ output?: unknown;
60
+ errorText?: string;
61
+ startedAt?: number;
62
+ endedAt?: number;
63
+ };
64
+ export declare function AgentRunToolCall({ name, summary, icon, input, output, errorText, startedAt, endedAt, ...props }: AgentRunToolCallProps): import("react").JSX.Element;
65
+ export type AgentRunAsideProps = ComponentProps<"aside">;
66
+ export declare function AgentRunAside({ className, ...props }: AgentRunAsideProps): import("react").JSX.Element;
67
+ export type AgentRunSectionProps = Omit<ComponentProps<typeof Card>, "title"> & {
68
+ title: ReactNode;
69
+ actions?: ReactNode;
70
+ };
71
+ export declare function AgentRunSection({ title, actions, children, ...props }: AgentRunSectionProps): import("react").JSX.Element;
72
+ export type AgentRunDetail = {
73
+ label: string;
74
+ value: ReactNode;
75
+ };
76
+ export type AgentRunDetailsProps = Omit<AgentRunSectionProps, "title" | "children"> & {
77
+ title?: ReactNode;
78
+ items: readonly AgentRunDetail[];
79
+ };
80
+ export declare function AgentRunDetails({ title, items, ...props }: AgentRunDetailsProps): import("react").JSX.Element | null;
81
+ //#endregion
82
+ export type { AgentRunStatus, AgentRunSummary };