@replohq/sandbox-runtime 1.10.2 → 1.10.3

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 (32) hide show
  1. package/_vendor/replo-utils/lib/fonts.d.ts +12 -1
  2. package/_vendor/replo-utils/lib/fonts.mjs +41 -3
  3. package/_vendor/replo-utils/lib/miniappPreview.d.ts +13 -0
  4. package/_vendor/replo-utils/lib/miniappPreview.mjs +48 -0
  5. package/_vendor/replo-utils/lib/misc.d.ts +4 -0
  6. package/_vendor/replo-utils/lib/misc.mjs +1 -1
  7. package/_vendor/schemas/componentPreview.d.ts +0 -55
  8. package/_vendor/schemas/componentPreview.mjs +1 -45
  9. package/_vendor/schemas/designLibrary.d.ts +23 -0
  10. package/_vendor/schemas/designLibrary.mjs +8 -1
  11. package/_vendor/schemas/generated/componentPreview.d.ts +0 -34
  12. package/_vendor/schemas/generated/selectMode.d.ts +8 -0
  13. package/_vendor/schemas/iframeRpcChannel.mjs +1 -1
  14. package/_vendor/schemas/selectMode.d.ts +25 -1
  15. package/_vendor/schemas/selectMode.mjs +2 -2
  16. package/_vendor/schemas/selectModeIframeRpc.d.ts +45 -5
  17. package/_vendor/schemas/selectModeIframeRpc.mjs +2 -2
  18. package/annotations/bridgeGate.d.ts +0 -5
  19. package/annotations/bridgeGate.js +2 -1
  20. package/buildMetadata.js +3 -3
  21. package/capturePage.d.ts +6 -0
  22. package/capturePage.js +118 -6
  23. package/{chunk-NBVMONRN.mjs → chunk-KFJZ2OLV.mjs} +10 -2
  24. package/{chunk-JUKCUPQ6.mjs → chunk-PH3S6LJI.mjs} +21 -1
  25. package/package.json +4 -4
  26. package/select-mode/descriptor/elementDescriptor.js +10 -2
  27. package/select-mode/descriptor/styleExtraction.d.ts +4 -0
  28. package/select-mode/descriptor/styleExtraction.js +134 -19
  29. package/select-mode/hooks/useOptimisticOverrides.js +7 -0
  30. package/select-mode/hooks/useSelectionInteractions.js +7 -5
  31. package/select-mode/iframeRpcChannel.d.ts +16 -1
  32. package/select-mode/selectModeStore.js +5 -1
@@ -4,8 +4,8 @@ export declare const SELECT_MODE_IFRAME_RPC_CHANNEL_ID: "replo:select-mode-ifram
4
4
  export declare const SELECT_MODE_IFRAME_RPC_CHANNEL_VERSION: 1;
5
5
  export declare const inlineEditFeedbackSchema: z.ZodObject<{
6
6
  status: z.ZodEnum<{
7
- unavailable: "unavailable";
8
7
  pending: "pending";
8
+ unavailable: "unavailable";
9
9
  failed: "failed";
10
10
  }>;
11
11
  rect: z.ZodObject<{
@@ -91,6 +91,10 @@ export declare const selectModeEventInputSchemas: {
91
91
  previewUrl: z.ZodNullable<z.ZodString>;
92
92
  }, z.core.$strip>>;
93
93
  }, z.core.$strip>>;
94
+ video: z.ZodOptional<z.ZodObject<{
95
+ src: z.ZodString;
96
+ currentSrc: z.ZodString;
97
+ }, z.core.$strip>>;
94
98
  definedStyles: z.ZodDefault<z.ZodArray<z.ZodObject<{
95
99
  property: z.ZodString;
96
100
  variant: z.ZodNullable<z.ZodObject<{
@@ -104,6 +108,7 @@ export declare const selectModeEventInputSchemas: {
104
108
  raw: z.ZodString;
105
109
  resolved: z.ZodString;
106
110
  }, z.core.$strip>>>;
111
+ authoredStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
107
112
  inheritedStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
108
113
  clickPosition: z.ZodObject<{
109
114
  x: z.ZodNumber;
@@ -233,6 +238,10 @@ export declare const websiteBuilderToPreviewIframeMethodConfig: {
233
238
  previewUrl: z.ZodNullable<z.ZodString>;
234
239
  }, z.core.$strip>>;
235
240
  }, z.core.$strip>>;
241
+ video: z.ZodOptional<z.ZodObject<{
242
+ src: z.ZodString;
243
+ currentSrc: z.ZodString;
244
+ }, z.core.$strip>>;
236
245
  definedStyles: z.ZodDefault<z.ZodArray<z.ZodObject<{
237
246
  property: z.ZodString;
238
247
  variant: z.ZodNullable<z.ZodObject<{
@@ -246,6 +255,7 @@ export declare const websiteBuilderToPreviewIframeMethodConfig: {
246
255
  raw: z.ZodString;
247
256
  resolved: z.ZodString;
248
257
  }, z.core.$strip>>>;
258
+ authoredStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
249
259
  inheritedStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
250
260
  clickPosition: z.ZodObject<{
251
261
  x: z.ZodNumber;
@@ -310,6 +320,10 @@ export declare const websiteBuilderToPreviewIframeMethodConfig: {
310
320
  previewUrl: z.ZodNullable<z.ZodString>;
311
321
  }, z.core.$strip>>;
312
322
  }, z.core.$strip>>;
323
+ video: z.ZodOptional<z.ZodObject<{
324
+ src: z.ZodString;
325
+ currentSrc: z.ZodString;
326
+ }, z.core.$strip>>;
313
327
  definedStyles: z.ZodDefault<z.ZodArray<z.ZodObject<{
314
328
  property: z.ZodString;
315
329
  variant: z.ZodNullable<z.ZodObject<{
@@ -323,6 +337,7 @@ export declare const websiteBuilderToPreviewIframeMethodConfig: {
323
337
  raw: z.ZodString;
324
338
  resolved: z.ZodString;
325
339
  }, z.core.$strip>>>;
340
+ authoredStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
326
341
  inheritedStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
327
342
  clickPosition: z.ZodObject<{
328
343
  x: z.ZodNumber;
@@ -369,8 +384,8 @@ export declare const websiteBuilderToPreviewIframeMethodConfig: {
369
384
  selectionType: z.ZodEnum<{
370
385
  drag: "drag";
371
386
  area: "area";
372
- element: "element";
373
387
  none: "none";
388
+ element: "element";
374
389
  }>;
375
390
  selectors: z.ZodOptional<z.ZodArray<z.ZodString>>;
376
391
  }, z.core.$strip>;
@@ -506,6 +521,10 @@ export declare const previewIframeToWebsiteBuilderMethodConfig: {
506
521
  previewUrl: z.ZodNullable<z.ZodString>;
507
522
  }, z.core.$strip>>;
508
523
  }, z.core.$strip>>;
524
+ video: z.ZodOptional<z.ZodObject<{
525
+ src: z.ZodString;
526
+ currentSrc: z.ZodString;
527
+ }, z.core.$strip>>;
509
528
  definedStyles: z.ZodDefault<z.ZodArray<z.ZodObject<{
510
529
  property: z.ZodString;
511
530
  variant: z.ZodNullable<z.ZodObject<{
@@ -519,6 +538,7 @@ export declare const previewIframeToWebsiteBuilderMethodConfig: {
519
538
  raw: z.ZodString;
520
539
  resolved: z.ZodString;
521
540
  }, z.core.$strip>>>;
541
+ authoredStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
522
542
  inheritedStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
523
543
  clickPosition: z.ZodObject<{
524
544
  x: z.ZodNumber;
@@ -601,6 +621,10 @@ export declare const previewIframeToWebsiteBuilderMethodConfig: {
601
621
  previewUrl: z.ZodNullable<z.ZodString>;
602
622
  }, z.core.$strip>>;
603
623
  }, z.core.$strip>>;
624
+ video: z.ZodOptional<z.ZodObject<{
625
+ src: z.ZodString;
626
+ currentSrc: z.ZodString;
627
+ }, z.core.$strip>>;
604
628
  definedStyles: z.ZodDefault<z.ZodArray<z.ZodObject<{
605
629
  property: z.ZodString;
606
630
  variant: z.ZodNullable<z.ZodObject<{
@@ -614,6 +638,7 @@ export declare const previewIframeToWebsiteBuilderMethodConfig: {
614
638
  raw: z.ZodString;
615
639
  resolved: z.ZodString;
616
640
  }, z.core.$strip>>>;
641
+ authoredStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
617
642
  inheritedStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
618
643
  clickPosition: z.ZodObject<{
619
644
  x: z.ZodNumber;
@@ -785,6 +810,10 @@ export declare const previewIframeToWebsiteBuilderMethodConfig: {
785
810
  previewUrl: z.ZodNullable<z.ZodString>;
786
811
  }, z.core.$strip>>;
787
812
  }, z.core.$strip>>;
813
+ video: z.ZodOptional<z.ZodObject<{
814
+ src: z.ZodString;
815
+ currentSrc: z.ZodString;
816
+ }, z.core.$strip>>;
788
817
  definedStyles: z.ZodDefault<z.ZodArray<z.ZodObject<{
789
818
  property: z.ZodString;
790
819
  variant: z.ZodNullable<z.ZodObject<{
@@ -798,6 +827,7 @@ export declare const previewIframeToWebsiteBuilderMethodConfig: {
798
827
  raw: z.ZodString;
799
828
  resolved: z.ZodString;
800
829
  }, z.core.$strip>>>;
830
+ authoredStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
801
831
  inheritedStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
802
832
  clickPosition: z.ZodObject<{
803
833
  x: z.ZodNumber;
@@ -852,8 +882,8 @@ export declare const previewIframeToWebsiteBuilderMethodConfig: {
852
882
  inputSchema: z.ZodObject<{
853
883
  feedback: z.ZodNullable<z.ZodObject<{
854
884
  status: z.ZodEnum<{
855
- unavailable: "unavailable";
856
885
  pending: "pending";
886
+ unavailable: "unavailable";
857
887
  failed: "failed";
858
888
  }>;
859
889
  rect: z.ZodObject<{
@@ -912,8 +942,8 @@ export declare const selectModeParentMessageSchema: z.ZodDiscriminatedUnion<[z.Z
912
942
  selectionType: z.ZodEnum<{
913
943
  drag: "drag";
914
944
  area: "area";
915
- element: "element";
916
945
  none: "none";
946
+ element: "element";
917
947
  }>;
918
948
  selectors: z.ZodOptional<z.ZodArray<z.ZodString>>;
919
949
  }, z.core.$strip>;
@@ -1046,6 +1076,10 @@ export declare const selectModeIframeMessageSchema: z.ZodDiscriminatedUnion<[z.Z
1046
1076
  previewUrl: z.ZodNullable<z.ZodString>;
1047
1077
  }, z.core.$strip>>;
1048
1078
  }, z.core.$strip>>;
1079
+ video: z.ZodOptional<z.ZodObject<{
1080
+ src: z.ZodString;
1081
+ currentSrc: z.ZodString;
1082
+ }, z.core.$strip>>;
1049
1083
  definedStyles: z.ZodDefault<z.ZodArray<z.ZodObject<{
1050
1084
  property: z.ZodString;
1051
1085
  variant: z.ZodNullable<z.ZodObject<{
@@ -1059,6 +1093,7 @@ export declare const selectModeIframeMessageSchema: z.ZodDiscriminatedUnion<[z.Z
1059
1093
  raw: z.ZodString;
1060
1094
  resolved: z.ZodString;
1061
1095
  }, z.core.$strip>>>;
1096
+ authoredStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
1062
1097
  inheritedStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
1063
1098
  clickPosition: z.ZodObject<{
1064
1099
  x: z.ZodNumber;
@@ -1225,6 +1260,10 @@ export declare const selectModeIframeMessageSchema: z.ZodDiscriminatedUnion<[z.Z
1225
1260
  previewUrl: z.ZodNullable<z.ZodString>;
1226
1261
  }, z.core.$strip>>;
1227
1262
  }, z.core.$strip>>;
1263
+ video: z.ZodOptional<z.ZodObject<{
1264
+ src: z.ZodString;
1265
+ currentSrc: z.ZodString;
1266
+ }, z.core.$strip>>;
1228
1267
  definedStyles: z.ZodDefault<z.ZodArray<z.ZodObject<{
1229
1268
  property: z.ZodString;
1230
1269
  variant: z.ZodNullable<z.ZodObject<{
@@ -1238,6 +1277,7 @@ export declare const selectModeIframeMessageSchema: z.ZodDiscriminatedUnion<[z.Z
1238
1277
  raw: z.ZodString;
1239
1278
  resolved: z.ZodString;
1240
1279
  }, z.core.$strip>>>;
1280
+ authoredStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
1241
1281
  inheritedStyles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
1242
1282
  clickPosition: z.ZodObject<{
1243
1283
  x: z.ZodNumber;
@@ -1290,8 +1330,8 @@ export declare const selectModeIframeMessageSchema: z.ZodDiscriminatedUnion<[z.Z
1290
1330
  data: z.ZodObject<{
1291
1331
  feedback: z.ZodNullable<z.ZodObject<{
1292
1332
  status: z.ZodEnum<{
1293
- unavailable: "unavailable";
1294
1333
  pending: "pending";
1334
+ unavailable: "unavailable";
1295
1335
  failed: "failed";
1296
1336
  }>;
1297
1337
  rect: z.ZodObject<{
@@ -1,5 +1,5 @@
1
- import { selectModeBoundingRectSchema, selectModeTiptapActiveMarksSchema, selectModeSelectionRectSchema, selectableDataSchema, selectModeHoverRectSchema, selectModeFormatCommandSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionTypeSchema, displayModeSchema, selectModeClickPositionSchema, selectedElementSchema } from '../../chunk-NBVMONRN.mjs';
2
- import '../../chunk-JUKCUPQ6.mjs';
1
+ import { selectModeBoundingRectSchema, selectModeTiptapActiveMarksSchema, selectModeSelectionRectSchema, selectableDataSchema, selectModeHoverRectSchema, selectModeFormatCommandSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionTypeSchema, displayModeSchema, selectModeClickPositionSchema, selectedElementSchema } from '../../chunk-KFJZ2OLV.mjs';
2
+ import '../../chunk-PH3S6LJI.mjs';
3
3
  import '../../chunk-FLYEIRFK.mjs';
4
4
  import '../../chunk-TESFJQID.mjs';
5
5
  import '../../chunk-G3S6ZNPF.mjs';
@@ -1,7 +1,2 @@
1
- /**
2
- * Origin gate for inbound editor-bridge messages, checked BEFORE any payload
3
- * parsing. The select-mode bridge predates this and validates payload shape
4
- * only; its hardening ticket is expected to adopt this helper.
5
- */
6
1
  export declare function isAllowedEditorOrigin(origin: string): boolean;
7
2
  export declare function isAllowedEditorMessage(event: MessageEvent): boolean;
@@ -1,10 +1,11 @@
1
+ import { isMiniappPreviewOrigin } from "../_vendor/replo-utils/lib/miniappPreview.mjs";
1
2
  const ALLOWED_EDITOR_ORIGIN_PATTERNS = [
2
3
  /^https?:\/\/localhost(?::\d+)?$/u,
3
4
  /^https:\/\/(?:[a-z0-9-]+\.)*replo\.app$/u,
4
5
  /^https:\/\/replo-[a-z0-9-]+\.ngrok\.app$/u
5
6
  ];
6
7
  function isAllowedEditorOrigin(origin) {
7
- return ALLOWED_EDITOR_ORIGIN_PATTERNS.some((pattern) => pattern.test(origin));
8
+ return isMiniappPreviewOrigin(origin) || ALLOWED_EDITOR_ORIGIN_PATTERNS.some((pattern) => pattern.test(origin));
8
9
  }
9
10
  function isAllowedEditorMessage(event) {
10
11
  if (typeof window === "undefined" || window.self === window.top) {
package/buildMetadata.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export const BUILD_METADATA = {
2
2
  "packageName": "@replohq/sandbox-runtime",
3
- "version": "1.10.2",
3
+ "version": "1.10.3",
4
4
  "branch": "HEAD",
5
- "commit": "0253f4740d039112c72fe121bf18ea8b609af18b",
6
- "builtAt": "2026-09-27T03:43:50.436Z"
5
+ "commit": "3a43b7ccb4b4143e6b0cbe1c5678c4702deb8b9d",
6
+ "builtAt": "2026-10-07T15:54:30.287Z"
7
7
  };
package/capturePage.d.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  export declare function parseYouTubeId(src: string): string | null;
2
2
  export declare function parseVimeoId(src: string): string | null;
3
+ /** The srcset candidate a browser would pick for `width` CSS pixels. */
4
+ export declare function pickSrcsetCandidate({ srcset, width, pixelRatio, }: {
5
+ srcset: string;
6
+ width: number;
7
+ pixelRatio: number;
8
+ }): string | null;
3
9
  /**
4
10
  * Draws the page's first screen (from the document top, regardless of scroll)
5
11
  * into an exact width×height JPEG. Videos and YouTube/Vimeo embeds, which the
package/capturePage.js CHANGED
@@ -7,6 +7,7 @@ const VIDEO_LOAD_TIMEOUT_MS = 4e3;
7
7
  const VIDEO_SEEK_TIMEOUT_MS = 1500;
8
8
  const PLACEHOLDER_BACKGROUND = "#d9d9d6";
9
9
  const IFRAME_PLACEHOLDER_MARKER = "repeating-linear-gradient(45deg";
10
+ const CAPTURE_SRC_ATTRIBUTE = "data-replo-capture-src";
10
11
  const vimeoOembedSchema = z.object({ thumbnail_url: z.string() });
11
12
  function parseYouTubeId(src) {
12
13
  const match = src.match(
@@ -61,18 +62,96 @@ function videoFrameDataUrl(video) {
61
62
  context.drawImage(video, 0, 0);
62
63
  return canvas.toDataURL("image/jpeg", VIDEO_FRAME_QUALITY);
63
64
  }
64
- function isOnScreen({
65
- element,
65
+ function isInFirstScreen({
66
+ rect,
66
67
  height
67
68
  }) {
68
- const rect = element.getBoundingClientRect();
69
- return rect.bottom > 0 && rect.top < height && rect.width > 0;
69
+ const isRendered = rect.width > 0 || rect.height > 0;
70
+ const isInViewport = rect.bottom > 0 && rect.top < height;
71
+ return isRendered && (isInViewport || rect.top + window.scrollY < height);
72
+ }
73
+ function parseSrcset(srcset) {
74
+ const candidates = [];
75
+ const candidatePattern = /[\s,]*([^\s,][^\s]*)/y;
76
+ let position = 0;
77
+ while (position < srcset.length) {
78
+ candidatePattern.lastIndex = position;
79
+ const match = candidatePattern.exec(srcset);
80
+ if (!match?.[1]) {
81
+ break;
82
+ }
83
+ position = candidatePattern.lastIndex;
84
+ if (match[1].endsWith(",")) {
85
+ candidates.push({ url: match[1].replace(/,+$/, ""), descriptor: "" });
86
+ continue;
87
+ }
88
+ const commaIndex = srcset.indexOf(",", position);
89
+ const descriptorEnd = commaIndex === -1 ? srcset.length : commaIndex;
90
+ candidates.push({
91
+ url: match[1],
92
+ descriptor: srcset.slice(position, descriptorEnd).trim()
93
+ });
94
+ position = descriptorEnd + 1;
95
+ }
96
+ return candidates;
97
+ }
98
+ function pickSrcsetCandidate({
99
+ srcset,
100
+ width,
101
+ pixelRatio
102
+ }) {
103
+ const candidates = parseSrcset(srcset).map(({ url, descriptor }) => {
104
+ const widthMatch = /^(\d+)w$/i.exec(descriptor);
105
+ if (widthMatch) {
106
+ return { url, density: Number(widthMatch[1]) / width };
107
+ }
108
+ const densityMatch = /^(\d*\.?\d+)x$/i.exec(descriptor);
109
+ return { url, density: densityMatch ? Number(densityMatch[1]) : 1 };
110
+ }).sort((left, right) => left.density - right.density);
111
+ const chosen = candidates.find((candidate) => candidate.density >= pixelRatio) ?? candidates.at(-1);
112
+ return chosen?.url ?? null;
113
+ }
114
+ const DECODABLE_IMAGE_TYPE = /^image\/(avif|webp|jpeg|png|gif|svg\+xml)$/i;
115
+ function getActiveSrcset(image) {
116
+ if (image.parentElement?.tagName === "PICTURE") {
117
+ for (const source of image.parentElement.querySelectorAll(
118
+ ":scope > source[srcset]"
119
+ )) {
120
+ const media = source.getAttribute("media");
121
+ const type = source.getAttribute("type");
122
+ if (media && !window.matchMedia(media).matches) {
123
+ continue;
124
+ }
125
+ if (type && !DECODABLE_IMAGE_TYPE.test(type.trim())) {
126
+ continue;
127
+ }
128
+ return source.getAttribute("srcset");
129
+ }
130
+ }
131
+ return image.getAttribute("srcset");
132
+ }
133
+ function getRepairedSource({
134
+ image,
135
+ rect
136
+ }) {
137
+ if (image.currentSrc) {
138
+ return null;
139
+ }
140
+ const srcset = getActiveSrcset(image);
141
+ if (!srcset) {
142
+ return null;
143
+ }
144
+ return pickSrcsetCandidate({
145
+ srcset,
146
+ width: rect.width || window.innerWidth,
147
+ pixelRatio: 1
148
+ });
70
149
  }
71
150
  async function readVideoFrame({
72
151
  video,
73
152
  height
74
153
  }) {
75
- if (!isOnScreen({ element: video, height })) {
154
+ if (!isInFirstScreen({ rect: video.getBoundingClientRect(), height })) {
76
155
  return null;
77
156
  }
78
157
  if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA && isCanvasReadable(video)) {
@@ -148,6 +227,20 @@ function createStandInsPlugin({
148
227
  return;
149
228
  }
150
229
  for (const image of clone.querySelectorAll("img")) {
230
+ const captureSrc = image.getAttribute(CAPTURE_SRC_ATTRIBUTE);
231
+ if (captureSrc) {
232
+ image.removeAttribute(CAPTURE_SRC_ATTRIBUTE);
233
+ image.removeAttribute("srcset");
234
+ image.removeAttribute("sizes");
235
+ image.setAttribute("src", captureSrc);
236
+ if (image.parentElement?.tagName === "PICTURE") {
237
+ for (const source of image.parentElement.querySelectorAll(
238
+ ":scope > source"
239
+ )) {
240
+ source.remove();
241
+ }
242
+ }
243
+ }
151
244
  const objectFit = objectFitByFrame.get(image.getAttribute("src") ?? "");
152
245
  if (objectFit) {
153
246
  image.style.objectFit = objectFit;
@@ -221,10 +314,25 @@ async function capturePage({
221
314
  video.setAttribute("poster", frame);
222
315
  objectFitByFrame.set(frame, getComputedStyle(video).objectFit);
223
316
  });
317
+ const offscreenImages = /* @__PURE__ */ new Set();
318
+ const repairedImages = [];
319
+ for (const image of document.querySelectorAll("img")) {
320
+ const rect = image.getBoundingClientRect();
321
+ if (!isInFirstScreen({ rect, height })) {
322
+ offscreenImages.add(image);
323
+ continue;
324
+ }
325
+ const repairedSource = getRepairedSource({ image, rect });
326
+ if (repairedSource) {
327
+ image.setAttribute(CAPTURE_SRC_ATTRIBUTE, repairedSource);
328
+ repairedImages.push(image);
329
+ }
330
+ }
224
331
  try {
225
332
  const raw = await snapdom.toRaw(document.body, {
226
333
  scale: 1,
227
334
  embedFonts: true,
335
+ exclude: (element) => offscreenImages.has(element),
228
336
  plugins: [createStandInsPlugin({ objectFitByFrame, iframeThumbnails })]
229
337
  });
230
338
  const image = new Image();
@@ -245,6 +353,9 @@ async function capturePage({
245
353
  context.drawImage(image, 0, 0);
246
354
  return await canvasToJpeg(canvas);
247
355
  } finally {
356
+ for (const image of repairedImages) {
357
+ image.removeAttribute(CAPTURE_SRC_ATTRIBUTE);
358
+ }
248
359
  for (const { video, poster } of originalPosters) {
249
360
  if (poster === null) {
250
361
  video.removeAttribute("poster");
@@ -257,5 +368,6 @@ async function capturePage({
257
368
  export {
258
369
  capturePage,
259
370
  parseVimeoId,
260
- parseYouTubeId
371
+ parseYouTubeId,
372
+ pickSrcsetCandidate
261
373
  };
@@ -1,4 +1,4 @@
1
- import { exhaustiveSwitch } from './chunk-JUKCUPQ6.mjs';
1
+ import { exhaustiveSwitch } from './chunk-PH3S6LJI.mjs';
2
2
  import { z } from 'zod';
3
3
 
4
4
  function parseJsonOrNull(json) {
@@ -120,6 +120,10 @@ var selectModeImageSchema = z.object({
120
120
  previewUrl: z.string().nullable(),
121
121
  sources: z.array(selectModeImageSourceSchema)
122
122
  }).describe("SelectModeImage");
123
+ var selectModeVideoSchema = z.object({
124
+ src: z.string(),
125
+ currentSrc: z.string()
126
+ }).describe("SelectModeVideo");
123
127
  var selectedElementSchema = z.object({
124
128
  kind: z.literal("react-element"),
125
129
  selector: z.string(),
@@ -132,7 +136,11 @@ var selectedElementSchema = z.object({
132
136
  textMetrics: selectModeTextMetricsSchema.nullable(),
133
137
  outerHTML: z.string(),
134
138
  image: selectModeImageSchema.optional(),
139
+ video: selectModeVideoSchema.optional(),
135
140
  definedStyles: z.array(selectModeDefinedStyleSchema).default([]),
141
+ authoredStyles: z.record(z.string(), z.string()).default({}).describe(
142
+ "Authored CSS value of the winning declaration per control property, custom properties expanded (e.g. 1.5rem for text-2xl at a wide viewport)."
143
+ ),
136
144
  inheritedStyles: z.record(z.string(), z.string()).default({}),
137
145
  clickPosition: selectModeClickPositionSchema,
138
146
  sourceLocation: selectModeSourceLocationSchema.optional(),
@@ -238,4 +246,4 @@ var selectModeSelectionSchema = z.discriminatedUnion("type", [
238
246
  })
239
247
  ]).describe("SelectModeSelection");
240
248
 
241
- export { SELECT_MODE_SELECTABLE_ATTRIBUTE, SELECT_MODE_SELECTABLE_PAYLOAD_ATTRIBUTE, displayModeSchema, getIframeOffset, isSelectedSection, readSelectablePayload, selectModeBoundingRectSchema, selectModeClickPositionSchema, selectModeDefinedStyleSchema, selectModeFiberChainEntrySchema, selectModeFormatCommandSchema, selectModeHoverRectSchema, selectModeImageSchema, selectModeImageScopeSchema, selectModeImageSourceSchema, selectModeInlineTextStatusSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionRectSchema, selectModeSelectionSchema, selectModeSelectionTypeSchema, selectModeSourceLocationSchema, selectModeStyleAtRuleSchema, selectModeStyleVariantSchema, selectModeTextMetricsSchema, selectModeTiptapActiveMarksSchema, selectableDataPayloadSchema, selectableDataSchema, selectablePlanDataSchema, selectablePlanPayloadSchema, selectedElementSchema, transformPoint, transformRect, transformSelectableData, transformSelectedElement };
249
+ export { SELECT_MODE_SELECTABLE_ATTRIBUTE, SELECT_MODE_SELECTABLE_PAYLOAD_ATTRIBUTE, displayModeSchema, getIframeOffset, isSelectedSection, readSelectablePayload, selectModeBoundingRectSchema, selectModeClickPositionSchema, selectModeDefinedStyleSchema, selectModeFiberChainEntrySchema, selectModeFormatCommandSchema, selectModeHoverRectSchema, selectModeImageSchema, selectModeImageScopeSchema, selectModeImageSourceSchema, selectModeInlineTextStatusSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionRectSchema, selectModeSelectionSchema, selectModeSelectionTypeSchema, selectModeSourceLocationSchema, selectModeStyleAtRuleSchema, selectModeStyleVariantSchema, selectModeTextMetricsSchema, selectModeTiptapActiveMarksSchema, selectModeVideoSchema, selectableDataPayloadSchema, selectableDataSchema, selectablePlanDataSchema, selectablePlanPayloadSchema, selectedElementSchema, transformPoint, transformRect, transformSelectableData, transformSelectedElement };
@@ -1391,6 +1391,26 @@ function formatFileSize(byteCount, decimalPlaces = 0) {
1391
1391
  var sleep = async (ms) => {
1392
1392
  return await new Promise((resolve) => setTimeout(resolve, ms));
1393
1393
  };
1394
+ function sleepWithSignal({
1395
+ delayMs,
1396
+ signal
1397
+ }) {
1398
+ if (signal === null) {
1399
+ return sleep(delayMs);
1400
+ }
1401
+ signal.throwIfAborted();
1402
+ return new Promise((resolve, reject) => {
1403
+ const onAbort = () => {
1404
+ clearTimeout(timeoutId);
1405
+ reject(signal.reason);
1406
+ };
1407
+ const timeoutId = setTimeout(() => {
1408
+ signal.removeEventListener("abort", onAbort);
1409
+ resolve();
1410
+ }, delayMs);
1411
+ signal.addEventListener("abort", onAbort, { once: true });
1412
+ });
1413
+ }
1394
1414
  function errorMessage(error) {
1395
1415
  if (error instanceof Error) {
1396
1416
  return error.message;
@@ -1402,4 +1422,4 @@ function errorMessage(error) {
1402
1422
  return String(error);
1403
1423
  }
1404
1424
 
1405
- export { coerceNumberToString, deepCloneAndMergeReplacingArrays, errorMessage, exhaustiveSwitch, formatFileSize, getFromRecordOrNull, hasOwnProperty12 as hasOwnProperty, invariant, isEmpty2 as isEmpty, isFunction_default, isNotNullish, isNullish, isPrimitive, isString_default, mergeReplacingArrays, mergeReplacingObjects, noop, preventDefault, sleep, warning };
1425
+ export { coerceNumberToString, deepCloneAndMergeReplacingArrays, errorMessage, exhaustiveSwitch, formatFileSize, getFromRecordOrNull, hasOwnProperty12 as hasOwnProperty, invariant, isEmpty2 as isEmpty, isFunction_default, isNotNullish, isNullish, isPrimitive, isString_default, mergeReplacingArrays, mergeReplacingObjects, noop, preventDefault, sleep, sleepWithSignal, warning };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@replohq/sandbox-runtime",
3
- "version": "1.10.2",
3
+ "version": "1.10.3",
4
4
  "reploBuild": {
5
5
  "packageName": "@replohq/sandbox-runtime",
6
- "version": "1.10.2",
6
+ "version": "1.10.3",
7
7
  "branch": "HEAD",
8
- "commit": "0253f4740d039112c72fe121bf18ea8b609af18b",
9
- "builtAt": "2026-09-27T03:43:50.436Z"
8
+ "commit": "3a43b7ccb4b4143e6b0cbe1c5678c4702deb8b9d",
9
+ "builtAt": "2026-10-07T15:54:30.287Z"
10
10
  },
11
11
  "description": "Replo sandbox runtime — editor integration for agent-built Next.js sites.",
12
12
  "license": "SEE LICENSE IN LICENSE",
@@ -3,11 +3,12 @@ import {
3
3
  SELECT_MODE_SELECTABLE_ATTRIBUTE,
4
4
  SELECT_MODE_SELECTABLE_PAYLOAD_ATTRIBUTE
5
5
  } from "../../_vendor/schemas/selectMode.mjs";
6
+ import { z } from "zod";
6
7
  import { getFiberChain } from "../fiber";
7
8
  import { getImageDescriptor } from "../imageSources";
8
9
  import { skipInlineTextMarkElements } from "../inlineTextMarks";
9
10
  import { getElementBoundingRect } from "../interactions/selectionGeometry";
10
- import { getDefinedStyles, getInheritedStyles } from "./styleExtraction";
11
+ import { getElementStyles, getInheritedStyles } from "./styleExtraction";
11
12
  const SECTION_SELECTOR = 'section, article, aside, [role="region"]';
12
13
  const SECTION_LABEL_LENGTH = 60;
13
14
  function normalizeSectionLabel(value) {
@@ -217,11 +218,13 @@ function buildElementDescriptor({
217
218
  if (selectableData) {
218
219
  return selectableData;
219
220
  }
221
+ const video = z.instanceof(HTMLVideoElement).safeParse(element);
220
222
  const textMetrics = getTextMetrics(element);
221
223
  const fiberChain = element instanceof HTMLElement ? getFiberChain(element) : [];
222
224
  const sourceLocation = fiberChain[0]?.sourceLocation;
223
225
  const trimmedText = getRenderedText(element);
224
226
  const sectionLabel = findSectionLabel(section);
227
+ const { definedStyles, authoredStyles } = getElementStyles(element);
225
228
  return {
226
229
  kind: "react-element",
227
230
  selector: buildSelector(element),
@@ -233,7 +236,12 @@ function buildElementDescriptor({
233
236
  textMetrics,
234
237
  outerHTML: element.outerHTML.slice(0, 4e3),
235
238
  image: getImageDescriptor(element),
236
- definedStyles: getDefinedStyles(element),
239
+ video: video.success ? {
240
+ src: video.data.getAttribute("src") ?? "",
241
+ currentSrc: video.data.currentSrc
242
+ } : void 0,
243
+ definedStyles,
244
+ authoredStyles,
237
245
  inheritedStyles: getInheritedStyles(element),
238
246
  clickPosition,
239
247
  sourceLocation,
@@ -1,3 +1,7 @@
1
1
  import type { SelectModeDefinedStyle } from "../../_vendor/schemas/generated/selectMode";
2
+ export declare function getElementStyles(element: Element): {
3
+ definedStyles: SelectModeDefinedStyle[];
4
+ authoredStyles: Record<string, string>;
5
+ };
2
6
  export declare function getDefinedStyles(element: Element): SelectModeDefinedStyle[];
3
7
  export declare function getInheritedStyles(element: Element): Record<string, string>;