The snip was selecting a region around the launcher panel sitting in the
middle of the screen. `activateEntrypoint` calls `showWindow()` when it
activates a command from a closed launcher, so a view command's window is
already up before any extension code runs. Capturing from inside the chat
view could never have worked.
- `snip` is now a `no-view` command. It calls `closeMainWindow()` first,
waits out the 50ms hide that `ui-service.hpp` schedules, then captures
with nothing on screen, then brings the window back by launching `chat`
with the file. `settleWindow()` exists because a capture started the
instant the close resolves could still find the window there.
- The chat's action no longer captures inline. It launches `snip` and
passes the session id, so there is one capture path, one capability
check, and one place that deals with the window.
- The hand-off relays through `snip` rather than going straight back to
`chat` on purpose: `ActivateEntrypointOptions::toggleIfAlreadyActive`
defaults to true, so launching the *already active* view command closes
the window instead of reopening it. A launch cannot override that flag,
but relaying through a different no-view command makes the toggle branch
unreachable.
- The action is hidden while streaming, since the hand-off unloads the
view and would abandon an answer mid-sentence.
- Vision is settled before the selector appears, so a model that cannot
read images never costs a selection gesture. `no` and `unknown` get
different HUD wording.
- `lib/snip.ts` holds the hand-off contract; `lib/hooks/useSnip.ts` is
gone, as nothing captures from a view any more.
Verified: 252 tests, tsc, eslint, prettier, and vici build. The deployed
manifest now reports `snip mode=no-view`.