Repository navigation
feat: classify why a window is unavailable, with the tool that recovers it - #72
Merged
Merged
Conversation
This was referenced Oct 17, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #1. This classifies why a target window is unavailable and gives the matching recovery hint. Everything is read only: no input is sent and nothing is recovered automatically.
Native (
desktop_windowsrows)session_lockedandapp_hidden.display_index: the display holding most of the window, indexed as indesktop_screen; null when the window is on no display.display_current_space_iddisplay_asleepWindowPlacesinspaces.swift. It reuses the feat: list Spaces and switch a display's Space #66 Spaces reads and handles displays that share Spaces. Created-window rows use the same struct.Classification (
src/availability.ts, pure functions)windowAvailability(row, listing)returns{state, reason, hint, space_id?, cause?}. Reasons are checked in this order:session_locked(c)minimized(b, same wording as fix: refuse actions on a minimized window with a restore hint #59)app_hidden(b)display_asleep(c)other_space(a):space_idsdon't include the display's current Space. The hint namesdesktop_switch_spacewithspace_id, ordesktop_focus/desktop_activate.off_screen(d): bounds are on no display. Hint:desktop_move.not_on_screen: macOS reports it off screen for no visible reason. Hint:desktop_focus.accessibility_unavailable:pixels_onlywhile on screen, so the state islimited.visiblefailureAvailability(message, row, listing)adds two failure-only reasons:sparse_accessibility(e): the window looks available or limited, but the read failed as incomplete. Hint: wait a second or two and inspect once more, or use pixels/OCR.window_gone: the window is missing from the listing.Where it shows up
desktop_windows: every row hasavailability.desktop_inspectfailure:target_availability.availability, plus the new placement fields. The guidance says it names the likely cause and can't prove it.desktop_readfailure: now hastarget_availabilitytoo (before, it had none).action.target_availability, read from a fresh listing, whenever the window isn't plainly available. If that read fails, the refusal is left as it was.Tests
test/availability.test.mjsruns ontest/fixtures/windows.json: 9 cases, covering every reason, the ordering, and the sparse-tree and missing-window paths.npm testscript (node --test), and a step in the check workflow.Docs: a table of reasons and recoveries in
docs/tools.md, plus thedesktop_windowsdescription.Live (signed dev client against the running host, screen locked):
session_locked.space_ids,display_indexanddisplay_current_space_idare filled in across both displays.