@base44-preview/vite-plugin 1.0.24-pr.91.a0a1f06 → 1.0.24-pr.93.2320a2c
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/html-injections-plugin.d.ts.map +1 -1
- package/dist/html-injections-plugin.js +6 -0
- package/dist/html-injections-plugin.js.map +1 -1
- package/dist/injections/keyboard-viewport-bridge.d.ts +2 -0
- package/dist/injections/keyboard-viewport-bridge.d.ts.map +1 -0
- package/dist/injections/keyboard-viewport-bridge.js +75 -0
- package/dist/injections/keyboard-viewport-bridge.js.map +1 -0
- package/dist/visual-edit-plugin.js +21 -13
- package/dist/visual-edit-plugin.js.map +1 -1
- package/package.json +1 -1
- package/src/html-injections-plugin.ts +6 -0
- package/src/injections/keyboard-viewport-bridge.ts +76 -0
- package/src/visual-edit-plugin.md +11 -10
- package/src/visual-edit-plugin.ts +24 -13
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html-injections-plugin.d.ts","sourceRoot":"","sources":["../src/html-injections-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAqB,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"html-injections-plugin.d.ts","sourceRoot":"","sources":["../src/html-injections-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAqB,MAAM,MAAM,CAAC;AA8EtD,wBAAgB,oBAAoB,CAAC,EACnC,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,EAAE;IACD,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,GAoFM,MAAM,CACZ"}
|
|
@@ -52,6 +52,11 @@ const INJECTIONS = {
|
|
|
52
52
|
injectTo: "head",
|
|
53
53
|
mode: "dev",
|
|
54
54
|
},
|
|
55
|
+
keyboardViewport: {
|
|
56
|
+
src: "/node_modules/@base44/vite-plugin/dist/injections/keyboard-viewport-bridge.js",
|
|
57
|
+
injectTo: "head",
|
|
58
|
+
mode: "dev",
|
|
59
|
+
},
|
|
55
60
|
analyticsTracker: {
|
|
56
61
|
injectTo: "head",
|
|
57
62
|
mode: "production",
|
|
@@ -64,6 +69,7 @@ export function htmlInjectionsPlugin({ hmrNotifier, navigationNotifier, visualEd
|
|
|
64
69
|
sandboxMount: true,
|
|
65
70
|
hmrNotifier,
|
|
66
71
|
navigationNotifier,
|
|
72
|
+
keyboardViewport: true,
|
|
67
73
|
analyticsTracker,
|
|
68
74
|
};
|
|
69
75
|
let resolvedEnv = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html-injections-plugin.js","sourceRoot":"","sources":["../src/html-injections-plugin.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"html-injections-plugin.js","sourceRoot":"","sources":["../src/html-injections-plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard-viewport-bridge.d.ts","sourceRoot":"","sources":["../../src/injections/keyboard-viewport-bridge.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
// keyboard-viewport-bridge — applies the parent-measured, keyboard-adjusted
|
|
3
|
+
// visible height inside the preview iframe.
|
|
4
|
+
//
|
|
5
|
+
// The on-screen keyboard shrinks the builder's (top-level) visual viewport but
|
|
6
|
+
// NOT the preview iframe's, so the app rendered inside the iframe can't tell it
|
|
7
|
+
// has less room and leaves a blank gap above the keyboard (bottom-anchored
|
|
8
|
+
// inputs end up behind it). The published app doesn't hit this — it's the
|
|
9
|
+
// top-level document, so the browser reflows it for the keyboard.
|
|
10
|
+
//
|
|
11
|
+
// The builder is the only side that sees the keyboard, so it measures the
|
|
12
|
+
// visible height and posts { type: "keyboard-viewport", height }. Here we cap
|
|
13
|
+
// the app's full-height roots to that height while the keyboard is open, so a
|
|
14
|
+
// flex column reflows its content (and its bottom input) above the keyboard.
|
|
15
|
+
//
|
|
16
|
+
// Inert until the parent posts; only runs inside the preview iframe.
|
|
17
|
+
if (window.self !== window.top) {
|
|
18
|
+
const MESSAGE_TYPE = "keyboard-viewport";
|
|
19
|
+
const HEIGHT_VAR = "--base44-keyboard-viewport-height";
|
|
20
|
+
const STYLE_ID = "base44-keyboard-viewport-style";
|
|
21
|
+
// Treat the keyboard as open only when the reported height is meaningfully
|
|
22
|
+
// below the layout height — filters out no-op / rounding resizes.
|
|
23
|
+
const KEYBOARD_OPEN_MIN_DELTA_PX = 100;
|
|
24
|
+
const ensureStyle = () => {
|
|
25
|
+
if (document.getElementById(STYLE_ID))
|
|
26
|
+
return;
|
|
27
|
+
const style = document.createElement("style");
|
|
28
|
+
style.id = STYLE_ID;
|
|
29
|
+
// Active only while HEIGHT_VAR is set on <html> (keyboard open). Cap the
|
|
30
|
+
// document, the mount node, and the app's full-height wrappers to the
|
|
31
|
+
// visible height. `code_generator` guarantees the app root uses
|
|
32
|
+
// `min-h-screen`/`h-screen`, so constraining those (plus 100vh/100dvh
|
|
33
|
+
// inline styles) makes the layout fit above the keyboard.
|
|
34
|
+
style.textContent = [
|
|
35
|
+
`html[style*="${HEIGHT_VAR}"], html[style*="${HEIGHT_VAR}"] body {`,
|
|
36
|
+
` height: var(${HEIGHT_VAR}) !important;`,
|
|
37
|
+
` min-height: var(${HEIGHT_VAR}) !important;`,
|
|
38
|
+
`}`,
|
|
39
|
+
`html[style*="${HEIGHT_VAR}"] #root {`,
|
|
40
|
+
` height: var(${HEIGHT_VAR}) !important;`,
|
|
41
|
+
` min-height: var(${HEIGHT_VAR}) !important;`,
|
|
42
|
+
` overflow: hidden;`,
|
|
43
|
+
`}`,
|
|
44
|
+
`html[style*="${HEIGHT_VAR}"] :is(.h-screen, .min-h-screen),`,
|
|
45
|
+
`html[style*="${HEIGHT_VAR}"] [style*="100vh"],`,
|
|
46
|
+
`html[style*="${HEIGHT_VAR}"] [style*="100dvh"] {`,
|
|
47
|
+
` height: var(${HEIGHT_VAR}) !important;`,
|
|
48
|
+
` min-height: var(${HEIGHT_VAR}) !important;`,
|
|
49
|
+
`}`,
|
|
50
|
+
].join("\n");
|
|
51
|
+
document.head.appendChild(style);
|
|
52
|
+
};
|
|
53
|
+
const apply = (height) => {
|
|
54
|
+
const root = document.documentElement;
|
|
55
|
+
const keyboardOpen = Number.isFinite(height) &&
|
|
56
|
+
height > 0 &&
|
|
57
|
+
window.innerHeight - height > KEYBOARD_OPEN_MIN_DELTA_PX;
|
|
58
|
+
if (!keyboardOpen) {
|
|
59
|
+
// Keyboard closed (or negligible delta) — release the cap so the app
|
|
60
|
+
// returns to its natural full-height layout.
|
|
61
|
+
root.style.removeProperty(HEIGHT_VAR);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
ensureStyle();
|
|
65
|
+
root.style.setProperty(HEIGHT_VAR, `${Math.round(height)}px`);
|
|
66
|
+
};
|
|
67
|
+
window.addEventListener("message", (event) => {
|
|
68
|
+
const data = event.data;
|
|
69
|
+
if (!data || data.type !== MESSAGE_TYPE || typeof data.height !== "number")
|
|
70
|
+
return;
|
|
71
|
+
apply(data.height);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=keyboard-viewport-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard-viewport-bridge.js","sourceRoot":"","sources":["../../src/injections/keyboard-viewport-bridge.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,4EAA4E;AAC5E,4CAA4C;AAC5C,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,2EAA2E;AAC3E,0EAA0E;AAC1E,kEAAkE;AAClE,EAAE;AACF,0EAA0E;AAC1E,8EAA8E;AAC9E,8EAA8E;AAC9E,6EAA6E;AAC7E,EAAE;AACF,qEAAqE;AAErE,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAG,mBAAmB,CAAC;IACzC,MAAM,UAAU,GAAG,mCAAmC,CAAC;IACvD,MAAM,QAAQ,GAAG,gCAAgC,CAAC;IAClD,2EAA2E;IAC3E,kEAAkE;IAClE,MAAM,0BAA0B,GAAG,GAAG,CAAC;IAEvC,MAAM,WAAW,GAAG,GAAS,EAAE;QAC7B,IAAI,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC;YAAE,OAAO;QAC9C,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC9C,KAAK,CAAC,EAAE,GAAG,QAAQ,CAAC;QACpB,yEAAyE;QACzE,sEAAsE;QACtE,gEAAgE;QAChE,sEAAsE;QACtE,0DAA0D;QAC1D,KAAK,CAAC,WAAW,GAAG;YAClB,gBAAgB,UAAU,oBAAoB,UAAU,WAAW;YACnE,iBAAiB,UAAU,eAAe;YAC1C,qBAAqB,UAAU,eAAe;YAC9C,GAAG;YACH,gBAAgB,UAAU,YAAY;YACtC,iBAAiB,UAAU,eAAe;YAC1C,qBAAqB,UAAU,eAAe;YAC9C,qBAAqB;YACrB,GAAG;YACH,gBAAgB,UAAU,mCAAmC;YAC7D,gBAAgB,UAAU,sBAAsB;YAChD,gBAAgB,UAAU,wBAAwB;YAClD,iBAAiB,UAAU,eAAe;YAC1C,qBAAqB,UAAU,eAAe;YAC9C,GAAG;SACJ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,MAAc,EAAQ,EAAE;QACrC,MAAM,IAAI,GAAG,QAAQ,CAAC,eAAe,CAAC;QACtC,MAAM,YAAY,GAChB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YACvB,MAAM,GAAG,CAAC;YACV,MAAM,CAAC,WAAW,GAAG,MAAM,GAAG,0BAA0B,CAAC;QAC3D,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,qEAAqE;YACrE,6CAA6C;YAC7C,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;YACtC,OAAO;QACT,CAAC;QACD,WAAW,EAAE,CAAC;QACd,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAChE,CAAC,CAAC;IAEF,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAmB,EAAE,EAAE;QACzD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAiD,CAAC;QACrE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO;QACnF,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -73,20 +73,28 @@ export function visualEditPlugin() {
|
|
|
73
73
|
},
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
|
-
// Emit the canonical real path (src/pages/Home.jsx) the backend and sandbox use, NOT a
|
|
77
|
-
// legacy alias (pages/Home). The path is anchored on the last "src" segment with the
|
|
78
|
-
// extension retained, so consumers get the file's true sandbox path directly.
|
|
79
76
|
function extractFilename(id) {
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return parts.slice(srcIndex).join("/"); // src/pages/Home.jsx, src/Layout.jsx, src/components/ui/Card.tsx
|
|
77
|
+
const pathParts = id.split("/");
|
|
78
|
+
const segmentIndex = findSegmentIndex(pathParts, ["pages", "components"]);
|
|
79
|
+
if (segmentIndex >= 0 && segmentIndex < pathParts.length - 1) {
|
|
80
|
+
const relevantParts = pathParts.slice(segmentIndex);
|
|
81
|
+
const last = relevantParts[relevantParts.length - 1];
|
|
82
|
+
relevantParts[relevantParts.length - 1] = stripExtension(last ?? "");
|
|
83
|
+
return relevantParts.join("/");
|
|
88
84
|
}
|
|
89
|
-
|
|
90
|
-
return
|
|
85
|
+
const lastPart = pathParts[pathParts.length - 1] ?? "";
|
|
86
|
+
return stripExtension(lastPart);
|
|
87
|
+
}
|
|
88
|
+
function findSegmentIndex(parts, segments) {
|
|
89
|
+
for (const segment of segments) {
|
|
90
|
+
const idx = parts.findIndex((part) => part === segment);
|
|
91
|
+
if (idx >= 0)
|
|
92
|
+
return idx;
|
|
93
|
+
}
|
|
94
|
+
return -1;
|
|
95
|
+
}
|
|
96
|
+
function stripExtension(filename) {
|
|
97
|
+
const dotIndex = filename.indexOf(".");
|
|
98
|
+
return dotIndex >= 0 ? filename.substring(0, dotIndex) : filename;
|
|
91
99
|
}
|
|
92
100
|
//# sourceMappingURL=visual-edit-plugin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"visual-edit-plugin.js","sourceRoot":"","sources":["../src/visual-edit-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAElC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,IAAI,EAAE,uBAAuB;QAC7B,KAAK,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,aAAa;QAChD,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,KAAK;QACZ,kBAAkB,CAAC,IAAS;YAC1B,MAAM,cAAc,GAAG,+GAA+G,CAAC;YACvI,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,GAAG,SAAS,CAAC,CAAC;QAC7D,CAAC;QACD,SAAS,CAAC,IAAS,EAAE,EAAO;YAC1B,IAAI,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;gBACpE,OAAO,IAAI,CAAC;YACd,CAAC;YAED,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAChC,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,QAAQ,GAAG,eAAe,CAAC,EAAE,CAAC,CAAC;YAErC,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE;oBACtB,UAAU,EAAE,QAAQ;oBACpB,OAAO,EAAE;wBACP,KAAK;wBACL,YAAY;wBACZ,mBAAmB;wBACnB,iBAAiB;wBACjB,kBAAkB;wBAClB,cAAc;wBACd,mBAAmB;wBACnB,qBAAqB;wBACrB,eAAe;wBACf,2BAA2B;wBAC3B,kBAAkB;wBAClB,iBAAiB;wBACjB,QAAQ;wBACR,sBAAsB;wBACtB,kBAAkB;qBACnB;iBACF,CAAC,CAAC;gBAEH,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACjB,MAAM,SAAS,GAAG,IAAI,YAAY,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;gBAEhD,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE;oBACpB,UAAU,CAAC,IAAI;wBACb,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;wBAC7B,IAAI,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC;4BAAE,OAAO;wBACxC,SAAS,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC;oBAC1D,CAAC;iBACF,CAAC,CAAC;gBAEH,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE;oBACnC,OAAO,EAAE,KAAK;oBACd,OAAO,EAAE,KAAK;oBACd,WAAW,EAAE,IAAI;iBAClB,CAAC,CAAC;gBAEH,OAAO;oBACL,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,GAAG,EAAE,IAAI;iBACV,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAC;gBAC9D,OAAO;oBACL,IAAI,EAAE,IAAI;oBACV,GAAG,EAAE,IAAI;iBACV,CAAC;YACJ,CAAC;QACH,CAAC;KACQ,CAAC;AACd,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"visual-edit-plugin.js","sourceRoot":"","sources":["../src/visual-edit-plugin.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -64,6 +64,11 @@ const INJECTIONS: Record<string, Injection> = {
|
|
|
64
64
|
injectTo: "head",
|
|
65
65
|
mode: "dev",
|
|
66
66
|
},
|
|
67
|
+
keyboardViewport: {
|
|
68
|
+
src: "/node_modules/@base44/vite-plugin/dist/injections/keyboard-viewport-bridge.js",
|
|
69
|
+
injectTo: "head",
|
|
70
|
+
mode: "dev",
|
|
71
|
+
},
|
|
67
72
|
analyticsTracker: {
|
|
68
73
|
injectTo: "head",
|
|
69
74
|
mode: "production",
|
|
@@ -87,6 +92,7 @@ export function htmlInjectionsPlugin({
|
|
|
87
92
|
sandboxMount: true,
|
|
88
93
|
hmrNotifier,
|
|
89
94
|
navigationNotifier,
|
|
95
|
+
keyboardViewport: true,
|
|
90
96
|
analyticsTracker,
|
|
91
97
|
};
|
|
92
98
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
// keyboard-viewport-bridge — applies the parent-measured, keyboard-adjusted
|
|
3
|
+
// visible height inside the preview iframe.
|
|
4
|
+
//
|
|
5
|
+
// The on-screen keyboard shrinks the builder's (top-level) visual viewport but
|
|
6
|
+
// NOT the preview iframe's, so the app rendered inside the iframe can't tell it
|
|
7
|
+
// has less room and leaves a blank gap above the keyboard (bottom-anchored
|
|
8
|
+
// inputs end up behind it). The published app doesn't hit this — it's the
|
|
9
|
+
// top-level document, so the browser reflows it for the keyboard.
|
|
10
|
+
//
|
|
11
|
+
// The builder is the only side that sees the keyboard, so it measures the
|
|
12
|
+
// visible height and posts { type: "keyboard-viewport", height }. Here we cap
|
|
13
|
+
// the app's full-height roots to that height while the keyboard is open, so a
|
|
14
|
+
// flex column reflows its content (and its bottom input) above the keyboard.
|
|
15
|
+
//
|
|
16
|
+
// Inert until the parent posts; only runs inside the preview iframe.
|
|
17
|
+
|
|
18
|
+
if (window.self !== window.top) {
|
|
19
|
+
const MESSAGE_TYPE = "keyboard-viewport";
|
|
20
|
+
const HEIGHT_VAR = "--base44-keyboard-viewport-height";
|
|
21
|
+
const STYLE_ID = "base44-keyboard-viewport-style";
|
|
22
|
+
// Treat the keyboard as open only when the reported height is meaningfully
|
|
23
|
+
// below the layout height — filters out no-op / rounding resizes.
|
|
24
|
+
const KEYBOARD_OPEN_MIN_DELTA_PX = 100;
|
|
25
|
+
|
|
26
|
+
const ensureStyle = (): void => {
|
|
27
|
+
if (document.getElementById(STYLE_ID)) return;
|
|
28
|
+
const style = document.createElement("style");
|
|
29
|
+
style.id = STYLE_ID;
|
|
30
|
+
// Active only while HEIGHT_VAR is set on <html> (keyboard open). Cap the
|
|
31
|
+
// document, the mount node, and the app's full-height wrappers to the
|
|
32
|
+
// visible height. `code_generator` guarantees the app root uses
|
|
33
|
+
// `min-h-screen`/`h-screen`, so constraining those (plus 100vh/100dvh
|
|
34
|
+
// inline styles) makes the layout fit above the keyboard.
|
|
35
|
+
style.textContent = [
|
|
36
|
+
`html[style*="${HEIGHT_VAR}"], html[style*="${HEIGHT_VAR}"] body {`,
|
|
37
|
+
` height: var(${HEIGHT_VAR}) !important;`,
|
|
38
|
+
` min-height: var(${HEIGHT_VAR}) !important;`,
|
|
39
|
+
`}`,
|
|
40
|
+
`html[style*="${HEIGHT_VAR}"] #root {`,
|
|
41
|
+
` height: var(${HEIGHT_VAR}) !important;`,
|
|
42
|
+
` min-height: var(${HEIGHT_VAR}) !important;`,
|
|
43
|
+
` overflow: hidden;`,
|
|
44
|
+
`}`,
|
|
45
|
+
`html[style*="${HEIGHT_VAR}"] :is(.h-screen, .min-h-screen),`,
|
|
46
|
+
`html[style*="${HEIGHT_VAR}"] [style*="100vh"],`,
|
|
47
|
+
`html[style*="${HEIGHT_VAR}"] [style*="100dvh"] {`,
|
|
48
|
+
` height: var(${HEIGHT_VAR}) !important;`,
|
|
49
|
+
` min-height: var(${HEIGHT_VAR}) !important;`,
|
|
50
|
+
`}`,
|
|
51
|
+
].join("\n");
|
|
52
|
+
document.head.appendChild(style);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const apply = (height: number): void => {
|
|
56
|
+
const root = document.documentElement;
|
|
57
|
+
const keyboardOpen =
|
|
58
|
+
Number.isFinite(height) &&
|
|
59
|
+
height > 0 &&
|
|
60
|
+
window.innerHeight - height > KEYBOARD_OPEN_MIN_DELTA_PX;
|
|
61
|
+
if (!keyboardOpen) {
|
|
62
|
+
// Keyboard closed (or negligible delta) — release the cap so the app
|
|
63
|
+
// returns to its natural full-height layout.
|
|
64
|
+
root.style.removeProperty(HEIGHT_VAR);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
ensureStyle();
|
|
68
|
+
root.style.setProperty(HEIGHT_VAR, `${Math.round(height)}px`);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
window.addEventListener("message", (event: MessageEvent) => {
|
|
72
|
+
const data = event.data as { type?: string; height?: number } | null;
|
|
73
|
+
if (!data || data.type !== MESSAGE_TYPE || typeof data.height !== "number") return;
|
|
74
|
+
apply(data.height);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -14,7 +14,7 @@ It adds two HTML `data-*` attributes to every JSX element:
|
|
|
14
14
|
|
|
15
15
|
// AFTER (served to browser):
|
|
16
16
|
<Button
|
|
17
|
-
data-source-location="
|
|
17
|
+
data-source-location="components/Button:42:8"
|
|
18
18
|
data-dynamic-content="false"
|
|
19
19
|
className="px-4"
|
|
20
20
|
>Click me</Button>
|
|
@@ -65,18 +65,19 @@ It adds two HTML `data-*` attributes to every JSX element:
|
|
|
65
65
|
|
|
66
66
|
### Step 1: Filename Extraction (lines 123-173)
|
|
67
67
|
|
|
68
|
-
Builds
|
|
68
|
+
Builds a human-readable source path from the full file path:
|
|
69
69
|
|
|
70
70
|
```
|
|
71
|
-
/Users/dev/project/src/pages/About/index.tsx → "
|
|
72
|
-
/Users/dev/project/src/components/ui/Button.tsx → "
|
|
73
|
-
/Users/dev/project/src/Layout.tsx → "
|
|
71
|
+
/Users/dev/project/src/pages/About/index.tsx → "pages/About/index"
|
|
72
|
+
/Users/dev/project/src/components/ui/Button.tsx → "components/ui/Button"
|
|
73
|
+
/Users/dev/project/src/Layout.tsx → "Layout"
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
**Rules:**
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
77
|
+
- Files under `/pages/` → preserves `pages/...` prefix with nested structure
|
|
78
|
+
- Files under `/components/` → preserves `components/...` prefix with nested structure
|
|
79
|
+
- All other files → just the filename (no directory prefix)
|
|
80
|
+
- File extensions are always stripped
|
|
80
81
|
|
|
81
82
|
### Step 2: AST Parsing (lines 177-196)
|
|
82
83
|
|
|
@@ -259,7 +260,7 @@ The parent window (editor UI) and the sandbox iframe can't share a DOM. The `dat
|
|
|
259
260
|
```
|
|
260
261
|
Parent Window (Editor UI) iframe Sandbox (React App)
|
|
261
262
|
───────────────────────── ──────────────────────────
|
|
262
|
-
"Select element
|
|
263
|
+
"Select element components/Button:42:8" → querySelector('[data-source-location="components/Button:42:8"]')
|
|
263
264
|
← "Element found: classes='px-4', isDynamic=false"
|
|
264
265
|
"Update classes to 'px-6 py-3'" → element.setAttribute("class", "px-6 py-3")
|
|
265
266
|
```
|
|
@@ -304,7 +305,7 @@ The `transformIndexHtml` hook injects the Tailwind CSS CDN, enabling visual edit
|
|
|
304
305
|
│ │ React App (DOM) │ │
|
|
305
306
|
│ │ │ │
|
|
306
307
|
│ │ <div data-source-location= │ │
|
|
307
|
-
│ │ "
|
|
308
|
+
│ │ "pages/Home:10:4" │ │
|
|
308
309
|
│ │ data-dynamic-content="true"> │ │
|
|
309
310
|
│ │ {greeting} │ │
|
|
310
311
|
│ │ </div> │ │
|
|
@@ -81,19 +81,30 @@ export function visualEditPlugin() {
|
|
|
81
81
|
} as Plugin;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
// Emit the canonical real path (src/pages/Home.jsx) the backend and sandbox use, NOT a
|
|
85
|
-
// legacy alias (pages/Home). The path is anchored on the last "src" segment with the
|
|
86
|
-
// extension retained, so consumers get the file's true sandbox path directly.
|
|
87
84
|
function extractFilename(id: string): string {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return
|
|
85
|
+
const pathParts = id.split("/");
|
|
86
|
+
|
|
87
|
+
const segmentIndex = findSegmentIndex(pathParts, ["pages", "components"]);
|
|
88
|
+
if (segmentIndex >= 0 && segmentIndex < pathParts.length - 1) {
|
|
89
|
+
const relevantParts = pathParts.slice(segmentIndex);
|
|
90
|
+
const last = relevantParts[relevantParts.length - 1];
|
|
91
|
+
relevantParts[relevantParts.length - 1] = stripExtension(last ?? "");
|
|
92
|
+
return relevantParts.join("/");
|
|
96
93
|
}
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
|
|
95
|
+
const lastPart = pathParts[pathParts.length - 1] ?? "";
|
|
96
|
+
return stripExtension(lastPart);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function findSegmentIndex(parts: string[], segments: string[]): number {
|
|
100
|
+
for (const segment of segments) {
|
|
101
|
+
const idx = parts.findIndex((part) => part === segment);
|
|
102
|
+
if (idx >= 0) return idx;
|
|
103
|
+
}
|
|
104
|
+
return -1;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function stripExtension(filename: string): string {
|
|
108
|
+
const dotIndex = filename.indexOf(".");
|
|
109
|
+
return dotIndex >= 0 ? filename.substring(0, dotIndex) : filename;
|
|
99
110
|
}
|