@buoy-gg/focus-inspector 7.0.31

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 (52) hide show
  1. package/LICENSE +58 -0
  2. package/README.md +136 -0
  3. package/lib/commonjs/detect/detectors.js +351 -0
  4. package/lib/commonjs/detect/detectors.js.map +1 -0
  5. package/lib/commonjs/index.js +106 -0
  6. package/lib/commonjs/index.js.map +1 -0
  7. package/lib/commonjs/observe/focusEventObserver.js +105 -0
  8. package/lib/commonjs/observe/focusEventObserver.js.map +1 -0
  9. package/lib/commonjs/observe/keyEventObserver.js +94 -0
  10. package/lib/commonjs/observe/keyEventObserver.js.map +1 -0
  11. package/lib/commonjs/observe/rawEventEmitter.js +44 -0
  12. package/lib/commonjs/observe/rawEventEmitter.js.map +1 -0
  13. package/lib/commonjs/package.json +1 -0
  14. package/lib/commonjs/scan/focusableScanner.js +497 -0
  15. package/lib/commonjs/scan/focusableScanner.js.map +1 -0
  16. package/lib/commonjs/store/focusStore.js +325 -0
  17. package/lib/commonjs/store/focusStore.js.map +1 -0
  18. package/lib/commonjs/sync/focusInspectorSyncAdapter.js +247 -0
  19. package/lib/commonjs/sync/focusInspectorSyncAdapter.js.map +1 -0
  20. package/lib/module/detect/detectors.js +341 -0
  21. package/lib/module/detect/detectors.js.map +1 -0
  22. package/lib/module/index.js +36 -0
  23. package/lib/module/index.js.map +1 -0
  24. package/lib/module/observe/focusEventObserver.js +99 -0
  25. package/lib/module/observe/focusEventObserver.js.map +1 -0
  26. package/lib/module/observe/keyEventObserver.js +86 -0
  27. package/lib/module/observe/keyEventObserver.js.map +1 -0
  28. package/lib/module/observe/rawEventEmitter.js +40 -0
  29. package/lib/module/observe/rawEventEmitter.js.map +1 -0
  30. package/lib/module/scan/focusableScanner.js +492 -0
  31. package/lib/module/scan/focusableScanner.js.map +1 -0
  32. package/lib/module/store/focusStore.js +312 -0
  33. package/lib/module/store/focusStore.js.map +1 -0
  34. package/lib/module/sync/focusInspectorSyncAdapter.js +244 -0
  35. package/lib/module/sync/focusInspectorSyncAdapter.js.map +1 -0
  36. package/lib/typescript/detect/detectors.d.ts +122 -0
  37. package/lib/typescript/detect/detectors.d.ts.map +1 -0
  38. package/lib/typescript/index.d.ts +28 -0
  39. package/lib/typescript/index.d.ts.map +1 -0
  40. package/lib/typescript/observe/focusEventObserver.d.ts +34 -0
  41. package/lib/typescript/observe/focusEventObserver.d.ts.map +1 -0
  42. package/lib/typescript/observe/keyEventObserver.d.ts +22 -0
  43. package/lib/typescript/observe/keyEventObserver.d.ts.map +1 -0
  44. package/lib/typescript/observe/rawEventEmitter.d.ts +29 -0
  45. package/lib/typescript/observe/rawEventEmitter.d.ts.map +1 -0
  46. package/lib/typescript/scan/focusableScanner.d.ts +95 -0
  47. package/lib/typescript/scan/focusableScanner.d.ts.map +1 -0
  48. package/lib/typescript/store/focusStore.d.ts +135 -0
  49. package/lib/typescript/store/focusStore.d.ts.map +1 -0
  50. package/lib/typescript/sync/focusInspectorSyncAdapter.d.ts +107 -0
  51. package/lib/typescript/sync/focusInspectorSyncAdapter.d.ts.map +1 -0
  52. package/package.json +74 -0
@@ -0,0 +1,122 @@
1
+ /**
2
+ * detectors — pure functions from the recorded stream to the three TV focus bug
3
+ * classes. No timers, no RN, no fiber access, so every rule below is a unit
4
+ * test rather than a device session.
5
+ *
6
+ * The honesty rule that shapes all of this: focus can only be OBSERVED from JS,
7
+ * never computed. There is no "where would RIGHT take me" API on either
8
+ * platform, and a JS re-implementation of Android's FocusFinder geometry would
9
+ * lie in exactly the cases this tool exists for (guides, traps, nextFocus*
10
+ * overrides). So geometry is only ever used to *downgrade* a flag ("that's the
11
+ * screen edge, not a dead end") or to *suspect* one — never to assert what the
12
+ * engine would do.
13
+ */
14
+ import type { FocusDirection, FocusProbe, FocusTransition } from "../store/focusStore";
15
+ import type { FocusableNode } from "../scan/focusableScanner";
16
+ export declare const DIRECTIONS: FocusDirection[];
17
+ /** Consecutive failed presses in one direction before it counts as a dead end. */
18
+ export declare const DEAD_END_ATTEMPTS = 3;
19
+ /** Presses before "unvisited" is worth reporting at all — below this it's noise. */
20
+ export declare const COVERAGE_MIN_PROBES = 12;
21
+ export interface DeadEndFlag {
22
+ tag: number;
23
+ direction: FocusDirection;
24
+ /** Consecutive presses in this direction that moved nothing. */
25
+ attempts: number;
26
+ /**
27
+ * The element's frame abuts the viewport edge in this direction, so "focus
28
+ * didn't move" is the expected behavior, not a bug.
29
+ */
30
+ atEdge: boolean;
31
+ }
32
+ export interface TrapFlag {
33
+ /** The element(s) focus cannot escape from, as a group. */
34
+ tags: number[];
35
+ /** What holds them: the enclosing guide's tag, or the single element's own. */
36
+ containerTag: number;
37
+ directions: FocusDirection[];
38
+ /**
39
+ * A `trapFocus*` prop on the enclosing guide explains the trap — probably
40
+ * intentional. NOTE: trapFocus does NOT hold on Android TV, so an OBSERVED
41
+ * trap there is always a real bug regardless of this hint.
42
+ */
43
+ guideTrapTag?: number;
44
+ }
45
+ export interface InvisibleStopFlag {
46
+ tag: number;
47
+ /** "guide" = a TVFocusGuideView (a stop with no highlight). */
48
+ reason: "guide" | "unknown";
49
+ at: number;
50
+ /** How many times focus has landed here. */
51
+ count: number;
52
+ }
53
+ export interface CoverageResult {
54
+ focusables: number;
55
+ visited: number;
56
+ unvisited: number[];
57
+ /**
58
+ * Unvisited AND no observed edge points at it AND a visited neighbour sits
59
+ * next to it geometrically. A SUSPICION, never a proof — say "suspected
60
+ * unreachable" in the UI, never "unreachable".
61
+ */
62
+ suspectedUnreachable: number[];
63
+ /** False until enough traversal has happened for absence to mean anything. */
64
+ meaningful: boolean;
65
+ }
66
+ export interface FocusFlags {
67
+ deadEnds: DeadEndFlag[];
68
+ traps: TrapFlag[];
69
+ invisibleStops: InvisibleStopFlag[];
70
+ lostCount: number;
71
+ }
72
+ /**
73
+ * A direction can legitimately move focus sometimes and not others (a rail
74
+ * scrolls, then hits its end), so a dead end is the *trailing run* of failures,
75
+ * not a lifetime tally. One successful move clears it.
76
+ */
77
+ export declare function detectDeadEnds(probes: FocusProbe[], nodes: FocusableNode[], minAttempts?: number, since?: number): DeadEndFlag[];
78
+ /**
79
+ * A trap is a REGION focus cannot get OUT of, which is usually not one element
80
+ * and is not "all four directions are dead".
81
+ *
82
+ * Both of the obvious rules were tried against the armed trap in the TV example
83
+ * (a row of three tiles in one `TVFocusGuideView`) and both missed it. Per
84
+ * element: left/right move freely between the tiles, so no single tile
85
+ * dead-ends four ways. All-four-directions on the group: the box is
86
+ * left-aligned with everything else on the screen, so LEFT is a legitimate
87
+ * screen edge and the union never reaches four.
88
+ *
89
+ * What actually defines the bug is escape: for every direction in which
90
+ * something focusable EXISTS outside the container, focus has to have been
91
+ * observed failing to get there. A direction with nothing beyond it is not
92
+ * evidence either way, and a container with nothing outside it at all is the
93
+ * whole screen, not a trap.
94
+ */
95
+ export declare function detectTraps(deadEnds: DeadEndFlag[], nodes: FocusableNode[]): TrapFlag[];
96
+ /**
97
+ * Focus that parked somewhere with no visible highlight — the "focus vanished"
98
+ * symptom. Two shapes: it landed on a TVFocusGuideView (a real focus stop the
99
+ * user cannot see), or on a tag the inventory has never heard of (a view
100
+ * outside the walked tree, or one that unmounted since the last scan).
101
+ */
102
+ export declare function detectInvisibleStops(transitions: FocusTransition[], nodes: FocusableNode[], since?: number): InvisibleStopFlag[];
103
+ /**
104
+ * `since` is the timestamp of the inventory these flags are judged against —
105
+ * normally the last scan. Everything older belonged to a screen that no longer
106
+ * exists, and judging it here produces confident nonsense.
107
+ */
108
+ export declare function detectFlags(transitions: FocusTransition[], probes: FocusProbe[], nodes: FocusableNode[], lostCount: number, since?: number): FocusFlags;
109
+ /**
110
+ * Coverage. Observed absence is EVIDENCE, not proof: focus is nondeterministic
111
+ * enough on tvOS that "never visited" cannot be promoted to "unreachable"
112
+ * without native support that does not exist. So the strong word is reserved
113
+ * for the case where geometry AND the observed graph agree.
114
+ */
115
+ export declare function computeCoverage(nodes: FocusableNode[], visited: number[], transitions: FocusTransition[], probes: FocusProbe[]): CoverageResult;
116
+ /**
117
+ * The observed out-edges of one element — what the desktop's D-pad compass
118
+ * renders. A direction with no entry is UNKNOWN (dim), which is a different
119
+ * statement from a dead end (pressed, went nowhere).
120
+ */
121
+ export declare function observedEdges(tag: number, transitions: FocusTransition[]): Partial<Record<FocusDirection, number | null>>;
122
+ //# sourceMappingURL=detectors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detectors.d.ts","sourceRoot":"","sources":["../../../src/detect/detectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,eAAO,MAAM,UAAU,EAAE,cAAc,EAAoC,CAAC;AAE5E,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC,oFAAoF;AACpF,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,cAAc,CAAC;IAC1B,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,+EAA+E;IAC/E,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,+DAA+D;IAC/D,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;;;;OAIG;IACH,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,8EAA8E;IAC9E,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,cAAc,EAAE,iBAAiB,EAAE,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,UAAU,EAAE,EACpB,KAAK,EAAE,aAAa,EAAE,EACtB,WAAW,GAAE,MAA0B,EACvC,KAAK,SAAI,GACR,WAAW,EAAE,CA2Bf;AA8FD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,QAAQ,EAAE,CA6EvF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,eAAe,EAAE,EAC9B,KAAK,EAAE,aAAa,EAAE,EACtB,KAAK,SAAI,GACR,iBAAiB,EAAE,CAiCrB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,WAAW,EAAE,eAAe,EAAE,EAC9B,MAAM,EAAE,UAAU,EAAE,EACpB,KAAK,EAAE,aAAa,EAAE,EACtB,SAAS,EAAE,MAAM,EACjB,KAAK,SAAI,GACR,UAAU,CAQZ;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,aAAa,EAAE,EACtB,OAAO,EAAE,MAAM,EAAE,EACjB,WAAW,EAAE,eAAe,EAAE,EAC9B,MAAM,EAAE,UAAU,EAAE,GACnB,cAAc,CAuChB;AAsBD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,eAAe,EAAE,GAC7B,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAOhD"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @buoy-gg/focus-inspector
3
+ *
4
+ * TV focus debugging for React Native TV apps, from Buoy Desktop: what holds
5
+ * D-pad focus right now, every focus transition with the direction that caused
6
+ * it, the inventory of focusable elements, and automatic flags for the three
7
+ * big TV focus bug classes — focus gets STUCK (dead end / trap), focus
8
+ * DISAPPEARS (parks on an invisible stop or leaves the RN tree), and an element
9
+ * is never REACHED.
10
+ *
11
+ * This package renders NOTHING on the device, and that is a hard constraint
12
+ * rather than a preference: on Android TV any focusable view in an overlay
13
+ * becomes a D-pad stop in the host app's focus order, so an on-device panel
14
+ * would change the navigation it is measuring. See the README.
15
+ *
16
+ * It also ships no native code — the focus stream comes from a JS-only global
17
+ * observer (`RawEventEmitter`'s `topFocus`/`topBlur`), which is why Buoy stays
18
+ * podspec-free.
19
+ */
20
+ export { focusInspectorSyncAdapter } from "./sync/focusInspectorSyncAdapter";
21
+ export type { FocusInspectorSnapshot } from "./sync/focusInspectorSyncAdapter";
22
+ export { clearHistory, getState, recordBlur, recordFocus, recordKey, resetStore, setTracking, subscribe, } from "./store/focusStore";
23
+ export type { FocusDirection, FocusProbe, FocusStoreState, FocusTransition, } from "./store/focusStore";
24
+ export { scanFocusables } from "./scan/focusableScanner";
25
+ export type { FocusableNode, FocusFrame, FocusGuideInfo, FocusScanResult, } from "./scan/focusableScanner";
26
+ export { computeCoverage, detectDeadEnds, detectFlags, detectInvisibleStops, detectTraps, observedEdges, } from "./detect/detectors";
27
+ export type { CoverageResult, DeadEndFlag, FocusFlags, InvisibleStopFlag, TrapFlag, } from "./detect/detectors";
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,YAAY,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAK/E,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,WAAW,EACX,SAAS,EACT,UAAU,EACV,WAAW,EACX,SAAS,GACV,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,cAAc,EACd,UAAU,EACV,eAAe,EACf,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,YAAY,EACV,aAAa,EACb,UAAU,EACV,cAAc,EACd,eAAe,GAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,eAAe,EACf,cAAc,EACd,WAAW,EACX,oBAAoB,EACpB,WAAW,EACX,aAAa,GACd,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,cAAc,EACd,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,QAAQ,GACT,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * focusEventObserver — the global, zero-instrumentation focus source.
3
+ *
4
+ * `react-native/Libraries/Core/RawEventEmitter` mirrors every Fabric-dispatched
5
+ * event BEFORE React's plugins or components see it, and on both tvOS and
6
+ * Android TV every focus change arrives there as `topFocus` / `topBlur` with
7
+ * the focused view's native tag. Its own source comment blesses exactly this
8
+ * use ("app-specific perf monitoring … and maybe debugging").
9
+ *
10
+ * Why not the obvious alternatives:
11
+ * - `onFocus`/`onBlur` props would require instrumenting the app's components,
12
+ * and would MISS the interesting cases: focus really does park on views with
13
+ * no handler (a TVFocusGuideView with `destinations` is an invisible focus
14
+ * stop). A per-component observer reports "focus is nowhere" for the exact
15
+ * bug this tool exists to catch.
16
+ * - `TVEventHandler`'s `focus`/`blur` HWEvents are deprecated and 0.85 deleted
17
+ * the Fabric calls that emitted them (fork issue #1037). They still fire on
18
+ * the 0.83 line, so they're folded in as optional enrichment below — never
19
+ * required, or the tool would work on one app and not the other.
20
+ * - There is no root-level capture View to wrap the app in: Buoy mounts as a
21
+ * SIBLING of the app tree, not a parent.
22
+ *
23
+ * The deep import is guarded. If it ever moves, the snapshot degrades to
24
+ * `observer: "unavailable"` and the panel says so, rather than the tool taking
25
+ * the app's bundle down.
26
+ */
27
+ export type ObserverStatus = "idle" | "listening" | "unavailable";
28
+ export declare function attachFocusObserver(): void;
29
+ export declare function detachFocusObserver(): void;
30
+ export declare function getFocusObserverStatus(): {
31
+ status: ObserverStatus;
32
+ reason: string | null;
33
+ };
34
+ //# sourceMappingURL=focusEventObserver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focusEventObserver.d.ts","sourceRoot":"","sources":["../../../src/observe/focusEventObserver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AASH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC;AA6BlE,wBAAgB,mBAAmB,IAAI,IAAI,CA2B1C;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAU1C;AAED,wBAAgB,sBAAsB,IAAI;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAE1F"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * keyEventObserver — the direction half of the stream.
3
+ *
4
+ * D-pad HWEvents carry NO tag on either platform (an Android payload is exactly
5
+ * `{"eventKeyAction":1,"eventType":"right"}`), and focus events carry no
6
+ * direction. So key events tell you *which way the user pushed* and focus
7
+ * events tell you *where focus went*; the store joins them on time.
8
+ *
9
+ * `TVEventHandler.addListener` (not the `useTVEventHandler` hook) because sync
10
+ * adapters are not React components — nothing here mounts.
11
+ *
12
+ * On the 0.83 fork line iOS Fabric ALSO still emits tagged `focus`/`blur`
13
+ * HWEvents here. Those are folded into the same store as enrichment so the
14
+ * older line gets a focus source even if the RawEventEmitter path ever fails —
15
+ * never depended on, because 0.85 deleted them.
16
+ */
17
+ export declare const isTV: boolean;
18
+ export declare const platformOS: string;
19
+ export declare function attachKeyObserver(): void;
20
+ export declare function detachKeyObserver(): void;
21
+ export declare function isKeyObserverListening(): boolean;
22
+ //# sourceMappingURL=keyEventObserver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyEventObserver.d.ts","sourceRoot":"","sources":["../../../src/observe/keyEventObserver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAuBH,eAAO,MAAM,IAAI,SAA6B,CAAC;AAC/C,eAAO,MAAM,UAAU,QAA+B,CAAC;AAiDvD,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAQxC;AAED,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The one guarded require for React Native's RawEventEmitter, kept in its own
3
+ * module for two reasons.
4
+ *
5
+ * 1. `react-native/Libraries/Core/RawEventEmitter` is a DEEP import. It warns
6
+ * under 0.85's export map and could move in any release, so the failure has
7
+ * to degrade to "observer unavailable" in the snapshot rather than taking
8
+ * the host app's bundle down. Metro only treats a require as optional when
9
+ * it sits LEXICALLY inside a try statement — wrapping it in a helper or an
10
+ * arrow makes Metro hard-fail the bundle instead.
11
+ * 2. It gives the unit suite a seam. Node's `require` resolves through the
12
+ * filesystem and ignores Vitest's module mocks, so the emitter can only be
13
+ * faked by mocking a module that is reached with a real `import`.
14
+ */
15
+ export interface RawEventSubscription {
16
+ remove: () => void;
17
+ }
18
+ export interface RawEventEmitterLike {
19
+ addListener: (eventType: string, listener: (event: {
20
+ eventName?: string;
21
+ nativeEvent?: Record<string, unknown>;
22
+ }) => void) => RawEventSubscription;
23
+ }
24
+ export interface RawEmitterResolution {
25
+ emitter: RawEventEmitterLike | null;
26
+ reason: string | null;
27
+ }
28
+ export declare function resolveRawEventEmitter(): RawEmitterResolution;
29
+ //# sourceMappingURL=rawEventEmitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rawEventEmitter.d.ts","sourceRoot":"","sources":["../../../src/observe/rawEventEmitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,CACX,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,IAAI,KACrF,oBAAoB,CAAC;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACpC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,wBAAgB,sBAAsB,IAAI,oBAAoB,CAc7D"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * focusableScanner — a read-only fiber walk that inventories everything on
3
+ * screen the TV focus engine could land on.
4
+ *
5
+ * Provably inert: it reads `memoizedProps` and calls `measureInWindow`. It adds
6
+ * no views, sets no props, and calls no focus command — which matters more here
7
+ * than anywhere else in Buoy, because on Android TV a single focusable view we
8
+ * rendered would insert itself into the host app's focus order and change the
9
+ * very navigation this tool measures.
10
+ *
11
+ * Traversal (multi-renderer roots, the Fabric/Paper/CLI-dev instance triad,
12
+ * Offscreen + inactive-`react-native-screens` pruning, measure-with-a-hang-
13
+ * guard) follows the house pattern from
14
+ * `packages/highlight-updates/src/highlight-updates/utils/screenElements.ts`.
15
+ * The repo convention is a per-package copy trimmed to the tool's needs rather
16
+ * than a shared walker library — see packages/CLAUDE.md.
17
+ *
18
+ * There is no JS API for "which view has focus" and none for "if focus is here
19
+ * and the user presses RIGHT, where does it go" (Android has
20
+ * `FabricUIManager.findNextFocusableElement` natively but never exposes it to
21
+ * JS; tvOS's UIKit engine decides at runtime and answers nothing). So this
22
+ * inventory is the *candidate set* — the observed transition stream is what
23
+ * says which of them focus actually reaches.
24
+ */
25
+ export interface FocusFrame {
26
+ x: number;
27
+ y: number;
28
+ width: number;
29
+ height: number;
30
+ }
31
+ export interface FocusGuideInfo {
32
+ autoFocus: boolean;
33
+ /** `destinations` is applied via a native command, so only presence is known. */
34
+ hasDestinations: boolean;
35
+ enabled: boolean;
36
+ trap: {
37
+ up: boolean;
38
+ down: boolean;
39
+ left: boolean;
40
+ right: boolean;
41
+ };
42
+ }
43
+ export interface FocusableNode {
44
+ nativeTag: number;
45
+ name: string;
46
+ owner?: string;
47
+ testID?: string;
48
+ label?: string;
49
+ text?: string;
50
+ /** Normalized [0,1] rect. Absent when the node could not be measured. */
51
+ frame?: FocusFrame;
52
+ focusProps: {
53
+ focusable?: boolean;
54
+ hasTVPreferredFocus?: boolean;
55
+ /** Which `nextFocus*` overrides are set — the values are native tags. */
56
+ nextFocus?: {
57
+ up?: boolean;
58
+ down?: boolean;
59
+ left?: boolean;
60
+ right?: boolean;
61
+ };
62
+ };
63
+ /** Present when this node IS a TVFocusGuideView (an invisible focus stop). */
64
+ guide?: FocusGuideInfo;
65
+ /** Native tag of the nearest enclosing guide, for the "intentional trap?" hint. */
66
+ guideAncestorTag?: number;
67
+ /** True when a press handler is reachable — a Pressable/Touchable stop. */
68
+ pressable?: boolean;
69
+ /**
70
+ * False when the element's box sits outside the window right now — below the
71
+ * fold of a ScrollView, typically. Still a real D-pad target (the engine
72
+ * scrolls to it), so it stays in the inventory and in the coverage
73
+ * denominator; it is only marked so the panel can say why it is unvisited.
74
+ */
75
+ onScreen?: boolean;
76
+ }
77
+ export interface FocusScanResult {
78
+ screen: {
79
+ width: number;
80
+ height: number;
81
+ };
82
+ nodes: FocusableNode[];
83
+ scannedAt: number;
84
+ }
85
+ export declare function scanFocusables(): Promise<FocusScanResult>;
86
+ /**
87
+ * Move focus to a native tag. Source-verified only until this runs on a device:
88
+ * `Commands.requestTVFocus` takes an instance ref, never a tag, so the last
89
+ * scan's instance table is the only way to get from one to the other.
90
+ */
91
+ export declare function requestFocusForTag(nativeTag: number): {
92
+ ok: boolean;
93
+ reason?: string;
94
+ };
95
+ //# sourceMappingURL=focusableScanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focusableScanner.d.ts","sourceRoot":"","sources":["../../../src/scan/focusableScanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AA8BH,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,iFAAiF;IACjF,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;CACrE;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE;QACV,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,yEAAyE;QACzE,SAAS,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,OAAO,CAAC;YAAC,IAAI,CAAC,EAAE,OAAO,CAAC;YAAC,IAAI,CAAC,EAAE,OAAO,CAAC;YAAC,KAAK,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;KAC/E,CAAC;IACF,8EAA8E;IAC9E,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AA6XD,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,CAAC,CA0D/D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAgBtF"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * focusStore — the device-side memory of the TV focus inspector.
3
+ *
4
+ * CONTRACT: this store only ever OBSERVES. It never adds a view, never sets a
5
+ * prop, never calls a focus command on its own. On Android TV any focusable
6
+ * view we rendered would become a D-pad stop in the host app's focus order, so
7
+ * a tool that measures focus by participating in it would measure itself. The
8
+ * only thing that ever moves focus is the explicit `focusElement` action a
9
+ * human triggers from the dashboard.
10
+ *
11
+ * It is deliberately react-native-free: observers push events in, so every rule
12
+ * below (transition pairing, direction attribution, dead-end probes) is a pure
13
+ * function of a timestamped event stream and is unit-testable without a device.
14
+ */
15
+ /** The four D-pad directions. Select/menu never move focus, so they're dropped. */
16
+ export type FocusDirection = "up" | "down" | "left" | "right";
17
+ export interface FocusTransition {
18
+ /** Monotonic counter — survives ring-buffer trimming, so it's a stable key. */
19
+ seq: number;
20
+ /** Where focus was. `null` at boot (first focus) or after focus was lost. */
21
+ fromTag: number | null;
22
+ /** Where focus went. `null` means focus LEFT the RN tree (the "vanished" bug). */
23
+ toTag: number | null;
24
+ /** D-pad direction credited to this move, when one could be attributed. */
25
+ direction: FocusDirection | null;
26
+ at: number;
27
+ /** How long focus sat on `fromTag` before this move. */
28
+ dwellMs?: number;
29
+ }
30
+ /**
31
+ * One D-pad press, recorded against the element that held focus when it
32
+ * happened, plus what it did. A probe with `movedTo: null` is one piece of
33
+ * dead-end evidence; N consecutive ones in the same direction is a flag.
34
+ */
35
+ export interface FocusProbe {
36
+ seq: number;
37
+ /** Focused element at press time. `null` = nothing had focus (already lost). */
38
+ tag: number | null;
39
+ direction: FocusDirection;
40
+ at: number;
41
+ /** False while we're still inside the attribution window. */
42
+ resolved: boolean;
43
+ /** Where focus went, or `null` for "focus did not move". */
44
+ movedTo: number | null;
45
+ }
46
+ /**
47
+ * Why nothing is reported as focused. These are NOT the same thing and must
48
+ * never be rendered the same way:
49
+ *
50
+ * - "never-observed": we have not seen a single focus event yet. Focus is
51
+ * almost certainly fine — there is no API for "which view has focus", so the
52
+ * tool learns the answer only when focus MOVES. Attaching to an app that is
53
+ * sitting still looks exactly like this until the first press.
54
+ * - "lost": we WATCHED a view blur with nothing taking over. That is the real
55
+ * "focus vanished" bug.
56
+ */
57
+ export type FocusPresence = "focused" | "never-observed" | "lost";
58
+ export interface FocusStoreState {
59
+ currentTag: number | null;
60
+ presence: FocusPresence;
61
+ /** When focus arrived at `currentTag` (for the dwell timer on the Now card). */
62
+ currentSince: number | null;
63
+ transitions: FocusTransition[];
64
+ probes: FocusProbe[];
65
+ /** Every tag focus has ever landed on this session. */
66
+ visited: number[];
67
+ /** Transitions whose `toTag` was null — focus left the tree. */
68
+ lostCount: number;
69
+ /** Total transitions recorded, including ones trimmed out of the buffer. */
70
+ transitionCount: number;
71
+ tracking: boolean;
72
+ }
73
+ /**
74
+ * A D-pad press and the focus pair it caused arrive in EITHER order, a few ms
75
+ * apart — both `HWEvent → blur → focus` and `blur → focus → HWEvent` were
76
+ * observed live on the same session. So direction attribution is a time-window
77
+ * join in both directions, never a sequence assumption.
78
+ */
79
+ export declare const DIRECTION_WINDOW_MS = 250;
80
+ /**
81
+ * The window for crediting a press to a move that was ALREADY recorded — the
82
+ * `blur → focus → HWEvent` ordering. Much tighter than the forward window on
83
+ * purpose: forward attribution is waiting for a consequence, but backward
84
+ * attribution is claiming a cause, and a press 200ms after a move is far more
85
+ * likely to be the NEXT press than a late-delivered echo of the last one.
86
+ * Delivery races were observed at a few ms.
87
+ */
88
+ export declare const BACKFILL_WINDOW_MS = 60;
89
+ /**
90
+ * How long a blur waits for its matching focus before we call it a loss. Focus
91
+ * pairs land within a frame or two; anything past this really has left the RN
92
+ * tree (system alert, another window, an unmounted subtree).
93
+ */
94
+ export declare const FOCUS_LOST_MS = 400;
95
+ /** A view took focus. `tag` is the native tag from the raw event's target. */
96
+ export declare function recordFocus(tag: number, at?: number): void;
97
+ /**
98
+ * A view lost focus. Held rather than recorded: the matching focus lands within
99
+ * a frame or two and turns this into a normal transition. Only a blur with NO
100
+ * follow-up is the "focus vanished" bug, and that is worth reporting.
101
+ *
102
+ * THE ORDERING TRAP: blur does NOT reliably precede its focus. On tvOS Fabric
103
+ * 0.85 the emitter delivers `topFocus(new)` and only THEN `topBlur(old)`, ~1ms
104
+ * apart (verified on the Apple TV simulator). A blur for a tag that no longer
105
+ * holds focus is therefore a late echo of a move already recorded — swallowing
106
+ * it is what keeps the tool from arming a loss timer for an element that was
107
+ * never actually orphaned, and then reporting a phantom "focus lost" plus a
108
+ * wrong `fromTag` on the next real move. Only a blur for the CURRENT holder can
109
+ * mean focus is leaving, and that test is order-agnostic on both platforms.
110
+ */
111
+ export declare function recordBlur(tag: number, at?: number): void;
112
+ /**
113
+ * A D-pad direction was pressed. Opens a probe against whatever holds focus,
114
+ * which resolves either to the element focus moved to or to `null` — "this
115
+ * direction did nothing from here", the raw material of every dead-end flag.
116
+ */
117
+ export declare function recordKey(direction: FocusDirection, at?: number): void;
118
+ export declare function getState(): FocusStoreState;
119
+ /**
120
+ * Wipe the history but KEEP where focus currently is — clearing is how a user
121
+ * starts a clean traversal run, and forgetting the current element would blank
122
+ * the Now card until the next press.
123
+ */
124
+ export declare function clearHistory(): void;
125
+ /**
126
+ * Pause/resume recording without detaching the native listeners. Useful when a
127
+ * user is driving the app by hand and doesn't want that traversal scored.
128
+ */
129
+ export declare function setTracking(enabled: boolean): void;
130
+ /** Full reset, including timers — the observers' detach path and tests. */
131
+ export declare function resetStore(): void;
132
+ export declare function subscribe(listener: () => void): () => void;
133
+ /** Exposed so the scanner can nudge a rescan when focus lands on a stranger. */
134
+ export declare function notifyChange(): void;
135
+ //# sourceMappingURL=focusStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focusStore.d.ts","sourceRoot":"","sources":["../../../src/store/focusStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,mFAAmF;AACnF,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE9D,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,GAAG,EAAE,MAAM,CAAC;IACZ,6EAA6E;IAC7E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,kFAAkF;IAClF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,2EAA2E;IAC3E,SAAS,EAAE,cAAc,GAAG,IAAI,CAAC;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,gFAAgF;IAChF,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,EAAE,cAAc,CAAC;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,6DAA6D;IAC7D,QAAQ,EAAE,OAAO,CAAC;IAClB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,gBAAgB,GAAG,MAAM,CAAC;AAElE,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,aAAa,CAAC;IACxB,gFAAgF;IAChF,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,uDAAuD;IACvD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;GAIG;AACH,eAAO,MAAM,aAAa,MAAM,CAAC;AA+EjC,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,GAAE,MAAmB,GAAG,IAAI,CAoBtE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,GAAE,MAAmB,GAAG,IAAI,CAerE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,cAAc,EAAE,EAAE,GAAE,MAAmB,GAAG,IAAI,CAsDlF;AAED,wBAAgB,QAAQ,IAAI,eAAe,CAc1C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAQnC;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAQlD;AAED,2EAA2E;AAC3E,wBAAgB,UAAU,IAAI,IAAI,CAiBjC;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAK1D;AAED,gFAAgF;AAChF,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * focusInspectorSyncAdapter — streams the focus picture to Buoy Desktop.
3
+ *
4
+ * Structurally matches external-sync's `ToolSyncAdapter` WITHOUT importing the
5
+ * package (the house rule every tool's adapter follows). Auto-discovery in
6
+ * @buoy-gg/core picks it up by export name and announces the "focus-inspector"
7
+ * capability.
8
+ *
9
+ * There is NO on-device UI and there never will be: on Android TV a focusable
10
+ * view in an overlay becomes a D-pad stop in the host app's focus order
11
+ * (`pointerEvents="box-none"` gates touch, not focus), so an on-device panel
12
+ * would corrupt the measurement. Everything device-side therefore lives in
13
+ * module scope and in this adapter's `subscribe` — headless FloatingDevTools
14
+ * mounts the adapters and NONE of the overlay components, so instrumentation
15
+ * inside a component would simply never run on TV.
16
+ *
17
+ * WORK SPLIT: the two event listeners attach at module load and stay attached;
18
+ * the fiber scan only runs while a dashboard is watching.
19
+ *
20
+ * That asymmetry is deliberate and was learned the hard way. Focus cannot be
21
+ * QUERIED on either platform — the tool only ever learns where focus is by
22
+ * watching it arrive. So an observer that attached on first `subscribe` knows
23
+ * nothing until focus next MOVES, which means opening the panel on a running
24
+ * app shows nothing, and, far worse, the tool is blind in precisely the case it
25
+ * exists for: focus is stuck, you press a direction, nothing moves, and so
26
+ * nothing is ever learned. Listening from the start is what makes "what holds
27
+ * focus right now" answerable at all.
28
+ *
29
+ * The cost is two event-emitter listeners and a bounded ring-buffer append per
30
+ * focus change — nothing next to the fiber walk, which stays gated on a
31
+ * dashboard actually watching. `setTracking(false)` is the off switch.
32
+ */
33
+ import { type FocusDirection, type FocusPresence, type FocusTransition } from "../store/focusStore";
34
+ import { type ObserverStatus } from "../observe/focusEventObserver";
35
+ import { type FocusableNode } from "../scan/focusableScanner";
36
+ import { computeCoverage, type FocusFlags } from "../detect/detectors";
37
+ export interface FocusInspectorSnapshot {
38
+ /** False on phones/tablets/web — the panel renders an "not a TV build" state. */
39
+ supported: boolean;
40
+ platform: string;
41
+ /** "unavailable" means the RawEventEmitter deep import moved; nothing is being observed. */
42
+ observer: ObserverStatus;
43
+ observerReason: string | null;
44
+ /** Whether the D-pad direction stream is attached (0.85 TVEventHandler). */
45
+ keysListening: boolean;
46
+ tracking: boolean;
47
+ /**
48
+ * Why `current` is null, when it is. "never-observed" is the ordinary
49
+ * cold-start case and must NOT be reported as a bug: focus cannot be queried,
50
+ * only watched arriving, so an app sitting still looks identical to one with
51
+ * no focus until something moves.
52
+ */
53
+ presence: FocusPresence;
54
+ current: {
55
+ tag: number;
56
+ node: FocusableNode | null;
57
+ since: number;
58
+ /** Where each direction has been OBSERVED to lead from here. */
59
+ edges: Partial<Record<FocusDirection, number | null>>;
60
+ } | null;
61
+ transitions: FocusTransition[];
62
+ focusables: FocusableNode[];
63
+ screen: {
64
+ width: number;
65
+ height: number;
66
+ } | null;
67
+ scannedAt: number | null;
68
+ flags: FocusFlags;
69
+ coverage: ReturnType<typeof computeCoverage>;
70
+ stats: {
71
+ focusables: number;
72
+ visited: number;
73
+ transitions: number;
74
+ flagged: number;
75
+ };
76
+ capturedAt: number;
77
+ }
78
+ export declare const focusInspectorSyncAdapter: {
79
+ version: number;
80
+ getSnapshot(): FocusInspectorSnapshot;
81
+ subscribe(onChange: () => void): () => void;
82
+ actions: {
83
+ rescan(): Promise<{
84
+ ok: true;
85
+ focusables: number;
86
+ }>;
87
+ clearHistory(): {
88
+ ok: true;
89
+ };
90
+ setTracking(params?: {
91
+ enabled?: boolean;
92
+ }): {
93
+ tracking: boolean;
94
+ };
95
+ /**
96
+ * Put focus on an element and watch what the D-pad does from there — the
97
+ * only write this tool ever performs, and only when a human asks for it.
98
+ */
99
+ focusElement(params?: {
100
+ nativeTag?: number;
101
+ }): {
102
+ ok: boolean;
103
+ reason?: string;
104
+ };
105
+ };
106
+ };
107
+ //# sourceMappingURL=focusInspectorSyncAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focusInspectorSyncAdapter.d.ts","sourceRoot":"","sources":["../../../src/sync/focusInspectorSyncAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAIH,OAAO,EAML,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,+BAA+B,CAAC;AAOvC,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,eAAe,EAA8B,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAYnG,MAAM,WAAW,sBAAsB;IACrC,iFAAiF;IACjF,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,4FAA4F;IAC5F,QAAQ,EAAE,cAAc,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,4EAA4E;IAC5E,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE;QACP,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,aAAa,GAAG,IAAI,CAAC;QAC3B,KAAK,EAAE,MAAM,CAAC;QACd,gEAAgE;QAChE,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;KACvD,GAAG,IAAI,CAAC;IACT,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;IAC7C,KAAK,EAAE;QACL,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;CACpB;AAoID,eAAO,MAAM,yBAAyB;;mBAGrB,sBAAsB;wBAIjB,MAAM,IAAI,GAAG,MAAM,IAAI;;kBA6CzB,OAAO,CAAC;YAAE,EAAE,EAAE,IAAI,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,CAAC;wBAKzC;YAAE,EAAE,EAAE,IAAI,CAAA;SAAE;6BAKP;YAAE,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,GAAG;YAAE,QAAQ,EAAE,OAAO,CAAA;SAAE;QAKlE;;;WAGG;8BACmB;YAAE,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG;YAAE,EAAE,EAAE,OAAO,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE;;CAOlF,CAAC"}