Commit ca3c718
Closes #2474 (P2, `area:ui`).
## The bug
Cmd/Ctrl+W deletes the active thread. Nothing checked whether a dialog
was on screen, so **closing Settings with the keystroke that closes
things everywhere else destroyed a conversation instead.** There is no
undo.
```ts
if (meta && e.key === 'w') {
e.preventDefault()
void confirmDeleteThread() // ← no idea Settings is open
}
```
## The fix
The guard reads the DOM — *is any `<dialog>` open* — rather than asking
a list of `isXOpen()` predicates.
That shape is deliberate. The renderer has **seventeen** dialogs, and
the one hand-maintained list of them sits three lines above this in the
Cmd/Ctrl+F handler, naming **four**: settings, the command palette, file
search, keyboard shortcuts. That list is exactly the failure the issue
anticipates when it says *"or any other dialog for that matter"*, so the
check is built so a new dialog is covered the moment it exists, with
nobody having to remember to register it.
**A behaviour change worth noticing:** the find bar now uses the same
check, so it also defers to the thirteen dialogs the list missed — an
approval, an SSH passphrase, a confirm. Each is a question it should not
open underneath, but it is a change beyond the reported bug, so push
back if you'd rather I left the four-item list alone.
`show()` counts as well as `showModal()`: the approval prompt appears
**inline over the chat** rather than modally, and it is still something
the user is answering.
### `preventDefault()` stays outside the guard
It is what keeps the keystroke from also reaching macOS's File ▸ Close
accelerator (`app-menu-file-items.ts` registers `{ role: 'close' }`
there). Moving it inside would hand Cmd+W back to the menu exactly when
a dialog is up. So the keystroke is still swallowed; only the delete is
skipped.
Doing nothing is the right answer rather than closing the dialog for
them — Esc already does that, in the handler immediately below, and
every dialog honours it.
## Left alone deliberately
The shortcut is matched inline as `meta && e.key === 'w'` rather than
through a `matchXShortcut` helper like its neighbours in
`keyboard-shortcuts.ts`. One consequence: with Caps Lock on, `e.key` is
`'W'` and the shortcut silently does nothing.
Routing it through a matcher would be tidier and testable in the same
place as its siblings — but it would also **widen** which keystrokes
delete a thread, and that is the wrong direction to move a destructive
shortcut without being asked. Happy to do it if you want it.
## Testing
`main.ts` binds its shortcuts inside a boot function with no seam to
call, so the wiring is pinned at the source level — the file's existing
test does the same for the layout boot — while the guard itself is
unit-tested against real `<dialog>` elements in happy-dom.
The wiring assertions test the **property**, not the text: *every*
`confirmDeleteThread()` call site is guarded, so a second unguarded one
added later fails the suite rather than slipping past an assertion that
only looked at the first.
| Check | Result |
|---|---|
| `node scripts/run-tests.mts` (whole repo) | **8641 tests, 8631 pass,
10 skipped, 0 fail** |
| `tsc --noEmit` (node + web) | clean |
| `pnpm run lint` / `oxfmt --check .` | clean |
| `check:dead-code` (840 modules) / `check:oracle` (243 specs, 15
invariants) | clean |
| `check:e2e-syntax` (269 files) / `demo:site:check` | clean |
| all three wiring assertions with the guard removed | fail, as designed
|
9 new tests: 5 on `isAnyDialogOpen` (closed dialog, modal, non-modal,
several at once, and one inserted as raw HTML that nothing registered),
3 on the Cmd+W / Cmd+F wiring, and the existing suite unchanged.
Two local-environment notes, neither affecting CI: reaching a full run
needs an uncommitted shim over node-pty's native loader (it cannot be
rebuilt in this sandbox — the agent proxy denies `iojs.org` and
`www.electronjs.org`), reverted before committing; and this container's
`node_modules` predates #2383, so
`patches/@anthropic-ai__sandbox-runtime@0.0.74.patch` was not applied
and `git-commit-signing.test.ts` failed until I applied its one-line
hunk by hand. That failure reproduces identically on `52c839a` with my
changes stashed, so it is a stale install here, not a base-branch break.
Refs #2474
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01Hpk1gEma9LhMUuvr2TwUj6
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Hpk1gEma9LhMUuvr2TwUj6)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jonathan Kingston <KingstonMailBox@gmail.com>
1 parent ad809fc commit ca3c718
6 files changed
Lines changed: 219 additions & 10 deletions
File tree
- src/renderer
- views
- tests/e2e
- screenshots
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
| 37 | + | |
37 | 38 | | |
38 | 39 | | |
39 | 40 | | |
| |||
653 | 654 | | |
654 | 655 | | |
655 | 656 | | |
656 | | - | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
657 | 661 | | |
658 | | - | |
659 | | - | |
660 | | - | |
661 | | - | |
662 | | - | |
663 | | - | |
664 | | - | |
| 662 | + | |
665 | 663 | | |
666 | 664 | | |
667 | 665 | | |
| |||
684 | 682 | | |
685 | 683 | | |
686 | 684 | | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
687 | 692 | | |
688 | 693 | | |
689 | | - | |
| 694 | + | |
690 | 695 | | |
691 | 696 | | |
692 | 697 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
0 commit comments