@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.
Files changed (237) hide show
  1. package/README.md +78 -0
  2. package/bin/arkcn.mjs +78 -0
  3. package/cli/commands.mjs +251 -0
  4. package/cli/project.mjs +168 -0
  5. package/cli/registry.mjs +50 -0
  6. package/cli/ui.mjs +46 -0
  7. package/mcp/server.mjs +149 -0
  8. package/package.json +201 -0
  9. package/registry/base.css +680 -0
  10. package/registry/index.json +1833 -0
  11. package/registry/items/accordion.json +18 -0
  12. package/registry/items/activity-feed.json +18 -0
  13. package/registry/items/alert-dialog.json +18 -0
  14. package/registry/items/alert.json +17 -0
  15. package/registry/items/angle-slider.json +17 -0
  16. package/registry/items/app-shell.json +28 -0
  17. package/registry/items/aspect-ratio.json +13 -0
  18. package/registry/items/attachment.json +19 -0
  19. package/registry/items/avatar.json +17 -0
  20. package/registry/items/badge.json +18 -0
  21. package/registry/items/breadcrumb.json +18 -0
  22. package/registry/items/bubble.json +18 -0
  23. package/registry/items/button-group.json +19 -0
  24. package/registry/items/button.json +18 -0
  25. package/registry/items/calendar.json +19 -0
  26. package/registry/items/canvas.json +24 -0
  27. package/registry/items/card.json +15 -0
  28. package/registry/items/carousel.json +19 -0
  29. package/registry/items/cascader.json +23 -0
  30. package/registry/items/chart.json +17 -0
  31. package/registry/items/chat.json +23 -0
  32. package/registry/items/checkbox.json +18 -0
  33. package/registry/items/clipboard.json +19 -0
  34. package/registry/items/collapsible.json +15 -0
  35. package/registry/items/color-picker.json +19 -0
  36. package/registry/items/combobox.json +20 -0
  37. package/registry/items/command.json +21 -0
  38. package/registry/items/comment-thread.json +25 -0
  39. package/registry/items/context-menu.json +18 -0
  40. package/registry/items/controllable.json +14 -0
  41. package/registry/items/data-grid.json +24 -0
  42. package/registry/items/data-table-adapter.json +14 -0
  43. package/registry/items/data-table.json +32 -0
  44. package/registry/items/date-input.json +17 -0
  45. package/registry/items/dialog.json +19 -0
  46. package/registry/items/direction.json +13 -0
  47. package/registry/items/drag-preview.json +16 -0
  48. package/registry/items/drawer.json +18 -0
  49. package/registry/items/dropdown-menu.json +18 -0
  50. package/registry/items/editable.json +17 -0
  51. package/registry/items/empty.json +17 -0
  52. package/registry/items/field.json +19 -0
  53. package/registry/items/file-upload.json +19 -0
  54. package/registry/items/floating-panel.json +19 -0
  55. package/registry/items/floating-toolbar.json +16 -0
  56. package/registry/items/form.json +14 -0
  57. package/registry/items/format.json +15 -0
  58. package/registry/items/gantt.json +22 -0
  59. package/registry/items/history.json +14 -0
  60. package/registry/items/hotkeys.json +18 -0
  61. package/registry/items/hover-card.json +17 -0
  62. package/registry/items/image-cropper.json +17 -0
  63. package/registry/items/input-group.json +20 -0
  64. package/registry/items/input-otp.json +18 -0
  65. package/registry/items/input.json +15 -0
  66. package/registry/items/item.json +19 -0
  67. package/registry/items/json-tree-view.json +17 -0
  68. package/registry/items/kanban.json +26 -0
  69. package/registry/items/kbd.json +15 -0
  70. package/registry/items/label.json +15 -0
  71. package/registry/items/live-region.json +16 -0
  72. package/registry/items/marker.json +18 -0
  73. package/registry/items/marquee.json +17 -0
  74. package/registry/items/menubar.json +19 -0
  75. package/registry/items/message.json +15 -0
  76. package/registry/items/navigation-menu.json +19 -0
  77. package/registry/items/node-graph.json +22 -0
  78. package/registry/items/number-input.json +18 -0
  79. package/registry/items/pagination.json +18 -0
  80. package/registry/items/password-input.json +18 -0
  81. package/registry/items/popconfirm.json +20 -0
  82. package/registry/items/popover.json +18 -0
  83. package/registry/items/progress.json +17 -0
  84. package/registry/items/qr-code.json +17 -0
  85. package/registry/items/query-builder.json +23 -0
  86. package/registry/items/radio-group.json +17 -0
  87. package/registry/items/rating-group.json +18 -0
  88. package/registry/items/resizable.json +17 -0
  89. package/registry/items/rich-text-editor.json +37 -0
  90. package/registry/items/row-layout.json +15 -0
  91. package/registry/items/scheduler.json +23 -0
  92. package/registry/items/scroll-area.json +17 -0
  93. package/registry/items/segment-group.json +17 -0
  94. package/registry/items/select.json +19 -0
  95. package/registry/items/separator.json +15 -0
  96. package/registry/items/sheet.json +19 -0
  97. package/registry/items/sidebar.json +27 -0
  98. package/registry/items/signature-pad.json +19 -0
  99. package/registry/items/skeleton.json +15 -0
  100. package/registry/items/slider.json +17 -0
  101. package/registry/items/sonner.json +17 -0
  102. package/registry/items/spinner.json +17 -0
  103. package/registry/items/steps.json +19 -0
  104. package/registry/items/swap.json +17 -0
  105. package/registry/items/switch.json +17 -0
  106. package/registry/items/table.json +16 -0
  107. package/registry/items/tabs.json +18 -0
  108. package/registry/items/tags-input.json +18 -0
  109. package/registry/items/textarea.json +15 -0
  110. package/registry/items/time.json +14 -0
  111. package/registry/items/timer.json +18 -0
  112. package/registry/items/toc.json +17 -0
  113. package/registry/items/toggle-group.json +19 -0
  114. package/registry/items/toggle.json +18 -0
  115. package/registry/items/tooltip.json +17 -0
  116. package/registry/items/tour.json +19 -0
  117. package/registry/items/transfer-list.json +24 -0
  118. package/registry/items/tree-select.json +24 -0
  119. package/registry/items/tree-view.json +18 -0
  120. package/registry/items/use-mobile.json +13 -0
  121. package/registry/items/utils.json +17 -0
  122. package/registry/items/virtual-list.json +16 -0
  123. package/src/components/ui/accordion.tsx +111 -0
  124. package/src/components/ui/activity-feed.tsx +228 -0
  125. package/src/components/ui/alert-dialog.tsx +204 -0
  126. package/src/components/ui/alert.tsx +55 -0
  127. package/src/components/ui/angle-slider.tsx +117 -0
  128. package/src/components/ui/app-shell.tsx +985 -0
  129. package/src/components/ui/aspect-ratio.tsx +35 -0
  130. package/src/components/ui/attachment.tsx +193 -0
  131. package/src/components/ui/avatar.tsx +94 -0
  132. package/src/components/ui/badge.tsx +45 -0
  133. package/src/components/ui/breadcrumb.tsx +93 -0
  134. package/src/components/ui/bubble.tsx +119 -0
  135. package/src/components/ui/button-group.tsx +79 -0
  136. package/src/components/ui/button.tsx +65 -0
  137. package/src/components/ui/calendar.tsx +172 -0
  138. package/src/components/ui/canvas.tsx +723 -0
  139. package/src/components/ui/card.tsx +69 -0
  140. package/src/components/ui/carousel.tsx +228 -0
  141. package/src/components/ui/cascader.tsx +754 -0
  142. package/src/components/ui/chart.tsx +303 -0
  143. package/src/components/ui/chat.tsx +544 -0
  144. package/src/components/ui/checkbox.tsx +94 -0
  145. package/src/components/ui/clipboard.tsx +94 -0
  146. package/src/components/ui/collapsible.tsx +32 -0
  147. package/src/components/ui/color-picker.tsx +441 -0
  148. package/src/components/ui/combobox.tsx +400 -0
  149. package/src/components/ui/command.tsx +292 -0
  150. package/src/components/ui/comment-thread.tsx +923 -0
  151. package/src/components/ui/context-menu.tsx +331 -0
  152. package/src/components/ui/data-grid.tsx +1227 -0
  153. package/src/components/ui/data-table.tsx +993 -0
  154. package/src/components/ui/date-input.tsx +103 -0
  155. package/src/components/ui/dialog.tsx +159 -0
  156. package/src/components/ui/direction.tsx +32 -0
  157. package/src/components/ui/drawer.tsx +200 -0
  158. package/src/components/ui/dropdown-menu.tsx +329 -0
  159. package/src/components/ui/editable.tsx +90 -0
  160. package/src/components/ui/empty.tsx +86 -0
  161. package/src/components/ui/field.tsx +218 -0
  162. package/src/components/ui/file-upload.tsx +204 -0
  163. package/src/components/ui/floating-panel.tsx +199 -0
  164. package/src/components/ui/floating-toolbar.tsx +79 -0
  165. package/src/components/ui/format.tsx +22 -0
  166. package/src/components/ui/gantt.tsx +950 -0
  167. package/src/components/ui/hotkeys.tsx +340 -0
  168. package/src/components/ui/hover-card.tsx +83 -0
  169. package/src/components/ui/image-cropper.tsx +100 -0
  170. package/src/components/ui/input-group.tsx +133 -0
  171. package/src/components/ui/input-otp.tsx +82 -0
  172. package/src/components/ui/input.tsx +18 -0
  173. package/src/components/ui/item.tsx +174 -0
  174. package/src/components/ui/json-tree-view.tsx +37 -0
  175. package/src/components/ui/kanban.tsx +884 -0
  176. package/src/components/ui/kbd.tsx +20 -0
  177. package/src/components/ui/label.tsx +30 -0
  178. package/src/components/ui/live-region.tsx +54 -0
  179. package/src/components/ui/marker.tsx +66 -0
  180. package/src/components/ui/marquee.tsx +61 -0
  181. package/src/components/ui/menubar.tsx +456 -0
  182. package/src/components/ui/message.tsx +74 -0
  183. package/src/components/ui/navigation-menu.tsx +202 -0
  184. package/src/components/ui/node-graph.tsx +1723 -0
  185. package/src/components/ui/number-input.tsx +148 -0
  186. package/src/components/ui/pagination.tsx +88 -0
  187. package/src/components/ui/password-input.tsx +104 -0
  188. package/src/components/ui/popconfirm.tsx +149 -0
  189. package/src/components/ui/popover.tsx +126 -0
  190. package/src/components/ui/progress.tsx +114 -0
  191. package/src/components/ui/qr-code.tsx +51 -0
  192. package/src/components/ui/query-builder.tsx +938 -0
  193. package/src/components/ui/radio-group.tsx +108 -0
  194. package/src/components/ui/rating-group.tsx +91 -0
  195. package/src/components/ui/resizable.tsx +41 -0
  196. package/src/components/ui/rich-text-editor.tsx +845 -0
  197. package/src/components/ui/scheduler.tsx +1164 -0
  198. package/src/components/ui/scroll-area.tsx +82 -0
  199. package/src/components/ui/segment-group.tsx +95 -0
  200. package/src/components/ui/select.tsx +354 -0
  201. package/src/components/ui/separator.tsx +32 -0
  202. package/src/components/ui/sheet.tsx +130 -0
  203. package/src/components/ui/sidebar.tsx +665 -0
  204. package/src/components/ui/signature-pad.tsx +112 -0
  205. package/src/components/ui/skeleton.tsx +7 -0
  206. package/src/components/ui/slider.tsx +183 -0
  207. package/src/components/ui/sonner.tsx +37 -0
  208. package/src/components/ui/spinner.tsx +16 -0
  209. package/src/components/ui/steps.tsx +152 -0
  210. package/src/components/ui/swap.tsx +33 -0
  211. package/src/components/ui/switch.tsx +83 -0
  212. package/src/components/ui/table.tsx +72 -0
  213. package/src/components/ui/tabs.tsx +100 -0
  214. package/src/components/ui/tags-input.tsx +153 -0
  215. package/src/components/ui/textarea.tsx +17 -0
  216. package/src/components/ui/timer.tsx +67 -0
  217. package/src/components/ui/toc.tsx +64 -0
  218. package/src/components/ui/toggle-group.tsx +89 -0
  219. package/src/components/ui/toggle.tsx +46 -0
  220. package/src/components/ui/tooltip.tsx +113 -0
  221. package/src/components/ui/tour.tsx +168 -0
  222. package/src/components/ui/transfer-list.tsx +700 -0
  223. package/src/components/ui/tree-select.tsx +638 -0
  224. package/src/components/ui/tree-view.tsx +217 -0
  225. package/src/components/ui/virtual-list.tsx +343 -0
  226. package/src/hooks/use-mobile.ts +19 -0
  227. package/src/lib/controllable.ts +31 -0
  228. package/src/lib/data-table-adapter.ts +67 -0
  229. package/src/lib/drag-preview.ts +39 -0
  230. package/src/lib/form.ts +39 -0
  231. package/src/lib/history.ts +55 -0
  232. package/src/lib/row-layout.ts +81 -0
  233. package/src/lib/table.ts +483 -0
  234. package/src/lib/time.ts +37 -0
  235. package/src/lib/utils.ts +6 -0
  236. package/src/styles/arkcn.css +764 -0
  237. package/src/styles/index.css +3 -0
@@ -0,0 +1,985 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import {
5
+ BellIcon,
6
+ CheckIcon,
7
+ ChevronRightIcon,
8
+ ChevronsUpDownIcon,
9
+ MenuIcon,
10
+ MoonIcon,
11
+ SearchIcon,
12
+ SunIcon,
13
+ } from "lucide-react"
14
+ import { cn } from "@/lib/utils"
15
+
16
+ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
17
+ import { Badge } from "@/components/ui/badge"
18
+ import { Button } from "@/components/ui/button"
19
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
20
+ import { CommandDialog } from "@/components/ui/command"
21
+ import {
22
+ DropdownMenu,
23
+ DropdownMenuContent,
24
+ DropdownMenuGroup,
25
+ DropdownMenuItem,
26
+ DropdownMenuLabel,
27
+ DropdownMenuSeparator,
28
+ DropdownMenuTrigger,
29
+ } from "@/components/ui/dropdown-menu"
30
+ import { Kbd } from "@/components/ui/kbd"
31
+ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
32
+ import { Separator } from "@/components/ui/separator"
33
+ import {
34
+ Sidebar,
35
+ SidebarContent,
36
+ SidebarFooter,
37
+ SidebarGroup,
38
+ SidebarGroupLabel,
39
+ SidebarHeader,
40
+ SidebarInset,
41
+ SidebarMenu,
42
+ SidebarMenuButton,
43
+ SidebarMenuItem,
44
+ SidebarMenuSub,
45
+ SidebarMenuSubButton,
46
+ SidebarMenuSubItem,
47
+ SidebarProvider,
48
+ SidebarRail,
49
+ SidebarTrigger,
50
+ useSidebar,
51
+ } from "@/components/ui/sidebar"
52
+
53
+ /* -------------------------------------------------------------------------- */
54
+ /* Root */
55
+ /* -------------------------------------------------------------------------- */
56
+
57
+ type SidebarVariant = "sidebar" | "floating" | "inset"
58
+ type SidebarCollapsible = "offcanvas" | "icon" | "none"
59
+
60
+ type AppShellContextValue = {
61
+ variant: SidebarVariant
62
+ setVariant: (variant: SidebarVariant) => void
63
+ collapsible: SidebarCollapsible
64
+ setCollapsible: (collapsible: SidebarCollapsible) => void
65
+ searchOpen: boolean
66
+ setSearchOpen: React.Dispatch<React.SetStateAction<boolean>>
67
+ }
68
+
69
+ const AppShellContext = React.createContext<AppShellContextValue | null>(null)
70
+
71
+ function useAppShell() {
72
+ const ctx = React.useContext(AppShellContext)
73
+ if (!ctx) throw new Error("useAppShell must be used within <AppShell>")
74
+ return ctx
75
+ }
76
+
77
+ /**
78
+ * Root of the shell. Owns the sidebar open state (via `SidebarProvider`), the
79
+ * sidebar variant and collapse mode, and the global search open state with its
80
+ * Cmd/Ctrl+K shortcut. Routing, theming, and data are the consumer's.
81
+ */
82
+ function AppShell({
83
+ variant: variantProp,
84
+ defaultVariant = "inset",
85
+ onVariantChange,
86
+ collapsible: collapsibleProp,
87
+ defaultCollapsible = "icon",
88
+ onCollapsibleChange,
89
+ searchShortcut = true,
90
+ children,
91
+ ...sidebarProviderProps
92
+ }: Omit<React.ComponentProps<typeof SidebarProvider>, "children"> & {
93
+ variant?: SidebarVariant
94
+ defaultVariant?: SidebarVariant
95
+ onVariantChange?: (variant: SidebarVariant) => void
96
+ collapsible?: SidebarCollapsible
97
+ defaultCollapsible?: SidebarCollapsible
98
+ onCollapsibleChange?: (collapsible: SidebarCollapsible) => void
99
+ /** Toggle the search dialog with Cmd/Ctrl+K. */
100
+ searchShortcut?: boolean
101
+ children?: React.ReactNode
102
+ }) {
103
+ const [variantState, setVariantState] = React.useState(defaultVariant)
104
+ const [collapsibleState, setCollapsibleState] = React.useState(defaultCollapsible)
105
+ const [searchOpen, setSearchOpen] = React.useState(false)
106
+ const variant = variantProp ?? variantState
107
+ const collapsible = collapsibleProp ?? collapsibleState
108
+
109
+ React.useEffect(() => {
110
+ if (!searchShortcut) return
111
+ const down = (event: KeyboardEvent) => {
112
+ if (event.key === "k" && (event.metaKey || event.ctrlKey)) {
113
+ event.preventDefault()
114
+ setSearchOpen((open) => !open)
115
+ }
116
+ }
117
+ document.addEventListener("keydown", down)
118
+ return () => document.removeEventListener("keydown", down)
119
+ }, [searchShortcut])
120
+
121
+ const ctx = React.useMemo<AppShellContextValue>(
122
+ () => ({
123
+ variant,
124
+ setVariant: (next) => {
125
+ if (variantProp === undefined) setVariantState(next)
126
+ onVariantChange?.(next)
127
+ },
128
+ collapsible,
129
+ setCollapsible: (next) => {
130
+ if (collapsibleProp === undefined) setCollapsibleState(next)
131
+ onCollapsibleChange?.(next)
132
+ },
133
+ searchOpen,
134
+ setSearchOpen,
135
+ }),
136
+ [variant, variantProp, onVariantChange, collapsible, collapsibleProp, onCollapsibleChange, searchOpen]
137
+ )
138
+
139
+ return (
140
+ <AppShellContext.Provider value={ctx}>
141
+ <SidebarProvider {...sidebarProviderProps}>{children}</SidebarProvider>
142
+ </AppShellContext.Provider>
143
+ )
144
+ }
145
+
146
+ function AppShellSkipLink({
147
+ className,
148
+ href = "#content",
149
+ children = "Skip to main content",
150
+ ...props
151
+ }: React.ComponentProps<"a">) {
152
+ return (
153
+ <a
154
+ data-slot="app-shell-skip-link"
155
+ href={href}
156
+ className={cn(
157
+ "fixed inset-s-44 z-999 -translate-y-52 rounded-md bg-primary px-4 py-2 text-sm font-medium whitespace-nowrap text-primary-foreground opacity-95 shadow-sm transition focus:translate-y-3 focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none",
158
+ className
159
+ )}
160
+ {...props}
161
+ >
162
+ {children}
163
+ </a>
164
+ )
165
+ }
166
+
167
+ /* -------------------------------------------------------------------------- */
168
+ /* Sidebar */
169
+ /* -------------------------------------------------------------------------- */
170
+
171
+ function AppShellSidebar({ variant, collapsible, children, ...props }: React.ComponentProps<typeof Sidebar>) {
172
+ const shell = useAppShell()
173
+ return (
174
+ <Sidebar
175
+ data-slot="app-shell-sidebar"
176
+ variant={variant ?? shell.variant}
177
+ collapsible={collapsible ?? shell.collapsible}
178
+ {...props}
179
+ >
180
+ {children}
181
+ <SidebarRail />
182
+ </Sidebar>
183
+ )
184
+ }
185
+
186
+ /* Brand / team switcher trigger ------------------------------------------- */
187
+
188
+ function AppShellBrand({ className, ...props }: React.ComponentProps<typeof SidebarMenuButton>) {
189
+ return (
190
+ <SidebarMenu>
191
+ <SidebarMenuItem>
192
+ <SidebarMenuButton
193
+ data-slot="app-shell-brand"
194
+ size="lg"
195
+ className={cn("data-open:bg-sidebar-accent data-open:text-sidebar-accent-foreground", className)}
196
+ {...props}
197
+ />
198
+ </SidebarMenuItem>
199
+ </SidebarMenu>
200
+ )
201
+ }
202
+
203
+ function AppShellBrandLogo({ className, ...props }: React.ComponentProps<"div">) {
204
+ return (
205
+ <div
206
+ data-slot="app-shell-brand-logo"
207
+ className={cn(
208
+ "flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground [&_svg]:size-4",
209
+ className
210
+ )}
211
+ {...props}
212
+ />
213
+ )
214
+ }
215
+
216
+ function AppShellBrandText({ className, ...props }: React.ComponentProps<"div">) {
217
+ return (
218
+ <div
219
+ data-slot="app-shell-brand-text"
220
+ className={cn("grid flex-1 text-start text-sm/tight", className)}
221
+ {...props}
222
+ />
223
+ )
224
+ }
225
+
226
+ function AppShellBrandTitle({ className, ...props }: React.ComponentProps<"span">) {
227
+ return <span data-slot="app-shell-brand-title" className={cn("truncate font-semibold", className)} {...props} />
228
+ }
229
+
230
+ function AppShellBrandDescription({ className, ...props }: React.ComponentProps<"span">) {
231
+ return <span data-slot="app-shell-brand-description" className={cn("truncate text-xs", className)} {...props} />
232
+ }
233
+
234
+ function AppShellBrandChevron({ className, ...props }: React.ComponentProps<typeof ChevronsUpDownIcon>) {
235
+ return <ChevronsUpDownIcon data-slot="app-shell-brand-chevron" className={cn("ms-auto", className)} {...props} />
236
+ }
237
+
238
+ /* Navigation --------------------------------------------------------------- */
239
+
240
+ function AppShellNav({ ...props }: React.ComponentProps<typeof SidebarGroup>) {
241
+ return <SidebarGroup data-slot="app-shell-nav" {...props} />
242
+ }
243
+
244
+ function AppShellNavLabel({ ...props }: React.ComponentProps<typeof SidebarGroupLabel>) {
245
+ return <SidebarGroupLabel data-slot="app-shell-nav-label" {...props} />
246
+ }
247
+
248
+ function AppShellNavList({ ...props }: React.ComponentProps<typeof SidebarMenu>) {
249
+ return <SidebarMenu data-slot="app-shell-nav-list" {...props} />
250
+ }
251
+
252
+ function AppShellNavItem({ ...props }: React.ComponentProps<typeof SidebarMenuItem>) {
253
+ return <SidebarMenuItem data-slot="app-shell-nav-item" {...props} />
254
+ }
255
+
256
+ /**
257
+ * A top-level navigation link. Pass the link element as the child with
258
+ * `asChild` (default) so any router's link works. `active` marks the current
259
+ * route; `tooltip` shows when the sidebar is collapsed to icons.
260
+ */
261
+ function AppShellNavLink({
262
+ active = false,
263
+ asChild = true,
264
+ onClick,
265
+ ...props
266
+ }: Omit<React.ComponentProps<typeof SidebarMenuButton>, "isActive"> & {
267
+ active?: boolean
268
+ }) {
269
+ const { setOpenMobile } = useSidebar()
270
+ return (
271
+ <SidebarMenuButton
272
+ data-slot="app-shell-nav-link"
273
+ asChild={asChild}
274
+ isActive={active}
275
+ onClick={(event) => {
276
+ onClick?.(event)
277
+ setOpenMobile(false)
278
+ }}
279
+ {...props}
280
+ />
281
+ )
282
+ }
283
+
284
+ function AppShellNavBadge({ className, ...props }: React.ComponentProps<typeof Badge>) {
285
+ return (
286
+ <Badge
287
+ data-slot="app-shell-nav-badge"
288
+ className={cn("ms-auto rounded-full px-1 py-0 text-xs", className)}
289
+ {...props}
290
+ />
291
+ )
292
+ }
293
+
294
+ /**
295
+ * A navigation item with children. Expanded sidebar: a collapsible sub-list.
296
+ * Icon-collapsed sidebar on desktop: a flyout menu. Supply `trigger` for the
297
+ * button content and `menu` for the flyout items; `children` is the sub-list.
298
+ */
299
+ function AppShellNavCollapsible({
300
+ active = false,
301
+ defaultOpen,
302
+ tooltip,
303
+ trigger,
304
+ menu,
305
+ children,
306
+ ...props
307
+ }: React.ComponentProps<typeof SidebarMenuItem> & {
308
+ active?: boolean
309
+ defaultOpen?: boolean
310
+ tooltip?: string
311
+ trigger: React.ReactNode
312
+ /** Rendered inside the flyout when the sidebar is collapsed to icons. */
313
+ menu?: React.ReactNode
314
+ }) {
315
+ const { state, isMobile } = useSidebar()
316
+
317
+ if (state === "collapsed" && !isMobile) {
318
+ return (
319
+ <SidebarMenuItem data-slot="app-shell-nav-collapsible" {...props}>
320
+ <DropdownMenu positioning={{ placement: "right-start", gutter: 4 }}>
321
+ <DropdownMenuTrigger asChild>
322
+ <SidebarMenuButton tooltip={tooltip} isActive={active}>
323
+ {trigger}
324
+ <ChevronRightIcon className="ms-auto" />
325
+ </SidebarMenuButton>
326
+ </DropdownMenuTrigger>
327
+ <DropdownMenuContent data-slot="app-shell-nav-flyout">
328
+ <DropdownMenuGroup>
329
+ {tooltip && (
330
+ <>
331
+ <DropdownMenuLabel>{tooltip}</DropdownMenuLabel>
332
+ <DropdownMenuSeparator />
333
+ </>
334
+ )}
335
+ {menu}
336
+ </DropdownMenuGroup>
337
+ </DropdownMenuContent>
338
+ </DropdownMenu>
339
+ </SidebarMenuItem>
340
+ )
341
+ }
342
+
343
+ return (
344
+ <Collapsible
345
+ defaultOpen={defaultOpen ?? active}
346
+ className="group/collapsible"
347
+ unmountOnExit={false}
348
+ lazyMount={false}
349
+ >
350
+ <SidebarMenuItem data-slot="app-shell-nav-collapsible" {...props}>
351
+ <CollapsibleTrigger asChild>
352
+ <SidebarMenuButton tooltip={tooltip} isActive={active}>
353
+ {trigger}
354
+ <ChevronRightIcon className="ms-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90 rtl:rotate-180" />
355
+ </SidebarMenuButton>
356
+ </CollapsibleTrigger>
357
+ <CollapsibleContent>
358
+ <SidebarMenuSub data-slot="app-shell-nav-sublist">{children}</SidebarMenuSub>
359
+ </CollapsibleContent>
360
+ </SidebarMenuItem>
361
+ </Collapsible>
362
+ )
363
+ }
364
+
365
+ function AppShellNavSubItem({ ...props }: React.ComponentProps<typeof SidebarMenuSubItem>) {
366
+ return <SidebarMenuSubItem data-slot="app-shell-nav-subitem" {...props} />
367
+ }
368
+
369
+ function AppShellNavSubLink({
370
+ active = false,
371
+ asChild = true,
372
+ onClick,
373
+ ...props
374
+ }: Omit<React.ComponentProps<typeof SidebarMenuSubButton>, "isActive"> & { active?: boolean }) {
375
+ const { setOpenMobile } = useSidebar()
376
+ return (
377
+ <SidebarMenuSubButton
378
+ data-slot="app-shell-nav-sublink"
379
+ asChild={asChild}
380
+ isActive={active}
381
+ onClick={(event) => {
382
+ onClick?.(event)
383
+ setOpenMobile(false)
384
+ }}
385
+ {...props}
386
+ />
387
+ )
388
+ }
389
+
390
+ /** A flyout menu entry for the icon-collapsed state. */
391
+ function AppShellNavMenuLink({
392
+ active = false,
393
+ className,
394
+ asChild = true,
395
+ ...props
396
+ }: React.ComponentProps<typeof DropdownMenuItem> & { active?: boolean }) {
397
+ return (
398
+ <DropdownMenuItem
399
+ data-slot="app-shell-nav-menu-link"
400
+ asChild={asChild}
401
+ className={cn(active && "bg-secondary", className)}
402
+ {...props}
403
+ />
404
+ )
405
+ }
406
+
407
+ /* User footer trigger ------------------------------------------------------ */
408
+
409
+ function AppShellUser({ className, ...props }: React.ComponentProps<typeof SidebarMenuButton>) {
410
+ return (
411
+ <SidebarMenu>
412
+ <SidebarMenuItem>
413
+ <SidebarMenuButton
414
+ data-slot="app-shell-user"
415
+ size="lg"
416
+ className={cn("data-open:bg-sidebar-accent data-open:text-sidebar-accent-foreground", className)}
417
+ {...props}
418
+ />
419
+ </SidebarMenuItem>
420
+ </SidebarMenu>
421
+ )
422
+ }
423
+
424
+ function AppShellUserAvatar({
425
+ src,
426
+ alt,
427
+ fallback,
428
+ className,
429
+ ...props
430
+ }: React.ComponentProps<typeof Avatar> & { src?: string; alt?: string; fallback: React.ReactNode }) {
431
+ return (
432
+ <Avatar data-slot="app-shell-user-avatar" className={cn("rounded-lg", className)} {...props}>
433
+ {src && <AvatarImage src={src} alt={alt} />}
434
+ <AvatarFallback className="rounded-lg">{fallback}</AvatarFallback>
435
+ </Avatar>
436
+ )
437
+ }
438
+
439
+ function AppShellUserText({ className, ...props }: React.ComponentProps<"div">) {
440
+ return (
441
+ <div data-slot="app-shell-user-text" className={cn("grid flex-1 text-start text-sm/tight", className)} {...props} />
442
+ )
443
+ }
444
+
445
+ function AppShellUserName({ className, ...props }: React.ComponentProps<"span">) {
446
+ return <span data-slot="app-shell-user-name" className={cn("truncate font-semibold", className)} {...props} />
447
+ }
448
+
449
+ function AppShellUserEmail({ className, ...props }: React.ComponentProps<"span">) {
450
+ return <span data-slot="app-shell-user-email" className={cn("truncate text-xs", className)} {...props} />
451
+ }
452
+
453
+ /* -------------------------------------------------------------------------- */
454
+ /* Content area: header, main */
455
+ /* -------------------------------------------------------------------------- */
456
+
457
+ function AppShellContent({ className, ...props }: React.ComponentProps<typeof SidebarInset>) {
458
+ return (
459
+ <SidebarInset
460
+ data-slot="app-shell-content"
461
+ id="content"
462
+ className={cn(
463
+ "@container/content",
464
+ "has-data-[layout=fixed]:h-svh",
465
+ "peer-data-[variant=inset]:has-data-[layout=fixed]:h-[calc(100svh-(var(--spacing)*4))]",
466
+ className
467
+ )}
468
+ {...props}
469
+ />
470
+ )
471
+ }
472
+
473
+ function AppShellHeader({
474
+ className,
475
+ fixed = false,
476
+ children,
477
+ ...props
478
+ }: React.ComponentProps<"header"> & { fixed?: boolean }) {
479
+ const [scrolled, setScrolled] = React.useState(false)
480
+ React.useEffect(() => {
481
+ if (!fixed) return
482
+ const onScroll = () => setScrolled((document.body.scrollTop || document.documentElement.scrollTop) > 10)
483
+ onScroll()
484
+ document.addEventListener("scroll", onScroll, { passive: true })
485
+ return () => document.removeEventListener("scroll", onScroll)
486
+ }, [fixed])
487
+ return (
488
+ <header
489
+ data-slot="app-shell-header"
490
+ data-fixed={fixed ? "" : undefined}
491
+ data-scrolled={scrolled ? "" : undefined}
492
+ className={cn(
493
+ // Solid background; the top corners follow the inset card's radius so the
494
+ // header never squares off the rounded content area.
495
+ "z-50 h-16 rounded-t-[inherit] bg-background",
496
+ fixed && "peer/header sticky top-0 w-[inherit]",
497
+ fixed && scrolled ? "shadow-sm" : "shadow-none",
498
+ className
499
+ )}
500
+ {...props}
501
+ >
502
+ <div
503
+ className={cn(
504
+ "relative flex h-full items-center gap-3 p-4 sm:gap-4",
505
+ fixed &&
506
+ scrolled &&
507
+ "after:absolute after:inset-0 after:-z-10 after:rounded-[inherit] after:bg-background/60 after:backdrop-blur-lg"
508
+ )}
509
+ >
510
+ <SidebarTrigger variant="outline" className="max-md:scale-125" />
511
+ <Separator orientation="vertical" className="h-6" />
512
+ {children}
513
+ </div>
514
+ </header>
515
+ )
516
+ }
517
+
518
+ function AppShellHeaderActions({ className, ...props }: React.ComponentProps<"div">) {
519
+ return (
520
+ <div
521
+ data-slot="app-shell-header-actions"
522
+ className={cn("ms-auto flex items-center gap-3 sm:gap-4", className)}
523
+ {...props}
524
+ />
525
+ )
526
+ }
527
+
528
+ function AppShellMain({
529
+ className,
530
+ fixed = false,
531
+ fluid = false,
532
+ ...props
533
+ }: React.ComponentProps<"main"> & { fixed?: boolean; fluid?: boolean }) {
534
+ return (
535
+ <main
536
+ data-slot="app-shell-main"
537
+ data-layout={fixed ? "fixed" : "auto"}
538
+ className={cn(
539
+ "px-4 py-6",
540
+ fixed && "flex grow flex-col overflow-hidden",
541
+ !fluid && "@7xl/content:mx-auto @7xl/content:w-full @7xl/content:max-w-7xl",
542
+ className
543
+ )}
544
+ {...props}
545
+ />
546
+ )
547
+ }
548
+
549
+ /* Top nav (secondary, in the header) --------------------------------------- */
550
+
551
+ function AppShellTopNav({ className, ...props }: React.ComponentProps<"nav">) {
552
+ return (
553
+ <nav
554
+ data-slot="app-shell-top-nav"
555
+ className={cn("hidden items-center gap-4 lg:flex xl:gap-6", className)}
556
+ {...props}
557
+ />
558
+ )
559
+ }
560
+
561
+ function AppShellTopNavLink({ active = false, className, ...props }: React.ComponentProps<"a"> & { active?: boolean }) {
562
+ return (
563
+ <a
564
+ data-slot="app-shell-top-nav-link"
565
+ data-active={active ? "" : undefined}
566
+ className={cn(
567
+ "text-sm font-medium text-muted-foreground transition-colors hover:text-primary data-active:text-foreground",
568
+ className
569
+ )}
570
+ {...props}
571
+ />
572
+ )
573
+ }
574
+
575
+ /** The small-screen counterpart of `AppShellTopNav`: the same links in a menu. */
576
+ function AppShellTopNavMenu({
577
+ className,
578
+ children,
579
+ ...props
580
+ }: React.ComponentProps<typeof DropdownMenu> & { className?: string }) {
581
+ return (
582
+ <DropdownMenu positioning={{ placement: "bottom-start" }} {...props}>
583
+ <DropdownMenuTrigger asChild>
584
+ <Button
585
+ data-slot="app-shell-top-nav-menu-trigger"
586
+ size="icon"
587
+ variant="outline"
588
+ className={cn("md:size-7 lg:hidden", className)}
589
+ >
590
+ <MenuIcon />
591
+ <span className="sr-only">Toggle navigation menu</span>
592
+ </Button>
593
+ </DropdownMenuTrigger>
594
+ <DropdownMenuContent>{children}</DropdownMenuContent>
595
+ </DropdownMenu>
596
+ )
597
+ }
598
+
599
+ /* -------------------------------------------------------------------------- */
600
+ /* Search and command menu */
601
+ /* -------------------------------------------------------------------------- */
602
+
603
+ function AppShellSearch({
604
+ className,
605
+ placeholder = "Search",
606
+ onClick,
607
+ asChild,
608
+ children,
609
+ ...props
610
+ }: React.ComponentProps<typeof Button> & { placeholder?: string }) {
611
+ const shell = useAppShell()
612
+ return (
613
+ <Button
614
+ data-slot="app-shell-search"
615
+ variant="outline"
616
+ asChild={asChild}
617
+ className={cn(
618
+ "group relative h-8 w-full flex-1 justify-start rounded-md bg-muted/25 text-sm font-normal text-muted-foreground shadow-none hover:bg-accent sm:w-40 sm:pe-12 md:flex-none lg:w-52 xl:w-64",
619
+ className
620
+ )}
621
+ aria-keyshortcuts="Meta+K Control+K"
622
+ onClick={(event) => {
623
+ onClick?.(event)
624
+ if (!event.defaultPrevented) shell.setSearchOpen(true)
625
+ }}
626
+ {...props}
627
+ >
628
+ {asChild
629
+ ? children
630
+ : (children ?? (
631
+ <>
632
+ <SearchIcon aria-hidden className="absolute inset-s-1.5 top-1/2 size-4 -translate-y-1/2" />
633
+ <span className="ms-4">{placeholder}</span>
634
+ <Kbd className="pointer-events-none absolute inset-e-1.5 top-1.5 hidden sm:flex">⌘K</Kbd>
635
+ </>
636
+ ))}
637
+ </Button>
638
+ )
639
+ }
640
+
641
+ /** A `CommandDialog` bound to the shell's search state. */
642
+ function AppShellCommandDialog({
643
+ ...props
644
+ }: Omit<React.ComponentProps<typeof CommandDialog>, "open" | "onOpenChange">) {
645
+ const shell = useAppShell()
646
+ return <CommandDialog open={shell.searchOpen} onOpenChange={({ open }) => shell.setSearchOpen(open)} {...props} />
647
+ }
648
+
649
+ /* -------------------------------------------------------------------------- */
650
+ /* Notifications */
651
+ /* -------------------------------------------------------------------------- */
652
+
653
+ /**
654
+ * A header notifications popover. Data-agnostic: the consumer renders the
655
+ * items and owns read state; pass `count` to the trigger for the unread badge.
656
+ */
657
+ function AppShellNotifications({ positioning, ...props }: React.ComponentProps<typeof Popover>) {
658
+ return (
659
+ <Popover
660
+ data-slot="app-shell-notifications"
661
+ positioning={{ placement: "bottom-end", gutter: 8, ...positioning }}
662
+ {...props}
663
+ />
664
+ )
665
+ }
666
+
667
+ function AppShellNotificationsTrigger({
668
+ count = 0,
669
+ max = 99,
670
+ className,
671
+ children,
672
+ asChild,
673
+ ...props
674
+ }: React.ComponentProps<typeof Button> & {
675
+ /** Unread count shown as a badge; hidden when 0. */
676
+ count?: number
677
+ max?: number
678
+ }) {
679
+ const label = count > max ? `${max}+` : String(count)
680
+ return (
681
+ <PopoverTrigger asChild>
682
+ <Button
683
+ data-slot="app-shell-notifications-trigger"
684
+ data-unread={count > 0 ? "" : undefined}
685
+ variant="ghost"
686
+ size="icon-lg"
687
+ asChild={asChild}
688
+ className={cn("relative scale-95 rounded-full", className)}
689
+ aria-label={count > 0 ? `Notifications, ${label} unread` : "Notifications"}
690
+ {...props}
691
+ >
692
+ {asChild ? children : (children ?? <BellIcon className="size-[1.2rem]" />)}
693
+ {!asChild && count > 0 && (
694
+ <span
695
+ data-slot="app-shell-notifications-badge"
696
+ aria-hidden
697
+ className="absolute inset-e-0.5 top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-destructive px-1 text-[10px] leading-none font-medium text-white ring-2 ring-background"
698
+ >
699
+ {label}
700
+ </span>
701
+ )}
702
+ </Button>
703
+ </PopoverTrigger>
704
+ )
705
+ }
706
+
707
+ function AppShellNotificationsContent({ className, ...props }: React.ComponentProps<typeof PopoverContent>) {
708
+ return (
709
+ <PopoverContent
710
+ data-slot="app-shell-notifications-content"
711
+ className={cn("w-80 gap-0 p-0 sm:w-96", className)}
712
+ {...props}
713
+ />
714
+ )
715
+ }
716
+
717
+ function AppShellNotificationsHeader({ className, ...props }: React.ComponentProps<"div">) {
718
+ return (
719
+ <div
720
+ data-slot="app-shell-notifications-header"
721
+ className={cn("flex items-center justify-between gap-2 border-b px-3.5 py-2.5", className)}
722
+ {...props}
723
+ />
724
+ )
725
+ }
726
+
727
+ function AppShellNotificationsTitle({ className, ...props }: React.ComponentProps<"h3">) {
728
+ return <h3 data-slot="app-shell-notifications-title" className={cn("text-sm font-semibold", className)} {...props} />
729
+ }
730
+
731
+ /** A small text action for the header or footer, e.g. "Mark all as read". */
732
+ function AppShellNotificationsActionTrigger({
733
+ className,
734
+ variant = "link",
735
+ size = "sm",
736
+ ...props
737
+ }: React.ComponentProps<typeof Button>) {
738
+ return (
739
+ <Button
740
+ data-slot="app-shell-notifications-action-trigger"
741
+ variant={variant}
742
+ size={size}
743
+ className={cn("h-auto p-0 text-xs text-muted-foreground hover:text-foreground", className)}
744
+ {...props}
745
+ />
746
+ )
747
+ }
748
+
749
+ function AppShellNotificationsList({ className, ...props }: React.ComponentProps<"ul">) {
750
+ return (
751
+ <ul
752
+ data-slot="app-shell-notifications-list"
753
+ className={cn("no-scrollbar flex max-h-96 flex-col gap-0.5 overflow-y-auto p-1", className)}
754
+ {...props}
755
+ />
756
+ )
757
+ }
758
+
759
+ function AppShellNotificationItem({
760
+ unread,
761
+ asChild,
762
+ className,
763
+ children,
764
+ ...props
765
+ }: React.ComponentProps<"li"> & {
766
+ unread?: boolean
767
+ /** Render the item's child (e.g. an anchor or button) as the interactive surface. */
768
+ asChild?: boolean
769
+ }) {
770
+ const itemClassName = cn(
771
+ "group/notification relative flex w-full cursor-default gap-3 rounded-md px-2.5 py-2 text-start text-sm outline-hidden transition-colors select-none hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground data-unread:bg-primary/5 dark:data-unread:bg-primary/10",
772
+ className
773
+ )
774
+ const inner =
775
+ asChild && React.isValidElement(children)
776
+ ? React.cloneElement(children as React.ReactElement<Record<string, unknown>>, {
777
+ "data-slot": "app-shell-notification-item-surface",
778
+ "data-unread": unread ? "" : undefined,
779
+ className: cn(itemClassName, (children as React.ReactElement<{ className?: string }>).props.className),
780
+ })
781
+ : children
782
+ return (
783
+ <li
784
+ data-slot="app-shell-notification-item"
785
+ data-unread={unread ? "" : undefined}
786
+ className={asChild ? "flex" : itemClassName}
787
+ {...props}
788
+ >
789
+ {inner}
790
+ </li>
791
+ )
792
+ }
793
+
794
+ function AppShellNotificationItemIcon({ className, ...props }: React.ComponentProps<"div">) {
795
+ return (
796
+ <div
797
+ data-slot="app-shell-notification-item-icon"
798
+ className={cn(
799
+ "flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground [&_svg]:size-4",
800
+ className
801
+ )}
802
+ {...props}
803
+ />
804
+ )
805
+ }
806
+
807
+ function AppShellNotificationItemContent({ className, ...props }: React.ComponentProps<"div">) {
808
+ return (
809
+ <div
810
+ data-slot="app-shell-notification-item-content"
811
+ className={cn("flex min-w-0 flex-1 flex-col gap-0.5", className)}
812
+ {...props}
813
+ />
814
+ )
815
+ }
816
+
817
+ function AppShellNotificationItemTitle({ className, ...props }: React.ComponentProps<"p">) {
818
+ return (
819
+ <p
820
+ data-slot="app-shell-notification-item-title"
821
+ className={cn("truncate leading-tight font-medium group-data-unread/notification:text-foreground", className)}
822
+ {...props}
823
+ />
824
+ )
825
+ }
826
+
827
+ function AppShellNotificationItemDescription({ className, ...props }: React.ComponentProps<"p">) {
828
+ return (
829
+ <p
830
+ data-slot="app-shell-notification-item-description"
831
+ className={cn("line-clamp-2 text-xs text-muted-foreground", className)}
832
+ {...props}
833
+ />
834
+ )
835
+ }
836
+
837
+ function AppShellNotificationItemTime({ className, ...props }: React.ComponentProps<"time">) {
838
+ return (
839
+ <time
840
+ data-slot="app-shell-notification-item-time"
841
+ className={cn("text-[11px] text-muted-foreground", className)}
842
+ {...props}
843
+ />
844
+ )
845
+ }
846
+
847
+ /** The unread dot; renders only inside an unread item. */
848
+ function AppShellNotificationItemIndicator({ className, ...props }: React.ComponentProps<"span">) {
849
+ return (
850
+ <span
851
+ data-slot="app-shell-notification-item-indicator"
852
+ aria-hidden
853
+ className={cn(
854
+ "mt-1.5 hidden size-2 shrink-0 rounded-full bg-primary group-data-unread/notification:block",
855
+ className
856
+ )}
857
+ {...props}
858
+ />
859
+ )
860
+ }
861
+
862
+ function AppShellNotificationsEmpty({ className, ...props }: React.ComponentProps<"div">) {
863
+ return (
864
+ <div
865
+ data-slot="app-shell-notifications-empty"
866
+ className={cn(
867
+ "flex flex-col items-center justify-center gap-1 px-4 py-10 text-center text-sm text-muted-foreground [&_svg]:mb-1 [&_svg]:size-6",
868
+ className
869
+ )}
870
+ {...props}
871
+ />
872
+ )
873
+ }
874
+
875
+ function AppShellNotificationsFooter({ className, ...props }: React.ComponentProps<"div">) {
876
+ return (
877
+ <div
878
+ data-slot="app-shell-notifications-footer"
879
+ className={cn("flex items-center justify-center border-t px-3.5 py-2", className)}
880
+ {...props}
881
+ />
882
+ )
883
+ }
884
+
885
+ /* -------------------------------------------------------------------------- */
886
+ /* Theme toggle */
887
+ /* -------------------------------------------------------------------------- */
888
+
889
+ type Theme = "light" | "dark" | "system"
890
+
891
+ function AppShellThemeToggle({
892
+ theme,
893
+ onThemeChange,
894
+ className,
895
+ ...props
896
+ }: Omit<React.ComponentProps<typeof Button>, "onChange"> & {
897
+ theme: Theme
898
+ onThemeChange: (theme: Theme) => void
899
+ }) {
900
+ return (
901
+ <DropdownMenu positioning={{ placement: "bottom-end" }}>
902
+ <DropdownMenuTrigger asChild>
903
+ <Button
904
+ data-slot="app-shell-theme-toggle"
905
+ variant="ghost"
906
+ size="icon-lg"
907
+ className={cn("scale-95 rounded-full", className)}
908
+ {...props}
909
+ >
910
+ <SunIcon className="size-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
911
+ <MoonIcon className="absolute size-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
912
+ <span className="sr-only">Toggle theme</span>
913
+ </Button>
914
+ </DropdownMenuTrigger>
915
+ <DropdownMenuContent>
916
+ {(["light", "dark", "system"] as const).map((option) => (
917
+ <DropdownMenuItem key={option} value={option} onSelect={() => onThemeChange(option)}>
918
+ <span className="capitalize">{option}</span>
919
+ <CheckIcon className={cn("ms-auto size-3.5", theme !== option && "invisible")} />
920
+ </DropdownMenuItem>
921
+ ))}
922
+ </DropdownMenuContent>
923
+ </DropdownMenu>
924
+ )
925
+ }
926
+
927
+ export {
928
+ AppShell,
929
+ AppShellBrand,
930
+ AppShellBrandChevron,
931
+ AppShellBrandDescription,
932
+ AppShellBrandLogo,
933
+ AppShellBrandText,
934
+ AppShellBrandTitle,
935
+ AppShellCommandDialog,
936
+ AppShellContent,
937
+ AppShellHeader,
938
+ AppShellHeaderActions,
939
+ AppShellMain,
940
+ AppShellNav,
941
+ AppShellNavBadge,
942
+ AppShellNavCollapsible,
943
+ AppShellNavItem,
944
+ AppShellNavLabel,
945
+ AppShellNavLink,
946
+ AppShellNavList,
947
+ AppShellNavMenuLink,
948
+ AppShellNavSubItem,
949
+ AppShellNavSubLink,
950
+ AppShellNotificationItem,
951
+ AppShellNotificationItemContent,
952
+ AppShellNotificationItemDescription,
953
+ AppShellNotificationItemIcon,
954
+ AppShellNotificationItemIndicator,
955
+ AppShellNotificationItemTime,
956
+ AppShellNotificationItemTitle,
957
+ AppShellNotifications,
958
+ AppShellNotificationsActionTrigger,
959
+ AppShellNotificationsContent,
960
+ AppShellNotificationsEmpty,
961
+ AppShellNotificationsFooter,
962
+ AppShellNotificationsHeader,
963
+ AppShellNotificationsList,
964
+ AppShellNotificationsTitle,
965
+ AppShellNotificationsTrigger,
966
+ AppShellSearch,
967
+ AppShellSidebar,
968
+ AppShellSkipLink,
969
+ AppShellThemeToggle,
970
+ AppShellTopNav,
971
+ AppShellTopNavLink,
972
+ AppShellTopNavMenu,
973
+ AppShellUser,
974
+ AppShellUserAvatar,
975
+ AppShellUserEmail,
976
+ AppShellUserName,
977
+ AppShellUserText,
978
+ SidebarContent as AppShellSidebarContent,
979
+ SidebarFooter as AppShellSidebarFooter,
980
+ SidebarHeader as AppShellSidebarHeader,
981
+ useAppShell,
982
+ type SidebarCollapsible,
983
+ type SidebarVariant,
984
+ type Theme,
985
+ }