@replohq/sandbox-runtime 1.20.0 → 1.21.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.
Files changed (46) hide show
  1. package/_vendor/schemas/comments.d.ts +898 -0
  2. package/_vendor/schemas/commentsOverlayRpc.d.ts +348 -0
  3. package/_vendor/schemas/commentsOverlayRpc.mjs +92 -0
  4. package/_vendor/schemas/generated/comments.d.ts +237 -0
  5. package/_vendor/schemas/generated/commentsOverlayRpc.d.ts +18 -0
  6. package/_vendor/schemas/generated/selectMode.d.ts +3 -1
  7. package/_vendor/schemas/selectMode.d.ts +113 -0
  8. package/_vendor/schemas/selectMode.mjs +1 -1
  9. package/_vendor/schemas/selectModeIframeRpc.d.ts +226 -0
  10. package/_vendor/schemas/selectModeIframeRpc.mjs +1 -1
  11. package/buildMetadata.js +3 -3
  12. package/chunk-NVOBD46N.mjs +541 -0
  13. package/comments-overlay/CommentsOverlayMount.d.ts +1 -0
  14. package/comments-overlay/CommentsOverlayMount.js +44 -0
  15. package/comments-overlay/LICENSE-THIRD-PARTY.md +45 -0
  16. package/comments-overlay/bridge.d.ts +11 -0
  17. package/comments-overlay/bridge.js +136 -0
  18. package/comments-overlay/capture.d.ts +55 -0
  19. package/comments-overlay/capture.js +250 -0
  20. package/comments-overlay/originGate.d.ts +1 -0
  21. package/comments-overlay/originGate.js +11 -0
  22. package/comments-overlay/overlay.d.ts +52 -0
  23. package/comments-overlay/overlay.js +781 -0
  24. package/comments-overlay/resolve.d.ts +27 -0
  25. package/comments-overlay/resolve.js +201 -0
  26. package/comments-overlay/sourceLookup.d.ts +11 -0
  27. package/comments-overlay/sourceLookup.js +63 -0
  28. package/comments-overlay/textQuote.d.ts +17 -0
  29. package/comments-overlay/textQuote.js +87 -0
  30. package/comments-overlay/vendor/bippy.d.ts +53 -0
  31. package/comments-overlay/vendor/bippy.js +402 -0
  32. package/comments-overlay/vendor/elementAnchors.d.ts +9 -0
  33. package/comments-overlay/vendor/elementAnchors.js +144 -0
  34. package/comments-overlay/vendor/fiberElement.d.ts +21 -0
  35. package/comments-overlay/vendor/fiberElement.js +251 -0
  36. package/comments-overlay/vendor/htmlPreview.d.ts +13 -0
  37. package/comments-overlay/vendor/htmlPreview.js +179 -0
  38. package/comments-overlay/vendor/selector.d.ts +38 -0
  39. package/comments-overlay/vendor/selector.js +372 -0
  40. package/package.json +4 -4
  41. package/sandbox-runtime.js +2 -0
  42. package/select-mode/descriptor/elementDescriptor.d.ts +4 -3
  43. package/select-mode/descriptor/elementDescriptor.js +20 -30
  44. package/select-mode/iframeRpcChannel.d.ts +84 -0
  45. package/select-mode/symbolicate.js +25 -7
  46. package/chunk-QTGDSYSU.mjs +0 -249
@@ -0,0 +1,372 @@
1
+ /*!
2
+ * Copyright (c) 2025 Aiden Bai
3
+ * SPDX-License-Identifier: MIT
4
+ * https://github.com/aidenybai/react-grab
5
+ */
6
+ const FINDER_TIMEOUT_MS = 200;
7
+ const MAX_SELECTOR_COMBINATIONS = 1e4;
8
+ const SELECTOR_ATTR_VALUE_MAX_LENGTH_CHARS = 120;
9
+ const SEED_MIN_LENGTH = 3;
10
+ const ATTR_VALUE_MAX_LENGTH_CHARS = 100;
11
+ const BROAD_SELECTOR_TARGET_DESCENDANT_RATIO = 0.5;
12
+ const PENALTY_ID = 0;
13
+ const PENALTY_CLASS = 1;
14
+ const PENALTY_ATTR = 2;
15
+ const PENALTY_TAG = 5;
16
+ const PENALTY_NTH_OF_TYPE = 10;
17
+ const PENALTY_NTH_CHILD = 50;
18
+ const PREFERRED_SELECTOR_ATTRIBUTE_NAMES = [
19
+ "data-testid",
20
+ "data-test-id",
21
+ "data-test",
22
+ "data-cy",
23
+ "data-qa",
24
+ "aria-label",
25
+ "href",
26
+ "src",
27
+ "role",
28
+ "name",
29
+ "title",
30
+ "alt"
31
+ ];
32
+ const ACTIONABLE_SELECTOR_ROLES = /* @__PURE__ */ new Set([
33
+ "button",
34
+ "link",
35
+ "checkbox",
36
+ "radio",
37
+ "switch",
38
+ "tab",
39
+ "menuitem",
40
+ "option",
41
+ "textbox",
42
+ "combobox",
43
+ "slider",
44
+ "spinbutton"
45
+ ]);
46
+ const ACCEPTED_ATTR_NAMES = /* @__PURE__ */ new Set([
47
+ "role",
48
+ "name",
49
+ "aria-label",
50
+ "rel",
51
+ "href"
52
+ ]);
53
+ const UUID_ELEMENT_ID_PATTERN = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
54
+ const REACT_COLON_ELEMENT_ID_PATTERN = /:r[a-z0-9]+:/i;
55
+ const REACT_UNDERSCORE_ELEMENT_ID_PATTERN = /_r_[a-z0-9]+_(?:$|-)/i;
56
+ const REACT_GUILLEMET_ELEMENT_ID_PATTERN = /«r[a-z0-9]+»/i;
57
+ const GENERATED_FRAMEWORK_ELEMENT_ID_PATTERN = /^(?:downshift-\d+(?:-|$)|headlessui-[a-z-]+-\d+(?:-|$)|mui-\d+(?:-|$)|radix-\d+(?:-|$)|react-aria-\d+(?:-|$)|react-select-\d+(?:-|$))/i;
58
+ const EMBER_ELEMENT_ID_PATTERN = /^ember\d+$/i;
59
+ const NUMERIC_ELEMENT_ID_PATTERN = /^\d+$/;
60
+ function isStableElementId(elementId) {
61
+ return elementId.length > 0 && elementId.length <= SELECTOR_ATTR_VALUE_MAX_LENGTH_CHARS && !REACT_COLON_ELEMENT_ID_PATTERN.test(elementId) && !UUID_ELEMENT_ID_PATTERN.test(elementId) && !REACT_UNDERSCORE_ELEMENT_ID_PATTERN.test(elementId) && !REACT_GUILLEMET_ELEMENT_ID_PATTERN.test(elementId) && !GENERATED_FRAMEWORK_ELEMENT_ID_PATTERN.test(elementId) && !EMBER_ELEMENT_ID_PATTERN.test(elementId) && !NUMERIC_ELEMENT_ID_PATTERN.test(elementId);
62
+ }
63
+ function isPreferredSelectorAttribute(name, value) {
64
+ return PREFERRED_SELECTOR_ATTRIBUTE_NAMES.includes(name) && value.length > 0 && value.length <= SELECTOR_ATTR_VALUE_MAX_LENGTH_CHARS && (name !== "role" || value.split(/\s+/).some((role) => ACTIONABLE_SELECTOR_ROLES.has(role)));
65
+ }
66
+ function getSelectorRoot(element) {
67
+ const rootNode = element.getRootNode();
68
+ return rootNode instanceof Document || typeof ShadowRoot !== "undefined" && rootNode instanceof ShadowRoot ? rootNode : element.ownerDocument;
69
+ }
70
+ function isSelectorUniqueForElement(element, selector) {
71
+ try {
72
+ const matches = getSelectorRoot(element).querySelectorAll(selector);
73
+ return matches.length === 1 && matches[0] === element;
74
+ } catch {
75
+ return false;
76
+ }
77
+ }
78
+ function createSemanticSelector(element) {
79
+ const elementId = element.getAttribute("id");
80
+ if (elementId && isStableElementId(elementId)) {
81
+ const idSelector = `#${CSS.escape(elementId)}`;
82
+ if (isSelectorUniqueForElement(element, idSelector)) {
83
+ return idSelector;
84
+ }
85
+ }
86
+ for (const attributeName of PREFERRED_SELECTOR_ATTRIBUTE_NAMES) {
87
+ const attributeValue = element.getAttribute(attributeName);
88
+ if (!attributeValue || !isPreferredSelectorAttribute(attributeName, attributeValue)) {
89
+ continue;
90
+ }
91
+ const attributeSelector = `[${attributeName}=${JSON.stringify(attributeValue)}]`;
92
+ if (isSelectorUniqueForElement(element, attributeSelector)) {
93
+ return attributeSelector;
94
+ }
95
+ const tagSelector = `${element.tagName.toLowerCase()}${attributeSelector}`;
96
+ if (isSelectorUniqueForElement(element, tagSelector)) {
97
+ return tagSelector;
98
+ }
99
+ }
100
+ return null;
101
+ }
102
+ const SELECTOR_IDENTIFIER_QUERY = [
103
+ ...PREFERRED_SELECTOR_ATTRIBUTE_NAMES.filter((name) => name !== "role").map(
104
+ (name) => `[${name}]`
105
+ ),
106
+ ...Array.from(ACTIONABLE_SELECTOR_ROLES).map((role) => `[role~="${role}"]`)
107
+ ].join(",");
108
+ const GENERIC_SELECTOR_TARGET_QUERY = "button,input,select,textarea";
109
+ function hasSelectorIdentifier(element) {
110
+ const elementId = element.getAttribute("id");
111
+ return Boolean(
112
+ elementId && isStableElementId(elementId) || element.matches(SELECTOR_IDENTIFIER_QUERY)
113
+ );
114
+ }
115
+ function isSelectorTarget(element) {
116
+ return hasSelectorIdentifier(element) || element.matches(GENERIC_SELECTOR_TARGET_QUERY);
117
+ }
118
+ function isBroadSelectorTarget(element) {
119
+ const root = getSelectorRoot(element);
120
+ let bodyDescendantCount;
121
+ if (root instanceof Document) {
122
+ const { body, documentElement } = root;
123
+ if (element === body || element === documentElement) {
124
+ return true;
125
+ }
126
+ if (!body) {
127
+ return false;
128
+ }
129
+ bodyDescendantCount = body.getElementsByTagName("*").length;
130
+ } else {
131
+ bodyDescendantCount = root.querySelectorAll("*").length;
132
+ }
133
+ if (bodyDescendantCount === 0) {
134
+ return false;
135
+ }
136
+ return element.getElementsByTagName("*").length / bodyDescendantCount >= BROAD_SELECTOR_TARGET_DESCENDANT_RATIO;
137
+ }
138
+ function findSelectorTarget(element, isCandidateAccepted) {
139
+ let current = element;
140
+ while (current) {
141
+ if (isSelectorTarget(current)) {
142
+ const isBroad = isBroadSelectorTarget(current);
143
+ if (isBroad && current !== element) {
144
+ return element;
145
+ }
146
+ if (!isCandidateAccepted || isCandidateAccepted(current)) {
147
+ return current;
148
+ }
149
+ if (isBroad) {
150
+ return current;
151
+ }
152
+ if (!hasSelectorIdentifier(current) && current === element) {
153
+ return current;
154
+ }
155
+ }
156
+ current = current.parentElement;
157
+ }
158
+ return element;
159
+ }
160
+ function buildRelativeNthOfTypePath(ancestor, element) {
161
+ const parts = [];
162
+ for (let current = element; current && current !== ancestor; current = current.parentElement) {
163
+ const tagName = current.tagName;
164
+ const parent = current.parentElement ?? current.parentNode;
165
+ const siblings = parent && "children" in parent ? Array.from(parent.children).filter((sibling) => {
166
+ return sibling.tagName === tagName;
167
+ }) : [];
168
+ parts.unshift(
169
+ siblings.length > 1 ? `${tagName.toLowerCase()}:nth-of-type(${siblings.indexOf(current) + 1})` : tagName.toLowerCase()
170
+ );
171
+ }
172
+ return parts.join(" > ");
173
+ }
174
+ function createNearestSemanticSelector(element) {
175
+ const ownSelector = createSemanticSelector(element);
176
+ if (ownSelector) {
177
+ return ownSelector;
178
+ }
179
+ const accepted = { selector: null };
180
+ const target = findSelectorTarget(element, (candidate) => {
181
+ accepted.selector = createSemanticSelector(candidate);
182
+ return accepted.selector !== null;
183
+ });
184
+ if (target === element || !accepted.selector) {
185
+ return null;
186
+ }
187
+ const selector = `${accepted.selector} > ${buildRelativeNthOfTypePath(target, element)}`;
188
+ return isSelectorUniqueForElement(element, selector) ? selector : null;
189
+ }
190
+ function isWordLike(text) {
191
+ if (!/^[a-z-]{3,}$/i.test(text)) {
192
+ return false;
193
+ }
194
+ return text.split(/-|[A-Z]/).every((segment) => {
195
+ return segment.length > 2 && !/[^aeiou]{4,}/i.test(segment);
196
+ });
197
+ }
198
+ function isAcceptedAttr(name, value) {
199
+ const nameIsAccepted = ACCEPTED_ATTR_NAMES.has(name) || name.startsWith("data-") && isWordLike(name);
200
+ const valueIsAccepted = isWordLike(value) && value.length < ATTR_VALUE_MAX_LENGTH_CHARS || value.startsWith("#") && isWordLike(value.slice(1));
201
+ return nameIsAccepted && valueIsAccepted;
202
+ }
203
+ function buildSelectorString(path) {
204
+ return path.map((node) => node.name).reverse().join(" > ");
205
+ }
206
+ function calculatePenalty(path) {
207
+ return path.reduce((total, node) => total + node.penalty, 0);
208
+ }
209
+ function getChildIndex(element, filterTagName) {
210
+ const parentNode = element.parentNode;
211
+ if (!parentNode) {
212
+ return void 0;
213
+ }
214
+ let position = 0;
215
+ for (const sibling of Array.from(parentNode.children)) {
216
+ if (filterTagName === void 0 || sibling.tagName.toLowerCase() === filterTagName) {
217
+ position++;
218
+ }
219
+ if (sibling === element) {
220
+ return position;
221
+ }
222
+ }
223
+ return void 0;
224
+ }
225
+ function collectCandidateNodes(element) {
226
+ const candidates = [];
227
+ const elementId = element.getAttribute("id");
228
+ const tagName = element.tagName.toLowerCase();
229
+ if (elementId && isWordLike(elementId)) {
230
+ candidates.push({ name: `#${CSS.escape(elementId)}`, penalty: PENALTY_ID });
231
+ }
232
+ for (const className of Array.from(element.classList)) {
233
+ if (isWordLike(className)) {
234
+ candidates.push({
235
+ name: `.${CSS.escape(className)}`,
236
+ penalty: PENALTY_CLASS
237
+ });
238
+ }
239
+ }
240
+ for (const attribute of Array.from(element.attributes)) {
241
+ if (isAcceptedAttr(attribute.name, attribute.value) || isPreferredSelectorAttribute(attribute.name, attribute.value)) {
242
+ candidates.push({
243
+ name: `[${CSS.escape(attribute.name)}="${CSS.escape(attribute.value)}"]`,
244
+ penalty: PENALTY_ATTR
245
+ });
246
+ }
247
+ }
248
+ candidates.push({ name: tagName, penalty: PENALTY_TAG });
249
+ const typePosition = getChildIndex(element, tagName);
250
+ if (typePosition !== void 0) {
251
+ candidates.push({
252
+ name: tagName === "html" ? "html" : `${tagName}:nth-of-type(${typePosition})`,
253
+ penalty: PENALTY_NTH_OF_TYPE
254
+ });
255
+ }
256
+ const childPosition = getChildIndex(element);
257
+ if (childPosition !== void 0) {
258
+ candidates.push({
259
+ name: tagName === "html" ? "html" : `${tagName}:nth-child(${childPosition})`,
260
+ penalty: PENALTY_NTH_CHILD
261
+ });
262
+ }
263
+ return candidates;
264
+ }
265
+ function collectCombinations(stack, budget = MAX_SELECTOR_COMBINATIONS, currentPath = []) {
266
+ if (budget <= 0) {
267
+ return [];
268
+ }
269
+ const [level, ...rest] = stack;
270
+ if (!level) {
271
+ return [currentPath];
272
+ }
273
+ const results = [];
274
+ for (const node of level) {
275
+ const remainingBudget = budget - results.length;
276
+ if (remainingBudget <= 0) {
277
+ break;
278
+ }
279
+ results.push(
280
+ ...collectCombinations(rest, remainingBudget, [...currentPath, node])
281
+ );
282
+ }
283
+ return results;
284
+ }
285
+ function isPathUnique(path, root) {
286
+ try {
287
+ return root.querySelectorAll(buildSelectorString(path)).length === 1;
288
+ } catch {
289
+ return false;
290
+ }
291
+ }
292
+ function findUniqueSelector(target, timeoutMs = FINDER_TIMEOUT_MS) {
293
+ if (target.tagName.toLowerCase() === "html") {
294
+ return "html";
295
+ }
296
+ const root = getSelectorRoot(target);
297
+ const stopElement = root instanceof Document ? root.documentElement : null;
298
+ const startTime = Date.now();
299
+ const ancestorStack = [];
300
+ let current = target;
301
+ let depth = 0;
302
+ const findIn = (paths) => {
303
+ paths.sort(
304
+ (pathA, pathB) => calculatePenalty(pathA) - calculatePenalty(pathB)
305
+ );
306
+ for (const path of paths) {
307
+ if (Date.now() - startTime > timeoutMs) {
308
+ return "timeout";
309
+ }
310
+ if (isPathUnique(path, root)) {
311
+ return path;
312
+ }
313
+ }
314
+ return null;
315
+ };
316
+ while (current && current !== stopElement) {
317
+ ancestorStack.push(collectCandidateNodes(current));
318
+ current = current.parentElement;
319
+ depth++;
320
+ if (depth >= SEED_MIN_LENGTH) {
321
+ const found = findIn(collectCombinations(ancestorStack));
322
+ if (found === "timeout") {
323
+ return null;
324
+ }
325
+ if (found) {
326
+ return buildSelectorString(found);
327
+ }
328
+ }
329
+ }
330
+ if (depth < SEED_MIN_LENGTH) {
331
+ const found = findIn(collectCombinations(ancestorStack));
332
+ if (found && found !== "timeout") {
333
+ return buildSelectorString(found);
334
+ }
335
+ }
336
+ return null;
337
+ }
338
+ function createNthChildSelector(element) {
339
+ const segments = [];
340
+ const body = element.ownerDocument.body;
341
+ let current = element;
342
+ while (current) {
343
+ const currentId = current.getAttribute("id");
344
+ if (currentId) {
345
+ segments.unshift(`#${CSS.escape(currentId)}`);
346
+ break;
347
+ }
348
+ const parent = current.parentElement;
349
+ if (!parent) {
350
+ segments.unshift(current.tagName.toLowerCase());
351
+ break;
352
+ }
353
+ const nthChild = Array.from(parent.children).indexOf(current) + 1;
354
+ segments.unshift(`${current.tagName.toLowerCase()}:nth-child(${nthChild})`);
355
+ if (parent === body) {
356
+ segments.unshift("body");
357
+ break;
358
+ }
359
+ current = parent;
360
+ }
361
+ return segments.join(" > ");
362
+ }
363
+ export {
364
+ FINDER_TIMEOUT_MS,
365
+ buildRelativeNthOfTypePath,
366
+ createNearestSemanticSelector,
367
+ createNthChildSelector,
368
+ createSemanticSelector,
369
+ findSelectorTarget,
370
+ findUniqueSelector,
371
+ isStableElementId
372
+ };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@replohq/sandbox-runtime",
3
- "version": "1.20.0",
3
+ "version": "1.21.0",
4
4
  "reploBuild": {
5
5
  "packageName": "@replohq/sandbox-runtime",
6
- "version": "1.20.0",
6
+ "version": "1.21.0",
7
7
  "branch": "HEAD",
8
- "commit": "17c6d4e2e064b7d35efe5732361372f119e931b5",
9
- "builtAt": "2026-10-29T03:14:43.608Z"
8
+ "commit": "d5ce2185ff844319f59248b9da6641bf96f4c0a2",
9
+ "builtAt": "2026-10-29T04:07:57.675Z"
10
10
  },
11
11
  "description": "Replo sandbox runtime — editor integration for agent-built Next.js sites.",
12
12
  "license": "SEE LICENSE IN LICENSE",
@@ -3,6 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useViewportSnapshotResponder } from "./_vendor/replo-utils/react/use-viewport-snapshot-responder.mjs";
4
4
  import { AnnotationLayerMount } from "./annotations/AnnotationLayerMount";
5
5
  import { ClientErrorBoundary } from "./ClientErrorBoundary";
6
+ import { CommentsOverlayMount } from "./comments-overlay/CommentsOverlayMount";
6
7
  import { DesignLibraryPreview } from "./design-library-preview/DesignLibraryPreview";
7
8
  import { DocumentReadyBeacon } from "./DocumentReadyBeacon";
8
9
  import { HeatLayerMount } from "./heatmaps/HeatLayerMount";
@@ -28,6 +29,7 @@ function SandboxRuntime({ children }) {
28
29
  /* @__PURE__ */ jsx(PageCaptureResponder, {}),
29
30
  /* @__PURE__ */ jsx(PagePreviewHeightResponder, {}),
30
31
  /* @__PURE__ */ jsx(EditorSelection, {}),
32
+ /* @__PURE__ */ jsx(CommentsOverlayMount, {}),
31
33
  /* @__PURE__ */ jsx(AnnotationLayerMount, {}),
32
34
  /* @__PURE__ */ jsx(HeatLayerMount, {}),
33
35
  /* @__PURE__ */ jsx(DesignLibraryPreview, {}),
@@ -5,9 +5,10 @@ import type { SelectableData, SelectModeClickPosition } from "../../_vendor/sche
5
5
  */
6
6
  export declare function getNearestSelectableElement(element: Element): Element;
7
7
  /**
8
- * Build a CSS selector path from `element` up to the document root. Stops
9
- * (and short-circuits) at the first ancestor with an `id`. Used so the
10
- * parent can identify the element later without holding a DOM reference.
8
+ * The cheap structural selector for selections, siblings, sections and override
9
+ * targets, so they stay comparable (e.g. `isSelectedSection`).
10
+ * Runs once per repeated sibling on every click, so it must not use the
11
+ * capture's uniqueness search, which costs up to 200ms per element.
11
12
  */
12
13
  export declare function buildSelector(element: Element): string;
13
14
  /**
@@ -4,6 +4,10 @@ import {
4
4
  SELECT_MODE_SELECTABLE_PAYLOAD_ATTRIBUTE
5
5
  } from "../../_vendor/schemas/selectMode.mjs";
6
6
  import * as z from "zod";
7
+ import {
8
+ buildNthOfTypePath,
9
+ captureElementAnchor
10
+ } from "../../comments-overlay/capture";
7
11
  import { getFiberChain } from "../fiber";
8
12
  import { getImageDescriptor } from "../imageSources";
9
13
  import { skipInlineTextMarkElements } from "../inlineTextMarks";
@@ -155,34 +159,19 @@ function identifyElement({
155
159
  return tagName;
156
160
  }
157
161
  function buildSelector(element) {
158
- const parts = [];
159
- let currentElement = element;
160
- while (currentElement && currentElement !== document.documentElement) {
161
- const tag = currentElement.tagName.toLowerCase();
162
- const id = currentElement.id;
163
- if (id) {
164
- parts.unshift(`#${CSS.escape(id)}`);
165
- break;
166
- }
167
- const parentElement = currentElement.parentElement;
168
- if (parentElement) {
169
- const currentTag = currentElement.tagName;
170
- const siblings = Array.from(parentElement.children);
171
- const siblingElementsWithSameTag = siblings.filter((sibling) => {
172
- return sibling.tagName === currentTag;
173
- });
174
- if (siblingElementsWithSameTag.length > 1) {
175
- const index = siblingElementsWithSameTag.indexOf(currentElement) + 1;
176
- parts.unshift(`${tag}:nth-of-type(${index})`);
177
- } else {
178
- parts.unshift(tag);
179
- }
180
- } else {
181
- parts.unshift(tag);
182
- }
183
- currentElement = parentElement;
162
+ return buildNthOfTypePath(element);
163
+ }
164
+ function getAnchorSourceLocation(anchor) {
165
+ const source = anchor.source;
166
+ if (!source || source.line === void 0) {
167
+ return void 0;
184
168
  }
185
- return parts.join(" > ");
169
+ return {
170
+ filePath: source.filePath,
171
+ lineNumber: source.line,
172
+ ...source.col === void 0 ? {} : { columnNumber: source.col },
173
+ ...source.componentChain[0] ? { componentName: source.componentChain[0] } : {}
174
+ };
186
175
  }
187
176
  function getTextMetrics(element) {
188
177
  const text = element.textContent?.trim();
@@ -228,10 +217,10 @@ function buildElementDescriptor({
228
217
  const video = z.instanceof(HTMLVideoElement).safeParse(element);
229
218
  const textMetrics = getTextMetrics(element);
230
219
  const fiberChain = element instanceof HTMLElement ? getFiberChain(element) : [];
231
- const sourceLocation = fiberChain[0]?.sourceLocation;
232
220
  const trimmedText = getRenderedText(element);
233
221
  const sectionLabel = findSectionLabel(section);
234
222
  const { definedStyles, authoredStyles } = getElementStyles(element);
223
+ const anchor = captureElementAnchor(element, clickPosition);
235
224
  return {
236
225
  kind: "react-element",
237
226
  selector: buildSelector(element),
@@ -251,10 +240,11 @@ function buildElementDescriptor({
251
240
  authoredStyles,
252
241
  inheritedStyles: getInheritedStyles(element),
253
242
  clickPosition,
254
- sourceLocation,
243
+ sourceLocation: getAnchorSourceLocation(anchor),
255
244
  fiberChain,
256
245
  siblingSelectors,
257
- ...section ? { section: { selector: buildSelector(section) } } : {}
246
+ ...section ? { section: { selector: buildSelector(section) } } : {},
247
+ anchor
258
248
  };
259
249
  }
260
250
  export {
@@ -134,6 +134,34 @@ export declare function getSelectModeIframeRpcChannel(): import("../_vendor/sche
134
134
  props: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>;
135
135
  }, import("zod/v4/core").$strip>>>;
136
136
  siblingSelectors: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodString>>;
137
+ anchor: import("zod").ZodOptional<import("zod").ZodObject<{
138
+ type: import("zod").ZodLiteral<"element">;
139
+ semanticSelector: import("zod").ZodOptional<import("zod").ZodString>;
140
+ selector: import("zod").ZodString;
141
+ nthOfTypePath: import("zod").ZodString;
142
+ source: import("zod").ZodOptional<import("zod").ZodObject<{
143
+ filePath: import("zod").ZodString;
144
+ componentChain: import("zod").ZodArray<import("zod").ZodString>;
145
+ line: import("zod").ZodOptional<import("zod").ZodNumber>;
146
+ col: import("zod").ZodOptional<import("zod").ZodNumber>;
147
+ }, import("zod/v4/core").$strip>>;
148
+ listKey: import("zod").ZodOptional<import("zod").ZodString>;
149
+ tagName: import("zod").ZodString;
150
+ textPreview: import("zod").ZodString;
151
+ quote: import("zod").ZodOptional<import("zod").ZodObject<{
152
+ exact: import("zod").ZodString;
153
+ prefix: import("zod").ZodString;
154
+ suffix: import("zod").ZodString;
155
+ }, import("zod/v4/core").$strip>>;
156
+ offset: import("zod").ZodObject<{
157
+ x: import("zod").ZodNumber;
158
+ y: import("zod").ZodNumber;
159
+ }, import("zod/v4/core").$strip>;
160
+ context: import("zod").ZodOptional<import("zod").ZodObject<{
161
+ url: import("zod").ZodString;
162
+ locale: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
163
+ }, import("zod/v4/core").$strip>>;
164
+ }, import("zod/v4/core").$strip>>;
137
165
  }, import("zod/v4/core").$strip>;
138
166
  }, import("zod/v4/core").$strip>;
139
167
  outputSchema: import("zod").ZodObject<{
@@ -235,6 +263,34 @@ export declare function getSelectModeIframeRpcChannel(): import("../_vendor/sche
235
263
  props: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>;
236
264
  }, import("zod/v4/core").$strip>>>;
237
265
  siblingSelectors: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodString>>;
266
+ anchor: import("zod").ZodOptional<import("zod").ZodObject<{
267
+ type: import("zod").ZodLiteral<"element">;
268
+ semanticSelector: import("zod").ZodOptional<import("zod").ZodString>;
269
+ selector: import("zod").ZodString;
270
+ nthOfTypePath: import("zod").ZodString;
271
+ source: import("zod").ZodOptional<import("zod").ZodObject<{
272
+ filePath: import("zod").ZodString;
273
+ componentChain: import("zod").ZodArray<import("zod").ZodString>;
274
+ line: import("zod").ZodOptional<import("zod").ZodNumber>;
275
+ col: import("zod").ZodOptional<import("zod").ZodNumber>;
276
+ }, import("zod/v4/core").$strip>>;
277
+ listKey: import("zod").ZodOptional<import("zod").ZodString>;
278
+ tagName: import("zod").ZodString;
279
+ textPreview: import("zod").ZodString;
280
+ quote: import("zod").ZodOptional<import("zod").ZodObject<{
281
+ exact: import("zod").ZodString;
282
+ prefix: import("zod").ZodString;
283
+ suffix: import("zod").ZodString;
284
+ }, import("zod/v4/core").$strip>>;
285
+ offset: import("zod").ZodObject<{
286
+ x: import("zod").ZodNumber;
287
+ y: import("zod").ZodNumber;
288
+ }, import("zod/v4/core").$strip>;
289
+ context: import("zod").ZodOptional<import("zod").ZodObject<{
290
+ url: import("zod").ZodString;
291
+ locale: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
292
+ }, import("zod/v4/core").$strip>>;
293
+ }, import("zod/v4/core").$strip>>;
238
294
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
239
295
  kind: import("zod").ZodLiteral<"plan">;
240
296
  planId: import("zod").ZodString;
@@ -424,6 +480,34 @@ export declare function getSelectModeIframeRpcChannel(): import("../_vendor/sche
424
480
  props: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>;
425
481
  }, import("zod/v4/core").$strip>>>;
426
482
  siblingSelectors: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodString>>;
483
+ anchor: import("zod").ZodOptional<import("zod").ZodObject<{
484
+ type: import("zod").ZodLiteral<"element">;
485
+ semanticSelector: import("zod").ZodOptional<import("zod").ZodString>;
486
+ selector: import("zod").ZodString;
487
+ nthOfTypePath: import("zod").ZodString;
488
+ source: import("zod").ZodOptional<import("zod").ZodObject<{
489
+ filePath: import("zod").ZodString;
490
+ componentChain: import("zod").ZodArray<import("zod").ZodString>;
491
+ line: import("zod").ZodOptional<import("zod").ZodNumber>;
492
+ col: import("zod").ZodOptional<import("zod").ZodNumber>;
493
+ }, import("zod/v4/core").$strip>>;
494
+ listKey: import("zod").ZodOptional<import("zod").ZodString>;
495
+ tagName: import("zod").ZodString;
496
+ textPreview: import("zod").ZodString;
497
+ quote: import("zod").ZodOptional<import("zod").ZodObject<{
498
+ exact: import("zod").ZodString;
499
+ prefix: import("zod").ZodString;
500
+ suffix: import("zod").ZodString;
501
+ }, import("zod/v4/core").$strip>>;
502
+ offset: import("zod").ZodObject<{
503
+ x: import("zod").ZodNumber;
504
+ y: import("zod").ZodNumber;
505
+ }, import("zod/v4/core").$strip>;
506
+ context: import("zod").ZodOptional<import("zod").ZodObject<{
507
+ url: import("zod").ZodString;
508
+ locale: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
509
+ }, import("zod/v4/core").$strip>>;
510
+ }, import("zod/v4/core").$strip>>;
427
511
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
428
512
  kind: import("zod").ZodLiteral<"plan">;
429
513
  planId: import("zod").ZodString;
@@ -229,13 +229,17 @@ async function symbolicateSelectedElement(selectedElement) {
229
229
  });
230
230
  }
231
231
  }
232
- let sourceLocation = selectedElement.sourceLocation;
233
- if (fiberChain.length === 0 && sourceLocation && isLikelyCompiledLocation(sourceLocation)) {
234
- const captured = sourceLocation;
232
+ const capturedSourceLocation = selectedElement.sourceLocation;
233
+ let sourceLocation = capturedSourceLocation;
234
+ const symbolicatedCapture = {
235
+ location: null
236
+ };
237
+ if (sourceLocation && isLikelyCompiledLocation(sourceLocation)) {
235
238
  targets.push({
236
- location: captured,
239
+ location: sourceLocation,
237
240
  apply: (next) => {
238
241
  sourceLocation = next;
242
+ symbolicatedCapture.location = next;
239
243
  }
240
244
  });
241
245
  }
@@ -249,13 +253,27 @@ async function symbolicateSelectedElement(selectedElement) {
249
253
  const frame = extractResolvedFrame(resolved[index] ?? null);
250
254
  target.apply(applyResolved({ original: target.location, frame }));
251
255
  }
252
- if (fiberChain.length > 0) {
253
- sourceLocation = findUserSourceLocation(fiberChain) ?? fiberChain[0].sourceLocation;
256
+ if (!isUserSourceLocation(sourceLocation)) {
257
+ sourceLocation = findUserSourceLocation(fiberChain) ?? sourceLocation ?? fiberChain[0]?.sourceLocation;
254
258
  }
259
+ const anchor = selectedElement.anchor;
260
+ const anchorSource = anchor?.source;
261
+ const resolvedCapture = anchorSource && anchorSource.filePath === capturedSourceLocation?.filePath && anchorSource.line === capturedSourceLocation.lineNumber ? symbolicatedCapture.location : null;
255
262
  return {
256
263
  ...selectedElement,
257
264
  sourceLocation,
258
- fiberChain
265
+ fiberChain,
266
+ ...anchor && anchorSource && resolvedCapture ? {
267
+ anchor: {
268
+ ...anchor,
269
+ source: {
270
+ ...anchorSource,
271
+ filePath: resolvedCapture.filePath,
272
+ line: resolvedCapture.lineNumber,
273
+ col: resolvedCapture.columnNumber
274
+ }
275
+ }
276
+ } : {}
259
277
  };
260
278
  }
261
279
  export {