Canonical page: https://lattices.dev/lattices/docs/tiling-reference

# Tiling Reference

Complete reference for Lattices window tiling — positions, grids, execution paths, and voice interpretation.

## Position System

Every tile position is a cell in a **cols × rows** grid, expressed as fractional `(x, y, w, h)` of the screen's visible area (excluding menu bar and dock).

### Named Positions

All valid position strings that `TilePosition` accepts:

| Position string | Grid | Cell (col, row) | Description |
|---|---|---|---|
| `maximize` | 1×1 | full | Full screen (100% × 100%) |
| `center` | — | — | Centered floating (80% × 80%, offset 10%/10%) |
| **Halves (2×1, full height)** | | | |
| `left` | 2×1 | 0,0 | Left 50% |
| `right` | 2×1 | 1,0 | Right 50% |
| **Halves (1×2, full width)** | | | |
| `top` | 1×2 | 0,0 | Top 50% |
| `bottom` | 1×2 | 0,1 | Bottom 50% |
| **Quarters (2×2)** | | | |
| `top-left` | 2×2 | 0,0 | Top-left 25% |
| `top-right` | 2×2 | 1,0 | Top-right 25% |
| `bottom-left` | 2×2 | 0,1 | Bottom-left 25% |
| `bottom-right` | 2×2 | 1,1 | Bottom-right 25% |
| **Thirds (3×1, full height)** | | | |
| `left-third` | 3×1 | 0,0 | Left 33% column |
| `center-third` | 3×1 | 1,0 | Center 33% column |
| `right-third` | 3×1 | 2,0 | Right 33% column |
| **Sixths (3×2)** | | | |
| `top-left-third` | 3×2 | 0,0 | Top-left sixth |
| `top-center-third` | 3×2 | 1,0 | Top-center sixth |
| `top-right-third` | 3×2 | 2,0 | Top-right sixth |
| `bottom-left-third` | 3×2 | 0,1 | Bottom-left sixth |
| `bottom-center-third` | 3×2 | 1,1 | Bottom-center sixth |
| `bottom-right-third` | 3×2 | 2,1 | Bottom-right sixth |
| **Fourths (4×1, full height)** | | | |
| `first-fourth` | 4×1 | 0,0 | Leftmost 25% column |
| `second-fourth` | 4×1 | 1,0 | Second 25% column |
| `third-fourth` | 4×1 | 2,0 | Third 25% column |
| `last-fourth` | 4×1 | 3,0 | Rightmost 25% column |
| **Eighths (4×2)** | | | |
| `top-first-fourth` | 4×2 | 0,0 | Top row, 1st column |
| `top-second-fourth` | 4×2 | 1,0 | Top row, 2nd column |
| `top-third-fourth` | 4×2 | 2,0 | Top row, 3rd column |
| `top-last-fourth` | 4×2 | 3,0 | Top row, 4th column |
| `bottom-first-fourth` | 4×2 | 0,1 | Bottom row, 1st column |
| `bottom-second-fourth` | 4×2 | 1,1 | Bottom row, 2nd column |
| `bottom-third-fourth` | 4×2 | 2,1 | Bottom row, 3rd column |
| `bottom-last-fourth` | 4×2 | 3,1 | Bottom row, 4th column |
| **Horizontal thirds (1×3)** | | | |
| `top-third` | 1×3 | 0,0 | Top 33% row |
| `middle-third` | 1×3 | 0,1 | Middle 33% row |
| `bottom-third` | 1×3 | 0,2 | Bottom 33% row |
| **Edge quarters** | | | |
| `left-quarter` | 4×1 | 0,0 | Leftmost 25% column |
| `right-quarter` | 4×1 | 3,0 | Rightmost 25% column |
| `top-quarter` | 1×4 | 0,0 | Top 25% row |
| `bottom-quarter` | 1×4 | 0,3 | Bottom 25% row |

### Custom Grid Syntax

For arbitrary grids: compact `CxR:C,R` or canonical `grid:CxR:C,R`

- `C` = total columns, `R` = total rows
- Compact `CxR:C,R` starts at 1 from the top-left
- Canonical `grid:CxR:C,R` starts at 0 for API/wire compatibility
- Example: `5x3:3,2` or `grid:5x3:2,1` = center cell of a 5×3 grid
- Spans can use two inclusive corners, such as `4x4:1,1-2,2`

Parsed by `PlacementSpec` / `parseGridString()` into fractional `(x, y, w, h)`.

### App Shortcut Defaults

The menu bar app exposes tiling shortcuts in Settings > Shortcuts. Useful
defaults include:

- `Ctrl+Option+G`: show the 4x4 grid placement target for the frontmost window.
- `Ctrl+Option+V`: move the frontmost window into the least-occupied cell of a
  3x2 grid on its current screen.
- `Ctrl+Option+Space`: open the command bar; type `/tile 3x2:3,2` or another
  placement string to place the captured frontmost window precisely.
- `Ctrl+Option` + mouse: a small HUD sits at the cursor. Stay in the inner
  center and release to cancel; move a little (still in the center cell) for
  maximize; move into a sector for that tile. Release Ctrl+Option to apply.
  Settings → Input Controls can switch Loop ring vs Lattices 3×3 matrix. A
  Ctrl+Option key chord still wins if you press one during the hold.
  Command-drag snap keeps large edge/corner drop zones.

### Placement Contract

Placement strings are convenient at the boundary, but the daemon uses a
typed placement model internally:

- named tile positions
- arbitrary grid cells
- raw fractional rectangles

That is what keeps CLI, daemon, voice, and hands-off execution aligned.

## Drag Snap Zones

The menu bar app can also use placement specs as drag-to-snap targets.
When you drag a window, Lattices shows faint landing zones plus a live
preview of the resulting frame. Releasing over a zone tiles the dragged
window to that placement. Hold `Command` while dragging to reveal snap
mode, and release `Command` to drop back to a normal free drag without
ending the gesture.

The recommended agent-owned config lives in `~/.lattices/snap-zones.json`:

```json
{
  "enabled": true,
  "modifier": "command",
  "zoneOpacity": 0.08,
  "highlightOpacity": 0.18,
  "previewOpacity": 0.14,
  "rules": [
    {
      "id": "left-edge",
      "label": "Left",
      "placement": "left",
      "trigger": { "x": 0.0, "y": 0.18, "w": 0.12, "h": 0.64 },
      "priority": 10
    },
    {
      "id": "notes-rail",
      "label": "Notes",
      "placement": { "x": 0.68, "y": 0.0, "w": 0.32, "h": 1.0 },
      "trigger": { "x": 0.88, "y": 0.18, "w": 0.12, "h": 0.64 },
      "priority": 30
    }
  ]
}
```

Notes:

- `rules` is the preferred list key. `zones` is still accepted for backward compatibility.
- `modifier` accepts `command`, `option`, `control`, or `shift`.
- `placement` can be a named placement/preset string or raw fractions.
- `trigger` uses normalized `(x, y, w, h)` fractions of the screen's
  visible area, with `y = 0` at the top.
- `priority` breaks ties when trigger regions overlap.
- `trigger` can also be a named placement or preset string if you want
  the trigger region itself to reuse an existing tile definition.
- The older `~/.lattices/grid.json` `snapZones` section still works, but
  `~/.lattices/snap-zones.json` is the cleaner file for agents to edit.

## Execution Paths

The old split-brain tiling logic has been collapsed toward a shared path.
The canonical mutation is now:

```json
{ "method": "windows.place", "params": { "placement": "left" } }
```

All higher-level surfaces should compile into the same placement model:

- **Daemon / CLI**: `windows.place` is the canonical mutation
- **Compatibility**: `window.tile` maps to `windows.place`
- **Voice / hands-off**: parse natural language, then emit a placement spec
- **HUD**: still exposes a smaller shortcut set, but should target the same placement executor

The important change is that placement resolution now happens through
`PlacementSpec`, not through separate ad hoc parsers per surface.

## Frame Calculation

All paths eventually call one of:

1. **`WindowTiler.tileFrame(for:on:)`** — takes a `TilePosition` + `NSScreen`, returns a `CGRect` in AX coordinates (origin = top-left of primary display)
2. **`WindowTiler.tileFrame(fractions:inDisplay:)`** — takes raw `(x, y, w, h)` fractions + display rect

The math:
```
visible = screen.visibleFrame  (excludes menu bar + dock)
primaryH = primary screen height
axTop = primaryH - visible.maxY  (flip from AppKit bottom-left to AX top-left)

frame.x = visible.x + visible.width × fx
frame.y = axTop + visible.height × fy
frame.w = visible.width × fw
frame.h = visible.height × fh
```

## Window Targeting

The `tile_window` intent resolves the target window in this priority:

1. **`session`** slot → `LatticesApi.window.place` / `window.tile` compatibility wrapper
2. **`wid`** slot → `DesktopModel.shared.windows[wid]` (direct window ID lookup)
3. **`app`** slot → first matching window by `localizedCaseInsensitiveContains`, excluding recently-tiled windows (prevents double-matching in batch commands like "Chrome left, Chrome right")
4. **No target** → tiles the frontmost window

### HandsOff-specific targeting

The system prompt instructs the LLM to always use `wid` from the desktop snapshot, never `app`. This avoids ambiguity when multiple windows of the same app exist. In speech, the LLM says the app name; in the JSON action, it uses the wid.

## Common Layouts (multi-action)

These are composed from multiple `tile_window` actions:

| Layout | Actions |
|---|---|
| Split screen | left + right |
| Stack | top + bottom |
| Thirds | left-third + center-third + right-third |
| Quadrants | top-left + top-right + bottom-left + bottom-right |
| Six-up (3×2) | All six `*-*-third` positions |
| Eight-up (4×2) | All eight `*-*-fourth` positions |
| Distribute | Single `distribute` intent (auto-grid) |

CLI shortcuts compile into the same distributor:

- `lats tile family` → smart-grid the frontmost app's visible windows
- `lats distribute iTerm2 right` → smart-grid visible iTerm windows inside the right half

## HandsOff Smart Distribution

When the LLM sends multiple `tile_window` actions targeting the **same position**, `HandsOffSession.distributeTileActions()` subdivides:

- 2+ windows → "left" becomes top-left, left, bottom-left
- 2+ windows → "right" becomes top-right, right, bottom-right
- 2+ windows → "maximize" fans out to quadrants then halves

## Guardrails

- **Typed placement validation**: invalid placement strings or objects are rejected at the daemon boundary.
- **Recently-tiled dedup**: `IntentEngine.recentlyTiledWids` prevents the same window from being matched twice within 2 seconds during batch operations.
- **Compatibility wrappers**: `window.tile` still works, but routes through the same placement machinery.

## Current Gaps

1. **Voice extraction still needs to catch up**: the canonical executor understands horizontal thirds and edge quarters, but the local voice resolver still needs broader phrase coverage.
2. **HUD coverage is narrower than the executor**: keyboard tiling exposes a small subset of the full placement vocabulary.
3. **Optimization and layer actions are still wrapper-level**: `spaces.optimize` and `layers.activate` are now stable action IDs, but they currently wrap existing distributor and layer-switching behavior rather than a full planner.
