@replohq/sandbox-runtime 1.9.0 → 1.10.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/fonts.mjs +18 -6
- package/_vendor/schemas/generated/selectMode.d.ts +14 -0
- package/_vendor/schemas/generated/selectModeIframeRpc.d.ts +12 -2
- package/_vendor/schemas/selectMode.d.ts +24 -0
- package/_vendor/schemas/selectMode.mjs +1 -1
- package/_vendor/schemas/selectModeIframeRpc.d.ts +49 -0
- package/_vendor/schemas/selectModeIframeRpc.mjs +18 -2
- package/_vendor/schemas/selectModeImage.mjs +6 -2
- package/buildMetadata.js +3 -3
- package/{chunk-XWNBKE63.mjs → chunk-PJBGUXBG.mjs} +6 -1
- package/design-library-preview/DesignLibraryPreview.js +18 -6
- package/package.json +4 -4
- package/select-mode/EditorSelection.js +21 -4
- package/select-mode/SelectionFrames.js +56 -23
- package/select-mode/descriptor/elementDescriptor.d.ts +2 -1
- package/select-mode/descriptor/elementDescriptor.js +22 -14
- package/select-mode/hooks/useOptimisticOverrides.js +18 -6
- package/select-mode/hooks/useParentMessaging.d.ts +7 -1
- package/select-mode/hooks/useParentMessaging.js +6 -0
- package/select-mode/hooks/useSelectionHover.js +38 -15
- package/select-mode/hooks/useSelectionInteractions.js +9 -37
- package/select-mode/hooks/useSelectionScrollSync.js +5 -1
- package/select-mode/iframeRpcChannel.d.ts +10 -0
- package/select-mode/iframeRpcChannel.js +2 -0
- package/select-mode/imageOverridePreview.js +14 -5
- package/select-mode/imageSources.js +2 -1
- package/select-mode/inlineEditFeedback.js +9 -3
- package/select-mode/interactions/sectionGeometry.d.ts +3 -0
- package/select-mode/interactions/sectionGeometry.js +200 -0
- package/select-mode/selectModeStore.d.ts +36 -8
- package/select-mode/selectModeStore.js +141 -21
- package/select-mode/selectionFrame.d.ts +3 -1
- package/select-mode/selectionFrame.js +4 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { SELECT_MODE_SELECTABLE_ATTRIBUTE } from "../../_vendor/schemas/selectMode.mjs";
|
|
2
|
+
const GROUP_TAGS = /* @__PURE__ */ new Set(["DIV", "ARTICLE", "ASIDE", "FORM"]);
|
|
3
|
+
const LANDMARK_TAGS = /* @__PURE__ */ new Set(["HEADER", "NAV", "FOOTER"]);
|
|
4
|
+
const SECTION_TAGS = /* @__PURE__ */ new Set(["SECTION", "ARTICLE", ...LANDMARK_TAGS]);
|
|
5
|
+
const CONTAINER_TAGS = /* @__PURE__ */ new Set([...GROUP_TAGS, ...SECTION_TAGS]);
|
|
6
|
+
function findContainingSection(element) {
|
|
7
|
+
const rects = /* @__PURE__ */ new Map();
|
|
8
|
+
const styles = /* @__PURE__ */ new Map();
|
|
9
|
+
const visibleChildren = /* @__PURE__ */ new Map();
|
|
10
|
+
if (element.closest(`[${SELECT_MODE_SELECTABLE_ATTRIBUTE}]`)) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
if (element.closest('dialog, [role="dialog"], [aria-modal="true"]')) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
for (let parent = element; parent; parent = parent.parentElement) {
|
|
17
|
+
const position = getStyle(parent).position;
|
|
18
|
+
if (position === "fixed" && !LANDMARK_TAGS.has(parent.tagName)) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const lane = findContentLane();
|
|
23
|
+
if (!lane.contains(element)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const outerLaneRect = getRect(lane);
|
|
27
|
+
const laneStyle = getStyle(lane);
|
|
28
|
+
const paddingLeft = parseFloat(laneStyle.paddingLeft || "0");
|
|
29
|
+
const paddingRight = parseFloat(laneStyle.paddingRight || "0");
|
|
30
|
+
const laneRect = {
|
|
31
|
+
left: outerLaneRect.left + paddingLeft,
|
|
32
|
+
right: outerLaneRect.right - paddingRight,
|
|
33
|
+
width: outerLaneRect.width - paddingLeft - paddingRight
|
|
34
|
+
};
|
|
35
|
+
if (laneRect.width <= 0) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const tolerance = Math.max(4, laneRect.width * 0.04);
|
|
39
|
+
const section = findOutermostSection();
|
|
40
|
+
return section ? trimSectionWrappers(section) : null;
|
|
41
|
+
function findContentLane() {
|
|
42
|
+
let lane2 = element.closest("main") ?? element.ownerDocument.body;
|
|
43
|
+
while (true) {
|
|
44
|
+
const children = getVisibleChildren(lane2);
|
|
45
|
+
const child = children[0];
|
|
46
|
+
if (children.length !== 1 || !child || child.tagName !== "DIV" || hasSectionStyling(child) && (getVisibleChildren(child).filter((descendant) => {
|
|
47
|
+
return CONTAINER_TAGS.has(descendant.tagName);
|
|
48
|
+
}).length < 2 || getRect(child).height < getRect(lane2).height * 0.9)) {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
lane2 = child;
|
|
52
|
+
}
|
|
53
|
+
return lane2;
|
|
54
|
+
}
|
|
55
|
+
function findOutermostSection() {
|
|
56
|
+
const candidates = [];
|
|
57
|
+
for (let candidate = element; candidate && candidate !== lane; candidate = candidate.parentElement) {
|
|
58
|
+
candidates.push(candidate);
|
|
59
|
+
}
|
|
60
|
+
for (const candidate of candidates.reverse()) {
|
|
61
|
+
if (!CONTAINER_TAGS.has(candidate.tagName) || !spansLane(candidate)) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const style = getStyle(candidate);
|
|
65
|
+
if (style.display === "none" || style.display === "contents" || style.visibility === "hidden" || style.position === "absolute") {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const rect = getRect(candidate);
|
|
69
|
+
const minimumHeight = LANDMARK_TAGS.has(candidate.tagName) ? 20 : 48;
|
|
70
|
+
if (rect.height < minimumHeight || isMultiSectionWrapper(candidate)) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (SECTION_TAGS.has(candidate.tagName)) {
|
|
74
|
+
return candidate;
|
|
75
|
+
}
|
|
76
|
+
let parent = candidate.parentElement;
|
|
77
|
+
while (parent && getStyle(parent).display === "contents") {
|
|
78
|
+
parent = parent.parentElement;
|
|
79
|
+
}
|
|
80
|
+
const hasPeerBand = parent && getVisibleChildren(parent).some((peer) => {
|
|
81
|
+
if (peer === candidate || peer.contains(candidate) || !spansLane(peer)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
const peerRect = getRect(peer);
|
|
85
|
+
return peerRect.height >= 20 && (peerRect.bottom <= rect.top + 4 || peerRect.top >= rect.bottom - 4);
|
|
86
|
+
});
|
|
87
|
+
if (hasPeerBand) {
|
|
88
|
+
return candidate;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
function isMultiSectionWrapper(candidate) {
|
|
94
|
+
if (!GROUP_TAGS.has(candidate.tagName) || hasSectionStyling(candidate) || hasUngroupedText(candidate)) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const children = getVisibleChildren(candidate);
|
|
98
|
+
const childBands = children.filter((child) => {
|
|
99
|
+
return CONTAINER_TAGS.has(child.tagName) && spansLane(child) && getRect(child).height >= 48;
|
|
100
|
+
});
|
|
101
|
+
const hasLeftoverContent = children.some((child) => {
|
|
102
|
+
return !CONTAINER_TAGS.has(child.tagName) && Boolean(child.textContent?.trim());
|
|
103
|
+
});
|
|
104
|
+
if (childBands.length <= 1 || hasLeftoverContent) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
let minimumBottom = Infinity;
|
|
108
|
+
let maximumTop = -Infinity;
|
|
109
|
+
for (const child of childBands) {
|
|
110
|
+
const childRect = getRect(child);
|
|
111
|
+
minimumBottom = Math.min(minimumBottom, childRect.bottom);
|
|
112
|
+
maximumTop = Math.max(maximumTop, childRect.top);
|
|
113
|
+
}
|
|
114
|
+
return minimumBottom <= maximumTop + 4;
|
|
115
|
+
}
|
|
116
|
+
function trimSectionWrappers(section2) {
|
|
117
|
+
while (!SECTION_TAGS.has(section2.tagName) && !hasSectionStyling(section2)) {
|
|
118
|
+
const children = getVisibleChildren(section2);
|
|
119
|
+
const child = children[0];
|
|
120
|
+
if (children.length !== 1 || !child || !child.contains(element) || !CONTAINER_TAGS.has(child.tagName)) {
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
const outer = getRect(section2);
|
|
124
|
+
const inner = getRect(child);
|
|
125
|
+
if (Math.abs(outer.top - inner.top) > 4 || Math.abs(outer.bottom - inner.bottom) > 4 || !spansLane(child)) {
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
section2 = child;
|
|
129
|
+
}
|
|
130
|
+
return section2;
|
|
131
|
+
}
|
|
132
|
+
function spansLane(candidate) {
|
|
133
|
+
const rect = getRect(candidate);
|
|
134
|
+
return Math.abs(rect.left - laneRect.left) <= tolerance && Math.abs(rect.right - laneRect.right) <= tolerance;
|
|
135
|
+
}
|
|
136
|
+
function getRect(element2) {
|
|
137
|
+
const cached = rects.get(element2);
|
|
138
|
+
if (cached) {
|
|
139
|
+
return cached;
|
|
140
|
+
}
|
|
141
|
+
const rect = element2.getBoundingClientRect();
|
|
142
|
+
rects.set(element2, rect);
|
|
143
|
+
return rect;
|
|
144
|
+
}
|
|
145
|
+
function getStyle(element2) {
|
|
146
|
+
const cached = styles.get(element2);
|
|
147
|
+
if (cached) {
|
|
148
|
+
return cached;
|
|
149
|
+
}
|
|
150
|
+
const style = getComputedStyle(element2);
|
|
151
|
+
styles.set(element2, style);
|
|
152
|
+
return style;
|
|
153
|
+
}
|
|
154
|
+
function getVisibleChildren(element2) {
|
|
155
|
+
const cached = visibleChildren.get(element2);
|
|
156
|
+
if (cached) {
|
|
157
|
+
return cached;
|
|
158
|
+
}
|
|
159
|
+
const children = Array.from(element2.children).flatMap((child) => {
|
|
160
|
+
const style = getStyle(child);
|
|
161
|
+
if (style.display === "none" || style.visibility === "hidden" || ["fixed", "absolute"].includes(style.position) && !LANDMARK_TAGS.has(child.tagName)) {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
if (style.display === "contents") {
|
|
165
|
+
return getVisibleChildren(child);
|
|
166
|
+
}
|
|
167
|
+
const rect = getRect(child);
|
|
168
|
+
return rect.width > 0 && rect.height > 0 ? [child] : [];
|
|
169
|
+
});
|
|
170
|
+
visibleChildren.set(element2, children);
|
|
171
|
+
return children;
|
|
172
|
+
}
|
|
173
|
+
function hasUngroupedText(element2) {
|
|
174
|
+
return Array.from(element2.childNodes).some((node) => {
|
|
175
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
176
|
+
return Boolean(node.textContent?.trim());
|
|
177
|
+
}
|
|
178
|
+
if (!(node instanceof Element)) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
const style = getStyle(node);
|
|
182
|
+
return style.display === "contents" && style.visibility !== "hidden" && hasUngroupedText(node);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function hasSectionStyling(element2) {
|
|
186
|
+
const style = getStyle(element2);
|
|
187
|
+
return style.backgroundColor !== "rgba(0, 0, 0, 0)" && style.backgroundColor !== "transparent" && style.backgroundColor !== "" || style.backgroundImage !== "none" && style.backgroundImage !== "" || [
|
|
188
|
+
style.paddingTop,
|
|
189
|
+
style.paddingRight,
|
|
190
|
+
style.paddingBottom,
|
|
191
|
+
style.paddingLeft,
|
|
192
|
+
style.borderTopWidth,
|
|
193
|
+
style.borderBottomWidth
|
|
194
|
+
].some((value) => parseFloat(value) > 0);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
export {
|
|
198
|
+
CONTAINER_TAGS,
|
|
199
|
+
findContainingSection
|
|
200
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DisplayMode, SelectableData,
|
|
1
|
+
import type { DisplayMode, SelectableData, SelectModeClickPosition, SelectModeFormatCommand, SelectModeHoverRect, 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";
|
|
@@ -22,6 +22,10 @@ type LastClick = {
|
|
|
22
22
|
target: Element;
|
|
23
23
|
time: number;
|
|
24
24
|
};
|
|
25
|
+
type PostedHover = {
|
|
26
|
+
element: Element | null;
|
|
27
|
+
rect: SelectModeHoverRect | null;
|
|
28
|
+
};
|
|
25
29
|
interface SelectModeStoreState {
|
|
26
30
|
displayMode: DisplayMode;
|
|
27
31
|
interactionSession: SelectModeInteractionSession;
|
|
@@ -31,7 +35,8 @@ interface SelectModeStoreState {
|
|
|
31
35
|
x: number;
|
|
32
36
|
y: number;
|
|
33
37
|
} | null;
|
|
34
|
-
|
|
38
|
+
lastPostedHover: PostedHover | undefined;
|
|
39
|
+
sectionPreviewElement: Element | null;
|
|
35
40
|
hoverSyncAnimationFrameId: number | null;
|
|
36
41
|
lastClick: LastClick | null;
|
|
37
42
|
dragCandidates: DragCandidate[];
|
|
@@ -60,8 +65,20 @@ interface SelectModeStoreState {
|
|
|
60
65
|
postSelectedElementSnapshot: (options?: {
|
|
61
66
|
clearMissing?: boolean;
|
|
62
67
|
}) => boolean;
|
|
63
|
-
|
|
68
|
+
commitElementSelection: ({ element, clickPosition, isPendingClickOnSelection, }: {
|
|
69
|
+
element: Element;
|
|
70
|
+
clickPosition: SelectModeClickPosition;
|
|
71
|
+
isPendingClickOnSelection?: (data: SelectableData) => boolean;
|
|
72
|
+
}) => SelectableData;
|
|
73
|
+
expandToSection: (input: {
|
|
74
|
+
targetSelector: string;
|
|
75
|
+
}) => void;
|
|
76
|
+
setSectionPreview: (input: {
|
|
77
|
+
targetSelector: string | null;
|
|
78
|
+
}) => void;
|
|
79
|
+
showElementSelectionFrames: ({ elements, isSection, scrollOffset, }: {
|
|
64
80
|
elements: Element[];
|
|
81
|
+
isSection?: boolean;
|
|
65
82
|
scrollOffset: {
|
|
66
83
|
left: number;
|
|
67
84
|
top: number;
|
|
@@ -79,8 +96,7 @@ interface SelectModeStoreState {
|
|
|
79
96
|
x: number;
|
|
80
97
|
y: number;
|
|
81
98
|
} | null) => void;
|
|
82
|
-
|
|
83
|
-
resetHoverRectDedup: () => void;
|
|
99
|
+
setLastPostedHover: (hover: PostedHover | undefined) => void;
|
|
84
100
|
setHoverSyncAnimationFrameId: (id: number | null) => void;
|
|
85
101
|
setLastClick: (lastClick: LastClick | null) => void;
|
|
86
102
|
setDragCandidates: (dragCandidates: DragCandidate[]) => void;
|
|
@@ -111,8 +127,20 @@ export declare const useSelectModeStoreActions: () => {
|
|
|
111
127
|
postSelectedElementSnapshot: (options?: {
|
|
112
128
|
clearMissing?: boolean;
|
|
113
129
|
}) => boolean;
|
|
114
|
-
|
|
130
|
+
commitElementSelection: ({ element, clickPosition, isPendingClickOnSelection, }: {
|
|
131
|
+
element: Element;
|
|
132
|
+
clickPosition: SelectModeClickPosition;
|
|
133
|
+
isPendingClickOnSelection?: (data: SelectableData) => boolean;
|
|
134
|
+
}) => SelectableData;
|
|
135
|
+
expandToSection: (input: {
|
|
136
|
+
targetSelector: string;
|
|
137
|
+
}) => void;
|
|
138
|
+
setSectionPreview: (input: {
|
|
139
|
+
targetSelector: string | null;
|
|
140
|
+
}) => void;
|
|
141
|
+
showElementSelectionFrames: ({ elements, isSection, scrollOffset, }: {
|
|
115
142
|
elements: Element[];
|
|
143
|
+
isSection?: boolean;
|
|
116
144
|
scrollOffset: {
|
|
117
145
|
left: number;
|
|
118
146
|
top: number;
|
|
@@ -130,8 +158,7 @@ export declare const useSelectModeStoreActions: () => {
|
|
|
130
158
|
x: number;
|
|
131
159
|
y: number;
|
|
132
160
|
} | null) => void;
|
|
133
|
-
|
|
134
|
-
resetHoverRectDedup: () => void;
|
|
161
|
+
setLastPostedHover: (hover: PostedHover | undefined) => void;
|
|
135
162
|
setHoverSyncAnimationFrameId: (id: number | null) => void;
|
|
136
163
|
setLastClick: (lastClick: LastClick | null) => void;
|
|
137
164
|
setDragCandidates: (dragCandidates: DragCandidate[]) => void;
|
|
@@ -149,6 +176,7 @@ export declare const useSelectModeStoreActions: () => {
|
|
|
149
176
|
};
|
|
150
177
|
export declare const useDisplayMode: () => DisplayMode;
|
|
151
178
|
export declare const useSelectionFrames: () => SelectionFrameSpec[];
|
|
179
|
+
export declare const useSectionHoverRect: () => SelectModeHoverRect | null;
|
|
152
180
|
export declare const useIsSelectionFrameEditing: () => boolean;
|
|
153
181
|
export declare const useEditingElement: () => HTMLElement | null;
|
|
154
182
|
export declare const useInteractionSession: () => SelectModeInteractionSession;
|
|
@@ -10,9 +10,11 @@ import {
|
|
|
10
10
|
requestInlineTextEdit
|
|
11
11
|
} from "./iframeRpcChannel";
|
|
12
12
|
import { reportInlineEditFeedback } from "./inlineEditFeedback";
|
|
13
|
+
import { findContainingSection } from "./interactions/sectionGeometry";
|
|
13
14
|
import { getDocumentScrollOffset } from "./interactions/selectionGeometry";
|
|
14
15
|
import { isTextEditableElement } from "./isTextEditableElement";
|
|
15
16
|
import { getSelectionFrameSpecs } from "./selectionFrame";
|
|
17
|
+
import { isSelectionStillCurrent } from "./selectionPublish";
|
|
16
18
|
import { getConnectedSelectedElement, IDLE_SESSION } from "./selectModeSession";
|
|
17
19
|
import {
|
|
18
20
|
isUserSourceLocation,
|
|
@@ -42,6 +44,21 @@ function stopInlineEditRequest(request) {
|
|
|
42
44
|
}
|
|
43
45
|
request.stopFeedback();
|
|
44
46
|
}
|
|
47
|
+
function buildElementSelection({
|
|
48
|
+
element,
|
|
49
|
+
clickPosition
|
|
50
|
+
}) {
|
|
51
|
+
const section = findContainingSection(element);
|
|
52
|
+
const isSection = section === element;
|
|
53
|
+
const siblingElements = isSection ? [] : getRepetitionSiblingElements({ element });
|
|
54
|
+
const data = buildElementDescriptor({
|
|
55
|
+
element,
|
|
56
|
+
clickPosition,
|
|
57
|
+
siblingSelectors: siblingElements.map(buildSelector),
|
|
58
|
+
section
|
|
59
|
+
});
|
|
60
|
+
return { data, siblingElements, isSection };
|
|
61
|
+
}
|
|
45
62
|
function reportInlineEditingState({
|
|
46
63
|
activeMarks,
|
|
47
64
|
isInlineEditing,
|
|
@@ -59,7 +76,8 @@ const INITIAL_STATE = {
|
|
|
59
76
|
selectionFrames: [],
|
|
60
77
|
isSelectionFrameEditing: false,
|
|
61
78
|
lastPointerClientPosition: null,
|
|
62
|
-
|
|
79
|
+
lastPostedHover: void 0,
|
|
80
|
+
sectionPreviewElement: null,
|
|
63
81
|
hoverSyncAnimationFrameId: null,
|
|
64
82
|
lastClick: null,
|
|
65
83
|
dragCandidates: [],
|
|
@@ -294,7 +312,8 @@ const selectModeStore = createShallowStore(
|
|
|
294
312
|
set({
|
|
295
313
|
interactionSession: IDLE_SESSION,
|
|
296
314
|
selectionFrames: [],
|
|
297
|
-
|
|
315
|
+
lastPostedHover: void 0,
|
|
316
|
+
sectionPreviewElement: null,
|
|
298
317
|
hoverSyncAnimationFrameId: null,
|
|
299
318
|
dragAnimationFrameId: null,
|
|
300
319
|
dragCandidates: []
|
|
@@ -356,15 +375,19 @@ const selectModeStore = createShallowStore(
|
|
|
356
375
|
});
|
|
357
376
|
},
|
|
358
377
|
setInteractionSession: (interactionSession) => {
|
|
359
|
-
set({ interactionSession });
|
|
378
|
+
set({ interactionSession, sectionPreviewElement: null });
|
|
360
379
|
},
|
|
361
380
|
resetInteractionSession,
|
|
362
381
|
postSelectedElementSnapshot: ({ clearMissing = false } = {}) => {
|
|
363
382
|
const { interactionSession } = get();
|
|
364
|
-
if (interactionSession.type !== "element")
|
|
383
|
+
if (interactionSession.type !== "element") {
|
|
384
|
+
return true;
|
|
385
|
+
}
|
|
365
386
|
const element = getConnectedSelectedElement(interactionSession);
|
|
366
387
|
if (!element) {
|
|
367
|
-
if (!clearMissing)
|
|
388
|
+
if (!clearMissing) {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
368
391
|
get().actions.setSelectionType({ selectionType: "none" });
|
|
369
392
|
getWebsiteBuilderRpc()?.areaSelected({
|
|
370
393
|
rect: interactionSession.data.rect,
|
|
@@ -372,27 +395,39 @@ const selectModeStore = createShallowStore(
|
|
|
372
395
|
});
|
|
373
396
|
return true;
|
|
374
397
|
}
|
|
375
|
-
const
|
|
376
|
-
|
|
398
|
+
const {
|
|
399
|
+
data: descriptor,
|
|
400
|
+
siblingElements,
|
|
401
|
+
isSection
|
|
402
|
+
} = buildElementSelection({
|
|
377
403
|
element,
|
|
378
|
-
clickPosition: interactionSession.clickPosition ?? (interactionSession.data.kind === "react-element" ? interactionSession.data.clickPosition : { x: 0, y: 0 })
|
|
379
|
-
siblingSelectors: siblingElements.map(buildSelector)
|
|
404
|
+
clickPosition: interactionSession.clickPosition ?? (interactionSession.data.kind === "react-element" ? interactionSession.data.clickPosition : { x: 0, y: 0 })
|
|
380
405
|
});
|
|
381
|
-
if (descriptor.kind !== "react-element")
|
|
406
|
+
if (descriptor.kind !== "react-element") {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
382
409
|
const refreshedSession = {
|
|
383
410
|
...interactionSession,
|
|
384
411
|
element,
|
|
385
|
-
siblingElements
|
|
412
|
+
siblingElements,
|
|
413
|
+
data: descriptor
|
|
386
414
|
};
|
|
387
|
-
set({
|
|
415
|
+
set({
|
|
416
|
+
interactionSession: refreshedSession,
|
|
417
|
+
sectionPreviewElement: null
|
|
418
|
+
});
|
|
388
419
|
get().actions.showElementSelectionFrames({
|
|
389
420
|
elements: [element, ...siblingElements],
|
|
421
|
+
isSection,
|
|
390
422
|
scrollOffset: getDocumentScrollOffset()
|
|
391
423
|
});
|
|
392
424
|
void symbolicateSelectedElement(descriptor).then((symbolicated) => {
|
|
393
|
-
if (get().interactionSession !== refreshedSession)
|
|
394
|
-
|
|
425
|
+
if (get().interactionSession !== refreshedSession) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
if (symbolicated.sourceLocation && !isUserSourceLocation(symbolicated.sourceLocation)) {
|
|
395
429
|
return;
|
|
430
|
+
}
|
|
396
431
|
set({
|
|
397
432
|
interactionSession: { ...refreshedSession, data: symbolicated }
|
|
398
433
|
});
|
|
@@ -400,12 +435,94 @@ const selectModeStore = createShallowStore(
|
|
|
400
435
|
});
|
|
401
436
|
return true;
|
|
402
437
|
},
|
|
403
|
-
|
|
438
|
+
commitElementSelection: ({
|
|
439
|
+
element,
|
|
440
|
+
clickPosition,
|
|
441
|
+
isPendingClickOnSelection
|
|
442
|
+
}) => {
|
|
443
|
+
set({ sectionPreviewElement: null });
|
|
444
|
+
const { data, siblingElements, isSection } = buildElementSelection({
|
|
445
|
+
element,
|
|
446
|
+
clickPosition
|
|
447
|
+
});
|
|
448
|
+
get().actions.showElementSelectionFrames({
|
|
449
|
+
elements: [element, ...siblingElements],
|
|
450
|
+
isSection,
|
|
451
|
+
scrollOffset: getDocumentScrollOffset()
|
|
452
|
+
});
|
|
453
|
+
if (!element.isConnected) {
|
|
454
|
+
return data;
|
|
455
|
+
}
|
|
456
|
+
const rect = element.getBoundingClientRect();
|
|
457
|
+
set({
|
|
458
|
+
interactionSession: {
|
|
459
|
+
type: "element",
|
|
460
|
+
element,
|
|
461
|
+
siblingElements,
|
|
462
|
+
clickPosition: {
|
|
463
|
+
x: clickPosition.x - rect.left,
|
|
464
|
+
y: clickPosition.y - rect.top
|
|
465
|
+
},
|
|
466
|
+
data
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
const descriptorPromise = data.kind === "react-element" ? symbolicateSelectedElement(data) : Promise.resolve(data);
|
|
470
|
+
void descriptorPromise.then((result) => {
|
|
471
|
+
const session = get().interactionSession;
|
|
472
|
+
const isCurrent = session.type === "element" && session.data === data && isSelectionStillCurrent({
|
|
473
|
+
session,
|
|
474
|
+
selection: { type: "element", element }
|
|
475
|
+
});
|
|
476
|
+
if (isCurrent || isPendingClickOnSelection?.(data)) {
|
|
477
|
+
getWebsiteBuilderRpc()?.elementSelected({ data: result });
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
return data;
|
|
481
|
+
},
|
|
482
|
+
expandToSection: ({ targetSelector }) => {
|
|
483
|
+
const { editingElement, inlineEditRequest, interactionSession } = get();
|
|
484
|
+
if (interactionSession.type !== "element" || interactionSession.data.kind !== "react-element" || interactionSession.data.selector !== targetSelector || !interactionSession.data.section || editingElement || inlineEditRequest) {
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const section = interactionSession.element.isConnected ? findContainingSection(interactionSession.element) : null;
|
|
488
|
+
if (!section || section === interactionSession.element) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const rect = section.getBoundingClientRect();
|
|
492
|
+
get().actions.commitElementSelection({
|
|
493
|
+
element: section,
|
|
494
|
+
clickPosition: {
|
|
495
|
+
x: rect.left + rect.width / 2,
|
|
496
|
+
y: rect.top + Math.min(24, rect.height / 2)
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
},
|
|
500
|
+
setSectionPreview: ({ targetSelector }) => {
|
|
501
|
+
if (!targetSelector) {
|
|
502
|
+
set({ sectionPreviewElement: null });
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
const {
|
|
506
|
+
displayMode,
|
|
507
|
+
editingElement,
|
|
508
|
+
inlineEditRequest,
|
|
509
|
+
interactionSession
|
|
510
|
+
} = get();
|
|
511
|
+
if (displayMode !== "selection" || interactionSession.type !== "element" || interactionSession.data.kind !== "react-element" || interactionSession.data.selector !== targetSelector || !interactionSession.data.section || editingElement || inlineEditRequest) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
const section = interactionSession.element.isConnected ? findContainingSection(interactionSession.element) : null;
|
|
515
|
+
if (!section || section === interactionSession.element) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
set({ sectionPreviewElement: section });
|
|
519
|
+
},
|
|
520
|
+
showElementSelectionFrames: ({ elements, isSection, scrollOffset }) => {
|
|
404
521
|
set({
|
|
405
522
|
selectionFrames: getSelectionFrameSpecs({
|
|
406
523
|
elements,
|
|
407
524
|
scrollOffset,
|
|
408
|
-
variant: "element"
|
|
525
|
+
variant: isSection ? "section" : "element"
|
|
409
526
|
})
|
|
410
527
|
});
|
|
411
528
|
},
|
|
@@ -424,11 +541,8 @@ const selectModeStore = createShallowStore(
|
|
|
424
541
|
setLastPointerClientPosition: (lastPointerClientPosition) => {
|
|
425
542
|
set({ lastPointerClientPosition });
|
|
426
543
|
},
|
|
427
|
-
|
|
428
|
-
set({
|
|
429
|
-
},
|
|
430
|
-
resetHoverRectDedup: () => {
|
|
431
|
-
set({ lastPostedHoverRect: void 0 });
|
|
544
|
+
setLastPostedHover: (lastPostedHover) => {
|
|
545
|
+
set({ lastPostedHover });
|
|
432
546
|
},
|
|
433
547
|
setHoverSyncAnimationFrameId: (hoverSyncAnimationFrameId) => {
|
|
434
548
|
set({ hoverSyncAnimationFrameId });
|
|
@@ -557,6 +671,11 @@ const useSelectionFrames = () => {
|
|
|
557
671
|
(state) => state.selectionFrames
|
|
558
672
|
);
|
|
559
673
|
};
|
|
674
|
+
const useSectionHoverRect = () => {
|
|
675
|
+
return useShallowStore(useSelectModeStore(), (state) => {
|
|
676
|
+
return state.displayMode === "selection" && state.interactionSession.type !== "drag" && state.lastPostedHover?.rect?.isSection ? state.lastPostedHover.rect : null;
|
|
677
|
+
});
|
|
678
|
+
};
|
|
560
679
|
const useIsSelectionFrameEditing = () => {
|
|
561
680
|
return useShallowStore(
|
|
562
681
|
useSelectModeStore(),
|
|
@@ -579,6 +698,7 @@ export {
|
|
|
579
698
|
useEditingElement,
|
|
580
699
|
useInteractionSession,
|
|
581
700
|
useIsSelectionFrameEditing,
|
|
701
|
+
useSectionHoverRect,
|
|
582
702
|
useSelectModeStore,
|
|
583
703
|
useSelectModeStoreActions,
|
|
584
704
|
useSelectionFrames
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export declare const SELECTION_FRAMES_CONTAINER_ID = "replo-select-mode-selection-frames";
|
|
2
2
|
export declare const SELECTION_FRAME_BORDER_HEX = "#c026d3";
|
|
3
3
|
export declare const SELECTION_FRAME_EDITING_BORDER_HEX = "#e879f9";
|
|
4
|
-
export
|
|
4
|
+
export declare const SECTION_SELECTION_FRAME_BORDER_HEX = "#60a5fa";
|
|
5
|
+
export declare const SECTION_SELECTION_FRAME_BACKGROUND = "rgba(96, 165, 250, 0.06)";
|
|
6
|
+
export type SelectionFrameVariant = "element" | "area" | "section";
|
|
5
7
|
export type SelectionFramePageRect = {
|
|
6
8
|
x: number;
|
|
7
9
|
y: number;
|
|
@@ -1,6 +1,8 @@
|
|
|
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
|
+
const SECTION_SELECTION_FRAME_BORDER_HEX = "#60a5fa";
|
|
5
|
+
const SECTION_SELECTION_FRAME_BACKGROUND = "rgba(96, 165, 250, 0.06)";
|
|
4
6
|
function getSelectionFrameSpecs({
|
|
5
7
|
elements,
|
|
6
8
|
scrollOffset,
|
|
@@ -20,6 +22,8 @@ function getSelectionFrameSpecs({
|
|
|
20
22
|
});
|
|
21
23
|
}
|
|
22
24
|
export {
|
|
25
|
+
SECTION_SELECTION_FRAME_BACKGROUND,
|
|
26
|
+
SECTION_SELECTION_FRAME_BORDER_HEX,
|
|
23
27
|
SELECTION_FRAMES_CONTAINER_ID,
|
|
24
28
|
SELECTION_FRAME_BORDER_HEX,
|
|
25
29
|
SELECTION_FRAME_EDITING_BORDER_HEX,
|