English · 简体中文
These three things are already written by
setup-gui.sh(whichinstall.shcalls), so you normally never click them. The manual steps are kept here as (1) a fallback when the automation did not take, and (2) a way to understand what each item changes. To verify: run section 4 of./doctor.sh. To open the settings panel:Cmd+,
setup-gui.shwrites these three withdefaults write(plus removing the window border and hiding the tab bar when there is only one tab):TabStyleWithAutomaticOption=5(Minimal),Default Bookmark Guid(the default profile), and aGlobalKeyMapentry for Shift+Enter (in both the old and new key-mapping formats).⚠️ It quits iTerm2 first (otherwise iTerm2 overwrites the values when it exits), and it never runskillall cfprefsdafterwards (that would discard writes not yet flushed).
Settings → Appearance → General → Theme → Minimal
The tab bar blends into the terminal background and the grey title bar disappears, leaving the window as one flat canvas. While you are in that panel:
| Item | Where | Value |
|---|---|---|
| Tab bar in full screen | Appearance → Tabs | ✅ Show tab bar in fullscreen |
| Hide the tab bar with one tab | Appearance → Tabs | ✅ hide tab bar when there is only one tab |
| New-output indicator | Appearance → Tabs | ✅ Show "new output" indicator |
| Window border | Appearance → Windows | ❌ Show border around window |
install.sh has already put hekouwang-active-theme.json into the DynamicProfiles folder
(live on save, no restart needed).
Settings → Profiles → select the profile carrying the Dynamic tag →
Other Actions... → Set as Default
Settings → Keys → Key Bindings → +
| Field | Value |
|---|---|
| Keyboard Shortcut | press Shift+Enter |
| Action | Send Text |
| Text | \n |
What it is for: in Claude Code and similar AI CLIs, Enter submits and Shift+Enter adds a
newline — the same muscle memory as any chat app.
Profiles are not clicked together in the GUI; they are JSON, and they live in:
~/Library/Application Support/iTerm2/DynamicProfiles/
Four advantages: (1) live on save (iTerm2 watches the folder); (2) versionable — a new machine needs one file; (3) self-contained; (4) copy it, change the Guid and the colors, and you have a new theme.
The key fields (a complete file is config/themes/v2-mihei.json; every theme has the same
shape):
⚠️ Pitfall: do not inherit the font through"Dynamic Profile Parent Name"— once the parent profile is deleted, the font falls back to the system default silently and after a restart everything is thin and small. Write the settings that matter directly into the JSON.
Font strategy: "an elegant main font + Symbols Nerd Font as fallback". The main font does
not have to be a Nerd-patched build; icons are rendered by font-symbols-only-nerd-font, so
changing the main font never loses glyphs.
| Path | Item | Value | Why |
|---|---|---|---|
| Profiles → Terminal | Unlimited scrollback | ✅ | Nothing scrolls away, and Cmd+F becomes full-text search |
| Profiles → Terminal | Silence bell + Flash visual bell | ✅ | Mute it, flash instead |
| Profiles → General | Working Directory | your workspace | New windows start there |
| General → Startup | Window restoration | Only Restore Hotkey Window | Do not bring back a pile of old windows |
| General → Selection | Copy to pasteboard on selection | ✅ | Select and it is copied |
| General → Selection | Triple-click selects wrapped lines | ✅ | Triple-click grabs the whole logical line |
| Profiles → Text | Cursor: vertical bar, no blinking | — | A cursor that stays put |
System-wide (vim users need this — press and hold repeats the key instead of opening the accent menu):
defaults write -g ApplePressAndHoldEnabled -bool falsecurl -L https://iterm2.com/shell_integration/install_shell_integration_and_utilities.sh | bash| Capability | How |
|---|---|
| Navigate command blocks | Cmd+Shift+↑/↓ jumps between command outputs |
| Command status markers | A small triangle beside each command, red when it failed |
| Images in the terminal | imgcat picture.png |
| Remote copy | it2copy on a remote machine goes straight to your local clipboard |
| Download / upload | it2dl <file> / drag to upload |
| Change colors from a script | it2setcolor preset "Snazzy" |
Settings → Keys → Hotkey → Create a Dedicated Hotkey Window...
Opt+Space is a good hotkey: press it in any app and the terminal slides down from the top
of the screen; press again and it retracts.
You can add a little transparency and blur in that profile's Window settings.
Install it first: brew install tmux (already part of the CLI set in install.sh and a
doctor.sh check).
Why it matters: in -CC control mode, tmux windows and panes map to native iTerm2
tabs and splits (not character art — mouse, scrolling and copying are all native). An SSH
drop, a closed lid or a network change does not lose the remote session, and reconnecting
restores it as it was. On a server running long jobs, this is the feature that saves you.
# a local integrated session
tmux -CC
# remote: attach if it exists, create if not (reconnecting never loses the session)
ssh server -t 'tmux -CC attach || tmux -CC'
# named sessions (keep projects apart and reattach by name)
ssh server -t 'tmux -CC new -A -s deploy'Usage notes (these are all you normally need):
| Action | How |
|---|---|
| New window (= new tab) | Cmd+T (the native iTerm2 shortcut just works) |
| Split | Cmd+D / Cmd+Shift+D (as native) |
| Detach temporarily (leave it running) | Close that iTerm2 window, or the tmux prefix Ctrl+B then d |
| Reattach | Run the tmux -CC attach above again (over SSH for a remote one) |
| See which sessions exist | tmux ls on the remote |
| End a session for good | exit out of every window, or tmux kill-session -t <name> |
⚠️ In-CCintegration mode, do not use tmux's ownCtrl+B %/Ctrl+B "to split — leave that to iTerm2'sCmd+D. The prefix key is basically only needed forCtrl+B d(detach).
Adding tmux to an SSH alias (optional, put it in ~/.zshrc.local): for machines that
drop often or run long jobs, append -t 'tmux -CC new -A -s main' to the alias and every
connection lands in a persistent session:
# ~/.zshrc.local — persistent-session alias (a drop no longer loses it)
alias ecs:prod:web="ssh -o ServerAliveInterval=60 -t root@<YOUR_SERVER_IP> 'tmux -CC new -A -s main'"
⚠️ Check the remote actually has tmux before changing the alias, otherwise every connection greets you withcommand not found: tmux. Installing it remotely:apt install tmux(Debian/Ubuntu) oryum install tmux(CentOS/RHEL). If you are not sure which machines have it and want to change all the aliases at once, use the form with a fallback — without tmux it degrades to a normal login shell instead of erroring:alias ecs:prod:web="ssh -o ServerAliveInterval=60 -t root@<YOUR_SERVER_IP> 'tmux -CC new -A -s main || exec \$SHELL -l'"Note that
\$SHELLmust be escaped so it expands on the remote side. Also,-CCis iTerm2-only: in other apps (the VS Code terminal, say) the alias starts plain tmux without the native tab mapping.
⚠️ Conflicts with Claude Code's fullscreen mode (confirmed by the vendor, 2026-07): if you turn on Claude Code's fullscreen rendering (/tui fullscreen, an opt-in preview) inside a-CCsession, things break — scrolling stops working and a double click can corrupt the terminal state. The reason: under-CC, iTerm2 renders each pane as a native split and does not let tmux draw, which fights the alternate screen buffer a fullscreen program wants.
- Their words: "Fullscreen rendering is incompatible with iTerm2's tmux integration mode... Don't enable fullscreen rendering in
tmux -CCsessions." (code.claude.com/docs/en/fullscreen)- The blast radius is small: (1) it only affects the fullscreen preview; Claude Code's default classic renderer is unaffected and works with
-CCas usual. (2) The vendor says plain tmux (without-CC) is fine in iTerm2 — though that is the vendor's account, and some versions (issue #58364) have reported minor scrolling / scrollback problems in plain mode too. Deal with it if you hit it.- Conclusion: if you want Claude Code's fullscreen preview, do not run it inside a
-CCsession — either the default renderer plus-CC, or fullscreen plus plain tmux (or no tmux). Using-CCto survive SSH drops remains the headline feature and is unaffected.
These 4 are already written into the theme profile JSON and are delivered automatically by
install.sh / theme.sh — no GUI clicking.
Section 3 of doctor.sh checks they are there; to see or change them, go to
Settings → Profiles → Advanced → Triggers → Edit.
| Regex (excerpt) | Action | Effect |
|---|---|---|
\b(ERROR|FATAL|FAILED|PANIC|Exception)\b |
HighlightTrigger, red background | Error words in logs turn red (the word only, not the whole line) |
\b(WARN|WARNING|DEPRECATED|TODO|FIXME)\b |
HighlightTrigger, yellow background | Warnings and to-dos turn yellow |
\b(SUCCESS|SUCCEEDED|SUCCESSFUL|PASSED)\b |
HighlightTrigger, green background | Success words turn green |
…[Pp]assword…: |
PasswordTrigger (Instant) | A password prompt pops your password manager |
⚠️ The regexes are case-sensitive: only upper-caseERROR/WARNING/SUCCEEDEDand friends are marked (the log convention), so a lower-case "error" or "0 warnings" in prose does not get colored into noise.
Implementation notes (config as code, in triggers() inside config/themes/_generate.py):
- Highlight colors use the Dynamic Profile shorthand
{#foreground,#background}(official docs), taken from the current theme — which is why the highlight colors follow./theme.shwhen you reskin. actionis just iTerm2's trigger class name:HighlightTrigger/PasswordTrigger.partial:trueis Instant in the GUI (fire without waiting for a newline); a password prompt has no newline, so it must be Instant.- To change the rules: edit
triggers()→cd config/themes && python3 _generate.py→./theme.sh <current theme>→ open a new tab (Triggers only apply to new sessions; existing tabs keep the old set).
Why there is no "notify me when a long job finishes": macOS notifications have a fixed layout, and iTerm2's built-in Post Notification forces in a line of
A trigger fired in session…that cannot be removed. Usingterminal-notifiermeans an extra dependency plus manually allowing "trigger runs a command", and it did not look good in testing either. On balance it is left out to keep these four clean and reliable.
Smart Selection + Semantic History — also written into the Dynamic Profile by
smart_selection_rules() / semantic_history() in _generate.py.
| Match | Cmd-click / first action | Also in context menu |
|---|---|---|
path/to/file.ext:42 or :42:8 |
Open at line (best editor) | Copy path:line |
Git SHA ([0-9a-f]{7,40}) |
git show --stat in repo cwd |
Copy SHA |
localhost:3xxx / 127.0.0.1:… |
Open http://… in browser |
Copy host:port |
Stock iTerm2 rules (Paths, HTTP URL, email, …) are kept in the same list — setting
Smart Selection Rules replaces inheritance, so the generator embeds them.
Semantic History action = best editor (Cursor / VS Code / … if installed).
setup-gui.sh pins CommandSelection=YES so Cmd-click is not silently off.
./doctor.sh --status # semantic line must show product rules + SH=best editor
./theme.sh <theme> && Cmd+T # new tab requiredSettings → Profiles → Session → Status bar enabled → Configure
You can drag in CPU / memory / network / git-branch components; under the Minimal theme, put
it at the bottom with a background matching the theme.
Heavy starship users may prefer to leave it off to avoid saying everything twice.
This kit ships seven themes (3 community + 4 brand). One command, live on save, no restart:
./theme.sh # theme gallery (with true-color swatches) + the current one
./theme.sh v2-mihei # back to the default (V2 Warm Dark: warm dark + warm orange cursor)
./theme.sh v1-keji # V1 Tech Dark (near-black + mint cursor)
./theme.sh tokyo-night # Tokyo Night (also gruvbox-dark / catppuccin-mocha)
./theme.sh --preview v1-keji # look without switching: a whole fake terminalHow it works: the profiles in config/themes/*.json all share one Guid, and theme.sh
copies the selected one to
~/Library/Application Support/iTerm2/DynamicProfiles/hekouwang-active-theme.json, removing
older files that would collide on that Guid. Because the Guid never changes, the "default
profile" binding never breaks. The theme list is discovered by scanning
config/themes/*.json, so adding a theme does not mean editing theme.sh.
Adding a theme: add a 16-color palette under config/themes/palettes/ (a new
palettes/mine.py is discovered automatically), then re-run
cd config/themes && python3 _generate.py to produce the matching profile JSON. With the
paid pack present, the same run also emits the Warp YAML, the Ghostty theme and the whole
ecosystem/ set, and ./theme.sh <name> switches all of them together.
New Bookmarks). So to clear a value that was once
set wrongly through the GUI (Initial Text, say), the JSON has to declare it as empty
explicitly; simply not writing it does not clear it.
To try another scheme temporarily you can still import an
.itermcolorsby hand (Settings → Profiles → Colors → Color Presets... → Import...), for example from the several hundred in mbadolato/iTerm2-Color-Schemes. But themes you use regularly belong inconfig/themes/.
| What | Path |
|---|---|
| Dynamic Profiles | ~/Library/Application Support/iTerm2/DynamicProfiles/ |
| iTerm2 main preferences | ~/Library/Preferences/com.googlecode.iterm2.plist |
| Starship | ~/.config/starship.toml |
| zsh config | ~/.zshrc + ~/.zshrc.local (private) |
| Shell Integration | ~/.iterm2_shell_integration.zsh + ~/.iterm2/ |
| atuin database | ~/.local/share/atuin/history.db |
| Language setting | ~/.config/hekouwang-terminal/lang |
| Export every setting | Settings → General → Settings → Export All Settings... |
{ "Profiles": [{ "Name": "hekouwang · V2 Warm Dark", "Guid": "catppuccin-mocha-dynamic-2026", // must be unique and stable "Normal Font": "OperatorMono-Book 14", // main font (e.g. JetBrainsMono-Regular 14) "Non Ascii Font": "SymbolsNFM 14", // Nerd Font icon fallback "Use Non-ASCII Font": true, // ⚠️ without this line the fallback does nothing "Unlimited Scrollback": true, "Silence Bell": true }] }