Display Layout Manager is a desktop tool for arranging multiple monitors visually, applying saved display layouts to the operating system, and launching workspace actions attached to those profiles.
The current implementation is a Tauri v2 desktop app with a React, TypeScript, Zustand, React Flow, Tailwind frontend and a Rust display engine. The working native target is macOS. Windows and Linux X11 support are part of the planned Alpha/Beta architecture in PLAN.md, but native adapters for those platforms are not complete in this repo yet.
| Area | Status |
|---|---|
| macOS display detection | Implemented |
| Visual display canvas | Implemented |
| Layout profiles | Implemented |
| Profile Actions / workspace automation | Implemented for macOS profile apply |
| Apply positions to macOS | Implemented |
| Change primary display | Implemented through layout origin normalization |
| Change macOS scale/resolution modes | Implemented when CoreGraphics reports usable modes |
| Change macOS rotation | Experimental through displayplacer |
| Menu bar quick switching | Implemented |
| Automation rules | Implemented with display, time, app, launch, power, and Wi-Fi presets |
| Recovery banner | Implemented |
| Diagnostics export | Implemented |
| Windows native adapter | Planned |
| Linux X11 native adapter | Planned |
| Wayland support | Future |
The app queries active macOS displays through CoreGraphics and shows:
- Display id and stable MVP id
- Name
- Logical position
- Resolution
- Refresh rate when available
- Scale factor
- Rotation
- Primary display state
- Built-in versus external state
- Bounds
- Per-display capability flags
- Available macOS scale options when reported by the OS
Manufacturer, model, serial number, EDID metadata, and detailed connection type are intentionally limited in the current macOS MVP and may be null.
The center canvas is powered by React Flow and supports:
- Draggable monitor nodes
- Pan and zoom
- Mini map
- Grid background
- Snap to grid
- Multi-select through React Flow selection behavior
- Rotation preview on monitor nodes
- Dark and light mode through React Flow
colorMode - A canvas label showing the current OS layout, selected saved profile, dirty state, or actual recently applied profile
Dragging a display updates the in-memory layout draft. Press Apply to send the layout to macOS.
Profiles store named display arrangements. The profile panel supports:
- Add a new profile from the current draft
- Select and open a profile
- Save edits back into the selected profile
- Duplicate a profile
- Delete a profile
- Apply a profile
- Highlight the currently opened profile
Profiles persist to JSON in the app data directory.
Profiles can also store workspace actions. Profile actions run only after a profile layout applies successfully. Manual layout-only apply remains available when no saved profile is active.
Supported actions:
- Open a macOS
.appbundle - Open an
http,https, ormacappstoreURL - Pass one argument per line to opened apps
- Delay actions relative to the start of the action phase
- Place an opened app's front window on a selected monitor
- Gracefully request an app to quit by name
- Run a local
zshscript command when advanced scripts are enabled
Each action has an id, enabled flag, and optional conditions for platform, connected display, and display count. Unmatched conditions produce skipped action results rather than errors.
Scripts are disabled by default and marked as advanced. Window placement uses macOS System Events, so macOS may require Accessibility permission for Display Layout Manager in System Settings > Privacy & Security > Accessibility.
Arguments and window placement require a local installed .app bundle. App Store links open as URLs and do not launch the installed app unless macOS resolves that URL itself.
The right inspector shows the selected display and allows editing supported settings:
- Primary display
- Scale/resolution mode
- Rotation
Controls are capability-gated. Unsupported controls are disabled and show a short reason from the platform adapter.
The macOS adapter applies supported settings with native APIs where possible:
- Positions are applied with
CGBeginDisplayConfiguration,CGConfigureDisplayOrigin, andCGCompleteDisplayConfiguration. - Scale/resolution modes are applied with
CGDisplayCopyAllDisplayModes,CGDisplayCopyDisplayMode, andCGConfigureDisplayWithDisplayMode. - Primary display is represented by normalizing the selected primary display to origin
(0, 0). - Rotation is read-only in public CoreGraphics APIs, so the app supports experimental rotation only when
displayplaceris available.
Before applying a layout, the app persists the current OS layout as last-known-good. After a successful apply, it refreshes display state and creates a short recovery window.
Apple does not expose a public CoreGraphics setter for display rotation. To enable experimental rotation:
brew install displayplacerThe app searches for displayplacer in:
DISPLAYPLACER_PATH/opt/homebrew/bin/displayplacer/usr/local/bin/displayplacer/usr/bin/displayplacer- The current
PATH
Known displayplacer rotation-only errors containing res:0x0 and scaling:off are treated as non-fatal because the screen can still rotate successfully.
Automation rules apply saved workspace profiles from practical local context. Rules support preset triggers for:
- Display setup changes
- Local time schedules and time windows
- App/process opened, closed, or running state
- Display Layout Manager launch checks
- Power source
- Wi-Fi SSID context
Rules can add conditions for platform, connected displays, display count, internal/external displays, time windows, app running/not running, power source, and Wi-Fi SSID. Confirmation is the default. Per-rule auto-run is available only when explicitly selected, and cooldowns prevent repeated prompts or repeated automatic applies for unchanged context.
The native automation monitor checks local context every 15 seconds and emits matches to the UI. When multiple rules match, the app shows choices instead of preselecting one. Saved Profile Actions run when the matched profile applies successfully.
Local notifications are available for confirmation-required automation matches. They are enabled by default, appear even when the app is focused, and clicking the Open notification action focuses Display Layout Manager so the existing automation prompt can be reviewed. In macOS dev runs, Notification Center may list the sender under a system fallback app because the dev binary is not a bundled .app; packaged builds use Display Layout Manager's bundle identity.
After applying a layout, the app shows a 20-second recovery banner:
Keepclears the recovery state.Revertapplies the previous last-known-good layout.
If the app launches and a recovery state still exists, it offers the same recovery state to help recover from bad layouts.
The Diagnostics button exports a local JSON bundle and shows a toast when the download is triggered. The bundle includes:
- App version
- Generated timestamp
- Platform
- Redacted display snapshots
- Profile summaries
- Profile action counts, action types, and app names
- Automation rules, trigger/condition summaries, and recent events
- Current recovery state when present
Diagnostics hash display stable ids and Wi-Fi SSIDs, and do not export serial numbers, raw EDID, or script commands by default.
The Tauri menu bar/tray integration provides quick access to:
- Refresh displays
- Apply the current layout
- Apply saved profiles
The menu bar icon uses the app monitor icon and is configured in src-tauri/tauri.conf.json.
The app supports dark and light mode. The selected theme is saved in local storage and applied to:
- Shell UI
- Sidebars
- Inspector
- React Flow canvas
- Background grid
- Mini map
Runtime data is stored in the app data directory. On macOS this resolves through dirs::data_dir() and is usually:
~/Library/Application Support/Display Layout Manager
Files currently used:
profiles.jsonsettings.jsonlast-known-good-layout.jsonautomation-rules.jsonautomation-events.jsonrecovery-state.json
Install dependencies:
pnpm installRun the web preview with the mock backend:
pnpm devRun the native Tauri app:
pnpm tauri devBuild the frontend:
pnpm buildRun TypeScript and frontend tests:
pnpm testRun Rust tests:
cargo test --manifest-path src-tauri/Cargo.tomlBuild a debug app bundle:
pnpm tauri build --debugRecommended manual macOS checks:
- Launch with one display and confirm it renders correctly.
- Launch with two or more displays and confirm relative positions match macOS System Settings.
- Drag a display, apply the layout, and confirm the OS arrangement changes.
- Save, open, edit, duplicate, delete, and reapply profiles.
- Add Profile Actions, save a profile, apply it, and confirm action results appear.
- Pick
/Applications/Slack.appor another app and confirm native profile apply opens it. - Open an app with arguments, one argument per line.
- Add monitor-relative window placement and confirm macOS prompts for Accessibility permission if needed.
- Confirm close-app actions request a graceful quit.
- Confirm scripts are skipped by default, then run only after enabling the advanced Scripts toggle.
- Change primary display and confirm the main display moves to origin.
- Change scale/resolution on a display that reports multiple CoreGraphics modes.
- Install
displayplacer, rotate a display, and confirm both the OS and node preview update. - Export diagnostics and confirm a toast appears after the download starts.
- Use the menu bar item to apply profiles.
- Create an automation rule from the current setup and verify the confirmation prompt appears.
- Use recovery
KeepandRevertafter applying a layout.
- macOS rotation depends on
displayplacer; public CoreGraphics APIs only expose rotation reading. - Profile Actions are implemented natively for macOS first; Windows and Linux action executors are future adapter work.
- Window placement depends on macOS Accessibility permission and targets the front window of the selected app process.
- Script actions are local
zshcommands, disabled by default, and intentionally redacted from diagnostics. - Scale options depend on what CoreGraphics reports as usable for desktop GUI.
- Stable identity uses
macos-cg-{display_id}in the current macOS implementation, so robust EDID-based identity is still future work. - Windows and Linux X11 native adapters are planned but not complete.
- Wayland display management is future work because compositor support varies.
- Hotkeys, workspace presets, window management, per-monitor wallpapers, brightness control, cloud sync, and team sharing are future features described in
PLAN.md.
- Frontend app shell:
src/app/App.tsx - Canvas math and tests:
src/features/canvas/layoutMath.ts - Display state:
src/features/displays - Profile state:
src/features/profiles - Settings state:
src/features/settings - Automation, recovery, and diagnostics state:
src/features/betaStore.ts - Rust command surface:
src-tauri/src/commands/mod.rs - Rust display engine models:
src-tauri/src/display_engine/models.rs - Rust validation:
src-tauri/src/display_engine/validation.rs - Automation matching:
src-tauri/src/display_engine/automation.rs - Profile action execution:
src-tauri/src/profile_actions.rs - macOS adapter:
src-tauri/src/platform/macos/mod.rs - Persistence:
src-tauri/src/persistence - Menu bar integration:
src-tauri/src/tray.rs - App menu integration:
src-tauri/src/app_menu.rs
See PLAN.md for the full product and roadmap specification.