@ohhwells/bridge 0.1.54-next.157 → 0.1.55-next.158
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 +148 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +148 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -245,6 +245,76 @@ function applyBrandChrome(content) {
|
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
// src/lib/brand-kit.ts
|
|
249
|
+
var BRAND_KIT_KEY = "__ohw_brand";
|
|
250
|
+
var BRAND_VAR_PREFIX = "--ohw-brand-";
|
|
251
|
+
var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
|
|
252
|
+
(role) => `${BRAND_VAR_PREFIX}${role}`
|
|
253
|
+
);
|
|
254
|
+
var FONT_VARS = { heading: ["--font-heading", "--font-display"], body: ["--font-body"] };
|
|
255
|
+
var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
|
|
256
|
+
function brandColorVars(kit) {
|
|
257
|
+
const { dark, primary, accent, light } = kit.palette;
|
|
258
|
+
const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
|
|
259
|
+
return {
|
|
260
|
+
[`${BRAND_VAR_PREFIX}primary`]: primary,
|
|
261
|
+
[`${BRAND_VAR_PREFIX}accent`]: accent,
|
|
262
|
+
[`${BRAND_VAR_PREFIX}light`]: light,
|
|
263
|
+
[`${BRAND_VAR_PREFIX}dark`]: dark,
|
|
264
|
+
[`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
|
|
265
|
+
[`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
|
|
266
|
+
[`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function parseBrandKit(raw) {
|
|
270
|
+
if (!raw) return null;
|
|
271
|
+
try {
|
|
272
|
+
const parsed = JSON.parse(raw);
|
|
273
|
+
const p = parsed?.palette;
|
|
274
|
+
const f = parsed?.fonts;
|
|
275
|
+
if (!p || !f || typeof p.dark !== "string" || typeof p.primary !== "string" || typeof p.accent !== "string" || typeof p.light !== "string" || typeof f.heading !== "string" || typeof f.body !== "string") {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
|
|
280
|
+
fonts: { heading: f.heading, body: f.body }
|
|
281
|
+
};
|
|
282
|
+
} catch {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function familyOf(stack) {
|
|
287
|
+
const first = stack.split(",")[0]?.trim() ?? "";
|
|
288
|
+
return first.replace(/^['"]|['"]$/g, "");
|
|
289
|
+
}
|
|
290
|
+
function loadBrandFonts(families) {
|
|
291
|
+
const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
|
|
292
|
+
if (unique.length === 0) return;
|
|
293
|
+
const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
|
|
294
|
+
const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
|
|
295
|
+
let link = document.getElementById(BRAND_FONT_LINK_ID);
|
|
296
|
+
if (!link) {
|
|
297
|
+
link = document.createElement("link");
|
|
298
|
+
link.id = BRAND_FONT_LINK_ID;
|
|
299
|
+
link.rel = "stylesheet";
|
|
300
|
+
document.head.appendChild(link);
|
|
301
|
+
}
|
|
302
|
+
if (link.href !== href) link.href = href;
|
|
303
|
+
}
|
|
304
|
+
function applyBrandToDom(kit) {
|
|
305
|
+
const root = document.documentElement;
|
|
306
|
+
if (!kit) {
|
|
307
|
+
for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
|
|
308
|
+
for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
|
|
309
|
+
document.getElementById(BRAND_FONT_LINK_ID)?.remove();
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
|
|
313
|
+
for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
|
|
314
|
+
for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
|
|
315
|
+
loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
|
|
316
|
+
}
|
|
317
|
+
|
|
248
318
|
// src/ui/ai-tree/aiSectionsManager.tsx
|
|
249
319
|
var import_react_dom = require("react-dom");
|
|
250
320
|
var import_client = require("react-dom/client");
|
|
@@ -1331,17 +1401,34 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
|
1331
1401
|
var CONTAINER_ATTR = "data-ohw-ai-generated";
|
|
1332
1402
|
var REPLACED_ATTR = "data-ohw-ai-replaced-by";
|
|
1333
1403
|
var REMOVED_ATTR = "data-ohw-ai-removed";
|
|
1404
|
+
function readRootVar(name) {
|
|
1405
|
+
if (typeof document === "undefined") return "";
|
|
1406
|
+
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
1407
|
+
}
|
|
1408
|
+
function deriveBrandOverride() {
|
|
1409
|
+
const dark = readRootVar("--ohw-brand-dark");
|
|
1410
|
+
const primary = readRootVar("--ohw-brand-primary");
|
|
1411
|
+
const light = readRootVar("--ohw-brand-light");
|
|
1412
|
+
if (!dark || !primary || !light) return null;
|
|
1413
|
+
const accent = readRootVar("--ohw-brand-accent");
|
|
1414
|
+
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
1415
|
+
const body = readRootVar("--font-body");
|
|
1416
|
+
return {
|
|
1417
|
+
palette: { dark, primary, accent: accent || dark, light },
|
|
1418
|
+
fonts: {
|
|
1419
|
+
heading: heading || AI_DEFAULT_BRAND.fonts.heading,
|
|
1420
|
+
body: body || AI_DEFAULT_BRAND.fonts.body
|
|
1421
|
+
}
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1334
1424
|
function deriveTemplateBrand() {
|
|
1335
|
-
|
|
1336
|
-
const
|
|
1337
|
-
const
|
|
1338
|
-
const dark = read("--color-dark");
|
|
1339
|
-
const primary = read("--color-primary");
|
|
1340
|
-
const light = read("--color-light");
|
|
1425
|
+
const dark = readRootVar("--color-dark");
|
|
1426
|
+
const primary = readRootVar("--color-primary");
|
|
1427
|
+
const light = readRootVar("--color-light");
|
|
1341
1428
|
if (!dark || !primary || !light) return null;
|
|
1342
|
-
const accent =
|
|
1343
|
-
const heading =
|
|
1344
|
-
const body =
|
|
1429
|
+
const accent = readRootVar("--color-accent");
|
|
1430
|
+
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
1431
|
+
const body = readRootVar("--font-body");
|
|
1345
1432
|
return {
|
|
1346
1433
|
palette: { dark, primary, accent: accent || dark, light },
|
|
1347
1434
|
fonts: {
|
|
@@ -1433,7 +1520,9 @@ function syncReplacedOriginals(state) {
|
|
|
1433
1520
|
}
|
|
1434
1521
|
function applyAiSectionsToDom(state, options) {
|
|
1435
1522
|
if (typeof document === "undefined") return;
|
|
1523
|
+
const brandOverride = deriveBrandOverride();
|
|
1436
1524
|
const templateBrand = deriveTemplateBrand();
|
|
1525
|
+
const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
|
|
1437
1526
|
const activeIds = new Set(state.sections.map((entry) => entry.id));
|
|
1438
1527
|
for (const [id, section] of mounted) {
|
|
1439
1528
|
if (!activeIds.has(id)) {
|
|
@@ -1443,7 +1532,7 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1443
1532
|
}
|
|
1444
1533
|
}
|
|
1445
1534
|
for (const entry of state.sections) {
|
|
1446
|
-
const serialized = JSON.stringify(entry);
|
|
1535
|
+
const serialized = JSON.stringify(entry) + brandKey;
|
|
1447
1536
|
const existing = mounted.get(entry.id);
|
|
1448
1537
|
if (existing && existing.serialized === serialized && existing.container.isConnected) {
|
|
1449
1538
|
continue;
|
|
@@ -1467,7 +1556,7 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1467
1556
|
AiTreeRenderer,
|
|
1468
1557
|
{
|
|
1469
1558
|
tree: entry.tree,
|
|
1470
|
-
brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
1559
|
+
brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
1471
1560
|
resolveMedia,
|
|
1472
1561
|
editKeyPrefix: `ai.${entry.id}`
|
|
1473
1562
|
}
|
|
@@ -14102,6 +14191,7 @@ function OhhwellsBridge() {
|
|
|
14102
14191
|
const addNavAfterAnchorRef = (0, import_react16.useRef)(null);
|
|
14103
14192
|
const editContentRef = (0, import_react16.useRef)({});
|
|
14104
14193
|
const aiSectionsRef = (0, import_react16.useRef)("");
|
|
14194
|
+
const brandKitRef = (0, import_react16.useRef)("");
|
|
14105
14195
|
const pendingDeleteUndoRef = (0, import_react16.useRef)(null);
|
|
14106
14196
|
const [floatingPanel, setFloatingPanel] = (0, import_react16.useState)(null);
|
|
14107
14197
|
const floatingPanelOpenRef = (0, import_react16.useRef)(false);
|
|
@@ -15348,12 +15438,17 @@ function OhhwellsBridge() {
|
|
|
15348
15438
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
15349
15439
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
15350
15440
|
}
|
|
15441
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
15442
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
15443
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
15444
|
+
}
|
|
15351
15445
|
applyBrandChrome(content);
|
|
15352
15446
|
for (const [key, val] of Object.entries(content)) {
|
|
15353
15447
|
if (key === "__ohw_sections") continue;
|
|
15354
15448
|
if (key === AI_SECTIONS_KEY) continue;
|
|
15355
15449
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
15356
15450
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
15451
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
15357
15452
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
15358
15453
|
if (applyVideoSettingNode(key, val)) continue;
|
|
15359
15454
|
if (applyCarouselNode(key, val)) continue;
|
|
@@ -15436,10 +15531,14 @@ function OhhwellsBridge() {
|
|
|
15436
15531
|
observer?.disconnect();
|
|
15437
15532
|
try {
|
|
15438
15533
|
applyBrandChrome(content);
|
|
15534
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
15535
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
15536
|
+
}
|
|
15439
15537
|
for (const [key, val] of Object.entries(content)) {
|
|
15440
15538
|
if (key === "__ohw_sections") continue;
|
|
15441
15539
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
15442
15540
|
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
15541
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
15443
15542
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
15444
15543
|
if (applyVideoSettingNode(key, val)) continue;
|
|
15445
15544
|
if (applyCarouselNode(key, val)) continue;
|
|
@@ -15580,26 +15679,31 @@ function OhhwellsBridge() {
|
|
|
15580
15679
|
}, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
|
|
15581
15680
|
(0, import_react16.useEffect)(() => {
|
|
15582
15681
|
if (!isEditMode) return;
|
|
15682
|
+
let lastPosted = 0;
|
|
15583
15683
|
const measure = () => {
|
|
15584
15684
|
const h = document.body.scrollHeight;
|
|
15585
|
-
if (h > 50
|
|
15685
|
+
if (h > 50 && Math.abs(h - lastPosted) > 1) {
|
|
15686
|
+
lastPosted = h;
|
|
15687
|
+
postToParent2({ type: "ow:height", height: h });
|
|
15688
|
+
}
|
|
15689
|
+
};
|
|
15690
|
+
let raf = null;
|
|
15691
|
+
const schedule = () => {
|
|
15692
|
+
if (raf != null) return;
|
|
15693
|
+
raf = requestAnimationFrame(() => {
|
|
15694
|
+
raf = null;
|
|
15695
|
+
measure();
|
|
15696
|
+
});
|
|
15586
15697
|
};
|
|
15587
15698
|
const t1 = setTimeout(measure, 50);
|
|
15588
15699
|
const t2 = setTimeout(measure, 500);
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
const handleResize = () => {
|
|
15592
|
-
if (window.innerWidth === lastWidth) return;
|
|
15593
|
-
lastWidth = window.innerWidth;
|
|
15594
|
-
if (resizeTimer) clearTimeout(resizeTimer);
|
|
15595
|
-
resizeTimer = setTimeout(measure, 150);
|
|
15596
|
-
};
|
|
15597
|
-
window.addEventListener("resize", handleResize);
|
|
15700
|
+
const ro = new ResizeObserver(schedule);
|
|
15701
|
+
ro.observe(document.body);
|
|
15598
15702
|
return () => {
|
|
15599
15703
|
clearTimeout(t1);
|
|
15600
15704
|
clearTimeout(t2);
|
|
15601
|
-
if (
|
|
15602
|
-
|
|
15705
|
+
if (raf != null) cancelAnimationFrame(raf);
|
|
15706
|
+
ro.disconnect();
|
|
15603
15707
|
};
|
|
15604
15708
|
}, [pathname, isEditMode, postToParent2]);
|
|
15605
15709
|
(0, import_react16.useEffect)(() => {
|
|
@@ -16996,6 +17100,10 @@ function OhhwellsBridge() {
|
|
|
16996
17100
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
16997
17101
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
16998
17102
|
}
|
|
17103
|
+
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
17104
|
+
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
17105
|
+
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
17106
|
+
}
|
|
16999
17107
|
applyBrandChrome(content);
|
|
17000
17108
|
let sectionsJson = null;
|
|
17001
17109
|
for (const [key, val] of Object.entries(content)) {
|
|
@@ -17006,6 +17114,7 @@ function OhhwellsBridge() {
|
|
|
17006
17114
|
if (key === AI_SECTIONS_KEY) continue;
|
|
17007
17115
|
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17008
17116
|
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
17117
|
+
if (key === BRAND_KIT_KEY) continue;
|
|
17009
17118
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17010
17119
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17011
17120
|
if (applyCarouselNode(key, val)) continue;
|
|
@@ -17150,6 +17259,17 @@ function OhhwellsBridge() {
|
|
|
17150
17259
|
postAiSectionsChanged();
|
|
17151
17260
|
};
|
|
17152
17261
|
window.addEventListener("message", handleAiSetSections);
|
|
17262
|
+
const handleAiSetBrand = (e) => {
|
|
17263
|
+
if (e.data?.type !== "ow:ai-set-brand") return;
|
|
17264
|
+
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
17265
|
+
const previous = brandKitRef.current;
|
|
17266
|
+
brandKitRef.current = value;
|
|
17267
|
+
applyBrandToDom(parseBrandKit(value));
|
|
17268
|
+
if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
|
|
17269
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
|
|
17270
|
+
postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
|
|
17271
|
+
};
|
|
17272
|
+
window.addEventListener("message", handleAiSetBrand);
|
|
17153
17273
|
const handleDeactivate = (e) => {
|
|
17154
17274
|
if (e.data?.type !== "ow:deactivate") return;
|
|
17155
17275
|
if (Date.now() < linkPopoverGraceUntilRef.current) return;
|
|
@@ -17398,6 +17518,9 @@ function OhhwellsBridge() {
|
|
|
17398
17518
|
if (aiSectionsRef.current) {
|
|
17399
17519
|
nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
|
|
17400
17520
|
}
|
|
17521
|
+
if (brandKitRef.current) {
|
|
17522
|
+
nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
|
|
17523
|
+
}
|
|
17401
17524
|
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
17402
17525
|
};
|
|
17403
17526
|
const handleInsertSection = (e) => {
|
|
@@ -17800,6 +17923,7 @@ function OhhwellsBridge() {
|
|
|
17800
17923
|
window.removeEventListener("message", handleAiApplyTree);
|
|
17801
17924
|
window.removeEventListener("message", handleAiDeleteSection);
|
|
17802
17925
|
window.removeEventListener("message", handleAiSetSections);
|
|
17926
|
+
window.removeEventListener("message", handleAiSetBrand);
|
|
17803
17927
|
window.removeEventListener("message", handleDeactivate);
|
|
17804
17928
|
window.removeEventListener("message", handleToastAction);
|
|
17805
17929
|
window.removeEventListener("message", handleUiEscape);
|
|
@@ -18003,7 +18127,7 @@ function OhhwellsBridge() {
|
|
|
18003
18127
|
postToParent2({
|
|
18004
18128
|
type: "ow:ready",
|
|
18005
18129
|
version: "1",
|
|
18006
|
-
bridgeVersion: "0.1.
|
|
18130
|
+
bridgeVersion: "0.1.55",
|
|
18007
18131
|
path: pathname,
|
|
18008
18132
|
nodes: collectEditableNodes(editContentRef.current),
|
|
18009
18133
|
sections
|