A modular and powerful window management system for macOS that includes window tiling, application switching, and productivity tools.
- Grid-based Window Management: Organize windows in a customizable grid layout
- Screen-aware Layouts: Automatically detects screen size and orientation for optimal layouts
- Multiple Screen Support: Seamlessly manage windows across multiple displays
- Application Switching: Quick keyboard shortcuts for launching and toggling applications
- Pomodoro Timer: Built-in productivity timer with visual feedback
- Window Margins: Configurable spacing between windows and screen edges
- Focus Control: Quickly focus and cycle through windows in specific zones
- Smart Window Mapping: Automatically detect and place existing windows in zones
- Cross-Screen Navigation: Move windows and focus between screens with keyboard shortcuts
- Modular Architecture: Easily maintainable with centralized configuration
- Centralized Configuration: All settings in one config.lua file
~/.hammerspoon/
├── init.lua # Main initialization file
├── config.lua # Central configuration
└── modules/
├── pomodoor.lua # Pomodoro timer module
├── tiler.lua # Window management module
├── app_switcher.lua # Application switching module
├── lru_cache.lua # Least Recently Used cache implementation
└── window_memory.lua # Window position memory system
- Install Hammerspoon
- Clone this repository to
~/.hammerspoon/
- Restart Hammerspoon
The keyboard layout maps directly to screen positions:
y u i o p
h j k l ;
n m , . /
Each key corresponds to a specific zone on the screen grid:
y
: Top-left region cycling: full height of left column → top-left cellh
: Left side cycling: left two columns → left column → left three columnsn
: Bottom-left cycling: bottom-left cell → bottom half of left column
u
: Middle-top cycling: middle column → top half of middle column → top-middle cellj
: Middle cycling: middle-right columns → middle column → middle cellm
: Bottom-middle cycling: right-middle column → bottom half of middle column → bottom-middle cell
i
: Top-right cycling: right column → top half of right column → top-right cellk
: Right side cycling: two rightmost columns → right column → middle-right cell,
: Bottom-right cycling: bottom-right cell → bottom half of right columno
: Top-right cycling: top-right cell → top-right two cells widel
: Right side cycling: right column → right two columns → right half.
: Bottom-right cycling: bottom-right two cells wide → bottom-right cell
0
: Center cycling: center quarter → center two-thirds → full screen
Ctrl+Cmd+p
: Move current window to the next screenCtrl+Cmd+;
: Move current window to the previous screen
Shift+Ctrl+Cmd+[zone key]
: Focus on windows in that zone (cycles through windows)Shift+Ctrl+Cmd+p
: Move focus to next screenShift+Ctrl+Cmd+;
: Move focus to previous screen
Shift+Ctrl+[key]
: Launch or toggle application based on key bindings in config.luaShift+Ctrl+/
: Display help with keyboard shortcuts
Ctrl+Cmd+9
: Start pomodoro timerCtrl+Cmd+0
: Pause/reset pomodoro timerShift+Ctrl+Cmd+0
: Reset work count
Hyper+- (minus)
: Display window hintsHyper+= (equals)
: Launch Activity MonitorShift+Ctrl+Cmd+R
: Reload configuration
All customization is done through the config.lua
file, which serves as the single source of truth for all settings.
config.keys = {
mash = {"ctrl", "cmd"},
mash_app = {"shift", "ctrl"},
mash_shift = {"shift", "ctrl", "cmd"},
HYPER = {"shift", "ctrl", "alt", "cmd"}
}
config.app_switcher = {
-- Apps that need menu-based hiding
hide_workaround_apps = {'Arc'},
-- Apps that require exact mapping between launch name and display name
special_app_mappings = {
["bambustudio"] = "bambu studio" -- Launch name → Display name
},
-- Ambiguous app pairs that should not be considered matching
ambiguous_apps = {
{'notion', 'notion calendar'},
{'notion', 'notion mail'}
}
}
-- Application shortcuts
config.appCuts = {
q = 'BambuStudio',
w = 'Whatsapp',
e = 'Finder',
-- Add more apps here
}
config.tiler = {
debug = true,
modifier = {"ctrl", "cmd"},
-- Window margin settings
margins = {
enabled = true,
size = 5,
screen_edge = true
},
-- Screen detection configuration
screen_detection = {
-- Special screen name patterns and their preferred layouts
patterns = {
["DELL.*U32"] = { cols = 4, rows = 3 }, -- Dell 32-inch monitors
["LG.*QHD"] = { cols = 1, rows = 3 }, -- LG QHD in portrait mode
},
-- Size-based layouts (screen diagonal in inches)
sizes = {
large = { min = 27, layout = "4x3" }, -- 27" and larger - 4x3 grid
medium = { min = 24, max = 26.9, layout = "3x3" }, -- 24-26" - 3x3 grid
standard = { min = 20, max = 23.9, layout = "3x2" }, -- 20-23" - 3x2 grid
small = { max = 19.9, layout = "2x2" } -- Under 20" - 2x2 grid
},
-- Portrait mode layouts
portrait = {
large = { min = 23, layout = "1x3" }, -- 23" and larger in portrait - 1x3 grid
small = { max = 22.9, layout = "1x2" } -- Under 23" in portrait - 1x2 grid
}
},
-- Custom layouts for specific screens (exact name match)
layouts = {
custom = {
["DELL U3223QE"] = { cols = 4, rows = 3 },
["LG IPS QHD"] = { cols = 1, rows = 3 }
}
},
-- Default zone configurations
default_zone_configs = {
["y"] = {"a1:a2", "a1", "a1:b2"},
["h"] = {"a1:b3", "a1:a3", "a1:c3", "a2"},
-- More zone configurations...
},
-- Portrait mode zone configurations
portrait_zones = {
["y"] = {"a1", "a1:a2"},
["h"] = {"a2", "a1:a3"},
-- Additional settings...
}
}
config.pomodoro = {
enable_color_bar = true,
work_period_sec = 52 * 60, -- 52 minutes
rest_period_sec = 17 * 60, -- 17 minutes
indicator_height = 0.2,
indicator_alpha = 0.3,
indicator_in_all_spaces = true,
color_time_remaining = hs.drawing.color.green,
color_time_used = hs.drawing.color.red
}
The system uses a multi-stage process to detect the appropriate layout for each screen:
- Exact Name Match: Check if the screen name matches a layout in
layouts.custom
- Pattern Match: Check if the screen name matches any pattern in
screen_detection.patterns
- Size Detection: Extract screen size from name and match against
sizes
orportrait
settings - Resolution Fallback: If other methods fail, determine layout based on resolution
For custom monitor layouts, you can add new entries to the pattern detection:
config.tiler.screen_detection.patterns["YOUR_PATTERN"] = { cols = 3, rows = 2 }
The tiler uses a grid coordinate system with alphabetic columns and numeric rows:
a b c d
+----+----+----+----+
1 | a1 | b1 | c1 | d1 |
+----+----+----+----+
2 | a2 | b2 | c2 | d2 |
+----+----+----+----+
3 | a3 | b3 | c3 | d3 |
+----+----+----+----+
You can define regions using:
- String coordinates like
"a1"
(single cell) or"a1:b2"
(rectangle from a1 to b2) - Named positions like
"center"
,"left-half"
,"top-half"
, etc. - Table coordinates like
{1,1,2,2}
for programmatic definitions
If windows are not positioning correctly:
- Enable Debug Mode: Set
config.tiler.debug = true
in config.lua - Check Screen Detection: See if your screen is being correctly identified in the Hammerspoon console
- Add Pattern Match: Add a pattern for your monitor to
screen_detection.patterns
- Reload Configuration: Use
Cmd+Ctrl+Shift+R
- Check Console: Examine the Hammerspoon console for error messages
- Focus on Zone keyboard shortcut (Switch to the topmost window in a zone and cycle through them)
- Cross-screen focus navigation (Move focus between screens with keyboard shortcuts)
- Smart window mapping (Automatically detect and map existing windows to appropriate zones)
- Modular architecture (Split functionality into separate modules with central configuration)
- Centralized configuration (All settings in one config.lua file)
- Application-aware layouts (Save preferred zones for specific applications)
- Automatically arrange windows based on predefined layouts
- Dynamic row and column resizing
- Zen mode (minimize all windows other than the active one)
- Automatic window resizing based on content
- Save and load window layouts
- Support for Mac spaces
- Window stacking within zones (keep multiple windows in a single zone and cycle through them)
- Grid visualization overlay (display the grid layout temporarily when positioning windows)
- Mouse-based zone selection (Shift+drag to select a custom zone)
- Zone presets (quickly switch between different zone layouts)
- Built with Hammerspoon
- Window Tiler inspired by grid layout systems
- Pomodoro timer based on the Pomodoro Technique®