@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 +352 -228
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +352 -228
- package/dist/index.js.map +1 -1
- package/dist/styles.css +4 -0
- package/package.json +1 -1
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
|
|
5037
|
-
|
|
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)(
|
|
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 =
|
|
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.
|
|
31346
|
+
bridgeVersion: "0.1.142",
|
|
31223
31347
|
path: pathname,
|
|
31224
31348
|
nodes: collectEditableNodes(editContentRef.current),
|
|
31225
31349
|
sections
|