@ohhwells/bridge 0.1.142 → 0.1.143

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
@@ -5033,10 +5033,9 @@ function getLinkHref3(el) {
5033
5033
  }
5034
5034
  function collectEditableNodes(extraContent, root = document) {
5035
5035
  const isScoped = root !== document;
5036
- const editableEls = Array.from(
5037
- root.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="form"])')
5038
- );
5039
- if (isScoped && root instanceof HTMLElement && root.matches('[data-ohw-editable]:not([data-ohw-editable="form"])')) {
5036
+ const EDITABLE_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="form"]):not([data-ai-icon])';
5037
+ const editableEls = Array.from(root.querySelectorAll(EDITABLE_SELECTOR));
5038
+ if (isScoped && root instanceof HTMLElement && root.matches(EDITABLE_SELECTOR)) {
5040
5039
  editableEls.unshift(root);
5041
5040
  }
5042
5041
  const nodes = editableEls.map((el) => {
@@ -6303,6 +6302,281 @@ function reapRemovedAiSections(state, store, removedIds, excludeIds = /* @__PURE
6303
6302
  return { state: nextState, store: nextStore, reapedIds, slotPrefixes, changed: true };
6304
6303
  }
6305
6304
 
6305
+ // src/lib/ai-pricing-tree.ts
6306
+ var PRICING_MIN_CARDS = 1;
6307
+ var PRICING_MAX_CARDS = 4;
6308
+ function isPricingCard(node) {
6309
+ return typeof node === "object" && node !== null && node.type === "card" && node.slots?.preset === "pricing";
6310
+ }
6311
+ function isPricingCollection(node) {
6312
+ return node.type === "collection" && Array.isArray(node.children) && node.children.length > 0 && isPricingCard(node.children[0]);
6313
+ }
6314
+ function findCollection(node) {
6315
+ if (isPricingCollection(node)) return node;
6316
+ for (const child of node.children ?? []) {
6317
+ const found = findCollection(child);
6318
+ if (found) return found;
6319
+ }
6320
+ return null;
6321
+ }
6322
+ function findPricingCollection(tree) {
6323
+ for (const row of tree.rows ?? []) {
6324
+ for (const block of row.blocks ?? []) {
6325
+ const found = findCollection(block);
6326
+ if (found) return found;
6327
+ }
6328
+ }
6329
+ return null;
6330
+ }
6331
+ function pricingCardCount(tree) {
6332
+ return findPricingCollection(tree)?.children?.length ?? 0;
6333
+ }
6334
+ function withPricingCollection(tree, mutate) {
6335
+ const next = JSON.parse(JSON.stringify(tree));
6336
+ const collection = findPricingCollection(next);
6337
+ if (!collection || !collection.children) return null;
6338
+ for (const card of collection.children) {
6339
+ if (isPricingCard(card)) upgradeLegacyPricingSlots(card);
6340
+ }
6341
+ return mutate(collection) ? next : null;
6342
+ }
6343
+ function upgradeLegacyPricingSlots(card) {
6344
+ const slots = card.slots = card.slots ?? {};
6345
+ if (typeof slots.features === "string") {
6346
+ const rows = pricingFeatureList(card);
6347
+ if (rows.length > 0) slots.features = rows.slice(0, PRICING_FEATURES_MAX);
6348
+ }
6349
+ if (slots.featured === true) slots.featured = { enabled: true };
6350
+ else if (slots.featured === false) delete slots.featured;
6351
+ }
6352
+ function addPricingTier(tree) {
6353
+ return withPricingCollection(tree, (collection) => {
6354
+ const children = collection.children;
6355
+ if (children.length >= PRICING_MAX_CARDS) return false;
6356
+ const clone = JSON.parse(JSON.stringify(children[children.length - 1]));
6357
+ clone.slots = clone.slots ?? {};
6358
+ delete clone.slots.featured;
6359
+ clone.slots.heading = "New tier";
6360
+ children.push(clone);
6361
+ collection.slots = { ...collection.slots, itemsPerRow: children.length };
6362
+ return true;
6363
+ });
6364
+ }
6365
+ function removePricingTier(tree, index) {
6366
+ return withPricingCollection(tree, (collection) => {
6367
+ const children = collection.children;
6368
+ if (children.length <= PRICING_MIN_CARDS) return false;
6369
+ if (index < 0 || index >= children.length) return false;
6370
+ children.splice(index, 1);
6371
+ collection.slots = { ...collection.slots, itemsPerRow: children.length };
6372
+ return true;
6373
+ });
6374
+ }
6375
+ var PRICING_CARD_STYLES = [
6376
+ { value: "outline", label: "Outline", treatment: null },
6377
+ { value: "lift", label: "Lift", treatment: "lifted" },
6378
+ { value: "invert", label: "Invert", treatment: "inverted" },
6379
+ { value: "header", label: "Header", treatment: "accent-header" }
6380
+ ];
6381
+ var DEFAULT_FEATURED_BADGE = "Most popular";
6382
+ function featuredOf(target) {
6383
+ const raw = target?.slots?.featured;
6384
+ if (raw === true) return { enabled: true, treatment: "outlined" };
6385
+ return typeof raw === "object" && raw !== null ? raw : {};
6386
+ }
6387
+ function badgeOf(featured) {
6388
+ return typeof featured.badge === "string" ? featured.badge.trim() : "";
6389
+ }
6390
+ function pricingCardStyle(tree, index) {
6391
+ const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
6392
+ if (featured.enabled !== true) return "outline";
6393
+ return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
6394
+ }
6395
+ function pricingCardFeaturedTag(tree, index) {
6396
+ const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
6397
+ return featured.enabled === true && badgeOf(featured).length > 0;
6398
+ }
6399
+ function composeFeatured(style, tagOn, badgeText) {
6400
+ const treatment = PRICING_CARD_STYLES.find((s) => s.value === style)?.treatment ?? null;
6401
+ if (tagOn) return { enabled: true, badge: badgeText || DEFAULT_FEATURED_BADGE, treatment: treatment ?? "outlined" };
6402
+ if (treatment) return { enabled: true, treatment };
6403
+ return badgeText.length > 0 ? { enabled: false, badge: badgeText } : { enabled: false };
6404
+ }
6405
+ function withPricingCard(tree, index, mutate) {
6406
+ return withPricingCollection(tree, (collection) => {
6407
+ const children = collection.children;
6408
+ if (index < 0 || index >= children.length) return false;
6409
+ const target = children[index];
6410
+ target.slots = target.slots ?? {};
6411
+ mutate(target);
6412
+ return true;
6413
+ });
6414
+ }
6415
+ function setPricingCardStyle(tree, index, style) {
6416
+ if (!PRICING_CARD_STYLES.some((s) => s.value === style)) return null;
6417
+ return withPricingCard(tree, index, (target) => {
6418
+ const prev = featuredOf(target);
6419
+ const tagOn = prev.enabled === true && badgeOf(prev).length > 0;
6420
+ target.slots.featured = composeFeatured(style, tagOn, badgeOf(prev));
6421
+ });
6422
+ }
6423
+ function setPricingCardFeaturedTag(tree, index, on) {
6424
+ return withPricingCollection(tree, (collection) => {
6425
+ const children = collection.children;
6426
+ if (index < 0 || index >= children.length) return false;
6427
+ if (on) {
6428
+ children.forEach((card, i) => {
6429
+ if (i === index) return;
6430
+ const prev2 = featuredOf(card);
6431
+ if (prev2.enabled !== true || badgeOf(prev2).length === 0) return;
6432
+ card.slots = card.slots ?? {};
6433
+ card.slots.featured = composeFeatured(pricingCardStyleOf(prev2), false, badgeOf(prev2));
6434
+ });
6435
+ }
6436
+ const target = children[index];
6437
+ target.slots = target.slots ?? {};
6438
+ const prev = featuredOf(target);
6439
+ target.slots.featured = composeFeatured(pricingCardStyleOf(prev), on, badgeOf(prev));
6440
+ return true;
6441
+ });
6442
+ }
6443
+ function pricingCardStyleOf(featured) {
6444
+ if (featured.enabled !== true) return "outline";
6445
+ return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
6446
+ }
6447
+ function addPricingCardButton(tree, index, label) {
6448
+ return withPricingCollection(tree, (collection) => {
6449
+ const children = collection.children;
6450
+ if (index < 0 || index >= children.length) return false;
6451
+ const target = children[index];
6452
+ const buttons = (target.children ?? []).filter((c) => c.type === "button");
6453
+ if (buttons.length >= 2) return false;
6454
+ const hasPrimary = buttons.some((b) => b.slots?.variant === "primary");
6455
+ target.children = [
6456
+ ...target.children ?? [],
6457
+ { type: "button", span: 12, slots: { label, href: "#", variant: hasPrimary ? "secondary" : "primary" } }
6458
+ ];
6459
+ return true;
6460
+ });
6461
+ }
6462
+ function removePricingCardButton(tree, index) {
6463
+ return withPricingCollection(tree, (collection) => {
6464
+ const children = collection.children;
6465
+ if (index < 0 || index >= children.length) return false;
6466
+ const target = children[index];
6467
+ const buttons = (target.children ?? []).filter((c) => c.type === "button");
6468
+ if (buttons.length === 0) return false;
6469
+ const lastButton = buttons[buttons.length - 1];
6470
+ target.children = (target.children ?? []).filter((c) => c !== lastButton);
6471
+ return true;
6472
+ });
6473
+ }
6474
+ var PRICING_FEATURES_MIN = 1;
6475
+ var PRICING_FEATURES_MAX = 10;
6476
+ var PRICING_FEATURE_ICONS = [
6477
+ "check",
6478
+ "check-circle",
6479
+ "x",
6480
+ "star",
6481
+ "zap",
6482
+ "shield",
6483
+ "clock",
6484
+ "users",
6485
+ "infinity"
6486
+ ];
6487
+ function pricingFeatureList(target) {
6488
+ const raw = target.slots?.features;
6489
+ if (Array.isArray(raw)) {
6490
+ return raw.filter((item) => !!item && typeof item === "object" && typeof item.text === "string").map((item) => ({
6491
+ text: item.text,
6492
+ ...typeof item.icon === "string" ? { icon: item.icon } : {},
6493
+ ...typeof item.iconColor === "string" ? { iconColor: item.iconColor } : {}
6494
+ }));
6495
+ }
6496
+ if (typeof raw === "string") {
6497
+ return raw.split(/\n|<br\s*\/?>/i).map((line) => line.replace(/<[^>]+>/g, "").replace(/^[-*•]\s*/, "").trim().slice(0, 120)).filter(Boolean).map((text) => ({ text }));
6498
+ }
6499
+ return [];
6500
+ }
6501
+ function pricingCardFeatureCount(tree, index) {
6502
+ const target = findPricingCollection(tree)?.children?.[index];
6503
+ return target ? pricingFeatureList(target).length : 0;
6504
+ }
6505
+ function addPricingCardFeature(tree, index, text) {
6506
+ return withPricingCollection(tree, (collection) => {
6507
+ const children = collection.children;
6508
+ if (index < 0 || index >= children.length) return false;
6509
+ const target = children[index];
6510
+ const features = pricingFeatureList(target);
6511
+ if (features.length >= PRICING_FEATURES_MAX) return false;
6512
+ target.slots = target.slots ?? {};
6513
+ target.slots.features = [...features, { text }];
6514
+ return true;
6515
+ });
6516
+ }
6517
+ function setPricingCardFeatureIcon(tree, index, featureIndex, icon) {
6518
+ if (!PRICING_FEATURE_ICONS.includes(icon)) return null;
6519
+ return withPricingCollection(tree, (collection) => {
6520
+ const children = collection.children;
6521
+ if (index < 0 || index >= children.length) return false;
6522
+ const target = children[index];
6523
+ const features = pricingFeatureList(target);
6524
+ if (featureIndex < 0 || featureIndex >= features.length) return false;
6525
+ target.slots = target.slots ?? {};
6526
+ target.slots.features = features.map((row, i) => i === featureIndex ? { ...row, icon } : row);
6527
+ return true;
6528
+ });
6529
+ }
6530
+ function removePricingCardFeature(tree, index) {
6531
+ return withPricingCollection(tree, (collection) => {
6532
+ const children = collection.children;
6533
+ if (index < 0 || index >= children.length) return false;
6534
+ const target = children[index];
6535
+ const features = pricingFeatureList(target);
6536
+ if (features.length <= PRICING_FEATURES_MIN) return false;
6537
+ target.slots = target.slots ?? {};
6538
+ target.slots.features = features.slice(0, -1);
6539
+ return true;
6540
+ });
6541
+ }
6542
+
6543
+ // src/lib/ai-icon-edit.ts
6544
+ var FEATURE_ICON_PATH = /\.c(\d+)\.features\.(\d+)\.icon$/;
6545
+ var NODE_SEGMENT = /^[rbc]\d+$/;
6546
+ function nodeAtPath(tree, segments) {
6547
+ if (segments.length < 2 || segments.some((s) => !NODE_SEGMENT.test(s))) return null;
6548
+ if (!segments[0].startsWith("r") || !segments[1].startsWith("b")) return null;
6549
+ let node = tree.rows?.[Number(segments[0].slice(1))]?.blocks?.[Number(segments[1].slice(1))];
6550
+ for (const segment of segments.slice(2)) {
6551
+ if (!segment.startsWith("c")) return null;
6552
+ node = node?.children?.[Number(segment.slice(1))];
6553
+ }
6554
+ return node ?? null;
6555
+ }
6556
+ function applyIconNameToTree(tree, path, icon) {
6557
+ const feature = path.match(FEATURE_ICON_PATH);
6558
+ if (feature) {
6559
+ return setPricingCardFeatureIcon(tree, Number(feature[1]), Number(feature[2]), icon);
6560
+ }
6561
+ return rewriteIconSlot(tree, path, `lucide:${icon}`);
6562
+ }
6563
+ function applyIconUploadToTree(tree, path, mediaRef) {
6564
+ if (FEATURE_ICON_PATH.test(path)) return null;
6565
+ return rewriteIconSlot(tree, path, `icon:${mediaRef}`);
6566
+ }
6567
+ function rewriteIconSlot(tree, path, ref) {
6568
+ const segments = path.split(".");
6569
+ const slot = segments.pop();
6570
+ if (!slot || NODE_SEGMENT.test(slot)) return null;
6571
+ const next = JSON.parse(JSON.stringify(tree));
6572
+ const node = nodeAtPath(next, segments);
6573
+ if (!node) return null;
6574
+ const current = node.slots?.[slot];
6575
+ if (typeof current !== "string" || !/^(lucide|simple|icon):/.test(current)) return null;
6576
+ node.slots = { ...node.slots, [slot]: ref };
6577
+ return next;
6578
+ }
6579
+
6306
6580
  // src/lib/ai-footer-tree.ts
6307
6581
  var FOOTER_COLUMN_SPANS = [12, 6, 4, 3];
6308
6582
  var FOOTER_COLUMN_BLOCKS = [
@@ -7659,7 +7933,7 @@ function MediaBox({
7659
7933
  onError
7660
7934
  }) {
7661
7935
  const url = refValue ? ctx.resolveMedia(refValue) : null;
7662
- const isIcon = /^(lucide|simple):/.test(refValue);
7936
+ const isIcon = /^(lucide|simple|icon):/.test(refValue);
7663
7937
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
7664
7938
  const editAttrs = ctx.keyFor && editPath && !isIcon ? {
7665
7939
  "data-ohw-key": ctx.keyFor(editPath),
@@ -7667,10 +7941,13 @@ function MediaBox({
7667
7941
  } : {};
7668
7942
  if (isIcon) {
7669
7943
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
7944
+ const uploadedIconUrl = refValue.startsWith("icon:") ? ctx.resolveMedia(refValue.slice(5)) : null;
7945
+ const iconEditAttrs = ctx.keyFor && editPath && genericImageEdit ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "icon" } : {};
7670
7946
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
7671
7947
  "span",
7672
7948
  {
7673
7949
  "data-ai-icon": refValue,
7950
+ ...iconEditAttrs,
7674
7951
  style: {
7675
7952
  display: "inline-flex",
7676
7953
  width: 48,
@@ -7679,7 +7956,7 @@ function MediaBox({
7679
7956
  justifyContent: "center",
7680
7957
  color: ctx.brand.palette.dark
7681
7958
  },
7682
- children: Icon ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { size: 48, color: ctx.brand.palette.dark, strokeWidth: 1.5 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 24 }, children: "\u25C6" })
7959
+ children: uploadedIconUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: uploadedIconUrl, alt: "", style: { width: 48, height: 48, objectFit: "contain" } }) : Icon ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { size: 48, color: ctx.brand.palette.dark, strokeWidth: 1.5 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 24 }, children: "\u25C6" })
7683
7960
  }
7684
7961
  );
7685
7962
  }
@@ -8167,8 +8444,20 @@ function PricingCard({ node, ctx, path }) {
8167
8444
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-ai-features": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing4, padding: `${AI_TREE_TOKENS.spacing2}px 0` }, children: features.map((item, i) => {
8168
8445
  const Icon = lucideByName(item.icon || "check");
8169
8446
  const iconColor = pricingIconColor(item.iconColor, ctx.brand, fg);
8447
+ const iconAttrs = ctx.keyFor && !item.html ? {
8448
+ "data-ohw-key": ctx.keyFor(`${path}.features.${i}.icon`),
8449
+ "data-ohw-editable": "icon"
8450
+ } : {};
8170
8451
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", gap: AI_TREE_TOKENS.spacing3 }, children: [
8171
- Icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { size: 20, color: iconColor }),
8452
+ Icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
8453
+ "span",
8454
+ {
8455
+ "data-ai-icon": `lucide:${item.icon || "check"}`,
8456
+ ...iconAttrs,
8457
+ style: { display: "inline-flex", flexShrink: 0 },
8458
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { size: 20, color: iconColor })
8459
+ }
8460
+ ),
8172
8461
  item.html ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
8173
8462
  "span",
8174
8463
  {
@@ -18411,222 +18700,6 @@ function AiMediaStripOverlay({
18411
18700
  // src/ui/ai-section/AiPricingCardOverlay.tsx
18412
18701
  var import_react11 = require("react");
18413
18702
  var import_lucide_react9 = require("lucide-react");
18414
-
18415
- // src/lib/ai-pricing-tree.ts
18416
- var PRICING_MIN_CARDS = 1;
18417
- var PRICING_MAX_CARDS = 4;
18418
- function isPricingCard(node) {
18419
- return typeof node === "object" && node !== null && node.type === "card" && node.slots?.preset === "pricing";
18420
- }
18421
- function isPricingCollection(node) {
18422
- return node.type === "collection" && Array.isArray(node.children) && node.children.length > 0 && isPricingCard(node.children[0]);
18423
- }
18424
- function findCollection(node) {
18425
- if (isPricingCollection(node)) return node;
18426
- for (const child of node.children ?? []) {
18427
- const found = findCollection(child);
18428
- if (found) return found;
18429
- }
18430
- return null;
18431
- }
18432
- function findPricingCollection(tree) {
18433
- for (const row of tree.rows ?? []) {
18434
- for (const block of row.blocks ?? []) {
18435
- const found = findCollection(block);
18436
- if (found) return found;
18437
- }
18438
- }
18439
- return null;
18440
- }
18441
- function pricingCardCount(tree) {
18442
- return findPricingCollection(tree)?.children?.length ?? 0;
18443
- }
18444
- function withPricingCollection(tree, mutate) {
18445
- const next = JSON.parse(JSON.stringify(tree));
18446
- const collection = findPricingCollection(next);
18447
- if (!collection || !collection.children) return null;
18448
- for (const card of collection.children) {
18449
- if (isPricingCard(card)) upgradeLegacyPricingSlots(card);
18450
- }
18451
- return mutate(collection) ? next : null;
18452
- }
18453
- function upgradeLegacyPricingSlots(card) {
18454
- const slots = card.slots = card.slots ?? {};
18455
- if (typeof slots.features === "string") {
18456
- const rows = pricingFeatureList(card);
18457
- if (rows.length > 0) slots.features = rows.slice(0, PRICING_FEATURES_MAX);
18458
- }
18459
- if (slots.featured === true) slots.featured = { enabled: true };
18460
- else if (slots.featured === false) delete slots.featured;
18461
- }
18462
- function addPricingTier(tree) {
18463
- return withPricingCollection(tree, (collection) => {
18464
- const children = collection.children;
18465
- if (children.length >= PRICING_MAX_CARDS) return false;
18466
- const clone = JSON.parse(JSON.stringify(children[children.length - 1]));
18467
- clone.slots = clone.slots ?? {};
18468
- delete clone.slots.featured;
18469
- clone.slots.heading = "New tier";
18470
- children.push(clone);
18471
- collection.slots = { ...collection.slots, itemsPerRow: children.length };
18472
- return true;
18473
- });
18474
- }
18475
- function removePricingTier(tree, index) {
18476
- return withPricingCollection(tree, (collection) => {
18477
- const children = collection.children;
18478
- if (children.length <= PRICING_MIN_CARDS) return false;
18479
- if (index < 0 || index >= children.length) return false;
18480
- children.splice(index, 1);
18481
- collection.slots = { ...collection.slots, itemsPerRow: children.length };
18482
- return true;
18483
- });
18484
- }
18485
- var PRICING_CARD_STYLES = [
18486
- { value: "outline", label: "Outline", treatment: null },
18487
- { value: "lift", label: "Lift", treatment: "lifted" },
18488
- { value: "invert", label: "Invert", treatment: "inverted" },
18489
- { value: "header", label: "Header", treatment: "accent-header" }
18490
- ];
18491
- var DEFAULT_FEATURED_BADGE = "Most popular";
18492
- function featuredOf(target) {
18493
- const raw = target?.slots?.featured;
18494
- if (raw === true) return { enabled: true, treatment: "outlined" };
18495
- return typeof raw === "object" && raw !== null ? raw : {};
18496
- }
18497
- function badgeOf(featured) {
18498
- return typeof featured.badge === "string" ? featured.badge.trim() : "";
18499
- }
18500
- function pricingCardStyle(tree, index) {
18501
- const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
18502
- if (featured.enabled !== true) return "outline";
18503
- return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
18504
- }
18505
- function pricingCardFeaturedTag(tree, index) {
18506
- const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
18507
- return featured.enabled === true && badgeOf(featured).length > 0;
18508
- }
18509
- function composeFeatured(style, tagOn, badgeText) {
18510
- const treatment = PRICING_CARD_STYLES.find((s) => s.value === style)?.treatment ?? null;
18511
- if (tagOn) return { enabled: true, badge: badgeText || DEFAULT_FEATURED_BADGE, treatment: treatment ?? "outlined" };
18512
- if (treatment) return { enabled: true, treatment };
18513
- return badgeText.length > 0 ? { enabled: false, badge: badgeText } : { enabled: false };
18514
- }
18515
- function withPricingCard(tree, index, mutate) {
18516
- return withPricingCollection(tree, (collection) => {
18517
- const children = collection.children;
18518
- if (index < 0 || index >= children.length) return false;
18519
- const target = children[index];
18520
- target.slots = target.slots ?? {};
18521
- mutate(target);
18522
- return true;
18523
- });
18524
- }
18525
- function setPricingCardStyle(tree, index, style) {
18526
- if (!PRICING_CARD_STYLES.some((s) => s.value === style)) return null;
18527
- return withPricingCard(tree, index, (target) => {
18528
- const prev = featuredOf(target);
18529
- const tagOn = prev.enabled === true && badgeOf(prev).length > 0;
18530
- target.slots.featured = composeFeatured(style, tagOn, badgeOf(prev));
18531
- });
18532
- }
18533
- function setPricingCardFeaturedTag(tree, index, on) {
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 ?? {};
18548
- const prev = featuredOf(target);
18549
- target.slots.featured = composeFeatured(pricingCardStyleOf(prev), on, badgeOf(prev));
18550
- return true;
18551
- });
18552
- }
18553
- function pricingCardStyleOf(featured) {
18554
- if (featured.enabled !== true) return "outline";
18555
- return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
18556
- }
18557
- function addPricingCardButton(tree, index, label) {
18558
- return withPricingCollection(tree, (collection) => {
18559
- const children = collection.children;
18560
- if (index < 0 || index >= children.length) return false;
18561
- const target = children[index];
18562
- const buttons = (target.children ?? []).filter((c) => c.type === "button");
18563
- if (buttons.length >= 2) return false;
18564
- const hasPrimary = buttons.some((b) => b.slots?.variant === "primary");
18565
- target.children = [
18566
- ...target.children ?? [],
18567
- { type: "button", span: 12, slots: { label, href: "#", variant: hasPrimary ? "secondary" : "primary" } }
18568
- ];
18569
- return true;
18570
- });
18571
- }
18572
- function removePricingCardButton(tree, index) {
18573
- return withPricingCollection(tree, (collection) => {
18574
- const children = collection.children;
18575
- if (index < 0 || index >= children.length) return false;
18576
- const target = children[index];
18577
- const buttons = (target.children ?? []).filter((c) => c.type === "button");
18578
- if (buttons.length === 0) return false;
18579
- const lastButton = buttons[buttons.length - 1];
18580
- target.children = (target.children ?? []).filter((c) => c !== lastButton);
18581
- return true;
18582
- });
18583
- }
18584
- var PRICING_FEATURES_MIN = 1;
18585
- var PRICING_FEATURES_MAX = 10;
18586
- function pricingFeatureList(target) {
18587
- const raw = target.slots?.features;
18588
- if (Array.isArray(raw)) {
18589
- return raw.filter((item) => !!item && typeof item === "object" && typeof item.text === "string").map((item) => ({
18590
- text: item.text,
18591
- ...typeof item.icon === "string" ? { icon: item.icon } : {},
18592
- ...typeof item.iconColor === "string" ? { iconColor: item.iconColor } : {}
18593
- }));
18594
- }
18595
- if (typeof raw === "string") {
18596
- return raw.split(/\n|<br\s*\/?>/i).map((line) => line.replace(/<[^>]+>/g, "").replace(/^[-*•]\s*/, "").trim().slice(0, 120)).filter(Boolean).map((text) => ({ text }));
18597
- }
18598
- return [];
18599
- }
18600
- function pricingCardFeatureCount(tree, index) {
18601
- const target = findPricingCollection(tree)?.children?.[index];
18602
- return target ? pricingFeatureList(target).length : 0;
18603
- }
18604
- function addPricingCardFeature(tree, index, text) {
18605
- return withPricingCollection(tree, (collection) => {
18606
- const children = collection.children;
18607
- if (index < 0 || index >= children.length) return false;
18608
- const target = children[index];
18609
- const features = pricingFeatureList(target);
18610
- if (features.length >= PRICING_FEATURES_MAX) return false;
18611
- target.slots = target.slots ?? {};
18612
- target.slots.features = [...features, { text }];
18613
- return true;
18614
- });
18615
- }
18616
- function removePricingCardFeature(tree, index) {
18617
- return withPricingCollection(tree, (collection) => {
18618
- const children = collection.children;
18619
- if (index < 0 || index >= children.length) return false;
18620
- const target = children[index];
18621
- const features = pricingFeatureList(target);
18622
- if (features.length <= PRICING_FEATURES_MIN) return false;
18623
- target.slots = target.slots ?? {};
18624
- target.slots.features = features.slice(0, -1);
18625
- return true;
18626
- });
18627
- }
18628
-
18629
- // src/ui/ai-section/AiPricingCardOverlay.tsx
18630
18703
  var import_jsx_runtime20 = require("react/jsx-runtime");
18631
18704
  var PRIMARY4 = "var(--ohw-primary, #0885FE)";
18632
18705
  var CHROME_SELECTOR = "[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review], [data-ohw-more-menu]";
@@ -23752,6 +23825,23 @@ function currentIconRef(el) {
23752
23825
  );
23753
23826
  return named ? `lucide:${named.slice("lucide-".length)}` : "";
23754
23827
  }
23828
+ function requestValidateTree5(postToParent2, tree) {
23829
+ return new Promise((resolve) => {
23830
+ const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
23831
+ const timeout = window.setTimeout(() => {
23832
+ window.removeEventListener("message", onMessage);
23833
+ resolve({ ok: false, reasons: ["Validation request timed out."] });
23834
+ }, 8e3);
23835
+ function onMessage(e) {
23836
+ if (e.data?.type !== "ow:ai-validate-tree-result" || e.data.id !== id) return;
23837
+ window.clearTimeout(timeout);
23838
+ window.removeEventListener("message", onMessage);
23839
+ resolve({ ok: e.data.ok === true, reasons: Array.isArray(e.data.reasons) ? e.data.reasons : [] });
23840
+ }
23841
+ window.addEventListener("message", onMessage);
23842
+ postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
23843
+ });
23844
+ }
23755
23845
  function getEditMeasureEl(editable) {
23756
23846
  return editable.closest("[data-ohw-href-key]") ?? editable;
23757
23847
  }
@@ -28103,10 +28193,14 @@ function OhhwellsBridge() {
28103
28193
  e.preventDefault();
28104
28194
  e.stopPropagation();
28105
28195
  aiSectionApiRef.current?.selectFromElement(editable);
28196
+ const aiIcon = editable.hasAttribute("data-ai-icon");
28197
+ const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
28106
28198
  postToParentRef.current({
28107
28199
  type: "ow:icon-pick",
28108
28200
  key: editable.dataset.ohwKey ?? "",
28109
- current: currentIconRef(editable)
28201
+ current: currentIconRef(editable),
28202
+ ...aiIcon ? { iconMode: "name" } : {},
28203
+ ...featureIcon ? { options: [...PRICING_FEATURE_ICONS] } : {}
28110
28204
  });
28111
28205
  return;
28112
28206
  }
@@ -29353,15 +29447,43 @@ function OhhwellsBridge() {
29353
29447
  if (e.data?.type !== "ow:icon-markup") return;
29354
29448
  const { key, markup } = e.data;
29355
29449
  if (!key || typeof markup !== "string") return;
29356
- const targets = document.querySelectorAll(
29357
- `[data-ohw-key="${key}"][data-ohw-editable="icon"]`
29358
- );
29450
+ const targets = Array.from(
29451
+ document.querySelectorAll(`[data-ohw-key="${key}"][data-ohw-editable="icon"]`)
29452
+ ).filter((el) => !el.hasAttribute("data-ai-icon"));
29359
29453
  if (!targets.length) return;
29360
29454
  targets.forEach((el) => {
29361
29455
  applyIconMarkup(el, markup);
29362
29456
  });
29363
29457
  postToParentRef.current({ type: "ow:change", nodes: [{ key, text: markup }], flush: true });
29364
29458
  };
29459
+ const handleIconName = (e) => {
29460
+ if (e.data?.type !== "ow:icon-name") return;
29461
+ const { key, icon, url } = e.data;
29462
+ const isName = typeof icon === "string" && /^[a-z0-9-]+$/.test(icon);
29463
+ const isUpload = !isName && typeof url === "string" && /^https?:\/\/\S+$/i.test(url);
29464
+ if (!key || !isName && !isUpload) return;
29465
+ const state = parseAiSectionsState(aiSectionsRef.current);
29466
+ const entry = state.sections.find((s) => key.startsWith(aiSlotKeyPrefixFor(s.id)));
29467
+ if (!entry) return;
29468
+ const path = key.slice(aiSlotKeyPrefixFor(entry.id).length);
29469
+ const uploadRef = isUpload ? `icon-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}` : null;
29470
+ const nextTree = uploadRef ? applyIconUploadToTree(entry.tree, path, uploadRef) : applyIconNameToTree(entry.tree, path, icon);
29471
+ if (!nextTree) return;
29472
+ void requestValidateTree5(postToParentRef.current, nextTree).then(({ ok }) => {
29473
+ if (!ok) return;
29474
+ applyTreeLocalRef.current?.({
29475
+ id: entry.id,
29476
+ mode: "replace",
29477
+ targetSectionId: entry.id,
29478
+ tree: nextTree,
29479
+ label: entry.label,
29480
+ media: uploadRef ? { ...entry.media, [uploadRef]: url } : entry.media,
29481
+ ...entry.brand ? { brand: entry.brand } : {},
29482
+ // The user is looking at the icon they just changed — no need to recenter on it.
29483
+ skipScrollIntoView: true
29484
+ });
29485
+ });
29486
+ };
29365
29487
  const handleImageUrl = (e) => {
29366
29488
  if (e.data?.type !== "ow:image-url") return;
29367
29489
  const { key, url } = e.data;
@@ -30810,6 +30932,7 @@ function OhhwellsBridge() {
30810
30932
  window.addEventListener("message", handleSocialCancel);
30811
30933
  window.addEventListener("message", handleSocialUpdate);
30812
30934
  window.addEventListener("message", handleIconMarkup);
30935
+ window.addEventListener("message", handleIconName);
30813
30936
  window.addEventListener("message", handleImageUrl);
30814
30937
  window.addEventListener("message", handleImageUploading);
30815
30938
  window.addEventListener("message", handleCarouselChange);
@@ -30894,6 +31017,7 @@ function OhhwellsBridge() {
30894
31017
  window.removeEventListener("message", handleSocialCancel);
30895
31018
  window.removeEventListener("message", handleSocialUpdate);
30896
31019
  window.removeEventListener("message", handleIconMarkup);
31020
+ window.removeEventListener("message", handleIconName);
30897
31021
  window.removeEventListener("message", handleImageUrl);
30898
31022
  window.removeEventListener("message", handleImageUploading);
30899
31023
  window.removeEventListener("message", handleCarouselChange);
@@ -31219,7 +31343,7 @@ function OhhwellsBridge() {
31219
31343
  postToParent2({
31220
31344
  type: "ow:ready",
31221
31345
  version: "1",
31222
- bridgeVersion: "0.1.141",
31346
+ bridgeVersion: "0.1.142",
31223
31347
  path: pathname,
31224
31348
  nodes: collectEditableNodes(editContentRef.current),
31225
31349
  sections