@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.cjs CHANGED
@@ -46,6 +46,7 @@ __export(index_exports, {
46
46
  DropdownMenuItem: () => DropdownMenuItem,
47
47
  DropdownMenuSeparator: () => DropdownMenuSeparator,
48
48
  DropdownMenuTrigger: () => DropdownMenuTrigger,
49
+ EmptySection: () => EmptySection,
49
50
  ItemActionToolbar: () => ItemActionToolbar,
50
51
  ItemInteractionLayer: () => ItemInteractionLayer,
51
52
  LinkEditorPanel: () => LinkEditorPanel,
@@ -74,7 +75,7 @@ __export(index_exports, {
74
75
  module.exports = __toCommonJS(index_exports);
75
76
 
76
77
  // src/OhhwellsBridge.tsx
77
- var import_react15 = __toESM(require("react"), 1);
78
+ var import_react16 = __toESM(require("react"), 1);
78
79
  var import_client2 = require("react-dom/client");
79
80
  var import_react_dom3 = require("react-dom");
80
81
 
@@ -191,6 +192,129 @@ function deleteSectionFromState(state, sectionId) {
191
192
  return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
192
193
  }
193
194
 
195
+ // src/lib/brand-chrome.ts
196
+ var BRAND_NAME_KEY = "__ohw_brand_name";
197
+ var BRAND_TITLE_KEY = "__ohw_site_title";
198
+ var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
199
+ var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
200
+ BRAND_NAME_KEY,
201
+ BRAND_TITLE_KEY,
202
+ BRAND_FAVICON_LETTER_KEY
203
+ ]);
204
+ function upsertMeta(selector, attr, token, value) {
205
+ let el = document.head.querySelector(selector);
206
+ if (!el) {
207
+ el = document.createElement("meta");
208
+ el.setAttribute(attr, token);
209
+ document.head.appendChild(el);
210
+ }
211
+ if (el.getAttribute("content") !== value) el.setAttribute("content", value);
212
+ }
213
+ function escapeXml(value) {
214
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
215
+ }
216
+ function applyLetterFavicon(letter) {
217
+ 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>`;
218
+ const href = `data:image/svg+xml,${encodeURIComponent(svg)}`;
219
+ let link = document.head.querySelector('link[rel="icon"]');
220
+ if (!link) {
221
+ link = document.createElement("link");
222
+ link.rel = "icon";
223
+ document.head.appendChild(link);
224
+ }
225
+ link.type = "image/svg+xml";
226
+ if (link.href !== href) link.href = href;
227
+ }
228
+ function applyBrandChrome(content) {
229
+ const name = content[BRAND_NAME_KEY];
230
+ if (typeof name === "string" && name.length > 0) {
231
+ document.querySelectorAll("[data-ohw-wordmark]").forEach((el) => {
232
+ if (el.textContent !== name) el.textContent = name;
233
+ if (el.getAttribute("title") !== name) el.setAttribute("title", name);
234
+ });
235
+ }
236
+ const title = content[BRAND_TITLE_KEY];
237
+ if (typeof title === "string" && title.length > 0) {
238
+ if (document.title !== title) document.title = title;
239
+ upsertMeta('meta[property="og:title"]', "property", "og:title", title);
240
+ upsertMeta('meta[name="twitter:title"]', "name", "twitter:title", title);
241
+ }
242
+ const letter = content[BRAND_FAVICON_LETTER_KEY];
243
+ if (typeof letter === "string" && letter.length > 0) {
244
+ applyLetterFavicon(letter);
245
+ }
246
+ }
247
+
248
+ // src/lib/brand-kit.ts
249
+ var BRAND_KIT_KEY = "__ohw_brand";
250
+ var BRAND_VAR_PREFIX = "--ohw-brand-";
251
+ var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
252
+ (role) => `${BRAND_VAR_PREFIX}${role}`
253
+ );
254
+ var FONT_VARS = { heading: ["--font-heading", "--font-display"], body: ["--font-body"] };
255
+ var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
256
+ function brandColorVars(kit) {
257
+ const { dark, primary, accent, light } = kit.palette;
258
+ const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
259
+ return {
260
+ [`${BRAND_VAR_PREFIX}primary`]: primary,
261
+ [`${BRAND_VAR_PREFIX}accent`]: accent,
262
+ [`${BRAND_VAR_PREFIX}light`]: light,
263
+ [`${BRAND_VAR_PREFIX}dark`]: dark,
264
+ [`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
265
+ [`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
266
+ [`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
267
+ };
268
+ }
269
+ function parseBrandKit(raw) {
270
+ if (!raw) return null;
271
+ try {
272
+ const parsed = JSON.parse(raw);
273
+ const p = parsed?.palette;
274
+ const f = parsed?.fonts;
275
+ 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") {
276
+ return null;
277
+ }
278
+ return {
279
+ palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
280
+ fonts: { heading: f.heading, body: f.body }
281
+ };
282
+ } catch {
283
+ return null;
284
+ }
285
+ }
286
+ function familyOf(stack) {
287
+ const first = stack.split(",")[0]?.trim() ?? "";
288
+ return first.replace(/^['"]|['"]$/g, "");
289
+ }
290
+ function loadBrandFonts(families) {
291
+ const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
292
+ if (unique.length === 0) return;
293
+ const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
294
+ const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
295
+ let link = document.getElementById(BRAND_FONT_LINK_ID);
296
+ if (!link) {
297
+ link = document.createElement("link");
298
+ link.id = BRAND_FONT_LINK_ID;
299
+ link.rel = "stylesheet";
300
+ document.head.appendChild(link);
301
+ }
302
+ if (link.href !== href) link.href = href;
303
+ }
304
+ function applyBrandToDom(kit) {
305
+ const root = document.documentElement;
306
+ if (!kit) {
307
+ for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
308
+ for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
309
+ document.getElementById(BRAND_FONT_LINK_ID)?.remove();
310
+ return;
311
+ }
312
+ for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
313
+ for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
314
+ for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
315
+ loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
316
+ }
317
+
194
318
  // src/ui/ai-tree/aiSectionsManager.tsx
195
319
  var import_react_dom = require("react-dom");
196
320
  var import_client = require("react-dom/client");
@@ -223,6 +347,17 @@ var FEATURE_LINE_CSS = [
223
347
  function textAttrs(ctx, path) {
224
348
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
225
349
  }
350
+ var AI_RESPONSIVE_CSS = [
351
+ "@media (max-width: 960px) {",
352
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
353
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
354
+ "}",
355
+ "@media (max-width: 640px) {",
356
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
357
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
358
+ " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
359
+ "}"
360
+ ].join("\n");
226
361
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
227
362
  function MediaBox({
228
363
  refValue,
@@ -235,13 +370,17 @@ function MediaBox({
235
370
  const url = refValue ? ctx.resolveMedia(refValue) : null;
236
371
  const isIcon = /^(lucide|simple):/.test(refValue);
237
372
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
238
- const editAttrs = ctx.keyFor && editPath && !isIcon ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "image" } : {};
373
+ const editAttrs = ctx.keyFor && editPath ? {
374
+ "data-ohw-key": ctx.keyFor(editPath),
375
+ "data-ohw-editable": isIcon ? "icon" : "image"
376
+ } : {};
239
377
  if (isIcon) {
240
378
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
241
379
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
242
380
  "span",
243
381
  {
244
382
  "data-ai-icon": refValue,
383
+ ...editAttrs,
245
384
  style: {
246
385
  display: "inline-flex",
247
386
  width: 48,
@@ -961,6 +1100,7 @@ function Carousel({ items, itemsPerRow, ctx }) {
961
1100
  children: pageGroups.map((group, p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
962
1101
  "div",
963
1102
  {
1103
+ "data-ai-grid": String(itemsPerRow),
964
1104
  style: {
965
1105
  flex: "0 0 100%",
966
1106
  display: "grid",
@@ -1064,6 +1204,7 @@ function CollectionBlock({ node, ctx, path }) {
1064
1204
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1065
1205
  "div",
1066
1206
  {
1207
+ "data-ai-grid": String(itemsPerRow),
1067
1208
  style: {
1068
1209
  display: "grid",
1069
1210
  gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
@@ -1227,6 +1368,7 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1227
1368
  {
1228
1369
  "data-ai-section": tree.tag ?? "",
1229
1370
  ...bgAttrs,
1371
+ "data-ai-responsive": "",
1230
1372
  style: {
1231
1373
  position: "relative",
1232
1374
  padding: `${pad}px 0`,
@@ -1236,10 +1378,12 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1236
1378
  backgroundPosition: "center"
1237
1379
  },
1238
1380
  children: [
1381
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
1239
1382
  isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
1240
1383
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1241
1384
  "div",
1242
1385
  {
1386
+ "data-ai-section-inner": "",
1243
1387
  style: {
1244
1388
  position: "relative",
1245
1389
  maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
@@ -1250,6 +1394,7 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1250
1394
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1251
1395
  "div",
1252
1396
  {
1397
+ "data-ai-columns": "",
1253
1398
  style: {
1254
1399
  display: "grid",
1255
1400
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -1273,17 +1418,34 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
1273
1418
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1274
1419
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1275
1420
  var REMOVED_ATTR = "data-ohw-ai-removed";
1421
+ function readRootVar(name) {
1422
+ if (typeof document === "undefined") return "";
1423
+ return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
1424
+ }
1425
+ function deriveBrandOverride() {
1426
+ const dark = readRootVar("--ohw-brand-dark");
1427
+ const primary = readRootVar("--ohw-brand-primary");
1428
+ const light = readRootVar("--ohw-brand-light");
1429
+ if (!dark || !primary || !light) return null;
1430
+ const accent = readRootVar("--ohw-brand-accent");
1431
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1432
+ const body = readRootVar("--font-body");
1433
+ return {
1434
+ palette: { dark, primary, accent: accent || dark, light },
1435
+ fonts: {
1436
+ heading: heading || AI_DEFAULT_BRAND.fonts.heading,
1437
+ body: body || AI_DEFAULT_BRAND.fonts.body
1438
+ }
1439
+ };
1440
+ }
1276
1441
  function deriveTemplateBrand() {
1277
- if (typeof document === "undefined") return null;
1278
- const cs = getComputedStyle(document.documentElement);
1279
- const read = (name) => cs.getPropertyValue(name).trim();
1280
- const dark = read("--color-dark");
1281
- const primary = read("--color-primary");
1282
- const light = read("--color-light");
1442
+ const dark = readRootVar("--color-dark");
1443
+ const primary = readRootVar("--color-primary");
1444
+ const light = readRootVar("--color-light");
1283
1445
  if (!dark || !primary || !light) return null;
1284
- const accent = read("--color-accent");
1285
- const heading = read("--font-heading") || read("--font-display");
1286
- const body = read("--font-body");
1446
+ const accent = readRootVar("--color-accent");
1447
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1448
+ const body = readRootVar("--font-body");
1287
1449
  return {
1288
1450
  palette: { dark, primary, accent: accent || dark, light },
1289
1451
  fonts: {
@@ -1375,7 +1537,9 @@ function syncReplacedOriginals(state) {
1375
1537
  }
1376
1538
  function applyAiSectionsToDom(state, options) {
1377
1539
  if (typeof document === "undefined") return;
1540
+ const brandOverride = deriveBrandOverride();
1378
1541
  const templateBrand = deriveTemplateBrand();
1542
+ const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
1379
1543
  const activeIds = new Set(state.sections.map((entry) => entry.id));
1380
1544
  for (const [id, section] of mounted) {
1381
1545
  if (!activeIds.has(id)) {
@@ -1385,7 +1549,7 @@ function applyAiSectionsToDom(state, options) {
1385
1549
  }
1386
1550
  }
1387
1551
  for (const entry of state.sections) {
1388
- const serialized = JSON.stringify(entry);
1552
+ const serialized = JSON.stringify(entry) + brandKey;
1389
1553
  const existing = mounted.get(entry.id);
1390
1554
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
1391
1555
  continue;
@@ -1409,7 +1573,7 @@ function applyAiSectionsToDom(state, options) {
1409
1573
  AiTreeRenderer,
1410
1574
  {
1411
1575
  tree: entry.tree,
1412
- brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1576
+ brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1413
1577
  resolveMedia,
1414
1578
  editKeyPrefix: `ai.${entry.id}`
1415
1579
  }
@@ -2026,7 +2190,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2026
2190
  const autoId = (0, import_react5.useId)();
2027
2191
  const insertAfter = insertAfterProp ?? autoId;
2028
2192
  const [schedule, setSchedule] = (0, import_react5.useState)(null);
2029
- const [loading, setLoading] = (0, import_react5.useState)(true);
2193
+ const [loading, setLoading] = (0, import_react5.useState)(initialScheduleId !== null);
2030
2194
  const [inEditor, setInEditor] = (0, import_react5.useState)(false);
2031
2195
  const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
2032
2196
  const [modalState, setModalState] = (0, import_react5.useState)(null);
@@ -2200,8 +2364,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2200
2364
  "*"
2201
2365
  );
2202
2366
  };
2203
- if (!inEditor && !loading && !schedule) return null;
2204
2367
  const sectionId = `scheduling-${insertAfter}`;
2368
+ if (!inEditor && !loading && !schedule) {
2369
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2370
+ }
2205
2371
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2206
2372
  "section",
2207
2373
  {
@@ -5935,6 +6101,7 @@ function ToolbarActionTooltip({
5935
6101
  function ItemActionToolbar({
5936
6102
  onEditLink,
5937
6103
  onAddItem,
6104
+ onStyle,
5938
6105
  onSelectParent,
5939
6106
  onDuplicate,
5940
6107
  onDelete,
@@ -5946,6 +6113,8 @@ function ItemActionToolbar({
5946
6113
  deleteDisabled = false,
5947
6114
  showEditLink = true,
5948
6115
  showAddItem = true,
6116
+ showStyle = false,
6117
+ styleActive = false,
5949
6118
  showMore = true,
5950
6119
  tooltipSide = "bottom",
5951
6120
  dropdownOpen = null,
@@ -6019,6 +6188,22 @@ function ItemActionToolbar({
6019
6188
  children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Link, { className: "size-4 shrink-0", "aria-hidden": true })
6020
6189
  }
6021
6190
  ) : null,
6191
+ showStyle ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
6192
+ ToolbarActionTooltip,
6193
+ {
6194
+ label: "Style",
6195
+ side: tooltipSide,
6196
+ buttonProps: {
6197
+ active: styleActive,
6198
+ onMouseDown: (e) => {
6199
+ e.preventDefault();
6200
+ e.stopPropagation();
6201
+ onStyle?.();
6202
+ }
6203
+ },
6204
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Brush, { className: "size-4 shrink-0", "aria-hidden": true })
6205
+ }
6206
+ ) : null,
6022
6207
  showAddItem ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
6023
6208
  ToolbarActionTooltip,
6024
6209
  {
@@ -6136,6 +6321,7 @@ var FOCUS_RING = `0 0 0 4px color-mix(in srgb, ${PRIMARY} 12%, transparent)`;
6136
6321
  var DRAG_SHADOW = "0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.1)";
6137
6322
  var TOOLBAR_EDGE_MARGIN = 4;
6138
6323
  var SELECTION_CHROME_GAP = 4;
6324
+ var HOVER_CHROME_GAP = 2;
6139
6325
  var TOOLBAR_STROKE_GAP = 4;
6140
6326
  function getChromeZIndex(state) {
6141
6327
  switch (state) {
@@ -6291,10 +6477,11 @@ function ItemInteractionLayer({
6291
6477
  onItemPointerDown,
6292
6478
  onItemClick,
6293
6479
  itemDragSurface = true,
6294
- chromeGap = SELECTION_CHROME_GAP,
6480
+ chromeGap,
6295
6481
  className
6296
6482
  }) {
6297
6483
  if (state === "default") return null;
6484
+ const gap = chromeGap ?? (state === "hover" ? HOVER_CHROME_GAP : SELECTION_CHROME_GAP);
6298
6485
  const isActive = state === "active-top" || state === "active-bottom";
6299
6486
  const isDragging = state === "dragging";
6300
6487
  const showToolbar = isActive && toolbar;
@@ -6310,10 +6497,10 @@ function ItemInteractionLayer({
6310
6497
  className: cn("pointer-events-none", className),
6311
6498
  style: {
6312
6499
  position: "fixed",
6313
- top: rect.top - chromeGap,
6314
- left: rect.left - chromeGap,
6315
- width: rect.width + chromeGap * 2,
6316
- height: rect.height + chromeGap * 2,
6500
+ top: rect.top - gap,
6501
+ left: rect.left - gap,
6502
+ width: rect.width + gap * 2,
6503
+ height: rect.height + gap * 2,
6317
6504
  zIndex: getChromeZIndex(state)
6318
6505
  },
6319
6506
  children: [
@@ -9525,9 +9712,453 @@ function deleteNavbarItem(sourceAnchor) {
9525
9712
  };
9526
9713
  }
9527
9714
 
9715
+ // src/lib/icon-markup.ts
9716
+ var GLYPH_SELECTOR = "svg, img";
9717
+ function referenceBox(slot) {
9718
+ const row = slot.closest("[data-ohw-socials-row]") ?? slot.closest("a")?.parentElement ?? null;
9719
+ const neighbour = row ? Array.from(row.querySelectorAll('[data-ohw-editable="icon"]')).find((el) => el !== slot) : null;
9720
+ const source = (neighbour ?? slot).querySelector(GLYPH_SELECTOR);
9721
+ const box = source?.getBoundingClientRect() ?? null;
9722
+ return box?.width && box.height ? box : null;
9723
+ }
9724
+ function iconMarkupSizedFor(slot, markup) {
9725
+ const box = referenceBox(slot);
9726
+ if (!box) return markup;
9727
+ const holder = document.createElement("div");
9728
+ holder.innerHTML = markup;
9729
+ const glyph = holder.querySelector(GLYPH_SELECTOR);
9730
+ if (!glyph) return markup;
9731
+ glyph.style.width = `${Math.round(box.width)}px`;
9732
+ glyph.style.height = `${Math.round(box.height)}px`;
9733
+ return holder.innerHTML;
9734
+ }
9735
+ function applyIconMarkup(slot, markup) {
9736
+ if (!markup) return;
9737
+ const coloured = iconMarkupInheritingColour(markup);
9738
+ const sized = iconMarkupSizedFor(slot, coloured);
9739
+ if (slot.innerHTML !== sized) slot.innerHTML = sized;
9740
+ if (sized === coloured) {
9741
+ requestAnimationFrame(() => {
9742
+ if (!slot.isConnected) return;
9743
+ const resized = iconMarkupSizedFor(slot, coloured);
9744
+ if (resized !== coloured && slot.innerHTML !== resized) slot.innerHTML = resized;
9745
+ });
9746
+ }
9747
+ }
9748
+ function detectIconStyle(el) {
9749
+ const row = el.closest("[data-ohw-socials-row]");
9750
+ const glyphs = Array.from((row ?? el).querySelectorAll("svg"));
9751
+ const outlined = glyphs.some((svg) => {
9752
+ return Array.from(svg.querySelectorAll("*")).some((node) => {
9753
+ return node.getAttribute("stroke") !== null && node.getAttribute("stroke") !== "none";
9754
+ });
9755
+ });
9756
+ return outlined ? "outline" : "fill";
9757
+ }
9758
+ function iconMarkupInheritingColour(markup) {
9759
+ const holder = document.createElement("div");
9760
+ holder.innerHTML = markup;
9761
+ holder.querySelectorAll("svg *").forEach((node) => {
9762
+ if (node.getAttribute("fill") && node.getAttribute("fill") !== "none") {
9763
+ node.setAttribute("fill", "currentColor");
9764
+ }
9765
+ if (node.getAttribute("stroke") && node.getAttribute("stroke") !== "none") {
9766
+ node.setAttribute("stroke", "currentColor");
9767
+ }
9768
+ });
9769
+ return holder.innerHTML;
9770
+ }
9771
+
9772
+ // src/lib/socials-items.ts
9773
+ var ICON_SELECTOR = '[data-ohw-editable="icon"]';
9774
+ var SOCIAL_KEY_RE = /(^|-)social(s)?(-|$)/i;
9775
+ var SOCIALS_ROW_ATTR = "data-ohw-socials-row";
9776
+ var SOCIALS_ITEM_ATTR = "data-ohw-social-item";
9777
+ function isSocialItem(el) {
9778
+ if (!el) return false;
9779
+ const anchor = el instanceof HTMLAnchorElement ? el : el.closest("a");
9780
+ if (!anchor) return false;
9781
+ if (SOCIAL_KEY_RE.test(anchor.getAttribute("data-ohw-href-key") ?? "")) return true;
9782
+ return anchor.querySelectorAll(ICON_SELECTOR).length === 1;
9783
+ }
9784
+ function getSocialItem(el) {
9785
+ const anchor = el.closest("a");
9786
+ return isSocialItem(anchor) ? anchor : null;
9787
+ }
9788
+ function findSocialsRow(el) {
9789
+ const item = getSocialItem(el);
9790
+ if (!item) return null;
9791
+ const wrapper = item.parentElement;
9792
+ const row = wrapper && wrapper.querySelectorAll("a").length === 1 && wrapper.matches("li, div, span") ? wrapper.parentElement : wrapper;
9793
+ if (!row) return null;
9794
+ const anchors = Array.from(row.querySelectorAll("a"));
9795
+ if (!anchors.length || !anchors.every((anchor) => isSocialItem(anchor))) return null;
9796
+ return row;
9797
+ }
9798
+ function isSocialsRow(el) {
9799
+ const anchors = Array.from(el.querySelectorAll("a"));
9800
+ return anchors.length > 0 && anchors.every((anchor) => isSocialItem(anchor));
9801
+ }
9802
+ function listSocialItems(row) {
9803
+ return Array.from(row.children).map((child) => {
9804
+ if (!(child instanceof HTMLElement)) return null;
9805
+ const anchor = child.matches("a") ? child : child.querySelector("a");
9806
+ return isSocialItem(anchor) ? anchor : null;
9807
+ }).filter((item) => item !== null);
9808
+ }
9809
+ function socialRowUnit(item) {
9810
+ const row = findSocialsRow(item);
9811
+ let node = item;
9812
+ while (node.parentElement && node.parentElement !== row) {
9813
+ node = node.parentElement;
9814
+ }
9815
+ return node;
9816
+ }
9817
+ function listSocialsRows(root = document) {
9818
+ const rows = /* @__PURE__ */ new Set();
9819
+ root.querySelectorAll(`${ICON_SELECTOR}, a[data-ohw-href-key]`).forEach((el) => {
9820
+ const row = findSocialsRow(el);
9821
+ if (row) rows.add(row);
9822
+ });
9823
+ root.querySelectorAll(`[${SOCIALS_ROW_ATTR}]`).forEach((row) => rows.add(row));
9824
+ return Array.from(rows);
9825
+ }
9826
+ var rowTemplates = /* @__PURE__ */ new Map();
9827
+ function markSocialsRows(root = document) {
9828
+ root.querySelectorAll(`[${SOCIALS_ITEM_ATTR}]`).forEach((item) => {
9829
+ item.removeAttribute(SOCIALS_ITEM_ATTR);
9830
+ });
9831
+ listSocialsRows(root).forEach((row) => {
9832
+ row.setAttribute(SOCIALS_ROW_ATTR, "");
9833
+ const items = listSocialItems(row);
9834
+ if (items[0]) rowTemplates.set(rowKeyOf(row), socialRowUnit(items[0]).outerHTML);
9835
+ items.forEach((item, index) => {
9836
+ item.setAttribute(SOCIALS_ITEM_ATTR, String(index));
9837
+ const iconKey = socialIconKey(item);
9838
+ if (iconKey) ensureLabelSlot(item, iconKey);
9839
+ });
9840
+ });
9841
+ }
9842
+ var SOCIALS_LABEL_ATTR = "data-ohw-social-label";
9843
+ function ensureLabelSlot(item, iconKey) {
9844
+ if (socialLabelElement(item)) return;
9845
+ const label = document.createElement("span");
9846
+ label.setAttribute("data-ohw-key", `${iconKey}-label`);
9847
+ label.setAttribute("data-ohw-editable", "text");
9848
+ label.setAttribute(SOCIALS_LABEL_ATTR, "");
9849
+ label.style.display = "none";
9850
+ label.textContent = item.getAttribute("aria-label") ?? "";
9851
+ item.appendChild(label);
9852
+ }
9853
+ function socialLabelElement(item) {
9854
+ return item.querySelector(
9855
+ `[${SOCIALS_LABEL_ATTR}], [data-ohw-editable="text"], [data-ohw-editable="plain"]`
9856
+ );
9857
+ }
9858
+ function socialLabelKey(iconKey) {
9859
+ return `${iconKey}-label`;
9860
+ }
9861
+ function applyStoredValues(item, content) {
9862
+ const hrefKey = socialHrefKey(item);
9863
+ const iconKey = socialIconKey(item);
9864
+ if (hrefKey && content[hrefKey] !== void 0) item.setAttribute("href", content[hrefKey]);
9865
+ if (iconKey) {
9866
+ const glyph = item.querySelector(ICON_SELECTOR);
9867
+ if (glyph && content[iconKey]) applyIconMarkup(glyph, content[iconKey]);
9868
+ const label = item.querySelector(`[${SOCIALS_LABEL_ATTR}]`);
9869
+ label?.setAttribute("data-ohw-key", socialLabelKey(iconKey));
9870
+ const stored = content[socialLabelKey(iconKey)];
9871
+ if (label && stored) label.textContent = stored;
9872
+ }
9873
+ }
9874
+ function socialPlatformKey(iconKey) {
9875
+ return `${iconKey}-platform`;
9876
+ }
9877
+ function socialHrefKey(item) {
9878
+ return item.getAttribute("data-ohw-href-key");
9879
+ }
9880
+ function socialIconKey(item) {
9881
+ return item.querySelector(ICON_SELECTOR)?.dataset.ohwKey ?? null;
9882
+ }
9883
+ var SOCIALS_ORDER_KEY = "__ohw_socials_order";
9884
+ function fromMarkup(markup) {
9885
+ const holder = document.createElement("div");
9886
+ holder.innerHTML = markup;
9887
+ return holder.firstElementChild instanceof HTMLElement ? holder.firstElementChild : null;
9888
+ }
9889
+ var rowKeys = /* @__PURE__ */ new WeakMap();
9890
+ function rowKeyOf(row) {
9891
+ const first = listSocialItems(row)[0];
9892
+ const itemKey = first ? socialIconKey(first) ?? socialHrefKey(first)?.replace(/-href$/, "") : null;
9893
+ const derived = itemKey?.replace(/-[^-]+$/, "") || null;
9894
+ if (derived) rowKeys.set(row, derived);
9895
+ return derived ?? rowKeys.get(row) ?? "social";
9896
+ }
9897
+ function getSocialsOrderFromDom(root = document) {
9898
+ const order = {};
9899
+ listSocialsRows(root).forEach((row) => {
9900
+ order[rowKeyOf(row)] = listSocialItems(row).map((item) => socialHrefKey(item)).filter((key) => Boolean(key));
9901
+ });
9902
+ return order;
9903
+ }
9904
+ function hasStoredValue(content, hrefKey) {
9905
+ const iconKey = hrefKey.replace(/-href$/, "");
9906
+ return Boolean(content[hrefKey]) || Boolean(content[iconKey]);
9907
+ }
9908
+ function parseSocialsOrder(raw) {
9909
+ if (!raw) return null;
9910
+ try {
9911
+ const parsed = JSON.parse(raw);
9912
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
9913
+ } catch {
9914
+ return null;
9915
+ }
9916
+ }
9917
+ function nextSocialIndex(row, rowKey, content) {
9918
+ 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));
9919
+ return Math.max(-1, ...used) + 1;
9920
+ }
9921
+ function insertSocialItem(row, after, content = {}) {
9922
+ const rowKey = rowKeyOf(row);
9923
+ const template = listSocialItems(row)[0];
9924
+ const remembered = rowTemplates.get(rowKey);
9925
+ if (!template && !remembered) return null;
9926
+ const index = nextSocialIndex(row, rowKey, content);
9927
+ const iconKey = `${rowKey}-${index}`;
9928
+ const hrefKey = `${iconKey}-href`;
9929
+ const templateUnit = template ? socialRowUnit(template) : null;
9930
+ const unit = templateUnit ? templateUnit.cloneNode(true) : fromMarkup(remembered);
9931
+ const item = unit && (unit.matches("a") ? unit : unit.querySelector("a"));
9932
+ if (!unit || !item) return null;
9933
+ item.setAttribute("data-ohw-href-key", hrefKey);
9934
+ item.setAttribute("href", "");
9935
+ item.removeAttribute("aria-label");
9936
+ item.querySelectorAll("[data-ohw-hovered], [data-ohw-selected]").forEach((el) => {
9937
+ el.removeAttribute("data-ohw-hovered");
9938
+ el.removeAttribute("data-ohw-selected");
9939
+ });
9940
+ const icon = item.querySelector(ICON_SELECTOR);
9941
+ icon?.setAttribute("data-ohw-key", iconKey);
9942
+ item.querySelector(`[${SOCIALS_LABEL_ATTR}]`)?.remove();
9943
+ const afterUnit = after ? socialRowUnit(after) : null;
9944
+ if (afterUnit && afterUnit.parentElement === row) afterUnit.insertAdjacentElement("afterend", unit);
9945
+ else row.appendChild(unit);
9946
+ markSocialsRows(row.ownerDocument);
9947
+ return { item, hrefKey, iconKey, order: getSocialsOrderFromDom(row.ownerDocument) };
9948
+ }
9949
+ function duplicateSocialItem(item, content) {
9950
+ const row = findSocialsRow(item);
9951
+ const created = row ? insertSocialItem(row, item, content) : null;
9952
+ if (!created) return null;
9953
+ const sourceHref = socialHrefKey(item);
9954
+ const sourceIcon = socialIconKey(item);
9955
+ const link = created.item;
9956
+ if (sourceHref) link.setAttribute("href", item.getAttribute("href") ?? "");
9957
+ const glyph = item.querySelector(ICON_SELECTOR)?.innerHTML;
9958
+ if (glyph) {
9959
+ const slot = link.querySelector(ICON_SELECTOR);
9960
+ if (slot) slot.innerHTML = glyph;
9961
+ }
9962
+ return {
9963
+ ...created,
9964
+ copiedFrom: { href: sourceHref, icon: sourceIcon }
9965
+ };
9966
+ }
9967
+ function removeSocialItem(item, content) {
9968
+ const row = findSocialsRow(item);
9969
+ if (!row) return null;
9970
+ const hrefKey = socialHrefKey(item);
9971
+ const iconKey = socialIconKey(item);
9972
+ const removedKeys = [hrefKey, iconKey].filter((key) => Boolean(key));
9973
+ if (!removedKeys.length) return null;
9974
+ const previousOrder = getSocialsOrderFromDom(row.ownerDocument);
9975
+ const previousContent = Object.fromEntries(
9976
+ removedKeys.filter((key) => key in content).map((key) => [key, content[key]])
9977
+ );
9978
+ const unit = socialRowUnit(item);
9979
+ const nextSibling = unit.nextElementSibling;
9980
+ unit.remove();
9981
+ markSocialsRows(row.ownerDocument);
9982
+ return {
9983
+ removedKeys,
9984
+ previousContent,
9985
+ order: getSocialsOrderFromDom(row.ownerDocument),
9986
+ previousOrder,
9987
+ undo: () => {
9988
+ if (nextSibling) nextSibling.before(unit);
9989
+ else row.appendChild(unit);
9990
+ markSocialsRows(row.ownerDocument);
9991
+ }
9992
+ };
9993
+ }
9994
+ function applySocialsOrder(order, root = document) {
9995
+ listSocialsRows(root).forEach((row) => {
9996
+ const wanted = order[rowKeyOf(row)];
9997
+ if (!wanted) return;
9998
+ const byKey = new Map(listSocialItems(row).map((item) => [socialHrefKey(item), item]));
9999
+ wanted.forEach((key) => {
10000
+ const item = byKey.get(key);
10001
+ if (item) row.appendChild(socialRowUnit(item));
10002
+ });
10003
+ });
10004
+ markSocialsRows(root);
10005
+ }
10006
+ function reconcileSocialsFromContent(content, root = document) {
10007
+ markSocialsRows(root);
10008
+ const stored = parseSocialsOrder(content[SOCIALS_ORDER_KEY]);
10009
+ if (!stored) return;
10010
+ listSocialsRows(root).forEach((row) => {
10011
+ const wanted = stored[rowKeyOf(row)];
10012
+ if (!wanted) return;
10013
+ if (!wanted.length) return;
10014
+ wanted.forEach((key) => {
10015
+ if (listSocialItems(row).some((item) => socialHrefKey(item) === key)) return;
10016
+ if (!hasStoredValue(content, key)) return;
10017
+ const created = insertSocialItem(row, null, content);
10018
+ if (created) {
10019
+ created.item.setAttribute("data-ohw-href-key", key);
10020
+ created.item.querySelector(ICON_SELECTOR)?.setAttribute("data-ohw-key", key.replace(/-href$/, ""));
10021
+ applyStoredValues(created.item, content);
10022
+ }
10023
+ });
10024
+ const present = listSocialItems(row);
10025
+ const surviving = present.filter((item) => {
10026
+ const key = socialHrefKey(item);
10027
+ return !key || wanted.includes(key);
10028
+ });
10029
+ if (surviving.length) {
10030
+ present.forEach((item) => {
10031
+ if (!surviving.includes(item)) socialRowUnit(item).remove();
10032
+ });
10033
+ }
10034
+ });
10035
+ applySocialsOrder(stored, root);
10036
+ }
10037
+ var DROP_BAR_THICKNESS = 3;
10038
+ var DROP_BAR_GAP = 8;
10039
+ function buildSocialDropSlots(row) {
10040
+ const items = listSocialItems(row);
10041
+ if (!items.length) return [];
10042
+ const rects = items.map((item) => item.getBoundingClientRect());
10043
+ return items.concat(items[items.length - 1]).map((_, index) => {
10044
+ const previous = rects[index - 1];
10045
+ const next = rects[index];
10046
+ const centre = previous && next ? (previous.right + next.left) / 2 : next ? next.left - DROP_BAR_GAP : previous.right + DROP_BAR_GAP;
10047
+ const rect = next ?? previous;
10048
+ return {
10049
+ insertIndex: index,
10050
+ columnIndex: -1,
10051
+ left: centre - DROP_BAR_THICKNESS / 2,
10052
+ top: rect.top,
10053
+ width: DROP_BAR_THICKNESS,
10054
+ height: rect.height,
10055
+ direction: "vertical"
10056
+ };
10057
+ });
10058
+ }
10059
+ function findSocialByHrefKey(hrefKey, root = document) {
10060
+ const el = root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
10061
+ return el ? getSocialItem(el) : null;
10062
+ }
10063
+ function buildSocialDropSlotsForKey(hrefKey, root = document) {
10064
+ const item = findSocialByHrefKey(hrefKey, root);
10065
+ const row = item ? findSocialsRow(item) : null;
10066
+ return row ? buildSocialDropSlots(row) : [];
10067
+ }
10068
+ function hitTestSocialDropSlot(clientX, clientY, draggedHrefKey, root = document) {
10069
+ const distanceTo = (slot) => {
10070
+ const dx = clientX - (slot.left + slot.width / 2);
10071
+ const dy = clientY < slot.top ? slot.top - clientY : Math.max(0, clientY - (slot.top + slot.height));
10072
+ return Math.hypot(dx, dy);
10073
+ };
10074
+ const slots = buildSocialDropSlotsForKey(draggedHrefKey, root);
10075
+ return slots.reduce((best, slot) => {
10076
+ return !best || distanceTo(slot) < distanceTo(best) ? slot : best;
10077
+ }, null);
10078
+ }
10079
+ function planSocialMove(hrefKey, insertIndex, root = document) {
10080
+ const item = findSocialByHrefKey(hrefKey, root);
10081
+ const row = item ? findSocialsRow(item) : null;
10082
+ if (!row) return null;
10083
+ const order = getSocialsOrderFromDom(root);
10084
+ const key = rowKeyOf(row);
10085
+ const current = order[key];
10086
+ if (!current) return null;
10087
+ const from = current.indexOf(hrefKey);
10088
+ if (from < 0) return null;
10089
+ const next = current.filter((_, index) => index !== from);
10090
+ next.splice(insertIndex > from ? insertIndex - 1 : insertIndex, 0, hrefKey);
10091
+ return { ...order, [key]: next };
10092
+ }
10093
+ var SOCIALS_DISPLAY_KEY = "__ohw_socials_display";
10094
+ function readSocialsDisplay(row) {
10095
+ const items = listSocialItems(row);
10096
+ const visible = (el) => Boolean(el) && el.style.display !== "none" && el.getAttribute("data-ohw-hidden") === null;
10097
+ return {
10098
+ text: items.some((item) => visible(socialLabelElement(item))),
10099
+ icon: items.some((item) => visible(item.querySelector(ICON_SELECTOR)))
10100
+ };
10101
+ }
10102
+ function parseSocialsDisplay(raw) {
10103
+ if (!raw) return null;
10104
+ try {
10105
+ const parsed = JSON.parse(raw);
10106
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
10107
+ } catch {
10108
+ return null;
10109
+ }
10110
+ }
10111
+ function socialsDisplayFor(row, content) {
10112
+ return parseSocialsDisplay(content[SOCIALS_DISPLAY_KEY])?.[rowKeyOf(row)] ?? readSocialsDisplay(row);
10113
+ }
10114
+ function socialsDisplayWith(row, display, content) {
10115
+ return { ...parseSocialsDisplay(content[SOCIALS_DISPLAY_KEY]) ?? {}, [rowKeyOf(row)]: display };
10116
+ }
10117
+ function applySocialsDisplayToRow(row, display) {
10118
+ listSocialItems(row).forEach((item) => {
10119
+ const label = socialLabelElement(item);
10120
+ const icon = item.querySelector(ICON_SELECTOR);
10121
+ if (label) label.style.display = display.text ? "" : "none";
10122
+ if (icon) icon.style.display = display.icon ? "" : "none";
10123
+ });
10124
+ }
10125
+ function applySocialsDisplayFromContent(content, root = document) {
10126
+ const stored = parseSocialsDisplay(content[SOCIALS_DISPLAY_KEY]);
10127
+ if (!stored) return;
10128
+ listSocialsRows(root).forEach((row) => {
10129
+ const display = stored[rowKeyOf(row)];
10130
+ if (!display) return;
10131
+ if (display.icon) {
10132
+ listSocialItems(row).forEach((item) => {
10133
+ const iconKey = ensureIconSlot(item);
10134
+ const slot = item.querySelector(ICON_SELECTOR);
10135
+ if (iconKey && slot && content[iconKey]) applyIconMarkup(slot, content[iconKey]);
10136
+ });
10137
+ }
10138
+ applySocialsDisplayToRow(row, display);
10139
+ });
10140
+ }
10141
+ function socialsMissingIcons(row) {
10142
+ return listSocialItems(row).filter((item) => !item.querySelector(ICON_SELECTOR)).map((item) => ({ hrefKey: socialHrefKey(item) ?? "", url: item.getAttribute("href") ?? "" })).filter((entry) => Boolean(entry.hrefKey));
10143
+ }
10144
+ function ensureIconSlot(item) {
10145
+ const existing = item.querySelector(ICON_SELECTOR);
10146
+ if (existing) return existing.dataset.ohwKey ?? null;
10147
+ const hrefKey = socialHrefKey(item);
10148
+ if (!hrefKey) return null;
10149
+ const iconKey = hrefKey.replace(/-href$/, "");
10150
+ const slot = document.createElement("span");
10151
+ slot.setAttribute("data-ohw-key", iconKey);
10152
+ slot.setAttribute("data-ohw-editable", "icon");
10153
+ slot.style.display = "inline-flex";
10154
+ item.prepend(slot);
10155
+ return iconKey;
10156
+ }
10157
+
9528
10158
  // src/lib/footer-items.ts
9529
10159
  var FOOTER_ORDER_KEY = "__ohw_footer_order";
9530
10160
  var MAX_FOOTER_COLUMNS = 18;
10161
+ var MAX_FOOTER_ITEMS_PER_COLUMN = 7;
9531
10162
  var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
9532
10163
  function parseFooterHrefKey(key) {
9533
10164
  if (!key) return null;
@@ -9745,6 +10376,13 @@ function getNextFooterColumnIndex() {
9745
10376
  function canAddFooterColumn() {
9746
10377
  return listFooterColumns().length < MAX_FOOTER_COLUMNS;
9747
10378
  }
10379
+ function canAddFooterItem(column) {
10380
+ return listFooterLinksInColumn(column).length < MAX_FOOTER_ITEMS_PER_COLUMN;
10381
+ }
10382
+ function resolveFooterColumnForAdd(selected) {
10383
+ if (selected.hasAttribute("data-ohw-footer-col")) return selected;
10384
+ return selected.closest("[data-ohw-footer-col]") ?? (listFooterColumns().includes(selected) ? selected : null);
10385
+ }
9748
10386
  function buildFooterHeading(colIndex, text) {
9749
10387
  const heading = document.createElement("p");
9750
10388
  heading.setAttribute("data-ohw-editable", "text");
@@ -10369,6 +11007,329 @@ function deleteFooterColumn(column) {
10369
11007
  };
10370
11008
  }
10371
11009
 
11010
+ // src/lib/logo-identity.ts
11011
+ var LOGO_TEXT_KEYS = ["nav-logo-text", "footer-logo-text", "logo-text"];
11012
+ var LOGO_IMAGE_KEYS = ["nav-logo-image", "footer-logo", "footer-logo-image"];
11013
+ var LOGO_HREF_KEYS = ["nav-logo-href", "footer-logo-href", "logo-href"];
11014
+ var LOGO_PLACEHOLDER_KEY = "logo-is-placeholder";
11015
+ var LOGO_ALT_KEY = "logo-alt";
11016
+ var LOGO_IMAGE_URL_KEY = "nav-logo-image";
11017
+ var PLACEHOLDER_BUSINESS_NAME = "Business name";
11018
+ function resolveLogoDisplayText(text) {
11019
+ const trimmed = (text ?? "").trim();
11020
+ return trimmed || PLACEHOLDER_BUSINESS_NAME;
11021
+ }
11022
+ function isFooterLogoRoot(root) {
11023
+ return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
11024
+ }
11025
+ function imageKeyForRoot(root) {
11026
+ return isFooterLogoRoot(root) ? "footer-logo" : "nav-logo-image";
11027
+ }
11028
+ function textKeyForRoot(root) {
11029
+ return isFooterLogoRoot(root) ? "footer-logo-text" : "nav-logo-text";
11030
+ }
11031
+ function ensureLogoHrefKey(root) {
11032
+ if (!(root instanceof HTMLAnchorElement)) return;
11033
+ if (root.hasAttribute("data-ohw-href-key")) return;
11034
+ root.setAttribute("data-ohw-href-key", isFooterLogoRoot(root) ? "footer-logo-href" : "nav-logo-href");
11035
+ }
11036
+ function applyLogoIdentity(text, isPlaceholder) {
11037
+ const display = resolveLogoDisplayText(text);
11038
+ const placeholder = isPlaceholder || !text.trim() || display === PLACEHOLDER_BUSINESS_NAME;
11039
+ for (const key of LOGO_TEXT_KEYS) {
11040
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11041
+ if (el.textContent !== display) el.textContent = display;
11042
+ });
11043
+ }
11044
+ for (const key of LOGO_IMAGE_KEYS) {
11045
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11046
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
11047
+ if (img) img.alt = display;
11048
+ });
11049
+ }
11050
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((el) => {
11051
+ if (placeholder) el.setAttribute("data-ohw-placeholder", "");
11052
+ else el.removeAttribute("data-ohw-placeholder");
11053
+ });
11054
+ return display;
11055
+ }
11056
+ function applyLogoImage(url, alt) {
11057
+ const displayAlt = resolveLogoDisplayText(alt);
11058
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11059
+ ensureLogoHrefKey(root);
11060
+ const imageKey = imageKeyForRoot(root);
11061
+ const textKey = textKeyForRoot(root);
11062
+ 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");
11063
+ let textEl = root.querySelector(`[data-ohw-key="${textKey}"]`) ?? root.querySelector('[data-ohw-key="logo-text"]');
11064
+ if (url) {
11065
+ if (!img) {
11066
+ img = document.createElement("img");
11067
+ img.setAttribute("data-ohw-editable", "image");
11068
+ img.setAttribute("data-ohw-key", imageKey);
11069
+ img.alt = displayAlt;
11070
+ img.style.height = "";
11071
+ img.style.maxHeight = "none";
11072
+ img.style.width = "auto";
11073
+ img.style.display = "block";
11074
+ img.style.objectFit = "contain";
11075
+ root.insertBefore(img, root.firstChild);
11076
+ } else {
11077
+ img.setAttribute("data-ohw-editable", "image");
11078
+ img.setAttribute("data-ohw-key", imageKey);
11079
+ }
11080
+ img.removeAttribute("srcset");
11081
+ img.removeAttribute("sizes");
11082
+ img.src = url;
11083
+ img.alt = displayAlt;
11084
+ img.style.display = "block";
11085
+ if (textEl) textEl.style.display = "none";
11086
+ root.removeAttribute("data-ohw-placeholder");
11087
+ return;
11088
+ }
11089
+ if (img) {
11090
+ img.removeAttribute("src");
11091
+ img.removeAttribute("srcset");
11092
+ img.removeAttribute("sizes");
11093
+ img.alt = displayAlt;
11094
+ img.style.display = "none";
11095
+ }
11096
+ if (!textEl) {
11097
+ textEl = document.createElement("span");
11098
+ textEl.setAttribute("data-ohw-editable", "plain");
11099
+ textEl.setAttribute("data-ohw-key", textKey);
11100
+ root.appendChild(textEl);
11101
+ }
11102
+ textEl.style.display = "";
11103
+ if (textEl.textContent !== displayAlt) textEl.textContent = displayAlt;
11104
+ if (!displayAlt.trim() || displayAlt === PLACEHOLDER_BUSINESS_NAME) {
11105
+ root.setAttribute("data-ohw-placeholder", "");
11106
+ } else {
11107
+ root.removeAttribute("data-ohw-placeholder");
11108
+ }
11109
+ });
11110
+ }
11111
+ function applyLogoHref(href) {
11112
+ const target = href.trim() || "/";
11113
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11114
+ ensureLogoHrefKey(root);
11115
+ if (root instanceof HTMLAnchorElement) {
11116
+ root.setAttribute("href", target);
11117
+ }
11118
+ });
11119
+ for (const key of LOGO_HREF_KEYS) setStoredLinkHref(key, target);
11120
+ }
11121
+ function readLogoIdentityFromDom() {
11122
+ let imageUrl = null;
11123
+ for (const key of LOGO_IMAGE_KEYS) {
11124
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
11125
+ const img = el instanceof HTMLImageElement ? el : el?.querySelector("img");
11126
+ const attrSrc = img?.getAttribute("src")?.trim() ?? "";
11127
+ if (attrSrc && !attrSrc.startsWith("data:") && img && img.style.display !== "none") {
11128
+ imageUrl = img.currentSrc || img.src;
11129
+ break;
11130
+ }
11131
+ }
11132
+ let text = PLACEHOLDER_BUSINESS_NAME;
11133
+ let isPlaceholder = true;
11134
+ for (const key of LOGO_TEXT_KEYS) {
11135
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
11136
+ if (el?.textContent?.trim()) {
11137
+ text = el.textContent.trim();
11138
+ const logoRoot2 = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11139
+ isPlaceholder = logoRoot2?.hasAttribute("data-ohw-placeholder") === true || text === PLACEHOLDER_BUSINESS_NAME;
11140
+ break;
11141
+ }
11142
+ }
11143
+ if (imageUrl) {
11144
+ const logoImg = document.querySelector(
11145
+ '[data-ohw-key="nav-logo-image"], [data-ohw-key="footer-logo"]'
11146
+ );
11147
+ const alt = logoImg?.alt?.trim() || text;
11148
+ isPlaceholder = false;
11149
+ const hrefEl = document.querySelector(
11150
+ 'a[data-ohw-role="logo"], a[data-ohw-logo], [data-ohw-role="logo"]'
11151
+ );
11152
+ const href2 = (hrefEl instanceof HTMLAnchorElement ? hrefEl.getAttribute("href") : null) || hrefEl?.closest("a")?.getAttribute("href") || "/";
11153
+ return { text, isPlaceholder, imageUrl, href: href2, alt };
11154
+ }
11155
+ const logoRoot = document.querySelector('[data-ohw-role="logo"], [data-ohw-logo]');
11156
+ const href = (logoRoot instanceof HTMLAnchorElement ? logoRoot.getAttribute("href") : null) || logoRoot?.closest("a")?.getAttribute("href") || "/";
11157
+ return { text, isPlaceholder, imageUrl: null, href, alt: text };
11158
+ }
11159
+ function applyLogoFromContent(content) {
11160
+ 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);
11161
+ if (!hasLogoIdentity) return false;
11162
+ const logoText = content[LOGO_TEXT_KEYS[0]] ?? content[LOGO_TEXT_KEYS[1]] ?? readLogoIdentityFromDom().text;
11163
+ const logoAlt = content[LOGO_ALT_KEY] ?? logoText;
11164
+ const rawLogoImage = content[LOGO_IMAGE_URL_KEY] ?? content["footer-logo"] ?? content["footer-logo-image"] ?? null;
11165
+ const logoImageUrl = typeof rawLogoImage === "string" && rawLogoImage.trim() ? rawLogoImage.trim() : null;
11166
+ const imageExplicitlyCleared = LOGO_IMAGE_KEYS.some((key) => key in content) && !logoImageUrl;
11167
+ const logoIsPlaceholder = LOGO_PLACEHOLDER_KEY in content ? content[LOGO_PLACEHOLDER_KEY] !== "false" : !logoImageUrl && (!logoText.trim() || logoText === PLACEHOLDER_BUSINESS_NAME);
11168
+ if (logoImageUrl) {
11169
+ applyLogoImage(logoImageUrl, logoAlt);
11170
+ } else {
11171
+ if (imageExplicitlyCleared) applyLogoImage(null, logoAlt);
11172
+ applyLogoIdentity(logoText, logoIsPlaceholder);
11173
+ }
11174
+ const logoHref = content["nav-logo-href"] ?? content["footer-logo-href"] ?? content["logo-href"];
11175
+ if (typeof logoHref === "string" && logoHref.trim()) {
11176
+ applyLogoHref(logoHref);
11177
+ }
11178
+ return true;
11179
+ }
11180
+
11181
+ // src/lib/logo-size.ts
11182
+ var LOGO_SIZE_DEFAULTS = {
11183
+ navbar: 28,
11184
+ footer: 32
11185
+ };
11186
+ var LOGO_SIZE_MIN = 16;
11187
+ var LOGO_SIZE_MAX = 80;
11188
+ var LOGO_SIZE_DESKTOP_KEYS = {
11189
+ navbar: "nav-logo-size",
11190
+ footer: "footer-logo-size"
11191
+ };
11192
+ var LOGO_SIZE_MOBILE_KEYS = {
11193
+ navbar: "nav-logo-size-mobile",
11194
+ footer: "footer-logo-size-mobile"
11195
+ };
11196
+ var LOGO_SIZE_KEYS = [
11197
+ LOGO_SIZE_DESKTOP_KEYS.navbar,
11198
+ LOGO_SIZE_DESKTOP_KEYS.footer,
11199
+ LOGO_SIZE_MOBILE_KEYS.navbar,
11200
+ LOGO_SIZE_MOBILE_KEYS.footer
11201
+ ];
11202
+ function isFooterLogoRoot2(root) {
11203
+ return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
11204
+ }
11205
+ function getLogoPlacement(root) {
11206
+ return isFooterLogoRoot2(root) ? "footer" : "navbar";
11207
+ }
11208
+ function parseLogoSizePx(raw, fallback) {
11209
+ if (raw == null || raw === "") return fallback;
11210
+ const n = Number.parseFloat(raw);
11211
+ if (!Number.isFinite(n)) return fallback;
11212
+ return Math.min(LOGO_SIZE_MAX, Math.max(LOGO_SIZE_MIN, Math.round(n)));
11213
+ }
11214
+ function isMobileLogoSizeFollowing(content, placement) {
11215
+ const raw = content[LOGO_SIZE_MOBILE_KEYS[placement]];
11216
+ return raw == null || raw.trim() === "";
11217
+ }
11218
+ function resolveDesktopLogoSize(content, placement) {
11219
+ return parseLogoSizePx(content[LOGO_SIZE_DESKTOP_KEYS[placement]], LOGO_SIZE_DEFAULTS[placement]);
11220
+ }
11221
+ function resolveMobileLogoSize(content, placement) {
11222
+ if (isMobileLogoSizeFollowing(content, placement)) {
11223
+ return resolveDesktopLogoSize(content, placement);
11224
+ }
11225
+ return parseLogoSizePx(
11226
+ content[LOGO_SIZE_MOBILE_KEYS[placement]],
11227
+ resolveDesktopLogoSize(content, placement)
11228
+ );
11229
+ }
11230
+ function setRootSizeVars(root, desktopPx, mobilePx, following) {
11231
+ root.style.setProperty("--ohw-logo-size", `${desktopPx}px`);
11232
+ if (following) {
11233
+ root.style.removeProperty("--ohw-logo-size-mobile");
11234
+ } else {
11235
+ root.style.setProperty("--ohw-logo-size-mobile", `${mobilePx}px`);
11236
+ }
11237
+ root.querySelectorAll("img").forEach((img) => {
11238
+ img.style.height = "";
11239
+ img.style.maxHeight = "none";
11240
+ img.style.width = "auto";
11241
+ img.style.objectFit = "contain";
11242
+ });
11243
+ }
11244
+ function applyLogoSizes(content) {
11245
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11246
+ const placement = getLogoPlacement(root);
11247
+ const desktop = resolveDesktopLogoSize(content, placement);
11248
+ const following = isMobileLogoSizeFollowing(content, placement);
11249
+ const mobile = following ? desktop : resolveMobileLogoSize(content, placement);
11250
+ setRootSizeVars(root, desktop, mobile, following);
11251
+ });
11252
+ }
11253
+ function applyLogoSizeToPlacement(placement, desktopPx, mobilePx, following) {
11254
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11255
+ if (getLogoPlacement(root) !== placement) return;
11256
+ setRootSizeVars(root, desktopPx, mobilePx, following);
11257
+ });
11258
+ }
11259
+ function logoHasUploadedImage(logoEl) {
11260
+ if (logoEl.hasAttribute("data-ohw-placeholder")) return false;
11261
+ 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");
11262
+ if (!img) return false;
11263
+ const src = img.getAttribute("src")?.trim() ?? "";
11264
+ if (!src || src.startsWith("data:")) return false;
11265
+ if (img.style.display === "none") return false;
11266
+ return true;
11267
+ }
11268
+ function getLogoInteractionRect(logoEl) {
11269
+ if (logoHasUploadedImage(logoEl)) {
11270
+ 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");
11271
+ if (img) {
11272
+ const r2 = img.getBoundingClientRect();
11273
+ if (r2.width > 0 && r2.height > 0) return r2;
11274
+ }
11275
+ }
11276
+ const text = logoEl.querySelector(
11277
+ '[data-ohw-key="nav-logo-text"], [data-ohw-key="footer-logo-text"]'
11278
+ );
11279
+ if (text) {
11280
+ const style = window.getComputedStyle(text);
11281
+ if (style.display !== "none" && style.visibility !== "hidden") {
11282
+ const r2 = text.getBoundingClientRect();
11283
+ if (r2.width > 0 && r2.height > 0) return r2;
11284
+ }
11285
+ }
11286
+ return logoEl.getBoundingClientRect();
11287
+ }
11288
+ function readLogoSizeState(content, placement) {
11289
+ const desktopPx = resolveDesktopLogoSize(content, placement);
11290
+ const mobileFollowing = isMobileLogoSizeFollowing(content, placement);
11291
+ const mobilePx = mobileFollowing ? desktopPx : resolveMobileLogoSize(content, placement);
11292
+ return { desktopPx, mobilePx, mobileFollowing };
11293
+ }
11294
+
11295
+ // src/lib/site-wide-scope.ts
11296
+ function getLogoElement(el) {
11297
+ const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11298
+ if (marked) return marked;
11299
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
11300
+ const root = el.closest("nav, [data-ohw-nav-root], footer");
11301
+ if (!root) return null;
11302
+ const anchor = el.closest("a");
11303
+ if (anchor && root.contains(anchor) && !anchor.hasAttribute("data-ohw-href-key") && !anchor.closest("[data-ohw-nav-container]") && Boolean(anchor.querySelector("img") || anchor.matches("img"))) {
11304
+ return anchor;
11305
+ }
11306
+ const img = el.matches("img") ? el : null;
11307
+ if (img && !img.closest("[data-ohw-href-key]") && !img.closest("[data-ohw-nav-container]") && (img.closest("footer") || img.closest("nav, [data-ohw-nav-root]"))) {
11308
+ return img;
11309
+ }
11310
+ return null;
11311
+ }
11312
+ function isInFooter(el) {
11313
+ if (!el) return false;
11314
+ return Boolean(el.closest("footer") || el.closest('[data-ohw-section="footer"]'));
11315
+ }
11316
+ function isSiteWideElement(el) {
11317
+ if (!el) return false;
11318
+ if (getLogoElement(el)) return true;
11319
+ if (isInFooter(el)) return true;
11320
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-root")) {
11321
+ return true;
11322
+ }
11323
+ if (el.hasAttribute("data-ohw-href-key") && el.closest("nav, [data-ohw-nav-root], [data-ohw-nav-drawer], aside")) {
11324
+ return true;
11325
+ }
11326
+ if (el.closest('[data-ohw-role="navbar-button"]')) return true;
11327
+ return false;
11328
+ }
11329
+ function isSiteWideScopeActive(args) {
11330
+ return isSiteWideElement(args.selected) || isSiteWideElement(args.hoveredItem) || isSiteWideElement(args.hoveredNavContainer) || isSiteWideElement(args.active);
11331
+ }
11332
+
10372
11333
  // src/lib/add-footer-column.ts
10373
11334
  function buildFooterColumnEditContentPatch(result) {
10374
11335
  return {
@@ -10401,35 +11362,398 @@ function addFooterColumnWithPersist({
10401
11362
  return result;
10402
11363
  }
10403
11364
 
10404
- // src/lib/item-drag-interaction.ts
10405
- function disableNativeHrefDrag(el) {
10406
- if (el.draggable) el.draggable = false;
10407
- if (el.getAttribute("draggable") !== "false") {
10408
- el.setAttribute("draggable", "false");
11365
+ // src/ui/FloatingPanel.tsx
11366
+ var import_react13 = require("react");
11367
+ var import_lucide_react13 = require("lucide-react");
11368
+ var import_jsx_runtime26 = require("react/jsx-runtime");
11369
+ var PANEL_WIDTH = 256;
11370
+ var EDGE_MARGIN = 16;
11371
+ function getVisibleClip(parentScroll) {
11372
+ const left = 0;
11373
+ const right = window.innerWidth;
11374
+ if (!parentScroll) {
11375
+ return { top: 0, bottom: window.innerHeight, left, right };
10409
11376
  }
11377
+ const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
11378
+ const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
11379
+ const bottom = Math.min(window.innerHeight, visibleCanvasTop + canvasH - iframeOffsetTop);
11380
+ return { top, bottom: Math.max(top, bottom), left, right };
10410
11381
  }
10411
- function clearTextSelection() {
10412
- const sel = window.getSelection();
10413
- if (sel && !sel.isCollapsed) sel.removeAllRanges();
10414
- }
10415
- function armItemPressDrag() {
10416
- document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
10417
- }
10418
- function lockItemDuringDrag() {
10419
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
10420
- document.documentElement.setAttribute("data-ohw-item-dragging", "");
10421
- clearTextSelection();
11382
+ function defaultFloatingPanelPosition(parentScroll, panelHeight = 280) {
11383
+ const clip = getVisibleClip(parentScroll);
11384
+ return {
11385
+ x: Math.max(EDGE_MARGIN, clip.right - PANEL_WIDTH - EDGE_MARGIN),
11386
+ y: Math.min(
11387
+ Math.max(clip.top + EDGE_MARGIN, EDGE_MARGIN),
11388
+ Math.max(clip.top + EDGE_MARGIN, clip.bottom - panelHeight - EDGE_MARGIN)
11389
+ )
11390
+ };
10422
11391
  }
10423
- function unlockItemDragInteraction() {
10424
- const wasDragging = document.documentElement.hasAttribute("data-ohw-item-dragging");
10425
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
10426
- document.documentElement.removeAttribute("data-ohw-item-dragging");
10427
- if (wasDragging) clearTextSelection();
11392
+ function clampPosition(pos, parentScroll, panelW, panelH) {
11393
+ const clip = getVisibleClip(parentScroll);
11394
+ const maxX = Math.max(clip.left + EDGE_MARGIN, clip.right - panelW - EDGE_MARGIN);
11395
+ const maxY = Math.max(clip.top + EDGE_MARGIN, clip.bottom - panelH - EDGE_MARGIN);
11396
+ return {
11397
+ x: Math.min(Math.max(pos.x, clip.left + EDGE_MARGIN), maxX),
11398
+ y: Math.min(Math.max(pos.y, clip.top + EDGE_MARGIN), maxY)
11399
+ };
10428
11400
  }
10429
- var armFooterPressDrag = armItemPressDrag;
10430
- var lockFooterDuringDrag = lockItemDuringDrag;
10431
- var unlockFooterDragInteraction = unlockItemDragInteraction;
10432
-
11401
+ function FloatingPanel({
11402
+ open,
11403
+ title,
11404
+ context,
11405
+ icon,
11406
+ onClose,
11407
+ children,
11408
+ position,
11409
+ onPositionChange,
11410
+ parentScroll = null,
11411
+ className,
11412
+ bodyClassName
11413
+ }) {
11414
+ const panelRef = (0, import_react13.useRef)(null);
11415
+ const [measured, setMeasured] = (0, import_react13.useState)({ w: PANEL_WIDTH, h: 280 });
11416
+ const dragRef = (0, import_react13.useRef)(null);
11417
+ const resolved = position ?? defaultFloatingPanelPosition(parentScroll, measured.h);
11418
+ const clamped = clampPosition(resolved, parentScroll, measured.w, measured.h);
11419
+ (0, import_react13.useLayoutEffect)(() => {
11420
+ if (!open || !panelRef.current) return;
11421
+ const el = panelRef.current;
11422
+ const next = { w: el.offsetWidth || PANEL_WIDTH, h: el.offsetHeight || 280 };
11423
+ setMeasured((prev) => prev.w === next.w && prev.h === next.h ? prev : next);
11424
+ }, [open, children, title, context]);
11425
+ (0, import_react13.useEffect)(() => {
11426
+ if (!open || !position || !onPositionChange) return;
11427
+ const next = clampPosition(position, parentScroll, measured.w, measured.h);
11428
+ if (next.x !== position.x || next.y !== position.y) onPositionChange(next);
11429
+ }, [open, parentScroll, measured.w, measured.h, position, onPositionChange]);
11430
+ const onHeaderPointerDown = (0, import_react13.useCallback)(
11431
+ (e) => {
11432
+ if (e.button !== 0) return;
11433
+ if (e.target.closest("[data-ohw-floating-panel-close]")) return;
11434
+ e.preventDefault();
11435
+ e.stopPropagation();
11436
+ const el = e.currentTarget;
11437
+ el.setPointerCapture(e.pointerId);
11438
+ document.documentElement.setAttribute("data-ohw-panel-dragging", "");
11439
+ dragRef.current = {
11440
+ pointerId: e.pointerId,
11441
+ startX: e.clientX,
11442
+ startY: e.clientY,
11443
+ originX: clamped.x,
11444
+ originY: clamped.y
11445
+ };
11446
+ },
11447
+ [clamped.x, clamped.y]
11448
+ );
11449
+ const onHeaderPointerMove = (0, import_react13.useCallback)(
11450
+ (e) => {
11451
+ const drag = dragRef.current;
11452
+ if (!drag || drag.pointerId !== e.pointerId) return;
11453
+ e.preventDefault();
11454
+ const next = clampPosition(
11455
+ {
11456
+ x: drag.originX + (e.clientX - drag.startX),
11457
+ y: drag.originY + (e.clientY - drag.startY)
11458
+ },
11459
+ parentScroll,
11460
+ measured.w,
11461
+ measured.h
11462
+ );
11463
+ onPositionChange?.(next);
11464
+ },
11465
+ [measured.h, measured.w, onPositionChange, parentScroll]
11466
+ );
11467
+ const endDrag = (0, import_react13.useCallback)((e) => {
11468
+ const drag = dragRef.current;
11469
+ if (!drag || drag.pointerId !== e.pointerId) return;
11470
+ dragRef.current = null;
11471
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
11472
+ try {
11473
+ e.currentTarget.releasePointerCapture(e.pointerId);
11474
+ } catch {
11475
+ }
11476
+ }, []);
11477
+ (0, import_react13.useEffect)(() => {
11478
+ if (open) return;
11479
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
11480
+ }, [open]);
11481
+ (0, import_react13.useEffect)(() => () => document.documentElement.removeAttribute("data-ohw-panel-dragging"), []);
11482
+ if (!open) return null;
11483
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
11484
+ "div",
11485
+ {
11486
+ ref: panelRef,
11487
+ "data-ohw-floating-panel": "",
11488
+ role: "dialog",
11489
+ "aria-label": title,
11490
+ className: cn(
11491
+ // Above MediaOverlay / item chrome (2147483646); link-modal content shares this tier.
11492
+ "fixed z-[2147483647] flex w-64 flex-col overflow-hidden rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
11493
+ className
11494
+ ),
11495
+ style: { left: clamped.x, top: clamped.y },
11496
+ onMouseDown: (e) => e.stopPropagation(),
11497
+ onPointerDown: (e) => e.stopPropagation(),
11498
+ onClick: (e) => e.stopPropagation(),
11499
+ children: [
11500
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
11501
+ "div",
11502
+ {
11503
+ "data-ohw-floating-panel-header": "",
11504
+ className: "relative flex cursor-grab items-start gap-2 border-b border-border py-5 pl-5 pr-11 active:cursor-grabbing",
11505
+ onPointerDown: onHeaderPointerDown,
11506
+ onPointerMove: onHeaderPointerMove,
11507
+ onPointerUp: endDrag,
11508
+ onPointerCancel: endDrag,
11509
+ children: [
11510
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: [
11511
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center gap-2", children: [
11512
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "shrink-0 text-foreground", children: icon }) : null,
11513
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "min-w-0 flex-1 text-lg font-semibold leading-7 text-foreground", children: title })
11514
+ ] }),
11515
+ context ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "w-full text-sm leading-5 text-muted-foreground", children: context }) : null
11516
+ ] }),
11517
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
11518
+ "button",
11519
+ {
11520
+ type: "button",
11521
+ "data-ohw-floating-panel-close": "",
11522
+ "aria-label": "Close",
11523
+ className: "absolute right-2.5 top-2.5 rounded-sm p-1.5 text-foreground hover:bg-muted/50",
11524
+ onClick: (e) => {
11525
+ e.stopPropagation();
11526
+ onClose();
11527
+ },
11528
+ onPointerDown: (e) => e.stopPropagation(),
11529
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.X, { size: 16, "aria-hidden": true })
11530
+ }
11531
+ )
11532
+ ]
11533
+ }
11534
+ ),
11535
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
11536
+ "div",
11537
+ {
11538
+ "data-ohw-floating-panel-body": "",
11539
+ className: cn("flex w-full flex-col gap-4 p-5", bodyClassName),
11540
+ children
11541
+ }
11542
+ )
11543
+ ]
11544
+ }
11545
+ );
11546
+ }
11547
+
11548
+ // src/ui/logo-size-panel.tsx
11549
+ var import_lucide_react14 = require("lucide-react");
11550
+ var import_jsx_runtime27 = require("react/jsx-runtime");
11551
+ function SizeSlider({
11552
+ value,
11553
+ onChange
11554
+ }) {
11555
+ const pct = (value - LOGO_SIZE_MIN) / (LOGO_SIZE_MAX - LOGO_SIZE_MIN) * 100;
11556
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex w-full flex-col gap-3", children: [
11557
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex w-full items-center gap-2 text-sm font-medium leading-5", children: [
11558
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "min-w-0 flex-1 text-foreground", children: "Size" }),
11559
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "shrink-0 whitespace-nowrap text-muted-foreground", children: [
11560
+ value,
11561
+ " px"
11562
+ ] })
11563
+ ] }),
11564
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "relative h-2 w-full rounded-full bg-primary-50", children: [
11565
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11566
+ "div",
11567
+ {
11568
+ className: "absolute inset-y-0 left-0 rounded-full bg-primary",
11569
+ style: { width: `${pct}%` }
11570
+ }
11571
+ ),
11572
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11573
+ "input",
11574
+ {
11575
+ type: "range",
11576
+ min: LOGO_SIZE_MIN,
11577
+ max: LOGO_SIZE_MAX,
11578
+ step: 1,
11579
+ value,
11580
+ "aria-label": "Logo size",
11581
+ className: cn(
11582
+ "absolute inset-0 h-full w-full cursor-pointer appearance-none bg-transparent",
11583
+ "[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:size-5",
11584
+ "[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2",
11585
+ "[&::-webkit-slider-thumb]:border-primary [&::-webkit-slider-thumb]:bg-background",
11586
+ "[&::-moz-range-thumb]:size-5 [&::-moz-range-thumb]:rounded-full",
11587
+ "[&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-primary",
11588
+ "[&::-moz-range-thumb]:bg-background"
11589
+ ),
11590
+ onChange: (e) => onChange(Number(e.target.value))
11591
+ }
11592
+ )
11593
+ ] })
11594
+ ] });
11595
+ }
11596
+ function LogoSizePanel({
11597
+ viewport,
11598
+ sizePx,
11599
+ mobileFollowing = true,
11600
+ onSizeChange,
11601
+ onCustomizeMobile,
11602
+ onResetMobile,
11603
+ onUpdateEverywhere,
11604
+ className
11605
+ }) {
11606
+ const showFollowing = viewport === "mobile" && mobileFollowing;
11607
+ const showMobileSlider = viewport === "mobile" && !mobileFollowing;
11608
+ const showDesktopSlider = viewport === "desktop";
11609
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: cn("flex w-full flex-col gap-4", className), children: [
11610
+ showFollowing ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
11611
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-start gap-1", children: [
11612
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react14.Link, { size: 16, className: "mt-0.5 shrink-0 text-foreground", "aria-hidden": true }),
11613
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm font-semibold leading-5 text-foreground", children: "Following desktop size" })
11614
+ ] }),
11615
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm leading-5 text-muted-foreground", children: "Mobile uses the desktop size until you customize it. Change the desktop size and it follows automatically." }),
11616
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11617
+ Button,
11618
+ {
11619
+ type: "button",
11620
+ variant: "outline",
11621
+ size: "sm",
11622
+ className: "h-9 w-full min-w-0 cursor-pointer",
11623
+ onClick: onCustomizeMobile,
11624
+ children: "Customize for mobile"
11625
+ }
11626
+ )
11627
+ ] }) : null,
11628
+ showDesktopSlider || showMobileSlider ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SizeSlider, { value: sizePx, onChange: onSizeChange }) : null,
11629
+ showMobileSlider ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11630
+ Button,
11631
+ {
11632
+ type: "button",
11633
+ variant: "outline",
11634
+ size: "sm",
11635
+ className: "h-9 w-full min-w-0 cursor-pointer",
11636
+ onClick: onResetMobile,
11637
+ children: "Reset to desktop size"
11638
+ }
11639
+ ) : null,
11640
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-px w-full bg-border", role: "separator" }),
11641
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
11642
+ Button,
11643
+ {
11644
+ type: "button",
11645
+ variant: "outline",
11646
+ size: "sm",
11647
+ className: "h-9 w-full min-w-0 cursor-pointer gap-1",
11648
+ onClick: onUpdateEverywhere,
11649
+ children: [
11650
+ "Update logo everywhere",
11651
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react14.ArrowUpRight, { size: 16, "aria-hidden": true })
11652
+ ]
11653
+ }
11654
+ ),
11655
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm leading-5 text-muted-foreground", children: "Matches the right version to your background." })
11656
+ ] });
11657
+ }
11658
+
11659
+ // src/ui/socials-display-panel.tsx
11660
+ var import_jsx_runtime28 = require("react/jsx-runtime");
11661
+ function DisplaySwitch({
11662
+ label,
11663
+ checked,
11664
+ disabled,
11665
+ onChange
11666
+ }) {
11667
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex w-full items-center gap-2", children: [
11668
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11669
+ "span",
11670
+ {
11671
+ className: cn(
11672
+ "min-w-0 flex-1 text-sm font-medium leading-5",
11673
+ disabled ? "text-muted-foreground" : "text-foreground"
11674
+ ),
11675
+ children: label
11676
+ }
11677
+ ),
11678
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11679
+ "button",
11680
+ {
11681
+ type: "button",
11682
+ role: "switch",
11683
+ "aria-checked": checked,
11684
+ "aria-label": label,
11685
+ disabled,
11686
+ onClick: () => onChange(!checked),
11687
+ className: cn(
11688
+ "relative h-5 w-9 shrink-0 rounded-full transition-colors",
11689
+ checked ? "bg-primary" : "bg-primary-50",
11690
+ disabled ? "cursor-default opacity-50" : "cursor-pointer"
11691
+ ),
11692
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11693
+ "span",
11694
+ {
11695
+ className: cn(
11696
+ "absolute top-0.5 h-4 w-4 rounded-full bg-white transition-all",
11697
+ checked ? "left-[1.125rem]" : "left-0.5"
11698
+ )
11699
+ }
11700
+ )
11701
+ }
11702
+ )
11703
+ ] });
11704
+ }
11705
+ function SocialsDisplayPanel({ display, onChange, className }) {
11706
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: cn("flex w-full flex-col gap-3", className), children: [
11707
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11708
+ DisplaySwitch,
11709
+ {
11710
+ label: "Text",
11711
+ checked: display.text,
11712
+ disabled: display.text && !display.icon,
11713
+ onChange: (text) => onChange({ ...display, text })
11714
+ }
11715
+ ),
11716
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11717
+ DisplaySwitch,
11718
+ {
11719
+ label: "Icon",
11720
+ checked: display.icon,
11721
+ disabled: display.icon && !display.text,
11722
+ onChange: (icon) => onChange({ ...display, icon })
11723
+ }
11724
+ )
11725
+ ] });
11726
+ }
11727
+
11728
+ // src/lib/item-drag-interaction.ts
11729
+ function disableNativeHrefDrag(el) {
11730
+ if (el.draggable) el.draggable = false;
11731
+ if (el.getAttribute("draggable") !== "false") {
11732
+ el.setAttribute("draggable", "false");
11733
+ }
11734
+ }
11735
+ function clearTextSelection() {
11736
+ const sel = window.getSelection();
11737
+ if (sel && !sel.isCollapsed) sel.removeAllRanges();
11738
+ }
11739
+ function armItemPressDrag() {
11740
+ document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
11741
+ }
11742
+ function lockItemDuringDrag() {
11743
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
11744
+ document.documentElement.setAttribute("data-ohw-item-dragging", "");
11745
+ clearTextSelection();
11746
+ }
11747
+ function unlockItemDragInteraction() {
11748
+ const wasDragging = document.documentElement.hasAttribute("data-ohw-item-dragging");
11749
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
11750
+ document.documentElement.removeAttribute("data-ohw-item-dragging");
11751
+ if (wasDragging) clearTextSelection();
11752
+ }
11753
+ var armFooterPressDrag = armItemPressDrag;
11754
+ var lockFooterDuringDrag = lockItemDuringDrag;
11755
+ var unlockFooterDragInteraction = unlockItemDragInteraction;
11756
+
10433
11757
  // src/lib/nav-dnd.ts
10434
11758
  function listReorderableNavItems() {
10435
11759
  return listNavbarItems().filter((el) => {
@@ -10599,7 +11923,7 @@ function hitTestNavDropSlot(clientX, clientY, draggedHrefKey) {
10599
11923
  }
10600
11924
 
10601
11925
  // src/useNavItemDrag.ts
10602
- var import_react13 = require("react");
11926
+ var import_react14 = require("react");
10603
11927
  function useNavItemDrag({
10604
11928
  isEditMode,
10605
11929
  editContentRef,
@@ -10623,11 +11947,11 @@ function useNavItemDrag({
10623
11947
  getNavigationItemAnchor: getNavigationItemAnchor2,
10624
11948
  isDragHandleDisabled: isDragHandleDisabled2
10625
11949
  }) {
10626
- const navDragRef = (0, import_react13.useRef)(null);
10627
- const [navDropSlots, setNavDropSlots] = (0, import_react13.useState)([]);
10628
- const [activeNavDropIndex, setActiveNavDropIndex] = (0, import_react13.useState)(null);
10629
- const navPointerDragRef = (0, import_react13.useRef)(null);
10630
- const clearNavDragVisuals = (0, import_react13.useCallback)(() => {
11950
+ const navDragRef = (0, import_react14.useRef)(null);
11951
+ const [navDropSlots, setNavDropSlots] = (0, import_react14.useState)([]);
11952
+ const [activeNavDropIndex, setActiveNavDropIndex] = (0, import_react14.useState)(null);
11953
+ const navPointerDragRef = (0, import_react14.useRef)(null);
11954
+ const clearNavDragVisuals = (0, import_react14.useCallback)(() => {
10631
11955
  const session = navDragRef.current;
10632
11956
  const keepOpenEl = session?.draggedEl?.closest("[data-ohw-nav-children]") != null ? session.draggedEl : null;
10633
11957
  navDragRef.current = null;
@@ -10644,7 +11968,7 @@ function useNavItemDrag({
10644
11968
  document.documentElement.removeAttribute("data-ohw-nav-dragging-root");
10645
11969
  unlockItemDragInteraction();
10646
11970
  }, [setDraggedItemRect, setIsItemDragging, setSiblingHintRects]);
10647
- const refreshNavDragVisuals = (0, import_react13.useCallback)(
11971
+ const refreshNavDragVisuals = (0, import_react14.useCallback)(
10648
11972
  (session, activeSlot, clientX, clientY) => {
10649
11973
  setDraggedItemRect(session.draggedEl.getBoundingClientRect());
10650
11974
  if (typeof clientX === "number" && typeof clientY === "number") {
@@ -10662,13 +11986,13 @@ function useNavItemDrag({
10662
11986
  },
10663
11987
  [setDraggedItemRect, setSiblingHintRects]
10664
11988
  );
10665
- const refreshNavDragVisualsRef = (0, import_react13.useRef)(refreshNavDragVisuals);
11989
+ const refreshNavDragVisualsRef = (0, import_react14.useRef)(refreshNavDragVisuals);
10666
11990
  refreshNavDragVisualsRef.current = refreshNavDragVisuals;
10667
- const commitNavDragRef = (0, import_react13.useRef)(() => {
11991
+ const commitNavDragRef = (0, import_react14.useRef)(() => {
10668
11992
  });
10669
- const beginNavDragRef = (0, import_react13.useRef)(() => {
11993
+ const beginNavDragRef = (0, import_react14.useRef)(() => {
10670
11994
  });
10671
- const beginNavDrag = (0, import_react13.useCallback)(
11995
+ const beginNavDrag = (0, import_react14.useCallback)(
10672
11996
  (session) => {
10673
11997
  const rect = session.draggedEl.getBoundingClientRect();
10674
11998
  session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
@@ -10702,7 +12026,7 @@ function useNavItemDrag({
10702
12026
  ]
10703
12027
  );
10704
12028
  beginNavDragRef.current = beginNavDrag;
10705
- const commitNavDrag = (0, import_react13.useCallback)(
12029
+ const commitNavDrag = (0, import_react14.useCallback)(
10706
12030
  (clientX, clientY) => {
10707
12031
  const session = navDragRef.current;
10708
12032
  if (!session) {
@@ -10763,7 +12087,7 @@ function useNavItemDrag({
10763
12087
  [clearNavDragVisuals, deselectRef, editContentRef, postToParentRef, selectRef]
10764
12088
  );
10765
12089
  commitNavDragRef.current = commitNavDrag;
10766
- const startNavLinkDrag = (0, import_react13.useCallback)(
12090
+ const startNavLinkDrag = (0, import_react14.useCallback)(
10767
12091
  (anchor, clientX, clientY, wasSelected) => {
10768
12092
  if (footerDragRef.current) return false;
10769
12093
  const hrefKey = anchor.getAttribute("data-ohw-href-key");
@@ -10781,7 +12105,7 @@ function useNavItemDrag({
10781
12105
  },
10782
12106
  [beginNavDrag, footerDragRef, isDragHandleDisabled2, isCtaButton]
10783
12107
  );
10784
- const onNavDragOver = (0, import_react13.useCallback)(
12108
+ const onNavDragOver = (0, import_react14.useCallback)(
10785
12109
  (e) => {
10786
12110
  const session = navDragRef.current;
10787
12111
  if (!session) return false;
@@ -10793,7 +12117,7 @@ function useNavItemDrag({
10793
12117
  },
10794
12118
  []
10795
12119
  );
10796
- (0, import_react13.useEffect)(() => {
12120
+ (0, import_react14.useEffect)(() => {
10797
12121
  if (!isEditMode) return;
10798
12122
  const THRESHOLD = 10;
10799
12123
  const resolveWasSelected = (el) => {
@@ -10918,7 +12242,7 @@ function useNavItemDrag({
10918
12242
  setLinkPopover,
10919
12243
  suppressNextClickRef
10920
12244
  ]);
10921
- const armNavPressFromChrome = (0, import_react13.useCallback)(
12245
+ const armNavPressFromChrome = (0, import_react14.useCallback)(
10922
12246
  (selected, clientX, clientY, pointerId) => {
10923
12247
  const hrefKey = selected.getAttribute("data-ohw-href-key");
10924
12248
  if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
@@ -10949,8 +12273,8 @@ function useNavItemDrag({
10949
12273
  }
10950
12274
 
10951
12275
  // src/ui/footer-container-chrome.tsx
10952
- var import_lucide_react13 = require("lucide-react");
10953
- var import_jsx_runtime26 = require("react/jsx-runtime");
12276
+ var import_lucide_react15 = require("lucide-react");
12277
+ var import_jsx_runtime29 = require("react/jsx-runtime");
10954
12278
  function FooterContainerChrome({
10955
12279
  rect,
10956
12280
  onAdd,
@@ -10958,7 +12282,7 @@ function FooterContainerChrome({
10958
12282
  }) {
10959
12283
  const chromeGap = 6;
10960
12284
  const buttonMargin = 7;
10961
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
12285
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
10962
12286
  "div",
10963
12287
  {
10964
12288
  "data-ohw-footer-container-chrome": "",
@@ -10970,8 +12294,8 @@ function FooterContainerChrome({
10970
12294
  width: rect.width + chromeGap * 2,
10971
12295
  height: rect.height + chromeGap * 2
10972
12296
  },
10973
- children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Tooltip, { children: [
10974
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
12297
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(Tooltip, { children: [
12298
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
10975
12299
  "button",
10976
12300
  {
10977
12301
  type: "button",
@@ -10990,17 +12314,17 @@ function FooterContainerChrome({
10990
12314
  if (addDisabled) return;
10991
12315
  onAdd();
10992
12316
  },
10993
- children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12317
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
10994
12318
  }
10995
12319
  ) }),
10996
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
12320
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
10997
12321
  ] })
10998
12322
  }
10999
12323
  ) });
11000
12324
  }
11001
12325
 
11002
12326
  // src/lib/carousel.ts
11003
- var import_react14 = require("react");
12327
+ var import_react15 = require("react");
11004
12328
  var CAROUSEL_ATTR = "data-ohw-carousel";
11005
12329
  var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
11006
12330
  var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
@@ -11062,8 +12386,8 @@ function applyCarouselNode(key, val) {
11062
12386
  return true;
11063
12387
  }
11064
12388
  function useOhwCarousel(key, initial) {
11065
- const [images, setImages] = (0, import_react14.useState)(initial);
11066
- (0, import_react14.useEffect)(() => {
12389
+ const [images, setImages] = (0, import_react15.useState)(initial);
12390
+ (0, import_react15.useEffect)(() => {
11067
12391
  const el = document.querySelector(
11068
12392
  `[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`
11069
12393
  );
@@ -11176,6 +12500,18 @@ function collectEditableNodes(extraContent, root = document) {
11176
12500
  }
11177
12501
  if (extraContent && !isScoped) {
11178
12502
  applyNavFooterDeleteOverrides(byKey, extraContent);
12503
+ for (const key of LOGO_IMAGE_KEYS) {
12504
+ if (!(key in extraContent)) continue;
12505
+ byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
12506
+ }
12507
+ for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
12508
+ if (!(key in extraContent)) continue;
12509
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
12510
+ }
12511
+ for (const key of LOGO_SIZE_KEYS) {
12512
+ if (!(key in extraContent)) continue;
12513
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
12514
+ }
11179
12515
  }
11180
12516
  return Array.from(byKey.values());
11181
12517
  }
@@ -11278,7 +12614,7 @@ function isNavbarLinksContainer(el) {
11278
12614
  function isNavigationItem(el) {
11279
12615
  const anchor = el.matches("[data-ohw-href-key]") ? el : el.closest("[data-ohw-href-key]");
11280
12616
  if (!anchor) return false;
11281
- return Boolean(anchor.querySelector('[data-ohw-editable="text"]'));
12617
+ return Boolean(anchor.querySelector('[data-ohw-editable="text"]')) || Boolean(getSocialItem(anchor));
11282
12618
  }
11283
12619
  function findFooterItemGroup(item) {
11284
12620
  const explicit = item.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
@@ -11299,8 +12635,9 @@ function isInferredFooterGroup(el) {
11299
12635
  const footer = el.closest("footer");
11300
12636
  if (!footer || el === footer) return false;
11301
12637
  if (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column")) return true;
12638
+ if (isSocialsRow(el)) return false;
11302
12639
  const count = Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter(
11303
- isNavigationItem
12640
+ (item) => isNavigationItem(item) && !getSocialItem(item)
11304
12641
  ).length;
11305
12642
  return count >= 2;
11306
12643
  }
@@ -11344,7 +12681,8 @@ function deleteSelectedNavFooterItem(deps) {
11344
12681
  if (key.endsWith("-href")) applyLinkByKey2(key, text);
11345
12682
  else {
11346
12683
  document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`).forEach((el) => {
11347
- el.textContent = text;
12684
+ if (el.getAttribute("data-ohw-editable") === "icon") applyIconMarkup(el, text);
12685
+ else el.textContent = text;
11348
12686
  });
11349
12687
  }
11350
12688
  }
@@ -11406,6 +12744,21 @@ function deleteSelectedNavFooterItem(deps) {
11406
12744
  });
11407
12745
  return true;
11408
12746
  }
12747
+ const social = getSocialItem(selected);
12748
+ if (social) {
12749
+ const result = removeSocialItem(social, getEditContent());
12750
+ if (!result) return false;
12751
+ finishDelete({
12752
+ toastTitle: "Social deleted",
12753
+ removedKeys: result.removedKeys,
12754
+ previousContent: result.previousContent,
12755
+ orderKey: SOCIALS_ORDER_KEY,
12756
+ orderJson: JSON.stringify(result.order),
12757
+ previousOrderJson: JSON.stringify(result.previousOrder),
12758
+ undoDom: result.undo
12759
+ });
12760
+ return true;
12761
+ }
11409
12762
  if (isFooterHrefKey(hrefKey)) {
11410
12763
  const result = deleteFooterItem(selected);
11411
12764
  if (!result) return false;
@@ -11424,14 +12777,14 @@ function deleteSelectedNavFooterItem(deps) {
11424
12777
  }
11425
12778
 
11426
12779
  // src/ui/navbar-container-chrome.tsx
11427
- var import_lucide_react14 = require("lucide-react");
11428
- var import_jsx_runtime27 = require("react/jsx-runtime");
12780
+ var import_lucide_react16 = require("lucide-react");
12781
+ var import_jsx_runtime30 = require("react/jsx-runtime");
11429
12782
  function NavbarContainerChrome({
11430
12783
  rect,
11431
12784
  onAdd
11432
12785
  }) {
11433
12786
  const chromeGap = 6;
11434
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
12787
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
11435
12788
  "div",
11436
12789
  {
11437
12790
  "data-ohw-navbar-container-chrome": "",
@@ -11443,7 +12796,7 @@ function NavbarContainerChrome({
11443
12796
  width: rect.width + chromeGap * 2,
11444
12797
  height: rect.height + chromeGap * 2
11445
12798
  },
11446
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
12799
+ children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
11447
12800
  "button",
11448
12801
  {
11449
12802
  type: "button",
@@ -11460,7 +12813,7 @@ function NavbarContainerChrome({
11460
12813
  e.stopPropagation();
11461
12814
  onAdd();
11462
12815
  },
11463
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react14.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12816
+ children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react16.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
11464
12817
  }
11465
12818
  )
11466
12819
  }
@@ -11469,7 +12822,7 @@ function NavbarContainerChrome({
11469
12822
 
11470
12823
  // src/ui/drop-indicator.tsx
11471
12824
  var React10 = __toESM(require("react"), 1);
11472
- var import_jsx_runtime28 = require("react/jsx-runtime");
12825
+ var import_jsx_runtime31 = require("react/jsx-runtime");
11473
12826
  var dropIndicatorVariants = cva(
11474
12827
  "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
11475
12828
  {
@@ -11493,7 +12846,7 @@ var dropIndicatorVariants = cva(
11493
12846
  );
11494
12847
  var DropIndicator = React10.forwardRef(
11495
12848
  ({ className, direction, state, ...props }, ref) => {
11496
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
12849
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
11497
12850
  "div",
11498
12851
  {
11499
12852
  ref,
@@ -11510,7 +12863,7 @@ var DropIndicator = React10.forwardRef(
11510
12863
  DropIndicator.displayName = "DropIndicator";
11511
12864
 
11512
12865
  // src/ui/badge.tsx
11513
- var import_jsx_runtime29 = require("react/jsx-runtime");
12866
+ var import_jsx_runtime32 = require("react/jsx-runtime");
11514
12867
  var badgeVariants = cva(
11515
12868
  "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",
11516
12869
  {
@@ -11528,12 +12881,12 @@ var badgeVariants = cva(
11528
12881
  }
11529
12882
  );
11530
12883
  function Badge({ className, variant, ...props }) {
11531
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
12884
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
11532
12885
  }
11533
12886
 
11534
12887
  // src/OhhwellsBridge.tsx
11535
- var import_lucide_react15 = require("lucide-react");
11536
- var import_jsx_runtime30 = require("react/jsx-runtime");
12888
+ var import_lucide_react17 = require("lucide-react");
12889
+ var import_jsx_runtime33 = require("react/jsx-runtime");
11537
12890
  var PRIMARY3 = "#0885FE";
11538
12891
  var IMAGE_FADE_MS = 300;
11539
12892
  function runOpacityFade(el, onDone) {
@@ -11627,21 +12980,10 @@ function parseSchedulingInsertAfter(insertAfter) {
11627
12980
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
11628
12981
  };
11629
12982
  }
11630
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
11631
- const parsed = parseSchedulingInsertAfter(insertAfter);
11632
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
11633
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
11634
- return { effectiveInsertAfter, insertBefore };
11635
- }
11636
- function getSchedulingMountPoint(insertAfter) {
11637
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
11638
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
11639
- if (!anchorEl && anchor === "scheduling") {
11640
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
11641
- anchorEl = widgets.at(-1) ?? null;
11642
- }
11643
- if (!anchorEl) return null;
11644
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
12983
+ function resolveEntryAnchor(entry) {
12984
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
12985
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
12986
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
11645
12987
  }
11646
12988
  function schedulingMountDepth(insertAfter) {
11647
12989
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -11658,8 +13000,7 @@ function getPageSchedulingEntries(raw) {
11658
13000
  }
11659
13001
  }
11660
13002
  function isSchedulingWidgetMissing(entry) {
11661
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
11662
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
13003
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
11663
13004
  }
11664
13005
  function hasMissingSchedulingWidgets(entries) {
11665
13006
  return entries.some(isSchedulingWidgetMissing);
@@ -11689,16 +13030,17 @@ function initSectionsFromContent(content, removeExisting = false) {
11689
13030
  } catch {
11690
13031
  }
11691
13032
  }
11692
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
11693
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
11694
- const sectionId = schedulingSectionId(effectiveInsertAfter);
13033
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
13034
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
13035
+ const sectionId = schedulingSectionId(widgetId);
11695
13036
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
11696
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
11697
- if (!mountPoint) return false;
13037
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
13038
+ if (!anchorEl) return false;
13039
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
11698
13040
  const container = document.createElement("div");
11699
13041
  container.dataset.ohwSectionContainer = "scheduling";
11700
- if (insertBefore) {
11701
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
13042
+ if (beforeId) {
13043
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
11702
13044
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
11703
13045
  if (!beforePoint) return false;
11704
13046
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -11709,19 +13051,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
11709
13051
  }
11710
13052
  tail.insertAdjacentElement("afterend", container);
11711
13053
  }
11712
- const root = (0, import_client2.createRoot)(container);
11713
- (0, import_react_dom3.flushSync)(() => {
11714
- root.render(
11715
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
11716
- SchedulingWidget,
11717
- {
11718
- notifyOnConnect,
11719
- initialScheduleId: scheduleId,
11720
- insertAfter: effectiveInsertAfter
11721
- }
11722
- )
11723
- );
11724
- });
13054
+ try {
13055
+ const root = (0, import_client2.createRoot)(container);
13056
+ (0, import_react_dom3.flushSync)(() => {
13057
+ root.render(
13058
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13059
+ SchedulingWidget,
13060
+ {
13061
+ notifyOnConnect,
13062
+ initialScheduleId: scheduleId,
13063
+ insertAfter: widgetId
13064
+ }
13065
+ )
13066
+ );
13067
+ });
13068
+ } catch (err) {
13069
+ console.error("[ow:scheduling] render threw", err);
13070
+ container.remove();
13071
+ return false;
13072
+ }
11725
13073
  const tracker = getSectionsTracker();
11726
13074
  let sections = [];
11727
13075
  try {
@@ -11729,10 +13077,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
11729
13077
  } catch {
11730
13078
  }
11731
13079
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
11732
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
13080
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
11733
13081
  sections.push({
11734
13082
  type: "scheduling",
11735
- insertAfter: effectiveInsertAfter,
13083
+ insertAfter: widgetId,
13084
+ anchorId,
13085
+ beforeId: beforeId ?? null,
11736
13086
  pagePath: window.location.pathname,
11737
13087
  ...scheduleId ? { scheduleId } : {}
11738
13088
  });
@@ -11746,7 +13096,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
11746
13096
  for (let i = pending.length - 1; i >= 0; i--) {
11747
13097
  const entry = pending[i];
11748
13098
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
11749
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
13099
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
13100
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
11750
13101
  pending.splice(i, 1);
11751
13102
  }
11752
13103
  }
@@ -11760,6 +13111,17 @@ function applyLinkHref(el, val) {
11760
13111
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
11761
13112
  if (anchor) anchor.setAttribute("href", val);
11762
13113
  }
13114
+ function currentIconRef(el) {
13115
+ const uploaded = el instanceof HTMLImageElement ? el : el.querySelector("img");
13116
+ if (uploaded?.getAttribute("src")) {
13117
+ return uploaded.getAttribute("src") ?? "";
13118
+ }
13119
+ const svg = el instanceof SVGElement ? el : el.querySelector("svg");
13120
+ const named = Array.from(svg?.classList ?? []).find(
13121
+ (c) => c.startsWith("lucide-") && c !== "lucide-icon"
13122
+ );
13123
+ return named ? `lucide:${named.slice("lucide-".length)}` : "";
13124
+ }
11763
13125
  function getEditMeasureEl(editable) {
11764
13126
  return editable.closest("[data-ohw-href-key]") ?? editable;
11765
13127
  }
@@ -11804,8 +13166,11 @@ function isMediaEditable(el) {
11804
13166
  const t = el.dataset.ohwEditable;
11805
13167
  return t === "image" || t === "bg-image" || t === "video";
11806
13168
  }
13169
+ function isIconEditable(el) {
13170
+ return el.dataset.ohwEditable === "icon";
13171
+ }
11807
13172
  var MEDIA_SELECTOR = '[data-ohw-editable="image"], [data-ohw-editable="bg-image"], [data-ohw-editable="video"]';
11808
- var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"])';
13173
+ 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"])';
11809
13174
  function getVideoEl2(el) {
11810
13175
  return el instanceof HTMLVideoElement ? el : el.querySelector("video");
11811
13176
  }
@@ -11876,6 +13241,13 @@ function isInsideLinkEditor(target) {
11876
13241
  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"]')
11877
13242
  );
11878
13243
  }
13244
+ function isInsideFloatingPanel(target) {
13245
+ return Boolean(target.closest("[data-ohw-floating-panel]"));
13246
+ }
13247
+ function isPointOverFloatingPanel(clientX, clientY) {
13248
+ const el = document.elementFromPoint(clientX, clientY);
13249
+ return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
13250
+ }
11879
13251
  function getHrefKeyFromElement(el) {
11880
13252
  if (!el) return null;
11881
13253
  const anchor = el.closest("[data-ohw-href-key]");
@@ -11923,13 +13295,29 @@ function isNavItemPointerTarget(el) {
11923
13295
  function getNavigationItemAnchor(el) {
11924
13296
  const anchor = el.matches("[data-ohw-href-key]") ? el : el.closest("[data-ohw-href-key]");
11925
13297
  if (!anchor) return null;
11926
- if (!anchor.querySelector('[data-ohw-editable="text"]')) return null;
13298
+ if (!anchor.querySelector('[data-ohw-editable="text"]') && !getSocialItem(anchor)) return null;
11927
13299
  if (!isNavItemPointerTarget(anchor)) return null;
11928
13300
  return anchor;
11929
13301
  }
11930
13302
  function isNavigationItem2(el) {
11931
13303
  return getNavigationItemAnchor(el) !== null;
11932
13304
  }
13305
+ function requestSocialDialog(anchor, post, content) {
13306
+ const item = getSocialItem(anchor);
13307
+ if (!item) return false;
13308
+ const iconKey = item.querySelector('[data-ohw-editable="icon"]')?.dataset.ohwKey ?? "";
13309
+ post({
13310
+ type: "ow:social-pick",
13311
+ hrefKey: item.getAttribute("data-ohw-href-key") ?? "",
13312
+ iconKey,
13313
+ url: getLinkHref4(item),
13314
+ iconStyle: detectIconStyle(item),
13315
+ // What was chosen last time. Guessing from the address instead reads as "Website" for anything
13316
+ // unrecognised, and for an item with no address at all — so a deliberate choice looked lost.
13317
+ platformId: content[socialPlatformKey(iconKey)] ?? ""
13318
+ });
13319
+ return true;
13320
+ }
11933
13321
  function listNavigationItems() {
11934
13322
  return Array.from(
11935
13323
  document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
@@ -11964,7 +13352,7 @@ function getNavigationRoot(el) {
11964
13352
  return el.closest("nav, footer, aside");
11965
13353
  }
11966
13354
  function countFooterNavItems(el) {
11967
- return Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter(isNavigationItem2).length;
13355
+ return Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter((item) => isNavigationItem2(item) && !getSocialItem(item)).length;
11968
13356
  }
11969
13357
  function findFooterItemGroup2(item) {
11970
13358
  const explicit = item.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
@@ -11985,10 +13373,11 @@ function isInferredFooterGroup2(el) {
11985
13373
  const footer = el.closest("footer");
11986
13374
  if (!footer || el === footer) return false;
11987
13375
  if (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column")) return true;
13376
+ if (isSocialsRow(el)) return false;
11988
13377
  return countFooterNavItems(el) >= 2;
11989
13378
  }
11990
13379
  function isNavigationContainer(el) {
11991
- 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);
13380
+ 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);
11992
13381
  }
11993
13382
  function isNavbarLinksContainer2(el) {
11994
13383
  return el.hasAttribute("data-ohw-nav-container");
@@ -11996,6 +13385,11 @@ function isNavbarLinksContainer2(el) {
11996
13385
  function getFooterColumn(el) {
11997
13386
  return el.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
11998
13387
  }
13388
+ function isFooterAddItemDisabled(selected) {
13389
+ if (!selected) return false;
13390
+ const column = resolveFooterColumnForAdd(selected);
13391
+ return column ? !canAddFooterItem(column) : false;
13392
+ }
11999
13393
  function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
12000
13394
  if (getNavigationItemAnchor(target)) return null;
12001
13395
  const column = getFooterColumn(target);
@@ -12071,6 +13465,8 @@ function resolveNavContainerSelectionTarget(target, clientX, clientY) {
12071
13465
  return null;
12072
13466
  }
12073
13467
  function getNavigationSelectionParent(el) {
13468
+ const socialsRow = findSocialsRow(el);
13469
+ if (socialsRow) return socialsRow;
12074
13470
  if (isNavigationItem2(el)) {
12075
13471
  const childrenRoot = el.closest("[data-ohw-nav-children]");
12076
13472
  if (childrenRoot) {
@@ -12089,13 +13485,17 @@ function getNavigationSelectionParent(el) {
12089
13485
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
12090
13486
  return getFooterLinksContainer();
12091
13487
  }
12092
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
13488
+ 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)) {
12093
13489
  return getNavigationRoot(el);
12094
13490
  }
12095
13491
  return null;
12096
13492
  }
12097
13493
  function collectNavigationItemSiblingHintRects(selected) {
12098
13494
  if (!isNavigationItem2(selected)) return [];
13495
+ const socialsRow = findSocialsRow(selected);
13496
+ if (socialsRow) {
13497
+ return listSocialItems(socialsRow).filter((item) => item !== selected).map((item) => item.getBoundingClientRect());
13498
+ }
12099
13499
  const footerColumn = getFooterColumn(selected);
12100
13500
  if (footerColumn) {
12101
13501
  return listFooterLinksInColumn(footerColumn).filter((link) => link !== selected).map((link) => link.getBoundingClientRect());
@@ -12303,6 +13703,9 @@ var ICONS = {
12303
13703
  var SELECTION_CHROME_GAP2 = 4;
12304
13704
  var TOOLBAR_STROKE_GAP2 = 4;
12305
13705
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
13706
+ var HOVER_STROKE_WIDTH = 1.5;
13707
+ var TEXT_HOVER_SIDE_PAD = 4;
13708
+ var CHROME_PRIMARY = `var(--ohw-primary, ${PRIMARY3})`;
12306
13709
  var TOOLBAR_GROUPS = [
12307
13710
  [
12308
13711
  { cmd: "bold", title: "Bold" },
@@ -12328,7 +13731,7 @@ function EditGlowChrome({
12328
13731
  hideHandle = false
12329
13732
  }) {
12330
13733
  const GAP = SELECTION_CHROME_GAP2;
12331
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
13734
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
12332
13735
  "div",
12333
13736
  {
12334
13737
  ref: elRef,
@@ -12343,7 +13746,7 @@ function EditGlowChrome({
12343
13746
  zIndex: 2147483646
12344
13747
  },
12345
13748
  children: [
12346
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
13749
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
12347
13750
  "div",
12348
13751
  {
12349
13752
  style: {
@@ -12356,7 +13759,7 @@ function EditGlowChrome({
12356
13759
  }
12357
13760
  }
12358
13761
  ),
12359
- reorderHrefKey && !hideHandle && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
13762
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
12360
13763
  "div",
12361
13764
  {
12362
13765
  "data-ohw-drag-handle-container": "",
@@ -12368,7 +13771,7 @@ function EditGlowChrome({
12368
13771
  transform: "translate(calc(-100% - 7px), -50%)",
12369
13772
  pointerEvents: dragDisabled ? "none" : "auto"
12370
13773
  },
12371
- children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
13774
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
12372
13775
  DragHandle,
12373
13776
  {
12374
13777
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -12551,9 +13954,9 @@ function FloatingToolbar({
12551
13954
  showEditLink,
12552
13955
  onEditLink
12553
13956
  }) {
12554
- const localRef = import_react15.default.useRef(null);
12555
- const [measuredW, setMeasuredW] = import_react15.default.useState(330);
12556
- const setRefs = import_react15.default.useCallback(
13957
+ const localRef = import_react16.default.useRef(null);
13958
+ const [measuredW, setMeasuredW] = import_react16.default.useState(330);
13959
+ const setRefs = import_react16.default.useCallback(
12557
13960
  (node) => {
12558
13961
  localRef.current = node;
12559
13962
  if (typeof elRef === "function") elRef(node);
@@ -12565,7 +13968,7 @@ function FloatingToolbar({
12565
13968
  },
12566
13969
  [elRef]
12567
13970
  );
12568
- import_react15.default.useLayoutEffect(() => {
13971
+ import_react16.default.useLayoutEffect(() => {
12569
13972
  const node = localRef.current;
12570
13973
  if (!node) return;
12571
13974
  const update = () => {
@@ -12578,7 +13981,7 @@ function FloatingToolbar({
12578
13981
  return () => ro.disconnect();
12579
13982
  }, [showEditLink, activeCommands]);
12580
13983
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
12581
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
13984
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
12582
13985
  "div",
12583
13986
  {
12584
13987
  ref: setRefs,
@@ -12590,12 +13993,12 @@ function FloatingToolbar({
12590
13993
  zIndex: 2147483647,
12591
13994
  pointerEvents: "auto"
12592
13995
  },
12593
- children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(CustomToolbar, { children: [
12594
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_react15.default.Fragment, { children: [
12595
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(CustomToolbarDivider, {}),
13996
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(CustomToolbar, { children: [
13997
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_react16.default.Fragment, { children: [
13998
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CustomToolbarDivider, {}),
12596
13999
  btns.map((btn) => {
12597
14000
  const isActive = activeCommands.has(btn.cmd);
12598
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
14001
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
12599
14002
  CustomToolbarButton,
12600
14003
  {
12601
14004
  title: btn.title,
@@ -12604,7 +14007,7 @@ function FloatingToolbar({
12604
14007
  e.preventDefault();
12605
14008
  onCommand(btn.cmd);
12606
14009
  },
12607
- children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
14010
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
12608
14011
  "svg",
12609
14012
  {
12610
14013
  width: "16",
@@ -12625,7 +14028,7 @@ function FloatingToolbar({
12625
14028
  );
12626
14029
  })
12627
14030
  ] }, gi)),
12628
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
14031
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
12629
14032
  CustomToolbarButton,
12630
14033
  {
12631
14034
  type: "button",
@@ -12639,7 +14042,7 @@ function FloatingToolbar({
12639
14042
  e.preventDefault();
12640
14043
  e.stopPropagation();
12641
14044
  },
12642
- children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react15.Link, { className: "size-4 shrink-0", "aria-hidden": true })
14045
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.Link, { className: "size-4 shrink-0", "aria-hidden": true })
12643
14046
  }
12644
14047
  ) : null
12645
14048
  ] })
@@ -12656,7 +14059,7 @@ function StateToggle({
12656
14059
  states,
12657
14060
  onStateChange
12658
14061
  }) {
12659
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
14062
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
12660
14063
  ToggleGroup,
12661
14064
  {
12662
14065
  "data-ohw-state-toggle": "",
@@ -12670,11 +14073,12 @@ function StateToggle({
12670
14073
  left: rect.right - 8,
12671
14074
  transform: "translateX(-100%)"
12672
14075
  },
12673
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
14076
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
12674
14077
  }
12675
14078
  );
12676
14079
  }
12677
14080
  var contentCache = /* @__PURE__ */ new Map();
14081
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
12678
14082
  function resolveSubdomain(subdomainFromQuery) {
12679
14083
  if (subdomainFromQuery) return subdomainFromQuery;
12680
14084
  if (typeof window !== "undefined") {
@@ -12697,8 +14101,8 @@ function OhhwellsBridge() {
12697
14101
  const router = (0, import_navigation3.useRouter)();
12698
14102
  const searchParams = (0, import_navigation3.useSearchParams)();
12699
14103
  const isEditMode = isEditSessionActive();
12700
- const [bridgeRoot, setBridgeRoot] = (0, import_react15.useState)(null);
12701
- (0, import_react15.useEffect)(() => {
14104
+ const [bridgeRoot, setBridgeRoot] = (0, import_react16.useState)(null);
14105
+ (0, import_react16.useEffect)(() => {
12702
14106
  const figtreeFontId = "ohw-figtree-font";
12703
14107
  if (!document.getElementById(figtreeFontId)) {
12704
14108
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -12727,110 +14131,159 @@ function OhhwellsBridge() {
12727
14131
  const subdomain = resolveSubdomain(subdomainFromQuery);
12728
14132
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
12729
14133
  useSavedLinkNavigation(isEditMode);
12730
- const postToParent2 = (0, import_react15.useCallback)((data) => {
14134
+ const postToParent2 = (0, import_react16.useCallback)((data) => {
12731
14135
  if (typeof window !== "undefined" && window.parent !== window) {
12732
14136
  window.parent.postMessage(data, "*");
12733
14137
  }
12734
14138
  }, []);
12735
- const [fetchState, setFetchState] = (0, import_react15.useState)("idle");
12736
- const autoSaveTimers = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
12737
- const activeElRef = (0, import_react15.useRef)(null);
12738
- const pointerHeldRef = (0, import_react15.useRef)(false);
12739
- const selectedElRef = (0, import_react15.useRef)(null);
12740
- const selectedHrefKeyRef = (0, import_react15.useRef)(null);
12741
- const selectedFooterColAttrRef = (0, import_react15.useRef)(null);
12742
- const originalContentRef = (0, import_react15.useRef)(null);
12743
- const activeStateElRef = (0, import_react15.useRef)(null);
12744
- const parentScrollRef = (0, import_react15.useRef)(null);
12745
- const visibleViewportRef = (0, import_react15.useRef)(null);
12746
- const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react15.useState)(null);
12747
- const attachVisibleViewport = (0, import_react15.useCallback)((node) => {
14139
+ const [fetchState, setFetchState] = (0, import_react16.useState)("idle");
14140
+ const autoSaveTimers = (0, import_react16.useRef)(/* @__PURE__ */ new Map());
14141
+ const activeElRef = (0, import_react16.useRef)(null);
14142
+ const pointerHeldRef = (0, import_react16.useRef)(false);
14143
+ const selectedElRef = (0, import_react16.useRef)(null);
14144
+ const selectedHrefKeyRef = (0, import_react16.useRef)(null);
14145
+ const selectedFooterColAttrRef = (0, import_react16.useRef)(null);
14146
+ const originalContentRef = (0, import_react16.useRef)(null);
14147
+ const activeStateElRef = (0, import_react16.useRef)(null);
14148
+ const parentScrollRef = (0, import_react16.useRef)(null);
14149
+ const visibleViewportRef = (0, import_react16.useRef)(null);
14150
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react16.useState)(null);
14151
+ const attachVisibleViewport = (0, import_react16.useCallback)((node) => {
12748
14152
  visibleViewportRef.current = node;
12749
14153
  setDialogPortalContainer(node);
12750
14154
  if (node) applyVisibleViewport(node, parentScrollRef.current);
12751
14155
  }, []);
12752
- const toolbarElRef = (0, import_react15.useRef)(null);
12753
- const glowElRef = (0, import_react15.useRef)(null);
12754
- const hoveredImageRef = (0, import_react15.useRef)(null);
12755
- const hoveredImageHasTextOverlapRef = (0, import_react15.useRef)(false);
12756
- const dragOverElRef = (0, import_react15.useRef)(null);
12757
- const [mediaHover, setMediaHover] = (0, import_react15.useState)(null);
12758
- const [carouselHover, setCarouselHover] = (0, import_react15.useState)(null);
12759
- const [uploadingRects, setUploadingRects] = (0, import_react15.useState)({});
12760
- const hoveredGapRef = (0, import_react15.useRef)(null);
12761
- const imageUnhoverTimerRef = (0, import_react15.useRef)(null);
12762
- const imageShowTimerRef = (0, import_react15.useRef)(null);
12763
- const editStylesRef = (0, import_react15.useRef)(null);
12764
- const activateRef = (0, import_react15.useRef)(() => {
14156
+ const toolbarElRef = (0, import_react16.useRef)(null);
14157
+ const glowElRef = (0, import_react16.useRef)(null);
14158
+ const hoveredImageRef = (0, import_react16.useRef)(null);
14159
+ const hoveredImageHasTextOverlapRef = (0, import_react16.useRef)(false);
14160
+ const dragOverElRef = (0, import_react16.useRef)(null);
14161
+ const [mediaHover, setMediaHover] = (0, import_react16.useState)(null);
14162
+ const [carouselHover, setCarouselHover] = (0, import_react16.useState)(null);
14163
+ const [uploadingRects, setUploadingRects] = (0, import_react16.useState)({});
14164
+ const hoveredGapRef = (0, import_react16.useRef)(null);
14165
+ const imageUnhoverTimerRef = (0, import_react16.useRef)(null);
14166
+ const imageShowTimerRef = (0, import_react16.useRef)(null);
14167
+ const editStylesRef = (0, import_react16.useRef)(null);
14168
+ const activateRef = (0, import_react16.useRef)(() => {
14169
+ });
14170
+ const deactivateRef = (0, import_react16.useRef)(() => {
14171
+ });
14172
+ const selectRef = (0, import_react16.useRef)(() => {
12765
14173
  });
12766
- const deactivateRef = (0, import_react15.useRef)(() => {
14174
+ const selectFrameRef = (0, import_react16.useRef)(() => {
12767
14175
  });
12768
- const selectRef = (0, import_react15.useRef)(() => {
14176
+ const selectLogoRef = (0, import_react16.useRef)(() => {
12769
14177
  });
12770
- const selectFrameRef = (0, import_react15.useRef)(() => {
14178
+ const openLogoSizePanelRef = (0, import_react16.useRef)(() => {
12771
14179
  });
12772
- const deselectRef = (0, import_react15.useRef)(() => {
14180
+ const deselectRef = (0, import_react16.useRef)(() => {
12773
14181
  });
12774
- const reselectNavigationItemRef = (0, import_react15.useRef)(() => {
14182
+ const closeFloatingPanelOnlyRef = (0, import_react16.useRef)(() => {
12775
14183
  });
12776
- const commitNavigationTextEditRef = (0, import_react15.useRef)(() => {
14184
+ const reselectNavigationItemRef = (0, import_react16.useRef)(() => {
12777
14185
  });
12778
- const handleDeleteSelectedRef = (0, import_react15.useRef)(() => false);
12779
- const runPendingDeleteUndoRef = (0, import_react15.useRef)(() => false);
12780
- const isFooterFrameSelectionRef = (0, import_react15.useRef)(false);
12781
- const refreshActiveCommandsRef = (0, import_react15.useRef)(() => {
14186
+ const commitNavigationTextEditRef = (0, import_react16.useRef)(() => {
12782
14187
  });
12783
- const postToParentRef = (0, import_react15.useRef)(postToParent2);
14188
+ const handleDeleteSelectedRef = (0, import_react16.useRef)(() => false);
14189
+ const runPendingDeleteUndoRef = (0, import_react16.useRef)(() => false);
14190
+ const isFooterFrameSelectionRef = (0, import_react16.useRef)(false);
14191
+ const refreshActiveCommandsRef = (0, import_react16.useRef)(() => {
14192
+ });
14193
+ const postToParentRef = (0, import_react16.useRef)(postToParent2);
12784
14194
  postToParentRef.current = postToParent2;
12785
- const aiSectionApiRef = (0, import_react15.useRef)(null);
12786
- const sectionsLoadedRef = (0, import_react15.useRef)(false);
12787
- const pendingScheduleConfigRequests = (0, import_react15.useRef)([]);
12788
- const [toolbarRect, setToolbarRect] = (0, import_react15.useState)(null);
12789
- const [toolbarVariant, setToolbarVariant] = (0, import_react15.useState)("none");
12790
- const toolbarVariantRef = (0, import_react15.useRef)("none");
14195
+ const aiSectionApiRef = (0, import_react16.useRef)(null);
14196
+ const sectionsLoadedRef = (0, import_react16.useRef)(false);
14197
+ const pendingScheduleConfigRequests = (0, import_react16.useRef)([]);
14198
+ const [toolbarRect, setToolbarRect] = (0, import_react16.useState)(null);
14199
+ const [toolbarVariant, setToolbarVariant] = (0, import_react16.useState)("none");
14200
+ const toolbarVariantRef = (0, import_react16.useRef)("none");
12791
14201
  toolbarVariantRef.current = toolbarVariant;
12792
- const [selectedIsCta, setSelectedIsCta] = (0, import_react15.useState)(false);
12793
- const [reorderHrefKey, setReorderHrefKey] = (0, import_react15.useState)(null);
12794
- const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react15.useState)(false);
12795
- const [toggleState, setToggleState] = (0, import_react15.useState)(null);
12796
- const [maxBadge, setMaxBadge] = (0, import_react15.useState)(null);
12797
- const [activeCommands, setActiveCommands] = (0, import_react15.useState)(/* @__PURE__ */ new Set());
12798
- const [sectionGap, setSectionGap] = (0, import_react15.useState)(null);
12799
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react15.useState)(false);
12800
- const hoveredNavContainerRef = (0, import_react15.useRef)(null);
12801
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react15.useState)(null);
12802
- const hoveredItemElRef = (0, import_react15.useRef)(null);
12803
- const [hoveredItemRect, setHoveredItemRect] = (0, import_react15.useState)(null);
12804
- const siblingHintElRef = (0, import_react15.useRef)(null);
12805
- const [siblingHintRect, setSiblingHintRect] = (0, import_react15.useState)(null);
12806
- const [siblingHintRects, setSiblingHintRects] = (0, import_react15.useState)([]);
12807
- const [isItemDragging, setIsItemDragging] = (0, import_react15.useState)(false);
12808
- const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react15.useState)(false);
14202
+ const [selectedIsCta, setSelectedIsCta] = (0, import_react16.useState)(false);
14203
+ const [selectedIsSocial, setSelectedIsSocial] = (0, import_react16.useState)(false);
14204
+ const [selectedIsSocialsRow, setSelectedIsSocialsRow] = (0, import_react16.useState)(false);
14205
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react16.useState)(null);
14206
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react16.useState)(false);
14207
+ const [toggleState, setToggleState] = (0, import_react16.useState)(null);
14208
+ const [maxBadge, setMaxBadge] = (0, import_react16.useState)(null);
14209
+ const [activeCommands, setActiveCommands] = (0, import_react16.useState)(/* @__PURE__ */ new Set());
14210
+ const [sectionGap, setSectionGap] = (0, import_react16.useState)(null);
14211
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react16.useState)(false);
14212
+ const hoveredNavContainerRef = (0, import_react16.useRef)(null);
14213
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react16.useState)(null);
14214
+ const hoveredItemElRef = (0, import_react16.useRef)(null);
14215
+ const [hoveredItemRect, setHoveredItemRect] = (0, import_react16.useState)(null);
14216
+ const [hoveredTextRect, setHoveredTextRect] = (0, import_react16.useState)(null);
14217
+ (0, import_react16.useEffect)(() => {
14218
+ const sync = () => {
14219
+ const el = document.querySelector(
14220
+ "[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key])"
14221
+ );
14222
+ const target = el && !el.closest("[data-ohw-href-key]") ? el : null;
14223
+ if (!target) {
14224
+ setHoveredTextRect(null);
14225
+ return;
14226
+ }
14227
+ const r2 = target.getBoundingClientRect();
14228
+ setHoveredTextRect(new DOMRect(r2.x - TEXT_HOVER_SIDE_PAD, r2.y, r2.width + TEXT_HOVER_SIDE_PAD * 2, r2.height));
14229
+ };
14230
+ const observer = new MutationObserver(sync);
14231
+ observer.observe(document.documentElement, {
14232
+ attributes: true,
14233
+ attributeFilter: ["data-ohw-hovered"],
14234
+ subtree: true
14235
+ });
14236
+ return () => observer.disconnect();
14237
+ }, []);
14238
+ const siblingHintElRef = (0, import_react16.useRef)(null);
14239
+ const [siblingHintRect, setSiblingHintRect] = (0, import_react16.useState)(null);
14240
+ const [siblingHintRects, setSiblingHintRects] = (0, import_react16.useState)([]);
14241
+ const [isItemDragging, setIsItemDragging] = (0, import_react16.useState)(false);
14242
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react16.useState)(false);
12809
14243
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
12810
- const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react15.useState)(null);
12811
- const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react15.useState)(null);
12812
- const footerDragRef = (0, import_react15.useRef)(null);
12813
- const [footerDropSlots, setFooterDropSlots] = (0, import_react15.useState)([]);
12814
- const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react15.useState)(null);
12815
- const [draggedItemRect, setDraggedItemRect] = (0, import_react15.useState)(null);
12816
- const footerPointerDragRef = (0, import_react15.useRef)(null);
12817
- const suppressNextClickRef = (0, import_react15.useRef)(false);
12818
- const suppressClickUntilRef = (0, import_react15.useRef)(0);
12819
- const [linkPopover, setLinkPopover] = (0, import_react15.useState)(null);
12820
- const linkPopoverSessionRef = (0, import_react15.useRef)(null);
12821
- const addNavAfterAnchorRef = (0, import_react15.useRef)(null);
12822
- const editContentRef = (0, import_react15.useRef)({});
12823
- const aiSectionsRef = (0, import_react15.useRef)("");
12824
- const pendingDeleteUndoRef = (0, import_react15.useRef)(null);
12825
- const [sitePages, setSitePages] = (0, import_react15.useState)([]);
12826
- const [sectionsByPath, setSectionsByPath] = (0, import_react15.useState)({});
12827
- const sectionsPrefetchGenRef = (0, import_react15.useRef)(0);
12828
- const setLinkPopoverRef = (0, import_react15.useRef)(setLinkPopover);
12829
- const linkPopoverPanelRef = (0, import_react15.useRef)(null);
12830
- const linkPopoverOpenRef = (0, import_react15.useRef)(false);
12831
- const linkPopoverGraceUntilRef = (0, import_react15.useRef)(0);
14244
+ const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react16.useState)(null);
14245
+ const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react16.useState)(null);
14246
+ const footerDragRef = (0, import_react16.useRef)(null);
14247
+ const [footerDropSlots, setFooterDropSlots] = (0, import_react16.useState)([]);
14248
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react16.useState)(null);
14249
+ const [draggedItemRect, setDraggedItemRect] = (0, import_react16.useState)(null);
14250
+ const footerPointerDragRef = (0, import_react16.useRef)(null);
14251
+ const suppressNextClickRef = (0, import_react16.useRef)(false);
14252
+ const suppressClickUntilRef = (0, import_react16.useRef)(0);
14253
+ const [linkPopover, setLinkPopover] = (0, import_react16.useState)(null);
14254
+ const linkPopoverSessionRef = (0, import_react16.useRef)(null);
14255
+ const addNavAfterAnchorRef = (0, import_react16.useRef)(null);
14256
+ const editContentRef = (0, import_react16.useRef)({});
14257
+ const aiSectionsRef = (0, import_react16.useRef)("");
14258
+ const brandKitRef = (0, import_react16.useRef)("");
14259
+ const pendingDeleteUndoRef = (0, import_react16.useRef)(null);
14260
+ const [floatingPanel, setFloatingPanel] = (0, import_react16.useState)(null);
14261
+ const floatingPanelOpenRef = (0, import_react16.useRef)(false);
14262
+ const setFloatingPanelRef = (0, import_react16.useRef)(setFloatingPanel);
14263
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react16.useState)(null);
14264
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react16.useState)(null);
14265
+ const [editorViewport, setEditorViewport] = (0, import_react16.useState)("desktop");
14266
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react16.useState)(null);
14267
+ const [sitePages, setSitePages] = (0, import_react16.useState)([]);
14268
+ const [sectionsByPath, setSectionsByPath] = (0, import_react16.useState)({});
14269
+ const sectionsPrefetchGenRef = (0, import_react16.useRef)(0);
14270
+ const setLinkPopoverRef = (0, import_react16.useRef)(setLinkPopover);
14271
+ const linkPopoverPanelRef = (0, import_react16.useRef)(null);
14272
+ const linkPopoverOpenRef = (0, import_react16.useRef)(false);
14273
+ const linkPopoverGraceUntilRef = (0, import_react16.useRef)(0);
12832
14274
  setLinkPopoverRef.current = setLinkPopover;
14275
+ setFloatingPanelRef.current = setFloatingPanel;
12833
14276
  linkPopoverSessionRef.current = linkPopover;
14277
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
14278
+ (0, import_react16.useEffect)(() => {
14279
+ const syncViewport = () => {
14280
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
14281
+ setEditorViewport((prev) => prev === next ? prev : next);
14282
+ };
14283
+ syncViewport();
14284
+ window.addEventListener("resize", syncViewport);
14285
+ return () => window.removeEventListener("resize", syncViewport);
14286
+ }, []);
12834
14287
  const {
12835
14288
  navDragRef,
12836
14289
  navDropSlots,
@@ -12866,7 +14319,7 @@ function OhhwellsBridge() {
12866
14319
  const bumpLinkPopoverGrace = () => {
12867
14320
  linkPopoverGraceUntilRef.current = Date.now() + 350;
12868
14321
  };
12869
- const runSectionsPrefetch = (0, import_react15.useCallback)((pages) => {
14322
+ const runSectionsPrefetch = (0, import_react16.useCallback)((pages) => {
12870
14323
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
12871
14324
  const gen = ++sectionsPrefetchGenRef.current;
12872
14325
  const paths = pages.map((p) => p.path);
@@ -12885,9 +14338,9 @@ function OhhwellsBridge() {
12885
14338
  );
12886
14339
  });
12887
14340
  }, [isEditMode, pathname]);
12888
- const runSectionsPrefetchRef = (0, import_react15.useRef)(runSectionsPrefetch);
14341
+ const runSectionsPrefetchRef = (0, import_react16.useRef)(runSectionsPrefetch);
12889
14342
  runSectionsPrefetchRef.current = runSectionsPrefetch;
12890
- (0, import_react15.useEffect)(() => {
14343
+ (0, import_react16.useEffect)(() => {
12891
14344
  if (!linkPopover) {
12892
14345
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
12893
14346
  return;
@@ -12915,7 +14368,7 @@ function OhhwellsBridge() {
12915
14368
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
12916
14369
  };
12917
14370
  }, [linkPopover, postToParent2]);
12918
- (0, import_react15.useEffect)(() => {
14371
+ (0, import_react16.useEffect)(() => {
12919
14372
  if (!isEditMode) return;
12920
14373
  const useFixtures = shouldUseDevFixtures();
12921
14374
  if (useFixtures) {
@@ -12939,14 +14392,14 @@ function OhhwellsBridge() {
12939
14392
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
12940
14393
  return () => window.removeEventListener("message", onSitePages);
12941
14394
  }, [isEditMode, postToParent2]);
12942
- (0, import_react15.useEffect)(() => {
14395
+ (0, import_react16.useEffect)(() => {
12943
14396
  if (!isEditMode || shouldUseDevFixtures()) return;
12944
14397
  void loadAllSectionsManifest().then((manifest) => {
12945
14398
  if (Object.keys(manifest).length === 0) return;
12946
14399
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
12947
14400
  });
12948
14401
  }, [isEditMode]);
12949
- (0, import_react15.useEffect)(() => {
14402
+ (0, import_react16.useEffect)(() => {
12950
14403
  const update = () => {
12951
14404
  const el = activeElRef.current ?? selectedElRef.current;
12952
14405
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -12970,10 +14423,10 @@ function OhhwellsBridge() {
12970
14423
  vvp.removeEventListener("resize", update);
12971
14424
  };
12972
14425
  }, []);
12973
- const refreshStateRules = (0, import_react15.useCallback)(() => {
14426
+ const refreshStateRules = (0, import_react16.useCallback)(() => {
12974
14427
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
12975
14428
  }, []);
12976
- const processConfigRequest = (0, import_react15.useCallback)((insertAfterVal) => {
14429
+ const processConfigRequest = (0, import_react16.useCallback)((insertAfterVal) => {
12977
14430
  const tracker = getSectionsTracker();
12978
14431
  let entries = [];
12979
14432
  try {
@@ -12996,7 +14449,7 @@ function OhhwellsBridge() {
12996
14449
  }
12997
14450
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
12998
14451
  }, [isEditMode]);
12999
- const deactivate = (0, import_react15.useCallback)(() => {
14452
+ const deactivate = (0, import_react16.useCallback)(() => {
13000
14453
  const el = activeElRef.current;
13001
14454
  if (!el) return;
13002
14455
  const key = el.dataset.ohwKey;
@@ -13029,17 +14482,19 @@ function OhhwellsBridge() {
13029
14482
  setToolbarShowEditLink(false);
13030
14483
  postToParent2({ type: "ow:exit-edit" });
13031
14484
  }, [postToParent2]);
13032
- const clearSelectedAttr = (0, import_react15.useCallback)(() => {
14485
+ const clearSelectedAttr = (0, import_react16.useCallback)(() => {
13033
14486
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
13034
14487
  el.removeAttribute("data-ohw-selected");
13035
14488
  });
13036
14489
  }, []);
13037
- const deselect = (0, import_react15.useCallback)(() => {
14490
+ const deselect = (0, import_react16.useCallback)(() => {
13038
14491
  clearSelectedAttr();
13039
14492
  selectedElRef.current = null;
13040
14493
  selectedHrefKeyRef.current = null;
13041
14494
  selectedFooterColAttrRef.current = null;
13042
14495
  setSelectedIsCta(false);
14496
+ setSelectedIsSocial(false);
14497
+ setSelectedIsSocialsRow(false);
13043
14498
  setReorderHrefKey(null);
13044
14499
  setReorderDragDisabled(false);
13045
14500
  setIsFooterFrameSelection(false);
@@ -13051,17 +14506,21 @@ function OhhwellsBridge() {
13051
14506
  setIsItemDragging(false);
13052
14507
  hoveredNavContainerRef.current = null;
13053
14508
  setHoveredNavContainerRect(null);
14509
+ hoveredItemElRef.current = null;
14510
+ setHoveredItemRect(null);
14511
+ setFloatingPanel(null);
14512
+ setLogoSizeDraft(null);
13054
14513
  if (!activeElRef.current) {
13055
14514
  setNavGroupForceOpen(null, false);
13056
14515
  setToolbarRect(null);
13057
14516
  setToolbarVariant("none");
13058
14517
  }
13059
14518
  }, [clearSelectedAttr]);
13060
- const markSelected = (0, import_react15.useCallback)((el) => {
14519
+ const markSelected = (0, import_react16.useCallback)((el) => {
13061
14520
  clearSelectedAttr();
13062
14521
  el.setAttribute("data-ohw-selected", "");
13063
14522
  }, [clearSelectedAttr]);
13064
- const resolveHrefKeyElement = (0, import_react15.useCallback)((hrefKey) => {
14523
+ const resolveHrefKeyElement = (0, import_react16.useCallback)((hrefKey) => {
13065
14524
  if (isFooterHrefKey(hrefKey)) {
13066
14525
  return document.querySelector(
13067
14526
  `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
@@ -13076,7 +14535,7 @@ function OhhwellsBridge() {
13076
14535
  `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
13077
14536
  );
13078
14537
  }, []);
13079
- const resyncSelectedNavigationItem = (0, import_react15.useCallback)(() => {
14538
+ const resyncSelectedNavigationItem = (0, import_react16.useCallback)(() => {
13080
14539
  const hrefKey = selectedHrefKeyRef.current;
13081
14540
  if (hrefKey) {
13082
14541
  const link = resolveHrefKeyElement(hrefKey);
@@ -13114,12 +14573,14 @@ function OhhwellsBridge() {
13114
14573
  );
13115
14574
  }
13116
14575
  }, [resolveHrefKeyElement]);
13117
- const reselectNavigationItem = (0, import_react15.useCallback)((navAnchor) => {
14576
+ const reselectNavigationItem = (0, import_react16.useCallback)((navAnchor) => {
13118
14577
  selectedElRef.current = navAnchor;
13119
14578
  selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
13120
14579
  selectedFooterColAttrRef.current = null;
13121
14580
  markSelected(navAnchor);
13122
14581
  setSelectedIsCta(isCtaButton(navAnchor));
14582
+ setSelectedIsSocial(Boolean(getSocialItem(navAnchor)));
14583
+ setSelectedIsSocialsRow(false);
13123
14584
  const isDropdownTrigger = !isNestedNavChild(navAnchor) && (navItemHasDropdownChildren(navAnchor) || navItemOwnsDropdownPanel(navAnchor));
13124
14585
  if (isNestedNavChild(navAnchor)) {
13125
14586
  setNavGroupForceOpen(navAnchor, true);
@@ -13143,7 +14604,7 @@ function OhhwellsBridge() {
13143
14604
  setToolbarShowEditLink(false);
13144
14605
  setActiveCommands(/* @__PURE__ */ new Set());
13145
14606
  }, [markSelected]);
13146
- const commitNavigationTextEdit = (0, import_react15.useCallback)((navAnchor) => {
14607
+ const commitNavigationTextEdit = (0, import_react16.useCallback)((navAnchor) => {
13147
14608
  const el = activeElRef.current;
13148
14609
  if (!el) return;
13149
14610
  const key = el.dataset.ohwKey;
@@ -13170,7 +14631,7 @@ function OhhwellsBridge() {
13170
14631
  postToParent2({ type: "ow:exit-edit" });
13171
14632
  reselectNavigationItem(navAnchor);
13172
14633
  }, [postToParent2, reselectNavigationItem]);
13173
- const handleAddTopLevelNavItem = (0, import_react15.useCallback)(() => {
14634
+ const handleAddTopLevelNavItem = (0, import_react16.useCallback)(() => {
13174
14635
  const items = listNavbarRootItems();
13175
14636
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
13176
14637
  deselectRef.current();
@@ -13182,7 +14643,7 @@ function OhhwellsBridge() {
13182
14643
  intent: "add-nav"
13183
14644
  });
13184
14645
  }, []);
13185
- const maybeWarnNavLinkDropdownConflict = (0, import_react15.useCallback)(
14646
+ const maybeWarnNavLinkDropdownConflict = (0, import_react16.useCallback)(
13186
14647
  (anchor) => {
13187
14648
  if (!isNavbarHrefKey(anchor.getAttribute("data-ohw-href-key"))) return;
13188
14649
  if (!navDropdownsOpenOnClick()) return;
@@ -13195,7 +14656,7 @@ function OhhwellsBridge() {
13195
14656
  },
13196
14657
  [postToParent2]
13197
14658
  );
13198
- const handleNavDropdownOpenChange = (0, import_react15.useCallback)((open) => {
14659
+ const handleNavDropdownOpenChange = (0, import_react16.useCallback)((open) => {
13199
14660
  const selected = selectedElRef.current;
13200
14661
  if (!selected || !isNavigationItem2(selected)) return;
13201
14662
  setNavGroupForceOpen(selected, open);
@@ -13207,7 +14668,7 @@ function OhhwellsBridge() {
13207
14668
  }
13208
14669
  });
13209
14670
  }, []);
13210
- const handleFooterHeadingVisibleChange = (0, import_react15.useCallback)(
14671
+ const handleFooterHeadingVisibleChange = (0, import_react16.useCallback)(
13211
14672
  (visible) => {
13212
14673
  const selected = selectedElRef.current;
13213
14674
  if (!selected || !isFooterFrameSelectionRef.current) return;
@@ -13231,7 +14692,7 @@ function OhhwellsBridge() {
13231
14692
  },
13232
14693
  [postToParent2]
13233
14694
  );
13234
- const enterEditOnNewItem = (0, import_react15.useCallback)((anchor) => {
14695
+ const enterEditOnNewItem = (0, import_react16.useCallback)((anchor) => {
13235
14696
  const label = anchor.querySelector('[data-ohw-editable="text"]');
13236
14697
  if (!label) {
13237
14698
  selectRef.current(anchor);
@@ -13240,14 +14701,42 @@ function OhhwellsBridge() {
13240
14701
  setNavGroupForceOpen(anchor, true);
13241
14702
  activateRef.current(label);
13242
14703
  }, []);
13243
- const handleAddChildItem = (0, import_react15.useCallback)(() => {
14704
+ const handleAddChildItem = (0, import_react16.useCallback)(() => {
13244
14705
  const selected = selectedElRef.current;
13245
14706
  if (!selected) return;
14707
+ const socialsRow = isSocialsRow(selected) ? selected : findSocialsRow(selected);
14708
+ if (socialsRow) {
14709
+ const after = getSocialItem(selected);
14710
+ const result2 = insertSocialItem(socialsRow, after, editContentRef.current);
14711
+ if (!result2) return;
14712
+ const orderJson = JSON.stringify(result2.order);
14713
+ applySocialsDisplayToRow(socialsRow, socialsDisplayFor(socialsRow, editContentRef.current));
14714
+ editContentRef.current = { ...editContentRef.current, [SOCIALS_ORDER_KEY]: orderJson };
14715
+ postToParent2({ type: "ow:change", nodes: [{ key: SOCIALS_ORDER_KEY, text: orderJson }] });
14716
+ postToParentRef.current({
14717
+ type: "ow:social-pick",
14718
+ hrefKey: result2.hrefKey,
14719
+ iconKey: result2.iconKey,
14720
+ url: "",
14721
+ iconStyle: detectIconStyle(result2.item),
14722
+ platformId: "",
14723
+ // Lets the editor undo the insert if the dialog is dismissed: an item that was never given
14724
+ // an address should not survive a Cancel.
14725
+ isNew: true
14726
+ });
14727
+ return;
14728
+ }
13246
14729
  if (toolbarVariantRef.current === "select-frame" && isFooterFrameSelection) {
13247
- if (!selected.hasAttribute("data-ohw-footer-col") && !selected.closest("[data-ohw-footer-col]")) {
13248
- }
13249
- const column = (selected.hasAttribute("data-ohw-footer-col") ? selected : null) ?? selected.closest("[data-ohw-footer-col]") ?? (listFooterColumns().includes(selected) ? selected : null);
14730
+ const column = resolveFooterColumnForAdd(selected);
13250
14731
  if (!column) return;
14732
+ if (!canAddFooterItem(column)) {
14733
+ postToParent2({
14734
+ type: "ow:toast",
14735
+ title: `Maximum ${MAX_FOOTER_ITEMS_PER_COLUMN} items per column`,
14736
+ toastType: "error"
14737
+ });
14738
+ return;
14739
+ }
13251
14740
  const result2 = insertFooterItem(column, "/", "New link", null);
13252
14741
  applyLinkByKey(result2.hrefKey, result2.href);
13253
14742
  document.querySelectorAll(`[data-ohw-key="${result2.labelKey}"]`).forEach((el) => {
@@ -13316,7 +14805,7 @@ function OhhwellsBridge() {
13316
14805
  enterEditOnNewItem(result.anchor);
13317
14806
  });
13318
14807
  }, [enterEditOnNewItem, isFooterFrameSelection, maybeWarnNavLinkDropdownConflict, postToParent2]);
13319
- const handleAddFooterColumn = (0, import_react15.useCallback)(() => {
14808
+ const handleAddFooterColumn = (0, import_react16.useCallback)(() => {
13320
14809
  if (!canAddFooterColumn()) {
13321
14810
  postToParent2({
13322
14811
  type: "ow:toast",
@@ -13337,7 +14826,7 @@ function OhhwellsBridge() {
13337
14826
  selectRef.current(result.firstLink);
13338
14827
  });
13339
14828
  }, [postToParent2]);
13340
- const clearFooterDragVisuals = (0, import_react15.useCallback)(() => {
14829
+ const clearFooterDragVisuals = (0, import_react16.useCallback)(() => {
13341
14830
  footerDragRef.current = null;
13342
14831
  setSiblingHintRects([]);
13343
14832
  setFooterDropSlots([]);
@@ -13346,7 +14835,7 @@ function OhhwellsBridge() {
13346
14835
  setIsItemDragging(false);
13347
14836
  unlockFooterDragInteraction();
13348
14837
  }, []);
13349
- const refreshFooterDragVisuals = (0, import_react15.useCallback)((session, activeSlot, clientX, clientY) => {
14838
+ const refreshFooterDragVisuals = (0, import_react16.useCallback)((session, activeSlot, clientX, clientY) => {
13350
14839
  const dragged = session.draggedEl;
13351
14840
  setDraggedItemRect(dragged.getBoundingClientRect());
13352
14841
  if (typeof clientX === "number" && typeof clientY === "number") {
@@ -13355,6 +14844,13 @@ function OhhwellsBridge() {
13355
14844
  }
13356
14845
  session.activeSlot = activeSlot;
13357
14846
  setSiblingHintRects([]);
14847
+ if (session.kind === "social") {
14848
+ const slots2 = session.hrefKey ? buildSocialDropSlotsForKey(session.hrefKey) : [];
14849
+ setFooterDropSlots(slots2);
14850
+ const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
14851
+ setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
14852
+ return;
14853
+ }
13358
14854
  if (session.kind === "link") {
13359
14855
  const columns = listFooterColumns();
13360
14856
  const slots2 = [];
@@ -13371,13 +14867,13 @@ function OhhwellsBridge() {
13371
14867
  const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
13372
14868
  setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
13373
14869
  }, []);
13374
- const refreshFooterDragVisualsRef = (0, import_react15.useRef)(refreshFooterDragVisuals);
14870
+ const refreshFooterDragVisualsRef = (0, import_react16.useRef)(refreshFooterDragVisuals);
13375
14871
  refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
13376
- const commitFooterDragRef = (0, import_react15.useRef)(() => {
14872
+ const commitFooterDragRef = (0, import_react16.useRef)(() => {
13377
14873
  });
13378
- const beginFooterDragRef = (0, import_react15.useRef)(() => {
14874
+ const beginFooterDragRef = (0, import_react16.useRef)(() => {
13379
14875
  });
13380
- const beginFooterDrag = (0, import_react15.useCallback)(
14876
+ const beginFooterDrag = (0, import_react16.useCallback)(
13381
14877
  (session) => {
13382
14878
  const rect = session.draggedEl.getBoundingClientRect();
13383
14879
  session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
@@ -13391,13 +14887,13 @@ function OhhwellsBridge() {
13391
14887
  if (session.wasSelected && selectedElRef.current === session.draggedEl) {
13392
14888
  setToolbarRect(rect);
13393
14889
  }
13394
- const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
14890
+ 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);
13395
14891
  refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
13396
14892
  },
13397
14893
  [refreshFooterDragVisuals]
13398
14894
  );
13399
14895
  beginFooterDragRef.current = beginFooterDrag;
13400
- const commitFooterDrag = (0, import_react15.useCallback)(
14896
+ const commitFooterDrag = (0, import_react16.useCallback)(
13401
14897
  (clientX, clientY) => {
13402
14898
  const session = footerDragRef.current;
13403
14899
  if (!session) {
@@ -13407,8 +14903,11 @@ function OhhwellsBridge() {
13407
14903
  const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
13408
14904
  const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
13409
14905
  let nextOrder = null;
13410
- const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
13411
- if (session.kind === "link" && session.hrefKey && slot) {
14906
+ let nextSocialsOrder = null;
14907
+ 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);
14908
+ if (session.kind === "social" && session.hrefKey && slot) {
14909
+ nextSocialsOrder = planSocialMove(session.hrefKey, slot.insertIndex);
14910
+ } else if (session.kind === "link" && session.hrefKey && slot) {
13412
14911
  nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
13413
14912
  } else if (session.kind === "column" && slot) {
13414
14913
  nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
@@ -13466,6 +14965,27 @@ function OhhwellsBridge() {
13466
14965
  }
13467
14966
  deselectRef.current();
13468
14967
  };
14968
+ if (nextSocialsOrder) {
14969
+ const orderJson = JSON.stringify(nextSocialsOrder);
14970
+ editContentRef.current = { ...editContentRef.current, [SOCIALS_ORDER_KEY]: orderJson };
14971
+ applySocialsOrder(nextSocialsOrder);
14972
+ postToParentRef.current({
14973
+ type: "ow:change",
14974
+ nodes: [{ key: SOCIALS_ORDER_KEY, text: orderJson }]
14975
+ });
14976
+ applySelectionAfterDrop();
14977
+ clearFooterDragVisuals();
14978
+ const reapply = nextSocialsOrder;
14979
+ requestAnimationFrame(() => {
14980
+ if (editContentRef.current[SOCIALS_ORDER_KEY] === orderJson) applySocialsOrder(reapply);
14981
+ applySelectionAfterDrop();
14982
+ requestAnimationFrame(() => {
14983
+ if (editContentRef.current[SOCIALS_ORDER_KEY] === orderJson) applySocialsOrder(reapply);
14984
+ resyncSelectedNavigationItem();
14985
+ });
14986
+ });
14987
+ return;
14988
+ }
13469
14989
  if (nextOrder) {
13470
14990
  const orderJson = JSON.stringify(nextOrder);
13471
14991
  editContentRef.current = {
@@ -13501,10 +15021,25 @@ function OhhwellsBridge() {
13501
15021
  [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
13502
15022
  );
13503
15023
  commitFooterDragRef.current = commitFooterDrag;
13504
- const startFooterLinkDrag = (0, import_react15.useCallback)(
15024
+ const startFooterLinkDrag = (0, import_react16.useCallback)(
13505
15025
  (anchor, clientX, clientY, wasSelected) => {
13506
15026
  const hrefKey = anchor.getAttribute("data-ohw-href-key");
13507
- if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
15027
+ if (!hrefKey) return false;
15028
+ if (getSocialItem(anchor)) {
15029
+ beginFooterDrag({
15030
+ kind: "social",
15031
+ hrefKey,
15032
+ columnEl: null,
15033
+ sourceColumnIndex: 0,
15034
+ wasSelected,
15035
+ draggedEl: anchor,
15036
+ lastClientX: clientX,
15037
+ lastClientY: clientY,
15038
+ activeSlot: null
15039
+ });
15040
+ return true;
15041
+ }
15042
+ if (!isFooterHrefKey(hrefKey)) return false;
13508
15043
  const column = findFooterColumnForLink(anchor);
13509
15044
  const columns = listFooterColumns();
13510
15045
  beginFooterDrag({
@@ -13522,7 +15057,7 @@ function OhhwellsBridge() {
13522
15057
  },
13523
15058
  [beginFooterDrag]
13524
15059
  );
13525
- const startFooterColumnDrag = (0, import_react15.useCallback)(
15060
+ const startFooterColumnDrag = (0, import_react16.useCallback)(
13526
15061
  (columnEl, clientX, clientY, wasSelected) => {
13527
15062
  const columns = listFooterColumns();
13528
15063
  const idx = columns.indexOf(columnEl);
@@ -13542,7 +15077,7 @@ function OhhwellsBridge() {
13542
15077
  },
13543
15078
  [beginFooterDrag]
13544
15079
  );
13545
- const handleItemDragStart = (0, import_react15.useCallback)(
15080
+ const handleItemDragStart = (0, import_react16.useCallback)(
13546
15081
  (e) => {
13547
15082
  const selected = selectedElRef.current;
13548
15083
  if (!selected) {
@@ -13562,7 +15097,7 @@ function OhhwellsBridge() {
13562
15097
  },
13563
15098
  [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
13564
15099
  );
13565
- const handleItemDragEnd = (0, import_react15.useCallback)(
15100
+ const handleItemDragEnd = (0, import_react16.useCallback)(
13566
15101
  (e) => {
13567
15102
  if (footerDragRef.current) {
13568
15103
  const x = e?.clientX;
@@ -13588,7 +15123,7 @@ function OhhwellsBridge() {
13588
15123
  },
13589
15124
  [commitFooterDrag, commitNavDrag, navDragRef]
13590
15125
  );
13591
- const handleItemChromePointerDown = (0, import_react15.useCallback)((e) => {
15126
+ const handleItemChromePointerDown = (0, import_react16.useCallback)((e) => {
13592
15127
  if (e.button !== 0) return;
13593
15128
  const selected = selectedElRef.current;
13594
15129
  if (!selected) return;
@@ -13619,7 +15154,7 @@ function OhhwellsBridge() {
13619
15154
  }
13620
15155
  if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
13621
15156
  }, [armNavPressFromChrome]);
13622
- const handleItemChromeClick = (0, import_react15.useCallback)((clientX, clientY) => {
15157
+ const handleItemChromeClick = (0, import_react16.useCallback)((clientX, clientY) => {
13623
15158
  if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
13624
15159
  suppressNextClickRef.current = false;
13625
15160
  return;
@@ -13632,7 +15167,7 @@ function OhhwellsBridge() {
13632
15167
  }, []);
13633
15168
  reselectNavigationItemRef.current = reselectNavigationItem;
13634
15169
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
13635
- const select = (0, import_react15.useCallback)((anchor) => {
15170
+ const select = (0, import_react16.useCallback)((anchor) => {
13636
15171
  if (!isNavigationItem2(anchor)) return;
13637
15172
  if (activeElRef.current) deactivate();
13638
15173
  aiSectionApiRef.current?.selectFromElement(anchor);
@@ -13641,6 +15176,8 @@ function OhhwellsBridge() {
13641
15176
  selectedFooterColAttrRef.current = null;
13642
15177
  markSelected(anchor);
13643
15178
  setSelectedIsCta(isCtaButton(anchor));
15179
+ setSelectedIsSocial(Boolean(getSocialItem(anchor)));
15180
+ setSelectedIsSocialsRow(false);
13644
15181
  clearHrefKeyHover(anchor);
13645
15182
  const isDropdownTrigger = !isNestedNavChild(anchor) && (navItemHasDropdownChildren(anchor) || navItemOwnsDropdownPanel(anchor));
13646
15183
  if (isNestedNavChild(anchor)) {
@@ -13670,8 +15207,10 @@ function OhhwellsBridge() {
13670
15207
  setToolbarRect(anchor.getBoundingClientRect());
13671
15208
  setToolbarShowEditLink(false);
13672
15209
  setActiveCommands(/* @__PURE__ */ new Set());
15210
+ setFloatingPanel(null);
15211
+ setLogoSizeDraft(null);
13673
15212
  }, [deactivate, markSelected]);
13674
- const selectFrame = (0, import_react15.useCallback)((el) => {
15213
+ const selectFrame = (0, import_react16.useCallback)((el) => {
13675
15214
  if (!isNavigationContainer(el)) return;
13676
15215
  if (activeElRef.current) deactivate();
13677
15216
  aiSectionApiRef.current?.selectFromElement(el);
@@ -13681,6 +15220,8 @@ function OhhwellsBridge() {
13681
15220
  selectedFooterColAttrRef.current = isFooterColumn ? el.getAttribute("data-ohw-footer-col") ?? String(listFooterColumns().indexOf(el)) : null;
13682
15221
  markSelected(el);
13683
15222
  setSelectedIsCta(false);
15223
+ setSelectedIsSocial(false);
15224
+ setSelectedIsSocialsRow(isSocialsRow(el));
13684
15225
  clearHrefKeyHover(el);
13685
15226
  setNavGroupForceOpen(null, false);
13686
15227
  hoveredNavContainerRef.current = null;
@@ -13709,22 +15250,153 @@ function OhhwellsBridge() {
13709
15250
  nodes: [{ key: ensured.headingKey, text: ensured.text }]
13710
15251
  });
13711
15252
  }
13712
- setFooterHeadingVisible(isFooterColumnHeadingVisible(el));
13713
- } else {
13714
- setFooterHeadingVisible(null);
13715
- }
13716
- setToolbarVariant("select-frame");
13717
- setToolbarRect(el.getBoundingClientRect());
13718
- setToolbarShowEditLink(false);
13719
- setActiveCommands(/* @__PURE__ */ new Set());
13720
- }, [deactivate, markSelected, postToParent2]);
13721
- const activate = (0, import_react15.useCallback)((el, options) => {
15253
+ setFooterHeadingVisible(isFooterColumnHeadingVisible(el));
15254
+ } else {
15255
+ setFooterHeadingVisible(null);
15256
+ }
15257
+ setToolbarVariant("select-frame");
15258
+ setToolbarRect(el.getBoundingClientRect());
15259
+ setToolbarShowEditLink(false);
15260
+ setActiveCommands(/* @__PURE__ */ new Set());
15261
+ setFloatingPanel(null);
15262
+ setLogoSizeDraft(null);
15263
+ }, [deactivate, markSelected, postToParent2]);
15264
+ const selectLogo = (0, import_react16.useCallback)(
15265
+ (logoEl) => {
15266
+ if (activeElRef.current) deactivate();
15267
+ selectedElRef.current = logoEl;
15268
+ selectedHrefKeyRef.current = null;
15269
+ selectedFooterColAttrRef.current = null;
15270
+ markSelected(logoEl);
15271
+ setSelectedIsCta(false);
15272
+ setSelectedIsSocial(false);
15273
+ setSelectedIsSocialsRow(false);
15274
+ setSelectedIsSocialsRow(false);
15275
+ clearHrefKeyHover(logoEl);
15276
+ hoveredNavContainerRef.current = null;
15277
+ setHoveredNavContainerRect(null);
15278
+ setHoveredItemRect(null);
15279
+ hoveredItemElRef.current = null;
15280
+ siblingHintElRef.current = null;
15281
+ setSiblingHintRect(null);
15282
+ setSiblingHintRects([]);
15283
+ setIsItemDragging(false);
15284
+ setReorderHrefKey(null);
15285
+ setReorderDragDisabled(false);
15286
+ setIsFooterFrameSelection(false);
15287
+ setToolbarVariant("logo");
15288
+ setToolbarRect(getLogoInteractionRect(logoEl));
15289
+ setToolbarShowEditLink(false);
15290
+ setActiveCommands(/* @__PURE__ */ new Set());
15291
+ },
15292
+ [deactivate, markSelected]
15293
+ );
15294
+ const openLogoSizePanel = (0, import_react16.useCallback)((logoEl) => {
15295
+ const placement = getLogoPlacement(logoEl);
15296
+ const draft = readLogoSizeState(editContentRef.current, placement);
15297
+ setLogoSizeDraft(draft);
15298
+ setParentScrollSnap(parentScrollRef.current);
15299
+ setFloatingPanel({
15300
+ key: `logo-size:${placement}`,
15301
+ title: "Logo",
15302
+ context: placement === "navbar" ? "Navbar" : "Footer",
15303
+ kind: "logo-size",
15304
+ placement
15305
+ });
15306
+ }, []);
15307
+ const openSocialsDisplayPanel = (0, import_react16.useCallback)((row) => {
15308
+ setParentScrollSnap(parentScrollRef.current);
15309
+ setFloatingPanel({
15310
+ key: "socials-display",
15311
+ title: "Style",
15312
+ context: "Socials \xB7 Footer",
15313
+ kind: "socials-display",
15314
+ row
15315
+ });
15316
+ }, []);
15317
+ const changeSocialsDisplay = (0, import_react16.useCallback)(
15318
+ (row, next) => {
15319
+ if (next.icon) {
15320
+ const missing = socialsMissingIcons(row);
15321
+ listSocialItems(row).forEach((item) => ensureIconSlot(item));
15322
+ if (missing.length) {
15323
+ postToParentRef.current({ type: "ow:social-icons-needed", items: missing });
15324
+ }
15325
+ }
15326
+ applySocialsDisplayToRow(row, next);
15327
+ requestAnimationFrame(() => {
15328
+ if (selectedElRef.current === row && row.isConnected) setToolbarRect(row.getBoundingClientRect());
15329
+ });
15330
+ const displayJson = JSON.stringify(socialsDisplayWith(row, next, editContentRef.current));
15331
+ editContentRef.current = { ...editContentRef.current, [SOCIALS_DISPLAY_KEY]: displayJson };
15332
+ postToParentRef.current({
15333
+ type: "ow:change",
15334
+ nodes: [{ key: SOCIALS_DISPLAY_KEY, text: displayJson }],
15335
+ flush: true
15336
+ });
15337
+ },
15338
+ []
15339
+ );
15340
+ const closeFloatingPanelOnly = (0, import_react16.useCallback)(() => {
15341
+ setFloatingPanel(null);
15342
+ setLogoSizeDraft(null);
15343
+ }, []);
15344
+ const closeFloatingPanelAndDeselect = (0, import_react16.useCallback)(() => {
15345
+ setFloatingPanel(null);
15346
+ setLogoSizeDraft(null);
15347
+ deselectRef.current();
15348
+ }, []);
15349
+ const persistLogoSizeDraft = (0, import_react16.useCallback)(
15350
+ (placement, draft) => {
15351
+ const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
15352
+ const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
15353
+ const nodes = [
15354
+ { key: desktopKey, text: String(draft.desktopPx) }
15355
+ ];
15356
+ if (draft.mobileFollowing) {
15357
+ nodes.push({ key: mobileKey, text: "" });
15358
+ } else {
15359
+ nodes.push({ key: mobileKey, text: String(draft.mobilePx) });
15360
+ }
15361
+ editContentRef.current = {
15362
+ ...editContentRef.current,
15363
+ [desktopKey]: String(draft.desktopPx),
15364
+ [mobileKey]: draft.mobileFollowing ? "" : String(draft.mobilePx)
15365
+ };
15366
+ applyLogoSizeToPlacement(
15367
+ placement,
15368
+ draft.desktopPx,
15369
+ draft.mobileFollowing ? draft.desktopPx : draft.mobilePx,
15370
+ draft.mobileFollowing
15371
+ );
15372
+ postToParent2({ type: "ow:change", nodes });
15373
+ requestAnimationFrame(() => {
15374
+ const selected = selectedElRef.current;
15375
+ if (!selected || toolbarVariantRef.current !== "logo") return;
15376
+ const rect = getLogoInteractionRect(selected);
15377
+ setToolbarRect(rect);
15378
+ if (glowElRef.current) {
15379
+ const GAP = SELECTION_CHROME_GAP2;
15380
+ glowElRef.current.style.top = `${rect.top - GAP}px`;
15381
+ glowElRef.current.style.left = `${rect.left - GAP}px`;
15382
+ glowElRef.current.style.width = `${rect.width + GAP * 2}px`;
15383
+ glowElRef.current.style.height = `${rect.height + GAP * 2}px`;
15384
+ }
15385
+ });
15386
+ },
15387
+ [postToParent2]
15388
+ );
15389
+ const activate = (0, import_react16.useCallback)((el, options) => {
13722
15390
  if (activeElRef.current === el) return;
15391
+ if (isIconEditable(el)) return;
15392
+ if (el.hasAttribute("data-ohw-social-label")) return;
13723
15393
  clearSelectedAttr();
13724
15394
  selectedElRef.current = null;
13725
15395
  selectedHrefKeyRef.current = null;
13726
15396
  selectedFooterColAttrRef.current = null;
13727
15397
  setSelectedIsCta(false);
15398
+ setSelectedIsSocial(false);
15399
+ setSelectedIsSocialsRow(false);
13728
15400
  deactivate();
13729
15401
  if (hoveredImageRef.current) {
13730
15402
  hoveredImageRef.current = null;
@@ -13794,8 +15466,38 @@ function OhhwellsBridge() {
13794
15466
  deactivateRef.current = deactivate;
13795
15467
  selectRef.current = select;
13796
15468
  selectFrameRef.current = selectFrame;
15469
+ selectLogoRef.current = selectLogo;
15470
+ openLogoSizePanelRef.current = openLogoSizePanel;
13797
15471
  deselectRef.current = deselect;
13798
- (0, import_react15.useLayoutEffect)(() => {
15472
+ closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
15473
+ const lastSiteWideScopeRef = (0, import_react16.useRef)(null);
15474
+ (0, import_react16.useEffect)(() => {
15475
+ if (!isEditMode) {
15476
+ if (lastSiteWideScopeRef.current !== false) {
15477
+ lastSiteWideScopeRef.current = false;
15478
+ postToParent2({ type: "ow:site-wide-scope", active: false });
15479
+ }
15480
+ return;
15481
+ }
15482
+ const active = isSiteWideScopeActive({
15483
+ selected: selectedElRef.current,
15484
+ hoveredItem: hoveredItemElRef.current,
15485
+ hoveredNavContainer: hoveredNavContainerRef.current,
15486
+ active: activeElRef.current
15487
+ });
15488
+ if (lastSiteWideScopeRef.current === active) return;
15489
+ lastSiteWideScopeRef.current = active;
15490
+ postToParent2({ type: "ow:site-wide-scope", active });
15491
+ }, [
15492
+ isEditMode,
15493
+ hoveredItemRect,
15494
+ hoveredNavContainerRect,
15495
+ toolbarVariant,
15496
+ toolbarRect,
15497
+ isFooterFrameSelection,
15498
+ postToParent2
15499
+ ]);
15500
+ (0, import_react16.useLayoutEffect)(() => {
13799
15501
  if (!subdomain || isEditMode) {
13800
15502
  setFetchState("done");
13801
15503
  return;
@@ -13806,9 +15508,18 @@ function OhhwellsBridge() {
13806
15508
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
13807
15509
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
13808
15510
  }
15511
+ if (typeof content[BRAND_KIT_KEY] === "string") {
15512
+ brandKitRef.current = content[BRAND_KIT_KEY];
15513
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
15514
+ }
15515
+ applyBrandChrome(content);
13809
15516
  for (const [key, val] of Object.entries(content)) {
13810
15517
  if (key === "__ohw_sections") continue;
13811
15518
  if (key === AI_SECTIONS_KEY) continue;
15519
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
15520
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
15521
+ if (key === BRAND_KIT_KEY) continue;
15522
+ if (BRAND_CHROME_KEYS.has(key)) continue;
13812
15523
  if (applyVideoSettingNode(key, val)) continue;
13813
15524
  if (applyCarouselNode(key, val)) continue;
13814
15525
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -13835,14 +15546,20 @@ function OhhwellsBridge() {
13835
15546
  }
13836
15547
  } else if (el.dataset.ohwEditable === "link") {
13837
15548
  applyLinkHref(el, val);
15549
+ } else if (el.dataset.ohwEditable === "icon") {
15550
+ applyIconMarkup(el, val);
13838
15551
  } else if (el.innerHTML !== val) {
13839
15552
  el.innerHTML = val;
13840
15553
  }
13841
15554
  });
13842
15555
  applyLinkByKey(key, val);
13843
15556
  }
15557
+ applyLogoFromContent(content);
15558
+ applyLogoSizes(content);
13844
15559
  reconcileNavbarItemsFromContent(content);
13845
15560
  reconcileFooterOrderFromContent(content);
15561
+ reconcileSocialsFromContent(content);
15562
+ applySocialsDisplayFromContent(content);
13846
15563
  enforceLinkHrefs();
13847
15564
  initSectionsFromContent(content, true);
13848
15565
  sectionsLoadedRef.current = true;
@@ -13858,7 +15575,9 @@ function OhhwellsBridge() {
13858
15575
  let cancelled = false;
13859
15576
  setFetchState("loading");
13860
15577
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
13861
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
15578
+ const initialPath = pathname;
15579
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
15580
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
13862
15581
  if (cancelled) return;
13863
15582
  const content = data?.content ?? {};
13864
15583
  contentCache.set(subdomain, content);
@@ -13871,7 +15590,7 @@ function OhhwellsBridge() {
13871
15590
  cancelled = true;
13872
15591
  };
13873
15592
  }, [subdomain, isEditMode]);
13874
- (0, import_react15.useEffect)(() => {
15593
+ (0, import_react16.useEffect)(() => {
13875
15594
  if (!subdomain || isEditMode) return;
13876
15595
  let debounceTimer = null;
13877
15596
  let observer = null;
@@ -13881,8 +15600,16 @@ function OhhwellsBridge() {
13881
15600
  retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
13882
15601
  observer?.disconnect();
13883
15602
  try {
15603
+ applyBrandChrome(content);
15604
+ if (typeof content[BRAND_KIT_KEY] === "string") {
15605
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
15606
+ }
13884
15607
  for (const [key, val] of Object.entries(content)) {
13885
15608
  if (key === "__ohw_sections") continue;
15609
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
15610
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
15611
+ if (key === BRAND_KIT_KEY) continue;
15612
+ if (BRAND_CHROME_KEYS.has(key)) continue;
13886
15613
  if (applyVideoSettingNode(key, val)) continue;
13887
15614
  if (applyCarouselNode(key, val)) continue;
13888
15615
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -13903,8 +15630,11 @@ function OhhwellsBridge() {
13903
15630
  });
13904
15631
  applyLinkByKey(key, val);
13905
15632
  }
15633
+ applyLogoFromContent(content);
13906
15634
  reconcileNavbarItemsFromContent(content);
13907
15635
  reconcileFooterOrderFromContent(content);
15636
+ reconcileSocialsFromContent(content);
15637
+ applySocialsDisplayFromContent(content);
13908
15638
  } finally {
13909
15639
  observer?.observe(document.body, { childList: true, subtree: true });
13910
15640
  }
@@ -13915,6 +15645,17 @@ function OhhwellsBridge() {
13915
15645
  debounceTimer = setTimeout(applyFromCache, 150);
13916
15646
  };
13917
15647
  applyFromCache();
15648
+ const pathCacheKey = `${subdomain}::${pathname}`;
15649
+ if (!fetchedContentPaths.has(pathCacheKey)) {
15650
+ fetchedContentPaths.add(pathCacheKey);
15651
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
15652
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
15653
+ if (!data?.content) return;
15654
+ contentCache.set(subdomain, data.content);
15655
+ applyFromCache();
15656
+ }).catch(() => {
15657
+ });
15658
+ }
13918
15659
  observer = new MutationObserver(scheduleApply);
13919
15660
  observer.observe(document.body, { childList: true, subtree: true });
13920
15661
  return () => {
@@ -13922,16 +15663,16 @@ function OhhwellsBridge() {
13922
15663
  if (debounceTimer) clearTimeout(debounceTimer);
13923
15664
  };
13924
15665
  }, [subdomain, isEditMode, pathname]);
13925
- (0, import_react15.useLayoutEffect)(() => {
15666
+ (0, import_react16.useLayoutEffect)(() => {
13926
15667
  const el = document.getElementById("ohw-loader");
13927
15668
  if (!el) return;
13928
15669
  const visible = Boolean(subdomain) && fetchState !== "done";
13929
15670
  el.style.display = visible ? "flex" : "none";
13930
15671
  }, [subdomain, fetchState]);
13931
- (0, import_react15.useEffect)(() => {
15672
+ (0, import_react16.useEffect)(() => {
13932
15673
  postToParent2({ type: "ow:navigation", path: pathname });
13933
15674
  }, [pathname, postToParent2]);
13934
- (0, import_react15.useEffect)(() => {
15675
+ (0, import_react16.useEffect)(() => {
13935
15676
  if (!isEditMode) return;
13936
15677
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
13937
15678
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -13939,7 +15680,7 @@ function OhhwellsBridge() {
13939
15680
  deselectRef.current();
13940
15681
  deactivateRef.current();
13941
15682
  }, [pathname, isEditMode]);
13942
- (0, import_react15.useEffect)(() => {
15683
+ (0, import_react16.useEffect)(() => {
13943
15684
  const contentForNav = () => {
13944
15685
  if (isEditMode) return editContentRef.current;
13945
15686
  if (!subdomain) return {};
@@ -13971,8 +15712,10 @@ function OhhwellsBridge() {
13971
15712
  const content = contentForNav();
13972
15713
  reconcileNavbarItemsFromContent(content);
13973
15714
  reconcileFooterOrderFromContent(content);
15715
+ reconcileSocialsFromContent(content);
15716
+ applySocialsDisplayFromContent(content);
13974
15717
  document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
13975
- if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
15718
+ if (isFooterHrefKey(el.getAttribute("data-ohw-href-key")) || getSocialItem(el)) {
13976
15719
  disableNativeHrefDrag(el);
13977
15720
  }
13978
15721
  });
@@ -14004,31 +15747,36 @@ function OhhwellsBridge() {
14004
15747
  observer?.disconnect();
14005
15748
  };
14006
15749
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
14007
- (0, import_react15.useEffect)(() => {
15750
+ (0, import_react16.useEffect)(() => {
14008
15751
  if (!isEditMode) return;
15752
+ let lastPosted = 0;
14009
15753
  const measure = () => {
14010
15754
  const h = document.body.scrollHeight;
14011
- if (h > 50) postToParent2({ type: "ow:height", height: h });
15755
+ if (h > 50 && Math.abs(h - lastPosted) > 1) {
15756
+ lastPosted = h;
15757
+ postToParent2({ type: "ow:height", height: h });
15758
+ }
15759
+ };
15760
+ let raf = null;
15761
+ const schedule = () => {
15762
+ if (raf != null) return;
15763
+ raf = requestAnimationFrame(() => {
15764
+ raf = null;
15765
+ measure();
15766
+ });
14012
15767
  };
14013
15768
  const t1 = setTimeout(measure, 50);
14014
15769
  const t2 = setTimeout(measure, 500);
14015
- let lastWidth = window.innerWidth;
14016
- let resizeTimer = null;
14017
- const handleResize = () => {
14018
- if (window.innerWidth === lastWidth) return;
14019
- lastWidth = window.innerWidth;
14020
- if (resizeTimer) clearTimeout(resizeTimer);
14021
- resizeTimer = setTimeout(measure, 150);
14022
- };
14023
- window.addEventListener("resize", handleResize);
15770
+ const ro = new ResizeObserver(schedule);
15771
+ ro.observe(document.body);
14024
15772
  return () => {
14025
15773
  clearTimeout(t1);
14026
15774
  clearTimeout(t2);
14027
- if (resizeTimer) clearTimeout(resizeTimer);
14028
- window.removeEventListener("resize", handleResize);
15775
+ if (raf != null) cancelAnimationFrame(raf);
15776
+ ro.disconnect();
14029
15777
  };
14030
15778
  }, [pathname, isEditMode, postToParent2]);
14031
- (0, import_react15.useEffect)(() => {
15779
+ (0, import_react16.useEffect)(() => {
14032
15780
  if (!subdomainFromQuery || isEditMode) return;
14033
15781
  const handleClick = (e) => {
14034
15782
  const anchor = e.target.closest("a");
@@ -14044,7 +15792,7 @@ function OhhwellsBridge() {
14044
15792
  document.addEventListener("click", handleClick, true);
14045
15793
  return () => document.removeEventListener("click", handleClick, true);
14046
15794
  }, [subdomainFromQuery, isEditMode, router]);
14047
- (0, import_react15.useEffect)(() => {
15795
+ (0, import_react16.useEffect)(() => {
14048
15796
  if (!isEditMode) {
14049
15797
  editStylesRef.current?.base.remove();
14050
15798
  editStylesRef.current?.forceHover.remove();
@@ -14095,9 +15843,12 @@ function OhhwellsBridge() {
14095
15843
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
14096
15844
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
14097
15845
  [data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
15846
+ /* Text hover chrome is drawn by the overlay (see hoveredTextRect) \u2014 the CSS outline
15847
+ that used to draw it dashes denser than the overlay border, so identical specs
15848
+ still read as two different frames (OHH-695). The attribute stays: hover paths
15849
+ and suppression rules key off it. */
14098
15850
  [data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]) {
14099
- outline: 2px dashed ${PRIMARY3} !important;
14100
- outline-offset: 4px;
15851
+ outline: none !important;
14101
15852
  }
14102
15853
  [data-ohw-href-key] [data-ohw-hovered],
14103
15854
  [data-ohw-href-key][data-ohw-hovered],
@@ -14150,8 +15901,11 @@ function OhhwellsBridge() {
14150
15901
  stateViews.textContent = `
14151
15902
  [data-ohw-state-view]:not([data-ohw-state-view="default"]) { display: none; }
14152
15903
  [data-ohw-state-view="default"] [data-ohw-editable] { pointer-events: auto !important; }
14153
- [data-ohw-state-hovered] { outline: 2px dashed ${PRIMARY3} !important; outline-offset: 4px; }
15904
+ [data-ohw-state-hovered] { outline: ${HOVER_STROKE_WIDTH}px dashed ${CHROME_PRIMARY} !important; outline-offset: ${HOVER_CHROME_GAP}px; }
14154
15905
  [data-ohw-state-hovered]:has([data-ohw-hovered]) { outline: none !important; }
15906
+ /* :has() only sees descendants \u2014 when the card itself is the hovered text, the overlay
15907
+ already frames it, and the card outline doubled it (OHH-695). */
15908
+ [data-ohw-state-hovered][data-ohw-hovered] { outline: none !important; }
14155
15909
  `;
14156
15910
  document.head.appendChild(base);
14157
15911
  document.head.appendChild(forceHover);
@@ -14173,6 +15927,7 @@ function OhhwellsBridge() {
14173
15927
  if (target.closest("[data-ohw-state-toggle]")) return;
14174
15928
  if (target.closest("[data-ohw-max-badge]")) return;
14175
15929
  if (isInsideLinkEditor(target)) return;
15930
+ if (isInsideFloatingPanel(target)) return;
14176
15931
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
14177
15932
  const beneath = document.elementsFromPoint(e.clientX, e.clientY).find(
14178
15933
  (el) => el instanceof HTMLElement && !el.closest("[data-ohw-bridge-root]") && el.closest("[data-ohw-section]") != null
@@ -14236,6 +15991,21 @@ function OhhwellsBridge() {
14236
15991
  return;
14237
15992
  }
14238
15993
  }
15994
+ const logoEl = getLogoElement(target);
15995
+ if (logoEl) {
15996
+ e.preventDefault();
15997
+ e.stopPropagation();
15998
+ if (!logoHasUploadedImage(logoEl)) {
15999
+ deselectRef.current();
16000
+ deactivateRef.current();
16001
+ const identity = readLogoIdentityFromDom();
16002
+ postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
16003
+ return;
16004
+ }
16005
+ selectLogoRef.current(logoEl);
16006
+ openLogoSizePanelRef.current(logoEl);
16007
+ return;
16008
+ }
14239
16009
  const editable = target.closest("[data-ohw-editable]");
14240
16010
  if (editable) {
14241
16011
  if (editable.dataset.ohwEditable === "link") {
@@ -14252,6 +16022,17 @@ function OhhwellsBridge() {
14252
16022
  });
14253
16023
  return;
14254
16024
  }
16025
+ if (isIconEditable(editable) && !getSocialItem(editable)) {
16026
+ e.preventDefault();
16027
+ e.stopPropagation();
16028
+ aiSectionApiRef.current?.selectFromElement(editable);
16029
+ postToParentRef.current({
16030
+ type: "ow:icon-pick",
16031
+ key: editable.dataset.ohwKey ?? "",
16032
+ current: currentIconRef(editable)
16033
+ });
16034
+ return;
16035
+ }
14255
16036
  if (isMediaEditable(editable)) {
14256
16037
  e.preventDefault();
14257
16038
  e.stopPropagation();
@@ -14266,6 +16047,7 @@ function OhhwellsBridge() {
14266
16047
  e.stopPropagation();
14267
16048
  if (selectedElRef.current === navAnchor) {
14268
16049
  if (e.detail >= 2) return;
16050
+ if (requestSocialDialog(navAnchor, postToParentRef.current, editContentRef.current)) return;
14269
16051
  activateRef.current(editable, { caretX: e.clientX, caretY: e.clientY });
14270
16052
  return;
14271
16053
  }
@@ -14282,6 +16064,7 @@ function OhhwellsBridge() {
14282
16064
  e.preventDefault();
14283
16065
  e.stopPropagation();
14284
16066
  if (selectedElRef.current === hrefAnchor) {
16067
+ if (requestSocialDialog(hrefAnchor, postToParentRef.current, editContentRef.current)) return;
14285
16068
  const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
14286
16069
  if (textEditable) {
14287
16070
  activateRef.current(textEditable, {
@@ -14320,6 +16103,13 @@ function OhhwellsBridge() {
14320
16103
  selectFrameRef.current(navContainerToSelect);
14321
16104
  return;
14322
16105
  }
16106
+ const socialsRowToSelect = isSocialsRow(target) ? target : null;
16107
+ if (socialsRowToSelect && !getSocialItem(target)) {
16108
+ e.preventDefault();
16109
+ e.stopPropagation();
16110
+ selectFrameRef.current(socialsRowToSelect);
16111
+ return;
16112
+ }
14323
16113
  const footerColumnToSelect = resolveFooterColumnSelectionTarget(target, e.clientX, e.clientY);
14324
16114
  if (footerColumnToSelect) {
14325
16115
  e.preventDefault();
@@ -14368,9 +16158,11 @@ function OhhwellsBridge() {
14368
16158
  if (target.closest("[data-ohw-state-toggle]")) return;
14369
16159
  if (target.closest("[data-ohw-max-badge]")) return;
14370
16160
  if (isInsideLinkEditor(target)) return;
16161
+ if (isInsideFloatingPanel(target)) return;
14371
16162
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
14372
16163
  return;
14373
16164
  }
16165
+ if (getSocialItem(target)) return;
14374
16166
  const navLabel = getNavigationLabelEditable(target);
14375
16167
  const editable = navLabel?.editable ?? target.closest('[data-ohw-editable="text"], [data-ohw-editable="plain"]');
14376
16168
  if (!editable || isMediaEditable(editable) || editable.dataset.ohwEditable === "link") return;
@@ -14394,6 +16186,16 @@ function OhhwellsBridge() {
14394
16186
  setHoveredNavContainerRect(null);
14395
16187
  return;
14396
16188
  }
16189
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || isInsideFloatingPanel(target) || isPointOverFloatingPanel(e.clientX, e.clientY)) {
16190
+ hoveredItemElRef.current = null;
16191
+ setHoveredItemRect(null);
16192
+ hoveredNavContainerRef.current = null;
16193
+ setHoveredNavContainerRect(null);
16194
+ siblingHintElRef.current = null;
16195
+ setSiblingHintRect(null);
16196
+ setSiblingHintRects([]);
16197
+ return;
16198
+ }
14397
16199
  {
14398
16200
  const selected2 = selectedElRef.current;
14399
16201
  const selectedIsFooterColumn = Boolean(selected2) && !isFooterLinksContainer(selected2) && (selected2.hasAttribute("data-ohw-footer-col") || selected2.hasAttribute("data-ohw-footer-column") || Boolean(selected2.closest("footer") && isInferredFooterGroup2(selected2)));
@@ -14401,7 +16203,7 @@ function OhhwellsBridge() {
14401
16203
  const allowFooterLinksHover = toolbarVariantRef.current !== "select-frame" || selectedIsFooterColumn;
14402
16204
  if (allowNavContainerHover) {
14403
16205
  const navContainer = target.closest("[data-ohw-nav-container]");
14404
- if (navContainer && !getNavigationItemAnchor(target)) {
16206
+ if (navContainer && !getNavigationItemAnchor(target) && !getLogoElement(target)) {
14405
16207
  hoveredNavContainerRef.current = navContainer;
14406
16208
  setHoveredNavContainerRect(navContainer.getBoundingClientRect());
14407
16209
  hoveredItemElRef.current = null;
@@ -14430,6 +16232,15 @@ function OhhwellsBridge() {
14430
16232
  setHoveredNavContainerRect(null);
14431
16233
  }
14432
16234
  }
16235
+ const logoEl = getLogoElement(target);
16236
+ if (logoEl) {
16237
+ hoveredNavContainerRef.current = null;
16238
+ setHoveredNavContainerRect(null);
16239
+ if (selectedElRef.current === logoEl) return;
16240
+ hoveredItemElRef.current = logoEl;
16241
+ setHoveredItemRect(getLogoInteractionRect(logoEl));
16242
+ return;
16243
+ }
14433
16244
  const navAnchor = getNavigationItemAnchor(target);
14434
16245
  if (navAnchor) {
14435
16246
  hoveredNavContainerRef.current = null;
@@ -14455,6 +16266,11 @@ function OhhwellsBridge() {
14455
16266
  const selected = selectedElRef.current;
14456
16267
  if (selected && (selected === editable || selected.contains(editable))) return;
14457
16268
  if (!isMediaEditable(editable) && !editable.hasAttribute("contenteditable")) {
16269
+ if (isIconEditable(editable) && !getSocialItem(editable)) {
16270
+ hoveredItemElRef.current = editable;
16271
+ setHoveredItemRect(editable.getBoundingClientRect());
16272
+ return;
16273
+ }
14458
16274
  const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
14459
16275
  if (hoverTarget.hasAttribute("data-ohw-href-key")) {
14460
16276
  clearHrefKeyHover(hoverTarget);
@@ -14462,6 +16278,11 @@ function OhhwellsBridge() {
14462
16278
  setHoveredItemRect(hoverTarget.getBoundingClientRect());
14463
16279
  } else if (!isInsideNavigationItem(editable)) {
14464
16280
  hoverTarget.setAttribute("data-ohw-hovered", "");
16281
+ if (editable.closest("footer") || editable.closest('[data-ohw-section="footer"]')) {
16282
+ hoveredNavContainerRef.current = null;
16283
+ setHoveredNavContainerRect(null);
16284
+ hoveredItemElRef.current = editable;
16285
+ }
14465
16286
  }
14466
16287
  }
14467
16288
  };
@@ -14497,11 +16318,30 @@ function OhhwellsBridge() {
14497
16318
  }
14498
16319
  return;
14499
16320
  }
16321
+ const logoEl = getLogoElement(target);
16322
+ if (logoEl) {
16323
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
16324
+ if (related2 && (logoEl === related2 || logoEl.contains(related2) || related2.closest?.('[data-ohw-role="logo"], [data-ohw-logo]'))) {
16325
+ return;
16326
+ }
16327
+ if (hoveredItemElRef.current === logoEl) {
16328
+ hoveredItemElRef.current = null;
16329
+ setHoveredItemRect(null);
16330
+ }
16331
+ return;
16332
+ }
14500
16333
  const editable = target.closest("[data-ohw-editable]");
14501
16334
  if (!editable) return;
14502
16335
  const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
14503
16336
  if (related?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
14504
16337
  if (!isMediaEditable(editable)) {
16338
+ if (isIconEditable(editable) && !getSocialItem(editable) && hoveredItemElRef.current === editable) {
16339
+ if (!related?.closest("[data-ohw-item-interaction]")) {
16340
+ hoveredItemElRef.current = null;
16341
+ setHoveredItemRect(null);
16342
+ }
16343
+ return;
16344
+ }
14505
16345
  const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
14506
16346
  if (hoverTarget.hasAttribute("data-ohw-href-key")) {
14507
16347
  if (!related?.closest("[data-ohw-href-key]")) {
@@ -14510,6 +16350,13 @@ function OhhwellsBridge() {
14510
16350
  }
14511
16351
  } else {
14512
16352
  hoverTarget.removeAttribute("data-ohw-hovered");
16353
+ if (hoveredItemElRef.current === editable) {
16354
+ const stillOnEditable = related instanceof Element && related.closest("[data-ohw-editable]") === editable;
16355
+ if (!stillOnEditable) {
16356
+ hoveredItemElRef.current = null;
16357
+ setHoveredItemRect(null);
16358
+ }
16359
+ }
14513
16360
  }
14514
16361
  }
14515
16362
  };
@@ -14626,6 +16473,26 @@ function OhhwellsBridge() {
14626
16473
  hoveredNavContainerRef.current = null;
14627
16474
  setHoveredNavContainerRect(null);
14628
16475
  }
16476
+ const logoCandidates = [
16477
+ ...document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]'),
16478
+ ...document.querySelectorAll("nav a:not([data-ohw-href-key]), [data-ohw-nav-root] a:not([data-ohw-href-key])"),
16479
+ ...document.querySelectorAll("footer img")
16480
+ ];
16481
+ const seenLogos = /* @__PURE__ */ new Set();
16482
+ for (const candidate of logoCandidates) {
16483
+ const logo = getLogoElement(candidate);
16484
+ if (!logo || seenLogos.has(logo)) continue;
16485
+ seenLogos.add(logo);
16486
+ const r2 = logo.getBoundingClientRect();
16487
+ if (x < r2.left || x > r2.right || y < r2.top || y > r2.bottom) continue;
16488
+ hoveredNavContainerRef.current = null;
16489
+ setHoveredNavContainerRect(null);
16490
+ if (selectedElRef.current !== logo) {
16491
+ hoveredItemElRef.current = logo;
16492
+ setHoveredItemRect(getLogoInteractionRect(logo));
16493
+ }
16494
+ return;
16495
+ }
14629
16496
  const navContainers = Array.from(
14630
16497
  document.querySelectorAll("[data-ohw-nav-container]")
14631
16498
  );
@@ -14711,7 +16578,7 @@ function OhhwellsBridge() {
14711
16578
  }
14712
16579
  };
14713
16580
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
14714
- if (linkPopoverOpenRef.current) {
16581
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
14715
16582
  if (hoveredImageRef.current) {
14716
16583
  hoveredImageRef.current = null;
14717
16584
  hoveredImageHasTextOverlapRef.current = false;
@@ -14965,7 +16832,7 @@ function OhhwellsBridge() {
14965
16832
  }
14966
16833
  };
14967
16834
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
14968
- if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
16835
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || document.documentElement.hasAttribute("data-ohw-section-picking")) {
14969
16836
  if (activeStateElRef.current) {
14970
16837
  activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
14971
16838
  activeStateElRef.current = null;
@@ -15033,6 +16900,19 @@ function OhhwellsBridge() {
15033
16900
  };
15034
16901
  const handleMouseMove = (e) => {
15035
16902
  const { clientX, clientY } = e;
16903
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
16904
+ hoveredItemElRef.current = null;
16905
+ setHoveredItemRect(null);
16906
+ hoveredNavContainerRef.current = null;
16907
+ setHoveredNavContainerRect(null);
16908
+ siblingHintElRef.current = null;
16909
+ setSiblingHintRect(null);
16910
+ setSiblingHintRects([]);
16911
+ dismissImageHover();
16912
+ clearImageHover();
16913
+ setSectionGap(null);
16914
+ return;
16915
+ }
15036
16916
  probeSectionGapAt(clientX, clientY);
15037
16917
  probeImageAt(clientX, clientY);
15038
16918
  probeHoverCardsAt(clientX, clientY);
@@ -15041,6 +16921,11 @@ function OhhwellsBridge() {
15041
16921
  if (e.data?.type !== "ow:pointer-sync") return;
15042
16922
  const { clientX, clientY } = e.data;
15043
16923
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
16924
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
16925
+ dismissImageHover();
16926
+ clearImageHover();
16927
+ return;
16928
+ }
15044
16929
  probeSectionGapAt(clientX, clientY);
15045
16930
  probeImageAt(clientX, clientY);
15046
16931
  probeHoverCardsAt(clientX, clientY);
@@ -15050,7 +16935,7 @@ function OhhwellsBridge() {
15050
16935
  if (footerSession) {
15051
16936
  e.preventDefault();
15052
16937
  if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
15053
- const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
16938
+ 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);
15054
16939
  refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
15055
16940
  return;
15056
16941
  }
@@ -15102,6 +16987,62 @@ function OhhwellsBridge() {
15102
16987
  resumeAnimTracks();
15103
16988
  clearImageHover();
15104
16989
  };
16990
+ const handleSocialCancel = (e) => {
16991
+ if (e.data?.type !== "ow:social-cancel") return;
16992
+ const { hrefKey } = e.data;
16993
+ const item = hrefKey ? findSocialByHrefKey(hrefKey) : null;
16994
+ if (!item) return;
16995
+ const removed = removeSocialItem(item, editContentRef.current);
16996
+ if (!removed) return;
16997
+ const orderJson = JSON.stringify(removed.order);
16998
+ editContentRef.current = { ...editContentRef.current, [SOCIALS_ORDER_KEY]: orderJson };
16999
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: SOCIALS_ORDER_KEY, text: orderJson }] });
17000
+ deselectRef.current();
17001
+ };
17002
+ const handleSocialUpdate = (e) => {
17003
+ if (e.data?.type !== "ow:social-update") return;
17004
+ const updates = Array.isArray(e.data.items) ? e.data.items : [e.data];
17005
+ const nodes = [];
17006
+ for (const { hrefKey, iconKey, url, iconMarkup, platformId, label } of updates) {
17007
+ if (hrefKey) {
17008
+ document.querySelectorAll(`[data-ohw-href-key="${hrefKey}"]`).forEach((el) => applyLinkHref(el, url));
17009
+ nodes.push({ key: hrefKey, text: url });
17010
+ }
17011
+ if (iconKey && typeof iconMarkup === "string" && iconMarkup) {
17012
+ document.querySelectorAll(`[data-ohw-key="${iconKey}"][data-ohw-editable="icon"]`).forEach((el) => {
17013
+ applyIconMarkup(el, iconMarkup);
17014
+ });
17015
+ nodes.push({ key: iconKey, text: iconMarkup });
17016
+ }
17017
+ if (iconKey && platformId) nodes.push({ key: socialPlatformKey(iconKey), text: platformId });
17018
+ if (iconKey && label) {
17019
+ const labelKey = socialLabelKey(iconKey);
17020
+ document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
17021
+ el.textContent = label;
17022
+ });
17023
+ nodes.push({ key: labelKey, text: label });
17024
+ }
17025
+ }
17026
+ if (!nodes.length) return;
17027
+ editContentRef.current = {
17028
+ ...editContentRef.current,
17029
+ ...Object.fromEntries(nodes.map((node) => [node.key, node.text]))
17030
+ };
17031
+ postToParentRef.current({ type: "ow:change", nodes, flush: true });
17032
+ };
17033
+ const handleIconMarkup = (e) => {
17034
+ if (e.data?.type !== "ow:icon-markup") return;
17035
+ const { key, markup } = e.data;
17036
+ if (!key || typeof markup !== "string") return;
17037
+ const targets = document.querySelectorAll(
17038
+ `[data-ohw-key="${key}"][data-ohw-editable="icon"]`
17039
+ );
17040
+ if (!targets.length) return;
17041
+ targets.forEach((el) => {
17042
+ applyIconMarkup(el, markup);
17043
+ });
17044
+ postToParentRef.current({ type: "ow:change", nodes: [{ key, text: markup }], flush: true });
17045
+ };
15105
17046
  const handleImageUrl = (e) => {
15106
17047
  if (e.data?.type !== "ow:image-url") return;
15107
17048
  const { key, url } = e.data;
@@ -15234,6 +17175,11 @@ function OhhwellsBridge() {
15234
17175
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
15235
17176
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
15236
17177
  }
17178
+ if (typeof content[BRAND_KIT_KEY] === "string") {
17179
+ brandKitRef.current = content[BRAND_KIT_KEY];
17180
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17181
+ }
17182
+ applyBrandChrome(content);
15237
17183
  let sectionsJson = null;
15238
17184
  for (const [key, val] of Object.entries(content)) {
15239
17185
  if (key === "__ohw_sections") {
@@ -15241,6 +17187,10 @@ function OhhwellsBridge() {
15241
17187
  continue;
15242
17188
  }
15243
17189
  if (key === AI_SECTIONS_KEY) continue;
17190
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
17191
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
17192
+ if (key === BRAND_KIT_KEY) continue;
17193
+ if (BRAND_CHROME_KEYS.has(key)) continue;
15244
17194
  if (applyVideoSettingNode(key, val)) continue;
15245
17195
  if (applyCarouselNode(key, val)) continue;
15246
17196
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -15260,6 +17210,8 @@ function OhhwellsBridge() {
15260
17210
  });
15261
17211
  applyLinkByKey(key, val);
15262
17212
  }
17213
+ applyLogoFromContent(content);
17214
+ applyLogoSizes(content);
15263
17215
  if (sectionsJson) {
15264
17216
  initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
15265
17217
  sectionsLoadedRef.current = true;
@@ -15274,6 +17226,58 @@ function OhhwellsBridge() {
15274
17226
  if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
15275
17227
  postToParentRef.current({ type: "ow:hydrate-done" });
15276
17228
  };
17229
+ const handleUpdateLogoIdentity = (e) => {
17230
+ if (e.data?.type !== "ow:update-logo-identity") return;
17231
+ const rawText = typeof e.data.text === "string" ? e.data.text : "";
17232
+ const alt = typeof e.data.alt === "string" ? e.data.alt : rawText;
17233
+ const href = typeof e.data.href === "string" ? e.data.href : void 0;
17234
+ const imageProvided = "image" in e.data;
17235
+ const imageUrl = imageProvided && typeof e.data.image === "string" && e.data.image.trim() ? e.data.image.trim() : imageProvided ? null : void 0;
17236
+ let isPlaceholder = e.data.isPlaceholder !== false;
17237
+ if (imageUrl) isPlaceholder = false;
17238
+ else if (imageProvided && imageUrl === null) {
17239
+ isPlaceholder = e.data.isPlaceholder === true || !rawText.trim() || resolveLogoDisplayText(rawText) === PLACEHOLDER_BUSINESS_NAME;
17240
+ }
17241
+ const display = applyLogoIdentity(rawText, isPlaceholder);
17242
+ const displayAlt = resolveLogoDisplayText(alt || display);
17243
+ if (imageUrl !== void 0) {
17244
+ applyLogoImage(imageUrl, displayAlt);
17245
+ } else {
17246
+ for (const key of LOGO_IMAGE_KEYS) {
17247
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
17248
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
17249
+ if (img) img.alt = displayAlt;
17250
+ });
17251
+ }
17252
+ }
17253
+ if (href !== void 0) {
17254
+ applyLogoHref(href);
17255
+ applyLinkByKey("nav-logo-href", href);
17256
+ applyLinkByKey("footer-logo-href", href);
17257
+ applyLinkByKey("logo-href", href);
17258
+ }
17259
+ const nodes = [
17260
+ ...LOGO_TEXT_KEYS.map((key) => ({ key, text: display })),
17261
+ { key: LOGO_PLACEHOLDER_KEY, text: isPlaceholder ? "true" : "false" },
17262
+ { key: LOGO_ALT_KEY, text: displayAlt }
17263
+ ];
17264
+ if (imageUrl !== void 0) {
17265
+ if (imageUrl) {
17266
+ for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: imageUrl });
17267
+ } else {
17268
+ for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: "" });
17269
+ }
17270
+ }
17271
+ if (href !== void 0) {
17272
+ for (const key of LOGO_HREF_KEYS) nodes.push({ key, text: href.trim() || "/" });
17273
+ }
17274
+ editContentRef.current = {
17275
+ ...editContentRef.current,
17276
+ ...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
17277
+ };
17278
+ applyLogoSizes(editContentRef.current);
17279
+ postToParentRef.current({ type: "ow:change", nodes });
17280
+ };
15277
17281
  window.addEventListener("message", handleHydrate);
15278
17282
  const postAiSectionsChanged = () => {
15279
17283
  postToParentRef.current({
@@ -15330,6 +17334,17 @@ function OhhwellsBridge() {
15330
17334
  postAiSectionsChanged();
15331
17335
  };
15332
17336
  window.addEventListener("message", handleAiSetSections);
17337
+ const handleAiSetBrand = (e) => {
17338
+ if (e.data?.type !== "ow:ai-set-brand") return;
17339
+ const value = typeof e.data.value === "string" ? e.data.value : "";
17340
+ const previous = brandKitRef.current;
17341
+ brandKitRef.current = value;
17342
+ applyBrandToDom(parseBrandKit(value));
17343
+ if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
17344
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
17345
+ postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
17346
+ };
17347
+ window.addEventListener("message", handleAiSetBrand);
15333
17348
  const handleDeactivate = (e) => {
15334
17349
  if (e.data?.type !== "ow:deactivate") return;
15335
17350
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
@@ -15338,6 +17353,12 @@ function OhhwellsBridge() {
15338
17353
  closeLinkPopoverRef.current();
15339
17354
  return;
15340
17355
  }
17356
+ if (floatingPanelOpenRef.current) {
17357
+ setFloatingPanelRef.current(null);
17358
+ deselectRef.current();
17359
+ deactivateRef.current();
17360
+ return;
17361
+ }
15341
17362
  deselectRef.current();
15342
17363
  deactivateRef.current();
15343
17364
  };
@@ -15357,6 +17378,10 @@ function OhhwellsBridge() {
15357
17378
  closeLinkPopoverRef.current();
15358
17379
  return;
15359
17380
  }
17381
+ if (floatingPanelOpenRef.current) {
17382
+ closeFloatingPanelOnlyRef.current();
17383
+ return;
17384
+ }
15360
17385
  if (activeElRef.current) {
15361
17386
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
15362
17387
  const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
@@ -15387,6 +17412,10 @@ function OhhwellsBridge() {
15387
17412
  return;
15388
17413
  }
15389
17414
  if (selectedElRef.current) {
17415
+ if (toolbarVariantRef.current === "logo") {
17416
+ deselectRef.current();
17417
+ return;
17418
+ }
15390
17419
  if (toolbarVariantRef.current === "select-frame") {
15391
17420
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
15392
17421
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -15420,7 +17449,16 @@ function OhhwellsBridge() {
15420
17449
  closeLinkPopoverRef.current();
15421
17450
  return;
15422
17451
  }
17452
+ if (e.key === "Escape" && floatingPanelOpenRef.current) {
17453
+ e.preventDefault();
17454
+ closeFloatingPanelOnlyRef.current();
17455
+ return;
17456
+ }
15423
17457
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
17458
+ if (toolbarVariantRef.current === "logo") {
17459
+ deselectRef.current();
17460
+ return;
17461
+ }
15424
17462
  if (toolbarVariantRef.current === "select-frame") {
15425
17463
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
15426
17464
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -15498,7 +17536,8 @@ function OhhwellsBridge() {
15498
17536
  const handleScroll = () => {
15499
17537
  const focusEl = activeElRef.current ?? selectedElRef.current;
15500
17538
  if (focusEl) {
15501
- const r2 = activeElRef.current ? getEditMeasureEl(activeElRef.current).getBoundingClientRect() : focusEl.getBoundingClientRect();
17539
+ const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
17540
+ const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
15502
17541
  applyToolbarPos(r2);
15503
17542
  setToolbarRect(r2);
15504
17543
  setMaxBadge((prev) => prev && activeElRef.current ? { ...prev, rect: r2 } : prev);
@@ -15508,7 +17547,9 @@ function OhhwellsBridge() {
15508
17547
  setToggleState((prev) => prev ? { ...prev, rect } : null);
15509
17548
  }
15510
17549
  if (hoveredItemElRef.current) {
15511
- setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
17550
+ const hoverEl = hoveredItemElRef.current;
17551
+ const logo = getLogoElement(hoverEl);
17552
+ setHoveredItemRect(logo ? getLogoInteractionRect(logo) : hoverEl.getBoundingClientRect());
15512
17553
  }
15513
17554
  if (hoveredNavContainerRef.current) {
15514
17555
  setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
@@ -15528,7 +17569,7 @@ function OhhwellsBridge() {
15528
17569
  }
15529
17570
  if (footerDragRef.current) {
15530
17571
  const session = footerDragRef.current;
15531
- const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
17572
+ 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);
15532
17573
  refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
15533
17574
  }
15534
17575
  if (navDragRef.current) {
@@ -15552,6 +17593,9 @@ function OhhwellsBridge() {
15552
17593
  if (aiSectionsRef.current) {
15553
17594
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
15554
17595
  }
17596
+ if (brandKitRef.current) {
17597
+ nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
17598
+ }
15555
17599
  postToParentRef.current({ type: "ow:save-result", nodes });
15556
17600
  };
15557
17601
  const handleInsertSection = (e) => {
@@ -15562,8 +17606,12 @@ function OhhwellsBridge() {
15562
17606
  if (inserted) {
15563
17607
  const tracker = getSectionsTracker();
15564
17608
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
15565
- const h = document.documentElement.scrollHeight;
15566
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
17609
+ const reportHeight = () => {
17610
+ const h = document.body.scrollHeight;
17611
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
17612
+ };
17613
+ reportHeight();
17614
+ setTimeout(reportHeight, 500);
15567
17615
  }
15568
17616
  };
15569
17617
  const handleSwitchSchedule = (e) => {
@@ -15756,13 +17804,17 @@ function OhhwellsBridge() {
15756
17804
  if (e.data?.type !== "ow:parent-scroll") return;
15757
17805
  const { iframeOffsetTop, headerH, canvasH } = e.data;
15758
17806
  parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
17807
+ if (floatingPanelOpenRef.current) {
17808
+ setParentScrollSnap({ iframeOffsetTop, headerH, canvasH });
17809
+ }
15759
17810
  if (visibleViewportRef.current) {
15760
17811
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
15761
17812
  }
15762
17813
  const focusEl = activeElRef.current ?? selectedElRef.current;
15763
17814
  if (focusEl) {
15764
- const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : focusEl;
15765
- applyToolbarPos(measureEl.getBoundingClientRect());
17815
+ const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
17816
+ const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
17817
+ applyToolbarPos(r2);
15766
17818
  }
15767
17819
  };
15768
17820
  const handleClickAt = (e) => {
@@ -15787,6 +17839,25 @@ function OhhwellsBridge() {
15787
17839
  postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "", elementType: stateCardImage.dataset.ohwEditable ?? "image" });
15788
17840
  return;
15789
17841
  }
17842
+ const logoAtPoint = Array.from(
17843
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]')
17844
+ ).map((el) => getLogoElement(el)).find((logo) => {
17845
+ if (!logo) return false;
17846
+ const r2 = logo.getBoundingClientRect();
17847
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
17848
+ });
17849
+ if (logoAtPoint) {
17850
+ if (!logoHasUploadedImage(logoAtPoint)) {
17851
+ deselectRef.current();
17852
+ deactivateRef.current();
17853
+ const identity = readLogoIdentityFromDom();
17854
+ postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
17855
+ return;
17856
+ }
17857
+ selectLogoRef.current(logoAtPoint);
17858
+ openLogoSizePanelRef.current(logoAtPoint);
17859
+ return;
17860
+ }
15790
17861
  const textEditable = Array.from(
15791
17862
  document.querySelectorAll(NON_MEDIA_SELECTOR)
15792
17863
  ).find((el) => {
@@ -15848,6 +17919,9 @@ function OhhwellsBridge() {
15848
17919
  window.addEventListener("message", handleClearSchedulingWidget);
15849
17920
  window.addEventListener("message", handleRemoveSchedulingSection);
15850
17921
  window.addEventListener("message", handleCollectSection);
17922
+ window.addEventListener("message", handleSocialCancel);
17923
+ window.addEventListener("message", handleSocialUpdate);
17924
+ window.addEventListener("message", handleIconMarkup);
15851
17925
  window.addEventListener("message", handleImageUrl);
15852
17926
  window.addEventListener("message", handleImageUploading);
15853
17927
  window.addEventListener("message", handleCarouselChange);
@@ -15855,6 +17929,14 @@ function OhhwellsBridge() {
15855
17929
  window.addEventListener("message", handleParentScroll);
15856
17930
  window.addEventListener("message", handlePointerSync);
15857
17931
  window.addEventListener("message", handleClickAt);
17932
+ window.addEventListener("message", handleUpdateLogoIdentity);
17933
+ const handleViewMode = (e) => {
17934
+ if (e.data?.type !== "ow:view-mode") return;
17935
+ const mode = e.data.mode === "Mobile" || e.data.mode === "mobile" ? "mobile" : "desktop";
17936
+ setEditorViewport(mode);
17937
+ applyLogoSizes(editContentRef.current);
17938
+ };
17939
+ window.addEventListener("message", handleViewMode);
15858
17940
  const handleViewportResize = () => {
15859
17941
  if (visibleViewportRef.current) {
15860
17942
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
@@ -15899,6 +17981,9 @@ function OhhwellsBridge() {
15899
17981
  window.removeEventListener("message", handleClearSchedulingWidget);
15900
17982
  window.removeEventListener("message", handleRemoveSchedulingSection);
15901
17983
  window.removeEventListener("message", handleCollectSection);
17984
+ window.removeEventListener("message", handleSocialCancel);
17985
+ window.removeEventListener("message", handleSocialUpdate);
17986
+ window.removeEventListener("message", handleIconMarkup);
15902
17987
  window.removeEventListener("message", handleImageUrl);
15903
17988
  window.removeEventListener("message", handleImageUploading);
15904
17989
  window.removeEventListener("message", handleCarouselChange);
@@ -15907,10 +17992,13 @@ function OhhwellsBridge() {
15907
17992
  window.removeEventListener("resize", handleViewportResize);
15908
17993
  window.removeEventListener("message", handlePointerSync);
15909
17994
  window.removeEventListener("message", handleClickAt);
17995
+ window.removeEventListener("message", handleUpdateLogoIdentity);
17996
+ window.removeEventListener("message", handleViewMode);
15910
17997
  window.removeEventListener("message", handleHydrate);
15911
17998
  window.removeEventListener("message", handleAiApplyTree);
15912
17999
  window.removeEventListener("message", handleAiDeleteSection);
15913
18000
  window.removeEventListener("message", handleAiSetSections);
18001
+ window.removeEventListener("message", handleAiSetBrand);
15914
18002
  window.removeEventListener("message", handleDeactivate);
15915
18003
  window.removeEventListener("message", handleToastAction);
15916
18004
  window.removeEventListener("message", handleUiEscape);
@@ -15920,7 +18008,7 @@ function OhhwellsBridge() {
15920
18008
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
15921
18009
  };
15922
18010
  }, [isEditMode, refreshStateRules]);
15923
- (0, import_react15.useEffect)(() => {
18011
+ (0, import_react16.useEffect)(() => {
15924
18012
  if (!isEditMode) return;
15925
18013
  const THRESHOLD = 10;
15926
18014
  const resolveWasSelected = (el) => {
@@ -15936,13 +18024,13 @@ function OhhwellsBridge() {
15936
18024
  if (footerDragRef.current) return;
15937
18025
  const target = e.target;
15938
18026
  if (!target) return;
15939
- 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]')) {
18027
+ 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]')) {
15940
18028
  return;
15941
18029
  }
15942
18030
  if (target.closest("[data-ohw-item-drag-surface]")) return;
15943
- const anchor = getNavigationItemAnchor(target);
18031
+ const anchor = getNavigationItemAnchor(target) ?? (document.elementsFromPoint(e.clientX, e.clientY).map((el) => el instanceof HTMLElement ? getNavigationItemAnchor(el) : null).find((found) => found !== null) ?? null);
15944
18032
  const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
15945
- if (anchor && isFooterHrefKey(hrefKey)) {
18033
+ if (anchor && (isFooterHrefKey(hrefKey) || getSocialItem(anchor))) {
15946
18034
  footerPointerDragRef.current = {
15947
18035
  el: anchor,
15948
18036
  kind: "link",
@@ -15975,7 +18063,7 @@ function OhhwellsBridge() {
15975
18063
  clearTextSelection();
15976
18064
  const session = footerDragRef.current;
15977
18065
  if (!session) return;
15978
- const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
18066
+ 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);
15979
18067
  refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
15980
18068
  return;
15981
18069
  }
@@ -16002,7 +18090,7 @@ function OhhwellsBridge() {
16002
18090
  const column = findFooterColumnForLink(pending.el);
16003
18091
  const columns2 = listFooterColumns();
16004
18092
  beginFooterDragRef.current({
16005
- kind: "link",
18093
+ kind: getSocialItem(pending.el) ? "social" : "link",
16006
18094
  hrefKey: key,
16007
18095
  columnEl: column,
16008
18096
  sourceColumnIndex: column ? columns2.indexOf(column) : 0,
@@ -16074,7 +18162,7 @@ function OhhwellsBridge() {
16074
18162
  unlockFooterDragInteraction();
16075
18163
  };
16076
18164
  }, [isEditMode]);
16077
- (0, import_react15.useEffect)(() => {
18165
+ (0, import_react16.useEffect)(() => {
16078
18166
  const handler = (e) => {
16079
18167
  if (e.data?.type !== "ow:request-schedule-config") return;
16080
18168
  const insertAfterVal = e.data.insertAfter;
@@ -16090,7 +18178,7 @@ function OhhwellsBridge() {
16090
18178
  window.addEventListener("message", handler);
16091
18179
  return () => window.removeEventListener("message", handler);
16092
18180
  }, [processConfigRequest]);
16093
- (0, import_react15.useEffect)(() => {
18181
+ (0, import_react16.useEffect)(() => {
16094
18182
  if (!isEditMode) return;
16095
18183
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
16096
18184
  el.removeAttribute("data-ohw-active-state");
@@ -16114,7 +18202,7 @@ function OhhwellsBridge() {
16114
18202
  postToParent2({
16115
18203
  type: "ow:ready",
16116
18204
  version: "1",
16117
- bridgeVersion: "0.1.54",
18205
+ bridgeVersion: "0.1.55",
16118
18206
  path: pathname,
16119
18207
  nodes: collectEditableNodes(editContentRef.current),
16120
18208
  sections
@@ -16126,13 +18214,13 @@ function OhhwellsBridge() {
16126
18214
  clearTimeout(timer);
16127
18215
  };
16128
18216
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
16129
- (0, import_react15.useEffect)(() => {
18217
+ (0, import_react16.useEffect)(() => {
16130
18218
  scrollToHashSectionWhenReady();
16131
18219
  const onHashChange = () => scrollToHashSectionWhenReady();
16132
18220
  window.addEventListener("hashchange", onHashChange);
16133
18221
  return () => window.removeEventListener("hashchange", onHashChange);
16134
18222
  }, [pathname]);
16135
- const handleCommand = (0, import_react15.useCallback)((cmd) => {
18223
+ const handleCommand = (0, import_react16.useCallback)((cmd) => {
16136
18224
  const el = activeElRef.current;
16137
18225
  const selBefore = window.getSelection();
16138
18226
  let savedOffsets = null;
@@ -16168,7 +18256,7 @@ function OhhwellsBridge() {
16168
18256
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
16169
18257
  refreshActiveCommandsRef.current();
16170
18258
  }, []);
16171
- const handleStateChange = (0, import_react15.useCallback)((state) => {
18259
+ const handleStateChange = (0, import_react16.useCallback)((state) => {
16172
18260
  if (!activeStateElRef.current) return;
16173
18261
  const el = activeStateElRef.current;
16174
18262
  if (state === "Default") {
@@ -16181,7 +18269,7 @@ function OhhwellsBridge() {
16181
18269
  }
16182
18270
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
16183
18271
  }, [deactivate]);
16184
- const reselectAfterLinkPopover = (0, import_react15.useCallback)(
18272
+ const reselectAfterLinkPopover = (0, import_react16.useCallback)(
16185
18273
  (hrefKey) => {
16186
18274
  requestAnimationFrame(() => {
16187
18275
  const el = resolveHrefKeyElement(hrefKey);
@@ -16190,7 +18278,7 @@ function OhhwellsBridge() {
16190
18278
  },
16191
18279
  [resolveHrefKeyElement]
16192
18280
  );
16193
- const closeLinkPopover = (0, import_react15.useCallback)(() => {
18281
+ const closeLinkPopover = (0, import_react16.useCallback)(() => {
16194
18282
  const session = linkPopoverSessionRef.current;
16195
18283
  addNavAfterAnchorRef.current = null;
16196
18284
  setLinkPopover(null);
@@ -16198,9 +18286,9 @@ function OhhwellsBridge() {
16198
18286
  reselectAfterLinkPopover(session.key);
16199
18287
  }
16200
18288
  }, [reselectAfterLinkPopover]);
16201
- const closeLinkPopoverRef = (0, import_react15.useRef)(closeLinkPopover);
18289
+ const closeLinkPopoverRef = (0, import_react16.useRef)(closeLinkPopover);
16202
18290
  closeLinkPopoverRef.current = closeLinkPopover;
16203
- const openLinkPopoverForActive = (0, import_react15.useCallback)(() => {
18291
+ const openLinkPopoverForActive = (0, import_react16.useCallback)(() => {
16204
18292
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
16205
18293
  if (!hrefCtx) return;
16206
18294
  bumpLinkPopoverGrace();
@@ -16211,11 +18299,15 @@ function OhhwellsBridge() {
16211
18299
  });
16212
18300
  deactivate();
16213
18301
  }, [deactivate]);
16214
- const openLinkPopoverForSelected = (0, import_react15.useCallback)(() => {
18302
+ const openLinkPopoverForSelected = (0, import_react16.useCallback)(() => {
16215
18303
  const anchor = selectedElRef.current;
16216
18304
  if (!anchor) return;
16217
18305
  const key = anchor.getAttribute("data-ohw-href-key");
16218
18306
  if (!key) return;
18307
+ if (requestSocialDialog(anchor, postToParentRef.current, editContentRef.current)) {
18308
+ deselect();
18309
+ return;
18310
+ }
16219
18311
  bumpLinkPopoverGrace();
16220
18312
  setLinkPopover({
16221
18313
  key,
@@ -16224,7 +18316,7 @@ function OhhwellsBridge() {
16224
18316
  });
16225
18317
  deselect();
16226
18318
  }, [deselect]);
16227
- const handleSelectParent = (0, import_react15.useCallback)(() => {
18319
+ const handleSelectParent = (0, import_react16.useCallback)(() => {
16228
18320
  const selected = selectedElRef.current;
16229
18321
  if (!selected) return;
16230
18322
  if (toolbarVariantRef.current === "select-frame") {
@@ -16251,11 +18343,37 @@ function OhhwellsBridge() {
16251
18343
  }
16252
18344
  deselectRef.current();
16253
18345
  }, []);
16254
- const handleDuplicateSelected = (0, import_react15.useCallback)(() => {
18346
+ const handleDuplicateSelected = (0, import_react16.useCallback)(() => {
16255
18347
  const selected = selectedElRef.current;
16256
18348
  if (!selected || !isNavigationItem2(selected)) return;
16257
18349
  const hrefKey = selected.getAttribute("data-ohw-href-key");
16258
18350
  if (!hrefKey) return;
18351
+ const social = getSocialItem(selected);
18352
+ if (social) {
18353
+ const result = duplicateSocialItem(social, editContentRef.current);
18354
+ if (!result) return;
18355
+ const orderJson = JSON.stringify(result.order);
18356
+ const carried = [
18357
+ { from: result.copiedFrom?.href, to: result.hrefKey },
18358
+ { from: result.copiedFrom?.icon, to: result.iconKey },
18359
+ { from: result.copiedFrom?.icon ? socialPlatformKey(result.copiedFrom.icon) : null, to: socialPlatformKey(result.iconKey) }
18360
+ ];
18361
+ const nodes = [{ key: SOCIALS_ORDER_KEY, text: orderJson }];
18362
+ for (const { from, to } of carried) {
18363
+ const value = from ? editContentRef.current[from] : void 0;
18364
+ if (value) nodes.push({ key: to, text: value });
18365
+ }
18366
+ editContentRef.current = {
18367
+ ...editContentRef.current,
18368
+ ...Object.fromEntries(nodes.map((node) => [node.key, node.text]))
18369
+ };
18370
+ postToParent2({ type: "ow:change", nodes });
18371
+ enforceLinkHrefs();
18372
+ const copyRow = findSocialsRow(result.item);
18373
+ if (copyRow) applySocialsDisplayToRow(copyRow, socialsDisplayFor(copyRow, editContentRef.current));
18374
+ requestAnimationFrame(() => selectRef.current(result.item));
18375
+ return;
18376
+ }
16259
18377
  if (isNavbarHrefKey(hrefKey)) {
16260
18378
  const result = duplicateNavbarItem(selected);
16261
18379
  if (!result) return;
@@ -16341,7 +18459,7 @@ function OhhwellsBridge() {
16341
18459
  });
16342
18460
  }
16343
18461
  }, [postToParent2]);
16344
- const runPendingDeleteUndo = (0, import_react15.useCallback)(() => {
18462
+ const runPendingDeleteUndo = (0, import_react16.useCallback)(() => {
16345
18463
  const pending = pendingDeleteUndoRef.current;
16346
18464
  if (!pending) return false;
16347
18465
  pendingDeleteUndoRef.current = null;
@@ -16349,7 +18467,7 @@ function OhhwellsBridge() {
16349
18467
  enforceLinkHrefs();
16350
18468
  return true;
16351
18469
  }, []);
16352
- const handleDeleteSelected = (0, import_react15.useCallback)(() => {
18470
+ const handleDeleteSelected = (0, import_react16.useCallback)(() => {
16353
18471
  const selected = selectedElRef.current;
16354
18472
  if (!selected) return false;
16355
18473
  return deleteSelectedNavFooterItem({
@@ -16370,7 +18488,7 @@ function OhhwellsBridge() {
16370
18488
  }, [postToParent2]);
16371
18489
  handleDeleteSelectedRef.current = handleDeleteSelected;
16372
18490
  runPendingDeleteUndoRef.current = runPendingDeleteUndo;
16373
- const handleLinkPopoverSubmit = (0, import_react15.useCallback)(
18491
+ const handleLinkPopoverSubmit = (0, import_react16.useCallback)(
16374
18492
  (target) => {
16375
18493
  const session = linkPopoverSessionRef.current;
16376
18494
  if (!session) return;
@@ -16436,19 +18554,19 @@ function OhhwellsBridge() {
16436
18554
  const showEditLink = toolbarShowEditLink;
16437
18555
  const currentSections = sectionsByPath[pathname] ?? [];
16438
18556
  linkPopoverOpenRef.current = linkPopover !== null;
16439
- const handleMediaReplace = (0, import_react15.useCallback)(
18557
+ const handleMediaReplace = (0, import_react16.useCallback)(
16440
18558
  (key) => {
16441
18559
  postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
16442
18560
  },
16443
18561
  [postToParent2, mediaHover?.elementType]
16444
18562
  );
16445
- const handleEditCarousel = (0, import_react15.useCallback)(
18563
+ const handleEditCarousel = (0, import_react16.useCallback)(
16446
18564
  (key) => {
16447
18565
  postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
16448
18566
  },
16449
18567
  [postToParent2]
16450
18568
  );
16451
- const handleMediaFadeOutComplete = (0, import_react15.useCallback)((key) => {
18569
+ const handleMediaFadeOutComplete = (0, import_react16.useCallback)((key) => {
16452
18570
  setUploadingRects((prev) => {
16453
18571
  if (!(key in prev)) return prev;
16454
18572
  const next = { ...prev };
@@ -16456,7 +18574,7 @@ function OhhwellsBridge() {
16456
18574
  return next;
16457
18575
  });
16458
18576
  }, []);
16459
- const handleVideoSettingsChange = (0, import_react15.useCallback)(
18577
+ const handleVideoSettingsChange = (0, import_react16.useCallback)(
16460
18578
  (key, settings) => {
16461
18579
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
16462
18580
  const video = getVideoEl2(el);
@@ -16479,10 +18597,10 @@ function OhhwellsBridge() {
16479
18597
  [postToParent2]
16480
18598
  );
16481
18599
  return bridgeRoot ? (0, import_react_dom4.createPortal)(
16482
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
16483
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
16484
- isEditMode && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
16485
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18600
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
18601
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
18602
+ isEditMode && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
18603
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16486
18604
  MediaOverlay,
16487
18605
  {
16488
18606
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -16493,7 +18611,7 @@ function OhhwellsBridge() {
16493
18611
  },
16494
18612
  `uploading-${key}`
16495
18613
  )),
16496
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18614
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16497
18615
  MediaOverlay,
16498
18616
  {
16499
18617
  hover: mediaHover,
@@ -16502,11 +18620,11 @@ function OhhwellsBridge() {
16502
18620
  onVideoSettingsChange: handleVideoSettingsChange
16503
18621
  }
16504
18622
  ),
16505
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
16506
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
16507
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
16508
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
16509
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18623
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
18624
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
18625
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
18626
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
18627
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16510
18628
  "div",
16511
18629
  {
16512
18630
  className: "pointer-events-none fixed z-2147483646",
@@ -16516,7 +18634,7 @@ function OhhwellsBridge() {
16516
18634
  width: slot.width,
16517
18635
  height: slot.height
16518
18636
  },
16519
- children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18637
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16520
18638
  DropIndicator,
16521
18639
  {
16522
18640
  direction: slot.direction,
@@ -16527,7 +18645,7 @@ function OhhwellsBridge() {
16527
18645
  },
16528
18646
  `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
16529
18647
  )),
16530
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18648
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16531
18649
  "div",
16532
18650
  {
16533
18651
  className: "pointer-events-none fixed z-2147483646",
@@ -16537,7 +18655,7 @@ function OhhwellsBridge() {
16537
18655
  width: slot.width,
16538
18656
  height: slot.height
16539
18657
  },
16540
- children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18658
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16541
18659
  DropIndicator,
16542
18660
  {
16543
18661
  direction: slot.direction,
@@ -16548,10 +18666,11 @@ function OhhwellsBridge() {
16548
18666
  },
16549
18667
  `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
16550
18668
  )),
16551
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
16552
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
16553
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
16554
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18669
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
18670
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
18671
+ hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
18672
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
18673
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16555
18674
  FooterContainerChrome,
16556
18675
  {
16557
18676
  rect: toolbarRect,
@@ -16559,7 +18678,7 @@ function OhhwellsBridge() {
16559
18678
  addDisabled: !canAddFooterColumn()
16560
18679
  }
16561
18680
  ),
16562
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18681
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16563
18682
  ItemInteractionLayer,
16564
18683
  {
16565
18684
  rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
@@ -16571,26 +18690,34 @@ function OhhwellsBridge() {
16571
18690
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
16572
18691
  onDragHandleDragStart: handleItemDragStart,
16573
18692
  onDragHandleDragEnd: handleItemDragEnd,
16574
- onItemPointerDown: handleItemChromePointerDown,
16575
- onItemClick: handleItemChromeClick,
16576
- itemDragSurface: !isFooterFrameSelection,
16577
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && isFooterFrameSelection && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18693
+ onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
18694
+ onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
18695
+ itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
18696
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16578
18697
  ItemActionToolbar,
16579
18698
  {
16580
18699
  onEditLink: openLinkPopoverForSelected,
18700
+ onStyle: () => {
18701
+ const row = selectedElRef.current;
18702
+ if (!row) return;
18703
+ if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
18704
+ else openSocialsDisplayPanel(row);
18705
+ },
18706
+ showStyle: selectedIsSocialsRow,
18707
+ styleActive: floatingPanel?.kind === "socials-display",
16581
18708
  onAddItem: handleAddChildItem,
16582
18709
  onSelectParent: handleSelectParent,
16583
18710
  onDuplicate: handleDuplicateSelected,
16584
18711
  onDelete: handleDeleteSelected,
16585
- addItemDisabled: false,
18712
+ addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current),
16586
18713
  editLinkDisabled: false,
16587
18714
  moreDisabled: false,
16588
- duplicateDisabled: isFooterFrameSelection,
16589
- showEditLink: !isFooterFrameSelection && navDropdownPreviewOpen === null,
16590
- showAddItem: isFooterFrameSelection || !selectedIsCta && Boolean(
18715
+ duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow,
18716
+ showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
18717
+ showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
16591
18718
  selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
16592
18719
  ),
16593
- showMore: !selectedIsCta || isFooterFrameSelection,
18720
+ showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
16594
18721
  dropdownOpen: navDropdownPreviewOpen,
16595
18722
  onDropdownOpenChange: handleNavDropdownOpenChange,
16596
18723
  headingVisible: footerHeadingVisible,
@@ -16599,8 +18726,8 @@ function OhhwellsBridge() {
16599
18726
  ) : void 0
16600
18727
  }
16601
18728
  ),
16602
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
16603
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18729
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
18730
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16604
18731
  EditGlowChrome,
16605
18732
  {
16606
18733
  rect: toolbarRect,
@@ -16610,7 +18737,7 @@ function OhhwellsBridge() {
16610
18737
  hideHandle: isItemDragging
16611
18738
  }
16612
18739
  ),
16613
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18740
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16614
18741
  FloatingToolbar,
16615
18742
  {
16616
18743
  rect: toolbarRect,
@@ -16623,7 +18750,7 @@ function OhhwellsBridge() {
16623
18750
  }
16624
18751
  )
16625
18752
  ] }),
16626
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
18753
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
16627
18754
  "div",
16628
18755
  {
16629
18756
  "data-ohw-max-badge": "",
@@ -16649,7 +18776,7 @@ function OhhwellsBridge() {
16649
18776
  ]
16650
18777
  }
16651
18778
  ),
16652
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18779
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16653
18780
  StateToggle,
16654
18781
  {
16655
18782
  rect: toggleState.rect,
@@ -16658,15 +18785,15 @@ function OhhwellsBridge() {
16658
18785
  onStateChange: handleStateChange
16659
18786
  }
16660
18787
  ),
16661
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
18788
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
16662
18789
  "div",
16663
18790
  {
16664
18791
  "data-ohw-section-insert-line": "",
16665
18792
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
16666
18793
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
16667
18794
  children: [
16668
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
16669
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18795
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
18796
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16670
18797
  Badge,
16671
18798
  {
16672
18799
  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",
@@ -16683,11 +18810,11 @@ function OhhwellsBridge() {
16683
18810
  children: "Add Section"
16684
18811
  }
16685
18812
  ),
16686
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
18813
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
16687
18814
  ]
16688
18815
  }
16689
18816
  ),
16690
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
18817
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16691
18818
  LinkPopover,
16692
18819
  {
16693
18820
  panelRef: linkPopoverPanelRef,
@@ -16703,11 +18830,137 @@ function OhhwellsBridge() {
16703
18830
  onSubmit: handleLinkPopoverSubmit
16704
18831
  },
16705
18832
  linkPopover.key
18833
+ ) : null,
18834
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
18835
+ FloatingPanel,
18836
+ {
18837
+ open: true,
18838
+ title: floatingPanel.title,
18839
+ context: floatingPanel.context,
18840
+ position: floatingPanelPos,
18841
+ onPositionChange: setFloatingPanelPos,
18842
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
18843
+ onClose: closeFloatingPanelOnly,
18844
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
18845
+ SocialsDisplayPanel,
18846
+ {
18847
+ display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
18848
+ onChange: (next) => {
18849
+ changeSocialsDisplay(floatingPanel.row, next);
18850
+ setFloatingPanel({ ...floatingPanel });
18851
+ }
18852
+ }
18853
+ )
18854
+ }
18855
+ ) : null,
18856
+ floatingPanel && floatingPanel.kind === "logo-size" && logoSizeDraft ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
18857
+ FloatingPanel,
18858
+ {
18859
+ open: true,
18860
+ title: floatingPanel.title,
18861
+ context: floatingPanel.context,
18862
+ position: floatingPanelPos,
18863
+ onPositionChange: setFloatingPanelPos,
18864
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
18865
+ onClose: closeFloatingPanelAndDeselect,
18866
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
18867
+ LogoSizePanel,
18868
+ {
18869
+ viewport: editorViewport,
18870
+ sizePx: editorViewport === "mobile" && !logoSizeDraft.mobileFollowing ? logoSizeDraft.mobilePx : logoSizeDraft.desktopPx,
18871
+ mobileFollowing: logoSizeDraft.mobileFollowing,
18872
+ onSizeChange: (px) => {
18873
+ const next = editorViewport === "mobile" ? { ...logoSizeDraft, mobilePx: px, mobileFollowing: false } : {
18874
+ ...logoSizeDraft,
18875
+ desktopPx: px,
18876
+ mobilePx: logoSizeDraft.mobileFollowing ? px : logoSizeDraft.mobilePx
18877
+ };
18878
+ setLogoSizeDraft(next);
18879
+ persistLogoSizeDraft(floatingPanel.placement, next);
18880
+ },
18881
+ onCustomizeMobile: () => {
18882
+ const next = {
18883
+ ...logoSizeDraft,
18884
+ mobileFollowing: false,
18885
+ mobilePx: logoSizeDraft.desktopPx
18886
+ };
18887
+ setLogoSizeDraft(next);
18888
+ persistLogoSizeDraft(floatingPanel.placement, next);
18889
+ },
18890
+ onResetMobile: () => {
18891
+ const next = {
18892
+ ...logoSizeDraft,
18893
+ mobileFollowing: true,
18894
+ mobilePx: logoSizeDraft.desktopPx
18895
+ };
18896
+ setLogoSizeDraft(next);
18897
+ persistLogoSizeDraft(floatingPanel.placement, next);
18898
+ },
18899
+ onUpdateEverywhere: () => {
18900
+ const identity = readLogoIdentityFromDom();
18901
+ postToParent2({ type: "ow:open-logo-settings", ...identity });
18902
+ }
18903
+ }
18904
+ )
18905
+ }
16706
18906
  ) : null
16707
18907
  ] }),
16708
18908
  bridgeRoot
16709
18909
  ) : null;
16710
18910
  }
18911
+
18912
+ // src/ui/EmptySection.tsx
18913
+ var import_link = __toESM(require("next/link"), 1);
18914
+ var import_jsx_runtime34 = require("react/jsx-runtime");
18915
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
18916
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
18917
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
18918
+ "p",
18919
+ {
18920
+ style: {
18921
+ fontFamily: "var(--brand-font-body)",
18922
+ fontSize: "0.75rem",
18923
+ fontWeight: 500,
18924
+ letterSpacing: "0.15em",
18925
+ textTransform: "uppercase",
18926
+ color: "var(--brand-accent)",
18927
+ marginBottom: "1.5rem"
18928
+ },
18929
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_link.default, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
18930
+ }
18931
+ ),
18932
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
18933
+ "h1",
18934
+ {
18935
+ style: {
18936
+ fontFamily: "var(--brand-font-heading)",
18937
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
18938
+ lineHeight: 1.1,
18939
+ letterSpacing: "-0.025em",
18940
+ color: "var(--brand-text)",
18941
+ marginBottom: "1rem"
18942
+ },
18943
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
18944
+ children: title
18945
+ }
18946
+ ),
18947
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
18948
+ "p",
18949
+ {
18950
+ style: {
18951
+ fontFamily: "var(--brand-font-body)",
18952
+ fontSize: "1rem",
18953
+ lineHeight: 1.7,
18954
+ fontWeight: 300,
18955
+ color: "var(--brand-text-muted)",
18956
+ maxWidth: "340px"
18957
+ },
18958
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
18959
+ children: "This page doesn't have any content yet."
18960
+ }
18961
+ )
18962
+ ] });
18963
+ }
16711
18964
  // Annotate the CommonJS export names for ESM import in node:
16712
18965
  0 && (module.exports = {
16713
18966
  AI_DEFAULT_BRAND,
@@ -16725,6 +18978,7 @@ function OhhwellsBridge() {
16725
18978
  DropdownMenuItem,
16726
18979
  DropdownMenuSeparator,
16727
18980
  DropdownMenuTrigger,
18981
+ EmptySection,
16728
18982
  ItemActionToolbar,
16729
18983
  ItemInteractionLayer,
16730
18984
  LinkEditorPanel,