@ohhwells/bridge 0.1.64-next.176 → 0.1.65-next.179

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
@@ -157,7 +157,12 @@ function parseAiSectionsState(raw) {
157
157
  media: entry.media && typeof entry.media === "object" ? entry.media : {}
158
158
  }));
159
159
  const removed = Array.isArray(parsed.removed) ? parsed.removed.filter((id) => typeof id === "string" && id.length > 0) : [];
160
- return { v: 1, sections, ...removed.length ? { removed } : {} };
160
+ return {
161
+ v: 1,
162
+ sections,
163
+ ...removed.length ? { removed } : {},
164
+ ...parsed.hideTemplate === true ? { hideTemplate: true } : {}
165
+ };
161
166
  } catch {
162
167
  return EMPTY_AI_SECTIONS;
163
168
  }
@@ -534,6 +539,36 @@ var FEATURE_LINE_CSS = [
534
539
  `background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
535
540
  `mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
536
541
  ].join("");
542
+ function hexLuminance(color) {
543
+ const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
544
+ if (!m) return null;
545
+ const [r2, g, b] = [0, 2, 4].map((i) => {
546
+ const c = parseInt(m[1].slice(i, i + 2), 16) / 255;
547
+ return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
548
+ });
549
+ return 0.2126 * r2 + 0.7152 * g + 0.0722 * b;
550
+ }
551
+ function hexContrast(a, b) {
552
+ const la = hexLuminance(a);
553
+ const lb = hexLuminance(b);
554
+ if (la === null || lb === null) return null;
555
+ const [hi, lo] = la > lb ? [la, lb] : [lb, la];
556
+ return (hi + 0.05) / (lo + 0.05);
557
+ }
558
+ function accentBandContext(brand) {
559
+ const p = brand.palette;
560
+ const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
561
+ if (lightWins) {
562
+ return {
563
+ brand: { ...brand, palette: { dark: p.light, primary: p.light, accent: p.light, light: p.primary } },
564
+ buttonLabel: p.primary
565
+ };
566
+ }
567
+ return {
568
+ brand: { ...brand, palette: { dark: p.dark, primary: p.dark, accent: p.dark, light: p.light } },
569
+ buttonLabel: p.light
570
+ };
571
+ }
537
572
  function textAttrs(ctx, path) {
538
573
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
539
574
  }
@@ -641,7 +676,7 @@ function ButtonEl({
641
676
  textDecoration: "none",
642
677
  cursor: "pointer",
643
678
  ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body),
644
- ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: AI_TREE_TOKENS.textPrimaryForeground }
679
+ ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? AI_TREE_TOKENS.textPrimaryForeground }
645
680
  },
646
681
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
647
682
  }
@@ -1543,11 +1578,14 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1543
1578
  return null;
1544
1579
  }
1545
1580
  const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
1581
+ const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
1582
+ const blockBrand = band?.brand ?? resolvedBrand;
1546
1583
  const ctx = {
1547
- brand: resolvedBrand,
1584
+ brand: blockBrand,
1548
1585
  resolveMedia: resolveMedia ?? (() => null),
1549
- cardSurface: resolvedBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${resolvedBrand.palette.light} 90%, ${resolvedBrand.palette.dark})`,
1550
- keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null
1586
+ cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
1587
+ keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
1588
+ ...band ? { buttonLabel: band.buttonLabel } : {}
1551
1589
  };
1552
1590
  const settings = tree.settings ?? {};
1553
1591
  const pad = AI_TREE_TOKENS.sectionPadding[settings.spacing ?? "balanced"];
@@ -1582,7 +1620,7 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1582
1620
  backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
1583
1621
  backgroundSize: "cover",
1584
1622
  backgroundPosition: "center",
1585
- color: settings.sectionBackground === "accent" ? resolvedBrand.palette.light : void 0
1623
+ color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
1586
1624
  },
1587
1625
  children: [
1588
1626
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
@@ -1639,6 +1677,8 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
1639
1677
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1640
1678
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1641
1679
  var REMOVED_ATTR = "data-ohw-ai-removed";
1680
+ var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
1681
+ var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
1642
1682
  function readRootVar(name) {
1643
1683
  if (typeof document === "undefined") return "";
1644
1684
  return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
@@ -1738,6 +1778,24 @@ function syncRemovedSections(state) {
1738
1778
  }
1739
1779
  }
1740
1780
  }
1781
+ function syncTemplateHidden(state, pageHasSections) {
1782
+ const hide = state.hideTemplate === true && pageHasSections;
1783
+ for (const el of Array.from(document.querySelectorAll(`[${TEMPLATE_HIDDEN_ATTR}]`))) {
1784
+ if (!hide) {
1785
+ el.style.removeProperty("display");
1786
+ el.removeAttribute(TEMPLATE_HIDDEN_ATTR);
1787
+ }
1788
+ }
1789
+ if (!hide) return;
1790
+ for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
1791
+ if (el.hasAttribute(CONTAINER_ATTR)) continue;
1792
+ if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
1793
+ if (el.parentElement?.closest("[data-ohw-section]")) continue;
1794
+ if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR)) continue;
1795
+ el.style.display = "none";
1796
+ el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
1797
+ }
1798
+ }
1741
1799
  function syncReplacedOriginals(state) {
1742
1800
  for (const el of document.querySelectorAll(`[${REPLACED_ATTR}]`)) {
1743
1801
  const byId = el.getAttribute(REPLACED_ATTR) ?? "";
@@ -1808,6 +1866,7 @@ function applyAiSectionsToDom(state, options) {
1808
1866
  }
1809
1867
  syncReplacedOriginals(state);
1810
1868
  syncRemovedSections(state);
1869
+ syncTemplateHidden(state, pageSections.length > 0);
1811
1870
  }
1812
1871
 
1813
1872
  // src/useLinkHrefGuardian.ts
@@ -6589,6 +6648,7 @@ function getChromeStyle(state) {
6589
6648
  }
6590
6649
  function ClampedToolbarSlot({
6591
6650
  placement,
6651
+ align = "center",
6592
6652
  children
6593
6653
  }) {
6594
6654
  const slotRef = (0, import_react7.useRef)(null);
@@ -6610,7 +6670,7 @@ function ClampedToolbarSlot({
6610
6670
  Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN - half)
6611
6671
  );
6612
6672
  const offsetX = clampedCenter - centerX;
6613
- slot.style.transform = `translateX(calc(-50% + ${offsetX}px))`;
6673
+ slot.style.transform = align === "left" ? "none" : `translateX(calc(-50% + ${offsetX}px))`;
6614
6674
  };
6615
6675
  clamp();
6616
6676
  const ro = new ResizeObserver(clamp);
@@ -6621,19 +6681,20 @@ function ClampedToolbarSlot({
6621
6681
  ro.disconnect();
6622
6682
  window.removeEventListener("resize", clamp);
6623
6683
  };
6624
- }, [placement, children]);
6684
+ }, [placement, children, align]);
6625
6685
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6626
6686
  "div",
6627
6687
  {
6628
6688
  ref: slotRef,
6629
6689
  className: cn(
6630
- "pointer-events-auto absolute left-1/2",
6690
+ "pointer-events-auto absolute",
6691
+ align === "left" ? "left-0" : "left-1/2",
6631
6692
  placement === "top" ? "bottom-full" : "top-full"
6632
6693
  ),
6633
6694
  style: {
6634
6695
  marginBottom: placement === "top" ? TOOLBAR_STROKE_GAP : void 0,
6635
6696
  marginTop: placement === "bottom" ? TOOLBAR_STROKE_GAP : void 0,
6636
- transform: "translateX(-50%)"
6697
+ transform: align === "left" ? "none" : "translateX(-50%)"
6637
6698
  },
6638
6699
  "data-ohw-item-toolbar-anchor": placement,
6639
6700
  children
@@ -6702,6 +6763,7 @@ function ItemInteractionLayer({
6702
6763
  onItemClick,
6703
6764
  itemDragSurface = true,
6704
6765
  chromeGap,
6766
+ toolbarAlign = "center",
6705
6767
  className
6706
6768
  }) {
6707
6769
  if (state === "default") return null;
@@ -6789,8 +6851,8 @@ function ItemInteractionLayer({
6789
6851
  )
6790
6852
  }
6791
6853
  ),
6792
- showToolbar && state === "active-top" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ClampedToolbarSlot, { placement: "top", children: toolbar }),
6793
- showToolbar && state === "active-bottom" && !useDetachedBelowToolbar && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ClampedToolbarSlot, { placement: "bottom", children: toolbar }),
6854
+ showToolbar && state === "active-top" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ClampedToolbarSlot, { align: toolbarAlign, placement: "top", children: toolbar }),
6855
+ showToolbar && state === "active-bottom" && !useDetachedBelowToolbar && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ClampedToolbarSlot, { align: toolbarAlign, placement: "bottom", children: toolbar }),
6794
6856
  useDetachedBelowToolbar && toolbarBelowRect ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6795
6857
  DetachedBelowToolbarSlot,
6796
6858
  {
@@ -6804,14 +6866,580 @@ function ItemInteractionLayer({
6804
6866
  );
6805
6867
  }
6806
6868
 
6869
+ // src/lib/forms.ts
6870
+ var FORM_SELECTOR = '[data-ohw-editable="form"]';
6871
+ function getFormElement(el) {
6872
+ if (!el) return null;
6873
+ return el.closest(FORM_SELECTOR);
6874
+ }
6875
+ function formKeyOf(form) {
6876
+ return form.getAttribute("data-ohw-key");
6877
+ }
6878
+ function formHasLongText(form) {
6879
+ return form.querySelector("textarea") !== null;
6880
+ }
6881
+ function collectFormFields(form) {
6882
+ const fields = {};
6883
+ form.querySelectorAll(
6884
+ "input, textarea, select"
6885
+ ).forEach((input) => {
6886
+ const type = input.type;
6887
+ if (type === "submit" || type === "button" || type === "reset") return;
6888
+ const key = input.getAttribute("name") ?? input.getAttribute("data-ohw-key");
6889
+ if (!key) return;
6890
+ if (type === "checkbox") {
6891
+ fields[key] = input.checked ? "yes" : "no";
6892
+ return;
6893
+ }
6894
+ fields[key] = input.value;
6895
+ });
6896
+ return fields;
6897
+ }
6898
+ function formStateContainer(form) {
6899
+ return form.closest("[data-ohw-editable-state]") ?? form.parentElement ?? form;
6900
+ }
6901
+ function showSuccess(form, message) {
6902
+ const container = formStateContainer(form);
6903
+ const views = container.querySelectorAll("[data-ohw-state-view]");
6904
+ if (views.length > 0) {
6905
+ views.forEach((view) => {
6906
+ view.style.display = view.getAttribute("data-ohw-state-view") === "success" ? "block" : "none";
6907
+ });
6908
+ return;
6909
+ }
6910
+ const text = message ?? form.getAttribute("data-ohw-success-text") ?? DEFAULT_SUCCESS_TEXT;
6911
+ const note = document.createElement("p");
6912
+ note.setAttribute("data-ohw-form-success", "");
6913
+ note.setAttribute("role", "status");
6914
+ note.innerHTML = text;
6915
+ form.replaceChildren(note);
6916
+ }
6917
+ function showSubmitError(form) {
6918
+ let note = form.querySelector("[data-ohw-form-error]");
6919
+ if (!note) {
6920
+ note = document.createElement("p");
6921
+ note.setAttribute("data-ohw-form-error", "");
6922
+ note.setAttribute("role", "alert");
6923
+ note.style.marginTop = "8px";
6924
+ form.appendChild(note);
6925
+ }
6926
+ note.textContent = "Something went wrong. Please try again.";
6927
+ note.style.display = "";
6928
+ }
6929
+ function clearSubmitError(form) {
6930
+ const note = form.querySelector("[data-ohw-form-error]");
6931
+ if (note) note.style.display = "none";
6932
+ }
6933
+ var SUCCESS_TEXT_ATTR = "data-ohw-form-success-text";
6934
+ var HIDDEN_ATTR = "data-ohw-form-hidden";
6935
+ var DEFAULT_SUCCESS_TEXT = "Thanks! We'll be in touch.";
6936
+ function formSuccessKey(formKey) {
6937
+ return `${formKey}-success`;
6938
+ }
6939
+ function ensureSuccessTextEl(form, formKey, initialText) {
6940
+ let el = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
6941
+ if (el) return el;
6942
+ el = document.createElement("p");
6943
+ el.setAttribute(SUCCESS_TEXT_ATTR, "");
6944
+ el.setAttribute("data-ohw-editable", "text");
6945
+ el.setAttribute("data-ohw-key", formSuccessKey(formKey));
6946
+ el.innerHTML = initialText;
6947
+ el.style.display = "none";
6948
+ el.style.margin = "56px 4px 16px";
6949
+ form.appendChild(el);
6950
+ return el;
6951
+ }
6952
+ function setFormViewState(form, formKey, state, initialText) {
6953
+ const success = ensureSuccessTextEl(form, formKey, initialText);
6954
+ Array.from(form.children).forEach((child) => {
6955
+ if (!(child instanceof HTMLElement) || child === success) return;
6956
+ if (state === "success") {
6957
+ if (!child.hasAttribute(HIDDEN_ATTR)) {
6958
+ child.setAttribute(HIDDEN_ATTR, child.style.display);
6959
+ child.style.display = "none";
6960
+ }
6961
+ } else if (child.hasAttribute(HIDDEN_ATTR)) {
6962
+ child.style.display = child.getAttribute(HIDDEN_ATTR) ?? "";
6963
+ child.removeAttribute(HIDDEN_ATTR);
6964
+ }
6965
+ });
6966
+ success.style.display = state === "success" ? "" : "none";
6967
+ }
6968
+ var BOUND_ATTR = "data-ohw-form-bound";
6969
+ function bindPublishedForms(apiUrl, subdomain, content = {}) {
6970
+ document.querySelectorAll(FORM_SELECTOR).forEach((form) => {
6971
+ if (form.hasAttribute(BOUND_ATTR)) return;
6972
+ if (!(form instanceof HTMLFormElement)) return;
6973
+ const formKey = formKeyOf(form);
6974
+ if (!formKey) return;
6975
+ form.setAttribute(BOUND_ATTR, "");
6976
+ form.addEventListener("submit", async (e) => {
6977
+ e.preventDefault();
6978
+ clearSubmitError(form);
6979
+ const submitButton = form.querySelector(
6980
+ 'button[type="submit"], input[type="submit"], button:not([type])'
6981
+ );
6982
+ if (submitButton) submitButton.disabled = true;
6983
+ try {
6984
+ const response = await fetch(`${apiUrl}/api/public/sites/${subdomain}/forms/submissions`, {
6985
+ method: "POST",
6986
+ headers: { "Content-Type": "application/json" },
6987
+ body: JSON.stringify({
6988
+ formKey,
6989
+ fields: collectFormFields(form),
6990
+ hasLongText: formHasLongText(form)
6991
+ })
6992
+ });
6993
+ if (!response.ok) throw new Error(`submit failed: ${response.status}`);
6994
+ showSuccess(form, content[formSuccessKey(formKey)]);
6995
+ } catch {
6996
+ showSubmitError(form);
6997
+ if (submitButton) submitButton.disabled = false;
6998
+ }
6999
+ });
7000
+ });
7001
+ }
7002
+
7003
+ // src/lib/form-fields.ts
7004
+ var FIELD_DEFAULTS = {
7005
+ "short-text": { label: "Short text", placeholder: "Type placeholder text..." },
7006
+ "long-text": { label: "Long text", placeholder: "How can we help?" },
7007
+ email: { label: "Email", placeholder: "hello@example.com" },
7008
+ phone: { label: "Phone number", placeholder: "(555) 000-0000" }
7009
+ };
7010
+ var FIELD_TYPES = [
7011
+ { type: "short-text", label: "Short text" },
7012
+ { type: "long-text", label: "Long text" },
7013
+ { type: "email", label: "Email" },
7014
+ { type: "phone", label: "Phone" }
7015
+ ];
7016
+ var FIELD_ATTR = "data-ohw-form-field";
7017
+ var FIELD_TYPE_ATTR = "data-ohw-field-type";
7018
+ function fieldsKey(formKey) {
7019
+ return `${formKey}-fields`;
7020
+ }
7021
+ function inputTagFor(type) {
7022
+ if (type === "long-text") return { tag: "textarea" };
7023
+ if (type === "email") return { tag: "input", inputType: "email" };
7024
+ if (type === "phone") return { tag: "input", inputType: "tel" };
7025
+ return { tag: "input", inputType: "text" };
7026
+ }
7027
+ function inferType(input) {
7028
+ if (input.tagName === "TEXTAREA") return "long-text";
7029
+ const type = input.type;
7030
+ if (type === "email") return "email";
7031
+ if (type === "tel") return "phone";
7032
+ return "short-text";
7033
+ }
7034
+ function ensureFieldLabel(wrapper, key) {
7035
+ const existing = fieldLabelOf(wrapper);
7036
+ if (existing) {
7037
+ if (!existing.hasAttribute("data-ohw-editable")) {
7038
+ existing.setAttribute("data-ohw-editable", "text");
7039
+ existing.setAttribute("data-ohw-key", `${key}-label`);
7040
+ }
7041
+ return existing;
7042
+ }
7043
+ const input = fieldInputOf(wrapper);
7044
+ const label = document.createElement("label");
7045
+ label.setAttribute("data-ohw-editable", "text");
7046
+ label.setAttribute("data-ohw-key", `${key}-label`);
7047
+ label.setAttribute("data-ohw-field-label", "");
7048
+ label.style.display = "block";
7049
+ label.style.marginBottom = "6px";
7050
+ label.style.fontSize = "13px";
7051
+ label.style.fontWeight = "500";
7052
+ label.style.lineHeight = "1.3";
7053
+ const fromPlaceholder = input?.getAttribute("placeholder")?.trim();
7054
+ const text = fromPlaceholder && fromPlaceholder.length < 40 ? fromPlaceholder : key.replace(/[-_]/g, " ");
7055
+ label.textContent = input?.hasAttribute("required") ? `${text} *` : text;
7056
+ if (input && input.parentElement === wrapper) wrapper.insertBefore(label, input);
7057
+ else wrapper.insertBefore(label, wrapper.firstChild);
7058
+ return label;
7059
+ }
7060
+ function listFieldWrappers(form) {
7061
+ return Array.from(form.querySelectorAll(`[${FIELD_ATTR}]`));
7062
+ }
7063
+ function getFieldWrapper(el) {
7064
+ return el?.closest(`[${FIELD_ATTR}]`) ?? null;
7065
+ }
7066
+ function fieldInputOf(wrapper) {
7067
+ return wrapper.querySelector("input, textarea");
7068
+ }
7069
+ function fieldLabelOf(wrapper) {
7070
+ return wrapper.querySelector("label");
7071
+ }
7072
+ function fieldKeyOf(wrapper) {
7073
+ return wrapper.getAttribute(FIELD_ATTR) ?? "";
7074
+ }
7075
+ function fieldTypeOf(wrapper) {
7076
+ return wrapper.getAttribute(FIELD_TYPE_ATTR) ?? "short-text";
7077
+ }
7078
+ function isFieldRequired(wrapper) {
7079
+ return fieldInputOf(wrapper)?.hasAttribute("required") ?? false;
7080
+ }
7081
+ function markFormFields(form) {
7082
+ form.querySelectorAll("input, textarea").forEach((input) => {
7083
+ const type = input.type;
7084
+ if (type === "submit" || type === "button" || type === "reset" || type === "hidden") return;
7085
+ if (getFieldWrapper(input)) return;
7086
+ let wrapper = input;
7087
+ while (wrapper.parentElement && wrapper.parentElement !== form && wrapper.parentElement.querySelectorAll("input, textarea").length === 1) {
7088
+ wrapper = wrapper.parentElement;
7089
+ }
7090
+ if (wrapper === input) {
7091
+ const box = document.createElement("div");
7092
+ box.setAttribute("data-ohw-field-box", "");
7093
+ input.replaceWith(box);
7094
+ box.appendChild(input);
7095
+ wrapper = box;
7096
+ }
7097
+ const key = input.getAttribute("name") ?? input.getAttribute("data-ohw-key") ?? `field-${Date.now()}`;
7098
+ wrapper.setAttribute(FIELD_ATTR, key);
7099
+ wrapper.setAttribute(FIELD_TYPE_ATTR, inferType(input));
7100
+ ensureFieldLabel(wrapper, key);
7101
+ syncRequiredMark(wrapper);
7102
+ });
7103
+ return listFieldWrappers(form);
7104
+ }
7105
+ function readFieldsFromDom(form) {
7106
+ return listFieldWrappers(form).map((wrapper) => {
7107
+ const input = fieldInputOf(wrapper);
7108
+ return {
7109
+ key: fieldKeyOf(wrapper),
7110
+ type: fieldTypeOf(wrapper),
7111
+ label: fieldLabelOf(wrapper)?.textContent?.trim() ?? "",
7112
+ placeholder: input?.getAttribute("placeholder") ?? "",
7113
+ required: Boolean(input?.hasAttribute("required"))
7114
+ };
7115
+ });
7116
+ }
7117
+ function parseFieldSpecs(raw) {
7118
+ if (!raw) return null;
7119
+ try {
7120
+ const parsed = JSON.parse(raw);
7121
+ return Array.isArray(parsed) ? parsed : null;
7122
+ } catch {
7123
+ return null;
7124
+ }
7125
+ }
7126
+ function isDefaultText(value, pick) {
7127
+ const trimmed = value.trim().replace(/\s*\*$/, "");
7128
+ if (!trimmed) return true;
7129
+ return Object.values(FIELD_DEFAULTS).some((defaults) => pick(defaults) === trimmed);
7130
+ }
7131
+ function applyFieldType(wrapper, type) {
7132
+ const input = fieldInputOf(wrapper);
7133
+ if (!input) return;
7134
+ const defaults = FIELD_DEFAULTS[type];
7135
+ const placeholder = input.getAttribute("placeholder") ?? "";
7136
+ const label = fieldLabelOf(wrapper);
7137
+ const followsDefaults = {
7138
+ // A label the owner wrote stays; one that still reads as a type name (or as the old
7139
+ // placeholder the template shipped) follows the new type.
7140
+ label: isDefaultText(label?.textContent ?? "", (d) => d.label) || (label?.textContent ?? "").trim().replace(/\s*\*$/, "") === placeholder.trim()
7141
+ };
7142
+ const { tag, inputType } = inputTagFor(type);
7143
+ wrapper.setAttribute(FIELD_TYPE_ATTR, type);
7144
+ const shedSize = (el) => {
7145
+ if (type === "long-text") return;
7146
+ el.style.removeProperty("height");
7147
+ el.style.removeProperty("min-height");
7148
+ el.style.removeProperty("resize");
7149
+ el.removeAttribute("rows");
7150
+ };
7151
+ const applyDefaults = (el) => {
7152
+ el.setAttribute("placeholder", defaults.placeholder);
7153
+ if (label && followsDefaults.label) {
7154
+ const required = (label.textContent ?? "").trim().endsWith("*");
7155
+ label.textContent = required ? `${defaults.label} *` : defaults.label;
7156
+ }
7157
+ };
7158
+ if (input.tagName.toLowerCase() === tag) {
7159
+ if (inputType) input.type = inputType;
7160
+ shedSize(input);
7161
+ applyDefaults(input);
7162
+ return;
7163
+ }
7164
+ const next = document.createElement(tag);
7165
+ Array.from(input.attributes).forEach((attr) => {
7166
+ if (attr.name === "type") return;
7167
+ next.setAttribute(attr.name, attr.value);
7168
+ });
7169
+ if (inputType) next.setAttribute("type", inputType);
7170
+ shedSize(next);
7171
+ if (type === "long-text") next.setAttribute("rows", "4");
7172
+ input.replaceWith(next);
7173
+ applyDefaults(next);
7174
+ }
7175
+ function syncRequiredMark(wrapper) {
7176
+ const label = fieldLabelOf(wrapper);
7177
+ if (!label) return;
7178
+ const required = isFieldRequired(wrapper);
7179
+ const base = (label.textContent ?? "").replace(/\s*\*\s*$/, "").trimEnd();
7180
+ const next = required ? `${base} *` : base;
7181
+ if (label.textContent !== next) label.textContent = next;
7182
+ }
7183
+ function setFieldRequired(wrapper, required) {
7184
+ const input = fieldInputOf(wrapper);
7185
+ const label = fieldLabelOf(wrapper);
7186
+ if (!input) return;
7187
+ if (required) input.setAttribute("required", "");
7188
+ else input.removeAttribute("required");
7189
+ if (label) syncRequiredMark(wrapper);
7190
+ }
7191
+ var PLACEHOLDER_EDIT_ATTR = "data-ohw-placeholder-edit";
7192
+ function beginPlaceholderEdit(wrapper) {
7193
+ const input = fieldInputOf(wrapper);
7194
+ if (!input || input.hasAttribute(PLACEHOLDER_EDIT_ATTR)) return;
7195
+ input.setAttribute(PLACEHOLDER_EDIT_ATTR, "");
7196
+ input.value = input.getAttribute("placeholder") ?? "";
7197
+ input.setAttribute("placeholder", "");
7198
+ }
7199
+ function commitPlaceholderEdit(wrapper) {
7200
+ if (!wrapper) return false;
7201
+ const input = fieldInputOf(wrapper);
7202
+ if (!input || !input.hasAttribute(PLACEHOLDER_EDIT_ATTR)) return false;
7203
+ const typed = input.value;
7204
+ input.removeAttribute(PLACEHOLDER_EDIT_ATTR);
7205
+ input.value = "";
7206
+ const previous = input.getAttribute("placeholder") ?? "";
7207
+ input.setAttribute("placeholder", typed);
7208
+ return previous !== typed;
7209
+ }
7210
+ function setFieldPlaceholder(wrapper, placeholder) {
7211
+ fieldInputOf(wrapper)?.setAttribute("placeholder", placeholder);
7212
+ }
7213
+ function uniqueKey(form, base) {
7214
+ const taken = new Set(listFieldWrappers(form).map(fieldKeyOf));
7215
+ if (!taken.has(base)) return base;
7216
+ let n = 2;
7217
+ while (taken.has(`${base}-${n}`)) n += 1;
7218
+ return `${base}-${n}`;
7219
+ }
7220
+ function insertField(form, type) {
7221
+ const existing = listFieldWrappers(form);
7222
+ const source = existing[existing.length - 1] ?? null;
7223
+ const key = uniqueKey(form, type);
7224
+ let wrapper;
7225
+ if (source) {
7226
+ wrapper = source.cloneNode(true);
7227
+ source.after(wrapper);
7228
+ } else {
7229
+ wrapper = document.createElement("div");
7230
+ const label2 = document.createElement("label");
7231
+ const input2 = document.createElement("input");
7232
+ wrapper.append(label2, input2);
7233
+ const submit = form.querySelector('button, input[type="submit"]');
7234
+ if (submit) submit.before(wrapper);
7235
+ else form.appendChild(wrapper);
7236
+ }
7237
+ wrapper.setAttribute(FIELD_ATTR, key);
7238
+ wrapper.setAttribute(FIELD_TYPE_ATTR, type);
7239
+ applyFieldType(wrapper, type);
7240
+ const defaults = FIELD_DEFAULTS[type];
7241
+ const input = fieldInputOf(wrapper);
7242
+ if (input) {
7243
+ input.setAttribute("name", key);
7244
+ input.setAttribute("data-ohw-key", key);
7245
+ input.removeAttribute("required");
7246
+ input.setAttribute("placeholder", defaults.placeholder);
7247
+ input.value = "";
7248
+ }
7249
+ const label = ensureFieldLabel(wrapper, key);
7250
+ label.textContent = defaults.label;
7251
+ label.setAttribute("data-ohw-key", `${key}-label`);
7252
+ return wrapper;
7253
+ }
7254
+ function duplicateField(form, wrapper) {
7255
+ const copy = wrapper.cloneNode(true);
7256
+ const key = uniqueKey(form, `${fieldKeyOf(wrapper)}-copy`);
7257
+ copy.setAttribute(FIELD_ATTR, key);
7258
+ const input = fieldInputOf(copy);
7259
+ if (input) {
7260
+ input.setAttribute("name", key);
7261
+ input.setAttribute("data-ohw-key", key);
7262
+ input.value = "";
7263
+ }
7264
+ wrapper.after(copy);
7265
+ return copy;
7266
+ }
7267
+ function removeField(wrapper) {
7268
+ wrapper.remove();
7269
+ }
7270
+ function moveField(form, key, toIndex) {
7271
+ const wrappers = listFieldWrappers(form);
7272
+ const moving = wrappers.find((wrapper) => fieldKeyOf(wrapper) === key);
7273
+ if (!moving) return;
7274
+ const rest = wrappers.filter((wrapper) => wrapper !== moving);
7275
+ const target = rest[Math.max(0, Math.min(rest.length, toIndex))];
7276
+ if (target) target.before(moving);
7277
+ else rest[rest.length - 1]?.after(moving);
7278
+ }
7279
+ function reconcileFieldsFromContent(form, content) {
7280
+ const formKey = form.getAttribute("data-ohw-key");
7281
+ if (!formKey) return;
7282
+ markFormFields(form);
7283
+ const stored = parseFieldSpecs(content[fieldsKey(formKey)]);
7284
+ if (!stored) return;
7285
+ const byKey = new Map(listFieldWrappers(form).map((wrapper) => [fieldKeyOf(wrapper), wrapper]));
7286
+ stored.forEach((spec) => {
7287
+ let wrapper = byKey.get(spec.key) ?? null;
7288
+ if (!wrapper) {
7289
+ wrapper = insertField(form, spec.type);
7290
+ if (!wrapper) return;
7291
+ wrapper.setAttribute(FIELD_ATTR, spec.key);
7292
+ const input = fieldInputOf(wrapper);
7293
+ if (input) {
7294
+ input.setAttribute("name", spec.key);
7295
+ input.setAttribute("data-ohw-key", spec.key);
7296
+ }
7297
+ byKey.set(spec.key, wrapper);
7298
+ }
7299
+ applyFieldType(wrapper, spec.type);
7300
+ setFieldRequired(wrapper, spec.required);
7301
+ setFieldPlaceholder(wrapper, spec.placeholder);
7302
+ const label = fieldLabelOf(wrapper);
7303
+ if (label && spec.label) label.textContent = spec.label;
7304
+ });
7305
+ const wanted = new Set(stored.map((spec) => spec.key));
7306
+ listFieldWrappers(form).forEach((wrapper) => {
7307
+ if (!wanted.has(fieldKeyOf(wrapper))) wrapper.remove();
7308
+ });
7309
+ stored.forEach((spec, index) => moveField(form, spec.key, index));
7310
+ }
7311
+
7312
+ // src/ui/form-field-toolbar.tsx
7313
+ var import_lucide_react4 = require("lucide-react");
7314
+ var import_jsx_runtime13 = require("react/jsx-runtime");
7315
+ var TYPE_ICONS = {
7316
+ "short-text": import_lucide_react4.Type,
7317
+ "long-text": import_lucide_react4.TextQuote,
7318
+ email: import_lucide_react4.AtSign,
7319
+ phone: import_lucide_react4.Phone
7320
+ };
7321
+ function FormFieldToolbar({
7322
+ type,
7323
+ required,
7324
+ onTypeChange,
7325
+ onRequiredToggle,
7326
+ onDuplicate,
7327
+ onDelete
7328
+ }) {
7329
+ const TypeIcon = TYPE_ICONS[type];
7330
+ const typeLabel = FIELD_TYPES.find((entry) => entry.type === type)?.label ?? "Short text";
7331
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7332
+ "div",
7333
+ {
7334
+ "data-ohw-field-toolbar": "",
7335
+ className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
7336
+ children: [
7337
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DropdownMenu, { children: [
7338
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7339
+ "button",
7340
+ {
7341
+ type: "button",
7342
+ className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted/70",
7343
+ "data-ohw-field-type-trigger": "",
7344
+ children: [
7345
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TypeIcon, { size: 14, strokeWidth: 1.75, "aria-hidden": true }),
7346
+ typeLabel,
7347
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.ChevronDown, { size: 13, className: "text-muted-foreground", "aria-hidden": true })
7348
+ ]
7349
+ }
7350
+ ) }),
7351
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-[190px] p-1", children: FIELD_TYPES.map((entry) => {
7352
+ const Icon = TYPE_ICONS[entry.type];
7353
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7354
+ DropdownMenuItem,
7355
+ {
7356
+ onSelect: () => onTypeChange(entry.type),
7357
+ className: "rounded-md py-2 text-[13px] " + (entry.type === type ? "bg-primary/10" : ""),
7358
+ children: [
7359
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Icon, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
7360
+ entry.label
7361
+ ]
7362
+ },
7363
+ entry.type
7364
+ );
7365
+ }) })
7366
+ ] }),
7367
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
7368
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7369
+ "button",
7370
+ {
7371
+ type: "button",
7372
+ "aria-pressed": required,
7373
+ onClick: onRequiredToggle,
7374
+ className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-medium transition-colors " + (required ? "bg-primary/10 text-primary" : "text-foreground hover:bg-muted/70"),
7375
+ "data-ohw-field-required": "",
7376
+ children: [
7377
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.Asterisk, { size: 14, strokeWidth: 2, "aria-hidden": true }),
7378
+ "Required"
7379
+ ]
7380
+ }
7381
+ ),
7382
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
7383
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DropdownMenu, { children: [
7384
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7385
+ "button",
7386
+ {
7387
+ type: "button",
7388
+ title: "More",
7389
+ "aria-label": "More",
7390
+ className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/70",
7391
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.MoreHorizontal, { size: 15, "aria-hidden": true })
7392
+ }
7393
+ ) }),
7394
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-[170px] p-1", children: [
7395
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DropdownMenuItem, { onSelect: onDuplicate, className: "rounded-md py-2 text-[13px]", children: [
7396
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.Copy, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
7397
+ "Duplicate"
7398
+ ] }),
7399
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DropdownMenuItem, { variant: "destructive", onSelect: onDelete, className: "rounded-md py-2 text-[13px]", children: [
7400
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.Trash2, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
7401
+ "Delete"
7402
+ ] })
7403
+ ] })
7404
+ ] })
7405
+ ]
7406
+ }
7407
+ );
7408
+ }
7409
+ function FieldTypePicker({ onPick }) {
7410
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7411
+ "div",
7412
+ {
7413
+ "data-ohw-field-type-picker": "",
7414
+ className: "pointer-events-auto grid w-[420px] grid-cols-3 gap-3 rounded-xl border border-border bg-background p-4 shadow-lg",
7415
+ children: FIELD_TYPES.map((entry) => {
7416
+ const Icon = TYPE_ICONS[entry.type];
7417
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7418
+ "button",
7419
+ {
7420
+ type: "button",
7421
+ onClick: () => onPick(entry.type),
7422
+ className: "flex h-[104px] flex-col items-center justify-center gap-3 rounded-xl border border-border text-[15px] font-medium text-foreground transition-colors hover:border-primary hover:bg-primary/5",
7423
+ children: [
7424
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Icon, { size: 26, strokeWidth: 1.5, "aria-hidden": true }),
7425
+ entry.label
7426
+ ]
7427
+ },
7428
+ entry.type
7429
+ );
7430
+ })
7431
+ }
7432
+ );
7433
+ }
7434
+
6807
7435
  // src/ui/MediaOverlay.tsx
6808
7436
  var React7 = __toESM(require("react"), 1);
6809
- var import_lucide_react4 = require("lucide-react");
7437
+ var import_lucide_react5 = require("lucide-react");
6810
7438
 
6811
7439
  // src/ui/button.tsx
6812
7440
  var React6 = __toESM(require("react"), 1);
6813
7441
  var import_radix_ui5 = require("radix-ui");
6814
- var import_jsx_runtime13 = require("react/jsx-runtime");
7442
+ var import_jsx_runtime14 = require("react/jsx-runtime");
6815
7443
  var buttonVariants = cva(
6816
7444
  "inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium transition-colors outline-none disabled:pointer-events-none disabled:opacity-50 min-w-[80px] px-3 py-2",
6817
7445
  {
@@ -6835,7 +7463,7 @@ var buttonVariants = cva(
6835
7463
  var Button = React6.forwardRef(
6836
7464
  ({ className, variant, size, asChild = false, ...props }, ref) => {
6837
7465
  const Comp = asChild ? import_radix_ui5.Slot.Root : "button";
6838
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7466
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6839
7467
  Comp,
6840
7468
  {
6841
7469
  ref,
@@ -6849,7 +7477,7 @@ var Button = React6.forwardRef(
6849
7477
  Button.displayName = "Button";
6850
7478
 
6851
7479
  // src/ui/MediaOverlay.tsx
6852
- var import_jsx_runtime14 = require("react/jsx-runtime");
7480
+ var import_jsx_runtime15 = require("react/jsx-runtime");
6853
7481
  var MEDIA_UPLOAD_FADE_MS = 300;
6854
7482
  var VIDEO_SETTINGS_BAR_INSET = 8;
6855
7483
  var OVERLAY_BUTTON_STYLE = {
@@ -6907,7 +7535,7 @@ function MediaOverlay({
6907
7535
  return () => anim.cancel();
6908
7536
  }, [isUploading, fadingOut, onFadeOutComplete, hover.key]);
6909
7537
  if (isUploading) {
6910
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7538
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6911
7539
  "div",
6912
7540
  {
6913
7541
  ref: skeletonRef,
@@ -6916,11 +7544,11 @@ function MediaOverlay({
6916
7544
  "data-ohw-media-skeleton": "",
6917
7545
  "aria-hidden": true,
6918
7546
  style: { ...box, pointerEvents: "none" },
6919
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("style", { children: SKELETON_CSS })
7547
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("style", { children: SKELETON_CSS })
6920
7548
  }
6921
7549
  );
6922
7550
  }
6923
- const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
7551
+ const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
6924
7552
  "div",
6925
7553
  {
6926
7554
  "data-ohw-bridge": "",
@@ -6936,7 +7564,7 @@ function MediaOverlay({
6936
7564
  },
6937
7565
  onClick: (e) => e.stopPropagation(),
6938
7566
  children: [
6939
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7567
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6940
7568
  Button,
6941
7569
  {
6942
7570
  "data-ohw-media-overlay": "",
@@ -6951,10 +7579,10 @@ function MediaOverlay({
6951
7579
  e.stopPropagation();
6952
7580
  onVideoSettingsChange?.(hover.key, { autoplay: !autoplay });
6953
7581
  },
6954
- children: autoplay ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react4.Pause, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react4.Play, { size: 14 })
7582
+ children: autoplay ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Pause, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Play, { size: 14 })
6955
7583
  }
6956
7584
  ),
6957
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7585
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6958
7586
  Button,
6959
7587
  {
6960
7588
  "data-ohw-media-overlay": "",
@@ -6969,15 +7597,15 @@ function MediaOverlay({
6969
7597
  e.stopPropagation();
6970
7598
  onVideoSettingsChange?.(hover.key, { muted: !muted });
6971
7599
  },
6972
- children: muted ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react4.VolumeX, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react4.Volume2, { size: 14 })
7600
+ children: muted ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.VolumeX, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Volume2, { size: 14 })
6973
7601
  }
6974
7602
  )
6975
7603
  ]
6976
7604
  }
6977
7605
  ) : null;
6978
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
7606
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
6979
7607
  settingsBar,
6980
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7608
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6981
7609
  "div",
6982
7610
  {
6983
7611
  "data-ohw-bridge": "",
@@ -6994,7 +7622,7 @@ function MediaOverlay({
6994
7622
  background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
6995
7623
  },
6996
7624
  onClick: () => onReplace(hover.key),
6997
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
7625
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
6998
7626
  Button,
6999
7627
  {
7000
7628
  "data-ohw-media-overlay": "",
@@ -7012,7 +7640,7 @@ function MediaOverlay({
7012
7640
  onReplace(hover.key);
7013
7641
  },
7014
7642
  children: [
7015
- isVideo ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react4.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react4.ImageIcon, { size: 14 }),
7643
+ isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
7016
7644
  isVideo ? "Replace video" : "Replace image"
7017
7645
  ]
7018
7646
  }
@@ -7023,8 +7651,8 @@ function MediaOverlay({
7023
7651
  }
7024
7652
 
7025
7653
  // src/ui/CarouselOverlay.tsx
7026
- var import_lucide_react5 = require("lucide-react");
7027
- var import_jsx_runtime15 = require("react/jsx-runtime");
7654
+ var import_lucide_react6 = require("lucide-react");
7655
+ var import_jsx_runtime16 = require("react/jsx-runtime");
7028
7656
  var OVERLAY_BUTTON_STYLE2 = {
7029
7657
  pointerEvents: "auto",
7030
7658
  fontFamily: "Inter, sans-serif",
@@ -7036,7 +7664,7 @@ function CarouselOverlay({
7036
7664
  onEdit
7037
7665
  }) {
7038
7666
  const { rect } = hover;
7039
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7667
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7040
7668
  "div",
7041
7669
  {
7042
7670
  "data-ohw-bridge": "",
@@ -7054,7 +7682,7 @@ function CarouselOverlay({
7054
7682
  background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7055
7683
  },
7056
7684
  onClick: () => onEdit(hover.key),
7057
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7685
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
7058
7686
  Button,
7059
7687
  {
7060
7688
  "data-ohw-carousel-overlay": "",
@@ -7068,7 +7696,7 @@ function CarouselOverlay({
7068
7696
  onEdit(hover.key);
7069
7697
  },
7070
7698
  children: [
7071
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.GalleryHorizontal, { size: 14 }),
7699
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react6.GalleryHorizontal, { size: 14 }),
7072
7700
  "Edit gallery"
7073
7701
  ]
7074
7702
  }
@@ -7079,7 +7707,7 @@ function CarouselOverlay({
7079
7707
 
7080
7708
  // src/ui/ai-section/AiSectionOverlay.tsx
7081
7709
  var import_react8 = require("react");
7082
- var import_lucide_react6 = require("lucide-react");
7710
+ var import_lucide_react7 = require("lucide-react");
7083
7711
 
7084
7712
  // src/lib/sections.ts
7085
7713
  var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
@@ -7093,6 +7721,8 @@ function parseSectionsFromRoot(root) {
7093
7721
  const id = el.getAttribute("data-ohw-section") ?? "";
7094
7722
  if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
7095
7723
  if (el.parentElement?.closest("[data-ohw-section]")) continue;
7724
+ if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
7725
+ continue;
7096
7726
  seen.add(id);
7097
7727
  const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
7098
7728
  sections.push({ id, label });
@@ -7109,7 +7739,7 @@ function parseSectionsFromHtml(html) {
7109
7739
  }
7110
7740
 
7111
7741
  // src/ui/ai-section/AiSectionOverlay.tsx
7112
- var import_jsx_runtime16 = require("react/jsx-runtime");
7742
+ var import_jsx_runtime17 = require("react/jsx-runtime");
7113
7743
  function findSectionElement(instanceId) {
7114
7744
  const escaped = CSS.escape(instanceId);
7115
7745
  return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
@@ -7177,8 +7807,8 @@ function ReviewButton({
7177
7807
  color
7178
7808
  }) {
7179
7809
  const [hover, setHover] = (0, import_react8.useState)(false);
7180
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { position: "relative" }, children: [
7181
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7810
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { position: "relative" }, children: [
7811
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7182
7812
  "button",
7183
7813
  {
7184
7814
  type: "button",
@@ -7202,7 +7832,7 @@ function ReviewButton({
7202
7832
  children
7203
7833
  }
7204
7834
  ),
7205
- hover && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7835
+ hover && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7206
7836
  "div",
7207
7837
  {
7208
7838
  style: {
@@ -7368,8 +7998,8 @@ function AiSectionOverlay({
7368
7998
  isLast
7369
7999
  });
7370
8000
  }, [activeSelectionId, selectionRect, postToParent2]);
7371
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
7372
- hoverRect && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8001
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
8002
+ hoverRect && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7373
8003
  "div",
7374
8004
  {
7375
8005
  "data-ohw-ai-section-hover": "",
@@ -7387,7 +8017,7 @@ function AiSectionOverlay({
7387
8017
  }
7388
8018
  }
7389
8019
  ),
7390
- selectionRect && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8020
+ selectionRect && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7391
8021
  "div",
7392
8022
  {
7393
8023
  "data-ohw-ai-section-selected": "",
@@ -7405,7 +8035,7 @@ function AiSectionOverlay({
7405
8035
  }
7406
8036
  }
7407
8037
  ),
7408
- reviewRect && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8038
+ reviewRect && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
7409
8039
  "div",
7410
8040
  {
7411
8041
  "data-ohw-ai-review": "",
@@ -7427,7 +8057,7 @@ function AiSectionOverlay({
7427
8057
  cursor: "default"
7428
8058
  },
7429
8059
  onClick: (e) => e.stopPropagation(),
7430
- children: !reviewButtonsHidden && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8060
+ children: !reviewButtonsHidden && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
7431
8061
  "div",
7432
8062
  {
7433
8063
  style: {
@@ -7440,8 +8070,8 @@ function AiSectionOverlay({
7440
8070
  paddingTop: 12
7441
8071
  },
7442
8072
  children: [
7443
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ReviewButton, { label: "Accept", onClick: () => decide("accept"), background: PRIMARY2, color: "#ffffff", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react6.Check, { size: 16, strokeWidth: 2.5, "aria-hidden": true }) }),
7444
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ReviewButton, { label: "Discard", onClick: () => decide("discard"), background: "#EFF6FF", color: "#0c0a09", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react6.X, { size: 16, strokeWidth: 2, "aria-hidden": true }) })
8073
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ReviewButton, { label: "Accept", onClick: () => decide("accept"), background: PRIMARY2, color: "#ffffff", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.Check, { size: 16, strokeWidth: 2.5, "aria-hidden": true }) }),
8074
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ReviewButton, { label: "Discard", onClick: () => decide("discard"), background: "#EFF6FF", color: "#0c0a09", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.X, { size: 16, strokeWidth: 2, "aria-hidden": true }) })
7445
8075
  ]
7446
8076
  }
7447
8077
  )
@@ -7800,23 +8430,23 @@ var import_react12 = require("react");
7800
8430
  // src/ui/dialog.tsx
7801
8431
  var React8 = __toESM(require("react"), 1);
7802
8432
  var import_radix_ui6 = require("radix-ui");
7803
- var import_lucide_react7 = require("lucide-react");
7804
- var import_jsx_runtime17 = require("react/jsx-runtime");
8433
+ var import_lucide_react8 = require("lucide-react");
8434
+ var import_jsx_runtime18 = require("react/jsx-runtime");
7805
8435
  function Dialog2({
7806
8436
  ...props
7807
8437
  }) {
7808
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
8438
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
7809
8439
  }
7810
8440
  function DialogPortal({
7811
8441
  ...props
7812
8442
  }) {
7813
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
8443
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
7814
8444
  }
7815
8445
  function DialogOverlay({
7816
8446
  className,
7817
8447
  ...props
7818
8448
  }) {
7819
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8449
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7820
8450
  import_radix_ui6.Dialog.Overlay,
7821
8451
  {
7822
8452
  "data-slot": "dialog-overlay",
@@ -7829,9 +8459,9 @@ function DialogOverlay({
7829
8459
  var DialogContent = React8.forwardRef(
7830
8460
  ({ className, children, showCloseButton = true, container, ...props }, ref) => {
7831
8461
  const positionMode = container ? "absolute" : "fixed";
7832
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(DialogPortal, { container: container ?? void 0, children: [
7833
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
7834
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8462
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DialogPortal, { container: container ?? void 0, children: [
8463
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
8464
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
7835
8465
  import_radix_ui6.Dialog.Content,
7836
8466
  {
7837
8467
  ref,
@@ -7847,13 +8477,13 @@ var DialogContent = React8.forwardRef(
7847
8477
  ...props,
7848
8478
  children: [
7849
8479
  children,
7850
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8480
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7851
8481
  import_radix_ui6.Dialog.Close,
7852
8482
  {
7853
8483
  type: "button",
7854
8484
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
7855
8485
  "aria-label": "Close",
7856
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.X, { size: 16, "aria-hidden": true })
8486
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
7857
8487
  }
7858
8488
  ) : null
7859
8489
  ]
@@ -7867,13 +8497,13 @@ function DialogHeader({
7867
8497
  className,
7868
8498
  ...props
7869
8499
  }) {
7870
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
8500
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
7871
8501
  }
7872
8502
  function DialogFooter({
7873
8503
  className,
7874
8504
  ...props
7875
8505
  }) {
7876
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8506
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7877
8507
  "div",
7878
8508
  {
7879
8509
  className: cn("flex items-center justify-end gap-2", className),
@@ -7881,7 +8511,7 @@ function DialogFooter({
7881
8511
  }
7882
8512
  );
7883
8513
  }
7884
- var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8514
+ var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7885
8515
  import_radix_ui6.Dialog.Title,
7886
8516
  {
7887
8517
  ref,
@@ -7893,7 +8523,7 @@ var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
7893
8523
  }
7894
8524
  ));
7895
8525
  DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
7896
- var DialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8526
+ var DialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7897
8527
  import_radix_ui6.Dialog.Description,
7898
8528
  {
7899
8529
  ref,
@@ -7905,63 +8535,63 @@ DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
7905
8535
  var DialogClose = import_radix_ui6.Dialog.Close;
7906
8536
 
7907
8537
  // src/ui/link-modal/LinkEditorPanel.tsx
7908
- var import_lucide_react11 = require("lucide-react");
8538
+ var import_lucide_react12 = require("lucide-react");
7909
8539
 
7910
8540
  // src/ui/link-modal/DestinationBreadcrumb.tsx
7911
- var import_lucide_react8 = require("lucide-react");
7912
- var import_jsx_runtime18 = require("react/jsx-runtime");
8541
+ var import_lucide_react9 = require("lucide-react");
8542
+ var import_jsx_runtime19 = require("react/jsx-runtime");
7913
8543
  function DestinationBreadcrumb({
7914
8544
  pageTitle,
7915
8545
  sectionLabel
7916
8546
  }) {
7917
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
7918
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
7919
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-3", children: [
7920
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-2", children: [
7921
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
7922
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
8547
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
8548
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
8549
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-3", children: [
8550
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2", children: [
8551
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
8552
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
7923
8553
  ] }),
7924
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7925
- import_lucide_react8.ArrowRight,
8554
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
8555
+ import_lucide_react9.ArrowRight,
7926
8556
  {
7927
8557
  size: 16,
7928
8558
  className: "shrink-0 text-muted-foreground",
7929
8559
  "aria-hidden": true
7930
8560
  }
7931
8561
  ),
7932
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
7933
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7934
- import_lucide_react8.GalleryVertical,
8562
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
8563
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
8564
+ import_lucide_react9.GalleryVertical,
7935
8565
  {
7936
8566
  size: 16,
7937
8567
  className: "shrink-0 text-foreground",
7938
8568
  "aria-hidden": true
7939
8569
  }
7940
8570
  ),
7941
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
8571
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
7942
8572
  ] })
7943
8573
  ] })
7944
8574
  ] });
7945
8575
  }
7946
8576
 
7947
8577
  // src/ui/link-modal/SectionTreeItem.tsx
7948
- var import_lucide_react9 = require("lucide-react");
7949
- var import_jsx_runtime19 = require("react/jsx-runtime");
8578
+ var import_lucide_react10 = require("lucide-react");
8579
+ var import_jsx_runtime20 = require("react/jsx-runtime");
7950
8580
  function SectionTreeItem({
7951
8581
  section,
7952
8582
  onSelect,
7953
8583
  selected
7954
8584
  }) {
7955
8585
  const interactive = Boolean(onSelect);
7956
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
7957
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
8586
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
8587
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
7958
8588
  "div",
7959
8589
  {
7960
8590
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
7961
8591
  "aria-hidden": true
7962
8592
  }
7963
8593
  ),
7964
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
8594
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
7965
8595
  "div",
7966
8596
  {
7967
8597
  role: interactive ? "button" : void 0,
@@ -7979,15 +8609,15 @@ function SectionTreeItem({
7979
8609
  interactive && selected && "border-primary"
7980
8610
  ),
7981
8611
  children: [
7982
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
7983
- import_lucide_react9.GalleryVertical,
8612
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
8613
+ import_lucide_react10.GalleryVertical,
7984
8614
  {
7985
8615
  size: 16,
7986
8616
  className: "shrink-0 text-foreground",
7987
8617
  "aria-hidden": true
7988
8618
  }
7989
8619
  ),
7990
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
8620
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
7991
8621
  ]
7992
8622
  }
7993
8623
  )
@@ -7999,10 +8629,10 @@ var import_react9 = require("react");
7999
8629
 
8000
8630
  // src/ui/input.tsx
8001
8631
  var React9 = __toESM(require("react"), 1);
8002
- var import_jsx_runtime20 = require("react/jsx-runtime");
8632
+ var import_jsx_runtime21 = require("react/jsx-runtime");
8003
8633
  var Input = React9.forwardRef(
8004
8634
  ({ className, type, ...props }, ref) => {
8005
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
8635
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8006
8636
  "input",
8007
8637
  {
8008
8638
  type,
@@ -8021,9 +8651,9 @@ Input.displayName = "Input";
8021
8651
 
8022
8652
  // src/ui/label.tsx
8023
8653
  var import_radix_ui7 = require("radix-ui");
8024
- var import_jsx_runtime21 = require("react/jsx-runtime");
8654
+ var import_jsx_runtime22 = require("react/jsx-runtime");
8025
8655
  function Label({ className, ...props }) {
8026
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
8656
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8027
8657
  import_radix_ui7.Label.Root,
8028
8658
  {
8029
8659
  "data-slot": "label",
@@ -8034,12 +8664,12 @@ function Label({ className, ...props }) {
8034
8664
  }
8035
8665
 
8036
8666
  // src/ui/link-modal/UrlOrPageInput.tsx
8037
- var import_lucide_react10 = require("lucide-react");
8038
- var import_jsx_runtime22 = require("react/jsx-runtime");
8667
+ var import_lucide_react11 = require("lucide-react");
8668
+ var import_jsx_runtime23 = require("react/jsx-runtime");
8039
8669
  function FieldChevron({
8040
8670
  onClick
8041
8671
  }) {
8042
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8672
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8043
8673
  "button",
8044
8674
  {
8045
8675
  type: "button",
@@ -8047,7 +8677,7 @@ function FieldChevron({
8047
8677
  onClick,
8048
8678
  "aria-label": "Open page list",
8049
8679
  tabIndex: -1,
8050
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.ChevronDown, { size: 16 })
8680
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.ChevronDown, { size: 16 })
8051
8681
  }
8052
8682
  );
8053
8683
  }
@@ -8108,19 +8738,19 @@ function UrlOrPageInput({
8108
8738
  "data-ohw-link-field flex h-[36px] w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
8109
8739
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
8110
8740
  );
8111
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
8112
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
8113
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { ref: rootRef, className: "relative w-full", children: [
8114
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
8115
- selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8116
- import_lucide_react10.File,
8741
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
8742
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
8743
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { ref: rootRef, className: "relative w-full", children: [
8744
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
8745
+ selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8746
+ import_lucide_react11.File,
8117
8747
  {
8118
8748
  size: 16,
8119
8749
  className: "shrink-0 text-foreground",
8120
8750
  "aria-hidden": true
8121
8751
  }
8122
8752
  ) }) : null,
8123
- readOnly ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8753
+ readOnly ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8124
8754
  Input,
8125
8755
  {
8126
8756
  ref: inputRef,
@@ -8146,7 +8776,7 @@ function UrlOrPageInput({
8146
8776
  )
8147
8777
  }
8148
8778
  ),
8149
- selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8779
+ selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8150
8780
  "button",
8151
8781
  {
8152
8782
  type: "button",
@@ -8154,26 +8784,26 @@ function UrlOrPageInput({
8154
8784
  onMouseDown: clearSelection,
8155
8785
  "aria-label": "Clear selected page",
8156
8786
  tabIndex: -1,
8157
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.X, { size: 16, "aria-hidden": true })
8787
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.X, { size: 16, "aria-hidden": true })
8158
8788
  }
8159
8789
  ) : null,
8160
- !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
8790
+ !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
8161
8791
  ] }),
8162
- dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
8792
+ dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8163
8793
  "div",
8164
8794
  {
8165
8795
  "data-ohw-link-page-dropdown": "",
8166
8796
  className: "absolute left-0 right-0 top-[calc(100%+4px)] z-50 max-h-48 overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg",
8167
8797
  onMouseDown: (e) => e.preventDefault(),
8168
- children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
8798
+ children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8169
8799
  "button",
8170
8800
  {
8171
8801
  type: "button",
8172
8802
  className: "flex h-9 w-full items-center gap-2 border-0 bg-transparent px-3 text-left text-sm leading-5 text-foreground outline-none hover:bg-muted",
8173
8803
  onClick: () => onPageSelect(page),
8174
8804
  children: [
8175
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.File, { size: 16, className: "shrink-0", "aria-hidden": true }),
8176
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "truncate", children: page.title })
8805
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.File, { size: 16, className: "shrink-0", "aria-hidden": true }),
8806
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "truncate", children: page.title })
8177
8807
  ]
8178
8808
  },
8179
8809
  page.path
@@ -8181,34 +8811,34 @@ function UrlOrPageInput({
8181
8811
  }
8182
8812
  ) : null
8183
8813
  ] }),
8184
- urlError ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
8814
+ urlError ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
8185
8815
  ] });
8186
8816
  }
8187
8817
 
8188
8818
  // src/ui/link-modal/LinkEditorPanel.tsx
8189
- var import_jsx_runtime23 = require("react/jsx-runtime");
8819
+ var import_jsx_runtime24 = require("react/jsx-runtime");
8190
8820
  function LinkEditorPanel({ state, onClose }) {
8191
8821
  const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
8192
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
8193
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8822
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
8823
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
8194
8824
  "button",
8195
8825
  {
8196
8826
  type: "button",
8197
8827
  className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50 h-7",
8198
8828
  "aria-label": "Close",
8199
8829
  onClick: onClose,
8200
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.X, { size: 16, "aria-hidden": true })
8830
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.X, { size: 16, "aria-hidden": true })
8201
8831
  }
8202
8832
  ) }),
8203
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
8204
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
8205
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8833
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
8834
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
8835
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
8206
8836
  DestinationBreadcrumb,
8207
8837
  {
8208
8838
  pageTitle: state.selectedPage.title,
8209
8839
  sectionLabel: state.selectedSection.label
8210
8840
  }
8211
- ) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8841
+ ) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
8212
8842
  UrlOrPageInput,
8213
8843
  {
8214
8844
  value: state.searchValue,
@@ -8221,8 +8851,8 @@ function LinkEditorPanel({ state, onClose }) {
8221
8851
  urlError: state.urlError
8222
8852
  }
8223
8853
  ),
8224
- state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
8225
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8854
+ state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
8855
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
8226
8856
  Button,
8227
8857
  {
8228
8858
  type: "button",
@@ -8233,15 +8863,15 @@ function LinkEditorPanel({ state, onClose }) {
8233
8863
  children: "Choose a section"
8234
8864
  }
8235
8865
  ),
8236
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
8237
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
8238
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: "Pick a section this link should scroll to." })
8866
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
8867
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
8868
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: "Pick a section this link should scroll to." })
8239
8869
  ] })
8240
8870
  ] }) : null,
8241
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
8871
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
8242
8872
  ] }),
8243
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
8244
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8873
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
8874
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
8245
8875
  Button,
8246
8876
  {
8247
8877
  type: "button",
@@ -8256,7 +8886,7 @@ function LinkEditorPanel({ state, onClose }) {
8256
8886
  children: state.secondaryLabel
8257
8887
  }
8258
8888
  ),
8259
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8889
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
8260
8890
  Button,
8261
8891
  {
8262
8892
  type: "button",
@@ -8277,9 +8907,9 @@ function LinkEditorPanel({ state, onClose }) {
8277
8907
  // src/ui/link-modal/SectionPickerOverlay.tsx
8278
8908
  var import_react10 = require("react");
8279
8909
  var import_react_dom2 = require("react-dom");
8280
- var import_lucide_react12 = require("lucide-react");
8910
+ var import_lucide_react13 = require("lucide-react");
8281
8911
  var import_navigation2 = require("next/navigation");
8282
- var import_jsx_runtime24 = require("react/jsx-runtime");
8912
+ var import_jsx_runtime25 = require("react/jsx-runtime");
8283
8913
  var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
8284
8914
  function rectsEqual(a, b) {
8285
8915
  if (a.size !== b.size) return false;
@@ -8508,7 +9138,7 @@ function SectionPickerOverlay({
8508
9138
  const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
8509
9139
  if (!portalRoot) return null;
8510
9140
  return (0, import_react_dom2.createPortal)(
8511
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
9141
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
8512
9142
  "div",
8513
9143
  {
8514
9144
  "data-ohw-section-picker": "",
@@ -8518,12 +9148,12 @@ function SectionPickerOverlay({
8518
9148
  role: "dialog",
8519
9149
  "aria-label": "Choose a section",
8520
9150
  children: [
8521
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9151
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
8522
9152
  "div",
8523
9153
  {
8524
9154
  className: "pointer-events-auto fixed left-5 z-[2]",
8525
9155
  style: { top: chromeClip.top + 20 },
8526
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
9156
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
8527
9157
  Button,
8528
9158
  {
8529
9159
  type: "button",
@@ -8532,14 +9162,14 @@ function SectionPickerOverlay({
8532
9162
  className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
8533
9163
  onClick: onBack,
8534
9164
  children: [
8535
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
9165
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react13.ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
8536
9166
  "Back"
8537
9167
  ]
8538
9168
  }
8539
9169
  )
8540
9170
  }
8541
9171
  ),
8542
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9172
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
8543
9173
  "div",
8544
9174
  {
8545
9175
  className: "pointer-events-none fixed left-1/2 z-[2] rounded-lg px-4 py-3 text-xs leading-4 tracking-[0.18px] text-white shadow-md",
@@ -8552,7 +9182,7 @@ function SectionPickerOverlay({
8552
9182
  children: "Click on section to select"
8553
9183
  }
8554
9184
  ),
8555
- !isOnTargetPage ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9185
+ !isOnTargetPage ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
8556
9186
  "div",
8557
9187
  {
8558
9188
  className: "pointer-events-none fixed left-1/2 z-[1] -translate-x-1/2 rounded-md px-3 py-2 text-sm text-muted-foreground shadow-sm",
@@ -8560,14 +9190,14 @@ function SectionPickerOverlay({
8560
9190
  children: "Loading page preview\u2026"
8561
9191
  }
8562
9192
  ) : null,
8563
- isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
9193
+ isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
8564
9194
  isOnTargetPage ? liveSections.map((section) => {
8565
9195
  const rect = rects.get(section.id);
8566
9196
  if (!rect || rect.width <= 0 || rect.height <= 0) return null;
8567
9197
  const isSelected = selectedId === section.id;
8568
9198
  const isHovered = hoveredId === section.id;
8569
9199
  const isLit = isSelected || isHovered;
8570
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
9200
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
8571
9201
  "button",
8572
9202
  {
8573
9203
  type: "button",
@@ -8582,7 +9212,7 @@ function SectionPickerOverlay({
8582
9212
  "aria-label": `Select section ${section.label}`,
8583
9213
  onClick: () => handleSelect(section),
8584
9214
  children: [
8585
- isLit ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9215
+ isLit ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
8586
9216
  "span",
8587
9217
  {
8588
9218
  className: "pointer-events-none absolute",
@@ -8595,13 +9225,13 @@ function SectionPickerOverlay({
8595
9225
  "aria-hidden": true
8596
9226
  }
8597
9227
  ) : null,
8598
- isSelected ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9228
+ isSelected ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
8599
9229
  "span",
8600
9230
  {
8601
9231
  className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
8602
9232
  style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
8603
9233
  "aria-hidden": true,
8604
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.Check, { className: "size-5" })
9234
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react13.Check, { className: "size-5" })
8605
9235
  }
8606
9236
  ) : null
8607
9237
  ]
@@ -8781,7 +9411,7 @@ function useLinkModalState({
8781
9411
  }
8782
9412
 
8783
9413
  // src/ui/link-modal/LinkPopover.tsx
8784
- var import_jsx_runtime25 = require("react/jsx-runtime");
9414
+ var import_jsx_runtime26 = require("react/jsx-runtime");
8785
9415
  function postToParent(data) {
8786
9416
  window.parent?.postMessage(data, "*");
8787
9417
  }
@@ -8877,15 +9507,15 @@ function LinkPopover({
8877
9507
  );
8878
9508
  };
8879
9509
  }, [open, sectionPickerActive]);
8880
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
8881
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9510
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
9511
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
8882
9512
  Dialog2,
8883
9513
  {
8884
9514
  open: open && !sectionPickerActive,
8885
9515
  onOpenChange: (next) => {
8886
9516
  if (!next) onClose?.();
8887
9517
  },
8888
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9518
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
8889
9519
  DialogContent,
8890
9520
  {
8891
9521
  ref: panelRef,
@@ -8895,12 +9525,12 @@ function LinkPopover({
8895
9525
  "data-ohw-bridge": "",
8896
9526
  showCloseButton: false,
8897
9527
  className: "gap-0 p-0 w-full max-w-[448px] pointer-events-auto z-[2147483646] overflow-visible",
8898
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(LinkEditorPanel, { state, onClose })
9528
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(LinkEditorPanel, { state, onClose })
8899
9529
  }
8900
9530
  )
8901
9531
  }
8902
9532
  ),
8903
- sectionPickerActive && state.selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
9533
+ sectionPickerActive && state.selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
8904
9534
  SectionPickerOverlay,
8905
9535
  {
8906
9536
  pagePath: state.selectedPage.path,
@@ -10126,13 +10756,14 @@ function listSocialItems(row) {
10126
10756
  return isSocialItem(anchor) ? anchor : null;
10127
10757
  }).filter((item) => item !== null);
10128
10758
  }
10129
- function socialRowUnit(item) {
10130
- const row = findSocialsRow(item);
10759
+ function socialRowUnit(item, knownRow) {
10760
+ const row = knownRow ?? findSocialsRow(item);
10761
+ if (!row || !row.contains(item)) return null;
10131
10762
  let node = item;
10132
10763
  while (node.parentElement && node.parentElement !== row) {
10133
10764
  node = node.parentElement;
10134
10765
  }
10135
- return node;
10766
+ return node.parentElement === row ? node : null;
10136
10767
  }
10137
10768
  function listSocialsRows(root = document) {
10138
10769
  const rows = /* @__PURE__ */ new Set();
@@ -10151,7 +10782,8 @@ function markSocialsRows(root = document) {
10151
10782
  listSocialsRows(root).forEach((row) => {
10152
10783
  row.setAttribute(SOCIALS_ROW_ATTR, "");
10153
10784
  const items = listSocialItems(row);
10154
- if (items[0]) rowTemplates.set(rowKeyOf(row), socialRowUnit(items[0]).outerHTML);
10785
+ const firstUnit = items[0] ? socialRowUnit(items[0], row) : null;
10786
+ if (firstUnit) rowTemplates.set(rowKeyOf(row), firstUnit.outerHTML);
10155
10787
  items.forEach((item, index) => {
10156
10788
  item.setAttribute(SOCIALS_ITEM_ATTR, String(index));
10157
10789
  const iconKey = socialIconKey(item);
@@ -10295,7 +10927,8 @@ function removeSocialItem(item, content) {
10295
10927
  const previousContent = Object.fromEntries(
10296
10928
  removedKeys.filter((key) => key in content).map((key) => [key, content[key]])
10297
10929
  );
10298
- const unit = socialRowUnit(item);
10930
+ const unit = socialRowUnit(item, row);
10931
+ if (!unit) return null;
10299
10932
  const nextSibling = unit.nextElementSibling;
10300
10933
  unit.remove();
10301
10934
  markSocialsRows(row.ownerDocument);
@@ -10318,7 +10951,9 @@ function applySocialsOrder(order, root = document) {
10318
10951
  const byKey = new Map(listSocialItems(row).map((item) => [socialHrefKey(item), item]));
10319
10952
  wanted.forEach((key) => {
10320
10953
  const item = byKey.get(key);
10321
- if (item) row.appendChild(socialRowUnit(item));
10954
+ if (!item) return;
10955
+ const unit = socialRowUnit(item, row);
10956
+ if (unit) row.appendChild(unit);
10322
10957
  });
10323
10958
  });
10324
10959
  markSocialsRows(root);
@@ -10348,7 +10983,7 @@ function reconcileSocialsFromContent(content, root = document) {
10348
10983
  });
10349
10984
  if (surviving.length) {
10350
10985
  present.forEach((item) => {
10351
- if (!surviving.includes(item)) socialRowUnit(item).remove();
10986
+ if (!surviving.includes(item)) socialRowUnit(item)?.remove();
10352
10987
  });
10353
10988
  }
10354
10989
  });
@@ -11684,8 +12319,8 @@ function addFooterColumnWithPersist({
11684
12319
 
11685
12320
  // src/ui/FloatingPanel.tsx
11686
12321
  var import_react13 = require("react");
11687
- var import_lucide_react13 = require("lucide-react");
11688
- var import_jsx_runtime26 = require("react/jsx-runtime");
12322
+ var import_lucide_react14 = require("lucide-react");
12323
+ var import_jsx_runtime27 = require("react/jsx-runtime");
11689
12324
  var PANEL_WIDTH = 256;
11690
12325
  var EDGE_MARGIN = 16;
11691
12326
  function getVisibleClip(parentScroll) {
@@ -11800,7 +12435,7 @@ function FloatingPanel({
11800
12435
  }, [open]);
11801
12436
  (0, import_react13.useEffect)(() => () => document.documentElement.removeAttribute("data-ohw-panel-dragging"), []);
11802
12437
  if (!open) return null;
11803
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
12438
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
11804
12439
  "div",
11805
12440
  {
11806
12441
  ref: panelRef,
@@ -11817,7 +12452,7 @@ function FloatingPanel({
11817
12452
  onPointerDown: (e) => e.stopPropagation(),
11818
12453
  onClick: (e) => e.stopPropagation(),
11819
12454
  children: [
11820
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
12455
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
11821
12456
  "div",
11822
12457
  {
11823
12458
  "data-ohw-floating-panel-header": "",
@@ -11827,14 +12462,14 @@ function FloatingPanel({
11827
12462
  onPointerUp: endDrag,
11828
12463
  onPointerCancel: endDrag,
11829
12464
  children: [
11830
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: [
11831
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center gap-2", children: [
11832
- icon ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "shrink-0 text-foreground", children: icon }) : null,
11833
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "min-w-0 flex-1 text-lg font-semibold leading-7 text-foreground", children: title })
12465
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: [
12466
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center gap-2", children: [
12467
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "shrink-0 text-foreground", children: icon }) : null,
12468
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "min-w-0 flex-1 text-lg font-semibold leading-7 text-foreground", children: title })
11834
12469
  ] }),
11835
- context ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "w-full text-sm leading-5 text-muted-foreground", children: context }) : null
12470
+ context ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "w-full text-sm leading-5 text-muted-foreground", children: context }) : null
11836
12471
  ] }),
11837
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
12472
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11838
12473
  "button",
11839
12474
  {
11840
12475
  type: "button",
@@ -11846,13 +12481,13 @@ function FloatingPanel({
11846
12481
  onClose();
11847
12482
  },
11848
12483
  onPointerDown: (e) => e.stopPropagation(),
11849
- children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.X, { size: 16, "aria-hidden": true })
12484
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react14.X, { size: 16, "aria-hidden": true })
11850
12485
  }
11851
12486
  )
11852
12487
  ]
11853
12488
  }
11854
12489
  ),
11855
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
12490
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11856
12491
  "div",
11857
12492
  {
11858
12493
  "data-ohw-floating-panel-body": "",
@@ -11866,30 +12501,30 @@ function FloatingPanel({
11866
12501
  }
11867
12502
 
11868
12503
  // src/ui/logo-size-panel.tsx
11869
- var import_lucide_react14 = require("lucide-react");
11870
- var import_jsx_runtime27 = require("react/jsx-runtime");
12504
+ var import_lucide_react15 = require("lucide-react");
12505
+ var import_jsx_runtime28 = require("react/jsx-runtime");
11871
12506
  function SizeSlider({
11872
12507
  value,
11873
12508
  onChange
11874
12509
  }) {
11875
12510
  const pct = (value - LOGO_SIZE_MIN) / (LOGO_SIZE_MAX - LOGO_SIZE_MIN) * 100;
11876
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex w-full flex-col gap-3", children: [
11877
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex w-full items-center gap-2 text-sm font-medium leading-5", children: [
11878
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "min-w-0 flex-1 text-foreground", children: "Size" }),
11879
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "shrink-0 whitespace-nowrap text-muted-foreground", children: [
12511
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex w-full flex-col gap-3", children: [
12512
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex w-full items-center gap-2 text-sm font-medium leading-5", children: [
12513
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "min-w-0 flex-1 text-foreground", children: "Size" }),
12514
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "shrink-0 whitespace-nowrap text-muted-foreground", children: [
11880
12515
  value,
11881
12516
  " px"
11882
12517
  ] })
11883
12518
  ] }),
11884
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "relative h-2 w-full rounded-full bg-primary-50", children: [
11885
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
12519
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "relative h-2 w-full rounded-full bg-primary-50", children: [
12520
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11886
12521
  "div",
11887
12522
  {
11888
12523
  className: "absolute inset-y-0 left-0 rounded-full bg-primary",
11889
12524
  style: { width: `${pct}%` }
11890
12525
  }
11891
12526
  ),
11892
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
12527
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11893
12528
  "input",
11894
12529
  {
11895
12530
  type: "range",
@@ -11926,14 +12561,14 @@ function LogoSizePanel({
11926
12561
  const showFollowing = viewport === "mobile" && mobileFollowing;
11927
12562
  const showMobileSlider = viewport === "mobile" && !mobileFollowing;
11928
12563
  const showDesktopSlider = viewport === "desktop";
11929
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: cn("flex w-full flex-col gap-4", className), children: [
11930
- showFollowing ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
11931
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-start gap-1", children: [
11932
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react14.Link, { size: 16, className: "mt-0.5 shrink-0 text-foreground", "aria-hidden": true }),
11933
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm font-semibold leading-5 text-foreground", children: "Following desktop size" })
12564
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: cn("flex w-full flex-col gap-4", className), children: [
12565
+ showFollowing ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
12566
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-start gap-1", children: [
12567
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_lucide_react15.Link, { size: 16, className: "mt-0.5 shrink-0 text-foreground", "aria-hidden": true }),
12568
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-sm font-semibold leading-5 text-foreground", children: "Following desktop size" })
11934
12569
  ] }),
11935
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm leading-5 text-muted-foreground", children: "Mobile uses the desktop size until you customize it. Change the desktop size and it follows automatically." }),
11936
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
12570
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-sm leading-5 text-muted-foreground", children: "Mobile uses the desktop size until you customize it. Change the desktop size and it follows automatically." }),
12571
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11937
12572
  Button,
11938
12573
  {
11939
12574
  type: "button",
@@ -11945,8 +12580,8 @@ function LogoSizePanel({
11945
12580
  }
11946
12581
  )
11947
12582
  ] }) : null,
11948
- showDesktopSlider || showMobileSlider ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SizeSlider, { value: sizePx, onChange: onSizeChange }) : null,
11949
- showMobileSlider ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
12583
+ showDesktopSlider || showMobileSlider ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(SizeSlider, { value: sizePx, onChange: onSizeChange }) : null,
12584
+ showMobileSlider ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11950
12585
  Button,
11951
12586
  {
11952
12587
  type: "button",
@@ -11957,8 +12592,8 @@ function LogoSizePanel({
11957
12592
  children: "Reset to desktop size"
11958
12593
  }
11959
12594
  ) : null,
11960
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-px w-full bg-border", role: "separator" }),
11961
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
12595
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "h-px w-full bg-border", role: "separator" }),
12596
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
11962
12597
  Button,
11963
12598
  {
11964
12599
  type: "button",
@@ -11968,24 +12603,24 @@ function LogoSizePanel({
11968
12603
  onClick: onUpdateEverywhere,
11969
12604
  children: [
11970
12605
  "Update logo everywhere",
11971
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react14.ArrowUpRight, { size: 16, "aria-hidden": true })
12606
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_lucide_react15.ArrowUpRight, { size: 16, "aria-hidden": true })
11972
12607
  ]
11973
12608
  }
11974
12609
  ),
11975
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm leading-5 text-muted-foreground", children: "Matches the right version to your background." })
12610
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-sm leading-5 text-muted-foreground", children: "Matches the right version to your background." })
11976
12611
  ] });
11977
12612
  }
11978
12613
 
11979
12614
  // src/ui/socials-display-panel.tsx
11980
- var import_jsx_runtime28 = require("react/jsx-runtime");
12615
+ var import_jsx_runtime29 = require("react/jsx-runtime");
11981
12616
  function DisplaySwitch({
11982
12617
  label,
11983
12618
  checked,
11984
12619
  disabled,
11985
12620
  onChange
11986
12621
  }) {
11987
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex w-full items-center gap-2", children: [
11988
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
12622
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex w-full items-center gap-2", children: [
12623
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
11989
12624
  "span",
11990
12625
  {
11991
12626
  className: cn(
@@ -11995,7 +12630,7 @@ function DisplaySwitch({
11995
12630
  children: label
11996
12631
  }
11997
12632
  ),
11998
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
12633
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
11999
12634
  "button",
12000
12635
  {
12001
12636
  type: "button",
@@ -12009,7 +12644,7 @@ function DisplaySwitch({
12009
12644
  checked ? "bg-primary" : "bg-primary-50",
12010
12645
  disabled ? "cursor-default opacity-50" : "cursor-pointer"
12011
12646
  ),
12012
- children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
12647
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
12013
12648
  "span",
12014
12649
  {
12015
12650
  className: cn(
@@ -12023,8 +12658,8 @@ function DisplaySwitch({
12023
12658
  ] });
12024
12659
  }
12025
12660
  function SocialsDisplayPanel({ display, onChange, className }) {
12026
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: cn("flex w-full flex-col gap-3", className), children: [
12027
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
12661
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: cn("flex w-full flex-col gap-3", className), children: [
12662
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
12028
12663
  DisplaySwitch,
12029
12664
  {
12030
12665
  label: "Text",
@@ -12033,7 +12668,7 @@ function SocialsDisplayPanel({ display, onChange, className }) {
12033
12668
  onChange: (text) => onChange({ ...display, text })
12034
12669
  }
12035
12670
  ),
12036
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
12671
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
12037
12672
  DisplaySwitch,
12038
12673
  {
12039
12674
  label: "Icon",
@@ -12593,8 +13228,8 @@ function useNavItemDrag({
12593
13228
  }
12594
13229
 
12595
13230
  // src/ui/footer-container-chrome.tsx
12596
- var import_lucide_react15 = require("lucide-react");
12597
- var import_jsx_runtime29 = require("react/jsx-runtime");
13231
+ var import_lucide_react16 = require("lucide-react");
13232
+ var import_jsx_runtime30 = require("react/jsx-runtime");
12598
13233
  function FooterContainerChrome({
12599
13234
  rect,
12600
13235
  onAdd,
@@ -12602,7 +13237,7 @@ function FooterContainerChrome({
12602
13237
  }) {
12603
13238
  const chromeGap = 6;
12604
13239
  const buttonMargin = 7;
12605
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
13240
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
12606
13241
  "div",
12607
13242
  {
12608
13243
  "data-ohw-footer-container-chrome": "",
@@ -12614,8 +13249,8 @@ function FooterContainerChrome({
12614
13249
  width: rect.width + chromeGap * 2,
12615
13250
  height: rect.height + chromeGap * 2
12616
13251
  },
12617
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(Tooltip, { children: [
12618
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
13252
+ children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(Tooltip, { children: [
13253
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
12619
13254
  "button",
12620
13255
  {
12621
13256
  type: "button",
@@ -12634,10 +13269,10 @@ function FooterContainerChrome({
12634
13269
  if (addDisabled) return;
12635
13270
  onAdd();
12636
13271
  },
12637
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
13272
+ children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react16.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12638
13273
  }
12639
13274
  ) }),
12640
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
13275
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
12641
13276
  ] })
12642
13277
  }
12643
13278
  ) });
@@ -13097,14 +13732,14 @@ function deleteSelectedNavFooterItem(deps) {
13097
13732
  }
13098
13733
 
13099
13734
  // src/ui/navbar-container-chrome.tsx
13100
- var import_lucide_react16 = require("lucide-react");
13101
- var import_jsx_runtime30 = require("react/jsx-runtime");
13735
+ var import_lucide_react17 = require("lucide-react");
13736
+ var import_jsx_runtime31 = require("react/jsx-runtime");
13102
13737
  function NavbarContainerChrome({
13103
13738
  rect,
13104
13739
  onAdd
13105
13740
  }) {
13106
13741
  const chromeGap = 6;
13107
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
13742
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
13108
13743
  "div",
13109
13744
  {
13110
13745
  "data-ohw-navbar-container-chrome": "",
@@ -13116,7 +13751,7 @@ function NavbarContainerChrome({
13116
13751
  width: rect.width + chromeGap * 2,
13117
13752
  height: rect.height + chromeGap * 2
13118
13753
  },
13119
- children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
13754
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
13120
13755
  "button",
13121
13756
  {
13122
13757
  type: "button",
@@ -13133,7 +13768,7 @@ function NavbarContainerChrome({
13133
13768
  e.stopPropagation();
13134
13769
  onAdd();
13135
13770
  },
13136
- children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react16.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
13771
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react17.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
13137
13772
  }
13138
13773
  )
13139
13774
  }
@@ -13142,7 +13777,7 @@ function NavbarContainerChrome({
13142
13777
 
13143
13778
  // src/ui/drop-indicator.tsx
13144
13779
  var React10 = __toESM(require("react"), 1);
13145
- var import_jsx_runtime31 = require("react/jsx-runtime");
13780
+ var import_jsx_runtime32 = require("react/jsx-runtime");
13146
13781
  var dropIndicatorVariants = cva(
13147
13782
  "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
13148
13783
  {
@@ -13166,7 +13801,7 @@ var dropIndicatorVariants = cva(
13166
13801
  );
13167
13802
  var DropIndicator = React10.forwardRef(
13168
13803
  ({ className, direction, state, ...props }, ref) => {
13169
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
13804
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
13170
13805
  "div",
13171
13806
  {
13172
13807
  ref,
@@ -13183,7 +13818,7 @@ var DropIndicator = React10.forwardRef(
13183
13818
  DropIndicator.displayName = "DropIndicator";
13184
13819
 
13185
13820
  // src/ui/badge.tsx
13186
- var import_jsx_runtime32 = require("react/jsx-runtime");
13821
+ var import_jsx_runtime33 = require("react/jsx-runtime");
13187
13822
  var badgeVariants = cva(
13188
13823
  "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",
13189
13824
  {
@@ -13201,12 +13836,12 @@ var badgeVariants = cva(
13201
13836
  }
13202
13837
  );
13203
13838
  function Badge({ className, variant, ...props }) {
13204
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
13839
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
13205
13840
  }
13206
13841
 
13207
13842
  // src/OhhwellsBridge.tsx
13208
- var import_lucide_react17 = require("lucide-react");
13209
- var import_jsx_runtime33 = require("react/jsx-runtime");
13843
+ var import_lucide_react18 = require("lucide-react");
13844
+ var import_jsx_runtime34 = require("react/jsx-runtime");
13210
13845
  var PRIMARY3 = "#0885FE";
13211
13846
  var IMAGE_FADE_MS = 300;
13212
13847
  function runOpacityFade(el, onDone) {
@@ -13375,7 +14010,7 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
13375
14010
  const root = (0, import_client2.createRoot)(container);
13376
14011
  (0, import_react_dom3.flushSync)(() => {
13377
14012
  root.render(
13378
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
14013
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
13379
14014
  SchedulingWidget,
13380
14015
  {
13381
14016
  notifyOnConnect,
@@ -13490,7 +14125,7 @@ function isIconEditable(el) {
13490
14125
  return el.dataset.ohwEditable === "icon";
13491
14126
  }
13492
14127
  var MEDIA_SELECTOR = '[data-ohw-editable="image"], [data-ohw-editable="bg-image"], [data-ohw-editable="video"]';
13493
- var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]):not([data-ohw-editable="icon"])';
14128
+ var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]):not([data-ohw-editable="icon"]):not([data-ohw-editable="form"])';
13494
14129
  function getVideoEl2(el) {
13495
14130
  return el instanceof HTMLVideoElement ? el : el.querySelector("video");
13496
14131
  }
@@ -14051,7 +14686,7 @@ function EditGlowChrome({
14051
14686
  hideHandle = false
14052
14687
  }) {
14053
14688
  const GAP = SELECTION_CHROME_GAP2;
14054
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
14689
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
14055
14690
  "div",
14056
14691
  {
14057
14692
  ref: elRef,
@@ -14066,7 +14701,7 @@ function EditGlowChrome({
14066
14701
  zIndex: 2147483646
14067
14702
  },
14068
14703
  children: [
14069
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
14704
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
14070
14705
  "div",
14071
14706
  {
14072
14707
  style: {
@@ -14079,7 +14714,7 @@ function EditGlowChrome({
14079
14714
  }
14080
14715
  }
14081
14716
  ),
14082
- reorderHrefKey && !hideHandle && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
14717
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
14083
14718
  "div",
14084
14719
  {
14085
14720
  "data-ohw-drag-handle-container": "",
@@ -14091,7 +14726,7 @@ function EditGlowChrome({
14091
14726
  transform: "translate(calc(-100% - 7px), -50%)",
14092
14727
  pointerEvents: dragDisabled ? "none" : "auto"
14093
14728
  },
14094
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
14729
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
14095
14730
  DragHandle,
14096
14731
  {
14097
14732
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -14301,7 +14936,7 @@ function FloatingToolbar({
14301
14936
  return () => ro.disconnect();
14302
14937
  }, [showEditLink, activeCommands]);
14303
14938
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
14304
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
14939
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
14305
14940
  "div",
14306
14941
  {
14307
14942
  ref: setRefs,
@@ -14313,12 +14948,12 @@ function FloatingToolbar({
14313
14948
  zIndex: 2147483647,
14314
14949
  pointerEvents: "auto"
14315
14950
  },
14316
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(CustomToolbar, { children: [
14317
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_react16.default.Fragment, { children: [
14318
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CustomToolbarDivider, {}),
14951
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(CustomToolbar, { children: [
14952
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react16.default.Fragment, { children: [
14953
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(CustomToolbarDivider, {}),
14319
14954
  btns.map((btn) => {
14320
14955
  const isActive = activeCommands.has(btn.cmd);
14321
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
14956
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
14322
14957
  CustomToolbarButton,
14323
14958
  {
14324
14959
  title: btn.title,
@@ -14327,7 +14962,7 @@ function FloatingToolbar({
14327
14962
  e.preventDefault();
14328
14963
  onCommand(btn.cmd);
14329
14964
  },
14330
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
14965
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
14331
14966
  "svg",
14332
14967
  {
14333
14968
  width: "16",
@@ -14348,7 +14983,7 @@ function FloatingToolbar({
14348
14983
  );
14349
14984
  })
14350
14985
  ] }, gi)),
14351
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
14986
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
14352
14987
  CustomToolbarButton,
14353
14988
  {
14354
14989
  type: "button",
@@ -14362,7 +14997,7 @@ function FloatingToolbar({
14362
14997
  e.preventDefault();
14363
14998
  e.stopPropagation();
14364
14999
  },
14365
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.Link, { className: "size-4 shrink-0", "aria-hidden": true })
15000
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react18.Link, { className: "size-4 shrink-0", "aria-hidden": true })
14366
15001
  }
14367
15002
  ) : null
14368
15003
  ] })
@@ -14379,7 +15014,7 @@ function StateToggle({
14379
15014
  states,
14380
15015
  onStateChange
14381
15016
  }) {
14382
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
15017
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
14383
15018
  ToggleGroup,
14384
15019
  {
14385
15020
  "data-ohw-state-toggle": "",
@@ -14393,7 +15028,7 @@ function StateToggle({
14393
15028
  left: rect.right - 8,
14394
15029
  transform: "translateX(-100%)"
14395
15030
  },
14396
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
15031
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
14397
15032
  }
14398
15033
  );
14399
15034
  }
@@ -14516,6 +15151,157 @@ function OhhwellsBridge() {
14516
15151
  const sectionsLoadedRef = (0, import_react16.useRef)(false);
14517
15152
  const pendingScheduleConfigRequests = (0, import_react16.useRef)([]);
14518
15153
  const [toolbarRect, setToolbarRect] = (0, import_react16.useState)(null);
15154
+ const [formPickRect, setFormPickRect] = (0, import_react16.useState)(null);
15155
+ const formPickElRef = (0, import_react16.useRef)(null);
15156
+ const [formViewState, setFormViewStateUi] = (0, import_react16.useState)("default");
15157
+ const [formPickCount, setFormPickCount] = (0, import_react16.useState)(null);
15158
+ const [formHoverRect, setFormHoverRect] = (0, import_react16.useState)(null);
15159
+ const formHoverElRef = (0, import_react16.useRef)(null);
15160
+ const [fieldPickRect, setFieldPickRect] = (0, import_react16.useState)(null);
15161
+ const fieldPickElRef = (0, import_react16.useRef)(null);
15162
+ const [fieldPickState, setFieldPickState] = (0, import_react16.useState)(null);
15163
+ const [fieldTypePickerOpen, setFieldTypePickerOpen] = (0, import_react16.useState)(false);
15164
+ const clearFormPick = (0, import_react16.useCallback)(() => {
15165
+ const form = formPickElRef.current;
15166
+ if (form) {
15167
+ const key = formKeyOf(form);
15168
+ if (key) setFormViewState(form, key, "default", DEFAULT_SUCCESS_TEXT);
15169
+ }
15170
+ setFormViewStateUi("default");
15171
+ setFormPickCount(null);
15172
+ setFieldTypePickerOpen(false);
15173
+ fieldPickElRef.current = null;
15174
+ setFieldPickRect(null);
15175
+ setFieldPickState(null);
15176
+ formPickElRef.current = null;
15177
+ setFormPickRect(null);
15178
+ }, []);
15179
+ const clearFieldPick = (0, import_react16.useCallback)(() => {
15180
+ const wrapper = fieldPickElRef.current;
15181
+ if (commitPlaceholderEdit(wrapper) && wrapper) {
15182
+ const form = wrapper.closest('[data-ohw-editable="form"]');
15183
+ if (form) persistFieldsRef.current(form);
15184
+ }
15185
+ fieldPickElRef.current = null;
15186
+ setFieldPickRect(null);
15187
+ setFieldPickState(null);
15188
+ }, []);
15189
+ const persistFieldsRef = (0, import_react16.useRef)(() => {
15190
+ });
15191
+ const persistFields = (0, import_react16.useCallback)(
15192
+ (form) => {
15193
+ const key = formKeyOf(form);
15194
+ if (!key) return;
15195
+ const json = JSON.stringify(readFieldsFromDom(form));
15196
+ editContentRef.current = { ...editContentRef.current, [fieldsKey(key)]: json };
15197
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: fieldsKey(key), text: json }] });
15198
+ },
15199
+ []
15200
+ );
15201
+ persistFieldsRef.current = persistFields;
15202
+ const selectField = (0, import_react16.useCallback)((wrapper) => {
15203
+ if (fieldPickElRef.current && fieldPickElRef.current !== wrapper) {
15204
+ commitPlaceholderEdit(fieldPickElRef.current);
15205
+ }
15206
+ syncRequiredMark(wrapper);
15207
+ beginPlaceholderEdit(wrapper);
15208
+ formHoverElRef.current = null;
15209
+ setFormHoverRect(null);
15210
+ fieldPickElRef.current = wrapper;
15211
+ setFieldPickRect(wrapper.getBoundingClientRect());
15212
+ setFieldPickState({ type: fieldTypeOf(wrapper), required: isFieldRequired(wrapper) });
15213
+ setFieldTypePickerOpen(false);
15214
+ }, []);
15215
+ const withSelectedField = (0, import_react16.useCallback)(
15216
+ (run) => {
15217
+ const wrapper = fieldPickElRef.current;
15218
+ const form = formPickElRef.current;
15219
+ if (!wrapper || !form) return;
15220
+ commitPlaceholderEdit(wrapper);
15221
+ run(form, wrapper);
15222
+ beginPlaceholderEdit(wrapper);
15223
+ persistFields(form);
15224
+ setFormPickRect(form.getBoundingClientRect());
15225
+ },
15226
+ [persistFields]
15227
+ );
15228
+ const handleFieldTypeChange = (0, import_react16.useCallback)(
15229
+ (type) => withSelectedField((_form, wrapper) => {
15230
+ applyFieldType(wrapper, type);
15231
+ selectField(wrapper);
15232
+ }),
15233
+ [selectField, withSelectedField]
15234
+ );
15235
+ const handleFieldRequiredToggle = (0, import_react16.useCallback)(
15236
+ () => withSelectedField((_form, wrapper) => {
15237
+ setFieldRequired(wrapper, !isFieldRequired(wrapper));
15238
+ selectField(wrapper);
15239
+ }),
15240
+ [selectField, withSelectedField]
15241
+ );
15242
+ const handleFieldDuplicate = (0, import_react16.useCallback)(
15243
+ () => withSelectedField((form, wrapper) => {
15244
+ const copy = duplicateField(form, wrapper);
15245
+ selectField(copy);
15246
+ }),
15247
+ [selectField, withSelectedField]
15248
+ );
15249
+ const handleFieldDelete = (0, import_react16.useCallback)(
15250
+ () => withSelectedField((_form, wrapper) => {
15251
+ removeField(wrapper);
15252
+ clearFieldPick();
15253
+ postToParentRef.current({ type: "ow:toast", title: "Form field deleted" });
15254
+ }),
15255
+ [clearFieldPick, withSelectedField]
15256
+ );
15257
+ const handleAddField = (0, import_react16.useCallback)(
15258
+ (type) => {
15259
+ const form = formPickElRef.current;
15260
+ if (!form) return;
15261
+ const wrapper = insertField(form, type);
15262
+ setFieldTypePickerOpen(false);
15263
+ if (!wrapper) return;
15264
+ persistFields(form);
15265
+ setFormPickRect(null);
15266
+ requestAnimationFrame(() => {
15267
+ selectField(wrapper);
15268
+ wrapper.scrollIntoView({ block: "nearest", behavior: "smooth" });
15269
+ });
15270
+ },
15271
+ [persistFields, selectField]
15272
+ );
15273
+ const fieldDragRef = (0, import_react16.useRef)(null);
15274
+ const handleFieldDragStart = (0, import_react16.useCallback)(() => {
15275
+ const wrapper = fieldPickElRef.current;
15276
+ const form = formPickElRef.current;
15277
+ if (!wrapper || !form) return;
15278
+ fieldDragRef.current = { key: fieldKeyOf(wrapper), form };
15279
+ }, []);
15280
+ const handleFieldDragEnd = (0, import_react16.useCallback)(() => {
15281
+ fieldDragRef.current = null;
15282
+ setFieldDropIndex(null);
15283
+ }, []);
15284
+ const [fieldDropIndex, setFieldDropIndex] = (0, import_react16.useState)(null);
15285
+ const clearFormPickRef = (0, import_react16.useRef)(clearFormPick);
15286
+ clearFormPickRef.current = clearFormPick;
15287
+ (0, import_react16.useEffect)(() => {
15288
+ const el = fieldPickElRef.current;
15289
+ if (!el || fieldPickRect === null) return;
15290
+ const observer = new ResizeObserver(() => {
15291
+ if (fieldPickElRef.current === el) setFieldPickRect(el.getBoundingClientRect());
15292
+ });
15293
+ observer.observe(el);
15294
+ return () => observer.disconnect();
15295
+ }, [fieldPickRect !== null, fieldPickState]);
15296
+ (0, import_react16.useEffect)(() => {
15297
+ const el = formPickElRef.current;
15298
+ if (!el || formPickRect === null) return;
15299
+ const observer = new ResizeObserver(() => {
15300
+ if (formPickElRef.current === el) setFormPickRect(el.getBoundingClientRect());
15301
+ });
15302
+ observer.observe(el);
15303
+ return () => observer.disconnect();
15304
+ }, [formPickRect !== null, formViewState]);
14519
15305
  const [toolbarVariant, setToolbarVariant] = (0, import_react16.useState)("none");
14520
15306
  const toolbarVariantRef = (0, import_react16.useRef)("none");
14521
15307
  toolbarVariantRef.current = toolbarVariant;
@@ -14537,7 +15323,7 @@ function OhhwellsBridge() {
14537
15323
  (0, import_react16.useEffect)(() => {
14538
15324
  const sync = () => {
14539
15325
  const el = document.querySelector(
14540
- "[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key])"
15326
+ '[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]):not([data-ohw-editable="form"] *)'
14541
15327
  );
14542
15328
  const target = el && !el.closest("[data-ohw-href-key]") ? el : null;
14543
15329
  if (!target) {
@@ -15669,6 +16455,7 @@ function OhhwellsBridge() {
15669
16455
  setFloatingPanel(null);
15670
16456
  setLogoSizeDraft(null);
15671
16457
  }, []);
16458
+ closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
15672
16459
  const closeFloatingPanelAndDeselect = (0, import_react16.useCallback)(() => {
15673
16460
  setFloatingPanel(null);
15674
16461
  setLogoSizeDraft(null);
@@ -15832,14 +16619,14 @@ function OhhwellsBridge() {
15832
16619
  }
15833
16620
  const applyContent = (content) => {
15834
16621
  const imageLoads = [];
15835
- if (typeof content[AI_SECTIONS_KEY] === "string") {
15836
- aiSectionsRef.current = content[AI_SECTIONS_KEY];
15837
- applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
15838
- }
15839
16622
  if (typeof content[BRAND_KIT_KEY] === "string") {
15840
16623
  brandKitRef.current = content[BRAND_KIT_KEY];
15841
16624
  applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
15842
16625
  }
16626
+ if (typeof content[AI_SECTIONS_KEY] === "string") {
16627
+ aiSectionsRef.current = content[AI_SECTIONS_KEY];
16628
+ applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
16629
+ }
15843
16630
  if (typeof content[STYLE_STORE_KEY] === "string") {
15844
16631
  stylesRef.current = content[STYLE_STORE_KEY];
15845
16632
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
@@ -15924,6 +16711,98 @@ function OhhwellsBridge() {
15924
16711
  cancelled = true;
15925
16712
  };
15926
16713
  }, [subdomain, isEditMode]);
16714
+ (0, import_react16.useEffect)(() => {
16715
+ if (!isEditMode) return;
16716
+ const resolveIndex = (form, clientY) => {
16717
+ const wrappers = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== fieldDragRef.current?.key);
16718
+ for (let i = 0; i < wrappers.length; i += 1) {
16719
+ const rect = wrappers[i].getBoundingClientRect();
16720
+ if (clientY < rect.top + rect.height / 2) return i;
16721
+ }
16722
+ return wrappers.length;
16723
+ };
16724
+ const onDragOver = (e) => {
16725
+ const session = fieldDragRef.current;
16726
+ if (!session) return;
16727
+ e.preventDefault();
16728
+ e.stopPropagation();
16729
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
16730
+ setFieldDropIndex(resolveIndex(session.form, e.clientY));
16731
+ };
16732
+ const onDrop = (e) => {
16733
+ const session = fieldDragRef.current;
16734
+ if (!session) return;
16735
+ e.preventDefault();
16736
+ e.stopPropagation();
16737
+ moveField(session.form, session.key, resolveIndex(session.form, e.clientY));
16738
+ persistFields(session.form);
16739
+ const moved = listFieldWrappers(session.form).find((el) => fieldKeyOf(el) === session.key);
16740
+ if (moved) selectField(moved);
16741
+ setFormPickRect(session.form.getBoundingClientRect());
16742
+ fieldDragRef.current = null;
16743
+ setFieldDropIndex(null);
16744
+ };
16745
+ window.addEventListener("dragover", onDragOver, true);
16746
+ window.addEventListener("drop", onDrop, true);
16747
+ return () => {
16748
+ window.removeEventListener("dragover", onDragOver, true);
16749
+ window.removeEventListener("drop", onDrop, true);
16750
+ };
16751
+ }, [isEditMode, persistFields, selectField]);
16752
+ (0, import_react16.useEffect)(() => {
16753
+ if (!isEditMode) return;
16754
+ const mark = () => document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
16755
+ markFormFields(form);
16756
+ });
16757
+ mark();
16758
+ const observer = new MutationObserver(() => mark());
16759
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
16760
+ observer.observe(form, { childList: true, subtree: true, characterData: true });
16761
+ });
16762
+ return () => observer.disconnect();
16763
+ }, [isEditMode, fetchState, pathname]);
16764
+ (0, import_react16.useEffect)(() => {
16765
+ if (!isEditMode) return;
16766
+ let saveTimer = null;
16767
+ const onInput = (e) => {
16768
+ const input = e.target;
16769
+ if (!input || !("value" in input)) return;
16770
+ const wrapper = getFieldWrapper(input);
16771
+ if (!wrapper || wrapper !== fieldPickElRef.current) return;
16772
+ if (saveTimer) clearTimeout(saveTimer);
16773
+ saveTimer = setTimeout(() => {
16774
+ const form = formPickElRef.current;
16775
+ if (!form) return;
16776
+ const previous = input.getAttribute("placeholder");
16777
+ input.setAttribute("placeholder", input.value);
16778
+ persistFields(form);
16779
+ input.setAttribute("placeholder", previous ?? "");
16780
+ }, 400);
16781
+ };
16782
+ document.addEventListener("input", onInput, true);
16783
+ return () => document.removeEventListener("input", onInput, true);
16784
+ }, [isEditMode, persistFields]);
16785
+ (0, import_react16.useEffect)(() => {
16786
+ if (isEditMode || fetchState !== "done") return;
16787
+ const content = contentCache.get(subdomain) ?? {};
16788
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
16789
+ reconcileFieldsFromContent(form, content);
16790
+ });
16791
+ }, [isEditMode, fetchState, subdomain]);
16792
+ (0, import_react16.useEffect)(() => {
16793
+ if (!isEditMode) return;
16794
+ const swallow = (e) => {
16795
+ const target = e.target;
16796
+ if (target && getFormElement(target)) e.preventDefault();
16797
+ };
16798
+ document.addEventListener("submit", swallow, true);
16799
+ return () => document.removeEventListener("submit", swallow, true);
16800
+ }, [isEditMode]);
16801
+ (0, import_react16.useEffect)(() => {
16802
+ if (isEditMode || fetchState !== "done") return;
16803
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
16804
+ bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
16805
+ }, [isEditMode, fetchState, subdomain]);
15927
16806
  (0, import_react16.useEffect)(() => {
15928
16807
  if (!subdomain || isEditMode) return;
15929
16808
  let debounceTimer = null;
@@ -16157,7 +17036,9 @@ function OhhwellsBridge() {
16157
17036
  [style*="100vh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
16158
17037
  [style*="100svh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
16159
17038
  [style*="100dvh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
16160
- [data-ohw-editable] {
17039
+ /* Not the form: it is a layout container (flex/grid with gaps), and forcing block
17040
+ crushed its fields together (OHH-490). */
17041
+ [data-ohw-editable]:not([data-ohw-editable="form"]) {
16161
17042
  display: block;
16162
17043
  }
16163
17044
  /* Body text (no item-action toolbar) \u2014 first click enters text edit \u2192 I-beam.
@@ -16183,6 +17064,35 @@ function OhhwellsBridge() {
16183
17064
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
16184
17065
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
16185
17066
  [data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
17067
+ /* A form field is a design surface in the editor: its input takes the pointer so the
17068
+ field can be picked and hovered from anywhere inside it (OHH-642). */
17069
+ [data-ohw-editable="form"] [data-ohw-form-field] input,
17070
+ [data-ohw-editable="form"] [data-ohw-form-field] textarea,
17071
+ [data-ohw-editable="form"] [data-ohw-form-field] label {
17072
+ pointer-events: auto !important;
17073
+ cursor: pointer !important;
17074
+ }
17075
+ /* While a field is selected its placeholder is being written in the value, so the
17076
+ text reads as a placeholder rather than as an answer (OHH-642). */
17077
+ /* A field is a design surface here: dragging its corner resized it past the form and
17078
+ left the chrome behind. Height stays adjustable, width does not (OHH-642). */
17079
+ [data-ohw-editable="form"] [data-ohw-form-field] textarea {
17080
+ resize: vertical !important;
17081
+ max-width: 100% !important;
17082
+ }
17083
+ [data-ohw-editable="form"] [data-ohw-form-field] input,
17084
+ [data-ohw-editable="form"] [data-ohw-form-field] textarea {
17085
+ box-sizing: border-box !important;
17086
+ width: 100% !important;
17087
+ }
17088
+ /* Somewhere to click the block itself, on every side (OHH-642) \u2014 editor only. */
17089
+ [data-ohw-editable="form"] {
17090
+ padding: 18px !important;
17091
+ }
17092
+ [data-ohw-placeholder-edit] {
17093
+ color: color-mix(in srgb, currentColor 55%, transparent) !important;
17094
+ cursor: text !important;
17095
+ }
16186
17096
  /* Text hover chrome is drawn by the overlay (see hoveredTextRect) \u2014 the CSS outline
16187
17097
  that used to draw it dashes denser than the overlay border, so identical specs
16188
17098
  still read as two different frames (OHH-695). The attribute stays: hover paths
@@ -16269,6 +17179,49 @@ function OhhwellsBridge() {
16269
17179
  if (target.closest("[data-ohw-max-badge]")) return;
16270
17180
  if (isInsideLinkEditor(target)) return;
16271
17181
  if (isInsideFloatingPanel(target)) return;
17182
+ if (target.closest("[data-ohw-form-toolbar]")) return;
17183
+ if (target.closest(
17184
+ '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
17185
+ )) {
17186
+ return;
17187
+ }
17188
+ {
17189
+ const formEl = getFormElement(target);
17190
+ const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
17191
+ if (formEl && formKeyOf(formEl) && !onSuccessText) {
17192
+ const fieldEl = getFieldWrapper(target);
17193
+ if (fieldEl) {
17194
+ const label = target.closest("label");
17195
+ if (label && fieldPickElRef.current === fieldEl) return;
17196
+ e.preventDefault();
17197
+ e.stopPropagation();
17198
+ deactivateRef.current();
17199
+ deselectRef.current();
17200
+ formPickElRef.current = formEl;
17201
+ setFormPickRect(null);
17202
+ selectField(fieldEl);
17203
+ if (!label) fieldEl.querySelector("input, textarea")?.focus();
17204
+ return;
17205
+ }
17206
+ clearFieldPick();
17207
+ e.preventDefault();
17208
+ e.stopPropagation();
17209
+ deactivateRef.current();
17210
+ deselectRef.current();
17211
+ markFormFields(formEl);
17212
+ formHoverElRef.current = null;
17213
+ setFormHoverRect(null);
17214
+ formPickElRef.current = formEl;
17215
+ setFormPickRect(formEl.getBoundingClientRect());
17216
+ postToParentRef.current({
17217
+ type: "ow:form-selected",
17218
+ formKey: formKeyOf(formEl),
17219
+ hasLongText: formHasLongText(formEl)
17220
+ });
17221
+ return;
17222
+ }
17223
+ if (!formEl && formPickElRef.current) clearFormPick();
17224
+ }
16272
17225
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
16273
17226
  const beneath = document.elementsFromPoint(e.clientX, e.clientY).find(
16274
17227
  (el) => el instanceof HTMLElement && !el.closest("[data-ohw-bridge-root]") && el.closest("[data-ohw-section]") != null
@@ -16608,6 +17561,26 @@ function OhhwellsBridge() {
16608
17561
  return;
16609
17562
  }
16610
17563
  const editable = target.closest("[data-ohw-editable]");
17564
+ const hoverForm = getFormElement(target);
17565
+ if (hoverForm) {
17566
+ const hoverField = getFieldWrapper(target);
17567
+ const hoverTarget = hoverField ?? hoverForm;
17568
+ const selectedHere = hoverTarget === fieldPickElRef.current || hoverTarget === formPickElRef.current;
17569
+ hoveredItemElRef.current = null;
17570
+ setHoveredItemRect(null);
17571
+ if (selectedHere) {
17572
+ formHoverElRef.current = null;
17573
+ setFormHoverRect(null);
17574
+ } else {
17575
+ formHoverElRef.current = hoverTarget;
17576
+ setFormHoverRect(hoverTarget.getBoundingClientRect());
17577
+ }
17578
+ return;
17579
+ }
17580
+ if (formHoverElRef.current) {
17581
+ formHoverElRef.current = null;
17582
+ setFormHoverRect(null);
17583
+ }
16611
17584
  if (!editable) return;
16612
17585
  const selected = selectedElRef.current;
16613
17586
  if (selected && (selected === editable || selected.contains(editable))) return;
@@ -17244,6 +18217,13 @@ function OhhwellsBridge() {
17244
18217
  setSectionGap(null);
17245
18218
  }
17246
18219
  };
18220
+ const pointOwnedByFloatingPanel = (clientX, clientY) => {
18221
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging")) return true;
18222
+ const panel = document.querySelector("[data-ohw-floating-panel]");
18223
+ if (!panel) return false;
18224
+ const rect = panel.getBoundingClientRect();
18225
+ return clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom;
18226
+ };
17247
18227
  const handleMouseMove = (e) => {
17248
18228
  const { clientX, clientY } = e;
17249
18229
  if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
@@ -17517,14 +18497,14 @@ function OhhwellsBridge() {
17517
18497
  if (e.data?.type !== "ow:hydrate") return;
17518
18498
  const content = e.data.content;
17519
18499
  if (!content) return;
17520
- if (typeof content[AI_SECTIONS_KEY] === "string") {
17521
- aiSectionsRef.current = content[AI_SECTIONS_KEY];
17522
- applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
17523
- }
17524
18500
  if (typeof content[BRAND_KIT_KEY] === "string") {
17525
18501
  brandKitRef.current = content[BRAND_KIT_KEY];
17526
18502
  applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17527
18503
  }
18504
+ if (typeof content[AI_SECTIONS_KEY] === "string") {
18505
+ aiSectionsRef.current = content[AI_SECTIONS_KEY];
18506
+ applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
18507
+ }
17528
18508
  if (typeof content[STYLE_STORE_KEY] === "string") {
17529
18509
  stylesRef.current = content[STYLE_STORE_KEY];
17530
18510
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
@@ -17809,6 +18789,13 @@ function OhhwellsBridge() {
17809
18789
  }
17810
18790
  }
17811
18791
  };
18792
+ const handleFormCount = (e) => {
18793
+ if (e.data?.type !== "ow:form-count") return;
18794
+ const form = formPickElRef.current;
18795
+ if (!form || formKeyOf(form) !== e.data.formKey) return;
18796
+ setFormPickCount(typeof e.data.count === "number" ? e.data.count : null);
18797
+ };
18798
+ window.addEventListener("message", handleFormCount);
17812
18799
  window.addEventListener("message", handleUiEscape);
17813
18800
  const handleKeyDown = (e) => {
17814
18801
  if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
@@ -17828,6 +18815,11 @@ function OhhwellsBridge() {
17828
18815
  closeFloatingPanelOnlyRef.current();
17829
18816
  return;
17830
18817
  }
18818
+ if (e.key === "Escape" && formPickElRef.current) {
18819
+ e.preventDefault();
18820
+ clearFormPickRef.current();
18821
+ return;
18822
+ }
17831
18823
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
17832
18824
  if (toolbarVariantRef.current === "logo") {
17833
18825
  deselectRef.current();
@@ -18380,6 +19372,7 @@ function OhhwellsBridge() {
18380
19372
  window.removeEventListener("message", handleGetBrand);
18381
19373
  window.removeEventListener("message", handleDeactivate);
18382
19374
  window.removeEventListener("message", handleToastAction);
19375
+ window.removeEventListener("message", handleFormCount);
18383
19376
  window.removeEventListener("message", handleUiEscape);
18384
19377
  autoSaveTimers.current.forEach(clearTimeout);
18385
19378
  autoSaveTimers.current.clear();
@@ -18403,7 +19396,9 @@ function OhhwellsBridge() {
18403
19396
  if (footerDragRef.current) return;
18404
19397
  const target = e.target;
18405
19398
  if (!target) return;
18406
- if (target.closest('[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root], [data-ohw-floating-panel]')) {
19399
+ if (target.closest(
19400
+ '[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root], [data-ohw-floating-panel], [data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"]'
19401
+ )) {
18407
19402
  return;
18408
19403
  }
18409
19404
  if (target.closest("[data-ohw-item-drag-surface]")) return;
@@ -18581,7 +19576,7 @@ function OhhwellsBridge() {
18581
19576
  postToParent2({
18582
19577
  type: "ow:ready",
18583
19578
  version: "1",
18584
- bridgeVersion: "0.1.64",
19579
+ bridgeVersion: "0.1.65",
18585
19580
  path: pathname,
18586
19581
  nodes: collectEditableNodes(editContentRef.current),
18587
19582
  sections
@@ -18976,10 +19971,10 @@ function OhhwellsBridge() {
18976
19971
  [postToParent2]
18977
19972
  );
18978
19973
  return bridgeRoot ? (0, import_react_dom4.createPortal)(
18979
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
18980
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
18981
- isEditMode && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
18982
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19974
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
19975
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
19976
+ isEditMode && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
19977
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
18983
19978
  MediaOverlay,
18984
19979
  {
18985
19980
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -18990,7 +19985,7 @@ function OhhwellsBridge() {
18990
19985
  },
18991
19986
  `uploading-${key}`
18992
19987
  )),
18993
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19988
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
18994
19989
  MediaOverlay,
18995
19990
  {
18996
19991
  hover: mediaHover,
@@ -18999,11 +19994,11 @@ function OhhwellsBridge() {
18999
19994
  onVideoSettingsChange: handleVideoSettingsChange
19000
19995
  }
19001
19996
  ),
19002
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
19003
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
19004
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
19005
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
19006
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19997
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
19998
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
19999
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
20000
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
20001
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19007
20002
  "div",
19008
20003
  {
19009
20004
  className: "pointer-events-none fixed z-2147483646",
@@ -19013,7 +20008,7 @@ function OhhwellsBridge() {
19013
20008
  width: slot.width,
19014
20009
  height: slot.height
19015
20010
  },
19016
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20011
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19017
20012
  DropIndicator,
19018
20013
  {
19019
20014
  direction: slot.direction,
@@ -19024,7 +20019,7 @@ function OhhwellsBridge() {
19024
20019
  },
19025
20020
  `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
19026
20021
  )),
19027
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20022
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19028
20023
  "div",
19029
20024
  {
19030
20025
  className: "pointer-events-none fixed z-2147483646",
@@ -19034,7 +20029,7 @@ function OhhwellsBridge() {
19034
20029
  width: slot.width,
19035
20030
  height: slot.height
19036
20031
  },
19037
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20032
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19038
20033
  DropIndicator,
19039
20034
  {
19040
20035
  direction: slot.direction,
@@ -19045,11 +20040,179 @@ function OhhwellsBridge() {
19045
20040
  },
19046
20041
  `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
19047
20042
  )),
19048
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
19049
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
19050
- hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
19051
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
19052
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20043
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
20044
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
20045
+ hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
20046
+ formPickRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20047
+ ItemInteractionLayer,
20048
+ {
20049
+ rect: formPickRect,
20050
+ state: "active-top",
20051
+ itemDragSurface: false,
20052
+ toolbarAlign: "left",
20053
+ chromeGap: 24,
20054
+ toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
20055
+ "div",
20056
+ {
20057
+ "data-ohw-form-toolbar": "",
20058
+ className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
20059
+ children: [
20060
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20061
+ "button",
20062
+ {
20063
+ type: "button",
20064
+ "aria-label": "Add field",
20065
+ className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
20066
+ onClick: () => setFieldTypePickerOpen((open) => !open),
20067
+ "data-ohw-add-field": "",
20068
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react18.Plus, { size: 15, "aria-hidden": true })
20069
+ }
20070
+ ),
20071
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20072
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
20073
+ "button",
20074
+ {
20075
+ type: "button",
20076
+ className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-semibold text-foreground transition-colors hover:bg-muted/80",
20077
+ onClick: () => {
20078
+ const form = formPickElRef.current;
20079
+ if (!form) return;
20080
+ postToParent2({
20081
+ type: "ow:form-pick",
20082
+ formKey: formKeyOf(form),
20083
+ hasLongText: formHasLongText(form)
20084
+ });
20085
+ },
20086
+ children: [
20087
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react18.Settings, { size: 14, "aria-hidden": true }),
20088
+ "Form settings",
20089
+ formPickCount ? (
20090
+ // Counter pill, per the design — not a text suffix.
20091
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20092
+ "span",
20093
+ {
20094
+ "data-ohw-form-count": "",
20095
+ className: "ml-0.5 inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-full bg-primary px-1.5 text-[11px] font-semibold text-primary-foreground",
20096
+ children: formPickCount
20097
+ }
20098
+ )
20099
+ ) : null
20100
+ ]
20101
+ }
20102
+ ),
20103
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20104
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
20105
+ "button",
20106
+ {
20107
+ type: "button",
20108
+ className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-semibold text-foreground transition-colors hover:bg-muted/80",
20109
+ onClick: () => {
20110
+ const form = formPickElRef.current;
20111
+ if (!form) return;
20112
+ postToParent2({
20113
+ type: "ow:form-submissions",
20114
+ formKey: formKeyOf(form),
20115
+ hasLongText: formHasLongText(form)
20116
+ });
20117
+ },
20118
+ "data-ohw-view-submissions": "",
20119
+ children: [
20120
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react18.Table, { size: 14, "aria-hidden": true }),
20121
+ "View submissions"
20122
+ ]
20123
+ }
20124
+ ),
20125
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20126
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20127
+ "button",
20128
+ {
20129
+ type: "button",
20130
+ "aria-pressed": formViewState === state,
20131
+ className: "rounded-md px-2.5 py-1 text-[13px] font-semibold capitalize transition-colors " + (formViewState === state ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"),
20132
+ onClick: () => {
20133
+ const form = formPickElRef.current;
20134
+ const key = form ? formKeyOf(form) : null;
20135
+ if (!form || !key) return;
20136
+ const initial = editContentRef.current[formSuccessKey(key)] ?? DEFAULT_SUCCESS_TEXT;
20137
+ setFormViewState(form, key, state, initial);
20138
+ setFormViewStateUi(state);
20139
+ setFormPickRect(form.getBoundingClientRect());
20140
+ if (state === "success") {
20141
+ const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
20142
+ if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
20143
+ } else {
20144
+ deactivateRef.current();
20145
+ }
20146
+ },
20147
+ children: state
20148
+ },
20149
+ state
20150
+ )) })
20151
+ ]
20152
+ }
20153
+ )
20154
+ }
20155
+ ),
20156
+ formHoverRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20157
+ ItemInteractionLayer,
20158
+ {
20159
+ rect: formHoverRect,
20160
+ state: "hover",
20161
+ chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
20162
+ }
20163
+ ),
20164
+ fieldPickRect && fieldPickState && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20165
+ ItemInteractionLayer,
20166
+ {
20167
+ rect: fieldPickRect,
20168
+ state: "active-top",
20169
+ itemDragSurface: false,
20170
+ toolbarAlign: "left",
20171
+ chromeGap: 10,
20172
+ showHandle: true,
20173
+ dragHandleLabel: "Reorder field",
20174
+ onDragHandleDragStart: handleFieldDragStart,
20175
+ onDragHandleDragEnd: handleFieldDragEnd,
20176
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20177
+ FormFieldToolbar,
20178
+ {
20179
+ type: fieldPickState.type,
20180
+ required: fieldPickState.required,
20181
+ onTypeChange: handleFieldTypeChange,
20182
+ onRequiredToggle: handleFieldRequiredToggle,
20183
+ onDuplicate: handleFieldDuplicate,
20184
+ onDelete: handleFieldDelete
20185
+ }
20186
+ )
20187
+ }
20188
+ ),
20189
+ fieldDropIndex !== null && formPickElRef.current ? (() => {
20190
+ const wrappers = listFieldWrappers(formPickElRef.current).filter(
20191
+ (el) => fieldKeyOf(el) !== fieldDragRef.current?.key
20192
+ );
20193
+ const anchor = wrappers[Math.min(fieldDropIndex, wrappers.length - 1)];
20194
+ if (!anchor) return null;
20195
+ const rect = anchor.getBoundingClientRect();
20196
+ const atEnd = fieldDropIndex >= wrappers.length;
20197
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20198
+ "div",
20199
+ {
20200
+ className: "pointer-events-none fixed z-[2147483644]",
20201
+ style: { top: atEnd ? rect.bottom : rect.top, left: rect.left, width: rect.width },
20202
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(DropIndicator, { direction: "horizontal", state: "dragActive" })
20203
+ }
20204
+ );
20205
+ })() : null,
20206
+ fieldTypePickerOpen && formPickRect && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20207
+ "div",
20208
+ {
20209
+ className: "pointer-events-none fixed z-[2147483645]",
20210
+ style: { top: formPickRect.top + 16, left: formPickRect.left + 24 },
20211
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(FieldTypePicker, { onPick: handleAddField })
20212
+ }
20213
+ ),
20214
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
20215
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19053
20216
  FooterContainerChrome,
19054
20217
  {
19055
20218
  rect: toolbarRect,
@@ -19057,7 +20220,7 @@ function OhhwellsBridge() {
19057
20220
  addDisabled: !canAddFooterColumn()
19058
20221
  }
19059
20222
  ),
19060
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20223
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19061
20224
  ItemInteractionLayer,
19062
20225
  {
19063
20226
  rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
@@ -19072,7 +20235,7 @@ function OhhwellsBridge() {
19072
20235
  onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
19073
20236
  onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
19074
20237
  itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
19075
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20238
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19076
20239
  ItemActionToolbar,
19077
20240
  {
19078
20241
  onEditLink: openLinkPopoverForSelected,
@@ -19108,8 +20271,8 @@ function OhhwellsBridge() {
19108
20271
  ) : void 0
19109
20272
  }
19110
20273
  ),
19111
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
19112
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20274
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
20275
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19113
20276
  EditGlowChrome,
19114
20277
  {
19115
20278
  rect: toolbarRect,
@@ -19119,7 +20282,7 @@ function OhhwellsBridge() {
19119
20282
  hideHandle: isItemDragging
19120
20283
  }
19121
20284
  ),
19122
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20285
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19123
20286
  FloatingToolbar,
19124
20287
  {
19125
20288
  rect: toolbarRect,
@@ -19132,7 +20295,7 @@ function OhhwellsBridge() {
19132
20295
  }
19133
20296
  )
19134
20297
  ] }),
19135
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
20298
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
19136
20299
  "div",
19137
20300
  {
19138
20301
  "data-ohw-max-badge": "",
@@ -19158,7 +20321,7 @@ function OhhwellsBridge() {
19158
20321
  ]
19159
20322
  }
19160
20323
  ),
19161
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20324
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19162
20325
  StateToggle,
19163
20326
  {
19164
20327
  rect: toggleState.rect,
@@ -19167,15 +20330,15 @@ function OhhwellsBridge() {
19167
20330
  onStateChange: handleStateChange
19168
20331
  }
19169
20332
  ),
19170
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
20333
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
19171
20334
  "div",
19172
20335
  {
19173
20336
  "data-ohw-section-insert-line": "",
19174
20337
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
19175
20338
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
19176
20339
  children: [
19177
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
19178
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20340
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
20341
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19179
20342
  Badge,
19180
20343
  {
19181
20344
  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",
@@ -19192,11 +20355,11 @@ function OhhwellsBridge() {
19192
20355
  children: "Add Section"
19193
20356
  }
19194
20357
  ),
19195
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
20358
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
19196
20359
  ]
19197
20360
  }
19198
20361
  ),
19199
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20362
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19200
20363
  LinkPopover,
19201
20364
  {
19202
20365
  panelRef: linkPopoverPanelRef,
@@ -19213,7 +20376,7 @@ function OhhwellsBridge() {
19213
20376
  },
19214
20377
  linkPopover.key
19215
20378
  ) : null,
19216
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20379
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19217
20380
  FloatingPanel,
19218
20381
  {
19219
20382
  open: true,
@@ -19223,7 +20386,7 @@ function OhhwellsBridge() {
19223
20386
  onPositionChange: setFloatingPanelPos,
19224
20387
  parentScroll: parentScrollSnap ?? parentScrollRef.current,
19225
20388
  onClose: closeFloatingPanelOnly,
19226
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20389
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19227
20390
  SocialsDisplayPanel,
19228
20391
  {
19229
20392
  display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
@@ -19235,7 +20398,7 @@ function OhhwellsBridge() {
19235
20398
  )
19236
20399
  }
19237
20400
  ) : null,
19238
- floatingPanel && floatingPanel.kind === "logo-size" && logoSizeDraft ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20401
+ floatingPanel && floatingPanel.kind === "logo-size" && logoSizeDraft ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19239
20402
  FloatingPanel,
19240
20403
  {
19241
20404
  open: true,
@@ -19245,7 +20408,7 @@ function OhhwellsBridge() {
19245
20408
  onPositionChange: setFloatingPanelPos,
19246
20409
  parentScroll: parentScrollSnap ?? parentScrollRef.current,
19247
20410
  onClose: closeFloatingPanelAndDeselect,
19248
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20411
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19249
20412
  LogoSizePanel,
19250
20413
  {
19251
20414
  viewport: editorViewport,
@@ -19293,10 +20456,10 @@ function OhhwellsBridge() {
19293
20456
 
19294
20457
  // src/ui/EmptySection.tsx
19295
20458
  var import_link = __toESM(require("next/link"), 1);
19296
- var import_jsx_runtime34 = require("react/jsx-runtime");
20459
+ var import_jsx_runtime35 = require("react/jsx-runtime");
19297
20460
  function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
19298
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
19299
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20461
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
20462
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
19300
20463
  "p",
19301
20464
  {
19302
20465
  style: {
@@ -19308,10 +20471,10 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
19308
20471
  color: "var(--brand-accent)",
19309
20472
  marginBottom: "1.5rem"
19310
20473
  },
19311
- children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_link.default, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
20474
+ children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_link.default, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
19312
20475
  }
19313
20476
  ),
19314
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20477
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
19315
20478
  "h1",
19316
20479
  {
19317
20480
  style: {
@@ -19326,7 +20489,7 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
19326
20489
  children: title
19327
20490
  }
19328
20491
  ),
19329
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20492
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
19330
20493
  "p",
19331
20494
  {
19332
20495
  style: {