@ohhwells/bridge 0.1.29-next.20 → 0.1.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,752 +1,10 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React5, { useCallback as useCallback2, useEffect as useEffect3, useLayoutEffect, useRef as useRef3, useState as useState5 } from "react";
5
- import { createRoot } from "react-dom/client";
6
- import { flushSync } from "react-dom";
7
-
8
- // src/ui/SchedulingWidget.tsx
9
- import { useEffect, useMemo, useRef, useState as useState2 } from "react";
10
-
11
- // src/ui/EmailCaptureModal.tsx
12
- import { useState } from "react";
13
- import { Dialog } from "radix-ui";
14
- import { jsx, jsxs } from "react/jsx-runtime";
15
- function Spinner() {
16
- return /* @__PURE__ */ jsxs(
17
- "svg",
18
- {
19
- width: "32",
20
- height: "32",
21
- viewBox: "0 0 32 32",
22
- fill: "none",
23
- style: { animation: "ohw-spin 0.75s linear infinite" },
24
- children: [
25
- /* @__PURE__ */ jsx("style", { children: `@keyframes ohw-spin { to { transform: rotate(360deg); } }` }),
26
- /* @__PURE__ */ jsx("circle", { cx: "16", cy: "16", r: "12", stroke: "#e5e7eb", strokeWidth: "3" }),
27
- /* @__PURE__ */ jsx(
28
- "path",
29
- {
30
- d: "M16 4a12 12 0 0 1 12 12",
31
- stroke: "#3b82f6",
32
- strokeWidth: "3",
33
- strokeLinecap: "round"
34
- }
35
- )
36
- ]
37
- }
38
- );
39
- }
40
- function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
41
- const [email, setEmail] = useState("");
42
- const [submittedEmail, setSubmittedEmail] = useState("");
43
- const [loading, setLoading] = useState(false);
44
- const [success, setSuccess] = useState(false);
45
- const [error, setError] = useState(null);
46
- const isBook = title === "Confirm your spot";
47
- const handleSubmit = async () => {
48
- if (!email.trim()) return;
49
- setLoading(true);
50
- setError(null);
51
- try {
52
- await onSubmit(email.trim());
53
- setSubmittedEmail(email.trim());
54
- setSuccess(true);
55
- } catch (e) {
56
- setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
57
- } finally {
58
- setLoading(false);
59
- }
60
- };
61
- const handleKeyDown = (e) => {
62
- if (e.key === "Enter" && !loading) handleSubmit();
63
- };
64
- return /* @__PURE__ */ jsx(Dialog.Root, { open: true, onOpenChange: (open) => {
65
- if (!open) onClose();
66
- }, children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
67
- /* @__PURE__ */ jsx(
68
- Dialog.Overlay,
69
- {
70
- className: "fixed inset-0 z-50",
71
- style: { background: "rgba(0,0,0,0.45)" }
72
- }
73
- ),
74
- /* @__PURE__ */ jsxs(
75
- Dialog.Content,
76
- {
77
- className: "fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl shadow-xl outline-none font-body box-border overflow-hidden",
78
- style: { maxWidth: 400 },
79
- children: [
80
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 p-6", children: [
81
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
82
- /* @__PURE__ */ jsx(
83
- Dialog.Title,
84
- {
85
- className: "font-body text-xl font-bold m-0 leading-tight",
86
- style: { color: "var(--color-dark,#200C02)" },
87
- children: success ? isBook ? "Reservation confirmed!" : "You're on the waitlist!" : title
88
- }
89
- ),
90
- /* @__PURE__ */ jsx(
91
- Dialog.Description,
92
- {
93
- className: "font-body text-sm m-0",
94
- style: { color: "#6B7280" },
95
- children: success ? isBook ? `A confirmation email has been sent to ${submittedEmail}.` : "We'll let you know via email if someone cancels." : subtitle
96
- }
97
- )
98
- ] }),
99
- /* @__PURE__ */ jsx(
100
- Dialog.Close,
101
- {
102
- className: "shrink-0 w-7 h-7 flex items-center justify-center rounded-full bg-transparent border-none cursor-pointer mt-0.5 absolute top-2.5 right-1.5",
103
- style: { color: "#6B7280" },
104
- "aria-label": "Close",
105
- children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
106
- /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
107
- /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
108
- ] })
109
- }
110
- )
111
- ] }),
112
- !success && (loading ? (
113
- /* Loading state — full body replaced */
114
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center gap-3 px-7 py-12", children: [
115
- /* @__PURE__ */ jsx(Spinner, {}),
116
- /* @__PURE__ */ jsx("p", { className: "font-body text-sm m-0", style: { color: "#6B7280" }, children: "Please wait" })
117
- ] })
118
- ) : (
119
- /* Email form */
120
- /* @__PURE__ */ jsxs("div", { className: "px-7 pt-2 pb-6", children: [
121
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 mb-8", children: [
122
- /* @__PURE__ */ jsx("label", { className: "font-body text-sm font-medium", style: { color: "var(--color-dark,#200C02)" }, children: "Email" }),
123
- /* @__PURE__ */ jsx(
124
- "input",
125
- {
126
- type: "email",
127
- value: email,
128
- onChange: (e) => setEmail(e.target.value),
129
- onKeyDown: handleKeyDown,
130
- placeholder: "hello@gmail.com",
131
- autoFocus: true,
132
- className: "w-full h-10 px-3 rounded-lg border font-body text-sm outline-none box-border",
133
- style: {
134
- borderColor: "#E7E5E4",
135
- background: "#fff",
136
- color: "var(--color-dark,#200C02)"
137
- }
138
- }
139
- ),
140
- error && /* @__PURE__ */ jsx("p", { className: "font-body text-xs text-red-500 m-0", children: error })
141
- ] }),
142
- /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
143
- "button",
144
- {
145
- onClick: handleSubmit,
146
- disabled: !email.trim(),
147
- className: "h-9 px-5 rounded-lg font-body text-sm font-semibold cursor-pointer border-none disabled:opacity-50 disabled:cursor-not-allowed",
148
- style: {
149
- background: "var(--color-primary,#3D312B)",
150
- color: "var(--color-light,#FEFFF0)"
151
- },
152
- children: "Send"
153
- }
154
- ) })
155
- ] })
156
- ))
157
- ]
158
- }
159
- )
160
- ] }) });
161
- }
162
-
163
- // src/ui/SchedulingWidget.tsx
164
- import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
165
- var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
166
- var DAY_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
167
- function getApiDomain() {
168
- const h = window.location.hostname;
169
- for (const base of ["ohhwells.site", "theflowops-staging.com", "theflowops.com"]) {
170
- if (h.endsWith(`.${base}`)) return h.slice(0, -(base.length + 1));
171
- }
172
- return h;
173
- }
174
- function classRunsOnDate(cls, date, type) {
175
- if (type === "FIXED") {
176
- const d = String(date.getDate()).padStart(2, "0");
177
- const m = String(date.getMonth() + 1).padStart(2, "0");
178
- return cls.fixedDates?.some((fd) => fd.date === `${d}-${m}-${date.getFullYear()}`) ?? false;
179
- }
180
- return cls.weekdays?.some((w) => w.weekday === date.getDay()) ?? false;
181
- }
182
- function formatClassTime(cls) {
183
- let h = parseInt(cls.startTime.hour, 10);
184
- const m = parseInt(cls.startTime.minutes, 10);
185
- if (cls.startTime.time === "PM" && h !== 12) h += 12;
186
- if (cls.startTime.time === "AM" && h === 12) h = 0;
187
- const endTotal = h * 60 + m + cls.duration;
188
- const eh = Math.floor(endTotal / 60) % 24;
189
- const em = endTotal % 60;
190
- return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
191
- }
192
- function getBookingsOnDate(cls, date) {
193
- if (!cls.bookings?.length) return 0;
194
- const ds = date.toISOString().split("T")[0];
195
- return cls.bookings.filter((b) => {
196
- try {
197
- return new Date(b.classDate).toISOString().split("T")[0] === ds;
198
- } catch {
199
- return false;
200
- }
201
- }).length;
202
- }
203
- function buildTimezoneLabel(tz) {
204
- try {
205
- const now = /* @__PURE__ */ new Date();
206
- const timeStr = new Intl.DateTimeFormat("en-US", {
207
- hour: "2-digit",
208
- minute: "2-digit",
209
- hour12: false,
210
- timeZone: tz
211
- }).format(now);
212
- const offsetPart = new Intl.DateTimeFormat("en-US", { timeZoneName: "short", timeZone: tz }).formatToParts(now).find((p) => p.type === "timeZoneName")?.value ?? "";
213
- const city = tz.split("/").pop()?.replace(/_/g, " ") ?? tz;
214
- return `${timeStr} (${offsetPart}) ${city} time`;
215
- } catch {
216
- return tz;
217
- }
218
- }
219
- function EmptyState({ inEditor }) {
220
- const handleAddSchedule = () => {
221
- if (inEditor) {
222
- window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
223
- }
224
- };
225
- return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center gap-6 p-12 bg-[#FAFAF9] border-2 border-dashed border-[#E7E5E4] rounded-[10px] h-[264px] w-full box-border", children: [
226
- /* @__PURE__ */ jsx2("div", { className: "w-10 h-10 bg-[#F5F5F4] rounded-[10px] flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxs2(
227
- "svg",
228
- {
229
- width: "20",
230
- height: "20",
231
- viewBox: "0 0 24 24",
232
- fill: "none",
233
- stroke: "var(--color-accent, #A89B83)",
234
- strokeWidth: "1.5",
235
- strokeLinecap: "round",
236
- strokeLinejoin: "round",
237
- children: [
238
- /* @__PURE__ */ jsx2("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
239
- /* @__PURE__ */ jsx2("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
240
- /* @__PURE__ */ jsx2("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
241
- /* @__PURE__ */ jsx2("line", { x1: "3", y1: "10", x2: "21", y2: "10" }),
242
- /* @__PURE__ */ jsx2("line", { x1: "12", y1: "14", x2: "12", y2: "18" }),
243
- /* @__PURE__ */ jsx2("line", { x1: "10", y1: "16", x2: "14", y2: "16" })
244
- ]
245
- }
246
- ) }),
247
- /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-2", children: [
248
- /* @__PURE__ */ jsx2("p", { className: "font-body text-lg font-medium text-center text-[#0A0A0A] m-0", children: "No schedule yet" }),
249
- /* @__PURE__ */ jsx2("p", { className: "font-body text-base font-normal text-center text-(--color-accent,#A89B83) m-0", children: "Add a scheduling widget to get instant bookings." })
250
- ] }),
251
- /* @__PURE__ */ jsx2(
252
- "button",
253
- {
254
- onClick: handleAddSchedule,
255
- className: "h-9 px-2 py-1.5 flex items-center justify-center bg-white border border-[#E7E5E4] rounded-md font-body text-sm font-medium text-(--color-dark,#200C02) cursor-pointer",
256
- children: "Add Schedule"
257
- }
258
- )
259
- ] });
260
- }
261
- function LoadingSkeleton() {
262
- const shimmer = {
263
- background: "linear-gradient(90deg,#f0f0f0 25%,#e8e8e8 50%,#f0f0f0 75%)",
264
- backgroundSize: "200% 100%",
265
- animation: "ohw-shimmer 1.5s infinite"
266
- };
267
- return /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col gap-10", children: [
268
- /* @__PURE__ */ jsx2("style", { children: `@keyframes ohw-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}` }),
269
- /* @__PURE__ */ jsx2("div", { className: "flex h-[70px] items-stretch", children: Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col justify-between items-center px-1", children: [
270
- /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "32px", height: "14px", borderRadius: "4px" } }),
271
- /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "40px", height: "40px", borderRadius: "50%" } })
272
- ] }, i)) }),
273
- [0, 1, 2].map((i) => /* @__PURE__ */ jsxs2("div", { children: [
274
- /* @__PURE__ */ jsxs2("div", { className: "flex gap-4 py-4 sm:hidden", children: [
275
- /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 shrink-0", children: [
276
- /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "100px", height: "16px", borderRadius: "4px" } }),
277
- /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "52px", height: "20px", borderRadius: "999px" } }),
278
- /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "60px", height: "14px", borderRadius: "4px" } })
279
- ] }),
280
- /* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-2", children: [
281
- /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "75%", height: "16px", borderRadius: "4px" } }),
282
- /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "50%", height: "14px", borderRadius: "4px" } }),
283
- /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "100%", height: "44px", borderRadius: "8px" } })
284
- ] })
285
- ] }),
286
- /* @__PURE__ */ jsxs2("div", { className: "hidden sm:flex items-center gap-[60px] py-4", children: [
287
- /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "120px", height: "16px", borderRadius: "4px", flexShrink: 0 } }),
288
- /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-2", children: [
289
- /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "55%", height: "16px", borderRadius: "4px" } }),
290
- /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "38%", height: "14px", borderRadius: "4px" } })
291
- ] }),
292
- /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "56px", height: "32px", borderRadius: "4px", flexShrink: 0 } }),
293
- /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "200px", height: "44px", borderRadius: "8px", flexShrink: 0 } })
294
- ] }),
295
- i < 2 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
296
- ] }, i))
297
- ] });
298
- }
299
- function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
300
- const todayMs = useMemo(() => {
301
- const d = /* @__PURE__ */ new Date();
302
- d.setHours(0, 0, 0, 0);
303
- return d.getTime();
304
- }, []);
305
- const scrollRef = useRef(null);
306
- const [canScrollLeft, setCanScrollLeft] = useState2(false);
307
- const [canScrollRight, setCanScrollRight] = useState2(false);
308
- const updateScrollState = () => {
309
- const el = scrollRef.current;
310
- if (!el) return;
311
- setCanScrollLeft(el.scrollLeft > 0);
312
- setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
313
- };
314
- useEffect(() => {
315
- const el = scrollRef.current;
316
- if (!el) return;
317
- updateScrollState();
318
- el.addEventListener("scroll", updateScrollState);
319
- const ro = new ResizeObserver(updateScrollState);
320
- ro.observe(el);
321
- return () => {
322
- el.removeEventListener("scroll", updateScrollState);
323
- ro.disconnect();
324
- };
325
- }, [dates]);
326
- const scroll = (dir) => {
327
- scrollRef.current?.scrollBy({ left: dir === "left" ? -216 : 216, behavior: "smooth" });
328
- };
329
- const isDragging = useRef(false);
330
- const dragStartX = useRef(0);
331
- const dragStartScrollLeft = useRef(0);
332
- const onDragStart = (e) => {
333
- if (!scrollRef.current) return;
334
- isDragging.current = true;
335
- dragStartX.current = e.clientX;
336
- dragStartScrollLeft.current = scrollRef.current.scrollLeft;
337
- scrollRef.current.style.cursor = "grabbing";
338
- scrollRef.current.style.userSelect = "none";
339
- };
340
- const onDragMove = (e) => {
341
- if (!isDragging.current || !scrollRef.current) return;
342
- const delta = e.clientX - dragStartX.current;
343
- scrollRef.current.scrollLeft = dragStartScrollLeft.current - delta;
344
- };
345
- const onDragEnd = () => {
346
- if (!scrollRef.current) return;
347
- isDragging.current = false;
348
- scrollRef.current.style.cursor = "grab";
349
- scrollRef.current.style.userSelect = "";
350
- };
351
- const datesWithClasses = useMemo(
352
- () => new Set(dates.map(
353
- (d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
354
- ).filter((i) => i >= 0)),
355
- [dates, schedule]
356
- );
357
- const selectedDate = dates[selectedIdx];
358
- const selectedClasses = useMemo(
359
- () => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
360
- [schedule, selectedDate]
361
- );
362
- return /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col gap-10", children: [
363
- /* @__PURE__ */ jsxs2("div", { className: "relative w-full", children: [
364
- canScrollLeft && /* @__PURE__ */ jsx2(
365
- "button",
366
- {
367
- onClick: () => scroll("left"),
368
- className: "hidden sm:flex absolute left-0 top-1/2 -translate-y-1/2 z-10 w-8 h-8 items-center justify-center rounded-full border-none cursor-pointer",
369
- style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
370
- "aria-label": "Scroll left",
371
- children: /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "var(--color-dark,#200C02)", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx2("polyline", { points: "15 18 9 12 15 6" }) })
372
- }
373
- ),
374
- canScrollRight && /* @__PURE__ */ jsx2(
375
- "button",
376
- {
377
- onClick: () => scroll("right"),
378
- className: "hidden sm:flex absolute right-0 top-1/2 -translate-y-1/2 z-10 w-8 h-8 items-center justify-center rounded-full border-none cursor-pointer",
379
- style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
380
- "aria-label": "Scroll right",
381
- children: /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "var(--color-dark,#200C02)", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx2("polyline", { points: "9 18 15 12 9 6" }) })
382
- }
383
- ),
384
- canScrollLeft && /* @__PURE__ */ jsx2(
385
- "div",
386
- {
387
- className: "sm:hidden absolute left-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
388
- style: { background: "linear-gradient(to right, var(--color-light,#FEFFF0), transparent)" }
389
- }
390
- ),
391
- canScrollRight && /* @__PURE__ */ jsx2(
392
- "div",
393
- {
394
- className: "sm:hidden absolute right-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
395
- style: { background: "linear-gradient(to left, var(--color-light,#FEFFF0), transparent)" }
396
- }
397
- ),
398
- /* @__PURE__ */ jsx2(
399
- "div",
400
- {
401
- ref: scrollRef,
402
- className: "overflow-x-auto w-full",
403
- style: { scrollbarWidth: "none", msOverflowStyle: "none", touchAction: "pan-x", cursor: "grab" },
404
- onMouseDown: onDragStart,
405
- onMouseMove: onDragMove,
406
- onMouseUp: onDragEnd,
407
- onMouseLeave: onDragEnd,
408
- children: /* @__PURE__ */ jsx2("div", { className: "flex", style: { width: `max(100%, ${dates.length * 60}px)` }, children: dates.map((date, i) => {
409
- const isToday = date.getTime() === todayMs;
410
- const isSelected = i === selectedIdx;
411
- const clickable = datesWithClasses.has(i);
412
- const label = isToday ? "TODAY" : DAY_ABBR[date.getDay()];
413
- return /* @__PURE__ */ jsxs2(
414
- "div",
415
- {
416
- onClick: () => clickable && onSelectDate(i),
417
- className: `flex-1 h-[70px] flex flex-col justify-between items-center ${clickable ? "cursor-pointer opacity-100" : "cursor-default opacity-50"}`,
418
- children: [
419
- /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-center text-(--color-dark,#200C02)", children: label }),
420
- /* @__PURE__ */ jsx2(
421
- "div",
422
- {
423
- className: "w-10 h-10 rounded-full flex items-center justify-center",
424
- style: { background: isSelected ? "var(--color-primary, #3D312B)" : "transparent" },
425
- children: /* @__PURE__ */ jsx2(
426
- "span",
427
- {
428
- className: "font-body text-xl font-bold text-center tracking-display-tight",
429
- style: { color: isSelected ? "var(--color-light, #FEFFF0)" : "var(--color-dark, #200C02)" },
430
- children: date.getDate()
431
- }
432
- )
433
- }
434
- )
435
- ]
436
- },
437
- i
438
- );
439
- }) })
440
- }
441
- )
442
- ] }),
443
- selectedClasses.length === 0 ? /* @__PURE__ */ jsx2("p", { className: "font-body text-base text-(--color-accent,#A89B83) text-center py-8 m-0", children: "No classes scheduled for this day." }) : /* @__PURE__ */ jsx2("div", { className: "flex flex-col", children: selectedClasses.map((cls, i) => {
444
- const booked = getBookingsOnDate(cls, selectedDate);
445
- const available = cls.maxParticipants - booked;
446
- const isFull = available <= 0;
447
- return /* @__PURE__ */ jsxs2("div", { children: [
448
- /* @__PURE__ */ jsxs2("div", { className: "flex gap-4 py-4 sm:items-center sm:gap-[60px] box-border", children: [
449
- /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 shrink-0 sm:contents", children: [
450
- /* @__PURE__ */ jsx2("div", { className: "sm:w-[120px] sm:shrink-0", children: /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }) }),
451
- /* @__PURE__ */ jsx2(
452
- "span",
453
- {
454
- className: "inline-flex items-center px-2 py-0.5 rounded-full border text-xs font-medium sm:hidden",
455
- style: { borderColor: "#0885FE", color: "#0885FE" },
456
- children: "GROUP"
457
- }
458
- ),
459
- /* @__PURE__ */ jsx2("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
460
- /* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
461
- available,
462
- "/",
463
- cls.maxParticipants
464
- ] }),
465
- /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
466
- ] }) })
467
- ] }),
468
- /* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
469
- /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
470
- /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
471
- cls.hostName && /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
472
- ] }),
473
- /* @__PURE__ */ jsx2("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
474
- /* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
475
- available,
476
- "/",
477
- cls.maxParticipants
478
- ] }),
479
- /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
480
- ] }) }),
481
- /* @__PURE__ */ jsx2(
482
- "button",
483
- {
484
- className: "w-full sm:w-[200px] sm:shrink-0 px-5 py-[10px] flex items-center justify-center rounded-lg font-body text-base font-bold cursor-pointer box-border",
485
- style: isFull ? {
486
- background: "transparent",
487
- border: "1px solid var(--color-dark, #200C02)",
488
- color: "var(--color-dark, #200C02)"
489
- } : {
490
- background: "var(--color-primary, #3D312B)",
491
- border: "none",
492
- color: "var(--color-light, #FEFFF0)"
493
- },
494
- onClick: () => {
495
- if (!cls.id) return;
496
- onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
497
- },
498
- children: isFull ? "Join Waitlist" : "Book Now"
499
- }
500
- )
501
- ] })
502
- ] }),
503
- i < selectedClasses.length - 1 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
504
- ] }, cls.id ?? i);
505
- }) })
506
- ] });
507
- }
508
- function CalendarFoldIcon() {
509
- return /* @__PURE__ */ jsxs2(
510
- "svg",
511
- {
512
- width: "16",
513
- height: "16",
514
- viewBox: "0 0 24 24",
515
- fill: "none",
516
- stroke: "currentColor",
517
- strokeWidth: "2",
518
- strokeLinecap: "round",
519
- strokeLinejoin: "round",
520
- style: { flexShrink: 0 },
521
- children: [
522
- /* @__PURE__ */ jsx2("path", { d: "M8 2v4" }),
523
- /* @__PURE__ */ jsx2("path", { d: "M16 2v4" }),
524
- /* @__PURE__ */ jsx2("path", { d: "M21 17V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11Z" }),
525
- /* @__PURE__ */ jsx2("path", { d: "M3 10h18" }),
526
- /* @__PURE__ */ jsx2("path", { d: "M15 22v-4a2 2 0 0 1 2-2h4" })
527
- ]
528
- }
529
- );
530
- }
531
- function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter }) {
532
- const [schedule, setSchedule] = useState2(null);
533
- const [loading, setLoading] = useState2(true);
534
- const [inEditor, setInEditor] = useState2(false);
535
- const [isHovered, setIsHovered] = useState2(false);
536
- const [modalState, setModalState] = useState2(null);
537
- const switchScheduleIdRef = useRef(null);
538
- const dates = useMemo(() => {
539
- const today = /* @__PURE__ */ new Date();
540
- today.setHours(0, 0, 0, 0);
541
- return Array.from({ length: 14 }, (_, i) => {
542
- const d = new Date(today);
543
- d.setDate(today.getDate() + i);
544
- return d;
545
- });
546
- }, []);
547
- const [selectedIdx, setSelectedIdx] = useState2(0);
548
- useEffect(() => {
549
- if (!schedule?.classes) return;
550
- for (let i = 0; i < dates.length; i++) {
551
- if (schedule.classes.some((c) => classRunsOnDate(c, dates[i], schedule.type))) {
552
- setSelectedIdx(i);
553
- return;
554
- }
555
- }
556
- }, [schedule, dates]);
557
- useEffect(() => {
558
- if (typeof window === "undefined") return;
559
- const isInEditor = window.self !== window.top;
560
- setInEditor(isInEditor);
561
- if (isInEditor) {
562
- const startEmpty = initialScheduleId === null;
563
- if (startEmpty) setLoading(false);
564
- let initialized = startEmpty;
565
- const timer = !startEmpty ? setTimeout(() => {
566
- if (!initialized) {
567
- initialized = true;
568
- setLoading(false);
569
- }
570
- }, 5e3) : null;
571
- const handler = (e) => {
572
- if (e.data?.type === "ow:clear-scheduling-widget") {
573
- if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
574
- setSchedule(null);
575
- setLoading(false);
576
- return;
577
- }
578
- if (e.data?.type === "ow:switch-schedule") {
579
- if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
580
- switchScheduleIdRef.current = e.data.scheduleId ?? null;
581
- initialized = false;
582
- setLoading(true);
583
- window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
584
- return;
585
- }
586
- if (e.data?.type !== "ow:user-schedules-response") return;
587
- if (e.data.insertAfter && e.data.insertAfter !== insertAfter) return;
588
- if (initialized && switchScheduleIdRef.current === null) return;
589
- initialized = true;
590
- if (timer) clearTimeout(timer);
591
- const schedules = e.data.schedules ?? [];
592
- const isSwitching = switchScheduleIdRef.current !== null;
593
- const target = isSwitching ? schedules.find((s) => s.id === switchScheduleIdRef.current) ?? schedules[0] ?? null : initialScheduleId ? schedules.find((s) => s.id === initialScheduleId) ?? schedules[0] ?? null : schedules[0] ?? null;
594
- switchScheduleIdRef.current = null;
595
- setSchedule(target);
596
- setLoading(false);
597
- if (notifyOnConnect && target && !isSwitching) {
598
- window.parent.postMessage({
599
- type: "ow:schedule-connected",
600
- schedule: { id: target.id, name: target.name },
601
- insertAfter
602
- }, "*");
603
- window.postMessage({ type: "ow:schedule-linked", scheduleId: target.id, insertAfter }, "*");
604
- }
605
- };
606
- window.addEventListener("message", handler);
607
- if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
608
- return () => {
609
- window.removeEventListener("message", handler);
610
- if (timer) clearTimeout(timer);
611
- };
612
- }
613
- if (initialScheduleId === null) {
614
- setLoading(false);
615
- return;
616
- }
617
- ;
618
- (async () => {
619
- try {
620
- if (initialScheduleId) {
621
- const res = await fetch(`${API_URL}/api/schedule/id/${initialScheduleId}`);
622
- const data = await res.json();
623
- setSchedule(data?.id ? data : null);
624
- } else {
625
- const domain = getApiDomain();
626
- const sitemapRes = await fetch(`${API_URL}/api/schedule/sitemap/${domain}`);
627
- const slugs = await sitemapRes.json();
628
- if (!Array.isArray(slugs) || !slugs.length) {
629
- setLoading(false);
630
- return;
631
- }
632
- const { slug } = [...slugs].sort(
633
- (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
634
- )[0];
635
- const res = await fetch(`${API_URL}/api/schedule/${domain}/${slug}`);
636
- const data = await res.json();
637
- setSchedule(data?.id ? data : null);
638
- }
639
- } catch {
640
- }
641
- setLoading(false);
642
- })();
643
- }, []);
644
- const timezoneLabel = schedule?.timezone ? (() => {
645
- try {
646
- return buildTimezoneLabel(schedule.timezone);
647
- } catch {
648
- return void 0;
649
- }
650
- })() : void 0;
651
- const handleModalSubmit = async (email) => {
652
- if (!modalState) return;
653
- const { mode, classId, classDate } = modalState;
654
- const endpoint = mode === "book" ? `${API_URL}/api/schedule/class/${classId}/booking` : `${API_URL}/api/schedule/class/${classId}/waitlist`;
655
- const res = await fetch(endpoint, {
656
- method: "POST",
657
- headers: { "Content-Type": "application/json" },
658
- body: JSON.stringify({ classDate: classDate.toISOString(), email })
659
- });
660
- if (!res.ok) {
661
- const data = await res.json().catch(() => ({}));
662
- throw new Error(data?.message ?? "Something went wrong. Please try again.");
663
- }
664
- };
665
- const handleReplaceSchedule = () => {
666
- setIsHovered(false);
667
- if (schedule) {
668
- window.parent.postMessage({
669
- type: "ow:schedule-connected",
670
- schedule: { id: schedule.id, name: schedule.name },
671
- insertAfter
672
- }, "*");
673
- } else {
674
- window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
675
- }
676
- };
677
- const sectionId = `scheduling-${insertAfter}`;
678
- return /* @__PURE__ */ jsxs2(
679
- "section",
680
- {
681
- "data-ohw-section": sectionId,
682
- "data-ohw-scheduling-anchor": insertAfter,
683
- className: "w-full box-border py-10 px-5 sm:py-20 sm:px-16 font-body bg-(--color-light,#FEFFF0) relative",
684
- onMouseEnter: () => inEditor && setIsHovered(true),
685
- onMouseLeave: () => setIsHovered(false),
686
- children: [
687
- inEditor && isHovered && !!schedule && /* @__PURE__ */ jsxs2(
688
- "div",
689
- {
690
- className: "absolute inset-0 z-10 pointer-events-auto cursor-pointer",
691
- onClick: handleReplaceSchedule,
692
- children: [
693
- /* @__PURE__ */ jsx2("div", { className: "absolute inset-0", style: { background: "#0885FE", opacity: 0.18 } }),
694
- /* @__PURE__ */ jsx2("div", { className: "absolute inset-0", style: { boxShadow: "inset 0 0 0 1.5px #0885FE" } }),
695
- /* @__PURE__ */ jsx2("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs2(
696
- "div",
697
- {
698
- className: "bg-white border border-[#E7E5E4] rounded-md px-3 py-1.5 flex items-center gap-1.5",
699
- style: {
700
- fontSize: 14,
701
- lineHeight: "24px",
702
- fontFamily: "'Figtree', system-ui, sans-serif",
703
- fontWeight: 500,
704
- color: "#0C0A09"
705
- },
706
- children: [
707
- /* @__PURE__ */ jsx2(CalendarFoldIcon, {}),
708
- "Replace schedule"
709
- ]
710
- }
711
- ) })
712
- ]
713
- }
714
- ),
715
- /* @__PURE__ */ jsxs2("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
716
- /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-4", children: [
717
- /* @__PURE__ */ jsx2("h2", { className: "font-display text-4xl sm:text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
718
- schedule?.description && /* @__PURE__ */ jsx2("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
719
- ] }),
720
- /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col items-end gap-3", children: [
721
- timezoneLabel && /* @__PURE__ */ jsx2("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
722
- /* @__PURE__ */ jsx2("div", { className: "w-full p-0 sm:p-10 box-border", children: loading ? /* @__PURE__ */ jsx2(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ jsx2(EmptyState, { inEditor }) : /* @__PURE__ */ jsx2(
723
- ScheduleView,
724
- {
725
- schedule,
726
- dates,
727
- selectedIdx,
728
- onSelectDate: setSelectedIdx,
729
- onOpenModal: inEditor ? void 0 : (mode, classId, classDate) => setModalState({ mode, classId, classDate })
730
- }
731
- ) })
732
- ] })
733
- ] }),
734
- modalState && /* @__PURE__ */ jsx2(
735
- EmailCaptureModal,
736
- {
737
- title: modalState.mode === "book" ? "Confirm your spot" : "Leave your email",
738
- subtitle: modalState.mode === "book" ? "We'll send your booking confirmation here." : "We'll let you know when spots open up!",
739
- onSubmit: handleModalSubmit,
740
- onClose: () => setModalState(null)
741
- }
742
- )
743
- ]
744
- }
745
- );
746
- }
4
+ import React4, { useCallback as useCallback2, useEffect as useEffect2, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
747
5
 
748
6
  // src/ui/toggle-group.tsx
749
- import * as React2 from "react";
7
+ import * as React from "react";
750
8
 
751
9
  // node_modules/clsx/dist/clsx.mjs
752
10
  function r(e) {
@@ -4066,7 +3324,7 @@ var cva = (base, config) => (props) => {
4066
3324
 
4067
3325
  // src/ui/toggle.tsx
4068
3326
  import { Toggle as TogglePrimitive } from "radix-ui";
4069
- import { jsx as jsx3 } from "react/jsx-runtime";
3327
+ import { jsx } from "react/jsx-runtime";
4070
3328
  var toggleVariants = cva(
4071
3329
  "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
4072
3330
  {
@@ -4093,7 +3351,7 @@ function Toggle({
4093
3351
  size,
4094
3352
  ...props
4095
3353
  }) {
4096
- return /* @__PURE__ */ jsx3(
3354
+ return /* @__PURE__ */ jsx(
4097
3355
  TogglePrimitive.Root,
4098
3356
  {
4099
3357
  "data-slot": "toggle",
@@ -4104,8 +3362,8 @@ function Toggle({
4104
3362
  }
4105
3363
 
4106
3364
  // src/ui/toggle-group.tsx
4107
- import { jsx as jsx4 } from "react/jsx-runtime";
4108
- var ToggleGroupContext = React2.createContext({
3365
+ import { jsx as jsx2 } from "react/jsx-runtime";
3366
+ var ToggleGroupContext = React.createContext({
4109
3367
  value: "",
4110
3368
  onValueChange: () => {
4111
3369
  }
@@ -4117,13 +3375,13 @@ function ToggleGroup({
4117
3375
  children,
4118
3376
  ...props
4119
3377
  }) {
4120
- return /* @__PURE__ */ jsx4(
3378
+ return /* @__PURE__ */ jsx2(
4121
3379
  "div",
4122
3380
  {
4123
3381
  role: "group",
4124
3382
  className: cn("flex items-center gap-1 p-1 bg-muted rounded-md", className),
4125
3383
  ...props,
4126
- children: /* @__PURE__ */ jsx4(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
3384
+ children: /* @__PURE__ */ jsx2(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
4127
3385
  }
4128
3386
  );
4129
3387
  }
@@ -4133,8 +3391,8 @@ function ToggleGroupItem({
4133
3391
  children,
4134
3392
  ...props
4135
3393
  }) {
4136
- const { value: groupValue, onValueChange } = React2.useContext(ToggleGroupContext);
4137
- return /* @__PURE__ */ jsx4(
3394
+ const { value: groupValue, onValueChange } = React.useContext(ToggleGroupContext);
3395
+ return /* @__PURE__ */ jsx2(
4138
3396
  Toggle,
4139
3397
  {
4140
3398
  pressed: groupValue === value,
@@ -4448,9 +3706,9 @@ function calcPopoverPos(rect, parentScroll, approxW = 483, approxH = 320) {
4448
3706
  }
4449
3707
 
4450
3708
  // src/ui/button.tsx
4451
- import * as React3 from "react";
3709
+ import * as React2 from "react";
4452
3710
  import { Slot } from "radix-ui";
4453
- import { jsx as jsx5 } from "react/jsx-runtime";
3711
+ import { jsx as jsx3 } from "react/jsx-runtime";
4454
3712
  var buttonVariants = cva(
4455
3713
  "inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium transition-colors outline-none disabled:pointer-events-none disabled:opacity-50 min-w-[80px] px-3 py-2",
4456
3714
  {
@@ -4471,10 +3729,10 @@ var buttonVariants = cva(
4471
3729
  }
4472
3730
  }
4473
3731
  );
4474
- var Button = React3.forwardRef(
3732
+ var Button = React2.forwardRef(
4475
3733
  ({ className, variant, size, asChild = false, ...props }, ref) => {
4476
3734
  const Comp = asChild ? Slot.Root : "button";
4477
- return /* @__PURE__ */ jsx5(
3735
+ return /* @__PURE__ */ jsx3(
4478
3736
  Comp,
4479
3737
  {
4480
3738
  ref,
@@ -4488,75 +3746,75 @@ var Button = React3.forwardRef(
4488
3746
  Button.displayName = "Button";
4489
3747
 
4490
3748
  // src/ui/icons.tsx
4491
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
3749
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
4492
3750
  function IconX({ className, ...props }) {
4493
- return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4494
- /* @__PURE__ */ jsx6("path", { d: "M18 6 6 18" }),
4495
- /* @__PURE__ */ jsx6("path", { d: "m6 6 12 12" })
3751
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
3752
+ /* @__PURE__ */ jsx4("path", { d: "M18 6 6 18" }),
3753
+ /* @__PURE__ */ jsx4("path", { d: "m6 6 12 12" })
4496
3754
  ] });
4497
3755
  }
4498
3756
  function IconChevronDown({ className, ...props }) {
4499
- return /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx6("path", { d: "m6 9 6 6 6-6" }) });
3757
+ return /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx4("path", { d: "m6 9 6 6 6-6" }) });
4500
3758
  }
4501
3759
  function IconFile({ className, ...props }) {
4502
- return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4503
- /* @__PURE__ */ jsx6("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
4504
- /* @__PURE__ */ jsx6("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
3760
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
3761
+ /* @__PURE__ */ jsx4("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
3762
+ /* @__PURE__ */ jsx4("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
4505
3763
  ] });
4506
3764
  }
4507
3765
  function IconInfo({ className, ...props }) {
4508
- return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4509
- /* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "10" }),
4510
- /* @__PURE__ */ jsx6("path", { d: "M12 16v-4" }),
4511
- /* @__PURE__ */ jsx6("path", { d: "M12 8h.01" })
3766
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
3767
+ /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "10" }),
3768
+ /* @__PURE__ */ jsx4("path", { d: "M12 16v-4" }),
3769
+ /* @__PURE__ */ jsx4("path", { d: "M12 8h.01" })
4512
3770
  ] });
4513
3771
  }
4514
3772
  function IconArrowRight({ className, ...props }) {
4515
- return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4516
- /* @__PURE__ */ jsx6("path", { d: "M5 12h14" }),
4517
- /* @__PURE__ */ jsx6("path", { d: "m12 5 7 7-7 7" })
3773
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
3774
+ /* @__PURE__ */ jsx4("path", { d: "M5 12h14" }),
3775
+ /* @__PURE__ */ jsx4("path", { d: "m12 5 7 7-7 7" })
4518
3776
  ] });
4519
3777
  }
4520
3778
  function IconSection({ className, ...props }) {
4521
- return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4522
- /* @__PURE__ */ jsx6("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
4523
- /* @__PURE__ */ jsx6("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
4524
- /* @__PURE__ */ jsx6("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
3779
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
3780
+ /* @__PURE__ */ jsx4("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
3781
+ /* @__PURE__ */ jsx4("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
3782
+ /* @__PURE__ */ jsx4("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
4525
3783
  ] });
4526
3784
  }
4527
3785
 
4528
3786
  // src/ui/link-modal/DestinationBreadcrumb.tsx
4529
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
3787
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
4530
3788
  function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
4531
- return /* @__PURE__ */ jsxs4("div", { className: "flex w-full flex-col gap-2", children: [
4532
- /* @__PURE__ */ jsx7("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
4533
- /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-3", children: [
4534
- /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
4535
- /* @__PURE__ */ jsx7(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4536
- /* @__PURE__ */ jsx7("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
3789
+ return /* @__PURE__ */ jsxs2("div", { className: "flex w-full flex-col gap-2", children: [
3790
+ /* @__PURE__ */ jsx5("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
3791
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3", children: [
3792
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
3793
+ /* @__PURE__ */ jsx5(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
3794
+ /* @__PURE__ */ jsx5("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
4537
3795
  ] }),
4538
- /* @__PURE__ */ jsx7(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
4539
- /* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
4540
- /* @__PURE__ */ jsx7(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4541
- /* @__PURE__ */ jsx7("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
3796
+ /* @__PURE__ */ jsx5(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
3797
+ /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
3798
+ /* @__PURE__ */ jsx5(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
3799
+ /* @__PURE__ */ jsx5("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
4542
3800
  ] })
4543
3801
  ] })
4544
3802
  ] });
4545
3803
  }
4546
3804
 
4547
3805
  // src/ui/link-modal/SectionTreeItem.tsx
4548
- import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
3806
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
4549
3807
  function SectionTreeItem({ section, onSelect, selected }) {
4550
3808
  const interactive = Boolean(onSelect);
4551
- return /* @__PURE__ */ jsxs5("div", { className: "flex h-9 w-full items-end pl-3", children: [
4552
- /* @__PURE__ */ jsx8(
3809
+ return /* @__PURE__ */ jsxs3("div", { className: "flex h-9 w-full items-end pl-3", children: [
3810
+ /* @__PURE__ */ jsx6(
4553
3811
  "div",
4554
3812
  {
4555
3813
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
4556
3814
  "aria-hidden": true
4557
3815
  }
4558
3816
  ),
4559
- /* @__PURE__ */ jsxs5(
3817
+ /* @__PURE__ */ jsxs3(
4560
3818
  "div",
4561
3819
  {
4562
3820
  role: interactive ? "button" : void 0,
@@ -4574,8 +3832,8 @@ function SectionTreeItem({ section, onSelect, selected }) {
4574
3832
  interactive && selected && "border-primary"
4575
3833
  ),
4576
3834
  children: [
4577
- /* @__PURE__ */ jsx8(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4578
- /* @__PURE__ */ jsx8("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
3835
+ /* @__PURE__ */ jsx6(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
3836
+ /* @__PURE__ */ jsx6("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
4579
3837
  ]
4580
3838
  }
4581
3839
  )
@@ -4583,23 +3841,23 @@ function SectionTreeItem({ section, onSelect, selected }) {
4583
3841
  }
4584
3842
  function SectionPickerList({ sections, onSelect }) {
4585
3843
  if (sections.length === 0) {
4586
- return /* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
3844
+ return /* @__PURE__ */ jsx6("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
4587
3845
  }
4588
- return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", children: [
4589
- /* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
4590
- sections.map((section) => /* @__PURE__ */ jsx8(SectionTreeItem, { section, onSelect }, section.id))
3846
+ return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-1", children: [
3847
+ /* @__PURE__ */ jsx6("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
3848
+ sections.map((section) => /* @__PURE__ */ jsx6(SectionTreeItem, { section, onSelect }, section.id))
4591
3849
  ] });
4592
3850
  }
4593
3851
 
4594
3852
  // src/ui/link-modal/UrlOrPageInput.tsx
4595
- import { useId, useRef as useRef2, useState as useState3 } from "react";
3853
+ import { useId, useRef, useState } from "react";
4596
3854
 
4597
3855
  // src/ui/input.tsx
4598
- import * as React4 from "react";
4599
- import { jsx as jsx9 } from "react/jsx-runtime";
4600
- var Input = React4.forwardRef(
3856
+ import * as React3 from "react";
3857
+ import { jsx as jsx7 } from "react/jsx-runtime";
3858
+ var Input = React3.forwardRef(
4601
3859
  ({ className, type, ...props }, ref) => {
4602
- return /* @__PURE__ */ jsx9(
3860
+ return /* @__PURE__ */ jsx7(
4603
3861
  "input",
4604
3862
  {
4605
3863
  type,
@@ -4618,9 +3876,9 @@ Input.displayName = "Input";
4618
3876
 
4619
3877
  // src/ui/label.tsx
4620
3878
  import { Label as LabelPrimitive } from "radix-ui";
4621
- import { jsx as jsx10 } from "react/jsx-runtime";
3879
+ import { jsx as jsx8 } from "react/jsx-runtime";
4622
3880
  function Label({ className, ...props }) {
4623
- return /* @__PURE__ */ jsx10(
3881
+ return /* @__PURE__ */ jsx8(
4624
3882
  LabelPrimitive.Root,
4625
3883
  {
4626
3884
  "data-slot": "label",
@@ -4632,12 +3890,12 @@ function Label({ className, ...props }) {
4632
3890
 
4633
3891
  // src/ui/popover.tsx
4634
3892
  import { Popover as PopoverPrimitive } from "radix-ui";
4635
- import { jsx as jsx11 } from "react/jsx-runtime";
3893
+ import { jsx as jsx9 } from "react/jsx-runtime";
4636
3894
  function Popover({ ...props }) {
4637
- return /* @__PURE__ */ jsx11(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
3895
+ return /* @__PURE__ */ jsx9(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
4638
3896
  }
4639
3897
  function PopoverTrigger({ ...props }) {
4640
- return /* @__PURE__ */ jsx11(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
3898
+ return /* @__PURE__ */ jsx9(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
4641
3899
  }
4642
3900
  function PopoverContent({
4643
3901
  className,
@@ -4645,7 +3903,7 @@ function PopoverContent({
4645
3903
  sideOffset = 6,
4646
3904
  ...props
4647
3905
  }) {
4648
- return /* @__PURE__ */ jsx11(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx11(
3906
+ return /* @__PURE__ */ jsx9(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx9(
4649
3907
  PopoverPrimitive.Content,
4650
3908
  {
4651
3909
  "data-slot": "popover-content",
@@ -4661,9 +3919,9 @@ function PopoverContent({
4661
3919
  }
4662
3920
 
4663
3921
  // src/ui/link-modal/UrlOrPageInput.tsx
4664
- import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
3922
+ import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
4665
3923
  function FieldChevron({ onClick }) {
4666
- return /* @__PURE__ */ jsx12(
3924
+ return /* @__PURE__ */ jsx10(
4667
3925
  "button",
4668
3926
  {
4669
3927
  type: "button",
@@ -4671,7 +3929,7 @@ function FieldChevron({ onClick }) {
4671
3929
  onClick,
4672
3930
  "aria-label": "Open page list",
4673
3931
  tabIndex: -1,
4674
- children: /* @__PURE__ */ jsx12(IconChevronDown, {})
3932
+ children: /* @__PURE__ */ jsx10(IconChevronDown, {})
4675
3933
  }
4676
3934
  );
4677
3935
  }
@@ -4688,8 +3946,8 @@ function UrlOrPageInput({
4688
3946
  urlError
4689
3947
  }) {
4690
3948
  const inputId = useId();
4691
- const inputRef = useRef2(null);
4692
- const [isFocused, setIsFocused] = useState3(false);
3949
+ const inputRef = useRef(null);
3950
+ const [isFocused, setIsFocused] = useState(false);
4693
3951
  const openDropdown = () => {
4694
3952
  if (readOnly || filteredPages.length === 0) return;
4695
3953
  onDropdownOpenChange(true);
@@ -4710,12 +3968,12 @@ function UrlOrPageInput({
4710
3968
  "data-ohw-link-field flex h-10 w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
4711
3969
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
4712
3970
  );
4713
- return /* @__PURE__ */ jsxs6("div", { className: "flex w-full flex-col gap-2 p-0", children: [
4714
- /* @__PURE__ */ jsx12(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
4715
- /* @__PURE__ */ jsxs6(Popover, { open: dropdownOpen && !readOnly, onOpenChange: onDropdownOpenChange, children: [
4716
- /* @__PURE__ */ jsx12(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs6("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
4717
- selectedPage ? /* @__PURE__ */ jsx12("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx12(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
4718
- readOnly ? /* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx12(
3971
+ return /* @__PURE__ */ jsxs4("div", { className: "flex w-full flex-col gap-2 p-0", children: [
3972
+ /* @__PURE__ */ jsx10(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
3973
+ /* @__PURE__ */ jsxs4(Popover, { open: dropdownOpen && !readOnly, onOpenChange: onDropdownOpenChange, children: [
3974
+ /* @__PURE__ */ jsx10(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs4("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
3975
+ selectedPage ? /* @__PURE__ */ jsx10("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx10(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
3976
+ readOnly ? /* @__PURE__ */ jsx10("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx10(
4719
3977
  Input,
4720
3978
  {
4721
3979
  ref: inputRef,
@@ -4734,7 +3992,7 @@ function UrlOrPageInput({
4734
3992
  )
4735
3993
  }
4736
3994
  ),
4737
- selectedPage && !readOnly ? /* @__PURE__ */ jsx12(
3995
+ selectedPage && !readOnly ? /* @__PURE__ */ jsx10(
4738
3996
  "button",
4739
3997
  {
4740
3998
  type: "button",
@@ -4742,31 +4000,31 @@ function UrlOrPageInput({
4742
4000
  onMouseDown: clearSelection,
4743
4001
  "aria-label": "Clear selected page",
4744
4002
  tabIndex: -1,
4745
- children: /* @__PURE__ */ jsx12(IconX, { "aria-hidden": true })
4003
+ children: /* @__PURE__ */ jsx10(IconX, { "aria-hidden": true })
4746
4004
  }
4747
4005
  ) : null,
4748
- !readOnly ? /* @__PURE__ */ jsx12(FieldChevron, { onClick: toggleDropdown }) : null
4006
+ !readOnly ? /* @__PURE__ */ jsx10(FieldChevron, { onClick: toggleDropdown }) : null
4749
4007
  ] }) }),
4750
- filteredPages.length > 0 && /* @__PURE__ */ jsx12(PopoverContent, { "data-ohw-link-popover-root": "", onOpenAutoFocus: (e) => e.preventDefault(), children: filteredPages.map((page) => /* @__PURE__ */ jsxs6(
4008
+ filteredPages.length > 0 && /* @__PURE__ */ jsx10(PopoverContent, { "data-ohw-link-popover-root": "", onOpenAutoFocus: (e) => e.preventDefault(), children: filteredPages.map((page) => /* @__PURE__ */ jsxs4(
4751
4009
  "button",
4752
4010
  {
4753
4011
  type: "button",
4754
4012
  className: "flex h-9 w-full items-center gap-2 border-0 bg-transparent px-3 text-left text-sm leading-5 text-foreground outline-none hover:bg-muted",
4755
4013
  onClick: () => onPageSelect(page),
4756
4014
  children: [
4757
- /* @__PURE__ */ jsx12(IconFile, { className: "shrink-0", "aria-hidden": true }),
4758
- /* @__PURE__ */ jsx12("span", { className: "truncate", children: page.title })
4015
+ /* @__PURE__ */ jsx10(IconFile, { className: "shrink-0", "aria-hidden": true }),
4016
+ /* @__PURE__ */ jsx10("span", { className: "truncate", children: page.title })
4759
4017
  ]
4760
4018
  },
4761
4019
  page.path
4762
4020
  )) })
4763
4021
  ] }),
4764
- urlError ? /* @__PURE__ */ jsx12("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
4022
+ urlError ? /* @__PURE__ */ jsx10("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
4765
4023
  ] });
4766
4024
  }
4767
4025
 
4768
4026
  // src/ui/link-modal/useLinkModalState.ts
4769
- import { useCallback, useEffect as useEffect2, useMemo as useMemo2, useState as useState4 } from "react";
4027
+ import { useCallback, useEffect, useMemo, useState as useState2 } from "react";
4770
4028
  function useLinkModalState({
4771
4029
  open,
4772
4030
  mode,
@@ -4778,16 +4036,16 @@ function useLinkModalState({
4778
4036
  onClose,
4779
4037
  onSubmit
4780
4038
  }) {
4781
- const availablePages = useMemo2(
4039
+ const availablePages = useMemo(
4782
4040
  () => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
4783
4041
  [mode, pages, existingTargets]
4784
4042
  );
4785
- const [searchValue, setSearchValue] = useState4("");
4786
- const [selectedPage, setSelectedPage] = useState4(null);
4787
- const [selectedSection, setSelectedSection] = useState4(null);
4788
- const [step, setStep] = useState4("input");
4789
- const [dropdownOpen, setDropdownOpen] = useState4(false);
4790
- const [urlError, setUrlError] = useState4("");
4043
+ const [searchValue, setSearchValue] = useState2("");
4044
+ const [selectedPage, setSelectedPage] = useState2(null);
4045
+ const [selectedSection, setSelectedSection] = useState2(null);
4046
+ const [step, setStep] = useState2("input");
4047
+ const [dropdownOpen, setDropdownOpen] = useState2(false);
4048
+ const [urlError, setUrlError] = useState2("");
4791
4049
  const reset = useCallback(() => {
4792
4050
  setSearchValue("");
4793
4051
  setSelectedPage(null);
@@ -4796,7 +4054,7 @@ function useLinkModalState({
4796
4054
  setDropdownOpen(false);
4797
4055
  setUrlError("");
4798
4056
  }, []);
4799
- useEffect2(() => {
4057
+ useEffect(() => {
4800
4058
  if (!open) return;
4801
4059
  if (mode === "edit" && initialTarget) {
4802
4060
  const { pageRoute } = parseTarget(initialTarget);
@@ -4812,11 +4070,11 @@ function useLinkModalState({
4812
4070
  setDropdownOpen(false);
4813
4071
  setUrlError("");
4814
4072
  }, [open, mode, initialTarget, pages, sectionsByPath, reset]);
4815
- const filteredPages = useMemo2(() => {
4073
+ const filteredPages = useMemo(() => {
4816
4074
  if (!searchValue.trim()) return availablePages;
4817
4075
  return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
4818
4076
  }, [availablePages, searchValue]);
4819
- const activeSections = useMemo2(() => {
4077
+ const activeSections = useMemo(() => {
4820
4078
  if (!selectedPage) return [];
4821
4079
  return getSectionsForPath(sectionsByPath, selectedPage.path);
4822
4080
  }, [selectedPage, sectionsByPath]);
@@ -4862,7 +4120,7 @@ function useLinkModalState({
4862
4120
  reset();
4863
4121
  onClose();
4864
4122
  };
4865
- const isValid = useMemo2(() => {
4123
+ const isValid = useMemo(() => {
4866
4124
  if (urlError) return false;
4867
4125
  if (showBreadcrumb) return true;
4868
4126
  if (selectedPage) return true;
@@ -4929,7 +4187,7 @@ function useLinkModalState({
4929
4187
  }
4930
4188
 
4931
4189
  // src/ui/link-modal/LinkEditorPanel.tsx
4932
- import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
4190
+ import { Fragment, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
4933
4191
  function LinkEditorPanel({
4934
4192
  open = true,
4935
4193
  mode = "create",
@@ -4953,26 +4211,26 @@ function LinkEditorPanel({
4953
4211
  onSubmit
4954
4212
  });
4955
4213
  const isCancel = state.secondaryLabel === "Cancel";
4956
- return /* @__PURE__ */ jsxs7(Fragment2, { children: [
4957
- /* @__PURE__ */ jsx13(
4214
+ return /* @__PURE__ */ jsxs5(Fragment, { children: [
4215
+ /* @__PURE__ */ jsx11(
4958
4216
  "button",
4959
4217
  {
4960
4218
  type: "button",
4961
4219
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
4962
4220
  onClick: state.handleClose,
4963
4221
  "aria-label": "Close",
4964
- children: /* @__PURE__ */ jsx13(IconX, { "aria-hidden": true })
4222
+ children: /* @__PURE__ */ jsx11(IconX, { "aria-hidden": true })
4965
4223
  }
4966
4224
  ),
4967
- /* @__PURE__ */ jsx13("header", { className: "flex w-full flex-col gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx13("h2", { className: "m-0 w-full break-words text-lg font-semibold leading-none tracking-tight text-card-foreground", children: state.title }) }),
4968
- /* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
4969
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx13(
4225
+ /* @__PURE__ */ jsx11("header", { className: "flex w-full flex-col gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx11("h2", { className: "m-0 w-full break-words text-lg font-semibold leading-none tracking-tight text-card-foreground", children: state.title }) }),
4226
+ /* @__PURE__ */ jsxs5("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
4227
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx11(
4970
4228
  DestinationBreadcrumb,
4971
4229
  {
4972
4230
  pageTitle: state.selectedPage.title,
4973
4231
  sectionLabel: state.selectedSection.label
4974
4232
  }
4975
- ) : /* @__PURE__ */ jsx13(
4233
+ ) : /* @__PURE__ */ jsx11(
4976
4234
  UrlOrPageInput,
4977
4235
  {
4978
4236
  value: state.searchValue,
@@ -4985,18 +4243,18 @@ function LinkEditorPanel({
4985
4243
  urlError: state.urlError
4986
4244
  }
4987
4245
  ),
4988
- state.showChooseSection ? /* @__PURE__ */ jsxs7("div", { className: "flex flex-col justify-center gap-2", children: [
4989
- /* @__PURE__ */ jsx13(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
4990
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
4991
- /* @__PURE__ */ jsx13(IconInfo, { className: "shrink-0", "aria-hidden": true }),
4992
- /* @__PURE__ */ jsx13("span", { children: "Pick a section this link should scroll to." })
4246
+ state.showChooseSection ? /* @__PURE__ */ jsxs5("div", { className: "flex flex-col justify-center gap-2", children: [
4247
+ /* @__PURE__ */ jsx11(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
4248
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
4249
+ /* @__PURE__ */ jsx11(IconInfo, { className: "shrink-0", "aria-hidden": true }),
4250
+ /* @__PURE__ */ jsx11("span", { children: "Pick a section this link should scroll to." })
4993
4251
  ] })
4994
4252
  ] }) : null,
4995
- state.showSectionPicker ? /* @__PURE__ */ jsx13(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
4996
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx13(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
4253
+ state.showSectionPicker ? /* @__PURE__ */ jsx11(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
4254
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx11(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
4997
4255
  ] }),
4998
- /* @__PURE__ */ jsxs7("footer", { className: "flex w-full items-center justify-end gap-2 px-6 pb-6", children: [
4999
- /* @__PURE__ */ jsx13(
4256
+ /* @__PURE__ */ jsxs5("footer", { className: "flex w-full items-center justify-end gap-2 px-6 pb-6", children: [
4257
+ /* @__PURE__ */ jsx11(
5000
4258
  Button,
5001
4259
  {
5002
4260
  type: "button",
@@ -5011,7 +4269,7 @@ function LinkEditorPanel({
5011
4269
  children: state.secondaryLabel
5012
4270
  }
5013
4271
  ),
5014
- /* @__PURE__ */ jsx13(
4272
+ /* @__PURE__ */ jsx11(
5015
4273
  Button,
5016
4274
  {
5017
4275
  type: "button",
@@ -5030,7 +4288,7 @@ function LinkEditorPanel({
5030
4288
  }
5031
4289
 
5032
4290
  // src/ui/link-modal/LinkPopover.tsx
5033
- import { jsx as jsx14 } from "react/jsx-runtime";
4291
+ import { jsx as jsx12 } from "react/jsx-runtime";
5034
4292
  function LinkPopover({
5035
4293
  rect,
5036
4294
  parentScroll,
@@ -5038,7 +4296,7 @@ function LinkPopover({
5038
4296
  ...editorProps
5039
4297
  }) {
5040
4298
  const { top, left, transform } = calcPopoverPos(rect, parentScroll);
5041
- return /* @__PURE__ */ jsx14(
4299
+ return /* @__PURE__ */ jsx12(
5042
4300
  "div",
5043
4301
  {
5044
4302
  ref: panelRef,
@@ -5051,7 +4309,7 @@ function LinkPopover({
5051
4309
  ),
5052
4310
  style: { top, left, transform },
5053
4311
  onMouseDown: (e) => e.stopPropagation(),
5054
- children: /* @__PURE__ */ jsx14(LinkEditorPanel, { ...editorProps, open: true })
4312
+ children: /* @__PURE__ */ jsx12(LinkEditorPanel, { ...editorProps, open: true })
5055
4313
  }
5056
4314
  );
5057
4315
  }
@@ -5114,30 +4372,8 @@ function shouldUseDevFixtures() {
5114
4372
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
5115
4373
  }
5116
4374
 
5117
- // src/ui/badge.tsx
5118
- import { jsx as jsx15 } from "react/jsx-runtime";
5119
- var badgeVariants = cva(
5120
- "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
5121
- {
5122
- variants: {
5123
- variant: {
5124
- default: "border-transparent bg-primary text-primary-foreground",
5125
- secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
5126
- destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
5127
- outline: "text-foreground"
5128
- }
5129
- },
5130
- defaultVariants: {
5131
- variant: "default"
5132
- }
5133
- }
5134
- );
5135
- function Badge({ className, variant, ...props }) {
5136
- return /* @__PURE__ */ jsx15("div", { className: cn(badgeVariants({ variant }), className), ...props });
5137
- }
5138
-
5139
4375
  // src/OhhwellsBridge.tsx
5140
- import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
4376
+ import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
5141
4377
  var PRIMARY = "#0885FE";
5142
4378
  var IMAGE_FADE_MS = 300;
5143
4379
  function runOpacityFade(el, onDone) {
@@ -5188,163 +4424,6 @@ function fadeInBgImage(el, url, onReady) {
5188
4424
  if (!prevPos || prevPos === "static") el.style.position = prevPos;
5189
4425
  });
5190
4426
  }
5191
- function getSectionsTracker() {
5192
- let el = document.querySelector("[data-ohw-sections-tracker]");
5193
- if (!el) {
5194
- el = document.createElement("div");
5195
- el.setAttribute("data-ohw-sections-tracker", "");
5196
- el.style.display = "none";
5197
- document.body.appendChild(el);
5198
- }
5199
- return el;
5200
- }
5201
- function updateSectionScheduleId(insertAfter, scheduleId) {
5202
- const tracker = getSectionsTracker();
5203
- let sections = [];
5204
- try {
5205
- sections = JSON.parse(tracker.textContent || "[]");
5206
- } catch {
5207
- }
5208
- const currentPath = window.location.pathname;
5209
- const updated = sections.map((s) => {
5210
- if (s.type !== "scheduling" || s.insertAfter !== insertAfter) return s;
5211
- if (s.pagePath && s.pagePath !== currentPath) return s;
5212
- return { ...s, scheduleId };
5213
- });
5214
- tracker.textContent = JSON.stringify(updated);
5215
- return tracker.textContent ?? "[]";
5216
- }
5217
- function schedulingSectionId(insertAfter) {
5218
- return `scheduling-${insertAfter}`;
5219
- }
5220
- var INSERT_BEFORE_MARKER = ":before:";
5221
- function parseSchedulingInsertAfter(insertAfter) {
5222
- const idx = insertAfter.indexOf(INSERT_BEFORE_MARKER);
5223
- if (idx < 0) return { anchor: insertAfter, insertBefore: null };
5224
- return {
5225
- anchor: insertAfter.slice(0, idx),
5226
- insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
5227
- };
5228
- }
5229
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
5230
- const parsed = parseSchedulingInsertAfter(insertAfter);
5231
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
5232
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
5233
- return { effectiveInsertAfter, insertBefore };
5234
- }
5235
- function getSchedulingMountPoint(insertAfter) {
5236
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
5237
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
5238
- if (!anchorEl && anchor === "scheduling") {
5239
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
5240
- anchorEl = widgets.at(-1) ?? null;
5241
- }
5242
- if (!anchorEl) return null;
5243
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
5244
- }
5245
- function schedulingMountDepth(insertAfter) {
5246
- if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
5247
- return insertAfter.split(INSERT_BEFORE_MARKER).length - 1;
5248
- }
5249
- function getPageSchedulingEntries(raw) {
5250
- if (!raw) return [];
5251
- try {
5252
- const entries = JSON.parse(raw);
5253
- const currentPath = window.location.pathname;
5254
- return entries.filter((e) => e.type === "scheduling" && (!e.pagePath || e.pagePath === currentPath));
5255
- } catch {
5256
- return [];
5257
- }
5258
- }
5259
- function isSchedulingWidgetMissing(entry) {
5260
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
5261
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
5262
- }
5263
- function hasMissingSchedulingWidgets(entries) {
5264
- return entries.some(isSchedulingWidgetMissing);
5265
- }
5266
- function retryMissingSchedulingMounts(entries, notifyOnConnect = false) {
5267
- if (!hasMissingSchedulingWidgets(entries)) return;
5268
- mountSchedulingEntries(entries, notifyOnConnect);
5269
- }
5270
- function initSectionsFromContent(content, removeExisting = false) {
5271
- const raw = content["__ohw_sections"];
5272
- if (!raw) return;
5273
- try {
5274
- if (removeExisting) {
5275
- document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => el.remove());
5276
- }
5277
- const inEditor = typeof window !== "undefined" && window.self !== window.top;
5278
- if (inEditor) getSectionsTracker().textContent = raw;
5279
- const pageEntries = getPageSchedulingEntries(raw);
5280
- mountSchedulingEntries(pageEntries, false);
5281
- requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, false));
5282
- setTimeout(() => retryMissingSchedulingMounts(pageEntries, false), 250);
5283
- } catch {
5284
- }
5285
- }
5286
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
5287
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
5288
- const sectionId = schedulingSectionId(effectiveInsertAfter);
5289
- if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
5290
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
5291
- if (!mountPoint) return false;
5292
- const container = document.createElement("div");
5293
- container.dataset.ohwSectionContainer = "scheduling";
5294
- if (insertBefore) {
5295
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
5296
- const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
5297
- if (!beforePoint) return false;
5298
- beforePoint.insertAdjacentElement("beforebegin", container);
5299
- } else {
5300
- let tail = mountPoint;
5301
- while (tail.nextElementSibling?.dataset.ohwSectionContainer === "scheduling") {
5302
- tail = tail.nextElementSibling;
5303
- }
5304
- tail.insertAdjacentElement("afterend", container);
5305
- }
5306
- const root = createRoot(container);
5307
- flushSync(() => {
5308
- root.render(
5309
- /* @__PURE__ */ jsx16(
5310
- SchedulingWidget,
5311
- {
5312
- notifyOnConnect,
5313
- initialScheduleId: scheduleId,
5314
- insertAfter: effectiveInsertAfter
5315
- }
5316
- )
5317
- );
5318
- });
5319
- const tracker = getSectionsTracker();
5320
- let sections = [];
5321
- try {
5322
- sections = JSON.parse(tracker.textContent || "[]");
5323
- } catch {
5324
- }
5325
- const inEditor = typeof window !== "undefined" && window.self !== window.top;
5326
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
5327
- sections.push({
5328
- type: "scheduling",
5329
- insertAfter: effectiveInsertAfter,
5330
- pagePath: window.location.pathname,
5331
- ...scheduleId ? { scheduleId } : {}
5332
- });
5333
- tracker.textContent = JSON.stringify(sections);
5334
- }
5335
- return true;
5336
- }
5337
- function mountSchedulingEntries(entries, notifyOnConnect = false) {
5338
- const pending = entries.filter((e) => e.type === "scheduling").sort((a, b) => schedulingMountDepth(a.insertAfter) - schedulingMountDepth(b.insertAfter));
5339
- for (let attempt = 0; attempt < pending.length + 1 && pending.length > 0; attempt++) {
5340
- for (let i = pending.length - 1; i >= 0; i--) {
5341
- const { insertAfter, scheduleId } = pending[i];
5342
- if (mountSchedulingWidget(insertAfter, notifyOnConnect, scheduleId)) {
5343
- pending.splice(i, 1);
5344
- }
5345
- }
5346
- }
5347
- }
5348
4427
  function getLinkHref(el) {
5349
4428
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
5350
4429
  return anchor?.getAttribute("href") ?? "";
@@ -5544,7 +4623,7 @@ var TOOLBAR_GROUPS = [
5544
4623
  ];
5545
4624
  function GlowFrame({ rect, elRef }) {
5546
4625
  const GAP = 6;
5547
- return /* @__PURE__ */ jsx16(
4626
+ return /* @__PURE__ */ jsx13(
5548
4627
  "div",
5549
4628
  {
5550
4629
  ref: elRef,
@@ -5597,7 +4676,7 @@ function FloatingToolbar({
5597
4676
  onEditLink
5598
4677
  }) {
5599
4678
  const { top, left, transform } = calcToolbarPos(rect, parentScroll);
5600
- return /* @__PURE__ */ jsxs8(
4679
+ return /* @__PURE__ */ jsxs6(
5601
4680
  "div",
5602
4681
  {
5603
4682
  ref: elRef,
@@ -5622,11 +4701,11 @@ function FloatingToolbar({
5622
4701
  },
5623
4702
  onMouseDown: (e) => e.stopPropagation(),
5624
4703
  children: [
5625
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs8(React5.Fragment, { children: [
5626
- gi > 0 && /* @__PURE__ */ jsx16("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
4704
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs6(React4.Fragment, { children: [
4705
+ gi > 0 && /* @__PURE__ */ jsx13("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
5627
4706
  btns.map((btn) => {
5628
4707
  const isActive = activeCommands.has(btn.cmd);
5629
- return /* @__PURE__ */ jsx16(
4708
+ return /* @__PURE__ */ jsx13(
5630
4709
  "button",
5631
4710
  {
5632
4711
  title: btn.title,
@@ -5652,7 +4731,7 @@ function FloatingToolbar({
5652
4731
  flexShrink: 0,
5653
4732
  padding: 6
5654
4733
  },
5655
- children: /* @__PURE__ */ jsx16(
4734
+ children: /* @__PURE__ */ jsx13(
5656
4735
  "svg",
5657
4736
  {
5658
4737
  width: "16",
@@ -5672,7 +4751,7 @@ function FloatingToolbar({
5672
4751
  );
5673
4752
  })
5674
4753
  ] }, gi)),
5675
- showEditLink ? /* @__PURE__ */ jsx16(
4754
+ showEditLink ? /* @__PURE__ */ jsx13(
5676
4755
  "button",
5677
4756
  {
5678
4757
  type: "button",
@@ -5704,9 +4783,9 @@ function FloatingToolbar({
5704
4783
  flexShrink: 0,
5705
4784
  padding: 6
5706
4785
  },
5707
- children: /* @__PURE__ */ jsxs8("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
5708
- /* @__PURE__ */ jsx16("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
5709
- /* @__PURE__ */ jsx16("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
4786
+ children: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
4787
+ /* @__PURE__ */ jsx13("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
4788
+ /* @__PURE__ */ jsx13("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
5710
4789
  ] })
5711
4790
  }
5712
4791
  ) : null
@@ -5724,7 +4803,7 @@ function StateToggle({
5724
4803
  states,
5725
4804
  onStateChange
5726
4805
  }) {
5727
- return /* @__PURE__ */ jsx16(
4806
+ return /* @__PURE__ */ jsx13(
5728
4807
  ToggleGroup,
5729
4808
  {
5730
4809
  "data-ohw-state-toggle": "",
@@ -5738,35 +4817,18 @@ function StateToggle({
5738
4817
  left: rect.right - 8,
5739
4818
  transform: "translateX(-100%)"
5740
4819
  },
5741
- children: states.map((state) => /* @__PURE__ */ jsx16(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
4820
+ children: states.map((state) => /* @__PURE__ */ jsx13(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
5742
4821
  }
5743
4822
  );
5744
4823
  }
5745
4824
  var contentCache = /* @__PURE__ */ new Map();
5746
- function resolveSubdomain(subdomainFromQuery) {
5747
- if (subdomainFromQuery) return subdomainFromQuery;
5748
- if (typeof window !== "undefined") {
5749
- const parts = window.location.hostname.split(".");
5750
- if (parts.length >= 3 && parts[0] !== "www") return parts[0];
5751
- }
5752
- const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;
5753
- if (siteUrl) {
5754
- try {
5755
- const host = new URL(siteUrl).hostname;
5756
- const siteParts = host.split(".");
5757
- if (siteParts.length >= 3 && siteParts[0] !== "www") return siteParts[0];
5758
- } catch {
5759
- }
5760
- }
5761
- return "";
5762
- }
5763
4825
  function OhhwellsBridge() {
5764
4826
  const pathname = usePathname();
5765
4827
  const router = useRouter();
5766
4828
  const searchParams = useSearchParams();
5767
4829
  const isEditMode = isEditSessionActive();
5768
- const [bridgeRoot, setBridgeRoot] = useState5(null);
5769
- useEffect3(() => {
4830
+ const [bridgeRoot, setBridgeRoot] = useState3(null);
4831
+ useEffect2(() => {
5770
4832
  const figtreeFontId = "ohw-figtree-font";
5771
4833
  if (!document.getElementById(figtreeFontId)) {
5772
4834
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -5789,47 +4851,50 @@ function OhhwellsBridge() {
5789
4851
  };
5790
4852
  }, []);
5791
4853
  const subdomainFromQuery = searchParams.get("subdomain");
5792
- const subdomain = resolveSubdomain(subdomainFromQuery);
4854
+ const subdomain = subdomainFromQuery ?? (() => {
4855
+ if (typeof window === "undefined") return "";
4856
+ const parts = window.location.hostname.split(".");
4857
+ return parts.length >= 3 && parts[0] !== "www" ? parts[0] : "";
4858
+ })();
5793
4859
  const postToParent = useCallback2((data) => {
5794
4860
  if (typeof window !== "undefined" && window.parent !== window) {
5795
4861
  window.parent.postMessage(data, "*");
5796
4862
  }
5797
4863
  }, []);
5798
- const [fetchState, setFetchState] = useState5("idle");
5799
- const autoSaveTimers = useRef3(/* @__PURE__ */ new Map());
5800
- const activeElRef = useRef3(null);
5801
- const originalContentRef = useRef3(null);
5802
- const activeStateElRef = useRef3(null);
5803
- const parentScrollRef = useRef3(null);
5804
- const toolbarElRef = useRef3(null);
5805
- const glowElRef = useRef3(null);
5806
- const hoveredImageRef = useRef3(null);
5807
- const hoveredImageHasTextOverlapRef = useRef3(false);
5808
- const hoveredGapRef = useRef3(null);
5809
- const imageUnhoverTimerRef = useRef3(null);
5810
- const imageShowTimerRef = useRef3(null);
5811
- const editStylesRef = useRef3(null);
5812
- const activateRef = useRef3(() => {
4864
+ const [fetchState, setFetchState] = useState3("idle");
4865
+ const autoSaveTimers = useRef2(/* @__PURE__ */ new Map());
4866
+ const activeElRef = useRef2(null);
4867
+ const originalContentRef = useRef2(null);
4868
+ const activeStateElRef = useRef2(null);
4869
+ const parentScrollRef = useRef2(null);
4870
+ const toolbarElRef = useRef2(null);
4871
+ const glowElRef = useRef2(null);
4872
+ const hoveredImageRef = useRef2(null);
4873
+ const hoveredImageHasTextOverlapRef = useRef2(false);
4874
+ const imageUnhoverTimerRef = useRef2(null);
4875
+ const imageShowTimerRef = useRef2(null);
4876
+ const editStylesRef = useRef2(null);
4877
+ const activateRef = useRef2(() => {
5813
4878
  });
5814
- const deactivateRef = useRef3(() => {
4879
+ const deactivateRef = useRef2(() => {
5815
4880
  });
5816
- const refreshActiveCommandsRef = useRef3(() => {
4881
+ const refreshActiveCommandsRef = useRef2(() => {
5817
4882
  });
5818
- const postToParentRef = useRef3(postToParent);
4883
+ const postToParentRef = useRef2(postToParent);
5819
4884
  postToParentRef.current = postToParent;
5820
- const [toolbarRect, setToolbarRect] = useState5(null);
5821
- const [toggleState, setToggleState] = useState5(null);
5822
- const [maxBadge, setMaxBadge] = useState5(null);
5823
- const [activeCommands, setActiveCommands] = useState5(/* @__PURE__ */ new Set());
5824
- const [toolbarShowEditLink, setToolbarShowEditLink] = useState5(false);
5825
- const [linkPopover, setLinkPopover] = useState5(null);
5826
- const [sitePages, setSitePages] = useState5([]);
5827
- const [sectionsByPath, setSectionsByPath] = useState5({});
5828
- const sectionsPrefetchGenRef = useRef3(0);
5829
- const setLinkPopoverRef = useRef3(setLinkPopover);
5830
- const linkPopoverPanelRef = useRef3(null);
5831
- const linkPopoverOpenRef = useRef3(false);
5832
- const linkPopoverGraceUntilRef = useRef3(0);
4885
+ const [toolbarRect, setToolbarRect] = useState3(null);
4886
+ const [toggleState, setToggleState] = useState3(null);
4887
+ const [maxBadge, setMaxBadge] = useState3(null);
4888
+ const [activeCommands, setActiveCommands] = useState3(/* @__PURE__ */ new Set());
4889
+ const [toolbarShowEditLink, setToolbarShowEditLink] = useState3(false);
4890
+ const [linkPopover, setLinkPopover] = useState3(null);
4891
+ const [sitePages, setSitePages] = useState3([]);
4892
+ const [sectionsByPath, setSectionsByPath] = useState3({});
4893
+ const sectionsPrefetchGenRef = useRef2(0);
4894
+ const setLinkPopoverRef = useRef2(setLinkPopover);
4895
+ const linkPopoverPanelRef = useRef2(null);
4896
+ const linkPopoverOpenRef = useRef2(false);
4897
+ const linkPopoverGraceUntilRef = useRef2(0);
5833
4898
  setLinkPopoverRef.current = setLinkPopover;
5834
4899
  const bumpLinkPopoverGrace = () => {
5835
4900
  linkPopoverGraceUntilRef.current = Date.now() + 350;
@@ -5853,9 +4918,9 @@ function OhhwellsBridge() {
5853
4918
  );
5854
4919
  });
5855
4920
  }, [isEditMode, pathname]);
5856
- const runSectionsPrefetchRef = useRef3(runSectionsPrefetch);
4921
+ const runSectionsPrefetchRef = useRef2(runSectionsPrefetch);
5857
4922
  runSectionsPrefetchRef.current = runSectionsPrefetch;
5858
- useEffect3(() => {
4923
+ useEffect2(() => {
5859
4924
  if (!linkPopover) return;
5860
4925
  if (hoveredImageRef.current) {
5861
4926
  hoveredImageRef.current = null;
@@ -5863,7 +4928,7 @@ function OhhwellsBridge() {
5863
4928
  }
5864
4929
  postToParent({ type: "ow:image-unhover" });
5865
4930
  }, [linkPopover, postToParent]);
5866
- useEffect3(() => {
4931
+ useEffect2(() => {
5867
4932
  if (!isEditMode) return;
5868
4933
  const useFixtures = shouldUseDevFixtures();
5869
4934
  if (useFixtures) {
@@ -5887,15 +4952,14 @@ function OhhwellsBridge() {
5887
4952
  if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
5888
4953
  return () => window.removeEventListener("message", onSitePages);
5889
4954
  }, [isEditMode, postToParent]);
5890
- const [sectionGap, setSectionGap] = useState5(null);
5891
- useEffect3(() => {
4955
+ useEffect2(() => {
5892
4956
  if (!isEditMode || shouldUseDevFixtures()) return;
5893
4957
  void loadAllSectionsManifest().then((manifest) => {
5894
4958
  if (Object.keys(manifest).length === 0) return;
5895
4959
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
5896
4960
  });
5897
4961
  }, [isEditMode]);
5898
- useEffect3(() => {
4962
+ useEffect2(() => {
5899
4963
  const update = () => {
5900
4964
  const el = activeElRef.current;
5901
4965
  if (el) setToolbarRect(el.getBoundingClientRect());
@@ -5903,12 +4967,6 @@ function OhhwellsBridge() {
5903
4967
  if (!prev || !activeStateElRef.current) return prev;
5904
4968
  return { ...prev, rect: activeStateElRef.current.getBoundingClientRect() };
5905
4969
  });
5906
- setSectionGap((prev) => {
5907
- if (!prev) return prev;
5908
- const anchor = document.querySelector(`[data-ohw-section="${prev.insertAfter}"]`);
5909
- if (!anchor) return prev;
5910
- return { ...prev, y: anchor.getBoundingClientRect().bottom };
5911
- });
5912
4970
  };
5913
4971
  const vvp = window.visualViewport;
5914
4972
  if (!vvp) return;
@@ -5975,7 +5033,6 @@ function OhhwellsBridge() {
5975
5033
  const applyContent = (content) => {
5976
5034
  const imageLoads = [];
5977
5035
  for (const [key, val] of Object.entries(content)) {
5978
- if (key === "__ohw_sections") continue;
5979
5036
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
5980
5037
  if (el.dataset.ohwEditable === "image") {
5981
5038
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -5997,7 +5054,6 @@ function OhhwellsBridge() {
5997
5054
  });
5998
5055
  applyLinkByKey(key, val);
5999
5056
  }
6000
- initSectionsFromContent(content, true);
6001
5057
  return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
6002
5058
  }) : Promise.resolve();
6003
5059
  };
@@ -6022,15 +5078,12 @@ function OhhwellsBridge() {
6022
5078
  cancelled = true;
6023
5079
  };
6024
5080
  }, [subdomain, isEditMode, pathname]);
6025
- useEffect3(() => {
5081
+ useEffect2(() => {
6026
5082
  if (!subdomain || isEditMode) return;
6027
- let debounceTimer = null;
6028
5083
  const applyFromCache = () => {
6029
5084
  const content = contentCache.get(subdomain);
6030
5085
  if (!content) return;
6031
- retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
6032
5086
  for (const [key, val] of Object.entries(content)) {
6033
- if (key === "__ohw_sections") continue;
6034
5087
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
6035
5088
  if (el.dataset.ohwEditable === "image") {
6036
5089
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6047,28 +5100,21 @@ function OhhwellsBridge() {
6047
5100
  applyLinkByKey(key, val);
6048
5101
  }
6049
5102
  };
6050
- const scheduleApply = () => {
6051
- if (debounceTimer) clearTimeout(debounceTimer);
6052
- debounceTimer = setTimeout(applyFromCache, 150);
6053
- };
6054
- scheduleApply();
6055
- const observer = new MutationObserver(scheduleApply);
5103
+ applyFromCache();
5104
+ const observer = new MutationObserver(applyFromCache);
6056
5105
  observer.observe(document.body, { childList: true, subtree: true });
6057
- return () => {
6058
- observer.disconnect();
6059
- if (debounceTimer) clearTimeout(debounceTimer);
6060
- };
6061
- }, [subdomain, isEditMode, pathname]);
5106
+ return () => observer.disconnect();
5107
+ }, [subdomain, isEditMode]);
6062
5108
  useLayoutEffect(() => {
6063
5109
  const el = document.getElementById("ohw-loader");
6064
5110
  if (!el) return;
6065
5111
  const visible = Boolean(subdomain) && fetchState !== "done";
6066
5112
  el.style.display = visible ? "flex" : "none";
6067
5113
  }, [subdomain, fetchState]);
6068
- useEffect3(() => {
5114
+ useEffect2(() => {
6069
5115
  postToParent({ type: "ow:navigation", path: pathname });
6070
5116
  }, [pathname, postToParent]);
6071
- useEffect3(() => {
5117
+ useEffect2(() => {
6072
5118
  if (!isEditMode) return;
6073
5119
  const measure = () => {
6074
5120
  const h = document.body.scrollHeight;
@@ -6092,7 +5138,7 @@ function OhhwellsBridge() {
6092
5138
  window.removeEventListener("resize", handleResize);
6093
5139
  };
6094
5140
  }, [pathname, isEditMode, postToParent]);
6095
- useEffect3(() => {
5141
+ useEffect2(() => {
6096
5142
  if (!subdomainFromQuery || isEditMode) return;
6097
5143
  const handleClick = (e) => {
6098
5144
  const anchor = e.target.closest("a");
@@ -6108,7 +5154,7 @@ function OhhwellsBridge() {
6108
5154
  document.addEventListener("click", handleClick, true);
6109
5155
  return () => document.removeEventListener("click", handleClick, true);
6110
5156
  }, [subdomainFromQuery, isEditMode, router]);
6111
- useEffect3(() => {
5157
+ useEffect2(() => {
6112
5158
  if (!isEditMode) {
6113
5159
  editStylesRef.current?.base.remove();
6114
5160
  editStylesRef.current?.forceHover.remove();
@@ -6409,15 +5455,6 @@ function OhhwellsBridge() {
6409
5455
  textEditable.setAttribute("data-ohw-hovered", "");
6410
5456
  return;
6411
5457
  }
6412
- if (hoveredGapRef.current) {
6413
- if (hoveredImageRef.current) {
6414
- hoveredImageRef.current = null;
6415
- resumeAnimTracks();
6416
- postToParentRef.current({ type: "ow:image-unhover" });
6417
- }
6418
- document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
6419
- return;
6420
- }
6421
5458
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
6422
5459
  if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
6423
5460
  hoveredImageRef.current = imgEl;
@@ -6483,30 +5520,8 @@ function OhhwellsBridge() {
6483
5520
  }
6484
5521
  }
6485
5522
  };
6486
- const probeSectionGapAt = (clientX, clientY, fromParentViewport = false) => {
6487
- const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
6488
- const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
6489
- const ZONE = 20;
6490
- for (let i = 0; i < sections.length; i++) {
6491
- const a = sections[i];
6492
- const b = sections[i + 1] ?? null;
6493
- const boundaryY = a.getBoundingClientRect().bottom;
6494
- if (Math.abs(y - boundaryY) <= ZONE) {
6495
- const insertAfter = a.dataset.ohwSection ?? "";
6496
- const insertBefore = b?.dataset.ohwSection ?? null;
6497
- hoveredGapRef.current = { insertAfter, insertBefore };
6498
- setSectionGap({ insertAfter, insertBefore, y: boundaryY });
6499
- return;
6500
- }
6501
- }
6502
- if (hoveredGapRef.current) {
6503
- hoveredGapRef.current = null;
6504
- setSectionGap(null);
6505
- }
6506
- };
6507
5523
  const handleMouseMove = (e) => {
6508
5524
  const { clientX, clientY } = e;
6509
- probeSectionGapAt(clientX, clientY);
6510
5525
  probeImageAt(clientX, clientY);
6511
5526
  probeHoverCardsAt(clientX, clientY);
6512
5527
  };
@@ -6514,7 +5529,6 @@ function OhhwellsBridge() {
6514
5529
  if (e.data?.type !== "ow:pointer-sync") return;
6515
5530
  const { clientX, clientY } = e.data;
6516
5531
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
6517
- probeSectionGapAt(clientX, clientY);
6518
5532
  probeImageAt(clientX, clientY);
6519
5533
  probeHoverCardsAt(clientX, clientY);
6520
5534
  };
@@ -6672,12 +5686,7 @@ function OhhwellsBridge() {
6672
5686
  if (e.data?.type !== "ow:hydrate") return;
6673
5687
  const content = e.data.content;
6674
5688
  if (!content) return;
6675
- let sectionsJson = null;
6676
5689
  for (const [key, val] of Object.entries(content)) {
6677
- if (key === "__ohw_sections") {
6678
- sectionsJson = val;
6679
- continue;
6680
- }
6681
5690
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
6682
5691
  if (el.dataset.ohwEditable === "image") {
6683
5692
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6692,9 +5701,6 @@ function OhhwellsBridge() {
6692
5701
  });
6693
5702
  applyLinkByKey(key, val);
6694
5703
  }
6695
- if (sectionsJson) {
6696
- initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
6697
- }
6698
5704
  postToParentRef.current({ type: "ow:hydrate-done" });
6699
5705
  };
6700
5706
  window.addEventListener("message", handleHydrate);
@@ -6747,63 +5753,7 @@ function OhhwellsBridge() {
6747
5753
  };
6748
5754
  const handleSave = (e) => {
6749
5755
  if (e.data?.type !== "ow:save") return;
6750
- const nodes = collectEditableNodes();
6751
- const tracker = document.querySelector("[data-ohw-sections-tracker]");
6752
- if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
6753
- postToParentRef.current({ type: "ow:save-result", nodes });
6754
- };
6755
- const handleInsertSection = (e) => {
6756
- if (e.data?.type !== "ow:insert-section") return;
6757
- const { widgetType, insertAfter, insertBefore } = e.data;
6758
- if (widgetType !== "scheduling") return;
6759
- const inserted = mountSchedulingWidget(insertAfter, true, void 0, insertBefore ?? null);
6760
- if (inserted) {
6761
- const tracker = getSectionsTracker();
6762
- postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
6763
- const h = document.documentElement.scrollHeight;
6764
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
6765
- }
6766
- };
6767
- const handleSwitchSchedule = (e) => {
6768
- if (e.data?.type !== "ow:switch-schedule") return;
6769
- const scheduleId = e.data.scheduleId;
6770
- const insertAfter = e.data.insertAfter;
6771
- if (!scheduleId || !insertAfter) return;
6772
- const text = updateSectionScheduleId(insertAfter, scheduleId);
6773
- postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
6774
- };
6775
- const handleScheduleLinked = (e) => {
6776
- if (e.data?.type !== "ow:schedule-linked") return;
6777
- const scheduleId = e.data.scheduleId;
6778
- const insertAfter = e.data.insertAfter;
6779
- if (!scheduleId || !insertAfter) return;
6780
- const text = updateSectionScheduleId(insertAfter, scheduleId);
6781
- postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
6782
- };
6783
- const handleClearSchedulingWidget = (e) => {
6784
- if (e.data?.type !== "ow:clear-scheduling-widget") return;
6785
- const insertAfter = e.data.insertAfter;
6786
- if (!insertAfter) return;
6787
- const text = updateSectionScheduleId(insertAfter, null);
6788
- postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
6789
- };
6790
- const handleRemoveSchedulingSection = (e) => {
6791
- if (e.data?.type !== "ow:remove-scheduling-section") return;
6792
- document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => {
6793
- el.remove();
6794
- });
6795
- const tracker = getSectionsTracker();
6796
- let sections = [];
6797
- try {
6798
- sections = JSON.parse(tracker.textContent || "[]");
6799
- } catch {
6800
- }
6801
- const currentPath = window.location.pathname;
6802
- const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
6803
- tracker.textContent = JSON.stringify(updated);
6804
- postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
6805
- const h = document.documentElement.scrollHeight;
6806
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
5756
+ postToParentRef.current({ type: "ow:save-result", nodes: collectEditableNodes() });
6807
5757
  };
6808
5758
  const handleSelectionChange = () => {
6809
5759
  if (!activeElRef.current) return;
@@ -6908,11 +5858,6 @@ function OhhwellsBridge() {
6908
5858
  deactivateRef.current();
6909
5859
  };
6910
5860
  window.addEventListener("message", handleSave);
6911
- window.addEventListener("message", handleInsertSection);
6912
- window.addEventListener("message", handleSwitchSchedule);
6913
- window.addEventListener("message", handleScheduleLinked);
6914
- window.addEventListener("message", handleClearSchedulingWidget);
6915
- window.addEventListener("message", handleRemoveSchedulingSection);
6916
5861
  window.addEventListener("message", handleImageUrl);
6917
5862
  window.addEventListener("message", handleAnimLock);
6918
5863
  window.addEventListener("message", handleCanvasHeight);
@@ -6947,11 +5892,6 @@ function OhhwellsBridge() {
6947
5892
  document.removeEventListener("selectionchange", handleSelectionChange);
6948
5893
  window.removeEventListener("scroll", handleScroll, true);
6949
5894
  window.removeEventListener("message", handleSave);
6950
- window.removeEventListener("message", handleInsertSection);
6951
- window.removeEventListener("message", handleSwitchSchedule);
6952
- window.removeEventListener("message", handleScheduleLinked);
6953
- window.removeEventListener("message", handleClearSchedulingWidget);
6954
- window.removeEventListener("message", handleRemoveSchedulingSection);
6955
5895
  window.removeEventListener("message", handleImageUrl);
6956
5896
  window.removeEventListener("message", handleAnimLock);
6957
5897
  window.removeEventListener("message", handleCanvasHeight);
@@ -6966,7 +5906,7 @@ function OhhwellsBridge() {
6966
5906
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
6967
5907
  };
6968
5908
  }, [isEditMode, refreshStateRules]);
6969
- useEffect3(() => {
5909
+ useEffect2(() => {
6970
5910
  if (!isEditMode) return;
6971
5911
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
6972
5912
  el.removeAttribute("data-ohw-active-state");
@@ -6995,7 +5935,7 @@ function OhhwellsBridge() {
6995
5935
  clearTimeout(timer);
6996
5936
  };
6997
5937
  }, [pathname, isEditMode, refreshStateRules, postToParent]);
6998
- useEffect3(() => {
5938
+ useEffect2(() => {
6999
5939
  scrollToHashSectionWhenReady();
7000
5940
  const onHashChange = () => scrollToHashSectionWhenReady();
7001
5941
  window.addEventListener("hashchange", onHashChange);
@@ -7045,10 +5985,10 @@ function OhhwellsBridge() {
7045
5985
  const currentSections = sectionsByPath[pathname] ?? [];
7046
5986
  linkPopoverOpenRef.current = linkPopover !== null;
7047
5987
  return bridgeRoot ? createPortal(
7048
- /* @__PURE__ */ jsxs8(Fragment3, { children: [
7049
- toolbarRect && /* @__PURE__ */ jsxs8(Fragment3, { children: [
7050
- /* @__PURE__ */ jsx16(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
7051
- /* @__PURE__ */ jsx16(
5988
+ /* @__PURE__ */ jsxs6(Fragment2, { children: [
5989
+ toolbarRect && /* @__PURE__ */ jsxs6(Fragment2, { children: [
5990
+ /* @__PURE__ */ jsx13(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
5991
+ /* @__PURE__ */ jsx13(
7052
5992
  FloatingToolbar,
7053
5993
  {
7054
5994
  rect: toolbarRect,
@@ -7061,7 +6001,7 @@ function OhhwellsBridge() {
7061
6001
  }
7062
6002
  )
7063
6003
  ] }),
7064
- maxBadge && /* @__PURE__ */ jsxs8(
6004
+ maxBadge && /* @__PURE__ */ jsxs6(
7065
6005
  "div",
7066
6006
  {
7067
6007
  "data-ohw-max-badge": "",
@@ -7087,7 +6027,7 @@ function OhhwellsBridge() {
7087
6027
  ]
7088
6028
  }
7089
6029
  ),
7090
- toggleState && /* @__PURE__ */ jsx16(
6030
+ toggleState && /* @__PURE__ */ jsx13(
7091
6031
  StateToggle,
7092
6032
  {
7093
6033
  rect: toggleState.rect,
@@ -7096,7 +6036,7 @@ function OhhwellsBridge() {
7096
6036
  onStateChange: handleStateChange
7097
6037
  }
7098
6038
  ),
7099
- linkPopover ? /* @__PURE__ */ jsx16(
6039
+ linkPopover ? /* @__PURE__ */ jsx13(
7100
6040
  LinkPopover,
7101
6041
  {
7102
6042
  rect: linkPopover.rect,
@@ -7111,32 +6051,7 @@ function OhhwellsBridge() {
7111
6051
  onClose: closeLinkPopover,
7112
6052
  onSubmit: handleLinkPopoverSubmit
7113
6053
  }
7114
- ) : null,
7115
- sectionGap && /* @__PURE__ */ jsxs8(
7116
- "div",
7117
- {
7118
- "data-ohw-section-insert-line": "",
7119
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
7120
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
7121
- children: [
7122
- /* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
7123
- /* @__PURE__ */ jsx16(
7124
- Badge,
7125
- {
7126
- className: "px-8 py-1 bg-primary hover:bg-primary text-primary-foreground text-xs font-medium shrink-0 rounded-full cursor-pointer pointer-events-auto",
7127
- onClick: () => {
7128
- window.parent.postMessage(
7129
- { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
7130
- "*"
7131
- );
7132
- },
7133
- children: "Add Section"
7134
- }
7135
- ),
7136
- /* @__PURE__ */ jsx16("div", { className: "flex-1 bg-primary", style: { height: 3 } })
7137
- ]
7138
- }
7139
- )
6054
+ ) : null
7140
6055
  ] }),
7141
6056
  bridgeRoot
7142
6057
  ) : null;
@@ -7145,7 +6060,6 @@ export {
7145
6060
  LinkEditorPanel,
7146
6061
  LinkPopover,
7147
6062
  OhhwellsBridge,
7148
- SchedulingWidget,
7149
6063
  Toggle,
7150
6064
  ToggleGroup,
7151
6065
  ToggleGroupItem,