@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.
Files changed (44) hide show
  1. package/_vendor/replo-utils/lib/crypto.d.ts +1 -0
  2. package/_vendor/replo-utils/lib/crypto.mjs +1 -1
  3. package/_vendor/replo-utils/lib/fonts.mjs +2 -2
  4. package/_vendor/replo-utils/lib/math.mjs +3 -3
  5. package/_vendor/replo-utils/lib/misc.mjs +4 -4
  6. package/_vendor/replo-utils/lib/pageCapture.mjs +1 -1
  7. package/_vendor/schemas/errors.d.ts +0 -1
  8. package/_vendor/schemas/generated/pageHeatmap.d.ts +57 -0
  9. package/_vendor/schemas/generated/pageHeatmap.mjs +1 -0
  10. package/_vendor/schemas/iframeRpcChannel.mjs +6 -6
  11. package/_vendor/schemas/pageHeatmap.d.ts +132 -0
  12. package/_vendor/schemas/pageHeatmap.mjs +72 -0
  13. package/_vendor/schemas/previewHealthIframeRpc.mjs +6 -6
  14. package/_vendor/schemas/selectMode.d.ts +1 -1
  15. package/_vendor/schemas/selectMode.mjs +5 -5
  16. package/_vendor/schemas/selectModeIframeRpc.d.ts +2 -2
  17. package/_vendor/schemas/selectModeIframeRpc.mjs +5 -5
  18. package/buildMetadata.js +3 -3
  19. package/capturePage.js +5 -1
  20. package/{chunk-KRA4LUIR.mjs → chunk-36IPO47A.mjs} +2 -2
  21. package/{chunk-TP4ZXK4R.mjs → chunk-QTGDSYSU.mjs} +1 -1
  22. package/{chunk-FLYEIRFK.mjs → chunk-SKVPFN7M.mjs} +1 -1
  23. package/{chunk-PH3S6LJI.mjs → chunk-THOBCJJA.mjs} +2 -2
  24. package/{chunk-LIPH4O4F.mjs → chunk-ZTT7SNYN.mjs} +5 -2
  25. package/heatmaps/HeatLayerMount.d.ts +1 -0
  26. package/heatmaps/HeatLayerMount.js +12 -0
  27. package/heatmaps/heatBadges.d.ts +25 -0
  28. package/heatmaps/heatBadges.js +98 -0
  29. package/heatmaps/heatCore.d.ts +34 -0
  30. package/heatmaps/heatCore.js +465 -0
  31. package/heatmaps/heatLayer.d.ts +1 -0
  32. package/heatmaps/heatLayer.js +85 -0
  33. package/heatmaps/heatRender.d.ts +36 -0
  34. package/heatmaps/heatRender.js +198 -0
  35. package/package.json +4 -4
  36. package/sandbox-runtime.js +2 -0
  37. package/select-mode/hooks/useSelectionHover.js +5 -0
  38. package/select-mode/section-canvas/SectionCanvas.js +80 -124
  39. package/select-mode/section-canvas/sectionCanvasStore.d.ts +0 -2
  40. package/select-mode/section-canvas/sectionCanvasStore.js +0 -15
  41. package/select-mode/section-canvas/sectionGhost.d.ts +0 -5
  42. package/select-mode/section-canvas/sectionGhost.js +0 -23
  43. /package/{chunk-TESFJQID.mjs → chunk-4ITJNSXZ.mjs} +0 -0
  44. /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.18.0",
3
+ "version": "1.19.0",
4
4
  "reploBuild": {
5
5
  "packageName": "@replohq/sandbox-runtime",
6
- "version": "1.18.0",
6
+ "version": "1.19.0",
7
7
  "branch": "HEAD",
8
- "commit": "371ae98105e9f99b04918b3dffe40867380cd9ef",
9
- "builtAt": "2026-10-21T22:33:13.604Z"
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",
@@ -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
- let insertMarker = null;
241
- const insertingAt = canvas.insertingAt;
242
- if (typeof insertingAt === "number" && !drag) {
243
- const placed = getPlacedSections({ canvas, elementsByIndex });
244
- const gap = getSectionGaps(placed, canvas.sections.length).find(
245
- (candidate) => candidate.at === insertingAt
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
- if (gap) {
248
- const left = Math.max(
249
- visible.left,
250
- Math.min(...placed.map(({ box }) => box.left))
251
- );
252
- const right = Math.min(
253
- visible.right,
254
- Math.max(...placed.map(({ box }) => box.right))
255
- );
256
- const y = Math.min(
257
- Math.max(gap.y, PILL_HEIGHT * unit),
258
- window.innerHeight - PILL_HEIGHT * unit
259
- );
260
- insertMarker = /* @__PURE__ */ jsxs(Fragment, { children: [
261
- /* @__PURE__ */ jsx(
262
- "div",
263
- {
264
- style: {
265
- position: "fixed",
266
- left,
267
- width: right - left,
268
- top: y - 1.5 * unit,
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
- position: "fixed",
347
- left: drag.x + 14 * unit + ghostWidth > window.innerWidth ? drag.x - 14 * unit - ghostWidth : drag.x + 14 * unit,
348
- top: Math.min(
349
- drag.y + 14 * unit,
350
- window.innerHeight - ghostHeight - 8 * unit
351
- ),
352
- width: ghostWidth,
353
- height: ghostHeight,
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
- borderRadius: 6 * unit,
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: drag.ghostSrc ? /* @__PURE__ */ jsx(
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
- insertMarker,
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: box.left,
412
+ left: outlineLeft,
451
413
  top: box.top,
452
- width: box.right - box.left,
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 - Math.min(box.right, visible.right) + TOOLBAR_INSET * unit,
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,5 +0,0 @@
1
- /** A small picture of a section to drag around, or null when it can't be drawn. */
2
- export declare function captureSectionGhost({ elements, width, }: {
3
- elements: readonly HTMLElement[];
4
- width: number;
5
- }): Promise<string | null>;
@@ -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