clearcote 0.47.0 → 0.49.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/dist/isolated.js CHANGED
@@ -15,34 +15,195 @@
15
15
  *
16
16
  * Mirrors sdk/python/clearcote/_isolated.py and sdk/dotnet/src/Clearcote/IsolatedWorld.cs.
17
17
  */
18
+ const resultValue = (r) => {
19
+ if (!r || typeof r !== "object" || r.exceptionDetails)
20
+ return undefined;
21
+ return r.result?.value;
22
+ };
23
+ /** @internal exported for tests. DOM.getNodeForLocation hit-tests in DOCUMENT coordinates (the
24
+ * viewport point plus the scroll offset); Playwright's boxes, CDP's box model and the mouse are all
25
+ * VIEWPORT coordinates. Measured on r37: a field scrolled 547 px down answered "No node found" at its
26
+ * viewport centre and the input itself at centre + scroll.
27
+ *
28
+ * No ignorePointerEventsNone: in CDP `true` means "hit-test pointer-events:none elements TOO", so a
29
+ * decorative layer with pointer-events:none over a control was returned instead of the control and
30
+ * every form route fell back to stock Playwright (SC 209: 6/6 untrusted change events). The default
31
+ * (false) skips such layers, exactly as a real press does. */
32
+ export function nodeAtParams(x, y, scroll) {
33
+ const [sx, sy] = scroll ?? [0, 0];
34
+ return { x: roundHalfEven(x + sx), y: roundHalfEven(y + sy), includeUserAgentShadowDOM: false };
35
+ }
36
+ /** @internal exported for tests. The frame directly under `main` that contains `frameId` (itself for a
37
+ * direct child), from a Page.getFrameTree answer; null when it is not below the main frame. */
38
+ export function topChildFrame(tree, frameId, main) {
39
+ const parents = new Map();
40
+ const walk = (t, parent) => {
41
+ const id = t?.frame?.id;
42
+ if (!id)
43
+ return;
44
+ parents.set(id, parent);
45
+ for (const c of t.childFrames ?? [])
46
+ walk(c, id);
47
+ };
48
+ walk(tree, null);
49
+ let fid = frameId;
50
+ for (let n = 0; n < 64; n++) {
51
+ const up = parents.get(fid);
52
+ if (up === undefined || up === null)
53
+ return null;
54
+ if (up === main)
55
+ return fid;
56
+ fid = up;
57
+ }
58
+ return null;
59
+ }
60
+ /** Python's round(): half to even (60.5 -> 60), so the hit test picks the same pixel in every SDK. */
61
+ /** @internal Python's round(): half to even (the pixel DOM.getNodeForLocation is asked about). */
62
+ export function roundHalfEven(v) {
63
+ const f = Math.floor(v), d = v - f;
64
+ if (d > 0.5)
65
+ return f + 1;
66
+ if (d < 0.5)
67
+ return f;
68
+ return f % 2 === 0 ? f : f + 1;
69
+ }
18
70
  export class IsolatedWorld {
19
71
  page;
72
+ // Promises, not values: two humanized calls racing on one page (Promise.all of two fills) must share
73
+ // ONE CDP session and ONE isolated world. With plain fields both saw "none yet" across the await and
74
+ // each created its own.
20
75
  cdp = null;
21
76
  ctx = null;
77
+ frame = null;
22
78
  constructor(page) {
23
79
  this.page = page;
24
80
  }
81
+ session() {
82
+ if (!this.cdp) {
83
+ const p = Promise.resolve().then(() => this.page.context().newCDPSession(this.page));
84
+ this.cdp = p;
85
+ p.catch(() => { if (this.cdp === p)
86
+ this.cdp = null; }); // a failed attach may be retried later
87
+ }
88
+ return this.cdp;
89
+ }
90
+ async ensure() {
91
+ const cdp = await this.session();
92
+ if (!this.ctx) {
93
+ const p = (async () => {
94
+ const tree = await cdp.send("Page.getFrameTree");
95
+ const frame = tree.frameTree.frame.id;
96
+ const created = await cdp.send("Page.createIsolatedWorld", { frameId: frame });
97
+ this.frame = frame;
98
+ return { id: created.executionContextId, frame };
99
+ })();
100
+ this.ctx = p;
101
+ p.catch(() => this.drop(p));
102
+ }
103
+ const world = this.ctx;
104
+ await world;
105
+ return { cdp, world };
106
+ }
107
+ /** Forget `world` (a navigation destroyed it) -- only if no other call has already replaced it. */
108
+ drop(world) {
109
+ if (world && this.ctx === world)
110
+ this.ctx = null;
111
+ }
25
112
  /** Run `(fnSource)(arg)` (`arg` JSON-serialisable) and resolve to its JSON value, or undefined. */
26
113
  async evaluate(fnSource, arg) {
27
114
  for (let attempt = 0; attempt < 2; attempt++) { // a stale context (navigation) is re-created once
115
+ let world;
28
116
  try {
29
- if (!this.cdp)
30
- this.cdp = await this.page.context().newCDPSession(this.page);
31
- const cdp = this.cdp;
32
- if (this.ctx === null) {
33
- const tree = await cdp.send("Page.getFrameTree");
34
- const created = await cdp.send("Page.createIsolatedWorld", { frameId: tree.frameTree.frame.id });
35
- this.ctx = created.executionContextId;
36
- }
37
- const r = await cdp.send("Runtime.evaluate", {
38
- expression: `(${fnSource})(${JSON.stringify(arg ?? null)})`, contextId: this.ctx, returnByValue: true,
39
- });
40
- if (!r || r.exceptionDetails)
41
- return undefined;
42
- return r.result?.value;
117
+ const e = await this.ensure();
118
+ world = e.world;
119
+ return resultValue(await e.cdp.send("Runtime.evaluate", {
120
+ expression: `(${fnSource})(${JSON.stringify(arg ?? null)})`, contextId: (await world).id, returnByValue: true,
121
+ }));
43
122
  }
44
123
  catch {
45
- this.ctx = null;
124
+ this.drop(world);
125
+ }
126
+ }
127
+ return undefined;
128
+ }
129
+ /** A raw CDP call on this page's session (DOM reads that run no script), or undefined. */
130
+ async send(method, params) {
131
+ try {
132
+ const cdp = await this.session();
133
+ return (await cdp.send(method, params ?? {}));
134
+ }
135
+ catch {
136
+ return undefined;
137
+ }
138
+ }
139
+ /** The main frame's [scrollX, scrollY], read in the isolated world; [0, 0] if unknown. */
140
+ async scroll() {
141
+ const s = await this.evaluate(ISO_SCROLL);
142
+ return Array.isArray(s) && s.length === 2 ? [Number(s[0]), Number(s[1])] : [0, 0];
143
+ }
144
+ /** backendNodeId of the MAIN-FRAME element at viewport point (x, y) -- for a point inside a child
145
+ * frame, the <iframe> element that holds it --, or undefined. `scroll`: the page's [scrollX, scrollY]
146
+ * if the caller already has it (read here otherwise).
147
+ *
148
+ * CDP's hit test (DOM.getNodeForLocation) runs in the renderer with no script at all, so this is how
149
+ * humanize finds the element a Playwright locator means -- getByRole, getByLabel, `>> nth=`,
150
+ * .filter() -- without document.querySelector, which cannot parse those selectors (the old helpers
151
+ * threw on them and silently dropped to stock Playwright input). */
152
+ async nodeAt(x, y, scroll) {
153
+ try {
154
+ await this.ensure();
155
+ }
156
+ catch {
157
+ return undefined;
158
+ }
159
+ const hit = await this.send("DOM.getNodeForLocation", nodeAtParams(x, y, scroll ?? await this.scroll()));
160
+ if (!hit || hit.backendNodeId === undefined || hit.backendNodeId === null)
161
+ return undefined;
162
+ if (hit.frameId && this.frame && hit.frameId !== this.frame)
163
+ return this.ownerInMain(hit.frameId);
164
+ return hit.backendNodeId;
165
+ }
166
+ /** A point inside a child frame: the <iframe> element in the MAIN document that holds it (through
167
+ * every nesting level), or undefined. To the main document a press there lands on that element: a
168
+ * control the iframe covers is covered, a target that is (or contains) the iframe is reached. This
169
+ * replaced the only Playwright trial humanize still ran (it moved the pointer and pressed, and page
170
+ * listeners registered first saw it). */
171
+ async ownerInMain(frameId) {
172
+ const tree = await this.send("Page.getFrameTree");
173
+ const top = this.frame ? topChildFrame(tree?.frameTree, frameId, this.frame) : null;
174
+ if (!top)
175
+ return undefined;
176
+ const own = await this.send("DOM.getFrameOwner", { frameId: top });
177
+ return own && typeof own.backendNodeId === "number" ? own.backendNodeId : undefined;
178
+ }
179
+ /** Run `fnSource` -- a `function (arg) { ... }` whose `this` is the node -- in the isolated world and
180
+ * resolve to its JSON value, or undefined. */
181
+ async callOnNode(backendNodeId, fnSource, arg) {
182
+ for (let attempt = 0; attempt < 2; attempt++) {
183
+ let world;
184
+ try {
185
+ const e = await this.ensure();
186
+ world = e.world;
187
+ const obj = (await e.cdp.send("DOM.resolveNode", { backendNodeId, executionContextId: (await world).id })).object;
188
+ try {
189
+ return resultValue(await e.cdp.send("Runtime.callFunctionOn", {
190
+ objectId: obj.objectId, functionDeclaration: fnSource, arguments: [{ value: arg ?? null }],
191
+ returnByValue: true,
192
+ }));
193
+ }
194
+ finally {
195
+ try {
196
+ await e.cdp.send("Runtime.releaseObject", { objectId: obj.objectId });
197
+ }
198
+ catch { /* best effort */ }
199
+ }
200
+ }
201
+ catch (err) {
202
+ // A stale world (a navigation destroyed it) is re-created once. A node that is simply gone
203
+ // ("No node with given id") says nothing about the world, which stays.
204
+ if (world && !/context/i.test(String(err?.message ?? err)))
205
+ return undefined;
206
+ this.drop(world);
46
207
  }
47
208
  }
48
209
  return undefined;
@@ -58,21 +219,144 @@ export function worldFor(page) {
58
219
  }
59
220
  return w;
60
221
  }
61
- /** How long a trial action may look for a covered click point before the native path takes over. */
222
+ /** How long a covered click point is re-checked (an overlay fading out) before the native path takes over. */
62
223
  export const COVER_CHECK_MS = 400;
224
+ /** The timeout of the utility-world value reads that prove a fill landed. (It was also the one longer
225
+ * look of the Playwright trial that checked a point inside a child frame; no Playwright trial runs in
226
+ * humanized mode any more: such a point is answered by its <iframe> element, see nodeAt.) */
227
+ export const COVER_RETRY_MS = 2000;
63
228
  // The reads humanize makes, as functions of one JSON argument (run by IsolatedWorld.evaluate).
64
229
  export const ISO_VIEWPORT = "() => [innerWidth, innerHeight]";
65
230
  // The platform the page believes (the persona's), for the wheel notch size.
66
231
  export const ISO_PLATFORM = "() => { const d = navigator.userAgentData; return (d && d.platform) || navigator.platform || ''; }";
67
- export const ISO_IS_FOCUSED = "(s) => { const e = document.querySelector(s); return !!e && e === document.activeElement; }";
68
- export const ISO_SELECT_PLAN = `(a) => { const s = document.querySelector(a.sel);
69
- if (!s || s.multiple || s.disabled) return null;
70
- const os = [...s.options];
71
- let i = -1;
72
- if (a.by === 'index') i = (a.want >= 0 && a.want < os.length) ? a.want : -1;
73
- else if (a.by === 'label') i = os.findIndex(o => (o.label || o.textContent || '').trim() === String(a.want).trim());
74
- else i = os.findIndex(o => o.value === a.want);
75
- if (i < 0 || os[i].disabled) return null;
76
- return { to: i, from: s.selectedIndex, ret: os[i].value }; }`;
77
- export const ISO_SELECTED_INDEX = "(s) => { const e = document.querySelector(s); return e ? e.selectedIndex : -1; }";
232
+ // The main frame's scroll offset: DOM.getNodeForLocation wants document coordinates.
233
+ export const ISO_SCROLL = "() => [scrollX, scrollY]";
234
+ // The value of whatever has focus (input/textarea value, or a contenteditable's text): how the typing
235
+ // path checks that a fat-finger key actually landed before it deletes anything.
236
+ export const ISO_ACTIVE_VALUE = `() => { let e = document.activeElement;
237
+ while (e && e.shadowRoot && e.shadowRoot.activeElement) e = e.shadowRoot.activeElement;
238
+ if (!e || e.tagName === 'IFRAME' || e.tagName === 'FRAME') return null;
239
+ if (typeof e.value === 'string' && e.tagName !== 'BUTTON') return e.value;
240
+ return e.isContentEditable ? e.textContent : null; }`;
241
+ // What the typo gate needs about the focused field (type / inputmode / autocomplete), or null.
242
+ // Focus inside an open shadow root is followed to the real field; focus inside a frame cannot be read
243
+ // from here, so it reports {frame: true} and the gate fails CLOSED (no slips in a card or login form
244
+ // that lives in an iframe).
245
+ export const ISO_ACTIVE_FIELD = `() => { let e = document.activeElement; if (!e || e === document.body) return null;
246
+ while (e.shadowRoot && e.shadowRoot.activeElement) e = e.shadowRoot.activeElement;
247
+ if (e.tagName === 'IFRAME' || e.tagName === 'FRAME') return { frame: true };
248
+ return { type: e.tagName === 'INPUT' ? e.type : '', inputmode: e.getAttribute('inputmode'),
249
+ autocomplete: e.getAttribute('autocomplete') }; }`;
250
+ // Everything the form routes need about ONE element, read from the node the hit test found (a
251
+ // CDP callFunctionOn target: `this` is that node). `want` walks up to the control the locator means
252
+ // (a click on a <select>'s padding hits the select; one on a label's text does not, and then the
253
+ // caller tries another point). Returns null when no such ancestor exists.
254
+ export const ISO_DESCRIBE = `function (a) {
255
+ let e = this.nodeType === 1 ? this : this.parentElement;
256
+ if (!e) return null;
257
+ if (a && a.want) { const c = e.closest(a.want); if (!c) return null; e = c; }
258
+ const r = e.getBoundingClientRect(), cs = getComputedStyle(e), tag = e.tagName.toLowerCase();
259
+ const o = { tag, type: tag === 'input' ? e.type : '', ce: !!e.isContentEditable,
260
+ rect: [r.x, r.y, r.width, r.height], focused: e === document.activeElement,
261
+ value: typeof e.value === 'string' ? e.value : (e.isContentEditable ? e.textContent : null),
262
+ min: e.getAttribute('min'), max: e.getAttribute('max'), step: e.getAttribute('step'),
263
+ maxlength: typeof e.maxLength === 'number' ? e.maxLength : -1,
264
+ inputmode: e.getAttribute('inputmode'), autocomplete: e.getAttribute('autocomplete'),
265
+ readonly: !!e.readOnly, disabled: !!e.disabled, dir: cs.direction, wm: cs.writingMode,
266
+ bl: e.clientLeft || 0, bt: e.clientTop || 0 };
267
+ if (tag === 'select') {
268
+ o.multiple = e.multiple; o.size = e.size; o.selectedIndex = e.selectedIndex;
269
+ o.options = Array.from(e.options, x => ({ value: x.value, label: x.label,
270
+ disabled: x.disabled || !!(x.parentElement && x.parentElement.tagName === 'OPTGROUP' && x.parentElement.disabled),
271
+ selected: x.selected }));
272
+ if (e.multiple || e.size > 1)
273
+ o.optRects = Array.from(e.options, x => { const q = x.getBoundingClientRect(); return [q.x, q.y, q.width, q.height]; });
274
+ }
275
+ return o; }`;
276
+ // The controls a locator can mean for the form routes.
277
+ export const ISO_FORM_CONTROL = "input, textarea, select, [contenteditable=''], [contenteditable='true'], [contenteditable='plaintext-only']";
278
+ // Does a press at a click point reach the element the caller holds? `this` = the node the browser's
279
+ // hit test found there (DOM.getNodeForLocation, pointer-events:none skipped, as for a real press; for a
280
+ // point inside a child frame, the <iframe> element that holds it); `b` = the element's box, plus `sel`
281
+ // when the caller has a plain-CSS selector for it.
282
+ //
283
+ // With `sel` (its first match, which must still fill `b`), the answer is exact, as Playwright's own
284
+ // hit-target check decides it: the hit is the element or inside it (across open shadow roots), or inside
285
+ // a <label> whose control it is (the label passes the press on). Anything else on top -- an overlay
286
+ // with exactly the element's box, a badge -- covers it.
287
+ //
288
+ // Without one, by the box: the hit element itself, or an ancestor that has exactly that box, with no
289
+ // OTHER element of that same box beside the hit's path inside it that the press would miss. That twin
290
+ // is a wrapper's control under an overlay (<span class=wrap><input><span overlay></span></span>: the
291
+ // wrap has the input's box, the press lands on the overlay). A twin the press reaches anyway, or that no
292
+ // press could be meant for, does not count (SC 210 fix 2; counting every twin made a MUI-style button
293
+ // "covered" and sent its click to Playwright): one that takes no pointer events (pointer-events:none,
294
+ // not visibility:visible -- a ripple or focus-ring span, an icon layer), a control whose <label> holds
295
+ // the hit (a custom checkbox), and, inside an element that is itself a control, a decorative twin with
296
+ // no control in it (a background span under a button's text).
297
+ //
298
+ // The covered-point check without a Playwright trial: a trial MOVES the real pointer onto the element
299
+ // and back and performs a press whose events Playwright blocks, so a page saw a mousemove inside the
300
+ // target, a jump back and a pointerdown/click before every humanized click (verifier B).
301
+ export const ISO_HIT_IN_BOX = `function (b) {
302
+ const same = r => Math.abs(r.x - b.x) <= 1.5 && Math.abs(r.y - b.y) <= 1.5 &&
303
+ Math.abs(r.width - b.width) <= 1.5 && Math.abs(r.height - b.height) <= 1.5;
304
+ const up = n => { const p = n.parentNode; return n.parentElement || (p && p.nodeType === 11 && p.host) || null; };
305
+ const h = this.nodeType === 1 ? this : this.parentElement;
306
+ let t = null;
307
+ if (b.sel) { try { t = document.querySelector(b.sel); } catch (x) { t = null; } }
308
+ if (t && same(t.getBoundingClientRect())) {
309
+ for (let e = h, n = 0; e && n < 128; e = up(e), n++)
310
+ if (e === t || (e.tagName === 'LABEL' && e.control === t)) return true;
311
+ return false;
312
+ }
313
+ const CTL = 'a[href], area[href], button, input, select, textarea, label, summary, iframe, [contenteditable], ' +
314
+ '[tabindex], [role=button], [role=link], [role=checkbox], [role=radio], [role=switch], [role=tab], ' +
315
+ '[role=menuitem], [role=option], [role=combobox], [role=textbox]';
316
+ const takes = d => { const s = getComputedStyle(d); return s.pointerEvents !== 'none' && s.visibility === 'visible'; };
317
+ const path = new Set();
318
+ for (let e = h, n = 0; e && n < 128; e = up(e), n++) {
319
+ path.add(e);
320
+ if (same(e.getBoundingClientRect())) {
321
+ const all = e.getElementsByTagName('*'), ctl = e.matches(CTL);
322
+ if (all.length <= 4000)
323
+ for (const d of all) {
324
+ if (path.has(d) || !same(d.getBoundingClientRect()) || !takes(d)) continue;
325
+ if (d.labels && Array.prototype.some.call(d.labels, l => l.contains(h))) continue;
326
+ if (ctl && !d.matches(CTL) && !d.querySelector(CTL)) continue;
327
+ return false;
328
+ }
329
+ return true;
330
+ }
331
+ }
332
+ return false; }`;
333
+ // A list box's scroll state and one row's box, from any node inside the <select> (`this`): where its
334
+ // visible rows are (top / bottom of the client area), how far it is scrolled (st, of max), the row's
335
+ // rect, and which rows are selected.
336
+ export const ISO_LISTBOX_ROW = `function (a) {
337
+ const n = this.nodeType === 1 ? this : this.parentElement;
338
+ const e = n && n.closest('select');
339
+ if (!e || !e.options[a.i]) return null;
340
+ const r = e.getBoundingClientRect(), q = e.options[a.i].getBoundingClientRect();
341
+ return { box: [r.x, r.y, r.width, r.height], row: [q.x, q.y, q.width, q.height],
342
+ top: r.y + e.clientTop, bottom: r.y + e.clientTop + e.clientHeight,
343
+ st: e.scrollTop, max: e.scrollHeight - e.clientHeight,
344
+ sel: Array.from(e.options, x => x.selected) }; }`;
345
+ // The first match of a plain-CSS selector, by the browser's own querySelectorAll in this world (the
346
+ // page's hooks on its prototypes never see it): how many match, and the first one's box -- scrolled
347
+ // into view first when `scroll` is set (centred, only if needed: what Playwright's
348
+ // scrollIntoViewIfNeeded does). null when the selector is not CSS the browser accepts.
349
+ export const ISO_CSS_BOX = `(a) => { let l;
350
+ try { l = document.querySelectorAll(a.sel); } catch (e) { return null; }
351
+ if (!l.length) return { n: 0 };
352
+ const e = l[0];
353
+ if (a.scroll) { if (e.scrollIntoViewIfNeeded) e.scrollIntoViewIfNeeded(true);
354
+ else e.scrollIntoView({ block: 'center', inline: 'center' }); }
355
+ const r = e.getBoundingClientRect();
356
+ return { n: l.length, rect: [r.x, r.y, r.width, r.height] }; }`;
357
+ // Scroll the window by [dx, dy] at once (no smooth-scroll animation to wait out) and return how far
358
+ // it actually moved: the box humanize already holds moves by exactly that much.
359
+ export const ISO_SCROLL_BY = `(d) => { const x = scrollX, y = scrollY;
360
+ scrollBy({ left: d[0], top: d[1], behavior: 'instant' });
361
+ return [scrollX - x, scrollY - y]; }`;
78
362
  //# sourceMappingURL=isolated.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"isolated.js","sourceRoot":"","sources":["../src/isolated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,MAAM,OAAO,aAAa;IAIK;IAHrB,GAAG,GAAmB,IAAI,CAAC;IAC3B,GAAG,GAAkB,IAAI,CAAC;IAElC,YAA6B,IAAS;QAAT,SAAI,GAAJ,IAAI,CAAK;IAAG,CAAC;IAE1C,mGAAmG;IACnG,KAAK,CAAC,QAAQ,CAAc,QAAgB,EAAE,GAAa;QACzD,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,kDAAkD;YAChG,IAAI,CAAC;gBACH,IAAI,CAAC,IAAI,CAAC,GAAG;oBAAE,IAAI,CAAC,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC7E,MAAM,GAAG,GAAG,IAAI,CAAC,GAAc,CAAC;gBAChC,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;oBACtB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;oBACjD,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;oBACjG,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,kBAA4B,CAAC;gBAClD,CAAC;gBACD,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE;oBAC3C,UAAU,EAAE,IAAI,QAAQ,KAAK,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI;iBACtG,CAAC,CAAC;gBACH,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,gBAAgB;oBAAE,OAAO,SAAS,CAAC;gBAC/C,OAAO,CAAC,CAAC,MAAM,EAAE,KAAU,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;YAClB,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AAED,MAAM,MAAM,GAAG,IAAI,OAAO,EAAyB,CAAC;AAEpD,uDAAuD;AACvD,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,IAAI,CAAC,CAAC,EAAE,CAAC;QAAC,CAAC,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAAC,CAAC;IAC7D,OAAO,CAAC,CAAC;AACX,CAAC;AAED,oGAAoG;AACpG,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC;AAElC,+FAA+F;AAC/F,MAAM,CAAC,MAAM,YAAY,GAAG,iCAAiC,CAAC;AAC9D,4EAA4E;AAC5E,MAAM,CAAC,MAAM,YAAY,GAAG,oGAAoG,CAAC;AACjI,MAAM,CAAC,MAAM,cAAc,GACzB,6FAA6F,CAAC;AAChG,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;kEAQmC,CAAC;AACnE,MAAM,CAAC,MAAM,kBAAkB,GAAG,kFAAkF,CAAC"}
1
+ {"version":3,"file":"isolated.js","sourceRoot":"","sources":["../src/isolated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,MAAM,WAAW,GAAG,CAAI,CAAM,EAAiB,EAAE;IAC/C,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,gBAAgB;QAAE,OAAO,SAAS,CAAC;IACxE,OAAO,CAAC,CAAC,MAAM,EAAE,KAAU,CAAC;AAC9B,CAAC,CAAC;AAEF;;;;;;;;8DAQ8D;AAC9D,MAAM,UAAU,YAAY,CAAC,CAAS,EAAE,CAAS,EAAE,MAAyC;IAC1F,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAClC,OAAO,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,yBAAyB,EAAE,KAAK,EAAE,CAAC;AAClG,CAAC;AAED;+FAC+F;AAC/F,MAAM,UAAU,aAAa,CAAC,IAAS,EAAE,OAAe,EAAE,IAAY;IACpE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;IACjD,MAAM,IAAI,GAAG,CAAC,CAAM,EAAE,MAAqB,EAAE,EAAE;QAC7C,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACxB,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACxB,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,IAAI,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC;IACF,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACjB,IAAI,GAAG,GAAG,OAAO,CAAC;IAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACjD,IAAI,EAAE,KAAK,IAAI;YAAE,OAAO,GAAG,CAAC;QAC5B,GAAG,GAAG,EAAE,CAAC;IACX,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,sGAAsG;AACtG,kGAAkG;AAClG,MAAM,UAAU,aAAa,CAAC,CAAS;IACrC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,CAAC,GAAG,GAAG;QAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,CAAC,GAAG,GAAG;QAAE,OAAO,CAAC,CAAC;IACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC;AAKD,MAAM,OAAO,aAAa;IAQK;IAP7B,qGAAqG;IACrG,qGAAqG;IACrG,wBAAwB;IAChB,GAAG,GAA4B,IAAI,CAAC;IACpC,GAAG,GAA6B,IAAI,CAAC;IACrC,KAAK,GAAkB,IAAI,CAAC;IAEpC,YAA6B,IAAS;QAAT,SAAI,GAAJ,IAAI,CAAK;IAAG,CAAC;IAElC,OAAO;QACb,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACd,MAAM,CAAC,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACvG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;YACb,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,KAAK,CAAC;gBAAE,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,uCAAuC;QAClG,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC;IAClB,CAAC;IAEO,KAAK,CAAC,MAAM;QAClB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACd,MAAM,CAAC,GAAsB,CAAC,KAAK,IAAI,EAAE;gBACvC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;gBACjD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAY,CAAC;gBAChD,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC/E,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;gBACnB,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,kBAA4B,EAAE,KAAK,EAAE,CAAC;YAC7D,CAAC,CAAC,EAAE,CAAC;YACL,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;YACb,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9B,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC;QACvB,MAAM,KAAK,CAAC;QACZ,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,mGAAmG;IAC3F,IAAI,CAAC,KAA2C;QACtD,IAAI,KAAK,IAAI,IAAI,CAAC,GAAG,KAAK,KAAK;YAAE,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;IACnD,CAAC;IAED,mGAAmG;IACnG,KAAK,CAAC,QAAQ,CAAc,QAAgB,EAAE,GAAa;QACzD,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,kDAAkD;YAChG,IAAI,KAAoC,CAAC;YACzC,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC9B,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;gBAChB,OAAO,WAAW,CAAI,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE;oBACzD,UAAU,EAAE,IAAI,QAAQ,KAAK,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI;iBAC9G,CAAC,CAAC,CAAC;YACN,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,0FAA0F;IAC1F,KAAK,CAAC,IAAI,CAAU,MAAc,EAAE,MAAgC;QAClE,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACjC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,IAAI,EAAE,CAAC,CAAM,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED,0FAA0F;IAC1F,KAAK,CAAC,MAAM;QACV,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAU,UAAU,CAAC,CAAC;QACnD,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpF,CAAC;IAED;;;;;;;wEAOoE;IACpE,KAAK,CAAC,MAAM,CAAC,CAAS,EAAE,CAAS,EAAE,MAAyC;QAC1E,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACzG,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,aAAa,KAAK,SAAS,IAAI,GAAG,CAAC,aAAa,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QAC5F,IAAI,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,KAAK,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAClG,OAAO,GAAG,CAAC,aAAuB,CAAC;IACrC,CAAC;IAED;;;;6CAIyC;IACjC,KAAK,CAAC,WAAW,CAAC,OAAe;QACvC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAClD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACpF,IAAI,CAAC,GAAG;YAAE,OAAO,SAAS,CAAC;QAC3B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;QACnE,OAAO,GAAG,IAAI,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;IACtF,CAAC;IAED;kDAC8C;IAC9C,KAAK,CAAC,UAAU,CAAc,aAAqB,EAAE,QAAgB,EAAE,GAAa;QAClF,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;YAC7C,IAAI,KAAoC,CAAC;YACzC,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC9B,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;gBAChB,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,aAAa,EAAE,kBAAkB,EAAE,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;gBAClH,IAAI,CAAC;oBACH,OAAO,WAAW,CAAI,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,wBAAwB,EAAE;wBAC/D,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,mBAAmB,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC;wBAC1F,aAAa,EAAE,IAAI;qBACpB,CAAC,CAAC,CAAC;gBACN,CAAC;wBAAS,CAAC;oBACT,IAAI,CAAC;wBAAC,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;oBAAC,CAAC;oBAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;gBAC5G,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,2FAA2F;gBAC3F,uEAAuE;gBACvE,IAAI,KAAK,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAE,GAAW,EAAE,OAAO,IAAI,GAAG,CAAC,CAAC;oBAAE,OAAO,SAAS,CAAC;gBACtF,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AAED,MAAM,MAAM,GAAG,IAAI,OAAO,EAAyB,CAAC;AAEpD,uDAAuD;AACvD,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,IAAI,CAAC,CAAC,EAAE,CAAC;QAAC,CAAC,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAAC,CAAC;IAC7D,OAAO,CAAC,CAAC;AACX,CAAC;AAED,8GAA8G;AAC9G,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC;AAClC;;8FAE8F;AAC9F,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AAEnC,+FAA+F;AAC/F,MAAM,CAAC,MAAM,YAAY,GAAG,iCAAiC,CAAC;AAC9D,4EAA4E;AAC5E,MAAM,CAAC,MAAM,YAAY,GAAG,oGAAoG,CAAC;AACjI,qFAAqF;AACrF,MAAM,CAAC,MAAM,UAAU,GAAG,0BAA0B,CAAC;AAErD,sGAAsG;AACtG,gFAAgF;AAChF,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;uDAIuB,CAAC;AACxD,+FAA+F;AAC/F,sGAAsG;AACtG,qGAAqG;AACrG,4BAA4B;AAC5B,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;6DAI6B,CAAC;AAC9D,8FAA8F;AAC9F,oGAAoG;AACpG,iGAAiG;AACjG,0EAA0E;AAC1E,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;cAqBd,CAAC;AACf,uDAAuD;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,6GAA6G,CAAC;AAC9I,oGAAoG;AACpG,wGAAwG;AACxG,uGAAuG;AACvG,mDAAmD;AACnD,EAAE;AACF,oGAAoG;AACpG,yGAAyG;AACzG,oGAAoG;AACpG,wDAAwD;AACxD,EAAE;AACF,qGAAqG;AACrG,sGAAsG;AACtG,qGAAqG;AACrG,yGAAyG;AACzG,sGAAsG;AACtG,sGAAsG;AACtG,uGAAuG;AACvG,uGAAuG;AACvG,8DAA8D;AAC9D,EAAE;AACF,sGAAsG;AACtG,qGAAqG;AACrG,yFAAyF;AACzF,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA+BZ,CAAC;AACnB,qGAAqG;AACrG,qGAAqG;AACrG,qCAAqC;AACrC,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;qDAQsB,CAAC;AACtD,oGAAoG;AACpG,oGAAoG;AACpG,mFAAmF;AACnF,uFAAuF;AACvF,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;iEAOsC,CAAC;AAClE,oGAAoG;AACpG,gFAAgF;AAChF,MAAM,CAAC,MAAM,aAAa,GAAG;;uCAEU,CAAC"}
package/dist/motion.d.ts CHANGED
@@ -90,18 +90,59 @@ export interface PlanOpts {
90
90
  /** Target width/height (px) for the Fitts duration + click-point spread. Defaults ~24. */
91
91
  targetW?: number;
92
92
  targetH?: number;
93
- /** Append a tiny 1–2 px settling jiggle at the end (used for slider/drag seating). */
93
+ /** A drag that seats a handle: planned by planDrag, ending in a couple of sub-pixel seating jiggles. */
94
94
  settle?: boolean;
95
+ /** The button is held (a drag leg): planned by planDrag, landing exactly on `to`. */
96
+ held?: boolean;
95
97
  /** RNG for live micro-variation. Defaults to Math.random (non-reproducible). Pass a seeded Rng in tests. */
96
98
  rng?: Rng;
97
99
  }
98
100
  /** Minimum-jerk easing s(τ)=10τ³−15τ⁴+6τ⁵ (position fraction; speed 30τ²(1−τ)²). */
99
101
  export declare function minJerk(tau: number): number;
102
+ /** Per-identity path-shape traits, stable per seed (see shapeTraits). */
103
+ export interface ShapeTraits {
104
+ /** Share of aimed moves that pass the target and come back, 0.15–0.38. */
105
+ overshootRate: number;
106
+ /** Scale on the primary submovement's bow, 0.75–1.3. */
107
+ curve: number;
108
+ /** Scale on held-drag duration, 0.85–1.2. */
109
+ dragTempo: number;
110
+ }
111
+ /** The persona's path-shape traits, drawn from a salted stream of its seed.
112
+ *
113
+ * Measured on r38 / SDK 0.48.0 (SC 209): 1 of 36 planned approaches passed the target, against 15–40 %
114
+ * for real reaches and 14/36 for the engine's recorded trajectories, because the primary submovement
115
+ * always aimed at 84–94 % of the distance. Same values as Python `_motion.shape_traits` and .NET
116
+ * `Motion.ShapeTraitsOf`. */
117
+ export declare function shapeTraits(p: Persona): ShapeTraits;
100
118
  /**
101
119
  * Plan a free cursor move `from`→`to` as a sum of minimum-jerk submovements (primary + corrections)
102
120
  * with Fitts-scaled duration and colored noise. Returns the samples to dispatch.
121
+ *
122
+ * A move with a button held (`held`) or one that seats a handle (`settle`) is a drag, planned by
123
+ * {@link planDrag}.
124
+ *
125
+ * Shape (SC 209/210): the primary submovement arcs (peak sideways deviation ~6–18 % of the distance,
126
+ * per-persona curvature and handedness), reaches its speed peak before the middle of the move, and
127
+ * either stops short of the target (a short corrective submovement closes the gap) or, in
128
+ * `shapeTraits(p).overshootRate` of moves, passes it by 3–28 px and comes back. Straightness (distance /
129
+ * path length) of the planned points lands around 0.85–0.95, the band of real reaches.
103
130
  */
104
131
  export declare function planMove(from: Point, to: Point, p: Persona, opts?: PlanOpts): Step[];
132
+ /**
133
+ * Plan a held-button drag `from`→`to` (slider handle, drag-and-drop, range thumb).
134
+ *
135
+ * Measured on r38 / SDK 0.48.0 (SC 209): the held leg reused the aimed-move planner, whose arc put the
136
+ * hand up to 47 px off the line between grab and drop — off a 40 px slider track. A drag is a slower,
137
+ * flatter movement: one skewed min-jerk leg along the line with at most ~5 px of arc and ±6 px of total
138
+ * sideways error, 0.5–2 s for 300–400 px (`shapeTraits(p).dragTempo`), and now and then (about 0.6× the
139
+ * persona's overshoot rate) a 2.5–10 px overshoot and pull-back. `settle` adds a couple of sub-pixel
140
+ * seating jiggles before landing exactly on `to`.
141
+ */
142
+ export declare function planDrag(from: Point, to: Point, p: Persona, opts?: {
143
+ settle?: boolean;
144
+ rng?: Rng;
145
+ }): Step[];
105
146
  /** Grab/release dwell times (ms) for a held-button drag, drawn from the persona window. */
106
147
  export declare function dragDwell(p: Persona, rng?: Rng): {
107
148
  grabMs: number;
@@ -1 +1 @@
1
- {"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../src/motion.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../src/motion.ts"],"names":[],"mappings":"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"}