astro-md-editor 0.0.1
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/.output/nitro.json +17 -0
- package/.output/public/assets/index-Cc7yKB0o.js +19 -0
- package/.output/public/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/.output/public/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/.output/public/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/.output/public/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/.output/public/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/.output/public/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/.output/public/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/.output/public/assets/main-DDBjVFnt.js +17 -0
- package/.output/public/assets/styles-ggfdUHMo.css +1 -0
- package/.output/public/favicon.ico +0 -0
- package/.output/public/logo192.png +0 -0
- package/.output/public/logo512.png +0 -0
- package/.output/public/manifest.json +25 -0
- package/.output/public/robots.txt +3 -0
- package/.output/server/__root-C09LBXMv.mjs +40 -0
- package/.output/server/_chunks/ssr-renderer.mjs +21 -0
- package/.output/server/_libs/ajv-formats.mjs +330 -0
- package/.output/server/_libs/ajv.mjs +5484 -0
- package/.output/server/_libs/base-ui__react.mjs +8712 -0
- package/.output/server/_libs/base-ui__utils.mjs +980 -0
- package/.output/server/_libs/class-variance-authority.mjs +44 -0
- package/.output/server/_libs/clsx.mjs +16 -0
- package/.output/server/_libs/cookie-es.mjs +58 -0
- package/.output/server/_libs/croner.mjs +1 -0
- package/.output/server/_libs/crossws.mjs +1 -0
- package/.output/server/_libs/date-fns.mjs +1716 -0
- package/.output/server/_libs/date-fns__tz.mjs +217 -0
- package/.output/server/_libs/extend-shallow.mjs +35 -0
- package/.output/server/_libs/fast-deep-equal.mjs +38 -0
- package/.output/server/_libs/fast-uri.mjs +725 -0
- package/.output/server/_libs/floating-ui__core.mjs +663 -0
- package/.output/server/_libs/floating-ui__dom.mjs +624 -0
- package/.output/server/_libs/floating-ui__react-dom.mjs +279 -0
- package/.output/server/_libs/floating-ui__utils.mjs +322 -0
- package/.output/server/_libs/gray-matter.mjs +393 -0
- package/.output/server/_libs/h3-v2.mjs +276 -0
- package/.output/server/_libs/h3.mjs +400 -0
- package/.output/server/_libs/hookable.mjs +1 -0
- package/.output/server/_libs/is-extendable.mjs +13 -0
- package/.output/server/_libs/isbot.mjs +20 -0
- package/.output/server/_libs/js-yaml.mjs +2822 -0
- package/.output/server/_libs/json-schema-traverse.mjs +91 -0
- package/.output/server/_libs/kind-of.mjs +125 -0
- package/.output/server/_libs/lucide-react.mjs +177 -0
- package/.output/server/_libs/ohash.mjs +1 -0
- package/.output/server/_libs/react-day-picker.mjs +2216 -0
- package/.output/server/_libs/react-dom.mjs +10779 -0
- package/.output/server/_libs/react-resizable-panels.mjs +2024 -0
- package/.output/server/_libs/react.mjs +513 -0
- package/.output/server/_libs/reselect.mjs +326 -0
- package/.output/server/_libs/rou3.mjs +8 -0
- package/.output/server/_libs/section-matter.mjs +112 -0
- package/.output/server/_libs/seroval-plugins.mjs +58 -0
- package/.output/server/_libs/seroval.mjs +1765 -0
- package/.output/server/_libs/srvx.mjs +736 -0
- package/.output/server/_libs/strip-bom-string.mjs +16 -0
- package/.output/server/_libs/tabbable.mjs +342 -0
- package/.output/server/_libs/tailwind-merge.mjs +3175 -0
- package/.output/server/_libs/tanstack__history.mjs +217 -0
- package/.output/server/_libs/tanstack__react-router.mjs +1464 -0
- package/.output/server/_libs/tanstack__react-store.mjs +1 -0
- package/.output/server/_libs/tanstack__router-core.mjs +4912 -0
- package/.output/server/_libs/tanstack__store.mjs +1 -0
- package/.output/server/_libs/tiny-invariant.mjs +12 -0
- package/.output/server/_libs/tiny-warning.mjs +5 -0
- package/.output/server/_libs/ufo.mjs +54 -0
- package/.output/server/_libs/unctx.mjs +1 -0
- package/.output/server/_libs/unstorage.mjs +1 -0
- package/.output/server/_libs/use-sync-external-store.mjs +139 -0
- package/.output/server/_libs/zod.mjs +3634 -0
- package/.output/server/_libs/zustand.mjs +43 -0
- package/.output/server/_ssr/RightSidebar-RSY9M7XF.mjs +218 -0
- package/.output/server/_ssr/collections.server-D6U2tEsT.mjs +120 -0
- package/.output/server/_ssr/createServerRpc-29xaFZcb.mjs +12 -0
- package/.output/server/_ssr/index-BaqV4cZC.mjs +2083 -0
- package/.output/server/_ssr/index-sQBM6rwN.mjs +115 -0
- package/.output/server/_ssr/index.mjs +1448 -0
- package/.output/server/_ssr/router-D4G1DGr3.mjs +155 -0
- package/.output/server/_ssr/start-HYkvq4Ni.mjs +4 -0
- package/.output/server/_tanstack-start-manifest_v-CYEHh_qB.mjs +4 -0
- package/.output/server/index.mjs +451 -0
- package/README.md +118 -0
- package/index.mjs +21 -0
- package/package.json +86 -0
- package/scripts/bootstrap-collections.mjs +1201 -0
|
@@ -0,0 +1,2083 @@
|
|
|
1
|
+
import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
|
|
2
|
+
import { u as useCollectionsData, R as Route, c as createSsrRpc } from "./router-D4G1DGr3.mjs";
|
|
3
|
+
import { c as cva } from "../_libs/class-variance-authority.mjs";
|
|
4
|
+
import { c as clsx } from "../_libs/clsx.mjs";
|
|
5
|
+
import { t as twMerge } from "../_libs/tailwind-merge.mjs";
|
|
6
|
+
import { U as Ut, q as qt, Z as Zt } from "../_libs/react-resizable-panels.mjs";
|
|
7
|
+
import { A as Ajv } from "../_libs/ajv.mjs";
|
|
8
|
+
import { a as addFormats } from "../_libs/ajv-formats.mjs";
|
|
9
|
+
import { c as createServerFn } from "./index.mjs";
|
|
10
|
+
import { P as PanelLeftClose, a as PanelLeftOpen, L as LoaderCircle, S as Save, b as PanelRightClose, c as PanelRightOpen, C as ChevronDown, d as Check, X, e as CalendarDays, f as Clock3, g as ChevronUp, I as Image, h as Search, i as ChevronLeft, j as ChevronRight } from "../_libs/lucide-react.mjs";
|
|
11
|
+
import { c as create } from "../_libs/zustand.mjs";
|
|
12
|
+
import { S as SelectRoot, B as Button$1, a as SelectTrigger$1, b as SelectIcon, c as SelectValue$1, d as SelectPortal, e as SelectPositioner, f as SelectPopup, g as SelectList, h as SelectGroup$1, i as SelectItem$1, j as SelectItemText, k as SelectItemIndicator, u as useRender, m as mergeProps, I as Input$1, l as SelectScrollUpArrow, n as SelectScrollDownArrow, o as SwitchRoot, p as SwitchThumb, P as PopoverRoot, q as PopoverTrigger$1, r as PopoverPortal, s as PopoverPositioner, t as PopoverPopup } from "../_libs/base-ui__react.mjs";
|
|
13
|
+
import { g as getDefaultClassNames, D as DayPicker } from "../_libs/react-day-picker.mjs";
|
|
14
|
+
import { a as arrayType, s as stringType, o as objectType, l as literalType, r as recordType, u as unknownType } from "../_libs/zod.mjs";
|
|
15
|
+
import "../_libs/tanstack__react-router.mjs";
|
|
16
|
+
import "../_libs/tanstack__router-core.mjs";
|
|
17
|
+
import "../_libs/cookie-es.mjs";
|
|
18
|
+
import "../_libs/tanstack__history.mjs";
|
|
19
|
+
import "../_libs/tiny-invariant.mjs";
|
|
20
|
+
import "../_libs/seroval.mjs";
|
|
21
|
+
import "../_libs/seroval-plugins.mjs";
|
|
22
|
+
import "node:stream/web";
|
|
23
|
+
import "node:stream";
|
|
24
|
+
import "../_libs/react-dom.mjs";
|
|
25
|
+
import "util";
|
|
26
|
+
import "crypto";
|
|
27
|
+
import "async_hooks";
|
|
28
|
+
import "stream";
|
|
29
|
+
import "../_libs/isbot.mjs";
|
|
30
|
+
import "../_libs/tiny-warning.mjs";
|
|
31
|
+
import "node:async_hooks";
|
|
32
|
+
import "../_libs/h3-v2.mjs";
|
|
33
|
+
import "../_libs/rou3.mjs";
|
|
34
|
+
import "../_libs/srvx.mjs";
|
|
35
|
+
import "node:http";
|
|
36
|
+
import "node:https";
|
|
37
|
+
import "node:http2";
|
|
38
|
+
import "../_libs/base-ui__utils.mjs";
|
|
39
|
+
import "../_libs/reselect.mjs";
|
|
40
|
+
import "../_libs/use-sync-external-store.mjs";
|
|
41
|
+
import "../_libs/floating-ui__utils.mjs";
|
|
42
|
+
import "../_libs/floating-ui__react-dom.mjs";
|
|
43
|
+
import "../_libs/floating-ui__dom.mjs";
|
|
44
|
+
import "../_libs/floating-ui__core.mjs";
|
|
45
|
+
import "../_libs/tabbable.mjs";
|
|
46
|
+
import "../_libs/fast-deep-equal.mjs";
|
|
47
|
+
import "../_libs/json-schema-traverse.mjs";
|
|
48
|
+
import "../_libs/fast-uri.mjs";
|
|
49
|
+
import "../_libs/date-fns.mjs";
|
|
50
|
+
import "../_libs/date-fns__tz.mjs";
|
|
51
|
+
function cn(...inputs) {
|
|
52
|
+
return twMerge(clsx(inputs));
|
|
53
|
+
}
|
|
54
|
+
const buttonVariants = cva(
|
|
55
|
+
"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 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",
|
|
56
|
+
{
|
|
57
|
+
variants: {
|
|
58
|
+
variant: {
|
|
59
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
60
|
+
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",
|
|
61
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
62
|
+
ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
|
63
|
+
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",
|
|
64
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
65
|
+
},
|
|
66
|
+
size: {
|
|
67
|
+
default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
68
|
+
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",
|
|
69
|
+
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",
|
|
70
|
+
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
|
|
71
|
+
icon: "size-8",
|
|
72
|
+
"icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
73
|
+
"icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
74
|
+
"icon-lg": "size-9"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
defaultVariants: {
|
|
78
|
+
variant: "default",
|
|
79
|
+
size: "default"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
function Button({
|
|
84
|
+
className,
|
|
85
|
+
variant = "default",
|
|
86
|
+
size = "default",
|
|
87
|
+
...props
|
|
88
|
+
}) {
|
|
89
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
90
|
+
Button$1,
|
|
91
|
+
{
|
|
92
|
+
"data-slot": "button",
|
|
93
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
94
|
+
...props
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
function ResizablePanelGroup({
|
|
99
|
+
className,
|
|
100
|
+
...props
|
|
101
|
+
}) {
|
|
102
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
103
|
+
Ut,
|
|
104
|
+
{
|
|
105
|
+
"data-slot": "resizable-panel-group",
|
|
106
|
+
className: cn(
|
|
107
|
+
"flex h-full w-full aria-[orientation=vertical]:flex-col",
|
|
108
|
+
className
|
|
109
|
+
),
|
|
110
|
+
...props
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
function ResizablePanel({ ...props }) {
|
|
115
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(qt, { "data-slot": "resizable-panel", ...props });
|
|
116
|
+
}
|
|
117
|
+
function ResizableHandle({
|
|
118
|
+
withHandle,
|
|
119
|
+
className,
|
|
120
|
+
...props
|
|
121
|
+
}) {
|
|
122
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
123
|
+
Zt,
|
|
124
|
+
{
|
|
125
|
+
"data-slot": "resizable-handle",
|
|
126
|
+
className: cn(
|
|
127
|
+
"group/resize-handle bg-border ring-offset-background focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:outline-hidden aria-[orientation=horizontal]:h-px aria-[orientation=horizontal]:w-full aria-[orientation=horizontal]:after:left-0 aria-[orientation=horizontal]:after:h-1 aria-[orientation=horizontal]:after:w-full aria-[orientation=horizontal]:after:translate-x-0 aria-[orientation=horizontal]:after:-translate-y-1/2 [&[aria-orientation=horizontal]>div]:rotate-90",
|
|
128
|
+
className
|
|
129
|
+
),
|
|
130
|
+
...props,
|
|
131
|
+
children: withHandle && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-border z-10 flex h-6 w-1 shrink-0 rounded-lg opacity-0 transition-opacity duration-150 group-hover/resize-handle:opacity-100 group-focus-visible/resize-handle:opacity-100" })
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
const badgeVariants = cva(
|
|
136
|
+
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
|
137
|
+
{
|
|
138
|
+
variants: {
|
|
139
|
+
variant: {
|
|
140
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
141
|
+
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
142
|
+
destructive: "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
|
|
143
|
+
outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
144
|
+
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
145
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
defaultVariants: {
|
|
149
|
+
variant: "default"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
function Badge({
|
|
154
|
+
className,
|
|
155
|
+
variant = "default",
|
|
156
|
+
render,
|
|
157
|
+
...props
|
|
158
|
+
}) {
|
|
159
|
+
return useRender({
|
|
160
|
+
defaultTagName: "span",
|
|
161
|
+
props: mergeProps(
|
|
162
|
+
{
|
|
163
|
+
className: cn(badgeVariants({ variant }), className)
|
|
164
|
+
},
|
|
165
|
+
props
|
|
166
|
+
),
|
|
167
|
+
render,
|
|
168
|
+
state: {
|
|
169
|
+
slot: "badge",
|
|
170
|
+
variant
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
const Select = SelectRoot;
|
|
175
|
+
function SelectGroup({ className, ...props }) {
|
|
176
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
177
|
+
SelectGroup$1,
|
|
178
|
+
{
|
|
179
|
+
"data-slot": "select-group",
|
|
180
|
+
className: cn("scroll-my-1 p-1", className),
|
|
181
|
+
...props
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
function SelectValue({ className, ...props }) {
|
|
186
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
187
|
+
SelectValue$1,
|
|
188
|
+
{
|
|
189
|
+
"data-slot": "select-value",
|
|
190
|
+
className: cn("flex flex-1 text-left", className),
|
|
191
|
+
...props
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
function SelectTrigger({
|
|
196
|
+
className,
|
|
197
|
+
size = "default",
|
|
198
|
+
children,
|
|
199
|
+
...props
|
|
200
|
+
}) {
|
|
201
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
202
|
+
SelectTrigger$1,
|
|
203
|
+
{
|
|
204
|
+
"data-slot": "select-trigger",
|
|
205
|
+
"data-size": size,
|
|
206
|
+
className: cn(
|
|
207
|
+
"border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 flex w-fit items-center justify-between gap-1.5 rounded-lg border bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
208
|
+
className
|
|
209
|
+
),
|
|
210
|
+
...props,
|
|
211
|
+
children: [
|
|
212
|
+
children,
|
|
213
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
214
|
+
SelectIcon,
|
|
215
|
+
{
|
|
216
|
+
render: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "text-muted-foreground pointer-events-none size-4" })
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
function SelectContent({
|
|
224
|
+
className,
|
|
225
|
+
children,
|
|
226
|
+
side = "bottom",
|
|
227
|
+
sideOffset = 4,
|
|
228
|
+
align = "center",
|
|
229
|
+
alignOffset = 0,
|
|
230
|
+
alignItemWithTrigger = true,
|
|
231
|
+
...props
|
|
232
|
+
}) {
|
|
233
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectPortal, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
234
|
+
SelectPositioner,
|
|
235
|
+
{
|
|
236
|
+
side,
|
|
237
|
+
sideOffset,
|
|
238
|
+
align,
|
|
239
|
+
alignOffset,
|
|
240
|
+
alignItemWithTrigger,
|
|
241
|
+
className: "isolate z-50",
|
|
242
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
243
|
+
SelectPopup,
|
|
244
|
+
{
|
|
245
|
+
"data-slot": "select-content",
|
|
246
|
+
"data-align-trigger": alignItemWithTrigger,
|
|
247
|
+
className: cn(
|
|
248
|
+
"bg-popover text-popover-foreground ring-foreground/10 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 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg shadow-md ring-1 duration-100 data-[align-trigger=true]:animate-none",
|
|
249
|
+
className
|
|
250
|
+
),
|
|
251
|
+
...props,
|
|
252
|
+
children: [
|
|
253
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollUpButton, {}),
|
|
254
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectList, { children }),
|
|
255
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollDownButton, {})
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
) });
|
|
261
|
+
}
|
|
262
|
+
function SelectItem({
|
|
263
|
+
className,
|
|
264
|
+
children,
|
|
265
|
+
...props
|
|
266
|
+
}) {
|
|
267
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
268
|
+
SelectItem$1,
|
|
269
|
+
{
|
|
270
|
+
"data-slot": "select-item",
|
|
271
|
+
className: cn(
|
|
272
|
+
"focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
273
|
+
className
|
|
274
|
+
),
|
|
275
|
+
...props,
|
|
276
|
+
children: [
|
|
277
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectItemText, { className: "flex flex-1 shrink-0 gap-2 whitespace-nowrap", children }),
|
|
278
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
279
|
+
SelectItemIndicator,
|
|
280
|
+
{
|
|
281
|
+
render: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center" }),
|
|
282
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "pointer-events-none" })
|
|
283
|
+
}
|
|
284
|
+
)
|
|
285
|
+
]
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
function SelectScrollUpButton({
|
|
290
|
+
className,
|
|
291
|
+
...props
|
|
292
|
+
}) {
|
|
293
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
294
|
+
SelectScrollUpArrow,
|
|
295
|
+
{
|
|
296
|
+
"data-slot": "select-scroll-up-button",
|
|
297
|
+
className: cn(
|
|
298
|
+
"bg-popover top-0 z-10 flex w-full cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4",
|
|
299
|
+
className
|
|
300
|
+
),
|
|
301
|
+
...props,
|
|
302
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, {})
|
|
303
|
+
}
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
function SelectScrollDownButton({
|
|
307
|
+
className,
|
|
308
|
+
...props
|
|
309
|
+
}) {
|
|
310
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
311
|
+
SelectScrollDownArrow,
|
|
312
|
+
{
|
|
313
|
+
"data-slot": "select-scroll-down-button",
|
|
314
|
+
className: cn(
|
|
315
|
+
"bg-popover bottom-0 z-10 flex w-full cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4",
|
|
316
|
+
className
|
|
317
|
+
),
|
|
318
|
+
...props,
|
|
319
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, {})
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
function getFileName(pathLike) {
|
|
324
|
+
const normalized = pathLike.replaceAll("\\", "/");
|
|
325
|
+
const parts = normalized.split("/");
|
|
326
|
+
return parts[parts.length - 1] ?? pathLike;
|
|
327
|
+
}
|
|
328
|
+
function stripExtension(fileName) {
|
|
329
|
+
return fileName.replace(/\.[^.]+$/, "");
|
|
330
|
+
}
|
|
331
|
+
function getFileDisplayLabel(file) {
|
|
332
|
+
const title = file.data.title;
|
|
333
|
+
if (typeof title === "string" && title.trim().length > 0) {
|
|
334
|
+
return title.trim();
|
|
335
|
+
}
|
|
336
|
+
const fileName = getFileName(file.filePath || file.id);
|
|
337
|
+
return stripExtension(fileName);
|
|
338
|
+
}
|
|
339
|
+
function getSortedCollectionFiles(collection) {
|
|
340
|
+
if (!collection) {
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
return [...collection.files].sort((a, b) => {
|
|
344
|
+
const labelCompare = getFileDisplayLabel(a).localeCompare(
|
|
345
|
+
getFileDisplayLabel(b),
|
|
346
|
+
void 0,
|
|
347
|
+
{ sensitivity: "base" }
|
|
348
|
+
);
|
|
349
|
+
if (labelCompare !== 0) {
|
|
350
|
+
return labelCompare;
|
|
351
|
+
}
|
|
352
|
+
return a.id.localeCompare(b.id, void 0, { sensitivity: "base" });
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
function areEditorSearchEqual(a, b) {
|
|
356
|
+
return a.collection === b.collection && a.file === b.file;
|
|
357
|
+
}
|
|
358
|
+
function resolveEditorSelection(collections, search) {
|
|
359
|
+
if (collections.length === 0) {
|
|
360
|
+
return {
|
|
361
|
+
selectedCollection: void 0,
|
|
362
|
+
selectedFile: void 0,
|
|
363
|
+
normalizedSearch: {}
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
const bySearchCollection = collections.find(
|
|
367
|
+
(collection) => collection.name === search.collection
|
|
368
|
+
);
|
|
369
|
+
const selectedCollection = bySearchCollection ?? collections[0];
|
|
370
|
+
const files = getSortedCollectionFiles(selectedCollection);
|
|
371
|
+
const bySearchFile = files.find((file) => file.id === search.file);
|
|
372
|
+
const firstFile = files.at(0);
|
|
373
|
+
const selectedFile = bySearchFile ?? firstFile;
|
|
374
|
+
return {
|
|
375
|
+
selectedCollection,
|
|
376
|
+
selectedFile,
|
|
377
|
+
normalizedSearch: {
|
|
378
|
+
collection: selectedCollection.name,
|
|
379
|
+
file: selectedFile ? selectedFile.id : void 0
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function LeftSidebar({
|
|
384
|
+
collections,
|
|
385
|
+
selectedCollectionName,
|
|
386
|
+
selectedFileId,
|
|
387
|
+
onSelectCollection,
|
|
388
|
+
onSelectFile
|
|
389
|
+
}) {
|
|
390
|
+
const selectedCollection = collections.find(
|
|
391
|
+
(collection) => collection.name === selectedCollectionName
|
|
392
|
+
);
|
|
393
|
+
const files = getSortedCollectionFiles(selectedCollection);
|
|
394
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full flex-col gap-3", children: [
|
|
395
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
396
|
+
Select,
|
|
397
|
+
{
|
|
398
|
+
value: selectedCollectionName,
|
|
399
|
+
onValueChange: (nextCollectionName) => {
|
|
400
|
+
if (!nextCollectionName) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
onSelectCollection(nextCollectionName);
|
|
404
|
+
},
|
|
405
|
+
children: [
|
|
406
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "bg-muted/50 focus-visible:ring-ring/35 w-full border-transparent shadow-none focus-visible:border-transparent focus-visible:ring-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "Select a collection" }) }),
|
|
407
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectGroup, { children: collections.map((collection) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: collection.name, children: collection.name }, collection.name)) }) })
|
|
408
|
+
]
|
|
409
|
+
}
|
|
410
|
+
),
|
|
411
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-background/35 min-h-0 flex-1 overflow-auto rounded-md p-1", children: files.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-muted-foreground px-2 py-2 text-sm", children: "No files in this collection." }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-0.5", children: files.map((file) => {
|
|
412
|
+
const isActive = file.id === selectedFileId;
|
|
413
|
+
const title = getFileDisplayLabel(file);
|
|
414
|
+
const slug = file.id;
|
|
415
|
+
const isMdxFile = /\.mdx$/i.test(file.filePath);
|
|
416
|
+
const isDraft = file.data.draft === true;
|
|
417
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
418
|
+
Button,
|
|
419
|
+
{
|
|
420
|
+
type: "button",
|
|
421
|
+
variant: "ghost",
|
|
422
|
+
className: cn(
|
|
423
|
+
"h-auto w-full justify-start border-0 px-2.5 py-3 text-left shadow-none",
|
|
424
|
+
"hover:bg-background/70",
|
|
425
|
+
isActive && "bg-muted text-foreground hover:bg-muted/55"
|
|
426
|
+
),
|
|
427
|
+
onClick: () => onSelectFile(file.id),
|
|
428
|
+
title: file.filePath,
|
|
429
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex w-full min-w-0 flex-col items-start gap-0.5", children: [
|
|
430
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-full truncate text-sm font-normal", children: title }),
|
|
431
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex w-full min-w-0 items-center gap-1.5", children: [
|
|
432
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground min-w-0 flex-1 truncate text-xs", children: slug }),
|
|
433
|
+
isMdxFile ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
434
|
+
Badge,
|
|
435
|
+
{
|
|
436
|
+
variant: "secondary",
|
|
437
|
+
className: "bg-muted h-4 border-transparent px-1.5 text-[10px] leading-none",
|
|
438
|
+
children: "MDX"
|
|
439
|
+
}
|
|
440
|
+
) : null,
|
|
441
|
+
isDraft ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
442
|
+
Badge,
|
|
443
|
+
{
|
|
444
|
+
variant: "outline",
|
|
445
|
+
className: "h-4 border-transparent bg-amber-100 px-1.5 text-[10px] leading-none text-amber-900 dark:bg-amber-900/40 dark:text-amber-200",
|
|
446
|
+
children: "Draft"
|
|
447
|
+
}
|
|
448
|
+
) : null
|
|
449
|
+
] })
|
|
450
|
+
] })
|
|
451
|
+
},
|
|
452
|
+
file.id
|
|
453
|
+
);
|
|
454
|
+
}) }) })
|
|
455
|
+
] });
|
|
456
|
+
}
|
|
457
|
+
function Textarea({ className, ...props }) {
|
|
458
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
459
|
+
"textarea",
|
|
460
|
+
{
|
|
461
|
+
"data-slot": "textarea",
|
|
462
|
+
className: cn(
|
|
463
|
+
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 disabled:bg-input/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 flex field-sizing-content min-h-16 w-full rounded-lg border bg-transparent px-2.5 py-2 text-base transition-colors outline-none focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 md:text-sm",
|
|
464
|
+
className
|
|
465
|
+
),
|
|
466
|
+
...props
|
|
467
|
+
}
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
function isRecord(value) {
|
|
471
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
472
|
+
}
|
|
473
|
+
function cloneRecord(value) {
|
|
474
|
+
return JSON.parse(JSON.stringify(value));
|
|
475
|
+
}
|
|
476
|
+
function isEqualValue(a, b) {
|
|
477
|
+
if (Object.is(a, b)) {
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
481
|
+
if (a.length !== b.length) {
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
for (let index = 0; index < a.length; index += 1) {
|
|
485
|
+
if (!isEqualValue(a[index], b[index])) {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
if (isRecord(a) && isRecord(b)) {
|
|
492
|
+
const aKeys = Object.keys(a);
|
|
493
|
+
const bKeys = Object.keys(b);
|
|
494
|
+
if (aKeys.length !== bKeys.length) {
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
for (const key of aKeys) {
|
|
498
|
+
if (!Object.prototype.hasOwnProperty.call(b, key)) {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
if (!isEqualValue(a[key], b[key])) {
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return true;
|
|
506
|
+
}
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
function normalizeChip(value) {
|
|
510
|
+
const trimmed = value.trim();
|
|
511
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
512
|
+
}
|
|
513
|
+
function asStringArray(value) {
|
|
514
|
+
if (!Array.isArray(value)) {
|
|
515
|
+
return [];
|
|
516
|
+
}
|
|
517
|
+
return value.filter((item) => typeof item === "string");
|
|
518
|
+
}
|
|
519
|
+
function buildNextDraft(currentDraft, field, value) {
|
|
520
|
+
const nextDraft = {
|
|
521
|
+
...currentDraft
|
|
522
|
+
};
|
|
523
|
+
if (value === void 0) {
|
|
524
|
+
delete nextDraft[field];
|
|
525
|
+
} else {
|
|
526
|
+
nextDraft[field] = value;
|
|
527
|
+
}
|
|
528
|
+
return nextDraft;
|
|
529
|
+
}
|
|
530
|
+
function getIsDirty(params) {
|
|
531
|
+
if (!isEqualValue(params.draft, params.initial)) {
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
return params.contentDraft !== params.initialContent;
|
|
535
|
+
}
|
|
536
|
+
const useFrontmatterEditorStore = create(
|
|
537
|
+
(set) => ({
|
|
538
|
+
collectionName: void 0,
|
|
539
|
+
fileId: void 0,
|
|
540
|
+
draft: {},
|
|
541
|
+
initial: {},
|
|
542
|
+
contentDraft: "",
|
|
543
|
+
initialContent: "",
|
|
544
|
+
dirty: false,
|
|
545
|
+
touched: {},
|
|
546
|
+
errors: {},
|
|
547
|
+
chipInputs: {},
|
|
548
|
+
validationRequested: false,
|
|
549
|
+
loadSelection: ({ collectionName, fileId, data, content }) => set((state) => {
|
|
550
|
+
const sameSelection = state.collectionName === collectionName && state.fileId === fileId;
|
|
551
|
+
if (sameSelection) {
|
|
552
|
+
return state;
|
|
553
|
+
}
|
|
554
|
+
const normalizedData = cloneRecord(data);
|
|
555
|
+
return {
|
|
556
|
+
collectionName,
|
|
557
|
+
fileId,
|
|
558
|
+
draft: normalizedData,
|
|
559
|
+
initial: cloneRecord(normalizedData),
|
|
560
|
+
contentDraft: content,
|
|
561
|
+
initialContent: content,
|
|
562
|
+
dirty: false,
|
|
563
|
+
touched: {},
|
|
564
|
+
errors: {},
|
|
565
|
+
chipInputs: {},
|
|
566
|
+
validationRequested: false
|
|
567
|
+
};
|
|
568
|
+
}),
|
|
569
|
+
clearSelection: () => set({
|
|
570
|
+
collectionName: void 0,
|
|
571
|
+
fileId: void 0,
|
|
572
|
+
draft: {},
|
|
573
|
+
initial: {},
|
|
574
|
+
contentDraft: "",
|
|
575
|
+
initialContent: "",
|
|
576
|
+
dirty: false,
|
|
577
|
+
touched: {},
|
|
578
|
+
errors: {},
|
|
579
|
+
chipInputs: {},
|
|
580
|
+
validationRequested: false
|
|
581
|
+
}),
|
|
582
|
+
setFieldValue: (field, value) => set((state) => {
|
|
583
|
+
const nextDraft = buildNextDraft(state.draft, field, value);
|
|
584
|
+
return {
|
|
585
|
+
draft: nextDraft,
|
|
586
|
+
dirty: getIsDirty({
|
|
587
|
+
draft: nextDraft,
|
|
588
|
+
initial: state.initial,
|
|
589
|
+
contentDraft: state.contentDraft,
|
|
590
|
+
initialContent: state.initialContent
|
|
591
|
+
})
|
|
592
|
+
};
|
|
593
|
+
}),
|
|
594
|
+
setContentDraft: (content) => set((state) => ({
|
|
595
|
+
contentDraft: content,
|
|
596
|
+
dirty: getIsDirty({
|
|
597
|
+
draft: state.draft,
|
|
598
|
+
initial: state.initial,
|
|
599
|
+
contentDraft: content,
|
|
600
|
+
initialContent: state.initialContent
|
|
601
|
+
})
|
|
602
|
+
})),
|
|
603
|
+
commitSavedState: () => set((state) => ({
|
|
604
|
+
initial: cloneRecord(state.draft),
|
|
605
|
+
initialContent: state.contentDraft,
|
|
606
|
+
dirty: false,
|
|
607
|
+
touched: {},
|
|
608
|
+
errors: {},
|
|
609
|
+
validationRequested: false
|
|
610
|
+
})),
|
|
611
|
+
setFieldTouched: (field) => set((state) => ({
|
|
612
|
+
touched: {
|
|
613
|
+
...state.touched,
|
|
614
|
+
[field]: true
|
|
615
|
+
}
|
|
616
|
+
})),
|
|
617
|
+
setValidationRequested: () => set({
|
|
618
|
+
validationRequested: true
|
|
619
|
+
}),
|
|
620
|
+
setValidationErrors: (errors) => set({
|
|
621
|
+
errors
|
|
622
|
+
}),
|
|
623
|
+
setChipInput: (field, value) => set((state) => ({
|
|
624
|
+
chipInputs: {
|
|
625
|
+
...state.chipInputs,
|
|
626
|
+
[field]: value
|
|
627
|
+
}
|
|
628
|
+
})),
|
|
629
|
+
addChipValue: (field, value) => set((state) => {
|
|
630
|
+
const normalizedValue = normalizeChip(value);
|
|
631
|
+
if (!normalizedValue) {
|
|
632
|
+
return state;
|
|
633
|
+
}
|
|
634
|
+
const existingValues = asStringArray(state.draft[field]);
|
|
635
|
+
if (existingValues.includes(normalizedValue)) {
|
|
636
|
+
return {
|
|
637
|
+
chipInputs: {
|
|
638
|
+
...state.chipInputs,
|
|
639
|
+
[field]: ""
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
const nextValues = [...existingValues, normalizedValue];
|
|
644
|
+
const nextDraft = buildNextDraft(state.draft, field, nextValues);
|
|
645
|
+
return {
|
|
646
|
+
draft: nextDraft,
|
|
647
|
+
dirty: getIsDirty({
|
|
648
|
+
draft: nextDraft,
|
|
649
|
+
initial: state.initial,
|
|
650
|
+
contentDraft: state.contentDraft,
|
|
651
|
+
initialContent: state.initialContent
|
|
652
|
+
}),
|
|
653
|
+
chipInputs: {
|
|
654
|
+
...state.chipInputs,
|
|
655
|
+
[field]: ""
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
}),
|
|
659
|
+
removeChipValue: (field, index) => set((state) => {
|
|
660
|
+
const existingValues = asStringArray(state.draft[field]);
|
|
661
|
+
if (index < 0 || index >= existingValues.length) {
|
|
662
|
+
return state;
|
|
663
|
+
}
|
|
664
|
+
const nextValues = existingValues.filter((_, itemIndex) => {
|
|
665
|
+
return itemIndex !== index;
|
|
666
|
+
});
|
|
667
|
+
const nextDraft = buildNextDraft(
|
|
668
|
+
state.draft,
|
|
669
|
+
field,
|
|
670
|
+
nextValues.length > 0 ? nextValues : void 0
|
|
671
|
+
);
|
|
672
|
+
return {
|
|
673
|
+
draft: nextDraft,
|
|
674
|
+
dirty: getIsDirty({
|
|
675
|
+
draft: nextDraft,
|
|
676
|
+
initial: state.initial,
|
|
677
|
+
contentDraft: state.contentDraft,
|
|
678
|
+
initialContent: state.initialContent
|
|
679
|
+
})
|
|
680
|
+
};
|
|
681
|
+
}),
|
|
682
|
+
removeLastChipValue: (field) => set((state) => {
|
|
683
|
+
const existingValues = asStringArray(state.draft[field]);
|
|
684
|
+
if (existingValues.length === 0) {
|
|
685
|
+
return state;
|
|
686
|
+
}
|
|
687
|
+
const nextValues = existingValues.slice(0, -1);
|
|
688
|
+
const nextDraft = buildNextDraft(
|
|
689
|
+
state.draft,
|
|
690
|
+
field,
|
|
691
|
+
nextValues.length > 0 ? nextValues : void 0
|
|
692
|
+
);
|
|
693
|
+
return {
|
|
694
|
+
draft: nextDraft,
|
|
695
|
+
dirty: getIsDirty({
|
|
696
|
+
draft: nextDraft,
|
|
697
|
+
initial: state.initial,
|
|
698
|
+
contentDraft: state.contentDraft,
|
|
699
|
+
initialContent: state.initialContent
|
|
700
|
+
})
|
|
701
|
+
};
|
|
702
|
+
})
|
|
703
|
+
})
|
|
704
|
+
);
|
|
705
|
+
function Editor() {
|
|
706
|
+
const contentDraft = useFrontmatterEditorStore((state) => state.contentDraft);
|
|
707
|
+
const setContentDraft = useFrontmatterEditorStore(
|
|
708
|
+
(state) => state.setContentDraft
|
|
709
|
+
);
|
|
710
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-full min-h-0 w-full overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-auto flex h-full min-h-0 w-full max-w-3xl flex-col overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
711
|
+
Textarea,
|
|
712
|
+
{
|
|
713
|
+
id: "editor-content",
|
|
714
|
+
className: "placeholder:text-muted-foreground/65 field-sizing-fixed h-full min-h-0 flex-1 resize-none overflow-auto rounded-none border-0 bg-transparent px-8 py-6 text-[16px] leading-8 shadow-none focus-visible:border-transparent focus-visible:ring-0",
|
|
715
|
+
value: contentDraft,
|
|
716
|
+
onChange: (event) => {
|
|
717
|
+
setContentDraft(event.target.value);
|
|
718
|
+
},
|
|
719
|
+
placeholder: "Start writing your article..."
|
|
720
|
+
}
|
|
721
|
+
) }) });
|
|
722
|
+
}
|
|
723
|
+
function Calendar({
|
|
724
|
+
className,
|
|
725
|
+
classNames,
|
|
726
|
+
showOutsideDays = true,
|
|
727
|
+
captionLayout = "label",
|
|
728
|
+
buttonVariant = "ghost",
|
|
729
|
+
locale,
|
|
730
|
+
formatters,
|
|
731
|
+
components,
|
|
732
|
+
...props
|
|
733
|
+
}) {
|
|
734
|
+
const defaultClassNames = getDefaultClassNames();
|
|
735
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
736
|
+
DayPicker,
|
|
737
|
+
{
|
|
738
|
+
showOutsideDays,
|
|
739
|
+
className: cn(
|
|
740
|
+
"group/calendar bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent",
|
|
741
|
+
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
|
742
|
+
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
|
743
|
+
className
|
|
744
|
+
),
|
|
745
|
+
captionLayout,
|
|
746
|
+
locale,
|
|
747
|
+
formatters: {
|
|
748
|
+
formatMonthDropdown: (date) => date.toLocaleString(locale?.code, { month: "short" }),
|
|
749
|
+
...formatters
|
|
750
|
+
},
|
|
751
|
+
classNames: {
|
|
752
|
+
root: cn("w-fit", defaultClassNames.root),
|
|
753
|
+
months: cn(
|
|
754
|
+
"relative flex flex-col gap-4 md:flex-row",
|
|
755
|
+
defaultClassNames.months
|
|
756
|
+
),
|
|
757
|
+
month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
|
|
758
|
+
nav: cn(
|
|
759
|
+
"absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
|
|
760
|
+
defaultClassNames.nav
|
|
761
|
+
),
|
|
762
|
+
button_previous: cn(
|
|
763
|
+
buttonVariants({ variant: buttonVariant }),
|
|
764
|
+
"size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
|
|
765
|
+
defaultClassNames.button_previous
|
|
766
|
+
),
|
|
767
|
+
button_next: cn(
|
|
768
|
+
buttonVariants({ variant: buttonVariant }),
|
|
769
|
+
"size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
|
|
770
|
+
defaultClassNames.button_next
|
|
771
|
+
),
|
|
772
|
+
month_caption: cn(
|
|
773
|
+
"flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
|
|
774
|
+
defaultClassNames.month_caption
|
|
775
|
+
),
|
|
776
|
+
dropdowns: cn(
|
|
777
|
+
"flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
|
|
778
|
+
defaultClassNames.dropdowns
|
|
779
|
+
),
|
|
780
|
+
dropdown_root: cn(
|
|
781
|
+
"relative rounded-(--cell-radius)",
|
|
782
|
+
defaultClassNames.dropdown_root
|
|
783
|
+
),
|
|
784
|
+
dropdown: cn(
|
|
785
|
+
"absolute inset-0 bg-popover opacity-0",
|
|
786
|
+
defaultClassNames.dropdown
|
|
787
|
+
),
|
|
788
|
+
caption_label: cn(
|
|
789
|
+
"font-medium select-none",
|
|
790
|
+
captionLayout === "label" ? "text-sm" : "flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
|
|
791
|
+
defaultClassNames.caption_label
|
|
792
|
+
),
|
|
793
|
+
table: "w-full border-collapse",
|
|
794
|
+
weekdays: cn("flex", defaultClassNames.weekdays),
|
|
795
|
+
weekday: cn(
|
|
796
|
+
"flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none",
|
|
797
|
+
defaultClassNames.weekday
|
|
798
|
+
),
|
|
799
|
+
week: cn("mt-2 flex w-full", defaultClassNames.week),
|
|
800
|
+
week_number_header: cn(
|
|
801
|
+
"w-(--cell-size) select-none",
|
|
802
|
+
defaultClassNames.week_number_header
|
|
803
|
+
),
|
|
804
|
+
week_number: cn(
|
|
805
|
+
"text-[0.8rem] text-muted-foreground select-none",
|
|
806
|
+
defaultClassNames.week_number
|
|
807
|
+
),
|
|
808
|
+
day: cn(
|
|
809
|
+
"group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)",
|
|
810
|
+
props.showWeekNumber ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)" : "[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)",
|
|
811
|
+
defaultClassNames.day
|
|
812
|
+
),
|
|
813
|
+
range_start: cn(
|
|
814
|
+
"relative isolate z-0 rounded-l-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted",
|
|
815
|
+
defaultClassNames.range_start
|
|
816
|
+
),
|
|
817
|
+
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
|
818
|
+
range_end: cn(
|
|
819
|
+
"relative isolate z-0 rounded-r-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted",
|
|
820
|
+
defaultClassNames.range_end
|
|
821
|
+
),
|
|
822
|
+
today: cn(
|
|
823
|
+
"rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none",
|
|
824
|
+
defaultClassNames.today
|
|
825
|
+
),
|
|
826
|
+
outside: cn(
|
|
827
|
+
"text-muted-foreground aria-selected:text-muted-foreground",
|
|
828
|
+
defaultClassNames.outside
|
|
829
|
+
),
|
|
830
|
+
disabled: cn(
|
|
831
|
+
"text-muted-foreground opacity-50",
|
|
832
|
+
defaultClassNames.disabled
|
|
833
|
+
),
|
|
834
|
+
hidden: cn("invisible", defaultClassNames.hidden),
|
|
835
|
+
...classNames
|
|
836
|
+
},
|
|
837
|
+
components: {
|
|
838
|
+
Root: ({ className: className2, rootRef, ...props2 }) => {
|
|
839
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
840
|
+
"div",
|
|
841
|
+
{
|
|
842
|
+
"data-slot": "calendar",
|
|
843
|
+
ref: rootRef,
|
|
844
|
+
className: cn(className2),
|
|
845
|
+
...props2
|
|
846
|
+
}
|
|
847
|
+
);
|
|
848
|
+
},
|
|
849
|
+
Chevron: ({ className: className2, orientation, ...props2 }) => {
|
|
850
|
+
if (orientation === "left") {
|
|
851
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, { className: cn("size-4", className2), ...props2 });
|
|
852
|
+
}
|
|
853
|
+
if (orientation === "right") {
|
|
854
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
855
|
+
ChevronRight,
|
|
856
|
+
{
|
|
857
|
+
className: cn("size-4", className2),
|
|
858
|
+
...props2
|
|
859
|
+
}
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: cn("size-4", className2), ...props2 });
|
|
863
|
+
},
|
|
864
|
+
DayButton: ({ ...props2 }) => /* @__PURE__ */ jsxRuntimeExports.jsx(CalendarDayButton, { locale, ...props2 }),
|
|
865
|
+
WeekNumber: ({ children, ...props2 }) => {
|
|
866
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("td", { ...props2, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex size-(--cell-size) items-center justify-center text-center", children }) });
|
|
867
|
+
},
|
|
868
|
+
...components
|
|
869
|
+
},
|
|
870
|
+
...props
|
|
871
|
+
}
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
function CalendarDayButton({
|
|
875
|
+
className,
|
|
876
|
+
day,
|
|
877
|
+
modifiers,
|
|
878
|
+
locale,
|
|
879
|
+
...props
|
|
880
|
+
}) {
|
|
881
|
+
const defaultClassNames = getDefaultClassNames();
|
|
882
|
+
const ref = reactExports.useRef(null);
|
|
883
|
+
reactExports.useEffect(() => {
|
|
884
|
+
if (modifiers.focused) ref.current?.focus();
|
|
885
|
+
}, [modifiers.focused]);
|
|
886
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
887
|
+
Button,
|
|
888
|
+
{
|
|
889
|
+
variant: "ghost",
|
|
890
|
+
size: "icon",
|
|
891
|
+
"data-day": day.date.toLocaleDateString(locale?.code),
|
|
892
|
+
"data-selected-single": modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle,
|
|
893
|
+
"data-range-start": modifiers.range_start,
|
|
894
|
+
"data-range-end": modifiers.range_end,
|
|
895
|
+
"data-range-middle": modifiers.range_middle,
|
|
896
|
+
className: cn(
|
|
897
|
+
"group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-foreground relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-r-(--cell-radius) data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-l-(--cell-radius) [&>span]:text-xs [&>span]:opacity-70",
|
|
898
|
+
defaultClassNames.day,
|
|
899
|
+
className
|
|
900
|
+
),
|
|
901
|
+
...props
|
|
902
|
+
}
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
function Input({ className, type, ...props }) {
|
|
906
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
907
|
+
Input$1,
|
|
908
|
+
{
|
|
909
|
+
type,
|
|
910
|
+
"data-slot": "input",
|
|
911
|
+
className: cn(
|
|
912
|
+
"border-input file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 disabled:bg-input/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 h-8 w-full min-w-0 rounded-lg border 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 focus-visible:ring-3 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 md:text-sm",
|
|
913
|
+
className
|
|
914
|
+
),
|
|
915
|
+
...props
|
|
916
|
+
}
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
function Label({ className, ...props }) {
|
|
920
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
921
|
+
"label",
|
|
922
|
+
{
|
|
923
|
+
"data-slot": "label",
|
|
924
|
+
className: cn(
|
|
925
|
+
"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",
|
|
926
|
+
className
|
|
927
|
+
),
|
|
928
|
+
...props
|
|
929
|
+
}
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
function Popover({ ...props }) {
|
|
933
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverRoot, { "data-slot": "popover", ...props });
|
|
934
|
+
}
|
|
935
|
+
function PopoverTrigger({ ...props }) {
|
|
936
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverTrigger$1, { "data-slot": "popover-trigger", ...props });
|
|
937
|
+
}
|
|
938
|
+
function PopoverContent({
|
|
939
|
+
className,
|
|
940
|
+
align = "center",
|
|
941
|
+
alignOffset = 0,
|
|
942
|
+
side = "bottom",
|
|
943
|
+
sideOffset = 4,
|
|
944
|
+
...props
|
|
945
|
+
}) {
|
|
946
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverPortal, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
947
|
+
PopoverPositioner,
|
|
948
|
+
{
|
|
949
|
+
align,
|
|
950
|
+
alignOffset,
|
|
951
|
+
side,
|
|
952
|
+
sideOffset,
|
|
953
|
+
className: "isolate z-50",
|
|
954
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
955
|
+
PopoverPopup,
|
|
956
|
+
{
|
|
957
|
+
"data-slot": "popover-content",
|
|
958
|
+
className: cn(
|
|
959
|
+
"bg-popover text-popover-foreground ring-foreground/10 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 z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-lg p-2.5 text-sm shadow-md ring-1 outline-hidden duration-100",
|
|
960
|
+
className
|
|
961
|
+
),
|
|
962
|
+
...props
|
|
963
|
+
}
|
|
964
|
+
)
|
|
965
|
+
}
|
|
966
|
+
) });
|
|
967
|
+
}
|
|
968
|
+
function Switch({
|
|
969
|
+
className,
|
|
970
|
+
size = "default",
|
|
971
|
+
...props
|
|
972
|
+
}) {
|
|
973
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
974
|
+
SwitchRoot,
|
|
975
|
+
{
|
|
976
|
+
"data-slot": "switch",
|
|
977
|
+
"data-size": size,
|
|
978
|
+
className: cn(
|
|
979
|
+
"peer group/switch focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 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 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:ring-3 aria-invalid:ring-3 data-disabled:cursor-not-allowed data-disabled:opacity-50 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px]",
|
|
980
|
+
className
|
|
981
|
+
),
|
|
982
|
+
...props,
|
|
983
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
984
|
+
SwitchThumb,
|
|
985
|
+
{
|
|
986
|
+
"data-slot": "switch-thumb",
|
|
987
|
+
className: "bg-background dark:data-checked:bg-primary-foreground dark:data-unchecked:bg-foreground pointer-events-none block rounded-full 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)] group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0"
|
|
988
|
+
}
|
|
989
|
+
)
|
|
990
|
+
}
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
function padTwo$1(value) {
|
|
994
|
+
return value.toString().padStart(2, "0");
|
|
995
|
+
}
|
|
996
|
+
function toDateTimeLocal(date) {
|
|
997
|
+
const year = date.getFullYear();
|
|
998
|
+
const month = padTwo$1(date.getMonth() + 1);
|
|
999
|
+
const day = padTwo$1(date.getDate());
|
|
1000
|
+
const hour = padTwo$1(date.getHours());
|
|
1001
|
+
const minute = padTwo$1(date.getMinutes());
|
|
1002
|
+
return `${year}-${month}-${day}T${hour}:${minute}`;
|
|
1003
|
+
}
|
|
1004
|
+
function parseUnixTimestamp(value) {
|
|
1005
|
+
if (!Number.isFinite(value)) {
|
|
1006
|
+
return void 0;
|
|
1007
|
+
}
|
|
1008
|
+
const millis = Math.abs(value) < 1e12 ? value * 1e3 : value;
|
|
1009
|
+
const parsed = new Date(millis);
|
|
1010
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
1011
|
+
return void 0;
|
|
1012
|
+
}
|
|
1013
|
+
return parsed;
|
|
1014
|
+
}
|
|
1015
|
+
function toDateTimeLocalValue(value) {
|
|
1016
|
+
if (typeof value === "string") {
|
|
1017
|
+
const trimmed = value.trim();
|
|
1018
|
+
if (trimmed.length === 0) {
|
|
1019
|
+
return "";
|
|
1020
|
+
}
|
|
1021
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) {
|
|
1022
|
+
return `${trimmed}T00:00`;
|
|
1023
|
+
}
|
|
1024
|
+
const parsed = new Date(trimmed);
|
|
1025
|
+
if (!Number.isNaN(parsed.getTime())) {
|
|
1026
|
+
return toDateTimeLocal(parsed);
|
|
1027
|
+
}
|
|
1028
|
+
return "";
|
|
1029
|
+
}
|
|
1030
|
+
if (typeof value === "number") {
|
|
1031
|
+
const parsed = parseUnixTimestamp(value);
|
|
1032
|
+
return parsed ? toDateTimeLocal(parsed) : "";
|
|
1033
|
+
}
|
|
1034
|
+
return "";
|
|
1035
|
+
}
|
|
1036
|
+
function fromDateTimeLocalToIso(value) {
|
|
1037
|
+
if (!value) {
|
|
1038
|
+
return void 0;
|
|
1039
|
+
}
|
|
1040
|
+
const parsed = new Date(value);
|
|
1041
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
1042
|
+
return void 0;
|
|
1043
|
+
}
|
|
1044
|
+
return parsed.toISOString();
|
|
1045
|
+
}
|
|
1046
|
+
const ROOT_VALIDATION_KEY = "_root";
|
|
1047
|
+
const ajv = new Ajv({
|
|
1048
|
+
allErrors: true,
|
|
1049
|
+
strict: false
|
|
1050
|
+
});
|
|
1051
|
+
addFormats(ajv);
|
|
1052
|
+
const validatorCache = /* @__PURE__ */ new WeakMap();
|
|
1053
|
+
function getSchemaValidator(schema) {
|
|
1054
|
+
const cached = validatorCache.get(schema);
|
|
1055
|
+
if (cached) {
|
|
1056
|
+
return cached;
|
|
1057
|
+
}
|
|
1058
|
+
const validator = ajv.compile(schema);
|
|
1059
|
+
validatorCache.set(schema, validator);
|
|
1060
|
+
return validator;
|
|
1061
|
+
}
|
|
1062
|
+
function decodePointerSegment(segment) {
|
|
1063
|
+
return segment.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
1064
|
+
}
|
|
1065
|
+
function getTopLevelErrorKey(error) {
|
|
1066
|
+
if (error.keyword === "required") {
|
|
1067
|
+
const missingProperty = typeof error.params.missingProperty === "string" ? error.params.missingProperty : void 0;
|
|
1068
|
+
return missingProperty ?? ROOT_VALIDATION_KEY;
|
|
1069
|
+
}
|
|
1070
|
+
const cleanedPath = error.instancePath.replace(/^\//, "");
|
|
1071
|
+
if (cleanedPath.length === 0) {
|
|
1072
|
+
return ROOT_VALIDATION_KEY;
|
|
1073
|
+
}
|
|
1074
|
+
const firstSegment = cleanedPath.split("/")[0];
|
|
1075
|
+
return firstSegment ? decodePointerSegment(firstSegment) : ROOT_VALIDATION_KEY;
|
|
1076
|
+
}
|
|
1077
|
+
function toErrorMessage(error) {
|
|
1078
|
+
const message = error.message?.trim();
|
|
1079
|
+
return message && message.length > 0 ? message : "invalid value";
|
|
1080
|
+
}
|
|
1081
|
+
function mapAjvErrors(errors) {
|
|
1082
|
+
if (!errors || errors.length === 0) {
|
|
1083
|
+
return {};
|
|
1084
|
+
}
|
|
1085
|
+
const mapped = {};
|
|
1086
|
+
for (const error of errors) {
|
|
1087
|
+
const key = getTopLevelErrorKey(error);
|
|
1088
|
+
const message = toErrorMessage(error);
|
|
1089
|
+
const existing = mapped[key] ?? [];
|
|
1090
|
+
if (!existing.includes(message)) {
|
|
1091
|
+
mapped[key] = [...existing, message];
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
return mapped;
|
|
1095
|
+
}
|
|
1096
|
+
function validateFrontmatterDraft(schema, draft) {
|
|
1097
|
+
const validator = getSchemaValidator(schema);
|
|
1098
|
+
const valid = validator(draft);
|
|
1099
|
+
return {
|
|
1100
|
+
valid: Boolean(valid),
|
|
1101
|
+
errors: mapAjvErrors(validator.errors)
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
const recordSchema = recordType(stringType(), unknownType());
|
|
1105
|
+
const jsonObjectSchema = objectType({
|
|
1106
|
+
type: literalType("object"),
|
|
1107
|
+
properties: recordSchema,
|
|
1108
|
+
required: arrayType(stringType()).optional()
|
|
1109
|
+
}).passthrough();
|
|
1110
|
+
const propertySchema = objectType({
|
|
1111
|
+
type: stringType().optional(),
|
|
1112
|
+
format: stringType().optional(),
|
|
1113
|
+
enum: arrayType(stringType()).optional(),
|
|
1114
|
+
anyOf: arrayType(recordSchema).optional(),
|
|
1115
|
+
items: recordSchema.optional()
|
|
1116
|
+
}).passthrough();
|
|
1117
|
+
const ASTRO_DEF_PREFIX = "#/definitions/";
|
|
1118
|
+
function asRecord(value) {
|
|
1119
|
+
const parsed = recordSchema.safeParse(value);
|
|
1120
|
+
return parsed.success ? parsed.data : void 0;
|
|
1121
|
+
}
|
|
1122
|
+
function parsePropertySchema(value) {
|
|
1123
|
+
const parsed = propertySchema.safeParse(value);
|
|
1124
|
+
return parsed.success ? parsed.data : void 0;
|
|
1125
|
+
}
|
|
1126
|
+
function asObjectSchema(value) {
|
|
1127
|
+
const parsed = jsonObjectSchema.safeParse(value);
|
|
1128
|
+
return parsed.success ? parsed.data : void 0;
|
|
1129
|
+
}
|
|
1130
|
+
function isStringArraySchema(schema) {
|
|
1131
|
+
if (schema.type !== "array") {
|
|
1132
|
+
return false;
|
|
1133
|
+
}
|
|
1134
|
+
const itemSchema = parsePropertySchema(schema.items);
|
|
1135
|
+
return itemSchema?.type === "string";
|
|
1136
|
+
}
|
|
1137
|
+
function isDateAnyOfSchema(schema) {
|
|
1138
|
+
if (!schema.anyOf || schema.anyOf.length === 0) {
|
|
1139
|
+
return false;
|
|
1140
|
+
}
|
|
1141
|
+
let hasDateOption = false;
|
|
1142
|
+
for (const option of schema.anyOf) {
|
|
1143
|
+
const candidate = parsePropertySchema(option);
|
|
1144
|
+
if (!candidate) {
|
|
1145
|
+
return false;
|
|
1146
|
+
}
|
|
1147
|
+
if (candidate.type === "string" && (candidate.format === "date" || candidate.format === "date-time")) {
|
|
1148
|
+
hasDateOption = true;
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
if ((candidate.type === "integer" || candidate.type === "number") && candidate.format === "unix-time") {
|
|
1152
|
+
hasDateOption = true;
|
|
1153
|
+
continue;
|
|
1154
|
+
}
|
|
1155
|
+
if (candidate.type === "null") {
|
|
1156
|
+
continue;
|
|
1157
|
+
}
|
|
1158
|
+
return false;
|
|
1159
|
+
}
|
|
1160
|
+
return hasDateOption;
|
|
1161
|
+
}
|
|
1162
|
+
function resolveRequiredFields(required) {
|
|
1163
|
+
const parsed = arrayType(stringType()).safeParse(required);
|
|
1164
|
+
return new Set(parsed.success ? parsed.data : []);
|
|
1165
|
+
}
|
|
1166
|
+
function resolveCustomFieldKind(params) {
|
|
1167
|
+
const customKind = params.fieldUi?.[params.key];
|
|
1168
|
+
if (!customKind) {
|
|
1169
|
+
return void 0;
|
|
1170
|
+
}
|
|
1171
|
+
if (customKind.kind === "image" && params.property.type === "string") {
|
|
1172
|
+
return {
|
|
1173
|
+
kind: "image",
|
|
1174
|
+
key: params.key,
|
|
1175
|
+
required: params.required,
|
|
1176
|
+
sourceMode: customKind.mode
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
if (customKind.kind === "imageArray" && isStringArraySchema(params.property)) {
|
|
1180
|
+
return {
|
|
1181
|
+
kind: "imageArray",
|
|
1182
|
+
key: params.key,
|
|
1183
|
+
required: params.required,
|
|
1184
|
+
sourceMode: customKind.mode
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
if (customKind.kind === "color" && params.property.type === "string") {
|
|
1188
|
+
return {
|
|
1189
|
+
kind: "color",
|
|
1190
|
+
key: params.key,
|
|
1191
|
+
required: params.required
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
return void 0;
|
|
1195
|
+
}
|
|
1196
|
+
function resolveAstroObjectSchema(schema) {
|
|
1197
|
+
if (!schema) {
|
|
1198
|
+
return void 0;
|
|
1199
|
+
}
|
|
1200
|
+
const direct = asObjectSchema(schema);
|
|
1201
|
+
if (direct) {
|
|
1202
|
+
return direct;
|
|
1203
|
+
}
|
|
1204
|
+
const ref = typeof schema.$ref === "string" ? schema.$ref : void 0;
|
|
1205
|
+
if (!ref?.startsWith(ASTRO_DEF_PREFIX)) {
|
|
1206
|
+
return void 0;
|
|
1207
|
+
}
|
|
1208
|
+
const definitionKey = ref.slice(ASTRO_DEF_PREFIX.length);
|
|
1209
|
+
const definitions = asRecord(schema.definitions);
|
|
1210
|
+
const definitionSchema = definitions ? definitions[definitionKey] : void 0;
|
|
1211
|
+
return asObjectSchema(definitionSchema);
|
|
1212
|
+
}
|
|
1213
|
+
function resolveSchemaFields(schema, fieldUi) {
|
|
1214
|
+
const requiredFields = resolveRequiredFields(schema.required);
|
|
1215
|
+
const fields = [];
|
|
1216
|
+
for (const [key, rawPropertySchema] of Object.entries(schema.properties)) {
|
|
1217
|
+
if (key === "$schema") {
|
|
1218
|
+
continue;
|
|
1219
|
+
}
|
|
1220
|
+
const parsedProperty = parsePropertySchema(rawPropertySchema);
|
|
1221
|
+
const isRequired = requiredFields.has(key);
|
|
1222
|
+
if (!parsedProperty) {
|
|
1223
|
+
fields.push({
|
|
1224
|
+
kind: "unsupported",
|
|
1225
|
+
key,
|
|
1226
|
+
required: isRequired,
|
|
1227
|
+
reason: "invalid schema node"
|
|
1228
|
+
});
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
const customField = resolveCustomFieldKind({
|
|
1232
|
+
key,
|
|
1233
|
+
property: parsedProperty,
|
|
1234
|
+
required: isRequired,
|
|
1235
|
+
fieldUi
|
|
1236
|
+
});
|
|
1237
|
+
if (customField) {
|
|
1238
|
+
fields.push(customField);
|
|
1239
|
+
continue;
|
|
1240
|
+
}
|
|
1241
|
+
if (isDateAnyOfSchema(parsedProperty)) {
|
|
1242
|
+
fields.push({ kind: "dateAnyOf", key, required: isRequired });
|
|
1243
|
+
continue;
|
|
1244
|
+
}
|
|
1245
|
+
if (parsedProperty.type === "string" && (parsedProperty.format === "date" || parsedProperty.format === "date-time")) {
|
|
1246
|
+
fields.push({ kind: "dateAnyOf", key, required: isRequired });
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
if ((parsedProperty.type === "number" || parsedProperty.type === "integer") && parsedProperty.format === "unix-time") {
|
|
1250
|
+
fields.push({ kind: "dateAnyOf", key, required: isRequired });
|
|
1251
|
+
continue;
|
|
1252
|
+
}
|
|
1253
|
+
const enumValues = parsedProperty.enum;
|
|
1254
|
+
if (enumValues && enumValues.length > 0) {
|
|
1255
|
+
fields.push({
|
|
1256
|
+
kind: "enum",
|
|
1257
|
+
key,
|
|
1258
|
+
required: isRequired,
|
|
1259
|
+
options: enumValues
|
|
1260
|
+
});
|
|
1261
|
+
continue;
|
|
1262
|
+
}
|
|
1263
|
+
if (parsedProperty.type === "string") {
|
|
1264
|
+
fields.push({ kind: "string", key, required: isRequired });
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
if (parsedProperty.type === "boolean") {
|
|
1268
|
+
fields.push({ kind: "boolean", key, required: isRequired });
|
|
1269
|
+
continue;
|
|
1270
|
+
}
|
|
1271
|
+
if (parsedProperty.type === "number" || parsedProperty.type === "integer") {
|
|
1272
|
+
fields.push({ kind: "number", key, required: isRequired });
|
|
1273
|
+
continue;
|
|
1274
|
+
}
|
|
1275
|
+
if (isStringArraySchema(parsedProperty)) {
|
|
1276
|
+
fields.push({ kind: "stringArray", key, required: isRequired });
|
|
1277
|
+
continue;
|
|
1278
|
+
}
|
|
1279
|
+
if (parsedProperty.type === "array") {
|
|
1280
|
+
fields.push({
|
|
1281
|
+
kind: "unsupported",
|
|
1282
|
+
key,
|
|
1283
|
+
required: isRequired,
|
|
1284
|
+
reason: "array items are not string"
|
|
1285
|
+
});
|
|
1286
|
+
continue;
|
|
1287
|
+
}
|
|
1288
|
+
fields.push({
|
|
1289
|
+
kind: "unsupported",
|
|
1290
|
+
key,
|
|
1291
|
+
required: isRequired,
|
|
1292
|
+
reason: "schema type not supported"
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
return fields;
|
|
1296
|
+
}
|
|
1297
|
+
const SUBTLE_FIELD_CLASS = "border-transparent bg-muted/50 shadow-none focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-ring/35";
|
|
1298
|
+
const MAX_ASSET_RESULTS = 120;
|
|
1299
|
+
const HEX_COLOR_REGEX = /^#[0-9a-fA-F]{6}$/;
|
|
1300
|
+
function isObjectRecord$1(value) {
|
|
1301
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1302
|
+
}
|
|
1303
|
+
const listImageAssets = createServerFn({
|
|
1304
|
+
method: "POST"
|
|
1305
|
+
}).inputValidator((payload) => {
|
|
1306
|
+
if (!isObjectRecord$1(payload)) {
|
|
1307
|
+
throw new Error("Invalid asset list payload.");
|
|
1308
|
+
}
|
|
1309
|
+
const currentFilePath = payload.currentFilePath;
|
|
1310
|
+
if (typeof currentFilePath !== "string" || currentFilePath.length === 0) {
|
|
1311
|
+
throw new Error("Missing current file path.");
|
|
1312
|
+
}
|
|
1313
|
+
const sourceMode = payload.sourceMode;
|
|
1314
|
+
if (sourceMode !== "asset" && sourceMode !== "public") {
|
|
1315
|
+
throw new Error("Invalid image source mode.");
|
|
1316
|
+
}
|
|
1317
|
+
return {
|
|
1318
|
+
currentFilePath,
|
|
1319
|
+
sourceMode
|
|
1320
|
+
};
|
|
1321
|
+
}).handler(createSsrRpc("c39d1a9dd4bf0ef421bf4f2daf36aab12030ba358e594364ba10b3659b27b480"));
|
|
1322
|
+
const getImageAssetPreview = createServerFn({
|
|
1323
|
+
method: "POST"
|
|
1324
|
+
}).inputValidator((payload) => {
|
|
1325
|
+
if (!isObjectRecord$1(payload)) {
|
|
1326
|
+
throw new Error("Invalid asset preview payload.");
|
|
1327
|
+
}
|
|
1328
|
+
const assetId = payload.assetId;
|
|
1329
|
+
if (typeof assetId !== "string" || assetId.length === 0) {
|
|
1330
|
+
throw new Error("Missing asset id.");
|
|
1331
|
+
}
|
|
1332
|
+
return {
|
|
1333
|
+
assetId
|
|
1334
|
+
};
|
|
1335
|
+
}).handler(createSsrRpc("91a56e4138efcf4efad5b435140f056a882a5f54203e1c4d3b0f274ea2f876f5"));
|
|
1336
|
+
function toNormalCaseLabel(fieldName) {
|
|
1337
|
+
const normalized = fieldName.replace(/[_-]+/g, " ").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/\s+/g, " ").trim();
|
|
1338
|
+
if (normalized.length === 0) {
|
|
1339
|
+
return fieldName;
|
|
1340
|
+
}
|
|
1341
|
+
return normalized.split(" ").map((word) => {
|
|
1342
|
+
if (word === word.toUpperCase() && word.length > 1) {
|
|
1343
|
+
return word;
|
|
1344
|
+
}
|
|
1345
|
+
return word[0] ? word[0].toUpperCase() + word.slice(1).toLowerCase() : word;
|
|
1346
|
+
}).join(" ");
|
|
1347
|
+
}
|
|
1348
|
+
function getStringArrayValue(value) {
|
|
1349
|
+
if (!Array.isArray(value)) {
|
|
1350
|
+
return [];
|
|
1351
|
+
}
|
|
1352
|
+
return value.filter((item) => typeof item === "string");
|
|
1353
|
+
}
|
|
1354
|
+
function getDateTimeParts(value) {
|
|
1355
|
+
const localValue = toDateTimeLocalValue(value);
|
|
1356
|
+
if (!localValue) {
|
|
1357
|
+
return {
|
|
1358
|
+
date: "",
|
|
1359
|
+
time: "00:00"
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
const [date = "", time = "00:00"] = localValue.split("T");
|
|
1363
|
+
return {
|
|
1364
|
+
date,
|
|
1365
|
+
time
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
function padTwo(value) {
|
|
1369
|
+
return value.toString().padStart(2, "0");
|
|
1370
|
+
}
|
|
1371
|
+
function formatLocalDate(date) {
|
|
1372
|
+
const year = date.getFullYear();
|
|
1373
|
+
const month = padTwo(date.getMonth() + 1);
|
|
1374
|
+
const day = padTwo(date.getDate());
|
|
1375
|
+
return `${year}-${month}-${day}`;
|
|
1376
|
+
}
|
|
1377
|
+
function getIsoFromDateAndTime(dateValue, timeValue) {
|
|
1378
|
+
if (!dateValue) {
|
|
1379
|
+
return void 0;
|
|
1380
|
+
}
|
|
1381
|
+
return fromDateTimeLocalToIso(`${dateValue}T${timeValue || "00:00"}`);
|
|
1382
|
+
}
|
|
1383
|
+
function normalizeHexColor(value) {
|
|
1384
|
+
return value.trim().toUpperCase();
|
|
1385
|
+
}
|
|
1386
|
+
function isHexColor(value) {
|
|
1387
|
+
return HEX_COLOR_REGEX.test(value);
|
|
1388
|
+
}
|
|
1389
|
+
const HOUR_OPTIONS = Array.from({
|
|
1390
|
+
length: 24
|
|
1391
|
+
}, (_, index) => padTwo(index));
|
|
1392
|
+
const MINUTE_OPTIONS = Array.from({
|
|
1393
|
+
length: 60
|
|
1394
|
+
}, (_, index) => padTwo(index));
|
|
1395
|
+
function NumberField({
|
|
1396
|
+
fieldKey,
|
|
1397
|
+
value,
|
|
1398
|
+
onChange,
|
|
1399
|
+
onBlur
|
|
1400
|
+
}) {
|
|
1401
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(Input, { id: fieldKey, type: "number", className: SUBTLE_FIELD_CLASS, value: typeof value === "number" || typeof value === "string" ? String(value) : "", onChange: (event) => {
|
|
1402
|
+
const nextRaw = event.target.value;
|
|
1403
|
+
if (nextRaw.trim() === "") {
|
|
1404
|
+
onChange(void 0);
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
const parsed = Number(nextRaw);
|
|
1408
|
+
onChange(Number.isFinite(parsed) ? parsed : nextRaw);
|
|
1409
|
+
}, onBlur });
|
|
1410
|
+
}
|
|
1411
|
+
function BooleanField({
|
|
1412
|
+
fieldKey,
|
|
1413
|
+
value,
|
|
1414
|
+
onChange,
|
|
1415
|
+
onBlur
|
|
1416
|
+
}) {
|
|
1417
|
+
const isChecked = value === true;
|
|
1418
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-muted/50 flex h-9 items-center justify-between rounded-md px-3", children: [
|
|
1419
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground text-sm", children: isChecked ? "Enabled" : "Disabled" }),
|
|
1420
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Switch, { id: fieldKey, checked: isChecked, onCheckedChange: (checked) => onChange(checked), onBlur, "aria-label": fieldKey })
|
|
1421
|
+
] });
|
|
1422
|
+
}
|
|
1423
|
+
function DateAnyOfField({
|
|
1424
|
+
fieldKey,
|
|
1425
|
+
value,
|
|
1426
|
+
onChange,
|
|
1427
|
+
onBlur
|
|
1428
|
+
}) {
|
|
1429
|
+
const {
|
|
1430
|
+
date,
|
|
1431
|
+
time
|
|
1432
|
+
} = getDateTimeParts(value);
|
|
1433
|
+
const [hour = "00", minute = "00"] = time.split(":");
|
|
1434
|
+
const selectedDate = date ? /* @__PURE__ */ new Date(`${date}T00:00:00`) : void 0;
|
|
1435
|
+
const handleDateSelect = (nextDate) => {
|
|
1436
|
+
if (!nextDate) {
|
|
1437
|
+
onChange(void 0);
|
|
1438
|
+
onBlur();
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
const nextDatePart = formatLocalDate(nextDate);
|
|
1442
|
+
onChange(getIsoFromDateAndTime(nextDatePart, `${hour}:${minute}`));
|
|
1443
|
+
onBlur();
|
|
1444
|
+
};
|
|
1445
|
+
const handleTimeSelect = (nextHour, nextMinute) => {
|
|
1446
|
+
onChange(getIsoFromDateAndTime(date, `${nextHour}:${nextMinute}`));
|
|
1447
|
+
onBlur();
|
|
1448
|
+
};
|
|
1449
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1450
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(Popover, { children: [
|
|
1451
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(PopoverTrigger, { className: cn(buttonVariants({
|
|
1452
|
+
variant: "ghost"
|
|
1453
|
+
}), "bg-muted/50 hover:bg-muted/65 h-9 w-full justify-start border-0 px-3 text-sm font-normal shadow-none"), "aria-label": `${fieldKey} date`, children: [
|
|
1454
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(CalendarDays, { className: "text-muted-foreground size-4" }),
|
|
1455
|
+
selectedDate ? selectedDate.toLocaleDateString(void 0, {
|
|
1456
|
+
month: "short",
|
|
1457
|
+
day: "2-digit",
|
|
1458
|
+
year: "numeric"
|
|
1459
|
+
}) : "Pick a date"
|
|
1460
|
+
] }),
|
|
1461
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(PopoverContent, { className: "w-auto p-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Calendar, { mode: "single", selected: selectedDate, onSelect: handleDateSelect }) })
|
|
1462
|
+
] }),
|
|
1463
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
1464
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
|
|
1465
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock3, { className: "text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2" }),
|
|
1466
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: hour, onValueChange: (nextHour) => {
|
|
1467
|
+
if (!nextHour) {
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
handleTimeSelect(nextHour, minute);
|
|
1471
|
+
}, disabled: !date, children: [
|
|
1472
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "bg-muted/50 focus-visible:ring-ring/35 w-full border-transparent pl-8 shadow-none focus-visible:border-transparent focus-visible:ring-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "HH" }) }),
|
|
1473
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectGroup, { children: HOUR_OPTIONS.map((hourOption) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: hourOption, children: hourOption }, `hour-${hourOption}`)) }) })
|
|
1474
|
+
] })
|
|
1475
|
+
] }),
|
|
1476
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: minute, onValueChange: (nextMinute) => {
|
|
1477
|
+
if (!nextMinute) {
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
handleTimeSelect(hour, nextMinute);
|
|
1481
|
+
}, disabled: !date, children: [
|
|
1482
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "bg-muted/50 focus-visible:ring-ring/35 w-full border-transparent shadow-none focus-visible:border-transparent focus-visible:ring-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "MM" }) }),
|
|
1483
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectGroup, { children: MINUTE_OPTIONS.map((minuteOption) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: minuteOption, children: minuteOption }, `minute-${minuteOption}`)) }) })
|
|
1484
|
+
] })
|
|
1485
|
+
] })
|
|
1486
|
+
] });
|
|
1487
|
+
}
|
|
1488
|
+
function EnumField({
|
|
1489
|
+
fieldKey,
|
|
1490
|
+
value,
|
|
1491
|
+
options,
|
|
1492
|
+
onChange
|
|
1493
|
+
}) {
|
|
1494
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: typeof value === "string" ? value : void 0, onValueChange: (nextValue) => {
|
|
1495
|
+
if (!nextValue) {
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
onChange(nextValue);
|
|
1499
|
+
}, children: [
|
|
1500
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "bg-muted/50 focus-visible:ring-ring/35 w-full border-transparent shadow-none focus-visible:border-transparent focus-visible:ring-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "Select a value" }) }),
|
|
1501
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectGroup, { children: options.map((option) => {
|
|
1502
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: option, children: option }, `${fieldKey}-${option}`);
|
|
1503
|
+
}) }) })
|
|
1504
|
+
] });
|
|
1505
|
+
}
|
|
1506
|
+
function StringArrayField({
|
|
1507
|
+
fieldKey,
|
|
1508
|
+
values,
|
|
1509
|
+
chipInput,
|
|
1510
|
+
onChipInputChange,
|
|
1511
|
+
onAddChip,
|
|
1512
|
+
onRemoveChip,
|
|
1513
|
+
onBackspaceEmpty,
|
|
1514
|
+
onBlur,
|
|
1515
|
+
placeholder
|
|
1516
|
+
}) {
|
|
1517
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1518
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-1.5", children: values.map((chip, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { variant: "secondary", className: "bg-muted gap-1 border-transparent tracking-normal normal-case", children: [
|
|
1519
|
+
chip,
|
|
1520
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "text-muted-foreground hover:bg-accent hover:text-foreground inline-flex size-4 items-center justify-center rounded-full", onClick: () => onRemoveChip(index), "aria-label": `Remove ${chip}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-3" }) })
|
|
1521
|
+
] }, `${fieldKey}-${chip}-${index}`)) }),
|
|
1522
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Input, { id: fieldKey, className: SUBTLE_FIELD_CLASS, value: chipInput, onChange: (event) => onChipInputChange(event.target.value), onKeyDown: (event) => {
|
|
1523
|
+
if (event.key === "Enter" || event.key === ",") {
|
|
1524
|
+
event.preventDefault();
|
|
1525
|
+
onAddChip();
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
if (event.key === "Backspace" && chipInput.trim().length === 0) {
|
|
1529
|
+
event.preventDefault();
|
|
1530
|
+
onBackspaceEmpty();
|
|
1531
|
+
}
|
|
1532
|
+
}, onBlur, placeholder: placeholder ?? "Type and press Enter" })
|
|
1533
|
+
] });
|
|
1534
|
+
}
|
|
1535
|
+
function AssetThumbnail({
|
|
1536
|
+
assetId,
|
|
1537
|
+
label
|
|
1538
|
+
}) {
|
|
1539
|
+
const [previewUrl, setPreviewUrl] = reactExports.useState();
|
|
1540
|
+
const [hasFailed, setHasFailed] = reactExports.useState(false);
|
|
1541
|
+
const requestIdRef = reactExports.useRef(0);
|
|
1542
|
+
reactExports.useEffect(() => {
|
|
1543
|
+
requestIdRef.current += 1;
|
|
1544
|
+
const requestId = requestIdRef.current;
|
|
1545
|
+
let objectUrl;
|
|
1546
|
+
void (async () => {
|
|
1547
|
+
try {
|
|
1548
|
+
setHasFailed(false);
|
|
1549
|
+
const response = await getImageAssetPreview({
|
|
1550
|
+
data: {
|
|
1551
|
+
assetId
|
|
1552
|
+
}
|
|
1553
|
+
});
|
|
1554
|
+
if (!response.ok) {
|
|
1555
|
+
throw new Error(`Preview request failed (${response.status})`);
|
|
1556
|
+
}
|
|
1557
|
+
const blob = await response.blob();
|
|
1558
|
+
if (requestIdRef.current !== requestId) {
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
objectUrl = URL.createObjectURL(blob);
|
|
1562
|
+
setPreviewUrl(objectUrl);
|
|
1563
|
+
} catch {
|
|
1564
|
+
if (requestIdRef.current !== requestId) {
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
setHasFailed(true);
|
|
1568
|
+
setPreviewUrl(void 0);
|
|
1569
|
+
}
|
|
1570
|
+
})();
|
|
1571
|
+
return () => {
|
|
1572
|
+
requestIdRef.current += 1;
|
|
1573
|
+
if (objectUrl) {
|
|
1574
|
+
URL.revokeObjectURL(objectUrl);
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
}, [assetId]);
|
|
1578
|
+
if (previewUrl) {
|
|
1579
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: previewUrl, alt: label, className: "h-24 w-full rounded-md object-cover" });
|
|
1580
|
+
}
|
|
1581
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-muted/55 text-muted-foreground flex h-24 w-full items-center justify-center rounded-md", children: hasFailed ? /* @__PURE__ */ jsxRuntimeExports.jsx(Image, { className: "size-5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-5 animate-spin" }) });
|
|
1582
|
+
}
|
|
1583
|
+
function AssetPicker({
|
|
1584
|
+
currentFilePath,
|
|
1585
|
+
sourceMode,
|
|
1586
|
+
triggerLabel,
|
|
1587
|
+
onSelectPath
|
|
1588
|
+
}) {
|
|
1589
|
+
const [open, setOpen] = reactExports.useState(false);
|
|
1590
|
+
const [query, setQuery] = reactExports.useState("");
|
|
1591
|
+
const [loading, setLoading] = reactExports.useState(false);
|
|
1592
|
+
const [error, setError] = reactExports.useState();
|
|
1593
|
+
const [assets, setAssets] = reactExports.useState([]);
|
|
1594
|
+
const [loadedForKey, setLoadedForKey] = reactExports.useState();
|
|
1595
|
+
reactExports.useEffect(() => {
|
|
1596
|
+
const requestKey = `${currentFilePath}::${sourceMode}`;
|
|
1597
|
+
if (!open || loadedForKey === requestKey) {
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1600
|
+
let isCancelled = false;
|
|
1601
|
+
setLoading(true);
|
|
1602
|
+
setError(void 0);
|
|
1603
|
+
void listImageAssets({
|
|
1604
|
+
data: {
|
|
1605
|
+
currentFilePath,
|
|
1606
|
+
sourceMode
|
|
1607
|
+
}
|
|
1608
|
+
}).then((nextAssets) => {
|
|
1609
|
+
if (isCancelled) {
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
setAssets(nextAssets);
|
|
1613
|
+
setLoadedForKey(requestKey);
|
|
1614
|
+
}).catch((fetchError) => {
|
|
1615
|
+
if (isCancelled) {
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
const message = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
1619
|
+
setError(message);
|
|
1620
|
+
}).finally(() => {
|
|
1621
|
+
if (!isCancelled) {
|
|
1622
|
+
setLoading(false);
|
|
1623
|
+
}
|
|
1624
|
+
});
|
|
1625
|
+
return () => {
|
|
1626
|
+
isCancelled = true;
|
|
1627
|
+
};
|
|
1628
|
+
}, [currentFilePath, loadedForKey, open, sourceMode]);
|
|
1629
|
+
const filteredAssets = reactExports.useMemo(() => {
|
|
1630
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
1631
|
+
return assets.filter((asset) => {
|
|
1632
|
+
if (normalizedQuery.length === 0) {
|
|
1633
|
+
return true;
|
|
1634
|
+
}
|
|
1635
|
+
return asset.displayPath.toLowerCase().includes(normalizedQuery) || asset.value.toLowerCase().includes(normalizedQuery);
|
|
1636
|
+
});
|
|
1637
|
+
}, [assets, query]);
|
|
1638
|
+
const visibleAssets = filteredAssets.slice(0, MAX_ASSET_RESULTS);
|
|
1639
|
+
const sourceLabel = sourceMode === "public" ? "public" : "src";
|
|
1640
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
1641
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(PopoverTrigger, { className: cn(buttonVariants({
|
|
1642
|
+
variant: "outline"
|
|
1643
|
+
}), "h-8 border-dashed text-xs"), children: [
|
|
1644
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Image, { className: "size-3.5" }),
|
|
1645
|
+
triggerLabel
|
|
1646
|
+
] }),
|
|
1647
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(PopoverContent, { className: "w-[34rem] max-w-[calc(100vw-2rem)] gap-2 p-3", children: [
|
|
1648
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
1649
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { variant: "secondary", className: "bg-muted text-foreground/75 rounded-md border-transparent text-[11px] normal-case", children: [
|
|
1650
|
+
"source: ",
|
|
1651
|
+
sourceLabel
|
|
1652
|
+
] }),
|
|
1653
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-60 max-w-full", children: [
|
|
1654
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2" }),
|
|
1655
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Input, { value: query, onChange: (event) => {
|
|
1656
|
+
setQuery(event.target.value);
|
|
1657
|
+
}, className: "bg-muted/50 focus-visible:ring-ring/35 h-8 border-transparent pl-8 text-xs shadow-none focus-visible:border-transparent focus-visible:ring-2", placeholder: "Search assets...", "aria-label": "Search assets" })
|
|
1658
|
+
] })
|
|
1659
|
+
] }),
|
|
1660
|
+
loading ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-muted-foreground flex h-32 items-center justify-center gap-2 text-sm", children: [
|
|
1661
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-4 animate-spin" }),
|
|
1662
|
+
"Loading assets..."
|
|
1663
|
+
] }) : null,
|
|
1664
|
+
error ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-destructive rounded-md bg-red-500/10 px-3 py-2 text-xs", children: error }) : null,
|
|
1665
|
+
!loading && !error ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
1666
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid max-h-[22rem] grid-cols-3 gap-2 overflow-auto pr-1", children: visibleAssets.map((asset) => /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", className: "border-border/40 hover:border-ring/35 bg-muted/25 hover:bg-muted/35 space-y-1.5 rounded-lg border p-1.5 text-left transition-colors", onClick: () => {
|
|
1667
|
+
onSelectPath(asset.value);
|
|
1668
|
+
setOpen(false);
|
|
1669
|
+
}, title: asset.value, children: [
|
|
1670
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(AssetThumbnail, { assetId: asset.id, label: asset.displayPath }),
|
|
1671
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-foreground/85 truncate text-[11px]", children: asset.displayPath })
|
|
1672
|
+
] }, asset.id)) }),
|
|
1673
|
+
filteredAssets.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-muted-foreground text-xs", children: "No assets found." }) : null,
|
|
1674
|
+
filteredAssets.length > MAX_ASSET_RESULTS ? /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-muted-foreground text-xs", children: [
|
|
1675
|
+
"Showing first ",
|
|
1676
|
+
MAX_ASSET_RESULTS,
|
|
1677
|
+
" assets. Refine search for more."
|
|
1678
|
+
] }) : null
|
|
1679
|
+
] }) : null
|
|
1680
|
+
] })
|
|
1681
|
+
] });
|
|
1682
|
+
}
|
|
1683
|
+
function ImageField({
|
|
1684
|
+
fieldKey,
|
|
1685
|
+
currentFilePath,
|
|
1686
|
+
sourceMode,
|
|
1687
|
+
value,
|
|
1688
|
+
onChange,
|
|
1689
|
+
onBlur
|
|
1690
|
+
}) {
|
|
1691
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1692
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Input, { id: fieldKey, className: SUBTLE_FIELD_CLASS, value: typeof value === "string" ? value : "", onChange: (event) => {
|
|
1693
|
+
onChange(event.target.value || void 0);
|
|
1694
|
+
}, onBlur }),
|
|
1695
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(AssetPicker, { currentFilePath, sourceMode, triggerLabel: "Browse assets", onSelectPath: (selectedPath) => {
|
|
1696
|
+
onChange(selectedPath);
|
|
1697
|
+
onBlur();
|
|
1698
|
+
} })
|
|
1699
|
+
] });
|
|
1700
|
+
}
|
|
1701
|
+
function ImageArrayField({
|
|
1702
|
+
fieldKey,
|
|
1703
|
+
currentFilePath,
|
|
1704
|
+
sourceMode,
|
|
1705
|
+
values,
|
|
1706
|
+
chipInput,
|
|
1707
|
+
onChipInputChange,
|
|
1708
|
+
onAddChip,
|
|
1709
|
+
onRemoveChip,
|
|
1710
|
+
onBackspaceEmpty,
|
|
1711
|
+
onBlur,
|
|
1712
|
+
onSelectAsset
|
|
1713
|
+
}) {
|
|
1714
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1715
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(StringArrayField, { fieldKey, values, chipInput, onChipInputChange, onAddChip, onRemoveChip, onBackspaceEmpty, onBlur, placeholder: "Type path and press Enter" }),
|
|
1716
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(AssetPicker, { currentFilePath, sourceMode, triggerLabel: "Add from assets", onSelectPath: onSelectAsset })
|
|
1717
|
+
] });
|
|
1718
|
+
}
|
|
1719
|
+
function ColorField({
|
|
1720
|
+
fieldKey,
|
|
1721
|
+
value,
|
|
1722
|
+
onChange,
|
|
1723
|
+
onBlur
|
|
1724
|
+
}) {
|
|
1725
|
+
const stringValue = typeof value === "string" ? value : "";
|
|
1726
|
+
const normalizedValue = normalizeHexColor(stringValue);
|
|
1727
|
+
const hasValue = normalizedValue.length > 0;
|
|
1728
|
+
const hasValidValue = hasValue && isHexColor(normalizedValue);
|
|
1729
|
+
const pickerValue = hasValidValue ? normalizedValue : "#000000";
|
|
1730
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1731
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
|
|
1732
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Input, { id: fieldKey, className: SUBTLE_FIELD_CLASS, value: stringValue, onChange: (event) => {
|
|
1733
|
+
const nextRaw = event.target.value;
|
|
1734
|
+
onChange(nextRaw.length > 0 ? nextRaw : void 0);
|
|
1735
|
+
}, onBlur, placeholder: "#RRGGBB" }),
|
|
1736
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("input", { id: `${fieldKey}-picker`, type: "color", className: "border-border/50 h-9 w-12 cursor-pointer rounded-md border bg-transparent p-1", value: pickerValue, onChange: (event) => {
|
|
1737
|
+
onChange(normalizeHexColor(event.target.value));
|
|
1738
|
+
onBlur();
|
|
1739
|
+
}, "aria-label": `${fieldKey} color picker` })
|
|
1740
|
+
] }),
|
|
1741
|
+
hasValue && !hasValidValue ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-destructive/85 text-xs", children: "Use a hex color in #RRGGBB format." }) : null
|
|
1742
|
+
] });
|
|
1743
|
+
}
|
|
1744
|
+
function FieldErrorList({
|
|
1745
|
+
fieldKey,
|
|
1746
|
+
errors
|
|
1747
|
+
}) {
|
|
1748
|
+
if (!errors || errors.length === 0) {
|
|
1749
|
+
return null;
|
|
1750
|
+
}
|
|
1751
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1", children: errors.map((error, index) => /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-destructive/85 text-xs", children: error }, `${fieldKey}-error-${index}`)) });
|
|
1752
|
+
}
|
|
1753
|
+
function RightSidebar({
|
|
1754
|
+
selectedCollection,
|
|
1755
|
+
selectedFile
|
|
1756
|
+
}) {
|
|
1757
|
+
const draft = useFrontmatterEditorStore((state) => state.draft);
|
|
1758
|
+
const errors = useFrontmatterEditorStore((state) => state.errors);
|
|
1759
|
+
const chipInputs = useFrontmatterEditorStore((state) => state.chipInputs);
|
|
1760
|
+
const setFieldValue = useFrontmatterEditorStore((state) => state.setFieldValue);
|
|
1761
|
+
const setFieldTouched = useFrontmatterEditorStore((state) => state.setFieldTouched);
|
|
1762
|
+
const setValidationErrors = useFrontmatterEditorStore((state) => state.setValidationErrors);
|
|
1763
|
+
const setChipInput = useFrontmatterEditorStore((state) => state.setChipInput);
|
|
1764
|
+
const addChipValue = useFrontmatterEditorStore((state) => state.addChipValue);
|
|
1765
|
+
const removeChipValue = useFrontmatterEditorStore((state) => state.removeChipValue);
|
|
1766
|
+
const removeLastChipValue = useFrontmatterEditorStore((state) => state.removeLastChipValue);
|
|
1767
|
+
const objectSchema = reactExports.useMemo(() => {
|
|
1768
|
+
return resolveAstroObjectSchema(selectedCollection?.schema);
|
|
1769
|
+
}, [selectedCollection?.schema]);
|
|
1770
|
+
const resolvedFields = reactExports.useMemo(() => {
|
|
1771
|
+
if (!objectSchema) {
|
|
1772
|
+
return [];
|
|
1773
|
+
}
|
|
1774
|
+
return resolveSchemaFields(objectSchema, selectedCollection?.fieldUi);
|
|
1775
|
+
}, [objectSchema, selectedCollection?.fieldUi]);
|
|
1776
|
+
const runValidation = reactExports.useCallback(() => {
|
|
1777
|
+
if (!selectedCollection?.schema) {
|
|
1778
|
+
setValidationErrors({});
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1781
|
+
const result = validateFrontmatterDraft(selectedCollection.schema, draft);
|
|
1782
|
+
setValidationErrors(result.errors);
|
|
1783
|
+
}, [selectedCollection?.schema, draft, setValidationErrors]);
|
|
1784
|
+
const handleFieldBlur = reactExports.useCallback((fieldKey) => {
|
|
1785
|
+
setFieldTouched(fieldKey);
|
|
1786
|
+
runValidation();
|
|
1787
|
+
}, [runValidation, setFieldTouched]);
|
|
1788
|
+
reactExports.useEffect(() => {
|
|
1789
|
+
runValidation();
|
|
1790
|
+
}, [runValidation]);
|
|
1791
|
+
if (!selectedCollection || !selectedFile) {
|
|
1792
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground flex h-full items-center justify-center text-sm", children: "Select a file to edit frontmatter." });
|
|
1793
|
+
}
|
|
1794
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full flex-col gap-3", children: [
|
|
1795
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-foreground/90 text-sm font-semibold", children: "Frontmatter" }),
|
|
1796
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-h-0 flex-1 space-y-3 overflow-auto pr-1", children: [
|
|
1797
|
+
!objectSchema ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-muted/40 text-muted-foreground rounded-md px-3 py-2 text-sm", children: "This collection has no object schema available." }) : null,
|
|
1798
|
+
resolvedFields.map((field) => {
|
|
1799
|
+
const fieldErrors = errors[field.key];
|
|
1800
|
+
const label = toNormalCaseLabel(field.key);
|
|
1801
|
+
const value = draft[field.key];
|
|
1802
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-background/45 space-y-2 rounded-lg px-2.5 py-2.5", children: [
|
|
1803
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-between gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Label, { htmlFor: field.key, className: "text-muted-foreground text-xs font-medium tracking-wide", children: [
|
|
1804
|
+
label,
|
|
1805
|
+
field.required ? " *" : ""
|
|
1806
|
+
] }) }),
|
|
1807
|
+
field.kind === "string" && /* @__PURE__ */ jsxRuntimeExports.jsx(Input, { id: field.key, className: SUBTLE_FIELD_CLASS, value: typeof value === "string" ? value : "", onChange: (event) => {
|
|
1808
|
+
setFieldValue(field.key, event.target.value || void 0);
|
|
1809
|
+
}, onBlur: () => handleFieldBlur(field.key) }),
|
|
1810
|
+
field.kind === "image" && /* @__PURE__ */ jsxRuntimeExports.jsx(ImageField, { fieldKey: field.key, currentFilePath: selectedFile.filePath, sourceMode: field.sourceMode, value, onChange: (nextValue) => {
|
|
1811
|
+
setFieldValue(field.key, nextValue);
|
|
1812
|
+
}, onBlur: () => handleFieldBlur(field.key) }),
|
|
1813
|
+
field.kind === "color" && /* @__PURE__ */ jsxRuntimeExports.jsx(ColorField, { fieldKey: field.key, value, onChange: (nextValue) => {
|
|
1814
|
+
setFieldValue(field.key, nextValue);
|
|
1815
|
+
}, onBlur: () => handleFieldBlur(field.key) }),
|
|
1816
|
+
field.kind === "number" && /* @__PURE__ */ jsxRuntimeExports.jsx(NumberField, { fieldKey: field.key, value, onChange: (nextValue) => {
|
|
1817
|
+
setFieldValue(field.key, nextValue);
|
|
1818
|
+
}, onBlur: () => handleFieldBlur(field.key) }),
|
|
1819
|
+
field.kind === "boolean" && /* @__PURE__ */ jsxRuntimeExports.jsx(BooleanField, { fieldKey: field.key, value, onChange: (nextValue) => {
|
|
1820
|
+
setFieldValue(field.key, nextValue);
|
|
1821
|
+
}, onBlur: () => handleFieldBlur(field.key) }),
|
|
1822
|
+
field.kind === "enum" && /* @__PURE__ */ jsxRuntimeExports.jsx(EnumField, { fieldKey: field.key, value, options: field.options, onChange: (nextValue) => {
|
|
1823
|
+
setFieldValue(field.key, nextValue);
|
|
1824
|
+
handleFieldBlur(field.key);
|
|
1825
|
+
} }),
|
|
1826
|
+
field.kind === "stringArray" && /* @__PURE__ */ jsxRuntimeExports.jsx(StringArrayField, { fieldKey: field.key, values: getStringArrayValue(value), chipInput: chipInputs[field.key] ?? "", onChipInputChange: (nextValue) => {
|
|
1827
|
+
setChipInput(field.key, nextValue);
|
|
1828
|
+
}, onAddChip: () => {
|
|
1829
|
+
addChipValue(field.key, chipInputs[field.key] ?? "");
|
|
1830
|
+
handleFieldBlur(field.key);
|
|
1831
|
+
}, onRemoveChip: (index) => {
|
|
1832
|
+
removeChipValue(field.key, index);
|
|
1833
|
+
handleFieldBlur(field.key);
|
|
1834
|
+
}, onBackspaceEmpty: () => {
|
|
1835
|
+
removeLastChipValue(field.key);
|
|
1836
|
+
handleFieldBlur(field.key);
|
|
1837
|
+
}, onBlur: () => handleFieldBlur(field.key) }),
|
|
1838
|
+
field.kind === "imageArray" && /* @__PURE__ */ jsxRuntimeExports.jsx(ImageArrayField, { fieldKey: field.key, currentFilePath: selectedFile.filePath, sourceMode: field.sourceMode, values: getStringArrayValue(value), chipInput: chipInputs[field.key] ?? "", onChipInputChange: (nextValue) => {
|
|
1839
|
+
setChipInput(field.key, nextValue);
|
|
1840
|
+
}, onAddChip: () => {
|
|
1841
|
+
addChipValue(field.key, chipInputs[field.key] ?? "");
|
|
1842
|
+
handleFieldBlur(field.key);
|
|
1843
|
+
}, onRemoveChip: (index) => {
|
|
1844
|
+
removeChipValue(field.key, index);
|
|
1845
|
+
handleFieldBlur(field.key);
|
|
1846
|
+
}, onBackspaceEmpty: () => {
|
|
1847
|
+
removeLastChipValue(field.key);
|
|
1848
|
+
handleFieldBlur(field.key);
|
|
1849
|
+
}, onBlur: () => handleFieldBlur(field.key), onSelectAsset: (nextValue) => {
|
|
1850
|
+
addChipValue(field.key, nextValue);
|
|
1851
|
+
handleFieldBlur(field.key);
|
|
1852
|
+
} }),
|
|
1853
|
+
field.kind === "dateAnyOf" && /* @__PURE__ */ jsxRuntimeExports.jsx(DateAnyOfField, { fieldKey: field.key, value, onChange: (nextValue) => {
|
|
1854
|
+
setFieldValue(field.key, nextValue);
|
|
1855
|
+
}, onBlur: () => handleFieldBlur(field.key) }),
|
|
1856
|
+
field.kind === "unsupported" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-muted/40 text-muted-foreground rounded-md px-3 py-2 text-sm", children: [
|
|
1857
|
+
"Not yet supported: ",
|
|
1858
|
+
field.reason
|
|
1859
|
+
] }),
|
|
1860
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(FieldErrorList, { fieldKey: field.key, errors: fieldErrors })
|
|
1861
|
+
] }, field.key);
|
|
1862
|
+
}),
|
|
1863
|
+
errors[ROOT_VALIDATION_KEY]?.length ? /* @__PURE__ */ jsxRuntimeExports.jsx(FieldErrorList, { fieldKey: ROOT_VALIDATION_KEY, errors: errors[ROOT_VALIDATION_KEY] }) : null
|
|
1864
|
+
] })
|
|
1865
|
+
] });
|
|
1866
|
+
}
|
|
1867
|
+
function isObjectRecord(value) {
|
|
1868
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1869
|
+
}
|
|
1870
|
+
const saveSelectionToFile = createServerFn({
|
|
1871
|
+
method: "POST"
|
|
1872
|
+
}).inputValidator((payload) => {
|
|
1873
|
+
if (!isObjectRecord(payload)) {
|
|
1874
|
+
throw new Error("Invalid save payload.");
|
|
1875
|
+
}
|
|
1876
|
+
const collectionName = payload.collectionName;
|
|
1877
|
+
const fileId = payload.fileId;
|
|
1878
|
+
const draft = payload.draft;
|
|
1879
|
+
const content = payload.content;
|
|
1880
|
+
if (typeof collectionName !== "string" || collectionName.length === 0) {
|
|
1881
|
+
throw new Error("Missing collection name.");
|
|
1882
|
+
}
|
|
1883
|
+
if (typeof fileId !== "string" || fileId.length === 0) {
|
|
1884
|
+
throw new Error("Missing file id.");
|
|
1885
|
+
}
|
|
1886
|
+
if (!isObjectRecord(draft)) {
|
|
1887
|
+
throw new Error("Invalid frontmatter draft.");
|
|
1888
|
+
}
|
|
1889
|
+
if (typeof content !== "string") {
|
|
1890
|
+
throw new Error("Invalid markdown content.");
|
|
1891
|
+
}
|
|
1892
|
+
return {
|
|
1893
|
+
collectionName,
|
|
1894
|
+
fileId,
|
|
1895
|
+
draft,
|
|
1896
|
+
content
|
|
1897
|
+
};
|
|
1898
|
+
}).handler(createSsrRpc("b1ee42a5714ab9be8f37a4c5fca02ffd6269030b4d3ef663ff002865393e2386"));
|
|
1899
|
+
function App() {
|
|
1900
|
+
const data = useCollectionsData();
|
|
1901
|
+
const search = Route.useSearch();
|
|
1902
|
+
const [leftOpen, setLeftOpen] = reactExports.useState(true);
|
|
1903
|
+
const [rightOpen, setRightOpen] = reactExports.useState(true);
|
|
1904
|
+
const [isSaving, setIsSaving] = reactExports.useState(false);
|
|
1905
|
+
const [saveError, setSaveError] = reactExports.useState();
|
|
1906
|
+
const leftPanelRef = reactExports.useRef(null);
|
|
1907
|
+
const rightPanelRef = reactExports.useRef(null);
|
|
1908
|
+
const navigate = Route.useNavigate();
|
|
1909
|
+
const draft = useFrontmatterEditorStore((state) => state.draft);
|
|
1910
|
+
const contentDraft = useFrontmatterEditorStore((state) => state.contentDraft);
|
|
1911
|
+
const dirty = useFrontmatterEditorStore((state) => state.dirty);
|
|
1912
|
+
const loadSelection = useFrontmatterEditorStore((state) => state.loadSelection);
|
|
1913
|
+
const clearSelection = useFrontmatterEditorStore((state) => state.clearSelection);
|
|
1914
|
+
const commitSavedState = useFrontmatterEditorStore((state) => state.commitSavedState);
|
|
1915
|
+
const {
|
|
1916
|
+
selectedCollection,
|
|
1917
|
+
selectedFile,
|
|
1918
|
+
normalizedSearch
|
|
1919
|
+
} = reactExports.useMemo(() => resolveEditorSelection(data, search), [data, search]);
|
|
1920
|
+
reactExports.useEffect(() => {
|
|
1921
|
+
if (areEditorSearchEqual(search, normalizedSearch)) {
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
void navigate({
|
|
1925
|
+
search: normalizedSearch,
|
|
1926
|
+
replace: true
|
|
1927
|
+
});
|
|
1928
|
+
}, [navigate, normalizedSearch, search]);
|
|
1929
|
+
reactExports.useEffect(() => {
|
|
1930
|
+
if (!selectedCollection || !selectedFile) {
|
|
1931
|
+
clearSelection();
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
loadSelection({
|
|
1935
|
+
collectionName: selectedCollection.name,
|
|
1936
|
+
fileId: selectedFile.id,
|
|
1937
|
+
data: selectedFile.data,
|
|
1938
|
+
content: selectedFile.content
|
|
1939
|
+
});
|
|
1940
|
+
}, [clearSelection, loadSelection, selectedCollection, selectedFile]);
|
|
1941
|
+
reactExports.useEffect(() => {
|
|
1942
|
+
if (!dirty) {
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
const handleBeforeUnload = (event) => {
|
|
1946
|
+
event.preventDefault();
|
|
1947
|
+
event.returnValue = "";
|
|
1948
|
+
};
|
|
1949
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
1950
|
+
return () => {
|
|
1951
|
+
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
1952
|
+
};
|
|
1953
|
+
}, [dirty]);
|
|
1954
|
+
const canNavigateAway = reactExports.useCallback(() => {
|
|
1955
|
+
if (!dirty) {
|
|
1956
|
+
return true;
|
|
1957
|
+
}
|
|
1958
|
+
return window.confirm("You have unsaved changes. Discard changes and continue?");
|
|
1959
|
+
}, [dirty]);
|
|
1960
|
+
const selectCollection = reactExports.useCallback((collectionName) => {
|
|
1961
|
+
const nextCollection = data.find((collection) => collection.name === collectionName);
|
|
1962
|
+
if (!nextCollection) {
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1965
|
+
const nextFile = getSortedCollectionFiles(nextCollection).at(0);
|
|
1966
|
+
const nextSearch = {
|
|
1967
|
+
collection: nextCollection.name,
|
|
1968
|
+
file: nextFile?.id
|
|
1969
|
+
};
|
|
1970
|
+
if (areEditorSearchEqual(normalizedSearch, nextSearch)) {
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
if (!canNavigateAway()) {
|
|
1974
|
+
return;
|
|
1975
|
+
}
|
|
1976
|
+
void navigate({
|
|
1977
|
+
search: nextSearch
|
|
1978
|
+
});
|
|
1979
|
+
}, [canNavigateAway, data, navigate, normalizedSearch]);
|
|
1980
|
+
const selectFile = reactExports.useCallback((fileId) => {
|
|
1981
|
+
if (!selectedCollection) {
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
const nextSearch = {
|
|
1985
|
+
collection: selectedCollection.name,
|
|
1986
|
+
file: fileId
|
|
1987
|
+
};
|
|
1988
|
+
if (areEditorSearchEqual(normalizedSearch, nextSearch)) {
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1991
|
+
if (!canNavigateAway()) {
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
void navigate({
|
|
1995
|
+
search: nextSearch
|
|
1996
|
+
});
|
|
1997
|
+
}, [canNavigateAway, navigate, normalizedSearch, selectedCollection]);
|
|
1998
|
+
const toggleLeftSidebar = reactExports.useCallback(() => {
|
|
1999
|
+
const panel = leftPanelRef.current;
|
|
2000
|
+
if (!panel) {
|
|
2001
|
+
setLeftOpen((open) => !open);
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
2004
|
+
if (panel.isCollapsed()) {
|
|
2005
|
+
panel.expand();
|
|
2006
|
+
setLeftOpen(true);
|
|
2007
|
+
return;
|
|
2008
|
+
}
|
|
2009
|
+
panel.collapse();
|
|
2010
|
+
setLeftOpen(false);
|
|
2011
|
+
}, []);
|
|
2012
|
+
const toggleRightSidebar = reactExports.useCallback(() => {
|
|
2013
|
+
const panel = rightPanelRef.current;
|
|
2014
|
+
if (!panel) {
|
|
2015
|
+
setRightOpen((open) => !open);
|
|
2016
|
+
return;
|
|
2017
|
+
}
|
|
2018
|
+
if (panel.isCollapsed()) {
|
|
2019
|
+
panel.expand();
|
|
2020
|
+
setRightOpen(true);
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
2023
|
+
panel.collapse();
|
|
2024
|
+
setRightOpen(false);
|
|
2025
|
+
}, []);
|
|
2026
|
+
const syncSidebarState = reactExports.useCallback(() => {
|
|
2027
|
+
const nextLeftOpen = !(leftPanelRef.current?.isCollapsed() ?? false);
|
|
2028
|
+
const nextRightOpen = !(rightPanelRef.current?.isCollapsed() ?? false);
|
|
2029
|
+
setLeftOpen((current) => current === nextLeftOpen ? current : nextLeftOpen);
|
|
2030
|
+
setRightOpen((current) => current === nextRightOpen ? current : nextRightOpen);
|
|
2031
|
+
}, []);
|
|
2032
|
+
const handleSave = reactExports.useCallback(async () => {
|
|
2033
|
+
if (!selectedCollection || !selectedFile || !dirty || isSaving) {
|
|
2034
|
+
return;
|
|
2035
|
+
}
|
|
2036
|
+
setIsSaving(true);
|
|
2037
|
+
setSaveError(void 0);
|
|
2038
|
+
try {
|
|
2039
|
+
await saveSelectionToFile({
|
|
2040
|
+
data: {
|
|
2041
|
+
collectionName: selectedCollection.name,
|
|
2042
|
+
fileId: selectedFile.id,
|
|
2043
|
+
draft,
|
|
2044
|
+
content: contentDraft
|
|
2045
|
+
}
|
|
2046
|
+
});
|
|
2047
|
+
commitSavedState();
|
|
2048
|
+
} catch (error) {
|
|
2049
|
+
const message = error instanceof Error ? error.message : "Unable to save file.";
|
|
2050
|
+
setSaveError(message);
|
|
2051
|
+
} finally {
|
|
2052
|
+
setIsSaving(false);
|
|
2053
|
+
}
|
|
2054
|
+
}, [commitSavedState, contentDraft, dirty, draft, isSaving, selectedCollection, selectedFile]);
|
|
2055
|
+
reactExports.useEffect(() => {
|
|
2056
|
+
setSaveError(void 0);
|
|
2057
|
+
}, [selectedCollection?.name, selectedFile?.id]);
|
|
2058
|
+
const selectedFileLabel = selectedFile ? getFileDisplayLabel(selectedFile) : "No file selected";
|
|
2059
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-svh w-screen overflow-hidden p-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(ResizablePanelGroup, { orientation: "horizontal", onLayoutChanged: syncSidebarState, children: [
|
|
2060
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ResizablePanel, { id: "editor-left-sidebar", panelRef: leftPanelRef, collapsible: true, collapsedSize: 0, defaultSize: "18rem", minSize: "14rem", maxSize: "30rem", className: "min-w-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx("aside", { className: "bg-background/55 flex h-full flex-col overflow-hidden rounded-xl px-2.5 py-3 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LeftSidebar, { collections: data, selectedCollectionName: selectedCollection?.name, selectedFileId: selectedFile?.id, onSelectCollection: selectCollection, onSelectFile: selectFile }) }) }),
|
|
2061
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ResizableHandle, { withHandle: true, className: cn(leftOpen ? "mx-1 w-2 bg-transparent" : "mx-0 w-0", !leftOpen && "pointer-events-none opacity-0") }),
|
|
2062
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ResizablePanel, { id: "editor-main-panel", minSize: "30%", className: "min-w-0", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { className: "bg-background/75 flex h-full w-full min-w-0 flex-1 flex-col overflow-hidden rounded-xl px-3 py-2 backdrop-blur-sm", children: [
|
|
2063
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("header", { className: "border-border/45 flex items-center justify-between gap-3 border-b pb-2", children: [
|
|
2064
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex shrink-0 items-center gap-1.5", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "ghost", size: "icon", onClick: toggleLeftSidebar, "aria-label": leftOpen ? "Hide left sidebar" : "Show left sidebar", className: "text-muted-foreground hover:bg-background/65 hover:text-foreground size-8 border-0", children: leftOpen ? /* @__PURE__ */ jsxRuntimeExports.jsx(PanelLeftClose, {}) : /* @__PURE__ */ jsxRuntimeExports.jsx(PanelLeftOpen, {}) }) }),
|
|
2065
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-foreground/85 min-w-0 flex-1 truncate text-center text-sm font-medium", title: selectedFile?.id, children: selectedFileLabel }),
|
|
2066
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex shrink-0 items-center justify-end gap-2", children: [
|
|
2067
|
+
dirty ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleSave, disabled: isSaving || !selectedCollection || !selectedFile, className: "border-0", children: [
|
|
2068
|
+
isSaving ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Save, {}),
|
|
2069
|
+
"Save"
|
|
2070
|
+
] }) : null,
|
|
2071
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "ghost", size: "icon", onClick: toggleRightSidebar, "aria-label": rightOpen ? "Hide right sidebar" : "Show right sidebar", className: "text-muted-foreground hover:bg-background/65 hover:text-foreground size-8 border-0", children: rightOpen ? /* @__PURE__ */ jsxRuntimeExports.jsx(PanelRightClose, {}) : /* @__PURE__ */ jsxRuntimeExports.jsx(PanelRightOpen, {}) })
|
|
2072
|
+
] })
|
|
2073
|
+
] }),
|
|
2074
|
+
saveError ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-destructive/90 pt-2 text-xs", children: saveError }) : null,
|
|
2075
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex min-h-0 flex-1 overflow-hidden pt-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Editor, {}) })
|
|
2076
|
+
] }) }),
|
|
2077
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ResizableHandle, { withHandle: true, className: cn(rightOpen ? "mx-1 w-2 bg-transparent" : "mx-0 w-0", !rightOpen && "pointer-events-none opacity-0") }),
|
|
2078
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ResizablePanel, { id: "editor-right-sidebar", panelRef: rightPanelRef, collapsible: true, collapsedSize: 0, defaultSize: "18rem", minSize: "14rem", maxSize: "30rem", className: "min-w-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx("aside", { className: "bg-background/55 flex h-full flex-col overflow-hidden rounded-xl px-2.5 py-3 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsx(RightSidebar, { selectedCollection, selectedFile }) }) })
|
|
2079
|
+
] }) });
|
|
2080
|
+
}
|
|
2081
|
+
export {
|
|
2082
|
+
App as component
|
|
2083
|
+
};
|