- Shipped
- September 6, 2026 at 5:05 AM UTC
- Author
- Kamo
- Commit
- c7c1c10
A member with a dozen windows open could only put them away one at a time, and the only bulk gesture that existed — throwing each hex head at the goal — is a gesture, not a menu row. Right-clicking a title bar did nothing but raise the browser's own menu, which offers Reload and Save As. Right-clicking a title bar now offers minimize-all and close-all, each in two widths: every window, or every window of this tool. Right- clicking a hex head offers the same set the other way round — restore every head, or close every head, by all or by tool. The rows that would only repeat the row above them are hidden rather than shown disabled: with one terminal open, "Close all Terminal windows" and "Close all windows" are the same click, and a menu offering one action twice teaches members to read neither row. Close asks first, but only when there is something to ask about. The probe is the cancelable tool:requestClose the windows already answer, which is the codebase's existing "would closing you now destroy something" — MessageTool has vetoed on unsent text and SoftphoneTool on a live call since the throw-to-goal gesture needed telling. Those three lines are now useCloseGuard, and the windows that hold typed content and never answered do: the AI prompt, the SMS message and CRM notes, the new-ticket form, the bug report, and the chat, support and social composers, whose replies live in composerDraftStore outside any component's state and so answer through a thunk. Deliberately NOT a scan of the persisted draft:* props. Those cells hold a window's whole working state, defaults included — the calculator's display seeds as '0', the unit converter's input as '1', the SMS tab as 'chat' — so a generic "is anything stored" heuristic would confirm on windows holding nothing but what they opened with, and a confirmation that fires every time is one people learn to click through. Only the tool knows which of its own state is content. Terminals get one more row, on both menus. Every other tool's window IS the thing it holds, so closing it is the whole story; a terminal's window is a view onto a tmux session that outlives it, and closing the window leaves that session running and holding a row in the Interaction Center. endTerminalSession is the End button from that list reached from the other direction — DELETE the session, close the window, announce the change so the row goes without waiting for its poll — and takes two clicks to do it, as that button does. The bulk actions dispatch the same events a single window's own chrome does, so there is no second path through the store; the head list comes from the hex-head engine rather than the tool-windows store, because a floating sticky note spawns a head with no window in that store and a menu reading the store would silently leave those behind.