@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.js
CHANGED
|
@@ -4939,10 +4939,9 @@ function getLinkHref3(el) {
|
|
|
4939
4939
|
}
|
|
4940
4940
|
function collectEditableNodes(extraContent, root = document) {
|
|
4941
4941
|
const isScoped = root !== document;
|
|
4942
|
-
const
|
|
4943
|
-
|
|
4944
|
-
)
|
|
4945
|
-
if (isScoped && root instanceof HTMLElement && root.matches('[data-ohw-editable]:not([data-ohw-editable="form"])')) {
|
|
4942
|
+
const EDITABLE_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="form"]):not([data-ai-icon])';
|
|
4943
|
+
const editableEls = Array.from(root.querySelectorAll(EDITABLE_SELECTOR));
|
|
4944
|
+
if (isScoped && root instanceof HTMLElement && root.matches(EDITABLE_SELECTOR)) {
|
|
4946
4945
|
editableEls.unshift(root);
|
|
4947
4946
|
}
|
|
4948
4947
|
const nodes = editableEls.map((el) => {
|
|
@@ -6209,6 +6208,281 @@ function reapRemovedAiSections(state, store, removedIds, excludeIds = /* @__PURE
|
|
|
6209
6208
|
return { state: nextState, store: nextStore, reapedIds, slotPrefixes, changed: true };
|
|
6210
6209
|
}
|
|
6211
6210
|
|
|
6211
|
+
// src/lib/ai-pricing-tree.ts
|
|
6212
|
+
var PRICING_MIN_CARDS = 1;
|
|
6213
|
+
var PRICING_MAX_CARDS = 4;
|
|
6214
|
+
function isPricingCard(node) {
|
|
6215
|
+
return typeof node === "object" && node !== null && node.type === "card" && node.slots?.preset === "pricing";
|
|
6216
|
+
}
|
|
6217
|
+
function isPricingCollection(node) {
|
|
6218
|
+
return node.type === "collection" && Array.isArray(node.children) && node.children.length > 0 && isPricingCard(node.children[0]);
|
|
6219
|
+
}
|
|
6220
|
+
function findCollection(node) {
|
|
6221
|
+
if (isPricingCollection(node)) return node;
|
|
6222
|
+
for (const child of node.children ?? []) {
|
|
6223
|
+
const found = findCollection(child);
|
|
6224
|
+
if (found) return found;
|
|
6225
|
+
}
|
|
6226
|
+
return null;
|
|
6227
|
+
}
|
|
6228
|
+
function findPricingCollection(tree) {
|
|
6229
|
+
for (const row of tree.rows ?? []) {
|
|
6230
|
+
for (const block of row.blocks ?? []) {
|
|
6231
|
+
const found = findCollection(block);
|
|
6232
|
+
if (found) return found;
|
|
6233
|
+
}
|
|
6234
|
+
}
|
|
6235
|
+
return null;
|
|
6236
|
+
}
|
|
6237
|
+
function pricingCardCount(tree) {
|
|
6238
|
+
return findPricingCollection(tree)?.children?.length ?? 0;
|
|
6239
|
+
}
|
|
6240
|
+
function withPricingCollection(tree, mutate) {
|
|
6241
|
+
const next = JSON.parse(JSON.stringify(tree));
|
|
6242
|
+
const collection = findPricingCollection(next);
|
|
6243
|
+
if (!collection || !collection.children) return null;
|
|
6244
|
+
for (const card of collection.children) {
|
|
6245
|
+
if (isPricingCard(card)) upgradeLegacyPricingSlots(card);
|
|
6246
|
+
}
|
|
6247
|
+
return mutate(collection) ? next : null;
|
|
6248
|
+
}
|
|
6249
|
+
function upgradeLegacyPricingSlots(card) {
|
|
6250
|
+
const slots = card.slots = card.slots ?? {};
|
|
6251
|
+
if (typeof slots.features === "string") {
|
|
6252
|
+
const rows = pricingFeatureList(card);
|
|
6253
|
+
if (rows.length > 0) slots.features = rows.slice(0, PRICING_FEATURES_MAX);
|
|
6254
|
+
}
|
|
6255
|
+
if (slots.featured === true) slots.featured = { enabled: true };
|
|
6256
|
+
else if (slots.featured === false) delete slots.featured;
|
|
6257
|
+
}
|
|
6258
|
+
function addPricingTier(tree) {
|
|
6259
|
+
return withPricingCollection(tree, (collection) => {
|
|
6260
|
+
const children = collection.children;
|
|
6261
|
+
if (children.length >= PRICING_MAX_CARDS) return false;
|
|
6262
|
+
const clone = JSON.parse(JSON.stringify(children[children.length - 1]));
|
|
6263
|
+
clone.slots = clone.slots ?? {};
|
|
6264
|
+
delete clone.slots.featured;
|
|
6265
|
+
clone.slots.heading = "New tier";
|
|
6266
|
+
children.push(clone);
|
|
6267
|
+
collection.slots = { ...collection.slots, itemsPerRow: children.length };
|
|
6268
|
+
return true;
|
|
6269
|
+
});
|
|
6270
|
+
}
|
|
6271
|
+
function removePricingTier(tree, index) {
|
|
6272
|
+
return withPricingCollection(tree, (collection) => {
|
|
6273
|
+
const children = collection.children;
|
|
6274
|
+
if (children.length <= PRICING_MIN_CARDS) return false;
|
|
6275
|
+
if (index < 0 || index >= children.length) return false;
|
|
6276
|
+
children.splice(index, 1);
|
|
6277
|
+
collection.slots = { ...collection.slots, itemsPerRow: children.length };
|
|
6278
|
+
return true;
|
|
6279
|
+
});
|
|
6280
|
+
}
|
|
6281
|
+
var PRICING_CARD_STYLES = [
|
|
6282
|
+
{ value: "outline", label: "Outline", treatment: null },
|
|
6283
|
+
{ value: "lift", label: "Lift", treatment: "lifted" },
|
|
6284
|
+
{ value: "invert", label: "Invert", treatment: "inverted" },
|
|
6285
|
+
{ value: "header", label: "Header", treatment: "accent-header" }
|
|
6286
|
+
];
|
|
6287
|
+
var DEFAULT_FEATURED_BADGE = "Most popular";
|
|
6288
|
+
function featuredOf(target) {
|
|
6289
|
+
const raw = target?.slots?.featured;
|
|
6290
|
+
if (raw === true) return { enabled: true, treatment: "outlined" };
|
|
6291
|
+
return typeof raw === "object" && raw !== null ? raw : {};
|
|
6292
|
+
}
|
|
6293
|
+
function badgeOf(featured) {
|
|
6294
|
+
return typeof featured.badge === "string" ? featured.badge.trim() : "";
|
|
6295
|
+
}
|
|
6296
|
+
function pricingCardStyle(tree, index) {
|
|
6297
|
+
const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
|
|
6298
|
+
if (featured.enabled !== true) return "outline";
|
|
6299
|
+
return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
|
|
6300
|
+
}
|
|
6301
|
+
function pricingCardFeaturedTag(tree, index) {
|
|
6302
|
+
const featured = featuredOf(findPricingCollection(tree)?.children?.[index]);
|
|
6303
|
+
return featured.enabled === true && badgeOf(featured).length > 0;
|
|
6304
|
+
}
|
|
6305
|
+
function composeFeatured(style, tagOn, badgeText) {
|
|
6306
|
+
const treatment = PRICING_CARD_STYLES.find((s) => s.value === style)?.treatment ?? null;
|
|
6307
|
+
if (tagOn) return { enabled: true, badge: badgeText || DEFAULT_FEATURED_BADGE, treatment: treatment ?? "outlined" };
|
|
6308
|
+
if (treatment) return { enabled: true, treatment };
|
|
6309
|
+
return badgeText.length > 0 ? { enabled: false, badge: badgeText } : { enabled: false };
|
|
6310
|
+
}
|
|
6311
|
+
function withPricingCard(tree, index, mutate) {
|
|
6312
|
+
return withPricingCollection(tree, (collection) => {
|
|
6313
|
+
const children = collection.children;
|
|
6314
|
+
if (index < 0 || index >= children.length) return false;
|
|
6315
|
+
const target = children[index];
|
|
6316
|
+
target.slots = target.slots ?? {};
|
|
6317
|
+
mutate(target);
|
|
6318
|
+
return true;
|
|
6319
|
+
});
|
|
6320
|
+
}
|
|
6321
|
+
function setPricingCardStyle(tree, index, style) {
|
|
6322
|
+
if (!PRICING_CARD_STYLES.some((s) => s.value === style)) return null;
|
|
6323
|
+
return withPricingCard(tree, index, (target) => {
|
|
6324
|
+
const prev = featuredOf(target);
|
|
6325
|
+
const tagOn = prev.enabled === true && badgeOf(prev).length > 0;
|
|
6326
|
+
target.slots.featured = composeFeatured(style, tagOn, badgeOf(prev));
|
|
6327
|
+
});
|
|
6328
|
+
}
|
|
6329
|
+
function setPricingCardFeaturedTag(tree, index, on) {
|
|
6330
|
+
return withPricingCollection(tree, (collection) => {
|
|
6331
|
+
const children = collection.children;
|
|
6332
|
+
if (index < 0 || index >= children.length) return false;
|
|
6333
|
+
if (on) {
|
|
6334
|
+
children.forEach((card, i) => {
|
|
6335
|
+
if (i === index) return;
|
|
6336
|
+
const prev2 = featuredOf(card);
|
|
6337
|
+
if (prev2.enabled !== true || badgeOf(prev2).length === 0) return;
|
|
6338
|
+
card.slots = card.slots ?? {};
|
|
6339
|
+
card.slots.featured = composeFeatured(pricingCardStyleOf(prev2), false, badgeOf(prev2));
|
|
6340
|
+
});
|
|
6341
|
+
}
|
|
6342
|
+
const target = children[index];
|
|
6343
|
+
target.slots = target.slots ?? {};
|
|
6344
|
+
const prev = featuredOf(target);
|
|
6345
|
+
target.slots.featured = composeFeatured(pricingCardStyleOf(prev), on, badgeOf(prev));
|
|
6346
|
+
return true;
|
|
6347
|
+
});
|
|
6348
|
+
}
|
|
6349
|
+
function pricingCardStyleOf(featured) {
|
|
6350
|
+
if (featured.enabled !== true) return "outline";
|
|
6351
|
+
return PRICING_CARD_STYLES.find((s) => s.treatment === featured.treatment)?.value ?? "outline";
|
|
6352
|
+
}
|
|
6353
|
+
function addPricingCardButton(tree, index, label) {
|
|
6354
|
+
return withPricingCollection(tree, (collection) => {
|
|
6355
|
+
const children = collection.children;
|
|
6356
|
+
if (index < 0 || index >= children.length) return false;
|
|
6357
|
+
const target = children[index];
|
|
6358
|
+
const buttons = (target.children ?? []).filter((c) => c.type === "button");
|
|
6359
|
+
if (buttons.length >= 2) return false;
|
|
6360
|
+
const hasPrimary = buttons.some((b) => b.slots?.variant === "primary");
|
|
6361
|
+
target.children = [
|
|
6362
|
+
...target.children ?? [],
|
|
6363
|
+
{ type: "button", span: 12, slots: { label, href: "#", variant: hasPrimary ? "secondary" : "primary" } }
|
|
6364
|
+
];
|
|
6365
|
+
return true;
|
|
6366
|
+
});
|
|
6367
|
+
}
|
|
6368
|
+
function removePricingCardButton(tree, index) {
|
|
6369
|
+
return withPricingCollection(tree, (collection) => {
|
|
6370
|
+
const children = collection.children;
|
|
6371
|
+
if (index < 0 || index >= children.length) return false;
|
|
6372
|
+
const target = children[index];
|
|
6373
|
+
const buttons = (target.children ?? []).filter((c) => c.type === "button");
|
|
6374
|
+
if (buttons.length === 0) return false;
|
|
6375
|
+
const lastButton = buttons[buttons.length - 1];
|
|
6376
|
+
target.children = (target.children ?? []).filter((c) => c !== lastButton);
|
|
6377
|
+
return true;
|
|
6378
|
+
});
|
|
6379
|
+
}
|
|
6380
|
+
var PRICING_FEATURES_MIN = 1;
|
|
6381
|
+
var PRICING_FEATURES_MAX = 10;
|
|
6382
|
+
var PRICING_FEATURE_ICONS = [
|
|
6383
|
+
"check",
|
|
6384
|
+
"check-circle",
|
|
6385
|
+
"x",
|
|
6386
|
+
"star",
|
|
6387
|
+
"zap",
|
|
6388
|
+
"shield",
|
|
6389
|
+
"clock",
|
|
6390
|
+
"users",
|
|
6391
|
+
"infinity"
|
|
6392
|
+
];
|
|
6393
|
+
function pricingFeatureList(target) {
|
|
6394
|
+
const raw = target.slots?.features;
|
|
6395
|
+
if (Array.isArray(raw)) {
|
|
6396
|
+
return raw.filter((item) => !!item && typeof item === "object" && typeof item.text === "string").map((item) => ({
|
|
6397
|
+
text: item.text,
|
|
6398
|
+
...typeof item.icon === "string" ? { icon: item.icon } : {},
|
|
6399
|
+
...typeof item.iconColor === "string" ? { iconColor: item.iconColor } : {}
|
|
6400
|
+
}));
|
|
6401
|
+
}
|
|
6402
|
+
if (typeof raw === "string") {
|
|
6403
|
+
return raw.split(/\n|<br\s*\/?>/i).map((line) => line.replace(/<[^>]+>/g, "").replace(/^[-*•]\s*/, "").trim().slice(0, 120)).filter(Boolean).map((text) => ({ text }));
|
|
6404
|
+
}
|
|
6405
|
+
return [];
|
|
6406
|
+
}
|
|
6407
|
+
function pricingCardFeatureCount(tree, index) {
|
|
6408
|
+
const target = findPricingCollection(tree)?.children?.[index];
|
|
6409
|
+
return target ? pricingFeatureList(target).length : 0;
|
|
6410
|
+
}
|
|
6411
|
+
function addPricingCardFeature(tree, index, text) {
|
|
6412
|
+
return withPricingCollection(tree, (collection) => {
|
|
6413
|
+
const children = collection.children;
|
|
6414
|
+
if (index < 0 || index >= children.length) return false;
|
|
6415
|
+
const target = children[index];
|
|
6416
|
+
const features = pricingFeatureList(target);
|
|
6417
|
+
if (features.length >= PRICING_FEATURES_MAX) return false;
|
|
6418
|
+
target.slots = target.slots ?? {};
|
|
6419
|
+
target.slots.features = [...features, { text }];
|
|
6420
|
+
return true;
|
|
6421
|
+
});
|
|
6422
|
+
}
|
|
6423
|
+
function setPricingCardFeatureIcon(tree, index, featureIndex, icon) {
|
|
6424
|
+
if (!PRICING_FEATURE_ICONS.includes(icon)) return null;
|
|
6425
|
+
return withPricingCollection(tree, (collection) => {
|
|
6426
|
+
const children = collection.children;
|
|
6427
|
+
if (index < 0 || index >= children.length) return false;
|
|
6428
|
+
const target = children[index];
|
|
6429
|
+
const features = pricingFeatureList(target);
|
|
6430
|
+
if (featureIndex < 0 || featureIndex >= features.length) return false;
|
|
6431
|
+
target.slots = target.slots ?? {};
|
|
6432
|
+
target.slots.features = features.map((row, i) => i === featureIndex ? { ...row, icon } : row);
|
|
6433
|
+
return true;
|
|
6434
|
+
});
|
|
6435
|
+
}
|
|
6436
|
+
function removePricingCardFeature(tree, index) {
|
|
6437
|
+
return withPricingCollection(tree, (collection) => {
|
|
6438
|
+
const children = collection.children;
|
|
6439
|
+
if (index < 0 || index >= children.length) return false;
|
|
6440
|
+
const target = children[index];
|
|
6441
|
+
const features = pricingFeatureList(target);
|
|
6442
|
+
if (features.length <= PRICING_FEATURES_MIN) return false;
|
|
6443
|
+
target.slots = target.slots ?? {};
|
|
6444
|
+
target.slots.features = features.slice(0, -1);
|
|
6445
|
+
return true;
|
|
6446
|
+
});
|
|
6447
|
+
}
|
|
6448
|
+
|
|
6449
|
+
// src/lib/ai-icon-edit.ts
|
|
6450
|
+
var FEATURE_ICON_PATH = /\.c(\d+)\.features\.(\d+)\.icon$/;
|
|
6451
|
+
var NODE_SEGMENT = /^[rbc]\d+$/;
|
|
6452
|
+
function nodeAtPath(tree, segments) {
|
|
6453
|
+
if (segments.length < 2 || segments.some((s) => !NODE_SEGMENT.test(s))) return null;
|
|
6454
|
+
if (!segments[0].startsWith("r") || !segments[1].startsWith("b")) return null;
|
|
6455
|
+
let node = tree.rows?.[Number(segments[0].slice(1))]?.blocks?.[Number(segments[1].slice(1))];
|
|
6456
|
+
for (const segment of segments.slice(2)) {
|
|
6457
|
+
if (!segment.startsWith("c")) return null;
|
|
6458
|
+
node = node?.children?.[Number(segment.slice(1))];
|
|
6459
|
+
}
|
|
6460
|
+
return node ?? null;
|
|
6461
|
+
}
|
|
6462
|
+
function applyIconNameToTree(tree, path, icon) {
|
|
6463
|
+
const feature = path.match(FEATURE_ICON_PATH);
|
|
6464
|
+
if (feature) {
|
|
6465
|
+
return setPricingCardFeatureIcon(tree, Number(feature[1]), Number(feature[2]), icon);
|
|
6466
|
+
}
|
|
6467
|
+
return rewriteIconSlot(tree, path, `lucide:${icon}`);
|
|
6468
|
+
}
|
|
6469
|
+
function applyIconUploadToTree(tree, path, mediaRef) {
|
|
6470
|
+
if (FEATURE_ICON_PATH.test(path)) return null;
|
|
6471
|
+
return rewriteIconSlot(tree, path, `icon:${mediaRef}`);
|
|
6472
|
+
}
|
|
6473
|
+
function rewriteIconSlot(tree, path, ref) {
|
|
6474
|
+
const segments = path.split(".");
|
|
6475
|
+
const slot = segments.pop();
|
|
6476
|
+
if (!slot || NODE_SEGMENT.test(slot)) return null;
|
|
6477
|
+
const next = JSON.parse(JSON.stringify(tree));
|
|
6478
|
+
const node = nodeAtPath(next, segments);
|
|
6479
|
+
if (!node) return null;
|
|
6480
|
+
const current = node.slots?.[slot];
|
|
6481
|
+
if (typeof current !== "string" || !/^(lucide|simple|icon):/.test(current)) return null;
|
|
6482
|
+
node.slots = { ...node.slots, [slot]: ref };
|
|
6483
|
+
return next;
|
|
6484
|
+
}
|
|
6485
|
+
|
|
6212
6486
|
// src/lib/ai-footer-tree.ts
|
|
6213
6487
|
var FOOTER_COLUMN_SPANS = [12, 6, 4, 3];
|
|
6214
6488
|
var FOOTER_COLUMN_BLOCKS = [
|
|
@@ -7565,7 +7839,7 @@ function MediaBox({
|
|
|
7565
7839
|
onError
|
|
7566
7840
|
}) {
|
|
7567
7841
|
const url = refValue ? ctx.resolveMedia(refValue) : null;
|
|
7568
|
-
const isIcon = /^(lucide|simple):/.test(refValue);
|
|
7842
|
+
const isIcon = /^(lucide|simple|icon):/.test(refValue);
|
|
7569
7843
|
const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
|
|
7570
7844
|
const editAttrs = ctx.keyFor && editPath && !isIcon ? {
|
|
7571
7845
|
"data-ohw-key": ctx.keyFor(editPath),
|
|
@@ -7573,10 +7847,13 @@ function MediaBox({
|
|
|
7573
7847
|
} : {};
|
|
7574
7848
|
if (isIcon) {
|
|
7575
7849
|
const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
|
|
7850
|
+
const uploadedIconUrl = refValue.startsWith("icon:") ? ctx.resolveMedia(refValue.slice(5)) : null;
|
|
7851
|
+
const iconEditAttrs = ctx.keyFor && editPath && genericImageEdit ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "icon" } : {};
|
|
7576
7852
|
return /* @__PURE__ */ jsx(
|
|
7577
7853
|
"span",
|
|
7578
7854
|
{
|
|
7579
7855
|
"data-ai-icon": refValue,
|
|
7856
|
+
...iconEditAttrs,
|
|
7580
7857
|
style: {
|
|
7581
7858
|
display: "inline-flex",
|
|
7582
7859
|
width: 48,
|
|
@@ -7585,7 +7862,7 @@ function MediaBox({
|
|
|
7585
7862
|
justifyContent: "center",
|
|
7586
7863
|
color: ctx.brand.palette.dark
|
|
7587
7864
|
},
|
|
7588
|
-
children: Icon ? /* @__PURE__ */ jsx(Icon, { size: 48, color: ctx.brand.palette.dark, strokeWidth: 1.5 }) : /* @__PURE__ */ jsx("span", { style: { fontSize: 24 }, children: "\u25C6" })
|
|
7865
|
+
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
7866
|
}
|
|
7590
7867
|
);
|
|
7591
7868
|
}
|
|
@@ -8073,8 +8350,20 @@ function PricingCard({ node, ctx, path }) {
|
|
|
8073
8350
|
/* @__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
8351
|
const Icon = lucideByName(item.icon || "check");
|
|
8075
8352
|
const iconColor = pricingIconColor(item.iconColor, ctx.brand, fg);
|
|
8353
|
+
const iconAttrs = ctx.keyFor && !item.html ? {
|
|
8354
|
+
"data-ohw-key": ctx.keyFor(`${path}.features.${i}.icon`),
|
|
8355
|
+
"data-ohw-editable": "icon"
|
|
8356
|
+
} : {};
|
|
8076
8357
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "flex-start", gap: AI_TREE_TOKENS.spacing3 }, children: [
|
|
8077
|
-
Icon && /* @__PURE__ */ jsx(
|
|
8358
|
+
Icon && /* @__PURE__ */ jsx(
|
|
8359
|
+
"span",
|
|
8360
|
+
{
|
|
8361
|
+
"data-ai-icon": `lucide:${item.icon || "check"}`,
|
|
8362
|
+
...iconAttrs,
|
|
8363
|
+
style: { display: "inline-flex", flexShrink: 0 },
|
|
8364
|
+
children: /* @__PURE__ */ jsx(Icon, { size: 20, color: iconColor })
|
|
8365
|
+
}
|
|
8366
|
+
),
|
|
8078
8367
|
item.html ? /* @__PURE__ */ jsx(
|
|
8079
8368
|
"span",
|
|
8080
8369
|
{
|
|
@@ -18317,222 +18606,6 @@ function AiMediaStripOverlay({
|
|
|
18317
18606
|
// src/ui/ai-section/AiPricingCardOverlay.tsx
|
|
18318
18607
|
import { useCallback as useCallback4, useEffect as useEffect7, useLayoutEffect as useLayoutEffect4, useRef as useRef6, useState as useState9 } from "react";
|
|
18319
18608
|
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
18609
|
import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
18537
18610
|
var PRIMARY4 = "var(--ohw-primary, #0885FE)";
|
|
18538
18611
|
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 +23737,23 @@ function currentIconRef(el) {
|
|
|
23664
23737
|
);
|
|
23665
23738
|
return named ? `lucide:${named.slice("lucide-".length)}` : "";
|
|
23666
23739
|
}
|
|
23740
|
+
function requestValidateTree5(postToParent2, tree) {
|
|
23741
|
+
return new Promise((resolve) => {
|
|
23742
|
+
const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
23743
|
+
const timeout = window.setTimeout(() => {
|
|
23744
|
+
window.removeEventListener("message", onMessage);
|
|
23745
|
+
resolve({ ok: false, reasons: ["Validation request timed out."] });
|
|
23746
|
+
}, 8e3);
|
|
23747
|
+
function onMessage(e) {
|
|
23748
|
+
if (e.data?.type !== "ow:ai-validate-tree-result" || e.data.id !== id) return;
|
|
23749
|
+
window.clearTimeout(timeout);
|
|
23750
|
+
window.removeEventListener("message", onMessage);
|
|
23751
|
+
resolve({ ok: e.data.ok === true, reasons: Array.isArray(e.data.reasons) ? e.data.reasons : [] });
|
|
23752
|
+
}
|
|
23753
|
+
window.addEventListener("message", onMessage);
|
|
23754
|
+
postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
|
|
23755
|
+
});
|
|
23756
|
+
}
|
|
23667
23757
|
function getEditMeasureEl(editable) {
|
|
23668
23758
|
return editable.closest("[data-ohw-href-key]") ?? editable;
|
|
23669
23759
|
}
|
|
@@ -28015,10 +28105,14 @@ function OhhwellsBridge() {
|
|
|
28015
28105
|
e.preventDefault();
|
|
28016
28106
|
e.stopPropagation();
|
|
28017
28107
|
aiSectionApiRef.current?.selectFromElement(editable);
|
|
28108
|
+
const aiIcon = editable.hasAttribute("data-ai-icon");
|
|
28109
|
+
const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
|
|
28018
28110
|
postToParentRef.current({
|
|
28019
28111
|
type: "ow:icon-pick",
|
|
28020
28112
|
key: editable.dataset.ohwKey ?? "",
|
|
28021
|
-
current: currentIconRef(editable)
|
|
28113
|
+
current: currentIconRef(editable),
|
|
28114
|
+
...aiIcon ? { iconMode: "name" } : {},
|
|
28115
|
+
...featureIcon ? { options: [...PRICING_FEATURE_ICONS] } : {}
|
|
28022
28116
|
});
|
|
28023
28117
|
return;
|
|
28024
28118
|
}
|
|
@@ -29265,15 +29359,43 @@ function OhhwellsBridge() {
|
|
|
29265
29359
|
if (e.data?.type !== "ow:icon-markup") return;
|
|
29266
29360
|
const { key, markup } = e.data;
|
|
29267
29361
|
if (!key || typeof markup !== "string") return;
|
|
29268
|
-
const targets =
|
|
29269
|
-
`[data-ohw-key="${key}"][data-ohw-editable="icon"]`
|
|
29270
|
-
);
|
|
29362
|
+
const targets = Array.from(
|
|
29363
|
+
document.querySelectorAll(`[data-ohw-key="${key}"][data-ohw-editable="icon"]`)
|
|
29364
|
+
).filter((el) => !el.hasAttribute("data-ai-icon"));
|
|
29271
29365
|
if (!targets.length) return;
|
|
29272
29366
|
targets.forEach((el) => {
|
|
29273
29367
|
applyIconMarkup(el, markup);
|
|
29274
29368
|
});
|
|
29275
29369
|
postToParentRef.current({ type: "ow:change", nodes: [{ key, text: markup }], flush: true });
|
|
29276
29370
|
};
|
|
29371
|
+
const handleIconName = (e) => {
|
|
29372
|
+
if (e.data?.type !== "ow:icon-name") return;
|
|
29373
|
+
const { key, icon, url } = e.data;
|
|
29374
|
+
const isName = typeof icon === "string" && /^[a-z0-9-]+$/.test(icon);
|
|
29375
|
+
const isUpload = !isName && typeof url === "string" && /^https?:\/\/\S+$/i.test(url);
|
|
29376
|
+
if (!key || !isName && !isUpload) return;
|
|
29377
|
+
const state = parseAiSectionsState(aiSectionsRef.current);
|
|
29378
|
+
const entry = state.sections.find((s) => key.startsWith(aiSlotKeyPrefixFor(s.id)));
|
|
29379
|
+
if (!entry) return;
|
|
29380
|
+
const path = key.slice(aiSlotKeyPrefixFor(entry.id).length);
|
|
29381
|
+
const uploadRef = isUpload ? `icon-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}` : null;
|
|
29382
|
+
const nextTree = uploadRef ? applyIconUploadToTree(entry.tree, path, uploadRef) : applyIconNameToTree(entry.tree, path, icon);
|
|
29383
|
+
if (!nextTree) return;
|
|
29384
|
+
void requestValidateTree5(postToParentRef.current, nextTree).then(({ ok }) => {
|
|
29385
|
+
if (!ok) return;
|
|
29386
|
+
applyTreeLocalRef.current?.({
|
|
29387
|
+
id: entry.id,
|
|
29388
|
+
mode: "replace",
|
|
29389
|
+
targetSectionId: entry.id,
|
|
29390
|
+
tree: nextTree,
|
|
29391
|
+
label: entry.label,
|
|
29392
|
+
media: uploadRef ? { ...entry.media, [uploadRef]: url } : entry.media,
|
|
29393
|
+
...entry.brand ? { brand: entry.brand } : {},
|
|
29394
|
+
// The user is looking at the icon they just changed — no need to recenter on it.
|
|
29395
|
+
skipScrollIntoView: true
|
|
29396
|
+
});
|
|
29397
|
+
});
|
|
29398
|
+
};
|
|
29277
29399
|
const handleImageUrl = (e) => {
|
|
29278
29400
|
if (e.data?.type !== "ow:image-url") return;
|
|
29279
29401
|
const { key, url } = e.data;
|
|
@@ -30722,6 +30844,7 @@ function OhhwellsBridge() {
|
|
|
30722
30844
|
window.addEventListener("message", handleSocialCancel);
|
|
30723
30845
|
window.addEventListener("message", handleSocialUpdate);
|
|
30724
30846
|
window.addEventListener("message", handleIconMarkup);
|
|
30847
|
+
window.addEventListener("message", handleIconName);
|
|
30725
30848
|
window.addEventListener("message", handleImageUrl);
|
|
30726
30849
|
window.addEventListener("message", handleImageUploading);
|
|
30727
30850
|
window.addEventListener("message", handleCarouselChange);
|
|
@@ -30806,6 +30929,7 @@ function OhhwellsBridge() {
|
|
|
30806
30929
|
window.removeEventListener("message", handleSocialCancel);
|
|
30807
30930
|
window.removeEventListener("message", handleSocialUpdate);
|
|
30808
30931
|
window.removeEventListener("message", handleIconMarkup);
|
|
30932
|
+
window.removeEventListener("message", handleIconName);
|
|
30809
30933
|
window.removeEventListener("message", handleImageUrl);
|
|
30810
30934
|
window.removeEventListener("message", handleImageUploading);
|
|
30811
30935
|
window.removeEventListener("message", handleCarouselChange);
|
|
@@ -31131,7 +31255,7 @@ function OhhwellsBridge() {
|
|
|
31131
31255
|
postToParent2({
|
|
31132
31256
|
type: "ow:ready",
|
|
31133
31257
|
version: "1",
|
|
31134
|
-
bridgeVersion: "0.1.
|
|
31258
|
+
bridgeVersion: "0.1.142",
|
|
31135
31259
|
path: pathname,
|
|
31136
31260
|
nodes: collectEditableNodes(editContentRef.current),
|
|
31137
31261
|
sections
|