Skip to content

fix: make window auto-hide and alt keybinds conditional for Hyprland#49

Merged
VariableThe merged 1 commit into
mainfrom
fix/wayland-shortcuts-and-behavior
Jun 23, 2026
Merged

fix: make window auto-hide and alt keybinds conditional for Hyprland#49
VariableThe merged 1 commit into
mainfrom
fix/wayland-shortcuts-and-behavior

Conversation

@VariableThe

@VariableThe VariableThe commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • Changed
    • Global keyboard shortcuts now use Alt modifier instead of Ctrl/Cmd
    • Window no longer auto-hides when losing focus—app behaves as a standard window
    • Application now detects and adapts to your window manager environment
    • Onboarding documentation updated to reflect new shortcut defaults

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Hyprland Wayland compositor detection via a new Rust is_hyprland command that checks environment variables. The boolean result propagates to a Zustand store flag, which switches all application keybindings from Ctrl/Cmd to Alt and disables window auto-hide on focus loss when running under Hyprland. The onboarding Welcome.md is also updated to reflect the correct modifier.

Changes

Hyprland Wayland Support

Layer / File(s) Summary
Hyprland detection contract: types, store, and API bridge
src/types.d.ts, src/store/useAppStore.ts, src/api.ts, src-tauri/src/commands/system.rs, src-tauri/src/lib.rs
Adds isHyprland(): Promise<boolean> to ElectronAPI, adds isHyprland field and setIsHyprland setter to the Zustand store, wires the tauriApi.isHyprland invoke stub, and registers the Rust is_hyprland command that checks HYPRLAND_INSTANCE_SIGNATURE/HYPRLAND_CMD.
App startup detection and conditional window hide
src/App.tsx, src-tauri/src/lib.rs
On mount, App.tsx calls isHyprland() and stores the result. In the Rust WindowEvent::Focused handler, w.hide() is now skipped when Hyprland is detected.
Global hotkeys and settings shortcut defaults
src/hooks/useGlobalHotkey.ts, src/Settings.tsx
useGlobalHotkey computes a unified isMod from isHyprland and applies it across all shortcut branches; Settings.tsx selects Alt vs CommandOrControl as the default modifier for shortcutNewNote/shortcutToggle and rewires ShortcutInput to these new state variables.
Editor extensions and NoteSearch keybindings
src/lib/editor/extensions.ts, src/components/NoteSearch.tsx
CodeMirror keybindings for h, e, backspace/delete, and the mousedown link-open modifier switch to Alt-* / altKey on Hyprland; NoteSearch applies the same conditional modifier to both k-key action-menu branches.
Onboarding, tests, and docs
src-tauri/src/commands/fs.rs, src/setupTests.ts, CHANGELOG.md, AUDIT_LOG.md
run_onboarding() detects Hyprland to build correct Welcome.md content and force-rewrites files with legacy shortcut text. Test setup adds a localStorage mock and isHyprland stub. CHANGELOG and AUDIT_LOG are updated.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • VariableThe/PaperCache#10: Directly touches the same NoteSearch.tsx keyboard-handling logic that this PR modifies to add Hyprland-aware k-key shortcut conditions.

Poem

🐇 Hop hop, on Wayland I hop with glee,
Alt keys now rule where Ctrl used to be!
No hiding on focus—stand tall, little app,
Hyprland detected, no shortcut mishap.
The bunny has spoken: Alt-N takes flight! 🌿

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: making window auto-hide and Alt keybinds conditional based on Hyprland detection.
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.

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

✨ 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 fix/wayland-shortcuts-and-behavior

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.

@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: 4

🧹 Nitpick comments (2)
src-tauri/src/commands/fs.rs (1)

197-198: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the shared Hyprland detector instead of duplicating env-var checks.

This duplicates logic already defined in src-tauri/src/commands/system.rs:is_hyprland. Reusing that function avoids contract drift.

🤖 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-tauri/src/commands/fs.rs` around lines 197 - 198, The Hyprland detection
logic in fs.rs at lines 197-198 duplicates the env-var checks already
implemented in the is_hyprland function in system.rs. Replace the inline
environment variable checks for HYPRLAND_INSTANCE_SIGNATURE and HYPRLAND_CMD
with a call to the existing is_hyprland function from system.rs. Ensure the
is_hyprland function is accessible (make it public if necessary) and import it
into fs.rs, then use it to set the mod_key variable instead of performing the
duplicate checks inline.
src-tauri/src/lib.rs (1)

79-82: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Centralize Hyprland detection in one backend helper.

The Hyprland env check is duplicated here and in src-tauri/src/commands/system.rs; that can drift and desynchronize UI keybinding mode vs focus-hide behavior.

♻️ Proposed consolidation
// src-tauri/src/commands/system.rs
+pub fn detect_hyprland_env() -> bool {
+    std::env::var("HYPRLAND_INSTANCE_SIGNATURE").is_ok()
+        || std::env::var("HYPRLAND_CMD").is_ok()
+}
+
 #[tauri::command]
 pub fn is_hyprland() -> Result<bool, String> {
-    Ok(std::env::var("HYPRLAND_INSTANCE_SIGNATURE").is_ok() || std::env::var("HYPRLAND_CMD").is_ok())
+    Ok(detect_hyprland_env())
 }
// src-tauri/src/lib.rs
- let is_hyprland = std::env::var("HYPRLAND_INSTANCE_SIGNATURE").is_ok() || std::env::var("HYPRLAND_CMD").is_ok();
+ let is_hyprland = crate::commands::system::detect_hyprland_env();
🤖 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-tauri/src/lib.rs` around lines 79 - 82, The Hyprland environment variable
check (verifying HYPRLAND_INSTANCE_SIGNATURE and HYPRLAND_CMD) is duplicated
between src-tauri/src/lib.rs and src-tauri/src/commands/system.rs, which can
cause the UI keybinding mode and focus-hide behavior to drift if one location is
updated but not the other. Extract this Hyprland detection logic into a single
helper function or method in a common backend module, then replace both
instances of the duplicated check with calls to this centralized helper to
ensure consistent behavior across the codebase.
🤖 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-tauri/src/commands/fs.rs`:
- Around line 211-214: The current implementation uses broad substring matching
on lines 211-214 to detect if Welcome.md contains old default content, then
unconditionally rewrites the file, which will erase any custom user edits that
happen to include those shortcut strings. Replace the substring matching
conditions (checking for "use shortcuts to create a new one!", "Command/Ctrl +
Shift + N", or "Alt + Shift + N") with an exact content comparison that checks
if the file content matches the original default welcome_content exactly. This
way the file will only be overwritten if it is truly the unmodified default
version, preserving any user-edited Welcome.md files that may contain those
strings.

In `@src/App.tsx`:
- Around line 50-52: The window.electronAPI.isHyprland() promise call lacks
error handling, which can result in unhandled promise rejections during app
startup if the invoke fails. Add a .catch() handler after the .then() call in
the promise chain to explicitly handle failures, including optional error
logging, so the app remains in a predictable state even when the promise
rejects.

In `@src/Settings.tsx`:
- Around line 112-117: The code updates the global shortcut registration by
calling window.electronAPI.updateGlobalShortcut with the new shortcutToggle
value, but fails to persist this value back to localStorage. After the shortcut
update call in the block where window.electronAPI.updateGlobalShortcut is
invoked, add a localStorage.setItem call to save the shortcutToggle value using
the same key 'papercache-shortcut-toggle' that was used to read the old value,
ensuring the preference persists across application restarts.

In `@src/setupTests.ts`:
- Line 26: The getItem mock function in setupTests.ts uses the || operator which
incorrectly returns null for falsy stored values like empty strings, whereas the
real localStorage.getItem should return the actual stored value regardless of
truthiness. Fix the getItem mock by replacing the || null logic with a proper
key existence check (such as checking if the key exists in the store object
using an existence operator) so that empty strings and other falsy values stored
in the mock store are returned as-is rather than being replaced with null.

---

Nitpick comments:
In `@src-tauri/src/commands/fs.rs`:
- Around line 197-198: The Hyprland detection logic in fs.rs at lines 197-198
duplicates the env-var checks already implemented in the is_hyprland function in
system.rs. Replace the inline environment variable checks for
HYPRLAND_INSTANCE_SIGNATURE and HYPRLAND_CMD with a call to the existing
is_hyprland function from system.rs. Ensure the is_hyprland function is
accessible (make it public if necessary) and import it into fs.rs, then use it
to set the mod_key variable instead of performing the duplicate checks inline.

In `@src-tauri/src/lib.rs`:
- Around line 79-82: The Hyprland environment variable check (verifying
HYPRLAND_INSTANCE_SIGNATURE and HYPRLAND_CMD) is duplicated between
src-tauri/src/lib.rs and src-tauri/src/commands/system.rs, which can cause the
UI keybinding mode and focus-hide behavior to drift if one location is updated
but not the other. Extract this Hyprland detection logic into a single helper
function or method in a common backend module, then replace both instances of
the duplicated check with calls to this centralized helper to ensure consistent
behavior across the codebase.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 97d16e65-e8ce-485d-af84-b413976446cd

📥 Commits

Reviewing files that changed from the base of the PR and between 5607311 and 6f872e0.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (14)
  • AUDIT_LOG.md
  • CHANGELOG.md
  • src-tauri/src/commands/fs.rs
  • src-tauri/src/commands/system.rs
  • src-tauri/src/lib.rs
  • src/App.tsx
  • src/Settings.tsx
  • src/api.ts
  • src/components/NoteSearch.tsx
  • src/hooks/useGlobalHotkey.ts
  • src/lib/editor/extensions.ts
  • src/setupTests.ts
  • src/store/useAppStore.ts
  • src/types.d.ts

Comment thread src-tauri/src/commands/fs.rs
Comment thread src/App.tsx
Comment thread src/Settings.tsx
Comment thread src/setupTests.ts
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Failed to generate fixes. The agent execution returned an error and no code changes were found.

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.

1 participant