@ohhwells/bridge 0.1.140 → 0.1.142

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
@@ -72,6 +72,7 @@ __export(index_exports, {
72
72
  dropIndicatorVariants: () => dropIndicatorVariants,
73
73
  ensureFooterTree: () => ensureFooterTree,
74
74
  filterAvailablePages: () => filterAvailablePages,
75
+ foldAlignIntoTrees: () => foldAlignIntoTrees,
75
76
  getEditModeInitialState: () => getEditModeInitialState,
76
77
  insertFooterHours: () => insertFooterHours,
77
78
  isEditSessionActive: () => isEditSessionActive,
@@ -80,6 +81,7 @@ __export(index_exports, {
80
81
  isValidUrl: () => isValidUrl,
81
82
  nextViableSlide: () => nextViableSlide,
82
83
  parseAiFooterState: () => parseAiFooterState,
84
+ parseAiSectionsState: () => parseAiSectionsState,
83
85
  parseTarget: () => parseTarget,
84
86
  removeFooterBlock: () => removeFooterBlock,
85
87
  resolveFooterCopyright: () => resolveFooterCopyright,
@@ -1348,15 +1350,25 @@ function readAssignments(content) {
1348
1350
  function cssLength(style, prop) {
1349
1351
  return style.getPropertyValue(prop) || "0px";
1350
1352
  }
1353
+ function withTransitionsSettled(el, read) {
1354
+ const prior = el.style.getPropertyValue("transition");
1355
+ const priority = el.style.getPropertyPriority("transition");
1356
+ el.style.setProperty("transition", "none", "important");
1357
+ try {
1358
+ return read(getComputedStyle(el));
1359
+ } finally {
1360
+ if (prior) el.style.setProperty("transition", prior, priority);
1361
+ else el.style.removeProperty("transition");
1362
+ }
1363
+ }
1351
1364
  function readVariantSample(el) {
1352
- const style = getComputedStyle(el);
1353
- return {
1365
+ return withTransitionsSettled(el, (style) => ({
1354
1366
  background: style.backgroundColor,
1355
1367
  color: style.color,
1356
1368
  borderColor: style.borderTopColor,
1357
1369
  borderWidth: style.borderTopWidth,
1358
1370
  className: el.className || ""
1359
- };
1371
+ }));
1360
1372
  }
1361
1373
  function readSizeSample(el) {
1362
1374
  const style = getComputedStyle(el);
@@ -5296,19 +5308,21 @@ function findIndexSegment(keys) {
5296
5308
  if (len < 2 || tokenLists.some((t) => t.length !== len)) return null;
5297
5309
  let position = -1;
5298
5310
  for (let p = 0; p < len; p++) {
5299
- const values = tokenLists.map((t) => t[p]);
5300
- const allSame = values.every((v) => v === values[0]);
5311
+ const values2 = tokenLists.map((t) => t[p]);
5312
+ const allSame = values2.every((v) => v === values2[0]);
5301
5313
  if (allSame) continue;
5302
- const allNumeric = values.every((v) => /^\d+$/.test(v));
5303
- if (!allNumeric || position !== -1) return null;
5314
+ if (position !== -1) return null;
5304
5315
  position = p;
5305
5316
  }
5306
5317
  if (position === -1) return null;
5318
+ const values = tokenLists.map((t) => t[position]);
5319
+ const numericCount = values.filter((v) => /^\d+$/.test(v)).length;
5320
+ if (numericCount !== 0 && numericCount !== values.length) return null;
5321
+ if (numericCount === 0 && position === len - 1) return null;
5307
5322
  const group = tokenLists[0].slice(0, position).join("-");
5308
5323
  if (!group) return null;
5309
- const indices = tokenLists.map((t) => parseInt(t[position], 10));
5310
- if (new Set(indices).size !== indices.length) return null;
5311
- return { position, group };
5324
+ if (new Set(values).size !== values.length) return null;
5325
+ return numericCount === values.length ? { position, group } : { position, group, slug: true };
5312
5326
  }
5313
5327
  function singleIndexSegment(key) {
5314
5328
  const tokens = baseKey(key).split("-");
@@ -5354,7 +5368,19 @@ function detectItemGroups(sectionEl) {
5354
5368
  list.push(m);
5355
5369
  byShape.set(shape, list);
5356
5370
  }
5357
- clusters.push(...Array.from(byShape.values()));
5371
+ const shaped = Array.from(byShape.values());
5372
+ clusters.push(...shaped.filter((c) => c.length >= 2));
5373
+ const singles = shaped.filter((c) => c.length === 1).map((c) => c[0]);
5374
+ const bySlugShape = /* @__PURE__ */ new Map();
5375
+ for (const m of singles) {
5376
+ const primary = primaryKey(keyedDescendants(m));
5377
+ const tokens = baseKey(primary ?? "").split("-");
5378
+ const slugShape = `${tokens.length}|${tokens[0]}|${tokens[tokens.length - 1]}`;
5379
+ const list = bySlugShape.get(slugShape) ?? [];
5380
+ list.push(m);
5381
+ bySlugShape.set(slugShape, list);
5382
+ }
5383
+ clusters.push(...Array.from(bySlugShape.values()));
5358
5384
  }
5359
5385
  for (const members of clusters) {
5360
5386
  const keyedPerMember = members.map((m) => keyedDescendants(m));
@@ -5365,11 +5391,28 @@ function detectItemGroups(sectionEl) {
5365
5391
  const dedupeKey = `${segment.group}`;
5366
5392
  if (seenParents.has(dedupeKey)) continue;
5367
5393
  seenParents.add(dedupeKey);
5368
- const items = members.map((el, i) => ({
5369
- el,
5370
- index: indexFromKey(primaries[i], segment.position),
5371
- keyed: keyedPerMember[i]
5372
- }));
5394
+ let items;
5395
+ if (segment.slug) {
5396
+ const tokens = members.map((_, i) => baseKey(primaries[i]).split("-")[segment.position]);
5397
+ let nextOriginal = 1;
5398
+ const indices = tokens.map((t) => {
5399
+ const cloneMatch = t.match(/^x(\d+)$/);
5400
+ return cloneMatch ? parseInt(cloneMatch[1], 10) : nextOriginal++;
5401
+ });
5402
+ if (new Set(indices).size !== indices.length) continue;
5403
+ items = members.map((el, i) => ({
5404
+ el,
5405
+ index: indices[i],
5406
+ keyed: keyedPerMember[i],
5407
+ slugToken: tokens[i]
5408
+ }));
5409
+ } else {
5410
+ items = members.map((el, i) => ({
5411
+ el,
5412
+ index: indexFromKey(primaries[i], segment.position),
5413
+ keyed: keyedPerMember[i]
5414
+ }));
5415
+ }
5373
5416
  groups.push({ group: segment.group, parent, nested: false, items });
5374
5417
  }
5375
5418
  }
@@ -5493,6 +5536,18 @@ function rekeyItemClone(clone, source, sibling, group, newIndex) {
5493
5536
  const delta = newIndex - source.index;
5494
5537
  const cloneKeyed = keyedDescendants(clone);
5495
5538
  const pairs = [];
5539
+ if (source.slugToken) {
5540
+ source.keyed.forEach((src, i) => {
5541
+ const target = cloneKeyed[i];
5542
+ if (!target || target.attr !== src.attr) return;
5543
+ const tokens = baseKey(src.key).split("-");
5544
+ if (!tokens.includes(source.slugToken)) return;
5545
+ const next = tokens.map((t) => t === source.slugToken ? `x${newIndex}` : t).join("-") + keySuffix(src.key);
5546
+ target.el.setAttribute(src.attr, next);
5547
+ pairs.push({ from: src.key, to: next });
5548
+ });
5549
+ return pairs;
5550
+ }
5496
5551
  source.keyed.forEach((src, i) => {
5497
5552
  const target = cloneKeyed[i];
5498
5553
  if (!target || target.attr !== src.attr) return;
@@ -5536,7 +5591,12 @@ function syncGroup(detected, state) {
5536
5591
  clone.setAttribute(ITEM_ATTR, `${detected.group}:${entry.index}`);
5537
5592
  rekeyItemClone(clone, source, sibling, detected.group, entry.index);
5538
5593
  source.el.insertAdjacentElement("afterend", clone);
5539
- byIndex.set(entry.index, { el: clone, index: entry.index, keyed: keyedDescendants(clone) });
5594
+ byIndex.set(entry.index, {
5595
+ el: clone,
5596
+ index: entry.index,
5597
+ keyed: keyedDescendants(clone),
5598
+ ...source.slugToken ? { slugToken: `x${entry.index}` } : {}
5599
+ });
5540
5600
  }
5541
5601
  const listed = state.items.map((e) => byIndex.get(e.index)?.el).filter((el) => !!el);
5542
5602
  let prev = null;
@@ -5638,9 +5698,16 @@ function applyItemOps(store, ops, currentContent) {
5638
5698
  const nodes = [];
5639
5699
  const added = [];
5640
5700
  let elements = syncGroup(detected, state);
5701
+ const sluggy = detected.items.some((item) => item.slugToken);
5702
+ const slugTokenOf = (index) => detected.items.find((item) => item.index === index)?.slugToken ?? (sluggy ? `x${index}` : void 0);
5641
5703
  for (const add of ops.add ?? []) {
5642
5704
  const sourceEl = elements.get(add.cloneOf);
5643
- const source = sourceEl ? { el: sourceEl, index: add.cloneOf, keyed: keyedDescendants(sourceEl) } : void 0;
5705
+ const source = sourceEl ? {
5706
+ el: sourceEl,
5707
+ index: add.cloneOf,
5708
+ keyed: keyedDescendants(sourceEl),
5709
+ ...slugTokenOf(add.cloneOf) ? { slugToken: slugTokenOf(add.cloneOf) } : {}
5710
+ } : void 0;
5644
5711
  if (!source) continue;
5645
5712
  const newIndex = Math.max(...state.items.map((e) => e.index), ...detected.items.map((i) => i.index)) + 1;
5646
5713
  const sibling = detected.items.find((item) => item.index !== add.cloneOf && !item.el.hasAttribute(ITEM_CLONE_ATTR)) ?? null;
@@ -5667,7 +5734,12 @@ function applyItemOps(store, ops, currentContent) {
5667
5734
  if (anchor) anchor.insertAdjacentElement("afterend", clone);
5668
5735
  else source.el.insertAdjacentElement("afterend", clone);
5669
5736
  elements.set(newIndex, clone);
5670
- detected.items.push({ el: clone, index: newIndex, keyed: cloneKeyed });
5737
+ detected.items.push({
5738
+ el: clone,
5739
+ index: newIndex,
5740
+ keyed: cloneKeyed,
5741
+ ...sluggy ? { slugToken: `x${newIndex}` } : {}
5742
+ });
5671
5743
  added.push(newIndex);
5672
5744
  elements = syncGroup(detected, state);
5673
5745
  }
@@ -6110,16 +6182,25 @@ function foldAlignIntoTrees(state, store) {
6110
6182
  nextTrees.set(id, fresh);
6111
6183
  return fresh;
6112
6184
  };
6185
+ const stripChildAligns = (node) => node.children ? {
6186
+ ...node,
6187
+ children: node.children.map((child) => {
6188
+ const { align: _dropped, ...rest } = child;
6189
+ return stripChildAligns(rest);
6190
+ })
6191
+ } : node;
6113
6192
  const nodes = {};
6114
6193
  for (const [key, override] of Object.entries(store.nodes)) {
6115
6194
  const match = override.align !== void 0 && key.startsWith(AI_SLOT_KEY_PREFIX) ? key.match(/^ai\.(.+?)\.r(\d+)\.b(\d+)(?:\.|$)/) : null;
6116
6195
  const tree = match ? treeFor(match[1]) : void 0;
6117
- const block = match && tree ? tree.rows[Number(match[2])]?.blocks[Number(match[3])] : void 0;
6118
- if (!block) {
6196
+ const row = match && tree ? tree.rows[Number(match[2])] : void 0;
6197
+ const blockIdx = match ? Number(match[3]) : -1;
6198
+ const block = row?.blocks[blockIdx];
6199
+ if (!row || !block) {
6119
6200
  nodes[key] = override;
6120
6201
  continue;
6121
6202
  }
6122
- block.align = override.align;
6203
+ row.blocks[blockIdx] = { ...stripChildAligns(block), align: override.align };
6123
6204
  const rest = { ...override };
6124
6205
  delete rest.align;
6125
6206
  if (Object.keys(rest).length > 0) nodes[key] = rest;
@@ -6132,7 +6213,7 @@ function foldAlignIntoTrees(state, store) {
6132
6213
  continue;
6133
6214
  }
6134
6215
  for (const row of tree.rows) {
6135
- for (const block of row.blocks) block.align = override.align;
6216
+ row.blocks = row.blocks.map((block) => ({ ...stripChildAligns(block), align: override.align }));
6136
6217
  }
6137
6218
  const rest = { ...override };
6138
6219
  delete rest.align;
@@ -7293,6 +7374,7 @@ var blockAlignStyle = (blockAlign) => blockAlign ? {
7293
7374
  alignItems: blockAlign === "left" ? "flex-start" : blockAlign === "right" ? "flex-end" : "center",
7294
7375
  textAlign: blockAlign
7295
7376
  } : {};
7377
+ var fullBleedBlock = (block) => block.type === "carousel" || block.type === "group" && str(block.slots?.layout) === "overlap";
7296
7378
  var featureLines = (value) => value.split(/\n|<br\s*\/?>/i).map((line) => line.trim()).filter(Boolean);
7297
7379
  var AI_MOBILE_CSS = [
7298
7380
  // Unscoped (applies at every viewport, not just mobile): re-enables pointer-events for the
@@ -9358,8 +9440,8 @@ function renderNode(node, ctx, path, ambientAlign) {
9358
9440
  // layer (this group's i===0 sibling) can span edge-to-edge — reintroduced here,
9359
9441
  // scoped to just the overlay text, so a heading/copy/CTA stack doesn't stretch
9360
9442
  // the full section width instead of reading at the usual content width.
9361
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { maxWidth: AI_TREE_TOKENS.sectionMaxWidth, margin: "0 auto", padding: "0 40px", width: "100%", boxSizing: "border-box" }, children: renderNode(child, ctx, `${path}.c${i}`, child.align ?? "center") })
9362
- ) : renderNode(child, ctx, `${path}.c${i}`, i > 0 ? child.align ?? "center" : void 0),
9443
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { maxWidth: AI_TREE_TOKENS.sectionMaxWidth, margin: "0 auto", padding: "0 40px", width: "100%", boxSizing: "border-box" }, children: renderNode(child, ctx, `${path}.c${i}`, child.align ?? node.align ?? "center") })
9444
+ ) : renderNode(child, ctx, `${path}.c${i}`, i > 0 ? child.align ?? node.align ?? "center" : void 0),
9363
9445
  i === 0 && needsBaseScrim && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9364
9446
  "div",
9365
9447
  {
@@ -9392,7 +9474,7 @@ function renderNode(node, ctx, path, ambientAlign) {
9392
9474
  // child otherwise stretches full-width (unchanged pre-existing behaviour).
9393
9475
  ...blockAlignStyle(child.align ?? ambientAlign)
9394
9476
  },
9395
- children: renderNode(child, ctx, `${path}.c${i}`, ambientAlign)
9477
+ children: renderNode(child, ctx, `${path}.c${i}`, node.align ?? ambientAlign)
9396
9478
  },
9397
9479
  i
9398
9480
  ));
@@ -9422,8 +9504,20 @@ function renderNode(node, ctx, path, ambientAlign) {
9422
9504
  }
9423
9505
  case "button":
9424
9506
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ButtonEl, { slots, ctx, path });
9425
- case "button-row":
9426
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", gap: AI_TREE_TOKENS.spacing6, justifyContent: "center" }, children: (node.children ?? []).map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ButtonEl, { slots: child.slots ?? {}, ctx, path: `${path}.c${i}` }, i)) });
9507
+ case "button-row": {
9508
+ const rowAlign = node.align ?? ambientAlign;
9509
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9510
+ "div",
9511
+ {
9512
+ style: {
9513
+ display: "flex",
9514
+ gap: AI_TREE_TOKENS.spacing6,
9515
+ justifyContent: rowAlign === "left" ? "flex-start" : rowAlign === "right" ? "flex-end" : "center"
9516
+ },
9517
+ children: (node.children ?? []).map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ButtonEl, { slots: child.slots ?? {}, ctx, path: `${path}.c${i}` }, i))
9518
+ }
9519
+ );
9520
+ }
9427
9521
  case "media":
9428
9522
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9429
9523
  MediaBox,
@@ -9802,7 +9896,7 @@ function AiTreeRenderer({
9802
9896
  // its own horizontal `align`, which takes priority for that block.
9803
9897
  ...fillHeight ? { display: "grid" } : {},
9804
9898
  // Horizontal placement of the block's content within its column.
9805
- ...cellAlignStyle(block.align),
9899
+ ...fullBleedBlock(block) ? {} : cellAlignStyle(block.align),
9806
9900
  // space-between: each column becomes a flex column whose content spreads over
9807
9901
  // the full row height instead of clumping at the top.
9808
9902
  ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
@@ -10197,38 +10291,39 @@ function deriveTemplateButtonStyle() {
10197
10291
  el.querySelectorAll("[data-ohw-editable]").length <= 1
10198
10292
  );
10199
10293
  const isFilled = (el) => {
10200
- const bg = getComputedStyle(el).backgroundColor;
10294
+ const bg = withTransitionsSettled(el, (s) => s.backgroundColor);
10201
10295
  if (!bg || bg === "transparent") return false;
10202
10296
  const alpha = bg.match(/rgba?\([^)]*,\s*([\d.]+)\)$/);
10203
10297
  return !alpha || parseFloat(alpha[1]) > 0;
10204
10298
  };
10205
10299
  const btn = candidates.find(isFilled) ?? candidates[0];
10206
10300
  if (!btn) return null;
10207
- const cs = getComputedStyle(btn);
10208
- const filled = isFilled(btn);
10209
- const corners = [
10210
- cs.borderTopLeftRadius,
10211
- cs.borderTopRightRadius,
10212
- cs.borderBottomRightRadius,
10213
- cs.borderBottomLeftRadius
10214
- ].map((v) => v || "0px");
10215
- const radius = corners.every((v) => v === corners[0]) ? corners[0] : corners.join(" ");
10216
- const px2 = (v) => parseFloat(v) || 0;
10217
- const lineHeight = px2(cs.lineHeight) || px2(cs.fontSize) * 1.2;
10218
- const contentH = btn.getBoundingClientRect().height - px2(cs.borderTopWidth) - px2(cs.borderBottomWidth);
10219
- const impliedY = Math.round(Math.max(0, (contentH - lineHeight) / 2));
10220
- const padY = Math.max(px2(cs.paddingTop), px2(cs.paddingBottom), impliedY);
10221
- const padX = Math.max(px2(cs.paddingLeft), px2(cs.paddingRight));
10222
- return {
10223
- radius: radius || "10px",
10224
- padding: `${padY}px ${padX}px`,
10225
- ...filled ? { background: cs.backgroundColor, color: cs.color } : {},
10226
- fontFamily: cs.fontFamily || "",
10227
- fontSize: cs.fontSize || "",
10228
- fontWeight: cs.fontWeight || "",
10229
- letterSpacing: cs.letterSpacing || "",
10230
- textTransform: cs.textTransform || ""
10231
- };
10301
+ return withTransitionsSettled(btn, (cs) => {
10302
+ const filled = isFilled(btn);
10303
+ const corners = [
10304
+ cs.borderTopLeftRadius,
10305
+ cs.borderTopRightRadius,
10306
+ cs.borderBottomRightRadius,
10307
+ cs.borderBottomLeftRadius
10308
+ ].map((v) => v || "0px");
10309
+ const radius = corners.every((v) => v === corners[0]) ? corners[0] : corners.join(" ");
10310
+ const px2 = (v) => parseFloat(v) || 0;
10311
+ const lineHeight = px2(cs.lineHeight) || px2(cs.fontSize) * 1.2;
10312
+ const contentH = btn.getBoundingClientRect().height - px2(cs.borderTopWidth) - px2(cs.borderBottomWidth);
10313
+ const impliedY = Math.round(Math.max(0, (contentH - lineHeight) / 2));
10314
+ const padY = Math.max(px2(cs.paddingTop), px2(cs.paddingBottom), impliedY);
10315
+ const padX = Math.max(px2(cs.paddingLeft), px2(cs.paddingRight));
10316
+ return {
10317
+ radius: radius || "10px",
10318
+ padding: `${padY}px ${padX}px`,
10319
+ ...filled ? { background: cs.backgroundColor, color: cs.color } : {},
10320
+ fontFamily: cs.fontFamily || "",
10321
+ fontSize: cs.fontSize || "",
10322
+ fontWeight: cs.fontWeight || "",
10323
+ letterSpacing: cs.letterSpacing || "",
10324
+ textTransform: cs.textTransform || ""
10325
+ };
10326
+ });
10232
10327
  }
10233
10328
  var mounted = /* @__PURE__ */ new Map();
10234
10329
  function findTemplateSection(id) {
@@ -18436,10 +18531,23 @@ function setPricingCardStyle(tree, index, style) {
18436
18531
  });
18437
18532
  }
18438
18533
  function setPricingCardFeaturedTag(tree, index, on) {
18439
- return withPricingCard(tree, index, (target) => {
18534
+ return withPricingCollection(tree, (collection) => {
18535
+ const children = collection.children;
18536
+ if (index < 0 || index >= children.length) return false;
18537
+ if (on) {
18538
+ children.forEach((card, i) => {
18539
+ if (i === index) return;
18540
+ const prev2 = featuredOf(card);
18541
+ if (prev2.enabled !== true || badgeOf(prev2).length === 0) return;
18542
+ card.slots = card.slots ?? {};
18543
+ card.slots.featured = composeFeatured(pricingCardStyleOf(prev2), false, badgeOf(prev2));
18544
+ });
18545
+ }
18546
+ const target = children[index];
18547
+ target.slots = target.slots ?? {};
18440
18548
  const prev = featuredOf(target);
18441
- const style = pricingCardStyleOf(prev);
18442
- target.slots.featured = composeFeatured(style, on, badgeOf(prev));
18549
+ target.slots.featured = composeFeatured(pricingCardStyleOf(prev), on, badgeOf(prev));
18550
+ return true;
18443
18551
  });
18444
18552
  }
18445
18553
  function pricingCardStyleOf(featured) {
@@ -18689,25 +18797,40 @@ function ToolbarSeparator() {
18689
18797
  }
18690
18798
  function CardStyleMenu({
18691
18799
  value,
18692
- onChange
18800
+ onChange,
18801
+ disabled = false
18693
18802
  }) {
18694
18803
  const current = PRICING_CARD_STYLES.find((s) => s.value === value) ?? PRICING_CARD_STYLES[0];
18804
+ const trigger = /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18805
+ "button",
18806
+ {
18807
+ type: "button",
18808
+ "aria-label": "Card style",
18809
+ disabled,
18810
+ className: cn(
18811
+ "inline-flex h-8 min-w-[110px] items-center gap-[7px] rounded-[6px] bg-transparent px-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted data-[state=open]:bg-muted",
18812
+ disabled && "cursor-not-allowed opacity-40 hover:bg-transparent"
18813
+ ),
18814
+ children: [
18815
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StyleSwatch, { style: current.value }),
18816
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "flex-1 text-left", children: current.label }),
18817
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
18818
+ ]
18819
+ }
18820
+ );
18821
+ if (disabled) {
18822
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
18823
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
18824
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
18825
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "inline-flex", children: trigger }) }),
18826
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
18827
+ ] })
18828
+ ] });
18829
+ }
18695
18830
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
18696
18831
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
18697
18832
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DropdownMenu, { modal: false, children: [
18698
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18699
- "button",
18700
- {
18701
- type: "button",
18702
- "aria-label": "Card style",
18703
- className: "inline-flex h-8 min-w-[110px] items-center gap-[7px] rounded-[6px] bg-transparent px-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted data-[state=open]:bg-muted",
18704
- children: [
18705
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StyleSwatch, { style: current.value }),
18706
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "flex-1 text-left", children: current.label }),
18707
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
18708
- ]
18709
- }
18710
- ) }),
18833
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
18711
18834
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
18712
18835
  const selected = style.value === value;
18713
18836
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
@@ -18771,23 +18894,35 @@ function PricingCardToolbar({
18771
18894
  mobile ? "flex-wrap justify-center" : "flex-nowrap"
18772
18895
  ),
18773
18896
  children: [
18774
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle }),
18775
18897
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
18776
18898
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18777
18899
  CustomToolbarButton,
18778
18900
  {
18779
18901
  type: "button",
18780
- "aria-label": featuredTag ? "Remove featured tag" : "Add featured tag",
18902
+ "aria-label": featuredTag ? "Unmark as featured" : "Mark as featured",
18781
18903
  "aria-pressed": featuredTag,
18782
- active: featuredTag,
18783
18904
  onClick: () => onSetFeaturedTag(!featuredTag),
18784
- className: "size-8",
18785
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Star, { size: 16, fill: featuredTag ? "currentColor" : "none", style: { pointerEvents: "none" } })
18905
+ className: "group size-8",
18906
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18907
+ import_lucide_react9.Star,
18908
+ {
18909
+ size: 16,
18910
+ fill: "currentColor",
18911
+ strokeWidth: 0,
18912
+ className: cn(
18913
+ "transition-colors",
18914
+ featuredTag ? "text-[#f59e0b] group-hover:text-[#d97706]" : "text-[#d6d3d1] group-hover:text-[#fbbf24]"
18915
+ ),
18916
+ style: { pointerEvents: "none" }
18917
+ }
18918
+ )
18786
18919
  }
18787
18920
  ) }),
18788
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Remove featured tag" : "Add featured tag" })
18921
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
18789
18922
  ] }),
18790
18923
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18924
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
18925
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18791
18926
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ButtonGlyph, {}) }),
18792
18927
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "relative inline-flex", children: [
18793
18928
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ButtonGlyph, {}),
@@ -18930,7 +19065,12 @@ function AiPricingCardOverlay({
18930
19065
  index: active.index,
18931
19066
  pending,
18932
19067
  error,
18933
- onAddTier: () => runMutation(active.sectionId, addPricingTier),
19068
+ onAddTier: () => {
19069
+ const newIndex = pricingCardCount(activeEntry.tree);
19070
+ void runMutation(active.sectionId, addPricingTier).then((applied) => {
19071
+ if (applied) setActive({ sectionId: active.sectionId, index: newIndex });
19072
+ });
19073
+ },
18934
19074
  onRemoveTier: () => {
18935
19075
  void runMutation(active.sectionId, (tree) => removePricingTier(tree, active.index)).then(
18936
19076
  (applied) => {
@@ -30337,9 +30477,10 @@ function OhhwellsBridge() {
30337
30477
  if (e.data?.type !== "ow:collect-section") return;
30338
30478
  const sectionId = e.data.sectionId;
30339
30479
  if (typeof sectionId !== "string" || !sectionId) return;
30340
- const sectionEl = document.querySelector(
30480
+ const foundEl = document.querySelector(
30341
30481
  `[data-ohw-section="${CSS.escape(sectionId)}"]`
30342
30482
  );
30483
+ const sectionEl = foundEl && !foundEl.hasAttribute("data-ohw-ai-template-hidden") && !foundEl.hasAttribute("data-ohw-ai-removed") && !foundEl.hasAttribute("data-ohw-ai-replaced-by") ? foundEl : null;
30343
30484
  const nodes = sectionEl ? collectEditableNodes(void 0, sectionEl).filter(
30344
30485
  (n) => n.key && !n.key.startsWith("__ohw_") && !["meta", "sections", "video-setting"].includes(n.type)
30345
30486
  ).map((n) => ({ ...n, text: n.text ?? "", type: normalizeAiNodeType(n.type) })) : [];
@@ -31078,7 +31219,7 @@ function OhhwellsBridge() {
31078
31219
  postToParent2({
31079
31220
  type: "ow:ready",
31080
31221
  version: "1",
31081
- bridgeVersion: "0.1.139",
31222
+ bridgeVersion: "0.1.141",
31082
31223
  path: pathname,
31083
31224
  nodes: collectEditableNodes(editContentRef.current),
31084
31225
  sections
@@ -32294,6 +32435,7 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
32294
32435
  dropIndicatorVariants,
32295
32436
  ensureFooterTree,
32296
32437
  filterAvailablePages,
32438
+ foldAlignIntoTrees,
32297
32439
  getEditModeInitialState,
32298
32440
  insertFooterHours,
32299
32441
  isEditSessionActive,
@@ -32302,6 +32444,7 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
32302
32444
  isValidUrl,
32303
32445
  nextViableSlide,
32304
32446
  parseAiFooterState,
32447
+ parseAiSectionsState,
32305
32448
  parseTarget,
32306
32449
  removeFooterBlock,
32307
32450
  resolveFooterCopyright,