@vibecms/core 0.1.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/engine/index.d.mts +4 -0
- package/dist/engine/index.d.ts +4 -0
- package/dist/engine/index.js +1226 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/index.mjs +1184 -0
- package/dist/engine/index.mjs.map +1 -0
- package/dist/hooks/index.d.mts +27 -0
- package/dist/hooks/index.d.ts +27 -0
- package/dist/hooks/index.js +75 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/index.mjs +47 -0
- package/dist/hooks/index.mjs.map +1 -0
- package/dist/index-C3P1J_of.d.ts +339 -0
- package/dist/index-CO4uwTdH.d.mts +339 -0
- package/dist/index.css +2326 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +412 -0
- package/dist/index.d.ts +412 -0
- package/dist/index.js +3715 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3587 -0
- package/dist/index.mjs.map +1 -0
- package/dist/storage/index.d.mts +32 -0
- package/dist/storage/index.d.ts +32 -0
- package/dist/storage/index.js +103 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/index.mjs +75 -0
- package/dist/storage/index.mjs.map +1 -0
- package/dist/types-BkDsDmQJ.d.mts +83 -0
- package/dist/types-BkDsDmQJ.d.ts +83 -0
- package/dist/ui/index.d.mts +127 -0
- package/dist/ui/index.d.ts +127 -0
- package/dist/ui/index.js +1119 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/index.mjs +1030 -0
- package/dist/ui/index.mjs.map +1 -0
- package/dist/vite-plugin.d.mts +8 -0
- package/dist/vite-plugin.d.ts +8 -0
- package/dist/vite-plugin.js +193 -0
- package/dist/vite-plugin.js.map +1 -0
- package/dist/vite-plugin.mjs +165 -0
- package/dist/vite-plugin.mjs.map +1 -0
- package/package.json +89 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3715 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
AuthorCard: () => AuthorCard,
|
|
34
|
+
BrowserEngine: () => BrowserEngine,
|
|
35
|
+
Button: () => Button,
|
|
36
|
+
Command: () => Command,
|
|
37
|
+
CommandDialog: () => CommandDialog,
|
|
38
|
+
CommandEmpty: () => CommandEmpty,
|
|
39
|
+
CommandGroup: () => CommandGroup,
|
|
40
|
+
CommandInput: () => CommandInput,
|
|
41
|
+
CommandItem: () => CommandItem,
|
|
42
|
+
CommandList: () => CommandList,
|
|
43
|
+
CommandSeparator: () => CommandSeparator,
|
|
44
|
+
CommandShortcut: () => CommandShortcut,
|
|
45
|
+
DevEngine: () => DevEngine,
|
|
46
|
+
Dialog: () => Dialog,
|
|
47
|
+
DialogClose: () => DialogClose,
|
|
48
|
+
DialogContent: () => DialogContent,
|
|
49
|
+
DialogDescription: () => DialogDescription,
|
|
50
|
+
DialogFooter: () => DialogFooter,
|
|
51
|
+
DialogHeader: () => DialogHeader,
|
|
52
|
+
DialogOverlay: () => DialogOverlay,
|
|
53
|
+
DialogPortal: () => DialogPortal,
|
|
54
|
+
DialogTitle: () => DialogTitle,
|
|
55
|
+
DialogTrigger: () => DialogTrigger,
|
|
56
|
+
GitStorageDriver: () => GitStorageDriver,
|
|
57
|
+
Input: () => Input,
|
|
58
|
+
InputGroup: () => InputGroup,
|
|
59
|
+
InputGroupAddon: () => InputGroupAddon,
|
|
60
|
+
InputGroupButton: () => InputGroupButton,
|
|
61
|
+
InputGroupInput: () => InputGroupInput,
|
|
62
|
+
InputGroupText: () => InputGroupText,
|
|
63
|
+
InputGroupTextarea: () => InputGroupTextarea,
|
|
64
|
+
Label: () => Label,
|
|
65
|
+
NodeEngine: () => NodeEngine,
|
|
66
|
+
Popover: () => Popover,
|
|
67
|
+
PopoverContent: () => PopoverContent,
|
|
68
|
+
PopoverDescription: () => PopoverDescription,
|
|
69
|
+
PopoverHeader: () => PopoverHeader,
|
|
70
|
+
PopoverTitle: () => PopoverTitle,
|
|
71
|
+
PopoverTrigger: () => PopoverTrigger,
|
|
72
|
+
ScrollArea: () => ScrollArea,
|
|
73
|
+
ScrollBar: () => ScrollBar,
|
|
74
|
+
SeoHead: () => SeoHead,
|
|
75
|
+
Sheet: () => Sheet,
|
|
76
|
+
SheetClose: () => SheetClose,
|
|
77
|
+
SheetContent: () => SheetContent,
|
|
78
|
+
SheetDescription: () => SheetDescription,
|
|
79
|
+
SheetFooter: () => SheetFooter,
|
|
80
|
+
SheetHeader: () => SheetHeader,
|
|
81
|
+
SheetTitle: () => SheetTitle,
|
|
82
|
+
SheetTrigger: () => SheetTrigger,
|
|
83
|
+
Switch: () => Switch,
|
|
84
|
+
Textarea: () => Textarea,
|
|
85
|
+
Tooltip: () => Tooltip,
|
|
86
|
+
TooltipContent: () => TooltipContent,
|
|
87
|
+
TooltipProvider: () => TooltipProvider,
|
|
88
|
+
TooltipTrigger: () => TooltipTrigger,
|
|
89
|
+
VercelBlobDriver: () => VercelBlobDriver,
|
|
90
|
+
VibeEngine: () => VibeEngine,
|
|
91
|
+
VibeImage: () => VibeImage,
|
|
92
|
+
VibeOverlay: () => VibeOverlay,
|
|
93
|
+
VibeRichText: () => VibeRichText,
|
|
94
|
+
VibeText: () => VibeText,
|
|
95
|
+
assertValidCollection: () => assertValidCollection,
|
|
96
|
+
assertValidCommitOid: () => assertValidCommitOid,
|
|
97
|
+
assertValidSlug: () => assertValidSlug,
|
|
98
|
+
blockSchema: () => blockSchema,
|
|
99
|
+
buttonVariants: () => buttonVariants,
|
|
100
|
+
canEditCollection: () => canEditCollection,
|
|
101
|
+
ctaBannerBlockSchema: () => ctaBannerBlockSchema,
|
|
102
|
+
defineConfig: () => defineConfig,
|
|
103
|
+
faqBlockSchema: () => faqBlockSchema,
|
|
104
|
+
featuresBlockSchema: () => featuresBlockSchema,
|
|
105
|
+
formBlockSchema: () => formBlockSchema,
|
|
106
|
+
formFieldSchema: () => formFieldSchema,
|
|
107
|
+
getCollectionSchema: () => getCollectionSchema,
|
|
108
|
+
getConditionalLogic: () => getConditionalLogic,
|
|
109
|
+
getCustomBlockComponent: () => getCustomBlockComponent,
|
|
110
|
+
getReferenceCollection: () => getReferenceCollection,
|
|
111
|
+
getVibeUIType: () => getVibeUIType,
|
|
112
|
+
gridBlockSchema: () => gridBlockSchema,
|
|
113
|
+
heroBlockSchema: () => heroBlockSchema,
|
|
114
|
+
initVibeEngine: () => initVibeEngine,
|
|
115
|
+
logoCloudBlockSchema: () => logoCloudBlockSchema,
|
|
116
|
+
mediaGallerySchema: () => mediaGallerySchema,
|
|
117
|
+
populate: () => populate,
|
|
118
|
+
pricingBlockSchema: () => pricingBlockSchema,
|
|
119
|
+
processScheduledPublications: () => processScheduledPublications,
|
|
120
|
+
publishingSchema: () => publishingSchema,
|
|
121
|
+
registerCollectionSchemas: () => registerCollectionSchemas,
|
|
122
|
+
registerCustomBlocks: () => registerCustomBlocks,
|
|
123
|
+
resolveUserRole: () => resolveUserRole,
|
|
124
|
+
sanitizeRedirectPath: () => sanitizeRedirectPath,
|
|
125
|
+
seoSchema: () => seoSchema,
|
|
126
|
+
tabsBlockSchema: () => tabsBlockSchema,
|
|
127
|
+
testimonialsBlockSchema: () => testimonialsBlockSchema,
|
|
128
|
+
useVibeContent: () => useVibeContent,
|
|
129
|
+
useVibeDocument: () => useVibeDocument,
|
|
130
|
+
useVibeOverlay: () => useVibeOverlay,
|
|
131
|
+
validateCollection: () => validateCollection,
|
|
132
|
+
validateCollectionData: () => validateCollectionData,
|
|
133
|
+
validateCommitOid: () => validateCommitOid,
|
|
134
|
+
validateSlug: () => validateSlug,
|
|
135
|
+
vibeConditional: () => vibeConditional,
|
|
136
|
+
vibeEvents: () => vibeEvents,
|
|
137
|
+
vibeField: () => vibeField,
|
|
138
|
+
videoBlockSchema: () => videoBlockSchema
|
|
139
|
+
});
|
|
140
|
+
module.exports = __toCommonJS(index_exports);
|
|
141
|
+
|
|
142
|
+
// src/ui/button.tsx
|
|
143
|
+
var React = __toESM(require("react"));
|
|
144
|
+
var import_button = require("@base-ui/react/button");
|
|
145
|
+
var import_class_variance_authority = require("class-variance-authority");
|
|
146
|
+
|
|
147
|
+
// src/lib/utils.ts
|
|
148
|
+
var import_clsx = require("clsx");
|
|
149
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
150
|
+
function cn(...inputs) {
|
|
151
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/ui/button.tsx
|
|
155
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
156
|
+
var buttonVariants = (0, import_class_variance_authority.cva)(
|
|
157
|
+
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
158
|
+
{
|
|
159
|
+
variants: {
|
|
160
|
+
variant: {
|
|
161
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
162
|
+
outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
163
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
164
|
+
ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
|
165
|
+
destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
|
|
166
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
167
|
+
},
|
|
168
|
+
size: {
|
|
169
|
+
default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
170
|
+
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
171
|
+
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
172
|
+
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
|
|
173
|
+
icon: "size-8",
|
|
174
|
+
"icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
175
|
+
"icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
176
|
+
"icon-lg": "size-9"
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
defaultVariants: {
|
|
180
|
+
variant: "default",
|
|
181
|
+
size: "default"
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
var Button = React.forwardRef(({ className, variant, size, ...props }, ref) => {
|
|
186
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
187
|
+
import_button.Button,
|
|
188
|
+
{
|
|
189
|
+
ref,
|
|
190
|
+
"data-slot": "button",
|
|
191
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
192
|
+
...props
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
Button.displayName = "Button";
|
|
197
|
+
|
|
198
|
+
// src/ui/command.tsx
|
|
199
|
+
var import_cmdk = require("cmdk");
|
|
200
|
+
|
|
201
|
+
// src/ui/dialog.tsx
|
|
202
|
+
var React2 = __toESM(require("react"));
|
|
203
|
+
var import_dialog = require("@base-ui/react/dialog");
|
|
204
|
+
var import_lucide_react = require("lucide-react");
|
|
205
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
206
|
+
function Dialog({ ...props }) {
|
|
207
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dialog.Dialog.Root, { "data-slot": "dialog", ...props });
|
|
208
|
+
}
|
|
209
|
+
var DialogTrigger = React2.forwardRef(({ ...props }, ref) => {
|
|
210
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
211
|
+
import_dialog.Dialog.Trigger,
|
|
212
|
+
{
|
|
213
|
+
ref,
|
|
214
|
+
"data-slot": "dialog-trigger",
|
|
215
|
+
...props
|
|
216
|
+
}
|
|
217
|
+
);
|
|
218
|
+
});
|
|
219
|
+
DialogTrigger.displayName = "DialogTrigger";
|
|
220
|
+
function DialogPortal({ ...props }) {
|
|
221
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dialog.Dialog.Portal, { "data-slot": "dialog-portal", ...props });
|
|
222
|
+
}
|
|
223
|
+
var DialogClose = React2.forwardRef(({ ...props }, ref) => {
|
|
224
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
225
|
+
import_dialog.Dialog.Close,
|
|
226
|
+
{
|
|
227
|
+
ref,
|
|
228
|
+
"data-slot": "dialog-close",
|
|
229
|
+
...props
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
DialogClose.displayName = "DialogClose";
|
|
234
|
+
var DialogOverlay = React2.forwardRef(({ className, ...props }, ref) => {
|
|
235
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
236
|
+
import_dialog.Dialog.Backdrop,
|
|
237
|
+
{
|
|
238
|
+
ref,
|
|
239
|
+
"data-slot": "dialog-overlay",
|
|
240
|
+
className: cn(
|
|
241
|
+
"fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
|
|
242
|
+
className
|
|
243
|
+
),
|
|
244
|
+
...props
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
DialogOverlay.displayName = "DialogOverlay";
|
|
249
|
+
function DialogContent({
|
|
250
|
+
className,
|
|
251
|
+
children,
|
|
252
|
+
showCloseButton = true,
|
|
253
|
+
...props
|
|
254
|
+
}) {
|
|
255
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(DialogPortal, { children: [
|
|
256
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DialogOverlay, {}),
|
|
257
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
258
|
+
import_dialog.Dialog.Popup,
|
|
259
|
+
{
|
|
260
|
+
"data-slot": "dialog-content",
|
|
261
|
+
className: cn(
|
|
262
|
+
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-white p-4 text-sm text-neutral-900 ring-1 ring-neutral-200 shadow-xl duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
263
|
+
className
|
|
264
|
+
),
|
|
265
|
+
...props,
|
|
266
|
+
children: [
|
|
267
|
+
children,
|
|
268
|
+
showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
269
|
+
import_dialog.Dialog.Close,
|
|
270
|
+
{
|
|
271
|
+
"data-slot": "dialog-close",
|
|
272
|
+
render: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
273
|
+
Button,
|
|
274
|
+
{
|
|
275
|
+
variant: "ghost",
|
|
276
|
+
className: "absolute top-2 right-2",
|
|
277
|
+
size: "icon-sm"
|
|
278
|
+
}
|
|
279
|
+
),
|
|
280
|
+
children: [
|
|
281
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
282
|
+
import_lucide_react.XIcon,
|
|
283
|
+
{}
|
|
284
|
+
),
|
|
285
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "sr-only", children: "Close" })
|
|
286
|
+
]
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
]
|
|
290
|
+
}
|
|
291
|
+
)
|
|
292
|
+
] });
|
|
293
|
+
}
|
|
294
|
+
function DialogHeader({ className, ...props }) {
|
|
295
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
296
|
+
"div",
|
|
297
|
+
{
|
|
298
|
+
"data-slot": "dialog-header",
|
|
299
|
+
className: cn("flex flex-col gap-2", className),
|
|
300
|
+
...props
|
|
301
|
+
}
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
function DialogFooter({
|
|
305
|
+
className,
|
|
306
|
+
showCloseButton = false,
|
|
307
|
+
children,
|
|
308
|
+
...props
|
|
309
|
+
}) {
|
|
310
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
311
|
+
"div",
|
|
312
|
+
{
|
|
313
|
+
"data-slot": "dialog-footer",
|
|
314
|
+
className: cn(
|
|
315
|
+
"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end",
|
|
316
|
+
className
|
|
317
|
+
),
|
|
318
|
+
...props,
|
|
319
|
+
children: [
|
|
320
|
+
children,
|
|
321
|
+
showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dialog.Dialog.Close, { render: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { variant: "outline" }), children: "Close" })
|
|
322
|
+
]
|
|
323
|
+
}
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
function DialogTitle({ className, ...props }) {
|
|
327
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
328
|
+
import_dialog.Dialog.Title,
|
|
329
|
+
{
|
|
330
|
+
"data-slot": "dialog-title",
|
|
331
|
+
className: cn(
|
|
332
|
+
"font-heading text-base leading-none font-medium",
|
|
333
|
+
className
|
|
334
|
+
),
|
|
335
|
+
...props
|
|
336
|
+
}
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
function DialogDescription({
|
|
340
|
+
className,
|
|
341
|
+
...props
|
|
342
|
+
}) {
|
|
343
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
344
|
+
import_dialog.Dialog.Description,
|
|
345
|
+
{
|
|
346
|
+
"data-slot": "dialog-description",
|
|
347
|
+
className: cn(
|
|
348
|
+
"text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
|
|
349
|
+
className
|
|
350
|
+
),
|
|
351
|
+
...props
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// src/ui/input-group.tsx
|
|
357
|
+
var React5 = __toESM(require("react"));
|
|
358
|
+
var import_class_variance_authority2 = require("class-variance-authority");
|
|
359
|
+
|
|
360
|
+
// src/ui/input.tsx
|
|
361
|
+
var React3 = __toESM(require("react"));
|
|
362
|
+
var import_input = require("@base-ui/react/input");
|
|
363
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
364
|
+
var Input = React3.forwardRef(
|
|
365
|
+
({ className, type, ...props }, ref) => {
|
|
366
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
367
|
+
import_input.Input,
|
|
368
|
+
{
|
|
369
|
+
ref,
|
|
370
|
+
type,
|
|
371
|
+
"data-slot": "input",
|
|
372
|
+
className: cn(
|
|
373
|
+
"h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
|
|
374
|
+
className
|
|
375
|
+
),
|
|
376
|
+
...props
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
Input.displayName = "Input";
|
|
382
|
+
|
|
383
|
+
// src/ui/textarea.tsx
|
|
384
|
+
var React4 = __toESM(require("react"));
|
|
385
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
386
|
+
var Textarea = React4.forwardRef(({ className, ...props }, ref) => {
|
|
387
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
388
|
+
"textarea",
|
|
389
|
+
{
|
|
390
|
+
ref,
|
|
391
|
+
"data-slot": "textarea",
|
|
392
|
+
className: cn(
|
|
393
|
+
"flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
|
|
394
|
+
className
|
|
395
|
+
),
|
|
396
|
+
...props
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
});
|
|
400
|
+
Textarea.displayName = "Textarea";
|
|
401
|
+
|
|
402
|
+
// src/ui/input-group.tsx
|
|
403
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
404
|
+
var InputGroup = React5.forwardRef(
|
|
405
|
+
({ className, ...props }, ref) => {
|
|
406
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
407
|
+
"div",
|
|
408
|
+
{
|
|
409
|
+
ref,
|
|
410
|
+
"data-slot": "input-group",
|
|
411
|
+
role: "group",
|
|
412
|
+
className: cn(
|
|
413
|
+
"group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
|
|
414
|
+
className
|
|
415
|
+
),
|
|
416
|
+
...props
|
|
417
|
+
}
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
);
|
|
421
|
+
InputGroup.displayName = "InputGroup";
|
|
422
|
+
var inputGroupAddonVariants = (0, import_class_variance_authority2.cva)(
|
|
423
|
+
"flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
|
|
424
|
+
{
|
|
425
|
+
variants: {
|
|
426
|
+
align: {
|
|
427
|
+
"inline-start": "order-first pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem]",
|
|
428
|
+
"inline-end": "order-last pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem]",
|
|
429
|
+
"block-start": "order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2",
|
|
430
|
+
"block-end": "order-last w-full justify-start px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2"
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
defaultVariants: {
|
|
434
|
+
align: "inline-start"
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
);
|
|
438
|
+
var InputGroupAddon = React5.forwardRef(({ className, align = "inline-start", ...props }, ref) => {
|
|
439
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
440
|
+
"div",
|
|
441
|
+
{
|
|
442
|
+
ref,
|
|
443
|
+
role: "group",
|
|
444
|
+
"data-slot": "input-group-addon",
|
|
445
|
+
"data-align": align,
|
|
446
|
+
className: cn(inputGroupAddonVariants({ align }), className),
|
|
447
|
+
onClick: (e) => {
|
|
448
|
+
if (e.target.closest("button")) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
e.currentTarget.parentElement?.querySelector("input")?.focus();
|
|
452
|
+
},
|
|
453
|
+
...props
|
|
454
|
+
}
|
|
455
|
+
);
|
|
456
|
+
});
|
|
457
|
+
InputGroupAddon.displayName = "InputGroupAddon";
|
|
458
|
+
var inputGroupButtonVariants = (0, import_class_variance_authority2.cva)(
|
|
459
|
+
"flex items-center gap-2 text-sm shadow-none",
|
|
460
|
+
{
|
|
461
|
+
variants: {
|
|
462
|
+
size: {
|
|
463
|
+
xs: "h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
|
|
464
|
+
sm: "",
|
|
465
|
+
"icon-xs": "size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0",
|
|
466
|
+
"icon-sm": "size-8 p-0 has-[>svg]:p-0"
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
defaultVariants: {
|
|
470
|
+
size: "xs"
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
);
|
|
474
|
+
function InputGroupButton({
|
|
475
|
+
className,
|
|
476
|
+
type = "button",
|
|
477
|
+
variant = "ghost",
|
|
478
|
+
size = "xs",
|
|
479
|
+
...props
|
|
480
|
+
}) {
|
|
481
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
482
|
+
Button,
|
|
483
|
+
{
|
|
484
|
+
type,
|
|
485
|
+
"data-size": size,
|
|
486
|
+
variant,
|
|
487
|
+
className: cn(inputGroupButtonVariants({ size }), className),
|
|
488
|
+
...props
|
|
489
|
+
}
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
function InputGroupText({ className, ...props }) {
|
|
493
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
494
|
+
"span",
|
|
495
|
+
{
|
|
496
|
+
className: cn(
|
|
497
|
+
"flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
498
|
+
className
|
|
499
|
+
),
|
|
500
|
+
...props
|
|
501
|
+
}
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
function InputGroupInput({
|
|
505
|
+
className,
|
|
506
|
+
...props
|
|
507
|
+
}) {
|
|
508
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
509
|
+
Input,
|
|
510
|
+
{
|
|
511
|
+
"data-slot": "input-group-control",
|
|
512
|
+
className: cn(
|
|
513
|
+
"flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
|
|
514
|
+
className
|
|
515
|
+
),
|
|
516
|
+
...props
|
|
517
|
+
}
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
function InputGroupTextarea({
|
|
521
|
+
className,
|
|
522
|
+
...props
|
|
523
|
+
}) {
|
|
524
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
525
|
+
Textarea,
|
|
526
|
+
{
|
|
527
|
+
"data-slot": "input-group-control",
|
|
528
|
+
className: cn(
|
|
529
|
+
"flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
|
|
530
|
+
className
|
|
531
|
+
),
|
|
532
|
+
...props
|
|
533
|
+
}
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// src/ui/command.tsx
|
|
538
|
+
var import_lucide_react2 = require("lucide-react");
|
|
539
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
540
|
+
function Command({
|
|
541
|
+
className,
|
|
542
|
+
...props
|
|
543
|
+
}) {
|
|
544
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
545
|
+
import_cmdk.Command,
|
|
546
|
+
{
|
|
547
|
+
"data-slot": "command",
|
|
548
|
+
className: cn(
|
|
549
|
+
"flex size-full flex-col overflow-hidden rounded-xl! bg-white p-1 text-neutral-900",
|
|
550
|
+
className
|
|
551
|
+
),
|
|
552
|
+
...props
|
|
553
|
+
}
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
function CommandDialog({
|
|
557
|
+
title = "Command Palette",
|
|
558
|
+
description = "Search for a command to run...",
|
|
559
|
+
children,
|
|
560
|
+
className,
|
|
561
|
+
showCloseButton = false,
|
|
562
|
+
...props
|
|
563
|
+
}) {
|
|
564
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Dialog, { ...props, children: [
|
|
565
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(DialogHeader, { className: "sr-only", children: [
|
|
566
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DialogTitle, { children: title }),
|
|
567
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DialogDescription, { children: description })
|
|
568
|
+
] }),
|
|
569
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
570
|
+
DialogContent,
|
|
571
|
+
{
|
|
572
|
+
className: cn(
|
|
573
|
+
"top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0 shadow-2xl border border-neutral-200 bg-white",
|
|
574
|
+
className
|
|
575
|
+
),
|
|
576
|
+
showCloseButton,
|
|
577
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Command, { className: "[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-neutral-500 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 border-none", children })
|
|
578
|
+
}
|
|
579
|
+
)
|
|
580
|
+
] });
|
|
581
|
+
}
|
|
582
|
+
function CommandInput({
|
|
583
|
+
className,
|
|
584
|
+
...props
|
|
585
|
+
}) {
|
|
586
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { "data-slot": "command-input-wrapper", className: "p-1 pb-0", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(InputGroup, { className: "h-8! rounded-lg! border-input/30 bg-input/30 shadow-none! *:data-[slot=input-group-addon]:pl-2!", children: [
|
|
587
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
588
|
+
import_cmdk.Command.Input,
|
|
589
|
+
{
|
|
590
|
+
"data-slot": "command-input",
|
|
591
|
+
className: cn(
|
|
592
|
+
"w-full text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
|
|
593
|
+
className
|
|
594
|
+
),
|
|
595
|
+
...props
|
|
596
|
+
}
|
|
597
|
+
),
|
|
598
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(InputGroupAddon, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.SearchIcon, { className: "size-4 shrink-0 opacity-50" }) })
|
|
599
|
+
] }) });
|
|
600
|
+
}
|
|
601
|
+
function CommandList({
|
|
602
|
+
className,
|
|
603
|
+
...props
|
|
604
|
+
}) {
|
|
605
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
606
|
+
import_cmdk.Command.List,
|
|
607
|
+
{
|
|
608
|
+
"data-slot": "command-list",
|
|
609
|
+
className: cn(
|
|
610
|
+
"no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none",
|
|
611
|
+
className
|
|
612
|
+
),
|
|
613
|
+
...props
|
|
614
|
+
}
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
function CommandEmpty({
|
|
618
|
+
className,
|
|
619
|
+
...props
|
|
620
|
+
}) {
|
|
621
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
622
|
+
import_cmdk.Command.Empty,
|
|
623
|
+
{
|
|
624
|
+
"data-slot": "command-empty",
|
|
625
|
+
className: cn("py-6 text-center text-sm", className),
|
|
626
|
+
...props
|
|
627
|
+
}
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
function CommandGroup({
|
|
631
|
+
className,
|
|
632
|
+
...props
|
|
633
|
+
}) {
|
|
634
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
635
|
+
import_cmdk.Command.Group,
|
|
636
|
+
{
|
|
637
|
+
"data-slot": "command-group",
|
|
638
|
+
className: cn(
|
|
639
|
+
"overflow-hidden p-1 text-neutral-900 **:[[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-neutral-500",
|
|
640
|
+
className
|
|
641
|
+
),
|
|
642
|
+
...props
|
|
643
|
+
}
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
function CommandSeparator({
|
|
647
|
+
className,
|
|
648
|
+
...props
|
|
649
|
+
}) {
|
|
650
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
651
|
+
import_cmdk.Command.Separator,
|
|
652
|
+
{
|
|
653
|
+
"data-slot": "command-separator",
|
|
654
|
+
className: cn("-mx-1 h-px bg-border", className),
|
|
655
|
+
...props
|
|
656
|
+
}
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
function CommandItem({
|
|
660
|
+
className,
|
|
661
|
+
children,
|
|
662
|
+
...props
|
|
663
|
+
}) {
|
|
664
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
665
|
+
import_cmdk.Command.Item,
|
|
666
|
+
{
|
|
667
|
+
"data-slot": "command-item",
|
|
668
|
+
className: cn(
|
|
669
|
+
"group/command-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none in-data-[slot=dialog-content]:rounded-lg! data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-selected:bg-neutral-100 data-selected:text-neutral-900 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-selected:*:[svg]:text-neutral-900",
|
|
670
|
+
className
|
|
671
|
+
),
|
|
672
|
+
...props,
|
|
673
|
+
children: [
|
|
674
|
+
children,
|
|
675
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.CheckIcon, { className: "ml-auto opacity-0 group-has-data-[slot=command-shortcut]/command-item:hidden group-data-[checked=true]/command-item:opacity-100" })
|
|
676
|
+
]
|
|
677
|
+
}
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
function CommandShortcut({
|
|
681
|
+
className,
|
|
682
|
+
...props
|
|
683
|
+
}) {
|
|
684
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
685
|
+
"span",
|
|
686
|
+
{
|
|
687
|
+
"data-slot": "command-shortcut",
|
|
688
|
+
className: cn(
|
|
689
|
+
"ml-auto text-xs tracking-widest text-muted-foreground group-data-selected/command-item:text-foreground",
|
|
690
|
+
className
|
|
691
|
+
),
|
|
692
|
+
...props
|
|
693
|
+
}
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// src/ui/label.tsx
|
|
698
|
+
var React6 = __toESM(require("react"));
|
|
699
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
700
|
+
var Label = React6.forwardRef(
|
|
701
|
+
({ className, ...props }, ref) => {
|
|
702
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
703
|
+
"label",
|
|
704
|
+
{
|
|
705
|
+
ref,
|
|
706
|
+
"data-slot": "label",
|
|
707
|
+
className: cn(
|
|
708
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
709
|
+
className
|
|
710
|
+
),
|
|
711
|
+
...props
|
|
712
|
+
}
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
);
|
|
716
|
+
Label.displayName = "Label";
|
|
717
|
+
|
|
718
|
+
// src/ui/popover.tsx
|
|
719
|
+
var React7 = __toESM(require("react"));
|
|
720
|
+
var import_popover = require("@base-ui/react/popover");
|
|
721
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
722
|
+
function Popover({ ...props }) {
|
|
723
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_popover.Popover.Root, { "data-slot": "popover", ...props });
|
|
724
|
+
}
|
|
725
|
+
var PopoverTrigger = React7.forwardRef(({ ...props }, ref) => {
|
|
726
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
727
|
+
import_popover.Popover.Trigger,
|
|
728
|
+
{
|
|
729
|
+
ref,
|
|
730
|
+
"data-slot": "popover-trigger",
|
|
731
|
+
...props
|
|
732
|
+
}
|
|
733
|
+
);
|
|
734
|
+
});
|
|
735
|
+
PopoverTrigger.displayName = "PopoverTrigger";
|
|
736
|
+
var PopoverContent = React7.forwardRef(
|
|
737
|
+
({
|
|
738
|
+
className,
|
|
739
|
+
align = "center",
|
|
740
|
+
alignOffset = 0,
|
|
741
|
+
side = "bottom",
|
|
742
|
+
sideOffset = 4,
|
|
743
|
+
...props
|
|
744
|
+
}, ref) => {
|
|
745
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_popover.Popover.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
746
|
+
import_popover.Popover.Positioner,
|
|
747
|
+
{
|
|
748
|
+
align,
|
|
749
|
+
alignOffset,
|
|
750
|
+
side,
|
|
751
|
+
sideOffset,
|
|
752
|
+
className: "isolate z-50",
|
|
753
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
754
|
+
import_popover.Popover.Popup,
|
|
755
|
+
{
|
|
756
|
+
ref,
|
|
757
|
+
"data-slot": "popover-content",
|
|
758
|
+
className: cn(
|
|
759
|
+
"z-[9999] flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-lg bg-white border border-neutral-200 p-2.5 text-sm text-neutral-900 shadow-xl ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
760
|
+
className
|
|
761
|
+
),
|
|
762
|
+
...props
|
|
763
|
+
}
|
|
764
|
+
)
|
|
765
|
+
}
|
|
766
|
+
) });
|
|
767
|
+
}
|
|
768
|
+
);
|
|
769
|
+
PopoverContent.displayName = "PopoverContent";
|
|
770
|
+
var PopoverHeader = React7.forwardRef(({ className, ...props }, ref) => {
|
|
771
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
772
|
+
"div",
|
|
773
|
+
{
|
|
774
|
+
ref,
|
|
775
|
+
"data-slot": "popover-header",
|
|
776
|
+
className: cn("flex flex-col gap-0.5 text-sm", className),
|
|
777
|
+
...props
|
|
778
|
+
}
|
|
779
|
+
);
|
|
780
|
+
});
|
|
781
|
+
PopoverHeader.displayName = "PopoverHeader";
|
|
782
|
+
var PopoverTitle = React7.forwardRef(({ className, ...props }, ref) => {
|
|
783
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
784
|
+
import_popover.Popover.Title,
|
|
785
|
+
{
|
|
786
|
+
ref,
|
|
787
|
+
"data-slot": "popover-title",
|
|
788
|
+
className: cn("font-heading font-medium", className),
|
|
789
|
+
...props
|
|
790
|
+
}
|
|
791
|
+
);
|
|
792
|
+
});
|
|
793
|
+
PopoverTitle.displayName = "PopoverTitle";
|
|
794
|
+
var PopoverDescription = React7.forwardRef(({ className, ...props }, ref) => {
|
|
795
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
796
|
+
import_popover.Popover.Description,
|
|
797
|
+
{
|
|
798
|
+
ref,
|
|
799
|
+
"data-slot": "popover-description",
|
|
800
|
+
className: cn("text-muted-foreground", className),
|
|
801
|
+
...props
|
|
802
|
+
}
|
|
803
|
+
);
|
|
804
|
+
});
|
|
805
|
+
PopoverDescription.displayName = "PopoverDescription";
|
|
806
|
+
|
|
807
|
+
// src/ui/scroll-area.tsx
|
|
808
|
+
var React8 = __toESM(require("react"));
|
|
809
|
+
var import_scroll_area = require("@base-ui/react/scroll-area");
|
|
810
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
811
|
+
var ScrollArea = React8.forwardRef(({ className, children, ...props }, ref) => {
|
|
812
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
813
|
+
import_scroll_area.ScrollArea.Root,
|
|
814
|
+
{
|
|
815
|
+
ref,
|
|
816
|
+
"data-slot": "scroll-area",
|
|
817
|
+
className: cn("relative", className),
|
|
818
|
+
...props,
|
|
819
|
+
children: [
|
|
820
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
821
|
+
import_scroll_area.ScrollArea.Viewport,
|
|
822
|
+
{
|
|
823
|
+
"data-slot": "scroll-area-viewport",
|
|
824
|
+
className: "size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1",
|
|
825
|
+
children
|
|
826
|
+
}
|
|
827
|
+
),
|
|
828
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ScrollBar, {}),
|
|
829
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_scroll_area.ScrollArea.Corner, {})
|
|
830
|
+
]
|
|
831
|
+
}
|
|
832
|
+
);
|
|
833
|
+
});
|
|
834
|
+
ScrollArea.displayName = "ScrollArea";
|
|
835
|
+
var ScrollBar = React8.forwardRef(({ className, orientation = "vertical", ...props }, ref) => {
|
|
836
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
837
|
+
import_scroll_area.ScrollArea.Scrollbar,
|
|
838
|
+
{
|
|
839
|
+
ref,
|
|
840
|
+
"data-slot": "scroll-area-scrollbar",
|
|
841
|
+
"data-orientation": orientation,
|
|
842
|
+
orientation,
|
|
843
|
+
className: cn(
|
|
844
|
+
"flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent",
|
|
845
|
+
className
|
|
846
|
+
),
|
|
847
|
+
...props,
|
|
848
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
849
|
+
import_scroll_area.ScrollArea.Thumb,
|
|
850
|
+
{
|
|
851
|
+
"data-slot": "scroll-area-thumb",
|
|
852
|
+
className: "relative flex-1 rounded-full bg-border"
|
|
853
|
+
}
|
|
854
|
+
)
|
|
855
|
+
}
|
|
856
|
+
);
|
|
857
|
+
});
|
|
858
|
+
ScrollBar.displayName = "ScrollBar";
|
|
859
|
+
|
|
860
|
+
// src/ui/sheet.tsx
|
|
861
|
+
var React9 = __toESM(require("react"));
|
|
862
|
+
var import_dialog3 = require("@base-ui/react/dialog");
|
|
863
|
+
var import_lucide_react3 = require("lucide-react");
|
|
864
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
865
|
+
function Sheet({ ...props }) {
|
|
866
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_dialog3.Dialog.Root, { "data-slot": "sheet", ...props });
|
|
867
|
+
}
|
|
868
|
+
var SheetTrigger = React9.forwardRef(({ ...props }, ref) => {
|
|
869
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
870
|
+
import_dialog3.Dialog.Trigger,
|
|
871
|
+
{
|
|
872
|
+
ref,
|
|
873
|
+
"data-slot": "sheet-trigger",
|
|
874
|
+
...props
|
|
875
|
+
}
|
|
876
|
+
);
|
|
877
|
+
});
|
|
878
|
+
SheetTrigger.displayName = "SheetTrigger";
|
|
879
|
+
var SheetClose = React9.forwardRef(({ ...props }, ref) => {
|
|
880
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
881
|
+
import_dialog3.Dialog.Close,
|
|
882
|
+
{
|
|
883
|
+
ref,
|
|
884
|
+
"data-slot": "sheet-close",
|
|
885
|
+
...props
|
|
886
|
+
}
|
|
887
|
+
);
|
|
888
|
+
});
|
|
889
|
+
SheetClose.displayName = "SheetClose";
|
|
890
|
+
function SheetPortal({ ...props }) {
|
|
891
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_dialog3.Dialog.Portal, { "data-slot": "sheet-portal", ...props });
|
|
892
|
+
}
|
|
893
|
+
var SheetOverlay = React9.forwardRef(({ className, ...props }, ref) => {
|
|
894
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
895
|
+
import_dialog3.Dialog.Backdrop,
|
|
896
|
+
{
|
|
897
|
+
ref,
|
|
898
|
+
"data-slot": "sheet-overlay",
|
|
899
|
+
className: cn(
|
|
900
|
+
"fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs",
|
|
901
|
+
className
|
|
902
|
+
),
|
|
903
|
+
...props
|
|
904
|
+
}
|
|
905
|
+
);
|
|
906
|
+
});
|
|
907
|
+
SheetOverlay.displayName = "SheetOverlay";
|
|
908
|
+
function SheetContent({
|
|
909
|
+
className,
|
|
910
|
+
children,
|
|
911
|
+
side = "right",
|
|
912
|
+
showCloseButton = true,
|
|
913
|
+
...props
|
|
914
|
+
}) {
|
|
915
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(SheetPortal, { children: [
|
|
916
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SheetOverlay, {}),
|
|
917
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
918
|
+
import_dialog3.Dialog.Popup,
|
|
919
|
+
{
|
|
920
|
+
"data-slot": "sheet-content",
|
|
921
|
+
"data-side": side,
|
|
922
|
+
className: cn(
|
|
923
|
+
"fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm",
|
|
924
|
+
className
|
|
925
|
+
),
|
|
926
|
+
...props,
|
|
927
|
+
children: [
|
|
928
|
+
children,
|
|
929
|
+
showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
930
|
+
import_dialog3.Dialog.Close,
|
|
931
|
+
{
|
|
932
|
+
"data-slot": "sheet-close",
|
|
933
|
+
render: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
934
|
+
Button,
|
|
935
|
+
{
|
|
936
|
+
variant: "ghost",
|
|
937
|
+
className: "absolute top-3 right-3",
|
|
938
|
+
size: "icon-sm"
|
|
939
|
+
}
|
|
940
|
+
),
|
|
941
|
+
children: [
|
|
942
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
943
|
+
import_lucide_react3.XIcon,
|
|
944
|
+
{}
|
|
945
|
+
),
|
|
946
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "sr-only", children: "Close" })
|
|
947
|
+
]
|
|
948
|
+
}
|
|
949
|
+
)
|
|
950
|
+
]
|
|
951
|
+
}
|
|
952
|
+
)
|
|
953
|
+
] });
|
|
954
|
+
}
|
|
955
|
+
function SheetHeader({ className, ...props }) {
|
|
956
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
957
|
+
"div",
|
|
958
|
+
{
|
|
959
|
+
"data-slot": "sheet-header",
|
|
960
|
+
className: cn("flex flex-col gap-0.5 p-4", className),
|
|
961
|
+
...props
|
|
962
|
+
}
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
function SheetFooter({ className, ...props }) {
|
|
966
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
967
|
+
"div",
|
|
968
|
+
{
|
|
969
|
+
"data-slot": "sheet-footer",
|
|
970
|
+
className: cn("mt-auto flex flex-col gap-2 p-4", className),
|
|
971
|
+
...props
|
|
972
|
+
}
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
function SheetTitle({ className, ...props }) {
|
|
976
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
977
|
+
import_dialog3.Dialog.Title,
|
|
978
|
+
{
|
|
979
|
+
"data-slot": "sheet-title",
|
|
980
|
+
className: cn(
|
|
981
|
+
"font-heading text-base font-medium text-foreground",
|
|
982
|
+
className
|
|
983
|
+
),
|
|
984
|
+
...props
|
|
985
|
+
}
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
function SheetDescription({
|
|
989
|
+
className,
|
|
990
|
+
...props
|
|
991
|
+
}) {
|
|
992
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
993
|
+
import_dialog3.Dialog.Description,
|
|
994
|
+
{
|
|
995
|
+
"data-slot": "sheet-description",
|
|
996
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
997
|
+
...props
|
|
998
|
+
}
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// src/ui/switch.tsx
|
|
1003
|
+
var React10 = __toESM(require("react"));
|
|
1004
|
+
var import_switch = require("@base-ui/react/switch");
|
|
1005
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1006
|
+
var Switch = React10.forwardRef(({ className, size = "default", ...props }, ref) => {
|
|
1007
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1008
|
+
import_switch.Switch.Root,
|
|
1009
|
+
{
|
|
1010
|
+
ref,
|
|
1011
|
+
"data-slot": "switch",
|
|
1012
|
+
"data-size": size,
|
|
1013
|
+
className: cn(
|
|
1014
|
+
"peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
|
1015
|
+
className
|
|
1016
|
+
),
|
|
1017
|
+
...props,
|
|
1018
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1019
|
+
import_switch.Switch.Thumb,
|
|
1020
|
+
{
|
|
1021
|
+
"data-slot": "switch-thumb",
|
|
1022
|
+
className: "pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
|
|
1023
|
+
}
|
|
1024
|
+
)
|
|
1025
|
+
}
|
|
1026
|
+
);
|
|
1027
|
+
});
|
|
1028
|
+
Switch.displayName = "Switch";
|
|
1029
|
+
|
|
1030
|
+
// src/ui/tooltip.tsx
|
|
1031
|
+
var React11 = __toESM(require("react"));
|
|
1032
|
+
var import_tooltip = require("@base-ui/react/tooltip");
|
|
1033
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1034
|
+
function TooltipProvider({ ...props }) {
|
|
1035
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_tooltip.Tooltip.Provider, { ...props });
|
|
1036
|
+
}
|
|
1037
|
+
function Tooltip({ ...props }) {
|
|
1038
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_tooltip.Tooltip.Root, { "data-slot": "tooltip", ...props });
|
|
1039
|
+
}
|
|
1040
|
+
var TooltipTrigger = React11.forwardRef(({ ...props }, ref) => {
|
|
1041
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1042
|
+
import_tooltip.Tooltip.Trigger,
|
|
1043
|
+
{
|
|
1044
|
+
ref,
|
|
1045
|
+
"data-slot": "tooltip-trigger",
|
|
1046
|
+
render: (triggerProps) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { ...triggerProps, className: "contents" }),
|
|
1047
|
+
...props
|
|
1048
|
+
}
|
|
1049
|
+
);
|
|
1050
|
+
});
|
|
1051
|
+
TooltipTrigger.displayName = "TooltipTrigger";
|
|
1052
|
+
function TooltipContent({
|
|
1053
|
+
className,
|
|
1054
|
+
sideOffset = 4,
|
|
1055
|
+
side = "top",
|
|
1056
|
+
align = "center",
|
|
1057
|
+
alignOffset = 0,
|
|
1058
|
+
...props
|
|
1059
|
+
}) {
|
|
1060
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_tooltip.Tooltip.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1061
|
+
import_tooltip.Tooltip.Positioner,
|
|
1062
|
+
{
|
|
1063
|
+
sideOffset,
|
|
1064
|
+
side,
|
|
1065
|
+
align,
|
|
1066
|
+
alignOffset,
|
|
1067
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1068
|
+
import_tooltip.Tooltip.Popup,
|
|
1069
|
+
{
|
|
1070
|
+
"data-slot": "tooltip-content",
|
|
1071
|
+
className: cn(
|
|
1072
|
+
"z-[9999] overflow-hidden rounded-md bg-zinc-900 border border-white/10 px-3 py-1.5 text-xs text-white animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 shadow-xl",
|
|
1073
|
+
className
|
|
1074
|
+
),
|
|
1075
|
+
...props
|
|
1076
|
+
}
|
|
1077
|
+
)
|
|
1078
|
+
}
|
|
1079
|
+
) });
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// src/ui/SeoHead.tsx
|
|
1083
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1084
|
+
function SeoHead({ data, fallbackSiteName }) {
|
|
1085
|
+
if (!data) return null;
|
|
1086
|
+
const title = data.seoTitle || fallbackSiteName || "VibeCMS Site";
|
|
1087
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
1088
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("title", { children: title }),
|
|
1089
|
+
data.seoDescription && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("meta", { name: "description", content: data.seoDescription }),
|
|
1090
|
+
data.keywords && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("meta", { name: "keywords", content: data.keywords }),
|
|
1091
|
+
data.ogImage && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("meta", { property: "og:image", content: data.ogImage }),
|
|
1092
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("meta", { property: "og:title", content: title }),
|
|
1093
|
+
data.seoDescription && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("meta", { property: "og:description", content: data.seoDescription })
|
|
1094
|
+
] });
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// src/ui/AuthorCard.tsx
|
|
1098
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1099
|
+
function AuthorCard({ author }) {
|
|
1100
|
+
if (!author) return null;
|
|
1101
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-4 p-4 border rounded-lg bg-neutral-50 border-neutral-200", children: [
|
|
1102
|
+
author.headshot && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1103
|
+
"img",
|
|
1104
|
+
{
|
|
1105
|
+
src: author.headshot,
|
|
1106
|
+
alt: author.name,
|
|
1107
|
+
className: "w-16 h-16 rounded-full object-cover"
|
|
1108
|
+
}
|
|
1109
|
+
),
|
|
1110
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
|
|
1111
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h4", { className: "font-semibold text-lg text-neutral-900", children: author.name }),
|
|
1112
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-neutral-500 font-medium", children: author.role })
|
|
1113
|
+
] })
|
|
1114
|
+
] });
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// src/config.ts
|
|
1118
|
+
function defineConfig(config) {
|
|
1119
|
+
return config;
|
|
1120
|
+
}
|
|
1121
|
+
var schemaRegistry = /* @__PURE__ */ new Map();
|
|
1122
|
+
function registerCollectionSchemas(collections, singletons) {
|
|
1123
|
+
schemaRegistry.clear();
|
|
1124
|
+
if (collections) {
|
|
1125
|
+
for (const [key, col] of Object.entries(collections)) {
|
|
1126
|
+
schemaRegistry.set(key, col.schema);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
if (singletons) {
|
|
1130
|
+
for (const [key, col] of Object.entries(singletons)) {
|
|
1131
|
+
schemaRegistry.set(key, col.schema);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
function getCollectionSchema(collection) {
|
|
1136
|
+
return schemaRegistry.get(collection);
|
|
1137
|
+
}
|
|
1138
|
+
function validateCollectionData(collection, data) {
|
|
1139
|
+
const schema = schemaRegistry.get(collection);
|
|
1140
|
+
if (!schema) {
|
|
1141
|
+
return { success: true, data };
|
|
1142
|
+
}
|
|
1143
|
+
const result = schema.safeParse(data);
|
|
1144
|
+
if (result.success) {
|
|
1145
|
+
return { success: true, data: result.data };
|
|
1146
|
+
}
|
|
1147
|
+
const errors = result.error.issues.map(
|
|
1148
|
+
(issue) => `${issue.path.join(".")}: ${issue.message}`
|
|
1149
|
+
);
|
|
1150
|
+
return { success: false, errors };
|
|
1151
|
+
}
|
|
1152
|
+
function resolveUserRole(config, email) {
|
|
1153
|
+
if (!email) return null;
|
|
1154
|
+
if (!config.access || config.access.length === 0) {
|
|
1155
|
+
return "editor";
|
|
1156
|
+
}
|
|
1157
|
+
const entry = config.access.find((a) => a.email.toLowerCase() === email.toLowerCase());
|
|
1158
|
+
return entry?.role ?? null;
|
|
1159
|
+
}
|
|
1160
|
+
function canEditCollection(config, role, collectionKey) {
|
|
1161
|
+
if (!role) return false;
|
|
1162
|
+
if (role === "owner") return true;
|
|
1163
|
+
if (role === "viewer") return false;
|
|
1164
|
+
const collection = config.collections?.[collectionKey] || config.singletons?.[collectionKey];
|
|
1165
|
+
if (!collection) return false;
|
|
1166
|
+
const allowed = collection.editRoles ?? ["owner", "editor"];
|
|
1167
|
+
return allowed.includes(role);
|
|
1168
|
+
}
|
|
1169
|
+
var blockRegistry = /* @__PURE__ */ new Map();
|
|
1170
|
+
function registerCustomBlocks(blocks) {
|
|
1171
|
+
for (const block of blocks) {
|
|
1172
|
+
blockRegistry.set(block.type, block.component);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
function getCustomBlockComponent(type) {
|
|
1176
|
+
return blockRegistry.get(type);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
// src/engine/validation.ts
|
|
1180
|
+
var SLUG_REGEX = /^[a-z0-9][a-z0-9_/-]{0,100}$/i;
|
|
1181
|
+
var COMMIT_OID_REGEX = /^[0-9a-f]{5,40}$/;
|
|
1182
|
+
var COLLECTION_REGEX = /^[a-z0-9][a-z0-9_-]{0,50}$/i;
|
|
1183
|
+
function validateSlug(slug) {
|
|
1184
|
+
return typeof slug === "string" && SLUG_REGEX.test(slug);
|
|
1185
|
+
}
|
|
1186
|
+
function validateCommitOid(oid) {
|
|
1187
|
+
return typeof oid === "string" && COMMIT_OID_REGEX.test(oid);
|
|
1188
|
+
}
|
|
1189
|
+
function validateCollection(collection) {
|
|
1190
|
+
return typeof collection === "string" && COLLECTION_REGEX.test(collection);
|
|
1191
|
+
}
|
|
1192
|
+
function assertValidSlug(slug) {
|
|
1193
|
+
if (!validateSlug(slug)) {
|
|
1194
|
+
throw new Error(`Invalid slug: "${slug}". Slugs must match ${SLUG_REGEX}`);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
function assertValidCommitOid(oid) {
|
|
1198
|
+
if (!validateCommitOid(oid)) {
|
|
1199
|
+
throw new Error(`Invalid commit OID: "${oid}". Must be a hex SHA hash.`);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
function assertValidCollection(collection) {
|
|
1203
|
+
if (!validateCollection(collection)) {
|
|
1204
|
+
throw new Error(`Invalid collection name: "${collection}"`);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
function sanitizeRedirectPath(path) {
|
|
1208
|
+
if (!path) return "/";
|
|
1209
|
+
if (!path.startsWith("/")) return "/";
|
|
1210
|
+
if (path.startsWith("//")) return "/";
|
|
1211
|
+
if (path.includes("://")) return "/";
|
|
1212
|
+
if (path.includes("\\")) return "/";
|
|
1213
|
+
return path;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
// src/engine/engine-node.ts
|
|
1217
|
+
function hasReferenceToSlug(data, slug) {
|
|
1218
|
+
if (data === slug) return true;
|
|
1219
|
+
if (Array.isArray(data)) {
|
|
1220
|
+
return data.some((item) => hasReferenceToSlug(item, slug));
|
|
1221
|
+
}
|
|
1222
|
+
if (data && typeof data === "object") {
|
|
1223
|
+
for (const [key, value] of Object.entries(data)) {
|
|
1224
|
+
if (key.startsWith("_")) continue;
|
|
1225
|
+
if ([
|
|
1226
|
+
"title",
|
|
1227
|
+
"subtitle",
|
|
1228
|
+
"description",
|
|
1229
|
+
"content",
|
|
1230
|
+
"body",
|
|
1231
|
+
"text",
|
|
1232
|
+
"html",
|
|
1233
|
+
"seoTitle",
|
|
1234
|
+
"seoDescription",
|
|
1235
|
+
"label",
|
|
1236
|
+
"name",
|
|
1237
|
+
"placeholder",
|
|
1238
|
+
"helpText",
|
|
1239
|
+
"successMessage",
|
|
1240
|
+
"quote",
|
|
1241
|
+
"answer",
|
|
1242
|
+
"question",
|
|
1243
|
+
"caption",
|
|
1244
|
+
"alt",
|
|
1245
|
+
"icon",
|
|
1246
|
+
"url",
|
|
1247
|
+
"image",
|
|
1248
|
+
"ogImage",
|
|
1249
|
+
"backgroundColor",
|
|
1250
|
+
"textColor",
|
|
1251
|
+
"keywords"
|
|
1252
|
+
].includes(key)) {
|
|
1253
|
+
continue;
|
|
1254
|
+
}
|
|
1255
|
+
if (hasReferenceToSlug(value, slug)) return true;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
return false;
|
|
1259
|
+
}
|
|
1260
|
+
var ContentCache = class {
|
|
1261
|
+
cache = /* @__PURE__ */ new Map();
|
|
1262
|
+
maxEntries;
|
|
1263
|
+
ttlMs;
|
|
1264
|
+
constructor(maxEntries = 200, ttlMs = 3e4) {
|
|
1265
|
+
this.maxEntries = maxEntries;
|
|
1266
|
+
this.ttlMs = ttlMs;
|
|
1267
|
+
}
|
|
1268
|
+
get(key) {
|
|
1269
|
+
const entry = this.cache.get(key);
|
|
1270
|
+
if (!entry) return void 0;
|
|
1271
|
+
if (Date.now() > entry.expiresAt) {
|
|
1272
|
+
this.cache.delete(key);
|
|
1273
|
+
return void 0;
|
|
1274
|
+
}
|
|
1275
|
+
this.cache.delete(key);
|
|
1276
|
+
this.cache.set(key, entry);
|
|
1277
|
+
return entry.data;
|
|
1278
|
+
}
|
|
1279
|
+
set(key, data) {
|
|
1280
|
+
if (this.cache.size >= this.maxEntries) {
|
|
1281
|
+
const firstKey = this.cache.keys().next().value;
|
|
1282
|
+
if (firstKey !== void 0) this.cache.delete(firstKey);
|
|
1283
|
+
}
|
|
1284
|
+
this.cache.set(key, { data, expiresAt: Date.now() + this.ttlMs });
|
|
1285
|
+
}
|
|
1286
|
+
invalidate(key) {
|
|
1287
|
+
this.cache.delete(key);
|
|
1288
|
+
}
|
|
1289
|
+
invalidateCollection(collection) {
|
|
1290
|
+
for (const key of this.cache.keys()) {
|
|
1291
|
+
if (key.startsWith(`${collection}/`)) {
|
|
1292
|
+
this.cache.delete(key);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
var NodeEngine = class {
|
|
1298
|
+
contentDir = "src/content";
|
|
1299
|
+
publicDir = "public";
|
|
1300
|
+
config;
|
|
1301
|
+
contentCache = new ContentCache();
|
|
1302
|
+
async init(config) {
|
|
1303
|
+
this.config = config;
|
|
1304
|
+
if (config?.contentDir) this.contentDir = config.contentDir;
|
|
1305
|
+
if (config?.publicDir) this.publicDir = config.publicDir;
|
|
1306
|
+
}
|
|
1307
|
+
resolveFilename(slug, locale) {
|
|
1308
|
+
const base = slug.endsWith(".json") ? slug.replace(/\.json$/, "") : slug;
|
|
1309
|
+
return locale ? `${base}.${locale}.json` : `${base}.json`;
|
|
1310
|
+
}
|
|
1311
|
+
async read(collection, slug, options) {
|
|
1312
|
+
if (typeof window !== "undefined") return null;
|
|
1313
|
+
assertValidCollection(collection);
|
|
1314
|
+
assertValidSlug(slug);
|
|
1315
|
+
const filename = this.resolveFilename(slug, options?.locale);
|
|
1316
|
+
const cacheKey = `${collection}/${filename}`;
|
|
1317
|
+
const cached = this.contentCache.get(cacheKey);
|
|
1318
|
+
if (cached !== void 0) return cached;
|
|
1319
|
+
const fs = await import("fs/promises");
|
|
1320
|
+
const path = await import("path");
|
|
1321
|
+
const filePath = path.join(process.cwd(), this.contentDir, collection, filename);
|
|
1322
|
+
try {
|
|
1323
|
+
const [content, stat] = await Promise.all([
|
|
1324
|
+
fs.readFile(filePath, "utf-8"),
|
|
1325
|
+
fs.stat(filePath)
|
|
1326
|
+
]);
|
|
1327
|
+
const data = JSON.parse(content);
|
|
1328
|
+
data._vibeVersion = stat.mtimeMs;
|
|
1329
|
+
this.contentCache.set(cacheKey, data);
|
|
1330
|
+
return data;
|
|
1331
|
+
} catch (e) {
|
|
1332
|
+
return null;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
async write(collection, slug, data, options) {
|
|
1336
|
+
if (typeof window !== "undefined") return;
|
|
1337
|
+
assertValidCollection(collection);
|
|
1338
|
+
assertValidSlug(slug);
|
|
1339
|
+
const validation = validateCollectionData(collection, data);
|
|
1340
|
+
if (!validation.success) {
|
|
1341
|
+
throw new Error(
|
|
1342
|
+
`Schema validation failed for ${collection}/${slug}: ${validation.errors.join("; ")}`
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
const fs = await import("fs/promises");
|
|
1346
|
+
const path = await import("path");
|
|
1347
|
+
const filename = this.resolveFilename(slug, options?.locale);
|
|
1348
|
+
const dir = path.join(process.cwd(), this.contentDir, collection);
|
|
1349
|
+
await fs.mkdir(dir, { recursive: true });
|
|
1350
|
+
const filePath = path.join(dir, filename);
|
|
1351
|
+
const dataToWrite = { ...validation.data };
|
|
1352
|
+
const clientVersion = data._vibeVersion;
|
|
1353
|
+
delete dataToWrite._vibeVersion;
|
|
1354
|
+
if (clientVersion) {
|
|
1355
|
+
try {
|
|
1356
|
+
const stat = await fs.stat(filePath);
|
|
1357
|
+
if (Math.abs(stat.mtimeMs - clientVersion) > 100) {
|
|
1358
|
+
throw new Error(
|
|
1359
|
+
`Conflict: "${collection}/${slug}" was modified by another editor. Reload to see the latest version.`
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
} catch (e) {
|
|
1363
|
+
if (e.message.startsWith("Conflict:")) throw e;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
await fs.writeFile(filePath, JSON.stringify(dataToWrite, null, 2), "utf-8");
|
|
1367
|
+
this.contentCache.invalidate(`${collection}/${filename}`);
|
|
1368
|
+
}
|
|
1369
|
+
async delete(collection, slug, options) {
|
|
1370
|
+
if (typeof window !== "undefined") return;
|
|
1371
|
+
assertValidCollection(collection);
|
|
1372
|
+
assertValidSlug(slug);
|
|
1373
|
+
const fs = await import("fs/promises");
|
|
1374
|
+
const path = await import("path");
|
|
1375
|
+
const filename = this.resolveFilename(slug, options?.locale);
|
|
1376
|
+
const baseDir = path.join(process.cwd(), this.contentDir);
|
|
1377
|
+
const cols = await fs.readdir(baseDir);
|
|
1378
|
+
for (const col of cols) {
|
|
1379
|
+
if (col === collection) continue;
|
|
1380
|
+
const colDir = path.join(baseDir, col);
|
|
1381
|
+
const stat = await fs.stat(colDir);
|
|
1382
|
+
if (!stat.isDirectory()) continue;
|
|
1383
|
+
const files = await fs.readdir(colDir);
|
|
1384
|
+
for (const file of files) {
|
|
1385
|
+
if (!file.endsWith(".json")) continue;
|
|
1386
|
+
const raw = await fs.readFile(path.join(colDir, file), "utf8");
|
|
1387
|
+
try {
|
|
1388
|
+
const data = JSON.parse(raw);
|
|
1389
|
+
if (hasReferenceToSlug(data, slug)) {
|
|
1390
|
+
throw new Error(
|
|
1391
|
+
`Referential Integrity Error: Cannot delete "${slug}" because it is referenced by ${col}/${file}`
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
} catch (e) {
|
|
1395
|
+
if (e.message.startsWith("Referential Integrity")) throw e;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
const filePath = path.join(baseDir, collection, filename);
|
|
1400
|
+
await fs.unlink(filePath);
|
|
1401
|
+
this.contentCache.invalidate(`${collection}/${filename}`);
|
|
1402
|
+
}
|
|
1403
|
+
async isDirty() {
|
|
1404
|
+
return false;
|
|
1405
|
+
}
|
|
1406
|
+
async revert() {
|
|
1407
|
+
}
|
|
1408
|
+
async commit(_message, _author) {
|
|
1409
|
+
}
|
|
1410
|
+
async push() {
|
|
1411
|
+
}
|
|
1412
|
+
async pull() {
|
|
1413
|
+
}
|
|
1414
|
+
async writeMedia(file, author) {
|
|
1415
|
+
if (this.config?.media?.driver) {
|
|
1416
|
+
return this.config.media.driver.upload(file, { engine: this, author });
|
|
1417
|
+
}
|
|
1418
|
+
if (typeof window !== "undefined") return "";
|
|
1419
|
+
const fs = await import("fs/promises");
|
|
1420
|
+
const path = await import("path");
|
|
1421
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
1422
|
+
const dir = path.join(process.cwd(), this.publicDir, "assets", "uploads");
|
|
1423
|
+
await fs.mkdir(dir, { recursive: true });
|
|
1424
|
+
const safeName = file.name.replace(/[^a-z0-9.-]/gi, "_").toLowerCase();
|
|
1425
|
+
const filePath = path.join(dir, safeName);
|
|
1426
|
+
await fs.writeFile(filePath, buffer);
|
|
1427
|
+
const url = `/assets/uploads/${safeName}`;
|
|
1428
|
+
try {
|
|
1429
|
+
const sizeOf = (await import("image-size")).default;
|
|
1430
|
+
const dimensions = sizeOf(buffer);
|
|
1431
|
+
if (dimensions && dimensions.width && dimensions.height) {
|
|
1432
|
+
return {
|
|
1433
|
+
url,
|
|
1434
|
+
width: dimensions.width,
|
|
1435
|
+
height: dimensions.height,
|
|
1436
|
+
alt: file.name
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
} catch (e) {
|
|
1440
|
+
}
|
|
1441
|
+
return `/assets/uploads/${safeName}`;
|
|
1442
|
+
}
|
|
1443
|
+
async listMedia() {
|
|
1444
|
+
if (this.config?.media?.driver?.list) {
|
|
1445
|
+
return this.config.media.driver.list({ engine: this });
|
|
1446
|
+
}
|
|
1447
|
+
if (typeof window !== "undefined") return [];
|
|
1448
|
+
try {
|
|
1449
|
+
const fs = await import("fs/promises");
|
|
1450
|
+
const path = await import("path");
|
|
1451
|
+
const uploadsDir = path.join(process.cwd(), this.publicDir, "assets", "uploads");
|
|
1452
|
+
const files = await fs.readdir(uploadsDir);
|
|
1453
|
+
return files.filter((f) => !f.startsWith(".")).map((f) => `/assets/uploads/${f}`);
|
|
1454
|
+
} catch {
|
|
1455
|
+
return [];
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
async deleteMedia(filename) {
|
|
1459
|
+
if (this.config?.media?.driver?.delete) {
|
|
1460
|
+
return this.config.media.driver.delete(filename, { engine: this });
|
|
1461
|
+
}
|
|
1462
|
+
if (typeof window !== "undefined") return;
|
|
1463
|
+
const fs = await import("fs/promises");
|
|
1464
|
+
const path = await import("path");
|
|
1465
|
+
const safeName = filename.split("/").pop() || filename;
|
|
1466
|
+
const filePath = path.join(process.cwd(), this.publicDir, "assets", "uploads", safeName);
|
|
1467
|
+
try {
|
|
1468
|
+
await fs.unlink(filePath);
|
|
1469
|
+
} catch (e) {
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
async renameMedia(oldFilename, newFilename) {
|
|
1473
|
+
if (this.config?.media?.driver?.rename) {
|
|
1474
|
+
return this.config.media.driver.rename(oldFilename, newFilename, { engine: this });
|
|
1475
|
+
}
|
|
1476
|
+
if (typeof window !== "undefined") return;
|
|
1477
|
+
const fs = await import("fs/promises");
|
|
1478
|
+
const path = await import("path");
|
|
1479
|
+
const safeOldName = oldFilename.split("/").pop() || oldFilename;
|
|
1480
|
+
const safeNewName = newFilename.split("/").pop() || newFilename;
|
|
1481
|
+
const oldPath = path.join(process.cwd(), this.publicDir, "assets", "uploads", safeOldName);
|
|
1482
|
+
const newPath = path.join(process.cwd(), this.publicDir, "assets", "uploads", safeNewName);
|
|
1483
|
+
try {
|
|
1484
|
+
await fs.rename(oldPath, newPath);
|
|
1485
|
+
} catch (e) {
|
|
1486
|
+
throw new Error(`Failed to rename local media: ${e}`);
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
async pruneMedia() {
|
|
1490
|
+
if (typeof window !== "undefined") return { deletedCount: 0, bytesFreed: 0 };
|
|
1491
|
+
const fs = await import("fs/promises");
|
|
1492
|
+
const path = await import("path");
|
|
1493
|
+
let usedMedia = /* @__PURE__ */ new Set();
|
|
1494
|
+
const contentDir = path.join(process.cwd(), "src", "content");
|
|
1495
|
+
const uploadsDir = path.join(process.cwd(), this.publicDir, "assets", "uploads");
|
|
1496
|
+
async function scanDir(dir) {
|
|
1497
|
+
if (!require("fs").existsSync(dir)) return;
|
|
1498
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
1499
|
+
for (const entry of entries) {
|
|
1500
|
+
const fullPath = path.join(dir, entry.name);
|
|
1501
|
+
if (entry.isDirectory()) await scanDir(fullPath);
|
|
1502
|
+
else if (entry.name.endsWith(".json")) {
|
|
1503
|
+
const content = await fs.readFile(fullPath, "utf-8");
|
|
1504
|
+
const regex = /\/assets\/uploads\/([^"'\s\\]+)/g;
|
|
1505
|
+
let match;
|
|
1506
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1507
|
+
usedMedia.add(match[1]);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
await scanDir(contentDir);
|
|
1513
|
+
let deletedCount = 0;
|
|
1514
|
+
let bytesFreed = 0;
|
|
1515
|
+
if (require("fs").existsSync(uploadsDir)) {
|
|
1516
|
+
const uFiles = await fs.readdir(uploadsDir);
|
|
1517
|
+
for (const f of uFiles) {
|
|
1518
|
+
if (!usedMedia.has(f) && !f.startsWith(".")) {
|
|
1519
|
+
const stat = await fs.stat(path.join(uploadsDir, f));
|
|
1520
|
+
bytesFreed += stat.size;
|
|
1521
|
+
await fs.unlink(path.join(uploadsDir, f));
|
|
1522
|
+
deletedCount++;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
return { deletedCount, bytesFreed };
|
|
1527
|
+
}
|
|
1528
|
+
async getMediaUrl(relativePath) {
|
|
1529
|
+
return relativePath;
|
|
1530
|
+
}
|
|
1531
|
+
async list(collection, options) {
|
|
1532
|
+
if (typeof window !== "undefined") return [];
|
|
1533
|
+
try {
|
|
1534
|
+
const fs = await import("fs/promises");
|
|
1535
|
+
const path = await import("path");
|
|
1536
|
+
const dir = path.join(process.cwd(), this.contentDir, collection);
|
|
1537
|
+
const files = await fs.readdir(dir);
|
|
1538
|
+
const slugs = files.filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
|
|
1539
|
+
if (options?.withMeta) {
|
|
1540
|
+
const metaList = await Promise.all(slugs.map(async (slug) => {
|
|
1541
|
+
try {
|
|
1542
|
+
const content = await fs.readFile(path.join(dir, `${slug}.json`), "utf-8");
|
|
1543
|
+
const data = JSON.parse(content);
|
|
1544
|
+
return { slug, title: data.title || data.name || slug, ...data };
|
|
1545
|
+
} catch (e) {
|
|
1546
|
+
return { slug, title: slug };
|
|
1547
|
+
}
|
|
1548
|
+
}));
|
|
1549
|
+
return metaList;
|
|
1550
|
+
}
|
|
1551
|
+
return slugs;
|
|
1552
|
+
} catch {
|
|
1553
|
+
return [];
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
async getHistory(collection, slug) {
|
|
1557
|
+
if (typeof window !== "undefined") return [];
|
|
1558
|
+
assertValidCollection(collection);
|
|
1559
|
+
assertValidSlug(slug);
|
|
1560
|
+
try {
|
|
1561
|
+
const { execFileSync } = require("child_process");
|
|
1562
|
+
const filePath = `${this.contentDir}/${collection}/${slug}.json`;
|
|
1563
|
+
const raw = execFileSync(
|
|
1564
|
+
"git",
|
|
1565
|
+
["log", "--follow", "--format=%H%x00%s%x00%an%x00%ae%x00%at", "--", filePath],
|
|
1566
|
+
{ cwd: process.cwd(), encoding: "utf-8", timeout: 5e3 }
|
|
1567
|
+
).trim();
|
|
1568
|
+
if (!raw) return [];
|
|
1569
|
+
return raw.split("\n").map((line) => {
|
|
1570
|
+
const [oid, message, author, email, ts] = line.split("\0");
|
|
1571
|
+
return { oid, message, author, email, timestamp: parseInt(ts, 10) * 1e3 };
|
|
1572
|
+
});
|
|
1573
|
+
} catch {
|
|
1574
|
+
return [];
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
async getVersionContent(commitOid, collection, slug) {
|
|
1578
|
+
if (typeof window !== "undefined") return null;
|
|
1579
|
+
assertValidCollection(collection);
|
|
1580
|
+
assertValidSlug(slug);
|
|
1581
|
+
assertValidCommitOid(commitOid);
|
|
1582
|
+
try {
|
|
1583
|
+
const { execFileSync } = require("child_process");
|
|
1584
|
+
const filePath = `${this.contentDir}/${collection}/${slug}.json`;
|
|
1585
|
+
const content = execFileSync(
|
|
1586
|
+
"git",
|
|
1587
|
+
["show", `${commitOid}:${filePath}`],
|
|
1588
|
+
{ cwd: process.cwd(), encoding: "utf-8", timeout: 5e3 }
|
|
1589
|
+
);
|
|
1590
|
+
return JSON.parse(content);
|
|
1591
|
+
} catch {
|
|
1592
|
+
return null;
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
};
|
|
1596
|
+
|
|
1597
|
+
// src/engine/engine-browser.ts
|
|
1598
|
+
var DEFAULT_AUTHOR = { name: "VibeCMS Editor", email: "editor@vibecms.local" };
|
|
1599
|
+
function hasReferenceToSlug2(data, slug) {
|
|
1600
|
+
if (data === slug) return true;
|
|
1601
|
+
if (Array.isArray(data)) {
|
|
1602
|
+
return data.some((item) => hasReferenceToSlug2(item, slug));
|
|
1603
|
+
}
|
|
1604
|
+
if (data && typeof data === "object") {
|
|
1605
|
+
for (const [key, value] of Object.entries(data)) {
|
|
1606
|
+
if (key.startsWith("_")) continue;
|
|
1607
|
+
if ([
|
|
1608
|
+
"title",
|
|
1609
|
+
"subtitle",
|
|
1610
|
+
"description",
|
|
1611
|
+
"content",
|
|
1612
|
+
"body",
|
|
1613
|
+
"text",
|
|
1614
|
+
"html",
|
|
1615
|
+
"seoTitle",
|
|
1616
|
+
"seoDescription",
|
|
1617
|
+
"label",
|
|
1618
|
+
"name",
|
|
1619
|
+
"placeholder",
|
|
1620
|
+
"helpText",
|
|
1621
|
+
"successMessage",
|
|
1622
|
+
"quote",
|
|
1623
|
+
"answer",
|
|
1624
|
+
"question",
|
|
1625
|
+
"caption",
|
|
1626
|
+
"alt",
|
|
1627
|
+
"icon",
|
|
1628
|
+
"url",
|
|
1629
|
+
"image",
|
|
1630
|
+
"ogImage",
|
|
1631
|
+
"backgroundColor",
|
|
1632
|
+
"textColor",
|
|
1633
|
+
"keywords"
|
|
1634
|
+
].includes(key)) {
|
|
1635
|
+
continue;
|
|
1636
|
+
}
|
|
1637
|
+
if (hasReferenceToSlug2(value, slug)) return true;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
return false;
|
|
1641
|
+
}
|
|
1642
|
+
var BrowserEngine = class {
|
|
1643
|
+
fs;
|
|
1644
|
+
pfs;
|
|
1645
|
+
dir = "/vibecms";
|
|
1646
|
+
contentDir = "src/content";
|
|
1647
|
+
publicDir = "public";
|
|
1648
|
+
gitProxyUrl = "/api/git-proxy/";
|
|
1649
|
+
isInitializing = false;
|
|
1650
|
+
initialized = false;
|
|
1651
|
+
config;
|
|
1652
|
+
constructor() {
|
|
1653
|
+
if (typeof window !== "undefined") {
|
|
1654
|
+
try {
|
|
1655
|
+
const LightningFS = require("@isomorphic-git/lightning-fs");
|
|
1656
|
+
const FS = LightningFS.default || LightningFS;
|
|
1657
|
+
this.fs = new FS("vibecms-fs");
|
|
1658
|
+
this.pfs = this.fs.promises;
|
|
1659
|
+
} catch (e) {
|
|
1660
|
+
console.error("Failed to init lightning-fs", e);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
async init(config) {
|
|
1665
|
+
if (typeof window === "undefined") return;
|
|
1666
|
+
this.config = config;
|
|
1667
|
+
if (config?.contentDir) this.contentDir = config.contentDir;
|
|
1668
|
+
if (config?.publicDir) this.publicDir = config.publicDir;
|
|
1669
|
+
if (this.initialized || this.isInitializing) return;
|
|
1670
|
+
this.isInitializing = true;
|
|
1671
|
+
const git = await import("isomorphic-git");
|
|
1672
|
+
const http = (await import("isomorphic-git/http/web")).default;
|
|
1673
|
+
try {
|
|
1674
|
+
await this.pfs.readdir(this.dir);
|
|
1675
|
+
this.initialized = true;
|
|
1676
|
+
this.isInitializing = false;
|
|
1677
|
+
} catch (e) {
|
|
1678
|
+
try {
|
|
1679
|
+
await git.clone({
|
|
1680
|
+
fs: this.fs,
|
|
1681
|
+
http,
|
|
1682
|
+
dir: this.dir,
|
|
1683
|
+
corsProxy: config?.corsProxy || this.gitProxyUrl,
|
|
1684
|
+
url: config?.repoUrl || "",
|
|
1685
|
+
singleBranch: true,
|
|
1686
|
+
depth: 1
|
|
1687
|
+
});
|
|
1688
|
+
this.initialized = true;
|
|
1689
|
+
} catch (cloneErr) {
|
|
1690
|
+
console.error("Failed to clone repository:", cloneErr);
|
|
1691
|
+
} finally {
|
|
1692
|
+
this.isInitializing = false;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
resolveFilename(slug, locale) {
|
|
1697
|
+
const base = slug.endsWith(".json") ? slug.replace(/\.json$/, "") : slug;
|
|
1698
|
+
return locale ? `${base}.${locale}.json` : `${base}.json`;
|
|
1699
|
+
}
|
|
1700
|
+
async read(collection, slug, options) {
|
|
1701
|
+
if (typeof window === "undefined" || !this.pfs) return null;
|
|
1702
|
+
try {
|
|
1703
|
+
const filename = this.resolveFilename(slug, options?.locale);
|
|
1704
|
+
const filePath = `${this.dir}/${this.contentDir}/${collection}/${filename}`;
|
|
1705
|
+
const content = await this.pfs.readFile(filePath, { encoding: "utf8" });
|
|
1706
|
+
return JSON.parse(content);
|
|
1707
|
+
} catch (e) {
|
|
1708
|
+
return null;
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
async write(collection, slug, data, options) {
|
|
1712
|
+
if (typeof window === "undefined" || !this.pfs) return;
|
|
1713
|
+
const validation = validateCollectionData(collection, data);
|
|
1714
|
+
if (!validation.success) {
|
|
1715
|
+
throw new Error(
|
|
1716
|
+
`Schema validation failed for ${collection}/${slug}: ${validation.errors.join("; ")}`
|
|
1717
|
+
);
|
|
1718
|
+
}
|
|
1719
|
+
const filename = this.resolveFilename(slug, options?.locale);
|
|
1720
|
+
const dirPath = `${this.dir}/${this.contentDir}/${collection}`;
|
|
1721
|
+
const filePath = `${dirPath}/${filename}`;
|
|
1722
|
+
const parts = dirPath.split("/").filter(Boolean);
|
|
1723
|
+
let currentPath = "";
|
|
1724
|
+
for (const part of parts) {
|
|
1725
|
+
currentPath += `/${part}`;
|
|
1726
|
+
try {
|
|
1727
|
+
await this.pfs.mkdir(currentPath);
|
|
1728
|
+
} catch (e) {
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
await this.pfs.writeFile(filePath, JSON.stringify(validation.data, null, 2), "utf8");
|
|
1732
|
+
}
|
|
1733
|
+
async delete(collection, slug, options) {
|
|
1734
|
+
if (typeof window === "undefined" || !this.pfs) return;
|
|
1735
|
+
const baseDir = `${this.dir}/${this.contentDir}`;
|
|
1736
|
+
const cols = await this.pfs.readdir(baseDir);
|
|
1737
|
+
for (const col of cols) {
|
|
1738
|
+
if (col === collection) continue;
|
|
1739
|
+
const colDir = `${baseDir}/${col}`;
|
|
1740
|
+
try {
|
|
1741
|
+
const files = await this.pfs.readdir(colDir);
|
|
1742
|
+
for (const file of files) {
|
|
1743
|
+
if (!file.endsWith(".json")) continue;
|
|
1744
|
+
const raw = await this.pfs.readFile(`${colDir}/${file}`, { encoding: "utf8" });
|
|
1745
|
+
try {
|
|
1746
|
+
const data = JSON.parse(raw);
|
|
1747
|
+
if (hasReferenceToSlug2(data, slug)) {
|
|
1748
|
+
throw new Error(
|
|
1749
|
+
`Referential Integrity Error: Cannot delete "${slug}" because it is referenced by ${col}/${file}`
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1752
|
+
} catch (e) {
|
|
1753
|
+
if (e.message.startsWith("Referential Integrity")) throw e;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
} catch (e) {
|
|
1757
|
+
if (e.message?.startsWith("Referential Integrity")) throw e;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
const filename = this.resolveFilename(slug, options?.locale);
|
|
1761
|
+
await this.pfs.unlink(`${baseDir}/${collection}/${filename}`);
|
|
1762
|
+
}
|
|
1763
|
+
async isDirty(collection, slug) {
|
|
1764
|
+
if (typeof window === "undefined" || !this.pfs) return false;
|
|
1765
|
+
try {
|
|
1766
|
+
const git = await import("isomorphic-git");
|
|
1767
|
+
const relativeFilePath = `${this.contentDir}/${collection}/${slug}.json`;
|
|
1768
|
+
const status = await git.status({ fs: this.fs, dir: this.dir, filepath: relativeFilePath });
|
|
1769
|
+
return status !== "unmodified" && status !== "ignored";
|
|
1770
|
+
} catch (e) {
|
|
1771
|
+
return false;
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
async revert(collection, slug) {
|
|
1775
|
+
if (typeof window === "undefined" || !this.pfs) return;
|
|
1776
|
+
try {
|
|
1777
|
+
const git = await import("isomorphic-git");
|
|
1778
|
+
const relativeFilePath = `${this.contentDir}/${collection}/${slug}.json`;
|
|
1779
|
+
await git.checkout({
|
|
1780
|
+
fs: this.fs,
|
|
1781
|
+
dir: this.dir,
|
|
1782
|
+
filepaths: [relativeFilePath],
|
|
1783
|
+
force: true
|
|
1784
|
+
});
|
|
1785
|
+
} catch (e) {
|
|
1786
|
+
console.error("Failed to revert", e);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
async commit(message, author) {
|
|
1790
|
+
if (typeof window === "undefined" || !this.pfs) return;
|
|
1791
|
+
const git = await import("isomorphic-git");
|
|
1792
|
+
const commitAuthor = author || DEFAULT_AUTHOR;
|
|
1793
|
+
const listFiles = async (dirPath) => {
|
|
1794
|
+
const entries = await this.pfs.readdir(dirPath);
|
|
1795
|
+
let files = [];
|
|
1796
|
+
for (const entry of entries) {
|
|
1797
|
+
if (entry === ".git") continue;
|
|
1798
|
+
const internalPath = `${dirPath}/${entry}`;
|
|
1799
|
+
const stat = await this.pfs.stat(internalPath);
|
|
1800
|
+
if (stat.isDirectory()) {
|
|
1801
|
+
files = files.concat(await listFiles(internalPath));
|
|
1802
|
+
} else if (entry.endsWith(".json")) {
|
|
1803
|
+
files.push(internalPath.replace(`${this.dir}/`, ""));
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
return files;
|
|
1807
|
+
};
|
|
1808
|
+
try {
|
|
1809
|
+
const formFiles = await listFiles(`${this.dir}/${this.contentDir}`);
|
|
1810
|
+
for (const filepath of formFiles) {
|
|
1811
|
+
await git.add({ fs: this.fs, dir: this.dir, filepath });
|
|
1812
|
+
}
|
|
1813
|
+
await git.commit({
|
|
1814
|
+
fs: this.fs,
|
|
1815
|
+
dir: this.dir,
|
|
1816
|
+
author: { name: commitAuthor.name, email: commitAuthor.email },
|
|
1817
|
+
message: message || "CMS Content Update"
|
|
1818
|
+
});
|
|
1819
|
+
} catch (e) {
|
|
1820
|
+
console.error("Commit failed", e);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
async push(oauthToken) {
|
|
1824
|
+
if (typeof window === "undefined" || !this.pfs) return;
|
|
1825
|
+
const git = await import("isomorphic-git");
|
|
1826
|
+
const http = (await import("isomorphic-git/http/web")).default;
|
|
1827
|
+
await git.push({
|
|
1828
|
+
fs: this.fs,
|
|
1829
|
+
http,
|
|
1830
|
+
dir: this.dir,
|
|
1831
|
+
remote: "origin",
|
|
1832
|
+
ref: "main",
|
|
1833
|
+
onAuth: () => ({ oauth2format: "github", password: oauthToken })
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
async pull() {
|
|
1837
|
+
if (typeof window === "undefined" || !this.pfs) return;
|
|
1838
|
+
const git = await import("isomorphic-git");
|
|
1839
|
+
const http = (await import("isomorphic-git/http/web")).default;
|
|
1840
|
+
await git.pull({
|
|
1841
|
+
fs: this.fs,
|
|
1842
|
+
http,
|
|
1843
|
+
dir: this.dir,
|
|
1844
|
+
ref: "main",
|
|
1845
|
+
singleBranch: true,
|
|
1846
|
+
corsProxy: this.gitProxyUrl,
|
|
1847
|
+
author: { name: DEFAULT_AUTHOR.name, email: DEFAULT_AUTHOR.email }
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
async writeMedia(file, author) {
|
|
1851
|
+
if (this.config?.media?.driver) {
|
|
1852
|
+
return this.config.media.driver.upload(file, { engine: this, author });
|
|
1853
|
+
}
|
|
1854
|
+
if (typeof window === "undefined" || !this.pfs) return "";
|
|
1855
|
+
const git = await import("isomorphic-git");
|
|
1856
|
+
const dirPath = `${this.dir}/${this.publicDir}/assets/uploads`;
|
|
1857
|
+
try {
|
|
1858
|
+
const parts = `${this.publicDir}/assets/uploads`.split("/").filter(Boolean);
|
|
1859
|
+
let currentPath = this.dir;
|
|
1860
|
+
for (const part of parts) {
|
|
1861
|
+
currentPath += `/${part}`;
|
|
1862
|
+
try {
|
|
1863
|
+
await this.pfs.mkdir(currentPath);
|
|
1864
|
+
} catch (e) {
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
} catch (e) {
|
|
1868
|
+
}
|
|
1869
|
+
const safeName = file.name.replace(/[^a-z0-9.-]/gi, "_").toLowerCase();
|
|
1870
|
+
const filePath = `${dirPath}/${safeName}`;
|
|
1871
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
1872
|
+
const buffer = new Uint8Array(arrayBuffer);
|
|
1873
|
+
await this.pfs.writeFile(filePath, buffer);
|
|
1874
|
+
await git.add({ fs: this.fs, dir: this.dir, filepath: `${this.publicDir}/assets/uploads/${safeName}` });
|
|
1875
|
+
const commitAuthor = author || DEFAULT_AUTHOR;
|
|
1876
|
+
await git.commit({
|
|
1877
|
+
fs: this.fs,
|
|
1878
|
+
dir: this.dir,
|
|
1879
|
+
author: { name: commitAuthor.name, email: commitAuthor.email },
|
|
1880
|
+
message: `Upload media ${safeName}`
|
|
1881
|
+
});
|
|
1882
|
+
try {
|
|
1883
|
+
const blob = new Blob([buffer], { type: file.type });
|
|
1884
|
+
const src = URL.createObjectURL(blob);
|
|
1885
|
+
const img = new Image();
|
|
1886
|
+
img.src = src;
|
|
1887
|
+
await new Promise((resolve) => {
|
|
1888
|
+
img.onload = resolve;
|
|
1889
|
+
img.onerror = resolve;
|
|
1890
|
+
});
|
|
1891
|
+
return {
|
|
1892
|
+
url: `/assets/uploads/${safeName}`,
|
|
1893
|
+
width: img.naturalWidth,
|
|
1894
|
+
height: img.naturalHeight,
|
|
1895
|
+
alt: file.name
|
|
1896
|
+
};
|
|
1897
|
+
} catch {
|
|
1898
|
+
return `/assets/uploads/${safeName}`;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
async pruneMedia() {
|
|
1902
|
+
return { deletedCount: 0, bytesFreed: 0 };
|
|
1903
|
+
}
|
|
1904
|
+
async listMedia() {
|
|
1905
|
+
if (this.config?.media?.driver?.list) {
|
|
1906
|
+
return this.config.media.driver.list({ engine: this });
|
|
1907
|
+
}
|
|
1908
|
+
if (typeof window === "undefined" || !this.pfs) return [];
|
|
1909
|
+
try {
|
|
1910
|
+
const dirPath = `${this.dir}/${this.publicDir}/assets/uploads`;
|
|
1911
|
+
const files = await this.pfs.readdir(dirPath);
|
|
1912
|
+
return files.filter((f) => !f.startsWith(".")).map((f) => `/assets/uploads/${f}`);
|
|
1913
|
+
} catch {
|
|
1914
|
+
return [];
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
async deleteMedia(filename) {
|
|
1918
|
+
if (this.config?.media?.driver?.delete) {
|
|
1919
|
+
return this.config.media.driver.delete(filename, { engine: this });
|
|
1920
|
+
}
|
|
1921
|
+
if (typeof window === "undefined" || !this.pfs) return;
|
|
1922
|
+
const safeName = filename.split("/").pop() || filename;
|
|
1923
|
+
const filePath = `${this.dir}/${this.publicDir}/assets/uploads/${safeName}`;
|
|
1924
|
+
try {
|
|
1925
|
+
await this.pfs.unlink(filePath);
|
|
1926
|
+
const git = await import("isomorphic-git");
|
|
1927
|
+
await git.remove({ fs: this.fs, dir: this.dir, filepath: `${this.publicDir}/assets/uploads/${safeName}` });
|
|
1928
|
+
} catch {
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
async renameMedia(oldFilename, newFilename) {
|
|
1932
|
+
if (this.config?.media?.driver?.rename) {
|
|
1933
|
+
return this.config.media.driver.rename(oldFilename, newFilename, { engine: this });
|
|
1934
|
+
}
|
|
1935
|
+
if (typeof window === "undefined" || !this.pfs) return;
|
|
1936
|
+
const safeOldName = oldFilename.split("/").pop() || oldFilename;
|
|
1937
|
+
const safeNewName = newFilename.split("/").pop() || newFilename;
|
|
1938
|
+
const oldPath = `${this.dir}/${this.publicDir}/assets/uploads/${safeOldName}`;
|
|
1939
|
+
const newPath = `${this.dir}/${this.publicDir}/assets/uploads/${safeNewName}`;
|
|
1940
|
+
try {
|
|
1941
|
+
const buffer = await this.pfs.readFile(oldPath);
|
|
1942
|
+
await this.pfs.writeFile(newPath, buffer);
|
|
1943
|
+
await this.pfs.unlink(oldPath);
|
|
1944
|
+
const git = await import("isomorphic-git");
|
|
1945
|
+
await git.remove({ fs: this.fs, dir: this.dir, filepath: `${this.publicDir}/assets/uploads/${safeOldName}` });
|
|
1946
|
+
await git.add({ fs: this.fs, dir: this.dir, filepath: `${this.publicDir}/assets/uploads/${safeNewName}` });
|
|
1947
|
+
} catch {
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
async getMediaUrl(relativePath) {
|
|
1951
|
+
if (typeof window === "undefined" || !this.pfs) return relativePath;
|
|
1952
|
+
try {
|
|
1953
|
+
const filePath = `${this.dir}/${this.publicDir}${relativePath}`;
|
|
1954
|
+
const uint8Array = await this.pfs.readFile(filePath);
|
|
1955
|
+
const ext = relativePath.split(".").pop()?.toLowerCase();
|
|
1956
|
+
let mime = "image/png";
|
|
1957
|
+
if (ext === "jpg" || ext === "jpeg") mime = "image/jpeg";
|
|
1958
|
+
else if (ext === "svg") mime = "image/svg+xml";
|
|
1959
|
+
else if (ext === "gif") mime = "image/gif";
|
|
1960
|
+
else if (ext === "webp") mime = "image/webp";
|
|
1961
|
+
const blob = new Blob([uint8Array], { type: mime });
|
|
1962
|
+
return URL.createObjectURL(blob);
|
|
1963
|
+
} catch (e) {
|
|
1964
|
+
return relativePath;
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
async list(collection, options) {
|
|
1968
|
+
if (typeof window === "undefined" || !this.pfs) return [];
|
|
1969
|
+
try {
|
|
1970
|
+
const files = await this.pfs.readdir(`${this.dir}/${this.contentDir}/${collection}`);
|
|
1971
|
+
const slugs = files.filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
|
|
1972
|
+
if (options?.withMeta) {
|
|
1973
|
+
const metaList = await Promise.all(slugs.map(async (slug) => {
|
|
1974
|
+
try {
|
|
1975
|
+
const content = await this.pfs.readFile(`${this.dir}/${this.contentDir}/${collection}/${slug}.json`, { encoding: "utf8" });
|
|
1976
|
+
const data = JSON.parse(content);
|
|
1977
|
+
return { slug, title: data.title || data.name || slug, ...data };
|
|
1978
|
+
} catch (e) {
|
|
1979
|
+
return { slug, title: slug };
|
|
1980
|
+
}
|
|
1981
|
+
}));
|
|
1982
|
+
return metaList;
|
|
1983
|
+
}
|
|
1984
|
+
return slugs;
|
|
1985
|
+
} catch {
|
|
1986
|
+
return [];
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
async getHistory(collection, slug) {
|
|
1990
|
+
if (typeof window === "undefined" || !this.pfs) return [];
|
|
1991
|
+
try {
|
|
1992
|
+
const git = await import("isomorphic-git");
|
|
1993
|
+
const relativeFilePath = `${this.contentDir}/${collection}/${slug}.json`;
|
|
1994
|
+
const commits = await git.log({
|
|
1995
|
+
fs: this.fs,
|
|
1996
|
+
dir: this.dir,
|
|
1997
|
+
filepath: relativeFilePath
|
|
1998
|
+
});
|
|
1999
|
+
return commits.map((c) => ({
|
|
2000
|
+
oid: c.oid,
|
|
2001
|
+
message: c.commit.message,
|
|
2002
|
+
author: c.commit.author.name,
|
|
2003
|
+
email: c.commit.author.email,
|
|
2004
|
+
timestamp: c.commit.author.timestamp * 1e3
|
|
2005
|
+
}));
|
|
2006
|
+
} catch (e) {
|
|
2007
|
+
return [];
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
async getVersionContent(commitOid, collection, slug) {
|
|
2011
|
+
if (typeof window === "undefined" || !this.pfs) return null;
|
|
2012
|
+
try {
|
|
2013
|
+
const git = await import("isomorphic-git");
|
|
2014
|
+
const relativeFilePath = `${this.contentDir}/${collection}/${slug}.json`;
|
|
2015
|
+
const { blob } = await git.readBlob({
|
|
2016
|
+
fs: this.fs,
|
|
2017
|
+
dir: this.dir,
|
|
2018
|
+
oid: commitOid,
|
|
2019
|
+
filepath: relativeFilePath
|
|
2020
|
+
});
|
|
2021
|
+
const content = new TextDecoder("utf-8").decode(blob);
|
|
2022
|
+
return JSON.parse(content);
|
|
2023
|
+
} catch (e) {
|
|
2024
|
+
return null;
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
};
|
|
2028
|
+
|
|
2029
|
+
// src/engine/engine-dev.ts
|
|
2030
|
+
var DevEngine = class {
|
|
2031
|
+
apiBase = "/__vibecms/api";
|
|
2032
|
+
config;
|
|
2033
|
+
async init(config) {
|
|
2034
|
+
this.config = config;
|
|
2035
|
+
if (config?.devMode === false) {
|
|
2036
|
+
console.warn("DevEngine initialized but devMode is false");
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
async fetchApi(method, body) {
|
|
2040
|
+
const res = await fetch(this.apiBase, {
|
|
2041
|
+
method: "POST",
|
|
2042
|
+
headers: { "Content-Type": "application/json" },
|
|
2043
|
+
body: JSON.stringify({ method, ...body })
|
|
2044
|
+
});
|
|
2045
|
+
if (!res.ok) {
|
|
2046
|
+
const err = await res.json();
|
|
2047
|
+
throw new Error(err.message || "API Error");
|
|
2048
|
+
}
|
|
2049
|
+
return res.json();
|
|
2050
|
+
}
|
|
2051
|
+
async read(collection, slug, options) {
|
|
2052
|
+
return this.fetchApi("read", { collection, slug, options }).catch(() => null);
|
|
2053
|
+
}
|
|
2054
|
+
async write(collection, slug, data, options) {
|
|
2055
|
+
await this.fetchApi("write", { collection, slug, data, options });
|
|
2056
|
+
}
|
|
2057
|
+
async delete(collection, slug, options) {
|
|
2058
|
+
await this.fetchApi("delete", { collection, slug, options });
|
|
2059
|
+
}
|
|
2060
|
+
async isDirty() {
|
|
2061
|
+
return false;
|
|
2062
|
+
}
|
|
2063
|
+
async revert() {
|
|
2064
|
+
}
|
|
2065
|
+
async commit(_message, _author) {
|
|
2066
|
+
}
|
|
2067
|
+
async push() {
|
|
2068
|
+
}
|
|
2069
|
+
async pull() {
|
|
2070
|
+
}
|
|
2071
|
+
async writeMedia(file, author) {
|
|
2072
|
+
if (this.config?.media?.driver) {
|
|
2073
|
+
return this.config.media.driver.upload(file, { engine: this, author });
|
|
2074
|
+
}
|
|
2075
|
+
const formData = new FormData();
|
|
2076
|
+
formData.append("file", file);
|
|
2077
|
+
formData.append("method", "writeMedia");
|
|
2078
|
+
const res = await fetch(this.apiBase, {
|
|
2079
|
+
method: "POST",
|
|
2080
|
+
body: formData
|
|
2081
|
+
});
|
|
2082
|
+
if (!res.ok) throw new Error("Failed to upload media");
|
|
2083
|
+
const data = await res.json();
|
|
2084
|
+
return data.url && data.width ? data : data.url;
|
|
2085
|
+
}
|
|
2086
|
+
async listMedia() {
|
|
2087
|
+
if (this.config?.media?.driver?.list) {
|
|
2088
|
+
return this.config.media.driver.list({ engine: this });
|
|
2089
|
+
}
|
|
2090
|
+
const res = await this.fetchApi("listMedia");
|
|
2091
|
+
return res.files || [];
|
|
2092
|
+
}
|
|
2093
|
+
async deleteMedia(filename) {
|
|
2094
|
+
if (this.config?.media?.driver?.delete) {
|
|
2095
|
+
return this.config.media.driver.delete(filename, { engine: this });
|
|
2096
|
+
}
|
|
2097
|
+
await this.fetchApi("deleteMedia", { file: filename });
|
|
2098
|
+
}
|
|
2099
|
+
async renameMedia(oldFilename, newFilename) {
|
|
2100
|
+
if (this.config?.media?.driver?.rename) {
|
|
2101
|
+
return this.config.media.driver.rename(oldFilename, newFilename, { engine: this });
|
|
2102
|
+
}
|
|
2103
|
+
await this.fetchApi("renameMedia", { oldFile: oldFilename, newFile: newFilename });
|
|
2104
|
+
}
|
|
2105
|
+
async pruneMedia() {
|
|
2106
|
+
return this.fetchApi("pruneMedia");
|
|
2107
|
+
}
|
|
2108
|
+
async getMediaUrl(relativePath) {
|
|
2109
|
+
return relativePath;
|
|
2110
|
+
}
|
|
2111
|
+
async list(collection, options) {
|
|
2112
|
+
return this.fetchApi("list", { collection, options });
|
|
2113
|
+
}
|
|
2114
|
+
async getHistory(_collection, _slug) {
|
|
2115
|
+
return this.fetchApi("getHistory", { collection: _collection, slug: _slug }).catch(() => []);
|
|
2116
|
+
}
|
|
2117
|
+
async getVersionContent(commitOid, collection, slug) {
|
|
2118
|
+
return this.fetchApi("getVersionContent", { commitOid, collection, slug }).catch(() => null);
|
|
2119
|
+
}
|
|
2120
|
+
};
|
|
2121
|
+
|
|
2122
|
+
// src/license.ts
|
|
2123
|
+
var VIBE_PUBLIC_KEY_B64 = "MCowBQYDK2VwAyEAhoV+ydoo3GG0wM0tjBmtBWKMaHo7gVg25J5lsSfoBI0=";
|
|
2124
|
+
var VALIDATION_API = "https://api.vibecms.com/v1/license/validate";
|
|
2125
|
+
var _remoteCache = null;
|
|
2126
|
+
var REMOTE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2127
|
+
var _revokedAt = null;
|
|
2128
|
+
var GRACE_PERIOD_MS = 72 * 60 * 60 * 1e3;
|
|
2129
|
+
function base64urlDecode(str) {
|
|
2130
|
+
let b64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
2131
|
+
while (b64.length % 4 !== 0) b64 += "=";
|
|
2132
|
+
if (typeof Buffer !== "undefined") {
|
|
2133
|
+
return new Uint8Array(Buffer.from(b64, "base64"));
|
|
2134
|
+
}
|
|
2135
|
+
const binary = atob(b64);
|
|
2136
|
+
const bytes = new Uint8Array(binary.length);
|
|
2137
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
2138
|
+
return bytes;
|
|
2139
|
+
}
|
|
2140
|
+
function validateLicenseLocal(key) {
|
|
2141
|
+
if (!key || !key.startsWith("vibe_")) {
|
|
2142
|
+
throw new Error(
|
|
2143
|
+
'[VibeCMS] Invalid license key format. Keys start with "vibe_".\nGet your key at https://vibecms.com/pricing'
|
|
2144
|
+
);
|
|
2145
|
+
}
|
|
2146
|
+
const body = key.slice(5);
|
|
2147
|
+
const dotIndex = body.lastIndexOf(".");
|
|
2148
|
+
if (dotIndex === -1) {
|
|
2149
|
+
throw new Error(
|
|
2150
|
+
"[VibeCMS] Malformed license key. Expected format: vibe_<payload>.<signature>\nGet a new key at https://vibecms.com/pricing"
|
|
2151
|
+
);
|
|
2152
|
+
}
|
|
2153
|
+
const payloadB64 = body.slice(0, dotIndex);
|
|
2154
|
+
const signatureB64 = body.slice(dotIndex + 1);
|
|
2155
|
+
let payload;
|
|
2156
|
+
try {
|
|
2157
|
+
const payloadBytes = base64urlDecode(payloadB64);
|
|
2158
|
+
const payloadJson = new TextDecoder().decode(payloadBytes);
|
|
2159
|
+
payload = JSON.parse(payloadJson);
|
|
2160
|
+
} catch {
|
|
2161
|
+
throw new Error(
|
|
2162
|
+
"[VibeCMS] Invalid license key. Could not decode payload.\nCheck your key or get a new one at https://vibecms.com/pricing"
|
|
2163
|
+
);
|
|
2164
|
+
}
|
|
2165
|
+
if (!payload.sub || !payload.plan || !payload.iat || !payload.exp || !payload.kid) {
|
|
2166
|
+
throw new Error(
|
|
2167
|
+
"[VibeCMS] Invalid license key. Missing required fields.\nGet a new key at https://vibecms.com/pricing"
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2170
|
+
try {
|
|
2171
|
+
const crypto2 = require("crypto");
|
|
2172
|
+
const pubKeyDer = Buffer.from(VIBE_PUBLIC_KEY_B64, "base64");
|
|
2173
|
+
const publicKey = crypto2.createPublicKey({
|
|
2174
|
+
key: pubKeyDer,
|
|
2175
|
+
format: "der",
|
|
2176
|
+
type: "spki"
|
|
2177
|
+
});
|
|
2178
|
+
const payloadBytes = base64urlDecode(payloadB64);
|
|
2179
|
+
const signatureBytes = base64urlDecode(signatureB64);
|
|
2180
|
+
const isValid = crypto2.verify(null, payloadBytes, publicKey, signatureBytes);
|
|
2181
|
+
if (!isValid) {
|
|
2182
|
+
throw new Error("signature mismatch");
|
|
2183
|
+
}
|
|
2184
|
+
} catch (e) {
|
|
2185
|
+
if (e.message?.includes("[VibeCMS]")) throw e;
|
|
2186
|
+
throw new Error(
|
|
2187
|
+
"[VibeCMS] Invalid license key. Signature verification failed.\nCheck your key or get a new one at https://vibecms.com/pricing"
|
|
2188
|
+
);
|
|
2189
|
+
}
|
|
2190
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
2191
|
+
if (payload.exp < nowSec) {
|
|
2192
|
+
const expDate = new Date(payload.exp * 1e3).toLocaleDateString("en-US", {
|
|
2193
|
+
year: "numeric",
|
|
2194
|
+
month: "long",
|
|
2195
|
+
day: "numeric"
|
|
2196
|
+
});
|
|
2197
|
+
throw new Error(
|
|
2198
|
+
`[VibeCMS] License expired on ${expDate}.
|
|
2199
|
+
Renew at https://vibecms.com/account`
|
|
2200
|
+
);
|
|
2201
|
+
}
|
|
2202
|
+
return payload;
|
|
2203
|
+
}
|
|
2204
|
+
async function validateLicenseRemote(key, version) {
|
|
2205
|
+
if (_remoteCache && Date.now() - _remoteCache.checkedAt < REMOTE_CACHE_TTL_MS) {
|
|
2206
|
+
if (!_remoteCache.valid) {
|
|
2207
|
+
handleRevocation();
|
|
2208
|
+
}
|
|
2209
|
+
return;
|
|
2210
|
+
}
|
|
2211
|
+
try {
|
|
2212
|
+
const res = await fetch(VALIDATION_API, {
|
|
2213
|
+
method: "POST",
|
|
2214
|
+
headers: { "Content-Type": "application/json" },
|
|
2215
|
+
body: JSON.stringify({ key, version }),
|
|
2216
|
+
signal: AbortSignal.timeout(5e3)
|
|
2217
|
+
// 5s timeout
|
|
2218
|
+
});
|
|
2219
|
+
if (res.ok) {
|
|
2220
|
+
const data = await res.json();
|
|
2221
|
+
_remoteCache = {
|
|
2222
|
+
valid: data.valid !== false,
|
|
2223
|
+
reason: data.reason,
|
|
2224
|
+
checkedAt: Date.now()
|
|
2225
|
+
};
|
|
2226
|
+
if (!_remoteCache.valid) {
|
|
2227
|
+
handleRevocation();
|
|
2228
|
+
} else {
|
|
2229
|
+
_revokedAt = null;
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
} catch {
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
function handleRevocation() {
|
|
2236
|
+
if (!_revokedAt) {
|
|
2237
|
+
_revokedAt = Date.now();
|
|
2238
|
+
console.warn(
|
|
2239
|
+
"[VibeCMS] \u26A0 Warning: Your license key has been revoked.\nVibeCMS will stop working in 72 hours.\nContact support@vibecms.com or visit https://vibecms.com/account"
|
|
2240
|
+
);
|
|
2241
|
+
}
|
|
2242
|
+
const elapsed = Date.now() - _revokedAt;
|
|
2243
|
+
if (elapsed > GRACE_PERIOD_MS) {
|
|
2244
|
+
throw new Error(
|
|
2245
|
+
"[VibeCMS] License has been revoked and the grace period has expired.\nContact support@vibecms.com or get a new key at https://vibecms.com/pricing"
|
|
2246
|
+
);
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
function resolveLicenseKey(configKey) {
|
|
2250
|
+
const key = (typeof process !== "undefined" ? process.env?.VIBE_LICENSE_KEY : void 0) ?? configKey;
|
|
2251
|
+
if (!key) {
|
|
2252
|
+
throw new Error(
|
|
2253
|
+
"[VibeCMS] License key required.\nSet the VIBE_LICENSE_KEY environment variable or pass licenseKey in defineConfig().\nGet your free key at https://vibecms.com/pricing"
|
|
2254
|
+
);
|
|
2255
|
+
}
|
|
2256
|
+
return key;
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
// src/engine/index.ts
|
|
2260
|
+
var _licensed = false;
|
|
2261
|
+
var getEngine = () => {
|
|
2262
|
+
if (typeof window === "undefined") return new NodeEngine();
|
|
2263
|
+
const isLocal = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1";
|
|
2264
|
+
return isLocal ? new DevEngine() : new BrowserEngine();
|
|
2265
|
+
};
|
|
2266
|
+
var _raw = getEngine();
|
|
2267
|
+
var VibeEngine = new Proxy(_raw, {
|
|
2268
|
+
get(target, prop, receiver) {
|
|
2269
|
+
const value = Reflect.get(target, prop, receiver);
|
|
2270
|
+
if (!_licensed && typeof value === "function" && prop !== "init") {
|
|
2271
|
+
return (...args) => {
|
|
2272
|
+
throw new Error(
|
|
2273
|
+
"[VibeCMS] License key required. Call initVibeEngine() with a valid key.\nGet your key at https://vibecms.com/pricing"
|
|
2274
|
+
);
|
|
2275
|
+
};
|
|
2276
|
+
}
|
|
2277
|
+
return value;
|
|
2278
|
+
}
|
|
2279
|
+
});
|
|
2280
|
+
function initVibeEngine(config, collections, singletons) {
|
|
2281
|
+
const key = resolveLicenseKey(config.licenseKey);
|
|
2282
|
+
validateLicenseLocal(key);
|
|
2283
|
+
_licensed = true;
|
|
2284
|
+
validateLicenseRemote(key, "0.1.0").catch(() => {
|
|
2285
|
+
});
|
|
2286
|
+
if (collections || singletons) {
|
|
2287
|
+
registerCollectionSchemas(collections, singletons);
|
|
2288
|
+
}
|
|
2289
|
+
return VibeEngine.init(config);
|
|
2290
|
+
}
|
|
2291
|
+
async function populate(document2, refs) {
|
|
2292
|
+
if (!document2) return document2;
|
|
2293
|
+
const doc = { ...document2 };
|
|
2294
|
+
for (const ref of refs) {
|
|
2295
|
+
if (typeof doc[ref.field] === "string") {
|
|
2296
|
+
const data = await VibeEngine.read(ref.collection, doc[ref.field]);
|
|
2297
|
+
if (data) doc[ref.field] = data;
|
|
2298
|
+
} else if (Array.isArray(doc[ref.field])) {
|
|
2299
|
+
doc[ref.field] = await Promise.all(doc[ref.field].map(async (item) => {
|
|
2300
|
+
if (typeof item === "string") {
|
|
2301
|
+
const data = await VibeEngine.read(ref.collection, item);
|
|
2302
|
+
return data || item;
|
|
2303
|
+
}
|
|
2304
|
+
return item;
|
|
2305
|
+
}));
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
return doc;
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
// src/hooks/use-vibe-content.ts
|
|
2312
|
+
var import_react = require("react");
|
|
2313
|
+
function useVibeContent(initialData, collection, slug) {
|
|
2314
|
+
const [data, setData] = (0, import_react.useState)(initialData);
|
|
2315
|
+
(0, import_react.useEffect)(() => {
|
|
2316
|
+
const isEditor = typeof window !== "undefined" && (window.location.search.includes("vibe-editor=true") || window.VIBE_EDITOR_ACTIVE || sessionStorage.getItem("vibe_edit") === "true");
|
|
2317
|
+
if (!isEditor) return;
|
|
2318
|
+
const handleUpdate = (event) => {
|
|
2319
|
+
const eventSlug = event.detail.slug?.replace(".json", "");
|
|
2320
|
+
const targetSlug = slug.replace(".json", "");
|
|
2321
|
+
if (event.detail.collection === collection && eventSlug === targetSlug) {
|
|
2322
|
+
setData(event.detail.data);
|
|
2323
|
+
}
|
|
2324
|
+
};
|
|
2325
|
+
window.addEventListener("vibe-content-update", handleUpdate);
|
|
2326
|
+
return () => window.removeEventListener("vibe-content-update", handleUpdate);
|
|
2327
|
+
}, [collection, slug]);
|
|
2328
|
+
return data;
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
// src/hooks/use-vibe-document.ts
|
|
2332
|
+
var import_react2 = require("react");
|
|
2333
|
+
function useVibeDocument(initialData, collection, slug) {
|
|
2334
|
+
const [data, setData] = (0, import_react2.useState)(initialData);
|
|
2335
|
+
(0, import_react2.useEffect)(() => {
|
|
2336
|
+
setData(initialData);
|
|
2337
|
+
}, [initialData]);
|
|
2338
|
+
(0, import_react2.useEffect)(() => {
|
|
2339
|
+
const isEditor = typeof window !== "undefined" && (window.location.search.includes("vibe-editor=true") || window.VIBE_EDITOR_ACTIVE || sessionStorage.getItem("vibe_edit") === "true");
|
|
2340
|
+
if (!isEditor) return;
|
|
2341
|
+
const handleUpdate = (event) => {
|
|
2342
|
+
const eventSlug = event.detail.slug?.replace(".json", "");
|
|
2343
|
+
const targetSlug = slug.replace(".json", "");
|
|
2344
|
+
if (event.detail.collection === collection && eventSlug === targetSlug) {
|
|
2345
|
+
setData(event.detail.data);
|
|
2346
|
+
}
|
|
2347
|
+
};
|
|
2348
|
+
window.addEventListener("vibe-content-update", handleUpdate);
|
|
2349
|
+
return () => window.removeEventListener("vibe-content-update", handleUpdate);
|
|
2350
|
+
}, [collection, slug]);
|
|
2351
|
+
return data;
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
// src/schema/mixins.ts
|
|
2355
|
+
var import_zod = require("zod");
|
|
2356
|
+
var seoSchema = import_zod.z.object({
|
|
2357
|
+
seoTitle: vibeField(import_zod.z.string().optional(), "text"),
|
|
2358
|
+
seoDescription: vibeField(import_zod.z.string().optional(), "text"),
|
|
2359
|
+
ogImage: vibeField(import_zod.z.string().optional(), "image"),
|
|
2360
|
+
keywords: vibeField(import_zod.z.string().optional(), "text")
|
|
2361
|
+
});
|
|
2362
|
+
var publishingSchema = import_zod.z.object({
|
|
2363
|
+
isPublished: vibeField(import_zod.z.boolean().default(false), "boolean"),
|
|
2364
|
+
publishDate: vibeField(import_zod.z.string().optional(), "text"),
|
|
2365
|
+
scheduledPublishAt: vibeField(import_zod.z.string().optional(), "text")
|
|
2366
|
+
});
|
|
2367
|
+
var mediaGallerySchema = import_zod.z.object({
|
|
2368
|
+
images: vibeField(
|
|
2369
|
+
import_zod.z.array(
|
|
2370
|
+
import_zod.z.object({
|
|
2371
|
+
url: vibeField(import_zod.z.string(), "image"),
|
|
2372
|
+
alt: vibeField(import_zod.z.string().optional(), "text"),
|
|
2373
|
+
caption: vibeField(import_zod.z.string().optional(), "text")
|
|
2374
|
+
})
|
|
2375
|
+
).default([]),
|
|
2376
|
+
"array"
|
|
2377
|
+
)
|
|
2378
|
+
});
|
|
2379
|
+
|
|
2380
|
+
// src/schema/blocks.ts
|
|
2381
|
+
var import_zod2 = require("zod");
|
|
2382
|
+
var heroBlockSchema = import_zod2.z.object({
|
|
2383
|
+
_type: import_zod2.z.literal("hero"),
|
|
2384
|
+
title: vibeField(import_zod2.z.string(), "text"),
|
|
2385
|
+
subtitle: vibeField(import_zod2.z.string(), "rich-text"),
|
|
2386
|
+
cta: vibeField(import_zod2.z.string(), "text"),
|
|
2387
|
+
backgroundColor: vibeField(import_zod2.z.string().default("transparent"), "color-picker")
|
|
2388
|
+
});
|
|
2389
|
+
var featuresBlockSchema = import_zod2.z.object({
|
|
2390
|
+
_type: import_zod2.z.literal("features"),
|
|
2391
|
+
items: import_zod2.z.array(
|
|
2392
|
+
import_zod2.z.object({
|
|
2393
|
+
icon: vibeField(import_zod2.z.string().default("Zap"), "icon-picker"),
|
|
2394
|
+
title: vibeField(import_zod2.z.string(), "text"),
|
|
2395
|
+
description: vibeField(import_zod2.z.string(), "rich-text")
|
|
2396
|
+
})
|
|
2397
|
+
)
|
|
2398
|
+
});
|
|
2399
|
+
var logoCloudBlockSchema = import_zod2.z.object({
|
|
2400
|
+
_type: import_zod2.z.literal("logo-cloud"),
|
|
2401
|
+
title: vibeField(import_zod2.z.string().optional(), "text"),
|
|
2402
|
+
logos: import_zod2.z.array(
|
|
2403
|
+
import_zod2.z.object({
|
|
2404
|
+
image: vibeField(import_zod2.z.string(), "image"),
|
|
2405
|
+
alt: vibeField(import_zod2.z.string().optional(), "text"),
|
|
2406
|
+
link: vibeField(import_zod2.z.string().optional(), "text")
|
|
2407
|
+
})
|
|
2408
|
+
)
|
|
2409
|
+
});
|
|
2410
|
+
var faqBlockSchema = import_zod2.z.object({
|
|
2411
|
+
_type: import_zod2.z.literal("faq"),
|
|
2412
|
+
title: vibeField(import_zod2.z.string().default("Frequently Asked Questions"), "text"),
|
|
2413
|
+
items: import_zod2.z.array(
|
|
2414
|
+
import_zod2.z.object({
|
|
2415
|
+
question: vibeField(import_zod2.z.string(), "text"),
|
|
2416
|
+
answer: vibeField(import_zod2.z.string(), "rich-text")
|
|
2417
|
+
})
|
|
2418
|
+
)
|
|
2419
|
+
});
|
|
2420
|
+
var testimonialsBlockSchema = import_zod2.z.object({
|
|
2421
|
+
_type: import_zod2.z.literal("testimonials"),
|
|
2422
|
+
title: vibeField(import_zod2.z.string().optional(), "text"),
|
|
2423
|
+
items: import_zod2.z.array(
|
|
2424
|
+
import_zod2.z.object({
|
|
2425
|
+
quote: vibeField(import_zod2.z.string(), "text"),
|
|
2426
|
+
author: vibeField(import_zod2.z.string(), "text"),
|
|
2427
|
+
role: vibeField(import_zod2.z.string().optional(), "text"),
|
|
2428
|
+
image: vibeField(import_zod2.z.string().optional(), "image"),
|
|
2429
|
+
rating: vibeField(import_zod2.z.number().min(1).max(5).default(5), "number")
|
|
2430
|
+
})
|
|
2431
|
+
)
|
|
2432
|
+
});
|
|
2433
|
+
var pricingBlockSchema = import_zod2.z.object({
|
|
2434
|
+
_type: import_zod2.z.literal("pricing"),
|
|
2435
|
+
title: vibeField(import_zod2.z.string().default("Pricing Plans"), "text"),
|
|
2436
|
+
subtitle: vibeField(import_zod2.z.string().optional(), "text"),
|
|
2437
|
+
plans: import_zod2.z.array(
|
|
2438
|
+
import_zod2.z.object({
|
|
2439
|
+
name: vibeField(import_zod2.z.string(), "text"),
|
|
2440
|
+
priceMonthly: vibeField(import_zod2.z.string(), "text"),
|
|
2441
|
+
priceYearly: vibeField(import_zod2.z.string(), "text"),
|
|
2442
|
+
highlighted: vibeField(import_zod2.z.boolean().default(false), "boolean"),
|
|
2443
|
+
features: import_zod2.z.array(
|
|
2444
|
+
import_zod2.z.object({
|
|
2445
|
+
feature: vibeField(import_zod2.z.string(), "text"),
|
|
2446
|
+
included: vibeField(import_zod2.z.boolean().default(true), "boolean")
|
|
2447
|
+
})
|
|
2448
|
+
),
|
|
2449
|
+
ctaText: vibeField(import_zod2.z.string(), "text"),
|
|
2450
|
+
ctaUrl: vibeField(import_zod2.z.string(), "text")
|
|
2451
|
+
})
|
|
2452
|
+
)
|
|
2453
|
+
});
|
|
2454
|
+
var videoBlockSchema = import_zod2.z.object({
|
|
2455
|
+
_type: import_zod2.z.literal("video"),
|
|
2456
|
+
url: vibeField(import_zod2.z.string().url(), "text"),
|
|
2457
|
+
caption: vibeField(import_zod2.z.string().optional(), "text"),
|
|
2458
|
+
autoplay: vibeField(import_zod2.z.boolean().default(false), "boolean")
|
|
2459
|
+
});
|
|
2460
|
+
var formFieldSchema = import_zod2.z.object({
|
|
2461
|
+
_fieldType: import_zod2.z.enum(["text", "email", "textarea", "select", "checkbox", "radio", "number", "tel", "date", "file"]),
|
|
2462
|
+
label: vibeField(import_zod2.z.string(), "text"),
|
|
2463
|
+
name: vibeField(import_zod2.z.string(), "text"),
|
|
2464
|
+
placeholder: vibeField(import_zod2.z.string().optional(), "text"),
|
|
2465
|
+
required: vibeField(import_zod2.z.boolean().default(false), "boolean"),
|
|
2466
|
+
options: vibeField(
|
|
2467
|
+
import_zod2.z.string().optional(),
|
|
2468
|
+
"text"
|
|
2469
|
+
),
|
|
2470
|
+
// Comma-separated options for select/radio/checkbox
|
|
2471
|
+
helpText: vibeField(import_zod2.z.string().optional(), "text"),
|
|
2472
|
+
width: vibeField(import_zod2.z.enum(["full", "half"]).default("full"), "text")
|
|
2473
|
+
});
|
|
2474
|
+
var formBlockSchema = import_zod2.z.object({
|
|
2475
|
+
_type: import_zod2.z.literal("form"),
|
|
2476
|
+
title: vibeField(import_zod2.z.string().optional(), "text"),
|
|
2477
|
+
description: vibeField(import_zod2.z.string().optional(), "rich-text"),
|
|
2478
|
+
fields: import_zod2.z.array(formFieldSchema),
|
|
2479
|
+
submitLabel: vibeField(import_zod2.z.string().default("Absenden"), "text"),
|
|
2480
|
+
successMessage: vibeField(import_zod2.z.string().default("Vielen Dank! Ihre Nachricht wurde erfolgreich gesendet."), "text"),
|
|
2481
|
+
/** Where form data is sent: 'email' posts to a configured endpoint, 'webhook' sends JSON to a URL */
|
|
2482
|
+
action: vibeField(import_zod2.z.enum(["email", "webhook"]).default("email"), "text"),
|
|
2483
|
+
/** Email recipient or webhook URL */
|
|
2484
|
+
target: vibeField(import_zod2.z.string().optional(), "text"),
|
|
2485
|
+
/** DSGVO: Privacy consent checkbox text */
|
|
2486
|
+
privacyConsentText: vibeField(
|
|
2487
|
+
import_zod2.z.string().default("Ich stimme der Verarbeitung meiner Daten gem\xE4\xDF der Datenschutzerkl\xE4rung zu."),
|
|
2488
|
+
"text"
|
|
2489
|
+
),
|
|
2490
|
+
privacyConsentRequired: vibeField(import_zod2.z.boolean().default(true), "boolean")
|
|
2491
|
+
});
|
|
2492
|
+
var ctaBannerBlockSchema = import_zod2.z.object({
|
|
2493
|
+
_type: import_zod2.z.literal("cta"),
|
|
2494
|
+
title: vibeField(import_zod2.z.string(), "text"),
|
|
2495
|
+
subtitle: vibeField(import_zod2.z.string(), "rich-text"),
|
|
2496
|
+
buttonText: vibeField(import_zod2.z.string(), "text"),
|
|
2497
|
+
buttonUrl: vibeField(import_zod2.z.string(), "text"),
|
|
2498
|
+
backgroundColor: vibeField(import_zod2.z.string().default("indigo-500"), "color-picker"),
|
|
2499
|
+
textColor: vibeField(import_zod2.z.string().default("white"), "color-picker")
|
|
2500
|
+
});
|
|
2501
|
+
var blockSchema = import_zod2.z.lazy(
|
|
2502
|
+
() => import_zod2.z.discriminatedUnion("_type", [
|
|
2503
|
+
heroBlockSchema,
|
|
2504
|
+
featuresBlockSchema,
|
|
2505
|
+
gridBlockSchema,
|
|
2506
|
+
logoCloudBlockSchema,
|
|
2507
|
+
faqBlockSchema,
|
|
2508
|
+
testimonialsBlockSchema,
|
|
2509
|
+
pricingBlockSchema,
|
|
2510
|
+
videoBlockSchema,
|
|
2511
|
+
tabsBlockSchema,
|
|
2512
|
+
ctaBannerBlockSchema,
|
|
2513
|
+
formBlockSchema
|
|
2514
|
+
])
|
|
2515
|
+
);
|
|
2516
|
+
var tabsBlockSchema = import_zod2.z.object({
|
|
2517
|
+
_type: import_zod2.z.literal("tabs"),
|
|
2518
|
+
tabs: import_zod2.z.array(
|
|
2519
|
+
import_zod2.z.object({
|
|
2520
|
+
title: vibeField(import_zod2.z.string(), "text"),
|
|
2521
|
+
icon: vibeField(import_zod2.z.string().optional(), "icon-picker"),
|
|
2522
|
+
blocks: import_zod2.z.lazy(() => import_zod2.z.array(blockSchema)).default([])
|
|
2523
|
+
})
|
|
2524
|
+
)
|
|
2525
|
+
});
|
|
2526
|
+
var gridBlockSchema = import_zod2.z.object({
|
|
2527
|
+
_type: import_zod2.z.literal("grid"),
|
|
2528
|
+
columns: vibeField(import_zod2.z.enum(["1", "2", "3", "4"]).default("2"), "text"),
|
|
2529
|
+
blocks: import_zod2.z.lazy(() => import_zod2.z.array(blockSchema)).default([])
|
|
2530
|
+
});
|
|
2531
|
+
|
|
2532
|
+
// src/schema/index.ts
|
|
2533
|
+
function vibeField(schema, uiType, options) {
|
|
2534
|
+
if ((uiType === "reference" || uiType === "reference-array") && options?.collection) {
|
|
2535
|
+
return schema.describe(`ui:${uiType}:${options.collection}`);
|
|
2536
|
+
}
|
|
2537
|
+
return schema.describe(`ui:${uiType}`);
|
|
2538
|
+
}
|
|
2539
|
+
function vibeConditional(schema, dependsOnField, expectedValue) {
|
|
2540
|
+
const currentDesc = schema.description || "";
|
|
2541
|
+
return schema.describe(`${currentDesc}|dependsOn:${dependsOnField}=${expectedValue}`);
|
|
2542
|
+
}
|
|
2543
|
+
function getVibeUIType(schema) {
|
|
2544
|
+
if (!schema) return void 0;
|
|
2545
|
+
const desc = schema.description;
|
|
2546
|
+
if (desc && desc.startsWith("ui:")) {
|
|
2547
|
+
const typeStr = desc.split(":")[1];
|
|
2548
|
+
return typeStr;
|
|
2549
|
+
}
|
|
2550
|
+
return void 0;
|
|
2551
|
+
}
|
|
2552
|
+
function getReferenceCollection(schema) {
|
|
2553
|
+
if (!schema) return void 0;
|
|
2554
|
+
const desc = schema.description;
|
|
2555
|
+
if (desc && desc.startsWith("ui:reference:")) {
|
|
2556
|
+
return desc.split(":")[2];
|
|
2557
|
+
}
|
|
2558
|
+
if (desc && desc.startsWith("ui:reference-array:")) {
|
|
2559
|
+
return desc.split(":")[2];
|
|
2560
|
+
}
|
|
2561
|
+
return void 0;
|
|
2562
|
+
}
|
|
2563
|
+
function getConditionalLogic(schema) {
|
|
2564
|
+
if (!schema) return void 0;
|
|
2565
|
+
const desc = schema.description;
|
|
2566
|
+
if (desc && desc.includes("|dependsOn:")) {
|
|
2567
|
+
const section = desc.split("|").find((s) => s.startsWith("dependsOn:"));
|
|
2568
|
+
if (section) {
|
|
2569
|
+
const condition = section.replace("dependsOn:", "");
|
|
2570
|
+
const [field, value] = condition.split("=");
|
|
2571
|
+
return { field, value };
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
return void 0;
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
// src/events.ts
|
|
2578
|
+
var VibeEventBus = class {
|
|
2579
|
+
listeners = /* @__PURE__ */ new Map();
|
|
2580
|
+
webhooks = [];
|
|
2581
|
+
/**
|
|
2582
|
+
* Register webhooks from config.
|
|
2583
|
+
*/
|
|
2584
|
+
registerWebhooks(webhooks) {
|
|
2585
|
+
this.webhooks = webhooks;
|
|
2586
|
+
}
|
|
2587
|
+
/**
|
|
2588
|
+
* Subscribe to a specific event type or '*' for all events.
|
|
2589
|
+
*/
|
|
2590
|
+
on(type, listener) {
|
|
2591
|
+
const existing = this.listeners.get(type) || [];
|
|
2592
|
+
existing.push(listener);
|
|
2593
|
+
this.listeners.set(type, existing);
|
|
2594
|
+
return () => {
|
|
2595
|
+
const list = this.listeners.get(type) || [];
|
|
2596
|
+
this.listeners.set(type, list.filter((l) => l !== listener));
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2599
|
+
/**
|
|
2600
|
+
* Emit an event. Notifies all listeners and fires webhooks.
|
|
2601
|
+
*/
|
|
2602
|
+
async emit(event) {
|
|
2603
|
+
const typeListeners = this.listeners.get(event.type) || [];
|
|
2604
|
+
for (const listener of typeListeners) {
|
|
2605
|
+
try {
|
|
2606
|
+
await listener(event);
|
|
2607
|
+
} catch (e) {
|
|
2608
|
+
console.error(`[VibeCMS] Event listener error for ${event.type}:`, e);
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
const wildcardListeners = this.listeners.get("*") || [];
|
|
2612
|
+
for (const listener of wildcardListeners) {
|
|
2613
|
+
try {
|
|
2614
|
+
await listener(event);
|
|
2615
|
+
} catch (e) {
|
|
2616
|
+
console.error("[VibeCMS] Wildcard event listener error:", e);
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
if (typeof window !== "undefined") {
|
|
2620
|
+
window.dispatchEvent(new CustomEvent("vibe-content-update", { detail: event }));
|
|
2621
|
+
}
|
|
2622
|
+
this.fireWebhooks(event);
|
|
2623
|
+
}
|
|
2624
|
+
async fireWebhooks(event) {
|
|
2625
|
+
for (const webhook of this.webhooks) {
|
|
2626
|
+
if (webhook.events && webhook.events.length > 0 && !webhook.events.includes(event.type)) {
|
|
2627
|
+
continue;
|
|
2628
|
+
}
|
|
2629
|
+
try {
|
|
2630
|
+
const body = JSON.stringify(event);
|
|
2631
|
+
const headers = {
|
|
2632
|
+
"Content-Type": "application/json",
|
|
2633
|
+
"X-Vibe-Event": event.type
|
|
2634
|
+
};
|
|
2635
|
+
if (webhook.secret && typeof globalThis.crypto !== "undefined") {
|
|
2636
|
+
const encoder = new TextEncoder();
|
|
2637
|
+
const key = await crypto.subtle.importKey(
|
|
2638
|
+
"raw",
|
|
2639
|
+
encoder.encode(webhook.secret),
|
|
2640
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
2641
|
+
false,
|
|
2642
|
+
["sign"]
|
|
2643
|
+
);
|
|
2644
|
+
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
|
|
2645
|
+
const hex = Array.from(new Uint8Array(signature)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2646
|
+
headers["X-Vibe-Signature"] = `sha256=${hex}`;
|
|
2647
|
+
}
|
|
2648
|
+
fetch(webhook.url, {
|
|
2649
|
+
method: "POST",
|
|
2650
|
+
headers,
|
|
2651
|
+
body
|
|
2652
|
+
}).catch((err) => {
|
|
2653
|
+
console.error(`[VibeCMS] Webhook delivery failed for ${webhook.url}:`, err);
|
|
2654
|
+
});
|
|
2655
|
+
} catch (e) {
|
|
2656
|
+
console.error(`[VibeCMS] Webhook error for ${webhook.url}:`, e);
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
};
|
|
2661
|
+
var vibeEvents = new VibeEventBus();
|
|
2662
|
+
|
|
2663
|
+
// src/scheduler.ts
|
|
2664
|
+
var DEFAULT_SCHEDULER_AUTHOR = {
|
|
2665
|
+
name: "VibeCMS Scheduler",
|
|
2666
|
+
email: "scheduler@vibecms.local"
|
|
2667
|
+
};
|
|
2668
|
+
async function processScheduledPublications(engine, collections, options) {
|
|
2669
|
+
const now = /* @__PURE__ */ new Date();
|
|
2670
|
+
const published = [];
|
|
2671
|
+
for (const collection of collections) {
|
|
2672
|
+
const slugs = await engine.list(collection);
|
|
2673
|
+
for (const slug of slugs) {
|
|
2674
|
+
const actualSlug = typeof slug === "string" ? slug : slug.slug;
|
|
2675
|
+
const data = await engine.read(collection, actualSlug);
|
|
2676
|
+
if (!data) continue;
|
|
2677
|
+
if (data.isPublished) continue;
|
|
2678
|
+
if (!data.scheduledPublishAt) continue;
|
|
2679
|
+
const scheduledDate = new Date(data.scheduledPublishAt);
|
|
2680
|
+
if (isNaN(scheduledDate.getTime())) continue;
|
|
2681
|
+
if (now >= scheduledDate) {
|
|
2682
|
+
data.isPublished = true;
|
|
2683
|
+
data.publishDate = now.toISOString();
|
|
2684
|
+
data.scheduledPublishAt = void 0;
|
|
2685
|
+
await engine.write(collection, actualSlug, data);
|
|
2686
|
+
vibeEvents.emit({
|
|
2687
|
+
type: "content:publish",
|
|
2688
|
+
collection,
|
|
2689
|
+
slug: actualSlug,
|
|
2690
|
+
data,
|
|
2691
|
+
timestamp: Date.now()
|
|
2692
|
+
});
|
|
2693
|
+
published.push({ collection, slug: actualSlug });
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
const shouldCommit = options?.autoCommit !== false;
|
|
2698
|
+
if (shouldCommit && published.length > 0 && engine.commit) {
|
|
2699
|
+
const slugList = published.map((p) => `${p.collection}/${p.slug}`).join(", ");
|
|
2700
|
+
const author = options?.author || DEFAULT_SCHEDULER_AUTHOR;
|
|
2701
|
+
try {
|
|
2702
|
+
await engine.commit(`chore: auto-publish scheduled content (${slugList})`, author);
|
|
2703
|
+
vibeEvents.emit({
|
|
2704
|
+
type: "content:commit",
|
|
2705
|
+
data: { published },
|
|
2706
|
+
timestamp: Date.now(),
|
|
2707
|
+
author
|
|
2708
|
+
});
|
|
2709
|
+
if (options?.pushToken && engine.push) {
|
|
2710
|
+
await engine.push(options.pushToken);
|
|
2711
|
+
}
|
|
2712
|
+
} catch (e) {
|
|
2713
|
+
console.error("[VibeCMS Scheduler] Auto-commit failed:", e);
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
return published;
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
// src/storage/drivers/git.ts
|
|
2720
|
+
var GitStorageDriver = class {
|
|
2721
|
+
async upload(file, context) {
|
|
2722
|
+
return context.engine.writeMedia(file, context.author);
|
|
2723
|
+
}
|
|
2724
|
+
async list(context) {
|
|
2725
|
+
return context.engine.listMedia();
|
|
2726
|
+
}
|
|
2727
|
+
async delete(fileName, context) {
|
|
2728
|
+
return context.engine.deleteMedia(fileName);
|
|
2729
|
+
}
|
|
2730
|
+
async rename(oldFileName, newFileName, context) {
|
|
2731
|
+
return context.engine.renameMedia(oldFileName, newFileName);
|
|
2732
|
+
}
|
|
2733
|
+
};
|
|
2734
|
+
|
|
2735
|
+
// src/storage/drivers/vercel-blob.ts
|
|
2736
|
+
var VercelBlobDriver = class {
|
|
2737
|
+
endpoint;
|
|
2738
|
+
constructor(config) {
|
|
2739
|
+
this.endpoint = config?.endpoint || "/api/upload";
|
|
2740
|
+
}
|
|
2741
|
+
async upload(file, context) {
|
|
2742
|
+
const formData = new FormData();
|
|
2743
|
+
formData.append("file", file);
|
|
2744
|
+
const response = await fetch(this.endpoint, {
|
|
2745
|
+
method: "POST",
|
|
2746
|
+
body: formData
|
|
2747
|
+
});
|
|
2748
|
+
if (!response.ok) {
|
|
2749
|
+
const errData = await response.json().catch(() => null);
|
|
2750
|
+
throw new Error(errData?.error || "Failed to upload to Vercel Blob");
|
|
2751
|
+
}
|
|
2752
|
+
const data = await response.json();
|
|
2753
|
+
if (typeof window !== "undefined") {
|
|
2754
|
+
try {
|
|
2755
|
+
const tempUrl = URL.createObjectURL(file);
|
|
2756
|
+
const img = new Image();
|
|
2757
|
+
img.src = tempUrl;
|
|
2758
|
+
await new Promise((resolve) => {
|
|
2759
|
+
img.onload = resolve;
|
|
2760
|
+
img.onerror = resolve;
|
|
2761
|
+
});
|
|
2762
|
+
URL.revokeObjectURL(tempUrl);
|
|
2763
|
+
return {
|
|
2764
|
+
url: data.url,
|
|
2765
|
+
width: img.naturalWidth,
|
|
2766
|
+
height: img.naturalHeight,
|
|
2767
|
+
alt: file.name
|
|
2768
|
+
};
|
|
2769
|
+
} catch (e) {
|
|
2770
|
+
return data.url;
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
async list(context) {
|
|
2775
|
+
const response = await fetch(`${this.endpoint}?action=list`);
|
|
2776
|
+
if (!response.ok) throw new Error("Failed to list Vercel blobs");
|
|
2777
|
+
const data = await response.json();
|
|
2778
|
+
return data.files || [];
|
|
2779
|
+
}
|
|
2780
|
+
async delete(fileName, context) {
|
|
2781
|
+
const response = await fetch(`${this.endpoint}?action=delete&file=${encodeURIComponent(fileName)}`, { method: "DELETE" });
|
|
2782
|
+
if (!response.ok) throw new Error(`Failed to delete blob: ${fileName}`);
|
|
2783
|
+
}
|
|
2784
|
+
async rename(oldFileName, newFileName, context) {
|
|
2785
|
+
const response = await fetch(`${this.endpoint}?action=rename&oldFile=${encodeURIComponent(oldFileName)}&newFile=${encodeURIComponent(newFileName)}`, { method: "POST" });
|
|
2786
|
+
if (!response.ok) throw new Error(`Failed to rename blob: ${oldFileName}`);
|
|
2787
|
+
}
|
|
2788
|
+
};
|
|
2789
|
+
|
|
2790
|
+
// src/components/VibeText.tsx
|
|
2791
|
+
var import_react3 = require("react");
|
|
2792
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2793
|
+
function VibeText({
|
|
2794
|
+
value,
|
|
2795
|
+
fieldPath,
|
|
2796
|
+
collection,
|
|
2797
|
+
slug,
|
|
2798
|
+
as: Tag = "span",
|
|
2799
|
+
className = "",
|
|
2800
|
+
...rest
|
|
2801
|
+
}) {
|
|
2802
|
+
const [liveValue, setLiveValue] = (0, import_react3.useState)(value);
|
|
2803
|
+
const [isEditorActive, setIsEditorActive] = (0, import_react3.useState)(false);
|
|
2804
|
+
(0, import_react3.useEffect)(() => {
|
|
2805
|
+
setLiveValue(value);
|
|
2806
|
+
}, [value]);
|
|
2807
|
+
(0, import_react3.useEffect)(() => {
|
|
2808
|
+
const active = typeof window !== "undefined" && (window.location.search.includes("vibe-editor=true") || window.VIBE_EDITOR_ACTIVE || sessionStorage.getItem("vibe_edit") === "true");
|
|
2809
|
+
setIsEditorActive(active);
|
|
2810
|
+
if (!active || !collection || !slug) return;
|
|
2811
|
+
const handleUpdate = (event) => {
|
|
2812
|
+
const detail = event.detail;
|
|
2813
|
+
const eventSlug = detail.slug?.replace(".json", "");
|
|
2814
|
+
const targetSlug = slug.replace(".json", "");
|
|
2815
|
+
if (detail.collection === collection && eventSlug === targetSlug && detail.data) {
|
|
2816
|
+
const keys = fieldPath.split(".");
|
|
2817
|
+
let val = detail.data;
|
|
2818
|
+
for (const k of keys) {
|
|
2819
|
+
if (val === void 0 || val === null) break;
|
|
2820
|
+
val = val[k];
|
|
2821
|
+
}
|
|
2822
|
+
if (val !== void 0 && typeof val === "string") {
|
|
2823
|
+
setLiveValue(val);
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
};
|
|
2827
|
+
window.addEventListener("vibe-content-update", handleUpdate);
|
|
2828
|
+
return () => window.removeEventListener("vibe-content-update", handleUpdate);
|
|
2829
|
+
}, [collection, slug, fieldPath]);
|
|
2830
|
+
const handleClick = (e) => {
|
|
2831
|
+
if (!isEditorActive) return;
|
|
2832
|
+
e.stopPropagation();
|
|
2833
|
+
window.dispatchEvent(
|
|
2834
|
+
new CustomEvent("vibe-field-focus", { detail: { fieldPath } })
|
|
2835
|
+
);
|
|
2836
|
+
};
|
|
2837
|
+
const editorStyles = isEditorActive ? "cursor-pointer transition-all duration-200 ring-1 ring-transparent hover:ring-indigo-500 hover:bg-indigo-500/10 rounded" : "";
|
|
2838
|
+
const { value: _, fieldPath: __, collection: ___, slug: ____, as: _____, ...domProps } = rest;
|
|
2839
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2840
|
+
Tag,
|
|
2841
|
+
{
|
|
2842
|
+
className: `${editorStyles} ${className}`.trim(),
|
|
2843
|
+
onClick: handleClick,
|
|
2844
|
+
"data-vibe-path": isEditorActive ? fieldPath : void 0,
|
|
2845
|
+
...domProps,
|
|
2846
|
+
children: liveValue
|
|
2847
|
+
}
|
|
2848
|
+
);
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2851
|
+
// src/components/VibeImage.tsx
|
|
2852
|
+
var import_react4 = require("react");
|
|
2853
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
2854
|
+
function VibeImage({
|
|
2855
|
+
src,
|
|
2856
|
+
alt = "",
|
|
2857
|
+
fieldPath,
|
|
2858
|
+
collection,
|
|
2859
|
+
slug,
|
|
2860
|
+
className = "",
|
|
2861
|
+
width,
|
|
2862
|
+
height,
|
|
2863
|
+
...rest
|
|
2864
|
+
}) {
|
|
2865
|
+
const [liveSrc, setLiveSrc] = (0, import_react4.useState)(src);
|
|
2866
|
+
const [isEditorActive, setIsEditorActive] = (0, import_react4.useState)(false);
|
|
2867
|
+
(0, import_react4.useEffect)(() => {
|
|
2868
|
+
setLiveSrc(src);
|
|
2869
|
+
}, [src]);
|
|
2870
|
+
(0, import_react4.useEffect)(() => {
|
|
2871
|
+
const active = typeof window !== "undefined" && (window.location.search.includes("vibe-editor=true") || window.VIBE_EDITOR_ACTIVE || sessionStorage.getItem("vibe_edit") === "true");
|
|
2872
|
+
setIsEditorActive(active);
|
|
2873
|
+
if (!active || !collection || !slug) return;
|
|
2874
|
+
const handleUpdate = (event) => {
|
|
2875
|
+
const detail = event.detail;
|
|
2876
|
+
const eventSlug = detail.slug?.replace(".json", "");
|
|
2877
|
+
const targetSlug = slug.replace(".json", "");
|
|
2878
|
+
if (detail.collection === collection && eventSlug === targetSlug && detail.data) {
|
|
2879
|
+
const keys = fieldPath.split(".");
|
|
2880
|
+
let val = detail.data;
|
|
2881
|
+
for (const k of keys) {
|
|
2882
|
+
if (val === void 0 || val === null) break;
|
|
2883
|
+
val = val[k];
|
|
2884
|
+
}
|
|
2885
|
+
if (val !== void 0 && typeof val === "string") {
|
|
2886
|
+
setLiveSrc(val);
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
};
|
|
2890
|
+
window.addEventListener("vibe-content-update", handleUpdate);
|
|
2891
|
+
return () => window.removeEventListener("vibe-content-update", handleUpdate);
|
|
2892
|
+
}, [collection, slug, fieldPath]);
|
|
2893
|
+
const handleClick = (e) => {
|
|
2894
|
+
if (!isEditorActive) return;
|
|
2895
|
+
e.stopPropagation();
|
|
2896
|
+
window.dispatchEvent(
|
|
2897
|
+
new CustomEvent("vibe-field-focus", { detail: { fieldPath } })
|
|
2898
|
+
);
|
|
2899
|
+
};
|
|
2900
|
+
const editorStyles = isEditorActive ? "cursor-pointer transition-all duration-200 ring-1 ring-transparent hover:ring-indigo-500 hover:bg-indigo-500/10 rounded" : "";
|
|
2901
|
+
const { fieldPath: _, collection: __, slug: ___, ...domProps } = rest;
|
|
2902
|
+
if (!liveSrc) {
|
|
2903
|
+
if (!isEditorActive) return null;
|
|
2904
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2905
|
+
"div",
|
|
2906
|
+
{
|
|
2907
|
+
className: `${editorStyles} ${className} flex items-center justify-center bg-neutral-100 border-2 border-dashed border-neutral-300 rounded-lg min-h-[120px]`.trim(),
|
|
2908
|
+
onClick: handleClick,
|
|
2909
|
+
"data-vibe-path": fieldPath,
|
|
2910
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-neutral-400 text-sm", children: "Click to add image" })
|
|
2911
|
+
}
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2914
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2915
|
+
"img",
|
|
2916
|
+
{
|
|
2917
|
+
src: liveSrc,
|
|
2918
|
+
alt,
|
|
2919
|
+
width,
|
|
2920
|
+
height,
|
|
2921
|
+
className: `${editorStyles} ${className}`.trim(),
|
|
2922
|
+
onClick: handleClick,
|
|
2923
|
+
"data-vibe-path": isEditorActive ? fieldPath : void 0,
|
|
2924
|
+
...domProps
|
|
2925
|
+
}
|
|
2926
|
+
);
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
// src/components/VibeRichText.tsx
|
|
2930
|
+
var import_react5 = require("react");
|
|
2931
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
2932
|
+
function VibeRichText({
|
|
2933
|
+
html,
|
|
2934
|
+
fieldPath,
|
|
2935
|
+
collection,
|
|
2936
|
+
slug,
|
|
2937
|
+
as: Tag = "div",
|
|
2938
|
+
className = "",
|
|
2939
|
+
...rest
|
|
2940
|
+
}) {
|
|
2941
|
+
const [liveHtml, setLiveHtml] = (0, import_react5.useState)(html);
|
|
2942
|
+
const [isEditorActive, setIsEditorActive] = (0, import_react5.useState)(false);
|
|
2943
|
+
(0, import_react5.useEffect)(() => {
|
|
2944
|
+
setLiveHtml(html);
|
|
2945
|
+
}, [html]);
|
|
2946
|
+
(0, import_react5.useEffect)(() => {
|
|
2947
|
+
const active = typeof window !== "undefined" && (window.location.search.includes("vibe-editor=true") || window.VIBE_EDITOR_ACTIVE || sessionStorage.getItem("vibe_edit") === "true");
|
|
2948
|
+
setIsEditorActive(active);
|
|
2949
|
+
if (!active || !collection || !slug) return;
|
|
2950
|
+
const handleUpdate = (event) => {
|
|
2951
|
+
const detail = event.detail;
|
|
2952
|
+
const eventSlug = detail.slug?.replace(".json", "");
|
|
2953
|
+
const targetSlug = slug.replace(".json", "");
|
|
2954
|
+
if (detail.collection === collection && eventSlug === targetSlug && detail.data) {
|
|
2955
|
+
const keys = fieldPath.split(".");
|
|
2956
|
+
let val = detail.data;
|
|
2957
|
+
for (const k of keys) {
|
|
2958
|
+
if (val === void 0 || val === null) break;
|
|
2959
|
+
val = val[k];
|
|
2960
|
+
}
|
|
2961
|
+
if (val !== void 0 && typeof val === "string") {
|
|
2962
|
+
setLiveHtml(val);
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
};
|
|
2966
|
+
window.addEventListener("vibe-content-update", handleUpdate);
|
|
2967
|
+
return () => window.removeEventListener("vibe-content-update", handleUpdate);
|
|
2968
|
+
}, [collection, slug, fieldPath]);
|
|
2969
|
+
const handleClick = (e) => {
|
|
2970
|
+
if (!isEditorActive) return;
|
|
2971
|
+
e.stopPropagation();
|
|
2972
|
+
window.dispatchEvent(
|
|
2973
|
+
new CustomEvent("vibe-field-focus", { detail: { fieldPath } })
|
|
2974
|
+
);
|
|
2975
|
+
};
|
|
2976
|
+
const editorStyles = isEditorActive ? "cursor-pointer transition-all duration-200 ring-1 ring-transparent hover:ring-indigo-500 hover:bg-indigo-500/10 rounded" : "";
|
|
2977
|
+
const { html: _, fieldPath: __, collection: ___, slug: ____, as: _____, ...domProps } = rest;
|
|
2978
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
2979
|
+
Tag,
|
|
2980
|
+
{
|
|
2981
|
+
className: `${editorStyles} ${className}`.trim(),
|
|
2982
|
+
onClick: handleClick,
|
|
2983
|
+
"data-vibe-path": isEditorActive ? fieldPath : void 0,
|
|
2984
|
+
dangerouslySetInnerHTML: { __html: liveHtml || "" },
|
|
2985
|
+
...domProps
|
|
2986
|
+
}
|
|
2987
|
+
);
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
// src/components/VibeOverlay.tsx
|
|
2991
|
+
var import_react6 = require("react");
|
|
2992
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
2993
|
+
var PANEL_DEFAULT_WIDTH = 420;
|
|
2994
|
+
var PANEL_MIN_WIDTH = 320;
|
|
2995
|
+
var PANEL_MAX_WIDTH = 640;
|
|
2996
|
+
var MOBILE_BREAKPOINT = 768;
|
|
2997
|
+
var DRAWER_DEFAULT_HEIGHT_VH = 60;
|
|
2998
|
+
var DRAWER_MIN_HEIGHT_VH = 40;
|
|
2999
|
+
var DRAWER_MAX_HEIGHT_VH = 90;
|
|
3000
|
+
var Z_PANEL = 9998;
|
|
3001
|
+
var Z_BACKDROP = 9997;
|
|
3002
|
+
var Z_BADGE = 9999;
|
|
3003
|
+
var TRANSITION_CSS = "margin 0.3s cubic-bezier(0.4, 0, 0.2, 1)";
|
|
3004
|
+
var VibeOverlayCtx = (0, import_react6.createContext)({
|
|
3005
|
+
isActive: false,
|
|
3006
|
+
toggle: () => {
|
|
3007
|
+
},
|
|
3008
|
+
focusedField: null,
|
|
3009
|
+
isMobile: false
|
|
3010
|
+
});
|
|
3011
|
+
var useVibeOverlay = () => (0, import_react6.useContext)(VibeOverlayCtx);
|
|
3012
|
+
function clampWidth(requested) {
|
|
3013
|
+
const maxFromViewport = typeof window !== "undefined" ? Math.floor(window.innerWidth * 0.5) : PANEL_MAX_WIDTH;
|
|
3014
|
+
return Math.min(
|
|
3015
|
+
Math.min(PANEL_MAX_WIDTH, maxFromViewport),
|
|
3016
|
+
Math.max(PANEL_MIN_WIDTH, requested)
|
|
3017
|
+
);
|
|
3018
|
+
}
|
|
3019
|
+
function supportsDvh() {
|
|
3020
|
+
if (typeof CSS === "undefined") return false;
|
|
3021
|
+
return CSS.supports("height", "100dvh");
|
|
3022
|
+
}
|
|
3023
|
+
function VibeOverlay({
|
|
3024
|
+
config,
|
|
3025
|
+
position = "right",
|
|
3026
|
+
panelWidth: initialWidth = PANEL_DEFAULT_WIDTH,
|
|
3027
|
+
children
|
|
3028
|
+
}) {
|
|
3029
|
+
const [isActive, setIsActive] = (0, import_react6.useState)(false);
|
|
3030
|
+
const [isAuthenticated, setIsAuthenticated] = (0, import_react6.useState)(false);
|
|
3031
|
+
const [isLoaded, setIsLoaded] = (0, import_react6.useState)(false);
|
|
3032
|
+
const [focusedField, setFocusedField] = (0, import_react6.useState)(null);
|
|
3033
|
+
const [isMobile, setIsMobile] = (0, import_react6.useState)(false);
|
|
3034
|
+
const [width, setWidth] = (0, import_react6.useState)(clampWidth(initialWidth));
|
|
3035
|
+
const [drawerHeight, setDrawerHeight] = (0, import_react6.useState)(DRAWER_DEFAULT_HEIGHT_VH);
|
|
3036
|
+
const panelRef = (0, import_react6.useRef)(null);
|
|
3037
|
+
const isResizing = (0, import_react6.useRef)(false);
|
|
3038
|
+
const isDraggingDrawer = (0, import_react6.useRef)(false);
|
|
3039
|
+
const savedHtmlMargin = (0, import_react6.useRef)("");
|
|
3040
|
+
const savedHtmlOverflow = (0, import_react6.useRef)("");
|
|
3041
|
+
(0, import_react6.useEffect)(() => {
|
|
3042
|
+
if (!config.auth) {
|
|
3043
|
+
setIsAuthenticated(true);
|
|
3044
|
+
setIsLoaded(true);
|
|
3045
|
+
return;
|
|
3046
|
+
}
|
|
3047
|
+
config.auth.getSession().then((session) => {
|
|
3048
|
+
setIsAuthenticated(!!session);
|
|
3049
|
+
setIsLoaded(true);
|
|
3050
|
+
});
|
|
3051
|
+
}, [config.auth]);
|
|
3052
|
+
(0, import_react6.useEffect)(() => {
|
|
3053
|
+
try {
|
|
3054
|
+
const stored = sessionStorage.getItem("vibe_panel_width");
|
|
3055
|
+
if (stored) {
|
|
3056
|
+
const parsed = parseInt(stored, 10);
|
|
3057
|
+
if (!isNaN(parsed)) setWidth(clampWidth(parsed));
|
|
3058
|
+
}
|
|
3059
|
+
} catch {
|
|
3060
|
+
}
|
|
3061
|
+
}, []);
|
|
3062
|
+
(0, import_react6.useEffect)(() => {
|
|
3063
|
+
if (typeof window === "undefined") return;
|
|
3064
|
+
const mql = window.matchMedia(`(min-width: ${MOBILE_BREAKPOINT}px)`);
|
|
3065
|
+
const handler = (e) => {
|
|
3066
|
+
setIsMobile(!e.matches);
|
|
3067
|
+
};
|
|
3068
|
+
handler(mql);
|
|
3069
|
+
if (mql.addEventListener) {
|
|
3070
|
+
mql.addEventListener("change", handler);
|
|
3071
|
+
return () => mql.removeEventListener("change", handler);
|
|
3072
|
+
} else {
|
|
3073
|
+
mql.addListener(handler);
|
|
3074
|
+
return () => mql.removeListener(handler);
|
|
3075
|
+
}
|
|
3076
|
+
}, []);
|
|
3077
|
+
(0, import_react6.useEffect)(() => {
|
|
3078
|
+
const html = document.documentElement;
|
|
3079
|
+
if (isActive && !isMobile) {
|
|
3080
|
+
savedHtmlMargin.current = html.style.getPropertyValue(
|
|
3081
|
+
position === "right" ? "margin-right" : "margin-left"
|
|
3082
|
+
);
|
|
3083
|
+
savedHtmlOverflow.current = html.style.getPropertyValue("overflow-x");
|
|
3084
|
+
html.style.transition = TRANSITION_CSS;
|
|
3085
|
+
if (position === "right") {
|
|
3086
|
+
html.style.marginRight = `${width}px`;
|
|
3087
|
+
html.style.marginLeft = "";
|
|
3088
|
+
} else {
|
|
3089
|
+
html.style.marginLeft = `${width}px`;
|
|
3090
|
+
html.style.marginRight = "";
|
|
3091
|
+
}
|
|
3092
|
+
html.style.overflowX = "hidden";
|
|
3093
|
+
} else {
|
|
3094
|
+
html.style.transition = TRANSITION_CSS;
|
|
3095
|
+
if (position === "right") {
|
|
3096
|
+
html.style.marginRight = savedHtmlMargin.current || "";
|
|
3097
|
+
} else {
|
|
3098
|
+
html.style.marginLeft = savedHtmlMargin.current || "";
|
|
3099
|
+
}
|
|
3100
|
+
html.style.overflowX = savedHtmlOverflow.current || "";
|
|
3101
|
+
const tid = setTimeout(() => {
|
|
3102
|
+
html.style.transition = "";
|
|
3103
|
+
}, 350);
|
|
3104
|
+
return () => clearTimeout(tid);
|
|
3105
|
+
}
|
|
3106
|
+
return () => {
|
|
3107
|
+
html.style.marginRight = "";
|
|
3108
|
+
html.style.marginLeft = "";
|
|
3109
|
+
html.style.overflowX = savedHtmlOverflow.current || "";
|
|
3110
|
+
html.style.transition = "";
|
|
3111
|
+
};
|
|
3112
|
+
}, [isActive, isMobile, width, position]);
|
|
3113
|
+
(0, import_react6.useEffect)(() => {
|
|
3114
|
+
if (!isActive || isMobile) return;
|
|
3115
|
+
const handler = () => {
|
|
3116
|
+
setWidth((prev) => clampWidth(prev));
|
|
3117
|
+
};
|
|
3118
|
+
window.addEventListener("resize", handler);
|
|
3119
|
+
return () => window.removeEventListener("resize", handler);
|
|
3120
|
+
}, [isActive, isMobile]);
|
|
3121
|
+
const activate = (0, import_react6.useCallback)(() => {
|
|
3122
|
+
setIsActive(true);
|
|
3123
|
+
try {
|
|
3124
|
+
sessionStorage.setItem("vibe_edit", "true");
|
|
3125
|
+
} catch {
|
|
3126
|
+
}
|
|
3127
|
+
window.VIBE_EDITOR_ACTIVE = true;
|
|
3128
|
+
}, []);
|
|
3129
|
+
const deactivate = (0, import_react6.useCallback)(() => {
|
|
3130
|
+
setIsActive(false);
|
|
3131
|
+
setFocusedField(null);
|
|
3132
|
+
try {
|
|
3133
|
+
sessionStorage.removeItem("vibe_edit");
|
|
3134
|
+
} catch {
|
|
3135
|
+
}
|
|
3136
|
+
window.VIBE_EDITOR_ACTIVE = false;
|
|
3137
|
+
}, []);
|
|
3138
|
+
const toggle = (0, import_react6.useCallback)(() => {
|
|
3139
|
+
setIsActive((prev) => {
|
|
3140
|
+
const next = !prev;
|
|
3141
|
+
if (next) {
|
|
3142
|
+
try {
|
|
3143
|
+
sessionStorage.setItem("vibe_edit", "true");
|
|
3144
|
+
} catch {
|
|
3145
|
+
}
|
|
3146
|
+
window.VIBE_EDITOR_ACTIVE = true;
|
|
3147
|
+
} else {
|
|
3148
|
+
setFocusedField(null);
|
|
3149
|
+
try {
|
|
3150
|
+
sessionStorage.removeItem("vibe_edit");
|
|
3151
|
+
} catch {
|
|
3152
|
+
}
|
|
3153
|
+
window.VIBE_EDITOR_ACTIVE = false;
|
|
3154
|
+
}
|
|
3155
|
+
return next;
|
|
3156
|
+
});
|
|
3157
|
+
}, []);
|
|
3158
|
+
(0, import_react6.useEffect)(() => {
|
|
3159
|
+
if (!isLoaded || !isAuthenticated) return;
|
|
3160
|
+
const handler = (e) => {
|
|
3161
|
+
if (e.key === "e" && (e.metaKey || e.ctrlKey)) {
|
|
3162
|
+
e.preventDefault();
|
|
3163
|
+
toggle();
|
|
3164
|
+
}
|
|
3165
|
+
if (e.key === "Escape" && isActive) {
|
|
3166
|
+
deactivate();
|
|
3167
|
+
}
|
|
3168
|
+
};
|
|
3169
|
+
try {
|
|
3170
|
+
if (sessionStorage.getItem("vibe_edit") === "true") {
|
|
3171
|
+
activate();
|
|
3172
|
+
}
|
|
3173
|
+
} catch {
|
|
3174
|
+
}
|
|
3175
|
+
document.addEventListener("keydown", handler);
|
|
3176
|
+
return () => document.removeEventListener("keydown", handler);
|
|
3177
|
+
}, [isLoaded, isAuthenticated, isActive, toggle, activate, deactivate]);
|
|
3178
|
+
(0, import_react6.useEffect)(() => {
|
|
3179
|
+
if (!isActive) return;
|
|
3180
|
+
const handler = (e) => {
|
|
3181
|
+
if (panelRef.current?.contains(e.target)) return;
|
|
3182
|
+
const target = e.target;
|
|
3183
|
+
const vibeEl = target.closest("[data-vibe-path]");
|
|
3184
|
+
if (vibeEl && vibeEl.dataset.vibePath) {
|
|
3185
|
+
e.preventDefault();
|
|
3186
|
+
e.stopPropagation();
|
|
3187
|
+
const fieldPath = vibeEl.dataset.vibePath;
|
|
3188
|
+
setFocusedField(fieldPath);
|
|
3189
|
+
window.dispatchEvent(
|
|
3190
|
+
new CustomEvent("vibe-field-focus", { detail: { fieldPath } })
|
|
3191
|
+
);
|
|
3192
|
+
}
|
|
3193
|
+
};
|
|
3194
|
+
document.addEventListener("click", handler, true);
|
|
3195
|
+
return () => document.removeEventListener("click", handler, true);
|
|
3196
|
+
}, [isActive]);
|
|
3197
|
+
(0, import_react6.useEffect)(() => {
|
|
3198
|
+
if (!isActive) return;
|
|
3199
|
+
const handler = (e) => {
|
|
3200
|
+
const detail = e.detail;
|
|
3201
|
+
if (detail?.fieldPath) {
|
|
3202
|
+
setFocusedField(detail.fieldPath);
|
|
3203
|
+
}
|
|
3204
|
+
};
|
|
3205
|
+
window.addEventListener("vibe-field-focus", handler);
|
|
3206
|
+
return () => window.removeEventListener("vibe-field-focus", handler);
|
|
3207
|
+
}, [isActive]);
|
|
3208
|
+
const onResizeStart = (0, import_react6.useCallback)(
|
|
3209
|
+
(e) => {
|
|
3210
|
+
e.preventDefault();
|
|
3211
|
+
isResizing.current = true;
|
|
3212
|
+
const startX = e.clientX;
|
|
3213
|
+
const startWidth = width;
|
|
3214
|
+
const onMouseMove = (moveEvent) => {
|
|
3215
|
+
if (!isResizing.current) return;
|
|
3216
|
+
const delta = position === "right" ? startX - moveEvent.clientX : moveEvent.clientX - startX;
|
|
3217
|
+
const newWidth = clampWidth(startWidth + delta);
|
|
3218
|
+
setWidth(newWidth);
|
|
3219
|
+
};
|
|
3220
|
+
const onMouseUp = () => {
|
|
3221
|
+
isResizing.current = false;
|
|
3222
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
3223
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
3224
|
+
document.body.style.cursor = "";
|
|
3225
|
+
document.body.style.userSelect = "";
|
|
3226
|
+
try {
|
|
3227
|
+
sessionStorage.setItem("vibe_panel_width", String(width));
|
|
3228
|
+
} catch {
|
|
3229
|
+
}
|
|
3230
|
+
};
|
|
3231
|
+
document.body.style.cursor = "col-resize";
|
|
3232
|
+
document.body.style.userSelect = "none";
|
|
3233
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
3234
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
3235
|
+
},
|
|
3236
|
+
[width, position]
|
|
3237
|
+
);
|
|
3238
|
+
const onDrawerDragStart = (0, import_react6.useCallback)(
|
|
3239
|
+
(e) => {
|
|
3240
|
+
isDraggingDrawer.current = true;
|
|
3241
|
+
const startY = e.touches[0].clientY;
|
|
3242
|
+
const startHeightPx = drawerHeight / 100 * window.innerHeight;
|
|
3243
|
+
const onTouchMove = (moveEvent) => {
|
|
3244
|
+
if (!isDraggingDrawer.current) return;
|
|
3245
|
+
moveEvent.preventDefault();
|
|
3246
|
+
const deltaY = startY - moveEvent.touches[0].clientY;
|
|
3247
|
+
const newHeightPx = startHeightPx + deltaY;
|
|
3248
|
+
const newHeightVh = Math.min(
|
|
3249
|
+
DRAWER_MAX_HEIGHT_VH,
|
|
3250
|
+
Math.max(DRAWER_MIN_HEIGHT_VH, newHeightPx / window.innerHeight * 100)
|
|
3251
|
+
);
|
|
3252
|
+
setDrawerHeight(newHeightVh);
|
|
3253
|
+
};
|
|
3254
|
+
const onTouchEnd = () => {
|
|
3255
|
+
isDraggingDrawer.current = false;
|
|
3256
|
+
document.removeEventListener("touchmove", onTouchMove);
|
|
3257
|
+
document.removeEventListener("touchend", onTouchEnd);
|
|
3258
|
+
};
|
|
3259
|
+
document.addEventListener("touchmove", onTouchMove, { passive: false });
|
|
3260
|
+
document.addEventListener("touchend", onTouchEnd);
|
|
3261
|
+
},
|
|
3262
|
+
[drawerHeight]
|
|
3263
|
+
);
|
|
3264
|
+
if (!isLoaded || !isAuthenticated) return null;
|
|
3265
|
+
const fontFamily = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
3266
|
+
const useDvh = supportsDvh();
|
|
3267
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(VibeOverlayCtx.Provider, { value: { isActive, toggle, focusedField, isMobile }, children: [
|
|
3268
|
+
!isActive && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
3269
|
+
"button",
|
|
3270
|
+
{
|
|
3271
|
+
onClick: activate,
|
|
3272
|
+
style: {
|
|
3273
|
+
position: "fixed",
|
|
3274
|
+
bottom: 16,
|
|
3275
|
+
right: 16,
|
|
3276
|
+
zIndex: Z_BADGE,
|
|
3277
|
+
background: "#171717",
|
|
3278
|
+
color: "#fff",
|
|
3279
|
+
padding: "8px 14px",
|
|
3280
|
+
borderRadius: 10,
|
|
3281
|
+
border: "none",
|
|
3282
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
3283
|
+
cursor: "pointer",
|
|
3284
|
+
fontSize: 13,
|
|
3285
|
+
fontWeight: 500,
|
|
3286
|
+
fontFamily,
|
|
3287
|
+
display: "flex",
|
|
3288
|
+
alignItems: "center",
|
|
3289
|
+
gap: 6,
|
|
3290
|
+
transition: "background 0.2s, transform 0.2s"
|
|
3291
|
+
},
|
|
3292
|
+
onMouseEnter: (e) => {
|
|
3293
|
+
const el = e.currentTarget;
|
|
3294
|
+
el.style.background = "#2a2a2a";
|
|
3295
|
+
el.style.transform = "scale(1.04)";
|
|
3296
|
+
},
|
|
3297
|
+
onMouseLeave: (e) => {
|
|
3298
|
+
const el = e.currentTarget;
|
|
3299
|
+
el.style.background = "#171717";
|
|
3300
|
+
el.style.transform = "scale(1)";
|
|
3301
|
+
},
|
|
3302
|
+
title: "Toggle CMS Editor (Ctrl+E)",
|
|
3303
|
+
children: [
|
|
3304
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3305
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
|
|
3306
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("path", { d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })
|
|
3307
|
+
] }),
|
|
3308
|
+
"Edit"
|
|
3309
|
+
]
|
|
3310
|
+
}
|
|
3311
|
+
),
|
|
3312
|
+
isActive && isMobile && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3313
|
+
"div",
|
|
3314
|
+
{
|
|
3315
|
+
onClick: deactivate,
|
|
3316
|
+
style: {
|
|
3317
|
+
position: "fixed",
|
|
3318
|
+
inset: 0,
|
|
3319
|
+
zIndex: Z_BACKDROP,
|
|
3320
|
+
background: "rgba(0, 0, 0, 0.4)",
|
|
3321
|
+
backdropFilter: "blur(2px)",
|
|
3322
|
+
transition: "opacity 0.3s"
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
),
|
|
3326
|
+
isActive && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
3327
|
+
"div",
|
|
3328
|
+
{
|
|
3329
|
+
ref: panelRef,
|
|
3330
|
+
style: isMobile ? {
|
|
3331
|
+
// ── Mobile: Bottom Drawer ──
|
|
3332
|
+
position: "fixed",
|
|
3333
|
+
bottom: 0,
|
|
3334
|
+
left: 0,
|
|
3335
|
+
right: 0,
|
|
3336
|
+
height: useDvh ? `${drawerHeight}dvh` : `${drawerHeight}vh`,
|
|
3337
|
+
zIndex: Z_PANEL,
|
|
3338
|
+
fontFamily,
|
|
3339
|
+
display: "flex",
|
|
3340
|
+
flexDirection: "column",
|
|
3341
|
+
background: "#fff",
|
|
3342
|
+
borderTop: "1px solid #e5e5e5",
|
|
3343
|
+
borderRadius: "16px 16px 0 0",
|
|
3344
|
+
boxShadow: "0 -8px 32px rgba(0,0,0,0.12)",
|
|
3345
|
+
touchAction: "none",
|
|
3346
|
+
transition: isDraggingDrawer.current ? "none" : "height 0.2s ease"
|
|
3347
|
+
} : {
|
|
3348
|
+
// ── Desktop/Tablet: Docked Sidebar ──
|
|
3349
|
+
position: "fixed",
|
|
3350
|
+
top: 0,
|
|
3351
|
+
[position]: 0,
|
|
3352
|
+
width: `${width}px`,
|
|
3353
|
+
height: useDvh ? "100dvh" : "100vh",
|
|
3354
|
+
zIndex: Z_PANEL,
|
|
3355
|
+
fontFamily,
|
|
3356
|
+
display: "flex",
|
|
3357
|
+
flexDirection: "column",
|
|
3358
|
+
background: "#fff",
|
|
3359
|
+
borderLeft: position === "right" ? "1px solid #e5e5e5" : "none",
|
|
3360
|
+
borderRight: position === "left" ? "1px solid #e5e5e5" : "none",
|
|
3361
|
+
boxShadow: position === "right" ? "-4px 0 24px rgba(0,0,0,0.06)" : "4px 0 24px rgba(0,0,0,0.06)"
|
|
3362
|
+
},
|
|
3363
|
+
children: [
|
|
3364
|
+
isMobile && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3365
|
+
"div",
|
|
3366
|
+
{
|
|
3367
|
+
onTouchStart: onDrawerDragStart,
|
|
3368
|
+
style: {
|
|
3369
|
+
display: "flex",
|
|
3370
|
+
justifyContent: "center",
|
|
3371
|
+
alignItems: "center",
|
|
3372
|
+
height: 28,
|
|
3373
|
+
flexShrink: 0,
|
|
3374
|
+
cursor: "row-resize",
|
|
3375
|
+
touchAction: "none"
|
|
3376
|
+
},
|
|
3377
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3378
|
+
"div",
|
|
3379
|
+
{
|
|
3380
|
+
style: {
|
|
3381
|
+
width: 36,
|
|
3382
|
+
height: 4,
|
|
3383
|
+
borderRadius: 2,
|
|
3384
|
+
background: "#d4d4d4"
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
)
|
|
3388
|
+
}
|
|
3389
|
+
),
|
|
3390
|
+
!isMobile && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ResizeHandle, { position, onResizeStart }),
|
|
3391
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
3392
|
+
"div",
|
|
3393
|
+
{
|
|
3394
|
+
style: {
|
|
3395
|
+
height: 48,
|
|
3396
|
+
display: "flex",
|
|
3397
|
+
alignItems: "center",
|
|
3398
|
+
justifyContent: "space-between",
|
|
3399
|
+
padding: "0 16px",
|
|
3400
|
+
borderBottom: "1px solid #e5e5e5",
|
|
3401
|
+
background: "#fafafa",
|
|
3402
|
+
flexShrink: 0
|
|
3403
|
+
},
|
|
3404
|
+
children: [
|
|
3405
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { fontWeight: 600, fontSize: 13, color: "#171717" }, children: "VibeCMS" }),
|
|
3406
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3407
|
+
"button",
|
|
3408
|
+
{
|
|
3409
|
+
onClick: deactivate,
|
|
3410
|
+
style: {
|
|
3411
|
+
background: "none",
|
|
3412
|
+
border: "none",
|
|
3413
|
+
cursor: "pointer",
|
|
3414
|
+
padding: 4,
|
|
3415
|
+
color: "#a3a3a3",
|
|
3416
|
+
display: "flex",
|
|
3417
|
+
transition: "color 0.2s"
|
|
3418
|
+
},
|
|
3419
|
+
onMouseEnter: (e) => {
|
|
3420
|
+
e.currentTarget.style.color = "#171717";
|
|
3421
|
+
},
|
|
3422
|
+
onMouseLeave: (e) => {
|
|
3423
|
+
e.currentTarget.style.color = "#a3a3a3";
|
|
3424
|
+
},
|
|
3425
|
+
title: "Close Editor (Escape)",
|
|
3426
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3427
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
3428
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
3429
|
+
] })
|
|
3430
|
+
}
|
|
3431
|
+
)
|
|
3432
|
+
]
|
|
3433
|
+
}
|
|
3434
|
+
),
|
|
3435
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3436
|
+
"div",
|
|
3437
|
+
{
|
|
3438
|
+
style: {
|
|
3439
|
+
flex: 1,
|
|
3440
|
+
overflowY: "auto",
|
|
3441
|
+
overscrollBehavior: "contain",
|
|
3442
|
+
WebkitOverflowScrolling: "touch",
|
|
3443
|
+
minHeight: 0
|
|
3444
|
+
},
|
|
3445
|
+
children: children ? children : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3446
|
+
DefaultEditorContent,
|
|
3447
|
+
{
|
|
3448
|
+
focusedField,
|
|
3449
|
+
config
|
|
3450
|
+
}
|
|
3451
|
+
)
|
|
3452
|
+
}
|
|
3453
|
+
)
|
|
3454
|
+
]
|
|
3455
|
+
}
|
|
3456
|
+
)
|
|
3457
|
+
] });
|
|
3458
|
+
}
|
|
3459
|
+
function ResizeHandle({
|
|
3460
|
+
position,
|
|
3461
|
+
onResizeStart
|
|
3462
|
+
}) {
|
|
3463
|
+
const [isHovered, setIsHovered] = (0, import_react6.useState)(false);
|
|
3464
|
+
const edge = position === "right" ? "left" : "right";
|
|
3465
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3466
|
+
"div",
|
|
3467
|
+
{
|
|
3468
|
+
onMouseDown: onResizeStart,
|
|
3469
|
+
onMouseEnter: () => setIsHovered(true),
|
|
3470
|
+
onMouseLeave: () => setIsHovered(false),
|
|
3471
|
+
style: {
|
|
3472
|
+
position: "absolute",
|
|
3473
|
+
top: 0,
|
|
3474
|
+
[edge]: -3,
|
|
3475
|
+
width: 6,
|
|
3476
|
+
height: "100%",
|
|
3477
|
+
cursor: "col-resize",
|
|
3478
|
+
zIndex: 10,
|
|
3479
|
+
display: "flex",
|
|
3480
|
+
alignItems: "center",
|
|
3481
|
+
justifyContent: "center"
|
|
3482
|
+
},
|
|
3483
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3484
|
+
"div",
|
|
3485
|
+
{
|
|
3486
|
+
style: {
|
|
3487
|
+
width: 2,
|
|
3488
|
+
height: "100%",
|
|
3489
|
+
background: isHovered ? "#6366f1" : "transparent",
|
|
3490
|
+
transition: "background 0.2s",
|
|
3491
|
+
borderRadius: 1
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
)
|
|
3495
|
+
}
|
|
3496
|
+
);
|
|
3497
|
+
}
|
|
3498
|
+
function DefaultEditorContent({
|
|
3499
|
+
focusedField,
|
|
3500
|
+
config
|
|
3501
|
+
}) {
|
|
3502
|
+
const collections = config.collections ?? {};
|
|
3503
|
+
const singletons = config.singletons ?? {};
|
|
3504
|
+
const allCollections = { ...collections, ...singletons };
|
|
3505
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { padding: 16 }, children: [
|
|
3506
|
+
focusedField && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
3507
|
+
"div",
|
|
3508
|
+
{
|
|
3509
|
+
style: {
|
|
3510
|
+
background: "#eef2ff",
|
|
3511
|
+
border: "1px solid #c7d2fe",
|
|
3512
|
+
borderRadius: 8,
|
|
3513
|
+
padding: "10px 12px",
|
|
3514
|
+
marginBottom: 16,
|
|
3515
|
+
fontSize: 12
|
|
3516
|
+
},
|
|
3517
|
+
children: [
|
|
3518
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { color: "#6366f1", fontWeight: 600, marginBottom: 2 }, children: "Selected field" }),
|
|
3519
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3520
|
+
"code",
|
|
3521
|
+
{
|
|
3522
|
+
style: {
|
|
3523
|
+
color: "#4338ca",
|
|
3524
|
+
background: "#e0e7ff",
|
|
3525
|
+
padding: "2px 6px",
|
|
3526
|
+
borderRadius: 4,
|
|
3527
|
+
fontSize: 11,
|
|
3528
|
+
wordBreak: "break-all"
|
|
3529
|
+
},
|
|
3530
|
+
children: focusedField
|
|
3531
|
+
}
|
|
3532
|
+
)
|
|
3533
|
+
]
|
|
3534
|
+
}
|
|
3535
|
+
),
|
|
3536
|
+
Object.keys(allCollections).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { marginBottom: 24 }, children: [
|
|
3537
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3538
|
+
"div",
|
|
3539
|
+
{
|
|
3540
|
+
style: {
|
|
3541
|
+
fontSize: 11,
|
|
3542
|
+
fontWeight: 600,
|
|
3543
|
+
color: "#a3a3a3",
|
|
3544
|
+
textTransform: "uppercase",
|
|
3545
|
+
letterSpacing: "0.05em",
|
|
3546
|
+
marginBottom: 8
|
|
3547
|
+
},
|
|
3548
|
+
children: "Collections"
|
|
3549
|
+
}
|
|
3550
|
+
),
|
|
3551
|
+
Object.entries(allCollections).map(([key, col]) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
3552
|
+
"div",
|
|
3553
|
+
{
|
|
3554
|
+
style: {
|
|
3555
|
+
padding: "8px 10px",
|
|
3556
|
+
borderRadius: 6,
|
|
3557
|
+
border: "1px solid #f5f5f5",
|
|
3558
|
+
marginBottom: 4,
|
|
3559
|
+
fontSize: 13,
|
|
3560
|
+
color: "#525252",
|
|
3561
|
+
cursor: "pointer",
|
|
3562
|
+
transition: "background 0.15s"
|
|
3563
|
+
},
|
|
3564
|
+
onMouseEnter: (e) => {
|
|
3565
|
+
e.currentTarget.style.background = "#fafafa";
|
|
3566
|
+
},
|
|
3567
|
+
onMouseLeave: (e) => {
|
|
3568
|
+
e.currentTarget.style.background = "";
|
|
3569
|
+
},
|
|
3570
|
+
children: [
|
|
3571
|
+
col.name,
|
|
3572
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { color: "#d4d4d4", marginLeft: 6, fontSize: 11 }, children: key })
|
|
3573
|
+
]
|
|
3574
|
+
},
|
|
3575
|
+
key
|
|
3576
|
+
))
|
|
3577
|
+
] }),
|
|
3578
|
+
!focusedField && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
3579
|
+
"div",
|
|
3580
|
+
{
|
|
3581
|
+
style: {
|
|
3582
|
+
textAlign: "center",
|
|
3583
|
+
color: "#a3a3a3",
|
|
3584
|
+
fontSize: 12,
|
|
3585
|
+
padding: "24px 0",
|
|
3586
|
+
lineHeight: 1.7
|
|
3587
|
+
},
|
|
3588
|
+
children: [
|
|
3589
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { style: { marginBottom: 8 }, children: "Click any editable element on the page to select it." }),
|
|
3590
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("p", { style: { fontSize: 11, color: "#d4d4d4" }, children: [
|
|
3591
|
+
"Editable elements are created with",
|
|
3592
|
+
" ",
|
|
3593
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("code", { style: { background: "#f5f5f5", padding: "1px 4px", borderRadius: 3 }, children: "<VibeText>" }),
|
|
3594
|
+
",",
|
|
3595
|
+
" ",
|
|
3596
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("code", { style: { background: "#f5f5f5", padding: "1px 4px", borderRadius: 3 }, children: "<VibeImage>" }),
|
|
3597
|
+
", or",
|
|
3598
|
+
" ",
|
|
3599
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("code", { style: { background: "#f5f5f5", padding: "1px 4px", borderRadius: 3 }, children: "<VibeRichText>" })
|
|
3600
|
+
] })
|
|
3601
|
+
]
|
|
3602
|
+
}
|
|
3603
|
+
)
|
|
3604
|
+
] });
|
|
3605
|
+
}
|
|
3606
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
3607
|
+
0 && (module.exports = {
|
|
3608
|
+
AuthorCard,
|
|
3609
|
+
BrowserEngine,
|
|
3610
|
+
Button,
|
|
3611
|
+
Command,
|
|
3612
|
+
CommandDialog,
|
|
3613
|
+
CommandEmpty,
|
|
3614
|
+
CommandGroup,
|
|
3615
|
+
CommandInput,
|
|
3616
|
+
CommandItem,
|
|
3617
|
+
CommandList,
|
|
3618
|
+
CommandSeparator,
|
|
3619
|
+
CommandShortcut,
|
|
3620
|
+
DevEngine,
|
|
3621
|
+
Dialog,
|
|
3622
|
+
DialogClose,
|
|
3623
|
+
DialogContent,
|
|
3624
|
+
DialogDescription,
|
|
3625
|
+
DialogFooter,
|
|
3626
|
+
DialogHeader,
|
|
3627
|
+
DialogOverlay,
|
|
3628
|
+
DialogPortal,
|
|
3629
|
+
DialogTitle,
|
|
3630
|
+
DialogTrigger,
|
|
3631
|
+
GitStorageDriver,
|
|
3632
|
+
Input,
|
|
3633
|
+
InputGroup,
|
|
3634
|
+
InputGroupAddon,
|
|
3635
|
+
InputGroupButton,
|
|
3636
|
+
InputGroupInput,
|
|
3637
|
+
InputGroupText,
|
|
3638
|
+
InputGroupTextarea,
|
|
3639
|
+
Label,
|
|
3640
|
+
NodeEngine,
|
|
3641
|
+
Popover,
|
|
3642
|
+
PopoverContent,
|
|
3643
|
+
PopoverDescription,
|
|
3644
|
+
PopoverHeader,
|
|
3645
|
+
PopoverTitle,
|
|
3646
|
+
PopoverTrigger,
|
|
3647
|
+
ScrollArea,
|
|
3648
|
+
ScrollBar,
|
|
3649
|
+
SeoHead,
|
|
3650
|
+
Sheet,
|
|
3651
|
+
SheetClose,
|
|
3652
|
+
SheetContent,
|
|
3653
|
+
SheetDescription,
|
|
3654
|
+
SheetFooter,
|
|
3655
|
+
SheetHeader,
|
|
3656
|
+
SheetTitle,
|
|
3657
|
+
SheetTrigger,
|
|
3658
|
+
Switch,
|
|
3659
|
+
Textarea,
|
|
3660
|
+
Tooltip,
|
|
3661
|
+
TooltipContent,
|
|
3662
|
+
TooltipProvider,
|
|
3663
|
+
TooltipTrigger,
|
|
3664
|
+
VercelBlobDriver,
|
|
3665
|
+
VibeEngine,
|
|
3666
|
+
VibeImage,
|
|
3667
|
+
VibeOverlay,
|
|
3668
|
+
VibeRichText,
|
|
3669
|
+
VibeText,
|
|
3670
|
+
assertValidCollection,
|
|
3671
|
+
assertValidCommitOid,
|
|
3672
|
+
assertValidSlug,
|
|
3673
|
+
blockSchema,
|
|
3674
|
+
buttonVariants,
|
|
3675
|
+
canEditCollection,
|
|
3676
|
+
ctaBannerBlockSchema,
|
|
3677
|
+
defineConfig,
|
|
3678
|
+
faqBlockSchema,
|
|
3679
|
+
featuresBlockSchema,
|
|
3680
|
+
formBlockSchema,
|
|
3681
|
+
formFieldSchema,
|
|
3682
|
+
getCollectionSchema,
|
|
3683
|
+
getConditionalLogic,
|
|
3684
|
+
getCustomBlockComponent,
|
|
3685
|
+
getReferenceCollection,
|
|
3686
|
+
getVibeUIType,
|
|
3687
|
+
gridBlockSchema,
|
|
3688
|
+
heroBlockSchema,
|
|
3689
|
+
initVibeEngine,
|
|
3690
|
+
logoCloudBlockSchema,
|
|
3691
|
+
mediaGallerySchema,
|
|
3692
|
+
populate,
|
|
3693
|
+
pricingBlockSchema,
|
|
3694
|
+
processScheduledPublications,
|
|
3695
|
+
publishingSchema,
|
|
3696
|
+
registerCollectionSchemas,
|
|
3697
|
+
registerCustomBlocks,
|
|
3698
|
+
resolveUserRole,
|
|
3699
|
+
sanitizeRedirectPath,
|
|
3700
|
+
seoSchema,
|
|
3701
|
+
tabsBlockSchema,
|
|
3702
|
+
testimonialsBlockSchema,
|
|
3703
|
+
useVibeContent,
|
|
3704
|
+
useVibeDocument,
|
|
3705
|
+
useVibeOverlay,
|
|
3706
|
+
validateCollection,
|
|
3707
|
+
validateCollectionData,
|
|
3708
|
+
validateCommitOid,
|
|
3709
|
+
validateSlug,
|
|
3710
|
+
vibeConditional,
|
|
3711
|
+
vibeEvents,
|
|
3712
|
+
vibeField,
|
|
3713
|
+
videoBlockSchema
|
|
3714
|
+
});
|
|
3715
|
+
//# sourceMappingURL=index.js.map
|