summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authort <t@tjp.lol>2026-09-20 13:08:46 -0600
committert <t@tjp.lol>2026-09-20 13:10:59 -0600
commitefbac44a016258fe59d5d880accfb14d2c234d2d (patch)
treecaa627ecb419cb73c689053cffa9dff4f517c6c4
Some popular color themes, packaged up as a panto extension
-rw-r--r--LICENSE18
-rw-r--r--README.md97
-rw-r--r--_selfcheck.lua82
-rw-r--r--mise.toml32
-rw-r--r--panto-popular-themes-0.1.0-1.rockspec30
-rw-r--r--panto-popular-themes/init.lua548
-rw-r--r--scripts/verify-against-core.lua50
7 files changed, 857 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..876f03c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,18 @@
+MIT License
+
+Copyright (c) 2026 Travis Parker
+
+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/README.md b/README.md
new file mode 100644
index 0000000..f716ae8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,97 @@
+# panto-popular-themes
+
+`themes.popular` is a small, pure-Lua [pantograph](https://github.com/travisp/pantograph)
+extension. It registers the 19 popular palettes currently built into Panto as
+well as three Kanagawa variants, using the same role-to-SGR derivation as the
+core.
+
+## Install
+
+Add the rock to Panto's extension list:
+
+```toml
+[extensions]
+rocks = ["panto-popular-themes"]
+```
+
+The extension is named `themes.popular`; theme names are case-sensitive.
+
+## Themes
+
+- Tokyo Night
+- Catppuccin Mocha
+- Gruvbox Dark
+- Solarized Light
+- Solarized Dark
+- Nord
+- Dracula
+- Tokyo Storm
+- Tokyo Moon
+- Tokyo Day
+- Catppuccin Latte
+- Catppuccin Frappe
+- Catppuccin Macchiato
+- Gruvbox Dark Hard
+- Gruvbox Dark Soft
+- Gruvbox Light
+- Gruvbox Light Hard
+- Gruvbox Light Soft
+- Dracula Soft
+- Kanagawa Wave
+- Kanagawa Dragon
+- Kanagawa Lotus
+
+## Provenance and mapping
+
+The first 19 palettes are faithful ports of the data and role derivation in
+`src/tui_theme.zig` (Tokyo Night 270–290, Catppuccin Mocha 291–311,
+Gruvbox Dark 312–332, Solarized Light 333–351, Solarized Dark 352–371,
+Nord 372–394, Dracula 395–416, Tokyo Storm 417–437, Tokyo Moon 438–458,
+Tokyo Day 459–479, Catppuccin Latte 480–500, Catppuccin Frappe 501–521,
+Catppuccin Macchiato 522–542, Gruvbox Dark Hard 543–563, Gruvbox Dark
+Soft 564–584, Gruvbox Light 585–605, Gruvbox Light Hard 606–626,
+Gruvbox Light Soft 627–647, Dracula Soft 648–666; derivation
+`makePalette` 1046–1105). Their source pins are the ones documented by Panto:
+
+- Tokyo Night: [`folke/tokyonight.nvim` at `cdc07ac`](https://github.com/folke/tokyonight.nvim/tree/cdc07ac78467a233fd62c493de29a17e0cf2b2b6)
+- Catppuccin: [`catppuccin/nvim` at `edefef7`](https://github.com/catppuccin/nvim/tree/edefef779ab08ce1a4a404713e3012b0d202bd35)
+- Gruvbox: [`ellisonleao/gruvbox.nvim` 2.0.0 at `61b0b3b`](https://github.com/ellisonleao/gruvbox.nvim/blob/61b0b3be2f0cfd521667403a0367298144d6c165/lua/gruvbox.lua)
+- Solarized: [`maxmx03/solarized.nvim` 3.6.0 at `c0dfe1c`](https://github.com/maxmx03/solarized.nvim/tree/c0dfe1cbfabd93b546baf5f1408f5df7e02e2050)
+- Nord: [`nordtheme/vim` at `f13f5df`](https://github.com/nordtheme/vim/blob/f13f5dfbb784deddbc1d8195f34dfd9ec73e2295/colors/nord.vim)
+- Dracula: [`Mofiqul/dracula.nvim` at `ae752c1`](https://github.com/Mofiqul/dracula.nvim/tree/ae752c13e95fb7c5f58da4b5123cb804ea7568ee)
+
+Kanagawa is taken from the primary [`rebelot/kanagawa.nvim`](https://github.com/rebelot/kanagawa.nvim)
+repository at pinned commit
+[`bb85e4bfc8d89b0e62c8fa53ccdd13d12e2f77b3`](https://github.com/rebelot/kanagawa.nvim/tree/bb85e4bfc8d89b0e62c8fa53ccdd13d12e2f77b3),
+using its [`colors.lua`](https://raw.githubusercontent.com/rebelot/kanagawa.nvim/bb85e4bfc8d89b0e62c8fa53ccdd13d12e2f77b3/lua/kanagawa/colors.lua)
+and [`themes.lua`](https://raw.githubusercontent.com/rebelot/kanagawa.nvim/bb85e4bfc8d89b0e62c8fa53ccdd13d12e2f77b3/lua/kanagawa/themes.lua).
+Wave, Dragon, and Lotus map Normal foreground/background, Comment, semantic
+syntax groups, NormalFloat, Title/Directory, and diagnostics into Panto's
+17-color palette. Kanagawa authors real `DiffAdd`/`DiffDelete` backgrounds,
+so those are retained; the 20% canvas/status tint is used only when a source
+palette does not provide a diff background.
+
+As in core, each palette emits all 40 role SGR strings and `reset = ESC[0m`.
+Activation first performs a read-only lookup for each name and registers only
+names that are not already present. This makes activating the extension
+alongside Panto's built-ins or a third-party palette safe: any existing
+identical or different registration wins, while malformed values for a new
+registration still raise.
+
+The stock `panto` palette and Panto's five original palettes—Tidepool Tumble,
+Mossmallow, Peachy Pudding, Plum Pips, and Gilded Inkblot—stay in core and are
+not registered here.
+
+## Development
+
+```sh
+panto lua _selfcheck.lua
+PANTOGRAPH_DIR="${PANTOGRAPH_DIR:-../pantograph}"
+REPO_DIR="$(pwd)"
+(cd "$PANTOGRAPH_DIR" && panto lua "$REPO_DIR/scripts/verify-against-core.lua")
+```
+
+The second check uses the real Panto theme registry: it compares every role
+in the extension's computed snapshots with the 19 corresponding core snapshots,
+reporting the theme and role for any byte mismatch, then activates the
+extension to validate registration of the three Kanagawa additions.
diff --git a/_selfcheck.lua b/_selfcheck.lua
new file mode 100644
index 0000000..29f36de
--- /dev/null
+++ b/_selfcheck.lua
@@ -0,0 +1,82 @@
+-- Self-check for themes.popular. Run with: panto lua _selfcheck.lua
+
+local here = debug.getinfo(1, "S").source:match("^@(.*)/[^/]+$") or "."
+package.path = here .. "/?.lua;" .. package.path
+
+local roles = {
+ "dim", "assistant", "user", "tool", "footer", "tool_text",
+ "tool_pending", "tool_success", "tool_error", "compaction_header",
+ "thinking_header", "cursor", "err", "welcome", "waiting_peak",
+ "waiting_near", "waiting_far", "err_diff", "add_diff", "thinking",
+ "compaction", "user_bg", "tool_pending_bg", "tool_success_bg",
+ "tool_error_bg", "diff_remove_bg", "diff_add_bg", "user_text",
+ "tool_header", "canvas_fg", "canvas_bg", "syntax_base",
+ "syntax_comment", "syntax_string", "syntax_number", "syntax_keyword",
+ "syntax_function", "syntax_type", "syntax_variable", "syntax_tag",
+}
+
+local names = {
+ "Tokyo Night", "Catppuccin Mocha", "Gruvbox Dark", "Solarized Light",
+ "Solarized Dark", "Nord", "Dracula", "Tokyo Storm", "Tokyo Moon",
+ "Tokyo Day", "Catppuccin Latte", "Catppuccin Frappe",
+ "Catppuccin Macchiato", "Gruvbox Dark Hard", "Gruvbox Dark Soft",
+ "Gruvbox Light", "Gruvbox Light Hard", "Gruvbox Light Soft",
+ "Dracula Soft", "Kanagawa Wave", "Kanagawa Dragon", "Kanagawa Lotus",
+}
+
+local function copy(t)
+ local out = {}
+ for k, v in pairs(t) do out[k] = v end
+ return out
+end
+
+local registered = {}
+package.loaded["panto"] = {
+ ext = {
+ style = function(name, values)
+ assert(type(name) == "string")
+ assert(type(values) == "table", "registration values for " .. name)
+ local old = registered[name]
+ if old then
+ for k, v in pairs(values) do
+ assert(old[k] == v, "conflicting re-registration for " .. name .. ": " .. k)
+ end
+ else
+ registered[name] = copy(values)
+ end
+ end,
+ },
+}
+
+local entry = require("panto-popular-themes.init")
+assert(entry.name == "themes.popular", "entry name")
+assert(type(entry.activate) == "function", "entry activate")
+entry.activate()
+
+local count = 0
+for name in pairs(registered) do count = count + 1 end
+assert(count == 22, "expected 22 themes, got " .. count)
+for _, name in ipairs(names) do
+ local values = registered[name]
+ assert(values, "missing theme " .. name)
+ for _, role in ipairs(roles) do
+ assert(type(values[role]) == "string", name .. " missing role " .. role)
+ end
+ assert(values.reset == "\27[0m", name .. " reset")
+end
+
+local function eq(name, role, expected)
+ assert(registered[name][role] == expected,
+ name .. "." .. role .. " was " .. tostring(registered[name][role]) ..
+ ", expected " .. expected)
+end
+
+eq("Tokyo Night", "tool", "\27[38;2;122;162;247m")
+eq("Tokyo Night", "diff_add_bg", "\27[48;2;36;62;74m")
+eq("Solarized Light", "diff_add_bg", "\27[48;2;229;227;181m")
+eq("Kanagawa Lotus", "canvas_bg", "\27[48;2;242;236;188m")
+eq("Kanagawa Lotus", "syntax_tag", "\27[38;2;102;147;191m")
+
+eq("Kanagawa Wave", "reset", "\27[0m")
+
+print("selfcheck: OK")
diff --git a/mise.toml b/mise.toml
new file mode 100644
index 0000000..1de7ca9
--- /dev/null
+++ b/mise.toml
@@ -0,0 +1,32 @@
+# Release plumbing. `mise run version <x.y.z>` rewrites the rockspec.
+
+[tasks.check]
+description = "Run the self-check inside panto's interpreter"
+dir = "{{config_root}}"
+run = "panto lua _selfcheck.lua"
+
+[tasks.verify]
+description = "Compare moved themes against Panto core"
+dir = "{{config_root}}"
+run = '''
+set -eu
+PANTOGRAPH_DIR="${PANTOGRAPH_DIR:-../pantograph}"
+(cd "$PANTOGRAPH_DIR" && panto lua "{{config_root}}/scripts/verify-against-core.lua")
+'''
+
+[tasks.version]
+description = "Rewrite the rockspec for a version (then commit it)"
+dir = "{{config_root}}"
+run = '''
+set -eu
+NEW="{{arg(name='version')}}"
+SPEC="$(ls -1 ./*.rockspec)"
+PKG="$(sed -n 's/^package = "\\(.*\\)"/\\1/p' "$SPEC")"
+NEWSPEC="./$PKG-$NEW-1.rockspec"
+TMP="$(mktemp)"
+sed -e "s/^version = \\\".*\\\"/version = \\\"$NEW-1\\\"/" \\\
+ -e "s/^\\( *\\)tag = \\\"v[^\\\"]*\\\"/\\1tag = \\\"v$NEW\\\"/" "$SPEC" > "$TMP"
+mv "$TMP" "$NEWSPEC"
+[ "$SPEC" = "$NEWSPEC" ] || rm -f "$SPEC"
+luarocks lint "$NEWSPEC"
+'''
diff --git a/panto-popular-themes-0.1.0-1.rockspec b/panto-popular-themes-0.1.0-1.rockspec
new file mode 100644
index 0000000..10876e2
--- /dev/null
+++ b/panto-popular-themes-0.1.0-1.rockspec
@@ -0,0 +1,30 @@
+rockspec_format = "3.0"
+package = "panto-popular-themes"
+version = "0.1.0-1"
+
+source = {
+ url = "git+https://code.tjp.lol/panto-popular-themes.git",
+ tag = "v0.1.0",
+}
+
+description = {
+ summary = "Popular terminal themes for pantograph",
+ detailed = [[
+An out-of-tree panto extension registering the popular palettes shipped by
+pantograph, plus Kanagawa Wave, Dragon, and Lotus.
+]],
+ homepage = "https://code.tjp.lol/panto-popular-themes",
+ license = "MIT",
+ labels = { "panto", "pantograph", "theme", "themes", "terminal" },
+}
+
+dependencies = {
+ "lua >= 5.1",
+}
+
+build = {
+ type = "builtin",
+ modules = {
+ ["panto-popular-themes"] = "panto-popular-themes/init.lua",
+ },
+}
diff --git a/panto-popular-themes/init.lua b/panto-popular-themes/init.lua
new file mode 100644
index 0000000..51f3ca5
--- /dev/null
+++ b/panto-popular-themes/init.lua
@@ -0,0 +1,548 @@
+-- Popular terminal palettes for pantograph. The first 19 entries are copied
+-- from pantograph/src/tui_theme.zig; the final three are Kanagawa reductions.
+-- Activation probes panto.ext.style(name) before registering so an existing
+-- core or third-party theme wins without hiding validation errors.
+
+local palettes = {
+ { name = 'Tokyo Night', palette = {
+ canvas_fg = { r = 0xc0, g = 0xca, b = 0xf5 },
+ canvas_bg = { r = 0x1a, g = 0x1b, b = 0x26 },
+ surface = { r = 0x16, g = 0x16, b = 0x1e },
+ accent = { r = 0x7a, g = 0xa2, b = 0xf7 },
+ muted = { r = 0x56, g = 0x5f, b = 0x89 },
+ success = { r = 0x73, g = 0xda, b = 0xca },
+ err = { r = 0xdb, g = 0x4b, b = 0x4b },
+ warn = { r = 0xe0, g = 0xaf, b = 0x68 },
+ purple = { r = 0xbb, g = 0x9a, b = 0xf7 },
+ secondary = { r = 0x7d, g = 0xcf, b = 0xff },
+ syntax_string = { r = 0x9e, g = 0xce, b = 0x6a },
+ syntax_number = { r = 0xff, g = 0x9e, b = 0x64 },
+ syntax_keyword = { r = 0x9d, g = 0x7c, b = 0xd8 },
+ syntax_function = { r = 0x7a, g = 0xa2, b = 0xf7 },
+ syntax_type = { r = 0x2a, g = 0xc3, b = 0xde },
+ syntax_variable = { r = 0xc0, g = 0xca, b = 0xf5 },
+ syntax_tag = { r = 0xbb, g = 0x9a, b = 0xf7 },
+ diff_add_bg = { r = 0x24, g = 0x3e, b = 0x4a },
+ diff_remove_bg = { r = 0x4a, g = 0x27, b = 0x2f },
+ } },
+ { name = 'Catppuccin Mocha', palette = {
+ canvas_fg = { r = 0xcd, g = 0xd6, b = 0xf4 },
+ canvas_bg = { r = 0x1e, g = 0x1e, b = 0x2e },
+ surface = { r = 0x18, g = 0x18, b = 0x25 },
+ accent = { r = 0x89, g = 0xb4, b = 0xfa },
+ muted = { r = 0x93, g = 0x99, b = 0xb2 },
+ success = { r = 0x94, g = 0xe2, b = 0xd5 },
+ err = { r = 0xf3, g = 0x8b, b = 0xa8 },
+ warn = { r = 0xf9, g = 0xe2, b = 0xaf },
+ purple = { r = 0xcb, g = 0xa6, b = 0xf7 },
+ secondary = { r = 0x89, g = 0xdc, b = 0xeb },
+ syntax_string = { r = 0xa6, g = 0xe3, b = 0xa1 },
+ syntax_number = { r = 0xfa, g = 0xb3, b = 0x87 },
+ syntax_keyword = { r = 0xcb, g = 0xa6, b = 0xf7 },
+ syntax_function = { r = 0x89, g = 0xb4, b = 0xfa },
+ syntax_type = { r = 0xf9, g = 0xe2, b = 0xaf },
+ syntax_variable = { r = 0xcd, g = 0xd6, b = 0xf4 },
+ syntax_tag = { r = 0x89, g = 0xb4, b = 0xfa },
+ diff_add_bg = { r = 0x36, g = 0x41, b = 0x43 },
+ diff_remove_bg = { r = 0x44, g = 0x32, b = 0x44 },
+ } },
+ { name = 'Gruvbox Dark', palette = {
+ canvas_fg = { r = 0xeb, g = 0xdb, b = 0xb2 },
+ canvas_bg = { r = 0x28, g = 0x28, b = 0x28 },
+ surface = { r = 0x3c, g = 0x38, b = 0x36 },
+ accent = { r = 0x83, g = 0xa5, b = 0x98 },
+ muted = { r = 0x92, g = 0x83, b = 0x74 },
+ success = { r = 0xb8, g = 0xbb, b = 0x26 },
+ err = { r = 0xfb, g = 0x49, b = 0x34 },
+ warn = { r = 0xfa, g = 0xbd, b = 0x2f },
+ purple = { r = 0xd3, g = 0x86, b = 0x9b },
+ secondary = { r = 0x8e, g = 0xc0, b = 0x7c },
+ syntax_string = { r = 0xb8, g = 0xbb, b = 0x26 },
+ syntax_number = { r = 0xd3, g = 0x86, b = 0x9b },
+ syntax_keyword = { r = 0xfb, g = 0x49, b = 0x34 },
+ syntax_function = { r = 0xb8, g = 0xbb, b = 0x26 },
+ syntax_type = { r = 0xfa, g = 0xbd, b = 0x2f },
+ syntax_variable = { r = 0xeb, g = 0xdb, b = 0xb2 },
+ syntax_tag = { r = 0xfe, g = 0x80, b = 0x19 },
+ diff_add_bg = { r = 0x62, g = 0x69, b = 0x3e },
+ diff_remove_bg = { r = 0x72, g = 0x25, b = 0x29 },
+ } },
+ { name = 'Solarized Light', palette = {
+ canvas_fg = { r = 0x65, g = 0x7b, b = 0x83 },
+ canvas_bg = { r = 0xfd, g = 0xf6, b = 0xe3 },
+ surface = { r = 0xfb, g = 0xf3, b = 0xdb },
+ accent = { r = 0x26, g = 0x8b, b = 0xd2 },
+ muted = { r = 0x93, g = 0xa1, b = 0xa1 },
+ success = { r = 0x85, g = 0x99, b = 0x00 },
+ err = { r = 0xdc, g = 0x32, b = 0x2f },
+ warn = { r = 0xb5, g = 0x89, b = 0x00 },
+ purple = { r = 0xd3, g = 0x36, b = 0x82 },
+ secondary = { r = 0x2a, g = 0xa1, b = 0x98 },
+ syntax_string = { r = 0x2a, g = 0xa1, b = 0x98 },
+ syntax_number = { r = 0xd3, g = 0x36, b = 0x82 },
+ syntax_keyword = { r = 0x58, g = 0x6e, b = 0x75 },
+ syntax_function = { r = 0x26, g = 0x8b, b = 0xd2 },
+ syntax_type = { r = 0xb5, g = 0x89, b = 0x00 },
+ syntax_variable = { r = 0x26, g = 0x8b, b = 0xd2 },
+ syntax_tag = { r = 0x26, g = 0x8b, b = 0xd2 },
+ } },
+ { name = 'Solarized Dark', palette = {
+ canvas_fg = { r = 0x83, g = 0x94, b = 0x96 },
+ canvas_bg = { r = 0x00, g = 0x2b, b = 0x36 },
+ surface = { r = 0x00, g = 0x27, b = 0x31 },
+ accent = { r = 0x26, g = 0x8b, b = 0xd2 },
+ muted = { r = 0x58, g = 0x6e, b = 0x75 },
+ success = { r = 0x85, g = 0x99, b = 0x00 },
+ err = { r = 0xdc, g = 0x32, b = 0x2f },
+ warn = { r = 0xb5, g = 0x89, b = 0x00 },
+ purple = { r = 0x6c, g = 0x71, b = 0xc4 },
+ secondary = { r = 0x2a, g = 0xa1, b = 0x98 },
+ syntax_string = { r = 0x2a, g = 0xa1, b = 0x98 },
+ syntax_number = { r = 0x6c, g = 0x71, b = 0xc4 },
+ syntax_keyword = { r = 0x93, g = 0xa1, b = 0xa1 },
+ syntax_function = { r = 0x26, g = 0x8b, b = 0xd2 },
+ syntax_type = { r = 0xcb, g = 0x4b, b = 0x16 },
+ syntax_variable = { r = 0x26, g = 0x8b, b = 0xd2 },
+ syntax_tag = { r = 0x26, g = 0x8b, b = 0xd2 },
+ } },
+ { name = 'Nord', palette = {
+ canvas_fg = { r = 0xd8, g = 0xde, b = 0xe9 },
+ canvas_bg = { r = 0x2e, g = 0x34, b = 0x40 },
+ surface = { r = 0x43, g = 0x4c, b = 0x5e },
+ accent = { r = 0x88, g = 0xc0, b = 0xd0 },
+ muted = { r = 0x61, g = 0x6e, b = 0x88 },
+ success = { r = 0xa3, g = 0xbe, b = 0x8c },
+ err = { r = 0xbf, g = 0x61, b = 0x6a },
+ warn = { r = 0xeb, g = 0xcb, b = 0x8b },
+ purple = { r = 0xb4, g = 0x8e, b = 0xad },
+ secondary = { r = 0x5e, g = 0x81, b = 0xac },
+ syntax_string = { r = 0xa3, g = 0xbe, b = 0x8c },
+ syntax_number = { r = 0xb4, g = 0x8e, b = 0xad },
+ syntax_keyword = { r = 0x81, g = 0xa1, b = 0xc1 },
+ syntax_function = { r = 0x88, g = 0xc0, b = 0xd0 },
+ syntax_type = { r = 0x81, g = 0xa1, b = 0xc1 },
+ syntax_variable = { r = 0xd8, g = 0xde, b = 0xe9 },
+ syntax_tag = { r = 0xd8, g = 0xde, b = 0xe9 },
+ } },
+ { name = 'Dracula', palette = {
+ canvas_fg = { r = 0xf8, g = 0xf8, b = 0xf2 },
+ canvas_bg = { r = 0x28, g = 0x2a, b = 0x36 },
+ surface = { r = 0x21, g = 0x22, b = 0x2c },
+ accent = { r = 0x8b, g = 0xe9, b = 0xfd },
+ muted = { r = 0x62, g = 0x72, b = 0xa4 },
+ success = { r = 0x50, g = 0xfa, b = 0x7b },
+ err = { r = 0xff, g = 0x55, b = 0x55 },
+ warn = { r = 0xf1, g = 0xfa, b = 0x8c },
+ purple = { r = 0xbd, g = 0x93, b = 0xf9 },
+ secondary = { r = 0xff, g = 0x79, b = 0xc6 },
+ syntax_string = { r = 0xf1, g = 0xfa, b = 0x8c },
+ syntax_number = { r = 0xbd, g = 0x93, b = 0xf9 },
+ syntax_keyword = { r = 0xff, g = 0x79, b = 0xc6 },
+ syntax_function = { r = 0x50, g = 0xfa, b = 0x7b },
+ syntax_type = { r = 0xa4, g = 0xff, b = 0xff },
+ syntax_variable = { r = 0xf8, g = 0xf8, b = 0xf2 },
+ syntax_tag = { r = 0x8b, g = 0xe9, b = 0xfd },
+ } },
+ { name = 'Tokyo Storm', palette = {
+ canvas_fg = { r = 0xc0, g = 0xca, b = 0xf5 },
+ canvas_bg = { r = 0x24, g = 0x28, b = 0x3b },
+ surface = { r = 0x1f, g = 0x23, b = 0x35 },
+ accent = { r = 0x7a, g = 0xa2, b = 0xf7 },
+ muted = { r = 0x56, g = 0x5f, b = 0x89 },
+ success = { r = 0x73, g = 0xda, b = 0xca },
+ err = { r = 0xdb, g = 0x4b, b = 0x4b },
+ warn = { r = 0xe0, g = 0xaf, b = 0x68 },
+ purple = { r = 0xbb, g = 0x9a, b = 0xf7 },
+ secondary = { r = 0x7d, g = 0xcf, b = 0xff },
+ syntax_string = { r = 0x9e, g = 0xce, b = 0x6a },
+ syntax_number = { r = 0xff, g = 0x9e, b = 0x64 },
+ syntax_keyword = { r = 0x9d, g = 0x7c, b = 0xd8 },
+ syntax_function = { r = 0x7a, g = 0xa2, b = 0xf7 },
+ syntax_type = { r = 0x2a, g = 0xc3, b = 0xde },
+ syntax_variable = { r = 0xc0, g = 0xca, b = 0xf5 },
+ syntax_tag = { r = 0xbb, g = 0x9a, b = 0xf7 },
+ diff_add_bg = { r = 0x2b, g = 0x48, b = 0x5a },
+ diff_remove_bg = { r = 0x52, g = 0x31, b = 0x3f },
+ } },
+ { name = 'Tokyo Moon', palette = {
+ canvas_fg = { r = 0xc8, g = 0xd3, b = 0xf5 },
+ canvas_bg = { r = 0x22, g = 0x24, b = 0x36 },
+ surface = { r = 0x1e, g = 0x20, b = 0x30 },
+ accent = { r = 0x82, g = 0xaa, b = 0xff },
+ muted = { r = 0x63, g = 0x6d, b = 0xa6 },
+ success = { r = 0x4f, g = 0xd6, b = 0xbe },
+ err = { r = 0xc5, g = 0x3b, b = 0x53 },
+ warn = { r = 0xff, g = 0xc7, b = 0x77 },
+ purple = { r = 0xc0, g = 0x99, b = 0xff },
+ secondary = { r = 0x86, g = 0xe1, b = 0xfc },
+ syntax_string = { r = 0xc3, g = 0xe8, b = 0x8d },
+ syntax_number = { r = 0xff, g = 0x96, b = 0x6c },
+ syntax_keyword = { r = 0xfc, g = 0xa7, b = 0xea },
+ syntax_function = { r = 0x82, g = 0xaa, b = 0xff },
+ syntax_type = { r = 0x65, g = 0xbc, b = 0xff },
+ syntax_variable = { r = 0xc8, g = 0xd3, b = 0xf5 },
+ syntax_tag = { r = 0xc0, g = 0x99, b = 0xff },
+ diff_add_bg = { r = 0x2a, g = 0x45, b = 0x56 },
+ diff_remove_bg = { r = 0x4b, g = 0x2a, b = 0x3d },
+ } },
+ { name = 'Tokyo Day', palette = {
+ canvas_fg = { r = 0x37, g = 0x60, b = 0xbf },
+ canvas_bg = { r = 0xe1, g = 0xe2, b = 0xe7 },
+ surface = { r = 0xd0, g = 0xd5, b = 0xe3 },
+ accent = { r = 0x2e, g = 0x7d, b = 0xe9 },
+ muted = { r = 0x84, g = 0x8c, b = 0xb5 },
+ success = { r = 0x38, g = 0x70, b = 0x68 },
+ err = { r = 0xc6, g = 0x43, b = 0x43 },
+ warn = { r = 0x8c, g = 0x6c, b = 0x3e },
+ purple = { r = 0x98, g = 0x54, b = 0xf1 },
+ secondary = { r = 0x00, g = 0x71, b = 0x97 },
+ syntax_string = { r = 0x58, g = 0x75, b = 0x39 },
+ syntax_number = { r = 0xb1, g = 0x5c, b = 0x00 },
+ syntax_keyword = { r = 0x78, g = 0x47, b = 0xbd },
+ syntax_function = { r = 0x2e, g = 0x7d, b = 0xe9 },
+ syntax_type = { r = 0x18, g = 0x80, b = 0x92 },
+ syntax_variable = { r = 0x37, g = 0x60, b = 0xbf },
+ syntax_tag = { r = 0x98, g = 0x54, b = 0xf1 },
+ diff_add_bg = { r = 0xb7, g = 0xce, b = 0xd5 },
+ diff_remove_bg = { r = 0xda, g = 0xba, b = 0xbe },
+ } },
+ { name = 'Catppuccin Latte', palette = {
+ canvas_fg = { r = 0x4c, g = 0x4f, b = 0x69 },
+ canvas_bg = { r = 0xef, g = 0xf1, b = 0xf5 },
+ surface = { r = 0xe6, g = 0xe9, b = 0xef },
+ accent = { r = 0x1e, g = 0x66, b = 0xf5 },
+ muted = { r = 0x7c, g = 0x7f, b = 0x93 },
+ success = { r = 0x17, g = 0x92, b = 0x99 },
+ err = { r = 0xd2, g = 0x0f, b = 0x39 },
+ warn = { r = 0xdf, g = 0x8e, b = 0x1d },
+ purple = { r = 0x88, g = 0x39, b = 0xef },
+ secondary = { r = 0x04, g = 0xa5, b = 0xe5 },
+ syntax_string = { r = 0x40, g = 0xa0, b = 0x2b },
+ syntax_number = { r = 0xfe, g = 0x64, b = 0x0b },
+ syntax_keyword = { r = 0x88, g = 0x39, b = 0xef },
+ syntax_function = { r = 0x1e, g = 0x66, b = 0xf5 },
+ syntax_type = { r = 0xdf, g = 0x8e, b = 0x1d },
+ syntax_variable = { r = 0x4c, g = 0x4f, b = 0x69 },
+ syntax_tag = { r = 0x1e, g = 0x66, b = 0xf5 },
+ diff_add_bg = { r = 0xd0, g = 0xe2, b = 0xd1 },
+ diff_remove_bg = { r = 0xea, g = 0xc8, b = 0xd3 },
+ } },
+ { name = 'Catppuccin Frappe', palette = {
+ canvas_fg = { r = 0xc6, g = 0xd0, b = 0xf5 },
+ canvas_bg = { r = 0x30, g = 0x34, b = 0x46 },
+ surface = { r = 0x29, g = 0x2c, b = 0x3c },
+ accent = { r = 0x8c, g = 0xaa, b = 0xee },
+ muted = { r = 0x94, g = 0x9c, b = 0xbb },
+ success = { r = 0x81, g = 0xc8, b = 0xbe },
+ err = { r = 0xe7, g = 0x82, b = 0x84 },
+ warn = { r = 0xe5, g = 0xc8, b = 0x90 },
+ purple = { r = 0xca, g = 0x9e, b = 0xe6 },
+ secondary = { r = 0x99, g = 0xd1, b = 0xdb },
+ syntax_string = { r = 0xa6, g = 0xd1, b = 0x89 },
+ syntax_number = { r = 0xef, g = 0x9f, b = 0x76 },
+ syntax_keyword = { r = 0xca, g = 0x9e, b = 0xe6 },
+ syntax_function = { r = 0x8c, g = 0xaa, b = 0xee },
+ syntax_type = { r = 0xe5, g = 0xc8, b = 0x90 },
+ syntax_variable = { r = 0xc6, g = 0xd0, b = 0xf5 },
+ syntax_tag = { r = 0x8c, g = 0xaa, b = 0xee },
+ diff_add_bg = { r = 0x45, g = 0x50, b = 0x52 },
+ diff_remove_bg = { r = 0x51, g = 0x42, b = 0x51 },
+ } },
+ { name = 'Catppuccin Macchiato', palette = {
+ canvas_fg = { r = 0xca, g = 0xd3, b = 0xf5 },
+ canvas_bg = { r = 0x24, g = 0x27, b = 0x3a },
+ surface = { r = 0x1e, g = 0x20, b = 0x30 },
+ accent = { r = 0x8a, g = 0xad, b = 0xf4 },
+ muted = { r = 0x93, g = 0x9a, b = 0xb7 },
+ success = { r = 0x8b, g = 0xd5, b = 0xca },
+ err = { r = 0xed, g = 0x87, b = 0x96 },
+ warn = { r = 0xee, g = 0xd4, b = 0x9f },
+ purple = { r = 0xc6, g = 0xa0, b = 0xf6 },
+ secondary = { r = 0x91, g = 0xd7, b = 0xe3 },
+ syntax_string = { r = 0xa6, g = 0xda, b = 0x95 },
+ syntax_number = { r = 0xf5, g = 0xa9, b = 0x7f },
+ syntax_keyword = { r = 0xc6, g = 0xa0, b = 0xf6 },
+ syntax_function = { r = 0x8a, g = 0xad, b = 0xf4 },
+ syntax_type = { r = 0xee, g = 0xd4, b = 0x9f },
+ syntax_variable = { r = 0xca, g = 0xd3, b = 0xf5 },
+ syntax_tag = { r = 0x8a, g = 0xad, b = 0xf4 },
+ diff_add_bg = { r = 0x3b, g = 0x47, b = 0x4a },
+ diff_remove_bg = { r = 0x48, g = 0x38, b = 0x4b },
+ } },
+ { name = 'Gruvbox Dark Hard', palette = {
+ canvas_fg = { r = 0xeb, g = 0xdb, b = 0xb2 },
+ canvas_bg = { r = 0x1d, g = 0x20, b = 0x21 },
+ surface = { r = 0x3c, g = 0x38, b = 0x36 },
+ accent = { r = 0x83, g = 0xa5, b = 0x98 },
+ muted = { r = 0x92, g = 0x83, b = 0x74 },
+ success = { r = 0xb8, g = 0xbb, b = 0x26 },
+ err = { r = 0xfb, g = 0x49, b = 0x34 },
+ warn = { r = 0xfa, g = 0xbd, b = 0x2f },
+ purple = { r = 0xd3, g = 0x86, b = 0x9b },
+ secondary = { r = 0x8e, g = 0xc0, b = 0x7c },
+ syntax_string = { r = 0xb8, g = 0xbb, b = 0x26 },
+ syntax_number = { r = 0xd3, g = 0x86, b = 0x9b },
+ syntax_keyword = { r = 0xfb, g = 0x49, b = 0x34 },
+ syntax_function = { r = 0xb8, g = 0xbb, b = 0x26 },
+ syntax_type = { r = 0xfa, g = 0xbd, b = 0x2f },
+ syntax_variable = { r = 0xeb, g = 0xdb, b = 0xb2 },
+ syntax_tag = { r = 0xfe, g = 0x80, b = 0x19 },
+ diff_add_bg = { r = 0x5a, g = 0x63, b = 0x3a },
+ diff_remove_bg = { r = 0x79, g = 0x23, b = 0x29 },
+ } },
+ { name = 'Gruvbox Dark Soft', palette = {
+ canvas_fg = { r = 0xeb, g = 0xdb, b = 0xb2 },
+ canvas_bg = { r = 0x32, g = 0x30, b = 0x2f },
+ surface = { r = 0x3c, g = 0x38, b = 0x36 },
+ accent = { r = 0x83, g = 0xa5, b = 0x98 },
+ muted = { r = 0x92, g = 0x83, b = 0x74 },
+ success = { r = 0xb8, g = 0xbb, b = 0x26 },
+ err = { r = 0xfb, g = 0x49, b = 0x34 },
+ warn = { r = 0xfa, g = 0xbd, b = 0x2f },
+ purple = { r = 0xd3, g = 0x86, b = 0x9b },
+ secondary = { r = 0x8e, g = 0xc0, b = 0x7c },
+ syntax_string = { r = 0xb8, g = 0xbb, b = 0x26 },
+ syntax_number = { r = 0xd3, g = 0x86, b = 0x9b },
+ syntax_keyword = { r = 0xfb, g = 0x49, b = 0x34 },
+ syntax_function = { r = 0xb8, g = 0xbb, b = 0x26 },
+ syntax_type = { r = 0xfa, g = 0xbd, b = 0x2f },
+ syntax_variable = { r = 0xeb, g = 0xdb, b = 0xb2 },
+ syntax_tag = { r = 0xfe, g = 0x80, b = 0x19 },
+ diff_add_bg = { r = 0x68, g = 0x6d, b = 0x43 },
+ diff_remove_bg = { r = 0x7b, g = 0x2c, b = 0x2f },
+ } },
+ { name = 'Gruvbox Light', palette = {
+ canvas_fg = { r = 0x3c, g = 0x38, b = 0x36 },
+ canvas_bg = { r = 0xfb, g = 0xf1, b = 0xc7 },
+ surface = { r = 0xeb, g = 0xdb, b = 0xb2 },
+ accent = { r = 0x07, g = 0x66, b = 0x78 },
+ muted = { r = 0x92, g = 0x83, b = 0x74 },
+ success = { r = 0x79, g = 0x74, b = 0x0e },
+ err = { r = 0x9d, g = 0x00, b = 0x06 },
+ warn = { r = 0xb5, g = 0x76, b = 0x14 },
+ purple = { r = 0x8f, g = 0x3f, b = 0x71 },
+ secondary = { r = 0x42, g = 0x7b, b = 0x58 },
+ syntax_string = { r = 0x79, g = 0x74, b = 0x0e },
+ syntax_number = { r = 0x8f, g = 0x3f, b = 0x71 },
+ syntax_keyword = { r = 0x9d, g = 0x00, b = 0x06 },
+ syntax_function = { r = 0x79, g = 0x74, b = 0x0e },
+ syntax_type = { r = 0xb5, g = 0x76, b = 0x14 },
+ syntax_variable = { r = 0x3c, g = 0x38, b = 0x36 },
+ syntax_tag = { r = 0xaf, g = 0x3a, b = 0x03 },
+ diff_add_bg = { r = 0xd5, g = 0xd3, b = 0x9b },
+ diff_remove_bg = { r = 0xfc, g = 0x94, b = 0x87 },
+ } },
+ { name = 'Gruvbox Light Hard', palette = {
+ canvas_fg = { r = 0x3c, g = 0x38, b = 0x36 },
+ canvas_bg = { r = 0xf9, g = 0xf5, b = 0xd7 },
+ surface = { r = 0xeb, g = 0xdb, b = 0xb2 },
+ accent = { r = 0x07, g = 0x66, b = 0x78 },
+ muted = { r = 0x92, g = 0x83, b = 0x74 },
+ success = { r = 0x79, g = 0x74, b = 0x0e },
+ err = { r = 0x9d, g = 0x00, b = 0x06 },
+ warn = { r = 0xb5, g = 0x76, b = 0x14 },
+ purple = { r = 0x8f, g = 0x3f, b = 0x71 },
+ secondary = { r = 0x42, g = 0x7b, b = 0x58 },
+ syntax_string = { r = 0x79, g = 0x74, b = 0x0e },
+ syntax_number = { r = 0x8f, g = 0x3f, b = 0x71 },
+ syntax_keyword = { r = 0x9d, g = 0x00, b = 0x06 },
+ syntax_function = { r = 0x79, g = 0x74, b = 0x0e },
+ syntax_type = { r = 0xb5, g = 0x76, b = 0x14 },
+ syntax_variable = { r = 0x3c, g = 0x38, b = 0x36 },
+ syntax_tag = { r = 0xaf, g = 0x3a, b = 0x03 },
+ diff_add_bg = { r = 0xd3, g = 0xd6, b = 0xa5 },
+ diff_remove_bg = { r = 0xfc, g = 0x96, b = 0x90 },
+ } },
+ { name = 'Gruvbox Light Soft', palette = {
+ canvas_fg = { r = 0x3c, g = 0x38, b = 0x36 },
+ canvas_bg = { r = 0xf2, g = 0xe5, b = 0xbc },
+ surface = { r = 0xeb, g = 0xdb, b = 0xb2 },
+ accent = { r = 0x07, g = 0x66, b = 0x78 },
+ muted = { r = 0x92, g = 0x83, b = 0x74 },
+ success = { r = 0x79, g = 0x74, b = 0x0e },
+ err = { r = 0x9d, g = 0x00, b = 0x06 },
+ warn = { r = 0xb5, g = 0x76, b = 0x14 },
+ purple = { r = 0x8f, g = 0x3f, b = 0x71 },
+ secondary = { r = 0x42, g = 0x7b, b = 0x58 },
+ syntax_string = { r = 0x79, g = 0x74, b = 0x0e },
+ syntax_number = { r = 0x8f, g = 0x3f, b = 0x71 },
+ syntax_keyword = { r = 0x9d, g = 0x00, b = 0x06 },
+ syntax_function = { r = 0x79, g = 0x74, b = 0x0e },
+ syntax_type = { r = 0xb5, g = 0x76, b = 0x14 },
+ syntax_variable = { r = 0x3c, g = 0x38, b = 0x36 },
+ syntax_tag = { r = 0xaf, g = 0x3a, b = 0x03 },
+ diff_add_bg = { r = 0xce, g = 0xcb, b = 0x94 },
+ diff_remove_bg = { r = 0xf7, g = 0x8b, b = 0x7f },
+ } },
+ { name = 'Dracula Soft', palette = {
+ canvas_fg = { r = 0xf6, g = 0xf6, b = 0xf5 },
+ canvas_bg = { r = 0x29, g = 0x2a, b = 0x35 },
+ surface = { r = 0x21, g = 0x22, b = 0x2c },
+ accent = { r = 0xa7, g = 0xdf, b = 0xef },
+ muted = { r = 0x70, g = 0x74, b = 0x7f },
+ success = { r = 0x87, g = 0xe5, b = 0x8e },
+ err = { r = 0xdd, g = 0x6e, b = 0x6b },
+ warn = { r = 0xe8, g = 0xed, b = 0xa2 },
+ purple = { r = 0xba, g = 0xa0, b = 0xe8 },
+ secondary = { r = 0xe4, g = 0x8c, b = 0xc1 },
+ syntax_string = { r = 0xe8, g = 0xed, b = 0xa2 },
+ syntax_number = { r = 0xba, g = 0xa0, b = 0xe8 },
+ syntax_keyword = { r = 0xe4, g = 0x8c, b = 0xc1 },
+ syntax_function = { r = 0x87, g = 0xe5, b = 0x8e },
+ syntax_type = { r = 0xbc, g = 0xf4, b = 0xf5 },
+ syntax_variable = { r = 0xf6, g = 0xf6, b = 0xf5 },
+ syntax_tag = { r = 0xa7, g = 0xdf, b = 0xef },
+ } },
+ { name = 'Kanagawa Wave', palette = {
+ canvas_fg = { r = 0xdc, g = 0xd7, b = 0xba },
+ canvas_bg = { r = 0x1f, g = 0x1f, b = 0x28 },
+ surface = { r = 0x16, g = 0x16, b = 0x1d },
+ accent = { r = 0x7e, g = 0x9c, b = 0xd8 },
+ muted = { r = 0x72, g = 0x71, b = 0x69 },
+ success = { r = 0x98, g = 0xbb, b = 0x6c },
+ err = { r = 0xe8, g = 0x24, b = 0x24 },
+ warn = { r = 0xff, g = 0x9e, b = 0x3b },
+ purple = { r = 0x95, g = 0x7f, b = 0xb8 },
+ secondary = { r = 0x7a, g = 0xa8, b = 0x9f },
+ syntax_string = { r = 0x98, g = 0xbb, b = 0x6c },
+ syntax_number = { r = 0xd2, g = 0x7e, b = 0x99 },
+ syntax_keyword = { r = 0x95, g = 0x7f, b = 0xb8 },
+ syntax_function = { r = 0x7e, g = 0x9c, b = 0xd8 },
+ syntax_type = { r = 0x7a, g = 0xa8, b = 0x9f },
+ syntax_variable = { r = 0xdc, g = 0xd7, b = 0xba },
+ syntax_tag = { r = 0x7f, g = 0xb4, b = 0xca },
+ diff_add_bg = { r = 0x2b, g = 0x33, b = 0x28 },
+ diff_remove_bg = { r = 0x43, g = 0x24, b = 0x2b },
+ } },
+ { name = 'Kanagawa Dragon', palette = {
+ canvas_fg = { r = 0xc5, g = 0xc9, b = 0xc5 },
+ canvas_bg = { r = 0x18, g = 0x16, b = 0x16 },
+ surface = { r = 0x0d, g = 0x0c, b = 0x0c },
+ accent = { r = 0x8b, g = 0xa4, b = 0xb0 },
+ muted = { r = 0x73, g = 0x7c, b = 0x73 },
+ success = { r = 0x98, g = 0xbb, b = 0x6c },
+ err = { r = 0xe8, g = 0x24, b = 0x24 },
+ warn = { r = 0xff, g = 0x9e, b = 0x3b },
+ purple = { r = 0x89, g = 0x92, b = 0xa7 },
+ secondary = { r = 0x8e, g = 0xa4, b = 0xa2 },
+ syntax_string = { r = 0x8a, g = 0x9a, b = 0x7b },
+ syntax_number = { r = 0xa2, g = 0x92, b = 0xa3 },
+ syntax_keyword = { r = 0x89, g = 0x92, b = 0xa7 },
+ syntax_function = { r = 0x8b, g = 0xa4, b = 0xb0 },
+ syntax_type = { r = 0x8e, g = 0xa4, b = 0xa2 },
+ syntax_variable = { r = 0xc5, g = 0xc9, b = 0xc5 },
+ syntax_tag = { r = 0x94, g = 0x9f, b = 0xb5 },
+ diff_add_bg = { r = 0x2b, g = 0x33, b = 0x28 },
+ diff_remove_bg = { r = 0x43, g = 0x24, b = 0x2b },
+ } },
+ { name = 'Kanagawa Lotus', palette = {
+ canvas_fg = { r = 0x54, g = 0x54, b = 0x64 },
+ canvas_bg = { r = 0xf2, g = 0xec, b = 0xbc },
+ surface = { r = 0xd5, g = 0xce, b = 0xa3 },
+ accent = { r = 0x4d, g = 0x69, b = 0x9b },
+ muted = { r = 0x8a, g = 0x89, b = 0x80 },
+ success = { r = 0x6f, g = 0x89, b = 0x4e },
+ err = { r = 0xe8, g = 0x24, b = 0x24 },
+ warn = { r = 0xe9, g = 0x8a, b = 0x00 },
+ purple = { r = 0x62, g = 0x4c, b = 0x83 },
+ secondary = { r = 0x59, g = 0x7b, b = 0x75 },
+ syntax_string = { r = 0x6f, g = 0x89, b = 0x4e },
+ syntax_number = { r = 0xb3, g = 0x5b, b = 0x79 },
+ syntax_keyword = { r = 0x62, g = 0x4c, b = 0x83 },
+ syntax_function = { r = 0x4d, g = 0x69, b = 0x9b },
+ syntax_type = { r = 0x59, g = 0x7b, b = 0x75 },
+ syntax_variable = { r = 0x54, g = 0x54, b = 0x64 },
+ syntax_tag = { r = 0x66, g = 0x93, b = 0xbf },
+ diff_add_bg = { r = 0xb7, g = 0xd0, b = 0xae },
+ diff_remove_bg = { r = 0xd9, g = 0xa5, b = 0x94 },
+ } },
+}
+
+local function blend(base, accent, percent)
+ local q = 100 - percent
+ return {
+ r = math.floor((base.r * q + accent.r * percent) / 100),
+ g = math.floor((base.g * q + accent.g * percent) / 100),
+ b = math.floor((base.b * q + accent.b * percent) / 100),
+ }
+end
+
+local function fg(color)
+ return string.format("\27[38;2;%d;%d;%dm", color.r, color.g, color.b)
+end
+
+local function bg(color)
+ return string.format("\27[48;2;%d;%d;%dm", color.r, color.g, color.b)
+end
+
+local function derive(p)
+ local values = {
+ canvas_fg = fg(p.canvas_fg),
+ canvas_bg = bg(p.canvas_bg),
+ dim = fg(p.muted),
+ assistant = fg(p.canvas_fg),
+ user = fg(p.canvas_fg),
+ tool = fg(p.accent),
+ footer = fg(p.muted),
+ tool_text = fg(p.canvas_fg),
+ tool_pending = fg(p.muted),
+ tool_success = fg(p.success),
+ tool_error = fg(p.err),
+ compaction_header = fg(p.warn),
+ thinking_header = fg(p.purple),
+ cursor = "\27[7m",
+ err = fg(p.err),
+ welcome = fg(p.accent),
+ waiting_peak = fg(p.canvas_fg),
+ waiting_near = fg(p.secondary),
+ waiting_far = fg(p.muted),
+ err_diff = fg(p.err),
+ add_diff = fg(p.success),
+ thinking = fg(p.muted),
+ compaction = fg(p.muted),
+
+ user_bg = bg(p.surface),
+ tool_pending_bg = bg(p.surface),
+ tool_success_bg = bg(blend(p.canvas_bg, p.success, 20)),
+ tool_error_bg = bg(blend(p.canvas_bg, p.err, 20)),
+ diff_remove_bg = bg(p.diff_remove_bg or blend(p.canvas_bg, p.err, 20)),
+ diff_add_bg = bg(p.diff_add_bg or blend(p.canvas_bg, p.success, 20)),
+ user_text = fg(p.canvas_fg),
+ tool_header = fg(p.accent),
+
+ syntax_base = fg(p.canvas_fg),
+ syntax_comment = fg(p.muted),
+ syntax_string = fg(p.syntax_string),
+ syntax_number = fg(p.syntax_number),
+ syntax_keyword = fg(p.syntax_keyword),
+ syntax_function = fg(p.syntax_function),
+ syntax_type = fg(p.syntax_type),
+ syntax_variable = fg(p.syntax_variable),
+ syntax_tag = fg(p.syntax_tag),
+
+ reset = "\27[0m",
+ }
+ return values
+end
+
+local M = {}
+
+function M.activate()
+ local panto = require("panto")
+ for _, item in ipairs(palettes) do
+ local exists = pcall(panto.ext.style, item.name)
+ if not exists then
+ panto.ext.style(item.name, derive(item.palette))
+ end
+ end
+end
+
+function M.values(name)
+ for _, item in ipairs(palettes) do
+ if item.name == name then
+ return derive(item.palette)
+ end
+ end
+end
+
+return { name = "themes.popular", activate = M.activate, values = M.values }
diff --git a/scripts/verify-against-core.lua b/scripts/verify-against-core.lua
new file mode 100644
index 0000000..f66bb74
--- /dev/null
+++ b/scripts/verify-against-core.lua
@@ -0,0 +1,50 @@
+-- Run this with the real Panto interpreter from pantograph:
+-- panto lua scripts/verify-against-core.lua
+--
+-- Compare the extension's computed snapshots directly with core so mismatches
+-- remain visible even when activation skips names already owned by core.
+
+local source = debug.getinfo(1, "S").source
+local scripts_dir = source:match("^@(.*)/[^/]+$") or "."
+local root = scripts_dir .. "/.."
+package.path = root .. "/?.lua;" .. package.path
+
+local panto = require("panto")
+local roles = {
+ "dim", "assistant", "user", "tool", "footer", "tool_text",
+ "tool_pending", "tool_success", "tool_error", "compaction_header",
+ "thinking_header", "cursor", "err", "welcome", "waiting_peak",
+ "waiting_near", "waiting_far", "err_diff", "add_diff", "thinking",
+ "compaction", "user_bg", "tool_pending_bg", "tool_success_bg",
+ "tool_error_bg", "diff_remove_bg", "diff_add_bg", "user_text",
+ "tool_header", "canvas_fg", "canvas_bg", "syntax_base",
+ "syntax_comment", "syntax_string", "syntax_number", "syntax_keyword",
+ "syntax_function", "syntax_type", "syntax_variable", "syntax_tag",
+ "reset",
+}
+local moved = {
+ "Tokyo Night", "Catppuccin Mocha", "Gruvbox Dark", "Solarized Light",
+ "Solarized Dark", "Nord", "Dracula", "Tokyo Storm", "Tokyo Moon",
+ "Tokyo Day", "Catppuccin Latte", "Catppuccin Frappe",
+ "Catppuccin Macchiato", "Gruvbox Dark Hard", "Gruvbox Dark Soft",
+ "Gruvbox Light", "Gruvbox Light Hard", "Gruvbox Light Soft",
+ "Dracula Soft",
+}
+local entry = require("panto-popular-themes.init")
+assert(entry.name == "themes.popular", "wrong extension entry")
+assert(type(entry.values) == "function", "missing extension values")
+
+-- Activation skips names already owned by core or another extension, so
+-- compare the generated snapshots directly instead of relying on a conflict.
+for _, name in ipairs(moved) do
+ local expected = panto.ext.style(name)
+ local actual = entry.values(name)
+ assert(actual, "extension has no theme " .. name)
+ for _, role in ipairs(roles) do
+ if expected[role] ~= actual[role] then
+ error("theme " .. name .. " role " .. role .. " mismatch", 0)
+ end
+ end
+end
+
+entry.activate()