astro-awesomeness 0.8.0 → 1.0.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.
- package/dist/components/index.d.ts +1 -41
- package/dist/components/index.js +4 -211
- package/dist/components/index.js.map +1 -1
- package/dist/content/index.d.ts +12 -3
- package/dist/content/index.js +4 -2
- package/dist/content/index.js.map +1 -1
- package/dist/{index-BuHeGVBM.d.ts → index-BspVzulR.d.ts} +6 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/index.js +2 -2
- package/dist/{lib-8VQ_znvk.js → lib-V-jasC53.js} +11 -2
- package/dist/lib-V-jasC53.js.map +1 -0
- package/package.json +9 -2
- package/src/components/index.ts +0 -20
- package/src/content/index.test.ts +40 -1
- package/src/content/index.ts +7 -2
- package/src/layouts/base-layout.astro +17 -12
- package/src/layouts/list-layout.astro +4 -0
- package/src/layouts/post-layout.astro +4 -0
- package/src/lib/index.ts +1 -0
- package/src/lib/require-env.test.ts +26 -0
- package/src/lib/require-env.ts +12 -0
- package/dist/lib-8VQ_znvk.js.map +0 -1
- package/src/components/command-menu.tsx +0 -88
- package/src/components/ui/command.tsx +0 -162
- package/src/components/ui/dialog.tsx +0 -147
- package/src/lib/resolve-navigation-url.test.ts +0 -25
- package/src/lib/resolve-navigation-url.ts +0 -10
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { Button as Button$1 } from "@base-ui/react/button";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { VariantProps } from "class-variance-authority";
|
|
4
|
-
import { Command as Command$1 } from "cmdk";
|
|
5
|
-
import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
|
|
6
4
|
//#region src/components/ui/button-variants.d.ts
|
|
7
5
|
declare const buttonVariants: (props?: ({
|
|
8
6
|
size?: "default" | "icon" | "icon-lg" | "icon-sm" | "icon-xs" | "lg" | "sm" | "xs" | null | undefined;
|
|
@@ -15,49 +13,11 @@ type ButtonProps = Omit<React.ComponentProps<typeof Button$1>, "className"> & Va
|
|
|
15
13
|
};
|
|
16
14
|
declare const Button: ({ className, size, variant, ...props }: ButtonProps) => React.JSX.Element;
|
|
17
15
|
//#endregion
|
|
18
|
-
//#region src/components/ui/dialog.d.ts
|
|
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
|
-
showCloseButton?: boolean;
|
|
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
|
-
showCloseButton?: boolean;
|
|
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
|
-
//#endregion
|
|
31
|
-
//#region src/components/ui/command.d.ts
|
|
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
|
-
//#endregion
|
|
41
|
-
//#region src/components/command-menu.d.ts
|
|
42
|
-
type CommandMenuItem = {
|
|
43
|
-
description?: string;
|
|
44
|
-
title: string;
|
|
45
|
-
url: string;
|
|
46
|
-
};
|
|
47
|
-
type CommandMenuProps = {
|
|
48
|
-
emptyMessage?: string;
|
|
49
|
-
groupHeading?: string;
|
|
50
|
-
items: Array<CommandMenuItem>;
|
|
51
|
-
placeholder?: string;
|
|
52
|
-
title?: string;
|
|
53
|
-
};
|
|
54
|
-
declare const CommandMenu: ({ emptyMessage, groupHeading, items, placeholder, title }: CommandMenuProps) => import("react").JSX.Element;
|
|
55
|
-
//#endregion
|
|
56
16
|
//#region src/components/theme-toggle.d.ts
|
|
57
17
|
type Props = {
|
|
58
18
|
ariaLabel?: string;
|
|
59
19
|
};
|
|
60
20
|
declare const ThemeToggle: ({ ariaLabel }: Props) => import("react").JSX.Element;
|
|
61
21
|
//#endregion
|
|
62
|
-
export { Button,
|
|
22
|
+
export { Button, ThemeToggle, buttonVariants };
|
|
63
23
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/components/index.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { t as cn } from "../cn-DmzdyV07.js";
|
|
2
2
|
import { Button as Button$1 } from "@base-ui/react/button";
|
|
3
|
-
import {
|
|
3
|
+
import { useSyncExternalStore } from "react";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
|
-
import { jsx
|
|
6
|
-
import {
|
|
7
|
-
import { Moon, Search, SearchIcon, Sun, XIcon } from "lucide-react";
|
|
8
|
-
import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
import { Moon, Sun } from "lucide-react";
|
|
9
7
|
|
|
10
8
|
//#region src/components/ui/button-variants.ts
|
|
11
9
|
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", {
|
|
@@ -51,211 +49,6 @@ const Button = ({ className, size = "default", variant = "default", ...props })
|
|
|
51
49
|
});
|
|
52
50
|
};
|
|
53
51
|
|
|
54
|
-
//#endregion
|
|
55
|
-
//#region src/components/ui/dialog.tsx
|
|
56
|
-
const Dialog = ({ ...props }) => {
|
|
57
|
-
return /* @__PURE__ */ jsx(Dialog$1.Root, {
|
|
58
|
-
"data-slot": "dialog",
|
|
59
|
-
...props
|
|
60
|
-
});
|
|
61
|
-
};
|
|
62
|
-
const DialogTrigger = ({ ...props }) => {
|
|
63
|
-
return /* @__PURE__ */ jsx(Dialog$1.Trigger, {
|
|
64
|
-
"data-slot": "dialog-trigger",
|
|
65
|
-
...props
|
|
66
|
-
});
|
|
67
|
-
};
|
|
68
|
-
const DialogPortal = ({ ...props }) => {
|
|
69
|
-
return /* @__PURE__ */ jsx(Dialog$1.Portal, {
|
|
70
|
-
"data-slot": "dialog-portal",
|
|
71
|
-
...props
|
|
72
|
-
});
|
|
73
|
-
};
|
|
74
|
-
const DialogOverlay = ({ className, ...props }) => {
|
|
75
|
-
return /* @__PURE__ */ jsx(Dialog$1.Backdrop, {
|
|
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
|
-
"data-slot": "dialog-overlay",
|
|
78
|
-
...props
|
|
79
|
-
});
|
|
80
|
-
};
|
|
81
|
-
const DialogContent = ({ children, className, showCloseButton = true, ...props }) => {
|
|
82
|
-
return /* @__PURE__ */ jsxs(DialogPortal, {
|
|
83
|
-
"data-slot": "dialog-portal",
|
|
84
|
-
children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(Dialog$1.Popup, {
|
|
85
|
-
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),
|
|
86
|
-
"data-slot": "dialog-content",
|
|
87
|
-
...props,
|
|
88
|
-
children: [children, showCloseButton && /* @__PURE__ */ jsxs(Dialog$1.Close, {
|
|
89
|
-
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",
|
|
90
|
-
"data-slot": "dialog-close",
|
|
91
|
-
type: "button",
|
|
92
|
-
children: [/* @__PURE__ */ jsx(XIcon, {}), /* @__PURE__ */ jsx("span", {
|
|
93
|
-
className: "sr-only",
|
|
94
|
-
children: "Close"
|
|
95
|
-
})]
|
|
96
|
-
})]
|
|
97
|
-
})]
|
|
98
|
-
});
|
|
99
|
-
};
|
|
100
|
-
const DialogHeader = ({ className, ...props }) => {
|
|
101
|
-
return /* @__PURE__ */ jsx("div", {
|
|
102
|
-
className: cn("flex flex-col gap-2 text-center sm:text-left", className),
|
|
103
|
-
"data-slot": "dialog-header",
|
|
104
|
-
...props
|
|
105
|
-
});
|
|
106
|
-
};
|
|
107
|
-
const DialogFooter = ({ children, className, showCloseButton = false, ...props }) => {
|
|
108
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
109
|
-
className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className),
|
|
110
|
-
"data-slot": "dialog-footer",
|
|
111
|
-
...props,
|
|
112
|
-
children: [children, showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, {
|
|
113
|
-
render: /* @__PURE__ */ jsx(Button, { variant: "outline" }),
|
|
114
|
-
children: "Close"
|
|
115
|
-
})]
|
|
116
|
-
});
|
|
117
|
-
};
|
|
118
|
-
const DialogTitle = ({ className, ...props }) => {
|
|
119
|
-
return /* @__PURE__ */ jsx(Dialog$1.Title, {
|
|
120
|
-
className: cn("text-lg leading-none font-semibold", className),
|
|
121
|
-
"data-slot": "dialog-title",
|
|
122
|
-
...props
|
|
123
|
-
});
|
|
124
|
-
};
|
|
125
|
-
const DialogDescription = ({ className, ...props }) => {
|
|
126
|
-
return /* @__PURE__ */ jsx(Dialog$1.Description, {
|
|
127
|
-
className: cn("text-sm text-muted-foreground", className),
|
|
128
|
-
"data-slot": "dialog-description",
|
|
129
|
-
...props
|
|
130
|
-
});
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
//#endregion
|
|
134
|
-
//#region src/components/ui/command.tsx
|
|
135
|
-
const Command = ({ className, ...props }) => {
|
|
136
|
-
return /* @__PURE__ */ jsx(Command$1, {
|
|
137
|
-
className: cn("flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", className),
|
|
138
|
-
"data-slot": "command",
|
|
139
|
-
...props
|
|
140
|
-
});
|
|
141
|
-
};
|
|
142
|
-
const CommandInput = ({ className, ...props }) => {
|
|
143
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
144
|
-
className: "flex h-9 items-center gap-2 border-b px-3",
|
|
145
|
-
"data-slot": "command-input-wrapper",
|
|
146
|
-
children: [/* @__PURE__ */ jsx(SearchIcon, { className: "size-4 shrink-0 opacity-50" }), /* @__PURE__ */ jsx(Command$1.Input, {
|
|
147
|
-
className: cn("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", className),
|
|
148
|
-
"data-slot": "command-input",
|
|
149
|
-
...props
|
|
150
|
-
})]
|
|
151
|
-
});
|
|
152
|
-
};
|
|
153
|
-
const CommandList = ({ className, ...props }) => {
|
|
154
|
-
return /* @__PURE__ */ jsx(Command$1.List, {
|
|
155
|
-
className: cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className),
|
|
156
|
-
"data-slot": "command-list",
|
|
157
|
-
...props
|
|
158
|
-
});
|
|
159
|
-
};
|
|
160
|
-
const CommandEmpty = ({ ...props }) => {
|
|
161
|
-
return /* @__PURE__ */ jsx(Command$1.Empty, {
|
|
162
|
-
className: "py-6 text-center text-sm",
|
|
163
|
-
"data-slot": "command-empty",
|
|
164
|
-
...props
|
|
165
|
-
});
|
|
166
|
-
};
|
|
167
|
-
const CommandGroup = ({ className, ...props }) => {
|
|
168
|
-
return /* @__PURE__ */ jsx(Command$1.Group, {
|
|
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
|
-
"data-slot": "command-group",
|
|
171
|
-
...props
|
|
172
|
-
});
|
|
173
|
-
};
|
|
174
|
-
const CommandSeparator = ({ className, ...props }) => {
|
|
175
|
-
return /* @__PURE__ */ jsx(Command$1.Separator, {
|
|
176
|
-
className: cn("-mx-1 h-px bg-border", className),
|
|
177
|
-
"data-slot": "command-separator",
|
|
178
|
-
...props
|
|
179
|
-
});
|
|
180
|
-
};
|
|
181
|
-
const CommandItem = ({ className, ...props }) => {
|
|
182
|
-
return /* @__PURE__ */ jsx(Command$1.Item, {
|
|
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
|
-
"data-slot": "command-item",
|
|
185
|
-
...props
|
|
186
|
-
});
|
|
187
|
-
};
|
|
188
|
-
const CommandShortcut = ({ className, ...props }) => {
|
|
189
|
-
return /* @__PURE__ */ jsx("span", {
|
|
190
|
-
className: cn("ml-auto text-xs tracking-widest text-muted-foreground", className),
|
|
191
|
-
"data-slot": "command-shortcut",
|
|
192
|
-
...props
|
|
193
|
-
});
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
//#endregion
|
|
197
|
-
//#region src/lib/resolve-navigation-url.ts
|
|
198
|
-
const resolveNavigationUrl = (value, baseUrl) => {
|
|
199
|
-
try {
|
|
200
|
-
const url = new URL(value, baseUrl);
|
|
201
|
-
return url.protocol === "http:" || url.protocol === "https:" ? url.href : null;
|
|
202
|
-
} catch {
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
//#endregion
|
|
208
|
-
//#region src/components/command-menu.tsx
|
|
209
|
-
const CommandMenu = ({ emptyMessage = "No results.", groupHeading = "Posts", items, placeholder = "Search posts…", title = "Search posts" }) => {
|
|
210
|
-
const [open, setOpen] = useState(false);
|
|
211
|
-
useEffect(() => {
|
|
212
|
-
const handleKeyDown = (event) => {
|
|
213
|
-
if (event.key === "k" && (event.metaKey || event.ctrlKey)) {
|
|
214
|
-
event.preventDefault();
|
|
215
|
-
setOpen((value) => !value);
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
219
|
-
return () => {
|
|
220
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
221
|
-
};
|
|
222
|
-
}, []);
|
|
223
|
-
return /* @__PURE__ */ jsxs(Dialog, {
|
|
224
|
-
onOpenChange: setOpen,
|
|
225
|
-
open,
|
|
226
|
-
children: [/* @__PURE__ */ jsx(DialogTrigger, {
|
|
227
|
-
render: /* @__PURE__ */ jsx(Button, {
|
|
228
|
-
"aria-label": "Open search",
|
|
229
|
-
size: "icon",
|
|
230
|
-
variant: "ghost"
|
|
231
|
-
}),
|
|
232
|
-
children: /* @__PURE__ */ jsx(Search, {})
|
|
233
|
-
}), /* @__PURE__ */ jsxs(DialogContent, {
|
|
234
|
-
className: "p-0",
|
|
235
|
-
children: [/* @__PURE__ */ jsx(DialogTitle, {
|
|
236
|
-
className: "sr-only",
|
|
237
|
-
children: title
|
|
238
|
-
}), /* @__PURE__ */ jsxs(Command, { children: [/* @__PURE__ */ jsx(CommandInput, { placeholder }), /* @__PURE__ */ jsxs(CommandList, { children: [/* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }), /* @__PURE__ */ jsx(CommandGroup, {
|
|
239
|
-
heading: groupHeading,
|
|
240
|
-
children: items.map((item) => /* @__PURE__ */ jsx(CommandItem, {
|
|
241
|
-
onSelect: () => {
|
|
242
|
-
const destination = resolveNavigationUrl(item.url, window.location.href);
|
|
243
|
-
if (destination !== null && destination !== "") window.location.href = destination;
|
|
244
|
-
},
|
|
245
|
-
value: item.title,
|
|
246
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
247
|
-
className: "flex flex-col",
|
|
248
|
-
children: [/* @__PURE__ */ jsx("span", { children: item.title }), item.description !== void 0 && item.description !== "" ? /* @__PURE__ */ jsx("span", {
|
|
249
|
-
className: "text-muted-foreground text-xs",
|
|
250
|
-
children: item.description
|
|
251
|
-
}) : null]
|
|
252
|
-
})
|
|
253
|
-
}, item.url))
|
|
254
|
-
})] })] })]
|
|
255
|
-
})]
|
|
256
|
-
});
|
|
257
|
-
};
|
|
258
|
-
|
|
259
52
|
//#endregion
|
|
260
53
|
//#region src/components/theme-toggle.tsx
|
|
261
54
|
const getStoredTheme = () => {
|
|
@@ -308,5 +101,5 @@ const ThemeToggle = ({ ariaLabel = "Toggle theme" }) => {
|
|
|
308
101
|
};
|
|
309
102
|
|
|
310
103
|
//#endregion
|
|
311
|
-
export { Button,
|
|
104
|
+
export { Button, ThemeToggle, buttonVariants };
|
|
312
105
|
//# sourceMappingURL=index.js.map
|
|
@@ -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\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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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["ButtonPrimitive"],"sources":["../../src/components/ui/button-variants.ts","../../src/components/ui/button.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 { 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":";;;;;;;;AAEA,MAAM,iBAAiB,IACrB,+bACA;CACE,iBAAiB;EACf,MAAM;EACN,SAAS;CACX;CACA,UAAU;EACR,MAAM;GACJ,SAAS;GACT,MAAM;GACN,WAAW;GACX,WAAW;GACX,WAAW;GACX,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EACA,SAAS;GACP,SAAS;GACT,aACE;GACF,OAAO;GACP,MAAM;GACN,SACE;GACF,WAAW;EACb;CACF;AACF,CACF;;;;ACnBA,MAAM,UAAU,EAAE,WAAW,OAAO,WAAW,UAAU,WAAW,GAAG,YAAyB;CAC9F,OACE,oBAACA,UAAD;EACE,WAAW,GAAG,eAAe;GAAE;GAAW;GAAM;EAAQ,CAAC,CAAC;EAC1D,aAAW;EACX,aAAU;EACV,gBAAc;EACd,GAAI;CACL;AAEL;;;;AChBA,MAAM,uBAAqC;CACzC,IAAI,OAAO,WAAW,aACpB,OAAO;CAET,MAAM,SAAS,OAAO,aAAa,QAAQ,OAAO;CAClD,IAAI,WAAW,WAAW,WAAW,QACnC,OAAO;CAET,OAAO;AACT;AAEA,MAAM,uCAAuB,IAAI,IAAgB;AAEjD,MAAM,wBAAwB,aAAyB;CACrD,IAAI,OAAO,WAAW,aACpB,aAAa,CAAC;CAEhB,qBAAqB,IAAI,QAAQ;CAEjC,OAAO,iBAAiB,WAAW,QAAQ;CAC3C,aAAa;EACX,qBAAqB,OAAO,QAAQ;EACpC,OAAO,oBAAoB,WAAW,QAAQ;CAChD;AACF;AAEA,MAAM,kBAAkB,SAAgB;CACtC,OAAO,aAAa,QAAQ,SAAS,IAAI;CACzC,KAAK,MAAM,YAAY,sBACrB,SAAS;AAEb;AAEA,MAAM,uBAAgC;CACpC,IAAI,OAAO,WAAW,aACpB,OAAO;CAET,OAAO,OAAO,WAAW,8BAA8B,CAAC,CAAC;AAC3D;AAEA,MAAM,wBAAwB,aAAyB;CACrD,IAAI,OAAO,WAAW,aACpB,aAAa,CAAC;CAEhB,MAAM,QAAQ,OAAO,WAAW,8BAA8B;CAC9D,MAAM,iBAAiB,UAAU,QAAQ;CACzC,aAAa;EACX,MAAM,oBAAoB,UAAU,QAAQ;CAC9C;AACF;AAMA,MAAM,eAAe,EAAE,YAAY,qBAA4B;CAM7D,MAAM,cAAc,qBAAqB,sBAAsB,sBAAsB,KAAK;CAE1F,MAAM,QADW,qBAAqB,sBAAsB,sBAAsB,IACtD,MAAM,cAAc,SAAS;CAEzD,MAAM,0BAA0B;EAC9B,MAAM,OAAc,UAAU,SAAS,UAAU;EACjD,SAAS,gBAAgB,UAAU,OAAO,QAAQ,SAAS,MAAM;EACjE,eAAe,IAAI;CACrB;CAEA,OACE,oBAAC,QAAD;EAAQ,cAAY;EAAW,SAAS;EAAmB,MAAK;EAAO,SAAQ;YAC5E,UAAU,SAAS,oBAAC,KAAD,CAAM,KAAI,oBAAC,MAAD,CAAO;CAC/B;AAEZ"}
|
package/dist/content/index.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
declare namespace index_d_exports {
|
|
3
|
-
export { Author, Post, Tag, authorSchema, notDraft, postSchema, tagSchema };
|
|
3
|
+
export { Author, Post, Tag, authorSchema, byPubDateDesc, notDraft, postSchema, tagSchema };
|
|
4
4
|
}
|
|
5
5
|
declare const postSchema: z.ZodObject<{
|
|
6
6
|
author: z.ZodOptional<z.ZodString>;
|
|
7
7
|
categories: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
8
8
|
cover: z.ZodOptional<z.ZodString>;
|
|
9
9
|
coverAlt: z.ZodOptional<z.ZodString>;
|
|
10
|
-
description: z.ZodString
|
|
10
|
+
description: z.ZodDefault<z.ZodString>;
|
|
11
11
|
draft: z.ZodDefault<z.ZodBoolean>;
|
|
12
12
|
heroImageHeight: z.ZodOptional<z.ZodNumber>;
|
|
13
13
|
heroImageUrl: z.ZodOptional<z.ZodURL>;
|
|
@@ -42,6 +42,15 @@ declare const notDraft: (post: {
|
|
|
42
42
|
status?: string;
|
|
43
43
|
};
|
|
44
44
|
}) => boolean;
|
|
45
|
+
declare const byPubDateDesc: (a: {
|
|
46
|
+
data: {
|
|
47
|
+
pubDate: Date;
|
|
48
|
+
};
|
|
49
|
+
}, b: {
|
|
50
|
+
data: {
|
|
51
|
+
pubDate: Date;
|
|
52
|
+
};
|
|
53
|
+
}) => number;
|
|
45
54
|
//#endregion
|
|
46
|
-
export { type Author, type Post, type Tag, authorSchema, notDraft, postSchema, index_d_exports as t, tagSchema };
|
|
55
|
+
export { type Author, type Post, type Tag, authorSchema, byPubDateDesc, notDraft, postSchema, index_d_exports as t, tagSchema };
|
|
47
56
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/content/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { z } from "zod";
|
|
|
4
4
|
//#region src/content/index.ts
|
|
5
5
|
var content_exports = /* @__PURE__ */ __exportAll({
|
|
6
6
|
authorSchema: () => authorSchema,
|
|
7
|
+
byPubDateDesc: () => byPubDateDesc,
|
|
7
8
|
notDraft: () => notDraft,
|
|
8
9
|
postSchema: () => postSchema,
|
|
9
10
|
tagSchema: () => tagSchema
|
|
@@ -13,7 +14,7 @@ const postSchema = z.object({
|
|
|
13
14
|
categories: z.array(z.string()).default([]),
|
|
14
15
|
cover: z.string().optional(),
|
|
15
16
|
coverAlt: z.string().optional(),
|
|
16
|
-
description: z.string().
|
|
17
|
+
description: z.string().default(""),
|
|
17
18
|
draft: z.boolean().default(false),
|
|
18
19
|
heroImageHeight: z.number().int().optional(),
|
|
19
20
|
heroImageUrl: z.url().optional(),
|
|
@@ -38,7 +39,8 @@ const authorSchema = z.object({
|
|
|
38
39
|
url: z.url().optional()
|
|
39
40
|
});
|
|
40
41
|
const notDraft = (post) => post.data.status !== "DRAFT" && post.data.draft !== true;
|
|
42
|
+
const byPubDateDesc = (a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf();
|
|
41
43
|
|
|
42
44
|
//#endregion
|
|
43
|
-
export { authorSchema, notDraft, postSchema, content_exports as t, tagSchema };
|
|
45
|
+
export { authorSchema, byPubDateDesc, notDraft, postSchema, content_exports as t, tagSchema };
|
|
44
46
|
//# 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 categories: z.array(z.string()).default([]),\n cover: z.string().optional(),\n coverAlt: z.string().optional(),\n description: z.string().
|
|
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().default(\"\"),\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\n// Typed structurally instead of against `CollectionEntry` so this module never\n// imports `astro:content`: that is a virtual module tsdown cannot resolve.\nconst byPubDateDesc = (a: { data: { pubDate: Date } }, b: { data: { pubDate: Date } }): number =>\n b.data.pubDate.valueOf() - a.data.pubDate.valueOf();\n\nexport { authorSchema, byPubDateDesc, 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,QAAQ,EAAE;CAClC,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;AAItD,MAAM,iBAAiB,GAAgC,MACrD,EAAE,KAAK,QAAQ,QAAQ,IAAI,EAAE,KAAK,QAAQ,QAAQ"}
|
|
@@ -37,6 +37,9 @@ declare const readingTime: (input: string) => {
|
|
|
37
37
|
words: number;
|
|
38
38
|
};
|
|
39
39
|
//#endregion
|
|
40
|
+
//#region src/lib/require-env.d.ts
|
|
41
|
+
declare const requireEnv: (name: string) => string;
|
|
42
|
+
//#endregion
|
|
40
43
|
//#region src/lib/slugify.d.ts
|
|
41
44
|
declare const slugify: (input: string) => string;
|
|
42
45
|
//#endregion
|
|
@@ -44,8 +47,8 @@ declare const slugify: (input: string) => string;
|
|
|
44
47
|
declare const DEFAULT_TAG_BASE = "/tag";
|
|
45
48
|
declare const tagUrl: (tag: string, base?: string) => string;
|
|
46
49
|
declare namespace index_d_exports {
|
|
47
|
-
export { DEFAULT_TAG_BASE, PostLike, PostParams, PostUrlBuilder, cn, createPostUrl, formatDate, getRelatedPosts, readingTime, slugify, tagUrl };
|
|
50
|
+
export { DEFAULT_TAG_BASE, PostLike, PostParams, PostUrlBuilder, cn, createPostUrl, formatDate, getRelatedPosts, readingTime, requireEnv, slugify, tagUrl };
|
|
48
51
|
}
|
|
49
52
|
//#endregion
|
|
50
|
-
export {
|
|
51
|
-
//# sourceMappingURL=index-
|
|
53
|
+
export { requireEnv as a, PostParams as c, getRelatedPosts as d, formatDate as f, slugify as i, PostUrlBuilder as l, DEFAULT_TAG_BASE as n, readingTime as o, cn as p, tagUrl as r, PostLike as s, index_d_exports as t, createPostUrl as u };
|
|
54
|
+
//# sourceMappingURL=index-BspVzulR.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-
|
|
2
|
+
import { t as index_d_exports$1 } from "./index-BspVzulR.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
package/dist/lib/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { DEFAULT_TAG_BASE, type PostLike, type PostParams, type PostUrlBuilder, cn, createPostUrl, formatDate, getRelatedPosts, readingTime, slugify, tagUrl };
|
|
1
|
+
import { a as requireEnv, c as PostParams, d as getRelatedPosts, f as formatDate, i as slugify, l as PostUrlBuilder, n as DEFAULT_TAG_BASE, o as readingTime, p as cn, r as tagUrl, s as PostLike, u as createPostUrl } from "../index-BspVzulR.js";
|
|
2
|
+
export { DEFAULT_TAG_BASE, type PostLike, type PostParams, type PostUrlBuilder, cn, createPostUrl, formatDate, getRelatedPosts, readingTime, requireEnv, slugify, tagUrl };
|
package/dist/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { t as cn } from "../cn-DmzdyV07.js";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as readingTime, c as getRelatedPosts, i as requireEnv, l as formatDate, n as DEFAULT_TAG_BASE, o as createPostUrl, r as tagUrl, s as slugify } from "../lib-V-jasC53.js";
|
|
3
3
|
|
|
4
|
-
export { DEFAULT_TAG_BASE, cn, createPostUrl, formatDate, getRelatedPosts, readingTime, slugify, tagUrl };
|
|
4
|
+
export { DEFAULT_TAG_BASE, cn, createPostUrl, formatDate, getRelatedPosts, readingTime, requireEnv, slugify, tagUrl };
|
|
@@ -89,6 +89,14 @@ const readingTime = (input) => {
|
|
|
89
89
|
};
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/lib/require-env.ts
|
|
94
|
+
const requireEnv = (name) => {
|
|
95
|
+
const value = process.env[name];
|
|
96
|
+
if (value === void 0 || value === "") throw new Error(`Missing required env var: ${name}`);
|
|
97
|
+
return value;
|
|
98
|
+
};
|
|
99
|
+
|
|
92
100
|
//#endregion
|
|
93
101
|
//#region src/lib/tag-url.ts
|
|
94
102
|
const DEFAULT_TAG_BASE = "/tag";
|
|
@@ -103,10 +111,11 @@ var lib_exports = /* @__PURE__ */ __exportAll({
|
|
|
103
111
|
formatDate: () => formatDate,
|
|
104
112
|
getRelatedPosts: () => getRelatedPosts,
|
|
105
113
|
readingTime: () => readingTime,
|
|
114
|
+
requireEnv: () => requireEnv,
|
|
106
115
|
slugify: () => slugify,
|
|
107
116
|
tagUrl: () => tagUrl
|
|
108
117
|
});
|
|
109
118
|
|
|
110
119
|
//#endregion
|
|
111
|
-
export {
|
|
112
|
-
//# sourceMappingURL=lib-
|
|
120
|
+
export { readingTime as a, getRelatedPosts as c, requireEnv as i, formatDate as l, DEFAULT_TAG_BASE as n, createPostUrl as o, tagUrl as r, slugify as s, lib_exports as t };
|
|
121
|
+
//# sourceMappingURL=lib-V-jasC53.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lib-V-jasC53.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/require-env.ts","../src/lib/tag-url.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","// An unset shell variable reaches a CI build as \"\" rather than undefined, so an\n// empty value has to count as missing or the failure surfaces much later, inside\n// the content loader's HTTP call.\nconst requireEnv = (name: string): string => {\n const value = process.env[name];\n if (value === undefined || value === \"\") {\n throw new Error(`Missing required env var: ${name}`);\n }\n return value;\n};\n\nexport { requireEnv };\n","import { slugify } from \"./slugify\";\n\nconst DEFAULT_TAG_BASE = \"/tag\";\n\n// The trailing slash is not cosmetic. @astrojs/sitemap emits directory-style\n// URLs, so a chip pointing at /tag/foo names a URL that is absent from the\n// sitemap and only reachable through a redirect.\nconst tagUrl = (tag: string, base: string = DEFAULT_TAG_BASE): string =>\n `${base.replace(/\\/+$/v, \"\")}/${slugify(tag)}/`;\n\nexport { DEFAULT_TAG_BASE, tagUrl };\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 { requireEnv } from \"./require-env\";\nexport { slugify } from \"./slugify\";\nexport { DEFAULT_TAG_BASE, tagUrl } from \"./tag-url\";\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-awesomeness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Astro 6/7 blog theme with shadcn primitives and a Tailwind v4 preset.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@base-ui/react": "^1.6.0",
|
|
56
56
|
"class-variance-authority": "^0.7.1",
|
|
57
|
-
"cmdk": "^1.1.1",
|
|
58
57
|
"cnfast": "^0.0.8",
|
|
59
58
|
"lucide-react": "^1.24.0",
|
|
60
59
|
"tw-animate-css": "^1.4.0",
|
|
@@ -78,6 +77,14 @@
|
|
|
78
77
|
"react-dom": "^19.0.0",
|
|
79
78
|
"tailwindcss": "^4.0.0"
|
|
80
79
|
},
|
|
80
|
+
"peerDependenciesMeta": {
|
|
81
|
+
"react": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
84
|
+
"react-dom": {
|
|
85
|
+
"optional": true
|
|
86
|
+
}
|
|
87
|
+
},
|
|
81
88
|
"scripts": {
|
|
82
89
|
"dev": "tsdown --watch",
|
|
83
90
|
"build": "tsdown",
|
package/src/components/index.ts
CHANGED
|
@@ -1,23 +1,3 @@
|
|
|
1
1
|
export { Button } from "./ui/button";
|
|
2
2
|
export { buttonVariants } from "./ui/button-variants";
|
|
3
|
-
export {
|
|
4
|
-
Command,
|
|
5
|
-
CommandEmpty,
|
|
6
|
-
CommandGroup,
|
|
7
|
-
CommandInput,
|
|
8
|
-
CommandItem,
|
|
9
|
-
CommandList,
|
|
10
|
-
CommandSeparator,
|
|
11
|
-
CommandShortcut,
|
|
12
|
-
} from "./ui/command";
|
|
13
|
-
export {
|
|
14
|
-
Dialog,
|
|
15
|
-
DialogContent,
|
|
16
|
-
DialogDescription,
|
|
17
|
-
DialogFooter,
|
|
18
|
-
DialogHeader,
|
|
19
|
-
DialogTitle,
|
|
20
|
-
DialogTrigger,
|
|
21
|
-
} from "./ui/dialog";
|
|
22
|
-
export { CommandMenu, type CommandMenuItem, type CommandMenuProps } from "./command-menu";
|
|
23
3
|
export { ThemeToggle } from "./theme-toggle";
|