---
title: "Configuration"
description: "The config file and the Settings window, what each Settings page holds, importing from WezTerm, editors, coding agents and Herdr, the cursor, the update check, restoring pane contents, where data lives, environment variables, and how ThinkTerm's defaults differ from WezTerm's."
url: "https://docs.closex.org/thinkterm/configuration.html"
markdown_url: "https://docs.closex.org/thinkterm/configuration.md"
site: "CloseX Docs"
product: "ThinkTerm"
language: "en"
last_updated: "2026-11-08T12:04:16.000Z"
alternates:
  en: "https://docs.closex.org/thinkterm/configuration.md"
  zh-Hans: "https://docs.closex.org/zh/thinkterm/configuration.md"
  ja: "https://docs.closex.org/ja/thinkterm/configuration.md"
---

# Configuration

> **Documentation in progress**
> Some sections are still incomplete.

ThinkTerm is configured with a Lua file, the same way WezTerm is. The option names, the module name, and the syntax are unchanged — a ThinkTerm config still opens with:

```lua
local wezterm = require 'wezterm'
```

What differs is where the file is read from and where the data is written.

## Two stores, not one

The Lua file is the one you edit by hand, and it carries the terminal's own options.

Beside it, `~/.config/thinkterm/settings.json` is written by the Settings window, and holds the choices that have a control there — the theme, window opacity, keyboard shortcuts, scrolling and the overlay scrollbar, interface fonts and sidebar widths, the interface language, the renderer, tab icons. It is not meant to be edited by hand. Where the two overlap, a choice made in Settings is nearly always the one in force; [the exceptions are below](#where-settings-and-the-config-file-overlap).

The file is also rewritten as you work — a sidebar dragged or hidden, the window moved — and none of that reloads the Lua config, so it costs no reloaded fonts and no closed command palette. An edit made to it from outside, by an editor or another program, is picked up at once and applied the way the Settings window would apply it, rather than sitting unused until the next save writes the older copy back over it.

## The Settings window

`Settings` opens from the gear at the foot of the sidebar, with ⌘, on macOS, or with Ctrl+Shift+, elsewhere. Its pages, and where each is described:

| Page | Holds | Described in |
|---|---|---|
| General | Language, Renderer, Restore Main Window Frame, which config file loaded, the session server, Restore pane contents, Restart and Quit | This page, [The Window](https://docs.closex.org/thinkterm/window.md), [Remote](https://docs.closex.org/thinkterm/remote.md) |
| Appearance | Theme, Effective Color Scheme, App Icon, Typography, Window Opacity, on macOS 26 the Glass effect, the shade of the sidebars, tab bars and terminal, and the icons and status marks of the sidebar | [The Window](https://docs.closex.org/thinkterm/window.md), [Window opacity](https://docs.closex.org/thinkterm/window.md#window-opacity), [Icons and status](https://docs.closex.org/thinkterm/window.md#icons-and-status-in-the-sidebar) |
| Tab Icons | The program icons on pane tabs | [The Window](https://docs.closex.org/thinkterm/window.md#tab-icons) |
| Sidebar & Plugins | The right-hand panels, Status, installed plugins | [The Window](https://docs.closex.org/thinkterm/window.md#the-right-hand-panels), [Plugins](https://docs.closex.org/thinkterm/plugins.md), [Agent Status](https://docs.closex.org/thinkterm/agents.md) |
| Terminal | Default Shell, font, Scrolling, Scrollbar, Minimum text contrast, Cursor shape and Cursor blink, Mouse and clipboard, Split animation, Remote Split Resize, Bottom Quote | This page, [The Window](https://docs.closex.org/thinkterm/window.md), [Copy and paste](https://docs.closex.org/thinkterm/window.md#copy-and-paste), [Remote](https://docs.closex.org/thinkterm/remote.md), [Privacy and Security](https://docs.closex.org/thinkterm/privacy.md#links-and-pastes-a-program-prints) |
| Workspaces | Remote Files idle timeout, Download Folder, Remote Drop Destination, Notification Sounds, System Notifications and, on macOS, Dock Icon Count | [Files and Notes](https://docs.closex.org/thinkterm/files-and-notes.md), [Agent Status](https://docs.closex.org/thinkterm/agents.md), [This page](#sounds-notifications-and-the-dock-count) |
| Agents | Which coding agents are recognised, and how | [Agent Status](https://docs.closex.org/thinkterm/agents.md) |
| Web | Browser access and the links handed out | [Web](https://docs.closex.org/thinkterm/web.md) |
| Share & Sync | `Share terminals`, which lets other accounts on this machine watch your terminals, marked experimental | — |
| Permissions | macOS only: which permissions the programs in your terminals have | [This page](#permissions-on-macos) |
| Archived | Archived Projects | [Spaces, Projects and Threads](https://docs.closex.org/thinkterm/spaces.md) |
| Keymap | A keyboard that shows what each key does, and assigns shortcuts to commands | [Keyboard and Commands](https://docs.closex.org/thinkterm/keyboard.md#keymap-page) |
| Command Palette | The palette's shortcut, rows, font size and search | [Keyboard and Commands](https://docs.closex.org/thinkterm/keyboard.md) |
| Import | One-time imports of a WezTerm config, the folders your editors, coding agents and cmux worked in, and Herdr sessions | [This page](#importing) |
| Accessibility | `Show button shapes`, which keeps the round background of the sidebar's add buttons showing | — |
| Backup | Export and import of ThinkTerm's state | [This page](#backing-up-and-restoring) |
| Software Update | Update checks, how often they run, and installing | [This page](#the-update-check), [Updates](https://docs.closex.org/thinkterm/updates.md) |
| About | Version, build, links, and where the files live | [This page](#about-and-bug-reports) |

`Developer` has a switch that shows further pages, for working on ThinkTerm itself rather than for everyday use. The search box above the list finds a page by the settings on it.

### About and bug reports

`Settings › About` shows the version, build, platform and licence, and opens the source code, the release notes, the config file, the data folder, the third-party notices and the privacy policy. `Copy Info` puts the details a bug report needs on the clipboard: the version and build, the target platform and operating system, the config file and data folder paths and, once an update check has run, the latest release it saw. The paths include your home folder's name, so look before pasting it somewhere public.

## Where Settings and the config file overlap

A few choices can be made in both places. In these, Settings has the last word, and each has a way to hand the choice back to the file:

| In Settings | Stands in front of | To follow the config file again |
|---|---|---|
| `Settings › Terminal › Default Shell` | `default_prog`, for new terminals on this machine | Choose `Follow thinkterm.lua (default_prog)`, shown as `System default` when the file sets none |
| `Settings › Terminal › Font Size`, 8 to 48 | `font_size`, applied to open terminals at once | Reset the row |
| A scheme picked through `Settings › Appearance › Effective Color Scheme`, or `Change Theme` in the command palette | `color_scheme` | `Use configured default` in `Change Theme` |
| `Settings › Terminal › Minimum text contrast` | `text_min_contrast_ratio` | `Off` |
| `Settings › Command Palette › Font Size` | `command_palette_font_size` | Reset the row |
| A shortcut set in `Settings › Keymap` | The same key in `keys`, and the built-in default for it | `Reset` on the shortcut, or `Reset All Shortcuts` |
| `Settings › Appearance › Window Opacity` | `window_background_opacity`, and on macOS `macos_window_background_blur` | `Reset to Default` |
| `Settings › Terminal › Cursor shape` set to `Bar` or `Underline` | The shape in `default_cursor_style` | Choose `Block` |
| `Settings › Terminal › Cursor blink` set to `Smooth` or `Hard` | Whether the cursor blinks, which `default_cursor_style` otherwise decides, and with `Hard` also `cursor_blink_ease_in` and `cursor_blink_ease_out` | Choose `Off` |
| `Settings › Software Update › Check Automatically` and `Check Frequency` | `check_for_updates` and `check_for_updates_interval_seconds`, once the row has been used | None; see [the update check](#the-update-check) |

`Default Shell` lists the shells found on this machine and takes effect the next time a terminal opens. Only this machine's terminals follow it, including those kept in its session server; a remote server runs what its own configuration says. A shell that has been uninstalled since it was chosen falls back to the system default rather than leaving no terminal at all.

On Windows the list also offers `Git Bash`, started as a login shell, when Git for Windows is found under Program Files, under `%LOCALAPPDATA%\Programs\Git` or in a Scoop install. Where `wsl.exe` is present it lists up to three WSL distributions, the default one first, each as `<name> (WSL)`; Docker Desktop's own `docker-desktop` distributions are left out.

The font itself is not one of them: `Settings › Terminal › Font Family` only shows the font the config file names, and `font` in the file is where it is changed.

One overlap goes the other way. `enable_scroll_bar = true` in the config file replaces ThinkTerm's overlay scrollbar with WezTerm's own, whatever `Settings › Terminal › Scrollbar` says.

A shortcut set in `Settings › Keymap` is kept in `settings.json` and wins over the same key in `keys` and over the built-in default; the config file itself is never rewritten. A shortcut that runs a Lua function from the config file can only be changed in the file. See [Keyboard and Commands](https://docs.closex.org/thinkterm/keyboard.md#keymap-page).

`Window Opacity` is a slider from 30% to 100% in steps of 5, marked Beta, and it reads `Default` until it has been moved. Moving it sets `window_background_opacity` for every window, and while a macOS window is see-through it adds a background blur of 20 unless the file sets `macos_window_background_blur` itself. It is greyed out on Linux, where `window_background_opacity` in the file still works, and Settings notes that it is not yet stable on Windows; there `win32_system_backdrop` stays the file's to choose, and the frosted glass appears with the opacity once ThinkTerm restarts. See [The Window](https://docs.closex.org/thinkterm/window.md#window-opacity).

Without a scheme picked, a light interface also brings a light terminal scheme of its own — see [The Window](https://docs.closex.org/thinkterm/window.md#two-sets-of-colours).

The colour scheme and the window opacity chosen in Settings take effect whether or not a config file exists.

## Where the config file is read from

ThinkTerm loads the first file it finds, in this order:

1. `--config-file PATH` on the command line
2. `$THINKTERM_CONFIG_FILE`
3. `$WEZTERM_CONFIG_FILE`
4. `thinkterm.lua` next to the executable — Windows only
5. `wezterm.lua` next to the executable — Windows only
6. `~/.config/thinkterm/thinkterm.lua`
7. `~/.config/thinkterm/wezterm.lua`
8. `~/.thinkterm.lua`

Two more command-line flags sit alongside the first. `--skip-config` (`-n`) skips the search entirely and runs on built-in defaults, and `--config name=value`, which can be repeated, overrides single options on top of whatever file loaded. All three are global flags, so they go before the subcommand.

`$THINKTERM_CONFIG_DIR` and `$WEZTERM_CONFIG_DIR` are not part of that search. ThinkTerm *sets* them once a config has loaded, pointing at the directory the file came from, so that programs you launch can find it. Setting them yourself does not select a config.

The two Windows-only entries are deliberate. A config sitting beside the executable takes precedence so that ThinkTerm can be carried on a USB drive and run on a machine that has no config of its own, or someone else's.

The `wezterm.lua` filenames are not leftovers either: they let an existing WezTerm config be dropped into ThinkTerm's own directory without renaming it.

## What ThinkTerm takes from a WezTerm install

Your WezTerm config file is not loaded. None of the locations above point at `~/.config/wezterm/wezterm.lua`, and ThinkTerm never writes to it.

The separation is not total, though, and two paths are worth knowing about.

**`WEZTERM_CONFIG_FILE` is honoured.** It sits in the search order above, so if that variable is exported in your environment — as it may well be if that is how you set WezTerm up — ThinkTerm loads the file it names. WezTerm also sets it in every terminal it opens, so a ThinkTerm started from a shell inside WezTerm loads WezTerm's config. Set `THINKTERM_CONFIG_FILE`, or unset the WezTerm one, to keep the two installations apart.

**WezTerm's config directory is still searched for Lua modules and colour schemes.** `~/.config/wezterm/` and `~/.wezterm/` sit on the Lua `package.path`, and `~/.config/wezterm/colors/` is searched for colour schemes. So `require 'mymodule'` in a ThinkTerm config will find a module you wrote for WezTerm. It is the config file itself that is kept separate, not the whole directory.

## Importing

`Settings › Import` brings in what another program on this computer already has: the settings of a WezTerm config, the folders your editors, coding agents and cmux have worked in, and the sessions of Herdr. Every import happens once — nothing stays linked to its source afterwards.

The page has three steps, `Source`, `Review` and `Complete`. The first lists the sources as cards, and `Continue` reads the one you chose. `Back` returns to the cards, and `Import more` on the last step starts over from them. WezTerm is always listed. `Project folders` appears only when ThinkTerm finds one of the supported editors, coding agents or cmux on this computer, and Herdr, which carries a Beta badge, is offered on macOS, Linux and Windows.

The first time ThinkTerm opens, its welcome page lists the same things when it finds them, each with a button that opens this page on that source's `Review` step — see [Install](https://docs.closex.org/thinkterm/install.md#the-first-launch).

### Settings from WezTerm {#importing-from-wezterm}

The WezTerm card copies chosen settings out of an existing WezTerm config into ThinkTerm's. Afterwards the two files are independent, and a later change to the WezTerm file reaches ThinkTerm only if you import again.

It looks for the WezTerm config in these places and uses the first that exists:

1. `~/.wezterm.lua`
2. `$XDG_CONFIG_HOME/wezterm/wezterm.lua`, or `~/.config/wezterm/wezterm.lua` when `XDG_CONFIG_HOME` is unset
3. `wezterm/wezterm.lua` under each directory in `$XDG_CONFIG_DIRS` — macOS and Linux only

`WEZTERM_CONFIG_FILE` plays no part here, though it does in ThinkTerm's own config search above.

`Continue` loads that file — it is Lua, so it runs as it would in WezTerm — and the `Review` step shows where the WezTerm config and ThinkTerm's own were found, then lists what the file sets that can be imported. Only settings the file sets itself are offered: one it leaves at WezTerm's default is not, so an import never writes WezTerm's defaults over ThinkTerm's. The first time, every one found is selected, with `Select All` and `Clear` beside the count. Fifteen settings can be imported:

`color_scheme`, `window_background_opacity`, `macos_window_background_blur`, `inactive_pane_hsb`, `font`, `font_size`, `line_height`, `cell_width`, `default_prog`, `default_cwd`, `front_end`, `window_decorations`, `disable_default_key_bindings`, `keys`, `key_tables`

That list bounds what the importer copies for you, not what ThinkTerm supports. Any other WezTerm option still works when written into a ThinkTerm config by hand.

`Reload source` reads the file again. It and `Import Selected` are greyed out when no WezTerm config was found, and `Import Selected` also when nothing is ticked. `Import Selected` writes the chosen values, as a plain Lua table, to `~/.config/thinkterm/imported_from_wezterm.lua`. That file belongs to the importer: each import replaces it whole, so a change made in it is lost the next time. Make changes in your own config instead.

What happens next depends on whether you already have a ThinkTerm config in `~/.config/thinkterm/`:

- **Without one**, the import also creates `~/.config/thinkterm/thinkterm.lua`, which loads the imported file and returns the result. Nothing more is needed.
- **With one** — `thinkterm.lua`, or `wezterm.lua` — your file is left untouched, and the imported values do nothing until it loads them. Add this before the line that returns your config, where `config` is the table it returns:

```lua
local imported = require 'imported_from_wezterm'
for key, value in pairs(imported) do
  config[key] = value
end
```

Anything assigned after the loop overrides the imported value.

A config kept anywhere else is not noticed. If yours is `~/.thinkterm.lua`, the `thinkterm.lua` the import creates comes earlier in the search order and is loaded instead of it.

### Project folders from editors, coding agents and cmux

The `Project folders` card, captioned `From editors and coding agents`, offers folders you have worked in. Three kinds of program feed it:

- **Editors.** The folders that VS Code and the editors built on it have opened: VS Code, VS Code Insiders, Code - OSS, VSCodium and its Insiders build, Cursor, Windsurf, Antigravity, Kiro, Positron, Trae and Void. Each is looked for only in its own user-data folder. A multi-root workspace, which an editor records as a `.code-workspace` file rather than a folder, is skipped.
- **Coding agents.** The folders Claude Code, Codex and Pi were started in, read from the session records each keeps: Claude Code's project list in `~/.claude.json`, Codex's sessions under `~/.codex/sessions` and Pi's under `~/.pi/agent/sessions`.
- **cmux**, on macOS. Each workspace's folder, and the folders of its terminals, from the session cmux saves to `~/Library/Application Support/cmux`. They are dated by the last time cmux saved that session.

Only the folder paths and the dates they were last used come over. Nothing inside a folder is read, and neither is what was said in an agent's sessions.

The `Review` step lists the folders, the most recently used first, with a button per editor, agent or cmux to narrow the list. A folder used in the last 30 days starts ticked. One that is already a Project in the chosen Space reads `Already in the sidebar` and starts unticked. `Add to` chooses the local Space they go into — the one Settings was opened from, when that is local — and each ticked folder becomes a Project with one Thread. A line under the list counts the folders that were opened over SSH, in WSL or in a container, which cannot be added yet.

### Sessions from Herdr

The Herdr card imports one Herdr session into a new Space named `Herdr — <session>`. Each Herdr workspace becomes a Project holding one Thread, with that workspace's tabs and panes. It is marked Beta. ThinkTerm looks for sessions in Herdr's config folder, `~/.config/herdr` or `$XDG_CONFIG_HOME/herdr`, and on Windows `%APPDATA%\herdr` unless `XDG_CONFIG_HOME` is set: the default session, and each one under `sessions/`.

`Continue` reads the sessions. If there is one, its preview opens at once; with several, each has a `Preview` button, and a session already imported carries an `Imported` badge. The preview, headed `Review your import`, names the new Space, counts its Projects, primary tabs and terminals, and lists each Project's terminals. `Refresh preview` reads the session again. If the session's structure, running state or saved layout changes after the preview, the import stops and asks for a refresh first. Importing a session that was imported before warns you, because it creates another Space.

Herdr's own files are left unchanged, so starting Herdr again can restore its old layout and resume what it had saved.

Which of two modes applies depends on whether the session is running:

| | Running session | Saved layout |
|---|---|---|
| Button | `Move running session` | `Import layout` |
| What it does | Moves the session's terminals into ThinkTerm; the existing processes keep running | Opens fresh shells in the saved directories |
| Not carried over | Full scrollback and images. A full-screen program may need a redraw | Saved launch commands and agent resume commands are not run |
| Needs | Herdr's `live_handoff` capability, and a compatible protocol version | The saved directories to exist, in a layout format this ThinkTerm understands |

On Windows, Herdr cannot hand over running terminals, so only `Import layout` is there: it reads the layout Herdr saves a few seconds after each change, whether or not Herdr is running, and brings over its tabs, splits and folders, each pane with a fresh shell. That is the shell chosen in `Settings › Terminal › Default Shell`, else `default_prog` when it names a shell, else the command processor. Herdr can stay open, and what runs in it is left alone.

The import is refused, with the reason shown, when a requirement is not met. Both modes need the session to belong to your account and its directory to be accessible. A running session also needs Herdr and ThinkTerm to share the temporary directory, the sockets and the ThinkTerm executable, and on Linux `/tmp` must not be mounted `noexec`.

With Spaces on remote servers, `Which machine to import from` lists `This computer` and each of those hosts; a host that is not connected connects when chosen. Importing from a remote host reads that server's Herdr, and the processes stay on that server. The session must belong to the remote connection's account.

If the connection drops during an import, the page offers `Check import result`, which reconnects and recovers the outcome of that request. Use it rather than starting a second import. When the import finishes, `Open Space` opens the new Space.

## Where the data lives

**macOS**

- Config file — `~/.config/thinkterm/`
- Workspace data — `~/Library/Application Support/ThinkTerm/`
- Terminal state — `~/Library/Application Support/thinkterm/`
- Cache — `~/Library/Caches/thinkterm/`
- Runtime — `~/.local/share/thinkterm/`

**Linux**

- Config file — `~/.config/thinkterm/`
- Workspace data — `~/.local/share/ThinkTerm/`
- Terminal state — `~/.local/share/thinkterm/`
- Cache — `~/.cache/thinkterm/`
- Runtime — `$XDG_RUNTIME_DIR/thinkterm/`

**Windows**

- Config file — `%USERPROFILE%\.config\thinkterm\`
- Workspace data — `%APPDATA%\ThinkTerm\`
- Credentials — `%LOCALAPPDATA%\ThinkTerm\ssh\`
- Terminal state — `%APPDATA%\thinkterm\`
- Cache — `%LOCALAPPDATA%\thinkterm\`
- Runtime — `~/.local/share/thinkterm/`

Four things above are easy to trip over.

**The config path does not follow platform convention.** `~/.config/thinkterm/` is used on every operating system, Windows included — it is not `%APPDATA%`. Config and data therefore live apart on every platform, Linux included, where the config sits under `~/.config` and the data under `~/.local/share`.

**Two data directories differ only in case.** Workspace data — the Space, Project and Thread structure, snippets, and on Unix the SSH host book and its encryption key — is written to `ThinkTerm/`. Terminal and multiplexer state goes to `thinkterm/`. On macOS and Windows the filesystem is case-insensitive by default, so the two normally collapse into a single directory — on a case-sensitive APFS volume they do not. On Linux they are always separate. Both belong to ThinkTerm wherever they land.

**On Windows, credentials are kept out of the roaming profile.** The SSH host book and its encryption key are not in `%APPDATA%` with the rest of the workspace data; they live under `%LOCALAPPDATA%`, so that a domain sign-out does not copy them to a profile server. An older install's copies are moved there as a pair the first time ThinkTerm needs them. The browser listener's certificate and private key are kept there for the same reason.

**The runtime directory does not follow convention either.** The multiplexer socket, the pid file and the TLS material live in `$XDG_RUNTIME_DIR/thinkterm/` on Linux, and fall back to `~/.local/share/thinkterm/` on macOS and Windows — a `.local/share` path on two platforms that otherwise have nothing there. It is worth knowing when `thinkterm cli` cannot find a server.

A Notes Vault is not stored in either. It is a directory you choose, holding ordinary Markdown files; ThinkTerm records only the binding to it.

## Data carried over from WezTerm

When WezTerm's data directory exists — `~/Library/Application Support/wezterm/` on macOS, `~/.local/share/wezterm/` on Linux, `%APPDATA%\wezterm\` on Windows — ThinkTerm copies four things from it into its own terminal-state directory as it starts:

| Item | What it is |
|---|---|
| `plugins` | The Lua plugins `wezterm.plugin.require` has fetched |
| `recent-commands.json` | The command palette's recent commands |
| `recent-emoji.json` | The character selector's recent picks |
| `repl-history` | The debug overlay's Lua history |

Each is copied only while ThinkTerm has none of its own, so in practice it happens once, and it never overwrites anything. WezTerm's copies stay where they are, and nothing else in that directory is read.

## Backing up and restoring

`Settings › Backup` exports ThinkTerm's own state to a folder you choose, and brings it back from one. Nothing is backed up unless you export it.

An export creates a folder named `ThinkTerm Backup` followed by the date and time. It holds plain JSON files, listed in a `thinkterm-backup.json` beside them:

| File | Holds |
|---|---|
| `workspace_threads.json` | Spaces, Projects and Threads |
| `ssh_hosts.json` | ThinkTerm's own SSH host book |
| `snippets.json` | Snippets |
| `recent-commands.json` | Recent commands |

The Spaces are written from what ThinkTerm holds in memory, so the export has what is on screen even if the last save has not reached the disk. On macOS and Linux each file is written with mode `0600`.

`secret.key` is deliberately left out. Saved passwords travel in the host book still encrypted with this machine's key, so on another machine they have to be entered again. The Lua config, `settings.json` and a Notes Vault are not included either; they are ordinary files to copy as you would any other.

An import cannot swap those files under a running window, which holds them in memory and would write them back. So it checks every file of the backup first, then waits: it is applied by the next launch that finds no other ThinkTerm window running, before anything reads the state, and the page offers `Quit ThinkTerm` to get there. Each file it replaces is kept beside the original as `<name>.before-import-<time>.json`. If any file cannot be put in place, the ones already replaced get back what they had, so a failed import changes nothing; the failure is shown on the Backup page and is not retried.

One thing is merged rather than replaced. Spaces that the local session server still holds are added back the next time ThinkTerm connects to it, with the server's names winning where both have one. An import brings back what the backup holds; it does not remove what the server kept.

## Interface language

`Settings › General › Language` changes the language of ThinkTerm's own interface immediately, with no restart. `Follow System` takes it from your system settings and shows which language that resolved to.

The interface ships in English, Simplified Chinese, Japanese, French, German and Russian. This covers ThinkTerm's own chrome only — terminal content, and everything WezTerm renders, is unaffected.

Command names follow the language too: in the command palette, on the macOS menu bar and on the Keymap page. The menu bar is rebuilt the moment you switch. The palette remembers what you run by its English name, so its recents survive a change of language, and an English word still finds a command through the menu it sits in.

On macOS, Chinese needs PingFang, a font that macOS downloads on demand. When this Mac reads Simplified Chinese before Japanese or Korean and has no PingFang, ThinkTerm draws Han characters, CJK punctuation and kana in Hiragino Sans GB, which ships with macOS, so that characters used only in Simplified Chinese are not drawn as empty boxes. `Settings › General › Language` then adds a note under the row saying so: download `PingFang SC` in Font Book and restart ThinkTerm to use it. Where PingFang is installed, or this Mac does not read Simplified Chinese first, Hiragino Sans GB only draws Han characters that no other font has.

## Renderer

`Settings › General › Renderer` chooses between OpenGL and WebGpu for the terminal windows and the Settings window. Unlike the language, it takes effect the next time ThinkTerm starts. Left unchosen, it follows `front_end` in the config file, which is `OpenGL` on Windows and `WebGpu` everywhere else unless you set it — so those are the defaults, and `OpenGL` or `Software` there means OpenGL.

One of them failing is not fatal. When the renderer in force cannot start — no usable WebGpu adapter, or an OpenGL too old to run ThinkTerm's shaders — the window opens on the other one instead, and the Settings window does the same, so a machine where only one of them works is not locked out of the very control that would switch it. Only when both fail does the window not open. `front_end = 'Software'` never falls back.

## Cursor

`Settings › Terminal › Display` has two rows for the cursor.

`Cursor shape` is `Block`, `Bar` or `Underline`. `Block`, the default, leaves the shape to `default_cursor_style` in the config file, which is a block unless it says otherwise. `Bar` and `Underline` replace the shape the config file gives and keep its blink.

`Cursor blink` is `Off`, `Smooth` or `Hard`. `Off`, the default, leaves the cursor as the config file has it: steady, unless `default_cursor_style` asks for a blinking one. `Smooth` and `Hard` make the cursor blink. `Smooth` fades it in and out along the curve of `cursor_blink_ease_in` and `cursor_blink_ease_out`; `Hard` switches it between fully on and fully off, whatever those say. Each phase lasts `cursor_blink_rate`, which is 600 ms unless you set it.

A program that sets its own cursor style, as an editor does when it changes modes, keeps it. Both choices reach every open window at once, without reloading the config.

## The update check

The desktop app looks for a new release by itself, and `Settings › Software Update` has two rows for it: `Check Automatically`, a switch, and `Check Frequency`, which offers `Every 12 hours`, `Daily` and `Weekly` and is greyed out while the switch is off. Until you use a row, it follows the config file, `check_for_updates` for the switch and `check_for_updates_interval_seconds` for the frequency; once you have used it, the row wins and the file's option is no longer read. A change applies within a minute, without a restart.

A check is due once the frequency has passed since the last one on record, whichever check that was — one you started with `Check for Updates` counts. So a weekly check stays weekly across launches: a few seconds after ThinkTerm starts it checks only if that long has gone by. `Last Checked` shows when. With the switch off, ThinkTerm looks only when you press `Check for Updates`. [Updates](https://docs.closex.org/thinkterm/updates.md) covers the rest of the flow, and [Privacy and Security](https://docs.closex.org/thinkterm/privacy.md) what the request carries.

## Restoring pane contents

When ThinkTerm's terminals stop in an orderly way — you quit ThinkTerm with its terminals inside it, the computer shuts down or you log out, or the session server is stopped — ThinkTerm can keep what each pane showed and put it back the next time that Thread's layout is restored. `Settings › General › Restore pane contents` is the switch.

It is on by default on the machine the desktop app runs on: the app turns it on as it starts, unless it has been set either way, and turning it off is written down so that it stays off. It covers this computer's terminals only. On another machine, such as a remote host, it is off until you run `thinkterm cli restore-output on` in a terminal there; `restore-output off` turns it off again and deletes what was kept, and `restore-output status` says which it is.

What is kept is each local pane's main screen and its scrollback, as text and colours, up to 10,000 rows. The frame of a full-screen program, and images, are not kept. Before anything is written, whatever looks like a password, a token or a private key is replaced with `•`, so the original never reaches the disk. The records sit in `kept-output`, in the terminal-state directory [listed below](#where-the-data-lives). One is removed as soon as its layout is back, and after 15 days if the Thread is never reopened.

A restored pane shows the old output above its new shell, then a dim line, `── closed … · restored … ──`, with both times, and the shell starts under it. It is the old output, not the old process: nothing that was running starts again.

A crash or a power cut leaves no time to save, so those bring the layout back without the output.

## Folder access on macOS

A Project is bound to a folder, and macOS decides whether ThinkTerm may read it. When access is refused, the Project page says so and points at `System Settings › Privacy & Security › Files and Folders › ThinkTerm`.

Two details are specific to ThinkTerm.

**The Project context menu offers `Grant Folder Access…`.** It runs the same picker as the blocked page, then opens the Project's first Thread, so the grant is proved end to end rather than assumed.

**The session server asks under its own name.** When local terminals run in a background session server, that daemon carries its own macOS privacy identity, so the permission it needs is not necessarily the one you already granted to the GUI. If the refusal came from the server, the page says so and offers to open System Settings at the right place.

A refused folder is an error rather than a silent fallback. Earlier builds dropped back to the home directory without saying anything, which meant the refusal never reached the interface and could never be granted.

`THINKTERM_NO_PRIVACY_DISCLAIM` in the environment skips the step that gives the daemon that separate identity.

## Permissions on macOS {#permissions-on-macos}

macOS counts every program you run in ThinkTerm's terminals as ThinkTerm. `Settings › Permissions`, a page that exists on macOS only, reports four permissions those programs can have: `Full Disk Access`, `Screen & System Audio Recording`, `Microphone` and `Camera`. Each row reads `Allowed`, `Not allowed`, `Denied`, `Not asked yet` or `Unknown`, and its `Open System Settings` button opens the matching pane of System Settings. Looking never makes macOS ask you anything. Microphone and camera read `Not asked yet` until a program first wants one. Full Disk Access and screen recording cannot be told from never having been asked, so they read `Not allowed`.

`Full Disk Access` carries a warning of its own. Once it is allowed, any program in a terminal — scripts and coding agents included — can read and write Desktop, Documents, Downloads, Mail and other apps' data without macOS asking again. Turn it on only when you need it, and off in System Settings when you are done. It also has to be added by hand: click + in System Settings and drag the program into the list.

Which program that is depends on where local terminals run. With them kept in the background session server, which is the default, the permissions are the server's own, and the last row is headed `ThinkTerm Background Service`; otherwise it is `ThinkTerm`. A permission allowed for one is not the other's, which is why [a folder you allowed for ThinkTerm may need allowing again](#folder-access-on-macos). The row's button reveals the program in Finder. [Privacy and Security](https://docs.closex.org/thinkterm/privacy.md#permission-prompts-on-macos) explains why the prompts name ThinkTerm.

## Sounds, notifications and the Dock count

The `Notifications` card on `Settings › Workspaces` has up to three ways to be told about a Thread: `Notification Sounds`, `System Notifications` and, on macOS only, `Dock Icon Count`, which puts on ThinkTerm's Dock icon the number of Threads that have something you have not looked at yet. All three are on by default.

Under each is a choice of moments, `Finished`, `Needs You` and `Error`, all ticked until you take one out. Taking `Finished` out of `Notification Sounds` silences the sound for a finished Thread and leaves the one for a wait; the other two work the same way. The Dock count includes a Thread that is Done only while `Finished` is ticked, and one that needs attention only while `Needs You` or `Error` is ticked for what it is waiting on. [Agent Status](https://docs.closex.org/thinkterm/agents.md) describes the states and the sounds, and [Privacy and Security](https://docs.closex.org/thinkterm/privacy.md) what a system notification carries.

## The option reference

Almost every option is WezTerm's, unchanged, so the reference for them is upstream:

- [WezTerm configuration files](https://wezterm.org/config/files.html)

Three caveats.

ThinkTerm forked WezTerm on 31 March 2026, so options added upstream after that date may not exist here yet.

Some options keep their names and meanings but not their defaults; they are listed [below](#defaults-that-differ-from-wezterm).

And ThinkTerm adds options of its own, which upstream does not document:

| Option | Controls |
|---|---|
| `agent_status_detection` | Whether session status detection runs |
| `web_servers` | The browser listeners a multiplexer opens when it starts. See [Web](https://docs.closex.org/thinkterm/web.md) |
| `ui_colors` | The colours of ThinkTerm's own interface. `colors` remains the terminal's palette. See [Interface colours](#interface-colours) |
| `remote_mux_auto_claim_on_open` | Whether connecting to a remote host on purpose takes control of its terminal at once |
| `note_spellcheck_enabled` | Spell check in the Markdown editor |
| `note_remote_images_enabled` | Loading images from remote URLs in notes |
| `note_reading_max_width` | The widest a note's text column grows, in logical pixels (points on macOS). `820` by default; in a wider Notes area the column is centred |
| `right_sidebar_search_respects_gitignore` | Whether file search skips ignored files. The file tree still shows them |
| `unfocused_fps` | How often a visible but unfocused window repaints for terminal output. `10` by default; `0` turns the limit off |
| `kitty_image_memory_budget_mib` | How much image data each pane may keep, in MiB. `128` by default; `0` means unlimited. See [The Window](https://docs.closex.org/thinkterm/window.md#images-in-the-terminal) |
| `live_overview_preview_refresh_ms` | How often a Live Overview card re-reads its terminal. `300` by default |

`text_min_contrast_ratio` is upstream's option, but `Settings › Terminal › Minimum text contrast` sits over it: a ratio chosen there wins, and `Off` leaves the config file's value in charge.

Plugins are not configured in the Lua file. See [Plugins](https://docs.closex.org/thinkterm/plugins.md).

## Defaults that differ from WezTerm

These options mean what upstream says, but start from a different value when the config file leaves them unset:

| Option | WezTerm | ThinkTerm |
|---|---|---|
| `front_end` | `OpenGL` | `WebGpu`, but `OpenGL` on Windows |
| `initial_cols` | `80` | `120` |
| `initial_rows` | `24` | `32` |
| `max_fps` | `60` | `120` |
| `default_cwd` | unset | your home folder |
| `native_macos_fullscreen_mode` | `false` | `true` |
| `color_scheme`, on macOS | unset | `Apple System Colors`, when `colors` is not set either |
| `adjust_window_size_when_changing_font_size`, on macOS | unset | `false` |
| `cursor_blink_rate` | `800` | `600` |
| `command_palette_font_size` | `14` | `13` |

`default_cwd` matters when a terminal has nothing else to go on — no directory of its own and none reported by the terminal it opened from. It then starts in your home folder rather than in whatever directory ThinkTerm itself was started from. With `adjust_window_size_when_changing_font_size` off, changing the font size keeps the window's size and changes how many rows and columns fit in it.

Two more are worked out from the terminal's palette when unset. The inactive-pane dimming, `inactive_pane_hsb`, is lighter on a light colour scheme than on a dark one rather than always the same, and the pane divider colour is blended out of the terminal background. Set them explicitly and they behave exactly as upstream documents.

The colour scheme can also be decided in Settings rather than in the file; see [Where Settings and the config file overlap](#where-settings-and-the-config-file-overlap).

## Interface colours

`ui_colors` overrides the colours ThinkTerm draws around the terminal — the sidebars, the tab bar, the strips above the panes, the Settings window, the menus. Every slot is optional, and one left unset keeps the colour the theme would have used, so a table that names two colours changes two colours:

```lua
config.ui_colors = {
  sidebar_bg = '#282828',
  accent = '#d79921',
}
```

Colours are written as they are anywhere else in the config file. The slots:

| Group | Slots |
|---|---|
| Surfaces | `window_bg` (behind everything), `sidebar_bg`, `workspace_sidebar_bg` (a recessed sidebar ground; the page list in Settings), `header_bg` (the strip above the tabs), `card_bg` (a card on `window_bg`), `group_bg` (rows grouped inside a card), `separator` |
| Controls | `control_bg`, `control_hover_bg`, `control_pressed_bg`, `control_border`, `track_off` (the off half of a switch) |
| Sidebar | `sidebar_button_bg`, `sidebar_button_hover_bg`, `sidebar_row_hover_bg`, `sidebar_row_pressed_bg`, `sidebar_row_active_bg`, `sidebar_row_active_border` |
| Selection and accent | `selected_bg`, `selected_text`, `accent`, `accent_hover`, `on_accent` (text and icons on the accent), `danger` (destructive actions and errors) |
| Text | `text`, `secondary_text`, `muted_text` |
| Other | `scrollbar_thumb` (the overlay scrollbar), `spelling_error` (the underline under a misspelling) |

Under `Follow terminal colours` the surfaces and text move onto the terminal's background, but `accent`, `accent_hover`, `on_accent`, `selected_bg`, `danger` and `spelling_error` keep their designed values: a scheme's own blue is often hard to read on that scheme's background. Set them here if you want them to match.

## Environment variables

Every terminal ThinkTerm starts has these set:

| Variable | Value |
|---|---|
| `THINKTERM` | `1` |
| `THINKTERM_VERSION` | The ThinkTerm version |
| `TERM_PROGRAM`, `TERM_PROGRAM_VERSION` | `WezTerm` and the version. Kept so tools that recognise WezTerm keep working; test `THINKTERM` to tell ThinkTerm apart |
| `WEZTERM_PANE` | The pane's id, which `thinkterm cli` uses as its default pane |
| `WEZTERM_UNIX_SOCKET` | The multiplexer socket, when ThinkTerm has one |
| `THINKTERM_MUX_SERVER_ID` | Which multiplexer the pane runs in. `thinkterm tui` uses it to refuse attaching to the server it is itself running inside |
| `THINKTERM_CONFIG_FILE`, `WEZTERM_CONFIG_FILE` | The config file that loaded, when one did |
| `THINKTERM_CONFIG_DIR`, `WEZTERM_CONFIG_DIR` | The directory it loaded from |

And ThinkTerm reads these:

| Variable | Effect |
|---|---|
| `THINKTERM_CONFIG_FILE`, `WEZTERM_CONFIG_FILE` | The config file to load. See [the search order](#where-the-config-file-is-read-from) |
| `THINKTERM_DISABLE_SOUND` | Set to anything, silences the notification sounds whatever Settings says |
| `THINKTERM_UPDATE_TOKEN` | A GitHub token the update check sends, to lift the anonymous rate limit. A `GITHUB_TOKEN` in the environment is never sent. See [Updates](https://docs.closex.org/thinkterm/updates.md) |
| `THINKTERM_NO_PRIVACY_DISCLAIM` | Skips the separate macOS privacy identity of the session server. See [Folder access on macOS](#folder-access-on-macos) |
| `THINKTERM_TUI_CONFIG` | Where `thinkterm tui` reads its settings. See [TUI](https://docs.closex.org/thinkterm/tui.md) |
| `WEZTERM_LOG` | Log filtering, as in WezTerm |

## Related

- [The Window](https://docs.closex.org/thinkterm/window.md) — what the Appearance and Terminal pages change on screen
- [Keyboard and Commands](https://docs.closex.org/thinkterm/keyboard.md) — the command palette and its Settings page
- [Remote](https://docs.closex.org/thinkterm/remote.md) — the host book, and keeping local terminals in a session server
- [Plugins](https://docs.closex.org/thinkterm/plugins.md) — what plugins add and where they are switched
- [Updates](https://docs.closex.org/thinkterm/updates.md) — `check_for_updates` and the rest of the update flow
- [Privacy and Security](https://docs.closex.org/thinkterm/privacy.md) — what ThinkTerm sends over the network, and when
