astro-awesomeness 0.7.2 → 0.7.3

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.
@@ -16,27 +16,27 @@ type ButtonProps = Omit<React.ComponentProps<typeof Button$1>, "className"> & Va
16
16
  declare const Button: ({ className, size, variant, ...props }: ButtonProps) => React.JSX.Element;
17
17
  //#endregion
18
18
  //#region src/components/ui/dialog.d.ts
19
- declare function Dialog({ ...props }: React.ComponentProps<typeof Dialog$1.Root>): React.JSX.Element;
20
- declare function DialogTrigger({ ...props }: React.ComponentProps<typeof Dialog$1.Trigger>): React.JSX.Element;
21
- declare function DialogContent({ children, className, showCloseButton, ...props }: React.ComponentProps<typeof Dialog$1.Popup> & {
19
+ declare const Dialog: ({ ...props }: React.ComponentProps<typeof Dialog$1.Root>) => React.JSX.Element;
20
+ declare const DialogTrigger: ({ ...props }: React.ComponentProps<typeof Dialog$1.Trigger>) => React.JSX.Element;
21
+ declare const DialogContent: ({ children, className, showCloseButton, ...props }: React.ComponentProps<typeof Dialog$1.Popup> & {
22
22
  showCloseButton?: boolean;
23
- }): React.JSX.Element;
24
- declare function DialogHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
25
- declare function DialogFooter({ children, className, showCloseButton, ...props }: React.ComponentProps<"div"> & {
23
+ }) => React.JSX.Element;
24
+ declare const DialogHeader: ({ className, ...props }: React.ComponentProps<"div">) => React.JSX.Element;
25
+ declare const DialogFooter: ({ children, className, showCloseButton, ...props }: React.ComponentProps<"div"> & {
26
26
  showCloseButton?: boolean;
27
- }): React.JSX.Element;
28
- declare function DialogTitle({ className, ...props }: React.ComponentProps<typeof Dialog$1.Title>): React.JSX.Element;
29
- declare function DialogDescription({ className, ...props }: React.ComponentProps<typeof Dialog$1.Description>): React.JSX.Element;
27
+ }) => React.JSX.Element;
28
+ declare const DialogTitle: ({ className, ...props }: React.ComponentProps<typeof Dialog$1.Title>) => React.JSX.Element;
29
+ declare const DialogDescription: ({ className, ...props }: React.ComponentProps<typeof Dialog$1.Description>) => React.JSX.Element;
30
30
  //#endregion
31
31
  //#region src/components/ui/command.d.ts
32
- declare function Command({ className, ...props }: React.ComponentProps<typeof Command$1>): React.JSX.Element;
33
- declare function CommandInput({ className, ...props }: React.ComponentProps<typeof Command$1.Input>): React.JSX.Element;
34
- declare function CommandList({ className, ...props }: React.ComponentProps<typeof Command$1.List>): React.JSX.Element;
35
- declare function CommandEmpty({ ...props }: React.ComponentProps<typeof Command$1.Empty>): React.JSX.Element;
36
- declare function CommandGroup({ className, ...props }: React.ComponentProps<typeof Command$1.Group>): React.JSX.Element;
37
- declare function CommandSeparator({ className, ...props }: React.ComponentProps<typeof Command$1.Separator>): React.JSX.Element;
38
- declare function CommandItem({ className, ...props }: React.ComponentProps<typeof Command$1.Item>): React.JSX.Element;
39
- declare function CommandShortcut({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
32
+ declare const Command: ({ className, ...props }: React.ComponentProps<typeof Command$1>) => React.JSX.Element;
33
+ declare const CommandInput: ({ className, ...props }: React.ComponentProps<typeof Command$1.Input>) => React.JSX.Element;
34
+ declare const CommandList: ({ className, ...props }: React.ComponentProps<typeof Command$1.List>) => React.JSX.Element;
35
+ declare const CommandEmpty: ({ ...props }: React.ComponentProps<typeof Command$1.Empty>) => React.JSX.Element;
36
+ declare const CommandGroup: ({ className, ...props }: React.ComponentProps<typeof Command$1.Group>) => React.JSX.Element;
37
+ declare const CommandSeparator: ({ className, ...props }: React.ComponentProps<typeof Command$1.Separator>) => React.JSX.Element;
38
+ declare const CommandItem: ({ className, ...props }: React.ComponentProps<typeof Command$1.Item>) => React.JSX.Element;
39
+ declare const CommandShortcut: ({ className, ...props }: React.ComponentProps<"span">) => React.JSX.Element;
40
40
  //#endregion
41
41
  //#region src/components/command-menu.d.ts
42
42
  type CommandMenuItem = {
@@ -53,32 +53,32 @@ const Button = ({ className, size = "default", variant = "default", ...props })
53
53
 
54
54
  //#endregion
55
55
  //#region src/components/ui/dialog.tsx
56
- function Dialog({ ...props }) {
56
+ const Dialog = ({ ...props }) => {
57
57
  return /* @__PURE__ */ jsx(Dialog$1.Root, {
58
58
  "data-slot": "dialog",
59
59
  ...props
60
60
  });
61
- }
62
- function DialogTrigger({ ...props }) {
61
+ };
62
+ const DialogTrigger = ({ ...props }) => {
63
63
  return /* @__PURE__ */ jsx(Dialog$1.Trigger, {
64
64
  "data-slot": "dialog-trigger",
65
65
  ...props
66
66
  });
67
- }
68
- function DialogPortal({ ...props }) {
67
+ };
68
+ const DialogPortal = ({ ...props }) => {
69
69
  return /* @__PURE__ */ jsx(Dialog$1.Portal, {
70
70
  "data-slot": "dialog-portal",
71
71
  ...props
72
72
  });
73
- }
74
- function DialogOverlay({ className, ...props }) {
73
+ };
74
+ const DialogOverlay = ({ className, ...props }) => {
75
75
  return /* @__PURE__ */ jsx(Dialog$1.Backdrop, {
76
76
  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),
77
77
  "data-slot": "dialog-overlay",
78
78
  ...props
79
79
  });
80
- }
81
- function DialogContent({ children, className, showCloseButton = true, ...props }) {
80
+ };
81
+ const DialogContent = ({ children, className, showCloseButton = true, ...props }) => {
82
82
  return /* @__PURE__ */ jsxs(DialogPortal, {
83
83
  "data-slot": "dialog-portal",
84
84
  children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(Dialog$1.Popup, {
@@ -96,15 +96,15 @@ function DialogContent({ children, className, showCloseButton = true, ...props }
96
96
  })]
97
97
  })]
98
98
  });
99
- }
100
- function DialogHeader({ className, ...props }) {
99
+ };
100
+ const DialogHeader = ({ className, ...props }) => {
101
101
  return /* @__PURE__ */ jsx("div", {
102
102
  className: cn("flex flex-col gap-2 text-center sm:text-left", className),
103
103
  "data-slot": "dialog-header",
104
104
  ...props
105
105
  });
106
- }
107
- function DialogFooter({ children, className, showCloseButton = false, ...props }) {
106
+ };
107
+ const DialogFooter = ({ children, className, showCloseButton = false, ...props }) => {
108
108
  return /* @__PURE__ */ jsxs("div", {
109
109
  className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className),
110
110
  "data-slot": "dialog-footer",
@@ -114,32 +114,32 @@ function DialogFooter({ children, className, showCloseButton = false, ...props }
114
114
  children: "Close"
115
115
  })]
116
116
  });
117
- }
118
- function DialogTitle({ className, ...props }) {
117
+ };
118
+ const DialogTitle = ({ className, ...props }) => {
119
119
  return /* @__PURE__ */ jsx(Dialog$1.Title, {
120
120
  className: cn("text-lg leading-none font-semibold", className),
121
121
  "data-slot": "dialog-title",
122
122
  ...props
123
123
  });
124
- }
125
- function DialogDescription({ className, ...props }) {
124
+ };
125
+ const DialogDescription = ({ className, ...props }) => {
126
126
  return /* @__PURE__ */ jsx(Dialog$1.Description, {
127
127
  className: cn("text-sm text-muted-foreground", className),
128
128
  "data-slot": "dialog-description",
129
129
  ...props
130
130
  });
131
- }
131
+ };
132
132
 
133
133
  //#endregion
134
134
  //#region src/components/ui/command.tsx
135
- function Command({ className, ...props }) {
135
+ const Command = ({ className, ...props }) => {
136
136
  return /* @__PURE__ */ jsx(Command$1, {
137
137
  className: cn("flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", className),
138
138
  "data-slot": "command",
139
139
  ...props
140
140
  });
141
- }
142
- function CommandInput({ className, ...props }) {
141
+ };
142
+ const CommandInput = ({ className, ...props }) => {
143
143
  return /* @__PURE__ */ jsxs("div", {
144
144
  className: "flex h-9 items-center gap-2 border-b px-3",
145
145
  "data-slot": "command-input-wrapper",
@@ -149,49 +149,49 @@ function CommandInput({ className, ...props }) {
149
149
  ...props
150
150
  })]
151
151
  });
152
- }
153
- function CommandList({ className, ...props }) {
152
+ };
153
+ const CommandList = ({ className, ...props }) => {
154
154
  return /* @__PURE__ */ jsx(Command$1.List, {
155
155
  className: cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className),
156
156
  "data-slot": "command-list",
157
157
  ...props
158
158
  });
159
- }
160
- function CommandEmpty({ ...props }) {
159
+ };
160
+ const CommandEmpty = ({ ...props }) => {
161
161
  return /* @__PURE__ */ jsx(Command$1.Empty, {
162
162
  className: "py-6 text-center text-sm",
163
163
  "data-slot": "command-empty",
164
164
  ...props
165
165
  });
166
- }
167
- function CommandGroup({ className, ...props }) {
166
+ };
167
+ const CommandGroup = ({ className, ...props }) => {
168
168
  return /* @__PURE__ */ jsx(Command$1.Group, {
169
169
  className: cn("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", className),
170
170
  "data-slot": "command-group",
171
171
  ...props
172
172
  });
173
- }
174
- function CommandSeparator({ className, ...props }) {
173
+ };
174
+ const CommandSeparator = ({ className, ...props }) => {
175
175
  return /* @__PURE__ */ jsx(Command$1.Separator, {
176
176
  className: cn("-mx-1 h-px bg-border", className),
177
177
  "data-slot": "command-separator",
178
178
  ...props
179
179
  });
180
- }
181
- function CommandItem({ className, ...props }) {
180
+ };
181
+ const CommandItem = ({ className, ...props }) => {
182
182
  return /* @__PURE__ */ jsx(Command$1.Item, {
183
183
  className: cn("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", className),
184
184
  "data-slot": "command-item",
185
185
  ...props
186
186
  });
187
- }
188
- function CommandShortcut({ className, ...props }) {
187
+ };
188
+ const CommandShortcut = ({ className, ...props }) => {
189
189
  return /* @__PURE__ */ jsx("span", {
190
190
  className: cn("ml-auto text-xs tracking-widest text-muted-foreground", className),
191
191
  "data-slot": "command-shortcut",
192
192
  ...props
193
193
  });
194
- }
194
+ };
195
195
 
196
196
  //#endregion
197
197
  //#region src/lib/resolve-navigation-url.ts
@@ -240,12 +240,12 @@ const CommandMenu = ({ emptyMessage = "No results.", groupHeading = "Posts", ite
240
240
  children: items.map((item) => /* @__PURE__ */ jsx(CommandItem, {
241
241
  onSelect: () => {
242
242
  const destination = resolveNavigationUrl(item.url, window.location.href);
243
- if (destination) window.location.href = destination;
243
+ if (destination !== null && destination !== "") window.location.href = destination;
244
244
  },
245
245
  value: item.title,
246
246
  children: /* @__PURE__ */ jsxs("div", {
247
247
  className: "flex flex-col",
248
- children: [/* @__PURE__ */ jsx("span", { children: item.title }), item.description ? /* @__PURE__ */ jsx("span", {
248
+ children: [/* @__PURE__ */ jsx("span", { children: item.title }), item.description !== void 0 && item.description !== "" ? /* @__PURE__ */ jsx("span", {
249
249
  className: "text-muted-foreground text-xs",
250
250
  children: item.description
251
251
  }) : null]
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["ButtonPrimitive","DialogPrimitive","CommandPrimitive"],"sources":["../../src/components/ui/button-variants.ts","../../src/components/ui/button.tsx","../../src/components/ui/dialog.tsx","../../src/components/ui/command.tsx","../../src/lib/resolve-navigation-url.ts","../../src/components/command-menu.tsx","../../src/components/theme-toggle.tsx"],"sourcesContent":["import { cva } from \"class-variance-authority\";\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\nexport { buttonVariants };\n","import { Button as ButtonPrimitive } from \"@base-ui/react/button\";\nimport type { VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { buttonVariants } from \"./button-variants\";\n\ntype ButtonProps = Omit<React.ComponentProps<typeof ButtonPrimitive>, \"className\"> &\n VariantProps<typeof buttonVariants> & {\n className?: string;\n };\n\n// React 19 passes `ref` as a regular prop, so no forwardRef wrapper is needed.\nconst Button = ({ className, size = \"default\", variant = \"default\", ...props }: ButtonProps) => {\n return (\n <ButtonPrimitive\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 };\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 [&_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 <DialogContent\n className={cn(\"overflow-hidden p-0\", className)}\n showCloseButton={showCloseButton}\n >\n {/* Inside DialogContent so the sr-only name renders in the portaled popup, not the page. */}\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\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","const resolveNavigationUrl = (value: string, baseUrl: string): string | null => {\n try {\n const url = new URL(value, baseUrl);\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? url.href : null;\n } catch {\n return null;\n }\n};\n\nexport { resolveNavigationUrl };\n","import { Search } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\n\nimport { resolveNavigationUrl } from \"../lib/resolve-navigation-url\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"./ui/command\";\nimport { Dialog, DialogContent, DialogTitle, 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 title?: string;\n};\n\nconst CommandMenu = ({\n emptyMessage = \"No results.\",\n groupHeading = \"Posts\",\n items,\n placeholder = \"Search posts…\",\n title = \"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 <DialogTitle className=\"sr-only\">{title}</DialogTitle>\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 const destination = resolveNavigationUrl(item.url, window.location.href);\n if (destination) {\n window.location.href = destination;\n }\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 { 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 storedThemeListeners = new Set<() => void>();\n\nconst subscribeStoredTheme = (callback: () => void) => {\n if (typeof window === \"undefined\") {\n return () => {};\n }\n storedThemeListeners.add(callback);\n // \"storage\" only fires in other tabs; same-tab writes notify via setStoredTheme.\n window.addEventListener(\"storage\", callback);\n return () => {\n storedThemeListeners.delete(callback);\n window.removeEventListener(\"storage\", callback);\n };\n};\n\nconst setStoredTheme = (next: Theme) => {\n window.localStorage.setItem(\"theme\", next);\n for (const listener of storedThemeListeners) {\n listener();\n }\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 both reads: the server\n // snapshots (false / null) match the server HTML, then React re-syncs to the\n // real values right after hydration. Reading localStorage during the initial\n // render instead (e.g. in a useState initializer) makes React 19 log a\n // recoverable hydration error for every visitor with a stored theme.\n const prefersDark = useSyncExternalStore(subscribePrefersDark, getPrefersDark, () => false);\n const override = useSyncExternalStore(subscribeStoredTheme, getStoredTheme, () => null);\n const theme: Theme = override ?? (prefersDark ? \"dark\" : \"light\");\n\n const handleToggleTheme = () => {\n const next: Theme = theme === \"dark\" ? \"light\" : \"dark\";\n document.documentElement.classList.toggle(\"dark\", next === \"dark\");\n setStoredTheme(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-variants.ts","../../src/components/ui/button.tsx","../../src/components/ui/dialog.tsx","../../src/components/ui/command.tsx","../../src/lib/resolve-navigation-url.ts","../../src/components/command-menu.tsx","../../src/components/theme-toggle.tsx"],"sourcesContent":["import { cva } from \"class-variance-authority\";\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\nexport { buttonVariants };\n","import { Button as ButtonPrimitive } from \"@base-ui/react/button\";\nimport type { VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { buttonVariants } from \"./button-variants\";\n\ntype ButtonProps = Omit<React.ComponentProps<typeof ButtonPrimitive>, \"className\"> &\n VariantProps<typeof buttonVariants> & {\n className?: string;\n };\n\n// React 19 passes `ref` as a regular prop, so no forwardRef wrapper is needed.\nconst Button = ({ className, size = \"default\", variant = \"default\", ...props }: ButtonProps) => {\n return (\n <ButtonPrimitive\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 };\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\nconst Dialog = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) => {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />;\n};\n\nconst DialogTrigger = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) => {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n};\n\nconst DialogPortal = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) => {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n};\n\nconst DialogClose = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) => {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n};\n\nconst 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\nconst 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 [&_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\nconst 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\nconst 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\nconst DialogTitle = ({\n className,\n ...props\n}: 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\nconst 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\nconst 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\nconst 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 <DialogContent\n className={cn(\"overflow-hidden p-0\", className)}\n showCloseButton={showCloseButton}\n >\n {/* Inside DialogContent so the sr-only name renders in the portaled popup, not the page. */}\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\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\nconst 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\nconst CommandList = ({\n className,\n ...props\n}: 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\nconst 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\nconst 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\nconst 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\nconst CommandItem = ({\n className,\n ...props\n}: 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\nconst 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","const resolveNavigationUrl = (value: string, baseUrl: string): string | null => {\n try {\n const url = new URL(value, baseUrl);\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? url.href : null;\n } catch {\n return null;\n }\n};\n\nexport { resolveNavigationUrl };\n","import { Search } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\n\nimport { resolveNavigationUrl } from \"../lib/resolve-navigation-url\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"./ui/command\";\nimport { Dialog, DialogContent, DialogTitle, 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 title?: string;\n};\n\nconst CommandMenu = ({\n emptyMessage = \"No results.\",\n groupHeading = \"Posts\",\n items,\n placeholder = \"Search posts…\",\n title = \"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 <DialogTitle className=\"sr-only\">{title}</DialogTitle>\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 const destination = resolveNavigationUrl(item.url, window.location.href);\n if (destination !== null && destination !== \"\") {\n window.location.href = destination;\n }\n }}\n value={item.title}\n >\n <div className=\"flex flex-col\">\n <span>{item.title}</span>\n {item.description !== undefined && 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 { 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 storedThemeListeners = new Set<() => void>();\n\nconst subscribeStoredTheme = (callback: () => void) => {\n if (typeof window === \"undefined\") {\n return () => {};\n }\n storedThemeListeners.add(callback);\n // \"storage\" only fires in other tabs; same-tab writes notify via setStoredTheme.\n window.addEventListener(\"storage\", callback);\n return () => {\n storedThemeListeners.delete(callback);\n window.removeEventListener(\"storage\", callback);\n };\n};\n\nconst setStoredTheme = (next: Theme) => {\n window.localStorage.setItem(\"theme\", next);\n for (const listener of storedThemeListeners) {\n listener();\n }\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 both reads: the server\n // snapshots (false / null) match the server HTML, then React re-syncs to the\n // real values right after hydration. Reading localStorage during the initial\n // render instead (e.g. in a useState initializer) makes React 19 log a\n // recoverable hydration error for every visitor with a stored theme.\n const prefersDark = useSyncExternalStore(subscribePrefersDark, getPrefersDark, () => false);\n const override = useSyncExternalStore(subscribeStoredTheme, getStoredTheme, () => null);\n const theme: Theme = override ?? (prefersDark ? \"dark\" : \"light\");\n\n const handleToggleTheme = () => {\n const next: Theme = theme === \"dark\" ? \"light\" : \"dark\";\n document.documentElement.classList.toggle(\"dark\", next === \"dark\");\n setStoredTheme(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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"}
@@ -36,12 +36,12 @@ declare const authorSchema: z.ZodObject<{
36
36
  type Post = z.infer<typeof postSchema>;
37
37
  type Tag = z.infer<typeof tagSchema>;
38
38
  type Author = z.infer<typeof authorSchema>;
39
- declare const notDraft: <T extends {
39
+ declare const notDraft: (post: {
40
40
  data: {
41
41
  draft?: boolean;
42
42
  status?: string;
43
43
  };
44
- }>(post: T) => boolean;
44
+ }) => boolean;
45
45
  //#endregion
46
46
  export { type Author, type Post, type Tag, authorSchema, notDraft, postSchema, index_d_exports as t, tagSchema };
47
47
  //# sourceMappingURL=index.d.ts.map
@@ -37,7 +37,7 @@ const authorSchema = z.object({
37
37
  name: z.string().min(1),
38
38
  url: z.url().optional()
39
39
  });
40
- const notDraft = (post) => post.data.status !== "DRAFT" && !post.data.draft;
40
+ const notDraft = (post) => post.data.status !== "DRAFT" && post.data.draft !== true;
41
41
 
42
42
  //#endregion
43
43
  export { authorSchema, notDraft, postSchema, content_exports as t, tagSchema };
@@ -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 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// One source of truth for the draft guard so the loader contract stays in sync\n// with the consumer-side check (`data.status !== \"DRAFT\" && !data.draft`).\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;AAQD,MAAM,YAAsE,SAC1E,KAAK,KAAK,WAAW,WAAW,CAAC,KAAK,KAAK"}
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// One source of truth for the draft guard so the loader contract stays in sync\n// with the consumer-side check (`data.status !== \"DRAFT\" && !data.draft`).\nconst notDraft = (post: { data: { draft?: boolean; status?: string } }): boolean =>\n post.data.status !== \"DRAFT\" && post.data.draft !== true;\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;AAQD,MAAM,YAAY,SAChB,KAAK,KAAK,WAAW,WAAW,KAAK,KAAK,UAAU"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { t as content_exports } from "./content/index.js";
2
- import { t as lib_exports } from "./lib-DBsXnJT1.js";
2
+ import { t as lib_exports } from "./lib-WSV3WGQr.js";
3
3
 
4
4
  export { content_exports as content, lib_exports as lib };
package/dist/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { t as cn } from "../cn-DmzdyV07.js";
2
- import { a as getRelatedPosts, i as slugify, n as readingTime, o as formatDate, r as createPostUrl } from "../lib-DBsXnJT1.js";
2
+ import { a as getRelatedPosts, i as slugify, n as readingTime, o as formatDate, r as createPostUrl } from "../lib-WSV3WGQr.js";
3
3
 
4
4
  export { cn, createPostUrl, formatDate, getRelatedPosts, readingTime, slugify };
@@ -52,18 +52,18 @@ const slugify = (input) => input.normalize("NFD").replaceAll(/* @__PURE__ */ new
52
52
  const createPostUrl = (categorySlugMap, defaultCategory) => {
53
53
  const postParams = (post) => {
54
54
  const canonical = post.data.seo?.canonical_url;
55
- if (canonical) try {
55
+ if (canonical !== void 0 && canonical !== "") try {
56
56
  const segments = new URL(canonical).pathname.split("/").filter(Boolean);
57
57
  const category = segments.at(-2);
58
58
  const slug = segments.at(-1);
59
- if (category && slug) return {
59
+ if (category !== void 0 && slug !== void 0) return {
60
60
  category,
61
61
  slug
62
62
  };
63
63
  } catch {}
64
64
  const first = post.data.categories?.at(0);
65
65
  return {
66
- category: first ? categorySlugMap[first] ?? slugify(first) : defaultCategory,
66
+ category: first !== void 0 && first !== "" ? categorySlugMap[first] ?? slugify(first) : defaultCategory,
67
67
  slug: post.id
68
68
  };
69
69
  };
@@ -102,4 +102,4 @@ var lib_exports = /* @__PURE__ */ __exportAll({
102
102
 
103
103
  //#endregion
104
104
  export { getRelatedPosts as a, slugify as i, readingTime as n, formatDate as o, createPostUrl as r, lib_exports as t };
105
- //# sourceMappingURL=lib-DBsXnJT1.js.map
105
+ //# sourceMappingURL=lib-WSV3WGQr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib-WSV3WGQr.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 = (key: string, locale: string, options: Intl.DateTimeFormatOptions) => {\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 = (date: Date, locale: string = \"en-US\", options?: Intl.DateTimeFormatOptions) => {\n // Default-option calls (every blog passes only (date, locale)) key on locale\n // alone, skipping the JSON.stringify the custom-option path needs.\n if (!options) {\n return getFormatter(locale, locale, defaultOptions).format(date);\n }\n const merged = { ...defaultOptions, ...options };\n return getFormatter(`${locale}:${JSON.stringify(merged)}`, locale, merged).format(date);\n};\n\nexport { formatDate };\n","type HasTags = { data: { tags: Array<string> }; id: 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.id === current.id) {\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 !== undefined && 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 !== undefined && slug !== undefined) {\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 =\n first !== undefined && first !== \"\"\n ? (categorySlugMap[first] ?? slugify(first))\n : 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,KAAa,QAAgB,YAAwC;CACzF,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,cAAc,MAAY,SAAiB,SAAS,YAAyC;CAGjG,IAAI,CAAC,SACH,OAAO,aAAa,QAAQ,QAAQ,cAAc,CAAC,CAAC,OAAO,IAAI;CAEjE,MAAM,SAAS;EAAE,GAAG;EAAgB,GAAG;CAAQ;CAC/C,OAAO,aAAa,GAAG,OAAO,GAAG,KAAK,UAAU,MAAM,KAAK,QAAQ,MAAM,CAAC,CAAC,OAAO,IAAI;AACxF;;;;ACxBA,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,OAAO,QAAQ,IACtB;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,wBAAQ,GAAG,EAAE,CAAC,CACzB,YAAY,CAAC,CACb,2BAAW,kCAAgB,GAAG,EAAE,CAAC,CACjC,KAAK,CAAC,CACN,2BAAW,4BAAU,GAAG,GAAG,CAAC,CAC5B,2BAAW,0BAAU,GAAG,EAAE;;;;ACc/B,MAAM,iBACJ,iBACA,oBACmB;CACnB,MAAM,cAAc,SAA+B;EACjD,MAAM,YAAY,KAAK,KAAK,KAAK;EACjC,IAAI,cAAc,UAAa,cAAc,IAC3C,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,aAAa,UAAa,SAAS,QACrC,OAAO;IAAE;IAAU;GAAK;EAE5B,QAAQ,CAER;EAEF,MAAM,QAAQ,KAAK,KAAK,YAAY,GAAG,CAAC;EAKxC,OAAO;GAAE,UAHP,UAAU,UAAa,UAAU,KAC5B,gBAAgB,UAAU,QAAQ,KAAK,IACxC;GACa,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;;;;ACtDA,MAAM,mBAAmB;AAEzB,MAAM,eAAe,UAAkB;CACrC,MAAM,WAAW,MACd,2BAAW,0BAAU,GAAG,EAAE,CAAC,CAC3B,2BAAW,6BAAY,GAAG,GAAG,CAAC,CAC9B,2BAAW,uBAAM,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.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "Astro 6/7 blog theme with shadcn primitives and a Tailwind v4 preset.",
5
5
  "keywords": [
6
6
  "astro",
@@ -62,7 +62,7 @@ const CommandMenu = ({
62
62
  key={item.url}
63
63
  onSelect={() => {
64
64
  const destination = resolveNavigationUrl(item.url, window.location.href);
65
- if (destination) {
65
+ if (destination !== null && destination !== "") {
66
66
  window.location.href = destination;
67
67
  }
68
68
  }}
@@ -70,7 +70,7 @@ const CommandMenu = ({
70
70
  >
71
71
  <div className="flex flex-col">
72
72
  <span>{item.title}</span>
73
- {item.description ? (
73
+ {item.description !== undefined && item.description !== "" ? (
74
74
  <span className="text-muted-foreground text-xs">{item.description}</span>
75
75
  ) : null}
76
76
  </div>
@@ -5,7 +5,7 @@ import { SearchIcon } from "lucide-react";
5
5
  import { cn } from "../../lib/cn";
6
6
  import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./dialog";
7
7
 
8
- function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {
8
+ const Command = ({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) => {
9
9
  return (
10
10
  <CommandPrimitive
11
11
  className={cn(
@@ -16,9 +16,9 @@ function Command({ className, ...props }: React.ComponentProps<typeof CommandPri
16
16
  {...props}
17
17
  />
18
18
  );
19
- }
19
+ };
20
20
 
21
- function CommandDialog({
21
+ const CommandDialog = ({
22
22
  children,
23
23
  className,
24
24
  description = "Search for a command to run...",
@@ -31,7 +31,7 @@ function CommandDialog({
31
31
  description?: string;
32
32
  showCloseButton?: boolean;
33
33
  title?: string;
34
- }) {
34
+ }) => {
35
35
  return (
36
36
  <Dialog {...props}>
37
37
  <DialogContent
@@ -49,12 +49,12 @@ function CommandDialog({
49
49
  </DialogContent>
50
50
  </Dialog>
51
51
  );
52
- }
52
+ };
53
53
 
54
- function CommandInput({
54
+ const CommandInput = ({
55
55
  className,
56
56
  ...props
57
- }: React.ComponentProps<typeof CommandPrimitive.Input>) {
57
+ }: React.ComponentProps<typeof CommandPrimitive.Input>) => {
58
58
  return (
59
59
  <div className="flex h-9 items-center gap-2 border-b px-3" data-slot="command-input-wrapper">
60
60
  <SearchIcon className="size-4 shrink-0 opacity-50" />
@@ -68,9 +68,12 @@ function CommandInput({
68
68
  />
69
69
  </div>
70
70
  );
71
- }
71
+ };
72
72
 
73
- function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
73
+ const CommandList = ({
74
+ className,
75
+ ...props
76
+ }: React.ComponentProps<typeof CommandPrimitive.List>) => {
74
77
  return (
75
78
  <CommandPrimitive.List
76
79
  className={cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className)}
@@ -78,9 +81,9 @@ function CommandList({ className, ...props }: React.ComponentProps<typeof Comman
78
81
  {...props}
79
82
  />
80
83
  );
81
- }
84
+ };
82
85
 
83
- function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
86
+ const CommandEmpty = ({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) => {
84
87
  return (
85
88
  <CommandPrimitive.Empty
86
89
  className="py-6 text-center text-sm"
@@ -88,12 +91,12 @@ function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive
88
91
  {...props}
89
92
  />
90
93
  );
91
- }
94
+ };
92
95
 
93
- function CommandGroup({
96
+ const CommandGroup = ({
94
97
  className,
95
98
  ...props
96
- }: React.ComponentProps<typeof CommandPrimitive.Group>) {
99
+ }: React.ComponentProps<typeof CommandPrimitive.Group>) => {
97
100
  return (
98
101
  <CommandPrimitive.Group
99
102
  className={cn(
@@ -104,12 +107,12 @@ function CommandGroup({
104
107
  {...props}
105
108
  />
106
109
  );
107
- }
110
+ };
108
111
 
109
- function CommandSeparator({
112
+ const CommandSeparator = ({
110
113
  className,
111
114
  ...props
112
- }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
115
+ }: React.ComponentProps<typeof CommandPrimitive.Separator>) => {
113
116
  return (
114
117
  <CommandPrimitive.Separator
115
118
  className={cn("-mx-1 h-px bg-border", className)}
@@ -117,9 +120,12 @@ function CommandSeparator({
117
120
  {...props}
118
121
  />
119
122
  );
120
- }
123
+ };
121
124
 
122
- function CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {
125
+ const CommandItem = ({
126
+ className,
127
+ ...props
128
+ }: React.ComponentProps<typeof CommandPrimitive.Item>) => {
123
129
  return (
124
130
  <CommandPrimitive.Item
125
131
  className={cn(
@@ -130,9 +136,9 @@ function CommandItem({ className, ...props }: React.ComponentProps<typeof Comman
130
136
  {...props}
131
137
  />
132
138
  );
133
- }
139
+ };
134
140
 
135
- function CommandShortcut({ className, ...props }: React.ComponentProps<"span">) {
141
+ const CommandShortcut = ({ className, ...props }: React.ComponentProps<"span">) => {
136
142
  return (
137
143
  <span
138
144
  className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)}
@@ -140,7 +146,7 @@ function CommandShortcut({ className, ...props }: React.ComponentProps<"span">)
140
146
  {...props}
141
147
  />
142
148
  );
143
- }
149
+ };
144
150
 
145
151
  export {
146
152
  Command,
@@ -5,26 +5,26 @@ import { XIcon } from "lucide-react";
5
5
  import { cn } from "../../lib/cn";
6
6
  import { Button } from "./button";
7
7
 
8
- function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
8
+ const Dialog = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) => {
9
9
  return <DialogPrimitive.Root data-slot="dialog" {...props} />;
10
- }
10
+ };
11
11
 
12
- function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
12
+ const DialogTrigger = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) => {
13
13
  return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
14
- }
14
+ };
15
15
 
16
- function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
16
+ const DialogPortal = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) => {
17
17
  return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
18
- }
18
+ };
19
19
 
20
- function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
20
+ const DialogClose = ({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) => {
21
21
  return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
22
- }
22
+ };
23
23
 
24
- function DialogOverlay({
24
+ const DialogOverlay = ({
25
25
  className,
26
26
  ...props
27
- }: React.ComponentProps<typeof DialogPrimitive.Backdrop>) {
27
+ }: React.ComponentProps<typeof DialogPrimitive.Backdrop>) => {
28
28
  return (
29
29
  <DialogPrimitive.Backdrop
30
30
  className={cn(
@@ -35,16 +35,16 @@ function DialogOverlay({
35
35
  {...props}
36
36
  />
37
37
  );
38
- }
38
+ };
39
39
 
40
- function DialogContent({
40
+ const DialogContent = ({
41
41
  children,
42
42
  className,
43
43
  showCloseButton = true,
44
44
  ...props
45
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 />
@@ -70,9 +70,9 @@ function DialogContent({
70
70
  </DialogPrimitive.Popup>
71
71
  </DialogPortal>
72
72
  );
73
- }
73
+ };
74
74
 
75
- function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
75
+ const DialogHeader = ({ className, ...props }: React.ComponentProps<"div">) => {
76
76
  return (
77
77
  <div
78
78
  className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
@@ -80,16 +80,16 @@ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
80
80
  {...props}
81
81
  />
82
82
  );
83
- }
83
+ };
84
84
 
85
- function DialogFooter({
85
+ const DialogFooter = ({
86
86
  children,
87
87
  className,
88
88
  showCloseButton = false,
89
89
  ...props
90
90
  }: React.ComponentProps<"div"> & {
91
91
  showCloseButton?: boolean;
92
- }) {
92
+ }) => {
93
93
  return (
94
94
  <div
95
95
  className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
@@ -102,9 +102,12 @@ function DialogFooter({
102
102
  )}
103
103
  </div>
104
104
  );
105
- }
105
+ };
106
106
 
107
- function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
107
+ const DialogTitle = ({
108
+ className,
109
+ ...props
110
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) => {
108
111
  return (
109
112
  <DialogPrimitive.Title
110
113
  className={cn("text-lg leading-none font-semibold", className)}
@@ -112,12 +115,12 @@ function DialogTitle({ className, ...props }: React.ComponentProps<typeof Dialog
112
115
  {...props}
113
116
  />
114
117
  );
115
- }
118
+ };
116
119
 
117
- function DialogDescription({
120
+ const DialogDescription = ({
118
121
  className,
119
122
  ...props
120
- }: React.ComponentProps<typeof DialogPrimitive.Description>) {
123
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) => {
121
124
  return (
122
125
  <DialogPrimitive.Description
123
126
  className={cn("text-sm text-muted-foreground", className)}
@@ -125,7 +128,7 @@ function DialogDescription({
125
128
  {...props}
126
129
  />
127
130
  );
128
- }
131
+ };
129
132
 
130
133
  export {
131
134
  Dialog,
@@ -42,8 +42,8 @@ type Author = z.infer<typeof authorSchema>;
42
42
 
43
43
  // One source of truth for the draft guard so the loader contract stays in sync
44
44
  // with the consumer-side check (`data.status !== "DRAFT" && !data.draft`).
45
- const notDraft = <T extends { data: { draft?: boolean; status?: string } }>(post: T): boolean =>
46
- post.data.status !== "DRAFT" && !post.data.draft;
45
+ const notDraft = (post: { data: { draft?: boolean; status?: string } }): boolean =>
46
+ post.data.status !== "DRAFT" && post.data.draft !== true;
47
47
 
48
48
  export { authorSchema, notDraft, postSchema, tagSchema };
49
49
  export type { Author, Post, Tag };
@@ -26,12 +26,12 @@ const createPostUrl = (
26
26
  ): PostUrlBuilder => {
27
27
  const postParams = (post: PostLike): PostParams => {
28
28
  const canonical = post.data.seo?.canonical_url;
29
- if (canonical) {
29
+ if (canonical !== undefined && canonical !== "") {
30
30
  try {
31
31
  const segments = new URL(canonical).pathname.split("/").filter(Boolean);
32
32
  const category = segments.at(-2);
33
33
  const slug = segments.at(-1);
34
- if (category && slug) {
34
+ if (category !== undefined && slug !== undefined) {
35
35
  return { category, slug };
36
36
  }
37
37
  } catch {
@@ -39,7 +39,10 @@ const createPostUrl = (
39
39
  }
40
40
  }
41
41
  const first = post.data.categories?.at(0);
42
- const category = first ? (categorySlugMap[first] ?? slugify(first)) : defaultCategory;
42
+ const category =
43
+ first !== undefined && first !== ""
44
+ ? (categorySlugMap[first] ?? slugify(first))
45
+ : defaultCategory;
43
46
  return { category, slug: post.id };
44
47
  };
45
48
 
@@ -1 +0,0 @@
1
- {"version":3,"file":"lib-DBsXnJT1.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 = (key: string, locale: string, options: Intl.DateTimeFormatOptions) => {\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 = (date: Date, locale: string = \"en-US\", options?: Intl.DateTimeFormatOptions) => {\n // Default-option calls (every blog passes only (date, locale)) key on locale\n // alone, skipping the JSON.stringify the custom-option path needs.\n if (!options) {\n return getFormatter(locale, locale, defaultOptions).format(date);\n }\n const merged = { ...defaultOptions, ...options };\n return getFormatter(`${locale}:${JSON.stringify(merged)}`, locale, merged).format(date);\n};\n\nexport { formatDate };\n","type HasTags = { data: { tags: Array<string> }; id: 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.id === current.id) {\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,KAAa,QAAgB,YAAwC;CACzF,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,cAAc,MAAY,SAAiB,SAAS,YAAyC;CAGjG,IAAI,CAAC,SACH,OAAO,aAAa,QAAQ,QAAQ,cAAc,CAAC,CAAC,OAAO,IAAI;CAEjE,MAAM,SAAS;EAAE,GAAG;EAAgB,GAAG;CAAQ;CAC/C,OAAO,aAAa,GAAG,OAAO,GAAG,KAAK,UAAU,MAAM,KAAK,QAAQ,MAAM,CAAC,CAAC,OAAO,IAAI;AACxF;;;;ACxBA,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,OAAO,QAAQ,IACtB;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,wBAAQ,GAAG,EAAE,CAAC,CACzB,YAAY,CAAC,CACb,2BAAW,kCAAgB,GAAG,EAAE,CAAC,CACjC,KAAK,CAAC,CACN,2BAAW,4BAAU,GAAG,GAAG,CAAC,CAC5B,2BAAW,0BAAU,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,0BAAU,GAAG,EAAE,CAAC,CAC3B,2BAAW,6BAAY,GAAG,GAAG,CAAC,CAC9B,2BAAW,uBAAM,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"}