@buoy-gg/highlight-updates 7.0.35 → 7.0.38
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/README.md +28 -6
- package/lib/commonjs/highlight-updates/HighlightUpdatesOverlay.js +1 -281
- package/lib/commonjs/highlight-updates/components/HighlightFilterView.js +1 -1474
- package/lib/commonjs/highlight-updates/components/HighlightUpdatesModal.js +1 -641
- package/lib/commonjs/highlight-updates/components/IdentifierBadge.js +1 -273
- package/lib/commonjs/highlight-updates/components/IsolatedRenderList.js +1 -188
- package/lib/commonjs/highlight-updates/components/ModalHeaderContent.js +1 -293
- package/lib/commonjs/highlight-updates/components/RenderCauseBadge.js +1 -508
- package/lib/commonjs/highlight-updates/components/RenderDetailView.js +1 -846
- package/lib/commonjs/highlight-updates/components/RenderHistoryViewer.js +1 -915
- package/lib/commonjs/highlight-updates/components/RenderListItem.js +1 -235
- package/lib/commonjs/highlight-updates/components/RendersCopySettingsView.js +1 -466
- package/lib/commonjs/highlight-updates/components/StatsDisplay.js +1 -73
- package/lib/commonjs/highlight-updates/types/copySettings.js +1 -107
- package/lib/commonjs/highlight-updates/utils/CommitProfiler.js +1 -446
- package/lib/commonjs/highlight-updates/utils/HighlightUpdatesController.js +1 -1920
- package/lib/commonjs/highlight-updates/utils/PerformanceLogger.js +1 -359
- package/lib/commonjs/highlight-updates/utils/ProfilerInterceptor.js +1 -371
- package/lib/commonjs/highlight-updates/utils/RenderCauseDetector.js +1 -1840
- package/lib/commonjs/highlight-updates/utils/RenderTracker.js +1 -952
- package/lib/commonjs/highlight-updates/utils/ViewTypeMapper.js +1 -264
- package/lib/commonjs/highlight-updates/utils/copySettingsStorage.js +1 -32
- package/lib/commonjs/highlight-updates/utils/devtoolsComponentNames.js +1 -188
- package/lib/commonjs/highlight-updates/utils/namedComposite.js +1 -41
- package/lib/commonjs/highlight-updates/utils/ownStateChanged.js +1 -103
- package/lib/commonjs/highlight-updates/utils/rawEventEmitter.js +1 -50
- package/lib/commonjs/highlight-updates/utils/renderExportFormatter.js +1 -58
- package/lib/commonjs/highlight-updates/utils/rendersExportFormatter.js +1 -485
- package/lib/commonjs/highlight-updates/utils/screenElements.js +1 -1046
- package/lib/commonjs/highlight-updates/utils/tapMatch.js +1 -58
- package/lib/commonjs/highlight-updates/utils/testIDs.js +1 -68
- package/lib/commonjs/highlight-updates/utils/touchCapture.js +1 -277
- package/lib/commonjs/index.js +1 -429
- package/lib/commonjs/preset.js +1 -278
- package/lib/commonjs/sync/highlightUpdatesSyncAdapter.js +1 -235
- package/lib/module/highlight-updates/HighlightUpdatesOverlay.js +1 -274
- package/lib/module/highlight-updates/components/HighlightFilterView.js +1 -1468
- package/lib/module/highlight-updates/components/HighlightUpdatesModal.js +1 -634
- package/lib/module/highlight-updates/components/IdentifierBadge.js +1 -265
- package/lib/module/highlight-updates/components/IsolatedRenderList.js +1 -183
- package/lib/module/highlight-updates/components/ModalHeaderContent.js +1 -288
- package/lib/module/highlight-updates/components/RenderCauseBadge.js +1 -499
- package/lib/module/highlight-updates/components/RenderDetailView.js +1 -840
- package/lib/module/highlight-updates/components/RenderHistoryViewer.js +1 -908
- package/lib/module/highlight-updates/components/RenderListItem.js +1 -230
- package/lib/module/highlight-updates/components/RendersCopySettingsView.js +1 -462
- package/lib/module/highlight-updates/components/StatsDisplay.js +1 -70
- package/lib/module/highlight-updates/types/copySettings.js +1 -102
- package/lib/module/highlight-updates/utils/CommitProfiler.js +1 -442
- package/lib/module/highlight-updates/utils/HighlightUpdatesController.js +1 -1916
- package/lib/module/highlight-updates/utils/PerformanceLogger.js +1 -353
- package/lib/module/highlight-updates/utils/ProfilerInterceptor.js +1 -358
- package/lib/module/highlight-updates/utils/RenderCauseDetector.js +1 -1830
- package/lib/module/highlight-updates/utils/RenderTracker.js +1 -949
- package/lib/module/highlight-updates/utils/ViewTypeMapper.js +1 -255
- package/lib/module/highlight-updates/utils/copySettingsStorage.js +1 -26
- package/lib/module/highlight-updates/utils/devtoolsComponentNames.js +1 -178
- package/lib/module/highlight-updates/utils/namedComposite.js +1 -36
- package/lib/module/highlight-updates/utils/ownStateChanged.js +1 -98
- package/lib/module/highlight-updates/utils/rawEventEmitter.js +1 -46
- package/lib/module/highlight-updates/utils/renderExportFormatter.js +1 -54
- package/lib/module/highlight-updates/utils/rendersExportFormatter.js +1 -478
- package/lib/module/highlight-updates/utils/screenElements.js +1 -1040
- package/lib/module/highlight-updates/utils/tapMatch.js +1 -52
- package/lib/module/highlight-updates/utils/testIDs.js +1 -60
- package/lib/module/highlight-updates/utils/touchCapture.js +1 -270
- package/lib/module/index.js +1 -130
- package/lib/module/preset.js +1 -272
- package/lib/module/sync/highlightUpdatesSyncAdapter.js +1 -230
- package/lib/typescript/highlight-updates/utils/screenElements.d.ts +52 -1
- package/lib/typescript/highlight-updates/utils/tapMatch.d.ts +11 -0
- package/lib/typescript/sync/highlightUpdatesSyncAdapter.d.ts +1 -1
- package/package.json +8 -8
- package/lib/typescript/highlight-updates/HighlightUpdatesOverlay.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/HighlightFilterView.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/HighlightUpdatesModal.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/IdentifierBadge.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/IsolatedRenderList.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/ModalHeaderContent.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/RenderCauseBadge.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/RenderDetailView.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/RenderHistoryViewer.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/RenderListItem.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/RendersCopySettingsView.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/components/StatsDisplay.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/types/copySettings.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/CommitProfiler.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/HighlightUpdatesController.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/PerformanceLogger.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/ProfilerInterceptor.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/RenderCauseDetector.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/RenderTracker.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/ViewTypeMapper.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/copySettingsStorage.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/devtoolsComponentNames.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/namedComposite.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/ownStateChanged.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/rawEventEmitter.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/renderExportFormatter.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/rendersExportFormatter.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/screenElements.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/tapMatch.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/testIDs.d.ts.map +0 -1
- package/lib/typescript/highlight-updates/utils/touchCapture.d.ts.map +0 -1
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/preset.d.ts.map +0 -1
- package/lib/typescript/sync/highlightUpdatesSyncAdapter.d.ts.map +0 -1
|
@@ -1,58 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.isOnScreenTap = isOnScreenTap;
|
|
7
|
-
exports.isRectCenterOnScreen = isRectCenterOnScreen;
|
|
8
|
-
exports.scoreTapMatch = scoreTapMatch;
|
|
9
|
-
/**
|
|
10
|
-
* Shared tap-target scoring for describe_screen / tap_element.
|
|
11
|
-
*
|
|
12
|
-
* Ranking used to be substring-on-testID only, so `tap: "ASSETS"` preferred
|
|
13
|
-
* `nav-assets-tool-test` (score 25) over a visible dial icon, and off-screen
|
|
14
|
-
* Expo Router screens (y > 1) stayed in the candidate list.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/** True when the measured center is inside the window (10% slack for home-indicator / portal hosts). */
|
|
18
|
-
function isOnScreenTap(tap) {
|
|
19
|
-
if (!tap) return false;
|
|
20
|
-
return tap.x >= -0.1 && tap.x <= 1.1 && tap.y >= -0.1 && tap.y <= 1.1;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Point-in-window test in *points* (measureInWindow coords), with slack so
|
|
25
|
-
* the floating menu and rows clipped at the home indicator still count.
|
|
26
|
-
* Previous Expo Router screens typically measure at y > 1.2× window height.
|
|
27
|
-
*/
|
|
28
|
-
function isRectCenterOnScreen(rect, screenW, screenH) {
|
|
29
|
-
if (!rect || screenW <= 0 || screenH <= 0) return false;
|
|
30
|
-
const cx = rect.x + rect.width / 2;
|
|
31
|
-
const cy = rect.y + rect.height / 2;
|
|
32
|
-
const slackX = screenW * 0.1;
|
|
33
|
-
const slackY = screenH * 0.1;
|
|
34
|
-
return cx >= -slackX && cx <= screenW + slackX && cy >= -slackY && cy <= screenH + slackY;
|
|
35
|
-
}
|
|
36
|
-
function scoreTapMatch(c, params) {
|
|
37
|
-
if (params.nativeTag != null && c.nativeTag === params.nativeTag) return 1000;
|
|
38
|
-
const q = (params.query ?? params.testID ?? "").trim().toLowerCase();
|
|
39
|
-
if (!q) return 0;
|
|
40
|
-
const fields = [c.testID, c.label, c.text, c.name].filter(Boolean).map(s => s.toLowerCase());
|
|
41
|
-
let best = 0;
|
|
42
|
-
for (const f of fields) {
|
|
43
|
-
if (f === q) best = Math.max(best, 100);else if (f.startsWith(q)) best = Math.max(best, 50);else if (f.includes(q)) best = Math.max(best, 25);
|
|
44
|
-
}
|
|
45
|
-
if (params.testID && c.testID && c.testID.toLowerCase() === params.testID.toLowerCase()) {
|
|
46
|
-
best = Math.max(best, 200);
|
|
47
|
-
}
|
|
48
|
-
if (best === 0) return 0;
|
|
49
|
-
const id = (c.testID ?? "").toLowerCase();
|
|
50
|
-
// Dial icons are the thing agents mean when they tap a tool name. (No
|
|
51
|
-
// penalties for app testID patterns — this ships in the library, and we
|
|
52
|
-
// don't know consumer apps' naming conventions.)
|
|
53
|
-
if (id.startsWith("buoy-dial-")) best += 40;
|
|
54
|
-
if (c.tap) {
|
|
55
|
-
if (isOnScreenTap(c.tap)) best += 20;else best -= 40;
|
|
56
|
-
}
|
|
57
|
-
return best;
|
|
58
|
-
}
|
|
1
|
+
"use strict";function isOnScreenTap(e){return!!e&&e.x>=-.1&&e.x<=1.1&&e.y>=-.1&&e.y<=1.1}function isRectCenterOnScreen(e,t,r){if(!e||t<=0||r<=0)return!1;const s=e.x+e.width/2,a=e.y+e.height/2,n=.1*t,o=.1*r;return s>=-n&&s<=t+n&&a>=-o&&a<=r+o}function looseText(e){return String(e??"").toLowerCase().replace(/\s+/g," ").replace(/(\d)[,\u00a0\u202f](?=\d{3}\b)/g,"$1").trim()}function scoreTapMatch(e,t){if(null!=t.nativeTag&&e.nativeTag===t.nativeTag)return 1e3;const r=String(t.query??t.testID??"").trim().toLowerCase();if(!r)return 0;const s=[e.testID,e.label,e.text,e.name].filter(Boolean).map(e=>e.toLowerCase());let a=0;for(const e of s)e===r?a=Math.max(a,100):e.startsWith(r)?a=Math.max(a,50):e.includes(r)&&(a=Math.max(a,25));const n=looseText(r);if(n)for(const e of s){const t=looseText(e);t===n?a=Math.max(a,90):t.startsWith(n)?a=Math.max(a,45):t.includes(n)&&(a=Math.max(a,22))}return t.testID&&e.testID&&e.testID.toLowerCase()===t.testID.toLowerCase()&&(a=Math.max(a,200)),0===a?0:((e.testID??"").toLowerCase().startsWith("buoy-dial-")&&(a+=40),e.tap&&(isOnScreenTap(e.tap)?a+=20:a-=40),a)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.isOnScreenTap=isOnScreenTap,exports.isRectCenterOnScreen=isRectCenterOnScreen,exports.looseText=looseText,exports.scoreTapMatch=scoreTapMatch;
|
|
@@ -1,68 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.detectedItemTestID = detectedItemTestID;
|
|
7
|
-
exports.identifierSlug = identifierSlug;
|
|
8
|
-
exports.patternChipTestID = patternChipTestID;
|
|
9
|
-
exports.renderRowTestID = renderRowTestID;
|
|
10
|
-
exports.settingTestID = settingTestID;
|
|
11
|
-
/**
|
|
12
|
-
* Stable e2e handles for the Render Highlighter (highlight-updates).
|
|
13
|
-
*
|
|
14
|
-
* This tool's list is the hardest thing in Buoy to address, because the thing
|
|
15
|
-
* it lists is a NATIVE VIEW and the only key it has for one is the fiber's
|
|
16
|
-
* `nativeTag` — a per-session counter that changes on every launch, on every
|
|
17
|
-
* remount, and (on a dynamic screen) between two runs of the same test. A
|
|
18
|
-
* handle built from it would name a different component every time.
|
|
19
|
-
*
|
|
20
|
-
* So rows are addressed by their IDENTIFIER, in exactly the priority the list
|
|
21
|
-
* item itself uses to label them: testID → nativeID → component name →
|
|
22
|
-
* accessibility label → nativeTag. That is the same string the developer sees
|
|
23
|
-
* on the row, it is stable as long as the component is, and it is what a test
|
|
24
|
-
* can name up front. `nativeTag` remains the last resort for an anonymous host
|
|
25
|
-
* view, which no test should be naming anyway.
|
|
26
|
-
*
|
|
27
|
-
* Slug rules are mirrored in `example-dev-build/utils/renderBench.js` so the
|
|
28
|
-
* fixture and the tool agree about what a row is called; the suite's
|
|
29
|
-
* `fixture.js` spot-checks that they still do.
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
/** Any identifier string → a testID-safe slug. */
|
|
33
|
-
function identifierSlug(value) {
|
|
34
|
-
return String(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* `buoy-highlight-row-<slug>` for one row of the render list.
|
|
39
|
-
*
|
|
40
|
-
* Sub-handles hang off it: `-name`, `-count`, `-type`, `-cause`, `-identifier`,
|
|
41
|
-
* `-time`. The count is the tool's whole claim, so it gets its own handle
|
|
42
|
-
* rather than living inside the row's merged label.
|
|
43
|
-
*/
|
|
44
|
-
function renderRowTestID(identifier) {
|
|
45
|
-
return `buoy-highlight-row-${identifierSlug(identifier)}`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* `buoy-highlight-detected-<type>-<slug>` for one chip in DETECTED ITEMS.
|
|
50
|
-
*
|
|
51
|
-
* The type is in the handle because the same string legitimately appears under
|
|
52
|
-
* two categories — a component called `Pressable` and a viewType called
|
|
53
|
-
* `Pressable` are different filters with the same name, and a suite that added
|
|
54
|
-
* the wrong one would still see "a pattern was added".
|
|
55
|
-
*/
|
|
56
|
-
function detectedItemTestID(type, value) {
|
|
57
|
-
return `buoy-highlight-detected-${identifierSlug(type)}-${identifierSlug(value)}`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** `buoy-highlight-<mode>-chip-<type>-<slug>` for an active filter pattern. */
|
|
61
|
-
function patternChipTestID(mode, type, value) {
|
|
62
|
-
return `buoy-highlight-${mode}-chip-${identifierSlug(type)}-${identifierSlug(value)}`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** `buoy-highlight-setting-<key>` for a settings row's control. */
|
|
66
|
-
function settingTestID(key) {
|
|
67
|
-
return `buoy-highlight-setting-${key}`;
|
|
68
|
-
}
|
|
1
|
+
"use strict";function identifierSlug(e){return String(e).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"unknown"}function renderRowTestID(e){return`buoy-highlight-row-${identifierSlug(e)}`}function detectedItemTestID(e,t){return`buoy-highlight-detected-${identifierSlug(e)}-${identifierSlug(t)}`}function patternChipTestID(e,t,i){return`buoy-highlight-${e}-chip-${identifierSlug(t)}-${identifierSlug(i)}`}function settingTestID(e){return`buoy-highlight-setting-${e}`}Object.defineProperty(exports,"__esModule",{value:!0}),exports.detectedItemTestID=detectedItemTestID,exports.identifierSlug=identifierSlug,exports.patternChipTestID=patternChipTestID,exports.renderRowTestID=renderRowTestID,exports.settingTestID=settingTestID;
|
|
@@ -1,277 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.clearTouchCapture = clearTouchCapture;
|
|
7
|
-
exports.getTouchCaptureStatus = getTouchCaptureStatus;
|
|
8
|
-
exports.readTouchCapture = readTouchCapture;
|
|
9
|
-
exports.startTouchCapture = startTouchCapture;
|
|
10
|
-
exports.stopTouchCapture = stopTouchCapture;
|
|
11
|
-
var _screenElements = require("./screenElements");
|
|
12
|
-
var _rawEventEmitter = require("./rawEventEmitter");
|
|
13
|
-
/**
|
|
14
|
-
* touchCapture — what a HUMAN is doing to the app, as replayable intents.
|
|
15
|
-
*
|
|
16
|
-
* This is the recording half of the pair whose playback half is `tapElement`:
|
|
17
|
-
* it watches real finger input and names each interaction in exactly the
|
|
18
|
-
* vocabulary `tapElement` searches by, so a recorded macro can be replayed
|
|
19
|
-
* without a second guess about what "that button" meant.
|
|
20
|
-
*
|
|
21
|
-
* Why RawEventEmitter and not the obvious alternatives:
|
|
22
|
-
* - A capture-phase responder on a root View would be the textbook answer, but
|
|
23
|
-
* Buoy mounts as a SIBLING of the app tree, not a parent. There is no root to
|
|
24
|
-
* wrap without changing every host app's integration.
|
|
25
|
-
* - Patching `Pressability`, or rewriting `onPress` props on the fly, breaks the
|
|
26
|
-
* moment a component re-renders (props are rebuilt every render) and misses
|
|
27
|
-
* everything driven by react-native-gesture-handler anyway.
|
|
28
|
-
* - RawEventEmitter mirrors every dispatched event before React's plugins see
|
|
29
|
-
* it, needs zero app instrumentation, and is already proven in-tree by
|
|
30
|
-
* focus-inspector's TV focus observer.
|
|
31
|
-
*
|
|
32
|
-
* Three rules the recorder above depends on:
|
|
33
|
-
*
|
|
34
|
-
* THE TARGET IS RESOLVED AT touchStart, NOT touchEnd. A press handler can
|
|
35
|
-
* navigate synchronously, so resolving after the fact names an element on the
|
|
36
|
-
* screen the app moved TO. We resolve while the finger is still down and emit
|
|
37
|
-
* when it lifts.
|
|
38
|
-
*
|
|
39
|
-
* A DRAG IS NOT A TAP. Scrolling a list produces the same touchStart the
|
|
40
|
-
* button above it does. Movement past a threshold, or an explicit
|
|
41
|
-
* touchCancel when a ScrollView steals the responder, drops the record —
|
|
42
|
-
* otherwise every recording is half scroll noise.
|
|
43
|
-
*
|
|
44
|
-
* KEYSTROKES ARE NOT INTENTS. Typing "SAVE10" fires six change events; a
|
|
45
|
-
* macro step wants the finished string. Consecutive edits to the same input
|
|
46
|
-
* collapse into the latest value as they arrive.
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
/** Points of finger travel past which the gesture stops being a tap. */
|
|
50
|
-
const TAP_SLOP_PT = 14;
|
|
51
|
-
/** Hold time at or past which a tap is recorded as a long-press. */
|
|
52
|
-
const LONG_PRESS_MS = 550;
|
|
53
|
-
/** A tap that outlasts this was a gesture we don't model — drop it. */
|
|
54
|
-
const MAX_TAP_MS = 4_000;
|
|
55
|
-
/** Consecutive edits to the same input within this window are one intent. */
|
|
56
|
-
const TEXT_COALESCE_MS = 8_000;
|
|
57
|
-
/** Ring buffer size. A recording longer than this is not a macro anyway. */
|
|
58
|
-
const MAX_RECORDS = 600;
|
|
59
|
-
let emitter = null;
|
|
60
|
-
let emitterResolved = false;
|
|
61
|
-
let unavailableReason = null;
|
|
62
|
-
let subscriptions = [];
|
|
63
|
-
let status = "idle";
|
|
64
|
-
let records = [];
|
|
65
|
-
let seq = 0;
|
|
66
|
-
let pending = null;
|
|
67
|
-
function getEmitter() {
|
|
68
|
-
if (emitterResolved) return emitter;
|
|
69
|
-
emitterResolved = true;
|
|
70
|
-
const resolution = (0, _rawEventEmitter.resolveRawEventEmitter)();
|
|
71
|
-
emitter = resolution.emitter;
|
|
72
|
-
unavailableReason = resolution.reason;
|
|
73
|
-
return emitter;
|
|
74
|
-
}
|
|
75
|
-
function num(v) {
|
|
76
|
-
return typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** Fabric puts the touched view on `target`; `tag` is the older shape. */
|
|
80
|
-
function tagOf(event) {
|
|
81
|
-
const native = event?.nativeEvent;
|
|
82
|
-
if (!native) return null;
|
|
83
|
-
return num(native.target) ?? num(native.tag);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Touch coordinates. A touch event carries them at the top level on the
|
|
88
|
-
* synthesized event and inside `touches`/`changedTouches` on the raw one, and
|
|
89
|
-
* which of those is populated differs by platform and architecture — so read
|
|
90
|
-
* whichever answers rather than betting on one.
|
|
91
|
-
*/
|
|
92
|
-
function pointOf(event) {
|
|
93
|
-
const native = event?.nativeEvent;
|
|
94
|
-
if (!native) return null;
|
|
95
|
-
const direct = {
|
|
96
|
-
x: num(native.pageX),
|
|
97
|
-
y: num(native.pageY)
|
|
98
|
-
};
|
|
99
|
-
if (direct.x !== null && direct.y !== null) return {
|
|
100
|
-
x: direct.x,
|
|
101
|
-
y: direct.y
|
|
102
|
-
};
|
|
103
|
-
for (const key of ["changedTouches", "touches"]) {
|
|
104
|
-
const list = native[key];
|
|
105
|
-
if (Array.isArray(list) && list.length) {
|
|
106
|
-
const first = list[0];
|
|
107
|
-
const x = num(first?.pageX);
|
|
108
|
-
const y = num(first?.pageY);
|
|
109
|
-
if (x !== null && y !== null) return {
|
|
110
|
-
x,
|
|
111
|
-
y
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
117
|
-
function push(record) {
|
|
118
|
-
records.push({
|
|
119
|
-
...record,
|
|
120
|
-
seq: ++seq
|
|
121
|
-
});
|
|
122
|
-
if (records.length > MAX_RECORDS) records = records.slice(-MAX_RECORDS);
|
|
123
|
-
}
|
|
124
|
-
function onTouchStart(event) {
|
|
125
|
-
const tag = tagOf(event);
|
|
126
|
-
if (tag === null) return;
|
|
127
|
-
const point = pointOf(event);
|
|
128
|
-
let target = null;
|
|
129
|
-
try {
|
|
130
|
-
target = (0, _screenElements.resolveTouchTarget)(tag);
|
|
131
|
-
} catch {
|
|
132
|
-
// A fiber walk that throws must never break the app's touch handling.
|
|
133
|
-
}
|
|
134
|
-
pending = {
|
|
135
|
-
tag,
|
|
136
|
-
x: point?.x ?? 0,
|
|
137
|
-
y: point?.y ?? 0,
|
|
138
|
-
t: Date.now(),
|
|
139
|
-
drift: 0,
|
|
140
|
-
target
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
function onTouchMove(event) {
|
|
144
|
-
if (!pending) return;
|
|
145
|
-
const point = pointOf(event);
|
|
146
|
-
if (!point) return;
|
|
147
|
-
const dx = point.x - pending.x;
|
|
148
|
-
const dy = point.y - pending.y;
|
|
149
|
-
pending.drift = Math.max(pending.drift, Math.sqrt(dx * dx + dy * dy));
|
|
150
|
-
}
|
|
151
|
-
function onTouchEnd(event) {
|
|
152
|
-
const p = pending;
|
|
153
|
-
pending = null;
|
|
154
|
-
if (!p || !p.target) return;
|
|
155
|
-
const point = pointOf(event);
|
|
156
|
-
if (point) {
|
|
157
|
-
const dx = point.x - p.x;
|
|
158
|
-
const dy = point.y - p.y;
|
|
159
|
-
p.drift = Math.max(p.drift, Math.sqrt(dx * dx + dy * dy));
|
|
160
|
-
}
|
|
161
|
-
if (p.drift > TAP_SLOP_PT) return; // a drag, a scroll, a swipe — not a step
|
|
162
|
-
|
|
163
|
-
const held = Date.now() - p.t;
|
|
164
|
-
if (held > MAX_TAP_MS) return;
|
|
165
|
-
|
|
166
|
-
// A text input's tap is just "focus this field"; the intent is the text it
|
|
167
|
-
// ends up with, which arrives separately as a change event. Recording the
|
|
168
|
-
// focus tap too would put a step in every macro that does nothing on replay.
|
|
169
|
-
if (p.target.control === "text") return;
|
|
170
|
-
// Same for a toggle/slider: the tap and the value change are one intent, and
|
|
171
|
-
// the change event is the half that carries what it became.
|
|
172
|
-
if (p.target.control === "toggle" || p.target.control === "slider") return;
|
|
173
|
-
push({
|
|
174
|
-
t: Date.now(),
|
|
175
|
-
kind: held >= LONG_PRESS_MS ? "longPress" : "press",
|
|
176
|
-
target: p.target
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
function onTouchCancel() {
|
|
180
|
-
// A ScrollView (or any ancestor) taking the responder terminates the touch.
|
|
181
|
-
pending = null;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* `topChange` is the shared channel for "this control's value became X" —
|
|
186
|
-
* TextInput sends `text`, Switch sends `value`. Sliders arrive on their own
|
|
187
|
-
* channel. All three are the same idea: a control the user set.
|
|
188
|
-
*/
|
|
189
|
-
function onChange(event) {
|
|
190
|
-
const tag = tagOf(event);
|
|
191
|
-
if (tag === null) return;
|
|
192
|
-
const native = event.nativeEvent;
|
|
193
|
-
let kind;
|
|
194
|
-
let value;
|
|
195
|
-
if (typeof native.text === "string") {
|
|
196
|
-
kind = "text";
|
|
197
|
-
value = native.text;
|
|
198
|
-
} else if (typeof native.value === "boolean") {
|
|
199
|
-
kind = "toggle";
|
|
200
|
-
value = native.value;
|
|
201
|
-
} else if (typeof native.value === "number") {
|
|
202
|
-
kind = "slider";
|
|
203
|
-
value = native.value;
|
|
204
|
-
} else {
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
const last = records[records.length - 1];
|
|
208
|
-
if (last && last.kind === kind && last.target.nativeTag === tag && Date.now() - last.t < TEXT_COALESCE_MS) {
|
|
209
|
-
// Same control, still typing/dragging: this IS the previous intent, further
|
|
210
|
-
// along. Replace rather than append, or "SAVE10" becomes six macro steps.
|
|
211
|
-
last.value = value;
|
|
212
|
-
last.t = Date.now();
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
let target = null;
|
|
216
|
-
try {
|
|
217
|
-
target = (0, _screenElements.resolveTouchTarget)(tag);
|
|
218
|
-
} catch {
|
|
219
|
-
/* see onTouchStart */
|
|
220
|
-
}
|
|
221
|
-
if (!target) return;
|
|
222
|
-
push({
|
|
223
|
-
t: Date.now(),
|
|
224
|
-
kind,
|
|
225
|
-
target,
|
|
226
|
-
value
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
function startTouchCapture() {
|
|
230
|
-
if (status === "capturing") return getTouchCaptureStatus();
|
|
231
|
-
const raw = getEmitter();
|
|
232
|
-
if (!raw) {
|
|
233
|
-
status = "unavailable";
|
|
234
|
-
return getTouchCaptureStatus();
|
|
235
|
-
}
|
|
236
|
-
try {
|
|
237
|
-
subscriptions = [raw.addListener("topTouchStart", onTouchStart), raw.addListener("topTouchMove", onTouchMove), raw.addListener("topTouchEnd", onTouchEnd), raw.addListener("topTouchCancel", onTouchCancel), raw.addListener("topChange", onChange), raw.addListener("topValueChange", onChange), raw.addListener("topSlidingComplete", onChange)];
|
|
238
|
-
pending = null;
|
|
239
|
-
status = "capturing";
|
|
240
|
-
} catch (e) {
|
|
241
|
-
subscriptions = [];
|
|
242
|
-
status = "unavailable";
|
|
243
|
-
unavailableReason = e?.message ?? "addListener failed";
|
|
244
|
-
}
|
|
245
|
-
return getTouchCaptureStatus();
|
|
246
|
-
}
|
|
247
|
-
function stopTouchCapture() {
|
|
248
|
-
subscriptions.forEach(s => {
|
|
249
|
-
try {
|
|
250
|
-
s.remove();
|
|
251
|
-
} catch {
|
|
252
|
-
/* already gone is fine */
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
subscriptions = [];
|
|
256
|
-
pending = null;
|
|
257
|
-
if (status === "capturing") status = "idle";
|
|
258
|
-
}
|
|
259
|
-
function clearTouchCapture() {
|
|
260
|
-
records = [];
|
|
261
|
-
seq = 0;
|
|
262
|
-
}
|
|
263
|
-
function getTouchCaptureStatus() {
|
|
264
|
-
return {
|
|
265
|
-
status,
|
|
266
|
-
reason: status === "unavailable" ? unavailableReason : null
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/** Everything recorded after `sinceSeq`, plus where the caller now is. */
|
|
271
|
-
function readTouchCapture(sinceSeq = 0) {
|
|
272
|
-
return {
|
|
273
|
-
...getTouchCaptureStatus(),
|
|
274
|
-
records: records.filter(r => r.seq > sinceSeq),
|
|
275
|
-
seq
|
|
276
|
-
};
|
|
277
|
-
}
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.clearTouchCapture=clearTouchCapture,exports.getTouchCaptureStatus=getTouchCaptureStatus,exports.readTouchCapture=readTouchCapture,exports.startTouchCapture=startTouchCapture,exports.stopTouchCapture=stopTouchCapture;var _screenElements=require("./screenElements"),_rawEventEmitter=require("./rawEventEmitter");const TAP_SLOP_PT=14,LONG_PRESS_MS=550,MAX_TAP_MS=4e3,TEXT_COALESCE_MS=8e3,MAX_RECORDS=600;let emitter=null,emitterResolved=!1,unavailableReason=null,subscriptions=[],status="idle",records=[],seq=0,pending=null;function getEmitter(){if(emitterResolved)return emitter;emitterResolved=!0;const t=(0,_rawEventEmitter.resolveRawEventEmitter)();return emitter=t.emitter,unavailableReason=t.reason,emitter}function num(t){return"number"==typeof t&&Number.isFinite(t)?t:null}function tagOf(t){const e=t?.nativeEvent;return e?num(e.target)??num(e.tag):null}function pointOf(t){const e=t?.nativeEvent;if(!e)return null;const n={x:num(e.pageX),y:num(e.pageY)};if(null!==n.x&&null!==n.y)return{x:n.x,y:n.y};for(const t of["changedTouches","touches"]){const n=e[t];if(Array.isArray(n)&&n.length){const t=n[0],e=num(t?.pageX),r=num(t?.pageY);if(null!==e&&null!==r)return{x:e,y:r}}}return null}function push(t){records.push({...t,seq:++seq}),records.length>600&&(records=records.slice(-600))}function onTouchStart(t){const e=tagOf(t);if(null===e)return;const n=pointOf(t);let r=null;try{r=(0,_screenElements.resolveTouchTarget)(e)}catch{}pending={tag:e,x:n?.x??0,y:n?.y??0,t:Date.now(),drift:0,target:r}}function onTouchMove(t){if(!pending)return;const e=pointOf(t);if(!e)return;const n=e.x-pending.x,r=e.y-pending.y;pending.drift=Math.max(pending.drift,Math.sqrt(n*n+r*r))}function onTouchEnd(t){const e=pending;if(pending=null,!e||!e.target)return;const n=pointOf(t);if(n){const t=n.x-e.x,r=n.y-e.y;e.drift=Math.max(e.drift,Math.sqrt(t*t+r*r))}if(e.drift>14)return;const r=Date.now()-e.t;r>4e3||"text"!==e.target.control&&"toggle"!==e.target.control&&"slider"!==e.target.control&&push({t:Date.now(),kind:r>=550?"longPress":"press",target:e.target})}function onTouchCancel(){pending=null}function onChange(t){const e=tagOf(t);if(null===e)return;const n=t.nativeEvent;let r,u;if("string"==typeof n.text)r="text",u=n.text;else if("boolean"==typeof n.value)r="toggle",u=n.value;else{if("number"!=typeof n.value)return;r="slider",u=n.value}const o=records[records.length-1];if(o&&o.kind===r&&o.target.nativeTag===e&&Date.now()-o.t<8e3)return o.value=u,void(o.t=Date.now());let a=null;try{a=(0,_screenElements.resolveTouchTarget)(e)}catch{}a&&push({t:Date.now(),kind:r,target:a,value:u})}function startTouchCapture(){if("capturing"===status)return getTouchCaptureStatus();const t=getEmitter();if(!t)return status="unavailable",getTouchCaptureStatus();try{subscriptions=[t.addListener("topTouchStart",onTouchStart),t.addListener("topTouchMove",onTouchMove),t.addListener("topTouchEnd",onTouchEnd),t.addListener("topTouchCancel",onTouchCancel),t.addListener("topChange",onChange),t.addListener("topValueChange",onChange),t.addListener("topSlidingComplete",onChange)],pending=null,status="capturing"}catch(t){subscriptions=[],status="unavailable",unavailableReason=t?.message??"addListener failed"}return getTouchCaptureStatus()}function stopTouchCapture(){subscriptions.forEach(t=>{try{t.remove()}catch{}}),subscriptions=[],pending=null,"capturing"===status&&(status="idle")}function clearTouchCapture(){records=[],seq=0}function getTouchCaptureStatus(){return{status:status,reason:"unavailable"===status?unavailableReason:null}}function readTouchCapture(t=0){return{...getTouchCaptureStatus(),records:records.filter(e=>e.seq>t),seq:seq}}
|