@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.
- package/README.md +135 -0
- package/components.json +21 -0
- package/dist/index.css +4241 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +59 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +2169 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2182 -0
- package/dist/index.mjs.map +1 -0
- package/example/.env.sample +2 -0
- package/example/App.tsx +368 -0
- package/example/app.css +1 -0
- package/example/index.html +12 -0
- package/example/main.tsx +9 -0
- package/example/vite.config.ts +34 -0
- package/package.json +75 -0
- package/postcss.config.cjs +3 -0
- package/src/components/ai-elements/agent.tsx +140 -0
- package/src/components/ai-elements/artifact.tsx +147 -0
- package/src/components/ai-elements/attachments.tsx +421 -0
- package/src/components/ai-elements/audio-player.tsx +228 -0
- package/src/components/ai-elements/canvas.tsx +22 -0
- package/src/components/ai-elements/chain-of-thought.tsx +228 -0
- package/src/components/ai-elements/checkpoint.tsx +71 -0
- package/src/components/ai-elements/code-block.tsx +532 -0
- package/src/components/ai-elements/commit.tsx +448 -0
- package/src/components/ai-elements/confirmation.tsx +176 -0
- package/src/components/ai-elements/connection.tsx +28 -0
- package/src/components/ai-elements/context.tsx +408 -0
- package/src/components/ai-elements/controls.tsx +18 -0
- package/src/components/ai-elements/conversation.tsx +100 -0
- package/src/components/ai-elements/edge.tsx +140 -0
- package/src/components/ai-elements/environment-variables.tsx +295 -0
- package/src/components/ai-elements/file-tree.tsx +258 -0
- package/src/components/ai-elements/image.tsx +24 -0
- package/src/components/ai-elements/inline-citation.tsx +287 -0
- package/src/components/ai-elements/message.tsx +336 -0
- package/src/components/ai-elements/mic-selector.tsx +370 -0
- package/src/components/ai-elements/model-selector.tsx +211 -0
- package/src/components/ai-elements/node.tsx +71 -0
- package/src/components/ai-elements/open-in-chat.tsx +365 -0
- package/src/components/ai-elements/package-info.tsx +233 -0
- package/src/components/ai-elements/panel.tsx +15 -0
- package/src/components/ai-elements/persona.tsx +270 -0
- package/src/components/ai-elements/plan.tsx +142 -0
- package/src/components/ai-elements/prompt-input.tsx +1263 -0
- package/src/components/ai-elements/queue.tsx +274 -0
- package/src/components/ai-elements/reasoning.tsx +193 -0
- package/src/components/ai-elements/sandbox.tsx +126 -0
- package/src/components/ai-elements/schema-display.tsx +458 -0
- package/src/components/ai-elements/shimmer.tsx +64 -0
- package/src/components/ai-elements/snippet.tsx +139 -0
- package/src/components/ai-elements/sources.tsx +77 -0
- package/src/components/ai-elements/speech-input.tsx +301 -0
- package/src/components/ai-elements/stack-trace.tsx +482 -0
- package/src/components/ai-elements/suggestion.tsx +53 -0
- package/src/components/ai-elements/task.tsx +87 -0
- package/src/components/ai-elements/terminal.tsx +261 -0
- package/src/components/ai-elements/test-results.tsx +485 -0
- package/src/components/ai-elements/tool.tsx +174 -0
- package/src/components/ai-elements/toolbar.tsx +16 -0
- package/src/components/ai-elements/transcription.tsx +124 -0
- package/src/components/ai-elements/voice-selector.tsx +479 -0
- package/src/components/ai-elements/web-preview.tsx +263 -0
- package/src/components/chat/Chat.tsx +178 -0
- package/src/components/chat/Input.tsx +98 -0
- package/src/components/chat/Message.tsx +276 -0
- package/src/components/chat/index.ts +2 -0
- package/src/components/index.ts +1 -0
- package/src/components/ui/accordion.tsx +64 -0
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/avatar.tsx +107 -0
- package/src/components/ui/badge.tsx +48 -0
- package/src/components/ui/button-group.tsx +83 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/carousel.tsx +239 -0
- package/src/components/ui/collapsible.tsx +31 -0
- package/src/components/ui/command.tsx +184 -0
- package/src/components/ui/dialog.tsx +158 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/hover-card.tsx +42 -0
- package/src/components/ui/input-group.tsx +168 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/popover.tsx +87 -0
- package/src/components/ui/progress.tsx +31 -0
- package/src/components/ui/scroll-area.tsx +56 -0
- package/src/components/ui/select.tsx +190 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/spinner.tsx +16 -0
- package/src/components/ui/switch.tsx +33 -0
- package/src/components/ui/tabs.tsx +91 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tooltip.tsx +61 -0
- package/src/css/global.css +123 -0
- package/src/css/index.css +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-copy-to-clipboard.ts +31 -0
- package/src/index.ts +4 -0
- package/src/lib/utils.ts +6 -0
- package/src/locales/context.ts +8 -0
- package/src/locales/hooks.ts +20 -0
- package/src/locales/index.ts +3 -0
- package/src/locales/langs/en.ts +17 -0
- package/src/locales/langs/index.ts +12 -0
- package/src/locales/langs/zh-cn.ts +18 -0
- package/tsconfig.json +21 -0
- package/tsup.config.ts +21 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Badge } from "@/components/ui/badge";
|
|
4
|
+
import {
|
|
5
|
+
Collapsible,
|
|
6
|
+
CollapsibleContent,
|
|
7
|
+
CollapsibleTrigger,
|
|
8
|
+
} from "@/components/ui/collapsible";
|
|
9
|
+
import { cn } from "@/lib/utils";
|
|
10
|
+
import { ChevronRightIcon } from "lucide-react";
|
|
11
|
+
import {
|
|
12
|
+
type ComponentProps,
|
|
13
|
+
createContext,
|
|
14
|
+
type HTMLAttributes,
|
|
15
|
+
useContext,
|
|
16
|
+
} from "react";
|
|
17
|
+
|
|
18
|
+
type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
19
|
+
|
|
20
|
+
interface SchemaParameter {
|
|
21
|
+
name: string;
|
|
22
|
+
type: string;
|
|
23
|
+
required?: boolean;
|
|
24
|
+
description?: string;
|
|
25
|
+
location?: "path" | "query" | "header";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface SchemaProperty {
|
|
29
|
+
name: string;
|
|
30
|
+
type: string;
|
|
31
|
+
required?: boolean;
|
|
32
|
+
description?: string;
|
|
33
|
+
properties?: SchemaProperty[];
|
|
34
|
+
items?: SchemaProperty;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface SchemaDisplayContextType {
|
|
38
|
+
method: HttpMethod;
|
|
39
|
+
path: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
parameters?: SchemaParameter[];
|
|
42
|
+
requestBody?: SchemaProperty[];
|
|
43
|
+
responseBody?: SchemaProperty[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const SchemaDisplayContext = createContext<SchemaDisplayContextType>({
|
|
47
|
+
method: "GET",
|
|
48
|
+
path: "",
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export type SchemaDisplayProps = HTMLAttributes<HTMLDivElement> & {
|
|
52
|
+
method: HttpMethod;
|
|
53
|
+
path: string;
|
|
54
|
+
description?: string;
|
|
55
|
+
parameters?: SchemaParameter[];
|
|
56
|
+
requestBody?: SchemaProperty[];
|
|
57
|
+
responseBody?: SchemaProperty[];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const SchemaDisplay = ({
|
|
61
|
+
method,
|
|
62
|
+
path,
|
|
63
|
+
description,
|
|
64
|
+
parameters,
|
|
65
|
+
requestBody,
|
|
66
|
+
responseBody,
|
|
67
|
+
className,
|
|
68
|
+
children,
|
|
69
|
+
...props
|
|
70
|
+
}: SchemaDisplayProps) => (
|
|
71
|
+
<SchemaDisplayContext.Provider
|
|
72
|
+
value={{ method, path, description, parameters, requestBody, responseBody }}
|
|
73
|
+
>
|
|
74
|
+
<div
|
|
75
|
+
className={cn(
|
|
76
|
+
"overflow-hidden rounded-lg border bg-background",
|
|
77
|
+
className
|
|
78
|
+
)}
|
|
79
|
+
{...props}
|
|
80
|
+
>
|
|
81
|
+
{children ?? (
|
|
82
|
+
<>
|
|
83
|
+
<SchemaDisplayHeader>
|
|
84
|
+
<div className="flex items-center gap-3">
|
|
85
|
+
<SchemaDisplayMethod />
|
|
86
|
+
<SchemaDisplayPath />
|
|
87
|
+
</div>
|
|
88
|
+
</SchemaDisplayHeader>
|
|
89
|
+
{description && <SchemaDisplayDescription />}
|
|
90
|
+
<SchemaDisplayContent>
|
|
91
|
+
{parameters && parameters.length > 0 && <SchemaDisplayParameters />}
|
|
92
|
+
{requestBody && requestBody.length > 0 && <SchemaDisplayRequest />}
|
|
93
|
+
{responseBody && responseBody.length > 0 && (
|
|
94
|
+
<SchemaDisplayResponse />
|
|
95
|
+
)}
|
|
96
|
+
</SchemaDisplayContent>
|
|
97
|
+
</>
|
|
98
|
+
)}
|
|
99
|
+
</div>
|
|
100
|
+
</SchemaDisplayContext.Provider>
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
export type SchemaDisplayHeaderProps = HTMLAttributes<HTMLDivElement>;
|
|
104
|
+
|
|
105
|
+
export const SchemaDisplayHeader = ({
|
|
106
|
+
className,
|
|
107
|
+
children,
|
|
108
|
+
...props
|
|
109
|
+
}: SchemaDisplayHeaderProps) => (
|
|
110
|
+
<div
|
|
111
|
+
className={cn("flex items-center gap-3 border-b px-4 py-3", className)}
|
|
112
|
+
{...props}
|
|
113
|
+
>
|
|
114
|
+
{children}
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const methodStyles: Record<HttpMethod, string> = {
|
|
119
|
+
GET: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
|
|
120
|
+
POST: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
|
|
121
|
+
PUT: "bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400",
|
|
122
|
+
PATCH:
|
|
123
|
+
"bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400",
|
|
124
|
+
DELETE: "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400",
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export type SchemaDisplayMethodProps = ComponentProps<typeof Badge>;
|
|
128
|
+
|
|
129
|
+
export const SchemaDisplayMethod = ({
|
|
130
|
+
className,
|
|
131
|
+
children,
|
|
132
|
+
...props
|
|
133
|
+
}: SchemaDisplayMethodProps) => {
|
|
134
|
+
const { method } = useContext(SchemaDisplayContext);
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<Badge
|
|
138
|
+
className={cn("font-mono text-xs", methodStyles[method], className)}
|
|
139
|
+
variant="secondary"
|
|
140
|
+
{...props}
|
|
141
|
+
>
|
|
142
|
+
{children ?? method}
|
|
143
|
+
</Badge>
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export type SchemaDisplayPathProps = HTMLAttributes<HTMLSpanElement>;
|
|
148
|
+
|
|
149
|
+
export const SchemaDisplayPath = ({
|
|
150
|
+
className,
|
|
151
|
+
children,
|
|
152
|
+
...props
|
|
153
|
+
}: SchemaDisplayPathProps) => {
|
|
154
|
+
const { path } = useContext(SchemaDisplayContext);
|
|
155
|
+
|
|
156
|
+
// Highlight path parameters
|
|
157
|
+
const highlightedPath = path.replace(
|
|
158
|
+
/\{([^}]+)\}/g,
|
|
159
|
+
'<span class="text-blue-600 dark:text-blue-400">{$1}</span>'
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<span
|
|
164
|
+
className={cn("font-mono text-sm", className)}
|
|
165
|
+
// biome-ignore lint/security/noDangerouslySetInnerHtml: "needed for parameter highlighting"
|
|
166
|
+
dangerouslySetInnerHTML={{ __html: children ?? highlightedPath }}
|
|
167
|
+
{...props}
|
|
168
|
+
/>
|
|
169
|
+
);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export type SchemaDisplayDescriptionProps =
|
|
173
|
+
HTMLAttributes<HTMLParagraphElement>;
|
|
174
|
+
|
|
175
|
+
export const SchemaDisplayDescription = ({
|
|
176
|
+
className,
|
|
177
|
+
children,
|
|
178
|
+
...props
|
|
179
|
+
}: SchemaDisplayDescriptionProps) => {
|
|
180
|
+
const { description } = useContext(SchemaDisplayContext);
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<p
|
|
184
|
+
className={cn(
|
|
185
|
+
"border-b px-4 py-3 text-muted-foreground text-sm",
|
|
186
|
+
className
|
|
187
|
+
)}
|
|
188
|
+
{...props}
|
|
189
|
+
>
|
|
190
|
+
{children ?? description}
|
|
191
|
+
</p>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export type SchemaDisplayContentProps = HTMLAttributes<HTMLDivElement>;
|
|
196
|
+
|
|
197
|
+
export const SchemaDisplayContent = ({
|
|
198
|
+
className,
|
|
199
|
+
children,
|
|
200
|
+
...props
|
|
201
|
+
}: SchemaDisplayContentProps) => (
|
|
202
|
+
<div className={cn("divide-y", className)} {...props}>
|
|
203
|
+
{children}
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
export type SchemaDisplayParametersProps = ComponentProps<typeof Collapsible>;
|
|
208
|
+
|
|
209
|
+
export const SchemaDisplayParameters = ({
|
|
210
|
+
className,
|
|
211
|
+
children,
|
|
212
|
+
...props
|
|
213
|
+
}: SchemaDisplayParametersProps) => {
|
|
214
|
+
const { parameters } = useContext(SchemaDisplayContext);
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<Collapsible className={cn(className)} defaultOpen {...props}>
|
|
218
|
+
<CollapsibleTrigger className="group flex w-full items-center gap-2 px-4 py-3 text-left transition-colors hover:bg-muted/50">
|
|
219
|
+
<ChevronRightIcon className="size-4 shrink-0 text-muted-foreground transition-transform group-data-[state=open]:rotate-90" />
|
|
220
|
+
<span className="font-medium text-sm">Parameters</span>
|
|
221
|
+
<Badge className="ml-auto text-xs" variant="secondary">
|
|
222
|
+
{parameters?.length}
|
|
223
|
+
</Badge>
|
|
224
|
+
</CollapsibleTrigger>
|
|
225
|
+
<CollapsibleContent>
|
|
226
|
+
<div className="divide-y border-t">
|
|
227
|
+
{children ??
|
|
228
|
+
parameters?.map((param) => (
|
|
229
|
+
<SchemaDisplayParameter key={param.name} {...param} />
|
|
230
|
+
))}
|
|
231
|
+
</div>
|
|
232
|
+
</CollapsibleContent>
|
|
233
|
+
</Collapsible>
|
|
234
|
+
);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export type SchemaDisplayParameterProps = HTMLAttributes<HTMLDivElement> &
|
|
238
|
+
SchemaParameter;
|
|
239
|
+
|
|
240
|
+
export const SchemaDisplayParameter = ({
|
|
241
|
+
name,
|
|
242
|
+
type,
|
|
243
|
+
required,
|
|
244
|
+
description,
|
|
245
|
+
location,
|
|
246
|
+
className,
|
|
247
|
+
...props
|
|
248
|
+
}: SchemaDisplayParameterProps) => (
|
|
249
|
+
<div className={cn("px-4 py-3 pl-10", className)} {...props}>
|
|
250
|
+
<div className="flex items-center gap-2">
|
|
251
|
+
<span className="font-mono text-sm">{name}</span>
|
|
252
|
+
<Badge className="text-xs" variant="outline">
|
|
253
|
+
{type}
|
|
254
|
+
</Badge>
|
|
255
|
+
{location && (
|
|
256
|
+
<Badge className="text-xs" variant="secondary">
|
|
257
|
+
{location}
|
|
258
|
+
</Badge>
|
|
259
|
+
)}
|
|
260
|
+
{required && (
|
|
261
|
+
<Badge
|
|
262
|
+
className="bg-red-100 text-red-700 text-xs dark:bg-red-900/30 dark:text-red-400"
|
|
263
|
+
variant="secondary"
|
|
264
|
+
>
|
|
265
|
+
required
|
|
266
|
+
</Badge>
|
|
267
|
+
)}
|
|
268
|
+
</div>
|
|
269
|
+
{description && (
|
|
270
|
+
<p className="mt-1 text-muted-foreground text-sm">{description}</p>
|
|
271
|
+
)}
|
|
272
|
+
</div>
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
export type SchemaDisplayRequestProps = ComponentProps<typeof Collapsible>;
|
|
276
|
+
|
|
277
|
+
export const SchemaDisplayRequest = ({
|
|
278
|
+
className,
|
|
279
|
+
children,
|
|
280
|
+
...props
|
|
281
|
+
}: SchemaDisplayRequestProps) => {
|
|
282
|
+
const { requestBody } = useContext(SchemaDisplayContext);
|
|
283
|
+
|
|
284
|
+
return (
|
|
285
|
+
<Collapsible className={cn(className)} defaultOpen {...props}>
|
|
286
|
+
<CollapsibleTrigger className="group flex w-full items-center gap-2 px-4 py-3 text-left transition-colors hover:bg-muted/50">
|
|
287
|
+
<ChevronRightIcon className="size-4 shrink-0 text-muted-foreground transition-transform group-data-[state=open]:rotate-90" />
|
|
288
|
+
<span className="font-medium text-sm">Request Body</span>
|
|
289
|
+
</CollapsibleTrigger>
|
|
290
|
+
<CollapsibleContent>
|
|
291
|
+
<div className="border-t">
|
|
292
|
+
{children ??
|
|
293
|
+
requestBody?.map((prop) => (
|
|
294
|
+
<SchemaDisplayProperty key={prop.name} {...prop} depth={0} />
|
|
295
|
+
))}
|
|
296
|
+
</div>
|
|
297
|
+
</CollapsibleContent>
|
|
298
|
+
</Collapsible>
|
|
299
|
+
);
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
export type SchemaDisplayResponseProps = ComponentProps<typeof Collapsible>;
|
|
303
|
+
|
|
304
|
+
export const SchemaDisplayResponse = ({
|
|
305
|
+
className,
|
|
306
|
+
children,
|
|
307
|
+
...props
|
|
308
|
+
}: SchemaDisplayResponseProps) => {
|
|
309
|
+
const { responseBody } = useContext(SchemaDisplayContext);
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
<Collapsible className={cn(className)} defaultOpen {...props}>
|
|
313
|
+
<CollapsibleTrigger className="group flex w-full items-center gap-2 px-4 py-3 text-left transition-colors hover:bg-muted/50">
|
|
314
|
+
<ChevronRightIcon className="size-4 shrink-0 text-muted-foreground transition-transform group-data-[state=open]:rotate-90" />
|
|
315
|
+
<span className="font-medium text-sm">Response</span>
|
|
316
|
+
</CollapsibleTrigger>
|
|
317
|
+
<CollapsibleContent>
|
|
318
|
+
<div className="border-t">
|
|
319
|
+
{children ??
|
|
320
|
+
responseBody?.map((prop) => (
|
|
321
|
+
<SchemaDisplayProperty key={prop.name} {...prop} depth={0} />
|
|
322
|
+
))}
|
|
323
|
+
</div>
|
|
324
|
+
</CollapsibleContent>
|
|
325
|
+
</Collapsible>
|
|
326
|
+
);
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
export type SchemaDisplayBodyProps = HTMLAttributes<HTMLDivElement>;
|
|
330
|
+
|
|
331
|
+
export const SchemaDisplayBody = ({
|
|
332
|
+
className,
|
|
333
|
+
children,
|
|
334
|
+
...props
|
|
335
|
+
}: SchemaDisplayBodyProps) => (
|
|
336
|
+
<div className={cn("divide-y", className)} {...props}>
|
|
337
|
+
{children}
|
|
338
|
+
</div>
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
export type SchemaDisplayPropertyProps = HTMLAttributes<HTMLDivElement> &
|
|
342
|
+
SchemaProperty & {
|
|
343
|
+
depth?: number;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
export const SchemaDisplayProperty = ({
|
|
347
|
+
name,
|
|
348
|
+
type,
|
|
349
|
+
required,
|
|
350
|
+
description,
|
|
351
|
+
properties,
|
|
352
|
+
items,
|
|
353
|
+
depth = 0,
|
|
354
|
+
className,
|
|
355
|
+
...props
|
|
356
|
+
}: SchemaDisplayPropertyProps) => {
|
|
357
|
+
const hasChildren = properties || items;
|
|
358
|
+
const paddingLeft = 40 + depth * 16;
|
|
359
|
+
|
|
360
|
+
if (hasChildren) {
|
|
361
|
+
return (
|
|
362
|
+
<Collapsible defaultOpen={depth < 2}>
|
|
363
|
+
<CollapsibleTrigger
|
|
364
|
+
className={cn(
|
|
365
|
+
"group flex w-full items-center gap-2 py-3 text-left transition-colors hover:bg-muted/50",
|
|
366
|
+
className
|
|
367
|
+
)}
|
|
368
|
+
style={{ paddingLeft }}
|
|
369
|
+
>
|
|
370
|
+
<ChevronRightIcon className="size-4 shrink-0 text-muted-foreground transition-transform group-data-[state=open]:rotate-90" />
|
|
371
|
+
<span className="font-mono text-sm">{name}</span>
|
|
372
|
+
<Badge className="text-xs" variant="outline">
|
|
373
|
+
{type}
|
|
374
|
+
</Badge>
|
|
375
|
+
{required && (
|
|
376
|
+
<Badge
|
|
377
|
+
className="bg-red-100 text-red-700 text-xs dark:bg-red-900/30 dark:text-red-400"
|
|
378
|
+
variant="secondary"
|
|
379
|
+
>
|
|
380
|
+
required
|
|
381
|
+
</Badge>
|
|
382
|
+
)}
|
|
383
|
+
</CollapsibleTrigger>
|
|
384
|
+
{description && (
|
|
385
|
+
<p
|
|
386
|
+
className="pb-2 text-muted-foreground text-sm"
|
|
387
|
+
style={{ paddingLeft: paddingLeft + 24 }}
|
|
388
|
+
>
|
|
389
|
+
{description}
|
|
390
|
+
</p>
|
|
391
|
+
)}
|
|
392
|
+
<CollapsibleContent>
|
|
393
|
+
<div className="divide-y border-t">
|
|
394
|
+
{properties?.map((prop) => (
|
|
395
|
+
<SchemaDisplayProperty
|
|
396
|
+
key={prop.name}
|
|
397
|
+
{...prop}
|
|
398
|
+
depth={depth + 1}
|
|
399
|
+
/>
|
|
400
|
+
))}
|
|
401
|
+
{items && (
|
|
402
|
+
<SchemaDisplayProperty
|
|
403
|
+
{...items}
|
|
404
|
+
depth={depth + 1}
|
|
405
|
+
name={`${name}[]`}
|
|
406
|
+
/>
|
|
407
|
+
)}
|
|
408
|
+
</div>
|
|
409
|
+
</CollapsibleContent>
|
|
410
|
+
</Collapsible>
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return (
|
|
415
|
+
<div
|
|
416
|
+
className={cn("py-3 pr-4", className)}
|
|
417
|
+
style={{ paddingLeft }}
|
|
418
|
+
{...props}
|
|
419
|
+
>
|
|
420
|
+
<div className="flex items-center gap-2">
|
|
421
|
+
<span className="size-4" /> {/* Spacer for alignment */}
|
|
422
|
+
<span className="font-mono text-sm">{name}</span>
|
|
423
|
+
<Badge className="text-xs" variant="outline">
|
|
424
|
+
{type}
|
|
425
|
+
</Badge>
|
|
426
|
+
{required && (
|
|
427
|
+
<Badge
|
|
428
|
+
className="bg-red-100 text-red-700 text-xs dark:bg-red-900/30 dark:text-red-400"
|
|
429
|
+
variant="secondary"
|
|
430
|
+
>
|
|
431
|
+
required
|
|
432
|
+
</Badge>
|
|
433
|
+
)}
|
|
434
|
+
</div>
|
|
435
|
+
{description && (
|
|
436
|
+
<p className="mt-1 pl-6 text-muted-foreground text-sm">{description}</p>
|
|
437
|
+
)}
|
|
438
|
+
</div>
|
|
439
|
+
);
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
export type SchemaDisplayExampleProps = HTMLAttributes<HTMLPreElement>;
|
|
443
|
+
|
|
444
|
+
export const SchemaDisplayExample = ({
|
|
445
|
+
className,
|
|
446
|
+
children,
|
|
447
|
+
...props
|
|
448
|
+
}: SchemaDisplayExampleProps) => (
|
|
449
|
+
<pre
|
|
450
|
+
className={cn(
|
|
451
|
+
"mx-4 mb-4 overflow-auto rounded-md bg-muted p-4 font-mono text-sm",
|
|
452
|
+
className
|
|
453
|
+
)}
|
|
454
|
+
{...props}
|
|
455
|
+
>
|
|
456
|
+
{children}
|
|
457
|
+
</pre>
|
|
458
|
+
);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import { motion } from "motion/react";
|
|
5
|
+
import {
|
|
6
|
+
type CSSProperties,
|
|
7
|
+
type ElementType,
|
|
8
|
+
type JSX,
|
|
9
|
+
memo,
|
|
10
|
+
useMemo,
|
|
11
|
+
} from "react";
|
|
12
|
+
|
|
13
|
+
export interface TextShimmerProps {
|
|
14
|
+
children: string;
|
|
15
|
+
as?: ElementType;
|
|
16
|
+
className?: string;
|
|
17
|
+
duration?: number;
|
|
18
|
+
spread?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const ShimmerComponent = ({
|
|
22
|
+
children,
|
|
23
|
+
as: Component = "p",
|
|
24
|
+
className,
|
|
25
|
+
duration = 2,
|
|
26
|
+
spread = 2,
|
|
27
|
+
}: TextShimmerProps) => {
|
|
28
|
+
const MotionComponent = motion.create(
|
|
29
|
+
Component as keyof JSX.IntrinsicElements
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const dynamicSpread = useMemo(
|
|
33
|
+
() => (children?.length ?? 0) * spread,
|
|
34
|
+
[children, spread]
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<MotionComponent
|
|
39
|
+
animate={{ backgroundPosition: "0% center" }}
|
|
40
|
+
className={cn(
|
|
41
|
+
"relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent",
|
|
42
|
+
"[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-background),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]",
|
|
43
|
+
className
|
|
44
|
+
)}
|
|
45
|
+
initial={{ backgroundPosition: "100% center" }}
|
|
46
|
+
style={
|
|
47
|
+
{
|
|
48
|
+
"--spread": `${dynamicSpread}px`,
|
|
49
|
+
backgroundImage:
|
|
50
|
+
"var(--bg), linear-gradient(var(--color-muted-foreground), var(--color-muted-foreground))",
|
|
51
|
+
} as CSSProperties
|
|
52
|
+
}
|
|
53
|
+
transition={{
|
|
54
|
+
repeat: Number.POSITIVE_INFINITY,
|
|
55
|
+
duration,
|
|
56
|
+
ease: "linear",
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{children}
|
|
60
|
+
</MotionComponent>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const Shimmer = memo(ShimmerComponent);
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
InputGroup,
|
|
5
|
+
InputGroupAddon,
|
|
6
|
+
InputGroupButton,
|
|
7
|
+
InputGroupInput,
|
|
8
|
+
InputGroupText,
|
|
9
|
+
} from "@/components/ui/input-group";
|
|
10
|
+
import { cn } from "@/lib/utils";
|
|
11
|
+
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
12
|
+
import {
|
|
13
|
+
type ComponentProps,
|
|
14
|
+
createContext,
|
|
15
|
+
useContext,
|
|
16
|
+
useEffect,
|
|
17
|
+
useRef,
|
|
18
|
+
useState,
|
|
19
|
+
} from "react";
|
|
20
|
+
|
|
21
|
+
interface SnippetContextType {
|
|
22
|
+
code: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const SnippetContext = createContext<SnippetContextType>({
|
|
26
|
+
code: "",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export type SnippetProps = ComponentProps<typeof InputGroup> & {
|
|
30
|
+
code: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const Snippet = ({
|
|
34
|
+
code,
|
|
35
|
+
className,
|
|
36
|
+
children,
|
|
37
|
+
...props
|
|
38
|
+
}: SnippetProps) => (
|
|
39
|
+
<SnippetContext.Provider value={{ code }}>
|
|
40
|
+
<InputGroup className={cn("font-mono", className)} {...props}>
|
|
41
|
+
{children}
|
|
42
|
+
</InputGroup>
|
|
43
|
+
</SnippetContext.Provider>
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
export type SnippetAddonProps = ComponentProps<typeof InputGroupAddon>;
|
|
47
|
+
|
|
48
|
+
export const SnippetAddon = (props: SnippetAddonProps) => (
|
|
49
|
+
<InputGroupAddon {...props} />
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
export type SnippetTextProps = ComponentProps<typeof InputGroupText>;
|
|
53
|
+
|
|
54
|
+
export const SnippetText = ({ className, ...props }: SnippetTextProps) => (
|
|
55
|
+
<InputGroupText
|
|
56
|
+
className={cn("pl-2 font-normal text-muted-foreground", className)}
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
export type SnippetInputProps = Omit<
|
|
62
|
+
ComponentProps<typeof InputGroupInput>,
|
|
63
|
+
"readOnly" | "value"
|
|
64
|
+
>;
|
|
65
|
+
|
|
66
|
+
export const SnippetInput = ({ className, ...props }: SnippetInputProps) => {
|
|
67
|
+
const { code } = useContext(SnippetContext);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<InputGroupInput
|
|
71
|
+
className={cn("text-foreground", className)}
|
|
72
|
+
readOnly
|
|
73
|
+
value={code}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export type SnippetCopyButtonProps = ComponentProps<typeof InputGroupButton> & {
|
|
80
|
+
onCopy?: () => void;
|
|
81
|
+
onError?: (error: Error) => void;
|
|
82
|
+
timeout?: number;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const SnippetCopyButton = ({
|
|
86
|
+
onCopy,
|
|
87
|
+
onError,
|
|
88
|
+
timeout = 2000,
|
|
89
|
+
children,
|
|
90
|
+
className,
|
|
91
|
+
...props
|
|
92
|
+
}: SnippetCopyButtonProps) => {
|
|
93
|
+
const [isCopied, setIsCopied] = useState(false);
|
|
94
|
+
const timeoutRef = useRef<number>(0);
|
|
95
|
+
const { code } = useContext(SnippetContext);
|
|
96
|
+
|
|
97
|
+
const copyToClipboard = async () => {
|
|
98
|
+
if (typeof window === "undefined" || !navigator?.clipboard?.writeText) {
|
|
99
|
+
onError?.(new Error("Clipboard API not available"));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
if (!isCopied) {
|
|
105
|
+
await navigator.clipboard.writeText(code);
|
|
106
|
+
setIsCopied(true);
|
|
107
|
+
onCopy?.();
|
|
108
|
+
timeoutRef.current = window.setTimeout(
|
|
109
|
+
() => setIsCopied(false),
|
|
110
|
+
timeout
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
onError?.(error as Error);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
useEffect(
|
|
119
|
+
() => () => {
|
|
120
|
+
window.clearTimeout(timeoutRef.current);
|
|
121
|
+
},
|
|
122
|
+
[]
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const Icon = isCopied ? CheckIcon : CopyIcon;
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<InputGroupButton
|
|
129
|
+
aria-label="Copy"
|
|
130
|
+
className={className}
|
|
131
|
+
onClick={copyToClipboard}
|
|
132
|
+
size="icon-sm"
|
|
133
|
+
title="Copy"
|
|
134
|
+
{...props}
|
|
135
|
+
>
|
|
136
|
+
{children ?? <Icon className="size-3.5" size={14} />}
|
|
137
|
+
</InputGroupButton>
|
|
138
|
+
);
|
|
139
|
+
};
|