@ohhwells/bridge 0.1.18 → 0.1.20

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
@@ -32,6 +32,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
34
  OhhwellsBridge: () => OhhwellsBridge,
35
+ SchedulingWidget: () => SchedulingWidget,
35
36
  Toggle: () => Toggle,
36
37
  ToggleGroup: () => ToggleGroup,
37
38
  ToggleGroupItem: () => ToggleGroupItem,
@@ -40,10 +41,670 @@ __export(index_exports, {
40
41
  module.exports = __toCommonJS(index_exports);
41
42
 
42
43
  // src/OhhwellsBridge.tsx
43
- var import_react = __toESM(require("react"), 1);
44
+ var import_react3 = __toESM(require("react"), 1);
45
+ var import_client = require("react-dom/client");
46
+
47
+ // src/ui/SchedulingWidget.tsx
48
+ var import_react2 = require("react");
49
+
50
+ // src/ui/EmailCaptureModal.tsx
51
+ var import_react = require("react");
52
+ var import_radix_ui = require("radix-ui");
53
+ var import_jsx_runtime = require("react/jsx-runtime");
54
+ function Spinner() {
55
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
56
+ "svg",
57
+ {
58
+ width: "32",
59
+ height: "32",
60
+ viewBox: "0 0 32 32",
61
+ fill: "none",
62
+ style: { animation: "ohw-spin 0.75s linear infinite" },
63
+ children: [
64
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `@keyframes ohw-spin { to { transform: rotate(360deg); } }` }),
65
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "16", cy: "16", r: "12", stroke: "#e5e7eb", strokeWidth: "3" }),
66
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
67
+ "path",
68
+ {
69
+ d: "M16 4a12 12 0 0 1 12 12",
70
+ stroke: "#3b82f6",
71
+ strokeWidth: "3",
72
+ strokeLinecap: "round"
73
+ }
74
+ )
75
+ ]
76
+ }
77
+ );
78
+ }
79
+ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
80
+ const [email, setEmail] = (0, import_react.useState)("");
81
+ const [submittedEmail, setSubmittedEmail] = (0, import_react.useState)("");
82
+ const [loading, setLoading] = (0, import_react.useState)(false);
83
+ const [success, setSuccess] = (0, import_react.useState)(false);
84
+ const [error, setError] = (0, import_react.useState)(null);
85
+ const isBook = title === "Confirm your spot";
86
+ const handleSubmit = async () => {
87
+ if (!email.trim()) return;
88
+ setLoading(true);
89
+ setError(null);
90
+ try {
91
+ await onSubmit(email.trim());
92
+ setSubmittedEmail(email.trim());
93
+ setSuccess(true);
94
+ } catch (e) {
95
+ setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
96
+ } finally {
97
+ setLoading(false);
98
+ }
99
+ };
100
+ const handleKeyDown = (e) => {
101
+ if (e.key === "Enter" && !loading) handleSubmit();
102
+ };
103
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_radix_ui.Dialog.Root, { open: true, onOpenChange: (open) => {
104
+ if (!open) onClose();
105
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_radix_ui.Dialog.Portal, { children: [
106
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
107
+ import_radix_ui.Dialog.Overlay,
108
+ {
109
+ className: "fixed inset-0 z-50",
110
+ style: { background: "rgba(0,0,0,0.45)" }
111
+ }
112
+ ),
113
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
114
+ import_radix_ui.Dialog.Content,
115
+ {
116
+ 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",
117
+ style: { maxWidth: 400 },
118
+ children: [
119
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-start justify-between gap-3 p-6", children: [
120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1", children: [
121
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
122
+ import_radix_ui.Dialog.Title,
123
+ {
124
+ className: "font-body text-xl font-bold m-0 leading-tight",
125
+ style: { color: "var(--color-dark,#200C02)" },
126
+ children: success ? isBook ? "Reservation confirmed!" : "You're on the waitlist!" : title
127
+ }
128
+ ),
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
130
+ import_radix_ui.Dialog.Description,
131
+ {
132
+ className: "font-body text-sm m-0",
133
+ style: { color: "#6B7280" },
134
+ children: success ? isBook ? `A confirmation email has been sent to ${submittedEmail}.` : "We'll let you know via email if someone cancels." : subtitle
135
+ }
136
+ )
137
+ ] }),
138
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
139
+ import_radix_ui.Dialog.Close,
140
+ {
141
+ 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",
142
+ style: { color: "#6B7280" },
143
+ "aria-label": "Close",
144
+ 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: [
145
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
146
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
147
+ ] })
148
+ }
149
+ )
150
+ ] }),
151
+ !success && (loading ? (
152
+ /* Loading state — full body replaced */
153
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col items-center justify-center gap-3 px-7 py-12", children: [
154
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}),
155
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-body text-sm m-0", style: { color: "#6B7280" }, children: "Please wait" })
156
+ ] })
157
+ ) : (
158
+ /* Email form */
159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "px-7 pt-2 pb-6", children: [
160
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1.5 mb-8", children: [
161
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "font-body text-sm font-medium", style: { color: "var(--color-dark,#200C02)" }, children: "Email" }),
162
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
163
+ "input",
164
+ {
165
+ type: "email",
166
+ value: email,
167
+ onChange: (e) => setEmail(e.target.value),
168
+ onKeyDown: handleKeyDown,
169
+ placeholder: "hello@gmail.com",
170
+ autoFocus: true,
171
+ className: "w-full h-10 px-3 rounded-lg border font-body text-sm outline-none box-border",
172
+ style: {
173
+ borderColor: "#E7E5E4",
174
+ background: "#fff",
175
+ color: "var(--color-dark,#200C02)"
176
+ }
177
+ }
178
+ ),
179
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-body text-xs text-red-500 m-0", children: error })
180
+ ] }),
181
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
182
+ "button",
183
+ {
184
+ onClick: handleSubmit,
185
+ disabled: !email.trim(),
186
+ className: "h-9 px-5 rounded-lg font-body text-sm font-semibold cursor-pointer border-none disabled:opacity-50 disabled:cursor-not-allowed",
187
+ style: {
188
+ background: "var(--color-primary,#3D312B)",
189
+ color: "var(--color-light,#FEFFF0)"
190
+ },
191
+ children: "Send"
192
+ }
193
+ ) })
194
+ ] })
195
+ ))
196
+ ]
197
+ }
198
+ )
199
+ ] }) });
200
+ }
201
+
202
+ // src/ui/SchedulingWidget.tsx
203
+ var import_jsx_runtime2 = require("react/jsx-runtime");
204
+ var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
205
+ var DAY_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
206
+ function getApiDomain() {
207
+ const h = window.location.hostname;
208
+ for (const base of ["ohhwells.site", "theflowops-staging.com", "theflowops.com"]) {
209
+ if (h.endsWith(`.${base}`)) return h.slice(0, -(base.length + 1));
210
+ }
211
+ return h;
212
+ }
213
+ function classRunsOnDate(cls, date, type) {
214
+ if (type === "FIXED") {
215
+ const d = String(date.getDate()).padStart(2, "0");
216
+ const m = String(date.getMonth() + 1).padStart(2, "0");
217
+ return cls.fixedDates?.some((fd) => fd.date === `${d}-${m}-${date.getFullYear()}`) ?? false;
218
+ }
219
+ return cls.weekdays?.some((w) => w.weekday === date.getDay()) ?? false;
220
+ }
221
+ function formatClassTime(cls) {
222
+ let h = parseInt(cls.startTime.hour, 10);
223
+ const m = parseInt(cls.startTime.minutes, 10);
224
+ if (cls.startTime.time === "PM" && h !== 12) h += 12;
225
+ if (cls.startTime.time === "AM" && h === 12) h = 0;
226
+ const endTotal = h * 60 + m + cls.duration;
227
+ const eh = Math.floor(endTotal / 60) % 24;
228
+ const em = endTotal % 60;
229
+ return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
230
+ }
231
+ function getBookingsOnDate(cls, date) {
232
+ if (!cls.bookings?.length) return 0;
233
+ const ds = date.toISOString().split("T")[0];
234
+ return cls.bookings.filter((b) => {
235
+ try {
236
+ return new Date(b.classDate).toISOString().split("T")[0] === ds;
237
+ } catch {
238
+ return false;
239
+ }
240
+ }).length;
241
+ }
242
+ function buildTimezoneLabel(tz) {
243
+ try {
244
+ const now = /* @__PURE__ */ new Date();
245
+ const timeStr = new Intl.DateTimeFormat("en-US", {
246
+ hour: "2-digit",
247
+ minute: "2-digit",
248
+ hour12: false,
249
+ timeZone: tz
250
+ }).format(now);
251
+ const offsetPart = new Intl.DateTimeFormat("en-US", { timeZoneName: "short", timeZone: tz }).formatToParts(now).find((p) => p.type === "timeZoneName")?.value ?? "";
252
+ const city = tz.split("/").pop()?.replace(/_/g, " ") ?? tz;
253
+ return `${timeStr} (${offsetPart}) ${city} time`;
254
+ } catch {
255
+ return tz;
256
+ }
257
+ }
258
+ function EmptyState({ inEditor }) {
259
+ const handleAddSchedule = () => {
260
+ if (inEditor) {
261
+ window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
262
+ }
263
+ };
264
+ 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: [
265
+ /* @__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)(
266
+ "svg",
267
+ {
268
+ width: "20",
269
+ height: "20",
270
+ viewBox: "0 0 24 24",
271
+ fill: "none",
272
+ stroke: "var(--color-accent, #A89B83)",
273
+ strokeWidth: "1.5",
274
+ strokeLinecap: "round",
275
+ strokeLinejoin: "round",
276
+ children: [
277
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
278
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
279
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
280
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "3", y1: "10", x2: "21", y2: "10" }),
281
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "14", x2: "12", y2: "18" }),
282
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "10", y1: "16", x2: "14", y2: "16" })
283
+ ]
284
+ }
285
+ ) }),
286
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-2", children: [
287
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-lg font-medium text-center text-[#0A0A0A] m-0", children: "No schedule yet" }),
288
+ /* @__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." })
289
+ ] }),
290
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
291
+ "button",
292
+ {
293
+ onClick: handleAddSchedule,
294
+ 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",
295
+ children: "Add Schedule"
296
+ }
297
+ )
298
+ ] });
299
+ }
300
+ function LoadingSkeleton() {
301
+ const shimmer = {
302
+ background: "linear-gradient(90deg,#f0f0f0 25%,#e8e8e8 50%,#f0f0f0 75%)",
303
+ backgroundSize: "200% 100%",
304
+ animation: "ohw-shimmer 1.5s infinite"
305
+ };
306
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col gap-10", children: [
307
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `@keyframes ohw-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}` }),
308
+ /* @__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: [
309
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "32px", height: "14px", borderRadius: "4px" } }),
310
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "40px", height: "40px", borderRadius: "50%" } })
311
+ ] }, i)) }),
312
+ [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
313
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-[60px] py-4", children: [
314
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "120px", height: "16px", borderRadius: "4px", flexShrink: 0 } }),
315
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2", children: [
316
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "55%", height: "16px", borderRadius: "4px" } }),
317
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "38%", height: "14px", borderRadius: "4px" } })
318
+ ] }),
319
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "56px", height: "32px", borderRadius: "4px", flexShrink: 0 } }),
320
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...shimmer, width: "200px", height: "44px", borderRadius: "8px", flexShrink: 0 } })
321
+ ] }),
322
+ i < 2 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-px bg-black/20" })
323
+ ] }, i))
324
+ ] });
325
+ }
326
+ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
327
+ const todayMs = (0, import_react2.useMemo)(() => {
328
+ const d = /* @__PURE__ */ new Date();
329
+ d.setHours(0, 0, 0, 0);
330
+ return d.getTime();
331
+ }, []);
332
+ const scrollRef = (0, import_react2.useRef)(null);
333
+ const [canScrollLeft, setCanScrollLeft] = (0, import_react2.useState)(false);
334
+ const [canScrollRight, setCanScrollRight] = (0, import_react2.useState)(false);
335
+ const updateScrollState = () => {
336
+ const el = scrollRef.current;
337
+ if (!el) return;
338
+ setCanScrollLeft(el.scrollLeft > 0);
339
+ setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
340
+ };
341
+ (0, import_react2.useEffect)(() => {
342
+ const el = scrollRef.current;
343
+ if (!el) return;
344
+ updateScrollState();
345
+ el.addEventListener("scroll", updateScrollState);
346
+ const ro = new ResizeObserver(updateScrollState);
347
+ ro.observe(el);
348
+ return () => {
349
+ el.removeEventListener("scroll", updateScrollState);
350
+ ro.disconnect();
351
+ };
352
+ }, [dates]);
353
+ const scroll = (dir) => {
354
+ scrollRef.current?.scrollBy({ left: dir === "left" ? -216 : 216, behavior: "smooth" });
355
+ };
356
+ const datesWithClasses = (0, import_react2.useMemo)(
357
+ () => new Set(dates.map(
358
+ (d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
359
+ ).filter((i) => i >= 0)),
360
+ [dates, schedule]
361
+ );
362
+ const selectedDate = dates[selectedIdx];
363
+ const selectedClasses = (0, import_react2.useMemo)(
364
+ () => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
365
+ [schedule, selectedDate]
366
+ );
367
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col gap-10", children: [
368
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "relative w-full", children: [
369
+ canScrollLeft && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
370
+ "button",
371
+ {
372
+ onClick: () => scroll("left"),
373
+ className: "absolute left-0 top-1/2 -translate-y-1/2 z-10 w-8 h-8 flex items-center justify-center rounded-full border-none cursor-pointer",
374
+ style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
375
+ "aria-label": "Scroll left",
376
+ 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" }) })
377
+ }
378
+ ),
379
+ canScrollRight && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
380
+ "button",
381
+ {
382
+ onClick: () => scroll("right"),
383
+ className: "absolute right-0 top-1/2 -translate-y-1/2 z-10 w-8 h-8 flex items-center justify-center rounded-full border-none cursor-pointer",
384
+ style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
385
+ "aria-label": "Scroll right",
386
+ 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" }) })
387
+ }
388
+ ),
389
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
390
+ "div",
391
+ {
392
+ ref: scrollRef,
393
+ className: "overflow-x-auto w-full",
394
+ style: {
395
+ scrollbarWidth: "none",
396
+ msOverflowStyle: "none",
397
+ paddingLeft: canScrollLeft ? "36px" : 0,
398
+ paddingRight: canScrollRight ? "36px" : 0
399
+ },
400
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex", style: { width: `max(100%, ${dates.length * 60}px)` }, children: dates.map((date, i) => {
401
+ const isToday = date.getTime() === todayMs;
402
+ const isSelected = i === selectedIdx;
403
+ const clickable = datesWithClasses.has(i);
404
+ const label = isToday ? "TODAY" : DAY_ABBR[date.getDay()];
405
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
406
+ "div",
407
+ {
408
+ onClick: () => clickable && onSelectDate(i),
409
+ className: `flex-1 h-[70px] flex flex-col justify-between items-center ${clickable ? "cursor-pointer opacity-100" : "cursor-default opacity-50"}`,
410
+ children: [
411
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-center text-(--color-dark,#200C02)", children: label }),
412
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
413
+ "div",
414
+ {
415
+ className: "w-10 h-10 rounded-full flex items-center justify-center",
416
+ style: { background: isSelected ? "var(--color-primary, #3D312B)" : "transparent" },
417
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
418
+ "span",
419
+ {
420
+ className: "font-body text-xl font-bold text-center tracking-display-tight",
421
+ style: { color: isSelected ? "var(--color-light, #FEFFF0)" : "var(--color-dark, #200C02)" },
422
+ children: date.getDate()
423
+ }
424
+ )
425
+ }
426
+ )
427
+ ]
428
+ },
429
+ i
430
+ );
431
+ }) })
432
+ }
433
+ )
434
+ ] }),
435
+ 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) => {
436
+ const booked = getBookingsOnDate(cls, selectedDate);
437
+ const available = cls.maxParticipants - booked;
438
+ const isFull = available <= 0;
439
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
440
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-[60px] py-4 box-border", children: [
441
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-[120px] 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) }) }),
442
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-3 min-w-0", children: [
443
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
444
+ 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 })
445
+ ] }),
446
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-14 shrink-0 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: [
447
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
448
+ available,
449
+ "/",
450
+ cls.maxParticipants
451
+ ] }),
452
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
453
+ ] }) }),
454
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
455
+ "button",
456
+ {
457
+ className: "shrink-0 w-[200px] px-5 py-[10px] flex items-center justify-center rounded-lg font-body text-base font-bold cursor-pointer box-border",
458
+ style: isFull ? {
459
+ background: "transparent",
460
+ border: "1px solid var(--color-dark, #200C02)",
461
+ color: "var(--color-dark, #200C02)"
462
+ } : {
463
+ background: "var(--color-primary, #3D312B)",
464
+ border: "none",
465
+ color: "var(--color-light, #FEFFF0)"
466
+ },
467
+ onClick: () => {
468
+ if (!cls.id) return;
469
+ onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
470
+ },
471
+ children: isFull ? "Join Waitlist" : "Book Now"
472
+ }
473
+ )
474
+ ] }),
475
+ i < selectedClasses.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-px bg-black/20" })
476
+ ] }, cls.id ?? i);
477
+ }) })
478
+ ] });
479
+ }
480
+ function CalendarFoldIcon() {
481
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
482
+ "svg",
483
+ {
484
+ width: "16",
485
+ height: "16",
486
+ viewBox: "0 0 24 24",
487
+ fill: "none",
488
+ stroke: "currentColor",
489
+ strokeWidth: "2",
490
+ strokeLinecap: "round",
491
+ strokeLinejoin: "round",
492
+ style: { flexShrink: 0 },
493
+ children: [
494
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 2v4" }),
495
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 2v4" }),
496
+ /* @__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" }),
497
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 10h18" }),
498
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M15 22v-4a2 2 0 0 1 2-2h4" })
499
+ ]
500
+ }
501
+ );
502
+ }
503
+ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
504
+ const [schedule, setSchedule] = (0, import_react2.useState)(null);
505
+ const [loading, setLoading] = (0, import_react2.useState)(true);
506
+ const [inEditor, setInEditor] = (0, import_react2.useState)(false);
507
+ const [isHovered, setIsHovered] = (0, import_react2.useState)(false);
508
+ const [modalState, setModalState] = (0, import_react2.useState)(null);
509
+ const switchScheduleIdRef = (0, import_react2.useRef)(null);
510
+ const dates = (0, import_react2.useMemo)(() => {
511
+ const today = /* @__PURE__ */ new Date();
512
+ today.setHours(0, 0, 0, 0);
513
+ return Array.from({ length: 14 }, (_, i) => {
514
+ const d = new Date(today);
515
+ d.setDate(today.getDate() + i);
516
+ return d;
517
+ });
518
+ }, []);
519
+ const [selectedIdx, setSelectedIdx] = (0, import_react2.useState)(0);
520
+ (0, import_react2.useEffect)(() => {
521
+ if (!schedule?.classes) return;
522
+ for (let i = 0; i < dates.length; i++) {
523
+ if (schedule.classes.some((c) => classRunsOnDate(c, dates[i], schedule.type))) {
524
+ setSelectedIdx(i);
525
+ return;
526
+ }
527
+ }
528
+ }, [schedule, dates]);
529
+ (0, import_react2.useEffect)(() => {
530
+ if (typeof window === "undefined") return;
531
+ const isInEditor = window.self !== window.top;
532
+ setInEditor(isInEditor);
533
+ if (isInEditor) {
534
+ const startEmpty = initialScheduleId === null;
535
+ if (startEmpty) setLoading(false);
536
+ let initialized = startEmpty;
537
+ const timer = !startEmpty ? setTimeout(() => {
538
+ if (!initialized) {
539
+ initialized = true;
540
+ setLoading(false);
541
+ }
542
+ }, 5e3) : null;
543
+ const handler = (e) => {
544
+ if (e.data?.type === "ow:clear-scheduling-widget") {
545
+ setSchedule(null);
546
+ setLoading(false);
547
+ return;
548
+ }
549
+ if (e.data?.type === "ow:switch-schedule") {
550
+ switchScheduleIdRef.current = e.data.scheduleId ?? null;
551
+ initialized = false;
552
+ setLoading(true);
553
+ window.parent.postMessage({ type: "ow:request-user-schedules" }, "*");
554
+ return;
555
+ }
556
+ if (e.data?.type !== "ow:user-schedules-response") return;
557
+ if (initialized && switchScheduleIdRef.current === null) return;
558
+ initialized = true;
559
+ if (timer) clearTimeout(timer);
560
+ const schedules = e.data.schedules ?? [];
561
+ const isSwitching = switchScheduleIdRef.current !== null;
562
+ 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;
563
+ switchScheduleIdRef.current = null;
564
+ setSchedule(target);
565
+ setLoading(false);
566
+ if (notifyOnConnect && target && !isSwitching) {
567
+ window.parent.postMessage({ type: "ow:schedule-connected", schedule: { id: target.id, name: target.name } }, "*");
568
+ }
569
+ };
570
+ window.addEventListener("message", handler);
571
+ if (!startEmpty) window.parent.postMessage({ type: "ow:request-user-schedules" }, "*");
572
+ return () => {
573
+ window.removeEventListener("message", handler);
574
+ if (timer) clearTimeout(timer);
575
+ };
576
+ }
577
+ if (initialScheduleId === null) {
578
+ setLoading(false);
579
+ return;
580
+ }
581
+ ;
582
+ (async () => {
583
+ try {
584
+ if (initialScheduleId) {
585
+ const res = await fetch(`${API_URL}/api/schedule/id/${initialScheduleId}`);
586
+ const data = await res.json();
587
+ setSchedule(data?.id ? data : null);
588
+ } else {
589
+ const domain = getApiDomain();
590
+ const sitemapRes = await fetch(`${API_URL}/api/schedule/sitemap/${domain}`);
591
+ const slugs = await sitemapRes.json();
592
+ if (!Array.isArray(slugs) || !slugs.length) {
593
+ setLoading(false);
594
+ return;
595
+ }
596
+ const { slug } = [...slugs].sort(
597
+ (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
598
+ )[0];
599
+ const res = await fetch(`${API_URL}/api/schedule/${domain}/${slug}`);
600
+ const data = await res.json();
601
+ setSchedule(data?.id ? data : null);
602
+ }
603
+ } catch {
604
+ }
605
+ setLoading(false);
606
+ })();
607
+ }, []);
608
+ const timezoneLabel = schedule?.timezone ? (() => {
609
+ try {
610
+ return buildTimezoneLabel(schedule.timezone);
611
+ } catch {
612
+ return void 0;
613
+ }
614
+ })() : void 0;
615
+ const handleModalSubmit = async (email) => {
616
+ if (!modalState) return;
617
+ const { mode, classId, classDate } = modalState;
618
+ const endpoint = mode === "book" ? `${API_URL}/api/schedule/class/${classId}/booking` : `${API_URL}/api/schedule/class/${classId}/waitlist`;
619
+ const res = await fetch(endpoint, {
620
+ method: "POST",
621
+ headers: { "Content-Type": "application/json" },
622
+ body: JSON.stringify({ classDate: classDate.toISOString(), email })
623
+ });
624
+ if (!res.ok) {
625
+ const data = await res.json().catch(() => ({}));
626
+ throw new Error(data?.message ?? "Something went wrong. Please try again.");
627
+ }
628
+ };
629
+ const handleReplaceSchedule = () => {
630
+ setIsHovered(false);
631
+ if (schedule) {
632
+ window.parent.postMessage({ type: "ow:schedule-connected", schedule: { id: schedule.id, name: schedule.name } }, "*");
633
+ } else {
634
+ window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
635
+ }
636
+ };
637
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
638
+ "section",
639
+ {
640
+ "data-ohw-section": "scheduling",
641
+ className: "w-full box-border py-20 px-16 font-body bg-(--color-light,#FEFFF0) relative",
642
+ onMouseEnter: () => inEditor && setIsHovered(true),
643
+ onMouseLeave: () => setIsHovered(false),
644
+ children: [
645
+ inEditor && isHovered && !!schedule && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
646
+ "div",
647
+ {
648
+ className: "absolute inset-0 z-10 pointer-events-auto cursor-pointer",
649
+ onClick: handleReplaceSchedule,
650
+ children: [
651
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute inset-0", style: { background: "#0885FE", opacity: 0.18 } }),
652
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute inset-0", style: { boxShadow: "inset 0 0 0 1.5px #0885FE" } }),
653
+ /* @__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)(
654
+ "div",
655
+ {
656
+ className: "bg-white border border-[#E7E5E4] rounded-md px-3 py-1.5 flex items-center gap-1.5",
657
+ style: {
658
+ fontSize: 14,
659
+ lineHeight: "24px",
660
+ fontFamily: "'Figtree', system-ui, sans-serif",
661
+ fontWeight: 500,
662
+ color: "#0C0A09"
663
+ },
664
+ children: [
665
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CalendarFoldIcon, {}),
666
+ "Replace schedule"
667
+ ]
668
+ }
669
+ ) })
670
+ ]
671
+ }
672
+ ),
673
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
674
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
675
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { className: "font-display text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
676
+ 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 })
677
+ ] }),
678
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
679
+ timezoneLabel && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
680
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-full 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)(
681
+ ScheduleView,
682
+ {
683
+ schedule,
684
+ dates,
685
+ selectedIdx,
686
+ onSelectDate: setSelectedIdx,
687
+ onOpenModal: inEditor ? void 0 : (mode, classId, classDate) => setModalState({ mode, classId, classDate })
688
+ }
689
+ ) })
690
+ ] })
691
+ ] }),
692
+ modalState && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
693
+ EmailCaptureModal,
694
+ {
695
+ title: modalState.mode === "book" ? "Confirm your spot" : "Leave your email",
696
+ subtitle: modalState.mode === "book" ? "We'll send your booking confirmation here." : "We'll let you know when spots open up!",
697
+ onSubmit: handleModalSubmit,
698
+ onClose: () => setModalState(null)
699
+ }
700
+ )
701
+ ]
702
+ }
703
+ );
704
+ }
44
705
 
45
706
  // src/ui/toggle-group.tsx
46
- var React = __toESM(require("react"), 1);
707
+ var React2 = __toESM(require("react"), 1);
47
708
 
48
709
  // node_modules/clsx/dist/clsx.mjs
49
710
  function r(e) {
@@ -3362,8 +4023,8 @@ var cva = (base, config) => (props) => {
3362
4023
  };
3363
4024
 
3364
4025
  // src/ui/toggle.tsx
3365
- var import_radix_ui = require("radix-ui");
3366
- var import_jsx_runtime = require("react/jsx-runtime");
4026
+ var import_radix_ui2 = require("radix-ui");
4027
+ var import_jsx_runtime3 = require("react/jsx-runtime");
3367
4028
  var toggleVariants = cva(
3368
4029
  "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",
3369
4030
  {
@@ -3390,8 +4051,8 @@ function Toggle({
3390
4051
  size,
3391
4052
  ...props
3392
4053
  }) {
3393
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3394
- import_radix_ui.Toggle.Root,
4054
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4055
+ import_radix_ui2.Toggle.Root,
3395
4056
  {
3396
4057
  "data-slot": "toggle",
3397
4058
  className: cn(toggleVariants({ variant, size, className })),
@@ -3401,8 +4062,8 @@ function Toggle({
3401
4062
  }
3402
4063
 
3403
4064
  // src/ui/toggle-group.tsx
3404
- var import_jsx_runtime2 = require("react/jsx-runtime");
3405
- var ToggleGroupContext = React.createContext({
4065
+ var import_jsx_runtime4 = require("react/jsx-runtime");
4066
+ var ToggleGroupContext = React2.createContext({
3406
4067
  value: "",
3407
4068
  onValueChange: () => {
3408
4069
  }
@@ -3414,13 +4075,13 @@ function ToggleGroup({
3414
4075
  children,
3415
4076
  ...props
3416
4077
  }) {
3417
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
4078
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3418
4079
  "div",
3419
4080
  {
3420
4081
  role: "group",
3421
4082
  className: cn("flex items-center gap-1 p-1 bg-muted rounded-md", className),
3422
4083
  ...props,
3423
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
4084
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
3424
4085
  }
3425
4086
  );
3426
4087
  }
@@ -3430,8 +4091,8 @@ function ToggleGroupItem({
3430
4091
  children,
3431
4092
  ...props
3432
4093
  }) {
3433
- const { value: groupValue, onValueChange } = React.useContext(ToggleGroupContext);
3434
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
4094
+ const { value: groupValue, onValueChange } = React2.useContext(ToggleGroupContext);
4095
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3435
4096
  Toggle,
3436
4097
  {
3437
4098
  pressed: groupValue === value,
@@ -3473,7 +4134,7 @@ function isEditSessionActive() {
3473
4134
  }
3474
4135
 
3475
4136
  // src/ui/badge.tsx
3476
- var import_jsx_runtime3 = require("react/jsx-runtime");
4137
+ var import_jsx_runtime5 = require("react/jsx-runtime");
3477
4138
  var badgeVariants = cva(
3478
4139
  "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",
3479
4140
  {
@@ -3491,11 +4152,11 @@ var badgeVariants = cva(
3491
4152
  }
3492
4153
  );
3493
4154
  function Badge({ className, variant, ...props }) {
3494
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
4155
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
3495
4156
  }
3496
4157
 
3497
4158
  // src/OhhwellsBridge.tsx
3498
- var import_jsx_runtime4 = require("react/jsx-runtime");
4159
+ var import_jsx_runtime6 = require("react/jsx-runtime");
3499
4160
  var PRIMARY = "#0885FE";
3500
4161
  var IMAGE_FADE_MS = 300;
3501
4162
  function runOpacityFade(el, onDone) {
@@ -3546,6 +4207,41 @@ function fadeInBgImage(el, url, onReady) {
3546
4207
  if (!prevPos || prevPos === "static") el.style.position = prevPos;
3547
4208
  });
3548
4209
  }
4210
+ function getSectionsTracker() {
4211
+ let el = document.querySelector("[data-ohw-sections-tracker]");
4212
+ if (!el) {
4213
+ el = document.createElement("div");
4214
+ el.setAttribute("data-ohw-sections-tracker", "");
4215
+ el.style.display = "none";
4216
+ document.body.appendChild(el);
4217
+ }
4218
+ return el;
4219
+ }
4220
+ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId) {
4221
+ const anchor = document.querySelector(`[data-ohw-section="${insertAfter}"]`);
4222
+ if (!anchor) return false;
4223
+ if (anchor.nextElementSibling?.dataset.ohwSectionContainer) return false;
4224
+ const container = document.createElement("div");
4225
+ container.dataset.ohwSectionContainer = "scheduling";
4226
+ anchor.insertAdjacentElement("afterend", container);
4227
+ (0, import_client.createRoot)(container).render(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SchedulingWidget, { notifyOnConnect, initialScheduleId: scheduleId }));
4228
+ const tracker = getSectionsTracker();
4229
+ let sections = [];
4230
+ try {
4231
+ sections = JSON.parse(tracker.textContent || "[]");
4232
+ } catch {
4233
+ }
4234
+ if (!sections.find((s) => s.type === "scheduling" && s.insertAfter === insertAfter)) {
4235
+ sections.push({
4236
+ type: "scheduling",
4237
+ insertAfter,
4238
+ pagePath: window.location.pathname,
4239
+ ...scheduleId ? { scheduleId } : {}
4240
+ });
4241
+ tracker.textContent = JSON.stringify(sections);
4242
+ }
4243
+ return true;
4244
+ }
3549
4245
  function collectEditableNodes() {
3550
4246
  return Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
3551
4247
  if (el.dataset.ohwEditable === "image") {
@@ -3703,7 +4399,7 @@ var TOOLBAR_GROUPS = [
3703
4399
  ];
3704
4400
  function GlowFrame({ rect, elRef }) {
3705
4401
  const GAP = 6;
3706
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4402
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3707
4403
  "div",
3708
4404
  {
3709
4405
  ref: elRef,
@@ -3754,7 +4450,7 @@ function FloatingToolbar({
3754
4450
  activeCommands
3755
4451
  }) {
3756
4452
  const { top, left, transform } = calcToolbarPos(rect, parentScroll);
3757
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4453
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3758
4454
  "div",
3759
4455
  {
3760
4456
  ref: elRef,
@@ -3777,11 +4473,11 @@ function FloatingToolbar({
3777
4473
  pointerEvents: "auto",
3778
4474
  whiteSpace: "nowrap"
3779
4475
  },
3780
- children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react.default.Fragment, { children: [
3781
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
4476
+ children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react3.default.Fragment, { children: [
4477
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
3782
4478
  btns.map((btn) => {
3783
4479
  const isActive = activeCommands.has(btn.cmd);
3784
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4480
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3785
4481
  "button",
3786
4482
  {
3787
4483
  title: btn.title,
@@ -3807,7 +4503,7 @@ function FloatingToolbar({
3807
4503
  flexShrink: 0,
3808
4504
  padding: 6
3809
4505
  },
3810
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4506
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3811
4507
  "svg",
3812
4508
  {
3813
4509
  width: "16",
@@ -3840,7 +4536,7 @@ function StateToggle({
3840
4536
  states,
3841
4537
  onStateChange
3842
4538
  }) {
3843
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4539
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3844
4540
  ToggleGroup,
3845
4541
  {
3846
4542
  "data-ohw-state-toggle": "",
@@ -3854,7 +4550,7 @@ function StateToggle({
3854
4550
  left: rect.right - 8,
3855
4551
  transform: "translateX(-100%)"
3856
4552
  },
3857
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
4553
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
3858
4554
  }
3859
4555
  );
3860
4556
  }
@@ -3864,8 +4560,8 @@ function OhhwellsBridge() {
3864
4560
  const router = (0, import_navigation.useRouter)();
3865
4561
  const searchParams = (0, import_navigation.useSearchParams)();
3866
4562
  const isEditMode = isEditSessionActive();
3867
- const [bridgeRoot, setBridgeRoot] = (0, import_react.useState)(null);
3868
- (0, import_react.useEffect)(() => {
4563
+ const [bridgeRoot, setBridgeRoot] = (0, import_react3.useState)(null);
4564
+ (0, import_react3.useEffect)(() => {
3869
4565
  const figtreeFontId = "ohw-figtree-font";
3870
4566
  if (!document.getElementById(figtreeFontId)) {
3871
4567
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -3893,39 +4589,39 @@ function OhhwellsBridge() {
3893
4589
  const parts = window.location.hostname.split(".");
3894
4590
  return parts.length >= 3 && parts[0] !== "www" ? parts[0] : "";
3895
4591
  })();
3896
- const postToParent = (0, import_react.useCallback)((data) => {
4592
+ const postToParent = (0, import_react3.useCallback)((data) => {
3897
4593
  if (typeof window !== "undefined" && window.parent !== window) {
3898
4594
  window.parent.postMessage(data, "*");
3899
4595
  }
3900
4596
  }, []);
3901
- const [fetchState, setFetchState] = (0, import_react.useState)("idle");
3902
- const autoSaveTimers = (0, import_react.useRef)(/* @__PURE__ */ new Map());
3903
- const activeElRef = (0, import_react.useRef)(null);
3904
- const originalContentRef = (0, import_react.useRef)(null);
3905
- const activeStateElRef = (0, import_react.useRef)(null);
3906
- const parentScrollRef = (0, import_react.useRef)(null);
3907
- const toolbarElRef = (0, import_react.useRef)(null);
3908
- const glowElRef = (0, import_react.useRef)(null);
3909
- const hoveredImageRef = (0, import_react.useRef)(null);
3910
- const hoveredImageHasTextOverlapRef = (0, import_react.useRef)(false);
3911
- const hoveredGapRef = (0, import_react.useRef)(null);
3912
- const imageUnhoverTimerRef = (0, import_react.useRef)(null);
3913
- const imageShowTimerRef = (0, import_react.useRef)(null);
3914
- const editStylesRef = (0, import_react.useRef)(null);
3915
- const activateRef = (0, import_react.useRef)(() => {
4597
+ const [fetchState, setFetchState] = (0, import_react3.useState)("idle");
4598
+ const autoSaveTimers = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
4599
+ const activeElRef = (0, import_react3.useRef)(null);
4600
+ const originalContentRef = (0, import_react3.useRef)(null);
4601
+ const activeStateElRef = (0, import_react3.useRef)(null);
4602
+ const parentScrollRef = (0, import_react3.useRef)(null);
4603
+ const toolbarElRef = (0, import_react3.useRef)(null);
4604
+ const glowElRef = (0, import_react3.useRef)(null);
4605
+ const hoveredImageRef = (0, import_react3.useRef)(null);
4606
+ const hoveredImageHasTextOverlapRef = (0, import_react3.useRef)(false);
4607
+ const hoveredGapRef = (0, import_react3.useRef)(null);
4608
+ const imageUnhoverTimerRef = (0, import_react3.useRef)(null);
4609
+ const imageShowTimerRef = (0, import_react3.useRef)(null);
4610
+ const editStylesRef = (0, import_react3.useRef)(null);
4611
+ const activateRef = (0, import_react3.useRef)(() => {
3916
4612
  });
3917
- const deactivateRef = (0, import_react.useRef)(() => {
4613
+ const deactivateRef = (0, import_react3.useRef)(() => {
3918
4614
  });
3919
- const refreshActiveCommandsRef = (0, import_react.useRef)(() => {
4615
+ const refreshActiveCommandsRef = (0, import_react3.useRef)(() => {
3920
4616
  });
3921
- const postToParentRef = (0, import_react.useRef)(postToParent);
4617
+ const postToParentRef = (0, import_react3.useRef)(postToParent);
3922
4618
  postToParentRef.current = postToParent;
3923
- const [toolbarRect, setToolbarRect] = (0, import_react.useState)(null);
3924
- const [toggleState, setToggleState] = (0, import_react.useState)(null);
3925
- const [maxBadge, setMaxBadge] = (0, import_react.useState)(null);
3926
- const [activeCommands, setActiveCommands] = (0, import_react.useState)(/* @__PURE__ */ new Set());
3927
- const [sectionGap, setSectionGap] = (0, import_react.useState)(null);
3928
- (0, import_react.useEffect)(() => {
4619
+ const [toolbarRect, setToolbarRect] = (0, import_react3.useState)(null);
4620
+ const [toggleState, setToggleState] = (0, import_react3.useState)(null);
4621
+ const [maxBadge, setMaxBadge] = (0, import_react3.useState)(null);
4622
+ const [activeCommands, setActiveCommands] = (0, import_react3.useState)(/* @__PURE__ */ new Set());
4623
+ const [sectionGap, setSectionGap] = (0, import_react3.useState)(null);
4624
+ (0, import_react3.useEffect)(() => {
3929
4625
  const update = () => {
3930
4626
  const el = activeElRef.current;
3931
4627
  if (el) setToolbarRect(el.getBoundingClientRect());
@@ -3949,10 +4645,10 @@ function OhhwellsBridge() {
3949
4645
  vvp.removeEventListener("resize", update);
3950
4646
  };
3951
4647
  }, []);
3952
- const refreshStateRules = (0, import_react.useCallback)(() => {
4648
+ const refreshStateRules = (0, import_react3.useCallback)(() => {
3953
4649
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
3954
4650
  }, []);
3955
- const deactivate = (0, import_react.useCallback)(() => {
4651
+ const deactivate = (0, import_react3.useCallback)(() => {
3956
4652
  const el = activeElRef.current;
3957
4653
  if (!el) return;
3958
4654
  const key = el.dataset.ohwKey;
@@ -3977,7 +4673,7 @@ function OhhwellsBridge() {
3977
4673
  setActiveCommands(/* @__PURE__ */ new Set());
3978
4674
  postToParent({ type: "ow:exit-edit" });
3979
4675
  }, [postToParent]);
3980
- const activate = (0, import_react.useCallback)((el) => {
4676
+ const activate = (0, import_react3.useCallback)((el) => {
3981
4677
  if (activeElRef.current === el) return;
3982
4678
  deactivate();
3983
4679
  if (hoveredImageRef.current) {
@@ -3995,14 +4691,27 @@ function OhhwellsBridge() {
3995
4691
  }, [deactivate, postToParent]);
3996
4692
  activateRef.current = activate;
3997
4693
  deactivateRef.current = deactivate;
3998
- (0, import_react.useLayoutEffect)(() => {
4694
+ (0, import_react3.useLayoutEffect)(() => {
3999
4695
  if (!subdomain || isEditMode) {
4000
4696
  setFetchState("done");
4001
4697
  return;
4002
4698
  }
4003
4699
  const applyContent = (content) => {
4004
4700
  const imageLoads = [];
4701
+ document.querySelectorAll("[data-ohw-section-container]").forEach((el) => el.remove());
4702
+ if (content["__ohw_sections"]) {
4703
+ try {
4704
+ const entries = JSON.parse(content["__ohw_sections"]);
4705
+ const currentPath = window.location.pathname;
4706
+ entries.forEach(({ type, insertAfter, scheduleId, pagePath }) => {
4707
+ if (pagePath && pagePath !== currentPath) return;
4708
+ if (type === "scheduling") mountSchedulingWidget(insertAfter, false, scheduleId);
4709
+ });
4710
+ } catch {
4711
+ }
4712
+ }
4005
4713
  for (const [key, val] of Object.entries(content)) {
4714
+ if (key === "__ohw_sections") continue;
4006
4715
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
4007
4716
  if (el.dataset.ohwEditable === "image") {
4008
4717
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -4045,12 +4754,24 @@ function OhhwellsBridge() {
4045
4754
  cancelled = true;
4046
4755
  };
4047
4756
  }, [subdomain, isEditMode, pathname]);
4048
- (0, import_react.useEffect)(() => {
4757
+ (0, import_react3.useEffect)(() => {
4049
4758
  if (!subdomain || isEditMode) return;
4050
4759
  const applyFromCache = () => {
4051
4760
  const content = contentCache.get(subdomain);
4052
4761
  if (!content) return;
4762
+ if (content["__ohw_sections"]) {
4763
+ try {
4764
+ const entries = JSON.parse(content["__ohw_sections"]);
4765
+ const currentPath = window.location.pathname;
4766
+ entries.forEach(({ type, insertAfter, scheduleId, pagePath }) => {
4767
+ if (pagePath && pagePath !== currentPath) return;
4768
+ if (type === "scheduling") mountSchedulingWidget(insertAfter, false, scheduleId);
4769
+ });
4770
+ } catch {
4771
+ }
4772
+ }
4053
4773
  for (const [key, val] of Object.entries(content)) {
4774
+ if (key === "__ohw_sections") continue;
4054
4775
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
4055
4776
  if (el.dataset.ohwEditable === "image") {
4056
4777
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -4069,16 +4790,16 @@ function OhhwellsBridge() {
4069
4790
  observer.observe(document.body, { childList: true, subtree: true });
4070
4791
  return () => observer.disconnect();
4071
4792
  }, [subdomain, isEditMode]);
4072
- (0, import_react.useLayoutEffect)(() => {
4793
+ (0, import_react3.useLayoutEffect)(() => {
4073
4794
  const el = document.getElementById("ohw-loader");
4074
4795
  if (!el) return;
4075
4796
  const visible = Boolean(subdomain) && fetchState !== "done";
4076
4797
  el.style.display = visible ? "flex" : "none";
4077
4798
  }, [subdomain, fetchState]);
4078
- (0, import_react.useEffect)(() => {
4799
+ (0, import_react3.useEffect)(() => {
4079
4800
  postToParent({ type: "ow:navigation", path: pathname });
4080
4801
  }, [pathname, postToParent]);
4081
- (0, import_react.useEffect)(() => {
4802
+ (0, import_react3.useEffect)(() => {
4082
4803
  if (!isEditMode) return;
4083
4804
  const measure = () => {
4084
4805
  const h = document.body.scrollHeight;
@@ -4102,7 +4823,7 @@ function OhhwellsBridge() {
4102
4823
  window.removeEventListener("resize", handleResize);
4103
4824
  };
4104
4825
  }, [pathname, isEditMode, postToParent]);
4105
- (0, import_react.useEffect)(() => {
4826
+ (0, import_react3.useEffect)(() => {
4106
4827
  if (!subdomainFromQuery || isEditMode) return;
4107
4828
  const handleClick = (e) => {
4108
4829
  const anchor = e.target.closest("a");
@@ -4118,7 +4839,7 @@ function OhhwellsBridge() {
4118
4839
  document.addEventListener("click", handleClick, true);
4119
4840
  return () => document.removeEventListener("click", handleClick, true);
4120
4841
  }, [subdomainFromQuery, isEditMode, router]);
4121
- (0, import_react.useEffect)(() => {
4842
+ (0, import_react3.useEffect)(() => {
4122
4843
  if (!isEditMode) {
4123
4844
  editStylesRef.current?.base.remove();
4124
4845
  editStylesRef.current?.forceHover.remove();
@@ -4378,6 +5099,15 @@ function OhhwellsBridge() {
4378
5099
  textEditable.setAttribute("data-ohw-hovered", "");
4379
5100
  return;
4380
5101
  }
5102
+ if (hoveredGapRef.current) {
5103
+ if (hoveredImageRef.current) {
5104
+ hoveredImageRef.current = null;
5105
+ resumeAnimTracks();
5106
+ postToParentRef.current({ type: "ow:image-unhover" });
5107
+ }
5108
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
5109
+ return;
5110
+ }
4381
5111
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
4382
5112
  if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
4383
5113
  hoveredImageRef.current = imgEl;
@@ -4466,17 +5196,17 @@ function OhhwellsBridge() {
4466
5196
  };
4467
5197
  const handleMouseMove = (e) => {
4468
5198
  const { clientX, clientY } = e;
5199
+ probeSectionGapAt(clientX, clientY);
4469
5200
  probeImageAt(clientX, clientY);
4470
5201
  probeHoverCardsAt(clientX, clientY);
4471
- probeSectionGapAt(clientX, clientY);
4472
5202
  };
4473
5203
  const handlePointerSync = (e) => {
4474
5204
  if (e.data?.type !== "ow:pointer-sync") return;
4475
5205
  const { clientX, clientY } = e.data;
4476
5206
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
5207
+ probeSectionGapAt(clientX, clientY);
4477
5208
  probeImageAt(clientX, clientY);
4478
5209
  probeHoverCardsAt(clientX, clientY);
4479
- probeSectionGapAt(clientX, clientY);
4480
5210
  };
4481
5211
  const handleDragOver = (e) => {
4482
5212
  e.preventDefault();
@@ -4633,6 +5363,18 @@ function OhhwellsBridge() {
4633
5363
  const content = e.data.content;
4634
5364
  if (!content) return;
4635
5365
  for (const [key, val] of Object.entries(content)) {
5366
+ if (key === "__ohw_sections") {
5367
+ try {
5368
+ const entries = JSON.parse(val);
5369
+ const tracker = getSectionsTracker();
5370
+ tracker.textContent = val;
5371
+ entries.forEach(({ type, insertAfter, scheduleId }) => {
5372
+ if (type === "scheduling") mountSchedulingWidget(insertAfter, false, scheduleId);
5373
+ });
5374
+ } catch {
5375
+ }
5376
+ continue;
5377
+ }
4636
5378
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
4637
5379
  if (el.dataset.ohwEditable === "image") {
4638
5380
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -4687,7 +5429,71 @@ function OhhwellsBridge() {
4687
5429
  };
4688
5430
  const handleSave = (e) => {
4689
5431
  if (e.data?.type !== "ow:save") return;
4690
- postToParentRef.current({ type: "ow:save-result", nodes: collectEditableNodes() });
5432
+ const nodes = collectEditableNodes();
5433
+ const tracker = document.querySelector("[data-ohw-sections-tracker]");
5434
+ if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
5435
+ postToParentRef.current({ type: "ow:save-result", nodes });
5436
+ };
5437
+ const handleInsertSection = (e) => {
5438
+ if (e.data?.type !== "ow:insert-section") return;
5439
+ const { widgetType, insertAfter } = e.data;
5440
+ if (widgetType !== "scheduling") return;
5441
+ const inserted = mountSchedulingWidget(insertAfter, true);
5442
+ if (inserted) {
5443
+ const tracker = getSectionsTracker();
5444
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
5445
+ const h = document.documentElement.scrollHeight;
5446
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
5447
+ }
5448
+ };
5449
+ const handleSwitchSchedule = (e) => {
5450
+ if (e.data?.type !== "ow:switch-schedule") return;
5451
+ const scheduleId = e.data.scheduleId;
5452
+ if (!scheduleId) return;
5453
+ const tracker = getSectionsTracker();
5454
+ let sections = [];
5455
+ try {
5456
+ sections = JSON.parse(tracker.textContent || "[]");
5457
+ } catch {
5458
+ }
5459
+ const currentPath = window.location.pathname;
5460
+ const updated = sections.map(
5461
+ (s) => s.type === "scheduling" && s.pagePath === currentPath ? { ...s, scheduleId } : s
5462
+ );
5463
+ tracker.textContent = JSON.stringify(updated);
5464
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
5465
+ };
5466
+ const handleClearSchedulingWidget = (e) => {
5467
+ if (e.data?.type !== "ow:clear-scheduling-widget") return;
5468
+ const tracker = getSectionsTracker();
5469
+ let sections = [];
5470
+ try {
5471
+ sections = JSON.parse(tracker.textContent || "[]");
5472
+ } catch {
5473
+ }
5474
+ const currentPath = window.location.pathname;
5475
+ const updated = sections.map(
5476
+ (s) => s.type === "scheduling" && s.pagePath === currentPath ? { ...s, scheduleId: null } : s
5477
+ );
5478
+ tracker.textContent = JSON.stringify(updated);
5479
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
5480
+ };
5481
+ const handleRemoveSchedulingSection = (e) => {
5482
+ if (e.data?.type !== "ow:remove-scheduling-section") return;
5483
+ const els = document.querySelectorAll('[data-ohw-section="scheduling"]');
5484
+ els.forEach((el) => el.parentElement?.removeChild(el));
5485
+ const tracker = getSectionsTracker();
5486
+ let sections = [];
5487
+ try {
5488
+ sections = JSON.parse(tracker.textContent || "[]");
5489
+ } catch {
5490
+ }
5491
+ const currentPath = window.location.pathname;
5492
+ const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
5493
+ tracker.textContent = JSON.stringify(updated);
5494
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
5495
+ const h = document.documentElement.scrollHeight;
5496
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
4691
5497
  };
4692
5498
  const handleSelectionChange = () => {
4693
5499
  if (!activeElRef.current) return;
@@ -4792,6 +5598,10 @@ function OhhwellsBridge() {
4792
5598
  deactivateRef.current();
4793
5599
  };
4794
5600
  window.addEventListener("message", handleSave);
5601
+ window.addEventListener("message", handleInsertSection);
5602
+ window.addEventListener("message", handleSwitchSchedule);
5603
+ window.addEventListener("message", handleClearSchedulingWidget);
5604
+ window.addEventListener("message", handleRemoveSchedulingSection);
4795
5605
  window.addEventListener("message", handleImageUrl);
4796
5606
  window.addEventListener("message", handleAnimLock);
4797
5607
  window.addEventListener("message", handleCanvasHeight);
@@ -4826,6 +5636,10 @@ function OhhwellsBridge() {
4826
5636
  document.removeEventListener("selectionchange", handleSelectionChange);
4827
5637
  window.removeEventListener("scroll", handleScroll, true);
4828
5638
  window.removeEventListener("message", handleSave);
5639
+ window.removeEventListener("message", handleInsertSection);
5640
+ window.removeEventListener("message", handleSwitchSchedule);
5641
+ window.removeEventListener("message", handleClearSchedulingWidget);
5642
+ window.removeEventListener("message", handleRemoveSchedulingSection);
4829
5643
  window.removeEventListener("message", handleImageUrl);
4830
5644
  window.removeEventListener("message", handleAnimLock);
4831
5645
  window.removeEventListener("message", handleCanvasHeight);
@@ -4840,7 +5654,7 @@ function OhhwellsBridge() {
4840
5654
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
4841
5655
  };
4842
5656
  }, [isEditMode, refreshStateRules]);
4843
- (0, import_react.useEffect)(() => {
5657
+ (0, import_react3.useEffect)(() => {
4844
5658
  if (!isEditMode) return;
4845
5659
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
4846
5660
  el.removeAttribute("data-ohw-active-state");
@@ -4862,13 +5676,13 @@ function OhhwellsBridge() {
4862
5676
  clearTimeout(timer);
4863
5677
  };
4864
5678
  }, [pathname, isEditMode, refreshStateRules, postToParent]);
4865
- const handleCommand = (0, import_react.useCallback)((cmd) => {
5679
+ const handleCommand = (0, import_react3.useCallback)((cmd) => {
4866
5680
  document.execCommand(cmd, false);
4867
5681
  activeElRef.current?.focus();
4868
5682
  if (activeElRef.current) setToolbarRect(activeElRef.current.getBoundingClientRect());
4869
5683
  refreshActiveCommandsRef.current();
4870
5684
  }, []);
4871
- const handleStateChange = (0, import_react.useCallback)((state) => {
5685
+ const handleStateChange = (0, import_react3.useCallback)((state) => {
4872
5686
  if (!activeStateElRef.current) return;
4873
5687
  const el = activeStateElRef.current;
4874
5688
  if (state === "Default") {
@@ -4882,12 +5696,12 @@ function OhhwellsBridge() {
4882
5696
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
4883
5697
  }, [deactivate]);
4884
5698
  return bridgeRoot ? (0, import_react_dom.createPortal)(
4885
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
4886
- toolbarRect && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
4887
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
4888
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands })
5699
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5700
+ toolbarRect && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5701
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
5702
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands })
4889
5703
  ] }),
4890
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5704
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
4891
5705
  "div",
4892
5706
  {
4893
5707
  "data-ohw-max-badge": "",
@@ -4913,7 +5727,7 @@ function OhhwellsBridge() {
4913
5727
  ]
4914
5728
  }
4915
5729
  ),
4916
- toggleState && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5730
+ toggleState && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4917
5731
  StateToggle,
4918
5732
  {
4919
5733
  rect: toggleState.rect,
@@ -4922,15 +5736,15 @@ function OhhwellsBridge() {
4922
5736
  onStateChange: handleStateChange
4923
5737
  }
4924
5738
  ),
4925
- sectionGap && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5739
+ sectionGap && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
4926
5740
  "div",
4927
5741
  {
4928
5742
  "data-ohw-section-insert-line": "",
4929
5743
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
4930
5744
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
4931
5745
  children: [
4932
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
4933
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5746
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
5747
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4934
5748
  Badge,
4935
5749
  {
4936
5750
  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",
@@ -4943,7 +5757,7 @@ function OhhwellsBridge() {
4943
5757
  children: "Add Section"
4944
5758
  }
4945
5759
  ),
4946
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
5760
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
4947
5761
  ]
4948
5762
  }
4949
5763
  )
@@ -4954,6 +5768,7 @@ function OhhwellsBridge() {
4954
5768
  // Annotate the CommonJS export names for ESM import in node:
4955
5769
  0 && (module.exports = {
4956
5770
  OhhwellsBridge,
5771
+ SchedulingWidget,
4957
5772
  Toggle,
4958
5773
  ToggleGroup,
4959
5774
  ToggleGroupItem,