@replohq/sandbox-runtime 1.1.0 → 1.3.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/LinkInterceptor.js +39 -15
- package/LinkInterceptor.js.map +2 -2
- package/NavigationReceiver.d.ts +1 -0
- package/NavigationReceiver.js +25 -0
- package/NavigationReceiver.js.map +7 -0
- package/_vendor/schemas/selectMode.d.ts +9 -2
- package/_vendor/schemas/selectMode.mjs +1 -1
- package/_vendor/schemas/selectModeIframeRpc.d.ts +6 -0
- package/_vendor/schemas/selectModeIframeRpc.mjs +6 -1
- package/buildMetadata.js +3 -3
- package/{chunk-P5NWJM64.mjs → chunk-2455EG5R.mjs} +31 -21
- package/package.json +4 -4
- package/sandbox-runtime.js +2 -0
- package/sandbox-runtime.js.map +2 -2
- package/select-mode/displayModeStore.js.map +1 -1
- package/select-mode/iframeRpcChannel.d.ts +4 -1
- package/select-mode/iframeRpcChannel.js +8 -0
- package/select-mode/iframeRpcChannel.js.map +2 -2
package/LinkInterceptor.js
CHANGED
|
@@ -42,6 +42,24 @@ function LinkInterceptor() {
|
|
|
42
42
|
const originalPushState = history.pushState;
|
|
43
43
|
const originalReplaceState = history.replaceState;
|
|
44
44
|
const navigation = getWindowNavigation();
|
|
45
|
+
const reportNavigated = (rawUrl) => {
|
|
46
|
+
let route;
|
|
47
|
+
try {
|
|
48
|
+
route = new URL(
|
|
49
|
+
typeof rawUrl === "string" ? rawUrl : rawUrl.href,
|
|
50
|
+
window.location.href
|
|
51
|
+
).pathname;
|
|
52
|
+
} catch {
|
|
53
|
+
route = String(rawUrl);
|
|
54
|
+
}
|
|
55
|
+
window.parent.postMessage(
|
|
56
|
+
{
|
|
57
|
+
type: "replo:navigated",
|
|
58
|
+
route
|
|
59
|
+
},
|
|
60
|
+
"*"
|
|
61
|
+
);
|
|
62
|
+
};
|
|
45
63
|
window.open = function(url, target, features) {
|
|
46
64
|
if (url !== void 0 && url !== "" && urlLeavesSandbox(url) === true) {
|
|
47
65
|
return null;
|
|
@@ -52,13 +70,21 @@ function LinkInterceptor() {
|
|
|
52
70
|
if (url && urlLeavesSandbox(url) === true) {
|
|
53
71
|
return;
|
|
54
72
|
}
|
|
55
|
-
|
|
73
|
+
const result = originalPushState.call(history, state, unused, url);
|
|
74
|
+
if (url) {
|
|
75
|
+
reportNavigated(url);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
56
78
|
};
|
|
57
79
|
history.replaceState = function(state, unused, url) {
|
|
58
80
|
if (url && urlLeavesSandbox(url) === true) {
|
|
59
81
|
return;
|
|
60
82
|
}
|
|
61
|
-
|
|
83
|
+
const result = originalReplaceState.call(history, state, unused, url);
|
|
84
|
+
if (url) {
|
|
85
|
+
reportNavigated(url);
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
62
88
|
};
|
|
63
89
|
const handleNavigate = (event) => {
|
|
64
90
|
const destinationUrl = event.destination?.url;
|
|
@@ -95,20 +121,18 @@ function LinkInterceptor() {
|
|
|
95
121
|
blockNavigation(event);
|
|
96
122
|
return;
|
|
97
123
|
}
|
|
98
|
-
event.
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
route = new URL(href, window.location.href).pathname;
|
|
102
|
-
} catch {
|
|
103
|
-
route = href;
|
|
124
|
+
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
|
125
|
+
return;
|
|
104
126
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
127
|
+
if (link.hasAttribute("download")) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
queueMicrotask(() => {
|
|
131
|
+
if (event.defaultPrevented) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
reportNavigated(href);
|
|
135
|
+
});
|
|
112
136
|
};
|
|
113
137
|
navigation?.addEventListener("navigate", handleNavigate);
|
|
114
138
|
document.addEventListener("click", handleClick, true);
|
package/LinkInterceptor.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../LinkInterceptor.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\n\nimport { useEffect } from \"react\";\n\nimport { displayModeStore } from \"./select-mode/displayModeStore\";\n\ntype NavigationNavigateEvent = {\n destination?: { url?: string };\n preventDefault?: () => void;\n};\n\ntype NavigationLike = {\n addEventListener: (\n type: \"navigate\",\n listener: (event: NavigationNavigateEvent) => void,\n ) => void;\n removeEventListener: (\n type: \"navigate\",\n listener: (event: NavigationNavigateEvent) => void,\n ) => void;\n};\n\nfunction blockNavigation(event: MouseEvent) {\n event.preventDefault();\n event.stopPropagation();\n event.stopImmediatePropagation();\n}\n\nfunction isNavigationLike(value: unknown): value is NavigationLike {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n return (\n typeof Reflect.get(value, \"addEventListener\") === \"function\" &&\n typeof Reflect.get(value, \"removeEventListener\") === \"function\"\n );\n}\n\nfunction getWindowNavigation() {\n const navigation = Reflect.get(window, \"navigation\");\n return isNavigationLike(navigation) ? navigation : null;\n}\n\nfunction shouldInterceptNavigationForAgentPreview() {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n if (window.parent === window) {\n return false;\n }\n\n return (\n new URLSearchParams(window.location.search).get(\"__reploAgentPreview\") ===\n \"1\"\n );\n}\n\nfunction urlLeavesSandbox(rawUrl: string | URL): boolean | null {\n try {\n const resolved =\n typeof rawUrl === \"string\"\n ? new URL(rawUrl, window.location.href)\n : new URL(rawUrl.href, window.location.href);\n return resolved.origin !== window.location.origin;\n } catch {\n return null;\n }\n}\n\nexport default function LinkInterceptor() {\n // eslint-disable-next-line replo/no-use-effect -- Browser navigation APIs must be patched at client runtime and restored when the interceptor unmounts.\n useEffect(() => {\n if (!shouldInterceptNavigationForAgentPreview()) {\n return;\n }\n\n const originalWindowOpen = window.open;\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n const navigation = getWindowNavigation();\n\n window.open = function (\n url?: string | URL,\n target?: string,\n features?: string,\n ) {\n if (url !== undefined && url !== \"\" && urlLeavesSandbox(url) === true) {\n return null;\n }\n\n return originalWindowOpen.call(window, url, target, features);\n };\n\n history.pushState = function (\n state: Parameters<History[\"pushState\"]>[0],\n unused: string,\n url?: string | URL | null,\n ) {\n if (url && urlLeavesSandbox(url) === true) {\n return;\n }\n\n
|
|
5
|
-
"mappings": ";AAEA,SAAS,iBAAiB;AAE1B,SAAS,wBAAwB;AAkBjC,SAAS,gBAAgB,OAAmB;AAC1C,QAAM,eAAe;AACrB,QAAM,gBAAgB;AACtB,QAAM,yBAAyB;AACjC;AAEA,SAAS,iBAAiB,OAAyC;AACjE,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO;AAAA,EACT;AAEA,SACE,OAAO,QAAQ,IAAI,OAAO,kBAAkB,MAAM,cAClD,OAAO,QAAQ,IAAI,OAAO,qBAAqB,MAAM;AAEzD;AAEA,SAAS,sBAAsB;AAC7B,QAAM,aAAa,QAAQ,IAAI,QAAQ,YAAY;AACnD,SAAO,iBAAiB,UAAU,IAAI,aAAa;AACrD;AAEA,SAAS,2CAA2C;AAClD,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,WAAW,QAAQ;AAC5B,WAAO;AAAA,EACT;AAEA,SACE,IAAI,gBAAgB,OAAO,SAAS,MAAM,EAAE,IAAI,qBAAqB,MACrE;AAEJ;AAEA,SAAS,iBAAiB,QAAsC;AAC9D,MAAI;AACF,UAAM,WACJ,OAAO,WAAW,WACd,IAAI,IAAI,QAAQ,OAAO,SAAS,IAAI,IACpC,IAAI,IAAI,OAAO,MAAM,OAAO,SAAS,IAAI;AAC/C,WAAO,SAAS,WAAW,OAAO,SAAS;AAAA,EAC7C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEe,SAAR,kBAAmC;AAExC,YAAU,MAAM;AACd,QAAI,CAAC,yCAAyC,GAAG;AAC/C;AAAA,IACF;AAEA,UAAM,qBAAqB,OAAO;AAClC,UAAM,oBAAoB,QAAQ;AAClC,UAAM,uBAAuB,QAAQ;AACrC,UAAM,aAAa,oBAAoB;
|
|
4
|
+
"sourcesContent": ["\"use client\";\n\nimport { useEffect } from \"react\";\n\nimport { displayModeStore } from \"./select-mode/displayModeStore\";\n\ntype NavigationNavigateEvent = {\n destination?: { url?: string };\n preventDefault?: () => void;\n};\n\ntype NavigationLike = {\n addEventListener: (\n type: \"navigate\",\n listener: (event: NavigationNavigateEvent) => void,\n ) => void;\n removeEventListener: (\n type: \"navigate\",\n listener: (event: NavigationNavigateEvent) => void,\n ) => void;\n};\n\nfunction blockNavigation(event: MouseEvent) {\n event.preventDefault();\n event.stopPropagation();\n event.stopImmediatePropagation();\n}\n\nfunction isNavigationLike(value: unknown): value is NavigationLike {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n return (\n typeof Reflect.get(value, \"addEventListener\") === \"function\" &&\n typeof Reflect.get(value, \"removeEventListener\") === \"function\"\n );\n}\n\nfunction getWindowNavigation() {\n const navigation = Reflect.get(window, \"navigation\");\n return isNavigationLike(navigation) ? navigation : null;\n}\n\nfunction shouldInterceptNavigationForAgentPreview() {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n if (window.parent === window) {\n return false;\n }\n\n return (\n new URLSearchParams(window.location.search).get(\"__reploAgentPreview\") ===\n \"1\"\n );\n}\n\nfunction urlLeavesSandbox(rawUrl: string | URL): boolean | null {\n try {\n const resolved =\n typeof rawUrl === \"string\"\n ? new URL(rawUrl, window.location.href)\n : new URL(rawUrl.href, window.location.href);\n return resolved.origin !== window.location.origin;\n } catch {\n return null;\n }\n}\n\nexport default function LinkInterceptor() {\n // eslint-disable-next-line replo/no-use-effect -- Browser navigation APIs must be patched at client runtime and restored when the interceptor unmounts.\n useEffect(() => {\n if (!shouldInterceptNavigationForAgentPreview()) {\n return;\n }\n\n const originalWindowOpen = window.open;\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n const navigation = getWindowNavigation();\n\n // Tell the parent editor where the site's own navigation went so it can\n // keep its URL, page list, and select-mode in sync without reloading the\n // iframe. A fact, not a request: it is only sent once a navigation is\n // actually happening.\n const reportNavigated = (rawUrl: string | URL) => {\n let route: string;\n try {\n route = new URL(\n typeof rawUrl === \"string\" ? rawUrl : rawUrl.href,\n window.location.href,\n ).pathname;\n } catch {\n route = String(rawUrl);\n }\n window.parent.postMessage(\n {\n type: \"replo:navigated\",\n route,\n },\n \"*\",\n );\n };\n\n window.open = function (\n url?: string | URL,\n target?: string,\n features?: string,\n ) {\n if (url !== undefined && url !== \"\" && urlLeavesSandbox(url) === true) {\n return null;\n }\n\n return originalWindowOpen.call(window, url, target, features);\n };\n\n // The history patches are where client-side navigations are reported:\n // next/link cancels its click and routes through pushState, so the click\n // handler below cannot know whether the site actually navigated \u2014 these\n // patches fire exactly when it did (link clicks and programmatic\n // router.push/redirects alike).\n history.pushState = function (\n state: Parameters<History[\"pushState\"]>[0],\n unused: string,\n url?: string | URL | null,\n ) {\n if (url && urlLeavesSandbox(url) === true) {\n return;\n }\n\n const result = originalPushState.call(history, state, unused, url);\n if (url) {\n reportNavigated(url);\n }\n return result;\n };\n\n history.replaceState = function (\n state: Parameters<History[\"replaceState\"]>[0],\n unused: string,\n url?: string | URL | null,\n ) {\n if (url && urlLeavesSandbox(url) === true) {\n return;\n }\n\n const result = originalReplaceState.call(history, state, unused, url);\n if (url) {\n reportNavigated(url);\n }\n return result;\n };\n\n const handleNavigate = (event: NavigationNavigateEvent) => {\n const destinationUrl = event.destination?.url;\n if (\n destinationUrl === undefined ||\n destinationUrl === \"\" ||\n urlLeavesSandbox(destinationUrl) !== true\n ) {\n return;\n }\n\n event.preventDefault?.();\n };\n\n const handleClick = (event: MouseEvent) => {\n // While select mode is active a link click must select the element, not\n // navigate. `useSelectionInteractions` prevents the default nav, but this\n // sibling capture listener would still post `replo:navigate` and move the\n // preview off the page \u2014 `stopPropagation` there can't suppress a\n // listener on the same node, so we bail out here explicitly.\n if (displayModeStore.getSnapshot() === \"selection\") {\n return;\n }\n\n const target = event.target;\n\n if (!(target instanceof Element)) {\n return;\n }\n\n const link = target.closest(\"a[href]\");\n\n if (!(link instanceof HTMLAnchorElement)) {\n return;\n }\n\n const href = link.getAttribute(\"href\");\n if (!href) {\n return;\n }\n\n if (href.startsWith(\"#\")) {\n return;\n }\n\n const linkTarget = link.getAttribute(\"target\");\n if (linkTarget && linkTarget !== \"_self\") {\n blockNavigation(event);\n return;\n }\n\n if (urlLeavesSandbox(href) === true) {\n blockNavigation(event);\n return;\n }\n\n // A modified click (new tab / etc.) is the browser's to handle \u2014 the\n // iframe sandbox has no `allow-popups`, so it can't escape anyway.\n // Don't hijack it or tell the parent we navigated.\n if (\n event.button !== 0 ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey\n ) {\n return;\n }\n\n // A download link never navigates, so there is nothing to report.\n if (link.hasAttribute(\"download\")) {\n return;\n }\n\n // Let the site navigate itself \u2014 next/link does an instant client-side\n // transition, exactly like a normal Next.js app \u2014 instead of cancelling\n // the click and making the parent editor tear down and reload the whole\n // preview iframe. next/link navigations are reported by the pushState\n // patch above; this deferred report covers plain anchors the browser\n // itself navigates (a full document load inside the iframe). Deferred a\n // microtask because this capture listener runs before the site's own\n // handlers: a cancelled click (next/link included) navigates nothing.\n queueMicrotask(() => {\n if (event.defaultPrevented) {\n return;\n }\n reportNavigated(href);\n });\n };\n\n navigation?.addEventListener(\"navigate\", handleNavigate);\n document.addEventListener(\"click\", handleClick, true);\n\n return () => {\n window.open = originalWindowOpen;\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n navigation?.removeEventListener(\"navigate\", handleNavigate);\n document.removeEventListener(\"click\", handleClick, true);\n };\n }, []);\n\n return null;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function NavigationReceiver(): null;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useMemo } from "react";
|
|
3
|
+
import { useRouter } from "next/navigation";
|
|
4
|
+
import { useIframeRpcChannel } from "./_vendor/replo-utils/react/use-iframe-rpc-channel.mjs";
|
|
5
|
+
import {
|
|
6
|
+
getSelectModeIframeRpcChannel,
|
|
7
|
+
setNavigateToRouteHandler
|
|
8
|
+
} from "./select-mode/iframeRpcChannel";
|
|
9
|
+
function NavigationReceiver() {
|
|
10
|
+
const router = useRouter();
|
|
11
|
+
useIframeRpcChannel(useMemo(() => getSelectModeIframeRpcChannel(), []));
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
setNavigateToRouteHandler(({ pathname }) => {
|
|
14
|
+
router.push(pathname);
|
|
15
|
+
});
|
|
16
|
+
return () => {
|
|
17
|
+
setNavigateToRouteHandler(null);
|
|
18
|
+
};
|
|
19
|
+
}, [router]);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
NavigationReceiver
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=NavigationReceiver.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../NavigationReceiver.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\";\n\nimport { useEffect, useMemo } from \"react\";\n\nimport { useRouter } from \"next/navigation\";\n\nimport { useIframeRpcChannel } from \"replo-utils/react/use-iframe-rpc-channel\";\n\nimport {\n getSelectModeIframeRpcChannel,\n setNavigateToRouteHandler,\n} from \"./select-mode/iframeRpcChannel\";\n\n// Hands the editor's `navigateToRoute` commands to the app router; `LinkInterceptor` reports the resulting navigation up.\nexport function NavigationReceiver() {\n const router = useRouter();\n useIframeRpcChannel(useMemo(() => getSelectModeIframeRpcChannel(), []));\n\n // eslint-disable-next-line replo/no-use-effect -- RPC handler registration tied to the router's lifetime.\n useEffect(() => {\n setNavigateToRouteHandler(({ pathname }) => {\n router.push(pathname);\n });\n return () => {\n setNavigateToRouteHandler(null);\n };\n }, [router]);\n\n return null;\n}\n"],
|
|
5
|
+
"mappings": ";AAEA,SAAS,WAAW,eAAe;AAEnC,SAAS,iBAAiB;AAE1B,SAAS,2BAA2B;AAEpC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAGA,SAAS,qBAAqB;AACnC,QAAM,SAAS,UAAU;AACzB,sBAAoB,QAAQ,MAAM,8BAA8B,GAAG,CAAC,CAAC,CAAC;AAGtE,YAAU,MAAM;AACd,8BAA0B,CAAC,EAAE,SAAS,MAAM;AAC1C,aAAO,KAAK,QAAQ;AAAA,IACtB,CAAC;AACD,WAAO,MAAM;AACX,gCAA0B,IAAI;AAAA,IAChC;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -87,13 +87,15 @@ export type RectOffset = {
|
|
|
87
87
|
left: number;
|
|
88
88
|
top: number;
|
|
89
89
|
};
|
|
90
|
-
export declare function transformRect({ rect, offset, }: {
|
|
90
|
+
export declare function transformRect({ rect, offset, scale, }: {
|
|
91
91
|
rect: SelectModeBoundingRect;
|
|
92
92
|
offset: RectOffset;
|
|
93
|
+
scale?: number;
|
|
93
94
|
}): SelectModeBoundingRect;
|
|
94
|
-
export declare function transformPoint({ point, offset, }: {
|
|
95
|
+
export declare function transformPoint({ point, offset, scale, }: {
|
|
95
96
|
point: SelectModeClickPosition;
|
|
96
97
|
offset: RectOffset;
|
|
98
|
+
scale?: number;
|
|
97
99
|
}): SelectModeClickPosition;
|
|
98
100
|
export declare function getIframeOffset(iframe: HTMLIFrameElement | null): RectOffset;
|
|
99
101
|
export declare const selectedElementSchema: z.ZodObject<{
|
|
@@ -257,6 +259,11 @@ export type SelectModeBoundingRect = z.infer<typeof selectModeBoundingRectSchema
|
|
|
257
259
|
export type SelectModeClickPosition = z.infer<typeof selectModeClickPositionSchema>;
|
|
258
260
|
export type SelectableData = z.infer<typeof selectableDataSchema>;
|
|
259
261
|
export type SelectableDataPayload = z.infer<typeof selectableDataPayloadSchema>;
|
|
262
|
+
export declare function transformSelectedElement({ element, offset, scale, }: {
|
|
263
|
+
element: z.infer<typeof selectedElementSchema>;
|
|
264
|
+
offset: RectOffset;
|
|
265
|
+
scale?: number;
|
|
266
|
+
}): z.infer<typeof selectedElementSchema>;
|
|
260
267
|
export declare function transformSelectableData({ data, offset, }: {
|
|
261
268
|
data: SelectableData;
|
|
262
269
|
offset: RectOffset;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { SELECT_MODE_SELECTABLE_ATTRIBUTE, SELECT_MODE_SELECTABLE_PAYLOAD_ATTRIBUTE, displayModeSchema, getIframeOffset, readSelectablePayload, selectModeBoundingRectSchema, selectModeClickPositionSchema, selectModeDefinedStyleSchema, selectModeFiberChainEntrySchema, selectModeFormatCommandSchema, selectModeInlineTextStatusSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionRectSchema, selectModeSelectionSchema, selectModeSelectionTypeSchema, selectModeSourceLocationSchema, selectModeStyleAtRuleSchema, selectModeStyleVariantSchema, selectModeTextMetricsSchema, selectModeTiptapActiveMarksSchema, selectableDataPayloadSchema, selectableDataSchema, selectablePlanDataSchema, selectablePlanPayloadSchema, selectedElementSchema, transformPoint, transformRect, transformSelectableData } from '../../chunk-
|
|
1
|
+
export { SELECT_MODE_SELECTABLE_ATTRIBUTE, SELECT_MODE_SELECTABLE_PAYLOAD_ATTRIBUTE, displayModeSchema, getIframeOffset, readSelectablePayload, selectModeBoundingRectSchema, selectModeClickPositionSchema, selectModeDefinedStyleSchema, selectModeFiberChainEntrySchema, selectModeFormatCommandSchema, selectModeInlineTextStatusSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionRectSchema, selectModeSelectionSchema, selectModeSelectionTypeSchema, selectModeSourceLocationSchema, selectModeStyleAtRuleSchema, selectModeStyleVariantSchema, selectModeTextMetricsSchema, selectModeTiptapActiveMarksSchema, selectableDataPayloadSchema, selectableDataSchema, selectablePlanDataSchema, selectablePlanPayloadSchema, selectedElementSchema, transformPoint, transformRect, transformSelectableData, transformSelectedElement } from '../../chunk-2455EG5R.mjs';
|
|
2
2
|
import '../../chunk-N5EI2MOZ.mjs';
|
|
3
3
|
import '../../chunk-FNTGY7I5.mjs';
|
|
@@ -131,6 +131,12 @@ export declare const websiteBuilderToCanopyMethodConfig: {
|
|
|
131
131
|
}, z.core.$strip>;
|
|
132
132
|
timeoutMs: number;
|
|
133
133
|
};
|
|
134
|
+
navigateToRoute: {
|
|
135
|
+
inputSchema: z.ZodObject<{
|
|
136
|
+
pathname: z.ZodString;
|
|
137
|
+
}, z.core.$strip>;
|
|
138
|
+
outputSchema: z.ZodVoid;
|
|
139
|
+
};
|
|
134
140
|
setDisplayMode: {
|
|
135
141
|
inputSchema: z.ZodObject<{
|
|
136
142
|
displayMode: z.ZodEnum<{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { selectModeFormatCommandSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionTypeSchema, displayModeSchema, selectModeTiptapActiveMarksSchema, selectModeBoundingRectSchema, selectableDataSchema, selectModeSelectionRectSchema, selectModeClickPositionSchema, selectedElementSchema } from '../../chunk-
|
|
1
|
+
import { selectModeFormatCommandSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionTypeSchema, displayModeSchema, selectModeTiptapActiveMarksSchema, selectModeBoundingRectSchema, selectableDataSchema, selectModeSelectionRectSchema, selectModeClickPositionSchema, selectedElementSchema } from '../../chunk-2455EG5R.mjs';
|
|
2
2
|
import '../../chunk-N5EI2MOZ.mjs';
|
|
3
3
|
import '../../chunk-FNTGY7I5.mjs';
|
|
4
4
|
import { z } from 'zod';
|
|
@@ -18,6 +18,11 @@ var websiteBuilderToCanopyMethodConfig = {
|
|
|
18
18
|
outputSchema: selectedElementSchema,
|
|
19
19
|
timeoutMs: 1500
|
|
20
20
|
},
|
|
21
|
+
// Client-side transition command; fire-and-forget because success is observed as the `replo:navigated` echo.
|
|
22
|
+
navigateToRoute: {
|
|
23
|
+
inputSchema: z.object({ pathname: z.string() }),
|
|
24
|
+
outputSchema: z.void()
|
|
25
|
+
},
|
|
21
26
|
setDisplayMode: {
|
|
22
27
|
inputSchema: z.object({ displayMode: displayModeSchema }),
|
|
23
28
|
outputSchema: z.void()
|
package/buildMetadata.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const BUILD_METADATA = {
|
|
2
2
|
"packageName": "@replohq/sandbox-runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"branch": "HEAD",
|
|
5
|
-
"commit": "
|
|
6
|
-
"builtAt": "2026-08-
|
|
5
|
+
"commit": "544d5eef4f0142da5e8569baa4832a525d11fc06",
|
|
6
|
+
"builtAt": "2026-08-26T18:10:31.971Z"
|
|
7
7
|
};
|
|
@@ -71,26 +71,28 @@ var selectModeDefinedStyleSchema = z.object({
|
|
|
71
71
|
}).describe("SelectModeDefinedStyle");
|
|
72
72
|
function transformRect({
|
|
73
73
|
rect,
|
|
74
|
-
offset
|
|
74
|
+
offset,
|
|
75
|
+
scale = 1
|
|
75
76
|
}) {
|
|
76
77
|
return {
|
|
77
|
-
x: rect.x + offset.left,
|
|
78
|
-
y: rect.y + offset.top,
|
|
79
|
-
left: rect.left + offset.left,
|
|
80
|
-
right: rect.right + offset.left,
|
|
81
|
-
top: rect.top + offset.top,
|
|
82
|
-
bottom: rect.bottom + offset.top,
|
|
83
|
-
width: rect.width,
|
|
84
|
-
height: rect.height
|
|
78
|
+
x: rect.x * scale + offset.left,
|
|
79
|
+
y: rect.y * scale + offset.top,
|
|
80
|
+
left: rect.left * scale + offset.left,
|
|
81
|
+
right: rect.right * scale + offset.left,
|
|
82
|
+
top: rect.top * scale + offset.top,
|
|
83
|
+
bottom: rect.bottom * scale + offset.top,
|
|
84
|
+
width: rect.width * scale,
|
|
85
|
+
height: rect.height * scale
|
|
85
86
|
};
|
|
86
87
|
}
|
|
87
88
|
function transformPoint({
|
|
88
89
|
point,
|
|
89
|
-
offset
|
|
90
|
+
offset,
|
|
91
|
+
scale = 1
|
|
90
92
|
}) {
|
|
91
93
|
return {
|
|
92
|
-
x: point.x + offset.left,
|
|
93
|
-
y: point.y + offset.top
|
|
94
|
+
x: point.x * scale + offset.left,
|
|
95
|
+
y: point.y * scale + offset.top
|
|
94
96
|
};
|
|
95
97
|
}
|
|
96
98
|
function getIframeOffset(iframe) {
|
|
@@ -125,6 +127,21 @@ var selectablePlanDataSchema = z.object({
|
|
|
125
127
|
var selectablePlanPayloadSchema = selectablePlanDataSchema.omit({ rect: true }).describe("SelectablePlanPayload");
|
|
126
128
|
var selectableDataPayloadSchema = z.discriminatedUnion("kind", [selectablePlanPayloadSchema]).describe("SelectableDataPayload");
|
|
127
129
|
var selectableDataSchema = z.discriminatedUnion("kind", [selectedElementSchema, selectablePlanDataSchema]).describe("SelectableData");
|
|
130
|
+
function transformSelectedElement({
|
|
131
|
+
element,
|
|
132
|
+
offset,
|
|
133
|
+
scale = 1
|
|
134
|
+
}) {
|
|
135
|
+
return {
|
|
136
|
+
...element,
|
|
137
|
+
rect: transformRect({ rect: element.rect, offset, scale }),
|
|
138
|
+
clickPosition: transformPoint({
|
|
139
|
+
point: element.clickPosition,
|
|
140
|
+
offset,
|
|
141
|
+
scale
|
|
142
|
+
})
|
|
143
|
+
};
|
|
144
|
+
}
|
|
128
145
|
function transformSelectableData({
|
|
129
146
|
data,
|
|
130
147
|
offset
|
|
@@ -133,14 +150,7 @@ function transformSelectableData({
|
|
|
133
150
|
data,
|
|
134
151
|
"kind"
|
|
135
152
|
)({
|
|
136
|
-
"react-element": (element) => ({
|
|
137
|
-
...element,
|
|
138
|
-
rect: transformRect({ rect: element.rect, offset }),
|
|
139
|
-
clickPosition: transformPoint({
|
|
140
|
-
point: element.clickPosition,
|
|
141
|
-
offset
|
|
142
|
-
})
|
|
143
|
-
}),
|
|
153
|
+
"react-element": (element) => transformSelectedElement({ element, offset }),
|
|
144
154
|
plan: (plan) => ({
|
|
145
155
|
...plan,
|
|
146
156
|
rect: transformRect({ rect: plan.rect, offset })
|
|
@@ -202,4 +212,4 @@ var selectModeSelectionSchema = z.discriminatedUnion("type", [
|
|
|
202
212
|
})
|
|
203
213
|
]).describe("SelectModeSelection");
|
|
204
214
|
|
|
205
|
-
export { SELECT_MODE_SELECTABLE_ATTRIBUTE, SELECT_MODE_SELECTABLE_PAYLOAD_ATTRIBUTE, displayModeSchema, getIframeOffset, readSelectablePayload, selectModeBoundingRectSchema, selectModeClickPositionSchema, selectModeDefinedStyleSchema, selectModeFiberChainEntrySchema, selectModeFormatCommandSchema, selectModeInlineTextStatusSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionRectSchema, selectModeSelectionSchema, selectModeSelectionTypeSchema, selectModeSourceLocationSchema, selectModeStyleAtRuleSchema, selectModeStyleVariantSchema, selectModeTextMetricsSchema, selectModeTiptapActiveMarksSchema, selectableDataPayloadSchema, selectableDataSchema, selectablePlanDataSchema, selectablePlanPayloadSchema, selectedElementSchema, transformPoint, transformRect, transformSelectableData };
|
|
215
|
+
export { SELECT_MODE_SELECTABLE_ATTRIBUTE, SELECT_MODE_SELECTABLE_PAYLOAD_ATTRIBUTE, displayModeSchema, getIframeOffset, readSelectablePayload, selectModeBoundingRectSchema, selectModeClickPositionSchema, selectModeDefinedStyleSchema, selectModeFiberChainEntrySchema, selectModeFormatCommandSchema, selectModeInlineTextStatusSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionRectSchema, selectModeSelectionSchema, selectModeSelectionTypeSchema, selectModeSourceLocationSchema, selectModeStyleAtRuleSchema, selectModeStyleVariantSchema, selectModeTextMetricsSchema, selectModeTiptapActiveMarksSchema, selectableDataPayloadSchema, selectableDataSchema, selectablePlanDataSchema, selectablePlanPayloadSchema, selectedElementSchema, transformPoint, transformRect, transformSelectableData, transformSelectedElement };
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@replohq/sandbox-runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"reploBuild": {
|
|
5
5
|
"packageName": "@replohq/sandbox-runtime",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.3.0",
|
|
7
7
|
"branch": "HEAD",
|
|
8
|
-
"commit": "
|
|
9
|
-
"builtAt": "2026-08-
|
|
8
|
+
"commit": "544d5eef4f0142da5e8569baa4832a525d11fc06",
|
|
9
|
+
"builtAt": "2026-08-26T18:10:31.971Z"
|
|
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 { ClientErrorBoundary } from "./ClientErrorBoundary";
|
|
|
5
5
|
import { DesignLibraryPreview } from "./design-library-preview/DesignLibraryPreview";
|
|
6
6
|
import { DocumentReadyBeacon } from "./DocumentReadyBeacon";
|
|
7
7
|
import LinkInterceptor from "./LinkInterceptor";
|
|
8
|
+
import { NavigationReceiver } from "./NavigationReceiver";
|
|
8
9
|
import { NextErrorReporter } from "./NextErrorReporter";
|
|
9
10
|
import PostHogRelay from "./posthog-relay";
|
|
10
11
|
import { EditorSelection } from "./select-mode/EditorSelection";
|
|
@@ -15,6 +16,7 @@ function SandboxRuntime({ children }) {
|
|
|
15
16
|
/* @__PURE__ */ jsx(NextErrorReporter, {}),
|
|
16
17
|
/* @__PURE__ */ jsxs(ClientErrorBoundary, { children: [
|
|
17
18
|
/* @__PURE__ */ jsx(LinkInterceptor, {}),
|
|
19
|
+
/* @__PURE__ */ jsx(NavigationReceiver, {}),
|
|
18
20
|
/* @__PURE__ */ jsx(PostHogRelay, {}),
|
|
19
21
|
/* @__PURE__ */ jsx(EditorSelection, {}),
|
|
20
22
|
/* @__PURE__ */ jsx(AnnotationLayerMount, {}),
|
package/sandbox-runtime.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../sandbox-runtime.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\n\nimport { AnnotationLayerMount } from \"./annotations/AnnotationLayerMount\";\nimport { ClientErrorBoundary } from \"./ClientErrorBoundary\";\nimport { DesignLibraryPreview } from \"./design-library-preview/DesignLibraryPreview\";\nimport { DocumentReadyBeacon } from \"./DocumentReadyBeacon\";\nimport LinkInterceptor from \"./LinkInterceptor\";\nimport { NextErrorReporter } from \"./NextErrorReporter\";\nimport PostHogRelay from \"./posthog-relay\";\nimport { EditorSelection } from \"./select-mode/EditorSelection\";\n\nexport function SandboxRuntime({ children }: React.PropsWithChildren) {\n // The editor only embeds dev servers (`next dev`), so editor-integration components render there only \u2014 production builds ship none of them and behave like plain Next apps.\n const isDevServer = process.env.NODE_ENV === \"development\";\n\n return isDevServer ? (\n <>\n <DocumentReadyBeacon />\n {/* NOTE (Max, 2026-06-08): Outside ClientErrorBoundary on purpose \u2014 when\n the boundary catches a runtime error it swaps its children for a\n neutral cover, which would unmount the reporter and freeze it in the\n error state. It must stay mounted to observe Next's overlay across\n error <-> recover. */}\n <NextErrorReporter />\n <ClientErrorBoundary>\n <LinkInterceptor />\n <PostHogRelay />\n <EditorSelection />\n <AnnotationLayerMount />\n <DesignLibraryPreview />\n {children}\n </ClientErrorBoundary>\n </>\n ) : (\n children\n );\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\";\n\nimport { AnnotationLayerMount } from \"./annotations/AnnotationLayerMount\";\nimport { ClientErrorBoundary } from \"./ClientErrorBoundary\";\nimport { DesignLibraryPreview } from \"./design-library-preview/DesignLibraryPreview\";\nimport { DocumentReadyBeacon } from \"./DocumentReadyBeacon\";\nimport LinkInterceptor from \"./LinkInterceptor\";\nimport { NavigationReceiver } from \"./NavigationReceiver\";\nimport { NextErrorReporter } from \"./NextErrorReporter\";\nimport PostHogRelay from \"./posthog-relay\";\nimport { EditorSelection } from \"./select-mode/EditorSelection\";\n\nexport function SandboxRuntime({ children }: React.PropsWithChildren) {\n // The editor only embeds dev servers (`next dev`), so editor-integration components render there only \u2014 production builds ship none of them and behave like plain Next apps.\n const isDevServer = process.env.NODE_ENV === \"development\";\n\n return isDevServer ? (\n <>\n <DocumentReadyBeacon />\n {/* NOTE (Max, 2026-06-08): Outside ClientErrorBoundary on purpose \u2014 when\n the boundary catches a runtime error it swaps its children for a\n neutral cover, which would unmount the reporter and freeze it in the\n error state. It must stay mounted to observe Next's overlay across\n error <-> recover. */}\n <NextErrorReporter />\n <ClientErrorBoundary>\n <LinkInterceptor />\n <NavigationReceiver />\n <PostHogRelay />\n <EditorSelection />\n <AnnotationLayerMount />\n <DesignLibraryPreview />\n {children}\n </ClientErrorBoundary>\n </>\n ) : (\n children\n );\n}\n"],
|
|
5
|
+
"mappings": ";AAiBI,mBACE,KAOA,YARF;AAfJ,SAAS,4BAA4B;AACrC,SAAS,2BAA2B;AACpC,SAAS,4BAA4B;AACrC,SAAS,2BAA2B;AACpC,OAAO,qBAAqB;AAC5B,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,OAAO,kBAAkB;AACzB,SAAS,uBAAuB;AAEzB,SAAS,eAAe,EAAE,SAAS,GAA4B;AAEpE,QAAM,cAAc,QAAQ,IAAI,aAAa;AAE7C,SAAO,cACL,iCACE;AAAA,wBAAC,uBAAoB;AAAA,IAMrB,oBAAC,qBAAkB;AAAA,IACnB,qBAAC,uBACC;AAAA,0BAAC,mBAAgB;AAAA,MACjB,oBAAC,sBAAmB;AAAA,MACpB,oBAAC,gBAAa;AAAA,MACd,oBAAC,mBAAgB;AAAA,MACjB,oBAAC,wBAAqB;AAAA,MACtB,oBAAC,wBAAqB;AAAA,MACrB;AAAA,OACH;AAAA,KACF,IAEA;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../select-mode/displayModeStore.ts"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\n\nimport type { DisplayMode } from \"schemas/generated/selectMode\";\n\nimport { useSyncExternalStore } from \"react\";\n\n/**\n * Framework-agnostic singleton for the preview iframe's select-mode display\n * mode (\"browse\" vs \"selection\"). It lives as a module singleton so that\n * components that don't share a provider ancestor \u2014 `EditorSelection` (which\n * owns the pointer/selection machinery) and `LinkInterceptor` (a sibling under\n * `ReploProvider`) \u2014 can agree on whether the user is currently selecting.\n *\n * `LinkInterceptor` needs this because in agent-preview mode it
|
|
4
|
+
"sourcesContent": ["\"use client\";\n\nimport type { DisplayMode } from \"schemas/generated/selectMode\";\n\nimport { useSyncExternalStore } from \"react\";\n\n/**\n * Framework-agnostic singleton for the preview iframe's select-mode display\n * mode (\"browse\" vs \"selection\"). It lives as a module singleton so that\n * components that don't share a provider ancestor \u2014 `EditorSelection` (which\n * owns the pointer/selection machinery) and `LinkInterceptor` (a sibling under\n * `ReploProvider`) \u2014 can agree on whether the user is currently selecting.\n *\n * `LinkInterceptor` needs this because in agent-preview mode it observes\n * same-origin link clicks and reports the resulting navigation to the parent\n * editor (`replo:navigated`). While select mode is active that must stand\n * down so a link click selects the element instead of leaving the page \u2014\n * the two capture-phase click listeners otherwise race, and `stopPropagation`\n * can't suppress a sibling listener on the same node.\n *\n * @author Noah Gilmore 2026-07-23\n */\n\nconst listeners = new Set<() => void>();\nlet displayMode: DisplayMode = \"browse\";\n\n// select-mode DOM behavior is client-only, so the server snapshot is a stable\n// constant and never causes a hydration mismatch.\nconst SERVER_SNAPSHOT: DisplayMode = \"browse\";\n\nfunction notify() {\n for (const listener of listeners) {\n listener();\n }\n}\n\nexport const displayModeStore = {\n getSnapshot(): DisplayMode {\n return displayMode;\n },\n getServerSnapshot(): DisplayMode {\n return SERVER_SNAPSHOT;\n },\n subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n setDisplayMode(nextDisplayMode: DisplayMode) {\n if (nextDisplayMode === displayMode) {\n return;\n }\n displayMode = nextDisplayMode;\n notify();\n },\n /** Test-only: reset back to the default so specs don't leak state. */\n reset() {\n displayMode = \"browse\";\n },\n};\n\nexport function useDisplayMode(): DisplayMode {\n return useSyncExternalStore(\n displayModeStore.subscribe,\n displayModeStore.getSnapshot,\n displayModeStore.getServerSnapshot,\n );\n}\n"],
|
|
5
5
|
"mappings": ";AAIA,SAAS,4BAA4B;AAmBrC,MAAM,YAAY,oBAAI,IAAgB;AACtC,IAAI,cAA2B;AAI/B,MAAM,kBAA+B;AAErC,SAAS,SAAS;AAChB,aAAW,YAAY,WAAW;AAChC,aAAS;AAAA,EACX;AACF;AAEO,MAAM,mBAAmB;AAAA,EAC9B,cAA2B;AACzB,WAAO;AAAA,EACT;AAAA,EACA,oBAAiC;AAC/B,WAAO;AAAA,EACT;AAAA,EACA,UAAU,UAAkC;AAC1C,cAAU,IAAI,QAAQ;AACtB,WAAO,MAAM;AACX,gBAAU,OAAO,QAAQ;AAAA,IAC3B;AAAA,EACF;AAAA,EACA,eAAe,iBAA8B;AAC3C,QAAI,oBAAoB,aAAa;AACnC;AAAA,IACF;AACA,kBAAc;AACd,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,QAAQ;AACN,kBAAc;AAAA,EAChB;AACF;AAEO,SAAS,iBAA8B;AAC5C,SAAO;AAAA,IACL,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,EACnB;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,7 +2,10 @@ import type { SelectedElement } from "../_vendor/schemas/generated/selectMode";
|
|
|
2
2
|
import type { IframeRpcChannelInputHandlers, IframeRpcChannelRemote } from "../_vendor/schemas/iframeRpcChannel";
|
|
3
3
|
import { canopyToWebsiteBuilderMethodConfig, websiteBuilderToCanopyMethodConfig } from "../_vendor/schemas/selectModeIframeRpc";
|
|
4
4
|
export type WebsiteBuilderRpcRemote = IframeRpcChannelRemote<typeof canopyToWebsiteBuilderMethodConfig>;
|
|
5
|
-
type SelectModeParentMessagingHandlers = Omit<IframeRpcChannelInputHandlers<typeof websiteBuilderToCanopyMethodConfig>, "symbolicateSelectedElement">;
|
|
5
|
+
type SelectModeParentMessagingHandlers = Omit<IframeRpcChannelInputHandlers<typeof websiteBuilderToCanopyMethodConfig>, "symbolicateSelectedElement" | "navigateToRoute">;
|
|
6
|
+
export declare function setNavigateToRouteHandler(handler: ((input: {
|
|
7
|
+
pathname: string;
|
|
8
|
+
}) => void) | null): void;
|
|
6
9
|
export declare function getSelectModeIframeRpcChannel(): import("../_vendor/schemas/iframeRpcChannel").IframeRpcChannel<{
|
|
7
10
|
requestInlineTextEdit: {
|
|
8
11
|
inputSchema: import("zod").ZodObject<{
|
|
@@ -10,6 +10,10 @@ import {
|
|
|
10
10
|
} from "../_vendor/schemas/selectModeIframeRpc.mjs";
|
|
11
11
|
import { symbolicateSelectedElement } from "./symbolicate";
|
|
12
12
|
let messagingHandlers = null;
|
|
13
|
+
let navigateToRouteHandler = null;
|
|
14
|
+
function setNavigateToRouteHandler(handler) {
|
|
15
|
+
navigateToRouteHandler = handler;
|
|
16
|
+
}
|
|
13
17
|
function createSelectModeIframeRpcChannel() {
|
|
14
18
|
return createIframeRpcChannel({
|
|
15
19
|
subscriberWindow: window,
|
|
@@ -50,6 +54,9 @@ function createSelectModeIframeRpcChannel() {
|
|
|
50
54
|
},
|
|
51
55
|
symbolicateSelectedElement: (element) => {
|
|
52
56
|
return symbolicateSelectedElement(element);
|
|
57
|
+
},
|
|
58
|
+
navigateToRoute: (input) => {
|
|
59
|
+
navigateToRouteHandler?.(input);
|
|
53
60
|
}
|
|
54
61
|
}
|
|
55
62
|
});
|
|
@@ -87,6 +94,7 @@ export {
|
|
|
87
94
|
getSelectModeIframeRpcChannel,
|
|
88
95
|
getWebsiteBuilderRpc,
|
|
89
96
|
requestInlineTextEdit,
|
|
97
|
+
setNavigateToRouteHandler,
|
|
90
98
|
setSelectModeParentHandlers
|
|
91
99
|
};
|
|
92
100
|
//# sourceMappingURL=iframeRpcChannel.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../select-mode/iframeRpcChannel.ts"],
|
|
4
|
-
"sourcesContent": ["import type { SelectedElement } from \"schemas/generated/selectMode\";\nimport type {\n IframeRpcChannelInputHandlers,\n IframeRpcChannelRemote,\n} from \"schemas/iframeRpcChannel\";\n\nimport {\n createIframeRpcChannel,\n createIframeRpcRemote,\n} from \"schemas/iframeRpcChannel\";\nimport {\n canopyToWebsiteBuilderMethodConfig,\n SELECT_MODE_IFRAME_RPC_CHANNEL_ID,\n SELECT_MODE_IFRAME_RPC_CHANNEL_VERSION,\n websiteBuilderToCanopyMethodConfig,\n} from \"schemas/selectModeIframeRpc\";\n\nimport { symbolicateSelectedElement } from \"./symbolicate\";\n\nexport type WebsiteBuilderRpcRemote = IframeRpcChannelRemote<\n typeof canopyToWebsiteBuilderMethodConfig\n>;\n\ntype SelectModeParentMessagingHandlers = Omit<\n IframeRpcChannelInputHandlers<typeof websiteBuilderToCanopyMethodConfig>,\n \"symbolicateSelectedElement\"\n>;\n\n// Module-level handler pointers; `useParentMessaging` sets these on mount\n// and nulls them on unmount. Inbound void methods are dropped until then.\n// `symbolicateSelectedElement` is wired permanently on the channel.\nlet messagingHandlers: SelectModeParentMessagingHandlers | null = null;\n\nfunction createSelectModeIframeRpcChannel() {\n return createIframeRpcChannel({\n subscriberWindow: window,\n channelIdentifier: SELECT_MODE_IFRAME_RPC_CHANNEL_ID,\n version: SELECT_MODE_IFRAME_RPC_CHANNEL_VERSION,\n getTargetWindow: () => (window.parent === window ? null : window.parent),\n targetOrigin: \"*\",\n isExpectedMessage: (event) => event.source === window.parent,\n outgoingMethods: canopyToWebsiteBuilderMethodConfig,\n incomingMethods: websiteBuilderToCanopyMethodConfig,\n handlers: {\n setDisplayMode: (input) => {\n messagingHandlers?.setDisplayMode(input);\n },\n setSelectionType: (input) => {\n messagingHandlers?.setSelectionType(input);\n },\n applyOptimisticUpdate: (input) => {\n messagingHandlers?.applyOptimisticUpdate(input);\n },\n markCommitted: (input) => {\n messagingHandlers?.markCommitted(input);\n },\n resolveSessionOverrides: (input) => {\n messagingHandlers?.resolveSessionOverrides(input);\n },\n revertOverride: (input) => {\n messagingHandlers?.revertOverride(input);\n },\n enterEditMode: (input) => {\n messagingHandlers?.enterEditMode(input);\n },\n exitEditMode: (input) => {\n messagingHandlers?.exitEditMode(input);\n },\n formatCommand: (input) => {\n messagingHandlers?.formatCommand(input);\n },\n symbolicateSelectedElement: (element) => {\n return symbolicateSelectedElement(element);\n },\n },\n });\n}\n\nlet channel: ReturnType<typeof createSelectModeIframeRpcChannel> | null = null;\nlet websiteBuilderRpc: WebsiteBuilderRpcRemote | null = null;\n\nexport function getSelectModeIframeRpcChannel() {\n if (typeof window === \"undefined\") {\n return null;\n }\n channel ??= createSelectModeIframeRpcChannel();\n return channel;\n}\n\nexport function setSelectModeParentHandlers(\n nextHandlers: SelectModeParentMessagingHandlers | null,\n) {\n messagingHandlers = nextHandlers;\n}\n\nexport function getWebsiteBuilderRpc(): WebsiteBuilderRpcRemote | null {\n const currentChannel = getSelectModeIframeRpcChannel();\n if (!currentChannel) {\n return null;\n }\n websiteBuilderRpc ??= createIframeRpcRemote(currentChannel);\n return websiteBuilderRpc;\n}\n\nexport function requestInlineTextEdit(input: {\n editId: string;\n element: SelectedElement;\n}) {\n const remote = getWebsiteBuilderRpc();\n if (!remote) {\n return Promise.reject(\n new Error(\"Website Builder iframe RPC is unavailable\"),\n );\n }\n return remote.requestInlineTextEdit(input);\n}\n"],
|
|
5
|
-
"mappings": "AAMA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,kCAAkC;AAc3C,IAAI,oBAA8D;
|
|
4
|
+
"sourcesContent": ["import type { SelectedElement } from \"schemas/generated/selectMode\";\nimport type {\n IframeRpcChannelInputHandlers,\n IframeRpcChannelRemote,\n} from \"schemas/iframeRpcChannel\";\n\nimport {\n createIframeRpcChannel,\n createIframeRpcRemote,\n} from \"schemas/iframeRpcChannel\";\nimport {\n canopyToWebsiteBuilderMethodConfig,\n SELECT_MODE_IFRAME_RPC_CHANNEL_ID,\n SELECT_MODE_IFRAME_RPC_CHANNEL_VERSION,\n websiteBuilderToCanopyMethodConfig,\n} from \"schemas/selectModeIframeRpc\";\n\nimport { symbolicateSelectedElement } from \"./symbolicate\";\n\nexport type WebsiteBuilderRpcRemote = IframeRpcChannelRemote<\n typeof canopyToWebsiteBuilderMethodConfig\n>;\n\ntype SelectModeParentMessagingHandlers = Omit<\n IframeRpcChannelInputHandlers<typeof websiteBuilderToCanopyMethodConfig>,\n \"symbolicateSelectedElement\" | \"navigateToRoute\"\n>;\n\n// Module-level handler pointers; `useParentMessaging` sets these on mount\n// and nulls them on unmount. Inbound void methods are dropped until then.\n// `symbolicateSelectedElement` is wired permanently on the channel.\nlet messagingHandlers: SelectModeParentMessagingHandlers | null = null;\n\n// Set by `NavigationReceiver` while a router is mounted; unhandled commands are dropped.\nlet navigateToRouteHandler: ((input: { pathname: string }) => void) | null =\n null;\n\nexport function setNavigateToRouteHandler(\n handler: ((input: { pathname: string }) => void) | null,\n) {\n navigateToRouteHandler = handler;\n}\n\nfunction createSelectModeIframeRpcChannel() {\n return createIframeRpcChannel({\n subscriberWindow: window,\n channelIdentifier: SELECT_MODE_IFRAME_RPC_CHANNEL_ID,\n version: SELECT_MODE_IFRAME_RPC_CHANNEL_VERSION,\n getTargetWindow: () => (window.parent === window ? null : window.parent),\n targetOrigin: \"*\",\n isExpectedMessage: (event) => event.source === window.parent,\n outgoingMethods: canopyToWebsiteBuilderMethodConfig,\n incomingMethods: websiteBuilderToCanopyMethodConfig,\n handlers: {\n setDisplayMode: (input) => {\n messagingHandlers?.setDisplayMode(input);\n },\n setSelectionType: (input) => {\n messagingHandlers?.setSelectionType(input);\n },\n applyOptimisticUpdate: (input) => {\n messagingHandlers?.applyOptimisticUpdate(input);\n },\n markCommitted: (input) => {\n messagingHandlers?.markCommitted(input);\n },\n resolveSessionOverrides: (input) => {\n messagingHandlers?.resolveSessionOverrides(input);\n },\n revertOverride: (input) => {\n messagingHandlers?.revertOverride(input);\n },\n enterEditMode: (input) => {\n messagingHandlers?.enterEditMode(input);\n },\n exitEditMode: (input) => {\n messagingHandlers?.exitEditMode(input);\n },\n formatCommand: (input) => {\n messagingHandlers?.formatCommand(input);\n },\n symbolicateSelectedElement: (element) => {\n return symbolicateSelectedElement(element);\n },\n navigateToRoute: (input) => {\n navigateToRouteHandler?.(input);\n },\n },\n });\n}\n\nlet channel: ReturnType<typeof createSelectModeIframeRpcChannel> | null = null;\nlet websiteBuilderRpc: WebsiteBuilderRpcRemote | null = null;\n\nexport function getSelectModeIframeRpcChannel() {\n if (typeof window === \"undefined\") {\n return null;\n }\n channel ??= createSelectModeIframeRpcChannel();\n return channel;\n}\n\nexport function setSelectModeParentHandlers(\n nextHandlers: SelectModeParentMessagingHandlers | null,\n) {\n messagingHandlers = nextHandlers;\n}\n\nexport function getWebsiteBuilderRpc(): WebsiteBuilderRpcRemote | null {\n const currentChannel = getSelectModeIframeRpcChannel();\n if (!currentChannel) {\n return null;\n }\n websiteBuilderRpc ??= createIframeRpcRemote(currentChannel);\n return websiteBuilderRpc;\n}\n\nexport function requestInlineTextEdit(input: {\n editId: string;\n element: SelectedElement;\n}) {\n const remote = getWebsiteBuilderRpc();\n if (!remote) {\n return Promise.reject(\n new Error(\"Website Builder iframe RPC is unavailable\"),\n );\n }\n return remote.requestInlineTextEdit(input);\n}\n"],
|
|
5
|
+
"mappings": "AAMA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,kCAAkC;AAc3C,IAAI,oBAA8D;AAGlE,IAAI,yBACF;AAEK,SAAS,0BACd,SACA;AACA,2BAAyB;AAC3B;AAEA,SAAS,mCAAmC;AAC1C,SAAO,uBAAuB;AAAA,IAC5B,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,SAAS;AAAA,IACT,iBAAiB,MAAO,OAAO,WAAW,SAAS,OAAO,OAAO;AAAA,IACjE,cAAc;AAAA,IACd,mBAAmB,CAAC,UAAU,MAAM,WAAW,OAAO;AAAA,IACtD,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,UAAU;AAAA,MACR,gBAAgB,CAAC,UAAU;AACzB,2BAAmB,eAAe,KAAK;AAAA,MACzC;AAAA,MACA,kBAAkB,CAAC,UAAU;AAC3B,2BAAmB,iBAAiB,KAAK;AAAA,MAC3C;AAAA,MACA,uBAAuB,CAAC,UAAU;AAChC,2BAAmB,sBAAsB,KAAK;AAAA,MAChD;AAAA,MACA,eAAe,CAAC,UAAU;AACxB,2BAAmB,cAAc,KAAK;AAAA,MACxC;AAAA,MACA,yBAAyB,CAAC,UAAU;AAClC,2BAAmB,wBAAwB,KAAK;AAAA,MAClD;AAAA,MACA,gBAAgB,CAAC,UAAU;AACzB,2BAAmB,eAAe,KAAK;AAAA,MACzC;AAAA,MACA,eAAe,CAAC,UAAU;AACxB,2BAAmB,cAAc,KAAK;AAAA,MACxC;AAAA,MACA,cAAc,CAAC,UAAU;AACvB,2BAAmB,aAAa,KAAK;AAAA,MACvC;AAAA,MACA,eAAe,CAAC,UAAU;AACxB,2BAAmB,cAAc,KAAK;AAAA,MACxC;AAAA,MACA,4BAA4B,CAAC,YAAY;AACvC,eAAO,2BAA2B,OAAO;AAAA,MAC3C;AAAA,MACA,iBAAiB,CAAC,UAAU;AAC1B,iCAAyB,KAAK;AAAA,MAChC;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,IAAI,UAAsE;AAC1E,IAAI,oBAAoD;AAEjD,SAAS,gCAAgC;AAC9C,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO;AAAA,EACT;AACA,cAAY,iCAAiC;AAC7C,SAAO;AACT;AAEO,SAAS,4BACd,cACA;AACA,sBAAoB;AACtB;AAEO,SAAS,uBAAuD;AACrE,QAAM,iBAAiB,8BAA8B;AACrD,MAAI,CAAC,gBAAgB;AACnB,WAAO;AAAA,EACT;AACA,wBAAsB,sBAAsB,cAAc;AAC1D,SAAO;AACT;AAEO,SAAS,sBAAsB,OAGnC;AACD,QAAM,SAAS,qBAAqB;AACpC,MAAI,CAAC,QAAQ;AACX,WAAO,QAAQ;AAAA,MACb,IAAI,MAAM,2CAA2C;AAAA,IACvD;AAAA,EACF;AACA,SAAO,OAAO,sBAAsB,KAAK;AAC3C;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|