Repository navigation
Mouse-First v0.2: event-driven rewrite and drag/snap fixes - #1
Merged
Merged
Conversation
- README documents real requirements (no jq/socat), usage, IPC actions, every config key and how the plugin works. - CHANGELOG for 0.2.0. - scripts/dev-link.sh replaces install.sh for development; it never touches Hyprland config files. - CI runs the geometry tests, validates the manifest, checks Lua syntax and shellchecks scripts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dragging a window without the top-edge zone (SUPER + drag, or an app's own title bar) is a Hyprland drag, which places the window at "start position + pointer delta". Snaps could not be previewed or applied, and a snapped window only regained its size after the button was released. - Track the left button with two non-consuming binds (Hyprland's Lua API cannot read mouse buttons; hl.is_key_down only sees keyboard keys). - When a floating window starts moving under a held button, end Hyprland's drag (the drag dispatcher ends the current drag when called outside a button press) and hand it to the shell's DragController, which then follows cursor reports until release. Native drags now get the same restore-under-cursor, snap preview and snap-on-drop as the top-edge zone. - WindowStates waits while the button is held instead of treating the first moved frame as an un-snap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The v0.1 plugin was a single 2600-line bar widget that polled hyprctl, parsed Python/Bash output and drew a fullscreen overlay once per monitor. This rewrite keeps every feature but changes how it is built: - Service + thin bar widget: all state lives in one service; the bar widget only renders app icons, so multi-monitor setups no longer get one overlay (and one set of controls) per bar. - Compositor-side Lua module (src/lua/mousefirst.lua) loaded with `hyprctl eval`: reports the active window, monitor layout and button state as custom IPC events. No polling, no spawned processes; the controls follow windows during SUPER+drag and border resizes. - Explicit window state machine (free / snapped / maximized + restore geometry) replacing size caches and browser-specific magic numbers. - Drag controller in global coordinates: works across monitors, restores snapped/maximized windows under the cursor, pauses window animations while dragging, and never resizes a window during a native drag. - Pure, unit-tested geometry (src/js/geometry.js, tests/). - "Disable tiling" is a real window rule instead of re-floating windows. - Minimize uses special:minimized (shared with omadock), remembers the origin workspace on disk and migrates v0.1's special:mf-minimized. - Theme via qs.Commons, settings via FileView/JsonAdapter with atomic writes and live reload, icon lookup that survives missing themes. - Removes bin/* scripts, install.sh and the duplicated screenshot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Resizing a snapped window by its border drags the snapped windows that share that edge along, live, like a split view; both stay snapped. The edge math is a pure, tested helper (geometry.followEdge). - New options: drag to the top edge to maximize, center new windows (the floating rule is now two named rules, one per mode), optional snapping shortcuts (Super+Ctrl+Shift + arrows, off by default), and hiding the controls for chosen apps. Picture-in-picture players and windows narrower than the capsule never get controls. - Remove the old "wait until the window stops" fallback; the button is now always known. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hyprland 0.56.2 aborted in Config::Lua::Objects::CLuaKeybind (crash reports 697, 221205): its keybind methods null-check an std::optional<SP<SKeybind>> instead of the pointer, so calling any method on a handle whose bind is gone dereferences null. HL.Keybind :remove() also drops every bind with the same key and modifiers, so removing the first of our two mouse:272 binds killed the second, and removing that one crashed the compositor. This happened on every shell restart or plugin reload. - Register the button binds once per Lua state and never remove them; their callbacks are gated by MOUSE_FIRST.enabled. A config reload starts a fresh Lua state, so nothing accumulates. - Remove shortcuts with hl.unbind(<keys>), which does not go through the buggy handle code. - Forget handles left by earlier builds without touching them, so the fix can be applied over a running session. Verified in a nested Hyprland: old module, new module on top, stop, reinstall twice, shortcuts on/on/off and stop all survive, with exactly two button binds left. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… check QML syntax Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- One bar icon per app with a window count. Clicking launches the app, toggles its only window, or cycles through several (most recently used first). - Right-click menu: the app's windows, New window, Pin/Unpin (written to omadock's dock.json, so both stay in sync) and Close / Close all. - The icon index is cached on disk and only rebuilt, at low priority, when an app's icon is missing, instead of on every shell start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Add Español; the default language follows $LANG (pt, es, else en). - tests/i18n.test.mjs fails if any language misses a key. - README and CHANGELOG cover the new behaviour, options and shortcuts. - CI runs on pushes to main and on pull requests only, instead of twice per push to a PR branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This rewrites Mouse-First's internals. Every v0.1 feature is kept, and existing
~/.config/omarchy/mouse-first.jsonfiles keep working. SeeCHANGELOG.mdfor the user-facing list.Architecture
Service.qml, andBarWidget.qmlonly renders app icons.src/lua/mousefirst.lua, loaded withhyprctl eval). It reports the active window, the monitor layout (with reserved space) and the left-button state ascustom>>mousefirst>>…events. The shell no longer polls or spawns processes. The module is reinstalled afterhyprctl reload, and a generation token keeps a stale shell instance from stopping a newer one's module.src/WindowStates.qml): free / snapped / maximized, plus restore geometry. It replaces the old size caches and the browser-specific magic numbers.src/DragController.qml). Drags work across monitors, restore snapped or maximized windows under the cursor, and pause window animations while dragging.src/js/geometry.js+tests/).src/js/hypr.js): every window-targeting command goes through a single place that validates the address.qs.Commons. Config throughFileView/JsonAdapter(atomic writes, live reload).Bugs fixed
hyprctlpolling every 120 ms, plus a 300 ms settle lock.install.shcreated a stray$tmp_cfgfile and overwrotelooknfeel.lua.Removed: the
bin/*Python/Bash helpers,install.sh(replaced byscripts/dev-link.shfor development) and the duplicated screenshot.New: a language selector, and IPC actions for keybinds (
qs -p /usr/share/omarchy/shell ipc call mousefirst snap left, …), includingstatusfor bug reports.Test plan
Automated / scripted (done):
node --test tests/: 14 geometry tests pass.luac -p src/lua/mousefirst.luaspecial:minimized, focus moves to the next window, origin saved),restoreLast(window returns to its origin workspace), Hyprland-maximized window restored.mouse-first.jsonis edited by hand; the module and rule survivehyprctl reload.Manual, needs a real mouse:
omarchy restart shellkeeps settings and minimized origins.🤖 Generated with Claude Code