@replohq/sandbox-runtime 1.4.1 → 1.5.1
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/_vendor/replo-utils/lib/fonts.mjs +2 -1
- package/_vendor/replo-utils/lib/math.d.ts +60 -0
- package/_vendor/replo-utils/lib/math.mjs +99 -0
- package/_vendor/replo-utils/lib/misc.mjs +4 -2
- package/_vendor/schemas/designLibrary.d.ts +4 -4
- package/_vendor/schemas/iframeRpcChannel.mjs +5 -7
- package/_vendor/schemas/savedStyles.d.ts +2 -2
- package/_vendor/schemas/selectMode.mjs +5 -3
- package/_vendor/schemas/selectModeIframeRpc.mjs +5 -3
- package/annotations/annotationController.js +12 -6
- package/annotations/annotationController.js.map +2 -2
- package/buildMetadata.js +3 -3
- package/chunk-FLYEIRFK.mjs +10 -0
- package/{chunk-FNTGY7I5.mjs → chunk-G3S6ZNPF.mjs} +1 -5
- package/{chunk-N5EI2MOZ.mjs → chunk-JUKCUPQ6.mjs} +3 -9
- package/chunk-SGKQ5HGT.mjs +8 -0
- package/chunk-TESFJQID.mjs +5 -0
- package/{chunk-2455EG5R.mjs → chunk-UOBGRB5I.mjs} +1 -1
- package/next/config.d.ts +1 -0
- package/next/config.js +17 -4
- package/next/config.js.map +2 -2
- package/package.json +4 -4
- package/posthog-relay.js +2 -2
- package/posthog-relay.js.map +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isArray_default } from '../../../chunk-TESFJQID.mjs';
|
|
2
|
+
import { Symbol_default, isObjectLike_default, baseGetTag_default } from '../../../chunk-G3S6ZNPF.mjs';
|
|
2
3
|
|
|
3
4
|
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_arrayMap.js
|
|
4
5
|
function arrayMap(array, iteratee) {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export declare function isCloseTo(number: number, comparator: number, epsilon?: number): boolean;
|
|
2
|
+
export declare function round(number: number, precision: number): number;
|
|
3
|
+
/**
|
|
4
|
+
* Rounds a number to a given number of decimal places.
|
|
5
|
+
*
|
|
6
|
+
* Unlike {@link round} (which rounds to an integer before applying precision),
|
|
7
|
+
* this rounds to the requested decimal precision, e.g.
|
|
8
|
+
* `roundToPrecision({ value: 1.2345, precision: 2 })` → `1.23`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function roundToPrecision({ value, precision, }: {
|
|
11
|
+
value: number;
|
|
12
|
+
precision: number;
|
|
13
|
+
}): number;
|
|
14
|
+
export declare function clamp(number: number, minimum: number, maximum: number): number;
|
|
15
|
+
export declare function parseInteger(value: string | number): number;
|
|
16
|
+
export declare function parseIntegerOrDefault(value: string, defaultValue: number): number;
|
|
17
|
+
export declare function parseFloat(value: string | number): number;
|
|
18
|
+
export declare function parseFloatOrNull(value: string | null | undefined): number | null;
|
|
19
|
+
/**
|
|
20
|
+
* Converts cents to dollars.
|
|
21
|
+
* Commonly used for payment processor APIs (Stripe, Dub, etc.) that return amounts in cents.
|
|
22
|
+
*
|
|
23
|
+
* @param cents - Amount in cents
|
|
24
|
+
* @returns Amount in dollars
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* centsToDollars(12345) // 123.45
|
|
28
|
+
* centsToDollars(5000) // 50
|
|
29
|
+
* centsToDollars(0) // 0
|
|
30
|
+
*/
|
|
31
|
+
export declare function centsToDollars(cents: number): number;
|
|
32
|
+
/**
|
|
33
|
+
* @returns Distance between two points
|
|
34
|
+
*/
|
|
35
|
+
export declare function distance(x1: number, y1: number, x2: number, y2: number): number;
|
|
36
|
+
export declare function formatWithCommasAndTwoDecimals(num: number | undefined, shouldShowDecimals: boolean): string;
|
|
37
|
+
export declare function randomBigNumber(): number;
|
|
38
|
+
export declare function randomN(i: number): number;
|
|
39
|
+
export declare function randomBigString(): string;
|
|
40
|
+
/**
|
|
41
|
+
* Given a current value and a previous value, calculate the percentage change
|
|
42
|
+
* from the previous value to the current value.
|
|
43
|
+
*
|
|
44
|
+
* @author Max 2025-04-01
|
|
45
|
+
*/
|
|
46
|
+
export declare function calculatePercentageChange(current: number, previous: number): number | null;
|
|
47
|
+
/**
|
|
48
|
+
* Format bytes into a human readable string, favoring the largest unit possible.
|
|
49
|
+
*
|
|
50
|
+
* @param bytes - The number of bytes (can be a number or numeric string)
|
|
51
|
+
* @returns Human readable string (e.g., "1.50 GB", "256.00 MB", "1024 bytes")
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* formatBytes(1536) // "1.50 KB"
|
|
55
|
+
* formatBytes(1048576) // "1.00 MB"
|
|
56
|
+
* formatBytes("1073741824") // "1.00 GB"
|
|
57
|
+
*
|
|
58
|
+
* @author Max 2024-08-28
|
|
59
|
+
*/
|
|
60
|
+
export declare function formatBytes(bytes: number | string): string;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { randomUUID } from '../../../chunk-SGKQ5HGT.mjs';
|
|
2
|
+
import { isNumber_default } from '../../../chunk-FLYEIRFK.mjs';
|
|
3
|
+
import '../../../chunk-G3S6ZNPF.mjs';
|
|
4
|
+
|
|
5
|
+
// ../replo-utils/lib/math.ts
|
|
6
|
+
var EPSILON = 0.03;
|
|
7
|
+
function isCloseTo(number, comparator, epsilon = EPSILON) {
|
|
8
|
+
return Math.abs(number - comparator) < epsilon;
|
|
9
|
+
}
|
|
10
|
+
function round(number, precision) {
|
|
11
|
+
return Number.parseFloat(Math.round(number).toFixed(precision));
|
|
12
|
+
}
|
|
13
|
+
function roundToPrecision({
|
|
14
|
+
value,
|
|
15
|
+
precision
|
|
16
|
+
}) {
|
|
17
|
+
const factor = 10 ** precision;
|
|
18
|
+
return Math.round(value * factor) / factor;
|
|
19
|
+
}
|
|
20
|
+
function clamp(number, minimum, maximum) {
|
|
21
|
+
const _minimum = Math.min(minimum, maximum);
|
|
22
|
+
const _maximum = Math.max(minimum, maximum);
|
|
23
|
+
return Math.min(Math.max(_minimum, number), _maximum);
|
|
24
|
+
}
|
|
25
|
+
function parseInteger(value) {
|
|
26
|
+
let number;
|
|
27
|
+
if (isNumber_default(value) && Number.isInteger(value)) {
|
|
28
|
+
number = value;
|
|
29
|
+
} else {
|
|
30
|
+
number = Number.parseInt(value, 10);
|
|
31
|
+
}
|
|
32
|
+
return number === 0 ? 0 : number;
|
|
33
|
+
}
|
|
34
|
+
function parseIntegerOrDefault(value, defaultValue) {
|
|
35
|
+
const result = Number.parseInt(value);
|
|
36
|
+
if (Number.isNaN(result)) {
|
|
37
|
+
return defaultValue;
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
function parseFloat(value) {
|
|
42
|
+
const number = isNumber_default(value) ? value : Number.parseFloat(value);
|
|
43
|
+
return number === 0 ? 0 : number;
|
|
44
|
+
}
|
|
45
|
+
function parseFloatOrNull(value) {
|
|
46
|
+
if (value === null || value === void 0) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const num = Number.parseFloat(value);
|
|
50
|
+
return Number.isNaN(num) ? null : num;
|
|
51
|
+
}
|
|
52
|
+
function centsToDollars(cents) {
|
|
53
|
+
return cents / 100;
|
|
54
|
+
}
|
|
55
|
+
function distance(x1, y1, x2, y2) {
|
|
56
|
+
return Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2);
|
|
57
|
+
}
|
|
58
|
+
function formatWithCommasAndTwoDecimals(num, shouldShowDecimals) {
|
|
59
|
+
if (num === void 0) {
|
|
60
|
+
return shouldShowDecimals ? "0.00" : "0";
|
|
61
|
+
}
|
|
62
|
+
return new Intl.NumberFormat("en-US", {
|
|
63
|
+
minimumFractionDigits: shouldShowDecimals ? 2 : 0,
|
|
64
|
+
maximumFractionDigits: shouldShowDecimals ? 2 : 0
|
|
65
|
+
}).format(Math.abs(num));
|
|
66
|
+
}
|
|
67
|
+
function randomBigNumber() {
|
|
68
|
+
return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
|
|
69
|
+
}
|
|
70
|
+
function randomN(i) {
|
|
71
|
+
return Math.floor(Math.random() * i);
|
|
72
|
+
}
|
|
73
|
+
function randomBigString() {
|
|
74
|
+
return randomUUID().replaceAll("-", "");
|
|
75
|
+
}
|
|
76
|
+
function calculatePercentageChange(current, previous) {
|
|
77
|
+
if (previous === 0) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return (current - previous) / previous * 100;
|
|
81
|
+
}
|
|
82
|
+
var KB = 1024;
|
|
83
|
+
var MB = KB * 1024;
|
|
84
|
+
var GB = MB * 1024;
|
|
85
|
+
function formatBytes(bytes) {
|
|
86
|
+
const bytesNum = typeof bytes === "string" ? Number(bytes) : bytes;
|
|
87
|
+
if (bytesNum >= GB) {
|
|
88
|
+
return `${(bytesNum / GB).toFixed(2)} GB`;
|
|
89
|
+
}
|
|
90
|
+
if (bytesNum >= MB) {
|
|
91
|
+
return `${(bytesNum / MB).toFixed(2)} MB`;
|
|
92
|
+
}
|
|
93
|
+
if (bytesNum >= KB) {
|
|
94
|
+
return `${(bytesNum / KB).toFixed(2)} KB`;
|
|
95
|
+
}
|
|
96
|
+
return `${bytesNum} bytes`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { calculatePercentageChange, centsToDollars, clamp, distance, formatBytes, formatWithCommasAndTwoDecimals, isCloseTo, parseFloat, parseFloatOrNull, parseInteger, parseIntegerOrDefault, randomBigNumber, randomBigString, randomN, round, roundToPrecision };
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
export { coerceNumberToString, deepCloneAndMergeReplacingArrays, errorMessage, exhaustiveSwitch, formatFileSize, getFromRecordOrNull, hasOwnProperty, invariant, isEmpty, isFunction_default as isFunction, isNotNullish, isNullish,
|
|
2
|
-
|
|
1
|
+
export { coerceNumberToString, deepCloneAndMergeReplacingArrays, errorMessage, exhaustiveSwitch, formatFileSize, getFromRecordOrNull, hasOwnProperty, invariant, isEmpty, isFunction_default as isFunction, isNotNullish, isNullish, isPrimitive, isString_default as isString, mergeReplacingArrays, mergeReplacingObjects, noop, preventDefault, sleep, warning } from '../../../chunk-JUKCUPQ6.mjs';
|
|
2
|
+
export { isNumber_default as isNumber } from '../../../chunk-FLYEIRFK.mjs';
|
|
3
|
+
import '../../../chunk-TESFJQID.mjs';
|
|
4
|
+
import '../../../chunk-G3S6ZNPF.mjs';
|
|
@@ -51,9 +51,9 @@ export declare const savedTextStyleSchema: z.ZodObject<{
|
|
|
51
51
|
letterSpacing: z.ZodOptional<z.ZodString>;
|
|
52
52
|
lineHeight: z.ZodOptional<z.ZodString>;
|
|
53
53
|
textAlign: z.ZodOptional<z.ZodEnum<{
|
|
54
|
-
center: "center";
|
|
55
54
|
left: "left";
|
|
56
55
|
right: "right";
|
|
56
|
+
center: "center";
|
|
57
57
|
}>>;
|
|
58
58
|
textDecoration: z.ZodOptional<z.ZodEnum<{
|
|
59
59
|
none: "none";
|
|
@@ -125,9 +125,9 @@ export declare const designLibrarySavedStylesSchema: z.ZodRecord<z.ZodString, z.
|
|
|
125
125
|
letterSpacing: z.ZodOptional<z.ZodString>;
|
|
126
126
|
lineHeight: z.ZodOptional<z.ZodString>;
|
|
127
127
|
textAlign: z.ZodOptional<z.ZodEnum<{
|
|
128
|
-
center: "center";
|
|
129
128
|
left: "left";
|
|
130
129
|
right: "right";
|
|
130
|
+
center: "center";
|
|
131
131
|
}>>;
|
|
132
132
|
textDecoration: z.ZodOptional<z.ZodEnum<{
|
|
133
133
|
none: "none";
|
|
@@ -204,9 +204,9 @@ export declare const designLibrarySchema: z.ZodObject<{
|
|
|
204
204
|
letterSpacing: z.ZodOptional<z.ZodString>;
|
|
205
205
|
lineHeight: z.ZodOptional<z.ZodString>;
|
|
206
206
|
textAlign: z.ZodOptional<z.ZodEnum<{
|
|
207
|
-
center: "center";
|
|
208
207
|
left: "left";
|
|
209
208
|
right: "right";
|
|
209
|
+
center: "center";
|
|
210
210
|
}>>;
|
|
211
211
|
textDecoration: z.ZodOptional<z.ZodEnum<{
|
|
212
212
|
none: "none";
|
|
@@ -281,9 +281,9 @@ export declare const designLibrarySavedStyleMetadataSchema: z.ZodObject<{
|
|
|
281
281
|
letterSpacing: z.ZodOptional<z.ZodString>;
|
|
282
282
|
lineHeight: z.ZodOptional<z.ZodString>;
|
|
283
283
|
textAlign: z.ZodOptional<z.ZodEnum<{
|
|
284
|
-
center: "center";
|
|
285
284
|
left: "left";
|
|
286
285
|
right: "right";
|
|
286
|
+
center: "center";
|
|
287
287
|
}>>;
|
|
288
288
|
textDecoration: z.ZodOptional<z.ZodEnum<{
|
|
289
289
|
none: "none";
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import '../../chunk-
|
|
3
|
-
import
|
|
1
|
+
import { randomUUID } from '../../chunk-SGKQ5HGT.mjs';
|
|
2
|
+
import { isNullish } from '../../chunk-JUKCUPQ6.mjs';
|
|
3
|
+
import '../../chunk-FLYEIRFK.mjs';
|
|
4
|
+
import '../../chunk-TESFJQID.mjs';
|
|
5
|
+
import '../../chunk-G3S6ZNPF.mjs';
|
|
4
6
|
import { z } from 'zod';
|
|
5
7
|
|
|
6
|
-
function randomUUID() {
|
|
7
|
-
return v4();
|
|
8
|
-
}
|
|
9
|
-
|
|
10
8
|
// ../replo-utils/lib/log.ts
|
|
11
9
|
var defaultLogger = {
|
|
12
10
|
info: console.log,
|
|
@@ -59,9 +59,9 @@ export declare const savedStyleTextAttributesSchema: z.ZodObject<{
|
|
|
59
59
|
letterSpacing: z.ZodOptional<z.ZodString>;
|
|
60
60
|
lineHeight: z.ZodOptional<z.ZodString>;
|
|
61
61
|
textAlign: z.ZodOptional<z.ZodEnum<{
|
|
62
|
-
center: "center";
|
|
63
62
|
left: "left";
|
|
64
63
|
right: "right";
|
|
64
|
+
center: "center";
|
|
65
65
|
}>>;
|
|
66
66
|
textDecoration: z.ZodOptional<z.ZodEnum<{
|
|
67
67
|
none: "none";
|
|
@@ -119,9 +119,9 @@ export declare const savedStyleAttributesSchema: z.ZodDiscriminatedUnion<[z.ZodO
|
|
|
119
119
|
letterSpacing: z.ZodOptional<z.ZodString>;
|
|
120
120
|
lineHeight: z.ZodOptional<z.ZodString>;
|
|
121
121
|
textAlign: z.ZodOptional<z.ZodEnum<{
|
|
122
|
-
center: "center";
|
|
123
122
|
left: "left";
|
|
124
123
|
right: "right";
|
|
124
|
+
center: "center";
|
|
125
125
|
}>>;
|
|
126
126
|
textDecoration: z.ZodOptional<z.ZodEnum<{
|
|
127
127
|
none: "none";
|
|
@@ -1,3 +1,5 @@
|
|
|
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-
|
|
2
|
-
import '../../chunk-
|
|
3
|
-
import '../../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-UOBGRB5I.mjs';
|
|
2
|
+
import '../../chunk-JUKCUPQ6.mjs';
|
|
3
|
+
import '../../chunk-FLYEIRFK.mjs';
|
|
4
|
+
import '../../chunk-TESFJQID.mjs';
|
|
5
|
+
import '../../chunk-G3S6ZNPF.mjs';
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { selectModeFormatCommandSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionTypeSchema, displayModeSchema, selectModeTiptapActiveMarksSchema, selectModeBoundingRectSchema, selectableDataSchema, selectModeSelectionRectSchema, selectModeClickPositionSchema, selectedElementSchema } from '../../chunk-
|
|
2
|
-
import '../../chunk-
|
|
3
|
-
import '../../chunk-
|
|
1
|
+
import { selectModeFormatCommandSchema, selectModeOptimisticUpdatePayloadSchema, selectModeSelectionTypeSchema, displayModeSchema, selectModeTiptapActiveMarksSchema, selectModeBoundingRectSchema, selectableDataSchema, selectModeSelectionRectSchema, selectModeClickPositionSchema, selectedElementSchema } from '../../chunk-UOBGRB5I.mjs';
|
|
2
|
+
import '../../chunk-JUKCUPQ6.mjs';
|
|
3
|
+
import '../../chunk-FLYEIRFK.mjs';
|
|
4
|
+
import '../../chunk-TESFJQID.mjs';
|
|
5
|
+
import '../../chunk-G3S6ZNPF.mjs';
|
|
4
6
|
import { z } from 'zod';
|
|
5
7
|
|
|
6
8
|
var SELECT_MODE_IFRAME_RPC_CHANNEL_ID = "replo:select-mode-iframe-rpc";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clamp } from "../_vendor/replo-utils/lib/math.mjs";
|
|
1
2
|
import { exhaustiveSwitch } from "../_vendor/replo-utils/lib/misc.mjs";
|
|
2
3
|
import {
|
|
3
4
|
PAGE_ANNOTATIONS_PROTOCOL_VERSION,
|
|
@@ -139,13 +140,18 @@ function clampBadgeCenter({
|
|
|
139
140
|
}) {
|
|
140
141
|
const width = isFixed ? window.innerWidth : Math.max(document.documentElement.scrollWidth, window.innerWidth);
|
|
141
142
|
const height = isFixed ? window.innerHeight : Math.max(document.documentElement.scrollHeight, window.innerHeight);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
143
|
+
return {
|
|
144
|
+
x: clamp(
|
|
145
|
+
x,
|
|
146
|
+
BADGE_EDGE_INSET_PX,
|
|
147
|
+
Math.max(BADGE_EDGE_INSET_PX, width - BADGE_EDGE_INSET_PX)
|
|
148
|
+
),
|
|
149
|
+
y: clamp(
|
|
150
|
+
y,
|
|
151
|
+
BADGE_EDGE_INSET_PX,
|
|
152
|
+
Math.max(BADGE_EDGE_INSET_PX, height - BADGE_EDGE_INSET_PX)
|
|
153
|
+
)
|
|
147
154
|
};
|
|
148
|
-
return { x: clamp(x, width), y: clamp(y, height) };
|
|
149
155
|
}
|
|
150
156
|
function getMostSevereColorHex(members) {
|
|
151
157
|
const color = COLOR_SEVERITY.find((candidate) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../annotations/annotationController.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * In-iframe annotation controller: consumes declarative marker sets from the\n * editor bridge, resolves selectors against the live DOM, and drives the\n * shared annotation frame renderer. Scroll costs zero JavaScript (frames use\n * page coordinates); re-measure runs only on resize/mutation/container\n * scroll, batched per animation frame. Data-only protocol \u2014 the editor can\n * describe dots, never execute code.\n */\nimport type {\n PageAnnotationMarker,\n PageAnnotationsIframeMessage,\n} from \"schemas/generated/pageAnnotations\";\nimport type { AnnotationFrameSpec } from \"../select-mode/annotationFrames\";\n\nimport { exhaustiveSwitch } from \"replo-utils/lib/misc\";\nimport {\n PAGE_ANNOTATIONS_PROTOCOL_VERSION,\n pageAnnotationsParentMessageSchema,\n} from \"schemas/pageAnnotations\";\n\nimport {\n ANNOTATION_CONTAINER_ID,\n clearAnnotationFrames,\n getAnnotationBadgeViewportRect,\n setActiveAnnotationBadge,\n showAnnotationFrames,\n} from \"../select-mode/annotationFrames\";\nimport { getDocumentScrollOffset } from \"../select-mode/interactions/selectionGeometry\";\nimport { isAllowedEditorMessage, isAllowedEditorOrigin } from \"./bridgeGate\";\n\nconst CLUSTER_DISTANCE_PX = 24;\nconst CLUSTER_FAN_RADIUS_PX = 30;\n// Badges center on their element's top edge, so edge-hugging ones need insetting.\nconst BADGE_RADIUS_PX = 15;\nconst BADGE_EDGE_INSET_PX = BADGE_RADIUS_PX + 4;\nconst REMEASURE_DEBOUNCE_MS = 250;\n// Page-wide findings stack in a row near the top-left of the page and scroll\n// with it like any other annotation.\nconst PAGE_TOP_STACK_X_PX = 20;\nconst PAGE_TOP_STACK_Y_PX = 20;\nconst PAGE_TOP_STACK_GAP_PX = 36;\n\n// Replo brand palette (lattice --color-brand-*): the wire enum names stay\n// red/purple/amber but the rendered hues are brand magenta/purple/blue,\n// matching the editor-side dots and cards.\nconst COLOR_HEX: Record<PageAnnotationMarker[\"color\"], string> = {\n red: \"#ef196e\",\n purple: \"#8b32a8\",\n amber: \"#274ae2\",\n};\nconst COLOR_SEVERITY: PageAnnotationMarker[\"color\"][] = [\n \"red\",\n \"purple\",\n \"amber\",\n];\n\ninterface ResolvedMarker {\n marker: PageAnnotationMarker;\n element: Element | null;\n isFixed: boolean;\n rect: { x: number; y: number; width: number; height: number };\n // The editor-assigned wire number, rendered verbatim.\n displayNumber: number;\n}\n\ninterface MarkerCluster {\n id: string;\n centerX: number;\n centerY: number;\n isFixed: boolean;\n members: ResolvedMarker[];\n}\n\nlet isInitialized = false;\nlet editorOrigin: string | null = null;\nlet markers: PageAnnotationMarker[] = [];\nlet setId: string | null = null;\nlet expandedClusterId: string | null = null;\nlet activeMarkerId: string | null = null;\nlet remeasureFrameId: number | null = null;\nlet remeasureDebounceId: ReturnType<typeof setTimeout> | null = null;\nlet activeRectFrameId: number | null = null;\nlet observersActive = false;\nlet lastRenderSignature: string | null = null;\nlet resizeObserver: ResizeObserver | null = null;\nlet mutationObserver: MutationObserver | null = null;\n\nfunction postToEditor(message: PageAnnotationsIframeMessage) {\n if (editorOrigin === null) {\n return;\n }\n window.parent.postMessage(message, editorOrigin);\n}\n\nfunction hasFixedAncestor(element: Element): boolean {\n let current: Element | null = element;\n while (current !== null && current !== document.documentElement) {\n const position = getComputedStyle(current).position;\n if (position === \"fixed\" || position === \"sticky\") {\n return true;\n }\n current = current.parentElement;\n }\n return false;\n}\n\nfunction resolveMarkers(): {\n resolved: ResolvedMarker[];\n unresolvedIds: string[];\n} {\n const resolved: ResolvedMarker[] = [];\n const unresolvedIds: string[] = [];\n let pageTopSlot = 0;\n for (const marker of markers) {\n if (marker.placement === \"pageTop\") {\n // Page-wide finding: a fixed slot in the top-of-page stack, no selector.\n resolved.push({\n marker,\n element: null,\n isFixed: false,\n rect: {\n x: PAGE_TOP_STACK_X_PX + pageTopSlot * PAGE_TOP_STACK_GAP_PX,\n y: PAGE_TOP_STACK_Y_PX,\n width: 0,\n height: 0,\n },\n // The editor owns numbering; rendering the wire number keeps dots in sync with the panel.\n displayNumber: marker.number,\n });\n pageTopSlot += 1;\n continue;\n }\n let element: Element | null = null;\n try {\n element = document.querySelector(marker.selector);\n } catch {\n // Malformed selector \u2014 unresolved, never a throw.\n }\n const domRect = element?.getBoundingClientRect();\n if (!element || !domRect || (domRect.width === 0 && domRect.height === 0)) {\n unresolvedIds.push(marker.id);\n continue;\n }\n const isFixed = hasFixedAncestor(element);\n const scrollOffset = getDocumentScrollOffset();\n resolved.push({\n marker,\n element,\n isFixed,\n // Fixed frames use viewport coords; absolute frames use page coords.\n rect: {\n x: domRect.x + (isFixed ? 0 : scrollOffset.left),\n y: domRect.y + (isFixed ? 0 : scrollOffset.top),\n width: domRect.width,\n height: domRect.height,\n },\n displayNumber: marker.number,\n });\n }\n return { resolved, unresolvedIds };\n}\n\n// Greedy: a marker joins the first cluster whose badge anchor lies within\n// 24px; fixed and absolute markers never co-cluster (their coordinate spaces\n// diverge as soon as the page scrolls).\nfunction clusterResolvedMarkers(resolved: ResolvedMarker[]): MarkerCluster[] {\n const clusters: MarkerCluster[] = [];\n for (const entry of resolved) {\n const centerX = entry.rect.x + entry.rect.width / 2;\n const centerY = entry.rect.y;\n const host = clusters.find((cluster) => {\n return (\n cluster.isFixed === entry.isFixed &&\n Math.hypot(cluster.centerX - centerX, cluster.centerY - centerY) <\n CLUSTER_DISTANCE_PX\n );\n });\n if (host) {\n host.members.push(entry);\n } else {\n clusters.push({\n id: entry.marker.id,\n centerX,\n centerY,\n isFixed: entry.isFixed,\n members: [entry],\n });\n }\n }\n return clusters;\n}\n\n// Keeps a badge fully on screen: page box for absolute, viewport for fixed.\nfunction clampBadgeCenter({\n x,\n y,\n isFixed,\n}: {\n x: number;\n y: number;\n isFixed: boolean;\n}): { x: number; y: number } {\n const width = isFixed\n ? window.innerWidth\n : Math.max(document.documentElement.scrollWidth, window.innerWidth);\n const height = isFixed\n ? window.innerHeight\n : Math.max(document.documentElement.scrollHeight, window.innerHeight);\n const clamp = (value: number, limit: number) => {\n return Math.min(\n Math.max(value, BADGE_EDGE_INSET_PX),\n Math.max(BADGE_EDGE_INSET_PX, limit - BADGE_EDGE_INSET_PX),\n );\n };\n return { x: clamp(x, width), y: clamp(y, height) };\n}\n\nfunction getMostSevereColorHex(members: ResolvedMarker[]): string {\n const color =\n COLOR_SEVERITY.find((candidate) => {\n return members.some((member) => member.marker.color === candidate);\n }) ?? \"amber\";\n return COLOR_HEX[color];\n}\n\nfunction activateMarker(id: string) {\n activeMarkerId = id;\n const viewportRect = getAnnotationBadgeViewportRect(id);\n if (viewportRect) {\n postToEditor({ type: \"replo:annotations:clicked\", id, viewportRect });\n }\n // Style-only: a rebuild here would race the pointer sequence and swallow\n // the very click being handled.\n setActiveAnnotationBadge(id);\n}\n\nfunction buildMemberSpec({\n member,\n badgeCenter,\n}: {\n member: ResolvedMarker;\n badgeCenter: { x: number; y: number };\n}): AnnotationFrameSpec {\n const marker = member.marker;\n return {\n id: marker.id,\n pageRect: member.rect,\n badgeCenter: clampBadgeCenter({ ...badgeCenter, isFixed: member.isFixed }),\n positioning: member.isFixed ? \"fixed\" : \"absolute\",\n colorHex: COLOR_HEX[marker.color],\n badgeLabel: String(member.displayNumber),\n badgeAriaLabel: `Page issue ${member.displayNumber}`,\n isActive: activeMarkerId === marker.id,\n onBadgeClick: () => activateMarker(marker.id),\n };\n}\n\nfunction buildClusterSpecs(cluster: MarkerCluster): AnnotationFrameSpec[] {\n const [only] = cluster.members;\n if (cluster.members.length === 1 && only) {\n return [\n buildMemberSpec({\n member: only,\n badgeCenter: { x: cluster.centerX, y: cluster.centerY },\n }),\n ];\n }\n const hub = {\n x: cluster.centerX,\n y: cluster.centerY,\n width: 0,\n height: 0,\n };\n if (expandedClusterId !== cluster.id) {\n return [\n {\n id: `cluster:${cluster.id}`,\n pageRect: hub,\n badgeCenter: clampBadgeCenter({\n x: cluster.centerX,\n y: cluster.centerY,\n isFixed: cluster.isFixed,\n }),\n positioning: cluster.isFixed ? \"fixed\" : \"absolute\",\n colorHex: getMostSevereColorHex(cluster.members),\n badgeLabel: String(cluster.members.length),\n badgeAriaLabel: `${cluster.members.length} page issues near this element`,\n isActive: false,\n onBadgeClick: () => {\n expandedClusterId = cluster.id;\n render();\n },\n },\n ];\n }\n const memberSpecs = cluster.members.map((member, index) => {\n const angle = (index / cluster.members.length) * 2 * Math.PI - Math.PI / 2;\n return buildMemberSpec({\n member,\n badgeCenter: {\n x: cluster.centerX + Math.cos(angle) * CLUSTER_FAN_RADIUS_PX,\n y: cluster.centerY + Math.sin(angle) * CLUSTER_FAN_RADIUS_PX,\n },\n });\n });\n return [\n {\n id: `cluster:${cluster.id}`,\n pageRect: hub,\n badgeCenter: clampBadgeCenter({\n x: cluster.centerX,\n y: cluster.centerY,\n isFixed: cluster.isFixed,\n }),\n positioning: cluster.isFixed ? \"fixed\" : \"absolute\",\n colorHex: \"#94a3b8\",\n badgeLabel: \"\u00D7\",\n badgeAriaLabel: \"Collapse issue cluster\",\n isActive: false,\n onBadgeClick: () => {\n expandedClusterId = null;\n render();\n },\n },\n ...memberSpecs,\n ];\n}\n\nfunction render() {\n if (markers.length === 0) {\n lastRenderSignature = null;\n clearAnnotationFrames();\n syncObservers();\n return;\n }\n const { resolved, unresolvedIds } = resolveMarkers();\n if (activeMarkerId !== null && unresolvedIds.includes(activeMarkerId)) {\n activeMarkerId = null;\n }\n // Rebuilding the node pool mid-pointer-sequence swallows clicks, so skip\n // renders whose output would be identical to what is already on screen.\n const signature = [\n resolved\n .map((entry) => {\n return `${entry.marker.id}:${Math.round(entry.rect.x)},${Math.round(entry.rect.y)}`;\n })\n .join(\"|\"),\n `active:${activeMarkerId ?? \"\"}`,\n `expanded:${expandedClusterId ?? \"\"}`,\n `unresolved:${unresolvedIds.join(\",\")}`,\n ].join(\"#\");\n if (signature === lastRenderSignature) {\n return;\n }\n lastRenderSignature = signature;\n const frames = clusterResolvedMarkers(resolved).flatMap((cluster) => {\n return buildClusterSpecs(cluster);\n });\n showAnnotationFrames({ frames });\n if (setId !== null) {\n postToEditor({\n type: \"replo:annotations:unresolved\",\n setId,\n ids: unresolvedIds,\n });\n }\n syncObservers();\n}\n\nfunction scheduleRemeasure() {\n remeasureFrameId ??= requestAnimationFrame(() => {\n remeasureFrameId = null;\n render();\n });\n}\n\nfunction scheduleDebouncedRemeasure() {\n if (remeasureDebounceId !== null) {\n clearTimeout(remeasureDebounceId);\n }\n remeasureDebounceId = setTimeout(scheduleRemeasure, REMEASURE_DEBOUNCE_MS);\n}\n\nfunction handleScroll(event: Event) {\n // Document scroll is free for absolute frames; scrolls of inner overflow\n // containers move elements relative to the page, so re-measure everything.\n if (event.target !== document) {\n scheduleRemeasure();\n }\n if (activeMarkerId !== null) {\n const id = activeMarkerId;\n activeRectFrameId ??= requestAnimationFrame(() => {\n activeRectFrameId = null;\n const viewportRect = getAnnotationBadgeViewportRect(id);\n if (viewportRect) {\n postToEditor({\n type: \"replo:annotations:activeRect\",\n id,\n viewportRect,\n });\n }\n });\n }\n}\n\nfunction syncObservers() {\n const shouldObserve = markers.length > 0;\n if (shouldObserve === observersActive) {\n return;\n }\n observersActive = shouldObserve;\n if (shouldObserve) {\n window.addEventListener(\"resize\", scheduleRemeasure);\n window.addEventListener(\"scroll\", handleScroll, { capture: true });\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver = new ResizeObserver(scheduleRemeasure);\n resizeObserver.observe(document.body);\n }\n mutationObserver = new MutationObserver((records) => {\n // Rendering our own frames mutates body's subtree; reacting to that\n // would re-render forever. Only foreign mutations can move elements.\n const container = document.getElementById(ANNOTATION_CONTAINER_ID);\n const hasForeignMutation = records.some((record) => {\n return !container || !container.contains(record.target);\n });\n if (hasForeignMutation) {\n scheduleDebouncedRemeasure();\n }\n });\n mutationObserver.observe(document.body, { childList: true, subtree: true });\n } else {\n window.removeEventListener(\"resize\", scheduleRemeasure);\n window.removeEventListener(\"scroll\", handleScroll, { capture: true });\n resizeObserver?.disconnect();\n resizeObserver = null;\n mutationObserver?.disconnect();\n mutationObserver = null;\n // A pending remeasure would otherwise fire into a torn-down document.\n if (remeasureDebounceId !== null) {\n clearTimeout(remeasureDebounceId);\n remeasureDebounceId = null;\n }\n if (remeasureFrameId !== null) {\n cancelAnimationFrame(remeasureFrameId);\n remeasureFrameId = null;\n }\n }\n}\n\nfunction handleMessage(event: MessageEvent) {\n // Origin gate BEFORE any parsing \u2014 hostile embedders never reach the schema.\n if (!isAllowedEditorMessage(event)) {\n return;\n }\n const parsed = pageAnnotationsParentMessageSchema.safeParse(event.data);\n if (!parsed.success) {\n return;\n }\n editorOrigin = event.origin;\n exhaustiveSwitch(parsed.data)({\n \"replo:annotations:set\": (message) => {\n markers = message.markers;\n setId = message.setId;\n expandedClusterId = null;\n activeMarkerId = null;\n // A fresh set always paints (and re-posts unresolved), even if geometry\n // happens to match the previous render.\n lastRenderSignature = null;\n render();\n },\n \"replo:annotations:clear\": () => {\n markers = [];\n setId = null;\n expandedClusterId = null;\n activeMarkerId = null;\n render();\n },\n \"replo:annotations:requestReady\": () => {\n postToEditor({\n type: \"replo:annotations:ready\",\n protocolVersion: PAGE_ANNOTATIONS_PROTOCOL_VERSION,\n });\n },\n });\n}\n\nexport function initAnnotationLayer() {\n if (isInitialized || typeof window === \"undefined\") {\n return;\n }\n if (window.self === window.top) {\n return;\n }\n isInitialized = true;\n window.addEventListener(\"message\", handleMessage);\n // Announce on load for iframe-reload cases where the editor isn't polling;\n // the referrer origin is verified against the same allowlist first.\n try {\n const referrerOrigin = new URL(document.referrer).origin;\n if (isAllowedEditorOrigin(referrerOrigin)) {\n window.parent.postMessage(\n {\n type: \"replo:annotations:ready\",\n protocolVersion: PAGE_ANNOTATIONS_PROTOCOL_VERSION,\n },\n referrerOrigin,\n );\n }\n } catch {\n // No usable referrer \u2014 the editor's requestReady polling covers this.\n }\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/**\n * In-iframe annotation controller: consumes declarative marker sets from the\n * editor bridge, resolves selectors against the live DOM, and drives the\n * shared annotation frame renderer. Scroll costs zero JavaScript (frames use\n * page coordinates); re-measure runs only on resize/mutation/container\n * scroll, batched per animation frame. Data-only protocol \u2014 the editor can\n * describe dots, never execute code.\n */\nimport type {\n PageAnnotationMarker,\n PageAnnotationsIframeMessage,\n} from \"schemas/generated/pageAnnotations\";\nimport type { AnnotationFrameSpec } from \"../select-mode/annotationFrames\";\n\nimport { clamp } from \"replo-utils/lib/math\";\nimport { exhaustiveSwitch } from \"replo-utils/lib/misc\";\nimport {\n PAGE_ANNOTATIONS_PROTOCOL_VERSION,\n pageAnnotationsParentMessageSchema,\n} from \"schemas/pageAnnotations\";\n\nimport {\n ANNOTATION_CONTAINER_ID,\n clearAnnotationFrames,\n getAnnotationBadgeViewportRect,\n setActiveAnnotationBadge,\n showAnnotationFrames,\n} from \"../select-mode/annotationFrames\";\nimport { getDocumentScrollOffset } from \"../select-mode/interactions/selectionGeometry\";\nimport { isAllowedEditorMessage, isAllowedEditorOrigin } from \"./bridgeGate\";\n\nconst CLUSTER_DISTANCE_PX = 24;\nconst CLUSTER_FAN_RADIUS_PX = 30;\n// Badges center on their element's top edge, so edge-hugging ones need insetting.\nconst BADGE_RADIUS_PX = 15;\nconst BADGE_EDGE_INSET_PX = BADGE_RADIUS_PX + 4;\nconst REMEASURE_DEBOUNCE_MS = 250;\n// Page-wide findings stack in a row near the top-left of the page and scroll\n// with it like any other annotation.\nconst PAGE_TOP_STACK_X_PX = 20;\nconst PAGE_TOP_STACK_Y_PX = 20;\nconst PAGE_TOP_STACK_GAP_PX = 36;\n\n// Replo brand palette (lattice --color-brand-*): the wire enum names stay\n// red/purple/amber but the rendered hues are brand magenta/purple/blue,\n// matching the editor-side dots and cards.\nconst COLOR_HEX: Record<PageAnnotationMarker[\"color\"], string> = {\n red: \"#ef196e\",\n purple: \"#8b32a8\",\n amber: \"#274ae2\",\n};\nconst COLOR_SEVERITY: PageAnnotationMarker[\"color\"][] = [\n \"red\",\n \"purple\",\n \"amber\",\n];\n\ninterface ResolvedMarker {\n marker: PageAnnotationMarker;\n element: Element | null;\n isFixed: boolean;\n rect: { x: number; y: number; width: number; height: number };\n // The editor-assigned wire number, rendered verbatim.\n displayNumber: number;\n}\n\ninterface MarkerCluster {\n id: string;\n centerX: number;\n centerY: number;\n isFixed: boolean;\n members: ResolvedMarker[];\n}\n\nlet isInitialized = false;\nlet editorOrigin: string | null = null;\nlet markers: PageAnnotationMarker[] = [];\nlet setId: string | null = null;\nlet expandedClusterId: string | null = null;\nlet activeMarkerId: string | null = null;\nlet remeasureFrameId: number | null = null;\nlet remeasureDebounceId: ReturnType<typeof setTimeout> | null = null;\nlet activeRectFrameId: number | null = null;\nlet observersActive = false;\nlet lastRenderSignature: string | null = null;\nlet resizeObserver: ResizeObserver | null = null;\nlet mutationObserver: MutationObserver | null = null;\n\nfunction postToEditor(message: PageAnnotationsIframeMessage) {\n if (editorOrigin === null) {\n return;\n }\n window.parent.postMessage(message, editorOrigin);\n}\n\nfunction hasFixedAncestor(element: Element): boolean {\n let current: Element | null = element;\n while (current !== null && current !== document.documentElement) {\n const position = getComputedStyle(current).position;\n if (position === \"fixed\" || position === \"sticky\") {\n return true;\n }\n current = current.parentElement;\n }\n return false;\n}\n\nfunction resolveMarkers(): {\n resolved: ResolvedMarker[];\n unresolvedIds: string[];\n} {\n const resolved: ResolvedMarker[] = [];\n const unresolvedIds: string[] = [];\n let pageTopSlot = 0;\n for (const marker of markers) {\n if (marker.placement === \"pageTop\") {\n // Page-wide finding: a fixed slot in the top-of-page stack, no selector.\n resolved.push({\n marker,\n element: null,\n isFixed: false,\n rect: {\n x: PAGE_TOP_STACK_X_PX + pageTopSlot * PAGE_TOP_STACK_GAP_PX,\n y: PAGE_TOP_STACK_Y_PX,\n width: 0,\n height: 0,\n },\n // The editor owns numbering; rendering the wire number keeps dots in sync with the panel.\n displayNumber: marker.number,\n });\n pageTopSlot += 1;\n continue;\n }\n let element: Element | null = null;\n try {\n element = document.querySelector(marker.selector);\n } catch {\n // Malformed selector \u2014 unresolved, never a throw.\n }\n const domRect = element?.getBoundingClientRect();\n if (!element || !domRect || (domRect.width === 0 && domRect.height === 0)) {\n unresolvedIds.push(marker.id);\n continue;\n }\n const isFixed = hasFixedAncestor(element);\n const scrollOffset = getDocumentScrollOffset();\n resolved.push({\n marker,\n element,\n isFixed,\n // Fixed frames use viewport coords; absolute frames use page coords.\n rect: {\n x: domRect.x + (isFixed ? 0 : scrollOffset.left),\n y: domRect.y + (isFixed ? 0 : scrollOffset.top),\n width: domRect.width,\n height: domRect.height,\n },\n displayNumber: marker.number,\n });\n }\n return { resolved, unresolvedIds };\n}\n\n// Greedy: a marker joins the first cluster whose badge anchor lies within\n// 24px; fixed and absolute markers never co-cluster (their coordinate spaces\n// diverge as soon as the page scrolls).\nfunction clusterResolvedMarkers(resolved: ResolvedMarker[]): MarkerCluster[] {\n const clusters: MarkerCluster[] = [];\n for (const entry of resolved) {\n const centerX = entry.rect.x + entry.rect.width / 2;\n const centerY = entry.rect.y;\n const host = clusters.find((cluster) => {\n return (\n cluster.isFixed === entry.isFixed &&\n Math.hypot(cluster.centerX - centerX, cluster.centerY - centerY) <\n CLUSTER_DISTANCE_PX\n );\n });\n if (host) {\n host.members.push(entry);\n } else {\n clusters.push({\n id: entry.marker.id,\n centerX,\n centerY,\n isFixed: entry.isFixed,\n members: [entry],\n });\n }\n }\n return clusters;\n}\n\n// Keeps a badge fully on screen: page box for absolute, viewport for fixed.\nfunction clampBadgeCenter({\n x,\n y,\n isFixed,\n}: {\n x: number;\n y: number;\n isFixed: boolean;\n}): { x: number; y: number } {\n const width = isFixed\n ? window.innerWidth\n : Math.max(document.documentElement.scrollWidth, window.innerWidth);\n const height = isFixed\n ? window.innerHeight\n : Math.max(document.documentElement.scrollHeight, window.innerHeight);\n return {\n x: clamp(\n x,\n BADGE_EDGE_INSET_PX,\n Math.max(BADGE_EDGE_INSET_PX, width - BADGE_EDGE_INSET_PX),\n ),\n y: clamp(\n y,\n BADGE_EDGE_INSET_PX,\n Math.max(BADGE_EDGE_INSET_PX, height - BADGE_EDGE_INSET_PX),\n ),\n };\n}\n\nfunction getMostSevereColorHex(members: ResolvedMarker[]): string {\n const color =\n COLOR_SEVERITY.find((candidate) => {\n return members.some((member) => member.marker.color === candidate);\n }) ?? \"amber\";\n return COLOR_HEX[color];\n}\n\nfunction activateMarker(id: string) {\n activeMarkerId = id;\n const viewportRect = getAnnotationBadgeViewportRect(id);\n if (viewportRect) {\n postToEditor({ type: \"replo:annotations:clicked\", id, viewportRect });\n }\n // Style-only: a rebuild here would race the pointer sequence and swallow\n // the very click being handled.\n setActiveAnnotationBadge(id);\n}\n\nfunction buildMemberSpec({\n member,\n badgeCenter,\n}: {\n member: ResolvedMarker;\n badgeCenter: { x: number; y: number };\n}): AnnotationFrameSpec {\n const marker = member.marker;\n return {\n id: marker.id,\n pageRect: member.rect,\n badgeCenter: clampBadgeCenter({ ...badgeCenter, isFixed: member.isFixed }),\n positioning: member.isFixed ? \"fixed\" : \"absolute\",\n colorHex: COLOR_HEX[marker.color],\n badgeLabel: String(member.displayNumber),\n badgeAriaLabel: `Page issue ${member.displayNumber}`,\n isActive: activeMarkerId === marker.id,\n onBadgeClick: () => activateMarker(marker.id),\n };\n}\n\nfunction buildClusterSpecs(cluster: MarkerCluster): AnnotationFrameSpec[] {\n const [only] = cluster.members;\n if (cluster.members.length === 1 && only) {\n return [\n buildMemberSpec({\n member: only,\n badgeCenter: { x: cluster.centerX, y: cluster.centerY },\n }),\n ];\n }\n const hub = {\n x: cluster.centerX,\n y: cluster.centerY,\n width: 0,\n height: 0,\n };\n if (expandedClusterId !== cluster.id) {\n return [\n {\n id: `cluster:${cluster.id}`,\n pageRect: hub,\n badgeCenter: clampBadgeCenter({\n x: cluster.centerX,\n y: cluster.centerY,\n isFixed: cluster.isFixed,\n }),\n positioning: cluster.isFixed ? \"fixed\" : \"absolute\",\n colorHex: getMostSevereColorHex(cluster.members),\n badgeLabel: String(cluster.members.length),\n badgeAriaLabel: `${cluster.members.length} page issues near this element`,\n isActive: false,\n onBadgeClick: () => {\n expandedClusterId = cluster.id;\n render();\n },\n },\n ];\n }\n const memberSpecs = cluster.members.map((member, index) => {\n const angle = (index / cluster.members.length) * 2 * Math.PI - Math.PI / 2;\n return buildMemberSpec({\n member,\n badgeCenter: {\n x: cluster.centerX + Math.cos(angle) * CLUSTER_FAN_RADIUS_PX,\n y: cluster.centerY + Math.sin(angle) * CLUSTER_FAN_RADIUS_PX,\n },\n });\n });\n return [\n {\n id: `cluster:${cluster.id}`,\n pageRect: hub,\n badgeCenter: clampBadgeCenter({\n x: cluster.centerX,\n y: cluster.centerY,\n isFixed: cluster.isFixed,\n }),\n positioning: cluster.isFixed ? \"fixed\" : \"absolute\",\n colorHex: \"#94a3b8\",\n badgeLabel: \"\u00D7\",\n badgeAriaLabel: \"Collapse issue cluster\",\n isActive: false,\n onBadgeClick: () => {\n expandedClusterId = null;\n render();\n },\n },\n ...memberSpecs,\n ];\n}\n\nfunction render() {\n if (markers.length === 0) {\n lastRenderSignature = null;\n clearAnnotationFrames();\n syncObservers();\n return;\n }\n const { resolved, unresolvedIds } = resolveMarkers();\n if (activeMarkerId !== null && unresolvedIds.includes(activeMarkerId)) {\n activeMarkerId = null;\n }\n // Rebuilding the node pool mid-pointer-sequence swallows clicks, so skip\n // renders whose output would be identical to what is already on screen.\n const signature = [\n resolved\n .map((entry) => {\n return `${entry.marker.id}:${Math.round(entry.rect.x)},${Math.round(entry.rect.y)}`;\n })\n .join(\"|\"),\n `active:${activeMarkerId ?? \"\"}`,\n `expanded:${expandedClusterId ?? \"\"}`,\n `unresolved:${unresolvedIds.join(\",\")}`,\n ].join(\"#\");\n if (signature === lastRenderSignature) {\n return;\n }\n lastRenderSignature = signature;\n const frames = clusterResolvedMarkers(resolved).flatMap((cluster) => {\n return buildClusterSpecs(cluster);\n });\n showAnnotationFrames({ frames });\n if (setId !== null) {\n postToEditor({\n type: \"replo:annotations:unresolved\",\n setId,\n ids: unresolvedIds,\n });\n }\n syncObservers();\n}\n\nfunction scheduleRemeasure() {\n remeasureFrameId ??= requestAnimationFrame(() => {\n remeasureFrameId = null;\n render();\n });\n}\n\nfunction scheduleDebouncedRemeasure() {\n if (remeasureDebounceId !== null) {\n clearTimeout(remeasureDebounceId);\n }\n remeasureDebounceId = setTimeout(scheduleRemeasure, REMEASURE_DEBOUNCE_MS);\n}\n\nfunction handleScroll(event: Event) {\n // Document scroll is free for absolute frames; scrolls of inner overflow\n // containers move elements relative to the page, so re-measure everything.\n if (event.target !== document) {\n scheduleRemeasure();\n }\n if (activeMarkerId !== null) {\n const id = activeMarkerId;\n activeRectFrameId ??= requestAnimationFrame(() => {\n activeRectFrameId = null;\n const viewportRect = getAnnotationBadgeViewportRect(id);\n if (viewportRect) {\n postToEditor({\n type: \"replo:annotations:activeRect\",\n id,\n viewportRect,\n });\n }\n });\n }\n}\n\nfunction syncObservers() {\n const shouldObserve = markers.length > 0;\n if (shouldObserve === observersActive) {\n return;\n }\n observersActive = shouldObserve;\n if (shouldObserve) {\n window.addEventListener(\"resize\", scheduleRemeasure);\n window.addEventListener(\"scroll\", handleScroll, { capture: true });\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver = new ResizeObserver(scheduleRemeasure);\n resizeObserver.observe(document.body);\n }\n mutationObserver = new MutationObserver((records) => {\n // Rendering our own frames mutates body's subtree; reacting to that\n // would re-render forever. Only foreign mutations can move elements.\n const container = document.getElementById(ANNOTATION_CONTAINER_ID);\n const hasForeignMutation = records.some((record) => {\n return !container || !container.contains(record.target);\n });\n if (hasForeignMutation) {\n scheduleDebouncedRemeasure();\n }\n });\n mutationObserver.observe(document.body, { childList: true, subtree: true });\n } else {\n window.removeEventListener(\"resize\", scheduleRemeasure);\n window.removeEventListener(\"scroll\", handleScroll, { capture: true });\n resizeObserver?.disconnect();\n resizeObserver = null;\n mutationObserver?.disconnect();\n mutationObserver = null;\n // A pending remeasure would otherwise fire into a torn-down document.\n if (remeasureDebounceId !== null) {\n clearTimeout(remeasureDebounceId);\n remeasureDebounceId = null;\n }\n if (remeasureFrameId !== null) {\n cancelAnimationFrame(remeasureFrameId);\n remeasureFrameId = null;\n }\n }\n}\n\nfunction handleMessage(event: MessageEvent) {\n // Origin gate BEFORE any parsing \u2014 hostile embedders never reach the schema.\n if (!isAllowedEditorMessage(event)) {\n return;\n }\n const parsed = pageAnnotationsParentMessageSchema.safeParse(event.data);\n if (!parsed.success) {\n return;\n }\n editorOrigin = event.origin;\n exhaustiveSwitch(parsed.data)({\n \"replo:annotations:set\": (message) => {\n markers = message.markers;\n setId = message.setId;\n expandedClusterId = null;\n activeMarkerId = null;\n // A fresh set always paints (and re-posts unresolved), even if geometry\n // happens to match the previous render.\n lastRenderSignature = null;\n render();\n },\n \"replo:annotations:clear\": () => {\n markers = [];\n setId = null;\n expandedClusterId = null;\n activeMarkerId = null;\n render();\n },\n \"replo:annotations:requestReady\": () => {\n postToEditor({\n type: \"replo:annotations:ready\",\n protocolVersion: PAGE_ANNOTATIONS_PROTOCOL_VERSION,\n });\n },\n });\n}\n\nexport function initAnnotationLayer() {\n if (isInitialized || typeof window === \"undefined\") {\n return;\n }\n if (window.self === window.top) {\n return;\n }\n isInitialized = true;\n window.addEventListener(\"message\", handleMessage);\n // Announce on load for iframe-reload cases where the editor isn't polling;\n // the referrer origin is verified against the same allowlist first.\n try {\n const referrerOrigin = new URL(document.referrer).origin;\n if (isAllowedEditorOrigin(referrerOrigin)) {\n window.parent.postMessage(\n {\n type: \"replo:annotations:ready\",\n protocolVersion: PAGE_ANNOTATIONS_PROTOCOL_VERSION,\n },\n referrerOrigin,\n );\n }\n } catch {\n // No usable referrer \u2014 the editor's requestReady polling covers this.\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
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.5.1",
|
|
4
4
|
"branch": "HEAD",
|
|
5
|
-
"commit": "
|
|
6
|
-
"builtAt": "2026-
|
|
5
|
+
"commit": "46993e27f13bdc44fb8069250240b6cdae88a508",
|
|
6
|
+
"builtAt": "2026-09-04T14:54:15.479Z"
|
|
7
7
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { isObjectLike_default, baseGetTag_default } from './chunk-G3S6ZNPF.mjs';
|
|
2
|
+
|
|
3
|
+
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/isNumber.js
|
|
4
|
+
var numberTag = "[object Number]";
|
|
5
|
+
function isNumber(value) {
|
|
6
|
+
return typeof value == "number" || isObjectLike_default(value) && baseGetTag_default(value) == numberTag;
|
|
7
|
+
}
|
|
8
|
+
var isNumber_default = isNumber;
|
|
9
|
+
|
|
10
|
+
export { isNumber_default };
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/isArray.js
|
|
2
|
-
var isArray = Array.isArray;
|
|
3
|
-
var isArray_default = isArray;
|
|
4
|
-
|
|
5
1
|
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_freeGlobal.js
|
|
6
2
|
var freeGlobal = typeof global == "object" && global && global.Object === Object && global;
|
|
7
3
|
var freeGlobal_default = freeGlobal;
|
|
@@ -65,4 +61,4 @@ function isObjectLike(value) {
|
|
|
65
61
|
}
|
|
66
62
|
var isObjectLike_default = isObjectLike;
|
|
67
63
|
|
|
68
|
-
export { Symbol_default, baseGetTag_default, freeGlobal_default,
|
|
64
|
+
export { Symbol_default, baseGetTag_default, freeGlobal_default, isObjectLike_default, root_default };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isArray_default } from './chunk-TESFJQID.mjs';
|
|
2
|
+
import { root_default, isObjectLike_default, freeGlobal_default, baseGetTag_default, Symbol_default } from './chunk-G3S6ZNPF.mjs';
|
|
2
3
|
|
|
3
4
|
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_listCacheClear.js
|
|
4
5
|
function listCacheClear() {
|
|
@@ -1139,13 +1140,6 @@ function isEmpty(value) {
|
|
|
1139
1140
|
}
|
|
1140
1141
|
var isEmpty_default = isEmpty;
|
|
1141
1142
|
|
|
1142
|
-
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/isNumber.js
|
|
1143
|
-
var numberTag4 = "[object Number]";
|
|
1144
|
-
function isNumber(value) {
|
|
1145
|
-
return typeof value == "number" || isObjectLike_default(value) && baseGetTag_default(value) == numberTag4;
|
|
1146
|
-
}
|
|
1147
|
-
var isNumber_default = isNumber;
|
|
1148
|
-
|
|
1149
1143
|
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/isString.js
|
|
1150
1144
|
var stringTag4 = "[object String]";
|
|
1151
1145
|
function isString(value) {
|
|
@@ -1532,4 +1526,4 @@ function errorMessage(error) {
|
|
|
1532
1526
|
return String(error);
|
|
1533
1527
|
}
|
|
1534
1528
|
|
|
1535
|
-
export { coerceNumberToString, deepCloneAndMergeReplacingArrays, errorMessage, exhaustiveSwitch, formatFileSize, getFromRecordOrNull, hasOwnProperty12 as hasOwnProperty, invariant, isEmpty2 as isEmpty, isFunction_default, isNotNullish, isNullish,
|
|
1529
|
+
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 };
|
package/next/config.d.ts
CHANGED
package/next/config.js
CHANGED
|
@@ -1,18 +1,31 @@
|
|
|
1
|
+
import { existsSync } from "fs";
|
|
2
|
+
import path from "path";
|
|
1
3
|
const DEFAULT_PAGE_EXTENSIONS = ["tsx", "ts", "jsx", "js"];
|
|
4
|
+
const DEVELOPMENT_ONLY_PAGE_EXTENSIONS = ["dev.jsx", "dev.tsx"];
|
|
5
|
+
const BUILD_ONLY_PAGE_EXTENSIONS = ["published.tsx"];
|
|
6
|
+
const PUBLISH_TSCONFIG_FILENAME = "tsconfig.publish.json";
|
|
2
7
|
function getPageExtensions(configuredExtensions) {
|
|
3
|
-
const
|
|
8
|
+
const baseExtensions = (configuredExtensions ?? DEFAULT_PAGE_EXTENSIONS).filter((extension) => {
|
|
9
|
+
return !DEVELOPMENT_ONLY_PAGE_EXTENSIONS.includes(extension) && !BUILD_ONLY_PAGE_EXTENSIONS.includes(extension);
|
|
10
|
+
});
|
|
11
|
+
return process.env.BUILD_ENV === "build" ? [...BUILD_ONLY_PAGE_EXTENSIONS, ...baseExtensions] : [...DEVELOPMENT_ONLY_PAGE_EXTENSIONS, ...baseExtensions];
|
|
12
|
+
}
|
|
13
|
+
function getTypescriptConfig(configured) {
|
|
4
14
|
if (process.env.BUILD_ENV !== "build") {
|
|
5
|
-
return
|
|
15
|
+
return configured;
|
|
6
16
|
}
|
|
7
|
-
|
|
17
|
+
const tsconfigPath = path.join(process.cwd(), PUBLISH_TSCONFIG_FILENAME);
|
|
18
|
+
return existsSync(tsconfigPath) ? { ...configured, tsconfigPath: PUBLISH_TSCONFIG_FILENAME } : configured;
|
|
8
19
|
}
|
|
9
20
|
function withComponentPreviewPageExtension(config) {
|
|
10
21
|
return {
|
|
11
22
|
...config,
|
|
12
|
-
pageExtensions: getPageExtensions(config.pageExtensions)
|
|
23
|
+
pageExtensions: getPageExtensions(config.pageExtensions),
|
|
24
|
+
typescript: getTypescriptConfig(config.typescript)
|
|
13
25
|
};
|
|
14
26
|
}
|
|
15
27
|
export {
|
|
28
|
+
PUBLISH_TSCONFIG_FILENAME,
|
|
16
29
|
withComponentPreviewPageExtension
|
|
17
30
|
};
|
|
18
31
|
//# sourceMappingURL=config.js.map
|
package/next/config.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../next/config.ts"],
|
|
4
|
-
"sourcesContent": ["import type { NextConfig } from \"next\";\n\nconst DEFAULT_PAGE_EXTENSIONS = [\"tsx\", \"ts\", \"jsx\", \"js\"];\n\nfunction getPageExtensions(configuredExtensions: string[] | undefined) {\n const
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { existsSync } from \"fs\";\nimport path from \"path\";\nimport type { NextConfig } from \"next\";\n\nconst DEFAULT_PAGE_EXTENSIONS = [\"tsx\", \"ts\", \"jsx\", \"js\"];\n// Routed only by the sandbox dev server: site-component previews and page drafts.\nconst DEVELOPMENT_ONLY_PAGE_EXTENSIONS = [\"dev.jsx\", \"dev.tsx\"];\n// Routed only by publish builds: the live copy of every page.\nconst BUILD_ONLY_PAGE_EXTENSIONS = [\"published.tsx\"];\nexport const PUBLISH_TSCONFIG_FILENAME = \"tsconfig.publish.json\";\n\nfunction getPageExtensions(configuredExtensions: string[] | undefined) {\n const baseExtensions = (\n configuredExtensions ?? DEFAULT_PAGE_EXTENSIONS\n ).filter((extension) => {\n return (\n !DEVELOPMENT_ONLY_PAGE_EXTENSIONS.includes(extension) &&\n !BUILD_ONLY_PAGE_EXTENSIONS.includes(extension)\n );\n });\n\n return process.env.BUILD_ENV === \"build\"\n ? [...BUILD_ONLY_PAGE_EXTENSIONS, ...baseExtensions]\n : [...DEVELOPMENT_ONLY_PAGE_EXTENSIONS, ...baseExtensions];\n}\n\n// Drafts are excluded from the publish typecheck so unfinished pages never block a publish; live files stay checked.\nfunction getTypescriptConfig(configured: NextConfig[\"typescript\"]) {\n if (process.env.BUILD_ENV !== \"build\") {\n return configured;\n }\n\n const tsconfigPath = path.join(process.cwd(), PUBLISH_TSCONFIG_FILENAME);\n return existsSync(tsconfigPath)\n ? { ...configured, tsconfigPath: PUBLISH_TSCONFIG_FILENAME }\n : configured;\n}\n\nexport function withComponentPreviewPageExtension(\n config: NextConfig,\n): NextConfig {\n return {\n ...config,\n pageExtensions: getPageExtensions(config.pageExtensions),\n typescript: getTypescriptConfig(config.typescript),\n };\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,kBAAkB;AAC3B,OAAO,UAAU;AAGjB,MAAM,0BAA0B,CAAC,OAAO,MAAM,OAAO,IAAI;AAEzD,MAAM,mCAAmC,CAAC,WAAW,SAAS;AAE9D,MAAM,6BAA6B,CAAC,eAAe;AAC5C,MAAM,4BAA4B;AAEzC,SAAS,kBAAkB,sBAA4C;AACrE,QAAM,kBACJ,wBAAwB,yBACxB,OAAO,CAAC,cAAc;AACtB,WACE,CAAC,iCAAiC,SAAS,SAAS,KACpD,CAAC,2BAA2B,SAAS,SAAS;AAAA,EAElD,CAAC;AAED,SAAO,QAAQ,IAAI,cAAc,UAC7B,CAAC,GAAG,4BAA4B,GAAG,cAAc,IACjD,CAAC,GAAG,kCAAkC,GAAG,cAAc;AAC7D;AAGA,SAAS,oBAAoB,YAAsC;AACjE,MAAI,QAAQ,IAAI,cAAc,SAAS;AACrC,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,KAAK,KAAK,QAAQ,IAAI,GAAG,yBAAyB;AACvE,SAAO,WAAW,YAAY,IAC1B,EAAE,GAAG,YAAY,cAAc,0BAA0B,IACzD;AACN;AAEO,SAAS,kCACd,QACY;AACZ,SAAO;AAAA,IACL,GAAG;AAAA,IACH,gBAAgB,kBAAkB,OAAO,cAAc;AAAA,IACvD,YAAY,oBAAoB,OAAO,UAAU;AAAA,EACnD;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@replohq/sandbox-runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"reploBuild": {
|
|
5
5
|
"packageName": "@replohq/sandbox-runtime",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.5.1",
|
|
7
7
|
"branch": "HEAD",
|
|
8
|
-
"commit": "
|
|
9
|
-
"builtAt": "2026-
|
|
8
|
+
"commit": "46993e27f13bdc44fb8069250240b6cdae88a508",
|
|
9
|
+
"builtAt": "2026-09-04T14:54:15.479Z"
|
|
10
10
|
},
|
|
11
11
|
"description": "Replo sandbox runtime — editor integration for agent-built Next.js sites.",
|
|
12
12
|
"license": "SEE LICENSE IN LICENSE",
|
package/posthog-relay.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
3
|
import { startChildPostHogReplaySnapshotRelay } from "./_vendor/replo-utils/lib/posthogReplay.mjs";
|
|
4
|
-
const
|
|
4
|
+
const POSTHOG_PUBLIC_KEY = "phc_dRETsj85vNkKrvQQszEFRBibH5kUF9l7rbjACuKPanZ";
|
|
5
5
|
const POSTHOG_API_HOST = "https://d99bbtllqsaif.cloudfront.net";
|
|
6
6
|
let initPromise = null;
|
|
7
7
|
function loadPostHogScript() {
|
|
@@ -35,7 +35,7 @@ function initPostHogRelay() {
|
|
|
35
35
|
console.warn("[PostHog Relay] PostHog not available after script load");
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
|
-
window.posthog.init(
|
|
38
|
+
window.posthog.init(POSTHOG_PUBLIC_KEY, {
|
|
39
39
|
api_host: POSTHOG_API_HOST,
|
|
40
40
|
persistence: "localStorage",
|
|
41
41
|
autocapture: false,
|
package/posthog-relay.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../posthog-relay.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\";\n\n/**\n * PostHog Cross-Origin Iframe Relay\n *\n * Initializes PostHog inside the sandbox iframe as a \"relay\" - it captures DOM\n * mutations via rrweb and sends them to the parent window's PostHog instance\n * via postMessage, enabling session recordings to include iframe content.\n */\nimport { useEffect } from \"react\";\n\nimport { startChildPostHogReplaySnapshotRelay } from \"replo-utils/lib/posthogReplay\";\n\nconst
|
|
5
|
-
"mappings": ";AASA,SAAS,iBAAiB;AAE1B,SAAS,4CAA4C;AAErD,MAAM,
|
|
4
|
+
"sourcesContent": ["\"use client\";\n\n/**\n * PostHog Cross-Origin Iframe Relay\n *\n * Initializes PostHog inside the sandbox iframe as a \"relay\" - it captures DOM\n * mutations via rrweb and sends them to the parent window's PostHog instance\n * via postMessage, enabling session recordings to include iframe content.\n */\nimport { useEffect } from \"react\";\n\nimport { startChildPostHogReplaySnapshotRelay } from \"replo-utils/lib/posthogReplay\";\n\nconst POSTHOG_PUBLIC_KEY = \"phc_dRETsj85vNkKrvQQszEFRBibH5kUF9l7rbjACuKPanZ\";\nconst POSTHOG_API_HOST = \"https://d99bbtllqsaif.cloudfront.net\";\n\ninterface PostHog {\n init: (apiKey: string, config: Record<string, unknown>) => void;\n sessionRecordingStarted: () => boolean;\n}\n\ndeclare global {\n interface Window {\n posthog?: PostHog;\n }\n}\n\nlet initPromise: Promise<void> | null = null;\n\nfunction loadPostHogScript() {\n return new Promise<void>((resolve) => {\n if (window.posthog) {\n resolve();\n return;\n }\n\n const script = document.createElement(\"script\");\n script.src = `${POSTHOG_API_HOST}/static/array.js`;\n script.async = true;\n script.addEventListener(\"load\", () => resolve());\n script.addEventListener(\"error\", () => {\n resolve();\n console.warn(\"[PostHog Relay] Failed to load PostHog script\");\n });\n document.head.append(script);\n });\n}\n\nfunction initPostHogRelay() {\n if (window.self === window.top) {\n return Promise.resolve();\n }\n\n if (initPromise) {\n return initPromise;\n }\n\n initPromise = (async () => {\n await loadPostHogScript();\n\n if (!window.posthog) {\n initPromise = null;\n console.warn(\"[PostHog Relay] PostHog not available after script load\");\n return;\n }\n\n window.posthog.init(POSTHOG_PUBLIC_KEY, {\n api_host: POSTHOG_API_HOST,\n persistence: \"localStorage\",\n autocapture: false,\n // (Noah, 2026-07-14, REPL-29305): Disable performance capture \u2014 its PerformanceObserver\n // relays a non-cloneable PerformanceServerTiming across the iframe boundary via postMessage,\n // throwing a DataCloneError in the preview. Matches the bedrock-sdk relay.\n capture_performance: false,\n capture_pageview: false,\n session_recording: {\n recordCrossOriginIframes: true,\n // NOTE (Nick, 2026-02-02, REPL-23284): Disable canvas recording to prevent SecurityError on tainted canvases\n captureCanvas: { recordCanvas: false },\n },\n });\n })();\n\n return initPromise;\n}\n\nexport default function PostHogRelay() {\n // eslint-disable-next-line replo/no-use-effect -- External replay script and cross-frame listener lifecycle.\n useEffect(() => {\n const stopRelayingSnapshotRequests = startChildPostHogReplaySnapshotRelay({\n isRecordingStarted: () => {\n return window.posthog?.sessionRecordingStarted() ?? false;\n },\n targetWindow: window,\n });\n void initPostHogRelay();\n return stopRelayingSnapshotRequests;\n }, []);\n\n return null;\n}\n"],
|
|
5
|
+
"mappings": ";AASA,SAAS,iBAAiB;AAE1B,SAAS,4CAA4C;AAErD,MAAM,qBAAqB;AAC3B,MAAM,mBAAmB;AAazB,IAAI,cAAoC;AAExC,SAAS,oBAAoB;AAC3B,SAAO,IAAI,QAAc,CAAC,YAAY;AACpC,QAAI,OAAO,SAAS;AAClB,cAAQ;AACR;AAAA,IACF;AAEA,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,MAAM,GAAG,gBAAgB;AAChC,WAAO,QAAQ;AACf,WAAO,iBAAiB,QAAQ,MAAM,QAAQ,CAAC;AAC/C,WAAO,iBAAiB,SAAS,MAAM;AACrC,cAAQ;AACR,cAAQ,KAAK,+CAA+C;AAAA,IAC9D,CAAC;AACD,aAAS,KAAK,OAAO,MAAM;AAAA,EAC7B,CAAC;AACH;AAEA,SAAS,mBAAmB;AAC1B,MAAI,OAAO,SAAS,OAAO,KAAK;AAC9B,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAEA,MAAI,aAAa;AACf,WAAO;AAAA,EACT;AAEA,iBAAe,YAAY;AACzB,UAAM,kBAAkB;AAExB,QAAI,CAAC,OAAO,SAAS;AACnB,oBAAc;AACd,cAAQ,KAAK,yDAAyD;AACtE;AAAA,IACF;AAEA,WAAO,QAAQ,KAAK,oBAAoB;AAAA,MACtC,UAAU;AAAA,MACV,aAAa;AAAA,MACb,aAAa;AAAA;AAAA;AAAA;AAAA,MAIb,qBAAqB;AAAA,MACrB,kBAAkB;AAAA,MAClB,mBAAmB;AAAA,QACjB,0BAA0B;AAAA;AAAA,QAE1B,eAAe,EAAE,cAAc,MAAM;AAAA,MACvC;AAAA,IACF,CAAC;AAAA,EACH,GAAG;AAEH,SAAO;AACT;AAEe,SAAR,eAAgC;AAErC,YAAU,MAAM;AACd,UAAM,+BAA+B,qCAAqC;AAAA,MACxE,oBAAoB,MAAM;AACxB,eAAO,OAAO,SAAS,wBAAwB,KAAK;AAAA,MACtD;AAAA,MACA,cAAc;AAAA,IAChB,CAAC;AACD,SAAK,iBAAiB;AACtB,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AAEL,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|