@replohq/sandbox-runtime 1.7.12 → 1.8.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/_vendor/replo-utils/lib/errors.d.ts +1 -0
- package/_vendor/schemas/componentPreview.d.ts +16 -0
- package/_vendor/schemas/componentPreview.mjs +12 -3
- package/_vendor/schemas/errors.d.ts +7 -0
- package/_vendor/schemas/generated/componentPreview.d.ts +9 -0
- package/_vendor/schemas/generated/selectMode.d.ts +25 -0
- package/_vendor/schemas/iframeRpcChannel.mjs +46 -11
- package/_vendor/schemas/selectMode.d.ts +99 -0
- package/_vendor/schemas/selectMode.mjs +1 -1
- package/_vendor/schemas/selectModeIframeRpc.d.ts +280 -2
- package/_vendor/schemas/selectModeIframeRpc.mjs +42 -36
- package/_vendor/schemas/selectModeImage.d.ts +9 -0
- package/_vendor/schemas/selectModeImage.mjs +24 -0
- package/buildMetadata.js +3 -3
- package/{chunk-4T7WSMC3.mjs → chunk-XWNBKE63.mjs} +25 -1
- package/package.json +4 -4
- package/select-mode/descriptor/elementDescriptor.js +2 -0
- package/select-mode/hooks/useOptimisticOverrides.js +63 -12
- package/select-mode/hooks/useSelectionInteractions.js +1 -10
- package/select-mode/iframeRpcChannel.d.ts +45 -3
- package/select-mode/iframeRpcChannel.js +4 -4
- package/select-mode/imageOverridePreview.d.ts +6 -0
- package/select-mode/imageOverridePreview.js +77 -0
- package/select-mode/imageSources.d.ts +3 -0
- package/select-mode/imageSources.js +68 -0
- package/select-mode/selectModeStore.d.ts +6 -11
- package/select-mode/selectModeStore.js +7 -8
- package/select-mode/selectionFrame.d.ts +2 -8
- package/select-mode/selectionFrame.js +2 -15
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
2
|
import { buildGoogleFontsCssUrl, quoteFontFamily } from "../../_vendor/replo-utils/lib/fonts.mjs";
|
|
3
3
|
import { buildSelector } from "../descriptor/elementDescriptor";
|
|
4
|
+
import { createImagePreview } from "../imageOverridePreview";
|
|
4
5
|
import {
|
|
5
6
|
SELECT_MODE_OVERRIDE_ATTRIBUTE,
|
|
6
7
|
SELECT_MODE_OVERRIDE_STYLE_ATTRIBUTE
|
|
@@ -10,6 +11,7 @@ const OVERRIDE_REGISTRATION_TIMEOUT_MS = 15e3;
|
|
|
10
11
|
const SESSION_OUTCOME_RETENTION_MS = 6e4;
|
|
11
12
|
const OVERRIDE_REGISTRY_KEY = "reploSelectOverrideRegistryV2";
|
|
12
13
|
const SESSION_OUTCOME_REGISTRY_KEY = "reploSelectOverrideSessionOutcomesV2";
|
|
14
|
+
const imagePreviews = /* @__PURE__ */ new Map();
|
|
13
15
|
function getOverrideList() {
|
|
14
16
|
const existing = Reflect.get(window, OVERRIDE_REGISTRY_KEY);
|
|
15
17
|
if (Array.isArray(existing)) {
|
|
@@ -45,18 +47,12 @@ function getElementsForTargetMode({
|
|
|
45
47
|
})
|
|
46
48
|
];
|
|
47
49
|
}
|
|
48
|
-
function isImageSourcePayload({
|
|
49
|
-
element,
|
|
50
|
-
payload
|
|
51
|
-
}) {
|
|
52
|
-
return payload.kind === "attribute" && payload.name.toLowerCase() === "src" && element instanceof HTMLImageElement;
|
|
53
|
-
}
|
|
54
50
|
function getOriginalState({
|
|
55
51
|
element,
|
|
56
52
|
payload
|
|
57
53
|
}) {
|
|
58
54
|
if (payload.kind === "attribute") {
|
|
59
|
-
const names =
|
|
55
|
+
const names = [payload.name];
|
|
60
56
|
return {
|
|
61
57
|
kind: "attributes",
|
|
62
58
|
values: names.map((name) => ({
|
|
@@ -95,14 +91,12 @@ function applyPayload({
|
|
|
95
91
|
payload,
|
|
96
92
|
token
|
|
97
93
|
}) {
|
|
94
|
+
if (payload.kind === "image") return;
|
|
98
95
|
if (payload.kind === "css") {
|
|
99
96
|
addToken(element, token);
|
|
100
97
|
return;
|
|
101
98
|
}
|
|
102
99
|
if (payload.kind === "attribute") {
|
|
103
|
-
if (isImageSourcePayload({ element, payload })) {
|
|
104
|
-
element.removeAttribute("srcset");
|
|
105
|
-
}
|
|
106
100
|
element.setAttribute(payload.name, payload.value);
|
|
107
101
|
return;
|
|
108
102
|
}
|
|
@@ -207,6 +201,7 @@ function reapplyOverride({
|
|
|
207
201
|
override,
|
|
208
202
|
skipElement
|
|
209
203
|
}) {
|
|
204
|
+
if (override.payload.kind === "image") return;
|
|
210
205
|
for (const target of override.targets) {
|
|
211
206
|
const element = resolveTarget(target);
|
|
212
207
|
if (!element || skipElement && element.contains(skipElement)) {
|
|
@@ -222,6 +217,51 @@ function reapplyOverride({
|
|
|
222
217
|
applyPayload({ element, payload: override.payload, token: override.token });
|
|
223
218
|
}
|
|
224
219
|
}
|
|
220
|
+
function syncImagePreviews() {
|
|
221
|
+
const configurations = /* @__PURE__ */ new Map();
|
|
222
|
+
for (const override of getOverrideList()) {
|
|
223
|
+
if (override.payload.kind !== "image") continue;
|
|
224
|
+
for (const target of override.targets) {
|
|
225
|
+
const element = resolveTarget(target);
|
|
226
|
+
if (!(element instanceof HTMLImageElement)) continue;
|
|
227
|
+
const previous = configurations.get(element);
|
|
228
|
+
configurations.set(element, {
|
|
229
|
+
image: override.payload.image,
|
|
230
|
+
pending: !override.settled || Boolean(previous?.pending)
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
for (const [element, preview] of imagePreviews) {
|
|
235
|
+
if (configurations.has(element)) continue;
|
|
236
|
+
preview.dispose();
|
|
237
|
+
imagePreviews.delete(element);
|
|
238
|
+
}
|
|
239
|
+
const acknowledged = /* @__PURE__ */ new Set();
|
|
240
|
+
for (const [element, configuration] of configurations) {
|
|
241
|
+
let preview = imagePreviews.get(element);
|
|
242
|
+
if (!preview) {
|
|
243
|
+
preview = createImagePreview(element);
|
|
244
|
+
imagePreviews.set(element, preview);
|
|
245
|
+
}
|
|
246
|
+
if (!configuration.pending && preview.isPersisted(configuration.image)) {
|
|
247
|
+
preview.dispose();
|
|
248
|
+
imagePreviews.delete(element);
|
|
249
|
+
acknowledged.add(element);
|
|
250
|
+
} else {
|
|
251
|
+
preview.render(configuration.image);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
setOverrideList(
|
|
255
|
+
getOverrideList().filter((entry) => {
|
|
256
|
+
if (entry.payload.kind !== "image") return true;
|
|
257
|
+
entry.targets = entry.targets.filter((target) => {
|
|
258
|
+
const element = resolveTarget(target);
|
|
259
|
+
return !element || !acknowledged.has(element);
|
|
260
|
+
});
|
|
261
|
+
return entry.targets.length > 0;
|
|
262
|
+
})
|
|
263
|
+
);
|
|
264
|
+
}
|
|
225
265
|
function removeOverride({
|
|
226
266
|
editId,
|
|
227
267
|
outcome
|
|
@@ -233,6 +273,16 @@ function removeOverride({
|
|
|
233
273
|
return;
|
|
234
274
|
}
|
|
235
275
|
clearTimeout(override.registrationTimeout);
|
|
276
|
+
if (override.payload.kind === "image") {
|
|
277
|
+
override.settled = true;
|
|
278
|
+
setOverrideList(
|
|
279
|
+
getOverrideList().filter(
|
|
280
|
+
(entry) => entry !== override || outcome === "persisted"
|
|
281
|
+
)
|
|
282
|
+
);
|
|
283
|
+
syncImagePreviews();
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
236
286
|
getStyleElement(override.token)?.remove();
|
|
237
287
|
for (const target of override.targets) {
|
|
238
288
|
const element = resolveTarget(target);
|
|
@@ -279,6 +329,7 @@ function useOptimisticOverrides() {
|
|
|
279
329
|
});
|
|
280
330
|
const token = `override-${editId}`;
|
|
281
331
|
const override = {
|
|
332
|
+
settled: false,
|
|
282
333
|
editId,
|
|
283
334
|
injectedFontFamily: null,
|
|
284
335
|
payload,
|
|
@@ -286,7 +337,6 @@ function useOptimisticOverrides() {
|
|
|
286
337
|
removeOverride({ editId, outcome: "failed" });
|
|
287
338
|
}, OVERRIDE_REGISTRATION_TIMEOUT_MS),
|
|
288
339
|
sessionId: null,
|
|
289
|
-
state: "live",
|
|
290
340
|
targets: elements.map((element) => ({
|
|
291
341
|
element,
|
|
292
342
|
originalState: getOriginalState({ element, payload }),
|
|
@@ -297,6 +347,7 @@ function useOptimisticOverrides() {
|
|
|
297
347
|
getOverrideList().push(override);
|
|
298
348
|
installCssRule(override);
|
|
299
349
|
reapplyOverride({ override, skipElement: null });
|
|
350
|
+
syncImagePreviews();
|
|
300
351
|
},
|
|
301
352
|
[]
|
|
302
353
|
);
|
|
@@ -305,6 +356,7 @@ function useOptimisticOverrides() {
|
|
|
305
356
|
for (const override of getOverrideList()) {
|
|
306
357
|
reapplyOverride({ override, skipElement });
|
|
307
358
|
}
|
|
359
|
+
syncImagePreviews();
|
|
308
360
|
},
|
|
309
361
|
[]
|
|
310
362
|
);
|
|
@@ -317,7 +369,6 @@ function useOptimisticOverrides() {
|
|
|
317
369
|
}
|
|
318
370
|
clearTimeout(override.registrationTimeout);
|
|
319
371
|
override.sessionId = sessionId;
|
|
320
|
-
override.state = "fired";
|
|
321
372
|
const outcome = getSessionOutcomes().get(sessionId);
|
|
322
373
|
if (outcome) {
|
|
323
374
|
removeOverride({ editId, outcome });
|
|
@@ -402,16 +402,7 @@ function useSelectionInteractions() {
|
|
|
402
402
|
const scrollOffset = snapshot.type === "drag" ? snapshot.scrollOffset : getDocumentScrollOffset();
|
|
403
403
|
const candidates = store().dragCandidates;
|
|
404
404
|
resetDragState({ notifyParentDragCleared: false });
|
|
405
|
-
if (
|
|
406
|
-
const targetElement = event.target;
|
|
407
|
-
if (!(targetElement instanceof Element)) {
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
commitClickSelection({
|
|
411
|
-
targetElement,
|
|
412
|
-
pointerEvent: event,
|
|
413
|
-
candidates
|
|
414
|
-
});
|
|
405
|
+
if (snapshot.type !== "drag" || !dragStart) {
|
|
415
406
|
return;
|
|
416
407
|
}
|
|
417
408
|
const resolvedDragCurrent = dragCurrent ?? dragStart;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { SelectedElement } from "../_vendor/schemas/generated/selectMode";
|
|
2
2
|
import type { IframeRpcChannelInputHandlers, IframeRpcChannelRemote } from "../_vendor/schemas/iframeRpcChannel";
|
|
3
|
-
import {
|
|
4
|
-
export type WebsiteBuilderRpcRemote = IframeRpcChannelRemote<typeof
|
|
5
|
-
type SelectModeParentMessagingHandlers = Omit<IframeRpcChannelInputHandlers<typeof
|
|
3
|
+
import { previewIframeToWebsiteBuilderMethodConfig, websiteBuilderToPreviewIframeMethodConfig } from "../_vendor/schemas/selectModeIframeRpc";
|
|
4
|
+
export type WebsiteBuilderRpcRemote = IframeRpcChannelRemote<typeof previewIframeToWebsiteBuilderMethodConfig>;
|
|
5
|
+
type SelectModeParentMessagingHandlers = Omit<IframeRpcChannelInputHandlers<typeof websiteBuilderToPreviewIframeMethodConfig>, "symbolicateSelectedElement" | "navigateToRoute">;
|
|
6
6
|
export declare function setNavigateToRouteHandler(handler: ((input: {
|
|
7
7
|
pathname: string;
|
|
8
8
|
}) => void) | null): void;
|
|
@@ -32,6 +32,20 @@ export declare function getSelectModeIframeRpcChannel(): import("../_vendor/sche
|
|
|
32
32
|
lineHeight: import("zod").ZodNumber;
|
|
33
33
|
}, import("zod/v4/core").$strip>>;
|
|
34
34
|
outerHTML: import("zod").ZodString;
|
|
35
|
+
image: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
36
|
+
src: import("zod").ZodString;
|
|
37
|
+
currentSrc: import("zod").ZodString;
|
|
38
|
+
srcSet: import("zod").ZodString;
|
|
39
|
+
sizes: import("zod").ZodString;
|
|
40
|
+
previewUrl: import("zod").ZodNullable<import("zod").ZodString>;
|
|
41
|
+
sources: import("zod").ZodArray<import("zod").ZodObject<{
|
|
42
|
+
srcSet: import("zod").ZodString;
|
|
43
|
+
sizes: import("zod").ZodString;
|
|
44
|
+
media: import("zod").ZodString;
|
|
45
|
+
type: import("zod").ZodString;
|
|
46
|
+
previewUrl: import("zod").ZodNullable<import("zod").ZodString>;
|
|
47
|
+
}, import("zod/v4/core").$strip>>;
|
|
48
|
+
}, import("zod/v4/core").$strip>>;
|
|
35
49
|
definedStyles: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodObject<{
|
|
36
50
|
property: import("zod").ZodString;
|
|
37
51
|
variant: import("zod").ZodNullable<import("zod").ZodObject<{
|
|
@@ -109,6 +123,20 @@ export declare function getSelectModeIframeRpcChannel(): import("../_vendor/sche
|
|
|
109
123
|
lineHeight: import("zod").ZodNumber;
|
|
110
124
|
}, import("zod/v4/core").$strip>>;
|
|
111
125
|
outerHTML: import("zod").ZodString;
|
|
126
|
+
image: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
127
|
+
src: import("zod").ZodString;
|
|
128
|
+
currentSrc: import("zod").ZodString;
|
|
129
|
+
srcSet: import("zod").ZodString;
|
|
130
|
+
sizes: import("zod").ZodString;
|
|
131
|
+
previewUrl: import("zod").ZodNullable<import("zod").ZodString>;
|
|
132
|
+
sources: import("zod").ZodArray<import("zod").ZodObject<{
|
|
133
|
+
srcSet: import("zod").ZodString;
|
|
134
|
+
sizes: import("zod").ZodString;
|
|
135
|
+
media: import("zod").ZodString;
|
|
136
|
+
type: import("zod").ZodString;
|
|
137
|
+
previewUrl: import("zod").ZodNullable<import("zod").ZodString>;
|
|
138
|
+
}, import("zod/v4/core").$strip>>;
|
|
139
|
+
}, import("zod/v4/core").$strip>>;
|
|
112
140
|
definedStyles: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodObject<{
|
|
113
141
|
property: import("zod").ZodString;
|
|
114
142
|
variant: import("zod").ZodNullable<import("zod").ZodObject<{
|
|
@@ -274,6 +302,20 @@ export declare function getSelectModeIframeRpcChannel(): import("../_vendor/sche
|
|
|
274
302
|
lineHeight: import("zod").ZodNumber;
|
|
275
303
|
}, import("zod/v4/core").$strip>>;
|
|
276
304
|
outerHTML: import("zod").ZodString;
|
|
305
|
+
image: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
306
|
+
src: import("zod").ZodString;
|
|
307
|
+
currentSrc: import("zod").ZodString;
|
|
308
|
+
srcSet: import("zod").ZodString;
|
|
309
|
+
sizes: import("zod").ZodString;
|
|
310
|
+
previewUrl: import("zod").ZodNullable<import("zod").ZodString>;
|
|
311
|
+
sources: import("zod").ZodArray<import("zod").ZodObject<{
|
|
312
|
+
srcSet: import("zod").ZodString;
|
|
313
|
+
sizes: import("zod").ZodString;
|
|
314
|
+
media: import("zod").ZodString;
|
|
315
|
+
type: import("zod").ZodString;
|
|
316
|
+
previewUrl: import("zod").ZodNullable<import("zod").ZodString>;
|
|
317
|
+
}, import("zod/v4/core").$strip>>;
|
|
318
|
+
}, import("zod/v4/core").$strip>>;
|
|
277
319
|
definedStyles: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodObject<{
|
|
278
320
|
property: import("zod").ZodString;
|
|
279
321
|
variant: import("zod").ZodNullable<import("zod").ZodObject<{
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
createIframeRpcRemote
|
|
4
4
|
} from "../_vendor/schemas/iframeRpcChannel.mjs";
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
previewIframeToWebsiteBuilderMethodConfig,
|
|
7
7
|
SELECT_MODE_IFRAME_RPC_CHANNEL_ID,
|
|
8
8
|
SELECT_MODE_IFRAME_RPC_CHANNEL_VERSION,
|
|
9
|
-
|
|
9
|
+
websiteBuilderToPreviewIframeMethodConfig
|
|
10
10
|
} from "../_vendor/schemas/selectModeIframeRpc.mjs";
|
|
11
11
|
import { symbolicateSelectedElement } from "./symbolicate";
|
|
12
12
|
let messagingHandlers = null;
|
|
@@ -22,8 +22,8 @@ function createSelectModeIframeRpcChannel() {
|
|
|
22
22
|
getTargetWindow: () => window.parent === window ? null : window.parent,
|
|
23
23
|
targetOrigin: "*",
|
|
24
24
|
isExpectedMessage: (event) => event.source === window.parent,
|
|
25
|
-
outgoingMethods:
|
|
26
|
-
incomingMethods:
|
|
25
|
+
outgoingMethods: previewIframeToWebsiteBuilderMethodConfig,
|
|
26
|
+
incomingMethods: websiteBuilderToPreviewIframeMethodConfig,
|
|
27
27
|
handlers: {
|
|
28
28
|
setDisplayMode: (input) => {
|
|
29
29
|
messagingHandlers?.setDisplayMode(input);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { getImageEditSnapshot } from "../_vendor/schemas/selectModeImage.mjs";
|
|
2
|
+
import { getImageDescriptor, getPictureSources } from "./imageSources";
|
|
3
|
+
function createImagePreview(image) {
|
|
4
|
+
const picture = document.createElement("picture");
|
|
5
|
+
picture.hidden = true;
|
|
6
|
+
picture.dataset.reploImageResolver = "true";
|
|
7
|
+
const resolver = document.createElement("img");
|
|
8
|
+
resolver.alt = "";
|
|
9
|
+
const style = document.createElement("style");
|
|
10
|
+
const token = crypto.randomUUID();
|
|
11
|
+
image.dataset.reploImagePreview = token;
|
|
12
|
+
document.head.append(style);
|
|
13
|
+
let configuration = "";
|
|
14
|
+
let disposed = false;
|
|
15
|
+
function paint() {
|
|
16
|
+
if (disposed) return;
|
|
17
|
+
const url = resolver.currentSrc || resolver.src;
|
|
18
|
+
if (!url) return;
|
|
19
|
+
const rule = `[data-replo-image-preview="${token}"] { content: url(${JSON.stringify(url)}) !important; }`;
|
|
20
|
+
if (style.textContent !== rule) style.textContent = rule;
|
|
21
|
+
}
|
|
22
|
+
resolver.addEventListener("load", paint);
|
|
23
|
+
return {
|
|
24
|
+
render(config) {
|
|
25
|
+
const next = JSON.stringify(config);
|
|
26
|
+
if (configuration !== next) {
|
|
27
|
+
configuration = next;
|
|
28
|
+
const originals = getPictureSources(image);
|
|
29
|
+
const sources = config.sources.map((source, index) => {
|
|
30
|
+
const original = originals.find((candidate) => {
|
|
31
|
+
return candidate.media.trim() === source.media && candidate.type === source.type;
|
|
32
|
+
}) ?? originals[index];
|
|
33
|
+
const node = document.createElement("source");
|
|
34
|
+
node.media = source.media;
|
|
35
|
+
node.type = resolveMetadata(source.type, original?.type);
|
|
36
|
+
node.srcset = resolveMetadata(source.srcSet, original?.srcset);
|
|
37
|
+
node.sizes = resolveMetadata(source.sizes, original?.sizes);
|
|
38
|
+
return node;
|
|
39
|
+
});
|
|
40
|
+
resolver.setAttribute(
|
|
41
|
+
"src",
|
|
42
|
+
resolveMetadata(config.src, image.getAttribute("src"))
|
|
43
|
+
);
|
|
44
|
+
resolver.setAttribute(
|
|
45
|
+
"srcset",
|
|
46
|
+
resolveMetadata(config.srcSet, image.getAttribute("srcset"))
|
|
47
|
+
);
|
|
48
|
+
resolver.setAttribute(
|
|
49
|
+
"sizes",
|
|
50
|
+
resolveMetadata(config.sizes, image.sizes)
|
|
51
|
+
);
|
|
52
|
+
picture.replaceChildren(...sources, resolver);
|
|
53
|
+
if (!picture.isConnected) document.body.append(picture);
|
|
54
|
+
}
|
|
55
|
+
paint();
|
|
56
|
+
},
|
|
57
|
+
isPersisted(config) {
|
|
58
|
+
return JSON.stringify(
|
|
59
|
+
getImageEditSnapshot(getImageDescriptor(image), image.baseURI)
|
|
60
|
+
) === JSON.stringify(getImageEditSnapshot(config, image.baseURI));
|
|
61
|
+
},
|
|
62
|
+
dispose() {
|
|
63
|
+
disposed = true;
|
|
64
|
+
resolver.removeEventListener("load", paint);
|
|
65
|
+
picture.remove();
|
|
66
|
+
style.remove();
|
|
67
|
+
if (image.dataset.reploImagePreview === token)
|
|
68
|
+
delete image.dataset.reploImagePreview;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function resolveMetadata(value, original) {
|
|
73
|
+
return value === "[image metadata omitted]" ? original ?? "" : value;
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
createImagePreview
|
|
77
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
function getPictureSources(image) {
|
|
2
|
+
const parent = image.parentElement;
|
|
3
|
+
if (parent?.tagName !== "PICTURE") {
|
|
4
|
+
return [];
|
|
5
|
+
}
|
|
6
|
+
return Array.from(parent.children).filter(
|
|
7
|
+
(child) => {
|
|
8
|
+
return child instanceof HTMLSourceElement && Boolean(
|
|
9
|
+
child.compareDocumentPosition(image) & Node.DOCUMENT_POSITION_FOLLOWING
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
const MAX_IMAGE_METADATA_LENGTH = 2048;
|
|
15
|
+
const MAX_PICTURE_SOURCES = 16;
|
|
16
|
+
function boundedImageMetadata(value) {
|
|
17
|
+
if (value.length > MAX_IMAGE_METADATA_LENGTH || /data:/i.test(value)) {
|
|
18
|
+
return "[image metadata omitted]";
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
function getPreviewUrl({
|
|
23
|
+
src,
|
|
24
|
+
srcSet,
|
|
25
|
+
baseURI
|
|
26
|
+
}) {
|
|
27
|
+
if (src.length > MAX_IMAGE_METADATA_LENGTH || srcSet.length > MAX_IMAGE_METADATA_LENGTH)
|
|
28
|
+
return null;
|
|
29
|
+
const candidate = src || srcSet.trim().split(/\s+/)[0]?.replace(/,+$/, "");
|
|
30
|
+
if (!candidate) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const url = new URL(candidate, baseURI);
|
|
35
|
+
return ["http:", "https:"].includes(url.protocol) ? url.href : null;
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function getImageDescriptor(element) {
|
|
41
|
+
if (!(element instanceof HTMLImageElement)) {
|
|
42
|
+
return void 0;
|
|
43
|
+
}
|
|
44
|
+
const src = element.getAttribute("src") ?? "";
|
|
45
|
+
const srcSet = element.getAttribute("srcset") ?? "";
|
|
46
|
+
return {
|
|
47
|
+
src: boundedImageMetadata(src),
|
|
48
|
+
srcSet: boundedImageMetadata(srcSet),
|
|
49
|
+
sizes: boundedImageMetadata(element.sizes),
|
|
50
|
+
currentSrc: boundedImageMetadata(element.currentSrc),
|
|
51
|
+
previewUrl: getPreviewUrl({ src, srcSet, baseURI: element.baseURI }),
|
|
52
|
+
sources: getPictureSources(element).filter((source) => source.media.length <= MAX_IMAGE_METADATA_LENGTH).slice(0, MAX_PICTURE_SOURCES).map((source) => ({
|
|
53
|
+
srcSet: boundedImageMetadata(source.srcset),
|
|
54
|
+
sizes: boundedImageMetadata(source.sizes),
|
|
55
|
+
media: source.media.trim(),
|
|
56
|
+
type: boundedImageMetadata(source.type),
|
|
57
|
+
previewUrl: getPreviewUrl({
|
|
58
|
+
src: "",
|
|
59
|
+
srcSet: source.srcset,
|
|
60
|
+
baseURI: source.baseURI
|
|
61
|
+
})
|
|
62
|
+
}))
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
getImageDescriptor,
|
|
67
|
+
getPictureSources
|
|
68
|
+
};
|
|
@@ -1,25 +1,20 @@
|
|
|
1
|
-
import type { DisplayMode, SelectableData, SelectModeBoundingRect, SelectModeFormatCommand, SelectModeSelectionType } from "../_vendor/schemas/generated/selectMode";
|
|
1
|
+
import type { DisplayMode, SelectableData, SelectModeBoundingRect, SelectModeFormatCommand, SelectModeSelectionType, SelectModeTiptapActiveMarks } from "../_vendor/schemas/generated/selectMode";
|
|
2
2
|
import type { InlineEditFeedback } from "../_vendor/schemas/generated/selectModeIframeRpc";
|
|
3
3
|
import type { DragCandidate } from "./interactions/selectionGeometry";
|
|
4
4
|
import type { SelectionFrameSpec } from "./selectionFrame";
|
|
5
5
|
import type { SelectModeInteractionSession } from "./selectModeSession";
|
|
6
6
|
import type { InlineTiptapEditor } from "./tiptapInlineEditor";
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
italic: boolean;
|
|
10
|
-
underline: boolean;
|
|
11
|
-
strike: boolean;
|
|
12
|
-
};
|
|
13
|
-
export declare const EMPTY_ACTIVE_MARKS: ActiveMarks;
|
|
14
|
-
type InlineEditRequest = {
|
|
7
|
+
export declare const EMPTY_ACTIVE_MARKS: SelectModeTiptapActiveMarks;
|
|
8
|
+
type InlineEditDraft = {
|
|
15
9
|
status: "pending";
|
|
16
10
|
editId: string;
|
|
17
11
|
element: HTMLElement;
|
|
18
12
|
timeout: number;
|
|
19
|
-
stopFeedback: () => void;
|
|
20
13
|
} | {
|
|
21
14
|
status: Exclude<InlineEditFeedback["status"], "pending">;
|
|
22
15
|
element: HTMLElement;
|
|
16
|
+
};
|
|
17
|
+
type InlineEditRequest = InlineEditDraft & {
|
|
23
18
|
stopFeedback: () => void;
|
|
24
19
|
};
|
|
25
20
|
type LastClick = {
|
|
@@ -52,7 +47,7 @@ interface SelectModeStoreState {
|
|
|
52
47
|
initialHTML: string | null;
|
|
53
48
|
isCommitting: boolean;
|
|
54
49
|
beginEditingAt: number;
|
|
55
|
-
activeMarks:
|
|
50
|
+
activeMarks: SelectModeTiptapActiveMarks;
|
|
56
51
|
actions: {
|
|
57
52
|
reset: () => void;
|
|
58
53
|
setDisplayMode: (displayMode: DisplayMode) => void;
|
|
@@ -12,10 +12,7 @@ import {
|
|
|
12
12
|
import { reportInlineEditFeedback } from "./inlineEditFeedback";
|
|
13
13
|
import { getDocumentScrollOffset } from "./interactions/selectionGeometry";
|
|
14
14
|
import { isTextEditableElement } from "./isTextEditableElement";
|
|
15
|
-
import {
|
|
16
|
-
getAreaSelectionFrameSpecs,
|
|
17
|
-
getElementSelectionFrameSpecs
|
|
18
|
-
} from "./selectionFrame";
|
|
15
|
+
import { getSelectionFrameSpecs } from "./selectionFrame";
|
|
19
16
|
import { getConnectedSelectedElement, IDLE_SESSION } from "./selectModeSession";
|
|
20
17
|
import {
|
|
21
18
|
isUserSourceLocation,
|
|
@@ -405,17 +402,19 @@ const selectModeStore = createShallowStore(
|
|
|
405
402
|
},
|
|
406
403
|
showElementSelectionFrames: ({ elements, scrollOffset }) => {
|
|
407
404
|
set({
|
|
408
|
-
selectionFrames:
|
|
405
|
+
selectionFrames: getSelectionFrameSpecs({
|
|
409
406
|
elements,
|
|
410
|
-
scrollOffset
|
|
407
|
+
scrollOffset,
|
|
408
|
+
variant: "element"
|
|
411
409
|
})
|
|
412
410
|
});
|
|
413
411
|
},
|
|
414
412
|
showAreaSelectionFrames: ({ elements, scrollOffset }) => {
|
|
415
413
|
set({
|
|
416
|
-
selectionFrames:
|
|
414
|
+
selectionFrames: getSelectionFrameSpecs({
|
|
417
415
|
elements,
|
|
418
|
-
scrollOffset
|
|
416
|
+
scrollOffset,
|
|
417
|
+
variant: "area"
|
|
419
418
|
})
|
|
420
419
|
});
|
|
421
420
|
},
|
|
@@ -12,17 +12,11 @@ export type SelectionFrameSpec = {
|
|
|
12
12
|
pageRect: SelectionFramePageRect;
|
|
13
13
|
variant: SelectionFrameVariant;
|
|
14
14
|
};
|
|
15
|
-
export declare function
|
|
16
|
-
elements: Element[];
|
|
17
|
-
scrollOffset: {
|
|
18
|
-
left: number;
|
|
19
|
-
top: number;
|
|
20
|
-
};
|
|
21
|
-
}): SelectionFrameSpec[];
|
|
22
|
-
export declare function getAreaSelectionFrameSpecs({ elements, scrollOffset, }: {
|
|
15
|
+
export declare function getSelectionFrameSpecs({ elements, scrollOffset, variant, }: {
|
|
23
16
|
elements: Element[];
|
|
24
17
|
scrollOffset: {
|
|
25
18
|
left: number;
|
|
26
19
|
top: number;
|
|
27
20
|
};
|
|
21
|
+
variant: SelectionFrameVariant;
|
|
28
22
|
}): SelectionFrameSpec[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const SELECTION_FRAMES_CONTAINER_ID = "replo-select-mode-selection-frames";
|
|
2
2
|
const SELECTION_FRAME_BORDER_HEX = "#c026d3";
|
|
3
3
|
const SELECTION_FRAME_EDITING_BORDER_HEX = "#e879f9";
|
|
4
|
-
function
|
|
4
|
+
function getSelectionFrameSpecs({
|
|
5
5
|
elements,
|
|
6
6
|
scrollOffset,
|
|
7
7
|
variant
|
|
@@ -19,22 +19,9 @@ function getFrameSpecs({
|
|
|
19
19
|
};
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
|
-
function getElementSelectionFrameSpecs({
|
|
23
|
-
elements,
|
|
24
|
-
scrollOffset
|
|
25
|
-
}) {
|
|
26
|
-
return getFrameSpecs({ elements, scrollOffset, variant: "element" });
|
|
27
|
-
}
|
|
28
|
-
function getAreaSelectionFrameSpecs({
|
|
29
|
-
elements,
|
|
30
|
-
scrollOffset
|
|
31
|
-
}) {
|
|
32
|
-
return getFrameSpecs({ elements, scrollOffset, variant: "area" });
|
|
33
|
-
}
|
|
34
22
|
export {
|
|
35
23
|
SELECTION_FRAMES_CONTAINER_ID,
|
|
36
24
|
SELECTION_FRAME_BORDER_HEX,
|
|
37
25
|
SELECTION_FRAME_EDITING_BORDER_HEX,
|
|
38
|
-
|
|
39
|
-
getElementSelectionFrameSpecs
|
|
26
|
+
getSelectionFrameSpecs
|
|
40
27
|
};
|