Prerequisites
Install Method
Docker (docker compose up)
Operating System
Linux
Steps to Reproduce
- Open the email library and open an email so the reader view shows.
- Go back to the list, then permanently delete that email (not trash).
- Click the "..." (more / kebab) button on any other email row.
- Observe: the dropdown does not open — the button is dead until the page is reloaded.
This is one instance of a general pattern. The same stale-listener breakage can be triggered on most of the app's ad-hoc dropdown/context menus (gallery bulk menu, notes corner menu, the document export menu, the skills card menu, the tasks dropdown, the settings integration dropdowns, and more) any time a menu is dismissed by something other than an outside click.
Expected Behaviour
After a menu closes by any means — clicking an item, a Cancel button, Escape, or opening a different menu — every other menu trigger keeps working normally. No page refresh should ever be required to "unstick" a menu button.
Actual Behaviour
Each of these menus wires its own document-level outside-click listener, roughly:
const close = (ev) => { if (menu.contains(ev.target)) return; menu.remove(); document.removeEventListener('click', close, true); };
setTimeout(() => document.addEventListener('click', close, true), 10);
That listener only removes itself on an outside click. Every other dismissal path — an item click that calls menu.remove() directly, a Cancel button, Escape, or the "close the previously-open menu" reopen sweep — tears the node down without unregistering the listener, leaving it orphaned on document. The stale listener then interferes with the next menu interaction, so the trigger appears dead until a reload. No console error is produced, which is why it is easy to miss.
The repo already ships the right tool for this: the escMenuStack helper (bindMenuDismiss / dismissOrRemove) introduced in #684 and already used correctly by documentLibrary.js _showLibDropdown — but roughly 22 other menus were never migrated onto it.
Logs / Screenshots
No console error or stack trace is produced; the failure is silent — the menu trigger simply does nothing on the next click until the page is reloaded.
Model / Backend (if relevant)
N/A — this is a front-end (browser) UI bug, independent of the model backend or OS.
Are you willing to submit a fix?
Yes — I can open a PR
Additional Information
Root cause: the orphaned document click listener. Fix: route these menus through the existing escMenuStack helper so a single idempotent close() owns teardown on every dismissal path (item click, Cancel, Escape, outside click, reopen sweep).
A draft PR for the 16 mechanical (clean 1:1 migration) menus is up at #4684, left as a draft pending end-to-end verification. Six menus with more involved lifecycles (the notes reminder menu, the sessions long-press menu, the document markdown-toolbar menu, the emoji picker, and the compare model-selector) are noted there as a follow-up.
Transparency: this bug and the proposed fix were prepared with the assistance of an LLM agent (Claude Code). Per CONTRIBUTING.md's "Auto-generated PRs" note I am filing this issue first and leaving the PR as a draft for your call on whether to proceed.
Prerequisites
devbranch and the bug still reproduces there.Install Method
Docker (docker compose up)
Operating System
Linux
Steps to Reproduce
This is one instance of a general pattern. The same stale-listener breakage can be triggered on most of the app's ad-hoc dropdown/context menus (gallery bulk menu, notes corner menu, the document export menu, the skills card menu, the tasks dropdown, the settings integration dropdowns, and more) any time a menu is dismissed by something other than an outside click.
Expected Behaviour
After a menu closes by any means — clicking an item, a Cancel button, Escape, or opening a different menu — every other menu trigger keeps working normally. No page refresh should ever be required to "unstick" a menu button.
Actual Behaviour
Each of these menus wires its own
document-level outside-click listener, roughly:That listener only removes itself on an outside click. Every other dismissal path — an item click that calls
menu.remove()directly, a Cancel button, Escape, or the "close the previously-open menu" reopen sweep — tears the node down without unregistering the listener, leaving it orphaned ondocument. The stale listener then interferes with the next menu interaction, so the trigger appears dead until a reload. No console error is produced, which is why it is easy to miss.The repo already ships the right tool for this: the
escMenuStackhelper (bindMenuDismiss/dismissOrRemove) introduced in #684 and already used correctly bydocumentLibrary.js_showLibDropdown— but roughly 22 other menus were never migrated onto it.Logs / Screenshots
No console error or stack trace is produced; the failure is silent — the menu trigger simply does nothing on the next click until the page is reloaded.
Model / Backend (if relevant)
N/A — this is a front-end (browser) UI bug, independent of the model backend or OS.
Are you willing to submit a fix?
Yes — I can open a PR
Additional Information
Root cause: the orphaned
documentclick listener. Fix: route these menus through the existingescMenuStackhelper so a single idempotentclose()owns teardown on every dismissal path (item click, Cancel, Escape, outside click, reopen sweep).A draft PR for the 16 mechanical (clean 1:1 migration) menus is up at #4684, left as a draft pending end-to-end verification. Six menus with more involved lifecycles (the notes reminder menu, the sessions long-press menu, the document markdown-toolbar menu, the emoji picker, and the compare model-selector) are noted there as a follow-up.
Transparency: this bug and the proposed fix were prepared with the assistance of an LLM agent (Claude Code). Per CONTRIBUTING.md's "Auto-generated PRs" note I am filing this issue first and leaving the PR as a draft for your call on whether to proceed.