@cloudbase/agent-react-ui 0.0.23

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