@cloudbase/agent-react-ui 1.0.1-alpha.32
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 +123 -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,482 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import {
|
|
6
|
+
Collapsible,
|
|
7
|
+
CollapsibleContent,
|
|
8
|
+
CollapsibleTrigger,
|
|
9
|
+
} from "@/components/ui/collapsible";
|
|
10
|
+
import { cn } from "@/lib/utils";
|
|
11
|
+
import {
|
|
12
|
+
AlertTriangleIcon,
|
|
13
|
+
CheckIcon,
|
|
14
|
+
ChevronDownIcon,
|
|
15
|
+
CopyIcon,
|
|
16
|
+
} from "lucide-react";
|
|
17
|
+
import type { ComponentProps } from "react";
|
|
18
|
+
import { createContext, memo, useContext, useMemo, useState } from "react";
|
|
19
|
+
|
|
20
|
+
// Regex patterns for parsing stack traces
|
|
21
|
+
const STACK_FRAME_WITH_PARENS_REGEX = /^at\s+(.+?)\s+\((.+):(\d+):(\d+)\)$/;
|
|
22
|
+
const STACK_FRAME_WITHOUT_FN_REGEX = /^at\s+(.+):(\d+):(\d+)$/;
|
|
23
|
+
const ERROR_TYPE_REGEX = /^(\w+Error|Error):\s*(.*)$/;
|
|
24
|
+
const AT_PREFIX_REGEX = /^at\s+/;
|
|
25
|
+
|
|
26
|
+
interface StackFrame {
|
|
27
|
+
raw: string;
|
|
28
|
+
functionName: string | null;
|
|
29
|
+
filePath: string | null;
|
|
30
|
+
lineNumber: number | null;
|
|
31
|
+
columnNumber: number | null;
|
|
32
|
+
isInternal: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface ParsedStackTrace {
|
|
36
|
+
errorType: string | null;
|
|
37
|
+
errorMessage: string;
|
|
38
|
+
frames: StackFrame[];
|
|
39
|
+
raw: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface StackTraceContextValue {
|
|
43
|
+
trace: ParsedStackTrace;
|
|
44
|
+
raw: string;
|
|
45
|
+
isOpen: boolean;
|
|
46
|
+
setIsOpen: (open: boolean) => void;
|
|
47
|
+
onFilePathClick?: (filePath: string, line?: number, column?: number) => void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const StackTraceContext = createContext<StackTraceContextValue | null>(null);
|
|
51
|
+
|
|
52
|
+
const useStackTrace = () => {
|
|
53
|
+
const context = useContext(StackTraceContext);
|
|
54
|
+
if (!context) {
|
|
55
|
+
throw new Error("StackTrace components must be used within StackTrace");
|
|
56
|
+
}
|
|
57
|
+
return context;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const parseStackFrame = (line: string): StackFrame => {
|
|
61
|
+
const trimmed = line.trim();
|
|
62
|
+
|
|
63
|
+
// Pattern: at functionName (filePath:line:column)
|
|
64
|
+
const withParensMatch = trimmed.match(STACK_FRAME_WITH_PARENS_REGEX);
|
|
65
|
+
if (withParensMatch) {
|
|
66
|
+
const [, functionName, filePath, lineNum, colNum] = withParensMatch;
|
|
67
|
+
const isInternal =
|
|
68
|
+
filePath.includes("node_modules") ||
|
|
69
|
+
filePath.startsWith("node:") ||
|
|
70
|
+
filePath.includes("internal/");
|
|
71
|
+
return {
|
|
72
|
+
raw: trimmed,
|
|
73
|
+
functionName: functionName ?? null,
|
|
74
|
+
filePath: filePath ?? null,
|
|
75
|
+
lineNumber: lineNum ? Number.parseInt(lineNum, 10) : null,
|
|
76
|
+
columnNumber: colNum ? Number.parseInt(colNum, 10) : null,
|
|
77
|
+
isInternal,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Pattern: at filePath:line:column (no function name)
|
|
82
|
+
const withoutFnMatch = trimmed.match(STACK_FRAME_WITHOUT_FN_REGEX);
|
|
83
|
+
if (withoutFnMatch) {
|
|
84
|
+
const [, filePath, lineNum, colNum] = withoutFnMatch;
|
|
85
|
+
const isInternal =
|
|
86
|
+
(filePath?.includes("node_modules") ?? false) ||
|
|
87
|
+
(filePath?.startsWith("node:") ?? false) ||
|
|
88
|
+
(filePath?.includes("internal/") ?? false);
|
|
89
|
+
return {
|
|
90
|
+
raw: trimmed,
|
|
91
|
+
functionName: null,
|
|
92
|
+
filePath: filePath ?? null,
|
|
93
|
+
lineNumber: lineNum ? Number.parseInt(lineNum, 10) : null,
|
|
94
|
+
columnNumber: colNum ? Number.parseInt(colNum, 10) : null,
|
|
95
|
+
isInternal,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Fallback: unparseable line
|
|
100
|
+
return {
|
|
101
|
+
raw: trimmed,
|
|
102
|
+
functionName: null,
|
|
103
|
+
filePath: null,
|
|
104
|
+
lineNumber: null,
|
|
105
|
+
columnNumber: null,
|
|
106
|
+
isInternal: trimmed.includes("node_modules") || trimmed.includes("node:"),
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const parseStackTrace = (trace: string): ParsedStackTrace => {
|
|
111
|
+
const lines = trace.split("\n").filter((line) => line.trim());
|
|
112
|
+
|
|
113
|
+
if (lines.length === 0) {
|
|
114
|
+
return {
|
|
115
|
+
errorType: null,
|
|
116
|
+
errorMessage: trace,
|
|
117
|
+
frames: [],
|
|
118
|
+
raw: trace,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const firstLine = lines[0].trim();
|
|
123
|
+
let errorType: string | null = null;
|
|
124
|
+
let errorMessage = firstLine;
|
|
125
|
+
|
|
126
|
+
// Try to extract error type from "ErrorType: message" format
|
|
127
|
+
const errorMatch = firstLine.match(ERROR_TYPE_REGEX);
|
|
128
|
+
if (errorMatch) {
|
|
129
|
+
errorType = errorMatch[1];
|
|
130
|
+
errorMessage = errorMatch[2] || "";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Parse stack frames (lines starting with "at")
|
|
134
|
+
const frames = lines
|
|
135
|
+
.slice(1)
|
|
136
|
+
.filter((line) => line.trim().startsWith("at "))
|
|
137
|
+
.map(parseStackFrame);
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
errorType,
|
|
141
|
+
errorMessage,
|
|
142
|
+
frames,
|
|
143
|
+
raw: trace,
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export type StackTraceProps = ComponentProps<"div"> & {
|
|
148
|
+
trace: string;
|
|
149
|
+
open?: boolean;
|
|
150
|
+
defaultOpen?: boolean;
|
|
151
|
+
onOpenChange?: (open: boolean) => void;
|
|
152
|
+
onFilePathClick?: (filePath: string, line?: number, column?: number) => void;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export const StackTrace = memo(
|
|
156
|
+
({
|
|
157
|
+
trace,
|
|
158
|
+
className,
|
|
159
|
+
open,
|
|
160
|
+
defaultOpen = false,
|
|
161
|
+
onOpenChange,
|
|
162
|
+
onFilePathClick,
|
|
163
|
+
children,
|
|
164
|
+
...props
|
|
165
|
+
}: StackTraceProps) => {
|
|
166
|
+
const [isOpen, setIsOpen] = useControllableState({
|
|
167
|
+
prop: open,
|
|
168
|
+
defaultProp: defaultOpen,
|
|
169
|
+
onChange: onOpenChange,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const parsedTrace = useMemo(() => parseStackTrace(trace), [trace]);
|
|
173
|
+
|
|
174
|
+
const contextValue = useMemo(
|
|
175
|
+
() => ({
|
|
176
|
+
trace: parsedTrace,
|
|
177
|
+
raw: trace,
|
|
178
|
+
isOpen,
|
|
179
|
+
setIsOpen,
|
|
180
|
+
onFilePathClick,
|
|
181
|
+
}),
|
|
182
|
+
[parsedTrace, trace, isOpen, setIsOpen, onFilePathClick]
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<StackTraceContext.Provider value={contextValue}>
|
|
187
|
+
<div
|
|
188
|
+
className={cn(
|
|
189
|
+
"not-prose w-full overflow-hidden rounded-lg border bg-background font-mono text-sm",
|
|
190
|
+
className
|
|
191
|
+
)}
|
|
192
|
+
{...props}
|
|
193
|
+
>
|
|
194
|
+
{children}
|
|
195
|
+
</div>
|
|
196
|
+
</StackTraceContext.Provider>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
export type StackTraceHeaderProps = ComponentProps<typeof CollapsibleTrigger>;
|
|
202
|
+
|
|
203
|
+
export const StackTraceHeader = memo(
|
|
204
|
+
({ className, children, ...props }: StackTraceHeaderProps) => {
|
|
205
|
+
const { isOpen, setIsOpen } = useStackTrace();
|
|
206
|
+
|
|
207
|
+
return (
|
|
208
|
+
<Collapsible onOpenChange={setIsOpen} open={isOpen}>
|
|
209
|
+
<CollapsibleTrigger asChild {...props}>
|
|
210
|
+
<div
|
|
211
|
+
className={cn(
|
|
212
|
+
"flex w-full cursor-pointer items-center gap-3 p-3 text-left transition-colors hover:bg-muted/50",
|
|
213
|
+
className
|
|
214
|
+
)}
|
|
215
|
+
>
|
|
216
|
+
{children}
|
|
217
|
+
</div>
|
|
218
|
+
</CollapsibleTrigger>
|
|
219
|
+
</Collapsible>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
export type StackTraceErrorProps = ComponentProps<"div">;
|
|
225
|
+
|
|
226
|
+
export const StackTraceError = memo(
|
|
227
|
+
({ className, children, ...props }: StackTraceErrorProps) => (
|
|
228
|
+
<div
|
|
229
|
+
className={cn(
|
|
230
|
+
"flex flex-1 items-center gap-2 overflow-hidden",
|
|
231
|
+
className
|
|
232
|
+
)}
|
|
233
|
+
{...props}
|
|
234
|
+
>
|
|
235
|
+
<AlertTriangleIcon className="size-4 shrink-0 text-destructive" />
|
|
236
|
+
{children}
|
|
237
|
+
</div>
|
|
238
|
+
)
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
export type StackTraceErrorTypeProps = ComponentProps<"span">;
|
|
242
|
+
|
|
243
|
+
export const StackTraceErrorType = memo(
|
|
244
|
+
({ className, children, ...props }: StackTraceErrorTypeProps) => {
|
|
245
|
+
const { trace } = useStackTrace();
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<span
|
|
249
|
+
className={cn("shrink-0 font-semibold text-destructive", className)}
|
|
250
|
+
{...props}
|
|
251
|
+
>
|
|
252
|
+
{children ?? trace.errorType}
|
|
253
|
+
</span>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
export type StackTraceErrorMessageProps = ComponentProps<"span">;
|
|
259
|
+
|
|
260
|
+
export const StackTraceErrorMessage = memo(
|
|
261
|
+
({ className, children, ...props }: StackTraceErrorMessageProps) => {
|
|
262
|
+
const { trace } = useStackTrace();
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<span className={cn("truncate text-foreground", className)} {...props}>
|
|
266
|
+
{children ?? trace.errorMessage}
|
|
267
|
+
</span>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
export type StackTraceActionsProps = ComponentProps<"div">;
|
|
273
|
+
|
|
274
|
+
export const StackTraceActions = memo(
|
|
275
|
+
({ className, children, ...props }: StackTraceActionsProps) => (
|
|
276
|
+
// biome-ignore lint/a11y/noNoninteractiveElementInteractions: stopPropagation required for nested interactions
|
|
277
|
+
// biome-ignore lint/a11y/useSemanticElements: fieldset doesn't fit this UI pattern
|
|
278
|
+
<div
|
|
279
|
+
className={cn("flex shrink-0 items-center gap-1", className)}
|
|
280
|
+
onClick={(e) => e.stopPropagation()}
|
|
281
|
+
onKeyDown={(e) => {
|
|
282
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
283
|
+
e.stopPropagation();
|
|
284
|
+
}
|
|
285
|
+
}}
|
|
286
|
+
role="group"
|
|
287
|
+
{...props}
|
|
288
|
+
>
|
|
289
|
+
{children}
|
|
290
|
+
</div>
|
|
291
|
+
)
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
export type StackTraceCopyButtonProps = ComponentProps<typeof Button> & {
|
|
295
|
+
onCopy?: () => void;
|
|
296
|
+
onError?: (error: Error) => void;
|
|
297
|
+
timeout?: number;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
export const StackTraceCopyButton = memo(
|
|
301
|
+
({
|
|
302
|
+
onCopy,
|
|
303
|
+
onError,
|
|
304
|
+
timeout = 2000,
|
|
305
|
+
className,
|
|
306
|
+
children,
|
|
307
|
+
...props
|
|
308
|
+
}: StackTraceCopyButtonProps) => {
|
|
309
|
+
const [isCopied, setIsCopied] = useState(false);
|
|
310
|
+
const { raw } = useStackTrace();
|
|
311
|
+
|
|
312
|
+
const copyToClipboard = async () => {
|
|
313
|
+
if (typeof window === "undefined" || !navigator?.clipboard?.writeText) {
|
|
314
|
+
onError?.(new Error("Clipboard API not available"));
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
await navigator.clipboard.writeText(raw);
|
|
320
|
+
setIsCopied(true);
|
|
321
|
+
onCopy?.();
|
|
322
|
+
setTimeout(() => setIsCopied(false), timeout);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
onError?.(error as Error);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const Icon = isCopied ? CheckIcon : CopyIcon;
|
|
329
|
+
|
|
330
|
+
return (
|
|
331
|
+
<Button
|
|
332
|
+
className={cn("size-7", className)}
|
|
333
|
+
onClick={copyToClipboard}
|
|
334
|
+
size="icon"
|
|
335
|
+
variant="ghost"
|
|
336
|
+
{...props}
|
|
337
|
+
>
|
|
338
|
+
{children ?? <Icon size={14} />}
|
|
339
|
+
</Button>
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
export type StackTraceExpandButtonProps = ComponentProps<"div">;
|
|
345
|
+
|
|
346
|
+
export const StackTraceExpandButton = memo(
|
|
347
|
+
({ className, ...props }: StackTraceExpandButtonProps) => {
|
|
348
|
+
const { isOpen } = useStackTrace();
|
|
349
|
+
|
|
350
|
+
return (
|
|
351
|
+
<div
|
|
352
|
+
className={cn("flex size-7 items-center justify-center", className)}
|
|
353
|
+
{...props}
|
|
354
|
+
>
|
|
355
|
+
<ChevronDownIcon
|
|
356
|
+
className={cn(
|
|
357
|
+
"size-4 text-muted-foreground transition-transform",
|
|
358
|
+
isOpen ? "rotate-180" : "rotate-0"
|
|
359
|
+
)}
|
|
360
|
+
/>
|
|
361
|
+
</div>
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
export type StackTraceContentProps = ComponentProps<
|
|
367
|
+
typeof CollapsibleContent
|
|
368
|
+
> & {
|
|
369
|
+
maxHeight?: number;
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
export const StackTraceContent = memo(
|
|
373
|
+
({
|
|
374
|
+
className,
|
|
375
|
+
maxHeight = 400,
|
|
376
|
+
children,
|
|
377
|
+
...props
|
|
378
|
+
}: StackTraceContentProps) => {
|
|
379
|
+
const { isOpen } = useStackTrace();
|
|
380
|
+
|
|
381
|
+
return (
|
|
382
|
+
<Collapsible open={isOpen}>
|
|
383
|
+
<CollapsibleContent
|
|
384
|
+
className={cn(
|
|
385
|
+
"overflow-auto border-t bg-muted/30",
|
|
386
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
387
|
+
className
|
|
388
|
+
)}
|
|
389
|
+
style={{ maxHeight }}
|
|
390
|
+
{...props}
|
|
391
|
+
>
|
|
392
|
+
{children}
|
|
393
|
+
</CollapsibleContent>
|
|
394
|
+
</Collapsible>
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
export type StackTraceFramesProps = ComponentProps<"div"> & {
|
|
400
|
+
showInternalFrames?: boolean;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
export const StackTraceFrames = memo(
|
|
404
|
+
({
|
|
405
|
+
className,
|
|
406
|
+
showInternalFrames = true,
|
|
407
|
+
...props
|
|
408
|
+
}: StackTraceFramesProps) => {
|
|
409
|
+
const { trace, onFilePathClick } = useStackTrace();
|
|
410
|
+
|
|
411
|
+
const framesToShow = showInternalFrames
|
|
412
|
+
? trace.frames
|
|
413
|
+
: trace.frames.filter((f) => !f.isInternal);
|
|
414
|
+
|
|
415
|
+
return (
|
|
416
|
+
<div className={cn("space-y-1 p-3", className)} {...props}>
|
|
417
|
+
{framesToShow.map((frame, index) => (
|
|
418
|
+
<div
|
|
419
|
+
className={cn(
|
|
420
|
+
"text-xs",
|
|
421
|
+
frame.isInternal
|
|
422
|
+
? "text-muted-foreground/50"
|
|
423
|
+
: "text-foreground/90"
|
|
424
|
+
)}
|
|
425
|
+
key={`${frame.raw}-${index}`}
|
|
426
|
+
>
|
|
427
|
+
<span className="text-muted-foreground">at </span>
|
|
428
|
+
{frame.functionName && (
|
|
429
|
+
<span className={frame.isInternal ? "" : "text-foreground"}>
|
|
430
|
+
{frame.functionName}{" "}
|
|
431
|
+
</span>
|
|
432
|
+
)}
|
|
433
|
+
{frame.filePath && (
|
|
434
|
+
<>
|
|
435
|
+
<span className="text-muted-foreground">(</span>
|
|
436
|
+
<button
|
|
437
|
+
className={cn(
|
|
438
|
+
"underline decoration-dotted hover:text-primary",
|
|
439
|
+
onFilePathClick && "cursor-pointer"
|
|
440
|
+
)}
|
|
441
|
+
disabled={!onFilePathClick}
|
|
442
|
+
onClick={() => {
|
|
443
|
+
if (frame.filePath) {
|
|
444
|
+
onFilePathClick?.(
|
|
445
|
+
frame.filePath,
|
|
446
|
+
frame.lineNumber ?? undefined,
|
|
447
|
+
frame.columnNumber ?? undefined
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
}}
|
|
451
|
+
type="button"
|
|
452
|
+
>
|
|
453
|
+
{frame.filePath}
|
|
454
|
+
{frame.lineNumber !== null && `:${frame.lineNumber}`}
|
|
455
|
+
{frame.columnNumber !== null && `:${frame.columnNumber}`}
|
|
456
|
+
</button>
|
|
457
|
+
<span className="text-muted-foreground">)</span>
|
|
458
|
+
</>
|
|
459
|
+
)}
|
|
460
|
+
{!(frame.filePath || frame.functionName) && (
|
|
461
|
+
<span>{frame.raw.replace(AT_PREFIX_REGEX, "")}</span>
|
|
462
|
+
)}
|
|
463
|
+
</div>
|
|
464
|
+
))}
|
|
465
|
+
{framesToShow.length === 0 && (
|
|
466
|
+
<div className="text-muted-foreground text-xs">No stack frames</div>
|
|
467
|
+
)}
|
|
468
|
+
</div>
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
StackTrace.displayName = "StackTrace";
|
|
474
|
+
StackTraceHeader.displayName = "StackTraceHeader";
|
|
475
|
+
StackTraceError.displayName = "StackTraceError";
|
|
476
|
+
StackTraceErrorType.displayName = "StackTraceErrorType";
|
|
477
|
+
StackTraceErrorMessage.displayName = "StackTraceErrorMessage";
|
|
478
|
+
StackTraceActions.displayName = "StackTraceActions";
|
|
479
|
+
StackTraceCopyButton.displayName = "StackTraceCopyButton";
|
|
480
|
+
StackTraceExpandButton.displayName = "StackTraceExpandButton";
|
|
481
|
+
StackTraceContent.displayName = "StackTraceContent";
|
|
482
|
+
StackTraceFrames.displayName = "StackTraceFrames";
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
import type { ComponentProps } from "react";
|
|
7
|
+
|
|
8
|
+
export type SuggestionsProps = ComponentProps<typeof ScrollArea>;
|
|
9
|
+
|
|
10
|
+
export const Suggestions = ({
|
|
11
|
+
className,
|
|
12
|
+
children,
|
|
13
|
+
...props
|
|
14
|
+
}: SuggestionsProps) => (
|
|
15
|
+
<ScrollArea className="w-full overflow-x-auto whitespace-nowrap" {...props}>
|
|
16
|
+
<div className={cn("flex w-max flex-nowrap items-center gap-2", className)}>
|
|
17
|
+
{children}
|
|
18
|
+
</div>
|
|
19
|
+
<ScrollBar className="hidden" orientation="horizontal" />
|
|
20
|
+
</ScrollArea>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export type SuggestionProps = Omit<ComponentProps<typeof Button>, "onClick"> & {
|
|
24
|
+
suggestion: string;
|
|
25
|
+
onClick?: (suggestion: string) => void;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const Suggestion = ({
|
|
29
|
+
suggestion,
|
|
30
|
+
onClick,
|
|
31
|
+
className,
|
|
32
|
+
variant = "outline",
|
|
33
|
+
size = "sm",
|
|
34
|
+
children,
|
|
35
|
+
...props
|
|
36
|
+
}: SuggestionProps) => {
|
|
37
|
+
const handleClick = () => {
|
|
38
|
+
onClick?.(suggestion);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Button
|
|
43
|
+
className={cn("cursor-pointer rounded-full px-4", className)}
|
|
44
|
+
onClick={handleClick}
|
|
45
|
+
size={size}
|
|
46
|
+
type="button"
|
|
47
|
+
variant={variant}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
{children || suggestion}
|
|
51
|
+
</Button>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Collapsible,
|
|
5
|
+
CollapsibleContent,
|
|
6
|
+
CollapsibleTrigger,
|
|
7
|
+
} from "@/components/ui/collapsible";
|
|
8
|
+
import { cn } from "@/lib/utils";
|
|
9
|
+
import { ChevronDownIcon, SearchIcon } from "lucide-react";
|
|
10
|
+
import type { ComponentProps } from "react";
|
|
11
|
+
|
|
12
|
+
export type TaskItemFileProps = ComponentProps<"div">;
|
|
13
|
+
|
|
14
|
+
export const TaskItemFile = ({
|
|
15
|
+
children,
|
|
16
|
+
className,
|
|
17
|
+
...props
|
|
18
|
+
}: TaskItemFileProps) => (
|
|
19
|
+
<div
|
|
20
|
+
className={cn(
|
|
21
|
+
"inline-flex items-center gap-1 rounded-md border bg-secondary px-1.5 py-0.5 text-foreground text-xs",
|
|
22
|
+
className
|
|
23
|
+
)}
|
|
24
|
+
{...props}
|
|
25
|
+
>
|
|
26
|
+
{children}
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export type TaskItemProps = ComponentProps<"div">;
|
|
31
|
+
|
|
32
|
+
export const TaskItem = ({ children, className, ...props }: TaskItemProps) => (
|
|
33
|
+
<div className={cn("text-muted-foreground text-sm", className)} {...props}>
|
|
34
|
+
{children}
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
export type TaskProps = ComponentProps<typeof Collapsible>;
|
|
39
|
+
|
|
40
|
+
export const Task = ({
|
|
41
|
+
defaultOpen = true,
|
|
42
|
+
className,
|
|
43
|
+
...props
|
|
44
|
+
}: TaskProps) => (
|
|
45
|
+
<Collapsible className={cn(className)} defaultOpen={defaultOpen} {...props} />
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
export type TaskTriggerProps = ComponentProps<typeof CollapsibleTrigger> & {
|
|
49
|
+
title: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const TaskTrigger = ({
|
|
53
|
+
children,
|
|
54
|
+
className,
|
|
55
|
+
title,
|
|
56
|
+
...props
|
|
57
|
+
}: TaskTriggerProps) => (
|
|
58
|
+
<CollapsibleTrigger asChild className={cn("group", className)} {...props}>
|
|
59
|
+
{children ?? (
|
|
60
|
+
<div className="flex w-full cursor-pointer items-center gap-2 text-muted-foreground text-sm transition-colors hover:text-foreground">
|
|
61
|
+
<SearchIcon className="size-4" />
|
|
62
|
+
<p className="text-sm">{title}</p>
|
|
63
|
+
<ChevronDownIcon className="size-4 transition-transform group-data-[state=open]:rotate-180" />
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
</CollapsibleTrigger>
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
export type TaskContentProps = ComponentProps<typeof CollapsibleContent>;
|
|
70
|
+
|
|
71
|
+
export const TaskContent = ({
|
|
72
|
+
children,
|
|
73
|
+
className,
|
|
74
|
+
...props
|
|
75
|
+
}: TaskContentProps) => (
|
|
76
|
+
<CollapsibleContent
|
|
77
|
+
className={cn(
|
|
78
|
+
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
79
|
+
className
|
|
80
|
+
)}
|
|
81
|
+
{...props}
|
|
82
|
+
>
|
|
83
|
+
<div className="mt-4 space-y-2 border-muted border-l-2 pl-4">
|
|
84
|
+
{children}
|
|
85
|
+
</div>
|
|
86
|
+
</CollapsibleContent>
|
|
87
|
+
);
|