miolo 3.0.0-beta.21 → 3.0.0-beta.210

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 (246) hide show
  1. package/bin/build/build.mjs +53 -0
  2. package/bin/build/build_bin.mjs +17 -0
  3. package/bin/build/cli/client.mjs +52 -0
  4. package/bin/build/cli/css.mjs +22 -0
  5. package/bin/build/cli/index.mjs +40 -0
  6. package/bin/build/cli/ssr.mjs +21 -0
  7. package/bin/build/server/aliases.mjs +112 -0
  8. package/bin/build/server/babel.config.js +24 -0
  9. package/bin/build/server/banner.mjs +20 -0
  10. package/bin/build/server/bundle.mjs +111 -0
  11. package/bin/build/server/fix.mjs +15 -0
  12. package/bin/build/server/index.mjs +69 -0
  13. package/bin/build/server/options.mjs +83 -0
  14. package/bin/create/auth.mjs +23 -0
  15. package/bin/create/copy.mjs +175 -0
  16. package/bin/create/docker.mjs +25 -0
  17. package/bin/create/index.mjs +137 -0
  18. package/bin/create/pkgjson.mjs +72 -0
  19. package/bin/create/prepare-template.mjs +158 -0
  20. package/bin/create/validation.mjs +27 -0
  21. package/bin/dev/dev.mjs +32 -23
  22. package/bin/dev/dev_start.mjs +6 -5
  23. package/bin/index.mjs +94 -52
  24. package/bin/prod-bin/create-bin.mjs +42 -27
  25. package/bin/prod-bin/run.mjs +13 -9
  26. package/bin/{prod-run → run}/pid.mjs +4 -4
  27. package/bin/run/restart.mjs +13 -0
  28. package/bin/run/start.mjs +18 -0
  29. package/bin/run/stop.mjs +20 -0
  30. package/bin/util.mjs +35 -11
  31. package/package.json +59 -39
  32. package/src/config/.env +34 -12
  33. package/src/config/defaults.mjs +253 -185
  34. package/src/config/env.mjs +40 -22
  35. package/src/config/index.mjs +19 -24
  36. package/src/config/util.mjs +25 -10
  37. package/src/db-conn.mjs +34 -0
  38. package/src/engines/cron/emails.mjs +10 -5
  39. package/src/engines/cron/index.mjs +45 -51
  40. package/src/engines/cron/init.mjs +16 -17
  41. package/src/engines/cron/ipsum.mjs +65 -60
  42. package/src/engines/cron/syscheck.mjs +30 -30
  43. package/src/engines/emailer/index.mjs +1 -2
  44. package/src/engines/emailer/queue.mjs +14 -20
  45. package/src/engines/emailer/transporter.mjs +86 -74
  46. package/src/engines/geoip/index.mjs +23 -28
  47. package/src/engines/http/index.mjs +26 -15
  48. package/src/engines/logger/buildErrorEmailBody.mjs +72 -0
  49. package/src/engines/logger/index.mjs +114 -122
  50. package/src/engines/logger/injectStackTrace.mjs +59 -0
  51. package/src/engines/logger/logger_mail.mjs +47 -61
  52. package/src/engines/logger/reopenTransportOnHupSignal.mjs +12 -13
  53. package/src/engines/parser/Parser.mjs +77 -60
  54. package/src/engines/parser/index.mjs +1 -1
  55. package/src/engines/schema/diffObjs.mjs +41 -0
  56. package/src/engines/schema/index.mjs +4 -0
  57. package/src/engines/schema/input.mjs +54 -0
  58. package/src/engines/schema/output.mjs +66 -0
  59. package/src/engines/socket/index.mjs +44 -46
  60. package/src/index.mjs +15 -10
  61. package/src/middleware/auth/basic.mjs +41 -40
  62. package/src/middleware/auth/custom.mjs +10 -13
  63. package/src/middleware/auth/guest.mjs +27 -27
  64. package/src/middleware/auth/passport/index.mjs +374 -0
  65. package/src/middleware/auth/passport/session/index.mjs +43 -0
  66. package/src/middleware/auth/{credentials → passport}/session/store.mjs +35 -15
  67. package/src/middleware/auth/passport/session/store_koa_redis.mjs +3 -0
  68. package/src/middleware/context/cache/index.mjs +78 -33
  69. package/src/middleware/context/cache/options.mjs +19 -21
  70. package/src/middleware/context/db.mjs +45 -20
  71. package/src/middleware/context/index.mjs +12 -12
  72. package/src/middleware/extra.mjs +4 -5
  73. package/src/middleware/http/body.mjs +25 -25
  74. package/src/middleware/http/catcher.mjs +81 -8
  75. package/src/middleware/http/custom_blacklist.mjs +19 -16
  76. package/src/middleware/http/headers.mjs +37 -34
  77. package/src/middleware/http/ratelimit.mjs +16 -23
  78. package/src/middleware/http/request.mjs +60 -65
  79. package/src/middleware/routes/catch_js_error.mjs +30 -23
  80. package/src/middleware/routes/robots.mjs +4 -7
  81. package/src/middleware/routes/router/crud/attachCrudRoutes.mjs +108 -90
  82. package/src/middleware/routes/router/crud/getCrudConfig.mjs +31 -55
  83. package/src/middleware/routes/router/defaults.mjs +6 -19
  84. package/src/middleware/routes/router/index.mjs +17 -21
  85. package/src/middleware/routes/router/queries/attachQueriesRoutes.mjs +227 -50
  86. package/src/middleware/routes/router/queries/getQueriesConfig.mjs +45 -55
  87. package/src/middleware/routes/router/utils.mjs +41 -26
  88. package/src/middleware/ssr/context.mjs +5 -7
  89. package/src/middleware/ssr/html.mjs +66 -43
  90. package/src/middleware/ssr/loader.mjs +11 -14
  91. package/src/middleware/ssr/ssr_render.mjs +39 -22
  92. package/src/middleware/static/index.mjs +33 -14
  93. package/src/middleware/vite/devserver.mjs +38 -22
  94. package/src/middleware/vite/watcher.mjs +12 -14
  95. package/src/server-cron.mjs +13 -8
  96. package/src/server-dev.mjs +13 -16
  97. package/src/server.mjs +49 -51
  98. package/template/.agent/skills/miolo-app-arch/SKILL.md +218 -0
  99. package/template/.agent/skills/miolo-auth/SKILL.md +450 -0
  100. package/template/.agent/skills/miolo-cli-router/SKILL.md +394 -0
  101. package/template/.agent/skills/miolo-database/SKILL.md +358 -0
  102. package/template/.agent/skills/miolo-react-patterns/SKILL.md +426 -0
  103. package/template/.agent/skills/miolo-routing/SKILL.md +326 -0
  104. package/template/.agent/skills/miolo-schemas/SKILL.md +329 -0
  105. package/template/.agent/skills/miolo-session-context/SKILL.md +397 -0
  106. package/template/.agent/skills/miolo-ssr/SKILL.md +433 -0
  107. package/template/.editorconfig +18 -0
  108. package/template/.env +120 -0
  109. package/template/biome.json +63 -0
  110. package/template/components.json +21 -0
  111. package/template/db/init.sh +89 -0
  112. package/template/db/sql/00_drop.sql +2 -0
  113. package/template/db/sql/01_users.sql +31 -0
  114. package/template/db/sql/02_todos.sql +20 -0
  115. package/template/docker/Dockerfile +13 -0
  116. package/template/docker/docker-compose.yaml +79 -0
  117. package/template/gitignore +42 -0
  118. package/template/jsconfig.json +18 -0
  119. package/template/package.json +88 -0
  120. package/template/postcss.config.js +9 -0
  121. package/template/src/cli/App.jsx +25 -0
  122. package/template/src/cli/components/JsonTreeViewer.jsx +128 -0
  123. package/template/src/cli/components/shadcn-io/spinner/index.jsx +232 -0
  124. package/template/src/cli/components/stepper.jsx +408 -0
  125. package/template/src/cli/components/ui/avatar.jsx +36 -0
  126. package/template/src/cli/components/ui/badge.jsx +31 -0
  127. package/template/src/cli/components/ui/breadcrumb.jsx +97 -0
  128. package/template/src/cli/components/ui/card.jsx +73 -0
  129. package/template/src/cli/components/ui/collapsible.jsx +16 -0
  130. package/template/src/cli/components/ui/dropdown-menu.jsx +179 -0
  131. package/template/src/cli/components/ui/field.jsx +217 -0
  132. package/template/src/cli/components/ui/input.jsx +19 -0
  133. package/template/src/cli/components/ui/label.jsx +17 -0
  134. package/template/src/cli/components/ui/pagination.jsx +99 -0
  135. package/template/src/cli/components/ui/patched/alert.jsx +56 -0
  136. package/template/src/cli/components/ui/patched/button.jsx +45 -0
  137. package/template/src/cli/components/ui/patched/dialog.jsx +114 -0
  138. package/template/src/cli/components/ui/patched/sidebar.jsx +660 -0
  139. package/template/src/cli/components/ui/select.jsx +141 -0
  140. package/template/src/cli/components/ui/separator.jsx +21 -0
  141. package/template/src/cli/components/ui/sheet.jsx +115 -0
  142. package/template/src/cli/components/ui/skeleton.jsx +13 -0
  143. package/template/src/cli/components/ui/sonner.jsx +22 -0
  144. package/template/src/cli/components/ui/switch.jsx +25 -0
  145. package/template/src/cli/components/ui/table.jsx +88 -0
  146. package/template/src/cli/components/ui/textarea.jsx +16 -0
  147. package/template/src/cli/components/ui/tooltip.jsx +45 -0
  148. package/template/src/cli/config/store_keys.mjs +2 -0
  149. package/template/src/cli/context/data/DataContext.jsx +5 -0
  150. package/template/src/cli/context/data/DataProvider.jsx +44 -0
  151. package/template/src/cli/context/data/useBreads.mjs +15 -0
  152. package/template/src/cli/context/data/useDataContext.mjs +4 -0
  153. package/template/src/cli/context/session/SessionContext.mjs +4 -0
  154. package/template/src/cli/context/session/SessionProvider.jsx +31 -0
  155. package/template/src/cli/context/session/makePermissioner.mjs +34 -0
  156. package/template/src/cli/context/session/useSessionContext.mjs +6 -0
  157. package/template/src/cli/context/theme/ThemeContext.mjs +4 -0
  158. package/template/src/cli/context/theme/ThemeProvider.jsx +49 -0
  159. package/template/src/cli/context/theme/useThemeContext.mjs +6 -0
  160. package/template/src/cli/context/ui/UIContext.jsx +5 -0
  161. package/template/src/cli/context/ui/UIProvider.jsx +16 -0
  162. package/template/src/cli/context/ui/useUIContext.mjs +4 -0
  163. package/template/src/cli/context/util.mjs +17 -0
  164. package/template/src/cli/entry-cli.jsx +33 -0
  165. package/template/src/cli/hooks/useIsMobile.mjs +19 -0
  166. package/template/src/cli/hooks/useStoragedState.mjs +63 -0
  167. package/template/src/cli/index.html +29 -0
  168. package/template/src/cli/layout/app-sidebar.jsx +25 -0
  169. package/template/src/cli/layout/main-layout.jsx +63 -0
  170. package/template/src/cli/layout/nav-last-todos.jsx +72 -0
  171. package/template/src/cli/layout/nav-main.jsx +39 -0
  172. package/template/src/cli/layout/nav-user.jsx +105 -0
  173. package/template/src/cli/layout/prop-switcher.jsx +93 -0
  174. package/template/src/cli/lib/utils.mjs +10 -0
  175. package/template/src/cli/pages/Index.jsx +13 -0
  176. package/template/src/cli/pages/IndexOffline.jsx +13 -0
  177. package/template/src/cli/pages/IndexOnline.jsx +18 -0
  178. package/template/src/cli/pages/dash/Dashboard.jsx +29 -0
  179. package/template/src/cli/pages/offline/Login.jsx +43 -0
  180. package/template/src/cli/pages/offline/LoginForm.jsx +115 -0
  181. package/template/src/cli/pages/security/Security.jsx +39 -0
  182. package/template/src/cli/pages/security/SecurityForm.jsx +106 -0
  183. package/template/src/cli/pages/todos/TodoActions.jsx +99 -0
  184. package/template/src/cli/pages/todos/TodoAdd.jsx +43 -0
  185. package/template/src/cli/pages/todos/TodoList.jsx +60 -0
  186. package/template/src/cli/pages/todos/Todos.jsx +23 -0
  187. package/template/src/cli/pages/todos/context/TodosContext.jsx +5 -0
  188. package/template/src/cli/pages/todos/context/TodosProvider.jsx +191 -0
  189. package/template/src/cli/pages/todos/context/useTodosContext.mjs +4 -0
  190. package/template/src/ns/models/Todo.mjs +29 -0
  191. package/template/src/ns/models/TodoList.mjs +8 -0
  192. package/template/src/ns/models/User.mjs +40 -0
  193. package/template/src/server/bot/check_today.mjs +10 -0
  194. package/template/src/server/io/cache/base.mjs +21 -0
  195. package/template/src/server/io/db/filter.mjs +92 -0
  196. package/template/src/server/io/db/todos/delete.mjs +29 -0
  197. package/template/src/server/io/db/todos/find.mjs +13 -0
  198. package/template/src/server/io/db/todos/read.mjs +83 -0
  199. package/template/src/server/io/db/todos/toggle.mjs +37 -0
  200. package/template/src/server/io/db/todos/upsave.mjs +32 -0
  201. package/template/src/server/io/db/triggers/user.mjs +13 -0
  202. package/template/src/server/io/db/users/auth.mjs +132 -0
  203. package/template/src/server/io/db/users/pwd.mjs +38 -0
  204. package/template/src/server/io/db/users/save.mjs +17 -0
  205. package/template/src/server/miolo/auth/basic.mjs +15 -0
  206. package/template/src/server/miolo/auth/guest.mjs +3 -0
  207. package/template/src/server/miolo/auth/passport.mjs +73 -0
  208. package/template/src/server/miolo/cache.mjs +11 -0
  209. package/template/src/server/miolo/cron/foo.mjs +7 -0
  210. package/template/src/server/miolo/cron/index.mjs +28 -0
  211. package/template/src/server/miolo/cron/invalidate.mjs +21 -0
  212. package/template/src/server/miolo/db.mjs +36 -0
  213. package/template/src/server/miolo/http.mjs +14 -0
  214. package/template/src/server/miolo/index.mjs +43 -0
  215. package/template/src/server/miolo/routes/crud.mjs +16 -0
  216. package/template/src/server/miolo/routes/index.mjs +8 -0
  217. package/template/src/server/miolo/ssr/entry-server.jsx +13 -0
  218. package/template/src/server/miolo/ssr/loader.mjs +18 -0
  219. package/template/src/server/routes/index.mjs +66 -0
  220. package/template/src/server/routes/todos/mod.mjs +52 -0
  221. package/template/src/server/routes/todos/read.mjs +45 -0
  222. package/template/src/server/routes/todos/special.mjs +47 -0
  223. package/template/src/server/routes/users/user.mjs +54 -0
  224. package/template/src/server/server.mjs +10 -0
  225. package/template/src/server/utils/crypt.mjs +38 -0
  226. package/template/src/server/utils/io.mjs +15 -0
  227. package/template/src/server/utils/pwdfor.mjs +25 -0
  228. package/template/src/server/utils/schema.mjs +22 -0
  229. package/template/src/static/img/default/profile.png +0 -0
  230. package/template/src/static/img/favicon.ico +0 -0
  231. package/template/src/static/img/miolo_logo.png +0 -0
  232. package/template/src/static/img/miolo_name.png +0 -0
  233. package/template/src/static/public/manifest.json +21 -0
  234. package/template/src/static/public/sw.js +79 -0
  235. package/template/src/static/style/globals.css +156 -0
  236. package/template/src/static/style/json-tree.css +54 -0
  237. package/template/src/static/style/skeleton.css +49 -0
  238. package/bin/prod-build/build-client.mjs +0 -67
  239. package/bin/prod-build/build-server.mjs +0 -58
  240. package/bin/prod-run/restart.mjs +0 -9
  241. package/bin/prod-run/start.mjs +0 -15
  242. package/bin/prod-run/stop.mjs +0 -20
  243. package/src/engines/logger/verify.mjs +0 -22
  244. package/src/middleware/auth/credentials/index.mjs +0 -151
  245. package/src/middleware/auth/credentials/session/index.mjs +0 -24
  246. package/src/middleware/auth/credentials/session/store_koa_redis.mjs +0 -3
@@ -0,0 +1,660 @@
1
+ import { Slot } from "@radix-ui/react-slot"
2
+ import { cva } from "class-variance-authority"
3
+ import { PanelLeftIcon } from "lucide-react"
4
+ import {
5
+ createContext,
6
+ forwardRef,
7
+ useCallback,
8
+ useContext,
9
+ useEffect,
10
+ useMemo,
11
+ useState
12
+ } from "react"
13
+ import { Input } from "#cli/components/ui/input.jsx"
14
+ import { Button } from "#cli/components/ui/patched/button.jsx"
15
+ import { Separator } from "#cli/components/ui/separator.jsx"
16
+ import {
17
+ Sheet,
18
+ SheetContent,
19
+ SheetDescription,
20
+ SheetHeader,
21
+ SheetTitle
22
+ } from "#cli/components/ui/sheet.jsx"
23
+ import { Skeleton } from "#cli/components/ui/skeleton.jsx"
24
+ import {
25
+ Tooltip,
26
+ TooltipContent,
27
+ TooltipProvider,
28
+ TooltipTrigger
29
+ } from "#cli/components/ui/tooltip.jsx"
30
+ import useIsMobile from "#cli/hooks/useIsMobile.mjs"
31
+ import { cn } from "#cli/lib/utils.mjs"
32
+
33
+ const SIDEBAR_COOKIE_NAME = "sidebar_state"
34
+ const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
35
+ const SIDEBAR_WIDTH = "16rem"
36
+ const SIDEBAR_WIDTH_MOBILE = "18rem"
37
+ const SIDEBAR_WIDTH_ICON = "3rem"
38
+ const SIDEBAR_KEYBOARD_SHORTCUT = "b"
39
+
40
+ const SidebarContext = createContext(null)
41
+
42
+ function useSidebar() {
43
+ const context = useContext(SidebarContext)
44
+ if (!context) {
45
+ throw new Error("useSidebar must be used within a SidebarProvider.")
46
+ }
47
+
48
+ return context
49
+ }
50
+
51
+ function SidebarProvider({
52
+ defaultOpen = true,
53
+ open: openProp,
54
+ onOpenChange: setOpenProp,
55
+ className,
56
+ style,
57
+ children,
58
+ ...props
59
+ }) {
60
+ const isMobile = useIsMobile()
61
+ const [openMobile, setOpenMobile] = useState(false)
62
+
63
+ // This is the internal state of the sidebar.
64
+ // We use openProp and setOpenProp for control from outside the component.
65
+ const [_open, _setOpen] = useState(defaultOpen)
66
+ const open = openProp ?? _open
67
+ const setOpen = useCallback(
68
+ (value) => {
69
+ const openState = typeof value === "function" ? value(open) : value
70
+ if (setOpenProp) {
71
+ setOpenProp(openState)
72
+ } else {
73
+ _setOpen(openState)
74
+ }
75
+
76
+ // This sets the cookie to keep the sidebar state.
77
+ // biome-ignore lint/suspicious/noDocumentCookie: biome-ignore
78
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
79
+ },
80
+ [setOpenProp, open]
81
+ )
82
+
83
+ // Helper to toggle the sidebar.
84
+ const toggleSidebar = useCallback(() => {
85
+ return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open)
86
+ }, [isMobile, setOpen])
87
+
88
+ // Adds a keyboard shortcut to toggle the sidebar.
89
+ useEffect(() => {
90
+ const handleKeyDown = (event) => {
91
+ if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
92
+ event.preventDefault()
93
+ toggleSidebar()
94
+ }
95
+ }
96
+
97
+ window.addEventListener("keydown", handleKeyDown)
98
+ return () => window.removeEventListener("keydown", handleKeyDown)
99
+ }, [toggleSidebar])
100
+
101
+ // We add a state so that we can do data-state="expanded" or "collapsed".
102
+ // This makes it easier to style the sidebar with Tailwind classes.
103
+ const state = open ? "expanded" : "collapsed"
104
+
105
+ const contextValue = useMemo(
106
+ () => ({
107
+ state,
108
+ open,
109
+ setOpen,
110
+ isMobile,
111
+ openMobile,
112
+ setOpenMobile,
113
+ toggleSidebar
114
+ }),
115
+ [state, open, setOpen, isMobile, openMobile, toggleSidebar]
116
+ )
117
+
118
+ return (
119
+ <SidebarContext.Provider value={contextValue}>
120
+ <TooltipProvider delayDuration={0}>
121
+ <div
122
+ data-slot="sidebar-wrapper"
123
+ style={{
124
+ "--sidebar-width": SIDEBAR_WIDTH,
125
+ "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
126
+ ...style
127
+ }}
128
+ className={cn(
129
+ "group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full",
130
+ className
131
+ )}
132
+ {...props}
133
+ >
134
+ {children}
135
+ </div>
136
+ </TooltipProvider>
137
+ </SidebarContext.Provider>
138
+ )
139
+ }
140
+
141
+ function Sidebar({
142
+ side = "left",
143
+ variant = "sidebar",
144
+ collapsible = "offcanvas",
145
+ className,
146
+ children,
147
+ ...props
148
+ }) {
149
+ const { isMobile, state, openMobile, setOpenMobile } = useSidebar()
150
+
151
+ if (collapsible === "none") {
152
+ return (
153
+ <div
154
+ data-slot="sidebar"
155
+ className={cn(
156
+ "bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col",
157
+ className
158
+ )}
159
+ {...props}
160
+ >
161
+ {children}
162
+ </div>
163
+ )
164
+ }
165
+
166
+ if (isMobile) {
167
+ return (
168
+ <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
169
+ <SheetContent
170
+ data-sidebar="sidebar"
171
+ data-slot="sidebar"
172
+ data-mobile="true"
173
+ className="bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
174
+ style={{
175
+ "--sidebar-width": SIDEBAR_WIDTH_MOBILE
176
+ }}
177
+ side={side}
178
+ >
179
+ <SheetHeader className="sr-only">
180
+ <SheetTitle>Sidebar</SheetTitle>
181
+ <SheetDescription>Displays the mobile sidebar.</SheetDescription>
182
+ </SheetHeader>
183
+ <div className="flex h-full w-full flex-col">{children}</div>
184
+ </SheetContent>
185
+ </Sheet>
186
+ )
187
+ }
188
+
189
+ return (
190
+ <div
191
+ className="group peer text-sidebar-foreground hidden md:block"
192
+ data-state={state}
193
+ data-collapsible={state === "collapsed" ? collapsible : ""}
194
+ data-variant={variant}
195
+ data-side={side}
196
+ data-slot="sidebar"
197
+ >
198
+ {/* This is what handles the sidebar gap on desktop */}
199
+ <div
200
+ data-slot="sidebar-gap"
201
+ className={cn(
202
+ "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
203
+ "group-data-[collapsible=offcanvas]:w-0",
204
+ "group-data-[side=right]:rotate-180",
205
+ variant === "floating" || variant === "inset"
206
+ ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"
207
+ : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
208
+ )}
209
+ />
210
+ <div
211
+ data-slot="sidebar-container"
212
+ className={cn(
213
+ "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
214
+ side === "left"
215
+ ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
216
+ : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
217
+ // Adjust the padding for floating and inset variants.
218
+ variant === "floating" || variant === "inset"
219
+ ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
220
+ : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
221
+ className
222
+ )}
223
+ {...props}
224
+ >
225
+ <div
226
+ data-sidebar="sidebar"
227
+ data-slot="sidebar-inner"
228
+ className="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm"
229
+ >
230
+ {children}
231
+ </div>
232
+ </div>
233
+ </div>
234
+ )
235
+ }
236
+
237
+ function SidebarTrigger({ className, onClick, ...props }) {
238
+ const { toggleSidebar } = useSidebar()
239
+
240
+ return (
241
+ <Button
242
+ data-sidebar="trigger"
243
+ data-slot="sidebar-trigger"
244
+ variant="ghost"
245
+ size="icon"
246
+ className={cn("size-7", className)}
247
+ onClick={(event) => {
248
+ onClick?.(event)
249
+ toggleSidebar()
250
+ }}
251
+ {...props}
252
+ >
253
+ <PanelLeftIcon />
254
+ <span className="sr-only">Toggle Sidebar</span>
255
+ </Button>
256
+ )
257
+ }
258
+
259
+ function SidebarRail({ className, ...props }) {
260
+ const { toggleSidebar } = useSidebar()
261
+
262
+ return (
263
+ <button
264
+ data-sidebar="rail"
265
+ data-slot="sidebar-rail"
266
+ aria-label="Toggle Sidebar"
267
+ tabIndex={-1}
268
+ onClick={toggleSidebar}
269
+ title="Toggle Sidebar"
270
+ className={cn(
271
+ "hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex",
272
+ "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
273
+ "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
274
+ "hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
275
+ "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
276
+ "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
277
+ className
278
+ )}
279
+ {...props}
280
+ />
281
+ )
282
+ }
283
+
284
+ function SidebarInset({ className, ...props }) {
285
+ return (
286
+ <main
287
+ data-slot="sidebar-inset"
288
+ className={cn(
289
+ "bg-background relative flex w-full flex-1 flex-col",
290
+ "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
291
+ className
292
+ )}
293
+ {...props}
294
+ />
295
+ )
296
+ }
297
+
298
+ function SidebarInput({ className, ...props }) {
299
+ return (
300
+ <Input
301
+ data-slot="sidebar-input"
302
+ data-sidebar="input"
303
+ className={cn("bg-background h-8 w-full shadow-none", className)}
304
+ {...props}
305
+ />
306
+ )
307
+ }
308
+
309
+ function SidebarHeader({ className, ...props }) {
310
+ return (
311
+ <div
312
+ data-slot="sidebar-header"
313
+ data-sidebar="header"
314
+ className={cn("flex flex-col gap-2 p-2", className)}
315
+ {...props}
316
+ />
317
+ )
318
+ }
319
+
320
+ function SidebarFooter({ className, ...props }) {
321
+ return (
322
+ <div
323
+ data-slot="sidebar-footer"
324
+ data-sidebar="footer"
325
+ className={cn("flex flex-col gap-2 p-2", className)}
326
+ {...props}
327
+ />
328
+ )
329
+ }
330
+
331
+ function SidebarSeparator({ className, ...props }) {
332
+ return (
333
+ <Separator
334
+ data-slot="sidebar-separator"
335
+ data-sidebar="separator"
336
+ className={cn("bg-sidebar-border mx-2 w-auto", className)}
337
+ {...props}
338
+ />
339
+ )
340
+ }
341
+
342
+ function SidebarContent({ className, ...props }) {
343
+ return (
344
+ <div
345
+ data-slot="sidebar-content"
346
+ data-sidebar="content"
347
+ className={cn(
348
+ "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
349
+ className
350
+ )}
351
+ {...props}
352
+ />
353
+ )
354
+ }
355
+
356
+ function SidebarGroup({ className, ...props }) {
357
+ return (
358
+ <div
359
+ data-slot="sidebar-group"
360
+ data-sidebar="group"
361
+ className={cn("relative flex w-full min-w-0 flex-col p-2", className)}
362
+ {...props}
363
+ />
364
+ )
365
+ }
366
+
367
+ function SidebarGroupLabel({ className, asChild = false, ...props }) {
368
+ const Comp = asChild ? Slot : "div"
369
+
370
+ return (
371
+ <Comp
372
+ data-slot="sidebar-group-label"
373
+ data-sidebar="group-label"
374
+ className={cn(
375
+ "text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
376
+ "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
377
+ className
378
+ )}
379
+ {...props}
380
+ />
381
+ )
382
+ }
383
+
384
+ function SidebarGroupAction({ className, asChild = false, ...props }) {
385
+ const Comp = asChild ? Slot : "button"
386
+
387
+ return (
388
+ <Comp
389
+ data-slot="sidebar-group-action"
390
+ data-sidebar="group-action"
391
+ className={cn(
392
+ "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
393
+ // Increases the hit area of the button on mobile.
394
+ "after:absolute after:-inset-2 md:after:hidden",
395
+ "group-data-[collapsible=icon]:hidden",
396
+ className
397
+ )}
398
+ {...props}
399
+ />
400
+ )
401
+ }
402
+
403
+ function SidebarGroupContent({ className, ...props }) {
404
+ return (
405
+ <div
406
+ data-slot="sidebar-group-content"
407
+ data-sidebar="group-content"
408
+ className={cn("w-full text-sm", className)}
409
+ {...props}
410
+ />
411
+ )
412
+ }
413
+
414
+ function SidebarMenu({ className, ...props }) {
415
+ return (
416
+ <ul
417
+ data-slot="sidebar-menu"
418
+ data-sidebar="menu"
419
+ className={cn("flex w-full min-w-0 flex-col gap-1", className)}
420
+ {...props}
421
+ />
422
+ )
423
+ }
424
+
425
+ function SidebarMenuItem({ className, ...props }) {
426
+ return (
427
+ <li
428
+ data-slot="sidebar-menu-item"
429
+ data-sidebar="menu-item"
430
+ className={cn("group/menu-item relative", className)}
431
+ {...props}
432
+ />
433
+ )
434
+ }
435
+
436
+ const sidebarMenuButtonVariants = cva(
437
+ "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
438
+ {
439
+ variants: {
440
+ variant: {
441
+ default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
442
+ outline:
443
+ "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]"
444
+ },
445
+ size: {
446
+ default: "h-8 text-sm",
447
+ sm: "h-7 text-xs",
448
+ lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!"
449
+ }
450
+ },
451
+ defaultVariants: {
452
+ variant: "default",
453
+ size: "default"
454
+ }
455
+ }
456
+ )
457
+
458
+ const SidebarMenuButton = forwardRef(
459
+ (
460
+ {
461
+ asChild = false,
462
+ isActive = false,
463
+ variant = "default",
464
+ size = "default",
465
+ tooltip,
466
+ className,
467
+ ...props
468
+ },
469
+ ref
470
+ ) => {
471
+ const Comp = asChild ? Slot : "button"
472
+ const { isMobile, state } = useSidebar()
473
+
474
+ const button = (
475
+ <Comp
476
+ ref={ref}
477
+ data-slot="sidebar-menu-button"
478
+ data-sidebar="menu-button"
479
+ data-size={size}
480
+ data-active={isActive}
481
+ className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
482
+ {...props}
483
+ />
484
+ )
485
+
486
+ if (!tooltip) {
487
+ return button
488
+ }
489
+
490
+ if (typeof tooltip === "string") {
491
+ tooltip = {
492
+ children: tooltip
493
+ }
494
+ }
495
+
496
+ return (
497
+ <Tooltip>
498
+ <TooltipTrigger asChild>{button}</TooltipTrigger>
499
+ <TooltipContent
500
+ side="right"
501
+ align="center"
502
+ hidden={state !== "collapsed" || isMobile}
503
+ {...tooltip}
504
+ />
505
+ </Tooltip>
506
+ )
507
+ }
508
+ )
509
+
510
+ const SidebarMenuAction = forwardRef(
511
+ ({ className, asChild = false, showOnHover = false, ...props }, ref) => {
512
+ const Comp = asChild ? Slot : "button"
513
+
514
+ return (
515
+ <Comp
516
+ ref={ref}
517
+ data-slot="sidebar-menu-action"
518
+ data-sidebar="menu-action"
519
+ className={cn(
520
+ "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
521
+ // Increases the hit area of the button on mobile.
522
+ "after:absolute after:-inset-2 md:after:hidden",
523
+ "peer-data-[size=sm]/menu-button:top-1",
524
+ "peer-data-[size=default]/menu-button:top-1.5",
525
+ "peer-data-[size=lg]/menu-button:top-2.5",
526
+ "group-data-[collapsible=icon]:hidden",
527
+ showOnHover &&
528
+ "peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0",
529
+ className
530
+ )}
531
+ {...props}
532
+ />
533
+ )
534
+ }
535
+ )
536
+
537
+ function SidebarMenuBadge({ className, ...props }) {
538
+ return (
539
+ <div
540
+ data-slot="sidebar-menu-badge"
541
+ data-sidebar="menu-badge"
542
+ className={cn(
543
+ "text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none",
544
+ "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground",
545
+ "peer-data-[size=sm]/menu-button:top-1",
546
+ "peer-data-[size=default]/menu-button:top-1.5",
547
+ "peer-data-[size=lg]/menu-button:top-2.5",
548
+ "group-data-[collapsible=icon]:hidden",
549
+ className
550
+ )}
551
+ {...props}
552
+ />
553
+ )
554
+ }
555
+
556
+ function SidebarMenuSkeleton({ className, showIcon = false, ...props }) {
557
+ // Random width between 50 to 90%.
558
+ const width = useMemo(() => {
559
+ return `${Math.floor(Math.random() * 40) + 50}%`
560
+ }, [])
561
+
562
+ return (
563
+ <div
564
+ data-slot="sidebar-menu-skeleton"
565
+ data-sidebar="menu-skeleton"
566
+ className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
567
+ {...props}
568
+ >
569
+ {showIcon && <Skeleton className="size-4 rounded-md" data-sidebar="menu-skeleton-icon" />}
570
+ <Skeleton
571
+ className="h-4 max-w-(--skeleton-width) flex-1"
572
+ data-sidebar="menu-skeleton-text"
573
+ style={{
574
+ "--skeleton-width": width
575
+ }}
576
+ />
577
+ </div>
578
+ )
579
+ }
580
+
581
+ function SidebarMenuSub({ className, ...props }) {
582
+ return (
583
+ <ul
584
+ data-slot="sidebar-menu-sub"
585
+ data-sidebar="menu-sub"
586
+ className={cn(
587
+ "border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5",
588
+ "group-data-[collapsible=icon]:hidden",
589
+ className
590
+ )}
591
+ {...props}
592
+ />
593
+ )
594
+ }
595
+
596
+ function SidebarMenuSubItem({ className, ...props }) {
597
+ return (
598
+ <li
599
+ data-slot="sidebar-menu-sub-item"
600
+ data-sidebar="menu-sub-item"
601
+ className={cn("group/menu-sub-item relative", className)}
602
+ {...props}
603
+ />
604
+ )
605
+ }
606
+
607
+ function SidebarMenuSubButton({
608
+ asChild = false,
609
+ size = "md",
610
+ isActive = false,
611
+ className,
612
+ ...props
613
+ }) {
614
+ const Comp = asChild ? Slot : "a"
615
+
616
+ return (
617
+ <Comp
618
+ data-slot="sidebar-menu-sub-button"
619
+ data-sidebar="menu-sub-button"
620
+ data-size={size}
621
+ data-active={isActive}
622
+ className={cn(
623
+ "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
624
+ "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
625
+ size === "sm" && "text-xs",
626
+ size === "md" && "text-sm",
627
+ "group-data-[collapsible=icon]:hidden",
628
+ className
629
+ )}
630
+ {...props}
631
+ />
632
+ )
633
+ }
634
+
635
+ export {
636
+ Sidebar,
637
+ SidebarContent,
638
+ SidebarFooter,
639
+ SidebarGroup,
640
+ SidebarGroupAction,
641
+ SidebarGroupContent,
642
+ SidebarGroupLabel,
643
+ SidebarHeader,
644
+ SidebarInput,
645
+ SidebarInset,
646
+ SidebarMenu,
647
+ SidebarMenuAction,
648
+ SidebarMenuBadge,
649
+ SidebarMenuButton,
650
+ SidebarMenuItem,
651
+ SidebarMenuSkeleton,
652
+ SidebarMenuSub,
653
+ SidebarMenuSubButton,
654
+ SidebarMenuSubItem,
655
+ SidebarProvider,
656
+ SidebarRail,
657
+ SidebarSeparator,
658
+ SidebarTrigger,
659
+ useSidebar
660
+ }