@comty.io/core 0.1.8 → 0.1.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comty.io/core",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Comty visual comments, inspector and local agent tools",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -37,8 +37,10 @@
37
37
  "test:inspector": "node --test tests/inspector.browser.test.mjs tests/inspect-markup.browser.test.mjs tests/idle-network.browser.test.mjs",
38
38
  "test:placement": "node --test tests/placement.browser.test.mjs",
39
39
  "test:anchors": "node --test tests/anchors.test.mjs tests/anchor-reload.browser.test.mjs",
40
+ "test:auth": "node --test tests/auth-return.browser.test.mjs",
40
41
  "test:vite": "node --test tests/vite.test.mjs",
41
- "prepack": "npm run build"
42
+ "prepack": "npm run build",
43
+ "test:isolation": "node --test tests/style-isolation.browser.test.mjs"
42
44
  },
43
45
  "devDependencies": {
44
46
  "esbuild": "0.28.2",
package/src/anchors.js CHANGED
@@ -1,427 +1,192 @@
1
- export function createAnchorMatcher({
2
- getElementTextSignature,
3
- getElementOwnTextSignature,
4
- getAttributeSignature,
5
- getSiblingFingerprint,
6
- getPresentationLayer,
7
- isComtyNode,
8
- }) {
9
- function includesText(haystack, needle) {
10
- if (!haystack || !needle) return false;
11
- return haystack.toLowerCase().includes(needle.toLowerCase());
1
+ import { createResilientAnchors, readAnchor } from "./resilient-anchor.js";
2
+
3
+ export function createAnchorMatcher({ getPresentationLayer, isComtyNode }) {
4
+ const resilient = createResilientAnchors(isComtyNode, getPresentationLayer);
5
+
6
+ function getStableAnchorId(id) {
7
+ // React useId values change with mount order, including Base UI and Radix IDs.
8
+ return /^(?:(?:radix|base-ui)-)?(?:_r_[\w-]+_|:r[\w-]+:)$/i.test(id || "")
9
+ ? ""
10
+ : id || "";
11
+ }
12
+
13
+ function resolveAnchor(context, exact = null, previous = null) {
14
+ const anchor = readAnchor(context);
15
+ return anchor
16
+ ? resilient.resolve(anchor, previous, exact)
17
+ : { element: null, status: "uncertain" };
18
+ }
19
+
20
+ function getViewportBounds() {
21
+ const viewport = window.visualViewport;
22
+ if (!viewport) {
23
+ return {
24
+ left: 0,
25
+ top: 0,
26
+ right: window.innerWidth,
27
+ bottom: window.innerHeight,
28
+ };
12
29
  }
13
30
 
14
- function similarText(a, b) {
15
- if (!a || !b) return false;
16
- const words = value => new Set(value.toLowerCase().split(/\s+/).filter(Boolean));
17
- const left = words(a), right = words(b);
18
- const common = [...left].filter(word => right.has(word)).length;
19
- return common >= 2 && common / Math.max(left.size, right.size) >= 0.7;
20
- }
31
+ return {
32
+ left: viewport.offsetLeft,
33
+ top: viewport.offsetTop,
34
+ right: viewport.offsetLeft + viewport.width,
35
+ bottom: viewport.offsetTop + viewport.height,
36
+ };
37
+ }
21
38
 
22
- function scoreFingerprint(el, fingerprint) {
23
- if (!el || !fingerprint) return 0;
24
- if (
25
- fingerprint.tag &&
26
- el.tagName.toLowerCase() !== fingerprint.tag.toLowerCase()
27
- ) {
28
- return -1000;
39
+ function isElementTreeHidden(el) {
40
+ let current = el;
41
+ while (current instanceof Element) {
42
+ if (!current.isConnected) return true;
43
+ if (current.hasAttribute("hidden")) return true;
44
+ if (current.hasAttribute("inert")) return true;
45
+ if (current.getAttribute("aria-hidden") === "true") {
46
+ return true;
29
47
  }
30
48
 
31
- let score = 0;
32
- const text = getElementTextSignature(el);
33
- const ownText = getElementOwnTextSignature(el);
34
-
35
- score += 20;
36
- if (fingerprint.id) {
37
- score += el.id.slice(0, 160) === fingerprint.id ? 70 : -30;
49
+ const style = getComputedStyle(current);
50
+ if (style.display === "none") return true;
51
+ if (style.visibility === "hidden" || style.visibility === "collapse") {
52
+ return true;
38
53
  }
39
- const exactAttributes = [
40
- ["role", "role", 24],
41
- ["ariaLabel", "aria-label", 34],
42
- ["name", "name", 30],
43
- ["type", "type", 12],
44
- ["placeholder", "placeholder", 24],
45
- ["alt", "alt", 28],
46
- ["href", "href", 30],
47
- ["src", "src", 24],
48
- ["title", "title", 20],
49
- ];
50
- for (const [key, attribute, weight] of exactAttributes) {
51
- if (!fingerprint[key]) continue;
52
- score +=
53
- getAttributeSignature(el, attribute) === fingerprint[key]
54
- ? weight
55
- : -Math.ceil(weight / 3);
54
+ if (Number.parseFloat(style.opacity) <= 0.01) return true;
55
+ if (style.contentVisibility === "hidden") {
56
+ return true;
56
57
  }
57
58
 
58
- const testId =
59
- getAttributeSignature(el, "data-testid") ||
60
- getAttributeSignature(el, "data-test") ||
61
- getAttributeSignature(el, "data-cy");
62
- if (fingerprint.testId) {
63
- score += testId === fingerprint.testId ? 55 : -20;
59
+ if (current.tagName === "DETAILS" && !current.hasAttribute("open")) {
60
+ const summary = current.querySelector(":scope > summary");
61
+ if (!summary || !summary.contains(el)) return true;
64
62
  }
65
63
 
66
- const classes = Array.isArray(fingerprint.classes)
67
- ? fingerprint.classes
68
- : [];
69
- const matchingClasses = classes.filter((className) =>
70
- el.classList.contains(className),
71
- );
72
- score += matchingClasses.length * 7;
73
- if (classes.length > 0 && matchingClasses.length === 0) score -= 8;
74
-
75
- if (fingerprint.ownText && ownText === fingerprint.ownText) score += 42;
76
- else if (
77
- fingerprint.ownText &&
78
- includesText(ownText, fingerprint.ownText.slice(0, 80))
79
- ) {
80
- score += 20;
81
- }
82
-
83
- if (fingerprint.text && text === fingerprint.text) score += 32;
84
- else if (
85
- fingerprint.text &&
86
- includesText(text, fingerprint.text.slice(0, 80))
87
- ) {
88
- score += 15;
89
- }
90
-
91
- if (text !== fingerprint.text && similarText(text, fingerprint.text)) score += 24;
92
-
93
- if (typeof fingerprint.siblingIndex === "number" && el.parentElement) {
94
- const siblings = [...el.parentElement.children].filter(
95
- (child) => child.tagName === el.tagName,
96
- );
97
- if (siblings.indexOf(el) === fingerprint.siblingIndex) score += 8;
98
- }
99
-
100
- if (
101
- typeof fingerprint.childCount === "number" &&
102
- el.children.length === fingerprint.childCount
103
- ) {
104
- score += 4;
105
- }
106
-
107
- const rect = el.getBoundingClientRect();
108
- if (
109
- fingerprint.width > 0 &&
110
- rect.width > 0 &&
111
- Math.abs(rect.width - fingerprint.width) /
112
- Math.max(rect.width, fingerprint.width) <
113
- 0.2
114
- ) {
115
- score += 4;
116
- }
117
- if (
118
- fingerprint.height > 0 &&
119
- rect.height > 0 &&
120
- Math.abs(rect.height - fingerprint.height) /
121
- Math.max(rect.height, fingerprint.height) <
122
- 0.2
123
- ) {
124
- score += 4;
125
- }
126
-
127
- return score;
128
- }
129
-
130
- function scoreSibling(el, fingerprint) {
131
- if (!fingerprint) return 0;
132
- const sibling = getSiblingFingerprint(el);
133
- if (!sibling || sibling.tag !== fingerprint.tag) return -4;
134
- let score = 4;
135
- if (fingerprint.ariaLabel && sibling.ariaLabel === fingerprint.ariaLabel) {
136
- score += 6;
137
- }
138
- if (fingerprint.role && sibling.role === fingerprint.role) score += 4;
139
- if (fingerprint.text && sibling.text === fingerprint.text) score += 8;
140
- return score;
64
+ current = current.parentElement;
141
65
  }
66
+ return false;
67
+ }
142
68
 
143
- function scoreAnchorCandidate(el, context) {
144
- if (!el || !context?.target) return 0;
145
- let score = scoreFingerprint(el, context.target);
146
- if (score < 0) return score;
69
+ function pointIsInsideRect(x, y, rect) {
70
+ return x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;
71
+ }
147
72
 
148
- score += scoreSibling(
149
- el.previousElementSibling,
150
- context.target.previousSibling,
73
+ function isPointClipped(el, x, y) {
74
+ let ancestor = el.parentElement;
75
+ while (
76
+ ancestor &&
77
+ ancestor !== document.body &&
78
+ ancestor !== document.documentElement
79
+ ) {
80
+ const style = getComputedStyle(ancestor);
81
+ const clipsX = ["auto", "scroll", "hidden", "clip"].includes(
82
+ style.overflowX,
151
83
  );
152
- score += scoreSibling(el.nextElementSibling, context.target.nextSibling);
153
-
154
- const ancestors = Array.isArray(context.ancestors)
155
- ? context.ancestors
156
- : [];
157
- let parent = el.parentElement;
158
- for (
159
- let index = 0;
160
- parent && index < ancestors.length;
161
- index += 1
162
- ) {
163
- const ancestorScore = scoreFingerprint(
164
- parent,
165
- ancestors[index],
166
- );
167
- if (ancestorScore > 0) {
168
- score += Math.min(ancestorScore, 40) * (index === 0 ? 0.5 : 0.2);
169
- }
170
- // Distinguish repeated cards by their section, even when dimensions match.
171
- if (index < 3 && ancestors[index].text && getElementTextSignature(parent) === ancestors[index].text) score += 18;
172
- parent = parent.parentElement;
173
- }
174
-
175
- if (context.layer) {
176
- const layer = getPresentationLayer(el);
177
- if (!layer) return -1000;
178
- const layerScore = scoreFingerprint(layer, context.layer);
179
- if (layerScore < 30) return -1000;
180
- score += Math.min(layerScore, 60) * 0.5;
181
- }
182
-
183
- return score;
184
- }
185
-
186
- function matchesAnchorIdentity(el, fingerprint, ignoreDescendantText = false, allowTextChanges = true) {
187
- if (!el || !fingerprint || el.tagName.toLowerCase() !== fingerprint.tag) return false;
188
- const attributes = { ariaLabel: "aria-label", name: "name", href: "href", src: "src", alt: "alt", title: "title", role: "role" };
189
- for (const [key, attribute] of Object.entries(attributes)) {
190
- if (fingerprint[key] && getAttributeSignature(el, attribute) !== fingerprint[key]) return false;
191
- }
192
- if (fingerprint.id && el.id !== fingerprint.id) return false;
193
- if (fingerprint.testId && !["data-testid", "data-test", "data-cy"].some(name => getAttributeSignature(el, name) === fingerprint.testId)) return false;
194
- const stableIdentity = (fingerprint.id && el.id === fingerprint.id) ||
195
- (fingerprint.testId && ["data-testid", "data-test", "data-cy"].some(name => getAttributeSignature(el, name) === fingerprint.testId));
196
- if (fingerprint.ownText && getElementOwnTextSignature(el) !== fingerprint.ownText && !(allowTextChanges && stableIdentity)) return false;
197
- // Container text is useful for matching remounted drawers and dialogs.
198
- if (!ignoreDescendantText && !fingerprint.ownText && fingerprint.text && getElementTextSignature(el) !== fingerprint.text && !(allowTextChanges && stableIdentity)) {
199
- if (!allowTextChanges || !el.children.length || !similarText(getElementTextSignature(el), fingerprint.text)) return false;
200
- }
201
- return true;
202
- }
203
-
204
- function matchesAnchorContext(el, context) {
205
- if (!context) return true;
206
- // A unique page landmark keeps its identity when a child is edited.
207
- const pageContainer = !context.layer && (context.target?.tag === "main" || context.target?.role === "main") &&
208
- [...document.querySelectorAll('main, [role="main"]')].filter(node => !isComtyNode(node)).length === 1;
209
- if (!matchesAnchorIdentity(el, context.target, pageContainer)) return false;
210
- const parentFingerprint = context.ancestors?.[0];
211
- if (parentFingerprint?.text && parentFingerprint.text !== context.target.text &&
212
- !matchesAnchorIdentity(el.parentElement, parentFingerprint, pageContainer)) return false;
213
- const layer = getPresentationLayer(el);
214
- if (context.layer && (!layer || !matchesAnchorIdentity(layer, context.layer, false, false))) return false;
215
- if (context.version >= 2 && !context.layer && layer) return false;
216
- return scoreAnchorCandidate(el, context) >= 50;
217
- }
218
-
219
- function resolveAnchor(context, exact = null) {
220
- if (!context?.target) return { element: exact, status: exact ? "matched" : "missing" };
221
- let candidates;
222
- try {
223
- candidates = [...document.body.querySelectorAll(context.target.tag || "*")].filter(el => !isComtyNode(el));
224
- } catch {
225
- return { element: null, status: "missing" };
226
- }
227
- const matches = candidates
228
- .filter(el => matchesAnchorContext(el, context))
229
- .map(el => ({ el, score: scoreAnchorCandidate(el, context) + (context.version === 1 && el === exact ? 16 : 0) }))
230
- .sort((a, b) => b.score - a.score);
231
- if (context.candidateCount > 1) {
232
- const peers = candidates.filter(el => matchesAnchorIdentity(el, context.target));
233
- if (peers.length !== context.candidateCount) {
234
- // A removed identical sibling must not move its comment to the survivor.
235
- const distinctive = matches[0] && (context.target.id || context.target.testId);
236
- if (!distinctive) return { element: null, status: "uncertain" };
237
- }
238
- }
239
- if (matches[0]?.score >= 58 && (!matches[1] || matches[0].score - matches[1].score >= 12)) {
240
- return { element: matches[0].el, status: "matched" };
241
- }
242
- // A closed/unmounted overlay is not evidence that its contents were deleted.
243
- if (context.layer && !candidates.some(el => {
244
- const layer = getPresentationLayer(el);
245
- return layer && matchesAnchorIdentity(layer, context.layer, false, false);
246
- })) return { element: null, status: "hidden" };
247
- const plausible = matches.length || candidates.some(el =>
248
- (context.target.id && el.id === context.target.id) ||
249
- (context.target.text && (getElementTextSignature(el) === context.target.text || similarText(getElementTextSignature(el), context.target.text))));
250
- return { element: null, status: plausible ? "uncertain" : "missing" };
251
- }
252
-
253
- function resolveByAnchorContext(context) {
254
- return resolveAnchor(context).element;
255
- }
256
-
257
- function getViewportBounds() {
258
- const viewport = window.visualViewport;
259
- if (!viewport) {
260
- return {
261
- left: 0,
262
- top: 0,
263
- right: window.innerWidth,
264
- bottom: window.innerHeight,
265
- };
266
- }
267
-
268
- return {
269
- left: viewport.offsetLeft,
270
- top: viewport.offsetTop,
271
- right: viewport.offsetLeft + viewport.width,
272
- bottom: viewport.offsetTop + viewport.height,
273
- };
274
- }
275
-
276
- function isElementTreeHidden(el) {
277
- let current = el;
278
- while (current instanceof Element) {
279
- if (!current.isConnected) return true;
280
- if (current.hasAttribute("hidden")) return true;
281
- if (current.hasAttribute("inert")) return true;
282
- if (current.getAttribute("aria-hidden") === "true") {
283
- return true;
284
- }
285
-
286
- const style = getComputedStyle(current);
287
- if (style.display === "none") return true;
288
- if (style.visibility === "hidden" || style.visibility === "collapse") {
84
+ const clipsY = ["auto", "scroll", "hidden", "clip"].includes(
85
+ style.overflowY,
86
+ );
87
+ if (clipsX || clipsY) {
88
+ const rect = ancestor.getBoundingClientRect();
89
+ if (clipsX && (x < rect.left || x > rect.right)) {
289
90
  return true;
290
91
  }
291
- if (Number.parseFloat(style.opacity) <= 0.01) return true;
292
- if (style.contentVisibility === "hidden") {
92
+ if (clipsY && (y < rect.top || y > rect.bottom)) {
293
93
  return true;
294
94
  }
295
-
296
- if (
297
- current.tagName === "DETAILS" &&
298
- !current.hasAttribute("open")
299
- ) {
300
- const summary = current.querySelector(":scope > summary");
301
- if (!summary || !summary.contains(el)) return true;
302
- }
303
-
304
- current = current.parentElement;
305
- }
306
- return false;
307
- }
308
-
309
- function pointIsInsideRect(x, y, rect) {
310
- return (
311
- x >= rect.left &&
312
- x < rect.right &&
313
- y >= rect.top &&
314
- y < rect.bottom
315
- );
316
- }
317
-
318
- function isPointClipped(el, x, y) {
319
- let ancestor = el.parentElement;
320
- while (
321
- ancestor &&
322
- ancestor !== document.body &&
323
- ancestor !== document.documentElement
324
- ) {
325
- const style = getComputedStyle(ancestor);
326
- const clipsX = ["auto", "scroll", "hidden", "clip"].includes(
327
- style.overflowX,
328
- );
329
- const clipsY = ["auto", "scroll", "hidden", "clip"].includes(
330
- style.overflowY,
331
- );
332
- if (clipsX || clipsY) {
333
- const rect = ancestor.getBoundingClientRect();
334
- if (clipsX && (x < rect.left || x > rect.right)) {
335
- return true;
336
- }
337
- if (clipsY && (y < rect.top || y > rect.bottom)) {
338
- return true;
339
- }
340
- }
341
- ancestor = ancestor.parentElement;
342
- }
343
- return false;
344
- }
345
-
346
- function getTopActiveModal() {
347
- const modals = [...document.querySelectorAll('[aria-modal="true"]')];
348
- try {
349
- modals.push(...document.querySelectorAll("dialog:modal"));
350
- } catch {
351
- modals.push(...document.querySelectorAll("dialog[open]"));
352
95
  }
353
- const visibleModals = [...new Set(modals)].filter(
354
- (modal) => {
355
- if (isComtyNode(modal)) return false;
356
- const rect = modal.getBoundingClientRect();
357
- if (rect.width < 0.1 || rect.height < 0.1) return false;
358
- const viewport = getViewportBounds();
359
- if (
360
- rect.right <= viewport.left ||
361
- rect.left >= viewport.right ||
362
- rect.bottom <= viewport.top ||
363
- rect.top >= viewport.bottom
364
- ) {
365
- return false;
366
- }
367
- return !isElementTreeHidden(modal);
368
- },
369
- );
370
-
371
- return (
372
- visibleModals.sort((a, b) => {
373
- const aZIndex = Number.parseFloat(getComputedStyle(a).zIndex) || 0;
374
- const bZIndex = Number.parseFloat(getComputedStyle(b).zIndex) || 0;
375
- if (aZIndex !== bZIndex) return aZIndex - bZIndex;
376
- return a.compareDocumentPosition(b) &
377
- Node.DOCUMENT_POSITION_FOLLOWING
378
- ? -1
379
- : 1;
380
- })[visibleModals.length - 1] || null
381
- );
96
+ ancestor = ancestor.parentElement;
382
97
  }
98
+ return false;
99
+ }
383
100
 
384
- function getOccludingElement(el, x, y) {
385
- const stack = document
386
- .elementsFromPoint(x, y)
387
- .filter((candidate) => !isComtyNode(candidate));
388
- const top = stack[0];
389
- if (!top) return null;
390
- if (top === el || el.contains(top) || top.contains(el)) return null;
391
- return top;
101
+ function getTopActiveModal() {
102
+ const modals = [...document.querySelectorAll('[aria-modal="true"]')];
103
+ try {
104
+ modals.push(...document.querySelectorAll("dialog:modal"));
105
+ } catch {
106
+ modals.push(...document.querySelectorAll("dialog[open]"));
392
107
  }
393
-
394
- function getVisibleAnchorPoint(el, relX, relY, activeModal) {
395
- if (!el || !Number.isFinite(relX) || !Number.isFinite(relY) || isElementTreeHidden(el)) return null;
396
-
397
- const rect = el.getBoundingClientRect();
398
- if (rect.width < 0.1 || rect.height < 0.1) {
399
- return null;
400
- }
401
-
402
- const clientX = Math.min(
403
- Math.max(rect.left + relX * rect.width, rect.left + 0.01),
404
- rect.right - 0.01,
405
- );
406
- const clientY = Math.min(
407
- Math.max(rect.top + relY * rect.height, rect.top + 0.01),
408
- rect.bottom - 0.01,
409
- );
108
+ const visibleModals = [...new Set(modals)].filter((modal) => {
109
+ if (isComtyNode(modal)) return false;
110
+ const rect = modal.getBoundingClientRect();
111
+ if (rect.width < 0.1 || rect.height < 0.1) return false;
410
112
  const viewport = getViewportBounds();
411
- if (!pointIsInsideRect(clientX, clientY, viewport)) {
412
- return null;
413
- }
414
-
415
- if (isPointClipped(el, clientX, clientY)) return null;
416
-
417
- if (activeModal && !activeModal.contains(el)) {
418
- return null;
419
- }
420
-
421
- if (getOccludingElement(el, clientX, clientY)) return null;
422
-
423
- return { clientX, clientY };
424
- }
425
-
426
- return { matchesAnchorContext, resolveAnchor, resolveByAnchorContext, isElementTreeHidden, getTopActiveModal, getVisibleAnchorPoint };
113
+ if (
114
+ rect.right <= viewport.left ||
115
+ rect.left >= viewport.right ||
116
+ rect.bottom <= viewport.top ||
117
+ rect.top >= viewport.bottom
118
+ ) {
119
+ return false;
120
+ }
121
+ return !isElementTreeHidden(modal);
122
+ });
123
+
124
+ return (
125
+ visibleModals.sort((a, b) => {
126
+ const aZIndex = Number.parseFloat(getComputedStyle(a).zIndex) || 0;
127
+ const bZIndex = Number.parseFloat(getComputedStyle(b).zIndex) || 0;
128
+ if (aZIndex !== bZIndex) return aZIndex - bZIndex;
129
+ return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING
130
+ ? -1
131
+ : 1;
132
+ })[visibleModals.length - 1] || null
133
+ );
134
+ }
135
+
136
+ function getOccludingElement(el, x, y) {
137
+ const stack = document
138
+ .elementsFromPoint(x, y)
139
+ .filter((candidate) => !isComtyNode(candidate));
140
+ const top = stack[0];
141
+ if (!top) return null;
142
+ if (top === el || el.contains(top) || top.contains(el)) return null;
143
+ return top;
144
+ }
145
+
146
+ function getVisibleAnchorPoint(el, relX, relY, activeModal) {
147
+ if (
148
+ !el ||
149
+ !Number.isFinite(relX) ||
150
+ !Number.isFinite(relY) ||
151
+ isElementTreeHidden(el)
152
+ )
153
+ return null;
154
+
155
+ const rect = el.getBoundingClientRect();
156
+ if (rect.width < 0.1 || rect.height < 0.1) {
157
+ return null;
158
+ }
159
+
160
+ const clientX = Math.min(
161
+ Math.max(rect.left + relX * rect.width, rect.left + 0.01),
162
+ rect.right - 0.01,
163
+ );
164
+ const clientY = Math.min(
165
+ Math.max(rect.top + relY * rect.height, rect.top + 0.01),
166
+ rect.bottom - 0.01,
167
+ );
168
+ const viewport = getViewportBounds();
169
+ if (!pointIsInsideRect(clientX, clientY, viewport)) {
170
+ return null;
171
+ }
172
+
173
+ if (isPointClipped(el, clientX, clientY)) return null;
174
+
175
+ if (activeModal && !activeModal.contains(el)) {
176
+ return null;
177
+ }
178
+
179
+ if (getOccludingElement(el, clientX, clientY)) return null;
180
+
181
+ return { clientX, clientY };
182
+ }
183
+
184
+ return {
185
+ captureResilientAnchor: resilient.capture,
186
+ getStableAnchorId,
187
+ resolveAnchor,
188
+ isElementTreeHidden,
189
+ getTopActiveModal,
190
+ getVisibleAnchorPoint,
191
+ };
427
192
  }