@replohq/sandbox-runtime 1.7.5 → 1.7.7
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/ClientErrorBoundary.d.ts +3 -1
- package/ClientErrorBoundary.js +10 -2
- package/buildMetadata.js +3 -3
- package/next/component-preview-client.js +40 -20
- package/package.json +4 -4
- package/sandbox-runtime.js +1 -1
- package/select-mode/EditorSelection.js +30 -10
- package/select-mode/previewRendered.d.ts +1 -0
- package/select-mode/previewRendered.js +4 -0
package/ClientErrorBoundary.d.ts
CHANGED
|
@@ -7,4 +7,6 @@ import * as React from "react";
|
|
|
7
7
|
* vs. an actionable error screen); the embedder reloads the iframe once the error
|
|
8
8
|
* clears.
|
|
9
9
|
*/
|
|
10
|
-
export declare function ClientErrorBoundary({ children }: React.PropsWithChildren
|
|
10
|
+
export declare function ClientErrorBoundary({ children, resetKey, }: React.PropsWithChildren<{
|
|
11
|
+
resetKey: React.ReactNode;
|
|
12
|
+
}>): import("react/jsx-runtime").JSX.Element;
|
package/ClientErrorBoundary.js
CHANGED
|
@@ -11,6 +11,11 @@ class ErrorBoundary extends React.Component {
|
|
|
11
11
|
componentDidCatch(error, info) {
|
|
12
12
|
this.props.onError?.(error, info);
|
|
13
13
|
}
|
|
14
|
+
componentDidUpdate(previousProps, previousState) {
|
|
15
|
+
if (this.state.didCatch && previousState.didCatch && !Object.is(previousProps.resetKey, this.props.resetKey)) {
|
|
16
|
+
this.setState({ didCatch: false });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
14
19
|
render() {
|
|
15
20
|
if (this.state.didCatch) {
|
|
16
21
|
return /* @__PURE__ */ jsx(
|
|
@@ -28,7 +33,10 @@ class ErrorBoundary extends React.Component {
|
|
|
28
33
|
return this.props.children;
|
|
29
34
|
}
|
|
30
35
|
}
|
|
31
|
-
function ClientErrorBoundary({
|
|
36
|
+
function ClientErrorBoundary({
|
|
37
|
+
children,
|
|
38
|
+
resetKey
|
|
39
|
+
}) {
|
|
32
40
|
const handleError = React.useCallback(
|
|
33
41
|
(error, errorInfo) => {
|
|
34
42
|
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
@@ -49,7 +57,7 @@ function ClientErrorBoundary({ children }) {
|
|
|
49
57
|
},
|
|
50
58
|
[]
|
|
51
59
|
);
|
|
52
|
-
return /* @__PURE__ */ jsx(ErrorBoundary, { onError: handleError, children });
|
|
60
|
+
return /* @__PURE__ */ jsx(ErrorBoundary, { onError: handleError, resetKey, children });
|
|
53
61
|
}
|
|
54
62
|
export {
|
|
55
63
|
ClientErrorBoundary
|
package/buildMetadata.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const BUILD_METADATA = {
|
|
2
2
|
"packageName": "@replohq/sandbox-runtime",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.7",
|
|
4
4
|
"branch": "HEAD",
|
|
5
|
-
"commit": "
|
|
6
|
-
"builtAt": "2026-09-
|
|
5
|
+
"commit": "480d34ab39b9a190492b6d1280cc9a8d9a89e2ab",
|
|
6
|
+
"builtAt": "2026-09-18T21:38:59.533Z"
|
|
7
7
|
};
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
createIframeRpcChannel,
|
|
15
15
|
createIframeRpcRemote
|
|
16
16
|
} from "../_vendor/schemas/iframeRpcChannel.mjs";
|
|
17
|
+
import { PREVIEW_RENDERED_EVENT } from "../select-mode/previewRendered";
|
|
17
18
|
import { getComponentPreviewHref } from "./component-preview-href";
|
|
18
19
|
import {
|
|
19
20
|
mergeClientProps,
|
|
@@ -38,6 +39,7 @@ function getCurrentHref() {
|
|
|
38
39
|
function useComponentPreviewChannel(onSetProps) {
|
|
39
40
|
const onSetPropsRef = useRef(onSetProps);
|
|
40
41
|
onSetPropsRef.current = onSetProps;
|
|
42
|
+
const pendingUpdates = useRef(Promise.resolve());
|
|
41
43
|
useIframeRpcChannel(
|
|
42
44
|
useMemo(() => {
|
|
43
45
|
if (typeof window === "undefined") {
|
|
@@ -61,7 +63,20 @@ function useComponentPreviewChannel(onSetProps) {
|
|
|
61
63
|
outgoingMethods: componentPreviewToWebsiteBuilderMethodConfig,
|
|
62
64
|
incomingMethods: websiteBuilderToComponentPreviewMethodConfig,
|
|
63
65
|
handlers: {
|
|
64
|
-
setProps: (input) =>
|
|
66
|
+
setProps: (input) => {
|
|
67
|
+
const result = pendingUpdates.current.then(async () => {
|
|
68
|
+
const result2 = await onSetPropsRef.current(input);
|
|
69
|
+
window.dispatchEvent(new Event(PREVIEW_RENDERED_EVENT));
|
|
70
|
+
return result2;
|
|
71
|
+
});
|
|
72
|
+
pendingUpdates.current = result.then(
|
|
73
|
+
() => {
|
|
74
|
+
},
|
|
75
|
+
() => {
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
65
80
|
}
|
|
66
81
|
});
|
|
67
82
|
return {
|
|
@@ -109,30 +124,28 @@ function useComponentPreviewChannel(onSetProps) {
|
|
|
109
124
|
}, [])
|
|
110
125
|
);
|
|
111
126
|
}
|
|
112
|
-
function
|
|
113
|
-
const
|
|
114
|
-
const [isNavigationPending, startNavigation] = useTransition();
|
|
127
|
+
function useCommitPreviewUpdate() {
|
|
128
|
+
const [isUpdatePending, startUpdate] = useTransition();
|
|
115
129
|
const settleResolvers = useRef([]);
|
|
116
130
|
useEffect(() => {
|
|
117
|
-
if (!
|
|
131
|
+
if (!isUpdatePending) {
|
|
118
132
|
const resolvers = settleResolvers.current;
|
|
119
133
|
settleResolvers.current = [];
|
|
120
134
|
for (const resolve of resolvers) {
|
|
121
135
|
resolve();
|
|
122
136
|
}
|
|
123
137
|
}
|
|
124
|
-
}, [
|
|
125
|
-
return (
|
|
138
|
+
}, [isUpdatePending]);
|
|
139
|
+
return (update) => {
|
|
126
140
|
return new Promise((resolve) => {
|
|
127
141
|
settleResolvers.current.push(resolve);
|
|
128
|
-
|
|
129
|
-
router.replace(href, { scroll: false });
|
|
130
|
-
});
|
|
142
|
+
startUpdate(update);
|
|
131
143
|
});
|
|
132
144
|
};
|
|
133
145
|
}
|
|
134
146
|
function ComponentPreviewNavigation() {
|
|
135
|
-
const
|
|
147
|
+
const router = useRouter();
|
|
148
|
+
const commitPreviewUpdate = useCommitPreviewUpdate();
|
|
136
149
|
useComponentPreviewChannel(
|
|
137
150
|
async ({ props, sourceRevision }) => {
|
|
138
151
|
const nextHref = getComponentPreviewHref({
|
|
@@ -143,7 +156,9 @@ function ComponentPreviewNavigation() {
|
|
|
143
156
|
if (nextHref === getCurrentHref()) {
|
|
144
157
|
return { mode: "unchanged" };
|
|
145
158
|
}
|
|
146
|
-
await
|
|
159
|
+
await commitPreviewUpdate(() => {
|
|
160
|
+
return router.replace(nextHref, { scroll: false });
|
|
161
|
+
});
|
|
147
162
|
return { mode: "navigation" };
|
|
148
163
|
}
|
|
149
164
|
);
|
|
@@ -165,7 +180,8 @@ function ComponentPreviewStage({
|
|
|
165
180
|
propsRef.current = props;
|
|
166
181
|
const lastRawPropsRef = useRef(initialRawProps);
|
|
167
182
|
const lastSourceRevisionRef = useRef(initialSourceRevision);
|
|
168
|
-
const
|
|
183
|
+
const router = useRouter();
|
|
184
|
+
const commitPreviewUpdate = useCommitPreviewUpdate();
|
|
169
185
|
useComponentPreviewChannel(
|
|
170
186
|
async ({
|
|
171
187
|
props: nextRawProps,
|
|
@@ -183,17 +199,21 @@ function ComponentPreviewStage({
|
|
|
183
199
|
previous: lastRawPropsRef.current,
|
|
184
200
|
next: nextRawProps
|
|
185
201
|
})) {
|
|
186
|
-
await
|
|
202
|
+
await commitPreviewUpdate(() => {
|
|
203
|
+
return router.replace(nextHref, { scroll: false });
|
|
204
|
+
});
|
|
187
205
|
lastRawPropsRef.current = nextRawProps;
|
|
188
206
|
lastSourceRevisionRef.current = sourceRevision;
|
|
189
207
|
return { mode: "navigation" };
|
|
190
208
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
209
|
+
await commitPreviewUpdate(() => {
|
|
210
|
+
return setProps(
|
|
211
|
+
mergeClientProps({
|
|
212
|
+
currentResolved: propsRef.current,
|
|
213
|
+
nextRaw: nextRawProps
|
|
214
|
+
})
|
|
215
|
+
);
|
|
216
|
+
});
|
|
197
217
|
lastRawPropsRef.current = nextRawProps;
|
|
198
218
|
window.history.replaceState(null, "", nextHref);
|
|
199
219
|
return { mode: "client" };
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@replohq/sandbox-runtime",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.7",
|
|
4
4
|
"reploBuild": {
|
|
5
5
|
"packageName": "@replohq/sandbox-runtime",
|
|
6
|
-
"version": "1.7.
|
|
6
|
+
"version": "1.7.7",
|
|
7
7
|
"branch": "HEAD",
|
|
8
|
-
"commit": "
|
|
9
|
-
"builtAt": "2026-09-
|
|
8
|
+
"commit": "480d34ab39b9a190492b6d1280cc9a8d9a89e2ab",
|
|
9
|
+
"builtAt": "2026-09-18T21:38:59.533Z"
|
|
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
|
@@ -19,7 +19,7 @@ function SandboxRuntime({ children }) {
|
|
|
19
19
|
return isDevServer ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
20
20
|
/* @__PURE__ */ jsx(DocumentReadyBeacon, {}),
|
|
21
21
|
/* @__PURE__ */ jsx(NextErrorReporter, {}),
|
|
22
|
-
/* @__PURE__ */ jsxs(ClientErrorBoundary, { children: [
|
|
22
|
+
/* @__PURE__ */ jsxs(ClientErrorBoundary, { resetKey: children, children: [
|
|
23
23
|
/* @__PURE__ */ jsx(LinkInterceptor, {}),
|
|
24
24
|
/* @__PURE__ */ jsx(NavigationReceiver, {}),
|
|
25
25
|
/* @__PURE__ */ jsx(PostHogRelay, {}),
|
|
@@ -15,7 +15,10 @@ import { useSelectionScrollSync } from "./hooks/useSelectionScrollSync";
|
|
|
15
15
|
import { useSelectModeController } from "./hooks/useSelectModeController";
|
|
16
16
|
import { getRepetitionSiblingElements } from "./hooks/useSiblingResolution";
|
|
17
17
|
import { getWebsiteBuilderRpc } from "./iframeRpcChannel";
|
|
18
|
-
import {
|
|
18
|
+
import { getDocumentScrollOffset } from "./interactions/selectionGeometry";
|
|
19
|
+
import { PREVIEW_RENDERED_EVENT } from "./previewRendered";
|
|
20
|
+
import { showElementSelectionFrames } from "./selectionFrame";
|
|
21
|
+
import { getConnectedSelectedElement } from "./selectModeSession";
|
|
19
22
|
import { symbolicateSelectedElement } from "./symbolicate";
|
|
20
23
|
function EditorSelection() {
|
|
21
24
|
const {
|
|
@@ -40,9 +43,13 @@ function EditorSelection() {
|
|
|
40
43
|
if (session.type !== "element") {
|
|
41
44
|
return;
|
|
42
45
|
}
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
const element = getConnectedSelectedElement(session);
|
|
47
|
+
if (!element) {
|
|
48
|
+
setSelectionType({ selectionType: "none" });
|
|
49
|
+
getWebsiteBuilderRpc()?.areaSelected({
|
|
50
|
+
rect: session.data.rect,
|
|
51
|
+
items: []
|
|
52
|
+
});
|
|
46
53
|
return;
|
|
47
54
|
}
|
|
48
55
|
const siblingElements = getRepetitionSiblingElements({ element });
|
|
@@ -54,22 +61,24 @@ function EditorSelection() {
|
|
|
54
61
|
if (descriptor.kind !== "react-element") {
|
|
55
62
|
return;
|
|
56
63
|
}
|
|
57
|
-
|
|
64
|
+
const refreshedSession = {
|
|
58
65
|
...session,
|
|
59
66
|
element,
|
|
60
67
|
siblingElements,
|
|
61
68
|
data: descriptor
|
|
62
69
|
};
|
|
70
|
+
interactionSessionRef.current = refreshedSession;
|
|
71
|
+
showElementSelectionFrames({
|
|
72
|
+
elements: [element, ...siblingElements],
|
|
73
|
+
scrollOffset: getDocumentScrollOffset()
|
|
74
|
+
});
|
|
63
75
|
void symbolicateSelectedElement(descriptor).then((symbolicated) => {
|
|
64
|
-
if (
|
|
65
|
-
session: interactionSessionRef.current,
|
|
66
|
-
selection: { type: "element", element }
|
|
67
|
-
})) {
|
|
76
|
+
if (interactionSessionRef.current !== refreshedSession) {
|
|
68
77
|
return;
|
|
69
78
|
}
|
|
70
79
|
getWebsiteBuilderRpc()?.elementSelected({ data: symbolicated });
|
|
71
80
|
});
|
|
72
|
-
}, [interactionSessionRef]);
|
|
81
|
+
}, [interactionSessionRef, setSelectionType]);
|
|
73
82
|
useEffect(() => {
|
|
74
83
|
installFastRefreshRelay({
|
|
75
84
|
onComplete: () => {
|
|
@@ -77,6 +86,17 @@ function EditorSelection() {
|
|
|
77
86
|
postSelectedElementSnapshot();
|
|
78
87
|
}
|
|
79
88
|
});
|
|
89
|
+
window.addEventListener(
|
|
90
|
+
PREVIEW_RENDERED_EVENT,
|
|
91
|
+
postSelectedElementSnapshot
|
|
92
|
+
);
|
|
93
|
+
return () => {
|
|
94
|
+
window.removeEventListener(
|
|
95
|
+
PREVIEW_RENDERED_EVENT,
|
|
96
|
+
postSelectedElementSnapshot
|
|
97
|
+
);
|
|
98
|
+
installFastRefreshRelay();
|
|
99
|
+
};
|
|
80
100
|
}, [editingElementRef, postSelectedElementSnapshot, reapplyOverrides]);
|
|
81
101
|
useParentMessaging({
|
|
82
102
|
applyOptimisticUpdate,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PREVIEW_RENDERED_EVENT = "replo:preview-rendered";
|