@aomi-labs/widget-lib 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SHADCN-FETCH-GUIDE.md +105 -0
- package/components.json +10 -0
- package/dist/accordion.json +18 -0
- package/dist/alert.json +17 -0
- package/dist/aomi-frame.json +24 -0
- package/dist/assistant-thread-list.json +22 -0
- package/dist/assistant-thread.json +27 -0
- package/dist/assistant-threadlist-collapsible.json +23 -0
- package/dist/assistant-threadlist-sidebar.json +20 -0
- package/dist/assistant-tool-fallback.json +20 -0
- package/dist/avatar.json +17 -0
- package/dist/badge.json +17 -0
- package/dist/breadcrumb.json +17 -0
- package/dist/button.json +18 -0
- package/dist/card.json +15 -0
- package/dist/collapsible.json +17 -0
- package/dist/command.json +21 -0
- package/dist/dialog.json +18 -0
- package/dist/drawer.json +17 -0
- package/dist/input.json +15 -0
- package/dist/label.json +15 -0
- package/dist/notification.json +20 -0
- package/dist/popover.json +17 -0
- package/dist/registry.json +429 -0
- package/dist/separator.json +17 -0
- package/dist/sheet.json +18 -0
- package/dist/sidebar.json +18 -0
- package/dist/skeleton.json +15 -0
- package/dist/sonner.json +17 -0
- package/dist/tooltip.json +17 -0
- package/package.json +24 -88
- package/scripts/build-registry.js +74 -0
- package/src/components/aomi-frame.tsx +128 -0
- package/src/components/assistant-ui/attachment.tsx +235 -0
- package/src/components/assistant-ui/markdown-text.tsx +228 -0
- package/src/components/assistant-ui/thread-list.tsx +106 -0
- package/src/components/assistant-ui/thread.tsx +457 -0
- package/src/components/assistant-ui/threadlist-sidebar.tsx +71 -0
- package/src/components/assistant-ui/tool-fallback.tsx +48 -0
- package/src/components/assistant-ui/tooltip-icon-button.tsx +42 -0
- package/src/components/test/ThreadContextTest.tsx +204 -0
- package/src/components/tools/example-tool/ExampleTool.tsx +102 -0
- package/src/components/ui/accordion.tsx +58 -0
- package/src/components/ui/alert.tsx +62 -0
- package/src/components/ui/avatar.tsx +53 -0
- package/src/components/ui/badge.tsx +37 -0
- package/src/components/ui/breadcrumb.tsx +109 -0
- package/src/components/ui/button.tsx +59 -0
- package/src/components/ui/card.tsx +86 -0
- package/src/components/ui/collapsible.tsx +12 -0
- package/src/components/ui/command.tsx +156 -0
- package/src/components/ui/dialog.tsx +143 -0
- package/src/components/ui/drawer.tsx +118 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +20 -0
- package/src/components/ui/notification.tsx +57 -0
- package/src/components/ui/popover.tsx +33 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +139 -0
- package/src/components/ui/sidebar.tsx +820 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/ui/sonner.tsx +29 -0
- package/src/components/ui/tooltip.tsx +61 -0
- package/src/hooks/use-mobile.ts +21 -0
- package/src/index.ts +26 -0
- package/src/registry.ts +218 -0
- package/{dist/styles.css → src/themes/default.css} +21 -3
- package/src/themes/tokens.config.ts +39 -0
- package/tsconfig.json +19 -0
- package/README.md +0 -41
- package/dist/index.cjs +0 -3780
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -302
- package/dist/index.d.ts +0 -302
- package/dist/index.js +0 -3696
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useThreadContext } from "@aomi-labs/react";
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Test Component for Thread Context
|
|
9
|
+
*
|
|
10
|
+
* This component demonstrates and tests the ThreadContext functionality.
|
|
11
|
+
* Use this to verify Phase 2 is working correctly.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* 1. Wrap your app with <ThreadContextProvider>
|
|
15
|
+
* 2. Add <ThreadContextTest /> somewhere in your component tree
|
|
16
|
+
* 3. Click buttons to test thread operations
|
|
17
|
+
*/
|
|
18
|
+
export function ThreadContextTest() {
|
|
19
|
+
const {
|
|
20
|
+
currentThreadId,
|
|
21
|
+
setCurrentThreadId,
|
|
22
|
+
threads,
|
|
23
|
+
threadMetadata,
|
|
24
|
+
setThreadMessages,
|
|
25
|
+
updateThreadMetadata,
|
|
26
|
+
} = useThreadContext();
|
|
27
|
+
|
|
28
|
+
// Test: Create a new thread
|
|
29
|
+
const handleCreateThread = () => {
|
|
30
|
+
const newThreadId = `thread-${Date.now()}`;
|
|
31
|
+
|
|
32
|
+
// Add empty messages for new thread
|
|
33
|
+
setThreadMessages(newThreadId, []);
|
|
34
|
+
|
|
35
|
+
// Add metadata for new thread
|
|
36
|
+
updateThreadMetadata(newThreadId, {
|
|
37
|
+
title: `Thread ${newThreadId.slice(-4)}`,
|
|
38
|
+
status: "regular",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Switch to new thread
|
|
42
|
+
setCurrentThreadId(newThreadId);
|
|
43
|
+
|
|
44
|
+
console.log("✅ Created thread:", newThreadId);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Test: Switch thread
|
|
48
|
+
const handleSwitchThread = (threadId: string) => {
|
|
49
|
+
setCurrentThreadId(threadId);
|
|
50
|
+
console.log("✅ Switched to thread:", threadId);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Test: Archive current thread
|
|
54
|
+
const handleArchiveThread = () => {
|
|
55
|
+
updateThreadMetadata(currentThreadId, { status: "archived" });
|
|
56
|
+
console.log("✅ Archived thread:", currentThreadId);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Test: Unarchive current thread
|
|
60
|
+
const handleUnarchiveThread = () => {
|
|
61
|
+
updateThreadMetadata(currentThreadId, { status: "regular" });
|
|
62
|
+
console.log("✅ Unarchived thread:", currentThreadId);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Get current thread info
|
|
66
|
+
const currentMessages = threads.get(currentThreadId) || [];
|
|
67
|
+
const currentMeta = threadMetadata.get(currentThreadId);
|
|
68
|
+
|
|
69
|
+
// Get all thread IDs
|
|
70
|
+
const allThreadIds = Array.from(threads.keys());
|
|
71
|
+
const regularThreads = allThreadIds.filter(
|
|
72
|
+
(id) => threadMetadata.get(id)?.status === "regular",
|
|
73
|
+
);
|
|
74
|
+
const archivedThreads = allThreadIds.filter(
|
|
75
|
+
(id) => threadMetadata.get(id)?.status === "archived",
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<Card className="mx-auto my-8 w-full max-w-2xl">
|
|
80
|
+
<CardHeader>
|
|
81
|
+
<CardTitle>🧪 Thread Context Test</CardTitle>
|
|
82
|
+
</CardHeader>
|
|
83
|
+
<CardContent className="space-y-4">
|
|
84
|
+
{/* Current Thread Info */}
|
|
85
|
+
<div className="bg-muted/50 rounded border p-4">
|
|
86
|
+
<h3 className="mb-2 font-semibold">Current Thread</h3>
|
|
87
|
+
<div className="space-y-1 text-sm">
|
|
88
|
+
<p>
|
|
89
|
+
<strong>ID:</strong> {currentThreadId}
|
|
90
|
+
</p>
|
|
91
|
+
<p>
|
|
92
|
+
<strong>Title:</strong> {currentMeta?.title || "N/A"}
|
|
93
|
+
</p>
|
|
94
|
+
<p>
|
|
95
|
+
<strong>Status:</strong> {currentMeta?.status || "N/A"}
|
|
96
|
+
</p>
|
|
97
|
+
<p>
|
|
98
|
+
<strong>Messages:</strong> {currentMessages.length}
|
|
99
|
+
</p>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
{/* Actions */}
|
|
104
|
+
<div className="space-y-2">
|
|
105
|
+
<h3 className="font-semibold">Actions</h3>
|
|
106
|
+
<div className="flex flex-wrap gap-2">
|
|
107
|
+
<Button onClick={handleCreateThread} variant="default">
|
|
108
|
+
Create New Thread
|
|
109
|
+
</Button>
|
|
110
|
+
<Button
|
|
111
|
+
onClick={handleArchiveThread}
|
|
112
|
+
variant="outline"
|
|
113
|
+
disabled={currentMeta?.status === "archived"}
|
|
114
|
+
>
|
|
115
|
+
Archive Current
|
|
116
|
+
</Button>
|
|
117
|
+
<Button
|
|
118
|
+
onClick={handleUnarchiveThread}
|
|
119
|
+
variant="outline"
|
|
120
|
+
disabled={currentMeta?.status === "regular"}
|
|
121
|
+
>
|
|
122
|
+
Unarchive Current
|
|
123
|
+
</Button>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
{/* Thread List */}
|
|
128
|
+
<div className="space-y-2">
|
|
129
|
+
<h3 className="font-semibold">
|
|
130
|
+
Regular Threads ({regularThreads.length})
|
|
131
|
+
</h3>
|
|
132
|
+
<div className="space-y-1">
|
|
133
|
+
{regularThreads.map((threadId) => {
|
|
134
|
+
const meta = threadMetadata.get(threadId);
|
|
135
|
+
const isActive = threadId === currentThreadId;
|
|
136
|
+
return (
|
|
137
|
+
<button
|
|
138
|
+
key={threadId}
|
|
139
|
+
onClick={() => handleSwitchThread(threadId)}
|
|
140
|
+
className={`w-full rounded border px-3 py-2 text-left text-sm ${
|
|
141
|
+
isActive
|
|
142
|
+
? "bg-primary text-primary-foreground border-primary"
|
|
143
|
+
: "bg-background hover:bg-muted border-border"
|
|
144
|
+
}`}
|
|
145
|
+
>
|
|
146
|
+
{meta?.title || threadId}
|
|
147
|
+
{isActive && " (active)"}
|
|
148
|
+
</button>
|
|
149
|
+
);
|
|
150
|
+
})}
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
{archivedThreads.length > 0 && (
|
|
155
|
+
<div className="space-y-2">
|
|
156
|
+
<h3 className="font-semibold">
|
|
157
|
+
Archived Threads ({archivedThreads.length})
|
|
158
|
+
</h3>
|
|
159
|
+
<div className="space-y-1">
|
|
160
|
+
{archivedThreads.map((threadId) => {
|
|
161
|
+
const meta = threadMetadata.get(threadId);
|
|
162
|
+
const isActive = threadId === currentThreadId;
|
|
163
|
+
return (
|
|
164
|
+
<button
|
|
165
|
+
key={threadId}
|
|
166
|
+
onClick={() => handleSwitchThread(threadId)}
|
|
167
|
+
className={`w-full rounded border px-3 py-2 text-left text-sm opacity-60 ${
|
|
168
|
+
isActive
|
|
169
|
+
? "bg-primary text-primary-foreground border-primary"
|
|
170
|
+
: "bg-background hover:bg-muted border-border"
|
|
171
|
+
}`}
|
|
172
|
+
>
|
|
173
|
+
{meta?.title || threadId}
|
|
174
|
+
{isActive && " (active)"}
|
|
175
|
+
</button>
|
|
176
|
+
);
|
|
177
|
+
})}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
)}
|
|
181
|
+
|
|
182
|
+
{/* Debug Info */}
|
|
183
|
+
<details className="rounded border p-2 text-xs">
|
|
184
|
+
<summary className="mb-2 cursor-pointer font-semibold">
|
|
185
|
+
Debug Info
|
|
186
|
+
</summary>
|
|
187
|
+
<pre className="max-h-40 overflow-auto whitespace-pre-wrap">
|
|
188
|
+
{JSON.stringify(
|
|
189
|
+
{
|
|
190
|
+
currentThreadId,
|
|
191
|
+
totalThreads: allThreadIds.length,
|
|
192
|
+
regularCount: regularThreads.length,
|
|
193
|
+
archivedCount: archivedThreads.length,
|
|
194
|
+
threadIds: allThreadIds,
|
|
195
|
+
},
|
|
196
|
+
null,
|
|
197
|
+
2,
|
|
198
|
+
)}
|
|
199
|
+
</pre>
|
|
200
|
+
</details>
|
|
201
|
+
</CardContent>
|
|
202
|
+
</Card>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { makeAssistantToolUI } from "@assistant-ui/react";
|
|
4
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Example ToolUI Component
|
|
8
|
+
*
|
|
9
|
+
* This demonstrates how to create a custom UI for tool calls.
|
|
10
|
+
* Replace this with your actual tool implementation.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* 1. Define your tool args and result types
|
|
14
|
+
* 2. Update toolName to match your backend tool
|
|
15
|
+
* 3. Customize the render function with your UI
|
|
16
|
+
* 4. Import and render <ExampleTool /> in your page
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// Define the shape of your tool's arguments
|
|
20
|
+
type ExampleToolArgs = {
|
|
21
|
+
query: string;
|
|
22
|
+
limit?: number;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Define the shape of your tool's result
|
|
26
|
+
type ExampleToolResult = {
|
|
27
|
+
data: Array<{
|
|
28
|
+
id: string;
|
|
29
|
+
title: string;
|
|
30
|
+
description: string;
|
|
31
|
+
}>;
|
|
32
|
+
total: number;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const ExampleTool = makeAssistantToolUI<
|
|
36
|
+
ExampleToolArgs,
|
|
37
|
+
ExampleToolResult
|
|
38
|
+
>({
|
|
39
|
+
// IMPORTANT: Must match the tool name from your backend exactly!
|
|
40
|
+
toolName: "example_tool",
|
|
41
|
+
|
|
42
|
+
render: function ExampleToolUI({ args, argsText, result, status }) {
|
|
43
|
+
return (
|
|
44
|
+
<div className="my-4">
|
|
45
|
+
{/* Show the tool call */}
|
|
46
|
+
<pre className="text-muted-foreground mb-2 text-xs">
|
|
47
|
+
example_tool({argsText})
|
|
48
|
+
</pre>
|
|
49
|
+
|
|
50
|
+
<Card>
|
|
51
|
+
<CardHeader>
|
|
52
|
+
<CardTitle className="text-lg">
|
|
53
|
+
{status.type === "running" ? "Searching..." : "Search Results"}
|
|
54
|
+
</CardTitle>
|
|
55
|
+
</CardHeader>
|
|
56
|
+
<CardContent>
|
|
57
|
+
{/* Loading state */}
|
|
58
|
+
{status.type === "running" && (
|
|
59
|
+
<div className="flex items-center gap-2">
|
|
60
|
+
<div className="border-primary h-4 w-4 animate-spin rounded-full border-2 border-t-transparent" />
|
|
61
|
+
<span className="text-sm">
|
|
62
|
+
Searching for "{args.query}"...
|
|
63
|
+
</span>
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
|
|
67
|
+
{/* Results */}
|
|
68
|
+
{result && (
|
|
69
|
+
<div className="space-y-3">
|
|
70
|
+
<p className="text-muted-foreground text-sm">
|
|
71
|
+
Found {result.total} results
|
|
72
|
+
{args.limit && ` (showing top ${args.limit})`}
|
|
73
|
+
</p>
|
|
74
|
+
|
|
75
|
+
<div className="space-y-2">
|
|
76
|
+
{result.data.map((item) => (
|
|
77
|
+
<div
|
|
78
|
+
key={item.id}
|
|
79
|
+
className="hover:bg-muted/50 rounded-lg border p-3 transition-colors"
|
|
80
|
+
>
|
|
81
|
+
<h4 className="font-medium">{item.title}</h4>
|
|
82
|
+
<p className="text-muted-foreground mt-1 text-sm">
|
|
83
|
+
{item.description}
|
|
84
|
+
</p>
|
|
85
|
+
</div>
|
|
86
|
+
))}
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
)}
|
|
90
|
+
|
|
91
|
+
{/* Error state (if your result includes error) */}
|
|
92
|
+
{status.type === "incomplete" && (
|
|
93
|
+
<div className="text-destructive text-sm">
|
|
94
|
+
Failed to complete the search
|
|
95
|
+
</div>
|
|
96
|
+
)}
|
|
97
|
+
</CardContent>
|
|
98
|
+
</Card>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
},
|
|
102
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
|
5
|
+
import { ChevronDown } from "lucide-react";
|
|
6
|
+
|
|
7
|
+
import { cn } from "@aomi-labs/react";
|
|
8
|
+
|
|
9
|
+
const Accordion = AccordionPrimitive.Root;
|
|
10
|
+
|
|
11
|
+
const AccordionItem = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof AccordionPrimitive.Item>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
|
14
|
+
>(({ className, ...props }, ref) => (
|
|
15
|
+
<AccordionPrimitive.Item
|
|
16
|
+
ref={ref}
|
|
17
|
+
className={cn("border-b", className)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
));
|
|
21
|
+
AccordionItem.displayName = "AccordionItem";
|
|
22
|
+
|
|
23
|
+
const AccordionTrigger = React.forwardRef<
|
|
24
|
+
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
|
25
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
|
26
|
+
>(({ className, children, ...props }, ref) => (
|
|
27
|
+
<AccordionPrimitive.Header className="flex">
|
|
28
|
+
<AccordionPrimitive.Trigger
|
|
29
|
+
ref={ref}
|
|
30
|
+
className={cn(
|
|
31
|
+
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
|
32
|
+
className,
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
>
|
|
36
|
+
{children}
|
|
37
|
+
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
|
38
|
+
</AccordionPrimitive.Trigger>
|
|
39
|
+
</AccordionPrimitive.Header>
|
|
40
|
+
));
|
|
41
|
+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
|
42
|
+
|
|
43
|
+
const AccordionContent = React.forwardRef<
|
|
44
|
+
React.ElementRef<typeof AccordionPrimitive.Content>,
|
|
45
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
|
46
|
+
>(({ className, children, ...props }, ref) => (
|
|
47
|
+
<AccordionPrimitive.Content
|
|
48
|
+
ref={ref}
|
|
49
|
+
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm transition-all"
|
|
50
|
+
{...props}
|
|
51
|
+
>
|
|
52
|
+
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
|
53
|
+
</AccordionPrimitive.Content>
|
|
54
|
+
));
|
|
55
|
+
|
|
56
|
+
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
|
57
|
+
|
|
58
|
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@aomi-labs/react";
|
|
5
|
+
|
|
6
|
+
const alertVariants = cva(
|
|
7
|
+
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "bg-background text-foreground",
|
|
12
|
+
destructive:
|
|
13
|
+
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
variant: "default",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const Alert = React.forwardRef<
|
|
23
|
+
HTMLDivElement,
|
|
24
|
+
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
|
25
|
+
>(({ className, variant, ...props }, ref) => (
|
|
26
|
+
<div
|
|
27
|
+
ref={ref}
|
|
28
|
+
role="alert"
|
|
29
|
+
data-slot="alert"
|
|
30
|
+
className={cn(alertVariants({ variant }), className)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
));
|
|
34
|
+
Alert.displayName = "Alert";
|
|
35
|
+
|
|
36
|
+
const AlertTitle = React.forwardRef<
|
|
37
|
+
HTMLParagraphElement,
|
|
38
|
+
React.HTMLAttributes<HTMLHeadingElement>
|
|
39
|
+
>(({ className, ...props }, ref) => (
|
|
40
|
+
<h5
|
|
41
|
+
ref={ref}
|
|
42
|
+
data-slot="alert-title"
|
|
43
|
+
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
));
|
|
47
|
+
AlertTitle.displayName = "AlertTitle";
|
|
48
|
+
|
|
49
|
+
const AlertDescription = React.forwardRef<
|
|
50
|
+
HTMLParagraphElement,
|
|
51
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
52
|
+
>(({ className, ...props }, ref) => (
|
|
53
|
+
<div
|
|
54
|
+
ref={ref}
|
|
55
|
+
data-slot="alert-description"
|
|
56
|
+
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
));
|
|
60
|
+
AlertDescription.displayName = "AlertDescription";
|
|
61
|
+
|
|
62
|
+
export { Alert, AlertTitle, AlertDescription };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
5
|
+
|
|
6
|
+
import { cn } from "@aomi-labs/react";
|
|
7
|
+
|
|
8
|
+
function Avatar({
|
|
9
|
+
className,
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
|
12
|
+
return (
|
|
13
|
+
<AvatarPrimitive.Root
|
|
14
|
+
data-slot="avatar"
|
|
15
|
+
className={cn(
|
|
16
|
+
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
|
17
|
+
className,
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function AvatarImage({
|
|
25
|
+
className,
|
|
26
|
+
...props
|
|
27
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
|
28
|
+
return (
|
|
29
|
+
<AvatarPrimitive.Image
|
|
30
|
+
data-slot="avatar-image"
|
|
31
|
+
className={cn("aspect-square size-full", className)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function AvatarFallback({
|
|
38
|
+
className,
|
|
39
|
+
...props
|
|
40
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
|
41
|
+
return (
|
|
42
|
+
<AvatarPrimitive.Fallback
|
|
43
|
+
data-slot="avatar-fallback"
|
|
44
|
+
className={cn(
|
|
45
|
+
"bg-muted flex size-full items-center justify-center rounded-full",
|
|
46
|
+
className,
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { Avatar, AvatarImage, AvatarFallback };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@aomi-labs/react";
|
|
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
|
|
28
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
29
|
+
VariantProps<typeof badgeVariants> {}
|
|
30
|
+
|
|
31
|
+
function Badge({ className, variant, ...props }: BadgeProps) {
|
|
32
|
+
return (
|
|
33
|
+
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { ChevronRight, MoreHorizontal } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@aomi-labs/react";
|
|
6
|
+
|
|
7
|
+
function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
|
|
8
|
+
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
|
12
|
+
return (
|
|
13
|
+
<ol
|
|
14
|
+
data-slot="breadcrumb-list"
|
|
15
|
+
className={cn(
|
|
16
|
+
"text-muted-foreground flex flex-wrap items-center gap-1.5 break-words text-sm sm:gap-2.5",
|
|
17
|
+
className,
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
25
|
+
return (
|
|
26
|
+
<li
|
|
27
|
+
data-slot="breadcrumb-item"
|
|
28
|
+
className={cn("inline-flex items-center gap-1.5", className)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function BreadcrumbLink({
|
|
35
|
+
asChild,
|
|
36
|
+
className,
|
|
37
|
+
...props
|
|
38
|
+
}: React.ComponentProps<"a"> & {
|
|
39
|
+
asChild?: boolean;
|
|
40
|
+
}) {
|
|
41
|
+
const Comp = asChild ? Slot : "a";
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Comp
|
|
45
|
+
data-slot="breadcrumb-link"
|
|
46
|
+
className={cn("hover:text-foreground transition-colors", className)}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
|
53
|
+
return (
|
|
54
|
+
<span
|
|
55
|
+
data-slot="breadcrumb-page"
|
|
56
|
+
role="link"
|
|
57
|
+
aria-disabled="true"
|
|
58
|
+
aria-current="page"
|
|
59
|
+
className={cn("text-foreground font-normal", className)}
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function BreadcrumbSeparator({
|
|
66
|
+
children,
|
|
67
|
+
className,
|
|
68
|
+
...props
|
|
69
|
+
}: React.ComponentProps<"li">) {
|
|
70
|
+
return (
|
|
71
|
+
<li
|
|
72
|
+
data-slot="breadcrumb-separator"
|
|
73
|
+
role="presentation"
|
|
74
|
+
aria-hidden="true"
|
|
75
|
+
className={cn("[&>svg]:size-3.5", className)}
|
|
76
|
+
{...props}
|
|
77
|
+
>
|
|
78
|
+
{children ?? <ChevronRight />}
|
|
79
|
+
</li>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function BreadcrumbEllipsis({
|
|
84
|
+
className,
|
|
85
|
+
...props
|
|
86
|
+
}: React.ComponentProps<"span">) {
|
|
87
|
+
return (
|
|
88
|
+
<span
|
|
89
|
+
data-slot="breadcrumb-ellipsis"
|
|
90
|
+
role="presentation"
|
|
91
|
+
aria-hidden="true"
|
|
92
|
+
className={cn("flex size-9 items-center justify-center", className)}
|
|
93
|
+
{...props}
|
|
94
|
+
>
|
|
95
|
+
<MoreHorizontal className="size-4" />
|
|
96
|
+
<span className="sr-only">More</span>
|
|
97
|
+
</span>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
Breadcrumb,
|
|
103
|
+
BreadcrumbList,
|
|
104
|
+
BreadcrumbItem,
|
|
105
|
+
BreadcrumbLink,
|
|
106
|
+
BreadcrumbPage,
|
|
107
|
+
BreadcrumbSeparator,
|
|
108
|
+
BreadcrumbEllipsis,
|
|
109
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@aomi-labs/react";
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
14
|
+
destructive:
|
|
15
|
+
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
16
|
+
outline:
|
|
17
|
+
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
18
|
+
secondary:
|
|
19
|
+
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
20
|
+
ghost:
|
|
21
|
+
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
22
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
23
|
+
},
|
|
24
|
+
size: {
|
|
25
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
26
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
27
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
28
|
+
icon: "size-9",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
defaultVariants: {
|
|
32
|
+
variant: "default",
|
|
33
|
+
size: "default",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
function Button({
|
|
39
|
+
className,
|
|
40
|
+
variant,
|
|
41
|
+
size,
|
|
42
|
+
asChild = false,
|
|
43
|
+
...props
|
|
44
|
+
}: React.ComponentProps<"button"> &
|
|
45
|
+
VariantProps<typeof buttonVariants> & {
|
|
46
|
+
asChild?: boolean;
|
|
47
|
+
}) {
|
|
48
|
+
const Comp = asChild ? Slot : "button";
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Comp
|
|
52
|
+
data-slot="button"
|
|
53
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { Button, buttonVariants };
|