Skip to content

feat: classify why a window is unavailable, with the tool that recovers it - #72

Merged
iamnbutler merged 1 commit into
mainfrom
feat/availability
Oct 17, 2026
Merged

iamnbutler merged 1 commit into
mainfrom
feat/availability

Conversation

@iamnbutler

Copy link
Copy Markdown
Contributor

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_windows rows)

  • New listing-wide fields: session_locked and app_hidden.
  • Each row gains:
    • display_index: the display holding most of the window, indexed as in desktop_screen; null when the window is on no display.
    • display_current_space_id
    • display_asleep
  • These are built once per listing by WindowPlaces in spaces.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:
    1. session_locked (c)
    2. minimized (b, same wording as fix: refuse actions on a minimized window with a restore hint #59)
    3. app_hidden (b)
    4. display_asleep (c)
    5. other_space (a): space_ids don't include the display's current Space. The hint names desktop_switch_space with space_id, or desktop_focus / desktop_activate.
    6. off_screen (d): bounds are on no display. Hint: desktop_move.
    7. not_on_screen: macOS reports it off screen for no visible reason. Hint: desktop_focus.
    8. accessibility_unavailable: pixels_only while on screen, so the state is limited.
    9. visible
  • failureAvailability(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 has availability.
  • desktop_inspect failure: target_availability.availability, plus the new placement fields. The guidance says it names the likely cause and can't prove it.
  • desktop_read failure: now has target_availability too (before, it had none).
  • Action refusals with a window receipt: the result gets 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.mjs runs on test/fixtures/windows.json: 9 cases, covering every reason, the ordering, and the sparse-tree and missing-window paths.
  • New npm test script (node --test), and a step in the check workflow.

Docs: a table of reasons and recoveries in docs/tools.md, plus the desktop_windows description.

Live (signed dev client against the running host, screen locked):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant