blodemd 0.0.10 → 0.0.12
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 +11 -47
- package/dev-server/app/layout.tsx +1 -1
- package/dist/cli.mjs +1078 -406
- package/dist/cli.mjs.map +1 -1
- package/docs/app/globals.css +15 -1
- package/docs/components/api/api-playground.tsx +2 -2
- package/docs/components/docs/copy-page-menu.tsx +55 -27
- package/docs/components/docs/doc-header.tsx +1 -1
- package/docs/components/docs/doc-shell.tsx +89 -88
- package/docs/components/docs/doc-sidebar.tsx +6 -3
- package/docs/components/docs/doc-toc.tsx +1 -1
- package/docs/components/docs/mobile-nav.tsx +8 -16
- package/docs/components/docs/sidebar-scroll-area.tsx +58 -0
- package/docs/components/git/repo-picker.tsx +526 -0
- package/docs/components/mdx/agent-instructions.tsx +17 -0
- package/docs/components/mdx/code-block.tsx +6 -1
- package/docs/components/mdx/code-group.tsx +1 -1
- package/docs/components/mdx/iframe.tsx +62 -0
- package/docs/components/mdx/index.tsx +4 -0
- package/docs/components/mdx/tabs.tsx +5 -5
- package/docs/components/mdx/video.tsx +45 -12
- package/docs/components/third-parties.tsx +29 -0
- package/docs/components/ui/badge.tsx +61 -0
- package/docs/components/ui/breadcrumb.tsx +61 -41
- package/docs/components/ui/button-group.tsx +83 -0
- package/docs/components/ui/button.tsx +30 -55
- package/docs/components/ui/command.tsx +32 -4
- package/docs/components/ui/copy-button.tsx +12 -19
- package/docs/components/ui/dialog.tsx +50 -1
- package/docs/components/ui/input.tsx +16 -97
- package/docs/components/ui/kbd.tsx +98 -0
- package/docs/components/ui/morph-icon.tsx +79 -0
- package/docs/components/ui/popover.tsx +225 -30
- package/docs/components/ui/search.tsx +0 -9
- package/docs/components/ui/sheet.tsx +30 -1
- package/docs/components/ui/sidebar.tsx +332 -7
- package/docs/components/ui/site-footer.tsx +6 -4
- package/docs/components/ui/skeleton.tsx +11 -0
- package/docs/components/ui/switch.tsx +32 -0
- package/docs/components/ui/tabs.tsx +138 -0
- package/docs/lib/api-client.ts +72 -0
- package/docs/lib/contextual-options.ts +9 -0
- package/docs/lib/dashboard-session.ts +167 -0
- package/docs/lib/db.ts +13 -0
- package/docs/lib/env.ts +4 -3
- package/docs/lib/etag.ts +22 -0
- package/docs/lib/github-install.ts +33 -0
- package/docs/lib/project-authz.ts +46 -0
- package/docs/lib/routes.ts +5 -1
- package/docs/lib/supabase.ts +30 -6
- package/docs/lib/tenancy.ts +1 -0
- package/docs/lib/tenant-static.ts +206 -4
- package/docs/lib/tenants.ts +5 -1
- package/docs/lib/time-ago.ts +24 -0
- package/docs/lib/use-tab-observer.ts +71 -0
- package/package.json +2 -2
- package/packages/@repo/contracts/dist/git.d.ts +28 -0
- package/packages/@repo/contracts/dist/git.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/git.js +24 -0
- package/packages/@repo/contracts/dist/index.d.ts +1 -1
- package/packages/@repo/contracts/dist/index.d.ts.map +1 -1
- package/packages/@repo/contracts/dist/index.js +1 -1
- package/packages/@repo/contracts/src/git.ts +31 -0
- package/packages/@repo/contracts/src/index.ts +1 -1
- package/packages/@repo/models/dist/docs-config.d.ts +9 -0
- package/packages/@repo/models/dist/docs-config.d.ts.map +1 -1
- package/packages/@repo/models/dist/docs-config.js +7 -0
- package/packages/@repo/models/src/docs-config.ts +7 -0
- package/packages/@repo/previewing/dist/index.d.ts +4 -0
- package/packages/@repo/previewing/dist/index.d.ts.map +1 -1
- package/packages/@repo/previewing/dist/index.js +53 -2
- package/packages/@repo/previewing/src/index.ts +64 -2
- package/packages/@repo/validation/src/blodemd-docs-schema.json +8 -1
- package/scripts/prepare-package.mjs +14 -0
- package/packages/@repo/contracts/dist/api-key.d.ts +0 -30
- package/packages/@repo/contracts/dist/api-key.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/api-key.js +0 -20
- package/packages/@repo/contracts/src/api-key.ts +0 -27
|
@@ -12,6 +12,18 @@ const Sheet = ({
|
|
|
12
12
|
<SheetPrimitive.Root data-slot="sheet" {...props} />
|
|
13
13
|
);
|
|
14
14
|
|
|
15
|
+
const SheetTrigger = ({
|
|
16
|
+
...props
|
|
17
|
+
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) => (
|
|
18
|
+
<SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const SheetClose = ({
|
|
22
|
+
...props
|
|
23
|
+
}: React.ComponentProps<typeof SheetPrimitive.Close>) => (
|
|
24
|
+
<SheetPrimitive.Close data-slot="sheet-close" {...props} />
|
|
25
|
+
);
|
|
26
|
+
|
|
15
27
|
const SheetPortal = ({
|
|
16
28
|
...props
|
|
17
29
|
}: React.ComponentProps<typeof SheetPrimitive.Portal>) => (
|
|
@@ -79,6 +91,14 @@ const SheetHeader = ({ className, ...props }: React.ComponentProps<"div">) => (
|
|
|
79
91
|
/>
|
|
80
92
|
);
|
|
81
93
|
|
|
94
|
+
const SheetFooter = ({ className, ...props }: React.ComponentProps<"div">) => (
|
|
95
|
+
<div
|
|
96
|
+
data-slot="sheet-footer"
|
|
97
|
+
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
|
|
82
102
|
const SheetTitle = ({
|
|
83
103
|
className,
|
|
84
104
|
...props
|
|
@@ -101,4 +121,13 @@ const SheetDescription = ({
|
|
|
101
121
|
/>
|
|
102
122
|
);
|
|
103
123
|
|
|
104
|
-
export {
|
|
124
|
+
export {
|
|
125
|
+
Sheet,
|
|
126
|
+
SheetTrigger,
|
|
127
|
+
SheetClose,
|
|
128
|
+
SheetContent,
|
|
129
|
+
SheetHeader,
|
|
130
|
+
SheetFooter,
|
|
131
|
+
SheetTitle,
|
|
132
|
+
SheetDescription,
|
|
133
|
+
};
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
// oxlint-disable eslint-plugin-unicorn/no-document-cookie -- sidebar state persisted via cookie (by design)
|
|
3
|
+
// oxlint-disable eslint/no-shadow -- registry code; upstream uses `open` in nested scopes
|
|
4
|
+
// oxlint-disable eslint/no-param-reassign -- registry code normalises tooltip prop
|
|
5
|
+
// oxlint-disable eslint-plugin-react/button-has-type -- trigger intentionally uses default button behavior
|
|
6
|
+
// oxlint-disable eslint-plugin-react-perf/jsx-no-new-function-as-prop -- registry-defined trigger composition
|
|
2
7
|
|
|
3
8
|
import { mergeProps } from "@base-ui/react/merge-props";
|
|
4
9
|
import { useRender } from "@base-ui/react/use-render";
|
|
10
|
+
import { PanelLeftIcon } from "blode-icons-react";
|
|
5
11
|
import { cva } from "class-variance-authority";
|
|
6
12
|
import type { VariantProps } from "class-variance-authority";
|
|
7
13
|
import type * as React from "react";
|
|
@@ -14,6 +20,9 @@ import {
|
|
|
14
20
|
useState,
|
|
15
21
|
} from "react";
|
|
16
22
|
|
|
23
|
+
import { Button } from "@/components/ui/button";
|
|
24
|
+
import { Input } from "@/components/ui/input";
|
|
25
|
+
import { Separator } from "@/components/ui/separator";
|
|
17
26
|
import {
|
|
18
27
|
Sheet,
|
|
19
28
|
SheetContent,
|
|
@@ -21,6 +30,7 @@ import {
|
|
|
21
30
|
SheetHeader,
|
|
22
31
|
SheetTitle,
|
|
23
32
|
} from "@/components/ui/sheet";
|
|
33
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
24
34
|
import {
|
|
25
35
|
Tooltip,
|
|
26
36
|
TooltipContent,
|
|
@@ -88,7 +98,6 @@ const SidebarProvider = ({
|
|
|
88
98
|
}
|
|
89
99
|
|
|
90
100
|
// This sets the cookie to keep the sidebar state.
|
|
91
|
-
// oxlint-disable-next-line eslint-plugin-unicorn/no-document-cookie
|
|
92
101
|
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
|
|
93
102
|
},
|
|
94
103
|
[setOpenProp, open]
|
|
@@ -97,9 +106,7 @@ const SidebarProvider = ({
|
|
|
97
106
|
// Helper to toggle the sidebar.
|
|
98
107
|
const toggleSidebar = useCallback(
|
|
99
108
|
() =>
|
|
100
|
-
isMobile
|
|
101
|
-
? setOpenMobile((prevOpen) => !prevOpen)
|
|
102
|
-
: setOpen((prevOpen) => !prevOpen),
|
|
109
|
+
isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open),
|
|
103
110
|
[isMobile, setOpen]
|
|
104
111
|
);
|
|
105
112
|
|
|
@@ -263,6 +270,123 @@ const Sidebar = ({
|
|
|
263
270
|
);
|
|
264
271
|
};
|
|
265
272
|
|
|
273
|
+
const SidebarTrigger = ({
|
|
274
|
+
className,
|
|
275
|
+
onClick,
|
|
276
|
+
...props
|
|
277
|
+
}: React.ComponentProps<typeof Button>) => {
|
|
278
|
+
const { toggleSidebar } = useSidebar();
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<Button
|
|
282
|
+
className={cn("size-7", className)}
|
|
283
|
+
data-sidebar="trigger"
|
|
284
|
+
data-slot="sidebar-trigger"
|
|
285
|
+
onClick={(event) => {
|
|
286
|
+
onClick?.(event);
|
|
287
|
+
toggleSidebar();
|
|
288
|
+
}}
|
|
289
|
+
size="icon"
|
|
290
|
+
variant="ghost"
|
|
291
|
+
{...props}
|
|
292
|
+
>
|
|
293
|
+
<PanelLeftIcon />
|
|
294
|
+
<span className="sr-only">Toggle Sidebar</span>
|
|
295
|
+
</Button>
|
|
296
|
+
);
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const SidebarRail = ({
|
|
300
|
+
className,
|
|
301
|
+
...props
|
|
302
|
+
}: React.ComponentProps<"button">) => {
|
|
303
|
+
const { toggleSidebar } = useSidebar();
|
|
304
|
+
|
|
305
|
+
return (
|
|
306
|
+
<button
|
|
307
|
+
aria-label="Toggle Sidebar"
|
|
308
|
+
className={cn(
|
|
309
|
+
"absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex",
|
|
310
|
+
"in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
|
|
311
|
+
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
|
|
312
|
+
"group-data-[collapsible=offcanvas]:translate-x-0 hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:after:left-full",
|
|
313
|
+
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
|
|
314
|
+
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
|
|
315
|
+
className
|
|
316
|
+
)}
|
|
317
|
+
data-sidebar="rail"
|
|
318
|
+
data-slot="sidebar-rail"
|
|
319
|
+
onClick={toggleSidebar}
|
|
320
|
+
tabIndex={-1}
|
|
321
|
+
title="Toggle Sidebar"
|
|
322
|
+
{...props}
|
|
323
|
+
/>
|
|
324
|
+
);
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const SidebarInset = ({
|
|
328
|
+
className,
|
|
329
|
+
...props
|
|
330
|
+
}: React.ComponentProps<"main">) => (
|
|
331
|
+
<main
|
|
332
|
+
className={cn(
|
|
333
|
+
"relative flex w-full flex-1 flex-col bg-background",
|
|
334
|
+
"md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2 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",
|
|
335
|
+
className
|
|
336
|
+
)}
|
|
337
|
+
data-slot="sidebar-inset"
|
|
338
|
+
{...props}
|
|
339
|
+
/>
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
const SidebarInput = ({
|
|
343
|
+
className,
|
|
344
|
+
...props
|
|
345
|
+
}: React.ComponentProps<typeof Input>) => (
|
|
346
|
+
<Input
|
|
347
|
+
className={cn("h-8 w-full bg-background shadow-none", className)}
|
|
348
|
+
data-sidebar="input"
|
|
349
|
+
data-slot="sidebar-input"
|
|
350
|
+
{...props}
|
|
351
|
+
/>
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
const SidebarHeader = ({
|
|
355
|
+
className,
|
|
356
|
+
...props
|
|
357
|
+
}: React.ComponentProps<"div">) => (
|
|
358
|
+
<div
|
|
359
|
+
className={cn("flex flex-col gap-2 p-2", className)}
|
|
360
|
+
data-sidebar="header"
|
|
361
|
+
data-slot="sidebar-header"
|
|
362
|
+
{...props}
|
|
363
|
+
/>
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
const SidebarFooter = ({
|
|
367
|
+
className,
|
|
368
|
+
...props
|
|
369
|
+
}: React.ComponentProps<"div">) => (
|
|
370
|
+
<div
|
|
371
|
+
className={cn("flex flex-col gap-2 p-2", className)}
|
|
372
|
+
data-sidebar="footer"
|
|
373
|
+
data-slot="sidebar-footer"
|
|
374
|
+
{...props}
|
|
375
|
+
/>
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
const SidebarSeparator = ({
|
|
379
|
+
className,
|
|
380
|
+
...props
|
|
381
|
+
}: React.ComponentProps<typeof Separator>) => (
|
|
382
|
+
<Separator
|
|
383
|
+
className={cn("mx-2 w-auto bg-sidebar-border", className)}
|
|
384
|
+
data-sidebar="separator"
|
|
385
|
+
data-slot="sidebar-separator"
|
|
386
|
+
{...props}
|
|
387
|
+
/>
|
|
388
|
+
);
|
|
389
|
+
|
|
266
390
|
const SidebarContent = ({
|
|
267
391
|
className,
|
|
268
392
|
...props
|
|
@@ -312,6 +436,33 @@ const SidebarGroupLabel = ({
|
|
|
312
436
|
},
|
|
313
437
|
});
|
|
314
438
|
|
|
439
|
+
const SidebarGroupAction = ({
|
|
440
|
+
className,
|
|
441
|
+
asChild = false,
|
|
442
|
+
children,
|
|
443
|
+
...props
|
|
444
|
+
}: React.ComponentProps<"button"> & { asChild?: boolean }) =>
|
|
445
|
+
useRender({
|
|
446
|
+
defaultTagName: "button",
|
|
447
|
+
props: mergeProps<"button">(
|
|
448
|
+
{
|
|
449
|
+
className: cn(
|
|
450
|
+
"absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-hidden ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
451
|
+
// Increases the hit area of the button on mobile.
|
|
452
|
+
"after:absolute after:-inset-2 md:after:hidden",
|
|
453
|
+
"group-data-[collapsible=icon]:hidden",
|
|
454
|
+
className
|
|
455
|
+
),
|
|
456
|
+
},
|
|
457
|
+
asChild ? props : { ...props, children }
|
|
458
|
+
),
|
|
459
|
+
render: asChild ? (children as React.ReactElement) : undefined,
|
|
460
|
+
state: {
|
|
461
|
+
sidebar: "group-action",
|
|
462
|
+
slot: "sidebar-group-action",
|
|
463
|
+
},
|
|
464
|
+
});
|
|
465
|
+
|
|
315
466
|
const SidebarGroupContent = ({
|
|
316
467
|
className,
|
|
317
468
|
...props
|
|
@@ -404,8 +555,11 @@ const SidebarMenuButton = ({
|
|
|
404
555
|
return button;
|
|
405
556
|
}
|
|
406
557
|
|
|
407
|
-
|
|
408
|
-
|
|
558
|
+
if (typeof tooltip === "string") {
|
|
559
|
+
tooltip = {
|
|
560
|
+
children: tooltip,
|
|
561
|
+
};
|
|
562
|
+
}
|
|
409
563
|
|
|
410
564
|
return (
|
|
411
565
|
<Tooltip>
|
|
@@ -414,20 +568,191 @@ const SidebarMenuButton = ({
|
|
|
414
568
|
align="center"
|
|
415
569
|
hidden={state !== "collapsed" || isMobile}
|
|
416
570
|
side="right"
|
|
417
|
-
{...
|
|
571
|
+
{...tooltip}
|
|
418
572
|
/>
|
|
419
573
|
</Tooltip>
|
|
420
574
|
);
|
|
421
575
|
};
|
|
422
576
|
|
|
577
|
+
const SidebarMenuAction = ({
|
|
578
|
+
className,
|
|
579
|
+
asChild = false,
|
|
580
|
+
showOnHover = false,
|
|
581
|
+
children,
|
|
582
|
+
...props
|
|
583
|
+
}: React.ComponentProps<"button"> & {
|
|
584
|
+
asChild?: boolean;
|
|
585
|
+
showOnHover?: boolean;
|
|
586
|
+
}) =>
|
|
587
|
+
useRender({
|
|
588
|
+
defaultTagName: "button",
|
|
589
|
+
props: mergeProps<"button">(
|
|
590
|
+
{
|
|
591
|
+
className: cn(
|
|
592
|
+
"absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-hidden ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0",
|
|
593
|
+
// Increases the hit area of the button on mobile.
|
|
594
|
+
"after:absolute after:-inset-2 md:after:hidden",
|
|
595
|
+
"peer-data-[size=sm]/menu-button:top-1",
|
|
596
|
+
"peer-data-[size=default]/menu-button:top-1.5",
|
|
597
|
+
"peer-data-[size=lg]/menu-button:top-2.5",
|
|
598
|
+
"group-data-[collapsible=icon]:hidden",
|
|
599
|
+
showOnHover &&
|
|
600
|
+
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active]/menu-button:text-sidebar-accent-foreground md:opacity-0",
|
|
601
|
+
className
|
|
602
|
+
),
|
|
603
|
+
},
|
|
604
|
+
asChild ? props : { ...props, children }
|
|
605
|
+
),
|
|
606
|
+
render: asChild ? (children as React.ReactElement) : undefined,
|
|
607
|
+
state: {
|
|
608
|
+
sidebar: "menu-action",
|
|
609
|
+
slot: "sidebar-menu-action",
|
|
610
|
+
},
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
const SidebarMenuBadge = ({
|
|
614
|
+
className,
|
|
615
|
+
...props
|
|
616
|
+
}: React.ComponentProps<"div">) => (
|
|
617
|
+
<div
|
|
618
|
+
className={cn(
|
|
619
|
+
"pointer-events-none absolute right-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 font-medium text-sidebar-foreground text-xs tabular-nums",
|
|
620
|
+
"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active]/menu-button:text-sidebar-accent-foreground",
|
|
621
|
+
"peer-data-[size=sm]/menu-button:top-1",
|
|
622
|
+
"peer-data-[size=default]/menu-button:top-1.5",
|
|
623
|
+
"peer-data-[size=lg]/menu-button:top-2.5",
|
|
624
|
+
"group-data-[collapsible=icon]:hidden",
|
|
625
|
+
className
|
|
626
|
+
)}
|
|
627
|
+
data-sidebar="menu-badge"
|
|
628
|
+
data-slot="sidebar-menu-badge"
|
|
629
|
+
{...props}
|
|
630
|
+
/>
|
|
631
|
+
);
|
|
632
|
+
|
|
633
|
+
const SidebarMenuSkeleton = ({
|
|
634
|
+
className,
|
|
635
|
+
showIcon = false,
|
|
636
|
+
...props
|
|
637
|
+
}: React.ComponentProps<"div"> & {
|
|
638
|
+
showIcon?: boolean;
|
|
639
|
+
}) => {
|
|
640
|
+
// Random width between 50 to 90%.
|
|
641
|
+
const width = useMemo(() => `${Math.floor(Math.random() * 40) + 50}%`, []);
|
|
642
|
+
|
|
643
|
+
return (
|
|
644
|
+
<div
|
|
645
|
+
className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
|
|
646
|
+
data-sidebar="menu-skeleton"
|
|
647
|
+
data-slot="sidebar-menu-skeleton"
|
|
648
|
+
{...props}
|
|
649
|
+
>
|
|
650
|
+
{showIcon && (
|
|
651
|
+
<Skeleton
|
|
652
|
+
className="size-4 rounded-md"
|
|
653
|
+
data-sidebar="menu-skeleton-icon"
|
|
654
|
+
/>
|
|
655
|
+
)}
|
|
656
|
+
<Skeleton
|
|
657
|
+
className="h-4 max-w-(--skeleton-width) flex-1"
|
|
658
|
+
data-sidebar="menu-skeleton-text"
|
|
659
|
+
style={
|
|
660
|
+
{
|
|
661
|
+
"--skeleton-width": width,
|
|
662
|
+
} as React.CSSProperties
|
|
663
|
+
}
|
|
664
|
+
/>
|
|
665
|
+
</div>
|
|
666
|
+
);
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
const SidebarMenuSub = ({
|
|
670
|
+
className,
|
|
671
|
+
...props
|
|
672
|
+
}: React.ComponentProps<"ul">) => (
|
|
673
|
+
<ul
|
|
674
|
+
className={cn(
|
|
675
|
+
"mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-sidebar-border border-l px-2.5 py-0.5",
|
|
676
|
+
"group-data-[collapsible=icon]:hidden",
|
|
677
|
+
className
|
|
678
|
+
)}
|
|
679
|
+
data-sidebar="menu-sub"
|
|
680
|
+
data-slot="sidebar-menu-sub"
|
|
681
|
+
{...props}
|
|
682
|
+
/>
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
const SidebarMenuSubItem = ({
|
|
686
|
+
className,
|
|
687
|
+
...props
|
|
688
|
+
}: React.ComponentProps<"li">) => (
|
|
689
|
+
<li
|
|
690
|
+
className={cn("group/menu-sub-item relative", className)}
|
|
691
|
+
data-sidebar="menu-sub-item"
|
|
692
|
+
data-slot="sidebar-menu-sub-item"
|
|
693
|
+
{...props}
|
|
694
|
+
/>
|
|
695
|
+
);
|
|
696
|
+
|
|
697
|
+
const SidebarMenuSubButton = ({
|
|
698
|
+
asChild = false,
|
|
699
|
+
size = "md",
|
|
700
|
+
isActive = false,
|
|
701
|
+
className,
|
|
702
|
+
children,
|
|
703
|
+
...props
|
|
704
|
+
}: React.ComponentProps<"a"> & {
|
|
705
|
+
asChild?: boolean;
|
|
706
|
+
size?: "sm" | "md";
|
|
707
|
+
isActive?: boolean;
|
|
708
|
+
}) =>
|
|
709
|
+
useRender({
|
|
710
|
+
defaultTagName: "a",
|
|
711
|
+
props: mergeProps<"a">(
|
|
712
|
+
{
|
|
713
|
+
className: cn(
|
|
714
|
+
"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-hidden ring-sidebar-ring 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 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
|
|
715
|
+
"data-[active]:bg-sidebar-accent data-[active]:text-sidebar-accent-foreground",
|
|
716
|
+
size === "sm" && "text-xs",
|
|
717
|
+
size === "md" && "text-sm",
|
|
718
|
+
"group-data-[collapsible=icon]:hidden",
|
|
719
|
+
className
|
|
720
|
+
),
|
|
721
|
+
},
|
|
722
|
+
asChild ? props : { ...props, children }
|
|
723
|
+
),
|
|
724
|
+
render: asChild ? (children as React.ReactElement) : undefined,
|
|
725
|
+
state: {
|
|
726
|
+
active: isActive,
|
|
727
|
+
sidebar: "menu-sub-button",
|
|
728
|
+
size,
|
|
729
|
+
slot: "sidebar-menu-sub-button",
|
|
730
|
+
},
|
|
731
|
+
});
|
|
732
|
+
|
|
423
733
|
export {
|
|
424
734
|
Sidebar,
|
|
425
735
|
SidebarContent,
|
|
736
|
+
SidebarFooter,
|
|
426
737
|
SidebarGroup,
|
|
738
|
+
SidebarGroupAction,
|
|
427
739
|
SidebarGroupContent,
|
|
428
740
|
SidebarGroupLabel,
|
|
741
|
+
SidebarHeader,
|
|
742
|
+
SidebarInput,
|
|
743
|
+
SidebarInset,
|
|
429
744
|
SidebarMenu,
|
|
745
|
+
SidebarMenuAction,
|
|
746
|
+
SidebarMenuBadge,
|
|
430
747
|
SidebarMenuButton,
|
|
431
748
|
SidebarMenuItem,
|
|
749
|
+
SidebarMenuSkeleton,
|
|
750
|
+
SidebarMenuSub,
|
|
751
|
+
SidebarMenuSubButton,
|
|
752
|
+
SidebarMenuSubItem,
|
|
432
753
|
SidebarProvider,
|
|
754
|
+
SidebarRail,
|
|
755
|
+
SidebarSeparator,
|
|
756
|
+
SidebarTrigger,
|
|
757
|
+
useSidebar,
|
|
433
758
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import Image from "next/image";
|
|
2
2
|
|
|
3
|
+
import { Badge } from "@/components/ui/badge";
|
|
3
4
|
import { siteConfig } from "@/lib/config";
|
|
4
5
|
|
|
5
6
|
export const SiteFooter = () => (
|
|
6
|
-
<footer className="flex flex-col items-center justify-center gap-
|
|
7
|
+
<footer className="flex flex-col items-center justify-center gap-3 pt-16 pb-8 text-muted-foreground text-sm">
|
|
7
8
|
<div className="flex items-center gap-1">
|
|
8
9
|
Crafted by
|
|
9
10
|
<a
|
|
@@ -23,9 +24,10 @@ export const SiteFooter = () => (
|
|
|
23
24
|
Matthew Blode
|
|
24
25
|
</a>
|
|
25
26
|
</div>
|
|
26
|
-
<div className="flex items-center gap-2
|
|
27
|
-
<
|
|
28
|
-
|
|
27
|
+
<div className="flex items-center gap-2">
|
|
28
|
+
<Badge className="font-mono" variant="outline">
|
|
29
|
+
v{siteConfig.version}
|
|
30
|
+
</Badge>
|
|
29
31
|
<a
|
|
30
32
|
className="text-muted-foreground transition-colors hover:text-foreground"
|
|
31
33
|
href={siteConfig.links.github}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
|
+
|
|
3
|
+
const Skeleton = ({ className, ...props }: React.ComponentProps<"div">) => (
|
|
4
|
+
<div
|
|
5
|
+
data-slot="skeleton"
|
|
6
|
+
className={cn("animate-pulse rounded-md bg-accent", className)}
|
|
7
|
+
{...props}
|
|
8
|
+
/>
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
export { Skeleton };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
const Switch = React.forwardRef<
|
|
9
|
+
React.ElementRef<typeof SwitchPrimitive.Root>,
|
|
10
|
+
React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>
|
|
11
|
+
>(({ className, ...props }, ref) => (
|
|
12
|
+
<SwitchPrimitive.Root
|
|
13
|
+
className={cn(
|
|
14
|
+
"peer inline-flex h-[24px] w-[42px] shrink-0 cursor-pointer items-center rounded-full transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background data-disabled:cursor-not-allowed data-checked:bg-primary data-unchecked:bg-input data-disabled:opacity-50 data-unchecked:hover:bg-input-hover",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
data-slot="switch"
|
|
18
|
+
ref={ref}
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
21
|
+
<SwitchPrimitive.Thumb
|
|
22
|
+
className={cn(
|
|
23
|
+
"pointer-events-none block size-[18px] rounded-full bg-card shadow-lg ring-0 transition-transform data-checked:translate-x-[21px] data-unchecked:translate-x-[3px] dark:bg-card-foreground"
|
|
24
|
+
)}
|
|
25
|
+
data-slot="switch-thumb"
|
|
26
|
+
/>
|
|
27
|
+
</SwitchPrimitive.Root>
|
|
28
|
+
));
|
|
29
|
+
|
|
30
|
+
Switch.displayName = SwitchPrimitive.Root.displayName;
|
|
31
|
+
|
|
32
|
+
export { Switch };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Tabs as TabsPrimitive } from "@base-ui/react/tabs";
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
import type { VariantProps } from "class-variance-authority";
|
|
6
|
+
import mergeRefs from "merge-refs";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
|
|
9
|
+
import { useTabObserver } from "@/hooks/use-tab-observer";
|
|
10
|
+
import { cn } from "@/lib/utils";
|
|
11
|
+
|
|
12
|
+
const Tabs = ({
|
|
13
|
+
className,
|
|
14
|
+
orientation = "horizontal",
|
|
15
|
+
...props
|
|
16
|
+
}: React.ComponentProps<typeof TabsPrimitive.Root>) => (
|
|
17
|
+
<TabsPrimitive.Root
|
|
18
|
+
className={cn("group/tabs flex gap-2 data-horizontal:flex-col", className)}
|
|
19
|
+
data-orientation={orientation}
|
|
20
|
+
data-slot="tabs"
|
|
21
|
+
orientation={orientation}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const tabsListVariants = cva(
|
|
27
|
+
"group/tabs-list relative isolate inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground data-[variant=line]:rounded-none group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col",
|
|
28
|
+
{
|
|
29
|
+
defaultVariants: {
|
|
30
|
+
variant: "default",
|
|
31
|
+
},
|
|
32
|
+
variants: {
|
|
33
|
+
variant: {
|
|
34
|
+
default: "bg-muted",
|
|
35
|
+
line: "gap-1 bg-transparent",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const TabsList = ({
|
|
42
|
+
className,
|
|
43
|
+
variant = "default",
|
|
44
|
+
ref,
|
|
45
|
+
...props
|
|
46
|
+
}: React.ComponentProps<typeof TabsPrimitive.List> &
|
|
47
|
+
VariantProps<typeof tabsListVariants>) => {
|
|
48
|
+
const [indicatorStyle, setIndicatorStyle] = React.useState({
|
|
49
|
+
height: 0,
|
|
50
|
+
left: 0,
|
|
51
|
+
top: 0,
|
|
52
|
+
width: 0,
|
|
53
|
+
});
|
|
54
|
+
const [hasIndicatorPosition, setHasIndicatorPosition] = React.useState(false);
|
|
55
|
+
const [canAnimateIndicator, setCanAnimateIndicator] = React.useState(false);
|
|
56
|
+
const hasInitializedIndicator = React.useRef(false);
|
|
57
|
+
const { listRef } = useTabObserver({
|
|
58
|
+
onActiveTabChange: (_, activeTab) => {
|
|
59
|
+
setIndicatorStyle({
|
|
60
|
+
height: activeTab.offsetHeight,
|
|
61
|
+
left: activeTab.offsetLeft,
|
|
62
|
+
top: activeTab.offsetTop,
|
|
63
|
+
width: activeTab.offsetWidth,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (!hasInitializedIndicator.current) {
|
|
67
|
+
hasInitializedIndicator.current = true;
|
|
68
|
+
setHasIndicatorPosition(true);
|
|
69
|
+
requestAnimationFrame(() => {
|
|
70
|
+
setCanAnimateIndicator(true);
|
|
71
|
+
});
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
setHasIndicatorPosition(true);
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<TabsPrimitive.List
|
|
81
|
+
className={cn(tabsListVariants({ variant }), className)}
|
|
82
|
+
data-slot="tabs-list"
|
|
83
|
+
data-variant={variant}
|
|
84
|
+
ref={mergeRefs(ref, listRef)}
|
|
85
|
+
{...props}
|
|
86
|
+
>
|
|
87
|
+
{variant === "default" ? (
|
|
88
|
+
<span
|
|
89
|
+
aria-hidden="true"
|
|
90
|
+
className={cn(
|
|
91
|
+
"pointer-events-none absolute top-0 left-0 z-0 rounded-md bg-background shadow-sm",
|
|
92
|
+
canAnimateIndicator
|
|
93
|
+
? "transition-[width,height,transform,opacity] duration-300"
|
|
94
|
+
: "transition-none",
|
|
95
|
+
hasIndicatorPosition ? "opacity-100" : "opacity-0"
|
|
96
|
+
)}
|
|
97
|
+
style={{
|
|
98
|
+
height: `${indicatorStyle.height}px`,
|
|
99
|
+
transform: `translate(${indicatorStyle.left}px, ${indicatorStyle.top}px)`,
|
|
100
|
+
transitionTimingFunction: "cubic-bezier(0.65, 0, 0.35, 1)",
|
|
101
|
+
width: `${indicatorStyle.width}px`,
|
|
102
|
+
}}
|
|
103
|
+
/>
|
|
104
|
+
) : null}
|
|
105
|
+
{props.children}
|
|
106
|
+
</TabsPrimitive.List>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const TabsTrigger = ({
|
|
111
|
+
className,
|
|
112
|
+
...props
|
|
113
|
+
}: React.ComponentProps<typeof TabsPrimitive.Tab>) => (
|
|
114
|
+
<TabsPrimitive.Tab
|
|
115
|
+
className={cn(
|
|
116
|
+
"relative z-10 inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-1.5 py-0.5 font-medium text-foreground/60 text-sm transition-all hover:text-foreground focus-visible:border-ring focus-visible:outline-1 focus-visible:outline-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start group-data-[variant=line]/tabs-list:data-active:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
117
|
+
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
|
|
118
|
+
"data-active:text-foreground group-data-[variant=default]/tabs-list:data-active:bg-transparent dark:data-active:text-foreground group-data-[variant=default]/tabs-list:dark:data-active:border-transparent group-data-[variant=default]/tabs-list:dark:data-active:bg-transparent",
|
|
119
|
+
"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
|
|
120
|
+
className
|
|
121
|
+
)}
|
|
122
|
+
data-slot="tabs-trigger"
|
|
123
|
+
{...props}
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const TabsContent = ({
|
|
128
|
+
className,
|
|
129
|
+
...props
|
|
130
|
+
}: React.ComponentProps<typeof TabsPrimitive.Panel>) => (
|
|
131
|
+
<TabsPrimitive.Panel
|
|
132
|
+
className={cn("flex-1 text-sm outline-none", className)}
|
|
133
|
+
data-slot="tabs-content"
|
|
134
|
+
{...props}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };
|