@ohhwells/bridge 0.1.86-next.255 → 0.1.86-next.257

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
@@ -837,6 +837,22 @@ function getPageSectionOrderEntries(raw, currentPath) {
837
837
  return [];
838
838
  }
839
839
  }
840
+ function mergePageSectionOrder(raw, currentPath, pageEntries) {
841
+ let all = [];
842
+ if (raw) {
843
+ try {
844
+ const parsed = JSON.parse(raw);
845
+ if (Array.isArray(parsed)) all = parsed;
846
+ } catch {
847
+ }
848
+ }
849
+ const otherPages = all.filter((e) => e && e.pagePath && e.pagePath !== currentPath);
850
+ const pageIds = new Set(pageEntries.map((e) => e.instanceId));
851
+ const removedHere = all.filter(
852
+ (e) => e && (!e.pagePath || e.pagePath === currentPath) && e.removed && !pageIds.has(e.instanceId)
853
+ );
854
+ return [...otherPages, ...removedHere, ...pageEntries];
855
+ }
840
856
  function rekeySectionSubtree(root, instanceId) {
841
857
  const suffix = `::${instanceId}`;
842
858
  const pairs = [];
@@ -1195,6 +1211,25 @@ var FEATURE_LINE_CSS = [
1195
1211
  `background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
1196
1212
  `mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
1197
1213
  ].join("");
1214
+ function buttonShellStyle(ctx, fullWidth) {
1215
+ const bs = ctx.buttonStyle;
1216
+ if (bs) {
1217
+ return {
1218
+ borderRadius: bs.radius,
1219
+ ...bs.padding ? { padding: bs.padding } : {},
1220
+ ...bs.fontFamily ? { fontFamily: bs.fontFamily } : { fontFamily: ctx.brand.fonts.body },
1221
+ ...bs.fontSize ? { fontSize: bs.fontSize } : {},
1222
+ ...bs.fontWeight ? { fontWeight: bs.fontWeight } : {},
1223
+ ...bs.letterSpacing && bs.letterSpacing !== "normal" ? { letterSpacing: bs.letterSpacing } : {},
1224
+ ...bs.textTransform && bs.textTransform !== "none" ? { textTransform: bs.textTransform } : {}
1225
+ };
1226
+ }
1227
+ return {
1228
+ borderRadius: AI_TREE_TOKENS.radiusButton,
1229
+ padding: fullWidth ? `${AI_TREE_TOKENS.spacing2}px ${AI_TREE_TOKENS.spacing4}px` : `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
1230
+ ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
1231
+ };
1232
+ }
1198
1233
  function hexLuminance(color) {
1199
1234
  const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
1200
1235
  if (!m) return null;
@@ -1211,6 +1246,12 @@ function hexContrast(a, b) {
1211
1246
  const [hi, lo] = la > lb ? [la, lb] : [lb, la];
1212
1247
  return (hi + 0.05) / (lo + 0.05);
1213
1248
  }
1249
+ function primaryButtonLabel(brand) {
1250
+ const darkC = hexContrast(brand.palette.primary, brand.palette.dark);
1251
+ const lightC = hexContrast(brand.palette.primary, AI_TREE_TOKENS.textPrimaryForeground);
1252
+ if (darkC === null || lightC === null) return AI_TREE_TOKENS.textPrimaryForeground;
1253
+ return darkC > lightC ? brand.palette.dark : AI_TREE_TOKENS.textPrimaryForeground;
1254
+ }
1214
1255
  function accentBandContext(brand) {
1215
1256
  const p = brand.palette;
1216
1257
  const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
@@ -1330,12 +1371,10 @@ function ButtonEl({
1330
1371
  width: fullWidth ? "100%" : void 0,
1331
1372
  alignItems: "center",
1332
1373
  justifyContent: "center",
1333
- padding: fullWidth ? `${AI_TREE_TOKENS.spacing2}px ${AI_TREE_TOKENS.spacing4}px` : `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
1334
- borderRadius: AI_TREE_TOKENS.radiusButton,
1335
1374
  textDecoration: "none",
1336
1375
  cursor: "pointer",
1337
- ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body),
1338
- ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? AI_TREE_TOKENS.textPrimaryForeground }
1376
+ ...buttonShellStyle(ctx, fullWidth),
1377
+ ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand) }
1339
1378
  },
1340
1379
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
1341
1380
  }
@@ -2405,15 +2444,12 @@ function renderNode(node, ctx, path) {
2405
2444
  alignSelf: submitAlign,
2406
2445
  border: "none",
2407
2446
  cursor: "pointer",
2408
- padding: "12px 24px",
2409
- // Corner radius follows the host template's own buttons (measured from a template
2410
- // CTA); 8px only when the page has no template button to match.
2411
- borderRadius: ctx.buttonRadius ?? 8,
2447
+ // Shape/padding/typography follow the host template's own buttons.
2448
+ ...buttonShellStyle(ctx),
2412
2449
  // Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
2413
2450
  // reads correctly on custom palettes.
2414
2451
  background: ctx.brand.palette.primary,
2415
- color: ctx.buttonLabel ?? ctx.brand.palette.light,
2416
- ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
2452
+ color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand)
2417
2453
  },
2418
2454
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
2419
2455
  },
@@ -2448,7 +2484,7 @@ function renderNode(node, ctx, path) {
2448
2484
  function AiTreeRenderer({
2449
2485
  tree,
2450
2486
  brand,
2451
- buttonRadius,
2487
+ buttonStyle,
2452
2488
  resolveMedia,
2453
2489
  editKeyPrefix
2454
2490
  }) {
@@ -2469,7 +2505,7 @@ function AiTreeRenderer({
2469
2505
  cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
2470
2506
  keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
2471
2507
  sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
2472
- buttonRadius,
2508
+ buttonStyle,
2473
2509
  ...band ? { buttonLabel: band.buttonLabel } : {}
2474
2510
  };
2475
2511
  const settings = tree.settings ?? {};
@@ -2601,13 +2637,13 @@ function deriveBrandOverride() {
2601
2637
  };
2602
2638
  }
2603
2639
  function deriveTemplateBrand() {
2604
- const dark = readRootVar("--color-dark");
2605
- const primary = readRootVar("--color-primary");
2606
- const light = readRootVar("--color-light");
2640
+ const primary = readRootVar("--brand-primary") || readRootVar("--color-primary");
2641
+ const dark = readRootVar("--brand-text") || readRootVar("--color-dark");
2642
+ const light = readRootVar("--brand-background") || readRootVar("--color-light");
2607
2643
  if (!dark || !primary || !light) return null;
2608
- const accent = readRootVar("--color-accent");
2609
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
2610
- const body = readRootVar("--font-body");
2644
+ const accent = readRootVar("--brand-accent") || readRootVar("--color-accent");
2645
+ const heading = readRootVar("--brand-font-heading") || readRootVar("--font-heading") || readRootVar("--font-display");
2646
+ const body = readRootVar("--brand-font-body") || readRootVar("--font-body");
2611
2647
  return {
2612
2648
  palette: { dark, primary, accent: accent || dark, light },
2613
2649
  fonts: {
@@ -2616,12 +2652,32 @@ function deriveTemplateBrand() {
2616
2652
  }
2617
2653
  };
2618
2654
  }
2619
- function deriveTemplateButtonRadius() {
2655
+ function deriveTemplateButtonStyle() {
2620
2656
  if (typeof document === "undefined") return null;
2621
- const btn = document.querySelector('[data-ohw-role="button"]');
2657
+ const btn = Array.from(document.querySelectorAll('[data-ohw-role="button"]')).find(
2658
+ (el) => !el.closest(`[${CONTAINER_ATTR}]`)
2659
+ );
2622
2660
  if (!btn) return null;
2623
- const radius = getComputedStyle(btn).borderTopLeftRadius;
2624
- return radius || null;
2661
+ const cs = getComputedStyle(btn);
2662
+ const corners = [
2663
+ cs.borderTopLeftRadius,
2664
+ cs.borderTopRightRadius,
2665
+ cs.borderBottomRightRadius,
2666
+ cs.borderBottomLeftRadius
2667
+ ].map((v) => v || "0px");
2668
+ const radius = corners.every((v) => v === corners[0]) ? corners[0] : corners.join(" ");
2669
+ const px = (v) => parseFloat(v) || 0;
2670
+ const padY = Math.max(px(cs.paddingTop), px(cs.paddingBottom));
2671
+ const padX = Math.max(px(cs.paddingLeft), px(cs.paddingRight));
2672
+ return {
2673
+ radius: radius || "10px",
2674
+ padding: `${padY}px ${padX}px`,
2675
+ fontFamily: cs.fontFamily || "",
2676
+ fontSize: cs.fontSize || "",
2677
+ fontWeight: cs.fontWeight || "",
2678
+ letterSpacing: cs.letterSpacing || "",
2679
+ textTransform: cs.textTransform || ""
2680
+ };
2625
2681
  }
2626
2682
  var mounted = /* @__PURE__ */ new Map();
2627
2683
  function findTemplateSection(id) {
@@ -2791,7 +2847,7 @@ function applyAiSectionsToDom(state, options) {
2791
2847
  if (typeof document === "undefined") return;
2792
2848
  const brandOverride = deriveBrandOverride();
2793
2849
  const templateBrand = deriveTemplateBrand();
2794
- const templateButtonRadius = deriveTemplateButtonRadius();
2850
+ const templateButtonStyle = deriveTemplateButtonStyle();
2795
2851
  const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
2796
2852
  const pagePath = window.location.pathname;
2797
2853
  const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
@@ -2831,7 +2887,7 @@ function applyAiSectionsToDom(state, options) {
2831
2887
  {
2832
2888
  tree: entry.tree,
2833
2889
  brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
2834
- buttonRadius: templateButtonRadius,
2890
+ buttonStyle: templateButtonStyle,
2835
2891
  resolveMedia,
2836
2892
  editKeyPrefix: `ai.${entry.id}`
2837
2893
  }
@@ -14597,7 +14653,9 @@ function useSectionDrag({
14597
14653
  clearSectionDragVisuals();
14598
14654
  return;
14599
14655
  }
14600
- const orderJson = JSON.stringify(entries);
14656
+ const orderJson = JSON.stringify(
14657
+ mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
14658
+ );
14601
14659
  editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
14602
14660
  setAiSectionOrder(orderJson, window.location.pathname);
14603
14661
  postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
@@ -20899,7 +20957,9 @@ function OhhwellsBridge() {
20899
20957
  if (!instanceId || !direction) return;
20900
20958
  const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
20901
20959
  if (!entries) return;
20902
- const orderJson = JSON.stringify(entries);
20960
+ const orderJson = JSON.stringify(
20961
+ mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
20962
+ );
20903
20963
  editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
20904
20964
  setAiSectionOrder(orderJson, window.location.pathname);
20905
20965
  postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
@@ -20919,7 +20979,9 @@ function OhhwellsBridge() {
20919
20979
  const currentEntries = getPageSectionOrderEntries(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname);
20920
20980
  const entries = deleteSectionInstance(instanceId, window.location.pathname, currentEntries);
20921
20981
  if (!entries) return;
20922
- const orderJson = JSON.stringify(entries);
20982
+ const orderJson = JSON.stringify(
20983
+ mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
20984
+ );
20923
20985
  editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
20924
20986
  setAiSectionOrder(orderJson, window.location.pathname);
20925
20987
  postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
@@ -20938,7 +21000,9 @@ function OhhwellsBridge() {
20938
21000
  );
20939
21001
  const restored = restoreSectionInstance(instanceId, window.location.pathname, restoredEntries);
20940
21002
  if (!restored) return;
20941
- const restoredJson = JSON.stringify(restored);
21003
+ const restoredJson = JSON.stringify(
21004
+ mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, restored)
21005
+ );
20942
21006
  editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: restoredJson };
20943
21007
  setAiSectionOrder(restoredJson, window.location.pathname);
20944
21008
  postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: restoredJson }] });
@@ -20966,7 +21030,9 @@ function OhhwellsBridge() {
20966
21030
  const result = duplicateSectionInstance(instanceId, newId, window.location.pathname, currentEntries);
20967
21031
  if (!result) return;
20968
21032
  const { entries, keyRekeys } = result;
20969
- const orderJson = JSON.stringify(entries);
21033
+ const orderJson = JSON.stringify(
21034
+ mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
21035
+ );
20970
21036
  const nodes = [{ key: SECTION_ORDER_KEY, text: orderJson }];
20971
21037
  for (const { from, to } of keyRekeys) {
20972
21038
  const inherited = editContentRef.current[from];