@replohq/sandbox-runtime 1.18.0 → 1.19.0
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/_vendor/replo-utils/lib/crypto.d.ts +1 -0
- package/_vendor/replo-utils/lib/crypto.mjs +1 -1
- package/_vendor/replo-utils/lib/fonts.mjs +2 -2
- package/_vendor/replo-utils/lib/math.mjs +3 -3
- package/_vendor/replo-utils/lib/misc.mjs +4 -4
- package/_vendor/replo-utils/lib/pageCapture.mjs +1 -1
- package/_vendor/schemas/errors.d.ts +0 -1
- package/_vendor/schemas/generated/pageHeatmap.d.ts +57 -0
- package/_vendor/schemas/generated/pageHeatmap.mjs +1 -0
- package/_vendor/schemas/iframeRpcChannel.mjs +6 -6
- package/_vendor/schemas/pageHeatmap.d.ts +132 -0
- package/_vendor/schemas/pageHeatmap.mjs +72 -0
- package/_vendor/schemas/previewHealthIframeRpc.mjs +6 -6
- package/_vendor/schemas/selectMode.d.ts +1 -1
- package/_vendor/schemas/selectMode.mjs +5 -5
- package/_vendor/schemas/selectModeIframeRpc.d.ts +2 -2
- package/_vendor/schemas/selectModeIframeRpc.mjs +5 -5
- package/buildMetadata.js +3 -3
- package/capturePage.js +5 -1
- package/{chunk-KRA4LUIR.mjs → chunk-36IPO47A.mjs} +2 -2
- package/{chunk-TP4ZXK4R.mjs → chunk-QTGDSYSU.mjs} +1 -1
- package/{chunk-FLYEIRFK.mjs → chunk-SKVPFN7M.mjs} +1 -1
- package/{chunk-PH3S6LJI.mjs → chunk-THOBCJJA.mjs} +2 -2
- package/{chunk-LIPH4O4F.mjs → chunk-ZTT7SNYN.mjs} +5 -2
- package/heatmaps/HeatLayerMount.d.ts +1 -0
- package/heatmaps/HeatLayerMount.js +12 -0
- package/heatmaps/heatBadges.d.ts +25 -0
- package/heatmaps/heatBadges.js +98 -0
- package/heatmaps/heatCore.d.ts +34 -0
- package/heatmaps/heatCore.js +465 -0
- package/heatmaps/heatLayer.d.ts +1 -0
- package/heatmaps/heatLayer.js +85 -0
- package/heatmaps/heatRender.d.ts +36 -0
- package/heatmaps/heatRender.js +198 -0
- package/package.json +4 -4
- package/sandbox-runtime.js +2 -0
- package/select-mode/hooks/useSelectionHover.js +5 -0
- package/select-mode/section-canvas/SectionCanvas.js +80 -124
- package/select-mode/section-canvas/sectionCanvasStore.d.ts +0 -2
- package/select-mode/section-canvas/sectionCanvasStore.js +0 -15
- package/select-mode/section-canvas/sectionGhost.d.ts +0 -5
- package/select-mode/section-canvas/sectionGhost.js +0 -23
- /package/{chunk-TESFJQID.mjs → chunk-4ITJNSXZ.mjs} +0 -0
- /package/{chunk-G3S6ZNPF.mjs → chunk-XYS6G2ZC.mjs} +0 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
const STOPS = [
|
|
2
|
+
{ offset: 0, color: "rgba(40, 60, 255, 0)" },
|
|
3
|
+
{ offset: 0.18, color: "rgba(40, 90, 255, 0.55)" },
|
|
4
|
+
{ offset: 0.38, color: "rgba(0, 200, 220, 0.7)" },
|
|
5
|
+
{ offset: 0.55, color: "rgba(60, 220, 90, 0.78)" },
|
|
6
|
+
{ offset: 0.72, color: "rgba(255, 225, 40, 0.86)" },
|
|
7
|
+
{ offset: 0.86, color: "rgba(255, 140, 20, 0.92)" },
|
|
8
|
+
{ offset: 1, color: "rgba(240, 30, 40, 0.95)" }
|
|
9
|
+
];
|
|
10
|
+
const SCRIM_COLOR = "rgba(20, 18, 40, 0.24)";
|
|
11
|
+
const MARK_LINE_COLOR = "rgba(255, 255, 255, 0.95)";
|
|
12
|
+
const MARK_LABEL_BACKGROUND = "rgba(23, 23, 23, 0.85)";
|
|
13
|
+
const REACH_MARKS = [0.75, 0.5, 0.25];
|
|
14
|
+
const LABEL_FONT_PX = 13;
|
|
15
|
+
let paletteCache = null;
|
|
16
|
+
function getPalette() {
|
|
17
|
+
if (paletteCache) {
|
|
18
|
+
return paletteCache;
|
|
19
|
+
}
|
|
20
|
+
const canvas = document.createElement("canvas");
|
|
21
|
+
canvas.width = 256;
|
|
22
|
+
canvas.height = 1;
|
|
23
|
+
const context = canvas.getContext("2d", { willReadFrequently: true });
|
|
24
|
+
if (!context) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const gradient = context.createLinearGradient(0, 0, 256, 0);
|
|
28
|
+
for (const stop of STOPS) {
|
|
29
|
+
gradient.addColorStop(stop.offset, stop.color);
|
|
30
|
+
}
|
|
31
|
+
context.fillStyle = gradient;
|
|
32
|
+
context.fillRect(0, 0, 256, 1);
|
|
33
|
+
paletteCache = context.getImageData(0, 0, 256, 1).data;
|
|
34
|
+
return paletteCache;
|
|
35
|
+
}
|
|
36
|
+
const stampCache = /* @__PURE__ */ new Map();
|
|
37
|
+
function getDiscStamp(radius) {
|
|
38
|
+
const cached = stampCache.get(radius);
|
|
39
|
+
if (cached) {
|
|
40
|
+
return cached;
|
|
41
|
+
}
|
|
42
|
+
const size = Math.max(1, Math.ceil(radius * 4));
|
|
43
|
+
const stamp = document.createElement("canvas");
|
|
44
|
+
stamp.width = size;
|
|
45
|
+
stamp.height = size;
|
|
46
|
+
const context = stamp.getContext("2d");
|
|
47
|
+
if (!context) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const gradient = context.createRadialGradient(
|
|
51
|
+
size / 2,
|
|
52
|
+
size / 2,
|
|
53
|
+
0,
|
|
54
|
+
size / 2,
|
|
55
|
+
size / 2,
|
|
56
|
+
radius * 2
|
|
57
|
+
);
|
|
58
|
+
gradient.addColorStop(0, "rgba(0, 0, 0, 1)");
|
|
59
|
+
gradient.addColorStop(0.45, "rgba(0, 0, 0, 0.45)");
|
|
60
|
+
gradient.addColorStop(1, "rgba(0, 0, 0, 0)");
|
|
61
|
+
context.fillStyle = gradient;
|
|
62
|
+
context.fillRect(0, 0, size, size);
|
|
63
|
+
stampCache.set(radius, stamp);
|
|
64
|
+
return stamp;
|
|
65
|
+
}
|
|
66
|
+
function withScrimBehind(context) {
|
|
67
|
+
context.globalCompositeOperation = "destination-over";
|
|
68
|
+
context.fillStyle = SCRIM_COLOR;
|
|
69
|
+
context.fillRect(0, 0, context.canvas.width, context.canvas.height);
|
|
70
|
+
context.globalCompositeOperation = "source-over";
|
|
71
|
+
}
|
|
72
|
+
function drawHeatPoints({
|
|
73
|
+
context,
|
|
74
|
+
points,
|
|
75
|
+
radius
|
|
76
|
+
}) {
|
|
77
|
+
const { width, height } = context.canvas;
|
|
78
|
+
context.clearRect(0, 0, width, height);
|
|
79
|
+
const stamp = getDiscStamp(radius);
|
|
80
|
+
const palette = getPalette();
|
|
81
|
+
if (points.length > 0 && stamp && palette) {
|
|
82
|
+
const cells = /* @__PURE__ */ new Map();
|
|
83
|
+
for (const point of points) {
|
|
84
|
+
const key = `${Math.round(point.x / radius)},${Math.round(point.y / radius)}`;
|
|
85
|
+
cells.set(key, (cells.get(key) ?? 0) + point.weight);
|
|
86
|
+
}
|
|
87
|
+
const sums = [...cells.values()].sort((first, second) => first - second);
|
|
88
|
+
const max = Math.max(1, sums[Math.floor(sums.length * 0.97)] ?? 1);
|
|
89
|
+
for (const point of points) {
|
|
90
|
+
context.globalAlpha = Math.min(1, Math.max(0.04, point.weight / max));
|
|
91
|
+
context.drawImage(stamp, point.x - radius * 2, point.y - radius * 2);
|
|
92
|
+
}
|
|
93
|
+
context.globalAlpha = 1;
|
|
94
|
+
const image = context.getImageData(0, 0, width, height);
|
|
95
|
+
const pixels = image.data;
|
|
96
|
+
for (let index = 0; index < pixels.length; index += 4) {
|
|
97
|
+
const alpha = pixels[index + 3] ?? 0;
|
|
98
|
+
if (alpha === 0) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const offset = alpha * 4;
|
|
102
|
+
pixels[index] = palette[offset] ?? 0;
|
|
103
|
+
pixels[index + 1] = palette[offset + 1] ?? 0;
|
|
104
|
+
pixels[index + 2] = palette[offset + 2] ?? 0;
|
|
105
|
+
pixels[index + 3] = palette[offset + 3] ?? 0;
|
|
106
|
+
}
|
|
107
|
+
context.putImageData(image, 0, 0);
|
|
108
|
+
}
|
|
109
|
+
withScrimBehind(context);
|
|
110
|
+
}
|
|
111
|
+
function getScrollReach(buckets) {
|
|
112
|
+
const total = buckets.reduce((sum, bucket) => sum + bucket.views, 0);
|
|
113
|
+
const reach = Array.from({ length: 101 }, () => 0);
|
|
114
|
+
if (total === 0) {
|
|
115
|
+
return reach;
|
|
116
|
+
}
|
|
117
|
+
const viewsByPct = /* @__PURE__ */ new Map();
|
|
118
|
+
for (const bucket of buckets) {
|
|
119
|
+
viewsByPct.set(
|
|
120
|
+
bucket.pct,
|
|
121
|
+
(viewsByPct.get(bucket.pct) ?? 0) + bucket.views
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
let remaining = total;
|
|
125
|
+
for (let pct = 0; pct <= 100; pct++) {
|
|
126
|
+
reach[pct] = remaining / total;
|
|
127
|
+
remaining -= viewsByPct.get(pct) ?? 0;
|
|
128
|
+
}
|
|
129
|
+
return reach;
|
|
130
|
+
}
|
|
131
|
+
function drawScrollBands({
|
|
132
|
+
context,
|
|
133
|
+
reach
|
|
134
|
+
}) {
|
|
135
|
+
const { width, height } = context.canvas;
|
|
136
|
+
context.clearRect(0, 0, width, height);
|
|
137
|
+
const palette = getPalette();
|
|
138
|
+
if (!palette) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
for (let pct = 0; pct < 100; pct++) {
|
|
142
|
+
const share = Math.min(1, Math.max(0, reach[pct] ?? 0));
|
|
143
|
+
const offset = Math.round(share * 255) * 4;
|
|
144
|
+
const alpha = Math.min(0.62, (palette[offset + 3] ?? 0) / 255 * 0.7);
|
|
145
|
+
context.fillStyle = `rgba(${palette[offset] ?? 0}, ${palette[offset + 1] ?? 0}, ${palette[offset + 2] ?? 0}, ${alpha})`;
|
|
146
|
+
const top = Math.floor(pct / 100 * height);
|
|
147
|
+
const bottom = Math.ceil((pct + 1) / 100 * height);
|
|
148
|
+
context.fillRect(0, top, width, bottom - top);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function getReachMarks({
|
|
152
|
+
reach,
|
|
153
|
+
height
|
|
154
|
+
}) {
|
|
155
|
+
return REACH_MARKS.flatMap((share) => {
|
|
156
|
+
const pct = reach.findIndex((value) => value < share);
|
|
157
|
+
return pct > 0 ? [{ share, y: pct / 100 * height }] : [];
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function drawMarkLine({
|
|
161
|
+
context,
|
|
162
|
+
y,
|
|
163
|
+
label,
|
|
164
|
+
dashed,
|
|
165
|
+
scale
|
|
166
|
+
}) {
|
|
167
|
+
const { width } = context.canvas;
|
|
168
|
+
context.save();
|
|
169
|
+
context.strokeStyle = MARK_LINE_COLOR;
|
|
170
|
+
context.lineWidth = Math.max(1, 2 * scale);
|
|
171
|
+
context.setLineDash(dashed ? [8 * scale, 6 * scale] : []);
|
|
172
|
+
context.beginPath();
|
|
173
|
+
context.moveTo(0, y);
|
|
174
|
+
context.lineTo(width, y);
|
|
175
|
+
context.stroke();
|
|
176
|
+
const fontPx = LABEL_FONT_PX * scale;
|
|
177
|
+
context.font = `500 ${fontPx}px system-ui, -apple-system, sans-serif`;
|
|
178
|
+
const paddingX = 10 * scale;
|
|
179
|
+
const labelHeight = fontPx + 10 * scale;
|
|
180
|
+
const labelWidth = context.measureText(label).width + paddingX * 2;
|
|
181
|
+
const left = 24 * scale;
|
|
182
|
+
const top = Math.max(0, y - labelHeight - 6 * scale);
|
|
183
|
+
context.fillStyle = MARK_LABEL_BACKGROUND;
|
|
184
|
+
context.beginPath();
|
|
185
|
+
context.roundRect(left, top, labelWidth, labelHeight, labelHeight / 2);
|
|
186
|
+
context.fill();
|
|
187
|
+
context.fillStyle = "#fff";
|
|
188
|
+
context.textBaseline = "middle";
|
|
189
|
+
context.fillText(label, left + paddingX, top + labelHeight / 2);
|
|
190
|
+
context.restore();
|
|
191
|
+
}
|
|
192
|
+
export {
|
|
193
|
+
drawHeatPoints,
|
|
194
|
+
drawMarkLine,
|
|
195
|
+
drawScrollBands,
|
|
196
|
+
getReachMarks,
|
|
197
|
+
getScrollReach
|
|
198
|
+
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@replohq/sandbox-runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"reploBuild": {
|
|
5
5
|
"packageName": "@replohq/sandbox-runtime",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.19.0",
|
|
7
7
|
"branch": "HEAD",
|
|
8
|
-
"commit": "
|
|
9
|
-
"builtAt": "2026-10-
|
|
8
|
+
"commit": "1aa1f09e1dd141b243b7bb3b1ff1bc947fa01371",
|
|
9
|
+
"builtAt": "2026-10-23T05:44:42.441Z"
|
|
10
10
|
},
|
|
11
11
|
"description": "Replo sandbox runtime — editor integration for agent-built Next.js sites.",
|
|
12
12
|
"license": "SEE LICENSE IN LICENSE",
|
package/sandbox-runtime.js
CHANGED
|
@@ -5,6 +5,7 @@ import { AnnotationLayerMount } from "./annotations/AnnotationLayerMount";
|
|
|
5
5
|
import { ClientErrorBoundary } from "./ClientErrorBoundary";
|
|
6
6
|
import { DesignLibraryPreview } from "./design-library-preview/DesignLibraryPreview";
|
|
7
7
|
import { DocumentReadyBeacon } from "./DocumentReadyBeacon";
|
|
8
|
+
import { HeatLayerMount } from "./heatmaps/HeatLayerMount";
|
|
8
9
|
import LinkInterceptor from "./LinkInterceptor";
|
|
9
10
|
import { NavigationReceiver } from "./NavigationReceiver";
|
|
10
11
|
import { PageCaptureResponder } from "./PageCaptureResponder";
|
|
@@ -26,6 +27,7 @@ function SandboxRuntime({ children }) {
|
|
|
26
27
|
/* @__PURE__ */ jsx(PageCaptureResponder, {}),
|
|
27
28
|
/* @__PURE__ */ jsx(EditorSelection, {}),
|
|
28
29
|
/* @__PURE__ */ jsx(AnnotationLayerMount, {}),
|
|
30
|
+
/* @__PURE__ */ jsx(HeatLayerMount, {}),
|
|
29
31
|
/* @__PURE__ */ jsx(DesignLibraryPreview, {}),
|
|
30
32
|
children
|
|
31
33
|
] })
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "../interactions/sectionGeometry";
|
|
8
8
|
import { getElementBoundingRect } from "../interactions/selectionGeometry";
|
|
9
9
|
import { isSectionCanvasControl } from "../section-canvas/sectionCanvasControls";
|
|
10
|
+
import { sectionCanvasStore } from "../section-canvas/sectionCanvasStore";
|
|
10
11
|
import { useDisplayMode, useSelectModeStore } from "../selectModeStore";
|
|
11
12
|
function useSelectionHover() {
|
|
12
13
|
const store = useSelectModeStore();
|
|
@@ -31,6 +32,10 @@ function useSelectionHover() {
|
|
|
31
32
|
getWebsiteBuilderRpc()?.hoverElement({ rect: hoverRect });
|
|
32
33
|
};
|
|
33
34
|
const syncCurrentHover = (pointerClientPosition = store.getState().lastPointerClientPosition) => {
|
|
35
|
+
if (sectionCanvasStore.getState().drag) {
|
|
36
|
+
postHoveredElement(null);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
34
39
|
const previewSection = store.getState().sectionPreviewElement;
|
|
35
40
|
if (previewSection) {
|
|
36
41
|
if (previewSection.isConnected) {
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
getPlacedSections,
|
|
18
18
|
getSectionGaps,
|
|
19
|
-
GHOST_WIDTH_PX,
|
|
20
19
|
sectionCanvasStore,
|
|
21
20
|
useSectionCanvasStore
|
|
22
21
|
} from "./sectionCanvasStore";
|
|
@@ -29,10 +28,11 @@ const ACCENT_PRESSED = "#1d4ed8";
|
|
|
29
28
|
const PILL_HEIGHT = 22;
|
|
30
29
|
const PILL_SPREAD = 64;
|
|
31
30
|
const PILL_HALF_WIDTH = 52;
|
|
31
|
+
const LABEL_HALF_WIDTH = 80;
|
|
32
32
|
const SECTION_CANVAS_CSS = `
|
|
33
33
|
[${SECTION_CANVAS_ATTRIBUTE}] button { all: initial; box-sizing: border-box; display: flex; align-items: center; justify-content: center; pointer-events: auto; cursor: pointer !important; color: ${ACCENT}; background: #fff !important; }
|
|
34
34
|
[${SECTION_CANVAS_ATTRIBUTE}] button * { cursor: inherit !important; }
|
|
35
|
-
[${SECTION_CANVAS_ATTRIBUTE}] [data-shape="pill"] { position: absolute; transform: translate(-50%, -50%); white-space: nowrap; border-radius: 4px; background: ${ACCENT} !important; color: #fff !important; font-family: system-ui, -apple-system, sans-serif; font-weight: 600; box-shadow: 0 2px 6px rgba(15, 23, 42, 0.2); }
|
|
35
|
+
[${SECTION_CANVAS_ATTRIBUTE}] [data-shape="pill"] { position: absolute; display: flex; align-items: center; transform: translate(-50%, -50%); white-space: nowrap; border-radius: 4px; background: ${ACCENT} !important; color: #fff !important; font-family: system-ui, -apple-system, sans-serif; font-weight: 600; box-shadow: 0 2px 6px rgba(15, 23, 42, 0.2); }
|
|
36
36
|
[${SECTION_CANVAS_ATTRIBUTE}] button[data-shape="pill"]:hover, [${SECTION_CANVAS_ATTRIBUTE}] button[data-shape="pill"]:focus-visible { background: ${ACCENT_PRESSED} !important; color: #fff !important; }
|
|
37
37
|
[${SECTION_CANVAS_ATTRIBUTE}] button[data-shape="tool"] { border-radius: 4px; gap: 4px; background: transparent !important; color: #fff !important; font-family: system-ui, -apple-system, sans-serif; font-weight: 600; }
|
|
38
38
|
[${SECTION_CANVAS_ATTRIBUTE}] button[data-shape="tool"][data-grip] { cursor: grab !important; }
|
|
@@ -237,73 +237,55 @@ function SectionCanvas() {
|
|
|
237
237
|
}
|
|
238
238
|
);
|
|
239
239
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
240
|
+
const markedGap = drag ? drag.gap : canvas.insertingAt;
|
|
241
|
+
let gapMarker = null;
|
|
242
|
+
const placed = typeof markedGap === "number" ? getPlacedSections({ canvas, elementsByIndex }) : [];
|
|
243
|
+
const gap = getSectionGaps(placed, canvas.sections.length).find(
|
|
244
|
+
(candidate) => candidate.at === markedGap
|
|
245
|
+
);
|
|
246
|
+
if (gap) {
|
|
247
|
+
const left = Math.max(
|
|
248
|
+
visible.left,
|
|
249
|
+
Math.min(...placed.map(({ box }) => box.left))
|
|
246
250
|
);
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
height: 3 * unit,
|
|
270
|
-
background: ACCENT
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
),
|
|
274
|
-
/* @__PURE__ */ jsx(
|
|
275
|
-
"div",
|
|
276
|
-
{
|
|
277
|
-
"data-shape": "pill",
|
|
278
|
-
role: "status",
|
|
279
|
-
style: {
|
|
280
|
-
left: (left + right) / 2,
|
|
281
|
-
top: y,
|
|
282
|
-
height: PILL_HEIGHT * unit,
|
|
283
|
-
padding: `0 ${10 * unit}px`,
|
|
284
|
-
fontSize: 11 * unit,
|
|
285
|
-
display: "flex",
|
|
286
|
-
alignItems: "center"
|
|
287
|
-
},
|
|
288
|
-
children: "New Section Goes Here"
|
|
251
|
+
const right = Math.min(
|
|
252
|
+
visible.right,
|
|
253
|
+
Math.max(...placed.map(({ box }) => box.right))
|
|
254
|
+
);
|
|
255
|
+
const y = Math.min(
|
|
256
|
+
Math.max(gap.y, PILL_HEIGHT * unit),
|
|
257
|
+
window.innerHeight - PILL_HEIGHT * unit
|
|
258
|
+
);
|
|
259
|
+
gapMarker = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
260
|
+
/* @__PURE__ */ jsx(
|
|
261
|
+
"div",
|
|
262
|
+
{
|
|
263
|
+
"data-replo-section-drop-line": true,
|
|
264
|
+
style: {
|
|
265
|
+
position: "fixed",
|
|
266
|
+
left,
|
|
267
|
+
width: right - left,
|
|
268
|
+
top: y - 1.5 * unit,
|
|
269
|
+
height: 3 * unit,
|
|
270
|
+
borderRadius: 3 * unit,
|
|
271
|
+
background: ACCENT,
|
|
272
|
+
boxShadow: `0 0 0 ${unit}px #fff`
|
|
289
273
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
274
|
+
}
|
|
275
|
+
),
|
|
276
|
+
!drag && /* @__PURE__ */ jsx(
|
|
277
|
+
"div",
|
|
278
|
+
{
|
|
279
|
+
"data-shape": "pill",
|
|
280
|
+
role: "status",
|
|
281
|
+
style: pillStyle({ unit, left: (left + right) / 2, top: y }),
|
|
282
|
+
children: "New Section Goes Here"
|
|
283
|
+
}
|
|
284
|
+
)
|
|
285
|
+
] });
|
|
293
286
|
}
|
|
294
287
|
let dragLayer = null;
|
|
295
288
|
if (drag?.isActive && activeBox) {
|
|
296
|
-
const placed = getPlacedSections({ canvas, elementsByIndex });
|
|
297
|
-
const gap = drag.gap === null ? null : getSectionGaps(placed, canvas.sections.length).find((candidate) => {
|
|
298
|
-
return candidate.at === drag.gap;
|
|
299
|
-
});
|
|
300
|
-
const lineLeft = Math.min(...placed.map(({ box }) => box.left));
|
|
301
|
-
const lineRight = Math.max(...placed.map(({ box }) => box.right));
|
|
302
|
-
const ghostWidth = GHOST_WIDTH_PX * unit;
|
|
303
|
-
const ghostHeight = Math.min(
|
|
304
|
-
(activeBox.bottom - activeBox.top) / (activeBox.right - activeBox.left) * ghostWidth,
|
|
305
|
-
ghostWidth * 0.75
|
|
306
|
-
);
|
|
307
289
|
dragLayer = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
308
290
|
/* @__PURE__ */ jsx(
|
|
309
291
|
"div",
|
|
@@ -320,61 +302,26 @@ function SectionCanvas() {
|
|
|
320
302
|
}
|
|
321
303
|
}
|
|
322
304
|
),
|
|
323
|
-
gap && /* @__PURE__ */ jsx(
|
|
324
|
-
"div",
|
|
325
|
-
{
|
|
326
|
-
"data-replo-section-drop-line": true,
|
|
327
|
-
style: {
|
|
328
|
-
position: "fixed",
|
|
329
|
-
left: lineLeft,
|
|
330
|
-
width: lineRight - lineLeft,
|
|
331
|
-
top: Math.min(
|
|
332
|
-
Math.max(gap.y, 3 * unit),
|
|
333
|
-
window.innerHeight - 3 * unit
|
|
334
|
-
) - 1.5 * unit,
|
|
335
|
-
height: 3 * unit,
|
|
336
|
-
borderRadius: 3 * unit,
|
|
337
|
-
background: ACCENT,
|
|
338
|
-
boxShadow: `0 0 0 ${unit}px #fff`
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
),
|
|
342
305
|
/* @__PURE__ */ jsx(
|
|
343
306
|
"div",
|
|
344
307
|
{
|
|
308
|
+
"data-shape": "pill",
|
|
345
309
|
style: {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
310
|
+
...pillStyle({
|
|
311
|
+
unit,
|
|
312
|
+
left: Math.min(
|
|
313
|
+
Math.max(drag.x, LABEL_HALF_WIDTH * unit),
|
|
314
|
+
window.innerWidth - LABEL_HALF_WIDTH * unit
|
|
315
|
+
),
|
|
316
|
+
top: drag.y + 20 * unit + PILL_HEIGHT * unit > window.innerHeight ? drag.y - 20 * unit : drag.y + 20 * unit
|
|
317
|
+
}),
|
|
318
|
+
maxWidth: 2 * LABEL_HALF_WIDTH * unit,
|
|
319
|
+
display: "block",
|
|
320
|
+
lineHeight: `${PILL_HEIGHT * unit}px`,
|
|
354
321
|
overflow: "hidden",
|
|
355
|
-
|
|
356
|
-
background: "#fff",
|
|
357
|
-
boxShadow: `0 ${8 * unit}px ${24 * unit}px rgba(15, 23, 42, 0.25), 0 0 0 ${unit}px rgba(15, 23, 42, 0.12)`,
|
|
358
|
-
opacity: 0.95,
|
|
359
|
-
display: "flex",
|
|
360
|
-
alignItems: "center",
|
|
361
|
-
justifyContent: "center",
|
|
362
|
-
font: `500 ${12 * unit}px/1.3 system-ui, sans-serif`,
|
|
363
|
-
color: "#3f3f46"
|
|
322
|
+
textOverflow: "ellipsis"
|
|
364
323
|
},
|
|
365
|
-
children:
|
|
366
|
-
"img",
|
|
367
|
-
{
|
|
368
|
-
src: drag.ghostSrc,
|
|
369
|
-
alt: "",
|
|
370
|
-
style: {
|
|
371
|
-
display: "block",
|
|
372
|
-
width: "100%",
|
|
373
|
-
height: "auto",
|
|
374
|
-
alignSelf: "flex-start"
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
) : activeSection?.label ?? "Section"
|
|
324
|
+
children: activeSection?.label ?? "Section"
|
|
378
325
|
}
|
|
379
326
|
)
|
|
380
327
|
] });
|
|
@@ -397,7 +344,7 @@ function SectionCanvas() {
|
|
|
397
344
|
onPointerDown: (event) => event.stopPropagation(),
|
|
398
345
|
children: [
|
|
399
346
|
/* @__PURE__ */ jsx("style", { children: SECTION_CANVAS_CSS }),
|
|
400
|
-
|
|
347
|
+
gapMarker,
|
|
401
348
|
overlay,
|
|
402
349
|
dragLayer
|
|
403
350
|
]
|
|
@@ -406,6 +353,19 @@ function SectionCanvas() {
|
|
|
406
353
|
document.body
|
|
407
354
|
);
|
|
408
355
|
}
|
|
356
|
+
function pillStyle({
|
|
357
|
+
unit,
|
|
358
|
+
left,
|
|
359
|
+
top
|
|
360
|
+
}) {
|
|
361
|
+
return {
|
|
362
|
+
left,
|
|
363
|
+
top,
|
|
364
|
+
height: PILL_HEIGHT * unit,
|
|
365
|
+
padding: `0 ${10 * unit}px`,
|
|
366
|
+
fontSize: 11 * unit
|
|
367
|
+
};
|
|
368
|
+
}
|
|
409
369
|
function SectionControls({
|
|
410
370
|
index,
|
|
411
371
|
label,
|
|
@@ -435,6 +395,8 @@ function SectionControls({
|
|
|
435
395
|
),
|
|
436
396
|
visible.right - PILL_HALF_WIDTH * unit - spread
|
|
437
397
|
);
|
|
398
|
+
const outlineLeft = Math.max(box.left, visible.left);
|
|
399
|
+
const outlineRight = Math.min(box.right, visible.right);
|
|
438
400
|
const toolbarTop = Math.max(box.top, 0) + TOOLBAR_INSET * unit;
|
|
439
401
|
const textButton = {
|
|
440
402
|
height: size,
|
|
@@ -447,9 +409,9 @@ function SectionControls({
|
|
|
447
409
|
{
|
|
448
410
|
style: {
|
|
449
411
|
position: "fixed",
|
|
450
|
-
left:
|
|
412
|
+
left: outlineLeft,
|
|
451
413
|
top: box.top,
|
|
452
|
-
width:
|
|
414
|
+
width: outlineRight - outlineLeft,
|
|
453
415
|
height: box.bottom - box.top,
|
|
454
416
|
boxSizing: "border-box",
|
|
455
417
|
border: `${2 * unit}px solid ${ACCENT}`,
|
|
@@ -471,13 +433,7 @@ function SectionControls({
|
|
|
471
433
|
type: "button",
|
|
472
434
|
"data-shape": "pill",
|
|
473
435
|
"aria-label": `Add Section ${edge} ${label}`,
|
|
474
|
-
style: {
|
|
475
|
-
left,
|
|
476
|
-
top,
|
|
477
|
-
height: pillHeight,
|
|
478
|
-
padding: `0 ${10 * unit}px`,
|
|
479
|
-
fontSize: 11 * unit
|
|
480
|
-
},
|
|
436
|
+
style: pillStyle({ unit, left, top }),
|
|
481
437
|
onClick: () => actions.request({ type: "insert", at }),
|
|
482
438
|
children: "Add Section"
|
|
483
439
|
},
|
|
@@ -489,7 +445,7 @@ function SectionControls({
|
|
|
489
445
|
style: {
|
|
490
446
|
position: "fixed",
|
|
491
447
|
top: toolbarTop,
|
|
492
|
-
right: window.innerWidth -
|
|
448
|
+
right: window.innerWidth - outlineRight + TOOLBAR_INSET * unit,
|
|
493
449
|
display: "flex",
|
|
494
450
|
gap: 2 * unit,
|
|
495
451
|
padding: 2 * unit,
|
|
@@ -7,7 +7,6 @@ export type SectionDrag = {
|
|
|
7
7
|
x: number;
|
|
8
8
|
y: number;
|
|
9
9
|
isActive: boolean;
|
|
10
|
-
ghostSrc: string | null;
|
|
11
10
|
gap: number | null;
|
|
12
11
|
};
|
|
13
12
|
type PendingMove = {
|
|
@@ -52,7 +51,6 @@ type SectionCanvasStoreState = {
|
|
|
52
51
|
}) => void;
|
|
53
52
|
};
|
|
54
53
|
};
|
|
55
|
-
export declare const GHOST_WIDTH_PX = 240;
|
|
56
54
|
/** On-screen sections in page order, with their boxes. */
|
|
57
55
|
export declare function getPlacedSections(state: Pick<SectionCanvasStoreState, "canvas" | "elementsByIndex">): {
|
|
58
56
|
section: import("../../_vendor/schemas/generated/selectModeIframeRpc").SectionCanvasSection;
|
|
@@ -8,9 +8,7 @@ import {
|
|
|
8
8
|
startOptimisticSectionMove
|
|
9
9
|
} from "./optimisticSectionMove";
|
|
10
10
|
import { findSectionElements } from "./sectionElements";
|
|
11
|
-
import { captureSectionGhost } from "./sectionGhost";
|
|
12
11
|
const DRAG_THRESHOLD_PX = 4;
|
|
13
|
-
const GHOST_WIDTH_PX = 240;
|
|
14
12
|
let resolveToken = 0;
|
|
15
13
|
let moveCounter = 0;
|
|
16
14
|
function sameSections(previous, next) {
|
|
@@ -164,7 +162,6 @@ const sectionCanvasStore = createShallowStore(
|
|
|
164
162
|
getWebsiteBuilderRpc()?.sectionCanvasRequest({ request });
|
|
165
163
|
},
|
|
166
164
|
beginDrag: ({ index, pointerId, x, y }) => {
|
|
167
|
-
const elements = get().elementsByIndex.get(index) ?? [];
|
|
168
165
|
set({
|
|
169
166
|
drag: {
|
|
170
167
|
index,
|
|
@@ -174,20 +171,9 @@ const sectionCanvasStore = createShallowStore(
|
|
|
174
171
|
x,
|
|
175
172
|
y,
|
|
176
173
|
isActive: false,
|
|
177
|
-
ghostSrc: null,
|
|
178
174
|
gap: null
|
|
179
175
|
}
|
|
180
176
|
});
|
|
181
|
-
const scale = get().canvas?.previewScale ?? 1;
|
|
182
|
-
void captureSectionGhost({
|
|
183
|
-
elements,
|
|
184
|
-
width: GHOST_WIDTH_PX / scale
|
|
185
|
-
}).then((ghostSrc) => {
|
|
186
|
-
const drag = get().drag;
|
|
187
|
-
if (ghostSrc && drag?.pointerId === pointerId && drag.index === index) {
|
|
188
|
-
set({ drag: { ...drag, ghostSrc } });
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
177
|
},
|
|
192
178
|
updateDrag: ({ x, y }) => {
|
|
193
179
|
const state = get();
|
|
@@ -267,7 +253,6 @@ function useSectionCanvasStore(selector) {
|
|
|
267
253
|
return useShallowStore(sectionCanvasStore, selector);
|
|
268
254
|
}
|
|
269
255
|
export {
|
|
270
|
-
GHOST_WIDTH_PX,
|
|
271
256
|
getPlacedSections,
|
|
272
257
|
getSectionGaps,
|
|
273
258
|
sectionCanvasStore,
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
async function captureSectionGhost({
|
|
3
|
-
elements,
|
|
4
|
-
width
|
|
5
|
-
}) {
|
|
6
|
-
const element = elements.find((candidate) => candidate.isConnected);
|
|
7
|
-
if (!element) {
|
|
8
|
-
return null;
|
|
9
|
-
}
|
|
10
|
-
const rect = element.getBoundingClientRect();
|
|
11
|
-
if (rect.width === 0 || rect.height === 0) {
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
try {
|
|
15
|
-
const { snapdom } = await import("@zumer/snapdom");
|
|
16
|
-
return await snapdom.toRaw(element, { width, embedFonts: false });
|
|
17
|
-
} catch {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
export {
|
|
22
|
-
captureSectionGhost
|
|
23
|
-
};
|
|
File without changes
|
|
File without changes
|