par-term 0.49.0

Cross-platform GPU-accelerated terminal emulator with inline graphics support (Sixel, iTerm2, Kitty)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
# Profiles

par-term provides a profile system for saving and quickly launching shells with custom configurations, similar to iTerm2's profile system.

## Table of Contents
- [Overview](#overview)
- [Profile Settings](#profile-settings)
- [Managing Profiles](#managing-profiles)
  - [Settings UI](#settings-ui)
  - [Open Profiles](#open-profiles)
  - [Profiles Drawer](#profiles-drawer)
  - [Profile Shortcuts](#profile-shortcuts)
- [Creating Profiles](#creating-profiles)
  - [Profile Icon Picker](#profile-icon-picker)
- [Using Profiles](#using-profiles)
- [Which Profile a Tab Runs](#which-profile-a-tab-runs)
- [Auto-Switching](#auto-switching)
  - [Directory-Based Profile Switching](#directory-based-profile-switching)
  - [Tmux Profile Auto-Switching](#tmux-profile-auto-switching)
  - [par-mux Session-Based Switching](#par-mux-session-based-switching)
  - [Hostname-Based Switching](#hostname-based-switching)
  - [Auto-Switch Priority](#auto-switch-priority)
  - [Auto-Switch Visual Application](#auto-switch-visual-application)
  - [Profile Commands and Confirmation](#profile-commands-and-confirmation)
- [Tmux Auto-Connect](#tmux-auto-connect)
- [par-mux Auto-Attach](#par-mux-auto-attach)
- [Default Startup Directory](#default-startup-directory)
- [Per-Profile Badge Configuration](#per-profile-badge-configuration)
- [Per-Profile Shader Settings](#per-profile-shader-settings)
- [Per-Pane Background Settings](#per-pane-background-settings)
  - [Available Controls](#available-controls)
  - [Darken Control](#darken-control)
  - [Real-Time Preview](#real-time-preview)
  - [Settings UI](#settings-ui-1)
- [Dynamic Profiles](#dynamic-profiles)
  - [Configuration](#configuration-1)
  - [Background Refresh](#background-refresh)
  - [Local Cache](#local-cache)
  - [Conflict Resolution](#conflict-resolution)
  - [Security](#security)
  - [Visual Indicators](#visual-indicators)
  - [Keybinding](#keybinding)
  - [Dynamic Profiles Settings UI](#dynamic-profiles-settings-ui)
- [Storage](#storage)
- [Related Documentation](#related-documentation)

## Overview

Profiles allow you to save terminal configurations for quick access:

```mermaid
graph TD
    Profiles[Profile System]
    Manager[ProfileManager]
    Drawer[Open Profiles / Profiles Drawer]
    Modal[Settings > Profiles]
    Storage[profiles.yaml]
    Session[Tab, Window, or Split]

    Profiles --> Manager
    Manager --> Drawer
    Manager --> Modal
    Manager --> Storage

    Drawer -->|open_profile actions| Session
    Modal -->|Create/Edit| Manager

    style Profiles fill:#e65100,stroke:#ff9800,stroke-width:3px,color:#ffffff
    style Manager fill:#1b5e20,stroke:#4caf50,stroke-width:2px,color:#ffffff
    style Drawer fill:#0d47a1,stroke:#2196f3,stroke-width:2px,color:#ffffff
    style Modal fill:#4a148c,stroke:#9c27b0,stroke-width:2px,color:#ffffff
    style Storage fill:#37474f,stroke:#78909c,stroke-width:2px,color:#ffffff
    style Session fill:#880e4f,stroke:#c2185b,stroke-width:2px,color:#ffffff
```

## Profile Settings

Each profile can customize the following:

| Setting | Description | Required |
|---------|-------------|----------|
| **Name** | Display name for the profile | Yes |
| **Icon** | Nerd Font icon or custom text identifier | No |
| **Working Directory** | Initial directory for the tab | No |
| **Command** | Custom command (instead of default shell) | No |
| **Command Arguments** | Arguments for the custom command | No |
| **Tab Name** | Custom name for the terminal tab | No |
| **Shell** | Specific shell for this profile (overrides global) | No |
| **Login Shell** | Override global login shell setting (None/true/false) | No |
| **Tags** | Comma-separated tags for organization and filtering | No |
| **Parent Profile** | Inherit settings from another profile | No |
| **Keyboard Shortcut** | Records the chord bound to this profile's `open_profile:<id>` keybinding (see [Profile Shortcuts](#profile-shortcuts)) | No |
| **SSH Host** | SSH hostname for remote connections | No |
| **SSH User** | SSH username | No |
| **SSH Port** | SSH port number | No |
| **SSH Identity File** | Path to SSH identity/key file | No |
| **SSH Extra Args** | Additional SSH command-line arguments | No |
| **Hostname Patterns** | Glob patterns for SSH-based auto-switching | No |
| **Directory Patterns** | Glob patterns for CWD-based auto-switching | No |
| **Tmux Session Patterns** | Glob patterns for auto-switching (e.g., `work-*`) | No |
| **Tmux Session** | tmux session to auto-connect when this profile opens (uses create-or-attach) | No |
| **Tmux Mode** | Connection mode: Control Mode (full integration) or Normal (plain tmux in PTY) | No |
| **par-mux Session Patterns** | Glob patterns for auto-switching when the window attaches to a matching par-mux session | No |
| **par-mux Session** | par-mux session to create-or-attach when this profile opens (see [par-mux Auto-Attach](#par-mux-auto-attach)) | No |
| **Badge Text** | Custom badge format for this profile | No |
| **Badge Appearance** | Override badge color, font, position, size | No |
| **Background Shader** | Custom shader path/name override | No |
| **Shader Brightness** | Shader brightness override | No |
| **Shader Text Opacity** | Shader text opacity override | No |
| **Shader Animation Speed** | Shader animation speed override | No |
| **Shader Textures** | Custom iChannel0-3 texture set override | No |

## Managing Profiles

### Settings UI

Profile management is embedded in the Settings window under the **Profiles** tab. Open Settings (`F12` or `Cmd/Ctrl + ,`) and navigate to the Profiles tab.

**Profile Management Features:**
- Create, edit, delete, and reorder profiles inline
- Up/Down buttons to change profile order
- Edit (pencil) and Delete (trash) buttons per profile
- Unsaved changes indicator

**Profiles › Manage Profiles...** in the menu bar, the Profiles drawer's **Manage** button, the launcher's row menu, and the `manage_profiles` action (command palette: **Manage Profiles**) all open the Settings window to the Profiles tab.

### Open Profiles

**Open Profiles…** is one keyboard-first launcher for every profile (iTerm2's Open Profiles). It replaced the tab bar's old "New Tab" profile window.

**Opening it:**
- Press `Cmd+O` (macOS). On Windows/Linux it ships unbound: use the **Profiles** menu, the command palette, or bind `toggle_profile_drawer`
- Click the chevron next to the tab bar's `+` button
- With `new_tab_shortcut_shows_profiles: true`, the new-tab shortcut opens it

**Keys:**

| Key | Action |
|-----|--------|
| type | Filter by profile name or tag |
| `↑` `↓` `PgUp` `PgDn` `Home` `End` | Move the selection |
| `Enter` | Open the profile in a new tab |
| `Shift+Enter` | Open it in a new window |
| `Cmd+D` / `Cmd+Shift+D` | Split the focused pane right / down, running the profile |
| `Cmd+Enter` | Give the active tab this profile (and pin it) |
| `Ctrl+Space` | Mark the row; an activation with marked rows opens every marked profile |
| `Esc` or the toggle chord | Close |

Each row shows the profile's live keybinding (its `open_profile:<id>` chord), its tags, and a **dynamic** marker for profiles from a remote source. The first row, **Default**, is a plain tab running your configured shell. Click a tag chip to show only that tag. Right-click a row for **Open in** (New Tab, New Window, Split Right, Split Down, This Tab), **Edit Profile…**, and **Duplicate**.

A split with a profile works in local tabs. A par-mux or tmux pane cannot start a profile's program yet (the daemon's `split-window` takes a directory but no command), so the launcher says so instead of opening a pane that ignores the profile.

### Profiles Drawer

The Profiles drawer is Open Profiles pinned to the right edge of the window: the same list, keys, and actions. The terminal reflows beside it rather than drawing under it.

**Opening the drawer:**
- Click the toggle button on the right edge of the window,
- Choose **View › Profiles Panel** in the menu bar (checked while the drawer is open), or
- Bind `toggle_profiles_panel`

The drawer takes keys only while its filter has focus, so typing in the terminal never moves its selection. `Esc` in the filter, or its **✕** button, closes it.

### Profile Shortcuts

A profile shortcut is an ordinary keybinding to `open_profile:<id>`. Record it in the profile editor's **Keyboard Shortcut** field (General sub-tab): **Record** captures a chord and **Clear** removes it. Done writes the profile's one `open_profile:<id>` binding, replacing any previous chord rather than adding a second, and Settings' Save persists it. You can also bind it in **Settings › Keys › Key Bindings** like any other action. Either way it is conflict-checked against every other chord and shows in the command palette and the launcher.

The older per-profile **Keyboard Shortcut** field is migrated on first launch: each profile's shortcut becomes an `open_profile:<id>` binding with the same chord, and the field is cleared. A shortcut that does not parse, or whose chord another binding already holds, is left on the profile and logged; bind it by hand. A child profile no longer inherits its parent's shortcut.

Related actions: `open_profile_window:<id>`, `split_profile:<id>:right`, `split_profile:<id>:down`, and `set_tab_profile:<id>` (see [Keyboard Shortcuts](../guides/KEYBOARD_SHORTCUTS.md)).

## Creating Profiles

**Step-by-step:**

1. Open Settings › Profiles (**Profiles › Manage Profiles...** in the menu bar, the drawer's **Manage** button, or **Manage Profiles** in the command palette)
2. Click **+ New Profile**
3. Fill in the profile settings:
   - **Name** (required): Give your profile a descriptive name
   - **Icon**: Add a Nerd Font icon for visual identification
   - **Working Directory**: Set the starting directory
   - **Command**: Override the default shell (optional)
   - **Arguments**: Space-separated command arguments
   - **Tab Name**: Custom tab title (optional, on the Text & Badge sub-tab)
4. Optionally click the icon picker button to choose a Nerd Font icon
5. Click **Done** to return to the profile list
6. Click **Save** at the bottom of the Settings window. It writes profile and configuration changes together; **Revert** drops them

### Profile Icon Picker

The profile icon field includes an icon picker popup with ~200 curated Nerd Font icons organized in 14 categories. Nerd Font icons render reliably in the egui-based settings UI and each icon shows a descriptive tooltip on hover.

| Category | Description | Example Icons |
|----------|-------------|---------------|
| Terminal | Shells and terminal emulators | Terminal, Bash, PowerShell, tmux, Prompt |
| Dev & Tools | Languages and development tools | Code, GitHub, Python, Rust, TypeScript, Go, C/C++ |
| Files & Data | Files, folders, and storage | File, Folder, Database, Save, Package |
| Network & Cloud | Networking and cloud services | Globe, WiFi, Cloud, Server, SSH, AWS |
| Security | Locks, keys, and access control | Lock, Shield, Key, Eye, Warning |
| Git & VCS | Version control systems | Branch, Merge, Commit, GitHub, GitLab, BitBucket |
| Weather & Nature | Weather and natural elements | Sun, Moon, Snowflake, Lightning, Rainy, Tree, Leaf |
| Containers & Infra | Containers and infrastructure | Docker, Kubernetes, CPU, Gear, Memory |
| OS & Platforms | Operating systems and platforms | Apple, Windows, Linux, Android, Homebrew |
| Status & Alerts | Status indicators and signals | Check, Bolt, Rocket, Fire, Star |
| UI Actions | Common interface actions | Search, Edit, Copy, Plus, Refresh, Save |
| Navigation | Arrows and directional icons | Arrow Up/Down, Angle Left/Right, Reply |
| People & Misc | People and miscellaneous symbols | User, Robot, Gamepad, Music, Bookmark |
| Fun & Seasonal | Seasonal and fun symbols | Ghost, Skull, Gift, Magic Wand, Sparkle, Trophy |

Click any icon to set it as the profile icon, or type a custom value directly in the text field. Use the "Clear icon" button to remove the current icon.

**Example Profiles:**

| Profile | Command | Working Dir | Use Case |
|---------|---------|-------------|----------|
| Development | - | `~/projects` | General development |
| SSH Server | `ssh user@server` | - | Remote connection |
| Docker Shell | `docker exec -it container bash` | - | Container access |
| Python REPL | `python3` | `~/scripts` | Interactive Python |

## Using Profiles

**Launch a Profile:**

1. Open Profiles (`Cmd+O` on macOS, the tab bar chevron, or the Profiles menu)
2. Type part of its name or a tag
3. Press `Enter` (new tab), `Shift+Enter` (new window), or `Cmd+D` / `Cmd+Shift+D` (split)

The command palette lists four rows per profile: **Open Profile**, **Open Profile in New Window**, **Split with Profile**, and **Change Tab Profile**.

**What Happens:**
- A new tab opens with the profile's configuration
- Working directory is set if specified
- Custom command runs (or default shell if not specified)
- Tab name updates if specified

**Splits and restarts keep the profile's program:**
- Splitting a profile tab runs the same program in the new pane: the profile's SSH connection, command, or shell. The new pane starts in the focused pane's directory. Turn this off with `split_inherits_profile: false` (Settings > Panes > Layout & Dividers > "Splits run the tab's profile program") to get the default shell instead.
- A profile that attaches a tmux or par-mux session is not inherited, since its program would attach again. Splits of those tabs, and of par-mux tabs, work as usual.
- **Restart Pane** (`restart_pane`) reruns the program the pane was started with, so a profile tab's first pane restarts its SSH connection or command rather than a local shell. So do the `shell_exit_action` restart options.
- An SSH connection typed by hand, or opened with SSH Quick Connect (which types `ssh` into the current shell), is not a profile program: a split of that tab starts a local shell.

## Which Profile a Tab Runs

A tab remembers the profile it was opened from, and any profile an automatic switch applied on top of it:

- **Tab tooltip:** hover a tab for `Profile: Work`, or `Profile: Prod (auto: host prod.example.com)` while an automatic switch is applied.
- **Profile status-bar widget:** the active tab's profile name. It ships off; turn it on in Settings › Status Bar.
- **Variables:** `\(tab.profile_name)` in a badge or status-bar format is the active tab's profile (`Default` for a plain tab); `\(session.profile_name)` follows the active tab too.
- **Change it:** **Change Profile…** on the tab's context menu, `Cmd+Enter` in Open Profiles, or **Change Tab Profile: X** in the palette. This applies the profile's icon, title, badge, and shader to the tab (the running process is not replaced) and pins the tab.
- **Edit it:** **Session › Edit Tab's Profile...** in the menu bar, or the `edit_tab_profile` action, opens Settings › Profiles and names the tab's profile in a toast.

## Default Startup Directory

When opening a new terminal without a profile, par-term uses the configured startup directory mode.

### Startup Modes

| Mode | Description |
|------|-------------|
| `home` | Start in home directory (default) |
| `previous` | Start in the last used working directory |
| `custom` | Start in a user-specified directory |

### Configuration

```yaml
# Startup mode: "home", "previous", or "custom"
startup_directory_mode: "home"

# Custom directory (only used when mode is "custom")
startup_directory: "/path/to/directory"
```

### Settings UI

1. Press `F12` to open Settings
2. Navigate to **Terminal** → **Shell**
3. Find the **Startup Directory** section
4. Select mode and configure path if needed

### Priority

Directory selection follows this priority:

1. **Profile working directory** - If launching a profile with a directory set
2. **Legacy `working_directory`** - If set in config (for backwards compatibility)
3. **Startup directory mode** - Based on `startup_directory_mode` setting
4. **Home directory** - Fallback if configured path doesn't exist

> **📝 Note:** The `previous` mode requires shell integration to track directory changes while the shell runs.

## Auto-Switching

par-term can automatically apply profiles based on the current working directory, tmux session name, or remote hostname. Auto-switched profiles apply all visual settings including icon, title, badge, and optional command execution.

### Directory-Based Profile Switching

Profiles can automatically apply when the terminal's working directory matches configured glob patterns.

**Configuration:**

Add `directory_patterns` to a profile:

```yaml
- id: 550e8400-e29b-41d4-a716-446655440000
  name: Work Projects
  directory_patterns:
    - "~/Repos/work-*"
    - "~/Repos/company-*"
    - "/opt/projects/*"
  icon: "🏢"
  badge_text: "WORK"
```

**Pattern Examples:**

| Pattern | Matches |
|---------|---------|
| `~/Repos/work-*` | `~/Repos/work-api`, `~/Repos/work-frontend` |
| `/opt/projects/*` | Any directory under `/opt/projects/` |
| `~/Repos/par-term*` | `~/Repos/par-term`, `~/Repos/par-term-core` |

- Patterns support `~` for home directory expansion
- CWD changes are detected via OSC 7 (requires shell integration)
- First matching profile wins (check profile order)
- Profile clears when CWD no longer matches any pattern

**Settings UI:**

1. Open Settings > Profiles
2. Edit a profile
3. Open the **Auto-Switch** sub-tab
4. Enter comma-separated glob patterns in the **Directories** field

### Tmux Profile Auto-Switching

Profiles can automatically apply when connecting to tmux sessions with matching names.

**Configuration:**

Add `tmux_session_patterns` to a profile:

```yaml
- id: 550e8400-e29b-41d4-a716-446655440000
  name: Production
  tmux_session_patterns:
    - "*-prod"
    - "*-production"
    - "prod-*"
  badge_text: "🔴 PROD"
  badge_color: [255, 0, 0]
```

**Pattern Matching:**

| Pattern | Matches |
|---------|---------|
| `dev-*` | `dev-api`, `dev-frontend`, etc. |
| `*-prod` | `api-prod`, `web-prod`, etc. |
| `*server*` | `webserver`, `api-server-1`, etc. |
| `main` | Exact match only |

- Patterns are case-insensitive
- First matching profile wins (check profile order)
- Profile clears when tmux session ends

**Settings UI:**

1. Open Settings > Profiles
2. Edit a profile
3. Open the **Auto-Switch** sub-tab
4. Enter comma-separated patterns in the **tmux sessions** field: `work-*, *-production`

### par-mux Session-Based Switching

The par-mux analogue of the tmux rule: when the window attaches to a par-mux session — via the session picker, a profile's `mux_session_name` auto-connect, or the `mux_auto_attach` launch option — profiles with a matching `mux_session_patterns` entry apply to the attached tab.

```yaml
profiles:
  - name: Production
    mux_session_patterns:
      - "*-prod"
      - "PARMux"
```

Patterns are comma-separated globs in the **par-mux sessions** field of the Auto-Switch sub-tab, matched case-insensitively; the first matching profile wins (profile order decides). A pinned active tab keeps its profile, and opening a profile explicitly (which pins the tab) is never overridden by the rule.

### Hostname-Based Switching

Profiles automatically apply when connecting to remote hosts with matching hostnames, detected via OSC 7 shell-integration sequences (`file://hostname/path`). Hostname patterns are matched against the host extracted from the OSC 7 URL; the local host is ignored. Enter them in the **Hosts** field of the profile editor's Auto-Switch sub-tab.

### Auto-Switch Priority

When multiple auto-switch mechanisms could apply, the following priority order determines which profile wins:

1. **Explicit user selection** — a tab given a profile with Change Tab Profile, or pinned, never switches automatically
2. **Hostname match** — remote host detection via OSC 7 (an SSH session reports its host this way; highest auto priority)
3. **Directory match** — CWD-based matching via OSC 7
4. **Tmux session match** — tmux session name pattern matching (applied via the tmux gateway separately)
5. **par-mux session match** — par-mux session name pattern matching (applied at attach time; same independence as the tmux rule)

Tmux session matching runs independently through the gateway tab and does not compete with hostname or directory switching. par-mux session matching runs at attach time on the attached tab.

Every tab is evaluated, not only the one you are looking at: a background tab that connects to a matching host switches too.

**Announced and undoable.** Each switch shows a toast — `Profile Prod applied (host prod.example.com)` — with an **Undo** button. Undo reverts the tab and does not re-apply that profile until its rule stops matching (you leave the host or directory).

**Pin a tab** to keep automatic switching away from it: **Pin / Unpin Profile** on the tab's context menu, **Session › Pin Profile** in the menu bar (checked while the active tab is pinned), or the `toggle_tab_profile_pin` action. Changing a tab's profile by hand pins it too.

### Auto-Switch Visual Application

When a profile is auto-applied via any switching mechanism (directory, hostname, tmux session, or par-mux session), the following settings are applied to that tab:

| Setting | Description |
|---------|-------------|
| **Profile icon** | Displayed in the tab bar (horizontal and vertical layouts) |
| **Tab title** | Overrides the current tab title |
| **Badge text** | The tab's badge; it follows the tab, so switching tabs switches badges |
| **Badge styling** | Applies badge color, alpha, font, bold, margins, size while the tab is active |
| **Command** | Queues the profile's command for confirmation (if configured), aimed at the switched tab — see [Profile Commands and Confirmation](#profile-commands-and-confirmation) |

The tab's original title, icon, and badge are saved when an auto-profile applies and restored when it clears or is undone.

### Profile Commands and Confirmation

A profile's `command` is written into the running shell, and the thing that triggers an auto-switch is an OSC 7 sequence — which is emitted by whatever is producing terminal output, including a remote host you are SSH'd into. A `*` hostname pattern matches everything. Auto-switch therefore **never executes a profile command inline**. The command is queued in the same confirmation queue that trigger `RunCommand` and `SendText` actions use, so you see the exact command text before anything runs.

Two rules sit on top of that queue:

- **`ssh.ssh_auto_profile_switch`** gates hostname-driven switching entirely. With it off, a remote host cannot cause a profile switch at all.
- **A profile fetched from a dynamic source re-confirms every time**, even if you previously chose "Always Allow" for that exact command. Consenting to a profile *source* is not consent to arbitrary commands from it, and the source can change the command on any refresh. Local profiles do honour an earlier "Always Allow".

The approval is keyed on both the profile and the command text, so an "Always Allow" granted for one command does not carry over when the profile is edited or re-fetched with a different one. Profile-command approvals are also kept in a separate identifier space from trigger approvals, so a grant cannot leak between the two systems.

## Tmux Auto-Connect

Profiles can automatically connect to a named tmux session when opened. This is separate from the global `tmux_auto_attach` startup option — per-profile sessions let different profiles connect to different named sessions.

### Configuration

Set `tmux_session_name` on a profile to enable auto-connect. The profile uses **create-or-attach** semantics (`tmux new-session -A -s <name>`), so opening the profile either creates the session if it doesn't exist or attaches to it if it does.

```yaml
profiles:
  - name: Work
    tmux_session_name: work-session
    tmux_connection_mode: control_mode  # or: normal
```

### Connection Modes

| Mode | YAML value | Behavior |
|------|-----------|----------|
| **Control Mode** (default) | `control_mode` | Full par-term integration via `tmux -CC`. Enables pane sync, window tabs, and input routing. |
| **Normal** | `normal` | Plain tmux UI runs in the PTY. No par-term integration. |

### UI

In the profile editor (Settings → Profiles), the **Tmux Auto-Connect** collapsible section appears on the **Session** sub-tab:

- **Session Name** — leave empty to disable auto-connect
- **Connection Mode** — radio buttons for Control Mode vs Normal

### Behavior

- Auto-connect only fires when `tmux_enabled = true` in global config
- If the window is already connected to tmux (gateway active), the auto-connect is skipped silently
- Errors are logged via the debug log (`make tail-log`)

## par-mux Auto-Attach

Set `mux_session_name` on a profile to create-or-attach a par-mux daemon session when the profile opens. Sessions keep running in the daemon after par-term closes.

```yaml
profiles:
  - name: Work
    mux_session_name: work
```

In the profile editor, the **par-mux Auto-Attach** section on the **Session** sub-tab has a **Session Name** field (empty disables it). The name may not contain spaces, quotes, or backslashes, because the daemon command grammar has no quoting.

## Per-Profile Badge Configuration

Profiles can override global badge settings for visual differentiation per environment.

### Available Overrides

| Setting | Description |
|---------|-------------|
| `badge_text` | Custom badge format string |
| `badge_color` | RGB color override |
| `badge_color_alpha` | Opacity override (0.0-1.0) |
| `badge_font` | Font family override |
| `badge_font_bold` | Bold toggle override |
| `badge_top_margin` | Position override |
| `badge_right_margin` | Position override |
| `badge_max_width` | Size constraint override |
| `badge_max_height` | Size constraint override |

### Example: Environment Indicators

```yaml
# Production profile - red badge
- name: Production
  badge_text: "🔴 PROD"
  badge_color: [255, 0, 0]
  badge_color_alpha: 0.3

# Development profile - green badge
- name: Development
  badge_text: "🟢 DEV"
  badge_color: [0, 255, 0]
  badge_color_alpha: 0.2

# Staging profile - yellow badge
- name: Staging
  badge_text: "🟡 STAGING"
  badge_color: [255, 200, 0]
```

### Settings UI

1. Open profile editor (double-click profile or click edit)
2. Expand "Badge Appearance" section
3. Check boxes to enable individual overrides
4. Configure color, font, margins, and size as needed

## Per-Profile Shader Settings

Profiles can override the global background shader, allowing different visual effects per environment.

### Available Overrides

| Setting | Description |
|---------|-------------|
| `shader` | Shader path or name override |
| `shader_brightness` | Brightness adjustment override |
| `shader_text_opacity` | Text opacity override |
| `shader_animation_speed` | Animation speed override |
| `shader_texture_set` | Custom iChannel0-3 texture set (array of 4 optional paths) |

### Example

```yaml
- name: Presentation
  shader: "aurora"
  shader_brightness: 0.8
  shader_text_opacity: 0.9
```

See [Custom Shaders](CUSTOM_SHADERS.md) for the full shader system documentation.

## Per-Pane Background Settings

When using split panes, each pane can have its own custom background image that overrides the global background. Per-pane backgrounds support independent image selection, display mode, opacity, and darkening control.

### Available Controls

| Setting | Description | Range |
|---------|-------------|-------|
| **Background Image** | Custom image path for this pane | — |
| **Display Mode** | How the image fills the pane (fit, fill, stretch, tile, center) | — |
| **Opacity** | Transparency of the background image | 0.0–1.0 |
| **Darken** | Darkens the background by reducing RGB towards black, independent of opacity | 0.0–1.0 |

### Darken Control

The darken slider (0.0–1.0) reduces the RGB values of the background image towards black while leaving opacity unchanged. This allows you to dim a bright background without affecting its transparency level:

- **0.0** — No darkening applied (full brightness)
- **0.5** — Background is 50% darkened (RGB values reduced by half)
- **1.0** — Fully darkened to black

For example, a background with `opacity: 0.8` and `darken: 0.5` will be 80% opaque and 50% darker than the original image.

### Real-Time Preview

Per-pane background settings apply instantly as values change. Adjusting the image, mode, opacity, or darken level immediately updates the preview in the terminal. No manual "Apply" button is required — changes take effect in real-time.

### Example: Production vs. Development Panes

```yaml
pane_backgrounds:
  # Left pane: production environment with dim red overlay
  - index: 0
    image: "~/images/prod-bg.png"
    mode: fill
    opacity: 0.7
    darken: 0.3

  # Right pane: development environment with bright blue overlay
  - index: 1
    image: "~/images/dev-bg.png"
    mode: fit
    opacity: 0.6
    darken: 0.0
```

### Settings UI

Access per-pane background settings in **Settings > Panes > Pane Backgrounds**:

1. Click on a split pane in the preview (if using split panes)
2. Configure the following for the selected pane:
   - **Image**: Browse to and select a custom background image, or leave empty to use the global background
   - **Mode**: Choose how the image fills the pane
   - **Opacity**: Adjust the transparency slider (0.0–1.0)
   - **Darken**: Adjust the darkening slider (0.0–1.0)

Changes apply immediately to the terminal without requiring manual confirmation.

## Dynamic Profiles

par-term can fetch profiles from remote URLs, enabling teams to share standardized terminal configurations. Dynamic profiles are read-only and update automatically in the background.

```mermaid
graph TD
    Sources[Remote Sources]
    Fetch[Background Fetch]
    Cache[Local Cache]
    Merge[Profile Merger]
    Local[Local Profiles]
    Combined[Combined Profile List]

    Sources --> Fetch
    Fetch --> Cache
    Cache --> Merge
    Local --> Merge
    Merge --> Combined

    style Sources fill:#0d47a1,stroke:#2196f3,stroke-width:2px,color:#ffffff
    style Fetch fill:#1b5e20,stroke:#4caf50,stroke-width:2px,color:#ffffff
    style Cache fill:#37474f,stroke:#78909c,stroke-width:2px,color:#ffffff
    style Merge fill:#e65100,stroke:#ff9800,stroke-width:3px,color:#ffffff
    style Local fill:#4a148c,stroke:#9c27b0,stroke-width:2px,color:#ffffff
    style Combined fill:#880e4f,stroke:#c2185b,stroke-width:2px,color:#ffffff
```

### Configuration

Dynamic profile sources are defined in `config.yaml` as an array:

```yaml
dynamic_profile_sources:
  - url: "https://example.com/team-profiles.yaml"
    headers:
      Authorization: "Bearer <token>"
    refresh_interval_secs: 1800
    max_size_bytes: 1048576
    fetch_timeout_secs: 10
    enabled: true
    conflict_resolution: "local_wins"
  - url: "https://internal.corp/devops-profiles.yaml"
    refresh_interval_secs: 3600
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `url` | string | (required) | URL to fetch profiles from |
| `headers` | map | `{}` | Custom HTTP headers (e.g., auth tokens) |
| `refresh_interval_secs` | integer | `1800` | How often to re-fetch the source (in seconds) |
| `max_size_bytes` | integer | `1048576` | Maximum response size (1 MB default) |
| `fetch_timeout_secs` | integer | `10` | HTTP fetch timeout in seconds |
| `enabled` | boolean | `true` | Whether this source is active |
| `conflict_resolution` | string | `"local_wins"` | How to handle ID collisions with local profiles |

There is no per-source option for allowing plain HTTP. Whether `http://` URLs are permitted comes from the single global `allow_http_profiles` setting (default `false`), which is applied to every source at load time; adding an `allow_http` key to a source entry in `config.yaml` does nothing.

### Background Refresh

Dynamic profile sources refresh automatically on a configurable timer:

- The default refresh interval is 1800 seconds (30 minutes)
- Each source can have its own interval via `refresh_interval_secs`
- Refresh occurs in the background without blocking the UI
- Failed fetches retain the previously cached version

### Local Cache

Fetched profiles are cached locally to provide offline access and faster startup:

**Cache location:** `~/.config/par-term/cache/dynamic_profiles/`

- Each source URL maps to a separate cache file
- The cache is populated on first fetch and updated on each successful refresh
- On startup, par-term loads from cache immediately and refreshes in the background

### Conflict Resolution

When a dynamic profile has the same ID as a local profile, the `conflict_resolution` setting determines which takes precedence:

| Mode | Description |
|------|-------------|
| `local_wins` | Local profile takes precedence (default) |
| `remote_wins` | Dynamic profile overrides the local one |

### Security

A dynamic profile source can influence what command a shell runs, so the fetch path is defended at several layers.

- **Scheme allowlist**: only `https` is accepted, plus `http` when the global `allow_http_profiles` is `true`. This is an allowlist, not a `file://` denylist — every other scheme (`ftp:`, `data:`, `file:`, anything else) is rejected outright, regardless of the HTTP opt-in. Scheme matching is case-insensitive, so `HTTPS://` is recognised as HTTPS rather than misclassified
- **No credentials in the clear**: even with the HTTP opt-in enabled, a source whose headers include `Authorization`, or any header name containing `token` or `secret`, is refused rather than fetched. The opt-in permits plaintext transport, never plaintext credentials
- **Redirects cannot downgrade**: an `https` source is fetched with HTTPS enforced across the whole redirect chain, so a `302` answering with an `http://` `Location` fails instead of quietly continuing in the clear. (A source you explicitly opted into HTTP for has no such protection, by definition — it is already plaintext)
- **HTTP is loud**: when a source is fetched over plain HTTP, a warning naming the URL is written to both the debug log and the standard log on every fetch
- **Size limits**: a response body larger than `max_size_bytes` (1 MB by default) fails the fetch rather than being truncated and parsed, and `fetch_timeout_secs` (10s by default) bounds the request as a whole. Either way the source keeps its previously cached profiles
- **Catch-all command profiles are rejected at merge**: a fetched profile that pairs a command with a catch-all auto-switch pattern (`*` or `**` in `hostname_patterns`, `directory_patterns` or `tmux_session_patterns`) is dropped and logged rather than added. Such a profile would fire its command on the first hostname or directory the terminal reports. You can still write one locally; a remote source cannot
- **Fetched commands are never pre-approved**: a command carried by a dynamic profile re-confirms on every auto-switch even after an "Always Allow" — see [Profile Commands and Confirmation](#profile-commands-and-confirmation)

### Visual Indicators

Dynamic profiles are visually distinguished throughout the interface:

- A `[dynamic]` badge appears next to dynamic profile names in the profile modal and profile drawer
- Dynamic profiles are read-only; opening one shows a "managed by a remote source" notice and all form fields are disabled
- Edit and delete controls are disabled for dynamic profiles in the profile list

### Keybinding

Use the `reload_dynamic_profiles` action to manually trigger an immediate refresh of all dynamic profile sources:

```yaml
keybindings:
  - key: "CmdOrCtrl+Shift+F5"
    action: "reload_dynamic_profiles"
```

### Dynamic Profiles Settings UI

Dynamic profile sources can be managed in **Settings > Profiles > Dynamic Sources**:

- Add, edit, and remove remote source URLs
- Configure per-source headers, refresh interval, and size limits
- Set conflict resolution mode
- Enable or disable individual sources without removing them

## Storage

Profiles are stored in YAML format:

**Location:** `~/.config/par-term/profiles.yaml`

**Format:**
```yaml
- id: 550e8400-e29b-41d4-a716-446655440000
  name: Development
  working_directory: ~/projects
  icon: "\U0001F4BB"
  order: 0
- id: 6fa459ea-ee8a-3ca4-894e-db77e160355e
  name: SSH Server
  command: ssh
  command_args:
    - user@server
  icon: "\U0001F310"
  order: 1
```

**Key Points:**
- UUIDs uniquely identify each profile
- Order field controls display sequence
- Changes save immediately when clicking **Save** in the modal

**How the file is written:** profiles carry commands and SSH arguments, so `profiles.yaml` is written atomically and owner-only. The new contents are staged in a temporary file in the same directory at mode `0600`, flushed to disk, and then renamed over the target — so the final file is mode `0600` on Unix regardless of your umask, and an interrupted or failed save leaves the previous `profiles.yaml` byte-for-byte intact rather than truncated. That matters because a truncated profiles file parses as "no profiles" rather than as an error.

## Related Documentation

- [Keyboard Shortcuts](../guides/KEYBOARD_SHORTCUTS.md) - Profile keyboard shortcuts
- [Tabs](TABS.md) - Tab management and directory inheritance
- [SSH Host Management](SSH.md) - SSH profiles and host-based auto-switching
- [Badges](BADGES.md) - Badge system and variables
- [Integrations](INTEGRATIONS.md) - Shell integration for directory tracking