@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,325 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.FOCUS_LOST_MS = exports.DIRECTION_WINDOW_MS = exports.BACKFILL_WINDOW_MS = void 0;
7
+ exports.clearHistory = clearHistory;
8
+ exports.getState = getState;
9
+ exports.notifyChange = notifyChange;
10
+ exports.recordBlur = recordBlur;
11
+ exports.recordFocus = recordFocus;
12
+ exports.recordKey = recordKey;
13
+ exports.resetStore = resetStore;
14
+ exports.setTracking = setTracking;
15
+ exports.subscribe = subscribe;
16
+ /**
17
+ * focusStore — the device-side memory of the TV focus inspector.
18
+ *
19
+ * CONTRACT: this store only ever OBSERVES. It never adds a view, never sets a
20
+ * prop, never calls a focus command on its own. On Android TV any focusable
21
+ * view we rendered would become a D-pad stop in the host app's focus order, so
22
+ * a tool that measures focus by participating in it would measure itself. The
23
+ * only thing that ever moves focus is the explicit `focusElement` action a
24
+ * human triggers from the dashboard.
25
+ *
26
+ * It is deliberately react-native-free: observers push events in, so every rule
27
+ * below (transition pairing, direction attribution, dead-end probes) is a pure
28
+ * function of a timestamped event stream and is unit-testable without a device.
29
+ */
30
+
31
+ /** The four D-pad directions. Select/menu never move focus, so they're dropped. */
32
+
33
+ /**
34
+ * One D-pad press, recorded against the element that held focus when it
35
+ * happened, plus what it did. A probe with `movedTo: null` is one piece of
36
+ * dead-end evidence; N consecutive ones in the same direction is a flag.
37
+ */
38
+
39
+ /**
40
+ * Why nothing is reported as focused. These are NOT the same thing and must
41
+ * never be rendered the same way:
42
+ *
43
+ * - "never-observed": we have not seen a single focus event yet. Focus is
44
+ * almost certainly fine — there is no API for "which view has focus", so the
45
+ * tool learns the answer only when focus MOVES. Attaching to an app that is
46
+ * sitting still looks exactly like this until the first press.
47
+ * - "lost": we WATCHED a view blur with nothing taking over. That is the real
48
+ * "focus vanished" bug.
49
+ */
50
+
51
+ /**
52
+ * A D-pad press and the focus pair it caused arrive in EITHER order, a few ms
53
+ * apart — both `HWEvent → blur → focus` and `blur → focus → HWEvent` were
54
+ * observed live on the same session. So direction attribution is a time-window
55
+ * join in both directions, never a sequence assumption.
56
+ */
57
+ const DIRECTION_WINDOW_MS = exports.DIRECTION_WINDOW_MS = 250;
58
+
59
+ /**
60
+ * The window for crediting a press to a move that was ALREADY recorded — the
61
+ * `blur → focus → HWEvent` ordering. Much tighter than the forward window on
62
+ * purpose: forward attribution is waiting for a consequence, but backward
63
+ * attribution is claiming a cause, and a press 200ms after a move is far more
64
+ * likely to be the NEXT press than a late-delivered echo of the last one.
65
+ * Delivery races were observed at a few ms.
66
+ */
67
+ const BACKFILL_WINDOW_MS = exports.BACKFILL_WINDOW_MS = 60;
68
+
69
+ /**
70
+ * How long a blur waits for its matching focus before we call it a loss. Focus
71
+ * pairs land within a frame or two; anything past this really has left the RN
72
+ * tree (system alert, another window, an unmounted subtree).
73
+ */
74
+ const FOCUS_LOST_MS = exports.FOCUS_LOST_MS = 400;
75
+ const MAX_TRANSITIONS = 300;
76
+ const MAX_PROBES = 400;
77
+ let currentTag = null;
78
+ let currentSince = null;
79
+ let transitions = [];
80
+ let probes = [];
81
+ let visited = new Set();
82
+ let lostCount = 0;
83
+ let transitionSeq = 0;
84
+ let probeSeq = 0;
85
+ let tracking = true;
86
+
87
+ /** Blur is always dispatched before the matching focus — this is that half. */
88
+ let pendingBlur = null;
89
+ let lostTimer = null;
90
+ const probeTimers = new Set();
91
+ const listeners = new Set();
92
+ function emitChange() {
93
+ listeners.forEach(listener => {
94
+ listener();
95
+ });
96
+ }
97
+ function pushTransition(transition) {
98
+ transitions = transitions.length >= MAX_TRANSITIONS ? [...transitions.slice(1), transition] : [...transitions, transition];
99
+ }
100
+ function pushProbe(probe) {
101
+ probes = probes.length >= MAX_PROBES ? [...probes.slice(1), probe] : [...probes, probe];
102
+ }
103
+
104
+ /**
105
+ * Credit a just-recorded transition to a press that arrived BEFORE it, and
106
+ * close that press's probe. Returns the direction, if one was found.
107
+ */
108
+ function claimPendingProbe(fromTag, toTag, at) {
109
+ for (let i = probes.length - 1; i >= 0; i--) {
110
+ const probe = probes[i];
111
+ if (at - probe.at > DIRECTION_WINDOW_MS) break;
112
+ if (probe.resolved) continue;
113
+ if (probe.tag !== fromTag) continue;
114
+ probe.resolved = true;
115
+ probe.movedTo = toTag;
116
+ return probe.direction;
117
+ }
118
+ return null;
119
+ }
120
+ function recordTransition(fromTag, toTag, at) {
121
+ transitionSeq += 1;
122
+ const dwellMs = fromTag !== null && currentSince !== null ? Math.max(0, at - currentSince) : undefined;
123
+ const transition = {
124
+ seq: transitionSeq,
125
+ fromTag,
126
+ toTag,
127
+ direction: claimPendingProbe(fromTag, toTag, at),
128
+ at,
129
+ dwellMs
130
+ };
131
+ pushTransition(transition);
132
+ if (toTag === null) lostCount += 1;
133
+ }
134
+ function clearLostTimer() {
135
+ if (lostTimer !== null) {
136
+ clearTimeout(lostTimer);
137
+ lostTimer = null;
138
+ }
139
+ }
140
+
141
+ /** A view took focus. `tag` is the native tag from the raw event's target. */
142
+ function recordFocus(tag, at = Date.now()) {
143
+ if (!tracking || !Number.isFinite(tag)) return;
144
+ clearLostTimer();
145
+
146
+ // Prefer the pending blur as the source: on a real move the blur fires first
147
+ // and names the element focus actually left. `currentTag` is the fallback for
148
+ // the boot case (a first focus with no blur) and for platforms that skip the
149
+ // blur when focus moves between windows.
150
+ const fromTag = pendingBlur?.tag ?? currentTag;
151
+ pendingBlur = null;
152
+
153
+ // A focus event for the element that already has focus is a re-notification
154
+ // (re-entering the same view, a re-render), not a transition.
155
+ if (fromTag === tag && currentTag === tag) return;
156
+ recordTransition(fromTag, tag, at);
157
+ currentTag = tag;
158
+ currentSince = at;
159
+ visited.add(tag);
160
+ emitChange();
161
+ }
162
+
163
+ /**
164
+ * A view lost focus. Held rather than recorded: the matching focus lands within
165
+ * a frame or two and turns this into a normal transition. Only a blur with NO
166
+ * follow-up is the "focus vanished" bug, and that is worth reporting.
167
+ *
168
+ * THE ORDERING TRAP: blur does NOT reliably precede its focus. On tvOS Fabric
169
+ * 0.85 the emitter delivers `topFocus(new)` and only THEN `topBlur(old)`, ~1ms
170
+ * apart (verified on the Apple TV simulator). A blur for a tag that no longer
171
+ * holds focus is therefore a late echo of a move already recorded — swallowing
172
+ * it is what keeps the tool from arming a loss timer for an element that was
173
+ * never actually orphaned, and then reporting a phantom "focus lost" plus a
174
+ * wrong `fromTag` on the next real move. Only a blur for the CURRENT holder can
175
+ * mean focus is leaving, and that test is order-agnostic on both platforms.
176
+ */
177
+ function recordBlur(tag, at = Date.now()) {
178
+ if (!tracking || !Number.isFinite(tag)) return;
179
+ if (currentTag !== null && tag !== currentTag) return;
180
+ pendingBlur = {
181
+ tag,
182
+ at
183
+ };
184
+ clearLostTimer();
185
+ lostTimer = setTimeout(() => {
186
+ lostTimer = null;
187
+ if (!pendingBlur) return;
188
+ const lostFrom = pendingBlur.tag;
189
+ pendingBlur = null;
190
+ recordTransition(lostFrom, null, Date.now());
191
+ currentTag = null;
192
+ currentSince = null;
193
+ emitChange();
194
+ }, FOCUS_LOST_MS);
195
+ }
196
+
197
+ /**
198
+ * A D-pad direction was pressed. Opens a probe against whatever holds focus,
199
+ * which resolves either to the element focus moved to or to `null` — "this
200
+ * direction did nothing from here", the raw material of every dead-end flag.
201
+ */
202
+ function recordKey(direction, at = Date.now()) {
203
+ if (!tracking) return;
204
+
205
+ // The `blur → focus → HWEvent` ordering: the move is already recorded and
206
+ // still uncredited, so this press is its cause. Credit it and log the probe
207
+ // against the element focus left, NOT against `currentTag` (which the
208
+ // transition already advanced past).
209
+ //
210
+ // `fromTag !== null` is the guard that keeps this from eating the wrong
211
+ // press: a transition out of nowhere (boot-time focus, focus regained after a
212
+ // loss) has no source element, so no D-pad direction can have caused it —
213
+ // without the check, the first press after app launch would be swallowed by
214
+ // the launch's own focus event and never open a probe.
215
+ const latest = transitions[transitions.length - 1];
216
+ if (latest && latest.direction === null && latest.fromTag !== null && at - latest.at <= BACKFILL_WINDOW_MS) {
217
+ latest.direction = direction;
218
+ probeSeq += 1;
219
+ pushProbe({
220
+ seq: probeSeq,
221
+ tag: latest.fromTag,
222
+ direction,
223
+ at,
224
+ resolved: true,
225
+ movedTo: latest.toTag
226
+ });
227
+ emitChange();
228
+ return;
229
+ }
230
+ probeSeq += 1;
231
+ const probe = {
232
+ seq: probeSeq,
233
+ tag: currentTag,
234
+ direction,
235
+ at,
236
+ resolved: false,
237
+ movedTo: null
238
+ };
239
+ pushProbe(probe);
240
+ const timer = setTimeout(() => {
241
+ probeTimers.delete(timer);
242
+ if (probe.resolved) return;
243
+ probe.resolved = true;
244
+ probe.movedTo = null;
245
+ emitChange();
246
+ }, DIRECTION_WINDOW_MS);
247
+ probeTimers.add(timer);
248
+ emitChange();
249
+ }
250
+ function getState() {
251
+ return {
252
+ currentTag,
253
+ // `currentTag` is only ever nulled by the loss path, so a null with no
254
+ // transitions behind it means we simply have not seen focus yet.
255
+ presence: currentTag !== null ? "focused" : transitionSeq === 0 ? "never-observed" : "lost",
256
+ currentSince,
257
+ transitions,
258
+ probes,
259
+ visited: Array.from(visited),
260
+ lostCount,
261
+ transitionCount: transitionSeq,
262
+ tracking
263
+ };
264
+ }
265
+
266
+ /**
267
+ * Wipe the history but KEEP where focus currently is — clearing is how a user
268
+ * starts a clean traversal run, and forgetting the current element would blank
269
+ * the Now card until the next press.
270
+ */
271
+ function clearHistory() {
272
+ transitions = [];
273
+ probes = [];
274
+ visited = currentTag !== null ? new Set([currentTag]) : new Set();
275
+ lostCount = 0;
276
+ transitionSeq = 0;
277
+ probeSeq = 0;
278
+ emitChange();
279
+ }
280
+
281
+ /**
282
+ * Pause/resume recording without detaching the native listeners. Useful when a
283
+ * user is driving the app by hand and doesn't want that traversal scored.
284
+ */
285
+ function setTracking(enabled) {
286
+ if (tracking === enabled) return;
287
+ tracking = enabled;
288
+ if (!enabled) {
289
+ clearLostTimer();
290
+ pendingBlur = null;
291
+ }
292
+ emitChange();
293
+ }
294
+
295
+ /** Full reset, including timers — the observers' detach path and tests. */
296
+ function resetStore() {
297
+ clearLostTimer();
298
+ probeTimers.forEach(timer => {
299
+ clearTimeout(timer);
300
+ });
301
+ probeTimers.clear();
302
+ pendingBlur = null;
303
+ currentTag = null;
304
+ currentSince = null;
305
+ transitions = [];
306
+ probes = [];
307
+ visited = new Set();
308
+ lostCount = 0;
309
+ transitionSeq = 0;
310
+ probeSeq = 0;
311
+ tracking = true;
312
+ emitChange();
313
+ }
314
+ function subscribe(listener) {
315
+ listeners.add(listener);
316
+ return () => {
317
+ listeners.delete(listener);
318
+ };
319
+ }
320
+
321
+ /** Exposed so the scanner can nudge a rescan when focus lands on a stranger. */
322
+ function notifyChange() {
323
+ emitChange();
324
+ }
325
+ //# sourceMappingURL=focusStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["DIRECTION_WINDOW_MS","exports","BACKFILL_WINDOW_MS","FOCUS_LOST_MS","MAX_TRANSITIONS","MAX_PROBES","currentTag","currentSince","transitions","probes","visited","Set","lostCount","transitionSeq","probeSeq","tracking","pendingBlur","lostTimer","probeTimers","listeners","emitChange","forEach","listener","pushTransition","transition","length","slice","pushProbe","probe","claimPendingProbe","fromTag","toTag","at","i","resolved","tag","movedTo","direction","recordTransition","dwellMs","Math","max","undefined","seq","clearLostTimer","clearTimeout","recordFocus","Date","now","Number","isFinite","add","recordBlur","setTimeout","lostFrom","recordKey","latest","timer","delete","getState","presence","Array","from","transitionCount","clearHistory","setTracking","enabled","resetStore","clear","subscribe","notifyChange"],"sourceRoot":"../../../src","sources":["store/focusStore.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.focusInspectorSyncAdapter = void 0;
7
+ var _sharedUi = require("@buoy-gg/shared-ui");
8
+ var _focusStore = require("../store/focusStore");
9
+ var _focusEventObserver = require("../observe/focusEventObserver");
10
+ var _keyEventObserver = require("../observe/keyEventObserver");
11
+ var _focusableScanner = require("../scan/focusableScanner");
12
+ var _detectors = require("../detect/detectors");
13
+ /**
14
+ * focusInspectorSyncAdapter — streams the focus picture to Buoy Desktop.
15
+ *
16
+ * Structurally matches external-sync's `ToolSyncAdapter` WITHOUT importing the
17
+ * package (the house rule every tool's adapter follows). Auto-discovery in
18
+ * @buoy-gg/core picks it up by export name and announces the "focus-inspector"
19
+ * capability.
20
+ *
21
+ * There is NO on-device UI and there never will be: on Android TV a focusable
22
+ * view in an overlay becomes a D-pad stop in the host app's focus order
23
+ * (`pointerEvents="box-none"` gates touch, not focus), so an on-device panel
24
+ * would corrupt the measurement. Everything device-side therefore lives in
25
+ * module scope and in this adapter's `subscribe` — headless FloatingDevTools
26
+ * mounts the adapters and NONE of the overlay components, so instrumentation
27
+ * inside a component would simply never run on TV.
28
+ *
29
+ * WORK SPLIT: the two event listeners attach at module load and stay attached;
30
+ * the fiber scan only runs while a dashboard is watching.
31
+ *
32
+ * That asymmetry is deliberate and was learned the hard way. Focus cannot be
33
+ * QUERIED on either platform — the tool only ever learns where focus is by
34
+ * watching it arrive. So an observer that attached on first `subscribe` knows
35
+ * nothing until focus next MOVES, which means opening the panel on a running
36
+ * app shows nothing, and, far worse, the tool is blind in precisely the case it
37
+ * exists for: focus is stuck, you press a direction, nothing moves, and so
38
+ * nothing is ever learned. Listening from the start is what makes "what holds
39
+ * focus right now" answerable at all.
40
+ *
41
+ * The cost is two event-emitter listeners and a bounded ring-buffer append per
42
+ * focus change — nothing next to the fiber walk, which stays gated on a
43
+ * dashboard actually watching. `setTracking(false)` is the off switch.
44
+ */
45
+
46
+ /**
47
+ * Self-truncation budget. `attachDeviceSync` SILENTLY DROPS any snapshot over
48
+ * 2MB — the panel just goes stale with no error — so the adapter trims to fit
49
+ * rather than hoping it does.
50
+ */
51
+ const MAX_SNAPSHOT_BYTES = 1.5 * 1024 * 1024;
52
+
53
+ /** A rescan after focus lands on a stranger, debounced so a fast traversal doesn't thrash. */
54
+ const RESCAN_DEBOUNCE_MS = 500;
55
+ // Attach the moment the package is loaded — which is when @buoy-gg/core's
56
+ // adapter discovery requires it, i.e. only in builds that ship Buoy at all.
57
+ // Guarded on TV so a phone build pays literally nothing.
58
+ if (_keyEventObserver.isTV) {
59
+ (0, _focusEventObserver.attachFocusObserver)();
60
+ (0, _keyEventObserver.attachKeyObserver)();
61
+ }
62
+ let lastScan = null;
63
+ let scanning = false;
64
+ let rescanTimer = null;
65
+ let subscriberCount = 0;
66
+ function nodesByTag() {
67
+ const map = new Map();
68
+ for (const node of lastScan?.nodes ?? []) map.set(node.nativeTag, node);
69
+ return map;
70
+ }
71
+ async function runScan() {
72
+ if (scanning) return lastScan;
73
+ scanning = true;
74
+ try {
75
+ lastScan = await (0, _focusableScanner.scanFocusables)();
76
+ return lastScan;
77
+ } catch {
78
+ return lastScan;
79
+ } finally {
80
+ scanning = false;
81
+ (0, _focusStore.notifyChange)();
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Focus landed on a tag the inventory doesn't know. Either the screen changed
87
+ * or focus is on an element we never collected — both are worth a rescan, and
88
+ * the second is the invisible-stop case the panel needs a name for.
89
+ */
90
+ function scheduleRescanIfStranger() {
91
+ if (subscriberCount === 0) return;
92
+ const {
93
+ currentTag
94
+ } = (0, _focusStore.getState)();
95
+ if (currentTag === null) return;
96
+ if (nodesByTag().has(currentTag)) return;
97
+ if (rescanTimer !== null) return;
98
+ rescanTimer = setTimeout(() => {
99
+ rescanTimer = null;
100
+ void runScan();
101
+ }, RESCAN_DEBOUNCE_MS);
102
+ }
103
+
104
+ /**
105
+ * Newest-fitting trim, walking backwards from the most recent transition. A
106
+ * dropped snapshot is invisible on the dashboard, so the budget is spent on the
107
+ * transitions a person is actually looking at.
108
+ */
109
+ function fittingTransitions(transitions, budget) {
110
+ const out = [];
111
+ let bytes = 0;
112
+ for (let i = transitions.length - 1; i >= 0; i--) {
113
+ const size = (0, _sharedUi.approxJsonSize)(transitions[i], budget).bytes;
114
+ if (bytes + size > budget && out.length > 0) break;
115
+ out.push(transitions[i]);
116
+ bytes += size;
117
+ }
118
+ out.reverse();
119
+ return out;
120
+ }
121
+ function buildSnapshot() {
122
+ const state = (0, _focusStore.getState)();
123
+ const nodes = lastScan?.nodes ?? [];
124
+ const observer = (0, _focusEventObserver.getFocusObserverStatus)();
125
+ const flags = (0, _detectors.detectFlags)(state.transitions, state.probes, nodes, state.lostCount, lastScan?.scannedAt ?? 0);
126
+ const coverage = (0, _detectors.computeCoverage)(nodes, state.visited, state.transitions, state.probes);
127
+ const byTag = nodesByTag();
128
+
129
+ // Focusables come first in the budget — they are the smaller, bounded half
130
+ // (capped at 400 by the scanner) and the panel is unusable without them.
131
+ const focusablesBytes = (0, _sharedUi.approxJsonSize)(nodes, MAX_SNAPSHOT_BYTES).bytes;
132
+ const transitions = fittingTransitions(state.transitions, Math.max(64 * 1024, MAX_SNAPSHOT_BYTES - focusablesBytes));
133
+ const flagged = flags.deadEnds.filter(flag => !flag.atEdge).length + flags.traps.length + flags.invisibleStops.length + (flags.lostCount > 0 ? 1 : 0);
134
+ return {
135
+ supported: _keyEventObserver.isTV,
136
+ platform: _keyEventObserver.platformOS,
137
+ observer: observer.status,
138
+ observerReason: observer.reason,
139
+ keysListening: (0, _keyEventObserver.isKeyObserverListening)(),
140
+ tracking: state.tracking,
141
+ presence: state.presence,
142
+ current: state.currentTag === null ? null : {
143
+ tag: state.currentTag,
144
+ node: byTag.get(state.currentTag) ?? null,
145
+ since: state.currentSince ?? Date.now(),
146
+ edges: (0, _detectors.observedEdges)(state.currentTag, state.transitions)
147
+ },
148
+ transitions,
149
+ focusables: nodes,
150
+ screen: lastScan?.screen ?? null,
151
+ scannedAt: lastScan?.scannedAt ?? null,
152
+ flags,
153
+ coverage,
154
+ stats: {
155
+ focusables: coverage.focusables,
156
+ visited: coverage.visited,
157
+ transitions: state.transitionCount,
158
+ flagged
159
+ },
160
+ capturedAt: Date.now()
161
+ };
162
+ }
163
+ const focusInspectorSyncAdapter = exports.focusInspectorSyncAdapter = {
164
+ version: 1,
165
+ getSnapshot() {
166
+ return buildSnapshot();
167
+ },
168
+ subscribe(onChange) {
169
+ subscriberCount += 1;
170
+ if (subscriberCount === 1) {
171
+ // Observers are already attached (module load). Only the scan is gated.
172
+ void runScan();
173
+ }
174
+ const unsubscribeStore = (0, _focusStore.subscribe)(() => {
175
+ scheduleRescanIfStranger();
176
+ onChange();
177
+ });
178
+
179
+ // A dashboard can attach before the app's first screen has mounted, in
180
+ // which case the first scan finds nothing. Poll briefly so the inventory
181
+ // fills in without the user having to press anything.
182
+ let retries = 0;
183
+ let pollTimer = null;
184
+ const poll = () => {
185
+ retries += 1;
186
+ if ((lastScan?.nodes.length ?? 0) > 0 || retries >= 100) {
187
+ pollTimer = null;
188
+ return;
189
+ }
190
+ void runScan();
191
+ pollTimer = setTimeout(poll, 100);
192
+ };
193
+ pollTimer = setTimeout(poll, 100);
194
+ return () => {
195
+ unsubscribeStore();
196
+ if (pollTimer !== null) clearTimeout(pollTimer);
197
+ subscriberCount = Math.max(0, subscriberCount - 1);
198
+ if (subscriberCount === 0) {
199
+ if (rescanTimer !== null) {
200
+ clearTimeout(rescanTimer);
201
+ rescanTimer = null;
202
+ }
203
+ // The observers stay attached on purpose — see the note at the top.
204
+ // Detaching here would make the tool forget where focus is every time
205
+ // the panel is closed, and re-open blind.
206
+ }
207
+ };
208
+ },
209
+ actions: {
210
+ async rescan() {
211
+ const scan = await runScan();
212
+ return {
213
+ ok: true,
214
+ focusables: scan?.nodes.length ?? 0
215
+ };
216
+ },
217
+ clearHistory() {
218
+ (0, _focusStore.clearHistory)();
219
+ return {
220
+ ok: true
221
+ };
222
+ },
223
+ setTracking(params) {
224
+ (0, _focusStore.setTracking)(params?.enabled !== false);
225
+ return {
226
+ tracking: (0, _focusStore.getState)().tracking
227
+ };
228
+ },
229
+ /**
230
+ * Put focus on an element and watch what the D-pad does from there — the
231
+ * only write this tool ever performs, and only when a human asks for it.
232
+ */
233
+ focusElement(params) {
234
+ const tag = params?.nativeTag;
235
+ if (typeof tag !== "number") return {
236
+ ok: false,
237
+ reason: "nativeTag is required."
238
+ };
239
+ if (!_keyEventObserver.isTV) return {
240
+ ok: false,
241
+ reason: "Not a TV build — there is no focus engine."
242
+ };
243
+ return (0, _focusableScanner.requestFocusForTag)(tag);
244
+ }
245
+ }
246
+ };
247
+ //# sourceMappingURL=focusInspectorSyncAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_sharedUi","require","_focusStore","_focusEventObserver","_keyEventObserver","_focusableScanner","_detectors","MAX_SNAPSHOT_BYTES","RESCAN_DEBOUNCE_MS","isTV","attachFocusObserver","attachKeyObserver","lastScan","scanning","rescanTimer","subscriberCount","nodesByTag","map","Map","node","nodes","set","nativeTag","runScan","scanFocusables","notifyChange","scheduleRescanIfStranger","currentTag","getState","has","setTimeout","fittingTransitions","transitions","budget","out","bytes","i","length","size","approxJsonSize","push","reverse","buildSnapshot","state","observer","getFocusObserverStatus","flags","detectFlags","probes","lostCount","scannedAt","coverage","computeCoverage","visited","byTag","focusablesBytes","Math","max","flagged","deadEnds","filter","flag","atEdge","traps","invisibleStops","supported","platform","platformOS","status","observerReason","reason","keysListening","isKeyObserverListening","tracking","presence","current","tag","get","since","currentSince","Date","now","edges","observedEdges","focusables","screen","stats","transitionCount","capturedAt","focusInspectorSyncAdapter","exports","version","getSnapshot","subscribe","onChange","unsubscribeStore","subscribeStore","retries","pollTimer","poll","clearTimeout","actions","rescan","scan","ok","clearHistory","setTracking","params","enabled","focusElement","requestFocusForTag"],"sourceRoot":"../../../src","sources":["sync/focusInspectorSyncAdapter.ts"],"mappings":"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","ignoreList":[]}