harebear-ui 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/components/admin/app-shell.d.ts +4 -1
  2. package/dist/components/admin/app-shell.js +49 -46
  3. package/dist/components/admin/confirm-dialog.d.ts +17 -0
  4. package/dist/components/admin/confirm-dialog.js +20 -0
  5. package/dist/components/admin/copy-field.d.ts +17 -0
  6. package/dist/components/admin/copy-field.js +37 -0
  7. package/dist/components/admin/search-input.js +1 -1
  8. package/dist/components/admin/status.d.ts +12 -6
  9. package/dist/components/admin/status.js +8 -5
  10. package/dist/components/admin/user-chip.d.ts +1 -1
  11. package/dist/components/admin/user-chip.js +7 -4
  12. package/dist/components/ai/approval.d.ts +30 -0
  13. package/dist/components/ai/approval.js +81 -0
  14. package/dist/components/ai/chat-shell.d.ts +53 -0
  15. package/dist/components/ai/chat-shell.js +201 -0
  16. package/dist/components/ai/chat.d.ts +11 -3
  17. package/dist/components/ai/chat.js +21 -15
  18. package/dist/components/ai/code-block.js +1 -1
  19. package/dist/components/ai/conversation-list.d.ts +36 -3
  20. package/dist/components/ai/conversation-list.js +122 -13
  21. package/dist/components/ai/prompt-input.d.ts +28 -4
  22. package/dist/components/ai/prompt-input.js +52 -4
  23. package/dist/components/ai/tool-steps.d.ts +41 -0
  24. package/dist/components/ai/tool-steps.js +124 -0
  25. package/dist/components/ui/alert.d.ts +1 -1
  26. package/dist/components/ui/alert.js +5 -2
  27. package/dist/components/ui/badge.d.ts +2 -1
  28. package/dist/components/ui/badge.js +27 -14
  29. package/dist/components/ui/item.js +2 -2
  30. package/dist/components/ui/sidebar.d.ts +3 -1
  31. package/dist/components/ui/sidebar.js +7 -3
  32. package/dist/hooks/use-media-query.d.ts +2 -0
  33. package/dist/hooks/use-media-query.js +12 -0
  34. package/dist/index.d.ts +12 -6
  35. package/dist/index.js +10 -4
  36. package/dist/styles.css +24 -0
  37. package/docs/COMPONENTS.md +141 -72
  38. package/llms.txt +3 -3
  39. package/package.json +1 -1
  40. package/skills/harebear-ui/SKILL.md +4 -1
@@ -21,7 +21,10 @@ export interface AppBrandProps extends React.ComponentProps<"div"> {
21
21
  /** Optional trailing control, e.g. a workspace switcher button. */
22
22
  action?: React.ReactNode;
23
23
  }
24
- /** Logo tile + product name. Used in the sidebar header or a top bar. */
24
+ /**
25
+ * Logo tile + product name. Used in the sidebar header or a top bar.
26
+ * In a sidebar collapsed to its icon rail, only the tile shows.
27
+ */
25
28
  declare function AppBrand({ name, subtitle, icon, action, className, ...props }: AppBrandProps): React.JSX.Element;
26
29
  export interface AppShellProps {
27
30
  /** Sidebar header content, usually <AppBrand />. */
@@ -4,11 +4,14 @@ import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel
4
4
  import { cn } from "../../lib/utils.js";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  // src/components/admin/app-shell.tsx
7
- /** Logo tile + product name. Used in the sidebar header or a top bar. */
7
+ /**
8
+ * Logo tile + product name. Used in the sidebar header or a top bar.
9
+ * In a sidebar collapsed to its icon rail, only the tile shows.
10
+ */
8
11
  function AppBrand({ name, subtitle, icon, action, className, ...props }) {
9
12
  return /* @__PURE__ */ jsxs("div", {
10
13
  "data-slot": "app-brand",
11
- className: cn("flex items-center gap-3", className),
14
+ className: cn("flex items-center gap-3 group-data-[collapsible=icon]:justify-center", className),
12
15
  ...props,
13
16
  children: [
14
17
  icon && /* @__PURE__ */ jsx("div", {
@@ -16,7 +19,7 @@ function AppBrand({ name, subtitle, icon, action, className, ...props }) {
16
19
  children: icon
17
20
  }),
18
21
  /* @__PURE__ */ jsxs("div", {
19
- className: "min-w-0 flex-1",
22
+ className: "min-w-0 flex-1 group-data-[collapsible=icon]:hidden",
20
23
  children: [/* @__PURE__ */ jsx("div", {
21
24
  className: "truncate text-[15px] font-semibold",
22
25
  children: name
@@ -25,7 +28,10 @@ function AppBrand({ name, subtitle, icon, action, className, ...props }) {
25
28
  children: subtitle
26
29
  })]
27
30
  }),
28
- action
31
+ action && /* @__PURE__ */ jsx("div", {
32
+ className: "contents group-data-[collapsible=icon]:hidden",
33
+ children: action
34
+ })
29
35
  ]
30
36
  });
31
37
  }
@@ -37,54 +43,51 @@ function AppShell({ brand, nav = [], sidebarExtra, sidebarFooter, header, linkCo
37
43
  return /* @__PURE__ */ jsxs(SidebarProvider, {
38
44
  defaultOpen,
39
45
  style: { "--sidebar-width": "15.75rem" },
40
- children: [/* @__PURE__ */ jsxs(Sidebar, {
41
- className: "border-r-0",
42
- children: [
43
- brand && /* @__PURE__ */ jsx(SidebarHeader, {
44
- className: "px-4 pt-6 pb-2",
45
- children: brand
46
- }),
47
- /* @__PURE__ */ jsxs(SidebarContent, {
48
- className: "gap-5 px-2",
49
- children: [nav.map((section, i) => /* @__PURE__ */ jsxs(SidebarGroup, { children: [section.label && /* @__PURE__ */ jsx(SidebarGroupLabel, {
50
- className: "text-xs font-medium",
51
- children: section.label
52
- }), /* @__PURE__ */ jsx(SidebarMenu, {
53
- className: "gap-1",
54
- children: section.items.map((item) => /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(SidebarMenuButton, {
55
- asChild: !!item.href,
56
- isActive: item.active,
57
- onClick: item.onSelect,
58
- children: item.href ? /* @__PURE__ */ jsxs(Link, {
59
- href: item.href,
60
- children: [
61
- item.icon,
62
- /* @__PURE__ */ jsx("span", {
63
- className: "flex-1",
64
- children: item.label
65
- }),
66
- item.badge !== void 0 && /* @__PURE__ */ jsx(NavBadge, { children: item.badge })
67
- ]
68
- }) : /* @__PURE__ */ jsxs(Fragment, { children: [
46
+ children: [/* @__PURE__ */ jsxs(Sidebar, { children: [
47
+ brand && /* @__PURE__ */ jsx(SidebarHeader, {
48
+ className: "px-4 pt-6 pb-2",
49
+ children: brand
50
+ }),
51
+ /* @__PURE__ */ jsxs(SidebarContent, {
52
+ className: "gap-5 px-2",
53
+ children: [nav.map((section, i) => /* @__PURE__ */ jsxs(SidebarGroup, { children: [section.label && /* @__PURE__ */ jsx(SidebarGroupLabel, {
54
+ className: "text-xs font-medium",
55
+ children: section.label
56
+ }), /* @__PURE__ */ jsx(SidebarMenu, {
57
+ className: "gap-1",
58
+ children: section.items.map((item) => /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(SidebarMenuButton, {
59
+ asChild: !!item.href,
60
+ isActive: item.active,
61
+ onClick: item.onSelect,
62
+ children: item.href ? /* @__PURE__ */ jsxs(Link, {
63
+ href: item.href,
64
+ children: [
69
65
  item.icon,
70
66
  /* @__PURE__ */ jsx("span", {
71
67
  className: "flex-1",
72
68
  children: item.label
73
69
  }),
74
70
  item.badge !== void 0 && /* @__PURE__ */ jsx(NavBadge, { children: item.badge })
75
- ] })
76
- }) }, item.label))
77
- })] }, section.label ?? i)), sidebarExtra && /* @__PURE__ */ jsx("div", {
78
- className: "mt-auto px-2",
79
- children: sidebarExtra
80
- })]
81
- }),
82
- sidebarFooter && /* @__PURE__ */ jsx(SidebarFooter, {
83
- className: "px-4 pb-6",
84
- children: sidebarFooter
85
- })
86
- ]
87
- }), /* @__PURE__ */ jsxs(SidebarInset, {
71
+ ]
72
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [
73
+ item.icon,
74
+ /* @__PURE__ */ jsx("span", {
75
+ className: "flex-1",
76
+ children: item.label
77
+ }),
78
+ item.badge !== void 0 && /* @__PURE__ */ jsx(NavBadge, { children: item.badge })
79
+ ] })
80
+ }) }, item.label))
81
+ })] }, section.label ?? i)), sidebarExtra && /* @__PURE__ */ jsx("div", {
82
+ className: "mt-auto px-2",
83
+ children: sidebarExtra
84
+ })]
85
+ }),
86
+ sidebarFooter && /* @__PURE__ */ jsx(SidebarFooter, {
87
+ className: "px-4 pb-6",
88
+ children: sidebarFooter
89
+ })
90
+ ] }), /* @__PURE__ */ jsxs(SidebarInset, {
88
91
  className: cn("min-w-0", className),
89
92
  children: [header !== void 0 && /* @__PURE__ */ jsxs("header", {
90
93
  "data-slot": "app-header",
@@ -0,0 +1,17 @@
1
+ import * as React from "react";
2
+ export interface ConfirmDialogProps {
3
+ open: boolean;
4
+ onOpenChange: (open: boolean) => void;
5
+ title: React.ReactNode;
6
+ description?: React.ReactNode;
7
+ /** Default "Confirm". */
8
+ confirmLabel?: React.ReactNode;
9
+ /** Default "Cancel". */
10
+ cancelLabel?: React.ReactNode;
11
+ /** Red confirm button, for deletes and other destructive actions. */
12
+ destructive?: boolean;
13
+ onConfirm: () => void;
14
+ }
15
+ /** Controlled "Are you sure?" dialog: title, explanation, cancel and confirm. Closes after either choice. */
16
+ declare function ConfirmDialog({ open, onOpenChange, title, description, confirmLabel, cancelLabel, destructive, onConfirm }: ConfirmDialogProps): React.JSX.Element;
17
+ export { ConfirmDialog };
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "../ui/alert-dialog.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ // src/components/admin/confirm-dialog.tsx
5
+ /** Controlled "Are you sure?" dialog: title, explanation, cancel and confirm. Closes after either choice. */
6
+ function ConfirmDialog({ open, onOpenChange, title, description, confirmLabel = "Confirm", cancelLabel = "Cancel", destructive, onConfirm }) {
7
+ return /* @__PURE__ */ jsx(AlertDialog, {
8
+ open,
9
+ onOpenChange,
10
+ children: /* @__PURE__ */ jsxs(AlertDialogContent, {
11
+ "data-slot": "confirm-dialog",
12
+ children: [/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsx(AlertDialogTitle, { children: title }), description && /* @__PURE__ */ jsx(AlertDialogDescription, { children: description })] }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, { children: cancelLabel }), /* @__PURE__ */ jsx(AlertDialogAction, {
13
+ variant: destructive ? "destructive" : "default",
14
+ onClick: onConfirm,
15
+ children: confirmLabel
16
+ })] })]
17
+ })
18
+ });
19
+ }
20
+ export { ConfirmDialog };
@@ -0,0 +1,17 @@
1
+ import { InputGroup } from "../ui/input-group.js";
2
+ import * as React from "react";
3
+ export interface CopyFieldProps extends Omit<React.ComponentProps<typeof InputGroup>, "children"> {
4
+ /** The text copied. */
5
+ value: string;
6
+ /** What the field shows, if not the full value (e.g. a file name for a path). The full value is the tooltip. */
7
+ display?: string;
8
+ /** Leading label inside the field, e.g. "File" or "ID". */
9
+ label?: React.ReactNode;
10
+ /** Accessible name for the field. Default "Value". */
11
+ "aria-label"?: string;
12
+ /** Monospace text. Default true. */
13
+ mono?: boolean;
14
+ }
15
+ /** Read-only value with a copy button: IDs, paths, tokens' public parts, URLs. */
16
+ declare function CopyField({ value, display, label, mono, "aria-label": name, className, ...props }: CopyFieldProps): React.JSX.Element;
17
+ export { CopyField };
@@ -0,0 +1,37 @@
1
+ "use client";
2
+ import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText } from "../ui/input-group.js";
3
+ import { useCopyToClipboard } from "../../hooks/use-copy-to-clipboard.js";
4
+ import { cn } from "../../lib/utils.js";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { CheckIcon, CopyIcon } from "lucide-react";
7
+ // src/components/admin/copy-field.tsx
8
+ /** Read-only value with a copy button: IDs, paths, tokens' public parts, URLs. */
9
+ function CopyField({ value, display, label, mono = true, "aria-label": name = "Value", className, ...props }) {
10
+ const { copied, copy } = useCopyToClipboard();
11
+ return /* @__PURE__ */ jsxs(InputGroup, {
12
+ "data-slot": "copy-field",
13
+ className,
14
+ ...props,
15
+ children: [
16
+ label && /* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(InputGroupText, { children: label }) }),
17
+ /* @__PURE__ */ jsx(InputGroupInput, {
18
+ readOnly: true,
19
+ value: display ?? value,
20
+ title: value,
21
+ "aria-label": name,
22
+ className: cn(mono && "font-mono"),
23
+ onFocus: (e) => e.currentTarget.select()
24
+ }),
25
+ /* @__PURE__ */ jsx(InputGroupAddon, {
26
+ align: "inline-end",
27
+ children: /* @__PURE__ */ jsx(InputGroupButton, {
28
+ size: "icon-xs",
29
+ "aria-label": `Copy ${name.toLowerCase()}`,
30
+ onClick: () => copy(value),
31
+ children: copied ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(CopyIcon, {})
32
+ })
33
+ })
34
+ ]
35
+ });
36
+ }
37
+ export { CopyField };
@@ -10,7 +10,7 @@ function SearchInput({ shortcut, size = "default", className, wrapperClassName,
10
10
  const lg = size === "lg";
11
11
  return /* @__PURE__ */ jsxs("label", {
12
12
  "data-slot": "search-input",
13
- className: cn("relative block", wrapperClassName),
13
+ className: cn("relative block group-data-[collapsible=icon]:hidden", wrapperClassName),
14
14
  children: [
15
15
  /* @__PURE__ */ jsx(SearchIcon, {
16
16
  "aria-hidden": "true",
@@ -1,12 +1,18 @@
1
1
  import { Badge } from "../ui/badge.js";
2
2
  import * as React from "react";
3
3
  export type StatusTone = "success" | "warning" | "danger" | "info" | "neutral";
4
- /** Small colored dot. Use for status, legends, and project markers. */
5
- declare function StatusDot({ tone, className, ...props }: React.ComponentProps<"span"> & {
4
+ export interface StatusDotProps extends React.ComponentProps<"span"> {
6
5
  tone?: StatusTone;
7
- }): React.JSX.Element;
8
- /** Pill with a status dot and label, e.g. Paid / Pending / Refunded. */
9
- declare function StatusBadge({ tone, variant, children, className, ...props }: React.ComponentProps<typeof Badge> & {
6
+ /** Pulse the dot, for live states like "working" or "connecting". */
7
+ pulse?: boolean;
8
+ }
9
+ /** Small colored dot. Use for status, legends, and project markers. */
10
+ declare function StatusDot({ tone, pulse, className, ...props }: StatusDotProps): React.JSX.Element;
11
+ export interface StatusBadgeProps extends React.ComponentProps<typeof Badge> {
10
12
  tone?: StatusTone;
11
- }): React.JSX.Element;
13
+ /** Pulse the dot. */
14
+ pulse?: boolean;
15
+ }
16
+ /** Pill with a status dot and label, e.g. Paid / Pending / Refunded. `size="lg"` lines up with sm buttons. */
17
+ declare function StatusBadge({ tone, pulse, variant, children, className, ...props }: StatusBadgeProps): React.JSX.Element;
12
18
  export { StatusBadge, StatusDot };
@@ -11,22 +11,25 @@ const toneColor = {
11
11
  neutral: "bg-muted-foreground"
12
12
  };
13
13
  /** Small colored dot. Use for status, legends, and project markers. */
14
- function StatusDot({ tone = "neutral", className, ...props }) {
14
+ function StatusDot({ tone = "neutral", pulse, className, ...props }) {
15
15
  return /* @__PURE__ */ jsx("span", {
16
16
  "data-slot": "status-dot",
17
17
  "aria-hidden": "true",
18
- className: cn("inline-block size-2 shrink-0 rounded-full", toneColor[tone], className),
18
+ className: cn("inline-block size-2 shrink-0 rounded-full", toneColor[tone], pulse && "animate-pulse motion-reduce:animate-none", className),
19
19
  ...props
20
20
  });
21
21
  }
22
- /** Pill with a status dot and label, e.g. Paid / Pending / Refunded. */
23
- function StatusBadge({ tone = "neutral", variant = "secondary", children, className, ...props }) {
22
+ /** Pill with a status dot and label, e.g. Paid / Pending / Refunded. `size="lg"` lines up with sm buttons. */
23
+ function StatusBadge({ tone = "neutral", pulse, variant = "secondary", children, className, ...props }) {
24
24
  return /* @__PURE__ */ jsxs(Badge, {
25
25
  "data-slot": "status-badge",
26
26
  variant,
27
27
  className,
28
28
  ...props,
29
- children: [/* @__PURE__ */ jsx(StatusDot, { tone }), children]
29
+ children: [/* @__PURE__ */ jsx(StatusDot, {
30
+ tone,
31
+ pulse
32
+ }), children]
30
33
  });
31
34
  }
32
35
  export { StatusBadge, StatusDot };
@@ -9,6 +9,6 @@ export interface UserChipProps extends React.ComponentProps<"div"> {
9
9
  action?: React.ReactNode;
10
10
  size?: "sm" | "default" | "lg";
11
11
  }
12
- /** Avatar + name + secondary line. */
12
+ /** Avatar + name + secondary line. In a sidebar collapsed to its icon rail, only the avatar shows. */
13
13
  declare function UserChip({ name, subtitle, avatarUrl, action, size, className, ...props }: UserChipProps): React.JSX.Element;
14
14
  export { UserChip };
@@ -7,11 +7,11 @@ import { jsx, jsxs } from "react/jsx-runtime";
7
7
  function initials(name) {
8
8
  return name.split(/\s+/).filter(Boolean).slice(0, 2).map((p) => p[0]?.toUpperCase()).join("");
9
9
  }
10
- /** Avatar + name + secondary line. */
10
+ /** Avatar + name + secondary line. In a sidebar collapsed to its icon rail, only the avatar shows. */
11
11
  function UserChip({ name, subtitle, avatarUrl, action, size = "default", className, ...props }) {
12
12
  return /* @__PURE__ */ jsxs("div", {
13
13
  "data-slot": "user-chip",
14
- className: cn("flex min-w-0 items-center gap-3", className),
14
+ className: cn("flex min-w-0 items-center gap-3 group-data-[collapsible=icon]:justify-center", className),
15
15
  ...props,
16
16
  children: [
17
17
  /* @__PURE__ */ jsxs(Avatar, {
@@ -22,7 +22,7 @@ function UserChip({ name, subtitle, avatarUrl, action, size = "default", classNa
22
22
  }), /* @__PURE__ */ jsx(AvatarFallback, { children: initials(name) })]
23
23
  }),
24
24
  /* @__PURE__ */ jsxs("div", {
25
- className: "min-w-0 flex-1 leading-snug",
25
+ className: "min-w-0 flex-1 leading-snug group-data-[collapsible=icon]:hidden",
26
26
  children: [/* @__PURE__ */ jsx("div", {
27
27
  className: cn("truncate font-medium", size === "lg" && "text-lg font-semibold"),
28
28
  children: name
@@ -31,7 +31,10 @@ function UserChip({ name, subtitle, avatarUrl, action, size = "default", classNa
31
31
  children: subtitle
32
32
  })]
33
33
  }),
34
- action
34
+ action && /* @__PURE__ */ jsx("div", {
35
+ className: "contents group-data-[collapsible=icon]:hidden",
36
+ children: action
37
+ })
35
38
  ]
36
39
  });
37
40
  }
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ export interface ApprovalCardProps extends Omit<React.ComponentProps<"section">, "title"> {
3
+ /** e.g. "Approval needed: run a command on services". */
4
+ title: React.ReactNode;
5
+ /** Small line under the title, e.g. the permission mode and when it was asked. */
6
+ description?: React.ReactNode;
7
+ /** Leading icon in a warning-tinted circle. Default a shield. Pass null for none. */
8
+ icon?: React.ReactNode;
9
+ /** The exact command or change, shown in a wrapped code block with a copy button. */
10
+ code?: string;
11
+ /** Label above the code, e.g. "bash". */
12
+ codeLabel?: React.ReactNode;
13
+ /** The choices, usually Buttons: approve, approve for the session, deny. */
14
+ actions?: React.ReactNode;
15
+ /** Small print under the actions. */
16
+ footer?: React.ReactNode;
17
+ }
18
+ /**
19
+ * Highlighted card for anything the agent is waiting on the user for: a tool approval, a question, a choice.
20
+ * Body text goes in children (e.g. the agent's reason).
21
+ */
22
+ declare function ApprovalCard({ title, description, icon, code, codeLabel, actions, footer, className, children, ...props }: ApprovalCardProps): React.JSX.Element;
23
+ export interface ApprovalResultProps extends React.ComponentProps<"div"> {
24
+ decision: "approved" | "denied";
25
+ /** Trailing time, e.g. "10:42". */
26
+ time?: React.ReactNode;
27
+ }
28
+ /** One-line record of a decision in the thread, e.g. "You approved `docker restart x`, once". */
29
+ declare function ApprovalResult({ decision, time, className, children, ...props }: ApprovalResultProps): React.JSX.Element;
30
+ export { ApprovalCard, ApprovalResult };
@@ -0,0 +1,81 @@
1
+ "use client";
2
+ import { CodeBlock, CodeBlockActions, CodeBlockContent, CodeBlockCopyButton, CodeBlockHeader, CodeBlockTitle } from "../ui/code-block.js";
3
+ import { cn } from "../../lib/utils.js";
4
+ import * as React from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { CheckIcon, ShieldAlertIcon, XIcon } from "lucide-react";
7
+ // src/components/ai/approval.tsx
8
+ /**
9
+ * Highlighted card for anything the agent is waiting on the user for: a tool approval, a question, a choice.
10
+ * Body text goes in children (e.g. the agent's reason).
11
+ */
12
+ function ApprovalCard({ title, description, icon = /* @__PURE__ */ jsx(ShieldAlertIcon, {}), code, codeLabel, actions, footer, className, children, ...props }) {
13
+ const id = React.useId();
14
+ return /* @__PURE__ */ jsxs("section", {
15
+ "data-slot": "approval-card",
16
+ "aria-labelledby": id,
17
+ className: cn("flex min-w-0 flex-col gap-4 rounded-xl border border-warning/50 bg-card px-5.5 py-5 ring-4 ring-warning/10", className),
18
+ ...props,
19
+ children: [
20
+ /* @__PURE__ */ jsxs("div", {
21
+ className: "flex items-center gap-3",
22
+ children: [icon && /* @__PURE__ */ jsx("span", {
23
+ className: "grid size-8.5 shrink-0 place-items-center rounded-full bg-warning/12 text-warning [&_svg]:size-4.5",
24
+ children: icon
25
+ }), /* @__PURE__ */ jsxs("div", {
26
+ className: "flex min-w-0 flex-1 flex-col leading-snug",
27
+ children: [/* @__PURE__ */ jsx("h2", {
28
+ id,
29
+ className: "text-[15px] font-semibold whitespace-pre-wrap wrap-anywhere",
30
+ children: title
31
+ }), description && /* @__PURE__ */ jsx("span", {
32
+ className: "text-xs text-muted-foreground",
33
+ children: description
34
+ })]
35
+ })]
36
+ }),
37
+ children && /* @__PURE__ */ jsx("div", {
38
+ className: "text-[14.5px] leading-relaxed whitespace-pre-wrap wrap-anywhere [&_strong]:font-semibold",
39
+ children
40
+ }),
41
+ code !== void 0 && /* @__PURE__ */ jsxs(CodeBlock, { children: [/* @__PURE__ */ jsxs(CodeBlockHeader, { children: [/* @__PURE__ */ jsx(CodeBlockTitle, { children: codeLabel ?? "command" }), /* @__PURE__ */ jsx(CodeBlockActions, { children: /* @__PURE__ */ jsx(CodeBlockCopyButton, { value: code }) })] }), /* @__PURE__ */ jsx(CodeBlockContent, {
42
+ wrap: true,
43
+ children: code
44
+ })] }),
45
+ actions && /* @__PURE__ */ jsx("div", {
46
+ className: "flex flex-wrap items-center gap-2.5",
47
+ children: actions
48
+ }),
49
+ footer && /* @__PURE__ */ jsx("div", {
50
+ className: "text-xs text-muted-foreground wrap-anywhere",
51
+ children: footer
52
+ })
53
+ ]
54
+ });
55
+ }
56
+ /** One-line record of a decision in the thread, e.g. "You approved `docker restart x`, once". */
57
+ function ApprovalResult({ decision, time, className, children, ...props }) {
58
+ const denied = decision === "denied";
59
+ return /* @__PURE__ */ jsxs("div", {
60
+ "data-slot": "approval-result",
61
+ "data-decision": decision,
62
+ className: cn("flex min-w-0 items-center gap-2.5 px-1 text-[13px]", className),
63
+ ...props,
64
+ children: [
65
+ /* @__PURE__ */ jsx("span", {
66
+ "aria-hidden": "true",
67
+ className: cn("grid size-6.5 shrink-0 place-items-center rounded-full [&_svg]:size-3.5", denied ? "bg-muted text-muted-foreground" : "bg-success/12 text-success"),
68
+ children: denied ? /* @__PURE__ */ jsx(XIcon, {}) : /* @__PURE__ */ jsx(CheckIcon, {})
69
+ }),
70
+ /* @__PURE__ */ jsx("span", {
71
+ className: "min-w-0 flex-1 truncate",
72
+ children
73
+ }),
74
+ time && /* @__PURE__ */ jsx("span", {
75
+ className: "shrink-0 text-xs text-muted-foreground tabular-nums",
76
+ children: time
77
+ })
78
+ ]
79
+ });
80
+ }
81
+ export { ApprovalCard, ApprovalResult };
@@ -0,0 +1,53 @@
1
+ import * as React from "react";
2
+ export interface ChatShellProps {
3
+ /** Sidebar top, usually <AppBrand />. Collapses to its logo tile in the icon rail. */
4
+ brand?: React.ReactNode;
5
+ /** Under the brand, usually <NewConversationButton />. Stays visible in the icon rail. */
6
+ sidebarActions?: React.ReactNode;
7
+ /** Scrolling sidebar body, usually <ConversationList />. Hidden in the icon rail. */
8
+ sidebar?: React.ReactNode;
9
+ /** Sidebar bottom, usually <UserChip /> with a ThemeToggle as its action. */
10
+ sidebarFooter?: React.ReactNode;
11
+ /** Right-hand panel, e.g. Cards with details about the open conversation. Inline at 1280px and up, a sheet below. */
12
+ details?: React.ReactNode;
13
+ /** Sheet title and toggle label for the details panel. Default "Details". */
14
+ detailsTitle?: string;
15
+ /** Show the details panel at first on wide screens. Default true. */
16
+ defaultDetailsOpen?: boolean;
17
+ /** Remember the sidebar's expanded or collapsed state in localStorage under this key. */
18
+ storageKey?: string;
19
+ /** Sidebar expanded at first (when nothing is stored). Default true. */
20
+ defaultOpen?: boolean;
21
+ className?: string;
22
+ /** The main column, usually <ChatPanel />. */
23
+ children: React.ReactNode;
24
+ }
25
+ /**
26
+ * Full-height chat app layout: a sidebar that collapses to an icon rail (a sheet on mobile), the main chat
27
+ * column, and an optional details panel. Put a ChatPanel inside.
28
+ */
29
+ declare function ChatShell({ brand, sidebarActions, sidebar, sidebarFooter, details, detailsTitle, defaultDetailsOpen, storageKey, defaultOpen, className, children }: ChatShellProps): React.JSX.Element;
30
+ export interface ChatPanelProps extends Omit<React.ComponentProps<"section">, "title"> {
31
+ /** Conversation title. */
32
+ title?: React.ReactNode;
33
+ /** Line under the title, e.g. "Started Nov 10, 9:41". */
34
+ description?: React.ReactNode;
35
+ /** Header controls, e.g. <ModelPicker /> and a <StatusBadge size="lg" />. */
36
+ actions?: React.ReactNode;
37
+ /** Warning bar under the header, e.g. "Reconnecting…". */
38
+ notice?: React.ReactNode;
39
+ /** Under the thread, usually <PromptInput />. */
40
+ composer?: React.ReactNode;
41
+ /** Changing it starts a fresh thread view (scroll position resets). Pass the conversation id. */
42
+ conversationId?: React.Key;
43
+ /** Keep the thread scrolled to the bottom as it grows. Default true. */
44
+ autoScroll?: boolean;
45
+ /** The thread: ChatMessage, ToolGroup, ApprovalCard… */
46
+ children?: React.ReactNode;
47
+ }
48
+ /**
49
+ * The chat column as a card: header, optional notice, scrolling thread, composer. Inside ChatShell it adds the
50
+ * mobile sidebar button and the details toggle to the header by itself.
51
+ */
52
+ declare function ChatPanel({ title, description, actions, notice, composer, conversationId, autoScroll, className, children, ...props }: ChatPanelProps): React.JSX.Element;
53
+ export { ChatPanel, ChatShell };