Conversation
Adds an opt-in "Double-tap modifier keys" preference. When enabled and the Move/Resize shortcut is a modifier-only combo: - double-tap the Resize modifier -> maximize the window under the cursor to the screen's visible frame; a second double-tap restores the previous frame - double-tap the Move modifier -> minimize the window to the Dock Detection lives in DoubleTapActionManager (NSEvent global + local monitors on .flagsChanged). A normal press-and-hold drag never registers: both taps must be short and close together, with no other key, no mouse button held, and no active SwiftShift drag in between. WindowSnapActionRunner performs the action, targeting the window under the cursor (falling back to the focused window) and honoring the ignored-apps list; it keeps a small history of pre-maximize frames for restore. WindowManager gains getFrame/setFrame/setMinimized/isAlive, an AppKit<->AX rect converter (axRect(fromAppKit:)), screenAXVisibleFrame(containing:), and getFocusedWindow(). Wired into AppDelegate lifecycle, shortcut save/delete, and the post-system-event input-hook rebuild. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
|
@Liotou is attempting to deploy a commit to the Pablo Varela's projects Team on Vercel. A member of the Team first needs to authorize it. |
… shortcut handlePress/handleRelease guarded on !ShortcutsManager.hasActiveShortcut to avoid firing mid-drag, but holding the Move/Resize modifier arms that flag immediately (before any drag), so the guard tripped on every tap and the sequence never started. Drop that guard; instead reject the sequence only when a mouse button is held or the pointer drifts more than maxPointerDrift between the first tap and the trigger (covers keyboard-only move/resize, which tracks on mouse motion). Timing loosened slightly (0.30s tap / 0.40s gap) now that the drift check rules out drag false-positives. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds WindowFrameAnimator: interpolates the window frame from its current rect to the target over 0.18s with an ease-out cubic curve, ~90 steps/s, pushing each step through AXWindowWriter (background serial queue, latest-wins) so the main thread never blocks on AX IPC. For the gesture it also turns off the target app's AXEnhancedUserInterface (restored after), which is what keeps the motion smooth on Chromium/Electron windows. WindowSnapActionRunner now routes toggle-maximize / restore / center through the animator; a new trigger mid-glide cancels the running animation and starts a fresh one from the live frame, and reasons about the in-flight target when deciding maximize-vs-restore. Near-identical frames still snap directly. New WindowManager helpers: enhancedUIState(forAppOf:) / setEnhancedUI(_:forApp:). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New doubleTapActionsSwapped toggle (shown under "Double-tap modifier keys" only when that's on). Default: Resize modifier maximizes, Move modifier minimizes. When swapped: Move maximizes, Resize minimizes. loadConfigs() picks the maximize modifier from the preference; the parent row's subtitle reflects the current mapping. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
When com.apple.WindowManager EnableTiledWindowMargins is on (macOS 15+, System Settings → Desktop & Dock → Windows), maximize now insets the target by 8 pt on every side to match macOS's own tiling gap, instead of going edge-to-edge. Off / pre-Sequoia keeps the full visible frame. The already-maximized check compares against the same inset target. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
@codex pls review this PR and rewrite the description to be 250 words or less |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5666793fbb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Trimmed the description to ~250 words (kept What / Why / How / Scope / Tests / Try it). Leaving the code review itself to @codex as asked. 🤖 Addressed by Claude Code |
|
To use Codex here, create a Codex account and connect to github. |
- Cancel a pending double-tap sequence when an extra modifier joins the configured combo instead of treating it as a release. When one shortcut's flags are a subset of another's (e.g. Move=⌥, Resize=⌥⇧), pressing/releasing the extra modifier flipped the exact-flags match and was counted as a release-then-press, so releasing the still-held modifier could fire a double-tap that never happened. handleFlagsChanged now only calls handleRelease when a configured bit actually went up; a superset transition clears the tap state instead. - Cancel the maximize/restore animation before a real move/resize gesture starts (MouseTracker.prepareTracking). Both shared AXWindowWriter, so starting a drag mid-glide could apply animation frames to the newly tracked window, and the glide's endGesture() could detach the writer out from under the live drag. - WindowManager.isAlive now caps the AX messaging timeout at 0.15s (was the ~6s global default) and only accepts .success as "alive" (was "anything but .invalidUIElement", which kept records alive on .cannotComplete). Toggling maximize prunes up to 12 stored records synchronously on the main thread, so an unresponsive-but-not-terminated app could previously stall SwiftShift for many seconds. - MaximizedRecord now stores the frame it was maximized to. The "already maximized" check compares the live frame against that stored frame instead of a freshly recomputed target, so a target that moves after the fact (Dock, resolution, tiling-margin setting) no longer makes the window look "un-maximized" and silently discards the true restore frame. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
hey @Liotou, thanks for the PR. I tried it, and decided not to include this into Swift Shift. Reasons:
Feel free to keep running your own fork if you really want this. This is something I do with https://github.com/pablopunk/AeroSpace (since I want features the author does not want) |
What
Opt-in "Double-tap modifier keys" preference. With a modifier-only Move/Resize shortcut:
Targets the window under the cursor, falling back to the focused window; respects ignored apps. The maximize/restore transition is animated (~0.18s ease-out) and honors macOS's "Tiled windows have margins" setting (8pt edge gap when on).
Why
SwiftShift is mouse-driven. This adds a keyboard path for the two most common window actions with no new hotkey — it reuses the already-configured modifier (Resize → size, Move → sends the window away).
How
DoubleTapActionManagerwatches.flagsChangedwith a press/release state machine. A hold-drag never registers: both taps must be short (< 0.30s), close together (< 0.40s), no mouse button held, pointer barely moving.WindowSnapActionRunnerperforms the action, keeping a bounded history of pre-maximize frames for restore.WindowFrameAnimatorinterpolates the frame through the existingAXWindowWriterqueue and disablesAXEnhancedUserInterfacefor smooth motion on Electron apps. NewWindowManagerhelpers cover frame get/set, minimize, geometry, and tiling margins. Wired into lifecycle and shortcut save/delete likeMouseChordActionManager.Only modifier-only shortcuts are supported. "Maximize" = the visible frame, not native fullscreen.
Tests
xcodebuild testgreen, 53 tests including new coordinate-conversion tests. Timing and animation verified manually.Try it
Enable the preference, set Move/Resize to modifier-only shortcuts, hover a window, double-tap either modifier.
🤖 Generated with Claude Code