@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.js
CHANGED
|
@@ -1428,6 +1428,52 @@ function assignmentFor(el, content, tokens) {
|
|
|
1428
1428
|
function labelElement(el) {
|
|
1429
1429
|
return el.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ?? (el.matches('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ? el : null);
|
|
1430
1430
|
}
|
|
1431
|
+
var BRAND_TRACK_PRECEDENCE = {
|
|
1432
|
+
background: ["primary", "accent", "background", "surface", "on-primary", "text", "border", "text-muted"],
|
|
1433
|
+
color: ["on-primary", "text", "primary", "accent", "text-muted", "background", "surface", "border"],
|
|
1434
|
+
borderColor: ["border", "primary", "accent", "text", "on-primary", "background", "surface", "text-muted"]
|
|
1435
|
+
};
|
|
1436
|
+
var FONT_TRACK_VARS = ["--font-body", "--font-heading", "--font-display", "--brand-font-body", "--brand-font-heading"];
|
|
1437
|
+
function readRootVar(doc, name) {
|
|
1438
|
+
const root = doc.documentElement;
|
|
1439
|
+
return root.style.getPropertyValue(name).trim() || getComputedStyle(root).getPropertyValue(name).trim();
|
|
1440
|
+
}
|
|
1441
|
+
function sameColor(a, b) {
|
|
1442
|
+
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;
|
|
1443
|
+
}
|
|
1444
|
+
function brandTrackedColor(doc, color, roles) {
|
|
1445
|
+
if (!color || color.includes("var(")) return color;
|
|
1446
|
+
const parsed = parseColor(color);
|
|
1447
|
+
if (!parsed || isTransparent(parsed)) return color;
|
|
1448
|
+
for (const role of roles) {
|
|
1449
|
+
const name = `--brand-${role}`;
|
|
1450
|
+
const token = parseColor(readRootVar(doc, name));
|
|
1451
|
+
if (token && !isTransparent(token) && sameColor(parsed, token)) return `var(${name}, ${color})`;
|
|
1452
|
+
}
|
|
1453
|
+
return color;
|
|
1454
|
+
}
|
|
1455
|
+
function brandTrackedChrome(doc, chrome) {
|
|
1456
|
+
return {
|
|
1457
|
+
...chrome,
|
|
1458
|
+
background: brandTrackedColor(doc, chrome.background, BRAND_TRACK_PRECEDENCE.background),
|
|
1459
|
+
color: brandTrackedColor(doc, chrome.color, BRAND_TRACK_PRECEDENCE.color),
|
|
1460
|
+
borderColor: brandTrackedColor(doc, chrome.borderColor, BRAND_TRACK_PRECEDENCE.borderColor)
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
function firstFamily(stack) {
|
|
1464
|
+
const first = stack.split(",")[0]?.trim() ?? "";
|
|
1465
|
+
return first.replace(/^['"]|['"]$/g, "").toLowerCase();
|
|
1466
|
+
}
|
|
1467
|
+
function brandTrackedFontFamily(doc, stack) {
|
|
1468
|
+
if (!stack || stack.includes("var(")) return stack;
|
|
1469
|
+
const family = firstFamily(stack);
|
|
1470
|
+
if (!family) return stack;
|
|
1471
|
+
for (const name of FONT_TRACK_VARS) {
|
|
1472
|
+
const raw = readRootVar(doc, name);
|
|
1473
|
+
if (raw && firstFamily(raw) === family) return `var(${name}, ${stack})`;
|
|
1474
|
+
}
|
|
1475
|
+
return stack;
|
|
1476
|
+
}
|
|
1431
1477
|
var paintLockByEl = /* @__PURE__ */ new WeakMap();
|
|
1432
1478
|
var paintObserverByEl = /* @__PURE__ */ new WeakMap();
|
|
1433
1479
|
var paintingButton = false;
|
|
@@ -1454,7 +1500,7 @@ function paintButtonChrome(el, assignment, tokens) {
|
|
|
1454
1500
|
el.style.borderRadius = px(origin.size.borderRadius);
|
|
1455
1501
|
el.style.gap = geometry.gap;
|
|
1456
1502
|
el.style.height = px(geometry.height);
|
|
1457
|
-
el.style.fontFamily = typography.fontFamily;
|
|
1503
|
+
el.style.fontFamily = brandTrackedFontFamily(el.ownerDocument, typography.fontFamily);
|
|
1458
1504
|
el.style.fontWeight = typography.fontWeight;
|
|
1459
1505
|
el.style.letterSpacing = typography.letterSpacing;
|
|
1460
1506
|
el.style.textTransform = typography.textTransform;
|
|
@@ -1483,9 +1529,18 @@ function paintedTokensOf(el) {
|
|
|
1483
1529
|
return paintLockByEl.get(el)?.tokens ?? null;
|
|
1484
1530
|
}
|
|
1485
1531
|
function applyButtonStyle(el, assignment, tokens) {
|
|
1486
|
-
|
|
1532
|
+
const doc = el.ownerDocument;
|
|
1533
|
+
const tracked = {
|
|
1534
|
+
...tokens,
|
|
1535
|
+
variants: {
|
|
1536
|
+
primary: brandTrackedChrome(doc, tokens.variants.primary),
|
|
1537
|
+
secondary: brandTrackedChrome(doc, tokens.variants.secondary),
|
|
1538
|
+
ghost: brandTrackedChrome(doc, tokens.variants.ghost)
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
paintLockByEl.set(el, { assignment, tokens: tracked });
|
|
1487
1542
|
paintingButton = true;
|
|
1488
|
-
paintButtonChrome(el, assignment,
|
|
1543
|
+
paintButtonChrome(el, assignment, tracked);
|
|
1489
1544
|
paintingButton = false;
|
|
1490
1545
|
guardTemplateHover(el);
|
|
1491
1546
|
}
|
|
@@ -4939,10 +4994,9 @@ function getLinkHref3(el) {
|
|
|
4939
4994
|
}
|
|
4940
4995
|
function collectEditableNodes(extraContent, root = document) {
|
|
4941
4996
|
const isScoped = root !== document;
|
|
4942
|
-
const
|
|
4943
|
-
|
|
4944
|
-
)
|
|
4945
|
-
if (isScoped && root instanceof HTMLElement && root.matches('[data-ohw-editable]:not([data-ohw-editable="form"])')) {
|
|
4997
|
+
const EDITABLE_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="form"]):not([data-ai-icon])';
|
|
4998
|
+
const editableEls = Array.from(root.querySelectorAll(EDITABLE_SELECTOR));
|
|
4999
|
+
if (isScoped && root instanceof HTMLElement && root.matches(EDITABLE_SELECTOR)) {
|
|
4946
5000
|
editableEls.unshift(root);
|
|
4947
5001
|
}
|
|
4948
5002
|
const nodes = editableEls.map((el) => {
|
|
@@ -6209,6 +6263,281 @@ function reapRemovedAiSections(state, store, removedIds, excludeIds = /* @__PURE
|
|
|
6209
6263
|
return { state: nextState, store: nextStore, reapedIds, slotPrefixes, changed: true };
|
|
6210
6264
|
}
|
|
6211
6265
|
|
|
6266
|
+
// src/lib/ai-pricing-tree.ts
|
|
6267
|
+
var PRICING_MIN_CARDS = 1;
|
|
6268
|
+
var PRICING_MAX_CARDS = 4;
|
|
6269
|
+
function isPricingCard(node) {
|
|
6270
|
+
return typeof node === "object" && node !== null && node.type === "card" && node.slots?.preset === "pricing";
|
|
6271
|
+
}
|
|
6272
|
+
function isPricingCollection(node) {
|
|
6273
|
+
return node.type === "collection" && Array.isArray(node.children) && node.children.length > 0 && isPricingCard(node.children[0]);
|
|
6274
|
+
}
|
|
6275
|
+
function findCollection(node) {
|
|
6276
|
+
if (isPricingCollection(node)) return node;
|
|
6277
|
+
for (const child of node.children ?? []) {
|
|
6278
|
+
const found = findCollection(child);
|
|
6279
|
+
if (found) return found;
|
|
6280
|
+
}
|
|
6281
|
+
return null;
|
|
6282
|
+
}
|
|
6283
|
+
function findPricingCollection(tree) {
|
|
6284
|
+
for (const row of tree.rows ?? []) {
|
|
6285
|
+
for (const block of row.blocks ?? []) {
|
|
6286
|
+
const found = findCollection(block);
|
|
6287
|
+
if (found) return found;
|
|
6288
|
+
}
|
|
6289
|
+
}
|
|
6290
|
+
return null;
|
|
6291
|
+
}
|
|
6292
|
+
function pricingCardCount(tree) {
|
|
6293
|
+
return findPricingCollection(tree)?.children?.length ?? 0;
|
|
6294
|
+
}
|
|
6295
|
+
function withPricingCollection(tree, mutate) {
|
|
6296
|
+
const next = JSON.parse(JSON.stringify(tree));
|
|
6297
|
+
const collection = findPricingCollection(next);
|
|
6298
|
+
if (!collection || !collection.children) return null;
|
|
6299
|
+
for (const card of collection.children) {
|
|
6300
|
+
if (isPricingCard(card)) upgradeLegacyPricingSlots(card);
|
|
6301
|
+
}
|
|
6302
|
+
return mutate(collection) ? next : null;
|
|
6303
|
+
}
|
|
6304
|
+
function upgradeLegacyPricingSlots(card) {
|
|
6305
|
+
const slots = card.slots = card.slots ?? {};
|
|
6306
|
+
if (typeof slots.features === "string") {
|
|
6307
|
+
const rows = pricingFeatureList(card);
|
|
6308
|
+
if (rows.length > 0) slots.features = rows.slice(0, PRICING_FEATURES_MAX);
|
|
6309
|
+
}
|
|
6310
|
+
if (slots.featured === true) slots.featured = { enabled: true };
|
|
6311
|
+
else if (slots.featured === false) delete slots.featured;
|
|
6312
|
+
}
|
|
6313
|
+
function addPricingTier(tree) {
|
|
6314
|
+
return withPricingCollection(tree, (collection) => {
|
|
6315
|
+
const children = collection.children;
|
|
6316
|
+
if (children.length >= PRICING_MAX_CARDS) return false;
|
|
6317
|
+
const clone = JSON.parse(JSON.stringify(children[children.length - 1]));
|
|
6318
|
+
clone.slots = clone.slots ?? {};
|
|
6319
|
+
delete clone.slots.featured;
|
|
6320
|
+
clone.slots.heading = "New tier";
|
|
6321
|
+
children.push(clone);
|
|
6322
|
+
collection.slots = { ...collection.slots, itemsPerRow: children.length };
|
|
6323
|
+
return true;
|
|
6324
|
+
});
|
|
6325
|
+
}
|
|
6326
|
+
function removePricingTier(tree, index) {
|
|
6327
|
+
return withPricingCollection(tree, (collection) => {
|
|
6328
|
+
const children = collection.children;
|
|
6329
|
+
if (children.length <= PRICING_MIN_CARDS) return false;
|
|
6330
|
+
if (index < 0 || index >= children.length) return false;
|
|
6331
|
+
children.splice(index, 1);
|
|
6332
|
+
collection.slots = { ...collection.slots, itemsPerRow: children.length };
|
|
6333
|
+
return true;
|
|
6334
|
+
});
|
|
6335
|
+
}
|
|
6336
|
+
var PRICING_CARD_STYLES = [
|
|
6337
|
+
{ value: "outline", label: "Outline", treatment: null },
|
|
6338
|
+
{ value: "lift", label: "Lift", treatment: "lifted" },
|
|
6339
|
+
{ value: "invert", label: "Invert", treatment: "inverted" },
|
|
6340
|
+
{ value: "header", label: "Header", treatment: "accent-header" }
|
|
6341
|
+
];
|
|
6342
|
+
var DEFAULT_FEATURED_BADGE = "Most popular";
|
|
6343
|
+
function featuredOf(target) {
|
|
6344
|
+
const raw = target?.slots?.featured;
|
|
6345
|
+
if (raw === true) return { enabled: true, treatment: "outlined" };
|
|
6346
|
+
return typeof raw === "object" && raw !== null ? raw : {};
|
|
6347
|
+
}
|
|
6348
|
+
function badgeOf(featured) {
|
|
6349
|
+
return typeof featured.badge === "string" ? featured.badge.trim() : "";
|
|
6350
|
+
}
|
|
6351
|
+
function pricingCardStyle(tree, index) {
|
|
6352
|
+
const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
|
|
6353
|
+
if (featured.enabled !== true) return "outline";
|
|
6354
|
+
return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
|
|
6355
|
+
}
|
|
6356
|
+
function pricingCardFeaturedTag(tree, index) {
|
|
6357
|
+
const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
|
|
6358
|
+
return featured.enabled === true && badgeOf(featured).length > 0;
|
|
6359
|
+
}
|
|
6360
|
+
function composeFeatured(style, tagOn, badgeText) {
|
|
6361
|
+
const treatment = PRICING_CARD_STYLES.find((s) => s.value === style)?.treatment ?? null;
|
|
6362
|
+
if (tagOn) return { enabled: true, badge: badgeText || DEFAULT_FEATURED_BADGE, treatment: treatment ?? "outlined" };
|
|
6363
|
+
if (treatment) return { enabled: true, treatment };
|
|
6364
|
+
return badgeText.length > 0 ? { enabled: false, badge: badgeText } : { enabled: false };
|
|
6365
|
+
}
|
|
6366
|
+
function withPricingCard(tree, index, mutate) {
|
|
6367
|
+
return withPricingCollection(tree, (collection) => {
|
|
6368
|
+
const children = collection.children;
|
|
6369
|
+
if (index < 0 || index >= children.length) return false;
|
|
6370
|
+
const target = children[index];
|
|
6371
|
+
target.slots = target.slots ?? {};
|
|
6372
|
+
mutate(target);
|
|
6373
|
+
return true;
|
|
6374
|
+
});
|
|
6375
|
+
}
|
|
6376
|
+
function setPricingCardStyle(tree, index, style) {
|
|
6377
|
+
if (!PRICING_CARD_STYLES.some((s) => s.value === style)) return null;
|
|
6378
|
+
return withPricingCard(tree, index, (target) => {
|
|
6379
|
+
const prev = featuredOf(target);
|
|
6380
|
+
const tagOn = prev.enabled === true && badgeOf(prev).length > 0;
|
|
6381
|
+
target.slots.featured = composeFeatured(style, tagOn, badgeOf(prev));
|
|
6382
|
+
});
|
|
6383
|
+
}
|
|
6384
|
+
function setPricingCardFeaturedTag(tree, index, on) {
|
|
6385
|
+
return withPricingCollection(tree, (collection) => {
|
|
6386
|
+
const children = collection.children;
|
|
6387
|
+
if (index < 0 || index >= children.length) return false;
|
|
6388
|
+
if (on) {
|
|
6389
|
+
children.forEach((card, i) => {
|
|
6390
|
+
if (i === index) return;
|
|
6391
|
+
const prev2 = featuredOf(card);
|
|
6392
|
+
if (prev2.enabled !== true || badgeOf(prev2).length === 0) return;
|
|
6393
|
+
card.slots = card.slots ?? {};
|
|
6394
|
+
card.slots.featured = composeFeatured(pricingCardStyleOf(prev2), false, badgeOf(prev2));
|
|
6395
|
+
});
|
|
6396
|
+
}
|
|
6397
|
+
const target = children[index];
|
|
6398
|
+
target.slots = target.slots ?? {};
|
|
6399
|
+
const prev = featuredOf(target);
|
|
6400
|
+
target.slots.featured = composeFeatured(pricingCardStyleOf(prev), on, badgeOf(prev));
|
|
6401
|
+
return true;
|
|
6402
|
+
});
|
|
6403
|
+
}
|
|
6404
|
+
function pricingCardStyleOf(featured) {
|
|
6405
|
+
if (featured.enabled !== true) return "outline";
|
|
6406
|
+
return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
|
|
6407
|
+
}
|
|
6408
|
+
function addPricingCardButton(tree, index, label) {
|
|
6409
|
+
return withPricingCollection(tree, (collection) => {
|
|
6410
|
+
const children = collection.children;
|
|
6411
|
+
if (index < 0 || index >= children.length) return false;
|
|
6412
|
+
const target = children[index];
|
|
6413
|
+
const buttons = (target.children ?? []).filter((c) => c.type === "button");
|
|
6414
|
+
if (buttons.length >= 2) return false;
|
|
6415
|
+
const hasPrimary = buttons.some((b) => b.slots?.variant === "primary");
|
|
6416
|
+
target.children = [
|
|
6417
|
+
...target.children ?? [],
|
|
6418
|
+
{ type: "button", span: 12, slots: { label, href: "#", variant: hasPrimary ? "secondary" : "primary" } }
|
|
6419
|
+
];
|
|
6420
|
+
return true;
|
|
6421
|
+
});
|
|
6422
|
+
}
|
|
6423
|
+
function removePricingCardButton(tree, index) {
|
|
6424
|
+
return withPricingCollection(tree, (collection) => {
|
|
6425
|
+
const children = collection.children;
|
|
6426
|
+
if (index < 0 || index >= children.length) return false;
|
|
6427
|
+
const target = children[index];
|
|
6428
|
+
const buttons = (target.children ?? []).filter((c) => c.type === "button");
|
|
6429
|
+
if (buttons.length === 0) return false;
|
|
6430
|
+
const lastButton = buttons[buttons.length - 1];
|
|
6431
|
+
target.children = (target.children ?? []).filter((c) => c !== lastButton);
|
|
6432
|
+
return true;
|
|
6433
|
+
});
|
|
6434
|
+
}
|
|
6435
|
+
var PRICING_FEATURES_MIN = 1;
|
|
6436
|
+
var PRICING_FEATURES_MAX = 10;
|
|
6437
|
+
var PRICING_FEATURE_ICONS = [
|
|
6438
|
+
"check",
|
|
6439
|
+
"check-circle",
|
|
6440
|
+
"x",
|
|
6441
|
+
"star",
|
|
6442
|
+
"zap",
|
|
6443
|
+
"shield",
|
|
6444
|
+
"clock",
|
|
6445
|
+
"users",
|
|
6446
|
+
"infinity"
|
|
6447
|
+
];
|
|
6448
|
+
function pricingFeatureList(target) {
|
|
6449
|
+
const raw = target.slots?.features;
|
|
6450
|
+
if (Array.isArray(raw)) {
|
|
6451
|
+
return raw.filter((item) => !!item && typeof item === "object" && typeof item.text === "string").map((item) => ({
|
|
6452
|
+
text: item.text,
|
|
6453
|
+
...typeof item.icon === "string" ? { icon: item.icon } : {},
|
|
6454
|
+
...typeof item.iconColor === "string" ? { iconColor: item.iconColor } : {}
|
|
6455
|
+
}));
|
|
6456
|
+
}
|
|
6457
|
+
if (typeof raw === "string") {
|
|
6458
|
+
return raw.split(/\n|<br\s*\/?>/i).map((line) => line.replace(/<[^>]+>/g, "").replace(/^[-*•]\s*/, "").trim().slice(0, 120)).filter(Boolean).map((text) => ({ text }));
|
|
6459
|
+
}
|
|
6460
|
+
return [];
|
|
6461
|
+
}
|
|
6462
|
+
function pricingCardFeatureCount(tree, index) {
|
|
6463
|
+
const target = findPricingCollection(tree)?.children?.[index];
|
|
6464
|
+
return target ? pricingFeatureList(target).length : 0;
|
|
6465
|
+
}
|
|
6466
|
+
function addPricingCardFeature(tree, index, text) {
|
|
6467
|
+
return withPricingCollection(tree, (collection) => {
|
|
6468
|
+
const children = collection.children;
|
|
6469
|
+
if (index < 0 || index >= children.length) return false;
|
|
6470
|
+
const target = children[index];
|
|
6471
|
+
const features = pricingFeatureList(target);
|
|
6472
|
+
if (features.length >= PRICING_FEATURES_MAX) return false;
|
|
6473
|
+
target.slots = target.slots ?? {};
|
|
6474
|
+
target.slots.features = [...features, { text }];
|
|
6475
|
+
return true;
|
|
6476
|
+
});
|
|
6477
|
+
}
|
|
6478
|
+
function setPricingCardFeatureIcon(tree, index, featureIndex, icon) {
|
|
6479
|
+
if (!PRICING_FEATURE_ICONS.includes(icon)) return null;
|
|
6480
|
+
return withPricingCollection(tree, (collection) => {
|
|
6481
|
+
const children = collection.children;
|
|
6482
|
+
if (index < 0 || index >= children.length) return false;
|
|
6483
|
+
const target = children[index];
|
|
6484
|
+
const features = pricingFeatureList(target);
|
|
6485
|
+
if (featureIndex < 0 || featureIndex >= features.length) return false;
|
|
6486
|
+
target.slots = target.slots ?? {};
|
|
6487
|
+
target.slots.features = features.map((row, i) => i === featureIndex ? { ...row, icon } : row);
|
|
6488
|
+
return true;
|
|
6489
|
+
});
|
|
6490
|
+
}
|
|
6491
|
+
function removePricingCardFeature(tree, index) {
|
|
6492
|
+
return withPricingCollection(tree, (collection) => {
|
|
6493
|
+
const children = collection.children;
|
|
6494
|
+
if (index < 0 || index >= children.length) return false;
|
|
6495
|
+
const target = children[index];
|
|
6496
|
+
const features = pricingFeatureList(target);
|
|
6497
|
+
if (features.length <= PRICING_FEATURES_MIN) return false;
|
|
6498
|
+
target.slots = target.slots ?? {};
|
|
6499
|
+
target.slots.features = features.slice(0, -1);
|
|
6500
|
+
return true;
|
|
6501
|
+
});
|
|
6502
|
+
}
|
|
6503
|
+
|
|
6504
|
+
// src/lib/ai-icon-edit.ts
|
|
6505
|
+
var FEATURE_ICON_PATH = /\.c(\d+)\.features\.(\d+)\.icon$/;
|
|
6506
|
+
var NODE_SEGMENT = /^[rbc]\d+$/;
|
|
6507
|
+
function nodeAtPath(tree, segments) {
|
|
6508
|
+
if (segments.length < 2 || segments.some((s) => !NODE_SEGMENT.test(s))) return null;
|
|
6509
|
+
if (!segments[0].startsWith("r") || !segments[1].startsWith("b")) return null;
|
|
6510
|
+
let node = tree.rows?.[Number(segments[0].slice(1))]?.blocks?.[Number(segments[1].slice(1))];
|
|
6511
|
+
for (const segment of segments.slice(2)) {
|
|
6512
|
+
if (!segment.startsWith("c")) return null;
|
|
6513
|
+
node = node?.children?.[Number(segment.slice(1))];
|
|
6514
|
+
}
|
|
6515
|
+
return node ?? null;
|
|
6516
|
+
}
|
|
6517
|
+
function applyIconNameToTree(tree, path, icon) {
|
|
6518
|
+
const feature = path.match(FEATURE_ICON_PATH);
|
|
6519
|
+
if (feature) {
|
|
6520
|
+
return setPricingCardFeatureIcon(tree, Number(feature[1]), Number(feature[2]), icon);
|
|
6521
|
+
}
|
|
6522
|
+
return rewriteIconSlot(tree, path, `lucide:${icon}`);
|
|
6523
|
+
}
|
|
6524
|
+
function applyIconUploadToTree(tree, path, mediaRef) {
|
|
6525
|
+
if (FEATURE_ICON_PATH.test(path)) return null;
|
|
6526
|
+
return rewriteIconSlot(tree, path, `icon:${mediaRef}`);
|
|
6527
|
+
}
|
|
6528
|
+
function rewriteIconSlot(tree, path, ref) {
|
|
6529
|
+
const segments = path.split(".");
|
|
6530
|
+
const slot = segments.pop();
|
|
6531
|
+
if (!slot || NODE_SEGMENT.test(slot)) return null;
|
|
6532
|
+
const next = JSON.parse(JSON.stringify(tree));
|
|
6533
|
+
const node = nodeAtPath(next, segments);
|
|
6534
|
+
if (!node) return null;
|
|
6535
|
+
const current = node.slots?.[slot];
|
|
6536
|
+
if (typeof current !== "string" || !/^(lucide|simple|icon):/.test(current)) return null;
|
|
6537
|
+
node.slots = { ...node.slots, [slot]: ref };
|
|
6538
|
+
return next;
|
|
6539
|
+
}
|
|
6540
|
+
|
|
6212
6541
|
// src/lib/ai-footer-tree.ts
|
|
6213
6542
|
var FOOTER_COLUMN_SPANS = [12, 6, 4, 3];
|
|
6214
6543
|
var FOOTER_COLUMN_BLOCKS = [
|
|
@@ -6692,8 +7021,8 @@ function applyBrandToDom(kit) {
|
|
|
6692
7021
|
if (kit.fonts) {
|
|
6693
7022
|
appliedFonts = applyFonts ? kit.fonts : null;
|
|
6694
7023
|
}
|
|
7024
|
+
for (const name of BUTTON_VAR_NAMES) root.style.removeProperty(name);
|
|
6695
7025
|
if (kit.buttons) {
|
|
6696
|
-
for (const name of BUTTON_VAR_NAMES) root.style.removeProperty(name);
|
|
6697
7026
|
for (const [name, value] of Object.entries(buttonStyleVars({ ...kit, buttons: kit.buttons }))) root.style.setProperty(name, value);
|
|
6698
7027
|
}
|
|
6699
7028
|
syncButtonRules(kit.buttons ? kit : null);
|
|
@@ -7565,7 +7894,7 @@ function MediaBox({
|
|
|
7565
7894
|
onError
|
|
7566
7895
|
}) {
|
|
7567
7896
|
const url = refValue ? ctx.resolveMedia(refValue) : null;
|
|
7568
|
-
const isIcon = /^(lucide|simple):/.test(refValue);
|
|
7897
|
+
const isIcon = /^(lucide|simple|icon):/.test(refValue);
|
|
7569
7898
|
const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
|
|
7570
7899
|
const editAttrs = ctx.keyFor && editPath && !isIcon ? {
|
|
7571
7900
|
"data-ohw-key": ctx.keyFor(editPath),
|
|
@@ -7573,10 +7902,13 @@ function MediaBox({
|
|
|
7573
7902
|
} : {};
|
|
7574
7903
|
if (isIcon) {
|
|
7575
7904
|
const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
|
|
7905
|
+
const uploadedIconUrl = refValue.startsWith("icon:") ? ctx.resolveMedia(refValue.slice(5)) : null;
|
|
7906
|
+
const iconEditAttrs = ctx.keyFor && editPath && genericImageEdit ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "icon" } : {};
|
|
7576
7907
|
return /* @__PURE__ */ jsx(
|
|
7577
7908
|
"span",
|
|
7578
7909
|
{
|
|
7579
7910
|
"data-ai-icon": refValue,
|
|
7911
|
+
...iconEditAttrs,
|
|
7580
7912
|
style: {
|
|
7581
7913
|
display: "inline-flex",
|
|
7582
7914
|
width: 48,
|
|
@@ -7585,7 +7917,7 @@ function MediaBox({
|
|
|
7585
7917
|
justifyContent: "center",
|
|
7586
7918
|
color: ctx.brand.palette.dark
|
|
7587
7919
|
},
|
|
7588
|
-
children: Icon ? /* @__PURE__ */ jsx(Icon, { size: 48, color: ctx.brand.palette.dark, strokeWidth: 1.5 }) : /* @__PURE__ */ jsx("span", { style: { fontSize: 24 }, children: "\u25C6" })
|
|
7920
|
+
children: uploadedIconUrl ? /* @__PURE__ */ jsx("img", { src: uploadedIconUrl, alt: "", style: { width: 48, height: 48, objectFit: "contain" } }) : Icon ? /* @__PURE__ */ jsx(Icon, { size: 48, color: ctx.brand.palette.dark, strokeWidth: 1.5 }) : /* @__PURE__ */ jsx("span", { style: { fontSize: 24 }, children: "\u25C6" })
|
|
7589
7921
|
}
|
|
7590
7922
|
);
|
|
7591
7923
|
}
|
|
@@ -8073,8 +8405,20 @@ function PricingCard({ node, ctx, path }) {
|
|
|
8073
8405
|
/* @__PURE__ */ 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) => {
|
|
8074
8406
|
const Icon = lucideByName(item.icon || "check");
|
|
8075
8407
|
const iconColor = pricingIconColor(item.iconColor, ctx.brand, fg);
|
|
8408
|
+
const iconAttrs = ctx.keyFor && !item.html ? {
|
|
8409
|
+
"data-ohw-key": ctx.keyFor(`${path}.features.${i}.icon`),
|
|
8410
|
+
"data-ohw-editable": "icon"
|
|
8411
|
+
} : {};
|
|
8076
8412
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "flex-start", gap: AI_TREE_TOKENS.spacing3 }, children: [
|
|
8077
|
-
Icon && /* @__PURE__ */ jsx(
|
|
8413
|
+
Icon && /* @__PURE__ */ jsx(
|
|
8414
|
+
"span",
|
|
8415
|
+
{
|
|
8416
|
+
"data-ai-icon": `lucide:${item.icon || "check"}`,
|
|
8417
|
+
...iconAttrs,
|
|
8418
|
+
style: { display: "inline-flex", flexShrink: 0 },
|
|
8419
|
+
children: /* @__PURE__ */ jsx(Icon, { size: 20, color: iconColor })
|
|
8420
|
+
}
|
|
8421
|
+
),
|
|
8078
8422
|
item.html ? /* @__PURE__ */ jsx(
|
|
8079
8423
|
"span",
|
|
8080
8424
|
{
|
|
@@ -10079,7 +10423,7 @@ function isChromeSection2(el) {
|
|
|
10079
10423
|
if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) return true;
|
|
10080
10424
|
return el.tagName === "HEADER" || el.tagName === "FOOTER";
|
|
10081
10425
|
}
|
|
10082
|
-
function
|
|
10426
|
+
function readRootVar2(name) {
|
|
10083
10427
|
if (typeof document === "undefined") return "";
|
|
10084
10428
|
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
10085
10429
|
}
|
|
@@ -10119,14 +10463,14 @@ function humanizeFontFamily(name) {
|
|
|
10119
10463
|
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());
|
|
10120
10464
|
}
|
|
10121
10465
|
function readFontVar(name) {
|
|
10122
|
-
return humanizeFontFamily(primaryFontFamily(
|
|
10466
|
+
return humanizeFontFamily(primaryFontFamily(readRootVar2(name)));
|
|
10123
10467
|
}
|
|
10124
10468
|
function deriveBrandOverride() {
|
|
10125
|
-
const dark =
|
|
10126
|
-
const primary =
|
|
10127
|
-
const light =
|
|
10469
|
+
const dark = readRootVar2("--ohw-brand-dark");
|
|
10470
|
+
const primary = readRootVar2("--ohw-brand-primary");
|
|
10471
|
+
const light = readRootVar2("--ohw-brand-light");
|
|
10128
10472
|
if (!dark || !primary || !light) return null;
|
|
10129
|
-
const accent =
|
|
10473
|
+
const accent = readRootVar2("--ohw-brand-accent");
|
|
10130
10474
|
const heading = readFontVar("--font-heading") || readFontVar("--font-display");
|
|
10131
10475
|
const body = readFontVar("--font-body");
|
|
10132
10476
|
return {
|
|
@@ -10138,11 +10482,11 @@ function deriveBrandOverride() {
|
|
|
10138
10482
|
};
|
|
10139
10483
|
}
|
|
10140
10484
|
function deriveTemplateBrandLive() {
|
|
10141
|
-
const primary =
|
|
10142
|
-
const dark =
|
|
10143
|
-
const light =
|
|
10485
|
+
const primary = readRootVar2("--brand-primary") || readRootVar2("--color-primary");
|
|
10486
|
+
const dark = readRootVar2("--brand-text") || readRootVar2("--color-dark");
|
|
10487
|
+
const light = readRootVar2("--brand-background") || readRootVar2("--color-light");
|
|
10144
10488
|
if (!dark || !primary || !light) return null;
|
|
10145
|
-
const accent =
|
|
10489
|
+
const accent = readRootVar2("--brand-accent") || readRootVar2("--color-accent");
|
|
10146
10490
|
const heading = readFontVar("--brand-font-heading") || readFontVar("--font-heading") || readFontVar("--font-display");
|
|
10147
10491
|
const body = readFontVar("--brand-font-body") || readFontVar("--font-body");
|
|
10148
10492
|
return {
|
|
@@ -18317,222 +18661,6 @@ function AiMediaStripOverlay({
|
|
|
18317
18661
|
// src/ui/ai-section/AiPricingCardOverlay.tsx
|
|
18318
18662
|
import { useCallback as useCallback4, useEffect as useEffect7, useLayoutEffect as useLayoutEffect4, useRef as useRef6, useState as useState9 } from "react";
|
|
18319
18663
|
import { Check as Check2, ChevronDown as ChevronDown3, Minus, Plus as Plus2, Star as Star2 } from "lucide-react";
|
|
18320
|
-
|
|
18321
|
-
// src/lib/ai-pricing-tree.ts
|
|
18322
|
-
var PRICING_MIN_CARDS = 1;
|
|
18323
|
-
var PRICING_MAX_CARDS = 4;
|
|
18324
|
-
function isPricingCard(node) {
|
|
18325
|
-
return typeof node === "object" && node !== null && node.type === "card" && node.slots?.preset === "pricing";
|
|
18326
|
-
}
|
|
18327
|
-
function isPricingCollection(node) {
|
|
18328
|
-
return node.type === "collection" && Array.isArray(node.children) && node.children.length > 0 && isPricingCard(node.children[0]);
|
|
18329
|
-
}
|
|
18330
|
-
function findCollection(node) {
|
|
18331
|
-
if (isPricingCollection(node)) return node;
|
|
18332
|
-
for (const child of node.children ?? []) {
|
|
18333
|
-
const found = findCollection(child);
|
|
18334
|
-
if (found) return found;
|
|
18335
|
-
}
|
|
18336
|
-
return null;
|
|
18337
|
-
}
|
|
18338
|
-
function findPricingCollection(tree) {
|
|
18339
|
-
for (const row of tree.rows ?? []) {
|
|
18340
|
-
for (const block of row.blocks ?? []) {
|
|
18341
|
-
const found = findCollection(block);
|
|
18342
|
-
if (found) return found;
|
|
18343
|
-
}
|
|
18344
|
-
}
|
|
18345
|
-
return null;
|
|
18346
|
-
}
|
|
18347
|
-
function pricingCardCount(tree) {
|
|
18348
|
-
return findPricingCollection(tree)?.children?.length ?? 0;
|
|
18349
|
-
}
|
|
18350
|
-
function withPricingCollection(tree, mutate) {
|
|
18351
|
-
const next = JSON.parse(JSON.stringify(tree));
|
|
18352
|
-
const collection = findPricingCollection(next);
|
|
18353
|
-
if (!collection || !collection.children) return null;
|
|
18354
|
-
for (const card of collection.children) {
|
|
18355
|
-
if (isPricingCard(card)) upgradeLegacyPricingSlots(card);
|
|
18356
|
-
}
|
|
18357
|
-
return mutate(collection) ? next : null;
|
|
18358
|
-
}
|
|
18359
|
-
function upgradeLegacyPricingSlots(card) {
|
|
18360
|
-
const slots = card.slots = card.slots ?? {};
|
|
18361
|
-
if (typeof slots.features === "string") {
|
|
18362
|
-
const rows = pricingFeatureList(card);
|
|
18363
|
-
if (rows.length > 0) slots.features = rows.slice(0, PRICING_FEATURES_MAX);
|
|
18364
|
-
}
|
|
18365
|
-
if (slots.featured === true) slots.featured = { enabled: true };
|
|
18366
|
-
else if (slots.featured === false) delete slots.featured;
|
|
18367
|
-
}
|
|
18368
|
-
function addPricingTier(tree) {
|
|
18369
|
-
return withPricingCollection(tree, (collection) => {
|
|
18370
|
-
const children = collection.children;
|
|
18371
|
-
if (children.length >= PRICING_MAX_CARDS) return false;
|
|
18372
|
-
const clone = JSON.parse(JSON.stringify(children[children.length - 1]));
|
|
18373
|
-
clone.slots = clone.slots ?? {};
|
|
18374
|
-
delete clone.slots.featured;
|
|
18375
|
-
clone.slots.heading = "New tier";
|
|
18376
|
-
children.push(clone);
|
|
18377
|
-
collection.slots = { ...collection.slots, itemsPerRow: children.length };
|
|
18378
|
-
return true;
|
|
18379
|
-
});
|
|
18380
|
-
}
|
|
18381
|
-
function removePricingTier(tree, index) {
|
|
18382
|
-
return withPricingCollection(tree, (collection) => {
|
|
18383
|
-
const children = collection.children;
|
|
18384
|
-
if (children.length <= PRICING_MIN_CARDS) return false;
|
|
18385
|
-
if (index < 0 || index >= children.length) return false;
|
|
18386
|
-
children.splice(index, 1);
|
|
18387
|
-
collection.slots = { ...collection.slots, itemsPerRow: children.length };
|
|
18388
|
-
return true;
|
|
18389
|
-
});
|
|
18390
|
-
}
|
|
18391
|
-
var PRICING_CARD_STYLES = [
|
|
18392
|
-
{ value: "outline", label: "Outline", treatment: null },
|
|
18393
|
-
{ value: "lift", label: "Lift", treatment: "lifted" },
|
|
18394
|
-
{ value: "invert", label: "Invert", treatment: "inverted" },
|
|
18395
|
-
{ value: "header", label: "Header", treatment: "accent-header" }
|
|
18396
|
-
];
|
|
18397
|
-
var DEFAULT_FEATURED_BADGE = "Most popular";
|
|
18398
|
-
function featuredOf(target) {
|
|
18399
|
-
const raw = target?.slots?.featured;
|
|
18400
|
-
if (raw === true) return { enabled: true, treatment: "outlined" };
|
|
18401
|
-
return typeof raw === "object" && raw !== null ? raw : {};
|
|
18402
|
-
}
|
|
18403
|
-
function badgeOf(featured) {
|
|
18404
|
-
return typeof featured.badge === "string" ? featured.badge.trim() : "";
|
|
18405
|
-
}
|
|
18406
|
-
function pricingCardStyle(tree, index) {
|
|
18407
|
-
const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
|
|
18408
|
-
if (featured.enabled !== true) return "outline";
|
|
18409
|
-
return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
|
|
18410
|
-
}
|
|
18411
|
-
function pricingCardFeaturedTag(tree, index) {
|
|
18412
|
-
const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
|
|
18413
|
-
return featured.enabled === true && badgeOf(featured).length > 0;
|
|
18414
|
-
}
|
|
18415
|
-
function composeFeatured(style, tagOn, badgeText) {
|
|
18416
|
-
const treatment = PRICING_CARD_STYLES.find((s) => s.value === style)?.treatment ?? null;
|
|
18417
|
-
if (tagOn) return { enabled: true, badge: badgeText || DEFAULT_FEATURED_BADGE, treatment: treatment ?? "outlined" };
|
|
18418
|
-
if (treatment) return { enabled: true, treatment };
|
|
18419
|
-
return badgeText.length > 0 ? { enabled: false, badge: badgeText } : { enabled: false };
|
|
18420
|
-
}
|
|
18421
|
-
function withPricingCard(tree, index, mutate) {
|
|
18422
|
-
return withPricingCollection(tree, (collection) => {
|
|
18423
|
-
const children = collection.children;
|
|
18424
|
-
if (index < 0 || index >= children.length) return false;
|
|
18425
|
-
const target = children[index];
|
|
18426
|
-
target.slots = target.slots ?? {};
|
|
18427
|
-
mutate(target);
|
|
18428
|
-
return true;
|
|
18429
|
-
});
|
|
18430
|
-
}
|
|
18431
|
-
function setPricingCardStyle(tree, index, style) {
|
|
18432
|
-
if (!PRICING_CARD_STYLES.some((s) => s.value === style)) return null;
|
|
18433
|
-
return withPricingCard(tree, index, (target) => {
|
|
18434
|
-
const prev = featuredOf(target);
|
|
18435
|
-
const tagOn = prev.enabled === true && badgeOf(prev).length > 0;
|
|
18436
|
-
target.slots.featured = composeFeatured(style, tagOn, badgeOf(prev));
|
|
18437
|
-
});
|
|
18438
|
-
}
|
|
18439
|
-
function setPricingCardFeaturedTag(tree, index, on) {
|
|
18440
|
-
return withPricingCollection(tree, (collection) => {
|
|
18441
|
-
const children = collection.children;
|
|
18442
|
-
if (index < 0 || index >= children.length) return false;
|
|
18443
|
-
if (on) {
|
|
18444
|
-
children.forEach((card, i) => {
|
|
18445
|
-
if (i === index) return;
|
|
18446
|
-
const prev2 = featuredOf(card);
|
|
18447
|
-
if (prev2.enabled !== true || badgeOf(prev2).length === 0) return;
|
|
18448
|
-
card.slots = card.slots ?? {};
|
|
18449
|
-
card.slots.featured = composeFeatured(pricingCardStyleOf(prev2), false, badgeOf(prev2));
|
|
18450
|
-
});
|
|
18451
|
-
}
|
|
18452
|
-
const target = children[index];
|
|
18453
|
-
target.slots = target.slots ?? {};
|
|
18454
|
-
const prev = featuredOf(target);
|
|
18455
|
-
target.slots.featured = composeFeatured(pricingCardStyleOf(prev), on, badgeOf(prev));
|
|
18456
|
-
return true;
|
|
18457
|
-
});
|
|
18458
|
-
}
|
|
18459
|
-
function pricingCardStyleOf(featured) {
|
|
18460
|
-
if (featured.enabled !== true) return "outline";
|
|
18461
|
-
return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
|
|
18462
|
-
}
|
|
18463
|
-
function addPricingCardButton(tree, index, label) {
|
|
18464
|
-
return withPricingCollection(tree, (collection) => {
|
|
18465
|
-
const children = collection.children;
|
|
18466
|
-
if (index < 0 || index >= children.length) return false;
|
|
18467
|
-
const target = children[index];
|
|
18468
|
-
const buttons = (target.children ?? []).filter((c) => c.type === "button");
|
|
18469
|
-
if (buttons.length >= 2) return false;
|
|
18470
|
-
const hasPrimary = buttons.some((b) => b.slots?.variant === "primary");
|
|
18471
|
-
target.children = [
|
|
18472
|
-
...target.children ?? [],
|
|
18473
|
-
{ type: "button", span: 12, slots: { label, href: "#", variant: hasPrimary ? "secondary" : "primary" } }
|
|
18474
|
-
];
|
|
18475
|
-
return true;
|
|
18476
|
-
});
|
|
18477
|
-
}
|
|
18478
|
-
function removePricingCardButton(tree, index) {
|
|
18479
|
-
return withPricingCollection(tree, (collection) => {
|
|
18480
|
-
const children = collection.children;
|
|
18481
|
-
if (index < 0 || index >= children.length) return false;
|
|
18482
|
-
const target = children[index];
|
|
18483
|
-
const buttons = (target.children ?? []).filter((c) => c.type === "button");
|
|
18484
|
-
if (buttons.length === 0) return false;
|
|
18485
|
-
const lastButton = buttons[buttons.length - 1];
|
|
18486
|
-
target.children = (target.children ?? []).filter((c) => c !== lastButton);
|
|
18487
|
-
return true;
|
|
18488
|
-
});
|
|
18489
|
-
}
|
|
18490
|
-
var PRICING_FEATURES_MIN = 1;
|
|
18491
|
-
var PRICING_FEATURES_MAX = 10;
|
|
18492
|
-
function pricingFeatureList(target) {
|
|
18493
|
-
const raw = target.slots?.features;
|
|
18494
|
-
if (Array.isArray(raw)) {
|
|
18495
|
-
return raw.filter((item) => !!item && typeof item === "object" && typeof item.text === "string").map((item) => ({
|
|
18496
|
-
text: item.text,
|
|
18497
|
-
...typeof item.icon === "string" ? { icon: item.icon } : {},
|
|
18498
|
-
...typeof item.iconColor === "string" ? { iconColor: item.iconColor } : {}
|
|
18499
|
-
}));
|
|
18500
|
-
}
|
|
18501
|
-
if (typeof raw === "string") {
|
|
18502
|
-
return raw.split(/\n|<br\s*\/?>/i).map((line) => line.replace(/<[^>]+>/g, "").replace(/^[-*•]\s*/, "").trim().slice(0, 120)).filter(Boolean).map((text) => ({ text }));
|
|
18503
|
-
}
|
|
18504
|
-
return [];
|
|
18505
|
-
}
|
|
18506
|
-
function pricingCardFeatureCount(tree, index) {
|
|
18507
|
-
const target = findPricingCollection(tree)?.children?.[index];
|
|
18508
|
-
return target ? pricingFeatureList(target).length : 0;
|
|
18509
|
-
}
|
|
18510
|
-
function addPricingCardFeature(tree, index, text) {
|
|
18511
|
-
return withPricingCollection(tree, (collection) => {
|
|
18512
|
-
const children = collection.children;
|
|
18513
|
-
if (index < 0 || index >= children.length) return false;
|
|
18514
|
-
const target = children[index];
|
|
18515
|
-
const features = pricingFeatureList(target);
|
|
18516
|
-
if (features.length >= PRICING_FEATURES_MAX) return false;
|
|
18517
|
-
target.slots = target.slots ?? {};
|
|
18518
|
-
target.slots.features = [...features, { text }];
|
|
18519
|
-
return true;
|
|
18520
|
-
});
|
|
18521
|
-
}
|
|
18522
|
-
function removePricingCardFeature(tree, index) {
|
|
18523
|
-
return withPricingCollection(tree, (collection) => {
|
|
18524
|
-
const children = collection.children;
|
|
18525
|
-
if (index < 0 || index >= children.length) return false;
|
|
18526
|
-
const target = children[index];
|
|
18527
|
-
const features = pricingFeatureList(target);
|
|
18528
|
-
if (features.length <= PRICING_FEATURES_MIN) return false;
|
|
18529
|
-
target.slots = target.slots ?? {};
|
|
18530
|
-
target.slots.features = features.slice(0, -1);
|
|
18531
|
-
return true;
|
|
18532
|
-
});
|
|
18533
|
-
}
|
|
18534
|
-
|
|
18535
|
-
// src/ui/ai-section/AiPricingCardOverlay.tsx
|
|
18536
18664
|
import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
18537
18665
|
var PRIMARY4 = "var(--ohw-primary, #0885FE)";
|
|
18538
18666
|
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]";
|
|
@@ -23664,6 +23792,23 @@ function currentIconRef(el) {
|
|
|
23664
23792
|
);
|
|
23665
23793
|
return named ? `lucide:${named.slice("lucide-".length)}` : "";
|
|
23666
23794
|
}
|
|
23795
|
+
function requestValidateTree5(postToParent2, tree) {
|
|
23796
|
+
return new Promise((resolve) => {
|
|
23797
|
+
const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
23798
|
+
const timeout = window.setTimeout(() => {
|
|
23799
|
+
window.removeEventListener("message", onMessage);
|
|
23800
|
+
resolve({ ok: false, reasons: ["Validation request timed out."] });
|
|
23801
|
+
}, 8e3);
|
|
23802
|
+
function onMessage(e) {
|
|
23803
|
+
if (e.data?.type !== "ow:ai-validate-tree-result" || e.data.id !== id) return;
|
|
23804
|
+
window.clearTimeout(timeout);
|
|
23805
|
+
window.removeEventListener("message", onMessage);
|
|
23806
|
+
resolve({ ok: e.data.ok === true, reasons: Array.isArray(e.data.reasons) ? e.data.reasons : [] });
|
|
23807
|
+
}
|
|
23808
|
+
window.addEventListener("message", onMessage);
|
|
23809
|
+
postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
|
|
23810
|
+
});
|
|
23811
|
+
}
|
|
23667
23812
|
function getEditMeasureEl(editable) {
|
|
23668
23813
|
return editable.closest("[data-ohw-href-key]") ?? editable;
|
|
23669
23814
|
}
|
|
@@ -28015,10 +28160,14 @@ function OhhwellsBridge() {
|
|
|
28015
28160
|
e.preventDefault();
|
|
28016
28161
|
e.stopPropagation();
|
|
28017
28162
|
aiSectionApiRef.current?.selectFromElement(editable);
|
|
28163
|
+
const aiIcon = editable.hasAttribute("data-ai-icon");
|
|
28164
|
+
const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
|
|
28018
28165
|
postToParentRef.current({
|
|
28019
28166
|
type: "ow:icon-pick",
|
|
28020
28167
|
key: editable.dataset.ohwKey ?? "",
|
|
28021
|
-
current: currentIconRef(editable)
|
|
28168
|
+
current: currentIconRef(editable),
|
|
28169
|
+
...aiIcon ? { iconMode: "name" } : {},
|
|
28170
|
+
...featureIcon ? { options: [...PRICING_FEATURE_ICONS] } : {}
|
|
28022
28171
|
});
|
|
28023
28172
|
return;
|
|
28024
28173
|
}
|
|
@@ -29265,15 +29414,43 @@ function OhhwellsBridge() {
|
|
|
29265
29414
|
if (e.data?.type !== "ow:icon-markup") return;
|
|
29266
29415
|
const { key, markup } = e.data;
|
|
29267
29416
|
if (!key || typeof markup !== "string") return;
|
|
29268
|
-
const targets =
|
|
29269
|
-
`[data-ohw-key="${key}"][data-ohw-editable="icon"]`
|
|
29270
|
-
);
|
|
29417
|
+
const targets = Array.from(
|
|
29418
|
+
document.querySelectorAll(`[data-ohw-key="${key}"][data-ohw-editable="icon"]`)
|
|
29419
|
+
).filter((el) => !el.hasAttribute("data-ai-icon"));
|
|
29271
29420
|
if (!targets.length) return;
|
|
29272
29421
|
targets.forEach((el) => {
|
|
29273
29422
|
applyIconMarkup(el, markup);
|
|
29274
29423
|
});
|
|
29275
29424
|
postToParentRef.current({ type: "ow:change", nodes: [{ key, text: markup }], flush: true });
|
|
29276
29425
|
};
|
|
29426
|
+
const handleIconName = (e) => {
|
|
29427
|
+
if (e.data?.type !== "ow:icon-name") return;
|
|
29428
|
+
const { key, icon, url } = e.data;
|
|
29429
|
+
const isName = typeof icon === "string" && /^[a-z0-9-]+$/.test(icon);
|
|
29430
|
+
const isUpload = !isName && typeof url === "string" && /^https?:\/\/\S+$/i.test(url);
|
|
29431
|
+
if (!key || !isName && !isUpload) return;
|
|
29432
|
+
const state = parseAiSectionsState(aiSectionsRef.current);
|
|
29433
|
+
const entry = state.sections.find((s) => key.startsWith(aiSlotKeyPrefixFor(s.id)));
|
|
29434
|
+
if (!entry) return;
|
|
29435
|
+
const path = key.slice(aiSlotKeyPrefixFor(entry.id).length);
|
|
29436
|
+
const uploadRef = isUpload ? `icon-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}` : null;
|
|
29437
|
+
const nextTree = uploadRef ? applyIconUploadToTree(entry.tree, path, uploadRef) : applyIconNameToTree(entry.tree, path, icon);
|
|
29438
|
+
if (!nextTree) return;
|
|
29439
|
+
void requestValidateTree5(postToParentRef.current, nextTree).then(({ ok }) => {
|
|
29440
|
+
if (!ok) return;
|
|
29441
|
+
applyTreeLocalRef.current?.({
|
|
29442
|
+
id: entry.id,
|
|
29443
|
+
mode: "replace",
|
|
29444
|
+
targetSectionId: entry.id,
|
|
29445
|
+
tree: nextTree,
|
|
29446
|
+
label: entry.label,
|
|
29447
|
+
media: uploadRef ? { ...entry.media, [uploadRef]: url } : entry.media,
|
|
29448
|
+
...entry.brand ? { brand: entry.brand } : {},
|
|
29449
|
+
// The user is looking at the icon they just changed — no need to recenter on it.
|
|
29450
|
+
skipScrollIntoView: true
|
|
29451
|
+
});
|
|
29452
|
+
});
|
|
29453
|
+
};
|
|
29277
29454
|
const handleImageUrl = (e) => {
|
|
29278
29455
|
if (e.data?.type !== "ow:image-url") return;
|
|
29279
29456
|
const { key, url } = e.data;
|
|
@@ -30722,6 +30899,7 @@ function OhhwellsBridge() {
|
|
|
30722
30899
|
window.addEventListener("message", handleSocialCancel);
|
|
30723
30900
|
window.addEventListener("message", handleSocialUpdate);
|
|
30724
30901
|
window.addEventListener("message", handleIconMarkup);
|
|
30902
|
+
window.addEventListener("message", handleIconName);
|
|
30725
30903
|
window.addEventListener("message", handleImageUrl);
|
|
30726
30904
|
window.addEventListener("message", handleImageUploading);
|
|
30727
30905
|
window.addEventListener("message", handleCarouselChange);
|
|
@@ -30806,6 +30984,7 @@ function OhhwellsBridge() {
|
|
|
30806
30984
|
window.removeEventListener("message", handleSocialCancel);
|
|
30807
30985
|
window.removeEventListener("message", handleSocialUpdate);
|
|
30808
30986
|
window.removeEventListener("message", handleIconMarkup);
|
|
30987
|
+
window.removeEventListener("message", handleIconName);
|
|
30809
30988
|
window.removeEventListener("message", handleImageUrl);
|
|
30810
30989
|
window.removeEventListener("message", handleImageUploading);
|
|
30811
30990
|
window.removeEventListener("message", handleCarouselChange);
|
|
@@ -31131,7 +31310,7 @@ function OhhwellsBridge() {
|
|
|
31131
31310
|
postToParent2({
|
|
31132
31311
|
type: "ow:ready",
|
|
31133
31312
|
version: "1",
|
|
31134
|
-
bridgeVersion: "0.1.
|
|
31313
|
+
bridgeVersion: "0.1.143",
|
|
31135
31314
|
path: pathname,
|
|
31136
31315
|
nodes: collectEditableNodes(editContentRef.current),
|
|
31137
31316
|
sections
|