@ohhwells/bridge 0.1.142 → 0.1.144
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 +421 -242
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +421 -242
- package/dist/index.js.map +1 -1
- package/dist/styles.css +4 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1522,6 +1522,52 @@ function assignmentFor(el, content, tokens) {
|
|
|
1522
1522
|
function labelElement(el) {
|
|
1523
1523
|
return el.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ?? (el.matches('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ? el : null);
|
|
1524
1524
|
}
|
|
1525
|
+
var BRAND_TRACK_PRECEDENCE = {
|
|
1526
|
+
background: ["primary", "accent", "background", "surface", "on-primary", "text", "border", "text-muted"],
|
|
1527
|
+
color: ["on-primary", "text", "primary", "accent", "text-muted", "background", "surface", "border"],
|
|
1528
|
+
borderColor: ["border", "primary", "accent", "text", "on-primary", "background", "surface", "text-muted"]
|
|
1529
|
+
};
|
|
1530
|
+
var FONT_TRACK_VARS = ["--font-body", "--font-heading", "--font-display", "--brand-font-body", "--brand-font-heading"];
|
|
1531
|
+
function readRootVar(doc, name) {
|
|
1532
|
+
const root = doc.documentElement;
|
|
1533
|
+
return root.style.getPropertyValue(name).trim() || getComputedStyle(root).getPropertyValue(name).trim();
|
|
1534
|
+
}
|
|
1535
|
+
function sameColor(a, b) {
|
|
1536
|
+
return Math.round(a.r) === Math.round(b.r) && Math.round(a.g) === Math.round(b.g) && Math.round(a.b) === Math.round(b.b) && Math.abs(a.a - b.a) < 0.02;
|
|
1537
|
+
}
|
|
1538
|
+
function brandTrackedColor(doc, color, roles) {
|
|
1539
|
+
if (!color || color.includes("var(")) return color;
|
|
1540
|
+
const parsed = parseColor(color);
|
|
1541
|
+
if (!parsed || isTransparent(parsed)) return color;
|
|
1542
|
+
for (const role of roles) {
|
|
1543
|
+
const name = `--brand-${role}`;
|
|
1544
|
+
const token = parseColor(readRootVar(doc, name));
|
|
1545
|
+
if (token && !isTransparent(token) && sameColor(parsed, token)) return `var(${name}, ${color})`;
|
|
1546
|
+
}
|
|
1547
|
+
return color;
|
|
1548
|
+
}
|
|
1549
|
+
function brandTrackedChrome(doc, chrome) {
|
|
1550
|
+
return {
|
|
1551
|
+
...chrome,
|
|
1552
|
+
background: brandTrackedColor(doc, chrome.background, BRAND_TRACK_PRECEDENCE.background),
|
|
1553
|
+
color: brandTrackedColor(doc, chrome.color, BRAND_TRACK_PRECEDENCE.color),
|
|
1554
|
+
borderColor: brandTrackedColor(doc, chrome.borderColor, BRAND_TRACK_PRECEDENCE.borderColor)
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
function firstFamily(stack) {
|
|
1558
|
+
const first = stack.split(",")[0]?.trim() ?? "";
|
|
1559
|
+
return first.replace(/^['"]|['"]$/g, "").toLowerCase();
|
|
1560
|
+
}
|
|
1561
|
+
function brandTrackedFontFamily(doc, stack) {
|
|
1562
|
+
if (!stack || stack.includes("var(")) return stack;
|
|
1563
|
+
const family = firstFamily(stack);
|
|
1564
|
+
if (!family) return stack;
|
|
1565
|
+
for (const name of FONT_TRACK_VARS) {
|
|
1566
|
+
const raw = readRootVar(doc, name);
|
|
1567
|
+
if (raw && firstFamily(raw) === family) return `var(${name}, ${stack})`;
|
|
1568
|
+
}
|
|
1569
|
+
return stack;
|
|
1570
|
+
}
|
|
1525
1571
|
var paintLockByEl = /* @__PURE__ */ new WeakMap();
|
|
1526
1572
|
var paintObserverByEl = /* @__PURE__ */ new WeakMap();
|
|
1527
1573
|
var paintingButton = false;
|
|
@@ -1548,7 +1594,7 @@ function paintButtonChrome(el, assignment, tokens) {
|
|
|
1548
1594
|
el.style.borderRadius = px(origin.size.borderRadius);
|
|
1549
1595
|
el.style.gap = geometry.gap;
|
|
1550
1596
|
el.style.height = px(geometry.height);
|
|
1551
|
-
el.style.fontFamily = typography.fontFamily;
|
|
1597
|
+
el.style.fontFamily = brandTrackedFontFamily(el.ownerDocument, typography.fontFamily);
|
|
1552
1598
|
el.style.fontWeight = typography.fontWeight;
|
|
1553
1599
|
el.style.letterSpacing = typography.letterSpacing;
|
|
1554
1600
|
el.style.textTransform = typography.textTransform;
|
|
@@ -1577,9 +1623,18 @@ function paintedTokensOf(el) {
|
|
|
1577
1623
|
return paintLockByEl.get(el)?.tokens ?? null;
|
|
1578
1624
|
}
|
|
1579
1625
|
function applyButtonStyle(el, assignment, tokens) {
|
|
1580
|
-
|
|
1626
|
+
const doc = el.ownerDocument;
|
|
1627
|
+
const tracked = {
|
|
1628
|
+
...tokens,
|
|
1629
|
+
variants: {
|
|
1630
|
+
primary: brandTrackedChrome(doc, tokens.variants.primary),
|
|
1631
|
+
secondary: brandTrackedChrome(doc, tokens.variants.secondary),
|
|
1632
|
+
ghost: brandTrackedChrome(doc, tokens.variants.ghost)
|
|
1633
|
+
}
|
|
1634
|
+
};
|
|
1635
|
+
paintLockByEl.set(el, { assignment, tokens: tracked });
|
|
1581
1636
|
paintingButton = true;
|
|
1582
|
-
paintButtonChrome(el, assignment,
|
|
1637
|
+
paintButtonChrome(el, assignment, tracked);
|
|
1583
1638
|
paintingButton = false;
|
|
1584
1639
|
guardTemplateHover(el);
|
|
1585
1640
|
}
|
|
@@ -5033,10 +5088,9 @@ function getLinkHref3(el) {
|
|
|
5033
5088
|
}
|
|
5034
5089
|
function collectEditableNodes(extraContent, root = document) {
|
|
5035
5090
|
const isScoped = root !== document;
|
|
5036
|
-
const
|
|
5037
|
-
|
|
5038
|
-
)
|
|
5039
|
-
if (isScoped && root instanceof HTMLElement && root.matches('[data-ohw-editable]:not([data-ohw-editable="form"])')) {
|
|
5091
|
+
const EDITABLE_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="form"]):not([data-ai-icon])';
|
|
5092
|
+
const editableEls = Array.from(root.querySelectorAll(EDITABLE_SELECTOR));
|
|
5093
|
+
if (isScoped && root instanceof HTMLElement && root.matches(EDITABLE_SELECTOR)) {
|
|
5040
5094
|
editableEls.unshift(root);
|
|
5041
5095
|
}
|
|
5042
5096
|
const nodes = editableEls.map((el) => {
|
|
@@ -6303,6 +6357,281 @@ function reapRemovedAiSections(state, store, removedIds, excludeIds = /* @__PURE
|
|
|
6303
6357
|
return { state: nextState, store: nextStore, reapedIds, slotPrefixes, changed: true };
|
|
6304
6358
|
}
|
|
6305
6359
|
|
|
6360
|
+
// src/lib/ai-pricing-tree.ts
|
|
6361
|
+
var PRICING_MIN_CARDS = 1;
|
|
6362
|
+
var PRICING_MAX_CARDS = 4;
|
|
6363
|
+
function isPricingCard(node) {
|
|
6364
|
+
return typeof node === "object" && node !== null && node.type === "card" && node.slots?.preset === "pricing";
|
|
6365
|
+
}
|
|
6366
|
+
function isPricingCollection(node) {
|
|
6367
|
+
return node.type === "collection" && Array.isArray(node.children) && node.children.length > 0 && isPricingCard(node.children[0]);
|
|
6368
|
+
}
|
|
6369
|
+
function findCollection(node) {
|
|
6370
|
+
if (isPricingCollection(node)) return node;
|
|
6371
|
+
for (const child of node.children ?? []) {
|
|
6372
|
+
const found = findCollection(child);
|
|
6373
|
+
if (found) return found;
|
|
6374
|
+
}
|
|
6375
|
+
return null;
|
|
6376
|
+
}
|
|
6377
|
+
function findPricingCollection(tree) {
|
|
6378
|
+
for (const row of tree.rows ?? []) {
|
|
6379
|
+
for (const block of row.blocks ?? []) {
|
|
6380
|
+
const found = findCollection(block);
|
|
6381
|
+
if (found) return found;
|
|
6382
|
+
}
|
|
6383
|
+
}
|
|
6384
|
+
return null;
|
|
6385
|
+
}
|
|
6386
|
+
function pricingCardCount(tree) {
|
|
6387
|
+
return findPricingCollection(tree)?.children?.length ?? 0;
|
|
6388
|
+
}
|
|
6389
|
+
function withPricingCollection(tree, mutate) {
|
|
6390
|
+
const next = JSON.parse(JSON.stringify(tree));
|
|
6391
|
+
const collection = findPricingCollection(next);
|
|
6392
|
+
if (!collection || !collection.children) return null;
|
|
6393
|
+
for (const card of collection.children) {
|
|
6394
|
+
if (isPricingCard(card)) upgradeLegacyPricingSlots(card);
|
|
6395
|
+
}
|
|
6396
|
+
return mutate(collection) ? next : null;
|
|
6397
|
+
}
|
|
6398
|
+
function upgradeLegacyPricingSlots(card) {
|
|
6399
|
+
const slots = card.slots = card.slots ?? {};
|
|
6400
|
+
if (typeof slots.features === "string") {
|
|
6401
|
+
const rows = pricingFeatureList(card);
|
|
6402
|
+
if (rows.length > 0) slots.features = rows.slice(0, PRICING_FEATURES_MAX);
|
|
6403
|
+
}
|
|
6404
|
+
if (slots.featured === true) slots.featured = { enabled: true };
|
|
6405
|
+
else if (slots.featured === false) delete slots.featured;
|
|
6406
|
+
}
|
|
6407
|
+
function addPricingTier(tree) {
|
|
6408
|
+
return withPricingCollection(tree, (collection) => {
|
|
6409
|
+
const children = collection.children;
|
|
6410
|
+
if (children.length >= PRICING_MAX_CARDS) return false;
|
|
6411
|
+
const clone = JSON.parse(JSON.stringify(children[children.length - 1]));
|
|
6412
|
+
clone.slots = clone.slots ?? {};
|
|
6413
|
+
delete clone.slots.featured;
|
|
6414
|
+
clone.slots.heading = "New tier";
|
|
6415
|
+
children.push(clone);
|
|
6416
|
+
collection.slots = { ...collection.slots, itemsPerRow: children.length };
|
|
6417
|
+
return true;
|
|
6418
|
+
});
|
|
6419
|
+
}
|
|
6420
|
+
function removePricingTier(tree, index) {
|
|
6421
|
+
return withPricingCollection(tree, (collection) => {
|
|
6422
|
+
const children = collection.children;
|
|
6423
|
+
if (children.length <= PRICING_MIN_CARDS) return false;
|
|
6424
|
+
if (index < 0 || index >= children.length) return false;
|
|
6425
|
+
children.splice(index, 1);
|
|
6426
|
+
collection.slots = { ...collection.slots, itemsPerRow: children.length };
|
|
6427
|
+
return true;
|
|
6428
|
+
});
|
|
6429
|
+
}
|
|
6430
|
+
var PRICING_CARD_STYLES = [
|
|
6431
|
+
{ value: "outline", label: "Outline", treatment: null },
|
|
6432
|
+
{ value: "lift", label: "Lift", treatment: "lifted" },
|
|
6433
|
+
{ value: "invert", label: "Invert", treatment: "inverted" },
|
|
6434
|
+
{ value: "header", label: "Header", treatment: "accent-header" }
|
|
6435
|
+
];
|
|
6436
|
+
var DEFAULT_FEATURED_BADGE = "Most popular";
|
|
6437
|
+
function featuredOf(target) {
|
|
6438
|
+
const raw = target?.slots?.featured;
|
|
6439
|
+
if (raw === true) return { enabled: true, treatment: "outlined" };
|
|
6440
|
+
return typeof raw === "object" && raw !== null ? raw : {};
|
|
6441
|
+
}
|
|
6442
|
+
function badgeOf(featured) {
|
|
6443
|
+
return typeof featured.badge === "string" ? featured.badge.trim() : "";
|
|
6444
|
+
}
|
|
6445
|
+
function pricingCardStyle(tree, index) {
|
|
6446
|
+
const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
|
|
6447
|
+
if (featured.enabled !== true) return "outline";
|
|
6448
|
+
return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
|
|
6449
|
+
}
|
|
6450
|
+
function pricingCardFeaturedTag(tree, index) {
|
|
6451
|
+
const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
|
|
6452
|
+
return featured.enabled === true && badgeOf(featured).length > 0;
|
|
6453
|
+
}
|
|
6454
|
+
function composeFeatured(style, tagOn, badgeText) {
|
|
6455
|
+
const treatment = PRICING_CARD_STYLES.find((s) => s.value === style)?.treatment ?? null;
|
|
6456
|
+
if (tagOn) return { enabled: true, badge: badgeText || DEFAULT_FEATURED_BADGE, treatment: treatment ?? "outlined" };
|
|
6457
|
+
if (treatment) return { enabled: true, treatment };
|
|
6458
|
+
return badgeText.length > 0 ? { enabled: false, badge: badgeText } : { enabled: false };
|
|
6459
|
+
}
|
|
6460
|
+
function withPricingCard(tree, index, mutate) {
|
|
6461
|
+
return withPricingCollection(tree, (collection) => {
|
|
6462
|
+
const children = collection.children;
|
|
6463
|
+
if (index < 0 || index >= children.length) return false;
|
|
6464
|
+
const target = children[index];
|
|
6465
|
+
target.slots = target.slots ?? {};
|
|
6466
|
+
mutate(target);
|
|
6467
|
+
return true;
|
|
6468
|
+
});
|
|
6469
|
+
}
|
|
6470
|
+
function setPricingCardStyle(tree, index, style) {
|
|
6471
|
+
if (!PRICING_CARD_STYLES.some((s) => s.value === style)) return null;
|
|
6472
|
+
return withPricingCard(tree, index, (target) => {
|
|
6473
|
+
const prev = featuredOf(target);
|
|
6474
|
+
const tagOn = prev.enabled === true && badgeOf(prev).length > 0;
|
|
6475
|
+
target.slots.featured = composeFeatured(style, tagOn, badgeOf(prev));
|
|
6476
|
+
});
|
|
6477
|
+
}
|
|
6478
|
+
function setPricingCardFeaturedTag(tree, index, on) {
|
|
6479
|
+
return withPricingCollection(tree, (collection) => {
|
|
6480
|
+
const children = collection.children;
|
|
6481
|
+
if (index < 0 || index >= children.length) return false;
|
|
6482
|
+
if (on) {
|
|
6483
|
+
children.forEach((card, i) => {
|
|
6484
|
+
if (i === index) return;
|
|
6485
|
+
const prev2 = featuredOf(card);
|
|
6486
|
+
if (prev2.enabled !== true || badgeOf(prev2).length === 0) return;
|
|
6487
|
+
card.slots = card.slots ?? {};
|
|
6488
|
+
card.slots.featured = composeFeatured(pricingCardStyleOf(prev2), false, badgeOf(prev2));
|
|
6489
|
+
});
|
|
6490
|
+
}
|
|
6491
|
+
const target = children[index];
|
|
6492
|
+
target.slots = target.slots ?? {};
|
|
6493
|
+
const prev = featuredOf(target);
|
|
6494
|
+
target.slots.featured = composeFeatured(pricingCardStyleOf(prev), on, badgeOf(prev));
|
|
6495
|
+
return true;
|
|
6496
|
+
});
|
|
6497
|
+
}
|
|
6498
|
+
function pricingCardStyleOf(featured) {
|
|
6499
|
+
if (featured.enabled !== true) return "outline";
|
|
6500
|
+
return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
|
|
6501
|
+
}
|
|
6502
|
+
function addPricingCardButton(tree, index, label) {
|
|
6503
|
+
return withPricingCollection(tree, (collection) => {
|
|
6504
|
+
const children = collection.children;
|
|
6505
|
+
if (index < 0 || index >= children.length) return false;
|
|
6506
|
+
const target = children[index];
|
|
6507
|
+
const buttons = (target.children ?? []).filter((c) => c.type === "button");
|
|
6508
|
+
if (buttons.length >= 2) return false;
|
|
6509
|
+
const hasPrimary = buttons.some((b) => b.slots?.variant === "primary");
|
|
6510
|
+
target.children = [
|
|
6511
|
+
...target.children ?? [],
|
|
6512
|
+
{ type: "button", span: 12, slots: { label, href: "#", variant: hasPrimary ? "secondary" : "primary" } }
|
|
6513
|
+
];
|
|
6514
|
+
return true;
|
|
6515
|
+
});
|
|
6516
|
+
}
|
|
6517
|
+
function removePricingCardButton(tree, index) {
|
|
6518
|
+
return withPricingCollection(tree, (collection) => {
|
|
6519
|
+
const children = collection.children;
|
|
6520
|
+
if (index < 0 || index >= children.length) return false;
|
|
6521
|
+
const target = children[index];
|
|
6522
|
+
const buttons = (target.children ?? []).filter((c) => c.type === "button");
|
|
6523
|
+
if (buttons.length === 0) return false;
|
|
6524
|
+
const lastButton = buttons[buttons.length - 1];
|
|
6525
|
+
target.children = (target.children ?? []).filter((c) => c !== lastButton);
|
|
6526
|
+
return true;
|
|
6527
|
+
});
|
|
6528
|
+
}
|
|
6529
|
+
var PRICING_FEATURES_MIN = 1;
|
|
6530
|
+
var PRICING_FEATURES_MAX = 10;
|
|
6531
|
+
var PRICING_FEATURE_ICONS = [
|
|
6532
|
+
"check",
|
|
6533
|
+
"check-circle",
|
|
6534
|
+
"x",
|
|
6535
|
+
"star",
|
|
6536
|
+
"zap",
|
|
6537
|
+
"shield",
|
|
6538
|
+
"clock",
|
|
6539
|
+
"users",
|
|
6540
|
+
"infinity"
|
|
6541
|
+
];
|
|
6542
|
+
function pricingFeatureList(target) {
|
|
6543
|
+
const raw = target.slots?.features;
|
|
6544
|
+
if (Array.isArray(raw)) {
|
|
6545
|
+
return raw.filter((item) => !!item && typeof item === "object" && typeof item.text === "string").map((item) => ({
|
|
6546
|
+
text: item.text,
|
|
6547
|
+
...typeof item.icon === "string" ? { icon: item.icon } : {},
|
|
6548
|
+
...typeof item.iconColor === "string" ? { iconColor: item.iconColor } : {}
|
|
6549
|
+
}));
|
|
6550
|
+
}
|
|
6551
|
+
if (typeof raw === "string") {
|
|
6552
|
+
return raw.split(/\n|<br\s*\/?>/i).map((line) => line.replace(/<[^>]+>/g, "").replace(/^[-*•]\s*/, "").trim().slice(0, 120)).filter(Boolean).map((text) => ({ text }));
|
|
6553
|
+
}
|
|
6554
|
+
return [];
|
|
6555
|
+
}
|
|
6556
|
+
function pricingCardFeatureCount(tree, index) {
|
|
6557
|
+
const target = findPricingCollection(tree)?.children?.[index];
|
|
6558
|
+
return target ? pricingFeatureList(target).length : 0;
|
|
6559
|
+
}
|
|
6560
|
+
function addPricingCardFeature(tree, index, text) {
|
|
6561
|
+
return withPricingCollection(tree, (collection) => {
|
|
6562
|
+
const children = collection.children;
|
|
6563
|
+
if (index < 0 || index >= children.length) return false;
|
|
6564
|
+
const target = children[index];
|
|
6565
|
+
const features = pricingFeatureList(target);
|
|
6566
|
+
if (features.length >= PRICING_FEATURES_MAX) return false;
|
|
6567
|
+
target.slots = target.slots ?? {};
|
|
6568
|
+
target.slots.features = [...features, { text }];
|
|
6569
|
+
return true;
|
|
6570
|
+
});
|
|
6571
|
+
}
|
|
6572
|
+
function setPricingCardFeatureIcon(tree, index, featureIndex, icon) {
|
|
6573
|
+
if (!PRICING_FEATURE_ICONS.includes(icon)) return null;
|
|
6574
|
+
return withPricingCollection(tree, (collection) => {
|
|
6575
|
+
const children = collection.children;
|
|
6576
|
+
if (index < 0 || index >= children.length) return false;
|
|
6577
|
+
const target = children[index];
|
|
6578
|
+
const features = pricingFeatureList(target);
|
|
6579
|
+
if (featureIndex < 0 || featureIndex >= features.length) return false;
|
|
6580
|
+
target.slots = target.slots ?? {};
|
|
6581
|
+
target.slots.features = features.map((row, i) => i === featureIndex ? { ...row, icon } : row);
|
|
6582
|
+
return true;
|
|
6583
|
+
});
|
|
6584
|
+
}
|
|
6585
|
+
function removePricingCardFeature(tree, index) {
|
|
6586
|
+
return withPricingCollection(tree, (collection) => {
|
|
6587
|
+
const children = collection.children;
|
|
6588
|
+
if (index < 0 || index >= children.length) return false;
|
|
6589
|
+
const target = children[index];
|
|
6590
|
+
const features = pricingFeatureList(target);
|
|
6591
|
+
if (features.length <= PRICING_FEATURES_MIN) return false;
|
|
6592
|
+
target.slots = target.slots ?? {};
|
|
6593
|
+
target.slots.features = features.slice(0, -1);
|
|
6594
|
+
return true;
|
|
6595
|
+
});
|
|
6596
|
+
}
|
|
6597
|
+
|
|
6598
|
+
// src/lib/ai-icon-edit.ts
|
|
6599
|
+
var FEATURE_ICON_PATH = /\.c(\d+)\.features\.(\d+)\.icon$/;
|
|
6600
|
+
var NODE_SEGMENT = /^[rbc]\d+$/;
|
|
6601
|
+
function nodeAtPath(tree, segments) {
|
|
6602
|
+
if (segments.length < 2 || segments.some((s) => !NODE_SEGMENT.test(s))) return null;
|
|
6603
|
+
if (!segments[0].startsWith("r") || !segments[1].startsWith("b")) return null;
|
|
6604
|
+
let node = tree.rows?.[Number(segments[0].slice(1))]?.blocks?.[Number(segments[1].slice(1))];
|
|
6605
|
+
for (const segment of segments.slice(2)) {
|
|
6606
|
+
if (!segment.startsWith("c")) return null;
|
|
6607
|
+
node = node?.children?.[Number(segment.slice(1))];
|
|
6608
|
+
}
|
|
6609
|
+
return node ?? null;
|
|
6610
|
+
}
|
|
6611
|
+
function applyIconNameToTree(tree, path, icon) {
|
|
6612
|
+
const feature = path.match(FEATURE_ICON_PATH);
|
|
6613
|
+
if (feature) {
|
|
6614
|
+
return setPricingCardFeatureIcon(tree, Number(feature[1]), Number(feature[2]), icon);
|
|
6615
|
+
}
|
|
6616
|
+
return rewriteIconSlot(tree, path, `lucide:${icon}`);
|
|
6617
|
+
}
|
|
6618
|
+
function applyIconUploadToTree(tree, path, mediaRef) {
|
|
6619
|
+
if (FEATURE_ICON_PATH.test(path)) return null;
|
|
6620
|
+
return rewriteIconSlot(tree, path, `icon:${mediaRef}`);
|
|
6621
|
+
}
|
|
6622
|
+
function rewriteIconSlot(tree, path, ref) {
|
|
6623
|
+
const segments = path.split(".");
|
|
6624
|
+
const slot = segments.pop();
|
|
6625
|
+
if (!slot || NODE_SEGMENT.test(slot)) return null;
|
|
6626
|
+
const next = JSON.parse(JSON.stringify(tree));
|
|
6627
|
+
const node = nodeAtPath(next, segments);
|
|
6628
|
+
if (!node) return null;
|
|
6629
|
+
const current = node.slots?.[slot];
|
|
6630
|
+
if (typeof current !== "string" || !/^(lucide|simple|icon):/.test(current)) return null;
|
|
6631
|
+
node.slots = { ...node.slots, [slot]: ref };
|
|
6632
|
+
return next;
|
|
6633
|
+
}
|
|
6634
|
+
|
|
6306
6635
|
// src/lib/ai-footer-tree.ts
|
|
6307
6636
|
var FOOTER_COLUMN_SPANS = [12, 6, 4, 3];
|
|
6308
6637
|
var FOOTER_COLUMN_BLOCKS = [
|
|
@@ -6786,8 +7115,8 @@ function applyBrandToDom(kit) {
|
|
|
6786
7115
|
if (kit.fonts) {
|
|
6787
7116
|
appliedFonts = applyFonts ? kit.fonts : null;
|
|
6788
7117
|
}
|
|
7118
|
+
for (const name of BUTTON_VAR_NAMES) root.style.removeProperty(name);
|
|
6789
7119
|
if (kit.buttons) {
|
|
6790
|
-
for (const name of BUTTON_VAR_NAMES) root.style.removeProperty(name);
|
|
6791
7120
|
for (const [name, value] of Object.entries(buttonStyleVars({ ...kit, buttons: kit.buttons }))) root.style.setProperty(name, value);
|
|
6792
7121
|
}
|
|
6793
7122
|
syncButtonRules(kit.buttons ? kit : null);
|
|
@@ -7659,7 +7988,7 @@ function MediaBox({
|
|
|
7659
7988
|
onError
|
|
7660
7989
|
}) {
|
|
7661
7990
|
const url = refValue ? ctx.resolveMedia(refValue) : null;
|
|
7662
|
-
const isIcon = /^(lucide|simple):/.test(refValue);
|
|
7991
|
+
const isIcon = /^(lucide|simple|icon):/.test(refValue);
|
|
7663
7992
|
const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
|
|
7664
7993
|
const editAttrs = ctx.keyFor && editPath && !isIcon ? {
|
|
7665
7994
|
"data-ohw-key": ctx.keyFor(editPath),
|
|
@@ -7667,10 +7996,13 @@ function MediaBox({
|
|
|
7667
7996
|
} : {};
|
|
7668
7997
|
if (isIcon) {
|
|
7669
7998
|
const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
|
|
7999
|
+
const uploadedIconUrl = refValue.startsWith("icon:") ? ctx.resolveMedia(refValue.slice(5)) : null;
|
|
8000
|
+
const iconEditAttrs = ctx.keyFor && editPath && genericImageEdit ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "icon" } : {};
|
|
7670
8001
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
7671
8002
|
"span",
|
|
7672
8003
|
{
|
|
7673
8004
|
"data-ai-icon": refValue,
|
|
8005
|
+
...iconEditAttrs,
|
|
7674
8006
|
style: {
|
|
7675
8007
|
display: "inline-flex",
|
|
7676
8008
|
width: 48,
|
|
@@ -7679,7 +8011,7 @@ function MediaBox({
|
|
|
7679
8011
|
justifyContent: "center",
|
|
7680
8012
|
color: ctx.brand.palette.dark
|
|
7681
8013
|
},
|
|
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" })
|
|
8014
|
+
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
8015
|
}
|
|
7684
8016
|
);
|
|
7685
8017
|
}
|
|
@@ -8167,8 +8499,20 @@ function PricingCard({ node, ctx, path }) {
|
|
|
8167
8499
|
/* @__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
8500
|
const Icon = lucideByName(item.icon || "check");
|
|
8169
8501
|
const iconColor = pricingIconColor(item.iconColor, ctx.brand, fg);
|
|
8502
|
+
const iconAttrs = ctx.keyFor && !item.html ? {
|
|
8503
|
+
"data-ohw-key": ctx.keyFor(`${path}.features.${i}.icon`),
|
|
8504
|
+
"data-ohw-editable": "icon"
|
|
8505
|
+
} : {};
|
|
8170
8506
|
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)(
|
|
8507
|
+
Icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
8508
|
+
"span",
|
|
8509
|
+
{
|
|
8510
|
+
"data-ai-icon": `lucide:${item.icon || "check"}`,
|
|
8511
|
+
...iconAttrs,
|
|
8512
|
+
style: { display: "inline-flex", flexShrink: 0 },
|
|
8513
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { size: 20, color: iconColor })
|
|
8514
|
+
}
|
|
8515
|
+
),
|
|
8172
8516
|
item.html ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
8173
8517
|
"span",
|
|
8174
8518
|
{
|
|
@@ -10173,7 +10517,7 @@ function isChromeSection2(el) {
|
|
|
10173
10517
|
if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) return true;
|
|
10174
10518
|
return el.tagName === "HEADER" || el.tagName === "FOOTER";
|
|
10175
10519
|
}
|
|
10176
|
-
function
|
|
10520
|
+
function readRootVar2(name) {
|
|
10177
10521
|
if (typeof document === "undefined") return "";
|
|
10178
10522
|
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
10179
10523
|
}
|
|
@@ -10213,14 +10557,14 @@ function humanizeFontFamily(name) {
|
|
|
10213
10557
|
return name.replace(/^__/, "").replace(/_[0-9a-f]{6}$/i, "").replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim().replace(/\s+/g, " ").replace(/(^|\s)([a-z])/g, (_, sep, c) => sep + c.toUpperCase());
|
|
10214
10558
|
}
|
|
10215
10559
|
function readFontVar(name) {
|
|
10216
|
-
return humanizeFontFamily(primaryFontFamily(
|
|
10560
|
+
return humanizeFontFamily(primaryFontFamily(readRootVar2(name)));
|
|
10217
10561
|
}
|
|
10218
10562
|
function deriveBrandOverride() {
|
|
10219
|
-
const dark =
|
|
10220
|
-
const primary =
|
|
10221
|
-
const light =
|
|
10563
|
+
const dark = readRootVar2("--ohw-brand-dark");
|
|
10564
|
+
const primary = readRootVar2("--ohw-brand-primary");
|
|
10565
|
+
const light = readRootVar2("--ohw-brand-light");
|
|
10222
10566
|
if (!dark || !primary || !light) return null;
|
|
10223
|
-
const accent =
|
|
10567
|
+
const accent = readRootVar2("--ohw-brand-accent");
|
|
10224
10568
|
const heading = readFontVar("--font-heading") || readFontVar("--font-display");
|
|
10225
10569
|
const body = readFontVar("--font-body");
|
|
10226
10570
|
return {
|
|
@@ -10232,11 +10576,11 @@ function deriveBrandOverride() {
|
|
|
10232
10576
|
};
|
|
10233
10577
|
}
|
|
10234
10578
|
function deriveTemplateBrandLive() {
|
|
10235
|
-
const primary =
|
|
10236
|
-
const dark =
|
|
10237
|
-
const light =
|
|
10579
|
+
const primary = readRootVar2("--brand-primary") || readRootVar2("--color-primary");
|
|
10580
|
+
const dark = readRootVar2("--brand-text") || readRootVar2("--color-dark");
|
|
10581
|
+
const light = readRootVar2("--brand-background") || readRootVar2("--color-light");
|
|
10238
10582
|
if (!dark || !primary || !light) return null;
|
|
10239
|
-
const accent =
|
|
10583
|
+
const accent = readRootVar2("--brand-accent") || readRootVar2("--color-accent");
|
|
10240
10584
|
const heading = readFontVar("--brand-font-heading") || readFontVar("--font-heading") || readFontVar("--font-display");
|
|
10241
10585
|
const body = readFontVar("--brand-font-body") || readFontVar("--font-body");
|
|
10242
10586
|
return {
|
|
@@ -18411,222 +18755,6 @@ function AiMediaStripOverlay({
|
|
|
18411
18755
|
// src/ui/ai-section/AiPricingCardOverlay.tsx
|
|
18412
18756
|
var import_react11 = require("react");
|
|
18413
18757
|
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
18758
|
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
18631
18759
|
var PRIMARY4 = "var(--ohw-primary, #0885FE)";
|
|
18632
18760
|
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 +23880,23 @@ function currentIconRef(el) {
|
|
|
23752
23880
|
);
|
|
23753
23881
|
return named ? `lucide:${named.slice("lucide-".length)}` : "";
|
|
23754
23882
|
}
|
|
23883
|
+
function requestValidateTree5(postToParent2, tree) {
|
|
23884
|
+
return new Promise((resolve) => {
|
|
23885
|
+
const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
23886
|
+
const timeout = window.setTimeout(() => {
|
|
23887
|
+
window.removeEventListener("message", onMessage);
|
|
23888
|
+
resolve({ ok: false, reasons: ["Validation request timed out."] });
|
|
23889
|
+
}, 8e3);
|
|
23890
|
+
function onMessage(e) {
|
|
23891
|
+
if (e.data?.type !== "ow:ai-validate-tree-result" || e.data.id !== id) return;
|
|
23892
|
+
window.clearTimeout(timeout);
|
|
23893
|
+
window.removeEventListener("message", onMessage);
|
|
23894
|
+
resolve({ ok: e.data.ok === true, reasons: Array.isArray(e.data.reasons) ? e.data.reasons : [] });
|
|
23895
|
+
}
|
|
23896
|
+
window.addEventListener("message", onMessage);
|
|
23897
|
+
postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
|
|
23898
|
+
});
|
|
23899
|
+
}
|
|
23755
23900
|
function getEditMeasureEl(editable) {
|
|
23756
23901
|
return editable.closest("[data-ohw-href-key]") ?? editable;
|
|
23757
23902
|
}
|
|
@@ -28103,10 +28248,14 @@ function OhhwellsBridge() {
|
|
|
28103
28248
|
e.preventDefault();
|
|
28104
28249
|
e.stopPropagation();
|
|
28105
28250
|
aiSectionApiRef.current?.selectFromElement(editable);
|
|
28251
|
+
const aiIcon = editable.hasAttribute("data-ai-icon");
|
|
28252
|
+
const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
|
|
28106
28253
|
postToParentRef.current({
|
|
28107
28254
|
type: "ow:icon-pick",
|
|
28108
28255
|
key: editable.dataset.ohwKey ?? "",
|
|
28109
|
-
current: currentIconRef(editable)
|
|
28256
|
+
current: currentIconRef(editable),
|
|
28257
|
+
...aiIcon ? { iconMode: "name" } : {},
|
|
28258
|
+
...featureIcon ? { options: [...PRICING_FEATURE_ICONS] } : {}
|
|
28110
28259
|
});
|
|
28111
28260
|
return;
|
|
28112
28261
|
}
|
|
@@ -29353,15 +29502,43 @@ function OhhwellsBridge() {
|
|
|
29353
29502
|
if (e.data?.type !== "ow:icon-markup") return;
|
|
29354
29503
|
const { key, markup } = e.data;
|
|
29355
29504
|
if (!key || typeof markup !== "string") return;
|
|
29356
|
-
const targets =
|
|
29357
|
-
`[data-ohw-key="${key}"][data-ohw-editable="icon"]`
|
|
29358
|
-
);
|
|
29505
|
+
const targets = Array.from(
|
|
29506
|
+
document.querySelectorAll(`[data-ohw-key="${key}"][data-ohw-editable="icon"]`)
|
|
29507
|
+
).filter((el) => !el.hasAttribute("data-ai-icon"));
|
|
29359
29508
|
if (!targets.length) return;
|
|
29360
29509
|
targets.forEach((el) => {
|
|
29361
29510
|
applyIconMarkup(el, markup);
|
|
29362
29511
|
});
|
|
29363
29512
|
postToParentRef.current({ type: "ow:change", nodes: [{ key, text: markup }], flush: true });
|
|
29364
29513
|
};
|
|
29514
|
+
const handleIconName = (e) => {
|
|
29515
|
+
if (e.data?.type !== "ow:icon-name") return;
|
|
29516
|
+
const { key, icon, url } = e.data;
|
|
29517
|
+
const isName = typeof icon === "string" && /^[a-z0-9-]+$/.test(icon);
|
|
29518
|
+
const isUpload = !isName && typeof url === "string" && /^https?:\/\/\S+$/i.test(url);
|
|
29519
|
+
if (!key || !isName && !isUpload) return;
|
|
29520
|
+
const state = parseAiSectionsState(aiSectionsRef.current);
|
|
29521
|
+
const entry = state.sections.find((s) => key.startsWith(aiSlotKeyPrefixFor(s.id)));
|
|
29522
|
+
if (!entry) return;
|
|
29523
|
+
const path = key.slice(aiSlotKeyPrefixFor(entry.id).length);
|
|
29524
|
+
const uploadRef = isUpload ? `icon-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}` : null;
|
|
29525
|
+
const nextTree = uploadRef ? applyIconUploadToTree(entry.tree, path, uploadRef) : applyIconNameToTree(entry.tree, path, icon);
|
|
29526
|
+
if (!nextTree) return;
|
|
29527
|
+
void requestValidateTree5(postToParentRef.current, nextTree).then(({ ok }) => {
|
|
29528
|
+
if (!ok) return;
|
|
29529
|
+
applyTreeLocalRef.current?.({
|
|
29530
|
+
id: entry.id,
|
|
29531
|
+
mode: "replace",
|
|
29532
|
+
targetSectionId: entry.id,
|
|
29533
|
+
tree: nextTree,
|
|
29534
|
+
label: entry.label,
|
|
29535
|
+
media: uploadRef ? { ...entry.media, [uploadRef]: url } : entry.media,
|
|
29536
|
+
...entry.brand ? { brand: entry.brand } : {},
|
|
29537
|
+
// The user is looking at the icon they just changed — no need to recenter on it.
|
|
29538
|
+
skipScrollIntoView: true
|
|
29539
|
+
});
|
|
29540
|
+
});
|
|
29541
|
+
};
|
|
29365
29542
|
const handleImageUrl = (e) => {
|
|
29366
29543
|
if (e.data?.type !== "ow:image-url") return;
|
|
29367
29544
|
const { key, url } = e.data;
|
|
@@ -30810,6 +30987,7 @@ function OhhwellsBridge() {
|
|
|
30810
30987
|
window.addEventListener("message", handleSocialCancel);
|
|
30811
30988
|
window.addEventListener("message", handleSocialUpdate);
|
|
30812
30989
|
window.addEventListener("message", handleIconMarkup);
|
|
30990
|
+
window.addEventListener("message", handleIconName);
|
|
30813
30991
|
window.addEventListener("message", handleImageUrl);
|
|
30814
30992
|
window.addEventListener("message", handleImageUploading);
|
|
30815
30993
|
window.addEventListener("message", handleCarouselChange);
|
|
@@ -30894,6 +31072,7 @@ function OhhwellsBridge() {
|
|
|
30894
31072
|
window.removeEventListener("message", handleSocialCancel);
|
|
30895
31073
|
window.removeEventListener("message", handleSocialUpdate);
|
|
30896
31074
|
window.removeEventListener("message", handleIconMarkup);
|
|
31075
|
+
window.removeEventListener("message", handleIconName);
|
|
30897
31076
|
window.removeEventListener("message", handleImageUrl);
|
|
30898
31077
|
window.removeEventListener("message", handleImageUploading);
|
|
30899
31078
|
window.removeEventListener("message", handleCarouselChange);
|
|
@@ -31219,7 +31398,7 @@ function OhhwellsBridge() {
|
|
|
31219
31398
|
postToParent2({
|
|
31220
31399
|
type: "ow:ready",
|
|
31221
31400
|
version: "1",
|
|
31222
|
-
bridgeVersion: "0.1.
|
|
31401
|
+
bridgeVersion: "0.1.143",
|
|
31223
31402
|
path: pathname,
|
|
31224
31403
|
nodes: collectEditableNodes(editContentRef.current),
|
|
31225
31404
|
sections
|