zettcode-tui 0.4.0__py3-none-any.whl
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.
- zettcode/tui/__init__.py +422 -0
- zettcode/tui/_compat.py +32 -0
- zettcode/tui/capabilities.py +107 -0
- zettcode/tui/clipboard.py +91 -0
- zettcode/tui/console.py +229 -0
- zettcode/tui/core/__init__.py +63 -0
- zettcode/tui/core/app.py +658 -0
- zettcode/tui/core/events.py +134 -0
- zettcode/tui/core/focus.py +85 -0
- zettcode/tui/core/geometry.py +212 -0
- zettcode/tui/core/host.py +66 -0
- zettcode/tui/core/keymap.py +218 -0
- zettcode/tui/core/scheduler.py +105 -0
- zettcode/tui/core/screen.py +80 -0
- zettcode/tui/core/theme.py +248 -0
- zettcode/tui/core/widget.py +204 -0
- zettcode/tui/gallery.py +588 -0
- zettcode/tui/glyphs.py +73 -0
- zettcode/tui/input/__init__.py +18 -0
- zettcode/tui/input/bridge.py +46 -0
- zettcode/tui/input/decoder.py +234 -0
- zettcode/tui/input/events.py +64 -0
- zettcode/tui/input/keys.py +289 -0
- zettcode/tui/input/reader.py +147 -0
- zettcode/tui/layout/__init__.py +24 -0
- zettcode/tui/layout/box.py +153 -0
- zettcode/tui/layout/overlay.py +164 -0
- zettcode/tui/layout/scroll.py +477 -0
- zettcode/tui/layout/solver.py +95 -0
- zettcode/tui/layout/spacing.py +137 -0
- zettcode/tui/py.typed +0 -0
- zettcode/tui/render/__init__.py +71 -0
- zettcode/tui/render/ansi.py +371 -0
- zettcode/tui/render/canvas.py +153 -0
- zettcode/tui/render/code.py +456 -0
- zettcode/tui/render/color.py +177 -0
- zettcode/tui/render/renderer.py +151 -0
- zettcode/tui/render/rich_text.py +80 -0
- zettcode/tui/render/style.py +121 -0
- zettcode/tui/render/sweep.py +87 -0
- zettcode/tui/render/text.py +319 -0
- zettcode/tui/runner.py +316 -0
- zettcode/tui/terminal.py +222 -0
- zettcode/tui/testing/__init__.py +6 -0
- zettcode/tui/testing/harness.py +220 -0
- zettcode/tui/testing/snapshot.py +64 -0
- zettcode/tui/theme_file.py +300 -0
- zettcode/tui/themes/ayu-mirage.toml +44 -0
- zettcode/tui/themes/catppuccin-latte.toml +44 -0
- zettcode/tui/themes/catppuccin-mocha.toml +44 -0
- zettcode/tui/themes/dracula.toml +44 -0
- zettcode/tui/themes/github-dimmed.toml +44 -0
- zettcode/tui/themes/github-light.toml +44 -0
- zettcode/tui/themes/gruvbox-dark.toml +44 -0
- zettcode/tui/themes/gruvbox-light.toml +44 -0
- zettcode/tui/themes/kanagawa.toml +44 -0
- zettcode/tui/themes/nord.toml +44 -0
- zettcode/tui/themes/one-dark.toml +44 -0
- zettcode/tui/themes/rose-pine-dawn.toml +44 -0
- zettcode/tui/themes/rose-pine.toml +44 -0
- zettcode/tui/themes/solarized-dark.toml +44 -0
- zettcode/tui/themes/solarized-light.toml +44 -0
- zettcode/tui/themes/tokyo-night.toml +44 -0
- zettcode/tui/widgets/__init__.py +121 -0
- zettcode/tui/widgets/collapsible.py +116 -0
- zettcode/tui/widgets/completion.py +211 -0
- zettcode/tui/widgets/console.py +442 -0
- zettcode/tui/widgets/dialog.py +165 -0
- zettcode/tui/widgets/diff.py +236 -0
- zettcode/tui/widgets/list.py +293 -0
- zettcode/tui/widgets/list_page.py +121 -0
- zettcode/tui/widgets/markdown.py +657 -0
- zettcode/tui/widgets/progress.py +166 -0
- zettcode/tui/widgets/rich_text.py +84 -0
- zettcode/tui/widgets/status.py +106 -0
- zettcode/tui/widgets/table.py +101 -0
- zettcode/tui/widgets/tasks.py +130 -0
- zettcode/tui/widgets/text.py +111 -0
- zettcode/tui/widgets/textarea.py +671 -0
- zettcode/tui/widgets/toast.py +137 -0
- zettcode_tui-0.4.0.dist-info/METADATA +20 -0
- zettcode_tui-0.4.0.dist-info/RECORD +83 -0
- zettcode_tui-0.4.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"""Load a theme palette from TOML so colours do not have to be edited in code.
|
|
2
|
+
|
|
3
|
+
A palette is a file, and a palette's name is the file's name. The ones that ship with the program are TOML files in
|
|
4
|
+
``themes/`` beside this module, and the reader's own are ``<name>.toml`` files in a directory of theirs
|
|
5
|
+
(``~/.zettcode/themes``); both are read by the same code, so adding a palette is writing one file and nothing else.
|
|
6
|
+
Only the *names* are listed up front (a directory listing); a palette's file is read when it is asked for, so a
|
|
7
|
+
start pays for no palette it does not use.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
14
|
+
from dataclasses import dataclass, fields, replace
|
|
15
|
+
from functools import partial
|
|
16
|
+
from importlib import resources
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from ._compat import tomllib
|
|
20
|
+
from .core.theme import DARK, LIGHT, Theme, ToolTheme, theme_named
|
|
21
|
+
from .render.code import CodeTheme
|
|
22
|
+
from .render.color import parse_hex
|
|
23
|
+
|
|
24
|
+
#: The palettes that ship as files, in the order a menu lists them: the dark ones, then the light ones.
|
|
25
|
+
BUNDLED = (
|
|
26
|
+
"catppuccin-mocha",
|
|
27
|
+
"tokyo-night",
|
|
28
|
+
"nord",
|
|
29
|
+
"gruvbox-dark",
|
|
30
|
+
"rose-pine",
|
|
31
|
+
"kanagawa",
|
|
32
|
+
"dracula",
|
|
33
|
+
"one-dark",
|
|
34
|
+
"solarized-dark",
|
|
35
|
+
"github-dimmed",
|
|
36
|
+
"ayu-mirage",
|
|
37
|
+
"catppuccin-latte",
|
|
38
|
+
"rose-pine-dawn",
|
|
39
|
+
"gruvbox-light",
|
|
40
|
+
"solarized-light",
|
|
41
|
+
"github-light",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
#: What a palette's name may be. It is typed after ``/theme`` as one word, so it holds no space.
|
|
45
|
+
THEME_NAME = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]*")
|
|
46
|
+
|
|
47
|
+
#: Names the program reserves: they are what the terminal's own light or dark is mapped to, and a file cannot
|
|
48
|
+
#: take them over.
|
|
49
|
+
RESERVED_THEMES = (DARK.name, LIGHT.name)
|
|
50
|
+
|
|
51
|
+
#: The page the two built-in palettes paint when they are chosen by name.
|
|
52
|
+
PAGES = {DARK.name: "#232a2e", LIGHT.name: "#f7f8f5"}
|
|
53
|
+
|
|
54
|
+
EXAMPLE = """\
|
|
55
|
+
base = "dark" # dark | light
|
|
56
|
+
description = "my palette" # shown beside its name in the /theme menu
|
|
57
|
+
|
|
58
|
+
[ui]
|
|
59
|
+
accent = "#a7c080"
|
|
60
|
+
error = "#e67e80"
|
|
61
|
+
# diff_added = "#a7c080" # default: the accent
|
|
62
|
+
# diff_removed = "#e67e80" # default: the error colour
|
|
63
|
+
surface_side = "#393a44"
|
|
64
|
+
surface_peer = "#313244"
|
|
65
|
+
|
|
66
|
+
[tools]
|
|
67
|
+
read = "#89b4fa"
|
|
68
|
+
shell = "#fab387"
|
|
69
|
+
|
|
70
|
+
[code]
|
|
71
|
+
keyword = "#e58fa8"
|
|
72
|
+
string = "#9bddad"
|
|
73
|
+
comment = "#6d7a70"
|
|
74
|
+
number = "#d8b46a"
|
|
75
|
+
function = "#7fb7d8"
|
|
76
|
+
builtin = "#b8a6e0"
|
|
77
|
+
inline = "#9bddad"
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ThemeFileError(ValueError):
|
|
82
|
+
"""Raised when a theme file cannot be understood."""
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def theme_from_toml(text: str, *, base: Theme | None = None) -> Theme:
|
|
86
|
+
"""Build a theme from TOML text, starting from its base palette.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
text: TOML source; ``base`` selects ``dark`` or ``light`` and the ``ui``,
|
|
90
|
+
``tools``, and ``code`` tables override individual roles, so a file
|
|
91
|
+
only names the colours it wants to change.
|
|
92
|
+
base: Palette to override directly; when omitted the file's ``base`` key
|
|
93
|
+
(default ``dark``) is resolved through ``theme_named``.
|
|
94
|
+
"""
|
|
95
|
+
try:
|
|
96
|
+
data = tomllib.loads(text)
|
|
97
|
+
except tomllib.TOMLDecodeError as error:
|
|
98
|
+
raise ThemeFileError(f"Invalid theme file: {error}") from error
|
|
99
|
+
if not isinstance(data, dict):
|
|
100
|
+
raise ThemeFileError("Theme file must be a table")
|
|
101
|
+
|
|
102
|
+
palette = base
|
|
103
|
+
if palette is None:
|
|
104
|
+
requested = data.get("base", DARK.name)
|
|
105
|
+
try:
|
|
106
|
+
palette = theme_named(str(requested))
|
|
107
|
+
except ValueError as error:
|
|
108
|
+
raise ThemeFileError(str(error)) from error
|
|
109
|
+
|
|
110
|
+
ui = _select(data.get("ui", {}), {field.name for field in fields(Theme)} - {"code", "tools"})
|
|
111
|
+
tools = _select(
|
|
112
|
+
data.get("tools", {}),
|
|
113
|
+
{field.name for field in fields(ToolTheme)},
|
|
114
|
+
hyphenated={"web-search": "web_search", "ask-user": "ask_user"},
|
|
115
|
+
)
|
|
116
|
+
code = _select(data.get("code", {}), {field.name for field in fields(CodeTheme)})
|
|
117
|
+
return replace(palette, **ui, tools=replace(palette.tools, **tools), code=replace(palette.code, **code))
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def load_theme(path: Path, *, base: Theme | None = None) -> Theme:
|
|
121
|
+
"""Read a theme file from disk.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
path: File to read; a missing or unreadable file raises
|
|
125
|
+
``ThemeFileError`` rather than an ``OSError``.
|
|
126
|
+
base: Palette to override directly, bypassing the file's ``base`` key.
|
|
127
|
+
"""
|
|
128
|
+
try:
|
|
129
|
+
text = path.read_text(encoding="utf-8")
|
|
130
|
+
except OSError as error:
|
|
131
|
+
raise ThemeFileError(f"Cannot read {path}: {error}") from error
|
|
132
|
+
return theme_from_toml(text, base=base)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@dataclass(frozen=True, slots=True)
|
|
136
|
+
class ThemeSource:
|
|
137
|
+
"""A palette that something offers by name: the program's own files, or a plugin.
|
|
138
|
+
|
|
139
|
+
Attributes:
|
|
140
|
+
name: What ``/theme`` is typed with; one word.
|
|
141
|
+
load: Builds the palette. Called when it is asked for, never before, so a source costs nothing until picked.
|
|
142
|
+
description: The one-line note the menu shows beside the name, or a function that returns it (called only
|
|
143
|
+
when a menu is opened, for the same reason).
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
name: str
|
|
147
|
+
load: Callable[[], Theme]
|
|
148
|
+
description: str | Callable[[], str] = ""
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _bundled_text(name: str) -> str:
|
|
152
|
+
"""Return the text of one palette file that ships with the program."""
|
|
153
|
+
return (resources.files(__package__) / "themes" / f"{name}.toml").read_text(encoding="utf-8")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _load_bundled(name: str) -> Theme:
|
|
157
|
+
return theme_from_toml(_bundled_text(name))
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _bundled_note(name: str) -> str:
|
|
161
|
+
note = tomllib.loads(_bundled_text(name)).get("description")
|
|
162
|
+
return note if isinstance(note, str) else ""
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _file_note(path: Path) -> str:
|
|
166
|
+
note = tomllib.loads(path.read_text(encoding="utf-8")).get("description")
|
|
167
|
+
return note if isinstance(note, str) else ""
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def file_theme(name: str, path: Path) -> ThemeSource:
|
|
171
|
+
"""Return a source for a palette kept in a TOML file, read when it is asked for and not before."""
|
|
172
|
+
return ThemeSource(name, partial(load_theme, path), partial(_file_note, path))
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def bundled_themes() -> tuple[ThemeSource, ...]:
|
|
176
|
+
"""Return the palettes that ship as files, as sources: the built-in plugin registers these.
|
|
177
|
+
|
|
178
|
+
Nothing is read here; each file is opened when its palette is asked for, or its note when a menu is.
|
|
179
|
+
"""
|
|
180
|
+
return tuple(ThemeSource(name, partial(_load_bundled, name), partial(_bundled_note, name)) for name in BUNDLED)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _user_file(name: str, directory: Path | None) -> Path | None:
|
|
184
|
+
"""Return the reader's file for a palette name, or ``None``. A name that is not one word is never a file."""
|
|
185
|
+
if directory is None or name in RESERVED_THEMES or not THEME_NAME.fullmatch(name):
|
|
186
|
+
return None
|
|
187
|
+
path = directory / f"{name}.toml"
|
|
188
|
+
return path if path.is_file() else None
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _user_names(directory: Path | None) -> tuple[str, ...]:
|
|
192
|
+
"""Return the names of the reader's palettes: the ``.toml`` files in their directory, by name."""
|
|
193
|
+
if directory is None:
|
|
194
|
+
return ()
|
|
195
|
+
try:
|
|
196
|
+
stems = sorted(path.stem for path in directory.glob("*.toml") if path.is_file())
|
|
197
|
+
except OSError:
|
|
198
|
+
return ()
|
|
199
|
+
return tuple(stem for stem in stems if THEME_NAME.fullmatch(stem) and stem not in RESERVED_THEMES)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class ThemeCatalog:
|
|
203
|
+
"""Every palette that can be asked for by name, and where each comes from.
|
|
204
|
+
|
|
205
|
+
There are three places, in this order: the two the terminal's own light or dark maps to (``dark`` and ``light``,
|
|
206
|
+
which nothing can replace), the *sources* (the built-in plugin's files, then any other plugin's, in the order
|
|
207
|
+
they loaded), and the reader's own ``<name>.toml`` files. A reader's file shadows a source of the same name,
|
|
208
|
+
which is how a palette that ships is adjusted: copy it, change what is wrong with it, keep the name. A source
|
|
209
|
+
whose name is taken is ignored, the first one winning.
|
|
210
|
+
|
|
211
|
+
Only names are listed up front. A palette is built when it is asked for, so a catalog of sixty costs a start
|
|
212
|
+
nothing.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
sources: What plugins and the program offer.
|
|
216
|
+
directory: The reader's palette directory; one that is missing is the same as an empty one.
|
|
217
|
+
"""
|
|
218
|
+
|
|
219
|
+
def __init__(self, sources: Sequence[ThemeSource] = (), directory: Path | None = None) -> None:
|
|
220
|
+
chosen: dict[str, ThemeSource] = {}
|
|
221
|
+
for source in sources:
|
|
222
|
+
if source.name not in RESERVED_THEMES and source.name not in chosen and THEME_NAME.fullmatch(source.name):
|
|
223
|
+
chosen[source.name] = source
|
|
224
|
+
self._sources = chosen
|
|
225
|
+
self.directory = directory
|
|
226
|
+
|
|
227
|
+
def names(self) -> tuple[str, ...]:
|
|
228
|
+
"""Return the names in the order a menu lists them. Nothing is parsed."""
|
|
229
|
+
own = [name for name in _user_names(self.directory) if name not in self._sources]
|
|
230
|
+
return (*RESERVED_THEMES, *self._sources, *own)
|
|
231
|
+
|
|
232
|
+
def load(self, name: str) -> Theme:
|
|
233
|
+
"""Return the palette called ``name``.
|
|
234
|
+
|
|
235
|
+
Raises:
|
|
236
|
+
ThemeFileError: There is no such palette, or it cannot be built or understood. The message names what is
|
|
237
|
+
available, so a typo is answered by the list.
|
|
238
|
+
"""
|
|
239
|
+
if name in RESERVED_THEMES:
|
|
240
|
+
# Chosen by name, they paint their own page like every other palette: a light one over a dark terminal is
|
|
241
|
+
# light, not dark text on the terminal's black. Only the *automatic* pick leaves the page alone.
|
|
242
|
+
return replace(theme_named(name), background=PAGES[name])
|
|
243
|
+
path = _user_file(name, self.directory)
|
|
244
|
+
if path is not None:
|
|
245
|
+
loaded = load_theme(path)
|
|
246
|
+
elif name in self._sources:
|
|
247
|
+
try:
|
|
248
|
+
loaded = self._sources[name].load()
|
|
249
|
+
except ThemeFileError:
|
|
250
|
+
raise
|
|
251
|
+
except Exception as error: # noqa: BLE001 - a plugin's palette is its author's code
|
|
252
|
+
raise ThemeFileError(f"Theme {name!r} could not be built: {error}") from error
|
|
253
|
+
else:
|
|
254
|
+
raise ThemeFileError(f"Unknown theme: {name!r}. Available: {', '.join(self.names())}")
|
|
255
|
+
return replace(loaded, name=name, code=replace(loaded.code, name=name))
|
|
256
|
+
|
|
257
|
+
def notes(self) -> dict[str, str]:
|
|
258
|
+
"""Return the one-line note each palette carries, by name; a palette with none is left out.
|
|
259
|
+
|
|
260
|
+
Read from the files and the plugins, so this is for a menu that is being opened and not for the start.
|
|
261
|
+
Whatever cannot be read has no note, and is not an error here: it is the error of the person who picks it.
|
|
262
|
+
"""
|
|
263
|
+
notes: dict[str, str] = {}
|
|
264
|
+
for name in self.names():
|
|
265
|
+
if name in RESERVED_THEMES:
|
|
266
|
+
continue
|
|
267
|
+
path = _user_file(name, self.directory)
|
|
268
|
+
try:
|
|
269
|
+
if path is not None:
|
|
270
|
+
data = tomllib.loads(path.read_text(encoding="utf-8")).get("description")
|
|
271
|
+
else:
|
|
272
|
+
described = self._sources[name].description
|
|
273
|
+
data = described() if callable(described) else described
|
|
274
|
+
except Exception: # noqa: BLE001 - see above
|
|
275
|
+
continue
|
|
276
|
+
if isinstance(data, str) and data.strip():
|
|
277
|
+
notes[name] = data.strip()
|
|
278
|
+
return notes
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _select(values: object, allowed: set[str], *, hyphenated: Mapping[str, str] | None = None) -> dict[str, str]:
|
|
282
|
+
"""Validate one palette table against the keys the theme knows.
|
|
283
|
+
|
|
284
|
+
A role whose name is a word rather than an identifier is written with a hyphen in the file (``web-search``)
|
|
285
|
+
and kept as the field it names (``web_search``); ``hyphenated`` is that spelling, when a table has one.
|
|
286
|
+
"""
|
|
287
|
+
if not isinstance(values, dict):
|
|
288
|
+
raise ThemeFileError("Palette sections must be tables")
|
|
289
|
+
spelled = hyphenated or {}
|
|
290
|
+
chosen: dict[str, str] = {}
|
|
291
|
+
for key, value in values.items():
|
|
292
|
+
name = spelled.get(key, key)
|
|
293
|
+
if name not in allowed:
|
|
294
|
+
raise ThemeFileError(f"Unknown theme key: {key!r}")
|
|
295
|
+
if not isinstance(value, str):
|
|
296
|
+
raise ThemeFileError(f"{key} must be a string colour")
|
|
297
|
+
if name != "name" and parse_hex(value) is None:
|
|
298
|
+
raise ThemeFileError(f"{key} must be a #rrggbb colour, got {value!r}")
|
|
299
|
+
chosen[name] = value
|
|
300
|
+
return chosen
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "blue-grey with warm gold"
|
|
2
|
+
base = "dark"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#1f2430"
|
|
6
|
+
surface = "#242936"
|
|
7
|
+
surface_alt = "#2d3446"
|
|
8
|
+
surface_side = "#344458"
|
|
9
|
+
surface_peer = "#44465e"
|
|
10
|
+
text = "#cccac2"
|
|
11
|
+
muted = "#8d95a3"
|
|
12
|
+
subtle = "#a7aebc"
|
|
13
|
+
accent = "#ffcc66"
|
|
14
|
+
accent_bright = "#ffd173"
|
|
15
|
+
warning = "#ffcc66"
|
|
16
|
+
error = "#f28779"
|
|
17
|
+
border = "#33415e"
|
|
18
|
+
focus = "#ffd173"
|
|
19
|
+
selection = "#34455a"
|
|
20
|
+
diff_added = "#87d96c"
|
|
21
|
+
diff_removed = "#f28779"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#ffcc66"
|
|
25
|
+
search = "#73d0ff"
|
|
26
|
+
image = "#73d0ff"
|
|
27
|
+
write = "#ffad66"
|
|
28
|
+
delete = "#f28779"
|
|
29
|
+
shell = "#73d0ff"
|
|
30
|
+
plan = "#73d0ff"
|
|
31
|
+
subagent = "#5ccfe6"
|
|
32
|
+
message = "#f29e74"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#87d96c"
|
|
36
|
+
text = "#cccac2"
|
|
37
|
+
keyword = "#ffad66"
|
|
38
|
+
string = "#87d96c"
|
|
39
|
+
comment = "#8d95a3"
|
|
40
|
+
number = "#ffad66"
|
|
41
|
+
function = "#73d0ff"
|
|
42
|
+
builtin = "#dfbfff"
|
|
43
|
+
operator = "#a7aebc"
|
|
44
|
+
punctuator = "#a7aebc"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "creamy lavender-grey, saturated accents"
|
|
2
|
+
base = "light"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#eff1f5"
|
|
6
|
+
surface = "#f7f8fb"
|
|
7
|
+
surface_alt = "#e6e9ef"
|
|
8
|
+
surface_side = "#d2dcf0"
|
|
9
|
+
surface_peer = "#dad2ef"
|
|
10
|
+
text = "#484b64"
|
|
11
|
+
muted = "#6c6f81"
|
|
12
|
+
subtle = "#575a71"
|
|
13
|
+
accent = "#8839ef"
|
|
14
|
+
accent_bright = "#5b6cca"
|
|
15
|
+
warning = "#a26715"
|
|
16
|
+
error = "#d20f39"
|
|
17
|
+
border = "#ccd0da"
|
|
18
|
+
focus = "#5b6cca"
|
|
19
|
+
selection = "#ccd0da"
|
|
20
|
+
diff_added = "#358423"
|
|
21
|
+
diff_removed = "#d20f39"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#a26715"
|
|
25
|
+
search = "#5b6cca"
|
|
26
|
+
image = "#5b6cca"
|
|
27
|
+
write = "#c54e09"
|
|
28
|
+
delete = "#d20f39"
|
|
29
|
+
shell = "#5b6cca"
|
|
30
|
+
plan = "#5b6cca"
|
|
31
|
+
subagent = "#148086"
|
|
32
|
+
message = "#aa5693"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#358423"
|
|
36
|
+
text = "#484b64"
|
|
37
|
+
keyword = "#8839ef"
|
|
38
|
+
string = "#358423"
|
|
39
|
+
comment = "#6c6f81"
|
|
40
|
+
number = "#c54e09"
|
|
41
|
+
function = "#1e66f5"
|
|
42
|
+
builtin = "#8839ef"
|
|
43
|
+
operator = "#575a71"
|
|
44
|
+
punctuator = "#575a71"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "soft purple-grey, pastel accents"
|
|
2
|
+
base = "dark"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#1e1e2e"
|
|
6
|
+
surface = "#262637"
|
|
7
|
+
surface_alt = "#313244"
|
|
8
|
+
surface_side = "#3a3f56"
|
|
9
|
+
surface_peer = "#45415b"
|
|
10
|
+
text = "#cdd6f4"
|
|
11
|
+
muted = "#8f93a8"
|
|
12
|
+
subtle = "#a6adc8"
|
|
13
|
+
accent = "#cba6f7"
|
|
14
|
+
accent_bright = "#b4befe"
|
|
15
|
+
warning = "#f9e2af"
|
|
16
|
+
error = "#f38ba8"
|
|
17
|
+
border = "#45475a"
|
|
18
|
+
focus = "#b4befe"
|
|
19
|
+
selection = "#45475a"
|
|
20
|
+
diff_added = "#a6e3a1"
|
|
21
|
+
diff_removed = "#f38ba8"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#f9e2af"
|
|
25
|
+
search = "#b4befe"
|
|
26
|
+
image = "#b4befe"
|
|
27
|
+
write = "#fab387"
|
|
28
|
+
delete = "#f38ba8"
|
|
29
|
+
shell = "#b4befe"
|
|
30
|
+
plan = "#b4befe"
|
|
31
|
+
subagent = "#94e2d5"
|
|
32
|
+
message = "#f5c2e7"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#a6e3a1"
|
|
36
|
+
text = "#cdd6f4"
|
|
37
|
+
keyword = "#cba6f7"
|
|
38
|
+
string = "#a6e3a1"
|
|
39
|
+
comment = "#8f93a8"
|
|
40
|
+
number = "#fab387"
|
|
41
|
+
function = "#89b4fa"
|
|
42
|
+
builtin = "#cba6f7"
|
|
43
|
+
operator = "#a6adc8"
|
|
44
|
+
punctuator = "#a6adc8"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "vivid purple, pink and green"
|
|
2
|
+
base = "dark"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#282a36"
|
|
6
|
+
surface = "#2f3140"
|
|
7
|
+
surface_alt = "#343746"
|
|
8
|
+
surface_side = "#3d4958"
|
|
9
|
+
surface_peer = "#46435d"
|
|
10
|
+
text = "#f8f8f2"
|
|
11
|
+
muted = "#8d99bd"
|
|
12
|
+
subtle = "#bdc0d6"
|
|
13
|
+
accent = "#bd93f9"
|
|
14
|
+
accent_bright = "#ff79c6"
|
|
15
|
+
warning = "#f1fa8c"
|
|
16
|
+
error = "#ff5555"
|
|
17
|
+
border = "#44475a"
|
|
18
|
+
focus = "#ff79c6"
|
|
19
|
+
selection = "#44475a"
|
|
20
|
+
diff_added = "#50fa7b"
|
|
21
|
+
diff_removed = "#ff5555"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#f1fa8c"
|
|
25
|
+
search = "#8be9fd"
|
|
26
|
+
image = "#8be9fd"
|
|
27
|
+
write = "#ffb86c"
|
|
28
|
+
delete = "#ff5555"
|
|
29
|
+
shell = "#8be9fd"
|
|
30
|
+
plan = "#8be9fd"
|
|
31
|
+
subagent = "#8be9fd"
|
|
32
|
+
message = "#ff79c6"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#50fa7b"
|
|
36
|
+
text = "#f8f8f2"
|
|
37
|
+
keyword = "#ff79c6"
|
|
38
|
+
string = "#50fa7b"
|
|
39
|
+
comment = "#8d99bd"
|
|
40
|
+
number = "#ffb86c"
|
|
41
|
+
function = "#8be9fd"
|
|
42
|
+
builtin = "#bd93f9"
|
|
43
|
+
operator = "#bdc0d6"
|
|
44
|
+
punctuator = "#bdc0d6"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "GitHub's dimmed dark, restrained"
|
|
2
|
+
base = "dark"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#22272e"
|
|
6
|
+
surface = "#2d333b"
|
|
7
|
+
surface_alt = "#373e47"
|
|
8
|
+
surface_side = "#3a4758"
|
|
9
|
+
surface_peer = "#44445b"
|
|
10
|
+
text = "#cad2db"
|
|
11
|
+
muted = "#959fa9"
|
|
12
|
+
subtle = "#b1bac4"
|
|
13
|
+
accent = "#539bf5"
|
|
14
|
+
accent_bright = "#6cb6ff"
|
|
15
|
+
warning = "#c69026"
|
|
16
|
+
error = "#e5534b"
|
|
17
|
+
border = "#444c56"
|
|
18
|
+
focus = "#6cb6ff"
|
|
19
|
+
selection = "#2f4766"
|
|
20
|
+
diff_added = "#57ab5a"
|
|
21
|
+
diff_removed = "#e5534b"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#c69026"
|
|
25
|
+
search = "#539bf5"
|
|
26
|
+
image = "#539bf5"
|
|
27
|
+
write = "#cc6b2c"
|
|
28
|
+
delete = "#e5534b"
|
|
29
|
+
shell = "#539bf5"
|
|
30
|
+
plan = "#539bf5"
|
|
31
|
+
subagent = "#39c5cf"
|
|
32
|
+
message = "#e275ad"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#57ab5a"
|
|
36
|
+
text = "#cad2db"
|
|
37
|
+
keyword = "#f47067"
|
|
38
|
+
string = "#57ab5a"
|
|
39
|
+
comment = "#959fa9"
|
|
40
|
+
number = "#cc6b2c"
|
|
41
|
+
function = "#539bf5"
|
|
42
|
+
builtin = "#986ee2"
|
|
43
|
+
operator = "#b1bac4"
|
|
44
|
+
punctuator = "#b1bac4"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "plain white, standard blue/green/red"
|
|
2
|
+
base = "light"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#ffffff"
|
|
6
|
+
surface = "#f6f8fa"
|
|
7
|
+
surface_alt = "#eaeef2"
|
|
8
|
+
surface_side = "#d4e1f0"
|
|
9
|
+
surface_peer = "#dcd9f0"
|
|
10
|
+
text = "#1f2328"
|
|
11
|
+
muted = "#656d76"
|
|
12
|
+
subtle = "#555e67"
|
|
13
|
+
accent = "#0969da"
|
|
14
|
+
accent_bright = "#0550ae"
|
|
15
|
+
warning = "#9a6700"
|
|
16
|
+
error = "#cf222e"
|
|
17
|
+
border = "#d0d7de"
|
|
18
|
+
focus = "#0550ae"
|
|
19
|
+
selection = "#ddf4ff"
|
|
20
|
+
diff_added = "#1a7f37"
|
|
21
|
+
diff_removed = "#cf222e"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#9a6700"
|
|
25
|
+
search = "#0969da"
|
|
26
|
+
image = "#0969da"
|
|
27
|
+
write = "#bc4c00"
|
|
28
|
+
delete = "#cf222e"
|
|
29
|
+
shell = "#0969da"
|
|
30
|
+
plan = "#0969da"
|
|
31
|
+
subagent = "#1b7c83"
|
|
32
|
+
message = "#bf3989"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#1a7f37"
|
|
36
|
+
text = "#1f2328"
|
|
37
|
+
keyword = "#cf222e"
|
|
38
|
+
string = "#1a7f37"
|
|
39
|
+
comment = "#656d76"
|
|
40
|
+
number = "#bc4c00"
|
|
41
|
+
function = "#0969da"
|
|
42
|
+
builtin = "#8250df"
|
|
43
|
+
operator = "#555e67"
|
|
44
|
+
punctuator = "#555e67"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "warm brown-yellow, retro paper"
|
|
2
|
+
base = "dark"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#282828"
|
|
6
|
+
surface = "#32302f"
|
|
7
|
+
surface_alt = "#3c3836"
|
|
8
|
+
surface_side = "#434340"
|
|
9
|
+
surface_peer = "#504243"
|
|
10
|
+
text = "#ebdbb2"
|
|
11
|
+
muted = "#a29689"
|
|
12
|
+
subtle = "#c0b298"
|
|
13
|
+
accent = "#fe8019"
|
|
14
|
+
accent_bright = "#fabd2f"
|
|
15
|
+
warning = "#d79921"
|
|
16
|
+
error = "#fb4934"
|
|
17
|
+
border = "#504945"
|
|
18
|
+
focus = "#fabd2f"
|
|
19
|
+
selection = "#504945"
|
|
20
|
+
diff_added = "#b8bb26"
|
|
21
|
+
diff_removed = "#fb4934"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#d79921"
|
|
25
|
+
search = "#83a598"
|
|
26
|
+
image = "#83a598"
|
|
27
|
+
write = "#fe8019"
|
|
28
|
+
delete = "#fb4934"
|
|
29
|
+
shell = "#83a598"
|
|
30
|
+
plan = "#83a598"
|
|
31
|
+
subagent = "#8ec07c"
|
|
32
|
+
message = "#d3869b"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#b8bb26"
|
|
36
|
+
text = "#ebdbb2"
|
|
37
|
+
keyword = "#fb4934"
|
|
38
|
+
string = "#b8bb26"
|
|
39
|
+
comment = "#a29689"
|
|
40
|
+
number = "#fe8019"
|
|
41
|
+
function = "#83a598"
|
|
42
|
+
builtin = "#d3869b"
|
|
43
|
+
operator = "#c0b298"
|
|
44
|
+
punctuator = "#c0b298"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "cream paper, brown text"
|
|
2
|
+
base = "light"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#fbf1c7"
|
|
6
|
+
surface = "#f9f5d7"
|
|
7
|
+
surface_alt = "#ebdbb2"
|
|
8
|
+
surface_side = "#d4cfac"
|
|
9
|
+
surface_peer = "#dfc7aa"
|
|
10
|
+
text = "#3c3836"
|
|
11
|
+
muted = "#73675c"
|
|
12
|
+
subtle = "#504945"
|
|
13
|
+
accent = "#af3a03"
|
|
14
|
+
accent_bright = "#9e6712"
|
|
15
|
+
warning = "#9e6712"
|
|
16
|
+
error = "#9d0006"
|
|
17
|
+
border = "#d5c4a1"
|
|
18
|
+
focus = "#9e6712"
|
|
19
|
+
selection = "#d5c4a1"
|
|
20
|
+
diff_added = "#79740e"
|
|
21
|
+
diff_removed = "#9d0006"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#9e6712"
|
|
25
|
+
search = "#076678"
|
|
26
|
+
image = "#076678"
|
|
27
|
+
write = "#af3a03"
|
|
28
|
+
delete = "#9d0006"
|
|
29
|
+
shell = "#076678"
|
|
30
|
+
plan = "#076678"
|
|
31
|
+
subagent = "#427b58"
|
|
32
|
+
message = "#8f3f71"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#79740e"
|
|
36
|
+
text = "#3c3836"
|
|
37
|
+
keyword = "#9d0006"
|
|
38
|
+
string = "#79740e"
|
|
39
|
+
comment = "#73675c"
|
|
40
|
+
number = "#af3a03"
|
|
41
|
+
function = "#076678"
|
|
42
|
+
builtin = "#8f3f71"
|
|
43
|
+
operator = "#504945"
|
|
44
|
+
punctuator = "#504945"
|