@ohhwells/bridge 0.1.139-next.379 → 0.1.139-next.381
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 +345 -230
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +345 -230
- 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,274 @@ 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
|
+
const segments = path.split(".");
|
|
6562
|
+
const slot = segments.pop();
|
|
6563
|
+
if (!slot || NODE_SEGMENT.test(slot)) return null;
|
|
6564
|
+
const next = JSON.parse(JSON.stringify(tree));
|
|
6565
|
+
const node = nodeAtPath(next, segments);
|
|
6566
|
+
if (!node) return null;
|
|
6567
|
+
const current = node.slots?.[slot];
|
|
6568
|
+
if (typeof current !== "string" || !/^(lucide|simple):/.test(current)) return null;
|
|
6569
|
+
node.slots = { ...node.slots, [slot]: `lucide:${icon}` };
|
|
6570
|
+
return next;
|
|
6571
|
+
}
|
|
6572
|
+
|
|
6306
6573
|
// src/lib/ai-footer-tree.ts
|
|
6307
6574
|
var FOOTER_COLUMN_SPANS = [12, 6, 4, 3];
|
|
6308
6575
|
var FOOTER_COLUMN_BLOCKS = [
|
|
@@ -7688,17 +7955,18 @@ function MediaBox({
|
|
|
7688
7955
|
const url = refValue ? ctx.resolveMedia(refValue) : null;
|
|
7689
7956
|
const isIcon = /^(lucide|simple):/.test(refValue);
|
|
7690
7957
|
const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
|
|
7691
|
-
const editAttrs = ctx.keyFor && editPath ? {
|
|
7958
|
+
const editAttrs = ctx.keyFor && editPath && !isIcon ? {
|
|
7692
7959
|
"data-ohw-key": ctx.keyFor(editPath),
|
|
7693
|
-
...
|
|
7960
|
+
...genericImageEdit ? { "data-ohw-editable": "image" } : {}
|
|
7694
7961
|
} : {};
|
|
7695
7962
|
if (isIcon) {
|
|
7696
7963
|
const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
|
|
7964
|
+
const iconEditAttrs = ctx.keyFor && editPath && genericImageEdit ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "icon" } : {};
|
|
7697
7965
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
7698
7966
|
"span",
|
|
7699
7967
|
{
|
|
7700
7968
|
"data-ai-icon": refValue,
|
|
7701
|
-
...
|
|
7969
|
+
...iconEditAttrs,
|
|
7702
7970
|
style: {
|
|
7703
7971
|
display: "inline-flex",
|
|
7704
7972
|
width: 48,
|
|
@@ -8195,8 +8463,20 @@ function PricingCard({ node, ctx, path }) {
|
|
|
8195
8463
|
/* @__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) => {
|
|
8196
8464
|
const Icon = lucideByName(item.icon || "check");
|
|
8197
8465
|
const iconColor = pricingIconColor(item.iconColor, ctx.brand, fg);
|
|
8466
|
+
const iconAttrs = ctx.keyFor && !item.html ? {
|
|
8467
|
+
"data-ohw-key": ctx.keyFor(`${path}.features.${i}.icon`),
|
|
8468
|
+
"data-ohw-editable": "icon"
|
|
8469
|
+
} : {};
|
|
8198
8470
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", gap: AI_TREE_TOKENS.spacing3 }, children: [
|
|
8199
|
-
Icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
8471
|
+
Icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
8472
|
+
"span",
|
|
8473
|
+
{
|
|
8474
|
+
"data-ai-icon": `lucide:${item.icon || "check"}`,
|
|
8475
|
+
...iconAttrs,
|
|
8476
|
+
style: { display: "inline-flex", flexShrink: 0 },
|
|
8477
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { size: 20, color: iconColor })
|
|
8478
|
+
}
|
|
8479
|
+
),
|
|
8200
8480
|
item.html ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
8201
8481
|
"span",
|
|
8202
8482
|
{
|
|
@@ -18442,222 +18722,6 @@ function AiMediaStripOverlay({
|
|
|
18442
18722
|
// src/ui/ai-section/AiPricingCardOverlay.tsx
|
|
18443
18723
|
var import_react11 = require("react");
|
|
18444
18724
|
var import_lucide_react9 = require("lucide-react");
|
|
18445
|
-
|
|
18446
|
-
// src/lib/ai-pricing-tree.ts
|
|
18447
|
-
var PRICING_MIN_CARDS = 1;
|
|
18448
|
-
var PRICING_MAX_CARDS = 4;
|
|
18449
|
-
function isPricingCard(node) {
|
|
18450
|
-
return typeof node === "object" && node !== null && node.type === "card" && node.slots?.preset === "pricing";
|
|
18451
|
-
}
|
|
18452
|
-
function isPricingCollection(node) {
|
|
18453
|
-
return node.type === "collection" && Array.isArray(node.children) && node.children.length > 0 && isPricingCard(node.children[0]);
|
|
18454
|
-
}
|
|
18455
|
-
function findCollection(node) {
|
|
18456
|
-
if (isPricingCollection(node)) return node;
|
|
18457
|
-
for (const child of node.children ?? []) {
|
|
18458
|
-
const found = findCollection(child);
|
|
18459
|
-
if (found) return found;
|
|
18460
|
-
}
|
|
18461
|
-
return null;
|
|
18462
|
-
}
|
|
18463
|
-
function findPricingCollection(tree) {
|
|
18464
|
-
for (const row of tree.rows ?? []) {
|
|
18465
|
-
for (const block of row.blocks ?? []) {
|
|
18466
|
-
const found = findCollection(block);
|
|
18467
|
-
if (found) return found;
|
|
18468
|
-
}
|
|
18469
|
-
}
|
|
18470
|
-
return null;
|
|
18471
|
-
}
|
|
18472
|
-
function pricingCardCount(tree) {
|
|
18473
|
-
return findPricingCollection(tree)?.children?.length ?? 0;
|
|
18474
|
-
}
|
|
18475
|
-
function withPricingCollection(tree, mutate) {
|
|
18476
|
-
const next = JSON.parse(JSON.stringify(tree));
|
|
18477
|
-
const collection = findPricingCollection(next);
|
|
18478
|
-
if (!collection || !collection.children) return null;
|
|
18479
|
-
for (const card of collection.children) {
|
|
18480
|
-
if (isPricingCard(card)) upgradeLegacyPricingSlots(card);
|
|
18481
|
-
}
|
|
18482
|
-
return mutate(collection) ? next : null;
|
|
18483
|
-
}
|
|
18484
|
-
function upgradeLegacyPricingSlots(card) {
|
|
18485
|
-
const slots = card.slots = card.slots ?? {};
|
|
18486
|
-
if (typeof slots.features === "string") {
|
|
18487
|
-
const rows = pricingFeatureList(card);
|
|
18488
|
-
if (rows.length > 0) slots.features = rows.slice(0, PRICING_FEATURES_MAX);
|
|
18489
|
-
}
|
|
18490
|
-
if (slots.featured === true) slots.featured = { enabled: true };
|
|
18491
|
-
else if (slots.featured === false) delete slots.featured;
|
|
18492
|
-
}
|
|
18493
|
-
function addPricingTier(tree) {
|
|
18494
|
-
return withPricingCollection(tree, (collection) => {
|
|
18495
|
-
const children = collection.children;
|
|
18496
|
-
if (children.length >= PRICING_MAX_CARDS) return false;
|
|
18497
|
-
const clone = JSON.parse(JSON.stringify(children[children.length - 1]));
|
|
18498
|
-
clone.slots = clone.slots ?? {};
|
|
18499
|
-
delete clone.slots.featured;
|
|
18500
|
-
clone.slots.heading = "New tier";
|
|
18501
|
-
children.push(clone);
|
|
18502
|
-
collection.slots = { ...collection.slots, itemsPerRow: children.length };
|
|
18503
|
-
return true;
|
|
18504
|
-
});
|
|
18505
|
-
}
|
|
18506
|
-
function removePricingTier(tree, index) {
|
|
18507
|
-
return withPricingCollection(tree, (collection) => {
|
|
18508
|
-
const children = collection.children;
|
|
18509
|
-
if (children.length <= PRICING_MIN_CARDS) return false;
|
|
18510
|
-
if (index < 0 || index >= children.length) return false;
|
|
18511
|
-
children.splice(index, 1);
|
|
18512
|
-
collection.slots = { ...collection.slots, itemsPerRow: children.length };
|
|
18513
|
-
return true;
|
|
18514
|
-
});
|
|
18515
|
-
}
|
|
18516
|
-
var PRICING_CARD_STYLES = [
|
|
18517
|
-
{ value: "outline", label: "Outline", treatment: null },
|
|
18518
|
-
{ value: "lift", label: "Lift", treatment: "lifted" },
|
|
18519
|
-
{ value: "invert", label: "Invert", treatment: "inverted" },
|
|
18520
|
-
{ value: "header", label: "Header", treatment: "accent-header" }
|
|
18521
|
-
];
|
|
18522
|
-
var DEFAULT_FEATURED_BADGE = "Most popular";
|
|
18523
|
-
function featuredOf(target) {
|
|
18524
|
-
const raw = target?.slots?.featured;
|
|
18525
|
-
if (raw === true) return { enabled: true, treatment: "outlined" };
|
|
18526
|
-
return typeof raw === "object" && raw !== null ? raw : {};
|
|
18527
|
-
}
|
|
18528
|
-
function badgeOf(featured) {
|
|
18529
|
-
return typeof featured.badge === "string" ? featured.badge.trim() : "";
|
|
18530
|
-
}
|
|
18531
|
-
function pricingCardStyle(tree, index) {
|
|
18532
|
-
const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
|
|
18533
|
-
if (featured.enabled !== true) return "outline";
|
|
18534
|
-
return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
|
|
18535
|
-
}
|
|
18536
|
-
function pricingCardFeaturedTag(tree, index) {
|
|
18537
|
-
const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
|
|
18538
|
-
return featured.enabled === true && badgeOf(featured).length > 0;
|
|
18539
|
-
}
|
|
18540
|
-
function composeFeatured(style, tagOn, badgeText) {
|
|
18541
|
-
const treatment = PRICING_CARD_STYLES.find((s) => s.value === style)?.treatment ?? null;
|
|
18542
|
-
if (tagOn) return { enabled: true, badge: badgeText || DEFAULT_FEATURED_BADGE, treatment: treatment ?? "outlined" };
|
|
18543
|
-
if (treatment) return { enabled: true, treatment };
|
|
18544
|
-
return badgeText.length > 0 ? { enabled: false, badge: badgeText } : { enabled: false };
|
|
18545
|
-
}
|
|
18546
|
-
function withPricingCard(tree, index, mutate) {
|
|
18547
|
-
return withPricingCollection(tree, (collection) => {
|
|
18548
|
-
const children = collection.children;
|
|
18549
|
-
if (index < 0 || index >= children.length) return false;
|
|
18550
|
-
const target = children[index];
|
|
18551
|
-
target.slots = target.slots ?? {};
|
|
18552
|
-
mutate(target);
|
|
18553
|
-
return true;
|
|
18554
|
-
});
|
|
18555
|
-
}
|
|
18556
|
-
function setPricingCardStyle(tree, index, style) {
|
|
18557
|
-
if (!PRICING_CARD_STYLES.some((s) => s.value === style)) return null;
|
|
18558
|
-
return withPricingCard(tree, index, (target) => {
|
|
18559
|
-
const prev = featuredOf(target);
|
|
18560
|
-
const tagOn = prev.enabled === true && badgeOf(prev).length > 0;
|
|
18561
|
-
target.slots.featured = composeFeatured(style, tagOn, badgeOf(prev));
|
|
18562
|
-
});
|
|
18563
|
-
}
|
|
18564
|
-
function setPricingCardFeaturedTag(tree, index, on) {
|
|
18565
|
-
return withPricingCollection(tree, (collection) => {
|
|
18566
|
-
const children = collection.children;
|
|
18567
|
-
if (index < 0 || index >= children.length) return false;
|
|
18568
|
-
if (on) {
|
|
18569
|
-
children.forEach((card, i) => {
|
|
18570
|
-
if (i === index) return;
|
|
18571
|
-
const prev2 = featuredOf(card);
|
|
18572
|
-
if (prev2.enabled !== true || badgeOf(prev2).length === 0) return;
|
|
18573
|
-
card.slots = card.slots ?? {};
|
|
18574
|
-
card.slots.featured = composeFeatured(pricingCardStyleOf(prev2), false, badgeOf(prev2));
|
|
18575
|
-
});
|
|
18576
|
-
}
|
|
18577
|
-
const target = children[index];
|
|
18578
|
-
target.slots = target.slots ?? {};
|
|
18579
|
-
const prev = featuredOf(target);
|
|
18580
|
-
target.slots.featured = composeFeatured(pricingCardStyleOf(prev), on, badgeOf(prev));
|
|
18581
|
-
return true;
|
|
18582
|
-
});
|
|
18583
|
-
}
|
|
18584
|
-
function pricingCardStyleOf(featured) {
|
|
18585
|
-
if (featured.enabled !== true) return "outline";
|
|
18586
|
-
return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
|
|
18587
|
-
}
|
|
18588
|
-
function addPricingCardButton(tree, index, label) {
|
|
18589
|
-
return withPricingCollection(tree, (collection) => {
|
|
18590
|
-
const children = collection.children;
|
|
18591
|
-
if (index < 0 || index >= children.length) return false;
|
|
18592
|
-
const target = children[index];
|
|
18593
|
-
const buttons = (target.children ?? []).filter((c) => c.type === "button");
|
|
18594
|
-
if (buttons.length >= 2) return false;
|
|
18595
|
-
const hasPrimary = buttons.some((b) => b.slots?.variant === "primary");
|
|
18596
|
-
target.children = [
|
|
18597
|
-
...target.children ?? [],
|
|
18598
|
-
{ type: "button", span: 12, slots: { label, href: "#", variant: hasPrimary ? "secondary" : "primary" } }
|
|
18599
|
-
];
|
|
18600
|
-
return true;
|
|
18601
|
-
});
|
|
18602
|
-
}
|
|
18603
|
-
function removePricingCardButton(tree, index) {
|
|
18604
|
-
return withPricingCollection(tree, (collection) => {
|
|
18605
|
-
const children = collection.children;
|
|
18606
|
-
if (index < 0 || index >= children.length) return false;
|
|
18607
|
-
const target = children[index];
|
|
18608
|
-
const buttons = (target.children ?? []).filter((c) => c.type === "button");
|
|
18609
|
-
if (buttons.length === 0) return false;
|
|
18610
|
-
const lastButton = buttons[buttons.length - 1];
|
|
18611
|
-
target.children = (target.children ?? []).filter((c) => c !== lastButton);
|
|
18612
|
-
return true;
|
|
18613
|
-
});
|
|
18614
|
-
}
|
|
18615
|
-
var PRICING_FEATURES_MIN = 1;
|
|
18616
|
-
var PRICING_FEATURES_MAX = 10;
|
|
18617
|
-
function pricingFeatureList(target) {
|
|
18618
|
-
const raw = target.slots?.features;
|
|
18619
|
-
if (Array.isArray(raw)) {
|
|
18620
|
-
return raw.filter((item) => !!item && typeof item === "object" && typeof item.text === "string").map((item) => ({
|
|
18621
|
-
text: item.text,
|
|
18622
|
-
...typeof item.icon === "string" ? { icon: item.icon } : {},
|
|
18623
|
-
...typeof item.iconColor === "string" ? { iconColor: item.iconColor } : {}
|
|
18624
|
-
}));
|
|
18625
|
-
}
|
|
18626
|
-
if (typeof raw === "string") {
|
|
18627
|
-
return raw.split(/\n|<br\s*\/?>/i).map((line) => line.replace(/<[^>]+>/g, "").replace(/^[-*•]\s*/, "").trim().slice(0, 120)).filter(Boolean).map((text) => ({ text }));
|
|
18628
|
-
}
|
|
18629
|
-
return [];
|
|
18630
|
-
}
|
|
18631
|
-
function pricingCardFeatureCount(tree, index) {
|
|
18632
|
-
const target = findPricingCollection(tree)?.children?.[index];
|
|
18633
|
-
return target ? pricingFeatureList(target).length : 0;
|
|
18634
|
-
}
|
|
18635
|
-
function addPricingCardFeature(tree, index, text) {
|
|
18636
|
-
return withPricingCollection(tree, (collection) => {
|
|
18637
|
-
const children = collection.children;
|
|
18638
|
-
if (index < 0 || index >= children.length) return false;
|
|
18639
|
-
const target = children[index];
|
|
18640
|
-
const features = pricingFeatureList(target);
|
|
18641
|
-
if (features.length >= PRICING_FEATURES_MAX) return false;
|
|
18642
|
-
target.slots = target.slots ?? {};
|
|
18643
|
-
target.slots.features = [...features, { text }];
|
|
18644
|
-
return true;
|
|
18645
|
-
});
|
|
18646
|
-
}
|
|
18647
|
-
function removePricingCardFeature(tree, index) {
|
|
18648
|
-
return withPricingCollection(tree, (collection) => {
|
|
18649
|
-
const children = collection.children;
|
|
18650
|
-
if (index < 0 || index >= children.length) return false;
|
|
18651
|
-
const target = children[index];
|
|
18652
|
-
const features = pricingFeatureList(target);
|
|
18653
|
-
if (features.length <= PRICING_FEATURES_MIN) return false;
|
|
18654
|
-
target.slots = target.slots ?? {};
|
|
18655
|
-
target.slots.features = features.slice(0, -1);
|
|
18656
|
-
return true;
|
|
18657
|
-
});
|
|
18658
|
-
}
|
|
18659
|
-
|
|
18660
|
-
// src/ui/ai-section/AiPricingCardOverlay.tsx
|
|
18661
18725
|
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
18662
18726
|
var PRIMARY4 = "var(--ohw-primary, #0885FE)";
|
|
18663
18727
|
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]";
|
|
@@ -23784,6 +23848,23 @@ function currentIconRef(el) {
|
|
|
23784
23848
|
);
|
|
23785
23849
|
return named ? `lucide:${named.slice("lucide-".length)}` : "";
|
|
23786
23850
|
}
|
|
23851
|
+
function requestValidateTree5(postToParent2, tree) {
|
|
23852
|
+
return new Promise((resolve) => {
|
|
23853
|
+
const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
23854
|
+
const timeout = window.setTimeout(() => {
|
|
23855
|
+
window.removeEventListener("message", onMessage);
|
|
23856
|
+
resolve({ ok: false, reasons: ["Validation request timed out."] });
|
|
23857
|
+
}, 8e3);
|
|
23858
|
+
function onMessage(e) {
|
|
23859
|
+
if (e.data?.type !== "ow:ai-validate-tree-result" || e.data.id !== id) return;
|
|
23860
|
+
window.clearTimeout(timeout);
|
|
23861
|
+
window.removeEventListener("message", onMessage);
|
|
23862
|
+
resolve({ ok: e.data.ok === true, reasons: Array.isArray(e.data.reasons) ? e.data.reasons : [] });
|
|
23863
|
+
}
|
|
23864
|
+
window.addEventListener("message", onMessage);
|
|
23865
|
+
postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
|
|
23866
|
+
});
|
|
23867
|
+
}
|
|
23787
23868
|
function getEditMeasureEl(editable) {
|
|
23788
23869
|
return editable.closest("[data-ohw-href-key]") ?? editable;
|
|
23789
23870
|
}
|
|
@@ -27781,8 +27862,11 @@ function OhhwellsBridge() {
|
|
|
27781
27862
|
display: none !important;
|
|
27782
27863
|
}
|
|
27783
27864
|
/* Body text (no item-action toolbar) \u2014 first click enters text edit \u2192 I-beam.
|
|
27784
|
-
Exclude select-first nav/footer labels so grab/default on [data-ohw-href-key] can win.
|
|
27785
|
-
|
|
27865
|
+
Exclude select-first nav/footer labels so grab/default on [data-ohw-href-key] can win.
|
|
27866
|
+
Icons are excluded too: a glyph is replaced by a click, never typed into (OHH-623),
|
|
27867
|
+
so it takes the picker's pointer below, not an I-beam \u2014 this blanket !important used
|
|
27868
|
+
to beat the stylesheet's own non-!important icon rule (OHH-718). */
|
|
27869
|
+
[data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]):not([data-ohw-editable="link"]):not([data-ohw-editable="icon"]):not(:is([data-ohw-href-key] *)) {
|
|
27786
27870
|
cursor: text !important;
|
|
27787
27871
|
}
|
|
27788
27872
|
/* Select-first items: default arrow; grab only when drag-start is allowed (data-ohw-can-drag). */
|
|
@@ -27803,6 +27887,7 @@ function OhhwellsBridge() {
|
|
|
27803
27887
|
[data-ohw-editable="video"], [data-ohw-editable="video"] *,
|
|
27804
27888
|
[data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
|
|
27805
27889
|
[data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
|
|
27890
|
+
[data-ohw-editable="icon"], [data-ohw-editable="icon"] * { cursor: pointer !important; }
|
|
27806
27891
|
/* A text/plain editable nested inside a media editable (e.g. a placeholder's fallback
|
|
27807
27892
|
label inside its bg-image box) is its own click target, not the media picker \u2014 the
|
|
27808
27893
|
blanket media cursor rule above would otherwise show pointer where a click actually
|
|
@@ -28132,10 +28217,14 @@ function OhhwellsBridge() {
|
|
|
28132
28217
|
e.preventDefault();
|
|
28133
28218
|
e.stopPropagation();
|
|
28134
28219
|
aiSectionApiRef.current?.selectFromElement(editable);
|
|
28220
|
+
const aiIcon = editable.hasAttribute("data-ai-icon");
|
|
28221
|
+
const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
|
|
28135
28222
|
postToParentRef.current({
|
|
28136
28223
|
type: "ow:icon-pick",
|
|
28137
28224
|
key: editable.dataset.ohwKey ?? "",
|
|
28138
|
-
current: currentIconRef(editable)
|
|
28225
|
+
current: currentIconRef(editable),
|
|
28226
|
+
...aiIcon ? { iconMode: "name" } : {},
|
|
28227
|
+
...featureIcon ? { options: [...PRICING_FEATURE_ICONS] } : {}
|
|
28139
28228
|
});
|
|
28140
28229
|
return;
|
|
28141
28230
|
}
|
|
@@ -29397,15 +29486,39 @@ function OhhwellsBridge() {
|
|
|
29397
29486
|
if (e.data?.type !== "ow:icon-markup") return;
|
|
29398
29487
|
const { key, markup } = e.data;
|
|
29399
29488
|
if (!key || typeof markup !== "string") return;
|
|
29400
|
-
const targets =
|
|
29401
|
-
`[data-ohw-key="${key}"][data-ohw-editable="icon"]`
|
|
29402
|
-
);
|
|
29489
|
+
const targets = Array.from(
|
|
29490
|
+
document.querySelectorAll(`[data-ohw-key="${key}"][data-ohw-editable="icon"]`)
|
|
29491
|
+
).filter((el) => !el.hasAttribute("data-ai-icon"));
|
|
29403
29492
|
if (!targets.length) return;
|
|
29404
29493
|
targets.forEach((el) => {
|
|
29405
29494
|
applyIconMarkup(el, markup);
|
|
29406
29495
|
});
|
|
29407
29496
|
postToParentRef.current({ type: "ow:change", nodes: [{ key, text: markup }], flush: true });
|
|
29408
29497
|
};
|
|
29498
|
+
const handleIconName = (e) => {
|
|
29499
|
+
if (e.data?.type !== "ow:icon-name") return;
|
|
29500
|
+
const { key, icon } = e.data;
|
|
29501
|
+
if (!key || typeof icon !== "string" || !/^[a-z0-9-]+$/.test(icon)) return;
|
|
29502
|
+
const state = parseAiSectionsState(aiSectionsRef.current);
|
|
29503
|
+
const entry = state.sections.find((s) => key.startsWith(aiSlotKeyPrefixFor(s.id)));
|
|
29504
|
+
if (!entry) return;
|
|
29505
|
+
const nextTree = applyIconNameToTree(entry.tree, key.slice(aiSlotKeyPrefixFor(entry.id).length), icon);
|
|
29506
|
+
if (!nextTree) return;
|
|
29507
|
+
void requestValidateTree5(postToParentRef.current, nextTree).then(({ ok }) => {
|
|
29508
|
+
if (!ok) return;
|
|
29509
|
+
applyTreeLocalRef.current?.({
|
|
29510
|
+
id: entry.id,
|
|
29511
|
+
mode: "replace",
|
|
29512
|
+
targetSectionId: entry.id,
|
|
29513
|
+
tree: nextTree,
|
|
29514
|
+
label: entry.label,
|
|
29515
|
+
media: entry.media,
|
|
29516
|
+
...entry.brand ? { brand: entry.brand } : {},
|
|
29517
|
+
// The user is looking at the icon they just changed — no need to recenter on it.
|
|
29518
|
+
skipScrollIntoView: true
|
|
29519
|
+
});
|
|
29520
|
+
});
|
|
29521
|
+
};
|
|
29409
29522
|
const handleImageUrl = (e) => {
|
|
29410
29523
|
if (e.data?.type !== "ow:image-url") return;
|
|
29411
29524
|
const { key, url } = e.data;
|
|
@@ -30861,6 +30974,7 @@ function OhhwellsBridge() {
|
|
|
30861
30974
|
window.addEventListener("message", handleSocialCancel);
|
|
30862
30975
|
window.addEventListener("message", handleSocialUpdate);
|
|
30863
30976
|
window.addEventListener("message", handleIconMarkup);
|
|
30977
|
+
window.addEventListener("message", handleIconName);
|
|
30864
30978
|
window.addEventListener("message", handleImageUrl);
|
|
30865
30979
|
window.addEventListener("message", handleImageUploading);
|
|
30866
30980
|
window.addEventListener("message", handleCarouselChange);
|
|
@@ -30945,6 +31059,7 @@ function OhhwellsBridge() {
|
|
|
30945
31059
|
window.removeEventListener("message", handleSocialCancel);
|
|
30946
31060
|
window.removeEventListener("message", handleSocialUpdate);
|
|
30947
31061
|
window.removeEventListener("message", handleIconMarkup);
|
|
31062
|
+
window.removeEventListener("message", handleIconName);
|
|
30948
31063
|
window.removeEventListener("message", handleImageUrl);
|
|
30949
31064
|
window.removeEventListener("message", handleImageUploading);
|
|
30950
31065
|
window.removeEventListener("message", handleCarouselChange);
|