@multicomma/arkcn 0.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/README.md +78 -0
- package/bin/arkcn.mjs +78 -0
- package/cli/commands.mjs +251 -0
- package/cli/project.mjs +168 -0
- package/cli/registry.mjs +50 -0
- package/cli/ui.mjs +46 -0
- package/mcp/server.mjs +149 -0
- package/package.json +201 -0
- package/registry/base.css +680 -0
- package/registry/index.json +1833 -0
- package/registry/items/accordion.json +18 -0
- package/registry/items/activity-feed.json +18 -0
- package/registry/items/alert-dialog.json +18 -0
- package/registry/items/alert.json +17 -0
- package/registry/items/angle-slider.json +17 -0
- package/registry/items/app-shell.json +28 -0
- package/registry/items/aspect-ratio.json +13 -0
- package/registry/items/attachment.json +19 -0
- package/registry/items/avatar.json +17 -0
- package/registry/items/badge.json +18 -0
- package/registry/items/breadcrumb.json +18 -0
- package/registry/items/bubble.json +18 -0
- package/registry/items/button-group.json +19 -0
- package/registry/items/button.json +18 -0
- package/registry/items/calendar.json +19 -0
- package/registry/items/canvas.json +24 -0
- package/registry/items/card.json +15 -0
- package/registry/items/carousel.json +19 -0
- package/registry/items/cascader.json +23 -0
- package/registry/items/chart.json +17 -0
- package/registry/items/chat.json +23 -0
- package/registry/items/checkbox.json +18 -0
- package/registry/items/clipboard.json +19 -0
- package/registry/items/collapsible.json +15 -0
- package/registry/items/color-picker.json +19 -0
- package/registry/items/combobox.json +20 -0
- package/registry/items/command.json +21 -0
- package/registry/items/comment-thread.json +25 -0
- package/registry/items/context-menu.json +18 -0
- package/registry/items/controllable.json +14 -0
- package/registry/items/data-grid.json +24 -0
- package/registry/items/data-table-adapter.json +14 -0
- package/registry/items/data-table.json +32 -0
- package/registry/items/date-input.json +17 -0
- package/registry/items/dialog.json +19 -0
- package/registry/items/direction.json +13 -0
- package/registry/items/drag-preview.json +16 -0
- package/registry/items/drawer.json +18 -0
- package/registry/items/dropdown-menu.json +18 -0
- package/registry/items/editable.json +17 -0
- package/registry/items/empty.json +17 -0
- package/registry/items/field.json +19 -0
- package/registry/items/file-upload.json +19 -0
- package/registry/items/floating-panel.json +19 -0
- package/registry/items/floating-toolbar.json +16 -0
- package/registry/items/form.json +14 -0
- package/registry/items/format.json +15 -0
- package/registry/items/gantt.json +22 -0
- package/registry/items/history.json +14 -0
- package/registry/items/hotkeys.json +18 -0
- package/registry/items/hover-card.json +17 -0
- package/registry/items/image-cropper.json +17 -0
- package/registry/items/input-group.json +20 -0
- package/registry/items/input-otp.json +18 -0
- package/registry/items/input.json +15 -0
- package/registry/items/item.json +19 -0
- package/registry/items/json-tree-view.json +17 -0
- package/registry/items/kanban.json +26 -0
- package/registry/items/kbd.json +15 -0
- package/registry/items/label.json +15 -0
- package/registry/items/live-region.json +16 -0
- package/registry/items/marker.json +18 -0
- package/registry/items/marquee.json +17 -0
- package/registry/items/menubar.json +19 -0
- package/registry/items/message.json +15 -0
- package/registry/items/navigation-menu.json +19 -0
- package/registry/items/node-graph.json +22 -0
- package/registry/items/number-input.json +18 -0
- package/registry/items/pagination.json +18 -0
- package/registry/items/password-input.json +18 -0
- package/registry/items/popconfirm.json +20 -0
- package/registry/items/popover.json +18 -0
- package/registry/items/progress.json +17 -0
- package/registry/items/qr-code.json +17 -0
- package/registry/items/query-builder.json +23 -0
- package/registry/items/radio-group.json +17 -0
- package/registry/items/rating-group.json +18 -0
- package/registry/items/resizable.json +17 -0
- package/registry/items/rich-text-editor.json +37 -0
- package/registry/items/row-layout.json +15 -0
- package/registry/items/scheduler.json +23 -0
- package/registry/items/scroll-area.json +17 -0
- package/registry/items/segment-group.json +17 -0
- package/registry/items/select.json +19 -0
- package/registry/items/separator.json +15 -0
- package/registry/items/sheet.json +19 -0
- package/registry/items/sidebar.json +27 -0
- package/registry/items/signature-pad.json +19 -0
- package/registry/items/skeleton.json +15 -0
- package/registry/items/slider.json +17 -0
- package/registry/items/sonner.json +17 -0
- package/registry/items/spinner.json +17 -0
- package/registry/items/steps.json +19 -0
- package/registry/items/swap.json +17 -0
- package/registry/items/switch.json +17 -0
- package/registry/items/table.json +16 -0
- package/registry/items/tabs.json +18 -0
- package/registry/items/tags-input.json +18 -0
- package/registry/items/textarea.json +15 -0
- package/registry/items/time.json +14 -0
- package/registry/items/timer.json +18 -0
- package/registry/items/toc.json +17 -0
- package/registry/items/toggle-group.json +19 -0
- package/registry/items/toggle.json +18 -0
- package/registry/items/tooltip.json +17 -0
- package/registry/items/tour.json +19 -0
- package/registry/items/transfer-list.json +24 -0
- package/registry/items/tree-select.json +24 -0
- package/registry/items/tree-view.json +18 -0
- package/registry/items/use-mobile.json +13 -0
- package/registry/items/utils.json +17 -0
- package/registry/items/virtual-list.json +16 -0
- package/src/components/ui/accordion.tsx +111 -0
- package/src/components/ui/activity-feed.tsx +228 -0
- package/src/components/ui/alert-dialog.tsx +204 -0
- package/src/components/ui/alert.tsx +55 -0
- package/src/components/ui/angle-slider.tsx +117 -0
- package/src/components/ui/app-shell.tsx +985 -0
- package/src/components/ui/aspect-ratio.tsx +35 -0
- package/src/components/ui/attachment.tsx +193 -0
- package/src/components/ui/avatar.tsx +94 -0
- package/src/components/ui/badge.tsx +45 -0
- package/src/components/ui/breadcrumb.tsx +93 -0
- package/src/components/ui/bubble.tsx +119 -0
- package/src/components/ui/button-group.tsx +79 -0
- package/src/components/ui/button.tsx +65 -0
- package/src/components/ui/calendar.tsx +172 -0
- package/src/components/ui/canvas.tsx +723 -0
- package/src/components/ui/card.tsx +69 -0
- package/src/components/ui/carousel.tsx +228 -0
- package/src/components/ui/cascader.tsx +754 -0
- package/src/components/ui/chart.tsx +303 -0
- package/src/components/ui/chat.tsx +544 -0
- package/src/components/ui/checkbox.tsx +94 -0
- package/src/components/ui/clipboard.tsx +94 -0
- package/src/components/ui/collapsible.tsx +32 -0
- package/src/components/ui/color-picker.tsx +441 -0
- package/src/components/ui/combobox.tsx +400 -0
- package/src/components/ui/command.tsx +292 -0
- package/src/components/ui/comment-thread.tsx +923 -0
- package/src/components/ui/context-menu.tsx +331 -0
- package/src/components/ui/data-grid.tsx +1227 -0
- package/src/components/ui/data-table.tsx +993 -0
- package/src/components/ui/date-input.tsx +103 -0
- package/src/components/ui/dialog.tsx +159 -0
- package/src/components/ui/direction.tsx +32 -0
- package/src/components/ui/drawer.tsx +200 -0
- package/src/components/ui/dropdown-menu.tsx +329 -0
- package/src/components/ui/editable.tsx +90 -0
- package/src/components/ui/empty.tsx +86 -0
- package/src/components/ui/field.tsx +218 -0
- package/src/components/ui/file-upload.tsx +204 -0
- package/src/components/ui/floating-panel.tsx +199 -0
- package/src/components/ui/floating-toolbar.tsx +79 -0
- package/src/components/ui/format.tsx +22 -0
- package/src/components/ui/gantt.tsx +950 -0
- package/src/components/ui/hotkeys.tsx +340 -0
- package/src/components/ui/hover-card.tsx +83 -0
- package/src/components/ui/image-cropper.tsx +100 -0
- package/src/components/ui/input-group.tsx +133 -0
- package/src/components/ui/input-otp.tsx +82 -0
- package/src/components/ui/input.tsx +18 -0
- package/src/components/ui/item.tsx +174 -0
- package/src/components/ui/json-tree-view.tsx +37 -0
- package/src/components/ui/kanban.tsx +884 -0
- package/src/components/ui/kbd.tsx +20 -0
- package/src/components/ui/label.tsx +30 -0
- package/src/components/ui/live-region.tsx +54 -0
- package/src/components/ui/marker.tsx +66 -0
- package/src/components/ui/marquee.tsx +61 -0
- package/src/components/ui/menubar.tsx +456 -0
- package/src/components/ui/message.tsx +74 -0
- package/src/components/ui/navigation-menu.tsx +202 -0
- package/src/components/ui/node-graph.tsx +1723 -0
- package/src/components/ui/number-input.tsx +148 -0
- package/src/components/ui/pagination.tsx +88 -0
- package/src/components/ui/password-input.tsx +104 -0
- package/src/components/ui/popconfirm.tsx +149 -0
- package/src/components/ui/popover.tsx +126 -0
- package/src/components/ui/progress.tsx +114 -0
- package/src/components/ui/qr-code.tsx +51 -0
- package/src/components/ui/query-builder.tsx +938 -0
- package/src/components/ui/radio-group.tsx +108 -0
- package/src/components/ui/rating-group.tsx +91 -0
- package/src/components/ui/resizable.tsx +41 -0
- package/src/components/ui/rich-text-editor.tsx +845 -0
- package/src/components/ui/scheduler.tsx +1164 -0
- package/src/components/ui/scroll-area.tsx +82 -0
- package/src/components/ui/segment-group.tsx +95 -0
- package/src/components/ui/select.tsx +354 -0
- package/src/components/ui/separator.tsx +32 -0
- package/src/components/ui/sheet.tsx +130 -0
- package/src/components/ui/sidebar.tsx +665 -0
- package/src/components/ui/signature-pad.tsx +112 -0
- package/src/components/ui/skeleton.tsx +7 -0
- package/src/components/ui/slider.tsx +183 -0
- package/src/components/ui/sonner.tsx +37 -0
- package/src/components/ui/spinner.tsx +16 -0
- package/src/components/ui/steps.tsx +152 -0
- package/src/components/ui/swap.tsx +33 -0
- package/src/components/ui/switch.tsx +83 -0
- package/src/components/ui/table.tsx +72 -0
- package/src/components/ui/tabs.tsx +100 -0
- package/src/components/ui/tags-input.tsx +153 -0
- package/src/components/ui/textarea.tsx +17 -0
- package/src/components/ui/timer.tsx +67 -0
- package/src/components/ui/toc.tsx +64 -0
- package/src/components/ui/toggle-group.tsx +89 -0
- package/src/components/ui/toggle.tsx +46 -0
- package/src/components/ui/tooltip.tsx +113 -0
- package/src/components/ui/tour.tsx +168 -0
- package/src/components/ui/transfer-list.tsx +700 -0
- package/src/components/ui/tree-select.tsx +638 -0
- package/src/components/ui/tree-view.tsx +217 -0
- package/src/components/ui/virtual-list.tsx +343 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/lib/controllable.ts +31 -0
- package/src/lib/data-table-adapter.ts +67 -0
- package/src/lib/drag-preview.ts +39 -0
- package/src/lib/form.ts +39 -0
- package/src/lib/history.ts +55 -0
- package/src/lib/row-layout.ts +81 -0
- package/src/lib/table.ts +483 -0
- package/src/lib/time.ts +37 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles/arkcn.css +764 -0
- package/src/styles/index.css +3 -0
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { ark } from "@ark-ui/react"
|
|
5
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
6
|
+
import { ArrowLeftIcon, MessagesSquareIcon, SearchIcon, SendIcon } from "lucide-react"
|
|
7
|
+
import { cn } from "@/lib/utils"
|
|
8
|
+
import { useControllable } from "@/lib/controllable"
|
|
9
|
+
|
|
10
|
+
import { Button } from "@/components/ui/button"
|
|
11
|
+
import { ScrollArea } from "@/components/ui/scroll-area"
|
|
12
|
+
|
|
13
|
+
/* -------------------------------------------------------------------------- */
|
|
14
|
+
/* Root and context */
|
|
15
|
+
/* -------------------------------------------------------------------------- */
|
|
16
|
+
|
|
17
|
+
type ChatContextValue = {
|
|
18
|
+
/** Id of the selected conversation. */
|
|
19
|
+
value: string | null
|
|
20
|
+
setValue: (value: string | null) => void
|
|
21
|
+
/** On small screens the panel overlays the list only while this is true. */
|
|
22
|
+
mobileOpen: boolean
|
|
23
|
+
setMobileOpen: (open: boolean) => void
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ChatContext = React.createContext<ChatContextValue | null>(null)
|
|
27
|
+
|
|
28
|
+
function useChat(component = "Chat parts") {
|
|
29
|
+
const ctx = React.useContext(ChatContext)
|
|
30
|
+
if (!ctx) throw new Error(`${component} must be used within <Chat>`)
|
|
31
|
+
return ctx
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function Chat({
|
|
35
|
+
className,
|
|
36
|
+
value: valueProp,
|
|
37
|
+
defaultValue = null,
|
|
38
|
+
onValueChange,
|
|
39
|
+
...props
|
|
40
|
+
}: Omit<React.ComponentProps<"div">, "defaultValue"> & {
|
|
41
|
+
value?: string | null
|
|
42
|
+
defaultValue?: string | null
|
|
43
|
+
onValueChange?: (value: string | null) => void
|
|
44
|
+
}) {
|
|
45
|
+
const [value, setValue] = useControllable(valueProp, defaultValue, onValueChange)
|
|
46
|
+
const [mobileOpen, setMobileOpen] = React.useState(value !== null)
|
|
47
|
+
const ctx = React.useMemo(() => ({ value, setValue, mobileOpen, setMobileOpen }), [value, setValue, mobileOpen])
|
|
48
|
+
return (
|
|
49
|
+
<ChatContext.Provider value={ctx}>
|
|
50
|
+
<div
|
|
51
|
+
data-slot="chat"
|
|
52
|
+
data-selected={value !== null ? "" : undefined}
|
|
53
|
+
className={cn("relative flex h-full gap-6", className)}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
</ChatContext.Provider>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function ChatContextConsumer({ children }: { children: (ctx: ChatContextValue) => React.ReactNode }) {
|
|
61
|
+
return children(useChat("ChatContext"))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* -------------------------------------------------------------------------- */
|
|
65
|
+
/* Sidebar: header, search, conversation list */
|
|
66
|
+
/* -------------------------------------------------------------------------- */
|
|
67
|
+
|
|
68
|
+
function ChatSidebar({ className, ...props }: React.ComponentProps<"div">) {
|
|
69
|
+
return (
|
|
70
|
+
<div
|
|
71
|
+
data-slot="chat-sidebar"
|
|
72
|
+
className={cn("flex w-full flex-col gap-2 sm:w-56 lg:w-72 2xl:w-80", className)}
|
|
73
|
+
{...props}
|
|
74
|
+
/>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function ChatSidebarHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
79
|
+
return (
|
|
80
|
+
<div
|
|
81
|
+
data-slot="chat-sidebar-header"
|
|
82
|
+
className={cn(
|
|
83
|
+
"sticky top-0 z-10 -mx-4 flex flex-col gap-2 bg-background px-4 pb-3 shadow-md sm:static sm:z-auto sm:mx-0 sm:p-0 sm:shadow-none",
|
|
84
|
+
className
|
|
85
|
+
)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function ChatSidebarTitle({ className, children, ...props }: React.ComponentProps<"div">) {
|
|
92
|
+
return (
|
|
93
|
+
<div data-slot="chat-sidebar-title" className={cn("flex items-center justify-between py-2", className)} {...props}>
|
|
94
|
+
{children}
|
|
95
|
+
</div>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function ChatSidebarHeading({ className, children, ...props }: React.ComponentProps<"h1">) {
|
|
100
|
+
return (
|
|
101
|
+
<h1
|
|
102
|
+
data-slot="chat-sidebar-heading"
|
|
103
|
+
className={cn("flex items-center gap-2 text-2xl font-bold", className)}
|
|
104
|
+
{...props}
|
|
105
|
+
>
|
|
106
|
+
{children}
|
|
107
|
+
<MessagesSquareIcon className="size-5" />
|
|
108
|
+
</h1>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function ChatSearch({ className, ...props }: React.ComponentProps<"input">) {
|
|
113
|
+
return (
|
|
114
|
+
<label
|
|
115
|
+
data-slot="chat-search"
|
|
116
|
+
className={cn(
|
|
117
|
+
"flex h-9 w-full items-center gap-2 rounded-lg border border-input bg-transparent px-2.5 text-sm transition-colors has-focus-visible:border-ring has-focus-visible:ring-3 has-focus-visible:ring-ring/50 dark:bg-input/30",
|
|
118
|
+
className
|
|
119
|
+
)}
|
|
120
|
+
>
|
|
121
|
+
<SearchIcon className="size-4 shrink-0 text-muted-foreground" />
|
|
122
|
+
<span className="sr-only">Search</span>
|
|
123
|
+
<input
|
|
124
|
+
type="text"
|
|
125
|
+
data-slot="chat-search-input"
|
|
126
|
+
className="min-w-0 flex-1 bg-transparent outline-none placeholder:text-muted-foreground"
|
|
127
|
+
{...props}
|
|
128
|
+
/>
|
|
129
|
+
</label>
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function ChatConversationList({ className, children, ...props }: React.ComponentProps<typeof ScrollArea>) {
|
|
134
|
+
return (
|
|
135
|
+
<ScrollArea data-slot="chat-conversation-list" className={cn("-mx-3 h-full flex-1", className)} {...props}>
|
|
136
|
+
<div role="list" className="flex flex-col p-3 *:border-b *:border-border *:last:border-b-0">
|
|
137
|
+
{children}
|
|
138
|
+
</div>
|
|
139
|
+
</ScrollArea>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function ChatConversationItem({
|
|
144
|
+
className,
|
|
145
|
+
value,
|
|
146
|
+
onClick,
|
|
147
|
+
asChild,
|
|
148
|
+
...props
|
|
149
|
+
}: React.ComponentProps<typeof ark.button> & { value: string }) {
|
|
150
|
+
const ctx = useChat("ChatConversationItem")
|
|
151
|
+
const selected = ctx.value === value
|
|
152
|
+
return (
|
|
153
|
+
<div role="listitem" className="py-1">
|
|
154
|
+
<ark.button
|
|
155
|
+
asChild={asChild}
|
|
156
|
+
type="button"
|
|
157
|
+
data-slot="chat-conversation-item"
|
|
158
|
+
data-selected={selected ? "" : undefined}
|
|
159
|
+
aria-current={selected ? "true" : undefined}
|
|
160
|
+
className={cn(
|
|
161
|
+
"group/chat-conversation-item flex w-full gap-2 rounded-md p-2 text-start text-sm outline-none hover:bg-accent hover:text-accent-foreground focus-visible:ring-3 focus-visible:ring-ring/50 data-selected:sm:bg-muted",
|
|
162
|
+
className
|
|
163
|
+
)}
|
|
164
|
+
onClick={(event) => {
|
|
165
|
+
onClick?.(event)
|
|
166
|
+
if (event.defaultPrevented) return
|
|
167
|
+
ctx.setValue(value)
|
|
168
|
+
ctx.setMobileOpen(true)
|
|
169
|
+
}}
|
|
170
|
+
{...props}
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function ChatConversationInfo({ className, ...props }: React.ComponentProps<"div">) {
|
|
177
|
+
return <div data-slot="chat-conversation-info" className={cn("flex min-w-0 flex-col", className)} {...props} />
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function ChatConversationName({ className, ...props }: React.ComponentProps<"span">) {
|
|
181
|
+
return <span data-slot="chat-conversation-name" className={cn("font-medium", className)} {...props} />
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function ChatConversationPreview({ className, ...props }: React.ComponentProps<"span">) {
|
|
185
|
+
return (
|
|
186
|
+
<span
|
|
187
|
+
data-slot="chat-conversation-preview"
|
|
188
|
+
className={cn(
|
|
189
|
+
"line-clamp-2 text-muted-foreground group-hover/chat-conversation-item:text-accent-foreground/90",
|
|
190
|
+
className
|
|
191
|
+
)}
|
|
192
|
+
{...props}
|
|
193
|
+
/>
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* -------------------------------------------------------------------------- */
|
|
198
|
+
/* Panel: header, messages, composer */
|
|
199
|
+
/* -------------------------------------------------------------------------- */
|
|
200
|
+
|
|
201
|
+
function ChatPanel({ className, ...props }: React.ComponentProps<"div">) {
|
|
202
|
+
const ctx = useChat("ChatPanel")
|
|
203
|
+
return (
|
|
204
|
+
<div
|
|
205
|
+
data-slot="chat-panel"
|
|
206
|
+
data-mobile-open={ctx.mobileOpen ? "" : undefined}
|
|
207
|
+
className={cn(
|
|
208
|
+
"absolute inset-0 inset-s-full z-50 hidden w-full flex-1 flex-col border bg-background shadow-xs data-mobile-open:inset-s-0 data-mobile-open:flex sm:static sm:z-auto sm:flex sm:rounded-md",
|
|
209
|
+
className
|
|
210
|
+
)}
|
|
211
|
+
{...props}
|
|
212
|
+
/>
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function ChatHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
217
|
+
return (
|
|
218
|
+
<div
|
|
219
|
+
data-slot="chat-header"
|
|
220
|
+
className={cn(
|
|
221
|
+
"mb-1 flex flex-none items-center justify-between gap-3 bg-card p-4 shadow-lg sm:rounded-t-md",
|
|
222
|
+
className
|
|
223
|
+
)}
|
|
224
|
+
{...props}
|
|
225
|
+
/>
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Returns to the conversation list on small screens. Polymorphic via `asChild`. */
|
|
230
|
+
function ChatBackTrigger({ className, onClick, children, asChild, ...props }: React.ComponentProps<typeof Button>) {
|
|
231
|
+
const ctx = useChat("ChatBackTrigger")
|
|
232
|
+
return (
|
|
233
|
+
<Button
|
|
234
|
+
data-slot="chat-back-trigger"
|
|
235
|
+
size="icon"
|
|
236
|
+
variant="ghost"
|
|
237
|
+
asChild={asChild}
|
|
238
|
+
className={cn("-ms-2 sm:hidden", className)}
|
|
239
|
+
onClick={(event) => {
|
|
240
|
+
onClick?.(event)
|
|
241
|
+
if (!event.defaultPrevented) ctx.setMobileOpen(false)
|
|
242
|
+
}}
|
|
243
|
+
{...props}
|
|
244
|
+
>
|
|
245
|
+
{asChild
|
|
246
|
+
? children
|
|
247
|
+
: (children ?? (
|
|
248
|
+
<>
|
|
249
|
+
<ArrowLeftIcon className="rtl:rotate-180" />
|
|
250
|
+
<span className="sr-only">Back</span>
|
|
251
|
+
</>
|
|
252
|
+
))}
|
|
253
|
+
</Button>
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function ChatHeaderInfo({ className, ...props }: React.ComponentProps<"div">) {
|
|
258
|
+
return (
|
|
259
|
+
<div
|
|
260
|
+
data-slot="chat-header-info"
|
|
261
|
+
className={cn("flex min-w-0 items-center gap-2 lg:gap-4", className)}
|
|
262
|
+
{...props}
|
|
263
|
+
/>
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function ChatHeaderText({ className, ...props }: React.ComponentProps<"div">) {
|
|
268
|
+
return <div data-slot="chat-header-text" className={cn("flex min-w-0 flex-col", className)} {...props} />
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function ChatHeaderTitle({ className, ...props }: React.ComponentProps<"span">) {
|
|
272
|
+
return <span data-slot="chat-header-title" className={cn("text-sm font-medium lg:text-base", className)} {...props} />
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function ChatHeaderDescription({ className, ...props }: React.ComponentProps<"span">) {
|
|
276
|
+
return (
|
|
277
|
+
<span
|
|
278
|
+
data-slot="chat-header-description"
|
|
279
|
+
className={cn("line-clamp-1 max-w-32 text-xs text-muted-foreground lg:max-w-none lg:text-sm", className)}
|
|
280
|
+
{...props}
|
|
281
|
+
/>
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function ChatHeaderActions({ className, ...props }: React.ComponentProps<"div">) {
|
|
286
|
+
return (
|
|
287
|
+
<div
|
|
288
|
+
data-slot="chat-header-actions"
|
|
289
|
+
className={cn("-me-1 flex items-center gap-1 lg:gap-2", className)}
|
|
290
|
+
{...props}
|
|
291
|
+
/>
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function ChatBody({ className, ...props }: React.ComponentProps<"div">) {
|
|
296
|
+
return <div data-slot="chat-body" className={cn("flex flex-1 flex-col gap-2 px-4 pt-0 pb-4", className)} {...props} />
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Bottom-anchored message stream. Render messages newest-first: the column is
|
|
301
|
+
* reversed so the latest message sits at the bottom and the scroll position
|
|
302
|
+
* stays pinned there as messages arrive.
|
|
303
|
+
*/
|
|
304
|
+
function ChatMessages({ className, ...props }: React.ComponentProps<"div">) {
|
|
305
|
+
return (
|
|
306
|
+
<div data-slot="chat-messages" className="relative -me-4 flex flex-1 flex-col overflow-y-hidden">
|
|
307
|
+
<div
|
|
308
|
+
data-slot="chat-messages-viewport"
|
|
309
|
+
className={cn(
|
|
310
|
+
"flex h-40 w-full grow flex-col-reverse justify-start gap-4 overflow-y-auto py-2 pe-4 pb-4",
|
|
311
|
+
className
|
|
312
|
+
)}
|
|
313
|
+
{...props}
|
|
314
|
+
/>
|
|
315
|
+
</div>
|
|
316
|
+
)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function ChatDateSeparator({ className, ...props }: React.ComponentProps<"div">) {
|
|
320
|
+
return <div data-slot="chat-date-separator" className={cn("text-center text-xs", className)} {...props} />
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const chatMessageVariants = cva("group/chat-message max-w-72 px-3 py-2 wrap-break-word shadow-lg", {
|
|
324
|
+
variants: {
|
|
325
|
+
variant: {
|
|
326
|
+
sent: "self-end rounded-[16px_16px_0_16px] bg-primary/90 text-primary-foreground/75",
|
|
327
|
+
received: "self-start rounded-[16px_16px_16px_0] bg-muted",
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
defaultVariants: { variant: "received" },
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
function ChatMessage({
|
|
334
|
+
className,
|
|
335
|
+
variant = "received",
|
|
336
|
+
...props
|
|
337
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof chatMessageVariants>) {
|
|
338
|
+
return (
|
|
339
|
+
<div
|
|
340
|
+
data-slot="chat-message"
|
|
341
|
+
data-variant={variant}
|
|
342
|
+
className={cn(chatMessageVariants({ variant }), className)}
|
|
343
|
+
{...props}
|
|
344
|
+
/>
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function ChatMessageTime({ className, ...props }: React.ComponentProps<"span">) {
|
|
349
|
+
return (
|
|
350
|
+
<span
|
|
351
|
+
data-slot="chat-message-time"
|
|
352
|
+
className={cn(
|
|
353
|
+
"mt-1 block text-xs font-light text-foreground/75 italic group-data-[variant=sent]/chat-message:text-end group-data-[variant=sent]/chat-message:text-primary-foreground/85",
|
|
354
|
+
className
|
|
355
|
+
)}
|
|
356
|
+
{...props}
|
|
357
|
+
/>
|
|
358
|
+
)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function ChatComposer({ className, ...props }: React.ComponentProps<"form">) {
|
|
362
|
+
return <form data-slot="chat-composer" className={cn("flex w-full flex-none gap-2", className)} {...props} />
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function ChatComposerField({ className, ...props }: React.ComponentProps<"div">) {
|
|
366
|
+
return (
|
|
367
|
+
<div
|
|
368
|
+
data-slot="chat-composer-field"
|
|
369
|
+
className={cn(
|
|
370
|
+
"flex flex-1 items-center gap-2 rounded-md border border-input bg-card px-2 py-1 transition-colors has-focus-visible:border-ring has-focus-visible:ring-3 has-focus-visible:ring-ring/50 lg:gap-4",
|
|
371
|
+
className
|
|
372
|
+
)}
|
|
373
|
+
{...props}
|
|
374
|
+
/>
|
|
375
|
+
)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function ChatComposerActions({ className, ...props }: React.ComponentProps<"div">) {
|
|
379
|
+
return <div data-slot="chat-composer-actions" className={cn("flex items-center gap-1", className)} {...props} />
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function ChatComposerInput({ className, ...props }: React.ComponentProps<"input">) {
|
|
383
|
+
return (
|
|
384
|
+
<label className="flex-1">
|
|
385
|
+
<span className="sr-only">Message</span>
|
|
386
|
+
<input
|
|
387
|
+
type="text"
|
|
388
|
+
data-slot="chat-composer-input"
|
|
389
|
+
className={cn(
|
|
390
|
+
"h-8 w-full min-w-0 bg-transparent text-sm outline-none placeholder:text-muted-foreground",
|
|
391
|
+
className
|
|
392
|
+
)}
|
|
393
|
+
{...props}
|
|
394
|
+
/>
|
|
395
|
+
</label>
|
|
396
|
+
)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** Submits the composer form. Polymorphic via `asChild`. */
|
|
400
|
+
function ChatComposerSendTrigger({
|
|
401
|
+
className,
|
|
402
|
+
children,
|
|
403
|
+
asChild,
|
|
404
|
+
variant = "ghost",
|
|
405
|
+
size = "icon",
|
|
406
|
+
...props
|
|
407
|
+
}: React.ComponentProps<typeof Button>) {
|
|
408
|
+
return (
|
|
409
|
+
<Button
|
|
410
|
+
data-slot="chat-composer-send-trigger"
|
|
411
|
+
type="submit"
|
|
412
|
+
variant={variant}
|
|
413
|
+
size={size}
|
|
414
|
+
asChild={asChild}
|
|
415
|
+
className={className}
|
|
416
|
+
{...props}
|
|
417
|
+
>
|
|
418
|
+
{asChild
|
|
419
|
+
? children
|
|
420
|
+
: (children ?? (
|
|
421
|
+
<>
|
|
422
|
+
<SendIcon />
|
|
423
|
+
<span className="sr-only">Send</span>
|
|
424
|
+
</>
|
|
425
|
+
))}
|
|
426
|
+
</Button>
|
|
427
|
+
)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* -------------------------------------------------------------------------- */
|
|
431
|
+
/* Empty state */
|
|
432
|
+
/* -------------------------------------------------------------------------- */
|
|
433
|
+
|
|
434
|
+
function ChatEmpty({ className, ...props }: React.ComponentProps<"div">) {
|
|
435
|
+
return (
|
|
436
|
+
<div
|
|
437
|
+
data-slot="chat-empty"
|
|
438
|
+
className={cn(
|
|
439
|
+
"absolute inset-0 inset-s-full z-50 hidden w-full flex-1 flex-col justify-center rounded-md border bg-card shadow-xs sm:static sm:z-auto sm:flex",
|
|
440
|
+
className
|
|
441
|
+
)}
|
|
442
|
+
{...props}
|
|
443
|
+
/>
|
|
444
|
+
)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function ChatEmptyContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
448
|
+
return <div data-slot="chat-empty-content" className={cn("flex flex-col items-center gap-6", className)} {...props} />
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function ChatEmptyIcon({ className, children, ...props }: React.ComponentProps<"div">) {
|
|
452
|
+
return (
|
|
453
|
+
<div
|
|
454
|
+
data-slot="chat-empty-icon"
|
|
455
|
+
className={cn(
|
|
456
|
+
"flex size-16 items-center justify-center rounded-full border-2 border-border [&_svg]:size-8",
|
|
457
|
+
className
|
|
458
|
+
)}
|
|
459
|
+
{...props}
|
|
460
|
+
>
|
|
461
|
+
{children ?? <MessagesSquareIcon />}
|
|
462
|
+
</div>
|
|
463
|
+
)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function ChatEmptyTitle({ className, ...props }: React.ComponentProps<"h2">) {
|
|
467
|
+
return <h2 data-slot="chat-empty-title" className={cn("text-center text-xl font-semibold", className)} {...props} />
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function ChatEmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
|
|
471
|
+
return (
|
|
472
|
+
<p
|
|
473
|
+
data-slot="chat-empty-description"
|
|
474
|
+
className={cn("text-center text-sm text-muted-foreground", className)}
|
|
475
|
+
{...props}
|
|
476
|
+
/>
|
|
477
|
+
)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/* -------------------------------------------------------------------------- */
|
|
481
|
+
/* Helpers */
|
|
482
|
+
/* -------------------------------------------------------------------------- */
|
|
483
|
+
|
|
484
|
+
/** Group items by a key while preserving first-seen order of the keys. */
|
|
485
|
+
function groupBy<T>(items: T[], getKey: (item: T) => string): [string, T[]][] {
|
|
486
|
+
const groups = new Map<string, T[]>()
|
|
487
|
+
for (const item of items) {
|
|
488
|
+
const key = getKey(item)
|
|
489
|
+
const list = groups.get(key)
|
|
490
|
+
if (list) list.push(item)
|
|
491
|
+
else groups.set(key, [item])
|
|
492
|
+
}
|
|
493
|
+
return Array.from(groups.entries())
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** "Jane Doe" -> "JD"; single word -> first two letters; empty -> "?". */
|
|
497
|
+
function getInitials(name: string) {
|
|
498
|
+
const parts = name.trim().split(/\s+/).filter(Boolean)
|
|
499
|
+
if (parts.length === 0) return "?"
|
|
500
|
+
if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase()
|
|
501
|
+
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase()
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export {
|
|
505
|
+
Chat,
|
|
506
|
+
ChatBackTrigger,
|
|
507
|
+
ChatBody,
|
|
508
|
+
ChatComposer,
|
|
509
|
+
ChatComposerActions,
|
|
510
|
+
ChatComposerField,
|
|
511
|
+
ChatComposerInput,
|
|
512
|
+
ChatComposerSendTrigger,
|
|
513
|
+
ChatContextConsumer as ChatContext,
|
|
514
|
+
ChatConversationInfo,
|
|
515
|
+
ChatConversationItem,
|
|
516
|
+
ChatConversationList,
|
|
517
|
+
ChatConversationName,
|
|
518
|
+
ChatConversationPreview,
|
|
519
|
+
ChatDateSeparator,
|
|
520
|
+
ChatEmpty,
|
|
521
|
+
ChatEmptyContent,
|
|
522
|
+
ChatEmptyDescription,
|
|
523
|
+
ChatEmptyIcon,
|
|
524
|
+
ChatEmptyTitle,
|
|
525
|
+
ChatHeader,
|
|
526
|
+
ChatHeaderActions,
|
|
527
|
+
ChatHeaderDescription,
|
|
528
|
+
ChatHeaderInfo,
|
|
529
|
+
ChatHeaderText,
|
|
530
|
+
ChatHeaderTitle,
|
|
531
|
+
ChatMessage,
|
|
532
|
+
ChatMessageTime,
|
|
533
|
+
ChatMessages,
|
|
534
|
+
ChatPanel,
|
|
535
|
+
ChatSearch,
|
|
536
|
+
ChatSidebar,
|
|
537
|
+
ChatSidebarHeader,
|
|
538
|
+
ChatSidebarHeading,
|
|
539
|
+
ChatSidebarTitle,
|
|
540
|
+
chatMessageVariants,
|
|
541
|
+
getInitials,
|
|
542
|
+
groupBy,
|
|
543
|
+
useChat,
|
|
544
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
import { Checkbox as CheckboxPrimitive } from "@ark-ui/react"
|
|
4
|
+
import { CheckIcon, MinusIcon } from "lucide-react"
|
|
5
|
+
|
|
6
|
+
const checkboxControlClassName =
|
|
7
|
+
"relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 group-has-[:focus-visible]/field-label:ring-0 group-has-[:focus-visible]/field-label:not-data-checked:border-input after:absolute after:-inset-x-3 after:-inset-y-2 data-focus-visible:border-ring data-focus-visible:ring-3 data-focus-visible:ring-ring/50 data-disabled:cursor-not-allowed data-disabled:opacity-50 data-invalid:border-destructive data-invalid:ring-3 data-invalid:ring-destructive/20 data-invalid:data-checked:border-primary dark:bg-input/30 dark:data-invalid:border-destructive/50 dark:data-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground group-has-[:focus-visible]/field-label:data-checked:border-primary dark:data-checked:bg-primary"
|
|
8
|
+
|
|
9
|
+
/** `id` is omitted: set part ids through the root `ids` prop so Ark's internal lookups keep working. */
|
|
10
|
+
function Checkbox({ className, children, ...props }: Omit<React.ComponentProps<typeof CheckboxPrimitive.Root>, "id">) {
|
|
11
|
+
return (
|
|
12
|
+
<CheckboxPrimitive.Root
|
|
13
|
+
data-slot="checkbox"
|
|
14
|
+
className="group/checkbox peer inline-flex items-center gap-2"
|
|
15
|
+
{...props}
|
|
16
|
+
>
|
|
17
|
+
<CheckboxControl className={className}>
|
|
18
|
+
<CheckboxIndicator>
|
|
19
|
+
<CheckIcon />
|
|
20
|
+
</CheckboxIndicator>
|
|
21
|
+
<CheckboxIndicator indeterminate>
|
|
22
|
+
<MinusIcon />
|
|
23
|
+
</CheckboxIndicator>
|
|
24
|
+
</CheckboxControl>
|
|
25
|
+
{children && <CheckboxLabel>{children}</CheckboxLabel>}
|
|
26
|
+
<CheckboxHiddenInput />
|
|
27
|
+
</CheckboxPrimitive.Root>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** `id` is omitted: set part ids through the root `ids` prop so Ark's internal lookups keep working. */
|
|
32
|
+
function CheckboxRoot({ ...props }: Omit<React.ComponentProps<typeof CheckboxPrimitive.Root>, "id">) {
|
|
33
|
+
return <CheckboxPrimitive.Root data-slot="checkbox" {...props} />
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function CheckboxContext({ ...props }: React.ComponentProps<typeof CheckboxPrimitive.Context>) {
|
|
37
|
+
return <CheckboxPrimitive.Context {...props} />
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** `id` is omitted: set part ids through the root `ids` prop so Ark's internal lookups keep working. */
|
|
41
|
+
function CheckboxControl({ className, ...props }: Omit<React.ComponentProps<typeof CheckboxPrimitive.Control>, "id">) {
|
|
42
|
+
return (
|
|
43
|
+
<CheckboxPrimitive.Control
|
|
44
|
+
data-slot="checkbox-control"
|
|
45
|
+
className={cn(checkboxControlClassName, className)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function CheckboxIndicator({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Indicator>) {
|
|
52
|
+
return (
|
|
53
|
+
<CheckboxPrimitive.Indicator
|
|
54
|
+
data-slot="checkbox-indicator"
|
|
55
|
+
className={cn("grid place-content-center text-current transition-none [&>svg]:size-3.5", className)}
|
|
56
|
+
{...props}
|
|
57
|
+
/>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function CheckboxLabel({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Label>) {
|
|
62
|
+
return (
|
|
63
|
+
<CheckboxPrimitive.Label
|
|
64
|
+
data-slot="checkbox-label"
|
|
65
|
+
className={cn("text-sm leading-none select-none data-disabled:opacity-50", className)}
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** `id` is omitted: set part ids through the root `ids` prop so Ark's internal lookups keep working. */
|
|
72
|
+
function CheckboxHiddenInput({ ...props }: Omit<React.ComponentProps<typeof CheckboxPrimitive.HiddenInput>, "id">) {
|
|
73
|
+
return <CheckboxPrimitive.HiddenInput {...props} />
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function CheckboxGroup({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Group>) {
|
|
77
|
+
return <CheckboxPrimitive.Group data-slot="checkbox-group" className={cn("grid gap-2", className)} {...props} />
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function CheckboxGroupProvider({ ...props }: React.ComponentProps<typeof CheckboxPrimitive.GroupProvider>) {
|
|
81
|
+
return <CheckboxPrimitive.GroupProvider {...props} />
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
Checkbox,
|
|
86
|
+
CheckboxContext,
|
|
87
|
+
CheckboxControl,
|
|
88
|
+
CheckboxGroup,
|
|
89
|
+
CheckboxHiddenInput,
|
|
90
|
+
CheckboxIndicator,
|
|
91
|
+
CheckboxLabel,
|
|
92
|
+
CheckboxRoot,
|
|
93
|
+
CheckboxGroupProvider,
|
|
94
|
+
}
|