panes is a cross-platform window management app written in Rust.
The first milestone is intentionally narrow:
- native tray/menu shell,
- keyboard/menu-triggered window commands,
- shared layout logic for macOS and Windows,
- platform adapters that find the active window, detect displays, and apply calculated window rectangles.
Drag-to-snap, richer preferences, and platform-specific polish come after the keyboard/menu path is working.
crates/panes-core: platform-neutral commands, geometry, layout calculations, and window history.crates/panes-platform: traits for native platform adapters.crates/panes-macos: macOS accessibility, display, tray, and hotkey adapter.crates/panes-windows: Windows display, window, tray, and hotkey adapter.crates/panes-app: app entry point.
Shared geometry uses logical desktop coordinates with the primary display's
lower-left corner as the origin, x increasing rightward, and y increasing
upward. Displays left of or below the primary can have negative coordinates.
Native adapters convert to and from their platform coordinate systems at the
panes-platform boundary.
panes reads an optional TOML config file at startup:
- macOS:
~/Library/Application Support/panes/config.toml - Windows:
%APPDATA%\panes\config.toml - Linux:
~/.config/panes/config.toml
A missing file means built-in defaults. See docs/config.example.toml for all supported keys: layout settings (gap, split ratios, almost-maximize size, resize step), per-command hotkey overrides, disabled commands, and launch at login.
Invalid individual values fall back to their defaults with a warning on stderr; an unparseable file falls back to full defaults with an error naming the file and problem.
macOS keeps the compact Control+Option namespace. Windows keeps directional commands on arrow keys and assigns commands that previously used letters or digits to function keys. This avoids consuming AltGr character input and does not rely on Windows-key shortcuts reserved by the operating system. Center Half is available from the tray menu but is unbound by default.
| Command | macOS | Windows |
|---|---|---|
| Left Half | Control+Alt+ArrowLeft |
Control+Alt+ArrowLeft |
| Right Half | Control+Alt+ArrowRight |
Control+Alt+ArrowRight |
| Top Half | Control+Alt+ArrowUp |
Control+Alt+ArrowUp |
| Bottom Half | Control+Alt+ArrowDown |
Control+Alt+ArrowDown |
| Top Left | Control+Alt+U |
Control+Shift+F1 |
| Top Right | Control+Alt+I |
Control+Shift+F2 |
| Bottom Left | Control+Alt+J |
Control+Shift+F3 |
| Bottom Right | Control+Alt+K |
Control+Shift+F4 |
| First Third | Control+Alt+Digit1 |
Control+Shift+F5 |
| Center Third | Control+Alt+Digit2 |
Control+Shift+F6 |
| Last Third | Control+Alt+Digit3 |
Control+Shift+F7 |
| First Two Thirds | Control+Alt+Digit4 |
Control+Shift+F8 |
| Center Two Thirds | Control+Alt+Digit5 |
Control+Shift+F9 |
| Last Two Thirds | Control+Alt+Digit6 |
Control+Shift+F10 |
| Maximize | Control+Alt+Enter |
Control+Alt+Enter |
| Almost Maximize | Control+Alt+A |
Control+Shift+F11 |
| Maximize Height | Control+Alt+H |
Control+Alt+Shift+F1 |
| Center | Control+Alt+C |
Control+Alt+Shift+F2 |
| Restore | Control+Alt+Backspace |
Control+Alt+Backspace |
| Move Left | Control+Alt+Shift+ArrowLeft |
Control+Alt+Shift+ArrowLeft |
| Move Right | Control+Alt+Shift+ArrowRight |
Control+Alt+Shift+ArrowRight |
| Move Up | Control+Alt+Shift+ArrowUp |
Control+Alt+Shift+ArrowUp |
| Move Down | Control+Alt+Shift+ArrowDown |
Control+Alt+Shift+ArrowDown |
| Grow | Control+Alt+Equal |
Control+Alt+Shift+F3 |
| Shrink | Control+Alt+Minus |
Control+Alt+Shift+F4 |
Configuration overrides use these same accelerator names on either platform.
Use the tray's Launch at Login checkbox to update both the platform login
registration and [general].launch-at-login in the config file. Panes checks
the operating-system state at startup and repairs a missing or stale
registration to match the saved preference.
On macOS 13 or later, launch at login uses the bundled app's system service.
Build and launch Panes.app; a bare cargo run executable cannot register as
a login item. If macOS requires approval, the tray identifies that state and
you can allow Panes in System Settings → General → Login Items.
On Windows, launch at login uses the current user's Run registry key. Moving
the portable executable leaves the old entry stale; launching Panes from its
new location repairs or removes that entry according to the saved preference.
Run a command once against the currently focused window by its stable command id:
panes exec left-half
panes exec --delay 500 top-rightThe optional delay is measured in milliseconds and gives you time to focus the target window after invoking the command from a terminal. List every supported id with:
panes exec --listAn exec invocation is a separate one-shot process. It reads the normal
layout configuration, applies one command, and exits nonzero with an error if
the command id, permission state, focused window, or native operation is
invalid. It does not share window history with the resident tray process, so
restore and repeated-command cycling require the resident process until a
future inter-process command channel is added.
Transient desktop states—no focused window, no saved restore rectangle, or an
unsupported/vanished window—are ignored in release builds and emitted as
structured event=command_failure diagnostics in development builds.
Permission, native API, and display-geometry failures are also logged in
release builds. Command failures do not show tray notifications because common
focus and window-state changes would make them noisy during normal use.
Restore history is intentionally conservative. It is discarded when a native window identifier is observed with different application/window metadata or when the connected display geometry changes. Restore rectangles are fitted to the current work area so stale coordinates cannot place a window offscreen.
The workspace requires Rust 1.85.0. rust-toolchain.toml pins that release and
installs rustfmt and Clippy through rustup. Keep its channel aligned with the
workspace rust-version when raising the minimum supported compiler.
Clone the repository, then run the complete local check set:
cargo fmt --all -- --check
cargo check --workspace --all-targets
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-targetsOn macOS, install the Xcode command-line tools, then start the menu-bar app with
cargo run -p panes-app. Building the app bundle additionally requires the
standard qlmanage, sips, iconutil, and plutil tools included with macOS.
On 64-bit Windows 10 or 11, install rustup with the MSVC toolchain and Visual
Studio Build Tools with the Windows SDK, then run cargo run -p panes-app from
PowerShell. The Windows SDK resource compiler embeds the application manifest
and version metadata in release builds.
Pull requests and pushes to main run the same formatting, check, lint, and
test gates on macOS arm64 and Windows x86_64. Successful jobs also upload
versioned platform packages. See docs/releasing.md for the
artifact contract and deferred distribution work.
Panes needs macOS Accessibility permission to inspect and move application windows. On the first launch, it requests access with the system prompt. While access is missing, the tray shows Grant Accessibility Permission…; choose it to open System Settings → Privacy & Security → Accessibility.
Panes checks for the grant in the background and becomes ready without a restart. The tray item changes to Accessibility Permission Granted when the process is trusted. Window commands also perform a non-prompting trust check, so using a command never repeats the system prompt.
For permission-flow testing, quit Panes and reset Accessibility consent:
# Reset all Accessibility grants (useful for `cargo run` development builds).
tccutil reset Accessibility
# Reset only the bundled app.
tccutil reset Accessibility io.github.martinbha.panesAccessibility trust is tied to the app's identity, signature, and location. The bundled app uses a stable ad-hoc designated requirement, so rebuilding it at the same location should preserve its grant. Running a bare development binary, moving or replacing the app, or changing its signature may require you to reset or re-grant access.
On macOS, create a double-clickable release app with:
scripts/bundle-macos.shThe command creates dist/Panes.app. It is an ad-hoc signed local build
intended for development and manual testing. The stable signing requirement
helps preserve Accessibility permission across rebuilds at the same location,
but the app is not Developer ID signed or notarized. Move it to /Applications
before granting Accessibility permission if you want to keep it there. Panes
runs as a menu-bar-only app, so it does not appear in the Dock.
Create the versioned ZIP used by CI with:
scripts/package-macos.shSee macOS Accessibility permission for the first-launch flow and development reset commands.
For an app intended to leave the development machine, sign and notarize the bundle with an Apple Developer certificate before distributing it.
On 64-bit Windows, build a release executable and versioned portable ZIP from PowerShell:
scripts/package-windows.ps1The package is written to dist and contains panes.exe plus the README. The
release executable is a tray application, carries version and DPI-awareness
metadata, and does not open a console window. It is currently unsigned and is
distributed as a portable ZIP; an installer is intentionally deferred until
the release and signing process is established.