@ohhwells/bridge 0.1.19 → 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.js CHANGED
@@ -1,12 +1,166 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React3, { useCallback, useEffect as useEffect2, useLayoutEffect, useRef, useState as useState2 } from "react";
4
+ import React3, { useCallback, useEffect as useEffect2, useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
5
5
  import { createRoot } from "react-dom/client";
6
6
 
7
7
  // src/ui/SchedulingWidget.tsx
8
- import React, { useEffect, useMemo, useState } from "react";
9
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ import { useEffect, useMemo, useRef, useState as useState2 } from "react";
9
+
10
+ // src/ui/EmailCaptureModal.tsx
11
+ import { useState } from "react";
12
+ import { Dialog } from "radix-ui";
13
+ import { jsx, jsxs } from "react/jsx-runtime";
14
+ function Spinner() {
15
+ return /* @__PURE__ */ jsxs(
16
+ "svg",
17
+ {
18
+ width: "32",
19
+ height: "32",
20
+ viewBox: "0 0 32 32",
21
+ fill: "none",
22
+ style: { animation: "ohw-spin 0.75s linear infinite" },
23
+ children: [
24
+ /* @__PURE__ */ jsx("style", { children: `@keyframes ohw-spin { to { transform: rotate(360deg); } }` }),
25
+ /* @__PURE__ */ jsx("circle", { cx: "16", cy: "16", r: "12", stroke: "#e5e7eb", strokeWidth: "3" }),
26
+ /* @__PURE__ */ jsx(
27
+ "path",
28
+ {
29
+ d: "M16 4a12 12 0 0 1 12 12",
30
+ stroke: "#3b82f6",
31
+ strokeWidth: "3",
32
+ strokeLinecap: "round"
33
+ }
34
+ )
35
+ ]
36
+ }
37
+ );
38
+ }
39
+ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
40
+ const [email, setEmail] = useState("");
41
+ const [submittedEmail, setSubmittedEmail] = useState("");
42
+ const [loading, setLoading] = useState(false);
43
+ const [success, setSuccess] = useState(false);
44
+ const [error, setError] = useState(null);
45
+ const isBook = title === "Confirm your spot";
46
+ const handleSubmit = async () => {
47
+ if (!email.trim()) return;
48
+ setLoading(true);
49
+ setError(null);
50
+ try {
51
+ await onSubmit(email.trim());
52
+ setSubmittedEmail(email.trim());
53
+ setSuccess(true);
54
+ } catch (e) {
55
+ setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
56
+ } finally {
57
+ setLoading(false);
58
+ }
59
+ };
60
+ const handleKeyDown = (e) => {
61
+ if (e.key === "Enter" && !loading) handleSubmit();
62
+ };
63
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: true, onOpenChange: (open) => {
64
+ if (!open) onClose();
65
+ }, children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
66
+ /* @__PURE__ */ jsx(
67
+ Dialog.Overlay,
68
+ {
69
+ className: "fixed inset-0 z-50",
70
+ style: { background: "rgba(0,0,0,0.45)" }
71
+ }
72
+ ),
73
+ /* @__PURE__ */ jsxs(
74
+ Dialog.Content,
75
+ {
76
+ 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",
77
+ style: { maxWidth: 400 },
78
+ children: [
79
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 p-6", children: [
80
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
81
+ /* @__PURE__ */ jsx(
82
+ Dialog.Title,
83
+ {
84
+ className: "font-body text-xl font-bold m-0 leading-tight",
85
+ style: { color: "var(--color-dark,#200C02)" },
86
+ children: success ? isBook ? "Reservation confirmed!" : "You're on the waitlist!" : title
87
+ }
88
+ ),
89
+ /* @__PURE__ */ jsx(
90
+ Dialog.Description,
91
+ {
92
+ className: "font-body text-sm m-0",
93
+ style: { color: "#6B7280" },
94
+ children: success ? isBook ? `A confirmation email has been sent to ${submittedEmail}.` : "We'll let you know via email if someone cancels." : subtitle
95
+ }
96
+ )
97
+ ] }),
98
+ /* @__PURE__ */ jsx(
99
+ Dialog.Close,
100
+ {
101
+ 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",
102
+ style: { color: "#6B7280" },
103
+ "aria-label": "Close",
104
+ children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
105
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
106
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
107
+ ] })
108
+ }
109
+ )
110
+ ] }),
111
+ !success && (loading ? (
112
+ /* Loading state — full body replaced */
113
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center gap-3 px-7 py-12", children: [
114
+ /* @__PURE__ */ jsx(Spinner, {}),
115
+ /* @__PURE__ */ jsx("p", { className: "font-body text-sm m-0", style: { color: "#6B7280" }, children: "Please wait" })
116
+ ] })
117
+ ) : (
118
+ /* Email form */
119
+ /* @__PURE__ */ jsxs("div", { className: "px-7 pt-2 pb-6", children: [
120
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 mb-8", children: [
121
+ /* @__PURE__ */ jsx("label", { className: "font-body text-sm font-medium", style: { color: "var(--color-dark,#200C02)" }, children: "Email" }),
122
+ /* @__PURE__ */ jsx(
123
+ "input",
124
+ {
125
+ type: "email",
126
+ value: email,
127
+ onChange: (e) => setEmail(e.target.value),
128
+ onKeyDown: handleKeyDown,
129
+ placeholder: "hello@gmail.com",
130
+ autoFocus: true,
131
+ className: "w-full h-10 px-3 rounded-lg border font-body text-sm outline-none box-border",
132
+ style: {
133
+ borderColor: "#E7E5E4",
134
+ background: "#fff",
135
+ color: "var(--color-dark,#200C02)"
136
+ }
137
+ }
138
+ ),
139
+ error && /* @__PURE__ */ jsx("p", { className: "font-body text-xs text-red-500 m-0", children: error })
140
+ ] }),
141
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
142
+ "button",
143
+ {
144
+ onClick: handleSubmit,
145
+ disabled: !email.trim(),
146
+ className: "h-9 px-5 rounded-lg font-body text-sm font-semibold cursor-pointer border-none disabled:opacity-50 disabled:cursor-not-allowed",
147
+ style: {
148
+ background: "var(--color-primary,#3D312B)",
149
+ color: "var(--color-light,#FEFFF0)"
150
+ },
151
+ children: "Send"
152
+ }
153
+ ) })
154
+ ] })
155
+ ))
156
+ ]
157
+ }
158
+ )
159
+ ] }) });
160
+ }
161
+
162
+ // src/ui/SchedulingWidget.tsx
163
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
10
164
  var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
11
165
  var DAY_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
12
166
  function getApiDomain() {
@@ -67,8 +221,8 @@ function EmptyState({ inEditor }) {
67
221
  window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
68
222
  }
69
223
  };
70
- return /* @__PURE__ */ 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: [
71
- /* @__PURE__ */ jsx("div", { className: "w-10 h-10 bg-[#F5F5F4] rounded-[10px] flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxs(
224
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center gap-6 p-12 bg-[#FAFAF9] border-2 border-dashed border-[#E7E5E4] rounded-[10px] h-[264px] w-full box-border", children: [
225
+ /* @__PURE__ */ jsx2("div", { className: "w-10 h-10 bg-[#F5F5F4] rounded-[10px] flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxs2(
72
226
  "svg",
73
227
  {
74
228
  width: "20",
@@ -80,20 +234,20 @@ function EmptyState({ inEditor }) {
80
234
  strokeLinecap: "round",
81
235
  strokeLinejoin: "round",
82
236
  children: [
83
- /* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
84
- /* @__PURE__ */ jsx("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
85
- /* @__PURE__ */ jsx("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
86
- /* @__PURE__ */ jsx("line", { x1: "3", y1: "10", x2: "21", y2: "10" }),
87
- /* @__PURE__ */ jsx("line", { x1: "12", y1: "14", x2: "12", y2: "18" }),
88
- /* @__PURE__ */ jsx("line", { x1: "10", y1: "16", x2: "14", y2: "16" })
237
+ /* @__PURE__ */ jsx2("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
238
+ /* @__PURE__ */ jsx2("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
239
+ /* @__PURE__ */ jsx2("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
240
+ /* @__PURE__ */ jsx2("line", { x1: "3", y1: "10", x2: "21", y2: "10" }),
241
+ /* @__PURE__ */ jsx2("line", { x1: "12", y1: "14", x2: "12", y2: "18" }),
242
+ /* @__PURE__ */ jsx2("line", { x1: "10", y1: "16", x2: "14", y2: "16" })
89
243
  ]
90
244
  }
91
245
  ) }),
92
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2", children: [
93
- /* @__PURE__ */ jsx("p", { className: "font-body text-lg font-medium text-center text-[#0A0A0A] m-0", children: "No schedule yet" }),
94
- /* @__PURE__ */ 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." })
246
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-2", children: [
247
+ /* @__PURE__ */ jsx2("p", { className: "font-body text-lg font-medium text-center text-[#0A0A0A] m-0", children: "No schedule yet" }),
248
+ /* @__PURE__ */ jsx2("p", { className: "font-body text-base font-normal text-center text-(--color-accent,#A89B83) m-0", children: "Add a scheduling widget to get instant bookings." })
95
249
  ] }),
96
- /* @__PURE__ */ jsx(
250
+ /* @__PURE__ */ jsx2(
97
251
  "button",
98
252
  {
99
253
  onClick: handleAddSchedule,
@@ -109,32 +263,56 @@ function LoadingSkeleton() {
109
263
  backgroundSize: "200% 100%",
110
264
  animation: "ohw-shimmer 1.5s infinite"
111
265
  };
112
- return /* @__PURE__ */ jsxs("div", { className: "w-full flex flex-col gap-10", children: [
113
- /* @__PURE__ */ jsx("style", { children: `@keyframes ohw-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}` }),
114
- /* @__PURE__ */ jsx("div", { className: "flex h-[70px] items-stretch", children: Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col justify-between items-center px-1", children: [
115
- /* @__PURE__ */ jsx("div", { style: { ...shimmer, width: "32px", height: "14px", borderRadius: "4px" } }),
116
- /* @__PURE__ */ jsx("div", { style: { ...shimmer, width: "40px", height: "40px", borderRadius: "50%" } })
266
+ return /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col gap-10", children: [
267
+ /* @__PURE__ */ jsx2("style", { children: `@keyframes ohw-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}` }),
268
+ /* @__PURE__ */ jsx2("div", { className: "flex h-[70px] items-stretch", children: Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col justify-between items-center px-1", children: [
269
+ /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "32px", height: "14px", borderRadius: "4px" } }),
270
+ /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "40px", height: "40px", borderRadius: "50%" } })
117
271
  ] }, i)) }),
118
- [0, 1, 2].map((i) => /* @__PURE__ */ jsxs("div", { children: [
119
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-[60px] py-4", children: [
120
- /* @__PURE__ */ jsx("div", { style: { ...shimmer, width: "120px", height: "16px", borderRadius: "4px", flexShrink: 0 } }),
121
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-2", children: [
122
- /* @__PURE__ */ jsx("div", { style: { ...shimmer, width: "55%", height: "16px", borderRadius: "4px" } }),
123
- /* @__PURE__ */ jsx("div", { style: { ...shimmer, width: "38%", height: "14px", borderRadius: "4px" } })
272
+ [0, 1, 2].map((i) => /* @__PURE__ */ jsxs2("div", { children: [
273
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-[60px] py-4", children: [
274
+ /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "120px", height: "16px", borderRadius: "4px", flexShrink: 0 } }),
275
+ /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-2", children: [
276
+ /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "55%", height: "16px", borderRadius: "4px" } }),
277
+ /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "38%", height: "14px", borderRadius: "4px" } })
124
278
  ] }),
125
- /* @__PURE__ */ jsx("div", { style: { ...shimmer, width: "56px", height: "32px", borderRadius: "4px", flexShrink: 0 } }),
126
- /* @__PURE__ */ jsx("div", { style: { ...shimmer, width: "200px", height: "44px", borderRadius: "8px", flexShrink: 0 } })
279
+ /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "56px", height: "32px", borderRadius: "4px", flexShrink: 0 } }),
280
+ /* @__PURE__ */ jsx2("div", { style: { ...shimmer, width: "200px", height: "44px", borderRadius: "8px", flexShrink: 0 } })
127
281
  ] }),
128
- i < 2 && /* @__PURE__ */ jsx("div", { className: "h-px bg-black/20" })
282
+ i < 2 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
129
283
  ] }, i))
130
284
  ] });
131
285
  }
132
- function ScheduleView({ schedule, dates, selectedIdx, onSelectDate }) {
286
+ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
133
287
  const todayMs = useMemo(() => {
134
288
  const d = /* @__PURE__ */ new Date();
135
289
  d.setHours(0, 0, 0, 0);
136
290
  return d.getTime();
137
291
  }, []);
292
+ const scrollRef = useRef(null);
293
+ const [canScrollLeft, setCanScrollLeft] = useState2(false);
294
+ const [canScrollRight, setCanScrollRight] = useState2(false);
295
+ const updateScrollState = () => {
296
+ const el = scrollRef.current;
297
+ if (!el) return;
298
+ setCanScrollLeft(el.scrollLeft > 0);
299
+ setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
300
+ };
301
+ useEffect(() => {
302
+ const el = scrollRef.current;
303
+ if (!el) return;
304
+ updateScrollState();
305
+ el.addEventListener("scroll", updateScrollState);
306
+ const ro = new ResizeObserver(updateScrollState);
307
+ ro.observe(el);
308
+ return () => {
309
+ el.removeEventListener("scroll", updateScrollState);
310
+ ro.disconnect();
311
+ };
312
+ }, [dates]);
313
+ const scroll = (dir) => {
314
+ scrollRef.current?.scrollBy({ left: dir === "left" ? -216 : 216, behavior: "smooth" });
315
+ };
138
316
  const datesWithClasses = useMemo(
139
317
  () => new Set(dates.map(
140
318
  (d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
@@ -146,59 +324,94 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate }) {
146
324
  () => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
147
325
  [schedule, selectedDate]
148
326
  );
149
- return /* @__PURE__ */ jsxs("div", { className: "w-full flex flex-col gap-10", children: [
150
- /* @__PURE__ */ jsx("div", { className: "overflow-x-auto w-full", children: /* @__PURE__ */ jsx("div", { className: "flex min-w-max", children: dates.map((date, i) => {
151
- const isToday = date.getTime() === todayMs;
152
- const isSelected = i === selectedIdx;
153
- const clickable = datesWithClasses.has(i);
154
- const label = isToday ? "TODAY" : DAY_ABBR[date.getDay()];
155
- return /* @__PURE__ */ jsxs(
327
+ return /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col gap-10", children: [
328
+ /* @__PURE__ */ jsxs2("div", { className: "relative w-full", children: [
329
+ canScrollLeft && /* @__PURE__ */ jsx2(
330
+ "button",
331
+ {
332
+ onClick: () => scroll("left"),
333
+ 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",
334
+ style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
335
+ "aria-label": "Scroll left",
336
+ children: /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "var(--color-dark,#200C02)", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx2("polyline", { points: "15 18 9 12 15 6" }) })
337
+ }
338
+ ),
339
+ canScrollRight && /* @__PURE__ */ jsx2(
340
+ "button",
341
+ {
342
+ onClick: () => scroll("right"),
343
+ 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",
344
+ style: { background: "var(--color-light,#FEFFF0)", boxShadow: "0 1px 4px rgba(0,0,0,0.15)" },
345
+ "aria-label": "Scroll right",
346
+ children: /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "var(--color-dark,#200C02)", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx2("polyline", { points: "9 18 15 12 9 6" }) })
347
+ }
348
+ ),
349
+ /* @__PURE__ */ jsx2(
156
350
  "div",
157
351
  {
158
- onClick: () => clickable && onSelectDate(i),
159
- className: `flex-none w-[72px] h-[70px] flex flex-col justify-between items-center ${clickable ? "cursor-pointer opacity-100" : "cursor-default opacity-50"}`,
160
- children: [
161
- /* @__PURE__ */ jsx("span", { className: "font-body text-base font-normal text-center text-(--color-dark,#200C02)", children: label }),
162
- /* @__PURE__ */ jsx(
352
+ ref: scrollRef,
353
+ className: "overflow-x-auto w-full",
354
+ style: {
355
+ scrollbarWidth: "none",
356
+ msOverflowStyle: "none",
357
+ paddingLeft: canScrollLeft ? "36px" : 0,
358
+ paddingRight: canScrollRight ? "36px" : 0
359
+ },
360
+ children: /* @__PURE__ */ jsx2("div", { className: "flex", style: { width: `max(100%, ${dates.length * 60}px)` }, children: dates.map((date, i) => {
361
+ const isToday = date.getTime() === todayMs;
362
+ const isSelected = i === selectedIdx;
363
+ const clickable = datesWithClasses.has(i);
364
+ const label = isToday ? "TODAY" : DAY_ABBR[date.getDay()];
365
+ return /* @__PURE__ */ jsxs2(
163
366
  "div",
164
367
  {
165
- className: "w-10 h-10 rounded-full flex items-center justify-center",
166
- style: { background: isSelected ? "var(--color-primary, #3D312B)" : "transparent" },
167
- children: /* @__PURE__ */ jsx(
168
- "span",
169
- {
170
- className: "font-body text-xl font-bold text-center tracking-display-tight",
171
- style: { color: isSelected ? "var(--color-light, #FEFFF0)" : "var(--color-dark, #200C02)" },
172
- children: date.getDate()
173
- }
174
- )
175
- }
176
- )
177
- ]
178
- },
179
- i
180
- );
181
- }) }) }),
182
- selectedClasses.length === 0 ? /* @__PURE__ */ 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__ */ jsx("div", { className: "flex flex-col", children: selectedClasses.map((cls, i) => {
368
+ onClick: () => clickable && onSelectDate(i),
369
+ className: `flex-1 h-[70px] flex flex-col justify-between items-center ${clickable ? "cursor-pointer opacity-100" : "cursor-default opacity-50"}`,
370
+ children: [
371
+ /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-center text-(--color-dark,#200C02)", children: label }),
372
+ /* @__PURE__ */ jsx2(
373
+ "div",
374
+ {
375
+ className: "w-10 h-10 rounded-full flex items-center justify-center",
376
+ style: { background: isSelected ? "var(--color-primary, #3D312B)" : "transparent" },
377
+ children: /* @__PURE__ */ jsx2(
378
+ "span",
379
+ {
380
+ className: "font-body text-xl font-bold text-center tracking-display-tight",
381
+ style: { color: isSelected ? "var(--color-light, #FEFFF0)" : "var(--color-dark, #200C02)" },
382
+ children: date.getDate()
383
+ }
384
+ )
385
+ }
386
+ )
387
+ ]
388
+ },
389
+ i
390
+ );
391
+ }) })
392
+ }
393
+ )
394
+ ] }),
395
+ selectedClasses.length === 0 ? /* @__PURE__ */ jsx2("p", { className: "font-body text-base text-(--color-accent,#A89B83) text-center py-8 m-0", children: "No classes scheduled for this day." }) : /* @__PURE__ */ jsx2("div", { className: "flex flex-col", children: selectedClasses.map((cls, i) => {
183
396
  const booked = getBookingsOnDate(cls, selectedDate);
184
397
  const available = cls.maxParticipants - booked;
185
398
  const isFull = available <= 0;
186
- return /* @__PURE__ */ jsxs("div", { children: [
187
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-[60px] py-4 box-border", children: [
188
- /* @__PURE__ */ jsx("div", { className: "w-[120px] shrink-0", children: /* @__PURE__ */ jsx("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }) }),
189
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-3 min-w-0", children: [
190
- /* @__PURE__ */ jsx("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
191
- cls.hostName && /* @__PURE__ */ jsx("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
399
+ return /* @__PURE__ */ jsxs2("div", { children: [
400
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-[60px] py-4 box-border", children: [
401
+ /* @__PURE__ */ jsx2("div", { className: "w-[120px] shrink-0", children: /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }) }),
402
+ /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-3 min-w-0", children: [
403
+ /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
404
+ cls.hostName && /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
192
405
  ] }),
193
- /* @__PURE__ */ jsx("div", { className: "w-14 shrink-0 flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
194
- /* @__PURE__ */ jsxs("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
406
+ /* @__PURE__ */ jsx2("div", { className: "w-14 shrink-0 flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
407
+ /* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
195
408
  available,
196
409
  "/",
197
410
  cls.maxParticipants
198
411
  ] }),
199
- /* @__PURE__ */ jsx("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
412
+ /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
200
413
  ] }) }),
201
- /* @__PURE__ */ jsx(
414
+ /* @__PURE__ */ jsx2(
202
415
  "button",
203
416
  {
204
417
  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",
@@ -211,17 +424,21 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate }) {
211
424
  border: "none",
212
425
  color: "var(--color-light, #FEFFF0)"
213
426
  },
427
+ onClick: () => {
428
+ if (!cls.id) return;
429
+ onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
430
+ },
214
431
  children: isFull ? "Join Waitlist" : "Book Now"
215
432
  }
216
433
  )
217
434
  ] }),
218
- i < selectedClasses.length - 1 && /* @__PURE__ */ jsx("div", { className: "h-px bg-black/20" })
435
+ i < selectedClasses.length - 1 && /* @__PURE__ */ jsx2("div", { className: "h-px bg-black/20" })
219
436
  ] }, cls.id ?? i);
220
437
  }) })
221
438
  ] });
222
439
  }
223
440
  function CalendarFoldIcon() {
224
- return /* @__PURE__ */ jsxs(
441
+ return /* @__PURE__ */ jsxs2(
225
442
  "svg",
226
443
  {
227
444
  width: "16",
@@ -234,21 +451,22 @@ function CalendarFoldIcon() {
234
451
  strokeLinejoin: "round",
235
452
  style: { flexShrink: 0 },
236
453
  children: [
237
- /* @__PURE__ */ jsx("path", { d: "M8 2v4" }),
238
- /* @__PURE__ */ jsx("path", { d: "M16 2v4" }),
239
- /* @__PURE__ */ jsx("path", { d: "M21 17V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11Z" }),
240
- /* @__PURE__ */ jsx("path", { d: "M3 10h18" }),
241
- /* @__PURE__ */ jsx("path", { d: "M15 22v-4a2 2 0 0 1 2-2h4" })
454
+ /* @__PURE__ */ jsx2("path", { d: "M8 2v4" }),
455
+ /* @__PURE__ */ jsx2("path", { d: "M16 2v4" }),
456
+ /* @__PURE__ */ jsx2("path", { d: "M21 17V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11Z" }),
457
+ /* @__PURE__ */ jsx2("path", { d: "M3 10h18" }),
458
+ /* @__PURE__ */ jsx2("path", { d: "M15 22v-4a2 2 0 0 1 2-2h4" })
242
459
  ]
243
460
  }
244
461
  );
245
462
  }
246
463
  function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
247
- const [schedule, setSchedule] = useState(null);
248
- const [loading, setLoading] = useState(true);
249
- const [inEditor, setInEditor] = useState(false);
250
- const [isHovered, setIsHovered] = useState(false);
251
- const switchScheduleIdRef = React.useRef(null);
464
+ const [schedule, setSchedule] = useState2(null);
465
+ const [loading, setLoading] = useState2(true);
466
+ const [inEditor, setInEditor] = useState2(false);
467
+ const [isHovered, setIsHovered] = useState2(false);
468
+ const [modalState, setModalState] = useState2(null);
469
+ const switchScheduleIdRef = useRef(null);
252
470
  const dates = useMemo(() => {
253
471
  const today = /* @__PURE__ */ new Date();
254
472
  today.setHours(0, 0, 0, 0);
@@ -258,7 +476,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
258
476
  return d;
259
477
  });
260
478
  }, []);
261
- const [selectedIdx, setSelectedIdx] = useState(0);
479
+ const [selectedIdx, setSelectedIdx] = useState2(0);
262
480
  useEffect(() => {
263
481
  if (!schedule?.classes) return;
264
482
  for (let i = 0; i < dates.length; i++) {
@@ -354,6 +572,20 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
354
572
  return void 0;
355
573
  }
356
574
  })() : void 0;
575
+ const handleModalSubmit = async (email) => {
576
+ if (!modalState) return;
577
+ const { mode, classId, classDate } = modalState;
578
+ const endpoint = mode === "book" ? `${API_URL}/api/schedule/class/${classId}/booking` : `${API_URL}/api/schedule/class/${classId}/waitlist`;
579
+ const res = await fetch(endpoint, {
580
+ method: "POST",
581
+ headers: { "Content-Type": "application/json" },
582
+ body: JSON.stringify({ classDate: classDate.toISOString(), email })
583
+ });
584
+ if (!res.ok) {
585
+ const data = await res.json().catch(() => ({}));
586
+ throw new Error(data?.message ?? "Something went wrong. Please try again.");
587
+ }
588
+ };
357
589
  const handleReplaceSchedule = () => {
358
590
  setIsHovered(false);
359
591
  if (schedule) {
@@ -362,7 +594,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
362
594
  window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
363
595
  }
364
596
  };
365
- return /* @__PURE__ */ jsxs(
597
+ return /* @__PURE__ */ jsxs2(
366
598
  "section",
367
599
  {
368
600
  "data-ohw-section": "scheduling",
@@ -370,15 +602,15 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
370
602
  onMouseEnter: () => inEditor && setIsHovered(true),
371
603
  onMouseLeave: () => setIsHovered(false),
372
604
  children: [
373
- inEditor && isHovered && !!schedule && /* @__PURE__ */ jsxs(
605
+ inEditor && isHovered && !!schedule && /* @__PURE__ */ jsxs2(
374
606
  "div",
375
607
  {
376
608
  className: "absolute inset-0 z-10 pointer-events-auto cursor-pointer",
377
609
  onClick: handleReplaceSchedule,
378
610
  children: [
379
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0", style: { background: "#0885FE", opacity: 0.18 } }),
380
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0", style: { boxShadow: "inset 0 0 0 1.5px #0885FE" } }),
381
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs(
611
+ /* @__PURE__ */ jsx2("div", { className: "absolute inset-0", style: { background: "#0885FE", opacity: 0.18 } }),
612
+ /* @__PURE__ */ jsx2("div", { className: "absolute inset-0", style: { boxShadow: "inset 0 0 0 1.5px #0885FE" } }),
613
+ /* @__PURE__ */ jsx2("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs2(
382
614
  "div",
383
615
  {
384
616
  className: "bg-white border border-[#E7E5E4] rounded-md px-3 py-1.5 flex items-center gap-1.5",
@@ -390,7 +622,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
390
622
  color: "#0C0A09"
391
623
  },
392
624
  children: [
393
- /* @__PURE__ */ jsx(CalendarFoldIcon, {}),
625
+ /* @__PURE__ */ jsx2(CalendarFoldIcon, {}),
394
626
  "Replace schedule"
395
627
  ]
396
628
  }
@@ -398,24 +630,34 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
398
630
  ]
399
631
  }
400
632
  ),
401
- /* @__PURE__ */ jsxs("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
402
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4", children: [
403
- /* @__PURE__ */ jsx("h2", { className: "font-display text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
404
- schedule?.description && /* @__PURE__ */ jsx("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
633
+ /* @__PURE__ */ jsxs2("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
634
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-4", children: [
635
+ /* @__PURE__ */ jsx2("h2", { className: "font-display text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
636
+ schedule?.description && /* @__PURE__ */ jsx2("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
405
637
  ] }),
406
- /* @__PURE__ */ jsxs("div", { className: "w-full flex flex-col items-end gap-3", children: [
407
- timezoneLabel && /* @__PURE__ */ jsx("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
408
- /* @__PURE__ */ jsx("div", { className: "w-full p-10 box-border", children: loading ? /* @__PURE__ */ jsx(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ jsx(EmptyState, { inEditor }) : /* @__PURE__ */ jsx(
638
+ /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col items-end gap-3", children: [
639
+ timezoneLabel && /* @__PURE__ */ jsx2("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
640
+ /* @__PURE__ */ jsx2("div", { className: "w-full p-10 box-border", children: loading ? /* @__PURE__ */ jsx2(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ jsx2(EmptyState, { inEditor }) : /* @__PURE__ */ jsx2(
409
641
  ScheduleView,
410
642
  {
411
643
  schedule,
412
644
  dates,
413
645
  selectedIdx,
414
- onSelectDate: setSelectedIdx
646
+ onSelectDate: setSelectedIdx,
647
+ onOpenModal: inEditor ? void 0 : (mode, classId, classDate) => setModalState({ mode, classId, classDate })
415
648
  }
416
649
  ) })
417
650
  ] })
418
- ] })
651
+ ] }),
652
+ modalState && /* @__PURE__ */ jsx2(
653
+ EmailCaptureModal,
654
+ {
655
+ title: modalState.mode === "book" ? "Confirm your spot" : "Leave your email",
656
+ subtitle: modalState.mode === "book" ? "We'll send your booking confirmation here." : "We'll let you know when spots open up!",
657
+ onSubmit: handleModalSubmit,
658
+ onClose: () => setModalState(null)
659
+ }
660
+ )
419
661
  ]
420
662
  }
421
663
  );
@@ -3742,7 +3984,7 @@ var cva = (base, config) => (props) => {
3742
3984
 
3743
3985
  // src/ui/toggle.tsx
3744
3986
  import { Toggle as TogglePrimitive } from "radix-ui";
3745
- import { jsx as jsx2 } from "react/jsx-runtime";
3987
+ import { jsx as jsx3 } from "react/jsx-runtime";
3746
3988
  var toggleVariants = cva(
3747
3989
  "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",
3748
3990
  {
@@ -3769,7 +4011,7 @@ function Toggle({
3769
4011
  size,
3770
4012
  ...props
3771
4013
  }) {
3772
- return /* @__PURE__ */ jsx2(
4014
+ return /* @__PURE__ */ jsx3(
3773
4015
  TogglePrimitive.Root,
3774
4016
  {
3775
4017
  "data-slot": "toggle",
@@ -3780,7 +4022,7 @@ function Toggle({
3780
4022
  }
3781
4023
 
3782
4024
  // src/ui/toggle-group.tsx
3783
- import { jsx as jsx3 } from "react/jsx-runtime";
4025
+ import { jsx as jsx4 } from "react/jsx-runtime";
3784
4026
  var ToggleGroupContext = React2.createContext({
3785
4027
  value: "",
3786
4028
  onValueChange: () => {
@@ -3793,13 +4035,13 @@ function ToggleGroup({
3793
4035
  children,
3794
4036
  ...props
3795
4037
  }) {
3796
- return /* @__PURE__ */ jsx3(
4038
+ return /* @__PURE__ */ jsx4(
3797
4039
  "div",
3798
4040
  {
3799
4041
  role: "group",
3800
4042
  className: cn("flex items-center gap-1 p-1 bg-muted rounded-md", className),
3801
4043
  ...props,
3802
- children: /* @__PURE__ */ jsx3(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
4044
+ children: /* @__PURE__ */ jsx4(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
3803
4045
  }
3804
4046
  );
3805
4047
  }
@@ -3810,7 +4052,7 @@ function ToggleGroupItem({
3810
4052
  ...props
3811
4053
  }) {
3812
4054
  const { value: groupValue, onValueChange } = React2.useContext(ToggleGroupContext);
3813
- return /* @__PURE__ */ jsx3(
4055
+ return /* @__PURE__ */ jsx4(
3814
4056
  Toggle,
3815
4057
  {
3816
4058
  pressed: groupValue === value,
@@ -3852,7 +4094,7 @@ function isEditSessionActive() {
3852
4094
  }
3853
4095
 
3854
4096
  // src/ui/badge.tsx
3855
- import { jsx as jsx4 } from "react/jsx-runtime";
4097
+ import { jsx as jsx5 } from "react/jsx-runtime";
3856
4098
  var badgeVariants = cva(
3857
4099
  "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",
3858
4100
  {
@@ -3870,11 +4112,11 @@ var badgeVariants = cva(
3870
4112
  }
3871
4113
  );
3872
4114
  function Badge({ className, variant, ...props }) {
3873
- return /* @__PURE__ */ jsx4("div", { className: cn(badgeVariants({ variant }), className), ...props });
4115
+ return /* @__PURE__ */ jsx5("div", { className: cn(badgeVariants({ variant }), className), ...props });
3874
4116
  }
3875
4117
 
3876
4118
  // src/OhhwellsBridge.tsx
3877
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
4119
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
3878
4120
  var PRIMARY = "#0885FE";
3879
4121
  var IMAGE_FADE_MS = 300;
3880
4122
  function runOpacityFade(el, onDone) {
@@ -3942,7 +4184,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId)
3942
4184
  const container = document.createElement("div");
3943
4185
  container.dataset.ohwSectionContainer = "scheduling";
3944
4186
  anchor.insertAdjacentElement("afterend", container);
3945
- createRoot(container).render(/* @__PURE__ */ jsx5(SchedulingWidget, { notifyOnConnect, initialScheduleId: scheduleId }));
4187
+ createRoot(container).render(/* @__PURE__ */ jsx6(SchedulingWidget, { notifyOnConnect, initialScheduleId: scheduleId }));
3946
4188
  const tracker = getSectionsTracker();
3947
4189
  let sections = [];
3948
4190
  try {
@@ -4117,7 +4359,7 @@ var TOOLBAR_GROUPS = [
4117
4359
  ];
4118
4360
  function GlowFrame({ rect, elRef }) {
4119
4361
  const GAP = 6;
4120
- return /* @__PURE__ */ jsx5(
4362
+ return /* @__PURE__ */ jsx6(
4121
4363
  "div",
4122
4364
  {
4123
4365
  ref: elRef,
@@ -4168,7 +4410,7 @@ function FloatingToolbar({
4168
4410
  activeCommands
4169
4411
  }) {
4170
4412
  const { top, left, transform } = calcToolbarPos(rect, parentScroll);
4171
- return /* @__PURE__ */ jsx5(
4413
+ return /* @__PURE__ */ jsx6(
4172
4414
  "div",
4173
4415
  {
4174
4416
  ref: elRef,
@@ -4191,11 +4433,11 @@ function FloatingToolbar({
4191
4433
  pointerEvents: "auto",
4192
4434
  whiteSpace: "nowrap"
4193
4435
  },
4194
- children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs2(React3.Fragment, { children: [
4195
- gi > 0 && /* @__PURE__ */ jsx5("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
4436
+ children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs3(React3.Fragment, { children: [
4437
+ gi > 0 && /* @__PURE__ */ jsx6("span", { style: { display: "block", width: 1, height: 24, background: "#E7E5E4", flexShrink: 0 } }),
4196
4438
  btns.map((btn) => {
4197
4439
  const isActive = activeCommands.has(btn.cmd);
4198
- return /* @__PURE__ */ jsx5(
4440
+ return /* @__PURE__ */ jsx6(
4199
4441
  "button",
4200
4442
  {
4201
4443
  title: btn.title,
@@ -4221,7 +4463,7 @@ function FloatingToolbar({
4221
4463
  flexShrink: 0,
4222
4464
  padding: 6
4223
4465
  },
4224
- children: /* @__PURE__ */ jsx5(
4466
+ children: /* @__PURE__ */ jsx6(
4225
4467
  "svg",
4226
4468
  {
4227
4469
  width: "16",
@@ -4254,7 +4496,7 @@ function StateToggle({
4254
4496
  states,
4255
4497
  onStateChange
4256
4498
  }) {
4257
- return /* @__PURE__ */ jsx5(
4499
+ return /* @__PURE__ */ jsx6(
4258
4500
  ToggleGroup,
4259
4501
  {
4260
4502
  "data-ohw-state-toggle": "",
@@ -4268,7 +4510,7 @@ function StateToggle({
4268
4510
  left: rect.right - 8,
4269
4511
  transform: "translateX(-100%)"
4270
4512
  },
4271
- children: states.map((state) => /* @__PURE__ */ jsx5(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
4513
+ children: states.map((state) => /* @__PURE__ */ jsx6(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
4272
4514
  }
4273
4515
  );
4274
4516
  }
@@ -4278,7 +4520,7 @@ function OhhwellsBridge() {
4278
4520
  const router = useRouter();
4279
4521
  const searchParams = useSearchParams();
4280
4522
  const isEditMode = isEditSessionActive();
4281
- const [bridgeRoot, setBridgeRoot] = useState2(null);
4523
+ const [bridgeRoot, setBridgeRoot] = useState3(null);
4282
4524
  useEffect2(() => {
4283
4525
  const figtreeFontId = "ohw-figtree-font";
4284
4526
  if (!document.getElementById(figtreeFontId)) {
@@ -4312,33 +4554,33 @@ function OhhwellsBridge() {
4312
4554
  window.parent.postMessage(data, "*");
4313
4555
  }
4314
4556
  }, []);
4315
- const [fetchState, setFetchState] = useState2("idle");
4316
- const autoSaveTimers = useRef(/* @__PURE__ */ new Map());
4317
- const activeElRef = useRef(null);
4318
- const originalContentRef = useRef(null);
4319
- const activeStateElRef = useRef(null);
4320
- const parentScrollRef = useRef(null);
4321
- const toolbarElRef = useRef(null);
4322
- const glowElRef = useRef(null);
4323
- const hoveredImageRef = useRef(null);
4324
- const hoveredImageHasTextOverlapRef = useRef(false);
4325
- const hoveredGapRef = useRef(null);
4326
- const imageUnhoverTimerRef = useRef(null);
4327
- const imageShowTimerRef = useRef(null);
4328
- const editStylesRef = useRef(null);
4329
- const activateRef = useRef(() => {
4557
+ const [fetchState, setFetchState] = useState3("idle");
4558
+ const autoSaveTimers = useRef2(/* @__PURE__ */ new Map());
4559
+ const activeElRef = useRef2(null);
4560
+ const originalContentRef = useRef2(null);
4561
+ const activeStateElRef = useRef2(null);
4562
+ const parentScrollRef = useRef2(null);
4563
+ const toolbarElRef = useRef2(null);
4564
+ const glowElRef = useRef2(null);
4565
+ const hoveredImageRef = useRef2(null);
4566
+ const hoveredImageHasTextOverlapRef = useRef2(false);
4567
+ const hoveredGapRef = useRef2(null);
4568
+ const imageUnhoverTimerRef = useRef2(null);
4569
+ const imageShowTimerRef = useRef2(null);
4570
+ const editStylesRef = useRef2(null);
4571
+ const activateRef = useRef2(() => {
4330
4572
  });
4331
- const deactivateRef = useRef(() => {
4573
+ const deactivateRef = useRef2(() => {
4332
4574
  });
4333
- const refreshActiveCommandsRef = useRef(() => {
4575
+ const refreshActiveCommandsRef = useRef2(() => {
4334
4576
  });
4335
- const postToParentRef = useRef(postToParent);
4577
+ const postToParentRef = useRef2(postToParent);
4336
4578
  postToParentRef.current = postToParent;
4337
- const [toolbarRect, setToolbarRect] = useState2(null);
4338
- const [toggleState, setToggleState] = useState2(null);
4339
- const [maxBadge, setMaxBadge] = useState2(null);
4340
- const [activeCommands, setActiveCommands] = useState2(/* @__PURE__ */ new Set());
4341
- const [sectionGap, setSectionGap] = useState2(null);
4579
+ const [toolbarRect, setToolbarRect] = useState3(null);
4580
+ const [toggleState, setToggleState] = useState3(null);
4581
+ const [maxBadge, setMaxBadge] = useState3(null);
4582
+ const [activeCommands, setActiveCommands] = useState3(/* @__PURE__ */ new Set());
4583
+ const [sectionGap, setSectionGap] = useState3(null);
4342
4584
  useEffect2(() => {
4343
4585
  const update = () => {
4344
4586
  const el = activeElRef.current;
@@ -5414,12 +5656,12 @@ function OhhwellsBridge() {
5414
5656
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
5415
5657
  }, [deactivate]);
5416
5658
  return bridgeRoot ? createPortal(
5417
- /* @__PURE__ */ jsxs2(Fragment2, { children: [
5418
- toolbarRect && /* @__PURE__ */ jsxs2(Fragment2, { children: [
5419
- /* @__PURE__ */ jsx5(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
5420
- /* @__PURE__ */ jsx5(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands })
5659
+ /* @__PURE__ */ jsxs3(Fragment2, { children: [
5660
+ toolbarRect && /* @__PURE__ */ jsxs3(Fragment2, { children: [
5661
+ /* @__PURE__ */ jsx6(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
5662
+ /* @__PURE__ */ jsx6(FloatingToolbar, { rect: toolbarRect, parentScroll: parentScrollRef.current, elRef: toolbarElRef, onCommand: handleCommand, activeCommands })
5421
5663
  ] }),
5422
- maxBadge && /* @__PURE__ */ jsxs2(
5664
+ maxBadge && /* @__PURE__ */ jsxs3(
5423
5665
  "div",
5424
5666
  {
5425
5667
  "data-ohw-max-badge": "",
@@ -5445,7 +5687,7 @@ function OhhwellsBridge() {
5445
5687
  ]
5446
5688
  }
5447
5689
  ),
5448
- toggleState && /* @__PURE__ */ jsx5(
5690
+ toggleState && /* @__PURE__ */ jsx6(
5449
5691
  StateToggle,
5450
5692
  {
5451
5693
  rect: toggleState.rect,
@@ -5454,15 +5696,15 @@ function OhhwellsBridge() {
5454
5696
  onStateChange: handleStateChange
5455
5697
  }
5456
5698
  ),
5457
- sectionGap && /* @__PURE__ */ jsxs2(
5699
+ sectionGap && /* @__PURE__ */ jsxs3(
5458
5700
  "div",
5459
5701
  {
5460
5702
  "data-ohw-section-insert-line": "",
5461
5703
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
5462
5704
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
5463
5705
  children: [
5464
- /* @__PURE__ */ jsx5("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
5465
- /* @__PURE__ */ jsx5(
5706
+ /* @__PURE__ */ jsx6("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
5707
+ /* @__PURE__ */ jsx6(
5466
5708
  Badge,
5467
5709
  {
5468
5710
  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",
@@ -5475,7 +5717,7 @@ function OhhwellsBridge() {
5475
5717
  children: "Add Section"
5476
5718
  }
5477
5719
  ),
5478
- /* @__PURE__ */ jsx5("div", { className: "flex-1 bg-primary", style: { height: 3 } })
5720
+ /* @__PURE__ */ jsx6("div", { className: "flex-1 bg-primary", style: { height: 3 } })
5479
5721
  ]
5480
5722
  }
5481
5723
  )