@codrstudio/openclaude-chat 0.1.0 → 0.2.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 (62) hide show
  1. package/dist/components/StreamingIndicator.js +5 -5
  2. package/dist/display/DisplayReactRenderer.js +12 -12
  3. package/dist/display/react-sandbox/bootstrap.js +150 -150
  4. package/dist/styles.css +1 -2
  5. package/package.json +64 -61
  6. package/src/components/Chat.tsx +107 -107
  7. package/src/components/ErrorNote.tsx +35 -35
  8. package/src/components/LazyRender.tsx +42 -42
  9. package/src/components/Markdown.tsx +114 -114
  10. package/src/components/MessageBubble.tsx +107 -107
  11. package/src/components/MessageInput.tsx +421 -421
  12. package/src/components/MessageList.tsx +153 -153
  13. package/src/components/StreamingIndicator.tsx +19 -19
  14. package/src/display/AlertRenderer.tsx +23 -23
  15. package/src/display/CarouselRenderer.tsx +141 -141
  16. package/src/display/ChartRenderer.tsx +195 -195
  17. package/src/display/ChoiceButtonsRenderer.tsx +114 -114
  18. package/src/display/CodeBlockRenderer.tsx +49 -49
  19. package/src/display/ComparisonTableRenderer.tsx +132 -132
  20. package/src/display/DataTableRenderer.tsx +144 -144
  21. package/src/display/DisplayReactRenderer.tsx +269 -269
  22. package/src/display/FileCardRenderer.tsx +55 -55
  23. package/src/display/GalleryRenderer.tsx +65 -65
  24. package/src/display/ImageViewerRenderer.tsx +114 -114
  25. package/src/display/LinkPreviewRenderer.tsx +74 -74
  26. package/src/display/MapViewRenderer.tsx +75 -75
  27. package/src/display/MetricCardRenderer.tsx +29 -29
  28. package/src/display/PriceHighlightRenderer.tsx +62 -62
  29. package/src/display/ProductCardRenderer.tsx +112 -112
  30. package/src/display/ProgressStepsRenderer.tsx +59 -59
  31. package/src/display/SourcesListRenderer.tsx +47 -47
  32. package/src/display/SpreadsheetRenderer.tsx +86 -86
  33. package/src/display/StepTimelineRenderer.tsx +75 -75
  34. package/src/display/index.ts +21 -21
  35. package/src/display/react-sandbox/bootstrap.ts +155 -155
  36. package/src/display/registry.ts +84 -84
  37. package/src/display/sdk-types.ts +217 -217
  38. package/src/hooks/ChatProvider.tsx +21 -21
  39. package/src/hooks/useIsMobile.ts +15 -15
  40. package/src/hooks/useOpenClaudeChat.ts +476 -476
  41. package/src/index.ts +76 -76
  42. package/src/lib/utils.ts +6 -6
  43. package/src/parts/PartErrorBoundary.tsx +51 -51
  44. package/src/parts/PartRenderer.tsx +145 -145
  45. package/src/parts/ReasoningBlock.tsx +41 -41
  46. package/src/parts/ToolActivity.tsx +78 -78
  47. package/src/parts/ToolResult.tsx +79 -79
  48. package/src/styles.css +2 -2
  49. package/src/types.ts +41 -41
  50. package/src/ui/alert.tsx +77 -77
  51. package/src/ui/badge.tsx +36 -36
  52. package/src/ui/button.tsx +54 -54
  53. package/src/ui/card.tsx +68 -68
  54. package/src/ui/collapsible.tsx +7 -7
  55. package/src/ui/dialog.tsx +122 -122
  56. package/src/ui/dropdown-menu.tsx +76 -76
  57. package/src/ui/input.tsx +24 -24
  58. package/src/ui/progress.tsx +36 -36
  59. package/src/ui/scroll-area.tsx +48 -48
  60. package/src/ui/separator.tsx +31 -31
  61. package/src/ui/skeleton.tsx +9 -9
  62. package/src/ui/table.tsx +114 -114
@@ -1,78 +1,78 @@
1
- import { Check, Download, FileText, FilePlus, FolderSearch, Globe, Loader2, Pencil, Search, Terminal, Wrench, type LucideIcon } from "lucide-react";
2
- import { cn } from "../lib/utils.js";
3
-
4
- export type ToolActivityState = "call" | "partial-call" | "result";
5
-
6
- export interface ToolActivityProps {
7
- toolName: string;
8
- state: ToolActivityState;
9
- args?: Record<string, unknown>;
10
- className?: string;
11
- iconMap?: Partial<Record<string, LucideIcon>>;
12
- }
13
-
14
- export const defaultToolIconMap: Record<string, LucideIcon> = {
15
- WebSearch: Globe,
16
- Bash: Terminal,
17
- Read: FileText,
18
- Edit: Pencil,
19
- Write: FilePlus,
20
- Grep: Search,
21
- Glob: FolderSearch,
22
- WebFetch: Download,
23
- ListDir: FolderSearch,
24
- };
25
-
26
- function formatToolName(name: string): string {
27
- return name
28
- .replace(/_/g, " ")
29
- .replace(/([a-z])([A-Z])/g, "$1 $2")
30
- .replace(/\b\w/g, (c) => c.toUpperCase());
31
- }
32
-
33
- function resolveIcon(toolName: string, iconMap: Record<string, LucideIcon | undefined>): LucideIcon {
34
- return iconMap[toolName] ?? Wrench;
35
- }
36
-
37
- function formatArgs(toolName: string, args?: Record<string, unknown>): string | null {
38
- if (!args) return null;
39
- if (toolName === "Bash" && args.command) return String(args.command);
40
- if (toolName === "Read" && args.path) return String(args.path);
41
- if (toolName === "Edit" && args.file_path) return String(args.file_path);
42
- if (toolName === "Write" && args.file_path) return String(args.file_path);
43
- if (toolName === "Grep" && args.pattern) return String(args.pattern);
44
- if (toolName === "Glob" && args.pattern) return String(args.pattern);
45
- if (toolName === "WebSearch" && args.query) return String(args.query);
46
- if (toolName === "ListDir" && args.path) return String(args.path);
47
- return null;
48
- }
49
-
50
- export function ToolActivity({ toolName, state, args, className, iconMap }: ToolActivityProps) {
51
- const mergedIconMap = iconMap ? { ...defaultToolIconMap, ...iconMap } : defaultToolIconMap;
52
- const Icon = resolveIcon(toolName, mergedIconMap);
53
- const isActive = state === "call" || state === "partial-call";
54
- const displayName = formatToolName(toolName);
55
- const argsPreview = formatArgs(toolName, args);
56
-
57
- return (
58
- <div className={cn(
59
- "flex items-center gap-2 rounded-md border border-border bg-muted/40 px-3 py-2 text-sm",
60
- className
61
- )}>
62
- <span className="text-muted-foreground shrink-0" aria-hidden="true">
63
- <Icon size={14} />
64
- </span>
65
- <span className="font-medium font-mono text-foreground">{displayName}</span>
66
- {argsPreview && (
67
- <span className="truncate text-xs text-muted-foreground font-mono max-w-[300px]">{argsPreview}</span>
68
- )}
69
- <span className="ml-auto text-muted-foreground">
70
- {isActive ? (
71
- <Loader2 size={14} className="animate-spin" aria-label="Executando..." />
72
- ) : (
73
- <Check size={14} aria-label="Concluido" />
74
- )}
75
- </span>
76
- </div>
77
- );
78
- }
1
+ import { Check, Download, FileText, FilePlus, FolderSearch, Globe, Loader2, Pencil, Search, Terminal, Wrench, type LucideIcon } from "lucide-react";
2
+ import { cn } from "../lib/utils.js";
3
+
4
+ export type ToolActivityState = "call" | "partial-call" | "result";
5
+
6
+ export interface ToolActivityProps {
7
+ toolName: string;
8
+ state: ToolActivityState;
9
+ args?: Record<string, unknown>;
10
+ className?: string;
11
+ iconMap?: Partial<Record<string, LucideIcon>>;
12
+ }
13
+
14
+ export const defaultToolIconMap: Record<string, LucideIcon> = {
15
+ WebSearch: Globe,
16
+ Bash: Terminal,
17
+ Read: FileText,
18
+ Edit: Pencil,
19
+ Write: FilePlus,
20
+ Grep: Search,
21
+ Glob: FolderSearch,
22
+ WebFetch: Download,
23
+ ListDir: FolderSearch,
24
+ };
25
+
26
+ function formatToolName(name: string): string {
27
+ return name
28
+ .replace(/_/g, " ")
29
+ .replace(/([a-z])([A-Z])/g, "$1 $2")
30
+ .replace(/\b\w/g, (c) => c.toUpperCase());
31
+ }
32
+
33
+ function resolveIcon(toolName: string, iconMap: Record<string, LucideIcon | undefined>): LucideIcon {
34
+ return iconMap[toolName] ?? Wrench;
35
+ }
36
+
37
+ function formatArgs(toolName: string, args?: Record<string, unknown>): string | null {
38
+ if (!args) return null;
39
+ if (toolName === "Bash" && args.command) return String(args.command);
40
+ if (toolName === "Read" && args.path) return String(args.path);
41
+ if (toolName === "Edit" && args.file_path) return String(args.file_path);
42
+ if (toolName === "Write" && args.file_path) return String(args.file_path);
43
+ if (toolName === "Grep" && args.pattern) return String(args.pattern);
44
+ if (toolName === "Glob" && args.pattern) return String(args.pattern);
45
+ if (toolName === "WebSearch" && args.query) return String(args.query);
46
+ if (toolName === "ListDir" && args.path) return String(args.path);
47
+ return null;
48
+ }
49
+
50
+ export function ToolActivity({ toolName, state, args, className, iconMap }: ToolActivityProps) {
51
+ const mergedIconMap = iconMap ? { ...defaultToolIconMap, ...iconMap } : defaultToolIconMap;
52
+ const Icon = resolveIcon(toolName, mergedIconMap);
53
+ const isActive = state === "call" || state === "partial-call";
54
+ const displayName = formatToolName(toolName);
55
+ const argsPreview = formatArgs(toolName, args);
56
+
57
+ return (
58
+ <div className={cn(
59
+ "flex items-center gap-2 rounded-md border border-border bg-muted/40 px-3 py-2 text-sm",
60
+ className
61
+ )}>
62
+ <span className="text-muted-foreground shrink-0" aria-hidden="true">
63
+ <Icon size={14} />
64
+ </span>
65
+ <span className="font-medium font-mono text-foreground">{displayName}</span>
66
+ {argsPreview && (
67
+ <span className="truncate text-xs text-muted-foreground font-mono max-w-[300px]">{argsPreview}</span>
68
+ )}
69
+ <span className="ml-auto text-muted-foreground">
70
+ {isActive ? (
71
+ <Loader2 size={14} className="animate-spin" aria-label="Executando..." />
72
+ ) : (
73
+ <Check size={14} aria-label="Concluido" />
74
+ )}
75
+ </span>
76
+ </div>
77
+ );
78
+ }
@@ -1,79 +1,79 @@
1
- import { AlertCircle, CheckCircle, ChevronDown, ChevronRight } from "lucide-react";
2
- import { useState } from "react";
3
- import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible.js";
4
- import { cn } from "../lib/utils.js";
5
-
6
- export interface ToolResultProps {
7
- toolName: string;
8
- result: unknown;
9
- isError?: boolean;
10
- className?: string;
11
- }
12
-
13
- function serializeResult(result: unknown): string {
14
- try {
15
- if (typeof result === "object" && result !== null && "value" in (result as Record<string, unknown>)) {
16
- return String((result as Record<string, unknown>).value);
17
- }
18
- return JSON.stringify(result, null, 2);
19
- } catch {
20
- return String(result);
21
- }
22
- }
23
-
24
- function extractPreview(result: unknown): string | null {
25
- if (typeof result === "string") return result.length > 120 ? result.slice(0, 120) + "..." : result;
26
- if (typeof result === "object" && result !== null) {
27
- const r = result as Record<string, unknown>;
28
- if (typeof r.value === "string") {
29
- const v = r.value as string;
30
- return v.length > 120 ? v.slice(0, 120) + "..." : v;
31
- }
32
- }
33
- return null;
34
- }
35
-
36
- export function ToolResult({ toolName, result, isError = false, className }: ToolResultProps) {
37
- const [expanded, setExpanded] = useState(false);
38
- const serialized = serializeResult(result);
39
- const preview = extractPreview(result);
40
-
41
- return (
42
- <Collapsible open={expanded} onOpenChange={setExpanded} className={className}>
43
- <div className={cn(
44
- "rounded-md border text-sm overflow-hidden",
45
- isError
46
- ? "border-destructive/40 bg-destructive/5"
47
- : "border-border bg-muted/40"
48
- )}>
49
- <CollapsibleTrigger
50
- className={cn(
51
- "flex items-center gap-2 w-full px-3 py-2 text-left font-medium hover:bg-muted/60 cursor-pointer",
52
- )}
53
- aria-expanded={expanded}
54
- >
55
- {isError ? (
56
- <AlertCircle className="h-3.5 w-3.5 shrink-0 text-destructive" aria-hidden="true" />
57
- ) : (
58
- <CheckCircle className="h-3.5 w-3.5 shrink-0 text-muted-foreground" aria-hidden="true" />
59
- )}
60
- <span className={cn("font-mono text-xs", isError ? "text-destructive" : "text-foreground")}>
61
- {isError ? `${toolName} erro` : `${toolName} ok`}
62
- </span>
63
- {!expanded && preview && (
64
- <span className="truncate text-xs text-muted-foreground font-mono max-w-[400px] ml-1">{preview}</span>
65
- )}
66
- {expanded
67
- ? <ChevronDown className="h-3.5 w-3.5 shrink-0 text-muted-foreground ml-auto" aria-hidden="true" />
68
- : <ChevronRight className="h-3.5 w-3.5 shrink-0 text-muted-foreground ml-auto" aria-hidden="true" />
69
- }
70
- </CollapsibleTrigger>
71
- <CollapsibleContent>
72
- <pre className="max-h-80 overflow-y-auto p-3 font-mono text-xs whitespace-pre-wrap break-all bg-muted/30 border-t border-border">
73
- {serialized}
74
- </pre>
75
- </CollapsibleContent>
76
- </div>
77
- </Collapsible>
78
- );
79
- }
1
+ import { AlertCircle, CheckCircle, ChevronDown, ChevronRight } from "lucide-react";
2
+ import { useState } from "react";
3
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible.js";
4
+ import { cn } from "../lib/utils.js";
5
+
6
+ export interface ToolResultProps {
7
+ toolName: string;
8
+ result: unknown;
9
+ isError?: boolean;
10
+ className?: string;
11
+ }
12
+
13
+ function serializeResult(result: unknown): string {
14
+ try {
15
+ if (typeof result === "object" && result !== null && "value" in (result as Record<string, unknown>)) {
16
+ return String((result as Record<string, unknown>).value);
17
+ }
18
+ return JSON.stringify(result, null, 2);
19
+ } catch {
20
+ return String(result);
21
+ }
22
+ }
23
+
24
+ function extractPreview(result: unknown): string | null {
25
+ if (typeof result === "string") return result.length > 120 ? result.slice(0, 120) + "..." : result;
26
+ if (typeof result === "object" && result !== null) {
27
+ const r = result as Record<string, unknown>;
28
+ if (typeof r.value === "string") {
29
+ const v = r.value as string;
30
+ return v.length > 120 ? v.slice(0, 120) + "..." : v;
31
+ }
32
+ }
33
+ return null;
34
+ }
35
+
36
+ export function ToolResult({ toolName, result, isError = false, className }: ToolResultProps) {
37
+ const [expanded, setExpanded] = useState(false);
38
+ const serialized = serializeResult(result);
39
+ const preview = extractPreview(result);
40
+
41
+ return (
42
+ <Collapsible open={expanded} onOpenChange={setExpanded} className={className}>
43
+ <div className={cn(
44
+ "rounded-md border text-sm overflow-hidden",
45
+ isError
46
+ ? "border-destructive/40 bg-destructive/5"
47
+ : "border-border bg-muted/40"
48
+ )}>
49
+ <CollapsibleTrigger
50
+ className={cn(
51
+ "flex items-center gap-2 w-full px-3 py-2 text-left font-medium hover:bg-muted/60 cursor-pointer",
52
+ )}
53
+ aria-expanded={expanded}
54
+ >
55
+ {isError ? (
56
+ <AlertCircle className="h-3.5 w-3.5 shrink-0 text-destructive" aria-hidden="true" />
57
+ ) : (
58
+ <CheckCircle className="h-3.5 w-3.5 shrink-0 text-muted-foreground" aria-hidden="true" />
59
+ )}
60
+ <span className={cn("font-mono text-xs", isError ? "text-destructive" : "text-foreground")}>
61
+ {isError ? `${toolName} erro` : `${toolName} ok`}
62
+ </span>
63
+ {!expanded && preview && (
64
+ <span className="truncate text-xs text-muted-foreground font-mono max-w-[400px] ml-1">{preview}</span>
65
+ )}
66
+ {expanded
67
+ ? <ChevronDown className="h-3.5 w-3.5 shrink-0 text-muted-foreground ml-auto" aria-hidden="true" />
68
+ : <ChevronRight className="h-3.5 w-3.5 shrink-0 text-muted-foreground ml-auto" aria-hidden="true" />
69
+ }
70
+ </CollapsibleTrigger>
71
+ <CollapsibleContent>
72
+ <pre className="max-h-80 overflow-y-auto p-3 font-mono text-xs whitespace-pre-wrap break-all bg-muted/30 border-t border-border">
73
+ {serialized}
74
+ </pre>
75
+ </CollapsibleContent>
76
+ </div>
77
+ </Collapsible>
78
+ );
79
+ }
package/src/styles.css CHANGED
@@ -1,2 +1,2 @@
1
- @import "tailwindcss/utilities" layer(utilities);
2
- @source "./**/*.tsx";
1
+ @import "tailwindcss/utilities" layer(utilities);
2
+ @source "./**/*.tsx";
package/src/types.ts CHANGED
@@ -1,41 +1,41 @@
1
- // Tipos locais usados pelo @codrstudio/openclaude-chat.
2
- // Nao depende de @ai-sdk/react. Modelo inspirado nos "parts" do ai-sdk,
3
- // mas alimentado a partir de SDKMessage do @codrstudio/openclaude-sdk.
4
-
5
- export type TextPart = { type: "text"; text: string };
6
-
7
- export type ReasoningPart = { type: "reasoning"; reasoning: string };
8
-
9
- export type ToolInvocationState = "call" | "partial-call" | "result";
10
-
11
- export type ToolInvocationPart = {
12
- type: "tool-invocation";
13
- toolInvocation: {
14
- toolName: string;
15
- toolCallId: string;
16
- state: ToolInvocationState;
17
- args?: Record<string, unknown>;
18
- result?: unknown;
19
- isError?: boolean;
20
- };
21
- };
22
-
23
- export type ImageAttachmentPart = { type: "image"; _ref?: string; mimeType?: string };
24
- export type FileAttachmentPart = { type: "file"; _ref?: string; mimeType?: string };
25
-
26
- export type MessagePart =
27
- | TextPart
28
- | ReasoningPart
29
- | ToolInvocationPart
30
- | ImageAttachmentPart
31
- | FileAttachmentPart
32
- | { type: string };
33
-
34
- export type MessageRole = "user" | "assistant";
35
-
36
- export interface Message {
37
- id: string;
38
- role: MessageRole;
39
- content: string;
40
- parts: MessagePart[];
41
- }
1
+ // Tipos locais usados pelo @codrstudio/openclaude-chat.
2
+ // Nao depende de @ai-sdk/react. Modelo inspirado nos "parts" do ai-sdk,
3
+ // mas alimentado a partir de SDKMessage do @codrstudio/openclaude-sdk.
4
+
5
+ export type TextPart = { type: "text"; text: string };
6
+
7
+ export type ReasoningPart = { type: "reasoning"; reasoning: string };
8
+
9
+ export type ToolInvocationState = "call" | "partial-call" | "result";
10
+
11
+ export type ToolInvocationPart = {
12
+ type: "tool-invocation";
13
+ toolInvocation: {
14
+ toolName: string;
15
+ toolCallId: string;
16
+ state: ToolInvocationState;
17
+ args?: Record<string, unknown>;
18
+ result?: unknown;
19
+ isError?: boolean;
20
+ };
21
+ };
22
+
23
+ export type ImageAttachmentPart = { type: "image"; _ref?: string; mimeType?: string };
24
+ export type FileAttachmentPart = { type: "file"; _ref?: string; mimeType?: string };
25
+
26
+ export type MessagePart =
27
+ | TextPart
28
+ | ReasoningPart
29
+ | ToolInvocationPart
30
+ | ImageAttachmentPart
31
+ | FileAttachmentPart
32
+ | { type: string };
33
+
34
+ export type MessageRole = "user" | "assistant";
35
+
36
+ export interface Message {
37
+ id: string;
38
+ role: MessageRole;
39
+ content: string;
40
+ parts: MessagePart[];
41
+ }
package/src/ui/alert.tsx CHANGED
@@ -1,77 +1,77 @@
1
- import * as React from "react";
2
- import { cva, type VariantProps } from "class-variance-authority";
3
- import { cn } from "../lib/utils.js";
4
-
5
- const alertVariants = cva(
6
- "group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
7
- {
8
- variants: {
9
- variant: {
10
- default: "bg-card text-card-foreground",
11
- destructive:
12
- "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
13
- },
14
- },
15
- defaultVariants: {
16
- variant: "default",
17
- },
18
- }
19
- );
20
-
21
- function Alert({
22
- className,
23
- variant,
24
- ...props
25
- }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
26
- return (
27
- <div
28
- data-slot="alert"
29
- role="alert"
30
- className={cn(alertVariants({ variant }), className)}
31
- {...props}
32
- />
33
- );
34
- }
35
-
36
- function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
37
- return (
38
- <div
39
- data-slot="alert-title"
40
- className={cn(
41
- "font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
42
- className
43
- )}
44
- {...props}
45
- />
46
- );
47
- }
48
-
49
- function AlertDescription({
50
- className,
51
- ...props
52
- }: React.ComponentProps<"div">) {
53
- return (
54
- <div
55
- data-slot="alert-description"
56
- className={cn(
57
- "text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
58
- className
59
- )}
60
- {...props}
61
- />
62
- );
63
- }
64
-
65
- function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
66
- return (
67
- <div
68
- data-slot="alert-action"
69
- className={cn("absolute top-2 right-2", className)}
70
- {...props}
71
- />
72
- );
73
- }
74
-
75
- export { Alert, AlertTitle, AlertDescription, AlertAction };
76
- export type { VariantProps };
77
- export { alertVariants };
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "../lib/utils.js";
4
+
5
+ const alertVariants = cva(
6
+ "group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "bg-card text-card-foreground",
11
+ destructive:
12
+ "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
13
+ },
14
+ },
15
+ defaultVariants: {
16
+ variant: "default",
17
+ },
18
+ }
19
+ );
20
+
21
+ function Alert({
22
+ className,
23
+ variant,
24
+ ...props
25
+ }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
26
+ return (
27
+ <div
28
+ data-slot="alert"
29
+ role="alert"
30
+ className={cn(alertVariants({ variant }), className)}
31
+ {...props}
32
+ />
33
+ );
34
+ }
35
+
36
+ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
37
+ return (
38
+ <div
39
+ data-slot="alert-title"
40
+ className={cn(
41
+ "font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
42
+ className
43
+ )}
44
+ {...props}
45
+ />
46
+ );
47
+ }
48
+
49
+ function AlertDescription({
50
+ className,
51
+ ...props
52
+ }: React.ComponentProps<"div">) {
53
+ return (
54
+ <div
55
+ data-slot="alert-description"
56
+ className={cn(
57
+ "text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
58
+ className
59
+ )}
60
+ {...props}
61
+ />
62
+ );
63
+ }
64
+
65
+ function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
66
+ return (
67
+ <div
68
+ data-slot="alert-action"
69
+ className={cn("absolute top-2 right-2", className)}
70
+ {...props}
71
+ />
72
+ );
73
+ }
74
+
75
+ export { Alert, AlertTitle, AlertDescription, AlertAction };
76
+ export type { VariantProps };
77
+ export { alertVariants };
package/src/ui/badge.tsx CHANGED
@@ -1,36 +1,36 @@
1
- import { cva, type VariantProps } from "class-variance-authority"
2
- import * as React from "react"
3
-
4
- import { cn } from "../lib/utils"
5
-
6
- const badgeVariants = cva(
7
- "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8
- {
9
- variants: {
10
- variant: {
11
- default:
12
- "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13
- secondary:
14
- "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15
- destructive:
16
- "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17
- outline: "text-foreground",
18
- },
19
- },
20
- defaultVariants: {
21
- variant: "default",
22
- },
23
- }
24
- )
25
-
26
- export interface BadgeProps
27
- extends React.HTMLAttributes<HTMLDivElement>,
28
- VariantProps<typeof badgeVariants> {}
29
-
30
- function Badge({ className, variant, ...props }: BadgeProps) {
31
- return (
32
- <div className={cn(badgeVariants({ variant }), className)} {...props} />
33
- )
34
- }
35
-
36
- export { Badge, badgeVariants }
1
+ import { cva, type VariantProps } from "class-variance-authority"
2
+ import * as React from "react"
3
+
4
+ import { cn } from "../lib/utils"
5
+
6
+ const badgeVariants = cva(
7
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default:
12
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13
+ secondary:
14
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15
+ destructive:
16
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17
+ outline: "text-foreground",
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ variant: "default",
22
+ },
23
+ }
24
+ )
25
+
26
+ export interface BadgeProps
27
+ extends React.HTMLAttributes<HTMLDivElement>,
28
+ VariantProps<typeof badgeVariants> {}
29
+
30
+ function Badge({ className, variant, ...props }: BadgeProps) {
31
+ return (
32
+ <div className={cn(badgeVariants({ variant }), className)} {...props} />
33
+ )
34
+ }
35
+
36
+ export { Badge, badgeVariants }