Skip to content

Configuration

Configuration lives in ~/.config/infiniterm/, four files in two pairs, plus the folder ui-themes/ for the look of the app (see UI themes):

settings.json yours, only what you changed
settings.default.json every setting with its default, commented
keybindings.json yours, only what you rebound
keybindings.default.json every binding, commented

The .default files are rewritten at every launch, so editing them does nothing. Read them to see what exists, copy a line into your file and change it there. An upgrade never touches your files; new settings appear in the defaults. Each of the four files starts with a link to the reference page for its keys. Your files can be symlinks into a dotfiles repo: a save writes the file the link points at and keeps the link.

Cmd , opens settings and Cmd Shift , keybindings, each as a pair of editor cards. Changes apply on save, no restart, also when you save from another editor. A file that does not parse, or has the wrong kind of value (text where a number goes), is not applied: your previous settings stay, and the status bar says settings.json not applied with the reason until you save a valid file. keybindings.json works the same way. Unknown names, numbers out of range and trailing commas are accepted. In settings.json the setting names complete as you type (see JSON with a schema).

Keys are flat and dotted, so an override is one line:

{
"terminal.fontSize": 16,
"ui.showFps": true
}

Comments and trailing commas are allowed. Every setting is on the settings reference.

{
"cmd+shift+k": "card.clear",
"cmd+k": null
}

A new keybindings.json starts with commented examples; delete the // to switch one on. They are another chord to leave a locked editor, turning off the single-Escape unlock, F2 for go-to-line with a when, moving workspace tabs and stepping through themes from the keyboard, Cmd O to open a file and Cmd Alt C to start Claude Code.

A binding set to null is removed, which gives the key back to the terminal. A chord must hold cmd, with two exceptions: ctrl plus a digit, and ctrl+tab. A terminal needs Ctrl, Alt and bare keys for itself, but Ctrl plus a digit mostly means nothing to a shell. (On an xterm-compatible terminal Ctrl 3 sends Escape; if you live in vim, rebind it.) A third exception is a chord with a when that is false in a terminal, described next.

Give a chord an object with a when and the binding applies only while that holds:

{
"cmd+shift+e": { "command": "browser.leave", "when": "editorTextFocus" },
"cmd+t": { "command": null, "when": "editorTextFocus" }
}

The first line binds Cmd Shift E to browser.leave only in a locked editor. The second unbinds Cmd T there and nowhere else ("command": null removes the binding in that situation). To give one chord different commands in different places, use a list of objects. The last one whose when holds wins. The plain shape above still works.

A when joins conditions with && and ||, negates with !, compares with == and !=, and groups with parentheses. Text goes in single or double quotes, and true and false are allowed. For example cardKind == 'editor' && !editorHasSelection.

Key True when
cardKind the focused card’s kind: terminal, editor, browser, diff, transcript, page or none
terminalFocus a terminal card is focused
editorFocus an editor card is focused, arrowed to or locked
editorTextFocus an editor card is focused and locked
browserFocus a browser card is focused
cardLocked the focused editor or browser card holds the keyboard
overlay what is open over the canvas: none, palette, prompt, omnibox, shortcuts, find or switcher
phantomFocus an empty slot is focused
multiSelection more than one card is selected
suggestWidgetVisible the editor’s completion list is open
findWidgetVisible the find bar is open
editorHasSelection the editor has text selected

A key without cmd, such as Escape, F2 or a letter, can be bound this way, but only with a when that is false in a focused terminal: editorTextFocus, browserFocus or phantomFocus do, cardLocked does not. That way a binding can never take a terminal’s input. Otherwise the file reports an error and that binding is ignored. A mistake inside a when, such as an unknown key or a bracket left open, is reported the same way. The shortcuts panel and ift commands list only plain bindings so far, not the ones with a when.

Chords follow the physical key, not the character your layout prints on it, so cmd+= is the key right of - on every keyboard. Cmd H, Cmd M and Cmd Q belong to the menu and cannot be rebound. Every command id is on the commands reference. In the app, the shortcuts panel (Cmd /) shows each command’s id beside its label and finds commands by it: Up and Down highlight one, and Cmd C copies its id, ready to paste into keybindings.json.

All 500-odd schemes from iTerm2-Color-Schemes ship with the app; Violite is the default. Your own .itermcolors files go in:

~/Library/Application Support/dev.ekinertac.infiniterm/themes/

“Theme: switch…” in the palette lists the theme you have first, marked active, then every other one A to Z. Each one is applied as you move through the list, with the canvas left undimmed so the cards show its real colours, and the list keeps its order while you do. Escape, or a click outside the list, puts back the one you started on; Enter keeps the new one. The theme colours the app’s chrome, the editor’s syntax and the card labels too.

The look of the app is not in settings.json. It lives in ui-themes/<name>.json in the config folder, one file per look. "ui.theme" in settings.json picks the file (default "default"). “UI theme: switch…” in the palette lists them and previews each one live; Escape puts the old look back. A file applies as you save it. A file with a mistake is not applied: a notice says why and the look you had stays. To make another look, copy ui-themes/default.json and change it. The default file is created at the first launch and lists every key as a commented example with its default.

A ui theme holds these keys:

  • the window: ui.windowOpacity, ui.windowBlur, ui.windowColor, ui.icon;
  • the background picture: ui.backgroundImage, ui.backgroundImageFit, ui.backgroundImageInterval, ui.backgroundImageFade;
  • the cards and canvas: ui.cardRadius, ui.cardOpacity, ui.showGrid, ui.inactiveDim, ui.unfocusedDim, ui.hideLabelWhenFitted, ui.cardGradient, ui.cardGradientAngle, ui.focusRingWidth;
  • the interface font: ui.fontFamily, ui.fontSize, ui.fontWeight, ui.fontWeightBold;
  • the bars: titlebar.background, titlebar.border, statusbar.background and statusbar.border. A background is #rgb, #rrggbb, canvas, card, bar or transparent. A border is a colour or none;
  • the workspace tabs: tab.background, tab.activeBackground, tab.gradient, tab.opacity, tab.border, tab.activeBorder, tab.separator, tab.radius, tab.paddingX, tab.icons, tab.iconStyle, tab.iconMap, tab.iconSize;
  • the editor and browser card tabs: the workspace tab keys under cardTab., without the icon keys;
  • the card edges: card.border, card.borderOpacity, card.activeBorder, card.activeBorderOpacity;
  • the card header: card.header, card.headerIcons, card.headerIconSize, card.headerButtons, card.headerButtonsActiveOnly.

The first launch that has this feature moves any of these keys out of your settings.json into ui-themes/default.json. Your comments stay, and a note in settings.json says where the keys went. After that, a look key typed into settings.json is ignored, with a notice. In a window opened with ift join, the host’s settings apply but the look is your own.

Everything in this part and the next two is off until a theme sets it. A theme without these keys looks as before.

ui.cardGradient takes two colours, #rgb, #rrggbb or #rrggbbaa, for example ["#3a2c27", "#1f1a18cc"]. ui.cardGradientAngle is the direction in degrees, 0 to 360. The default, 180, runs top to bottom. ui.cardOpacity still applies to both colours, so the gradient stays translucent over the window. Browser cards stay flat. With a gradient on, the gradient reaches the focus ring and a resting card has no dark hairline border. A card with an agent in it keeps its coloured border.

ui.focusRingWidth sets the width of the focus ring in screen pixels, 0 to 10, at every zoom. With 0, the default, the ring is 3, 4 or 5 pixels, by zoom. The gap between a card and its ring is as wide as the ring, so a thinner ring also closes the gap.

  • tab.background is the fill of a tab that is not chosen, and tab.activeBackground is the fill of the chosen tab. Values are #rgb, #rrggbb, #rrggbbaa, canvas, card, bar or transparent. Unset, an idle tab has no fill.
  • tab.gradient is two colours, top to bottom, for the chosen tab. It replaces tab.activeBackground. [] keeps the flat fill.
  • tab.opacity, 0.1 to 1, multiplies the alpha of every tab fill.
  • tab.border and tab.activeBorder are a one pixel outline, a colour or none.
  • card.border is a resting card’s edge and card.activeBorder the focused card’s ring, each a colour or none. card.borderOpacity and card.activeBorderOpacity, 0 to 1, multiply their alpha. Agent state colours still paint over the resting edge. A locked card’s ring and the blue ring on the rest of a selection keep their colours. Unset keeps the built-in edge, which is not drawn while ui.cardGradient is set.
  • tab.separator is a thin line between idle tabs, a colour or none. No line touches the chosen tab.
  • tab.radius rounds the corners, 0 to 40 pixels.
  • tab.paddingX is the space on each side of a tab’s content, 0 to 40 pixels. Unset keeps 6.
  • tab.iconSize is the size of the tab icon in pixels, 6 to 64. Unset keeps the built-in size, which follows the tab’s text.
  • The tabs at the top of editor and browser cards take the same keys under cardTab.: cardTab.background, cardTab.activeBackground, cardTab.gradient, cardTab.opacity, cardTab.border, cardTab.activeBorder, cardTab.separator, cardTab.radius and cardTab.paddingX. Values and defaults are the workspace tabs’, so the same values make both look alike. A folder editor’s name over its file tree is drawn like an idle card tab.

tab.icons: true puts an icon on each tab for the program that runs in the workspace’s focused card. When no card is focused, it uses the first card. A shell at its prompt shows the shell. tab.iconStyle is "glyph" (default) or "image". A glyph is a Nerd Font glyph, and the app has glyphs for common shells, runtimes and coding agents. An image is a file in ui-themes/<theme name>/icons/, where the theme name is the file name without .json. The file is <program>.svg or <program>.png, or default.svg or default.png for the rest. A program with no file gets a glyph. tab.iconMap maps a program name to a glyph or to a file name ending in .svg or .png, for example {"bun": "bun.svg", "default": ""}. A file name must be a plain name, with no folder in it. The key default covers programs not listed.

While a coding agent is working or waiting on you in a card, that card’s icon is the agent’s, not the program’s. The agent’s name is the key (claude, codex, pi, …), and agent covers all agents not listed; the built-in glyph is a robot. A card whose agent is done or failed shows its program again. The card header uses the same choice.

card.header: true adds a header row inside terminal, transcript and page cards. It shows the card’s name, and the corner chip is not drawn while the header is on. The header is 30 pixels high at 100% zoom, and the card’s body gets the rest. A terminal then has fewer rows and the same number of columns. Dragging the header moves the card. Editor, diff and browser cards keep their own tab strip and get no header.

  • card.headerIcons: true shows the program’s icon at the left. It uses tab.iconStyle and tab.iconMap, so a card and its tab agree. card.headerIconSize sets its size in pixels, 6 to 64; unset keeps the built-in size.
  • card.headerButtons lists the buttons, left to right, from "split-right", "split-down", "maximise" and "close". Each runs the command of that name. No button shows unless listed.
  • card.headerButtonsActiveOnly: true shows the buttons only on the focused card. The other headers show the icon and the name, and nothing can be clicked where the buttons would be.

With the header on, the chip’s agent colour is not drawn. The card’s border still shows the state.

The keys below go in a ui theme file, not in settings.json.

ui.windowOpacity (0.1 to 1) lets the desktop show through the canvas, title bar and status bar, and ui.windowBlur blurs it. ui.backgroundImage puts a picture behind the canvas: "dusk", "aurora" or "ember" come with the app, or give a path to your own; ui.backgroundImageFit is "cover" (crop to fill), "contain" or "top-left" (fill, but crop only the right and bottom) or "actual-size" (the picture’s own size from the top-left corner, never rescaled when the window is resized). A picture is opaque, so it hides the desktop. Give a list instead, ["dusk", "~/Pictures/lake.jpg"], and the pictures take turns in that order: each stays ui.backgroundImageInterval seconds (300 by default), with a crossfade of ui.backgroundImageFade seconds (2 by default, 0 for a cut). An entry that does not exist is skipped. Only two pictures are in memory at a time, so a long list costs no more than a short one. ui.cardOpacity lets the canvas and the picture show through card backgrounds while the text stays solid; browser cards stay opaque. ui.cardRadius rounds the corners of cards and group frames, in pixels at 100% zoom (0 to 40, default 0 for square corners); the border, the focus ring, the label and the tabs follow it. The window’s own corners stay as macOS draws them. "ui.showGrid": false leaves the canvas plain.

“Window: change the title bar and Dock colour” in the palette tints the title bar and the workspace tabs, and badges the Dock icon. Pick a named colour (each one shows as you move through the list, Escape puts it back) or “Custom hex colour…” for a code like 3b82f6. The choice is saved as ui.windowColor in the ui theme in force, a name or a hex code, empty for none. In an ift connect window the same command sets that server’s colour instead, and “Default: from the host name” goes back to the automatic one.

ui.icon picks the picture in the Dock: "default" (four outlined cards) or "filled". It changes when you save the file or switch the ui theme. The window colour badge stays on top of it. The icon in Finder and Launchpad stays the default, because a running app cannot change it.

The right end of the status bar shows the version and the build, like 0.6.9 · build 1403. ui.hideVersion and ui.hideBuildNumber (both off by default) hide one part each. They are settings.json keys, not theme keys.

ui.statusBarPwd (off by default) shows the focused terminal card’s folder in the status bar, with your home folder as ~. It is left out when the card’s label already is the path, and a long path is cut so the build number stays. ui.statusBarPwdSide puts it on the "left" (default) or the "right". "ui.hideBuildNumber": true in settings.json hides “build N” at the right end of the status bar.

The workspace tabs, card labels, status bar, palette and dialogs use the macOS system font. ui.fontFamily changes it (a font name, for example "Menlo" to bring back the monospace look), ui.fontSize sets its size, and ui.fontWeight and ui.fontWeightBold set the normal and bold weights. Terminal, editor and document text keep their own settings (terminal.fontFamily and the rest), and Cmd = and Cmd - still change only the terminal. A change applies as you save. These keys are in the ui theme too.

Cmd Ctrl S lists your snippets and pastes the one you pick into the focused card, the same way Cmd V would, so Claude takes a multi-line prompt as one block.

Snippets are plain files in ~/.config/infiniterm/snippets/, one per snippet. The file name without its extension is the snippet’s name; the contents are pasted as written, less one trailing line break. The picker’s last row opens the folder.

infiniterm can push a message to your phone when a card starts waiting on you, fails or finishes, so you find out while you are away from the Mac. It uses ntfy: install the free ntfy app on the phone and subscribe to a topic.

Push is off until the file ntfy-topic exists in ~/.config/infiniterm/ with one line: your topic, made of letters, digits, _ and -, at most 64 characters. Use 20 or more random ones. The topic is the only secret (anyone who knows it can read and post to it), so it is a file and not a setting, because settings.json is often synced or committed. Do not commit ntfy-topic. infiniterm never creates or writes it, and reads it each time a push is sent, so changing it needs no restart.

  • notify.waiting, notify.failed and notify.done (all on) choose which states push.
  • notify.skipWhenFocused (on) sends nothing while the infiniterm window is in front, because you can see the card colours. Another app in front still pushes.
  • notify.ntfy.server (https://ntfy.sh) names the server, so a self-hosted ntfy works.

The title is the card’s number and name, the body says what happened. A state that was already there when the app started, and a repeated report of the same state, send nothing. A failed send is one line in agent.log.

The infiniterm iPhone app can also be told when a card needs you while the app is closed. When its “Notify when the app is closed” switch is on, it registers itself on the Mac with ift push add, and the file push-devices in ~/.config/infiniterm/ then holds one line per phone. Those ids let anyone push to the phone, so do not commit that file. ift push list shows the phones (the start of each id only) and ift push remove <id> removes one. These pushes use the same notify.* settings, and so does the phone’s list of cards: with notify.done off, a done card is neither a banner nor a row in the phone’s widget. notify.skipWhenFocused holds back banners only, not the list. The pushes go through a small relay that sees card and workspace names and the Mac’s computer name (for example “Ekin’s Mac mini”) on the way.

  • Your shells, one per card, started as your login shell with INFINITERM_CARD_ID in the environment. Nothing is typed into them that you did not type.
  • ~/.config/infiniterm/ for settings, keybindings and snippets, and ~/Library/Application Support/dev.ekinertac.infiniterm/ for the canvas, drafts, themes, the window frame, agent.log, the browser profile and address-bar history. Deleting workspace.json there resets the canvas and leaves your settings alone. Any of these files can be a symlink: every file infiniterm writes is written where the link points, and the link stays.
  • macOS privacy prompts, only when a program in a card asks for something macOS guards (Photos, the camera, your Documents folder and others). Nothing is asked at install or launch. See privacy prompts.
  • ps, lsof and git as subprocesses, to label cards and for the diff and blame cards.
  • The network only from browser cards, the update check, phone notifications to the ntfy server you chose (and only once you create ntfy-topic), address-bar suggestions from Google, which are off unless you turn on browser.suggestions, one request to api.lemonsqueezy.com when you run ift licence <email> <key>, ssh to the hosts you name in ift connect, and a download from GitHub for ift connect --install.

No account, no telemetry.