Complete examples
These examples demonstrate how to combine Shoji’s building blocks for practical workflows.
Developer setup
A complete configuration for software development with multiple layout options.
-- ~/.hammerspoon/init.luahs.loadSpoon("Shoji")
local shoji = spoon.Shoji
-- Access building blockslocal Modifiers = shoji.Modifierslocal Compose = shoji.Combinators.Compose
local Tall = shoji.Layouts.Talllocal Wide = shoji.Layouts.Widelocal Grid = shoji.Layouts.Gridlocal BSP = shoji.Layouts.BSPlocal Column = shoji.Layouts.Columnlocal Fullscreen = shoji.Layouts.Fullscreen
-------------------------------------------------------------------------- Modified layouts------------------------------------------------------------------------
-- Magnified tall: focused window gets 1.4x largerlocal MagnifiedTall = Modifiers.wrap( Tall, Modifiers.Magnifier.create({ ratio = 1.4, reflow = true }))
-- Mirrored tall: master on right instead of leftlocal TallRight = Modifiers.wrap( Tall, Modifiers.Mirror.horizontal(), { name = "tall-right", displayName = "Tall (Right)" })
-------------------------------------------------------------------------- Composed layouts------------------------------------------------------------------------
-- Developer: main editor (65%), reference stack (35%)local Developer = Compose.horizontal({ { ratio = 0.65, layout = Tall, count = 1 }, { ratio = 0.35, layout = Column },}, { name = "developer", displayName = "Developer",})
-- IDE: editor (60%), grid + terminal (40%)local IDE = Compose.horizontal({ { ratio = 0.6, layout = Tall, count = 2 }, { ratio = 0.4, layout = Compose.vertical({ { ratio = 0.5, layout = Grid, count = 2 }, { ratio = 0.5, layout = Fullscreen }, })},}, { name = "ide", displayName = "IDE Layout",})
-- Presentation: main content (85%), notes strip (15%)local Presentation = Compose.vertical({ { ratio = 0.85, layout = Fullscreen, count = 1 }, { ratio = 0.15, layout = Column },}, { name = "presentation", displayName = "Presentation",})
-------------------------------------------------------------------------- Configuration------------------------------------------------------------------------
shoji:start({ layouts = { MagnifiedTall, TallRight, Developer, IDE, Presentation, }, enabled_layouts = { "tall", "developer", "ide", "fullscreen", }, gap_outer = 8, gap_inner = 8, retile_on_focus = true, -- Required for magnifier})
shoji:bindHotkeys({ -- Layout cycling cycle_layout = { { "cmd", "ctrl", "alt" }, "space" },
-- Window focus focus_next = { { "ctrl", "alt" }, "'" }, focus_prev = { { "ctrl", "alt" }, ";" },
-- Window swapping swap_main = { { "cmd", "ctrl", "alt" }, "return" },
-- Layout adjustments increase_main_ratio = { { "ctrl", "alt" }, "." }, decrease_main_ratio = { { "ctrl", "alt" }, "," },
-- Float toggle toggle_float = { { "ctrl", "alt", "shift" }, "t" },})Keybindings summary
| Keys | Action |
|---|---|
cmd+ctrl+alt+space | Cycle layouts |
ctrl+alt+' | Focus next window |
ctrl+alt+; | Focus previous window |
cmd+ctrl+alt+return | Swap with main |
ctrl+alt+. | Expand main area |
ctrl+alt+, | Shrink main area |
ctrl+alt+shift+t | Toggle floating |
Ultrawide monitor setup
Extra gaps and centered layouts work well on ultrawide displays.
hs.loadSpoon("Shoji")
local shoji = spoon.Shojilocal Modifiers = shoji.Modifierslocal Tall = shoji.Layouts.Talllocal BSP = shoji.Layouts.BSP
-- Centered at 85% with extra gapslocal CenteredTall = Modifiers.chain(Tall, { Modifiers.Centered.ratio(0.85), Modifiers.Gaps.add({ outer = 20, inner = 12 }),}, { name = "centered-tall", displayName = "Centered Tall",})
-- BSP with breathing roomlocal SpacedBSP = Modifiers.chain(BSP, { Modifiers.Gaps.add({ outer = 24, inner = 8 }),}, { name = "spaced-bsp", displayName = "Spaced BSP", preserveActions = true, -- Keep BSP resize actions})
shoji:start({ layouts = { CenteredTall, SpacedBSP }, enabled_layouts = { "centered-tall", "spaced-bsp" }, gap_outer = 16, gap_inner = 8,})Minimal configuration
A simple setup with just the essentials.
hs.loadSpoon("Shoji")
local shoji = spoon.Shojilocal Modifiers = shoji.Modifierslocal Tall = shoji.Layouts.Tall
-- Tall with master on rightlocal TallRight = Modifiers.wrap( Tall, Modifiers.Mirror.horizontal(), { name = "tall-right", displayName = "Tall (Right)" })
shoji:start({ layouts = { TallRight }, enabled_layouts = { "tall", "tall-right", "fullscreen" }, gap_outer = 8, gap_inner = 8,})
shoji:bindHotkeys({ cycle_layout = { { "cmd", "ctrl", "alt" }, "space" }, swap_main = { { "cmd", "ctrl", "alt" }, "return" }, toggle_float = { { "ctrl", "alt", "shift" }, "t" },})Focus-aware magnification
Magnify the focused window while keeping others visible.
hs.loadSpoon("Shoji")
local shoji = spoon.Shojilocal Modifiers = shoji.Modifierslocal Tall = shoji.Layouts.Talllocal BSP = shoji.Layouts.BSP
local MagnifiedTall = Modifiers.wrap( Tall, Modifiers.Magnifier.create({ ratio = 1.5, reflow = true, minSize = 100, }))
local MagnifiedBSP = Modifiers.wrap( BSP, Modifiers.Magnifier.create({ ratio = 1.3 }), { preserveActions = true })
shoji:start({ layouts = { MagnifiedTall, MagnifiedBSP }, enabled_layouts = { "tall+magnify", "bsp+magnify" }, retile_on_focus = true, -- Essential for magnifier})Building blocks reference
Modifiers
| Modifier | Purpose |
|---|---|
Magnifier.create() | Enlarge focused window |
Mirror.horizontal() | Flip left/right |
Mirror.vertical() | Flip top/bottom |
Gaps.add() | Extra spacing |
Centered.ratio() | Scale and center |
Conditional.when() | Apply modifier conditionally |
Reorder.wrap() | Sort windows before layout |
Combinators
| Combinator | Purpose |
|---|---|
Compose.horizontal() | Split screen left-to-right |
Compose.vertical() | Split screen top-to-bottom |
Functions
| Function | Purpose |
|---|---|
Modifiers.wrap() | Apply one modifier |
Modifiers.chain() | Apply multiple modifiers |