@ohhwells/bridge 0.1.64-next.176 → 0.1.64
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 +1505 -1560
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -18
- package/dist/index.d.ts +6 -18
- package/dist/index.js +1494 -1548
- package/dist/index.js.map +1 -1
- package/dist/styles.css +99 -59
- package/package.json +1 -6
- package/dist/pages.cjs +0 -141
- package/dist/pages.cjs.map +0 -1
- package/dist/pages.d.cts +0 -45
- package/dist/pages.d.ts +0 -45
- package/dist/pages.js +0 -107
- package/dist/pages.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -46,7 +46,6 @@ __export(index_exports, {
|
|
|
46
46
|
DropdownMenuItem: () => DropdownMenuItem,
|
|
47
47
|
DropdownMenuSeparator: () => DropdownMenuSeparator,
|
|
48
48
|
DropdownMenuTrigger: () => DropdownMenuTrigger,
|
|
49
|
-
EmptySection: () => EmptySection,
|
|
50
49
|
ItemActionToolbar: () => ItemActionToolbar,
|
|
51
50
|
ItemInteractionLayer: () => ItemInteractionLayer,
|
|
52
51
|
LinkEditorPanel: () => LinkEditorPanel,
|
|
@@ -170,7 +169,6 @@ function applyTreeToState(state, payload) {
|
|
|
170
169
|
const entry = {
|
|
171
170
|
id: payload.id,
|
|
172
171
|
label: payload.label ?? "Generated section",
|
|
173
|
-
...typeof payload.path === "string" && payload.path ? { path: payload.path } : {},
|
|
174
172
|
afterSection: payload.mode === "insert" && !insertBefore ? payload.targetSectionId ?? null : null,
|
|
175
173
|
...insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
|
|
176
174
|
...payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
|
|
@@ -193,317 +191,6 @@ function deleteSectionFromState(state, sectionId) {
|
|
|
193
191
|
return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
|
|
194
192
|
}
|
|
195
193
|
|
|
196
|
-
// src/lib/brand-chrome.ts
|
|
197
|
-
var BRAND_NAME_KEY = "__ohw_brand_name";
|
|
198
|
-
var BRAND_TITLE_KEY = "__ohw_site_title";
|
|
199
|
-
var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
|
|
200
|
-
var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
|
|
201
|
-
BRAND_NAME_KEY,
|
|
202
|
-
BRAND_TITLE_KEY,
|
|
203
|
-
BRAND_FAVICON_LETTER_KEY
|
|
204
|
-
]);
|
|
205
|
-
function upsertMeta(selector, attr, token, value) {
|
|
206
|
-
let el = document.head.querySelector(selector);
|
|
207
|
-
if (!el) {
|
|
208
|
-
el = document.createElement("meta");
|
|
209
|
-
el.setAttribute(attr, token);
|
|
210
|
-
document.head.appendChild(el);
|
|
211
|
-
}
|
|
212
|
-
if (el.getAttribute("content") !== value) el.setAttribute("content", value);
|
|
213
|
-
}
|
|
214
|
-
function escapeXml(value) {
|
|
215
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
216
|
-
}
|
|
217
|
-
function applyLetterFavicon(letter) {
|
|
218
|
-
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="12" fill="#111827"/><text x="32" y="46" font-family="system-ui,-apple-system,Segoe UI,sans-serif" font-size="40" font-weight="700" text-anchor="middle" fill="#ffffff">${escapeXml(letter)}</text></svg>`;
|
|
219
|
-
const href = `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
|
220
|
-
let link = document.head.querySelector('link[rel="icon"]');
|
|
221
|
-
if (!link) {
|
|
222
|
-
link = document.createElement("link");
|
|
223
|
-
link.rel = "icon";
|
|
224
|
-
document.head.appendChild(link);
|
|
225
|
-
}
|
|
226
|
-
link.type = "image/svg+xml";
|
|
227
|
-
if (link.href !== href) link.href = href;
|
|
228
|
-
}
|
|
229
|
-
function applyBrandChrome(content) {
|
|
230
|
-
const name = content[BRAND_NAME_KEY];
|
|
231
|
-
if (typeof name === "string" && name.length > 0) {
|
|
232
|
-
document.querySelectorAll("[data-ohw-wordmark]").forEach((el) => {
|
|
233
|
-
if (el.textContent !== name) el.textContent = name;
|
|
234
|
-
if (el.getAttribute("title") !== name) el.setAttribute("title", name);
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
const title = content[BRAND_TITLE_KEY];
|
|
238
|
-
if (typeof title === "string" && title.length > 0) {
|
|
239
|
-
if (document.title !== title) document.title = title;
|
|
240
|
-
upsertMeta('meta[property="og:title"]', "property", "og:title", title);
|
|
241
|
-
upsertMeta('meta[name="twitter:title"]', "name", "twitter:title", title);
|
|
242
|
-
}
|
|
243
|
-
const letter = content[BRAND_FAVICON_LETTER_KEY];
|
|
244
|
-
if (typeof letter === "string" && letter.length > 0) {
|
|
245
|
-
applyLetterFavicon(letter);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// src/lib/brand-kit.ts
|
|
250
|
-
var BRAND_KIT_KEY = "__ohw_brand";
|
|
251
|
-
var BRAND_VAR_PREFIX = "--ohw-brand-";
|
|
252
|
-
var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
|
|
253
|
-
(role) => `${BRAND_VAR_PREFIX}${role}`
|
|
254
|
-
);
|
|
255
|
-
var FONT_VARS = {
|
|
256
|
-
heading: ["--font-heading", "--font-display", "--brand-font-heading"],
|
|
257
|
-
body: ["--font-body", "--brand-font-body"]
|
|
258
|
-
};
|
|
259
|
-
var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
|
|
260
|
-
function brandColorVars(kit) {
|
|
261
|
-
const { dark, primary, accent, light } = kit.palette;
|
|
262
|
-
const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
|
|
263
|
-
return {
|
|
264
|
-
[`${BRAND_VAR_PREFIX}primary`]: primary,
|
|
265
|
-
[`${BRAND_VAR_PREFIX}accent`]: accent,
|
|
266
|
-
[`${BRAND_VAR_PREFIX}light`]: light,
|
|
267
|
-
[`${BRAND_VAR_PREFIX}dark`]: dark,
|
|
268
|
-
[`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
|
|
269
|
-
[`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
|
|
270
|
-
[`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
function parseBrandKit(raw) {
|
|
274
|
-
if (!raw) return null;
|
|
275
|
-
try {
|
|
276
|
-
const parsed = JSON.parse(raw);
|
|
277
|
-
const p = parsed?.palette;
|
|
278
|
-
const f = parsed?.fonts;
|
|
279
|
-
if (!p || !f || typeof p.dark !== "string" || typeof p.primary !== "string" || typeof p.accent !== "string" || typeof p.light !== "string" || typeof f.heading !== "string" || typeof f.body !== "string") {
|
|
280
|
-
return null;
|
|
281
|
-
}
|
|
282
|
-
return {
|
|
283
|
-
palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
|
|
284
|
-
fonts: { heading: f.heading, body: f.body }
|
|
285
|
-
};
|
|
286
|
-
} catch {
|
|
287
|
-
return null;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
function familyOf(stack) {
|
|
291
|
-
const first = stack.split(",")[0]?.trim() ?? "";
|
|
292
|
-
return first.replace(/^['"]|['"]$/g, "");
|
|
293
|
-
}
|
|
294
|
-
function loadBrandFonts(families) {
|
|
295
|
-
const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
|
|
296
|
-
if (unique.length === 0) return;
|
|
297
|
-
const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
|
|
298
|
-
const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
|
|
299
|
-
let link = document.getElementById(BRAND_FONT_LINK_ID);
|
|
300
|
-
if (!link) {
|
|
301
|
-
link = document.createElement("link");
|
|
302
|
-
link.id = BRAND_FONT_LINK_ID;
|
|
303
|
-
link.rel = "stylesheet";
|
|
304
|
-
document.head.appendChild(link);
|
|
305
|
-
}
|
|
306
|
-
if (link.href !== href) link.href = href;
|
|
307
|
-
}
|
|
308
|
-
function applyBrandToDom(kit) {
|
|
309
|
-
const root = document.documentElement;
|
|
310
|
-
if (!kit) {
|
|
311
|
-
for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
|
|
312
|
-
for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
|
|
313
|
-
document.getElementById(BRAND_FONT_LINK_ID)?.remove();
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
|
|
317
|
-
for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
|
|
318
|
-
for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
|
|
319
|
-
loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// src/lib/section-styles.ts
|
|
323
|
-
var STYLE_STORE_KEY = "__ohw_styles";
|
|
324
|
-
var STYLE_SHEET_ID = "ohw-section-styles";
|
|
325
|
-
function parseStyleStore(raw) {
|
|
326
|
-
if (!raw) return null;
|
|
327
|
-
try {
|
|
328
|
-
const parsed = JSON.parse(raw);
|
|
329
|
-
if (parsed?.v !== 1) return null;
|
|
330
|
-
return {
|
|
331
|
-
v: 1,
|
|
332
|
-
sections: typeof parsed.sections === "object" && parsed.sections ? parsed.sections : {},
|
|
333
|
-
nodes: typeof parsed.nodes === "object" && parsed.nodes ? parsed.nodes : {}
|
|
334
|
-
};
|
|
335
|
-
} catch {
|
|
336
|
-
return null;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
var BG_VALUES = {
|
|
340
|
-
surface: "color-mix(in srgb, var(--ohw-brand-light, var(--color-light, #ECECEB)) 94%, var(--ohw-brand-dark, var(--color-dark, #0C0A09)))",
|
|
341
|
-
accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
|
|
342
|
-
"accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
|
|
343
|
-
};
|
|
344
|
-
var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
|
|
345
|
-
function styleSheetCss() {
|
|
346
|
-
const rules = [];
|
|
347
|
-
for (const [tone, value] of Object.entries(BG_VALUES)) {
|
|
348
|
-
rules.push(`[data-ohw-style-bg="${tone}"] { background: ${value} !important; }`);
|
|
349
|
-
}
|
|
350
|
-
rules.push(
|
|
351
|
-
`[data-ohw-style-bg="accent"] { color: var(--ohw-brand-light, var(--color-light, #FFFFFF)) !important; }`
|
|
352
|
-
);
|
|
353
|
-
rules.push(
|
|
354
|
-
`[data-ohw-style-distribution="space-between"] { display: flex !important; flex-direction: column; justify-content: space-between; }`,
|
|
355
|
-
`[data-ohw-style-distribution="center"] { display: flex !important; flex-direction: column; justify-content: center; }`
|
|
356
|
-
);
|
|
357
|
-
for (const [scale, size] of Object.entries(HEADLINE_SIZES)) {
|
|
358
|
-
rules.push(
|
|
359
|
-
`[data-ohw-style-headline="${scale}"] :is(h1, h2, h3) { font-size: ${size}px !important; line-height: 1.15 !important; }`
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
rules.push(
|
|
363
|
-
`[data-ohw-style-aspect="fill-height"] img { height: 100% !important; aspect-ratio: auto !important; object-fit: cover; }`
|
|
364
|
-
);
|
|
365
|
-
for (const aspect of ["1:1", "4:5", "3:4", "3:2", "16:9", "2:1", "3:1"]) {
|
|
366
|
-
rules.push(
|
|
367
|
-
`[data-ohw-style-aspect="${aspect.replace(":", "-")}"] img { aspect-ratio: ${aspect.replace(":", " / ")} !important; height: auto !important; object-fit: cover; }`
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
const pad = { tight: 40, balanced: 64, airy: 96 };
|
|
371
|
-
for (const [spacing, px] of Object.entries(pad)) {
|
|
372
|
-
rules.push(
|
|
373
|
-
`[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
|
|
374
|
-
);
|
|
375
|
-
}
|
|
376
|
-
return rules.join("\n");
|
|
377
|
-
}
|
|
378
|
-
var STYLE_FONT_LINK_ID = "ohw-style-fonts";
|
|
379
|
-
function loadStyleFonts(families) {
|
|
380
|
-
const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
|
|
381
|
-
const existing = document.getElementById(STYLE_FONT_LINK_ID);
|
|
382
|
-
if (unique.length === 0) {
|
|
383
|
-
existing?.remove();
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
|
|
387
|
-
const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
|
|
388
|
-
let link = existing;
|
|
389
|
-
if (!link) {
|
|
390
|
-
link = document.createElement("link");
|
|
391
|
-
link.id = STYLE_FONT_LINK_ID;
|
|
392
|
-
link.rel = "stylesheet";
|
|
393
|
-
document.head.appendChild(link);
|
|
394
|
-
}
|
|
395
|
-
if (link.href !== href) link.href = href;
|
|
396
|
-
}
|
|
397
|
-
var SECTION_ATTRS = {
|
|
398
|
-
sectionBackground: "data-ohw-style-bg",
|
|
399
|
-
textDistribution: "data-ohw-style-distribution",
|
|
400
|
-
headlineScale: "data-ohw-style-headline",
|
|
401
|
-
imageAspect: "data-ohw-style-aspect",
|
|
402
|
-
spacing: "data-ohw-style-spacing"
|
|
403
|
-
};
|
|
404
|
-
var NODE_WROTE_ATTR = "data-ohw-style-node";
|
|
405
|
-
var NODE_PROPS = ["color", "font-family", "font-size", "background"];
|
|
406
|
-
function saveInline(el, prop) {
|
|
407
|
-
const attr = `data-ohw-style-prev-${prop}`;
|
|
408
|
-
if (!el.hasAttribute(attr)) el.setAttribute(attr, el.style.getPropertyValue(prop));
|
|
409
|
-
}
|
|
410
|
-
function restoreInline(el, prop) {
|
|
411
|
-
const attr = `data-ohw-style-prev-${prop}`;
|
|
412
|
-
if (!el.hasAttribute(attr)) return;
|
|
413
|
-
const prev = el.getAttribute(attr) ?? "";
|
|
414
|
-
if (prev) el.style.setProperty(prop, prev);
|
|
415
|
-
else el.style.removeProperty(prop);
|
|
416
|
-
el.removeAttribute(attr);
|
|
417
|
-
}
|
|
418
|
-
function ensureStyleSheet() {
|
|
419
|
-
let el = document.getElementById(STYLE_SHEET_ID);
|
|
420
|
-
if (!el) {
|
|
421
|
-
el = document.createElement("style");
|
|
422
|
-
el.id = STYLE_SHEET_ID;
|
|
423
|
-
document.head.appendChild(el);
|
|
424
|
-
}
|
|
425
|
-
const css = styleSheetCss();
|
|
426
|
-
if (el.textContent !== css) el.textContent = css;
|
|
427
|
-
}
|
|
428
|
-
function clearSectionAttrs(root) {
|
|
429
|
-
for (const attr of Object.values(SECTION_ATTRS)) {
|
|
430
|
-
for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
|
|
431
|
-
}
|
|
432
|
-
for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
|
|
433
|
-
restoreInline(el, "background");
|
|
434
|
-
el.removeAttribute("data-ohw-style-bgcolor");
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
function clearNodeProps(root) {
|
|
438
|
-
for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
|
|
439
|
-
const h = el;
|
|
440
|
-
for (const prop of NODE_PROPS) restoreInline(h, prop);
|
|
441
|
-
h.removeAttribute(NODE_WROTE_ATTR);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
function buttonSurfaceOf(el) {
|
|
445
|
-
return el.closest("a, button") ?? el;
|
|
446
|
-
}
|
|
447
|
-
function applyStylesToDom(store) {
|
|
448
|
-
ensureStyleSheet();
|
|
449
|
-
clearSectionAttrs(document);
|
|
450
|
-
clearNodeProps(document);
|
|
451
|
-
loadStyleFonts(
|
|
452
|
-
store ? Object.values(store.nodes).flatMap((n) => n.fontFamily ? [n.fontFamily] : []) : []
|
|
453
|
-
);
|
|
454
|
-
if (!store) return;
|
|
455
|
-
for (const [sectionId, override] of Object.entries(store.sections)) {
|
|
456
|
-
const sections = document.querySelectorAll(
|
|
457
|
-
`[data-ohw-section="${CSS.escape(sectionId)}"]`
|
|
458
|
-
);
|
|
459
|
-
for (const section of Array.from(sections)) {
|
|
460
|
-
for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
|
|
461
|
-
const value = override[prop];
|
|
462
|
-
if (value === void 0) continue;
|
|
463
|
-
if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
|
|
464
|
-
section.setAttribute(attr, String(value).replace(":", "-"));
|
|
465
|
-
}
|
|
466
|
-
if (override.sectionBackgroundColor !== void 0) {
|
|
467
|
-
saveInline(section, "background");
|
|
468
|
-
section.style.setProperty("background", override.sectionBackgroundColor, "important");
|
|
469
|
-
section.setAttribute("data-ohw-style-bgcolor", "");
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
for (const [key, override] of Object.entries(store.nodes)) {
|
|
474
|
-
const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
|
|
475
|
-
for (const el of Array.from(nodes)) {
|
|
476
|
-
if (override.color !== void 0) {
|
|
477
|
-
saveInline(el, "color");
|
|
478
|
-
el.style.setProperty("color", override.color, "important");
|
|
479
|
-
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
480
|
-
}
|
|
481
|
-
if (override.fontFamily !== void 0) {
|
|
482
|
-
saveInline(el, "font-family");
|
|
483
|
-
el.style.setProperty("font-family", `'${override.fontFamily}'`, "important");
|
|
484
|
-
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
485
|
-
}
|
|
486
|
-
if (override.fontSize !== void 0) {
|
|
487
|
-
saveInline(el, "font-size");
|
|
488
|
-
el.style.setProperty("font-size", `${override.fontSize}px`, "important");
|
|
489
|
-
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
490
|
-
}
|
|
491
|
-
if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
|
|
492
|
-
const surface = buttonSurfaceOf(el);
|
|
493
|
-
if (override.buttonBackground !== void 0) {
|
|
494
|
-
saveInline(surface, "background");
|
|
495
|
-
surface.style.setProperty("background", override.buttonBackground, "important");
|
|
496
|
-
}
|
|
497
|
-
if (override.buttonText !== void 0) {
|
|
498
|
-
saveInline(surface, "color");
|
|
499
|
-
surface.style.setProperty("color", override.buttonText, "important");
|
|
500
|
-
}
|
|
501
|
-
surface.setAttribute(NODE_WROTE_ATTR, "");
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
194
|
// src/ui/ai-tree/aiSectionsManager.tsx
|
|
508
195
|
var import_react_dom = require("react-dom");
|
|
509
196
|
var import_client = require("react-dom/client");
|
|
@@ -518,8 +205,7 @@ function lucideByName(name) {
|
|
|
518
205
|
}
|
|
519
206
|
var typeStyle = (spec, font) => ({
|
|
520
207
|
fontFamily: font,
|
|
521
|
-
|
|
522
|
-
fontSize: spec.size >= 24 ? `clamp(${Math.max(18, Math.round(spec.size * 0.6))}px, ${(spec.size / 9).toFixed(2)}vw, ${spec.size}px)` : spec.size,
|
|
208
|
+
fontSize: spec.size,
|
|
523
209
|
lineHeight: spec.line,
|
|
524
210
|
fontWeight: spec.weight
|
|
525
211
|
});
|
|
@@ -546,8 +232,6 @@ var AI_RESPONSIVE_CSS = [
|
|
|
546
232
|
" [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
|
|
547
233
|
" [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
|
|
548
234
|
" [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
|
|
549
|
-
" [data-ai-responsive] { overflow-x: hidden; }",
|
|
550
|
-
" [data-ai-responsive] img { max-width: 100%; }",
|
|
551
235
|
"}"
|
|
552
236
|
].join("\n");
|
|
553
237
|
var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
|
@@ -1555,20 +1239,6 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1555
1239
|
const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
|
|
1556
1240
|
const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
|
|
1557
1241
|
const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
|
|
1558
|
-
const toneBackground = (() => {
|
|
1559
|
-
const { dark, primary, light } = resolvedBrand.palette;
|
|
1560
|
-
switch (settings.sectionBackground) {
|
|
1561
|
-
case "surface":
|
|
1562
|
-
return `color-mix(in srgb, ${light} 94%, ${dark})`;
|
|
1563
|
-
case "accent":
|
|
1564
|
-
return primary;
|
|
1565
|
-
case "accent-soft":
|
|
1566
|
-
return `color-mix(in srgb, ${primary} 12%, ${light})`;
|
|
1567
|
-
default:
|
|
1568
|
-
return void 0;
|
|
1569
|
-
}
|
|
1570
|
-
})();
|
|
1571
|
-
const distributed = !isOverlay && settings.textDistribution;
|
|
1572
1242
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1573
1243
|
"section",
|
|
1574
1244
|
{
|
|
@@ -1578,11 +1248,10 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1578
1248
|
style: {
|
|
1579
1249
|
position: "relative",
|
|
1580
1250
|
padding: `${pad}px 0`,
|
|
1581
|
-
background:
|
|
1251
|
+
background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
|
|
1582
1252
|
backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
|
|
1583
1253
|
backgroundSize: "cover",
|
|
1584
|
-
backgroundPosition: "center"
|
|
1585
|
-
color: settings.sectionBackground === "accent" ? resolvedBrand.palette.light : void 0
|
|
1254
|
+
backgroundPosition: "center"
|
|
1586
1255
|
},
|
|
1587
1256
|
children: [
|
|
1588
1257
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
|
|
@@ -1606,24 +1275,10 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
|
|
|
1606
1275
|
display: "grid",
|
|
1607
1276
|
gridTemplateColumns: "repeat(12, 1fr)",
|
|
1608
1277
|
gap: AI_TREE_TOKENS.spacing6,
|
|
1609
|
-
alignItems:
|
|
1278
|
+
alignItems: settings.verticalPosition === "top" ? "start" : "center",
|
|
1610
1279
|
marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
|
|
1611
1280
|
},
|
|
1612
|
-
children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1613
|
-
"div",
|
|
1614
|
-
{
|
|
1615
|
-
"data-ai-cell": "",
|
|
1616
|
-
style: {
|
|
1617
|
-
gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
|
|
1618
|
-
minWidth: 0,
|
|
1619
|
-
// space-between: each column becomes a flex column whose content spreads over
|
|
1620
|
-
// the full row height instead of clumping at the top.
|
|
1621
|
-
...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
|
|
1622
|
-
},
|
|
1623
|
-
children: renderNode(block, ctx, `r${r2}.b${b}`)
|
|
1624
|
-
},
|
|
1625
|
-
b
|
|
1626
|
-
))
|
|
1281
|
+
children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`, minWidth: 0 }, children: renderNode(block, ctx, `r${r2}.b${b}`) }, b))
|
|
1627
1282
|
},
|
|
1628
1283
|
r2
|
|
1629
1284
|
))
|
|
@@ -1639,34 +1294,17 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
|
1639
1294
|
var CONTAINER_ATTR = "data-ohw-ai-generated";
|
|
1640
1295
|
var REPLACED_ATTR = "data-ohw-ai-replaced-by";
|
|
1641
1296
|
var REMOVED_ATTR = "data-ohw-ai-removed";
|
|
1642
|
-
function readRootVar(name) {
|
|
1643
|
-
if (typeof document === "undefined") return "";
|
|
1644
|
-
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
1645
|
-
}
|
|
1646
|
-
function deriveBrandOverride() {
|
|
1647
|
-
const dark = readRootVar("--ohw-brand-dark");
|
|
1648
|
-
const primary = readRootVar("--ohw-brand-primary");
|
|
1649
|
-
const light = readRootVar("--ohw-brand-light");
|
|
1650
|
-
if (!dark || !primary || !light) return null;
|
|
1651
|
-
const accent = readRootVar("--ohw-brand-accent");
|
|
1652
|
-
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
1653
|
-
const body = readRootVar("--font-body");
|
|
1654
|
-
return {
|
|
1655
|
-
palette: { dark, primary, accent: accent || dark, light },
|
|
1656
|
-
fonts: {
|
|
1657
|
-
heading: heading || AI_DEFAULT_BRAND.fonts.heading,
|
|
1658
|
-
body: body || AI_DEFAULT_BRAND.fonts.body
|
|
1659
|
-
}
|
|
1660
|
-
};
|
|
1661
|
-
}
|
|
1662
1297
|
function deriveTemplateBrand() {
|
|
1663
|
-
|
|
1664
|
-
const
|
|
1665
|
-
const
|
|
1298
|
+
if (typeof document === "undefined") return null;
|
|
1299
|
+
const cs = getComputedStyle(document.documentElement);
|
|
1300
|
+
const read = (name) => cs.getPropertyValue(name).trim();
|
|
1301
|
+
const dark = read("--color-dark");
|
|
1302
|
+
const primary = read("--color-primary");
|
|
1303
|
+
const light = read("--color-light");
|
|
1666
1304
|
if (!dark || !primary || !light) return null;
|
|
1667
|
-
const accent =
|
|
1668
|
-
const heading =
|
|
1669
|
-
const body =
|
|
1305
|
+
const accent = read("--color-accent");
|
|
1306
|
+
const heading = read("--font-heading") || read("--font-display");
|
|
1307
|
+
const body = read("--font-body");
|
|
1670
1308
|
return {
|
|
1671
1309
|
palette: { dark, primary, accent: accent || dark, light },
|
|
1672
1310
|
fonts: {
|
|
@@ -1758,12 +1396,8 @@ function syncReplacedOriginals(state) {
|
|
|
1758
1396
|
}
|
|
1759
1397
|
function applyAiSectionsToDom(state, options) {
|
|
1760
1398
|
if (typeof document === "undefined") return;
|
|
1761
|
-
const brandOverride = deriveBrandOverride();
|
|
1762
1399
|
const templateBrand = deriveTemplateBrand();
|
|
1763
|
-
const
|
|
1764
|
-
const pagePath = window.location.pathname;
|
|
1765
|
-
const pageSections = state.sections.filter((entry) => !entry.path || entry.path === pagePath);
|
|
1766
|
-
const activeIds = new Set(pageSections.map((entry) => entry.id));
|
|
1400
|
+
const activeIds = new Set(state.sections.map((entry) => entry.id));
|
|
1767
1401
|
for (const [id, section] of mounted) {
|
|
1768
1402
|
if (!activeIds.has(id)) {
|
|
1769
1403
|
section.root.unmount();
|
|
@@ -1771,8 +1405,8 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1771
1405
|
mounted.delete(id);
|
|
1772
1406
|
}
|
|
1773
1407
|
}
|
|
1774
|
-
for (const entry of
|
|
1775
|
-
const serialized = JSON.stringify(entry)
|
|
1408
|
+
for (const entry of state.sections) {
|
|
1409
|
+
const serialized = JSON.stringify(entry);
|
|
1776
1410
|
const existing = mounted.get(entry.id);
|
|
1777
1411
|
if (existing && existing.serialized === serialized && existing.container.isConnected) {
|
|
1778
1412
|
continue;
|
|
@@ -1786,7 +1420,6 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1786
1420
|
mounted.delete(entry.id);
|
|
1787
1421
|
}
|
|
1788
1422
|
container.setAttribute("data-ohw-section", entry.id);
|
|
1789
|
-
container.setAttribute("data-ohw-instance", entry.id);
|
|
1790
1423
|
container.setAttribute("data-ohw-section-label", entry.label);
|
|
1791
1424
|
placeContainer(container, entry);
|
|
1792
1425
|
const root = mounted.get(entry.id)?.root ?? (0, import_client.createRoot)(container);
|
|
@@ -1797,7 +1430,7 @@ function applyAiSectionsToDom(state, options) {
|
|
|
1797
1430
|
AiTreeRenderer,
|
|
1798
1431
|
{
|
|
1799
1432
|
tree: entry.tree,
|
|
1800
|
-
brand:
|
|
1433
|
+
brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
1801
1434
|
resolveMedia,
|
|
1802
1435
|
editKeyPrefix: `ai.${entry.id}`
|
|
1803
1436
|
}
|
|
@@ -2414,7 +2047,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
2414
2047
|
const autoId = (0, import_react5.useId)();
|
|
2415
2048
|
const insertAfter = insertAfterProp ?? autoId;
|
|
2416
2049
|
const [schedule, setSchedule] = (0, import_react5.useState)(null);
|
|
2417
|
-
const [loading, setLoading] = (0, import_react5.useState)(
|
|
2050
|
+
const [loading, setLoading] = (0, import_react5.useState)(true);
|
|
2418
2051
|
const [inEditor, setInEditor] = (0, import_react5.useState)(false);
|
|
2419
2052
|
const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
|
|
2420
2053
|
const [modalState, setModalState] = (0, import_react5.useState)(null);
|
|
@@ -2588,10 +2221,8 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
|
|
|
2588
2221
|
"*"
|
|
2589
2222
|
);
|
|
2590
2223
|
};
|
|
2224
|
+
if (!inEditor && !loading && !schedule) return null;
|
|
2591
2225
|
const sectionId = `scheduling-${insertAfter}`;
|
|
2592
|
-
if (!inEditor && !loading && !schedule) {
|
|
2593
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
|
|
2594
|
-
}
|
|
2595
2226
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2596
2227
|
"section",
|
|
2597
2228
|
{
|
|
@@ -6589,6 +6220,7 @@ function getChromeStyle(state) {
|
|
|
6589
6220
|
}
|
|
6590
6221
|
function ClampedToolbarSlot({
|
|
6591
6222
|
placement,
|
|
6223
|
+
align = "center",
|
|
6592
6224
|
children
|
|
6593
6225
|
}) {
|
|
6594
6226
|
const slotRef = (0, import_react7.useRef)(null);
|
|
@@ -6610,7 +6242,7 @@ function ClampedToolbarSlot({
|
|
|
6610
6242
|
Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN - half)
|
|
6611
6243
|
);
|
|
6612
6244
|
const offsetX = clampedCenter - centerX;
|
|
6613
|
-
slot.style.transform = `translateX(calc(-50% + ${offsetX}px))`;
|
|
6245
|
+
slot.style.transform = align === "left" ? "none" : `translateX(calc(-50% + ${offsetX}px))`;
|
|
6614
6246
|
};
|
|
6615
6247
|
clamp();
|
|
6616
6248
|
const ro = new ResizeObserver(clamp);
|
|
@@ -6621,19 +6253,20 @@ function ClampedToolbarSlot({
|
|
|
6621
6253
|
ro.disconnect();
|
|
6622
6254
|
window.removeEventListener("resize", clamp);
|
|
6623
6255
|
};
|
|
6624
|
-
}, [placement, children]);
|
|
6256
|
+
}, [placement, children, align]);
|
|
6625
6257
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
6626
6258
|
"div",
|
|
6627
6259
|
{
|
|
6628
6260
|
ref: slotRef,
|
|
6629
6261
|
className: cn(
|
|
6630
|
-
"pointer-events-auto absolute
|
|
6262
|
+
"pointer-events-auto absolute",
|
|
6263
|
+
align === "left" ? "left-0" : "left-1/2",
|
|
6631
6264
|
placement === "top" ? "bottom-full" : "top-full"
|
|
6632
6265
|
),
|
|
6633
6266
|
style: {
|
|
6634
6267
|
marginBottom: placement === "top" ? TOOLBAR_STROKE_GAP : void 0,
|
|
6635
6268
|
marginTop: placement === "bottom" ? TOOLBAR_STROKE_GAP : void 0,
|
|
6636
|
-
transform: "translateX(-50%)"
|
|
6269
|
+
transform: align === "left" ? "none" : "translateX(-50%)"
|
|
6637
6270
|
},
|
|
6638
6271
|
"data-ohw-item-toolbar-anchor": placement,
|
|
6639
6272
|
children
|
|
@@ -6702,6 +6335,7 @@ function ItemInteractionLayer({
|
|
|
6702
6335
|
onItemClick,
|
|
6703
6336
|
itemDragSurface = true,
|
|
6704
6337
|
chromeGap,
|
|
6338
|
+
toolbarAlign = "center",
|
|
6705
6339
|
className
|
|
6706
6340
|
}) {
|
|
6707
6341
|
if (state === "default") return null;
|
|
@@ -6789,8 +6423,8 @@ function ItemInteractionLayer({
|
|
|
6789
6423
|
)
|
|
6790
6424
|
}
|
|
6791
6425
|
),
|
|
6792
|
-
showToolbar && state === "active-top" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ClampedToolbarSlot, { placement: "top", children: toolbar }),
|
|
6793
|
-
showToolbar && state === "active-bottom" && !useDetachedBelowToolbar && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ClampedToolbarSlot, { placement: "bottom", children: toolbar }),
|
|
6426
|
+
showToolbar && state === "active-top" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ClampedToolbarSlot, { align: toolbarAlign, placement: "top", children: toolbar }),
|
|
6427
|
+
showToolbar && state === "active-bottom" && !useDetachedBelowToolbar && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ClampedToolbarSlot, { align: toolbarAlign, placement: "bottom", children: toolbar }),
|
|
6794
6428
|
useDetachedBelowToolbar && toolbarBelowRect ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
6795
6429
|
DetachedBelowToolbarSlot,
|
|
6796
6430
|
{
|
|
@@ -6804,14 +6438,624 @@ function ItemInteractionLayer({
|
|
|
6804
6438
|
);
|
|
6805
6439
|
}
|
|
6806
6440
|
|
|
6441
|
+
// src/lib/forms.ts
|
|
6442
|
+
var FORM_SELECTOR = '[data-ohw-editable="form"]';
|
|
6443
|
+
function getFormElement(el) {
|
|
6444
|
+
if (!el) return null;
|
|
6445
|
+
return el.closest(FORM_SELECTOR);
|
|
6446
|
+
}
|
|
6447
|
+
function formKeyOf(form) {
|
|
6448
|
+
return form.getAttribute("data-ohw-key");
|
|
6449
|
+
}
|
|
6450
|
+
function formHasLongText(form) {
|
|
6451
|
+
return form.querySelector("textarea") !== null;
|
|
6452
|
+
}
|
|
6453
|
+
function collectFormFields(form) {
|
|
6454
|
+
const fields = {};
|
|
6455
|
+
form.querySelectorAll(
|
|
6456
|
+
"input, textarea, select"
|
|
6457
|
+
).forEach((input) => {
|
|
6458
|
+
const type = input.type;
|
|
6459
|
+
if (type === "submit" || type === "button" || type === "reset") return;
|
|
6460
|
+
const key = input.getAttribute("name") ?? input.getAttribute("data-ohw-key");
|
|
6461
|
+
if (!key) return;
|
|
6462
|
+
if (type === "checkbox") {
|
|
6463
|
+
fields[key] = input.checked ? "yes" : "no";
|
|
6464
|
+
return;
|
|
6465
|
+
}
|
|
6466
|
+
fields[key] = input.value;
|
|
6467
|
+
});
|
|
6468
|
+
return fields;
|
|
6469
|
+
}
|
|
6470
|
+
function formStateContainer(form) {
|
|
6471
|
+
return form.closest("[data-ohw-editable-state]") ?? form.parentElement ?? form;
|
|
6472
|
+
}
|
|
6473
|
+
function showSuccess(form, message) {
|
|
6474
|
+
const container = formStateContainer(form);
|
|
6475
|
+
const views = container.querySelectorAll("[data-ohw-state-view]");
|
|
6476
|
+
if (views.length > 0) {
|
|
6477
|
+
views.forEach((view) => {
|
|
6478
|
+
view.style.display = view.getAttribute("data-ohw-state-view") === "success" ? "block" : "none";
|
|
6479
|
+
});
|
|
6480
|
+
return;
|
|
6481
|
+
}
|
|
6482
|
+
const text = message || form.getAttribute("data-ohw-success-text") || DEFAULT_SUCCESS_TEXT;
|
|
6483
|
+
const note = document.createElement("p");
|
|
6484
|
+
note.setAttribute("data-ohw-form-success", "");
|
|
6485
|
+
note.setAttribute("role", "status");
|
|
6486
|
+
note.innerHTML = text;
|
|
6487
|
+
form.replaceChildren(note);
|
|
6488
|
+
}
|
|
6489
|
+
function showSubmitError(form, message = "Something went wrong. Please try again.") {
|
|
6490
|
+
let note = form.querySelector("[data-ohw-form-error]");
|
|
6491
|
+
if (!note) {
|
|
6492
|
+
note = document.createElement("p");
|
|
6493
|
+
note.setAttribute("data-ohw-form-error", "");
|
|
6494
|
+
note.setAttribute("role", "alert");
|
|
6495
|
+
note.style.marginTop = "8px";
|
|
6496
|
+
form.appendChild(note);
|
|
6497
|
+
}
|
|
6498
|
+
note.textContent = message;
|
|
6499
|
+
note.style.display = "";
|
|
6500
|
+
}
|
|
6501
|
+
function clearSubmitError(form) {
|
|
6502
|
+
const note = form.querySelector("[data-ohw-form-error]");
|
|
6503
|
+
if (note) note.style.display = "none";
|
|
6504
|
+
}
|
|
6505
|
+
var SUCCESS_TEXT_ATTR = "data-ohw-form-success-text";
|
|
6506
|
+
var HIDDEN_ATTR = "data-ohw-form-hidden";
|
|
6507
|
+
var DEFAULT_SUCCESS_TEXT = "Thanks! We'll be in touch.";
|
|
6508
|
+
function formSuccessKey(formKey) {
|
|
6509
|
+
return `${formKey}-success`;
|
|
6510
|
+
}
|
|
6511
|
+
function ensureSuccessTextEl(form, formKey, initialText) {
|
|
6512
|
+
let el = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
|
|
6513
|
+
if (el) return el;
|
|
6514
|
+
el = document.createElement("p");
|
|
6515
|
+
el.setAttribute(SUCCESS_TEXT_ATTR, "");
|
|
6516
|
+
el.setAttribute("data-ohw-editable", "text");
|
|
6517
|
+
el.setAttribute("data-ohw-key", formSuccessKey(formKey));
|
|
6518
|
+
el.innerHTML = initialText;
|
|
6519
|
+
el.style.display = "none";
|
|
6520
|
+
el.style.margin = "56px 4px 16px";
|
|
6521
|
+
form.appendChild(el);
|
|
6522
|
+
return el;
|
|
6523
|
+
}
|
|
6524
|
+
function successInitialFor(form, formKey, content) {
|
|
6525
|
+
return content[formSuccessKey(formKey)] || form.getAttribute("data-ohw-success-text") || DEFAULT_SUCCESS_TEXT;
|
|
6526
|
+
}
|
|
6527
|
+
function setFormViewState(form, formKey, state, initialText) {
|
|
6528
|
+
const success = ensureSuccessTextEl(form, formKey, initialText);
|
|
6529
|
+
Array.from(form.children).forEach((child) => {
|
|
6530
|
+
if (!(child instanceof HTMLElement) || child === success) return;
|
|
6531
|
+
if (state === "success") {
|
|
6532
|
+
if (!child.hasAttribute(HIDDEN_ATTR)) {
|
|
6533
|
+
child.setAttribute(HIDDEN_ATTR, child.style.display);
|
|
6534
|
+
child.style.display = "none";
|
|
6535
|
+
}
|
|
6536
|
+
} else if (child.hasAttribute(HIDDEN_ATTR)) {
|
|
6537
|
+
child.style.display = child.getAttribute(HIDDEN_ATTR) ?? "";
|
|
6538
|
+
child.removeAttribute(HIDDEN_ATTR);
|
|
6539
|
+
}
|
|
6540
|
+
});
|
|
6541
|
+
success.style.display = state === "success" ? "" : "none";
|
|
6542
|
+
}
|
|
6543
|
+
var BOUND_ATTR = "data-ohw-form-bound";
|
|
6544
|
+
var publishedFormContext = {
|
|
6545
|
+
apiUrl: "",
|
|
6546
|
+
subdomain: "",
|
|
6547
|
+
content: {}
|
|
6548
|
+
};
|
|
6549
|
+
function bindPublishedForms(apiUrl, subdomain, content = {}) {
|
|
6550
|
+
publishedFormContext = { apiUrl, subdomain, content };
|
|
6551
|
+
if (document.documentElement.hasAttribute(BOUND_ATTR)) return;
|
|
6552
|
+
document.documentElement.setAttribute(BOUND_ATTR, "");
|
|
6553
|
+
document.addEventListener(
|
|
6554
|
+
"submit",
|
|
6555
|
+
async (e) => {
|
|
6556
|
+
const target = e.target;
|
|
6557
|
+
const form = target?.closest(FORM_SELECTOR);
|
|
6558
|
+
if (!form || !(form instanceof HTMLFormElement)) return;
|
|
6559
|
+
const formKey = formKeyOf(form);
|
|
6560
|
+
if (!formKey) return;
|
|
6561
|
+
e.preventDefault();
|
|
6562
|
+
clearSubmitError(form);
|
|
6563
|
+
const filled = collectFormFields(form);
|
|
6564
|
+
if (!Object.values(filled).some((value) => value.trim() !== "")) {
|
|
6565
|
+
showSubmitError(form, "Please fill in at least one field.");
|
|
6566
|
+
return;
|
|
6567
|
+
}
|
|
6568
|
+
const submitButton = form.querySelector(
|
|
6569
|
+
'button[type="submit"], input[type="submit"], button:not([type])'
|
|
6570
|
+
);
|
|
6571
|
+
if (submitButton) submitButton.disabled = true;
|
|
6572
|
+
try {
|
|
6573
|
+
const { apiUrl: api, subdomain: site, content: latest } = publishedFormContext;
|
|
6574
|
+
const response = await fetch(`${api}/api/public/sites/${site}/forms/submissions`, {
|
|
6575
|
+
method: "POST",
|
|
6576
|
+
headers: { "Content-Type": "application/json" },
|
|
6577
|
+
body: JSON.stringify({
|
|
6578
|
+
formKey,
|
|
6579
|
+
fields: filled,
|
|
6580
|
+
hasLongText: formHasLongText(form)
|
|
6581
|
+
})
|
|
6582
|
+
});
|
|
6583
|
+
if (!response.ok) throw new Error(`submit failed: ${response.status}`);
|
|
6584
|
+
showSuccess(form, latest[formSuccessKey(formKey)]);
|
|
6585
|
+
} catch {
|
|
6586
|
+
showSubmitError(form);
|
|
6587
|
+
if (submitButton) submitButton.disabled = false;
|
|
6588
|
+
}
|
|
6589
|
+
},
|
|
6590
|
+
true
|
|
6591
|
+
);
|
|
6592
|
+
}
|
|
6593
|
+
|
|
6594
|
+
// src/lib/form-fields.ts
|
|
6595
|
+
var FIELD_DEFAULTS = {
|
|
6596
|
+
"short-text": { label: "Short text", placeholder: "Type placeholder text..." },
|
|
6597
|
+
"long-text": { label: "Long text", placeholder: "How can we help?" },
|
|
6598
|
+
email: { label: "Email", placeholder: "hello@example.com" },
|
|
6599
|
+
phone: { label: "Phone number", placeholder: "(555) 000-0000" }
|
|
6600
|
+
};
|
|
6601
|
+
var FIELD_TYPES = [
|
|
6602
|
+
{ type: "short-text", label: "Short text" },
|
|
6603
|
+
{ type: "long-text", label: "Long text" },
|
|
6604
|
+
{ type: "email", label: "Email" },
|
|
6605
|
+
{ type: "phone", label: "Phone" }
|
|
6606
|
+
];
|
|
6607
|
+
var FIELD_ATTR = "data-ohw-form-field";
|
|
6608
|
+
var FIELD_TYPE_ATTR = "data-ohw-field-type";
|
|
6609
|
+
var PLACEHOLDER_EDIT_ATTR = "data-ohw-placeholder-edit";
|
|
6610
|
+
function fieldsKey(formKey) {
|
|
6611
|
+
return `${formKey}-fields`;
|
|
6612
|
+
}
|
|
6613
|
+
function inputTagFor(type) {
|
|
6614
|
+
if (type === "long-text") return { tag: "textarea" };
|
|
6615
|
+
if (type === "email") return { tag: "input", inputType: "email" };
|
|
6616
|
+
if (type === "phone") return { tag: "input", inputType: "tel" };
|
|
6617
|
+
return { tag: "input", inputType: "text" };
|
|
6618
|
+
}
|
|
6619
|
+
function inferType(input) {
|
|
6620
|
+
if (input.tagName === "TEXTAREA") return "long-text";
|
|
6621
|
+
const type = input.type;
|
|
6622
|
+
if (type === "email") return "email";
|
|
6623
|
+
if (type === "tel") return "phone";
|
|
6624
|
+
return "short-text";
|
|
6625
|
+
}
|
|
6626
|
+
function ensureFieldLabel(wrapper, key) {
|
|
6627
|
+
const existing = fieldLabelOf(wrapper);
|
|
6628
|
+
if (existing) {
|
|
6629
|
+
if (!existing.hasAttribute("data-ohw-editable")) {
|
|
6630
|
+
existing.setAttribute("data-ohw-editable", "text");
|
|
6631
|
+
existing.setAttribute("data-ohw-key", `${key}-label`);
|
|
6632
|
+
}
|
|
6633
|
+
return existing;
|
|
6634
|
+
}
|
|
6635
|
+
const input = fieldInputOf(wrapper);
|
|
6636
|
+
const label = document.createElement("label");
|
|
6637
|
+
label.setAttribute("data-ohw-editable", "text");
|
|
6638
|
+
label.setAttribute("data-ohw-key", `${key}-label`);
|
|
6639
|
+
label.setAttribute("data-ohw-field-label", "");
|
|
6640
|
+
label.style.display = "block";
|
|
6641
|
+
label.style.marginBottom = "6px";
|
|
6642
|
+
label.style.fontSize = "13px";
|
|
6643
|
+
label.style.fontWeight = "500";
|
|
6644
|
+
label.style.lineHeight = "1.3";
|
|
6645
|
+
const fromPlaceholder = input?.getAttribute("placeholder")?.trim();
|
|
6646
|
+
const text = fromPlaceholder && fromPlaceholder.length < 40 ? fromPlaceholder : key.replace(/[-_]/g, " ");
|
|
6647
|
+
label.textContent = text;
|
|
6648
|
+
if (input && input.parentElement === wrapper) wrapper.insertBefore(label, input);
|
|
6649
|
+
else wrapper.insertBefore(label, wrapper.firstChild);
|
|
6650
|
+
return label;
|
|
6651
|
+
}
|
|
6652
|
+
function listFieldWrappers(form) {
|
|
6653
|
+
return Array.from(form.querySelectorAll(`[${FIELD_ATTR}]`));
|
|
6654
|
+
}
|
|
6655
|
+
function getFieldWrapper(el) {
|
|
6656
|
+
return el?.closest(`[${FIELD_ATTR}]`) ?? null;
|
|
6657
|
+
}
|
|
6658
|
+
function fieldInputOf(wrapper) {
|
|
6659
|
+
return wrapper.querySelector("input, textarea");
|
|
6660
|
+
}
|
|
6661
|
+
function fieldLabelOf(wrapper) {
|
|
6662
|
+
return wrapper.querySelector("label");
|
|
6663
|
+
}
|
|
6664
|
+
function fieldKeyOf(wrapper) {
|
|
6665
|
+
return wrapper.getAttribute(FIELD_ATTR) ?? "";
|
|
6666
|
+
}
|
|
6667
|
+
function fieldTypeOf(wrapper) {
|
|
6668
|
+
return wrapper.getAttribute(FIELD_TYPE_ATTR) ?? "short-text";
|
|
6669
|
+
}
|
|
6670
|
+
function isFieldRequired(wrapper) {
|
|
6671
|
+
return fieldInputOf(wrapper)?.hasAttribute("required") ?? false;
|
|
6672
|
+
}
|
|
6673
|
+
function markFormFields(form) {
|
|
6674
|
+
form.querySelectorAll("input, textarea").forEach((input) => {
|
|
6675
|
+
const type = input.type;
|
|
6676
|
+
if (type === "submit" || type === "button" || type === "reset" || type === "hidden") return;
|
|
6677
|
+
if (getFieldWrapper(input)) return;
|
|
6678
|
+
let wrapper = input;
|
|
6679
|
+
while (wrapper.parentElement && wrapper.parentElement !== form && wrapper.parentElement.querySelectorAll("input, textarea").length === 1) {
|
|
6680
|
+
wrapper = wrapper.parentElement;
|
|
6681
|
+
}
|
|
6682
|
+
if (wrapper === input) {
|
|
6683
|
+
const box = document.createElement("div");
|
|
6684
|
+
box.setAttribute("data-ohw-field-box", "");
|
|
6685
|
+
input.replaceWith(box);
|
|
6686
|
+
box.appendChild(input);
|
|
6687
|
+
wrapper = box;
|
|
6688
|
+
}
|
|
6689
|
+
const key = input.getAttribute("name") ?? input.getAttribute("data-ohw-key") ?? `field-${Date.now()}`;
|
|
6690
|
+
wrapper.setAttribute(FIELD_ATTR, key);
|
|
6691
|
+
wrapper.setAttribute(FIELD_TYPE_ATTR, inferType(input));
|
|
6692
|
+
ensureFieldLabel(wrapper, key);
|
|
6693
|
+
syncRequiredMark(wrapper);
|
|
6694
|
+
});
|
|
6695
|
+
return listFieldWrappers(form);
|
|
6696
|
+
}
|
|
6697
|
+
function readFieldsFromDom(form) {
|
|
6698
|
+
return listFieldWrappers(form).map((wrapper) => {
|
|
6699
|
+
const input = fieldInputOf(wrapper);
|
|
6700
|
+
return {
|
|
6701
|
+
key: fieldKeyOf(wrapper),
|
|
6702
|
+
type: fieldTypeOf(wrapper),
|
|
6703
|
+
label: fieldLabelText(fieldLabelOf(wrapper)).trim(),
|
|
6704
|
+
// While a field is selected its placeholder text lives in the value (see
|
|
6705
|
+
// beginPlaceholderEdit), so reading the attribute would store an empty one — whoever
|
|
6706
|
+
// happens to save at that moment must still record what the owner typed.
|
|
6707
|
+
placeholder: input ? input.hasAttribute(PLACEHOLDER_EDIT_ATTR) ? input.value : input.getAttribute("placeholder") ?? "" : "",
|
|
6708
|
+
required: Boolean(input?.hasAttribute("required"))
|
|
6709
|
+
};
|
|
6710
|
+
});
|
|
6711
|
+
}
|
|
6712
|
+
function parseFieldSpecs(raw) {
|
|
6713
|
+
if (!raw) return null;
|
|
6714
|
+
try {
|
|
6715
|
+
const parsed = JSON.parse(raw);
|
|
6716
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
6717
|
+
} catch {
|
|
6718
|
+
return null;
|
|
6719
|
+
}
|
|
6720
|
+
}
|
|
6721
|
+
function isDefaultText(value, pick) {
|
|
6722
|
+
const trimmed = value.trim().replace(/\s*\*$/, "");
|
|
6723
|
+
if (!trimmed) return true;
|
|
6724
|
+
return Object.values(FIELD_DEFAULTS).some((defaults) => pick(defaults) === trimmed);
|
|
6725
|
+
}
|
|
6726
|
+
function applyFieldType(wrapper, type) {
|
|
6727
|
+
const input = fieldInputOf(wrapper);
|
|
6728
|
+
if (!input) return;
|
|
6729
|
+
const defaults = FIELD_DEFAULTS[type];
|
|
6730
|
+
const placeholder = input.getAttribute("placeholder") ?? "";
|
|
6731
|
+
const label = fieldLabelOf(wrapper);
|
|
6732
|
+
const followsDefaults = {
|
|
6733
|
+
// A label the owner wrote stays; one that still reads as a type name (or as the old
|
|
6734
|
+
// placeholder the template shipped) follows the new type.
|
|
6735
|
+
label: isDefaultText(label?.textContent ?? "", (d) => d.label) || fieldLabelText(label).trim() === placeholder.trim()
|
|
6736
|
+
};
|
|
6737
|
+
const { tag, inputType } = inputTagFor(type);
|
|
6738
|
+
wrapper.setAttribute(FIELD_TYPE_ATTR, type);
|
|
6739
|
+
const shedSize = (el) => {
|
|
6740
|
+
if (type === "long-text") return;
|
|
6741
|
+
el.style.removeProperty("height");
|
|
6742
|
+
el.style.removeProperty("min-height");
|
|
6743
|
+
el.style.removeProperty("resize");
|
|
6744
|
+
el.removeAttribute("rows");
|
|
6745
|
+
};
|
|
6746
|
+
const applyDefaults = (el) => {
|
|
6747
|
+
el.setAttribute("placeholder", defaults.placeholder);
|
|
6748
|
+
if (label && followsDefaults.label) {
|
|
6749
|
+
label.textContent = defaults.label;
|
|
6750
|
+
}
|
|
6751
|
+
};
|
|
6752
|
+
if (input.tagName.toLowerCase() === tag) {
|
|
6753
|
+
if (inputType) input.type = inputType;
|
|
6754
|
+
shedSize(input);
|
|
6755
|
+
applyDefaults(input);
|
|
6756
|
+
return;
|
|
6757
|
+
}
|
|
6758
|
+
const next = document.createElement(tag);
|
|
6759
|
+
Array.from(input.attributes).forEach((attr) => {
|
|
6760
|
+
if (attr.name === "type") return;
|
|
6761
|
+
next.setAttribute(attr.name, attr.value);
|
|
6762
|
+
});
|
|
6763
|
+
if (inputType) next.setAttribute("type", inputType);
|
|
6764
|
+
shedSize(next);
|
|
6765
|
+
if (type === "long-text") next.setAttribute("rows", "4");
|
|
6766
|
+
input.replaceWith(next);
|
|
6767
|
+
applyDefaults(next);
|
|
6768
|
+
}
|
|
6769
|
+
var REQUIRED_MARK_ATTR = "data-ohw-field-required";
|
|
6770
|
+
function fieldLabelText(label) {
|
|
6771
|
+
if (!label) return "";
|
|
6772
|
+
return (label.textContent ?? "").replace(/\s*\*\s*$/, "").trimEnd();
|
|
6773
|
+
}
|
|
6774
|
+
function syncRequiredMark(wrapper) {
|
|
6775
|
+
const label = fieldLabelOf(wrapper);
|
|
6776
|
+
if (!label) return;
|
|
6777
|
+
label.querySelectorAll("[data-ohw-required-mark]").forEach((el) => el.remove());
|
|
6778
|
+
const words = fieldLabelText(label);
|
|
6779
|
+
if ((label.textContent ?? "") !== words) label.textContent = words;
|
|
6780
|
+
if (isFieldRequired(wrapper)) label.setAttribute(REQUIRED_MARK_ATTR, "");
|
|
6781
|
+
else label.removeAttribute(REQUIRED_MARK_ATTR);
|
|
6782
|
+
}
|
|
6783
|
+
function setFieldRequired(wrapper, required) {
|
|
6784
|
+
const input = fieldInputOf(wrapper);
|
|
6785
|
+
const label = fieldLabelOf(wrapper);
|
|
6786
|
+
if (!input) return;
|
|
6787
|
+
if (required) input.setAttribute("required", "");
|
|
6788
|
+
else input.removeAttribute("required");
|
|
6789
|
+
if (label) syncRequiredMark(wrapper);
|
|
6790
|
+
}
|
|
6791
|
+
function beginPlaceholderEdit(wrapper) {
|
|
6792
|
+
const input = fieldInputOf(wrapper);
|
|
6793
|
+
if (!input || input.hasAttribute(PLACEHOLDER_EDIT_ATTR)) return;
|
|
6794
|
+
input.setAttribute(PLACEHOLDER_EDIT_ATTR, "");
|
|
6795
|
+
input.value = input.getAttribute("placeholder") ?? "";
|
|
6796
|
+
input.setAttribute("placeholder", "");
|
|
6797
|
+
}
|
|
6798
|
+
function commitPlaceholderEdit(wrapper) {
|
|
6799
|
+
if (!wrapper) return false;
|
|
6800
|
+
const input = fieldInputOf(wrapper);
|
|
6801
|
+
if (!input || !input.hasAttribute(PLACEHOLDER_EDIT_ATTR)) return false;
|
|
6802
|
+
const typed = input.value;
|
|
6803
|
+
input.removeAttribute(PLACEHOLDER_EDIT_ATTR);
|
|
6804
|
+
input.value = "";
|
|
6805
|
+
const previous = input.getAttribute("placeholder") ?? "";
|
|
6806
|
+
input.setAttribute("placeholder", typed);
|
|
6807
|
+
return previous !== typed;
|
|
6808
|
+
}
|
|
6809
|
+
function setFieldPlaceholder(wrapper, placeholder) {
|
|
6810
|
+
fieldInputOf(wrapper)?.setAttribute("placeholder", placeholder);
|
|
6811
|
+
}
|
|
6812
|
+
function uniqueKey(form, base) {
|
|
6813
|
+
const taken = new Set(listFieldWrappers(form).map(fieldKeyOf));
|
|
6814
|
+
if (!taken.has(base)) return base;
|
|
6815
|
+
let n = 2;
|
|
6816
|
+
while (taken.has(`${base}-${n}`)) n += 1;
|
|
6817
|
+
return `${base}-${n}`;
|
|
6818
|
+
}
|
|
6819
|
+
function insertField(form, type) {
|
|
6820
|
+
const existing = listFieldWrappers(form);
|
|
6821
|
+
const source = existing[existing.length - 1] ?? null;
|
|
6822
|
+
const key = uniqueKey(form, type);
|
|
6823
|
+
let wrapper;
|
|
6824
|
+
if (source) {
|
|
6825
|
+
wrapper = source.cloneNode(true);
|
|
6826
|
+
source.after(wrapper);
|
|
6827
|
+
} else {
|
|
6828
|
+
wrapper = document.createElement("div");
|
|
6829
|
+
const label2 = document.createElement("label");
|
|
6830
|
+
const input2 = document.createElement("input");
|
|
6831
|
+
wrapper.append(label2, input2);
|
|
6832
|
+
const submit = form.querySelector('button, input[type="submit"]');
|
|
6833
|
+
if (submit) submit.before(wrapper);
|
|
6834
|
+
else form.appendChild(wrapper);
|
|
6835
|
+
}
|
|
6836
|
+
wrapper.setAttribute(FIELD_ATTR, key);
|
|
6837
|
+
wrapper.setAttribute(FIELD_TYPE_ATTR, type);
|
|
6838
|
+
applyFieldType(wrapper, type);
|
|
6839
|
+
const defaults = FIELD_DEFAULTS[type];
|
|
6840
|
+
const input = fieldInputOf(wrapper);
|
|
6841
|
+
if (input) {
|
|
6842
|
+
input.setAttribute("name", key);
|
|
6843
|
+
input.removeAttribute("required");
|
|
6844
|
+
input.setAttribute("placeholder", defaults.placeholder);
|
|
6845
|
+
input.value = "";
|
|
6846
|
+
}
|
|
6847
|
+
const label = ensureFieldLabel(wrapper, key);
|
|
6848
|
+
label.textContent = defaults.label;
|
|
6849
|
+
label.setAttribute("data-ohw-key", `${key}-label`);
|
|
6850
|
+
return wrapper;
|
|
6851
|
+
}
|
|
6852
|
+
function duplicateField(form, wrapper) {
|
|
6853
|
+
const copy = wrapper.cloneNode(true);
|
|
6854
|
+
const key = uniqueKey(form, `${fieldKeyOf(wrapper)}-copy`);
|
|
6855
|
+
copy.setAttribute(FIELD_ATTR, key);
|
|
6856
|
+
const input = fieldInputOf(copy);
|
|
6857
|
+
if (input) {
|
|
6858
|
+
input.setAttribute("name", key);
|
|
6859
|
+
input.removeAttribute("data-ohw-key");
|
|
6860
|
+
input.value = "";
|
|
6861
|
+
}
|
|
6862
|
+
const label = fieldLabelOf(copy);
|
|
6863
|
+
if (label) label.setAttribute("data-ohw-key", `${key}-label`);
|
|
6864
|
+
wrapper.after(copy);
|
|
6865
|
+
return copy;
|
|
6866
|
+
}
|
|
6867
|
+
function removeField(wrapper) {
|
|
6868
|
+
wrapper.remove();
|
|
6869
|
+
}
|
|
6870
|
+
function applyFieldOrder(form, keys) {
|
|
6871
|
+
const wrappers = listFieldWrappers(form);
|
|
6872
|
+
const byKey = new Map(wrappers.map((wrapper) => [fieldKeyOf(wrapper), wrapper]));
|
|
6873
|
+
const ordered = keys.map((key) => byKey.get(key)).filter((w) => Boolean(w));
|
|
6874
|
+
if (ordered.length !== wrappers.length) return;
|
|
6875
|
+
const slots = wrappers.map((wrapper) => {
|
|
6876
|
+
const slot = document.createComment("ohw-field-slot");
|
|
6877
|
+
wrapper.replaceWith(slot);
|
|
6878
|
+
return slot;
|
|
6879
|
+
});
|
|
6880
|
+
ordered.forEach((wrapper, index) => slots[index].replaceWith(wrapper));
|
|
6881
|
+
}
|
|
6882
|
+
function moveField(form, key, toIndex) {
|
|
6883
|
+
const keys = listFieldWrappers(form).map(fieldKeyOf);
|
|
6884
|
+
const from = keys.indexOf(key);
|
|
6885
|
+
if (from === -1) return;
|
|
6886
|
+
const rest = keys.filter((k) => k !== key);
|
|
6887
|
+
rest.splice(Math.max(0, Math.min(rest.length, toIndex)), 0, key);
|
|
6888
|
+
applyFieldOrder(form, rest);
|
|
6889
|
+
}
|
|
6890
|
+
function reconcileFieldsFromContent(form, content) {
|
|
6891
|
+
const formKey = form.getAttribute("data-ohw-key");
|
|
6892
|
+
if (!formKey) return;
|
|
6893
|
+
markFormFields(form);
|
|
6894
|
+
const stored = parseFieldSpecs(content[fieldsKey(formKey)]);
|
|
6895
|
+
if (!stored) return;
|
|
6896
|
+
const byKey = new Map(listFieldWrappers(form).map((wrapper) => [fieldKeyOf(wrapper), wrapper]));
|
|
6897
|
+
stored.forEach((spec) => {
|
|
6898
|
+
let wrapper = byKey.get(spec.key) ?? null;
|
|
6899
|
+
if (!wrapper) {
|
|
6900
|
+
wrapper = insertField(form, spec.type);
|
|
6901
|
+
if (!wrapper) return;
|
|
6902
|
+
wrapper.setAttribute(FIELD_ATTR, spec.key);
|
|
6903
|
+
const input = fieldInputOf(wrapper);
|
|
6904
|
+
if (input) {
|
|
6905
|
+
input.setAttribute("name", spec.key);
|
|
6906
|
+
}
|
|
6907
|
+
fieldLabelOf(wrapper)?.setAttribute("data-ohw-key", `${spec.key}-label`);
|
|
6908
|
+
byKey.set(spec.key, wrapper);
|
|
6909
|
+
}
|
|
6910
|
+
applyFieldType(wrapper, spec.type);
|
|
6911
|
+
setFieldRequired(wrapper, spec.required);
|
|
6912
|
+
setFieldPlaceholder(wrapper, spec.placeholder);
|
|
6913
|
+
const label = fieldLabelOf(wrapper);
|
|
6914
|
+
if (label && spec.label) label.textContent = spec.label.replace(/\s*\*\s*$/, "").trimEnd();
|
|
6915
|
+
syncRequiredMark(wrapper);
|
|
6916
|
+
});
|
|
6917
|
+
const wanted = new Set(stored.map((spec) => spec.key));
|
|
6918
|
+
listFieldWrappers(form).forEach((wrapper) => {
|
|
6919
|
+
if (!wanted.has(fieldKeyOf(wrapper))) wrapper.remove();
|
|
6920
|
+
});
|
|
6921
|
+
const currentOrder = listFieldWrappers(form).map(fieldKeyOf).join("\0");
|
|
6922
|
+
const storedOrder = stored.map((spec) => spec.key).join("\0");
|
|
6923
|
+
if (currentOrder !== storedOrder) {
|
|
6924
|
+
applyFieldOrder(form, stored.map((spec) => spec.key));
|
|
6925
|
+
}
|
|
6926
|
+
}
|
|
6927
|
+
|
|
6928
|
+
// src/ui/form-field-toolbar.tsx
|
|
6929
|
+
var import_lucide_react4 = require("lucide-react");
|
|
6930
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
6931
|
+
var TYPE_ICONS = {
|
|
6932
|
+
"short-text": import_lucide_react4.Type,
|
|
6933
|
+
"long-text": import_lucide_react4.TextQuote,
|
|
6934
|
+
email: import_lucide_react4.AtSign,
|
|
6935
|
+
phone: import_lucide_react4.Phone
|
|
6936
|
+
};
|
|
6937
|
+
function FormFieldToolbar({
|
|
6938
|
+
type,
|
|
6939
|
+
required,
|
|
6940
|
+
onTypeChange,
|
|
6941
|
+
onRequiredToggle,
|
|
6942
|
+
onDuplicate,
|
|
6943
|
+
onDelete
|
|
6944
|
+
}) {
|
|
6945
|
+
const TypeIcon = TYPE_ICONS[type];
|
|
6946
|
+
const typeLabel = FIELD_TYPES.find((entry) => entry.type === type)?.label ?? "Short text";
|
|
6947
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
6948
|
+
"div",
|
|
6949
|
+
{
|
|
6950
|
+
"data-ohw-field-toolbar": "",
|
|
6951
|
+
className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
|
|
6952
|
+
children: [
|
|
6953
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DropdownMenu, { children: [
|
|
6954
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
6955
|
+
"button",
|
|
6956
|
+
{
|
|
6957
|
+
type: "button",
|
|
6958
|
+
className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted/70",
|
|
6959
|
+
"data-ohw-field-type-trigger": "",
|
|
6960
|
+
children: [
|
|
6961
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TypeIcon, { size: 14, strokeWidth: 1.75, "aria-hidden": true }),
|
|
6962
|
+
typeLabel,
|
|
6963
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.ChevronDown, { size: 13, className: "text-muted-foreground", "aria-hidden": true })
|
|
6964
|
+
]
|
|
6965
|
+
}
|
|
6966
|
+
) }),
|
|
6967
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-[190px] p-1", children: FIELD_TYPES.map((entry) => {
|
|
6968
|
+
const Icon = TYPE_ICONS[entry.type];
|
|
6969
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
6970
|
+
DropdownMenuItem,
|
|
6971
|
+
{
|
|
6972
|
+
onSelect: () => onTypeChange(entry.type),
|
|
6973
|
+
className: "rounded-md py-2 text-[13px] " + (entry.type === type ? "bg-primary/10" : ""),
|
|
6974
|
+
children: [
|
|
6975
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Icon, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
|
|
6976
|
+
entry.label
|
|
6977
|
+
]
|
|
6978
|
+
},
|
|
6979
|
+
entry.type
|
|
6980
|
+
);
|
|
6981
|
+
}) })
|
|
6982
|
+
] }),
|
|
6983
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
|
|
6984
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
6985
|
+
"button",
|
|
6986
|
+
{
|
|
6987
|
+
type: "button",
|
|
6988
|
+
"aria-pressed": required,
|
|
6989
|
+
onClick: onRequiredToggle,
|
|
6990
|
+
className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-medium transition-colors " + (required ? "bg-primary/10 text-primary" : "text-foreground hover:bg-muted/70"),
|
|
6991
|
+
"data-ohw-field-required": "",
|
|
6992
|
+
children: [
|
|
6993
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.Asterisk, { size: 14, strokeWidth: 2, "aria-hidden": true }),
|
|
6994
|
+
"Required"
|
|
6995
|
+
]
|
|
6996
|
+
}
|
|
6997
|
+
),
|
|
6998
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
|
|
6999
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DropdownMenu, { children: [
|
|
7000
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
7001
|
+
"button",
|
|
7002
|
+
{
|
|
7003
|
+
type: "button",
|
|
7004
|
+
title: "More",
|
|
7005
|
+
"aria-label": "More",
|
|
7006
|
+
className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/70",
|
|
7007
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.MoreHorizontal, { size: 15, "aria-hidden": true })
|
|
7008
|
+
}
|
|
7009
|
+
) }),
|
|
7010
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-[170px] p-1", children: [
|
|
7011
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DropdownMenuItem, { onSelect: onDuplicate, className: "rounded-md py-2 text-[13px]", children: [
|
|
7012
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.Copy, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
|
|
7013
|
+
"Duplicate"
|
|
7014
|
+
] }),
|
|
7015
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DropdownMenuItem, { variant: "destructive", onSelect: onDelete, className: "rounded-md py-2 text-[13px]", children: [
|
|
7016
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.Trash2, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
|
|
7017
|
+
"Delete"
|
|
7018
|
+
] })
|
|
7019
|
+
] })
|
|
7020
|
+
] })
|
|
7021
|
+
]
|
|
7022
|
+
}
|
|
7023
|
+
);
|
|
7024
|
+
}
|
|
7025
|
+
function FieldTypePicker({ onPick }) {
|
|
7026
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
7027
|
+
"div",
|
|
7028
|
+
{
|
|
7029
|
+
"data-ohw-field-type-picker": "",
|
|
7030
|
+
className: "pointer-events-auto grid w-[420px] grid-cols-3 gap-3 rounded-xl border border-border bg-background p-4 shadow-lg",
|
|
7031
|
+
children: FIELD_TYPES.map((entry) => {
|
|
7032
|
+
const Icon = TYPE_ICONS[entry.type];
|
|
7033
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
7034
|
+
"button",
|
|
7035
|
+
{
|
|
7036
|
+
type: "button",
|
|
7037
|
+
onClick: () => onPick(entry.type),
|
|
7038
|
+
className: "flex h-[104px] flex-col items-center justify-center gap-3 rounded-xl border border-border text-[15px] font-medium text-foreground transition-colors hover:border-primary hover:bg-primary/5",
|
|
7039
|
+
children: [
|
|
7040
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Icon, { size: 26, strokeWidth: 1.5, "aria-hidden": true }),
|
|
7041
|
+
entry.label
|
|
7042
|
+
]
|
|
7043
|
+
},
|
|
7044
|
+
entry.type
|
|
7045
|
+
);
|
|
7046
|
+
})
|
|
7047
|
+
}
|
|
7048
|
+
);
|
|
7049
|
+
}
|
|
7050
|
+
|
|
6807
7051
|
// src/ui/MediaOverlay.tsx
|
|
6808
7052
|
var React7 = __toESM(require("react"), 1);
|
|
6809
|
-
var
|
|
7053
|
+
var import_lucide_react5 = require("lucide-react");
|
|
6810
7054
|
|
|
6811
7055
|
// src/ui/button.tsx
|
|
6812
7056
|
var React6 = __toESM(require("react"), 1);
|
|
6813
7057
|
var import_radix_ui5 = require("radix-ui");
|
|
6814
|
-
var
|
|
7058
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
6815
7059
|
var buttonVariants = cva(
|
|
6816
7060
|
"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium transition-colors outline-none disabled:pointer-events-none disabled:opacity-50 min-w-[80px] px-3 py-2",
|
|
6817
7061
|
{
|
|
@@ -6835,7 +7079,7 @@ var buttonVariants = cva(
|
|
|
6835
7079
|
var Button = React6.forwardRef(
|
|
6836
7080
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
6837
7081
|
const Comp = asChild ? import_radix_ui5.Slot.Root : "button";
|
|
6838
|
-
return /* @__PURE__ */ (0,
|
|
7082
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
6839
7083
|
Comp,
|
|
6840
7084
|
{
|
|
6841
7085
|
ref,
|
|
@@ -6849,7 +7093,7 @@ var Button = React6.forwardRef(
|
|
|
6849
7093
|
Button.displayName = "Button";
|
|
6850
7094
|
|
|
6851
7095
|
// src/ui/MediaOverlay.tsx
|
|
6852
|
-
var
|
|
7096
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
6853
7097
|
var MEDIA_UPLOAD_FADE_MS = 300;
|
|
6854
7098
|
var VIDEO_SETTINGS_BAR_INSET = 8;
|
|
6855
7099
|
var OVERLAY_BUTTON_STYLE = {
|
|
@@ -6907,7 +7151,7 @@ function MediaOverlay({
|
|
|
6907
7151
|
return () => anim.cancel();
|
|
6908
7152
|
}, [isUploading, fadingOut, onFadeOutComplete, hover.key]);
|
|
6909
7153
|
if (isUploading) {
|
|
6910
|
-
return /* @__PURE__ */ (0,
|
|
7154
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
6911
7155
|
"div",
|
|
6912
7156
|
{
|
|
6913
7157
|
ref: skeletonRef,
|
|
@@ -6916,11 +7160,11 @@ function MediaOverlay({
|
|
|
6916
7160
|
"data-ohw-media-skeleton": "",
|
|
6917
7161
|
"aria-hidden": true,
|
|
6918
7162
|
style: { ...box, pointerEvents: "none" },
|
|
6919
|
-
children: /* @__PURE__ */ (0,
|
|
7163
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("style", { children: SKELETON_CSS })
|
|
6920
7164
|
}
|
|
6921
7165
|
);
|
|
6922
7166
|
}
|
|
6923
|
-
const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ (0,
|
|
7167
|
+
const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
6924
7168
|
"div",
|
|
6925
7169
|
{
|
|
6926
7170
|
"data-ohw-bridge": "",
|
|
@@ -6936,7 +7180,7 @@ function MediaOverlay({
|
|
|
6936
7180
|
},
|
|
6937
7181
|
onClick: (e) => e.stopPropagation(),
|
|
6938
7182
|
children: [
|
|
6939
|
-
/* @__PURE__ */ (0,
|
|
7183
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
6940
7184
|
Button,
|
|
6941
7185
|
{
|
|
6942
7186
|
"data-ohw-media-overlay": "",
|
|
@@ -6951,10 +7195,10 @@ function MediaOverlay({
|
|
|
6951
7195
|
e.stopPropagation();
|
|
6952
7196
|
onVideoSettingsChange?.(hover.key, { autoplay: !autoplay });
|
|
6953
7197
|
},
|
|
6954
|
-
children: autoplay ? /* @__PURE__ */ (0,
|
|
7198
|
+
children: autoplay ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Pause, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Play, { size: 14 })
|
|
6955
7199
|
}
|
|
6956
7200
|
),
|
|
6957
|
-
/* @__PURE__ */ (0,
|
|
7201
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
6958
7202
|
Button,
|
|
6959
7203
|
{
|
|
6960
7204
|
"data-ohw-media-overlay": "",
|
|
@@ -6969,15 +7213,15 @@ function MediaOverlay({
|
|
|
6969
7213
|
e.stopPropagation();
|
|
6970
7214
|
onVideoSettingsChange?.(hover.key, { muted: !muted });
|
|
6971
7215
|
},
|
|
6972
|
-
children: muted ? /* @__PURE__ */ (0,
|
|
7216
|
+
children: muted ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.VolumeX, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Volume2, { size: 14 })
|
|
6973
7217
|
}
|
|
6974
7218
|
)
|
|
6975
7219
|
]
|
|
6976
7220
|
}
|
|
6977
7221
|
) : null;
|
|
6978
|
-
return /* @__PURE__ */ (0,
|
|
7222
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
6979
7223
|
settingsBar,
|
|
6980
|
-
/* @__PURE__ */ (0,
|
|
7224
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
6981
7225
|
"div",
|
|
6982
7226
|
{
|
|
6983
7227
|
"data-ohw-bridge": "",
|
|
@@ -6994,7 +7238,7 @@ function MediaOverlay({
|
|
|
6994
7238
|
background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
|
|
6995
7239
|
},
|
|
6996
7240
|
onClick: () => onReplace(hover.key),
|
|
6997
|
-
children: /* @__PURE__ */ (0,
|
|
7241
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
6998
7242
|
Button,
|
|
6999
7243
|
{
|
|
7000
7244
|
"data-ohw-media-overlay": "",
|
|
@@ -7012,7 +7256,7 @@ function MediaOverlay({
|
|
|
7012
7256
|
onReplace(hover.key);
|
|
7013
7257
|
},
|
|
7014
7258
|
children: [
|
|
7015
|
-
isVideo ? /* @__PURE__ */ (0,
|
|
7259
|
+
isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
|
|
7016
7260
|
isVideo ? "Replace video" : "Replace image"
|
|
7017
7261
|
]
|
|
7018
7262
|
}
|
|
@@ -7023,8 +7267,8 @@ function MediaOverlay({
|
|
|
7023
7267
|
}
|
|
7024
7268
|
|
|
7025
7269
|
// src/ui/CarouselOverlay.tsx
|
|
7026
|
-
var
|
|
7027
|
-
var
|
|
7270
|
+
var import_lucide_react6 = require("lucide-react");
|
|
7271
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
7028
7272
|
var OVERLAY_BUTTON_STYLE2 = {
|
|
7029
7273
|
pointerEvents: "auto",
|
|
7030
7274
|
fontFamily: "Inter, sans-serif",
|
|
@@ -7036,7 +7280,7 @@ function CarouselOverlay({
|
|
|
7036
7280
|
onEdit
|
|
7037
7281
|
}) {
|
|
7038
7282
|
const { rect } = hover;
|
|
7039
|
-
return /* @__PURE__ */ (0,
|
|
7283
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
7040
7284
|
"div",
|
|
7041
7285
|
{
|
|
7042
7286
|
"data-ohw-bridge": "",
|
|
@@ -7054,7 +7298,7 @@ function CarouselOverlay({
|
|
|
7054
7298
|
background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
|
|
7055
7299
|
},
|
|
7056
7300
|
onClick: () => onEdit(hover.key),
|
|
7057
|
-
children: /* @__PURE__ */ (0,
|
|
7301
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
7058
7302
|
Button,
|
|
7059
7303
|
{
|
|
7060
7304
|
"data-ohw-carousel-overlay": "",
|
|
@@ -7068,7 +7312,7 @@ function CarouselOverlay({
|
|
|
7068
7312
|
onEdit(hover.key);
|
|
7069
7313
|
},
|
|
7070
7314
|
children: [
|
|
7071
|
-
/* @__PURE__ */ (0,
|
|
7315
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react6.GalleryHorizontal, { size: 14 }),
|
|
7072
7316
|
"Edit gallery"
|
|
7073
7317
|
]
|
|
7074
7318
|
}
|
|
@@ -7079,7 +7323,7 @@ function CarouselOverlay({
|
|
|
7079
7323
|
|
|
7080
7324
|
// src/ui/ai-section/AiSectionOverlay.tsx
|
|
7081
7325
|
var import_react8 = require("react");
|
|
7082
|
-
var
|
|
7326
|
+
var import_lucide_react7 = require("lucide-react");
|
|
7083
7327
|
|
|
7084
7328
|
// src/lib/sections.ts
|
|
7085
7329
|
var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
|
|
@@ -7109,13 +7353,9 @@ function parseSectionsFromHtml(html) {
|
|
|
7109
7353
|
}
|
|
7110
7354
|
|
|
7111
7355
|
// src/ui/ai-section/AiSectionOverlay.tsx
|
|
7112
|
-
var
|
|
7113
|
-
function
|
|
7114
|
-
const
|
|
7115
|
-
return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
|
|
7116
|
-
}
|
|
7117
|
-
function readRect(instanceId) {
|
|
7118
|
-
const el = findSectionElement(instanceId);
|
|
7356
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
7357
|
+
function readRect(sectionId) {
|
|
7358
|
+
const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
|
|
7119
7359
|
if (!el) return null;
|
|
7120
7360
|
const r2 = el.getBoundingClientRect();
|
|
7121
7361
|
if (r2.width <= 0 || r2.height <= 0) return null;
|
|
@@ -7138,7 +7378,7 @@ function useLiveSectionRect(sectionId) {
|
|
|
7138
7378
|
const opts = { capture: true, passive: true };
|
|
7139
7379
|
window.addEventListener("scroll", update, opts);
|
|
7140
7380
|
window.addEventListener("resize", update);
|
|
7141
|
-
const el =
|
|
7381
|
+
const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
|
|
7142
7382
|
const ro = el ? new ResizeObserver(update) : null;
|
|
7143
7383
|
if (el && ro) ro.observe(el);
|
|
7144
7384
|
const interval = setInterval(update, 500);
|
|
@@ -7151,14 +7391,6 @@ function useLiveSectionRect(sectionId) {
|
|
|
7151
7391
|
}, [sectionId]);
|
|
7152
7392
|
return rect;
|
|
7153
7393
|
}
|
|
7154
|
-
function computeSectionBoundaryFlags(instanceId) {
|
|
7155
|
-
const topLevel = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
7156
|
-
(el) => !el.parentElement?.closest("[data-ohw-section]")
|
|
7157
|
-
);
|
|
7158
|
-
const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
|
|
7159
|
-
if (index === -1) return { isFirst: true, isLast: true };
|
|
7160
|
-
return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
|
|
7161
|
-
}
|
|
7162
7394
|
var PRIMARY2 = "#0885FE";
|
|
7163
7395
|
function edgeAwareRadius(rect) {
|
|
7164
7396
|
const container = window.innerWidth <= 480 ? 16 : 24;
|
|
@@ -7177,8 +7409,8 @@ function ReviewButton({
|
|
|
7177
7409
|
color
|
|
7178
7410
|
}) {
|
|
7179
7411
|
const [hover, setHover] = (0, import_react8.useState)(false);
|
|
7180
|
-
return /* @__PURE__ */ (0,
|
|
7181
|
-
/* @__PURE__ */ (0,
|
|
7412
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { position: "relative" }, children: [
|
|
7413
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
7182
7414
|
"button",
|
|
7183
7415
|
{
|
|
7184
7416
|
type: "button",
|
|
@@ -7202,7 +7434,7 @@ function ReviewButton({
|
|
|
7202
7434
|
children
|
|
7203
7435
|
}
|
|
7204
7436
|
),
|
|
7205
|
-
hover && /* @__PURE__ */ (0,
|
|
7437
|
+
hover && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
7206
7438
|
"div",
|
|
7207
7439
|
{
|
|
7208
7440
|
style: {
|
|
@@ -7232,7 +7464,6 @@ function AiSectionOverlay({
|
|
|
7232
7464
|
}) {
|
|
7233
7465
|
const [selectedId, setSelectedId] = (0, import_react8.useState)(null);
|
|
7234
7466
|
const [reviewId, setReviewId] = (0, import_react8.useState)(null);
|
|
7235
|
-
const [reviewButtonsHidden, setReviewButtonsHidden] = (0, import_react8.useState)(false);
|
|
7236
7467
|
const reviewIdRef = (0, import_react8.useRef)(null);
|
|
7237
7468
|
reviewIdRef.current = reviewId;
|
|
7238
7469
|
const selectedIdRef = (0, import_react8.useRef)(null);
|
|
@@ -7241,7 +7472,7 @@ function AiSectionOverlay({
|
|
|
7241
7472
|
(el) => {
|
|
7242
7473
|
postToParent2({
|
|
7243
7474
|
type: "ow:section-selected",
|
|
7244
|
-
sectionId: el
|
|
7475
|
+
sectionId: el?.dataset.ohwSection ?? null,
|
|
7245
7476
|
sectionLabel: el ? el.dataset.ohwSectionLabel ?? titleCaseSectionId(el.dataset.ohwSection ?? "") : null
|
|
7246
7477
|
});
|
|
7247
7478
|
},
|
|
@@ -7250,7 +7481,7 @@ function AiSectionOverlay({
|
|
|
7250
7481
|
const selectFromElement = (0, import_react8.useCallback)(
|
|
7251
7482
|
(el, options) => {
|
|
7252
7483
|
const sectionEl = el?.closest("[data-ohw-section]") ?? null;
|
|
7253
|
-
const id = sectionEl
|
|
7484
|
+
const id = sectionEl?.dataset.ohwSection ?? null;
|
|
7254
7485
|
if (id === selectedIdRef.current) return;
|
|
7255
7486
|
setSelectedId(id);
|
|
7256
7487
|
if (options?.report !== false) report(sectionEl);
|
|
@@ -7291,10 +7522,9 @@ function AiSectionOverlay({
|
|
|
7291
7522
|
}
|
|
7292
7523
|
const found = readRect(sectionId) != null;
|
|
7293
7524
|
setReviewId(found ? sectionId : null);
|
|
7294
|
-
setReviewButtonsHidden(e.data.hideButtons === true);
|
|
7295
7525
|
postToParent2({ type: "ow:ai-review-started", sectionId, found });
|
|
7296
7526
|
if (found) {
|
|
7297
|
-
document.querySelector(`[data-ohw-
|
|
7527
|
+
document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
7298
7528
|
}
|
|
7299
7529
|
}
|
|
7300
7530
|
};
|
|
@@ -7313,7 +7543,7 @@ function AiSectionOverlay({
|
|
|
7313
7543
|
return;
|
|
7314
7544
|
}
|
|
7315
7545
|
const sec = t.closest("[data-ohw-section]");
|
|
7316
|
-
setHoveredId(sec
|
|
7546
|
+
setHoveredId(sec?.dataset.ohwSection ?? null);
|
|
7317
7547
|
};
|
|
7318
7548
|
const onLeave = () => setHoveredId(null);
|
|
7319
7549
|
document.addEventListener("mousemove", onMove, { passive: true });
|
|
@@ -7345,31 +7575,11 @@ function AiSectionOverlay({
|
|
|
7345
7575
|
},
|
|
7346
7576
|
[postToParent2]
|
|
7347
7577
|
);
|
|
7348
|
-
const
|
|
7349
|
-
const selectionRect = useLiveSectionRect(activeSelectionId);
|
|
7578
|
+
const selectionRect = useLiveSectionRect(reviewId ? null : selectedId);
|
|
7350
7579
|
const reviewRect = useLiveSectionRect(reviewId);
|
|
7351
7580
|
const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
|
|
7352
|
-
(0,
|
|
7353
|
-
|
|
7354
|
-
postToParent2({ type: "ow:section-rect", instanceId: null, rect: null });
|
|
7355
|
-
return;
|
|
7356
|
-
}
|
|
7357
|
-
const { isFirst, isLast } = computeSectionBoundaryFlags(activeSelectionId);
|
|
7358
|
-
postToParent2({
|
|
7359
|
-
type: "ow:section-rect",
|
|
7360
|
-
instanceId: activeSelectionId,
|
|
7361
|
-
rect: {
|
|
7362
|
-
top: selectionRect.top + window.scrollY,
|
|
7363
|
-
left: selectionRect.left + window.scrollX,
|
|
7364
|
-
width: selectionRect.width,
|
|
7365
|
-
height: selectionRect.height
|
|
7366
|
-
},
|
|
7367
|
-
isFirst,
|
|
7368
|
-
isLast
|
|
7369
|
-
});
|
|
7370
|
-
}, [activeSelectionId, selectionRect, postToParent2]);
|
|
7371
|
-
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
7372
|
-
hoverRect && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
7581
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
7582
|
+
hoverRect && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
7373
7583
|
"div",
|
|
7374
7584
|
{
|
|
7375
7585
|
"data-ohw-ai-section-hover": "",
|
|
@@ -7387,7 +7597,7 @@ function AiSectionOverlay({
|
|
|
7387
7597
|
}
|
|
7388
7598
|
}
|
|
7389
7599
|
),
|
|
7390
|
-
selectionRect && /* @__PURE__ */ (0,
|
|
7600
|
+
selectionRect && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
7391
7601
|
"div",
|
|
7392
7602
|
{
|
|
7393
7603
|
"data-ohw-ai-section-selected": "",
|
|
@@ -7405,7 +7615,7 @@ function AiSectionOverlay({
|
|
|
7405
7615
|
}
|
|
7406
7616
|
}
|
|
7407
7617
|
),
|
|
7408
|
-
reviewRect && /* @__PURE__ */ (0,
|
|
7618
|
+
reviewRect && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
7409
7619
|
"div",
|
|
7410
7620
|
{
|
|
7411
7621
|
"data-ohw-ai-review": "",
|
|
@@ -7418,16 +7628,13 @@ function AiSectionOverlay({
|
|
|
7418
7628
|
border: `2px solid ${PRIMARY2}`,
|
|
7419
7629
|
borderRadius: edgeAwareRadius(reviewRect),
|
|
7420
7630
|
zIndex: 2147483200,
|
|
7421
|
-
// The veil itself: swallows clicks so the section stays locked until decided.
|
|
7422
|
-
// stopPropagation only guards the bubble phase; the bridge's capture-phase click
|
|
7423
|
-
// handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
|
|
7424
|
-
// Accept/Discard resolves to the media beneath and opens the file picker.
|
|
7631
|
+
// The veil itself: swallows clicks so the section stays locked until decided.
|
|
7425
7632
|
background: "rgba(8, 133, 254, 0.04)",
|
|
7426
7633
|
pointerEvents: "auto",
|
|
7427
7634
|
cursor: "default"
|
|
7428
7635
|
},
|
|
7429
7636
|
onClick: (e) => e.stopPropagation(),
|
|
7430
|
-
children:
|
|
7637
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
7431
7638
|
"div",
|
|
7432
7639
|
{
|
|
7433
7640
|
style: {
|
|
@@ -7440,8 +7647,8 @@ function AiSectionOverlay({
|
|
|
7440
7647
|
paddingTop: 12
|
|
7441
7648
|
},
|
|
7442
7649
|
children: [
|
|
7443
|
-
/* @__PURE__ */ (0,
|
|
7444
|
-
/* @__PURE__ */ (0,
|
|
7650
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ReviewButton, { label: "Accept", onClick: () => decide("accept"), background: PRIMARY2, color: "#ffffff", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.Check, { size: 16, strokeWidth: 2.5, "aria-hidden": true }) }),
|
|
7651
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ReviewButton, { label: "Discard", onClick: () => decide("discard"), background: "#EFF6FF", color: "#0c0a09", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.X, { size: 16, strokeWidth: 2, "aria-hidden": true }) })
|
|
7445
7652
|
]
|
|
7446
7653
|
}
|
|
7447
7654
|
)
|
|
@@ -7800,23 +8007,23 @@ var import_react12 = require("react");
|
|
|
7800
8007
|
// src/ui/dialog.tsx
|
|
7801
8008
|
var React8 = __toESM(require("react"), 1);
|
|
7802
8009
|
var import_radix_ui6 = require("radix-ui");
|
|
7803
|
-
var
|
|
7804
|
-
var
|
|
8010
|
+
var import_lucide_react8 = require("lucide-react");
|
|
8011
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
7805
8012
|
function Dialog2({
|
|
7806
8013
|
...props
|
|
7807
8014
|
}) {
|
|
7808
|
-
return /* @__PURE__ */ (0,
|
|
8015
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
|
|
7809
8016
|
}
|
|
7810
8017
|
function DialogPortal({
|
|
7811
8018
|
...props
|
|
7812
8019
|
}) {
|
|
7813
|
-
return /* @__PURE__ */ (0,
|
|
8020
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
|
|
7814
8021
|
}
|
|
7815
8022
|
function DialogOverlay({
|
|
7816
8023
|
className,
|
|
7817
8024
|
...props
|
|
7818
8025
|
}) {
|
|
7819
|
-
return /* @__PURE__ */ (0,
|
|
8026
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7820
8027
|
import_radix_ui6.Dialog.Overlay,
|
|
7821
8028
|
{
|
|
7822
8029
|
"data-slot": "dialog-overlay",
|
|
@@ -7829,9 +8036,9 @@ function DialogOverlay({
|
|
|
7829
8036
|
var DialogContent = React8.forwardRef(
|
|
7830
8037
|
({ className, children, showCloseButton = true, container, ...props }, ref) => {
|
|
7831
8038
|
const positionMode = container ? "absolute" : "fixed";
|
|
7832
|
-
return /* @__PURE__ */ (0,
|
|
7833
|
-
/* @__PURE__ */ (0,
|
|
7834
|
-
/* @__PURE__ */ (0,
|
|
8039
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DialogPortal, { container: container ?? void 0, children: [
|
|
8040
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
|
|
8041
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
7835
8042
|
import_radix_ui6.Dialog.Content,
|
|
7836
8043
|
{
|
|
7837
8044
|
ref,
|
|
@@ -7847,13 +8054,13 @@ var DialogContent = React8.forwardRef(
|
|
|
7847
8054
|
...props,
|
|
7848
8055
|
children: [
|
|
7849
8056
|
children,
|
|
7850
|
-
showCloseButton ? /* @__PURE__ */ (0,
|
|
8057
|
+
showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7851
8058
|
import_radix_ui6.Dialog.Close,
|
|
7852
8059
|
{
|
|
7853
8060
|
type: "button",
|
|
7854
8061
|
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
|
|
7855
8062
|
"aria-label": "Close",
|
|
7856
|
-
children: /* @__PURE__ */ (0,
|
|
8063
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
|
|
7857
8064
|
}
|
|
7858
8065
|
) : null
|
|
7859
8066
|
]
|
|
@@ -7867,13 +8074,13 @@ function DialogHeader({
|
|
|
7867
8074
|
className,
|
|
7868
8075
|
...props
|
|
7869
8076
|
}) {
|
|
7870
|
-
return /* @__PURE__ */ (0,
|
|
8077
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
|
|
7871
8078
|
}
|
|
7872
8079
|
function DialogFooter({
|
|
7873
8080
|
className,
|
|
7874
8081
|
...props
|
|
7875
8082
|
}) {
|
|
7876
|
-
return /* @__PURE__ */ (0,
|
|
8083
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7877
8084
|
"div",
|
|
7878
8085
|
{
|
|
7879
8086
|
className: cn("flex items-center justify-end gap-2", className),
|
|
@@ -7881,7 +8088,7 @@ function DialogFooter({
|
|
|
7881
8088
|
}
|
|
7882
8089
|
);
|
|
7883
8090
|
}
|
|
7884
|
-
var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0,
|
|
8091
|
+
var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7885
8092
|
import_radix_ui6.Dialog.Title,
|
|
7886
8093
|
{
|
|
7887
8094
|
ref,
|
|
@@ -7893,7 +8100,7 @@ var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
|
|
|
7893
8100
|
}
|
|
7894
8101
|
));
|
|
7895
8102
|
DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
|
|
7896
|
-
var DialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0,
|
|
8103
|
+
var DialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
7897
8104
|
import_radix_ui6.Dialog.Description,
|
|
7898
8105
|
{
|
|
7899
8106
|
ref,
|
|
@@ -7905,63 +8112,63 @@ DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
|
|
|
7905
8112
|
var DialogClose = import_radix_ui6.Dialog.Close;
|
|
7906
8113
|
|
|
7907
8114
|
// src/ui/link-modal/LinkEditorPanel.tsx
|
|
7908
|
-
var
|
|
8115
|
+
var import_lucide_react12 = require("lucide-react");
|
|
7909
8116
|
|
|
7910
8117
|
// src/ui/link-modal/DestinationBreadcrumb.tsx
|
|
7911
|
-
var
|
|
7912
|
-
var
|
|
8118
|
+
var import_lucide_react9 = require("lucide-react");
|
|
8119
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
7913
8120
|
function DestinationBreadcrumb({
|
|
7914
8121
|
pageTitle,
|
|
7915
8122
|
sectionLabel
|
|
7916
8123
|
}) {
|
|
7917
|
-
return /* @__PURE__ */ (0,
|
|
7918
|
-
/* @__PURE__ */ (0,
|
|
7919
|
-
/* @__PURE__ */ (0,
|
|
7920
|
-
/* @__PURE__ */ (0,
|
|
7921
|
-
/* @__PURE__ */ (0,
|
|
7922
|
-
/* @__PURE__ */ (0,
|
|
8124
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
|
|
8125
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
|
|
8126
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
8127
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
8128
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
|
|
8129
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
|
|
7923
8130
|
] }),
|
|
7924
|
-
/* @__PURE__ */ (0,
|
|
7925
|
-
|
|
8131
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
8132
|
+
import_lucide_react9.ArrowRight,
|
|
7926
8133
|
{
|
|
7927
8134
|
size: 16,
|
|
7928
8135
|
className: "shrink-0 text-muted-foreground",
|
|
7929
8136
|
"aria-hidden": true
|
|
7930
8137
|
}
|
|
7931
8138
|
),
|
|
7932
|
-
/* @__PURE__ */ (0,
|
|
7933
|
-
/* @__PURE__ */ (0,
|
|
7934
|
-
|
|
8139
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
8140
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
8141
|
+
import_lucide_react9.GalleryVertical,
|
|
7935
8142
|
{
|
|
7936
8143
|
size: 16,
|
|
7937
8144
|
className: "shrink-0 text-foreground",
|
|
7938
8145
|
"aria-hidden": true
|
|
7939
8146
|
}
|
|
7940
8147
|
),
|
|
7941
|
-
/* @__PURE__ */ (0,
|
|
8148
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
|
|
7942
8149
|
] })
|
|
7943
8150
|
] })
|
|
7944
8151
|
] });
|
|
7945
8152
|
}
|
|
7946
8153
|
|
|
7947
8154
|
// src/ui/link-modal/SectionTreeItem.tsx
|
|
7948
|
-
var
|
|
7949
|
-
var
|
|
8155
|
+
var import_lucide_react10 = require("lucide-react");
|
|
8156
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
7950
8157
|
function SectionTreeItem({
|
|
7951
8158
|
section,
|
|
7952
8159
|
onSelect,
|
|
7953
8160
|
selected
|
|
7954
8161
|
}) {
|
|
7955
8162
|
const interactive = Boolean(onSelect);
|
|
7956
|
-
return /* @__PURE__ */ (0,
|
|
7957
|
-
/* @__PURE__ */ (0,
|
|
8163
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
|
|
8164
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
7958
8165
|
"div",
|
|
7959
8166
|
{
|
|
7960
8167
|
className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
|
|
7961
8168
|
"aria-hidden": true
|
|
7962
8169
|
}
|
|
7963
8170
|
),
|
|
7964
|
-
/* @__PURE__ */ (0,
|
|
8171
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
7965
8172
|
"div",
|
|
7966
8173
|
{
|
|
7967
8174
|
role: interactive ? "button" : void 0,
|
|
@@ -7979,15 +8186,15 @@ function SectionTreeItem({
|
|
|
7979
8186
|
interactive && selected && "border-primary"
|
|
7980
8187
|
),
|
|
7981
8188
|
children: [
|
|
7982
|
-
/* @__PURE__ */ (0,
|
|
7983
|
-
|
|
8189
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
8190
|
+
import_lucide_react10.GalleryVertical,
|
|
7984
8191
|
{
|
|
7985
8192
|
size: 16,
|
|
7986
8193
|
className: "shrink-0 text-foreground",
|
|
7987
8194
|
"aria-hidden": true
|
|
7988
8195
|
}
|
|
7989
8196
|
),
|
|
7990
|
-
/* @__PURE__ */ (0,
|
|
8197
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
|
|
7991
8198
|
]
|
|
7992
8199
|
}
|
|
7993
8200
|
)
|
|
@@ -7999,10 +8206,10 @@ var import_react9 = require("react");
|
|
|
7999
8206
|
|
|
8000
8207
|
// src/ui/input.tsx
|
|
8001
8208
|
var React9 = __toESM(require("react"), 1);
|
|
8002
|
-
var
|
|
8209
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
8003
8210
|
var Input = React9.forwardRef(
|
|
8004
8211
|
({ className, type, ...props }, ref) => {
|
|
8005
|
-
return /* @__PURE__ */ (0,
|
|
8212
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
8006
8213
|
"input",
|
|
8007
8214
|
{
|
|
8008
8215
|
type,
|
|
@@ -8021,9 +8228,9 @@ Input.displayName = "Input";
|
|
|
8021
8228
|
|
|
8022
8229
|
// src/ui/label.tsx
|
|
8023
8230
|
var import_radix_ui7 = require("radix-ui");
|
|
8024
|
-
var
|
|
8231
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
8025
8232
|
function Label({ className, ...props }) {
|
|
8026
|
-
return /* @__PURE__ */ (0,
|
|
8233
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
8027
8234
|
import_radix_ui7.Label.Root,
|
|
8028
8235
|
{
|
|
8029
8236
|
"data-slot": "label",
|
|
@@ -8034,12 +8241,12 @@ function Label({ className, ...props }) {
|
|
|
8034
8241
|
}
|
|
8035
8242
|
|
|
8036
8243
|
// src/ui/link-modal/UrlOrPageInput.tsx
|
|
8037
|
-
var
|
|
8038
|
-
var
|
|
8244
|
+
var import_lucide_react11 = require("lucide-react");
|
|
8245
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
8039
8246
|
function FieldChevron({
|
|
8040
8247
|
onClick
|
|
8041
8248
|
}) {
|
|
8042
|
-
return /* @__PURE__ */ (0,
|
|
8249
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
8043
8250
|
"button",
|
|
8044
8251
|
{
|
|
8045
8252
|
type: "button",
|
|
@@ -8047,7 +8254,7 @@ function FieldChevron({
|
|
|
8047
8254
|
onClick,
|
|
8048
8255
|
"aria-label": "Open page list",
|
|
8049
8256
|
tabIndex: -1,
|
|
8050
|
-
children: /* @__PURE__ */ (0,
|
|
8257
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.ChevronDown, { size: 16 })
|
|
8051
8258
|
}
|
|
8052
8259
|
);
|
|
8053
8260
|
}
|
|
@@ -8108,19 +8315,19 @@ function UrlOrPageInput({
|
|
|
8108
8315
|
"data-ohw-link-field flex h-[36px] w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
|
|
8109
8316
|
urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
|
|
8110
8317
|
);
|
|
8111
|
-
return /* @__PURE__ */ (0,
|
|
8112
|
-
/* @__PURE__ */ (0,
|
|
8113
|
-
/* @__PURE__ */ (0,
|
|
8114
|
-
/* @__PURE__ */ (0,
|
|
8115
|
-
selectedPage ? /* @__PURE__ */ (0,
|
|
8116
|
-
|
|
8318
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
|
|
8319
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
|
|
8320
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { ref: rootRef, className: "relative w-full", children: [
|
|
8321
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
|
|
8322
|
+
selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
8323
|
+
import_lucide_react11.File,
|
|
8117
8324
|
{
|
|
8118
8325
|
size: 16,
|
|
8119
8326
|
className: "shrink-0 text-foreground",
|
|
8120
8327
|
"aria-hidden": true
|
|
8121
8328
|
}
|
|
8122
8329
|
) }) : null,
|
|
8123
|
-
readOnly ? /* @__PURE__ */ (0,
|
|
8330
|
+
readOnly ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
8124
8331
|
Input,
|
|
8125
8332
|
{
|
|
8126
8333
|
ref: inputRef,
|
|
@@ -8146,7 +8353,7 @@ function UrlOrPageInput({
|
|
|
8146
8353
|
)
|
|
8147
8354
|
}
|
|
8148
8355
|
),
|
|
8149
|
-
selectedPage && !readOnly ? /* @__PURE__ */ (0,
|
|
8356
|
+
selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
8150
8357
|
"button",
|
|
8151
8358
|
{
|
|
8152
8359
|
type: "button",
|
|
@@ -8154,26 +8361,26 @@ function UrlOrPageInput({
|
|
|
8154
8361
|
onMouseDown: clearSelection,
|
|
8155
8362
|
"aria-label": "Clear selected page",
|
|
8156
8363
|
tabIndex: -1,
|
|
8157
|
-
children: /* @__PURE__ */ (0,
|
|
8364
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.X, { size: 16, "aria-hidden": true })
|
|
8158
8365
|
}
|
|
8159
8366
|
) : null,
|
|
8160
|
-
!readOnly ? /* @__PURE__ */ (0,
|
|
8367
|
+
!readOnly ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
|
|
8161
8368
|
] }),
|
|
8162
|
-
dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0,
|
|
8369
|
+
dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
8163
8370
|
"div",
|
|
8164
8371
|
{
|
|
8165
8372
|
"data-ohw-link-page-dropdown": "",
|
|
8166
8373
|
className: "absolute left-0 right-0 top-[calc(100%+4px)] z-50 max-h-48 overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg",
|
|
8167
8374
|
onMouseDown: (e) => e.preventDefault(),
|
|
8168
|
-
children: filteredPages.map((page) => /* @__PURE__ */ (0,
|
|
8375
|
+
children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
8169
8376
|
"button",
|
|
8170
8377
|
{
|
|
8171
8378
|
type: "button",
|
|
8172
8379
|
className: "flex h-9 w-full items-center gap-2 border-0 bg-transparent px-3 text-left text-sm leading-5 text-foreground outline-none hover:bg-muted",
|
|
8173
8380
|
onClick: () => onPageSelect(page),
|
|
8174
8381
|
children: [
|
|
8175
|
-
/* @__PURE__ */ (0,
|
|
8176
|
-
/* @__PURE__ */ (0,
|
|
8382
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.File, { size: 16, className: "shrink-0", "aria-hidden": true }),
|
|
8383
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "truncate", children: page.title })
|
|
8177
8384
|
]
|
|
8178
8385
|
},
|
|
8179
8386
|
page.path
|
|
@@ -8181,34 +8388,34 @@ function UrlOrPageInput({
|
|
|
8181
8388
|
}
|
|
8182
8389
|
) : null
|
|
8183
8390
|
] }),
|
|
8184
|
-
urlError ? /* @__PURE__ */ (0,
|
|
8391
|
+
urlError ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
|
|
8185
8392
|
] });
|
|
8186
8393
|
}
|
|
8187
8394
|
|
|
8188
8395
|
// src/ui/link-modal/LinkEditorPanel.tsx
|
|
8189
|
-
var
|
|
8396
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
8190
8397
|
function LinkEditorPanel({ state, onClose }) {
|
|
8191
8398
|
const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
|
|
8192
|
-
return /* @__PURE__ */ (0,
|
|
8193
|
-
/* @__PURE__ */ (0,
|
|
8399
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
|
|
8400
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
8194
8401
|
"button",
|
|
8195
8402
|
{
|
|
8196
8403
|
type: "button",
|
|
8197
8404
|
className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50 h-7",
|
|
8198
8405
|
"aria-label": "Close",
|
|
8199
8406
|
onClick: onClose,
|
|
8200
|
-
children: /* @__PURE__ */ (0,
|
|
8407
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.X, { size: 16, "aria-hidden": true })
|
|
8201
8408
|
}
|
|
8202
8409
|
) }),
|
|
8203
|
-
/* @__PURE__ */ (0,
|
|
8204
|
-
/* @__PURE__ */ (0,
|
|
8205
|
-
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0,
|
|
8410
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
|
|
8411
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
|
|
8412
|
+
state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
8206
8413
|
DestinationBreadcrumb,
|
|
8207
8414
|
{
|
|
8208
8415
|
pageTitle: state.selectedPage.title,
|
|
8209
8416
|
sectionLabel: state.selectedSection.label
|
|
8210
8417
|
}
|
|
8211
|
-
) : /* @__PURE__ */ (0,
|
|
8418
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
8212
8419
|
UrlOrPageInput,
|
|
8213
8420
|
{
|
|
8214
8421
|
value: state.searchValue,
|
|
@@ -8221,8 +8428,8 @@ function LinkEditorPanel({ state, onClose }) {
|
|
|
8221
8428
|
urlError: state.urlError
|
|
8222
8429
|
}
|
|
8223
8430
|
),
|
|
8224
|
-
state.showChooseSection ? /* @__PURE__ */ (0,
|
|
8225
|
-
/* @__PURE__ */ (0,
|
|
8431
|
+
state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
|
|
8432
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
8226
8433
|
Button,
|
|
8227
8434
|
{
|
|
8228
8435
|
type: "button",
|
|
@@ -8233,15 +8440,15 @@ function LinkEditorPanel({ state, onClose }) {
|
|
|
8233
8440
|
children: "Choose a section"
|
|
8234
8441
|
}
|
|
8235
8442
|
),
|
|
8236
|
-
/* @__PURE__ */ (0,
|
|
8237
|
-
/* @__PURE__ */ (0,
|
|
8238
|
-
/* @__PURE__ */ (0,
|
|
8443
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
|
|
8444
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
|
|
8445
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: "Pick a section this link should scroll to." })
|
|
8239
8446
|
] })
|
|
8240
8447
|
] }) : null,
|
|
8241
|
-
state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0,
|
|
8448
|
+
state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
|
|
8242
8449
|
] }),
|
|
8243
|
-
/* @__PURE__ */ (0,
|
|
8244
|
-
/* @__PURE__ */ (0,
|
|
8450
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
|
|
8451
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
8245
8452
|
Button,
|
|
8246
8453
|
{
|
|
8247
8454
|
type: "button",
|
|
@@ -8256,7 +8463,7 @@ function LinkEditorPanel({ state, onClose }) {
|
|
|
8256
8463
|
children: state.secondaryLabel
|
|
8257
8464
|
}
|
|
8258
8465
|
),
|
|
8259
|
-
/* @__PURE__ */ (0,
|
|
8466
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
8260
8467
|
Button,
|
|
8261
8468
|
{
|
|
8262
8469
|
type: "button",
|
|
@@ -8277,9 +8484,9 @@ function LinkEditorPanel({ state, onClose }) {
|
|
|
8277
8484
|
// src/ui/link-modal/SectionPickerOverlay.tsx
|
|
8278
8485
|
var import_react10 = require("react");
|
|
8279
8486
|
var import_react_dom2 = require("react-dom");
|
|
8280
|
-
var
|
|
8487
|
+
var import_lucide_react13 = require("lucide-react");
|
|
8281
8488
|
var import_navigation2 = require("next/navigation");
|
|
8282
|
-
var
|
|
8489
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
8283
8490
|
var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
|
|
8284
8491
|
function rectsEqual(a, b) {
|
|
8285
8492
|
if (a.size !== b.size) return false;
|
|
@@ -8508,7 +8715,7 @@ function SectionPickerOverlay({
|
|
|
8508
8715
|
const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
|
|
8509
8716
|
if (!portalRoot) return null;
|
|
8510
8717
|
return (0, import_react_dom2.createPortal)(
|
|
8511
|
-
/* @__PURE__ */ (0,
|
|
8718
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
8512
8719
|
"div",
|
|
8513
8720
|
{
|
|
8514
8721
|
"data-ohw-section-picker": "",
|
|
@@ -8518,12 +8725,12 @@ function SectionPickerOverlay({
|
|
|
8518
8725
|
role: "dialog",
|
|
8519
8726
|
"aria-label": "Choose a section",
|
|
8520
8727
|
children: [
|
|
8521
|
-
/* @__PURE__ */ (0,
|
|
8728
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
8522
8729
|
"div",
|
|
8523
8730
|
{
|
|
8524
8731
|
className: "pointer-events-auto fixed left-5 z-[2]",
|
|
8525
8732
|
style: { top: chromeClip.top + 20 },
|
|
8526
|
-
children: /* @__PURE__ */ (0,
|
|
8733
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
8527
8734
|
Button,
|
|
8528
8735
|
{
|
|
8529
8736
|
type: "button",
|
|
@@ -8532,14 +8739,14 @@ function SectionPickerOverlay({
|
|
|
8532
8739
|
className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
|
|
8533
8740
|
onClick: onBack,
|
|
8534
8741
|
children: [
|
|
8535
|
-
/* @__PURE__ */ (0,
|
|
8742
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react13.ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
|
|
8536
8743
|
"Back"
|
|
8537
8744
|
]
|
|
8538
8745
|
}
|
|
8539
8746
|
)
|
|
8540
8747
|
}
|
|
8541
8748
|
),
|
|
8542
|
-
/* @__PURE__ */ (0,
|
|
8749
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
8543
8750
|
"div",
|
|
8544
8751
|
{
|
|
8545
8752
|
className: "pointer-events-none fixed left-1/2 z-[2] rounded-lg px-4 py-3 text-xs leading-4 tracking-[0.18px] text-white shadow-md",
|
|
@@ -8552,7 +8759,7 @@ function SectionPickerOverlay({
|
|
|
8552
8759
|
children: "Click on section to select"
|
|
8553
8760
|
}
|
|
8554
8761
|
),
|
|
8555
|
-
!isOnTargetPage ? /* @__PURE__ */ (0,
|
|
8762
|
+
!isOnTargetPage ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
8556
8763
|
"div",
|
|
8557
8764
|
{
|
|
8558
8765
|
className: "pointer-events-none fixed left-1/2 z-[1] -translate-x-1/2 rounded-md px-3 py-2 text-sm text-muted-foreground shadow-sm",
|
|
@@ -8560,14 +8767,14 @@ function SectionPickerOverlay({
|
|
|
8560
8767
|
children: "Loading page preview\u2026"
|
|
8561
8768
|
}
|
|
8562
8769
|
) : null,
|
|
8563
|
-
isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ (0,
|
|
8770
|
+
isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
|
|
8564
8771
|
isOnTargetPage ? liveSections.map((section) => {
|
|
8565
8772
|
const rect = rects.get(section.id);
|
|
8566
8773
|
if (!rect || rect.width <= 0 || rect.height <= 0) return null;
|
|
8567
8774
|
const isSelected = selectedId === section.id;
|
|
8568
8775
|
const isHovered = hoveredId === section.id;
|
|
8569
8776
|
const isLit = isSelected || isHovered;
|
|
8570
|
-
return /* @__PURE__ */ (0,
|
|
8777
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
8571
8778
|
"button",
|
|
8572
8779
|
{
|
|
8573
8780
|
type: "button",
|
|
@@ -8582,7 +8789,7 @@ function SectionPickerOverlay({
|
|
|
8582
8789
|
"aria-label": `Select section ${section.label}`,
|
|
8583
8790
|
onClick: () => handleSelect(section),
|
|
8584
8791
|
children: [
|
|
8585
|
-
isLit ? /* @__PURE__ */ (0,
|
|
8792
|
+
isLit ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
8586
8793
|
"span",
|
|
8587
8794
|
{
|
|
8588
8795
|
className: "pointer-events-none absolute",
|
|
@@ -8595,13 +8802,13 @@ function SectionPickerOverlay({
|
|
|
8595
8802
|
"aria-hidden": true
|
|
8596
8803
|
}
|
|
8597
8804
|
) : null,
|
|
8598
|
-
isSelected ? /* @__PURE__ */ (0,
|
|
8805
|
+
isSelected ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
8599
8806
|
"span",
|
|
8600
8807
|
{
|
|
8601
8808
|
className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
|
|
8602
8809
|
style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
|
|
8603
8810
|
"aria-hidden": true,
|
|
8604
|
-
children: /* @__PURE__ */ (0,
|
|
8811
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react13.Check, { className: "size-5" })
|
|
8605
8812
|
}
|
|
8606
8813
|
) : null
|
|
8607
8814
|
]
|
|
@@ -8781,7 +8988,7 @@ function useLinkModalState({
|
|
|
8781
8988
|
}
|
|
8782
8989
|
|
|
8783
8990
|
// src/ui/link-modal/LinkPopover.tsx
|
|
8784
|
-
var
|
|
8991
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
8785
8992
|
function postToParent(data) {
|
|
8786
8993
|
window.parent?.postMessage(data, "*");
|
|
8787
8994
|
}
|
|
@@ -8877,15 +9084,15 @@ function LinkPopover({
|
|
|
8877
9084
|
);
|
|
8878
9085
|
};
|
|
8879
9086
|
}, [open, sectionPickerActive]);
|
|
8880
|
-
return /* @__PURE__ */ (0,
|
|
8881
|
-
/* @__PURE__ */ (0,
|
|
9087
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
|
|
9088
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
8882
9089
|
Dialog2,
|
|
8883
9090
|
{
|
|
8884
9091
|
open: open && !sectionPickerActive,
|
|
8885
9092
|
onOpenChange: (next) => {
|
|
8886
9093
|
if (!next) onClose?.();
|
|
8887
9094
|
},
|
|
8888
|
-
children: /* @__PURE__ */ (0,
|
|
9095
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
8889
9096
|
DialogContent,
|
|
8890
9097
|
{
|
|
8891
9098
|
ref: panelRef,
|
|
@@ -8895,12 +9102,12 @@ function LinkPopover({
|
|
|
8895
9102
|
"data-ohw-bridge": "",
|
|
8896
9103
|
showCloseButton: false,
|
|
8897
9104
|
className: "gap-0 p-0 w-full max-w-[448px] pointer-events-auto z-[2147483646] overflow-visible",
|
|
8898
|
-
children: /* @__PURE__ */ (0,
|
|
9105
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(LinkEditorPanel, { state, onClose })
|
|
8899
9106
|
}
|
|
8900
9107
|
)
|
|
8901
9108
|
}
|
|
8902
9109
|
),
|
|
8903
|
-
sectionPickerActive && state.selectedPage ? /* @__PURE__ */ (0,
|
|
9110
|
+
sectionPickerActive && state.selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
8904
9111
|
SectionPickerOverlay,
|
|
8905
9112
|
{
|
|
8906
9113
|
pagePath: state.selectedPage.path,
|
|
@@ -10126,13 +10333,14 @@ function listSocialItems(row) {
|
|
|
10126
10333
|
return isSocialItem(anchor) ? anchor : null;
|
|
10127
10334
|
}).filter((item) => item !== null);
|
|
10128
10335
|
}
|
|
10129
|
-
function socialRowUnit(item) {
|
|
10130
|
-
const row = findSocialsRow(item);
|
|
10336
|
+
function socialRowUnit(item, knownRow) {
|
|
10337
|
+
const row = knownRow ?? findSocialsRow(item);
|
|
10338
|
+
if (!row || !row.contains(item)) return null;
|
|
10131
10339
|
let node = item;
|
|
10132
10340
|
while (node.parentElement && node.parentElement !== row) {
|
|
10133
10341
|
node = node.parentElement;
|
|
10134
10342
|
}
|
|
10135
|
-
return node;
|
|
10343
|
+
return node.parentElement === row ? node : null;
|
|
10136
10344
|
}
|
|
10137
10345
|
function listSocialsRows(root = document) {
|
|
10138
10346
|
const rows = /* @__PURE__ */ new Set();
|
|
@@ -10151,7 +10359,8 @@ function markSocialsRows(root = document) {
|
|
|
10151
10359
|
listSocialsRows(root).forEach((row) => {
|
|
10152
10360
|
row.setAttribute(SOCIALS_ROW_ATTR, "");
|
|
10153
10361
|
const items = listSocialItems(row);
|
|
10154
|
-
|
|
10362
|
+
const firstUnit = items[0] ? socialRowUnit(items[0], row) : null;
|
|
10363
|
+
if (firstUnit) rowTemplates.set(rowKeyOf(row), firstUnit.outerHTML);
|
|
10155
10364
|
items.forEach((item, index) => {
|
|
10156
10365
|
item.setAttribute(SOCIALS_ITEM_ATTR, String(index));
|
|
10157
10366
|
const iconKey = socialIconKey(item);
|
|
@@ -10295,7 +10504,8 @@ function removeSocialItem(item, content) {
|
|
|
10295
10504
|
const previousContent = Object.fromEntries(
|
|
10296
10505
|
removedKeys.filter((key) => key in content).map((key) => [key, content[key]])
|
|
10297
10506
|
);
|
|
10298
|
-
const unit = socialRowUnit(item);
|
|
10507
|
+
const unit = socialRowUnit(item, row);
|
|
10508
|
+
if (!unit) return null;
|
|
10299
10509
|
const nextSibling = unit.nextElementSibling;
|
|
10300
10510
|
unit.remove();
|
|
10301
10511
|
markSocialsRows(row.ownerDocument);
|
|
@@ -10318,7 +10528,9 @@ function applySocialsOrder(order, root = document) {
|
|
|
10318
10528
|
const byKey = new Map(listSocialItems(row).map((item) => [socialHrefKey(item), item]));
|
|
10319
10529
|
wanted.forEach((key) => {
|
|
10320
10530
|
const item = byKey.get(key);
|
|
10321
|
-
if (item)
|
|
10531
|
+
if (!item) return;
|
|
10532
|
+
const unit = socialRowUnit(item, row);
|
|
10533
|
+
if (unit) row.appendChild(unit);
|
|
10322
10534
|
});
|
|
10323
10535
|
});
|
|
10324
10536
|
markSocialsRows(root);
|
|
@@ -10348,7 +10560,7 @@ function reconcileSocialsFromContent(content, root = document) {
|
|
|
10348
10560
|
});
|
|
10349
10561
|
if (surviving.length) {
|
|
10350
10562
|
present.forEach((item) => {
|
|
10351
|
-
if (!surviving.includes(item)) socialRowUnit(item)
|
|
10563
|
+
if (!surviving.includes(item)) socialRowUnit(item)?.remove();
|
|
10352
10564
|
});
|
|
10353
10565
|
}
|
|
10354
10566
|
});
|
|
@@ -11327,296 +11539,42 @@ function deleteFooterColumn(column) {
|
|
|
11327
11539
|
};
|
|
11328
11540
|
}
|
|
11329
11541
|
|
|
11330
|
-
// src/lib/
|
|
11331
|
-
|
|
11332
|
-
|
|
11333
|
-
|
|
11334
|
-
|
|
11335
|
-
|
|
11336
|
-
|
|
11337
|
-
|
|
11338
|
-
function resolveLogoDisplayText(text) {
|
|
11339
|
-
const trimmed = (text ?? "").trim();
|
|
11340
|
-
return trimmed || PLACEHOLDER_BUSINESS_NAME;
|
|
11341
|
-
}
|
|
11342
|
-
function isFooterLogoRoot(root) {
|
|
11343
|
-
return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
|
|
11344
|
-
}
|
|
11345
|
-
function imageKeyForRoot(root) {
|
|
11346
|
-
return isFooterLogoRoot(root) ? "footer-logo" : "nav-logo-image";
|
|
11347
|
-
}
|
|
11348
|
-
function textKeyForRoot(root) {
|
|
11349
|
-
return isFooterLogoRoot(root) ? "footer-logo-text" : "nav-logo-text";
|
|
11350
|
-
}
|
|
11351
|
-
function ensureLogoHrefKey(root) {
|
|
11352
|
-
if (!(root instanceof HTMLAnchorElement)) return;
|
|
11353
|
-
if (root.hasAttribute("data-ohw-href-key")) return;
|
|
11354
|
-
root.setAttribute("data-ohw-href-key", isFooterLogoRoot(root) ? "footer-logo-href" : "nav-logo-href");
|
|
11355
|
-
}
|
|
11356
|
-
function applyLogoIdentity(text, isPlaceholder) {
|
|
11357
|
-
const display = resolveLogoDisplayText(text);
|
|
11358
|
-
const placeholder = isPlaceholder || !text.trim() || display === PLACEHOLDER_BUSINESS_NAME;
|
|
11359
|
-
for (const key of LOGO_TEXT_KEYS) {
|
|
11360
|
-
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
11361
|
-
if (el.textContent !== display) el.textContent = display;
|
|
11362
|
-
});
|
|
11363
|
-
}
|
|
11364
|
-
for (const key of LOGO_IMAGE_KEYS) {
|
|
11365
|
-
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
11366
|
-
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
11367
|
-
if (img) img.alt = display;
|
|
11368
|
-
});
|
|
11369
|
-
}
|
|
11370
|
-
document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((el) => {
|
|
11371
|
-
if (placeholder) el.setAttribute("data-ohw-placeholder", "");
|
|
11372
|
-
else el.removeAttribute("data-ohw-placeholder");
|
|
11373
|
-
});
|
|
11374
|
-
return display;
|
|
11375
|
-
}
|
|
11376
|
-
function applyLogoImage(url, alt) {
|
|
11377
|
-
const displayAlt = resolveLogoDisplayText(alt);
|
|
11378
|
-
document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
|
|
11379
|
-
ensureLogoHrefKey(root);
|
|
11380
|
-
const imageKey = imageKeyForRoot(root);
|
|
11381
|
-
const textKey = textKeyForRoot(root);
|
|
11382
|
-
let img = root.querySelector(`img[data-ohw-key="${imageKey}"]`) ?? (root.querySelector(`[data-ohw-key="${imageKey}"]`) instanceof HTMLImageElement ? root.querySelector(`[data-ohw-key="${imageKey}"]`) : null) ?? root.querySelector("img");
|
|
11383
|
-
let textEl = root.querySelector(`[data-ohw-key="${textKey}"]`) ?? root.querySelector('[data-ohw-key="logo-text"]');
|
|
11384
|
-
if (url) {
|
|
11385
|
-
if (!img) {
|
|
11386
|
-
img = document.createElement("img");
|
|
11387
|
-
img.setAttribute("data-ohw-editable", "image");
|
|
11388
|
-
img.setAttribute("data-ohw-key", imageKey);
|
|
11389
|
-
img.alt = displayAlt;
|
|
11390
|
-
img.style.height = "";
|
|
11391
|
-
img.style.maxHeight = "none";
|
|
11392
|
-
img.style.width = "auto";
|
|
11393
|
-
img.style.display = "block";
|
|
11394
|
-
img.style.objectFit = "contain";
|
|
11395
|
-
root.insertBefore(img, root.firstChild);
|
|
11396
|
-
} else {
|
|
11397
|
-
img.setAttribute("data-ohw-editable", "image");
|
|
11398
|
-
img.setAttribute("data-ohw-key", imageKey);
|
|
11399
|
-
}
|
|
11400
|
-
img.removeAttribute("srcset");
|
|
11401
|
-
img.removeAttribute("sizes");
|
|
11402
|
-
img.src = url;
|
|
11403
|
-
img.alt = displayAlt;
|
|
11404
|
-
img.style.display = "block";
|
|
11405
|
-
if (textEl) textEl.style.display = "none";
|
|
11406
|
-
root.removeAttribute("data-ohw-placeholder");
|
|
11407
|
-
return;
|
|
11408
|
-
}
|
|
11409
|
-
if (img) {
|
|
11410
|
-
img.removeAttribute("src");
|
|
11411
|
-
img.removeAttribute("srcset");
|
|
11412
|
-
img.removeAttribute("sizes");
|
|
11413
|
-
img.alt = displayAlt;
|
|
11414
|
-
img.style.display = "none";
|
|
11415
|
-
}
|
|
11416
|
-
if (!textEl) {
|
|
11417
|
-
textEl = document.createElement("span");
|
|
11418
|
-
textEl.setAttribute("data-ohw-editable", "plain");
|
|
11419
|
-
textEl.setAttribute("data-ohw-key", textKey);
|
|
11420
|
-
root.appendChild(textEl);
|
|
11421
|
-
}
|
|
11422
|
-
textEl.style.display = "";
|
|
11423
|
-
if (textEl.textContent !== displayAlt) textEl.textContent = displayAlt;
|
|
11424
|
-
if (!displayAlt.trim() || displayAlt === PLACEHOLDER_BUSINESS_NAME) {
|
|
11425
|
-
root.setAttribute("data-ohw-placeholder", "");
|
|
11426
|
-
} else {
|
|
11427
|
-
root.removeAttribute("data-ohw-placeholder");
|
|
11428
|
-
}
|
|
11429
|
-
});
|
|
11430
|
-
}
|
|
11431
|
-
function applyLogoHref(href) {
|
|
11432
|
-
const target = href.trim() || "/";
|
|
11433
|
-
document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
|
|
11434
|
-
ensureLogoHrefKey(root);
|
|
11435
|
-
if (root instanceof HTMLAnchorElement) {
|
|
11436
|
-
root.setAttribute("href", target);
|
|
11437
|
-
}
|
|
11438
|
-
});
|
|
11439
|
-
for (const key of LOGO_HREF_KEYS) setStoredLinkHref(key, target);
|
|
11440
|
-
}
|
|
11441
|
-
function readLogoIdentityFromDom() {
|
|
11442
|
-
let imageUrl = null;
|
|
11443
|
-
for (const key of LOGO_IMAGE_KEYS) {
|
|
11444
|
-
const el = document.querySelector(`[data-ohw-key="${key}"]`);
|
|
11445
|
-
const img = el instanceof HTMLImageElement ? el : el?.querySelector("img");
|
|
11446
|
-
const attrSrc = img?.getAttribute("src")?.trim() ?? "";
|
|
11447
|
-
if (attrSrc && !attrSrc.startsWith("data:") && img && img.style.display !== "none") {
|
|
11448
|
-
imageUrl = img.currentSrc || img.src;
|
|
11449
|
-
break;
|
|
11450
|
-
}
|
|
11451
|
-
}
|
|
11452
|
-
let text = PLACEHOLDER_BUSINESS_NAME;
|
|
11453
|
-
let isPlaceholder = true;
|
|
11454
|
-
for (const key of LOGO_TEXT_KEYS) {
|
|
11455
|
-
const el = document.querySelector(`[data-ohw-key="${key}"]`);
|
|
11456
|
-
if (el?.textContent?.trim()) {
|
|
11457
|
-
text = el.textContent.trim();
|
|
11458
|
-
const logoRoot2 = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
|
|
11459
|
-
isPlaceholder = logoRoot2?.hasAttribute("data-ohw-placeholder") === true || text === PLACEHOLDER_BUSINESS_NAME;
|
|
11460
|
-
break;
|
|
11461
|
-
}
|
|
11462
|
-
}
|
|
11463
|
-
if (imageUrl) {
|
|
11464
|
-
const logoImg = document.querySelector(
|
|
11465
|
-
'[data-ohw-key="nav-logo-image"], [data-ohw-key="footer-logo"]'
|
|
11466
|
-
);
|
|
11467
|
-
const alt = logoImg?.alt?.trim() || text;
|
|
11468
|
-
isPlaceholder = false;
|
|
11469
|
-
const hrefEl = document.querySelector(
|
|
11470
|
-
'a[data-ohw-role="logo"], a[data-ohw-logo], [data-ohw-role="logo"]'
|
|
11471
|
-
);
|
|
11472
|
-
const href2 = (hrefEl instanceof HTMLAnchorElement ? hrefEl.getAttribute("href") : null) || hrefEl?.closest("a")?.getAttribute("href") || "/";
|
|
11473
|
-
return { text, isPlaceholder, imageUrl, href: href2, alt };
|
|
11474
|
-
}
|
|
11475
|
-
const logoRoot = document.querySelector('[data-ohw-role="logo"], [data-ohw-logo]');
|
|
11476
|
-
const href = (logoRoot instanceof HTMLAnchorElement ? logoRoot.getAttribute("href") : null) || logoRoot?.closest("a")?.getAttribute("href") || "/";
|
|
11477
|
-
return { text, isPlaceholder, imageUrl: null, href, alt: text };
|
|
11478
|
-
}
|
|
11479
|
-
function applyLogoFromContent(content) {
|
|
11480
|
-
const hasLogoIdentity = LOGO_PLACEHOLDER_KEY in content || LOGO_TEXT_KEYS.some((key) => key in content) || LOGO_IMAGE_KEYS.some((key) => key in content) || LOGO_ALT_KEY in content || LOGO_HREF_KEYS.some((key) => key in content);
|
|
11481
|
-
if (!hasLogoIdentity) return false;
|
|
11482
|
-
const logoText = content[LOGO_TEXT_KEYS[0]] ?? content[LOGO_TEXT_KEYS[1]] ?? readLogoIdentityFromDom().text;
|
|
11483
|
-
const logoAlt = content[LOGO_ALT_KEY] ?? logoText;
|
|
11484
|
-
const rawLogoImage = content[LOGO_IMAGE_URL_KEY] ?? content["footer-logo"] ?? content["footer-logo-image"] ?? null;
|
|
11485
|
-
const logoImageUrl = typeof rawLogoImage === "string" && rawLogoImage.trim() ? rawLogoImage.trim() : null;
|
|
11486
|
-
const imageExplicitlyCleared = LOGO_IMAGE_KEYS.some((key) => key in content) && !logoImageUrl;
|
|
11487
|
-
const logoIsPlaceholder = LOGO_PLACEHOLDER_KEY in content ? content[LOGO_PLACEHOLDER_KEY] !== "false" : !logoImageUrl && (!logoText.trim() || logoText === PLACEHOLDER_BUSINESS_NAME);
|
|
11488
|
-
if (logoImageUrl) {
|
|
11489
|
-
applyLogoImage(logoImageUrl, logoAlt);
|
|
11490
|
-
} else {
|
|
11491
|
-
if (imageExplicitlyCleared) applyLogoImage(null, logoAlt);
|
|
11492
|
-
applyLogoIdentity(logoText, logoIsPlaceholder);
|
|
11493
|
-
}
|
|
11494
|
-
const logoHref = content["nav-logo-href"] ?? content["footer-logo-href"] ?? content["logo-href"];
|
|
11495
|
-
if (typeof logoHref === "string" && logoHref.trim()) {
|
|
11496
|
-
applyLogoHref(logoHref);
|
|
11497
|
-
}
|
|
11498
|
-
return true;
|
|
11499
|
-
}
|
|
11500
|
-
|
|
11501
|
-
// src/lib/logo-size.ts
|
|
11502
|
-
var LOGO_SIZE_DEFAULTS = {
|
|
11503
|
-
navbar: 28,
|
|
11504
|
-
footer: 32
|
|
11505
|
-
};
|
|
11506
|
-
var LOGO_SIZE_MIN = 16;
|
|
11507
|
-
var LOGO_SIZE_MAX = 80;
|
|
11508
|
-
var LOGO_SIZE_DESKTOP_KEYS = {
|
|
11509
|
-
navbar: "nav-logo-size",
|
|
11510
|
-
footer: "footer-logo-size"
|
|
11511
|
-
};
|
|
11512
|
-
var LOGO_SIZE_MOBILE_KEYS = {
|
|
11513
|
-
navbar: "nav-logo-size-mobile",
|
|
11514
|
-
footer: "footer-logo-size-mobile"
|
|
11515
|
-
};
|
|
11516
|
-
var LOGO_SIZE_KEYS = [
|
|
11517
|
-
LOGO_SIZE_DESKTOP_KEYS.navbar,
|
|
11518
|
-
LOGO_SIZE_DESKTOP_KEYS.footer,
|
|
11519
|
-
LOGO_SIZE_MOBILE_KEYS.navbar,
|
|
11520
|
-
LOGO_SIZE_MOBILE_KEYS.footer
|
|
11521
|
-
];
|
|
11522
|
-
function isFooterLogoRoot2(root) {
|
|
11523
|
-
return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
|
|
11524
|
-
}
|
|
11525
|
-
function getLogoPlacement(root) {
|
|
11526
|
-
return isFooterLogoRoot2(root) ? "footer" : "navbar";
|
|
11527
|
-
}
|
|
11528
|
-
function parseLogoSizePx(raw, fallback) {
|
|
11529
|
-
if (raw == null || raw === "") return fallback;
|
|
11530
|
-
const n = Number.parseFloat(raw);
|
|
11531
|
-
if (!Number.isFinite(n)) return fallback;
|
|
11532
|
-
return Math.min(LOGO_SIZE_MAX, Math.max(LOGO_SIZE_MIN, Math.round(n)));
|
|
11533
|
-
}
|
|
11534
|
-
function isMobileLogoSizeFollowing(content, placement) {
|
|
11535
|
-
const raw = content[LOGO_SIZE_MOBILE_KEYS[placement]];
|
|
11536
|
-
return raw == null || raw.trim() === "";
|
|
11537
|
-
}
|
|
11538
|
-
function resolveDesktopLogoSize(content, placement) {
|
|
11539
|
-
return parseLogoSizePx(content[LOGO_SIZE_DESKTOP_KEYS[placement]], LOGO_SIZE_DEFAULTS[placement]);
|
|
11540
|
-
}
|
|
11541
|
-
function resolveMobileLogoSize(content, placement) {
|
|
11542
|
-
if (isMobileLogoSizeFollowing(content, placement)) {
|
|
11543
|
-
return resolveDesktopLogoSize(content, placement);
|
|
11544
|
-
}
|
|
11545
|
-
return parseLogoSizePx(
|
|
11546
|
-
content[LOGO_SIZE_MOBILE_KEYS[placement]],
|
|
11547
|
-
resolveDesktopLogoSize(content, placement)
|
|
11548
|
-
);
|
|
11542
|
+
// src/lib/add-footer-column.ts
|
|
11543
|
+
function buildFooterColumnEditContentPatch(result) {
|
|
11544
|
+
return {
|
|
11545
|
+
[result.headingKey]: result.heading,
|
|
11546
|
+
[result.hrefKey]: result.href,
|
|
11547
|
+
[result.labelKey]: result.label,
|
|
11548
|
+
[FOOTER_ORDER_KEY]: JSON.stringify(result.order)
|
|
11549
|
+
};
|
|
11549
11550
|
}
|
|
11550
|
-
function
|
|
11551
|
-
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11551
|
+
function addFooterColumnWithPersist({
|
|
11552
|
+
postToParent: postToParent2
|
|
11553
|
+
}) {
|
|
11554
|
+
if (!canAddFooterColumn()) {
|
|
11555
|
+
postToParent2({
|
|
11556
|
+
type: "ow:toast",
|
|
11557
|
+
title: `Maximum ${MAX_FOOTER_COLUMNS} columns`,
|
|
11558
|
+
toastType: "error"
|
|
11559
|
+
});
|
|
11560
|
+
return null;
|
|
11556
11561
|
}
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
}
|
|
11564
|
-
function applyLogoSizes(content) {
|
|
11565
|
-
document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
|
|
11566
|
-
const placement = getLogoPlacement(root);
|
|
11567
|
-
const desktop = resolveDesktopLogoSize(content, placement);
|
|
11568
|
-
const following = isMobileLogoSizeFollowing(content, placement);
|
|
11569
|
-
const mobile = following ? desktop : resolveMobileLogoSize(content, placement);
|
|
11570
|
-
setRootSizeVars(root, desktop, mobile, following);
|
|
11571
|
-
});
|
|
11572
|
-
}
|
|
11573
|
-
function applyLogoSizeToPlacement(placement, desktopPx, mobilePx, following) {
|
|
11574
|
-
document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
|
|
11575
|
-
if (getLogoPlacement(root) !== placement) return;
|
|
11576
|
-
setRootSizeVars(root, desktopPx, mobilePx, following);
|
|
11562
|
+
const result = insertFooterColumn();
|
|
11563
|
+
const patch = buildFooterColumnEditContentPatch(result);
|
|
11564
|
+
setStoredLinkHref(result.hrefKey, result.href);
|
|
11565
|
+
postToParent2({
|
|
11566
|
+
type: "ow:change",
|
|
11567
|
+
nodes: Object.entries(patch).map(([key, text]) => ({ key, text }))
|
|
11577
11568
|
});
|
|
11578
|
-
}
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
const img = logoEl.querySelector('img[data-ohw-key="nav-logo-image"], img[data-ohw-key="footer-logo"], img[data-ohw-key="footer-logo-image"]') ?? logoEl.querySelector("img");
|
|
11582
|
-
if (!img) return false;
|
|
11583
|
-
const src = img.getAttribute("src")?.trim() ?? "";
|
|
11584
|
-
if (!src || src.startsWith("data:")) return false;
|
|
11585
|
-
if (img.style.display === "none") return false;
|
|
11586
|
-
return true;
|
|
11587
|
-
}
|
|
11588
|
-
function getLogoInteractionRect(logoEl) {
|
|
11589
|
-
if (logoHasUploadedImage(logoEl)) {
|
|
11590
|
-
const img = logoEl.querySelector('img[data-ohw-key="nav-logo-image"], img[data-ohw-key="footer-logo"], img[data-ohw-key="footer-logo-image"]') ?? logoEl.querySelector("img");
|
|
11591
|
-
if (img) {
|
|
11592
|
-
const r2 = img.getBoundingClientRect();
|
|
11593
|
-
if (r2.width > 0 && r2.height > 0) return r2;
|
|
11594
|
-
}
|
|
11595
|
-
}
|
|
11596
|
-
const text = logoEl.querySelector(
|
|
11597
|
-
'[data-ohw-key="nav-logo-text"], [data-ohw-key="footer-logo-text"]'
|
|
11598
|
-
);
|
|
11599
|
-
if (text) {
|
|
11600
|
-
const style = window.getComputedStyle(text);
|
|
11601
|
-
if (style.display !== "none" && style.visibility !== "hidden") {
|
|
11602
|
-
const r2 = text.getBoundingClientRect();
|
|
11603
|
-
if (r2.width > 0 && r2.height > 0) return r2;
|
|
11604
|
-
}
|
|
11605
|
-
}
|
|
11606
|
-
return logoEl.getBoundingClientRect();
|
|
11607
|
-
}
|
|
11608
|
-
function readLogoSizeState(content, placement) {
|
|
11609
|
-
const desktopPx = resolveDesktopLogoSize(content, placement);
|
|
11610
|
-
const mobileFollowing = isMobileLogoSizeFollowing(content, placement);
|
|
11611
|
-
const mobilePx = mobileFollowing ? desktopPx : resolveMobileLogoSize(content, placement);
|
|
11612
|
-
return { desktopPx, mobilePx, mobileFollowing };
|
|
11569
|
+
postToParent2({ type: "ow:toast", title: "Item added", toastType: "success" });
|
|
11570
|
+
enforceLinkHrefs();
|
|
11571
|
+
return result;
|
|
11613
11572
|
}
|
|
11614
11573
|
|
|
11615
11574
|
// src/lib/site-wide-scope.ts
|
|
11616
11575
|
function getLogoElement(el) {
|
|
11617
11576
|
const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
|
|
11618
11577
|
if (marked) return marked;
|
|
11619
|
-
if (el.closest('[data-ohw-editable="icon"]')) return null;
|
|
11620
11578
|
const root = el.closest("nav, [data-ohw-nav-root], footer");
|
|
11621
11579
|
if (!root) return null;
|
|
11622
11580
|
const anchor = el.closest("a");
|
|
@@ -11650,42 +11608,10 @@ function isSiteWideScopeActive(args) {
|
|
|
11650
11608
|
return isSiteWideElement(args.selected) || isSiteWideElement(args.hoveredItem) || isSiteWideElement(args.hoveredNavContainer) || isSiteWideElement(args.active);
|
|
11651
11609
|
}
|
|
11652
11610
|
|
|
11653
|
-
// src/lib/add-footer-column.ts
|
|
11654
|
-
function buildFooterColumnEditContentPatch(result) {
|
|
11655
|
-
return {
|
|
11656
|
-
[result.headingKey]: result.heading,
|
|
11657
|
-
[result.hrefKey]: result.href,
|
|
11658
|
-
[result.labelKey]: result.label,
|
|
11659
|
-
[FOOTER_ORDER_KEY]: JSON.stringify(result.order)
|
|
11660
|
-
};
|
|
11661
|
-
}
|
|
11662
|
-
function addFooterColumnWithPersist({
|
|
11663
|
-
postToParent: postToParent2
|
|
11664
|
-
}) {
|
|
11665
|
-
if (!canAddFooterColumn()) {
|
|
11666
|
-
postToParent2({
|
|
11667
|
-
type: "ow:toast",
|
|
11668
|
-
title: `Maximum ${MAX_FOOTER_COLUMNS} columns`,
|
|
11669
|
-
toastType: "error"
|
|
11670
|
-
});
|
|
11671
|
-
return null;
|
|
11672
|
-
}
|
|
11673
|
-
const result = insertFooterColumn();
|
|
11674
|
-
const patch = buildFooterColumnEditContentPatch(result);
|
|
11675
|
-
setStoredLinkHref(result.hrefKey, result.href);
|
|
11676
|
-
postToParent2({
|
|
11677
|
-
type: "ow:change",
|
|
11678
|
-
nodes: Object.entries(patch).map(([key, text]) => ({ key, text }))
|
|
11679
|
-
});
|
|
11680
|
-
postToParent2({ type: "ow:toast", title: "Item added", toastType: "success" });
|
|
11681
|
-
enforceLinkHrefs();
|
|
11682
|
-
return result;
|
|
11683
|
-
}
|
|
11684
|
-
|
|
11685
11611
|
// src/ui/FloatingPanel.tsx
|
|
11686
11612
|
var import_react13 = require("react");
|
|
11687
|
-
var
|
|
11688
|
-
var
|
|
11613
|
+
var import_lucide_react14 = require("lucide-react");
|
|
11614
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
11689
11615
|
var PANEL_WIDTH = 256;
|
|
11690
11616
|
var EDGE_MARGIN = 16;
|
|
11691
11617
|
function getVisibleClip(parentScroll) {
|
|
@@ -11800,7 +11726,7 @@ function FloatingPanel({
|
|
|
11800
11726
|
}, [open]);
|
|
11801
11727
|
(0, import_react13.useEffect)(() => () => document.documentElement.removeAttribute("data-ohw-panel-dragging"), []);
|
|
11802
11728
|
if (!open) return null;
|
|
11803
|
-
return /* @__PURE__ */ (0,
|
|
11729
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
11804
11730
|
"div",
|
|
11805
11731
|
{
|
|
11806
11732
|
ref: panelRef,
|
|
@@ -11817,7 +11743,7 @@ function FloatingPanel({
|
|
|
11817
11743
|
onPointerDown: (e) => e.stopPropagation(),
|
|
11818
11744
|
onClick: (e) => e.stopPropagation(),
|
|
11819
11745
|
children: [
|
|
11820
|
-
/* @__PURE__ */ (0,
|
|
11746
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
11821
11747
|
"div",
|
|
11822
11748
|
{
|
|
11823
11749
|
"data-ohw-floating-panel-header": "",
|
|
@@ -11827,14 +11753,14 @@ function FloatingPanel({
|
|
|
11827
11753
|
onPointerUp: endDrag,
|
|
11828
11754
|
onPointerCancel: endDrag,
|
|
11829
11755
|
children: [
|
|
11830
|
-
/* @__PURE__ */ (0,
|
|
11831
|
-
/* @__PURE__ */ (0,
|
|
11832
|
-
icon ? /* @__PURE__ */ (0,
|
|
11833
|
-
/* @__PURE__ */ (0,
|
|
11756
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: [
|
|
11757
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
11758
|
+
icon ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "shrink-0 text-foreground", children: icon }) : null,
|
|
11759
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "min-w-0 flex-1 text-lg font-semibold leading-7 text-foreground", children: title })
|
|
11834
11760
|
] }),
|
|
11835
|
-
context ? /* @__PURE__ */ (0,
|
|
11761
|
+
context ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "w-full text-sm leading-5 text-muted-foreground", children: context }) : null
|
|
11836
11762
|
] }),
|
|
11837
|
-
/* @__PURE__ */ (0,
|
|
11763
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
11838
11764
|
"button",
|
|
11839
11765
|
{
|
|
11840
11766
|
type: "button",
|
|
@@ -11846,13 +11772,13 @@ function FloatingPanel({
|
|
|
11846
11772
|
onClose();
|
|
11847
11773
|
},
|
|
11848
11774
|
onPointerDown: (e) => e.stopPropagation(),
|
|
11849
|
-
children: /* @__PURE__ */ (0,
|
|
11775
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react14.X, { size: 16, "aria-hidden": true })
|
|
11850
11776
|
}
|
|
11851
11777
|
)
|
|
11852
11778
|
]
|
|
11853
11779
|
}
|
|
11854
11780
|
),
|
|
11855
|
-
/* @__PURE__ */ (0,
|
|
11781
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
11856
11782
|
"div",
|
|
11857
11783
|
{
|
|
11858
11784
|
"data-ohw-floating-panel-body": "",
|
|
@@ -11865,117 +11791,6 @@ function FloatingPanel({
|
|
|
11865
11791
|
);
|
|
11866
11792
|
}
|
|
11867
11793
|
|
|
11868
|
-
// src/ui/logo-size-panel.tsx
|
|
11869
|
-
var import_lucide_react14 = require("lucide-react");
|
|
11870
|
-
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
11871
|
-
function SizeSlider({
|
|
11872
|
-
value,
|
|
11873
|
-
onChange
|
|
11874
|
-
}) {
|
|
11875
|
-
const pct = (value - LOGO_SIZE_MIN) / (LOGO_SIZE_MAX - LOGO_SIZE_MIN) * 100;
|
|
11876
|
-
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex w-full flex-col gap-3", children: [
|
|
11877
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex w-full items-center gap-2 text-sm font-medium leading-5", children: [
|
|
11878
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "min-w-0 flex-1 text-foreground", children: "Size" }),
|
|
11879
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "shrink-0 whitespace-nowrap text-muted-foreground", children: [
|
|
11880
|
-
value,
|
|
11881
|
-
" px"
|
|
11882
|
-
] })
|
|
11883
|
-
] }),
|
|
11884
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "relative h-2 w-full rounded-full bg-primary-50", children: [
|
|
11885
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
11886
|
-
"div",
|
|
11887
|
-
{
|
|
11888
|
-
className: "absolute inset-y-0 left-0 rounded-full bg-primary",
|
|
11889
|
-
style: { width: `${pct}%` }
|
|
11890
|
-
}
|
|
11891
|
-
),
|
|
11892
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
11893
|
-
"input",
|
|
11894
|
-
{
|
|
11895
|
-
type: "range",
|
|
11896
|
-
min: LOGO_SIZE_MIN,
|
|
11897
|
-
max: LOGO_SIZE_MAX,
|
|
11898
|
-
step: 1,
|
|
11899
|
-
value,
|
|
11900
|
-
"aria-label": "Logo size",
|
|
11901
|
-
className: cn(
|
|
11902
|
-
"absolute inset-0 h-full w-full cursor-pointer appearance-none bg-transparent",
|
|
11903
|
-
"[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:size-5",
|
|
11904
|
-
"[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2",
|
|
11905
|
-
"[&::-webkit-slider-thumb]:border-primary [&::-webkit-slider-thumb]:bg-background",
|
|
11906
|
-
"[&::-moz-range-thumb]:size-5 [&::-moz-range-thumb]:rounded-full",
|
|
11907
|
-
"[&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-primary",
|
|
11908
|
-
"[&::-moz-range-thumb]:bg-background"
|
|
11909
|
-
),
|
|
11910
|
-
onChange: (e) => onChange(Number(e.target.value))
|
|
11911
|
-
}
|
|
11912
|
-
)
|
|
11913
|
-
] })
|
|
11914
|
-
] });
|
|
11915
|
-
}
|
|
11916
|
-
function LogoSizePanel({
|
|
11917
|
-
viewport,
|
|
11918
|
-
sizePx,
|
|
11919
|
-
mobileFollowing = true,
|
|
11920
|
-
onSizeChange,
|
|
11921
|
-
onCustomizeMobile,
|
|
11922
|
-
onResetMobile,
|
|
11923
|
-
onUpdateEverywhere,
|
|
11924
|
-
className
|
|
11925
|
-
}) {
|
|
11926
|
-
const showFollowing = viewport === "mobile" && mobileFollowing;
|
|
11927
|
-
const showMobileSlider = viewport === "mobile" && !mobileFollowing;
|
|
11928
|
-
const showDesktopSlider = viewport === "desktop";
|
|
11929
|
-
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: cn("flex w-full flex-col gap-4", className), children: [
|
|
11930
|
-
showFollowing ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
|
|
11931
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-start gap-1", children: [
|
|
11932
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react14.Link, { size: 16, className: "mt-0.5 shrink-0 text-foreground", "aria-hidden": true }),
|
|
11933
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm font-semibold leading-5 text-foreground", children: "Following desktop size" })
|
|
11934
|
-
] }),
|
|
11935
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm leading-5 text-muted-foreground", children: "Mobile uses the desktop size until you customize it. Change the desktop size and it follows automatically." }),
|
|
11936
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
11937
|
-
Button,
|
|
11938
|
-
{
|
|
11939
|
-
type: "button",
|
|
11940
|
-
variant: "outline",
|
|
11941
|
-
size: "sm",
|
|
11942
|
-
className: "h-9 w-full min-w-0 cursor-pointer",
|
|
11943
|
-
onClick: onCustomizeMobile,
|
|
11944
|
-
children: "Customize for mobile"
|
|
11945
|
-
}
|
|
11946
|
-
)
|
|
11947
|
-
] }) : null,
|
|
11948
|
-
showDesktopSlider || showMobileSlider ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SizeSlider, { value: sizePx, onChange: onSizeChange }) : null,
|
|
11949
|
-
showMobileSlider ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
11950
|
-
Button,
|
|
11951
|
-
{
|
|
11952
|
-
type: "button",
|
|
11953
|
-
variant: "outline",
|
|
11954
|
-
size: "sm",
|
|
11955
|
-
className: "h-9 w-full min-w-0 cursor-pointer",
|
|
11956
|
-
onClick: onResetMobile,
|
|
11957
|
-
children: "Reset to desktop size"
|
|
11958
|
-
}
|
|
11959
|
-
) : null,
|
|
11960
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-px w-full bg-border", role: "separator" }),
|
|
11961
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
11962
|
-
Button,
|
|
11963
|
-
{
|
|
11964
|
-
type: "button",
|
|
11965
|
-
variant: "outline",
|
|
11966
|
-
size: "sm",
|
|
11967
|
-
className: "h-9 w-full min-w-0 cursor-pointer gap-1",
|
|
11968
|
-
onClick: onUpdateEverywhere,
|
|
11969
|
-
children: [
|
|
11970
|
-
"Update logo everywhere",
|
|
11971
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react14.ArrowUpRight, { size: 16, "aria-hidden": true })
|
|
11972
|
-
]
|
|
11973
|
-
}
|
|
11974
|
-
),
|
|
11975
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm leading-5 text-muted-foreground", children: "Matches the right version to your background." })
|
|
11976
|
-
] });
|
|
11977
|
-
}
|
|
11978
|
-
|
|
11979
11794
|
// src/ui/socials-display-panel.tsx
|
|
11980
11795
|
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
11981
11796
|
function DisplaySwitch({
|
|
@@ -12744,8 +12559,10 @@ function getLinkHref3(el) {
|
|
|
12744
12559
|
}
|
|
12745
12560
|
function collectEditableNodes(extraContent, root = document) {
|
|
12746
12561
|
const isScoped = root !== document;
|
|
12747
|
-
const editableEls = Array.from(
|
|
12748
|
-
|
|
12562
|
+
const editableEls = Array.from(
|
|
12563
|
+
root.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="form"])')
|
|
12564
|
+
);
|
|
12565
|
+
if (isScoped && root instanceof HTMLElement && root.matches('[data-ohw-editable]:not([data-ohw-editable="form"])')) {
|
|
12749
12566
|
editableEls.unshift(root);
|
|
12750
12567
|
}
|
|
12751
12568
|
const nodes = editableEls.map((el) => {
|
|
@@ -12820,18 +12637,6 @@ function collectEditableNodes(extraContent, root = document) {
|
|
|
12820
12637
|
}
|
|
12821
12638
|
if (extraContent && !isScoped) {
|
|
12822
12639
|
applyNavFooterDeleteOverrides(byKey, extraContent);
|
|
12823
|
-
for (const key of LOGO_IMAGE_KEYS) {
|
|
12824
|
-
if (!(key in extraContent)) continue;
|
|
12825
|
-
byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
|
|
12826
|
-
}
|
|
12827
|
-
for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
|
|
12828
|
-
if (!(key in extraContent)) continue;
|
|
12829
|
-
byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
|
|
12830
|
-
}
|
|
12831
|
-
for (const key of LOGO_SIZE_KEYS) {
|
|
12832
|
-
if (!(key in extraContent)) continue;
|
|
12833
|
-
byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
|
|
12834
|
-
}
|
|
12835
12640
|
}
|
|
12836
12641
|
return Array.from(byKey.values());
|
|
12837
12642
|
}
|
|
@@ -13300,10 +13105,21 @@ function parseSchedulingInsertAfter(insertAfter) {
|
|
|
13300
13105
|
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
13301
13106
|
};
|
|
13302
13107
|
}
|
|
13303
|
-
function
|
|
13304
|
-
|
|
13305
|
-
const
|
|
13306
|
-
|
|
13108
|
+
function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
|
|
13109
|
+
const parsed = parseSchedulingInsertAfter(insertAfter);
|
|
13110
|
+
const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
|
|
13111
|
+
const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
|
|
13112
|
+
return { effectiveInsertAfter, insertBefore };
|
|
13113
|
+
}
|
|
13114
|
+
function getSchedulingMountPoint(insertAfter) {
|
|
13115
|
+
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
13116
|
+
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
13117
|
+
if (!anchorEl && anchor === "scheduling") {
|
|
13118
|
+
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
13119
|
+
anchorEl = widgets.at(-1) ?? null;
|
|
13120
|
+
}
|
|
13121
|
+
if (!anchorEl) return null;
|
|
13122
|
+
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
13307
13123
|
}
|
|
13308
13124
|
function schedulingMountDepth(insertAfter) {
|
|
13309
13125
|
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
@@ -13320,7 +13136,8 @@ function getPageSchedulingEntries(raw) {
|
|
|
13320
13136
|
}
|
|
13321
13137
|
}
|
|
13322
13138
|
function isSchedulingWidgetMissing(entry) {
|
|
13323
|
-
|
|
13139
|
+
const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
|
|
13140
|
+
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
13324
13141
|
}
|
|
13325
13142
|
function hasMissingSchedulingWidgets(entries) {
|
|
13326
13143
|
return entries.some(isSchedulingWidgetMissing);
|
|
@@ -13350,17 +13167,16 @@ function initSectionsFromContent(content, removeExisting = false) {
|
|
|
13350
13167
|
} catch {
|
|
13351
13168
|
}
|
|
13352
13169
|
}
|
|
13353
|
-
function mountSchedulingWidget(
|
|
13354
|
-
const
|
|
13355
|
-
const sectionId = schedulingSectionId(
|
|
13170
|
+
function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
|
|
13171
|
+
const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
|
|
13172
|
+
const sectionId = schedulingSectionId(effectiveInsertAfter);
|
|
13356
13173
|
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
13357
|
-
const
|
|
13358
|
-
if (!
|
|
13359
|
-
const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
13174
|
+
const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
|
|
13175
|
+
if (!mountPoint) return false;
|
|
13360
13176
|
const container = document.createElement("div");
|
|
13361
13177
|
container.dataset.ohwSectionContainer = "scheduling";
|
|
13362
|
-
if (
|
|
13363
|
-
const beforeAnchor = document.querySelector(`[data-ohw-section="${
|
|
13178
|
+
if (insertBefore) {
|
|
13179
|
+
const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
|
|
13364
13180
|
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
13365
13181
|
if (!beforePoint) return false;
|
|
13366
13182
|
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
@@ -13371,25 +13187,19 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
|
|
|
13371
13187
|
}
|
|
13372
13188
|
tail.insertAdjacentElement("afterend", container);
|
|
13373
13189
|
}
|
|
13374
|
-
|
|
13375
|
-
|
|
13376
|
-
|
|
13377
|
-
|
|
13378
|
-
|
|
13379
|
-
|
|
13380
|
-
|
|
13381
|
-
|
|
13382
|
-
|
|
13383
|
-
|
|
13384
|
-
|
|
13385
|
-
|
|
13386
|
-
|
|
13387
|
-
});
|
|
13388
|
-
} catch (err) {
|
|
13389
|
-
console.error("[ow:scheduling] render threw", err);
|
|
13390
|
-
container.remove();
|
|
13391
|
-
return false;
|
|
13392
|
-
}
|
|
13190
|
+
const root = (0, import_client2.createRoot)(container);
|
|
13191
|
+
(0, import_react_dom3.flushSync)(() => {
|
|
13192
|
+
root.render(
|
|
13193
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
13194
|
+
SchedulingWidget,
|
|
13195
|
+
{
|
|
13196
|
+
notifyOnConnect,
|
|
13197
|
+
initialScheduleId: scheduleId,
|
|
13198
|
+
insertAfter: effectiveInsertAfter
|
|
13199
|
+
}
|
|
13200
|
+
)
|
|
13201
|
+
);
|
|
13202
|
+
});
|
|
13393
13203
|
const tracker = getSectionsTracker();
|
|
13394
13204
|
let sections = [];
|
|
13395
13205
|
try {
|
|
@@ -13397,12 +13207,10 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
|
|
|
13397
13207
|
} catch {
|
|
13398
13208
|
}
|
|
13399
13209
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
13400
|
-
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter ===
|
|
13210
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
|
|
13401
13211
|
sections.push({
|
|
13402
13212
|
type: "scheduling",
|
|
13403
|
-
insertAfter:
|
|
13404
|
-
anchorId,
|
|
13405
|
-
beforeId: beforeId ?? null,
|
|
13213
|
+
insertAfter: effectiveInsertAfter,
|
|
13406
13214
|
pagePath: window.location.pathname,
|
|
13407
13215
|
...scheduleId ? { scheduleId } : {}
|
|
13408
13216
|
});
|
|
@@ -13416,8 +13224,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
|
13416
13224
|
for (let i = pending.length - 1; i >= 0; i--) {
|
|
13417
13225
|
const entry = pending[i];
|
|
13418
13226
|
const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
|
|
13419
|
-
|
|
13420
|
-
if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
|
|
13227
|
+
if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
|
|
13421
13228
|
pending.splice(i, 1);
|
|
13422
13229
|
}
|
|
13423
13230
|
}
|
|
@@ -13490,7 +13297,11 @@ function isIconEditable(el) {
|
|
|
13490
13297
|
return el.dataset.ohwEditable === "icon";
|
|
13491
13298
|
}
|
|
13492
13299
|
var MEDIA_SELECTOR = '[data-ohw-editable="image"], [data-ohw-editable="bg-image"], [data-ohw-editable="video"]';
|
|
13493
|
-
var
|
|
13300
|
+
var EDITOR_CHROME_SELECTOR = '[data-ohw-toolbar], [data-ohw-form-toolbar], [data-ohw-field-toolbar], [data-ohw-item-toolbar], [data-ohw-more-menu], [data-ohw-state-toggle], [data-ohw-max-badge], [data-slot="dropdown-menu-content"], [data-slot="dropdown-menu-item"]';
|
|
13301
|
+
function isOverEditorChrome(x, y) {
|
|
13302
|
+
return document.elementsFromPoint(x, y).some((el) => el instanceof HTMLElement && el.matches(EDITOR_CHROME_SELECTOR));
|
|
13303
|
+
}
|
|
13304
|
+
var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]):not([data-ohw-editable="icon"]):not([data-ohw-editable="form"])';
|
|
13494
13305
|
function getVideoEl2(el) {
|
|
13495
13306
|
return el instanceof HTMLVideoElement ? el : el.querySelector("video");
|
|
13496
13307
|
}
|
|
@@ -13561,13 +13372,6 @@ function isInsideLinkEditor(target) {
|
|
|
13561
13372
|
target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest("[data-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
|
|
13562
13373
|
);
|
|
13563
13374
|
}
|
|
13564
|
-
function isInsideFloatingPanel(target) {
|
|
13565
|
-
return Boolean(target.closest("[data-ohw-floating-panel]"));
|
|
13566
|
-
}
|
|
13567
|
-
function isPointOverFloatingPanel(clientX, clientY) {
|
|
13568
|
-
const el = document.elementFromPoint(clientX, clientY);
|
|
13569
|
-
return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
|
|
13570
|
-
}
|
|
13571
13375
|
function getHrefKeyFromElement(el) {
|
|
13572
13376
|
if (!el) return null;
|
|
13573
13377
|
const anchor = el.closest("[data-ohw-href-key]");
|
|
@@ -13615,7 +13419,8 @@ function isNavItemPointerTarget(el) {
|
|
|
13615
13419
|
function getNavigationItemAnchor(el) {
|
|
13616
13420
|
const anchor = el.matches("[data-ohw-href-key]") ? el : el.closest("[data-ohw-href-key]");
|
|
13617
13421
|
if (!anchor) return null;
|
|
13618
|
-
if (!anchor.querySelector('[data-ohw-editable="text"]') && !getSocialItem(anchor))
|
|
13422
|
+
if (!anchor.matches('[data-ohw-editable="text"], [data-ohw-editable="plain"]') && !anchor.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') && !getSocialItem(anchor))
|
|
13423
|
+
return null;
|
|
13619
13424
|
if (!isNavItemPointerTarget(anchor)) return null;
|
|
13620
13425
|
return anchor;
|
|
13621
13426
|
}
|
|
@@ -13805,7 +13610,7 @@ function getNavigationSelectionParent(el) {
|
|
|
13805
13610
|
if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
|
|
13806
13611
|
return getFooterLinksContainer();
|
|
13807
13612
|
}
|
|
13808
|
-
if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") ||
|
|
13613
|
+
if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
|
|
13809
13614
|
return getNavigationRoot(el);
|
|
13810
13615
|
}
|
|
13811
13616
|
return null;
|
|
@@ -14398,7 +14203,6 @@ function StateToggle({
|
|
|
14398
14203
|
);
|
|
14399
14204
|
}
|
|
14400
14205
|
var contentCache = /* @__PURE__ */ new Map();
|
|
14401
|
-
var fetchedContentPaths = /* @__PURE__ */ new Set();
|
|
14402
14206
|
function resolveSubdomain(subdomainFromQuery) {
|
|
14403
14207
|
if (subdomainFromQuery) return subdomainFromQuery;
|
|
14404
14208
|
if (typeof window !== "undefined") {
|
|
@@ -14493,14 +14297,8 @@ function OhhwellsBridge() {
|
|
|
14493
14297
|
});
|
|
14494
14298
|
const selectFrameRef = (0, import_react16.useRef)(() => {
|
|
14495
14299
|
});
|
|
14496
|
-
const selectLogoRef = (0, import_react16.useRef)(() => {
|
|
14497
|
-
});
|
|
14498
|
-
const openLogoSizePanelRef = (0, import_react16.useRef)(() => {
|
|
14499
|
-
});
|
|
14500
14300
|
const deselectRef = (0, import_react16.useRef)(() => {
|
|
14501
14301
|
});
|
|
14502
|
-
const closeFloatingPanelOnlyRef = (0, import_react16.useRef)(() => {
|
|
14503
|
-
});
|
|
14504
14302
|
const reselectNavigationItemRef = (0, import_react16.useRef)(() => {
|
|
14505
14303
|
});
|
|
14506
14304
|
const commitNavigationTextEditRef = (0, import_react16.useRef)(() => {
|
|
@@ -14516,6 +14314,182 @@ function OhhwellsBridge() {
|
|
|
14516
14314
|
const sectionsLoadedRef = (0, import_react16.useRef)(false);
|
|
14517
14315
|
const pendingScheduleConfigRequests = (0, import_react16.useRef)([]);
|
|
14518
14316
|
const [toolbarRect, setToolbarRect] = (0, import_react16.useState)(null);
|
|
14317
|
+
const [formPickRect, setFormPickRect] = (0, import_react16.useState)(null);
|
|
14318
|
+
const formPickElRef = (0, import_react16.useRef)(null);
|
|
14319
|
+
const [formViewState, setFormViewStateUi] = (0, import_react16.useState)("default");
|
|
14320
|
+
const [formPickCount, setFormPickCount] = (0, import_react16.useState)(null);
|
|
14321
|
+
const [formHoverRect, setFormHoverRect] = (0, import_react16.useState)(null);
|
|
14322
|
+
const formHoverElRef = (0, import_react16.useRef)(null);
|
|
14323
|
+
const [fieldPickRect, setFieldPickRect] = (0, import_react16.useState)(null);
|
|
14324
|
+
const fieldPickElRef = (0, import_react16.useRef)(null);
|
|
14325
|
+
const [fieldPickState, setFieldPickState] = (0, import_react16.useState)(null);
|
|
14326
|
+
const [fieldTypePickerOpen, setFieldTypePickerOpen] = (0, import_react16.useState)(false);
|
|
14327
|
+
const clearFormPick = (0, import_react16.useCallback)(() => {
|
|
14328
|
+
const form = formPickElRef.current;
|
|
14329
|
+
const editing = fieldPickElRef.current;
|
|
14330
|
+
if (commitPlaceholderEdit(editing) && editing) {
|
|
14331
|
+
const owner = editing.closest('[data-ohw-editable="form"]');
|
|
14332
|
+
if (owner) persistFieldsRef.current(owner);
|
|
14333
|
+
}
|
|
14334
|
+
if (form) {
|
|
14335
|
+
const key = formKeyOf(form);
|
|
14336
|
+
if (key) setFormViewState(form, key, "default", successInitialFor(form, key, editContentRef.current));
|
|
14337
|
+
}
|
|
14338
|
+
setFormViewStateUi("default");
|
|
14339
|
+
setFormPickCount(null);
|
|
14340
|
+
setFieldTypePickerOpen(false);
|
|
14341
|
+
fieldPickElRef.current = null;
|
|
14342
|
+
setFieldPickRect(null);
|
|
14343
|
+
setFieldPickState(null);
|
|
14344
|
+
formPickElRef.current = null;
|
|
14345
|
+
setFormPickRect(null);
|
|
14346
|
+
}, []);
|
|
14347
|
+
const clearFieldPick = (0, import_react16.useCallback)(() => {
|
|
14348
|
+
const wrapper = fieldPickElRef.current;
|
|
14349
|
+
if (commitPlaceholderEdit(wrapper) && wrapper) {
|
|
14350
|
+
const form = wrapper.closest('[data-ohw-editable="form"]');
|
|
14351
|
+
if (form) persistFieldsRef.current(form);
|
|
14352
|
+
}
|
|
14353
|
+
fieldPickElRef.current = null;
|
|
14354
|
+
setFieldPickRect(null);
|
|
14355
|
+
setFieldPickState(null);
|
|
14356
|
+
}, []);
|
|
14357
|
+
const persistFieldsRef = (0, import_react16.useRef)(() => {
|
|
14358
|
+
});
|
|
14359
|
+
const persistFields = (0, import_react16.useCallback)(
|
|
14360
|
+
(form) => {
|
|
14361
|
+
const key = formKeyOf(form);
|
|
14362
|
+
if (!key) return;
|
|
14363
|
+
const json = JSON.stringify(readFieldsFromDom(form));
|
|
14364
|
+
editContentRef.current = { ...editContentRef.current, [fieldsKey(key)]: json };
|
|
14365
|
+
postToParentRef.current({ type: "ow:change", nodes: [{ key: fieldsKey(key), text: json }] });
|
|
14366
|
+
},
|
|
14367
|
+
[]
|
|
14368
|
+
);
|
|
14369
|
+
persistFieldsRef.current = persistFields;
|
|
14370
|
+
const selectField = (0, import_react16.useCallback)((wrapper) => {
|
|
14371
|
+
if (fieldPickElRef.current && fieldPickElRef.current !== wrapper) {
|
|
14372
|
+
commitPlaceholderEdit(fieldPickElRef.current);
|
|
14373
|
+
}
|
|
14374
|
+
syncRequiredMark(wrapper);
|
|
14375
|
+
beginPlaceholderEdit(wrapper);
|
|
14376
|
+
formHoverElRef.current = null;
|
|
14377
|
+
setFormHoverRect(null);
|
|
14378
|
+
fieldPickElRef.current = wrapper;
|
|
14379
|
+
setFieldPickRect(wrapper.getBoundingClientRect());
|
|
14380
|
+
setFieldPickState({ type: fieldTypeOf(wrapper), required: isFieldRequired(wrapper) });
|
|
14381
|
+
setFieldTypePickerOpen(false);
|
|
14382
|
+
}, []);
|
|
14383
|
+
const withSelectedField = (0, import_react16.useCallback)(
|
|
14384
|
+
(run) => {
|
|
14385
|
+
const wrapper = fieldPickElRef.current;
|
|
14386
|
+
const form = formPickElRef.current;
|
|
14387
|
+
if (!wrapper || !form) return;
|
|
14388
|
+
commitPlaceholderEdit(wrapper);
|
|
14389
|
+
run(form, wrapper);
|
|
14390
|
+
persistFields(form);
|
|
14391
|
+
beginPlaceholderEdit(wrapper);
|
|
14392
|
+
setFormPickRect(form.getBoundingClientRect());
|
|
14393
|
+
},
|
|
14394
|
+
[persistFields]
|
|
14395
|
+
);
|
|
14396
|
+
const handleFieldTypeChange = (0, import_react16.useCallback)(
|
|
14397
|
+
(type) => withSelectedField((_form, wrapper) => {
|
|
14398
|
+
applyFieldType(wrapper, type);
|
|
14399
|
+
selectField(wrapper);
|
|
14400
|
+
}),
|
|
14401
|
+
[selectField, withSelectedField]
|
|
14402
|
+
);
|
|
14403
|
+
const handleFieldRequiredToggle = (0, import_react16.useCallback)(
|
|
14404
|
+
() => withSelectedField((_form, wrapper) => {
|
|
14405
|
+
setFieldRequired(wrapper, !isFieldRequired(wrapper));
|
|
14406
|
+
selectField(wrapper);
|
|
14407
|
+
}),
|
|
14408
|
+
[selectField, withSelectedField]
|
|
14409
|
+
);
|
|
14410
|
+
const handleFieldDuplicate = (0, import_react16.useCallback)(
|
|
14411
|
+
() => withSelectedField((form, wrapper) => {
|
|
14412
|
+
const copy = duplicateField(form, wrapper);
|
|
14413
|
+
selectField(copy);
|
|
14414
|
+
}),
|
|
14415
|
+
[selectField, withSelectedField]
|
|
14416
|
+
);
|
|
14417
|
+
const handleFieldDelete = (0, import_react16.useCallback)(
|
|
14418
|
+
() => withSelectedField((_form, wrapper) => {
|
|
14419
|
+
removeField(wrapper);
|
|
14420
|
+
clearFieldPick();
|
|
14421
|
+
postToParentRef.current({ type: "ow:toast", title: "Form field deleted" });
|
|
14422
|
+
}),
|
|
14423
|
+
[clearFieldPick, withSelectedField]
|
|
14424
|
+
);
|
|
14425
|
+
const handleAddField = (0, import_react16.useCallback)(
|
|
14426
|
+
(type) => {
|
|
14427
|
+
const form = formPickElRef.current;
|
|
14428
|
+
if (!form) return;
|
|
14429
|
+
const wrapper = insertField(form, type);
|
|
14430
|
+
setFieldTypePickerOpen(false);
|
|
14431
|
+
if (!wrapper) return;
|
|
14432
|
+
persistFields(form);
|
|
14433
|
+
setFormPickRect(null);
|
|
14434
|
+
requestAnimationFrame(() => {
|
|
14435
|
+
selectField(wrapper);
|
|
14436
|
+
wrapper.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
14437
|
+
});
|
|
14438
|
+
},
|
|
14439
|
+
[persistFields, selectField]
|
|
14440
|
+
);
|
|
14441
|
+
const fieldDragRef = (0, import_react16.useRef)(null);
|
|
14442
|
+
const buildFieldDropSlots = (0, import_react16.useCallback)((form, draggedKey) => {
|
|
14443
|
+
const others = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== draggedKey);
|
|
14444
|
+
const slots = others.map((el) => {
|
|
14445
|
+
const rect = el.getBoundingClientRect();
|
|
14446
|
+
return { top: rect.top, left: rect.left, width: rect.width };
|
|
14447
|
+
});
|
|
14448
|
+
const last = others[others.length - 1];
|
|
14449
|
+
if (last) {
|
|
14450
|
+
const rect = last.getBoundingClientRect();
|
|
14451
|
+
slots.push({ top: rect.bottom, left: rect.left, width: rect.width });
|
|
14452
|
+
}
|
|
14453
|
+
return slots;
|
|
14454
|
+
}, []);
|
|
14455
|
+
const handleFieldDragStart = (0, import_react16.useCallback)(() => {
|
|
14456
|
+
const wrapper = fieldPickElRef.current;
|
|
14457
|
+
const form = formPickElRef.current;
|
|
14458
|
+
if (!wrapper || !form) return;
|
|
14459
|
+
const key = fieldKeyOf(wrapper);
|
|
14460
|
+
fieldDragRef.current = { key, form };
|
|
14461
|
+
setFieldDragging(true);
|
|
14462
|
+
setFieldDropSlots(buildFieldDropSlots(form, key));
|
|
14463
|
+
}, [buildFieldDropSlots]);
|
|
14464
|
+
const handleFieldDragEnd = (0, import_react16.useCallback)(() => {
|
|
14465
|
+
fieldDragRef.current = null;
|
|
14466
|
+
setFieldDropIndex(null);
|
|
14467
|
+
setFieldDropSlots([]);
|
|
14468
|
+
setFieldDragging(false);
|
|
14469
|
+
}, []);
|
|
14470
|
+
const [fieldDropIndex, setFieldDropIndex] = (0, import_react16.useState)(null);
|
|
14471
|
+
const [fieldDropSlots, setFieldDropSlots] = (0, import_react16.useState)([]);
|
|
14472
|
+
const [fieldDragging, setFieldDragging] = (0, import_react16.useState)(false);
|
|
14473
|
+
const clearFormPickRef = (0, import_react16.useRef)(clearFormPick);
|
|
14474
|
+
clearFormPickRef.current = clearFormPick;
|
|
14475
|
+
(0, import_react16.useEffect)(() => {
|
|
14476
|
+
const el = fieldPickElRef.current;
|
|
14477
|
+
if (!el || fieldPickRect === null) return;
|
|
14478
|
+
const observer = new ResizeObserver(() => {
|
|
14479
|
+
if (fieldPickElRef.current === el) setFieldPickRect(el.getBoundingClientRect());
|
|
14480
|
+
});
|
|
14481
|
+
observer.observe(el);
|
|
14482
|
+
return () => observer.disconnect();
|
|
14483
|
+
}, [fieldPickRect !== null, fieldPickState]);
|
|
14484
|
+
(0, import_react16.useEffect)(() => {
|
|
14485
|
+
const el = formPickElRef.current;
|
|
14486
|
+
if (!el || formPickRect === null) return;
|
|
14487
|
+
const observer = new ResizeObserver(() => {
|
|
14488
|
+
if (formPickElRef.current === el) setFormPickRect(el.getBoundingClientRect());
|
|
14489
|
+
});
|
|
14490
|
+
observer.observe(el);
|
|
14491
|
+
return () => observer.disconnect();
|
|
14492
|
+
}, [formPickRect !== null, formViewState]);
|
|
14519
14493
|
const [toolbarVariant, setToolbarVariant] = (0, import_react16.useState)("none");
|
|
14520
14494
|
const toolbarVariantRef = (0, import_react16.useRef)("none");
|
|
14521
14495
|
toolbarVariantRef.current = toolbarVariant;
|
|
@@ -14537,7 +14511,7 @@ function OhhwellsBridge() {
|
|
|
14537
14511
|
(0, import_react16.useEffect)(() => {
|
|
14538
14512
|
const sync = () => {
|
|
14539
14513
|
const el = document.querySelector(
|
|
14540
|
-
|
|
14514
|
+
'[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]):not([data-ohw-editable="form"] *)'
|
|
14541
14515
|
);
|
|
14542
14516
|
const target = el && !el.closest("[data-ohw-href-key]") ? el : null;
|
|
14543
14517
|
if (!target) {
|
|
@@ -14561,6 +14535,11 @@ function OhhwellsBridge() {
|
|
|
14561
14535
|
const [isItemDragging, setIsItemDragging] = (0, import_react16.useState)(false);
|
|
14562
14536
|
const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react16.useState)(false);
|
|
14563
14537
|
isFooterFrameSelectionRef.current = isFooterFrameSelection;
|
|
14538
|
+
const [floatingPanel, setFloatingPanel] = (0, import_react16.useState)(null);
|
|
14539
|
+
const floatingPanelOpenRef = (0, import_react16.useRef)(false);
|
|
14540
|
+
floatingPanelOpenRef.current = floatingPanel !== null;
|
|
14541
|
+
const [floatingPanelPos, setFloatingPanelPos] = (0, import_react16.useState)(null);
|
|
14542
|
+
const [parentScrollSnap, setParentScrollSnap] = (0, import_react16.useState)(null);
|
|
14564
14543
|
const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react16.useState)(null);
|
|
14565
14544
|
const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react16.useState)(null);
|
|
14566
14545
|
const footerDragRef = (0, import_react16.useRef)(null);
|
|
@@ -14575,16 +14554,7 @@ function OhhwellsBridge() {
|
|
|
14575
14554
|
const addNavAfterAnchorRef = (0, import_react16.useRef)(null);
|
|
14576
14555
|
const editContentRef = (0, import_react16.useRef)({});
|
|
14577
14556
|
const aiSectionsRef = (0, import_react16.useRef)("");
|
|
14578
|
-
const brandKitRef = (0, import_react16.useRef)("");
|
|
14579
|
-
const stylesRef = (0, import_react16.useRef)("");
|
|
14580
14557
|
const pendingDeleteUndoRef = (0, import_react16.useRef)(null);
|
|
14581
|
-
const [floatingPanel, setFloatingPanel] = (0, import_react16.useState)(null);
|
|
14582
|
-
const floatingPanelOpenRef = (0, import_react16.useRef)(false);
|
|
14583
|
-
const setFloatingPanelRef = (0, import_react16.useRef)(setFloatingPanel);
|
|
14584
|
-
const [floatingPanelPos, setFloatingPanelPos] = (0, import_react16.useState)(null);
|
|
14585
|
-
const [logoSizeDraft, setLogoSizeDraft] = (0, import_react16.useState)(null);
|
|
14586
|
-
const [editorViewport, setEditorViewport] = (0, import_react16.useState)("desktop");
|
|
14587
|
-
const [parentScrollSnap, setParentScrollSnap] = (0, import_react16.useState)(null);
|
|
14588
14558
|
const [sitePages, setSitePages] = (0, import_react16.useState)([]);
|
|
14589
14559
|
const [sectionsByPath, setSectionsByPath] = (0, import_react16.useState)({});
|
|
14590
14560
|
const sectionsPrefetchGenRef = (0, import_react16.useRef)(0);
|
|
@@ -14593,18 +14563,7 @@ function OhhwellsBridge() {
|
|
|
14593
14563
|
const linkPopoverOpenRef = (0, import_react16.useRef)(false);
|
|
14594
14564
|
const linkPopoverGraceUntilRef = (0, import_react16.useRef)(0);
|
|
14595
14565
|
setLinkPopoverRef.current = setLinkPopover;
|
|
14596
|
-
setFloatingPanelRef.current = setFloatingPanel;
|
|
14597
14566
|
linkPopoverSessionRef.current = linkPopover;
|
|
14598
|
-
floatingPanelOpenRef.current = Boolean(floatingPanel);
|
|
14599
|
-
(0, import_react16.useEffect)(() => {
|
|
14600
|
-
const syncViewport = () => {
|
|
14601
|
-
const next = window.innerWidth <= 480 ? "mobile" : "desktop";
|
|
14602
|
-
setEditorViewport((prev) => prev === next ? prev : next);
|
|
14603
|
-
};
|
|
14604
|
-
syncViewport();
|
|
14605
|
-
window.addEventListener("resize", syncViewport);
|
|
14606
|
-
return () => window.removeEventListener("resize", syncViewport);
|
|
14607
|
-
}, []);
|
|
14608
14567
|
const {
|
|
14609
14568
|
navDragRef,
|
|
14610
14569
|
navDropSlots,
|
|
@@ -14773,7 +14732,9 @@ function OhhwellsBridge() {
|
|
|
14773
14732
|
const deactivate = (0, import_react16.useCallback)(() => {
|
|
14774
14733
|
const el = activeElRef.current;
|
|
14775
14734
|
if (!el) return;
|
|
14776
|
-
const
|
|
14735
|
+
const isFormBlock = el.dataset.ohwEditable === "form";
|
|
14736
|
+
const isFormControl = el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement;
|
|
14737
|
+
const key = isFormBlock || isFormControl ? void 0 : el.dataset.ohwKey;
|
|
14777
14738
|
if (key) {
|
|
14778
14739
|
const timer = autoSaveTimers.current.get(key);
|
|
14779
14740
|
if (timer !== void 0) {
|
|
@@ -14790,6 +14751,12 @@ function OhhwellsBridge() {
|
|
|
14790
14751
|
}
|
|
14791
14752
|
el.removeAttribute("contenteditable");
|
|
14792
14753
|
el.removeAttribute("data-ohw-editing");
|
|
14754
|
+
const labelField = getFieldWrapper(el);
|
|
14755
|
+
if (labelField) {
|
|
14756
|
+
syncRequiredMark(labelField);
|
|
14757
|
+
const owner = labelField.closest('[data-ohw-editable="form"]');
|
|
14758
|
+
if (owner) persistFieldsRef.current(owner);
|
|
14759
|
+
}
|
|
14793
14760
|
activeElRef.current = null;
|
|
14794
14761
|
setNavGroupForceOpen(null, false);
|
|
14795
14762
|
setReorderHrefKey(null);
|
|
@@ -14829,8 +14796,6 @@ function OhhwellsBridge() {
|
|
|
14829
14796
|
setHoveredNavContainerRect(null);
|
|
14830
14797
|
hoveredItemElRef.current = null;
|
|
14831
14798
|
setHoveredItemRect(null);
|
|
14832
|
-
setFloatingPanel(null);
|
|
14833
|
-
setLogoSizeDraft(null);
|
|
14834
14799
|
if (!activeElRef.current) {
|
|
14835
14800
|
setNavGroupForceOpen(null, false);
|
|
14836
14801
|
setToolbarRect(null);
|
|
@@ -14945,6 +14910,12 @@ function OhhwellsBridge() {
|
|
|
14945
14910
|
}
|
|
14946
14911
|
el.removeAttribute("contenteditable");
|
|
14947
14912
|
el.removeAttribute("data-ohw-editing");
|
|
14913
|
+
const labelField = getFieldWrapper(el);
|
|
14914
|
+
if (labelField) {
|
|
14915
|
+
syncRequiredMark(labelField);
|
|
14916
|
+
const owner = labelField.closest('[data-ohw-editable="form"]');
|
|
14917
|
+
if (owner) persistFieldsRef.current(owner);
|
|
14918
|
+
}
|
|
14948
14919
|
activeElRef.current = null;
|
|
14949
14920
|
setMaxBadge(null);
|
|
14950
14921
|
setActiveCommands(/* @__PURE__ */ new Set());
|
|
@@ -15536,8 +15507,6 @@ function OhhwellsBridge() {
|
|
|
15536
15507
|
setToolbarRect(anchor.getBoundingClientRect());
|
|
15537
15508
|
setToolbarShowEditLink(false);
|
|
15538
15509
|
setActiveCommands(/* @__PURE__ */ new Set());
|
|
15539
|
-
setFloatingPanel(null);
|
|
15540
|
-
setLogoSizeDraft(null);
|
|
15541
15510
|
}, [deactivate, markSelected]);
|
|
15542
15511
|
const selectFrame = (0, import_react16.useCallback)((el) => {
|
|
15543
15512
|
if (!isNavigationContainer(el)) return;
|
|
@@ -15587,51 +15556,7 @@ function OhhwellsBridge() {
|
|
|
15587
15556
|
setToolbarRect(el.getBoundingClientRect());
|
|
15588
15557
|
setToolbarShowEditLink(false);
|
|
15589
15558
|
setActiveCommands(/* @__PURE__ */ new Set());
|
|
15590
|
-
setFloatingPanel(null);
|
|
15591
|
-
setLogoSizeDraft(null);
|
|
15592
15559
|
}, [deactivate, markSelected, postToParent2]);
|
|
15593
|
-
const selectLogo = (0, import_react16.useCallback)(
|
|
15594
|
-
(logoEl) => {
|
|
15595
|
-
if (activeElRef.current) deactivate();
|
|
15596
|
-
selectedElRef.current = logoEl;
|
|
15597
|
-
selectedHrefKeyRef.current = null;
|
|
15598
|
-
selectedFooterColAttrRef.current = null;
|
|
15599
|
-
markSelected(logoEl);
|
|
15600
|
-
setSelectedIsCta(false);
|
|
15601
|
-
setSelectedIsSocial(false);
|
|
15602
|
-
setSelectedIsSocialsRow(false);
|
|
15603
|
-
clearHrefKeyHover(logoEl);
|
|
15604
|
-
hoveredNavContainerRef.current = null;
|
|
15605
|
-
setHoveredNavContainerRect(null);
|
|
15606
|
-
setHoveredItemRect(null);
|
|
15607
|
-
hoveredItemElRef.current = null;
|
|
15608
|
-
siblingHintElRef.current = null;
|
|
15609
|
-
setSiblingHintRect(null);
|
|
15610
|
-
setSiblingHintRects([]);
|
|
15611
|
-
setIsItemDragging(false);
|
|
15612
|
-
setReorderHrefKey(null);
|
|
15613
|
-
setReorderDragDisabled(false);
|
|
15614
|
-
setIsFooterFrameSelection(false);
|
|
15615
|
-
setToolbarVariant("logo");
|
|
15616
|
-
setToolbarRect(getLogoInteractionRect(logoEl));
|
|
15617
|
-
setToolbarShowEditLink(false);
|
|
15618
|
-
setActiveCommands(/* @__PURE__ */ new Set());
|
|
15619
|
-
},
|
|
15620
|
-
[deactivate, markSelected]
|
|
15621
|
-
);
|
|
15622
|
-
const openLogoSizePanel = (0, import_react16.useCallback)((logoEl) => {
|
|
15623
|
-
const placement = getLogoPlacement(logoEl);
|
|
15624
|
-
const draft = readLogoSizeState(editContentRef.current, placement);
|
|
15625
|
-
setLogoSizeDraft(draft);
|
|
15626
|
-
setParentScrollSnap(parentScrollRef.current);
|
|
15627
|
-
setFloatingPanel({
|
|
15628
|
-
key: `logo-size:${placement}`,
|
|
15629
|
-
title: "Logo",
|
|
15630
|
-
context: placement === "navbar" ? "Navbar" : "Footer",
|
|
15631
|
-
kind: "logo-size",
|
|
15632
|
-
placement
|
|
15633
|
-
});
|
|
15634
|
-
}, []);
|
|
15635
15560
|
const openSocialsDisplayPanel = (0, import_react16.useCallback)((row) => {
|
|
15636
15561
|
setParentScrollSnap(parentScrollRef.current);
|
|
15637
15562
|
setFloatingPanel({
|
|
@@ -15667,53 +15592,13 @@ function OhhwellsBridge() {
|
|
|
15667
15592
|
);
|
|
15668
15593
|
const closeFloatingPanelOnly = (0, import_react16.useCallback)(() => {
|
|
15669
15594
|
setFloatingPanel(null);
|
|
15670
|
-
setLogoSizeDraft(null);
|
|
15671
15595
|
}, []);
|
|
15596
|
+
const closeFloatingPanelOnlyRef = (0, import_react16.useRef)(closeFloatingPanelOnly);
|
|
15597
|
+
closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
|
|
15672
15598
|
const closeFloatingPanelAndDeselect = (0, import_react16.useCallback)(() => {
|
|
15673
15599
|
setFloatingPanel(null);
|
|
15674
|
-
setLogoSizeDraft(null);
|
|
15675
15600
|
deselectRef.current();
|
|
15676
15601
|
}, []);
|
|
15677
|
-
const persistLogoSizeDraft = (0, import_react16.useCallback)(
|
|
15678
|
-
(placement, draft) => {
|
|
15679
|
-
const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
|
|
15680
|
-
const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
|
|
15681
|
-
const nodes = [
|
|
15682
|
-
{ key: desktopKey, text: String(draft.desktopPx) }
|
|
15683
|
-
];
|
|
15684
|
-
if (draft.mobileFollowing) {
|
|
15685
|
-
nodes.push({ key: mobileKey, text: "" });
|
|
15686
|
-
} else {
|
|
15687
|
-
nodes.push({ key: mobileKey, text: String(draft.mobilePx) });
|
|
15688
|
-
}
|
|
15689
|
-
editContentRef.current = {
|
|
15690
|
-
...editContentRef.current,
|
|
15691
|
-
[desktopKey]: String(draft.desktopPx),
|
|
15692
|
-
[mobileKey]: draft.mobileFollowing ? "" : String(draft.mobilePx)
|
|
15693
|
-
};
|
|
15694
|
-
applyLogoSizeToPlacement(
|
|
15695
|
-
placement,
|
|
15696
|
-
draft.desktopPx,
|
|
15697
|
-
draft.mobileFollowing ? draft.desktopPx : draft.mobilePx,
|
|
15698
|
-
draft.mobileFollowing
|
|
15699
|
-
);
|
|
15700
|
-
postToParent2({ type: "ow:change", nodes });
|
|
15701
|
-
requestAnimationFrame(() => {
|
|
15702
|
-
const selected = selectedElRef.current;
|
|
15703
|
-
if (!selected || toolbarVariantRef.current !== "logo") return;
|
|
15704
|
-
const rect = getLogoInteractionRect(selected);
|
|
15705
|
-
setToolbarRect(rect);
|
|
15706
|
-
if (glowElRef.current) {
|
|
15707
|
-
const GAP = SELECTION_CHROME_GAP2;
|
|
15708
|
-
glowElRef.current.style.top = `${rect.top - GAP}px`;
|
|
15709
|
-
glowElRef.current.style.left = `${rect.left - GAP}px`;
|
|
15710
|
-
glowElRef.current.style.width = `${rect.width + GAP * 2}px`;
|
|
15711
|
-
glowElRef.current.style.height = `${rect.height + GAP * 2}px`;
|
|
15712
|
-
}
|
|
15713
|
-
});
|
|
15714
|
-
},
|
|
15715
|
-
[postToParent2]
|
|
15716
|
-
);
|
|
15717
15602
|
const activate = (0, import_react16.useCallback)((el, options) => {
|
|
15718
15603
|
if (activeElRef.current === el) return;
|
|
15719
15604
|
if (isIconEditable(el)) return;
|
|
@@ -15794,10 +15679,7 @@ function OhhwellsBridge() {
|
|
|
15794
15679
|
deactivateRef.current = deactivate;
|
|
15795
15680
|
selectRef.current = select;
|
|
15796
15681
|
selectFrameRef.current = selectFrame;
|
|
15797
|
-
selectLogoRef.current = selectLogo;
|
|
15798
|
-
openLogoSizePanelRef.current = openLogoSizePanel;
|
|
15799
15682
|
deselectRef.current = deselect;
|
|
15800
|
-
closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
|
|
15801
15683
|
const lastSiteWideScopeRef = (0, import_react16.useRef)(null);
|
|
15802
15684
|
(0, import_react16.useEffect)(() => {
|
|
15803
15685
|
if (!isEditMode) {
|
|
@@ -15836,23 +15718,9 @@ function OhhwellsBridge() {
|
|
|
15836
15718
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
15837
15719
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
15838
15720
|
}
|
|
15839
|
-
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
15840
|
-
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
15841
|
-
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
15842
|
-
}
|
|
15843
|
-
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
15844
|
-
stylesRef.current = content[STYLE_STORE_KEY];
|
|
15845
|
-
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
15846
|
-
}
|
|
15847
|
-
applyBrandChrome(content);
|
|
15848
15721
|
for (const [key, val] of Object.entries(content)) {
|
|
15849
15722
|
if (key === "__ohw_sections") continue;
|
|
15850
15723
|
if (key === AI_SECTIONS_KEY) continue;
|
|
15851
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
15852
|
-
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
15853
|
-
if (key === BRAND_KIT_KEY) continue;
|
|
15854
|
-
if (key === STYLE_STORE_KEY) continue;
|
|
15855
|
-
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
15856
15724
|
if (applyVideoSettingNode(key, val)) continue;
|
|
15857
15725
|
if (applyCarouselNode(key, val)) continue;
|
|
15858
15726
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -15881,14 +15749,13 @@ function OhhwellsBridge() {
|
|
|
15881
15749
|
applyLinkHref(el, val);
|
|
15882
15750
|
} else if (el.dataset.ohwEditable === "icon") {
|
|
15883
15751
|
applyIconMarkup(el, val);
|
|
15752
|
+
} else if (el.dataset.ohwEditable === "form") {
|
|
15884
15753
|
} else if (el.innerHTML !== val) {
|
|
15885
15754
|
el.innerHTML = val;
|
|
15886
15755
|
}
|
|
15887
15756
|
});
|
|
15888
15757
|
applyLinkByKey(key, val);
|
|
15889
15758
|
}
|
|
15890
|
-
applyLogoFromContent(content);
|
|
15891
|
-
applyLogoSizes(content);
|
|
15892
15759
|
reconcileNavbarItemsFromContent(content);
|
|
15893
15760
|
reconcileFooterOrderFromContent(content);
|
|
15894
15761
|
reconcileSocialsFromContent(content);
|
|
@@ -15909,9 +15776,7 @@ function OhhwellsBridge() {
|
|
|
15909
15776
|
let cancelled = false;
|
|
15910
15777
|
setFetchState("loading");
|
|
15911
15778
|
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
15912
|
-
|
|
15913
|
-
fetchedContentPaths.add(`${subdomain}::${initialPath}`);
|
|
15914
|
-
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
15779
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
15915
15780
|
if (cancelled) return;
|
|
15916
15781
|
const content = data?.content ?? {};
|
|
15917
15782
|
contentCache.set(subdomain, content);
|
|
@@ -15924,6 +15789,102 @@ function OhhwellsBridge() {
|
|
|
15924
15789
|
cancelled = true;
|
|
15925
15790
|
};
|
|
15926
15791
|
}, [subdomain, isEditMode]);
|
|
15792
|
+
(0, import_react16.useEffect)(() => {
|
|
15793
|
+
if (!isEditMode) return;
|
|
15794
|
+
const resolveIndex = (form, clientY) => {
|
|
15795
|
+
const wrappers = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== fieldDragRef.current?.key);
|
|
15796
|
+
for (let i = 0; i < wrappers.length; i += 1) {
|
|
15797
|
+
const rect = wrappers[i].getBoundingClientRect();
|
|
15798
|
+
if (clientY < rect.top + rect.height / 2) return i;
|
|
15799
|
+
}
|
|
15800
|
+
return wrappers.length;
|
|
15801
|
+
};
|
|
15802
|
+
const onDragOver = (e) => {
|
|
15803
|
+
const session = fieldDragRef.current;
|
|
15804
|
+
if (!session) return;
|
|
15805
|
+
e.preventDefault();
|
|
15806
|
+
e.stopPropagation();
|
|
15807
|
+
if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
|
|
15808
|
+
setFieldDropSlots(buildFieldDropSlots(session.form, session.key));
|
|
15809
|
+
setFieldDropIndex(resolveIndex(session.form, e.clientY));
|
|
15810
|
+
};
|
|
15811
|
+
const onDrop = (e) => {
|
|
15812
|
+
const session = fieldDragRef.current;
|
|
15813
|
+
if (!session) return;
|
|
15814
|
+
e.preventDefault();
|
|
15815
|
+
e.stopPropagation();
|
|
15816
|
+
moveField(session.form, session.key, resolveIndex(session.form, e.clientY));
|
|
15817
|
+
persistFields(session.form);
|
|
15818
|
+
const moved = listFieldWrappers(session.form).find((el) => fieldKeyOf(el) === session.key);
|
|
15819
|
+
if (moved) selectField(moved);
|
|
15820
|
+
setFormPickRect(session.form.getBoundingClientRect());
|
|
15821
|
+
fieldDragRef.current = null;
|
|
15822
|
+
setFieldDropIndex(null);
|
|
15823
|
+
setFieldDropSlots([]);
|
|
15824
|
+
setFieldDragging(false);
|
|
15825
|
+
};
|
|
15826
|
+
window.addEventListener("dragover", onDragOver, true);
|
|
15827
|
+
window.addEventListener("drop", onDrop, true);
|
|
15828
|
+
return () => {
|
|
15829
|
+
window.removeEventListener("dragover", onDragOver, true);
|
|
15830
|
+
window.removeEventListener("drop", onDrop, true);
|
|
15831
|
+
};
|
|
15832
|
+
}, [buildFieldDropSlots, isEditMode, persistFields, selectField]);
|
|
15833
|
+
(0, import_react16.useEffect)(() => {
|
|
15834
|
+
if (!isEditMode) return;
|
|
15835
|
+
const mark = () => document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
|
|
15836
|
+
markFormFields(form);
|
|
15837
|
+
});
|
|
15838
|
+
mark();
|
|
15839
|
+
const observer = new MutationObserver(() => mark());
|
|
15840
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
|
|
15841
|
+
observer.observe(form, { childList: true, subtree: true, characterData: true });
|
|
15842
|
+
});
|
|
15843
|
+
return () => observer.disconnect();
|
|
15844
|
+
}, [isEditMode, fetchState, pathname]);
|
|
15845
|
+
(0, import_react16.useEffect)(() => {
|
|
15846
|
+
if (!isEditMode) return;
|
|
15847
|
+
let saveTimer = null;
|
|
15848
|
+
const onInput = (e) => {
|
|
15849
|
+
const input = e.target;
|
|
15850
|
+
if (!input || !("value" in input)) return;
|
|
15851
|
+
const wrapper = getFieldWrapper(input);
|
|
15852
|
+
if (!wrapper || wrapper !== fieldPickElRef.current) return;
|
|
15853
|
+
if (saveTimer) clearTimeout(saveTimer);
|
|
15854
|
+
const owner = wrapper.closest('[data-ohw-editable="form"]');
|
|
15855
|
+
saveTimer = setTimeout(() => {
|
|
15856
|
+
const form = owner ?? formPickElRef.current;
|
|
15857
|
+
if (!form) return;
|
|
15858
|
+
const previous = input.getAttribute("placeholder");
|
|
15859
|
+
input.setAttribute("placeholder", input.value);
|
|
15860
|
+
persistFields(form);
|
|
15861
|
+
input.setAttribute("placeholder", previous ?? "");
|
|
15862
|
+
}, 400);
|
|
15863
|
+
};
|
|
15864
|
+
document.addEventListener("input", onInput, true);
|
|
15865
|
+
return () => document.removeEventListener("input", onInput, true);
|
|
15866
|
+
}, [isEditMode, persistFields]);
|
|
15867
|
+
(0, import_react16.useEffect)(() => {
|
|
15868
|
+
if (isEditMode || fetchState !== "done") return;
|
|
15869
|
+
const content = contentCache.get(subdomain) ?? {};
|
|
15870
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
|
|
15871
|
+
reconcileFieldsFromContent(form, content);
|
|
15872
|
+
});
|
|
15873
|
+
}, [isEditMode, fetchState, subdomain]);
|
|
15874
|
+
(0, import_react16.useEffect)(() => {
|
|
15875
|
+
if (!isEditMode) return;
|
|
15876
|
+
const swallow = (e) => {
|
|
15877
|
+
const target = e.target;
|
|
15878
|
+
if (target && getFormElement(target)) e.preventDefault();
|
|
15879
|
+
};
|
|
15880
|
+
document.addEventListener("submit", swallow, true);
|
|
15881
|
+
return () => document.removeEventListener("submit", swallow, true);
|
|
15882
|
+
}, [isEditMode]);
|
|
15883
|
+
(0, import_react16.useEffect)(() => {
|
|
15884
|
+
if (isEditMode || fetchState !== "done") return;
|
|
15885
|
+
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
15886
|
+
bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
|
|
15887
|
+
}, [isEditMode, fetchState, subdomain]);
|
|
15927
15888
|
(0, import_react16.useEffect)(() => {
|
|
15928
15889
|
if (!subdomain || isEditMode) return;
|
|
15929
15890
|
let debounceTimer = null;
|
|
@@ -15935,21 +15896,8 @@ function OhhwellsBridge() {
|
|
|
15935
15896
|
initSectionInstancesFromContent(content, window.location.pathname);
|
|
15936
15897
|
observer?.disconnect();
|
|
15937
15898
|
try {
|
|
15938
|
-
applyBrandChrome(content);
|
|
15939
|
-
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
15940
|
-
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
15941
|
-
}
|
|
15942
|
-
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
15943
|
-
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
15944
|
-
}
|
|
15945
15899
|
for (const [key, val] of Object.entries(content)) {
|
|
15946
15900
|
if (key === "__ohw_sections") continue;
|
|
15947
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
15948
|
-
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
15949
|
-
if (key === BRAND_KIT_KEY) continue;
|
|
15950
|
-
if (key === STYLE_STORE_KEY) continue;
|
|
15951
|
-
if (key === STYLE_STORE_KEY) continue;
|
|
15952
|
-
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
15953
15901
|
if (applyVideoSettingNode(key, val)) continue;
|
|
15954
15902
|
if (applyCarouselNode(key, val)) continue;
|
|
15955
15903
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -15964,17 +15912,23 @@ function OhhwellsBridge() {
|
|
|
15964
15912
|
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
15965
15913
|
} else if (el.dataset.ohwEditable === "link") {
|
|
15966
15914
|
applyLinkHref(el, val);
|
|
15915
|
+
} else if (el.dataset.ohwEditable === "form") {
|
|
15967
15916
|
} else if (el.innerHTML !== val) {
|
|
15968
15917
|
el.innerHTML = val;
|
|
15969
15918
|
}
|
|
15970
15919
|
});
|
|
15971
15920
|
applyLinkByKey(key, val);
|
|
15972
15921
|
}
|
|
15973
|
-
applyLogoFromContent(content);
|
|
15974
15922
|
reconcileNavbarItemsFromContent(content);
|
|
15975
15923
|
reconcileFooterOrderFromContent(content);
|
|
15976
15924
|
reconcileSocialsFromContent(content);
|
|
15977
15925
|
applySocialsDisplayFromContent(content);
|
|
15926
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
|
|
15927
|
+
if (!form.querySelector("[data-ohw-form-success]")) {
|
|
15928
|
+
reconcileFieldsFromContent(form, content);
|
|
15929
|
+
}
|
|
15930
|
+
listFieldWrappers(form).forEach(syncRequiredMark);
|
|
15931
|
+
});
|
|
15978
15932
|
} finally {
|
|
15979
15933
|
observer?.observe(document.body, { childList: true, subtree: true });
|
|
15980
15934
|
}
|
|
@@ -15985,17 +15939,6 @@ function OhhwellsBridge() {
|
|
|
15985
15939
|
debounceTimer = setTimeout(applyFromCache, 150);
|
|
15986
15940
|
};
|
|
15987
15941
|
applyFromCache();
|
|
15988
|
-
const pathCacheKey = `${subdomain}::${pathname}`;
|
|
15989
|
-
if (!fetchedContentPaths.has(pathCacheKey)) {
|
|
15990
|
-
fetchedContentPaths.add(pathCacheKey);
|
|
15991
|
-
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
15992
|
-
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
15993
|
-
if (!data?.content) return;
|
|
15994
|
-
contentCache.set(subdomain, data.content);
|
|
15995
|
-
applyFromCache();
|
|
15996
|
-
}).catch(() => {
|
|
15997
|
-
});
|
|
15998
|
-
}
|
|
15999
15942
|
observer = new MutationObserver(scheduleApply);
|
|
16000
15943
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
16001
15944
|
return () => {
|
|
@@ -16089,31 +16032,26 @@ function OhhwellsBridge() {
|
|
|
16089
16032
|
}, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
|
|
16090
16033
|
(0, import_react16.useEffect)(() => {
|
|
16091
16034
|
if (!isEditMode) return;
|
|
16092
|
-
let lastPosted = 0;
|
|
16093
16035
|
const measure = () => {
|
|
16094
16036
|
const h = document.body.scrollHeight;
|
|
16095
|
-
if (h > 50
|
|
16096
|
-
lastPosted = h;
|
|
16097
|
-
postToParent2({ type: "ow:height", height: h });
|
|
16098
|
-
}
|
|
16099
|
-
};
|
|
16100
|
-
let raf = null;
|
|
16101
|
-
const schedule = () => {
|
|
16102
|
-
if (raf != null) return;
|
|
16103
|
-
raf = requestAnimationFrame(() => {
|
|
16104
|
-
raf = null;
|
|
16105
|
-
measure();
|
|
16106
|
-
});
|
|
16037
|
+
if (h > 50) postToParent2({ type: "ow:height", height: h });
|
|
16107
16038
|
};
|
|
16108
16039
|
const t1 = setTimeout(measure, 50);
|
|
16109
16040
|
const t2 = setTimeout(measure, 500);
|
|
16110
|
-
|
|
16111
|
-
|
|
16041
|
+
let lastWidth = window.innerWidth;
|
|
16042
|
+
let resizeTimer = null;
|
|
16043
|
+
const handleResize = () => {
|
|
16044
|
+
if (window.innerWidth === lastWidth) return;
|
|
16045
|
+
lastWidth = window.innerWidth;
|
|
16046
|
+
if (resizeTimer) clearTimeout(resizeTimer);
|
|
16047
|
+
resizeTimer = setTimeout(measure, 150);
|
|
16048
|
+
};
|
|
16049
|
+
window.addEventListener("resize", handleResize);
|
|
16112
16050
|
return () => {
|
|
16113
16051
|
clearTimeout(t1);
|
|
16114
16052
|
clearTimeout(t2);
|
|
16115
|
-
if (
|
|
16116
|
-
|
|
16053
|
+
if (resizeTimer) clearTimeout(resizeTimer);
|
|
16054
|
+
window.removeEventListener("resize", handleResize);
|
|
16117
16055
|
};
|
|
16118
16056
|
}, [pathname, isEditMode, postToParent2]);
|
|
16119
16057
|
(0, import_react16.useEffect)(() => {
|
|
@@ -16157,7 +16095,9 @@ function OhhwellsBridge() {
|
|
|
16157
16095
|
[style*="100vh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
|
|
16158
16096
|
[style*="100svh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
|
|
16159
16097
|
[style*="100dvh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
|
|
16160
|
-
|
|
16098
|
+
/* Not the form: it is a layout container (flex/grid with gaps), and forcing block
|
|
16099
|
+
crushed its fields together (OHH-490). */
|
|
16100
|
+
[data-ohw-editable]:not([data-ohw-editable="form"]) {
|
|
16161
16101
|
display: block;
|
|
16162
16102
|
}
|
|
16163
16103
|
/* Body text (no item-action toolbar) \u2014 first click enters text edit \u2192 I-beam.
|
|
@@ -16183,6 +16123,35 @@ function OhhwellsBridge() {
|
|
|
16183
16123
|
[data-ohw-editable="video"], [data-ohw-editable="video"] *,
|
|
16184
16124
|
[data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
|
|
16185
16125
|
[data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
|
|
16126
|
+
/* A form field is a design surface in the editor: its input takes the pointer so the
|
|
16127
|
+
field can be picked and hovered from anywhere inside it (OHH-642). */
|
|
16128
|
+
[data-ohw-editable="form"] [data-ohw-form-field] input,
|
|
16129
|
+
[data-ohw-editable="form"] [data-ohw-form-field] textarea,
|
|
16130
|
+
[data-ohw-editable="form"] [data-ohw-form-field] label {
|
|
16131
|
+
pointer-events: auto !important;
|
|
16132
|
+
cursor: pointer !important;
|
|
16133
|
+
}
|
|
16134
|
+
/* While a field is selected its placeholder is being written in the value, so the
|
|
16135
|
+
text reads as a placeholder rather than as an answer (OHH-642). */
|
|
16136
|
+
/* A field is a design surface here: dragging its corner resized it past the form and
|
|
16137
|
+
left the chrome behind. Height stays adjustable, width does not (OHH-642). */
|
|
16138
|
+
[data-ohw-editable="form"] [data-ohw-form-field] textarea {
|
|
16139
|
+
resize: vertical !important;
|
|
16140
|
+
max-width: 100% !important;
|
|
16141
|
+
}
|
|
16142
|
+
[data-ohw-editable="form"] [data-ohw-form-field] input,
|
|
16143
|
+
[data-ohw-editable="form"] [data-ohw-form-field] textarea {
|
|
16144
|
+
box-sizing: border-box !important;
|
|
16145
|
+
width: 100% !important;
|
|
16146
|
+
}
|
|
16147
|
+
/* Somewhere to click the block itself, on every side (OHH-642) \u2014 editor only. */
|
|
16148
|
+
[data-ohw-editable="form"] {
|
|
16149
|
+
padding: 18px !important;
|
|
16150
|
+
}
|
|
16151
|
+
[data-ohw-placeholder-edit] {
|
|
16152
|
+
color: color-mix(in srgb, currentColor 55%, transparent) !important;
|
|
16153
|
+
cursor: text !important;
|
|
16154
|
+
}
|
|
16186
16155
|
/* Text hover chrome is drawn by the overlay (see hoveredTextRect) \u2014 the CSS outline
|
|
16187
16156
|
that used to draw it dashes denser than the overlay border, so identical specs
|
|
16188
16157
|
still read as two different frames (OHH-695). The attribute stays: hover paths
|
|
@@ -16263,12 +16232,53 @@ function OhhwellsBridge() {
|
|
|
16263
16232
|
return;
|
|
16264
16233
|
}
|
|
16265
16234
|
const target = e.target;
|
|
16266
|
-
if (target.closest("[data-ohw-ai-review]")) return;
|
|
16267
16235
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
16268
16236
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
16269
16237
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
16270
16238
|
if (isInsideLinkEditor(target)) return;
|
|
16271
|
-
if (
|
|
16239
|
+
if (target.closest("[data-ohw-form-toolbar]")) return;
|
|
16240
|
+
if (target.closest(
|
|
16241
|
+
'[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
|
|
16242
|
+
)) {
|
|
16243
|
+
return;
|
|
16244
|
+
}
|
|
16245
|
+
{
|
|
16246
|
+
const formEl = getFormElement(target);
|
|
16247
|
+
const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
|
|
16248
|
+
if (formEl && formKeyOf(formEl) && !onSuccessText) {
|
|
16249
|
+
const fieldEl = getFieldWrapper(target);
|
|
16250
|
+
if (fieldEl) {
|
|
16251
|
+
const label = target.closest("label");
|
|
16252
|
+
if (label && fieldPickElRef.current === fieldEl) return;
|
|
16253
|
+
e.preventDefault();
|
|
16254
|
+
e.stopPropagation();
|
|
16255
|
+
deactivateRef.current();
|
|
16256
|
+
deselectRef.current();
|
|
16257
|
+
formPickElRef.current = formEl;
|
|
16258
|
+
setFormPickRect(null);
|
|
16259
|
+
selectField(fieldEl);
|
|
16260
|
+
if (!label) fieldEl.querySelector("input, textarea")?.focus();
|
|
16261
|
+
return;
|
|
16262
|
+
}
|
|
16263
|
+
clearFieldPick();
|
|
16264
|
+
e.preventDefault();
|
|
16265
|
+
e.stopPropagation();
|
|
16266
|
+
deactivateRef.current();
|
|
16267
|
+
deselectRef.current();
|
|
16268
|
+
markFormFields(formEl);
|
|
16269
|
+
formHoverElRef.current = null;
|
|
16270
|
+
setFormHoverRect(null);
|
|
16271
|
+
formPickElRef.current = formEl;
|
|
16272
|
+
setFormPickRect(formEl.getBoundingClientRect());
|
|
16273
|
+
postToParentRef.current({
|
|
16274
|
+
type: "ow:form-selected",
|
|
16275
|
+
formKey: formKeyOf(formEl),
|
|
16276
|
+
hasLongText: formHasLongText(formEl)
|
|
16277
|
+
});
|
|
16278
|
+
return;
|
|
16279
|
+
}
|
|
16280
|
+
if (!formEl && formPickElRef.current) clearFormPick();
|
|
16281
|
+
}
|
|
16272
16282
|
if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
|
|
16273
16283
|
const beneath = document.elementsFromPoint(e.clientX, e.clientY).find(
|
|
16274
16284
|
(el) => el instanceof HTMLElement && !el.closest("[data-ohw-bridge-root]") && el.closest("[data-ohw-section]") != null
|
|
@@ -16336,15 +16346,9 @@ function OhhwellsBridge() {
|
|
|
16336
16346
|
if (logoEl) {
|
|
16337
16347
|
e.preventDefault();
|
|
16338
16348
|
e.stopPropagation();
|
|
16339
|
-
|
|
16340
|
-
|
|
16341
|
-
|
|
16342
|
-
const identity = readLogoIdentityFromDom();
|
|
16343
|
-
postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
|
|
16344
|
-
return;
|
|
16345
|
-
}
|
|
16346
|
-
selectLogoRef.current(logoEl);
|
|
16347
|
-
openLogoSizePanelRef.current(logoEl);
|
|
16349
|
+
deselectRef.current();
|
|
16350
|
+
deactivateRef.current();
|
|
16351
|
+
postToParentRef.current({ type: "ow:open-logo-settings" });
|
|
16348
16352
|
return;
|
|
16349
16353
|
}
|
|
16350
16354
|
const editable = target.closest("[data-ohw-editable]");
|
|
@@ -16499,12 +16503,10 @@ function OhhwellsBridge() {
|
|
|
16499
16503
|
};
|
|
16500
16504
|
const handleDblClick = (e) => {
|
|
16501
16505
|
const target = e.target;
|
|
16502
|
-
if (target.closest("[data-ohw-ai-review]")) return;
|
|
16503
16506
|
if (target.closest("[data-ohw-toolbar]")) return;
|
|
16504
16507
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
16505
16508
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
16506
16509
|
if (isInsideLinkEditor(target)) return;
|
|
16507
|
-
if (isInsideFloatingPanel(target)) return;
|
|
16508
16510
|
if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
|
|
16509
16511
|
return;
|
|
16510
16512
|
}
|
|
@@ -16532,16 +16534,26 @@ function OhhwellsBridge() {
|
|
|
16532
16534
|
setHoveredNavContainerRect(null);
|
|
16533
16535
|
return;
|
|
16534
16536
|
}
|
|
16535
|
-
if (
|
|
16537
|
+
if (target.closest(EDITOR_CHROME_SELECTOR) || isOverEditorChrome(e.clientX, e.clientY) || isInsideLinkEditor(target)) {
|
|
16538
|
+
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
16536
16539
|
hoveredItemElRef.current = null;
|
|
16537
16540
|
setHoveredItemRect(null);
|
|
16538
16541
|
hoveredNavContainerRef.current = null;
|
|
16539
16542
|
setHoveredNavContainerRect(null);
|
|
16543
|
+
formHoverElRef.current = null;
|
|
16544
|
+
setFormHoverRect(null);
|
|
16540
16545
|
siblingHintElRef.current = null;
|
|
16541
16546
|
setSiblingHintRect(null);
|
|
16542
16547
|
setSiblingHintRects([]);
|
|
16543
16548
|
return;
|
|
16544
16549
|
}
|
|
16550
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || target.closest("[data-ohw-floating-panel]")) {
|
|
16551
|
+
hoveredItemElRef.current = null;
|
|
16552
|
+
setHoveredItemRect(null);
|
|
16553
|
+
hoveredNavContainerRef.current = null;
|
|
16554
|
+
setHoveredNavContainerRect(null);
|
|
16555
|
+
return;
|
|
16556
|
+
}
|
|
16545
16557
|
{
|
|
16546
16558
|
const selected2 = selectedElRef.current;
|
|
16547
16559
|
const selectedIsFooterColumn = Boolean(selected2) && !isFooterLinksContainer(selected2) && (selected2.hasAttribute("data-ohw-footer-col") || selected2.hasAttribute("data-ohw-footer-column") || Boolean(selected2.closest("footer") && isInferredFooterGroup2(selected2)));
|
|
@@ -16584,7 +16596,7 @@ function OhhwellsBridge() {
|
|
|
16584
16596
|
setHoveredNavContainerRect(null);
|
|
16585
16597
|
if (selectedElRef.current === logoEl) return;
|
|
16586
16598
|
hoveredItemElRef.current = logoEl;
|
|
16587
|
-
setHoveredItemRect(
|
|
16599
|
+
setHoveredItemRect(logoEl.getBoundingClientRect());
|
|
16588
16600
|
return;
|
|
16589
16601
|
}
|
|
16590
16602
|
const navAnchor = getNavigationItemAnchor(target);
|
|
@@ -16608,6 +16620,26 @@ function OhhwellsBridge() {
|
|
|
16608
16620
|
return;
|
|
16609
16621
|
}
|
|
16610
16622
|
const editable = target.closest("[data-ohw-editable]");
|
|
16623
|
+
const hoverForm = getFormElement(target);
|
|
16624
|
+
if (hoverForm) {
|
|
16625
|
+
const hoverField = getFieldWrapper(target);
|
|
16626
|
+
const hoverTarget = hoverField ?? hoverForm;
|
|
16627
|
+
const selectedHere = hoverTarget === fieldPickElRef.current || hoverTarget === formPickElRef.current;
|
|
16628
|
+
hoveredItemElRef.current = null;
|
|
16629
|
+
setHoveredItemRect(null);
|
|
16630
|
+
if (selectedHere) {
|
|
16631
|
+
formHoverElRef.current = null;
|
|
16632
|
+
setFormHoverRect(null);
|
|
16633
|
+
} else {
|
|
16634
|
+
formHoverElRef.current = hoverTarget;
|
|
16635
|
+
setFormHoverRect(hoverTarget.getBoundingClientRect());
|
|
16636
|
+
}
|
|
16637
|
+
return;
|
|
16638
|
+
}
|
|
16639
|
+
if (formHoverElRef.current) {
|
|
16640
|
+
formHoverElRef.current = null;
|
|
16641
|
+
setFormHoverRect(null);
|
|
16642
|
+
}
|
|
16611
16643
|
if (!editable) return;
|
|
16612
16644
|
const selected = selectedElRef.current;
|
|
16613
16645
|
if (selected && (selected === editable || selected.contains(editable))) return;
|
|
@@ -16628,6 +16660,7 @@ function OhhwellsBridge() {
|
|
|
16628
16660
|
hoveredNavContainerRef.current = null;
|
|
16629
16661
|
setHoveredNavContainerRect(null);
|
|
16630
16662
|
hoveredItemElRef.current = editable;
|
|
16663
|
+
setHoveredItemRect(editable.getBoundingClientRect());
|
|
16631
16664
|
}
|
|
16632
16665
|
}
|
|
16633
16666
|
}
|
|
@@ -16835,7 +16868,7 @@ function OhhwellsBridge() {
|
|
|
16835
16868
|
setHoveredNavContainerRect(null);
|
|
16836
16869
|
if (selectedElRef.current !== logo) {
|
|
16837
16870
|
hoveredItemElRef.current = logo;
|
|
16838
|
-
setHoveredItemRect(
|
|
16871
|
+
setHoveredItemRect(logo.getBoundingClientRect());
|
|
16839
16872
|
}
|
|
16840
16873
|
return;
|
|
16841
16874
|
}
|
|
@@ -16924,7 +16957,7 @@ function OhhwellsBridge() {
|
|
|
16924
16957
|
}
|
|
16925
16958
|
};
|
|
16926
16959
|
const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
|
|
16927
|
-
if (linkPopoverOpenRef.current
|
|
16960
|
+
if (linkPopoverOpenRef.current) {
|
|
16928
16961
|
if (hoveredImageRef.current) {
|
|
16929
16962
|
hoveredImageRef.current = null;
|
|
16930
16963
|
hoveredImageHasTextOverlapRef.current = false;
|
|
@@ -17178,7 +17211,7 @@ function OhhwellsBridge() {
|
|
|
17178
17211
|
}
|
|
17179
17212
|
};
|
|
17180
17213
|
const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
|
|
17181
|
-
if (linkPopoverOpenRef.current ||
|
|
17214
|
+
if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
|
|
17182
17215
|
if (activeStateElRef.current) {
|
|
17183
17216
|
activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
|
|
17184
17217
|
activeStateElRef.current = null;
|
|
@@ -17244,19 +17277,24 @@ function OhhwellsBridge() {
|
|
|
17244
17277
|
setSectionGap(null);
|
|
17245
17278
|
}
|
|
17246
17279
|
};
|
|
17280
|
+
const pointOwnedByFloatingPanel = (clientX, clientY) => {
|
|
17281
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging")) return true;
|
|
17282
|
+
const panel = document.querySelector("[data-ohw-floating-panel]");
|
|
17283
|
+
if (!panel) return false;
|
|
17284
|
+
const rect = panel.getBoundingClientRect();
|
|
17285
|
+
return clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom;
|
|
17286
|
+
};
|
|
17247
17287
|
const handleMouseMove = (e) => {
|
|
17248
17288
|
const { clientX, clientY } = e;
|
|
17249
|
-
if (
|
|
17289
|
+
if (pointOwnedByFloatingPanel(clientX, clientY)) return;
|
|
17290
|
+
if (isOverEditorChrome(clientX, clientY)) {
|
|
17291
|
+
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
17292
|
+
formHoverElRef.current = null;
|
|
17293
|
+
setFormHoverRect(null);
|
|
17250
17294
|
hoveredItemElRef.current = null;
|
|
17251
17295
|
setHoveredItemRect(null);
|
|
17252
17296
|
hoveredNavContainerRef.current = null;
|
|
17253
17297
|
setHoveredNavContainerRect(null);
|
|
17254
|
-
siblingHintElRef.current = null;
|
|
17255
|
-
setSiblingHintRect(null);
|
|
17256
|
-
setSiblingHintRects([]);
|
|
17257
|
-
dismissImageHover();
|
|
17258
|
-
clearImageHover();
|
|
17259
|
-
setSectionGap(null);
|
|
17260
17298
|
return;
|
|
17261
17299
|
}
|
|
17262
17300
|
probeSectionGapAt(clientX, clientY);
|
|
@@ -17267,11 +17305,7 @@ function OhhwellsBridge() {
|
|
|
17267
17305
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
17268
17306
|
const { clientX, clientY } = e.data;
|
|
17269
17307
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
17270
|
-
if (
|
|
17271
|
-
dismissImageHover();
|
|
17272
|
-
clearImageHover();
|
|
17273
|
-
return;
|
|
17274
|
-
}
|
|
17308
|
+
if (pointOwnedByFloatingPanel(clientX, clientY)) return;
|
|
17275
17309
|
probeSectionGapAt(clientX, clientY);
|
|
17276
17310
|
probeImageAt(clientX, clientY);
|
|
17277
17311
|
probeHoverCardsAt(clientX, clientY);
|
|
@@ -17521,15 +17555,6 @@ function OhhwellsBridge() {
|
|
|
17521
17555
|
aiSectionsRef.current = content[AI_SECTIONS_KEY];
|
|
17522
17556
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
17523
17557
|
}
|
|
17524
|
-
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
17525
|
-
brandKitRef.current = content[BRAND_KIT_KEY];
|
|
17526
|
-
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
17527
|
-
}
|
|
17528
|
-
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
17529
|
-
stylesRef.current = content[STYLE_STORE_KEY];
|
|
17530
|
-
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
17531
|
-
}
|
|
17532
|
-
applyBrandChrome(content);
|
|
17533
17558
|
let sectionsJson = null;
|
|
17534
17559
|
for (const [key, val] of Object.entries(content)) {
|
|
17535
17560
|
if (key === "__ohw_sections") {
|
|
@@ -17537,11 +17562,6 @@ function OhhwellsBridge() {
|
|
|
17537
17562
|
continue;
|
|
17538
17563
|
}
|
|
17539
17564
|
if (key === AI_SECTIONS_KEY) continue;
|
|
17540
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17541
|
-
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
17542
|
-
if (key === BRAND_KIT_KEY) continue;
|
|
17543
|
-
if (key === STYLE_STORE_KEY) continue;
|
|
17544
|
-
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17545
17565
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17546
17566
|
if (applyCarouselNode(key, val)) continue;
|
|
17547
17567
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17561,8 +17581,6 @@ function OhhwellsBridge() {
|
|
|
17561
17581
|
});
|
|
17562
17582
|
applyLinkByKey(key, val);
|
|
17563
17583
|
}
|
|
17564
|
-
applyLogoFromContent(content);
|
|
17565
|
-
applyLogoSizes(content);
|
|
17566
17584
|
if (sectionsJson) {
|
|
17567
17585
|
initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
|
|
17568
17586
|
sectionsLoadedRef.current = true;
|
|
@@ -17578,58 +17596,6 @@ function OhhwellsBridge() {
|
|
|
17578
17596
|
if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
|
|
17579
17597
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
17580
17598
|
};
|
|
17581
|
-
const handleUpdateLogoIdentity = (e) => {
|
|
17582
|
-
if (e.data?.type !== "ow:update-logo-identity") return;
|
|
17583
|
-
const rawText = typeof e.data.text === "string" ? e.data.text : "";
|
|
17584
|
-
const alt = typeof e.data.alt === "string" ? e.data.alt : rawText;
|
|
17585
|
-
const href = typeof e.data.href === "string" ? e.data.href : void 0;
|
|
17586
|
-
const imageProvided = "image" in e.data;
|
|
17587
|
-
const imageUrl = imageProvided && typeof e.data.image === "string" && e.data.image.trim() ? e.data.image.trim() : imageProvided ? null : void 0;
|
|
17588
|
-
let isPlaceholder = e.data.isPlaceholder !== false;
|
|
17589
|
-
if (imageUrl) isPlaceholder = false;
|
|
17590
|
-
else if (imageProvided && imageUrl === null) {
|
|
17591
|
-
isPlaceholder = e.data.isPlaceholder === true || !rawText.trim() || resolveLogoDisplayText(rawText) === PLACEHOLDER_BUSINESS_NAME;
|
|
17592
|
-
}
|
|
17593
|
-
const display = applyLogoIdentity(rawText, isPlaceholder);
|
|
17594
|
-
const displayAlt = resolveLogoDisplayText(alt || display);
|
|
17595
|
-
if (imageUrl !== void 0) {
|
|
17596
|
-
applyLogoImage(imageUrl, displayAlt);
|
|
17597
|
-
} else {
|
|
17598
|
-
for (const key of LOGO_IMAGE_KEYS) {
|
|
17599
|
-
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
17600
|
-
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
17601
|
-
if (img) img.alt = displayAlt;
|
|
17602
|
-
});
|
|
17603
|
-
}
|
|
17604
|
-
}
|
|
17605
|
-
if (href !== void 0) {
|
|
17606
|
-
applyLogoHref(href);
|
|
17607
|
-
applyLinkByKey("nav-logo-href", href);
|
|
17608
|
-
applyLinkByKey("footer-logo-href", href);
|
|
17609
|
-
applyLinkByKey("logo-href", href);
|
|
17610
|
-
}
|
|
17611
|
-
const nodes = [
|
|
17612
|
-
...LOGO_TEXT_KEYS.map((key) => ({ key, text: display })),
|
|
17613
|
-
{ key: LOGO_PLACEHOLDER_KEY, text: isPlaceholder ? "true" : "false" },
|
|
17614
|
-
{ key: LOGO_ALT_KEY, text: displayAlt }
|
|
17615
|
-
];
|
|
17616
|
-
if (imageUrl !== void 0) {
|
|
17617
|
-
if (imageUrl) {
|
|
17618
|
-
for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: imageUrl });
|
|
17619
|
-
} else {
|
|
17620
|
-
for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: "" });
|
|
17621
|
-
}
|
|
17622
|
-
}
|
|
17623
|
-
if (href !== void 0) {
|
|
17624
|
-
for (const key of LOGO_HREF_KEYS) nodes.push({ key, text: href.trim() || "/" });
|
|
17625
|
-
}
|
|
17626
|
-
editContentRef.current = {
|
|
17627
|
-
...editContentRef.current,
|
|
17628
|
-
...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
|
|
17629
|
-
};
|
|
17630
|
-
applyLogoSizes(editContentRef.current);
|
|
17631
|
-
postToParentRef.current({ type: "ow:change", nodes });
|
|
17632
|
-
};
|
|
17633
17599
|
window.addEventListener("message", handleHydrate);
|
|
17634
17600
|
const postAiSectionsChanged = () => {
|
|
17635
17601
|
postToParentRef.current({
|
|
@@ -17643,10 +17609,7 @@ function OhhwellsBridge() {
|
|
|
17643
17609
|
const payload = e.data.payload;
|
|
17644
17610
|
if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
|
|
17645
17611
|
const previous = aiSectionsRef.current;
|
|
17646
|
-
const nextState = applyTreeToState(parseAiSectionsState(previous),
|
|
17647
|
-
...payload,
|
|
17648
|
-
path: payload.path ?? window.location.pathname
|
|
17649
|
-
});
|
|
17612
|
+
const nextState = applyTreeToState(parseAiSectionsState(previous), payload);
|
|
17650
17613
|
const nextValue = serializeAiSectionsState(nextState);
|
|
17651
17614
|
aiSectionsRef.current = nextValue;
|
|
17652
17615
|
applyAiSectionsToDom(nextState);
|
|
@@ -17683,42 +17646,12 @@ function OhhwellsBridge() {
|
|
|
17683
17646
|
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
17684
17647
|
aiSectionsRef.current = value;
|
|
17685
17648
|
applyAiSectionsToDom(parseAiSectionsState(value));
|
|
17686
|
-
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
17687
17649
|
const restoredHeight = document.documentElement.scrollHeight;
|
|
17688
17650
|
if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
|
|
17689
17651
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
|
|
17690
17652
|
postAiSectionsChanged();
|
|
17691
17653
|
};
|
|
17692
17654
|
window.addEventListener("message", handleAiSetSections);
|
|
17693
|
-
const handleAiSetBrand = (e) => {
|
|
17694
|
-
if (e.data?.type !== "ow:ai-set-brand") return;
|
|
17695
|
-
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
17696
|
-
const previous = brandKitRef.current;
|
|
17697
|
-
brandKitRef.current = value;
|
|
17698
|
-
applyBrandToDom(parseBrandKit(value));
|
|
17699
|
-
if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
|
|
17700
|
-
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
17701
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
|
|
17702
|
-
postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
|
|
17703
|
-
};
|
|
17704
|
-
window.addEventListener("message", handleAiSetBrand);
|
|
17705
|
-
const handleAiSetStyles = (e) => {
|
|
17706
|
-
if (e.data?.type !== "ow:ai-set-styles") return;
|
|
17707
|
-
const value = typeof e.data.value === "string" ? e.data.value : "";
|
|
17708
|
-
const previous = stylesRef.current;
|
|
17709
|
-
stylesRef.current = value;
|
|
17710
|
-
applyStylesToDom(parseStyleStore(value));
|
|
17711
|
-
postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
|
|
17712
|
-
postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
|
|
17713
|
-
};
|
|
17714
|
-
window.addEventListener("message", handleAiSetStyles);
|
|
17715
|
-
const handleGetBrand = (e) => {
|
|
17716
|
-
if (e.data?.type !== "ow:get-brand") return;
|
|
17717
|
-
const template = deriveTemplateBrand();
|
|
17718
|
-
const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
|
|
17719
|
-
postToParentRef.current({ type: "ow:brand-value", value });
|
|
17720
|
-
};
|
|
17721
|
-
window.addEventListener("message", handleGetBrand);
|
|
17722
17655
|
const handleDeactivate = (e) => {
|
|
17723
17656
|
if (e.data?.type !== "ow:deactivate") return;
|
|
17724
17657
|
if (Date.now() < linkPopoverGraceUntilRef.current) return;
|
|
@@ -17727,12 +17660,6 @@ function OhhwellsBridge() {
|
|
|
17727
17660
|
closeLinkPopoverRef.current();
|
|
17728
17661
|
return;
|
|
17729
17662
|
}
|
|
17730
|
-
if (floatingPanelOpenRef.current) {
|
|
17731
|
-
setFloatingPanelRef.current(null);
|
|
17732
|
-
deselectRef.current();
|
|
17733
|
-
deactivateRef.current();
|
|
17734
|
-
return;
|
|
17735
|
-
}
|
|
17736
17663
|
deselectRef.current();
|
|
17737
17664
|
deactivateRef.current();
|
|
17738
17665
|
};
|
|
@@ -17786,10 +17713,6 @@ function OhhwellsBridge() {
|
|
|
17786
17713
|
return;
|
|
17787
17714
|
}
|
|
17788
17715
|
if (selectedElRef.current) {
|
|
17789
|
-
if (toolbarVariantRef.current === "logo") {
|
|
17790
|
-
deselectRef.current();
|
|
17791
|
-
return;
|
|
17792
|
-
}
|
|
17793
17716
|
if (toolbarVariantRef.current === "select-frame") {
|
|
17794
17717
|
const parent2 = getNavigationSelectionParent(selectedElRef.current);
|
|
17795
17718
|
if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
|
|
@@ -17809,6 +17732,13 @@ function OhhwellsBridge() {
|
|
|
17809
17732
|
}
|
|
17810
17733
|
}
|
|
17811
17734
|
};
|
|
17735
|
+
const handleFormCount = (e) => {
|
|
17736
|
+
if (e.data?.type !== "ow:form-count") return;
|
|
17737
|
+
const form = formPickElRef.current;
|
|
17738
|
+
if (!form || formKeyOf(form) !== e.data.formKey) return;
|
|
17739
|
+
setFormPickCount(typeof e.data.count === "number" ? e.data.count : null);
|
|
17740
|
+
};
|
|
17741
|
+
window.addEventListener("message", handleFormCount);
|
|
17812
17742
|
window.addEventListener("message", handleUiEscape);
|
|
17813
17743
|
const handleKeyDown = (e) => {
|
|
17814
17744
|
if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
|
|
@@ -17828,11 +17758,12 @@ function OhhwellsBridge() {
|
|
|
17828
17758
|
closeFloatingPanelOnlyRef.current();
|
|
17829
17759
|
return;
|
|
17830
17760
|
}
|
|
17761
|
+
if (e.key === "Escape" && formPickElRef.current) {
|
|
17762
|
+
e.preventDefault();
|
|
17763
|
+
clearFormPickRef.current();
|
|
17764
|
+
return;
|
|
17765
|
+
}
|
|
17831
17766
|
if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
|
|
17832
|
-
if (toolbarVariantRef.current === "logo") {
|
|
17833
|
-
deselectRef.current();
|
|
17834
|
-
return;
|
|
17835
|
-
}
|
|
17836
17767
|
if (toolbarVariantRef.current === "select-frame") {
|
|
17837
17768
|
const parent2 = getNavigationSelectionParent(selectedElRef.current);
|
|
17838
17769
|
if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
|
|
@@ -17910,8 +17841,7 @@ function OhhwellsBridge() {
|
|
|
17910
17841
|
const handleScroll = () => {
|
|
17911
17842
|
const focusEl = activeElRef.current ?? selectedElRef.current;
|
|
17912
17843
|
if (focusEl) {
|
|
17913
|
-
const
|
|
17914
|
-
const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
|
|
17844
|
+
const r2 = activeElRef.current ? getEditMeasureEl(activeElRef.current).getBoundingClientRect() : focusEl.getBoundingClientRect();
|
|
17915
17845
|
applyToolbarPos(r2);
|
|
17916
17846
|
setToolbarRect(r2);
|
|
17917
17847
|
setMaxBadge((prev) => prev && activeElRef.current ? { ...prev, rect: r2 } : prev);
|
|
@@ -17921,9 +17851,7 @@ function OhhwellsBridge() {
|
|
|
17921
17851
|
setToggleState((prev) => prev ? { ...prev, rect } : null);
|
|
17922
17852
|
}
|
|
17923
17853
|
if (hoveredItemElRef.current) {
|
|
17924
|
-
|
|
17925
|
-
const logo = getLogoElement(hoverEl);
|
|
17926
|
-
setHoveredItemRect(logo ? getLogoInteractionRect(logo) : hoverEl.getBoundingClientRect());
|
|
17854
|
+
setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
|
|
17927
17855
|
}
|
|
17928
17856
|
if (hoveredNavContainerRef.current) {
|
|
17929
17857
|
setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
|
|
@@ -17967,12 +17895,13 @@ function OhhwellsBridge() {
|
|
|
17967
17895
|
if (aiSectionsRef.current) {
|
|
17968
17896
|
nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
|
|
17969
17897
|
}
|
|
17970
|
-
|
|
17971
|
-
|
|
17972
|
-
|
|
17973
|
-
|
|
17974
|
-
|
|
17975
|
-
|
|
17898
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
|
|
17899
|
+
const formKey = formKeyOf(form);
|
|
17900
|
+
if (!formKey) return;
|
|
17901
|
+
const specs = readFieldsFromDom(form);
|
|
17902
|
+
const text = specs.length ? JSON.stringify(specs) : editContentRef.current[fieldsKey(formKey)];
|
|
17903
|
+
if (text) nodes.push({ key: fieldsKey(formKey), type: "text", text });
|
|
17904
|
+
});
|
|
17976
17905
|
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
17977
17906
|
};
|
|
17978
17907
|
const handleInsertSection = (e) => {
|
|
@@ -17983,12 +17912,8 @@ function OhhwellsBridge() {
|
|
|
17983
17912
|
if (inserted) {
|
|
17984
17913
|
const tracker = getSectionsTracker();
|
|
17985
17914
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
17986
|
-
const
|
|
17987
|
-
|
|
17988
|
-
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
17989
|
-
};
|
|
17990
|
-
reportHeight();
|
|
17991
|
-
setTimeout(reportHeight, 500);
|
|
17915
|
+
const h = document.documentElement.scrollHeight;
|
|
17916
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
17992
17917
|
}
|
|
17993
17918
|
};
|
|
17994
17919
|
const handleSwitchSchedule = (e) => {
|
|
@@ -18181,17 +18106,13 @@ function OhhwellsBridge() {
|
|
|
18181
18106
|
if (e.data?.type !== "ow:parent-scroll") return;
|
|
18182
18107
|
const { iframeOffsetTop, headerH, canvasH } = e.data;
|
|
18183
18108
|
parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
|
|
18184
|
-
if (floatingPanelOpenRef.current) {
|
|
18185
|
-
setParentScrollSnap({ iframeOffsetTop, headerH, canvasH });
|
|
18186
|
-
}
|
|
18187
18109
|
if (visibleViewportRef.current) {
|
|
18188
18110
|
applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
|
|
18189
18111
|
}
|
|
18190
18112
|
const focusEl = activeElRef.current ?? selectedElRef.current;
|
|
18191
18113
|
if (focusEl) {
|
|
18192
|
-
const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) :
|
|
18193
|
-
|
|
18194
|
-
applyToolbarPos(r2);
|
|
18114
|
+
const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : focusEl;
|
|
18115
|
+
applyToolbarPos(measureEl.getBoundingClientRect());
|
|
18195
18116
|
}
|
|
18196
18117
|
};
|
|
18197
18118
|
const handleClickAt = (e) => {
|
|
@@ -18224,15 +18145,9 @@ function OhhwellsBridge() {
|
|
|
18224
18145
|
return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
|
|
18225
18146
|
});
|
|
18226
18147
|
if (logoAtPoint) {
|
|
18227
|
-
|
|
18228
|
-
|
|
18229
|
-
|
|
18230
|
-
const identity = readLogoIdentityFromDom();
|
|
18231
|
-
postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
|
|
18232
|
-
return;
|
|
18233
|
-
}
|
|
18234
|
-
selectLogoRef.current(logoAtPoint);
|
|
18235
|
-
openLogoSizePanelRef.current(logoAtPoint);
|
|
18148
|
+
deselectRef.current();
|
|
18149
|
+
deactivateRef.current();
|
|
18150
|
+
postToParentRef.current({ type: "ow:open-logo-settings" });
|
|
18236
18151
|
return;
|
|
18237
18152
|
}
|
|
18238
18153
|
const textEditable = Array.from(
|
|
@@ -18306,14 +18221,6 @@ function OhhwellsBridge() {
|
|
|
18306
18221
|
window.addEventListener("message", handleParentScroll);
|
|
18307
18222
|
window.addEventListener("message", handlePointerSync);
|
|
18308
18223
|
window.addEventListener("message", handleClickAt);
|
|
18309
|
-
window.addEventListener("message", handleUpdateLogoIdentity);
|
|
18310
|
-
const handleViewMode = (e) => {
|
|
18311
|
-
if (e.data?.type !== "ow:view-mode") return;
|
|
18312
|
-
const mode = e.data.mode === "Mobile" || e.data.mode === "mobile" ? "mobile" : "desktop";
|
|
18313
|
-
setEditorViewport(mode);
|
|
18314
|
-
applyLogoSizes(editContentRef.current);
|
|
18315
|
-
};
|
|
18316
|
-
window.addEventListener("message", handleViewMode);
|
|
18317
18224
|
const handleViewportResize = () => {
|
|
18318
18225
|
if (visibleViewportRef.current) {
|
|
18319
18226
|
applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
|
|
@@ -18369,17 +18276,13 @@ function OhhwellsBridge() {
|
|
|
18369
18276
|
window.removeEventListener("resize", handleViewportResize);
|
|
18370
18277
|
window.removeEventListener("message", handlePointerSync);
|
|
18371
18278
|
window.removeEventListener("message", handleClickAt);
|
|
18372
|
-
window.removeEventListener("message", handleUpdateLogoIdentity);
|
|
18373
|
-
window.removeEventListener("message", handleViewMode);
|
|
18374
18279
|
window.removeEventListener("message", handleHydrate);
|
|
18375
18280
|
window.removeEventListener("message", handleAiApplyTree);
|
|
18376
18281
|
window.removeEventListener("message", handleAiDeleteSection);
|
|
18377
18282
|
window.removeEventListener("message", handleAiSetSections);
|
|
18378
|
-
window.removeEventListener("message", handleAiSetBrand);
|
|
18379
|
-
window.removeEventListener("message", handleAiSetStyles);
|
|
18380
|
-
window.removeEventListener("message", handleGetBrand);
|
|
18381
18283
|
window.removeEventListener("message", handleDeactivate);
|
|
18382
18284
|
window.removeEventListener("message", handleToastAction);
|
|
18285
|
+
window.removeEventListener("message", handleFormCount);
|
|
18383
18286
|
window.removeEventListener("message", handleUiEscape);
|
|
18384
18287
|
autoSaveTimers.current.forEach(clearTimeout);
|
|
18385
18288
|
autoSaveTimers.current.clear();
|
|
@@ -18403,7 +18306,9 @@ function OhhwellsBridge() {
|
|
|
18403
18306
|
if (footerDragRef.current) return;
|
|
18404
18307
|
const target = e.target;
|
|
18405
18308
|
if (!target) return;
|
|
18406
|
-
if (target.closest(
|
|
18309
|
+
if (target.closest(
|
|
18310
|
+
'[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root], [data-ohw-floating-panel], [data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"]'
|
|
18311
|
+
)) {
|
|
18407
18312
|
return;
|
|
18408
18313
|
}
|
|
18409
18314
|
if (target.closest("[data-ohw-item-drag-surface]")) return;
|
|
@@ -18581,7 +18486,7 @@ function OhhwellsBridge() {
|
|
|
18581
18486
|
postToParent2({
|
|
18582
18487
|
type: "ow:ready",
|
|
18583
18488
|
version: "1",
|
|
18584
|
-
bridgeVersion: "0.1.
|
|
18489
|
+
bridgeVersion: "0.1.63",
|
|
18585
18490
|
path: pathname,
|
|
18586
18491
|
nodes: collectEditableNodes(editContentRef.current),
|
|
18587
18492
|
sections
|
|
@@ -19048,6 +18953,151 @@ function OhhwellsBridge() {
|
|
|
19048
18953
|
hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
|
|
19049
18954
|
hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
|
|
19050
18955
|
hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
|
|
18956
|
+
formPickRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
18957
|
+
ItemInteractionLayer,
|
|
18958
|
+
{
|
|
18959
|
+
rect: formPickRect,
|
|
18960
|
+
state: "active-top",
|
|
18961
|
+
itemDragSurface: false,
|
|
18962
|
+
toolbarAlign: "left",
|
|
18963
|
+
chromeGap: 24,
|
|
18964
|
+
toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
18965
|
+
"div",
|
|
18966
|
+
{
|
|
18967
|
+
"data-ohw-form-toolbar": "",
|
|
18968
|
+
className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
|
|
18969
|
+
children: [
|
|
18970
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
18971
|
+
"button",
|
|
18972
|
+
{
|
|
18973
|
+
type: "button",
|
|
18974
|
+
"aria-label": "Add field",
|
|
18975
|
+
className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
|
|
18976
|
+
onClick: () => setFieldTypePickerOpen((open) => !open),
|
|
18977
|
+
"data-ohw-add-field": "",
|
|
18978
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.Plus, { size: 15, "aria-hidden": true })
|
|
18979
|
+
}
|
|
18980
|
+
),
|
|
18981
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
|
|
18982
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
18983
|
+
"button",
|
|
18984
|
+
{
|
|
18985
|
+
type: "button",
|
|
18986
|
+
className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-semibold text-foreground transition-colors hover:bg-muted/80",
|
|
18987
|
+
onClick: () => {
|
|
18988
|
+
const form = formPickElRef.current;
|
|
18989
|
+
if (!form) return;
|
|
18990
|
+
postToParent2({
|
|
18991
|
+
type: "ow:form-pick",
|
|
18992
|
+
formKey: formKeyOf(form),
|
|
18993
|
+
hasLongText: formHasLongText(form)
|
|
18994
|
+
});
|
|
18995
|
+
},
|
|
18996
|
+
children: [
|
|
18997
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.Settings, { size: 14, "aria-hidden": true }),
|
|
18998
|
+
"Form settings",
|
|
18999
|
+
formPickCount ? (
|
|
19000
|
+
// Counter pill, per the design — not a text suffix.
|
|
19001
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
19002
|
+
"span",
|
|
19003
|
+
{
|
|
19004
|
+
"data-ohw-form-count": "",
|
|
19005
|
+
className: "ml-0.5 inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-full bg-primary px-1.5 text-[11px] font-semibold text-primary-foreground",
|
|
19006
|
+
children: formPickCount
|
|
19007
|
+
}
|
|
19008
|
+
)
|
|
19009
|
+
) : null
|
|
19010
|
+
]
|
|
19011
|
+
}
|
|
19012
|
+
),
|
|
19013
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
|
|
19014
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
19015
|
+
"button",
|
|
19016
|
+
{
|
|
19017
|
+
type: "button",
|
|
19018
|
+
"aria-pressed": formViewState === state,
|
|
19019
|
+
className: "rounded-md px-2.5 py-1 text-[13px] font-semibold capitalize transition-colors " + (formViewState === state ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"),
|
|
19020
|
+
onClick: () => {
|
|
19021
|
+
const form = formPickElRef.current;
|
|
19022
|
+
const key = form ? formKeyOf(form) : null;
|
|
19023
|
+
if (!form || !key) return;
|
|
19024
|
+
const initial = successInitialFor(form, key, editContentRef.current);
|
|
19025
|
+
setFormViewState(form, key, state, initial);
|
|
19026
|
+
setFormViewStateUi(state);
|
|
19027
|
+
setFormPickRect(form.getBoundingClientRect());
|
|
19028
|
+
if (state === "success") {
|
|
19029
|
+
const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
|
|
19030
|
+
if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
|
|
19031
|
+
} else {
|
|
19032
|
+
deactivateRef.current();
|
|
19033
|
+
}
|
|
19034
|
+
},
|
|
19035
|
+
children: state
|
|
19036
|
+
},
|
|
19037
|
+
state
|
|
19038
|
+
)) })
|
|
19039
|
+
]
|
|
19040
|
+
}
|
|
19041
|
+
)
|
|
19042
|
+
}
|
|
19043
|
+
),
|
|
19044
|
+
formHoverRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
19045
|
+
ItemInteractionLayer,
|
|
19046
|
+
{
|
|
19047
|
+
rect: formHoverRect,
|
|
19048
|
+
state: "hover",
|
|
19049
|
+
chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
|
|
19050
|
+
}
|
|
19051
|
+
),
|
|
19052
|
+
fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
19053
|
+
ItemInteractionLayer,
|
|
19054
|
+
{
|
|
19055
|
+
rect: fieldPickRect,
|
|
19056
|
+
state: fieldDragging ? "dragging" : "active-top",
|
|
19057
|
+
itemDragSurface: false,
|
|
19058
|
+
toolbarAlign: "left",
|
|
19059
|
+
chromeGap: 10,
|
|
19060
|
+
showHandle: true,
|
|
19061
|
+
dragHandleLabel: "Reorder field",
|
|
19062
|
+
onDragHandleDragStart: handleFieldDragStart,
|
|
19063
|
+
onDragHandleDragEnd: handleFieldDragEnd,
|
|
19064
|
+
toolbar: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
19065
|
+
FormFieldToolbar,
|
|
19066
|
+
{
|
|
19067
|
+
type: fieldPickState.type,
|
|
19068
|
+
required: fieldPickState.required,
|
|
19069
|
+
onTypeChange: handleFieldTypeChange,
|
|
19070
|
+
onRequiredToggle: handleFieldRequiredToggle,
|
|
19071
|
+
onDuplicate: handleFieldDuplicate,
|
|
19072
|
+
onDelete: handleFieldDelete
|
|
19073
|
+
}
|
|
19074
|
+
)
|
|
19075
|
+
}
|
|
19076
|
+
),
|
|
19077
|
+
fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
19078
|
+
"div",
|
|
19079
|
+
{
|
|
19080
|
+
className: "pointer-events-none fixed z-[2147483644]",
|
|
19081
|
+
style: { top: slot.top, left: slot.left, width: slot.width },
|
|
19082
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
19083
|
+
DropIndicator,
|
|
19084
|
+
{
|
|
19085
|
+
direction: "horizontal",
|
|
19086
|
+
state: fieldDropIndex === i ? "dragActive" : "dragIdle",
|
|
19087
|
+
className: "!w-full"
|
|
19088
|
+
}
|
|
19089
|
+
)
|
|
19090
|
+
},
|
|
19091
|
+
`field-drop-${i}`
|
|
19092
|
+
)) : null,
|
|
19093
|
+
fieldTypePickerOpen && formPickRect && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
19094
|
+
"div",
|
|
19095
|
+
{
|
|
19096
|
+
className: "pointer-events-none fixed z-[2147483645]",
|
|
19097
|
+
style: { top: formPickRect.top + 16, left: formPickRect.left + 24 },
|
|
19098
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(FieldTypePicker, { onPick: handleAddField })
|
|
19099
|
+
}
|
|
19100
|
+
),
|
|
19051
19101
|
toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
|
|
19052
19102
|
toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
19053
19103
|
FooterContainerChrome,
|
|
@@ -19057,7 +19107,7 @@ function OhhwellsBridge() {
|
|
|
19057
19107
|
addDisabled: !canAddFooterColumn()
|
|
19058
19108
|
}
|
|
19059
19109
|
),
|
|
19060
|
-
toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame"
|
|
19110
|
+
toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
19061
19111
|
ItemInteractionLayer,
|
|
19062
19112
|
{
|
|
19063
19113
|
rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
|
|
@@ -19069,9 +19119,9 @@ function OhhwellsBridge() {
|
|
|
19069
19119
|
dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
|
|
19070
19120
|
onDragHandleDragStart: handleItemDragStart,
|
|
19071
19121
|
onDragHandleDragEnd: handleItemDragEnd,
|
|
19072
|
-
onItemPointerDown:
|
|
19073
|
-
onItemClick:
|
|
19074
|
-
itemDragSurface:
|
|
19122
|
+
onItemPointerDown: handleItemChromePointerDown,
|
|
19123
|
+
onItemClick: handleItemChromeClick,
|
|
19124
|
+
itemDragSurface: !isFooterFrameSelection,
|
|
19075
19125
|
toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
19076
19126
|
ItemActionToolbar,
|
|
19077
19127
|
{
|
|
@@ -19234,115 +19284,11 @@ function OhhwellsBridge() {
|
|
|
19234
19284
|
}
|
|
19235
19285
|
)
|
|
19236
19286
|
}
|
|
19237
|
-
) : null,
|
|
19238
|
-
floatingPanel && floatingPanel.kind === "logo-size" && logoSizeDraft ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
19239
|
-
FloatingPanel,
|
|
19240
|
-
{
|
|
19241
|
-
open: true,
|
|
19242
|
-
title: floatingPanel.title,
|
|
19243
|
-
context: floatingPanel.context,
|
|
19244
|
-
position: floatingPanelPos,
|
|
19245
|
-
onPositionChange: setFloatingPanelPos,
|
|
19246
|
-
parentScroll: parentScrollSnap ?? parentScrollRef.current,
|
|
19247
|
-
onClose: closeFloatingPanelAndDeselect,
|
|
19248
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
19249
|
-
LogoSizePanel,
|
|
19250
|
-
{
|
|
19251
|
-
viewport: editorViewport,
|
|
19252
|
-
sizePx: editorViewport === "mobile" && !logoSizeDraft.mobileFollowing ? logoSizeDraft.mobilePx : logoSizeDraft.desktopPx,
|
|
19253
|
-
mobileFollowing: logoSizeDraft.mobileFollowing,
|
|
19254
|
-
onSizeChange: (px) => {
|
|
19255
|
-
const next = editorViewport === "mobile" ? { ...logoSizeDraft, mobilePx: px, mobileFollowing: false } : {
|
|
19256
|
-
...logoSizeDraft,
|
|
19257
|
-
desktopPx: px,
|
|
19258
|
-
mobilePx: logoSizeDraft.mobileFollowing ? px : logoSizeDraft.mobilePx
|
|
19259
|
-
};
|
|
19260
|
-
setLogoSizeDraft(next);
|
|
19261
|
-
persistLogoSizeDraft(floatingPanel.placement, next);
|
|
19262
|
-
},
|
|
19263
|
-
onCustomizeMobile: () => {
|
|
19264
|
-
const next = {
|
|
19265
|
-
...logoSizeDraft,
|
|
19266
|
-
mobileFollowing: false,
|
|
19267
|
-
mobilePx: logoSizeDraft.desktopPx
|
|
19268
|
-
};
|
|
19269
|
-
setLogoSizeDraft(next);
|
|
19270
|
-
persistLogoSizeDraft(floatingPanel.placement, next);
|
|
19271
|
-
},
|
|
19272
|
-
onResetMobile: () => {
|
|
19273
|
-
const next = {
|
|
19274
|
-
...logoSizeDraft,
|
|
19275
|
-
mobileFollowing: true,
|
|
19276
|
-
mobilePx: logoSizeDraft.desktopPx
|
|
19277
|
-
};
|
|
19278
|
-
setLogoSizeDraft(next);
|
|
19279
|
-
persistLogoSizeDraft(floatingPanel.placement, next);
|
|
19280
|
-
},
|
|
19281
|
-
onUpdateEverywhere: () => {
|
|
19282
|
-
const identity = readLogoIdentityFromDom();
|
|
19283
|
-
postToParent2({ type: "ow:open-logo-settings", ...identity });
|
|
19284
|
-
}
|
|
19285
|
-
}
|
|
19286
|
-
)
|
|
19287
|
-
}
|
|
19288
19287
|
) : null
|
|
19289
19288
|
] }),
|
|
19290
19289
|
bridgeRoot
|
|
19291
19290
|
) : null;
|
|
19292
19291
|
}
|
|
19293
|
-
|
|
19294
|
-
// src/ui/EmptySection.tsx
|
|
19295
|
-
var import_link = __toESM(require("next/link"), 1);
|
|
19296
|
-
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
19297
|
-
function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
|
|
19298
|
-
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
19299
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
19300
|
-
"p",
|
|
19301
|
-
{
|
|
19302
|
-
style: {
|
|
19303
|
-
fontFamily: "var(--brand-font-body)",
|
|
19304
|
-
fontSize: "0.75rem",
|
|
19305
|
-
fontWeight: 500,
|
|
19306
|
-
letterSpacing: "0.15em",
|
|
19307
|
-
textTransform: "uppercase",
|
|
19308
|
-
color: "var(--brand-accent)",
|
|
19309
|
-
marginBottom: "1.5rem"
|
|
19310
|
-
},
|
|
19311
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_link.default, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
|
|
19312
|
-
}
|
|
19313
|
-
),
|
|
19314
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
19315
|
-
"h1",
|
|
19316
|
-
{
|
|
19317
|
-
style: {
|
|
19318
|
-
fontFamily: "var(--brand-font-heading)",
|
|
19319
|
-
fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
|
|
19320
|
-
lineHeight: 1.1,
|
|
19321
|
-
letterSpacing: "-0.025em",
|
|
19322
|
-
color: "var(--brand-text)",
|
|
19323
|
-
marginBottom: "1rem"
|
|
19324
|
-
},
|
|
19325
|
-
...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
|
|
19326
|
-
children: title
|
|
19327
|
-
}
|
|
19328
|
-
),
|
|
19329
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
19330
|
-
"p",
|
|
19331
|
-
{
|
|
19332
|
-
style: {
|
|
19333
|
-
fontFamily: "var(--brand-font-body)",
|
|
19334
|
-
fontSize: "1rem",
|
|
19335
|
-
lineHeight: 1.7,
|
|
19336
|
-
fontWeight: 300,
|
|
19337
|
-
color: "var(--brand-text-muted)",
|
|
19338
|
-
maxWidth: "340px"
|
|
19339
|
-
},
|
|
19340
|
-
...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
|
|
19341
|
-
children: "This page doesn't have any content yet."
|
|
19342
|
-
}
|
|
19343
|
-
)
|
|
19344
|
-
] });
|
|
19345
|
-
}
|
|
19346
19292
|
// Annotate the CommonJS export names for ESM import in node:
|
|
19347
19293
|
0 && (module.exports = {
|
|
19348
19294
|
AI_DEFAULT_BRAND,
|
|
@@ -19360,7 +19306,6 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
|
|
|
19360
19306
|
DropdownMenuItem,
|
|
19361
19307
|
DropdownMenuSeparator,
|
|
19362
19308
|
DropdownMenuTrigger,
|
|
19363
|
-
EmptySection,
|
|
19364
19309
|
ItemActionToolbar,
|
|
19365
19310
|
ItemInteractionLayer,
|
|
19366
19311
|
LinkEditorPanel,
|