template-processor: even simpler terminals color def.

This commit is contained in:
Lemmy
2026-01-21 20:29:38 -05:00
parent 090fe26056
commit b524eefeda
6 changed files with 131 additions and 379 deletions
+2 -66
View File
@@ -42,39 +42,7 @@
"selectionFg": "#cdd6f4",
"selectionBg": "#585b70",
"cursorText": "#1e1e2e",
"cursor": "#f5e0dc",
"composeCursor": "#f2cdcd",
"scrollbarThumb": "#585b70",
"split": "#6c7086",
"visualBell": "#313244",
"indexed": {
"16": "#fab387",
"17": "#f5e0dc"
},
"tabBar": {
"background": "#11111b",
"inactiveTabEdge": "#313244",
"activeTab": {
"bg": "#cba6f7",
"fg": "#11111b"
},
"inactiveTab": {
"bg": "#181825",
"fg": "#cdd6f4"
},
"inactiveTabHover": {
"bg": "#1e1e2e",
"fg": "#cdd6f4"
},
"newTab": {
"bg": "#313244",
"fg": "#cdd6f4"
},
"newTabHover": {
"bg": "#45475a",
"fg": "#cdd6f4"
}
}
"cursor": "#f5e0dc"
}
},
"light": {
@@ -120,39 +88,7 @@
"selectionFg": "#c6d0f5",
"selectionBg": "#626880",
"cursorText": "#303446",
"cursor": "#f2d5cf",
"composeCursor": "#dd7878",
"scrollbarThumb": "#acb0be",
"split": "#9ca0b0",
"visualBell": "#ccd0da",
"indexed": {
"16": "#fe640b",
"17": "#dc8a78"
},
"tabBar": {
"background": "#dce0e8",
"inactiveTabEdge": "#ccd0da",
"activeTab": {
"bg": "#8839ef",
"fg": "#dce0e8"
},
"inactiveTab": {
"bg": "#e6e9ef",
"fg": "#4c4f69"
},
"inactiveTabHover": {
"bg": "#eff1f5",
"fg": "#4c4f69"
},
"newTab": {
"bg": "#ccd0da",
"fg": "#4c4f69"
},
"newTabHover": {
"bg": "#bcc0cc",
"fg": "#4c4f69"
}
}
"cursor": "#f2d5cf"
}
}
}
+2 -54
View File
@@ -42,33 +42,7 @@
"selectionFg": "#ffffff",
"selectionBg": "#44475a",
"cursorText": "#282a36",
"cursor": "#f8f8f2",
"composeCursor": "#ffb86c",
"scrollbarThumb": "#44475a",
"split": "#6272a4",
"tabBar": {
"background": "#282a36",
"activeTab": {
"bg": "#bd93f9",
"fg": "#282a36"
},
"inactiveTab": {
"bg": "#282a36",
"fg": "#f8f8f2"
},
"inactiveTabHover": {
"bg": "#6272a4",
"fg": "#f8f8f2"
},
"newTab": {
"bg": "#282a36",
"fg": "#f8f8f2"
},
"newTabHover": {
"bg": "#ff79c6",
"fg": "#f8f8f2"
}
}
"cursor": "#f8f8f2"
}
},
"light": {
@@ -114,33 +88,7 @@
"selectionFg": "#ffffff",
"selectionBg": "#6272a4",
"cursorText": "#ffffff",
"cursor": "#282a36",
"composeCursor": "#ffb86c",
"scrollbarThumb": "#44475a",
"split": "#44475a",
"tabBar": {
"background": "#f8f8f2",
"activeTab": {
"bg": "#8332f4",
"fg": "#f8f8f2"
},
"inactiveTab": {
"bg": "#f8f8f2",
"fg": "#282a36"
},
"inactiveTabHover": {
"bg": "#e6e6ea",
"fg": "#44475a"
},
"newTab": {
"bg": "#f8f8f2",
"fg": "#282a36"
},
"newTabHover": {
"bg": "#ff1399",
"fg": "#f8f8f2"
}
}
"cursor": "#282a36"
}
}
}
+19 -71
View File
@@ -42,33 +42,7 @@
"selectionFg": "#ebfafa",
"selectionBg": "#bf4f8e",
"cursorText": "#37f499",
"cursor": "#f8f8f2",
"composeCursor": "#f7c67f",
"scrollbarThumb": "#37f499",
"split": "#a48cf2",
"tabBar": {
"background": "#212337",
"activeTab": {
"bg": "#37f499",
"fg": "#212337"
},
"inactiveTab": {
"bg": "#212337",
"fg": "#04d1f9"
},
"inactiveTabHover": {
"bg": "#37f499",
"fg": "#212337"
},
"newTab": {
"bg": "#212337",
"fg": "#ebfafa"
},
"newTabHover": {
"bg": "#37f499",
"fg": "#ebfafa"
}
}
"cursor": "#f8f8f2"
}
},
"light": {
@@ -90,57 +64,31 @@
"mOnHover": "#171928",
"terminal": {
"normal": {
"black": "#ebfafa # black \u2192 white",
"red": "#ba1a1a # red (darker)",
"green": "#1a7f4c # green (darker)",
"yellow": "#9e8c13 # yellow (darker)",
"blue": "#3a3e8c # blue (darker)",
"magenta": "#8c2a6c # magenta (darker)",
"cyan": "#1a6c8c # cyan (darker)",
"white": "#212337 # white \u2192 black"
"black": "#ebfafa",
"red": "#ba1a1a",
"green": "#1a7f4c",
"yellow": "#9e8c13",
"blue": "#3a3e8c",
"magenta": "#8c2a6c",
"cyan": "#1a6c8c",
"white": "#212337"
},
"bright": {
"black": "#7081d0 # keep as accent",
"red": "#d23b3b # lighter red",
"green": "#37f499 # original green",
"yellow": "#e9f941 # original yellow",
"blue": "#9071f4 # original blue",
"magenta": "#f265b5 # original magenta",
"cyan": "#04d1f9 # original cyan",
"white": "#212337 # black"
"black": "#7081d0",
"red": "#d23b3b",
"green": "#37f499",
"yellow": "#e9f941",
"blue": "#9071f4",
"magenta": "#f265b5",
"cyan": "#04d1f9",
"white": "#212337"
},
"foreground": "#212337",
"background": "#ebfafa",
"selectionFg": "#212337",
"selectionBg": "#bf4f8e",
"cursorText": "#1a7f4c",
"cursor": "#212337",
"composeCursor": "#B88F4F",
"scrollbarThumb": "#2FC280",
"split": "#8A71D0",
"tabBar": {
"background": "#f2f4f8",
"activeTab": {
"bg": "#2FC280",
"fg": "#f2f4f8"
},
"inactiveTab": {
"bg": "#f2f4f8",
"fg": "#039EBE"
},
"inactiveTabHover": {
"bg": "#2FC280",
"fg": "#f2f4f8"
},
"newTab": {
"bg": "#f2f4f8",
"fg": "#171928"
},
"newTabHover": {
"bg": "#2FC280",
"fg": "#171928"
}
}
"cursor": "#212337"
}
}
}
}
@@ -42,31 +42,7 @@
"selectionFg": "#c0caf5",
"selectionBg": "#283457",
"cursorText": "#1a1b26",
"cursor": "#c0caf5",
"tabBar": {
"background": "#191b28",
"inactiveTabEdge": "#16161e",
"activeTab": {
"bg": "#1a1b26",
"fg": "#7aa2f7"
},
"inactiveTab": {
"bg": "#16161e",
"fg": "#545c7e"
},
"inactiveTabHover": {
"bg": "#16161e",
"fg": "#7aa2f7"
},
"newTab": {
"bg": "#191b28",
"fg": "#7aa2f7"
},
"newTabHover": {
"bg": "#7aa2f7",
"fg": "#16161e"
}
}
"cursor": "#c0caf5"
}
},
"light": {
@@ -112,31 +88,7 @@
"selectionFg": "#3760bf",
"selectionBg": "#99a7df",
"cursorText": "#e1e2e7",
"cursor": "#3760bf",
"tabBar": {
"background": "#191b28",
"inactiveTabEdge": "#e9e9ec",
"activeTab": {
"bg": "#e1e2e7",
"fg": "#2e7de9"
},
"inactiveTab": {
"bg": "#e9e9ec",
"fg": "#8990b3"
},
"inactiveTabHover": {
"bg": "#e9e9ec",
"fg": "#2e7de9"
},
"newTab": {
"bg": "#191b28",
"fg": "#2e7de9"
},
"newTabHover": {
"bg": "#2e7de9",
"fg": "#e9e9ec"
}
}
"cursor": "#3760bf"
}
}
}
+99 -137
View File
@@ -7,14 +7,29 @@ Supports: foot, ghostty, kitty, alacritty, wezterm
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Optional
from dataclasses import dataclass
def darken_hex(color: str, percent: float) -> str:
"""Darken a hex color by a percentage (0-100)."""
hex_color = color.lstrip("#")
r = int(hex_color[0:2], 16)
g = int(hex_color[2:4], 16)
b = int(hex_color[4:6], 16)
factor = 1 - (percent / 100)
r = max(0, int(r * factor))
g = max(0, int(g * factor))
b = max(0, int(b * factor))
return f"#{r:02x}{g:02x}{b:02x}"
@dataclass
class TerminalColors:
"""Terminal color scheme data."""
# Base colors (from JSON)
foreground: str
background: str
cursor: str
@@ -24,36 +39,61 @@ class TerminalColors:
normal: dict[str, str] # black, red, green, yellow, blue, magenta, cyan, white
bright: dict[str, str] # same keys
# Extended colors (optional, for wezterm etc.)
compose_cursor: Optional[str] = None
scrollbar_thumb: Optional[str] = None
split: Optional[str] = None
visual_bell: Optional[str] = None
# Indexed colors (optional)
indexed: dict[int, str] = field(default_factory=dict)
# Tab bar colors (optional, for wezterm)
tab_bar: Optional[dict] = None
# WezTerm extended colors (always derived)
compose_cursor: str
scrollbar_thumb: str
split: str
visual_bell: str
indexed: dict[int, str]
tab_bar: dict
@classmethod
def from_dict(cls, data: dict) -> "TerminalColors":
"""Create TerminalColors from a dictionary (JSON schema format)."""
def from_dict(cls, data: dict, scheme: dict) -> "TerminalColors":
"""Create TerminalColors with auto-derived WezTerm extended colors.
Args:
data: Terminal color data (foreground, background, cursor, normal, bright, etc.)
scheme: Scheme UI colors (mPrimary, mOnPrimary, mSecondary)
"""
# Base colors
foreground = data["foreground"]
background = data["background"]
cursor = data["cursor"]
cursor_text = data.get("cursorText", background)
selection_fg = data.get("selectionFg", foreground)
selection_bg = data.get("selectionBg", "#585b70")
normal = data["normal"]
bright = data["bright"]
# Scheme accent colors
m_primary = scheme.get("mPrimary", cursor)
m_on_primary = scheme.get("mOnPrimary", cursor_text)
m_secondary = scheme.get("mSecondary", normal["yellow"])
return cls(
foreground=data["foreground"],
background=data["background"],
cursor=data["cursor"],
cursor_text=data.get("cursorText", data["background"]),
selection_fg=data.get("selectionFg", data["foreground"]),
selection_bg=data.get("selectionBg", data.get("cursor", "#585b70")),
normal=data["normal"],
bright=data["bright"],
compose_cursor=data.get("composeCursor"),
scrollbar_thumb=data.get("scrollbarThumb"),
split=data.get("split"),
visual_bell=data.get("visualBell"),
indexed=data.get("indexed", {}),
tab_bar=data.get("tabBar"),
foreground=foreground,
background=background,
cursor=cursor,
cursor_text=cursor_text,
selection_fg=selection_fg,
selection_bg=selection_bg,
normal=normal,
bright=bright,
# Derived WezTerm colors
compose_cursor=cursor,
scrollbar_thumb=selection_bg,
split=bright["black"],
visual_bell=normal["black"],
indexed={16: m_secondary, 17: cursor},
tab_bar={
"background": darken_hex(background, 10),
"inactiveTabEdge": selection_bg,
"activeTab": {"bg": m_primary, "fg": m_on_primary},
"inactiveTab": {"bg": darken_hex(background, 5), "fg": foreground},
"inactiveTabHover": {"bg": background, "fg": foreground},
"newTab": {"bg": selection_bg, "fg": foreground},
"newTabHover": {"bg": bright["black"], "fg": foreground},
},
)
@@ -188,6 +228,7 @@ class TerminalGenerator:
def generate_wezterm(self) -> str:
"""Generate wezterm theme (full TOML with metadata)."""
c = self.colors
tb = c.tab_bar
lines = ["[colors]"]
# Ansi colors array
@@ -196,7 +237,6 @@ class TerminalGenerator:
lines.append(f' "{self._ensure_hash(c.normal[name])}",')
lines.append("]")
# Primary
lines.append(f'background = "{self._ensure_hash(c.background)}"')
# Brights array
@@ -206,122 +246,44 @@ class TerminalGenerator:
lines.append("]")
# Extended colors
if c.compose_cursor:
lines.append(f'compose_cursor = "{self._ensure_hash(c.compose_cursor)}"')
lines.append(f'compose_cursor = "{self._ensure_hash(c.compose_cursor)}"')
lines.append(f'cursor_bg = "{self._ensure_hash(c.cursor)}"')
lines.append(f'cursor_border = "{self._ensure_hash(c.cursor)}"')
lines.append(f'cursor_fg = "{self._ensure_hash(c.cursor_text)}"')
lines.append(f'foreground = "{self._ensure_hash(c.foreground)}"')
if c.scrollbar_thumb:
lines.append(f'scrollbar_thumb = "{self._ensure_hash(c.scrollbar_thumb)}"')
lines.append(f'scrollbar_thumb = "{self._ensure_hash(c.scrollbar_thumb)}"')
lines.append(f'selection_bg = "{self._ensure_hash(c.selection_bg)}"')
lines.append(f'selection_fg = "{self._ensure_hash(c.selection_fg)}"')
if c.split:
lines.append(f'split = "{self._ensure_hash(c.split)}"')
if c.visual_bell:
lines.append(f'visual_bell = "{self._ensure_hash(c.visual_bell)}"')
lines.append(f'split = "{self._ensure_hash(c.split)}"')
lines.append(f'visual_bell = "{self._ensure_hash(c.visual_bell)}"')
# Indexed colors
if c.indexed:
lines.append("")
lines.append("[colors.indexed]")
for idx, color in sorted(c.indexed.items()):
lines.append(f'{idx} = "{self._ensure_hash(color)}"')
# Tab bar
lines.append("")
lines.append("[colors.tab_bar]")
lines.append(f'background = "{self._ensure_hash(tb["background"])}"')
lines.append(f'inactive_tab_edge = "{self._ensure_hash(tb["inactiveTabEdge"])}"')
for section, key in [
("activeTab", "active_tab"),
("inactiveTab", "inactive_tab"),
("inactiveTabHover", "inactive_tab_hover"),
("newTab", "new_tab"),
("newTabHover", "new_tab_hover"),
]:
lines.append("")
lines.append("[colors.indexed]")
for idx, color in sorted(c.indexed.items()):
lines.append(f'{idx} = "{self._ensure_hash(color)}"')
# Tab bar (optional)
if c.tab_bar:
tb = c.tab_bar
lines.append("")
lines.append("[colors.tab_bar]")
if "background" in tb:
lines.append(f'background = "{self._ensure_hash(tb["background"])}"')
if "inactiveTabEdge" in tb:
lines.append(
f'inactive_tab_edge = "{self._ensure_hash(tb["inactiveTabEdge"])}"'
)
# Active tab
if "activeTab" in tb:
at = tb["activeTab"]
lines.append("")
lines.append("[colors.tab_bar.active_tab]")
lines.append(f'bg_color = "{self._ensure_hash(at.get("bg", c.cursor))}"')
lines.append(
f'fg_color = "{self._ensure_hash(at.get("fg", c.cursor_text))}"'
)
lines.append('intensity = "Normal"')
lines.append("italic = false")
lines.append("strikethrough = false")
lines.append('underline = "None"')
# Inactive tab
if "inactiveTab" in tb:
it = tb["inactiveTab"]
lines.append("")
lines.append("[colors.tab_bar.inactive_tab]")
lines.append(
f'bg_color = "{self._ensure_hash(it.get("bg", c.background))}"'
)
lines.append(
f'fg_color = "{self._ensure_hash(it.get("fg", c.foreground))}"'
)
lines.append('intensity = "Normal"')
lines.append("italic = false")
lines.append("strikethrough = false")
lines.append('underline = "None"')
# Inactive tab hover
if "inactiveTabHover" in tb:
ith = tb["inactiveTabHover"]
lines.append("")
lines.append("[colors.tab_bar.inactive_tab_hover]")
lines.append(
f'bg_color = "{self._ensure_hash(ith.get("bg", c.background))}"'
)
lines.append(
f'fg_color = "{self._ensure_hash(ith.get("fg", c.foreground))}"'
)
lines.append('intensity = "Normal"')
lines.append("italic = false")
lines.append("strikethrough = false")
lines.append('underline = "None"')
# New tab
if "newTab" in tb:
nt = tb["newTab"]
lines.append("")
lines.append("[colors.tab_bar.new_tab]")
lines.append(
f'bg_color = "{self._ensure_hash(nt.get("bg", c.selection_bg))}"'
)
lines.append(
f'fg_color = "{self._ensure_hash(nt.get("fg", c.foreground))}"'
)
lines.append('intensity = "Normal"')
lines.append("italic = false")
lines.append("strikethrough = false")
lines.append('underline = "None"')
# New tab hover
if "newTabHover" in tb:
nth = tb["newTabHover"]
lines.append("")
lines.append("[colors.tab_bar.new_tab_hover]")
lines.append(
f'bg_color = "{self._ensure_hash(nth.get("bg", c.bright["black"]))}"'
)
lines.append(
f'fg_color = "{self._ensure_hash(nth.get("fg", c.foreground))}"'
)
lines.append('intensity = "Normal"')
lines.append("italic = false")
lines.append("strikethrough = false")
lines.append('underline = "None"')
lines.append(f"[colors.tab_bar.{key}]")
lines.append(f'bg_color = "{self._ensure_hash(tb[section]["bg"])}"')
lines.append(f'fg_color = "{self._ensure_hash(tb[section]["fg"])}"')
lines.append('intensity = "Normal"')
lines.append("italic = false")
lines.append("strikethrough = false")
lines.append('underline = "None"')
# Metadata
lines.append("")
@@ -344,7 +344,13 @@ def main() -> int:
return 0
try:
terminal_colors = TerminalColors.from_dict(mode_data["terminal"])
# Extract scheme UI colors for derivation (mPrimary, mOnPrimary, mSecondary)
scheme_colors = {
"mPrimary": mode_data.get("mPrimary"),
"mOnPrimary": mode_data.get("mOnPrimary"),
"mSecondary": mode_data.get("mSecondary"),
}
terminal_colors = TerminalColors.from_dict(mode_data["terminal"], scheme_colors)
generator = TerminalGenerator(terminal_colors)
for terminal_id, output_path in terminal_outputs.items():