@ohhwells/bridge 0.1.54 → 0.1.55-next.160

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React11, { useCallback as useCallback6, useEffect as useEffect11, useLayoutEffect as useLayoutEffect3, useRef as useRef8, useState as useState10 } from "react";
4
+ import React11, { useCallback as useCallback7, useEffect as useEffect12, useLayoutEffect as useLayoutEffect4, useRef as useRef9, useState as useState11 } from "react";
5
5
  import { createRoot as createRoot2 } from "react-dom/client";
6
6
  import { flushSync as flushSync2 } from "react-dom";
7
7
 
@@ -118,6 +118,129 @@ function deleteSectionFromState(state, sectionId) {
118
118
  return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
119
119
  }
120
120
 
121
+ // src/lib/brand-chrome.ts
122
+ var BRAND_NAME_KEY = "__ohw_brand_name";
123
+ var BRAND_TITLE_KEY = "__ohw_site_title";
124
+ var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
125
+ var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
126
+ BRAND_NAME_KEY,
127
+ BRAND_TITLE_KEY,
128
+ BRAND_FAVICON_LETTER_KEY
129
+ ]);
130
+ function upsertMeta(selector, attr, token, value) {
131
+ let el = document.head.querySelector(selector);
132
+ if (!el) {
133
+ el = document.createElement("meta");
134
+ el.setAttribute(attr, token);
135
+ document.head.appendChild(el);
136
+ }
137
+ if (el.getAttribute("content") !== value) el.setAttribute("content", value);
138
+ }
139
+ function escapeXml(value) {
140
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
141
+ }
142
+ function applyLetterFavicon(letter) {
143
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="12" fill="#111827"/><text x="32" y="46" font-family="system-ui,-apple-system,Segoe UI,sans-serif" font-size="40" font-weight="700" text-anchor="middle" fill="#ffffff">${escapeXml(letter)}</text></svg>`;
144
+ const href = `data:image/svg+xml,${encodeURIComponent(svg)}`;
145
+ let link = document.head.querySelector('link[rel="icon"]');
146
+ if (!link) {
147
+ link = document.createElement("link");
148
+ link.rel = "icon";
149
+ document.head.appendChild(link);
150
+ }
151
+ link.type = "image/svg+xml";
152
+ if (link.href !== href) link.href = href;
153
+ }
154
+ function applyBrandChrome(content) {
155
+ const name = content[BRAND_NAME_KEY];
156
+ if (typeof name === "string" && name.length > 0) {
157
+ document.querySelectorAll("[data-ohw-wordmark]").forEach((el) => {
158
+ if (el.textContent !== name) el.textContent = name;
159
+ if (el.getAttribute("title") !== name) el.setAttribute("title", name);
160
+ });
161
+ }
162
+ const title = content[BRAND_TITLE_KEY];
163
+ if (typeof title === "string" && title.length > 0) {
164
+ if (document.title !== title) document.title = title;
165
+ upsertMeta('meta[property="og:title"]', "property", "og:title", title);
166
+ upsertMeta('meta[name="twitter:title"]', "name", "twitter:title", title);
167
+ }
168
+ const letter = content[BRAND_FAVICON_LETTER_KEY];
169
+ if (typeof letter === "string" && letter.length > 0) {
170
+ applyLetterFavicon(letter);
171
+ }
172
+ }
173
+
174
+ // src/lib/brand-kit.ts
175
+ var BRAND_KIT_KEY = "__ohw_brand";
176
+ var BRAND_VAR_PREFIX = "--ohw-brand-";
177
+ var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
178
+ (role) => `${BRAND_VAR_PREFIX}${role}`
179
+ );
180
+ var FONT_VARS = { heading: ["--font-heading", "--font-display"], body: ["--font-body"] };
181
+ var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
182
+ function brandColorVars(kit) {
183
+ const { dark, primary, accent, light } = kit.palette;
184
+ const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
185
+ return {
186
+ [`${BRAND_VAR_PREFIX}primary`]: primary,
187
+ [`${BRAND_VAR_PREFIX}accent`]: accent,
188
+ [`${BRAND_VAR_PREFIX}light`]: light,
189
+ [`${BRAND_VAR_PREFIX}dark`]: dark,
190
+ [`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
191
+ [`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
192
+ [`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
193
+ };
194
+ }
195
+ function parseBrandKit(raw) {
196
+ if (!raw) return null;
197
+ try {
198
+ const parsed = JSON.parse(raw);
199
+ const p = parsed?.palette;
200
+ const f = parsed?.fonts;
201
+ if (!p || !f || typeof p.dark !== "string" || typeof p.primary !== "string" || typeof p.accent !== "string" || typeof p.light !== "string" || typeof f.heading !== "string" || typeof f.body !== "string") {
202
+ return null;
203
+ }
204
+ return {
205
+ palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
206
+ fonts: { heading: f.heading, body: f.body }
207
+ };
208
+ } catch {
209
+ return null;
210
+ }
211
+ }
212
+ function familyOf(stack) {
213
+ const first = stack.split(",")[0]?.trim() ?? "";
214
+ return first.replace(/^['"]|['"]$/g, "");
215
+ }
216
+ function loadBrandFonts(families) {
217
+ const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
218
+ if (unique.length === 0) return;
219
+ const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
220
+ const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
221
+ let link = document.getElementById(BRAND_FONT_LINK_ID);
222
+ if (!link) {
223
+ link = document.createElement("link");
224
+ link.id = BRAND_FONT_LINK_ID;
225
+ link.rel = "stylesheet";
226
+ document.head.appendChild(link);
227
+ }
228
+ if (link.href !== href) link.href = href;
229
+ }
230
+ function applyBrandToDom(kit) {
231
+ const root = document.documentElement;
232
+ if (!kit) {
233
+ for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
234
+ for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
235
+ document.getElementById(BRAND_FONT_LINK_ID)?.remove();
236
+ return;
237
+ }
238
+ for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
239
+ for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
240
+ for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
241
+ loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
242
+ }
243
+
121
244
  // src/ui/ai-tree/aiSectionsManager.tsx
122
245
  import { flushSync } from "react-dom";
123
246
  import { createRoot } from "react-dom/client";
@@ -150,6 +273,17 @@ var FEATURE_LINE_CSS = [
150
273
  function textAttrs(ctx, path) {
151
274
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
152
275
  }
276
+ var AI_RESPONSIVE_CSS = [
277
+ "@media (max-width: 960px) {",
278
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
279
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
280
+ "}",
281
+ "@media (max-width: 640px) {",
282
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
283
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
284
+ " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
285
+ "}"
286
+ ].join("\n");
153
287
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
154
288
  function MediaBox({
155
289
  refValue,
@@ -162,13 +296,17 @@ function MediaBox({
162
296
  const url = refValue ? ctx.resolveMedia(refValue) : null;
163
297
  const isIcon = /^(lucide|simple):/.test(refValue);
164
298
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
165
- const editAttrs = ctx.keyFor && editPath && !isIcon ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "image" } : {};
299
+ const editAttrs = ctx.keyFor && editPath ? {
300
+ "data-ohw-key": ctx.keyFor(editPath),
301
+ "data-ohw-editable": isIcon ? "icon" : "image"
302
+ } : {};
166
303
  if (isIcon) {
167
304
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
168
305
  return /* @__PURE__ */ jsx(
169
306
  "span",
170
307
  {
171
308
  "data-ai-icon": refValue,
309
+ ...editAttrs,
172
310
  style: {
173
311
  display: "inline-flex",
174
312
  width: 48,
@@ -888,6 +1026,7 @@ function Carousel({ items, itemsPerRow, ctx }) {
888
1026
  children: pageGroups.map((group, p) => /* @__PURE__ */ jsx(
889
1027
  "div",
890
1028
  {
1029
+ "data-ai-grid": String(itemsPerRow),
891
1030
  style: {
892
1031
  flex: "0 0 100%",
893
1032
  display: "grid",
@@ -991,6 +1130,7 @@ function CollectionBlock({ node, ctx, path }) {
991
1130
  return /* @__PURE__ */ jsx(
992
1131
  "div",
993
1132
  {
1133
+ "data-ai-grid": String(itemsPerRow),
994
1134
  style: {
995
1135
  display: "grid",
996
1136
  gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
@@ -1154,6 +1294,7 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1154
1294
  {
1155
1295
  "data-ai-section": tree.tag ?? "",
1156
1296
  ...bgAttrs,
1297
+ "data-ai-responsive": "",
1157
1298
  style: {
1158
1299
  position: "relative",
1159
1300
  padding: `${pad}px 0`,
@@ -1163,10 +1304,12 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1163
1304
  backgroundPosition: "center"
1164
1305
  },
1165
1306
  children: [
1307
+ /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
1166
1308
  isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
1167
1309
  /* @__PURE__ */ jsx(
1168
1310
  "div",
1169
1311
  {
1312
+ "data-ai-section-inner": "",
1170
1313
  style: {
1171
1314
  position: "relative",
1172
1315
  maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
@@ -1177,6 +1320,7 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1177
1320
  children: tree.rows.map((row, r2) => /* @__PURE__ */ jsx(
1178
1321
  "div",
1179
1322
  {
1323
+ "data-ai-columns": "",
1180
1324
  style: {
1181
1325
  display: "grid",
1182
1326
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -1200,17 +1344,34 @@ import { jsx as jsx2 } from "react/jsx-runtime";
1200
1344
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1201
1345
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1202
1346
  var REMOVED_ATTR = "data-ohw-ai-removed";
1347
+ function readRootVar(name) {
1348
+ if (typeof document === "undefined") return "";
1349
+ return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
1350
+ }
1351
+ function deriveBrandOverride() {
1352
+ const dark = readRootVar("--ohw-brand-dark");
1353
+ const primary = readRootVar("--ohw-brand-primary");
1354
+ const light = readRootVar("--ohw-brand-light");
1355
+ if (!dark || !primary || !light) return null;
1356
+ const accent = readRootVar("--ohw-brand-accent");
1357
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1358
+ const body = readRootVar("--font-body");
1359
+ return {
1360
+ palette: { dark, primary, accent: accent || dark, light },
1361
+ fonts: {
1362
+ heading: heading || AI_DEFAULT_BRAND.fonts.heading,
1363
+ body: body || AI_DEFAULT_BRAND.fonts.body
1364
+ }
1365
+ };
1366
+ }
1203
1367
  function deriveTemplateBrand() {
1204
- if (typeof document === "undefined") return null;
1205
- const cs = getComputedStyle(document.documentElement);
1206
- const read = (name) => cs.getPropertyValue(name).trim();
1207
- const dark = read("--color-dark");
1208
- const primary = read("--color-primary");
1209
- const light = read("--color-light");
1368
+ const dark = readRootVar("--color-dark");
1369
+ const primary = readRootVar("--color-primary");
1370
+ const light = readRootVar("--color-light");
1210
1371
  if (!dark || !primary || !light) return null;
1211
- const accent = read("--color-accent");
1212
- const heading = read("--font-heading") || read("--font-display");
1213
- const body = read("--font-body");
1372
+ const accent = readRootVar("--color-accent");
1373
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1374
+ const body = readRootVar("--font-body");
1214
1375
  return {
1215
1376
  palette: { dark, primary, accent: accent || dark, light },
1216
1377
  fonts: {
@@ -1302,7 +1463,9 @@ function syncReplacedOriginals(state) {
1302
1463
  }
1303
1464
  function applyAiSectionsToDom(state, options) {
1304
1465
  if (typeof document === "undefined") return;
1466
+ const brandOverride = deriveBrandOverride();
1305
1467
  const templateBrand = deriveTemplateBrand();
1468
+ const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
1306
1469
  const activeIds = new Set(state.sections.map((entry) => entry.id));
1307
1470
  for (const [id, section] of mounted) {
1308
1471
  if (!activeIds.has(id)) {
@@ -1312,7 +1475,7 @@ function applyAiSectionsToDom(state, options) {
1312
1475
  }
1313
1476
  }
1314
1477
  for (const entry of state.sections) {
1315
- const serialized = JSON.stringify(entry);
1478
+ const serialized = JSON.stringify(entry) + brandKey;
1316
1479
  const existing = mounted.get(entry.id);
1317
1480
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
1318
1481
  continue;
@@ -1336,7 +1499,7 @@ function applyAiSectionsToDom(state, options) {
1336
1499
  AiTreeRenderer,
1337
1500
  {
1338
1501
  tree: entry.tree,
1339
- brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1502
+ brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1340
1503
  resolveMedia,
1341
1504
  editKeyPrefix: `ai.${entry.id}`
1342
1505
  }
@@ -1953,7 +2116,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
1953
2116
  const autoId = useId();
1954
2117
  const insertAfter = insertAfterProp ?? autoId;
1955
2118
  const [schedule, setSchedule] = useState2(null);
1956
- const [loading, setLoading] = useState2(true);
2119
+ const [loading, setLoading] = useState2(initialScheduleId !== null);
1957
2120
  const [inEditor, setInEditor] = useState2(false);
1958
2121
  const [isHovered, setIsHovered] = useState2(false);
1959
2122
  const [modalState, setModalState] = useState2(null);
@@ -2127,8 +2290,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2127
2290
  "*"
2128
2291
  );
2129
2292
  };
2130
- if (!inEditor && !loading && !schedule) return null;
2131
2293
  const sectionId = `scheduling-${insertAfter}`;
2294
+ if (!inEditor && !loading && !schedule) {
2295
+ return /* @__PURE__ */ jsx4("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2296
+ }
2132
2297
  return /* @__PURE__ */ jsxs3(
2133
2298
  "section",
2134
2299
  {
@@ -5697,7 +5862,7 @@ CustomToolbarButton.displayName = "CustomToolbarButton";
5697
5862
 
5698
5863
  // src/ui/item-action-toolbar.tsx
5699
5864
  import { useState as useState3 } from "react";
5700
- import { Copy, CornerLeftUp, Heading, Link, MoreHorizontal, Plus, Trash2 } from "lucide-react";
5865
+ import { Brush, Copy, CornerLeftUp, Heading, Link, MoreHorizontal, Plus, Trash2 } from "lucide-react";
5701
5866
 
5702
5867
  // src/ui/dropdown-menu.tsx
5703
5868
  import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
@@ -5862,6 +6027,7 @@ function ToolbarActionTooltip({
5862
6027
  function ItemActionToolbar({
5863
6028
  onEditLink,
5864
6029
  onAddItem,
6030
+ onStyle,
5865
6031
  onSelectParent,
5866
6032
  onDuplicate,
5867
6033
  onDelete,
@@ -5873,6 +6039,8 @@ function ItemActionToolbar({
5873
6039
  deleteDisabled = false,
5874
6040
  showEditLink = true,
5875
6041
  showAddItem = true,
6042
+ showStyle = false,
6043
+ styleActive = false,
5876
6044
  showMore = true,
5877
6045
  tooltipSide = "bottom",
5878
6046
  dropdownOpen = null,
@@ -5946,6 +6114,22 @@ function ItemActionToolbar({
5946
6114
  children: /* @__PURE__ */ jsx11(Link, { className: "size-4 shrink-0", "aria-hidden": true })
5947
6115
  }
5948
6116
  ) : null,
6117
+ showStyle ? /* @__PURE__ */ jsx11(
6118
+ ToolbarActionTooltip,
6119
+ {
6120
+ label: "Style",
6121
+ side: tooltipSide,
6122
+ buttonProps: {
6123
+ active: styleActive,
6124
+ onMouseDown: (e) => {
6125
+ e.preventDefault();
6126
+ e.stopPropagation();
6127
+ onStyle?.();
6128
+ }
6129
+ },
6130
+ children: /* @__PURE__ */ jsx11(Brush, { className: "size-4 shrink-0", "aria-hidden": true })
6131
+ }
6132
+ ) : null,
5949
6133
  showAddItem ? /* @__PURE__ */ jsx11(
5950
6134
  ToolbarActionTooltip,
5951
6135
  {
@@ -6063,6 +6247,7 @@ var FOCUS_RING = `0 0 0 4px color-mix(in srgb, ${PRIMARY} 12%, transparent)`;
6063
6247
  var DRAG_SHADOW = "0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.1)";
6064
6248
  var TOOLBAR_EDGE_MARGIN = 4;
6065
6249
  var SELECTION_CHROME_GAP = 4;
6250
+ var HOVER_CHROME_GAP = 2;
6066
6251
  var TOOLBAR_STROKE_GAP = 4;
6067
6252
  function getChromeZIndex(state) {
6068
6253
  switch (state) {
@@ -6218,10 +6403,11 @@ function ItemInteractionLayer({
6218
6403
  onItemPointerDown,
6219
6404
  onItemClick,
6220
6405
  itemDragSurface = true,
6221
- chromeGap = SELECTION_CHROME_GAP,
6406
+ chromeGap,
6222
6407
  className
6223
6408
  }) {
6224
6409
  if (state === "default") return null;
6410
+ const gap = chromeGap ?? (state === "hover" ? HOVER_CHROME_GAP : SELECTION_CHROME_GAP);
6225
6411
  const isActive = state === "active-top" || state === "active-bottom";
6226
6412
  const isDragging = state === "dragging";
6227
6413
  const showToolbar = isActive && toolbar;
@@ -6237,10 +6423,10 @@ function ItemInteractionLayer({
6237
6423
  className: cn("pointer-events-none", className),
6238
6424
  style: {
6239
6425
  position: "fixed",
6240
- top: rect.top - chromeGap,
6241
- left: rect.left - chromeGap,
6242
- width: rect.width + chromeGap * 2,
6243
- height: rect.height + chromeGap * 2,
6426
+ top: rect.top - gap,
6427
+ left: rect.left - gap,
6428
+ width: rect.width + gap * 2,
6429
+ height: rect.height + gap * 2,
6244
6430
  zIndex: getChromeZIndex(state)
6245
6431
  },
6246
6432
  children: [
@@ -9452,9 +9638,453 @@ function deleteNavbarItem(sourceAnchor) {
9452
9638
  };
9453
9639
  }
9454
9640
 
9641
+ // src/lib/icon-markup.ts
9642
+ var GLYPH_SELECTOR = "svg, img";
9643
+ function referenceBox(slot) {
9644
+ const row = slot.closest("[data-ohw-socials-row]") ?? slot.closest("a")?.parentElement ?? null;
9645
+ const neighbour = row ? Array.from(row.querySelectorAll('[data-ohw-editable="icon"]')).find((el) => el !== slot) : null;
9646
+ const source = (neighbour ?? slot).querySelector(GLYPH_SELECTOR);
9647
+ const box = source?.getBoundingClientRect() ?? null;
9648
+ return box?.width && box.height ? box : null;
9649
+ }
9650
+ function iconMarkupSizedFor(slot, markup) {
9651
+ const box = referenceBox(slot);
9652
+ if (!box) return markup;
9653
+ const holder = document.createElement("div");
9654
+ holder.innerHTML = markup;
9655
+ const glyph = holder.querySelector(GLYPH_SELECTOR);
9656
+ if (!glyph) return markup;
9657
+ glyph.style.width = `${Math.round(box.width)}px`;
9658
+ glyph.style.height = `${Math.round(box.height)}px`;
9659
+ return holder.innerHTML;
9660
+ }
9661
+ function applyIconMarkup(slot, markup) {
9662
+ if (!markup) return;
9663
+ const coloured = iconMarkupInheritingColour(markup);
9664
+ const sized = iconMarkupSizedFor(slot, coloured);
9665
+ if (slot.innerHTML !== sized) slot.innerHTML = sized;
9666
+ if (sized === coloured) {
9667
+ requestAnimationFrame(() => {
9668
+ if (!slot.isConnected) return;
9669
+ const resized = iconMarkupSizedFor(slot, coloured);
9670
+ if (resized !== coloured && slot.innerHTML !== resized) slot.innerHTML = resized;
9671
+ });
9672
+ }
9673
+ }
9674
+ function detectIconStyle(el) {
9675
+ const row = el.closest("[data-ohw-socials-row]");
9676
+ const glyphs = Array.from((row ?? el).querySelectorAll("svg"));
9677
+ const outlined = glyphs.some((svg) => {
9678
+ return Array.from(svg.querySelectorAll("*")).some((node) => {
9679
+ return node.getAttribute("stroke") !== null && node.getAttribute("stroke") !== "none";
9680
+ });
9681
+ });
9682
+ return outlined ? "outline" : "fill";
9683
+ }
9684
+ function iconMarkupInheritingColour(markup) {
9685
+ const holder = document.createElement("div");
9686
+ holder.innerHTML = markup;
9687
+ holder.querySelectorAll("svg *").forEach((node) => {
9688
+ if (node.getAttribute("fill") && node.getAttribute("fill") !== "none") {
9689
+ node.setAttribute("fill", "currentColor");
9690
+ }
9691
+ if (node.getAttribute("stroke") && node.getAttribute("stroke") !== "none") {
9692
+ node.setAttribute("stroke", "currentColor");
9693
+ }
9694
+ });
9695
+ return holder.innerHTML;
9696
+ }
9697
+
9698
+ // src/lib/socials-items.ts
9699
+ var ICON_SELECTOR = '[data-ohw-editable="icon"]';
9700
+ var SOCIAL_KEY_RE = /(^|-)social(s)?(-|$)/i;
9701
+ var SOCIALS_ROW_ATTR = "data-ohw-socials-row";
9702
+ var SOCIALS_ITEM_ATTR = "data-ohw-social-item";
9703
+ function isSocialItem(el) {
9704
+ if (!el) return false;
9705
+ const anchor = el instanceof HTMLAnchorElement ? el : el.closest("a");
9706
+ if (!anchor) return false;
9707
+ if (SOCIAL_KEY_RE.test(anchor.getAttribute("data-ohw-href-key") ?? "")) return true;
9708
+ return anchor.querySelectorAll(ICON_SELECTOR).length === 1;
9709
+ }
9710
+ function getSocialItem(el) {
9711
+ const anchor = el.closest("a");
9712
+ return isSocialItem(anchor) ? anchor : null;
9713
+ }
9714
+ function findSocialsRow(el) {
9715
+ const item = getSocialItem(el);
9716
+ if (!item) return null;
9717
+ const wrapper = item.parentElement;
9718
+ const row = wrapper && wrapper.querySelectorAll("a").length === 1 && wrapper.matches("li, div, span") ? wrapper.parentElement : wrapper;
9719
+ if (!row) return null;
9720
+ const anchors = Array.from(row.querySelectorAll("a"));
9721
+ if (!anchors.length || !anchors.every((anchor) => isSocialItem(anchor))) return null;
9722
+ return row;
9723
+ }
9724
+ function isSocialsRow(el) {
9725
+ const anchors = Array.from(el.querySelectorAll("a"));
9726
+ return anchors.length > 0 && anchors.every((anchor) => isSocialItem(anchor));
9727
+ }
9728
+ function listSocialItems(row) {
9729
+ return Array.from(row.children).map((child) => {
9730
+ if (!(child instanceof HTMLElement)) return null;
9731
+ const anchor = child.matches("a") ? child : child.querySelector("a");
9732
+ return isSocialItem(anchor) ? anchor : null;
9733
+ }).filter((item) => item !== null);
9734
+ }
9735
+ function socialRowUnit(item) {
9736
+ const row = findSocialsRow(item);
9737
+ let node = item;
9738
+ while (node.parentElement && node.parentElement !== row) {
9739
+ node = node.parentElement;
9740
+ }
9741
+ return node;
9742
+ }
9743
+ function listSocialsRows(root = document) {
9744
+ const rows = /* @__PURE__ */ new Set();
9745
+ root.querySelectorAll(`${ICON_SELECTOR}, a[data-ohw-href-key]`).forEach((el) => {
9746
+ const row = findSocialsRow(el);
9747
+ if (row) rows.add(row);
9748
+ });
9749
+ root.querySelectorAll(`[${SOCIALS_ROW_ATTR}]`).forEach((row) => rows.add(row));
9750
+ return Array.from(rows);
9751
+ }
9752
+ var rowTemplates = /* @__PURE__ */ new Map();
9753
+ function markSocialsRows(root = document) {
9754
+ root.querySelectorAll(`[${SOCIALS_ITEM_ATTR}]`).forEach((item) => {
9755
+ item.removeAttribute(SOCIALS_ITEM_ATTR);
9756
+ });
9757
+ listSocialsRows(root).forEach((row) => {
9758
+ row.setAttribute(SOCIALS_ROW_ATTR, "");
9759
+ const items = listSocialItems(row);
9760
+ if (items[0]) rowTemplates.set(rowKeyOf(row), socialRowUnit(items[0]).outerHTML);
9761
+ items.forEach((item, index) => {
9762
+ item.setAttribute(SOCIALS_ITEM_ATTR, String(index));
9763
+ const iconKey = socialIconKey(item);
9764
+ if (iconKey) ensureLabelSlot(item, iconKey);
9765
+ });
9766
+ });
9767
+ }
9768
+ var SOCIALS_LABEL_ATTR = "data-ohw-social-label";
9769
+ function ensureLabelSlot(item, iconKey) {
9770
+ if (socialLabelElement(item)) return;
9771
+ const label = document.createElement("span");
9772
+ label.setAttribute("data-ohw-key", `${iconKey}-label`);
9773
+ label.setAttribute("data-ohw-editable", "text");
9774
+ label.setAttribute(SOCIALS_LABEL_ATTR, "");
9775
+ label.style.display = "none";
9776
+ label.textContent = item.getAttribute("aria-label") ?? "";
9777
+ item.appendChild(label);
9778
+ }
9779
+ function socialLabelElement(item) {
9780
+ return item.querySelector(
9781
+ `[${SOCIALS_LABEL_ATTR}], [data-ohw-editable="text"], [data-ohw-editable="plain"]`
9782
+ );
9783
+ }
9784
+ function socialLabelKey(iconKey) {
9785
+ return `${iconKey}-label`;
9786
+ }
9787
+ function applyStoredValues(item, content) {
9788
+ const hrefKey = socialHrefKey(item);
9789
+ const iconKey = socialIconKey(item);
9790
+ if (hrefKey && content[hrefKey] !== void 0) item.setAttribute("href", content[hrefKey]);
9791
+ if (iconKey) {
9792
+ const glyph = item.querySelector(ICON_SELECTOR);
9793
+ if (glyph && content[iconKey]) applyIconMarkup(glyph, content[iconKey]);
9794
+ const label = item.querySelector(`[${SOCIALS_LABEL_ATTR}]`);
9795
+ label?.setAttribute("data-ohw-key", socialLabelKey(iconKey));
9796
+ const stored = content[socialLabelKey(iconKey)];
9797
+ if (label && stored) label.textContent = stored;
9798
+ }
9799
+ }
9800
+ function socialPlatformKey(iconKey) {
9801
+ return `${iconKey}-platform`;
9802
+ }
9803
+ function socialHrefKey(item) {
9804
+ return item.getAttribute("data-ohw-href-key");
9805
+ }
9806
+ function socialIconKey(item) {
9807
+ return item.querySelector(ICON_SELECTOR)?.dataset.ohwKey ?? null;
9808
+ }
9809
+ var SOCIALS_ORDER_KEY = "__ohw_socials_order";
9810
+ function fromMarkup(markup) {
9811
+ const holder = document.createElement("div");
9812
+ holder.innerHTML = markup;
9813
+ return holder.firstElementChild instanceof HTMLElement ? holder.firstElementChild : null;
9814
+ }
9815
+ var rowKeys = /* @__PURE__ */ new WeakMap();
9816
+ function rowKeyOf(row) {
9817
+ const first = listSocialItems(row)[0];
9818
+ const itemKey = first ? socialIconKey(first) ?? socialHrefKey(first)?.replace(/-href$/, "") : null;
9819
+ const derived = itemKey?.replace(/-[^-]+$/, "") || null;
9820
+ if (derived) rowKeys.set(row, derived);
9821
+ return derived ?? rowKeys.get(row) ?? "social";
9822
+ }
9823
+ function getSocialsOrderFromDom(root = document) {
9824
+ const order = {};
9825
+ listSocialsRows(root).forEach((row) => {
9826
+ order[rowKeyOf(row)] = listSocialItems(row).map((item) => socialHrefKey(item)).filter((key) => Boolean(key));
9827
+ });
9828
+ return order;
9829
+ }
9830
+ function hasStoredValue(content, hrefKey) {
9831
+ const iconKey = hrefKey.replace(/-href$/, "");
9832
+ return Boolean(content[hrefKey]) || Boolean(content[iconKey]);
9833
+ }
9834
+ function parseSocialsOrder(raw) {
9835
+ if (!raw) return null;
9836
+ try {
9837
+ const parsed = JSON.parse(raw);
9838
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
9839
+ } catch {
9840
+ return null;
9841
+ }
9842
+ }
9843
+ function nextSocialIndex(row, rowKey, content) {
9844
+ const used = listSocialItems(row).map((item) => socialHrefKey(item)).concat(Object.keys(content)).map((key) => key?.match(new RegExp(`^${rowKey}-(\\d+)`))?.[1]).map((digits) => digits === void 0 ? -1 : Number(digits));
9845
+ return Math.max(-1, ...used) + 1;
9846
+ }
9847
+ function insertSocialItem(row, after, content = {}) {
9848
+ const rowKey = rowKeyOf(row);
9849
+ const template = listSocialItems(row)[0];
9850
+ const remembered = rowTemplates.get(rowKey);
9851
+ if (!template && !remembered) return null;
9852
+ const index = nextSocialIndex(row, rowKey, content);
9853
+ const iconKey = `${rowKey}-${index}`;
9854
+ const hrefKey = `${iconKey}-href`;
9855
+ const templateUnit = template ? socialRowUnit(template) : null;
9856
+ const unit = templateUnit ? templateUnit.cloneNode(true) : fromMarkup(remembered);
9857
+ const item = unit && (unit.matches("a") ? unit : unit.querySelector("a"));
9858
+ if (!unit || !item) return null;
9859
+ item.setAttribute("data-ohw-href-key", hrefKey);
9860
+ item.setAttribute("href", "");
9861
+ item.removeAttribute("aria-label");
9862
+ item.querySelectorAll("[data-ohw-hovered], [data-ohw-selected]").forEach((el) => {
9863
+ el.removeAttribute("data-ohw-hovered");
9864
+ el.removeAttribute("data-ohw-selected");
9865
+ });
9866
+ const icon = item.querySelector(ICON_SELECTOR);
9867
+ icon?.setAttribute("data-ohw-key", iconKey);
9868
+ item.querySelector(`[${SOCIALS_LABEL_ATTR}]`)?.remove();
9869
+ const afterUnit = after ? socialRowUnit(after) : null;
9870
+ if (afterUnit && afterUnit.parentElement === row) afterUnit.insertAdjacentElement("afterend", unit);
9871
+ else row.appendChild(unit);
9872
+ markSocialsRows(row.ownerDocument);
9873
+ return { item, hrefKey, iconKey, order: getSocialsOrderFromDom(row.ownerDocument) };
9874
+ }
9875
+ function duplicateSocialItem(item, content) {
9876
+ const row = findSocialsRow(item);
9877
+ const created = row ? insertSocialItem(row, item, content) : null;
9878
+ if (!created) return null;
9879
+ const sourceHref = socialHrefKey(item);
9880
+ const sourceIcon = socialIconKey(item);
9881
+ const link = created.item;
9882
+ if (sourceHref) link.setAttribute("href", item.getAttribute("href") ?? "");
9883
+ const glyph = item.querySelector(ICON_SELECTOR)?.innerHTML;
9884
+ if (glyph) {
9885
+ const slot = link.querySelector(ICON_SELECTOR);
9886
+ if (slot) slot.innerHTML = glyph;
9887
+ }
9888
+ return {
9889
+ ...created,
9890
+ copiedFrom: { href: sourceHref, icon: sourceIcon }
9891
+ };
9892
+ }
9893
+ function removeSocialItem(item, content) {
9894
+ const row = findSocialsRow(item);
9895
+ if (!row) return null;
9896
+ const hrefKey = socialHrefKey(item);
9897
+ const iconKey = socialIconKey(item);
9898
+ const removedKeys = [hrefKey, iconKey].filter((key) => Boolean(key));
9899
+ if (!removedKeys.length) return null;
9900
+ const previousOrder = getSocialsOrderFromDom(row.ownerDocument);
9901
+ const previousContent = Object.fromEntries(
9902
+ removedKeys.filter((key) => key in content).map((key) => [key, content[key]])
9903
+ );
9904
+ const unit = socialRowUnit(item);
9905
+ const nextSibling = unit.nextElementSibling;
9906
+ unit.remove();
9907
+ markSocialsRows(row.ownerDocument);
9908
+ return {
9909
+ removedKeys,
9910
+ previousContent,
9911
+ order: getSocialsOrderFromDom(row.ownerDocument),
9912
+ previousOrder,
9913
+ undo: () => {
9914
+ if (nextSibling) nextSibling.before(unit);
9915
+ else row.appendChild(unit);
9916
+ markSocialsRows(row.ownerDocument);
9917
+ }
9918
+ };
9919
+ }
9920
+ function applySocialsOrder(order, root = document) {
9921
+ listSocialsRows(root).forEach((row) => {
9922
+ const wanted = order[rowKeyOf(row)];
9923
+ if (!wanted) return;
9924
+ const byKey = new Map(listSocialItems(row).map((item) => [socialHrefKey(item), item]));
9925
+ wanted.forEach((key) => {
9926
+ const item = byKey.get(key);
9927
+ if (item) row.appendChild(socialRowUnit(item));
9928
+ });
9929
+ });
9930
+ markSocialsRows(root);
9931
+ }
9932
+ function reconcileSocialsFromContent(content, root = document) {
9933
+ markSocialsRows(root);
9934
+ const stored = parseSocialsOrder(content[SOCIALS_ORDER_KEY]);
9935
+ if (!stored) return;
9936
+ listSocialsRows(root).forEach((row) => {
9937
+ const wanted = stored[rowKeyOf(row)];
9938
+ if (!wanted) return;
9939
+ if (!wanted.length) return;
9940
+ wanted.forEach((key) => {
9941
+ if (listSocialItems(row).some((item) => socialHrefKey(item) === key)) return;
9942
+ if (!hasStoredValue(content, key)) return;
9943
+ const created = insertSocialItem(row, null, content);
9944
+ if (created) {
9945
+ created.item.setAttribute("data-ohw-href-key", key);
9946
+ created.item.querySelector(ICON_SELECTOR)?.setAttribute("data-ohw-key", key.replace(/-href$/, ""));
9947
+ applyStoredValues(created.item, content);
9948
+ }
9949
+ });
9950
+ const present = listSocialItems(row);
9951
+ const surviving = present.filter((item) => {
9952
+ const key = socialHrefKey(item);
9953
+ return !key || wanted.includes(key);
9954
+ });
9955
+ if (surviving.length) {
9956
+ present.forEach((item) => {
9957
+ if (!surviving.includes(item)) socialRowUnit(item).remove();
9958
+ });
9959
+ }
9960
+ });
9961
+ applySocialsOrder(stored, root);
9962
+ }
9963
+ var DROP_BAR_THICKNESS = 3;
9964
+ var DROP_BAR_GAP = 8;
9965
+ function buildSocialDropSlots(row) {
9966
+ const items = listSocialItems(row);
9967
+ if (!items.length) return [];
9968
+ const rects = items.map((item) => item.getBoundingClientRect());
9969
+ return items.concat(items[items.length - 1]).map((_, index) => {
9970
+ const previous = rects[index - 1];
9971
+ const next = rects[index];
9972
+ const centre = previous && next ? (previous.right + next.left) / 2 : next ? next.left - DROP_BAR_GAP : previous.right + DROP_BAR_GAP;
9973
+ const rect = next ?? previous;
9974
+ return {
9975
+ insertIndex: index,
9976
+ columnIndex: -1,
9977
+ left: centre - DROP_BAR_THICKNESS / 2,
9978
+ top: rect.top,
9979
+ width: DROP_BAR_THICKNESS,
9980
+ height: rect.height,
9981
+ direction: "vertical"
9982
+ };
9983
+ });
9984
+ }
9985
+ function findSocialByHrefKey(hrefKey, root = document) {
9986
+ const el = root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
9987
+ return el ? getSocialItem(el) : null;
9988
+ }
9989
+ function buildSocialDropSlotsForKey(hrefKey, root = document) {
9990
+ const item = findSocialByHrefKey(hrefKey, root);
9991
+ const row = item ? findSocialsRow(item) : null;
9992
+ return row ? buildSocialDropSlots(row) : [];
9993
+ }
9994
+ function hitTestSocialDropSlot(clientX, clientY, draggedHrefKey, root = document) {
9995
+ const distanceTo = (slot) => {
9996
+ const dx = clientX - (slot.left + slot.width / 2);
9997
+ const dy = clientY < slot.top ? slot.top - clientY : Math.max(0, clientY - (slot.top + slot.height));
9998
+ return Math.hypot(dx, dy);
9999
+ };
10000
+ const slots = buildSocialDropSlotsForKey(draggedHrefKey, root);
10001
+ return slots.reduce((best, slot) => {
10002
+ return !best || distanceTo(slot) < distanceTo(best) ? slot : best;
10003
+ }, null);
10004
+ }
10005
+ function planSocialMove(hrefKey, insertIndex, root = document) {
10006
+ const item = findSocialByHrefKey(hrefKey, root);
10007
+ const row = item ? findSocialsRow(item) : null;
10008
+ if (!row) return null;
10009
+ const order = getSocialsOrderFromDom(root);
10010
+ const key = rowKeyOf(row);
10011
+ const current = order[key];
10012
+ if (!current) return null;
10013
+ const from = current.indexOf(hrefKey);
10014
+ if (from < 0) return null;
10015
+ const next = current.filter((_, index) => index !== from);
10016
+ next.splice(insertIndex > from ? insertIndex - 1 : insertIndex, 0, hrefKey);
10017
+ return { ...order, [key]: next };
10018
+ }
10019
+ var SOCIALS_DISPLAY_KEY = "__ohw_socials_display";
10020
+ function readSocialsDisplay(row) {
10021
+ const items = listSocialItems(row);
10022
+ const visible = (el) => Boolean(el) && el.style.display !== "none" && el.getAttribute("data-ohw-hidden") === null;
10023
+ return {
10024
+ text: items.some((item) => visible(socialLabelElement(item))),
10025
+ icon: items.some((item) => visible(item.querySelector(ICON_SELECTOR)))
10026
+ };
10027
+ }
10028
+ function parseSocialsDisplay(raw) {
10029
+ if (!raw) return null;
10030
+ try {
10031
+ const parsed = JSON.parse(raw);
10032
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
10033
+ } catch {
10034
+ return null;
10035
+ }
10036
+ }
10037
+ function socialsDisplayFor(row, content) {
10038
+ return parseSocialsDisplay(content[SOCIALS_DISPLAY_KEY])?.[rowKeyOf(row)] ?? readSocialsDisplay(row);
10039
+ }
10040
+ function socialsDisplayWith(row, display, content) {
10041
+ return { ...parseSocialsDisplay(content[SOCIALS_DISPLAY_KEY]) ?? {}, [rowKeyOf(row)]: display };
10042
+ }
10043
+ function applySocialsDisplayToRow(row, display) {
10044
+ listSocialItems(row).forEach((item) => {
10045
+ const label = socialLabelElement(item);
10046
+ const icon = item.querySelector(ICON_SELECTOR);
10047
+ if (label) label.style.display = display.text ? "" : "none";
10048
+ if (icon) icon.style.display = display.icon ? "" : "none";
10049
+ });
10050
+ }
10051
+ function applySocialsDisplayFromContent(content, root = document) {
10052
+ const stored = parseSocialsDisplay(content[SOCIALS_DISPLAY_KEY]);
10053
+ if (!stored) return;
10054
+ listSocialsRows(root).forEach((row) => {
10055
+ const display = stored[rowKeyOf(row)];
10056
+ if (!display) return;
10057
+ if (display.icon) {
10058
+ listSocialItems(row).forEach((item) => {
10059
+ const iconKey = ensureIconSlot(item);
10060
+ const slot = item.querySelector(ICON_SELECTOR);
10061
+ if (iconKey && slot && content[iconKey]) applyIconMarkup(slot, content[iconKey]);
10062
+ });
10063
+ }
10064
+ applySocialsDisplayToRow(row, display);
10065
+ });
10066
+ }
10067
+ function socialsMissingIcons(row) {
10068
+ return listSocialItems(row).filter((item) => !item.querySelector(ICON_SELECTOR)).map((item) => ({ hrefKey: socialHrefKey(item) ?? "", url: item.getAttribute("href") ?? "" })).filter((entry) => Boolean(entry.hrefKey));
10069
+ }
10070
+ function ensureIconSlot(item) {
10071
+ const existing = item.querySelector(ICON_SELECTOR);
10072
+ if (existing) return existing.dataset.ohwKey ?? null;
10073
+ const hrefKey = socialHrefKey(item);
10074
+ if (!hrefKey) return null;
10075
+ const iconKey = hrefKey.replace(/-href$/, "");
10076
+ const slot = document.createElement("span");
10077
+ slot.setAttribute("data-ohw-key", iconKey);
10078
+ slot.setAttribute("data-ohw-editable", "icon");
10079
+ slot.style.display = "inline-flex";
10080
+ item.prepend(slot);
10081
+ return iconKey;
10082
+ }
10083
+
9455
10084
  // src/lib/footer-items.ts
9456
10085
  var FOOTER_ORDER_KEY = "__ohw_footer_order";
9457
10086
  var MAX_FOOTER_COLUMNS = 18;
10087
+ var MAX_FOOTER_ITEMS_PER_COLUMN = 7;
9458
10088
  var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
9459
10089
  function parseFooterHrefKey(key) {
9460
10090
  if (!key) return null;
@@ -9672,6 +10302,13 @@ function getNextFooterColumnIndex() {
9672
10302
  function canAddFooterColumn() {
9673
10303
  return listFooterColumns().length < MAX_FOOTER_COLUMNS;
9674
10304
  }
10305
+ function canAddFooterItem(column) {
10306
+ return listFooterLinksInColumn(column).length < MAX_FOOTER_ITEMS_PER_COLUMN;
10307
+ }
10308
+ function resolveFooterColumnForAdd(selected) {
10309
+ if (selected.hasAttribute("data-ohw-footer-col")) return selected;
10310
+ return selected.closest("[data-ohw-footer-col]") ?? (listFooterColumns().includes(selected) ? selected : null);
10311
+ }
9675
10312
  function buildFooterHeading(colIndex, text) {
9676
10313
  const heading = document.createElement("p");
9677
10314
  heading.setAttribute("data-ohw-editable", "text");
@@ -10296,6 +10933,329 @@ function deleteFooterColumn(column) {
10296
10933
  };
10297
10934
  }
10298
10935
 
10936
+ // src/lib/logo-identity.ts
10937
+ var LOGO_TEXT_KEYS = ["nav-logo-text", "footer-logo-text", "logo-text"];
10938
+ var LOGO_IMAGE_KEYS = ["nav-logo-image", "footer-logo", "footer-logo-image"];
10939
+ var LOGO_HREF_KEYS = ["nav-logo-href", "footer-logo-href", "logo-href"];
10940
+ var LOGO_PLACEHOLDER_KEY = "logo-is-placeholder";
10941
+ var LOGO_ALT_KEY = "logo-alt";
10942
+ var LOGO_IMAGE_URL_KEY = "nav-logo-image";
10943
+ var PLACEHOLDER_BUSINESS_NAME = "Business name";
10944
+ function resolveLogoDisplayText(text) {
10945
+ const trimmed = (text ?? "").trim();
10946
+ return trimmed || PLACEHOLDER_BUSINESS_NAME;
10947
+ }
10948
+ function isFooterLogoRoot(root) {
10949
+ return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
10950
+ }
10951
+ function imageKeyForRoot(root) {
10952
+ return isFooterLogoRoot(root) ? "footer-logo" : "nav-logo-image";
10953
+ }
10954
+ function textKeyForRoot(root) {
10955
+ return isFooterLogoRoot(root) ? "footer-logo-text" : "nav-logo-text";
10956
+ }
10957
+ function ensureLogoHrefKey(root) {
10958
+ if (!(root instanceof HTMLAnchorElement)) return;
10959
+ if (root.hasAttribute("data-ohw-href-key")) return;
10960
+ root.setAttribute("data-ohw-href-key", isFooterLogoRoot(root) ? "footer-logo-href" : "nav-logo-href");
10961
+ }
10962
+ function applyLogoIdentity(text, isPlaceholder) {
10963
+ const display = resolveLogoDisplayText(text);
10964
+ const placeholder = isPlaceholder || !text.trim() || display === PLACEHOLDER_BUSINESS_NAME;
10965
+ for (const key of LOGO_TEXT_KEYS) {
10966
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
10967
+ if (el.textContent !== display) el.textContent = display;
10968
+ });
10969
+ }
10970
+ for (const key of LOGO_IMAGE_KEYS) {
10971
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
10972
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
10973
+ if (img) img.alt = display;
10974
+ });
10975
+ }
10976
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((el) => {
10977
+ if (placeholder) el.setAttribute("data-ohw-placeholder", "");
10978
+ else el.removeAttribute("data-ohw-placeholder");
10979
+ });
10980
+ return display;
10981
+ }
10982
+ function applyLogoImage(url, alt) {
10983
+ const displayAlt = resolveLogoDisplayText(alt);
10984
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
10985
+ ensureLogoHrefKey(root);
10986
+ const imageKey = imageKeyForRoot(root);
10987
+ const textKey = textKeyForRoot(root);
10988
+ let img = root.querySelector(`img[data-ohw-key="${imageKey}"]`) ?? (root.querySelector(`[data-ohw-key="${imageKey}"]`) instanceof HTMLImageElement ? root.querySelector(`[data-ohw-key="${imageKey}"]`) : null) ?? root.querySelector("img");
10989
+ let textEl = root.querySelector(`[data-ohw-key="${textKey}"]`) ?? root.querySelector('[data-ohw-key="logo-text"]');
10990
+ if (url) {
10991
+ if (!img) {
10992
+ img = document.createElement("img");
10993
+ img.setAttribute("data-ohw-editable", "image");
10994
+ img.setAttribute("data-ohw-key", imageKey);
10995
+ img.alt = displayAlt;
10996
+ img.style.height = "";
10997
+ img.style.maxHeight = "none";
10998
+ img.style.width = "auto";
10999
+ img.style.display = "block";
11000
+ img.style.objectFit = "contain";
11001
+ root.insertBefore(img, root.firstChild);
11002
+ } else {
11003
+ img.setAttribute("data-ohw-editable", "image");
11004
+ img.setAttribute("data-ohw-key", imageKey);
11005
+ }
11006
+ img.removeAttribute("srcset");
11007
+ img.removeAttribute("sizes");
11008
+ img.src = url;
11009
+ img.alt = displayAlt;
11010
+ img.style.display = "block";
11011
+ if (textEl) textEl.style.display = "none";
11012
+ root.removeAttribute("data-ohw-placeholder");
11013
+ return;
11014
+ }
11015
+ if (img) {
11016
+ img.removeAttribute("src");
11017
+ img.removeAttribute("srcset");
11018
+ img.removeAttribute("sizes");
11019
+ img.alt = displayAlt;
11020
+ img.style.display = "none";
11021
+ }
11022
+ if (!textEl) {
11023
+ textEl = document.createElement("span");
11024
+ textEl.setAttribute("data-ohw-editable", "plain");
11025
+ textEl.setAttribute("data-ohw-key", textKey);
11026
+ root.appendChild(textEl);
11027
+ }
11028
+ textEl.style.display = "";
11029
+ if (textEl.textContent !== displayAlt) textEl.textContent = displayAlt;
11030
+ if (!displayAlt.trim() || displayAlt === PLACEHOLDER_BUSINESS_NAME) {
11031
+ root.setAttribute("data-ohw-placeholder", "");
11032
+ } else {
11033
+ root.removeAttribute("data-ohw-placeholder");
11034
+ }
11035
+ });
11036
+ }
11037
+ function applyLogoHref(href) {
11038
+ const target = href.trim() || "/";
11039
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11040
+ ensureLogoHrefKey(root);
11041
+ if (root instanceof HTMLAnchorElement) {
11042
+ root.setAttribute("href", target);
11043
+ }
11044
+ });
11045
+ for (const key of LOGO_HREF_KEYS) setStoredLinkHref(key, target);
11046
+ }
11047
+ function readLogoIdentityFromDom() {
11048
+ let imageUrl = null;
11049
+ for (const key of LOGO_IMAGE_KEYS) {
11050
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
11051
+ const img = el instanceof HTMLImageElement ? el : el?.querySelector("img");
11052
+ const attrSrc = img?.getAttribute("src")?.trim() ?? "";
11053
+ if (attrSrc && !attrSrc.startsWith("data:") && img && img.style.display !== "none") {
11054
+ imageUrl = img.currentSrc || img.src;
11055
+ break;
11056
+ }
11057
+ }
11058
+ let text = PLACEHOLDER_BUSINESS_NAME;
11059
+ let isPlaceholder = true;
11060
+ for (const key of LOGO_TEXT_KEYS) {
11061
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
11062
+ if (el?.textContent?.trim()) {
11063
+ text = el.textContent.trim();
11064
+ const logoRoot2 = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11065
+ isPlaceholder = logoRoot2?.hasAttribute("data-ohw-placeholder") === true || text === PLACEHOLDER_BUSINESS_NAME;
11066
+ break;
11067
+ }
11068
+ }
11069
+ if (imageUrl) {
11070
+ const logoImg = document.querySelector(
11071
+ '[data-ohw-key="nav-logo-image"], [data-ohw-key="footer-logo"]'
11072
+ );
11073
+ const alt = logoImg?.alt?.trim() || text;
11074
+ isPlaceholder = false;
11075
+ const hrefEl = document.querySelector(
11076
+ 'a[data-ohw-role="logo"], a[data-ohw-logo], [data-ohw-role="logo"]'
11077
+ );
11078
+ const href2 = (hrefEl instanceof HTMLAnchorElement ? hrefEl.getAttribute("href") : null) || hrefEl?.closest("a")?.getAttribute("href") || "/";
11079
+ return { text, isPlaceholder, imageUrl, href: href2, alt };
11080
+ }
11081
+ const logoRoot = document.querySelector('[data-ohw-role="logo"], [data-ohw-logo]');
11082
+ const href = (logoRoot instanceof HTMLAnchorElement ? logoRoot.getAttribute("href") : null) || logoRoot?.closest("a")?.getAttribute("href") || "/";
11083
+ return { text, isPlaceholder, imageUrl: null, href, alt: text };
11084
+ }
11085
+ function applyLogoFromContent(content) {
11086
+ const hasLogoIdentity = LOGO_PLACEHOLDER_KEY in content || LOGO_TEXT_KEYS.some((key) => key in content) || LOGO_IMAGE_KEYS.some((key) => key in content) || LOGO_ALT_KEY in content || LOGO_HREF_KEYS.some((key) => key in content);
11087
+ if (!hasLogoIdentity) return false;
11088
+ const logoText = content[LOGO_TEXT_KEYS[0]] ?? content[LOGO_TEXT_KEYS[1]] ?? readLogoIdentityFromDom().text;
11089
+ const logoAlt = content[LOGO_ALT_KEY] ?? logoText;
11090
+ const rawLogoImage = content[LOGO_IMAGE_URL_KEY] ?? content["footer-logo"] ?? content["footer-logo-image"] ?? null;
11091
+ const logoImageUrl = typeof rawLogoImage === "string" && rawLogoImage.trim() ? rawLogoImage.trim() : null;
11092
+ const imageExplicitlyCleared = LOGO_IMAGE_KEYS.some((key) => key in content) && !logoImageUrl;
11093
+ const logoIsPlaceholder = LOGO_PLACEHOLDER_KEY in content ? content[LOGO_PLACEHOLDER_KEY] !== "false" : !logoImageUrl && (!logoText.trim() || logoText === PLACEHOLDER_BUSINESS_NAME);
11094
+ if (logoImageUrl) {
11095
+ applyLogoImage(logoImageUrl, logoAlt);
11096
+ } else {
11097
+ if (imageExplicitlyCleared) applyLogoImage(null, logoAlt);
11098
+ applyLogoIdentity(logoText, logoIsPlaceholder);
11099
+ }
11100
+ const logoHref = content["nav-logo-href"] ?? content["footer-logo-href"] ?? content["logo-href"];
11101
+ if (typeof logoHref === "string" && logoHref.trim()) {
11102
+ applyLogoHref(logoHref);
11103
+ }
11104
+ return true;
11105
+ }
11106
+
11107
+ // src/lib/logo-size.ts
11108
+ var LOGO_SIZE_DEFAULTS = {
11109
+ navbar: 28,
11110
+ footer: 32
11111
+ };
11112
+ var LOGO_SIZE_MIN = 16;
11113
+ var LOGO_SIZE_MAX = 80;
11114
+ var LOGO_SIZE_DESKTOP_KEYS = {
11115
+ navbar: "nav-logo-size",
11116
+ footer: "footer-logo-size"
11117
+ };
11118
+ var LOGO_SIZE_MOBILE_KEYS = {
11119
+ navbar: "nav-logo-size-mobile",
11120
+ footer: "footer-logo-size-mobile"
11121
+ };
11122
+ var LOGO_SIZE_KEYS = [
11123
+ LOGO_SIZE_DESKTOP_KEYS.navbar,
11124
+ LOGO_SIZE_DESKTOP_KEYS.footer,
11125
+ LOGO_SIZE_MOBILE_KEYS.navbar,
11126
+ LOGO_SIZE_MOBILE_KEYS.footer
11127
+ ];
11128
+ function isFooterLogoRoot2(root) {
11129
+ return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
11130
+ }
11131
+ function getLogoPlacement(root) {
11132
+ return isFooterLogoRoot2(root) ? "footer" : "navbar";
11133
+ }
11134
+ function parseLogoSizePx(raw, fallback) {
11135
+ if (raw == null || raw === "") return fallback;
11136
+ const n = Number.parseFloat(raw);
11137
+ if (!Number.isFinite(n)) return fallback;
11138
+ return Math.min(LOGO_SIZE_MAX, Math.max(LOGO_SIZE_MIN, Math.round(n)));
11139
+ }
11140
+ function isMobileLogoSizeFollowing(content, placement) {
11141
+ const raw = content[LOGO_SIZE_MOBILE_KEYS[placement]];
11142
+ return raw == null || raw.trim() === "";
11143
+ }
11144
+ function resolveDesktopLogoSize(content, placement) {
11145
+ return parseLogoSizePx(content[LOGO_SIZE_DESKTOP_KEYS[placement]], LOGO_SIZE_DEFAULTS[placement]);
11146
+ }
11147
+ function resolveMobileLogoSize(content, placement) {
11148
+ if (isMobileLogoSizeFollowing(content, placement)) {
11149
+ return resolveDesktopLogoSize(content, placement);
11150
+ }
11151
+ return parseLogoSizePx(
11152
+ content[LOGO_SIZE_MOBILE_KEYS[placement]],
11153
+ resolveDesktopLogoSize(content, placement)
11154
+ );
11155
+ }
11156
+ function setRootSizeVars(root, desktopPx, mobilePx, following) {
11157
+ root.style.setProperty("--ohw-logo-size", `${desktopPx}px`);
11158
+ if (following) {
11159
+ root.style.removeProperty("--ohw-logo-size-mobile");
11160
+ } else {
11161
+ root.style.setProperty("--ohw-logo-size-mobile", `${mobilePx}px`);
11162
+ }
11163
+ root.querySelectorAll("img").forEach((img) => {
11164
+ img.style.height = "";
11165
+ img.style.maxHeight = "none";
11166
+ img.style.width = "auto";
11167
+ img.style.objectFit = "contain";
11168
+ });
11169
+ }
11170
+ function applyLogoSizes(content) {
11171
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11172
+ const placement = getLogoPlacement(root);
11173
+ const desktop = resolveDesktopLogoSize(content, placement);
11174
+ const following = isMobileLogoSizeFollowing(content, placement);
11175
+ const mobile = following ? desktop : resolveMobileLogoSize(content, placement);
11176
+ setRootSizeVars(root, desktop, mobile, following);
11177
+ });
11178
+ }
11179
+ function applyLogoSizeToPlacement(placement, desktopPx, mobilePx, following) {
11180
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11181
+ if (getLogoPlacement(root) !== placement) return;
11182
+ setRootSizeVars(root, desktopPx, mobilePx, following);
11183
+ });
11184
+ }
11185
+ function logoHasUploadedImage(logoEl) {
11186
+ if (logoEl.hasAttribute("data-ohw-placeholder")) return false;
11187
+ const img = logoEl.querySelector('img[data-ohw-key="nav-logo-image"], img[data-ohw-key="footer-logo"], img[data-ohw-key="footer-logo-image"]') ?? logoEl.querySelector("img");
11188
+ if (!img) return false;
11189
+ const src = img.getAttribute("src")?.trim() ?? "";
11190
+ if (!src || src.startsWith("data:")) return false;
11191
+ if (img.style.display === "none") return false;
11192
+ return true;
11193
+ }
11194
+ function getLogoInteractionRect(logoEl) {
11195
+ if (logoHasUploadedImage(logoEl)) {
11196
+ const img = logoEl.querySelector('img[data-ohw-key="nav-logo-image"], img[data-ohw-key="footer-logo"], img[data-ohw-key="footer-logo-image"]') ?? logoEl.querySelector("img");
11197
+ if (img) {
11198
+ const r2 = img.getBoundingClientRect();
11199
+ if (r2.width > 0 && r2.height > 0) return r2;
11200
+ }
11201
+ }
11202
+ const text = logoEl.querySelector(
11203
+ '[data-ohw-key="nav-logo-text"], [data-ohw-key="footer-logo-text"]'
11204
+ );
11205
+ if (text) {
11206
+ const style = window.getComputedStyle(text);
11207
+ if (style.display !== "none" && style.visibility !== "hidden") {
11208
+ const r2 = text.getBoundingClientRect();
11209
+ if (r2.width > 0 && r2.height > 0) return r2;
11210
+ }
11211
+ }
11212
+ return logoEl.getBoundingClientRect();
11213
+ }
11214
+ function readLogoSizeState(content, placement) {
11215
+ const desktopPx = resolveDesktopLogoSize(content, placement);
11216
+ const mobileFollowing = isMobileLogoSizeFollowing(content, placement);
11217
+ const mobilePx = mobileFollowing ? desktopPx : resolveMobileLogoSize(content, placement);
11218
+ return { desktopPx, mobilePx, mobileFollowing };
11219
+ }
11220
+
11221
+ // src/lib/site-wide-scope.ts
11222
+ function getLogoElement(el) {
11223
+ const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11224
+ if (marked) return marked;
11225
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
11226
+ const root = el.closest("nav, [data-ohw-nav-root], footer");
11227
+ if (!root) return null;
11228
+ const anchor = el.closest("a");
11229
+ if (anchor && root.contains(anchor) && !anchor.hasAttribute("data-ohw-href-key") && !anchor.closest("[data-ohw-nav-container]") && Boolean(anchor.querySelector("img") || anchor.matches("img"))) {
11230
+ return anchor;
11231
+ }
11232
+ const img = el.matches("img") ? el : null;
11233
+ if (img && !img.closest("[data-ohw-href-key]") && !img.closest("[data-ohw-nav-container]") && (img.closest("footer") || img.closest("nav, [data-ohw-nav-root]"))) {
11234
+ return img;
11235
+ }
11236
+ return null;
11237
+ }
11238
+ function isInFooter(el) {
11239
+ if (!el) return false;
11240
+ return Boolean(el.closest("footer") || el.closest('[data-ohw-section="footer"]'));
11241
+ }
11242
+ function isSiteWideElement(el) {
11243
+ if (!el) return false;
11244
+ if (getLogoElement(el)) return true;
11245
+ if (isInFooter(el)) return true;
11246
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-root")) {
11247
+ return true;
11248
+ }
11249
+ if (el.hasAttribute("data-ohw-href-key") && el.closest("nav, [data-ohw-nav-root], [data-ohw-nav-drawer], aside")) {
11250
+ return true;
11251
+ }
11252
+ if (el.closest('[data-ohw-role="navbar-button"]')) return true;
11253
+ return false;
11254
+ }
11255
+ function isSiteWideScopeActive(args) {
11256
+ return isSiteWideElement(args.selected) || isSiteWideElement(args.hoveredItem) || isSiteWideElement(args.hoveredNavContainer) || isSiteWideElement(args.active);
11257
+ }
11258
+
10299
11259
  // src/lib/add-footer-column.ts
10300
11260
  function buildFooterColumnEditContentPatch(result) {
10301
11261
  return {
@@ -10328,37 +11288,406 @@ function addFooterColumnWithPersist({
10328
11288
  return result;
10329
11289
  }
10330
11290
 
10331
- // src/lib/item-drag-interaction.ts
10332
- function disableNativeHrefDrag(el) {
10333
- if (el.draggable) el.draggable = false;
10334
- if (el.getAttribute("draggable") !== "false") {
10335
- el.setAttribute("draggable", "false");
11291
+ // src/ui/FloatingPanel.tsx
11292
+ import {
11293
+ useCallback as useCallback5,
11294
+ useEffect as useEffect9,
11295
+ useLayoutEffect as useLayoutEffect3,
11296
+ useRef as useRef7,
11297
+ useState as useState8
11298
+ } from "react";
11299
+ import { X as X5 } from "lucide-react";
11300
+ import { jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
11301
+ var PANEL_WIDTH = 256;
11302
+ var EDGE_MARGIN = 16;
11303
+ function getVisibleClip(parentScroll) {
11304
+ const left = 0;
11305
+ const right = window.innerWidth;
11306
+ if (!parentScroll) {
11307
+ return { top: 0, bottom: window.innerHeight, left, right };
10336
11308
  }
11309
+ const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
11310
+ const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
11311
+ const bottom = Math.min(window.innerHeight, visibleCanvasTop + canvasH - iframeOffsetTop);
11312
+ return { top, bottom: Math.max(top, bottom), left, right };
10337
11313
  }
10338
- function clearTextSelection() {
10339
- const sel = window.getSelection();
10340
- if (sel && !sel.isCollapsed) sel.removeAllRanges();
10341
- }
10342
- function armItemPressDrag() {
10343
- document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
10344
- }
10345
- function lockItemDuringDrag() {
10346
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
10347
- document.documentElement.setAttribute("data-ohw-item-dragging", "");
10348
- clearTextSelection();
11314
+ function defaultFloatingPanelPosition(parentScroll, panelHeight = 280) {
11315
+ const clip = getVisibleClip(parentScroll);
11316
+ return {
11317
+ x: Math.max(EDGE_MARGIN, clip.right - PANEL_WIDTH - EDGE_MARGIN),
11318
+ y: Math.min(
11319
+ Math.max(clip.top + EDGE_MARGIN, EDGE_MARGIN),
11320
+ Math.max(clip.top + EDGE_MARGIN, clip.bottom - panelHeight - EDGE_MARGIN)
11321
+ )
11322
+ };
10349
11323
  }
10350
- function unlockItemDragInteraction() {
10351
- const wasDragging = document.documentElement.hasAttribute("data-ohw-item-dragging");
10352
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
10353
- document.documentElement.removeAttribute("data-ohw-item-dragging");
10354
- if (wasDragging) clearTextSelection();
11324
+ function clampPosition(pos, parentScroll, panelW, panelH) {
11325
+ const clip = getVisibleClip(parentScroll);
11326
+ const maxX = Math.max(clip.left + EDGE_MARGIN, clip.right - panelW - EDGE_MARGIN);
11327
+ const maxY = Math.max(clip.top + EDGE_MARGIN, clip.bottom - panelH - EDGE_MARGIN);
11328
+ return {
11329
+ x: Math.min(Math.max(pos.x, clip.left + EDGE_MARGIN), maxX),
11330
+ y: Math.min(Math.max(pos.y, clip.top + EDGE_MARGIN), maxY)
11331
+ };
10355
11332
  }
10356
- var armFooterPressDrag = armItemPressDrag;
10357
- var lockFooterDuringDrag = lockItemDuringDrag;
10358
- var unlockFooterDragInteraction = unlockItemDragInteraction;
10359
-
10360
- // src/lib/nav-dnd.ts
10361
- function listReorderableNavItems() {
11333
+ function FloatingPanel({
11334
+ open,
11335
+ title,
11336
+ context,
11337
+ icon,
11338
+ onClose,
11339
+ children,
11340
+ position,
11341
+ onPositionChange,
11342
+ parentScroll = null,
11343
+ className,
11344
+ bodyClassName
11345
+ }) {
11346
+ const panelRef = useRef7(null);
11347
+ const [measured, setMeasured] = useState8({ w: PANEL_WIDTH, h: 280 });
11348
+ const dragRef = useRef7(null);
11349
+ const resolved = position ?? defaultFloatingPanelPosition(parentScroll, measured.h);
11350
+ const clamped = clampPosition(resolved, parentScroll, measured.w, measured.h);
11351
+ useLayoutEffect3(() => {
11352
+ if (!open || !panelRef.current) return;
11353
+ const el = panelRef.current;
11354
+ const next = { w: el.offsetWidth || PANEL_WIDTH, h: el.offsetHeight || 280 };
11355
+ setMeasured((prev) => prev.w === next.w && prev.h === next.h ? prev : next);
11356
+ }, [open, children, title, context]);
11357
+ useEffect9(() => {
11358
+ if (!open || !position || !onPositionChange) return;
11359
+ const next = clampPosition(position, parentScroll, measured.w, measured.h);
11360
+ if (next.x !== position.x || next.y !== position.y) onPositionChange(next);
11361
+ }, [open, parentScroll, measured.w, measured.h, position, onPositionChange]);
11362
+ const onHeaderPointerDown = useCallback5(
11363
+ (e) => {
11364
+ if (e.button !== 0) return;
11365
+ if (e.target.closest("[data-ohw-floating-panel-close]")) return;
11366
+ e.preventDefault();
11367
+ e.stopPropagation();
11368
+ const el = e.currentTarget;
11369
+ el.setPointerCapture(e.pointerId);
11370
+ document.documentElement.setAttribute("data-ohw-panel-dragging", "");
11371
+ dragRef.current = {
11372
+ pointerId: e.pointerId,
11373
+ startX: e.clientX,
11374
+ startY: e.clientY,
11375
+ originX: clamped.x,
11376
+ originY: clamped.y
11377
+ };
11378
+ },
11379
+ [clamped.x, clamped.y]
11380
+ );
11381
+ const onHeaderPointerMove = useCallback5(
11382
+ (e) => {
11383
+ const drag = dragRef.current;
11384
+ if (!drag || drag.pointerId !== e.pointerId) return;
11385
+ e.preventDefault();
11386
+ const next = clampPosition(
11387
+ {
11388
+ x: drag.originX + (e.clientX - drag.startX),
11389
+ y: drag.originY + (e.clientY - drag.startY)
11390
+ },
11391
+ parentScroll,
11392
+ measured.w,
11393
+ measured.h
11394
+ );
11395
+ onPositionChange?.(next);
11396
+ },
11397
+ [measured.h, measured.w, onPositionChange, parentScroll]
11398
+ );
11399
+ const endDrag = useCallback5((e) => {
11400
+ const drag = dragRef.current;
11401
+ if (!drag || drag.pointerId !== e.pointerId) return;
11402
+ dragRef.current = null;
11403
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
11404
+ try {
11405
+ e.currentTarget.releasePointerCapture(e.pointerId);
11406
+ } catch {
11407
+ }
11408
+ }, []);
11409
+ useEffect9(() => {
11410
+ if (open) return;
11411
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
11412
+ }, [open]);
11413
+ useEffect9(() => () => document.documentElement.removeAttribute("data-ohw-panel-dragging"), []);
11414
+ if (!open) return null;
11415
+ return /* @__PURE__ */ jsxs16(
11416
+ "div",
11417
+ {
11418
+ ref: panelRef,
11419
+ "data-ohw-floating-panel": "",
11420
+ role: "dialog",
11421
+ "aria-label": title,
11422
+ className: cn(
11423
+ // Above MediaOverlay / item chrome (2147483646); link-modal content shares this tier.
11424
+ "fixed z-[2147483647] flex w-64 flex-col overflow-hidden rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
11425
+ className
11426
+ ),
11427
+ style: { left: clamped.x, top: clamped.y },
11428
+ onMouseDown: (e) => e.stopPropagation(),
11429
+ onPointerDown: (e) => e.stopPropagation(),
11430
+ onClick: (e) => e.stopPropagation(),
11431
+ children: [
11432
+ /* @__PURE__ */ jsxs16(
11433
+ "div",
11434
+ {
11435
+ "data-ohw-floating-panel-header": "",
11436
+ className: "relative flex cursor-grab items-start gap-2 border-b border-border py-5 pl-5 pr-11 active:cursor-grabbing",
11437
+ onPointerDown: onHeaderPointerDown,
11438
+ onPointerMove: onHeaderPointerMove,
11439
+ onPointerUp: endDrag,
11440
+ onPointerCancel: endDrag,
11441
+ children: [
11442
+ /* @__PURE__ */ jsxs16("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: [
11443
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2", children: [
11444
+ icon ? /* @__PURE__ */ jsx26("span", { className: "shrink-0 text-foreground", children: icon }) : null,
11445
+ /* @__PURE__ */ jsx26("p", { className: "min-w-0 flex-1 text-lg font-semibold leading-7 text-foreground", children: title })
11446
+ ] }),
11447
+ context ? /* @__PURE__ */ jsx26("p", { className: "w-full text-sm leading-5 text-muted-foreground", children: context }) : null
11448
+ ] }),
11449
+ /* @__PURE__ */ jsx26(
11450
+ "button",
11451
+ {
11452
+ type: "button",
11453
+ "data-ohw-floating-panel-close": "",
11454
+ "aria-label": "Close",
11455
+ className: "absolute right-2.5 top-2.5 rounded-sm p-1.5 text-foreground hover:bg-muted/50",
11456
+ onClick: (e) => {
11457
+ e.stopPropagation();
11458
+ onClose();
11459
+ },
11460
+ onPointerDown: (e) => e.stopPropagation(),
11461
+ children: /* @__PURE__ */ jsx26(X5, { size: 16, "aria-hidden": true })
11462
+ }
11463
+ )
11464
+ ]
11465
+ }
11466
+ ),
11467
+ /* @__PURE__ */ jsx26(
11468
+ "div",
11469
+ {
11470
+ "data-ohw-floating-panel-body": "",
11471
+ className: cn("flex w-full flex-col gap-4 p-5", bodyClassName),
11472
+ children
11473
+ }
11474
+ )
11475
+ ]
11476
+ }
11477
+ );
11478
+ }
11479
+
11480
+ // src/ui/logo-size-panel.tsx
11481
+ import { ArrowUpRight, Link as Link2 } from "lucide-react";
11482
+ import { jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
11483
+ function SizeSlider({
11484
+ value,
11485
+ onChange
11486
+ }) {
11487
+ const pct = (value - LOGO_SIZE_MIN) / (LOGO_SIZE_MAX - LOGO_SIZE_MIN) * 100;
11488
+ return /* @__PURE__ */ jsxs17("div", { className: "flex w-full flex-col gap-3", children: [
11489
+ /* @__PURE__ */ jsxs17("div", { className: "flex w-full items-center gap-2 text-sm font-medium leading-5", children: [
11490
+ /* @__PURE__ */ jsx27("span", { className: "min-w-0 flex-1 text-foreground", children: "Size" }),
11491
+ /* @__PURE__ */ jsxs17("span", { className: "shrink-0 whitespace-nowrap text-muted-foreground", children: [
11492
+ value,
11493
+ " px"
11494
+ ] })
11495
+ ] }),
11496
+ /* @__PURE__ */ jsxs17("div", { className: "relative h-2 w-full rounded-full bg-primary-50", children: [
11497
+ /* @__PURE__ */ jsx27(
11498
+ "div",
11499
+ {
11500
+ className: "absolute inset-y-0 left-0 rounded-full bg-primary",
11501
+ style: { width: `${pct}%` }
11502
+ }
11503
+ ),
11504
+ /* @__PURE__ */ jsx27(
11505
+ "input",
11506
+ {
11507
+ type: "range",
11508
+ min: LOGO_SIZE_MIN,
11509
+ max: LOGO_SIZE_MAX,
11510
+ step: 1,
11511
+ value,
11512
+ "aria-label": "Logo size",
11513
+ className: cn(
11514
+ "absolute inset-0 h-full w-full cursor-pointer appearance-none bg-transparent",
11515
+ "[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:size-5",
11516
+ "[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2",
11517
+ "[&::-webkit-slider-thumb]:border-primary [&::-webkit-slider-thumb]:bg-background",
11518
+ "[&::-moz-range-thumb]:size-5 [&::-moz-range-thumb]:rounded-full",
11519
+ "[&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-primary",
11520
+ "[&::-moz-range-thumb]:bg-background"
11521
+ ),
11522
+ onChange: (e) => onChange(Number(e.target.value))
11523
+ }
11524
+ )
11525
+ ] })
11526
+ ] });
11527
+ }
11528
+ function LogoSizePanel({
11529
+ viewport,
11530
+ sizePx,
11531
+ mobileFollowing = true,
11532
+ onSizeChange,
11533
+ onCustomizeMobile,
11534
+ onResetMobile,
11535
+ onUpdateEverywhere,
11536
+ className
11537
+ }) {
11538
+ const showFollowing = viewport === "mobile" && mobileFollowing;
11539
+ const showMobileSlider = viewport === "mobile" && !mobileFollowing;
11540
+ const showDesktopSlider = viewport === "desktop";
11541
+ return /* @__PURE__ */ jsxs17("div", { className: cn("flex w-full flex-col gap-4", className), children: [
11542
+ showFollowing ? /* @__PURE__ */ jsxs17("div", { className: "flex w-full flex-col gap-2", children: [
11543
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-start gap-1", children: [
11544
+ /* @__PURE__ */ jsx27(Link2, { size: 16, className: "mt-0.5 shrink-0 text-foreground", "aria-hidden": true }),
11545
+ /* @__PURE__ */ jsx27("p", { className: "text-sm font-semibold leading-5 text-foreground", children: "Following desktop size" })
11546
+ ] }),
11547
+ /* @__PURE__ */ jsx27("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." }),
11548
+ /* @__PURE__ */ jsx27(
11549
+ Button,
11550
+ {
11551
+ type: "button",
11552
+ variant: "outline",
11553
+ size: "sm",
11554
+ className: "h-9 w-full min-w-0 cursor-pointer",
11555
+ onClick: onCustomizeMobile,
11556
+ children: "Customize for mobile"
11557
+ }
11558
+ )
11559
+ ] }) : null,
11560
+ showDesktopSlider || showMobileSlider ? /* @__PURE__ */ jsx27(SizeSlider, { value: sizePx, onChange: onSizeChange }) : null,
11561
+ showMobileSlider ? /* @__PURE__ */ jsx27(
11562
+ Button,
11563
+ {
11564
+ type: "button",
11565
+ variant: "outline",
11566
+ size: "sm",
11567
+ className: "h-9 w-full min-w-0 cursor-pointer",
11568
+ onClick: onResetMobile,
11569
+ children: "Reset to desktop size"
11570
+ }
11571
+ ) : null,
11572
+ /* @__PURE__ */ jsx27("div", { className: "h-px w-full bg-border", role: "separator" }),
11573
+ /* @__PURE__ */ jsxs17(
11574
+ Button,
11575
+ {
11576
+ type: "button",
11577
+ variant: "outline",
11578
+ size: "sm",
11579
+ className: "h-9 w-full min-w-0 cursor-pointer gap-1",
11580
+ onClick: onUpdateEverywhere,
11581
+ children: [
11582
+ "Update logo everywhere",
11583
+ /* @__PURE__ */ jsx27(ArrowUpRight, { size: 16, "aria-hidden": true })
11584
+ ]
11585
+ }
11586
+ ),
11587
+ /* @__PURE__ */ jsx27("p", { className: "text-sm leading-5 text-muted-foreground", children: "Matches the right version to your background." })
11588
+ ] });
11589
+ }
11590
+
11591
+ // src/ui/socials-display-panel.tsx
11592
+ import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
11593
+ function DisplaySwitch({
11594
+ label,
11595
+ checked,
11596
+ disabled,
11597
+ onChange
11598
+ }) {
11599
+ return /* @__PURE__ */ jsxs18("div", { className: "flex w-full items-center gap-2", children: [
11600
+ /* @__PURE__ */ jsx28(
11601
+ "span",
11602
+ {
11603
+ className: cn(
11604
+ "min-w-0 flex-1 text-sm font-medium leading-5",
11605
+ disabled ? "text-muted-foreground" : "text-foreground"
11606
+ ),
11607
+ children: label
11608
+ }
11609
+ ),
11610
+ /* @__PURE__ */ jsx28(
11611
+ "button",
11612
+ {
11613
+ type: "button",
11614
+ role: "switch",
11615
+ "aria-checked": checked,
11616
+ "aria-label": label,
11617
+ disabled,
11618
+ onClick: () => onChange(!checked),
11619
+ className: cn(
11620
+ "relative h-5 w-9 shrink-0 rounded-full transition-colors",
11621
+ checked ? "bg-primary" : "bg-primary-50",
11622
+ disabled ? "cursor-default opacity-50" : "cursor-pointer"
11623
+ ),
11624
+ children: /* @__PURE__ */ jsx28(
11625
+ "span",
11626
+ {
11627
+ className: cn(
11628
+ "absolute top-0.5 h-4 w-4 rounded-full bg-white transition-all",
11629
+ checked ? "left-[1.125rem]" : "left-0.5"
11630
+ )
11631
+ }
11632
+ )
11633
+ }
11634
+ )
11635
+ ] });
11636
+ }
11637
+ function SocialsDisplayPanel({ display, onChange, className }) {
11638
+ return /* @__PURE__ */ jsxs18("div", { className: cn("flex w-full flex-col gap-3", className), children: [
11639
+ /* @__PURE__ */ jsx28(
11640
+ DisplaySwitch,
11641
+ {
11642
+ label: "Text",
11643
+ checked: display.text,
11644
+ disabled: display.text && !display.icon,
11645
+ onChange: (text) => onChange({ ...display, text })
11646
+ }
11647
+ ),
11648
+ /* @__PURE__ */ jsx28(
11649
+ DisplaySwitch,
11650
+ {
11651
+ label: "Icon",
11652
+ checked: display.icon,
11653
+ disabled: display.icon && !display.text,
11654
+ onChange: (icon) => onChange({ ...display, icon })
11655
+ }
11656
+ )
11657
+ ] });
11658
+ }
11659
+
11660
+ // src/lib/item-drag-interaction.ts
11661
+ function disableNativeHrefDrag(el) {
11662
+ if (el.draggable) el.draggable = false;
11663
+ if (el.getAttribute("draggable") !== "false") {
11664
+ el.setAttribute("draggable", "false");
11665
+ }
11666
+ }
11667
+ function clearTextSelection() {
11668
+ const sel = window.getSelection();
11669
+ if (sel && !sel.isCollapsed) sel.removeAllRanges();
11670
+ }
11671
+ function armItemPressDrag() {
11672
+ document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
11673
+ }
11674
+ function lockItemDuringDrag() {
11675
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
11676
+ document.documentElement.setAttribute("data-ohw-item-dragging", "");
11677
+ clearTextSelection();
11678
+ }
11679
+ function unlockItemDragInteraction() {
11680
+ const wasDragging = document.documentElement.hasAttribute("data-ohw-item-dragging");
11681
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
11682
+ document.documentElement.removeAttribute("data-ohw-item-dragging");
11683
+ if (wasDragging) clearTextSelection();
11684
+ }
11685
+ var armFooterPressDrag = armItemPressDrag;
11686
+ var lockFooterDuringDrag = lockItemDuringDrag;
11687
+ var unlockFooterDragInteraction = unlockItemDragInteraction;
11688
+
11689
+ // src/lib/nav-dnd.ts
11690
+ function listReorderableNavItems() {
10362
11691
  return listNavbarItems().filter((el) => {
10363
11692
  const key = el.getAttribute("data-ohw-href-key");
10364
11693
  return isNavbarHrefKey(key) && !isNestedNavChild(el);
@@ -10526,7 +11855,7 @@ function hitTestNavDropSlot(clientX, clientY, draggedHrefKey) {
10526
11855
  }
10527
11856
 
10528
11857
  // src/useNavItemDrag.ts
10529
- import { useCallback as useCallback5, useEffect as useEffect9, useRef as useRef7, useState as useState8 } from "react";
11858
+ import { useCallback as useCallback6, useEffect as useEffect10, useRef as useRef8, useState as useState9 } from "react";
10530
11859
  function useNavItemDrag({
10531
11860
  isEditMode,
10532
11861
  editContentRef,
@@ -10550,11 +11879,11 @@ function useNavItemDrag({
10550
11879
  getNavigationItemAnchor: getNavigationItemAnchor2,
10551
11880
  isDragHandleDisabled: isDragHandleDisabled2
10552
11881
  }) {
10553
- const navDragRef = useRef7(null);
10554
- const [navDropSlots, setNavDropSlots] = useState8([]);
10555
- const [activeNavDropIndex, setActiveNavDropIndex] = useState8(null);
10556
- const navPointerDragRef = useRef7(null);
10557
- const clearNavDragVisuals = useCallback5(() => {
11882
+ const navDragRef = useRef8(null);
11883
+ const [navDropSlots, setNavDropSlots] = useState9([]);
11884
+ const [activeNavDropIndex, setActiveNavDropIndex] = useState9(null);
11885
+ const navPointerDragRef = useRef8(null);
11886
+ const clearNavDragVisuals = useCallback6(() => {
10558
11887
  const session = navDragRef.current;
10559
11888
  const keepOpenEl = session?.draggedEl?.closest("[data-ohw-nav-children]") != null ? session.draggedEl : null;
10560
11889
  navDragRef.current = null;
@@ -10571,7 +11900,7 @@ function useNavItemDrag({
10571
11900
  document.documentElement.removeAttribute("data-ohw-nav-dragging-root");
10572
11901
  unlockItemDragInteraction();
10573
11902
  }, [setDraggedItemRect, setIsItemDragging, setSiblingHintRects]);
10574
- const refreshNavDragVisuals = useCallback5(
11903
+ const refreshNavDragVisuals = useCallback6(
10575
11904
  (session, activeSlot, clientX, clientY) => {
10576
11905
  setDraggedItemRect(session.draggedEl.getBoundingClientRect());
10577
11906
  if (typeof clientX === "number" && typeof clientY === "number") {
@@ -10589,13 +11918,13 @@ function useNavItemDrag({
10589
11918
  },
10590
11919
  [setDraggedItemRect, setSiblingHintRects]
10591
11920
  );
10592
- const refreshNavDragVisualsRef = useRef7(refreshNavDragVisuals);
11921
+ const refreshNavDragVisualsRef = useRef8(refreshNavDragVisuals);
10593
11922
  refreshNavDragVisualsRef.current = refreshNavDragVisuals;
10594
- const commitNavDragRef = useRef7(() => {
11923
+ const commitNavDragRef = useRef8(() => {
10595
11924
  });
10596
- const beginNavDragRef = useRef7(() => {
11925
+ const beginNavDragRef = useRef8(() => {
10597
11926
  });
10598
- const beginNavDrag = useCallback5(
11927
+ const beginNavDrag = useCallback6(
10599
11928
  (session) => {
10600
11929
  const rect = session.draggedEl.getBoundingClientRect();
10601
11930
  session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
@@ -10629,7 +11958,7 @@ function useNavItemDrag({
10629
11958
  ]
10630
11959
  );
10631
11960
  beginNavDragRef.current = beginNavDrag;
10632
- const commitNavDrag = useCallback5(
11961
+ const commitNavDrag = useCallback6(
10633
11962
  (clientX, clientY) => {
10634
11963
  const session = navDragRef.current;
10635
11964
  if (!session) {
@@ -10690,7 +12019,7 @@ function useNavItemDrag({
10690
12019
  [clearNavDragVisuals, deselectRef, editContentRef, postToParentRef, selectRef]
10691
12020
  );
10692
12021
  commitNavDragRef.current = commitNavDrag;
10693
- const startNavLinkDrag = useCallback5(
12022
+ const startNavLinkDrag = useCallback6(
10694
12023
  (anchor, clientX, clientY, wasSelected) => {
10695
12024
  if (footerDragRef.current) return false;
10696
12025
  const hrefKey = anchor.getAttribute("data-ohw-href-key");
@@ -10708,7 +12037,7 @@ function useNavItemDrag({
10708
12037
  },
10709
12038
  [beginNavDrag, footerDragRef, isDragHandleDisabled2, isCtaButton]
10710
12039
  );
10711
- const onNavDragOver = useCallback5(
12040
+ const onNavDragOver = useCallback6(
10712
12041
  (e) => {
10713
12042
  const session = navDragRef.current;
10714
12043
  if (!session) return false;
@@ -10720,7 +12049,7 @@ function useNavItemDrag({
10720
12049
  },
10721
12050
  []
10722
12051
  );
10723
- useEffect9(() => {
12052
+ useEffect10(() => {
10724
12053
  if (!isEditMode) return;
10725
12054
  const THRESHOLD = 10;
10726
12055
  const resolveWasSelected = (el) => {
@@ -10845,7 +12174,7 @@ function useNavItemDrag({
10845
12174
  setLinkPopover,
10846
12175
  suppressNextClickRef
10847
12176
  ]);
10848
- const armNavPressFromChrome = useCallback5(
12177
+ const armNavPressFromChrome = useCallback6(
10849
12178
  (selected, clientX, clientY, pointerId) => {
10850
12179
  const hrefKey = selected.getAttribute("data-ohw-href-key");
10851
12180
  if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
@@ -10877,7 +12206,7 @@ function useNavItemDrag({
10877
12206
 
10878
12207
  // src/ui/footer-container-chrome.tsx
10879
12208
  import { Plus as Plus2 } from "lucide-react";
10880
- import { jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
12209
+ import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
10881
12210
  function FooterContainerChrome({
10882
12211
  rect,
10883
12212
  onAdd,
@@ -10885,7 +12214,7 @@ function FooterContainerChrome({
10885
12214
  }) {
10886
12215
  const chromeGap = 6;
10887
12216
  const buttonMargin = 7;
10888
- return /* @__PURE__ */ jsx26(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx26(
12217
+ return /* @__PURE__ */ jsx29(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx29(
10889
12218
  "div",
10890
12219
  {
10891
12220
  "data-ohw-footer-container-chrome": "",
@@ -10897,8 +12226,8 @@ function FooterContainerChrome({
10897
12226
  width: rect.width + chromeGap * 2,
10898
12227
  height: rect.height + chromeGap * 2
10899
12228
  },
10900
- children: /* @__PURE__ */ jsxs16(Tooltip, { children: [
10901
- /* @__PURE__ */ jsx26(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx26(
12229
+ children: /* @__PURE__ */ jsxs19(Tooltip, { children: [
12230
+ /* @__PURE__ */ jsx29(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
10902
12231
  "button",
10903
12232
  {
10904
12233
  type: "button",
@@ -10917,17 +12246,17 @@ function FooterContainerChrome({
10917
12246
  if (addDisabled) return;
10918
12247
  onAdd();
10919
12248
  },
10920
- children: /* @__PURE__ */ jsx26(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12249
+ children: /* @__PURE__ */ jsx29(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
10921
12250
  }
10922
12251
  ) }),
10923
- /* @__PURE__ */ jsx26(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
12252
+ /* @__PURE__ */ jsx29(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
10924
12253
  ] })
10925
12254
  }
10926
12255
  ) });
10927
12256
  }
10928
12257
 
10929
12258
  // src/lib/carousel.ts
10930
- import { useEffect as useEffect10, useState as useState9 } from "react";
12259
+ import { useEffect as useEffect11, useState as useState10 } from "react";
10931
12260
  var CAROUSEL_ATTR = "data-ohw-carousel";
10932
12261
  var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
10933
12262
  var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
@@ -10989,8 +12318,8 @@ function applyCarouselNode(key, val) {
10989
12318
  return true;
10990
12319
  }
10991
12320
  function useOhwCarousel(key, initial) {
10992
- const [images, setImages] = useState9(initial);
10993
- useEffect10(() => {
12321
+ const [images, setImages] = useState10(initial);
12322
+ useEffect11(() => {
10994
12323
  const el = document.querySelector(
10995
12324
  `[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`
10996
12325
  );
@@ -11103,6 +12432,18 @@ function collectEditableNodes(extraContent, root = document) {
11103
12432
  }
11104
12433
  if (extraContent && !isScoped) {
11105
12434
  applyNavFooterDeleteOverrides(byKey, extraContent);
12435
+ for (const key of LOGO_IMAGE_KEYS) {
12436
+ if (!(key in extraContent)) continue;
12437
+ byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
12438
+ }
12439
+ for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
12440
+ if (!(key in extraContent)) continue;
12441
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
12442
+ }
12443
+ for (const key of LOGO_SIZE_KEYS) {
12444
+ if (!(key in extraContent)) continue;
12445
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
12446
+ }
11106
12447
  }
11107
12448
  return Array.from(byKey.values());
11108
12449
  }
@@ -11205,7 +12546,7 @@ function isNavbarLinksContainer(el) {
11205
12546
  function isNavigationItem(el) {
11206
12547
  const anchor = el.matches("[data-ohw-href-key]") ? el : el.closest("[data-ohw-href-key]");
11207
12548
  if (!anchor) return false;
11208
- return Boolean(anchor.querySelector('[data-ohw-editable="text"]'));
12549
+ return Boolean(anchor.querySelector('[data-ohw-editable="text"]')) || Boolean(getSocialItem(anchor));
11209
12550
  }
11210
12551
  function findFooterItemGroup(item) {
11211
12552
  const explicit = item.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
@@ -11226,8 +12567,9 @@ function isInferredFooterGroup(el) {
11226
12567
  const footer = el.closest("footer");
11227
12568
  if (!footer || el === footer) return false;
11228
12569
  if (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column")) return true;
12570
+ if (isSocialsRow(el)) return false;
11229
12571
  const count = Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter(
11230
- isNavigationItem
12572
+ (item) => isNavigationItem(item) && !getSocialItem(item)
11231
12573
  ).length;
11232
12574
  return count >= 2;
11233
12575
  }
@@ -11271,7 +12613,8 @@ function deleteSelectedNavFooterItem(deps) {
11271
12613
  if (key.endsWith("-href")) applyLinkByKey2(key, text);
11272
12614
  else {
11273
12615
  document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`).forEach((el) => {
11274
- el.textContent = text;
12616
+ if (el.getAttribute("data-ohw-editable") === "icon") applyIconMarkup(el, text);
12617
+ else el.textContent = text;
11275
12618
  });
11276
12619
  }
11277
12620
  }
@@ -11333,6 +12676,21 @@ function deleteSelectedNavFooterItem(deps) {
11333
12676
  });
11334
12677
  return true;
11335
12678
  }
12679
+ const social = getSocialItem(selected);
12680
+ if (social) {
12681
+ const result = removeSocialItem(social, getEditContent());
12682
+ if (!result) return false;
12683
+ finishDelete({
12684
+ toastTitle: "Social deleted",
12685
+ removedKeys: result.removedKeys,
12686
+ previousContent: result.previousContent,
12687
+ orderKey: SOCIALS_ORDER_KEY,
12688
+ orderJson: JSON.stringify(result.order),
12689
+ previousOrderJson: JSON.stringify(result.previousOrder),
12690
+ undoDom: result.undo
12691
+ });
12692
+ return true;
12693
+ }
11336
12694
  if (isFooterHrefKey(hrefKey)) {
11337
12695
  const result = deleteFooterItem(selected);
11338
12696
  if (!result) return false;
@@ -11352,13 +12710,13 @@ function deleteSelectedNavFooterItem(deps) {
11352
12710
 
11353
12711
  // src/ui/navbar-container-chrome.tsx
11354
12712
  import { Plus as Plus3 } from "lucide-react";
11355
- import { jsx as jsx27 } from "react/jsx-runtime";
12713
+ import { jsx as jsx30 } from "react/jsx-runtime";
11356
12714
  function NavbarContainerChrome({
11357
12715
  rect,
11358
12716
  onAdd
11359
12717
  }) {
11360
12718
  const chromeGap = 6;
11361
- return /* @__PURE__ */ jsx27(
12719
+ return /* @__PURE__ */ jsx30(
11362
12720
  "div",
11363
12721
  {
11364
12722
  "data-ohw-navbar-container-chrome": "",
@@ -11370,7 +12728,7 @@ function NavbarContainerChrome({
11370
12728
  width: rect.width + chromeGap * 2,
11371
12729
  height: rect.height + chromeGap * 2
11372
12730
  },
11373
- children: /* @__PURE__ */ jsx27(
12731
+ children: /* @__PURE__ */ jsx30(
11374
12732
  "button",
11375
12733
  {
11376
12734
  type: "button",
@@ -11387,7 +12745,7 @@ function NavbarContainerChrome({
11387
12745
  e.stopPropagation();
11388
12746
  onAdd();
11389
12747
  },
11390
- children: /* @__PURE__ */ jsx27(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12748
+ children: /* @__PURE__ */ jsx30(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
11391
12749
  }
11392
12750
  )
11393
12751
  }
@@ -11396,7 +12754,7 @@ function NavbarContainerChrome({
11396
12754
 
11397
12755
  // src/ui/drop-indicator.tsx
11398
12756
  import * as React10 from "react";
11399
- import { jsx as jsx28 } from "react/jsx-runtime";
12757
+ import { jsx as jsx31 } from "react/jsx-runtime";
11400
12758
  var dropIndicatorVariants = cva(
11401
12759
  "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
11402
12760
  {
@@ -11420,7 +12778,7 @@ var dropIndicatorVariants = cva(
11420
12778
  );
11421
12779
  var DropIndicator = React10.forwardRef(
11422
12780
  ({ className, direction, state, ...props }, ref) => {
11423
- return /* @__PURE__ */ jsx28(
12781
+ return /* @__PURE__ */ jsx31(
11424
12782
  "div",
11425
12783
  {
11426
12784
  ref,
@@ -11437,7 +12795,7 @@ var DropIndicator = React10.forwardRef(
11437
12795
  DropIndicator.displayName = "DropIndicator";
11438
12796
 
11439
12797
  // src/ui/badge.tsx
11440
- import { jsx as jsx29 } from "react/jsx-runtime";
12798
+ import { jsx as jsx32 } from "react/jsx-runtime";
11441
12799
  var badgeVariants = cva(
11442
12800
  "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",
11443
12801
  {
@@ -11455,12 +12813,12 @@ var badgeVariants = cva(
11455
12813
  }
11456
12814
  );
11457
12815
  function Badge({ className, variant, ...props }) {
11458
- return /* @__PURE__ */ jsx29("div", { className: cn(badgeVariants({ variant }), className), ...props });
12816
+ return /* @__PURE__ */ jsx32("div", { className: cn(badgeVariants({ variant }), className), ...props });
11459
12817
  }
11460
12818
 
11461
12819
  // src/OhhwellsBridge.tsx
11462
- import { Link as Link2 } from "lucide-react";
11463
- import { Fragment as Fragment8, jsx as jsx30, jsxs as jsxs17 } from "react/jsx-runtime";
12820
+ import { Link as Link3 } from "lucide-react";
12821
+ import { Fragment as Fragment8, jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
11464
12822
  var PRIMARY3 = "#0885FE";
11465
12823
  var IMAGE_FADE_MS = 300;
11466
12824
  function runOpacityFade(el, onDone) {
@@ -11554,21 +12912,10 @@ function parseSchedulingInsertAfter(insertAfter) {
11554
12912
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
11555
12913
  };
11556
12914
  }
11557
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
11558
- const parsed = parseSchedulingInsertAfter(insertAfter);
11559
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
11560
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
11561
- return { effectiveInsertAfter, insertBefore };
11562
- }
11563
- function getSchedulingMountPoint(insertAfter) {
11564
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
11565
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
11566
- if (!anchorEl && anchor === "scheduling") {
11567
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
11568
- anchorEl = widgets.at(-1) ?? null;
11569
- }
11570
- if (!anchorEl) return null;
11571
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
12915
+ function resolveEntryAnchor(entry) {
12916
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
12917
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
12918
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
11572
12919
  }
11573
12920
  function schedulingMountDepth(insertAfter) {
11574
12921
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -11585,8 +12932,7 @@ function getPageSchedulingEntries(raw) {
11585
12932
  }
11586
12933
  }
11587
12934
  function isSchedulingWidgetMissing(entry) {
11588
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
11589
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
12935
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
11590
12936
  }
11591
12937
  function hasMissingSchedulingWidgets(entries) {
11592
12938
  return entries.some(isSchedulingWidgetMissing);
@@ -11616,16 +12962,17 @@ function initSectionsFromContent(content, removeExisting = false) {
11616
12962
  } catch {
11617
12963
  }
11618
12964
  }
11619
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
11620
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
11621
- const sectionId = schedulingSectionId(effectiveInsertAfter);
12965
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
12966
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
12967
+ const sectionId = schedulingSectionId(widgetId);
11622
12968
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
11623
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
11624
- if (!mountPoint) return false;
12969
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
12970
+ if (!anchorEl) return false;
12971
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
11625
12972
  const container = document.createElement("div");
11626
12973
  container.dataset.ohwSectionContainer = "scheduling";
11627
- if (insertBefore) {
11628
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
12974
+ if (beforeId) {
12975
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
11629
12976
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
11630
12977
  if (!beforePoint) return false;
11631
12978
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -11636,19 +12983,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
11636
12983
  }
11637
12984
  tail.insertAdjacentElement("afterend", container);
11638
12985
  }
11639
- const root = createRoot2(container);
11640
- flushSync2(() => {
11641
- root.render(
11642
- /* @__PURE__ */ jsx30(
11643
- SchedulingWidget,
11644
- {
11645
- notifyOnConnect,
11646
- initialScheduleId: scheduleId,
11647
- insertAfter: effectiveInsertAfter
11648
- }
11649
- )
11650
- );
11651
- });
12986
+ try {
12987
+ const root = createRoot2(container);
12988
+ flushSync2(() => {
12989
+ root.render(
12990
+ /* @__PURE__ */ jsx33(
12991
+ SchedulingWidget,
12992
+ {
12993
+ notifyOnConnect,
12994
+ initialScheduleId: scheduleId,
12995
+ insertAfter: widgetId
12996
+ }
12997
+ )
12998
+ );
12999
+ });
13000
+ } catch (err) {
13001
+ console.error("[ow:scheduling] render threw", err);
13002
+ container.remove();
13003
+ return false;
13004
+ }
11652
13005
  const tracker = getSectionsTracker();
11653
13006
  let sections = [];
11654
13007
  try {
@@ -11656,10 +13009,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
11656
13009
  } catch {
11657
13010
  }
11658
13011
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
11659
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
13012
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
11660
13013
  sections.push({
11661
13014
  type: "scheduling",
11662
- insertAfter: effectiveInsertAfter,
13015
+ insertAfter: widgetId,
13016
+ anchorId,
13017
+ beforeId: beforeId ?? null,
11663
13018
  pagePath: window.location.pathname,
11664
13019
  ...scheduleId ? { scheduleId } : {}
11665
13020
  });
@@ -11673,7 +13028,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
11673
13028
  for (let i = pending.length - 1; i >= 0; i--) {
11674
13029
  const entry = pending[i];
11675
13030
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
11676
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
13031
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
13032
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
11677
13033
  pending.splice(i, 1);
11678
13034
  }
11679
13035
  }
@@ -11687,6 +13043,17 @@ function applyLinkHref(el, val) {
11687
13043
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
11688
13044
  if (anchor) anchor.setAttribute("href", val);
11689
13045
  }
13046
+ function currentIconRef(el) {
13047
+ const uploaded = el instanceof HTMLImageElement ? el : el.querySelector("img");
13048
+ if (uploaded?.getAttribute("src")) {
13049
+ return uploaded.getAttribute("src") ?? "";
13050
+ }
13051
+ const svg = el instanceof SVGElement ? el : el.querySelector("svg");
13052
+ const named = Array.from(svg?.classList ?? []).find(
13053
+ (c) => c.startsWith("lucide-") && c !== "lucide-icon"
13054
+ );
13055
+ return named ? `lucide:${named.slice("lucide-".length)}` : "";
13056
+ }
11690
13057
  function getEditMeasureEl(editable) {
11691
13058
  return editable.closest("[data-ohw-href-key]") ?? editable;
11692
13059
  }
@@ -11731,8 +13098,11 @@ function isMediaEditable(el) {
11731
13098
  const t = el.dataset.ohwEditable;
11732
13099
  return t === "image" || t === "bg-image" || t === "video";
11733
13100
  }
13101
+ function isIconEditable(el) {
13102
+ return el.dataset.ohwEditable === "icon";
13103
+ }
11734
13104
  var MEDIA_SELECTOR = '[data-ohw-editable="image"], [data-ohw-editable="bg-image"], [data-ohw-editable="video"]';
11735
- var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"])';
13105
+ 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"])';
11736
13106
  function getVideoEl2(el) {
11737
13107
  return el instanceof HTMLVideoElement ? el : el.querySelector("video");
11738
13108
  }
@@ -11803,6 +13173,13 @@ function isInsideLinkEditor(target) {
11803
13173
  target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest("[data-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
11804
13174
  );
11805
13175
  }
13176
+ function isInsideFloatingPanel(target) {
13177
+ return Boolean(target.closest("[data-ohw-floating-panel]"));
13178
+ }
13179
+ function isPointOverFloatingPanel(clientX, clientY) {
13180
+ const el = document.elementFromPoint(clientX, clientY);
13181
+ return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
13182
+ }
11806
13183
  function getHrefKeyFromElement(el) {
11807
13184
  if (!el) return null;
11808
13185
  const anchor = el.closest("[data-ohw-href-key]");
@@ -11850,13 +13227,29 @@ function isNavItemPointerTarget(el) {
11850
13227
  function getNavigationItemAnchor(el) {
11851
13228
  const anchor = el.matches("[data-ohw-href-key]") ? el : el.closest("[data-ohw-href-key]");
11852
13229
  if (!anchor) return null;
11853
- if (!anchor.querySelector('[data-ohw-editable="text"]')) return null;
13230
+ if (!anchor.querySelector('[data-ohw-editable="text"]') && !getSocialItem(anchor)) return null;
11854
13231
  if (!isNavItemPointerTarget(anchor)) return null;
11855
13232
  return anchor;
11856
13233
  }
11857
13234
  function isNavigationItem2(el) {
11858
13235
  return getNavigationItemAnchor(el) !== null;
11859
13236
  }
13237
+ function requestSocialDialog(anchor, post, content) {
13238
+ const item = getSocialItem(anchor);
13239
+ if (!item) return false;
13240
+ const iconKey = item.querySelector('[data-ohw-editable="icon"]')?.dataset.ohwKey ?? "";
13241
+ post({
13242
+ type: "ow:social-pick",
13243
+ hrefKey: item.getAttribute("data-ohw-href-key") ?? "",
13244
+ iconKey,
13245
+ url: getLinkHref4(item),
13246
+ iconStyle: detectIconStyle(item),
13247
+ // What was chosen last time. Guessing from the address instead reads as "Website" for anything
13248
+ // unrecognised, and for an item with no address at all — so a deliberate choice looked lost.
13249
+ platformId: content[socialPlatformKey(iconKey)] ?? ""
13250
+ });
13251
+ return true;
13252
+ }
11860
13253
  function listNavigationItems() {
11861
13254
  return Array.from(
11862
13255
  document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
@@ -11891,7 +13284,7 @@ function getNavigationRoot(el) {
11891
13284
  return el.closest("nav, footer, aside");
11892
13285
  }
11893
13286
  function countFooterNavItems(el) {
11894
- return Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter(isNavigationItem2).length;
13287
+ return Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter((item) => isNavigationItem2(item) && !getSocialItem(item)).length;
11895
13288
  }
11896
13289
  function findFooterItemGroup2(item) {
11897
13290
  const explicit = item.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
@@ -11912,10 +13305,11 @@ function isInferredFooterGroup2(el) {
11912
13305
  const footer = el.closest("footer");
11913
13306
  if (!footer || el === footer) return false;
11914
13307
  if (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column")) return true;
13308
+ if (isSocialsRow(el)) return false;
11915
13309
  return countFooterNavItems(el) >= 2;
11916
13310
  }
11917
13311
  function isNavigationContainer(el) {
11918
- return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el) || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup2(el);
13312
+ return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el) || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isSocialsRow(el) || isInferredFooterGroup2(el);
11919
13313
  }
11920
13314
  function isNavbarLinksContainer2(el) {
11921
13315
  return el.hasAttribute("data-ohw-nav-container");
@@ -11923,6 +13317,11 @@ function isNavbarLinksContainer2(el) {
11923
13317
  function getFooterColumn(el) {
11924
13318
  return el.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
11925
13319
  }
13320
+ function isFooterAddItemDisabled(selected) {
13321
+ if (!selected) return false;
13322
+ const column = resolveFooterColumnForAdd(selected);
13323
+ return column ? !canAddFooterItem(column) : false;
13324
+ }
11926
13325
  function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
11927
13326
  if (getNavigationItemAnchor(target)) return null;
11928
13327
  const column = getFooterColumn(target);
@@ -11998,6 +13397,8 @@ function resolveNavContainerSelectionTarget(target, clientX, clientY) {
11998
13397
  return null;
11999
13398
  }
12000
13399
  function getNavigationSelectionParent(el) {
13400
+ const socialsRow = findSocialsRow(el);
13401
+ if (socialsRow) return socialsRow;
12001
13402
  if (isNavigationItem2(el)) {
12002
13403
  const childrenRoot = el.closest("[data-ohw-nav-children]");
12003
13404
  if (childrenRoot) {
@@ -12016,13 +13417,17 @@ function getNavigationSelectionParent(el) {
12016
13417
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
12017
13418
  return getFooterLinksContainer();
12018
13419
  }
12019
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
13420
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isFooterLinksContainer(el) || isInferredFooterGroup2(el)) {
12020
13421
  return getNavigationRoot(el);
12021
13422
  }
12022
13423
  return null;
12023
13424
  }
12024
13425
  function collectNavigationItemSiblingHintRects(selected) {
12025
13426
  if (!isNavigationItem2(selected)) return [];
13427
+ const socialsRow = findSocialsRow(selected);
13428
+ if (socialsRow) {
13429
+ return listSocialItems(socialsRow).filter((item) => item !== selected).map((item) => item.getBoundingClientRect());
13430
+ }
12026
13431
  const footerColumn = getFooterColumn(selected);
12027
13432
  if (footerColumn) {
12028
13433
  return listFooterLinksInColumn(footerColumn).filter((link) => link !== selected).map((link) => link.getBoundingClientRect());
@@ -12230,6 +13635,9 @@ var ICONS = {
12230
13635
  var SELECTION_CHROME_GAP2 = 4;
12231
13636
  var TOOLBAR_STROKE_GAP2 = 4;
12232
13637
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
13638
+ var HOVER_STROKE_WIDTH = 1.5;
13639
+ var TEXT_HOVER_SIDE_PAD = 4;
13640
+ var CHROME_PRIMARY = `var(--ohw-primary, ${PRIMARY3})`;
12233
13641
  var TOOLBAR_GROUPS = [
12234
13642
  [
12235
13643
  { cmd: "bold", title: "Bold" },
@@ -12255,7 +13663,7 @@ function EditGlowChrome({
12255
13663
  hideHandle = false
12256
13664
  }) {
12257
13665
  const GAP = SELECTION_CHROME_GAP2;
12258
- return /* @__PURE__ */ jsxs17(
13666
+ return /* @__PURE__ */ jsxs20(
12259
13667
  "div",
12260
13668
  {
12261
13669
  ref: elRef,
@@ -12270,7 +13678,7 @@ function EditGlowChrome({
12270
13678
  zIndex: 2147483646
12271
13679
  },
12272
13680
  children: [
12273
- /* @__PURE__ */ jsx30(
13681
+ /* @__PURE__ */ jsx33(
12274
13682
  "div",
12275
13683
  {
12276
13684
  style: {
@@ -12283,7 +13691,7 @@ function EditGlowChrome({
12283
13691
  }
12284
13692
  }
12285
13693
  ),
12286
- reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx30(
13694
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx33(
12287
13695
  "div",
12288
13696
  {
12289
13697
  "data-ohw-drag-handle-container": "",
@@ -12295,7 +13703,7 @@ function EditGlowChrome({
12295
13703
  transform: "translate(calc(-100% - 7px), -50%)",
12296
13704
  pointerEvents: dragDisabled ? "none" : "auto"
12297
13705
  },
12298
- children: /* @__PURE__ */ jsx30(
13706
+ children: /* @__PURE__ */ jsx33(
12299
13707
  DragHandle,
12300
13708
  {
12301
13709
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -12505,7 +13913,7 @@ function FloatingToolbar({
12505
13913
  return () => ro.disconnect();
12506
13914
  }, [showEditLink, activeCommands]);
12507
13915
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
12508
- return /* @__PURE__ */ jsx30(
13916
+ return /* @__PURE__ */ jsx33(
12509
13917
  "div",
12510
13918
  {
12511
13919
  ref: setRefs,
@@ -12517,12 +13925,12 @@ function FloatingToolbar({
12517
13925
  zIndex: 2147483647,
12518
13926
  pointerEvents: "auto"
12519
13927
  },
12520
- children: /* @__PURE__ */ jsxs17(CustomToolbar, { children: [
12521
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs17(React11.Fragment, { children: [
12522
- gi > 0 && /* @__PURE__ */ jsx30(CustomToolbarDivider, {}),
13928
+ children: /* @__PURE__ */ jsxs20(CustomToolbar, { children: [
13929
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs20(React11.Fragment, { children: [
13930
+ gi > 0 && /* @__PURE__ */ jsx33(CustomToolbarDivider, {}),
12523
13931
  btns.map((btn) => {
12524
13932
  const isActive = activeCommands.has(btn.cmd);
12525
- return /* @__PURE__ */ jsx30(
13933
+ return /* @__PURE__ */ jsx33(
12526
13934
  CustomToolbarButton,
12527
13935
  {
12528
13936
  title: btn.title,
@@ -12531,7 +13939,7 @@ function FloatingToolbar({
12531
13939
  e.preventDefault();
12532
13940
  onCommand(btn.cmd);
12533
13941
  },
12534
- children: /* @__PURE__ */ jsx30(
13942
+ children: /* @__PURE__ */ jsx33(
12535
13943
  "svg",
12536
13944
  {
12537
13945
  width: "16",
@@ -12552,7 +13960,7 @@ function FloatingToolbar({
12552
13960
  );
12553
13961
  })
12554
13962
  ] }, gi)),
12555
- showEditLink ? /* @__PURE__ */ jsx30(
13963
+ showEditLink ? /* @__PURE__ */ jsx33(
12556
13964
  CustomToolbarButton,
12557
13965
  {
12558
13966
  type: "button",
@@ -12566,7 +13974,7 @@ function FloatingToolbar({
12566
13974
  e.preventDefault();
12567
13975
  e.stopPropagation();
12568
13976
  },
12569
- children: /* @__PURE__ */ jsx30(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
13977
+ children: /* @__PURE__ */ jsx33(Link3, { className: "size-4 shrink-0", "aria-hidden": true })
12570
13978
  }
12571
13979
  ) : null
12572
13980
  ] })
@@ -12583,7 +13991,7 @@ function StateToggle({
12583
13991
  states,
12584
13992
  onStateChange
12585
13993
  }) {
12586
- return /* @__PURE__ */ jsx30(
13994
+ return /* @__PURE__ */ jsx33(
12587
13995
  ToggleGroup,
12588
13996
  {
12589
13997
  "data-ohw-state-toggle": "",
@@ -12597,11 +14005,12 @@ function StateToggle({
12597
14005
  left: rect.right - 8,
12598
14006
  transform: "translateX(-100%)"
12599
14007
  },
12600
- children: states.map((state) => /* @__PURE__ */ jsx30(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
14008
+ children: states.map((state) => /* @__PURE__ */ jsx33(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
12601
14009
  }
12602
14010
  );
12603
14011
  }
12604
14012
  var contentCache = /* @__PURE__ */ new Map();
14013
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
12605
14014
  function resolveSubdomain(subdomainFromQuery) {
12606
14015
  if (subdomainFromQuery) return subdomainFromQuery;
12607
14016
  if (typeof window !== "undefined") {
@@ -12624,8 +14033,8 @@ function OhhwellsBridge() {
12624
14033
  const router = useRouter3();
12625
14034
  const searchParams = useSearchParams();
12626
14035
  const isEditMode = isEditSessionActive();
12627
- const [bridgeRoot, setBridgeRoot] = useState10(null);
12628
- useEffect11(() => {
14036
+ const [bridgeRoot, setBridgeRoot] = useState11(null);
14037
+ useEffect12(() => {
12629
14038
  const figtreeFontId = "ohw-figtree-font";
12630
14039
  if (!document.getElementById(figtreeFontId)) {
12631
14040
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -12654,110 +14063,159 @@ function OhhwellsBridge() {
12654
14063
  const subdomain = resolveSubdomain(subdomainFromQuery);
12655
14064
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
12656
14065
  useSavedLinkNavigation(isEditMode);
12657
- const postToParent2 = useCallback6((data) => {
14066
+ const postToParent2 = useCallback7((data) => {
12658
14067
  if (typeof window !== "undefined" && window.parent !== window) {
12659
14068
  window.parent.postMessage(data, "*");
12660
14069
  }
12661
14070
  }, []);
12662
- const [fetchState, setFetchState] = useState10("idle");
12663
- const autoSaveTimers = useRef8(/* @__PURE__ */ new Map());
12664
- const activeElRef = useRef8(null);
12665
- const pointerHeldRef = useRef8(false);
12666
- const selectedElRef = useRef8(null);
12667
- const selectedHrefKeyRef = useRef8(null);
12668
- const selectedFooterColAttrRef = useRef8(null);
12669
- const originalContentRef = useRef8(null);
12670
- const activeStateElRef = useRef8(null);
12671
- const parentScrollRef = useRef8(null);
12672
- const visibleViewportRef = useRef8(null);
12673
- const [dialogPortalContainer, setDialogPortalContainer] = useState10(null);
12674
- const attachVisibleViewport = useCallback6((node) => {
14071
+ const [fetchState, setFetchState] = useState11("idle");
14072
+ const autoSaveTimers = useRef9(/* @__PURE__ */ new Map());
14073
+ const activeElRef = useRef9(null);
14074
+ const pointerHeldRef = useRef9(false);
14075
+ const selectedElRef = useRef9(null);
14076
+ const selectedHrefKeyRef = useRef9(null);
14077
+ const selectedFooterColAttrRef = useRef9(null);
14078
+ const originalContentRef = useRef9(null);
14079
+ const activeStateElRef = useRef9(null);
14080
+ const parentScrollRef = useRef9(null);
14081
+ const visibleViewportRef = useRef9(null);
14082
+ const [dialogPortalContainer, setDialogPortalContainer] = useState11(null);
14083
+ const attachVisibleViewport = useCallback7((node) => {
12675
14084
  visibleViewportRef.current = node;
12676
14085
  setDialogPortalContainer(node);
12677
14086
  if (node) applyVisibleViewport(node, parentScrollRef.current);
12678
14087
  }, []);
12679
- const toolbarElRef = useRef8(null);
12680
- const glowElRef = useRef8(null);
12681
- const hoveredImageRef = useRef8(null);
12682
- const hoveredImageHasTextOverlapRef = useRef8(false);
12683
- const dragOverElRef = useRef8(null);
12684
- const [mediaHover, setMediaHover] = useState10(null);
12685
- const [carouselHover, setCarouselHover] = useState10(null);
12686
- const [uploadingRects, setUploadingRects] = useState10({});
12687
- const hoveredGapRef = useRef8(null);
12688
- const imageUnhoverTimerRef = useRef8(null);
12689
- const imageShowTimerRef = useRef8(null);
12690
- const editStylesRef = useRef8(null);
12691
- const activateRef = useRef8(() => {
14088
+ const toolbarElRef = useRef9(null);
14089
+ const glowElRef = useRef9(null);
14090
+ const hoveredImageRef = useRef9(null);
14091
+ const hoveredImageHasTextOverlapRef = useRef9(false);
14092
+ const dragOverElRef = useRef9(null);
14093
+ const [mediaHover, setMediaHover] = useState11(null);
14094
+ const [carouselHover, setCarouselHover] = useState11(null);
14095
+ const [uploadingRects, setUploadingRects] = useState11({});
14096
+ const hoveredGapRef = useRef9(null);
14097
+ const imageUnhoverTimerRef = useRef9(null);
14098
+ const imageShowTimerRef = useRef9(null);
14099
+ const editStylesRef = useRef9(null);
14100
+ const activateRef = useRef9(() => {
14101
+ });
14102
+ const deactivateRef = useRef9(() => {
14103
+ });
14104
+ const selectRef = useRef9(() => {
14105
+ });
14106
+ const selectFrameRef = useRef9(() => {
12692
14107
  });
12693
- const deactivateRef = useRef8(() => {
14108
+ const selectLogoRef = useRef9(() => {
12694
14109
  });
12695
- const selectRef = useRef8(() => {
14110
+ const openLogoSizePanelRef = useRef9(() => {
12696
14111
  });
12697
- const selectFrameRef = useRef8(() => {
14112
+ const deselectRef = useRef9(() => {
12698
14113
  });
12699
- const deselectRef = useRef8(() => {
14114
+ const closeFloatingPanelOnlyRef = useRef9(() => {
12700
14115
  });
12701
- const reselectNavigationItemRef = useRef8(() => {
14116
+ const reselectNavigationItemRef = useRef9(() => {
12702
14117
  });
12703
- const commitNavigationTextEditRef = useRef8(() => {
14118
+ const commitNavigationTextEditRef = useRef9(() => {
12704
14119
  });
12705
- const handleDeleteSelectedRef = useRef8(() => false);
12706
- const runPendingDeleteUndoRef = useRef8(() => false);
12707
- const isFooterFrameSelectionRef = useRef8(false);
12708
- const refreshActiveCommandsRef = useRef8(() => {
14120
+ const handleDeleteSelectedRef = useRef9(() => false);
14121
+ const runPendingDeleteUndoRef = useRef9(() => false);
14122
+ const isFooterFrameSelectionRef = useRef9(false);
14123
+ const refreshActiveCommandsRef = useRef9(() => {
12709
14124
  });
12710
- const postToParentRef = useRef8(postToParent2);
14125
+ const postToParentRef = useRef9(postToParent2);
12711
14126
  postToParentRef.current = postToParent2;
12712
- const aiSectionApiRef = useRef8(null);
12713
- const sectionsLoadedRef = useRef8(false);
12714
- const pendingScheduleConfigRequests = useRef8([]);
12715
- const [toolbarRect, setToolbarRect] = useState10(null);
12716
- const [toolbarVariant, setToolbarVariant] = useState10("none");
12717
- const toolbarVariantRef = useRef8("none");
14127
+ const aiSectionApiRef = useRef9(null);
14128
+ const sectionsLoadedRef = useRef9(false);
14129
+ const pendingScheduleConfigRequests = useRef9([]);
14130
+ const [toolbarRect, setToolbarRect] = useState11(null);
14131
+ const [toolbarVariant, setToolbarVariant] = useState11("none");
14132
+ const toolbarVariantRef = useRef9("none");
12718
14133
  toolbarVariantRef.current = toolbarVariant;
12719
- const [selectedIsCta, setSelectedIsCta] = useState10(false);
12720
- const [reorderHrefKey, setReorderHrefKey] = useState10(null);
12721
- const [reorderDragDisabled, setReorderDragDisabled] = useState10(false);
12722
- const [toggleState, setToggleState] = useState10(null);
12723
- const [maxBadge, setMaxBadge] = useState10(null);
12724
- const [activeCommands, setActiveCommands] = useState10(/* @__PURE__ */ new Set());
12725
- const [sectionGap, setSectionGap] = useState10(null);
12726
- const [toolbarShowEditLink, setToolbarShowEditLink] = useState10(false);
12727
- const hoveredNavContainerRef = useRef8(null);
12728
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState10(null);
12729
- const hoveredItemElRef = useRef8(null);
12730
- const [hoveredItemRect, setHoveredItemRect] = useState10(null);
12731
- const siblingHintElRef = useRef8(null);
12732
- const [siblingHintRect, setSiblingHintRect] = useState10(null);
12733
- const [siblingHintRects, setSiblingHintRects] = useState10([]);
12734
- const [isItemDragging, setIsItemDragging] = useState10(false);
12735
- const [isFooterFrameSelection, setIsFooterFrameSelection] = useState10(false);
14134
+ const [selectedIsCta, setSelectedIsCta] = useState11(false);
14135
+ const [selectedIsSocial, setSelectedIsSocial] = useState11(false);
14136
+ const [selectedIsSocialsRow, setSelectedIsSocialsRow] = useState11(false);
14137
+ const [reorderHrefKey, setReorderHrefKey] = useState11(null);
14138
+ const [reorderDragDisabled, setReorderDragDisabled] = useState11(false);
14139
+ const [toggleState, setToggleState] = useState11(null);
14140
+ const [maxBadge, setMaxBadge] = useState11(null);
14141
+ const [activeCommands, setActiveCommands] = useState11(/* @__PURE__ */ new Set());
14142
+ const [sectionGap, setSectionGap] = useState11(null);
14143
+ const [toolbarShowEditLink, setToolbarShowEditLink] = useState11(false);
14144
+ const hoveredNavContainerRef = useRef9(null);
14145
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState11(null);
14146
+ const hoveredItemElRef = useRef9(null);
14147
+ const [hoveredItemRect, setHoveredItemRect] = useState11(null);
14148
+ const [hoveredTextRect, setHoveredTextRect] = useState11(null);
14149
+ useEffect12(() => {
14150
+ const sync = () => {
14151
+ const el = document.querySelector(
14152
+ "[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key])"
14153
+ );
14154
+ const target = el && !el.closest("[data-ohw-href-key]") ? el : null;
14155
+ if (!target) {
14156
+ setHoveredTextRect(null);
14157
+ return;
14158
+ }
14159
+ const r2 = target.getBoundingClientRect();
14160
+ setHoveredTextRect(new DOMRect(r2.x - TEXT_HOVER_SIDE_PAD, r2.y, r2.width + TEXT_HOVER_SIDE_PAD * 2, r2.height));
14161
+ };
14162
+ const observer = new MutationObserver(sync);
14163
+ observer.observe(document.documentElement, {
14164
+ attributes: true,
14165
+ attributeFilter: ["data-ohw-hovered"],
14166
+ subtree: true
14167
+ });
14168
+ return () => observer.disconnect();
14169
+ }, []);
14170
+ const siblingHintElRef = useRef9(null);
14171
+ const [siblingHintRect, setSiblingHintRect] = useState11(null);
14172
+ const [siblingHintRects, setSiblingHintRects] = useState11([]);
14173
+ const [isItemDragging, setIsItemDragging] = useState11(false);
14174
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = useState11(false);
12736
14175
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
12737
- const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState10(null);
12738
- const [footerHeadingVisible, setFooterHeadingVisible] = useState10(null);
12739
- const footerDragRef = useRef8(null);
12740
- const [footerDropSlots, setFooterDropSlots] = useState10([]);
12741
- const [activeFooterDropIndex, setActiveFooterDropIndex] = useState10(null);
12742
- const [draggedItemRect, setDraggedItemRect] = useState10(null);
12743
- const footerPointerDragRef = useRef8(null);
12744
- const suppressNextClickRef = useRef8(false);
12745
- const suppressClickUntilRef = useRef8(0);
12746
- const [linkPopover, setLinkPopover] = useState10(null);
12747
- const linkPopoverSessionRef = useRef8(null);
12748
- const addNavAfterAnchorRef = useRef8(null);
12749
- const editContentRef = useRef8({});
12750
- const aiSectionsRef = useRef8("");
12751
- const pendingDeleteUndoRef = useRef8(null);
12752
- const [sitePages, setSitePages] = useState10([]);
12753
- const [sectionsByPath, setSectionsByPath] = useState10({});
12754
- const sectionsPrefetchGenRef = useRef8(0);
12755
- const setLinkPopoverRef = useRef8(setLinkPopover);
12756
- const linkPopoverPanelRef = useRef8(null);
12757
- const linkPopoverOpenRef = useRef8(false);
12758
- const linkPopoverGraceUntilRef = useRef8(0);
14176
+ const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState11(null);
14177
+ const [footerHeadingVisible, setFooterHeadingVisible] = useState11(null);
14178
+ const footerDragRef = useRef9(null);
14179
+ const [footerDropSlots, setFooterDropSlots] = useState11([]);
14180
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = useState11(null);
14181
+ const [draggedItemRect, setDraggedItemRect] = useState11(null);
14182
+ const footerPointerDragRef = useRef9(null);
14183
+ const suppressNextClickRef = useRef9(false);
14184
+ const suppressClickUntilRef = useRef9(0);
14185
+ const [linkPopover, setLinkPopover] = useState11(null);
14186
+ const linkPopoverSessionRef = useRef9(null);
14187
+ const addNavAfterAnchorRef = useRef9(null);
14188
+ const editContentRef = useRef9({});
14189
+ const aiSectionsRef = useRef9("");
14190
+ const brandKitRef = useRef9("");
14191
+ const pendingDeleteUndoRef = useRef9(null);
14192
+ const [floatingPanel, setFloatingPanel] = useState11(null);
14193
+ const floatingPanelOpenRef = useRef9(false);
14194
+ const setFloatingPanelRef = useRef9(setFloatingPanel);
14195
+ const [floatingPanelPos, setFloatingPanelPos] = useState11(null);
14196
+ const [logoSizeDraft, setLogoSizeDraft] = useState11(null);
14197
+ const [editorViewport, setEditorViewport] = useState11("desktop");
14198
+ const [parentScrollSnap, setParentScrollSnap] = useState11(null);
14199
+ const [sitePages, setSitePages] = useState11([]);
14200
+ const [sectionsByPath, setSectionsByPath] = useState11({});
14201
+ const sectionsPrefetchGenRef = useRef9(0);
14202
+ const setLinkPopoverRef = useRef9(setLinkPopover);
14203
+ const linkPopoverPanelRef = useRef9(null);
14204
+ const linkPopoverOpenRef = useRef9(false);
14205
+ const linkPopoverGraceUntilRef = useRef9(0);
12759
14206
  setLinkPopoverRef.current = setLinkPopover;
14207
+ setFloatingPanelRef.current = setFloatingPanel;
12760
14208
  linkPopoverSessionRef.current = linkPopover;
14209
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
14210
+ useEffect12(() => {
14211
+ const syncViewport = () => {
14212
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
14213
+ setEditorViewport((prev) => prev === next ? prev : next);
14214
+ };
14215
+ syncViewport();
14216
+ window.addEventListener("resize", syncViewport);
14217
+ return () => window.removeEventListener("resize", syncViewport);
14218
+ }, []);
12761
14219
  const {
12762
14220
  navDragRef,
12763
14221
  navDropSlots,
@@ -12793,7 +14251,7 @@ function OhhwellsBridge() {
12793
14251
  const bumpLinkPopoverGrace = () => {
12794
14252
  linkPopoverGraceUntilRef.current = Date.now() + 350;
12795
14253
  };
12796
- const runSectionsPrefetch = useCallback6((pages) => {
14254
+ const runSectionsPrefetch = useCallback7((pages) => {
12797
14255
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
12798
14256
  const gen = ++sectionsPrefetchGenRef.current;
12799
14257
  const paths = pages.map((p) => p.path);
@@ -12812,9 +14270,9 @@ function OhhwellsBridge() {
12812
14270
  );
12813
14271
  });
12814
14272
  }, [isEditMode, pathname]);
12815
- const runSectionsPrefetchRef = useRef8(runSectionsPrefetch);
14273
+ const runSectionsPrefetchRef = useRef9(runSectionsPrefetch);
12816
14274
  runSectionsPrefetchRef.current = runSectionsPrefetch;
12817
- useEffect11(() => {
14275
+ useEffect12(() => {
12818
14276
  if (!linkPopover) {
12819
14277
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
12820
14278
  return;
@@ -12842,7 +14300,7 @@ function OhhwellsBridge() {
12842
14300
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
12843
14301
  };
12844
14302
  }, [linkPopover, postToParent2]);
12845
- useEffect11(() => {
14303
+ useEffect12(() => {
12846
14304
  if (!isEditMode) return;
12847
14305
  const useFixtures = shouldUseDevFixtures();
12848
14306
  if (useFixtures) {
@@ -12866,14 +14324,14 @@ function OhhwellsBridge() {
12866
14324
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
12867
14325
  return () => window.removeEventListener("message", onSitePages);
12868
14326
  }, [isEditMode, postToParent2]);
12869
- useEffect11(() => {
14327
+ useEffect12(() => {
12870
14328
  if (!isEditMode || shouldUseDevFixtures()) return;
12871
14329
  void loadAllSectionsManifest().then((manifest) => {
12872
14330
  if (Object.keys(manifest).length === 0) return;
12873
14331
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
12874
14332
  });
12875
14333
  }, [isEditMode]);
12876
- useEffect11(() => {
14334
+ useEffect12(() => {
12877
14335
  const update = () => {
12878
14336
  const el = activeElRef.current ?? selectedElRef.current;
12879
14337
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -12897,10 +14355,10 @@ function OhhwellsBridge() {
12897
14355
  vvp.removeEventListener("resize", update);
12898
14356
  };
12899
14357
  }, []);
12900
- const refreshStateRules = useCallback6(() => {
14358
+ const refreshStateRules = useCallback7(() => {
12901
14359
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
12902
14360
  }, []);
12903
- const processConfigRequest = useCallback6((insertAfterVal) => {
14361
+ const processConfigRequest = useCallback7((insertAfterVal) => {
12904
14362
  const tracker = getSectionsTracker();
12905
14363
  let entries = [];
12906
14364
  try {
@@ -12923,7 +14381,7 @@ function OhhwellsBridge() {
12923
14381
  }
12924
14382
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
12925
14383
  }, [isEditMode]);
12926
- const deactivate = useCallback6(() => {
14384
+ const deactivate = useCallback7(() => {
12927
14385
  const el = activeElRef.current;
12928
14386
  if (!el) return;
12929
14387
  const key = el.dataset.ohwKey;
@@ -12956,17 +14414,19 @@ function OhhwellsBridge() {
12956
14414
  setToolbarShowEditLink(false);
12957
14415
  postToParent2({ type: "ow:exit-edit" });
12958
14416
  }, [postToParent2]);
12959
- const clearSelectedAttr = useCallback6(() => {
14417
+ const clearSelectedAttr = useCallback7(() => {
12960
14418
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
12961
14419
  el.removeAttribute("data-ohw-selected");
12962
14420
  });
12963
14421
  }, []);
12964
- const deselect = useCallback6(() => {
14422
+ const deselect = useCallback7(() => {
12965
14423
  clearSelectedAttr();
12966
14424
  selectedElRef.current = null;
12967
14425
  selectedHrefKeyRef.current = null;
12968
14426
  selectedFooterColAttrRef.current = null;
12969
14427
  setSelectedIsCta(false);
14428
+ setSelectedIsSocial(false);
14429
+ setSelectedIsSocialsRow(false);
12970
14430
  setReorderHrefKey(null);
12971
14431
  setReorderDragDisabled(false);
12972
14432
  setIsFooterFrameSelection(false);
@@ -12978,17 +14438,21 @@ function OhhwellsBridge() {
12978
14438
  setIsItemDragging(false);
12979
14439
  hoveredNavContainerRef.current = null;
12980
14440
  setHoveredNavContainerRect(null);
14441
+ hoveredItemElRef.current = null;
14442
+ setHoveredItemRect(null);
14443
+ setFloatingPanel(null);
14444
+ setLogoSizeDraft(null);
12981
14445
  if (!activeElRef.current) {
12982
14446
  setNavGroupForceOpen(null, false);
12983
14447
  setToolbarRect(null);
12984
14448
  setToolbarVariant("none");
12985
14449
  }
12986
14450
  }, [clearSelectedAttr]);
12987
- const markSelected = useCallback6((el) => {
14451
+ const markSelected = useCallback7((el) => {
12988
14452
  clearSelectedAttr();
12989
14453
  el.setAttribute("data-ohw-selected", "");
12990
14454
  }, [clearSelectedAttr]);
12991
- const resolveHrefKeyElement = useCallback6((hrefKey) => {
14455
+ const resolveHrefKeyElement = useCallback7((hrefKey) => {
12992
14456
  if (isFooterHrefKey(hrefKey)) {
12993
14457
  return document.querySelector(
12994
14458
  `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
@@ -13003,7 +14467,7 @@ function OhhwellsBridge() {
13003
14467
  `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
13004
14468
  );
13005
14469
  }, []);
13006
- const resyncSelectedNavigationItem = useCallback6(() => {
14470
+ const resyncSelectedNavigationItem = useCallback7(() => {
13007
14471
  const hrefKey = selectedHrefKeyRef.current;
13008
14472
  if (hrefKey) {
13009
14473
  const link = resolveHrefKeyElement(hrefKey);
@@ -13041,12 +14505,14 @@ function OhhwellsBridge() {
13041
14505
  );
13042
14506
  }
13043
14507
  }, [resolveHrefKeyElement]);
13044
- const reselectNavigationItem = useCallback6((navAnchor) => {
14508
+ const reselectNavigationItem = useCallback7((navAnchor) => {
13045
14509
  selectedElRef.current = navAnchor;
13046
14510
  selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
13047
14511
  selectedFooterColAttrRef.current = null;
13048
14512
  markSelected(navAnchor);
13049
14513
  setSelectedIsCta(isCtaButton(navAnchor));
14514
+ setSelectedIsSocial(Boolean(getSocialItem(navAnchor)));
14515
+ setSelectedIsSocialsRow(false);
13050
14516
  const isDropdownTrigger = !isNestedNavChild(navAnchor) && (navItemHasDropdownChildren(navAnchor) || navItemOwnsDropdownPanel(navAnchor));
13051
14517
  if (isNestedNavChild(navAnchor)) {
13052
14518
  setNavGroupForceOpen(navAnchor, true);
@@ -13070,7 +14536,7 @@ function OhhwellsBridge() {
13070
14536
  setToolbarShowEditLink(false);
13071
14537
  setActiveCommands(/* @__PURE__ */ new Set());
13072
14538
  }, [markSelected]);
13073
- const commitNavigationTextEdit = useCallback6((navAnchor) => {
14539
+ const commitNavigationTextEdit = useCallback7((navAnchor) => {
13074
14540
  const el = activeElRef.current;
13075
14541
  if (!el) return;
13076
14542
  const key = el.dataset.ohwKey;
@@ -13097,7 +14563,7 @@ function OhhwellsBridge() {
13097
14563
  postToParent2({ type: "ow:exit-edit" });
13098
14564
  reselectNavigationItem(navAnchor);
13099
14565
  }, [postToParent2, reselectNavigationItem]);
13100
- const handleAddTopLevelNavItem = useCallback6(() => {
14566
+ const handleAddTopLevelNavItem = useCallback7(() => {
13101
14567
  const items = listNavbarRootItems();
13102
14568
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
13103
14569
  deselectRef.current();
@@ -13109,7 +14575,7 @@ function OhhwellsBridge() {
13109
14575
  intent: "add-nav"
13110
14576
  });
13111
14577
  }, []);
13112
- const maybeWarnNavLinkDropdownConflict = useCallback6(
14578
+ const maybeWarnNavLinkDropdownConflict = useCallback7(
13113
14579
  (anchor) => {
13114
14580
  if (!isNavbarHrefKey(anchor.getAttribute("data-ohw-href-key"))) return;
13115
14581
  if (!navDropdownsOpenOnClick()) return;
@@ -13122,7 +14588,7 @@ function OhhwellsBridge() {
13122
14588
  },
13123
14589
  [postToParent2]
13124
14590
  );
13125
- const handleNavDropdownOpenChange = useCallback6((open) => {
14591
+ const handleNavDropdownOpenChange = useCallback7((open) => {
13126
14592
  const selected = selectedElRef.current;
13127
14593
  if (!selected || !isNavigationItem2(selected)) return;
13128
14594
  setNavGroupForceOpen(selected, open);
@@ -13134,7 +14600,7 @@ function OhhwellsBridge() {
13134
14600
  }
13135
14601
  });
13136
14602
  }, []);
13137
- const handleFooterHeadingVisibleChange = useCallback6(
14603
+ const handleFooterHeadingVisibleChange = useCallback7(
13138
14604
  (visible) => {
13139
14605
  const selected = selectedElRef.current;
13140
14606
  if (!selected || !isFooterFrameSelectionRef.current) return;
@@ -13158,7 +14624,7 @@ function OhhwellsBridge() {
13158
14624
  },
13159
14625
  [postToParent2]
13160
14626
  );
13161
- const enterEditOnNewItem = useCallback6((anchor) => {
14627
+ const enterEditOnNewItem = useCallback7((anchor) => {
13162
14628
  const label = anchor.querySelector('[data-ohw-editable="text"]');
13163
14629
  if (!label) {
13164
14630
  selectRef.current(anchor);
@@ -13167,14 +14633,42 @@ function OhhwellsBridge() {
13167
14633
  setNavGroupForceOpen(anchor, true);
13168
14634
  activateRef.current(label);
13169
14635
  }, []);
13170
- const handleAddChildItem = useCallback6(() => {
14636
+ const handleAddChildItem = useCallback7(() => {
13171
14637
  const selected = selectedElRef.current;
13172
14638
  if (!selected) return;
14639
+ const socialsRow = isSocialsRow(selected) ? selected : findSocialsRow(selected);
14640
+ if (socialsRow) {
14641
+ const after = getSocialItem(selected);
14642
+ const result2 = insertSocialItem(socialsRow, after, editContentRef.current);
14643
+ if (!result2) return;
14644
+ const orderJson = JSON.stringify(result2.order);
14645
+ applySocialsDisplayToRow(socialsRow, socialsDisplayFor(socialsRow, editContentRef.current));
14646
+ editContentRef.current = { ...editContentRef.current, [SOCIALS_ORDER_KEY]: orderJson };
14647
+ postToParent2({ type: "ow:change", nodes: [{ key: SOCIALS_ORDER_KEY, text: orderJson }] });
14648
+ postToParentRef.current({
14649
+ type: "ow:social-pick",
14650
+ hrefKey: result2.hrefKey,
14651
+ iconKey: result2.iconKey,
14652
+ url: "",
14653
+ iconStyle: detectIconStyle(result2.item),
14654
+ platformId: "",
14655
+ // Lets the editor undo the insert if the dialog is dismissed: an item that was never given
14656
+ // an address should not survive a Cancel.
14657
+ isNew: true
14658
+ });
14659
+ return;
14660
+ }
13173
14661
  if (toolbarVariantRef.current === "select-frame" && isFooterFrameSelection) {
13174
- if (!selected.hasAttribute("data-ohw-footer-col") && !selected.closest("[data-ohw-footer-col]")) {
13175
- }
13176
- const column = (selected.hasAttribute("data-ohw-footer-col") ? selected : null) ?? selected.closest("[data-ohw-footer-col]") ?? (listFooterColumns().includes(selected) ? selected : null);
14662
+ const column = resolveFooterColumnForAdd(selected);
13177
14663
  if (!column) return;
14664
+ if (!canAddFooterItem(column)) {
14665
+ postToParent2({
14666
+ type: "ow:toast",
14667
+ title: `Maximum ${MAX_FOOTER_ITEMS_PER_COLUMN} items per column`,
14668
+ toastType: "error"
14669
+ });
14670
+ return;
14671
+ }
13178
14672
  const result2 = insertFooterItem(column, "/", "New link", null);
13179
14673
  applyLinkByKey(result2.hrefKey, result2.href);
13180
14674
  document.querySelectorAll(`[data-ohw-key="${result2.labelKey}"]`).forEach((el) => {
@@ -13243,7 +14737,7 @@ function OhhwellsBridge() {
13243
14737
  enterEditOnNewItem(result.anchor);
13244
14738
  });
13245
14739
  }, [enterEditOnNewItem, isFooterFrameSelection, maybeWarnNavLinkDropdownConflict, postToParent2]);
13246
- const handleAddFooterColumn = useCallback6(() => {
14740
+ const handleAddFooterColumn = useCallback7(() => {
13247
14741
  if (!canAddFooterColumn()) {
13248
14742
  postToParent2({
13249
14743
  type: "ow:toast",
@@ -13264,7 +14758,7 @@ function OhhwellsBridge() {
13264
14758
  selectRef.current(result.firstLink);
13265
14759
  });
13266
14760
  }, [postToParent2]);
13267
- const clearFooterDragVisuals = useCallback6(() => {
14761
+ const clearFooterDragVisuals = useCallback7(() => {
13268
14762
  footerDragRef.current = null;
13269
14763
  setSiblingHintRects([]);
13270
14764
  setFooterDropSlots([]);
@@ -13273,7 +14767,7 @@ function OhhwellsBridge() {
13273
14767
  setIsItemDragging(false);
13274
14768
  unlockFooterDragInteraction();
13275
14769
  }, []);
13276
- const refreshFooterDragVisuals = useCallback6((session, activeSlot, clientX, clientY) => {
14770
+ const refreshFooterDragVisuals = useCallback7((session, activeSlot, clientX, clientY) => {
13277
14771
  const dragged = session.draggedEl;
13278
14772
  setDraggedItemRect(dragged.getBoundingClientRect());
13279
14773
  if (typeof clientX === "number" && typeof clientY === "number") {
@@ -13282,6 +14776,13 @@ function OhhwellsBridge() {
13282
14776
  }
13283
14777
  session.activeSlot = activeSlot;
13284
14778
  setSiblingHintRects([]);
14779
+ if (session.kind === "social") {
14780
+ const slots2 = session.hrefKey ? buildSocialDropSlotsForKey(session.hrefKey) : [];
14781
+ setFooterDropSlots(slots2);
14782
+ const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
14783
+ setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
14784
+ return;
14785
+ }
13285
14786
  if (session.kind === "link") {
13286
14787
  const columns = listFooterColumns();
13287
14788
  const slots2 = [];
@@ -13298,13 +14799,13 @@ function OhhwellsBridge() {
13298
14799
  const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
13299
14800
  setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
13300
14801
  }, []);
13301
- const refreshFooterDragVisualsRef = useRef8(refreshFooterDragVisuals);
14802
+ const refreshFooterDragVisualsRef = useRef9(refreshFooterDragVisuals);
13302
14803
  refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
13303
- const commitFooterDragRef = useRef8(() => {
14804
+ const commitFooterDragRef = useRef9(() => {
13304
14805
  });
13305
- const beginFooterDragRef = useRef8(() => {
14806
+ const beginFooterDragRef = useRef9(() => {
13306
14807
  });
13307
- const beginFooterDrag = useCallback6(
14808
+ const beginFooterDrag = useCallback7(
13308
14809
  (session) => {
13309
14810
  const rect = session.draggedEl.getBoundingClientRect();
13310
14811
  session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
@@ -13318,13 +14819,13 @@ function OhhwellsBridge() {
13318
14819
  if (session.wasSelected && selectedElRef.current === session.draggedEl) {
13319
14820
  setToolbarRect(rect);
13320
14821
  }
13321
- const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
14822
+ const initialSlot = session.kind === "social" && session.hrefKey ? hitTestSocialDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
13322
14823
  refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
13323
14824
  },
13324
14825
  [refreshFooterDragVisuals]
13325
14826
  );
13326
14827
  beginFooterDragRef.current = beginFooterDrag;
13327
- const commitFooterDrag = useCallback6(
14828
+ const commitFooterDrag = useCallback7(
13328
14829
  (clientX, clientY) => {
13329
14830
  const session = footerDragRef.current;
13330
14831
  if (!session) {
@@ -13334,8 +14835,11 @@ function OhhwellsBridge() {
13334
14835
  const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
13335
14836
  const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
13336
14837
  let nextOrder = null;
13337
- const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
13338
- if (session.kind === "link" && session.hrefKey && slot) {
14838
+ let nextSocialsOrder = null;
14839
+ const slot = session.activeSlot ?? (session.kind === "social" && session.hrefKey ? hitTestSocialDropSlot(x, y, session.hrefKey) : session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
14840
+ if (session.kind === "social" && session.hrefKey && slot) {
14841
+ nextSocialsOrder = planSocialMove(session.hrefKey, slot.insertIndex);
14842
+ } else if (session.kind === "link" && session.hrefKey && slot) {
13339
14843
  nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
13340
14844
  } else if (session.kind === "column" && slot) {
13341
14845
  nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
@@ -13393,6 +14897,27 @@ function OhhwellsBridge() {
13393
14897
  }
13394
14898
  deselectRef.current();
13395
14899
  };
14900
+ if (nextSocialsOrder) {
14901
+ const orderJson = JSON.stringify(nextSocialsOrder);
14902
+ editContentRef.current = { ...editContentRef.current, [SOCIALS_ORDER_KEY]: orderJson };
14903
+ applySocialsOrder(nextSocialsOrder);
14904
+ postToParentRef.current({
14905
+ type: "ow:change",
14906
+ nodes: [{ key: SOCIALS_ORDER_KEY, text: orderJson }]
14907
+ });
14908
+ applySelectionAfterDrop();
14909
+ clearFooterDragVisuals();
14910
+ const reapply = nextSocialsOrder;
14911
+ requestAnimationFrame(() => {
14912
+ if (editContentRef.current[SOCIALS_ORDER_KEY] === orderJson) applySocialsOrder(reapply);
14913
+ applySelectionAfterDrop();
14914
+ requestAnimationFrame(() => {
14915
+ if (editContentRef.current[SOCIALS_ORDER_KEY] === orderJson) applySocialsOrder(reapply);
14916
+ resyncSelectedNavigationItem();
14917
+ });
14918
+ });
14919
+ return;
14920
+ }
13396
14921
  if (nextOrder) {
13397
14922
  const orderJson = JSON.stringify(nextOrder);
13398
14923
  editContentRef.current = {
@@ -13428,10 +14953,25 @@ function OhhwellsBridge() {
13428
14953
  [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
13429
14954
  );
13430
14955
  commitFooterDragRef.current = commitFooterDrag;
13431
- const startFooterLinkDrag = useCallback6(
14956
+ const startFooterLinkDrag = useCallback7(
13432
14957
  (anchor, clientX, clientY, wasSelected) => {
13433
14958
  const hrefKey = anchor.getAttribute("data-ohw-href-key");
13434
- if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
14959
+ if (!hrefKey) return false;
14960
+ if (getSocialItem(anchor)) {
14961
+ beginFooterDrag({
14962
+ kind: "social",
14963
+ hrefKey,
14964
+ columnEl: null,
14965
+ sourceColumnIndex: 0,
14966
+ wasSelected,
14967
+ draggedEl: anchor,
14968
+ lastClientX: clientX,
14969
+ lastClientY: clientY,
14970
+ activeSlot: null
14971
+ });
14972
+ return true;
14973
+ }
14974
+ if (!isFooterHrefKey(hrefKey)) return false;
13435
14975
  const column = findFooterColumnForLink(anchor);
13436
14976
  const columns = listFooterColumns();
13437
14977
  beginFooterDrag({
@@ -13449,7 +14989,7 @@ function OhhwellsBridge() {
13449
14989
  },
13450
14990
  [beginFooterDrag]
13451
14991
  );
13452
- const startFooterColumnDrag = useCallback6(
14992
+ const startFooterColumnDrag = useCallback7(
13453
14993
  (columnEl, clientX, clientY, wasSelected) => {
13454
14994
  const columns = listFooterColumns();
13455
14995
  const idx = columns.indexOf(columnEl);
@@ -13469,7 +15009,7 @@ function OhhwellsBridge() {
13469
15009
  },
13470
15010
  [beginFooterDrag]
13471
15011
  );
13472
- const handleItemDragStart = useCallback6(
15012
+ const handleItemDragStart = useCallback7(
13473
15013
  (e) => {
13474
15014
  const selected = selectedElRef.current;
13475
15015
  if (!selected) {
@@ -13489,7 +15029,7 @@ function OhhwellsBridge() {
13489
15029
  },
13490
15030
  [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
13491
15031
  );
13492
- const handleItemDragEnd = useCallback6(
15032
+ const handleItemDragEnd = useCallback7(
13493
15033
  (e) => {
13494
15034
  if (footerDragRef.current) {
13495
15035
  const x = e?.clientX;
@@ -13515,7 +15055,7 @@ function OhhwellsBridge() {
13515
15055
  },
13516
15056
  [commitFooterDrag, commitNavDrag, navDragRef]
13517
15057
  );
13518
- const handleItemChromePointerDown = useCallback6((e) => {
15058
+ const handleItemChromePointerDown = useCallback7((e) => {
13519
15059
  if (e.button !== 0) return;
13520
15060
  const selected = selectedElRef.current;
13521
15061
  if (!selected) return;
@@ -13546,7 +15086,7 @@ function OhhwellsBridge() {
13546
15086
  }
13547
15087
  if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
13548
15088
  }, [armNavPressFromChrome]);
13549
- const handleItemChromeClick = useCallback6((clientX, clientY) => {
15089
+ const handleItemChromeClick = useCallback7((clientX, clientY) => {
13550
15090
  if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
13551
15091
  suppressNextClickRef.current = false;
13552
15092
  return;
@@ -13559,7 +15099,7 @@ function OhhwellsBridge() {
13559
15099
  }, []);
13560
15100
  reselectNavigationItemRef.current = reselectNavigationItem;
13561
15101
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
13562
- const select = useCallback6((anchor) => {
15102
+ const select = useCallback7((anchor) => {
13563
15103
  if (!isNavigationItem2(anchor)) return;
13564
15104
  if (activeElRef.current) deactivate();
13565
15105
  aiSectionApiRef.current?.selectFromElement(anchor);
@@ -13568,6 +15108,8 @@ function OhhwellsBridge() {
13568
15108
  selectedFooterColAttrRef.current = null;
13569
15109
  markSelected(anchor);
13570
15110
  setSelectedIsCta(isCtaButton(anchor));
15111
+ setSelectedIsSocial(Boolean(getSocialItem(anchor)));
15112
+ setSelectedIsSocialsRow(false);
13571
15113
  clearHrefKeyHover(anchor);
13572
15114
  const isDropdownTrigger = !isNestedNavChild(anchor) && (navItemHasDropdownChildren(anchor) || navItemOwnsDropdownPanel(anchor));
13573
15115
  if (isNestedNavChild(anchor)) {
@@ -13597,8 +15139,10 @@ function OhhwellsBridge() {
13597
15139
  setToolbarRect(anchor.getBoundingClientRect());
13598
15140
  setToolbarShowEditLink(false);
13599
15141
  setActiveCommands(/* @__PURE__ */ new Set());
15142
+ setFloatingPanel(null);
15143
+ setLogoSizeDraft(null);
13600
15144
  }, [deactivate, markSelected]);
13601
- const selectFrame = useCallback6((el) => {
15145
+ const selectFrame = useCallback7((el) => {
13602
15146
  if (!isNavigationContainer(el)) return;
13603
15147
  if (activeElRef.current) deactivate();
13604
15148
  aiSectionApiRef.current?.selectFromElement(el);
@@ -13608,6 +15152,8 @@ function OhhwellsBridge() {
13608
15152
  selectedFooterColAttrRef.current = isFooterColumn ? el.getAttribute("data-ohw-footer-col") ?? String(listFooterColumns().indexOf(el)) : null;
13609
15153
  markSelected(el);
13610
15154
  setSelectedIsCta(false);
15155
+ setSelectedIsSocial(false);
15156
+ setSelectedIsSocialsRow(isSocialsRow(el));
13611
15157
  clearHrefKeyHover(el);
13612
15158
  setNavGroupForceOpen(null, false);
13613
15159
  hoveredNavContainerRef.current = null;
@@ -13636,22 +15182,153 @@ function OhhwellsBridge() {
13636
15182
  nodes: [{ key: ensured.headingKey, text: ensured.text }]
13637
15183
  });
13638
15184
  }
13639
- setFooterHeadingVisible(isFooterColumnHeadingVisible(el));
13640
- } else {
13641
- setFooterHeadingVisible(null);
13642
- }
13643
- setToolbarVariant("select-frame");
13644
- setToolbarRect(el.getBoundingClientRect());
13645
- setToolbarShowEditLink(false);
13646
- setActiveCommands(/* @__PURE__ */ new Set());
13647
- }, [deactivate, markSelected, postToParent2]);
13648
- const activate = useCallback6((el, options) => {
15185
+ setFooterHeadingVisible(isFooterColumnHeadingVisible(el));
15186
+ } else {
15187
+ setFooterHeadingVisible(null);
15188
+ }
15189
+ setToolbarVariant("select-frame");
15190
+ setToolbarRect(el.getBoundingClientRect());
15191
+ setToolbarShowEditLink(false);
15192
+ setActiveCommands(/* @__PURE__ */ new Set());
15193
+ setFloatingPanel(null);
15194
+ setLogoSizeDraft(null);
15195
+ }, [deactivate, markSelected, postToParent2]);
15196
+ const selectLogo = useCallback7(
15197
+ (logoEl) => {
15198
+ if (activeElRef.current) deactivate();
15199
+ selectedElRef.current = logoEl;
15200
+ selectedHrefKeyRef.current = null;
15201
+ selectedFooterColAttrRef.current = null;
15202
+ markSelected(logoEl);
15203
+ setSelectedIsCta(false);
15204
+ setSelectedIsSocial(false);
15205
+ setSelectedIsSocialsRow(false);
15206
+ setSelectedIsSocialsRow(false);
15207
+ clearHrefKeyHover(logoEl);
15208
+ hoveredNavContainerRef.current = null;
15209
+ setHoveredNavContainerRect(null);
15210
+ setHoveredItemRect(null);
15211
+ hoveredItemElRef.current = null;
15212
+ siblingHintElRef.current = null;
15213
+ setSiblingHintRect(null);
15214
+ setSiblingHintRects([]);
15215
+ setIsItemDragging(false);
15216
+ setReorderHrefKey(null);
15217
+ setReorderDragDisabled(false);
15218
+ setIsFooterFrameSelection(false);
15219
+ setToolbarVariant("logo");
15220
+ setToolbarRect(getLogoInteractionRect(logoEl));
15221
+ setToolbarShowEditLink(false);
15222
+ setActiveCommands(/* @__PURE__ */ new Set());
15223
+ },
15224
+ [deactivate, markSelected]
15225
+ );
15226
+ const openLogoSizePanel = useCallback7((logoEl) => {
15227
+ const placement = getLogoPlacement(logoEl);
15228
+ const draft = readLogoSizeState(editContentRef.current, placement);
15229
+ setLogoSizeDraft(draft);
15230
+ setParentScrollSnap(parentScrollRef.current);
15231
+ setFloatingPanel({
15232
+ key: `logo-size:${placement}`,
15233
+ title: "Logo",
15234
+ context: placement === "navbar" ? "Navbar" : "Footer",
15235
+ kind: "logo-size",
15236
+ placement
15237
+ });
15238
+ }, []);
15239
+ const openSocialsDisplayPanel = useCallback7((row) => {
15240
+ setParentScrollSnap(parentScrollRef.current);
15241
+ setFloatingPanel({
15242
+ key: "socials-display",
15243
+ title: "Style",
15244
+ context: "Socials \xB7 Footer",
15245
+ kind: "socials-display",
15246
+ row
15247
+ });
15248
+ }, []);
15249
+ const changeSocialsDisplay = useCallback7(
15250
+ (row, next) => {
15251
+ if (next.icon) {
15252
+ const missing = socialsMissingIcons(row);
15253
+ listSocialItems(row).forEach((item) => ensureIconSlot(item));
15254
+ if (missing.length) {
15255
+ postToParentRef.current({ type: "ow:social-icons-needed", items: missing });
15256
+ }
15257
+ }
15258
+ applySocialsDisplayToRow(row, next);
15259
+ requestAnimationFrame(() => {
15260
+ if (selectedElRef.current === row && row.isConnected) setToolbarRect(row.getBoundingClientRect());
15261
+ });
15262
+ const displayJson = JSON.stringify(socialsDisplayWith(row, next, editContentRef.current));
15263
+ editContentRef.current = { ...editContentRef.current, [SOCIALS_DISPLAY_KEY]: displayJson };
15264
+ postToParentRef.current({
15265
+ type: "ow:change",
15266
+ nodes: [{ key: SOCIALS_DISPLAY_KEY, text: displayJson }],
15267
+ flush: true
15268
+ });
15269
+ },
15270
+ []
15271
+ );
15272
+ const closeFloatingPanelOnly = useCallback7(() => {
15273
+ setFloatingPanel(null);
15274
+ setLogoSizeDraft(null);
15275
+ }, []);
15276
+ const closeFloatingPanelAndDeselect = useCallback7(() => {
15277
+ setFloatingPanel(null);
15278
+ setLogoSizeDraft(null);
15279
+ deselectRef.current();
15280
+ }, []);
15281
+ const persistLogoSizeDraft = useCallback7(
15282
+ (placement, draft) => {
15283
+ const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
15284
+ const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
15285
+ const nodes = [
15286
+ { key: desktopKey, text: String(draft.desktopPx) }
15287
+ ];
15288
+ if (draft.mobileFollowing) {
15289
+ nodes.push({ key: mobileKey, text: "" });
15290
+ } else {
15291
+ nodes.push({ key: mobileKey, text: String(draft.mobilePx) });
15292
+ }
15293
+ editContentRef.current = {
15294
+ ...editContentRef.current,
15295
+ [desktopKey]: String(draft.desktopPx),
15296
+ [mobileKey]: draft.mobileFollowing ? "" : String(draft.mobilePx)
15297
+ };
15298
+ applyLogoSizeToPlacement(
15299
+ placement,
15300
+ draft.desktopPx,
15301
+ draft.mobileFollowing ? draft.desktopPx : draft.mobilePx,
15302
+ draft.mobileFollowing
15303
+ );
15304
+ postToParent2({ type: "ow:change", nodes });
15305
+ requestAnimationFrame(() => {
15306
+ const selected = selectedElRef.current;
15307
+ if (!selected || toolbarVariantRef.current !== "logo") return;
15308
+ const rect = getLogoInteractionRect(selected);
15309
+ setToolbarRect(rect);
15310
+ if (glowElRef.current) {
15311
+ const GAP = SELECTION_CHROME_GAP2;
15312
+ glowElRef.current.style.top = `${rect.top - GAP}px`;
15313
+ glowElRef.current.style.left = `${rect.left - GAP}px`;
15314
+ glowElRef.current.style.width = `${rect.width + GAP * 2}px`;
15315
+ glowElRef.current.style.height = `${rect.height + GAP * 2}px`;
15316
+ }
15317
+ });
15318
+ },
15319
+ [postToParent2]
15320
+ );
15321
+ const activate = useCallback7((el, options) => {
13649
15322
  if (activeElRef.current === el) return;
15323
+ if (isIconEditable(el)) return;
15324
+ if (el.hasAttribute("data-ohw-social-label")) return;
13650
15325
  clearSelectedAttr();
13651
15326
  selectedElRef.current = null;
13652
15327
  selectedHrefKeyRef.current = null;
13653
15328
  selectedFooterColAttrRef.current = null;
13654
15329
  setSelectedIsCta(false);
15330
+ setSelectedIsSocial(false);
15331
+ setSelectedIsSocialsRow(false);
13655
15332
  deactivate();
13656
15333
  if (hoveredImageRef.current) {
13657
15334
  hoveredImageRef.current = null;
@@ -13721,8 +15398,38 @@ function OhhwellsBridge() {
13721
15398
  deactivateRef.current = deactivate;
13722
15399
  selectRef.current = select;
13723
15400
  selectFrameRef.current = selectFrame;
15401
+ selectLogoRef.current = selectLogo;
15402
+ openLogoSizePanelRef.current = openLogoSizePanel;
13724
15403
  deselectRef.current = deselect;
13725
- useLayoutEffect3(() => {
15404
+ closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
15405
+ const lastSiteWideScopeRef = useRef9(null);
15406
+ useEffect12(() => {
15407
+ if (!isEditMode) {
15408
+ if (lastSiteWideScopeRef.current !== false) {
15409
+ lastSiteWideScopeRef.current = false;
15410
+ postToParent2({ type: "ow:site-wide-scope", active: false });
15411
+ }
15412
+ return;
15413
+ }
15414
+ const active = isSiteWideScopeActive({
15415
+ selected: selectedElRef.current,
15416
+ hoveredItem: hoveredItemElRef.current,
15417
+ hoveredNavContainer: hoveredNavContainerRef.current,
15418
+ active: activeElRef.current
15419
+ });
15420
+ if (lastSiteWideScopeRef.current === active) return;
15421
+ lastSiteWideScopeRef.current = active;
15422
+ postToParent2({ type: "ow:site-wide-scope", active });
15423
+ }, [
15424
+ isEditMode,
15425
+ hoveredItemRect,
15426
+ hoveredNavContainerRect,
15427
+ toolbarVariant,
15428
+ toolbarRect,
15429
+ isFooterFrameSelection,
15430
+ postToParent2
15431
+ ]);
15432
+ useLayoutEffect4(() => {
13726
15433
  if (!subdomain || isEditMode) {
13727
15434
  setFetchState("done");
13728
15435
  return;
@@ -13733,9 +15440,18 @@ function OhhwellsBridge() {
13733
15440
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
13734
15441
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
13735
15442
  }
15443
+ if (typeof content[BRAND_KIT_KEY] === "string") {
15444
+ brandKitRef.current = content[BRAND_KIT_KEY];
15445
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
15446
+ }
15447
+ applyBrandChrome(content);
13736
15448
  for (const [key, val] of Object.entries(content)) {
13737
15449
  if (key === "__ohw_sections") continue;
13738
15450
  if (key === AI_SECTIONS_KEY) continue;
15451
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
15452
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
15453
+ if (key === BRAND_KIT_KEY) continue;
15454
+ if (BRAND_CHROME_KEYS.has(key)) continue;
13739
15455
  if (applyVideoSettingNode(key, val)) continue;
13740
15456
  if (applyCarouselNode(key, val)) continue;
13741
15457
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -13762,14 +15478,20 @@ function OhhwellsBridge() {
13762
15478
  }
13763
15479
  } else if (el.dataset.ohwEditable === "link") {
13764
15480
  applyLinkHref(el, val);
15481
+ } else if (el.dataset.ohwEditable === "icon") {
15482
+ applyIconMarkup(el, val);
13765
15483
  } else if (el.innerHTML !== val) {
13766
15484
  el.innerHTML = val;
13767
15485
  }
13768
15486
  });
13769
15487
  applyLinkByKey(key, val);
13770
15488
  }
15489
+ applyLogoFromContent(content);
15490
+ applyLogoSizes(content);
13771
15491
  reconcileNavbarItemsFromContent(content);
13772
15492
  reconcileFooterOrderFromContent(content);
15493
+ reconcileSocialsFromContent(content);
15494
+ applySocialsDisplayFromContent(content);
13773
15495
  enforceLinkHrefs();
13774
15496
  initSectionsFromContent(content, true);
13775
15497
  sectionsLoadedRef.current = true;
@@ -13785,7 +15507,9 @@ function OhhwellsBridge() {
13785
15507
  let cancelled = false;
13786
15508
  setFetchState("loading");
13787
15509
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
13788
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
15510
+ const initialPath = pathname;
15511
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
15512
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
13789
15513
  if (cancelled) return;
13790
15514
  const content = data?.content ?? {};
13791
15515
  contentCache.set(subdomain, content);
@@ -13798,7 +15522,7 @@ function OhhwellsBridge() {
13798
15522
  cancelled = true;
13799
15523
  };
13800
15524
  }, [subdomain, isEditMode]);
13801
- useEffect11(() => {
15525
+ useEffect12(() => {
13802
15526
  if (!subdomain || isEditMode) return;
13803
15527
  let debounceTimer = null;
13804
15528
  let observer = null;
@@ -13808,8 +15532,16 @@ function OhhwellsBridge() {
13808
15532
  retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
13809
15533
  observer?.disconnect();
13810
15534
  try {
15535
+ applyBrandChrome(content);
15536
+ if (typeof content[BRAND_KIT_KEY] === "string") {
15537
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
15538
+ }
13811
15539
  for (const [key, val] of Object.entries(content)) {
13812
15540
  if (key === "__ohw_sections") continue;
15541
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
15542
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
15543
+ if (key === BRAND_KIT_KEY) continue;
15544
+ if (BRAND_CHROME_KEYS.has(key)) continue;
13813
15545
  if (applyVideoSettingNode(key, val)) continue;
13814
15546
  if (applyCarouselNode(key, val)) continue;
13815
15547
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -13830,8 +15562,11 @@ function OhhwellsBridge() {
13830
15562
  });
13831
15563
  applyLinkByKey(key, val);
13832
15564
  }
15565
+ applyLogoFromContent(content);
13833
15566
  reconcileNavbarItemsFromContent(content);
13834
15567
  reconcileFooterOrderFromContent(content);
15568
+ reconcileSocialsFromContent(content);
15569
+ applySocialsDisplayFromContent(content);
13835
15570
  } finally {
13836
15571
  observer?.observe(document.body, { childList: true, subtree: true });
13837
15572
  }
@@ -13842,6 +15577,17 @@ function OhhwellsBridge() {
13842
15577
  debounceTimer = setTimeout(applyFromCache, 150);
13843
15578
  };
13844
15579
  applyFromCache();
15580
+ const pathCacheKey = `${subdomain}::${pathname}`;
15581
+ if (!fetchedContentPaths.has(pathCacheKey)) {
15582
+ fetchedContentPaths.add(pathCacheKey);
15583
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
15584
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
15585
+ if (!data?.content) return;
15586
+ contentCache.set(subdomain, data.content);
15587
+ applyFromCache();
15588
+ }).catch(() => {
15589
+ });
15590
+ }
13845
15591
  observer = new MutationObserver(scheduleApply);
13846
15592
  observer.observe(document.body, { childList: true, subtree: true });
13847
15593
  return () => {
@@ -13849,16 +15595,16 @@ function OhhwellsBridge() {
13849
15595
  if (debounceTimer) clearTimeout(debounceTimer);
13850
15596
  };
13851
15597
  }, [subdomain, isEditMode, pathname]);
13852
- useLayoutEffect3(() => {
15598
+ useLayoutEffect4(() => {
13853
15599
  const el = document.getElementById("ohw-loader");
13854
15600
  if (!el) return;
13855
15601
  const visible = Boolean(subdomain) && fetchState !== "done";
13856
15602
  el.style.display = visible ? "flex" : "none";
13857
15603
  }, [subdomain, fetchState]);
13858
- useEffect11(() => {
15604
+ useEffect12(() => {
13859
15605
  postToParent2({ type: "ow:navigation", path: pathname });
13860
15606
  }, [pathname, postToParent2]);
13861
- useEffect11(() => {
15607
+ useEffect12(() => {
13862
15608
  if (!isEditMode) return;
13863
15609
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
13864
15610
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -13866,7 +15612,7 @@ function OhhwellsBridge() {
13866
15612
  deselectRef.current();
13867
15613
  deactivateRef.current();
13868
15614
  }, [pathname, isEditMode]);
13869
- useEffect11(() => {
15615
+ useEffect12(() => {
13870
15616
  const contentForNav = () => {
13871
15617
  if (isEditMode) return editContentRef.current;
13872
15618
  if (!subdomain) return {};
@@ -13898,8 +15644,10 @@ function OhhwellsBridge() {
13898
15644
  const content = contentForNav();
13899
15645
  reconcileNavbarItemsFromContent(content);
13900
15646
  reconcileFooterOrderFromContent(content);
15647
+ reconcileSocialsFromContent(content);
15648
+ applySocialsDisplayFromContent(content);
13901
15649
  document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
13902
- if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
15650
+ if (isFooterHrefKey(el.getAttribute("data-ohw-href-key")) || getSocialItem(el)) {
13903
15651
  disableNativeHrefDrag(el);
13904
15652
  }
13905
15653
  });
@@ -13931,31 +15679,36 @@ function OhhwellsBridge() {
13931
15679
  observer?.disconnect();
13932
15680
  };
13933
15681
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
13934
- useEffect11(() => {
15682
+ useEffect12(() => {
13935
15683
  if (!isEditMode) return;
15684
+ let lastPosted = 0;
13936
15685
  const measure = () => {
13937
15686
  const h = document.body.scrollHeight;
13938
- if (h > 50) postToParent2({ type: "ow:height", height: h });
15687
+ if (h > 50 && Math.abs(h - lastPosted) > 1) {
15688
+ lastPosted = h;
15689
+ postToParent2({ type: "ow:height", height: h });
15690
+ }
15691
+ };
15692
+ let raf = null;
15693
+ const schedule = () => {
15694
+ if (raf != null) return;
15695
+ raf = requestAnimationFrame(() => {
15696
+ raf = null;
15697
+ measure();
15698
+ });
13939
15699
  };
13940
15700
  const t1 = setTimeout(measure, 50);
13941
15701
  const t2 = setTimeout(measure, 500);
13942
- let lastWidth = window.innerWidth;
13943
- let resizeTimer = null;
13944
- const handleResize = () => {
13945
- if (window.innerWidth === lastWidth) return;
13946
- lastWidth = window.innerWidth;
13947
- if (resizeTimer) clearTimeout(resizeTimer);
13948
- resizeTimer = setTimeout(measure, 150);
13949
- };
13950
- window.addEventListener("resize", handleResize);
15702
+ const ro = new ResizeObserver(schedule);
15703
+ ro.observe(document.body);
13951
15704
  return () => {
13952
15705
  clearTimeout(t1);
13953
15706
  clearTimeout(t2);
13954
- if (resizeTimer) clearTimeout(resizeTimer);
13955
- window.removeEventListener("resize", handleResize);
15707
+ if (raf != null) cancelAnimationFrame(raf);
15708
+ ro.disconnect();
13956
15709
  };
13957
15710
  }, [pathname, isEditMode, postToParent2]);
13958
- useEffect11(() => {
15711
+ useEffect12(() => {
13959
15712
  if (!subdomainFromQuery || isEditMode) return;
13960
15713
  const handleClick = (e) => {
13961
15714
  const anchor = e.target.closest("a");
@@ -13971,7 +15724,7 @@ function OhhwellsBridge() {
13971
15724
  document.addEventListener("click", handleClick, true);
13972
15725
  return () => document.removeEventListener("click", handleClick, true);
13973
15726
  }, [subdomainFromQuery, isEditMode, router]);
13974
- useEffect11(() => {
15727
+ useEffect12(() => {
13975
15728
  if (!isEditMode) {
13976
15729
  editStylesRef.current?.base.remove();
13977
15730
  editStylesRef.current?.forceHover.remove();
@@ -14022,9 +15775,12 @@ function OhhwellsBridge() {
14022
15775
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
14023
15776
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
14024
15777
  [data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
15778
+ /* Text hover chrome is drawn by the overlay (see hoveredTextRect) \u2014 the CSS outline
15779
+ that used to draw it dashes denser than the overlay border, so identical specs
15780
+ still read as two different frames (OHH-695). The attribute stays: hover paths
15781
+ and suppression rules key off it. */
14025
15782
  [data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]) {
14026
- outline: 2px dashed ${PRIMARY3} !important;
14027
- outline-offset: 4px;
15783
+ outline: none !important;
14028
15784
  }
14029
15785
  [data-ohw-href-key] [data-ohw-hovered],
14030
15786
  [data-ohw-href-key][data-ohw-hovered],
@@ -14077,8 +15833,11 @@ function OhhwellsBridge() {
14077
15833
  stateViews.textContent = `
14078
15834
  [data-ohw-state-view]:not([data-ohw-state-view="default"]) { display: none; }
14079
15835
  [data-ohw-state-view="default"] [data-ohw-editable] { pointer-events: auto !important; }
14080
- [data-ohw-state-hovered] { outline: 2px dashed ${PRIMARY3} !important; outline-offset: 4px; }
15836
+ [data-ohw-state-hovered] { outline: ${HOVER_STROKE_WIDTH}px dashed ${CHROME_PRIMARY} !important; outline-offset: ${HOVER_CHROME_GAP}px; }
14081
15837
  [data-ohw-state-hovered]:has([data-ohw-hovered]) { outline: none !important; }
15838
+ /* :has() only sees descendants \u2014 when the card itself is the hovered text, the overlay
15839
+ already frames it, and the card outline doubled it (OHH-695). */
15840
+ [data-ohw-state-hovered][data-ohw-hovered] { outline: none !important; }
14082
15841
  `;
14083
15842
  document.head.appendChild(base);
14084
15843
  document.head.appendChild(forceHover);
@@ -14100,6 +15859,7 @@ function OhhwellsBridge() {
14100
15859
  if (target.closest("[data-ohw-state-toggle]")) return;
14101
15860
  if (target.closest("[data-ohw-max-badge]")) return;
14102
15861
  if (isInsideLinkEditor(target)) return;
15862
+ if (isInsideFloatingPanel(target)) return;
14103
15863
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
14104
15864
  const beneath = document.elementsFromPoint(e.clientX, e.clientY).find(
14105
15865
  (el) => el instanceof HTMLElement && !el.closest("[data-ohw-bridge-root]") && el.closest("[data-ohw-section]") != null
@@ -14163,6 +15923,21 @@ function OhhwellsBridge() {
14163
15923
  return;
14164
15924
  }
14165
15925
  }
15926
+ const logoEl = getLogoElement(target);
15927
+ if (logoEl) {
15928
+ e.preventDefault();
15929
+ e.stopPropagation();
15930
+ if (!logoHasUploadedImage(logoEl)) {
15931
+ deselectRef.current();
15932
+ deactivateRef.current();
15933
+ const identity = readLogoIdentityFromDom();
15934
+ postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
15935
+ return;
15936
+ }
15937
+ selectLogoRef.current(logoEl);
15938
+ openLogoSizePanelRef.current(logoEl);
15939
+ return;
15940
+ }
14166
15941
  const editable = target.closest("[data-ohw-editable]");
14167
15942
  if (editable) {
14168
15943
  if (editable.dataset.ohwEditable === "link") {
@@ -14179,6 +15954,17 @@ function OhhwellsBridge() {
14179
15954
  });
14180
15955
  return;
14181
15956
  }
15957
+ if (isIconEditable(editable) && !getSocialItem(editable)) {
15958
+ e.preventDefault();
15959
+ e.stopPropagation();
15960
+ aiSectionApiRef.current?.selectFromElement(editable);
15961
+ postToParentRef.current({
15962
+ type: "ow:icon-pick",
15963
+ key: editable.dataset.ohwKey ?? "",
15964
+ current: currentIconRef(editable)
15965
+ });
15966
+ return;
15967
+ }
14182
15968
  if (isMediaEditable(editable)) {
14183
15969
  e.preventDefault();
14184
15970
  e.stopPropagation();
@@ -14193,6 +15979,7 @@ function OhhwellsBridge() {
14193
15979
  e.stopPropagation();
14194
15980
  if (selectedElRef.current === navAnchor) {
14195
15981
  if (e.detail >= 2) return;
15982
+ if (requestSocialDialog(navAnchor, postToParentRef.current, editContentRef.current)) return;
14196
15983
  activateRef.current(editable, { caretX: e.clientX, caretY: e.clientY });
14197
15984
  return;
14198
15985
  }
@@ -14209,6 +15996,7 @@ function OhhwellsBridge() {
14209
15996
  e.preventDefault();
14210
15997
  e.stopPropagation();
14211
15998
  if (selectedElRef.current === hrefAnchor) {
15999
+ if (requestSocialDialog(hrefAnchor, postToParentRef.current, editContentRef.current)) return;
14212
16000
  const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
14213
16001
  if (textEditable) {
14214
16002
  activateRef.current(textEditable, {
@@ -14247,6 +16035,13 @@ function OhhwellsBridge() {
14247
16035
  selectFrameRef.current(navContainerToSelect);
14248
16036
  return;
14249
16037
  }
16038
+ const socialsRowToSelect = isSocialsRow(target) ? target : null;
16039
+ if (socialsRowToSelect && !getSocialItem(target)) {
16040
+ e.preventDefault();
16041
+ e.stopPropagation();
16042
+ selectFrameRef.current(socialsRowToSelect);
16043
+ return;
16044
+ }
14250
16045
  const footerColumnToSelect = resolveFooterColumnSelectionTarget(target, e.clientX, e.clientY);
14251
16046
  if (footerColumnToSelect) {
14252
16047
  e.preventDefault();
@@ -14295,9 +16090,11 @@ function OhhwellsBridge() {
14295
16090
  if (target.closest("[data-ohw-state-toggle]")) return;
14296
16091
  if (target.closest("[data-ohw-max-badge]")) return;
14297
16092
  if (isInsideLinkEditor(target)) return;
16093
+ if (isInsideFloatingPanel(target)) return;
14298
16094
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
14299
16095
  return;
14300
16096
  }
16097
+ if (getSocialItem(target)) return;
14301
16098
  const navLabel = getNavigationLabelEditable(target);
14302
16099
  const editable = navLabel?.editable ?? target.closest('[data-ohw-editable="text"], [data-ohw-editable="plain"]');
14303
16100
  if (!editable || isMediaEditable(editable) || editable.dataset.ohwEditable === "link") return;
@@ -14321,6 +16118,16 @@ function OhhwellsBridge() {
14321
16118
  setHoveredNavContainerRect(null);
14322
16119
  return;
14323
16120
  }
16121
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || isInsideFloatingPanel(target) || isPointOverFloatingPanel(e.clientX, e.clientY)) {
16122
+ hoveredItemElRef.current = null;
16123
+ setHoveredItemRect(null);
16124
+ hoveredNavContainerRef.current = null;
16125
+ setHoveredNavContainerRect(null);
16126
+ siblingHintElRef.current = null;
16127
+ setSiblingHintRect(null);
16128
+ setSiblingHintRects([]);
16129
+ return;
16130
+ }
14324
16131
  {
14325
16132
  const selected2 = selectedElRef.current;
14326
16133
  const selectedIsFooterColumn = Boolean(selected2) && !isFooterLinksContainer(selected2) && (selected2.hasAttribute("data-ohw-footer-col") || selected2.hasAttribute("data-ohw-footer-column") || Boolean(selected2.closest("footer") && isInferredFooterGroup2(selected2)));
@@ -14328,7 +16135,7 @@ function OhhwellsBridge() {
14328
16135
  const allowFooterLinksHover = toolbarVariantRef.current !== "select-frame" || selectedIsFooterColumn;
14329
16136
  if (allowNavContainerHover) {
14330
16137
  const navContainer = target.closest("[data-ohw-nav-container]");
14331
- if (navContainer && !getNavigationItemAnchor(target)) {
16138
+ if (navContainer && !getNavigationItemAnchor(target) && !getLogoElement(target)) {
14332
16139
  hoveredNavContainerRef.current = navContainer;
14333
16140
  setHoveredNavContainerRect(navContainer.getBoundingClientRect());
14334
16141
  hoveredItemElRef.current = null;
@@ -14357,6 +16164,15 @@ function OhhwellsBridge() {
14357
16164
  setHoveredNavContainerRect(null);
14358
16165
  }
14359
16166
  }
16167
+ const logoEl = getLogoElement(target);
16168
+ if (logoEl) {
16169
+ hoveredNavContainerRef.current = null;
16170
+ setHoveredNavContainerRect(null);
16171
+ if (selectedElRef.current === logoEl) return;
16172
+ hoveredItemElRef.current = logoEl;
16173
+ setHoveredItemRect(getLogoInteractionRect(logoEl));
16174
+ return;
16175
+ }
14360
16176
  const navAnchor = getNavigationItemAnchor(target);
14361
16177
  if (navAnchor) {
14362
16178
  hoveredNavContainerRef.current = null;
@@ -14382,6 +16198,11 @@ function OhhwellsBridge() {
14382
16198
  const selected = selectedElRef.current;
14383
16199
  if (selected && (selected === editable || selected.contains(editable))) return;
14384
16200
  if (!isMediaEditable(editable) && !editable.hasAttribute("contenteditable")) {
16201
+ if (isIconEditable(editable) && !getSocialItem(editable)) {
16202
+ hoveredItemElRef.current = editable;
16203
+ setHoveredItemRect(editable.getBoundingClientRect());
16204
+ return;
16205
+ }
14385
16206
  const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
14386
16207
  if (hoverTarget.hasAttribute("data-ohw-href-key")) {
14387
16208
  clearHrefKeyHover(hoverTarget);
@@ -14389,6 +16210,11 @@ function OhhwellsBridge() {
14389
16210
  setHoveredItemRect(hoverTarget.getBoundingClientRect());
14390
16211
  } else if (!isInsideNavigationItem(editable)) {
14391
16212
  hoverTarget.setAttribute("data-ohw-hovered", "");
16213
+ if (editable.closest("footer") || editable.closest('[data-ohw-section="footer"]')) {
16214
+ hoveredNavContainerRef.current = null;
16215
+ setHoveredNavContainerRect(null);
16216
+ hoveredItemElRef.current = editable;
16217
+ }
14392
16218
  }
14393
16219
  }
14394
16220
  };
@@ -14424,11 +16250,30 @@ function OhhwellsBridge() {
14424
16250
  }
14425
16251
  return;
14426
16252
  }
16253
+ const logoEl = getLogoElement(target);
16254
+ if (logoEl) {
16255
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
16256
+ if (related2 && (logoEl === related2 || logoEl.contains(related2) || related2.closest?.('[data-ohw-role="logo"], [data-ohw-logo]'))) {
16257
+ return;
16258
+ }
16259
+ if (hoveredItemElRef.current === logoEl) {
16260
+ hoveredItemElRef.current = null;
16261
+ setHoveredItemRect(null);
16262
+ }
16263
+ return;
16264
+ }
14427
16265
  const editable = target.closest("[data-ohw-editable]");
14428
16266
  if (!editable) return;
14429
16267
  const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
14430
16268
  if (related?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
14431
16269
  if (!isMediaEditable(editable)) {
16270
+ if (isIconEditable(editable) && !getSocialItem(editable) && hoveredItemElRef.current === editable) {
16271
+ if (!related?.closest("[data-ohw-item-interaction]")) {
16272
+ hoveredItemElRef.current = null;
16273
+ setHoveredItemRect(null);
16274
+ }
16275
+ return;
16276
+ }
14432
16277
  const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
14433
16278
  if (hoverTarget.hasAttribute("data-ohw-href-key")) {
14434
16279
  if (!related?.closest("[data-ohw-href-key]")) {
@@ -14437,6 +16282,13 @@ function OhhwellsBridge() {
14437
16282
  }
14438
16283
  } else {
14439
16284
  hoverTarget.removeAttribute("data-ohw-hovered");
16285
+ if (hoveredItemElRef.current === editable) {
16286
+ const stillOnEditable = related instanceof Element && related.closest("[data-ohw-editable]") === editable;
16287
+ if (!stillOnEditable) {
16288
+ hoveredItemElRef.current = null;
16289
+ setHoveredItemRect(null);
16290
+ }
16291
+ }
14440
16292
  }
14441
16293
  }
14442
16294
  };
@@ -14553,6 +16405,26 @@ function OhhwellsBridge() {
14553
16405
  hoveredNavContainerRef.current = null;
14554
16406
  setHoveredNavContainerRect(null);
14555
16407
  }
16408
+ const logoCandidates = [
16409
+ ...document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]'),
16410
+ ...document.querySelectorAll("nav a:not([data-ohw-href-key]), [data-ohw-nav-root] a:not([data-ohw-href-key])"),
16411
+ ...document.querySelectorAll("footer img")
16412
+ ];
16413
+ const seenLogos = /* @__PURE__ */ new Set();
16414
+ for (const candidate of logoCandidates) {
16415
+ const logo = getLogoElement(candidate);
16416
+ if (!logo || seenLogos.has(logo)) continue;
16417
+ seenLogos.add(logo);
16418
+ const r2 = logo.getBoundingClientRect();
16419
+ if (x < r2.left || x > r2.right || y < r2.top || y > r2.bottom) continue;
16420
+ hoveredNavContainerRef.current = null;
16421
+ setHoveredNavContainerRect(null);
16422
+ if (selectedElRef.current !== logo) {
16423
+ hoveredItemElRef.current = logo;
16424
+ setHoveredItemRect(getLogoInteractionRect(logo));
16425
+ }
16426
+ return;
16427
+ }
14556
16428
  const navContainers = Array.from(
14557
16429
  document.querySelectorAll("[data-ohw-nav-container]")
14558
16430
  );
@@ -14638,7 +16510,7 @@ function OhhwellsBridge() {
14638
16510
  }
14639
16511
  };
14640
16512
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
14641
- if (linkPopoverOpenRef.current) {
16513
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
14642
16514
  if (hoveredImageRef.current) {
14643
16515
  hoveredImageRef.current = null;
14644
16516
  hoveredImageHasTextOverlapRef.current = false;
@@ -14892,7 +16764,7 @@ function OhhwellsBridge() {
14892
16764
  }
14893
16765
  };
14894
16766
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
14895
- if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
16767
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || document.documentElement.hasAttribute("data-ohw-section-picking")) {
14896
16768
  if (activeStateElRef.current) {
14897
16769
  activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
14898
16770
  activeStateElRef.current = null;
@@ -14960,6 +16832,19 @@ function OhhwellsBridge() {
14960
16832
  };
14961
16833
  const handleMouseMove = (e) => {
14962
16834
  const { clientX, clientY } = e;
16835
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
16836
+ hoveredItemElRef.current = null;
16837
+ setHoveredItemRect(null);
16838
+ hoveredNavContainerRef.current = null;
16839
+ setHoveredNavContainerRect(null);
16840
+ siblingHintElRef.current = null;
16841
+ setSiblingHintRect(null);
16842
+ setSiblingHintRects([]);
16843
+ dismissImageHover();
16844
+ clearImageHover();
16845
+ setSectionGap(null);
16846
+ return;
16847
+ }
14963
16848
  probeSectionGapAt(clientX, clientY);
14964
16849
  probeImageAt(clientX, clientY);
14965
16850
  probeHoverCardsAt(clientX, clientY);
@@ -14968,6 +16853,11 @@ function OhhwellsBridge() {
14968
16853
  if (e.data?.type !== "ow:pointer-sync") return;
14969
16854
  const { clientX, clientY } = e.data;
14970
16855
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
16856
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
16857
+ dismissImageHover();
16858
+ clearImageHover();
16859
+ return;
16860
+ }
14971
16861
  probeSectionGapAt(clientX, clientY);
14972
16862
  probeImageAt(clientX, clientY);
14973
16863
  probeHoverCardsAt(clientX, clientY);
@@ -14977,7 +16867,7 @@ function OhhwellsBridge() {
14977
16867
  if (footerSession) {
14978
16868
  e.preventDefault();
14979
16869
  if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
14980
- const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
16870
+ const slot = footerSession.kind === "social" && footerSession.hrefKey ? hitTestSocialDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
14981
16871
  refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
14982
16872
  return;
14983
16873
  }
@@ -15029,6 +16919,62 @@ function OhhwellsBridge() {
15029
16919
  resumeAnimTracks();
15030
16920
  clearImageHover();
15031
16921
  };
16922
+ const handleSocialCancel = (e) => {
16923
+ if (e.data?.type !== "ow:social-cancel") return;
16924
+ const { hrefKey } = e.data;
16925
+ const item = hrefKey ? findSocialByHrefKey(hrefKey) : null;
16926
+ if (!item) return;
16927
+ const removed = removeSocialItem(item, editContentRef.current);
16928
+ if (!removed) return;
16929
+ const orderJson = JSON.stringify(removed.order);
16930
+ editContentRef.current = { ...editContentRef.current, [SOCIALS_ORDER_KEY]: orderJson };
16931
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: SOCIALS_ORDER_KEY, text: orderJson }] });
16932
+ deselectRef.current();
16933
+ };
16934
+ const handleSocialUpdate = (e) => {
16935
+ if (e.data?.type !== "ow:social-update") return;
16936
+ const updates = Array.isArray(e.data.items) ? e.data.items : [e.data];
16937
+ const nodes = [];
16938
+ for (const { hrefKey, iconKey, url, iconMarkup, platformId, label } of updates) {
16939
+ if (hrefKey) {
16940
+ document.querySelectorAll(`[data-ohw-href-key="${hrefKey}"]`).forEach((el) => applyLinkHref(el, url));
16941
+ nodes.push({ key: hrefKey, text: url });
16942
+ }
16943
+ if (iconKey && typeof iconMarkup === "string" && iconMarkup) {
16944
+ document.querySelectorAll(`[data-ohw-key="${iconKey}"][data-ohw-editable="icon"]`).forEach((el) => {
16945
+ applyIconMarkup(el, iconMarkup);
16946
+ });
16947
+ nodes.push({ key: iconKey, text: iconMarkup });
16948
+ }
16949
+ if (iconKey && platformId) nodes.push({ key: socialPlatformKey(iconKey), text: platformId });
16950
+ if (iconKey && label) {
16951
+ const labelKey = socialLabelKey(iconKey);
16952
+ document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
16953
+ el.textContent = label;
16954
+ });
16955
+ nodes.push({ key: labelKey, text: label });
16956
+ }
16957
+ }
16958
+ if (!nodes.length) return;
16959
+ editContentRef.current = {
16960
+ ...editContentRef.current,
16961
+ ...Object.fromEntries(nodes.map((node) => [node.key, node.text]))
16962
+ };
16963
+ postToParentRef.current({ type: "ow:change", nodes, flush: true });
16964
+ };
16965
+ const handleIconMarkup = (e) => {
16966
+ if (e.data?.type !== "ow:icon-markup") return;
16967
+ const { key, markup } = e.data;
16968
+ if (!key || typeof markup !== "string") return;
16969
+ const targets = document.querySelectorAll(
16970
+ `[data-ohw-key="${key}"][data-ohw-editable="icon"]`
16971
+ );
16972
+ if (!targets.length) return;
16973
+ targets.forEach((el) => {
16974
+ applyIconMarkup(el, markup);
16975
+ });
16976
+ postToParentRef.current({ type: "ow:change", nodes: [{ key, text: markup }], flush: true });
16977
+ };
15032
16978
  const handleImageUrl = (e) => {
15033
16979
  if (e.data?.type !== "ow:image-url") return;
15034
16980
  const { key, url } = e.data;
@@ -15161,6 +17107,11 @@ function OhhwellsBridge() {
15161
17107
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
15162
17108
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
15163
17109
  }
17110
+ if (typeof content[BRAND_KIT_KEY] === "string") {
17111
+ brandKitRef.current = content[BRAND_KIT_KEY];
17112
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17113
+ }
17114
+ applyBrandChrome(content);
15164
17115
  let sectionsJson = null;
15165
17116
  for (const [key, val] of Object.entries(content)) {
15166
17117
  if (key === "__ohw_sections") {
@@ -15168,6 +17119,10 @@ function OhhwellsBridge() {
15168
17119
  continue;
15169
17120
  }
15170
17121
  if (key === AI_SECTIONS_KEY) continue;
17122
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
17123
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
17124
+ if (key === BRAND_KIT_KEY) continue;
17125
+ if (BRAND_CHROME_KEYS.has(key)) continue;
15171
17126
  if (applyVideoSettingNode(key, val)) continue;
15172
17127
  if (applyCarouselNode(key, val)) continue;
15173
17128
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -15187,6 +17142,8 @@ function OhhwellsBridge() {
15187
17142
  });
15188
17143
  applyLinkByKey(key, val);
15189
17144
  }
17145
+ applyLogoFromContent(content);
17146
+ applyLogoSizes(content);
15190
17147
  if (sectionsJson) {
15191
17148
  initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
15192
17149
  sectionsLoadedRef.current = true;
@@ -15201,6 +17158,58 @@ function OhhwellsBridge() {
15201
17158
  if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
15202
17159
  postToParentRef.current({ type: "ow:hydrate-done" });
15203
17160
  };
17161
+ const handleUpdateLogoIdentity = (e) => {
17162
+ if (e.data?.type !== "ow:update-logo-identity") return;
17163
+ const rawText = typeof e.data.text === "string" ? e.data.text : "";
17164
+ const alt = typeof e.data.alt === "string" ? e.data.alt : rawText;
17165
+ const href = typeof e.data.href === "string" ? e.data.href : void 0;
17166
+ const imageProvided = "image" in e.data;
17167
+ const imageUrl = imageProvided && typeof e.data.image === "string" && e.data.image.trim() ? e.data.image.trim() : imageProvided ? null : void 0;
17168
+ let isPlaceholder = e.data.isPlaceholder !== false;
17169
+ if (imageUrl) isPlaceholder = false;
17170
+ else if (imageProvided && imageUrl === null) {
17171
+ isPlaceholder = e.data.isPlaceholder === true || !rawText.trim() || resolveLogoDisplayText(rawText) === PLACEHOLDER_BUSINESS_NAME;
17172
+ }
17173
+ const display = applyLogoIdentity(rawText, isPlaceholder);
17174
+ const displayAlt = resolveLogoDisplayText(alt || display);
17175
+ if (imageUrl !== void 0) {
17176
+ applyLogoImage(imageUrl, displayAlt);
17177
+ } else {
17178
+ for (const key of LOGO_IMAGE_KEYS) {
17179
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
17180
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
17181
+ if (img) img.alt = displayAlt;
17182
+ });
17183
+ }
17184
+ }
17185
+ if (href !== void 0) {
17186
+ applyLogoHref(href);
17187
+ applyLinkByKey("nav-logo-href", href);
17188
+ applyLinkByKey("footer-logo-href", href);
17189
+ applyLinkByKey("logo-href", href);
17190
+ }
17191
+ const nodes = [
17192
+ ...LOGO_TEXT_KEYS.map((key) => ({ key, text: display })),
17193
+ { key: LOGO_PLACEHOLDER_KEY, text: isPlaceholder ? "true" : "false" },
17194
+ { key: LOGO_ALT_KEY, text: displayAlt }
17195
+ ];
17196
+ if (imageUrl !== void 0) {
17197
+ if (imageUrl) {
17198
+ for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: imageUrl });
17199
+ } else {
17200
+ for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: "" });
17201
+ }
17202
+ }
17203
+ if (href !== void 0) {
17204
+ for (const key of LOGO_HREF_KEYS) nodes.push({ key, text: href.trim() || "/" });
17205
+ }
17206
+ editContentRef.current = {
17207
+ ...editContentRef.current,
17208
+ ...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
17209
+ };
17210
+ applyLogoSizes(editContentRef.current);
17211
+ postToParentRef.current({ type: "ow:change", nodes });
17212
+ };
15204
17213
  window.addEventListener("message", handleHydrate);
15205
17214
  const postAiSectionsChanged = () => {
15206
17215
  postToParentRef.current({
@@ -15257,6 +17266,17 @@ function OhhwellsBridge() {
15257
17266
  postAiSectionsChanged();
15258
17267
  };
15259
17268
  window.addEventListener("message", handleAiSetSections);
17269
+ const handleAiSetBrand = (e) => {
17270
+ if (e.data?.type !== "ow:ai-set-brand") return;
17271
+ const value = typeof e.data.value === "string" ? e.data.value : "";
17272
+ const previous = brandKitRef.current;
17273
+ brandKitRef.current = value;
17274
+ applyBrandToDom(parseBrandKit(value));
17275
+ if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
17276
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
17277
+ postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
17278
+ };
17279
+ window.addEventListener("message", handleAiSetBrand);
15260
17280
  const handleDeactivate = (e) => {
15261
17281
  if (e.data?.type !== "ow:deactivate") return;
15262
17282
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
@@ -15265,6 +17285,12 @@ function OhhwellsBridge() {
15265
17285
  closeLinkPopoverRef.current();
15266
17286
  return;
15267
17287
  }
17288
+ if (floatingPanelOpenRef.current) {
17289
+ setFloatingPanelRef.current(null);
17290
+ deselectRef.current();
17291
+ deactivateRef.current();
17292
+ return;
17293
+ }
15268
17294
  deselectRef.current();
15269
17295
  deactivateRef.current();
15270
17296
  };
@@ -15284,6 +17310,10 @@ function OhhwellsBridge() {
15284
17310
  closeLinkPopoverRef.current();
15285
17311
  return;
15286
17312
  }
17313
+ if (floatingPanelOpenRef.current) {
17314
+ closeFloatingPanelOnlyRef.current();
17315
+ return;
17316
+ }
15287
17317
  if (activeElRef.current) {
15288
17318
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
15289
17319
  const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
@@ -15314,6 +17344,10 @@ function OhhwellsBridge() {
15314
17344
  return;
15315
17345
  }
15316
17346
  if (selectedElRef.current) {
17347
+ if (toolbarVariantRef.current === "logo") {
17348
+ deselectRef.current();
17349
+ return;
17350
+ }
15317
17351
  if (toolbarVariantRef.current === "select-frame") {
15318
17352
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
15319
17353
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -15347,7 +17381,16 @@ function OhhwellsBridge() {
15347
17381
  closeLinkPopoverRef.current();
15348
17382
  return;
15349
17383
  }
17384
+ if (e.key === "Escape" && floatingPanelOpenRef.current) {
17385
+ e.preventDefault();
17386
+ closeFloatingPanelOnlyRef.current();
17387
+ return;
17388
+ }
15350
17389
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
17390
+ if (toolbarVariantRef.current === "logo") {
17391
+ deselectRef.current();
17392
+ return;
17393
+ }
15351
17394
  if (toolbarVariantRef.current === "select-frame") {
15352
17395
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
15353
17396
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -15425,7 +17468,8 @@ function OhhwellsBridge() {
15425
17468
  const handleScroll = () => {
15426
17469
  const focusEl = activeElRef.current ?? selectedElRef.current;
15427
17470
  if (focusEl) {
15428
- const r2 = activeElRef.current ? getEditMeasureEl(activeElRef.current).getBoundingClientRect() : focusEl.getBoundingClientRect();
17471
+ const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
17472
+ const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
15429
17473
  applyToolbarPos(r2);
15430
17474
  setToolbarRect(r2);
15431
17475
  setMaxBadge((prev) => prev && activeElRef.current ? { ...prev, rect: r2 } : prev);
@@ -15435,7 +17479,9 @@ function OhhwellsBridge() {
15435
17479
  setToggleState((prev) => prev ? { ...prev, rect } : null);
15436
17480
  }
15437
17481
  if (hoveredItemElRef.current) {
15438
- setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
17482
+ const hoverEl = hoveredItemElRef.current;
17483
+ const logo = getLogoElement(hoverEl);
17484
+ setHoveredItemRect(logo ? getLogoInteractionRect(logo) : hoverEl.getBoundingClientRect());
15439
17485
  }
15440
17486
  if (hoveredNavContainerRef.current) {
15441
17487
  setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
@@ -15455,7 +17501,7 @@ function OhhwellsBridge() {
15455
17501
  }
15456
17502
  if (footerDragRef.current) {
15457
17503
  const session = footerDragRef.current;
15458
- const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
17504
+ const slot = session.kind === "social" && session.hrefKey ? hitTestSocialDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
15459
17505
  refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
15460
17506
  }
15461
17507
  if (navDragRef.current) {
@@ -15479,6 +17525,9 @@ function OhhwellsBridge() {
15479
17525
  if (aiSectionsRef.current) {
15480
17526
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
15481
17527
  }
17528
+ if (brandKitRef.current) {
17529
+ nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
17530
+ }
15482
17531
  postToParentRef.current({ type: "ow:save-result", nodes });
15483
17532
  };
15484
17533
  const handleInsertSection = (e) => {
@@ -15489,8 +17538,12 @@ function OhhwellsBridge() {
15489
17538
  if (inserted) {
15490
17539
  const tracker = getSectionsTracker();
15491
17540
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
15492
- const h = document.documentElement.scrollHeight;
15493
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
17541
+ const reportHeight = () => {
17542
+ const h = document.body.scrollHeight;
17543
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
17544
+ };
17545
+ reportHeight();
17546
+ setTimeout(reportHeight, 500);
15494
17547
  }
15495
17548
  };
15496
17549
  const handleSwitchSchedule = (e) => {
@@ -15683,13 +17736,17 @@ function OhhwellsBridge() {
15683
17736
  if (e.data?.type !== "ow:parent-scroll") return;
15684
17737
  const { iframeOffsetTop, headerH, canvasH } = e.data;
15685
17738
  parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
17739
+ if (floatingPanelOpenRef.current) {
17740
+ setParentScrollSnap({ iframeOffsetTop, headerH, canvasH });
17741
+ }
15686
17742
  if (visibleViewportRef.current) {
15687
17743
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
15688
17744
  }
15689
17745
  const focusEl = activeElRef.current ?? selectedElRef.current;
15690
17746
  if (focusEl) {
15691
- const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : focusEl;
15692
- applyToolbarPos(measureEl.getBoundingClientRect());
17747
+ const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
17748
+ const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
17749
+ applyToolbarPos(r2);
15693
17750
  }
15694
17751
  };
15695
17752
  const handleClickAt = (e) => {
@@ -15714,6 +17771,25 @@ function OhhwellsBridge() {
15714
17771
  postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "", elementType: stateCardImage.dataset.ohwEditable ?? "image" });
15715
17772
  return;
15716
17773
  }
17774
+ const logoAtPoint = Array.from(
17775
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]')
17776
+ ).map((el) => getLogoElement(el)).find((logo) => {
17777
+ if (!logo) return false;
17778
+ const r2 = logo.getBoundingClientRect();
17779
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
17780
+ });
17781
+ if (logoAtPoint) {
17782
+ if (!logoHasUploadedImage(logoAtPoint)) {
17783
+ deselectRef.current();
17784
+ deactivateRef.current();
17785
+ const identity = readLogoIdentityFromDom();
17786
+ postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
17787
+ return;
17788
+ }
17789
+ selectLogoRef.current(logoAtPoint);
17790
+ openLogoSizePanelRef.current(logoAtPoint);
17791
+ return;
17792
+ }
15717
17793
  const textEditable = Array.from(
15718
17794
  document.querySelectorAll(NON_MEDIA_SELECTOR)
15719
17795
  ).find((el) => {
@@ -15775,6 +17851,9 @@ function OhhwellsBridge() {
15775
17851
  window.addEventListener("message", handleClearSchedulingWidget);
15776
17852
  window.addEventListener("message", handleRemoveSchedulingSection);
15777
17853
  window.addEventListener("message", handleCollectSection);
17854
+ window.addEventListener("message", handleSocialCancel);
17855
+ window.addEventListener("message", handleSocialUpdate);
17856
+ window.addEventListener("message", handleIconMarkup);
15778
17857
  window.addEventListener("message", handleImageUrl);
15779
17858
  window.addEventListener("message", handleImageUploading);
15780
17859
  window.addEventListener("message", handleCarouselChange);
@@ -15782,6 +17861,14 @@ function OhhwellsBridge() {
15782
17861
  window.addEventListener("message", handleParentScroll);
15783
17862
  window.addEventListener("message", handlePointerSync);
15784
17863
  window.addEventListener("message", handleClickAt);
17864
+ window.addEventListener("message", handleUpdateLogoIdentity);
17865
+ const handleViewMode = (e) => {
17866
+ if (e.data?.type !== "ow:view-mode") return;
17867
+ const mode = e.data.mode === "Mobile" || e.data.mode === "mobile" ? "mobile" : "desktop";
17868
+ setEditorViewport(mode);
17869
+ applyLogoSizes(editContentRef.current);
17870
+ };
17871
+ window.addEventListener("message", handleViewMode);
15785
17872
  const handleViewportResize = () => {
15786
17873
  if (visibleViewportRef.current) {
15787
17874
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
@@ -15826,6 +17913,9 @@ function OhhwellsBridge() {
15826
17913
  window.removeEventListener("message", handleClearSchedulingWidget);
15827
17914
  window.removeEventListener("message", handleRemoveSchedulingSection);
15828
17915
  window.removeEventListener("message", handleCollectSection);
17916
+ window.removeEventListener("message", handleSocialCancel);
17917
+ window.removeEventListener("message", handleSocialUpdate);
17918
+ window.removeEventListener("message", handleIconMarkup);
15829
17919
  window.removeEventListener("message", handleImageUrl);
15830
17920
  window.removeEventListener("message", handleImageUploading);
15831
17921
  window.removeEventListener("message", handleCarouselChange);
@@ -15834,10 +17924,13 @@ function OhhwellsBridge() {
15834
17924
  window.removeEventListener("resize", handleViewportResize);
15835
17925
  window.removeEventListener("message", handlePointerSync);
15836
17926
  window.removeEventListener("message", handleClickAt);
17927
+ window.removeEventListener("message", handleUpdateLogoIdentity);
17928
+ window.removeEventListener("message", handleViewMode);
15837
17929
  window.removeEventListener("message", handleHydrate);
15838
17930
  window.removeEventListener("message", handleAiApplyTree);
15839
17931
  window.removeEventListener("message", handleAiDeleteSection);
15840
17932
  window.removeEventListener("message", handleAiSetSections);
17933
+ window.removeEventListener("message", handleAiSetBrand);
15841
17934
  window.removeEventListener("message", handleDeactivate);
15842
17935
  window.removeEventListener("message", handleToastAction);
15843
17936
  window.removeEventListener("message", handleUiEscape);
@@ -15847,7 +17940,7 @@ function OhhwellsBridge() {
15847
17940
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
15848
17941
  };
15849
17942
  }, [isEditMode, refreshStateRules]);
15850
- useEffect11(() => {
17943
+ useEffect12(() => {
15851
17944
  if (!isEditMode) return;
15852
17945
  const THRESHOLD = 10;
15853
17946
  const resolveWasSelected = (el) => {
@@ -15863,13 +17956,13 @@ function OhhwellsBridge() {
15863
17956
  if (footerDragRef.current) return;
15864
17957
  const target = e.target;
15865
17958
  if (!target) return;
15866
- 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]')) {
17959
+ 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]')) {
15867
17960
  return;
15868
17961
  }
15869
17962
  if (target.closest("[data-ohw-item-drag-surface]")) return;
15870
- const anchor = getNavigationItemAnchor(target);
17963
+ const anchor = getNavigationItemAnchor(target) ?? (document.elementsFromPoint(e.clientX, e.clientY).map((el) => el instanceof HTMLElement ? getNavigationItemAnchor(el) : null).find((found) => found !== null) ?? null);
15871
17964
  const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
15872
- if (anchor && isFooterHrefKey(hrefKey)) {
17965
+ if (anchor && (isFooterHrefKey(hrefKey) || getSocialItem(anchor))) {
15873
17966
  footerPointerDragRef.current = {
15874
17967
  el: anchor,
15875
17968
  kind: "link",
@@ -15902,7 +17995,7 @@ function OhhwellsBridge() {
15902
17995
  clearTextSelection();
15903
17996
  const session = footerDragRef.current;
15904
17997
  if (!session) return;
15905
- const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
17998
+ const slot = session.kind === "social" && session.hrefKey ? hitTestSocialDropSlot(e.clientX, e.clientY, session.hrefKey) : session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
15906
17999
  refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
15907
18000
  return;
15908
18001
  }
@@ -15929,7 +18022,7 @@ function OhhwellsBridge() {
15929
18022
  const column = findFooterColumnForLink(pending.el);
15930
18023
  const columns2 = listFooterColumns();
15931
18024
  beginFooterDragRef.current({
15932
- kind: "link",
18025
+ kind: getSocialItem(pending.el) ? "social" : "link",
15933
18026
  hrefKey: key,
15934
18027
  columnEl: column,
15935
18028
  sourceColumnIndex: column ? columns2.indexOf(column) : 0,
@@ -16001,7 +18094,7 @@ function OhhwellsBridge() {
16001
18094
  unlockFooterDragInteraction();
16002
18095
  };
16003
18096
  }, [isEditMode]);
16004
- useEffect11(() => {
18097
+ useEffect12(() => {
16005
18098
  const handler = (e) => {
16006
18099
  if (e.data?.type !== "ow:request-schedule-config") return;
16007
18100
  const insertAfterVal = e.data.insertAfter;
@@ -16017,7 +18110,7 @@ function OhhwellsBridge() {
16017
18110
  window.addEventListener("message", handler);
16018
18111
  return () => window.removeEventListener("message", handler);
16019
18112
  }, [processConfigRequest]);
16020
- useEffect11(() => {
18113
+ useEffect12(() => {
16021
18114
  if (!isEditMode) return;
16022
18115
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
16023
18116
  el.removeAttribute("data-ohw-active-state");
@@ -16041,7 +18134,7 @@ function OhhwellsBridge() {
16041
18134
  postToParent2({
16042
18135
  type: "ow:ready",
16043
18136
  version: "1",
16044
- bridgeVersion: "0.1.54",
18137
+ bridgeVersion: "0.1.55",
16045
18138
  path: pathname,
16046
18139
  nodes: collectEditableNodes(editContentRef.current),
16047
18140
  sections
@@ -16053,13 +18146,13 @@ function OhhwellsBridge() {
16053
18146
  clearTimeout(timer);
16054
18147
  };
16055
18148
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
16056
- useEffect11(() => {
18149
+ useEffect12(() => {
16057
18150
  scrollToHashSectionWhenReady();
16058
18151
  const onHashChange = () => scrollToHashSectionWhenReady();
16059
18152
  window.addEventListener("hashchange", onHashChange);
16060
18153
  return () => window.removeEventListener("hashchange", onHashChange);
16061
18154
  }, [pathname]);
16062
- const handleCommand = useCallback6((cmd) => {
18155
+ const handleCommand = useCallback7((cmd) => {
16063
18156
  const el = activeElRef.current;
16064
18157
  const selBefore = window.getSelection();
16065
18158
  let savedOffsets = null;
@@ -16095,7 +18188,7 @@ function OhhwellsBridge() {
16095
18188
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
16096
18189
  refreshActiveCommandsRef.current();
16097
18190
  }, []);
16098
- const handleStateChange = useCallback6((state) => {
18191
+ const handleStateChange = useCallback7((state) => {
16099
18192
  if (!activeStateElRef.current) return;
16100
18193
  const el = activeStateElRef.current;
16101
18194
  if (state === "Default") {
@@ -16108,7 +18201,7 @@ function OhhwellsBridge() {
16108
18201
  }
16109
18202
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
16110
18203
  }, [deactivate]);
16111
- const reselectAfterLinkPopover = useCallback6(
18204
+ const reselectAfterLinkPopover = useCallback7(
16112
18205
  (hrefKey) => {
16113
18206
  requestAnimationFrame(() => {
16114
18207
  const el = resolveHrefKeyElement(hrefKey);
@@ -16117,7 +18210,7 @@ function OhhwellsBridge() {
16117
18210
  },
16118
18211
  [resolveHrefKeyElement]
16119
18212
  );
16120
- const closeLinkPopover = useCallback6(() => {
18213
+ const closeLinkPopover = useCallback7(() => {
16121
18214
  const session = linkPopoverSessionRef.current;
16122
18215
  addNavAfterAnchorRef.current = null;
16123
18216
  setLinkPopover(null);
@@ -16125,9 +18218,9 @@ function OhhwellsBridge() {
16125
18218
  reselectAfterLinkPopover(session.key);
16126
18219
  }
16127
18220
  }, [reselectAfterLinkPopover]);
16128
- const closeLinkPopoverRef = useRef8(closeLinkPopover);
18221
+ const closeLinkPopoverRef = useRef9(closeLinkPopover);
16129
18222
  closeLinkPopoverRef.current = closeLinkPopover;
16130
- const openLinkPopoverForActive = useCallback6(() => {
18223
+ const openLinkPopoverForActive = useCallback7(() => {
16131
18224
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
16132
18225
  if (!hrefCtx) return;
16133
18226
  bumpLinkPopoverGrace();
@@ -16138,11 +18231,15 @@ function OhhwellsBridge() {
16138
18231
  });
16139
18232
  deactivate();
16140
18233
  }, [deactivate]);
16141
- const openLinkPopoverForSelected = useCallback6(() => {
18234
+ const openLinkPopoverForSelected = useCallback7(() => {
16142
18235
  const anchor = selectedElRef.current;
16143
18236
  if (!anchor) return;
16144
18237
  const key = anchor.getAttribute("data-ohw-href-key");
16145
18238
  if (!key) return;
18239
+ if (requestSocialDialog(anchor, postToParentRef.current, editContentRef.current)) {
18240
+ deselect();
18241
+ return;
18242
+ }
16146
18243
  bumpLinkPopoverGrace();
16147
18244
  setLinkPopover({
16148
18245
  key,
@@ -16151,7 +18248,7 @@ function OhhwellsBridge() {
16151
18248
  });
16152
18249
  deselect();
16153
18250
  }, [deselect]);
16154
- const handleSelectParent = useCallback6(() => {
18251
+ const handleSelectParent = useCallback7(() => {
16155
18252
  const selected = selectedElRef.current;
16156
18253
  if (!selected) return;
16157
18254
  if (toolbarVariantRef.current === "select-frame") {
@@ -16178,11 +18275,37 @@ function OhhwellsBridge() {
16178
18275
  }
16179
18276
  deselectRef.current();
16180
18277
  }, []);
16181
- const handleDuplicateSelected = useCallback6(() => {
18278
+ const handleDuplicateSelected = useCallback7(() => {
16182
18279
  const selected = selectedElRef.current;
16183
18280
  if (!selected || !isNavigationItem2(selected)) return;
16184
18281
  const hrefKey = selected.getAttribute("data-ohw-href-key");
16185
18282
  if (!hrefKey) return;
18283
+ const social = getSocialItem(selected);
18284
+ if (social) {
18285
+ const result = duplicateSocialItem(social, editContentRef.current);
18286
+ if (!result) return;
18287
+ const orderJson = JSON.stringify(result.order);
18288
+ const carried = [
18289
+ { from: result.copiedFrom?.href, to: result.hrefKey },
18290
+ { from: result.copiedFrom?.icon, to: result.iconKey },
18291
+ { from: result.copiedFrom?.icon ? socialPlatformKey(result.copiedFrom.icon) : null, to: socialPlatformKey(result.iconKey) }
18292
+ ];
18293
+ const nodes = [{ key: SOCIALS_ORDER_KEY, text: orderJson }];
18294
+ for (const { from, to } of carried) {
18295
+ const value = from ? editContentRef.current[from] : void 0;
18296
+ if (value) nodes.push({ key: to, text: value });
18297
+ }
18298
+ editContentRef.current = {
18299
+ ...editContentRef.current,
18300
+ ...Object.fromEntries(nodes.map((node) => [node.key, node.text]))
18301
+ };
18302
+ postToParent2({ type: "ow:change", nodes });
18303
+ enforceLinkHrefs();
18304
+ const copyRow = findSocialsRow(result.item);
18305
+ if (copyRow) applySocialsDisplayToRow(copyRow, socialsDisplayFor(copyRow, editContentRef.current));
18306
+ requestAnimationFrame(() => selectRef.current(result.item));
18307
+ return;
18308
+ }
16186
18309
  if (isNavbarHrefKey(hrefKey)) {
16187
18310
  const result = duplicateNavbarItem(selected);
16188
18311
  if (!result) return;
@@ -16268,7 +18391,7 @@ function OhhwellsBridge() {
16268
18391
  });
16269
18392
  }
16270
18393
  }, [postToParent2]);
16271
- const runPendingDeleteUndo = useCallback6(() => {
18394
+ const runPendingDeleteUndo = useCallback7(() => {
16272
18395
  const pending = pendingDeleteUndoRef.current;
16273
18396
  if (!pending) return false;
16274
18397
  pendingDeleteUndoRef.current = null;
@@ -16276,7 +18399,7 @@ function OhhwellsBridge() {
16276
18399
  enforceLinkHrefs();
16277
18400
  return true;
16278
18401
  }, []);
16279
- const handleDeleteSelected = useCallback6(() => {
18402
+ const handleDeleteSelected = useCallback7(() => {
16280
18403
  const selected = selectedElRef.current;
16281
18404
  if (!selected) return false;
16282
18405
  return deleteSelectedNavFooterItem({
@@ -16297,7 +18420,7 @@ function OhhwellsBridge() {
16297
18420
  }, [postToParent2]);
16298
18421
  handleDeleteSelectedRef.current = handleDeleteSelected;
16299
18422
  runPendingDeleteUndoRef.current = runPendingDeleteUndo;
16300
- const handleLinkPopoverSubmit = useCallback6(
18423
+ const handleLinkPopoverSubmit = useCallback7(
16301
18424
  (target) => {
16302
18425
  const session = linkPopoverSessionRef.current;
16303
18426
  if (!session) return;
@@ -16363,19 +18486,19 @@ function OhhwellsBridge() {
16363
18486
  const showEditLink = toolbarShowEditLink;
16364
18487
  const currentSections = sectionsByPath[pathname] ?? [];
16365
18488
  linkPopoverOpenRef.current = linkPopover !== null;
16366
- const handleMediaReplace = useCallback6(
18489
+ const handleMediaReplace = useCallback7(
16367
18490
  (key) => {
16368
18491
  postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
16369
18492
  },
16370
18493
  [postToParent2, mediaHover?.elementType]
16371
18494
  );
16372
- const handleEditCarousel = useCallback6(
18495
+ const handleEditCarousel = useCallback7(
16373
18496
  (key) => {
16374
18497
  postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
16375
18498
  },
16376
18499
  [postToParent2]
16377
18500
  );
16378
- const handleMediaFadeOutComplete = useCallback6((key) => {
18501
+ const handleMediaFadeOutComplete = useCallback7((key) => {
16379
18502
  setUploadingRects((prev) => {
16380
18503
  if (!(key in prev)) return prev;
16381
18504
  const next = { ...prev };
@@ -16383,7 +18506,7 @@ function OhhwellsBridge() {
16383
18506
  return next;
16384
18507
  });
16385
18508
  }, []);
16386
- const handleVideoSettingsChange = useCallback6(
18509
+ const handleVideoSettingsChange = useCallback7(
16387
18510
  (key, settings) => {
16388
18511
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
16389
18512
  const video = getVideoEl2(el);
@@ -16406,10 +18529,10 @@ function OhhwellsBridge() {
16406
18529
  [postToParent2]
16407
18530
  );
16408
18531
  return bridgeRoot ? createPortal2(
16409
- /* @__PURE__ */ jsxs17(Fragment8, { children: [
16410
- /* @__PURE__ */ jsx30("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
16411
- isEditMode && /* @__PURE__ */ jsx30(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
16412
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx30(
18532
+ /* @__PURE__ */ jsxs20(Fragment8, { children: [
18533
+ /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
18534
+ isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
18535
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
16413
18536
  MediaOverlay,
16414
18537
  {
16415
18538
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -16420,7 +18543,7 @@ function OhhwellsBridge() {
16420
18543
  },
16421
18544
  `uploading-${key}`
16422
18545
  )),
16423
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx30(
18546
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx33(
16424
18547
  MediaOverlay,
16425
18548
  {
16426
18549
  hover: mediaHover,
@@ -16429,11 +18552,11 @@ function OhhwellsBridge() {
16429
18552
  onVideoSettingsChange: handleVideoSettingsChange
16430
18553
  }
16431
18554
  ),
16432
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx30(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
16433
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx30(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
16434
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx30(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
16435
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx30(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
16436
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx30(
18555
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
18556
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
18557
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
18558
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
18559
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
16437
18560
  "div",
16438
18561
  {
16439
18562
  className: "pointer-events-none fixed z-2147483646",
@@ -16443,7 +18566,7 @@ function OhhwellsBridge() {
16443
18566
  width: slot.width,
16444
18567
  height: slot.height
16445
18568
  },
16446
- children: /* @__PURE__ */ jsx30(
18569
+ children: /* @__PURE__ */ jsx33(
16447
18570
  DropIndicator,
16448
18571
  {
16449
18572
  direction: slot.direction,
@@ -16454,7 +18577,7 @@ function OhhwellsBridge() {
16454
18577
  },
16455
18578
  `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
16456
18579
  )),
16457
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx30(
18580
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
16458
18581
  "div",
16459
18582
  {
16460
18583
  className: "pointer-events-none fixed z-2147483646",
@@ -16464,7 +18587,7 @@ function OhhwellsBridge() {
16464
18587
  width: slot.width,
16465
18588
  height: slot.height
16466
18589
  },
16467
- children: /* @__PURE__ */ jsx30(
18590
+ children: /* @__PURE__ */ jsx33(
16468
18591
  DropIndicator,
16469
18592
  {
16470
18593
  direction: slot.direction,
@@ -16475,10 +18598,11 @@ function OhhwellsBridge() {
16475
18598
  },
16476
18599
  `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
16477
18600
  )),
16478
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx30(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
16479
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx30(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
16480
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx30(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
16481
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx30(
18601
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
18602
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
18603
+ hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
18604
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
18605
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
16482
18606
  FooterContainerChrome,
16483
18607
  {
16484
18608
  rect: toolbarRect,
@@ -16486,7 +18610,7 @@ function OhhwellsBridge() {
16486
18610
  addDisabled: !canAddFooterColumn()
16487
18611
  }
16488
18612
  ),
16489
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx30(
18613
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
16490
18614
  ItemInteractionLayer,
16491
18615
  {
16492
18616
  rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
@@ -16498,26 +18622,34 @@ function OhhwellsBridge() {
16498
18622
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
16499
18623
  onDragHandleDragStart: handleItemDragStart,
16500
18624
  onDragHandleDragEnd: handleItemDragEnd,
16501
- onItemPointerDown: handleItemChromePointerDown,
16502
- onItemClick: handleItemChromeClick,
16503
- itemDragSurface: !isFooterFrameSelection,
16504
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && isFooterFrameSelection && !isItemDragging ? /* @__PURE__ */ jsx30(
18625
+ onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
18626
+ onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
18627
+ itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
18628
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
16505
18629
  ItemActionToolbar,
16506
18630
  {
16507
18631
  onEditLink: openLinkPopoverForSelected,
18632
+ onStyle: () => {
18633
+ const row = selectedElRef.current;
18634
+ if (!row) return;
18635
+ if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
18636
+ else openSocialsDisplayPanel(row);
18637
+ },
18638
+ showStyle: selectedIsSocialsRow,
18639
+ styleActive: floatingPanel?.kind === "socials-display",
16508
18640
  onAddItem: handleAddChildItem,
16509
18641
  onSelectParent: handleSelectParent,
16510
18642
  onDuplicate: handleDuplicateSelected,
16511
18643
  onDelete: handleDeleteSelected,
16512
- addItemDisabled: false,
18644
+ addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current),
16513
18645
  editLinkDisabled: false,
16514
18646
  moreDisabled: false,
16515
- duplicateDisabled: isFooterFrameSelection,
16516
- showEditLink: !isFooterFrameSelection && navDropdownPreviewOpen === null,
16517
- showAddItem: isFooterFrameSelection || !selectedIsCta && Boolean(
18647
+ duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow,
18648
+ showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
18649
+ showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
16518
18650
  selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
16519
18651
  ),
16520
- showMore: !selectedIsCta || isFooterFrameSelection,
18652
+ showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
16521
18653
  dropdownOpen: navDropdownPreviewOpen,
16522
18654
  onDropdownOpenChange: handleNavDropdownOpenChange,
16523
18655
  headingVisible: footerHeadingVisible,
@@ -16526,8 +18658,8 @@ function OhhwellsBridge() {
16526
18658
  ) : void 0
16527
18659
  }
16528
18660
  ),
16529
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs17(Fragment8, { children: [
16530
- /* @__PURE__ */ jsx30(
18661
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
18662
+ /* @__PURE__ */ jsx33(
16531
18663
  EditGlowChrome,
16532
18664
  {
16533
18665
  rect: toolbarRect,
@@ -16537,7 +18669,7 @@ function OhhwellsBridge() {
16537
18669
  hideHandle: isItemDragging
16538
18670
  }
16539
18671
  ),
16540
- /* @__PURE__ */ jsx30(
18672
+ /* @__PURE__ */ jsx33(
16541
18673
  FloatingToolbar,
16542
18674
  {
16543
18675
  rect: toolbarRect,
@@ -16550,7 +18682,7 @@ function OhhwellsBridge() {
16550
18682
  }
16551
18683
  )
16552
18684
  ] }),
16553
- maxBadge && /* @__PURE__ */ jsxs17(
18685
+ maxBadge && /* @__PURE__ */ jsxs20(
16554
18686
  "div",
16555
18687
  {
16556
18688
  "data-ohw-max-badge": "",
@@ -16576,7 +18708,7 @@ function OhhwellsBridge() {
16576
18708
  ]
16577
18709
  }
16578
18710
  ),
16579
- toggleState && !linkPopover && /* @__PURE__ */ jsx30(
18711
+ toggleState && !linkPopover && /* @__PURE__ */ jsx33(
16580
18712
  StateToggle,
16581
18713
  {
16582
18714
  rect: toggleState.rect,
@@ -16585,15 +18717,15 @@ function OhhwellsBridge() {
16585
18717
  onStateChange: handleStateChange
16586
18718
  }
16587
18719
  ),
16588
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs17(
18720
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
16589
18721
  "div",
16590
18722
  {
16591
18723
  "data-ohw-section-insert-line": "",
16592
18724
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
16593
18725
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
16594
18726
  children: [
16595
- /* @__PURE__ */ jsx30("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
16596
- /* @__PURE__ */ jsx30(
18727
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
18728
+ /* @__PURE__ */ jsx33(
16597
18729
  Badge,
16598
18730
  {
16599
18731
  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",
@@ -16610,11 +18742,11 @@ function OhhwellsBridge() {
16610
18742
  children: "Add Section"
16611
18743
  }
16612
18744
  ),
16613
- /* @__PURE__ */ jsx30("div", { className: "flex-1 bg-primary", style: { height: 3 } })
18745
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
16614
18746
  ]
16615
18747
  }
16616
18748
  ),
16617
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx30(
18749
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
16618
18750
  LinkPopover,
16619
18751
  {
16620
18752
  panelRef: linkPopoverPanelRef,
@@ -16630,11 +18762,137 @@ function OhhwellsBridge() {
16630
18762
  onSubmit: handleLinkPopoverSubmit
16631
18763
  },
16632
18764
  linkPopover.key
18765
+ ) : null,
18766
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
18767
+ FloatingPanel,
18768
+ {
18769
+ open: true,
18770
+ title: floatingPanel.title,
18771
+ context: floatingPanel.context,
18772
+ position: floatingPanelPos,
18773
+ onPositionChange: setFloatingPanelPos,
18774
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
18775
+ onClose: closeFloatingPanelOnly,
18776
+ children: /* @__PURE__ */ jsx33(
18777
+ SocialsDisplayPanel,
18778
+ {
18779
+ display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
18780
+ onChange: (next) => {
18781
+ changeSocialsDisplay(floatingPanel.row, next);
18782
+ setFloatingPanel({ ...floatingPanel });
18783
+ }
18784
+ }
18785
+ )
18786
+ }
18787
+ ) : null,
18788
+ floatingPanel && floatingPanel.kind === "logo-size" && logoSizeDraft ? /* @__PURE__ */ jsx33(
18789
+ FloatingPanel,
18790
+ {
18791
+ open: true,
18792
+ title: floatingPanel.title,
18793
+ context: floatingPanel.context,
18794
+ position: floatingPanelPos,
18795
+ onPositionChange: setFloatingPanelPos,
18796
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
18797
+ onClose: closeFloatingPanelAndDeselect,
18798
+ children: /* @__PURE__ */ jsx33(
18799
+ LogoSizePanel,
18800
+ {
18801
+ viewport: editorViewport,
18802
+ sizePx: editorViewport === "mobile" && !logoSizeDraft.mobileFollowing ? logoSizeDraft.mobilePx : logoSizeDraft.desktopPx,
18803
+ mobileFollowing: logoSizeDraft.mobileFollowing,
18804
+ onSizeChange: (px) => {
18805
+ const next = editorViewport === "mobile" ? { ...logoSizeDraft, mobilePx: px, mobileFollowing: false } : {
18806
+ ...logoSizeDraft,
18807
+ desktopPx: px,
18808
+ mobilePx: logoSizeDraft.mobileFollowing ? px : logoSizeDraft.mobilePx
18809
+ };
18810
+ setLogoSizeDraft(next);
18811
+ persistLogoSizeDraft(floatingPanel.placement, next);
18812
+ },
18813
+ onCustomizeMobile: () => {
18814
+ const next = {
18815
+ ...logoSizeDraft,
18816
+ mobileFollowing: false,
18817
+ mobilePx: logoSizeDraft.desktopPx
18818
+ };
18819
+ setLogoSizeDraft(next);
18820
+ persistLogoSizeDraft(floatingPanel.placement, next);
18821
+ },
18822
+ onResetMobile: () => {
18823
+ const next = {
18824
+ ...logoSizeDraft,
18825
+ mobileFollowing: true,
18826
+ mobilePx: logoSizeDraft.desktopPx
18827
+ };
18828
+ setLogoSizeDraft(next);
18829
+ persistLogoSizeDraft(floatingPanel.placement, next);
18830
+ },
18831
+ onUpdateEverywhere: () => {
18832
+ const identity = readLogoIdentityFromDom();
18833
+ postToParent2({ type: "ow:open-logo-settings", ...identity });
18834
+ }
18835
+ }
18836
+ )
18837
+ }
16633
18838
  ) : null
16634
18839
  ] }),
16635
18840
  bridgeRoot
16636
18841
  ) : null;
16637
18842
  }
18843
+
18844
+ // src/ui/EmptySection.tsx
18845
+ import Link4 from "next/link";
18846
+ import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
18847
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
18848
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
18849
+ /* @__PURE__ */ jsx34(
18850
+ "p",
18851
+ {
18852
+ style: {
18853
+ fontFamily: "var(--brand-font-body)",
18854
+ fontSize: "0.75rem",
18855
+ fontWeight: 500,
18856
+ letterSpacing: "0.15em",
18857
+ textTransform: "uppercase",
18858
+ color: "var(--brand-accent)",
18859
+ marginBottom: "1.5rem"
18860
+ },
18861
+ children: /* @__PURE__ */ jsx34(Link4, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
18862
+ }
18863
+ ),
18864
+ /* @__PURE__ */ jsx34(
18865
+ "h1",
18866
+ {
18867
+ style: {
18868
+ fontFamily: "var(--brand-font-heading)",
18869
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
18870
+ lineHeight: 1.1,
18871
+ letterSpacing: "-0.025em",
18872
+ color: "var(--brand-text)",
18873
+ marginBottom: "1rem"
18874
+ },
18875
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
18876
+ children: title
18877
+ }
18878
+ ),
18879
+ /* @__PURE__ */ jsx34(
18880
+ "p",
18881
+ {
18882
+ style: {
18883
+ fontFamily: "var(--brand-font-body)",
18884
+ fontSize: "1rem",
18885
+ lineHeight: 1.7,
18886
+ fontWeight: 300,
18887
+ color: "var(--brand-text-muted)",
18888
+ maxWidth: "340px"
18889
+ },
18890
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
18891
+ children: "This page doesn't have any content yet."
18892
+ }
18893
+ )
18894
+ ] });
18895
+ }
16638
18896
  export {
16639
18897
  AI_DEFAULT_BRAND,
16640
18898
  AI_TREE_SCHEMA_VERSIONS,
@@ -16651,6 +18909,7 @@ export {
16651
18909
  DropdownMenuItem,
16652
18910
  DropdownMenuSeparator,
16653
18911
  DropdownMenuTrigger,
18912
+ EmptySection,
16654
18913
  ItemActionToolbar,
16655
18914
  ItemInteractionLayer,
16656
18915
  LinkEditorPanel,