@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.cjs CHANGED
@@ -41,12 +41,166 @@ __export(index_exports, {
41
41
  module.exports = __toCommonJS(index_exports);
42
42
 
43
43
  // src/OhhwellsBridge.tsx
44
- var import_react2 = __toESM(require("react"), 1);
44
+ var import_react3 = __toESM(require("react"), 1);
45
45
  var import_client = require("react-dom/client");
46
46
 
47
47
  // src/ui/SchedulingWidget.tsx
48
- var import_react = __toESM(require("react"), 1);
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");
49
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");
50
204
  var API_URL = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
51
205
  var DAY_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
52
206
  function getApiDomain() {
@@ -107,8 +261,8 @@ function EmptyState({ inEditor }) {
107
261
  window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
108
262
  }
109
263
  };
110
- return /* @__PURE__ */ (0, import_jsx_runtime.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: [
111
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-10 h-10 bg-[#F5F5F4] rounded-[10px] flex items-center justify-center shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
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)(
112
266
  "svg",
113
267
  {
114
268
  width: "20",
@@ -120,20 +274,20 @@ function EmptyState({ inEditor }) {
120
274
  strokeLinecap: "round",
121
275
  strokeLinejoin: "round",
122
276
  children: [
123
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
124
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
125
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
126
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "3", y1: "10", x2: "21", y2: "10" }),
127
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", y1: "14", x2: "12", y2: "18" }),
128
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "10", y1: "16", x2: "14", y2: "16" })
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" })
129
283
  ]
130
284
  }
131
285
  ) }),
132
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col items-center gap-2", children: [
133
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-body text-lg font-medium text-center text-[#0A0A0A] m-0", children: "No schedule yet" }),
134
- /* @__PURE__ */ (0, import_jsx_runtime.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." })
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." })
135
289
  ] }),
136
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
290
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
137
291
  "button",
138
292
  {
139
293
  onClick: handleAddSchedule,
@@ -149,96 +303,155 @@ function LoadingSkeleton() {
149
303
  backgroundSize: "200% 100%",
150
304
  animation: "ohw-shimmer 1.5s infinite"
151
305
  };
152
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "w-full flex flex-col gap-10", children: [
153
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `@keyframes ohw-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}` }),
154
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex h-[70px] items-stretch", children: Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 flex flex-col justify-between items-center px-1", children: [
155
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...shimmer, width: "32px", height: "14px", borderRadius: "4px" } }),
156
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...shimmer, width: "40px", height: "40px", borderRadius: "50%" } })
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%" } })
157
311
  ] }, i)) }),
158
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
159
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-[60px] py-4", children: [
160
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...shimmer, width: "120px", height: "16px", borderRadius: "4px", flexShrink: 0 } }),
161
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 flex flex-col gap-2", children: [
162
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...shimmer, width: "55%", height: "16px", borderRadius: "4px" } }),
163
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...shimmer, width: "38%", height: "14px", borderRadius: "4px" } })
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" } })
164
318
  ] }),
165
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...shimmer, width: "56px", height: "32px", borderRadius: "4px", flexShrink: 0 } }),
166
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...shimmer, width: "200px", height: "44px", borderRadius: "8px", flexShrink: 0 } })
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 } })
167
321
  ] }),
168
- i < 2 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-px bg-black/20" })
322
+ i < 2 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-px bg-black/20" })
169
323
  ] }, i))
170
324
  ] });
171
325
  }
172
- function ScheduleView({ schedule, dates, selectedIdx, onSelectDate }) {
173
- const todayMs = (0, import_react.useMemo)(() => {
326
+ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal }) {
327
+ const todayMs = (0, import_react2.useMemo)(() => {
174
328
  const d = /* @__PURE__ */ new Date();
175
329
  d.setHours(0, 0, 0, 0);
176
330
  return d.getTime();
177
331
  }, []);
178
- const datesWithClasses = (0, import_react.useMemo)(
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)(
179
357
  () => new Set(dates.map(
180
358
  (d, i) => schedule.classes?.some((c) => classRunsOnDate(c, d, schedule.type)) ? i : -1
181
359
  ).filter((i) => i >= 0)),
182
360
  [dates, schedule]
183
361
  );
184
362
  const selectedDate = dates[selectedIdx];
185
- const selectedClasses = (0, import_react.useMemo)(
363
+ const selectedClasses = (0, import_react2.useMemo)(
186
364
  () => (schedule.classes ?? []).filter((c) => selectedDate && classRunsOnDate(c, selectedDate, schedule.type)),
187
365
  [schedule, selectedDate]
188
366
  );
189
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "w-full flex flex-col gap-10", children: [
190
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "overflow-x-auto w-full", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex min-w-max", children: dates.map((date, i) => {
191
- const isToday = date.getTime() === todayMs;
192
- const isSelected = i === selectedIdx;
193
- const clickable = datesWithClasses.has(i);
194
- const label = isToday ? "TODAY" : DAY_ABBR[date.getDay()];
195
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
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)(
196
390
  "div",
197
391
  {
198
- onClick: () => clickable && onSelectDate(i),
199
- className: `flex-none w-[72px] h-[70px] flex flex-col justify-between items-center ${clickable ? "cursor-pointer opacity-100" : "cursor-default opacity-50"}`,
200
- children: [
201
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-body text-base font-normal text-center text-(--color-dark,#200C02)", children: label }),
202
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
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)(
203
406
  "div",
204
407
  {
205
- className: "w-10 h-10 rounded-full flex items-center justify-center",
206
- style: { background: isSelected ? "var(--color-primary, #3D312B)" : "transparent" },
207
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
208
- "span",
209
- {
210
- className: "font-body text-xl font-bold text-center tracking-display-tight",
211
- style: { color: isSelected ? "var(--color-light, #FEFFF0)" : "var(--color-dark, #200C02)" },
212
- children: date.getDate()
213
- }
214
- )
215
- }
216
- )
217
- ]
218
- },
219
- i
220
- );
221
- }) }) }),
222
- selectedClasses.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.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_runtime.jsx)("div", { className: "flex flex-col", children: selectedClasses.map((cls, i) => {
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) => {
223
436
  const booked = getBookingsOnDate(cls, selectedDate);
224
437
  const available = cls.maxParticipants - booked;
225
438
  const isFull = available <= 0;
226
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
227
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-[60px] py-4 box-border", children: [
228
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-[120px] shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block", children: formatClassTime(cls) }) }),
229
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 flex flex-col gap-3 min-w-0", children: [
230
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
231
- cls.hostName && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
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 })
232
445
  ] }),
233
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-14 shrink-0 flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
234
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
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: [
235
448
  available,
236
449
  "/",
237
450
  cls.maxParticipants
238
451
  ] }),
239
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
452
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "available" })
240
453
  ] }) }),
241
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
454
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
242
455
  "button",
243
456
  {
244
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",
@@ -251,17 +464,21 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate }) {
251
464
  border: "none",
252
465
  color: "var(--color-light, #FEFFF0)"
253
466
  },
467
+ onClick: () => {
468
+ if (!cls.id) return;
469
+ onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
470
+ },
254
471
  children: isFull ? "Join Waitlist" : "Book Now"
255
472
  }
256
473
  )
257
474
  ] }),
258
- i < selectedClasses.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-px bg-black/20" })
475
+ i < selectedClasses.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-px bg-black/20" })
259
476
  ] }, cls.id ?? i);
260
477
  }) })
261
478
  ] });
262
479
  }
263
480
  function CalendarFoldIcon() {
264
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
481
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
265
482
  "svg",
266
483
  {
267
484
  width: "16",
@@ -274,22 +491,23 @@ function CalendarFoldIcon() {
274
491
  strokeLinejoin: "round",
275
492
  style: { flexShrink: 0 },
276
493
  children: [
277
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 2v4" }),
278
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M16 2v4" }),
279
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 17V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11Z" }),
280
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 10h18" }),
281
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M15 22v-4a2 2 0 0 1 2-2h4" })
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" })
282
499
  ]
283
500
  }
284
501
  );
285
502
  }
286
503
  function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
287
- const [schedule, setSchedule] = (0, import_react.useState)(null);
288
- const [loading, setLoading] = (0, import_react.useState)(true);
289
- const [inEditor, setInEditor] = (0, import_react.useState)(false);
290
- const [isHovered, setIsHovered] = (0, import_react.useState)(false);
291
- const switchScheduleIdRef = import_react.default.useRef(null);
292
- const dates = (0, import_react.useMemo)(() => {
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)(() => {
293
511
  const today = /* @__PURE__ */ new Date();
294
512
  today.setHours(0, 0, 0, 0);
295
513
  return Array.from({ length: 14 }, (_, i) => {
@@ -298,8 +516,8 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
298
516
  return d;
299
517
  });
300
518
  }, []);
301
- const [selectedIdx, setSelectedIdx] = (0, import_react.useState)(0);
302
- (0, import_react.useEffect)(() => {
519
+ const [selectedIdx, setSelectedIdx] = (0, import_react2.useState)(0);
520
+ (0, import_react2.useEffect)(() => {
303
521
  if (!schedule?.classes) return;
304
522
  for (let i = 0; i < dates.length; i++) {
305
523
  if (schedule.classes.some((c) => classRunsOnDate(c, dates[i], schedule.type))) {
@@ -308,7 +526,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
308
526
  }
309
527
  }
310
528
  }, [schedule, dates]);
311
- (0, import_react.useEffect)(() => {
529
+ (0, import_react2.useEffect)(() => {
312
530
  if (typeof window === "undefined") return;
313
531
  const isInEditor = window.self !== window.top;
314
532
  setInEditor(isInEditor);
@@ -394,6 +612,20 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
394
612
  return void 0;
395
613
  }
396
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
+ };
397
629
  const handleReplaceSchedule = () => {
398
630
  setIsHovered(false);
399
631
  if (schedule) {
@@ -402,7 +634,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
402
634
  window.parent.postMessage({ type: "ow:scheduling-not-connected" }, "*");
403
635
  }
404
636
  };
405
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
637
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
406
638
  "section",
407
639
  {
408
640
  "data-ohw-section": "scheduling",
@@ -410,15 +642,15 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
410
642
  onMouseEnter: () => inEditor && setIsHovered(true),
411
643
  onMouseLeave: () => setIsHovered(false),
412
644
  children: [
413
- inEditor && isHovered && !!schedule && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
645
+ inEditor && isHovered && !!schedule && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
414
646
  "div",
415
647
  {
416
648
  className: "absolute inset-0 z-10 pointer-events-auto cursor-pointer",
417
649
  onClick: handleReplaceSchedule,
418
650
  children: [
419
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-0", style: { background: "#0885FE", opacity: 0.18 } }),
420
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-0", style: { boxShadow: "inset 0 0 0 1.5px #0885FE" } }),
421
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
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)(
422
654
  "div",
423
655
  {
424
656
  className: "bg-white border border-[#E7E5E4] rounded-md px-3 py-1.5 flex items-center gap-1.5",
@@ -430,7 +662,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
430
662
  color: "#0C0A09"
431
663
  },
432
664
  children: [
433
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CalendarFoldIcon, {}),
665
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CalendarFoldIcon, {}),
434
666
  "Replace schedule"
435
667
  ]
436
668
  }
@@ -438,24 +670,34 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId }) {
438
670
  ]
439
671
  }
440
672
  ),
441
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
442
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
443
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "font-display text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
444
- schedule?.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
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 })
445
677
  ] }),
446
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
447
- timezoneLabel && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-body text-sm font-normal text-(--color-accent,#A89B83)", children: timezoneLabel }),
448
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-full p-10 box-border", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoadingSkeleton, {}) : !schedule ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyState, { inEditor }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
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)(
449
681
  ScheduleView,
450
682
  {
451
683
  schedule,
452
684
  dates,
453
685
  selectedIdx,
454
- onSelectDate: setSelectedIdx
686
+ onSelectDate: setSelectedIdx,
687
+ onOpenModal: inEditor ? void 0 : (mode, classId, classDate) => setModalState({ mode, classId, classDate })
455
688
  }
456
689
  ) })
457
690
  ] })
458
- ] })
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
+ )
459
701
  ]
460
702
  }
461
703
  );
@@ -3781,8 +4023,8 @@ var cva = (base, config) => (props) => {
3781
4023
  };
3782
4024
 
3783
4025
  // src/ui/toggle.tsx
3784
- var import_radix_ui = require("radix-ui");
3785
- var import_jsx_runtime2 = require("react/jsx-runtime");
4026
+ var import_radix_ui2 = require("radix-ui");
4027
+ var import_jsx_runtime3 = require("react/jsx-runtime");
3786
4028
  var toggleVariants = cva(
3787
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",
3788
4030
  {
@@ -3809,8 +4051,8 @@ function Toggle({
3809
4051
  size,
3810
4052
  ...props
3811
4053
  }) {
3812
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
3813
- import_radix_ui.Toggle.Root,
4054
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4055
+ import_radix_ui2.Toggle.Root,
3814
4056
  {
3815
4057
  "data-slot": "toggle",
3816
4058
  className: cn(toggleVariants({ variant, size, className })),
@@ -3820,7 +4062,7 @@ function Toggle({
3820
4062
  }
3821
4063
 
3822
4064
  // src/ui/toggle-group.tsx
3823
- var import_jsx_runtime3 = require("react/jsx-runtime");
4065
+ var import_jsx_runtime4 = require("react/jsx-runtime");
3824
4066
  var ToggleGroupContext = React2.createContext({
3825
4067
  value: "",
3826
4068
  onValueChange: () => {
@@ -3833,13 +4075,13 @@ function ToggleGroup({
3833
4075
  children,
3834
4076
  ...props
3835
4077
  }) {
3836
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4078
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3837
4079
  "div",
3838
4080
  {
3839
4081
  role: "group",
3840
4082
  className: cn("flex items-center gap-1 p-1 bg-muted rounded-md", className),
3841
4083
  ...props,
3842
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
4084
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToggleGroupContext.Provider, { value: { value, onValueChange }, children })
3843
4085
  }
3844
4086
  );
3845
4087
  }
@@ -3850,7 +4092,7 @@ function ToggleGroupItem({
3850
4092
  ...props
3851
4093
  }) {
3852
4094
  const { value: groupValue, onValueChange } = React2.useContext(ToggleGroupContext);
3853
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4095
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3854
4096
  Toggle,
3855
4097
  {
3856
4098
  pressed: groupValue === value,
@@ -3892,7 +4134,7 @@ function isEditSessionActive() {
3892
4134
  }
3893
4135
 
3894
4136
  // src/ui/badge.tsx
3895
- var import_jsx_runtime4 = require("react/jsx-runtime");
4137
+ var import_jsx_runtime5 = require("react/jsx-runtime");
3896
4138
  var badgeVariants = cva(
3897
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",
3898
4140
  {
@@ -3910,11 +4152,11 @@ var badgeVariants = cva(
3910
4152
  }
3911
4153
  );
3912
4154
  function Badge({ className, variant, ...props }) {
3913
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
4155
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
3914
4156
  }
3915
4157
 
3916
4158
  // src/OhhwellsBridge.tsx
3917
- var import_jsx_runtime5 = require("react/jsx-runtime");
4159
+ var import_jsx_runtime6 = require("react/jsx-runtime");
3918
4160
  var PRIMARY = "#0885FE";
3919
4161
  var IMAGE_FADE_MS = 300;
3920
4162
  function runOpacityFade(el, onDone) {
@@ -3982,7 +4224,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId)
3982
4224
  const container = document.createElement("div");
3983
4225
  container.dataset.ohwSectionContainer = "scheduling";
3984
4226
  anchor.insertAdjacentElement("afterend", container);
3985
- (0, import_client.createRoot)(container).render(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SchedulingWidget, { notifyOnConnect, initialScheduleId: scheduleId }));
4227
+ (0, import_client.createRoot)(container).render(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SchedulingWidget, { notifyOnConnect, initialScheduleId: scheduleId }));
3986
4228
  const tracker = getSectionsTracker();
3987
4229
  let sections = [];
3988
4230
  try {
@@ -4157,7 +4399,7 @@ var TOOLBAR_GROUPS = [
4157
4399
  ];
4158
4400
  function GlowFrame({ rect, elRef }) {
4159
4401
  const GAP = 6;
4160
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4402
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4161
4403
  "div",
4162
4404
  {
4163
4405
  ref: elRef,
@@ -4208,7 +4450,7 @@ function FloatingToolbar({
4208
4450
  activeCommands
4209
4451
  }) {
4210
4452
  const { top, left, transform } = calcToolbarPos(rect, parentScroll);
4211
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4453
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4212
4454
  "div",
4213
4455
  {
4214
4456
  ref: elRef,
@@ -4231,11 +4473,11 @@ function FloatingToolbar({
4231
4473
  pointerEvents: "auto",
4232
4474
  whiteSpace: "nowrap"
4233
4475
  },
4234
- children: TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react2.default.Fragment, { children: [
4235
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.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 } }),
4236
4478
  btns.map((btn) => {
4237
4479
  const isActive = activeCommands.has(btn.cmd);
4238
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4480
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4239
4481
  "button",
4240
4482
  {
4241
4483
  title: btn.title,
@@ -4261,7 +4503,7 @@ function FloatingToolbar({
4261
4503
  flexShrink: 0,
4262
4504
  padding: 6
4263
4505
  },
4264
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4506
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4265
4507
  "svg",
4266
4508
  {
4267
4509
  width: "16",
@@ -4294,7 +4536,7 @@ function StateToggle({
4294
4536
  states,
4295
4537
  onStateChange
4296
4538
  }) {
4297
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4539
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4298
4540
  ToggleGroup,
4299
4541
  {
4300
4542
  "data-ohw-state-toggle": "",
@@ -4308,7 +4550,7 @@ function StateToggle({
4308
4550
  left: rect.right - 8,
4309
4551
  transform: "translateX(-100%)"
4310
4552
  },
4311
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime5.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))
4312
4554
  }
4313
4555
  );
4314
4556
  }
@@ -4318,8 +4560,8 @@ function OhhwellsBridge() {
4318
4560
  const router = (0, import_navigation.useRouter)();
4319
4561
  const searchParams = (0, import_navigation.useSearchParams)();
4320
4562
  const isEditMode = isEditSessionActive();
4321
- const [bridgeRoot, setBridgeRoot] = (0, import_react2.useState)(null);
4322
- (0, import_react2.useEffect)(() => {
4563
+ const [bridgeRoot, setBridgeRoot] = (0, import_react3.useState)(null);
4564
+ (0, import_react3.useEffect)(() => {
4323
4565
  const figtreeFontId = "ohw-figtree-font";
4324
4566
  if (!document.getElementById(figtreeFontId)) {
4325
4567
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -4347,39 +4589,39 @@ function OhhwellsBridge() {
4347
4589
  const parts = window.location.hostname.split(".");
4348
4590
  return parts.length >= 3 && parts[0] !== "www" ? parts[0] : "";
4349
4591
  })();
4350
- const postToParent = (0, import_react2.useCallback)((data) => {
4592
+ const postToParent = (0, import_react3.useCallback)((data) => {
4351
4593
  if (typeof window !== "undefined" && window.parent !== window) {
4352
4594
  window.parent.postMessage(data, "*");
4353
4595
  }
4354
4596
  }, []);
4355
- const [fetchState, setFetchState] = (0, import_react2.useState)("idle");
4356
- const autoSaveTimers = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
4357
- const activeElRef = (0, import_react2.useRef)(null);
4358
- const originalContentRef = (0, import_react2.useRef)(null);
4359
- const activeStateElRef = (0, import_react2.useRef)(null);
4360
- const parentScrollRef = (0, import_react2.useRef)(null);
4361
- const toolbarElRef = (0, import_react2.useRef)(null);
4362
- const glowElRef = (0, import_react2.useRef)(null);
4363
- const hoveredImageRef = (0, import_react2.useRef)(null);
4364
- const hoveredImageHasTextOverlapRef = (0, import_react2.useRef)(false);
4365
- const hoveredGapRef = (0, import_react2.useRef)(null);
4366
- const imageUnhoverTimerRef = (0, import_react2.useRef)(null);
4367
- const imageShowTimerRef = (0, import_react2.useRef)(null);
4368
- const editStylesRef = (0, import_react2.useRef)(null);
4369
- const activateRef = (0, import_react2.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)(() => {
4370
4612
  });
4371
- const deactivateRef = (0, import_react2.useRef)(() => {
4613
+ const deactivateRef = (0, import_react3.useRef)(() => {
4372
4614
  });
4373
- const refreshActiveCommandsRef = (0, import_react2.useRef)(() => {
4615
+ const refreshActiveCommandsRef = (0, import_react3.useRef)(() => {
4374
4616
  });
4375
- const postToParentRef = (0, import_react2.useRef)(postToParent);
4617
+ const postToParentRef = (0, import_react3.useRef)(postToParent);
4376
4618
  postToParentRef.current = postToParent;
4377
- const [toolbarRect, setToolbarRect] = (0, import_react2.useState)(null);
4378
- const [toggleState, setToggleState] = (0, import_react2.useState)(null);
4379
- const [maxBadge, setMaxBadge] = (0, import_react2.useState)(null);
4380
- const [activeCommands, setActiveCommands] = (0, import_react2.useState)(/* @__PURE__ */ new Set());
4381
- const [sectionGap, setSectionGap] = (0, import_react2.useState)(null);
4382
- (0, import_react2.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)(() => {
4383
4625
  const update = () => {
4384
4626
  const el = activeElRef.current;
4385
4627
  if (el) setToolbarRect(el.getBoundingClientRect());
@@ -4403,10 +4645,10 @@ function OhhwellsBridge() {
4403
4645
  vvp.removeEventListener("resize", update);
4404
4646
  };
4405
4647
  }, []);
4406
- const refreshStateRules = (0, import_react2.useCallback)(() => {
4648
+ const refreshStateRules = (0, import_react3.useCallback)(() => {
4407
4649
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
4408
4650
  }, []);
4409
- const deactivate = (0, import_react2.useCallback)(() => {
4651
+ const deactivate = (0, import_react3.useCallback)(() => {
4410
4652
  const el = activeElRef.current;
4411
4653
  if (!el) return;
4412
4654
  const key = el.dataset.ohwKey;
@@ -4431,7 +4673,7 @@ function OhhwellsBridge() {
4431
4673
  setActiveCommands(/* @__PURE__ */ new Set());
4432
4674
  postToParent({ type: "ow:exit-edit" });
4433
4675
  }, [postToParent]);
4434
- const activate = (0, import_react2.useCallback)((el) => {
4676
+ const activate = (0, import_react3.useCallback)((el) => {
4435
4677
  if (activeElRef.current === el) return;
4436
4678
  deactivate();
4437
4679
  if (hoveredImageRef.current) {
@@ -4449,7 +4691,7 @@ function OhhwellsBridge() {
4449
4691
  }, [deactivate, postToParent]);
4450
4692
  activateRef.current = activate;
4451
4693
  deactivateRef.current = deactivate;
4452
- (0, import_react2.useLayoutEffect)(() => {
4694
+ (0, import_react3.useLayoutEffect)(() => {
4453
4695
  if (!subdomain || isEditMode) {
4454
4696
  setFetchState("done");
4455
4697
  return;
@@ -4512,7 +4754,7 @@ function OhhwellsBridge() {
4512
4754
  cancelled = true;
4513
4755
  };
4514
4756
  }, [subdomain, isEditMode, pathname]);
4515
- (0, import_react2.useEffect)(() => {
4757
+ (0, import_react3.useEffect)(() => {
4516
4758
  if (!subdomain || isEditMode) return;
4517
4759
  const applyFromCache = () => {
4518
4760
  const content = contentCache.get(subdomain);
@@ -4548,16 +4790,16 @@ function OhhwellsBridge() {
4548
4790
  observer.observe(document.body, { childList: true, subtree: true });
4549
4791
  return () => observer.disconnect();
4550
4792
  }, [subdomain, isEditMode]);
4551
- (0, import_react2.useLayoutEffect)(() => {
4793
+ (0, import_react3.useLayoutEffect)(() => {
4552
4794
  const el = document.getElementById("ohw-loader");
4553
4795
  if (!el) return;
4554
4796
  const visible = Boolean(subdomain) && fetchState !== "done";
4555
4797
  el.style.display = visible ? "flex" : "none";
4556
4798
  }, [subdomain, fetchState]);
4557
- (0, import_react2.useEffect)(() => {
4799
+ (0, import_react3.useEffect)(() => {
4558
4800
  postToParent({ type: "ow:navigation", path: pathname });
4559
4801
  }, [pathname, postToParent]);
4560
- (0, import_react2.useEffect)(() => {
4802
+ (0, import_react3.useEffect)(() => {
4561
4803
  if (!isEditMode) return;
4562
4804
  const measure = () => {
4563
4805
  const h = document.body.scrollHeight;
@@ -4581,7 +4823,7 @@ function OhhwellsBridge() {
4581
4823
  window.removeEventListener("resize", handleResize);
4582
4824
  };
4583
4825
  }, [pathname, isEditMode, postToParent]);
4584
- (0, import_react2.useEffect)(() => {
4826
+ (0, import_react3.useEffect)(() => {
4585
4827
  if (!subdomainFromQuery || isEditMode) return;
4586
4828
  const handleClick = (e) => {
4587
4829
  const anchor = e.target.closest("a");
@@ -4597,7 +4839,7 @@ function OhhwellsBridge() {
4597
4839
  document.addEventListener("click", handleClick, true);
4598
4840
  return () => document.removeEventListener("click", handleClick, true);
4599
4841
  }, [subdomainFromQuery, isEditMode, router]);
4600
- (0, import_react2.useEffect)(() => {
4842
+ (0, import_react3.useEffect)(() => {
4601
4843
  if (!isEditMode) {
4602
4844
  editStylesRef.current?.base.remove();
4603
4845
  editStylesRef.current?.forceHover.remove();
@@ -5412,7 +5654,7 @@ function OhhwellsBridge() {
5412
5654
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
5413
5655
  };
5414
5656
  }, [isEditMode, refreshStateRules]);
5415
- (0, import_react2.useEffect)(() => {
5657
+ (0, import_react3.useEffect)(() => {
5416
5658
  if (!isEditMode) return;
5417
5659
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
5418
5660
  el.removeAttribute("data-ohw-active-state");
@@ -5434,13 +5676,13 @@ function OhhwellsBridge() {
5434
5676
  clearTimeout(timer);
5435
5677
  };
5436
5678
  }, [pathname, isEditMode, refreshStateRules, postToParent]);
5437
- const handleCommand = (0, import_react2.useCallback)((cmd) => {
5679
+ const handleCommand = (0, import_react3.useCallback)((cmd) => {
5438
5680
  document.execCommand(cmd, false);
5439
5681
  activeElRef.current?.focus();
5440
5682
  if (activeElRef.current) setToolbarRect(activeElRef.current.getBoundingClientRect());
5441
5683
  refreshActiveCommandsRef.current();
5442
5684
  }, []);
5443
- const handleStateChange = (0, import_react2.useCallback)((state) => {
5685
+ const handleStateChange = (0, import_react3.useCallback)((state) => {
5444
5686
  if (!activeStateElRef.current) return;
5445
5687
  const el = activeStateElRef.current;
5446
5688
  if (state === "Default") {
@@ -5454,12 +5696,12 @@ function OhhwellsBridge() {
5454
5696
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
5455
5697
  }, [deactivate]);
5456
5698
  return bridgeRoot ? (0, import_react_dom.createPortal)(
5457
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
5458
- toolbarRect && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
5459
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(GlowFrame, { rect: toolbarRect, elRef: glowElRef }),
5460
- /* @__PURE__ */ (0, import_jsx_runtime5.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 })
5461
5703
  ] }),
5462
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5704
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
5463
5705
  "div",
5464
5706
  {
5465
5707
  "data-ohw-max-badge": "",
@@ -5485,7 +5727,7 @@ function OhhwellsBridge() {
5485
5727
  ]
5486
5728
  }
5487
5729
  ),
5488
- toggleState && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
5730
+ toggleState && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5489
5731
  StateToggle,
5490
5732
  {
5491
5733
  rect: toggleState.rect,
@@ -5494,15 +5736,15 @@ function OhhwellsBridge() {
5494
5736
  onStateChange: handleStateChange
5495
5737
  }
5496
5738
  ),
5497
- sectionGap && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5739
+ sectionGap && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
5498
5740
  "div",
5499
5741
  {
5500
5742
  "data-ohw-section-insert-line": "",
5501
5743
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
5502
5744
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
5503
5745
  children: [
5504
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
5505
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
5746
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
5747
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5506
5748
  Badge,
5507
5749
  {
5508
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",
@@ -5515,7 +5757,7 @@ function OhhwellsBridge() {
5515
5757
  children: "Add Section"
5516
5758
  }
5517
5759
  ),
5518
- /* @__PURE__ */ (0, import_jsx_runtime5.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 } })
5519
5761
  ]
5520
5762
  }
5521
5763
  )