@kecan0406/ttheme 1.0.35 → 1.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,646 +1,129 @@
1
- # ttheme
2
-
3
- Character terminal palettes — every new tab takes the next one.
4
-
5
- One hundred and eight palettes drawn from Vocaloid, Evangelion, Madoka Magica,
6
- Steins;Gate, Lucky☆Star, Bocchi the Rock!, Monogatari, Sailor Moon, Serial
7
- Experiments Lain, VA-11 Hall-A, Persona 5, Undertale, Call of the Night,
8
- Higurashi, Doki Doki Literature Club, Umineko, K-On!, Cyberpunk: Edgerunners,
9
- Touhou Project, Chuunibyou and Jujutsu Kaisen, built for **Ghostty, kitty, Alacritty,
10
- WezTerm and iTerm2**, plus a zsh layer that repaints tabs at runtime — one palette everywhere, or
11
- the next one on every new tab.
12
-
13
- Two things separate this from the usual color-scheme dump:
14
-
1
+ <h1 align="center">ttheme</h1>
2
+
3
+ <p align="center"><em>Character terminal palettes — readable by rule, switched live, one tab at a time.</em></p>
4
+
5
+ <p align="center">
6
+ <a href="https://www.npmjs.com/package/@kecan0406/ttheme"><img src="https://img.shields.io/npm/v/@kecan0406/ttheme?style=flat-square" alt="npm version"></a>
7
+ <a href="https://github.com/kecan0406/ttheme/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/kecan0406/ttheme/ci.yml?branch=main&style=flat-square&label=ci" alt="CI status"></a>
8
+ <a href="https://kecan0406.github.io/ttheme/"><img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fkecan0406.github.io%2Fttheme%2Fmanifest.json&query=%24.palettes.length&label=palettes&style=flat-square&color=c796c8" alt="palettes in the catalog"></a>
9
+ <a href="LICENSE"><img src="https://img.shields.io/npm/l/@kecan0406/ttheme?style=flat-square" alt="MIT license"></a>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://kecan0406.github.io/ttheme/">Site</a> ·
14
+ <a href="#palettes">Palettes</a> ·
15
+ <a href="docs/usage.md">Usage</a> ·
16
+ <a href="docs/terminals.md">Terminals</a> ·
17
+ <a href="CONTRIBUTING.md">Contributing</a>
18
+ </p>
19
+
20
+ <p align="center">
21
+ <img src="docs/demo.gif" alt="ttheme preview: typing nichi filters to the Nichijou palettes, each one repaints the tab as the cursor lands on it, then rei is picked and applied" width="860">
22
+ </p>
23
+
24
+ ## Why ttheme
25
+
26
+ - **Colors from the character.** Every palette keeps the three colors that
27
+ identify its character and borrows its ANSI ramp from an established scheme,
28
+ harmonized so the whole catalog reads alike.
15
29
  - **Every palette passes an accessibility gate.** Body text at 7:1 against the
16
- background (WCAG AAA), every meaningful ANSI color at 3:1. The build fails if a
17
- palette regresses, so "it looked cute in the screenshot" cannot ship.
18
- - **Colors switch at runtime, not just at startup.** Tabs get different palettes
19
- in the same window, and any tab can be repainted at any time.
30
+ background (WCAG AAA), every meaningful ANSI color at 3:1, and each accent stays
31
+ in its role — red reads as an error, green as success. The build fails if a
32
+ palette regresses.
33
+ - **Switched live, per tab.** Tabs in one window can wear different palettes,
34
+ any tab can be repainted at any time, a directory can pin its own, and a new
35
+ tab can take the next character.
20
36
 
21
- ## Terminal support
22
-
23
- Nothing is emulated — a terminal that cannot express something simply does not
24
- get it.
25
-
26
- | Feature | Ghostty | iTerm2 | kitty | Alacritty | WezTerm | Windows Terminal | Warp | Terminal.app | Other |
27
- |---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
28
- | **Setup with `init`** | ![Done][done] | ![Done][done] | ![Done][done] | ![Done][done] | ![Done][done] | ![Done][done] | ![Done][done] | ![No][no] | ![No][no] |
29
- | **Palette files** | ![Done][done] | ![Done][done] | ![Done][done] | ![Done][done] | ![Done][done] | ![Done][done] | ![Done][done] | ![No][no] | ![No][no] |
30
- | **Runtime repaint** | ![Done][done] | ![Done][done] | ![Done][done] | ![Done][done] | ![Done][done] | ![Done][done] | ![No][no] | ![Done][done] | ![Partial][partial] |
31
- | **Background pictures** | ![Done][done] | ![Done][done] | ![Done][done] | ![No][no] | ![Partial][partial] | ![No][no] | ![No][no] | ![No][no] | ![No][no] |
32
-
33
- - **Setup with `init`** — iTerm2 is offered on macOS only, Windows Terminal
34
- from WSL (or a native Windows zsh), where init writes a settings fragment on
35
- the Windows side. Warp gets its themes, and the default palette through its
36
- `settings.toml` (the Warp builds that have one).
37
- - **Palette files** — iTerm2 gets one dynamic profile per palette, plus
38
- `.itermcolors` in the archive; Windows Terminal gets one fragment carrying
39
- every installed scheme.
40
- - **Runtime repaint** — OSC escape sequences everywhere, per pane in kitty,
41
- WezTerm and Windows Terminal, one color per sequence (Alacritty drops an OSC 4
42
- carrying more than seven). kitty and Windows Terminal reset every OSC color
43
- when their config reloads, so a changed default would undo the palettes open
44
- tabs wear: kitty's watcher puts each window's palette back as the reload ends,
45
- and a Windows Terminal tab repaints itself at its next prompt or focus.
46
- Terminal.app takes every OSC color but no OSC reset, so ttheme reads its
47
- colors when the shell starts and puts them back itself. Warp answers OSC
48
- color queries but paints one theme app-wide and never the background an OSC
49
- sets, so the shell layer stays off there and `ttheme use <palette>` points you at
50
- `ttheme default <palette>`, which puts the palette on every Warp window; any other terminal that speaks OSC 4/10/11 (foot, Konsole,
51
- VTE-based terminals…) gets repainting and nothing else.
52
- - **Colors only** — ttheme sets no font, font size or shader anywhere; those
53
- stay what you configured in your terminal.
54
- - **Background pictures** — `find`, `ttheme image` and `preview`'s live
55
- backdrop. Ghostty shows one picture app-wide, following the focused tab;
56
- iTerm2 (3.7 or newer) shows one per tab and kitty one per window (a split
57
- pane included), both with the same preview, tuning and find. WezTerm shows the
58
- picture of the active tab per window and switches it as `preview` moves, but
59
- has no in-terminal preview or tuning — find and tune from Ghostty, iTerm2 or
60
- kitty. Alacritty has no graphics at all. Cut-outs and baked crops need macOS.
61
-
62
- [done]: https://img.shields.io/badge/Done-2ea44f?style=flat-square
63
- [partial]: https://img.shields.io/badge/Partial-e3b341?style=flat-square
64
- [no]: https://img.shields.io/badge/Not%20supported-d0d7de?style=flat-square
65
-
66
- ## Palettes
67
-
68
- | Group | Palettes |
69
- |---|---|
70
- | Vocaloid | `miku` `kumori` `rin` `len` `luka` `kaito` `meiko` |
71
- | Evangelion — エヴァンゲリオン | `asuka` `rei` `kaworu` `misato` `eva01` `magi` |
72
- | Madoka Magica — 魔法少女まどか☆マギカ | `madoka` `homura` `sayaka` `kyoko` `mami` `nagisa` `kyubey` |
73
- | Steins;Gate — シュタインズ・ゲート | `kurisu` `mayuri` `suzuha` |
74
- | Lucky☆Star — らき☆すた | `konata` `kagami` `tsukasa` `miyuki` |
75
- | Bocchi the Rock! — ぼっち・ざ・ろっく! | `bocchi` `nijika` `ryo` `kita` |
76
- | Monogatari — 〈物語〉シリーズ | `hitagi` `shinobu` `tsubasa` `nadeko` |
77
- | Sailor Moon — 美少女戦士セーラームーン | `moon` `mercury` `mars` `jupiter` `venus` |
78
- | Lain — serial experiments lain | `lain` |
79
- | VA-11 Hall-A — Cyberpunk Bartender Action | `jill` `dorothy` `alma` `stella` `sei` `valhalla` |
80
- | Persona 5 — ペルソナ5 | `joker` `skull` `panther` `fox` `queen` `oracle` `velvet` |
81
- | Undertale | `toriel` `sans` `papyrus` `undyne` `mettaton` `determination` |
82
- | Call of the Night — よふかしのうた | `nazuna` `kou` `seri` `anko` `yofukashi` |
83
- | Higurashi — ひぐらしのなく頃に | `rena` `mion` `rika` `satoko` `hinamizawa` |
84
- | Doki Doki Literature Club | `sayori` `natsuki` `yuri` `monika` `glitch` |
85
- | Umineko — うみねこのなく頃に | `battler` `beatrice` `bernkastel` `lambdadelta` `rokkenjima` |
86
- | K-On! — けいおん! | `yui` `ritsu` `mio` `mugi` `azusa` |
87
- | Cyberpunk: Edgerunners — サイバーパンク エッジランナーズ | `david` `lucy` `rebecca` `nightcity` |
88
- | Touhou Project — 東方Project | `reimu` `marisa` `cirno` `youmu` `patchouli` `flandre` |
89
- | Chuunibyou — 中二病でも恋がしたい! | `rikka` `yuuta` `shinka` `dekomori` `kumin` |
90
- | Jujutsu Kaisen — 呪術廻戦 | `gojo` `yuji` `megumi` `nobara` `nanami` `geto` `sukuna` |
91
- | — | `neutral` (default) |
92
-
93
- `neutral` sits out of the rotation and the listings — it is the pre-shell
94
- default, still applyable as `ttheme neutral`.
95
-
96
- Backgrounds and cursors are hand-tuned; the 16 ANSI colors are borrowed from
97
- established themes (Nvim Dark, Nightfox, Oxocarbon, Selenized, Monokai Pro,
98
- Duotone, Gruvbox, Rose Pine, TokyoNight, Everforest, IR Black…) or from
99
- published character themes, picked to match each character's
100
- tones while passing the contrast gate. Each palette records where its ANSI set
101
- came from — `ttheme` prints it, and it is in the theme's TOML.
102
-
103
- ## Install
104
-
105
- **Ghostty, kitty, Alacritty, WezTerm, iTerm2, Windows Terminal or Warp** — one command, no clone:
37
+ ## Quick start
106
38
 
107
39
  ```sh
108
40
  npx @kecan0406/ttheme@latest init
109
41
  ```
110
42
 
111
- It asks which terminals to wire and which series to install — `space` marks
112
- one, enter moves on, and `select all` takes the lot — and how the terminal
113
- should wear them: `default` (preselected) puts one palette on every tab,
114
- `rotate` gives every new tab the next palette, `keep` leaves your terminal colors
115
- alone and only installs. Then it shows what it is about to change and writes it all at once,
116
- so cancelling before that touches nothing. It places the zsh layer under
117
- `~/.config/ttheme` and edits your terminal config and `~/.zshrc` between
118
- `# ttheme begin` / `# ttheme end` markers — everything outside the markers is
119
- left alone, and what goes in is colors only. Before its first edit of a file it
120
- keeps a copy beside it, `<file>.ttheme.bak`, and it writes through symlinks, so a
121
- dotfiles repo keeps its links. A key you set yourself stays yours: a Ghostty
122
- `command` or `shell-integration`, kitty's `window_logo_scale` and
123
- `window_logo_alpha`. Every file it adds to a terminal's own folders is named
124
- `ttheme-<palette>`, so a theme of yours is never overwritten. Alacritty lets its
125
- own config win over an import, so colors set in `alacritty.toml` hide the
126
- default palette — init says so when it finds them. iTerm2 has no config file to edit, so it gets one profile per
127
- installed palette instead — `ttheme · miku` and so on, in
128
- `~/Library/Application Support/iTerm2/DynamicProfiles/ttheme.json`, which
129
- iTerm2 reloads by itself whenever `ttheme browse` adds or drops one. One more,
130
- `ttheme · default`, always wears your default palette (none under `keep`), and
131
- init makes it iTerm2's default profile, so new tabs and windows open wearing the
132
- palette and its picture from their first frame and follow `ttheme default` from
133
- then on. iTerm2 reads its default profile only when it starts, so quit and
134
- reopen it once after init. The profile it replaces stays the parent of every
135
- ttheme profile, so your font and keys carry over, and `keep` gives it back.
136
- WezTerm's config is Lua, so init puts a two-line block that runs
137
- `~/.config/ttheme/wezterm.lua` just before your config's `return config` (or
138
- writes a small `wezterm.lua` when there is none); WezTerm reloads it by itself.
139
- Windows Terminal gets a fragment,
140
- `%LOCALAPPDATA%\Microsoft\Windows Terminal\Fragments\ttheme\ttheme.json`,
141
- with every installed scheme and the default palette on the profile init ran in —
142
- `settings.json` is never edited, only touched so the terminal reloads. Warp gets
143
- a theme per palette, `~/.warp/themes/ttheme-<palette>.yaml`, and the default
144
- palette as the one `theme` key of `[appearance.themes]` in `~/.warp/settings.toml`
145
- — Warp applies it within a few seconds, and `keep` puts back the theme you had.
146
- It ends with a receipt, paints the first palette onto the tab you
147
- ran it in (not under `keep`) and lists what to do next (`exec zsh` for the
148
- `ttheme` command here, a terminal restart for new tabs).
149
- Running it again updates in place; `--yes` (`-y`) skips every prompt and installs no
150
- palettes, and init refuses to run without a terminal unless it is given —
151
- `ttheme browse` opens the full catalog any time, to add or drop single palettes.
43
+ It asks which terminals to wire and which series to install, shows every file it
44
+ is about to change, and writes nothing until you confirm. Then:
152
45
 
153
46
  ```sh
154
- npx @kecan0406/ttheme@latest uninstall
47
+ exec zsh # the ttheme command in this tab
48
+ ttheme preview # browse live — the tab repaints as the cursor moves
155
49
  ```
156
50
 
157
- takes it all back out, after listing what it will do: every ttheme block
158
- leaves its config, Warp gets its theme back and iTerm2 its own default profile,
159
- and the `ttheme-*` theme files, `~/.config/ttheme` (installed pictures
160
- included), the cache and the iTerm2 and Windows Terminal files are deleted. A
161
- config you did not touch since ttheme first edited it comes back byte for byte
162
- and its backup goes; one you edited since keeps `<file>.ttheme.bak`. Open shells
163
- drop the layer at their next prompt.
51
+ <details>
52
+ <summary><b>What init changes</b></summary>
164
53
 
165
- **Any of them by hand** — one archive per terminal in the
166
- [latest release](https://github.com/kecan0406/ttheme/releases/latest):
54
+ - Your terminal config and `~/.zshrc` get a block between `# ttheme begin` /
55
+ `# ttheme end`; everything outside it is left alone, and what goes in is
56
+ colors only — no font, font size or shader.
57
+ - Each file is backed up once to `<file>.ttheme.bak` before the first edit, and
58
+ written through symlinks, so a dotfiles repo keeps its links.
59
+ - A key you set yourself stays yours, and every file ttheme adds to a terminal's
60
+ folders is named `ttheme-<palette>`.
61
+ - `npx @kecan0406/ttheme@latest uninstall` lists and then reverses all of it.
167
62
 
168
- ```sh
169
- REL=https://github.com/kecan0406/ttheme/releases/latest/download
170
-
171
- # kitty
172
- curl -L $REL/ttheme-kitty.tar.gz | tar xz
173
- cp kitty/themes/ttheme-miku.conf ~/.config/kitty/themes/
174
- # kitty.conf: include themes/ttheme-miku.conf
175
-
176
- # alacritty
177
- curl -L $REL/ttheme-alacritty.tar.gz | tar xz
178
- cp alacritty/themes/ttheme-miku.toml ~/.config/alacritty/themes/
179
- # alacritty.toml: [general]
180
- # import = ["~/.config/alacritty/themes/ttheme-miku.toml"]
181
-
182
- # wezterm
183
- curl -L $REL/ttheme-wezterm.tar.gz | tar xz
184
- cp wezterm/colors/ttheme-miku.toml ~/.config/wezterm/colors/
185
- # wezterm.lua: config.color_scheme = "ttheme-miku"
186
-
187
- # iTerm2 — open a .itermcolors to add it to Settings > Profiles > Colors presets
188
- curl -L $REL/ttheme-iterm2.tar.gz | tar xz
189
-
190
- # windows terminal — every scheme in one fragment
191
- curl -L $REL/ttheme-windows-terminal.tar.gz | tar xz
192
- # copy windows-terminal/ttheme.json into
193
- # %LOCALAPPDATA%\Microsoft\Windows Terminal\Fragments\ttheme\
194
-
195
- # warp
196
- curl -L $REL/ttheme-warp.tar.gz | tar xz
197
- cp warp/themes/ttheme-miku.yaml ~/.warp/themes/
198
- ```
63
+ Per-terminal details, manual install from the release archives and building from
64
+ a checkout are in [docs/install.md](docs/install.md).
199
65
 
200
- Each Ghostty theme file also carries the dock-icon colors, derived from the
201
- palette.
66
+ </details>
202
67
 
203
- Building from a checkout works too: `mise install && bun install && mise run
204
- build` writes the same tree to `dist/`, `mise run sandbox` tries it in a
205
- throwaway home, and `mise run bin:build && node bin/ttheme.js init` installs
206
- it for real.
207
-
208
- ## Use
68
+ ## Palettes
209
69
 
210
- ```
211
- ttheme list every palette, grouped, with previews
212
- ttheme use paint this tab (a unique prefix works: ttheme use ho)
213
- ttheme preview browse live — focus repaints the tab, enter keeps it (this tab or default)
214
- ttheme next advance this tab to the next palette
215
- ttheme default make a palette the one new tabs open with
216
- ttheme on wear the default palette in new tabs again
217
- ttheme off take the palette and picture off every tab — the terminal's own colors until ttheme on
218
- ttheme pin pick a palette for this directory — cd into it repaints, cd out restores
219
- ttheme unpin drop the palette pinned to this directory
220
- ttheme config edit settings in $EDITOR — they apply in new tabs
221
- ttheme help the list above, in your terminal
222
-
223
- ttheme browse pick palettes from the catalog in a live picker
224
- ttheme list the catalog, ● installed and ○ not (a query filters it, --json prints it as JSON)
225
- ttheme add install palettes from the catalog
226
- ttheme remove uninstall palettes
227
- ttheme update refresh the catalog from the registry
228
- ```
70
+ <p align="center">
71
+ <a href="https://kecan0406.github.io/ttheme/"><img src="https://kecan0406.github.io/ttheme/readme/series.svg" alt="One card per series, drawn in its lead palette" width="856"></a>
72
+ </p>
229
73
 
230
- `ttheme <command> --help` (or `ttheme help <command>`) describes one command
231
- without running it, and `ttheme --version` prints the version. `TTHEME_DEBUG=1`
232
- in front of any command prints where an error came from, for a bug report.
233
-
234
- In `preview`, series and palettes are listed by name (`TTHEME_SORT=series`
235
- keeps the order they were added), groups start folded with the cursor on the
236
- current palette;
237
- `↑`/`↓` move (the tab repaints as the focus lands on a palette), `←`/`→` fold
238
- and unfold (so do enter and space on a series), page up/down and home/end
239
- jump, typing filters by substring and underlines the match (ctrl-u clears it),
240
- enter applies, and esc steps back — first out of the filter, then out of the
241
- preview with the original colors restored. Each palette row carries its 16
242
- colors, normal over bright. The last line lists only the keys that work right
243
- there, names the mode when it is not plain browsing (`FILTER`, `TUNE`, `CONFIG`,
244
- `APPLY`, `KEYS`) and pins where esc goes to the right; `?` shows all of them.
245
- alt-c opens the settings below in place — `↑`/`↓` pick one, `←`/`→` change it
246
- (the sort and the search hint animation change live), enter writes the changed
247
- lines to `config.zsh`, esc puts every value back. From 76
248
- columns on, a sample session sits against the right edge of the window — the
249
- 16 colors, a prompt, git and test output, a selection — in the palette under
250
- the cursor. The list and the sample widen with the window (the sample up to 64
251
- columns) and the gap between them takes the rest; the tuning panel takes the
252
- sample's place while open, and so does the key list once the sample is 48
253
- columns wide.
254
- Under Ghostty or iTerm2 with `TTHEME_TAB_PALETTE=off` (the default), enter asks **this tab** or
255
- **default**: default records the palette as your default palette (so a later
256
- `add` or `remove` keeps it), rewrites `theme =` in the `# ttheme begin` block of
257
- your Ghostty config and sends `SIGUSR2` to the Ghostty that owns the tab, and
258
- rewrites iTerm2's `ttheme · default` profile, which iTerm2 reloads by itself — so new tabs, and open tabs you have not
259
- painted by hand, take the palette without a restart. Painting is per surface
260
- otherwise: a new tab starts from the configured theme, not from what the last
261
- tab was painted.
262
-
263
- A palette can also bring a Ghostty background image. The block `init` writes
264
- includes `~/.config/ttheme/backgrounds/shown.conf` with an optional
265
- `config-file = ?…`, and that one line names the palette whose picture is up.
266
- Putting a palette on — `ttheme use <name>`, enter in `preview`, `ttheme next`,
267
- `ttheme default`, cd into a pinned directory — rewrites the line and sends
268
- `SIGUSR2`, so whatever Ghostty settings you put in `<palette>.conf` arrive with
269
- the palette, and palettes without a file show no image:
74
+ <details>
75
+ <summary><b>Every palette</b></summary>
76
+ <br>
77
+ <img src="https://kecan0406.github.io/ttheme/readme/palettes.svg" alt="Every palette in the catalog, grouped by series" width="856">
78
+ </details>
270
79
 
271
- ```
272
- # ~/.config/ttheme/backgrounds/kagami.conf
273
- background-image = ~/.config/ttheme/backgrounds/kagami.png
274
- background-image-fit = cover
275
- background-image-opacity = 0.2
276
- ```
80
+ Both images are drawn from the live catalog, so they always match what
81
+ `ttheme browse` offers. `init` installs the series you pick; `ttheme browse`
82
+ adds or drops single palettes any time, and a merged palette reaches everyone
83
+ through `ttheme update` without waiting for a release.
277
84
 
278
- ttheme ships no images; they stay on your machine. A background is Ghostty
279
- config, not an escape sequence, and Ghostty keeps one background for the whole
280
- app, so one picture is up at a time, in every window. Moving to another tab
281
- brings its own: while a tab sits at its prompt it asks for focus events, and
282
- taking focus puts that tab's picture up — or clears it, when its palette has
283
- none. A tab busy with a command catches up at its next prompt, focus reporting
284
- is off while that command runs, and a tab whose colors are not a ttheme palette
285
- leaves the picture where it is.
286
-
287
- iTerm2 keeps a picture per tab. There the picture lives in the palette's own
288
- profile — `ttheme · kagami` carries the file, its opacity as Blend and cover or
289
- contain as the image mode, rewritten whenever the picture or its tuning
290
- changes — and putting a palette with a picture on, or taking one off, moves the
291
- tab to that palette's profile with `OSC 1337 SetProfile`. The first time,
292
- iTerm2 asks at the top of the tab whether a control sequence may change the
293
- profile: Always Allow lets every later switch through — put the palette on once
294
- more for the one that asked — and a tab it refuses still wears the palette's
295
- colors, only without the picture. iTerm2 has no
296
- position setting, so a tuned picture that is not centered is baked onto a canvas
297
- the size of the window, as the sizes above 100% are.
298
-
299
- A palette with no background yet can find one: on it in `preview`, tab opens
300
- **find**, which searches a booru for the palette's character tag
301
- (`meta.booru`) and lays the results out as a grid of thumbnails. It starts on
302
- danbooru; tab moves to konachan, then yande.re, then back — they share one tag
303
- vocabulary, so the same tag works on each. danbooru and yande.re carry every
304
- rating and `TTHEME_FIND_RATING` picks which come through; konachan is
305
- `konachan.net`, the mirror that carries safe posts only, since `konachan.com`
306
- answers with a Cloudflare challenge. When a site's connection is cut off — the
307
- way a network that blocks boorus by name drops danbooru — find asks whether to
308
- turn on the unblock proxy (`TTHEME_FIND_UNBLOCK`); `y` saves the setting and
309
- opens find again through it, `n` leaves it off. The
310
- sites sit in a strip of tabs under the query, the one you are on lit in its own
311
- color, and it comes again as a badge at the start of the line above a picture
312
- you try on, next to the site the artwork itself came from when the post records
313
- one (`pixiv.net`, `deviantart.com`). The grid starts
314
- with every post of the character; `c` narrows it to **cutouts** — posts carrying
315
- the site's transparency tags (`transparent_background` on danbooru,
316
- `transparent` or `vector` on konachan, `transparent_png` on yande.re) whose PNG header says they have
317
- an alpha channel; yande.re's `transparent_png` already means exactly that, so
318
- its posts skip the header check, which its slow file server would drag out —
319
- and the counter reads shown out of checked, `13/33`. find checks only as far as
320
- the grid reaches and checks more as you scroll; each answer is remembered, so
321
- coming back to a site or a palette never asks its file server again, and a site
322
- you have already seen comes back the moment you tab to it. Newer series carry
323
- few of those tags, so `TTHEME_FIND_CUTOUTS` names the ones to look for, site by
324
- site.
325
-
326
- Under each thumbnail is its post id and size, and under that whoever made it:
327
- the artist, which every site names through its tag types at no extra
328
- request, and the uploader as `@name` where a post has no artist tag. A score follows as
329
- `★22` on the sites that keep one. When the same hand uploads the same picture at
330
- the same size over and over, find folds that run into one tile marked `×9`,
331
- wherever the order puts its pictures, and folds in the same way a picture
332
- another site holds too — re-encoded at the same shape, cut from the post its
333
- source names, or hung under the same parent; space unfolds it and folds it
334
- again. Pictures by the uploader of the backgrounds
335
- you already have in the same series come first and carry `≈`, so a series keeps
336
- one hand; anything under 1600 px on its long edge shows its size in yellow.
337
-
338
- `s` opens the settings — the ratings to list, the nudity and underwear tags to
339
- block, cutouts or all, newest or score, and whether runs fold — with ↑↓ on the
340
- setting, enter to save them to `config.zsh` and esc to put them back. ←→ change
341
- a value; on the two rows of checkboxes (rating and block) they move between the
342
- boxes and space ticks one, so `safe` and `explicit` can be listed without
343
- `questionable`, or underwear let through while nudity stays blocked. At least
344
- one rating stays ticked. Whatever is not the default shows next to the query, so
345
- the screen never hides what it is filtering by.
346
-
347
- `/` opens the query for editing — type a tag to search for something else, or paste a post url or id
348
- (`https://yande.re/post/show/214705`, `konachan:244200`, `7159377`) to go
349
- straight to that one picture. While you type, danbooru's tag completion lists
350
- up to eight tags that start like the last word, each with its post count — the
351
- way to find a costume variant such as `amane_suzuha_(beta)` or a tag you only
352
- half remember; `↑`/`↓` pick one and enter searches it. A palette with no `meta.booru` tag at all opens
353
- find on that empty query, so it can have a background too. `o` opens the post's
354
- page in a browser.
355
-
356
- A picture of your own goes in the same way. Drop an image file on the window
357
- while find is open, or paste it — a file copied in Finder, its path, or a link
358
- to the image — and find tries it on as if it were a post. `ctrl+v` (or `v`
359
- outside the search field, `alt+v` on Windows) reads the clipboard itself, which
360
- is how a screenshot or a browser's "Copy Image" gets in, since no terminal
361
- pastes picture data as text — on macOS ctrl+shift+cmd+4 takes a screenshot
362
- straight to the clipboard. When the window comes back into focus with a picture
363
- on the clipboard, find says so for a few seconds; text on the clipboard that is
364
- neither a file nor a link goes into the search field instead. Every terminal hands a dropped
365
- file over as its path, each quoted its own way, and find reads them all; kitty
366
- and iTerm2 3.7 also deliver the dropped picture itself through kitty's
367
- drag-and-drop protocol, so a picture dragged out of a browser works there too,
368
- and kitty's clipboard protocol makes Cmd+V with a picture on the clipboard work
369
- directly, over ssh as well. PNG and JPEG work everywhere; macOS converts any
370
- other picture (HEIC, WebP, GIF, TIFF…) and shrinks one over 25 megapixels. The
371
- installed picture is named `local` in the conf's `# from` line, with the file or
372
- link it came from, and keeps its original under `backgrounds/originals/` like a
373
- booru post. Over ssh `ctrl+v` can only reach the clipboard through kitty's protocol,
374
- since the clipboard is on your own machine.
375
-
376
- Enter tries the picture on: ttheme downloads the original and paints the whole
377
- window with it the way the installed background will look — tinted with the
378
- palette, cropped with headroom above the face, at the opacity the contrast gate
379
- allows — over a sample of shell output, with the share of transparent pixels
380
- next to its size (`opaque` when there are none). On macOS an opaque picture is
381
- cut out first: ttheme asks the system's own Vision framework (macOS 14 or newer,
382
- through `osascript` — nothing is installed or uploaded) for the character alone,
383
- marks the picture `cut out`, and `x` switches between the cut-out and the picture
384
- as it is. When Vision finds no character, or would leave almost nothing or
385
- remove almost nothing, the picture stays opaque; elsewhere it always does. A post over 25 megapixels is
386
- fetched as the site's own smaller copy instead — up to 3500 px on yande.re and
387
- konachan, 850 px on danbooru — and its size carries `↓`; those
388
- copies are JPEGs, so a cutout tried on that way comes out opaque. `←`/`→` try
389
- the neighbours, which find fetches ahead of you two at a time, and enter installs
390
- and returns to the grid with the tile marked `✓`, the tab, search and scroll as
391
- you left them; esc then hands the last one installed to the preview, which
392
- carries on straight into the tuning panel below. `c` switches
393
- between every post and the cutouts, esc goes back. Unless the settings say otherwise,
394
- only `safe` and `general` posts are shown, and none tagged with nudity or
395
- underwear (`nude`, `panties` and each site's own spelling of them) — with only
396
- `safe` ticked yande.re and konachan are asked for `rating:s`, whatever else is
397
- searched; ticking more ratings or unticking a block in the `s` panel widen it. danbooru takes only two tags from a
398
- signed-out search, so on it a cutout search leaves the score order out and says
399
- so. Thumbnails and header checks stay in
400
- `~/.cache/ttheme/<site>/`; the pictures you try on last only while find is open.
401
- When a site asks ttheme to slow down, find waits as long as it names, up to a
402
- minute, with a countdown at the bottom. ttheme keeps no
403
- list of images — the tag is all it knows about a character.
404
-
405
- An install writes `<palette>.<hash>.png` (the tinted figure),
406
- `<palette>.<hash>@fill-<focus>.png` (the picture made at the shape of the window
407
- find ran in: a cut-out stands whole from its head down against the right edge, a
408
- little taller than the window, and a wallpaper covers it from its top) — the hash is of
409
- their content, because Ghostty and iTerm2 reload a background only when its path
410
- changes, so no two pictures may share a name — `<palette>.conf` and the untouched original under
411
- `backgrounds/originals/`, and starts it untuned — the picture it replaces moves
412
- to the shelf with the tuning it had. The
413
- conf opens with where the picture came from —
414
- `# from yande.re 214705 https://yande.re/post/show/214705` — and the tuning
415
- panel shows it next to the palette's name.
416
-
417
- `preview` shows the background of the palette under the cursor while you
418
- browse: through the kitty graphics protocol (Ghostty, kitty, and iTerm2 3.7 or newer)
419
- it draws that palette's `background-image` where Ghostty would place it, faded by
420
- `background-image-opacity`, behind the list — or just its plain background when
421
- it has no file. Only PNG images preview.
422
-
423
- On a palette with a background, tab opens a panel that tunes it in place:
424
- `↑`/`↓` pick size, position or opacity, and `←`/`→` change it (with shift, ten
425
- steps at a time). Size walks 1% at a time, shown large in the middle of the
426
- screen as it changes: 100% is the whole image fitted into the window
427
- (`contain`), below that it shrinks to 20%, above it the image grows around the
428
- face until it covers the window, and the top step is **fill** (`cover`). Fill
429
- uses `<palette>.<hash>@fill-<focus>.png` when it sits beside the image — a crop made
430
- to fill the window, whose name carries the height of the figure at the window's middle, which
431
- the sizes above 100% zoom around — and the image itself otherwise. Position
432
- steps through the nine `background-image-position` anchors, or `1`–`9` jump to
433
- one in reading order; opacity moves by 0.01. Space turns the palette's
434
- background off and on, `=` returns it to its defaults, enter keeps the change
435
- and esc puts back what the panel opened with. `f` in the panel opens find again
436
- to replace the picture.
437
-
438
- A palette holds every picture installed on it: an install shelves the one on
439
- screen rather than dropping it, `,` and `.` walk the saved pictures, and `D`
440
- removes the one shown. Each picture carries its own settings — size, position,
441
- opacity, the off switch and the baked crops travel with it under
442
- `backgrounds/shelf/<palette>/<post>/`, so walking back to a picture puts it
443
- back the way you left it, and tuning you have not confirmed is written to the
444
- picture before `,` or `.` moves off it.
445
-
446
- The palette's `.conf` holds those defaults; the preview only appends two
447
- optional includes to it and keeps everything else in `<palette>.tune.conf` (the
448
- tuning) and `<palette>.off.conf` (the off switch), which Ghostty loads after
449
- the conf. Kept changes are written when the preview closes, by whichever key,
450
- and reach both terminals from whichever one ran the preview: Ghostty reloads
451
- when it is showing that palette, and iTerm2's profiles are rewritten. Ghostty has no scale setting,
452
- so every size but 100% and fill is baked into a copy beside the image and the
453
- tuning points at it: below 100% onto a transparent canvas of the image's own
454
- size (`kagami.1a2b3c4d@60-bottom-right.png`, fitted with `contain`), above it onto
455
- one of the window's size at the time (`kagami.1a2b3c4d@130-center-2880x1800.png`,
456
- with `cover`). With iTerm2 wired, a size of 100% or less that is not centered
457
- goes onto the window-sized canvas too, since iTerm2 cannot place an image.
458
- Baking runs `sips`, so those sizes are offered on macOS only.
459
-
460
- The preview draws inside the cell grid, and Ghostty's `window-padding` around
461
- it keeps showing the configured background. While the cursor rests on the
462
- configured palette and nothing has been tuned, the preview draws nothing and
463
- lets that background show through whole. Once Ghostty supports kitty's relative
464
- placements (merged after 1.3.1), the preview notices when it opens and hangs
465
- its layers out over the padding instead, so every palette reaches the window
466
- edge.
467
-
468
- `pin` opens the same browser and, on enter, asks **this directory** or **and
469
- below**; the answer lands in `~/.config/ttheme/pins`, one `path palette` per
470
- line, where `path/**` covers everything below it (`~` works, and the file is
471
- yours to edit). From then on a tab that `cd`s into a pinned path takes its
472
- palette — symlinks resolve to the pinned directory — and `cd`ing out restores
473
- what the tab had before, unless you painted it by hand in between, in which
474
- case your pick stays. The nearest pinned ancestor wins, so a project can pin
475
- one palette and a subfolder another. Open tabs pick up a changed pins file on
476
- their next `cd`; `unpin` drops the pin on the current directory.
477
-
478
- `preview`, `next`, `default`, `pin`, `unpin`, `config` and `help` match exactly; every other first argument is read
479
- as a palette name, where a unique prefix is enough. Mistyped names get a "did
480
- you mean" suggestion instead of a wall of output. Piped output drops color and
481
- turns tab-separated, and `NO_COLOR` is respected.
482
-
483
- With `TTHEME_TAB_PALETTE=seq`, new tabs take the next palette in group order, with the counter shared across
484
- tabs — so opening four tabs walks you through four different characters rather
485
- than rolling the same one twice.
486
-
487
- Settings live in `~/.config/ttheme/config.zsh` — `init` seeds it with every
488
- setting at its default on a commented line, so a default a later version changes
489
- reaches you, and never overwrites a line you changed — bar `TTHEME_TAB_PALETTE`, which its
490
- default/rotate/keep question sets — `ttheme config` opens it in `$EDITOR` and alt-c in `preview` edits it in place. Only a value that differs from the default
491
- is left uncommented; setting one back comments it out again. Each line is a plain zsh
492
- `: ${VAR:=value}` assignment, so a variable exported before the layer loads
493
- still wins:
494
-
495
- | Setting | Default | |
496
- |---|---|---|
497
- | `TTHEME_TAB_PALETTE` | `off` | `off` keeps new tabs on the terminal's configured theme (`preview` → default changes it); `seq` gives every new tab the next palette. `init` writes `seq` for rotate and `off` for default and keep |
498
- | `TTHEME_ANNOUNCE` | `1` | `0` silences the one-line notice under "Last login:" |
499
- | `TTHEME_FX` | `typewriter` | search hint animation — `typewriter`, `decode` or `glitch` |
500
- | `TTHEME_SORT` | `abc` | `series` lists series and palettes in the order they were added instead of by name — in `ttheme`, `preview` and, once exported, the `init` picker |
501
- | `TTHEME_FIND_RATING` | `safe` | the ratings `find` lists, any of `safe`, `questionable` and `explicit` separated by spaces (`"safe explicit"`). Each site is read in its own vocabulary, so danbooru's `s` (sensitive) is not mistaken for yande.re's `s` (safe). The set shows next to the query whenever it is not just `safe` |
502
- | `TTHEME_FIND_BLOCK` | `nudity underwear` | the posts `find` drops by tag: `nudity` (`nude`, `naked`, `topless`…), `underwear` (`panties`, `bra`, `lingerie`… — each with the site's own spelling), both, or `none` to keep every post. What is let through shows next to the query as `allows …` |
503
- | `TTHEME_FIND_POSTS` | `all` | what `find` opens on — every post of the character, or only the transparent cutouts (`cutouts`) |
504
- | `TTHEME_FIND_CUTOUTS` | — | the tags `find` calls a transparent cutout, as `key=tag,tag` pairs separated by spaces, keyed by `danbooru`, `konachan` or `yande` (`konachan=transparent,vector yande=transparent_png`). A site with several tags matches any of them; `danbooru=` names none, so its cutouts are the posts whose PNG header has an alpha channel. A site named here loses yande.re's shortcut of skipping that header check. Sites left out keep the built-in tags |
505
- | `TTHEME_FIND_SOLO` | `on` | `on` keeps only the posts tagged `solo` — the character alone, which is what a backdrop needs. konachan and yande.re do not tag how many people a picture shows, so their posts borrow the tags danbooru holds for the same file, one request per page; a post danbooru does not have is left alone. The query line says `solo` while it is on |
506
- | `TTHEME_FIND_ORDER` | `fit` | the order `find` lists posts in — `fit` ranks each page it fetches by how well a post makes a backdrop (solo, resolution against the window, a cutout or an aspect close to the window's, score within the page, the palette's colors in its preview; comics, monochrome, sketches and landscapes sink) and appends it below what is already shown, so nothing moves under you; `newest`; or `score` |
507
- | `TTHEME_FIND_SETS` | `fold` | a run of the same picture at the same size from one uploader, and a picture another site holds too: `fold` shows it as one tile marked `×N`, `show` lists every one |
508
- | `TTHEME_FIND_REMOVE_BG` | `on` | on macOS, `on` cuts the character out of an opaque picture `find` tries on, with the system's Vision framework; `off` leaves it as it is. The row is in the `s` panel as `remove bg` |
509
- | `TTHEME_FIND_UNBLOCK` | `0` | find offers to turn it on when a site's connection is cut off. `1` sends `find`'s requests to danbooru through a proxy it runs on `127.0.0.1`, which splits the TLS handshake across two records so a network that blocks it by hostname cannot read the name; konachan and yande.re, which no network is known to block, still go direct. It is not a VPN: the address you reach is unchanged and nothing else on the machine is affected. Needs node 22.21 or newer, and the query line says `unblock` while it is on |
510
- | `TTHEME_FIND_HOSTS` | — | send a `find` site somewhere else, as `key=https://host` pairs (`danbooru=https://safebooru.donmai.us`, the general-rated mirror, reachable where danbooru is blocked). The tab keeps its name and carries `*` |
511
-
512
- ## The catalog
513
-
514
- The catalog is not installed wholesale: `init` installs the series you pick, and
515
- `init --yes` none at all. `ttheme browse` opens the catalog as a live picker —
516
- groups fold and unfold, typing filters (a query has no spaces, `space` is the
517
- pick key), the tab repaints as the cursor lands on a palette, `space` marks one
518
- (a series from its header, everything shown from `select all`), and enter
519
- installs exactly what is marked and removes what is not:
85
+ ## Usage
520
86
 
521
- ```
522
- ◆ catalog (4/107 · 2 picked)
523
- │ ⌕ bo_
524
- │ ○ select all (4)
525
- │ ▾ Bocchi the Rock! (2/4) ぼっち・ざ・ろっく!
526
- │ ▶ ● bocchi ● ▁▁▁▁▁▁ Sakura + Kessoku
527
- │ ● kita ● ▁▁▁▁▁▁ Wild Cherry + Kessoku
528
- │ ○ nijika ● ▁▁▁▁▁▁ Medallion
529
- │ ○ ryo ● ▁▁▁▁▁▁ TokyoNight Storm
530
- └ ↑↓ move · ←→ fold · space pick · type to filter · enter install · esc cancel
531
- ```
87
+ | Command | |
88
+ |---|---|
89
+ | `ttheme preview` | browse live — the tab repaints as the cursor moves, enter applies it to this tab or makes it the default |
90
+ | `ttheme use <palette>` | paint this tab (a unique prefix works) |
91
+ | `ttheme next` | advance this tab to the next palette |
92
+ | `ttheme default <palette>` | the palette new tabs open with |
93
+ | `ttheme pin` / `unpin` | a palette for this directory — `cd` in repaints, `cd` out restores |
94
+ | `ttheme browse` | pick palettes from the catalog |
95
+ | `ttheme on` / `off` | wear the default again / give the terminal its own colors back |
96
+ | `ttheme config` | settings in `$EDITOR` |
97
+
98
+ `ttheme help` lists everything. Preview's keys, directory pins, rotating new
99
+ tabs and every setting are in [docs/usage.md](docs/usage.md).
532
100
 
533
- The counts stay honest: `4/107` is what the filter matched out of the catalog,
534
- `2 picked` is the install set, and `(2/4)` on the series header is how many of
535
- its shown palettes are in it. The dot and the bar are the palette's real cursor
536
- and its ANSI colors.
101
+ ## Terminal support
537
102
 
538
- The same four verbs work without the picker:
103
+ | | Ghostty | iTerm2 | kitty | Alacritty | WezTerm | Windows Terminal | Warp | Terminal.app |
104
+ |---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
105
+ | **Setup with `init`** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — |
106
+ | **Runtime repaint** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — | ✅ |
107
+ | **Background pictures** | ✅ | ✅ | ✅ | — | ◐ | — | — | — |
539
108
 
540
- ```sh
541
- ttheme list jujutsu # what the catalog has, and what you already installed
542
- ttheme add gojo geto # two more, written into your terminal configs
543
- ttheme remove kyubey # and one fewer
544
- ttheme update # new palettes, without an npm release
545
- ```
109
+ Nothing is emulated — a terminal that cannot express something does not get it.
110
+ Any other terminal that speaks OSC 4/10/11 gets runtime repainting. Why each cell
111
+ is what it is: [docs/terminals.md](docs/terminals.md).
546
112
 
547
- `browse`, `add` and `remove` rewrite `palettes.zsh`, each wired terminal's
548
- `themes/` directory and the `theme =` line in its config, then the shell re-reads
549
- them — the change is live in the tab you ran it in. The first palette you install
550
- becomes the one new windows open with.
551
-
552
- `update` refetches the catalog from the registry; every palette in it has already
553
- passed the contrast gate in CI, and `add` checks the numbers again before it
554
- writes anything.
555
-
556
- Installed palettes are listed in `~/.config/ttheme/installed.json`, and the
557
- catalog is cached in `~/.config/ttheme/catalog.json`.
558
-
559
- ## Terminal notes
560
-
561
- WezTerm keeps an OSC-set palette per pane, but a picture belongs to the window:
562
- the Lua module ttheme runs follows the active pane's `ttheme_shown` user var,
563
- which the shell layer sets whenever the tab's palette changes, and reads the
564
- picture from `backgrounds/<palette>.conf` itself — so a picture installed or
565
- tuned from Ghostty or iTerm2 reaches WezTerm within a second.
566
-
567
- kitty gets a watcher, `~/.config/ttheme/kitty.py`, which the kitty block loads:
568
- the shell layer tells it what a window wears and shows through user vars
569
- (`ttheme_worn`, `ttheme_shown`, `ttheme_startup`), and it answers with the
570
- window's logo — kitty's per-window picture, drawn above the default background
571
- and below any cell with a background of its own, which does not scroll — read
572
- from `backgrounds/<palette>.conf` itself, so a picture tuned in another terminal
573
- shows the next time a kitty window takes focus. kitty 0.49 draws every logo at
574
- the global `window_logo_alpha` and scales every logo by `window_logo_scale`, so
575
- the block sets those to 1 and 100 and the watcher fades the picture's own alpha
576
- to the tuned opacity instead. kitty reloads its config by itself whenever
577
- `kitty.conf` or a file it includes changes, and a reload resets every color an
578
- OSC set; the watcher wraps the reload and puts each window's palette back, and
579
- `sync` writes a file only when its content changed, so installing palettes
580
- reloads nothing. The watcher reaches kitty windows opened after `init`.
581
-
582
- Alacritty reloads its config by itself and keeps OSC colors through a reload.
583
- `sync` puts its import in the config's own `[general]` table when there is one —
584
- TOML allows a table once — and leaves a config that already imports files
585
- there alone, with a note.
586
-
587
- iTerm2 reads the colors an OSC sets as Display P3 — its default color space — so
588
- its profiles and `.itermcolors` files are written in P3 too: a tab opened on a
589
- `ttheme · <palette>` profile and a tab repainted to that palette land on the
590
- same colors, and ttheme recognizes the palette either way.
591
-
592
- ## Adding a palette
593
-
594
- One file — copy any of `themes/*.toml`. `CONTRIBUTING.md` has the rules for
595
- sending one back; the short version is that this repository takes **color values
596
- only and never images**:
597
-
598
- ```toml
599
- [meta]
600
- name = "madoka" # must match the filename
601
- group = "Madoka Magica" # needs a [[group]] in themes/_groups.toml
602
- order = 15 # position in the rotation
603
- ansi_source = "Elegant + Magica" # what the harmonizer was fed
604
- booru = "kaname_madoka" # booru character tag find searches
605
- signature = ["cursor", "ansi1", "ansi3"] # the three slots the site draws as identity
606
-
607
- [colors]
608
- background = "#201516"
609
- foreground = "#fbdbdd"
610
- cursor = "#ffc1c6"
611
- selection_background = "#553444"
612
- ansi = [ ... 16 colors ... ]
613
- ```
113
+ ## Background pictures
614
114
 
615
- The series title and its `native` reading live once in `themes/_groups.toml`,
616
- never in a theme file. Then:
115
+ A palette can wear a picture behind the text. ttheme ships none: `ttheme
116
+ preview` → tab searches danbooru, konachan and yande.re for the character live
117
+ (safe-rated by default), or takes a picture you paste or drop, cuts the
118
+ character out on macOS, tints it to the palette and installs it on your machine
119
+ only. Ghostty shows the focused tab's picture, iTerm2 one per tab, kitty one per
120
+ window. See [docs/backgrounds.md](docs/backgrounds.md).
617
121
 
618
- ```sh
619
- mise run build # regenerates dist/ for every terminal — fails on bad contrast
620
- mise run build --only kitty # just one terminal's subtree
621
- mise run test
622
- mise run compat # the terminal compatibility cases, in each installed terminal
623
- ```
122
+ ## Contributing
624
123
 
625
- `mise run compat` opens each installed terminal behind your windows on a
626
- throwaway home (`mise run sandbox`), runs the cases in `tests/compat/cases.zsh`
627
- inside it — adapter detection, OSC set/query/reset, what the window really
628
- paints (read off a screenshot), `ttheme use <palette>` and its restore, cell size,
629
- kitty graphics, synchronized output and focus reporting — and compares them
630
- with `tests/compat/expect.tsv`: a case that used to pass and fails is a
631
- regression and fails the run, and `--update` records what was measured.
632
-
633
- All one hundred and eight currently pass unwaived — `kyubey` runs tightest at
634
- 4.28:1 where accents need 3:1: it is the one light palette, so every accent has
635
- to darken enough to hold against a near-white background. If a palette genuinely
636
- has to break a rule, waive it by name and say why; a waiver without a reason
637
- fails the test suite.
638
-
639
- ```toml
640
- [contrast]
641
- waive = ["foreground"] # foreground | accents | ansi0-dark | light-ansi | ansi8-visible
642
- reason = "why this palette is the exception"
643
- ```
124
+ A palette is one TOML file of color values — never images.
125
+ [CONTRIBUTING.md](CONTRIBUTING.md) has the rules, the file format and the
126
+ checks it must pass.
644
127
 
645
128
  ## Credits
646
129
 
@@ -661,3 +144,7 @@ audio or trademarked asset is redistributed here — only color values.
661
144
  Terminal background images are downloaded from the booru you pick only when you
662
145
  ask `find` for one, built on your own machine and written to
663
146
  `~/.config/ttheme/backgrounds/`; none ship in this repository or on npm.
147
+
148
+ ## License
149
+
150
+ [MIT](LICENSE)