astro-awesomeness 0.4.0 → 0.5.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.
@@ -5,4 +5,4 @@ const cn = (...inputs) => twMerge(clsx(inputs));
5
5
  //#endregion
6
6
  export { cn as t };
7
7
 
8
- //# sourceMappingURL=cn-B3UhF_Ec.js.map
8
+ //# sourceMappingURL=cn-C_hacsED.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cn-B3UhF_Ec.js","names":[],"sources":["../src/lib/cn.ts"],"sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nconst cn = (...inputs: Array<ClassValue>) => twMerge(clsx(inputs));\n\nexport { cn };\n"],"mappings":";;;AAGA,MAAM,MAAM,GAAG,WAA8B,QAAQ,KAAK,MAAM,CAAC"}
1
+ {"version":3,"file":"cn-C_hacsED.js","names":[],"sources":["../src/lib/cn.ts"],"sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nconst cn = (...inputs: Array<ClassValue>) => twMerge(clsx(inputs));\n\nexport { cn };\n"],"mappings":";;;AAGA,MAAM,MAAM,GAAG,WAA8B,QAAQ,KAAK,MAAM,CAAC"}
@@ -1,42 +1,39 @@
1
1
  import { VariantProps } from "class-variance-authority";
2
2
  import * as React from "react";
3
- import { Dialog as Dialog$1 } from "radix-ui";
4
3
  import { Command as Command$1 } from "cmdk";
4
+ import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
5
5
 
6
6
  //#region src/components/ui/button.d.ts
7
7
  declare const buttonVariants: (props?: ({
8
8
  size?: "default" | "icon" | "icon-lg" | "icon-sm" | "icon-xs" | "lg" | "sm" | "xs" | null | undefined;
9
9
  variant?: "default" | "destructive" | "ghost" | "link" | "outline" | "secondary" | null | undefined;
10
10
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
11
- declare const Button: ({
12
- asChild,
13
- className,
14
- size,
15
- variant,
16
- ...props
17
- }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
18
- asChild?: boolean;
19
- }) => import("react/jsx-runtime").JSX.Element;
11
+ declare const Button: React.ForwardRefExoticComponent<Omit<Omit<Omit<import("@base-ui/react").ButtonProps, "ref"> & React.RefAttributes<HTMLElement>, "ref">, "className"> & VariantProps<(props?: ({
12
+ size?: "default" | "icon" | "icon-lg" | "icon-sm" | "icon-xs" | "lg" | "sm" | "xs" | null | undefined;
13
+ variant?: "default" | "destructive" | "ghost" | "link" | "outline" | "secondary" | null | undefined;
14
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
15
+ className?: string;
16
+ } & React.RefAttributes<HTMLElement>>;
20
17
  //#endregion
21
18
  //#region src/components/ui/dialog.d.ts
22
19
  declare function Dialog({
23
20
  ...props
24
- }: React.ComponentProps<typeof Dialog$1.Root>): import("react/jsx-runtime").JSX.Element;
21
+ }: React.ComponentProps<typeof Dialog$1.Root>): React.JSX.Element;
25
22
  declare function DialogTrigger({
26
23
  ...props
27
- }: React.ComponentProps<typeof Dialog$1.Trigger>): import("react/jsx-runtime").JSX.Element;
24
+ }: React.ComponentProps<typeof Dialog$1.Trigger>): React.JSX.Element;
28
25
  declare function DialogContent({
29
26
  children,
30
27
  className,
31
28
  showCloseButton,
32
29
  ...props
33
- }: React.ComponentProps<typeof Dialog$1.Content> & {
30
+ }: React.ComponentProps<typeof Dialog$1.Popup> & {
34
31
  showCloseButton?: boolean;
35
- }): import("react/jsx-runtime").JSX.Element;
32
+ }): React.JSX.Element;
36
33
  declare function DialogHeader({
37
34
  className,
38
35
  ...props
39
- }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
36
+ }: React.ComponentProps<"div">): React.JSX.Element;
40
37
  declare function DialogFooter({
41
38
  children,
42
39
  className,
@@ -44,48 +41,48 @@ declare function DialogFooter({
44
41
  ...props
45
42
  }: React.ComponentProps<"div"> & {
46
43
  showCloseButton?: boolean;
47
- }): import("react/jsx-runtime").JSX.Element;
44
+ }): React.JSX.Element;
48
45
  declare function DialogTitle({
49
46
  className,
50
47
  ...props
51
- }: React.ComponentProps<typeof Dialog$1.Title>): import("react/jsx-runtime").JSX.Element;
48
+ }: React.ComponentProps<typeof Dialog$1.Title>): React.JSX.Element;
52
49
  declare function DialogDescription({
53
50
  className,
54
51
  ...props
55
- }: React.ComponentProps<typeof Dialog$1.Description>): import("react/jsx-runtime").JSX.Element;
52
+ }: React.ComponentProps<typeof Dialog$1.Description>): React.JSX.Element;
56
53
  //#endregion
57
54
  //#region src/components/ui/command.d.ts
58
55
  declare function Command({
59
56
  className,
60
57
  ...props
61
- }: React.ComponentProps<typeof Command$1>): import("react/jsx-runtime").JSX.Element;
58
+ }: React.ComponentProps<typeof Command$1>): React.JSX.Element;
62
59
  declare function CommandInput({
63
60
  className,
64
61
  ...props
65
- }: React.ComponentProps<typeof Command$1.Input>): import("react/jsx-runtime").JSX.Element;
62
+ }: React.ComponentProps<typeof Command$1.Input>): React.JSX.Element;
66
63
  declare function CommandList({
67
64
  className,
68
65
  ...props
69
- }: React.ComponentProps<typeof Command$1.List>): import("react/jsx-runtime").JSX.Element;
66
+ }: React.ComponentProps<typeof Command$1.List>): React.JSX.Element;
70
67
  declare function CommandEmpty({
71
68
  ...props
72
- }: React.ComponentProps<typeof Command$1.Empty>): import("react/jsx-runtime").JSX.Element;
69
+ }: React.ComponentProps<typeof Command$1.Empty>): React.JSX.Element;
73
70
  declare function CommandGroup({
74
71
  className,
75
72
  ...props
76
- }: React.ComponentProps<typeof Command$1.Group>): import("react/jsx-runtime").JSX.Element;
73
+ }: React.ComponentProps<typeof Command$1.Group>): React.JSX.Element;
77
74
  declare function CommandSeparator({
78
75
  className,
79
76
  ...props
80
- }: React.ComponentProps<typeof Command$1.Separator>): import("react/jsx-runtime").JSX.Element;
77
+ }: React.ComponentProps<typeof Command$1.Separator>): React.JSX.Element;
81
78
  declare function CommandItem({
82
79
  className,
83
80
  ...props
84
- }: React.ComponentProps<typeof Command$1.Item>): import("react/jsx-runtime").JSX.Element;
81
+ }: React.ComponentProps<typeof Command$1.Item>): React.JSX.Element;
85
82
  declare function CommandShortcut({
86
83
  className,
87
84
  ...props
88
- }: React.ComponentProps<"span">): import("react/jsx-runtime").JSX.Element;
85
+ }: React.ComponentProps<"span">): React.JSX.Element;
89
86
  //#endregion
90
87
  //#region src/components/command-menu.d.ts
91
88
  type CommandMenuItem = {
@@ -104,7 +101,7 @@ declare const CommandMenu: ({
104
101
  groupHeading,
105
102
  items,
106
103
  placeholder
107
- }: CommandMenuProps) => import("react/jsx-runtime").JSX.Element;
104
+ }: CommandMenuProps) => import("react").JSX.Element;
108
105
  //#endregion
109
106
  //#region src/components/theme-toggle.d.ts
110
107
  type Props = {
@@ -112,7 +109,7 @@ type Props = {
112
109
  };
113
110
  declare const ThemeToggle: ({
114
111
  ariaLabel
115
- }: Props) => import("react/jsx-runtime").JSX.Element;
112
+ }: Props) => import("react").JSX.Element;
116
113
  //#endregion
117
114
  export { Button, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandMenu, type CommandMenuItem, type CommandMenuProps, CommandSeparator, CommandShortcut, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, ThemeToggle, buttonVariants };
118
115
  //# sourceMappingURL=index.d.ts.map
@@ -1,10 +1,12 @@
1
- import { t as cn } from "../cn-B3UhF_Ec.js";
1
+ import { t as cn } from "../cn-C_hacsED.js";
2
+ import { Button as Button$1 } from "@base-ui/react/button";
2
3
  import { cva } from "class-variance-authority";
4
+ import * as React from "react";
3
5
  import { useEffect, useState, useSyncExternalStore } from "react";
4
- import { Dialog as Dialog$1, Slot } from "radix-ui";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
  import { Command as Command$1 } from "cmdk";
7
8
  import { Moon, Search, SearchIcon, Sun, XIcon } from "lucide-react";
9
+ import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
8
10
  //#region src/components/ui/button.tsx
9
11
  const buttonVariants = cva("inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
10
12
  defaultVariants: {
@@ -32,8 +34,8 @@ const buttonVariants = cva("inline-flex shrink-0 items-center justify-center gap
32
34
  }
33
35
  }
34
36
  });
35
- const Button = ({ asChild = false, className, size = "default", variant = "default", ...props }) => {
36
- return /* @__PURE__ */ jsx(asChild ? Slot.Root : "button", {
37
+ const Button = React.forwardRef(({ className, size = "default", variant = "default", ...props }, ref) => {
38
+ return /* @__PURE__ */ jsx(Button$1, {
37
39
  className: cn(buttonVariants({
38
40
  className,
39
41
  size,
@@ -42,9 +44,11 @@ const Button = ({ asChild = false, className, size = "default", variant = "defau
42
44
  "data-size": size,
43
45
  "data-slot": "button",
44
46
  "data-variant": variant,
47
+ ref,
45
48
  ...props
46
49
  });
47
- };
50
+ });
51
+ Button.displayName = "Button";
48
52
  //#endregion
49
53
  //#region src/components/ui/dialog.tsx
50
54
  function Dialog({ ...props }) {
@@ -66,8 +70,8 @@ function DialogPortal({ ...props }) {
66
70
  });
67
71
  }
68
72
  function DialogOverlay({ className, ...props }) {
69
- return /* @__PURE__ */ jsx(Dialog$1.Overlay, {
70
- className: cn("fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0", className),
73
+ return /* @__PURE__ */ jsx(Dialog$1.Backdrop, {
74
+ className: cn("fixed inset-0 z-50 bg-black/50 data-[closed]:animate-out data-[closed]:fade-out-0 data-[open]:animate-in data-[open]:fade-in-0", className),
71
75
  "data-slot": "dialog-overlay",
72
76
  ...props
73
77
  });
@@ -75,13 +79,14 @@ function DialogOverlay({ className, ...props }) {
75
79
  function DialogContent({ children, className, showCloseButton = true, ...props }) {
76
80
  return /* @__PURE__ */ jsxs(DialogPortal, {
77
81
  "data-slot": "dialog-portal",
78
- children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(Dialog$1.Content, {
79
- className: cn("fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg", className),
82
+ children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(Dialog$1.Popup, {
83
+ className: cn("fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:zoom-out-95 data-[open]:animate-in data-[open]:fade-in-0 data-[open]:zoom-in-95 sm:max-w-lg", className),
80
84
  "data-slot": "dialog-content",
81
85
  ...props,
82
86
  children: [children, showCloseButton && /* @__PURE__ */ jsxs(Dialog$1.Close, {
83
87
  className: "absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
84
88
  "data-slot": "dialog-close",
89
+ type: "button",
85
90
  children: [/* @__PURE__ */ jsx(XIcon, {}), /* @__PURE__ */ jsx("span", {
86
91
  className: "sr-only",
87
92
  children: "Close"
@@ -103,11 +108,8 @@ function DialogFooter({ children, className, showCloseButton = false, ...props }
103
108
  "data-slot": "dialog-footer",
104
109
  ...props,
105
110
  children: [children, showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, {
106
- asChild: true,
107
- children: /* @__PURE__ */ jsx(Button, {
108
- variant: "outline",
109
- children: "Close"
110
- })
111
+ render: /* @__PURE__ */ jsx(Button, { variant: "outline" }),
112
+ children: "Close"
111
113
  })]
112
114
  });
113
115
  }
@@ -207,13 +209,12 @@ const CommandMenu = ({ emptyMessage = "No results.", groupHeading = "Posts", ite
207
209
  onOpenChange: setOpen,
208
210
  open,
209
211
  children: [/* @__PURE__ */ jsx(DialogTrigger, {
210
- asChild: true,
211
- children: /* @__PURE__ */ jsx(Button, {
212
+ render: /* @__PURE__ */ jsx(Button, {
212
213
  "aria-label": "Open search",
213
214
  size: "icon",
214
- variant: "ghost",
215
- children: /* @__PURE__ */ jsx(Search, {})
216
- })
215
+ variant: "ghost"
216
+ }),
217
+ children: /* @__PURE__ */ jsx(Search, {})
217
218
  }), /* @__PURE__ */ jsx(DialogContent, {
218
219
  className: "p-0",
219
220
  children: /* @__PURE__ */ jsxs(Command, { children: [/* @__PURE__ */ jsx(CommandInput, { placeholder }), /* @__PURE__ */ jsxs(CommandList, { children: [/* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }), /* @__PURE__ */ jsx(CommandGroup, {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["DialogPrimitive","CommandPrimitive"],"sources":["../../src/components/ui/button.tsx","../../src/components/ui/dialog.tsx","../../src/components/ui/command.tsx","../../src/components/command-menu.tsx","../../src/components/theme-toggle.tsx"],"sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { Slot } from \"radix-ui\";\n\nimport { cn } from \"../../lib/cn\";\n\nconst buttonVariants = cva(\n \"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n defaultVariants: {\n size: \"default\",\n variant: \"default\",\n },\n variants: {\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n icon: \"size-9\",\n \"icon-lg\": \"size-10\",\n \"icon-sm\": \"size-8\",\n \"icon-xs\": \"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n sm: \"h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5\",\n xs: \"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3\",\n },\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40\",\n ghost: \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n },\n },\n },\n);\n\nconst Button = ({\n asChild = false,\n className,\n size = \"default\",\n variant = \"default\",\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) => {\n const Comp = asChild ? Slot.Root : \"button\";\n\n return (\n <Comp\n className={cn(buttonVariants({ className, size, variant }))}\n data-size={size}\n data-slot=\"button\"\n data-variant={variant}\n {...props}\n />\n );\n};\n\nexport { Button, buttonVariants };\n","import * as React from \"react\";\nimport { XIcon } from \"lucide-react\";\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/cn\";\nimport { Button } from \"./button\";\n\nfunction Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />;\n}\n\nfunction DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nfunction DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nfunction DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n className={cn(\n \"fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0\",\n className,\n )}\n data-slot=\"dialog-overlay\"\n {...props}\n />\n );\n}\n\nfunction DialogContent({\n children,\n className,\n showCloseButton = true,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n showCloseButton?: boolean;\n}) {\n return (\n <DialogPortal data-slot=\"dialog-portal\">\n <DialogOverlay />\n <DialogPrimitive.Content\n className={cn(\n \"fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg\",\n className,\n )}\n data-slot=\"dialog-content\"\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close\n className=\"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n data-slot=\"dialog-close\"\n >\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n data-slot=\"dialog-header\"\n {...props}\n />\n );\n}\n\nfunction DialogFooter({\n children,\n className,\n showCloseButton = false,\n ...props\n}: React.ComponentProps<\"div\"> & {\n showCloseButton?: boolean;\n}) {\n return (\n <div\n className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n data-slot=\"dialog-footer\"\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close asChild>\n <Button variant=\"outline\">Close</Button>\n </DialogPrimitive.Close>\n )}\n </div>\n );\n}\n\nfunction DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n className={cn(\"text-lg leading-none font-semibold\", className)}\n data-slot=\"dialog-title\"\n {...props}\n />\n );\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n className={cn(\"text-sm text-muted-foreground\", className)}\n data-slot=\"dialog-description\"\n {...props}\n />\n );\n}\n\nexport {\n Dialog,\n // fallow-ignore-next-line unused-export\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n // fallow-ignore-next-line unused-export\n DialogOverlay,\n // fallow-ignore-next-line unused-export\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n};\n","import * as React from \"react\";\nimport { Command as CommandPrimitive } from \"cmdk\";\nimport { SearchIcon } from \"lucide-react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"./dialog\";\n\nfunction Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {\n return (\n <CommandPrimitive\n className={cn(\n \"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground\",\n className,\n )}\n data-slot=\"command\"\n {...props}\n />\n );\n}\n\nfunction CommandDialog({\n children,\n className,\n description = \"Search for a command to run...\",\n showCloseButton = true,\n title = \"Command Palette\",\n ...props\n}: React.ComponentProps<typeof Dialog> & {\n className?: string;\n description?: string;\n showCloseButton?: boolean;\n title?: string;\n}) {\n return (\n <Dialog {...props}>\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\n <DialogContent\n className={cn(\"overflow-hidden p-0\", className)}\n showCloseButton={showCloseButton}\n >\n <Command className=\"**:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5\">\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n\nfunction CommandInput({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Input>) {\n return (\n <div className=\"flex h-9 items-center gap-2 border-b px-3\" data-slot=\"command-input-wrapper\">\n <SearchIcon className=\"size-4 shrink-0 opacity-50\" />\n <CommandPrimitive.Input\n className={cn(\n \"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n data-slot=\"command-input\"\n {...props}\n />\n </div>\n );\n}\n\nfunction CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {\n return (\n <CommandPrimitive.List\n className={cn(\"max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto\", className)}\n data-slot=\"command-list\"\n {...props}\n />\n );\n}\n\nfunction CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {\n return (\n <CommandPrimitive.Empty\n className=\"py-6 text-center text-sm\"\n data-slot=\"command-empty\"\n {...props}\n />\n );\n}\n\nfunction CommandGroup({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Group>) {\n return (\n <CommandPrimitive.Group\n className={cn(\n \"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground\",\n className,\n )}\n data-slot=\"command-group\"\n {...props}\n />\n );\n}\n\nfunction CommandSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Separator>) {\n return (\n <CommandPrimitive.Separator\n className={cn(\"-mx-1 h-px bg-border\", className)}\n data-slot=\"command-separator\"\n {...props}\n />\n );\n}\n\nfunction CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {\n return (\n <CommandPrimitive.Item\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className,\n )}\n data-slot=\"command-item\"\n {...props}\n />\n );\n}\n\nfunction CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n className={cn(\"ml-auto text-xs tracking-widest text-muted-foreground\", className)}\n data-slot=\"command-shortcut\"\n {...props}\n />\n );\n}\n\nexport {\n Command,\n // fallow-ignore-next-line unused-export\n CommandDialog,\n CommandInput,\n CommandList,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandShortcut,\n CommandSeparator,\n};\n","import { Search } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"./ui/command\";\nimport { Dialog, DialogContent, DialogTrigger } from \"./ui/dialog\";\nimport { Button } from \"./ui/button\";\n\ntype CommandMenuItem = { description?: string; title: string; url: string };\n\ntype CommandMenuProps = {\n emptyMessage?: string;\n groupHeading?: string;\n items: Array<CommandMenuItem>;\n placeholder?: string;\n};\n\nconst CommandMenu = ({\n emptyMessage = \"No results.\",\n groupHeading = \"Posts\",\n items,\n placeholder = \"Search posts…\",\n}: CommandMenuProps) => {\n const [open, setOpen] = useState(false);\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"k\" && (event.metaKey || event.ctrlKey)) {\n event.preventDefault();\n setOpen((value) => !value);\n }\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, []);\n\n return (\n <Dialog onOpenChange={setOpen} open={open}>\n <DialogTrigger asChild>\n <Button aria-label=\"Open search\" size=\"icon\" variant=\"ghost\">\n <Search />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"p-0\">\n <Command>\n <CommandInput placeholder={placeholder} />\n <CommandList>\n <CommandEmpty>{emptyMessage}</CommandEmpty>\n <CommandGroup heading={groupHeading}>\n {items.map((item) => (\n <CommandItem\n key={item.url}\n onSelect={() => {\n window.location.href = item.url;\n }}\n value={item.title}\n >\n <div className=\"flex flex-col\">\n <span>{item.title}</span>\n {item.description ? (\n <span className=\"text-muted-foreground text-xs\">{item.description}</span>\n ) : null}\n </div>\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </DialogContent>\n </Dialog>\n );\n};\n\nexport { CommandMenu };\nexport type { CommandMenuItem, CommandMenuProps };\n","import { Moon, Sun } from \"lucide-react\";\nimport { useState, useSyncExternalStore } from \"react\";\n\nimport { Button } from \"./ui/button\";\n\ntype Theme = \"light\" | \"dark\";\n\nconst getStoredTheme = (): Theme | null => {\n if (typeof window === \"undefined\") {\n return null;\n }\n const stored = window.localStorage.getItem(\"theme\");\n if (stored === \"light\" || stored === \"dark\") {\n return stored;\n }\n return null;\n};\n\nconst getPrefersDark = (): boolean => {\n if (typeof window === \"undefined\") {\n return false;\n }\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n};\n\nconst subscribePrefersDark = (callback: () => void) => {\n if (typeof window === \"undefined\") {\n return () => {};\n }\n const media = window.matchMedia(\"(prefers-color-scheme: dark)\");\n media.addEventListener(\"change\", callback);\n return () => {\n media.removeEventListener(\"change\", callback);\n };\n};\n\ntype Props = {\n ariaLabel?: string;\n};\n\nconst ThemeToggle = ({ ariaLabel = \"Toggle theme\" }: Props) => {\n // useSyncExternalStore avoids hydration mismatch warnings while still letting\n // us read the actual system preference on the client.\n const prefersDark = useSyncExternalStore(subscribePrefersDark, getPrefersDark, () => false);\n const [override, setOverride] = useState<Theme | null>(() => getStoredTheme());\n const theme: Theme = override ?? (prefersDark ? \"dark\" : \"light\");\n\n const handleToggleTheme = () => {\n const next: Theme = theme === \"dark\" ? \"light\" : \"dark\";\n window.localStorage.setItem(\"theme\", next);\n document.documentElement.classList.toggle(\"dark\", next === \"dark\");\n setOverride(next);\n };\n\n return (\n <Button aria-label={ariaLabel} onClick={handleToggleTheme} size=\"icon\" variant=\"ghost\">\n {theme === \"dark\" ? <Sun /> : <Moon />}\n </Button>\n );\n};\n\nexport { ThemeToggle };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["ButtonPrimitive","DialogPrimitive","CommandPrimitive"],"sources":["../../src/components/ui/button.tsx","../../src/components/ui/dialog.tsx","../../src/components/ui/command.tsx","../../src/components/command-menu.tsx","../../src/components/theme-toggle.tsx"],"sourcesContent":["import { Button as ButtonPrimitive } from \"@base-ui/react/button\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"../../lib/cn\";\n\nconst buttonVariants = cva(\n \"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n defaultVariants: {\n size: \"default\",\n variant: \"default\",\n },\n variants: {\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n icon: \"size-9\",\n \"icon-lg\": \"size-10\",\n \"icon-sm\": \"size-8\",\n \"icon-xs\": \"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n sm: \"h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5\",\n xs: \"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3\",\n },\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40\",\n ghost: \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n },\n },\n },\n);\n\ntype ButtonProps = Omit<React.ComponentPropsWithoutRef<typeof ButtonPrimitive>, \"className\"> &\n VariantProps<typeof buttonVariants> & {\n className?: string;\n };\n\nconst Button = React.forwardRef<React.ComponentRef<typeof ButtonPrimitive>, ButtonProps>(\n ({ className, size = \"default\", variant = \"default\", ...props }, ref) => {\n return (\n <ButtonPrimitive\n className={cn(buttonVariants({ className, size, variant }))}\n data-size={size}\n data-slot=\"button\"\n data-variant={variant}\n ref={ref}\n {...props}\n />\n );\n },\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n","import { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\";\nimport * as React from \"react\";\nimport { XIcon } from \"lucide-react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { Button } from \"./button\";\n\nfunction Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />;\n}\n\nfunction DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nfunction DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nfunction DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Backdrop>) {\n return (\n <DialogPrimitive.Backdrop\n className={cn(\n \"fixed inset-0 z-50 bg-black/50 data-[closed]:animate-out data-[closed]:fade-out-0 data-[open]:animate-in data-[open]:fade-in-0\",\n className,\n )}\n data-slot=\"dialog-overlay\"\n {...props}\n />\n );\n}\n\nfunction DialogContent({\n children,\n className,\n showCloseButton = true,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Popup> & {\n showCloseButton?: boolean;\n}) {\n return (\n <DialogPortal data-slot=\"dialog-portal\">\n <DialogOverlay />\n <DialogPrimitive.Popup\n className={cn(\n \"fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:zoom-out-95 data-[open]:animate-in data-[open]:fade-in-0 data-[open]:zoom-in-95 sm:max-w-lg\",\n className,\n )}\n data-slot=\"dialog-content\"\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close\n className=\"absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n data-slot=\"dialog-close\"\n type=\"button\"\n >\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Popup>\n </DialogPortal>\n );\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n data-slot=\"dialog-header\"\n {...props}\n />\n );\n}\n\nfunction DialogFooter({\n children,\n className,\n showCloseButton = false,\n ...props\n}: React.ComponentProps<\"div\"> & {\n showCloseButton?: boolean;\n}) {\n return (\n <div\n className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n data-slot=\"dialog-footer\"\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close render={<Button variant=\"outline\" />}>Close</DialogPrimitive.Close>\n )}\n </div>\n );\n}\n\nfunction DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n className={cn(\"text-lg leading-none font-semibold\", className)}\n data-slot=\"dialog-title\"\n {...props}\n />\n );\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n className={cn(\"text-sm text-muted-foreground\", className)}\n data-slot=\"dialog-description\"\n {...props}\n />\n );\n}\n\nexport {\n Dialog,\n // fallow-ignore-next-line unused-export\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n // fallow-ignore-next-line unused-export\n DialogOverlay,\n // fallow-ignore-next-line unused-export\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n};\n","import * as React from \"react\";\nimport { Command as CommandPrimitive } from \"cmdk\";\nimport { SearchIcon } from \"lucide-react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"./dialog\";\n\nfunction Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {\n return (\n <CommandPrimitive\n className={cn(\n \"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground\",\n className,\n )}\n data-slot=\"command\"\n {...props}\n />\n );\n}\n\nfunction CommandDialog({\n children,\n className,\n description = \"Search for a command to run...\",\n showCloseButton = true,\n title = \"Command Palette\",\n ...props\n}: React.ComponentProps<typeof Dialog> & {\n children?: React.ReactNode;\n className?: string;\n description?: string;\n showCloseButton?: boolean;\n title?: string;\n}) {\n return (\n <Dialog {...props}>\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\n <DialogContent\n className={cn(\"overflow-hidden p-0\", className)}\n showCloseButton={showCloseButton}\n >\n <Command className=\"**:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5\">\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n\nfunction CommandInput({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Input>) {\n return (\n <div className=\"flex h-9 items-center gap-2 border-b px-3\" data-slot=\"command-input-wrapper\">\n <SearchIcon className=\"size-4 shrink-0 opacity-50\" />\n <CommandPrimitive.Input\n className={cn(\n \"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n data-slot=\"command-input\"\n {...props}\n />\n </div>\n );\n}\n\nfunction CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {\n return (\n <CommandPrimitive.List\n className={cn(\"max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto\", className)}\n data-slot=\"command-list\"\n {...props}\n />\n );\n}\n\nfunction CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {\n return (\n <CommandPrimitive.Empty\n className=\"py-6 text-center text-sm\"\n data-slot=\"command-empty\"\n {...props}\n />\n );\n}\n\nfunction CommandGroup({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Group>) {\n return (\n <CommandPrimitive.Group\n className={cn(\n \"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground\",\n className,\n )}\n data-slot=\"command-group\"\n {...props}\n />\n );\n}\n\nfunction CommandSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof CommandPrimitive.Separator>) {\n return (\n <CommandPrimitive.Separator\n className={cn(\"-mx-1 h-px bg-border\", className)}\n data-slot=\"command-separator\"\n {...props}\n />\n );\n}\n\nfunction CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {\n return (\n <CommandPrimitive.Item\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className,\n )}\n data-slot=\"command-item\"\n {...props}\n />\n );\n}\n\nfunction CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n className={cn(\"ml-auto text-xs tracking-widest text-muted-foreground\", className)}\n data-slot=\"command-shortcut\"\n {...props}\n />\n );\n}\n\nexport {\n Command,\n // fallow-ignore-next-line unused-export\n CommandDialog,\n CommandInput,\n CommandList,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandShortcut,\n CommandSeparator,\n};\n","import { Search } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"./ui/command\";\nimport { Dialog, DialogContent, DialogTrigger } from \"./ui/dialog\";\nimport { Button } from \"./ui/button\";\n\ntype CommandMenuItem = { description?: string; title: string; url: string };\n\ntype CommandMenuProps = {\n emptyMessage?: string;\n groupHeading?: string;\n items: Array<CommandMenuItem>;\n placeholder?: string;\n};\n\nconst CommandMenu = ({\n emptyMessage = \"No results.\",\n groupHeading = \"Posts\",\n items,\n placeholder = \"Search posts…\",\n}: CommandMenuProps) => {\n const [open, setOpen] = useState(false);\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"k\" && (event.metaKey || event.ctrlKey)) {\n event.preventDefault();\n setOpen((value) => !value);\n }\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, []);\n\n return (\n <Dialog onOpenChange={setOpen} open={open}>\n <DialogTrigger render={<Button aria-label=\"Open search\" size=\"icon\" variant=\"ghost\" />}>\n <Search />\n </DialogTrigger>\n <DialogContent className=\"p-0\">\n <Command>\n <CommandInput placeholder={placeholder} />\n <CommandList>\n <CommandEmpty>{emptyMessage}</CommandEmpty>\n <CommandGroup heading={groupHeading}>\n {items.map((item) => (\n <CommandItem\n key={item.url}\n onSelect={() => {\n window.location.href = item.url;\n }}\n value={item.title}\n >\n <div className=\"flex flex-col\">\n <span>{item.title}</span>\n {item.description ? (\n <span className=\"text-muted-foreground text-xs\">{item.description}</span>\n ) : null}\n </div>\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </DialogContent>\n </Dialog>\n );\n};\n\nexport { CommandMenu };\nexport type { CommandMenuItem, CommandMenuProps };\n","import { Moon, Sun } from \"lucide-react\";\nimport { useState, useSyncExternalStore } from \"react\";\n\nimport { Button } from \"./ui/button\";\n\ntype Theme = \"light\" | \"dark\";\n\nconst getStoredTheme = (): Theme | null => {\n if (typeof window === \"undefined\") {\n return null;\n }\n const stored = window.localStorage.getItem(\"theme\");\n if (stored === \"light\" || stored === \"dark\") {\n return stored;\n }\n return null;\n};\n\nconst getPrefersDark = (): boolean => {\n if (typeof window === \"undefined\") {\n return false;\n }\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n};\n\nconst subscribePrefersDark = (callback: () => void) => {\n if (typeof window === \"undefined\") {\n return () => {};\n }\n const media = window.matchMedia(\"(prefers-color-scheme: dark)\");\n media.addEventListener(\"change\", callback);\n return () => {\n media.removeEventListener(\"change\", callback);\n };\n};\n\ntype Props = {\n ariaLabel?: string;\n};\n\nconst ThemeToggle = ({ ariaLabel = \"Toggle theme\" }: Props) => {\n // useSyncExternalStore avoids hydration mismatch on system preference reads.\n const prefersDark = useSyncExternalStore(subscribePrefersDark, getPrefersDark, () => false);\n const [override, setOverride] = useState<Theme | null>(() => getStoredTheme());\n const theme: Theme = override ?? (prefersDark ? \"dark\" : \"light\");\n\n const handleToggleTheme = () => {\n const next: Theme = theme === \"dark\" ? \"light\" : \"dark\";\n window.localStorage.setItem(\"theme\", next);\n document.documentElement.classList.toggle(\"dark\", next === \"dark\");\n setOverride(next);\n };\n\n return (\n <Button aria-label={ariaLabel} onClick={handleToggleTheme} size=\"icon\" variant=\"ghost\">\n {theme === \"dark\" ? <Sun /> : <Moon />}\n </Button>\n );\n};\n\nexport { ThemeToggle };\n"],"mappings":"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"}
@@ -2,16 +2,25 @@ import { z } from "zod";
2
2
 
3
3
  //#region src/content/index.d.ts
4
4
  declare namespace index_d_exports {
5
- export { Author, Post, Tag, authorSchema, postSchema, tagSchema };
5
+ export { Author, Post, Tag, authorSchema, notDraft, postSchema, tagSchema };
6
6
  }
7
7
  declare const postSchema: z.ZodObject<{
8
8
  author: z.ZodOptional<z.ZodString>;
9
+ categories: z.ZodDefault<z.ZodArray<z.ZodString>>;
9
10
  cover: z.ZodOptional<z.ZodString>;
10
11
  coverAlt: z.ZodOptional<z.ZodString>;
11
12
  description: z.ZodString;
12
13
  draft: z.ZodDefault<z.ZodBoolean>;
14
+ heroImageHeight: z.ZodOptional<z.ZodNumber>;
15
+ heroImageUrl: z.ZodOptional<z.ZodURL>;
16
+ heroImageWidth: z.ZodOptional<z.ZodNumber>;
13
17
  pubDate: z.ZodCoercedDate<unknown>;
18
+ seo: z.ZodOptional<z.ZodObject<{
19
+ canonical_url: z.ZodOptional<z.ZodString>;
20
+ }, z.core.$strip>>;
14
21
  series: z.ZodOptional<z.ZodString>;
22
+ slug: z.ZodOptional<z.ZodString>;
23
+ status: z.ZodOptional<z.ZodString>;
15
24
  tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
16
25
  title: z.ZodString;
17
26
  updatedDate: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -29,6 +38,12 @@ declare const authorSchema: z.ZodObject<{
29
38
  type Post = z.infer<typeof postSchema>;
30
39
  type Tag = z.infer<typeof tagSchema>;
31
40
  type Author = z.infer<typeof authorSchema>;
41
+ declare const notDraft: <T extends {
42
+ data: {
43
+ draft?: boolean;
44
+ status?: string;
45
+ };
46
+ }>(post: T) => boolean;
32
47
  //#endregion
33
- export { type Author, type Post, type Tag, authorSchema, postSchema, index_d_exports as t, tagSchema };
48
+ export { type Author, type Post, type Tag, authorSchema, notDraft, postSchema, index_d_exports as t, tagSchema };
34
49
  //# sourceMappingURL=index.d.ts.map
@@ -1,19 +1,27 @@
1
- import { t as __exportAll } from "../chunk-pbuEa-1d.js";
1
+ import { t as __exportAll } from "../chunk-D7D4PA-g.js";
2
2
  import { z } from "zod";
3
3
  //#region src/content/index.ts
4
4
  var content_exports = /* @__PURE__ */ __exportAll({
5
5
  authorSchema: () => authorSchema,
6
+ notDraft: () => notDraft,
6
7
  postSchema: () => postSchema,
7
8
  tagSchema: () => tagSchema
8
9
  });
9
10
  const postSchema = z.object({
10
11
  author: z.string().optional(),
12
+ categories: z.array(z.string()).default([]),
11
13
  cover: z.string().optional(),
12
14
  coverAlt: z.string().optional(),
13
15
  description: z.string().min(1),
14
16
  draft: z.boolean().default(false),
17
+ heroImageHeight: z.number().int().optional(),
18
+ heroImageUrl: z.url().optional(),
19
+ heroImageWidth: z.number().int().optional(),
15
20
  pubDate: z.coerce.date(),
21
+ seo: z.object({ canonical_url: z.string().optional() }).optional(),
16
22
  series: z.string().optional(),
23
+ slug: z.string().optional(),
24
+ status: z.string().optional(),
17
25
  tags: z.array(z.string()).default([]),
18
26
  title: z.string().min(1),
19
27
  updatedDate: z.coerce.date().optional()
@@ -28,7 +36,8 @@ const authorSchema = z.object({
28
36
  name: z.string().min(1),
29
37
  url: z.url().optional()
30
38
  });
39
+ const notDraft = (post) => post.data.status !== "DRAFT" && !post.data.draft;
31
40
  //#endregion
32
- export { authorSchema, postSchema, content_exports as t, tagSchema };
41
+ export { authorSchema, notDraft, postSchema, content_exports as t, tagSchema };
33
42
 
34
43
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/content/index.ts"],"sourcesContent":["import { z } from \"zod\";\n\nconst postSchema = z.object({\n author: z.string().optional(),\n cover: z.string().optional(),\n coverAlt: z.string().optional(),\n description: z.string().min(1),\n draft: z.boolean().default(false),\n pubDate: z.coerce.date(),\n series: z.string().optional(),\n tags: z.array(z.string()).default([]),\n title: z.string().min(1),\n updatedDate: z.coerce.date().optional(),\n});\n\nconst tagSchema = z.object({\n description: z.string().optional(),\n name: z.string().min(1),\n});\n\nconst authorSchema = z.object({\n avatar: z.string().optional(),\n bio: z.string().optional(),\n name: z.string().min(1),\n url: z.url().optional(),\n});\n\ntype Post = z.infer<typeof postSchema>;\ntype Tag = z.infer<typeof tagSchema>;\ntype Author = z.infer<typeof authorSchema>;\n\nexport { authorSchema, postSchema, tagSchema };\nexport type { Author, Post, Tag };\n"],"mappings":";;;;;;;;AAEA,MAAM,aAAa,EAAE,OAAO;CAC1B,QAAQ,EAAE,OAAO,EAAE,SAAS;CAC5B,OAAO,EAAE,OAAO,EAAE,SAAS;CAC3B,UAAU,EAAE,OAAO,EAAE,SAAS;CAC9B,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;CAC7B,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK;CAChC,SAAS,EAAE,OAAO,KAAK;CACvB,QAAQ,EAAE,OAAO,EAAE,SAAS;CAC5B,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;CACpC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;CACvB,aAAa,EAAE,OAAO,KAAK,EAAE,SAAS;AACxC,CAAC;AAED,MAAM,YAAY,EAAE,OAAO;CACzB,aAAa,EAAE,OAAO,EAAE,SAAS;CACjC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AACxB,CAAC;AAED,MAAM,eAAe,EAAE,OAAO;CAC5B,QAAQ,EAAE,OAAO,EAAE,SAAS;CAC5B,KAAK,EAAE,OAAO,EAAE,SAAS;CACzB,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;CACtB,KAAK,EAAE,IAAI,EAAE,SAAS;AACxB,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/content/index.ts"],"sourcesContent":["import { z } from \"zod\";\n\nconst postSchema = z.object({\n author: z.string().optional(),\n categories: z.array(z.string()).default([]),\n cover: z.string().optional(),\n coverAlt: z.string().optional(),\n description: z.string().min(1),\n draft: z.boolean().default(false),\n heroImageHeight: z.number().int().optional(),\n heroImageUrl: z.url().optional(),\n heroImageWidth: z.number().int().optional(),\n pubDate: z.coerce.date(),\n seo: z\n .object({\n canonical_url: z.string().optional(),\n })\n .optional(),\n series: z.string().optional(),\n slug: z.string().optional(),\n status: z.string().optional(),\n tags: z.array(z.string()).default([]),\n title: z.string().min(1),\n updatedDate: z.coerce.date().optional(),\n});\n\nconst tagSchema = z.object({\n description: z.string().optional(),\n name: z.string().min(1),\n});\n\nconst authorSchema = z.object({\n avatar: z.string().optional(),\n bio: z.string().optional(),\n name: z.string().min(1),\n url: z.url().optional(),\n});\n\ntype Post = z.infer<typeof postSchema>;\ntype Tag = z.infer<typeof tagSchema>;\ntype Author = z.infer<typeof authorSchema>;\n\n// Predicate the 12 blog repos previously inlined ~72 times per repo\n// (`data.status !== \"DRAFT\" && !data.draft`). One source of truth so the\n// loader contract stays in sync with the consumer-side guard.\nconst notDraft = <T extends { data: { draft?: boolean; status?: string } }>(post: T): boolean =>\n post.data.status !== \"DRAFT\" && !post.data.draft;\n\nexport { authorSchema, notDraft, postSchema, tagSchema };\nexport type { Author, Post, Tag };\n"],"mappings":";;;;;;;;;AAEA,MAAM,aAAa,EAAE,OAAO;CAC1B,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS;CAC5B,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;CAC1C,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;CAC3B,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;CAC9B,aAAa,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CAC7B,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,KAAK;CAChC,iBAAiB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS;CAC3C,cAAc,EAAE,IAAI,CAAC,CAAC,SAAS;CAC/B,gBAAgB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS;CAC1C,SAAS,EAAE,OAAO,KAAK;CACvB,KAAK,EACF,OAAO,EACN,eAAe,EAAE,OAAO,CAAC,CAAC,SAAS,EACrC,CAAC,CAAC,CACD,SAAS;CACZ,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS;CAC5B,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;CAC1B,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS;CAC5B,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;CACpC,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACvB,aAAa,EAAE,OAAO,KAAK,CAAC,CAAC,SAAS;AACxC,CAAC;AAED,MAAM,YAAY,EAAE,OAAO;CACzB,aAAa,EAAE,OAAO,CAAC,CAAC,SAAS;CACjC,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;AACxB,CAAC;AAED,MAAM,eAAe,EAAE,OAAO;CAC5B,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS;CAC5B,KAAK,EAAE,OAAO,CAAC,CAAC,SAAS;CACzB,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACtB,KAAK,EAAE,IAAI,CAAC,CAAC,SAAS;AACxB,CAAC;AASD,MAAM,YAAsE,SAC1E,KAAK,KAAK,WAAW,WAAW,CAAC,KAAK,KAAK"}
@@ -0,0 +1,51 @@
1
+ import { ClassValue } from "clsx";
2
+
3
+ //#region src/lib/cn.d.ts
4
+ declare const cn: (...inputs: Array<ClassValue>) => string;
5
+ //#endregion
6
+ //#region src/lib/format-date.d.ts
7
+ declare const formatDate: (date: Date, locale?: string, options?: Intl.DateTimeFormatOptions) => string;
8
+ //#endregion
9
+ //#region src/lib/get-related-posts.d.ts
10
+ type HasTags = {
11
+ data: {
12
+ tags: Array<string>;
13
+ };
14
+ slug: string;
15
+ };
16
+ declare const getRelatedPosts: <T extends HasTags>(current: T, all: Array<T>, n: number) => T[];
17
+ //#endregion
18
+ //#region src/lib/post-url.d.ts
19
+ type PostLike = {
20
+ data: {
21
+ categories?: Array<string>;
22
+ seo?: {
23
+ canonical_url?: string;
24
+ };
25
+ };
26
+ id: string;
27
+ };
28
+ type PostParams = {
29
+ category: string;
30
+ slug: string;
31
+ };
32
+ type PostUrlBuilder = {
33
+ postParams: (post: PostLike) => PostParams;
34
+ postUrl: (post: PostLike) => string;
35
+ };
36
+ declare const createPostUrl: (categorySlugMap: Record<string, string>, defaultCategory: string) => PostUrlBuilder;
37
+ //#endregion
38
+ //#region src/lib/reading-time.d.ts
39
+ declare const readingTime: (input: string) => {
40
+ minutes: number;
41
+ words: number;
42
+ };
43
+ //#endregion
44
+ //#region src/lib/slugify.d.ts
45
+ declare const slugify: (input: string) => string;
46
+ declare namespace index_d_exports {
47
+ export { PostLike, PostParams, PostUrlBuilder, cn, createPostUrl, formatDate, getRelatedPosts, readingTime, slugify };
48
+ }
49
+ //#endregion
50
+ export { PostParams as a, getRelatedPosts as c, PostLike as i, formatDate as l, slugify as n, PostUrlBuilder as o, readingTime as r, createPostUrl as s, index_d_exports as t, cn as u };
51
+ //# sourceMappingURL=index-pemcLff6.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { Author, Post, Tag, t as index_d_exports } from "./content/index.js";
2
- import { t as index_d_exports$1 } from "./index-BAPPsft-.js";
2
+ import { t as index_d_exports$1 } from "./index-pemcLff6.js";
3
3
  export { type Author, type Post, type Tag, index_d_exports as content, index_d_exports$1 as lib };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { t as content_exports } from "./content/index.js";
2
- import { t as lib_exports } from "./lib-BjqsRto0.js";
2
+ import { t as lib_exports } from "./lib-Dn4hRxdC.js";
3
3
  export { content_exports as content, lib_exports as lib };
@@ -1,2 +1,2 @@
1
- import { a as formatDate, i as getRelatedPosts, n as slugify, o as cn, r as readingTime } from "../index-BAPPsft-.js";
2
- export { cn, formatDate, getRelatedPosts, readingTime, slugify };
1
+ import { a as PostParams, c as getRelatedPosts, i as PostLike, l as formatDate, n as slugify, o as PostUrlBuilder, r as readingTime, s as createPostUrl, u as cn } from "../index-pemcLff6.js";
2
+ export { type PostLike, type PostParams, type PostUrlBuilder, cn, createPostUrl, formatDate, getRelatedPosts, readingTime, slugify };
package/dist/lib/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { t as cn } from "../cn-B3UhF_Ec.js";
2
- import { a as formatDate, i as getRelatedPosts, n as slugify, r as readingTime } from "../lib-BjqsRto0.js";
3
- export { cn, formatDate, getRelatedPosts, readingTime, slugify };
1
+ import { t as cn } from "../cn-C_hacsED.js";
2
+ import { a as getRelatedPosts, i as slugify, n as readingTime, o as formatDate, r as createPostUrl } from "../lib-Dn4hRxdC.js";
3
+ export { cn, createPostUrl, formatDate, getRelatedPosts, readingTime, slugify };
@@ -1,5 +1,5 @@
1
- import { t as __exportAll } from "./chunk-pbuEa-1d.js";
2
- import { t as cn } from "./cn-B3UhF_Ec.js";
1
+ import { t as __exportAll } from "./chunk-D7D4PA-g.js";
2
+ import { t as cn } from "./cn-C_hacsED.js";
3
3
  //#region src/lib/format-date.ts
4
4
  const defaultOptions = {
5
5
  day: "numeric",
@@ -38,6 +38,38 @@ const getRelatedPosts = (current, all, n) => {
38
38
  return scored.toSorted((x, y) => y.score - x.score).slice(0, n).map(({ post }) => post);
39
39
  };
40
40
  //#endregion
41
+ //#region src/lib/slugify.ts
42
+ const slugify = (input) => input.normalize("NFD").replaceAll(/* @__PURE__ */ new RegExp("[̀-ͯ]", "gv"), "").toLowerCase().replaceAll(/* @__PURE__ */ new RegExp("[^a-z0-9\\s\\-]", "gv"), "").trim().replaceAll(/* @__PURE__ */ new RegExp("[\\s\\-]+", "gv"), "-").replaceAll(/* @__PURE__ */ new RegExp("^-+|-+$", "gv"), "");
43
+ //#endregion
44
+ //#region src/lib/post-url.ts
45
+ const createPostUrl = (categorySlugMap, defaultCategory) => {
46
+ const postParams = (post) => {
47
+ const canonical = post.data.seo?.canonical_url;
48
+ if (canonical) try {
49
+ const segments = new URL(canonical).pathname.split("/").filter(Boolean);
50
+ const category = segments.at(-2);
51
+ const slug = segments.at(-1);
52
+ if (category && slug) return {
53
+ category,
54
+ slug
55
+ };
56
+ } catch {}
57
+ const first = post.data.categories?.at(0);
58
+ return {
59
+ category: first ? categorySlugMap[first] ?? slugify(first) : defaultCategory,
60
+ slug: post.id
61
+ };
62
+ };
63
+ const postUrl = (post) => {
64
+ const { category, slug } = postParams(post);
65
+ return `/${category}/${slug}/`;
66
+ };
67
+ return {
68
+ postParams,
69
+ postUrl
70
+ };
71
+ };
72
+ //#endregion
41
73
  //#region src/lib/reading-time.ts
42
74
  const WORDS_PER_MINUTE = 200;
43
75
  const readingTime = (input) => {
@@ -49,18 +81,16 @@ const readingTime = (input) => {
49
81
  };
50
82
  };
51
83
  //#endregion
52
- //#region src/lib/slugify.ts
53
- const slugify = (input) => input.normalize("NFD").replaceAll(/* @__PURE__ */ new RegExp("[̀-ͯ]", "gv"), "").toLowerCase().replaceAll(/* @__PURE__ */ new RegExp("[^a-z0-9\\s\\-]", "gv"), "").trim().replaceAll(/* @__PURE__ */ new RegExp("[\\s\\-]+", "gv"), "-").replaceAll(/* @__PURE__ */ new RegExp("^-+|-+$", "gv"), "");
54
- //#endregion
55
84
  //#region src/lib/index.ts
56
85
  var lib_exports = /* @__PURE__ */ __exportAll({
57
86
  cn: () => cn,
87
+ createPostUrl: () => createPostUrl,
58
88
  formatDate: () => formatDate,
59
89
  getRelatedPosts: () => getRelatedPosts,
60
90
  readingTime: () => readingTime,
61
91
  slugify: () => slugify
62
92
  });
63
93
  //#endregion
64
- export { formatDate as a, getRelatedPosts as i, slugify as n, readingTime as r, lib_exports as t };
94
+ export { getRelatedPosts as a, slugify as i, readingTime as n, formatDate as o, createPostUrl as r, lib_exports as t };
65
95
 
66
- //# sourceMappingURL=lib-BjqsRto0.js.map
96
+ //# sourceMappingURL=lib-Dn4hRxdC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib-Dn4hRxdC.js","names":[],"sources":["../src/lib/format-date.ts","../src/lib/get-related-posts.ts","../src/lib/slugify.ts","../src/lib/post-url.ts","../src/lib/reading-time.ts","../src/lib/index.ts"],"sourcesContent":["const defaultOptions: Intl.DateTimeFormatOptions = {\n day: \"numeric\",\n month: \"long\",\n timeZone: \"UTC\",\n year: \"numeric\",\n};\n\nconst formatterCache = new Map<string, Intl.DateTimeFormat>();\n\nconst getFormatter = (locale: string, options: Intl.DateTimeFormatOptions) => {\n const key = `${locale}:${JSON.stringify(options)}`;\n let formatter = formatterCache.get(key);\n if (!formatter) {\n formatter = new Intl.DateTimeFormat(locale, options);\n formatterCache.set(key, formatter);\n }\n return formatter;\n};\n\nconst formatDate = (\n date: Date,\n locale: string = \"en-US\",\n options: Intl.DateTimeFormatOptions = defaultOptions,\n) => getFormatter(locale, { ...defaultOptions, ...options }).format(date);\n\nexport { formatDate };\n","type HasTags = { data: { tags: Array<string> }; slug: string };\n\nconst getRelatedPosts = <T extends HasTags>(current: T, all: Array<T>, n: number) => {\n const currentTagSet = new Set(current.data.tags);\n const scored: Array<{ post: T; score: number }> = [];\n\n for (const post of all) {\n if (post.slug === current.slug) {\n continue;\n }\n let score = 0;\n for (const tag of post.data.tags) {\n if (currentTagSet.has(tag)) {\n score++;\n }\n }\n scored.push({ post, score });\n }\n\n return scored\n .toSorted((x, y) => y.score - x.score)\n .slice(0, n)\n .map(({ post }) => post);\n};\n\nexport { getRelatedPosts };\n","const slugify = (input: string) =>\n input\n .normalize(\"NFD\")\n .replaceAll(/[̀-ͯ]/gv, \"\")\n .toLowerCase()\n .replaceAll(/[^a-z0-9\\s\\-]/gv, \"\")\n .trim()\n .replaceAll(/[\\s\\-]+/gv, \"-\")\n .replaceAll(/^-+|-+$/gv, \"\");\n\nexport { slugify };\n","import { slugify } from \"./slugify\";\n\ntype PostLike = {\n data: {\n categories?: Array<string>;\n seo?: { canonical_url?: string };\n };\n id: string;\n};\n\ntype PostParams = { category: string; slug: string };\n\ntype PostUrlBuilder = {\n postParams: (post: PostLike) => PostParams;\n postUrl: (post: PostLike) => string;\n};\n\n// Per-blog URL builder. Each blog supplies its own pt-BR -> slug map\n// (`category-slugs.json`) and the catch-all category used when the post has\n// no categories. The canonical_url short-circuit lets WordPress migrations\n// keep their original URL pair while everything else falls back to a slugified\n// derived category + Astro entry id.\nconst createPostUrl = (\n categorySlugMap: Record<string, string>,\n defaultCategory: string,\n): PostUrlBuilder => {\n const postParams = (post: PostLike): PostParams => {\n const canonical = post.data.seo?.canonical_url;\n if (canonical) {\n try {\n const segments = new URL(canonical).pathname.split(\"/\").filter(Boolean);\n const category = segments.at(-2);\n const slug = segments.at(-1);\n if (category && slug) {\n return { category, slug };\n }\n } catch {\n // Malformed canonical_url — fall through to category-derived logic.\n }\n }\n const first = post.data.categories?.at(0);\n const category = first ? (categorySlugMap[first] ?? slugify(first)) : defaultCategory;\n return { category, slug: post.id };\n };\n\n const postUrl = (post: PostLike): string => {\n const { category, slug } = postParams(post);\n return `/${category}/${slug}/`;\n };\n\n return { postParams, postUrl };\n};\n\nexport type { PostLike, PostParams, PostUrlBuilder };\nexport { createPostUrl };\n","const WORDS_PER_MINUTE = 200;\n\nconst readingTime = (input: string) => {\n const stripped = input\n .replaceAll(/<[^>]*>/gv, \"\")\n .replaceAll(/[#*_`>\\-]/gv, \" \")\n .replaceAll(/\\s+/gv, \" \")\n .trim();\n const words = stripped.length === 0 ? 0 : stripped.split(\" \").length;\n const minutes = Math.max(1, Math.ceil(words / WORDS_PER_MINUTE));\n return { minutes, words };\n};\n\nexport { readingTime };\n","export { cn } from \"./cn\";\nexport { formatDate } from \"./format-date\";\nexport { getRelatedPosts } from \"./get-related-posts\";\nexport type { PostLike, PostParams, PostUrlBuilder } from \"./post-url\";\nexport { createPostUrl } from \"./post-url\";\nexport { readingTime } from \"./reading-time\";\nexport { slugify } from \"./slugify\";\n"],"mappings":";;;AAAA,MAAM,iBAA6C;CACjD,KAAK;CACL,OAAO;CACP,UAAU;CACV,MAAM;AACR;AAEA,MAAM,iCAAiB,IAAI,IAAiC;AAE5D,MAAM,gBAAgB,QAAgB,YAAwC;CAC5E,MAAM,MAAM,GAAG,OAAO,GAAG,KAAK,UAAU,OAAO;CAC/C,IAAI,YAAY,eAAe,IAAI,GAAG;CACtC,IAAI,CAAC,WAAW;EACd,YAAY,IAAI,KAAK,eAAe,QAAQ,OAAO;EACnD,eAAe,IAAI,KAAK,SAAS;CACnC;CACA,OAAO;AACT;AAEA,MAAM,cACJ,MACA,SAAiB,SACjB,UAAsC,mBACnC,aAAa,QAAQ;CAAE,GAAG;CAAgB,GAAG;AAAQ,CAAC,CAAC,CAAC,OAAO,IAAI;;;ACrBxE,MAAM,mBAAsC,SAAY,KAAe,MAAc;CACnF,MAAM,gBAAgB,IAAI,IAAI,QAAQ,KAAK,IAAI;CAC/C,MAAM,SAA4C,CAAC;CAEnD,KAAK,MAAM,QAAQ,KAAK;EACtB,IAAI,KAAK,SAAS,QAAQ,MACxB;EAEF,IAAI,QAAQ;EACZ,KAAK,MAAM,OAAO,KAAK,KAAK,MAC1B,IAAI,cAAc,IAAI,GAAG,GACvB;EAGJ,OAAO,KAAK;GAAE;GAAM;EAAM,CAAC;CAC7B;CAEA,OAAO,OACJ,UAAU,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CACrC,MAAM,GAAG,CAAC,CAAC,CACX,KAAK,EAAE,WAAW,IAAI;AAC3B;;;ACvBA,MAAM,WAAW,UACf,MACG,UAAU,KAAK,CAAC,CAChB,2BAAW,IAAA,OAAA,SAAA,IAAQ,GAAG,EAAE,CAAC,CACzB,YAAY,CAAC,CACb,2BAAW,IAAA,OAAA,mBAAA,IAAgB,GAAG,EAAE,CAAC,CACjC,KAAK,CAAC,CACN,2BAAW,IAAA,OAAA,aAAA,IAAU,GAAG,GAAG,CAAC,CAC5B,2BAAW,IAAA,OAAA,WAAA,IAAU,GAAG,EAAE;;;ACc/B,MAAM,iBACJ,iBACA,oBACmB;CACnB,MAAM,cAAc,SAA+B;EACjD,MAAM,YAAY,KAAK,KAAK,KAAK;EACjC,IAAI,WACF,IAAI;GACF,MAAM,WAAW,IAAI,IAAI,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO;GACtE,MAAM,WAAW,SAAS,GAAG,EAAE;GAC/B,MAAM,OAAO,SAAS,GAAG,EAAE;GAC3B,IAAI,YAAY,MACd,OAAO;IAAE;IAAU;GAAK;EAE5B,QAAQ,CAER;EAEF,MAAM,QAAQ,KAAK,KAAK,YAAY,GAAG,CAAC;EAExC,OAAO;GAAE,UADQ,QAAS,gBAAgB,UAAU,QAAQ,KAAK,IAAK;GACnD,MAAM,KAAK;EAAG;CACnC;CAEA,MAAM,WAAW,SAA2B;EAC1C,MAAM,EAAE,UAAU,SAAS,WAAW,IAAI;EAC1C,OAAO,IAAI,SAAS,GAAG,KAAK;CAC9B;CAEA,OAAO;EAAE;EAAY;CAAQ;AAC/B;;;ACnDA,MAAM,mBAAmB;AAEzB,MAAM,eAAe,UAAkB;CACrC,MAAM,WAAW,MACd,2BAAW,IAAA,OAAA,WAAA,IAAU,GAAG,EAAE,CAAC,CAC3B,2BAAW,IAAA,OAAA,cAAA,IAAY,GAAG,GAAG,CAAC,CAC9B,2BAAW,IAAA,OAAA,QAAA,IAAM,GAAG,GAAG,CAAC,CACxB,KAAK;CACR,MAAM,QAAQ,SAAS,WAAW,IAAI,IAAI,SAAS,MAAM,GAAG,CAAC,CAAC;CAE9D,OAAO;EAAE,SADO,KAAK,IAAI,GAAG,KAAK,KAAK,QAAQ,gBAAgB,CAC/C;EAAG;CAAM;AAC1B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-awesomeness",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Astro 6 blog theme with shadcn primitives and a Tailwind v4 preset.",
5
5
  "keywords": [
6
6
  "astro",
@@ -10,11 +10,19 @@
10
10
  "tailwindcss"
11
11
  ],
12
12
  "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/pedroapfilho/astro-theme-awesomeness.git",
16
+ "directory": "packages/astro-awesomeness"
17
+ },
13
18
  "files": [
14
19
  "dist",
15
20
  "src"
16
21
  ],
17
22
  "type": "module",
23
+ "sideEffects": [
24
+ "*.css"
25
+ ],
18
26
  "exports": {
19
27
  ".": {
20
28
  "types": "./dist/index.d.ts",
@@ -40,26 +48,29 @@
40
48
  },
41
49
  "./styles.css": "./dist/styles/globals.css"
42
50
  },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
43
54
  "dependencies": {
55
+ "@base-ui/react": "^1.5.0",
44
56
  "class-variance-authority": "^0.7.1",
45
57
  "clsx": "^2.1.1",
46
58
  "cmdk": "^1.1.1",
47
- "lucide-react": "^1.16.0",
48
- "radix-ui": "^1.4.3",
59
+ "lucide-react": "^1.17.0",
49
60
  "tailwind-merge": "^3.6.0",
50
61
  "tw-animate-css": "^1.4.0",
51
62
  "zod": "^4.4.3"
52
63
  },
53
64
  "devDependencies": {
54
65
  "@tailwindcss/postcss": "^4.3.0",
55
- "@types/node": "^24.0.0",
56
- "@types/react": "^19.2.15",
66
+ "@types/node": "^24.13.1",
67
+ "@types/react": "^19.2.17",
57
68
  "@types/react-dom": "^19.2.3",
58
- "@vitest/coverage-v8": "^4.1.7",
59
- "happy-dom": "^20.9.0",
69
+ "@vitest/coverage-v8": "^4.1.8",
60
70
  "tailwindcss": "^4.3.0",
61
71
  "typescript": "^6.0.3",
62
- "vitest": "^4.1.7",
72
+ "vitest": "^4.1.8",
73
+ "@repo/config-vitest": "0.0.0",
63
74
  "@repo/typescript-config": "0.0.0"
64
75
  },
65
76
  "peerDependencies": {
@@ -28,10 +28,7 @@ const href = `/blog/${post.id}`;
28
28
  </article>
29
29
 
30
30
  <style>
31
- /* Skip layout/paint for off-screen cards on long post lists. The first
32
- three remain eagerly rendered so above-the-fold paint isn't blocked by
33
- visibility containment evaluation. `contain-intrinsic-size: auto …`
34
- lets the browser cache real measurements after first render. */
31
+ /* Keep first 3 eager so above-the-fold paint isn't gated on containment checks. */
35
32
  .post-card:nth-child(n + 4) {
36
33
  content-visibility: auto;
37
34
  contain-intrinsic-size: auto 200px;
@@ -44,10 +44,8 @@ const CommandMenu = ({
44
44
 
45
45
  return (
46
46
  <Dialog onOpenChange={setOpen} open={open}>
47
- <DialogTrigger asChild>
48
- <Button aria-label="Open search" size="icon" variant="ghost">
49
- <Search />
50
- </Button>
47
+ <DialogTrigger render={<Button aria-label="Open search" size="icon" variant="ghost" />}>
48
+ <Search />
51
49
  </DialogTrigger>
52
50
  <DialogContent className="p-0">
53
51
  <Command>
@@ -39,8 +39,7 @@ type Props = {
39
39
  };
40
40
 
41
41
  const ThemeToggle = ({ ariaLabel = "Toggle theme" }: Props) => {
42
- // useSyncExternalStore avoids hydration mismatch warnings while still letting
43
- // us read the actual system preference on the client.
42
+ // useSyncExternalStore avoids hydration mismatch on system preference reads.
44
43
  const prefersDark = useSyncExternalStore(subscribePrefersDark, getPrefersDark, () => false);
45
44
  const [override, setOverride] = useState<Theme | null>(() => getStoredTheme());
46
45
  const theme: Theme = override ?? (prefersDark ? "dark" : "light");
@@ -1,6 +1,6 @@
1
+ import { Button as ButtonPrimitive } from "@base-ui/react/button";
1
2
  import { cva, type VariantProps } from "class-variance-authority";
2
3
  import * as React from "react";
3
- import { Slot } from "radix-ui";
4
4
 
5
5
  import { cn } from "../../lib/cn";
6
6
 
@@ -36,27 +36,25 @@ const buttonVariants = cva(
36
36
  },
37
37
  );
38
38
 
39
- const Button = ({
40
- asChild = false,
41
- className,
42
- size = "default",
43
- variant = "default",
44
- ...props
45
- }: React.ComponentProps<"button"> &
39
+ type ButtonProps = Omit<React.ComponentPropsWithoutRef<typeof ButtonPrimitive>, "className"> &
46
40
  VariantProps<typeof buttonVariants> & {
47
- asChild?: boolean;
48
- }) => {
49
- const Comp = asChild ? Slot.Root : "button";
41
+ className?: string;
42
+ };
50
43
 
51
- return (
52
- <Comp
53
- className={cn(buttonVariants({ className, size, variant }))}
54
- data-size={size}
55
- data-slot="button"
56
- data-variant={variant}
57
- {...props}
58
- />
59
- );
60
- };
44
+ const Button = React.forwardRef<React.ComponentRef<typeof ButtonPrimitive>, ButtonProps>(
45
+ ({ className, size = "default", variant = "default", ...props }, ref) => {
46
+ return (
47
+ <ButtonPrimitive
48
+ className={cn(buttonVariants({ className, size, variant }))}
49
+ data-size={size}
50
+ data-slot="button"
51
+ data-variant={variant}
52
+ ref={ref}
53
+ {...props}
54
+ />
55
+ );
56
+ },
57
+ );
58
+ Button.displayName = "Button";
61
59
 
62
60
  export { Button, buttonVariants };
@@ -26,6 +26,7 @@ function CommandDialog({
26
26
  title = "Command Palette",
27
27
  ...props
28
28
  }: React.ComponentProps<typeof Dialog> & {
29
+ children?: React.ReactNode;
29
30
  className?: string;
30
31
  description?: string;
31
32
  showCloseButton?: boolean;
@@ -1,6 +1,6 @@
1
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
1
2
  import * as React from "react";
2
3
  import { XIcon } from "lucide-react";
3
- import { Dialog as DialogPrimitive } from "radix-ui";
4
4
 
5
5
  import { cn } from "../../lib/cn";
6
6
  import { Button } from "./button";
@@ -24,11 +24,11 @@ function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.C
24
24
  function DialogOverlay({
25
25
  className,
26
26
  ...props
27
- }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
27
+ }: React.ComponentProps<typeof DialogPrimitive.Backdrop>) {
28
28
  return (
29
- <DialogPrimitive.Overlay
29
+ <DialogPrimitive.Backdrop
30
30
  className={cn(
31
- "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
31
+ "fixed inset-0 z-50 bg-black/50 data-[closed]:animate-out data-[closed]:fade-out-0 data-[open]:animate-in data-[open]:fade-in-0",
32
32
  className,
33
33
  )}
34
34
  data-slot="dialog-overlay"
@@ -42,15 +42,15 @@ function DialogContent({
42
42
  className,
43
43
  showCloseButton = true,
44
44
  ...props
45
- }: React.ComponentProps<typeof DialogPrimitive.Content> & {
45
+ }: React.ComponentProps<typeof DialogPrimitive.Popup> & {
46
46
  showCloseButton?: boolean;
47
47
  }) {
48
48
  return (
49
49
  <DialogPortal data-slot="dialog-portal">
50
50
  <DialogOverlay />
51
- <DialogPrimitive.Content
51
+ <DialogPrimitive.Popup
52
52
  className={cn(
53
- "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg",
53
+ "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:zoom-out-95 data-[open]:animate-in data-[open]:fade-in-0 data-[open]:zoom-in-95 sm:max-w-lg",
54
54
  className,
55
55
  )}
56
56
  data-slot="dialog-content"
@@ -61,12 +61,13 @@ function DialogContent({
61
61
  <DialogPrimitive.Close
62
62
  className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
63
63
  data-slot="dialog-close"
64
+ type="button"
64
65
  >
65
66
  <XIcon />
66
67
  <span className="sr-only">Close</span>
67
68
  </DialogPrimitive.Close>
68
69
  )}
69
- </DialogPrimitive.Content>
70
+ </DialogPrimitive.Popup>
70
71
  </DialogPortal>
71
72
  );
72
73
  }
@@ -97,9 +98,7 @@ function DialogFooter({
97
98
  >
98
99
  {children}
99
100
  {showCloseButton && (
100
- <DialogPrimitive.Close asChild>
101
- <Button variant="outline">Close</Button>
102
- </DialogPrimitive.Close>
101
+ <DialogPrimitive.Close render={<Button variant="outline" />}>Close</DialogPrimitive.Close>
103
102
  )}
104
103
  </div>
105
104
  );
@@ -2,12 +2,23 @@ import { z } from "zod";
2
2
 
3
3
  const postSchema = z.object({
4
4
  author: z.string().optional(),
5
+ categories: z.array(z.string()).default([]),
5
6
  cover: z.string().optional(),
6
7
  coverAlt: z.string().optional(),
7
8
  description: z.string().min(1),
8
9
  draft: z.boolean().default(false),
10
+ heroImageHeight: z.number().int().optional(),
11
+ heroImageUrl: z.url().optional(),
12
+ heroImageWidth: z.number().int().optional(),
9
13
  pubDate: z.coerce.date(),
14
+ seo: z
15
+ .object({
16
+ canonical_url: z.string().optional(),
17
+ })
18
+ .optional(),
10
19
  series: z.string().optional(),
20
+ slug: z.string().optional(),
21
+ status: z.string().optional(),
11
22
  tags: z.array(z.string()).default([]),
12
23
  title: z.string().min(1),
13
24
  updatedDate: z.coerce.date().optional(),
@@ -29,5 +40,11 @@ type Post = z.infer<typeof postSchema>;
29
40
  type Tag = z.infer<typeof tagSchema>;
30
41
  type Author = z.infer<typeof authorSchema>;
31
42
 
32
- export { authorSchema, postSchema, tagSchema };
43
+ // Predicate the 12 blog repos previously inlined ~72 times per repo
44
+ // (`data.status !== "DRAFT" && !data.draft`). One source of truth so the
45
+ // loader contract stays in sync with the consumer-side guard.
46
+ const notDraft = <T extends { data: { draft?: boolean; status?: string } }>(post: T): boolean =>
47
+ post.data.status !== "DRAFT" && !post.data.draft;
48
+
49
+ export { authorSchema, notDraft, postSchema, tagSchema };
33
50
  export type { Author, Post, Tag };
package/src/lib/index.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { cn } from "./cn";
2
2
  export { formatDate } from "./format-date";
3
3
  export { getRelatedPosts } from "./get-related-posts";
4
+ export type { PostLike, PostParams, PostUrlBuilder } from "./post-url";
5
+ export { createPostUrl } from "./post-url";
4
6
  export { readingTime } from "./reading-time";
5
7
  export { slugify } from "./slugify";
@@ -0,0 +1,50 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { createPostUrl } from "./post-url";
4
+
5
+ const map = { "Aluguel de Espaços": "aluguel-de-espacos", Arquitetura: "arquitetura" };
6
+ const DEFAULT = "sem-categoria";
7
+
8
+ describe("createPostUrl", () => {
9
+ const { postParams, postUrl } = createPostUrl(map, DEFAULT);
10
+
11
+ it("returns mapped category + post id when categories has a known entry", () => {
12
+ const post = { data: { categories: ["Arquitetura"] }, id: "casa-minimal" };
13
+ expect(postParams(post)).toEqual({ category: "arquitetura", slug: "casa-minimal" });
14
+ expect(postUrl(post)).toBe("/arquitetura/casa-minimal/");
15
+ });
16
+
17
+ it("slugifies an unmapped first category", () => {
18
+ const post = { data: { categories: ["Arquitetura Moderna"] }, id: "x" };
19
+ expect(postParams(post)).toEqual({ category: "arquitetura-moderna", slug: "x" });
20
+ });
21
+
22
+ it("uses the default category when there are no categories", () => {
23
+ const post = { data: { categories: [] }, id: "orphan" };
24
+ expect(postUrl(post)).toBe("/sem-categoria/orphan/");
25
+ });
26
+
27
+ it("honors seo.canonical_url over derived category when it has >=2 segments", () => {
28
+ const post = {
29
+ data: { categories: ["Arquitetura"], seo: { canonical_url: "https://x.com/foo/bar/" } },
30
+ id: "ignored",
31
+ };
32
+ expect(postParams(post)).toEqual({ category: "foo", slug: "bar" });
33
+ });
34
+
35
+ it("falls back to derived category when seo.canonical_url is malformed", () => {
36
+ const post = {
37
+ data: { categories: ["Arquitetura"], seo: { canonical_url: "not-a-url" } },
38
+ id: "x",
39
+ };
40
+ expect(postParams(post)).toEqual({ category: "arquitetura", slug: "x" });
41
+ });
42
+
43
+ it("falls back when canonical_url has fewer than 2 segments", () => {
44
+ const post = {
45
+ data: { categories: ["Arquitetura"], seo: { canonical_url: "https://x.com/foo" } },
46
+ id: "x",
47
+ };
48
+ expect(postParams(post)).toEqual({ category: "arquitetura", slug: "x" });
49
+ });
50
+ });
@@ -0,0 +1,55 @@
1
+ import { slugify } from "./slugify";
2
+
3
+ type PostLike = {
4
+ data: {
5
+ categories?: Array<string>;
6
+ seo?: { canonical_url?: string };
7
+ };
8
+ id: string;
9
+ };
10
+
11
+ type PostParams = { category: string; slug: string };
12
+
13
+ type PostUrlBuilder = {
14
+ postParams: (post: PostLike) => PostParams;
15
+ postUrl: (post: PostLike) => string;
16
+ };
17
+
18
+ // Per-blog URL builder. Each blog supplies its own pt-BR -> slug map
19
+ // (`category-slugs.json`) and the catch-all category used when the post has
20
+ // no categories. The canonical_url short-circuit lets WordPress migrations
21
+ // keep their original URL pair while everything else falls back to a slugified
22
+ // derived category + Astro entry id.
23
+ const createPostUrl = (
24
+ categorySlugMap: Record<string, string>,
25
+ defaultCategory: string,
26
+ ): PostUrlBuilder => {
27
+ const postParams = (post: PostLike): PostParams => {
28
+ const canonical = post.data.seo?.canonical_url;
29
+ if (canonical) {
30
+ try {
31
+ const segments = new URL(canonical).pathname.split("/").filter(Boolean);
32
+ const category = segments.at(-2);
33
+ const slug = segments.at(-1);
34
+ if (category && slug) {
35
+ return { category, slug };
36
+ }
37
+ } catch {
38
+ // Malformed canonical_url — fall through to category-derived logic.
39
+ }
40
+ }
41
+ const first = post.data.categories?.at(0);
42
+ const category = first ? (categorySlugMap[first] ?? slugify(first)) : defaultCategory;
43
+ return { category, slug: post.id };
44
+ };
45
+
46
+ const postUrl = (post: PostLike): string => {
47
+ const { category, slug } = postParams(post);
48
+ return `/${category}/${slug}/`;
49
+ };
50
+
51
+ return { postParams, postUrl };
52
+ };
53
+
54
+ export type { PostLike, PostParams, PostUrlBuilder };
55
+ export { createPostUrl };
@@ -1,31 +0,0 @@
1
- import { ClassValue } from "clsx";
2
-
3
- //#region src/lib/cn.d.ts
4
- declare const cn: (...inputs: Array<ClassValue>) => string;
5
- //#endregion
6
- //#region src/lib/format-date.d.ts
7
- declare const formatDate: (date: Date, locale?: string, options?: Intl.DateTimeFormatOptions) => string;
8
- //#endregion
9
- //#region src/lib/get-related-posts.d.ts
10
- type HasTags = {
11
- data: {
12
- tags: Array<string>;
13
- };
14
- slug: string;
15
- };
16
- declare const getRelatedPosts: <T extends HasTags>(current: T, all: Array<T>, n: number) => T[];
17
- //#endregion
18
- //#region src/lib/reading-time.d.ts
19
- declare const readingTime: (input: string) => {
20
- minutes: number;
21
- words: number;
22
- };
23
- //#endregion
24
- //#region src/lib/slugify.d.ts
25
- declare const slugify: (input: string) => string;
26
- declare namespace index_d_exports {
27
- export { cn, formatDate, getRelatedPosts, readingTime, slugify };
28
- }
29
- //#endregion
30
- export { formatDate as a, getRelatedPosts as i, slugify as n, cn as o, readingTime as r, index_d_exports as t };
31
- //# sourceMappingURL=index-BAPPsft-.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lib-BjqsRto0.js","names":[],"sources":["../src/lib/format-date.ts","../src/lib/get-related-posts.ts","../src/lib/reading-time.ts","../src/lib/slugify.ts","../src/lib/index.ts"],"sourcesContent":["const defaultOptions: Intl.DateTimeFormatOptions = {\n day: \"numeric\",\n month: \"long\",\n timeZone: \"UTC\",\n year: \"numeric\",\n};\n\nconst formatterCache = new Map<string, Intl.DateTimeFormat>();\n\nconst getFormatter = (locale: string, options: Intl.DateTimeFormatOptions) => {\n const key = `${locale}:${JSON.stringify(options)}`;\n let formatter = formatterCache.get(key);\n if (!formatter) {\n formatter = new Intl.DateTimeFormat(locale, options);\n formatterCache.set(key, formatter);\n }\n return formatter;\n};\n\nconst formatDate = (\n date: Date,\n locale: string = \"en-US\",\n options: Intl.DateTimeFormatOptions = defaultOptions,\n) => getFormatter(locale, { ...defaultOptions, ...options }).format(date);\n\nexport { formatDate };\n","type HasTags = { data: { tags: Array<string> }; slug: string };\n\nconst getRelatedPosts = <T extends HasTags>(current: T, all: Array<T>, n: number) => {\n const currentTagSet = new Set(current.data.tags);\n const scored: Array<{ post: T; score: number }> = [];\n\n for (const post of all) {\n if (post.slug === current.slug) {\n continue;\n }\n let score = 0;\n for (const tag of post.data.tags) {\n if (currentTagSet.has(tag)) {\n score++;\n }\n }\n scored.push({ post, score });\n }\n\n return scored\n .toSorted((x, y) => y.score - x.score)\n .slice(0, n)\n .map(({ post }) => post);\n};\n\nexport { getRelatedPosts };\n","const WORDS_PER_MINUTE = 200;\n\nconst readingTime = (input: string) => {\n const stripped = input\n .replaceAll(/<[^>]*>/gv, \"\")\n .replaceAll(/[#*_`>\\-]/gv, \" \")\n .replaceAll(/\\s+/gv, \" \")\n .trim();\n const words = stripped.length === 0 ? 0 : stripped.split(\" \").length;\n const minutes = Math.max(1, Math.ceil(words / WORDS_PER_MINUTE));\n return { minutes, words };\n};\n\nexport { readingTime };\n","const slugify = (input: string) =>\n input\n .normalize(\"NFD\")\n .replaceAll(/[̀-ͯ]/gv, \"\")\n .toLowerCase()\n .replaceAll(/[^a-z0-9\\s\\-]/gv, \"\")\n .trim()\n .replaceAll(/[\\s\\-]+/gv, \"-\")\n .replaceAll(/^-+|-+$/gv, \"\");\n\nexport { slugify };\n","export { cn } from \"./cn\";\nexport { formatDate } from \"./format-date\";\nexport { getRelatedPosts } from \"./get-related-posts\";\nexport { readingTime } from \"./reading-time\";\nexport { slugify } from \"./slugify\";\n"],"mappings":";;;AAAA,MAAM,iBAA6C;CACjD,KAAK;CACL,OAAO;CACP,UAAU;CACV,MAAM;AACR;AAEA,MAAM,iCAAiB,IAAI,IAAiC;AAE5D,MAAM,gBAAgB,QAAgB,YAAwC;CAC5E,MAAM,MAAM,GAAG,OAAO,GAAG,KAAK,UAAU,OAAO;CAC/C,IAAI,YAAY,eAAe,IAAI,GAAG;CACtC,IAAI,CAAC,WAAW;EACd,YAAY,IAAI,KAAK,eAAe,QAAQ,OAAO;EACnD,eAAe,IAAI,KAAK,SAAS;CACnC;CACA,OAAO;AACT;AAEA,MAAM,cACJ,MACA,SAAiB,SACjB,UAAsC,mBACnC,aAAa,QAAQ;CAAE,GAAG;CAAgB,GAAG;AAAQ,CAAC,EAAE,OAAO,IAAI;;;ACrBxE,MAAM,mBAAsC,SAAY,KAAe,MAAc;CACnF,MAAM,gBAAgB,IAAI,IAAI,QAAQ,KAAK,IAAI;CAC/C,MAAM,SAA4C,CAAC;CAEnD,KAAK,MAAM,QAAQ,KAAK;EACtB,IAAI,KAAK,SAAS,QAAQ,MACxB;EAEF,IAAI,QAAQ;EACZ,KAAK,MAAM,OAAO,KAAK,KAAK,MAC1B,IAAI,cAAc,IAAI,GAAG,GACvB;EAGJ,OAAO,KAAK;GAAE;GAAM;EAAM,CAAC;CAC7B;CAEA,OAAO,OACJ,UAAU,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EACpC,MAAM,GAAG,CAAC,EACV,KAAK,EAAE,WAAW,IAAI;AAC3B;;;ACvBA,MAAM,mBAAmB;AAEzB,MAAM,eAAe,UAAkB;CACrC,MAAM,WAAW,MACd,2BAAW,IAAA,OAAA,WAAA,IAAU,GAAG,EAAE,EAC1B,2BAAW,IAAA,OAAA,cAAA,IAAY,GAAG,GAAG,EAC7B,2BAAW,IAAA,OAAA,QAAA,IAAM,GAAG,GAAG,EACvB,KAAK;CACR,MAAM,QAAQ,SAAS,WAAW,IAAI,IAAI,SAAS,MAAM,GAAG,EAAE;CAE9D,OAAO;EAAE,SADO,KAAK,IAAI,GAAG,KAAK,KAAK,QAAQ,gBAAgB,CAC/C;EAAG;CAAM;AAC1B;;;ACXA,MAAM,WAAW,UACf,MACG,UAAU,KAAK,EACf,2BAAW,IAAA,OAAA,SAAA,IAAQ,GAAG,EAAE,EACxB,YAAY,EACZ,2BAAW,IAAA,OAAA,mBAAA,IAAgB,GAAG,EAAE,EAChC,KAAK,EACL,2BAAW,IAAA,OAAA,aAAA,IAAU,GAAG,GAAG,EAC3B,2BAAW,IAAA,OAAA,WAAA,IAAU,GAAG,EAAE"}
File without changes