@ohhwells/bridge 0.1.152 → 0.1.153-next.415
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 +1186 -884
- 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 +1249 -933
- 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,103 @@ 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.415";
|
|
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.415" : "";
|
|
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 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;
|
|
17283
|
+
function isVercelPreviewHost() {
|
|
17284
|
+
if (typeof location === "undefined") {
|
|
17285
|
+
return false;
|
|
17286
|
+
}
|
|
17287
|
+
return location.hostname.toLowerCase().endsWith(".vercel.ohhwells.site");
|
|
17288
|
+
}
|
|
17289
|
+
function reportOutageBeacon(args) {
|
|
17290
|
+
if (typeof window === "undefined" || !args.subdomain) {
|
|
17291
|
+
return;
|
|
17292
|
+
}
|
|
17293
|
+
if (isVercelPreviewHost()) {
|
|
17294
|
+
return;
|
|
17295
|
+
}
|
|
17296
|
+
if (args.reason === "page_error" && args.detail && RECOVERABLE_PAGE_ERROR.test(args.detail)) {
|
|
17297
|
+
return;
|
|
17298
|
+
}
|
|
17299
|
+
const key = `${args.subdomain}|${args.reason}`;
|
|
17300
|
+
const now = Date.now();
|
|
17301
|
+
const prev = lastSent.get(key) || 0;
|
|
17302
|
+
if (now - prev < DEDUP_MS) {
|
|
17303
|
+
return;
|
|
17304
|
+
}
|
|
17305
|
+
lastSent.set(key, now);
|
|
17306
|
+
const body = JSON.stringify({
|
|
17307
|
+
subdomain: args.subdomain,
|
|
17308
|
+
reason: args.reason,
|
|
17309
|
+
detail: args.detail?.slice(0, 300),
|
|
17310
|
+
templateId: args.templateId,
|
|
17311
|
+
bridgeVersion: bridgeVersion(),
|
|
17312
|
+
href: typeof location !== "undefined" ? location.href : void 0
|
|
17313
|
+
});
|
|
17314
|
+
const url = `${apiBase()}/api/public/sites/outage-beacon`;
|
|
17315
|
+
try {
|
|
17316
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
17317
|
+
const blob = new Blob([body], { type: "application/json" });
|
|
17318
|
+
if (navigator.sendBeacon(url, blob)) {
|
|
17319
|
+
return;
|
|
17320
|
+
}
|
|
17321
|
+
}
|
|
17322
|
+
void fetch(url, {
|
|
17323
|
+
method: "POST",
|
|
17324
|
+
headers: { "Content-Type": "application/json" },
|
|
17325
|
+
body,
|
|
17326
|
+
keepalive: true,
|
|
17327
|
+
cache: "no-store"
|
|
17328
|
+
}).catch(() => {
|
|
17329
|
+
});
|
|
17330
|
+
} catch {
|
|
17331
|
+
}
|
|
17332
|
+
}
|
|
17333
|
+
function armLoaderHangBeacon(subdomain, fetchState, hangMs = 15e3) {
|
|
17334
|
+
if (!subdomain || fetchState !== "loading") {
|
|
17335
|
+
return () => {
|
|
17336
|
+
};
|
|
17337
|
+
}
|
|
17338
|
+
const timer = window.setTimeout(() => {
|
|
17339
|
+
reportOutageBeacon({
|
|
17340
|
+
subdomain,
|
|
17341
|
+
reason: "loader_stuck",
|
|
17342
|
+
detail: `fetchState still loading after ${hangMs}ms`
|
|
17343
|
+
});
|
|
17344
|
+
}, hangMs);
|
|
17345
|
+
return () => window.clearTimeout(timer);
|
|
17346
|
+
}
|
|
17347
|
+
|
|
17023
17348
|
// src/lib/form-fields.ts
|
|
17024
17349
|
var FIELD_DEFAULTS = {
|
|
17025
17350
|
"short-text": { label: "Short text", placeholder: "Type placeholder text..." },
|
|
@@ -17620,6 +17945,7 @@ function MediaOverlay({
|
|
|
17620
17945
|
(prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
|
|
17621
17946
|
);
|
|
17622
17947
|
}, [isVideo]);
|
|
17948
|
+
const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
|
|
17623
17949
|
const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
|
|
17624
17950
|
const box = {
|
|
17625
17951
|
position: "fixed",
|
|
@@ -17718,11 +18044,10 @@ function MediaOverlay({
|
|
|
17718
18044
|
className: "flex items-center justify-center cursor-pointer",
|
|
17719
18045
|
style: {
|
|
17720
18046
|
...box,
|
|
17721
|
-
//
|
|
17722
|
-
//
|
|
17723
|
-
//
|
|
17724
|
-
|
|
17725
|
-
pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
|
|
18047
|
+
// Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
|
|
18048
|
+
// (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
|
|
18049
|
+
// and cueOnly also need clicks to fall through — Replace / video controls opt back in.
|
|
18050
|
+
pointerEvents: "none",
|
|
17726
18051
|
// Selected: a firm component ring with no wash, so the image reads as chosen rather
|
|
17727
18052
|
// than hovered. Hover keeps the existing tinted preview.
|
|
17728
18053
|
boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
|
|
@@ -17749,17 +18074,17 @@ function MediaOverlay({
|
|
|
17749
18074
|
},
|
|
17750
18075
|
children: [
|
|
17751
18076
|
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
|
-
|
|
18077
|
+
replaceLabel
|
|
17753
18078
|
]
|
|
17754
18079
|
}
|
|
17755
18080
|
),
|
|
17756
|
-
replaceMode
|
|
18081
|
+
showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
17757
18082
|
Button,
|
|
17758
18083
|
{
|
|
17759
18084
|
"data-ohw-media-overlay": "",
|
|
17760
18085
|
variant: "outline",
|
|
17761
18086
|
size: "sm",
|
|
17762
|
-
"aria-label":
|
|
18087
|
+
"aria-label": replaceLabel,
|
|
17763
18088
|
className: "gap-1.5 cursor-pointer hover:bg-background",
|
|
17764
18089
|
style: {
|
|
17765
18090
|
...OVERLAY_BUTTON_STYLE,
|
|
@@ -17782,7 +18107,7 @@ function MediaOverlay({
|
|
|
17782
18107
|
},
|
|
17783
18108
|
children: [
|
|
17784
18109
|
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" ?
|
|
18110
|
+
replaceMode === "full" ? replaceLabel : null
|
|
17786
18111
|
]
|
|
17787
18112
|
}
|
|
17788
18113
|
)
|
|
@@ -18292,7 +18617,7 @@ function AiSectionOverlay({
|
|
|
18292
18617
|
|
|
18293
18618
|
// src/ui/ai-section/AiMediaStripOverlay.tsx
|
|
18294
18619
|
var import_react10 = require("react");
|
|
18295
|
-
var
|
|
18620
|
+
var import_lucide_react9 = require("lucide-react");
|
|
18296
18621
|
|
|
18297
18622
|
// src/lib/ai-media-strip-tree.ts
|
|
18298
18623
|
var MEDIA_STRIP_ITEM_MIN = 1;
|
|
@@ -18406,14 +18731,6 @@ function setMediaStripItemAsset(tree, index, assetId) {
|
|
|
18406
18731
|
return true;
|
|
18407
18732
|
});
|
|
18408
18733
|
}
|
|
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
18734
|
function setMediaStripItemLink(tree, index, link) {
|
|
18418
18735
|
return withMediaStrip(tree, (block) => {
|
|
18419
18736
|
const items = block.items;
|
|
@@ -18431,40 +18748,167 @@ function setMediaStripItemLink(tree, index, link) {
|
|
|
18431
18748
|
});
|
|
18432
18749
|
}
|
|
18433
18750
|
|
|
18434
|
-
// src/ui/
|
|
18751
|
+
// src/ui/dialog.tsx
|
|
18752
|
+
var React9 = __toESM(require("react"), 1);
|
|
18753
|
+
var import_radix_ui6 = require("radix-ui");
|
|
18754
|
+
var import_lucide_react8 = require("lucide-react");
|
|
18435
18755
|
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;
|
|
18756
|
+
function Dialog2({
|
|
18757
|
+
...props
|
|
18758
|
+
}) {
|
|
18759
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
|
|
18447
18760
|
}
|
|
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 };
|
|
18761
|
+
function DialogPortal({
|
|
18762
|
+
...props
|
|
18763
|
+
}) {
|
|
18764
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
|
|
18454
18765
|
}
|
|
18455
|
-
function
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
|
|
18460
|
-
|
|
18766
|
+
function DialogOverlay({
|
|
18767
|
+
className,
|
|
18768
|
+
...props
|
|
18769
|
+
}) {
|
|
18770
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18771
|
+
import_radix_ui6.Dialog.Overlay,
|
|
18772
|
+
{
|
|
18773
|
+
"data-slot": "dialog-overlay",
|
|
18774
|
+
"data-ohw-link-modal-root": "",
|
|
18775
|
+
className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
|
|
18776
|
+
...props
|
|
18461
18777
|
}
|
|
18462
|
-
|
|
18463
|
-
|
|
18464
|
-
|
|
18465
|
-
|
|
18466
|
-
|
|
18467
|
-
|
|
18778
|
+
);
|
|
18779
|
+
}
|
|
18780
|
+
var DialogContent = React9.forwardRef(
|
|
18781
|
+
({ className, children, showCloseButton = true, container, ...props }, ref) => {
|
|
18782
|
+
const positionMode = container ? "absolute" : "fixed";
|
|
18783
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DialogPortal, { container: container ?? void 0, children: [
|
|
18784
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
|
|
18785
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
18786
|
+
import_radix_ui6.Dialog.Content,
|
|
18787
|
+
{
|
|
18788
|
+
ref,
|
|
18789
|
+
"data-slot": "dialog-content",
|
|
18790
|
+
className: cn(
|
|
18791
|
+
positionMode,
|
|
18792
|
+
"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",
|
|
18793
|
+
"rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
|
|
18794
|
+
container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
|
|
18795
|
+
className
|
|
18796
|
+
),
|
|
18797
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
18798
|
+
...props,
|
|
18799
|
+
children: [
|
|
18800
|
+
children,
|
|
18801
|
+
showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18802
|
+
import_radix_ui6.Dialog.Close,
|
|
18803
|
+
{
|
|
18804
|
+
type: "button",
|
|
18805
|
+
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
18806
|
+
"aria-label": "Close",
|
|
18807
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
|
|
18808
|
+
}
|
|
18809
|
+
) : null
|
|
18810
|
+
]
|
|
18811
|
+
}
|
|
18812
|
+
)
|
|
18813
|
+
] });
|
|
18814
|
+
}
|
|
18815
|
+
);
|
|
18816
|
+
DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
|
|
18817
|
+
function DialogHeader({
|
|
18818
|
+
className,
|
|
18819
|
+
...props
|
|
18820
|
+
}) {
|
|
18821
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
|
|
18822
|
+
}
|
|
18823
|
+
function DialogFooter({
|
|
18824
|
+
className,
|
|
18825
|
+
...props
|
|
18826
|
+
}) {
|
|
18827
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18828
|
+
"div",
|
|
18829
|
+
{
|
|
18830
|
+
className: cn("flex items-center justify-end gap-2", className),
|
|
18831
|
+
...props
|
|
18832
|
+
}
|
|
18833
|
+
);
|
|
18834
|
+
}
|
|
18835
|
+
var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18836
|
+
import_radix_ui6.Dialog.Title,
|
|
18837
|
+
{
|
|
18838
|
+
ref,
|
|
18839
|
+
className: cn(
|
|
18840
|
+
"text-lg font-semibold leading-none tracking-tight text-card-foreground",
|
|
18841
|
+
className
|
|
18842
|
+
),
|
|
18843
|
+
...props
|
|
18844
|
+
}
|
|
18845
|
+
));
|
|
18846
|
+
DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
|
|
18847
|
+
var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
18848
|
+
import_radix_ui6.Dialog.Description,
|
|
18849
|
+
{
|
|
18850
|
+
ref,
|
|
18851
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
18852
|
+
...props
|
|
18853
|
+
}
|
|
18854
|
+
));
|
|
18855
|
+
DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
|
|
18856
|
+
var DialogClose = import_radix_ui6.Dialog.Close;
|
|
18857
|
+
|
|
18858
|
+
// src/ui/ai-section/AiMediaStripOverlay.tsx
|
|
18859
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
18860
|
+
var PRIMARY3 = "var(--ohw-primary, #0885FE)";
|
|
18861
|
+
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]";
|
|
18862
|
+
function findMediaStripItemElement(sectionId, index) {
|
|
18863
|
+
const sectionEl = findByInstanceId(sectionId);
|
|
18864
|
+
if (!sectionEl) return null;
|
|
18865
|
+
const items = sectionEl.querySelectorAll("[data-ohw-media-strip-item]");
|
|
18866
|
+
for (const el of Array.from(items)) {
|
|
18867
|
+
if (Number(el.dataset.ohwMediaStripIndex) === index) return el;
|
|
18868
|
+
}
|
|
18869
|
+
return null;
|
|
18870
|
+
}
|
|
18871
|
+
function itemRefFromTarget(target) {
|
|
18872
|
+
if (!(target instanceof Element)) return null;
|
|
18873
|
+
if (target.closest(CHROME_SELECTOR)) return null;
|
|
18874
|
+
const itemEl = target.closest("[data-ohw-media-strip-item]");
|
|
18875
|
+
if (!itemEl) return null;
|
|
18876
|
+
const sectionEl = itemEl.closest("[data-ohw-section]");
|
|
18877
|
+
const unit = sectionEl ? movableUnit(sectionEl) : null;
|
|
18878
|
+
const sectionId = unit ? instanceIdOf(unit) || null : null;
|
|
18879
|
+
const index = Number(itemEl.dataset.ohwMediaStripIndex);
|
|
18880
|
+
if (!sectionId || Number.isNaN(index)) return null;
|
|
18881
|
+
return { sectionId, index };
|
|
18882
|
+
}
|
|
18883
|
+
function sameRef(a, b) {
|
|
18884
|
+
return a?.sectionId === b?.sectionId && a?.index === b?.index;
|
|
18885
|
+
}
|
|
18886
|
+
function rectOf(el) {
|
|
18887
|
+
if (!el) return null;
|
|
18888
|
+
const r2 = el.getBoundingClientRect();
|
|
18889
|
+
if (r2.width <= 0 || r2.height <= 0) return null;
|
|
18890
|
+
return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
|
|
18891
|
+
}
|
|
18892
|
+
function readRect2(ref) {
|
|
18893
|
+
return rectOf(ref && findMediaStripItemElement(ref.sectionId, ref.index));
|
|
18894
|
+
}
|
|
18895
|
+
function readStripRect(ref) {
|
|
18896
|
+
const item = ref && findMediaStripItemElement(ref.sectionId, ref.index);
|
|
18897
|
+
return rectOf(item?.closest("[data-ohw-media-strip]") ?? item);
|
|
18898
|
+
}
|
|
18899
|
+
function useLiveItemRect(ref, strip = false) {
|
|
18900
|
+
const [rect, setRect] = (0, import_react10.useState)(null);
|
|
18901
|
+
(0, import_react10.useEffect)(() => {
|
|
18902
|
+
if (!ref) {
|
|
18903
|
+
setRect(null);
|
|
18904
|
+
return;
|
|
18905
|
+
}
|
|
18906
|
+
const update = () => {
|
|
18907
|
+
const next = strip ? readStripRect(ref) : readRect2(ref);
|
|
18908
|
+
setRect(
|
|
18909
|
+
(prev) => prev && next && prev.top === next.top && prev.left === next.left && prev.width === next.width && prev.height === next.height ? prev : next
|
|
18910
|
+
);
|
|
18911
|
+
};
|
|
18468
18912
|
update();
|
|
18469
18913
|
const opts = { capture: true, passive: true };
|
|
18470
18914
|
window.addEventListener("scroll", update, opts);
|
|
@@ -18475,9 +18919,49 @@ function useLiveItemRect(ref) {
|
|
|
18475
18919
|
window.removeEventListener("resize", update);
|
|
18476
18920
|
clearInterval(interval);
|
|
18477
18921
|
};
|
|
18478
|
-
}, [ref?.sectionId, ref?.index]);
|
|
18922
|
+
}, [ref?.sectionId, ref?.index, strip]);
|
|
18479
18923
|
return rect;
|
|
18480
18924
|
}
|
|
18925
|
+
var TOOLBAR_STRIP_GAP = 52;
|
|
18926
|
+
var TOOLBAR_STRIP_GAP_MOBILE = 42;
|
|
18927
|
+
var TOOLBAR_EDGE_MARGIN2 = 4;
|
|
18928
|
+
function StripToolbarSlot({ stripRect, children }) {
|
|
18929
|
+
const slotRef = (0, import_react10.useRef)(null);
|
|
18930
|
+
const [shift, setShift] = (0, import_react10.useState)(0);
|
|
18931
|
+
const centerX = stripRect.left + stripRect.width / 2;
|
|
18932
|
+
(0, import_react10.useLayoutEffect)(() => {
|
|
18933
|
+
const slot = slotRef.current;
|
|
18934
|
+
if (!slot) return;
|
|
18935
|
+
const clamp = () => {
|
|
18936
|
+
const w = slot.offsetWidth;
|
|
18937
|
+
if (w <= 0) return;
|
|
18938
|
+
const half = w / 2;
|
|
18939
|
+
const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
|
|
18940
|
+
setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
|
|
18941
|
+
};
|
|
18942
|
+
clamp();
|
|
18943
|
+
const ro = new ResizeObserver(clamp);
|
|
18944
|
+
ro.observe(slot);
|
|
18945
|
+
return () => ro.disconnect();
|
|
18946
|
+
}, [centerX, children]);
|
|
18947
|
+
const mobile = window.innerWidth <= 480;
|
|
18948
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
18949
|
+
"div",
|
|
18950
|
+
{
|
|
18951
|
+
ref: slotRef,
|
|
18952
|
+
"data-ohw-bridge": "",
|
|
18953
|
+
"data-ohw-item-interaction": "",
|
|
18954
|
+
className: "pointer-events-auto fixed",
|
|
18955
|
+
style: {
|
|
18956
|
+
top: stripRect.top - (mobile ? TOOLBAR_STRIP_GAP_MOBILE : TOOLBAR_STRIP_GAP),
|
|
18957
|
+
left: centerX + shift,
|
|
18958
|
+
transform: "translate(-50%, -100%)",
|
|
18959
|
+
zIndex: 2147483645
|
|
18960
|
+
},
|
|
18961
|
+
children
|
|
18962
|
+
}
|
|
18963
|
+
);
|
|
18964
|
+
}
|
|
18481
18965
|
function requestValidateTree(postToParent2, tree) {
|
|
18482
18966
|
return new Promise((resolve) => {
|
|
18483
18967
|
const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -18514,116 +18998,72 @@ function requestAssetPick(postToParent2) {
|
|
|
18514
18998
|
postToParent2({ type: "ow:asset-pick", id });
|
|
18515
18999
|
});
|
|
18516
19000
|
}
|
|
18517
|
-
function
|
|
19001
|
+
function ToolbarAction({
|
|
18518
19002
|
label,
|
|
18519
19003
|
onClick,
|
|
18520
|
-
disabled,
|
|
18521
|
-
active
|
|
19004
|
+
disabled = false,
|
|
19005
|
+
active = false,
|
|
19006
|
+
children
|
|
18522
19007
|
}) {
|
|
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
|
-
);
|
|
19008
|
+
const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, active, onClick, className: "size-8", children });
|
|
19009
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
|
|
19010
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: button }) : button }),
|
|
19011
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
|
|
19012
|
+
] });
|
|
18550
19013
|
}
|
|
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",
|
|
19014
|
+
function ToolbarSeparator() {
|
|
19015
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
|
|
19016
|
+
}
|
|
19017
|
+
function OptionMenu({
|
|
19018
|
+
label,
|
|
19019
|
+
value,
|
|
19020
|
+
options,
|
|
19021
|
+
onChange
|
|
19022
|
+
}) {
|
|
19023
|
+
const current = options.find((o) => o.value === value) ?? options[0];
|
|
19024
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
|
|
19025
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground", children: label }),
|
|
19026
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DropdownMenu, { modal: false, children: [
|
|
19027
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
19028
|
+
"button",
|
|
19029
|
+
{
|
|
19030
|
+
type: "button",
|
|
19031
|
+
"aria-label": label,
|
|
19032
|
+
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",
|
|
19033
|
+
children: [
|
|
19034
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex-1 text-left", children: current.label }),
|
|
19035
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
|
|
19036
|
+
]
|
|
19037
|
+
}
|
|
19038
|
+
) }),
|
|
19039
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[132px] min-w-[132px] p-1", children: options.map((option) => {
|
|
19040
|
+
const selected = option.value === value;
|
|
19041
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
19042
|
+
DropdownMenuItem,
|
|
18586
19043
|
{
|
|
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
|
-
);
|
|
19044
|
+
role: "menuitemradio",
|
|
19045
|
+
"aria-checked": selected,
|
|
19046
|
+
onSelect: () => onChange(option.value),
|
|
19047
|
+
className: cn("grid grid-cols-[1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
|
|
19048
|
+
children: [
|
|
19049
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: option.label }),
|
|
19050
|
+
selected && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
|
|
19051
|
+
]
|
|
19052
|
+
},
|
|
19053
|
+
option.value
|
|
19054
|
+
);
|
|
19055
|
+
}) })
|
|
19056
|
+
] })
|
|
19057
|
+
] });
|
|
18618
19058
|
}
|
|
18619
19059
|
var VARIANTS = [
|
|
18620
19060
|
{ value: "logos", label: "Logos" },
|
|
18621
19061
|
{ value: "reel", label: "Reel" }
|
|
18622
19062
|
];
|
|
18623
19063
|
var ALIGNS = [
|
|
18624
|
-
{ value: "left", label: "
|
|
18625
|
-
{ value: "center", label: "
|
|
18626
|
-
{ value: "right", label: "
|
|
19064
|
+
{ value: "left", label: "Align left", Icon: import_lucide_react9.AlignLeft },
|
|
19065
|
+
{ value: "center", label: "Align center", Icon: import_lucide_react9.AlignCenter },
|
|
19066
|
+
{ value: "right", label: "Align right", Icon: import_lucide_react9.AlignRight }
|
|
18627
19067
|
];
|
|
18628
19068
|
var HOVERS = [
|
|
18629
19069
|
{ value: "none", label: "None" },
|
|
@@ -18631,104 +19071,104 @@ var HOVERS = [
|
|
|
18631
19071
|
{ value: "zoom", label: "Zoom" },
|
|
18632
19072
|
{ value: "fade", label: "Fade" }
|
|
18633
19073
|
];
|
|
18634
|
-
var TREATMENTS = [
|
|
18635
|
-
{ value: "color", label: "Color" },
|
|
18636
|
-
{ value: "grayscale", label: "Grayscale" }
|
|
18637
|
-
];
|
|
18638
19074
|
var HTTP_URL_RE = /^https?:\/\/\S+$/i;
|
|
18639
|
-
function
|
|
18640
|
-
|
|
19075
|
+
function LinkModal({
|
|
19076
|
+
href,
|
|
19077
|
+
linkTarget,
|
|
19078
|
+
onClose,
|
|
19079
|
+
onSave
|
|
19080
|
+
}) {
|
|
19081
|
+
const [draft, setDraft] = (0, import_react10.useState)(href);
|
|
19082
|
+
const [newTab, setNewTab] = (0, import_react10.useState)(linkTarget !== "same-tab");
|
|
18641
19083
|
const [invalid, setInvalid] = (0, import_react10.useState)(false);
|
|
18642
|
-
|
|
18643
|
-
setDraft(value);
|
|
18644
|
-
setInvalid(false);
|
|
18645
|
-
}, [value]);
|
|
18646
|
-
const commit = () => {
|
|
19084
|
+
const save = () => {
|
|
18647
19085
|
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
|
-
]
|
|
19086
|
+
if (next && !HTTP_URL_RE.test(next)) {
|
|
19087
|
+
setInvalid(true);
|
|
19088
|
+
return;
|
|
18717
19089
|
}
|
|
18718
|
-
|
|
19090
|
+
onSave(next, newTab ? "new-tab" : "same-tab");
|
|
19091
|
+
onClose();
|
|
19092
|
+
};
|
|
19093
|
+
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: [
|
|
19094
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogTitle, { className: "text-base", children: "Image link" }) }),
|
|
19095
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-3", children: [
|
|
19096
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19097
|
+
"input",
|
|
19098
|
+
{
|
|
19099
|
+
autoFocus: true,
|
|
19100
|
+
type: "text",
|
|
19101
|
+
inputMode: "url",
|
|
19102
|
+
value: draft,
|
|
19103
|
+
onChange: (e) => {
|
|
19104
|
+
setDraft(e.target.value);
|
|
19105
|
+
setInvalid(false);
|
|
19106
|
+
},
|
|
19107
|
+
onKeyDown: (e) => {
|
|
19108
|
+
if (e.key === "Enter") save();
|
|
19109
|
+
},
|
|
19110
|
+
placeholder: "https://example.com",
|
|
19111
|
+
"aria-label": "Image link URL",
|
|
19112
|
+
"aria-invalid": invalid,
|
|
19113
|
+
className: cn(
|
|
19114
|
+
"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",
|
|
19115
|
+
invalid && "border-destructive focus:border-destructive"
|
|
19116
|
+
)
|
|
19117
|
+
}
|
|
19118
|
+
),
|
|
19119
|
+
invalid && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-xs text-destructive", children: "Enter a full URL starting with http:// or https://" }),
|
|
19120
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex items-center gap-2 text-[13px] text-foreground", children: [
|
|
19121
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("input", { type: "checkbox", checked: newTab, onChange: (e) => setNewTab(e.target.checked) }),
|
|
19122
|
+
"Open in new tab"
|
|
19123
|
+
] })
|
|
19124
|
+
] }),
|
|
19125
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DialogFooter, { children: [
|
|
19126
|
+
href && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19127
|
+
"button",
|
|
19128
|
+
{
|
|
19129
|
+
type: "button",
|
|
19130
|
+
onClick: () => {
|
|
19131
|
+
onSave("", "new-tab");
|
|
19132
|
+
onClose();
|
|
19133
|
+
},
|
|
19134
|
+
className: "mr-auto rounded-[6px] px-3 py-2 text-[13px] font-medium text-destructive transition-colors hover:bg-muted",
|
|
19135
|
+
children: "Remove link"
|
|
19136
|
+
}
|
|
19137
|
+
),
|
|
19138
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19139
|
+
"button",
|
|
19140
|
+
{
|
|
19141
|
+
type: "button",
|
|
19142
|
+
onClick: onClose,
|
|
19143
|
+
className: "rounded-[6px] px-3 py-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted",
|
|
19144
|
+
children: "Cancel"
|
|
19145
|
+
}
|
|
19146
|
+
),
|
|
19147
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19148
|
+
"button",
|
|
19149
|
+
{
|
|
19150
|
+
type: "button",
|
|
19151
|
+
onClick: save,
|
|
19152
|
+
className: "rounded-[6px] bg-bridge-primary px-3 py-2 text-[13px] font-medium text-primary-foreground transition-opacity hover:opacity-90",
|
|
19153
|
+
children: "Save"
|
|
19154
|
+
}
|
|
19155
|
+
)
|
|
19156
|
+
] })
|
|
19157
|
+
] }) });
|
|
18719
19158
|
}
|
|
18720
19159
|
function MediaStripToolbar({
|
|
18721
19160
|
entry,
|
|
18722
19161
|
index,
|
|
18723
19162
|
pending,
|
|
18724
19163
|
error,
|
|
19164
|
+
itemEditOn,
|
|
19165
|
+
onToggleItemEdit,
|
|
18725
19166
|
onSetVariant,
|
|
18726
19167
|
onSetAlign,
|
|
18727
19168
|
onSetHover,
|
|
18728
19169
|
onSetTreatment,
|
|
18729
19170
|
onResize,
|
|
18730
|
-
|
|
18731
|
-
onSetLink,
|
|
19171
|
+
onOpenLink,
|
|
18732
19172
|
onAddItem,
|
|
18733
19173
|
onRemoveItem,
|
|
18734
19174
|
onChangeImage,
|
|
@@ -18737,81 +19177,102 @@ function MediaStripToolbar({
|
|
|
18737
19177
|
}) {
|
|
18738
19178
|
const items = mediaStripItems(entry.tree);
|
|
18739
19179
|
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
19180
|
const slots = findMediaStripSlots(entry.tree);
|
|
18744
19181
|
const variant = slots?.variant === "reel" ? "reel" : "logos";
|
|
18745
19182
|
const align = slots?.align === "left" || slots?.align === "right" ? slots.align : "center";
|
|
18746
19183
|
const hover = slots?.hover === "none" || slots?.hover === "lift" || slots?.hover === "zoom" || slots?.hover === "fade" ? slots.hover : "lift";
|
|
18747
19184
|
const treatment = slots?.treatment === "color" || slots?.treatment === "grayscale" ? slots.treatment : variant === "logos" ? "grayscale" : "color";
|
|
18748
19185
|
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)(
|
|
19186
|
+
const mobile = window.innerWidth <= 480;
|
|
19187
|
+
const gatedLabel = (label) => itemEditOn ? label : "Turn on the star to edit this image";
|
|
19188
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
18761
19189
|
"div",
|
|
18762
19190
|
{
|
|
18763
19191
|
"data-ohw-bridge": "",
|
|
18764
19192
|
"data-ohw-toolbar": "",
|
|
18765
19193
|
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
|
-
},
|
|
19194
|
+
className: cn("flex flex-col items-center gap-1.5 font-sans", pending && "pointer-events-none opacity-60"),
|
|
19195
|
+
style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
|
|
18779
19196
|
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
|
-
|
|
19197
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
19198
|
+
"div",
|
|
19199
|
+
{
|
|
19200
|
+
className: cn(
|
|
19201
|
+
"flex items-center gap-1 rounded-[10px] border border-border bg-background p-1 whitespace-nowrap",
|
|
19202
|
+
"shadow-[0px_10px_15px_-3px_rgba(0,0,0,0.1),0px_4px_6px_-4px_rgba(0,0,0,0.1)]",
|
|
19203
|
+
mobile ? "flex-wrap justify-center" : "flex-nowrap"
|
|
19204
|
+
),
|
|
19205
|
+
children: [
|
|
19206
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
|
|
19207
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19208
|
+
CustomToolbarButton,
|
|
19209
|
+
{
|
|
19210
|
+
type: "button",
|
|
19211
|
+
"aria-label": itemEditOn ? "Done editing this image" : "Edit this image",
|
|
19212
|
+
"aria-pressed": itemEditOn,
|
|
19213
|
+
onClick: onToggleItemEdit,
|
|
19214
|
+
className: "group size-8",
|
|
19215
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19216
|
+
import_lucide_react9.Star,
|
|
19217
|
+
{
|
|
19218
|
+
size: 16,
|
|
19219
|
+
fill: "currentColor",
|
|
19220
|
+
strokeWidth: 0,
|
|
19221
|
+
className: cn(
|
|
19222
|
+
"transition-colors",
|
|
19223
|
+
itemEditOn ? "text-[#f59e0b] group-hover:text-[#d97706]" : "text-[#d6d3d1] group-hover:text-[#fbbf24]"
|
|
19224
|
+
),
|
|
19225
|
+
style: { pointerEvents: "none" }
|
|
19226
|
+
}
|
|
19227
|
+
)
|
|
19228
|
+
}
|
|
19229
|
+
) }),
|
|
19230
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: itemEditOn ? "Done editing this image" : "Edit this image" })
|
|
19231
|
+
] }),
|
|
19232
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
|
|
19233
|
+
/* @__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" } }) }),
|
|
19234
|
+
/* @__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" } }) }),
|
|
19235
|
+
/* @__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" } }) }),
|
|
19236
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19237
|
+
ToolbarAction,
|
|
19238
|
+
{
|
|
19239
|
+
label: gatedLabel("Move right"),
|
|
19240
|
+
onClick: onMoveRight,
|
|
19241
|
+
disabled: !itemEditOn || index >= itemCount - 1,
|
|
19242
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ArrowRight, { size: 16, style: { pointerEvents: "none" } })
|
|
19243
|
+
}
|
|
19244
|
+
),
|
|
19245
|
+
/* @__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" } }) }),
|
|
19246
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
|
|
19247
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OptionMenu, { label: "Layout", value: variant, options: VARIANTS, onChange: onSetVariant }),
|
|
19248
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OptionMenu, { label: "Hover", value: hover, options: HOVERS, onChange: onSetHover }),
|
|
19249
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19250
|
+
ToolbarAction,
|
|
19251
|
+
{
|
|
19252
|
+
label: treatment === "grayscale" ? "Show in color" : "Show grayscale",
|
|
19253
|
+
active: treatment === "grayscale",
|
|
19254
|
+
onClick: () => onSetTreatment(treatment === "grayscale" ? "color" : "grayscale"),
|
|
19255
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Palette, { size: 16, style: { pointerEvents: "none" } })
|
|
19256
|
+
}
|
|
19257
|
+
),
|
|
19258
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
|
|
19259
|
+
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)),
|
|
19260
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
|
|
19261
|
+
/* @__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" } }) }),
|
|
19262
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "min-w-[40px] text-center text-xs tabular-nums text-muted-foreground", children: [
|
|
19263
|
+
sizeValue,
|
|
19264
|
+
"px"
|
|
19265
|
+
] }),
|
|
19266
|
+
/* @__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" } }) }),
|
|
19267
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
|
|
19268
|
+
/* @__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" } }) })
|
|
19269
|
+
]
|
|
19270
|
+
}
|
|
19271
|
+
),
|
|
19272
|
+
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
19273
|
]
|
|
18813
19274
|
}
|
|
18814
|
-
);
|
|
19275
|
+
) });
|
|
18815
19276
|
}
|
|
18816
19277
|
function findMediaStripSlots(tree) {
|
|
18817
19278
|
const stack = tree.rows.flatMap((row) => row.blocks);
|
|
@@ -18830,31 +19291,23 @@ function AiMediaStripOverlay({
|
|
|
18830
19291
|
}) {
|
|
18831
19292
|
const [hover, setHover] = (0, import_react10.useState)(null);
|
|
18832
19293
|
const [active, setActive] = (0, import_react10.useState)(null);
|
|
19294
|
+
const [itemEditOn, setItemEditOn] = (0, import_react10.useState)(false);
|
|
19295
|
+
const [linkOpen, setLinkOpen] = (0, import_react10.useState)(false);
|
|
18833
19296
|
const [pending, setPending] = (0, import_react10.useState)(false);
|
|
18834
19297
|
const [error, setError] = (0, import_react10.useState)(null);
|
|
18835
19298
|
const activeRef = (0, import_react10.useRef)(null);
|
|
18836
19299
|
activeRef.current = active;
|
|
19300
|
+
const linkOpenRef = (0, import_react10.useRef)(false);
|
|
19301
|
+
linkOpenRef.current = linkOpen;
|
|
18837
19302
|
(0, import_react10.useEffect)(() => {
|
|
18838
19303
|
const onMove = (e) => {
|
|
19304
|
+
const ref = itemRefFromTarget(e.target);
|
|
18839
19305
|
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 });
|
|
19306
|
+
const overEditable = Boolean(t?.closest?.("[data-ohw-editable]"));
|
|
19307
|
+
setHover((prev) => {
|
|
19308
|
+
const next = ref && !overEditable ? ref : null;
|
|
19309
|
+
return sameRef(prev, next) ? prev : next;
|
|
19310
|
+
});
|
|
18858
19311
|
};
|
|
18859
19312
|
const onLeave = () => setHover(null);
|
|
18860
19313
|
document.addEventListener("mousemove", onMove, { passive: true });
|
|
@@ -18866,23 +19319,25 @@ function AiMediaStripOverlay({
|
|
|
18866
19319
|
}, []);
|
|
18867
19320
|
(0, import_react10.useEffect)(() => {
|
|
18868
19321
|
const onClick = (e) => {
|
|
18869
|
-
const current = activeRef.current;
|
|
18870
|
-
if (!current) return;
|
|
18871
19322
|
const t = e.target;
|
|
18872
|
-
if (!(t instanceof
|
|
18873
|
-
if (t.closest(
|
|
18874
|
-
const
|
|
18875
|
-
if (
|
|
18876
|
-
|
|
19323
|
+
if (!(t instanceof Element)) return;
|
|
19324
|
+
if (t.closest(CHROME_SELECTOR)) return;
|
|
19325
|
+
const ref = itemRefFromTarget(t);
|
|
19326
|
+
if (sameRef(activeRef.current, ref)) return;
|
|
19327
|
+
if (ref) clearInteractionChromeRef.current();
|
|
19328
|
+
setItemEditOn(false);
|
|
18877
19329
|
setError(null);
|
|
19330
|
+
setActive(ref);
|
|
18878
19331
|
};
|
|
18879
19332
|
document.addEventListener("click", onClick, true);
|
|
18880
19333
|
return () => document.removeEventListener("click", onClick, true);
|
|
18881
|
-
}, []);
|
|
19334
|
+
}, [clearInteractionChromeRef]);
|
|
18882
19335
|
(0, import_react10.useEffect)(() => {
|
|
18883
19336
|
const onBlur = () => {
|
|
18884
19337
|
if (!activeRef.current) return;
|
|
18885
19338
|
setActive(null);
|
|
19339
|
+
setItemEditOn(false);
|
|
19340
|
+
setLinkOpen(false);
|
|
18886
19341
|
setError(null);
|
|
18887
19342
|
};
|
|
18888
19343
|
window.addEventListener("blur", onBlur);
|
|
@@ -18892,7 +19347,10 @@ function AiMediaStripOverlay({
|
|
|
18892
19347
|
const onKeyDown = (e) => {
|
|
18893
19348
|
if (e.key !== "Escape") return;
|
|
18894
19349
|
if (!activeRef.current) return;
|
|
19350
|
+
if (linkOpenRef.current) return;
|
|
19351
|
+
if (document.querySelector('[data-ohw-more-menu][data-state="open"]')) return;
|
|
18895
19352
|
setActive(null);
|
|
19353
|
+
setItemEditOn(false);
|
|
18896
19354
|
setError(null);
|
|
18897
19355
|
};
|
|
18898
19356
|
document.addEventListener("keydown", onKeyDown);
|
|
@@ -18926,137 +19384,133 @@ function AiMediaStripOverlay({
|
|
|
18926
19384
|
tree: nextTree,
|
|
18927
19385
|
label: entry.label,
|
|
18928
19386
|
media: extraMedia ? { ...entry.media, ...extraMedia } : entry.media,
|
|
18929
|
-
...entry.brand ? { brand: entry.brand } : {}
|
|
19387
|
+
...entry.brand ? { brand: entry.brand } : {},
|
|
19388
|
+
// The user is already looking at the strip they just edited — recentering the viewport
|
|
19389
|
+
// on every edit reads as the section jumping around.
|
|
19390
|
+
skipScrollIntoView: true
|
|
18930
19391
|
});
|
|
18931
19392
|
return true;
|
|
18932
19393
|
},
|
|
18933
19394
|
[findEntry, postToParent2, applyTreeLocalRef]
|
|
18934
19395
|
);
|
|
18935
19396
|
const activeEntry = active ? findEntry(active.sectionId) : null;
|
|
18936
|
-
const hoverHandleRect = useLiveItemRect(!active && hover ? hover : null);
|
|
18937
19397
|
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) => {
|
|
19398
|
+
const activeStripRect = useLiveItemRect(active, true);
|
|
19399
|
+
const hoverRect = useLiveItemRect(hover && !sameRef(hover, active) ? hover : null);
|
|
19400
|
+
const activeItems = activeEntry ? mediaStripItems(activeEntry.tree) : [];
|
|
19401
|
+
const activeItem = active ? activeItems[active.index] : void 0;
|
|
19402
|
+
const activeHref = activeItem?.href ?? "";
|
|
19403
|
+
const activeTarget = activeItem?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
|
|
19404
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
19405
|
+
hoverRect && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
|
|
19406
|
+
activeItemRect && activeStripRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
19407
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: activeItemRect, state: "active-top" }),
|
|
19408
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StripToolbarSlot, { stripRect: activeStripRect, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19409
|
+
MediaStripToolbar,
|
|
19410
|
+
{
|
|
19411
|
+
entry: activeEntry,
|
|
19412
|
+
index: active.index,
|
|
19413
|
+
pending,
|
|
19414
|
+
error,
|
|
19415
|
+
itemEditOn,
|
|
19416
|
+
onToggleItemEdit: () => setItemEditOn((on) => !on),
|
|
19417
|
+
onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
|
|
19418
|
+
onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
|
|
19419
|
+
onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
|
|
19420
|
+
onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
|
|
19421
|
+
onResize: (delta) => {
|
|
19422
|
+
const slots = findMediaStripSlots(activeEntry.tree);
|
|
19423
|
+
const variant = slots?.variant === "reel" ? "reel" : "logos";
|
|
19424
|
+
const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
|
|
19425
|
+
void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
|
|
19426
|
+
},
|
|
19427
|
+
onOpenLink: () => setLinkOpen(true),
|
|
19428
|
+
onAddItem: () => {
|
|
19429
|
+
void requestAssetPick(postToParent2).then((picked) => {
|
|
19430
|
+
if (!picked) return;
|
|
19431
|
+
const item = { assetId: picked.assetId, alt: "Logo" };
|
|
18998
19432
|
void runMutation(
|
|
18999
19433
|
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
|
-
);
|
|
19434
|
+
(tree) => addMediaStripItem(tree, item, active.index),
|
|
19435
|
+
{ [picked.assetId]: picked.url }
|
|
19436
|
+
).then((applied) => {
|
|
19437
|
+
if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
|
|
19024
19438
|
});
|
|
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
|
-
}
|
|
19439
|
+
});
|
|
19440
|
+
},
|
|
19441
|
+
onChangeImage: () => {
|
|
19442
|
+
void requestAssetPick(postToParent2).then((picked) => {
|
|
19443
|
+
if (!picked) return;
|
|
19444
|
+
void runMutation(
|
|
19445
|
+
active.sectionId,
|
|
19446
|
+
(tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
|
|
19447
|
+
{ [picked.assetId]: picked.url }
|
|
19045
19448
|
);
|
|
19046
|
-
}
|
|
19449
|
+
});
|
|
19450
|
+
},
|
|
19451
|
+
onRemoveItem: () => {
|
|
19452
|
+
void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
|
|
19453
|
+
(applied) => {
|
|
19454
|
+
if (applied) setActive(null);
|
|
19455
|
+
}
|
|
19456
|
+
);
|
|
19457
|
+
},
|
|
19458
|
+
onMoveLeft: () => {
|
|
19459
|
+
void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
|
|
19460
|
+
(applied) => {
|
|
19461
|
+
if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
|
|
19462
|
+
}
|
|
19463
|
+
);
|
|
19464
|
+
},
|
|
19465
|
+
onMoveRight: () => {
|
|
19466
|
+
void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
|
|
19467
|
+
(applied) => {
|
|
19468
|
+
if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
|
|
19469
|
+
}
|
|
19470
|
+
);
|
|
19047
19471
|
}
|
|
19048
|
-
|
|
19049
|
-
}
|
|
19050
|
-
|
|
19472
|
+
}
|
|
19473
|
+
) }),
|
|
19474
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19475
|
+
"div",
|
|
19476
|
+
{
|
|
19477
|
+
"data-ohw-bridge": "",
|
|
19478
|
+
"data-ohw-item-interaction": "",
|
|
19479
|
+
"aria-hidden": true,
|
|
19480
|
+
className: "pointer-events-none fixed rounded-[4px] px-[7px] py-[3px] font-sans text-[11px] font-medium leading-none text-primary-foreground whitespace-nowrap",
|
|
19481
|
+
style: {
|
|
19482
|
+
top: activeItemRect.top + activeItemRect.height + 10,
|
|
19483
|
+
left: activeItemRect.left - 5,
|
|
19484
|
+
background: PRIMARY3,
|
|
19485
|
+
zIndex: 2147483644
|
|
19486
|
+
},
|
|
19487
|
+
children: "Media item"
|
|
19488
|
+
}
|
|
19489
|
+
),
|
|
19490
|
+
linkOpen && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
19491
|
+
LinkModal,
|
|
19492
|
+
{
|
|
19493
|
+
href: activeHref,
|
|
19494
|
+
linkTarget: activeTarget,
|
|
19495
|
+
onClose: () => setLinkOpen(false),
|
|
19496
|
+
onSave: (href, linkTarget) => {
|
|
19497
|
+
void runMutation(
|
|
19498
|
+
active.sectionId,
|
|
19499
|
+
(tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
|
|
19500
|
+
);
|
|
19501
|
+
}
|
|
19502
|
+
}
|
|
19503
|
+
)
|
|
19504
|
+
] })
|
|
19051
19505
|
] });
|
|
19052
19506
|
}
|
|
19053
19507
|
|
|
19054
19508
|
// src/ui/ai-section/AiPricingCardOverlay.tsx
|
|
19055
19509
|
var import_react11 = require("react");
|
|
19056
|
-
var
|
|
19057
|
-
var
|
|
19510
|
+
var import_lucide_react10 = require("lucide-react");
|
|
19511
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
19058
19512
|
var PRIMARY4 = "var(--ohw-primary, #0885FE)";
|
|
19059
|
-
var
|
|
19513
|
+
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
19514
|
function findPricingCardElement(sectionId, index) {
|
|
19061
19515
|
const sectionEl = findByInstanceId(sectionId);
|
|
19062
19516
|
if (!sectionEl) return null;
|
|
@@ -19068,7 +19522,7 @@ function findPricingCardElement(sectionId, index) {
|
|
|
19068
19522
|
}
|
|
19069
19523
|
function cardRefFromTarget(target) {
|
|
19070
19524
|
if (!(target instanceof Element)) return null;
|
|
19071
|
-
if (target.closest(
|
|
19525
|
+
if (target.closest(CHROME_SELECTOR2)) return null;
|
|
19072
19526
|
const cardEl = target.closest("[data-ohw-pricing-card]");
|
|
19073
19527
|
if (!cardEl) return null;
|
|
19074
19528
|
const sectionEl = cardEl.closest("[data-ohw-section]");
|
|
@@ -19078,21 +19532,21 @@ function cardRefFromTarget(target) {
|
|
|
19078
19532
|
if (!sectionId || Number.isNaN(index)) return null;
|
|
19079
19533
|
return { sectionId, index };
|
|
19080
19534
|
}
|
|
19081
|
-
function
|
|
19535
|
+
function sameRef2(a, b) {
|
|
19082
19536
|
return a?.sectionId === b?.sectionId && a?.index === b?.index;
|
|
19083
19537
|
}
|
|
19084
|
-
function
|
|
19538
|
+
function rectOf2(el) {
|
|
19085
19539
|
if (!el) return null;
|
|
19086
19540
|
const r2 = el.getBoundingClientRect();
|
|
19087
19541
|
if (r2.width <= 0 || r2.height <= 0) return null;
|
|
19088
19542
|
return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
|
|
19089
19543
|
}
|
|
19090
19544
|
function readRect3(ref) {
|
|
19091
|
-
return
|
|
19545
|
+
return rectOf2(ref && findPricingCardElement(ref.sectionId, ref.index));
|
|
19092
19546
|
}
|
|
19093
19547
|
function readGridRect(ref) {
|
|
19094
19548
|
const card = ref && findPricingCardElement(ref.sectionId, ref.index);
|
|
19095
|
-
return
|
|
19549
|
+
return rectOf2(card?.closest("[data-ohw-pricing-collection]") ?? card);
|
|
19096
19550
|
}
|
|
19097
19551
|
function useLiveCardRect(ref, grid = false) {
|
|
19098
19552
|
const [rect, setRect] = (0, import_react11.useState)(null);
|
|
@@ -19122,7 +19576,7 @@ function useLiveCardRect(ref, grid = false) {
|
|
|
19122
19576
|
}
|
|
19123
19577
|
var TOOLBAR_GRID_GAP = 52;
|
|
19124
19578
|
var TOOLBAR_GRID_GAP_MOBILE = 42;
|
|
19125
|
-
var
|
|
19579
|
+
var TOOLBAR_EDGE_MARGIN3 = 4;
|
|
19126
19580
|
function GridToolbarSlot({ gridRect, children }) {
|
|
19127
19581
|
const slotRef = (0, import_react11.useRef)(null);
|
|
19128
19582
|
const [shift, setShift] = (0, import_react11.useState)(0);
|
|
@@ -19134,7 +19588,7 @@ function GridToolbarSlot({ gridRect, children }) {
|
|
|
19134
19588
|
const w = slot.offsetWidth;
|
|
19135
19589
|
if (w <= 0) return;
|
|
19136
19590
|
const half = w / 2;
|
|
19137
|
-
const clamped = Math.max(
|
|
19591
|
+
const clamped = Math.max(TOOLBAR_EDGE_MARGIN3 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half));
|
|
19138
19592
|
setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
|
|
19139
19593
|
};
|
|
19140
19594
|
clamp();
|
|
@@ -19143,7 +19597,7 @@ function GridToolbarSlot({ gridRect, children }) {
|
|
|
19143
19597
|
return () => ro.disconnect();
|
|
19144
19598
|
}, [centerX, children]);
|
|
19145
19599
|
const mobile = window.innerWidth <= 480;
|
|
19146
|
-
return /* @__PURE__ */ (0,
|
|
19600
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
19147
19601
|
"div",
|
|
19148
19602
|
{
|
|
19149
19603
|
ref: slotRef,
|
|
@@ -19178,7 +19632,7 @@ function requestValidateTree2(postToParent2, tree) {
|
|
|
19178
19632
|
});
|
|
19179
19633
|
}
|
|
19180
19634
|
function StyleSwatch({ style }) {
|
|
19181
|
-
return /* @__PURE__ */ (0,
|
|
19635
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
19182
19636
|
"span",
|
|
19183
19637
|
{
|
|
19184
19638
|
"aria-hidden": true,
|
|
@@ -19192,35 +19646,35 @@ function StyleSwatch({ style }) {
|
|
|
19192
19646
|
);
|
|
19193
19647
|
}
|
|
19194
19648
|
function ButtonGlyph() {
|
|
19195
|
-
return /* @__PURE__ */ (0,
|
|
19196
|
-
/* @__PURE__ */ (0,
|
|
19197
|
-
/* @__PURE__ */ (0,
|
|
19649
|
+
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: [
|
|
19650
|
+
/* @__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" }),
|
|
19651
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
19198
19652
|
] });
|
|
19199
19653
|
}
|
|
19200
19654
|
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,
|
|
19655
|
+
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: [
|
|
19656
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
19657
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
19658
|
+
/* @__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" }),
|
|
19659
|
+
/* @__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" }),
|
|
19660
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
19661
|
+
mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
|
|
19208
19662
|
] });
|
|
19209
19663
|
}
|
|
19210
|
-
function
|
|
19664
|
+
function ToolbarAction2({
|
|
19211
19665
|
label,
|
|
19212
19666
|
onClick,
|
|
19213
19667
|
disabled = false,
|
|
19214
19668
|
children
|
|
19215
19669
|
}) {
|
|
19216
|
-
const button = /* @__PURE__ */ (0,
|
|
19217
|
-
return /* @__PURE__ */ (0,
|
|
19218
|
-
/* @__PURE__ */ (0,
|
|
19219
|
-
/* @__PURE__ */ (0,
|
|
19670
|
+
const button = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
|
|
19671
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
|
|
19672
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "inline-flex", children: button }) : button }),
|
|
19673
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
|
|
19220
19674
|
] });
|
|
19221
19675
|
}
|
|
19222
|
-
function
|
|
19223
|
-
return /* @__PURE__ */ (0,
|
|
19676
|
+
function ToolbarSeparator2() {
|
|
19677
|
+
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
19678
|
}
|
|
19225
19679
|
function CardStyleMenu({
|
|
19226
19680
|
value,
|
|
@@ -19228,7 +19682,7 @@ function CardStyleMenu({
|
|
|
19228
19682
|
disabled = false
|
|
19229
19683
|
}) {
|
|
19230
19684
|
const current = PRICING_CARD_STYLES.find((s) => s.value === value) ?? PRICING_CARD_STYLES[0];
|
|
19231
|
-
const trigger = /* @__PURE__ */ (0,
|
|
19685
|
+
const trigger = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
19232
19686
|
"button",
|
|
19233
19687
|
{
|
|
19234
19688
|
type: "button",
|
|
@@ -19239,28 +19693,28 @@ function CardStyleMenu({
|
|
|
19239
19693
|
disabled && "cursor-not-allowed opacity-40 hover:bg-transparent"
|
|
19240
19694
|
),
|
|
19241
19695
|
children: [
|
|
19242
|
-
/* @__PURE__ */ (0,
|
|
19243
|
-
/* @__PURE__ */ (0,
|
|
19244
|
-
/* @__PURE__ */ (0,
|
|
19696
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StyleSwatch, { style: current.value }),
|
|
19697
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "flex-1 text-left", children: current.label }),
|
|
19698
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
|
|
19245
19699
|
]
|
|
19246
19700
|
}
|
|
19247
19701
|
);
|
|
19248
19702
|
if (disabled) {
|
|
19249
|
-
return /* @__PURE__ */ (0,
|
|
19250
|
-
/* @__PURE__ */ (0,
|
|
19251
|
-
/* @__PURE__ */ (0,
|
|
19252
|
-
/* @__PURE__ */ (0,
|
|
19253
|
-
/* @__PURE__ */ (0,
|
|
19703
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
|
|
19704
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
|
|
19705
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
|
|
19706
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "inline-flex", children: trigger }) }),
|
|
19707
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
|
|
19254
19708
|
] })
|
|
19255
19709
|
] });
|
|
19256
19710
|
}
|
|
19257
|
-
return /* @__PURE__ */ (0,
|
|
19258
|
-
/* @__PURE__ */ (0,
|
|
19259
|
-
/* @__PURE__ */ (0,
|
|
19260
|
-
/* @__PURE__ */ (0,
|
|
19261
|
-
/* @__PURE__ */ (0,
|
|
19711
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
|
|
19712
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
|
|
19713
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DropdownMenu, { modal: false, children: [
|
|
19714
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
|
|
19715
|
+
/* @__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
19716
|
const selected = style.value === value;
|
|
19263
|
-
return /* @__PURE__ */ (0,
|
|
19717
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
19264
19718
|
DropdownMenuItem,
|
|
19265
19719
|
{
|
|
19266
19720
|
role: "menuitemradio",
|
|
@@ -19268,9 +19722,9 @@ function CardStyleMenu({
|
|
|
19268
19722
|
onSelect: () => onChange(style.value),
|
|
19269
19723
|
className: cn("grid grid-cols-[18px_1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
|
|
19270
19724
|
children: [
|
|
19271
|
-
/* @__PURE__ */ (0,
|
|
19272
|
-
/* @__PURE__ */ (0,
|
|
19273
|
-
selected && /* @__PURE__ */ (0,
|
|
19725
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StyleSwatch, { style: style.value }),
|
|
19726
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: style.label }),
|
|
19727
|
+
selected && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Check, { size: 16, className: "text-bridge-primary" })
|
|
19274
19728
|
]
|
|
19275
19729
|
},
|
|
19276
19730
|
style.value
|
|
@@ -19300,7 +19754,7 @@ function PricingCardToolbar({
|
|
|
19300
19754
|
const style = pricingCardStyle(entry.tree, index);
|
|
19301
19755
|
const featuredTag = pricingCardFeaturedTag(entry.tree, index);
|
|
19302
19756
|
const mobile = window.innerWidth <= 480;
|
|
19303
|
-
return /* @__PURE__ */ (0,
|
|
19757
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
19304
19758
|
"div",
|
|
19305
19759
|
{
|
|
19306
19760
|
"data-ohw-bridge": "",
|
|
@@ -19312,7 +19766,7 @@ function PricingCardToolbar({
|
|
|
19312
19766
|
),
|
|
19313
19767
|
style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
|
|
19314
19768
|
children: [
|
|
19315
|
-
/* @__PURE__ */ (0,
|
|
19769
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
19316
19770
|
"div",
|
|
19317
19771
|
{
|
|
19318
19772
|
className: cn(
|
|
@@ -19321,8 +19775,8 @@ function PricingCardToolbar({
|
|
|
19321
19775
|
mobile ? "flex-wrap justify-center" : "flex-nowrap"
|
|
19322
19776
|
),
|
|
19323
19777
|
children: [
|
|
19324
|
-
/* @__PURE__ */ (0,
|
|
19325
|
-
/* @__PURE__ */ (0,
|
|
19778
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
|
|
19779
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
19326
19780
|
CustomToolbarButton,
|
|
19327
19781
|
{
|
|
19328
19782
|
type: "button",
|
|
@@ -19330,8 +19784,8 @@ function PricingCardToolbar({
|
|
|
19330
19784
|
"aria-pressed": featuredTag,
|
|
19331
19785
|
onClick: () => onSetFeaturedTag(!featuredTag),
|
|
19332
19786
|
className: "group size-8",
|
|
19333
|
-
children: /* @__PURE__ */ (0,
|
|
19334
|
-
|
|
19787
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
19788
|
+
import_lucide_react10.Star,
|
|
19335
19789
|
{
|
|
19336
19790
|
size: 16,
|
|
19337
19791
|
fill: "currentColor",
|
|
@@ -19345,25 +19799,25 @@ function PricingCardToolbar({
|
|
|
19345
19799
|
)
|
|
19346
19800
|
}
|
|
19347
19801
|
) }),
|
|
19348
|
-
/* @__PURE__ */ (0,
|
|
19802
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
|
|
19349
19803
|
] }),
|
|
19350
|
-
/* @__PURE__ */ (0,
|
|
19351
|
-
/* @__PURE__ */ (0,
|
|
19352
|
-
/* @__PURE__ */ (0,
|
|
19353
|
-
/* @__PURE__ */ (0,
|
|
19354
|
-
/* @__PURE__ */ (0,
|
|
19355
|
-
/* @__PURE__ */ (0,
|
|
19356
|
-
/* @__PURE__ */ (0,
|
|
19804
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
|
|
19805
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
|
|
19806
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
|
|
19807
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ButtonGlyph, {}) }),
|
|
19808
|
+
/* @__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: [
|
|
19809
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ButtonGlyph, {}),
|
|
19810
|
+
/* @__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
19811
|
] }) }),
|
|
19358
|
-
/* @__PURE__ */ (0,
|
|
19359
|
-
/* @__PURE__ */ (0,
|
|
19360
|
-
/* @__PURE__ */ (0,
|
|
19361
|
-
/* @__PURE__ */ (0,
|
|
19362
|
-
/* @__PURE__ */ (0,
|
|
19812
|
+
/* @__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" }) }),
|
|
19813
|
+
/* @__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" }) }),
|
|
19814
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
|
|
19815
|
+
/* @__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" } }) }),
|
|
19816
|
+
/* @__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
19817
|
]
|
|
19364
19818
|
}
|
|
19365
19819
|
),
|
|
19366
|
-
error && /* @__PURE__ */ (0,
|
|
19820
|
+
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
19821
|
]
|
|
19368
19822
|
}
|
|
19369
19823
|
) });
|
|
@@ -19397,7 +19851,7 @@ function AiPricingCardOverlay({
|
|
|
19397
19851
|
const overEditable = Boolean(t?.closest?.("[data-ohw-editable], [data-ohw-role], a, button"));
|
|
19398
19852
|
setHover((prev) => {
|
|
19399
19853
|
const next = ref && !overEditable ? ref : null;
|
|
19400
|
-
return
|
|
19854
|
+
return sameRef2(prev, next) ? prev : next;
|
|
19401
19855
|
});
|
|
19402
19856
|
};
|
|
19403
19857
|
const onLeave = () => setHover(null);
|
|
@@ -19412,10 +19866,10 @@ function AiPricingCardOverlay({
|
|
|
19412
19866
|
const onClick = (e) => {
|
|
19413
19867
|
const t = e.target;
|
|
19414
19868
|
if (!(t instanceof Element)) return;
|
|
19415
|
-
if (t.closest(
|
|
19869
|
+
if (t.closest(CHROME_SELECTOR2)) return;
|
|
19416
19870
|
const ref = cardRefFromTarget(t);
|
|
19417
|
-
setActive((prev) =>
|
|
19418
|
-
if (!ref || !
|
|
19871
|
+
setActive((prev) => sameRef2(prev, ref) ? prev : ref);
|
|
19872
|
+
if (!ref || !sameRef2(activeRef.current, ref)) setError(null);
|
|
19419
19873
|
};
|
|
19420
19874
|
document.addEventListener("click", onClick, true);
|
|
19421
19875
|
return () => document.removeEventListener("click", onClick, true);
|
|
@@ -19480,12 +19934,12 @@ function AiPricingCardOverlay({
|
|
|
19480
19934
|
const activeEntry = active ? findEntry(active.sectionId) : null;
|
|
19481
19935
|
const activeCardRect = useLiveCardRect(active);
|
|
19482
19936
|
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,
|
|
19937
|
+
const hoverRect = useLiveCardRect(hover && !sameRef2(hover, active) ? hover : null);
|
|
19938
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
|
|
19939
|
+
hoverRect && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
|
|
19940
|
+
activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
|
|
19941
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
|
|
19942
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
19489
19943
|
PricingCardToolbar,
|
|
19490
19944
|
{
|
|
19491
19945
|
entry: activeEntry,
|
|
@@ -19513,7 +19967,7 @@ function AiPricingCardOverlay({
|
|
|
19513
19967
|
onRemoveFeature: () => runMutation(active.sectionId, (tree) => removePricingCardFeature(tree, active.index))
|
|
19514
19968
|
}
|
|
19515
19969
|
) }),
|
|
19516
|
-
/* @__PURE__ */ (0,
|
|
19970
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
19517
19971
|
"div",
|
|
19518
19972
|
{
|
|
19519
19973
|
"data-ohw-bridge": "",
|
|
@@ -19535,7 +19989,7 @@ function AiPricingCardOverlay({
|
|
|
19535
19989
|
|
|
19536
19990
|
// src/ui/ai-section/AiVideoOverlay.tsx
|
|
19537
19991
|
var import_react12 = require("react");
|
|
19538
|
-
var
|
|
19992
|
+
var import_lucide_react11 = require("lucide-react");
|
|
19539
19993
|
|
|
19540
19994
|
// src/lib/ai-video-tree.ts
|
|
19541
19995
|
function resolveVideoBlock(tree, path) {
|
|
@@ -19606,10 +20060,10 @@ function extractYouTubeId(input) {
|
|
|
19606
20060
|
}
|
|
19607
20061
|
|
|
19608
20062
|
// src/ui/ai-section/AiVideoOverlay.tsx
|
|
19609
|
-
var
|
|
20063
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
19610
20064
|
var PRIMARY5 = "#0885FE";
|
|
19611
|
-
var
|
|
19612
|
-
var
|
|
20065
|
+
var OHW_BLUE = `var(--ohw-primary, ${PRIMARY5})`;
|
|
20066
|
+
var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
|
|
19613
20067
|
function findVideoBlockElement(sectionId, path) {
|
|
19614
20068
|
const sectionEl = findByInstanceId(sectionId);
|
|
19615
20069
|
if (!sectionEl) return null;
|
|
@@ -19669,12 +20123,12 @@ function requestValidateTree3(postToParent2, tree) {
|
|
|
19669
20123
|
postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
|
|
19670
20124
|
});
|
|
19671
20125
|
}
|
|
19672
|
-
function
|
|
20126
|
+
function Pill({
|
|
19673
20127
|
label,
|
|
19674
20128
|
onClick,
|
|
19675
20129
|
disabled
|
|
19676
20130
|
}) {
|
|
19677
|
-
return /* @__PURE__ */ (0,
|
|
20131
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
19678
20132
|
"button",
|
|
19679
20133
|
{
|
|
19680
20134
|
type: "button",
|
|
@@ -19690,7 +20144,7 @@ function Pill2({
|
|
|
19690
20144
|
border: "none",
|
|
19691
20145
|
cursor: disabled ? "default" : "pointer",
|
|
19692
20146
|
background: "#ffffff",
|
|
19693
|
-
color:
|
|
20147
|
+
color: OHW_BLUE_DARK,
|
|
19694
20148
|
opacity: disabled ? 0.4 : 1,
|
|
19695
20149
|
fontSize: 12,
|
|
19696
20150
|
lineHeight: "16px",
|
|
@@ -19733,8 +20187,8 @@ function VideoUrlInput({ value, onCommit }) {
|
|
|
19733
20187
|
onCommit(id);
|
|
19734
20188
|
}
|
|
19735
20189
|
};
|
|
19736
|
-
return /* @__PURE__ */ (0,
|
|
19737
|
-
/* @__PURE__ */ (0,
|
|
20190
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
|
|
20191
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
19738
20192
|
"div",
|
|
19739
20193
|
{
|
|
19740
20194
|
style: {
|
|
@@ -19750,12 +20204,12 @@ function VideoUrlInput({ value, onCommit }) {
|
|
|
19750
20204
|
boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
|
|
19751
20205
|
},
|
|
19752
20206
|
children: [
|
|
19753
|
-
/* @__PURE__ */ (0,
|
|
20207
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
19754
20208
|
"span",
|
|
19755
20209
|
{
|
|
19756
20210
|
style: {
|
|
19757
20211
|
flex: "0 0 auto",
|
|
19758
|
-
color:
|
|
20212
|
+
color: OHW_BLUE_DARK,
|
|
19759
20213
|
fontSize: 10,
|
|
19760
20214
|
lineHeight: "16px",
|
|
19761
20215
|
fontWeight: 700,
|
|
@@ -19765,7 +20219,7 @@ function VideoUrlInput({ value, onCommit }) {
|
|
|
19765
20219
|
children: "Video"
|
|
19766
20220
|
}
|
|
19767
20221
|
),
|
|
19768
|
-
/* @__PURE__ */ (0,
|
|
20222
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
19769
20223
|
"input",
|
|
19770
20224
|
{
|
|
19771
20225
|
type: "text",
|
|
@@ -19802,7 +20256,7 @@ function VideoUrlInput({ value, onCommit }) {
|
|
|
19802
20256
|
]
|
|
19803
20257
|
}
|
|
19804
20258
|
),
|
|
19805
|
-
invalid && /* @__PURE__ */ (0,
|
|
20259
|
+
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
20260
|
] });
|
|
19807
20261
|
}
|
|
19808
20262
|
function VideoToolbar({
|
|
@@ -19812,7 +20266,7 @@ function VideoToolbar({
|
|
|
19812
20266
|
onSetVideoId,
|
|
19813
20267
|
onRemove
|
|
19814
20268
|
}) {
|
|
19815
|
-
return /* @__PURE__ */ (0,
|
|
20269
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
19816
20270
|
"div",
|
|
19817
20271
|
{
|
|
19818
20272
|
"data-ohw-bridge": "",
|
|
@@ -19824,7 +20278,7 @@ function VideoToolbar({
|
|
|
19824
20278
|
gap: 6,
|
|
19825
20279
|
alignItems: "center",
|
|
19826
20280
|
width: 280,
|
|
19827
|
-
background:
|
|
20281
|
+
background: OHW_BLUE,
|
|
19828
20282
|
padding: 8,
|
|
19829
20283
|
borderRadius: 12,
|
|
19830
20284
|
boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
|
|
@@ -19832,9 +20286,9 @@ function VideoToolbar({
|
|
|
19832
20286
|
opacity: pending ? 0.6 : 1
|
|
19833
20287
|
},
|
|
19834
20288
|
children: [
|
|
19835
|
-
/* @__PURE__ */ (0,
|
|
19836
|
-
/* @__PURE__ */ (0,
|
|
19837
|
-
error && /* @__PURE__ */ (0,
|
|
20289
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
|
|
20290
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Pill, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
|
|
20291
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
|
|
19838
20292
|
]
|
|
19839
20293
|
}
|
|
19840
20294
|
);
|
|
@@ -19954,8 +20408,8 @@ function AiVideoOverlay({
|
|
|
19954
20408
|
const activeBlockRect = useLiveBlockRect(active);
|
|
19955
20409
|
const TOOLBAR_APPROX_H = 90;
|
|
19956
20410
|
const toolbarState = activeBlockRect && activeBlockRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
|
|
19957
|
-
return /* @__PURE__ */ (0,
|
|
19958
|
-
hoverHandleRect && hover && /* @__PURE__ */ (0,
|
|
20411
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
20412
|
+
hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
19959
20413
|
"button",
|
|
19960
20414
|
{
|
|
19961
20415
|
type: "button",
|
|
@@ -19983,15 +20437,15 @@ function AiVideoOverlay({
|
|
|
19983
20437
|
fontSize: 14,
|
|
19984
20438
|
fontWeight: 700
|
|
19985
20439
|
},
|
|
19986
|
-
children: /* @__PURE__ */ (0,
|
|
20440
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
|
|
19987
20441
|
}
|
|
19988
20442
|
),
|
|
19989
|
-
activeBlockRect && active && activeEntry && /* @__PURE__ */ (0,
|
|
20443
|
+
activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
19990
20444
|
ItemInteractionLayer,
|
|
19991
20445
|
{
|
|
19992
20446
|
rect: activeBlockRect,
|
|
19993
20447
|
state: toolbarState,
|
|
19994
|
-
toolbar: /* @__PURE__ */ (0,
|
|
20448
|
+
toolbar: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
19995
20449
|
VideoToolbar,
|
|
19996
20450
|
{
|
|
19997
20451
|
videoId: videoIdAt(activeEntry.tree, active.path),
|
|
@@ -20015,7 +20469,7 @@ function AiVideoOverlay({
|
|
|
20015
20469
|
|
|
20016
20470
|
// src/ui/ai-section/AiFadeCarouselOverlay.tsx
|
|
20017
20471
|
var import_react13 = require("react");
|
|
20018
|
-
var
|
|
20472
|
+
var import_lucide_react12 = require("lucide-react");
|
|
20019
20473
|
|
|
20020
20474
|
// src/lib/ai-carousel-tree.ts
|
|
20021
20475
|
function isCarouselBlock(node) {
|
|
@@ -20066,10 +20520,10 @@ function setCarouselAutoplay(tree, on) {
|
|
|
20066
20520
|
}
|
|
20067
20521
|
|
|
20068
20522
|
// src/ui/ai-section/AiFadeCarouselOverlay.tsx
|
|
20069
|
-
var
|
|
20523
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
20070
20524
|
var PRIMARY6 = "#0885FE";
|
|
20071
|
-
var
|
|
20072
|
-
var
|
|
20525
|
+
var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY6})`;
|
|
20526
|
+
var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
|
|
20073
20527
|
function findCarouselItemElement(sectionId, index) {
|
|
20074
20528
|
const sectionEl = findByInstanceId(sectionId);
|
|
20075
20529
|
if (!sectionEl) return null;
|
|
@@ -20148,8 +20602,8 @@ function requestAssetPick2(postToParent2) {
|
|
|
20148
20602
|
postToParent2({ type: "ow:asset-pick", id });
|
|
20149
20603
|
});
|
|
20150
20604
|
}
|
|
20151
|
-
function
|
|
20152
|
-
return /* @__PURE__ */ (0,
|
|
20605
|
+
function Pill2({ label, onClick, disabled }) {
|
|
20606
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20153
20607
|
"button",
|
|
20154
20608
|
{
|
|
20155
20609
|
type: "button",
|
|
@@ -20165,7 +20619,7 @@ function Pill3({ label, onClick, disabled }) {
|
|
|
20165
20619
|
border: "none",
|
|
20166
20620
|
cursor: disabled ? "default" : "pointer",
|
|
20167
20621
|
background: "#ffffff",
|
|
20168
|
-
color:
|
|
20622
|
+
color: OHW_BLUE_DARK2,
|
|
20169
20623
|
opacity: disabled ? 0.4 : 1,
|
|
20170
20624
|
fontSize: 12,
|
|
20171
20625
|
lineHeight: "16px",
|
|
@@ -20183,7 +20637,7 @@ function TogglePill({
|
|
|
20183
20637
|
onToggle,
|
|
20184
20638
|
disabled
|
|
20185
20639
|
}) {
|
|
20186
|
-
return /* @__PURE__ */ (0,
|
|
20640
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
20187
20641
|
"button",
|
|
20188
20642
|
{
|
|
20189
20643
|
type: "button",
|
|
@@ -20202,7 +20656,7 @@ function TogglePill({
|
|
|
20202
20656
|
border: "none",
|
|
20203
20657
|
cursor: disabled ? "default" : "pointer",
|
|
20204
20658
|
background: "#ffffff",
|
|
20205
|
-
color:
|
|
20659
|
+
color: OHW_BLUE_DARK2,
|
|
20206
20660
|
opacity: disabled ? 0.4 : 1,
|
|
20207
20661
|
fontSize: 12,
|
|
20208
20662
|
lineHeight: "16px",
|
|
@@ -20212,7 +20666,7 @@ function TogglePill({
|
|
|
20212
20666
|
},
|
|
20213
20667
|
children: [
|
|
20214
20668
|
label,
|
|
20215
|
-
/* @__PURE__ */ (0,
|
|
20669
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20216
20670
|
"span",
|
|
20217
20671
|
{
|
|
20218
20672
|
"aria-hidden": true,
|
|
@@ -20225,10 +20679,10 @@ function TogglePill({
|
|
|
20225
20679
|
// Off is a grey track, not a washed-out blue: the pill's own white background already
|
|
20226
20680
|
// carries the brand colour in the text, and a pale blue track next to it read as
|
|
20227
20681
|
// "disabled control" rather than "switch that is off".
|
|
20228
|
-
background: on ?
|
|
20682
|
+
background: on ? OHW_BLUE_DARK2 : "#CBD5E1",
|
|
20229
20683
|
transition: "background 120ms ease"
|
|
20230
20684
|
},
|
|
20231
|
-
children: /* @__PURE__ */ (0,
|
|
20685
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20232
20686
|
"span",
|
|
20233
20687
|
{
|
|
20234
20688
|
style: {
|
|
@@ -20257,7 +20711,7 @@ function CarouselSlideToolbar({
|
|
|
20257
20711
|
onChangeImage,
|
|
20258
20712
|
onToggleAutoplay
|
|
20259
20713
|
}) {
|
|
20260
|
-
return /* @__PURE__ */ (0,
|
|
20714
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
20261
20715
|
"div",
|
|
20262
20716
|
{
|
|
20263
20717
|
"data-ohw-bridge": "",
|
|
@@ -20268,7 +20722,7 @@ function CarouselSlideToolbar({
|
|
|
20268
20722
|
flexDirection: "column",
|
|
20269
20723
|
gap: 6,
|
|
20270
20724
|
alignItems: "center",
|
|
20271
|
-
background:
|
|
20725
|
+
background: OHW_BLUE2,
|
|
20272
20726
|
padding: 8,
|
|
20273
20727
|
borderRadius: 12,
|
|
20274
20728
|
boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
|
|
@@ -20280,11 +20734,11 @@ function CarouselSlideToolbar({
|
|
|
20280
20734
|
pointerEvents: pending ? "none" : "auto"
|
|
20281
20735
|
},
|
|
20282
20736
|
children: [
|
|
20283
|
-
/* @__PURE__ */ (0,
|
|
20284
|
-
/* @__PURE__ */ (0,
|
|
20285
|
-
/* @__PURE__ */ (0,
|
|
20737
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
|
|
20738
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Pill2, { label: "Change Image", onClick: onChangeImage }),
|
|
20739
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
|
|
20286
20740
|
] }),
|
|
20287
|
-
error && /* @__PURE__ */ (0,
|
|
20741
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
|
|
20288
20742
|
]
|
|
20289
20743
|
}
|
|
20290
20744
|
);
|
|
@@ -20424,8 +20878,8 @@ function AiFadeCarouselOverlay({
|
|
|
20424
20878
|
const activeItemRect = useLiveItemRect2(active);
|
|
20425
20879
|
const TOOLBAR_APPROX_H = 90;
|
|
20426
20880
|
const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
|
|
20427
|
-
return /* @__PURE__ */ (0,
|
|
20428
|
-
hoverHandleRect && hover && /* @__PURE__ */ (0,
|
|
20881
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
|
|
20882
|
+
hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20429
20883
|
"button",
|
|
20430
20884
|
{
|
|
20431
20885
|
type: "button",
|
|
@@ -20454,10 +20908,10 @@ function AiFadeCarouselOverlay({
|
|
|
20454
20908
|
fontSize: 14,
|
|
20455
20909
|
fontWeight: 700
|
|
20456
20910
|
},
|
|
20457
|
-
children: /* @__PURE__ */ (0,
|
|
20911
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.Star, { size: 14, style: { pointerEvents: "none" } })
|
|
20458
20912
|
}
|
|
20459
20913
|
),
|
|
20460
|
-
swapping && /* @__PURE__ */ (0,
|
|
20914
|
+
swapping && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20461
20915
|
"div",
|
|
20462
20916
|
{
|
|
20463
20917
|
ref: skeletonRef,
|
|
@@ -20475,15 +20929,15 @@ function AiFadeCarouselOverlay({
|
|
|
20475
20929
|
zIndex: 2147483646,
|
|
20476
20930
|
pointerEvents: "none"
|
|
20477
20931
|
},
|
|
20478
|
-
children: /* @__PURE__ */ (0,
|
|
20932
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("style", { children: MEDIA_SKELETON_CSS })
|
|
20479
20933
|
}
|
|
20480
20934
|
),
|
|
20481
|
-
activeItemRect && active && activeEntry && /* @__PURE__ */ (0,
|
|
20935
|
+
activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20482
20936
|
ItemInteractionLayer,
|
|
20483
20937
|
{
|
|
20484
20938
|
rect: activeItemRect,
|
|
20485
20939
|
state: toolbarState,
|
|
20486
|
-
toolbar: /* @__PURE__ */ (0,
|
|
20940
|
+
toolbar: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
20487
20941
|
CarouselSlideToolbar,
|
|
20488
20942
|
{
|
|
20489
20943
|
pending,
|
|
@@ -20576,165 +21030,30 @@ function findButtonAtPoint(x, y, media) {
|
|
|
20576
21030
|
if (hits.length === 0) return null;
|
|
20577
21031
|
return hits.reduce((best, el) => best.contains(el) ? el : best);
|
|
20578
21032
|
}
|
|
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
|
-
};
|
|
21033
|
+
function findNavbarCtaAtPoint(x, y) {
|
|
21034
|
+
const hits = Array.from(
|
|
21035
|
+
document.querySelectorAll('[data-ohw-role="navbar-button"]')
|
|
21036
|
+
).filter((el) => containsPoint(el, x, y) && !el.closest("[data-ohw-bridge]"));
|
|
21037
|
+
if (hits.length === 0) return null;
|
|
21038
|
+
return hits.reduce((best, el) => best.contains(el) ? el : best);
|
|
21039
|
+
}
|
|
21040
|
+
|
|
21041
|
+
// src/lib/session-search.ts
|
|
21042
|
+
var STORAGE_KEY = "ohw-preserved-search";
|
|
21043
|
+
function readPreservedSearch() {
|
|
21044
|
+
if (typeof window === "undefined") return "";
|
|
21045
|
+
const fromUrl = window.location.search;
|
|
21046
|
+
if (fromUrl && fromUrl !== "?") {
|
|
21047
|
+
sessionStorage.setItem(STORAGE_KEY, fromUrl);
|
|
21048
|
+
return fromUrl;
|
|
20732
21049
|
}
|
|
20733
|
-
return
|
|
21050
|
+
return sessionStorage.getItem(STORAGE_KEY) ?? "";
|
|
20734
21051
|
}
|
|
20735
|
-
function
|
|
20736
|
-
const
|
|
20737
|
-
|
|
21052
|
+
function isEditSessionActive() {
|
|
21053
|
+
const search = readPreservedSearch();
|
|
21054
|
+
if (!search || search === "?") return false;
|
|
21055
|
+
const q = search.startsWith("?") ? search.slice(1) : search;
|
|
21056
|
+
return new URLSearchParams(q).get("mode") === "edit";
|
|
20738
21057
|
}
|
|
20739
21058
|
|
|
20740
21059
|
// src/lib/prefetch-sections.ts
|
|
@@ -20836,113 +21155,6 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
|
|
|
20836
21155
|
// src/ui/link-modal/LinkPopover.tsx
|
|
20837
21156
|
var import_react17 = require("react");
|
|
20838
21157
|
|
|
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
21158
|
// src/ui/link-modal/LinkEditorPanel.tsx
|
|
20947
21159
|
var import_lucide_react16 = require("lucide-react");
|
|
20948
21160
|
|
|
@@ -22046,6 +22258,7 @@ function shouldUseDevFixtures() {
|
|
|
22046
22258
|
function getLogoElement(el) {
|
|
22047
22259
|
const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
|
|
22048
22260
|
if (marked) return marked;
|
|
22261
|
+
if (el.closest('[data-ohw-editable="icon"]')) return null;
|
|
22049
22262
|
const root = el.closest("nav, [data-ohw-nav-root], footer");
|
|
22050
22263
|
if (!root) return null;
|
|
22051
22264
|
const anchor = el.closest("a");
|
|
@@ -24626,7 +24839,7 @@ function getNavigationSelectionParent(el) {
|
|
|
24626
24839
|
if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
|
|
24627
24840
|
return resolveFooterLinksContainerFrom(el);
|
|
24628
24841
|
}
|
|
24629
|
-
if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
|
|
24842
|
+
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
24843
|
return getNavigationRoot(el);
|
|
24631
24844
|
}
|
|
24632
24845
|
return null;
|
|
@@ -24848,7 +25061,6 @@ var ICONS = {
|
|
|
24848
25061
|
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
25062
|
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
25063
|
};
|
|
24851
|
-
var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
|
|
24852
25064
|
var SELECTION_CHROME_GAP2 = 4;
|
|
24853
25065
|
var TOOLBAR_STROKE_GAP2 = 4;
|
|
24854
25066
|
var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
|
|
@@ -24946,7 +25158,7 @@ function applyVisibleViewport(el, parentScroll) {
|
|
|
24946
25158
|
el.style.top = `${top}px`;
|
|
24947
25159
|
el.style.height = `${height}px`;
|
|
24948
25160
|
}
|
|
24949
|
-
var
|
|
25161
|
+
var TOOLBAR_EDGE_MARGIN4 = 4;
|
|
24950
25162
|
function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
|
|
24951
25163
|
const isAbove = transform.includes("translateY(-100%)");
|
|
24952
25164
|
let visualTop = isAbove ? top - approxH : top;
|
|
@@ -24973,8 +25185,8 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
|
|
|
24973
25185
|
function clampToolbarLeft(centerX, toolbarWidth) {
|
|
24974
25186
|
const half = toolbarWidth / 2;
|
|
24975
25187
|
return Math.max(
|
|
24976
|
-
|
|
24977
|
-
Math.min(centerX, window.innerWidth -
|
|
25188
|
+
TOOLBAR_EDGE_MARGIN4 + half,
|
|
25189
|
+
Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN4 - half)
|
|
24978
25190
|
);
|
|
24979
25191
|
}
|
|
24980
25192
|
function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
|
|
@@ -25405,6 +25617,34 @@ function OhhwellsBridge() {
|
|
|
25405
25617
|
const [fetchState, setFetchState] = (0, import_react21.useState)("idle");
|
|
25406
25618
|
const [showBranding, setShowBranding] = (0, import_react21.useState)(false);
|
|
25407
25619
|
const autoSaveTimers = (0, import_react21.useRef)(/* @__PURE__ */ new Map());
|
|
25620
|
+
(0, import_react21.useEffect)(() => {
|
|
25621
|
+
stampBridgeVersionOnDocument();
|
|
25622
|
+
}, []);
|
|
25623
|
+
(0, import_react21.useEffect)(() => {
|
|
25624
|
+
if (isEditMode) return;
|
|
25625
|
+
return armLoaderHangBeacon(subdomain, fetchState);
|
|
25626
|
+
}, [isEditMode, subdomain, fetchState]);
|
|
25627
|
+
(0, import_react21.useEffect)(() => {
|
|
25628
|
+
if (isEditMode || !subdomain) return;
|
|
25629
|
+
const onError = (event) => {
|
|
25630
|
+
reportOutageBeacon({
|
|
25631
|
+
subdomain,
|
|
25632
|
+
reason: "page_error",
|
|
25633
|
+
detail: event.message || "window error"
|
|
25634
|
+
});
|
|
25635
|
+
};
|
|
25636
|
+
const onRejection = (event) => {
|
|
25637
|
+
const reason = event.reason;
|
|
25638
|
+
const detail = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "unhandledrejection";
|
|
25639
|
+
reportOutageBeacon({ subdomain, reason: "page_error", detail });
|
|
25640
|
+
};
|
|
25641
|
+
window.addEventListener("error", onError);
|
|
25642
|
+
window.addEventListener("unhandledrejection", onRejection);
|
|
25643
|
+
return () => {
|
|
25644
|
+
window.removeEventListener("error", onError);
|
|
25645
|
+
window.removeEventListener("unhandledrejection", onRejection);
|
|
25646
|
+
};
|
|
25647
|
+
}, [isEditMode, subdomain]);
|
|
25408
25648
|
const activeElRef = (0, import_react21.useRef)(null);
|
|
25409
25649
|
const pointerHeldRef = (0, import_react21.useRef)(false);
|
|
25410
25650
|
const selectedElRef = (0, import_react21.useRef)(null);
|
|
@@ -25826,21 +26066,6 @@ function OhhwellsBridge() {
|
|
|
25826
26066
|
const [isItemDragging, setIsItemDragging] = (0, import_react21.useState)(false);
|
|
25827
26067
|
const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react21.useState)(false);
|
|
25828
26068
|
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
26069
|
const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react21.useState)(null);
|
|
25845
26070
|
const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react21.useState)(null);
|
|
25846
26071
|
const footerDragRef = (0, import_react21.useRef)(null);
|
|
@@ -25866,7 +26091,23 @@ function OhhwellsBridge() {
|
|
|
25866
26091
|
const stylesRef = (0, import_react21.useRef)("");
|
|
25867
26092
|
const appliedDrawerRef = (0, import_react21.useRef)(null);
|
|
25868
26093
|
const pendingDeleteUndoRef = (0, import_react21.useRef)(null);
|
|
26094
|
+
const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
|
|
26095
|
+
const floatingPanelOpenRef = (0, import_react21.useRef)(false);
|
|
26096
|
+
const floatingPanelRef = (0, import_react21.useRef)(null);
|
|
26097
|
+
floatingPanelRef.current = floatingPanel;
|
|
26098
|
+
const setFloatingPanelRef = (0, import_react21.useRef)(setFloatingPanel);
|
|
26099
|
+
const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
|
|
26100
|
+
const buttonCollectionDragRef = (0, import_react21.useRef)(null);
|
|
26101
|
+
const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
|
|
26102
|
+
const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
|
|
26103
|
+
const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
|
|
26104
|
+
const buttonPointerDragRef = (0, import_react21.useRef)(null);
|
|
26105
|
+
const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
|
|
26106
|
+
const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
|
|
26107
|
+
const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
|
|
26108
|
+
const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
|
|
25869
26109
|
const [sitePages, setSitePages] = (0, import_react21.useState)([]);
|
|
26110
|
+
const collectionItemsRef = (0, import_react21.useRef)(/* @__PURE__ */ new Map());
|
|
25870
26111
|
const [sectionsByPath, setSectionsByPath] = (0, import_react21.useState)({});
|
|
25871
26112
|
const sectionsPrefetchGenRef = (0, import_react21.useRef)(0);
|
|
25872
26113
|
const setLinkPopoverRef = (0, import_react21.useRef)(setLinkPopover);
|
|
@@ -25874,7 +26115,18 @@ function OhhwellsBridge() {
|
|
|
25874
26115
|
const linkPopoverOpenRef = (0, import_react21.useRef)(false);
|
|
25875
26116
|
const linkPopoverGraceUntilRef = (0, import_react21.useRef)(0);
|
|
25876
26117
|
setLinkPopoverRef.current = setLinkPopover;
|
|
26118
|
+
setFloatingPanelRef.current = setFloatingPanel;
|
|
25877
26119
|
linkPopoverSessionRef.current = linkPopover;
|
|
26120
|
+
floatingPanelOpenRef.current = Boolean(floatingPanel);
|
|
26121
|
+
(0, import_react21.useEffect)(() => {
|
|
26122
|
+
const syncViewport = () => {
|
|
26123
|
+
const next = window.innerWidth <= 480 ? "mobile" : "desktop";
|
|
26124
|
+
setEditorViewport((prev) => prev === next ? prev : next);
|
|
26125
|
+
};
|
|
26126
|
+
syncViewport();
|
|
26127
|
+
window.addEventListener("resize", syncViewport);
|
|
26128
|
+
return () => window.removeEventListener("resize", syncViewport);
|
|
26129
|
+
}, []);
|
|
25878
26130
|
(0, import_react21.useEffect)(() => {
|
|
25879
26131
|
setSectionOrderPersistHandler((entries) => {
|
|
25880
26132
|
const currentPath = window.location.pathname;
|
|
@@ -26010,10 +26262,12 @@ function OhhwellsBridge() {
|
|
|
26010
26262
|
const onSitePages = (e) => {
|
|
26011
26263
|
if (e.data?.type !== "ow:site-pages" || !Array.isArray(e.data.pages)) return;
|
|
26012
26264
|
if (useFixtures) return;
|
|
26013
|
-
const
|
|
26265
|
+
const incoming = e.data.pages;
|
|
26266
|
+
const mapped = incoming.map((p) => ({
|
|
26014
26267
|
path: p.path,
|
|
26015
26268
|
title: p.title
|
|
26016
26269
|
}));
|
|
26270
|
+
collectionItemsRef.current = indexCollectionItems(incoming);
|
|
26017
26271
|
setSitePages(mapped);
|
|
26018
26272
|
runSectionsPrefetchRef.current(mapped);
|
|
26019
26273
|
};
|
|
@@ -27569,6 +27823,7 @@ function OhhwellsBridge() {
|
|
|
27569
27823
|
applyAiFooterFromContent(content);
|
|
27570
27824
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
27571
27825
|
stylesRef.current = content[STYLE_STORE_KEY];
|
|
27826
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
27572
27827
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
27573
27828
|
}
|
|
27574
27829
|
applyBrandChrome(content);
|
|
@@ -27579,12 +27834,12 @@ function OhhwellsBridge() {
|
|
|
27579
27834
|
if (key === "__ohw_sections") continue;
|
|
27580
27835
|
if (key === AI_SECTIONS_KEY) continue;
|
|
27581
27836
|
if (key === AI_FOOTER_KEY) continue;
|
|
27837
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
27838
|
+
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
27582
27839
|
if (key === BRAND_KIT_KEY) continue;
|
|
27583
27840
|
if (key === STYLE_STORE_KEY) continue;
|
|
27584
27841
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
27585
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
27586
27842
|
if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
|
|
27587
|
-
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
27588
27843
|
if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
|
|
27589
27844
|
continue;
|
|
27590
27845
|
}
|
|
@@ -27661,7 +27916,17 @@ function OhhwellsBridge() {
|
|
|
27661
27916
|
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
27662
27917
|
const initialPath = pathname;
|
|
27663
27918
|
fetchedContentPaths.add(`${subdomain}::${initialPath}`);
|
|
27664
|
-
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) =>
|
|
27919
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => {
|
|
27920
|
+
if (!r2.ok) {
|
|
27921
|
+
reportOutageBeacon({
|
|
27922
|
+
subdomain,
|
|
27923
|
+
reason: "content_fetch_failed",
|
|
27924
|
+
detail: `HTTP ${r2.status}`
|
|
27925
|
+
});
|
|
27926
|
+
return null;
|
|
27927
|
+
}
|
|
27928
|
+
return r2.json();
|
|
27929
|
+
}).then((data) => {
|
|
27665
27930
|
if (cancelled) return;
|
|
27666
27931
|
const content = data?.content ?? {};
|
|
27667
27932
|
const branding = Boolean(data?.showBranding);
|
|
@@ -27670,7 +27935,12 @@ function OhhwellsBridge() {
|
|
|
27670
27935
|
recordOverriddenPaths(subdomain, data?.overriddenPaths);
|
|
27671
27936
|
setShowBranding(branding);
|
|
27672
27937
|
return applyContent(content);
|
|
27673
|
-
}).catch(() => {
|
|
27938
|
+
}).catch((err) => {
|
|
27939
|
+
reportOutageBeacon({
|
|
27940
|
+
subdomain,
|
|
27941
|
+
reason: "content_fetch_failed",
|
|
27942
|
+
detail: err instanceof Error ? err.message : "fetch failed"
|
|
27943
|
+
});
|
|
27674
27944
|
}).finally(() => {
|
|
27675
27945
|
if (!cancelled) setFetchState("done");
|
|
27676
27946
|
});
|
|
@@ -27800,17 +28070,18 @@ function OhhwellsBridge() {
|
|
|
27800
28070
|
}
|
|
27801
28071
|
applyAiFooterFromContent(content);
|
|
27802
28072
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
28073
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
27803
28074
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
27804
28075
|
}
|
|
27805
28076
|
for (const [key, val] of Object.entries(content)) {
|
|
27806
28077
|
if (key === "__ohw_sections") continue;
|
|
27807
28078
|
if (key === AI_SECTIONS_KEY) continue;
|
|
27808
28079
|
if (key === AI_FOOTER_KEY) continue;
|
|
28080
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
28081
|
+
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
27809
28082
|
if (key === BRAND_KIT_KEY) continue;
|
|
27810
28083
|
if (key === STYLE_STORE_KEY) continue;
|
|
27811
28084
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
27812
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
27813
|
-
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
27814
28085
|
if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
|
|
27815
28086
|
continue;
|
|
27816
28087
|
}
|
|
@@ -28077,26 +28348,11 @@ function OhhwellsBridge() {
|
|
|
28077
28348
|
const t2 = setTimeout(measure, 500);
|
|
28078
28349
|
const ro = new ResizeObserver(schedule);
|
|
28079
28350
|
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
28351
|
return () => {
|
|
28094
28352
|
clearTimeout(t1);
|
|
28095
28353
|
clearTimeout(t2);
|
|
28096
28354
|
if (raf != null) cancelAnimationFrame(raf);
|
|
28097
28355
|
ro.disconnect();
|
|
28098
|
-
clearResizeTimers();
|
|
28099
|
-
window.removeEventListener("resize", handleResize);
|
|
28100
28356
|
};
|
|
28101
28357
|
}, [pathname, isEditMode, postToParent2]);
|
|
28102
28358
|
(0, import_react21.useEffect)(() => {
|
|
@@ -28393,6 +28649,13 @@ function OhhwellsBridge() {
|
|
|
28393
28649
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
28394
28650
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
28395
28651
|
if (isInsideLinkEditor(target)) return;
|
|
28652
|
+
if (isInsideFloatingPanel(target)) return;
|
|
28653
|
+
if (target.closest("[data-ohw-form-toolbar]")) return;
|
|
28654
|
+
if (target.closest(
|
|
28655
|
+
'[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
|
|
28656
|
+
)) {
|
|
28657
|
+
return;
|
|
28658
|
+
}
|
|
28396
28659
|
if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
|
|
28397
28660
|
clearMediaSelectionRef.current();
|
|
28398
28661
|
}
|
|
@@ -28416,11 +28679,15 @@ function OhhwellsBridge() {
|
|
|
28416
28679
|
return;
|
|
28417
28680
|
}
|
|
28418
28681
|
}
|
|
28419
|
-
|
|
28420
|
-
if (
|
|
28421
|
-
|
|
28422
|
-
|
|
28423
|
-
|
|
28682
|
+
const collectionItem = matchCollectionItemClick(target, collectionItemsRef.current);
|
|
28683
|
+
if (collectionItem) {
|
|
28684
|
+
e.preventDefault();
|
|
28685
|
+
e.stopPropagation();
|
|
28686
|
+
postToParentRef.current({
|
|
28687
|
+
type: "ow:collection-item-selected",
|
|
28688
|
+
path: collectionItem.path,
|
|
28689
|
+
collection: collectionItem.collection
|
|
28690
|
+
});
|
|
28424
28691
|
return;
|
|
28425
28692
|
}
|
|
28426
28693
|
{
|
|
@@ -28576,9 +28843,13 @@ function OhhwellsBridge() {
|
|
|
28576
28843
|
if (isIconEditable(editable) && !getSocialItem(editable)) {
|
|
28577
28844
|
e.preventDefault();
|
|
28578
28845
|
e.stopPropagation();
|
|
28579
|
-
selectIconElementRef.current(editable);
|
|
28580
28846
|
const aiIcon = editable.hasAttribute("data-ai-icon");
|
|
28581
28847
|
const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
|
|
28848
|
+
if (aiIcon) {
|
|
28849
|
+
aiSectionApiRef.current?.selectFromElement(editable);
|
|
28850
|
+
} else {
|
|
28851
|
+
selectIconElementRef.current(editable);
|
|
28852
|
+
}
|
|
28582
28853
|
postToParentRef.current({
|
|
28583
28854
|
type: "ow:icon-pick",
|
|
28584
28855
|
key: editable.dataset.ohwKey ?? "",
|
|
@@ -28806,6 +29077,9 @@ function OhhwellsBridge() {
|
|
|
28806
29077
|
setHoveredItemRect(null);
|
|
28807
29078
|
hoveredNavContainerRef.current = null;
|
|
28808
29079
|
setHoveredNavContainerRect(null);
|
|
29080
|
+
siblingHintElRef.current = null;
|
|
29081
|
+
setSiblingHintRect(null);
|
|
29082
|
+
setSiblingHintRects([]);
|
|
28809
29083
|
return;
|
|
28810
29084
|
}
|
|
28811
29085
|
{
|
|
@@ -28926,7 +29200,6 @@ function OhhwellsBridge() {
|
|
|
28926
29200
|
hoveredNavContainerRef.current = null;
|
|
28927
29201
|
setHoveredNavContainerRect(null);
|
|
28928
29202
|
hoveredItemElRef.current = editable;
|
|
28929
|
-
setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
|
|
28930
29203
|
}
|
|
28931
29204
|
}
|
|
28932
29205
|
}
|
|
@@ -29636,7 +29909,11 @@ function OhhwellsBridge() {
|
|
|
29636
29909
|
};
|
|
29637
29910
|
const handleMouseMove = (e) => {
|
|
29638
29911
|
const { clientX, clientY } = e;
|
|
29639
|
-
if (pointOwnedByFloatingPanel(clientX, clientY))
|
|
29912
|
+
if (pointOwnedByFloatingPanel(clientX, clientY)) {
|
|
29913
|
+
dismissImageHover();
|
|
29914
|
+
clearImageHover();
|
|
29915
|
+
return;
|
|
29916
|
+
}
|
|
29640
29917
|
if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
|
|
29641
29918
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
29642
29919
|
formHoverElRef.current = null;
|
|
@@ -29665,7 +29942,12 @@ function OhhwellsBridge() {
|
|
|
29665
29942
|
setHoveredItemRect(null);
|
|
29666
29943
|
hoveredNavContainerRef.current = null;
|
|
29667
29944
|
setHoveredNavContainerRect(null);
|
|
29945
|
+
siblingHintElRef.current = null;
|
|
29946
|
+
setSiblingHintRect(null);
|
|
29947
|
+
setSiblingHintRects([]);
|
|
29668
29948
|
dismissImageHover();
|
|
29949
|
+
clearImageHover();
|
|
29950
|
+
setSectionGap(null);
|
|
29669
29951
|
return;
|
|
29670
29952
|
}
|
|
29671
29953
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
@@ -29677,7 +29959,11 @@ function OhhwellsBridge() {
|
|
|
29677
29959
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
29678
29960
|
const { clientX, clientY } = e.data;
|
|
29679
29961
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
29680
|
-
if (pointOwnedByFloatingPanel(clientX, clientY))
|
|
29962
|
+
if (pointOwnedByFloatingPanel(clientX, clientY)) {
|
|
29963
|
+
dismissImageHover();
|
|
29964
|
+
clearImageHover();
|
|
29965
|
+
return;
|
|
29966
|
+
}
|
|
29681
29967
|
if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
|
|
29682
29968
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
29683
29969
|
probeSectionGapAt(clientX, clientY);
|
|
@@ -30057,6 +30343,7 @@ function OhhwellsBridge() {
|
|
|
30057
30343
|
applyAiFooterFromContent(content);
|
|
30058
30344
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
30059
30345
|
stylesRef.current = content[STYLE_STORE_KEY];
|
|
30346
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
30060
30347
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
30061
30348
|
}
|
|
30062
30349
|
applyBrandChrome(content);
|
|
@@ -30071,12 +30358,12 @@ function OhhwellsBridge() {
|
|
|
30071
30358
|
}
|
|
30072
30359
|
if (key === AI_SECTIONS_KEY) continue;
|
|
30073
30360
|
if (key === AI_FOOTER_KEY) continue;
|
|
30361
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
30362
|
+
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
30074
30363
|
if (key === BRAND_KIT_KEY) continue;
|
|
30075
30364
|
if (key === STYLE_STORE_KEY) continue;
|
|
30076
30365
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
30077
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
30078
30366
|
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
30367
|
if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
|
|
30081
30368
|
continue;
|
|
30082
30369
|
}
|
|
@@ -30712,12 +30999,24 @@ function OhhwellsBridge() {
|
|
|
30712
30999
|
closeLinkPopoverRef.current();
|
|
30713
31000
|
return;
|
|
30714
31001
|
}
|
|
31002
|
+
if (floatingPanelOpenRef.current) {
|
|
31003
|
+
setFloatingPanelRef.current(null);
|
|
31004
|
+
deselectRef.current();
|
|
31005
|
+
deactivateRef.current();
|
|
31006
|
+
return;
|
|
31007
|
+
}
|
|
30715
31008
|
deselectRef.current();
|
|
30716
31009
|
deactivateRef.current();
|
|
30717
31010
|
clearMediaSelectionRef.current();
|
|
30718
31011
|
clearIconSelectionRef.current();
|
|
30719
31012
|
};
|
|
30720
31013
|
window.addEventListener("message", handleDeactivate);
|
|
31014
|
+
const handleSelectNav = (e) => {
|
|
31015
|
+
if (e.data?.type !== "ow:select-nav") return;
|
|
31016
|
+
const navContainer = findSelectableNavContainer();
|
|
31017
|
+
if (navContainer) selectFrameRef.current(navContainer);
|
|
31018
|
+
};
|
|
31019
|
+
window.addEventListener("message", handleSelectNav);
|
|
30721
31020
|
const handleToastAction = (e) => {
|
|
30722
31021
|
if (e.data?.type !== "ow:toast-action") return;
|
|
30723
31022
|
const actionId = typeof e.data.actionId === "string" ? e.data.actionId : null;
|
|
@@ -31534,10 +31833,11 @@ function OhhwellsBridge() {
|
|
|
31534
31833
|
window.removeEventListener("message", handleDeleteSection);
|
|
31535
31834
|
window.removeEventListener("message", handleDuplicateSection);
|
|
31536
31835
|
window.removeEventListener("message", handleDeactivate);
|
|
31537
|
-
|
|
31836
|
+
window.removeEventListener("message", handleSelectNav);
|
|
31538
31837
|
window.removeEventListener("message", handleToastAction);
|
|
31539
31838
|
window.removeEventListener("message", handleFormCount);
|
|
31540
31839
|
window.removeEventListener("message", handleUiEscape);
|
|
31840
|
+
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
31541
31841
|
autoSaveTimers.current.forEach(clearTimeout);
|
|
31542
31842
|
autoSaveTimers.current.clear();
|
|
31543
31843
|
if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
|
|
@@ -31831,7 +32131,7 @@ function OhhwellsBridge() {
|
|
|
31831
32131
|
postToParent2({
|
|
31832
32132
|
type: "ow:ready",
|
|
31833
32133
|
version: "1",
|
|
31834
|
-
bridgeVersion: "0.1.
|
|
32134
|
+
bridgeVersion: "0.1.153-next.415",
|
|
31835
32135
|
path: pathname,
|
|
31836
32136
|
nodes: collectEditableNodes(editContentRef.current),
|
|
31837
32137
|
sections
|
|
@@ -33057,6 +33357,7 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
|
|
|
33057
33357
|
foldAlignIntoTrees,
|
|
33058
33358
|
getEditModeInitialState,
|
|
33059
33359
|
hasMountedContentNodes,
|
|
33360
|
+
indexCollectionItems,
|
|
33060
33361
|
insertFooterHours,
|
|
33061
33362
|
isEditSessionActive,
|
|
33062
33363
|
isPlainLeftClick,
|
|
@@ -33064,6 +33365,7 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
|
|
|
33064
33365
|
isRenderableTree,
|
|
33065
33366
|
isStickySearchParam,
|
|
33066
33367
|
isValidUrl,
|
|
33368
|
+
matchCollectionItemClick,
|
|
33067
33369
|
nextViableSlide,
|
|
33068
33370
|
parseAiFooterState,
|
|
33069
33371
|
parseAiSectionsState,
|