par-term 0.30.10

Cross-platform GPU-accelerated terminal emulator with inline graphics support (Sixel, iTerm2, Kitty)
# Window Arrangements

Save and restore complete window layouts, including window positions, sizes, tab working directories, and active tab indices. This feature provides iTerm2-style arrangement management for par-term.

## Table of Contents
- [Overview]#overview
- [Saving Arrangements]#saving-arrangements
  - [Via Settings UI]#via-settings-ui
  - [Via View Menu]#via-view-menu
  - [Via Keybinding]#via-keybinding
  - [Duplicate Name Handling]#duplicate-name-handling
- [Restoring Arrangements]#restoring-arrangements
  - [Via Settings UI]#via-settings-ui-1
  - [Via Keybinding]#via-keybinding-1
  - [Auto-Restore on Startup]#auto-restore-on-startup
- [Monitor Awareness]#monitor-awareness
  - [Relative Position Storage]#relative-position-storage
  - [Monitor Matching]#monitor-matching
  - [Position Clamping]#position-clamping
- [Managing Arrangements]#managing-arrangements
  - [Renaming]#renaming
  - [Deleting]#deleting
  - [Reordering]#reordering
- [Configuration]#configuration
  - [Config Options]#config-options
  - [Keybinding Actions]#keybinding-actions
  - [YAML Persistence]#yaml-persistence
- [Architecture]#architecture
- [Related Documentation]#related-documentation

## Overview

Window arrangements capture the full state of your workspace and allow you to restore it later. Each arrangement stores:

- All open windows and their positions and sizes
- The monitor each window belongs to
- All tabs within each window and their working directories
- Per-tab customizations: user-set tab names, custom tab colors, and custom tab icons
- The active (focused) tab index in each window

```mermaid
graph TD
    Capture[Capture Arrangement]
    Storage[YAML Storage]
    Restore[Restore Arrangement]

    Capture --> Windows[Window Positions & Sizes]
    Capture --> Monitors[Monitor Layout]
    Capture --> Tabs[Tab CWDs & Titles]
    Capture --> Props[Tab Colors, Icons & Names]
    Capture --> Active[Active Tab Indices]

    Windows --> Storage
    Monitors --> Storage
    Tabs --> Storage
    Props --> Storage
    Active --> Storage

    Storage --> Mapping[Monitor Mapping]
    Mapping --> Clamping[Position Clamping]
    Clamping --> Restore

    style Capture fill:#e65100,stroke:#ff9800,stroke-width:3px,color:#ffffff
    style Storage fill:#0d47a1,stroke:#2196f3,stroke-width:2px,color:#ffffff
    style Restore fill:#1b5e20,stroke:#4caf50,stroke-width:2px,color:#ffffff
    style Windows fill:#37474f,stroke:#78909c,stroke-width:2px,color:#ffffff
    style Monitors fill:#37474f,stroke:#78909c,stroke-width:2px,color:#ffffff
    style Tabs fill:#37474f,stroke:#78909c,stroke-width:2px,color:#ffffff
    style Props fill:#37474f,stroke:#78909c,stroke-width:2px,color:#ffffff
    style Active fill:#37474f,stroke:#78909c,stroke-width:2px,color:#ffffff
    style Mapping fill:#4a148c,stroke:#9c27b0,stroke-width:2px,color:#ffffff
    style Clamping fill:#880e4f,stroke:#c2185b,stroke-width:2px,color:#ffffff
```

## tmux Session Capture and Restore

When saving an arrangement, par-term captures the active tmux session name for each window (if connected in control mode). When restoring the arrangement, each window automatically reconnects to its saved tmux session.

**Capture**: The session name stored in `window_state.tmux_state.tmux_session_name` is written to the arrangement YAML as `tmux_session_name` on the `WindowSnapshot`. Only visible tabs are captured — the hidden control-mode gateway tab is excluded from the tab list because it is a transient PTY connection, not a user tab.

**Restore**: On restore, if `tmux_session_name` is set and `tmux_enabled = true`, a single empty gateway shell tab is created and `initiate_tmux_gateway(Some(session_name))` is called. The real tmux window tabs are then re-created by the tmux session via normal layout-change notifications — they are never reconstructed from the saved tab list. Failures to connect are logged as warnings but do not prevent the window from opening.

**Why only one shell tab**: The saved tab list contains the tmux display tabs from the previous session. If those CWDs were used to spawn new shell processes on restore, you would end up with duplicate tabs (the ghost shells plus the real tmux windows). By passing only one empty CWD, the gateway shell is the only process spawned; the rest of the tab bar is populated by tmux itself.

**Normal mode sessions**: Sessions connected in Normal mode (plain tmux in PTY) are not captured — par-term has no visibility into their session name. Only control-mode sessions are preserved across arrangement save/restore.

## Saving Arrangements

Saving an arrangement captures a snapshot of every open window, including its position, size, monitor, tabs, and active tab index.

### Via Settings UI

1. Open Settings (press `F12` or use the View menu)
2. Navigate to the **Window** tab
3. Scroll to the **Save Current Layout** section
4. Enter a name for the arrangement and click **Save**

### Via View Menu

Select **View > Save Window Arrangement...** from the menu bar. This opens the Settings window to the Window tab (Arrangements section) where you can enter a name and save.

### Via Keybinding

Add a keybinding for the `save_arrangement` action in your `config.yaml`:

```yaml
keybindings:
  - key: "Ctrl+Shift+S"
    action: "save_arrangement"
```

This opens the Settings window to the Window tab (Arrangements section) for naming and saving.

### Duplicate Name Handling

When saving an arrangement with a name that already exists (matched case-insensitively), par-term displays a confirmation dialog asking whether to overwrite the existing arrangement. Choosing **Overwrite** replaces the old arrangement with the current layout. Choosing **Cancel** returns to the name input without saving.

## Restoring Arrangements

Restoring an arrangement closes all current windows and recreates the saved layout. Each window is placed on the correct monitor with its saved position, size, tabs, and active tab index. Per-tab customizations -- user-set names, custom colors, and custom icons -- are faithfully reapplied to the correct tabs in each window.

In multi-window layouts, par-term uses the exact `WindowId` returned from each window creation to apply tab properties, ensuring that custom colors, icons, and user titles are never misapplied to the wrong window.

> **Warning:** Restoring an arrangement replaces all current windows. Any unsaved state in open terminals is lost.

### Via Settings UI

1. Open Settings (press `F12` or use the View menu)
2. Navigate to the **Window** tab
3. Scroll to the **Saved Arrangements** section
4. Find the arrangement to restore and click **Restore** next to its name
5. Confirm the restore in the dialog that appears

### Via Keybinding

Add a keybinding using the `restore_arrangement:<name>` action format:

```yaml
keybindings:
  - key: "Ctrl+Shift+R"
    action: "restore_arrangement:Work Setup"
```

The arrangement name after the colon must match a saved arrangement name (matched case-insensitively). This restores the arrangement immediately without a confirmation dialog.

### Auto-Restore on Startup

Configure an arrangement to restore automatically every time par-term launches:

```yaml
auto_restore_arrangement: "Work Setup"
```

**Via Settings UI:**

1. Open Settings > **Window** tab
2. Scroll to the **Auto-Restore on Startup** section
3. Select an arrangement from the dropdown (or **None (disabled)** to turn off auto-restore)

When auto-restore is enabled, par-term skips normal window creation and instead restores the named arrangement. If the arrangement no longer exists, par-term falls back to creating a default window.

## Monitor Awareness

Arrangements are designed to work reliably across different monitor configurations, including when monitors are added, removed, or rearranged.

### Relative Position Storage

Window positions are stored in scale-factor-independent **logical pixels** relative to their monitor's origin. This ensures arrangements restore correctly across monitors with different DPI values (e.g., a Retina display alongside a standard monitor). Positions and sizes use `LogicalPosition`/`LogicalSize` so that winit applies the correct per-monitor DPI conversion on restore.

For example, a window at screen coordinates `(2660, 200)` on a monitor whose origin is `(2560, 0)` is stored as relative position `(100, 200)` in logical pixels.

### Monitor Matching

When restoring an arrangement, par-term maps saved monitors to currently available monitors using a priority-based strategy:

```mermaid
graph TD
    Start[Restore Window]
    NameMatch{Match by<br/>Monitor Name?}
    IndexMatch{Match by<br/>Monitor Index?}
    Primary[Use Primary<br/>Monitor]
    Placed[Window Placed]

    Start --> NameMatch
    NameMatch -->|Yes| Placed
    NameMatch -->|No| IndexMatch
    IndexMatch -->|Yes| Placed
    IndexMatch -->|No| Primary
    Primary --> Placed

    style Start fill:#e65100,stroke:#ff9800,stroke-width:3px,color:#ffffff
    style NameMatch fill:#ff6f00,stroke:#ffa726,stroke-width:2px,color:#ffffff
    style IndexMatch fill:#ff6f00,stroke:#ffa726,stroke-width:2px,color:#ffffff
    style Primary fill:#0d47a1,stroke:#2196f3,stroke-width:2px,color:#ffffff
    style Placed fill:#1b5e20,stroke:#4caf50,stroke-width:2px,color:#ffffff
```

1. **Match by name**: The monitor's display name (e.g., "DELL U2720Q") is compared against the saved name
2. **Match by index**: If no name match is found, the saved monitor index is used if it is within bounds
3. **Primary fallback**: If neither name nor index matches, the window is placed on the primary monitor (index 0)

### Position Clamping

After computing the target position, par-term clamps the window to ensure it remains visible on the target monitor. This handles scenarios where the monitor resolution has changed or the saved position would place the window off-screen.

- Window width and height are clamped to not exceed the monitor dimensions
- At least 100 pixels of the window must remain visible on each axis
- Windows that would be entirely off-screen are pulled back into the visible area

## Managing Arrangements

The **Arrangements** sections within the **Window** tab in Settings provide controls for managing saved arrangements.

### Renaming

Click **Rename** next to an arrangement to change its name. Enter the new name and confirm. If the arrangement is referenced by `auto_restore_arrangement` in the config, update that setting to match the new name.

### Deleting

Click **Delete** next to an arrangement. A confirmation dialog prevents accidental deletion. Deletion is permanent and cannot be undone.

### Reordering

Use the **up arrow** and **down arrow** buttons next to each arrangement to change the display order. The order is persisted to the arrangements file automatically.

## Configuration

### Config Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `auto_restore_arrangement` | `string` or `null` | `null` | Name of arrangement to auto-restore on startup. Set to `null` or omit to disable |

```yaml
# Auto-restore a layout on startup
auto_restore_arrangement: "Work Setup"
```

### Keybinding Actions

Two keybinding actions are available for arrangements:

| Action | Description |
|--------|-------------|
| `save_arrangement` | Opens the Settings Window tab (Arrangements section) for saving |
| `restore_arrangement:<name>` | Restores the named arrangement immediately |

```yaml
keybindings:
  - key: "Ctrl+Shift+S"
    action: "save_arrangement"
  - key: "Ctrl+Shift+1"
    action: "restore_arrangement:Work Setup"
  - key: "Ctrl+Shift+2"
    action: "restore_arrangement:Home Setup"
```

### YAML Persistence

Arrangements are stored in a dedicated YAML file separate from the main configuration:

- **Linux/macOS**: `~/.config/par-term/arrangements.yaml`
- **Windows**: `%APPDATA%\par-term\arrangements.yaml`

The file contains a list of arrangement objects. Each arrangement stores its unique ID, display name, the monitor layout at capture time, and the full window/tab state. The file is created automatically when the first arrangement is saved, and the parent directory is created if it does not exist.

Example structure:

```yaml
- id: "550e8400-e29b-41d4-a716-446655440000"
  name: "Work Setup"
  monitor_layout:
    - name: "DELL U2720Q"
      index: 0
      position: [0, 0]
      size: [2560, 1440]
      scale_factor: 1.0
  windows:
    - monitor:
        name: "DELL U2720Q"
        index: 0
        position: [0, 0]
        size: [2560, 1440]
        scale_factor: 1.0
      position_relative: [100, 200]
      size: [800, 600]
      tmux_session_name: "work-session"
      tabs:
        - cwd: "/home/user/projects"
          title: "zsh"
          user_title: "Editor"
          custom_color: [255, 165, 0]
          custom_icon: "rocket"
        - cwd: "/home/user/logs"
          title: "zsh"
          user_title: null
          custom_color: null
          custom_icon: null
      active_tab_index: 0
  created_at: "2026-01-15T10:30:00Z"
  order: 0
```

## Architecture

The arrangements module is organized across several crates:

```mermaid
graph TD
    SettingsUIArr[arrangements.rs<br/>par-term-settings-ui<br/>Types & ArrangementManager]
    ConfigSnapshot[snapshot_types.rs<br/>par-term-config<br/>TabSnapshot shared type]
    Capture[capture.rs<br/>src/arrangements<br/>Capture Live State]
    Restore[restore.rs<br/>src/arrangements<br/>Monitor Mapping & Clamping]
    Storage[storage.rs<br/>src/arrangements<br/>YAML Persistence]
    Mod[mod.rs<br/>src/arrangements<br/>Re-exports from settings-ui]
    WinMgr[WindowManager<br/>Orchestration]
    SettingsUI[Window Tab<br/>Arrangements Section<br/>egui UI]
    Menu[View Menu<br/>Save Item]
    Input[Input Events<br/>Keybinding Actions]

    WinMgr --> Capture
    WinMgr --> Restore
    WinMgr --> Storage
    SettingsUI --> WinMgr
    Menu --> WinMgr
    Input --> WinMgr
    Mod --> Capture
    Mod --> Restore
    Mod --> Storage
    SettingsUIArr --> ConfigSnapshot
    Mod --> SettingsUIArr

    style SettingsUIArr fill:#e65100,stroke:#ff9800,stroke-width:3px,color:#ffffff
    style ConfigSnapshot fill:#880e4f,stroke:#c2185b,stroke-width:2px,color:#ffffff
    style Capture fill:#1b5e20,stroke:#4caf50,stroke-width:2px,color:#ffffff
    style Restore fill:#1b5e20,stroke:#4caf50,stroke-width:2px,color:#ffffff
    style Storage fill:#0d47a1,stroke:#2196f3,stroke-width:2px,color:#ffffff
    style Mod fill:#37474f,stroke:#78909c,stroke-width:2px,color:#ffffff
    style WinMgr fill:#4a148c,stroke:#9c27b0,stroke-width:2px,color:#ffffff
    style SettingsUI fill:#880e4f,stroke:#c2185b,stroke-width:2px,color:#ffffff
    style Menu fill:#37474f,stroke:#78909c,stroke-width:2px,color:#ffffff
    style Input fill:#37474f,stroke:#78909c,stroke-width:2px,color:#ffffff
```

**Key types in `par-term-settings-ui/src/arrangements.rs`:**

- `MonitorInfo`: Captures monitor name, index, position, size, and scale factor at save time
- `WindowSnapshot`: Stores a window's monitor, relative position, size, tabs, and active tab index
- `WindowArrangement`: A named collection of window snapshots with monitor layout and metadata
- `ArrangementManager`: Manages the collection of saved arrangements with ordering, lookup by name, and CRUD operations

**Shared type in `par-term-config/src/snapshot_types.rs`:**

- `TabSnapshot`: Stores a tab's working directory, title, and optional per-tab customizations (user-set name, custom color, custom icon). Shared between arrangements and session restore.

**Capture flow** (`src/arrangements/capture.rs`): Enumerates all monitors via the winit event loop, iterates over all open windows, determines each window's monitor, computes the position relative to the monitor origin, and collects tab CWDs, titles, and per-tab customizations (user-set names, custom colors, and custom icons).

**Restore flow** (`src/arrangements/restore.rs`): Builds a monitor mapping from saved monitors to available monitors, converts relative positions back to absolute coordinates on the matched monitor, clamps positions to ensure visibility, and applies per-tab customizations using the exact `WindowId` from each created window to guarantee correct assignment in multi-window layouts.

**Storage** (`src/arrangements/storage.rs`): Serializes and deserializes arrangements to/from YAML using serde. Handles missing files (returns empty manager), empty files, and corrupt files (returns error). Automatically creates parent directories on save.

## Related Documentation

- [Window Management]WINDOW_MANAGEMENT.md - Window types, multi-monitor support, and display settings
- [Keyboard Shortcuts]KEYBOARD_SHORTCUTS.md - Keybinding configuration reference
- [Tabs]TABS.md - Tab management and navigation