Private
Public Access
0
0

feat(theme): finalize semantic color lift and fix light theme UI elements

This commit is contained in:
2026-06-05 00:29:27 -04:00
parent 7735b6cba7
commit 4041782776
15 changed files with 661 additions and 701 deletions
-35
View File
@@ -1,35 +0,0 @@
import os
import tomllib
from pathlib import Path
def compact_toml(path: Path):
with open(path, "rb") as f:
data = tomllib.load(f)
lines = []
# Write top level fields
if "name" in data: lines.append(f'name = "{data["name"]}"')
if "syntax_palette" in data: lines.append(f'syntax_palette = "{data["syntax_palette"]}"')
if "description" in data: lines.append(f'description = "{data["description"]}"')
lines.append("")
# Write colors table
if "colors" in data:
lines.append("[colors]")
colors = data["colors"]
# Find max key length for alignment
max_key = max(len(k) for k in colors.keys()) if colors else 0
# Sort keys to keep it predictable
for k in sorted(colors.keys()):
v = colors[k]
# Format as compact array [R, G, B]
val_str = f"[{v[0]:3}, {v[1]:3}, {v[2]:3}]"
lines.append(f"{k:<{max_key}} = {val_str}")
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
print(f"Compacted {path}")
if __name__ == "__main__":
for f in Path("themes").glob("*.toml"):
compact_toml(f)
-108
View File
@@ -1,108 +0,0 @@
import re
from pathlib import Path
# Mapping regex -> replacement
# Note: we use theme.get_color("semantic", alpha=...)
REPLACEMENTS = [
# status_error
(r'imgui\.ImVec4\(1\.0, 0\.0, 0\.0, 0\.2\)', 'theme.get_color("status_error", alpha=0.2)'),
(r'imgui\.ImVec4\(1, 0, 0, 1\)', 'theme.get_color("status_error")'),
(r'imgui\.ImVec4\(1, 0\.3, 0\.3, ([\d\.]+)\)', r'theme.get_color("status_error", alpha=\1)'),
(r'imgui\.ImVec4\(1, 0\.3, 0\.3, 1\)', 'theme.get_color("status_error")'),
(r'imgui\.ImVec4\(1, 0\.4, 0\.4, 1\)', 'theme.get_color("status_error")'),
(r'vec4\(255, 100, 100\)', 'theme.get_color("status_error")'),
(r'vec4\(255, 72, 64, ([\d\.]+)\)', r'theme.get_color("status_error", alpha=\1)'),
(r'vec4\(255, 72, 64\)', 'theme.get_color("status_error")'),
(r'vec4\(255, 77, 77\)', 'theme.get_color("status_error")'),
(r'vec4\(230, 51, 51\)', 'theme.get_color("status_error")'),
(r'vec4\(240, 80, 80\)', 'theme.get_color("status_error")'),
(r'vec4\(255, 120, 120\)', 'theme.get_color("status_error")'),
(r'vec4\(255, 50, 50, ([\d\.]+)\)', r'theme.get_color("status_error", alpha=\1)'),
(r'vec4\(255, 100, 100, ([\d\.]+)\)', r'theme.get_color("status_error", alpha=\1)'),
# status_warning
(r'imgui\.ImVec4\(1\.0, 0\.85, 0\.2, 0\.15\)', 'theme.get_color("status_warning", alpha=0.15)'),
(r'imgui\.ImVec4\(1\.0, 0\.85, 0\.2, 1\.0\)', 'theme.get_color("status_warning")'),
(r'imgui\.ImVec4\(1, 0\.5, 0, 1\)', 'theme.get_color("status_warning")'),
(r'imgui\.ImVec4\(1, 1, 0, 1\)', 'theme.get_color("status_warning")'),
(r'imgui\.ImVec4\(1\.0, 1\.0, 0\.0, 1\.0\)', 'theme.get_color("status_warning")'),
(r'imgui\.ImVec4\(1\.0, 0\.5, 0\.0, 1\.0\)', 'theme.get_color("status_warning")'),
(r'imgui\.ImVec4\(1\.0, 0\.3, 0\.0, 1\.0\)', 'theme.get_color("status_warning")'),
(r'vec4\(255, 152, 48\)', 'theme.get_color("status_warning")'),
(r'vec4\(255, 200, 100\)', 'theme.get_color("status_warning")'),
(r'vec4\(200, 200, 100\)', 'theme.get_color("status_warning")'),
(r'vec4\(255, 230, 77\)', 'theme.get_color("status_warning")'),
(r'vec4\(255, 255, 100\)', 'theme.get_color("status_warning")'),
(r'vec4\(240, 200, 80\)', 'theme.get_color("status_warning")'),
(r'vec4\(255, 150, 50\)', 'theme.get_color("status_warning")'),
(r'vec4\(200, 180, 100\)', 'theme.get_color("status_warning")'),
# status_success
(r'vec4\(80, 255, 80\)', 'theme.get_color("status_success")'),
(r'vec4\(80, 255, 80, ([\d\.]+)\)', r'theme.get_color("status_success", alpha=\1)'),
(r'imgui\.ImVec4\(0, 1, 0, 1\)', 'theme.get_color("status_success")'),
(r'imgui\.ImVec4\(0\.2, 0\.9, 0\.2, 1\)', 'theme.get_color("status_success")'),
(r'imgui\.ImVec4\(0\.2, 0\.8, 0\.2, 1\.0\)', 'theme.get_color("status_success")'),
(r'imgui\.ImVec4\(0\.39, 1\.0, 0\.39, ([\d\.]+)\)', r'theme.get_color("status_success", alpha=\1)'),
(r'imgui\.ImVec4\(0\.3, 0\.8, 0\.3, 1\)', 'theme.get_color("status_success")'),
(r'vec4\(100, 255, 100\)', 'theme.get_color("status_success")'),
(r'vec4\(120, 220, 120\)', 'theme.get_color("status_success")'),
(r'vec4\(0, 255, 0, ([\d\.]+)\)', r'theme.get_color("status_success", alpha=\1)'),
(r'vec4\(51, 230, 51\)', 'theme.get_color("status_success")'),
# status_info
(r'imgui\.ImVec4\(0, 1, 1, 1\)', 'theme.get_color("status_info")'),
(r'imgui\.ImVec4\(0\.3, 0\.8, 1, 1\)', 'theme.get_color("status_info")'),
(r'imgui\.ImVec4\(0\.4, 0\.6, 0\.7, 1\.0\)', 'theme.get_color("status_info")'),
(r'vec4\(77, 178, 255\)', 'theme.get_color("status_info")'),
(r'vec4\(100, 150, 180\)', 'theme.get_color("status_info")'),
# text_disabled
(r'imgui\.ImVec4\(0\.7, 0\.7, 0\.7, 1\)', 'theme.get_color("text_disabled")'),
(r'vec4\(180, 180, 180\)', 'theme.get_color("text_disabled")'),
(r'vec4\(160, 160, 160\)', 'theme.get_color("text_disabled")'),
(r'vec4\(200, 220, 160\)', 'theme.get_color("text_disabled")'),
# bubble backgrounds
(r'imgui\.ImVec4\(0\.15, 0\.14, 0\.10, 0\.7\)', 'theme.get_color("bubble_vendor", alpha=0.7)'),
(r'vec4\(50, 40, 20\)', 'theme.get_color("bubble_vendor")'),
# Slices
(r'vec4\(255, 165, 0, 0\.2\)', 'theme.get_color("slice_manual", alpha=0.2)'),
(r'vec4\(0, 255, 0, 0\.15\)', 'theme.get_color("slice_auto", alpha=0.15)'),
(r'vec4\(100, 100, 255, 0\.3\)', 'theme.get_color("slice_selection", alpha=0.3)'),
(r'imgui\.ImVec4\(1\.0, 0\.65, 0, 0\.2\)', 'theme.get_color("slice_manual", alpha=0.2)'),
(r'imgui\.ImVec4\(0, 1\.0, 0, 0\.1\)', 'theme.get_color("slice_auto", alpha=0.1)'),
(r'imgui\.ImVec4\(0\.4, 0\.4, 1\.0, 0\.3\)', 'theme.get_color("slice_selection", alpha=0.3)'),
(r'imgui\.ImVec4\(0, 1\.0, 0, 0\.15\)', 'theme.get_color("slice_auto", alpha=0.15)'),
(r'imgui\.ImVec4\(0, 0, 1\.0, 0\.15\)', 'theme.get_color("slice_selection", alpha=0.15)'),
(r'imgui\.ImVec4\(1\.0, 1\.0, 0, 0\.2\)', 'theme.get_color("status_warning", alpha=0.2)'),
]
def apply_lift(path: Path):
content = path.read_text(encoding="utf-8")
original = content
for pattern, replacement in REPLACEMENTS:
content = re.sub(pattern, replacement, content)
# Specialized replacements
# MMA status block
content = re.sub(r'if app\.mma_status == "idle": status_col = imgui\.ImVec4\(0\.7, 0\.7, 0\.7, 1\)',
'if app.mma_status == "idle": status_col = theme.get_color("text_disabled")', content)
content = re.sub(r'elif app\.mma_status == "running": status_col = vec4\(80, 255, 80\) if is_nerv else imgui\.ImVec4\(1, 1, 0, 1\)',
'elif app.mma_status == "running": status_col = theme.get_color("status_warning")', content)
content = re.sub(r'elif app\.mma_status == "done": status_col = imgui\.ImVec4\(0, 1, 0, 1\)',
'elif app.mma_status == "done": status_col = theme.get_color("status_success")', content)
content = re.sub(r'elif app\.mma_status == "error": status_col = vec4\(255, 72, 64\) if is_nerv else imgui\.ImVec4\(1, 0, 0, 1\)',
'elif app.mma_status == "error": status_col = theme.get_color("status_error")', content)
content = re.sub(r'elif app\.mma_status == "paused": status_col = imgui\.ImVec4\(1, 0\.5, 0, 1\)',
'elif app.mma_status == "paused": status_col = theme.get_color("status_warning")', content)
if content != original:
path.write_text(content, encoding="utf-8")
print(f"Lifted colors in {path}")
if __name__ == "__main__":
apply_lift(Path("src/gui_2.py"))
-82
View File
@@ -1,82 +0,0 @@
import os
import tomllib
import tomli_w
from pathlib import Path
# Get all keys from ThemePalette defaults
from src.theme_models import ThemePalette
default_palette = ThemePalette()
ALL_KEYS = list(default_palette.to_dict().keys())
DARK_SEMANTIC = {
"status_success": [80, 255, 80],
"status_warning": [255, 152, 48],
"status_error": [255, 72, 64],
"status_info": [0, 255, 255],
"bubble_user": [30, 45, 75],
"bubble_ai": [35, 65, 45],
"bubble_vendor": [65, 55, 30],
"bubble_system": [25, 25, 25],
"slice_manual": [255, 165, 0],
"slice_auto": [0, 255, 0],
"slice_selection": [100, 100, 255],
"diff_added": [51, 230, 51],
"diff_removed": [230, 51, 51],
"diff_header": [77, 178, 255],
}
LIGHT_SEMANTIC = {
"status_success": [40, 180, 40],
"status_warning": [200, 140, 0],
"status_error": [200, 40, 40],
"status_info": [40, 100, 200],
"bubble_user": [220, 230, 255],
"bubble_ai": [220, 255, 220],
"bubble_vendor": [255, 240, 200],
"bubble_system": [240, 240, 240],
"slice_manual": [255, 200, 0],
"slice_auto": [80, 255, 80],
"slice_selection": [180, 180, 255],
"diff_added": [40, 180, 40],
"diff_removed": [200, 40, 40],
"diff_header": [40, 100, 200],
}
def standardize_theme(path: Path):
with open(path, "rb") as f:
data = tomllib.load(f)
name = data.get("name", path.stem)
syntax = data.get("syntax_palette", "dark")
colors = data.get("colors", {})
is_light = "light" in name.lower() or syntax == "light"
semantic = LIGHT_SEMANTIC if is_light else DARK_SEMANTIC
# Merge semantic
for k, v in semantic.items():
if k not in colors:
colors[k] = v
# Ensure all keys are present (optional, but good for completeness)
# for k in ALL_KEYS:
# if k not in colors:
# # We use defaults from ThemePalette anyway, so no need to bloat
# pass
new_data = {
"name": name,
"syntax_palette": syntax,
"description": data.get("description", ""),
"colors": colors
}
with open(path, "wb") as f:
tomli_w.dump(new_data, f)
print(f"Standardized {path}")
if __name__ == "__main__":
themes_dir = Path("themes")
for f in themes_dir.glob("*.toml"):
standardize_theme(f)