@elizaos/client 1.6.1-alpha.3 → 1.6.1-alpha.5

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 (48) hide show
  1. package/dist/assets/{main-C4q5_rtN.js → main-4tyUgNqd.js} +3 -3
  2. package/dist/assets/{main-C4q5_rtN.js.map → main-4tyUgNqd.js.map} +1 -1
  3. package/dist/assets/{main-BNtEiK3o.js → main-Bbs84AcL.js} +77 -63
  4. package/dist/assets/main-Bbs84AcL.js.map +1 -0
  5. package/dist/assets/{main-BOBWcKWW.css → main-CNv6B3RZ.css} +597 -71
  6. package/dist/assets/react-vendor-DxnAFk-d.js +611 -0
  7. package/dist/assets/react-vendor-DxnAFk-d.js.map +1 -0
  8. package/dist/index.html +1 -1
  9. package/package.json +8 -4
  10. package/src/components/agent-prism/Avatar.tsx +164 -0
  11. package/src/components/agent-prism/Badge.tsx +109 -0
  12. package/src/components/agent-prism/Button.tsx +138 -0
  13. package/src/components/agent-prism/CollapseAndExpandControls.tsx +45 -0
  14. package/src/components/agent-prism/CollapsibleSection.tsx +121 -0
  15. package/src/components/agent-prism/DetailsView/DetailsView.tsx +141 -0
  16. package/src/components/agent-prism/DetailsView/DetailsViewAttributesTab.tsx +45 -0
  17. package/src/components/agent-prism/DetailsView/DetailsViewHeader.tsx +77 -0
  18. package/src/components/agent-prism/DetailsView/DetailsViewHeaderActions.tsx +21 -0
  19. package/src/components/agent-prism/DetailsView/DetailsViewInputOutputTab.tsx +210 -0
  20. package/src/components/agent-prism/DetailsView/DetailsViewMetrics.tsx +53 -0
  21. package/src/components/agent-prism/DetailsView/DetailsViewRawDataTab.tsx +24 -0
  22. package/src/components/agent-prism/IconButton.tsx +75 -0
  23. package/src/components/agent-prism/PriceBadge.tsx +12 -0
  24. package/src/components/agent-prism/SearchInput.tsx +17 -0
  25. package/src/components/agent-prism/SpanCard/SpanCard.tsx +467 -0
  26. package/src/components/agent-prism/SpanCard/SpanCardBadges.tsx +35 -0
  27. package/src/components/agent-prism/SpanCard/SpanCardConnector.tsx +36 -0
  28. package/src/components/agent-prism/SpanCard/SpanCardTimeline.tsx +60 -0
  29. package/src/components/agent-prism/SpanCard/SpanCardToggle.tsx +32 -0
  30. package/src/components/agent-prism/SpanStatus.tsx +79 -0
  31. package/src/components/agent-prism/Tabs.tsx +141 -0
  32. package/src/components/agent-prism/TextInput.tsx +142 -0
  33. package/src/components/agent-prism/TimestampBadge.tsx +28 -0
  34. package/src/components/agent-prism/TokensBadge.tsx +26 -0
  35. package/src/components/agent-prism/TraceList/TraceList.tsx +80 -0
  36. package/src/components/agent-prism/TraceList/TraceListItem.tsx +79 -0
  37. package/src/components/agent-prism/TraceList/TraceListItemHeader.tsx +46 -0
  38. package/src/components/agent-prism/TraceViewer.tsx +476 -0
  39. package/src/components/agent-prism/TreeView.tsx +57 -0
  40. package/src/components/agent-prism/shared.ts +210 -0
  41. package/src/components/agent-runs/AgentRunTimeline.tsx +64 -673
  42. package/src/components/agent-sidebar.tsx +2 -2
  43. package/src/components/chat.tsx +8 -8
  44. package/src/lib/agent-prism-utils.ts +46 -0
  45. package/src/lib/eliza-span-adapter.ts +487 -0
  46. package/dist/assets/main-BNtEiK3o.js.map +0 -1
  47. package/dist/assets/react-vendor-pe76PXQl.js +0 -546
  48. package/dist/assets/react-vendor-pe76PXQl.js.map +0 -1
package/dist/index.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" type="image/x-icon" href="/favicon.ico" />
7
7
  <title>ElizaOS - Client</title>
8
- <script type="module" crossorigin src="/assets/main-C4q5_rtN.js"></script>
8
+ <script type="module" crossorigin src="/assets/main-4tyUgNqd.js"></script>
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elizaos/client",
3
- "version": "1.6.1-alpha.3",
3
+ "version": "1.6.1-alpha.5",
4
4
  "description": "Web client interface for ElizaOS agents",
5
5
  "repository": {
6
6
  "type": "git",
@@ -61,8 +61,10 @@
61
61
  "access": "public"
62
62
  },
63
63
  "dependencies": {
64
- "@elizaos/api-client": "1.6.1-alpha.3",
65
- "@elizaos/core": "1.6.1-alpha.3",
64
+ "@elizaos/api-client": "1.6.1-alpha.5",
65
+ "@elizaos/core": "1.6.1-alpha.5",
66
+ "@evilmartians/agent-prism-data": "^0.0.4",
67
+ "@evilmartians/agent-prism-types": "^0.0.4",
66
68
  "@hookform/resolvers": "^5.1.1",
67
69
  "@radix-ui/react-alert-dialog": "^1.0.5",
68
70
  "@radix-ui/react-avatar": "^1.1.3",
@@ -84,6 +86,7 @@
84
86
  "@uidotdev/usehooks": "^2.4.1",
85
87
  "buffer": "^6.0.3",
86
88
  "class-variance-authority": "^0.7.1",
89
+ "classnames": "^2.5.1",
87
90
  "clsx": "2.1.1",
88
91
  "cmdk": "^1.0.4",
89
92
  "date-fns": "^4.1.0",
@@ -96,6 +99,7 @@
96
99
  "react-force-graph": "^1.47.6",
97
100
  "react-force-graph-2d": "^1.27.1",
98
101
  "react-joyride": "^2.9.3",
102
+ "react-json-pretty": "^2.2.0",
99
103
  "react-markdown": "^10.1.0",
100
104
  "react-resizable-panels": "^2.1.7",
101
105
  "react-router": "^7.3.0",
@@ -157,5 +161,5 @@
157
161
  "vite-tsconfig-paths": "^5.1.4",
158
162
  "wait-on": "^8.0.3"
159
163
  },
160
- "gitHead": "19058b935fffc6409bf94eb65b90a680d809ce73"
164
+ "gitHead": "90c052c82d42bdbfac79771ccdbcd6a1be939562"
161
165
  }
@@ -0,0 +1,164 @@
1
+ import { cn } from "@/lib/utils";
2
+ import { User } from "lucide-react";
3
+ import { useState, type ComponentPropsWithRef, type ReactElement } from "react";
4
+
5
+ import {
6
+ ROUNDED_CLASSES,
7
+ type ColorVariant,
8
+ type ComponentSize,
9
+ } from "./shared.ts";
10
+
11
+ export type AvatarSize = Extract<
12
+ ComponentSize,
13
+ "4" | "6" | "8" | "9" | "10" | "11" | "12" | "16"
14
+ >;
15
+
16
+ const sizeClasses: Record<AvatarSize, string> = {
17
+ "4": "size-4 text-xs",
18
+ "6": "size-6 text-xs",
19
+ "8": "size-8 text-xs",
20
+ "9": "size-9 text-sm",
21
+ "10": "size-10 text-base",
22
+ "11": "size-11 text-lg",
23
+ "12": "size-12 text-xl",
24
+ "16": "size-16 text-2xl",
25
+ };
26
+
27
+ const iconSizeClasses: Record<AvatarSize, string> = {
28
+ "4": "size-3",
29
+ "6": "size-4",
30
+ "8": "size-6",
31
+ "9": "size-7",
32
+ "10": "size-8",
33
+ "11": "size-9",
34
+ "12": "size-10",
35
+ "16": "size-12",
36
+ };
37
+
38
+ const textSizeClasses: Record<AvatarSize, string> = {
39
+ "4": "text-xs",
40
+ "6": "text-xs",
41
+ "8": "text-xs",
42
+ "9": "text-sm",
43
+ "10": "text-base",
44
+ "11": "text-lg",
45
+ "12": "text-xl",
46
+ "16": "text-2xl",
47
+ };
48
+
49
+ const bgColorClasses: Record<ColorVariant, string> = {
50
+ gray: "bg-muted-foreground",
51
+ red: "bg-destructive",
52
+ orange: "bg-chart-1",
53
+ yellow: "bg-chart-5",
54
+ teal: "bg-chart-2",
55
+ indigo: "bg-primary",
56
+ purple: "bg-primary",
57
+ sky: "bg-chart-4",
58
+ cyan: "bg-chart-3",
59
+ emerald: "bg-accent",
60
+ };
61
+
62
+ export type AvatarProps = ComponentPropsWithRef<"div"> & {
63
+ /**
64
+ * The image source for the avatar
65
+ */
66
+ src?: string;
67
+ /**
68
+ * The alt text for the avatar
69
+ */
70
+ alt?: string;
71
+ /**
72
+ * The size of the avatar
73
+ * @default "md"
74
+ */
75
+ size?: AvatarSize;
76
+ /**
77
+ * The border radius of the avatar
78
+ * @default "full"
79
+ */
80
+ rounded?: "none" | "sm" | "md" | "lg" | "full";
81
+ /**
82
+ * Background color theme for the letter avatar
83
+ * Uses the unified color theme system
84
+ * @default "gray"
85
+ */
86
+ bgColor?: ColorVariant;
87
+ /**
88
+ * Text color for the letter avatar
89
+ * @default "white"
90
+ */
91
+ textColor?: "white" | "black";
92
+ /**
93
+ * Custom letter to display (will use first letter of alt if not provided)
94
+ */
95
+ letter?: string;
96
+ /**
97
+ * Optional className for additional styling
98
+ */
99
+ className?: string;
100
+ };
101
+
102
+ export const Avatar = ({
103
+ src,
104
+ alt = "Avatar",
105
+ size = "10",
106
+ rounded = "full",
107
+ bgColor = "gray",
108
+ textColor = "white",
109
+ letter,
110
+ className = "",
111
+ ...rest
112
+ }: AvatarProps): ReactElement => {
113
+ const [error, setError] = useState(false);
114
+
115
+ const displayLetter = letter ? letter.charAt(0) : alt.charAt(0).toUpperCase();
116
+
117
+ const actualTextColor = textColor === "white" ? "text-white" : "text-black";
118
+
119
+ return (
120
+ <div
121
+ className={cn(
122
+ "flex items-center justify-center overflow-hidden",
123
+ "bg-muted",
124
+ error && "border border-border",
125
+ sizeClasses[size],
126
+ textSizeClasses[size],
127
+ ROUNDED_CLASSES[rounded],
128
+ className,
129
+ )}
130
+ {...rest}
131
+ >
132
+ {error ? (
133
+ <User
134
+ className={cn(
135
+ iconSizeClasses[size],
136
+ "text-muted-foreground",
137
+ )}
138
+ />
139
+ ) : (
140
+ <>
141
+ {src ? (
142
+ <img
143
+ src={src}
144
+ alt={alt}
145
+ className="h-full w-full object-cover"
146
+ onError={() => setError(true)}
147
+ />
148
+ ) : (
149
+ <div
150
+ className={cn(
151
+ "flex h-full w-full items-center justify-center",
152
+ bgColorClasses[bgColor],
153
+ actualTextColor,
154
+ "font-medium",
155
+ )}
156
+ >
157
+ {displayLetter}
158
+ </div>
159
+ )}
160
+ </>
161
+ )}
162
+ </div>
163
+ );
164
+ };
@@ -0,0 +1,109 @@
1
+ import type { ComponentPropsWithRef, ReactElement, ReactNode } from "react";
2
+
3
+ import { cn } from "@/lib/utils";
4
+
5
+ import {
6
+ COLOR_THEME_CLASSES,
7
+ type ColorVariant,
8
+ type ComponentSize,
9
+ } from "./shared.ts";
10
+
11
+ type BadgeSize = Extract<ComponentSize, "4" | "5" | "6" | "7">;
12
+
13
+ const sizeClasses: Record<BadgeSize, string> = {
14
+ "4": "px-1 gap-1 h-4",
15
+ "5": "px-1.5 gap-1 h-5",
16
+ "6": "px-2 gap-1.5 h-6",
17
+ "7": "px-2.5 gap-2 h-7",
18
+ };
19
+
20
+ const textSizes: Record<BadgeSize, string> = {
21
+ "4": "text-xs font-normal leading-3",
22
+ "5": "text-xs font-medium",
23
+ "6": "text-sm font-medium",
24
+ "7": "text-sm font-medium",
25
+ };
26
+
27
+ export type BadgeProps = ComponentPropsWithRef<"span"> & {
28
+ /**
29
+ * The content of the badge
30
+ */
31
+ label: ReactNode;
32
+
33
+ /**
34
+ * The color theme of the badge
35
+ * Uses the unified color theme system
36
+ * @default "gray"
37
+ */
38
+ theme: ColorVariant;
39
+
40
+ /**
41
+ * The visual variant of the badge
42
+ * @default "solid"
43
+ */
44
+ variant?: "solid" | "outline";
45
+
46
+ /**
47
+ * The size of the badge
48
+ * @default "md"
49
+ */
50
+ size?: BadgeSize;
51
+
52
+ /**
53
+ * Optional icon to display at the start of the badge
54
+ */
55
+ iconStart?: ReactElement;
56
+
57
+ /**
58
+ * Optional icon to display at the end of the badge
59
+ */
60
+ iconEnd?: ReactElement;
61
+
62
+ /**
63
+ * Optional className for additional styling
64
+ */
65
+ className?: string;
66
+ };
67
+
68
+ export const Badge = ({
69
+ label,
70
+ theme = "gray",
71
+ variant = "solid",
72
+ size = "4",
73
+ iconStart,
74
+ iconEnd,
75
+ className = "",
76
+ ...rest
77
+ }: BadgeProps): ReactElement => {
78
+ const { bg, darkBg, text, darkText } = COLOR_THEME_CLASSES[theme];
79
+
80
+ const variantClasses =
81
+ variant === "outline"
82
+ ? `border ${text} ${darkText} bg-transparent dark:bg-transparent border-current`
83
+ : `${bg} ${text} ${darkBg} ${darkText}`;
84
+
85
+ return (
86
+ <span
87
+ className={cn(
88
+ "inline-flex min-w-0 items-center overflow-hidden rounded font-medium",
89
+ variantClasses,
90
+ sizeClasses[size],
91
+ className,
92
+ )}
93
+ {...rest}
94
+ >
95
+ {iconStart && <span className="shrink-0">{iconStart}</span>}
96
+
97
+ <span
98
+ className={cn(
99
+ textSizes[size],
100
+ "min-w-0 max-w-full flex-shrink-0 truncate tracking-normal",
101
+ )}
102
+ >
103
+ {label}
104
+ </span>
105
+
106
+ {iconEnd && <span className="shrink-0">{iconEnd}</span>}
107
+ </span>
108
+ );
109
+ };
@@ -0,0 +1,138 @@
1
+ import type { ComponentPropsWithRef, ReactElement } from "react";
2
+
3
+ import { cn } from "@/lib/utils";
4
+
5
+ import {
6
+ ROUNDED_CLASSES,
7
+ type ColorVariant,
8
+ type ComponentSize,
9
+ } from "./shared.ts";
10
+
11
+ type ButtonSize = Extract<
12
+ ComponentSize,
13
+ "6" | "7" | "8" | "9" | "10" | "11" | "12" | "16"
14
+ >;
15
+
16
+ const BASE_CLASSES =
17
+ "inline-flex items-center justify-center font-medium transition-all duration-200";
18
+
19
+ const sizeClasses = {
20
+ "6": "h-6 px-2 gap-1 text-xs",
21
+ "7": "h-7 px-2 gap-1 text-xs",
22
+ "8": "h-8 px-2 gap-1 text-xs",
23
+ "9": "h-9 px-2.5 gap-2 text-sm",
24
+ "10": "h-10 px-4 gap-2 text-sm",
25
+ "11": "h-11 px-5 gap-3 text-base",
26
+ "12": "h-12 px-5 gap-2.5 text-base",
27
+ "16": "h-16 px-7 gap-3 text-lg",
28
+ };
29
+
30
+ const filledThemeClasses: Record<ColorVariant, string> = {
31
+ gray: "bg-muted text-muted-foreground",
32
+ purple: "bg-primary text-primary-foreground",
33
+ indigo: "bg-primary text-primary-foreground",
34
+ orange: "bg-chart-1 text-primary-foreground",
35
+ teal: "bg-chart-2 text-primary-foreground",
36
+ cyan: "bg-chart-3 text-primary-foreground",
37
+ sky: "bg-chart-4 text-primary-foreground",
38
+ yellow: "bg-chart-5 text-primary-foreground",
39
+ emerald: "bg-accent text-accent-foreground",
40
+ red: "bg-destructive text-destructive-foreground",
41
+ };
42
+
43
+ const variantClasses = {
44
+ filled: "",
45
+ outlined:
46
+ "border border-2 bg-transparent text-foreground border-border",
47
+ ghost: "bg-transparent text-muted-foreground",
48
+ };
49
+
50
+ export type ButtonProps = ComponentPropsWithRef<"button"> & {
51
+ /**
52
+ * The size of the button
53
+ * @default "6"
54
+ */
55
+ size?: ButtonSize;
56
+
57
+ /**
58
+ * The color theme of the button
59
+ * @default "gray"
60
+ */
61
+ theme?: ColorVariant;
62
+
63
+ /**
64
+ * The border radius of the button
65
+ * @default "md"
66
+ */
67
+ rounded?: "none" | "sm" | "md" | "lg" | "full";
68
+
69
+ /**
70
+ * The visual variant of the button
71
+ * @default "filled"
72
+ */
73
+ variant?: "filled" | "outlined" | "ghost";
74
+
75
+ /**
76
+ * Makes the button full width
77
+ * @default false
78
+ */
79
+ fullWidth?: boolean;
80
+
81
+ /**
82
+ * Optional icon to display at the start of the button
83
+ */
84
+ iconStart?: ReactElement;
85
+
86
+ /**
87
+ * Optional icon to display at the end of the button
88
+ */
89
+ iconEnd?: ReactElement;
90
+ };
91
+
92
+ export const Button = ({
93
+ children,
94
+ size = "6",
95
+ theme = "gray",
96
+ rounded = "md",
97
+ variant = "filled",
98
+ fullWidth = false,
99
+ disabled = false,
100
+ iconStart,
101
+ iconEnd,
102
+ type = "button",
103
+ onClick,
104
+ className = "",
105
+ ...rest
106
+ }: ButtonProps) => {
107
+ const widthClass = fullWidth ? "w-full" : "";
108
+ const stateClasses = disabled
109
+ ? "cursor-not-allowed opacity-50"
110
+ : "hover:opacity-70";
111
+ const filledThemeClass =
112
+ variant === "filled"
113
+ ? filledThemeClasses[theme] || filledThemeClasses.gray
114
+ : "";
115
+
116
+ return (
117
+ <button
118
+ type={type}
119
+ onClick={onClick}
120
+ disabled={disabled}
121
+ className={cn(
122
+ BASE_CLASSES,
123
+ sizeClasses[size],
124
+ ROUNDED_CLASSES[rounded],
125
+ variantClasses[variant],
126
+ filledThemeClass,
127
+ widthClass,
128
+ stateClasses,
129
+ className,
130
+ )}
131
+ {...rest}
132
+ >
133
+ {iconStart && <span className="mr-1">{iconStart}</span>}
134
+ {children}
135
+ {iconEnd && <span className="ml-1">{iconEnd}</span>}
136
+ </button>
137
+ );
138
+ };
@@ -0,0 +1,45 @@
1
+ import type { ComponentPropsWithRef } from "react";
2
+
3
+ import { ChevronsUpDown, ChevronsDownUp } from "lucide-react";
4
+
5
+ import { IconButton } from "./IconButton.tsx";
6
+
7
+ export type SpanCardExpandAllButtonProps = ComponentPropsWithRef<"button"> & {
8
+ onExpandAll: () => void;
9
+ };
10
+
11
+ export type SpanCardCollapseAllButtonProps = ComponentPropsWithRef<"button"> & {
12
+ onCollapseAll: () => void;
13
+ };
14
+
15
+ export const ExpandAllButton = ({
16
+ onExpandAll,
17
+ ...rest
18
+ }: SpanCardExpandAllButtonProps) => {
19
+ return (
20
+ <IconButton
21
+ size="7"
22
+ onClick={onExpandAll}
23
+ aria-label="Expand all"
24
+ {...rest}
25
+ >
26
+ <ChevronsUpDown className="size-3.5" />
27
+ </IconButton>
28
+ );
29
+ };
30
+
31
+ export const CollapseAllButton = ({
32
+ onCollapseAll,
33
+ ...rest
34
+ }: SpanCardCollapseAllButtonProps) => {
35
+ return (
36
+ <IconButton
37
+ size="7"
38
+ onClick={onCollapseAll}
39
+ aria-label="Collapse all"
40
+ {...rest}
41
+ >
42
+ <ChevronsDownUp className="size-3.5" />
43
+ </IconButton>
44
+ );
45
+ };
@@ -0,0 +1,121 @@
1
+ import * as Collapsible from "@radix-ui/react-collapsible";
2
+ import { cn } from "@/lib/utils";
3
+ import { ChevronDown } from "lucide-react";
4
+ import * as React from "react";
5
+
6
+ export interface CollapsibleSectionProps {
7
+ /**
8
+ * The title text displayed in the trigger button
9
+ */
10
+ title: string;
11
+
12
+ /**
13
+ * The content to display on the right side of the title
14
+ */
15
+ rightContent?: React.ReactNode;
16
+
17
+ /**
18
+ * The content to display when the section is expanded
19
+ */
20
+ children: React.ReactNode;
21
+
22
+ /**
23
+ * Whether the section starts in an open state
24
+ * @default false
25
+ */
26
+ defaultOpen?: boolean;
27
+
28
+ /**
29
+ * Optional className for the root container
30
+ */
31
+ className?: string;
32
+
33
+ /**
34
+ * Optional className for the trigger button
35
+ */
36
+ triggerClassName?: string;
37
+
38
+ /**
39
+ * Optional className for the content area
40
+ */
41
+ contentClassName?: string;
42
+
43
+ /**
44
+ * Optional callback fired when the section is expanded or collapsed
45
+ */
46
+ onOpenChange?: (open: boolean) => void;
47
+ }
48
+
49
+ export const CollapsibleSection: React.FC<CollapsibleSectionProps> = ({
50
+ title,
51
+ rightContent,
52
+ children,
53
+ defaultOpen = false,
54
+ className = "",
55
+ triggerClassName = "",
56
+ contentClassName = "",
57
+ onOpenChange,
58
+ }) => {
59
+ const [open, setOpen] = React.useState(defaultOpen);
60
+
61
+ const handleOpenChange = React.useCallback(
62
+ (open: boolean): void => {
63
+ setOpen(open);
64
+ onOpenChange?.(open);
65
+ },
66
+ [onOpenChange],
67
+ );
68
+
69
+ const handleKeyDown = React.useCallback(
70
+ (e: React.KeyboardEvent<HTMLDivElement>): void => {
71
+ if (e.key === "Enter" || e.key === " ") {
72
+ e.preventDefault();
73
+ handleOpenChange(!open);
74
+ }
75
+ },
76
+ [handleOpenChange, open],
77
+ );
78
+
79
+ return (
80
+ <Collapsible.Root
81
+ open={open}
82
+ onOpenChange={handleOpenChange}
83
+ className={cn("rounded-lg", className)}
84
+ >
85
+ <Collapsible.Trigger asChild>
86
+ <div
87
+ tabIndex={0}
88
+ role="button"
89
+ className={cn(
90
+ "mb-1 flex w-full items-center justify-between gap-2 rounded-lg px-1 py-3 text-left text-sm font-medium text-foreground",
91
+ triggerClassName,
92
+ )}
93
+ onKeyDown={handleKeyDown}
94
+ aria-expanded={open}
95
+ aria-label={`${open ? "Collapse" : "Expand"} content of "${title}" section`}
96
+ >
97
+ <div className="flex w-full items-center gap-2">
98
+ <ChevronDown
99
+ className={cn(
100
+ "h-3 w-3 text-muted-foreground transition-transform duration-200",
101
+ open && "rotate-180",
102
+ )}
103
+ />
104
+ <span className="truncate">{title}</span>
105
+ </div>
106
+
107
+ {rightContent}
108
+ </div>
109
+ </Collapsible.Trigger>
110
+
111
+ <Collapsible.Content
112
+ className={cn(
113
+ "data-[state=closed]:animate-slideUp data-[state=open]:animate-slideDown",
114
+ contentClassName,
115
+ )}
116
+ >
117
+ {children}
118
+ </Collapsible.Content>
119
+ </Collapsible.Root>
120
+ );
121
+ };