Skip to content

Turns app:globalhotkey into a dedicated quake mode#3151

Open
Midnight145 wants to merge 7 commits intowavetermdev:mainfrom
Midnight145:quake
Open

Turns app:globalhotkey into a dedicated quake mode#3151
Midnight145 wants to merge 7 commits intowavetermdev:mainfrom
Midnight145:quake

Conversation

@Midnight145
Copy link
Copy Markdown

@Midnight145 Midnight145 commented Mar 29, 2026

closes #3138, closes #2128

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 29, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 29, 2026

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fb16b6f2-3ddb-4738-9f7b-3d7e0c1ac6ed

📥 Commits

Reviewing files that changed from the base of the PR and between 936b44f and a146ec7.

📒 Files selected for processing (1)
  • emain/emain-window.ts

Walkthrough

A module-level nullable quakeWindow was added and set to the first created WaveBrowserWindow, cleared when that window is closed or destroyed. The global hotkey flow was replaced with an async toggle guarded by quakeToggleInProgress to avoid races. If quakeWindow exists and is visible it hides; otherwise it shows the window, selecting the target display by cursor position and preserving work-area-relative offsets when moving. Fullscreen windows are temporarily exited and later restored using pre/post delays. If no valid quakeWindow exists, a new window is created. After showing, the window and its active tab webContents are focused.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.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
Title check ✅ Passed The title clearly and concisely describes the main change: converting the global hotkey functionality into a quake mode toggle system for the Wave window.
Description check ✅ Passed The description references the two linked issues (#3138 and #2128) that this PR addresses, directly relating to the changeset objectives.
Linked Issues check ✅ Passed The code changes implement quake mode functionality with show/hide toggle [#2128] and window focusing/bringing to front [#3138], meeting both linked issue requirements.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing quake mode and fixing the global hotkey window-focusing behavior specified in issues #3138 and #2128.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot bot commented Mar 29, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (1 file)
  • emain/emain-window.ts - No new issues found

Notes:

  • Latest commit (a146ec7) fixes off-by-one bug in moveWindowToDisplay: maxXOffset and maxYOffset now correctly use nextWidth/nextHeight (clamped dimensions) instead of curBounds.width/curBounds.height
  • Previous review findings resolved - the window resizing logic is now consistent
  • PR removes the "quake window" concept entirely (quakeWindow variable, getDisplayForQuakeToggle, moveWindowToDisplay, PRE/POST_QUAKE_FULLSCREEN_DELAY_MS) in favor of simpler focus behavior
  • No new issues introduced

Reviewed by minimax-m2.5-20260211 · 349,508 tokens


Reviewed by minimax-m2.5-20260211 · 184,370 tokens

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

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)
emain/emain-window.ts (1)

964-971: Consider documenting or externalizing the transition delays.

The hardcoded delays (120ms, 80ms) for fullscreen transitions are environment-dependent and may not be sufficient on all window managers or systems. A brief comment explaining why these values were chosen would help maintainability. The double moveWindowToDisplay call (before show and after delay) could also use a comment explaining why it's necessary.

📝 Suggested documentation
+                        // Delay needed for WM to process fullscreen exit before move
                         quakeWindow.setFullScreen(false);
                         await delay(120);
                         moveWindowToDisplay(quakeWindow, targetDisplay);
                         quakeWindow.show();
                         if (wasFullscreen) {
+                            // Re-apply move after show to handle WMs that reset position
                             await delay(80);
                             moveWindowToDisplay(quakeWindow, targetDisplay);
                             quakeWindow.setFullScreen(true);
                         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@emain/emain-window.ts` around lines 964 - 971, Replace the magic numbers and
unexplained duplicate move call with named constants and a short comment:
extract the delays (120 and 80 ms) into descriptively named constants (e.g.,
FULLSCREEN_PRE_SHOW_DELAY_MS and FULLSCREEN_POST_SHOW_DELAY_MS) and add a
comment near moveWindowToDisplay(quakeWindow, targetDisplay) +
quakeWindow.show() explaining why the window is moved before showing and again
after the small delay (to handle timing/race conditions with some window
managers), and reference the wasFullscreen branch where
quakeWindow.setFullScreen(true) is called so future maintainers know these
values control the fullscreen transition timing and where to tweak them.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@emain/emain-window.ts`:
- Around line 952-987: The hotkey handler can run concurrently causing flicker;
add a guard (e.g., a module-scoped boolean like isTogglingQuake or a simple
mutex) around the globalShortcut.register callback to return immediately if an
operation is already in progress, set the guard true before starting the async
sequence inside fireAndForget and ensure it is cleared in a finally block after
operations that reference quakeWindow, createNewWaveWindow, moveWindowToDisplay,
getDisplayForQuakeToggle, etc.; this prevents overlapping executions while
preserving existing behavior.

---

Nitpick comments:
In `@emain/emain-window.ts`:
- Around line 964-971: Replace the magic numbers and unexplained duplicate move
call with named constants and a short comment: extract the delays (120 and 80
ms) into descriptively named constants (e.g., FULLSCREEN_PRE_SHOW_DELAY_MS and
FULLSCREEN_POST_SHOW_DELAY_MS) and add a comment near
moveWindowToDisplay(quakeWindow, targetDisplay) + quakeWindow.show() explaining
why the window is moved before showing and again after the small delay (to
handle timing/race conditions with some window managers), and reference the
wasFullscreen branch where quakeWindow.setFullScreen(true) is called so future
maintainers know these values control the fullscreen transition timing and where
to tweak them.
🪄 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: 601a194c-9623-42cf-8c88-03309cf094d8

📥 Commits

Reviewing files that changed from the base of the PR and between 96c2526 and 8b28b93.

📒 Files selected for processing (1)
  • emain/emain-window.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@emain/emain-window.ts`:
- Around line 714-718: Currently createBrowserWindow() assigns the global
quakeWindow based on creation order; instead stop setting quakeWindow inside
createBrowserWindow() and add an explicit way for callers to designate the quake
window (e.g., accept a designateAsQuakeWindow boolean or expose a
setQuakeWindow(bwin) helper). Update relaunchBrowserWindows() to call
createBrowserWindow(...) for each window but pass designateAsQuakeWindow = true
when windowId === primaryWindowId (so the primary restored window becomes
quakeWindow), and update the hotkey path that creates a replacement quake window
to also set designateAsQuakeWindow = true or call setQuakeWindow(bwin) after
creation; ensure the global quakeWindow variable is only set by that explicit
mechanism (referencing quakeWindow, createBrowserWindow(),
relaunchBrowserWindows(), primaryWindowId).
- Around line 965-986: The toggle path reads quakeWindow, then awaits delays and
calls methods on it; capture the current window into a local (e.g., const win =
quakeWindow) before performing any awaits and, after each await (after
PRE_QUAKE_FULLSCREEN_DELAY_MS and POST_QUAKE_FULLSCREEN_DELAY_MS), validate that
win is still present and not destroyed (guard with if (!win ||
win.isDestroyed()) return; or abort the toggle flow) before calling
moveWindowToDisplay(win, ...), win.show(), win.setFullScreen(...), win.focus(),
and accessing win.activeTabView?.webContents.focus(); apply the same
local-capture-and-guard pattern around both await points to avoid dereferencing
a nulled/destroyed quakeWindow.
🪄 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: 17e130fe-1b5a-4e2d-878f-791ba3057b99

📥 Commits

Reviewing files that changed from the base of the PR and between 8b28b93 and bad2cdb.

📒 Files selected for processing (1)
  • emain/emain-window.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
emain/emain-window.ts (1)

714-718: ⚠️ Potential issue | 🟠 Major

Don't pick the quake window by creation order.

relaunchBrowserWindows() restores windowIds in reverse order, so this still designates the last restored window instead of primaryWindowId. After a relaunch, the hotkey can end up hiding/showing the wrong restored window. Please make the caller designate the quake window explicitly instead of inferring it here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@emain/emain-window.ts` around lines 714 - 718, Currently this file
auto-assigns quakeWindow by creation order (quakeWindow, bwin,
bwin.waveWindowId), which conflicts with relaunchBrowserWindows restoring
windowIds in reverse; remove the automatic designation here so the caller
explicitly sets quakeWindow (e.g., after relaunchBrowserWindows using
primaryWindowId). Concretely: delete or disable the block that assigns
quakeWindow = bwin (and the console.log), and rely on the caller to set
quakeWindow (or pass an explicit flag/primaryWindowId to this module) rather
than inferring from creation order.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@emain/emain-window.ts`:
- Around line 926-946: moveWindowToDisplay currently only clamps x/y offsets so
when curBounds.width/height exceed targetDisplay.workArea the window stays too
large and extends off-screen; update moveWindowToDisplay to clamp
curBounds.width/height to targetArea.width/height (and adjust x/y after
clamping) before calling win.setBounds, e.g., compute newWidth =
Math.min(curBounds.width, targetArea.width) and newHeight =
Math.min(curBounds.height, targetArea.height), recalc maxXOffset/maxYOffset and
nextX/nextY using the clamped sizes, then call win.setBounds with {
...curBounds, width: newWidth, height: newHeight, x: nextX, y: nextY } so the
window fits fully inside targetArea.

---

Duplicate comments:
In `@emain/emain-window.ts`:
- Around line 714-718: Currently this file auto-assigns quakeWindow by creation
order (quakeWindow, bwin, bwin.waveWindowId), which conflicts with
relaunchBrowserWindows restoring windowIds in reverse; remove the automatic
designation here so the caller explicitly sets quakeWindow (e.g., after
relaunchBrowserWindows using primaryWindowId). Concretely: delete or disable the
block that assigns quakeWindow = bwin (and the console.log), and rely on the
caller to set quakeWindow (or pass an explicit flag/primaryWindowId to this
module) rather than inferring from creation order.
🪄 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: 12db5393-d493-4011-8d62-c766216e32cd

📥 Commits

Reviewing files that changed from the base of the PR and between bad2cdb and 260622a.

📒 Files selected for processing (1)
  • emain/emain-window.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
emain/emain-window.ts (1)

989-992: ⚠️ Potential issue | 🟡 Minor

Add a destroyed check before final focus operations.

After show() and the optional fullscreen restore, the code accesses window.focus() and window.activeTabView without a final destroyed check. While unlikely, the window could close during the show/fullscreen sequence.

🛡️ Suggested hardening
-                            window.focus();
-                            if (window.activeTabView?.webContents) {
-                                window.activeTabView.webContents.focus();
-                            }
+                            if (!window.isDestroyed()) {
+                                window.focus();
+                                window.activeTabView?.webContents?.focus();
+                            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@emain/emain-window.ts` around lines 989 - 992, After the show()/fullscreen
sequence, guard the final focus operations with a destroyed check: verify the
BrowserWindow instance is not destroyed (e.g., using window.isDestroyed())
before calling window.focus(), and then only call
window.activeTabView.webContents.focus() if window.activeTabView and its
webContents still exist; update the code around window.focus() and
window.activeTabView?.webContents.focus() to early-return or skip these calls
when window.isDestroyed() is true or when activeTabView/webContents are missing.
🧹 Nitpick comments (1)
emain/emain-window.ts (1)

936-947: Bounds clamping works correctly, but could be clearer.

The width/height clamping was added (good!), but maxXOffset/maxYOffset still reference curBounds.width/curBounds.height rather than the clamped nextWidth/nextHeight. The current code happens to work because when curBounds exceeds targetArea, the max offset clamps to 0 anyway, but using the clamped values would make the intent clearer.

♻️ Optional cleanup
 const nextHeight = Math.min(curBounds.height, targetArea.height);
 const nextWidth = Math.min(curBounds.width, targetArea.width);
-const maxXOffset = Math.max(0, targetArea.width - curBounds.width);
-const maxYOffset = Math.max(0, targetArea.height - curBounds.height);
+const maxXOffset = Math.max(0, targetArea.width - nextWidth);
+const maxYOffset = Math.max(0, targetArea.height - nextHeight);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@emain/emain-window.ts` around lines 936 - 947, The maxXOffset and maxYOffset
calculations still use curBounds.width/curBounds.height instead of the clamped
nextWidth/nextHeight, which is misleading; update maxXOffset = Math.max(0,
targetArea.width - nextWidth) and maxYOffset = Math.max(0, targetArea.height -
nextHeight) so the offset clamping uses the actual clamped dimensions, then keep
sourceXOffset/sourceYOffset and nextX/nextY calculations as-is and call
win.setBounds with the updated nextWidth/nextHeight.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@emain/emain-window.ts`:
- Around line 989-992: After the show()/fullscreen sequence, guard the final
focus operations with a destroyed check: verify the BrowserWindow instance is
not destroyed (e.g., using window.isDestroyed()) before calling window.focus(),
and then only call window.activeTabView.webContents.focus() if
window.activeTabView and its webContents still exist; update the code around
window.focus() and window.activeTabView?.webContents.focus() to early-return or
skip these calls when window.isDestroyed() is true or when
activeTabView/webContents are missing.

---

Nitpick comments:
In `@emain/emain-window.ts`:
- Around line 936-947: The maxXOffset and maxYOffset calculations still use
curBounds.width/curBounds.height instead of the clamped nextWidth/nextHeight,
which is misleading; update maxXOffset = Math.max(0, targetArea.width -
nextWidth) and maxYOffset = Math.max(0, targetArea.height - nextHeight) so the
offset clamping uses the actual clamped dimensions, then keep
sourceXOffset/sourceYOffset and nextX/nextY calculations as-is and call
win.setBounds with the updated nextWidth/nextHeight.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a76773a2-6634-4973-a521-f09c5d53277e

📥 Commits

Reviewing files that changed from the base of the PR and between 260622a and 936b44f.

📒 Files selected for processing (1)
  • emain/emain-window.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: "app:globalhotkey" doesn't bring the window to the front [Feature]: add quake mode

2 participants