@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.cjs CHANGED
@@ -34,7 +34,6 @@ __export(index_exports, {
34
34
  LinkEditorPanel: () => LinkEditorPanel,
35
35
  LinkPopover: () => LinkPopover,
36
36
  OhhwellsBridge: () => OhhwellsBridge,
37
- SchedulingWidget: () => SchedulingWidget,
38
37
  Toggle: () => Toggle,
39
38
  ToggleGroup: () => ToggleGroup,
40
39
  ToggleGroupItem: () => ToggleGroupItem,
@@ -49,752 +48,10 @@ __export(index_exports, {
49
48
  module.exports = __toCommonJS(index_exports);
50
49
 
51
50
  // src/OhhwellsBridge.tsx
52
- var import_react5 = __toESM(require("react"), 1);
53
- var import_client = require("react-dom/client");
54
- var import_react_dom = require("react-dom");
55
-
56
- // src/ui/SchedulingWidget.tsx
57
- var import_react2 = require("react");
58
-
59
- // src/ui/EmailCaptureModal.tsx
60
- var import_react = require("react");
61
- var import_radix_ui = require("radix-ui");
62
- var import_jsx_runtime = require("react/jsx-runtime");
63
- function Spinner() {
64
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
65
- "svg",
66
- {
67
- width: "32",
68
- height: "32",
69
- viewBox: "0 0 32 32",
70
- fill: "none",
71
- style: { animation: "ohw-spin 0.75s linear infinite" },
72
- children: [
73
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `@keyframes ohw-spin { to { transform: rotate(360deg); } }` }),
74
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "16", cy: "16", r: "12", stroke: "#e5e7eb", strokeWidth: "3" }),
75
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
76
- "path",
77
- {
78
- d: "M16 4a12 12 0 0 1 12 12",
79
- stroke: "#3b82f6",
80
- strokeWidth: "3",
81
- strokeLinecap: "round"
82
- }
83
- )
84
- ]
85
- }
86
- );
87
- }
88
- function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
89
- const [email, setEmail] = (0, import_react.useState)("");
90
- const [submittedEmail, setSubmittedEmail] = (0, import_react.useState)("");
91
- const [loading, setLoading] = (0, import_react.useState)(false);
92
- const [success, setSuccess] = (0, import_react.useState)(false);
93
- const [error, setError] = (0, import_react.useState)(null);
94
- const isBook = title === "Confirm your spot";
95
- const handleSubmit = async () => {
96
- if (!email.trim()) return;
97
- setLoading(true);
98
- setError(null);
99
- try {
100
- await onSubmit(email.trim());
101
- setSubmittedEmail(email.trim());
102
- setSuccess(true);
103
- } catch (e) {
104
- setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
105
- } finally {
106
- setLoading(false);
107
- }
108
- };
109
- const handleKeyDown = (e) => {
110
- if (e.key === "Enter" && !loading) handleSubmit();
111
- };
112
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_radix_ui.Dialog.Root, { open: true, onOpenChange: (open) => {
113
- if (!open) onClose();
114
- }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_radix_ui.Dialog.Portal, { children: [
115
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
116
- import_radix_ui.Dialog.Overlay,
117
- {
118
- className: "fixed inset-0 z-50",
119
- style: { background: "rgba(0,0,0,0.45)" }
120
- }
121
- ),
122
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
123
- import_radix_ui.Dialog.Content,
124
- {
125
- 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",
126
- style: { maxWidth: 400 },
127
- children: [
128
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-start justify-between gap-3 p-6", children: [
129
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1", children: [
130
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
131
- import_radix_ui.Dialog.Title,
132
- {
133
- className: "font-body text-xl font-bold m-0 leading-tight",
134
- style: { color: "var(--color-dark,#200C02)" },
135
- children: success ? isBook ? "Reservation confirmed!" : "You're on the waitlist!" : title
136
- }
137
- ),
138
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
139
- import_radix_ui.Dialog.Description,
140
- {
141
- className: "font-body text-sm m-0",
142
- style: { color: "#6B7280" },
143
- children: success ? isBook ? `A confirmation email has been sent to ${submittedEmail}.` : "We'll let you know via email if someone cancels." : subtitle
144
- }
145
- )
146
- ] }),
147
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
148
- import_radix_ui.Dialog.Close,
149
- {
150
- 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",
151
- style: { color: "#6B7280" },
152
- "aria-label": "Close",
153
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
154
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
155
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
156
- ] })
157
- }
158
- )
159
- ] }),
160
- !success && (loading ? (
161
- /* Loading state — full body replaced */
162
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col items-center justify-center gap-3 px-7 py-12", children: [
163
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}),
164
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-body text-sm m-0", style: { color: "#6B7280" }, children: "Please wait" })
165
- ] })
166
- ) : (
167
- /* Email form */
168
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "px-7 pt-2 pb-6", children: [
169
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1.5 mb-8", children: [
170
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "font-body text-sm font-medium", style: { color: "var(--color-dark,#200C02)" }, children: "Email" }),
171
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
172
- "input",
173
- {
174
- type: "email",
175
- value: email,
176
- onChange: (e) => setEmail(e.target.value),
177
- onKeyDown: handleKeyDown,
178
- placeholder: "hello@gmail.com",
179
- autoFocus: true,
180
- className: "w-full h-10 px-3 rounded-lg border font-body text-sm outline-none box-border",
181
- style: {
182
- borderColor: "#E7E5E4",
183
- background: "#fff",
184
- color: "var(--color-dark,#200C02)"
185
- }
186
- }
187
- ),
188
- error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-body text-xs text-red-500 m-0", children: error })
189
- ] }),
190
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
191
- "button",
192
- {
193
- onClick: handleSubmit,
194
- disabled: !email.trim(),
195
- className: "h-9 px-5 rounded-lg font-body text-sm font-semibold cursor-pointer border-none disabled:opacity-50 disabled:cursor-not-allowed",
196
- style: {
197
- background: "var(--color-primary,#3D312B)",
198
- color: "var(--color-light,#FEFFF0)"
199
- },
200
- children: "Send"
201
- }
202
- ) })
203
- ] })
204
- ))
205
- ]
206
- }
207
- )
208
- ] }) });
209
- }
210
-
211
- // src/ui/SchedulingWidget.tsx
212
- var import_jsx_runtime2 = require("react/jsx-runtime");
213
- var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
214
- var DAY_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
215
- function getApiDomain() {
216
- const h = window.location.hostname;
217
- for (const base of ["ohhwells.site", "theflowops-staging.com", "theflowops.com"]) {
218
- if (h.endsWith(`.${base}`)) return h.slice(0, -(base.length + 1));
219
- }
220
- return h;
221
- }
222
- function classRunsOnDate(cls, date, type) {
223
- if (type === "FIXED") {
224
- const d = String(date.getDate()).padStart(2, "0");
225
- const m = String(date.getMonth() + 1).padStart(2, "0");
226
- return cls.fixedDates?.some((fd) => fd.date === `${d}-${m}-${date.getFullYear()}`) ?? false;
227
- }
228
- return cls.weekdays?.some((w) => w.weekday === date.getDay()) ?? false;
229
- }
230
- function formatClassTime(cls) {
231
- let h = parseInt(cls.startTime.hour, 10);
232
- const m = parseInt(cls.startTime.minutes, 10);
233
- if (cls.startTime.time === "PM" && h !== 12) h += 12;
234
- if (cls.startTime.time === "AM" && h === 12) h = 0;
235
- const endTotal = h * 60 + m + cls.duration;
236
- const eh = Math.floor(endTotal / 60) % 24;
237
- const em = endTotal % 60;
238
- return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
239
- }
240
- function getBookingsOnDate(cls, date) {
241
- if (!cls.bookings?.length) return 0;
242
- const ds = date.toISOString().split("T")[0];
243
- return cls.bookings.filter((b) => {
244
- try {
245
- return new Date(b.classDate).toISOString().split("T")[0] === ds;
246
- } catch {
247
- return false;
248
- }
249
- }).length;
250
- }
251
- function buildTimezoneLabel(tz) {
252
- try {
253
- const now = /* @__PURE__ */ new Date();
254
- const timeStr = new Intl.DateTimeFormat("en-US", {
255
- hour: "2-digit",
256
- minute: "2-digit",
257
- hour12: false,
258
- timeZone: tz
259
- }).format(now);
260
- const offsetPart = new Intl.DateTimeFormat("en-US", { timeZoneName: "short", timeZone: tz }).formatToParts(now).find((p) => p.type === "timeZoneName")?.value ?? "";
261
- const city = tz.split("/").pop()?.replace(/_/g, " ") ?? tz;
262
- return `${timeStr} (${offsetPart}) ${city} time`;
263
- } catch {
264
- return tz;
265
- }
266
- }
267
- function EmptyState({ inEditor }) {
268
- const handleAddSchedule = () => {
269
- if (inEditor) {
270
- window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
271
- }
272
- };
273
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("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: [
274
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-10 h-10 bg-[#F5F5F4] rounded-[10px] flex items-center justify-center shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
275
- "svg",
276
- {
277
- width: "20",
278
- height: "20",
279
- viewBox: "0 0 24 24",
280
- fill: "none",
281
- stroke: "var(--color-accent, #A89B83)",
282
- strokeWidth: "1.5",
283
- strokeLinecap: "round",
284
- strokeLinejoin: "round",
285
- children: [
286
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
287
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
288
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
289
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "3", y1: "10", x2: "21", y2: "10" }),
290
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "14", x2: "12", y2: "18" }),
291
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "10", y1: "16", x2: "14", y2: "16" })
292
- ]
293
- }
294
- ) }),
295
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-2", children: [
296
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-lg font-medium text-center text-[#0A0A0A] m-0", children: "No schedule yet" }),
297
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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." })
298
- ] }),
299
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
300
- "button",
301
- {
302
- onClick: handleAddSchedule,
303
- 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",
304
- children: "Add Schedule"
305
- }
306
- )
307
- ] });
308
- }
309
- function LoadingSkeleton() {
310
- const shimmer = {
311
- background: "linear-gradient(90deg,#f0f0f0 25%,#e8e8e8 50%,#f0f0f0 75%)",
312
- backgroundSize: "200% 100%",
313
- animation: "ohw-shimmer 1.5s infinite"
314
- };
315
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col gap-10", children: [
316
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `@keyframes ohw-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}` }),
317
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex h-[70px] items-stretch", children: Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col justify-between items-center px-1", children: [
318
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "32px", height: "14px", borderRadius: "4px" } }),
319
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "40px", height: "40px", borderRadius: "50%" } })
320
- ] }, i)) }),
321
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
322
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-4 py-4 sm:hidden", children: [
323
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-2 shrink-0", children: [
324
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "100px", height: "16px", borderRadius: "4px" } }),
325
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "52px", height: "20px", borderRadius: "999px" } }),
326
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "60px", height: "14px", borderRadius: "4px" } })
327
- ] }),
328
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-1 flex-col gap-2", children: [
329
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "75%", height: "16px", borderRadius: "4px" } }),
330
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "50%", height: "14px", borderRadius: "4px" } }),
331
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "100%", height: "44px", borderRadius: "8px" } })
332
- ] })
333
- ] }),
334
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hidden sm:flex items-center gap-[60px] py-4", children: [
335
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "120px", height: "16px", borderRadius: "4px", flexShrink: 0 } }),
336
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2", children: [
337
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "55%", height: "16px", borderRadius: "4px" } }),
338
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "38%", height: "14px", borderRadius: "4px" } })
339
- ] }),
340
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "56px", height: "32px", borderRadius: "4px", flexShrink: 0 } }),
341
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "200px", height: "44px", borderRadius: "8px", flexShrink: 0 } })
342
- ] }),
343
- i < 2 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-px bg-black/20" })
344
- ] }, i))
345
- ] });
346
- }
347
- function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
348
- const todayMs = (0, import_react2.useMemo)(() => {
349
- const d = /* @__PURE__ */ new Date();
350
- d.setHours(0, 0, 0, 0);
351
- return d.getTime();
352
- }, []);
353
- const scrollRef = (0, import_react2.useRef)(null);
354
- const [canScrollLeft, setCanScrollLeft] = (0, import_react2.useState)(false);
355
- const [canScrollRight, setCanScrollRight] = (0, import_react2.useState)(false);
356
- const updateScrollState = () => {
357
- const el = scrollRef.current;
358
- if (!el) return;
359
- setCanScrollLeft(el.scrollLeft > 0);
360
- setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
361
- };
362
- (0, import_react2.useEffect)(() => {
363
- const el = scrollRef.current;
364
- if (!el) return;
365
- updateScrollState();
366
- el.addEventListener("scroll", updateScrollState);
367
- const ro = new ResizeObserver(updateScrollState);
368
- ro.observe(el);
369
- return () => {
370
- el.removeEventListener("scroll", updateScrollState);
371
- ro.disconnect();
372
- };
373
- }, [dates]);
374
- const scroll = (dir) => {
375
- scrollRef.current?.scrollBy({ left: dir === "left" ? -216 : 216, behavior: "smooth" });
376
- };
377
- const isDragging = (0, import_react2.useRef)(false);
378
- const dragStartX = (0, import_react2.useRef)(0);
379
- const dragStartScrollLeft = (0, import_react2.useRef)(0);
380
- const onDragStart = (e) => {
381
- if (!scrollRef.current) return;
382
- isDragging.current = true;
383
- dragStartX.current = e.clientX;
384
- dragStartScrollLeft.current = scrollRef.current.scrollLeft;
385
- scrollRef.current.style.cursor = "grabbing";
386
- scrollRef.current.style.userSelect = "none";
387
- };
388
- const onDragMove = (e) => {
389
- if (!isDragging.current || !scrollRef.current) return;
390
- const delta = e.clientX - dragStartX.current;
391
- scrollRef.current.scrollLeft = dragStartScrollLeft.current - delta;
392
- };
393
- const onDragEnd = () => {
394
- if (!scrollRef.current) return;
395
- isDragging.current = false;
396
- scrollRef.current.style.cursor = "grab";
397
- scrollRef.current.style.userSelect = "";
398
- };
399
- const datesWithClasses = (0, import_react2.useMemo)(
400
- () => new Set(dates.map(
401
- (d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
402
- ).filter((i) => i >= 0)),
403
- [dates, schedule]
404
- );
405
- const selectedDate = dates[selectedIdx];
406
- const selectedClasses = (0, import_react2.useMemo)(
407
- () => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
408
- [schedule, selectedDate]
409
- );
410
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col gap-10", children: [
411
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "relative w-full", children: [
412
- canScrollLeft && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
413
- "button",
414
- {
415
- onClick: () => scroll("left"),
416
- 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",
417
- style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
418
- "aria-label": "Scroll left",
419
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: "15 18 9 12 15 6" }) })
420
- }
421
- ),
422
- canScrollRight && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
423
- "button",
424
- {
425
- onClick: () => scroll("right"),
426
- 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",
427
- style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
428
- "aria-label": "Scroll right",
429
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: "9 18 15 12 9 6" }) })
430
- }
431
- ),
432
- canScrollLeft && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
433
- "div",
434
- {
435
- className: "sm:hidden absolute left-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
436
- style: { background: "linear-gradient(to right, var(--color-light,#FEFFF0), transparent)" }
437
- }
438
- ),
439
- canScrollRight && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
440
- "div",
441
- {
442
- className: "sm:hidden absolute right-0 top-0 bottom-0 w-10 z-10 pointer-events-none",
443
- style: { background: "linear-gradient(to left, var(--color-light,#FEFFF0), transparent)" }
444
- }
445
- ),
446
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
447
- "div",
448
- {
449
- ref: scrollRef,
450
- className: "overflow-x-auto w-full",
451
- style: { scrollbarWidth: "none", msOverflowStyle: "none", touchAction: "pan-x", cursor: "grab" },
452
- onMouseDown: onDragStart,
453
- onMouseMove: onDragMove,
454
- onMouseUp: onDragEnd,
455
- onMouseLeave: onDragEnd,
456
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex", style: { width: `max(100%, ${dates.length * 60}px)` }, children: dates.map((date, i) => {
457
- const isToday = date.getTime() === todayMs;
458
- const isSelected = i === selectedIdx;
459
- const clickable = datesWithClasses.has(i);
460
- const label = isToday ? "TODAY" : DAY_ABBR[date.getDay()];
461
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
462
- "div",
463
- {
464
- onClick: () => clickable && onSelectDate(i),
465
- className: `flex-1 h-[70px] flex flex-col justify-between items-center ${clickable ? "cursor-pointer opacity-100" : "cursor-default opacity-50"}`,
466
- children: [
467
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-center text-(--color-dark,#200C02)", children: label }),
468
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
469
- "div",
470
- {
471
- className: "w-10 h-10 rounded-full flex items-center justify-center",
472
- style: { background: isSelected ? "var(--color-primary, #3D312B)" : "transparent" },
473
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
474
- "span",
475
- {
476
- className: "font-body text-xl font-bold text-center tracking-display-tight",
477
- style: { color: isSelected ? "var(--color-light, #FEFFF0)" : "var(--color-dark, #200C02)" },
478
- children: date.getDate()
479
- }
480
- )
481
- }
482
- )
483
- ]
484
- },
485
- i
486
- );
487
- }) })
488
- }
489
- )
490
- ] }),
491
- selectedClasses.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-base text-(--color-accent,#A89B83) text-center py-8 m-0", children: "No classes scheduled for this day." }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex flex-col", children: selectedClasses.map((cls, i) => {
492
- const booked = getBookingsOnDate(cls, selectedDate);
493
- const available = cls.maxParticipants - booked;
494
- const isFull = available <= 0;
495
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
496
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-4 py-4 sm:items-center sm:gap-[60px] box-border", children: [
497
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-2 shrink-0 sm:contents", children: [
498
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:w-[120px] sm:shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }) }),
499
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
500
- "span",
501
- {
502
- className: "inline-flex items-center px-2 py-0.5 rounded-full border text-xs font-medium sm:hidden",
503
- style: { borderColor: "#0885FE", color: "#0885FE" },
504
- children: "GROUP"
505
- }
506
- ),
507
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
508
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
509
- available,
510
- "/",
511
- cls.maxParticipants
512
- ] }),
513
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
514
- ] }) })
515
- ] }),
516
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
517
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
518
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
519
- cls.hostName && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
520
- ] }),
521
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
522
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
523
- available,
524
- "/",
525
- cls.maxParticipants
526
- ] }),
527
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
528
- ] }) }),
529
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
530
- "button",
531
- {
532
- 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",
533
- style: isFull ? {
534
- background: "transparent",
535
- border: "1px solid var(--color-dark, #200C02)",
536
- color: "var(--color-dark, #200C02)"
537
- } : {
538
- background: "var(--color-primary, #3D312B)",
539
- border: "none",
540
- color: "var(--color-light, #FEFFF0)"
541
- },
542
- onClick: () => {
543
- if (!cls.id) return;
544
- onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
545
- },
546
- children: isFull ? "Join Waitlist" : "Book Now"
547
- }
548
- )
549
- ] })
550
- ] }),
551
- i < selectedClasses.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-px bg-black/20" })
552
- ] }, cls.id ?? i);
553
- }) })
554
- ] });
555
- }
556
- function CalendarFoldIcon() {
557
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
558
- "svg",
559
- {
560
- width: "16",
561
- height: "16",
562
- viewBox: "0 0 24 24",
563
- fill: "none",
564
- stroke: "currentColor",
565
- strokeWidth: "2",
566
- strokeLinecap: "round",
567
- strokeLinejoin: "round",
568
- style: { flexShrink: 0 },
569
- children: [
570
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 2v4" }),
571
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 2v4" }),
572
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 17V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11Z" }),
573
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 10h18" }),
574
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M15 22v-4a2 2 0 0 1 2-2h4" })
575
- ]
576
- }
577
- );
578
- }
579
- function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAfter }) {
580
- const [schedule, setSchedule] = (0, import_react2.useState)(null);
581
- const [loading, setLoading] = (0, import_react2.useState)(true);
582
- const [inEditor, setInEditor] = (0, import_react2.useState)(false);
583
- const [isHovered, setIsHovered] = (0, import_react2.useState)(false);
584
- const [modalState, setModalState] = (0, import_react2.useState)(null);
585
- const switchScheduleIdRef = (0, import_react2.useRef)(null);
586
- const dates = (0, import_react2.useMemo)(() => {
587
- const today = /* @__PURE__ */ new Date();
588
- today.setHours(0, 0, 0, 0);
589
- return Array.from({ length: 14 }, (_, i) => {
590
- const d = new Date(today);
591
- d.setDate(today.getDate() + i);
592
- return d;
593
- });
594
- }, []);
595
- const [selectedIdx, setSelectedIdx] = (0, import_react2.useState)(0);
596
- (0, import_react2.useEffect)(() => {
597
- if (!schedule?.classes) return;
598
- for (let i = 0; i < dates.length; i++) {
599
- if (schedule.classes.some((c) => classRunsOnDate(c, dates[i], schedule.type))) {
600
- setSelectedIdx(i);
601
- return;
602
- }
603
- }
604
- }, [schedule, dates]);
605
- (0, import_react2.useEffect)(() => {
606
- if (typeof window === "undefined") return;
607
- const isInEditor = window.self !== window.top;
608
- setInEditor(isInEditor);
609
- if (isInEditor) {
610
- const startEmpty = initialScheduleId === null;
611
- if (startEmpty) setLoading(false);
612
- let initialized = startEmpty;
613
- const timer = !startEmpty ? setTimeout(() => {
614
- if (!initialized) {
615
- initialized = true;
616
- setLoading(false);
617
- }
618
- }, 5e3) : null;
619
- const handler = (e) => {
620
- if (e.data?.type === "ow:clear-scheduling-widget") {
621
- if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
622
- setSchedule(null);
623
- setLoading(false);
624
- return;
625
- }
626
- if (e.data?.type === "ow:switch-schedule") {
627
- if (!e.data.insertAfter || e.data.insertAfter !== insertAfter) return;
628
- switchScheduleIdRef.current = e.data.scheduleId ?? null;
629
- initialized = false;
630
- setLoading(true);
631
- window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
632
- return;
633
- }
634
- if (e.data?.type !== "ow:user-schedules-response") return;
635
- if (e.data.insertAfter && e.data.insertAfter !== insertAfter) return;
636
- if (initialized && switchScheduleIdRef.current === null) return;
637
- initialized = true;
638
- if (timer) clearTimeout(timer);
639
- const schedules = e.data.schedules ?? [];
640
- const isSwitching = switchScheduleIdRef.current !== null;
641
- 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;
642
- switchScheduleIdRef.current = null;
643
- setSchedule(target);
644
- setLoading(false);
645
- if (notifyOnConnect && target && !isSwitching) {
646
- window.parent.postMessage({
647
- type: "ow:schedule-connected",
648
- schedule: { id: target.id, name: target.name },
649
- insertAfter
650
- }, "*");
651
- window.postMessage({ type: "ow:schedule-linked", scheduleId: target.id, insertAfter }, "*");
652
- }
653
- };
654
- window.addEventListener("message", handler);
655
- if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules", insertAfter }, "*");
656
- return () => {
657
- window.removeEventListener("message", handler);
658
- if (timer) clearTimeout(timer);
659
- };
660
- }
661
- if (initialScheduleId === null) {
662
- setLoading(false);
663
- return;
664
- }
665
- ;
666
- (async () => {
667
- try {
668
- if (initialScheduleId) {
669
- const res = await fetch(`${API_URL}/api/schedule/id/${initialScheduleId}`);
670
- const data = await res.json();
671
- setSchedule(data?.id ? data : null);
672
- } else {
673
- const domain = getApiDomain();
674
- const sitemapRes = await fetch(`${API_URL}/api/schedule/sitemap/${domain}`);
675
- const slugs = await sitemapRes.json();
676
- if (!Array.isArray(slugs) || !slugs.length) {
677
- setLoading(false);
678
- return;
679
- }
680
- const { slug } = [...slugs].sort(
681
- (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
682
- )[0];
683
- const res = await fetch(`${API_URL}/api/schedule/${domain}/${slug}`);
684
- const data = await res.json();
685
- setSchedule(data?.id ? data : null);
686
- }
687
- } catch {
688
- }
689
- setLoading(false);
690
- })();
691
- }, []);
692
- const timezoneLabel = schedule?.timezone ? (() => {
693
- try {
694
- return buildTimezoneLabel(schedule.timezone);
695
- } catch {
696
- return void 0;
697
- }
698
- })() : void 0;
699
- const handleModalSubmit = async (email) => {
700
- if (!modalState) return;
701
- const { mode, classId, classDate } = modalState;
702
- const endpoint = mode === "book" ? `${API_URL}/api/schedule/class/${classId}/booking` : `${API_URL}/api/schedule/class/${classId}/waitlist`;
703
- const res = await fetch(endpoint, {
704
- method: "POST",
705
- headers: { "Content-Type": "application/json" },
706
- body: JSON.stringify({ classDate: classDate.toISOString(), email })
707
- });
708
- if (!res.ok) {
709
- const data = await res.json().catch(() => ({}));
710
- throw new Error(data?.message ?? "Something went wrong. Please try again.");
711
- }
712
- };
713
- const handleReplaceSchedule = () => {
714
- setIsHovered(false);
715
- if (schedule) {
716
- window.parent.postMessage({
717
- type: "ow:schedule-connected",
718
- schedule: { id: schedule.id, name: schedule.name },
719
- insertAfter
720
- }, "*");
721
- } else {
722
- window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
723
- }
724
- };
725
- const sectionId = `scheduling-${insertAfter}`;
726
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
727
- "section",
728
- {
729
- "data-ohw-section": sectionId,
730
- "data-ohw-scheduling-anchor": insertAfter,
731
- className: "w-full box-border py-10 px-5 sm:py-20 sm:px-16 font-body bg-(--color-light,#FEFFF0) relative",
732
- onMouseEnter: () => inEditor && setIsHovered(true),
733
- onMouseLeave: () => setIsHovered(false),
734
- children: [
735
- inEditor && isHovered && !!schedule && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
736
- "div",
737
- {
738
- className: "absolute inset-0 z-10 pointer-events-auto cursor-pointer",
739
- onClick: handleReplaceSchedule,
740
- children: [
741
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute inset-0", style: { background: "#0885FE", opacity: 0.18 } }),
742
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute inset-0", style: { boxShadow: "inset 0 0 0 1.5px #0885FE" } }),
743
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
744
- "div",
745
- {
746
- className: "bg-white border border-[#E7E5E4] rounded-md px-3 py-1.5 flex items-center gap-1.5",
747
- style: {
748
- fontSize: 14,
749
- lineHeight: "24px",
750
- fontFamily: "'Figtree', system-ui, sans-serif",
751
- fontWeight: 500,
752
- color: "#0C0A09"
753
- },
754
- children: [
755
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CalendarFoldIcon, {}),
756
- "Replace schedule"
757
- ]
758
- }
759
- ) })
760
- ]
761
- }
762
- ),
763
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
764
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
765
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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" }),
766
- schedule?.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
767
- ] }),
768
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
769
- timezoneLabel && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
770
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-full p-0 sm:p-10 box-border", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EmptyState, { inEditor }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
771
- ScheduleView,
772
- {
773
- schedule,
774
- dates,
775
- selectedIdx,
776
- onSelectDate: setSelectedIdx,
777
- onOpenModal: inEditor ? void 0 : (mode, classId, classDate) => setModalState({ mode, classId, classDate })
778
- }
779
- ) })
780
- ] })
781
- ] }),
782
- modalState && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
783
- EmailCaptureModal,
784
- {
785
- title: modalState.mode === "book" ? "Confirm your spot" : "Leave your email",
786
- subtitle: modalState.mode === "book" ? "We'll send your booking confirmation here." : "We'll let you know when spots open up!",
787
- onSubmit: handleModalSubmit,
788
- onClose: () => setModalState(null)
789
- }
790
- )
791
- ]
792
- }
793
- );
794
- }
51
+ var import_react3 = __toESM(require("react"), 1);
795
52
 
796
53
  // src/ui/toggle-group.tsx
797
- var React2 = __toESM(require("react"), 1);
54
+ var React = __toESM(require("react"), 1);
798
55
 
799
56
  // node_modules/clsx/dist/clsx.mjs
800
57
  function r(e) {
@@ -4113,8 +3370,8 @@ var cva = (base, config) => (props) => {
4113
3370
  };
4114
3371
 
4115
3372
  // src/ui/toggle.tsx
4116
- var import_radix_ui2 = require("radix-ui");
4117
- var import_jsx_runtime3 = require("react/jsx-runtime");
3373
+ var import_radix_ui = require("radix-ui");
3374
+ var import_jsx_runtime = require("react/jsx-runtime");
4118
3375
  var toggleVariants = cva(
4119
3376
  "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",
4120
3377
  {
@@ -4141,8 +3398,8 @@ function Toggle({
4141
3398
  size,
4142
3399
  ...props
4143
3400
  }) {
4144
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4145
- import_radix_ui2.Toggle.Root,
3401
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3402
+ import_radix_ui.Toggle.Root,
4146
3403
  {
4147
3404
  "data-slot": "toggle",
4148
3405
  className: cn(toggleVariants({ variant, size, className })),
@@ -4152,8 +3409,8 @@ function Toggle({
4152
3409
  }
4153
3410
 
4154
3411
  // src/ui/toggle-group.tsx
4155
- var import_jsx_runtime4 = require("react/jsx-runtime");
4156
- var ToggleGroupContext = React2.createContext({
3412
+ var import_jsx_runtime2 = require("react/jsx-runtime");
3413
+ var ToggleGroupContext = React.createContext({
4157
3414
  value: "",
4158
3415
  onValueChange: () => {
4159
3416
  }
@@ -4165,13 +3422,13 @@ function ToggleGroup({
4165
3422
  children,
4166
3423
  ...props
4167
3424
  }) {
4168
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3425
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
4169
3426
  "div",
4170
3427
  {
4171
3428
  role: "group",
4172
3429
  className: cn("flex items-center gap-1 p-1 bg-muted rounded-md", className),
4173
3430
  ...props,
4174
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
3431
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
4175
3432
  }
4176
3433
  );
4177
3434
  }
@@ -4181,8 +3438,8 @@ function ToggleGroupItem({
4181
3438
  children,
4182
3439
  ...props
4183
3440
  }) {
4184
- const { value: groupValue, onValueChange } = React2.useContext(ToggleGroupContext);
4185
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3441
+ const { value: groupValue, onValueChange } = React.useContext(ToggleGroupContext);
3442
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
4186
3443
  Toggle,
4187
3444
  {
4188
3445
  pressed: groupValue === value,
@@ -4202,7 +3459,7 @@ function ToggleGroupItem({
4202
3459
  }
4203
3460
 
4204
3461
  // src/OhhwellsBridge.tsx
4205
- var import_react_dom2 = require("react-dom");
3462
+ var import_react_dom = require("react-dom");
4206
3463
  var import_navigation = require("next/navigation");
4207
3464
 
4208
3465
  // src/lib/session-search.ts
@@ -4496,9 +3753,9 @@ function calcPopoverPos(rect, parentScroll, approxW = 483, approxH = 320) {
4496
3753
  }
4497
3754
 
4498
3755
  // src/ui/button.tsx
4499
- var React3 = __toESM(require("react"), 1);
4500
- var import_radix_ui3 = require("radix-ui");
4501
- var import_jsx_runtime5 = require("react/jsx-runtime");
3756
+ var React2 = __toESM(require("react"), 1);
3757
+ var import_radix_ui2 = require("radix-ui");
3758
+ var import_jsx_runtime3 = require("react/jsx-runtime");
4502
3759
  var buttonVariants = cva(
4503
3760
  "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",
4504
3761
  {
@@ -4519,10 +3776,10 @@ var buttonVariants = cva(
4519
3776
  }
4520
3777
  }
4521
3778
  );
4522
- var Button = React3.forwardRef(
3779
+ var Button = React2.forwardRef(
4523
3780
  ({ className, variant, size, asChild = false, ...props }, ref) => {
4524
- const Comp = asChild ? import_radix_ui3.Slot.Root : "button";
4525
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3781
+ const Comp = asChild ? import_radix_ui2.Slot.Root : "button";
3782
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4526
3783
  Comp,
4527
3784
  {
4528
3785
  ref,
@@ -4536,75 +3793,75 @@ var Button = React3.forwardRef(
4536
3793
  Button.displayName = "Button";
4537
3794
 
4538
3795
  // src/ui/icons.tsx
4539
- var import_jsx_runtime6 = require("react/jsx-runtime");
3796
+ var import_jsx_runtime4 = require("react/jsx-runtime");
4540
3797
  function IconX({ className, ...props }) {
4541
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4542
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M18 6 6 18" }),
4543
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m6 6 12 12" })
3798
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
3799
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M18 6 6 18" }),
3800
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "m6 6 12 12" })
4544
3801
  ] });
4545
3802
  }
4546
3803
  function IconChevronDown({ className, ...props }) {
4547
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m6 9 6 6 6-6" }) });
3804
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "m6 9 6 6 6-6" }) });
4548
3805
  }
4549
3806
  function IconFile({ className, ...props }) {
4550
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4551
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
4552
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
3807
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
3808
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
3809
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
4553
3810
  ] });
4554
3811
  }
4555
3812
  function IconInfo({ className, ...props }) {
4556
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4557
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
4558
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 16v-4" }),
4559
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 8h.01" })
3813
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
3814
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
3815
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 16v-4" }),
3816
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 8h.01" })
4560
3817
  ] });
4561
3818
  }
4562
3819
  function IconArrowRight({ className, ...props }) {
4563
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4564
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5 12h14" }),
4565
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m12 5 7 7-7 7" })
3820
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
3821
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M5 12h14" }),
3822
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "m12 5 7 7-7 7" })
4566
3823
  ] });
4567
3824
  }
4568
3825
  function IconSection({ className, ...props }) {
4569
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
4570
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
4571
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
4572
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
3826
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
3827
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
3828
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
3829
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
4573
3830
  ] });
4574
3831
  }
4575
3832
 
4576
3833
  // src/ui/link-modal/DestinationBreadcrumb.tsx
4577
- var import_jsx_runtime7 = require("react/jsx-runtime");
3834
+ var import_jsx_runtime5 = require("react/jsx-runtime");
4578
3835
  function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
4579
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
4580
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
4581
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-3", children: [
4582
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2", children: [
4583
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4584
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
3836
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
3837
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
3838
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-3", children: [
3839
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", children: [
3840
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
3841
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
4585
3842
  ] }),
4586
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
4587
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
4588
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4589
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
3843
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
3844
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
3845
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
3846
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
4590
3847
  ] })
4591
3848
  ] })
4592
3849
  ] });
4593
3850
  }
4594
3851
 
4595
3852
  // src/ui/link-modal/SectionTreeItem.tsx
4596
- var import_jsx_runtime8 = require("react/jsx-runtime");
3853
+ var import_jsx_runtime6 = require("react/jsx-runtime");
4597
3854
  function SectionTreeItem({ section, onSelect, selected }) {
4598
3855
  const interactive = Boolean(onSelect);
4599
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
4600
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3856
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
3857
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4601
3858
  "div",
4602
3859
  {
4603
3860
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
4604
3861
  "aria-hidden": true
4605
3862
  }
4606
3863
  ),
4607
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3864
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
4608
3865
  "div",
4609
3866
  {
4610
3867
  role: interactive ? "button" : void 0,
@@ -4622,8 +3879,8 @@ function SectionTreeItem({ section, onSelect, selected }) {
4622
3879
  interactive && selected && "border-primary"
4623
3880
  ),
4624
3881
  children: [
4625
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
4626
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
3882
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
3883
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
4627
3884
  ]
4628
3885
  }
4629
3886
  )
@@ -4631,23 +3888,23 @@ function SectionTreeItem({ section, onSelect, selected }) {
4631
3888
  }
4632
3889
  function SectionPickerList({ sections, onSelect }) {
4633
3890
  if (sections.length === 0) {
4634
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
3891
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
4635
3892
  }
4636
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-col gap-1", children: [
4637
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
4638
- sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SectionTreeItem, { section, onSelect }, section.id))
3893
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col gap-1", children: [
3894
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
3895
+ sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionTreeItem, { section, onSelect }, section.id))
4639
3896
  ] });
4640
3897
  }
4641
3898
 
4642
3899
  // src/ui/link-modal/UrlOrPageInput.tsx
4643
- var import_react3 = require("react");
3900
+ var import_react = require("react");
4644
3901
 
4645
3902
  // src/ui/input.tsx
4646
- var React4 = __toESM(require("react"), 1);
4647
- var import_jsx_runtime9 = require("react/jsx-runtime");
4648
- var Input = React4.forwardRef(
3903
+ var React3 = __toESM(require("react"), 1);
3904
+ var import_jsx_runtime7 = require("react/jsx-runtime");
3905
+ var Input = React3.forwardRef(
4649
3906
  ({ className, type, ...props }, ref) => {
4650
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3907
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4651
3908
  "input",
4652
3909
  {
4653
3910
  type,
@@ -4665,11 +3922,11 @@ var Input = React4.forwardRef(
4665
3922
  Input.displayName = "Input";
4666
3923
 
4667
3924
  // src/ui/label.tsx
4668
- var import_radix_ui4 = require("radix-ui");
4669
- var import_jsx_runtime10 = require("react/jsx-runtime");
3925
+ var import_radix_ui3 = require("radix-ui");
3926
+ var import_jsx_runtime8 = require("react/jsx-runtime");
4670
3927
  function Label({ className, ...props }) {
4671
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4672
- import_radix_ui4.Label.Root,
3928
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3929
+ import_radix_ui3.Label.Root,
4673
3930
  {
4674
3931
  "data-slot": "label",
4675
3932
  className: cn("text-sm font-medium leading-5 text-foreground", className),
@@ -4679,13 +3936,13 @@ function Label({ className, ...props }) {
4679
3936
  }
4680
3937
 
4681
3938
  // src/ui/popover.tsx
4682
- var import_radix_ui5 = require("radix-ui");
4683
- var import_jsx_runtime11 = require("react/jsx-runtime");
3939
+ var import_radix_ui4 = require("radix-ui");
3940
+ var import_jsx_runtime9 = require("react/jsx-runtime");
4684
3941
  function Popover({ ...props }) {
4685
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_radix_ui5.Popover.Root, { "data-slot": "popover", ...props });
3942
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Popover.Root, { "data-slot": "popover", ...props });
4686
3943
  }
4687
3944
  function PopoverTrigger({ ...props }) {
4688
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_radix_ui5.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
3945
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
4689
3946
  }
4690
3947
  function PopoverContent({
4691
3948
  className,
@@ -4693,8 +3950,8 @@ function PopoverContent({
4693
3950
  sideOffset = 6,
4694
3951
  ...props
4695
3952
  }) {
4696
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_radix_ui5.Popover.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4697
- import_radix_ui5.Popover.Content,
3953
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_radix_ui4.Popover.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3954
+ import_radix_ui4.Popover.Content,
4698
3955
  {
4699
3956
  "data-slot": "popover-content",
4700
3957
  align,
@@ -4709,9 +3966,9 @@ function PopoverContent({
4709
3966
  }
4710
3967
 
4711
3968
  // src/ui/link-modal/UrlOrPageInput.tsx
4712
- var import_jsx_runtime12 = require("react/jsx-runtime");
3969
+ var import_jsx_runtime10 = require("react/jsx-runtime");
4713
3970
  function FieldChevron({ onClick }) {
4714
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3971
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4715
3972
  "button",
4716
3973
  {
4717
3974
  type: "button",
@@ -4719,7 +3976,7 @@ function FieldChevron({ onClick }) {
4719
3976
  onClick,
4720
3977
  "aria-label": "Open page list",
4721
3978
  tabIndex: -1,
4722
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconChevronDown, {})
3979
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(IconChevronDown, {})
4723
3980
  }
4724
3981
  );
4725
3982
  }
@@ -4735,9 +3992,9 @@ function UrlOrPageInput({
4735
3992
  readOnly = false,
4736
3993
  urlError
4737
3994
  }) {
4738
- const inputId = (0, import_react3.useId)();
4739
- const inputRef = (0, import_react3.useRef)(null);
4740
- const [isFocused, setIsFocused] = (0, import_react3.useState)(false);
3995
+ const inputId = (0, import_react.useId)();
3996
+ const inputRef = (0, import_react.useRef)(null);
3997
+ const [isFocused, setIsFocused] = (0, import_react.useState)(false);
4741
3998
  const openDropdown = () => {
4742
3999
  if (readOnly || filteredPages.length === 0) return;
4743
4000
  onDropdownOpenChange(true);
@@ -4758,12 +4015,12 @@ function UrlOrPageInput({
4758
4015
  "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]",
4759
4016
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
4760
4017
  );
4761
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
4762
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
4763
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Popover, { open: dropdownOpen && !readOnly, onOpenChange: onDropdownOpenChange, children: [
4764
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
4765
- selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
4766
- readOnly ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4018
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
4019
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
4020
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Popover, { open: dropdownOpen && !readOnly, onOpenChange: onDropdownOpenChange, children: [
4021
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
4022
+ selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
4023
+ readOnly ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4767
4024
  Input,
4768
4025
  {
4769
4026
  ref: inputRef,
@@ -4782,7 +4039,7 @@ function UrlOrPageInput({
4782
4039
  )
4783
4040
  }
4784
4041
  ),
4785
- selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4042
+ selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4786
4043
  "button",
4787
4044
  {
4788
4045
  type: "button",
@@ -4790,31 +4047,31 @@ function UrlOrPageInput({
4790
4047
  onMouseDown: clearSelection,
4791
4048
  "aria-label": "Clear selected page",
4792
4049
  tabIndex: -1,
4793
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconX, { "aria-hidden": true })
4050
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(IconX, { "aria-hidden": true })
4794
4051
  }
4795
4052
  ) : null,
4796
- !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
4053
+ !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
4797
4054
  ] }) }),
4798
- filteredPages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PopoverContent, { "data-ohw-link-popover-root": "", onOpenAutoFocus: (e) => e.preventDefault(), children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4055
+ filteredPages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PopoverContent, { "data-ohw-link-popover-root": "", onOpenAutoFocus: (e) => e.preventDefault(), children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4799
4056
  "button",
4800
4057
  {
4801
4058
  type: "button",
4802
4059
  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",
4803
4060
  onClick: () => onPageSelect(page),
4804
4061
  children: [
4805
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(IconFile, { className: "shrink-0", "aria-hidden": true }),
4806
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "truncate", children: page.title })
4062
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(IconFile, { className: "shrink-0", "aria-hidden": true }),
4063
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "truncate", children: page.title })
4807
4064
  ]
4808
4065
  },
4809
4066
  page.path
4810
4067
  )) })
4811
4068
  ] }),
4812
- urlError ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
4069
+ urlError ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
4813
4070
  ] });
4814
4071
  }
4815
4072
 
4816
4073
  // src/ui/link-modal/useLinkModalState.ts
4817
- var import_react4 = require("react");
4074
+ var import_react2 = require("react");
4818
4075
  function useLinkModalState({
4819
4076
  open,
4820
4077
  mode,
@@ -4826,17 +4083,17 @@ function useLinkModalState({
4826
4083
  onClose,
4827
4084
  onSubmit
4828
4085
  }) {
4829
- const availablePages = (0, import_react4.useMemo)(
4086
+ const availablePages = (0, import_react2.useMemo)(
4830
4087
  () => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
4831
4088
  [mode, pages, existingTargets]
4832
4089
  );
4833
- const [searchValue, setSearchValue] = (0, import_react4.useState)("");
4834
- const [selectedPage, setSelectedPage] = (0, import_react4.useState)(null);
4835
- const [selectedSection, setSelectedSection] = (0, import_react4.useState)(null);
4836
- const [step, setStep] = (0, import_react4.useState)("input");
4837
- const [dropdownOpen, setDropdownOpen] = (0, import_react4.useState)(false);
4838
- const [urlError, setUrlError] = (0, import_react4.useState)("");
4839
- const reset = (0, import_react4.useCallback)(() => {
4090
+ const [searchValue, setSearchValue] = (0, import_react2.useState)("");
4091
+ const [selectedPage, setSelectedPage] = (0, import_react2.useState)(null);
4092
+ const [selectedSection, setSelectedSection] = (0, import_react2.useState)(null);
4093
+ const [step, setStep] = (0, import_react2.useState)("input");
4094
+ const [dropdownOpen, setDropdownOpen] = (0, import_react2.useState)(false);
4095
+ const [urlError, setUrlError] = (0, import_react2.useState)("");
4096
+ const reset = (0, import_react2.useCallback)(() => {
4840
4097
  setSearchValue("");
4841
4098
  setSelectedPage(null);
4842
4099
  setSelectedSection(null);
@@ -4844,7 +4101,7 @@ function useLinkModalState({
4844
4101
  setDropdownOpen(false);
4845
4102
  setUrlError("");
4846
4103
  }, []);
4847
- (0, import_react4.useEffect)(() => {
4104
+ (0, import_react2.useEffect)(() => {
4848
4105
  if (!open) return;
4849
4106
  if (mode === "edit" && initialTarget) {
4850
4107
  const { pageRoute } = parseTarget(initialTarget);
@@ -4860,11 +4117,11 @@ function useLinkModalState({
4860
4117
  setDropdownOpen(false);
4861
4118
  setUrlError("");
4862
4119
  }, [open, mode, initialTarget, pages, sectionsByPath, reset]);
4863
- const filteredPages = (0, import_react4.useMemo)(() => {
4120
+ const filteredPages = (0, import_react2.useMemo)(() => {
4864
4121
  if (!searchValue.trim()) return availablePages;
4865
4122
  return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
4866
4123
  }, [availablePages, searchValue]);
4867
- const activeSections = (0, import_react4.useMemo)(() => {
4124
+ const activeSections = (0, import_react2.useMemo)(() => {
4868
4125
  if (!selectedPage) return [];
4869
4126
  return getSectionsForPath(sectionsByPath, selectedPage.path);
4870
4127
  }, [selectedPage, sectionsByPath]);
@@ -4910,7 +4167,7 @@ function useLinkModalState({
4910
4167
  reset();
4911
4168
  onClose();
4912
4169
  };
4913
- const isValid = (0, import_react4.useMemo)(() => {
4170
+ const isValid = (0, import_react2.useMemo)(() => {
4914
4171
  if (urlError) return false;
4915
4172
  if (showBreadcrumb) return true;
4916
4173
  if (selectedPage) return true;
@@ -4977,7 +4234,7 @@ function useLinkModalState({
4977
4234
  }
4978
4235
 
4979
4236
  // src/ui/link-modal/LinkEditorPanel.tsx
4980
- var import_jsx_runtime13 = require("react/jsx-runtime");
4237
+ var import_jsx_runtime11 = require("react/jsx-runtime");
4981
4238
  function LinkEditorPanel({
4982
4239
  open = true,
4983
4240
  mode = "create",
@@ -5001,26 +4258,26 @@ function LinkEditorPanel({
5001
4258
  onSubmit
5002
4259
  });
5003
4260
  const isCancel = state.secondaryLabel === "Cancel";
5004
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
5005
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4261
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
4262
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
5006
4263
  "button",
5007
4264
  {
5008
4265
  type: "button",
5009
4266
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5010
4267
  onClick: state.handleClose,
5011
4268
  "aria-label": "Close",
5012
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconX, { "aria-hidden": true })
4269
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconX, { "aria-hidden": true })
5013
4270
  }
5014
4271
  ),
5015
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("header", { className: "flex w-full flex-col gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: "m-0 w-full break-words text-lg font-semibold leading-none tracking-tight text-card-foreground", children: state.title }) }),
5016
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5017
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4272
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("header", { className: "flex w-full flex-col gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { className: "m-0 w-full break-words text-lg font-semibold leading-none tracking-tight text-card-foreground", children: state.title }) }),
4273
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
4274
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
5018
4275
  DestinationBreadcrumb,
5019
4276
  {
5020
4277
  pageTitle: state.selectedPage.title,
5021
4278
  sectionLabel: state.selectedSection.label
5022
4279
  }
5023
- ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4280
+ ) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
5024
4281
  UrlOrPageInput,
5025
4282
  {
5026
4283
  value: state.searchValue,
@@ -5033,18 +4290,18 @@ function LinkEditorPanel({
5033
4290
  urlError: state.urlError
5034
4291
  }
5035
4292
  ),
5036
- state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5037
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5038
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5039
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5040
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Pick a section this link should scroll to." })
4293
+ state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
4294
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
4295
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
4296
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconInfo, { className: "shrink-0", "aria-hidden": true }),
4297
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: "Pick a section this link should scroll to." })
5041
4298
  ] })
5042
4299
  ] }) : null,
5043
- state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5044
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
4300
+ state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
4301
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5045
4302
  ] }),
5046
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("footer", { className: "flex w-full items-center justify-end gap-2 px-6 pb-6", children: [
5047
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4303
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("footer", { className: "flex w-full items-center justify-end gap-2 px-6 pb-6", children: [
4304
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
5048
4305
  Button,
5049
4306
  {
5050
4307
  type: "button",
@@ -5059,7 +4316,7 @@ function LinkEditorPanel({
5059
4316
  children: state.secondaryLabel
5060
4317
  }
5061
4318
  ),
5062
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4319
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
5063
4320
  Button,
5064
4321
  {
5065
4322
  type: "button",
@@ -5078,7 +4335,7 @@ function LinkEditorPanel({
5078
4335
  }
5079
4336
 
5080
4337
  // src/ui/link-modal/LinkPopover.tsx
5081
- var import_jsx_runtime14 = require("react/jsx-runtime");
4338
+ var import_jsx_runtime12 = require("react/jsx-runtime");
5082
4339
  function LinkPopover({
5083
4340
  rect,
5084
4341
  parentScroll,
@@ -5086,7 +4343,7 @@ function LinkPopover({
5086
4343
  ...editorProps
5087
4344
  }) {
5088
4345
  const { top, left, transform } = calcPopoverPos(rect, parentScroll);
5089
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4346
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5090
4347
  "div",
5091
4348
  {
5092
4349
  ref: panelRef,
@@ -5099,7 +4356,7 @@ function LinkPopover({
5099
4356
  ),
5100
4357
  style: { top, left, transform },
5101
4358
  onMouseDown: (e) => e.stopPropagation(),
5102
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(LinkEditorPanel, { ...editorProps, open: true })
4359
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(LinkEditorPanel, { ...editorProps, open: true })
5103
4360
  }
5104
4361
  );
5105
4362
  }
@@ -5162,30 +4419,8 @@ function shouldUseDevFixtures() {
5162
4419
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
5163
4420
  }
5164
4421
 
5165
- // src/ui/badge.tsx
5166
- var import_jsx_runtime15 = require("react/jsx-runtime");
5167
- var badgeVariants = cva(
5168
- "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",
5169
- {
5170
- variants: {
5171
- variant: {
5172
- default: "border-transparent bg-primary text-primary-foreground",
5173
- secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
5174
- destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
5175
- outline: "text-foreground"
5176
- }
5177
- },
5178
- defaultVariants: {
5179
- variant: "default"
5180
- }
5181
- }
5182
- );
5183
- function Badge({ className, variant, ...props }) {
5184
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
5185
- }
5186
-
5187
4422
  // src/OhhwellsBridge.tsx
5188
- var import_jsx_runtime16 = require("react/jsx-runtime");
4423
+ var import_jsx_runtime13 = require("react/jsx-runtime");
5189
4424
  var PRIMARY = "#0885FE";
5190
4425
  var IMAGE_FADE_MS = 300;
5191
4426
  function runOpacityFade(el, onDone) {
@@ -5236,163 +4471,6 @@ function fadeInBgImage(el, url, onReady) {
5236
4471
  if (!prevPos || prevPos === "static") el.style.position = prevPos;
5237
4472
  });
5238
4473
  }
5239
- function getSectionsTracker() {
5240
- let el = document.querySelector("[data-ohw-sections-tracker]");
5241
- if (!el) {
5242
- el = document.createElement("div");
5243
- el.setAttribute("data-ohw-sections-tracker", "");
5244
- el.style.display = "none";
5245
- document.body.appendChild(el);
5246
- }
5247
- return el;
5248
- }
5249
- function updateSectionScheduleId(insertAfter, scheduleId) {
5250
- const tracker = getSectionsTracker();
5251
- let sections = [];
5252
- try {
5253
- sections = JSON.parse(tracker.textContent || "[]");
5254
- } catch {
5255
- }
5256
- const currentPath = window.location.pathname;
5257
- const updated = sections.map((s) => {
5258
- if (s.type !== "scheduling" || s.insertAfter !== insertAfter) return s;
5259
- if (s.pagePath && s.pagePath !== currentPath) return s;
5260
- return { ...s, scheduleId };
5261
- });
5262
- tracker.textContent = JSON.stringify(updated);
5263
- return tracker.textContent ?? "[]";
5264
- }
5265
- function schedulingSectionId(insertAfter) {
5266
- return `scheduling-${insertAfter}`;
5267
- }
5268
- var INSERT_BEFORE_MARKER = ":before:";
5269
- function parseSchedulingInsertAfter(insertAfter) {
5270
- const idx = insertAfter.indexOf(INSERT_BEFORE_MARKER);
5271
- if (idx < 0) return { anchor: insertAfter, insertBefore: null };
5272
- return {
5273
- anchor: insertAfter.slice(0, idx),
5274
- insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
5275
- };
5276
- }
5277
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
5278
- const parsed = parseSchedulingInsertAfter(insertAfter);
5279
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
5280
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
5281
- return { effectiveInsertAfter, insertBefore };
5282
- }
5283
- function getSchedulingMountPoint(insertAfter) {
5284
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
5285
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
5286
- if (!anchorEl && anchor === "scheduling") {
5287
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
5288
- anchorEl = widgets.at(-1) ?? null;
5289
- }
5290
- if (!anchorEl) return null;
5291
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
5292
- }
5293
- function schedulingMountDepth(insertAfter) {
5294
- if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
5295
- return insertAfter.split(INSERT_BEFORE_MARKER).length - 1;
5296
- }
5297
- function getPageSchedulingEntries(raw) {
5298
- if (!raw) return [];
5299
- try {
5300
- const entries = JSON.parse(raw);
5301
- const currentPath = window.location.pathname;
5302
- return entries.filter((e) => e.type === "scheduling" && (!e.pagePath || e.pagePath === currentPath));
5303
- } catch {
5304
- return [];
5305
- }
5306
- }
5307
- function isSchedulingWidgetMissing(entry) {
5308
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
5309
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
5310
- }
5311
- function hasMissingSchedulingWidgets(entries) {
5312
- return entries.some(isSchedulingWidgetMissing);
5313
- }
5314
- function retryMissingSchedulingMounts(entries, notifyOnConnect = false) {
5315
- if (!hasMissingSchedulingWidgets(entries)) return;
5316
- mountSchedulingEntries(entries, notifyOnConnect);
5317
- }
5318
- function initSectionsFromContent(content, removeExisting = false) {
5319
- const raw = content["__ohw_sections"];
5320
- if (!raw) return;
5321
- try {
5322
- if (removeExisting) {
5323
- document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => el.remove());
5324
- }
5325
- const inEditor = typeof window !== "undefined" && window.self !== window.top;
5326
- if (inEditor) getSectionsTracker().textContent = raw;
5327
- const pageEntries = getPageSchedulingEntries(raw);
5328
- mountSchedulingEntries(pageEntries, false);
5329
- requestAnimationFrame(() => retryMissingSchedulingMounts(pageEntries, false));
5330
- setTimeout(() => retryMissingSchedulingMounts(pageEntries, false), 250);
5331
- } catch {
5332
- }
5333
- }
5334
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
5335
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
5336
- const sectionId = schedulingSectionId(effectiveInsertAfter);
5337
- if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
5338
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
5339
- if (!mountPoint) return false;
5340
- const container = document.createElement("div");
5341
- container.dataset.ohwSectionContainer = "scheduling";
5342
- if (insertBefore) {
5343
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
5344
- const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
5345
- if (!beforePoint) return false;
5346
- beforePoint.insertAdjacentElement("beforebegin", container);
5347
- } else {
5348
- let tail = mountPoint;
5349
- while (tail.nextElementSibling?.dataset.ohwSectionContainer === "scheduling") {
5350
- tail = tail.nextElementSibling;
5351
- }
5352
- tail.insertAdjacentElement("afterend", container);
5353
- }
5354
- const root = (0, import_client.createRoot)(container);
5355
- (0, import_react_dom.flushSync)(() => {
5356
- root.render(
5357
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5358
- SchedulingWidget,
5359
- {
5360
- notifyOnConnect,
5361
- initialScheduleId: scheduleId,
5362
- insertAfter: effectiveInsertAfter
5363
- }
5364
- )
5365
- );
5366
- });
5367
- const tracker = getSectionsTracker();
5368
- let sections = [];
5369
- try {
5370
- sections = JSON.parse(tracker.textContent || "[]");
5371
- } catch {
5372
- }
5373
- const inEditor = typeof window !== "undefined" && window.self !== window.top;
5374
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
5375
- sections.push({
5376
- type: "scheduling",
5377
- insertAfter: effectiveInsertAfter,
5378
- pagePath: window.location.pathname,
5379
- ...scheduleId ? { scheduleId } : {}
5380
- });
5381
- tracker.textContent = JSON.stringify(sections);
5382
- }
5383
- return true;
5384
- }
5385
- function mountSchedulingEntries(entries, notifyOnConnect = false) {
5386
- const pending = entries.filter((e) => e.type === "scheduling").sort((a, b) => schedulingMountDepth(a.insertAfter) - schedulingMountDepth(b.insertAfter));
5387
- for (let attempt = 0; attempt < pending.length + 1 && pending.length > 0; attempt++) {
5388
- for (let i = pending.length - 1; i >= 0; i--) {
5389
- const { insertAfter, scheduleId } = pending[i];
5390
- if (mountSchedulingWidget(insertAfter, notifyOnConnect, scheduleId)) {
5391
- pending.splice(i, 1);
5392
- }
5393
- }
5394
- }
5395
- }
5396
4474
  function getLinkHref(el) {
5397
4475
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
5398
4476
  return anchor?.getAttribute("href") ?? "";
@@ -5592,7 +4670,7 @@ var TOOLBAR_GROUPS = [
5592
4670
  ];
5593
4671
  function GlowFrame({ rect, elRef }) {
5594
4672
  const GAP = 6;
5595
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4673
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5596
4674
  "div",
5597
4675
  {
5598
4676
  ref: elRef,
@@ -5645,7 +4723,7 @@ function FloatingToolbar({
5645
4723
  onEditLink
5646
4724
  }) {
5647
4725
  const { top, left, transform } = calcToolbarPos(rect, parentScroll);
5648
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4726
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
5649
4727
  "div",
5650
4728
  {
5651
4729
  ref: elRef,
@@ -5670,11 +4748,11 @@ function FloatingToolbar({
5670
4748
  },
5671
4749
  onMouseDown: (e) => e.stopPropagation(),
5672
4750
  children: [
5673
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react5.default.Fragment, { children: [
5674
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
4751
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react3.default.Fragment, { children: [
4752
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
5675
4753
  btns.map((btn) => {
5676
4754
  const isActive = activeCommands.has(btn.cmd);
5677
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4755
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5678
4756
  "button",
5679
4757
  {
5680
4758
  title: btn.title,
@@ -5700,7 +4778,7 @@ function FloatingToolbar({
5700
4778
  flexShrink: 0,
5701
4779
  padding: 6
5702
4780
  },
5703
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4781
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5704
4782
  "svg",
5705
4783
  {
5706
4784
  width: "16",
@@ -5720,7 +4798,7 @@ function FloatingToolbar({
5720
4798
  );
5721
4799
  })
5722
4800
  ] }, gi)),
5723
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4801
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5724
4802
  "button",
5725
4803
  {
5726
4804
  type: "button",
@@ -5752,9 +4830,9 @@ function FloatingToolbar({
5752
4830
  flexShrink: 0,
5753
4831
  padding: 6
5754
4832
  },
5755
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("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: [
5756
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
5757
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
4833
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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: [
4834
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
4835
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
5758
4836
  ] })
5759
4837
  }
5760
4838
  ) : null
@@ -5772,7 +4850,7 @@ function StateToggle({
5772
4850
  states,
5773
4851
  onStateChange
5774
4852
  }) {
5775
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4853
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5776
4854
  ToggleGroup,
5777
4855
  {
5778
4856
  "data-ohw-state-toggle": "",
@@ -5786,35 +4864,18 @@ function StateToggle({
5786
4864
  left: rect.right - 8,
5787
4865
  transform: "translateX(-100%)"
5788
4866
  },
5789
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
4867
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
5790
4868
  }
5791
4869
  );
5792
4870
  }
5793
4871
  var contentCache = /* @__PURE__ */ new Map();
5794
- function resolveSubdomain(subdomainFromQuery) {
5795
- if (subdomainFromQuery) return subdomainFromQuery;
5796
- if (typeof window !== "undefined") {
5797
- const parts = window.location.hostname.split(".");
5798
- if (parts.length >= 3 && parts[0] !== "www") return parts[0];
5799
- }
5800
- const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;
5801
- if (siteUrl) {
5802
- try {
5803
- const host = new URL(siteUrl).hostname;
5804
- const siteParts = host.split(".");
5805
- if (siteParts.length >= 3 && siteParts[0] !== "www") return siteParts[0];
5806
- } catch {
5807
- }
5808
- }
5809
- return "";
5810
- }
5811
4872
  function OhhwellsBridge() {
5812
4873
  const pathname = (0, import_navigation.usePathname)();
5813
4874
  const router = (0, import_navigation.useRouter)();
5814
4875
  const searchParams = (0, import_navigation.useSearchParams)();
5815
4876
  const isEditMode = isEditSessionActive();
5816
- const [bridgeRoot, setBridgeRoot] = (0, import_react5.useState)(null);
5817
- (0, import_react5.useEffect)(() => {
4877
+ const [bridgeRoot, setBridgeRoot] = (0, import_react3.useState)(null);
4878
+ (0, import_react3.useEffect)(() => {
5818
4879
  const figtreeFontId = "ohw-figtree-font";
5819
4880
  if (!document.getElementById(figtreeFontId)) {
5820
4881
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -5837,52 +4898,55 @@ function OhhwellsBridge() {
5837
4898
  };
5838
4899
  }, []);
5839
4900
  const subdomainFromQuery = searchParams.get("subdomain");
5840
- const subdomain = resolveSubdomain(subdomainFromQuery);
5841
- const postToParent = (0, import_react5.useCallback)((data) => {
4901
+ const subdomain = subdomainFromQuery ?? (() => {
4902
+ if (typeof window === "undefined") return "";
4903
+ const parts = window.location.hostname.split(".");
4904
+ return parts.length >= 3 && parts[0] !== "www" ? parts[0] : "";
4905
+ })();
4906
+ const postToParent = (0, import_react3.useCallback)((data) => {
5842
4907
  if (typeof window !== "undefined" && window.parent !== window) {
5843
4908
  window.parent.postMessage(data, "*");
5844
4909
  }
5845
4910
  }, []);
5846
- const [fetchState, setFetchState] = (0, import_react5.useState)("idle");
5847
- const autoSaveTimers = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
5848
- const activeElRef = (0, import_react5.useRef)(null);
5849
- const originalContentRef = (0, import_react5.useRef)(null);
5850
- const activeStateElRef = (0, import_react5.useRef)(null);
5851
- const parentScrollRef = (0, import_react5.useRef)(null);
5852
- const toolbarElRef = (0, import_react5.useRef)(null);
5853
- const glowElRef = (0, import_react5.useRef)(null);
5854
- const hoveredImageRef = (0, import_react5.useRef)(null);
5855
- const hoveredImageHasTextOverlapRef = (0, import_react5.useRef)(false);
5856
- const hoveredGapRef = (0, import_react5.useRef)(null);
5857
- const imageUnhoverTimerRef = (0, import_react5.useRef)(null);
5858
- const imageShowTimerRef = (0, import_react5.useRef)(null);
5859
- const editStylesRef = (0, import_react5.useRef)(null);
5860
- const activateRef = (0, import_react5.useRef)(() => {
4911
+ const [fetchState, setFetchState] = (0, import_react3.useState)("idle");
4912
+ const autoSaveTimers = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
4913
+ const activeElRef = (0, import_react3.useRef)(null);
4914
+ const originalContentRef = (0, import_react3.useRef)(null);
4915
+ const activeStateElRef = (0, import_react3.useRef)(null);
4916
+ const parentScrollRef = (0, import_react3.useRef)(null);
4917
+ const toolbarElRef = (0, import_react3.useRef)(null);
4918
+ const glowElRef = (0, import_react3.useRef)(null);
4919
+ const hoveredImageRef = (0, import_react3.useRef)(null);
4920
+ const hoveredImageHasTextOverlapRef = (0, import_react3.useRef)(false);
4921
+ const imageUnhoverTimerRef = (0, import_react3.useRef)(null);
4922
+ const imageShowTimerRef = (0, import_react3.useRef)(null);
4923
+ const editStylesRef = (0, import_react3.useRef)(null);
4924
+ const activateRef = (0, import_react3.useRef)(() => {
5861
4925
  });
5862
- const deactivateRef = (0, import_react5.useRef)(() => {
4926
+ const deactivateRef = (0, import_react3.useRef)(() => {
5863
4927
  });
5864
- const refreshActiveCommandsRef = (0, import_react5.useRef)(() => {
4928
+ const refreshActiveCommandsRef = (0, import_react3.useRef)(() => {
5865
4929
  });
5866
- const postToParentRef = (0, import_react5.useRef)(postToParent);
4930
+ const postToParentRef = (0, import_react3.useRef)(postToParent);
5867
4931
  postToParentRef.current = postToParent;
5868
- const [toolbarRect, setToolbarRect] = (0, import_react5.useState)(null);
5869
- const [toggleState, setToggleState] = (0, import_react5.useState)(null);
5870
- const [maxBadge, setMaxBadge] = (0, import_react5.useState)(null);
5871
- const [activeCommands, setActiveCommands] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
5872
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react5.useState)(false);
5873
- const [linkPopover, setLinkPopover] = (0, import_react5.useState)(null);
5874
- const [sitePages, setSitePages] = (0, import_react5.useState)([]);
5875
- const [sectionsByPath, setSectionsByPath] = (0, import_react5.useState)({});
5876
- const sectionsPrefetchGenRef = (0, import_react5.useRef)(0);
5877
- const setLinkPopoverRef = (0, import_react5.useRef)(setLinkPopover);
5878
- const linkPopoverPanelRef = (0, import_react5.useRef)(null);
5879
- const linkPopoverOpenRef = (0, import_react5.useRef)(false);
5880
- const linkPopoverGraceUntilRef = (0, import_react5.useRef)(0);
4932
+ const [toolbarRect, setToolbarRect] = (0, import_react3.useState)(null);
4933
+ const [toggleState, setToggleState] = (0, import_react3.useState)(null);
4934
+ const [maxBadge, setMaxBadge] = (0, import_react3.useState)(null);
4935
+ const [activeCommands, setActiveCommands] = (0, import_react3.useState)(/* @__PURE__ */ new Set());
4936
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react3.useState)(false);
4937
+ const [linkPopover, setLinkPopover] = (0, import_react3.useState)(null);
4938
+ const [sitePages, setSitePages] = (0, import_react3.useState)([]);
4939
+ const [sectionsByPath, setSectionsByPath] = (0, import_react3.useState)({});
4940
+ const sectionsPrefetchGenRef = (0, import_react3.useRef)(0);
4941
+ const setLinkPopoverRef = (0, import_react3.useRef)(setLinkPopover);
4942
+ const linkPopoverPanelRef = (0, import_react3.useRef)(null);
4943
+ const linkPopoverOpenRef = (0, import_react3.useRef)(false);
4944
+ const linkPopoverGraceUntilRef = (0, import_react3.useRef)(0);
5881
4945
  setLinkPopoverRef.current = setLinkPopover;
5882
4946
  const bumpLinkPopoverGrace = () => {
5883
4947
  linkPopoverGraceUntilRef.current = Date.now() + 350;
5884
4948
  };
5885
- const runSectionsPrefetch = (0, import_react5.useCallback)((pages) => {
4949
+ const runSectionsPrefetch = (0, import_react3.useCallback)((pages) => {
5886
4950
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
5887
4951
  const gen = ++sectionsPrefetchGenRef.current;
5888
4952
  const paths = pages.map((p) => p.path);
@@ -5901,9 +4965,9 @@ function OhhwellsBridge() {
5901
4965
  );
5902
4966
  });
5903
4967
  }, [isEditMode, pathname]);
5904
- const runSectionsPrefetchRef = (0, import_react5.useRef)(runSectionsPrefetch);
4968
+ const runSectionsPrefetchRef = (0, import_react3.useRef)(runSectionsPrefetch);
5905
4969
  runSectionsPrefetchRef.current = runSectionsPrefetch;
5906
- (0, import_react5.useEffect)(() => {
4970
+ (0, import_react3.useEffect)(() => {
5907
4971
  if (!linkPopover) return;
5908
4972
  if (hoveredImageRef.current) {
5909
4973
  hoveredImageRef.current = null;
@@ -5911,7 +4975,7 @@ function OhhwellsBridge() {
5911
4975
  }
5912
4976
  postToParent({ type: "ow:image-unhover" });
5913
4977
  }, [linkPopover, postToParent]);
5914
- (0, import_react5.useEffect)(() => {
4978
+ (0, import_react3.useEffect)(() => {
5915
4979
  if (!isEditMode) return;
5916
4980
  const useFixtures = shouldUseDevFixtures();
5917
4981
  if (useFixtures) {
@@ -5935,15 +4999,14 @@ function OhhwellsBridge() {
5935
4999
  if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
5936
5000
  return () => window.removeEventListener("message", onSitePages);
5937
5001
  }, [isEditMode, postToParent]);
5938
- const [sectionGap, setSectionGap] = (0, import_react5.useState)(null);
5939
- (0, import_react5.useEffect)(() => {
5002
+ (0, import_react3.useEffect)(() => {
5940
5003
  if (!isEditMode || shouldUseDevFixtures()) return;
5941
5004
  void loadAllSectionsManifest().then((manifest) => {
5942
5005
  if (Object.keys(manifest).length === 0) return;
5943
5006
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
5944
5007
  });
5945
5008
  }, [isEditMode]);
5946
- (0, import_react5.useEffect)(() => {
5009
+ (0, import_react3.useEffect)(() => {
5947
5010
  const update = () => {
5948
5011
  const el = activeElRef.current;
5949
5012
  if (el) setToolbarRect(el.getBoundingClientRect());
@@ -5951,12 +5014,6 @@ function OhhwellsBridge() {
5951
5014
  if (!prev || !activeStateElRef.current) return prev;
5952
5015
  return { ...prev, rect: activeStateElRef.current.getBoundingClientRect() };
5953
5016
  });
5954
- setSectionGap((prev) => {
5955
- if (!prev) return prev;
5956
- const anchor = document.querySelector(`[data-ohw-section="${prev.insertAfter}"]`);
5957
- if (!anchor) return prev;
5958
- return { ...prev, y: anchor.getBoundingClientRect().bottom };
5959
- });
5960
5017
  };
5961
5018
  const vvp = window.visualViewport;
5962
5019
  if (!vvp) return;
@@ -5967,10 +5024,10 @@ function OhhwellsBridge() {
5967
5024
  vvp.removeEventListener("resize", update);
5968
5025
  };
5969
5026
  }, []);
5970
- const refreshStateRules = (0, import_react5.useCallback)(() => {
5027
+ const refreshStateRules = (0, import_react3.useCallback)(() => {
5971
5028
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
5972
5029
  }, []);
5973
- const deactivate = (0, import_react5.useCallback)(() => {
5030
+ const deactivate = (0, import_react3.useCallback)(() => {
5974
5031
  const el = activeElRef.current;
5975
5032
  if (!el) return;
5976
5033
  const key = el.dataset.ohwKey;
@@ -5996,7 +5053,7 @@ function OhhwellsBridge() {
5996
5053
  setToolbarShowEditLink(false);
5997
5054
  postToParent({ type: "ow:exit-edit" });
5998
5055
  }, [postToParent]);
5999
- const activate = (0, import_react5.useCallback)((el) => {
5056
+ const activate = (0, import_react3.useCallback)((el) => {
6000
5057
  if (activeElRef.current === el) return;
6001
5058
  deactivate();
6002
5059
  if (hoveredImageRef.current) {
@@ -6015,7 +5072,7 @@ function OhhwellsBridge() {
6015
5072
  }, [deactivate, postToParent]);
6016
5073
  activateRef.current = activate;
6017
5074
  deactivateRef.current = deactivate;
6018
- (0, import_react5.useLayoutEffect)(() => {
5075
+ (0, import_react3.useLayoutEffect)(() => {
6019
5076
  if (!subdomain || isEditMode) {
6020
5077
  setFetchState("done");
6021
5078
  return;
@@ -6023,7 +5080,6 @@ function OhhwellsBridge() {
6023
5080
  const applyContent = (content) => {
6024
5081
  const imageLoads = [];
6025
5082
  for (const [key, val] of Object.entries(content)) {
6026
- if (key === "__ohw_sections") continue;
6027
5083
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
6028
5084
  if (el.dataset.ohwEditable === "image") {
6029
5085
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6045,7 +5101,6 @@ function OhhwellsBridge() {
6045
5101
  });
6046
5102
  applyLinkByKey(key, val);
6047
5103
  }
6048
- initSectionsFromContent(content, true);
6049
5104
  return imageLoads.length > 0 ? Promise.all(imageLoads).then(() => {
6050
5105
  }) : Promise.resolve();
6051
5106
  };
@@ -6070,15 +5125,12 @@ function OhhwellsBridge() {
6070
5125
  cancelled = true;
6071
5126
  };
6072
5127
  }, [subdomain, isEditMode, pathname]);
6073
- (0, import_react5.useEffect)(() => {
5128
+ (0, import_react3.useEffect)(() => {
6074
5129
  if (!subdomain || isEditMode) return;
6075
- let debounceTimer = null;
6076
5130
  const applyFromCache = () => {
6077
5131
  const content = contentCache.get(subdomain);
6078
5132
  if (!content) return;
6079
- retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
6080
5133
  for (const [key, val] of Object.entries(content)) {
6081
- if (key === "__ohw_sections") continue;
6082
5134
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
6083
5135
  if (el.dataset.ohwEditable === "image") {
6084
5136
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6095,28 +5147,21 @@ function OhhwellsBridge() {
6095
5147
  applyLinkByKey(key, val);
6096
5148
  }
6097
5149
  };
6098
- const scheduleApply = () => {
6099
- if (debounceTimer) clearTimeout(debounceTimer);
6100
- debounceTimer = setTimeout(applyFromCache, 150);
6101
- };
6102
- scheduleApply();
6103
- const observer = new MutationObserver(scheduleApply);
5150
+ applyFromCache();
5151
+ const observer = new MutationObserver(applyFromCache);
6104
5152
  observer.observe(document.body, { childList: true, subtree: true });
6105
- return () => {
6106
- observer.disconnect();
6107
- if (debounceTimer) clearTimeout(debounceTimer);
6108
- };
6109
- }, [subdomain, isEditMode, pathname]);
6110
- (0, import_react5.useLayoutEffect)(() => {
5153
+ return () => observer.disconnect();
5154
+ }, [subdomain, isEditMode]);
5155
+ (0, import_react3.useLayoutEffect)(() => {
6111
5156
  const el = document.getElementById("ohw-loader");
6112
5157
  if (!el) return;
6113
5158
  const visible = Boolean(subdomain) && fetchState !== "done";
6114
5159
  el.style.display = visible ? "flex" : "none";
6115
5160
  }, [subdomain, fetchState]);
6116
- (0, import_react5.useEffect)(() => {
5161
+ (0, import_react3.useEffect)(() => {
6117
5162
  postToParent({ type: "ow:navigation", path: pathname });
6118
5163
  }, [pathname, postToParent]);
6119
- (0, import_react5.useEffect)(() => {
5164
+ (0, import_react3.useEffect)(() => {
6120
5165
  if (!isEditMode) return;
6121
5166
  const measure = () => {
6122
5167
  const h = document.body.scrollHeight;
@@ -6140,7 +5185,7 @@ function OhhwellsBridge() {
6140
5185
  window.removeEventListener("resize", handleResize);
6141
5186
  };
6142
5187
  }, [pathname, isEditMode, postToParent]);
6143
- (0, import_react5.useEffect)(() => {
5188
+ (0, import_react3.useEffect)(() => {
6144
5189
  if (!subdomainFromQuery || isEditMode) return;
6145
5190
  const handleClick = (e) => {
6146
5191
  const anchor = e.target.closest("a");
@@ -6156,7 +5201,7 @@ function OhhwellsBridge() {
6156
5201
  document.addEventListener("click", handleClick, true);
6157
5202
  return () => document.removeEventListener("click", handleClick, true);
6158
5203
  }, [subdomainFromQuery, isEditMode, router]);
6159
- (0, import_react5.useEffect)(() => {
5204
+ (0, import_react3.useEffect)(() => {
6160
5205
  if (!isEditMode) {
6161
5206
  editStylesRef.current?.base.remove();
6162
5207
  editStylesRef.current?.forceHover.remove();
@@ -6457,15 +5502,6 @@ function OhhwellsBridge() {
6457
5502
  textEditable.setAttribute("data-ohw-hovered", "");
6458
5503
  return;
6459
5504
  }
6460
- if (hoveredGapRef.current) {
6461
- if (hoveredImageRef.current) {
6462
- hoveredImageRef.current = null;
6463
- resumeAnimTracks();
6464
- postToParentRef.current({ type: "ow:image-unhover" });
6465
- }
6466
- document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
6467
- return;
6468
- }
6469
5505
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
6470
5506
  if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
6471
5507
  hoveredImageRef.current = imgEl;
@@ -6531,30 +5567,8 @@ function OhhwellsBridge() {
6531
5567
  }
6532
5568
  }
6533
5569
  };
6534
- const probeSectionGapAt = (clientX, clientY, fromParentViewport = false) => {
6535
- const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
6536
- const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
6537
- const ZONE = 20;
6538
- for (let i = 0; i < sections.length; i++) {
6539
- const a = sections[i];
6540
- const b = sections[i + 1] ?? null;
6541
- const boundaryY = a.getBoundingClientRect().bottom;
6542
- if (Math.abs(y - boundaryY) <= ZONE) {
6543
- const insertAfter = a.dataset.ohwSection ?? "";
6544
- const insertBefore = b?.dataset.ohwSection ?? null;
6545
- hoveredGapRef.current = { insertAfter, insertBefore };
6546
- setSectionGap({ insertAfter, insertBefore, y: boundaryY });
6547
- return;
6548
- }
6549
- }
6550
- if (hoveredGapRef.current) {
6551
- hoveredGapRef.current = null;
6552
- setSectionGap(null);
6553
- }
6554
- };
6555
5570
  const handleMouseMove = (e) => {
6556
5571
  const { clientX, clientY } = e;
6557
- probeSectionGapAt(clientX, clientY);
6558
5572
  probeImageAt(clientX, clientY);
6559
5573
  probeHoverCardsAt(clientX, clientY);
6560
5574
  };
@@ -6562,7 +5576,6 @@ function OhhwellsBridge() {
6562
5576
  if (e.data?.type !== "ow:pointer-sync") return;
6563
5577
  const { clientX, clientY } = e.data;
6564
5578
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
6565
- probeSectionGapAt(clientX, clientY);
6566
5579
  probeImageAt(clientX, clientY);
6567
5580
  probeHoverCardsAt(clientX, clientY);
6568
5581
  };
@@ -6720,12 +5733,7 @@ function OhhwellsBridge() {
6720
5733
  if (e.data?.type !== "ow:hydrate") return;
6721
5734
  const content = e.data.content;
6722
5735
  if (!content) return;
6723
- let sectionsJson = null;
6724
5736
  for (const [key, val] of Object.entries(content)) {
6725
- if (key === "__ohw_sections") {
6726
- sectionsJson = val;
6727
- continue;
6728
- }
6729
5737
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
6730
5738
  if (el.dataset.ohwEditable === "image") {
6731
5739
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6740,9 +5748,6 @@ function OhhwellsBridge() {
6740
5748
  });
6741
5749
  applyLinkByKey(key, val);
6742
5750
  }
6743
- if (sectionsJson) {
6744
- initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
6745
- }
6746
5751
  postToParentRef.current({ type: "ow:hydrate-done" });
6747
5752
  };
6748
5753
  window.addEventListener("message", handleHydrate);
@@ -6795,63 +5800,7 @@ function OhhwellsBridge() {
6795
5800
  };
6796
5801
  const handleSave = (e) => {
6797
5802
  if (e.data?.type !== "ow:save") return;
6798
- const nodes = collectEditableNodes();
6799
- const tracker = document.querySelector("[data-ohw-sections-tracker]");
6800
- if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
6801
- postToParentRef.current({ type: "ow:save-result", nodes });
6802
- };
6803
- const handleInsertSection = (e) => {
6804
- if (e.data?.type !== "ow:insert-section") return;
6805
- const { widgetType, insertAfter, insertBefore } = e.data;
6806
- if (widgetType !== "scheduling") return;
6807
- const inserted = mountSchedulingWidget(insertAfter, true, void 0, insertBefore ?? null);
6808
- if (inserted) {
6809
- const tracker = getSectionsTracker();
6810
- postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
6811
- const h = document.documentElement.scrollHeight;
6812
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
6813
- }
6814
- };
6815
- const handleSwitchSchedule = (e) => {
6816
- if (e.data?.type !== "ow:switch-schedule") return;
6817
- const scheduleId = e.data.scheduleId;
6818
- const insertAfter = e.data.insertAfter;
6819
- if (!scheduleId || !insertAfter) return;
6820
- const text = updateSectionScheduleId(insertAfter, scheduleId);
6821
- postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
6822
- };
6823
- const handleScheduleLinked = (e) => {
6824
- if (e.data?.type !== "ow:schedule-linked") return;
6825
- const scheduleId = e.data.scheduleId;
6826
- const insertAfter = e.data.insertAfter;
6827
- if (!scheduleId || !insertAfter) return;
6828
- const text = updateSectionScheduleId(insertAfter, scheduleId);
6829
- postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
6830
- };
6831
- const handleClearSchedulingWidget = (e) => {
6832
- if (e.data?.type !== "ow:clear-scheduling-widget") return;
6833
- const insertAfter = e.data.insertAfter;
6834
- if (!insertAfter) return;
6835
- const text = updateSectionScheduleId(insertAfter, null);
6836
- postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text }] });
6837
- };
6838
- const handleRemoveSchedulingSection = (e) => {
6839
- if (e.data?.type !== "ow:remove-scheduling-section") return;
6840
- document.querySelectorAll('[data-ohw-section-container="scheduling"]').forEach((el) => {
6841
- el.remove();
6842
- });
6843
- const tracker = getSectionsTracker();
6844
- let sections = [];
6845
- try {
6846
- sections = JSON.parse(tracker.textContent || "[]");
6847
- } catch {
6848
- }
6849
- const currentPath = window.location.pathname;
6850
- const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
6851
- tracker.textContent = JSON.stringify(updated);
6852
- postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
6853
- const h = document.documentElement.scrollHeight;
6854
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
5803
+ postToParentRef.current({ type: "ow:save-result", nodes: collectEditableNodes() });
6855
5804
  };
6856
5805
  const handleSelectionChange = () => {
6857
5806
  if (!activeElRef.current) return;
@@ -6956,11 +5905,6 @@ function OhhwellsBridge() {
6956
5905
  deactivateRef.current();
6957
5906
  };
6958
5907
  window.addEventListener("message", handleSave);
6959
- window.addEventListener("message", handleInsertSection);
6960
- window.addEventListener("message", handleSwitchSchedule);
6961
- window.addEventListener("message", handleScheduleLinked);
6962
- window.addEventListener("message", handleClearSchedulingWidget);
6963
- window.addEventListener("message", handleRemoveSchedulingSection);
6964
5908
  window.addEventListener("message", handleImageUrl);
6965
5909
  window.addEventListener("message", handleAnimLock);
6966
5910
  window.addEventListener("message", handleCanvasHeight);
@@ -6995,11 +5939,6 @@ function OhhwellsBridge() {
6995
5939
  document.removeEventListener("selectionchange", handleSelectionChange);
6996
5940
  window.removeEventListener("scroll", handleScroll, true);
6997
5941
  window.removeEventListener("message", handleSave);
6998
- window.removeEventListener("message", handleInsertSection);
6999
- window.removeEventListener("message", handleSwitchSchedule);
7000
- window.removeEventListener("message", handleScheduleLinked);
7001
- window.removeEventListener("message", handleClearSchedulingWidget);
7002
- window.removeEventListener("message", handleRemoveSchedulingSection);
7003
5942
  window.removeEventListener("message", handleImageUrl);
7004
5943
  window.removeEventListener("message", handleAnimLock);
7005
5944
  window.removeEventListener("message", handleCanvasHeight);
@@ -7014,7 +5953,7 @@ function OhhwellsBridge() {
7014
5953
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
7015
5954
  };
7016
5955
  }, [isEditMode, refreshStateRules]);
7017
- (0, import_react5.useEffect)(() => {
5956
+ (0, import_react3.useEffect)(() => {
7018
5957
  if (!isEditMode) return;
7019
5958
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
7020
5959
  el.removeAttribute("data-ohw-active-state");
@@ -7043,19 +5982,19 @@ function OhhwellsBridge() {
7043
5982
  clearTimeout(timer);
7044
5983
  };
7045
5984
  }, [pathname, isEditMode, refreshStateRules, postToParent]);
7046
- (0, import_react5.useEffect)(() => {
5985
+ (0, import_react3.useEffect)(() => {
7047
5986
  scrollToHashSectionWhenReady();
7048
5987
  const onHashChange = () => scrollToHashSectionWhenReady();
7049
5988
  window.addEventListener("hashchange", onHashChange);
7050
5989
  return () => window.removeEventListener("hashchange", onHashChange);
7051
5990
  }, [pathname]);
7052
- const handleCommand = (0, import_react5.useCallback)((cmd) => {
5991
+ const handleCommand = (0, import_react3.useCallback)((cmd) => {
7053
5992
  document.execCommand(cmd, false);
7054
5993
  activeElRef.current?.focus();
7055
5994
  if (activeElRef.current) setToolbarRect(activeElRef.current.getBoundingClientRect());
7056
5995
  refreshActiveCommandsRef.current();
7057
5996
  }, []);
7058
- const handleStateChange = (0, import_react5.useCallback)((state) => {
5997
+ const handleStateChange = (0, import_react3.useCallback)((state) => {
7059
5998
  if (!activeStateElRef.current) return;
7060
5999
  const el = activeStateElRef.current;
7061
6000
  if (state === "Default") {
@@ -7068,8 +6007,8 @@ function OhhwellsBridge() {
7068
6007
  }
7069
6008
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
7070
6009
  }, [deactivate]);
7071
- const closeLinkPopover = (0, import_react5.useCallback)(() => setLinkPopover(null), []);
7072
- const openLinkPopoverForActive = (0, import_react5.useCallback)(() => {
6010
+ const closeLinkPopover = (0, import_react3.useCallback)(() => setLinkPopover(null), []);
6011
+ const openLinkPopoverForActive = (0, import_react3.useCallback)(() => {
7073
6012
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
7074
6013
  if (!hrefCtx) return;
7075
6014
  bumpLinkPopoverGrace();
@@ -7079,7 +6018,7 @@ function OhhwellsBridge() {
7079
6018
  rect: hrefCtx.anchor.getBoundingClientRect()
7080
6019
  });
7081
6020
  }, []);
7082
- const handleLinkPopoverSubmit = (0, import_react5.useCallback)(
6021
+ const handleLinkPopoverSubmit = (0, import_react3.useCallback)(
7083
6022
  (target) => {
7084
6023
  if (!linkPopover) return;
7085
6024
  const { key } = linkPopover;
@@ -7092,11 +6031,11 @@ function OhhwellsBridge() {
7092
6031
  const showEditLink = toolbarShowEditLink;
7093
6032
  const currentSections = sectionsByPath[pathname] ?? [];
7094
6033
  linkPopoverOpenRef.current = linkPopover !== null;
7095
- return bridgeRoot ? (0, import_react_dom2.createPortal)(
7096
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
7097
- toolbarRect && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
7098
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
7099
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
6034
+ return bridgeRoot ? (0, import_react_dom.createPortal)(
6035
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
6036
+ toolbarRect && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
6037
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
6038
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7100
6039
  FloatingToolbar,
7101
6040
  {
7102
6041
  rect: toolbarRect,
@@ -7109,7 +6048,7 @@ function OhhwellsBridge() {
7109
6048
  }
7110
6049
  )
7111
6050
  ] }),
7112
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
6051
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7113
6052
  "div",
7114
6053
  {
7115
6054
  "data-ohw-max-badge": "",
@@ -7135,7 +6074,7 @@ function OhhwellsBridge() {
7135
6074
  ]
7136
6075
  }
7137
6076
  ),
7138
- toggleState && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
6077
+ toggleState && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7139
6078
  StateToggle,
7140
6079
  {
7141
6080
  rect: toggleState.rect,
@@ -7144,7 +6083,7 @@ function OhhwellsBridge() {
7144
6083
  onStateChange: handleStateChange
7145
6084
  }
7146
6085
  ),
7147
- linkPopover ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
6086
+ linkPopover ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7148
6087
  LinkPopover,
7149
6088
  {
7150
6089
  rect: linkPopover.rect,
@@ -7159,32 +6098,7 @@ function OhhwellsBridge() {
7159
6098
  onClose: closeLinkPopover,
7160
6099
  onSubmit: handleLinkPopoverSubmit
7161
6100
  }
7162
- ) : null,
7163
- sectionGap && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
7164
- "div",
7165
- {
7166
- "data-ohw-section-insert-line": "",
7167
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
7168
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
7169
- children: [
7170
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
7171
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7172
- Badge,
7173
- {
7174
- 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",
7175
- onClick: () => {
7176
- window.parent.postMessage(
7177
- { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
7178
- "*"
7179
- );
7180
- },
7181
- children: "Add Section"
7182
- }
7183
- ),
7184
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
7185
- ]
7186
- }
7187
- )
6101
+ ) : null
7188
6102
  ] }),
7189
6103
  bridgeRoot
7190
6104
  ) : null;
@@ -7194,7 +6108,6 @@ function OhhwellsBridge() {
7194
6108
  LinkEditorPanel,
7195
6109
  LinkPopover,
7196
6110
  OhhwellsBridge,
7197
- SchedulingWidget,
7198
6111
  Toggle,
7199
6112
  ToggleGroup,
7200
6113
  ToggleGroupItem,