@replohq/sandbox-runtime 1.16.1 → 1.17.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 (38) hide show
  1. package/_vendor/replo-utils/lib/historyKeys.d.ts +2 -0
  2. package/_vendor/replo-utils/lib/historyKeys.mjs +18 -0
  3. package/_vendor/replo-utils/lib/viewportSnapshot.d.ts +1 -1
  4. package/_vendor/replo-utils/lib/viewportSnapshot.mjs +1 -1
  5. package/_vendor/replo-utils/react/use-viewport-snapshot-responder.mjs +28 -5222
  6. package/_vendor/schemas/errors.d.ts +3 -43
  7. package/_vendor/schemas/generated/selectModeIframeRpc.d.ts +43 -0
  8. package/_vendor/schemas/iframeRpcChannel.mjs +2 -0
  9. package/_vendor/schemas/reploError.d.ts +43 -0
  10. package/_vendor/schemas/selectModeIframeRpc.d.ts +146 -7
  11. package/_vendor/schemas/selectModeIframeRpc.mjs +73 -10
  12. package/buildMetadata.js +3 -3
  13. package/{chunk-3YA3QXUQ.mjs → chunk-B3PLD7SH.mjs} +29 -18
  14. package/package.json +4 -4
  15. package/rrweb-snapshot-GTQBXO2Y.mjs +5761 -0
  16. package/select-mode/EditorSelection.js +5 -1
  17. package/select-mode/fast-refresh-relay.js +82 -3
  18. package/select-mode/fiber.d.ts +6 -0
  19. package/select-mode/fiber.js +33 -0
  20. package/select-mode/hooks/useParentMessaging.js +10 -3
  21. package/select-mode/hooks/useSelectionHover.js +2 -1
  22. package/select-mode/hooks/useSelectionInteractions.js +10 -0
  23. package/select-mode/iframeRpcChannel.d.ts +45 -7
  24. package/select-mode/iframeRpcChannel.js +9 -8
  25. package/select-mode/section-canvas/SectionCanvas.d.ts +7 -0
  26. package/select-mode/section-canvas/SectionCanvas.js +599 -0
  27. package/select-mode/section-canvas/optimisticSectionMove.d.ts +39 -0
  28. package/select-mode/section-canvas/optimisticSectionMove.js +172 -0
  29. package/select-mode/section-canvas/sectionCanvasControls.d.ts +3 -0
  30. package/select-mode/section-canvas/sectionCanvasControls.js +8 -0
  31. package/select-mode/section-canvas/sectionCanvasStore.d.ts +71 -0
  32. package/select-mode/section-canvas/sectionCanvasStore.js +275 -0
  33. package/select-mode/section-canvas/sectionElements.d.ts +16 -0
  34. package/select-mode/section-canvas/sectionElements.js +94 -0
  35. package/select-mode/section-canvas/sectionGhost.d.ts +5 -0
  36. package/select-mode/section-canvas/sectionGhost.js +23 -0
  37. package/select-mode/symbolicate.d.ts +2 -0
  38. package/select-mode/symbolicate.js +18 -1
@@ -8,6 +8,8 @@ import { useSelectionHover } from "./hooks/useSelectionHover";
8
8
  import { useSelectionInteractions } from "./hooks/useSelectionInteractions";
9
9
  import { useSelectionScrollSync } from "./hooks/useSelectionScrollSync";
10
10
  import { PREVIEW_RENDERED_EVENT } from "./previewRendered";
11
+ import { SectionCanvas } from "./section-canvas/SectionCanvas";
12
+ import { sectionCanvasStore } from "./section-canvas/sectionCanvasStore";
11
13
  import { SelectionFrames } from "./SelectionFrames";
12
14
  import {
13
15
  useDisplayMode,
@@ -90,6 +92,7 @@ function EditorSelection() {
90
92
  onComplete: () => {
91
93
  reapplyOverrides({ skipElement: store.getState().editingElement });
92
94
  watchRender();
95
+ sectionCanvasStore.getState().actions.handlePreviewRefresh();
93
96
  }
94
97
  });
95
98
  window.addEventListener(PREVIEW_RENDERED_EVENT, watchRender);
@@ -151,7 +154,8 @@ function EditorSelection() {
151
154
  [data-select-mode-inline-editor][contenteditable="true"],
152
155
  [data-select-mode-inline-editor][contenteditable="true"] * { cursor: text !important; }
153
156
  ` }),
154
- /* @__PURE__ */ jsx(SelectionFrames, {})
157
+ /* @__PURE__ */ jsx(SelectionFrames, {}),
158
+ /* @__PURE__ */ jsx(SectionCanvas, {})
155
159
  ] });
156
160
  }
157
161
  export {
@@ -1,5 +1,8 @@
1
1
  import { getWebsiteBuilderRpc } from "./iframeRpcChannel";
2
+ import { SECTION_CANVAS_ATTRIBUTE } from "./section-canvas/sectionCanvasControls";
2
3
  import { invalidateSymbolicationCache } from "./symbolicate";
4
+ const SERVER_REFRESH_HEADER = "next-hmr-refresh";
5
+ const SERVER_REFRESH_COMMIT_TIMEOUT_MS = 2e3;
3
6
  let hasInstalledFastRefreshRelay = false;
4
7
  let onFastRefreshComplete = null;
5
8
  let lastLatencyMs = null;
@@ -11,6 +14,7 @@ function installFastRefreshRelay({
11
14
  return;
12
15
  }
13
16
  hasInstalledFastRefreshRelay = true;
17
+ installServerRefreshRelay();
14
18
  const originalCallback = window.__NEXT_HMR_LATENCY_CB;
15
19
  window.__NEXT_HMR_LATENCY_CB = (latencyMs) => {
16
20
  const isRepeat = latencyMs === lastLatencyMs;
@@ -19,12 +23,87 @@ function installFastRefreshRelay({
19
23
  originalCallback?.(latencyMs);
20
24
  return;
21
25
  }
22
- invalidateSymbolicationCache();
23
- getWebsiteBuilderRpc()?.fastRefreshComplete(void 0);
24
- onFastRefreshComplete?.();
26
+ reportRefreshComplete();
25
27
  originalCallback?.(latencyMs);
26
28
  };
27
29
  }
30
+ function reportRefreshComplete() {
31
+ invalidateSymbolicationCache();
32
+ getWebsiteBuilderRpc()?.fastRefreshComplete(void 0);
33
+ onFastRefreshComplete?.();
34
+ }
35
+ function isServerRefreshRequest(input, init) {
36
+ try {
37
+ return new Headers(init?.headers).has(SERVER_REFRESH_HEADER) || input instanceof Request && input.headers.has(SERVER_REFRESH_HEADER);
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+ function nextReactCommit() {
43
+ const hook = Reflect.get(
44
+ window,
45
+ "__REACT_DEVTOOLS_GLOBAL_HOOK__"
46
+ );
47
+ if (!hook || typeof hook.onCommitFiberRoot !== "function") {
48
+ return null;
49
+ }
50
+ return new Promise((resolve) => {
51
+ const original = hook.onCommitFiberRoot;
52
+ hook.onCommitFiberRoot = function relayCommit(...args) {
53
+ hook.onCommitFiberRoot = original;
54
+ resolve();
55
+ return original?.apply(this, args);
56
+ };
57
+ });
58
+ }
59
+ function nextPageMutation() {
60
+ let observer = null;
61
+ const promise = new Promise((resolve) => {
62
+ observer = new MutationObserver((records) => {
63
+ const isPageChange = records.some((record) => {
64
+ const element = record.target instanceof Element ? record.target : record.target.parentElement;
65
+ return element !== null && document.body.contains(element) && !element.closest(`[${SECTION_CANVAS_ATTRIBUTE}]`);
66
+ });
67
+ if (isPageChange) {
68
+ observer?.disconnect();
69
+ resolve();
70
+ }
71
+ });
72
+ observer.observe(document.documentElement, {
73
+ childList: true,
74
+ subtree: true,
75
+ characterData: true
76
+ });
77
+ });
78
+ return { promise, cancel: () => observer?.disconnect() };
79
+ }
80
+ function installServerRefreshRelay() {
81
+ const originalFetch = window.fetch;
82
+ window.fetch = function relayServerRefresh(input, init) {
83
+ const response = originalFetch.call(this, input, init);
84
+ if (isServerRefreshRequest(input, init)) {
85
+ void response.then((received) => received.clone().arrayBuffer()).then(
86
+ async () => {
87
+ const commit = nextReactCommit();
88
+ const mutation = commit ? null : nextPageMutation();
89
+ let timeout;
90
+ await Promise.race([
91
+ commit ?? mutation?.promise,
92
+ new Promise((resolve) => {
93
+ timeout = setTimeout(resolve, SERVER_REFRESH_COMMIT_TIMEOUT_MS);
94
+ })
95
+ ]);
96
+ mutation?.cancel();
97
+ clearTimeout(timeout);
98
+ reportRefreshComplete();
99
+ },
100
+ () => {
101
+ }
102
+ );
103
+ }
104
+ return response;
105
+ };
106
+ }
28
107
  export {
29
108
  installFastRefreshRelay
30
109
  };
@@ -48,4 +48,10 @@ export declare function getFiberChain(element: HTMLElement): {
48
48
  componentName: string | undefined;
49
49
  props: Record<string, unknown> | undefined;
50
50
  }[];
51
+ /**
52
+ * JSX call sites from an element outward (its own, then each owner's), still compiled. Skips
53
+ * `resolveCurrentFiber`'s tree scan: a call site is the same in both fiber generations, and this
54
+ * runs for every element near the top of the page.
55
+ */
56
+ export declare function getElementSourceChain(element: HTMLElement, maxEntries?: number): DebugSource[];
51
57
  export {};
@@ -493,9 +493,42 @@ function getFiberChain(element) {
493
493
  }
494
494
  return fiberChain;
495
495
  }
496
+ function getElementSourceChain(element, maxEntries = 16) {
497
+ const chain = [];
498
+ const seen = /* @__PURE__ */ new Set();
499
+ const seenOwners = /* @__PURE__ */ new Set();
500
+ const push = (source) => {
501
+ if (!source) {
502
+ return;
503
+ }
504
+ const key = `${source.fileName}:${source.lineNumber}:${source.columnNumber ?? ""}`;
505
+ if (!seen.has(key)) {
506
+ seen.add(key);
507
+ chain.push(source);
508
+ }
509
+ };
510
+ let fiber = getFiberFromElement(element);
511
+ let depth = 0;
512
+ while (fiber && depth < MAX_FIBER_CHAIN_DEPTH && chain.length < maxEntries) {
513
+ push(parseDebugStackSource(fiber._debugStack));
514
+ let owner = fiber._debugOwner;
515
+ while (owner && !seenOwners.has(owner) && chain.length < maxEntries) {
516
+ seenOwners.add(owner);
517
+ if (!("name" in owner)) {
518
+ break;
519
+ }
520
+ push(parseDebugStackSource(owner.debugStack));
521
+ owner = owner.owner;
522
+ }
523
+ fiber = fiber.return;
524
+ depth += 1;
525
+ }
526
+ return chain;
527
+ }
496
528
  export {
497
529
  findDebugSource,
498
530
  getComponentName,
531
+ getElementSourceChain,
499
532
  getFiberChain,
500
533
  getFiberFromElement,
501
534
  getRepetitionHostNodes,
@@ -1,6 +1,9 @@
1
1
  import { useEffect, useMemo } from "react";
2
2
  import { useIframeRpcChannel } from "../../_vendor/replo-utils/react/use-iframe-rpc-channel.mjs";
3
- import { iframeRuntimeBuildMetadataSchema } from "../../_vendor/schemas/selectModeIframeRpc.mjs";
3
+ import {
4
+ iframeRuntimeBuildMetadataSchema,
5
+ SECTION_CANVAS_CAPABILITY
6
+ } from "../../_vendor/schemas/selectModeIframeRpc.mjs";
4
7
  import * as z from "zod";
5
8
  import { BUILD_METADATA } from "../../buildMetadata";
6
9
  import {
@@ -9,6 +12,7 @@ import {
9
12
  setSelectModeParentHandlers
10
13
  } from "../iframeRpcChannel";
11
14
  const SDK_BUILD_METADATA_SPECIFIER = "@replohq/sdk/lib/buildMetadata";
15
+ const RUNTIME_CAPABILITIES = [SECTION_CANVAS_CAPABILITY];
12
16
  const sdkBuildMetadataModuleSchema = z.object({
13
17
  BUILD_METADATA: iframeRuntimeBuildMetadataSchema
14
18
  });
@@ -18,7 +22,10 @@ async function announceSdkBuildMetadata() {
18
22
  await import(SDK_BUILD_METADATA_SPECIFIER)
19
23
  );
20
24
  if (sdkBuildMetadata.success) {
21
- getWebsiteBuilderRpc()?.iframeReady(sdkBuildMetadata.data.BUILD_METADATA);
25
+ getWebsiteBuilderRpc()?.iframeReady({
26
+ ...sdkBuildMetadata.data.BUILD_METADATA,
27
+ capabilities: RUNTIME_CAPABILITIES
28
+ });
22
29
  }
23
30
  } catch {
24
31
  }
@@ -78,7 +85,7 @@ function useParentMessaging({
78
85
  }
79
86
  });
80
87
  const rpc = getWebsiteBuilderRpc();
81
- rpc?.iframeReady(BUILD_METADATA);
88
+ rpc?.iframeReady({ ...BUILD_METADATA, capabilities: RUNTIME_CAPABILITIES });
82
89
  void announceSdkBuildMetadata();
83
90
  return () => {
84
91
  setSelectModeParentHandlers(null);
@@ -6,6 +6,7 @@ import {
6
6
  findContainingSection
7
7
  } from "../interactions/sectionGeometry";
8
8
  import { getElementBoundingRect } from "../interactions/selectionGeometry";
9
+ import { isSectionCanvasControl } from "../section-canvas/sectionCanvasControls";
9
10
  import { useDisplayMode, useSelectModeStore } from "../selectModeStore";
10
11
  function useSelectionHover() {
11
12
  const store = useSelectModeStore();
@@ -54,7 +55,7 @@ function useSelectionHover() {
54
55
  hoveredElement = hoveredElementStack[hoveredElementStack.length - 1] ?? null;
55
56
  }
56
57
  postHoveredElement(
57
- hoveredElement ? getNearestSelectableElement(hoveredElement) : null
58
+ hoveredElement && !isSectionCanvasControl(hoveredElement) ? getNearestSelectableElement(hoveredElement) : null
58
59
  );
59
60
  };
60
61
  const scheduleHoverSync = () => {
@@ -20,6 +20,7 @@ import {
20
20
  unionElementPageRects
21
21
  } from "../interactions/selectionGeometry";
22
22
  import { isTextEditableElement } from "../isTextEditableElement";
23
+ import { isSectionCanvasControl } from "../section-canvas/sectionCanvasControls";
23
24
  import { isSelectionStillCurrent } from "../selectionPublish";
24
25
  import {
25
26
  getConnectedSelectedElement,
@@ -172,6 +173,9 @@ function useSelectionInteractions() {
172
173
  );
173
174
  };
174
175
  const handlePointerDown = (event) => {
176
+ if (isSectionCanvasControl(event.target)) {
177
+ return;
178
+ }
175
179
  if (event.button !== 0) {
176
180
  return;
177
181
  }
@@ -362,6 +366,9 @@ function useSelectionInteractions() {
362
366
  });
363
367
  };
364
368
  const handlePointerUp = (event) => {
369
+ if (isSectionCanvasControl(event.target)) {
370
+ return;
371
+ }
365
372
  if (event.button !== 0) {
366
373
  return;
367
374
  }
@@ -419,6 +426,9 @@ function useSelectionInteractions() {
419
426
  });
420
427
  };
421
428
  const handleClick = (event) => {
429
+ if (isSectionCanvasControl(event.target)) {
430
+ return;
431
+ }
422
432
  if (editingElementRef?.current && event.target instanceof Node && editingElementRef.current.contains(event.target)) {
423
433
  return;
424
434
  }
@@ -2,11 +2,54 @@ import type { SelectedElement } from "../_vendor/schemas/generated/selectMode";
2
2
  import type { IframeRpcChannelInputHandlers, IframeRpcChannelRemote } from "../_vendor/schemas/iframeRpcChannel";
3
3
  import { previewIframeToWebsiteBuilderMethodConfig, websiteBuilderToPreviewIframeMethodConfig } from "../_vendor/schemas/selectModeIframeRpc";
4
4
  export type WebsiteBuilderRpcRemote = IframeRpcChannelRemote<typeof previewIframeToWebsiteBuilderMethodConfig>;
5
- type SelectModeParentMessagingHandlers = Omit<IframeRpcChannelInputHandlers<typeof websiteBuilderToPreviewIframeMethodConfig>, "symbolicateSelectedElement" | "navigateToRoute">;
5
+ type SelectModeParentMessagingHandlers = Omit<IframeRpcChannelInputHandlers<typeof websiteBuilderToPreviewIframeMethodConfig>, "symbolicateSelectedElement" | "navigateToRoute" | "setSectionCanvas" | "settleSectionMove">;
6
6
  export declare function setNavigateToRouteHandler(handler: ((input: {
7
7
  pathname: string;
8
8
  }) => void) | null): void;
9
9
  export declare function getSelectModeIframeRpcChannel(): import("../_vendor/schemas/iframeRpcChannel").IframeRpcChannel<{
10
+ sectionCanvasRequest: {
11
+ inputSchema: import("zod").ZodObject<{
12
+ request: import("zod").ZodDiscriminatedUnion<[import("zod").ZodObject<{
13
+ type: import("zod").ZodLiteral<"insert">;
14
+ at: import("zod").ZodNumber;
15
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
16
+ type: import("zod").ZodLiteral<"move">;
17
+ moveId: import("zod").ZodString;
18
+ index: import("zod").ZodNumber;
19
+ to: import("zod").ZodNumber;
20
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
21
+ type: import("zod").ZodLiteral<"remove">;
22
+ index: import("zod").ZodNumber;
23
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
24
+ type: import("zod").ZodLiteral<"undo">;
25
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
26
+ type: import("zod").ZodLiteral<"redo">;
27
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
28
+ type: import("zod").ZodLiteral<"cancelInsert">;
29
+ }, import("zod/v4/core").$strip>], "type">;
30
+ }, import("zod/v4/core").$strip>;
31
+ outputSchema: import("zod").ZodVoid;
32
+ };
33
+ sectionCanvasLayoutChanged: {
34
+ inputSchema: import("zod").ZodObject<{
35
+ sections: import("zod").ZodArray<import("zod").ZodObject<{
36
+ index: import("zod").ZodNumber;
37
+ startLine: import("zod").ZodNumber;
38
+ endLine: import("zod").ZodNumber;
39
+ rect: import("zod").ZodObject<{
40
+ x: import("zod").ZodNumber;
41
+ y: import("zod").ZodNumber;
42
+ left: import("zod").ZodNumber;
43
+ top: import("zod").ZodNumber;
44
+ right: import("zod").ZodNumber;
45
+ bottom: import("zod").ZodNumber;
46
+ width: import("zod").ZodNumber;
47
+ height: import("zod").ZodNumber;
48
+ }, import("zod/v4/core").$strip>;
49
+ }, import("zod/v4/core").$strip>>;
50
+ }, import("zod/v4/core").$strip>;
51
+ outputSchema: import("zod").ZodVoid;
52
+ };
10
53
  requestInlineTextEdit: {
11
54
  inputSchema: import("zod").ZodObject<{
12
55
  editId: import("zod").ZodString;
@@ -105,6 +148,7 @@ export declare function getSelectModeIframeRpcChannel(): import("../_vendor/sche
105
148
  branch: import("zod").ZodNullable<import("zod").ZodString>;
106
149
  commit: import("zod").ZodNullable<import("zod").ZodString>;
107
150
  builtAt: import("zod").ZodNullable<import("zod").ZodString>;
151
+ capabilities: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
108
152
  }, import("zod/v4/core").$strip>;
109
153
  outputSchema: import("zod").ZodVoid;
110
154
  };
@@ -446,12 +490,6 @@ export declare function getSelectModeIframeRpcChannel(): import("../_vendor/sche
446
490
  inputSchema: import("zod").ZodUndefined;
447
491
  outputSchema: import("zod").ZodVoid;
448
492
  };
449
- navigationAccepted: {
450
- inputSchema: import("zod").ZodObject<{
451
- navigationId: import("zod").ZodNumber;
452
- }, import("zod/v4/core").$strip>;
453
- outputSchema: import("zod").ZodVoid;
454
- };
455
493
  }> | null;
456
494
  export declare function setSelectModeParentHandlers(nextHandlers: SelectModeParentMessagingHandlers | null): void;
457
495
  export declare function getWebsiteBuilderRpc(): WebsiteBuilderRpcRemote | null;
@@ -8,6 +8,7 @@ import {
8
8
  SELECT_MODE_IFRAME_RPC_CHANNEL_VERSION,
9
9
  websiteBuilderToPreviewIframeMethodConfig
10
10
  } from "../_vendor/schemas/selectModeIframeRpc.mjs";
11
+ import { sectionCanvasStore } from "./section-canvas/sectionCanvasStore";
11
12
  import { symbolicateSelectedElement } from "./symbolicate";
12
13
  let messagingHandlers = null;
13
14
  let navigateToRouteHandler = null;
@@ -54,17 +55,17 @@ function createSelectModeIframeRpcChannel() {
54
55
  formatCommand: (input) => {
55
56
  messagingHandlers?.formatCommand(input);
56
57
  },
58
+ setSectionCanvas: ({ canvas }) => {
59
+ sectionCanvasStore.getState().actions.setCanvas(canvas);
60
+ },
61
+ settleSectionMove: (input) => {
62
+ sectionCanvasStore.getState().actions.settleMove(input);
63
+ },
57
64
  symbolicateSelectedElement: (element) => {
58
65
  return symbolicateSelectedElement(element);
59
66
  },
60
- navigateToRoute: ({ pathname, navigationId }, { channel: channel2 }) => {
61
- if (!navigateToRouteHandler) {
62
- return;
63
- }
64
- navigateToRouteHandler({ pathname });
65
- if (navigationId !== void 0) {
66
- channel2.navigationAccepted({ navigationId });
67
- }
67
+ navigateToRoute: (input) => {
68
+ navigateToRouteHandler?.(input);
68
69
  }
69
70
  }
70
71
  });
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Section controls drawn on the preview, after Squarespace's editor: the hovered or selected section
3
+ * gets a bold outline with an "Add Section" pill on each edge, a grip that drags it to a new place
4
+ * (with a picture of it and a drop line), and a trash button. Every edit is a request to the
5
+ * Website Builder, which owns the source.
6
+ */
7
+ export declare function SectionCanvas(): import("react").ReactPortal | null;