@base44/vite-plugin 1.0.12 → 1.0.14
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/injections/page-height-bridge.d.ts +6 -0
- package/dist/injections/page-height-bridge.d.ts.map +1 -1
- package/dist/injections/page-height-bridge.js +124 -44
- package/dist/injections/page-height-bridge.js.map +1 -1
- package/dist/injections/visual-edit-agent.d.ts.map +1 -1
- package/dist/injections/visual-edit-agent.js +10 -2
- package/dist/injections/visual-edit-agent.js.map +1 -1
- package/dist/statics/index.mjs +7 -7
- package/dist/statics/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/injections/page-height-bridge.ts +148 -46
- package/src/injections/visual-edit-agent.ts +19 -4
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
// fire on their own.
|
|
4
4
|
//
|
|
5
5
|
// parent → child { type: "freeze-vh-units", referenceVhBase?: number }
|
|
6
|
-
// Rewrites every `vh` in <style> + CSSOM to
|
|
7
|
-
// vh ↔ auto-resize feedback loop).
|
|
8
|
-
//
|
|
6
|
+
// Rewrites every `vh` in <style> + CSSOM to a CSS variable-backed
|
|
7
|
+
// expression (kills the vh ↔ auto-resize feedback loop). Repeated calls
|
|
8
|
+
// update the variable, so callers can rebase without recovering raw CSS.
|
|
9
|
+
// Idempotent; covers HMR-added styles too. Fire-and-forget.
|
|
9
10
|
//
|
|
10
11
|
// parent → child { type: "measure-page-height", settleMs?: number }
|
|
11
|
-
// After `settleMs` (default 2000) posts the page's
|
|
12
|
+
// After `settleMs` (default 2000) posts the page's measured content height
|
|
12
13
|
// as `{ type: "page-height-measured", height }` to the requester.
|
|
13
14
|
|
|
14
15
|
type IncomingMessage = {
|
|
@@ -24,10 +25,17 @@ type IndexableCssRule = CSSRule & {
|
|
|
24
25
|
|
|
25
26
|
type Debouncer = { trigger: () => void; cancel: () => void };
|
|
26
27
|
|
|
28
|
+
export type PageHeightBridgeController = {
|
|
29
|
+
freezeVhUnits: (override?: number) => void;
|
|
30
|
+
measurePageHeight: (origin: string, settleMs?: number) => void;
|
|
31
|
+
teardown: () => void;
|
|
32
|
+
};
|
|
33
|
+
|
|
27
34
|
const FALLBACK_VHBASE: number = 900;
|
|
28
35
|
const MIN_VHBASE: number = 400;
|
|
29
36
|
const DEFAULT_SETTLE_MS: number = 2000;
|
|
30
37
|
const NEUTRALIZE_DEBOUNCE_MS: number = 16;
|
|
38
|
+
const REFERENCE_VH_BASE_VAR: string = "--base44-reference-vh-base";
|
|
31
39
|
|
|
32
40
|
const noop: () => void = (): void => {};
|
|
33
41
|
|
|
@@ -45,34 +53,7 @@ export function setupPageHeightBridge(): () => void {
|
|
|
45
53
|
if (window.self === window.top) return noop;
|
|
46
54
|
started = true;
|
|
47
55
|
|
|
48
|
-
|
|
49
|
-
let vhForceRun: (() => void) | null = null;
|
|
50
|
-
let pendingResponse: number | undefined;
|
|
51
|
-
let pendingOrigin: string = "*";
|
|
52
|
-
|
|
53
|
-
const freezeVhUnits = (override: number | undefined): void => {
|
|
54
|
-
if (vhCleanups) {
|
|
55
|
-
vhForceRun?.();
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
const referenceVhBase: number = resolveReferenceVhBase(override);
|
|
59
|
-
vhCleanups = [];
|
|
60
|
-
vhForceRun = startVhNeutralizer(referenceVhBase, vhCleanups);
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// Target the requester's origin so the height isn't broadcast to anyone
|
|
64
|
-
// who happens to embed us. Falls back to "*" when origin is unavailable
|
|
65
|
-
// (jsdom default, sandboxed iframes with `null` origin).
|
|
66
|
-
const measurePageHeight = (origin: string, settleMs: number): void => {
|
|
67
|
-
pendingOrigin = origin;
|
|
68
|
-
if (pendingResponse !== undefined) window.clearTimeout(pendingResponse);
|
|
69
|
-
pendingResponse = window.setTimeout((): void => {
|
|
70
|
-
requestAnimationFrame((): void => {
|
|
71
|
-
const height: number = measureContentHeight();
|
|
72
|
-
window.parent.postMessage({ type: "page-height-measured", height }, pendingOrigin);
|
|
73
|
-
});
|
|
74
|
-
}, settleMs);
|
|
75
|
-
};
|
|
56
|
+
const controller: PageHeightBridgeController = createPageHeightBridgeController();
|
|
76
57
|
|
|
77
58
|
const onMessage = (event: MessageEvent): void => {
|
|
78
59
|
const data: IncomingMessage | null = (event.data ?? null) as IncomingMessage | null;
|
|
@@ -81,7 +62,7 @@ export function setupPageHeightBridge(): () => void {
|
|
|
81
62
|
case "freeze-vh-units": {
|
|
82
63
|
const override: number | undefined =
|
|
83
64
|
typeof data.referenceVhBase === "number" ? data.referenceVhBase : undefined;
|
|
84
|
-
freezeVhUnits(override);
|
|
65
|
+
controller.freezeVhUnits(override);
|
|
85
66
|
return;
|
|
86
67
|
}
|
|
87
68
|
case "measure-page-height": {
|
|
@@ -89,7 +70,7 @@ export function setupPageHeightBridge(): () => void {
|
|
|
89
70
|
typeof data.settleMs === "number" ? data.settleMs : DEFAULT_SETTLE_MS;
|
|
90
71
|
const origin: string =
|
|
91
72
|
event.origin && event.origin !== "null" ? event.origin : "*";
|
|
92
|
-
measurePageHeight(origin, settleMs);
|
|
73
|
+
controller.measurePageHeight(origin, settleMs);
|
|
93
74
|
return;
|
|
94
75
|
}
|
|
95
76
|
}
|
|
@@ -103,12 +84,50 @@ export function setupPageHeightBridge(): () => void {
|
|
|
103
84
|
torn = true;
|
|
104
85
|
started = false;
|
|
105
86
|
window.removeEventListener("message", onMessage);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
87
|
+
controller.teardown();
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function createPageHeightBridgeController(): PageHeightBridgeController {
|
|
92
|
+
let vhCleanups: Array<() => void> | null = null;
|
|
93
|
+
let vhForceRun: (() => void) | null = null;
|
|
94
|
+
let pendingResponse: number | undefined;
|
|
95
|
+
let pendingOrigin: string = "*";
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
freezeVhUnits: (override: number | undefined): void => {
|
|
99
|
+
const referenceVhBase: number = resolveReferenceVhBase(override);
|
|
100
|
+
setReferenceVhBase(referenceVhBase);
|
|
101
|
+
if (vhCleanups) {
|
|
102
|
+
vhForceRun?.();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
vhCleanups = [];
|
|
106
|
+
vhForceRun = startVhNeutralizer(vhCleanups);
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
// Target the requester's origin so the height isn't broadcast to anyone
|
|
110
|
+
// who happens to embed us. Falls back to "*" when origin is unavailable
|
|
111
|
+
// (jsdom default, sandboxed iframes with `null` origin).
|
|
112
|
+
measurePageHeight: (origin: string, settleMs: number = DEFAULT_SETTLE_MS): void => {
|
|
113
|
+
pendingOrigin = origin;
|
|
114
|
+
if (pendingResponse !== undefined) window.clearTimeout(pendingResponse);
|
|
115
|
+
pendingResponse = window.setTimeout((): void => {
|
|
116
|
+
requestAnimationFrame((): void => {
|
|
117
|
+
const height: number = measureContentHeight();
|
|
118
|
+
window.parent.postMessage({ type: "page-height-measured", height }, pendingOrigin);
|
|
119
|
+
});
|
|
120
|
+
}, settleMs);
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
teardown: (): void => {
|
|
124
|
+
if (vhCleanups) {
|
|
125
|
+
for (const c of vhCleanups) c();
|
|
126
|
+
vhCleanups = null;
|
|
127
|
+
vhForceRun = null;
|
|
128
|
+
}
|
|
129
|
+
if (pendingResponse !== undefined) window.clearTimeout(pendingResponse);
|
|
130
|
+
},
|
|
112
131
|
};
|
|
113
132
|
}
|
|
114
133
|
|
|
@@ -118,15 +137,16 @@ function resolveReferenceVhBase(override: number | undefined): number {
|
|
|
118
137
|
return detected >= MIN_VHBASE ? detected : FALLBACK_VHBASE;
|
|
119
138
|
}
|
|
120
139
|
|
|
140
|
+
function setReferenceVhBase(referenceVhBase: number): void {
|
|
141
|
+
document.documentElement.style.setProperty(REFERENCE_VH_BASE_VAR, `${referenceVhBase}px`);
|
|
142
|
+
}
|
|
143
|
+
|
|
121
144
|
// Caches keep work proportional to *new* CSS, not total CSS. <head> observer
|
|
122
145
|
// catches `<style>`/`<link>` adds; per-element observers catch HMR text edits.
|
|
123
146
|
// Returns a `forceRun` so the caller can re-trigger neutralization on later
|
|
124
147
|
// parent requests (idempotent — already-rewritten text is skipped via the
|
|
125
148
|
// processed sets).
|
|
126
|
-
function startVhNeutralizer(
|
|
127
|
-
referenceVhBase: number,
|
|
128
|
-
cleanups: Array<() => void>,
|
|
129
|
-
): () => void {
|
|
149
|
+
function startVhNeutralizer(cleanups: Array<() => void>): () => void {
|
|
130
150
|
const VH_RE: RegExp = /(\d+(?:\.\d+)?)vh\b/g;
|
|
131
151
|
const processedStyles: WeakSet<HTMLStyleElement> = new WeakSet();
|
|
132
152
|
const processedSheets: WeakMap<CSSStyleSheet, number> = new WeakMap();
|
|
@@ -136,7 +156,7 @@ function startVhNeutralizer(
|
|
|
136
156
|
const neutralize = (): void => {
|
|
137
157
|
const rewrite = (input: string): string =>
|
|
138
158
|
input.replace(VH_RE, (_match: string, n: string): string =>
|
|
139
|
-
|
|
159
|
+
`calc(var(${REFERENCE_VH_BASE_VAR}) * ${formatVhFactor(n)})`,
|
|
140
160
|
);
|
|
141
161
|
|
|
142
162
|
document.querySelectorAll<HTMLStyleElement>("style").forEach((el: HTMLStyleElement): void => {
|
|
@@ -214,6 +234,10 @@ function startVhNeutralizer(
|
|
|
214
234
|
return debouncedNeutralize;
|
|
215
235
|
}
|
|
216
236
|
|
|
237
|
+
function formatVhFactor(value: string): string {
|
|
238
|
+
return String(Number((parseFloat(value) / 100).toFixed(6)));
|
|
239
|
+
}
|
|
240
|
+
|
|
217
241
|
function rewriteVhInRules(rules: CSSRuleList, rewrite: (value: string) => string): void {
|
|
218
242
|
for (let i: number = 0; i < rules.length; i++) {
|
|
219
243
|
const rule: IndexableCssRule | undefined = rules[i] as IndexableCssRule | undefined;
|
|
@@ -241,10 +265,88 @@ function containsStylesheetNode(nodes: NodeList): boolean {
|
|
|
241
265
|
}
|
|
242
266
|
|
|
243
267
|
function measureContentHeight(): number {
|
|
244
|
-
|
|
268
|
+
const scrollHeight: number = Math.max(
|
|
245
269
|
document.documentElement.scrollHeight,
|
|
246
270
|
document.body?.scrollHeight ?? 0,
|
|
247
271
|
);
|
|
272
|
+
const viewportHeight: number = Math.max(
|
|
273
|
+
window.innerHeight || 0,
|
|
274
|
+
document.documentElement.clientHeight,
|
|
275
|
+
document.body?.clientHeight ?? 0,
|
|
276
|
+
);
|
|
277
|
+
const contentBottom: number = measureElementContentBottom(viewportHeight);
|
|
278
|
+
const referenceVhBase: number = readReferenceVhBase();
|
|
279
|
+
if (contentBottom > 0) return Math.ceil(Math.max(contentBottom, referenceVhBase));
|
|
280
|
+
return Math.ceil(Math.max(scrollHeight, referenceVhBase));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function readReferenceVhBase(): number {
|
|
284
|
+
const value: string = document.documentElement.style.getPropertyValue(REFERENCE_VH_BASE_VAR);
|
|
285
|
+
const parsed: number = parseFloat(value);
|
|
286
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function measureElementContentBottom(viewportHeight: number): number {
|
|
290
|
+
if (!document.body) return 0;
|
|
291
|
+
const elements: Element[] = [document.body, ...Array.from(document.body.querySelectorAll("*"))];
|
|
292
|
+
const contentBottoms: WeakMap<Element, number> = new WeakMap();
|
|
293
|
+
const viewportBottom: number = window.scrollY + viewportHeight;
|
|
294
|
+
|
|
295
|
+
for (let i: number = elements.length - 1; i >= 0; i--) {
|
|
296
|
+
const el: Element | undefined = elements[i];
|
|
297
|
+
if (!el) continue;
|
|
298
|
+
const childContentBottom: number = readChildrenContentBottom(el, contentBottoms);
|
|
299
|
+
const rawBottom: number = readElementBottom(el);
|
|
300
|
+
const selfBottom: number = isViewportStretchedContainer(rawBottom, childContentBottom, viewportBottom)
|
|
301
|
+
? 0
|
|
302
|
+
: rawBottom;
|
|
303
|
+
contentBottoms.set(el, Math.max(childContentBottom, selfBottom));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return contentBottoms.get(document.body) ?? 0;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function readChildrenContentBottom(
|
|
310
|
+
el: Element,
|
|
311
|
+
contentBottoms: WeakMap<Element, number>,
|
|
312
|
+
): number {
|
|
313
|
+
let childContentBottom: number = 0;
|
|
314
|
+
for (let i: number = 0; i < el.children.length; i++) {
|
|
315
|
+
const child: Element | undefined = el.children[i];
|
|
316
|
+
if (!child) continue;
|
|
317
|
+
childContentBottom = Math.max(childContentBottom, contentBottoms.get(child) ?? 0);
|
|
318
|
+
}
|
|
319
|
+
return childContentBottom;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function readElementBottom(el: Element): number {
|
|
323
|
+
const computedStyle: CSSStyleDeclaration = window.getComputedStyle(el);
|
|
324
|
+
if (isOutOfFlowDecoration(computedStyle)) return 0;
|
|
325
|
+
const rect: DOMRect = el.getBoundingClientRect();
|
|
326
|
+
if (rect.width === 0 && rect.height === 0) return 0;
|
|
327
|
+
return rect.bottom + window.scrollY + readMarginBottom(computedStyle);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function readMarginBottom(computedStyle: CSSStyleDeclaration): number {
|
|
331
|
+
const marginBottom: number = parseFloat(computedStyle.marginBottom);
|
|
332
|
+
return Number.isFinite(marginBottom) ? marginBottom : 0;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function isOutOfFlowDecoration(computedStyle: CSSStyleDeclaration): boolean {
|
|
336
|
+
if (computedStyle.position === "fixed") return true;
|
|
337
|
+
return computedStyle.position === "absolute" && computedStyle.pointerEvents === "none";
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function isViewportStretchedContainer(
|
|
341
|
+
elementBottom: number,
|
|
342
|
+
childBottom: number,
|
|
343
|
+
viewportBottom: number,
|
|
344
|
+
): boolean {
|
|
345
|
+
return (
|
|
346
|
+
childBottom > 0 &&
|
|
347
|
+
Math.abs(elementBottom - viewportBottom) <= 1 &&
|
|
348
|
+
elementBottom - childBottom > 8
|
|
349
|
+
);
|
|
248
350
|
}
|
|
249
351
|
|
|
250
352
|
function createDebouncer(fn: () => void, delayMs: number): Debouncer {
|
|
@@ -3,13 +3,13 @@ import { createLayerController } from "./layer-dropdown/controller.js";
|
|
|
3
3
|
import { LAYER_DROPDOWN_ATTR } from "./layer-dropdown/consts.js";
|
|
4
4
|
import { createInlineEditController } from "../capabilities/inline-edit/index.js";
|
|
5
5
|
import { THEME_FONT_PREVIEW_ID } from "../consts.js";
|
|
6
|
-
import {
|
|
6
|
+
import { createPageHeightBridgeController } from "./page-height-bridge.js";
|
|
7
7
|
|
|
8
8
|
const REPOSITION_DELAY_MS = 50;
|
|
9
9
|
|
|
10
10
|
export function setupVisualEditAgent() {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
const pageHeightBridge = createPageHeightBridgeController();
|
|
12
|
+
|
|
13
13
|
// State variables (replacing React useState/useRef)
|
|
14
14
|
let isVisualEditMode = false;
|
|
15
15
|
let isPopoverDragging = false;
|
|
@@ -641,6 +641,21 @@ export function setupVisualEditAgent() {
|
|
|
641
641
|
}
|
|
642
642
|
break;
|
|
643
643
|
|
|
644
|
+
case "freeze-vh-units":
|
|
645
|
+
pageHeightBridge.freezeVhUnits(
|
|
646
|
+
typeof message.referenceVhBase === "number"
|
|
647
|
+
? message.referenceVhBase
|
|
648
|
+
: undefined
|
|
649
|
+
);
|
|
650
|
+
break;
|
|
651
|
+
|
|
652
|
+
case "measure-page-height":
|
|
653
|
+
pageHeightBridge.measurePageHeight(
|
|
654
|
+
event.origin && event.origin !== "null" ? event.origin : "*",
|
|
655
|
+
typeof message.settleMs === "number" ? message.settleMs : undefined
|
|
656
|
+
);
|
|
657
|
+
break;
|
|
658
|
+
|
|
644
659
|
default:
|
|
645
660
|
break;
|
|
646
661
|
}
|
|
@@ -726,4 +741,4 @@ export function setupVisualEditAgent() {
|
|
|
726
741
|
|
|
727
742
|
// Send ready message to parent
|
|
728
743
|
window.parent.postMessage({ type: "visual-edit-agent-ready" }, "*");
|
|
729
|
-
}
|
|
744
|
+
}
|