@sero-ai/ui 0.1.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 (113) hide show
  1. package/.turbo/turbo-typecheck.log +4 -0
  2. package/components.json +18 -0
  3. package/package.json +70 -0
  4. package/src/components/ai-elements/agent.tsx +142 -0
  5. package/src/components/ai-elements/artifact.tsx +149 -0
  6. package/src/components/ai-elements/attachments.tsx +427 -0
  7. package/src/components/ai-elements/audio-player.tsx +232 -0
  8. package/src/components/ai-elements/canvas.tsx +26 -0
  9. package/src/components/ai-elements/chain-of-thought.tsx +223 -0
  10. package/src/components/ai-elements/checkpoint.tsx +72 -0
  11. package/src/components/ai-elements/code-block.tsx +555 -0
  12. package/src/components/ai-elements/commit.tsx +449 -0
  13. package/src/components/ai-elements/confirmation.tsx +173 -0
  14. package/src/components/ai-elements/connection.tsx +28 -0
  15. package/src/components/ai-elements/context.tsx +410 -0
  16. package/src/components/ai-elements/controls.tsx +19 -0
  17. package/src/components/ai-elements/conversation.tsx +167 -0
  18. package/src/components/ai-elements/edge.tsx +144 -0
  19. package/src/components/ai-elements/environment-variables.tsx +325 -0
  20. package/src/components/ai-elements/file-tree.tsx +298 -0
  21. package/src/components/ai-elements/image.tsx +25 -0
  22. package/src/components/ai-elements/inline-citation.tsx +294 -0
  23. package/src/components/ai-elements/jsx-preview.tsx +250 -0
  24. package/src/components/ai-elements/message.tsx +359 -0
  25. package/src/components/ai-elements/mic-selector.tsx +372 -0
  26. package/src/components/ai-elements/model-selector.tsx +214 -0
  27. package/src/components/ai-elements/node.tsx +72 -0
  28. package/src/components/ai-elements/open-in-chat.tsx +367 -0
  29. package/src/components/ai-elements/package-info.tsx +235 -0
  30. package/src/components/ai-elements/panel.tsx +16 -0
  31. package/src/components/ai-elements/persona.tsx +280 -0
  32. package/src/components/ai-elements/plan.tsx +144 -0
  33. package/src/components/ai-elements/prompt-input.tsx +1341 -0
  34. package/src/components/ai-elements/queue.tsx +275 -0
  35. package/src/components/ai-elements/reasoning.tsx +229 -0
  36. package/src/components/ai-elements/sandbox.tsx +133 -0
  37. package/src/components/ai-elements/schema-display.tsx +473 -0
  38. package/src/components/ai-elements/shimmer.tsx +78 -0
  39. package/src/components/ai-elements/snippet.tsx +141 -0
  40. package/src/components/ai-elements/sources.tsx +78 -0
  41. package/src/components/ai-elements/speech-input.tsx +324 -0
  42. package/src/components/ai-elements/stack-trace.tsx +531 -0
  43. package/src/components/ai-elements/suggestion.tsx +58 -0
  44. package/src/components/ai-elements/task.tsx +88 -0
  45. package/src/components/ai-elements/terminal.tsx +277 -0
  46. package/src/components/ai-elements/test-results.tsx +497 -0
  47. package/src/components/ai-elements/tool.tsx +174 -0
  48. package/src/components/ai-elements/toolbar.tsx +17 -0
  49. package/src/components/ai-elements/transcription.tsx +126 -0
  50. package/src/components/ai-elements/voice-selector.tsx +525 -0
  51. package/src/components/ai-elements/web-preview.tsx +282 -0
  52. package/src/components/ui/accordion.tsx +64 -0
  53. package/src/components/ui/alert-dialog.tsx +196 -0
  54. package/src/components/ui/alert.tsx +66 -0
  55. package/src/components/ui/aspect-ratio.tsx +11 -0
  56. package/src/components/ui/avatar.tsx +107 -0
  57. package/src/components/ui/badge.tsx +48 -0
  58. package/src/components/ui/breadcrumb.tsx +109 -0
  59. package/src/components/ui/button-group.tsx +83 -0
  60. package/src/components/ui/button.tsx +64 -0
  61. package/src/components/ui/calendar.tsx +218 -0
  62. package/src/components/ui/card.tsx +92 -0
  63. package/src/components/ui/carousel.tsx +239 -0
  64. package/src/components/ui/chart.tsx +355 -0
  65. package/src/components/ui/checkbox.tsx +32 -0
  66. package/src/components/ui/collapsible.tsx +31 -0
  67. package/src/components/ui/combobox.tsx +310 -0
  68. package/src/components/ui/command.tsx +184 -0
  69. package/src/components/ui/context-menu.tsx +252 -0
  70. package/src/components/ui/dialog.tsx +158 -0
  71. package/src/components/ui/direction.tsx +22 -0
  72. package/src/components/ui/drawer.tsx +133 -0
  73. package/src/components/ui/dropdown-menu.tsx +257 -0
  74. package/src/components/ui/empty.tsx +104 -0
  75. package/src/components/ui/field.tsx +248 -0
  76. package/src/components/ui/form.tsx +165 -0
  77. package/src/components/ui/hover-card.tsx +42 -0
  78. package/src/components/ui/input-group.tsx +168 -0
  79. package/src/components/ui/input-otp.tsx +77 -0
  80. package/src/components/ui/input.tsx +21 -0
  81. package/src/components/ui/item.tsx +193 -0
  82. package/src/components/ui/kbd.tsx +28 -0
  83. package/src/components/ui/label.tsx +22 -0
  84. package/src/components/ui/menubar.tsx +276 -0
  85. package/src/components/ui/native-select.tsx +53 -0
  86. package/src/components/ui/navigation-menu.tsx +168 -0
  87. package/src/components/ui/pagination.tsx +127 -0
  88. package/src/components/ui/popover.tsx +87 -0
  89. package/src/components/ui/progress.tsx +31 -0
  90. package/src/components/ui/radio-group.tsx +43 -0
  91. package/src/components/ui/resizable.tsx +53 -0
  92. package/src/components/ui/scroll-area.tsx +56 -0
  93. package/src/components/ui/select.tsx +190 -0
  94. package/src/components/ui/separator.tsx +28 -0
  95. package/src/components/ui/sheet.tsx +145 -0
  96. package/src/components/ui/sidebar.tsx +724 -0
  97. package/src/components/ui/skeleton.tsx +13 -0
  98. package/src/components/ui/slider.tsx +61 -0
  99. package/src/components/ui/sonner.tsx +40 -0
  100. package/src/components/ui/spinner.tsx +16 -0
  101. package/src/components/ui/switch.tsx +33 -0
  102. package/src/components/ui/table.tsx +116 -0
  103. package/src/components/ui/tabs.tsx +91 -0
  104. package/src/components/ui/textarea.tsx +18 -0
  105. package/src/components/ui/toggle-group.tsx +83 -0
  106. package/src/components/ui/toggle.tsx +47 -0
  107. package/src/components/ui/tooltip.tsx +57 -0
  108. package/src/components/ui/tree.tsx +207 -0
  109. package/src/hooks/use-mobile.ts +17 -0
  110. package/src/index.ts +16 -0
  111. package/src/lib/utils.ts +6 -0
  112. package/src/styles/globals.css +373 -0
  113. package/tsconfig.json +18 -0
@@ -0,0 +1,280 @@
1
+ "use client";
2
+
3
+ import type { RiveParameters } from "@rive-app/react-webgl2";
4
+ import type { FC, ReactNode } from "react";
5
+
6
+ import { cn } from "../../lib/utils";
7
+ import {
8
+ useRive,
9
+ useStateMachineInput,
10
+ useViewModel,
11
+ useViewModelInstance,
12
+ useViewModelInstanceColor,
13
+ } from "@rive-app/react-webgl2";
14
+ import { memo, useEffect, useMemo, useRef, useState } from "react";
15
+
16
+ export type PersonaState =
17
+ | "idle"
18
+ | "listening"
19
+ | "thinking"
20
+ | "speaking"
21
+ | "asleep";
22
+
23
+ interface PersonaProps {
24
+ state: PersonaState;
25
+ onLoad?: RiveParameters["onLoad"];
26
+ onLoadError?: RiveParameters["onLoadError"];
27
+ onReady?: () => void;
28
+ onPause?: RiveParameters["onPause"];
29
+ onPlay?: RiveParameters["onPlay"];
30
+ onStop?: RiveParameters["onStop"];
31
+ className?: string;
32
+ variant?: keyof typeof sources;
33
+ }
34
+
35
+ // The state machine name is always 'default' for Elements AI visuals
36
+ const stateMachine = "default";
37
+
38
+ const sources = {
39
+ command: {
40
+ dynamicColor: true,
41
+ hasModel: true,
42
+ source:
43
+ "https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/command-2.0.riv",
44
+ },
45
+ glint: {
46
+ dynamicColor: true,
47
+ hasModel: true,
48
+ source:
49
+ "https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/glint-2.0.riv",
50
+ },
51
+ halo: {
52
+ dynamicColor: true,
53
+ hasModel: true,
54
+ source:
55
+ "https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/halo-2.0.riv",
56
+ },
57
+ mana: {
58
+ dynamicColor: false,
59
+ hasModel: true,
60
+ source:
61
+ "https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/mana-2.0.riv",
62
+ },
63
+ obsidian: {
64
+ dynamicColor: true,
65
+ hasModel: true,
66
+ source:
67
+ "https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/obsidian-2.0.riv",
68
+ },
69
+ opal: {
70
+ dynamicColor: false,
71
+ hasModel: false,
72
+ source:
73
+ "https://ejiidnob33g9ap1r.public.blob.vercel-storage.com/orb-1.2.riv",
74
+ },
75
+ };
76
+
77
+ const getCurrentTheme = (): "light" | "dark" => {
78
+ if (typeof window !== "undefined") {
79
+ if (document.documentElement.classList.contains("dark")) {
80
+ return "dark";
81
+ }
82
+ if (window.matchMedia?.("(prefers-color-scheme: dark)").matches) {
83
+ return "dark";
84
+ }
85
+ }
86
+ return "light";
87
+ };
88
+
89
+ const useTheme = (enabled: boolean) => {
90
+ const [theme, setTheme] = useState<"light" | "dark">(getCurrentTheme);
91
+
92
+ useEffect(() => {
93
+ // Skip if not enabled (avoids unnecessary observers for non-dynamic-color variants)
94
+ if (!enabled) {
95
+ return;
96
+ }
97
+
98
+ // Watch for classList changes
99
+ const observer = new MutationObserver(() => {
100
+ setTheme(getCurrentTheme());
101
+ });
102
+
103
+ observer.observe(document.documentElement, {
104
+ attributeFilter: ["class"],
105
+ attributes: true,
106
+ });
107
+
108
+ // Watch for OS-level theme changes
109
+ let mql: MediaQueryList | null = null;
110
+ const handleMediaChange = () => {
111
+ setTheme(getCurrentTheme());
112
+ };
113
+
114
+ if (window.matchMedia) {
115
+ mql = window.matchMedia("(prefers-color-scheme: dark)");
116
+ mql.addEventListener("change", handleMediaChange);
117
+ }
118
+
119
+ return () => {
120
+ observer.disconnect();
121
+ if (mql) {
122
+ mql.removeEventListener("change", handleMediaChange);
123
+ }
124
+ };
125
+ }, [enabled]);
126
+
127
+ return theme;
128
+ };
129
+
130
+ interface PersonaWithModelProps {
131
+ rive: ReturnType<typeof useRive>["rive"];
132
+ source: (typeof sources)[keyof typeof sources];
133
+ children: React.ReactNode;
134
+ }
135
+
136
+ const PersonaWithModel = memo(
137
+ ({ rive, source, children }: PersonaWithModelProps) => {
138
+ const theme = useTheme(source.dynamicColor);
139
+ const viewModel = useViewModel(rive, { useDefault: true });
140
+ const viewModelInstance = useViewModelInstance(viewModel, {
141
+ rive,
142
+ useDefault: true,
143
+ });
144
+ const viewModelInstanceColor = useViewModelInstanceColor(
145
+ "color",
146
+ viewModelInstance
147
+ );
148
+
149
+ useEffect(() => {
150
+ if (!(viewModelInstanceColor && source.dynamicColor)) {
151
+ return;
152
+ }
153
+
154
+ const [r, g, b] = theme === "dark" ? [255, 255, 255] : [0, 0, 0];
155
+ viewModelInstanceColor.setRgb(r, g, b);
156
+ }, [viewModelInstanceColor, theme, source.dynamicColor]);
157
+
158
+ return children;
159
+ }
160
+ );
161
+
162
+ PersonaWithModel.displayName = "PersonaWithModel";
163
+
164
+ interface PersonaWithoutModelProps {
165
+ children: ReactNode;
166
+ }
167
+
168
+ const PersonaWithoutModel = memo(
169
+ ({ children }: PersonaWithoutModelProps) => children
170
+ );
171
+
172
+ PersonaWithoutModel.displayName = "PersonaWithoutModel";
173
+
174
+ export const Persona: FC<PersonaProps> = memo(
175
+ ({
176
+ variant = "obsidian",
177
+ state = "idle",
178
+ onLoad,
179
+ onLoadError,
180
+ onReady,
181
+ onPause,
182
+ onPlay,
183
+ onStop,
184
+ className,
185
+ }) => {
186
+ const source = sources[variant];
187
+
188
+ if (!source) {
189
+ throw new Error(`Invalid variant: ${variant}`);
190
+ }
191
+
192
+ // Stabilize callbacks to prevent useRive from reinitializing
193
+ const callbacksRef = useRef({
194
+ onLoad,
195
+ onLoadError,
196
+ onPause,
197
+ onPlay,
198
+ onReady,
199
+ onStop,
200
+ });
201
+
202
+ useEffect(() => {
203
+ callbacksRef.current = {
204
+ onLoad,
205
+ onLoadError,
206
+ onPause,
207
+ onPlay,
208
+ onReady,
209
+ onStop,
210
+ };
211
+ }, [onLoad, onLoadError, onPause, onPlay, onReady, onStop]);
212
+
213
+ const stableCallbacks = useMemo(
214
+ () => ({
215
+ onLoad: ((loadedRive) =>
216
+ callbacksRef.current.onLoad?.(
217
+ loadedRive
218
+ )) as RiveParameters["onLoad"],
219
+ onLoadError: ((err) =>
220
+ callbacksRef.current.onLoadError?.(
221
+ err
222
+ )) as RiveParameters["onLoadError"],
223
+ onPause: ((event) =>
224
+ callbacksRef.current.onPause?.(event)) as RiveParameters["onPause"],
225
+ onPlay: ((event) =>
226
+ callbacksRef.current.onPlay?.(event)) as RiveParameters["onPlay"],
227
+ onReady: () => callbacksRef.current.onReady?.(),
228
+ onStop: ((event) =>
229
+ callbacksRef.current.onStop?.(event)) as RiveParameters["onStop"],
230
+ }),
231
+ []
232
+ );
233
+
234
+ const { rive, RiveComponent } = useRive({
235
+ autoplay: true,
236
+ onLoad: stableCallbacks.onLoad,
237
+ onLoadError: stableCallbacks.onLoadError,
238
+ onPause: stableCallbacks.onPause,
239
+ onPlay: stableCallbacks.onPlay,
240
+ onRiveReady: stableCallbacks.onReady,
241
+ onStop: stableCallbacks.onStop,
242
+ src: source.source,
243
+ stateMachines: stateMachine,
244
+ });
245
+
246
+ const listeningInput = useStateMachineInput(
247
+ rive,
248
+ stateMachine,
249
+ "listening"
250
+ );
251
+ const thinkingInput = useStateMachineInput(rive, stateMachine, "thinking");
252
+ const speakingInput = useStateMachineInput(rive, stateMachine, "speaking");
253
+ const asleepInput = useStateMachineInput(rive, stateMachine, "asleep");
254
+
255
+ useEffect(() => {
256
+ if (listeningInput) {
257
+ listeningInput.value = state === "listening";
258
+ }
259
+ if (thinkingInput) {
260
+ thinkingInput.value = state === "thinking";
261
+ }
262
+ if (speakingInput) {
263
+ speakingInput.value = state === "speaking";
264
+ }
265
+ if (asleepInput) {
266
+ asleepInput.value = state === "asleep";
267
+ }
268
+ }, [state, listeningInput, thinkingInput, speakingInput, asleepInput]);
269
+
270
+ const Component = source.hasModel ? PersonaWithModel : PersonaWithoutModel;
271
+
272
+ return (
273
+ <Component rive={rive} source={source}>
274
+ <RiveComponent className={cn("size-16 shrink-0", className)} />
275
+ </Component>
276
+ );
277
+ }
278
+ );
279
+
280
+ Persona.displayName = "Persona";
@@ -0,0 +1,144 @@
1
+ "use client";
2
+
3
+ import type { ComponentProps } from "react";
4
+
5
+ import { Button } from "../ui/button";
6
+ import {
7
+ Card,
8
+ CardAction,
9
+ CardContent,
10
+ CardDescription,
11
+ CardFooter,
12
+ CardHeader,
13
+ CardTitle,
14
+ } from "../ui/card";
15
+ import {
16
+ Collapsible,
17
+ CollapsibleContent,
18
+ CollapsibleTrigger,
19
+ } from "../ui/collapsible";
20
+ import { cn } from "../../lib/utils";
21
+ import { ChevronsUpDownIcon } from "lucide-react";
22
+ import { createContext, useContext } from "react";
23
+
24
+ import { Shimmer } from "./shimmer";
25
+
26
+ interface PlanContextValue {
27
+ isStreaming: boolean;
28
+ }
29
+
30
+ const PlanContext = createContext<PlanContextValue | null>(null);
31
+
32
+ const usePlan = () => {
33
+ const context = useContext(PlanContext);
34
+ if (!context) {
35
+ throw new Error("Plan components must be used within Plan");
36
+ }
37
+ return context;
38
+ };
39
+
40
+ export type PlanProps = ComponentProps<typeof Collapsible> & {
41
+ isStreaming?: boolean;
42
+ };
43
+
44
+ export const Plan = ({
45
+ className,
46
+ isStreaming = false,
47
+ children,
48
+ ...props
49
+ }: PlanProps) => (
50
+ <PlanContext.Provider value={{ isStreaming }}>
51
+ <Collapsible asChild data-slot="plan" {...props}>
52
+ <Card className={cn("shadow-none", className)}>{children}</Card>
53
+ </Collapsible>
54
+ </PlanContext.Provider>
55
+ );
56
+
57
+ export type PlanHeaderProps = ComponentProps<typeof CardHeader>;
58
+
59
+ export const PlanHeader = ({ className, ...props }: PlanHeaderProps) => (
60
+ <CardHeader
61
+ className={cn("flex items-start justify-between", className)}
62
+ data-slot="plan-header"
63
+ {...props}
64
+ />
65
+ );
66
+
67
+ export type PlanTitleProps = Omit<
68
+ ComponentProps<typeof CardTitle>,
69
+ "children"
70
+ > & {
71
+ children: string;
72
+ };
73
+
74
+ export const PlanTitle = ({ children, ...props }: PlanTitleProps) => {
75
+ const { isStreaming } = usePlan();
76
+
77
+ return (
78
+ <CardTitle data-slot="plan-title" {...props}>
79
+ {isStreaming ? <Shimmer>{children}</Shimmer> : children}
80
+ </CardTitle>
81
+ );
82
+ };
83
+
84
+ export type PlanDescriptionProps = Omit<
85
+ ComponentProps<typeof CardDescription>,
86
+ "children"
87
+ > & {
88
+ children: string;
89
+ };
90
+
91
+ export const PlanDescription = ({
92
+ className,
93
+ children,
94
+ ...props
95
+ }: PlanDescriptionProps) => {
96
+ const { isStreaming } = usePlan();
97
+
98
+ return (
99
+ <CardDescription
100
+ className={cn("text-balance", className)}
101
+ data-slot="plan-description"
102
+ {...props}
103
+ >
104
+ {isStreaming ? <Shimmer>{children}</Shimmer> : children}
105
+ </CardDescription>
106
+ );
107
+ };
108
+
109
+ export type PlanActionProps = ComponentProps<typeof CardAction>;
110
+
111
+ export const PlanAction = (props: PlanActionProps) => (
112
+ <CardAction data-slot="plan-action" {...props} />
113
+ );
114
+
115
+ export type PlanContentProps = ComponentProps<typeof CardContent>;
116
+
117
+ export const PlanContent = (props: PlanContentProps) => (
118
+ <CollapsibleContent asChild>
119
+ <CardContent data-slot="plan-content" {...props} />
120
+ </CollapsibleContent>
121
+ );
122
+
123
+ export type PlanFooterProps = ComponentProps<"div">;
124
+
125
+ export const PlanFooter = (props: PlanFooterProps) => (
126
+ <CardFooter data-slot="plan-footer" {...props} />
127
+ );
128
+
129
+ export type PlanTriggerProps = ComponentProps<typeof CollapsibleTrigger>;
130
+
131
+ export const PlanTrigger = ({ className, ...props }: PlanTriggerProps) => (
132
+ <CollapsibleTrigger asChild>
133
+ <Button
134
+ className={cn("size-8", className)}
135
+ data-slot="plan-trigger"
136
+ size="icon"
137
+ variant="ghost"
138
+ {...props}
139
+ >
140
+ <ChevronsUpDownIcon className="size-4" />
141
+ <span className="sr-only">Toggle plan</span>
142
+ </Button>
143
+ </CollapsibleTrigger>
144
+ );