summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.zig59
-rw-r--r--docs/design.typ20
-rw-r--r--docs/helix-keys.md2
-rw-r--r--next-steps.txt2
-rw-r--r--src/builtins.zig32
-rw-r--r--src/config.zig6
-rw-r--r--src/output_pane.zig54
-rw-r--r--src/pardes.zig159
-rw-r--r--src/themes/acme.zig29
-rw-r--r--src/themes/dark.zig22
-rw-r--r--src/themes/helix.zig47
-rw-r--r--test/snapshots/leader.golden4
-rw-r--r--test/snapshots/theme.golden64
-rw-r--r--test/snapshots/theme.snap15
-rw-r--r--test/snapshots/themesel.golden434
-rw-r--r--test/snapshots/themesel.snap63
-rw-r--r--tools/gen_themes.zig414
-rw-r--r--vendor/themes/ayu.LICENSE21
-rw-r--r--vendor/themes/ayu.json1228
-rw-r--r--vendor/themes/catppuccin_mocha.toml168
-rw-r--r--vendor/themes/dracula.toml159
-rw-r--r--vendor/themes/everforest_light.toml150
-rw-r--r--vendor/themes/gruvbox.toml153
-rw-r--r--vendor/themes/helix.LICENSE373
-rw-r--r--vendor/themes/monokai.toml121
-rw-r--r--vendor/themes/nord.toml199
-rw-r--r--vendor/themes/one.LICENSE21
-rw-r--r--vendor/themes/one.json843
-rw-r--r--vendor/themes/solarized_light.toml155
29 files changed, 4870 insertions, 147 deletions
diff --git a/build.zig b/build.zig
index c11ccccd..04516cc8 100644
--- a/build.zig
+++ b/build.zig
@@ -93,7 +93,8 @@ pub fn build(b: *std.Build) void {
else
null;
- // build-time IO, only used to slurp the grammars' highlights.scm queries
+ // build-time IO: slurps the grammars' highlights.scm queries, and reads
+ // vendor/themes to find the theme sources
var threaded: std.Io.Threaded = .init(b.allocator, .{});
defer threaded.deinit();
const io = threaded.io();
@@ -115,6 +116,40 @@ pub fn build(b: *std.Build) void {
.link_libc = true,
});
+ // THEMES. tools/gen_themes.zig turns each vendored helix .toml / zed .json
+ // into one .zig file per theme plus a list.zig that imports them all;
+ // src/pardes.zig folds that list into the ring beside the three it ships.
+ //
+ // Straight into the build cache and handed over as a module, rather than
+ // written back into src/: the output then has no freshness problem to own
+ // (zig re-runs the generator only when a vendored file changes, and a
+ // cached run leaves the compile's inputs byte-identical, so `zig build`
+ // with nothing touched still does nothing), there is nothing to gitignore
+ // and no stale .zig can survive a deleted source.
+ //
+ // The INPUT list is read from the directory here rather than written out,
+ // so adding a theme is dropping a file in — and each file goes in as a
+ // content-hashed argument, which is what makes that new file re-run the
+ // step and nothing else.
+ const theme_gen = b.addExecutable(.{
+ .name = "pardes-gen-themes",
+ .root_module = b.createModule(.{
+ .target = b.graph.host,
+ .optimize = .ReleaseSafe,
+ .root_source_file = b.path("tools/gen_themes.zig"),
+ }),
+ });
+ const run_theme_gen = b.addRunArtifact(theme_gen);
+ const themes_out = run_theme_gen.addOutputDirectoryArg("themes");
+ for (vendoredThemes(b, io)) |name| run_theme_gen.addFileArg(b.path(b.fmt("vendor/themes/{s}", .{name})));
+ const themes_mod = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ .root_source_file = themes_out.path(b, "list.zig"),
+ });
+ root_mod.addImport("generated_themes", themes_mod);
+ hx_core_mod.addImport("generated_themes", themes_mod);
+
// static archives the emcc link needs beyond the zig object (grammars, SDL)
var wasm_libs: std.ArrayList(std.Build.LazyPath) = .empty;
@@ -713,6 +748,28 @@ fn compileGlsl(b: *std.Build, src: []const u8, stage: []const u8, out: []const u
return cmd.addOutputFileArg(out);
}
+/// The theme sources, sorted. SORTED because the run step is cached by its
+/// argv: readdir order is whatever the filesystem feels like, and an argv that
+/// shuffles is a cache miss and a rebuild every time. Anything that is not a
+/// .toml or a .json is skipped, which is what lets the upstream LICENSE files
+/// sit beside the themes they cover.
+fn vendoredThemes(b: *std.Build, io: std.Io) []const []const u8 {
+ var dir = b.build_root.handle.openDir(io, "vendor/themes", .{ .iterate = true }) catch @panic("open vendor/themes");
+ defer dir.close(io);
+ var names: std.ArrayList([]const u8) = .empty;
+ var it = dir.iterate();
+ while (it.next(io) catch @panic("read vendor/themes")) |e| {
+ if (!std.mem.endsWith(u8, e.name, ".toml") and !std.mem.endsWith(u8, e.name, ".json")) continue;
+ names.append(b.allocator, b.dupe(e.name)) catch @panic("OOM");
+ }
+ std.mem.sort([]const u8, names.items, {}, struct {
+ fn lt(_: void, a: []const u8, c: []const u8) bool {
+ return std.mem.order(u8, a, c) == .lt;
+ }
+ }.lt);
+ return names.items;
+}
+
fn detectEmscriptenSysroot(b: *std.Build) ?[]const u8 {
if (b.option([]const u8, "emscripten-sysroot", "path to the Emscripten sysroot")) |path| return path;
if (b.sysroot) |path| return path;
diff --git a/docs/design.typ b/docs/design.typ
index 7a9cc5c7..5d6daad2 100644
--- a/docs/design.typ
+++ b/docs/design.typ
@@ -243,13 +243,19 @@ visible-range first), Save, open-at-line, undo/redo. Image: zstbi decode,
kitty graphics when available, petscii matcher fallback (C64/terminal
palettes, ascii glyph set toggle). Tutor: embedded text as file pane.
-*Chrome.* Themes helix (default; near-black page, chrome one grey-ramp step
-off it, colors lifted from the helix editor's own theme), dark, acme-light —
-the first two leave a child's ANSI palette native, acme-light resolves it onto
-the page so shell output stays readable on yellow. A theme owns the page, the
-tag bar, the gutter and the move box; the selection colors stay fixed because
-they encode which button you pressed. Colors toggles all recolor passes; Debug
-stats overlay; Dump writes state ZON.
+*Chrome.* One theme per `.zig` file, folded into the ring at comptime: ours
+first — helix (default; near-black page, chrome one grey-ramp step off it,
+colors lifted from the helix editor's own theme), dark, acme-light — then
+everything `tools/gen_themes.zig` exports at build time out of the vendored
+helix `.toml` and zed `.json` sources in `vendor/themes`. helix and dark leave
+a child's ANSI palette native; acme-light and the zed exports resolve it onto
+the page so shell output stays readable on a light one. A theme owns the page,
+the tag bar, the gutter and the move box; the selection colors stay fixed
+because they encode which button you pressed. `NextColor` walks the ring,
+`Theme <name>` jumps to one and `ThemeSel` (`SPC t t`) lists them all into an
+output buffer whose rows are those very commands — so n/N EXECUTE each row
+instead of looking it and stepping the list wears the themes. Colors toggles
+all recolor passes; Debug stats overlay; Dump writes state ZON.
*Web (replay viewer parity).* Embedded dump; focus, border/move drags, wheel,
scrollbar, Colors/NextColor, pinch, render scale, and link-LOOK opening a new
diff --git a/docs/helix-keys.md b/docs/helix-keys.md
index 8583dee0..36002eed 100644
--- a/docs/helix-keys.md
+++ b/docs/helix-keys.md
@@ -92,7 +92,7 @@ with a reason in `test/hxcases/waivers.jsonl`.
| `Ctrl-w` + `h/j/k/l`/arrows | directional pane focus prefix — normal/tty modes only | pardes' own window handling (helix window mode skipped, section C). Runs the SAME `Left`/`Down`/`Up`/`Right` builtins `SPC w h/j/k/l` runs; kept alongside the leader because a pane in raw **tty** mode never sees `SPC` (the shell owns it), so this is the only keyboard way out of one. Insert mode owns `Ctrl-w` = delete-word-back, so a tag being TYPED into swallows it; from a tag in normal mode (`:`) it moves focus to the neighbour's BODY, while the bare letters `h/j/k/l` there move to its TAGLINE (next row) | pardes-specific |
| `Alt-n` | new terminal below (any mode) | shadows helix `Alt-n` TS sibling-select — skipped anyway (tree-sitter) | pardes-specific |
| `Alt-c` | move active terminal to a fresh column (any mode) | helix `Alt-c` is change-noyank; the pardes window op wins (do-not-touch contract). `Alt-d` + `i` covers the behavior | waived (`alt-c-window-op`) |
-| `Space` (normal, body) | the pardes LEADER: a key path from here runs a BUILTIN with no arguments — the same builtins the topbar and the tags hold. `SPC k` Kill, `SPC d` Del, `SPC f s` Save, `SPC f f` Find (fd over the pane's directory, results into `+Search`), `SPC f g` Grep (grep -R over the CONTENTS of every pane's directory, the ones another pane already covers dropped, rows relative to the asking pane's own directory, results into `+Search`; like Find it takes an ARGUMENT — `Grep foo` executed, or a selection chorded onto the word, searches that and skips the input), `SPC h t` Tutor, `SPC c n`/`SPC c d` Newcol/Delcol, `SPC t d/c/n/r` Debug/Colors/NextColor/Crt, `SPC t p/l/a` Petscii/Palette/Ascii (an image pane's renderer toggles — the words its tag used to spell out, so the bar stays a bar), `SPC s d`/`SPC s r` Dump/Restore, `SPC w h/j/k/l` Left/Down/Up/Right (directional pane focus — the `Ctrl-w` prefix's four moves as builtins), `SPC w t` Toggleterm (focus hops between the most recently focused document — file, image or `+Search`/`+Help` buffer — and the most recently focused terminal; the same key hops back). `?` at ANY depth opens the Help builtin listing what the prefix can still reach (`SPC ?` = all, `SPC h ?` = the docs group), into a `+Help` output buffer. The pending path shows at the right edge of the active pane's tag; Esc — or any unmapped key — abandons it | helix spends Space on pickers/LSP (section C); pardes has neither, and acme's builtins are what a leader is for. Enum-derived: the paths, the Help lines and the execute dispatch all fold out of one `Builtin` enum at comptime. Normal mode on a BODY only — tags are always insert, tty keys belong to the program | pardes-specific |
+| `Space` (normal, body) | the pardes LEADER: a key path from here runs a BUILTIN with no arguments — the same builtins the topbar and the tags hold. `SPC k` Kill, `SPC d` Del, `SPC f s` Save, `SPC f f` Find (fd over the pane's directory, results into `+Search`), `SPC f g` Grep (grep -R over the CONTENTS of every pane's directory, the ones another pane already covers dropped, rows relative to the asking pane's own directory, results into `+Search`; like Find it takes an ARGUMENT — `Grep foo` executed, or a selection chorded onto the word, searches that and skips the input), `SPC h t` Tutor, `SPC c n`/`SPC c d` Newcol/Delcol, `SPC t d/c/n/r` Debug/Colors/NextColor/Crt, `SPC t t` ThemeSel (the whole theme ring as `Theme <name>` rows in an output buffer — the one list whose rows are COMMANDS, so n/N execute each row instead of looking it and stepping is a live preview), `SPC t p/l/a` Petscii/Palette/Ascii (an image pane's renderer toggles — the words its tag used to spell out, so the bar stays a bar), `SPC s d`/`SPC s r` Dump/Restore, `SPC w h/j/k/l` Left/Down/Up/Right (directional pane focus — the `Ctrl-w` prefix's four moves as builtins), `SPC w t` Toggleterm (focus hops between the most recently focused document — file, image or `+Search`/`+Help` buffer — and the most recently focused terminal; the same key hops back). `?` at ANY depth opens the Help builtin listing what the prefix can still reach (`SPC ?` = all, `SPC h ?` = the docs group), into a `+Help` output buffer. The pending path shows at the right edge of the active pane's tag; Esc — or any unmapped key — abandons it | helix spends Space on pickers/LSP (section C); pardes has neither, and acme's builtins are what a leader is for. Enum-derived: the paths, the Help lines and the execute dispatch all fold out of one `Builtin` enum at comptime. Normal mode on a BODY only — tags are always insert, tty keys belong to the program | pardes-specific |
| `/` then `n`/`N` (any pane) | pardes' own plain-substring search into a `+Search` output buffer, n/N walk the result rows and look them (EXPLICIT selections) | KEEP, do not touch; not in the corpus (helix `/` is regex search) | pardes-specific |
| `n`/`N` (terminal panes, no search armed) | lookable-token motion over scrollback (EXPLICIT selections) | KEEP, do not touch | pardes-specific |
| insert: printable text | file: real edit; terminal: typed run splice | | helix-verified (file) |
diff --git a/next-steps.txt b/next-steps.txt
index a230beca..9cc22419 100644
--- a/next-steps.txt
+++ b/next-steps.txt
@@ -4,7 +4,7 @@
- Add multiple cursor support, and Ctrl-c auto comments. Use the same methodology of running helix in a harness as the oracle for differential testing. Also add regex interactive selection like on helix and the more specific selection semantics, use https://github.com/mnemnion/mvzr for regexes, add it using zig fetch.
- Add a new builtin that will list all builtins and their respective shortcuts if they have any (note that shortcuts may be spc+... or just keyboard or mouse chords/movements)
- Scrolling on a touchpad moves horizontally accidentally way too much, add some heuristics to prioritize vertical scrolling and make horizontal scrolling work only when the user is a bit more explicit about it - for example by measuring if there was recent high-enough vertical movement, etc.
-- Add a new builtin to list the themes and create an output window to select those themes; each theme now must live on its own .zig file, when moving with n/N on out ThemeSel output window it will select that theme so it's a interactive way to select the themes. We're being clever now with how these output windows will work, this feature depends on the new semantics for output windows but basically now the ThemeSel output panes will set the Command flag that will basically tell that when doing n/N movement on the output pane instead of Look-ing it will Execute the line, so the ThemeSel output pane will be like: (Theme acme, Theme a, Theme b) seperated by lines so the n/N will automatically consider selecting the Theme + argument (this is not output pane specific, it should be generalized to work on every pane kind), and will Exec it. Add new themes that will be automatically exported from helix and zed, a new binary will execute that will generate .zig files, this binary will be added as a execute step on the build.zig so when building it will regenarate those .zig that will be then included on the runtime as themes.
++ Add a new builtin to list the themes and create an output window to select those themes; each theme now must live on its own .zig file, when moving with n/N on out ThemeSel output window it will select that theme so it's a interactive way to select the themes. We're being clever now with how these output windows will work, this feature depends on the new semantics for output windows but basically now the ThemeSel output panes will set the Command flag that will basically tell that when doing n/N movement on the output pane instead of Look-ing it will Execute the line, so the ThemeSel output pane will be like: (Theme acme, Theme a, Theme b) seperated by lines so the n/N will automatically consider selecting the Theme + argument (this is not output pane specific, it should be generalized to work on every pane kind), and will Exec it. Add new themes that will be automatically exported from helix and zed, a new binary will execute that will generate .zig files, this binary will be added as a execute step on the build.zig so when building it will regenarate those .zig that will be then included on the runtime as themes.
- Use zig's 0.16 new threaded io and the std functions that the build system uses to listen for file changes, it needs to use something from the os so it's not a busy wait. It will only track files on open panes. when a file is updated, doing Undo will return to the state that it was before so if a file automatically updates the user won't lose the unsaved state as it was before and the editor won't need to handle with merging the unsaved changes and the new update.
- There's some bug when rendering from a ssh session - the screen gets dirty with old chars when scrolling.
- There's some bad performance when dealing with specially large files or long lines; write a perf harness for this and optimize the performance.
diff --git a/src/builtins.zig b/src/builtins.zig
index 7e20a95b..620c6b1c 100644
--- a/src/builtins.zig
+++ b/src/builtins.zig
@@ -182,6 +182,38 @@ pub const NextColor = struct {
}
};
+/// The theme BY NAME — `Theme acme`. The ring grew past the point where
+/// cycling to the one you want is reasonable, so this is the way to ask for
+/// one, and NextColor stays as the way to browse. Inert without an argument
+/// (there is no theme called nothing), which is also why it has no leader path:
+/// a key path names a builtin and can never carry the name of a theme.
+///
+/// A LINEAR SCAN over a couple of dozen names, on a keystroke: the alternative
+/// is a comptime name->index map, which is a second copy of the ring to build
+/// for a lookup nobody will ever measure.
+pub const Theme = struct {
+ pub fn run(c: Ctx) void {
+ const want = std.mem.trim(u8, c.arg orelse return, " \t\r\n");
+ for (pardes.themes, 0..) |t, i| {
+ if (std.mem.eql(u8, t.name, want)) {
+ c.p.theme_idx = i;
+ return;
+ }
+ }
+ }
+};
+
+/// ...and the list of what Theme takes, as a buffer you walk. Its rows are
+/// `Theme <name>` COMMANDS rather than locations, so n/N execute them instead
+/// of looking them (output_pane.Traits.executes) and stepping the list wears
+/// each theme in turn — the picker is the list, and there is nothing to
+/// confirm because arriving already applied it.
+pub const ThemeSel = struct {
+ pub fn run(c: Ctx) void {
+ output_pane.openThemes(c.p, c.id);
+ }
+};
+
pub const Crt = struct {
pub fn run(c: Ctx) void {
c.p.crt_on = !c.p.crt_on;
diff --git a/src/config.zig b/src/config.zig
index c4249270..35be4b9f 100644
--- a/src/config.zig
+++ b/src/config.zig
@@ -108,6 +108,11 @@ pub const leader_path = std.EnumArray(Builtin, ?[]const u8).init(.{
.Colors = "tc",
.NextColor = "tn",
.Crt = "tr",
+ // the theme picker joins the toggles it belongs with; `Theme` itself takes
+ // a NAME, and a key path can never carry one, so it has none (the same
+ // reason Look and Exec have none)
+ .ThemeSel = "tt",
+ .Theme = null,
// the image toggles join the same `t` group; Palette takes `l` because
// `p` is Petscii's and `c` is Colors'.
.Petscii = "tp",
@@ -401,6 +406,7 @@ pub const symbol_marker = " WsSymbols /";
pub const search_buffer = "+Search";
pub const help_buffer = "+Help";
pub const jumps_buffer = "+Jumps";
+pub const themes_buffer = "+Themes";
pub const hover_buffer = "+Hover";
pub const lsp_buffer = "+Lsp";
diff --git a/src/output_pane.zig b/src/output_pane.zig
index 8e08285f..a0583506 100644
--- a/src/output_pane.zig
+++ b/src/output_pane.zig
@@ -80,6 +80,19 @@ pub const Traits = struct {
/// look path resolves, so the buffer IS helix's picker. Prose (a hover
/// blurb, a rename diff) has nowhere to step to.
steps: bool = false,
+ /// ...and what a step DOES with the row it lands on. Off, the row is a
+ /// LOCATION and its leading word is LOOKED. On, the row is a COMMAND LINE
+ /// and the whole of it is EXECUTED — so walking the list runs each row in
+ /// turn, which is what makes a picker over things that take effect
+ /// immediately (ThemeSel) a plain list of the words you would have typed.
+ /// Both go through the ordinary builtin (config.look_cmd / exec_cmd), so a
+ /// row does exactly what the matching mouse button on it would.
+ ///
+ /// Nothing about this is output-pane specific, which is why it is a column
+ /// here and not a branch in searchStep: `file_row` answers it too, so a
+ /// file pane whose lines happen to be commands is one word away from
+ /// behaving the same.
+ executes: bool = false,
/// an answer of exactly ONE row jumps straight there instead of opening
/// this buffer at all — helix: the gotos jump on a single location and
/// show a picker on several, a symbol list is always a picker.
@@ -114,6 +127,10 @@ pub fn traits(o: Origin) Traits {
// the focus history, one location per row: not a search, but the
// same kind of list, so n/N walk it and a row is a look target
.Jumplist => .{ .name = config.jumps_buffer, .steps = true },
+ // the theme ring, one `Theme <name>` per row. The only buffer whose
+ // rows are COMMANDS rather than locations, so n/N execute them:
+ // walking the list wears each theme, and stopping is picking one.
+ .ThemeSel => .{ .name = config.themes_buffer, .steps = true, .executes = true },
// Find (file names) and Grep (file contents) both list locations;
// no other builtin opens a buffer, and the day one does it lands
// here rather than in a call site.
@@ -210,7 +227,6 @@ pub fn open(p: *Pardes, id: usize, dir: []const u8, from: Origin, arg: []const u
/// content line for anything holding text, else the pane's directory: enough
/// to recognise the place without opening it.
pub fn openJumps(p: *Pardes, id: usize) void {
- const pane = p.panes[id] orelse return;
const arena = p.scratch.allocator();
var out: std.ArrayList(u8) = .empty;
for (p.jumps[0..p.njumps]) |j| {
@@ -241,19 +257,47 @@ pub fn openJumps(p: *Pardes, id: usize) void {
std.fmt.allocPrint(arena, "{s}:{d}:{d} {s}\n", .{ loc, j.line, j.col, what[0..cut] }) catch return;
out.appendSlice(arena, row) catch return;
}
- const content = p.gpa.dupe(u8, out.items) catch return;
+ openStepped(p, id, .{ .cmd = .Jumplist }, out.items);
+}
+
+/// The ThemeSel builtin: the theme ring written out as one `Theme <name>` row
+/// per theme — the ordinary builtin with its argument, exactly the line you
+/// would type — into a buffer whose `executes` trait makes n/N RUN each row
+/// rather than look it. So walking the list is trying the themes on, and
+/// stopping on one is choosing it; there is no picker mode, no preview state
+/// and nothing to commit or cancel, because every step already did the thing.
+///
+/// The command's own name comes from the builtin rather than a literal: the
+/// word is derived from the struct in exactly one place (builtins.word), and a
+/// rename there must not leave rows here that name something that is gone.
+pub fn openThemes(p: *Pardes, id: usize) void {
+ const arena = p.scratch.allocator();
+ var out: std.ArrayList(u8) = .empty;
+ for (pardes.themes) |t| {
+ out.print(arena, comptime builtins.word(builtins.Theme) ++ " {s}\n", .{t.name}) catch return;
+ }
+ openStepped(p, id, .{ .cmd = .ThemeSel }, out.items);
+}
+
+/// Open a buffer n/N will walk, and arm them on it: the shared tail of every
+/// builtin that answers with a list. `text` is borrowed (the callers build it
+/// in the scratch arena) and copied into a gpa buffer the pane adopts.
+///
+/// Focus stays with the pane that ASKED, exactly as it does after a search:
+/// n/N are read there, and they step the buffer they just armed.
+fn openStepped(p: *Pardes, id: usize, from: Origin, text: []const u8) void {
+ const pane = p.panes[id] orelse return;
+ const content = p.gpa.dupe(u8, text) catch return;
const dir = if (pane.file) |f| (std.fs.path.dirname(f.path) orelse "/") else pane.cwdSlice();
const free = p.freeSlot() orelse {
p.gpa.free(content);
return;
};
- const np = open(p, free, dir, .{ .cmd = .Jumplist }, "", content) catch {
+ const np = open(p, free, dir, from, "", content) catch {
p.gpa.free(content);
return;
};
p.placeDoc(id, free, np);
- // focus stays with the pane that asked, exactly as it does after a search:
- // n/N are read there, and they step the buffer they just armed.
p.active = id;
pane.search_pane = free;
pane.search_row = null;
diff --git a/src/pardes.zig b/src/pardes.zig
index 3bf32c82..4d6556f3 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -216,90 +216,47 @@ pub const Theme = struct {
palette: ?[16][3]u8,
};
-pub const themes = [_]Theme{
- // Copied off the helix the user actually runs: config.toml says
- // theme = "pardes", i.e. ~/.config/helix/themes/pardes.toml. Every value
- // below was then PINNED against a real `hx sample.zig` render captured
- // through a pty, not read off the TOML and hoped for -- the SGR runs were
- // 48;2;8;8;8 page, 38;2;189;189;189 text, 38;2;98;98;98 line numbers,
- // 1;38;2;207;135;232 keywords, 38;2;227;199;138 strings AND numeric
- // constants, 38;2;255;255;255 comments.
- //
- // "dark and minimal" is the half that is not a table. That theme sets no
- // ui.statusline at all, so helix paints its status row in the page's own
- // colors: in the capture, rows 8..23 carry one unbroken 48;2;8;8;8 run and
- // the statusline is told apart from the file by its words alone. pardes
- // cannot go quite that far -- its tag bar is a click target and the gutter
- // is a drag handle -- so chrome lifts by the smallest honest step instead:
- // the theme's greys are all xterm grayscale-ramp entries (232 #080808,
- // 241 #626262, 246 #949494, 250 #bdbdbd), so the chrome takes ramp entries
- // too -- 233 #121212 for the bars, 238 #444444 for the thumb, 235 #262626
- // for an unfocused box. No hue anywhere outside the syntax colors.
- //
- // Shape: bg/fg are explicit rather than null, because "just really dark"
- // must not depend on whose terminal it lands in -- but `palette` stays null
- // so a shell's ANSI indices still resolve to the host's own 16 colors.
- // helix does the same: it dresses the page it draws and leaves the colors a
- // program emits alone. Only acme-light, which has to stay readable on
- // yellow, resolves the palette itself.
- .{
- .name = "helix",
- .bg = .{ 0x08, 0x08, 0x08 },
- .fg = .{ 0xbd, 0xbd, 0xbd },
- .tag_bg = .{ 0x12, 0x12, 0x12 },
- .tag_fg = .{ 0x94, 0x94, 0x94 },
- .box = .{ 0x62, 0x62, 0x62 },
- .box_dim = .{ 0x26, 0x26, 0x26 },
- .kw = .{ 0xcf, 0x87, 0xe8 },
- .str = .{ 0xe3, 0xc7, 0x8a },
- .num = .{ 0xe3, 0xc7, 0x8a },
- .comment = .{ 0xff, 0xff, 0xff },
- .lineno = .{ 0x62, 0x62, 0x62 },
- .scroll_track = .{ 0x12, 0x12, 0x12 },
- .scroll_thumb = .{ 0x44, 0x44, 0x44 },
- .palette = null,
- },
- .{
- .name = "dark",
- .bg = null,
- .fg = null,
- .tag_bg = .{ 0x2c, 0x2a, 0x3e },
- .tag_fg = .{ 0x96, 0x96, 0x96 },
- .box = .{ 0x6e, 0x4f, 0xd0 },
- .box_dim = .{ 0x38, 0x3c, 0x54 },
- .kw = .{ 0xcc, 0x70, 0xd8 },
- .str = .{ 0x8a, 0xb8, 0x7a },
- .num = .{ 0xd0, 0xc0, 0x70 },
- .comment = .{ 0xf0, 0xf0, 0xf0 },
- .lineno = .{ 0x5a, 0x5a, 0x5a },
- .scroll_track = .{ 0x2a, 0x2a, 0x2a },
- .scroll_thumb = .{ 0x52, 0x52, 0x52 },
- .palette = null,
- },
- .{
- .name = "acme",
- .bg = .{ 0xff, 0xff, 0xea },
- .fg = .{ 0x00, 0x00, 0x00 },
- .tag_bg = .{ 0xea, 0xff, 0xff },
- .tag_fg = .{ 0x00, 0x00, 0x00 },
- .box = .{ 0x6e, 0x4f, 0xd0 },
- .box_dim = .{ 0x38, 0x3c, 0x54 },
- .kw = .{ 0x88, 0x22, 0x99 },
- .str = .{ 0x00, 0x77, 0x33 },
- .num = .{ 0xa0, 0x52, 0x00 },
- .comment = .{ 0x55, 0x55, 0x55 },
- .lineno = .{ 0x99, 0x99, 0x80 },
- .scroll_track = .{ 0x99, 0x99, 0x4c },
- .scroll_thumb = .{ 0xff, 0xff, 0xea },
- .palette = .{
- .{ 0x00, 0x00, 0x00 }, .{ 0xaa, 0x22, 0x22 }, .{ 0x22, 0x80, 0x22 }, .{ 0x88, 0x66, 0x00 },
- .{ 0x22, 0x44, 0xbb }, .{ 0x99, 0x33, 0x99 }, .{ 0x11, 0x88, 0x88 }, .{ 0x55, 0x55, 0x55 },
- .{ 0x88, 0x88, 0x88 }, .{ 0xcc, 0x33, 0x33 }, .{ 0x33, 0xaa, 0x33 }, .{ 0xaa, 0x77, 0x00 },
- .{ 0x33, 0x55, 0xdd }, .{ 0xbb, 0x44, 0xbb }, .{ 0x22, 0xaa, 0xaa }, .{ 0x00, 0x00, 0x00 },
- },
- },
+/// The three themes pardes ships with, in RING ORDER, which is the one thing
+/// the files themselves cannot say: `helix` is index 0 and so what boots, and
+/// NextColor walks from here out into the generated ones. Written here rather
+/// than in a fourth file because the order IS the information — and it is
+/// load-bearing, since test/snapshots/theme.snap captures the first three
+/// steps of the ring by their colors.
+const curated = struct {
+ pub const helix = @import("themes/helix.zig");
+ pub const dark = @import("themes/dark.zig");
+ pub const acme = @import("themes/acme.zig");
};
+/// A zig file IS a struct, so the FILES are the list: this walks a container's
+/// declarations — each one an imported theme file — and copies its `theme`
+/// value into a real Theme. FIELD BY FIELD rather than by plain coercion,
+/// because a theme file deliberately imports nothing (it is data, not code) and
+/// zig will not coerce a whole anonymous struct into a named one; assigning one
+/// field at a time puts each value in a result location that knows the type,
+/// which is also what makes a missing field a compile error naming it.
+///
+/// A FUNCTION and not a const for the same reason builtins.all is one: it is
+/// only ever a signature to whoever walks it, never a value in its own way.
+fn fold(comptime C: type) []const Theme {
+ comptime {
+ @setEvalBranchQuota(20000); // themes x fields, and each field a @field lookup
+ var out: []const Theme = &.{};
+ for (@typeInfo(C).@"struct".decls) |d| {
+ var t: Theme = undefined;
+ for (@typeInfo(Theme).@"struct".fields) |f| @field(t, f.name) = @field(@field(C, d.name).theme, f.name);
+ out = out ++ &[_]Theme{t};
+ }
+ return out;
+ }
+}
+
+/// The ring: ours, then every theme tools/gen_themes.zig exported out of the
+/// helix and zed sources in vendor/themes (build.zig runs it and hands the
+/// result over as a module). Adding one is dropping a file in there — there is
+/// no list here to append to, which is the whole point of folding the files.
+pub const themes = fold(curated) ++ fold(@import("generated_themes"));
+
// ---- the boundary types ----
pub const Color = union(enum) { default, index: u8, rgb: [3]u8 };
@@ -2827,10 +2784,12 @@ pub const Pardes = struct {
}
/// n/N: step to the next/previous row of this pane's results buffer and
- /// look it — every row there IS a result, so the leading file-ish word
- /// (`path:LINE:COL` or `@pN:LINE:COL`) does the rest through the ordinary
- /// look path. False = no live search: a terminal's n/N falls back to
- /// lookStep, anything else stays put.
+ /// ACT on it — which of the two acme verbs that is comes from the buffer's
+ /// own traits, so this is one motion over "a list of things you can run",
+ /// not two kinds of stepping. A location list (every search, the jumplist,
+ /// every language answer) Looks the leading `path:LINE:COL` word; a command
+ /// list (ThemeSel) Execs the whole row. False = no live search: a
+ /// terminal's n/N falls back to lookStep, anything else stays put.
fn searchStep(p: *Pardes, id: usize, delta: i32) bool {
const pane = p.panes[id] orelse return false;
const rid = pane.search_pane orelse return false;
@@ -2838,7 +2797,8 @@ pub const Pardes = struct {
const rf = if (rp.file) |*f| f else return false;
// one question covers both hazards: a freed slot can be reused by an
// unrelated pane, and a buffer of PROSE has nowhere to step to
- if (!output_pane.fileTraits(rf.output).steps) return false;
+ const tr = output_pane.fileTraits(rf.output);
+ if (!tr.steps) return false;
// fresh results: n starts at the first row, N has nothing behind it
const nrows: i64 = @intCast(std.mem.count(u8, rf.content, "\n"));
const step: i64 = if (pane.search_row) |c| @as(i64, @intCast(c)) + delta else if (delta > 0) 0 else -1;
@@ -2853,9 +2813,18 @@ pub const Pardes = struct {
rp.cur_pinned = true;
rp.ensureCursorVisible();
const ln = modal.lineSlice(rf.content, @intCast(step));
- var hi: usize = 0;
- while (hi < ln.len and config.isFileChar(ln[hi])) hi += 1;
- p.lookAt(rid, ln[0..hi]);
+ // Both arms are the BUILTIN, run on the results pane — the same call a
+ // middle or right click on that row would make, so a stepped row and a
+ // clicked row can never drift apart. A command row goes whole (its
+ // argument is the tail after the name); a location row is cut to the
+ // leading file-ish word, since the rest of it is the matched text.
+ if (tr.executes) {
+ p.runBuiltin(config.exec_cmd, rid, "", std.mem.trim(u8, ln, " \t\r"));
+ } else {
+ var hi: usize = 0;
+ while (hi < ln.len and config.isFileChar(ln[hi])) hi += 1;
+ p.runBuiltin(config.look_cmd, rid, "", ln[0..hi]);
+ }
// the look may focus what it opened — a Find row opens a whole new
// file pane every time — so focus comes back to the pane that owns the
// search and the next n keeps stepping. A `/` row looks at the
@@ -5125,15 +5094,15 @@ pub const Pardes = struct {
defer p.exec_depth -= 1;
// The builtins that take an ARGUMENT match their name with a TAIL:
// `Restore <path>`, `Find <pat>`, `Grep <pat>`, `Rename <name>`,
- // `WsSymbols <sym>`, and the two verbs themselves — `Look <word>`,
- // `Exec <cmd>`, which is what makes `` @`Look .` `` nest (the tail goes
- // straight back through here). Every other name must match WHOLE, so
- // `Kill foo` is a shell command and not Kill.
+ // `WsSymbols <sym>`, `Theme <name>`, and the two verbs themselves —
+ // `Look <word>`, `Exec <cmd>`, which is what makes `` @`Look .` `` nest
+ // (the tail goes straight back through here). Every other name must
+ // match WHOLE, so `Kill foo` is a shell command and not Kill.
const sp = std.mem.indexOfAny(u8, cmd, " \t");
const bi: ?Builtin = std.meta.stringToEnum(Builtin, cmd) orelse blk: {
const head = std.meta.stringToEnum(Builtin, cmd[0 .. sp orelse break :blk null]) orelse break :blk null;
break :blk switch (head) {
- .Restore, .Find, .Grep, .Rename, .WsSymbols, .Look, .Exec => head,
+ .Restore, .Find, .Grep, .Rename, .WsSymbols, .Theme, .Look, .Exec => head,
else => null,
};
};
diff --git a/src/themes/acme.zig b/src/themes/acme.zig
new file mode 100644
index 00000000..925cacfb
--- /dev/null
+++ b/src/themes/acme.zig
@@ -0,0 +1,29 @@
+//! acme's yellow page — the light theme, and the one that has to resolve
+//! `palette` itself. A theme that only dresses the page can leave a child's
+//! ANSI indices alone and let the host answer them; on #ffffea that is how you
+//! get pale yellow `ls` output on pale yellow paper. So the sixteen below are
+//! darkened for paper: index 7 (the "white" a shell writes most of its output
+//! in) is a mid grey, and 15 (bright white) is BLACK, because a program asking
+//! for the brightest color on a light page means "make this stand out".
+pub const theme = .{
+ .name = "acme",
+ .bg = .{ 0xff, 0xff, 0xea },
+ .fg = .{ 0x00, 0x00, 0x00 },
+ .tag_bg = .{ 0xea, 0xff, 0xff },
+ .tag_fg = .{ 0x00, 0x00, 0x00 },
+ .box = .{ 0x6e, 0x4f, 0xd0 },
+ .box_dim = .{ 0x38, 0x3c, 0x54 },
+ .kw = .{ 0x88, 0x22, 0x99 },
+ .str = .{ 0x00, 0x77, 0x33 },
+ .num = .{ 0xa0, 0x52, 0x00 },
+ .comment = .{ 0x55, 0x55, 0x55 },
+ .lineno = .{ 0x99, 0x99, 0x80 },
+ .scroll_track = .{ 0x99, 0x99, 0x4c },
+ .scroll_thumb = .{ 0xff, 0xff, 0xea },
+ .palette = .{
+ .{ 0x00, 0x00, 0x00 }, .{ 0xaa, 0x22, 0x22 }, .{ 0x22, 0x80, 0x22 }, .{ 0x88, 0x66, 0x00 },
+ .{ 0x22, 0x44, 0xbb }, .{ 0x99, 0x33, 0x99 }, .{ 0x11, 0x88, 0x88 }, .{ 0x55, 0x55, 0x55 },
+ .{ 0x88, 0x88, 0x88 }, .{ 0xcc, 0x33, 0x33 }, .{ 0x33, 0xaa, 0x33 }, .{ 0xaa, 0x77, 0x00 },
+ .{ 0x33, 0x55, 0xdd }, .{ 0xbb, 0x44, 0xbb }, .{ 0x22, 0xaa, 0xaa }, .{ 0x00, 0x00, 0x00 },
+ },
+};
diff --git a/src/themes/dark.zig b/src/themes/dark.zig
new file mode 100644
index 00000000..df418982
--- /dev/null
+++ b/src/themes/dark.zig
@@ -0,0 +1,22 @@
+//! pardes's own dark theme, and the boot default until the helix capture
+//! displaced it. It stays in the ring because it is the only one that leaves
+//! `bg`/`fg` NULL: the page keeps the host terminal's own default cell, so
+//! pardes sits inside whatever the surrounding terminal already looks like and
+//! only paints its chrome. Every other theme here dresses the whole page.
+pub const theme = .{
+ .name = "dark",
+ .bg = null,
+ .fg = null,
+ .tag_bg = .{ 0x2c, 0x2a, 0x3e },
+ .tag_fg = .{ 0x96, 0x96, 0x96 },
+ .box = .{ 0x6e, 0x4f, 0xd0 },
+ .box_dim = .{ 0x38, 0x3c, 0x54 },
+ .kw = .{ 0xcc, 0x70, 0xd8 },
+ .str = .{ 0x8a, 0xb8, 0x7a },
+ .num = .{ 0xd0, 0xc0, 0x70 },
+ .comment = .{ 0xf0, 0xf0, 0xf0 },
+ .lineno = .{ 0x5a, 0x5a, 0x5a },
+ .scroll_track = .{ 0x2a, 0x2a, 0x2a },
+ .scroll_thumb = .{ 0x52, 0x52, 0x52 },
+ .palette = null,
+};
diff --git a/src/themes/helix.zig b/src/themes/helix.zig
new file mode 100644
index 00000000..26c156ff
--- /dev/null
+++ b/src/themes/helix.zig
@@ -0,0 +1,47 @@
+//! Copied off the helix the user actually runs: config.toml says
+//! theme = "pardes", i.e. ~/.config/helix/themes/pardes.toml. Every value below
+//! was then PINNED against a real `hx sample.zig` render captured through a
+//! pty, not read off the TOML and hoped for — the SGR runs were 48;2;8;8;8
+//! page, 38;2;189;189;189 text, 38;2;98;98;98 line numbers, 1;38;2;207;135;232
+//! keywords, 38;2;227;199;138 strings AND numeric constants, 38;2;255;255;255
+//! comments. `zig build snap` holds them there (test/snapshots/theme.snap), so
+//! these fifteen numbers are a golden and not taste.
+//!
+//! "dark and minimal" is the half that is not a table. That theme sets no
+//! ui.statusline at all, so helix paints its status row in the page's own
+//! colors: in the capture, rows 8..23 carry one unbroken 48;2;8;8;8 run and the
+//! statusline is told apart from the file by its words alone. pardes cannot go
+//! quite that far — its tag bar is a click target and the gutter is a drag
+//! handle — so chrome lifts by the smallest honest step instead: the theme's
+//! greys are all xterm grayscale-ramp entries (232 #080808, 241 #626262,
+//! 246 #949494, 250 #bdbdbd), so the chrome takes ramp entries too — 233
+//! #121212 for the bars, 238 #444444 for the thumb, 235 #262626 for an
+//! unfocused box. No hue anywhere outside the syntax colors.
+//!
+//! Shape: bg/fg are explicit rather than null, because "just really dark" must
+//! not depend on whose terminal it lands in — but `palette` stays null so a
+//! shell's ANSI indices still resolve to the host's own 16 colors. helix does
+//! the same: it dresses the page it draws and leaves the colors a program emits
+//! alone. Only acme, which has to stay readable on yellow, resolves the palette
+//! itself.
+//!
+//! No imports, and neither has any other theme file: a theme is DATA. The
+//! `.{...}` becomes a pardes.Theme in pardes.zig's `fold`, which is also what
+//! turns a missing field here into a compile error naming it.
+pub const theme = .{
+ .name = "helix",
+ .bg = .{ 0x08, 0x08, 0x08 },
+ .fg = .{ 0xbd, 0xbd, 0xbd },
+ .tag_bg = .{ 0x12, 0x12, 0x12 },
+ .tag_fg = .{ 0x94, 0x94, 0x94 },
+ .box = .{ 0x62, 0x62, 0x62 },
+ .box_dim = .{ 0x26, 0x26, 0x26 },
+ .kw = .{ 0xcf, 0x87, 0xe8 },
+ .str = .{ 0xe3, 0xc7, 0x8a },
+ .num = .{ 0xe3, 0xc7, 0x8a },
+ .comment = .{ 0xff, 0xff, 0xff },
+ .lineno = .{ 0x62, 0x62, 0x62 },
+ .scroll_track = .{ 0x12, 0x12, 0x12 },
+ .scroll_thumb = .{ 0x44, 0x44, 0x44 },
+ .palette = null,
+};
diff --git a/test/snapshots/leader.golden b/test/snapshots/leader.golden
index 831d4557..0ed14a68 100644
--- a/test/snapshots/leader.golden
+++ b/test/snapshots/leader.golden
@@ -399,8 +399,8 @@
| 7 SPC t n NextColor
| 8 SPC t p Petscii
| 9 SPC t r Crt
-| 10
-|
+| 10 SPC t t ThemeSel
+| 11
|
|
|
diff --git a/test/snapshots/theme.golden b/test/snapshots/theme.golden
index 520258ff..c4a5a6d9 100644
--- a/test/snapshots/theme.golden
+++ b/test/snapshots/theme.golden
@@ -246,7 +246,7 @@
|27: 0-99 d,#ffffea,
|28: 0-99 d,#ffffea,
|29: 0-99 d,#ffffea,
-== snap helix-again grid=100x30 cursor=4,4
+== snap generated grid=100x30 cursor=4,4
|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
| NOR /tmp/pardes-snap/theme/cwd Del NOR /tmp/pardes-snap/theme/cwd Del
|
@@ -277,34 +277,34 @@
|
|
|
-== style helix-again
-|0: 0-52 #949494,#121212, 53-99 d,#121212,
-|1: 0-1 d,#626262, 2-35 #949494,#121212, 36-49 d,#121212, 50-51 d,#262626, 52-85 #949494,#121212, 86-99 d,#121212,
-|2: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|3: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|4: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|5: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|6: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|7: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|8: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|9: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|10: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|11: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|12: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|13: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|14: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|15: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|16: 0-1 d,#262626, 2-35 #949494,#121212, 36-49 d,#121212, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|17: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|18: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|19: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|20: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|21: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|22: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|23: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|24: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|25: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|26: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|27: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|28: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
-|29: 0-1 d,#444444, 2-49 #bdbdbd,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+== style generated
+|0: 0-52 #8a8986,#313337, 53-99 d,#313337,
+|1: 0-1 d,#5ac1fe, 2-35 #8a8986,#313337, 36-49 d,#313337, 50-51 d,#3f4043, 52-85 #8a8986,#313337, 86-99 d,#313337,
+|2: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|3: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|4: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|5: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|6: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|7: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|8: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|9: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|10: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|11: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|12: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|13: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|14: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|15: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|16: 0-1 d,#3f4043, 2-35 #8a8986,#313337, 36-49 d,#313337, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|17: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|18: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|19: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|20: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|21: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|22: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|23: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|24: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|25: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|26: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|27: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|28: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|29: 0-1 d,#bfbdb6, 2-49 #bfbdb6,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
diff --git a/test/snapshots/theme.snap b/test/snapshots/theme.snap
index ca0b22ba..399b1950 100644
--- a/test/snapshots/theme.snap
+++ b/test/snapshots/theme.snap
@@ -1,9 +1,18 @@
# The theme ring, proved by style and not by text. Boot is "helix" (index 0,
# the default): near-black page, tag bar one grey-ramp step off it, grey move
# box. NextColor (still in the topbar) then walks to "dark" — the OLD default,
-# which has to stay reachable — then to acme-light, then back around to helix.
+# which has to stay reachable — then to acme-light.
# Colors, which left the bar for its leader path, toggles the recolor passes
# off and on from SPC t c — the same builtin either way.
+#
+# The fourth click used to come back around to helix, because the ring was
+# three long. It is not any more: everything vendor/themes exports follows the
+# three pardes ships with (pardes.zig: `curated ++ generated`), so the click
+# lands on the FIRST GENERATED theme instead. That capture is named for what it
+# is rather than for which theme it happens to be, because the generated half
+# is sorted by name and a theme dropped into vendor/themes can take the slot.
+# The wrap itself is `% themes.len` and clicking twenty times to see it is not
+# worth a golden; themesel.snap covers reaching a theme by name.
file t.txt x
start 30 100 -n 3
wait 8000 t.txt
@@ -33,5 +42,5 @@ stable 700 10000
press middle 25 1
release middle 25 1
stable 700 10000
-snap helix-again
-snapstyle helix-again
+snap generated
+snapstyle generated
diff --git a/test/snapshots/themesel.golden b/test/snapshots/themesel.golden
new file mode 100644
index 00000000..f35e5566
--- /dev/null
+++ b/test/snapshots/themesel.golden
@@ -0,0 +1,434 @@
+== snap boot grid=100x30 cursor=7,2
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/themesel/cwd/t.txt Save Del NOR /tmp/pardes-snap/themesel/cwd Del
+| 1 line 1
+| 2 line 2 t.txt
+| 3 line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6
+| 7 line 7
+| 8 line 8
+| 9 line 9
+| 10 line 10
+| 11 line 11
+| 12 line 12
+| 13 line 13
+| 14 line 14
+| 15 line 15
+| 16 line 16
+| 17 line 17
+| 18 line 18
+| 19 line 19
+| 20 line 20
+| 21 line 21
+| 22 line 22
+| 23 line 23
+| 24 line 24
+| 25 line 25
+| 26 line 26
+| 27 line 27
+| 28 line 28
+== snap list grid=100x30 cursor=7,2
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/themesel/cwd/t.txt Save Del NOR /tmp/pardes-snap/themesel/cwd Del
+| 1 line 1
+| 2 line 2 t.txt
+| 3 line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6
+| 7 line 7
+| 8 line 8
+| 9 line 9
+| 10 line 10
+| 11 line 11
+| 12 line 12
+| 13 line 13
+| 14 line 14
+| NOR /tmp/pardes-snap/themesel/cwd/+Themes Del
+| 1 Theme helix
+| 2 Theme dark
+| 3 Theme acme
+| 4 Theme ayu_dark
+| 5 Theme ayu_light
+| 6 Theme ayu_mirage
+| 7 Theme catppuccin_mocha
+| 8 Theme dracula
+| 9 Theme everforest_light
+| 10 Theme gruvbox
+| 11 Theme monokai
+| 12 Theme nord
+| 13 Theme one_dark
+== snap step-helix grid=100x30 cursor=7,2
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/themesel/cwd/t.txt Save Del NOR /tmp/pardes-snap/themesel/cwd Del
+| 1 line 1
+| 2 line 2 t.txt
+| 3 line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6
+| 7 line 7
+| 8 line 8
+| 9 line 9
+| 10 line 10
+| 11 line 11
+| 12 line 12
+| 13 line 13
+| 14 line 14
+| NOR /tmp/pardes-snap/themesel/cwd/+Themes Del
+| 1 Theme helix
+| 2 Theme dark
+| 3 Theme acme
+| 4 Theme ayu_dark
+| 5 Theme ayu_light
+| 6 Theme ayu_mirage
+| 7 Theme catppuccin_mocha
+| 8 Theme dracula
+| 9 Theme everforest_light
+| 10 Theme gruvbox
+| 11 Theme monokai
+| 12 Theme nord
+| 13 Theme one_dark
+== style step-helix
+|0: 0-52 #949494,#121212, 53-99 d,#121212,
+|1: 0-1 d,#626262, 2-49 #949494,#121212, 50-51 d,#262626, 52-88 #949494,#121212, 89-99 d,#121212,
+|2: 0-1 d,#444444, 2-6 #949494,#121212, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|3: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|4: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|5: 0-1 d,#444444, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|6: 0-1 d,#121212, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|7: 0-1 d,#121212, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|8: 0-1 d,#121212, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|9: 0-1 d,#121212, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|10: 0-1 d,#121212, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|11: 0-1 d,#121212, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|12: 0-1 d,#121212, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|13: 0-1 d,#121212, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|14: 0-1 d,#121212, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|15: 0-1 d,#121212, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|16: 0-1 d,#262626, 2-46 #949494,#121212, 47-49 d,#121212, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|17: 0-1 d,#444444, 2-6 #626262,#080808, 7-49 #d8c8a8,#4a3a1e, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|18: 0-1 d,#444444, 2-6 #626262,#080808, 7-16 #bdbdbd,#080808, 17-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|19: 0-1 d,#444444, 2-6 #626262,#080808, 7-16 #bdbdbd,#080808, 17-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|20: 0-1 d,#444444, 2-6 #626262,#080808, 7-20 #bdbdbd,#080808, 21-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|21: 0-1 d,#444444, 2-6 #626262,#080808, 7-21 #bdbdbd,#080808, 22-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|22: 0-1 d,#444444, 2-6 #626262,#080808, 7-22 #bdbdbd,#080808, 23-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|23: 0-1 d,#444444, 2-6 #626262,#080808, 7-28 #bdbdbd,#080808, 29-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|24: 0-1 d,#444444, 2-6 #626262,#080808, 7-19 #bdbdbd,#080808, 20-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|25: 0-1 d,#444444, 2-6 #626262,#080808, 7-28 #bdbdbd,#080808, 29-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|26: 0-1 d,#444444, 2-6 #626262,#080808, 7-19 #bdbdbd,#080808, 20-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|27: 0-1 d,#121212, 2-6 #626262,#080808, 7-19 #bdbdbd,#080808, 20-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|28: 0-1 d,#121212, 2-6 #626262,#080808, 7-16 #bdbdbd,#080808, 17-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+|29: 0-1 d,#121212, 2-6 #626262,#080808, 7-20 #bdbdbd,#080808, 21-49 d,#080808, 50-51 d,#444444, 52-99 #bdbdbd,#080808,
+== snap step-dark grid=100x30 cursor=7,2
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/themesel/cwd/t.txt Save Del NOR /tmp/pardes-snap/themesel/cwd Del
+| 1 line 1
+| 2 line 2 t.txt
+| 3 line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6
+| 7 line 7
+| 8 line 8
+| 9 line 9
+| 10 line 10
+| 11 line 11
+| 12 line 12
+| 13 line 13
+| 14 line 14
+| NOR /tmp/pardes-snap/themesel/cwd/+Themes Del
+| 1 Theme helix
+| 2 Theme dark
+| 3 Theme acme
+| 4 Theme ayu_dark
+| 5 Theme ayu_light
+| 6 Theme ayu_mirage
+| 7 Theme catppuccin_mocha
+| 8 Theme dracula
+| 9 Theme everforest_light
+| 10 Theme gruvbox
+| 11 Theme monokai
+| 12 Theme nord
+| 13 Theme one_dark
+== style step-dark
+|0: 0-52 #969696,#2c2a3e, 53-99 d,#2c2a3e,
+|1: 0-1 d,#6e4fd0, 2-49 #969696,#2c2a3e, 50-51 d,#383c54, 52-88 #969696,#2c2a3e, 89-99 d,#2c2a3e,
+|2: 0-1 d,#525252, 2-6 #969696,#2c2a3e, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|3: 0-1 d,#525252, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|4: 0-1 d,#525252, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|5: 0-1 d,#525252, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|6: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|7: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|8: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|9: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|10: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|11: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|12: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|13: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|14: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|15: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|16: 0-1 d,#383c54, 2-46 #969696,#2c2a3e, 47-49 d,#2c2a3e, 50-51 d,#525252, 52-99 d,d,
+|17: 0-1 d,#525252, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|18: 0-1 d,#525252, 2-6 #5a5a5a,d, 7-49 #d8c8a8,#4a3a1e, 50-51 d,#525252, 52-99 d,d,
+|19: 0-1 d,#525252, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|20: 0-1 d,#525252, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|21: 0-1 d,#525252, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|22: 0-1 d,#525252, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|23: 0-1 d,#525252, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|24: 0-1 d,#525252, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|25: 0-1 d,#525252, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|26: 0-1 d,#525252, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|27: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|28: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+|29: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-49 d,d, 50-51 d,#525252, 52-99 d,d,
+== snap step-acme grid=100x30 cursor=7,2
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/themesel/cwd/t.txt Save Del NOR /tmp/pardes-snap/themesel/cwd Del
+| 1 line 1
+| 2 line 2 t.txt
+| 3 line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6
+| 7 line 7
+| 8 line 8
+| 9 line 9
+| 10 line 10
+| 11 line 11
+| 12 line 12
+| 13 line 13
+| 14 line 14
+| NOR /tmp/pardes-snap/themesel/cwd/+Themes Del
+| 1 Theme helix
+| 2 Theme dark
+| 3 Theme acme
+| 4 Theme ayu_dark
+| 5 Theme ayu_light
+| 6 Theme ayu_mirage
+| 7 Theme catppuccin_mocha
+| 8 Theme dracula
+| 9 Theme everforest_light
+| 10 Theme gruvbox
+| 11 Theme monokai
+| 12 Theme nord
+| 13 Theme one_dark
+== style step-acme
+|0: 0-52 #000000,#eaffff, 53-99 d,#eaffff,
+|1: 0-1 d,#6e4fd0, 2-49 #000000,#eaffff, 50-51 d,#383c54, 52-88 #000000,#eaffff, 89-99 d,#eaffff,
+|2: 0-1 d,#ffffea, 2-6 #000000,#eaffff, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|3: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|4: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|5: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|6: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|7: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|8: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|9: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|10: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|11: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|12: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|13: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|14: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|15: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|16: 0-1 d,#383c54, 2-46 #000000,#eaffff, 47-49 d,#eaffff, 50-51 d,#ffffea, 52-99 #000000,#ffffea,
+|17: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-17 #000000,#ffffea, 18-51 d,#ffffea, 52-99 #000000,#ffffea,
+|18: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-16 #000000,#ffffea, 17-51 d,#ffffea, 52-99 #000000,#ffffea,
+|19: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-49 #d8c8a8,#4a3a1e, 50-51 d,#ffffea, 52-99 #000000,#ffffea,
+|20: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-20 #000000,#ffffea, 21-51 d,#ffffea, 52-99 #000000,#ffffea,
+|21: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-21 #000000,#ffffea, 22-51 d,#ffffea, 52-99 #000000,#ffffea,
+|22: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-22 #000000,#ffffea, 23-51 d,#ffffea, 52-99 #000000,#ffffea,
+|23: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-28 #000000,#ffffea, 29-51 d,#ffffea, 52-99 #000000,#ffffea,
+|24: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-19 #000000,#ffffea, 20-51 d,#ffffea, 52-99 #000000,#ffffea,
+|25: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-28 #000000,#ffffea, 29-51 d,#ffffea, 52-99 #000000,#ffffea,
+|26: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-19 #000000,#ffffea, 20-51 d,#ffffea, 52-99 #000000,#ffffea,
+|27: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-19 #000000,#ffffea, 20-51 d,#ffffea, 52-99 #000000,#ffffea,
+|28: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-16 #000000,#ffffea, 17-51 d,#ffffea, 52-99 #000000,#ffffea,
+|29: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-20 #000000,#ffffea, 21-51 d,#ffffea, 52-99 #000000,#ffffea,
+== snap step-generated grid=100x30 cursor=7,2
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/themesel/cwd/t.txt Save Del NOR /tmp/pardes-snap/themesel/cwd Del
+| 1 line 1
+| 2 line 2 t.txt
+| 3 line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6
+| 7 line 7
+| 8 line 8
+| 9 line 9
+| 10 line 10
+| 11 line 11
+| 12 line 12
+| 13 line 13
+| 14 line 14
+| NOR /tmp/pardes-snap/themesel/cwd/+Themes Del
+| 1 Theme helix
+| 2 Theme dark
+| 3 Theme acme
+| 4 Theme ayu_dark
+| 5 Theme ayu_light
+| 6 Theme ayu_mirage
+| 7 Theme catppuccin_mocha
+| 8 Theme dracula
+| 9 Theme everforest_light
+| 10 Theme gruvbox
+| 11 Theme monokai
+| 12 Theme nord
+| 13 Theme one_dark
+== style step-generated
+|0: 0-52 #8a8986,#313337, 53-99 d,#313337,
+|1: 0-1 d,#5ac1fe, 2-49 #8a8986,#313337, 50-51 d,#3f4043, 52-88 #8a8986,#313337, 89-99 d,#313337,
+|2: 0-1 d,#bfbdb6, 2-6 #8a8986,#313337, 7-12 #bfbdb6,#0d1016, 13-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|3: 0-1 d,#bfbdb6, 2-6 #4b4c4e,#0d1016, 7-12 #bfbdb6,#0d1016, 13-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|4: 0-1 d,#bfbdb6, 2-6 #4b4c4e,#0d1016, 7-12 #bfbdb6,#0d1016, 13-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|5: 0-1 d,#bfbdb6, 2-6 #4b4c4e,#0d1016, 7-12 #bfbdb6,#0d1016, 13-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|6: 0-1 d,#2d2f34, 2-6 #4b4c4e,#0d1016, 7-12 #bfbdb6,#0d1016, 13-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|7: 0-1 d,#2d2f34, 2-6 #4b4c4e,#0d1016, 7-12 #bfbdb6,#0d1016, 13-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|8: 0-1 d,#2d2f34, 2-6 #4b4c4e,#0d1016, 7-12 #bfbdb6,#0d1016, 13-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|9: 0-1 d,#2d2f34, 2-6 #4b4c4e,#0d1016, 7-12 #bfbdb6,#0d1016, 13-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|10: 0-1 d,#2d2f34, 2-6 #4b4c4e,#0d1016, 7-12 #bfbdb6,#0d1016, 13-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|11: 0-1 d,#2d2f34, 2-6 #4b4c4e,#0d1016, 7-13 #bfbdb6,#0d1016, 14-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|12: 0-1 d,#2d2f34, 2-6 #4b4c4e,#0d1016, 7-13 #bfbdb6,#0d1016, 14-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|13: 0-1 d,#2d2f34, 2-6 #4b4c4e,#0d1016, 7-13 #bfbdb6,#0d1016, 14-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|14: 0-1 d,#2d2f34, 2-6 #4b4c4e,#0d1016, 7-13 #bfbdb6,#0d1016, 14-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|15: 0-1 d,#2d2f34, 2-6 #4b4c4e,#0d1016, 7-13 #bfbdb6,#0d1016, 14-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|16: 0-1 d,#3f4043, 2-46 #8a8986,#313337, 47-49 d,#313337, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|17: 0-1 d,#bfbdb6, 2-6 #4b4c4e,#0d1016, 7-17 #bfbdb6,#0d1016, 18-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|18: 0-1 d,#bfbdb6, 2-6 #4b4c4e,#0d1016, 7-16 #bfbdb6,#0d1016, 17-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|19: 0-1 d,#bfbdb6, 2-6 #4b4c4e,#0d1016, 7-16 #bfbdb6,#0d1016, 17-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|20: 0-1 d,#bfbdb6, 2-6 #4b4c4e,#0d1016, 7-49 #d8c8a8,#4a3a1e, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|21: 0-1 d,#bfbdb6, 2-6 #4b4c4e,#0d1016, 7-21 #bfbdb6,#0d1016, 22-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|22: 0-1 d,#bfbdb6, 2-6 #4b4c4e,#0d1016, 7-22 #bfbdb6,#0d1016, 23-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|23: 0-1 d,#bfbdb6, 2-6 #4b4c4e,#0d1016, 7-28 #bfbdb6,#0d1016, 29-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|24: 0-1 d,#bfbdb6, 2-6 #4b4c4e,#0d1016, 7-19 #bfbdb6,#0d1016, 20-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|25: 0-1 d,#bfbdb6, 2-6 #4b4c4e,#0d1016, 7-28 #bfbdb6,#0d1016, 29-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|26: 0-1 d,#bfbdb6, 2-6 #4b4c4e,#0d1016, 7-19 #bfbdb6,#0d1016, 20-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|27: 0-1 d,#2d2f34, 2-6 #4b4c4e,#0d1016, 7-19 #bfbdb6,#0d1016, 20-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|28: 0-1 d,#2d2f34, 2-6 #4b4c4e,#0d1016, 7-16 #bfbdb6,#0d1016, 17-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+|29: 0-1 d,#2d2f34, 2-6 #4b4c4e,#0d1016, 7-20 #bfbdb6,#0d1016, 21-49 d,#0d1016, 50-51 d,#bfbdb6, 52-99 #bfbdb6,#0d1016,
+== snap step-back grid=100x30 cursor=7,2
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/themesel/cwd/t.txt Save Del NOR /tmp/pardes-snap/themesel/cwd Del
+| 1 line 1
+| 2 line 2 t.txt
+| 3 line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6
+| 7 line 7
+| 8 line 8
+| 9 line 9
+| 10 line 10
+| 11 line 11
+| 12 line 12
+| 13 line 13
+| 14 line 14
+| NOR /tmp/pardes-snap/themesel/cwd/+Themes Del
+| 1 Theme helix
+| 2 Theme dark
+| 3 Theme acme
+| 4 Theme ayu_dark
+| 5 Theme ayu_light
+| 6 Theme ayu_mirage
+| 7 Theme catppuccin_mocha
+| 8 Theme dracula
+| 9 Theme everforest_light
+| 10 Theme gruvbox
+| 11 Theme monokai
+| 12 Theme nord
+| 13 Theme one_dark
+== style step-back
+|0: 0-52 #000000,#eaffff, 53-99 d,#eaffff,
+|1: 0-1 d,#6e4fd0, 2-49 #000000,#eaffff, 50-51 d,#383c54, 52-88 #000000,#eaffff, 89-99 d,#eaffff,
+|2: 0-1 d,#ffffea, 2-6 #000000,#eaffff, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|3: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|4: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|5: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|6: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|7: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|8: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|9: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|10: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|11: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|12: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|13: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|14: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|15: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|16: 0-1 d,#383c54, 2-46 #000000,#eaffff, 47-49 d,#eaffff, 50-51 d,#ffffea, 52-99 #000000,#ffffea,
+|17: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-17 #000000,#ffffea, 18-51 d,#ffffea, 52-99 #000000,#ffffea,
+|18: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-16 #000000,#ffffea, 17-51 d,#ffffea, 52-99 #000000,#ffffea,
+|19: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-49 #d8c8a8,#4a3a1e, 50-51 d,#ffffea, 52-99 #000000,#ffffea,
+|20: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-20 #000000,#ffffea, 21-51 d,#ffffea, 52-99 #000000,#ffffea,
+|21: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-21 #000000,#ffffea, 22-51 d,#ffffea, 52-99 #000000,#ffffea,
+|22: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-22 #000000,#ffffea, 23-51 d,#ffffea, 52-99 #000000,#ffffea,
+|23: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-28 #000000,#ffffea, 29-51 d,#ffffea, 52-99 #000000,#ffffea,
+|24: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-19 #000000,#ffffea, 20-51 d,#ffffea, 52-99 #000000,#ffffea,
+|25: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-28 #000000,#ffffea, 29-51 d,#ffffea, 52-99 #000000,#ffffea,
+|26: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-19 #000000,#ffffea, 20-51 d,#ffffea, 52-99 #000000,#ffffea,
+|27: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-19 #000000,#ffffea, 20-51 d,#ffffea, 52-99 #000000,#ffffea,
+|28: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-16 #000000,#ffffea, 17-51 d,#ffffea, 52-99 #000000,#ffffea,
+|29: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-20 #000000,#ffffea, 21-51 d,#ffffea, 52-99 #000000,#ffffea,
+== snap picked grid=100x30 cursor=7,2
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/themesel/cwd/t.txt Save Del NOR /tmp/pardes-snap/themesel/cwd Del
+| 1 line 1
+| 2 line 2 t.txt
+| 3 line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6
+| 7 line 7
+| 8 line 8
+| 9 line 9
+| 10 line 10
+| 11 line 11
+| 12 line 12
+| 13 line 13
+| 14 line 14
+| 15 line 15
+| 16 line 16
+| 17 line 17
+| 18 line 18
+| 19 line 19
+| 20 line 20
+| 21 line 21
+| 22 line 22
+| 23 line 23
+| 24 line 24
+| 25 line 25
+| 26 line 26
+| 27 line 27
+| 28 line 28
+== style picked
+|0: 0-52 #000000,#eaffff, 53-99 d,#eaffff,
+|1: 0-1 d,#6e4fd0, 2-49 #000000,#eaffff, 50-51 d,#383c54, 52-88 #000000,#eaffff, 89-99 d,#eaffff,
+|2: 0-1 d,#ffffea, 2-6 #000000,#eaffff, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|3: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|4: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|5: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|6: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|7: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|8: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|9: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|10: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-12 #000000,#ffffea, 13-51 d,#ffffea, 52-99 #000000,#ffffea,
+|11: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|12: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|13: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|14: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|15: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|16: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|17: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|18: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|19: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|20: 0-1 d,#ffffea, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|21: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|22: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|23: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|24: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|25: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|26: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|27: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|28: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
+|29: 0-1 d,#99994c, 2-6 #999980,#ffffea, 7-13 #000000,#ffffea, 14-51 d,#ffffea, 52-99 #000000,#ffffea,
diff --git a/test/snapshots/themesel.snap b/test/snapshots/themesel.snap
new file mode 100644
index 00000000..3dd7e2dc
--- /dev/null
+++ b/test/snapshots/themesel.snap
@@ -0,0 +1,63 @@
+# RULE: a results buffer is a list of things you can RUN, and its traits say
+# which verb runs them. Every list before this one held LOCATIONS, so stepping
+# a row with n/N Looks it. `SPC t t` (ThemeSel) opens the first one whose rows
+# are COMMANDS — `Theme <name>`, exactly the line you would type — so n/N Exec
+# each row instead. Walking the list therefore wears the themes as you pass
+# them, and picking one is stopping on it: there is no preview state, nothing
+# to confirm and nothing to cancel, because arriving already did it.
+#
+# Both halves of the ring are in the buffer: the three pardes ships with come
+# first (helix, dark, acme), then everything tools/gen_themes.zig exported from
+# the vendored helix .toml and zed .json files, sorted. The colors below are
+# read with snapstyle, so this proves the generated themes are real data and
+# not just names in a list.
+lines t.txt 40
+start 30 100 t.txt
+wait 8000 t.txt
+stable 700 20000
+snap boot
+# SPC t t: the list lands below the pane that asked, focus stays HERE — n/N are
+# read in this pane and they step the buffer they just armed.
+key space t t
+wait 10000 Theme helix
+stable 700 15000
+snap list
+# n selects row 1 and runs it: `Theme helix`, which is already the boot theme,
+# so only the selection moves. That is the honest first step — an idempotent
+# command has to be safe to step onto.
+key n
+stable 700 15000
+snap step-helix
+snapstyle step-helix
+# ...and now the theme really does change under the cursor, one row at a time
+key n
+stable 700 15000
+snap step-dark
+snapstyle step-dark
+key n
+stable 700 15000
+snap step-acme
+snapstyle step-acme
+# past the curated three and into the generated half: a zed theme, palette and
+# all (zed names its sixteen terminal ANSI colors, so a generated one can
+# repaint a shell's output where a helix theme leaves it alone)
+key n
+stable 700 15000
+snap step-generated
+snapstyle step-generated
+# N walks back the same way, running the row it lands on — so the theme
+# rewinds too
+key N
+stable 700 15000
+snap step-back
+snapstyle step-back
+# PICKING is just stopping. Walk down into the list and Esc it away (an output
+# buffer has no file behind it, so Esc IS Del there): the theme the last step
+# left behind is still on.
+key space w j
+stable 400 5000
+key esc
+settle 100
+stable 700 15000
+snap picked
+snapstyle picked
diff --git a/tools/gen_themes.zig b/tools/gen_themes.zig
new file mode 100644
index 00000000..50c32b8b
--- /dev/null
+++ b/tools/gen_themes.zig
@@ -0,0 +1,414 @@
+//! Build-time generator: a vendored helix or zed theme in, one `.zig` file per
+//! theme out. build.zig runs it before anything compiles and hands the output
+//! directory to src/pardes.zig as the `generated_themes` module, so a theme is
+//! an ordinary source file by the time the compiler sees it.
+//!
+//! ADDING A THEME IS DROPPING A FILE INTO `vendor/themes/`: a helix `.toml` or
+//! a zed `.json`. The extension picks the reader and records where it came
+//! from; build.zig reads the directory, so there is no list anywhere to append
+//! to. A zed file holds a whole FAMILY (one.json is One Dark and One Light), so
+//! one input can be several themes out.
+//!
+//! Why vendored and not read out of ~/05-genizah: `zig build` must work on a
+//! fresh clone with no network and no helix checkout. The files are small and
+//! we consume a dozen fields out of each, so a curated handful beats a mirror
+//! of all 214 helix ships — bulk buys nothing when pardes throws away 95% of
+//! what a theme carries.
+//!
+//! Provenance: vendor/themes/*.toml are helix's own runtime themes (from
+//! helix-editor/helix runtime/themes, each file keeping its author's header);
+//! *.json are zed's (zed-industries/zed assets/themes). Every one carries its
+//! upstream licence beside it as `<name>.LICENSE`, and build.zig's directory
+//! read skips anything that is not a .toml or a .json so they can sit there.
+//!
+//! A theme that cannot be mapped is FATAL, naming the file and the key. The
+//! vendored set is curated, so a missing key means the file is not what this
+//! reader thinks it is — printing a warning and emitting a black-on-black theme
+//! would put that mistake on screen instead of in the build log.
+const std = @import("std");
+
+const Rgb = [3]u8;
+
+/// pardes's Theme, minus the type: the generated file spells an anonymous
+/// `.{...}` because a theme file deliberately imports NOTHING (it is data, not
+/// code) — src/pardes.zig's `fold` is what turns it into a real Theme.
+const Theme = struct {
+ name: []const u8,
+ bg: ?Rgb,
+ fg: ?Rgb,
+ tag_bg: Rgb,
+ tag_fg: Rgb,
+ box: Rgb,
+ box_dim: Rgb,
+ kw: Rgb,
+ str: Rgb,
+ num: Rgb,
+ comment: Rgb,
+ lineno: Rgb,
+ scroll_track: Rgb,
+ scroll_thumb: Rgb,
+ palette: ?[16]Rgb,
+};
+
+pub fn main(init: std.process.Init) !void {
+ const io = init.io;
+ const gpa = init.gpa;
+ const arena = init.arena.allocator();
+ const args = try init.minimal.args.toSlice(arena);
+ if (args.len < 3) std.process.fatal("usage: gen-themes <out-dir> <theme-source>...", .{});
+
+ var themes: std.ArrayList(Theme) = .empty;
+ for (args[2..]) |src| {
+ const text = try std.Io.Dir.cwd().readFileAlloc(io, src, arena, .limited(8 << 20));
+ if (std.mem.endsWith(u8, src, ".toml"))
+ try readHelix(arena, src, text, &themes)
+ else if (std.mem.endsWith(u8, src, ".json"))
+ try readZed(arena, gpa, src, text, &themes)
+ else
+ std.process.fatal("{s}: not a helix .toml or a zed .json", .{src});
+ }
+ // sorted, so the ring's tail is the same on every machine whatever order
+ // the directory happened to be read in
+ std.mem.sort(Theme, themes.items, {}, byName);
+
+ var dir = try std.Io.Dir.cwd().openDir(io, args[1], .{});
+ defer dir.close(io);
+ var buf: std.Io.Writer.Allocating = .init(arena);
+ for (themes.items) |t| {
+ buf.clearRetainingCapacity();
+ try emit(&buf.writer, t);
+ try write(io, dir, arena, t.name, buf.written());
+ }
+ // the INDEX, and the only reason it is generated rather than written: a zig
+ // file cannot list a directory at comptime, so the imports have to come
+ // from whoever can. pardes.zig folds its declarations into the ring.
+ buf.clearRetainingCapacity();
+ try buf.writer.writeAll("// Generated by tools/gen_themes.zig. Do not edit.\n");
+ for (themes.items) |t| try buf.writer.print("pub const {s} = @import(\"{s}.zig\");\n", .{ t.name, t.name });
+ try write(io, dir, arena, "list", buf.written());
+}
+
+fn write(io: std.Io, dir: std.Io.Dir, arena: std.mem.Allocator, stem: []const u8, text: []const u8) !void {
+ const file = try dir.createFile(io, try std.fmt.allocPrint(arena, "{s}.zig", .{stem}), .{});
+ defer file.close(io);
+ try file.writeStreamingAll(io, text);
+}
+
+fn byName(_: void, a: Theme, b: Theme) bool {
+ return std.mem.order(u8, a.name, b.name) == .lt;
+}
+
+fn emit(w: *std.Io.Writer, t: Theme) !void {
+ try w.writeAll("// Generated by tools/gen_themes.zig. Do not edit.\npub const theme = .{\n");
+ try w.print(" .name = \"{s}\",\n", .{t.name});
+ inline for (@typeInfo(Theme).@"struct".fields) |f| {
+ if (comptime std.mem.eql(u8, f.name, "name")) continue;
+ if (comptime std.mem.eql(u8, f.name, "palette")) continue;
+ try w.print(" .{s} = ", .{f.name});
+ if (comptime @typeInfo(f.type) == .optional) {
+ if (@field(t, f.name)) |c| try rgb(w, c) else try w.writeAll("null");
+ } else try rgb(w, @field(t, f.name));
+ try w.writeAll(",\n");
+ }
+ if (t.palette) |p| {
+ try w.writeAll(" .palette = .{\n");
+ for (p) |c| {
+ try w.writeAll(" ");
+ try rgb(w, c);
+ try w.writeAll(",\n");
+ }
+ try w.writeAll(" },\n");
+ } else try w.writeAll(" .palette = null,\n");
+ try w.writeAll("};\n");
+}
+
+fn rgb(w: *std.Io.Writer, c: Rgb) !void {
+ try w.print(".{{ 0x{x:0>2}, 0x{x:0>2}, 0x{x:0>2} }}", .{ c[0], c[1], c[2] });
+}
+
+/// `Ayu Mirage` -> `ayu_mirage`: the user-visible theme NAME, the generated
+/// file's stem and its declaration in the index, all one string. It has to be a
+/// single word (a `Theme <name>` row is executed by splitting on the first
+/// space) and a legal identifier (it is a decl in list.zig).
+fn slug(arena: std.mem.Allocator, raw: []const u8) ![]const u8 {
+ var out: std.ArrayList(u8) = .empty;
+ for (raw) |c| {
+ if (std.ascii.isAlphanumeric(c)) try out.append(arena, std.ascii.toLower(c))
+ // one underscore per run of punctuation, and never a trailing one
+ else if (out.items.len > 0 and out.items[out.items.len - 1] != '_') try out.append(arena, '_');
+ }
+ const s = std.mem.trim(u8, out.items, "_");
+ if (s.len == 0 or !std.ascii.isAlphabetic(s[0])) std.process.fatal("theme name {s} does not slug to an identifier", .{raw});
+ return s;
+}
+
+// ---- helix (.toml) ----
+
+// Not a TOML parser: helix themes are one `key = value` per line, where a value
+// is either a bare color string or an inline table, plus a `[palette]` section
+// of name -> color. A real parser would be a dependency and several hundred
+// lines to read a shape this flat.
+
+/// what a key was assigned, unresolved — `fg`/`bg` name a palette entry or a
+/// literal `#rrggbb`, and helix's bare form (`"ui.text" = "nord4"`) is an fg
+const Pair = struct { fg: ?[]const u8 = null, bg: ?[]const u8 = null };
+
+fn readHelix(arena: std.mem.Allocator, path: []const u8, text: []const u8, out: *std.ArrayList(Theme)) !void {
+ var palette: std.StringHashMapUnmanaged([]const u8) = .empty;
+ var keys: std.StringHashMapUnmanaged(Pair) = .empty;
+ var in_palette = false;
+ var lines = std.mem.splitScalar(u8, text, '\n');
+ while (lines.next()) |raw| {
+ const line = std.mem.trim(u8, uncomment(raw), " \t\r");
+ if (line.len == 0) continue;
+ if (line[0] == '[') {
+ in_palette = std.mem.eql(u8, line, "[palette]");
+ continue;
+ }
+ const eq = topLevel(line, '=') orelse continue;
+ const key = unquote(std.mem.trim(u8, line[0..eq], " \t"));
+ const val = std.mem.trim(u8, line[eq + 1 ..], " \t");
+ if (val.len == 0) continue;
+ if (in_palette) {
+ try palette.put(arena, key, unquote(val));
+ } else if (quoted(val)) {
+ try keys.put(arena, key, .{ .fg = unquote(val) });
+ } else if (val[0] == '{') {
+ try keys.put(arena, key, table(val));
+ }
+ }
+ // `inherits = "other"` needs a second file we did not read, so the theme we
+ // would emit is only the half that overrides. Vendor the parent instead.
+ if (keys.contains("inherits")) std.process.fatal("{s}: inherits= is not supported; vendor a theme that stands alone", .{path});
+
+ const h: Helix = .{ .path = path, .keys = keys, .palette = palette };
+ // helix's own rule for anything a theme left unset: the page's colors.
+ const fg = h.need("ui.text", .fg);
+ const bg = h.need("ui.background", .bg);
+ // The move box is the accent that says "here". helix's is the primary
+ // cursor's background — but a theme that draws its cursor by REVERSING
+ // video (nord, monokai) never names one, so the fallback is the loudest
+ // hue the theme did commit to.
+ const box = h.first(&.{ .{ "ui.cursor.primary", .bg }, .{ "ui.cursor", .bg }, .{ "keyword", .fg } }) orelse
+ std.process.fatal("{s}: no ui.cursor.primary/ui.cursor background and no keyword color", .{path});
+ const sel = h.get("ui.selection", .bg) orelse bg;
+ try out.append(arena, .{
+ .name = try slug(arena, std.fs.path.stem(path)),
+ // explicit rather than null: a helix theme dresses its whole page, and
+ // half-dressing it over whatever the host terminal defaults to is how
+ // you get grey-on-grey. `palette` stays null for the opposite reason —
+ // helix leaves the colors a PROGRAM emits alone, and so do we.
+ .bg = bg,
+ .fg = fg,
+ .tag_bg = h.get("ui.statusline", .bg) orelse bg,
+ .tag_fg = h.get("ui.statusline", .fg) orelse fg,
+ .box = box,
+ .box_dim = sel,
+ .kw = h.need("keyword", .fg),
+ .str = h.need("string", .fg),
+ // most themes only colour `constant`; the ones that split out numbers
+ // are being more specific, so they win
+ .num = h.first(&.{ .{ "constant.numeric", .fg }, .{ "constant", .fg } }) orelse
+ std.process.fatal("{s}: no constant.numeric or constant color", .{path}),
+ .comment = h.need("comment", .fg),
+ .lineno = h.get("ui.linenr", .fg) orelse fg,
+ // the scrollbar is chrome, so it takes the chrome colors: the tag bar's
+ // own background for the track, the selection (one step brighter than
+ // anything else in the theme's greys) for the thumb riding it
+ .scroll_track = h.get("ui.statusline", .bg) orelse bg,
+ .scroll_thumb = sel,
+ .palette = null,
+ });
+}
+
+const Which = enum { fg, bg };
+
+const Helix = struct {
+ path: []const u8,
+ keys: std.StringHashMapUnmanaged(Pair),
+ palette: std.StringHashMapUnmanaged([]const u8),
+
+ fn get(h: Helix, key: []const u8, which: Which) ?Rgb {
+ const p = h.keys.get(key) orelse return null;
+ return h.color(switch (which) {
+ .fg => p.fg orelse return null,
+ .bg => p.bg orelse return null,
+ });
+ }
+
+ fn need(h: Helix, key: []const u8, which: Which) Rgb {
+ return h.get(key, which) orelse
+ std.process.fatal("{s}: missing {s}.{s}", .{ h.path, key, @tagName(which) });
+ }
+
+ fn first(h: Helix, tries: []const struct { []const u8, Which }) ?Rgb {
+ for (tries) |t| if (h.get(t[0], t[1])) |c| return c;
+ return null;
+ }
+
+ /// a literal `#rrggbb`, or a `[palette]` name that resolves to one. A
+ /// palette entry may itself be a name (helix lets one alias another), so
+ /// this walks — bounded, because a cycle is a broken theme and not a hang.
+ fn color(h: Helix, start: []const u8) Rgb {
+ var name = start;
+ for (0..8) |_| {
+ if (name.len > 0 and name[0] == '#') return hex(h.path, name);
+ name = h.palette.get(name) orelse
+ std.process.fatal("{s}: color \"{s}\" is neither #rrggbb nor a [palette] entry", .{ h.path, start });
+ }
+ std.process.fatal("{s}: [palette] entry \"{s}\" never resolves to a color", .{ h.path, start });
+ }
+};
+
+/// cut a `#` comment, unless it is inside a string — where every colour in the
+/// file lives (`bg = "#282828" # main background`). TOML has two string
+/// quotes and themes use both, sometimes in the same file: solarized writes its
+/// whole palette in '...'.
+fn uncomment(line: []const u8) []const u8 {
+ var q: u8 = 0;
+ for (line, 0..) |c, i| {
+ if (q != 0) {
+ if (c == q) q = 0;
+ } else if (c == '"' or c == '\'') q = c else if (c == '#') return line[0..i];
+ }
+ return line;
+}
+
+/// index of the first `c` outside a string and outside brackets — the `=` that
+/// separates a key from its value, or the commas between an inline table's
+/// entries (an `underline = { color = ..., style = ... }` nested inside must
+/// not split)
+fn topLevel(s: []const u8, c: u8) ?usize {
+ var q: u8 = 0;
+ var depth: usize = 0;
+ for (s, 0..) |ch, i| {
+ if (q != 0) {
+ if (ch == q) q = 0;
+ continue;
+ }
+ switch (ch) {
+ '"', '\'' => q = ch,
+ '{', '[' => depth += 1,
+ '}', ']' => depth -|= 1,
+ else => if (ch == c and depth == 0) return i,
+ }
+ }
+ return null;
+}
+
+fn quoted(s: []const u8) bool {
+ return s.len > 0 and (s[0] == '"' or s[0] == '\'');
+}
+
+fn unquote(s: []const u8) []const u8 {
+ if (s.len >= 2 and quoted(s) and s[s.len - 1] == s[0]) return s[1 .. s.len - 1];
+ return s;
+}
+
+/// `{ fg = "red1", modifiers = ["italic"] }` -> the fg/bg it names
+fn table(s: []const u8) Pair {
+ var out: Pair = .{};
+ const body = s[1 .. s.len - @intFromBool(s[s.len - 1] == '}')];
+ var rest = body;
+ while (rest.len > 0) {
+ const end = topLevel(rest, ',') orelse rest.len;
+ const item = rest[0..end];
+ rest = rest[@min(end + 1, rest.len)..];
+ const eq = topLevel(item, '=') orelse continue;
+ const k = std.mem.trim(u8, item[0..eq], " \t");
+ const v = std.mem.trim(u8, item[eq + 1 ..], " \t");
+ if (!quoted(v)) continue;
+ if (std.mem.eql(u8, k, "fg")) out.fg = unquote(v);
+ if (std.mem.eql(u8, k, "bg")) out.bg = unquote(v);
+ }
+ return out;
+}
+
+// ---- zed (.json) ----
+
+// One file is a FAMILY: `themes` is an array of variants, each with a flat
+// `style` map of dotted names plus a `style.syntax` map of {color, ...}.
+// Everything is spelled out (no palette indirection), and unlike helix a zed
+// theme names all sixteen terminal ANSI colors — which is the whole reason to
+// take them: a light theme is only usable if the shell's own output is
+// repainted for it, and pardes's acme-light is the hand-written proof.
+
+fn readZed(arena: std.mem.Allocator, gpa: std.mem.Allocator, path: []const u8, text: []const u8, out: *std.ArrayList(Theme)) !void {
+ const parsed = std.json.parseFromSlice(std.json.Value, gpa, text, .{}) catch
+ std.process.fatal("{s}: not valid JSON", .{path});
+ defer parsed.deinit();
+ const root = switch (parsed.value) {
+ .object => |o| o,
+ else => std.process.fatal("{s}: top level is not an object", .{path}),
+ };
+ const list = root.get("themes") orelse std.process.fatal("{s}: no `themes` array", .{path});
+ for (list.array.items) |variant| {
+ const v = variant.object;
+ const z: Zed = .{
+ .path = path,
+ .style = (v.get("style") orelse std.process.fatal("{s}: a variant has no `style`", .{path})).object,
+ };
+ const syntax = (z.style.get("syntax") orelse std.process.fatal("{s}: a variant has no `style.syntax`", .{path})).object;
+ var pal: [16]Rgb = undefined;
+ for (ansi, 0..) |n, i| {
+ pal[i] = z.need(try std.fmt.allocPrint(arena, "terminal.ansi.{s}", .{n}));
+ pal[i + 8] = z.need(try std.fmt.allocPrint(arena, "terminal.ansi.bright_{s}", .{n}));
+ }
+ try out.append(arena, .{
+ .name = try slug(arena, (v.get("name") orelse std.process.fatal("{s}: a variant has no `name`", .{path})).string),
+ .bg = z.need("editor.background"),
+ .fg = z.need("editor.foreground"),
+ .tag_bg = z.need("status_bar.background"),
+ .tag_fg = z.need("text.muted"),
+ // zed names its accent, so the move box does not have to be guessed
+ // out of the cursor the way it is for a helix theme
+ .box = z.need("text.accent"),
+ .box_dim = z.need("border"),
+ .kw = z.syn(syntax, "keyword"),
+ .str = z.syn(syntax, "string"),
+ .num = z.syn(syntax, "number"),
+ .comment = z.syn(syntax, "comment"),
+ .lineno = z.need("editor.line_number"),
+ .scroll_track = z.need("scrollbar.thumb.border"),
+ .scroll_thumb = z.need("scrollbar.thumb.background"),
+ .palette = pal,
+ });
+ }
+}
+
+/// zed's order, which is also the ANSI one: 0-7 plain, 8-15 the `bright_` half
+const ansi = [8][]const u8{ "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" };
+
+const Zed = struct {
+ path: []const u8,
+ style: std.json.ObjectMap,
+
+ fn need(z: Zed, key: []const u8) Rgb {
+ const v = z.style.get(key) orelse std.process.fatal("{s}: missing style.{s}", .{ z.path, key });
+ return switch (v) {
+ .string => |s| hex(z.path, s),
+ else => std.process.fatal("{s}: style.{s} is not a color string", .{ z.path, key }),
+ };
+ }
+
+ fn syn(z: Zed, syntax: std.json.ObjectMap, key: []const u8) Rgb {
+ const v = syntax.get(key) orelse std.process.fatal("{s}: missing style.syntax.{s}", .{ z.path, key });
+ const c = v.object.get("color") orelse std.process.fatal("{s}: style.syntax.{s} has no color", .{ z.path, key });
+ return switch (c) {
+ .string => |s| hex(z.path, s),
+ else => std.process.fatal("{s}: style.syntax.{s}.color is not a string", .{ z.path, key }),
+ };
+ }
+};
+
+/// `#rrggbb`, or zed's `#rrggbbaa`. The alpha is DROPPED: pardes paints opaque
+/// cells, and the only translucent colors we read are the scrollbar's, where a
+/// dropped alpha reads as a slightly bolder bar and nothing else.
+fn hex(path: []const u8, s: []const u8) Rgb {
+ const d = if (s.len > 0 and s[0] == '#') s[1..] else s;
+ if (d.len != 6 and d.len != 8) std.process.fatal("{s}: \"{s}\" is not #rrggbb", .{ path, s });
+ var out: Rgb = undefined;
+ for (&out, 0..) |*c, i| c.* = std.fmt.parseInt(u8, d[i * 2 ..][0..2], 16) catch
+ std.process.fatal("{s}: \"{s}\" is not #rrggbb", .{ path, s });
+ return out;
+}
diff --git a/vendor/themes/ayu.LICENSE b/vendor/themes/ayu.LICENSE
new file mode 100644
index 00000000..37a92292
--- /dev/null
+++ b/vendor/themes/ayu.LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Ike Ku
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/themes/ayu.json b/vendor/themes/ayu.json
new file mode 100644
index 00000000..ee5073e7
--- /dev/null
+++ b/vendor/themes/ayu.json
@@ -0,0 +1,1228 @@
+{
+ "$schema": "https://zed.dev/schema/themes/v0.2.0.json",
+ "name": "Ayu",
+ "author": "Zed Industries",
+ "themes": [
+ {
+ "name": "Ayu Dark",
+ "appearance": "dark",
+ "style": {
+ "border": "#3f4043ff",
+ "border.variant": "#2d2f34ff",
+ "border.focused": "#1b4a6eff",
+ "border.selected": "#1b4a6eff",
+ "border.transparent": "#00000000",
+ "border.disabled": "#383a3eff",
+ "elevated_surface.background": "#1f2127ff",
+ "surface.background": "#1f2127ff",
+ "background": "#313337ff",
+ "element.background": "#1f2127ff",
+ "element.hover": "#2d2f34ff",
+ "element.active": "#3e4043ff",
+ "element.selected": "#3e4043ff",
+ "element.disabled": "#1f2127ff",
+ "drop_target.background": "#8a898680",
+ "ghost_element.background": "#00000000",
+ "ghost_element.hover": "#2d2f34ff",
+ "ghost_element.active": "#3e4043ff",
+ "ghost_element.selected": "#3e4043ff",
+ "ghost_element.disabled": "#1f2127ff",
+ "text": "#bfbdb6ff",
+ "text.muted": "#8a8986ff",
+ "text.placeholder": "#696a6aff",
+ "text.disabled": "#696a6aff",
+ "text.accent": "#5ac1feff",
+ "icon": "#bfbdb6ff",
+ "icon.muted": "#8a8986ff",
+ "icon.disabled": "#696a6aff",
+ "icon.placeholder": "#8a8986ff",
+ "icon.accent": "#5ac1feff",
+ "status_bar.background": "#313337ff",
+ "title_bar.background": "#313337ff",
+ "title_bar.inactive_background": "#1f2127ff",
+ "toolbar.background": "#0d1016ff",
+ "tab_bar.background": "#1f2127ff",
+ "tab.inactive_background": "#1f2127ff",
+ "tab.active_background": "#0d1016ff",
+ "search.match_background": "#5ac2fe66",
+ "search.active_match_background": "#ea570166",
+ "panel.background": "#1f2127ff",
+ "panel.focused_border": "#5ac1feff",
+ "pane.focused_border": null,
+ "scrollbar.thumb.background": "#bfbdb64c",
+ "scrollbar.thumb.hover_background": "#2d2f34ff",
+ "scrollbar.thumb.border": "#2d2f34ff",
+ "scrollbar.track.background": "#00000000",
+ "scrollbar.track.border": "#1b1e24ff",
+ "editor.foreground": "#bfbdb6ff",
+ "editor.background": "#0d1016ff",
+ "editor.gutter.background": "#0d1016ff",
+ "editor.subheader.background": "#1f2127ff",
+ "editor.active_line.background": "#1f2127bf",
+ "editor.highlighted_line.background": "#1f2127ff",
+ "editor.line_number": "#4b4c4e",
+ "editor.active_line_number": "#cbcccd",
+ "editor.hover_line_number": "#a1a2a5",
+ "editor.invisible": "#666767ff",
+ "editor.wrap_guide": "#bfbdb60d",
+ "editor.active_wrap_guide": "#bfbdb61a",
+ "editor.document_highlight.read_background": "#5ac1fe1a",
+ "editor.document_highlight.write_background": "#66676766",
+ "terminal.background": "#0d1016ff",
+ "terminal.foreground": "#bfbdb6ff",
+ "terminal.bright_foreground": "#bfbdb6ff",
+ "terminal.dim_foreground": "#85847fff",
+ "terminal.ansi.black": "#0d1016ff",
+ "terminal.ansi.bright_black": "#545557ff",
+ "terminal.ansi.dim_black": "#3a3b3cff",
+ "terminal.ansi.red": "#ef7177ff",
+ "terminal.ansi.bright_red": "#83353bff",
+ "terminal.ansi.dim_red": "#a74f53ff",
+ "terminal.ansi.green": "#aad84cff",
+ "terminal.ansi.bright_green": "#567627ff",
+ "terminal.ansi.dim_green": "#769735ff",
+ "terminal.ansi.yellow": "#feb454ff",
+ "terminal.ansi.bright_yellow": "#92582bff",
+ "terminal.ansi.dim_yellow": "#b17d3aff",
+ "terminal.ansi.blue": "#5ac1feff",
+ "terminal.ansi.bright_blue": "#27618cff",
+ "terminal.ansi.dim_blue": "#3e87b1ff",
+ "terminal.ansi.magenta": "#39bae5ff",
+ "terminal.ansi.bright_magenta": "#205a78ff",
+ "terminal.ansi.dim_magenta": "#2782a0ff",
+ "terminal.ansi.cyan": "#95e5cbff",
+ "terminal.ansi.bright_cyan": "#4c806fff",
+ "terminal.ansi.dim_cyan": "#68a08eff",
+ "terminal.ansi.white": "#bfbdb6ff",
+ "terminal.ansi.bright_white": "#fafafaff",
+ "terminal.ansi.dim_white": "#85847fff",
+ "link_text.hover": "#5ac1feff",
+ "conflict": "#feb454ff",
+ "conflict.background": "#572815ff",
+ "conflict.border": "#754221ff",
+ "created": "#aad84cff",
+ "created.background": "#294113ff",
+ "created.border": "#405c1cff",
+ "deleted": "#ef7177ff",
+ "deleted.background": "#48161bff",
+ "deleted.border": "#66272dff",
+ "error": "#ef7177ff",
+ "error.background": "#48161bff",
+ "error.border": "#66272dff",
+ "hidden": "#696a6aff",
+ "hidden.background": "#313337ff",
+ "hidden.border": "#383a3eff",
+ "hint": "#628b80ff",
+ "hint.background": "#0d2f4eff",
+ "hint.border": "#1b4a6eff",
+ "ignored": "#696a6aff",
+ "ignored.background": "#313337ff",
+ "ignored.border": "#3f4043ff",
+ "info": "#5ac1feff",
+ "info.background": "#0d2f4eff",
+ "info.border": "#1b4a6eff",
+ "modified": "#feb454ff",
+ "modified.background": "#572815ff",
+ "modified.border": "#754221ff",
+ "predictive": "#5a728bff",
+ "predictive.background": "#294113ff",
+ "predictive.border": "#405c1cff",
+ "renamed": "#5ac1feff",
+ "renamed.background": "#0d2f4eff",
+ "renamed.border": "#1b4a6eff",
+ "success": "#aad84cff",
+ "success.background": "#294113ff",
+ "success.border": "#405c1cff",
+ "unreachable": "#8a8986ff",
+ "unreachable.background": "#313337ff",
+ "unreachable.border": "#3f4043ff",
+ "warning": "#feb454ff",
+ "warning.background": "#572815ff",
+ "warning.border": "#754221ff",
+ "players": [
+ {
+ "cursor": "#5ac1feff",
+ "background": "#5ac1feff",
+ "selection": "#5ac1fe3d"
+ },
+ {
+ "cursor": "#39bae5ff",
+ "background": "#39bae5ff",
+ "selection": "#39bae53d"
+ },
+ {
+ "cursor": "#fe8f40ff",
+ "background": "#fe8f40ff",
+ "selection": "#fe8f403d"
+ },
+ {
+ "cursor": "#d2a6feff",
+ "background": "#d2a6feff",
+ "selection": "#d2a6fe3d"
+ },
+ {
+ "cursor": "#95e5cbff",
+ "background": "#95e5cbff",
+ "selection": "#95e5cb3d"
+ },
+ {
+ "cursor": "#ef7177ff",
+ "background": "#ef7177ff",
+ "selection": "#ef71773d"
+ },
+ {
+ "cursor": "#feb454ff",
+ "background": "#feb454ff",
+ "selection": "#feb4543d"
+ },
+ {
+ "cursor": "#aad84cff",
+ "background": "#aad84cff",
+ "selection": "#aad84c3d"
+ }
+ ],
+ "syntax": {
+ "attribute": {
+ "color": "#5ac1feff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "boolean": {
+ "color": "#d2a6ffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "comment": {
+ "color": "#5c6773ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "comment.doc": {
+ "color": "#8c8b88ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "constant": {
+ "color": "#d2a6ffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "constructor": {
+ "color": "#5ac1feff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "embedded": {
+ "color": "#bfbdb6ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "emphasis": {
+ "color": "#5ac1feff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "emphasis.strong": {
+ "color": "#5ac1feff",
+ "font_style": null,
+ "font_weight": 700
+ },
+ "enum": {
+ "color": "#fe8f40ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "function": {
+ "color": "#ffb353ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "hint": {
+ "color": "#628b80ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "keyword": {
+ "color": "#ff8f3fff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "label": {
+ "color": "#5ac1feff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "link_text": {
+ "color": "#fe8f40ff",
+ "font_style": "italic",
+ "font_weight": null
+ },
+ "link_uri": {
+ "color": "#aad84cff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "namespace": {
+ "color": "#bfbdb6ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "number": {
+ "color": "#d2a6ffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "operator": {
+ "color": "#f29668ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "predictive": {
+ "color": "#5a728bff",
+ "font_style": "italic",
+ "font_weight": null
+ },
+ "preproc": {
+ "color": "#ff8f3fff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "primary": {
+ "color": "#bfbdb6ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "property": {
+ "color": "#5ac1feff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation": {
+ "color": "#a6a5a0ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.bracket": {
+ "color": "#a6a5a0ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.delimiter": {
+ "color": "#a6a5a0ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.list_marker": {
+ "color": "#a6a5a0ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.markup": {
+ "color": "#a6a5a0ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.special": {
+ "color": "#d2a6ffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "selector": {
+ "color": "#d2a6ffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "selector.pseudo": {
+ "color": "#5ac1feff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string": {
+ "color": "#a9d94bff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.escape": {
+ "color": "#8c8b88ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.regex": {
+ "color": "#95e6cbff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.special": {
+ "color": "#e5b572ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.special.symbol": {
+ "color": "#fe8f40ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "tag": {
+ "color": "#5ac1feff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "text.literal": {
+ "color": "#fe8f40ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "title": {
+ "color": "#bfbdb6ff",
+ "font_style": null,
+ "font_weight": 700
+ },
+ "type": {
+ "color": "#59c2ffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variable": {
+ "color": "#bfbdb6ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variable.parameter": {
+ "color": "#d2a6ffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variant": {
+ "color": "#5ac1feff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "diff.plus": {
+ "color": "#aad94cff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "diff.minus": {
+ "color": "#f07178ff",
+ "font_style": null,
+ "font_weight": null
+ }
+ }
+ }
+ },
+ {
+ "name": "Ayu Light",
+ "appearance": "light",
+ "style": {
+ "border": "#cfd1d2ff",
+ "border.variant": "#dfe0e1ff",
+ "border.focused": "#c4daf6ff",
+ "border.selected": "#c4daf6ff",
+ "border.transparent": "#00000000",
+ "border.disabled": "#d5d6d8ff",
+ "elevated_surface.background": "#ececedff",
+ "surface.background": "#ececedff",
+ "background": "#dcdddeff",
+ "element.background": "#ececedff",
+ "element.hover": "#dfe0e1ff",
+ "element.active": "#cfd0d2ff",
+ "element.selected": "#cfd0d2ff",
+ "element.disabled": "#ececedff",
+ "drop_target.background": "#8b8e9280",
+ "ghost_element.background": "#00000000",
+ "ghost_element.hover": "#dfe0e1ff",
+ "ghost_element.active": "#cfd0d2ff",
+ "ghost_element.selected": "#cfd0d2ff",
+ "ghost_element.disabled": "#ececedff",
+ "text": "#5c6166ff",
+ "text.muted": "#8b8e92ff",
+ "text.placeholder": "#a9acaeff",
+ "text.disabled": "#a9acaeff",
+ "text.accent": "#3b9ee5ff",
+ "icon": "#5c6166ff",
+ "icon.muted": "#8b8e92ff",
+ "icon.disabled": "#a9acaeff",
+ "icon.placeholder": "#8b8e92ff",
+ "icon.accent": "#3b9ee5ff",
+ "status_bar.background": "#dcdddeff",
+ "title_bar.background": "#dcdddeff",
+ "title_bar.inactive_background": "#ececedff",
+ "toolbar.background": "#fcfcfcff",
+ "tab_bar.background": "#ececedff",
+ "tab.inactive_background": "#ececedff",
+ "tab.active_background": "#fcfcfcff",
+ "search.match_background": "#3b9ee566",
+ "search.active_match_background": "#f88b3666",
+ "panel.background": "#ececedff",
+ "panel.focused_border": "#3b9ee5ff",
+ "pane.focused_border": null,
+ "scrollbar.thumb.background": "#5c61664c",
+ "scrollbar.thumb.hover_background": "#dfe0e1ff",
+ "scrollbar.thumb.border": "#dfe0e1ff",
+ "scrollbar.track.background": "#00000000",
+ "scrollbar.track.border": "#efeff0ff",
+ "editor.foreground": "#5c6166ff",
+ "editor.background": "#fcfcfcff",
+ "editor.gutter.background": "#fcfcfcff",
+ "editor.subheader.background": "#ececedff",
+ "editor.active_line.background": "#ececedbf",
+ "editor.highlighted_line.background": "#ececedff",
+ "editor.line_number": "#b0b3b5",
+ "editor.active_line_number": "#313435",
+ "editor.hover_line_number": "#62686a",
+ "editor.invisible": "#acafb1ff",
+ "editor.wrap_guide": "#5c61660d",
+ "editor.active_wrap_guide": "#5c61661a",
+ "editor.document_highlight.read_background": "#3b9ee51a",
+ "editor.document_highlight.write_background": "#acafb166",
+ "terminal.background": "#fcfcfcff",
+ "terminal.foreground": "#5c6166ff",
+ "terminal.bright_foreground": "#5c6166ff",
+ "terminal.dim_foreground": "#fcfcfcff",
+ "terminal.ansi.black": "#5c6166ff",
+ "terminal.ansi.bright_black": "#3b9ee5ff",
+ "terminal.ansi.dim_black": "#9c9fa2ff",
+ "terminal.ansi.red": "#ef7271ff",
+ "terminal.ansi.bright_red": "#febab6ff",
+ "terminal.ansi.dim_red": "#833538ff",
+ "terminal.ansi.green": "#85b304ff",
+ "terminal.ansi.bright_green": "#c7d98fff",
+ "terminal.ansi.dim_green": "#445613ff",
+ "terminal.ansi.yellow": "#f1ad49ff",
+ "terminal.ansi.bright_yellow": "#fed5a3ff",
+ "terminal.ansi.dim_yellow": "#8a5227ff",
+ "terminal.ansi.blue": "#3b9ee5ff",
+ "terminal.ansi.bright_blue": "#abcdf2ff",
+ "terminal.ansi.dim_blue": "#214c76ff",
+ "terminal.ansi.magenta": "#55b4d3ff",
+ "terminal.ansi.bright_magenta": "#b1d8e8ff",
+ "terminal.ansi.dim_magenta": "#2f5669ff",
+ "terminal.ansi.cyan": "#4dbf99ff",
+ "terminal.ansi.bright_cyan": "#ace0cbff",
+ "terminal.ansi.dim_cyan": "#2a5f4aff",
+ "terminal.ansi.white": "#fcfcfcff",
+ "terminal.ansi.bright_white": "#ffffffff",
+ "terminal.ansi.dim_white": "#bcbec0ff",
+ "link_text.hover": "#3b9ee5ff",
+ "conflict": "#f1ad49ff",
+ "conflict.background": "#ffeedaff",
+ "conflict.border": "#ffe1beff",
+ "created": "#85b304ff",
+ "created.background": "#e9efd2ff",
+ "created.border": "#d7e3aeff",
+ "deleted": "#ef7271ff",
+ "deleted.background": "#ffe3e1ff",
+ "deleted.border": "#ffcdcaff",
+ "error": "#ef7271ff",
+ "error.background": "#ffe3e1ff",
+ "error.border": "#ffcdcaff",
+ "hidden": "#a9acaeff",
+ "hidden.background": "#dcdddeff",
+ "hidden.border": "#d5d6d8ff",
+ "hint": "#8ca7c2ff",
+ "hint.background": "#deebfaff",
+ "hint.border": "#c4daf6ff",
+ "ignored": "#a9acaeff",
+ "ignored.background": "#dcdddeff",
+ "ignored.border": "#cfd1d2ff",
+ "info": "#3b9ee5ff",
+ "info.background": "#deebfaff",
+ "info.border": "#c4daf6ff",
+ "modified": "#f1ad49ff",
+ "modified.background": "#ffeedaff",
+ "modified.border": "#ffe1beff",
+ "predictive": "#9eb9d3ff",
+ "predictive.background": "#e9efd2ff",
+ "predictive.border": "#d7e3aeff",
+ "renamed": "#3b9ee5ff",
+ "renamed.background": "#deebfaff",
+ "renamed.border": "#c4daf6ff",
+ "success": "#85b304ff",
+ "success.background": "#e9efd2ff",
+ "success.border": "#d7e3aeff",
+ "unreachable": "#8b8e92ff",
+ "unreachable.background": "#dcdddeff",
+ "unreachable.border": "#cfd1d2ff",
+ "warning": "#f1ad49ff",
+ "warning.background": "#ffeedaff",
+ "warning.border": "#ffe1beff",
+ "players": [
+ {
+ "cursor": "#3b9ee5ff",
+ "background": "#3b9ee5ff",
+ "selection": "#3b9ee53d"
+ },
+ {
+ "cursor": "#55b4d3ff",
+ "background": "#55b4d3ff",
+ "selection": "#55b4d33d"
+ },
+ {
+ "cursor": "#f98d3fff",
+ "background": "#f98d3fff",
+ "selection": "#f98d3f3d"
+ },
+ {
+ "cursor": "#a37accff",
+ "background": "#a37accff",
+ "selection": "#a37acc3d"
+ },
+ {
+ "cursor": "#4dbf99ff",
+ "background": "#4dbf99ff",
+ "selection": "#4dbf993d"
+ },
+ {
+ "cursor": "#ef7271ff",
+ "background": "#ef7271ff",
+ "selection": "#ef72713d"
+ },
+ {
+ "cursor": "#f1ad49ff",
+ "background": "#f1ad49ff",
+ "selection": "#f1ad493d"
+ },
+ {
+ "cursor": "#85b304ff",
+ "background": "#85b304ff",
+ "selection": "#85b3043d"
+ }
+ ],
+ "syntax": {
+ "attribute": {
+ "color": "#3b9ee5ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "boolean": {
+ "color": "#a37accff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "comment": {
+ "color": "#abb0b6ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "comment.doc": {
+ "color": "#898d90ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "constant": {
+ "color": "#a37accff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "constructor": {
+ "color": "#3b9ee5ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "embedded": {
+ "color": "#5c6166ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "emphasis": {
+ "color": "#3b9ee5ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "emphasis.strong": {
+ "color": "#3b9ee5ff",
+ "font_style": null,
+ "font_weight": 700
+ },
+ "enum": {
+ "color": "#f98d3fff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "function": {
+ "color": "#f2ad48ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "hint": {
+ "color": "#8ca7c2ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "keyword": {
+ "color": "#fa8d3eff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "label": {
+ "color": "#3b9ee5ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "link_text": {
+ "color": "#f98d3fff",
+ "font_style": "italic",
+ "font_weight": null
+ },
+ "link_uri": {
+ "color": "#85b304ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "namespace": {
+ "color": "#5c6166ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "number": {
+ "color": "#a37accff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "operator": {
+ "color": "#ed9365ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "predictive": {
+ "color": "#9eb9d3ff",
+ "font_style": "italic",
+ "font_weight": null
+ },
+ "preproc": {
+ "color": "#fa8d3eff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "primary": {
+ "color": "#5c6166ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "property": {
+ "color": "#3b9ee5ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation": {
+ "color": "#73777bff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.bracket": {
+ "color": "#73777bff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.delimiter": {
+ "color": "#73777bff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.list_marker": {
+ "color": "#73777bff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.markup": {
+ "color": "#73777bff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.special": {
+ "color": "#a37accff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "selector": {
+ "color": "#a37accff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "selector.pseudo": {
+ "color": "#3b9ee5ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string": {
+ "color": "#86b300ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.escape": {
+ "color": "#898d90ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.regex": {
+ "color": "#4bbf98ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.special": {
+ "color": "#e6ba7eff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.special.symbol": {
+ "color": "#f98d3fff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "tag": {
+ "color": "#3b9ee5ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "text.literal": {
+ "color": "#f98d3fff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "title": {
+ "color": "#5c6166ff",
+ "font_style": null,
+ "font_weight": 700
+ },
+ "type": {
+ "color": "#389ee6ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variable": {
+ "color": "#5c6166ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variable.parameter": {
+ "color": "#a37accff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variant": {
+ "color": "#3b9ee5ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "diff.plus": {
+ "color": "#6cbf43ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "diff.minus": {
+ "color": "#ff6666ff",
+ "font_style": null,
+ "font_weight": null
+ }
+ }
+ }
+ },
+ {
+ "name": "Ayu Mirage",
+ "appearance": "dark",
+ "style": {
+ "border": "#53565dff",
+ "border.variant": "#43464fff",
+ "border.focused": "#24556fff",
+ "border.selected": "#24556fff",
+ "border.transparent": "#00000000",
+ "border.disabled": "#4d5058ff",
+ "elevated_surface.background": "#353944ff",
+ "surface.background": "#353944ff",
+ "background": "#464a52ff",
+ "element.background": "#353944ff",
+ "element.hover": "#43464fff",
+ "element.active": "#53565dff",
+ "element.selected": "#53565dff",
+ "element.disabled": "#353944ff",
+ "drop_target.background": "#9a9a9880",
+ "ghost_element.background": "#00000000",
+ "ghost_element.hover": "#43464fff",
+ "ghost_element.active": "#53565dff",
+ "ghost_element.selected": "#53565dff",
+ "ghost_element.disabled": "#353944ff",
+ "text": "#cccac2ff",
+ "text.muted": "#9a9a98ff",
+ "text.placeholder": "#7b7d7fff",
+ "text.disabled": "#7b7d7fff",
+ "text.accent": "#72cffeff",
+ "icon": "#cccac2ff",
+ "icon.muted": "#9a9a98ff",
+ "icon.disabled": "#7b7d7fff",
+ "icon.placeholder": "#9a9a98ff",
+ "icon.accent": "#72cffeff",
+ "status_bar.background": "#464a52ff",
+ "title_bar.background": "#464a52ff",
+ "title_bar.inactive_background": "#353944ff",
+ "toolbar.background": "#242835ff",
+ "tab_bar.background": "#353944ff",
+ "tab.inactive_background": "#353944ff",
+ "tab.active_background": "#242835ff",
+ "search.match_background": "#73cffe66",
+ "search.active_match_background": "#fd722b66",
+ "panel.background": "#353944ff",
+ "panel.focused_border": null,
+ "pane.focused_border": null,
+ "scrollbar.thumb.background": "#cccac24c",
+ "scrollbar.thumb.hover_background": "#43464fff",
+ "scrollbar.thumb.border": "#43464fff",
+ "scrollbar.track.background": "#00000000",
+ "scrollbar.track.border": "#323641ff",
+ "editor.foreground": "#cccac2ff",
+ "editor.background": "#242835ff",
+ "editor.gutter.background": "#242835ff",
+ "editor.subheader.background": "#353944ff",
+ "editor.active_line.background": "#353944bf",
+ "editor.highlighted_line.background": "#353944ff",
+ "editor.line_number": "#575c6b",
+ "editor.active_line_number": "#e1e3ea",
+ "editor.hover_line_number": "#b2b6c8",
+ "editor.invisible": "#787a7cff",
+ "editor.wrap_guide": "#cccac20d",
+ "editor.active_wrap_guide": "#cccac21a",
+ "editor.document_highlight.read_background": "#72cffe1a",
+ "editor.document_highlight.write_background": "#787a7c66",
+ "terminal.background": "#242835ff",
+ "terminal.foreground": "#cccac2ff",
+ "terminal.bright_foreground": "#cccac2ff",
+ "terminal.dim_foreground": "#8e8d87ff",
+ "terminal.ansi.black": "#242835ff",
+ "terminal.ansi.bright_black": "#67696eff",
+ "terminal.ansi.dim_black": "#48494dff",
+ "terminal.ansi.red": "#f18779ff",
+ "terminal.ansi.bright_red": "#833f3cff",
+ "terminal.ansi.dim_red": "#a85e54ff",
+ "terminal.ansi.green": "#d5fe80ff",
+ "terminal.ansi.bright_green": "#75993cff",
+ "terminal.ansi.dim_green": "#95b159ff",
+ "terminal.ansi.yellow": "#fecf72ff",
+ "terminal.ansi.bright_yellow": "#937237ff",
+ "terminal.ansi.dim_yellow": "#b1904fff",
+ "terminal.ansi.blue": "#72cffeff",
+ "terminal.ansi.bright_blue": "#336d8dff",
+ "terminal.ansi.dim_blue": "#4f90b1ff",
+ "terminal.ansi.magenta": "#5bcde5ff",
+ "terminal.ansi.bright_magenta": "#2b6c7bff",
+ "terminal.ansi.dim_magenta": "#3f8fa0ff",
+ "terminal.ansi.cyan": "#95e5cbff",
+ "terminal.ansi.bright_cyan": "#4c806fff",
+ "terminal.ansi.dim_cyan": "#68a08eff",
+ "terminal.ansi.white": "#cccac2ff",
+ "terminal.ansi.bright_white": "#fafafaff",
+ "terminal.ansi.dim_white": "#8e8d87ff",
+ "link_text.hover": "#72cffeff",
+ "conflict": "#fecf72ff",
+ "conflict.background": "#574018ff",
+ "conflict.border": "#765a29ff",
+ "created": "#d5fe80ff",
+ "created.background": "#426117ff",
+ "created.border": "#5d7e2cff",
+ "deleted": "#f18779ff",
+ "deleted.background": "#481a1bff",
+ "deleted.border": "#662e2dff",
+ "error": "#f18779ff",
+ "error.background": "#481a1bff",
+ "error.border": "#662e2dff",
+ "hidden": "#7b7d7fff",
+ "hidden.background": "#464a52ff",
+ "hidden.border": "#4d5058ff",
+ "hint": "#7399a3ff",
+ "hint.background": "#123950ff",
+ "hint.border": "#24556fff",
+ "ignored": "#7b7d7fff",
+ "ignored.background": "#464a52ff",
+ "ignored.border": "#53565dff",
+ "info": "#72cffeff",
+ "info.background": "#123950ff",
+ "info.border": "#24556fff",
+ "modified": "#fecf72ff",
+ "modified.background": "#574018ff",
+ "modified.border": "#765a29ff",
+ "predictive": "#6d839bff",
+ "predictive.background": "#426117ff",
+ "predictive.border": "#5d7e2cff",
+ "renamed": "#72cffeff",
+ "renamed.background": "#123950ff",
+ "renamed.border": "#24556fff",
+ "success": "#d5fe80ff",
+ "success.background": "#426117ff",
+ "success.border": "#5d7e2cff",
+ "unreachable": "#9a9a98ff",
+ "unreachable.background": "#464a52ff",
+ "unreachable.border": "#53565dff",
+ "warning": "#fecf72ff",
+ "warning.background": "#574018ff",
+ "warning.border": "#765a29ff",
+ "players": [
+ {
+ "cursor": "#72cffeff",
+ "background": "#72cffeff",
+ "selection": "#72cffe3d"
+ },
+ {
+ "cursor": "#5bcde5ff",
+ "background": "#5bcde5ff",
+ "selection": "#5bcde53d"
+ },
+ {
+ "cursor": "#fead66ff",
+ "background": "#fead66ff",
+ "selection": "#fead663d"
+ },
+ {
+ "cursor": "#debffeff",
+ "background": "#debffeff",
+ "selection": "#debffe3d"
+ },
+ {
+ "cursor": "#95e5cbff",
+ "background": "#95e5cbff",
+ "selection": "#95e5cb3d"
+ },
+ {
+ "cursor": "#f18779ff",
+ "background": "#f18779ff",
+ "selection": "#f187793d"
+ },
+ {
+ "cursor": "#fecf72ff",
+ "background": "#fecf72ff",
+ "selection": "#fecf723d"
+ },
+ {
+ "cursor": "#d5fe80ff",
+ "background": "#d5fe80ff",
+ "selection": "#d5fe803d"
+ }
+ ],
+ "syntax": {
+ "attribute": {
+ "color": "#72cffeff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "boolean": {
+ "color": "#dfbfffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "comment": {
+ "color": "#5c6773ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "comment.doc": {
+ "color": "#9b9b99ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "constant": {
+ "color": "#dfbfffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "constructor": {
+ "color": "#72cffeff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "embedded": {
+ "color": "#cccac2ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "emphasis": {
+ "color": "#72cffeff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "emphasis.strong": {
+ "color": "#72cffeff",
+ "font_style": null,
+ "font_weight": 700
+ },
+ "enum": {
+ "color": "#fead66ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "function": {
+ "color": "#ffd173ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "hint": {
+ "color": "#7399a3ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "keyword": {
+ "color": "#ffad65ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "label": {
+ "color": "#72cffeff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "link_text": {
+ "color": "#fead66ff",
+ "font_style": "italic",
+ "font_weight": null
+ },
+ "link_uri": {
+ "color": "#d5fe80ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "namespace": {
+ "color": "#cccac2ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "number": {
+ "color": "#dfbfffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "operator": {
+ "color": "#f29e74ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "predictive": {
+ "color": "#6d839bff",
+ "font_style": "italic",
+ "font_weight": null
+ },
+ "preproc": {
+ "color": "#ffad65ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "primary": {
+ "color": "#cccac2ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "property": {
+ "color": "#72cffeff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation": {
+ "color": "#b4b3aeff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.bracket": {
+ "color": "#b4b3aeff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.delimiter": {
+ "color": "#b4b3aeff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.list_marker": {
+ "color": "#b4b3aeff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.markup": {
+ "color": "#b4b3aeff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.special": {
+ "color": "#dfbfffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "selector": {
+ "color": "#dfbfffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "selector.pseudo": {
+ "color": "#72cffeff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string": {
+ "color": "#d4fe7fff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.escape": {
+ "color": "#9b9b99ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.regex": {
+ "color": "#95e6cbff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.special": {
+ "color": "#ffdfb3ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.special.symbol": {
+ "color": "#fead66ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "tag": {
+ "color": "#72cffeff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "text.literal": {
+ "color": "#fead66ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "title": {
+ "color": "#cccac2ff",
+ "font_style": null,
+ "font_weight": 700
+ },
+ "type": {
+ "color": "#73cfffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variable": {
+ "color": "#cccac2ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variable.parameter": {
+ "color": "#dfbfffff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variant": {
+ "color": "#72cffeff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "diff.plus": {
+ "color": "#aad94cff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "diff.minus": {
+ "color": "#f07178ff",
+ "font_style": null,
+ "font_weight": null
+ }
+ }
+ }
+ }
+ ]
+}
diff --git a/vendor/themes/catppuccin_mocha.toml b/vendor/themes/catppuccin_mocha.toml
new file mode 100644
index 00000000..e4cbea7f
--- /dev/null
+++ b/vendor/themes/catppuccin_mocha.toml
@@ -0,0 +1,168 @@
+# NOTE: For contributors looking to modify the theme, please submit a pull request at https://github.com/catppuccin/helix instead of updating this file. Changes are frequently synchronized from the catppuccin/helix theme repository.
+# Syntax highlighting
+# -------------------
+"attribute" = "yellow"
+
+"type" = "yellow"
+"type.builtin" = "mauve"
+"type.enum.variant" = "teal"
+
+"constructor" = "sapphire"
+
+"constant" = "peach"
+"constant.character" = "teal"
+"constant.character.escape" = "pink"
+
+"string" = "green"
+"string.regexp" = "pink"
+"string.special" = "blue"
+"string.special.symbol" = "red"
+
+"comment" = { fg = "overlay2", modifiers = ["italic"] }
+
+"variable" = "text"
+"variable.parameter" = { fg = "maroon", modifiers = ["italic"] }
+"variable.builtin" = "red"
+"variable.other.member" = "blue"
+
+"label" = "sapphire" # used for lifetimes
+
+"punctuation" = "overlay2"
+"punctuation.special" = "sky"
+
+"keyword" = "mauve"
+"keyword.control.conditional" = { fg = "mauve", modifiers = ["italic"] }
+
+"operator" = "sky"
+
+"function" = "blue"
+"function.macro" = "rosewater"
+
+"tag" = "blue"
+
+"namespace" = { fg = "yellow", modifiers = ["italic"] }
+
+"special" = "blue" # fuzzy highlight
+
+"markup.heading.1" = "red"
+"markup.heading.2" = "peach"
+"markup.heading.3" = "yellow"
+"markup.heading.4" = "green"
+"markup.heading.5" = "sapphire"
+"markup.heading.6" = "lavender"
+"markup.list" = "teal"
+"markup.list.unchecked" = "overlay2"
+"markup.list.checked" = "green"
+"markup.bold" = { fg = "red", modifiers = ["bold"] }
+"markup.italic" = { fg = "red", modifiers = ["italic"] }
+"markup.strikethrough" = { modifiers = ["crossed_out"] }
+"markup.link.url" = { fg = "blue", modifiers = ["italic", "underlined"] }
+"markup.link.text" = "lavender"
+"markup.link.label" = "sapphire"
+"markup.raw" = "green"
+"markup.quote" = "pink"
+
+"diff.plus" = "green"
+"diff.minus" = "red"
+"diff.delta" = "blue"
+
+# User Interface
+# --------------
+"ui.background" = { fg = "text", bg = "base" }
+
+"ui.linenr" = { fg = "surface1" }
+"ui.linenr.selected" = { fg = "lavender" }
+
+"ui.statusline" = { fg = "subtext1", bg = "mantle" }
+"ui.statusline.inactive" = { fg = "surface2", bg = "mantle" }
+"ui.statusline.normal" = { fg = "base", bg = "rosewater", modifiers = ["bold"] }
+"ui.statusline.insert" = { fg = "base", bg = "green", modifiers = ["bold"] }
+"ui.statusline.select" = { fg = "base", bg = "lavender", modifiers = ["bold"] }
+
+"ui.popup" = { fg = "text", bg = "surface0" }
+"ui.window" = { fg = "crust" }
+"ui.help" = { fg = "overlay2", bg = "surface0" }
+
+"ui.bufferline" = { fg = "subtext0", bg = "mantle" }
+"ui.bufferline.active" = { fg = "mauve", bg = "base", underline = { color = "mauve", style = "line" } }
+"ui.bufferline.background" = { bg = "crust" }
+
+"ui.text" = "text"
+"ui.text.focus" = { fg = "text", bg = "surface0", modifiers = ["bold"] }
+"ui.text.inactive" = { fg = "overlay1" }
+"ui.text.directory" = { fg = "blue" }
+
+"ui.virtual" = "overlay0"
+"ui.virtual.ruler" = { bg = "surface0" }
+"ui.virtual.indent-guide" = "surface0"
+"ui.virtual.inlay-hint" = { fg = "surface1", bg = "mantle" }
+"ui.virtual.jump-label" = { fg = "rosewater", modifiers = ["bold"] }
+
+"ui.selection" = { bg = "surface1" }
+
+"ui.cursor" = { fg = "base", bg = "secondary_cursor" }
+"ui.cursor.primary" = { fg = "base", bg = "rosewater" }
+"ui.cursor.match" = { fg = "peach", modifiers = ["bold"] }
+
+"ui.cursor.primary.normal" = { fg = "base", bg = "rosewater" }
+"ui.cursor.primary.insert" = { fg = "base", bg = "green" }
+"ui.cursor.primary.select" = { fg = "base", bg = "lavender" }
+
+"ui.cursor.normal" = { fg = "base", bg = "secondary_cursor_normal" }
+"ui.cursor.insert" = { fg = "base", bg = "secondary_cursor_insert" }
+"ui.cursor.select" = { fg = "base", bg = "secondary_cursor_select" }
+
+"ui.cursorline.primary" = { bg = "cursorline" }
+
+"ui.highlight" = { bg = "surface1", modifiers = ["bold"] }
+
+"ui.menu" = { fg = "overlay2", bg = "surface0" }
+"ui.menu.selected" = { fg = "text", bg = "surface1", modifiers = ["bold"] }
+
+"diagnostic.error" = { underline = { color = "red", style = "curl" } }
+"diagnostic.warning" = { underline = { color = "yellow", style = "curl" } }
+"diagnostic.info" = { underline = { color = "sky", style = "curl" } }
+"diagnostic.hint" = { underline = { color = "teal", style = "curl" } }
+"diagnostic.unnecessary" = { modifiers = ["dim"] }
+"diagnostic.deprecated" = { modifiers = ["crossed_out"] }
+
+error = "red"
+warning = "yellow"
+info = "sky"
+hint = "teal"
+
+rainbow = ["red", "peach", "yellow", "green", "sapphire", "lavender"]
+
+[palette]
+rosewater = "#f5e0dc"
+flamingo = "#f2cdcd"
+pink = "#f5c2e7"
+mauve = "#cba6f7"
+red = "#f38ba8"
+maroon = "#eba0ac"
+peach = "#fab387"
+yellow = "#f9e2af"
+green = "#a6e3a1"
+teal = "#94e2d5"
+sky = "#89dceb"
+sapphire = "#74c7ec"
+blue = "#89b4fa"
+lavender = "#b4befe"
+text = "#cdd6f4"
+subtext1 = "#bac2de"
+subtext0 = "#a6adc8"
+overlay2 = "#9399b2"
+overlay1 = "#7f849c"
+overlay0 = "#6c7086"
+surface2 = "#585b70"
+surface1 = "#45475a"
+surface0 = "#313244"
+base = "#1e1e2e"
+mantle = "#181825"
+crust = "#11111b"
+
+cursorline = "#2a2b3c"
+secondary_cursor = "#b5a6a8"
+secondary_cursor_select = "#878ec0"
+secondary_cursor_normal = "#b5a6a8"
+secondary_cursor_insert = "#7ea87f"
diff --git a/vendor/themes/dracula.toml b/vendor/themes/dracula.toml
new file mode 100644
index 00000000..8b4abe16
--- /dev/null
+++ b/vendor/themes/dracula.toml
@@ -0,0 +1,159 @@
+# Author : Sebastian Zivota <[email protected]>
+# Author : Chirikumbrah
+
+"annotation" = { fg = "foreground" }
+
+"attribute" = { fg = "green", modifiers = ["italic"] }
+
+"comment" = { fg = "comment" }
+"comment.block" = { fg = "comment" }
+"comment.block.documentation" = { fg = "comment" }
+"comment.line" = { fg = "comment" }
+"comment.line.documentation" = { fg = "comment" }
+
+"constant" = { fg = "purple" }
+"constant.builtin" = { fg = "purple" }
+"constant.builtin.boolean" = { fg = "purple" }
+"constant.character" = { fg = "cyan" }
+"constant.character.escape" = { fg = "pink" }
+"constant.macro" = { fg = "purple" }
+"constant.numeric" = { fg = "purple" }
+"constructor" = { fg = "purple" }
+
+"definition" = { underline = { color = "cyan" } }
+
+"diagnostic" = { underline = { color = "orange", style = "curl" } }
+"diagnostic.hint" = { underline = { color = "purple", style = "curl" } }
+"diagnostic.warning" = { underline = { color = "yellow", style = "curl" } }
+"diagnostic.error" = { underline = { color = "red", style = "curl" } }
+"diagnostic.info" = { underline = { color = "cyan", style = "curl" } }
+"diagnostic.unnecessary" = { modifiers = ["dim"] }
+"diagnostic.deprecated" = { modifiers = ["crossed_out"] }
+
+"error" = { fg = "red" }
+"hint" = { fg = "purple" }
+"info" = { fg = "cyan" }
+"warning" = { fg = "yellow" }
+
+"diff.delta" = { fg = "orange" }
+"diff.minus" = { fg = "red" }
+"diff.plus" = { fg = "green" }
+
+# d
+"function" = { fg = "green" }
+"function.builtin" = { fg = "green" }
+"function.call" = { fg = "green" }
+"function.macro" = { fg = "purple" }
+"function.method" = { fg = "green" }
+
+"keyword" = { fg = "pink" }
+"keyword.control.conditional" = { fg = "pink" }
+"keyword.control.exception" = { fg = "purple" }
+"keyword.control.import" = { fg = "pink" }
+"keyword.control.repeat" = { fg = "pink" }
+"keyword.directive" = { fg = "green" }
+"keyword.function" = { fg = "pink" }
+"keyword.operator" = { fg = "pink" }
+"keyword.return" = { fg = "pink" }
+"keyword.storage" = { fg = "pink" }
+"keyword.storage.modifier" = { fg = "pink" }
+"keyword.storage.type" = { fg = "cyan", modifiers = ["italic"] }
+
+"label" = { fg = "cyan" }
+
+"markup.bold" = { fg = "orange", modifiers = ["bold"] }
+"markup.heading" = { fg = "purple", modifiers = ["bold"] }
+"markup.italic" = { fg = "yellow", modifiers = ["italic"] }
+"markup.link.text" = { fg = "pink" }
+"markup.link.url" = { fg = "cyan" }
+"markup.list" = { fg = "cyan" }
+"markup.quote" = { fg = "yellow", modifiers = ["italic"] }
+"markup.raw" = { fg = "foreground" }
+"markup.strikethrough" = { modifiers = ["crossed_out"] }
+
+"punctuation" = { fg = "foreground" }
+"punctuation.bracket" = { fg = "foreground" }
+"punctuation.delimiter" = { fg = "foreground" }
+"punctuation.special" = { fg = "pink" }
+
+"special" = { fg = "pink" }
+
+"string" = { fg = "yellow" }
+"string.regexp" = { fg = "red" }
+"string.special" = { fg = "orange" }
+"string.symbol" = { fg = "yellow" }
+
+"tag" = { fg = "pink" }
+"tag.attribute" = { fg = "purple" }
+"tag.delimiter" = { fg = "foreground" }
+
+"type" = { fg = "cyan", modifiers = ["italic"] }
+"type.builtin" = { fg = "cyan" }
+"type.enum.variant" = { fg = "foreground", modifiers = ["italic"] }
+
+"ui.background" = { fg = "foreground", bg = "background" }
+"ui.cursor" = { fg = "background", bg = "purple", modifiers = ["dim"] }
+"ui.cursor.insert" = { fg = "background", bg = "green", modifiers = ["dim"] }
+"ui.cursor.match" = { fg = "foreground", bg = "grey" }
+"ui.cursor.normal" = { fg = "background", bg = "purple", modifiers = ["dim"] }
+"ui.cursor.primary.insert" = { fg = "background", bg = "green" }
+"ui.cursor.primary.normal" = { fg = "background", bg = "purple" }
+"ui.cursor.primary.select" = { fg = "background", bg = "cyan" }
+"ui.cursor.select" = { fg = "background", bg = "cyan", modifiers = ["dim"] }
+"ui.cursorline.primary" = { bg = "cursorline" }
+"ui.debug" = { fg = "red" }
+"ui.help" = { fg = "foreground", bg = "black" }
+"ui.highlight.frameline" = { fg = "background", bg = "red" }
+"ui.linenr" = { fg = "comment" }
+"ui.linenr.selected" = { fg = "foreground" }
+"ui.menu" = { fg = "foreground", bg = "current_line" }
+"ui.menu.scroll" = { fg = "foreground", bg = "current_line" }
+"ui.menu.selected" = { fg = "current_line", bg = "purple", modifiers = ["dim"] }
+"ui.popup" = { fg = "foreground", bg = "black" }
+"ui.selection" = { bg = "selection" }
+"ui.selection.primary" = { bg = "current_line" }
+"ui.statusline" = { fg = "foreground", bg = "darker" }
+"ui.statusline.inactive" = { fg = "comment", bg = "darker" }
+"ui.statusline.insert" = { fg = "black", bg = "green", modifiers = ["bold"] }
+"ui.statusline.normal" = { fg = "black", bg = "purple", modifiers = ["bold"] }
+"ui.statusline.select" = { fg = "black", bg = "cyan", modifiers = ["bold"] }
+"ui.text" = { fg = "foreground" }
+"ui.text.focus" = { fg = "cyan" }
+"ui.text.directory" = { fg = "cyan" }
+"ui.virtual.indent-guide" = { fg = "indent" }
+"ui.virtual.inlay-hint" = { fg = "cyan" }
+"ui.virtual.inlay-hint.parameter" = { fg = "cyan", modifiers = ["italic", "dim"] }
+"ui.virtual.inlay-hint.type" = { fg = "cyan", modifiers = ["italic", "dim"] }
+"ui.virtual.jump-label" = { fg = "pink", modifiers = ["bold"] }
+"ui.virtual.ruler" = { bg = "black" }
+"ui.virtual.whitespace" = { fg = "whitespace" }
+"ui.virtual.wrap" = { fg = "current_line" }
+"ui.window" = { fg = "foreground" }
+
+"variable" = { fg = "foreground" }
+"variable.builtin" = { fg = "purple", modifiers = ["italic"] }
+"variable.other" = { fg = "foreground" }
+"variable.other.member" = { fg = "foreground" }
+"variable.parameter" = { fg = "orange", modifiers = ["italic"] }
+
+
+[palette]
+background = "#282A36"
+black = "#191A21"
+comment = "#6272A4"
+current_line = "#44475a"
+cursorline = "#2d303e"
+cyan = "#8be9fd"
+darker = "#222430"
+foreground = "#f8f8f2"
+green = "#50fa7b"
+grey = "#666771"
+indent = "#56596a"
+orange = "#ffb86c"
+pink = "#ff79c6"
+purple = "#BD93F9"
+red = "#ff5555"
+selection = "#363848"
+whitespace = "#586693"
+yellow = "#f1fa8c"
+
diff --git a/vendor/themes/everforest_light.toml b/vendor/themes/everforest_light.toml
new file mode 100644
index 00000000..312c27ab
--- /dev/null
+++ b/vendor/themes/everforest_light.toml
@@ -0,0 +1,150 @@
+# Everforest (Light Medium)
+# Authors: CptPotato, WindSoilder, basbebe
+
+# Original Author:
+# URL: https://github.com/sainnhe/everforest
+# Filename: colors/everforest.vim
+# Author: sainnhe
+# License: MIT License
+
+"type" = "yellow"
+"constant" = "fg"
+"constant.builtin" = { fg = "purple", modifiers = ["italic"] }
+"constant.builtin.boolean" = "purple"
+"constant.numeric" = "purple"
+"constant.character.escape" = "green"
+"string" = "aqua"
+"string.regexp" = "green"
+"string.special" = "yellow"
+"comment" = { fg = "grey1", modifiers = ["italic"] }
+"variable" = "fg"
+"variable.builtin" = { fg = "purple", modifiers = ["italic"] }
+"variable.parameter" = "fg"
+"variable.other.member" = "blue"
+"label" = "orange"
+"punctuation" = "grey2"
+"punctuation.delimiter" = "grey1"
+"punctuation.bracket" = "fg"
+"punctuation.special" = "blue"
+"keyword" = "red"
+"keyword.operator" = "orange"
+"keyword.directive" = "purple"
+"keyword.storage" = "red"
+"operator" = "orange"
+"function" = "green"
+"function.macro" = "green"
+"tag" = "orange"
+"namespace" = { fg = "yellow", modifiers = ["italic"] }
+"attribute" = { fg = "purple", modifiers = ["italic"] }
+"constructor" = "green"
+"module" = "yellow"
+"special" = "blue"
+
+"markup.heading.marker" = "grey1"
+"markup.heading.1" = { fg = "red", modifiers = ["bold"] }
+"markup.heading.2" = { fg = "orange", modifiers = ["bold"] }
+"markup.heading.3" = { fg = "yellow", modifiers = ["bold"] }
+"markup.heading.4" = { fg = "green", modifiers = ["bold"] }
+"markup.heading.5" = { fg = "blue", modifiers = ["bold"] }
+"markup.heading.6" = { fg = "purple", modifiers = ["bold"] }
+"markup.list" = "red"
+"markup.bold" = { modifiers = ["bold"] }
+"markup.italic" = { modifiers = ["italic"] }
+"markup.strikethrough" = { modifiers = ["crossed_out"] }
+"markup.link.url" = { fg = "blue", underline = { style = "line" } }
+"markup.link.label" = "orange"
+"markup.link.text" = "purple"
+"markup.quote" = "grey1"
+"markup.raw.inline" = "green"
+"markup.raw.block" = "aqua"
+
+"diff.plus" = "green"
+"diff.delta" = "blue"
+"diff.minus" = "red"
+
+"ui.background" = { bg = "bg0" }
+"ui.background.separator" = "grey0"
+"ui.cursor" = { fg = "bg1", bg = "grey2" }
+"ui.cursor.insert" = { fg = "bg0", bg = "grey1" }
+"ui.cursor.select" = { fg = "bg0", bg = "blue" }
+"ui.cursor.match" = { bg = "bg4", modifiers = ["bold"] }
+"ui.cursor.primary" = { fg = "bg0", bg = "fg" }
+"ui.cursorline.primary" = { bg = "bg1" }
+"ui.cursorline.secondary" = { bg = "bg2" }
+"ui.selection" = { bg = "bg3" }
+"ui.linenr" = "grey0"
+"ui.linenr.selected" = "grey2"
+"ui.statusline" = { fg = "grey2", bg = "bg3" }
+"ui.statusline.inactive" = { fg = "grey0", bg = "bg1" }
+"ui.statusline.normal" = { fg = "bg0", bg = "statusline1", modifiers = [
+ "bold",
+] }
+"ui.statusline.insert" = { fg = "bg0", bg = "statusline2", modifiers = [
+ "bold",
+] }
+"ui.statusline.select" = { fg = "bg0", bg = "blue", modifiers = ["bold"] }
+"ui.bufferline" = { fg = "grey2", bg = "bg3" }
+"ui.bufferline.active" = { fg = "bg0", bg = "statusline1", modifiers = [
+ "bold",
+] }
+"ui.popup" = { fg = "grey2", bg = "bg2" }
+"ui.picker.header" = { modifiers = ["bold", "underlined"] }
+"ui.window" = { fg = "bg4", bg = "bg_dim" }
+"ui.help" = { fg = "fg", bg = "bg2" }
+"ui.text" = "fg"
+"ui.text.directory" = { fg = "green" }
+"ui.text.focus" = "fg"
+"ui.menu" = { fg = "fg", bg = "bg3" }
+"ui.menu.selected" = { fg = "bg0", bg = "green" }
+"ui.virtual.ruler" = { bg = "bg3" }
+"ui.virtual.whitespace" = { fg = "bg4" }
+"ui.virtual.indent-guide" = { fg = "bg4" }
+"ui.virtual.inlay-hint" = { fg = "grey0" }
+"ui.virtual.wrap" = { fg = "grey0" }
+"ui.virtual.jump-label" = { fg = "red", bg = "bg0", modifiers = [
+ "bold",
+] }
+
+"hint" = "green"
+"info" = "blue"
+"warning" = "yellow"
+"error" = "red"
+
+"diagnostic.hint" = { underline = { color = "green", style = "curl" } }
+"diagnostic.info" = { underline = { color = "blue", style = "curl" } }
+"diagnostic.warning" = { underline = { color = "yellow", style = "curl" } }
+"diagnostic.error" = { underline = { color = "red", style = "curl" } }
+"diagnostic.unnecessary" = { modifiers = ["dim"] }
+"diagnostic.deprecated" = { modifiers = ["crossed_out"] }
+
+[palette]
+
+bg_dim = "#efebd4"
+bg0 = "#fdf6e3"
+bg1 = "#f4f0d9"
+bg2 = "#efebd4"
+bg3 = "#e6e2cc"
+bg4 = "#e0dcc7"
+bg5 = "#bdc3af"
+bg_red = "#fbe3da"
+bg_visual = "#eaedc8"
+bg_yellow = "#faedcd"
+bg_green = "#f0f1d2"
+bg_blue = "#e9f0e9"
+
+fg = "#5c6a72"
+red = "#f85552"
+orange = "#f57d26"
+yellow = "#dfa000"
+green = "#8da101"
+blue = "#3a94c5"
+aqua = "#35a77c"
+purple = "#df69ba"
+
+grey0 = "#a6b0a0"
+grey1 = "#939f91"
+grey2 = "#829181"
+statusline1 = "#93b259"
+statusline2 = "#708089"
+statusline3 = "#e66868"
diff --git a/vendor/themes/gruvbox.toml b/vendor/themes/gruvbox.toml
new file mode 100644
index 00000000..6c5bcd09
--- /dev/null
+++ b/vendor/themes/gruvbox.toml
@@ -0,0 +1,153 @@
+# Author : Jakub Bartodziej <[email protected]>
+# The theme uses the gruvbox dark palette with standard contrast: github.com/morhetz/gruvbox
+
+"annotation" = { fg = "fg1" }
+
+"attribute" = { fg = "aqua1", modifiers = ["italic"] }
+
+"comment" = { fg = "gray", modifiers = ["italic"] }
+
+"constant" = { fg = "purple1" }
+"constant.character" = { fg = "aqua1" }
+"constant.character.escape" = { fg = "orange1" }
+"constant.macro" = { fg = "aqua1" }
+"constructor" = { fg = "purple1" }
+
+"definition" = { underline = { color = "aqua1" } }
+
+"diagnostic" = { underline = { color = "orange1", style = "curl" } }
+"diagnostic.deprecated" = { modifiers = ["crossed_out"] }
+"diagnostic.error" = { underline = { color = "red1", style = "curl" } }
+"diagnostic.hint" = { underline = { color = "blue1", style = "curl" } }
+"diagnostic.info" = { underline = { color = "aqua1", style = "curl" } }
+"diagnostic.warning" = { underline = { color = "yellow1", style = "curl" } }
+# "diagnostic.unnecessary" = { modifiers = ["dim"] } # do not remove this for future resolving
+
+"error" = { fg = "red1" }
+"hint" = { fg = "blue1" }
+"info" = { fg = "aqua1" }
+"warning" = { fg = "yellow1" }
+
+"diff.delta" = { fg = "yellow1" }
+"diff.minus" = { fg = "red1" }
+"diff.plus" = { fg = "green1" }
+
+"function" = { fg = "green1" }
+"function.builtin" = { fg = "yellow1" }
+"function.macro" = { fg = "blue1" }
+
+"keyword" = { fg = "red1" }
+"keyword.control.import" = { fg = "aqua1" }
+
+"label" = { fg = "red1" }
+
+"markup.bold" = { modifiers = ["bold"] }
+"markup.heading" = "aqua1"
+"markup.italic" = { modifiers = ["italic"] }
+"markup.link.text" = "red1"
+"markup.link.url" = { fg = "green1", modifiers = ["underlined"] }
+"markup.raw" = "red1"
+"markup.strikethrough" = { modifiers = ["crossed_out"] }
+
+"module" = { fg = "aqua1" }
+
+"namespace" = { fg = "fg1" }
+
+"operator" = { fg = "purple1" }
+
+"punctuation" = { fg = "orange1" }
+
+"special" = { fg = "purple0" }
+
+"string" = { fg = "green1" }
+"string.regexp" = { fg = "orange1" }
+"string.special" = { fg = "orange1" }
+"string.symbol" = { fg = "yellow1" }
+
+"tag" = { fg = "aqua1" }
+
+"type" = { fg = "yellow1" }
+"type.enum.variant" = { modifiers = ["italic"] }
+
+"ui.background" = { bg = "bg0" }
+"ui.bufferline" = { fg = "fg1", bg = "bg1" }
+"ui.bufferline.active" = { fg = "bg0", bg = "yellow0" }
+"ui.bufferline.background" = { bg = "bg2" }
+
+"ui.cursor" = { fg = "bg1", bg = "bg2" }
+"ui.cursor.insert" = { fg = "bg1", bg = "blue0" }
+"ui.cursor.normal" = { fg = "bg1", bg = "gray" }
+"ui.cursor.select" = { fg = "bg1", bg = "orange0" }
+"ui.cursor.match" = { fg = "fg3", bg = "bg3" }
+
+"ui.cursor.primary" = { bg = "fg3", fg = "bg1" }
+"ui.cursor.primary.insert" = { fg = "bg1", bg = "blue1" }
+"ui.cursor.primary.normal" = { fg = "bg1", bg = "fg3" }
+"ui.cursor.primary.select" = { fg = "bg1", bg = "orange1" }
+
+"ui.cursorline" = { bg = "bg0_s" }
+"ui.cursorline.primary" = { bg = "bg1" }
+
+"ui.help" = { bg = "bg1", fg = "fg1" }
+"ui.linenr" = { fg = "bg3" }
+"ui.linenr.selected" = { fg = "yellow1" }
+"ui.menu" = { fg = "fg1", bg = "bg2" }
+"ui.menu.selected" = { fg = "green1", bg = "bg1", modifiers = ["bold"] }
+"ui.popup" = { bg = "bg1" }
+"ui.picker.header.column" = { underline.style = "line" }
+"ui.picker.header.column.active" = { modifiers = ["bold"], underline.style = "line" }
+"ui.selection" = { bg = "bg2" }
+"ui.selection.primary" = { bg = "bg3" }
+
+"ui.statusline" = { fg = "fg1", bg = "bg2" }
+"ui.statusline.inactive" = { fg = "fg4", bg = "bg2" }
+"ui.statusline.insert" = { fg = "bg1", bg = "blue1", modifiers = ["bold"] }
+"ui.statusline.normal" = { fg = "bg1", bg = "fg3", modifiers = ["bold"] }
+"ui.statusline.select" = { fg = "bg1", bg = "orange1", modifiers = ["bold"] }
+
+"ui.text" = { fg = "fg1" }
+"ui.text.focus" = { fg = "green1", bg="bg1" }
+"ui.text.directory" = { fg = "blue1" }
+"ui.virtual.inlay-hint" = { fg = "gray" }
+"ui.virtual.jump-label" = { fg = "purple0", modifiers = ["bold"] }
+"ui.virtual.ruler" = { bg = "bg1" }
+"ui.virtual.whitespace" = "bg2"
+"ui.virtual.wrap" = { fg = "bg2" }
+"ui.window" = { bg = "bg1" }
+
+"variable" = { fg = "fg1" }
+"variable.builtin" = { fg = "orange1", modifiers = ["italic"] }
+"variable.other.member" = { fg = "blue1" }
+"variable.parameter" = { fg = "blue1", modifiers = ["italic"] }
+
+
+[palette]
+bg0 = "#282828" # main background
+bg0_s = "#32302f"
+bg1 = "#3c3836"
+bg2 = "#504945"
+bg3 = "#665c54"
+bg4 = "#7c6f64"
+
+fg0 = "#fbf1c7"
+fg1 = "#ebdbb2" # main foreground
+fg2 = "#d5c4a1"
+fg3 = "#bdae93"
+fg4 = "#a89984"
+
+gray = "#928374"
+
+red0 = "#cc241d" # neutral
+red1 = "#fb4934" # bright
+green0 = "#98971a"
+green1 = "#b8bb26"
+yellow0 = "#d79921"
+yellow1 = "#fabd2f"
+blue0 = "#458588"
+blue1 = "#83a598"
+purple0 = "#b16286"
+purple1 = "#d3869b"
+aqua0 = "#689d6a"
+aqua1 = "#8ec07c"
+orange0 = "#d65d0e"
+orange1 = "#fe8019"
diff --git a/vendor/themes/helix.LICENSE b/vendor/themes/helix.LICENSE
new file mode 100644
index 00000000..a612ad98
--- /dev/null
+++ b/vendor/themes/helix.LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/vendor/themes/monokai.toml b/vendor/themes/monokai.toml
new file mode 100644
index 00000000..dfa7040c
--- /dev/null
+++ b/vendor/themes/monokai.toml
@@ -0,0 +1,121 @@
+# Author: Shafkath Shuhan <[email protected]>
+
+"tag" = { fg = "tag" }
+
+"namespace" = { fg = "type" }
+"module" = { fg = "type" }
+
+"type" = { fg = "type" }
+"type.builtin" = { fg = "#66D9EF" }
+"type.enum.variant" = { fg = "text" }
+"constructor" = { fg = "fn_declaration" }
+"variable.other.member" = { fg = "variable" }
+
+"keyword" = { fg = "keyword" }
+"keyword.directive" = { fg = "keyword" }
+"keyword.control" = { fg = "keyword" }
+"label" = { fg = "keyword" }
+
+"special" = { fg = "keyword" }
+"operator" = { fg = "text" }
+
+"punctuation" = { fg = "text" }
+"punctuation.delimiter" = { fg = "text" }
+
+"variable" = { fg = "variable" }
+"variable.parameter" = { fg = "#fd971f" }
+"variable.builtin" = { fg = "keyword" }
+"constant" = { fg = "variable" }
+"constant.builtin" = { fg = "#ae81ff" }
+
+"function" = { fg = "fn_declaration" }
+"function.builtin" = { fg = "fn_declaration" }
+"function.macro" = { fg = "keyword" }
+"attribute" = { fg = "fn_declaration" }
+
+"comment" = { fg = "#88846F" }
+"ui.virtual.whitespace" = "#88846F"
+"ui.virtual.ruler" = { bg = "#24241e" }
+"ui.virtual.jump-label" = { fg = "special", modifiers = ["bold"] }
+
+"string" = { fg = "#e6db74" }
+"constant.character" = { fg = "#e6db74" }
+"string.regexp" = { fg = "regex" }
+"constant.numeric" = { fg = "#ae81ff" }
+"constant.character.escape" = { fg = "#ae81ff" }
+
+# TODO
+"markup.heading" = "blue"
+"markup.list" = "red"
+"markup.bold" = { fg = "yellow", modifiers = ["bold"] }
+"markup.italic" = { fg = "magenta", modifiers = ["italic"] }
+"markup.strikethrough" = { modifiers = ["crossed_out"] }
+"markup.link.url" = { fg = "yellow", modifiers = ["underlined"] }
+"markup.link.text" = "red"
+"markup.quote" = "cyan"
+"markup.raw" = "green"
+
+"diff.plus" = { fg = "#a6e22e" }
+"diff.delta" = { fg = "#fd971f" }
+"diff.minus" = { fg = "#f92672" }
+
+"ui.background" = { fg = "text", bg = "background" }
+
+"ui.window" = { bg = "widget" }
+"ui.popup" = { fg = "text", bg = "widget" }
+"ui.help" = { fg = "text", bg = "widget" }
+"ui.menu" = { fg = "text", bg = "widget" }
+"ui.menu.selected" = { bg = "#414339" }
+
+"ui.cursor" = { fg = "cursor", modifiers = ["reversed"] }
+"ui.cursor.primary" = { fg = "cursor", modifiers = ["reversed"] }
+"ui.cursor.match" = { fg = "#888888", modifiers = ["reversed"] }
+
+"ui.selection" = { bg = "#878b91" }
+"ui.selection.primary" = { bg = "#575b61" }
+
+"ui.linenr" = { fg = "#90908a" }
+"ui.linenr.selected" = { fg = "#c2c2bf" }
+
+"ui.cursorline" = { bg = "#24241e" }
+"ui.statusline" = { fg = "active_text", bg = "#414339" }
+"ui.statusline.inactive" = { fg = "active_text", bg = "#75715e" }
+
+# Malformed ANSI: grey2, bg3. See 'https://github.com/helix-editor/helix/issues/5709'
+# "ui.bufferline" = { fg = "grey2", bg = "bg3" }
+"ui.bufferline.active" = { fg = "active_text", bg = "selection", modifiers = [
+ "bold",
+] }
+"ui.virtual.inlay-hint" = { fg = "#88846F" }
+
+"ui.text" = { fg = "text" }
+"ui.text.focus" = { fg = "active_text" }
+
+"warning" = { fg = "#cca700" }
+"error" = { fg = "#f48771" }
+"info" = { fg = "#75beff" }
+"hint" = { fg = "#eeeeb3" }
+
+"diagnostic.warning" = { underline = { color = "#cca700", style = "curl" } }
+"diagnostic.error" = { underline = { color = "#f48771", style = "curl" } }
+"diagnostic.info" = { underline = { color = "#75beff", style = "curl" } }
+"diagnostic.hint" = { underline = { color = "#eeeeb3", style = "curl" } }
+"diagnostic.unnecessary" = { modifiers = ["dim"] }
+"diagnostic.deprecated" = { modifiers = ["crossed_out"] }
+
+[palette]
+type = "#A6E22E"
+keyword = "#F92672"
+regex = "#CE9178"
+special = "#C586C0"
+variable = "#F8F8F2"
+fn_declaration = "#A6E22E"
+
+background = "#272822"
+text = "#f8f8f2"
+active_text = "#ffffff"
+cursor = "#a6a6a6"
+inactive_cursor = "#878b91"
+widget = "#1e1f1c"
+selection = "#414339"
+tag = "#F92672"
diff --git a/vendor/themes/nord.toml b/vendor/themes/nord.toml
new file mode 100644
index 00000000..db7b6190
--- /dev/null
+++ b/vendor/themes/nord.toml
@@ -0,0 +1,199 @@
+# Nord (Dark Ambiance) port for Helix (https://helix-editor.com/)
+# https://docs.helix-editor.com/themes.html
+# https://www.nordtheme.com/docs/colors-and-palettes
+
+## SYNTAX HIGHLIGHTING
+
+# Constants
+"constant" = "nord4"
+"constant.builtin" = "nord9"
+"constant.builtin.boolean" = "nord9"
+"constant.builtin.character" = "nord15"
+"constant.character.escape" = "nord13"
+"constant.macro" = "nord9"
+"constant.numeric" = "nord15"
+"constructor" = "nord8"
+
+# Diagnostics
+"diagnostic" = { underline = { color = "nord13", style = "curl" } }
+"diagnostic.error" = { underline = { color = "nord11", style = "curl" } }
+"error" = "nord11"
+"diagnostic.hint" = { underline = { color = "nord10", style = "curl" } }
+"hint" = "nord10"
+"diagnostic.info" = { underline = { color = "nord8", style = "curl" } }
+"info" = "nord8"
+"diagnostic.warning" = { underline = { color = "nord13", style = "curl" } }
+"warning" = "nord13"
+"diagnostic.unnecessary" = { modifiers = ["dim"] }
+"diagnostic.deprecated" = { modifiers = ["crossed_out"] }
+
+# Diffs
+"diff.delta" = "nord13"
+"diff.minus" = "nord11"
+"diff.plus" = "nord14"
+
+# Functions
+"function" = "nord8"
+"function.builtin" = "nord7"
+"function.method" = "nord8"
+"function.macro" = "nord9"
+"function.special" = "nord9"
+
+# Git
+"git.delta.moved" = "nord12"
+
+# Keywords
+"keyword" = "nord9"
+"keyword.control.conditional" = "nord9"
+"keyword.control.exception" = "nord9"
+"keyword.control.repeat" = "nord9"
+"keyword.directive" = "nord9"
+"keyword.function" = "nord9"
+"keyword.operator" = "nord9"
+"keyword.return" = "nord9"
+"keyword.storage.modifier" = "nord9"
+"keyword.storage.type" = "nord9"
+
+# Punctuation
+"punctuation" = "nord6"
+"punctuation.bracket" = "nord6"
+"punctuation.delimiter" = "nord6"
+"punctuation.special" = "nord9"
+
+# Strings
+"string" = "nord14"
+"string.escape" = "nord13"
+"string.regex" = "nord13"
+"string.special" = "nord13"
+
+# Types
+"type" = "nord7"
+"type.builtin" = "nord7"
+
+# Variables
+"variable" = "nord4"
+"variable.builtin" = "nord9"
+"variable.other.member" = "nord4"
+"variable.parameter" = "nord8"
+"attribute" = "nord9"
+
+# Misc.
+"label" = "nord7"
+"namespace" = "nord4"
+"operator" = "nord9"
+"special" = "nord9"
+"tag" = "nord7"
+"comment" = { fg = "nord3_bright", modifiers = ["italic"] }
+
+## EDITOR UI COLORS
+
+"ui.background" = { bg = "nord0" }
+"ui.text" = "nord4"
+"ui.window" = "nord1"
+
+# Debug
+"ui.debug.active" = "nord13"
+"ui.debug.breakpoint" = "nord11"
+
+# Popus and menus
+"ui.menu" = { bg = "nord1" }
+"ui.menu.scroll" = { fg = "nord4", bg = "nord3" }
+"ui.menu.selected" = { fg = "nord8", bg = "nord2" }
+"ui.popup" = { bg = "nord1" }
+"ui.popup.info" = { bg = "nord1" }
+"ui.help" = { bg = "nord1" }
+"ui.text.focus" = { fg = "nord8", bg = "nord2" }
+
+# Gutter
+"ui.gutter" = "nord5"
+"ui.linenr" = "nord3"
+"ui.linenr.selected" = "nord5"
+
+# Cursor
+"ui.cursor" = { fg = "nord4", modifiers = ["reversed"] }
+"ui.cursorcolumn.primary" = { bg = "nord1" }
+"ui.cursorline.primary" = { bg = "nord1" }
+"ui.cursor.match" = { bg = "nord3" }
+
+"ui.selection" = { bg = "nord3" }
+"ui.highlight" = { fg = "nord8", bg = "nord2" }
+
+# Statusline
+"ui.statusline" = { bg = "nord1" }
+"ui.statusline.inactive" = { fg = "nord8", bg = "nord1" }
+"ui.statusline.insert" = { fg = "nord1", bg = "nord6" }
+"ui.statusline.normal" = { fg = "nord1", bg = "nord8" }
+"ui.statusline.select" = { fg = "nord1", bg = "nord7" }
+"ui.statusline.separator" = "nord3"
+
+# Virtual/invisible text
+"ui.virtual.indent-guide" = "nord3"
+"ui.virtual.inlay-hint" = { fg = "nord3", modifiers = ["italic"] }
+"ui.virtual.jump-label" = { fg = "nord11", modifiers = ["bold"] }
+"ui.virtual.ruler" = { bg = "nord1" }
+"ui.virtual.whitespace" = "nord3"
+"ui.virtual.wrap" = "nord3"
+
+# Bufferline
+"ui.bufferline" = { fg = "nord5", bg = "nord1" }
+"ui.bufferline.active" = { fg = "nord6", bg = "nord2", underline = { color = "nord8", style = "line" }, modifiers = [
+ "italic",
+] }
+
+# Markup
+"markup.heading" = "nord8"
+"markup.list" = "nord9"
+"markup.bold" = { modifiers = ["bold"] }
+"markup.italic" = { modifiers = ["italic"] }
+"markup.strikethrough" = { modifiers = ["crossed_out"] }
+"markup.link.text" = "nord8"
+"markup.raw" = "nord7"
+
+# Rainbow brackets
+rainbow = ["nord13", "nord15", "nord14", "nord12"]
+
+[palette]
+# Polar Night is made up of four darker colors that are commonly used for base elements like backgrounds or text color in bright ambiance designs.
+#
+# The origin color or the Polar Night palette
+nord0 = "#2e3440"
+# A brighter shade color based on nord0
+nord1 = "#3B4252"
+# An even more brighter shade color of nord0
+nord2 = "#434C5E"
+# The brightest shade color based on nord0
+nord3 = "#4C566A"
+# 10% brighter for comments, see https://github.com/nordtheme/nord/issues/94
+nord3_bright = "#616e88"
+
+# Snow Storm is made up of three bright colors that are commonly used for text colors or base UI elements in bright ambiance designs.
+# The origin color or the Snow Storm palette
+nord4 = "#D8DEE9"
+# A brighter shade color of nord4
+nord5 = "#E5E9F0"
+# The brightest shade color based on nord4
+nord6 = "#ECEFF4"
+
+# Frost can be described as the heart palette of Nord, a group of four bluish colors that are commonly used for primary UI component and text highlighting and essential code syntax elements.
+#
+# A calm and highly contrasted color reminiscent of frozen polar water
+nord7 = "#8FBCBB"
+# The bright and shiny primary accent color reminiscent of pure and clear ice
+nord8 = "#88C0D0"
+# A more darkened and less saturated color reminiscent of arctic waters
+nord9 = "#81A1C1"
+# A dark and intensive color reminiscent of the deep arctic ocean
+nord10 = "#5E81AC"
+
+# Aurora consists of five colorful components reminiscent of the "Aurora borealis", sometimes referred to as polar lights or northern lights.
+#
+# Red
+nord11 = "#BF616A"
+# Orange
+nord12 = "#D08770"
+# Yellow
+nord13 = "#EBCB8B"
+# Green
+nord14 = "#A3BE8C"
+# Purple
+nord15 = "#B48EAD"
diff --git a/vendor/themes/one.LICENSE b/vendor/themes/one.LICENSE
new file mode 100644
index 00000000..f7637d33
--- /dev/null
+++ b/vendor/themes/one.LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 GitHub Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/themes/one.json b/vendor/themes/one.json
new file mode 100644
index 00000000..698b8feb
--- /dev/null
+++ b/vendor/themes/one.json
@@ -0,0 +1,843 @@
+{
+ "$schema": "https://zed.dev/schema/themes/v0.2.0.json",
+ "name": "One",
+ "author": "Zed Industries",
+ "themes": [
+ {
+ "name": "One Dark",
+ "appearance": "dark",
+ "style": {
+ "border": "#464b57ff",
+ "border.variant": "#363c46ff",
+ "border.focused": "#47679eff",
+ "border.selected": "#293b5bff",
+ "border.transparent": "#00000000",
+ "border.disabled": "#414754ff",
+ "elevated_surface.background": "#2f343eff",
+ "surface.background": "#2f343eff",
+ "background": "#3b414dff",
+ "element.background": "#2e343eff",
+ "element.hover": "#363c46ff",
+ "element.active": "#454a56ff",
+ "element.selected": "#454a56ff",
+ "element.disabled": "#2e343eff",
+ "drop_target.background": "#83899480",
+ "ghost_element.background": "#00000000",
+ "ghost_element.hover": "#363c46ff",
+ "ghost_element.active": "#454a56ff",
+ "ghost_element.selected": "#454a56ff",
+ "ghost_element.disabled": "#2e343eff",
+ "text": "#dce0e5ff",
+ "text.muted": "#a9afbcff",
+ "text.placeholder": "#878a98ff",
+ "text.disabled": "#878a98ff",
+ "text.accent": "#74ade8ff",
+ "icon": "#dce0e5ff",
+ "icon.muted": "#a9afbcff",
+ "icon.disabled": "#878a98ff",
+ "icon.placeholder": "#a9afbcff",
+ "icon.accent": "#74ade8ff",
+ "status_bar.background": "#3b414dff",
+ "title_bar.background": "#3b414dff",
+ "title_bar.inactive_background": "#2e343eff",
+ "toolbar.background": "#282c33ff",
+ "tab_bar.background": "#2f343eff",
+ "tab.inactive_background": "#2f343eff",
+ "tab.active_background": "#282c33ff",
+ "search.match_background": "#74ade866",
+ "search.active_match_background": "#e8af7466",
+ "panel.background": "#2f343eff",
+ "panel.focused_border": null,
+ "pane.focused_border": null,
+ "scrollbar.thumb.background": "#c8ccd44c",
+ "scrollbar.thumb.hover_background": "#363c46ff",
+ "scrollbar.thumb.border": "#363c46ff",
+ "scrollbar.track.background": "#00000000",
+ "scrollbar.track.border": "#2e333cff",
+ "editor.foreground": "#acb2beff",
+ "editor.background": "#282c33ff",
+ "editor.gutter.background": "#282c33ff",
+ "editor.subheader.background": "#2f343eff",
+ "editor.active_line.background": "#2f343ebf",
+ "editor.highlighted_line.background": "#2f343eff",
+ "editor.line_number": "#4e5a5f",
+ "editor.active_line_number": "#d0d4da",
+ "editor.hover_line_number": "#acb0b4",
+ "editor.invisible": "#4e5a5f",
+ "editor.wrap_guide": "#c8ccd40d",
+ "editor.active_wrap_guide": "#c8ccd41a",
+ "editor.document_highlight.read_background": "#74ade81a",
+ "editor.document_highlight.write_background": "#555a6366",
+ "terminal.background": "#282c34ff",
+ "terminal.foreground": "#abb2bfff",
+ "terminal.bright_foreground": "#dce0e5ff",
+ "terminal.dim_foreground": "#636d83ff",
+ "terminal.ansi.black": "#282c34ff",
+ "terminal.ansi.bright_black": "#636d83ff",
+ "terminal.ansi.dim_black": "#3b3f4aff",
+ "terminal.ansi.red": "#e06c75ff",
+ "terminal.ansi.bright_red": "#EA858Bff",
+ "terminal.ansi.dim_red": "#a7545aff",
+ "terminal.ansi.green": "#98c379ff",
+ "terminal.ansi.bright_green": "#AAD581ff",
+ "terminal.ansi.dim_green": "#6d8f59ff",
+ "terminal.ansi.yellow": "#e5c07bff",
+ "terminal.ansi.bright_yellow": "#FFD885ff",
+ "terminal.ansi.dim_yellow": "#b8985bff",
+ "terminal.ansi.blue": "#61afefff",
+ "terminal.ansi.bright_blue": "#85C1FFff",
+ "terminal.ansi.dim_blue": "#457cadff",
+ "terminal.ansi.magenta": "#c678ddff",
+ "terminal.ansi.bright_magenta": "#D398EBff",
+ "terminal.ansi.dim_magenta": "#8d54a0ff",
+ "terminal.ansi.cyan": "#56b6c2ff",
+ "terminal.ansi.bright_cyan": "#6ED5DEff",
+ "terminal.ansi.dim_cyan": "#3c818aff",
+ "terminal.ansi.white": "#abb2bfff",
+ "terminal.ansi.bright_white": "#fafafaff",
+ "terminal.ansi.dim_white": "#8f969bff",
+ "link_text.hover": "#74ade8ff",
+ "version_control.added": "#27a657ff",
+ "version_control.modified": "#d3b020ff",
+ "version_control.word_added": "#2EA04859",
+ "version_control.word_deleted": "#78081BCC",
+ "version_control.deleted": "#e06c76ff",
+ "version_control.conflict_marker.ours": "#a1c1811a",
+ "version_control.conflict_marker.theirs": "#74ade81a",
+ "conflict": "#dec184ff",
+ "conflict.background": "#dec1841a",
+ "conflict.border": "#5d4c2fff",
+ "created": "#a1c181ff",
+ "created.background": "#a1c1811a",
+ "created.border": "#38482fff",
+ "deleted": "#d07277ff",
+ "deleted.background": "#d072771a",
+ "deleted.border": "#4c2b2cff",
+ "error": "#d07277ff",
+ "error.background": "#d072771a",
+ "error.border": "#4c2b2cff",
+ "hidden": "#878a98ff",
+ "hidden.background": "#696b771a",
+ "hidden.border": "#414754ff",
+ "hint": "#788ca6ff",
+ "hint.background": "#5a6f891a",
+ "hint.border": "#293b5bff",
+ "ignored": "#878a98ff",
+ "ignored.background": "#696b771a",
+ "ignored.border": "#464b57ff",
+ "info": "#74ade8ff",
+ "info.background": "#74ade81a",
+ "info.border": "#293b5bff",
+ "modified": "#dec184ff",
+ "modified.background": "#dec1841a",
+ "modified.border": "#5d4c2fff",
+ "predictive": "#5a6a87ff",
+ "predictive.background": "#5a6a871a",
+ "predictive.border": "#38482fff",
+ "renamed": "#74ade8ff",
+ "renamed.background": "#74ade81a",
+ "renamed.border": "#293b5bff",
+ "success": "#a1c181ff",
+ "success.background": "#a1c1811a",
+ "success.border": "#38482fff",
+ "unreachable": "#a9afbcff",
+ "unreachable.background": "#8389941a",
+ "unreachable.border": "#464b57ff",
+ "warning": "#dec184ff",
+ "warning.background": "#dec1841a",
+ "warning.border": "#5d4c2fff",
+ "players": [
+ {
+ "cursor": "#74ade8ff",
+ "background": "#74ade8ff",
+ "selection": "#74ade83d"
+ },
+ {
+ "cursor": "#be5046ff",
+ "background": "#be5046ff",
+ "selection": "#be50463d"
+ },
+ {
+ "cursor": "#bf956aff",
+ "background": "#bf956aff",
+ "selection": "#bf956a3d"
+ },
+ {
+ "cursor": "#b477cfff",
+ "background": "#b477cfff",
+ "selection": "#b477cf3d"
+ },
+ {
+ "cursor": "#6eb4bfff",
+ "background": "#6eb4bfff",
+ "selection": "#6eb4bf3d"
+ },
+ {
+ "cursor": "#d07277ff",
+ "background": "#d07277ff",
+ "selection": "#d072773d"
+ },
+ {
+ "cursor": "#dec184ff",
+ "background": "#dec184ff",
+ "selection": "#dec1843d"
+ },
+ {
+ "cursor": "#a1c181ff",
+ "background": "#a1c181ff",
+ "selection": "#a1c1813d"
+ }
+ ],
+ "syntax": {
+ "attribute": {
+ "color": "#74ade8ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "boolean": {
+ "color": "#bf956aff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "comment": {
+ "color": "#5d636fff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "comment.doc": {
+ "color": "#878e98ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "constant": {
+ "color": "#dfc184ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "constructor": {
+ "color": "#73ade9ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "embedded": {
+ "color": "#dce0e5ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "emphasis": {
+ "color": "#74ade8ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "emphasis.strong": {
+ "color": "#bf956aff",
+ "font_style": null,
+ "font_weight": 700
+ },
+ "enum": {
+ "color": "#6eb4bfff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "function": {
+ "color": "#73ade9ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "hint": {
+ "color": "#788ca6ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "keyword": {
+ "color": "#b477cfff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "label": {
+ "color": "#74ade8ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "link_text": {
+ "color": "#73ade9ff",
+ "font_style": "italic",
+ "font_weight": null
+ },
+ "link_uri": {
+ "color": "#6eb4bfff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "namespace": {
+ "color": "#dce0e5ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "number": {
+ "color": "#bf956aff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "operator": {
+ "color": "#6eb4bfff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "predictive": {
+ "color": "#5a6a87ff",
+ "font_style": "italic",
+ "font_weight": null
+ },
+ "preproc": {
+ "color": "#b477cfff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "primary": {
+ "color": "#acb2beff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "property": {
+ "color": "#d07277ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation": {
+ "color": "#acb2beff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.bracket": {
+ "color": "#b2b9c6ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.delimiter": {
+ "color": "#b2b9c6ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.list_marker": {
+ "color": "#d07277ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.markup": {
+ "color": "#d07277ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.special": {
+ "color": "#b1574bff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "selector": {
+ "color": "#dfc184ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "selector.pseudo": {
+ "color": "#74ade8ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string": {
+ "color": "#a1c181ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.escape": {
+ "color": "#878e98ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.regex": {
+ "color": "#bf956aff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.special": {
+ "color": "#bf956aff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.special.symbol": {
+ "color": "#bf956aff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "tag": {
+ "color": "#74ade8ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "text.literal": {
+ "color": "#a1c181ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "title": {
+ "color": "#d07277ff",
+ "font_style": null,
+ "font_weight": 400
+ },
+ "type": {
+ "color": "#6eb4bfff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variable": {
+ "color": "#acb2beff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variable.parameter": {
+ "color": "#d07277ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variable.special": {
+ "color": "#bf956aff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variant": {
+ "color": "#73ade9ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "diff.plus": {
+ "color": "#98c379ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "diff.minus": {
+ "color": "#e06c75ff",
+ "font_style": null,
+ "font_weight": null
+ }
+ }
+ }
+ },
+ {
+ "name": "One Light",
+ "appearance": "light",
+ "style": {
+ "border": "#c9c9caff",
+ "border.variant": "#dfdfe0ff",
+ "border.focused": "#7d82e8ff",
+ "border.selected": "#cbcdf6ff",
+ "border.transparent": "#00000000",
+ "border.disabled": "#d3d3d4ff",
+ "elevated_surface.background": "#ebebecff",
+ "surface.background": "#ebebecff",
+ "background": "#dcdcddff",
+ "element.background": "#ebebecff",
+ "element.hover": "#dfdfe0ff",
+ "element.active": "#cacacaff",
+ "element.selected": "#cacacaff",
+ "element.disabled": "#ebebecff",
+ "drop_target.background": "#7e808780",
+ "ghost_element.background": "#00000000",
+ "ghost_element.hover": "#dfdfe0ff",
+ "ghost_element.active": "#cacacaff",
+ "ghost_element.selected": "#cacacaff",
+ "ghost_element.disabled": "#ebebecff",
+ "text": "#242529ff",
+ "text.muted": "#58585aff",
+ "text.placeholder": "#7e8086ff",
+ "text.disabled": "#7e8086ff",
+ "text.accent": "#5c78e2ff",
+ "icon": "#242529ff",
+ "icon.muted": "#58585aff",
+ "icon.disabled": "#7e8086ff",
+ "icon.placeholder": "#58585aff",
+ "icon.accent": "#5c78e2ff",
+ "status_bar.background": "#dcdcddff",
+ "title_bar.background": "#dcdcddff",
+ "title_bar.inactive_background": "#ebebecff",
+ "toolbar.background": "#fafafaff",
+ "tab_bar.background": "#ebebecff",
+ "tab.inactive_background": "#ebebecff",
+ "tab.active_background": "#fafafaff",
+ "search.match_background": "#5c79e266",
+ "search.active_match_background": "#d0a92366",
+ "panel.background": "#ebebecff",
+ "panel.focused_border": null,
+ "pane.focused_border": null,
+ "scrollbar.thumb.background": "#383a414c",
+ "scrollbar.thumb.hover_background": "#dfdfe0ff",
+ "scrollbar.thumb.border": "#dfdfe0ff",
+ "scrollbar.track.background": "#00000000",
+ "scrollbar.track.border": "#eeeeeeff",
+ "editor.foreground": "#242529ff",
+ "editor.background": "#fafafaff",
+ "editor.gutter.background": "#fafafaff",
+ "editor.subheader.background": "#ebebecff",
+ "editor.active_line.background": "#ebebecbf",
+ "editor.highlighted_line.background": "#ebebecff",
+ "editor.line_number": "#b4b4bb",
+ "editor.active_line_number": "#44454b",
+ "editor.hover_line_number": "#61616b",
+ "editor.invisible": "#b4b4bb",
+ "editor.wrap_guide": "#383a410d",
+ "editor.active_wrap_guide": "#383a411a",
+ "editor.document_highlight.read_background": "#5c78e225",
+ "editor.document_highlight.write_background": "#a3a3a466",
+ "terminal.background": "#fafafaff",
+ "terminal.foreground": "#2a2c33ff",
+ "terminal.bright_foreground": "#2a2c33ff",
+ "terminal.dim_foreground": "#bbbbbbff",
+ "terminal.ansi.black": "#000000ff",
+ "terminal.ansi.bright_black": "#000000ff",
+ "terminal.ansi.dim_black": "#555555ff",
+ "terminal.ansi.red": "#de3e35ff",
+ "terminal.ansi.bright_red": "#de3e35ff",
+ "terminal.ansi.dim_red": "#9c2b26ff",
+ "terminal.ansi.green": "#3f953aff",
+ "terminal.ansi.bright_green": "#3f953aff",
+ "terminal.ansi.dim_green": "#2b6927ff",
+ "terminal.ansi.yellow": "#d2b67cff",
+ "terminal.ansi.bright_yellow": "#d2b67cff",
+ "terminal.ansi.dim_yellow": "#a48c5aff",
+ "terminal.ansi.blue": "#2f5af3ff",
+ "terminal.ansi.bright_blue": "#2f5af3ff",
+ "terminal.ansi.dim_blue": "#2140abff",
+ "terminal.ansi.magenta": "#950095ff",
+ "terminal.ansi.bright_magenta": "#a00095ff",
+ "terminal.ansi.dim_magenta": "#6a006aff",
+ "terminal.ansi.cyan": "#0997b3ff",
+ "terminal.ansi.bright_cyan": "#0bbcd6ff",
+ "terminal.ansi.dim_cyan": "#0a7b92ff",
+ "terminal.ansi.white": "#bbbbbbff",
+ "terminal.ansi.bright_white": "#ffffffff",
+ "terminal.ansi.dim_white": "#888888ff",
+ "link_text.hover": "#5c78e2ff",
+ "version_control.added": "#27a657ff",
+ "version_control.modified": "#d3b020ff",
+ "version_control.word_added": "#2EA04859",
+ "version_control.word_deleted": "#F85149CC",
+ "version_control.deleted": "#e06c76ff",
+ "conflict": "#a48819ff",
+ "conflict.background": "#faf2e6ff",
+ "conflict.border": "#f4e7d1ff",
+ "created": "#669f59ff",
+ "created.background": "#dfeadbff",
+ "created.border": "#c8dcc1ff",
+ "deleted": "#d36151ff",
+ "deleted.background": "#fbdfd9ff",
+ "deleted.border": "#f6c6bdff",
+ "error": "#d36151ff",
+ "error.background": "#fbdfd9ff",
+ "error.border": "#f6c6bdff",
+ "hidden": "#7e8086ff",
+ "hidden.background": "#dcdcddff",
+ "hidden.border": "#d3d3d4ff",
+ "hint": "#7274a7ff",
+ "hint.background": "#e2e2faff",
+ "hint.border": "#cbcdf6ff",
+ "ignored": "#7e8086ff",
+ "ignored.background": "#dcdcddff",
+ "ignored.border": "#c9c9caff",
+ "info": "#5c78e2ff",
+ "info.background": "#e2e2faff",
+ "info.border": "#cbcdf6ff",
+ "modified": "#a48819ff",
+ "modified.background": "#faf2e6ff",
+ "modified.border": "#f4e7d1ff",
+ "predictive": "#9b9ec6ff",
+ "predictive.background": "#dfeadbff",
+ "predictive.border": "#c8dcc1ff",
+ "renamed": "#5c78e2ff",
+ "renamed.background": "#e2e2faff",
+ "renamed.border": "#cbcdf6ff",
+ "success": "#669f59ff",
+ "success.background": "#dfeadbff",
+ "success.border": "#c8dcc1ff",
+ "unreachable": "#58585aff",
+ "unreachable.background": "#dcdcddff",
+ "unreachable.border": "#c9c9caff",
+ "warning": "#a48819ff",
+ "warning.background": "#faf2e6ff",
+ "warning.border": "#f4e7d1ff",
+ "players": [
+ {
+ "cursor": "#5c78e2ff",
+ "background": "#5c78e2ff",
+ "selection": "#5c78e23d"
+ },
+ {
+ "cursor": "#984ea5ff",
+ "background": "#984ea5ff",
+ "selection": "#984ea53d"
+ },
+ {
+ "cursor": "#ad6e26ff",
+ "background": "#ad6e26ff",
+ "selection": "#ad6e263d"
+ },
+ {
+ "cursor": "#a349abff",
+ "background": "#a349abff",
+ "selection": "#a349ab3d"
+ },
+ {
+ "cursor": "#3a82b7ff",
+ "background": "#3a82b7ff",
+ "selection": "#3a82b73d"
+ },
+ {
+ "cursor": "#d36151ff",
+ "background": "#d36151ff",
+ "selection": "#d361513d"
+ },
+ {
+ "cursor": "#a48819ff",
+ "background": "#dec184ff",
+ "selection": "#dec1843d"
+ },
+ {
+ "cursor": "#669f59ff",
+ "background": "#669f59ff",
+ "selection": "#669f593d"
+ }
+ ],
+ "syntax": {
+ "attribute": {
+ "color": "#5c78e2ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "boolean": {
+ "color": "#ad6e25ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "comment": {
+ "color": "#a2a3a7ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "comment.doc": {
+ "color": "#7c7e86ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "constant": {
+ "color": "#c18401ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "constructor": {
+ "color": "#5c78e2ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "embedded": {
+ "color": "#242529ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "emphasis": {
+ "color": "#5c78e2ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "emphasis.strong": {
+ "color": "#ad6e25ff",
+ "font_style": null,
+ "font_weight": 700
+ },
+ "enum": {
+ "color": "#3882b7ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "function": {
+ "color": "#5b79e3ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "hint": {
+ "color": "#7274a7ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "keyword": {
+ "color": "#a449abff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "label": {
+ "color": "#5c78e2ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "link_text": {
+ "color": "#5b79e3ff",
+ "font_style": "italic",
+ "font_weight": null
+ },
+ "link_uri": {
+ "color": "#3882b7ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "namespace": {
+ "color": "#242529ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "number": {
+ "color": "#ad6e25ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "operator": {
+ "color": "#3882b7ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "predictive": {
+ "color": "#9b9ec6ff",
+ "font_style": "italic",
+ "font_weight": null
+ },
+ "preproc": {
+ "color": "#a449abff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "primary": {
+ "color": "#242529ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "property": {
+ "color": "#d3604fff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation": {
+ "color": "#242529ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.bracket": {
+ "color": "#4d4f52ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.delimiter": {
+ "color": "#4d4f52ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.list_marker": {
+ "color": "#d3604fff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.markup": {
+ "color": "#d3604fff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "punctuation.special": {
+ "color": "#b92b46ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "selector": {
+ "color": "#669f59ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "selector.pseudo": {
+ "color": "#5c78e2ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string": {
+ "color": "#649f57ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.escape": {
+ "color": "#7c7e86ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.regex": {
+ "color": "#ad6e26ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.special": {
+ "color": "#ad6e26ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "string.special.symbol": {
+ "color": "#ad6e26ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "tag": {
+ "color": "#5c78e2ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "text.literal": {
+ "color": "#649f57ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "title": {
+ "color": "#d3604fff",
+ "font_style": null,
+ "font_weight": 400
+ },
+ "type": {
+ "color": "#3882b7ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variable": {
+ "color": "#242529ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variable.parameter": {
+ "color": "#d3604fff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variable.special": {
+ "color": "#ad6e25ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "variant": {
+ "color": "#5b79e3ff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "diff.plus": {
+ "color": "#50a14fff",
+ "font_style": null,
+ "font_weight": null
+ },
+ "diff.minus": {
+ "color": "#e45649ff",
+ "font_style": null,
+ "font_weight": null
+ }
+ }
+ }
+ }
+ ]
+}
diff --git a/vendor/themes/solarized_light.toml b/vendor/themes/solarized_light.toml
new file mode 100644
index 00000000..2db54ca1
--- /dev/null
+++ b/vendor/themes/solarized_light.toml
@@ -0,0 +1,155 @@
+"attribute" = { fg = "violet" }
+"keyword" = { fg = "green" }
+"keyword.directive" = { fg = "orange" }
+"namespace" = { fg = "violet" }
+"operator" = { fg = "green" }
+"special" = { fg = "orange" }
+"variable.builtin" = { fg = "cyan", modifiers = ["bold"] }
+"variable.function" = { fg = "blue" }
+"type" = { fg = "yellow" }
+"type.builtin" = { fg = "yellow", modifiers = ["bold"] }
+"constructor" = { fg = "blue" }
+"function" = { fg = "blue" }
+"function.macro" = { fg = "magenta" }
+"function.builtin" = { fg = "blue", modifiers = ["bold"] }
+"function.special" = { fg = "magenta" }
+"comment" = { fg = "base01" }
+"string" = { fg = "cyan" }
+"constant" = { fg = "cyan" }
+"constant.builtin" = { fg = "cyan", modifiers = ["bold"] }
+"constant.character.escape" = { fg = "red", modifiers = ["bold"] }
+"label" = { fg = "green" }
+"module" = { fg = "violet" }
+"tag" = { fg = "magenta" }
+
+# TODO
+"markup.heading" = "blue"
+"markup.list" = "red"
+"markup.bold" = { fg = "yellow", modifiers = ["bold"] }
+"markup.italic" = { fg = "magenta", modifiers = ["italic"] }
+"markup.strikethrough" = { modifiers = ["crossed_out"] }
+"markup.link.url" = { fg = "yellow", modifiers = ["underlined"] }
+"markup.link.text" = "red"
+"markup.quote" = "cyan"
+"markup.raw" = "green"
+
+"diff.plus" = { fg = "green" }
+"diff.delta" = { fg = "yellow" }
+"diff.minus" = { fg = "red" }
+
+# 背景
+# background
+"ui.background" = { bg = "base03" }
+
+"ui.virtual.whitespace" = { fg = "base01" }
+"ui.virtual.inlay-hint" = { fg = "base01", modifiers = ["italic"] }
+"ui.virtual.jump-label" = { fg = "red", modifiers = ["bold"] }
+
+# 行号栏
+# line number column
+"ui.linenr" = { fg = "base0", bg = "base02" }
+# 当前行号栏
+# current line number column
+"ui.linenr.selected" = { fg = "blue", modifiers = ["bold"] }
+# cursorline
+"ui.cursorline" = { bg = "base0" }
+
+# 状态栏
+# status bar
+"ui.statusline" = { fg = "base03", bg = "base0" }
+"ui.statusline.normal" = { bg = "blue" }
+"ui.statusline.insert" = { bg = "green" }
+"ui.statusline.select" = { bg = "yellow" }
+
+# 非活动状态栏
+# inactive status bar
+"ui.statusline.inactive" = { fg = "base1", bg = "base01" }
+
+# 补全窗口, preview窗口
+# Completion window, preview window
+"ui.popup" = { bg = "base02" }
+# 影响 补全选中 cmd弹出信息选中
+# Affect completion selection, cmd pop-up information selection
+"ui.menu.selected" = { fg = "base02", bg = "base2"}
+"ui.menu" = { fg = "base0", bg = "base02" }
+# ??
+"ui.window" = { fg = "base3" }
+# 命令行 补全的帮助信息
+# Command line completion help information
+"ui.help" = { modifiers = ["reversed"] }
+
+# 快捷键窗口
+# Shortcut window
+"ui.popup.info" = { bg = "base1" }
+# 快捷键字体
+# Shortcut font
+"ui.text.info" = {fg = "base02", modifiers = ["bold"]}
+
+# 普通ui的字体样式
+# Normal ui font style
+"ui.text" = { fg = "base1" }
+# 影响 picker列表选中, 快捷键帮助窗口文本
+# Affects picker list selection, shortcut key help window text
+"ui.text.focus" = { fg = "blue", modifiers = ["bold"]}
+
+# 主光标/selection
+# main cursor/selection
+"ui.cursor.primary" = { fg = "base03", bg = "base1" }
+"ui.cursor.select" = { fg = "base02", bg = "cyan" }
+
+"ui.cursorline.primary" = { bg = "base02" }
+"ui.cursorline.secondary" = { bg = "base025" }
+
+"ui.selection" = { bg = "base0175" }
+"ui.selection.primary" = { bg = "base015" }
+
+"ui.virtual.indent-guide" = { fg = "base02" }
+"ui.virtual.ruler" = { bg = "base02" }
+
+# normal模式的光标
+# normal mode cursor
+"ui.cursor" = {fg = "base02", bg = "cyan"}
+"ui.cursor.insert" = {fg = "base03", bg = "base3"}
+# 当前光标匹配的标点符号
+# The punctuation character matched by the current cursor
+"ui.cursor.match" = { fg = "base02", bg = "base015" }
+
+"warning" = { fg = "orange", modifiers= ["bold", "underlined"] }
+"error" = { fg = "red", modifiers= ["bold", "underlined"] }
+"info" = { fg = "blue", modifiers= ["bold", "underlined"] }
+"hint" = { fg = "base01", modifiers= ["bold", "underlined"] }
+
+"diagnostic.warning" = { underline = { style = "curl", color = "orange" } }
+"diagnostic.error" = { underline = { style = "curl", color = "red" } }
+"diagnostic.info" = { underline = { style = "curl", color = "blue" } }
+"diagnostic.hint" = { underline = { style = "curl", color = "base01" } }
+"diagnostic.unnecessary" = { modifiers = ["dim"] }
+"diagnostic.deprecated" = { modifiers = ["crossed_out"] }
+
+
+[palette]
+red = '#dc322f'
+green = '#859900'
+yellow = '#b58900'
+blue = '#268bd2'
+magenta = '#d33682'
+cyan = '#2aa198'
+orange = '#cb4b16'
+violet = '#6c71c4'
+
+# 深色 越来越深
+# dark getting darker
+base0 = '#657b83'
+base1 = '#586e75'
+base2 = '#073642'
+base3 = '#002b36'
+
+# 浅色 越來越浅
+# Lighter and lighter
+base00 = '#839496'
+base01 = '#93a1a1'
+base015 = '#c5c8bd'
+base0175 = '#dddbcc'
+base02 = '#eee8d5'
+base025 = '#f5eedb'
+base03 = '#fdf6e3'