Skip to content

MM-69578: fix: allow closing windows other than the main window with cmd + w on macOS - #3878

Merged
devinbinnie merged 8 commits into
masterfrom
claude/admiring-fermat-u7azna
Jul 23, 2026
Merged

MM-69578: fix: allow closing windows other than the main window with cmd + w on macOS#3878
devinbinnie merged 8 commits into
masterfrom
claude/admiring-fermat-u7azna

Conversation

@svelle

@svelle svelle commented Jun 30, 2026

Copy link
Copy Markdown
Member

Summary

This PR fixes the behavior of keyboard shortcuts (particularly Cmd+W) when DevTools is open. When DevTools gains focus, the app menu is now rebuilt so that Cmd+W targets the DevTools window instead of the main window. Similarly, when DevTools loses focus, the menu is rebuilt again to restore normal behavior.

The fix adds event listeners for devtools-focused and devtools-closed events on web contents, emitting UPDATE_SHORTCUT_MENU to trigger menu reconstruction. This ensures consistent behavior with pop-out windows, which already handle focus changes via their BrowserWindow focus event.

Changes

  • Modified src/app/menus/appMenu/view.ts to add DevTools event listeners when opening DevTools from the View menu
  • Modified src/app/views/MattermostWebContentsView.ts to add the same DevTools event listeners when opening DevTools from the web view
  • Added import of ipcMain from electron and UPDATE_SHORTCUT_MENU constant from common/communication

Checklist

Release Note

Fixed keyboard shortcuts (Cmd+W) to target windows other than the main window when in focus.

https://claude.ai/code/session_01Uerg9bXjuH1nnSbjvwMaMM

Change Impact: 🟡 Medium

Regression Risk: The change is fairly targeted, but it touches multiple window/webContents initialization paths and global shortcut-menu refresh behavior. Risk is moderate because DevTools focus/close handling is now wired into several components, and regressions could affect macOS shortcut behavior or menu updates across windows.

QA Recommendation: Recommended manual QA on macOS for DevTools focus/close transitions in the main window, calls widget window, and popout window, with a quick sanity check on menu shortcut behavior after switching focus.
Generated by CodeRabbitAI

@github-actions github-actions Bot added the E2E/Run Run Desktop E2E Tests label Jun 30, 2026
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

DevTools focus/close listeners were added to three windows—MattermostWebContentsView, MainWindow, and CallsWidgetWindow—each emitting an UPDATE_SHORTCUT_MENU IPC event via ipcMain when devtools-focused or devtools-closed fires. Corresponding unit tests were added or extended, and a null-safety test was added for the app menu.

Changes

DevTools shortcut menu refresh

Layer / File(s) Summary
MattermostWebContentsView DevTools listeners and tests
src/app/views/MattermostWebContentsView.ts, src/app/views/MattermostWebContentsView.test.js
Registers devtools-focused/devtools-closed listeners emitting UPDATE_SHORTCUT_MENU during construction; adds test mocks (openDevTools, closeDevTools, isDevToolsOpened, ipcMain.emit) and tests for listener registration, IPC emission, and macOS reset timer behavior.
MainWindow DevTools listeners and tests
src/app/mainWindow/mainWindow.ts, src/app/mainWindow/mainWindow.test.js
Imports UPDATE_SHORTCUT_MENU and registers devtools-focused/devtools-closed listeners during init; extends test mocks with ipcMain.emit and adds tests asserting listener registration and emission.
CallsWidgetWindow DevTools listeners
src/app/callsWidgetWindow.ts
Adds devtools-focused/devtools-closed listeners on both the main widget window and pop-out window, each emitting UPDATE_SHORTCUT_MENU.
App menu null-window test
src/app/menus/appMenu/view.test.js
Adds jest.clearAllMocks() to beforeEach and a test verifying the Developer Tools menu click doesn't throw when MainWindow.get() returns null.

Estimated code review effort: 2 (Simple) | ~12 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WebContents
  participant Window as MainWindow / MattermostWebContentsView / CallsWidgetWindow
  participant ipcMain

  WebContents->>Window: devtools-focused event
  Window->>ipcMain: emit UPDATE_SHORTCUT_MENU
  WebContents->>Window: devtools-closed event
  Window->>ipcMain: emit UPDATE_SHORTCUT_MENU
Loading

Suggested reviewers: devinbinnie, saturninoabril

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the macOS Cmd+W shortcut fix and is clearly related to the DevTools-focused shortcut behavior change.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/admiring-fermat-u7azna

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@svelle svelle changed the title Update app menu when DevTools gains/loses focus MM-69578: fix: rebuild app menu when DevTools window gains focus on macOS Jun 30, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/app/menus/appMenu/view.ts (1)

10-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Place common/communication before the other common/* imports.

The added import is out of the ESLint-enforced alphabetical order for the internal alias group.

Proposed import order
-import Config from 'common/config';
-import ServerManager from 'common/servers/serverManager';
 import {UPDATE_SHORTCUT_MENU} from 'common/communication';
+import Config from 'common/config';
+import ServerManager from 'common/servers/serverManager';

As per coding guidelines, “Follow import order enforced by ESLint: builtins → external → @mattermost/* → internal aliases (app, common, main, renderer) → types → siblings/parent/index, with groups separated by blank lines and alphabetized within groups.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/menus/appMenu/view.ts` around lines 10 - 12, The import block in
view.ts is out of ESLint’s required alphabetical order for internal aliases.
Reorder the `common/*` imports so `common/communication` is placed before
`common/config` and `common/servers/serverManager`, keeping the internal alias
group alphabetized and preserving the existing import grouping rules.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/views/MattermostWebContentsView.ts`:
- Around line 328-335: The DevTools shortcut-menu refresh listeners in
MattermostWebContentsView are being removed by the macOS close-and-reopen path,
so the reopened DevTools no longer gets updates. Update the
devtools-closed/devtools-focused handling around the webContentsView.webContents
listeners to re-register the refresh listener after the synthetic close path,
and ensure UPDATE_SHORTCUT_MENU still fires for the reopened DevTools state.

---

Nitpick comments:
In `@src/app/menus/appMenu/view.ts`:
- Around line 10-12: The import block in view.ts is out of ESLint’s required
alphabetical order for internal aliases. Reorder the `common/*` imports so
`common/communication` is placed before `common/config` and
`common/servers/serverManager`, keeping the internal alias group alphabetized
and preserving the existing import grouping rules.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 77d17f5f-3745-427a-8013-3e219e685dc2

📥 Commits

Reviewing files that changed from the base of the PR and between c436e1f and cf2bca9.

📒 Files selected for processing (2)
  • src/app/menus/appMenu/view.ts
  • src/app/views/MattermostWebContentsView.ts

Comment thread src/app/views/MattermostWebContentsView.ts Outdated
@github-actions github-actions Bot added E2E/Run Run Desktop E2E Tests and removed E2E/Run Run Desktop E2E Tests labels Jun 30, 2026
@github-actions github-actions Bot added E2E/Run Run Desktop E2E Tests and removed E2E/Run Run Desktop E2E Tests labels Jun 30, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/menus/appMenu/view.test.js`:
- Around line 252-312: The two `createViewMenu` tests are sharing `ipcMain.emit`
call history, so the `devtools-closed` assertion can pass from a previous
`devtools-focused` emission. Isolate mock state per test by clearing
`ipcMain.emit` in the test setup (for example in `beforeEach`) along with the
other menu-related mocks, so each case verifies only its own
`onDevToolsFocused`/`onDevToolsClosed` flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 90b2b07e-4648-4c5b-8ccb-bc6317830191

📥 Commits

Reviewing files that changed from the base of the PR and between 42f542b and 56ac3d6.

📒 Files selected for processing (1)
  • src/app/menus/appMenu/view.test.js

Comment thread src/app/menus/appMenu/view.test.js Outdated
@github-actions github-actions Bot added the E2E/Run Run Desktop E2E Tests label Jun 30, 2026
@github-actions github-actions Bot removed the E2E/Run Run Desktop E2E Tests label Jun 30, 2026

@devinbinnie devinbinnie left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good QoL improvement, I have some suggestions though.

Also, should we account for the calls widget window as well? That also has a dev tools option.

Comment thread src/app/menus/appMenu/view.ts Outdated
})(),
click() {
MainWindow.get()?.webContents.openDevTools({mode: 'detach'});
const wc = MainWindow.get()?.webContents;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do this registration and listener setup in mainWindow.ts where the other listeners for the main window are? Feels weird to be adding it here in the click handler.

this.webContentsView.webContents.off('devtools-focused', onDevToolsFocused);
ipcMain.emit(UPDATE_SHORTCUT_MENU);
};
this.webContentsView.webContents.once('devtools-closed', onDevToolsClosed);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is just firing when the devtools is being interacted with, do we really need the on and off here? I think we can just register the listener to run permanently like we do with the other listeners per web contents. I don't think we need to toggle the listener on/off dependent on whether the devtools is open, since it should only fire these events when the devtools is open. And in any case, it probably shouldn't matter if it updates the shortcut menu more often.

The same should apply for the main window as well.

@devinbinnie devinbinnie added the 2: Dev Review Requires review by a core committer label Jul 6, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/callsWidgetWindow.ts`:
- Around line 213-214: CallsWidgetWindow is registering DevTools event handlers
on its webContents, but the test coverage does not verify that behavior. Update
src/app/callsWidgetWindow.test.js to assert that CallsWidgetWindow (via onShow()
or the relevant setup path) registers both devtools-focused and devtools-closed
listeners on the widget/popout webContents, and that those listeners emit
UPDATE_SHORTCUT_MENU through ipcMain when triggered. Use CallsWidgetWindow,
onShow(), and UPDATE_SHORTCUT_MENU to locate the behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 746ef186-e6b3-4884-96e2-c273bc8f517d

📥 Commits

Reviewing files that changed from the base of the PR and between d756367 and fa001e3.

📒 Files selected for processing (6)
  • src/app/callsWidgetWindow.ts
  • src/app/mainWindow/mainWindow.test.js
  • src/app/mainWindow/mainWindow.ts
  • src/app/menus/appMenu/view.test.js
  • src/app/views/MattermostWebContentsView.test.js
  • src/app/views/MattermostWebContentsView.ts

Comment thread src/app/callsWidgetWindow.ts Outdated
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Documentation Impact Analysis — no longer needed

A previous automated documentation impact comment exists, but the latest analysis determined that no documentation changes are needed.

The Docs/Needed label may still be present from the earlier analysis. A maintainer can remove it after confirming no docs updates are required.

@devinbinnie devinbinnie left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple nits, but much better. Can we make sure this is QAed or tested somehow especially with the changes to the calls widget?

Thanks @svelle :)

Comment thread src/app/mainWindow/mainWindow.ts Outdated
this.win.browserWindow.on('blur', this.onBlur);
this.win.browserWindow.contentView.on('bounds-changed', this.handleBoundsChanged);
this.win.browserWindow.webContents.on('before-input-event', this.onBeforeInputEvent);
this.win.browserWindow.webContents.on('devtools-focused', () => ipcMain.emit(UPDATE_SHORTCUT_MENU));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Make these a member function of the MainWindow class so we don't add multiple unnecessary functions.

});
this.webContentsView.webContents.on('did-navigate-in-page', () => this.handlePageTitleUpdated(this.webContentsView.webContents.getTitle()));
this.webContentsView.webContents.on('page-title-updated', (_, newTitle) => this.handlePageTitleUpdated(newTitle));
this.webContentsView.webContents.on('devtools-focused', () => ipcMain.emit(UPDATE_SHORTCUT_MENU));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here re: nit

Comment thread src/app/callsWidgetWindow.ts Outdated
this.win.webContents.on('will-navigate', this.onNavigate);
this.win.webContents.on('did-start-navigation', this.onNavigate);
this.win.webContents.on('devtools-focused', () => ipcMain.emit(UPDATE_SHORTCUT_MENU));
this.win.webContents.on('devtools-closed', () => ipcMain.emit(UPDATE_SHORTCUT_MENU));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too.

@devinbinnie devinbinnie added 4: Reviews Complete All reviewers have approved the pull request and removed 2: Dev Review Requires review by a core committer labels Jul 6, 2026
@mattermost-build

Copy link
Copy Markdown
Contributor

This PR has been automatically labelled "stale" because it hasn't had recent activity.
A core team member will check in on the status of the PR to help with questions.
Thank you for your contribution!

claude added 5 commits July 22, 2026 14:53
DevTools windows are not BrowserWindows, so BrowserWindow.getFocusedWindow()
returns null when they're focused. The app menu was only rebuilt on main window
focus (MAIN_WINDOW_FOCUSED) and popout window focus, so Cmd+W with a DevTools
window in focus would still fire the stale handler from when the main window
was focused — closing the main window instead of the DevTools window.

Fix by listening to 'devtools-focused' on the WebContents whose DevTools
opened, and emitting UPDATE_SHORTCUT_MENU to trigger a menu rebuild. The
rebuilt menu has role:'close' with CmdOrCtrl+W (no focused BrowserWindow),
so macOS sends performClose: to the frontmost window (the DevTools window).
Also listen to 'devtools-closed' to remove the listener and rebuild again.

Mirrors the pattern used by popout windows, which call MenuManager.refreshMenu()
in their BrowserWindow 'focus' handler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uerg9bXjuH1nnSbjvwMaMM
…acOS reopen

- Move common/communication import before common/config to satisfy ESLint import/order rule
- Extract DevTools menu refresh listener setup into registerDevToolsMenuRefresh() so
  the macOS close-and-reopen workaround path also re-registers the listeners after
  the synthetic devtools-closed consumes them (per CodeRabbit review)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uerg9bXjuH1nnSbjvwMaMM
Update existing test mock to include isDevToolsOpened, on, once, and off
methods required by the new click handler. Add tests covering: early return
when MainWindow is null, guard preventing duplicate listener registration
when DevTools is already open, devtools-focused emitting UPDATE_SHORTCUT_MENU,
and devtools-closed removing the focus listener and emitting UPDATE_SHORTCUT_MENU.
…isteners

Cover the registerDevToolsMenuRefresh logic added to openDevTools: listener
registration, devtools-focused/devtools-closed handlers emitting
UPDATE_SHORTCUT_MENU, the guard against double-registration, and the macOS
workaround path that re-registers listeners after the synthetic close-and-reopen.
Also clear all mocks in beforeEach of view.test.js to prevent shared ipcMain
call history between devtools-focused and devtools-closed assertions.
…n each openDevTools call

Per reviewer feedback:
- Move devtools-focused/devtools-closed listeners into constructors/init methods
  (MattermostWebContentsView constructor, MainWindow.init, CallsWidgetWindow.init
  and onPopOutCreate) so they are registered once and stay active permanently.
- Simplify view.ts click handler to just call openDevTools directly; listener
  setup now lives in mainWindow.ts where the other main-window listeners are.
- Also cover the calls widget window and its popout (as suggested by reviewer).
- Simplify openDevTools in MattermostWebContentsView to remove the conditional
  registerDevToolsMenuRefresh helper; only the macOS reopen workaround remains.
- Update all affected tests to reflect the new architecture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uerg9bXjuH1nnSbjvwMaMM
claude and others added 3 commits July 22, 2026 14:54
Fix broken onPopOutCreate test where the will-redirect listener was
being overwritten by the new devtools-focused/devtools-closed listeners.
Add 8 new tests covering devtools listener registration and UPDATE_SHORTCUT_MENU
emission for both the widget window (init) and the popout (onPopOutCreate).
Replace inline () => ipcMain.emit(UPDATE_SHORTCUT_MENU) arrow functions
passed to webContents.on() with a named private emitShortcutMenuUpdate
member on each class, avoiding unnecessary repeated function allocations.
…md+W

Electron's BrowserWindow.getFocusedWindow() returns the owner window when
that window's DevTools is focused, so rebuilding the menu alone still left
Close Tab bound to Cmd+W for Main Window DevTools. Exclude the
isDevToolsFocused case when deciding close accelerators.

Co-authored-by: Sven Hüster <svelle@users.noreply.github.com>
@cursor
cursor Bot force-pushed the claude/admiring-fermat-u7azna branch from 8c2b931 to 3ba7a87 Compare July 22, 2026 14:54
@github-actions github-actions Bot added E2E/Run Run Desktop E2E Tests and removed E2E/Run Run Desktop E2E Tests labels Jul 22, 2026
@svelle

svelle commented Jul 22, 2026

Copy link
Copy Markdown
Member Author

@devinbinnie thanks for your reviews on this.

I think I addressed it all now. I also finally went and actually tested it (claude opened the PR before I was ready for it to be reviewed 😅 ) it's working like a charm now, even with the calls pop-out widget and dev tools.

@svelle svelle changed the title MM-69578: fix: rebuild app menu when DevTools window gains focus on macOS MM-69578: fix: allow closing windows other than the main window with cmd + w on macOS Jul 23, 2026
@devinbinnie
devinbinnie merged commit 983f4f8 into master Jul 23, 2026
35 checks passed
@devinbinnie
devinbinnie deleted the claude/admiring-fermat-u7azna branch July 23, 2026 12:57
@amyblais amyblais added this to the v6.1.0 milestone Jul 24, 2026 — with Claude
@amyblais amyblais modified the milestones: v6.1.0, v6.4.0 Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4: Reviews Complete All reviewers have approved the pull request Lifecycle/1:stale release-note

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants