@ohhwells/bridge 0.1.152 → 0.1.153-next.417
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 +1188 -882
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -1
- package/dist/index.d.ts +31 -1
- package/dist/index.js +1251 -931
- package/dist/index.js.map +1 -1
- package/dist/pages.cjs +1 -1
- package/dist/pages.cjs.map +1 -1
- package/dist/pages.js +1 -1
- package/dist/pages.js.map +1 -1
- package/dist/styles.css +116 -0
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -83,6 +83,7 @@ __export(index_exports, {
|
|
|
83
83
|
foldAlignIntoTrees: () => foldAlignIntoTrees,
|
|
84
84
|
getEditModeInitialState: () => getEditModeInitialState,
|
|
85
85
|
hasMountedContentNodes: () => hasMountedContentNodes,
|
|
86
|
+
indexCollectionItems: () => indexCollectionItems,
|
|
86
87
|
insertFooterHours: () => insertFooterHours,
|
|
87
88
|
isEditSessionActive: () => isEditSessionActive,
|
|
88
89
|
isPlainLeftClick: () => isPlainLeftClick,
|
|
@@ -90,6 +91,7 @@ __export(index_exports, {
|
|
|
90
91
|
isRenderableTree: () => isRenderableTree,
|
|
91
92
|
isStickySearchParam: () => isStickySearchParam,
|
|
92
93
|
isValidUrl: () => isValidUrl,
|
|
94
|
+
matchCollectionItemClick: () => matchCollectionItemClick,
|
|
93
95
|
nextViableSlide: () => nextViableSlide,
|
|
94
96
|
parseAiFooterState: () => parseAiFooterState,
|
|
95
97
|
parseAiSectionsState: () => parseAiSectionsState,
|
|
@@ -6652,6 +6654,21 @@ function pricingCardFeatureCount(tree, index) {
|
|
|
6652
6654
|
const target = findPricingCollection(tree)?.children?.[index];
|
|
6653
6655
|
return target ? pricingFeatureList(target).length : 0;
|
|
6654
6656
|
}
|
|
6657
|
+
function prevailingFeatureIcon(features) {
|
|
6658
|
+
const counts = /* @__PURE__ */ new Map();
|
|
6659
|
+
for (const row of features) {
|
|
6660
|
+
const key = `${row.icon ?? ""}|${row.iconColor ?? ""}`;
|
|
6661
|
+
const entry = counts.get(key) ?? { count: 0, ...row.icon ? { icon: row.icon } : {}, ...row.iconColor ? { iconColor: row.iconColor } : {} };
|
|
6662
|
+
entry.count += 1;
|
|
6663
|
+
counts.set(key, entry);
|
|
6664
|
+
}
|
|
6665
|
+
let best = null;
|
|
6666
|
+
for (const entry of counts.values()) {
|
|
6667
|
+
if (!best || entry.count > best.count) best = entry;
|
|
6668
|
+
}
|
|
6669
|
+
if (!best) return {};
|
|
6670
|
+
return { ...best.icon ? { icon: best.icon } : {}, ...best.iconColor ? { iconColor: best.iconColor } : {} };
|
|
6671
|
+
}
|
|
6655
6672
|
function addPricingCardFeature(tree, index, text) {
|
|
6656
6673
|
return withPricingCollection(tree, (collection) => {
|
|
6657
6674
|
const children = collection.children;
|
|
@@ -6660,7 +6677,7 @@ function addPricingCardFeature(tree, index, text) {
|
|
|
6660
6677
|
const features = pricingFeatureList(target);
|
|
6661
6678
|
if (features.length >= PRICING_FEATURES_MAX) return false;
|
|
6662
6679
|
target.slots = target.slots ?? {};
|
|
6663
|
-
target.slots.features = [...features, { text }];
|
|
6680
|
+
target.slots.features = [...features, { text, ...prevailingFeatureIcon(features) }];
|
|
6664
6681
|
return true;
|
|
6665
6682
|
});
|
|
6666
6683
|
}
|
|
@@ -7271,6 +7288,21 @@ function applyCustomCodeToHead(content) {
|
|
|
7271
7288
|
if (beforeNodes.length > 0) document.head.append(...beforeNodes);
|
|
7272
7289
|
}
|
|
7273
7290
|
|
|
7291
|
+
// src/lib/selectNavContainer.ts
|
|
7292
|
+
function isOnScreen(el) {
|
|
7293
|
+
const rect = el.getBoundingClientRect();
|
|
7294
|
+
if (rect.width <= 0 || rect.height <= 0) return false;
|
|
7295
|
+
if (el.offsetParent !== null) return true;
|
|
7296
|
+
return getComputedStyle(el).position === "fixed";
|
|
7297
|
+
}
|
|
7298
|
+
function findSelectableNavContainer(doc = document) {
|
|
7299
|
+
const visible = Array.from(doc.querySelectorAll("[data-ohw-nav-container]")).filter(isOnScreen);
|
|
7300
|
+
if (visible.length === 0) return null;
|
|
7301
|
+
return visible.reduce(
|
|
7302
|
+
(best, el) => el.getBoundingClientRect().top < best.getBoundingClientRect().top ? el : best
|
|
7303
|
+
);
|
|
7304
|
+
}
|
|
7305
|
+
|
|
7274
7306
|
// src/ui/ai-tree/aiSectionsManager.tsx
|
|
7275
7307
|
var import_react_dom = require("react-dom");
|
|
7276
7308
|
var import_client = require("react-dom/client");
|
|
@@ -7844,7 +7876,7 @@ var AI_MOBILE_CSS = [
|
|
|
7844
7876
|
// page's first section. Top only (bottom needs no nav clearance); ~96px approximates the
|
|
7845
7877
|
// template nav's own height (measured ~94px) — there's no shared `--ohw-header-h` var to read
|
|
7846
7878
|
// an exact figure from (no live measurement exists), so this is a safe approximation, not a
|
|
7847
|
-
// pixel-exact fit. Horizontal spacing already comes from `data-ai-
|
|
7879
|
+
// pixel-exact fit. Horizontal spacing already comes from `data-ai-section-inner`.
|
|
7848
7880
|
'[data-ai-overlay-clearance] [data-ai-overlap-layer="overlay"]{padding-top:96px}',
|
|
7849
7881
|
// A full-bleed carousel's height is pure arithmetic: it spans the whole window, so 16/9 turns
|
|
7850
7882
|
// window width straight into height. Measured at 16/9 it came out ~90% of the viewport at EVERY
|
|
@@ -7860,8 +7892,10 @@ var AI_MOBILE_CSS = [
|
|
|
7860
7892
|
"[data-ai-section]{overflow-x:hidden}",
|
|
7861
7893
|
// `:not([data-ai-full-bleed])`: this `!important` gutter beat the inline `padding: 0` that a
|
|
7862
7894
|
// full-bleed section's container sets, so a carousel/media hero that is edge-to-edge on desktop
|
|
7863
|
-
// still got a 20px band of section background down both sides on a phone.
|
|
7864
|
-
|
|
7895
|
+
// still got a 20px band of section background down both sides on a phone. The footer's own
|
|
7896
|
+
// container keeps the unguarded rule below — `data-ai-full-bleed` can never appear on it.
|
|
7897
|
+
"[data-ai-section-inner]:not([data-ai-full-bleed]){padding:0 20px !important}",
|
|
7898
|
+
"[data-ai-container]{padding:0 20px !important}",
|
|
7865
7899
|
"[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
|
|
7866
7900
|
"[data-ai-cell]{width:100%;min-width:0}",
|
|
7867
7901
|
// The rule above turns every row into a COLUMN flex container on phones, which silently breaks
|
|
@@ -8066,6 +8100,31 @@ function pricingCardEmphasis(featured, ctx, hasBg) {
|
|
|
8066
8100
|
function textAttrs(ctx, path) {
|
|
8067
8101
|
return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
|
|
8068
8102
|
}
|
|
8103
|
+
var AI_RESPONSIVE_CSS = [
|
|
8104
|
+
"@media (max-width: 960px) {",
|
|
8105
|
+
" [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
|
|
8106
|
+
' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
|
|
8107
|
+
"}",
|
|
8108
|
+
"@media (max-width: 640px) {",
|
|
8109
|
+
" [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
|
|
8110
|
+
" [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
|
|
8111
|
+
// Group containers flatten to a column on phones; span placements come along for free.
|
|
8112
|
+
// `:not([data-ai-group="overlap"])` for the same reason as the twin pair in AI_MOBILE_CSS — see
|
|
8113
|
+
// the comment there. This sheet's copy is the narrower selector, so without the exemption it
|
|
8114
|
+
// flattens an `overlap` hero below 640px even though the other rule stands down: measured a 390px
|
|
8115
|
+
// viewport with the heading/CTA in flow *under* the image, 219px of section background where the
|
|
8116
|
+
// layered text used to be.
|
|
8117
|
+
' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) { display: flex !important; flex-direction: column !important; }',
|
|
8118
|
+
' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) > * { grid-column: auto !important; }',
|
|
8119
|
+
// `:not([data-ai-full-bleed])` for the same reason as the twin rule in AI_MOBILE_CSS: this one
|
|
8120
|
+
// is narrower (0,2,0 vs 0,1,0) and also `!important`, so without the guard it wins outright and
|
|
8121
|
+
// a full-bleed hero gets its 20px side band back below 640px only — the one width where the
|
|
8122
|
+
// other rule had already been taught to stand down.
|
|
8123
|
+
" [data-ai-responsive] [data-ai-section-inner]:not([data-ai-full-bleed]) { padding-left: 20px !important; padding-right: 20px !important; }",
|
|
8124
|
+
" [data-ai-responsive] { overflow-x: hidden; }",
|
|
8125
|
+
" [data-ai-responsive] img { max-width: 100%; }",
|
|
8126
|
+
"}"
|
|
8127
|
+
].join("\n");
|
|
8069
8128
|
var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
|
8070
8129
|
function MediaBox({
|
|
8071
8130
|
refValue,
|
|
@@ -8120,7 +8179,7 @@ function MediaBox({
|
|
|
8120
8179
|
position: fill ? "absolute" : void 0,
|
|
8121
8180
|
inset: fill ? 0 : void 0,
|
|
8122
8181
|
width: fill ? "100%" : fixedSize ? "auto" : "100%",
|
|
8123
|
-
height: fill ? "100%" : void 0,
|
|
8182
|
+
height: fill ? "100%" : fixedSize ? height : void 0,
|
|
8124
8183
|
aspectRatio: fill ? void 0 : aspectRatio,
|
|
8125
8184
|
minHeight: fill || fixedSize ? void 0 : height ?? (aspectRatio ? void 0 : 180),
|
|
8126
8185
|
borderRadius: radius,
|
|
@@ -9616,7 +9675,7 @@ function CollectionBlock({ node, ctx, path }) {
|
|
|
9616
9675
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
9617
9676
|
"div",
|
|
9618
9677
|
{
|
|
9619
|
-
"data-ai-grid":
|
|
9678
|
+
"data-ai-grid": String(itemsPerRow),
|
|
9620
9679
|
"data-ohw-pricing-collection": isPricing ? "" : void 0,
|
|
9621
9680
|
style: {
|
|
9622
9681
|
display: "grid",
|
|
@@ -9918,7 +9977,7 @@ function renderNode(node, ctx, path, ambientAlign) {
|
|
|
9918
9977
|
},
|
|
9919
9978
|
children: [
|
|
9920
9979
|
i > 0 && ctx.heroOverlapFullBleed ? (
|
|
9921
|
-
// The section's `data-ai-
|
|
9980
|
+
// The section's `data-ai-section-inner` gave up its max-width/gutter so the base
|
|
9922
9981
|
// layer (this group's i===0 sibling) can span edge-to-edge — reintroduced here,
|
|
9923
9982
|
// scoped to just the overlay text, so a heading/copy/CTA stack doesn't stretch
|
|
9924
9983
|
// the full section width instead of reading at the usual content width.
|
|
@@ -10337,6 +10396,7 @@ function AiTreeRenderer({
|
|
|
10337
10396
|
...fillHeight ? { "data-ai-fill-height": "" } : {},
|
|
10338
10397
|
...needsOverlayClearance ? { "data-ai-overlay-clearance": "" } : {},
|
|
10339
10398
|
...bgAttrs,
|
|
10399
|
+
"data-ai-responsive": "",
|
|
10340
10400
|
style: {
|
|
10341
10401
|
position: "relative",
|
|
10342
10402
|
padding: `${padCss} 0`,
|
|
@@ -10350,12 +10410,13 @@ function AiTreeRenderer({
|
|
|
10350
10410
|
...fillHeight ? { display: "grid" } : {}
|
|
10351
10411
|
},
|
|
10352
10412
|
children: [
|
|
10353
|
-
|
|
10413
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
|
|
10354
10414
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
|
|
10415
|
+
isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
|
|
10355
10416
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
10356
10417
|
"div",
|
|
10357
10418
|
{
|
|
10358
|
-
"data-ai-
|
|
10419
|
+
"data-ai-section-inner": "",
|
|
10359
10420
|
...fullBleedMedia ? { "data-ai-full-bleed": "" } : {},
|
|
10360
10421
|
style: {
|
|
10361
10422
|
position: "relative",
|
|
@@ -10372,7 +10433,7 @@ function AiTreeRenderer({
|
|
|
10372
10433
|
children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
10373
10434
|
"div",
|
|
10374
10435
|
{
|
|
10375
|
-
"data-ai-
|
|
10436
|
+
"data-ai-columns": "",
|
|
10376
10437
|
style: {
|
|
10377
10438
|
display: "grid",
|
|
10378
10439
|
gridTemplateColumns: "repeat(12, 1fr)",
|
|
@@ -11034,9 +11095,10 @@ function applyAiSectionsToDom(state, options) {
|
|
|
11034
11095
|
if (existing && existing.serialized === serialized && existing.container.isConnected) {
|
|
11035
11096
|
continue;
|
|
11036
11097
|
}
|
|
11037
|
-
const isNewMount = !existing || !existing.container.isConnected;
|
|
11038
11098
|
let container = existing?.container;
|
|
11099
|
+
let isNewMount = false;
|
|
11039
11100
|
if (!container || !container.isConnected) {
|
|
11101
|
+
isNewMount = true;
|
|
11040
11102
|
existing?.root.unmount();
|
|
11041
11103
|
container?.remove();
|
|
11042
11104
|
container = document.createElement("div");
|
|
@@ -11680,6 +11742,172 @@ function useLinkHrefGuardian(...deps) {
|
|
|
11680
11742
|
}, deps);
|
|
11681
11743
|
}
|
|
11682
11744
|
|
|
11745
|
+
// src/ui/link-modal/linkModal.utils.ts
|
|
11746
|
+
function parseTarget(target) {
|
|
11747
|
+
if (!target) return { pageRoute: "", sectionId: null };
|
|
11748
|
+
const hashIndex = target.indexOf("#");
|
|
11749
|
+
if (hashIndex === -1) return { pageRoute: target, sectionId: null };
|
|
11750
|
+
return {
|
|
11751
|
+
pageRoute: target.slice(0, hashIndex),
|
|
11752
|
+
sectionId: target.slice(hashIndex + 1) || null
|
|
11753
|
+
};
|
|
11754
|
+
}
|
|
11755
|
+
function normalizePath(path) {
|
|
11756
|
+
const trimmed = path.trim();
|
|
11757
|
+
if (!trimmed) return "/";
|
|
11758
|
+
try {
|
|
11759
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
11760
|
+
return new URL(trimmed).pathname || "/";
|
|
11761
|
+
}
|
|
11762
|
+
} catch {
|
|
11763
|
+
}
|
|
11764
|
+
const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
11765
|
+
if (withSlash.length > 1 && withSlash.endsWith("/")) return withSlash.slice(0, -1);
|
|
11766
|
+
return withSlash;
|
|
11767
|
+
}
|
|
11768
|
+
function findPageByPath(pages, path) {
|
|
11769
|
+
const normalized = normalizePath(path);
|
|
11770
|
+
return pages.find((p) => normalizePath(p.path) === normalized);
|
|
11771
|
+
}
|
|
11772
|
+
function inferPageTitleFromPath(path) {
|
|
11773
|
+
const normalized = normalizePath(path);
|
|
11774
|
+
if (normalized === "/") return "Home";
|
|
11775
|
+
const segment = normalized.split("/").filter(Boolean).pop() ?? "";
|
|
11776
|
+
if (!segment) return "Home";
|
|
11777
|
+
return segment.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
11778
|
+
}
|
|
11779
|
+
function isInternalPath(route) {
|
|
11780
|
+
const trimmed = route.trim();
|
|
11781
|
+
if (!trimmed) return false;
|
|
11782
|
+
if (/^https?:\/\//i.test(trimmed)) return false;
|
|
11783
|
+
if (trimmed.startsWith("/")) return true;
|
|
11784
|
+
if (trimmed.includes(" ") || trimmed.includes(".")) return false;
|
|
11785
|
+
return true;
|
|
11786
|
+
}
|
|
11787
|
+
function resolvePage(route, pages) {
|
|
11788
|
+
const found = findPageByPath(pages, route);
|
|
11789
|
+
if (found) return { path: found.path, title: found.title };
|
|
11790
|
+
return { path: normalizePath(route), title: inferPageTitleFromPath(route) };
|
|
11791
|
+
}
|
|
11792
|
+
function getSectionsForPath(sectionsByPath, pageRoute) {
|
|
11793
|
+
if (!sectionsByPath) return [];
|
|
11794
|
+
const normalized = normalizePath(pageRoute);
|
|
11795
|
+
return sectionsByPath[normalized] ?? sectionsByPath[pageRoute] ?? [];
|
|
11796
|
+
}
|
|
11797
|
+
function buildTarget(page, section) {
|
|
11798
|
+
if (section) return `${page.path}#${section.id}`;
|
|
11799
|
+
return page.path;
|
|
11800
|
+
}
|
|
11801
|
+
function isValidUrl(urlString) {
|
|
11802
|
+
try {
|
|
11803
|
+
const url = new URL(urlString);
|
|
11804
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
11805
|
+
const hostname = url.hostname;
|
|
11806
|
+
if (!hostname || hostname.length === 0) return false;
|
|
11807
|
+
if (hostname.includes(" ")) return false;
|
|
11808
|
+
if (hostname === "localhost") return true;
|
|
11809
|
+
if (!hostname.includes(".")) return false;
|
|
11810
|
+
if (!/[a-z]/i.test(hostname)) return false;
|
|
11811
|
+
return true;
|
|
11812
|
+
} catch {
|
|
11813
|
+
return false;
|
|
11814
|
+
}
|
|
11815
|
+
}
|
|
11816
|
+
function normalizeUrl(value) {
|
|
11817
|
+
const trimmed = value.trim();
|
|
11818
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) return trimmed;
|
|
11819
|
+
return `https://${trimmed}`;
|
|
11820
|
+
}
|
|
11821
|
+
function validateUrlInput(value, pages, allPages) {
|
|
11822
|
+
const trimmed = value.trim();
|
|
11823
|
+
if (!trimmed) return "";
|
|
11824
|
+
const exactPage = allPages.find((p) => p.title.toLowerCase() === trimmed.toLowerCase());
|
|
11825
|
+
if (exactPage) return "";
|
|
11826
|
+
const hasPrefixMatch = pages.some((p) => p.title.toLowerCase().startsWith(trimmed.toLowerCase()));
|
|
11827
|
+
if (hasPrefixMatch) {
|
|
11828
|
+
return "Select a page from the list or enter a valid URL";
|
|
11829
|
+
}
|
|
11830
|
+
const hasScheme = /^https?:\/\//i.test(trimmed);
|
|
11831
|
+
if (!hasScheme && !trimmed.includes(".")) {
|
|
11832
|
+
return "Please enter a valid URL (e.g., https://example.com)";
|
|
11833
|
+
}
|
|
11834
|
+
if (!isValidUrl(normalizeUrl(trimmed))) {
|
|
11835
|
+
return "Please enter a valid URL (e.g., https://example.com)";
|
|
11836
|
+
}
|
|
11837
|
+
return "";
|
|
11838
|
+
}
|
|
11839
|
+
function getEditModeInitialState(target, pages, sections) {
|
|
11840
|
+
const empty = {
|
|
11841
|
+
searchValue: "",
|
|
11842
|
+
selectedPage: null,
|
|
11843
|
+
selectedSection: null,
|
|
11844
|
+
step: "input"
|
|
11845
|
+
};
|
|
11846
|
+
if (!target) return empty;
|
|
11847
|
+
if (isValidUrl(normalizeUrl(target))) {
|
|
11848
|
+
return { searchValue: target, selectedPage: null, selectedSection: null, step: "input" };
|
|
11849
|
+
}
|
|
11850
|
+
const { pageRoute, sectionId } = parseTarget(target);
|
|
11851
|
+
if (sectionId) {
|
|
11852
|
+
if (isInternalPath(pageRoute)) {
|
|
11853
|
+
const page = resolvePage(pageRoute, pages);
|
|
11854
|
+
const sec = sections.find((s) => s.id === sectionId);
|
|
11855
|
+
return {
|
|
11856
|
+
searchValue: page.title,
|
|
11857
|
+
selectedPage: page,
|
|
11858
|
+
selectedSection: sec ? { id: sec.id, label: sec.label } : null,
|
|
11859
|
+
step: "input"
|
|
11860
|
+
};
|
|
11861
|
+
}
|
|
11862
|
+
return { searchValue: pageRoute || target, selectedPage: null, selectedSection: null, step: "input" };
|
|
11863
|
+
}
|
|
11864
|
+
if (isInternalPath(pageRoute)) {
|
|
11865
|
+
const page = resolvePage(pageRoute, pages);
|
|
11866
|
+
return {
|
|
11867
|
+
searchValue: page.title,
|
|
11868
|
+
selectedPage: page,
|
|
11869
|
+
selectedSection: null,
|
|
11870
|
+
step: "input"
|
|
11871
|
+
};
|
|
11872
|
+
}
|
|
11873
|
+
return { searchValue: target, selectedPage: null, selectedSection: null, step: "input" };
|
|
11874
|
+
}
|
|
11875
|
+
function filterAvailablePages(pages, existingTargets) {
|
|
11876
|
+
const taken = new Set(existingTargets);
|
|
11877
|
+
return pages.filter((p) => !taken.has(p.path));
|
|
11878
|
+
}
|
|
11879
|
+
|
|
11880
|
+
// src/lib/collection-item-click.ts
|
|
11881
|
+
function indexCollectionItems(pages) {
|
|
11882
|
+
const index = /* @__PURE__ */ new Map();
|
|
11883
|
+
for (const page of pages) {
|
|
11884
|
+
if (page.kind !== "COLLECTION_ITEM" || !page.collection || !page.path) continue;
|
|
11885
|
+
index.set(normalizePath(page.path), { path: page.path, collection: page.collection });
|
|
11886
|
+
}
|
|
11887
|
+
return index;
|
|
11888
|
+
}
|
|
11889
|
+
function pathOfHref(href) {
|
|
11890
|
+
const trimmed = href.trim();
|
|
11891
|
+
if (!trimmed || trimmed.startsWith("#")) return null;
|
|
11892
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed) && !/^https?:/i.test(trimmed)) return null;
|
|
11893
|
+
const withoutHash = trimmed.split("#")[0];
|
|
11894
|
+
const withoutQuery = withoutHash.split("?")[0];
|
|
11895
|
+
if (!withoutQuery) return null;
|
|
11896
|
+
return normalizePath(withoutQuery);
|
|
11897
|
+
}
|
|
11898
|
+
function matchCollectionItemClick(target, index) {
|
|
11899
|
+
if (!target || index.size === 0) return null;
|
|
11900
|
+
const text = target.closest('[data-ohw-editable="text"], [data-ohw-editable="plain"]');
|
|
11901
|
+
if (!text) return null;
|
|
11902
|
+
const anchor = text.closest("a");
|
|
11903
|
+
if (!anchor || anchor.closest(CTA_BUTTON_SELECTOR)) return null;
|
|
11904
|
+
const href = anchor.getAttribute("href");
|
|
11905
|
+
if (!href) return null;
|
|
11906
|
+
const path = pathOfHref(href);
|
|
11907
|
+
if (!path) return null;
|
|
11908
|
+
return index.get(path) ?? null;
|
|
11909
|
+
}
|
|
11910
|
+
|
|
11683
11911
|
// src/lib/sticky-search-params.ts
|
|
11684
11912
|
var STICKY_EXACT = /* @__PURE__ */ new Set([
|
|
11685
11913
|
// Site identity. Required in local dev and in the canvas editor's iframe, where there is no
|
|
@@ -17020,6 +17248,107 @@ function bindPublishedForms(apiUrl, subdomain, content = {}) {
|
|
|
17020
17248
|
);
|
|
17021
17249
|
}
|
|
17022
17250
|
|
|
17251
|
+
// src/lib/outage-beacon.ts
|
|
17252
|
+
var DEDUP_MS = 5 * 60 * 1e3;
|
|
17253
|
+
var lastSent = /* @__PURE__ */ new Map();
|
|
17254
|
+
function apiBase() {
|
|
17255
|
+
return process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
17256
|
+
}
|
|
17257
|
+
function bridgeVersion() {
|
|
17258
|
+
try {
|
|
17259
|
+
if (true) {
|
|
17260
|
+
return "0.1.153-next.417";
|
|
17261
|
+
}
|
|
17262
|
+
} catch {
|
|
17263
|
+
}
|
|
17264
|
+
if (typeof document !== "undefined") {
|
|
17265
|
+
return document.documentElement.getAttribute("data-ohw-bridge-version") || void 0;
|
|
17266
|
+
}
|
|
17267
|
+
return void 0;
|
|
17268
|
+
}
|
|
17269
|
+
function stampBridgeVersionOnDocument() {
|
|
17270
|
+
if (typeof document === "undefined") {
|
|
17271
|
+
return;
|
|
17272
|
+
}
|
|
17273
|
+
try {
|
|
17274
|
+
const v = true ? "0.1.153-next.417" : "";
|
|
17275
|
+
if (v) {
|
|
17276
|
+
document.documentElement.setAttribute("data-ohw-bridge-version", v);
|
|
17277
|
+
window.__OHW_BRIDGE_VERSION__ = v;
|
|
17278
|
+
}
|
|
17279
|
+
} catch {
|
|
17280
|
+
}
|
|
17281
|
+
}
|
|
17282
|
+
var TRANSIENT_CONTENT_FETCH = /^(Failed to fetch|Load failed|NetworkError when attempting to fetch resource\.?|fetch failed|The user aborted a request\.?|The operation was aborted\.?)$/i;
|
|
17283
|
+
var RECOVERABLE_PAGE_ERROR = /Minified React error #(?:418|419|421|422|423|425)\b|Hydration failed because the initial UI does not match|Text content does not match server-rendered HTML|There was an error while hydrating|did not match\. Server:/i;
|
|
17284
|
+
function isVercelPreviewHost() {
|
|
17285
|
+
if (typeof location === "undefined") {
|
|
17286
|
+
return false;
|
|
17287
|
+
}
|
|
17288
|
+
return location.hostname.toLowerCase().endsWith(".vercel.ohhwells.site");
|
|
17289
|
+
}
|
|
17290
|
+
function reportOutageBeacon(args) {
|
|
17291
|
+
if (typeof window === "undefined" || !args.subdomain) {
|
|
17292
|
+
return;
|
|
17293
|
+
}
|
|
17294
|
+
if (isVercelPreviewHost()) {
|
|
17295
|
+
return;
|
|
17296
|
+
}
|
|
17297
|
+
if (args.reason === "page_error" && args.detail && RECOVERABLE_PAGE_ERROR.test(args.detail)) {
|
|
17298
|
+
return;
|
|
17299
|
+
}
|
|
17300
|
+
if (args.reason === "content_fetch_failed" && args.detail && TRANSIENT_CONTENT_FETCH.test(args.detail.trim())) {
|
|
17301
|
+
return;
|
|
17302
|
+
}
|
|
17303
|
+
const key = `${args.subdomain}|${args.reason}`;
|
|
17304
|
+
const now = Date.now();
|
|
17305
|
+
const prev = lastSent.get(key) || 0;
|
|
17306
|
+
if (now - prev < DEDUP_MS) {
|
|
17307
|
+
return;
|
|
17308
|
+
}
|
|
17309
|
+
lastSent.set(key, now);
|
|
17310
|
+
const body = JSON.stringify({
|
|
17311
|
+
subdomain: args.subdomain,
|
|
17312
|
+
reason: args.reason,
|
|
17313
|
+
detail: args.detail?.slice(0, 300),
|
|
17314
|
+
templateId: args.templateId,
|
|
17315
|
+
bridgeVersion: bridgeVersion(),
|
|
17316
|
+
href: typeof location !== "undefined" ? location.href : void 0
|
|
17317
|
+
});
|
|
17318
|
+
const url = `${apiBase()}/api/public/sites/outage-beacon`;
|
|
17319
|
+
try {
|
|
17320
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
17321
|
+
const blob = new Blob([body], { type: "application/json" });
|
|
17322
|
+
if (navigator.sendBeacon(url, blob)) {
|
|
17323
|
+
return;
|
|
17324
|
+
}
|
|
17325
|
+
}
|
|
17326
|
+
void fetch(url, {
|
|
17327
|
+
method: "POST",
|
|
17328
|
+
headers: { "Content-Type": "application/json" },
|
|
17329
|
+
body,
|
|
17330
|
+
keepalive: true,
|
|
17331
|
+
cache: "no-store"
|
|
17332
|
+
}).catch(() => {
|
|
17333
|
+
});
|
|
17334
|
+
} catch {
|
|
17335
|
+
}
|
|
17336
|
+
}
|
|
17337
|
+
function armLoaderHangBeacon(subdomain, fetchState, hangMs = 15e3) {
|
|
17338
|
+
if (!subdomain || fetchState !== "loading") {
|
|
17339
|
+
return () => {
|
|
17340
|
+
};
|
|
17341
|
+
}
|
|
17342
|
+
const timer = window.setTimeout(() => {
|
|
17343
|
+
reportOutageBeacon({
|
|
17344
|
+
subdomain,
|
|
17345
|
+
reason: "loader_stuck",
|
|
17346
|
+
detail: `fetchState still loading after ${hangMs}ms`
|
|
17347
|
+
});
|
|
17348
|
+
}, hangMs);
|
|
17349
|
+
return () => window.clearTimeout(timer);
|
|
17350
|
+
}
|
|
17351
|
+
|
|
17023
17352
|
// src/lib/form-fields.ts
|
|
17024
17353
|
var FIELD_DEFAULTS = {
|
|
17025
17354
|
"short-text": { label: "Short text", placeholder: "Type placeholder text..." },
|
|
@@ -17620,6 +17949,7 @@ function MediaOverlay({
|
|
|
17620
17949
|
(prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
|
|
17621
17950
|
);
|
|
17622
17951
|
}, [isVideo]);
|
|
17952
|
+
const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
|
|
17623
17953
|
const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
|
|
17624
17954
|
const box = {
|
|
17625
17955
|
position: "fixed",
|
|
@@ -17718,11 +18048,10 @@ function MediaOverlay({
|
|
|
17718
18048
|
className: "flex items-center justify-center cursor-pointer",
|
|
17719
18049
|
style: {
|
|
17720
18050
|
...box,
|
|
17721
|
-
//
|
|
17722
|
-
//
|
|
17723
|
-
//
|
|
17724
|
-
|
|
17725
|
-
pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
|
|
18051
|
+
// Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
|
|
18052
|
+
// (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
|
|
18053
|
+
// and cueOnly also need clicks to fall through — Replace / video controls opt back in.
|
|
18054
|
+
pointerEvents: "none",
|
|
17726
18055
|
// Selected: a firm component ring with no wash, so the image reads as chosen rather
|
|
17727
18056
|
// than hovered. Hover keeps the existing tinted preview.
|
|
17728
18057
|
boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
|
|
@@ -17749,17 +18078,17 @@ function MediaOverlay({
|
|
|
17749
18078
|
},
|
|
17750
18079
|
children: [
|
|
17751
18080
|
isVideo ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
|
|
17752
|
-
|
|
18081
|
+
replaceLabel
|
|
17753
18082
|
]
|
|
17754
18083
|
}
|
|
17755
18084
|
),
|
|
17756
|
-
replaceMode
|
|
18085
|
+
showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
17757
18086
|
Button,
|
|
17758
18087
|
{
|
|
17759
18088
|
"data-ohw-media-overlay": "",
|
|
17760
18089
|
variant: "outline",
|
|
17761
18090
|
size: "sm",
|
|
17762
|
-
"aria-label":
|
|
18091
|
+
"aria-label": replaceLabel,
|
|
17763
18092
|
className: "gap-1.5 cursor-pointer hover:bg-background",
|
|
17764
18093
|
style: {
|
|
17765
18094
|
...OVERLAY_BUTTON_STYLE,
|
|
@@ -17782,7 +18111,7 @@ function MediaOverlay({
|
|
|
17782
18111
|
},
|
|
17783
18112
|
children: [
|
|
17784
18113
|
isVideo ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
|
|
17785
|
-
replaceMode === "full" ?
|
|
18114
|
+
replaceMode === "full" ? replaceLabel : null
|
|
17786
18115
|
]
|
|
17787
18116
|
}
|
|
17788
18117
|
)
|
|
@@ -18292,7 +18621,7 @@ function AiSectionOverlay({
|
|
|
18292
18621
|
|
|
18293
18622
|
// src/ui/ai-section/AiMediaStripOverlay.tsx
|
|
18294
18623
|
var import_react10 = require("react");
|
|
18295
|
-
var
|
|
18624
|
+
var import_lucide_react9 = require("lucide-react");
|
|
18296
18625
|
|
|
18297
18626
|
// src/lib/ai-media-strip-tree.ts
|
|
18298
18627
|
var MEDIA_STRIP_ITEM_MIN = 1;
|
|
@@ -18406,14 +18735,6 @@ function setMediaStripItemAsset(tree, index, assetId) {
|
|
|
18406
18735
|
return true;
|
|
18407
18736
|
});
|
|
18408
18737
|
}
|
|
18409
|
-
function setMediaStripItemAlt(tree, index, alt) {
|
|
18410
|
-
return withMediaStrip(tree, (block) => {
|
|
18411
|
-
const items = block.items;
|
|
18412
|
-
if (index < 0 || index >= items.length) return false;
|
|
18413
|
-
items[index] = { ...items[index], alt };
|
|
18414
|
-
return true;
|
|
18415
|
-
});
|
|
18416
|
-
}
|
|
18417
18738
|
function setMediaStripItemLink(tree, index, link) {
|
|
18418
18739
|
return withMediaStrip(tree, (block) => {
|
|
18419
18740
|
const items = block.items;
|
|
@@ -18431,38 +18752,165 @@ function setMediaStripItemLink(tree, index, link) {
|
|
|
18431
18752
|
});
|
|
18432
18753
|
}
|
|
18433
18754
|
|
|
18434
|
-
// src/ui/
|
|
18755
|
+
// src/ui/dialog.tsx
|
|
18756
|
+
var React9 = __toESM(require("react"), 1);
|
|
18757
|
+
var import_radix_ui6 = require("radix-ui");
|
|
18758
|
+
var import_lucide_react8 = require("lucide-react");
|
|
18435
18759
|
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
18436
|
-
|
|
18437
|
-
|
|
18438
|
-
|
|
18439
|
-
|
|
18440
|
-
const sectionEl = findByInstanceId(sectionId);
|
|
18441
|
-
if (!sectionEl) return null;
|
|
18442
|
-
const items = sectionEl.querySelectorAll("[data-ohw-media-strip-item]");
|
|
18443
|
-
for (const el of Array.from(items)) {
|
|
18444
|
-
if (Number(el.dataset.ohwMediaStripIndex) === index) return el;
|
|
18445
|
-
}
|
|
18446
|
-
return null;
|
|
18760
|
+
function Dialog2({
|
|
18761
|
+
...props
|
|
18762
|
+
}) {
|
|
18763
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
|
|
18447
18764
|
}
|
|
18448
|
-
function
|
|
18449
|
-
|
|
18450
|
-
|
|
18451
|
-
|
|
18452
|
-
if (r2.width <= 0 || r2.height <= 0) return null;
|
|
18453
|
-
return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
|
|
18765
|
+
function DialogPortal({
|
|
18766
|
+
...props
|
|
18767
|
+
}) {
|
|
18768
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
|
|
18454
18769
|
}
|
|
18455
|
-
function
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
|
|
18460
|
-
|
|
18770
|
+
function DialogOverlay({
|
|
18771
|
+
className,
|
|
18772
|
+
...props
|
|
18773
|
+
}) {
|
|
18774
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18775
|
+
import_radix_ui6.Dialog.Overlay,
|
|
18776
|
+
{
|
|
18777
|
+
"data-slot": "dialog-overlay",
|
|
18778
|
+
"data-ohw-link-modal-root": "",
|
|
18779
|
+
className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
|
|
18780
|
+
...props
|
|
18461
18781
|
}
|
|
18462
|
-
|
|
18463
|
-
|
|
18464
|
-
|
|
18465
|
-
|
|
18782
|
+
);
|
|
18783
|
+
}
|
|
18784
|
+
var DialogContent = React9.forwardRef(
|
|
18785
|
+
({ className, children, showCloseButton = true, container, ...props }, ref) => {
|
|
18786
|
+
const positionMode = container ? "absolute" : "fixed";
|
|
18787
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DialogPortal, { container: container ?? void 0, children: [
|
|
18788
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
|
|
18789
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
18790
|
+
import_radix_ui6.Dialog.Content,
|
|
18791
|
+
{
|
|
18792
|
+
ref,
|
|
18793
|
+
"data-slot": "dialog-content",
|
|
18794
|
+
className: cn(
|
|
18795
|
+
positionMode,
|
|
18796
|
+
"left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
|
|
18797
|
+
"rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
|
|
18798
|
+
container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
|
|
18799
|
+
className
|
|
18800
|
+
),
|
|
18801
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
18802
|
+
...props,
|
|
18803
|
+
children: [
|
|
18804
|
+
children,
|
|
18805
|
+
showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18806
|
+
import_radix_ui6.Dialog.Close,
|
|
18807
|
+
{
|
|
18808
|
+
type: "button",
|
|
18809
|
+
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
18810
|
+
"aria-label": "Close",
|
|
18811
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
|
|
18812
|
+
}
|
|
18813
|
+
) : null
|
|
18814
|
+
]
|
|
18815
|
+
}
|
|
18816
|
+
)
|
|
18817
|
+
] });
|
|
18818
|
+
}
|
|
18819
|
+
);
|
|
18820
|
+
DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
|
|
18821
|
+
function DialogHeader({
|
|
18822
|
+
className,
|
|
18823
|
+
...props
|
|
18824
|
+
}) {
|
|
18825
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
|
|
18826
|
+
}
|
|
18827
|
+
function DialogFooter({
|
|
18828
|
+
className,
|
|
18829
|
+
...props
|
|
18830
|
+
}) {
|
|
18831
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18832
|
+
"div",
|
|
18833
|
+
{
|
|
18834
|
+
className: cn("flex items-center justify-end gap-2", className),
|
|
18835
|
+
...props
|
|
18836
|
+
}
|
|
18837
|
+
);
|
|
18838
|
+
}
|
|
18839
|
+
var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18840
|
+
import_radix_ui6.Dialog.Title,
|
|
18841
|
+
{
|
|
18842
|
+
ref,
|
|
18843
|
+
className: cn(
|
|
18844
|
+
"text-lg font-semibold leading-none tracking-tight text-card-foreground",
|
|
18845
|
+
className
|
|
18846
|
+
),
|
|
18847
|
+
...props
|
|
18848
|
+
}
|
|
18849
|
+
));
|
|
18850
|
+
DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
|
|
18851
|
+
var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18852
|
+
import_radix_ui6.Dialog.Description,
|
|
18853
|
+
{
|
|
18854
|
+
ref,
|
|
18855
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
18856
|
+
...props
|
|
18857
|
+
}
|
|
18858
|
+
));
|
|
18859
|
+
DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
|
|
18860
|
+
var DialogClose = import_radix_ui6.Dialog.Close;
|
|
18861
|
+
|
|
18862
|
+
// src/ui/ai-section/AiMediaStripOverlay.tsx
|
|
18863
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
18864
|
+
var PRIMARY3 = "var(--ohw-primary, #0885FE)";
|
|
18865
|
+
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]";
|
|
18866
|
+
function findMediaStripItemElement(sectionId, index) {
|
|
18867
|
+
const sectionEl = findByInstanceId(sectionId);
|
|
18868
|
+
if (!sectionEl) return null;
|
|
18869
|
+
const items = sectionEl.querySelectorAll("[data-ohw-media-strip-item]");
|
|
18870
|
+
for (const el of Array.from(items)) {
|
|
18871
|
+
if (Number(el.dataset.ohwMediaStripIndex) === index) return el;
|
|
18872
|
+
}
|
|
18873
|
+
return null;
|
|
18874
|
+
}
|
|
18875
|
+
function itemRefFromTarget(target) {
|
|
18876
|
+
if (!(target instanceof Element)) return null;
|
|
18877
|
+
if (target.closest(CHROME_SELECTOR)) return null;
|
|
18878
|
+
const itemEl = target.closest("[data-ohw-media-strip-item]");
|
|
18879
|
+
if (!itemEl) return null;
|
|
18880
|
+
const sectionEl = itemEl.closest("[data-ohw-section]");
|
|
18881
|
+
const unit = sectionEl ? movableUnit(sectionEl) : null;
|
|
18882
|
+
const sectionId = unit ? instanceIdOf(unit) || null : null;
|
|
18883
|
+
const index = Number(itemEl.dataset.ohwMediaStripIndex);
|
|
18884
|
+
if (!sectionId || Number.isNaN(index)) return null;
|
|
18885
|
+
return { sectionId, index };
|
|
18886
|
+
}
|
|
18887
|
+
function sameRef(a, b) {
|
|
18888
|
+
return a?.sectionId === b?.sectionId && a?.index === b?.index;
|
|
18889
|
+
}
|
|
18890
|
+
function rectOf(el) {
|
|
18891
|
+
if (!el) return null;
|
|
18892
|
+
const r2 = el.getBoundingClientRect();
|
|
18893
|
+
if (r2.width <= 0 || r2.height <= 0) return null;
|
|
18894
|
+
return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
|
|
18895
|
+
}
|
|
18896
|
+
function readRect2(ref) {
|
|
18897
|
+
return rectOf(ref && findMediaStripItemElement(ref.sectionId, ref.index));
|
|
18898
|
+
}
|
|
18899
|
+
function readStripRect(ref) {
|
|
18900
|
+
const item = ref && findMediaStripItemElement(ref.sectionId, ref.index);
|
|
18901
|
+
return rectOf(item?.closest("[data-ohw-media-strip]") ?? item);
|
|
18902
|
+
}
|
|
18903
|
+
function useLiveItemRect(ref, strip = false) {
|
|
18904
|
+
const [rect, setRect] = (0, import_react10.useState)(null);
|
|
18905
|
+
(0, import_react10.useEffect)(() => {
|
|
18906
|
+
if (!ref) {
|
|
18907
|
+
setRect(null);
|
|
18908
|
+
return;
|
|
18909
|
+
}
|
|
18910
|
+
const update = () => {
|
|
18911
|
+
const next = strip ? readStripRect(ref) : readRect2(ref);
|
|
18912
|
+
setRect(
|
|
18913
|
+
(prev) => prev && next && prev.top === next.top && prev.left === next.left && prev.width === next.width && prev.height === next.height ? prev : next
|
|
18466
18914
|
);
|
|
18467
18915
|
};
|
|
18468
18916
|
update();
|
|
@@ -18475,9 +18923,49 @@ function useLiveItemRect(ref) {
|
|
|
18475
18923
|
window.removeEventListener("resize", update);
|
|
18476
18924
|
clearInterval(interval);
|
|
18477
18925
|
};
|
|
18478
|
-
}, [ref?.sectionId, ref?.index]);
|
|
18926
|
+
}, [ref?.sectionId, ref?.index, strip]);
|
|
18479
18927
|
return rect;
|
|
18480
18928
|
}
|
|
18929
|
+
var TOOLBAR_STRIP_GAP = 52;
|
|
18930
|
+
var TOOLBAR_STRIP_GAP_MOBILE = 42;
|
|
18931
|
+
var TOOLBAR_EDGE_MARGIN2 = 4;
|
|
18932
|
+
function StripToolbarSlot({ stripRect, children }) {
|
|
18933
|
+
const slotRef = (0, import_react10.useRef)(null);
|
|
18934
|
+
const [shift, setShift] = (0, import_react10.useState)(0);
|
|
18935
|
+
const centerX = stripRect.left + stripRect.width / 2;
|
|
18936
|
+
(0, import_react10.useLayoutEffect)(() => {
|
|
18937
|
+
const slot = slotRef.current;
|
|
18938
|
+
if (!slot) return;
|
|
18939
|
+
const clamp = () => {
|
|
18940
|
+
const w = slot.offsetWidth;
|
|
18941
|
+
if (w <= 0) return;
|
|
18942
|
+
const half = w / 2;
|
|
18943
|
+
const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
|
|
18944
|
+
setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
|
|
18945
|
+
};
|
|
18946
|
+
clamp();
|
|
18947
|
+
const ro = new ResizeObserver(clamp);
|
|
18948
|
+
ro.observe(slot);
|
|
18949
|
+
return () => ro.disconnect();
|
|
18950
|
+
}, [centerX, children]);
|
|
18951
|
+
const mobile = window.innerWidth <= 480;
|
|
18952
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
18953
|
+
"div",
|
|
18954
|
+
{
|
|
18955
|
+
ref: slotRef,
|
|
18956
|
+
"data-ohw-bridge": "",
|
|
18957
|
+
"data-ohw-item-interaction": "",
|
|
18958
|
+
className: "pointer-events-auto fixed",
|
|
18959
|
+
style: {
|
|
18960
|
+
top: stripRect.top - (mobile ? TOOLBAR_STRIP_GAP_MOBILE : TOOLBAR_STRIP_GAP),
|
|
18961
|
+
left: centerX + shift,
|
|
18962
|
+
transform: "translate(-50%, -100%)",
|
|
18963
|
+
zIndex: 2147483645
|
|
18964
|
+
},
|
|
18965
|
+
children
|
|
18966
|
+
}
|
|
18967
|
+
);
|
|
18968
|
+
}
|
|
18481
18969
|
function requestValidateTree(postToParent2, tree) {
|
|
18482
18970
|
return new Promise((resolve) => {
|
|
18483
18971
|
const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -18514,116 +19002,72 @@ function requestAssetPick(postToParent2) {
|
|
|
18514
19002
|
postToParent2({ type: "ow:asset-pick", id });
|
|
18515
19003
|
});
|
|
18516
19004
|
}
|
|
18517
|
-
function
|
|
19005
|
+
function ToolbarAction({
|
|
18518
19006
|
label,
|
|
18519
19007
|
onClick,
|
|
18520
|
-
disabled,
|
|
18521
|
-
active
|
|
19008
|
+
disabled = false,
|
|
19009
|
+
active = false,
|
|
19010
|
+
children
|
|
18522
19011
|
}) {
|
|
18523
|
-
|
|
18524
|
-
|
|
18525
|
-
{
|
|
18526
|
-
|
|
18527
|
-
|
|
18528
|
-
disabled,
|
|
18529
|
-
style: {
|
|
18530
|
-
height: 28,
|
|
18531
|
-
padding: "0 10px",
|
|
18532
|
-
display: "inline-flex",
|
|
18533
|
-
alignItems: "center",
|
|
18534
|
-
gap: 4,
|
|
18535
|
-
borderRadius: 999,
|
|
18536
|
-
border: "none",
|
|
18537
|
-
cursor: disabled ? "default" : "pointer",
|
|
18538
|
-
background: active ? OHW_BLUE_DARK : "#ffffff",
|
|
18539
|
-
color: active ? "#ffffff" : OHW_BLUE_DARK,
|
|
18540
|
-
opacity: disabled ? 0.4 : 1,
|
|
18541
|
-
fontSize: 12,
|
|
18542
|
-
lineHeight: "16px",
|
|
18543
|
-
fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
|
|
18544
|
-
whiteSpace: "nowrap",
|
|
18545
|
-
flex: "0 0 auto"
|
|
18546
|
-
},
|
|
18547
|
-
children: label
|
|
18548
|
-
}
|
|
18549
|
-
);
|
|
19012
|
+
const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, active, onClick, className: "size-8", children });
|
|
19013
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
|
|
19014
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: button }) : button }),
|
|
19015
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
|
|
19016
|
+
] });
|
|
18550
19017
|
}
|
|
18551
|
-
function
|
|
18552
|
-
|
|
18553
|
-
|
|
18554
|
-
|
|
18555
|
-
|
|
18556
|
-
|
|
18557
|
-
|
|
18558
|
-
|
|
18559
|
-
|
|
18560
|
-
|
|
18561
|
-
|
|
18562
|
-
|
|
18563
|
-
|
|
18564
|
-
|
|
18565
|
-
|
|
18566
|
-
|
|
18567
|
-
|
|
18568
|
-
|
|
18569
|
-
|
|
18570
|
-
|
|
18571
|
-
|
|
18572
|
-
|
|
18573
|
-
|
|
18574
|
-
|
|
18575
|
-
|
|
18576
|
-
|
|
18577
|
-
|
|
18578
|
-
|
|
18579
|
-
|
|
18580
|
-
},
|
|
18581
|
-
children: "Alt"
|
|
18582
|
-
}
|
|
18583
|
-
),
|
|
18584
|
-
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18585
|
-
"input",
|
|
19018
|
+
function ToolbarSeparator() {
|
|
19019
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
|
|
19020
|
+
}
|
|
19021
|
+
function OptionMenu({
|
|
19022
|
+
label,
|
|
19023
|
+
value,
|
|
19024
|
+
options,
|
|
19025
|
+
onChange
|
|
19026
|
+
}) {
|
|
19027
|
+
const current = options.find((o) => o.value === value) ?? options[0];
|
|
19028
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
|
|
19029
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground", children: label }),
|
|
19030
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DropdownMenu, { modal: false, children: [
|
|
19031
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
19032
|
+
"button",
|
|
19033
|
+
{
|
|
19034
|
+
type: "button",
|
|
19035
|
+
"aria-label": label,
|
|
19036
|
+
className: "inline-flex h-8 min-w-[76px] items-center gap-[7px] rounded-[6px] bg-transparent px-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted data-[state=open]:bg-muted",
|
|
19037
|
+
children: [
|
|
19038
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex-1 text-left", children: current.label }),
|
|
19039
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
|
|
19040
|
+
]
|
|
19041
|
+
}
|
|
19042
|
+
) }),
|
|
19043
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[132px] min-w-[132px] p-1", children: options.map((option) => {
|
|
19044
|
+
const selected = option.value === value;
|
|
19045
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
19046
|
+
DropdownMenuItem,
|
|
18586
19047
|
{
|
|
18587
|
-
|
|
18588
|
-
|
|
18589
|
-
|
|
18590
|
-
|
|
18591
|
-
|
|
18592
|
-
|
|
18593
|
-
|
|
18594
|
-
|
|
18595
|
-
|
|
18596
|
-
|
|
18597
|
-
|
|
18598
|
-
|
|
18599
|
-
|
|
18600
|
-
|
|
18601
|
-
"aria-label": "Image alt text",
|
|
18602
|
-
style: {
|
|
18603
|
-
flex: 1,
|
|
18604
|
-
minWidth: 0,
|
|
18605
|
-
border: "none",
|
|
18606
|
-
outline: "none",
|
|
18607
|
-
background: "transparent",
|
|
18608
|
-
fontSize: 12,
|
|
18609
|
-
lineHeight: "16px",
|
|
18610
|
-
fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
|
|
18611
|
-
color: "#1f2937"
|
|
18612
|
-
}
|
|
18613
|
-
}
|
|
18614
|
-
)
|
|
18615
|
-
]
|
|
18616
|
-
}
|
|
18617
|
-
);
|
|
19048
|
+
role: "menuitemradio",
|
|
19049
|
+
"aria-checked": selected,
|
|
19050
|
+
onSelect: () => onChange(option.value),
|
|
19051
|
+
className: cn("grid grid-cols-[1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
|
|
19052
|
+
children: [
|
|
19053
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: option.label }),
|
|
19054
|
+
selected && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
|
|
19055
|
+
]
|
|
19056
|
+
},
|
|
19057
|
+
option.value
|
|
19058
|
+
);
|
|
19059
|
+
}) })
|
|
19060
|
+
] })
|
|
19061
|
+
] });
|
|
18618
19062
|
}
|
|
18619
19063
|
var VARIANTS = [
|
|
18620
19064
|
{ value: "logos", label: "Logos" },
|
|
18621
19065
|
{ value: "reel", label: "Reel" }
|
|
18622
19066
|
];
|
|
18623
19067
|
var ALIGNS = [
|
|
18624
|
-
{ value: "left", label: "
|
|
18625
|
-
{ value: "center", label: "
|
|
18626
|
-
{ value: "right", label: "
|
|
19068
|
+
{ value: "left", label: "Align left", Icon: import_lucide_react9.AlignLeft },
|
|
19069
|
+
{ value: "center", label: "Align center", Icon: import_lucide_react9.AlignCenter },
|
|
19070
|
+
{ value: "right", label: "Align right", Icon: import_lucide_react9.AlignRight }
|
|
18627
19071
|
];
|
|
18628
19072
|
var HOVERS = [
|
|
18629
19073
|
{ value: "none", label: "None" },
|
|
@@ -18631,104 +19075,104 @@ var HOVERS = [
|
|
|
18631
19075
|
{ value: "zoom", label: "Zoom" },
|
|
18632
19076
|
{ value: "fade", label: "Fade" }
|
|
18633
19077
|
];
|
|
18634
|
-
var TREATMENTS = [
|
|
18635
|
-
{ value: "color", label: "Color" },
|
|
18636
|
-
{ value: "grayscale", label: "Grayscale" }
|
|
18637
|
-
];
|
|
18638
19078
|
var HTTP_URL_RE = /^https?:\/\/\S+$/i;
|
|
18639
|
-
function
|
|
18640
|
-
|
|
19079
|
+
function LinkModal({
|
|
19080
|
+
href,
|
|
19081
|
+
linkTarget,
|
|
19082
|
+
onClose,
|
|
19083
|
+
onSave
|
|
19084
|
+
}) {
|
|
19085
|
+
const [draft, setDraft] = (0, import_react10.useState)(href);
|
|
19086
|
+
const [newTab, setNewTab] = (0, import_react10.useState)(linkTarget !== "same-tab");
|
|
18641
19087
|
const [invalid, setInvalid] = (0, import_react10.useState)(false);
|
|
18642
|
-
|
|
18643
|
-
setDraft(value);
|
|
18644
|
-
setInvalid(false);
|
|
18645
|
-
}, [value]);
|
|
18646
|
-
const commit = () => {
|
|
19088
|
+
const save = () => {
|
|
18647
19089
|
const next = draft.trim();
|
|
18648
|
-
if (next
|
|
18649
|
-
|
|
18650
|
-
|
|
18651
|
-
onCommit(next);
|
|
18652
|
-
};
|
|
18653
|
-
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
18654
|
-
"div",
|
|
18655
|
-
{
|
|
18656
|
-
style: {
|
|
18657
|
-
display: "flex",
|
|
18658
|
-
alignItems: "center",
|
|
18659
|
-
gap: 6,
|
|
18660
|
-
height: 28,
|
|
18661
|
-
padding: "0 12px",
|
|
18662
|
-
borderRadius: 999,
|
|
18663
|
-
background: "#ffffff",
|
|
18664
|
-
width: "100%",
|
|
18665
|
-
maxWidth: 260,
|
|
18666
|
-
boxSizing: "border-box",
|
|
18667
|
-
boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
|
|
18668
|
-
},
|
|
18669
|
-
children: [
|
|
18670
|
-
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18671
|
-
"span",
|
|
18672
|
-
{
|
|
18673
|
-
style: {
|
|
18674
|
-
flex: "0 0 auto",
|
|
18675
|
-
color: OHW_BLUE_DARK,
|
|
18676
|
-
fontSize: 10,
|
|
18677
|
-
fontWeight: 700,
|
|
18678
|
-
textTransform: "uppercase",
|
|
18679
|
-
letterSpacing: 0.4
|
|
18680
|
-
},
|
|
18681
|
-
children: "Link"
|
|
18682
|
-
}
|
|
18683
|
-
),
|
|
18684
|
-
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18685
|
-
"input",
|
|
18686
|
-
{
|
|
18687
|
-
type: "text",
|
|
18688
|
-
inputMode: "url",
|
|
18689
|
-
value: draft,
|
|
18690
|
-
onChange: (e) => setDraft(e.target.value),
|
|
18691
|
-
onBlur: commit,
|
|
18692
|
-
onKeyDown: (e) => {
|
|
18693
|
-
if (e.key === "Enter") e.currentTarget.blur();
|
|
18694
|
-
if (e.key === "Escape") {
|
|
18695
|
-
setDraft(value);
|
|
18696
|
-
setInvalid(false);
|
|
18697
|
-
e.currentTarget.blur();
|
|
18698
|
-
}
|
|
18699
|
-
},
|
|
18700
|
-
placeholder: "https://example.com",
|
|
18701
|
-
"aria-label": "Image link URL",
|
|
18702
|
-
"aria-invalid": invalid,
|
|
18703
|
-
style: {
|
|
18704
|
-
flex: 1,
|
|
18705
|
-
minWidth: 0,
|
|
18706
|
-
border: "none",
|
|
18707
|
-
outline: "none",
|
|
18708
|
-
background: "transparent",
|
|
18709
|
-
fontSize: 12,
|
|
18710
|
-
lineHeight: "16px",
|
|
18711
|
-
fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
|
|
18712
|
-
color: "#1f2937"
|
|
18713
|
-
}
|
|
18714
|
-
}
|
|
18715
|
-
)
|
|
18716
|
-
]
|
|
19090
|
+
if (next && !HTTP_URL_RE.test(next)) {
|
|
19091
|
+
setInvalid(true);
|
|
19092
|
+
return;
|
|
18717
19093
|
}
|
|
18718
|
-
|
|
19094
|
+
onSave(next, newTab ? "new-tab" : "same-tab");
|
|
19095
|
+
onClose();
|
|
19096
|
+
};
|
|
19097
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Dialog2, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DialogContent, { "data-ohw-link-modal-root": "", className: "max-w-[400px] gap-4 p-5", children: [
|
|
19098
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogTitle, { className: "text-base", children: "Image link" }) }),
|
|
19099
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-3", children: [
|
|
19100
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19101
|
+
"input",
|
|
19102
|
+
{
|
|
19103
|
+
autoFocus: true,
|
|
19104
|
+
type: "text",
|
|
19105
|
+
inputMode: "url",
|
|
19106
|
+
value: draft,
|
|
19107
|
+
onChange: (e) => {
|
|
19108
|
+
setDraft(e.target.value);
|
|
19109
|
+
setInvalid(false);
|
|
19110
|
+
},
|
|
19111
|
+
onKeyDown: (e) => {
|
|
19112
|
+
if (e.key === "Enter") save();
|
|
19113
|
+
},
|
|
19114
|
+
placeholder: "https://example.com",
|
|
19115
|
+
"aria-label": "Image link URL",
|
|
19116
|
+
"aria-invalid": invalid,
|
|
19117
|
+
className: cn(
|
|
19118
|
+
"h-9 w-full rounded-[6px] border border-border bg-background px-3 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/70 focus:border-bridge-primary",
|
|
19119
|
+
invalid && "border-destructive focus:border-destructive"
|
|
19120
|
+
)
|
|
19121
|
+
}
|
|
19122
|
+
),
|
|
19123
|
+
invalid && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-xs text-destructive", children: "Enter a full URL starting with http:// or https://" }),
|
|
19124
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex items-center gap-2 text-[13px] text-foreground", children: [
|
|
19125
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("input", { type: "checkbox", checked: newTab, onChange: (e) => setNewTab(e.target.checked) }),
|
|
19126
|
+
"Open in new tab"
|
|
19127
|
+
] })
|
|
19128
|
+
] }),
|
|
19129
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DialogFooter, { children: [
|
|
19130
|
+
href && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19131
|
+
"button",
|
|
19132
|
+
{
|
|
19133
|
+
type: "button",
|
|
19134
|
+
onClick: () => {
|
|
19135
|
+
onSave("", "new-tab");
|
|
19136
|
+
onClose();
|
|
19137
|
+
},
|
|
19138
|
+
className: "mr-auto rounded-[6px] px-3 py-2 text-[13px] font-medium text-destructive transition-colors hover:bg-muted",
|
|
19139
|
+
children: "Remove link"
|
|
19140
|
+
}
|
|
19141
|
+
),
|
|
19142
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19143
|
+
"button",
|
|
19144
|
+
{
|
|
19145
|
+
type: "button",
|
|
19146
|
+
onClick: onClose,
|
|
19147
|
+
className: "rounded-[6px] px-3 py-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted",
|
|
19148
|
+
children: "Cancel"
|
|
19149
|
+
}
|
|
19150
|
+
),
|
|
19151
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19152
|
+
"button",
|
|
19153
|
+
{
|
|
19154
|
+
type: "button",
|
|
19155
|
+
onClick: save,
|
|
19156
|
+
className: "rounded-[6px] bg-bridge-primary px-3 py-2 text-[13px] font-medium text-primary-foreground transition-opacity hover:opacity-90",
|
|
19157
|
+
children: "Save"
|
|
19158
|
+
}
|
|
19159
|
+
)
|
|
19160
|
+
] })
|
|
19161
|
+
] }) });
|
|
18719
19162
|
}
|
|
18720
19163
|
function MediaStripToolbar({
|
|
18721
19164
|
entry,
|
|
18722
19165
|
index,
|
|
18723
19166
|
pending,
|
|
18724
19167
|
error,
|
|
19168
|
+
itemEditOn,
|
|
19169
|
+
onToggleItemEdit,
|
|
18725
19170
|
onSetVariant,
|
|
18726
19171
|
onSetAlign,
|
|
18727
19172
|
onSetHover,
|
|
18728
19173
|
onSetTreatment,
|
|
18729
19174
|
onResize,
|
|
18730
|
-
|
|
18731
|
-
onSetLink,
|
|
19175
|
+
onOpenLink,
|
|
18732
19176
|
onAddItem,
|
|
18733
19177
|
onRemoveItem,
|
|
18734
19178
|
onChangeImage,
|
|
@@ -18737,81 +19181,102 @@ function MediaStripToolbar({
|
|
|
18737
19181
|
}) {
|
|
18738
19182
|
const items = mediaStripItems(entry.tree);
|
|
18739
19183
|
const itemCount = items.length;
|
|
18740
|
-
const currentAlt = items[index]?.alt ?? "";
|
|
18741
|
-
const currentHref = items[index]?.href ?? "";
|
|
18742
|
-
const currentTarget = items[index]?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
|
|
18743
19184
|
const slots = findMediaStripSlots(entry.tree);
|
|
18744
19185
|
const variant = slots?.variant === "reel" ? "reel" : "logos";
|
|
18745
19186
|
const align = slots?.align === "left" || slots?.align === "right" ? slots.align : "center";
|
|
18746
19187
|
const hover = slots?.hover === "none" || slots?.hover === "lift" || slots?.hover === "zoom" || slots?.hover === "fade" ? slots.hover : "lift";
|
|
18747
19188
|
const treatment = slots?.treatment === "color" || slots?.treatment === "grayscale" ? slots.treatment : variant === "logos" ? "grayscale" : "color";
|
|
18748
19189
|
const sizeValue = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
|
|
18749
|
-
const
|
|
18750
|
-
const
|
|
18751
|
-
|
|
18752
|
-
gap: 6,
|
|
18753
|
-
flexWrap: "nowrap",
|
|
18754
|
-
justifyContent: "center",
|
|
18755
|
-
overflowX: "auto",
|
|
18756
|
-
WebkitOverflowScrolling: "touch",
|
|
18757
|
-
width: "100%",
|
|
18758
|
-
scrollbarWidth: "none"
|
|
18759
|
-
} : { display: "flex", gap: 6, flexWrap: "nowrap", justifyContent: "center" };
|
|
18760
|
-
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
19190
|
+
const mobile = window.innerWidth <= 480;
|
|
19191
|
+
const gatedLabel = (label) => itemEditOn ? label : "Turn on the star to edit this image";
|
|
19192
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
18761
19193
|
"div",
|
|
18762
19194
|
{
|
|
18763
19195
|
"data-ohw-bridge": "",
|
|
18764
19196
|
"data-ohw-toolbar": "",
|
|
18765
19197
|
onMouseDown: (e) => e.stopPropagation(),
|
|
18766
|
-
|
|
18767
|
-
|
|
18768
|
-
flexDirection: "column",
|
|
18769
|
-
gap: 6,
|
|
18770
|
-
alignItems: "center",
|
|
18771
|
-
background: OHW_BLUE,
|
|
18772
|
-
padding: 8,
|
|
18773
|
-
borderRadius: 12,
|
|
18774
|
-
boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
|
|
18775
|
-
pointerEvents: pending ? "none" : "auto",
|
|
18776
|
-
opacity: pending ? 0.6 : 1,
|
|
18777
|
-
maxWidth: isMobile ? "calc(100vw - 24px)" : "none"
|
|
18778
|
-
},
|
|
19198
|
+
className: cn("flex flex-col items-center gap-1.5 font-sans", pending && "pointer-events-none opacity-60"),
|
|
19199
|
+
style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
|
|
18779
19200
|
children: [
|
|
18780
|
-
/* @__PURE__ */ (0,
|
|
18781
|
-
|
|
18782
|
-
|
|
18783
|
-
|
|
18784
|
-
|
|
18785
|
-
|
|
18786
|
-
|
|
18787
|
-
|
|
18788
|
-
|
|
18789
|
-
|
|
18790
|
-
|
|
18791
|
-
|
|
18792
|
-
|
|
18793
|
-
|
|
18794
|
-
|
|
18795
|
-
|
|
18796
|
-
|
|
18797
|
-
|
|
18798
|
-
|
|
18799
|
-
|
|
18800
|
-
|
|
18801
|
-
|
|
18802
|
-
|
|
18803
|
-
|
|
18804
|
-
|
|
18805
|
-
|
|
18806
|
-
|
|
18807
|
-
|
|
18808
|
-
|
|
18809
|
-
|
|
18810
|
-
|
|
18811
|
-
|
|
19201
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
19202
|
+
"div",
|
|
19203
|
+
{
|
|
19204
|
+
className: cn(
|
|
19205
|
+
"flex items-center gap-1 rounded-[10px] border border-border bg-background p-1 whitespace-nowrap",
|
|
19206
|
+
"shadow-[0px_10px_15px_-3px_rgba(0,0,0,0.1),0px_4px_6px_-4px_rgba(0,0,0,0.1)]",
|
|
19207
|
+
mobile ? "flex-wrap justify-center" : "flex-nowrap"
|
|
19208
|
+
),
|
|
19209
|
+
children: [
|
|
19210
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
|
|
19211
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19212
|
+
CustomToolbarButton,
|
|
19213
|
+
{
|
|
19214
|
+
type: "button",
|
|
19215
|
+
"aria-label": itemEditOn ? "Done editing this image" : "Edit this image",
|
|
19216
|
+
"aria-pressed": itemEditOn,
|
|
19217
|
+
onClick: onToggleItemEdit,
|
|
19218
|
+
className: "group size-8",
|
|
19219
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19220
|
+
import_lucide_react9.Star,
|
|
19221
|
+
{
|
|
19222
|
+
size: 16,
|
|
19223
|
+
fill: "currentColor",
|
|
19224
|
+
strokeWidth: 0,
|
|
19225
|
+
className: cn(
|
|
19226
|
+
"transition-colors",
|
|
19227
|
+
itemEditOn ? "text-[#f59e0b] group-hover:text-[#d97706]" : "text-[#d6d3d1] group-hover:text-[#fbbf24]"
|
|
19228
|
+
),
|
|
19229
|
+
style: { pointerEvents: "none" }
|
|
19230
|
+
}
|
|
19231
|
+
)
|
|
19232
|
+
}
|
|
19233
|
+
) }),
|
|
19234
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: itemEditOn ? "Done editing this image" : "Edit this image" })
|
|
19235
|
+
] }),
|
|
19236
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
|
|
19237
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Change image"), onClick: onChangeImage, disabled: !itemEditOn, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Image, { size: 16, style: { pointerEvents: "none" } }) }),
|
|
19238
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Link"), onClick: onOpenLink, disabled: !itemEditOn, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Link, { size: 16, style: { pointerEvents: "none" } }) }),
|
|
19239
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Move left"), onClick: onMoveLeft, disabled: !itemEditOn || index <= 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ArrowLeft, { size: 16, style: { pointerEvents: "none" } }) }),
|
|
19240
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19241
|
+
ToolbarAction,
|
|
19242
|
+
{
|
|
19243
|
+
label: gatedLabel("Move right"),
|
|
19244
|
+
onClick: onMoveRight,
|
|
19245
|
+
disabled: !itemEditOn || index >= itemCount - 1,
|
|
19246
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ArrowRight, { size: 16, style: { pointerEvents: "none" } })
|
|
19247
|
+
}
|
|
19248
|
+
),
|
|
19249
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Remove image"), onClick: onRemoveItem, disabled: !itemEditOn || itemCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ImageMinus, { size: 16, style: { pointerEvents: "none" } }) }),
|
|
19250
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
|
|
19251
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OptionMenu, { label: "Layout", value: variant, options: VARIANTS, onChange: onSetVariant }),
|
|
19252
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OptionMenu, { label: "Hover", value: hover, options: HOVERS, onChange: onSetHover }),
|
|
19253
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19254
|
+
ToolbarAction,
|
|
19255
|
+
{
|
|
19256
|
+
label: treatment === "grayscale" ? "Show in color" : "Show grayscale",
|
|
19257
|
+
active: treatment === "grayscale",
|
|
19258
|
+
onClick: () => onSetTreatment(treatment === "grayscale" ? "color" : "grayscale"),
|
|
19259
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Palette, { size: 16, style: { pointerEvents: "none" } })
|
|
19260
|
+
}
|
|
19261
|
+
),
|
|
19262
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
|
|
19263
|
+
ALIGNS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label, active: align === value, onClick: () => onSetAlign(value), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Icon, { size: 16, style: { pointerEvents: "none" } }) }, value)),
|
|
19264
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
|
|
19265
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Smaller", onClick: () => onResize(-8), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Minus, { size: 16, style: { pointerEvents: "none" } }) }),
|
|
19266
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "min-w-[40px] text-center text-xs tabular-nums text-muted-foreground", children: [
|
|
19267
|
+
sizeValue,
|
|
19268
|
+
"px"
|
|
19269
|
+
] }),
|
|
19270
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Larger", onClick: () => onResize(8), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
|
|
19271
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
|
|
19272
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add image", onClick: onAddItem, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ImagePlus, { size: 16, style: { pointerEvents: "none" } }) })
|
|
19273
|
+
]
|
|
19274
|
+
}
|
|
19275
|
+
),
|
|
19276
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
|
|
18812
19277
|
]
|
|
18813
19278
|
}
|
|
18814
|
-
);
|
|
19279
|
+
) });
|
|
18815
19280
|
}
|
|
18816
19281
|
function findMediaStripSlots(tree) {
|
|
18817
19282
|
const stack = tree.rows.flatMap((row) => row.blocks);
|
|
@@ -18830,31 +19295,23 @@ function AiMediaStripOverlay({
|
|
|
18830
19295
|
}) {
|
|
18831
19296
|
const [hover, setHover] = (0, import_react10.useState)(null);
|
|
18832
19297
|
const [active, setActive] = (0, import_react10.useState)(null);
|
|
19298
|
+
const [itemEditOn, setItemEditOn] = (0, import_react10.useState)(false);
|
|
19299
|
+
const [linkOpen, setLinkOpen] = (0, import_react10.useState)(false);
|
|
18833
19300
|
const [pending, setPending] = (0, import_react10.useState)(false);
|
|
18834
19301
|
const [error, setError] = (0, import_react10.useState)(null);
|
|
18835
19302
|
const activeRef = (0, import_react10.useRef)(null);
|
|
18836
19303
|
activeRef.current = active;
|
|
19304
|
+
const linkOpenRef = (0, import_react10.useRef)(false);
|
|
19305
|
+
linkOpenRef.current = linkOpen;
|
|
18837
19306
|
(0, import_react10.useEffect)(() => {
|
|
18838
19307
|
const onMove = (e) => {
|
|
19308
|
+
const ref = itemRefFromTarget(e.target);
|
|
18839
19309
|
const t = e.target;
|
|
18840
|
-
|
|
18841
|
-
|
|
18842
|
-
|
|
18843
|
-
|
|
18844
|
-
|
|
18845
|
-
if (!itemEl) {
|
|
18846
|
-
setHover(null);
|
|
18847
|
-
return;
|
|
18848
|
-
}
|
|
18849
|
-
const sectionEl = itemEl.closest("[data-ohw-section]");
|
|
18850
|
-
const unit = sectionEl ? movableUnit(sectionEl) : null;
|
|
18851
|
-
const sectionId = unit ? instanceIdOf(unit) || null : null;
|
|
18852
|
-
const index = Number(itemEl.dataset.ohwMediaStripIndex);
|
|
18853
|
-
if (!sectionId || Number.isNaN(index)) {
|
|
18854
|
-
setHover(null);
|
|
18855
|
-
return;
|
|
18856
|
-
}
|
|
18857
|
-
setHover({ sectionId, index });
|
|
19310
|
+
const overEditable = Boolean(t?.closest?.("[data-ohw-editable]"));
|
|
19311
|
+
setHover((prev) => {
|
|
19312
|
+
const next = ref && !overEditable ? ref : null;
|
|
19313
|
+
return sameRef(prev, next) ? prev : next;
|
|
19314
|
+
});
|
|
18858
19315
|
};
|
|
18859
19316
|
const onLeave = () => setHover(null);
|
|
18860
19317
|
document.addEventListener("mousemove", onMove, { passive: true });
|
|
@@ -18866,23 +19323,25 @@ function AiMediaStripOverlay({
|
|
|
18866
19323
|
}, []);
|
|
18867
19324
|
(0, import_react10.useEffect)(() => {
|
|
18868
19325
|
const onClick = (e) => {
|
|
18869
|
-
const current = activeRef.current;
|
|
18870
|
-
if (!current) return;
|
|
18871
19326
|
const t = e.target;
|
|
18872
|
-
if (!(t instanceof
|
|
18873
|
-
if (t.closest(
|
|
18874
|
-
const
|
|
18875
|
-
if (
|
|
18876
|
-
|
|
19327
|
+
if (!(t instanceof Element)) return;
|
|
19328
|
+
if (t.closest(CHROME_SELECTOR)) return;
|
|
19329
|
+
const ref = itemRefFromTarget(t);
|
|
19330
|
+
if (sameRef(activeRef.current, ref)) return;
|
|
19331
|
+
if (ref) clearInteractionChromeRef.current();
|
|
19332
|
+
setItemEditOn(false);
|
|
18877
19333
|
setError(null);
|
|
19334
|
+
setActive(ref);
|
|
18878
19335
|
};
|
|
18879
19336
|
document.addEventListener("click", onClick, true);
|
|
18880
19337
|
return () => document.removeEventListener("click", onClick, true);
|
|
18881
|
-
}, []);
|
|
19338
|
+
}, [clearInteractionChromeRef]);
|
|
18882
19339
|
(0, import_react10.useEffect)(() => {
|
|
18883
19340
|
const onBlur = () => {
|
|
18884
19341
|
if (!activeRef.current) return;
|
|
18885
19342
|
setActive(null);
|
|
19343
|
+
setItemEditOn(false);
|
|
19344
|
+
setLinkOpen(false);
|
|
18886
19345
|
setError(null);
|
|
18887
19346
|
};
|
|
18888
19347
|
window.addEventListener("blur", onBlur);
|
|
@@ -18892,7 +19351,10 @@ function AiMediaStripOverlay({
|
|
|
18892
19351
|
const onKeyDown = (e) => {
|
|
18893
19352
|
if (e.key !== "Escape") return;
|
|
18894
19353
|
if (!activeRef.current) return;
|
|
19354
|
+
if (linkOpenRef.current) return;
|
|
19355
|
+
if (document.querySelector('[data-ohw-more-menu][data-state="open"]')) return;
|
|
18895
19356
|
setActive(null);
|
|
19357
|
+
setItemEditOn(false);
|
|
18896
19358
|
setError(null);
|
|
18897
19359
|
};
|
|
18898
19360
|
document.addEventListener("keydown", onKeyDown);
|
|
@@ -18926,137 +19388,133 @@ function AiMediaStripOverlay({
|
|
|
18926
19388
|
tree: nextTree,
|
|
18927
19389
|
label: entry.label,
|
|
18928
19390
|
media: extraMedia ? { ...entry.media, ...extraMedia } : entry.media,
|
|
18929
|
-
...entry.brand ? { brand: entry.brand } : {}
|
|
19391
|
+
...entry.brand ? { brand: entry.brand } : {},
|
|
19392
|
+
// The user is already looking at the strip they just edited — recentering the viewport
|
|
19393
|
+
// on every edit reads as the section jumping around.
|
|
19394
|
+
skipScrollIntoView: true
|
|
18930
19395
|
});
|
|
18931
19396
|
return true;
|
|
18932
19397
|
},
|
|
18933
19398
|
[findEntry, postToParent2, applyTreeLocalRef]
|
|
18934
19399
|
);
|
|
18935
19400
|
const activeEntry = active ? findEntry(active.sectionId) : null;
|
|
18936
|
-
const hoverHandleRect = useLiveItemRect(!active && hover ? hover : null);
|
|
18937
19401
|
const activeItemRect = useLiveItemRect(active);
|
|
18938
|
-
const
|
|
18939
|
-
const
|
|
18940
|
-
|
|
18941
|
-
|
|
18942
|
-
|
|
18943
|
-
|
|
18944
|
-
|
|
18945
|
-
|
|
18946
|
-
|
|
18947
|
-
|
|
18948
|
-
|
|
18949
|
-
|
|
18950
|
-
|
|
18951
|
-
|
|
18952
|
-
|
|
18953
|
-
|
|
18954
|
-
|
|
18955
|
-
|
|
18956
|
-
|
|
18957
|
-
|
|
18958
|
-
|
|
18959
|
-
|
|
18960
|
-
|
|
18961
|
-
|
|
18962
|
-
|
|
18963
|
-
|
|
18964
|
-
|
|
18965
|
-
|
|
18966
|
-
|
|
18967
|
-
|
|
18968
|
-
|
|
18969
|
-
|
|
18970
|
-
|
|
18971
|
-
|
|
18972
|
-
activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18973
|
-
ItemInteractionLayer,
|
|
18974
|
-
{
|
|
18975
|
-
rect: activeItemRect,
|
|
18976
|
-
state: toolbarState,
|
|
18977
|
-
toolbar: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18978
|
-
MediaStripToolbar,
|
|
18979
|
-
{
|
|
18980
|
-
entry: activeEntry,
|
|
18981
|
-
index: active.index,
|
|
18982
|
-
pending,
|
|
18983
|
-
error,
|
|
18984
|
-
onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
|
|
18985
|
-
onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
|
|
18986
|
-
onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
|
|
18987
|
-
onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
|
|
18988
|
-
onResize: (delta) => {
|
|
18989
|
-
const slots = findMediaStripSlots(activeEntry.tree);
|
|
18990
|
-
const variant = slots?.variant === "reel" ? "reel" : "logos";
|
|
18991
|
-
const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
|
|
18992
|
-
void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
|
|
18993
|
-
},
|
|
18994
|
-
onSetAlt: (alt) => {
|
|
18995
|
-
void runMutation(active.sectionId, (tree) => setMediaStripItemAlt(tree, active.index, alt));
|
|
18996
|
-
},
|
|
18997
|
-
onSetLink: (href, linkTarget) => {
|
|
19402
|
+
const activeStripRect = useLiveItemRect(active, true);
|
|
19403
|
+
const hoverRect = useLiveItemRect(hover && !sameRef(hover, active) ? hover : null);
|
|
19404
|
+
const activeItems = activeEntry ? mediaStripItems(activeEntry.tree) : [];
|
|
19405
|
+
const activeItem = active ? activeItems[active.index] : void 0;
|
|
19406
|
+
const activeHref = activeItem?.href ?? "";
|
|
19407
|
+
const activeTarget = activeItem?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
|
|
19408
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
19409
|
+
hoverRect && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
|
|
19410
|
+
activeItemRect && activeStripRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
19411
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: activeItemRect, state: "active-top" }),
|
|
19412
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StripToolbarSlot, { stripRect: activeStripRect, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19413
|
+
MediaStripToolbar,
|
|
19414
|
+
{
|
|
19415
|
+
entry: activeEntry,
|
|
19416
|
+
index: active.index,
|
|
19417
|
+
pending,
|
|
19418
|
+
error,
|
|
19419
|
+
itemEditOn,
|
|
19420
|
+
onToggleItemEdit: () => setItemEditOn((on) => !on),
|
|
19421
|
+
onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
|
|
19422
|
+
onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
|
|
19423
|
+
onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
|
|
19424
|
+
onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
|
|
19425
|
+
onResize: (delta) => {
|
|
19426
|
+
const slots = findMediaStripSlots(activeEntry.tree);
|
|
19427
|
+
const variant = slots?.variant === "reel" ? "reel" : "logos";
|
|
19428
|
+
const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
|
|
19429
|
+
void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
|
|
19430
|
+
},
|
|
19431
|
+
onOpenLink: () => setLinkOpen(true),
|
|
19432
|
+
onAddItem: () => {
|
|
19433
|
+
void requestAssetPick(postToParent2).then((picked) => {
|
|
19434
|
+
if (!picked) return;
|
|
19435
|
+
const item = { assetId: picked.assetId, alt: "Logo" };
|
|
18998
19436
|
void runMutation(
|
|
18999
19437
|
active.sectionId,
|
|
19000
|
-
(tree) =>
|
|
19001
|
-
|
|
19002
|
-
|
|
19003
|
-
|
|
19004
|
-
void requestAssetPick(postToParent2).then((picked) => {
|
|
19005
|
-
if (!picked) return;
|
|
19006
|
-
const item = { assetId: picked.assetId, alt: "Logo" };
|
|
19007
|
-
void runMutation(
|
|
19008
|
-
active.sectionId,
|
|
19009
|
-
(tree) => addMediaStripItem(tree, item, active.index),
|
|
19010
|
-
{ [picked.assetId]: picked.url }
|
|
19011
|
-
).then((applied) => {
|
|
19012
|
-
if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
|
|
19013
|
-
});
|
|
19014
|
-
});
|
|
19015
|
-
},
|
|
19016
|
-
onChangeImage: () => {
|
|
19017
|
-
void requestAssetPick(postToParent2).then((picked) => {
|
|
19018
|
-
if (!picked) return;
|
|
19019
|
-
void runMutation(
|
|
19020
|
-
active.sectionId,
|
|
19021
|
-
(tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
|
|
19022
|
-
{ [picked.assetId]: picked.url }
|
|
19023
|
-
);
|
|
19438
|
+
(tree) => addMediaStripItem(tree, item, active.index),
|
|
19439
|
+
{ [picked.assetId]: picked.url }
|
|
19440
|
+
).then((applied) => {
|
|
19441
|
+
if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
|
|
19024
19442
|
});
|
|
19025
|
-
}
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
19029
|
-
|
|
19030
|
-
|
|
19031
|
-
|
|
19032
|
-
|
|
19033
|
-
|
|
19034
|
-
void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
|
|
19035
|
-
(applied) => {
|
|
19036
|
-
if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
|
|
19037
|
-
}
|
|
19038
|
-
);
|
|
19039
|
-
},
|
|
19040
|
-
onMoveRight: () => {
|
|
19041
|
-
void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
|
|
19042
|
-
(applied) => {
|
|
19043
|
-
if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
|
|
19044
|
-
}
|
|
19443
|
+
});
|
|
19444
|
+
},
|
|
19445
|
+
onChangeImage: () => {
|
|
19446
|
+
void requestAssetPick(postToParent2).then((picked) => {
|
|
19447
|
+
if (!picked) return;
|
|
19448
|
+
void runMutation(
|
|
19449
|
+
active.sectionId,
|
|
19450
|
+
(tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
|
|
19451
|
+
{ [picked.assetId]: picked.url }
|
|
19045
19452
|
);
|
|
19046
|
-
}
|
|
19453
|
+
});
|
|
19454
|
+
},
|
|
19455
|
+
onRemoveItem: () => {
|
|
19456
|
+
void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
|
|
19457
|
+
(applied) => {
|
|
19458
|
+
if (applied) setActive(null);
|
|
19459
|
+
}
|
|
19460
|
+
);
|
|
19461
|
+
},
|
|
19462
|
+
onMoveLeft: () => {
|
|
19463
|
+
void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
|
|
19464
|
+
(applied) => {
|
|
19465
|
+
if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
|
|
19466
|
+
}
|
|
19467
|
+
);
|
|
19468
|
+
},
|
|
19469
|
+
onMoveRight: () => {
|
|
19470
|
+
void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
|
|
19471
|
+
(applied) => {
|
|
19472
|
+
if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
|
|
19473
|
+
}
|
|
19474
|
+
);
|
|
19047
19475
|
}
|
|
19048
|
-
|
|
19049
|
-
}
|
|
19050
|
-
|
|
19476
|
+
}
|
|
19477
|
+
) }),
|
|
19478
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19479
|
+
"div",
|
|
19480
|
+
{
|
|
19481
|
+
"data-ohw-bridge": "",
|
|
19482
|
+
"data-ohw-item-interaction": "",
|
|
19483
|
+
"aria-hidden": true,
|
|
19484
|
+
className: "pointer-events-none fixed rounded-[4px] px-[7px] py-[3px] font-sans text-[11px] font-medium leading-none text-primary-foreground whitespace-nowrap",
|
|
19485
|
+
style: {
|
|
19486
|
+
top: activeItemRect.top + activeItemRect.height + 10,
|
|
19487
|
+
left: activeItemRect.left - 5,
|
|
19488
|
+
background: PRIMARY3,
|
|
19489
|
+
zIndex: 2147483644
|
|
19490
|
+
},
|
|
19491
|
+
children: "Media item"
|
|
19492
|
+
}
|
|
19493
|
+
),
|
|
19494
|
+
linkOpen && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19495
|
+
LinkModal,
|
|
19496
|
+
{
|
|
19497
|
+
href: activeHref,
|
|
19498
|
+
linkTarget: activeTarget,
|
|
19499
|
+
onClose: () => setLinkOpen(false),
|
|
19500
|
+
onSave: (href, linkTarget) => {
|
|
19501
|
+
void runMutation(
|
|
19502
|
+
active.sectionId,
|
|
19503
|
+
(tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
|
|
19504
|
+
);
|
|
19505
|
+
}
|
|
19506
|
+
}
|
|
19507
|
+
)
|
|
19508
|
+
] })
|
|
19051
19509
|
] });
|
|
19052
19510
|
}
|
|
19053
19511
|
|
|
19054
19512
|
// src/ui/ai-section/AiPricingCardOverlay.tsx
|
|
19055
19513
|
var import_react11 = require("react");
|
|
19056
|
-
var
|
|
19057
|
-
var
|
|
19514
|
+
var import_lucide_react10 = require("lucide-react");
|
|
19515
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
19058
19516
|
var PRIMARY4 = "var(--ohw-primary, #0885FE)";
|
|
19059
|
-
var
|
|
19517
|
+
var CHROME_SELECTOR2 = "[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]";
|
|
19060
19518
|
function findPricingCardElement(sectionId, index) {
|
|
19061
19519
|
const sectionEl = findByInstanceId(sectionId);
|
|
19062
19520
|
if (!sectionEl) return null;
|
|
@@ -19068,7 +19526,7 @@ function findPricingCardElement(sectionId, index) {
|
|
|
19068
19526
|
}
|
|
19069
19527
|
function cardRefFromTarget(target) {
|
|
19070
19528
|
if (!(target instanceof Element)) return null;
|
|
19071
|
-
if (target.closest(
|
|
19529
|
+
if (target.closest(CHROME_SELECTOR2)) return null;
|
|
19072
19530
|
const cardEl = target.closest("[data-ohw-pricing-card]");
|
|
19073
19531
|
if (!cardEl) return null;
|
|
19074
19532
|
const sectionEl = cardEl.closest("[data-ohw-section]");
|
|
@@ -19078,21 +19536,21 @@ function cardRefFromTarget(target) {
|
|
|
19078
19536
|
if (!sectionId || Number.isNaN(index)) return null;
|
|
19079
19537
|
return { sectionId, index };
|
|
19080
19538
|
}
|
|
19081
|
-
function
|
|
19539
|
+
function sameRef2(a, b) {
|
|
19082
19540
|
return a?.sectionId === b?.sectionId && a?.index === b?.index;
|
|
19083
19541
|
}
|
|
19084
|
-
function
|
|
19542
|
+
function rectOf2(el) {
|
|
19085
19543
|
if (!el) return null;
|
|
19086
19544
|
const r2 = el.getBoundingClientRect();
|
|
19087
19545
|
if (r2.width <= 0 || r2.height <= 0) return null;
|
|
19088
19546
|
return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
|
|
19089
19547
|
}
|
|
19090
19548
|
function readRect3(ref) {
|
|
19091
|
-
return
|
|
19549
|
+
return rectOf2(ref && findPricingCardElement(ref.sectionId, ref.index));
|
|
19092
19550
|
}
|
|
19093
19551
|
function readGridRect(ref) {
|
|
19094
19552
|
const card = ref && findPricingCardElement(ref.sectionId, ref.index);
|
|
19095
|
-
return
|
|
19553
|
+
return rectOf2(card?.closest("[data-ohw-pricing-collection]") ?? card);
|
|
19096
19554
|
}
|
|
19097
19555
|
function useLiveCardRect(ref, grid = false) {
|
|
19098
19556
|
const [rect, setRect] = (0, import_react11.useState)(null);
|
|
@@ -19122,7 +19580,7 @@ function useLiveCardRect(ref, grid = false) {
|
|
|
19122
19580
|
}
|
|
19123
19581
|
var TOOLBAR_GRID_GAP = 52;
|
|
19124
19582
|
var TOOLBAR_GRID_GAP_MOBILE = 42;
|
|
19125
|
-
var
|
|
19583
|
+
var TOOLBAR_EDGE_MARGIN3 = 4;
|
|
19126
19584
|
function GridToolbarSlot({ gridRect, children }) {
|
|
19127
19585
|
const slotRef = (0, import_react11.useRef)(null);
|
|
19128
19586
|
const [shift, setShift] = (0, import_react11.useState)(0);
|
|
@@ -19134,7 +19592,7 @@ function GridToolbarSlot({ gridRect, children }) {
|
|
|
19134
19592
|
const w = slot.offsetWidth;
|
|
19135
19593
|
if (w <= 0) return;
|
|
19136
19594
|
const half = w / 2;
|
|
19137
|
-
const clamped = Math.max(
|
|
19595
|
+
const clamped = Math.max(TOOLBAR_EDGE_MARGIN3 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half));
|
|
19138
19596
|
setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
|
|
19139
19597
|
};
|
|
19140
19598
|
clamp();
|
|
@@ -19143,7 +19601,7 @@ function GridToolbarSlot({ gridRect, children }) {
|
|
|
19143
19601
|
return () => ro.disconnect();
|
|
19144
19602
|
}, [centerX, children]);
|
|
19145
19603
|
const mobile = window.innerWidth <= 480;
|
|
19146
|
-
return /* @__PURE__ */ (0,
|
|
19604
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
19147
19605
|
"div",
|
|
19148
19606
|
{
|
|
19149
19607
|
ref: slotRef,
|
|
@@ -19178,7 +19636,7 @@ function requestValidateTree2(postToParent2, tree) {
|
|
|
19178
19636
|
});
|
|
19179
19637
|
}
|
|
19180
19638
|
function StyleSwatch({ style }) {
|
|
19181
|
-
return /* @__PURE__ */ (0,
|
|
19639
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
19182
19640
|
"span",
|
|
19183
19641
|
{
|
|
19184
19642
|
"aria-hidden": true,
|
|
@@ -19192,35 +19650,35 @@ function StyleSwatch({ style }) {
|
|
|
19192
19650
|
);
|
|
19193
19651
|
}
|
|
19194
19652
|
function ButtonGlyph() {
|
|
19195
|
-
return /* @__PURE__ */ (0,
|
|
19196
|
-
/* @__PURE__ */ (0,
|
|
19197
|
-
/* @__PURE__ */ (0,
|
|
19653
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
|
|
19654
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
19655
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
19198
19656
|
] });
|
|
19199
19657
|
}
|
|
19200
19658
|
function FeatureGlyph({ mode }) {
|
|
19201
|
-
return /* @__PURE__ */ (0,
|
|
19202
|
-
/* @__PURE__ */ (0,
|
|
19203
|
-
/* @__PURE__ */ (0,
|
|
19204
|
-
/* @__PURE__ */ (0,
|
|
19205
|
-
/* @__PURE__ */ (0,
|
|
19206
|
-
/* @__PURE__ */ (0,
|
|
19207
|
-
mode === "add" && /* @__PURE__ */ (0,
|
|
19659
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
|
|
19660
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
19661
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
19662
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
|
|
19663
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
|
|
19664
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
19665
|
+
mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
|
|
19208
19666
|
] });
|
|
19209
19667
|
}
|
|
19210
|
-
function
|
|
19668
|
+
function ToolbarAction2({
|
|
19211
19669
|
label,
|
|
19212
19670
|
onClick,
|
|
19213
19671
|
disabled = false,
|
|
19214
19672
|
children
|
|
19215
19673
|
}) {
|
|
19216
|
-
const button = /* @__PURE__ */ (0,
|
|
19217
|
-
return /* @__PURE__ */ (0,
|
|
19218
|
-
/* @__PURE__ */ (0,
|
|
19219
|
-
/* @__PURE__ */ (0,
|
|
19674
|
+
const button = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
|
|
19675
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
|
|
19676
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "inline-flex", children: button }) : button }),
|
|
19677
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
|
|
19220
19678
|
] });
|
|
19221
19679
|
}
|
|
19222
|
-
function
|
|
19223
|
-
return /* @__PURE__ */ (0,
|
|
19680
|
+
function ToolbarSeparator2() {
|
|
19681
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
|
|
19224
19682
|
}
|
|
19225
19683
|
function CardStyleMenu({
|
|
19226
19684
|
value,
|
|
@@ -19228,7 +19686,7 @@ function CardStyleMenu({
|
|
|
19228
19686
|
disabled = false
|
|
19229
19687
|
}) {
|
|
19230
19688
|
const current = PRICING_CARD_STYLES.find((s) => s.value === value) ?? PRICING_CARD_STYLES[0];
|
|
19231
|
-
const trigger = /* @__PURE__ */ (0,
|
|
19689
|
+
const trigger = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
19232
19690
|
"button",
|
|
19233
19691
|
{
|
|
19234
19692
|
type: "button",
|
|
@@ -19239,28 +19697,28 @@ function CardStyleMenu({
|
|
|
19239
19697
|
disabled && "cursor-not-allowed opacity-40 hover:bg-transparent"
|
|
19240
19698
|
),
|
|
19241
19699
|
children: [
|
|
19242
|
-
/* @__PURE__ */ (0,
|
|
19243
|
-
/* @__PURE__ */ (0,
|
|
19244
|
-
/* @__PURE__ */ (0,
|
|
19700
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StyleSwatch, { style: current.value }),
|
|
19701
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "flex-1 text-left", children: current.label }),
|
|
19702
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
|
|
19245
19703
|
]
|
|
19246
19704
|
}
|
|
19247
19705
|
);
|
|
19248
19706
|
if (disabled) {
|
|
19249
|
-
return /* @__PURE__ */ (0,
|
|
19250
|
-
/* @__PURE__ */ (0,
|
|
19251
|
-
/* @__PURE__ */ (0,
|
|
19252
|
-
/* @__PURE__ */ (0,
|
|
19253
|
-
/* @__PURE__ */ (0,
|
|
19707
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
|
|
19708
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
|
|
19709
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
|
|
19710
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "inline-flex", children: trigger }) }),
|
|
19711
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
|
|
19254
19712
|
] })
|
|
19255
19713
|
] });
|
|
19256
19714
|
}
|
|
19257
|
-
return /* @__PURE__ */ (0,
|
|
19258
|
-
/* @__PURE__ */ (0,
|
|
19259
|
-
/* @__PURE__ */ (0,
|
|
19260
|
-
/* @__PURE__ */ (0,
|
|
19261
|
-
/* @__PURE__ */ (0,
|
|
19715
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
|
|
19716
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
|
|
19717
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DropdownMenu, { modal: false, children: [
|
|
19718
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
|
|
19719
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
|
|
19262
19720
|
const selected = style.value === value;
|
|
19263
|
-
return /* @__PURE__ */ (0,
|
|
19721
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
19264
19722
|
DropdownMenuItem,
|
|
19265
19723
|
{
|
|
19266
19724
|
role: "menuitemradio",
|
|
@@ -19268,9 +19726,9 @@ function CardStyleMenu({
|
|
|
19268
19726
|
onSelect: () => onChange(style.value),
|
|
19269
19727
|
className: cn("grid grid-cols-[18px_1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
|
|
19270
19728
|
children: [
|
|
19271
|
-
/* @__PURE__ */ (0,
|
|
19272
|
-
/* @__PURE__ */ (0,
|
|
19273
|
-
selected && /* @__PURE__ */ (0,
|
|
19729
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StyleSwatch, { style: style.value }),
|
|
19730
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: style.label }),
|
|
19731
|
+
selected && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Check, { size: 16, className: "text-bridge-primary" })
|
|
19274
19732
|
]
|
|
19275
19733
|
},
|
|
19276
19734
|
style.value
|
|
@@ -19300,7 +19758,7 @@ function PricingCardToolbar({
|
|
|
19300
19758
|
const style = pricingCardStyle(entry.tree, index);
|
|
19301
19759
|
const featuredTag = pricingCardFeaturedTag(entry.tree, index);
|
|
19302
19760
|
const mobile = window.innerWidth <= 480;
|
|
19303
|
-
return /* @__PURE__ */ (0,
|
|
19761
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
19304
19762
|
"div",
|
|
19305
19763
|
{
|
|
19306
19764
|
"data-ohw-bridge": "",
|
|
@@ -19312,7 +19770,7 @@ function PricingCardToolbar({
|
|
|
19312
19770
|
),
|
|
19313
19771
|
style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
|
|
19314
19772
|
children: [
|
|
19315
|
-
/* @__PURE__ */ (0,
|
|
19773
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
19316
19774
|
"div",
|
|
19317
19775
|
{
|
|
19318
19776
|
className: cn(
|
|
@@ -19321,8 +19779,8 @@ function PricingCardToolbar({
|
|
|
19321
19779
|
mobile ? "flex-wrap justify-center" : "flex-nowrap"
|
|
19322
19780
|
),
|
|
19323
19781
|
children: [
|
|
19324
|
-
/* @__PURE__ */ (0,
|
|
19325
|
-
/* @__PURE__ */ (0,
|
|
19782
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
|
|
19783
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
19326
19784
|
CustomToolbarButton,
|
|
19327
19785
|
{
|
|
19328
19786
|
type: "button",
|
|
@@ -19330,8 +19788,8 @@ function PricingCardToolbar({
|
|
|
19330
19788
|
"aria-pressed": featuredTag,
|
|
19331
19789
|
onClick: () => onSetFeaturedTag(!featuredTag),
|
|
19332
19790
|
className: "group size-8",
|
|
19333
|
-
children: /* @__PURE__ */ (0,
|
|
19334
|
-
|
|
19791
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
19792
|
+
import_lucide_react10.Star,
|
|
19335
19793
|
{
|
|
19336
19794
|
size: 16,
|
|
19337
19795
|
fill: "currentColor",
|
|
@@ -19345,25 +19803,25 @@ function PricingCardToolbar({
|
|
|
19345
19803
|
)
|
|
19346
19804
|
}
|
|
19347
19805
|
) }),
|
|
19348
|
-
/* @__PURE__ */ (0,
|
|
19806
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
|
|
19349
19807
|
] }),
|
|
19350
|
-
/* @__PURE__ */ (0,
|
|
19351
|
-
/* @__PURE__ */ (0,
|
|
19352
|
-
/* @__PURE__ */ (0,
|
|
19353
|
-
/* @__PURE__ */ (0,
|
|
19354
|
-
/* @__PURE__ */ (0,
|
|
19355
|
-
/* @__PURE__ */ (0,
|
|
19356
|
-
/* @__PURE__ */ (0,
|
|
19808
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
|
|
19809
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
|
|
19810
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
|
|
19811
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ButtonGlyph, {}) }),
|
|
19812
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "relative inline-flex", children: [
|
|
19813
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ButtonGlyph, {}),
|
|
19814
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Minus, { size: 10, strokeWidth: 2.5, className: "absolute -right-1.5 -top-1.5 rounded-full bg-background text-bridge-primary" })
|
|
19357
19815
|
] }) }),
|
|
19358
|
-
/* @__PURE__ */ (0,
|
|
19359
|
-
/* @__PURE__ */ (0,
|
|
19360
|
-
/* @__PURE__ */ (0,
|
|
19361
|
-
/* @__PURE__ */ (0,
|
|
19362
|
-
/* @__PURE__ */ (0,
|
|
19816
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FeatureGlyph, { mode: "add" }) }),
|
|
19817
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FeatureGlyph, { mode: "remove" }) }),
|
|
19818
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
|
|
19819
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
|
|
19820
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Minus, { size: 16, style: { pointerEvents: "none" } }) })
|
|
19363
19821
|
]
|
|
19364
19822
|
}
|
|
19365
19823
|
),
|
|
19366
|
-
error && /* @__PURE__ */ (0,
|
|
19824
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
|
|
19367
19825
|
]
|
|
19368
19826
|
}
|
|
19369
19827
|
) });
|
|
@@ -19397,7 +19855,7 @@ function AiPricingCardOverlay({
|
|
|
19397
19855
|
const overEditable = Boolean(t?.closest?.("[data-ohw-editable], [data-ohw-role], a, button"));
|
|
19398
19856
|
setHover((prev) => {
|
|
19399
19857
|
const next = ref && !overEditable ? ref : null;
|
|
19400
|
-
return
|
|
19858
|
+
return sameRef2(prev, next) ? prev : next;
|
|
19401
19859
|
});
|
|
19402
19860
|
};
|
|
19403
19861
|
const onLeave = () => setHover(null);
|
|
@@ -19412,10 +19870,10 @@ function AiPricingCardOverlay({
|
|
|
19412
19870
|
const onClick = (e) => {
|
|
19413
19871
|
const t = e.target;
|
|
19414
19872
|
if (!(t instanceof Element)) return;
|
|
19415
|
-
if (t.closest(
|
|
19873
|
+
if (t.closest(CHROME_SELECTOR2)) return;
|
|
19416
19874
|
const ref = cardRefFromTarget(t);
|
|
19417
|
-
setActive((prev) =>
|
|
19418
|
-
if (!ref || !
|
|
19875
|
+
setActive((prev) => sameRef2(prev, ref) ? prev : ref);
|
|
19876
|
+
if (!ref || !sameRef2(activeRef.current, ref)) setError(null);
|
|
19419
19877
|
};
|
|
19420
19878
|
document.addEventListener("click", onClick, true);
|
|
19421
19879
|
return () => document.removeEventListener("click", onClick, true);
|
|
@@ -19480,12 +19938,12 @@ function AiPricingCardOverlay({
|
|
|
19480
19938
|
const activeEntry = active ? findEntry(active.sectionId) : null;
|
|
19481
19939
|
const activeCardRect = useLiveCardRect(active);
|
|
19482
19940
|
const activeGridRect = useLiveCardRect(active, true);
|
|
19483
|
-
const hoverRect = useLiveCardRect(hover && !
|
|
19484
|
-
return /* @__PURE__ */ (0,
|
|
19485
|
-
hoverRect && /* @__PURE__ */ (0,
|
|
19486
|
-
activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0,
|
|
19487
|
-
/* @__PURE__ */ (0,
|
|
19488
|
-
/* @__PURE__ */ (0,
|
|
19941
|
+
const hoverRect = useLiveCardRect(hover && !sameRef2(hover, active) ? hover : null);
|
|
19942
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
|
|
19943
|
+
hoverRect && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
|
|
19944
|
+
activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
|
|
19945
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
|
|
19946
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
19489
19947
|
PricingCardToolbar,
|
|
19490
19948
|
{
|
|
19491
19949
|
entry: activeEntry,
|
|
@@ -19513,7 +19971,7 @@ function AiPricingCardOverlay({
|
|
|
19513
19971
|
onRemoveFeature: () => runMutation(active.sectionId, (tree) => removePricingCardFeature(tree, active.index))
|
|
19514
19972
|
}
|
|
19515
19973
|
) }),
|
|
19516
|
-
/* @__PURE__ */ (0,
|
|
19974
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
19517
19975
|
"div",
|
|
19518
19976
|
{
|
|
19519
19977
|
"data-ohw-bridge": "",
|
|
@@ -19535,7 +19993,7 @@ function AiPricingCardOverlay({
|
|
|
19535
19993
|
|
|
19536
19994
|
// src/ui/ai-section/AiVideoOverlay.tsx
|
|
19537
19995
|
var import_react12 = require("react");
|
|
19538
|
-
var
|
|
19996
|
+
var import_lucide_react11 = require("lucide-react");
|
|
19539
19997
|
|
|
19540
19998
|
// src/lib/ai-video-tree.ts
|
|
19541
19999
|
function resolveVideoBlock(tree, path) {
|
|
@@ -19606,10 +20064,10 @@ function extractYouTubeId(input) {
|
|
|
19606
20064
|
}
|
|
19607
20065
|
|
|
19608
20066
|
// src/ui/ai-section/AiVideoOverlay.tsx
|
|
19609
|
-
var
|
|
20067
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
19610
20068
|
var PRIMARY5 = "#0885FE";
|
|
19611
|
-
var
|
|
19612
|
-
var
|
|
20069
|
+
var OHW_BLUE = `var(--ohw-primary, ${PRIMARY5})`;
|
|
20070
|
+
var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
|
|
19613
20071
|
function findVideoBlockElement(sectionId, path) {
|
|
19614
20072
|
const sectionEl = findByInstanceId(sectionId);
|
|
19615
20073
|
if (!sectionEl) return null;
|
|
@@ -19669,12 +20127,12 @@ function requestValidateTree3(postToParent2, tree) {
|
|
|
19669
20127
|
postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
|
|
19670
20128
|
});
|
|
19671
20129
|
}
|
|
19672
|
-
function
|
|
20130
|
+
function Pill({
|
|
19673
20131
|
label,
|
|
19674
20132
|
onClick,
|
|
19675
20133
|
disabled
|
|
19676
20134
|
}) {
|
|
19677
|
-
return /* @__PURE__ */ (0,
|
|
20135
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
19678
20136
|
"button",
|
|
19679
20137
|
{
|
|
19680
20138
|
type: "button",
|
|
@@ -19690,7 +20148,7 @@ function Pill2({
|
|
|
19690
20148
|
border: "none",
|
|
19691
20149
|
cursor: disabled ? "default" : "pointer",
|
|
19692
20150
|
background: "#ffffff",
|
|
19693
|
-
color:
|
|
20151
|
+
color: OHW_BLUE_DARK,
|
|
19694
20152
|
opacity: disabled ? 0.4 : 1,
|
|
19695
20153
|
fontSize: 12,
|
|
19696
20154
|
lineHeight: "16px",
|
|
@@ -19733,8 +20191,8 @@ function VideoUrlInput({ value, onCommit }) {
|
|
|
19733
20191
|
onCommit(id);
|
|
19734
20192
|
}
|
|
19735
20193
|
};
|
|
19736
|
-
return /* @__PURE__ */ (0,
|
|
19737
|
-
/* @__PURE__ */ (0,
|
|
20194
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
|
|
20195
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
19738
20196
|
"div",
|
|
19739
20197
|
{
|
|
19740
20198
|
style: {
|
|
@@ -19750,12 +20208,12 @@ function VideoUrlInput({ value, onCommit }) {
|
|
|
19750
20208
|
boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
|
|
19751
20209
|
},
|
|
19752
20210
|
children: [
|
|
19753
|
-
/* @__PURE__ */ (0,
|
|
20211
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
19754
20212
|
"span",
|
|
19755
20213
|
{
|
|
19756
20214
|
style: {
|
|
19757
20215
|
flex: "0 0 auto",
|
|
19758
|
-
color:
|
|
20216
|
+
color: OHW_BLUE_DARK,
|
|
19759
20217
|
fontSize: 10,
|
|
19760
20218
|
lineHeight: "16px",
|
|
19761
20219
|
fontWeight: 700,
|
|
@@ -19765,7 +20223,7 @@ function VideoUrlInput({ value, onCommit }) {
|
|
|
19765
20223
|
children: "Video"
|
|
19766
20224
|
}
|
|
19767
20225
|
),
|
|
19768
|
-
/* @__PURE__ */ (0,
|
|
20226
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
19769
20227
|
"input",
|
|
19770
20228
|
{
|
|
19771
20229
|
type: "text",
|
|
@@ -19802,7 +20260,7 @@ function VideoUrlInput({ value, onCommit }) {
|
|
|
19802
20260
|
]
|
|
19803
20261
|
}
|
|
19804
20262
|
),
|
|
19805
|
-
invalid && /* @__PURE__ */ (0,
|
|
20263
|
+
invalid && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, textAlign: "center" }, children: "That doesn't look like a YouTube link \u2014 try pasting the video URL again." })
|
|
19806
20264
|
] });
|
|
19807
20265
|
}
|
|
19808
20266
|
function VideoToolbar({
|
|
@@ -19812,7 +20270,7 @@ function VideoToolbar({
|
|
|
19812
20270
|
onSetVideoId,
|
|
19813
20271
|
onRemove
|
|
19814
20272
|
}) {
|
|
19815
|
-
return /* @__PURE__ */ (0,
|
|
20273
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
19816
20274
|
"div",
|
|
19817
20275
|
{
|
|
19818
20276
|
"data-ohw-bridge": "",
|
|
@@ -19824,7 +20282,7 @@ function VideoToolbar({
|
|
|
19824
20282
|
gap: 6,
|
|
19825
20283
|
alignItems: "center",
|
|
19826
20284
|
width: 280,
|
|
19827
|
-
background:
|
|
20285
|
+
background: OHW_BLUE,
|
|
19828
20286
|
padding: 8,
|
|
19829
20287
|
borderRadius: 12,
|
|
19830
20288
|
boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
|
|
@@ -19832,9 +20290,9 @@ function VideoToolbar({
|
|
|
19832
20290
|
opacity: pending ? 0.6 : 1
|
|
19833
20291
|
},
|
|
19834
20292
|
children: [
|
|
19835
|
-
/* @__PURE__ */ (0,
|
|
19836
|
-
/* @__PURE__ */ (0,
|
|
19837
|
-
error && /* @__PURE__ */ (0,
|
|
20293
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
|
|
20294
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Pill, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
|
|
20295
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
|
|
19838
20296
|
]
|
|
19839
20297
|
}
|
|
19840
20298
|
);
|
|
@@ -19954,8 +20412,8 @@ function AiVideoOverlay({
|
|
|
19954
20412
|
const activeBlockRect = useLiveBlockRect(active);
|
|
19955
20413
|
const TOOLBAR_APPROX_H = 90;
|
|
19956
20414
|
const toolbarState = activeBlockRect && activeBlockRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
|
|
19957
|
-
return /* @__PURE__ */ (0,
|
|
19958
|
-
hoverHandleRect && hover && /* @__PURE__ */ (0,
|
|
20415
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
20416
|
+
hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
19959
20417
|
"button",
|
|
19960
20418
|
{
|
|
19961
20419
|
type: "button",
|
|
@@ -19983,15 +20441,15 @@ function AiVideoOverlay({
|
|
|
19983
20441
|
fontSize: 14,
|
|
19984
20442
|
fontWeight: 700
|
|
19985
20443
|
},
|
|
19986
|
-
children: /* @__PURE__ */ (0,
|
|
20444
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
|
|
19987
20445
|
}
|
|
19988
20446
|
),
|
|
19989
|
-
activeBlockRect && active && activeEntry && /* @__PURE__ */ (0,
|
|
20447
|
+
activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
19990
20448
|
ItemInteractionLayer,
|
|
19991
20449
|
{
|
|
19992
20450
|
rect: activeBlockRect,
|
|
19993
20451
|
state: toolbarState,
|
|
19994
|
-
toolbar: /* @__PURE__ */ (0,
|
|
20452
|
+
toolbar: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
19995
20453
|
VideoToolbar,
|
|
19996
20454
|
{
|
|
19997
20455
|
videoId: videoIdAt(activeEntry.tree, active.path),
|
|
@@ -20015,7 +20473,7 @@ function AiVideoOverlay({
|
|
|
20015
20473
|
|
|
20016
20474
|
// src/ui/ai-section/AiFadeCarouselOverlay.tsx
|
|
20017
20475
|
var import_react13 = require("react");
|
|
20018
|
-
var
|
|
20476
|
+
var import_lucide_react12 = require("lucide-react");
|
|
20019
20477
|
|
|
20020
20478
|
// src/lib/ai-carousel-tree.ts
|
|
20021
20479
|
function isCarouselBlock(node) {
|
|
@@ -20066,10 +20524,10 @@ function setCarouselAutoplay(tree, on) {
|
|
|
20066
20524
|
}
|
|
20067
20525
|
|
|
20068
20526
|
// src/ui/ai-section/AiFadeCarouselOverlay.tsx
|
|
20069
|
-
var
|
|
20527
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
20070
20528
|
var PRIMARY6 = "#0885FE";
|
|
20071
|
-
var
|
|
20072
|
-
var
|
|
20529
|
+
var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY6})`;
|
|
20530
|
+
var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
|
|
20073
20531
|
function findCarouselItemElement(sectionId, index) {
|
|
20074
20532
|
const sectionEl = findByInstanceId(sectionId);
|
|
20075
20533
|
if (!sectionEl) return null;
|
|
@@ -20148,8 +20606,8 @@ function requestAssetPick2(postToParent2) {
|
|
|
20148
20606
|
postToParent2({ type: "ow:asset-pick", id });
|
|
20149
20607
|
});
|
|
20150
20608
|
}
|
|
20151
|
-
function
|
|
20152
|
-
return /* @__PURE__ */ (0,
|
|
20609
|
+
function Pill2({ label, onClick, disabled }) {
|
|
20610
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20153
20611
|
"button",
|
|
20154
20612
|
{
|
|
20155
20613
|
type: "button",
|
|
@@ -20165,7 +20623,7 @@ function Pill3({ label, onClick, disabled }) {
|
|
|
20165
20623
|
border: "none",
|
|
20166
20624
|
cursor: disabled ? "default" : "pointer",
|
|
20167
20625
|
background: "#ffffff",
|
|
20168
|
-
color:
|
|
20626
|
+
color: OHW_BLUE_DARK2,
|
|
20169
20627
|
opacity: disabled ? 0.4 : 1,
|
|
20170
20628
|
fontSize: 12,
|
|
20171
20629
|
lineHeight: "16px",
|
|
@@ -20183,7 +20641,7 @@ function TogglePill({
|
|
|
20183
20641
|
onToggle,
|
|
20184
20642
|
disabled
|
|
20185
20643
|
}) {
|
|
20186
|
-
return /* @__PURE__ */ (0,
|
|
20644
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
20187
20645
|
"button",
|
|
20188
20646
|
{
|
|
20189
20647
|
type: "button",
|
|
@@ -20202,7 +20660,7 @@ function TogglePill({
|
|
|
20202
20660
|
border: "none",
|
|
20203
20661
|
cursor: disabled ? "default" : "pointer",
|
|
20204
20662
|
background: "#ffffff",
|
|
20205
|
-
color:
|
|
20663
|
+
color: OHW_BLUE_DARK2,
|
|
20206
20664
|
opacity: disabled ? 0.4 : 1,
|
|
20207
20665
|
fontSize: 12,
|
|
20208
20666
|
lineHeight: "16px",
|
|
@@ -20212,7 +20670,7 @@ function TogglePill({
|
|
|
20212
20670
|
},
|
|
20213
20671
|
children: [
|
|
20214
20672
|
label,
|
|
20215
|
-
/* @__PURE__ */ (0,
|
|
20673
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20216
20674
|
"span",
|
|
20217
20675
|
{
|
|
20218
20676
|
"aria-hidden": true,
|
|
@@ -20225,10 +20683,10 @@ function TogglePill({
|
|
|
20225
20683
|
// Off is a grey track, not a washed-out blue: the pill's own white background already
|
|
20226
20684
|
// carries the brand colour in the text, and a pale blue track next to it read as
|
|
20227
20685
|
// "disabled control" rather than "switch that is off".
|
|
20228
|
-
background: on ?
|
|
20686
|
+
background: on ? OHW_BLUE_DARK2 : "#CBD5E1",
|
|
20229
20687
|
transition: "background 120ms ease"
|
|
20230
20688
|
},
|
|
20231
|
-
children: /* @__PURE__ */ (0,
|
|
20689
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20232
20690
|
"span",
|
|
20233
20691
|
{
|
|
20234
20692
|
style: {
|
|
@@ -20257,7 +20715,7 @@ function CarouselSlideToolbar({
|
|
|
20257
20715
|
onChangeImage,
|
|
20258
20716
|
onToggleAutoplay
|
|
20259
20717
|
}) {
|
|
20260
|
-
return /* @__PURE__ */ (0,
|
|
20718
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
20261
20719
|
"div",
|
|
20262
20720
|
{
|
|
20263
20721
|
"data-ohw-bridge": "",
|
|
@@ -20268,7 +20726,7 @@ function CarouselSlideToolbar({
|
|
|
20268
20726
|
flexDirection: "column",
|
|
20269
20727
|
gap: 6,
|
|
20270
20728
|
alignItems: "center",
|
|
20271
|
-
background:
|
|
20729
|
+
background: OHW_BLUE2,
|
|
20272
20730
|
padding: 8,
|
|
20273
20731
|
borderRadius: 12,
|
|
20274
20732
|
boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
|
|
@@ -20280,11 +20738,11 @@ function CarouselSlideToolbar({
|
|
|
20280
20738
|
pointerEvents: pending ? "none" : "auto"
|
|
20281
20739
|
},
|
|
20282
20740
|
children: [
|
|
20283
|
-
/* @__PURE__ */ (0,
|
|
20284
|
-
/* @__PURE__ */ (0,
|
|
20285
|
-
/* @__PURE__ */ (0,
|
|
20741
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
|
|
20742
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Pill2, { label: "Change Image", onClick: onChangeImage }),
|
|
20743
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
|
|
20286
20744
|
] }),
|
|
20287
|
-
error && /* @__PURE__ */ (0,
|
|
20745
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
|
|
20288
20746
|
]
|
|
20289
20747
|
}
|
|
20290
20748
|
);
|
|
@@ -20424,8 +20882,8 @@ function AiFadeCarouselOverlay({
|
|
|
20424
20882
|
const activeItemRect = useLiveItemRect2(active);
|
|
20425
20883
|
const TOOLBAR_APPROX_H = 90;
|
|
20426
20884
|
const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
|
|
20427
|
-
return /* @__PURE__ */ (0,
|
|
20428
|
-
hoverHandleRect && hover && /* @__PURE__ */ (0,
|
|
20885
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
|
|
20886
|
+
hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20429
20887
|
"button",
|
|
20430
20888
|
{
|
|
20431
20889
|
type: "button",
|
|
@@ -20454,10 +20912,10 @@ function AiFadeCarouselOverlay({
|
|
|
20454
20912
|
fontSize: 14,
|
|
20455
20913
|
fontWeight: 700
|
|
20456
20914
|
},
|
|
20457
|
-
children: /* @__PURE__ */ (0,
|
|
20915
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.Star, { size: 14, style: { pointerEvents: "none" } })
|
|
20458
20916
|
}
|
|
20459
20917
|
),
|
|
20460
|
-
swapping && /* @__PURE__ */ (0,
|
|
20918
|
+
swapping && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20461
20919
|
"div",
|
|
20462
20920
|
{
|
|
20463
20921
|
ref: skeletonRef,
|
|
@@ -20475,15 +20933,15 @@ function AiFadeCarouselOverlay({
|
|
|
20475
20933
|
zIndex: 2147483646,
|
|
20476
20934
|
pointerEvents: "none"
|
|
20477
20935
|
},
|
|
20478
|
-
children: /* @__PURE__ */ (0,
|
|
20936
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("style", { children: MEDIA_SKELETON_CSS })
|
|
20479
20937
|
}
|
|
20480
20938
|
),
|
|
20481
|
-
activeItemRect && active && activeEntry && /* @__PURE__ */ (0,
|
|
20939
|
+
activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20482
20940
|
ItemInteractionLayer,
|
|
20483
20941
|
{
|
|
20484
20942
|
rect: activeItemRect,
|
|
20485
20943
|
state: toolbarState,
|
|
20486
|
-
toolbar: /* @__PURE__ */ (0,
|
|
20944
|
+
toolbar: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20487
20945
|
CarouselSlideToolbar,
|
|
20488
20946
|
{
|
|
20489
20947
|
pending,
|
|
@@ -20576,165 +21034,30 @@ function findButtonAtPoint(x, y, media) {
|
|
|
20576
21034
|
if (hits.length === 0) return null;
|
|
20577
21035
|
return hits.reduce((best, el) => best.contains(el) ? el : best);
|
|
20578
21036
|
}
|
|
20579
|
-
function findNavbarCtaAtPoint(x, y) {
|
|
20580
|
-
const hits = Array.from(
|
|
20581
|
-
document.querySelectorAll('[data-ohw-role="navbar-button"]')
|
|
20582
|
-
).filter((el) => containsPoint(el, x, y) && !el.closest("[data-ohw-bridge]"));
|
|
20583
|
-
if (hits.length === 0) return null;
|
|
20584
|
-
return hits.reduce((best, el) => best.contains(el) ? el : best);
|
|
20585
|
-
}
|
|
20586
|
-
|
|
20587
|
-
// src/lib/session-search.ts
|
|
20588
|
-
var STORAGE_KEY = "ohw-preserved-search";
|
|
20589
|
-
function readPreservedSearch() {
|
|
20590
|
-
if (typeof window === "undefined") return "";
|
|
20591
|
-
const fromUrl = window.location.search;
|
|
20592
|
-
if (fromUrl && fromUrl !== "?") {
|
|
20593
|
-
sessionStorage.setItem(STORAGE_KEY, fromUrl);
|
|
20594
|
-
return fromUrl;
|
|
20595
|
-
}
|
|
20596
|
-
return sessionStorage.getItem(STORAGE_KEY) ?? "";
|
|
20597
|
-
}
|
|
20598
|
-
function isEditSessionActive() {
|
|
20599
|
-
const search = readPreservedSearch();
|
|
20600
|
-
if (!search || search === "?") return false;
|
|
20601
|
-
const q = search.startsWith("?") ? search.slice(1) : search;
|
|
20602
|
-
return new URLSearchParams(q).get("mode") === "edit";
|
|
20603
|
-
}
|
|
20604
|
-
|
|
20605
|
-
// src/ui/link-modal/linkModal.utils.ts
|
|
20606
|
-
function parseTarget(target) {
|
|
20607
|
-
if (!target) return { pageRoute: "", sectionId: null };
|
|
20608
|
-
const hashIndex = target.indexOf("#");
|
|
20609
|
-
if (hashIndex === -1) return { pageRoute: target, sectionId: null };
|
|
20610
|
-
return {
|
|
20611
|
-
pageRoute: target.slice(0, hashIndex),
|
|
20612
|
-
sectionId: target.slice(hashIndex + 1) || null
|
|
20613
|
-
};
|
|
20614
|
-
}
|
|
20615
|
-
function normalizePath(path) {
|
|
20616
|
-
const trimmed = path.trim();
|
|
20617
|
-
if (!trimmed) return "/";
|
|
20618
|
-
try {
|
|
20619
|
-
if (/^https?:\/\//i.test(trimmed)) {
|
|
20620
|
-
return new URL(trimmed).pathname || "/";
|
|
20621
|
-
}
|
|
20622
|
-
} catch {
|
|
20623
|
-
}
|
|
20624
|
-
const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
20625
|
-
if (withSlash.length > 1 && withSlash.endsWith("/")) return withSlash.slice(0, -1);
|
|
20626
|
-
return withSlash;
|
|
20627
|
-
}
|
|
20628
|
-
function findPageByPath(pages, path) {
|
|
20629
|
-
const normalized = normalizePath(path);
|
|
20630
|
-
return pages.find((p) => normalizePath(p.path) === normalized);
|
|
20631
|
-
}
|
|
20632
|
-
function inferPageTitleFromPath(path) {
|
|
20633
|
-
const normalized = normalizePath(path);
|
|
20634
|
-
if (normalized === "/") return "Home";
|
|
20635
|
-
const segment = normalized.split("/").filter(Boolean).pop() ?? "";
|
|
20636
|
-
if (!segment) return "Home";
|
|
20637
|
-
return segment.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
20638
|
-
}
|
|
20639
|
-
function isInternalPath(route) {
|
|
20640
|
-
const trimmed = route.trim();
|
|
20641
|
-
if (!trimmed) return false;
|
|
20642
|
-
if (/^https?:\/\//i.test(trimmed)) return false;
|
|
20643
|
-
if (trimmed.startsWith("/")) return true;
|
|
20644
|
-
if (trimmed.includes(" ") || trimmed.includes(".")) return false;
|
|
20645
|
-
return true;
|
|
20646
|
-
}
|
|
20647
|
-
function resolvePage(route, pages) {
|
|
20648
|
-
const found = findPageByPath(pages, route);
|
|
20649
|
-
if (found) return { path: found.path, title: found.title };
|
|
20650
|
-
return { path: normalizePath(route), title: inferPageTitleFromPath(route) };
|
|
20651
|
-
}
|
|
20652
|
-
function getSectionsForPath(sectionsByPath, pageRoute) {
|
|
20653
|
-
if (!sectionsByPath) return [];
|
|
20654
|
-
const normalized = normalizePath(pageRoute);
|
|
20655
|
-
return sectionsByPath[normalized] ?? sectionsByPath[pageRoute] ?? [];
|
|
20656
|
-
}
|
|
20657
|
-
function buildTarget(page, section) {
|
|
20658
|
-
if (section) return `${page.path}#${section.id}`;
|
|
20659
|
-
return page.path;
|
|
20660
|
-
}
|
|
20661
|
-
function isValidUrl(urlString) {
|
|
20662
|
-
try {
|
|
20663
|
-
const url = new URL(urlString);
|
|
20664
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
20665
|
-
const hostname = url.hostname;
|
|
20666
|
-
if (!hostname || hostname.length === 0) return false;
|
|
20667
|
-
if (hostname.includes(" ")) return false;
|
|
20668
|
-
if (hostname === "localhost") return true;
|
|
20669
|
-
if (!hostname.includes(".")) return false;
|
|
20670
|
-
if (!/[a-z]/i.test(hostname)) return false;
|
|
20671
|
-
return true;
|
|
20672
|
-
} catch {
|
|
20673
|
-
return false;
|
|
20674
|
-
}
|
|
20675
|
-
}
|
|
20676
|
-
function normalizeUrl(value) {
|
|
20677
|
-
const trimmed = value.trim();
|
|
20678
|
-
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) return trimmed;
|
|
20679
|
-
return `https://${trimmed}`;
|
|
20680
|
-
}
|
|
20681
|
-
function validateUrlInput(value, pages, allPages) {
|
|
20682
|
-
const trimmed = value.trim();
|
|
20683
|
-
if (!trimmed) return "";
|
|
20684
|
-
const exactPage = allPages.find((p) => p.title.toLowerCase() === trimmed.toLowerCase());
|
|
20685
|
-
if (exactPage) return "";
|
|
20686
|
-
const hasPrefixMatch = pages.some((p) => p.title.toLowerCase().startsWith(trimmed.toLowerCase()));
|
|
20687
|
-
if (hasPrefixMatch) {
|
|
20688
|
-
return "Select a page from the list or enter a valid URL";
|
|
20689
|
-
}
|
|
20690
|
-
const hasScheme = /^https?:\/\//i.test(trimmed);
|
|
20691
|
-
if (!hasScheme && !trimmed.includes(".")) {
|
|
20692
|
-
return "Please enter a valid URL (e.g., https://example.com)";
|
|
20693
|
-
}
|
|
20694
|
-
if (!isValidUrl(normalizeUrl(trimmed))) {
|
|
20695
|
-
return "Please enter a valid URL (e.g., https://example.com)";
|
|
20696
|
-
}
|
|
20697
|
-
return "";
|
|
20698
|
-
}
|
|
20699
|
-
function getEditModeInitialState(target, pages, sections) {
|
|
20700
|
-
const empty = {
|
|
20701
|
-
searchValue: "",
|
|
20702
|
-
selectedPage: null,
|
|
20703
|
-
selectedSection: null,
|
|
20704
|
-
step: "input"
|
|
20705
|
-
};
|
|
20706
|
-
if (!target) return empty;
|
|
20707
|
-
if (isValidUrl(normalizeUrl(target))) {
|
|
20708
|
-
return { searchValue: target, selectedPage: null, selectedSection: null, step: "input" };
|
|
20709
|
-
}
|
|
20710
|
-
const { pageRoute, sectionId } = parseTarget(target);
|
|
20711
|
-
if (sectionId) {
|
|
20712
|
-
if (isInternalPath(pageRoute)) {
|
|
20713
|
-
const page = resolvePage(pageRoute, pages);
|
|
20714
|
-
const sec = sections.find((s) => s.id === sectionId);
|
|
20715
|
-
return {
|
|
20716
|
-
searchValue: page.title,
|
|
20717
|
-
selectedPage: page,
|
|
20718
|
-
selectedSection: sec ? { id: sec.id, label: sec.label } : null,
|
|
20719
|
-
step: "input"
|
|
20720
|
-
};
|
|
20721
|
-
}
|
|
20722
|
-
return { searchValue: pageRoute || target, selectedPage: null, selectedSection: null, step: "input" };
|
|
20723
|
-
}
|
|
20724
|
-
if (isInternalPath(pageRoute)) {
|
|
20725
|
-
const page = resolvePage(pageRoute, pages);
|
|
20726
|
-
return {
|
|
20727
|
-
searchValue: page.title,
|
|
20728
|
-
selectedPage: page,
|
|
20729
|
-
selectedSection: null,
|
|
20730
|
-
step: "input"
|
|
20731
|
-
};
|
|
21037
|
+
function findNavbarCtaAtPoint(x, y) {
|
|
21038
|
+
const hits = Array.from(
|
|
21039
|
+
document.querySelectorAll('[data-ohw-role="navbar-button"]')
|
|
21040
|
+
).filter((el) => containsPoint(el, x, y) && !el.closest("[data-ohw-bridge]"));
|
|
21041
|
+
if (hits.length === 0) return null;
|
|
21042
|
+
return hits.reduce((best, el) => best.contains(el) ? el : best);
|
|
21043
|
+
}
|
|
21044
|
+
|
|
21045
|
+
// src/lib/session-search.ts
|
|
21046
|
+
var STORAGE_KEY = "ohw-preserved-search";
|
|
21047
|
+
function readPreservedSearch() {
|
|
21048
|
+
if (typeof window === "undefined") return "";
|
|
21049
|
+
const fromUrl = window.location.search;
|
|
21050
|
+
if (fromUrl && fromUrl !== "?") {
|
|
21051
|
+
sessionStorage.setItem(STORAGE_KEY, fromUrl);
|
|
21052
|
+
return fromUrl;
|
|
20732
21053
|
}
|
|
20733
|
-
return
|
|
21054
|
+
return sessionStorage.getItem(STORAGE_KEY) ?? "";
|
|
20734
21055
|
}
|
|
20735
|
-
function
|
|
20736
|
-
const
|
|
20737
|
-
|
|
21056
|
+
function isEditSessionActive() {
|
|
21057
|
+
const search = readPreservedSearch();
|
|
21058
|
+
if (!search || search === "?") return false;
|
|
21059
|
+
const q = search.startsWith("?") ? search.slice(1) : search;
|
|
21060
|
+
return new URLSearchParams(q).get("mode") === "edit";
|
|
20738
21061
|
}
|
|
20739
21062
|
|
|
20740
21063
|
// src/lib/prefetch-sections.ts
|
|
@@ -20836,113 +21159,6 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
|
|
|
20836
21159
|
// src/ui/link-modal/LinkPopover.tsx
|
|
20837
21160
|
var import_react17 = require("react");
|
|
20838
21161
|
|
|
20839
|
-
// src/ui/dialog.tsx
|
|
20840
|
-
var React9 = __toESM(require("react"), 1);
|
|
20841
|
-
var import_radix_ui6 = require("radix-ui");
|
|
20842
|
-
var import_lucide_react12 = require("lucide-react");
|
|
20843
|
-
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
20844
|
-
function Dialog2({
|
|
20845
|
-
...props
|
|
20846
|
-
}) {
|
|
20847
|
-
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
|
|
20848
|
-
}
|
|
20849
|
-
function DialogPortal({
|
|
20850
|
-
...props
|
|
20851
|
-
}) {
|
|
20852
|
-
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
|
|
20853
|
-
}
|
|
20854
|
-
function DialogOverlay({
|
|
20855
|
-
className,
|
|
20856
|
-
...props
|
|
20857
|
-
}) {
|
|
20858
|
-
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20859
|
-
import_radix_ui6.Dialog.Overlay,
|
|
20860
|
-
{
|
|
20861
|
-
"data-slot": "dialog-overlay",
|
|
20862
|
-
"data-ohw-link-modal-root": "",
|
|
20863
|
-
className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
|
|
20864
|
-
...props
|
|
20865
|
-
}
|
|
20866
|
-
);
|
|
20867
|
-
}
|
|
20868
|
-
var DialogContent = React9.forwardRef(
|
|
20869
|
-
({ className, children, showCloseButton = true, container, ...props }, ref) => {
|
|
20870
|
-
const positionMode = container ? "absolute" : "fixed";
|
|
20871
|
-
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DialogPortal, { container: container ?? void 0, children: [
|
|
20872
|
-
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
|
|
20873
|
-
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
20874
|
-
import_radix_ui6.Dialog.Content,
|
|
20875
|
-
{
|
|
20876
|
-
ref,
|
|
20877
|
-
"data-slot": "dialog-content",
|
|
20878
|
-
className: cn(
|
|
20879
|
-
positionMode,
|
|
20880
|
-
"left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
|
|
20881
|
-
"rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
|
|
20882
|
-
container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
|
|
20883
|
-
className
|
|
20884
|
-
),
|
|
20885
|
-
onMouseDown: (e) => e.stopPropagation(),
|
|
20886
|
-
...props,
|
|
20887
|
-
children: [
|
|
20888
|
-
children,
|
|
20889
|
-
showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20890
|
-
import_radix_ui6.Dialog.Close,
|
|
20891
|
-
{
|
|
20892
|
-
type: "button",
|
|
20893
|
-
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
20894
|
-
"aria-label": "Close",
|
|
20895
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.X, { size: 16, "aria-hidden": true })
|
|
20896
|
-
}
|
|
20897
|
-
) : null
|
|
20898
|
-
]
|
|
20899
|
-
}
|
|
20900
|
-
)
|
|
20901
|
-
] });
|
|
20902
|
-
}
|
|
20903
|
-
);
|
|
20904
|
-
DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
|
|
20905
|
-
function DialogHeader({
|
|
20906
|
-
className,
|
|
20907
|
-
...props
|
|
20908
|
-
}) {
|
|
20909
|
-
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
|
|
20910
|
-
}
|
|
20911
|
-
function DialogFooter({
|
|
20912
|
-
className,
|
|
20913
|
-
...props
|
|
20914
|
-
}) {
|
|
20915
|
-
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20916
|
-
"div",
|
|
20917
|
-
{
|
|
20918
|
-
className: cn("flex items-center justify-end gap-2", className),
|
|
20919
|
-
...props
|
|
20920
|
-
}
|
|
20921
|
-
);
|
|
20922
|
-
}
|
|
20923
|
-
var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20924
|
-
import_radix_ui6.Dialog.Title,
|
|
20925
|
-
{
|
|
20926
|
-
ref,
|
|
20927
|
-
className: cn(
|
|
20928
|
-
"text-lg font-semibold leading-none tracking-tight text-card-foreground",
|
|
20929
|
-
className
|
|
20930
|
-
),
|
|
20931
|
-
...props
|
|
20932
|
-
}
|
|
20933
|
-
));
|
|
20934
|
-
DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
|
|
20935
|
-
var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20936
|
-
import_radix_ui6.Dialog.Description,
|
|
20937
|
-
{
|
|
20938
|
-
ref,
|
|
20939
|
-
className: cn("text-sm text-muted-foreground", className),
|
|
20940
|
-
...props
|
|
20941
|
-
}
|
|
20942
|
-
));
|
|
20943
|
-
DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
|
|
20944
|
-
var DialogClose = import_radix_ui6.Dialog.Close;
|
|
20945
|
-
|
|
20946
21162
|
// src/ui/link-modal/LinkEditorPanel.tsx
|
|
20947
21163
|
var import_lucide_react16 = require("lucide-react");
|
|
20948
21164
|
|
|
@@ -22046,6 +22262,7 @@ function shouldUseDevFixtures() {
|
|
|
22046
22262
|
function getLogoElement(el) {
|
|
22047
22263
|
const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
|
|
22048
22264
|
if (marked) return marked;
|
|
22265
|
+
if (el.closest('[data-ohw-editable="icon"]')) return null;
|
|
22049
22266
|
const root = el.closest("nav, [data-ohw-nav-root], footer");
|
|
22050
22267
|
if (!root) return null;
|
|
22051
22268
|
const anchor = el.closest("a");
|
|
@@ -24626,7 +24843,7 @@ function getNavigationSelectionParent(el) {
|
|
|
24626
24843
|
if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
|
|
24627
24844
|
return resolveFooterLinksContainerFrom(el);
|
|
24628
24845
|
}
|
|
24629
|
-
if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
|
|
24846
|
+
if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isFooterLinksContainer(el) || isInferredFooterGroup2(el)) {
|
|
24630
24847
|
return getNavigationRoot(el);
|
|
24631
24848
|
}
|
|
24632
24849
|
return null;
|
|
@@ -24848,7 +25065,6 @@ var ICONS = {
|
|
|
24848
25065
|
insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
|
|
24849
25066
|
insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
|
|
24850
25067
|
};
|
|
24851
|
-
var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
|
|
24852
25068
|
var SELECTION_CHROME_GAP2 = 4;
|
|
24853
25069
|
var TOOLBAR_STROKE_GAP2 = 4;
|
|
24854
25070
|
var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
|
|
@@ -24946,7 +25162,7 @@ function applyVisibleViewport(el, parentScroll) {
|
|
|
24946
25162
|
el.style.top = `${top}px`;
|
|
24947
25163
|
el.style.height = `${height}px`;
|
|
24948
25164
|
}
|
|
24949
|
-
var
|
|
25165
|
+
var TOOLBAR_EDGE_MARGIN4 = 4;
|
|
24950
25166
|
function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
|
|
24951
25167
|
const isAbove = transform.includes("translateY(-100%)");
|
|
24952
25168
|
let visualTop = isAbove ? top - approxH : top;
|
|
@@ -24973,8 +25189,8 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
|
|
|
24973
25189
|
function clampToolbarLeft(centerX, toolbarWidth) {
|
|
24974
25190
|
const half = toolbarWidth / 2;
|
|
24975
25191
|
return Math.max(
|
|
24976
|
-
|
|
24977
|
-
Math.min(centerX, window.innerWidth -
|
|
25192
|
+
TOOLBAR_EDGE_MARGIN4 + half,
|
|
25193
|
+
Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN4 - half)
|
|
24978
25194
|
);
|
|
24979
25195
|
}
|
|
24980
25196
|
function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
|
|
@@ -25405,6 +25621,34 @@ function OhhwellsBridge() {
|
|
|
25405
25621
|
const [fetchState, setFetchState] = (0, import_react21.useState)("idle");
|
|
25406
25622
|
const [showBranding, setShowBranding] = (0, import_react21.useState)(false);
|
|
25407
25623
|
const autoSaveTimers = (0, import_react21.useRef)(/* @__PURE__ */ new Map());
|
|
25624
|
+
(0, import_react21.useEffect)(() => {
|
|
25625
|
+
stampBridgeVersionOnDocument();
|
|
25626
|
+
}, []);
|
|
25627
|
+
(0, import_react21.useEffect)(() => {
|
|
25628
|
+
if (isEditMode) return;
|
|
25629
|
+
return armLoaderHangBeacon(subdomain, fetchState);
|
|
25630
|
+
}, [isEditMode, subdomain, fetchState]);
|
|
25631
|
+
(0, import_react21.useEffect)(() => {
|
|
25632
|
+
if (isEditMode || !subdomain) return;
|
|
25633
|
+
const onError = (event) => {
|
|
25634
|
+
reportOutageBeacon({
|
|
25635
|
+
subdomain,
|
|
25636
|
+
reason: "page_error",
|
|
25637
|
+
detail: event.message || "window error"
|
|
25638
|
+
});
|
|
25639
|
+
};
|
|
25640
|
+
const onRejection = (event) => {
|
|
25641
|
+
const reason = event.reason;
|
|
25642
|
+
const detail = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "unhandledrejection";
|
|
25643
|
+
reportOutageBeacon({ subdomain, reason: "page_error", detail });
|
|
25644
|
+
};
|
|
25645
|
+
window.addEventListener("error", onError);
|
|
25646
|
+
window.addEventListener("unhandledrejection", onRejection);
|
|
25647
|
+
return () => {
|
|
25648
|
+
window.removeEventListener("error", onError);
|
|
25649
|
+
window.removeEventListener("unhandledrejection", onRejection);
|
|
25650
|
+
};
|
|
25651
|
+
}, [isEditMode, subdomain]);
|
|
25408
25652
|
const activeElRef = (0, import_react21.useRef)(null);
|
|
25409
25653
|
const pointerHeldRef = (0, import_react21.useRef)(false);
|
|
25410
25654
|
const selectedElRef = (0, import_react21.useRef)(null);
|
|
@@ -25826,21 +26070,6 @@ function OhhwellsBridge() {
|
|
|
25826
26070
|
const [isItemDragging, setIsItemDragging] = (0, import_react21.useState)(false);
|
|
25827
26071
|
const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react21.useState)(false);
|
|
25828
26072
|
isFooterFrameSelectionRef.current = isFooterFrameSelection;
|
|
25829
|
-
const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
|
|
25830
|
-
const floatingPanelOpenRef = (0, import_react21.useRef)(false);
|
|
25831
|
-
floatingPanelOpenRef.current = floatingPanel !== null;
|
|
25832
|
-
const floatingPanelRef = (0, import_react21.useRef)(null);
|
|
25833
|
-
floatingPanelRef.current = floatingPanel;
|
|
25834
|
-
const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
|
|
25835
|
-
const buttonCollectionDragRef = (0, import_react21.useRef)(null);
|
|
25836
|
-
const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
|
|
25837
|
-
const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
|
|
25838
|
-
const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
|
|
25839
|
-
const buttonPointerDragRef = (0, import_react21.useRef)(null);
|
|
25840
|
-
const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
|
|
25841
|
-
const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
|
|
25842
|
-
const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
|
|
25843
|
-
const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
|
|
25844
26073
|
const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react21.useState)(null);
|
|
25845
26074
|
const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react21.useState)(null);
|
|
25846
26075
|
const footerDragRef = (0, import_react21.useRef)(null);
|
|
@@ -25866,7 +26095,23 @@ function OhhwellsBridge() {
|
|
|
25866
26095
|
const stylesRef = (0, import_react21.useRef)("");
|
|
25867
26096
|
const appliedDrawerRef = (0, import_react21.useRef)(null);
|
|
25868
26097
|
const pendingDeleteUndoRef = (0, import_react21.useRef)(null);
|
|
26098
|
+
const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
|
|
26099
|
+
const floatingPanelOpenRef = (0, import_react21.useRef)(false);
|
|
26100
|
+
const floatingPanelRef = (0, import_react21.useRef)(null);
|
|
26101
|
+
floatingPanelRef.current = floatingPanel;
|
|
26102
|
+
const setFloatingPanelRef = (0, import_react21.useRef)(setFloatingPanel);
|
|
26103
|
+
const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
|
|
26104
|
+
const buttonCollectionDragRef = (0, import_react21.useRef)(null);
|
|
26105
|
+
const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
|
|
26106
|
+
const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
|
|
26107
|
+
const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
|
|
26108
|
+
const buttonPointerDragRef = (0, import_react21.useRef)(null);
|
|
26109
|
+
const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
|
|
26110
|
+
const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
|
|
26111
|
+
const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
|
|
26112
|
+
const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
|
|
25869
26113
|
const [sitePages, setSitePages] = (0, import_react21.useState)([]);
|
|
26114
|
+
const collectionItemsRef = (0, import_react21.useRef)(/* @__PURE__ */ new Map());
|
|
25870
26115
|
const [sectionsByPath, setSectionsByPath] = (0, import_react21.useState)({});
|
|
25871
26116
|
const sectionsPrefetchGenRef = (0, import_react21.useRef)(0);
|
|
25872
26117
|
const setLinkPopoverRef = (0, import_react21.useRef)(setLinkPopover);
|
|
@@ -25874,7 +26119,18 @@ function OhhwellsBridge() {
|
|
|
25874
26119
|
const linkPopoverOpenRef = (0, import_react21.useRef)(false);
|
|
25875
26120
|
const linkPopoverGraceUntilRef = (0, import_react21.useRef)(0);
|
|
25876
26121
|
setLinkPopoverRef.current = setLinkPopover;
|
|
26122
|
+
setFloatingPanelRef.current = setFloatingPanel;
|
|
25877
26123
|
linkPopoverSessionRef.current = linkPopover;
|
|
26124
|
+
floatingPanelOpenRef.current = Boolean(floatingPanel);
|
|
26125
|
+
(0, import_react21.useEffect)(() => {
|
|
26126
|
+
const syncViewport = () => {
|
|
26127
|
+
const next = window.innerWidth <= 480 ? "mobile" : "desktop";
|
|
26128
|
+
setEditorViewport((prev) => prev === next ? prev : next);
|
|
26129
|
+
};
|
|
26130
|
+
syncViewport();
|
|
26131
|
+
window.addEventListener("resize", syncViewport);
|
|
26132
|
+
return () => window.removeEventListener("resize", syncViewport);
|
|
26133
|
+
}, []);
|
|
25878
26134
|
(0, import_react21.useEffect)(() => {
|
|
25879
26135
|
setSectionOrderPersistHandler((entries) => {
|
|
25880
26136
|
const currentPath = window.location.pathname;
|
|
@@ -26010,10 +26266,12 @@ function OhhwellsBridge() {
|
|
|
26010
26266
|
const onSitePages = (e) => {
|
|
26011
26267
|
if (e.data?.type !== "ow:site-pages" || !Array.isArray(e.data.pages)) return;
|
|
26012
26268
|
if (useFixtures) return;
|
|
26013
|
-
const
|
|
26269
|
+
const incoming = e.data.pages;
|
|
26270
|
+
const mapped = incoming.map((p) => ({
|
|
26014
26271
|
path: p.path,
|
|
26015
26272
|
title: p.title
|
|
26016
26273
|
}));
|
|
26274
|
+
collectionItemsRef.current = indexCollectionItems(incoming);
|
|
26017
26275
|
setSitePages(mapped);
|
|
26018
26276
|
runSectionsPrefetchRef.current(mapped);
|
|
26019
26277
|
};
|
|
@@ -27569,6 +27827,7 @@ function OhhwellsBridge() {
|
|
|
27569
27827
|
applyAiFooterFromContent(content);
|
|
27570
27828
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
27571
27829
|
stylesRef.current = content[STYLE_STORE_KEY];
|
|
27830
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
27572
27831
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
27573
27832
|
}
|
|
27574
27833
|
applyBrandChrome(content);
|
|
@@ -27579,12 +27838,12 @@ function OhhwellsBridge() {
|
|
|
27579
27838
|
if (key === "__ohw_sections") continue;
|
|
27580
27839
|
if (key === AI_SECTIONS_KEY) continue;
|
|
27581
27840
|
if (key === AI_FOOTER_KEY) continue;
|
|
27841
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
27842
|
+
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
27582
27843
|
if (key === BRAND_KIT_KEY) continue;
|
|
27583
27844
|
if (key === STYLE_STORE_KEY) continue;
|
|
27584
27845
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
27585
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
27586
27846
|
if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
|
|
27587
|
-
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
27588
27847
|
if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
|
|
27589
27848
|
continue;
|
|
27590
27849
|
}
|
|
@@ -27661,7 +27920,17 @@ function OhhwellsBridge() {
|
|
|
27661
27920
|
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
27662
27921
|
const initialPath = pathname;
|
|
27663
27922
|
fetchedContentPaths.add(`${subdomain}::${initialPath}`);
|
|
27664
|
-
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) =>
|
|
27923
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => {
|
|
27924
|
+
if (!r2.ok) {
|
|
27925
|
+
reportOutageBeacon({
|
|
27926
|
+
subdomain,
|
|
27927
|
+
reason: "content_fetch_failed",
|
|
27928
|
+
detail: `HTTP ${r2.status}`
|
|
27929
|
+
});
|
|
27930
|
+
return null;
|
|
27931
|
+
}
|
|
27932
|
+
return r2.json();
|
|
27933
|
+
}).then((data) => {
|
|
27665
27934
|
if (cancelled) return;
|
|
27666
27935
|
const content = data?.content ?? {};
|
|
27667
27936
|
const branding = Boolean(data?.showBranding);
|
|
@@ -27670,7 +27939,12 @@ function OhhwellsBridge() {
|
|
|
27670
27939
|
recordOverriddenPaths(subdomain, data?.overriddenPaths);
|
|
27671
27940
|
setShowBranding(branding);
|
|
27672
27941
|
return applyContent(content);
|
|
27673
|
-
}).catch(() => {
|
|
27942
|
+
}).catch((err) => {
|
|
27943
|
+
reportOutageBeacon({
|
|
27944
|
+
subdomain,
|
|
27945
|
+
reason: "content_fetch_failed",
|
|
27946
|
+
detail: err instanceof Error ? err.message : "fetch failed"
|
|
27947
|
+
});
|
|
27674
27948
|
}).finally(() => {
|
|
27675
27949
|
if (!cancelled) setFetchState("done");
|
|
27676
27950
|
});
|
|
@@ -27800,17 +28074,18 @@ function OhhwellsBridge() {
|
|
|
27800
28074
|
}
|
|
27801
28075
|
applyAiFooterFromContent(content);
|
|
27802
28076
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
28077
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
27803
28078
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
27804
28079
|
}
|
|
27805
28080
|
for (const [key, val] of Object.entries(content)) {
|
|
27806
28081
|
if (key === "__ohw_sections") continue;
|
|
27807
28082
|
if (key === AI_SECTIONS_KEY) continue;
|
|
27808
28083
|
if (key === AI_FOOTER_KEY) continue;
|
|
28084
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
28085
|
+
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
27809
28086
|
if (key === BRAND_KIT_KEY) continue;
|
|
27810
28087
|
if (key === STYLE_STORE_KEY) continue;
|
|
27811
28088
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
27812
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
27813
|
-
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
27814
28089
|
if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
|
|
27815
28090
|
continue;
|
|
27816
28091
|
}
|
|
@@ -28077,26 +28352,11 @@ function OhhwellsBridge() {
|
|
|
28077
28352
|
const t2 = setTimeout(measure, 500);
|
|
28078
28353
|
const ro = new ResizeObserver(schedule);
|
|
28079
28354
|
ro.observe(document.body);
|
|
28080
|
-
let lastWidth = window.innerWidth;
|
|
28081
|
-
let resizeTimers = [];
|
|
28082
|
-
const clearResizeTimers = () => {
|
|
28083
|
-
resizeTimers.forEach(clearTimeout);
|
|
28084
|
-
resizeTimers = [];
|
|
28085
|
-
};
|
|
28086
|
-
const handleResize = () => {
|
|
28087
|
-
if (window.innerWidth === lastWidth) return;
|
|
28088
|
-
lastWidth = window.innerWidth;
|
|
28089
|
-
clearResizeTimers();
|
|
28090
|
-
resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
|
|
28091
|
-
};
|
|
28092
|
-
window.addEventListener("resize", handleResize);
|
|
28093
28355
|
return () => {
|
|
28094
28356
|
clearTimeout(t1);
|
|
28095
28357
|
clearTimeout(t2);
|
|
28096
28358
|
if (raf != null) cancelAnimationFrame(raf);
|
|
28097
28359
|
ro.disconnect();
|
|
28098
|
-
clearResizeTimers();
|
|
28099
|
-
window.removeEventListener("resize", handleResize);
|
|
28100
28360
|
};
|
|
28101
28361
|
}, [pathname, isEditMode, postToParent2]);
|
|
28102
28362
|
(0, import_react21.useEffect)(() => {
|
|
@@ -28393,6 +28653,13 @@ function OhhwellsBridge() {
|
|
|
28393
28653
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
28394
28654
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
28395
28655
|
if (isInsideLinkEditor(target)) return;
|
|
28656
|
+
if (isInsideFloatingPanel(target)) return;
|
|
28657
|
+
if (target.closest("[data-ohw-form-toolbar]")) return;
|
|
28658
|
+
if (target.closest(
|
|
28659
|
+
'[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
|
|
28660
|
+
)) {
|
|
28661
|
+
return;
|
|
28662
|
+
}
|
|
28396
28663
|
if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
|
|
28397
28664
|
clearMediaSelectionRef.current();
|
|
28398
28665
|
}
|
|
@@ -28416,11 +28683,15 @@ function OhhwellsBridge() {
|
|
|
28416
28683
|
return;
|
|
28417
28684
|
}
|
|
28418
28685
|
}
|
|
28419
|
-
|
|
28420
|
-
if (
|
|
28421
|
-
|
|
28422
|
-
|
|
28423
|
-
|
|
28686
|
+
const collectionItem = matchCollectionItemClick(target, collectionItemsRef.current);
|
|
28687
|
+
if (collectionItem) {
|
|
28688
|
+
e.preventDefault();
|
|
28689
|
+
e.stopPropagation();
|
|
28690
|
+
postToParentRef.current({
|
|
28691
|
+
type: "ow:collection-item-selected",
|
|
28692
|
+
path: collectionItem.path,
|
|
28693
|
+
collection: collectionItem.collection
|
|
28694
|
+
});
|
|
28424
28695
|
return;
|
|
28425
28696
|
}
|
|
28426
28697
|
{
|
|
@@ -28576,9 +28847,13 @@ function OhhwellsBridge() {
|
|
|
28576
28847
|
if (isIconEditable(editable) && !getSocialItem(editable)) {
|
|
28577
28848
|
e.preventDefault();
|
|
28578
28849
|
e.stopPropagation();
|
|
28579
|
-
selectIconElementRef.current(editable);
|
|
28580
28850
|
const aiIcon = editable.hasAttribute("data-ai-icon");
|
|
28581
28851
|
const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
|
|
28852
|
+
if (aiIcon) {
|
|
28853
|
+
aiSectionApiRef.current?.selectFromElement(editable);
|
|
28854
|
+
} else {
|
|
28855
|
+
selectIconElementRef.current(editable);
|
|
28856
|
+
}
|
|
28582
28857
|
postToParentRef.current({
|
|
28583
28858
|
type: "ow:icon-pick",
|
|
28584
28859
|
key: editable.dataset.ohwKey ?? "",
|
|
@@ -28806,6 +29081,9 @@ function OhhwellsBridge() {
|
|
|
28806
29081
|
setHoveredItemRect(null);
|
|
28807
29082
|
hoveredNavContainerRef.current = null;
|
|
28808
29083
|
setHoveredNavContainerRect(null);
|
|
29084
|
+
siblingHintElRef.current = null;
|
|
29085
|
+
setSiblingHintRect(null);
|
|
29086
|
+
setSiblingHintRects([]);
|
|
28809
29087
|
return;
|
|
28810
29088
|
}
|
|
28811
29089
|
{
|
|
@@ -28926,7 +29204,6 @@ function OhhwellsBridge() {
|
|
|
28926
29204
|
hoveredNavContainerRef.current = null;
|
|
28927
29205
|
setHoveredNavContainerRect(null);
|
|
28928
29206
|
hoveredItemElRef.current = editable;
|
|
28929
|
-
setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
|
|
28930
29207
|
}
|
|
28931
29208
|
}
|
|
28932
29209
|
}
|
|
@@ -29636,7 +29913,11 @@ function OhhwellsBridge() {
|
|
|
29636
29913
|
};
|
|
29637
29914
|
const handleMouseMove = (e) => {
|
|
29638
29915
|
const { clientX, clientY } = e;
|
|
29639
|
-
if (pointOwnedByFloatingPanel(clientX, clientY))
|
|
29916
|
+
if (pointOwnedByFloatingPanel(clientX, clientY)) {
|
|
29917
|
+
dismissImageHover();
|
|
29918
|
+
clearImageHover();
|
|
29919
|
+
return;
|
|
29920
|
+
}
|
|
29640
29921
|
if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
|
|
29641
29922
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
29642
29923
|
formHoverElRef.current = null;
|
|
@@ -29665,7 +29946,12 @@ function OhhwellsBridge() {
|
|
|
29665
29946
|
setHoveredItemRect(null);
|
|
29666
29947
|
hoveredNavContainerRef.current = null;
|
|
29667
29948
|
setHoveredNavContainerRect(null);
|
|
29949
|
+
siblingHintElRef.current = null;
|
|
29950
|
+
setSiblingHintRect(null);
|
|
29951
|
+
setSiblingHintRects([]);
|
|
29668
29952
|
dismissImageHover();
|
|
29953
|
+
clearImageHover();
|
|
29954
|
+
setSectionGap(null);
|
|
29669
29955
|
return;
|
|
29670
29956
|
}
|
|
29671
29957
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
@@ -29677,7 +29963,11 @@ function OhhwellsBridge() {
|
|
|
29677
29963
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
29678
29964
|
const { clientX, clientY } = e.data;
|
|
29679
29965
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
29680
|
-
if (pointOwnedByFloatingPanel(clientX, clientY))
|
|
29966
|
+
if (pointOwnedByFloatingPanel(clientX, clientY)) {
|
|
29967
|
+
dismissImageHover();
|
|
29968
|
+
clearImageHover();
|
|
29969
|
+
return;
|
|
29970
|
+
}
|
|
29681
29971
|
if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
|
|
29682
29972
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
29683
29973
|
probeSectionGapAt(clientX, clientY);
|
|
@@ -30057,6 +30347,7 @@ function OhhwellsBridge() {
|
|
|
30057
30347
|
applyAiFooterFromContent(content);
|
|
30058
30348
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
30059
30349
|
stylesRef.current = content[STYLE_STORE_KEY];
|
|
30350
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
30060
30351
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
30061
30352
|
}
|
|
30062
30353
|
applyBrandChrome(content);
|
|
@@ -30071,12 +30362,12 @@ function OhhwellsBridge() {
|
|
|
30071
30362
|
}
|
|
30072
30363
|
if (key === AI_SECTIONS_KEY) continue;
|
|
30073
30364
|
if (key === AI_FOOTER_KEY) continue;
|
|
30365
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
30366
|
+
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
30074
30367
|
if (key === BRAND_KIT_KEY) continue;
|
|
30075
30368
|
if (key === STYLE_STORE_KEY) continue;
|
|
30076
30369
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
30077
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
30078
30370
|
if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
|
|
30079
|
-
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
30080
30371
|
if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
|
|
30081
30372
|
continue;
|
|
30082
30373
|
}
|
|
@@ -30712,12 +31003,24 @@ function OhhwellsBridge() {
|
|
|
30712
31003
|
closeLinkPopoverRef.current();
|
|
30713
31004
|
return;
|
|
30714
31005
|
}
|
|
31006
|
+
if (floatingPanelOpenRef.current) {
|
|
31007
|
+
setFloatingPanelRef.current(null);
|
|
31008
|
+
deselectRef.current();
|
|
31009
|
+
deactivateRef.current();
|
|
31010
|
+
return;
|
|
31011
|
+
}
|
|
30715
31012
|
deselectRef.current();
|
|
30716
31013
|
deactivateRef.current();
|
|
30717
31014
|
clearMediaSelectionRef.current();
|
|
30718
31015
|
clearIconSelectionRef.current();
|
|
30719
31016
|
};
|
|
30720
31017
|
window.addEventListener("message", handleDeactivate);
|
|
31018
|
+
const handleSelectNav = (e) => {
|
|
31019
|
+
if (e.data?.type !== "ow:select-nav") return;
|
|
31020
|
+
const navContainer = findSelectableNavContainer();
|
|
31021
|
+
if (navContainer) selectFrameRef.current(navContainer);
|
|
31022
|
+
};
|
|
31023
|
+
window.addEventListener("message", handleSelectNav);
|
|
30721
31024
|
const handleToastAction = (e) => {
|
|
30722
31025
|
if (e.data?.type !== "ow:toast-action") return;
|
|
30723
31026
|
const actionId = typeof e.data.actionId === "string" ? e.data.actionId : null;
|
|
@@ -31534,10 +31837,11 @@ function OhhwellsBridge() {
|
|
|
31534
31837
|
window.removeEventListener("message", handleDeleteSection);
|
|
31535
31838
|
window.removeEventListener("message", handleDuplicateSection);
|
|
31536
31839
|
window.removeEventListener("message", handleDeactivate);
|
|
31537
|
-
|
|
31840
|
+
window.removeEventListener("message", handleSelectNav);
|
|
31538
31841
|
window.removeEventListener("message", handleToastAction);
|
|
31539
31842
|
window.removeEventListener("message", handleFormCount);
|
|
31540
31843
|
window.removeEventListener("message", handleUiEscape);
|
|
31844
|
+
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
31541
31845
|
autoSaveTimers.current.forEach(clearTimeout);
|
|
31542
31846
|
autoSaveTimers.current.clear();
|
|
31543
31847
|
if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
|
|
@@ -31831,7 +32135,7 @@ function OhhwellsBridge() {
|
|
|
31831
32135
|
postToParent2({
|
|
31832
32136
|
type: "ow:ready",
|
|
31833
32137
|
version: "1",
|
|
31834
|
-
bridgeVersion: "0.1.
|
|
32138
|
+
bridgeVersion: "0.1.153-next.417",
|
|
31835
32139
|
path: pathname,
|
|
31836
32140
|
nodes: collectEditableNodes(editContentRef.current),
|
|
31837
32141
|
sections
|
|
@@ -33057,6 +33361,7 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
|
|
|
33057
33361
|
foldAlignIntoTrees,
|
|
33058
33362
|
getEditModeInitialState,
|
|
33059
33363
|
hasMountedContentNodes,
|
|
33364
|
+
indexCollectionItems,
|
|
33060
33365
|
insertFooterHours,
|
|
33061
33366
|
isEditSessionActive,
|
|
33062
33367
|
isPlainLeftClick,
|
|
@@ -33064,6 +33369,7 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
|
|
|
33064
33369
|
isRenderableTree,
|
|
33065
33370
|
isStickySearchParam,
|
|
33066
33371
|
isValidUrl,
|
|
33372
|
+
matchCollectionItemClick,
|
|
33067
33373
|
nextViableSlide,
|
|
33068
33374
|
parseAiFooterState,
|
|
33069
33375
|
parseAiSectionsState,
|