v0.2.5 16b95cb

themes.md RawHistoryFolder

zot themes

zot themes are JSON files that override any subset of the built-in light/dark TUI theme. Nothing is required: a theme can change one color, only the spinner, only syntax highlighting, or all of them. Missing values inherit from zot's built-in default for the detected terminal background.

Where themes live

User themes are discovered from:

$ZOT_HOME/themes/*.json

Open /settings and choose color theme to switch. Changes are saved in $ZOT_HOME/config.json and apply immediately. If the selected file is deleted later, zot resets the setting to the built-in auto (default) theme.

Theme files bundled with extensions are discovered in-place from loaded extension directories:

$ZOT_HOME/extensions/<extension>/theme.json
$ZOT_HOME/extensions/<extension>/themes/theme.json
<project>/.zot/extensions/<extension>/theme.json
<project>/.zot/extensions/<extension>/themes/theme.json

zot does not copy extension themes into $ZOT_HOME/themes; extension owned themes stay in the extension directory. The settings picker shows source info such as from extension my-theme-extension.

Minimal themes

All of these are valid.

Metadata only:

{
  "name": "my-theme",
  "description": "Metadata only; all visuals inherit zot defaults."
}

One shared color for both light and dark terminals:

{
  "name": "pink-accent",
  "colors": {
    "accent": 204
  }
}

One color per mode:

{
  "name": "split-accent",
  "colors": {
    "dark": { "accent": 204 },
    "light": { "accent": 161 }
  }
}

Spinner-only:

{
  "name": "custom-spinner",
  "description": "Only changes the busy spinner.",
  "spinner_frames": ["◢", "◣", "◤", "◥"],
  "spinner_messages": ["working"],
  "spinner_interval_ms": 120
}

Dark-only themes still work on light terminals. If colors.light is missing, zot applies colors.dark overrides on top of the built-in light default. The inverse also works.

{
  "name": "custom-spinner",
  "description": "An alternative spinner for zot that only displays a single spinner text.",
  "colors": {
    "dark": {
      "spinner_frames": ["◢", "◣", "◤", "◥"],
      "spinner_messages": ["working"],
      "spinner_interval_ms": 120
    }
  }
}

Full shape

All fields are optional.

{
  "name": "my-theme",
  "description": "Shown in /settings → color theme.",
  "color_descriptions": {
    "accent": "Optional documentation for humans. zot ignores this object."
  },
  "colors": {
    "dark": {
      "fg": 253,
      "muted": 244,
      "accent": 111,
      "user": 180,
      "user_bubble_bg": "#42454b",
      "user_bubble_fg": 248,
      "assistant": 117,
      "tool": 114,
      "tool_out": 245,
      "error": 203,
      "warning": 214,
      "spinner": 183,
      "selection_bg": 24,
      "selection_fg": 231,
      "spinner_frames": ["⠋", "⠙", "⠚", "⠞", "⠖", "⠦", "⠴", "⠲", "⠳", "⠓"],
      "spinner_messages": ["thinking", "working"],
      "spinner_interval_ms": 80,
      "syntax_base_style": "monokai",
      "syntax": {
        "keyword": "#81a1c1 bold",
        "keyword_constant": "#81a1c1",
        "keyword_declaration": "#81a1c1",
        "keyword_namespace": "#81a1c1",
        "keyword_reserved": "#81a1c1 bold",
        "keyword_type": "#88c0d0",
        "name_builtin": "#88c0d0",
        "name_function": "#8fbcbb",
        "name_class": "#a3be8c bold",
        "name_decorator": "#b48ead",
        "literal_string": "#a3be8c",
        "literal_string_escape": "#bf616a",
        "literal_number": "#d08770",
        "comment": "#616e88 italic",
        "comment_preproc": "#b48ead",
        "operator": "#eceff4",
        "punctuation": "#d8dee9",
        "text": "#e5e9f0"
      }
    },
    "light": {
      "fg": 236,
      "muted": 244,
      "accent": 33
    }
  }
}

You may also put overrides directly at the top level or directly under colors when they should apply to both modes:

{
  "name": "tiny",
  "accent": 204,
  "colors": {
    "spinner_messages": ["shipping"]
  }
}

Color fields

Most color fields are xterm-256 indexes (0–255).

  • fg — default foreground text.
  • muted — secondary text, dividers, gutters, inactive hints.
  • accent — prompt bar, bullets, links, headings, active markers.
  • user — user role label color; mostly compatibility.
  • user_bubble_bg — background behind user message rows.
  • user_bubble_fg — foreground inside user message rows.
  • assistant — assistant/zot accent and spinner text.
  • tool — tool names, success marks, diff additions.
  • tool_out — plain tool-output text.
  • error — errors, refused calls, diff deletions.
  • warning — warnings and high context-usage state.
  • spinner — reserved spinner color slot.
  • selection_bg — highlighted row background.
  • selection_fg — highlighted row foreground.

user_bubble_bg supports richer terminal color forms:

254
"#42454b"
{ "mode": "256", "index": 254 }
{ "mode": "ansi", "index": 100 }
{ "mode": "rgb", "r": 66, "g": 69, "b": 75 }

Spinner fields

Spinner settings can appear at top level, under colors, or under colors.dark / colors.light.

  • spinner_frames — list of frame strings. Single-cell glyphs keep status-bar alignment clean.
  • spinner_messages — list of messages; zot picks one per turn.
  • spinner_interval_ms — frame interval in milliseconds. Missing or invalid falls back to 80ms.

Syntax fields

Syntax highlighting uses Chroma style entries. Values may include attributes after the color, such as bold, italic, or underline.

Supported syntax override keys:

keyword, keyword_constant, keyword_declaration, keyword_namespace,
keyword_reserved, keyword_type, name_builtin, name_function,
name_class, name_decorator, literal_string, literal_string_escape,
literal_number, comment, comment_preproc, operator, punctuation, text

Example:

{
  "colors": {
    "dark": {
      "syntax_base_style": "monokai",
      "syntax": {
        "keyword": "#f05b8d",
        "name_function": "#b675f1",
        "literal_string": "#58c760",
        "comment": "#a1a1a1 italic"
      }
    }
  }
}

Theme-only extensions

An extension can exist only to ship a theme. No slash command, subprocess, or executable is required when the extension contains a valid theme file.

$ZOT_HOME/extensions/my-theme-extension/
├── extension.json
└── theme.json

extension.json:

{
  "name": "my-theme-extension",
  "version": "1.0.0",
  "description": "Ships a zot color theme",
  "enabled": true
}

No exec is needed when theme.json or themes/theme.json exists. If exec is present, zot treats it as a normal extension too.

Validate

zot theme files are plain JSON, not JSONC. Validate before installing:

python3 -m json.tool theme.json >/dev/null
source
1# zot themes
2
3zot themes are JSON files that override any subset of the built-in
4light/dark TUI theme. Nothing is required: a theme can change one
5color, only the spinner, only syntax highlighting, or all of them.
6Missing values inherit from zot's built-in default for the detected
7terminal background.
8
9## Where themes live
10
11User themes are discovered from:
12
13```text
14$ZOT_HOME/themes/*.json
15```
16
17Open `/settings` and choose **color theme** to switch. Changes are
18saved in `$ZOT_HOME/config.json` and apply immediately. If the selected
19file is deleted later, zot resets the setting to the built-in auto
20(default) theme.
21
22Theme files bundled with extensions are discovered in-place from loaded
23extension directories:
24
25```text
26$ZOT_HOME/extensions/<extension>/theme.json
27$ZOT_HOME/extensions/<extension>/themes/theme.json
28<project>/.zot/extensions/<extension>/theme.json
29<project>/.zot/extensions/<extension>/themes/theme.json
30```
31
32zot does **not** copy extension themes into `$ZOT_HOME/themes`; extension
33owned themes stay in the extension directory. The settings picker shows
34source info such as `from extension my-theme-extension`.
35
36## Minimal themes
37
38All of these are valid.
39
40Metadata only:
41
42```json
43{
44  "name": "my-theme",
45  "description": "Metadata only; all visuals inherit zot defaults."
46}
47```
48
49One shared color for both light and dark terminals:
50
51```json
52{
53  "name": "pink-accent",
54  "colors": {
55    "accent": 204
56  }
57}
58```
59
60One color per mode:
61
62```json
63{
64  "name": "split-accent",
65  "colors": {
66    "dark": { "accent": 204 },
67    "light": { "accent": 161 }
68  }
69}
70```
71
72Spinner-only:
73
74```json
75{
76  "name": "custom-spinner",
77  "description": "Only changes the busy spinner.",
78  "spinner_frames": ["◢", "◣", "◤", "◥"],
79  "spinner_messages": ["working"],
80  "spinner_interval_ms": 120
81}
82```
83
84Dark-only themes still work on light terminals. If `colors.light` is
85missing, zot applies `colors.dark` overrides on top of the built-in
86light default. The inverse also works.
87
88```json
89{
90  "name": "custom-spinner",
91  "description": "An alternative spinner for zot that only displays a single spinner text.",
92  "colors": {
93    "dark": {
94      "spinner_frames": ["◢", "◣", "◤", "◥"],
95      "spinner_messages": ["working"],
96      "spinner_interval_ms": 120
97    }
98  }
99}
100```
101
102## Full shape
103
104All fields are optional.
105
106```json
107{
108  "name": "my-theme",
109  "description": "Shown in /settings → color theme.",
110  "color_descriptions": {
111    "accent": "Optional documentation for humans. zot ignores this object."
112  },
113  "colors": {
114    "dark": {
115      "fg": 253,
116      "muted": 244,
117      "accent": 111,
118      "user": 180,
119      "user_bubble_bg": "#42454b",
120      "user_bubble_fg": 248,
121      "assistant": 117,
122      "tool": 114,
123      "tool_out": 245,
124      "error": 203,
125      "warning": 214,
126      "spinner": 183,
127      "selection_bg": 24,
128      "selection_fg": 231,
129      "spinner_frames": ["⠋", "⠙", "⠚", "⠞", "⠖", "⠦", "⠴", "⠲", "⠳", "⠓"],
130      "spinner_messages": ["thinking", "working"],
131      "spinner_interval_ms": 80,
132      "syntax_base_style": "monokai",
133      "syntax": {
134        "keyword": "#81a1c1 bold",
135        "keyword_constant": "#81a1c1",
136        "keyword_declaration": "#81a1c1",
137        "keyword_namespace": "#81a1c1",
138        "keyword_reserved": "#81a1c1 bold",
139        "keyword_type": "#88c0d0",
140        "name_builtin": "#88c0d0",
141        "name_function": "#8fbcbb",
142        "name_class": "#a3be8c bold",
143        "name_decorator": "#b48ead",
144        "literal_string": "#a3be8c",
145        "literal_string_escape": "#bf616a",
146        "literal_number": "#d08770",
147        "comment": "#616e88 italic",
148        "comment_preproc": "#b48ead",
149        "operator": "#eceff4",
150        "punctuation": "#d8dee9",
151        "text": "#e5e9f0"
152      }
153    },
154    "light": {
155      "fg": 236,
156      "muted": 244,
157      "accent": 33
158    }
159  }
160}
161```
162
163You may also put overrides directly at the top level or directly under
164`colors` when they should apply to both modes:
165
166```json
167{
168  "name": "tiny",
169  "accent": 204,
170  "colors": {
171    "spinner_messages": ["shipping"]
172  }
173}
174```
175
176## Color fields
177
178Most color fields are xterm-256 indexes (`0`–`255`).
179
180- `fg` — default foreground text.
181- `muted` — secondary text, dividers, gutters, inactive hints.
182- `accent` — prompt bar, bullets, links, headings, active markers.
183- `user` — user role label color; mostly compatibility.
184- `user_bubble_bg` — background behind user message rows.
185- `user_bubble_fg` — foreground inside user message rows.
186- `assistant` — assistant/zot accent and spinner text.
187- `tool` — tool names, success marks, diff additions.
188- `tool_out` — plain tool-output text.
189- `error` — errors, refused calls, diff deletions.
190- `warning` — warnings and high context-usage state.
191- `spinner` — reserved spinner color slot.
192- `selection_bg` — highlighted row background.
193- `selection_fg` — highlighted row foreground.
194
195`user_bubble_bg` supports richer terminal color forms:
196
197```json
198254
199"#42454b"
200{ "mode": "256", "index": 254 }
201{ "mode": "ansi", "index": 100 }
202{ "mode": "rgb", "r": 66, "g": 69, "b": 75 }
203```
204
205## Spinner fields
206
207Spinner settings can appear at top level, under `colors`, or under
208`colors.dark` / `colors.light`.
209
210- `spinner_frames` — list of frame strings. Single-cell glyphs keep
211  status-bar alignment clean.
212- `spinner_messages` — list of messages; zot picks one per turn.
213- `spinner_interval_ms` — frame interval in milliseconds. Missing or
214  invalid falls back to 80ms.
215
216## Syntax fields
217
218Syntax highlighting uses Chroma style entries. Values may include
219attributes after the color, such as `bold`, `italic`, or `underline`.
220
221Supported syntax override keys:
222
223```text
224keyword, keyword_constant, keyword_declaration, keyword_namespace,
225keyword_reserved, keyword_type, name_builtin, name_function,
226name_class, name_decorator, literal_string, literal_string_escape,
227literal_number, comment, comment_preproc, operator, punctuation, text
228```
229
230Example:
231
232```json
233{
234  "colors": {
235    "dark": {
236      "syntax_base_style": "monokai",
237      "syntax": {
238        "keyword": "#f05b8d",
239        "name_function": "#b675f1",
240        "literal_string": "#58c760",
241        "comment": "#a1a1a1 italic"
242      }
243    }
244  }
245}
246```
247
248## Theme-only extensions
249
250An extension can exist only to ship a theme. No slash command,
251subprocess, or executable is required when the extension contains a
252valid theme file.
253
254```text
255$ZOT_HOME/extensions/my-theme-extension/
256├── extension.json
257└── theme.json
258```
259
260`extension.json`:
261
262```json
263{
264  "name": "my-theme-extension",
265  "version": "1.0.0",
266  "description": "Ships a zot color theme",
267  "enabled": true
268}
269```
270
271No `exec` is needed when `theme.json` or `themes/theme.json` exists.
272If `exec` is present, zot treats it as a normal extension too.
273
274## Validate
275
276zot theme files are plain JSON, not JSONC. Validate before installing:
277
278```bash
279python3 -m json.tool theme.json >/dev/null
280```

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close