Private
Public Access
0
0

style(themes): compact TOML formatting and lift semantic colors

This commit is contained in:
2026-06-05 00:02:46 -04:00
parent 06e305aba6
commit 7ea52cbbe8
16 changed files with 915 additions and 516 deletions
+28 -3
View File
@@ -16,7 +16,7 @@ paths = [
"C:/projects/manual_slop/manual_slop.toml", "C:/projects/manual_slop/manual_slop.toml",
"C:/projects/Pikuma/ps1-ai/pikuma_ps1.toml", "C:/projects/Pikuma/ps1-ai/pikuma_ps1.toml",
] ]
active = "C:/projects/Pikuma/ps1-ai/pikuma_ps1.toml" active = "C:/projects/gencpp/.ai/gencpp_sloppy.toml"
[gui] [gui]
separate_message_panel = true separate_message_panel = true
@@ -62,13 +62,38 @@ Diagnostics = false
"Undo/Redo History" = false "Undo/Redo History" = false
[theme] [theme]
palette = "moss" palette = "gruvbox_dark"
font_path = "C:/projects/manual_slop/assets/fonts/MapleMono-Regular.ttf" font_path = "C:/projects/manual_slop/assets/fonts/MapleMono-Regular.ttf"
font_size = 20.0 font_size = 20.0
scale = 1.0 scale = 1.0199999809265137
transparency = 1.0 transparency = 1.0
child_transparency = 1.0 child_transparency = 1.0
[theme.tone_mapping.moss]
brightness = 1.059999942779541
contrast = 0.5799999833106995
gamma = 1.059999942779541
[theme.tone_mapping.Binks]
brightness = 0.5600000023841858
contrast = 0.7900000214576721
gamma = 2.2100000381469727
[theme.tone_mapping.gray_variations]
brightness = 0.7699999809265137
contrast = 0.7200000286102295
gamma = 0.6899999976158142
[theme.tone_mapping.solarized_light]
brightness = 0.6899999976158142
contrast = 0.8600000143051147
gamma = 0.7699999809265137
[theme.tone_mapping."10x Dark"]
brightness = 0.75
contrast = 1.0
gamma = 1.0
[mma] [mma]
max_workers = 4 max_workers = 4
+28 -28
View File
@@ -44,20 +44,20 @@ Collapsed=0
DockId=0x00000010,0 DockId=0x00000010,0
[Window][Message] [Window][Message]
Pos=1196,28 Pos=1216,28
Size=1362,1548 Size=1603,1709
Collapsed=0 Collapsed=0
DockId=0x00000006,0 DockId=0x00000006,0
[Window][Response] [Window][Response]
Pos=0,28 Pos=0,28
Size=1194,1548 Size=1214,1709
Collapsed=0 Collapsed=0
DockId=0x00000010,5 DockId=0x00000010,5
[Window][Tool Calls] [Window][Tool Calls]
Pos=1196,28 Pos=1216,28
Size=1362,1548 Size=1603,1709
Collapsed=0 Collapsed=0
DockId=0x00000006,3 DockId=0x00000006,3
@@ -76,10 +76,10 @@ Collapsed=0
DockId=0xAFC85805,2 DockId=0xAFC85805,2
[Window][Theme] [Window][Theme]
Pos=0,28 Pos=0,29
Size=1194,1548 Size=737,1195
Collapsed=0 Collapsed=0
DockId=0x00000010,0 DockId=0x00000010,1
[Window][Text Viewer - Entry #7] [Window][Text Viewer - Entry #7]
Pos=379,324 Pos=379,324
@@ -105,26 +105,26 @@ Collapsed=0
DockId=0x0000000D,0 DockId=0x0000000D,0
[Window][Discussion Hub] [Window][Discussion Hub]
Pos=1196,28 Pos=739,29
Size=1362,1548 Size=974,1195
Collapsed=0 Collapsed=0
DockId=0x00000006,1 DockId=0x00000006,0
[Window][Operations Hub] [Window][Operations Hub]
Pos=0,28 Pos=0,28
Size=1194,1548 Size=1214,1709
Collapsed=0 Collapsed=0
DockId=0x00000010,4 DockId=0x00000010,4
[Window][Files & Media] [Window][Files & Media]
Pos=0,28 Pos=0,29
Size=1194,1548 Size=737,1195
Collapsed=0 Collapsed=0
DockId=0x00000010,2 DockId=0x00000010,2
[Window][AI Settings] [Window][AI Settings]
Pos=0,28 Pos=0,29
Size=1194,1548 Size=737,1195
Collapsed=0 Collapsed=0
DockId=0x00000010,3 DockId=0x00000010,3
@@ -140,8 +140,8 @@ Collapsed=0
DockId=0x00000006,2 DockId=0x00000006,2
[Window][Log Management] [Window][Log Management]
Pos=1196,28 Pos=1216,28
Size=1362,1548 Size=1603,1709
Collapsed=0 Collapsed=0
DockId=0x00000006,2 DockId=0x00000006,2
@@ -409,10 +409,10 @@ Collapsed=0
DockId=0x00000006,1 DockId=0x00000006,1
[Window][Project Settings] [Window][Project Settings]
Pos=0,28 Pos=0,29
Size=1194,1548 Size=737,1195
Collapsed=0 Collapsed=0
DockId=0x00000010,1 DockId=0x00000010,0
[Window][Undo/Redo History] [Window][Undo/Redo History]
Pos=678,28 Pos=678,28
@@ -682,10 +682,10 @@ Column 1 Width=80
Column 2 Width=150 Column 2 Width=150
[Table][0x7804123E,3] [Table][0x7804123E,3]
RefScale=20 RefScale=21
Column 0 Width=99 Column 0 Width=103
Column 1 Weight=1.0000 Column 1 Weight=1.0000
Column 2 Width=627 Column 2 Width=658
[Table][0x09B0112E,3] [Table][0x09B0112E,3]
RefScale=20 RefScale=20
@@ -829,13 +829,13 @@ Column 4 Weight=1.0000
DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02 DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02
DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,28 Size=2558,1548 Split=X DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,29 Size=1713,1195 Split=X
DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2357,1183 Split=X DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2357,1183 Split=X
DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2 DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2
DockNode ID=0x00000005 Parent=0x0000000B SizeRef=1194,1681 Split=Y Selected=0x3F1379AF DockNode ID=0x00000005 Parent=0x0000000B SizeRef=737,1681 Split=Y Selected=0x3F1379AF
DockNode ID=0x00000010 Parent=0x00000005 SizeRef=983,1140 CentralNode=1 Selected=0x418C7449 DockNode ID=0x00000010 Parent=0x00000005 SizeRef=983,1140 CentralNode=1 Selected=0x8CA2375C
DockNode ID=0x00000011 Parent=0x00000005 SizeRef=983,184 Selected=0x432BAE4E DockNode ID=0x00000011 Parent=0x00000005 SizeRef=983,184 Selected=0x432BAE4E
DockNode ID=0x00000006 Parent=0x0000000B SizeRef=1362,1681 Selected=0x2C0206CE DockNode ID=0x00000006 Parent=0x0000000B SizeRef=974,1681 Selected=0x6F2B5B04
DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6 DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6
DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=488,1183 Selected=0x3AEC3498 DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=488,1183 Selected=0x3AEC3498
+35
View File
@@ -0,0 +1,35 @@
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
@@ -0,0 +1,108 @@
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
@@ -0,0 +1,82 @@
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)
+104 -104
View File
@@ -80,22 +80,23 @@ def hide_tk_root() -> Tk:
def vec4(r: float, g: float, b: float, a: float = 1.0) -> imgui.ImVec4: def vec4(r: float, g: float, b: float, a: float = 1.0) -> imgui.ImVec4:
return imgui.ImVec4(r/255.0, g/255.0, b/255.0, a) return imgui.ImVec4(r/255.0, g/255.0, b/255.0, a)
C_OUT: imgui.ImVec4 = vec4(100, 200, 255) # Standard Color Constants (now bound to the theming system)
C_IN: imgui.ImVec4 = vec4(140, 255, 160) def C_OUT() -> imgui.ImVec4: return theme.get_color("status_info")
C_REQ: imgui.ImVec4 = vec4(255, 220, 100) def C_IN() -> imgui.ImVec4: return theme.get_color("status_success")
C_RES: imgui.ImVec4 = vec4(180, 255, 180) def C_REQ() -> imgui.ImVec4: return theme.get_color("status_warning")
C_TC: imgui.ImVec4 = vec4(255, 180, 80) def C_RES() -> imgui.ImVec4: return theme.get_color("status_success")
C_TR: imgui.ImVec4 = vec4(180, 220, 255) def C_TC() -> imgui.ImVec4: return theme.get_color("status_warning")
C_TRS: imgui.ImVec4 = vec4(200, 180, 255) def C_TR() -> imgui.ImVec4: return theme.get_color("status_info")
C_LBL: imgui.ImVec4 = vec4(180, 180, 180) def C_TRS() -> imgui.ImVec4: return theme.get_color("status_info")
C_VAL: imgui.ImVec4 = vec4(220, 220, 220) def C_LBL() -> imgui.ImVec4: return theme.get_color("text_disabled")
C_KEY: imgui.ImVec4 = vec4(140, 200, 255) def C_VAL() -> imgui.ImVec4: return theme.get_color("text")
C_NUM: imgui.ImVec4 = vec4(180, 255, 180) def C_KEY() -> imgui.ImVec4: return theme.get_color("status_info")
C_TRM: imgui.ImVec4 = vec4(160, 160, 150) # Trimmed/Cruft def C_NUM() -> imgui.ImVec4: return theme.get_color("status_success")
C_SUB: imgui.ImVec4 = vec4(220, 200, 120) def C_TRM() -> imgui.ImVec4: return theme.get_color("text_disabled")
def C_SUB() -> imgui.ImVec4: return theme.get_color("text_disabled")
DIR_COLORS: dict[str, imgui.ImVec4] = {"OUT": C_OUT, "IN": C_IN} DIR_COLORS: dict[str, typing.Callable[[], imgui.ImVec4]] = {"OUT": C_OUT, "IN": C_IN}
KIND_COLORS: dict[str, imgui.ImVec4] = {"request": C_REQ, "response": C_RES, "tool_call": C_TC, "tool_result": C_TR, "tool_result_send": C_TRS} KIND_COLORS: dict[str, typing.Callable[[], imgui.ImVec4]] = {"request": C_REQ, "response": C_RES, "tool_call": C_TC, "tool_result": C_TR, "tool_result_send": C_TRS}
HEAVY_KEYS: set[str] = {"message", "text", "script", "output", "content"} HEAVY_KEYS: set[str] = {"message", "text", "script", "output", "content"}
def render_text_viewer(app: App, label: str, content: str, text_type: str = 'text', force_open: bool = False, id_suffix: str = "") -> None: def render_text_viewer(app: App, label: str, content: str, text_type: str = 'text', force_open: bool = False, id_suffix: str = "") -> None:
@@ -783,7 +784,7 @@ class App:
if self.perf_profiling_enabled: self.perf_monitor.start_component("_gui_func") if self.perf_profiling_enabled: self.perf_monitor.start_component("_gui_func")
try: try:
if self.is_viewing_prior_session: if self.is_viewing_prior_session:
with imscope.style_color(imgui.Col_.window_bg, vec4(50, 40, 20)): with imscope.style_color(imgui.Col_.window_bg, theme.get_color("bubble_vendor")):
render_main_interface(self) render_main_interface(self)
else: else:
render_main_interface(self) render_main_interface(self)
@@ -1378,9 +1379,9 @@ def render_usage_analytics_panel(app: App) -> None:
if app.controller.rag_config and app.controller.rag_config.enabled: if app.controller.rag_config and app.controller.rag_config.enabled:
# imgui.same_line() # imgui.same_line()
status = app.controller.rag_status status = app.controller.rag_status
if status == "indexing...": color = vec4(100, 255, 100) if status == "indexing...": color = theme.get_color("status_success")
elif status == "error": color = vec4(255, 100, 100) elif status == "error": color = theme.get_color("status_error")
else: color = vec4(180, 180, 180) else: color = theme.get_color("text_disabled")
imgui.text_colored(color, f"[RAG: {status}]") imgui.text_colored(color, f"[RAG: {status}]")
if imgui.is_item_hovered(): imgui.set_tooltip(f"RAG is enabled. Status: {status}. Click to rebuild index.") if imgui.is_item_hovered(): imgui.set_tooltip(f"RAG is enabled. Status: {status}. Click to rebuild index.")
@@ -1448,7 +1449,7 @@ def render_diagnostics_panel(app: App) -> None:
imgui.text(comp_name) imgui.text(comp_name)
imgui.table_next_column() imgui.table_next_column()
if avg_val > 10.0: if avg_val > 10.0:
imgui.text_colored(imgui.ImVec4(1.0, 0.2, 0.2, 1.0), f"{avg_val:.2f}") imgui.text_colored(theme.get_color("status_error"), f"{avg_val:.2f}")
else: else:
imgui.text(f"{avg_val:.2f}") imgui.text(f"{avg_val:.2f}")
imgui.table_next_column() imgui.table_next_column()
@@ -1512,7 +1513,7 @@ def render_cache_panel(app: App) -> None:
ttl_pct = (ttl_remaining / ttl_total * 100) if ttl_total > 0 else 0 ttl_pct = (ttl_remaining / ttl_total * 100) if ttl_total > 0 else 0
imgui.text(f"Age: {age_str}") imgui.text(f"Age: {age_str}")
imgui.text(f"TTL: {remaining_str} ({ttl_pct:.0f}%)") imgui.text(f"TTL: {remaining_str} ({ttl_pct:.0f}%)")
color = imgui.ImVec4(0.2, 0.8, 0.2, 1.0) color = theme.get_color("status_success")
if ttl_pct < 20: color = imgui.ImVec4(1.0, 0.2, 0.2, 1.0) if ttl_pct < 20: color = imgui.ImVec4(1.0, 0.2, 0.2, 1.0)
elif ttl_pct < 50: color = imgui.ImVec4(1.0, 0.8, 0.0, 1.0) elif ttl_pct < 50: color = imgui.ImVec4(1.0, 0.8, 0.0, 1.0)
imgui.push_style_color(imgui.Col_.plot_histogram, color) imgui.push_style_color(imgui.Col_.plot_histogram, color)
@@ -1558,7 +1559,7 @@ def render_tool_analytics_panel(app: App) -> None:
imgui.table_set_column_index(2) imgui.table_set_column_index(2)
imgui.text(f"{avg_time:.0f}") imgui.text(f"{avg_time:.0f}")
imgui.table_set_column_index(3) imgui.table_set_column_index(3)
if fail_pct > 0: imgui.text_colored(imgui.ImVec4(1.0, 0.2, 0.2, 1.0), f"{fail_pct:.0f}%") if fail_pct > 0: imgui.text_colored(theme.get_color("status_error"), f"{fail_pct:.0f}%")
else: imgui.text("0%") else: imgui.text("0%")
imgui.end_table() imgui.end_table()
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_tool_analytics_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_tool_analytics_panel")
@@ -1588,9 +1589,9 @@ def render_token_budget_panel(app: App) -> None:
current = stats.get("estimated_prompt_tokens", stats.get("total_tokens", 0)) current = stats.get("estimated_prompt_tokens", stats.get("total_tokens", 0))
limit = stats.get("max_prompt_tokens", 0) limit = stats.get("max_prompt_tokens", 0)
headroom = stats.get("headroom_tokens", max(0, limit - current)) headroom = stats.get("headroom_tokens", max(0, limit - current))
if pct < 50.0: color = imgui.ImVec4(0.2, 0.8, 0.2, 1.0) if pct < 50.0: color = theme.get_color("status_success")
elif pct < 80.0: color = imgui.ImVec4(1.0, 0.8, 0.0, 1.0) elif pct < 80.0: color = theme.get_color("status_warning")
else: color = imgui.ImVec4(1.0, 0.2, 0.2, 1.0) else: color = theme.get_color("status_error")
imgui.push_style_color(imgui.Col_.plot_histogram, color) imgui.push_style_color(imgui.Col_.plot_histogram, color)
imgui.progress_bar(pct / 100.0, imgui.ImVec2(-1, 0), f"{pct:.1f}%") imgui.progress_bar(pct / 100.0, imgui.ImVec2(-1, 0), f"{pct:.1f}%")
imgui.pop_style_color() imgui.pop_style_color()
@@ -1629,14 +1630,14 @@ def render_token_budget_panel(app: App) -> None:
imgui.table_set_column_index(0); render_selectable_label(app, f"tier_{tier}", tier, width=-1) imgui.table_set_column_index(0); render_selectable_label(app, f"tier_{tier}", tier, width=-1)
imgui.table_set_column_index(1); render_selectable_label(app, f"model_{tier}", model.split("-")[0], width=-1) imgui.table_set_column_index(1); render_selectable_label(app, f"model_{tier}", model.split("-")[0], width=-1)
imgui.table_set_column_index(2); render_selectable_label(app, f"tokens_{tier}", f"{tokens:,}", width=-1) imgui.table_set_column_index(2); render_selectable_label(app, f"tokens_{tier}", f"{tokens:,}", width=-1)
imgui.table_set_column_index(3); render_selectable_label(app, f"cost_{tier}", f"${cost:.4f}", width=-1, color=imgui.ImVec4(0.2, 0.9, 0.2, 1)) imgui.table_set_column_index(3); render_selectable_label(app, f"cost_{tier}", f"${cost:.4f}", width=-1, color=theme.get_color("status_success"))
imgui.end_table() imgui.end_table()
tier_total = sum(cost_tracker.estimate_cost(stats.get('model', ''), stats.get('input', 0), stats.get('output', 0)) for stats in app.mma_tier_usage.values()) tier_total = sum(cost_tracker.estimate_cost(stats.get('model', ''), stats.get('input', 0), stats.get('output', 0)) for stats in app.mma_tier_usage.values())
render_selectable_label(app, "session_total_cost", f"Session Total: ${tier_total:.4f}", width=-1, color=imgui.ImVec4(0, 1, 0, 1)) render_selectable_label(app, "session_total_cost", f"Session Total: ${tier_total:.4f}", width=-1, color=theme.get_color("status_success"))
else: else:
imgui.text_disabled("No MMA tier usage data") imgui.text_disabled("No MMA tier usage data")
if stats.get("would_trim"): if stats.get("would_trim"):
imgui.text_colored(imgui.ImVec4(1.0, 0.3, 0.0, 1.0), "WARNING: Next call will trim history") imgui.text_colored(theme.get_color("status_warning"), "WARNING: Next call will trim history")
trimmable = stats.get("trimmable_turns", 0) trimmable = stats.get("trimmable_turns", 0)
if trimmable: imgui.text_disabled(f"Trimmable turns: {trimmable}") if trimmable: imgui.text_disabled(f"Trimmable turns: {trimmable}")
msgs = stats.get("messages") msgs = stats.get("messages")
@@ -1704,7 +1705,7 @@ def render_log_management(app: App) -> None:
imgui.table_next_column() imgui.table_next_column()
whitelisted = s_data.get("whitelisted", False) whitelisted = s_data.get("whitelisted", False)
if whitelisted: if whitelisted:
imgui.text_colored(vec4(255, 215, 0), "YES") imgui.text_colored(theme.get_color("status_warning"), "YES")
else: else:
imgui.text("NO") imgui.text("NO")
metadata = s_data.get("metadata") or {} metadata = s_data.get("metadata") or {}
@@ -2015,10 +2016,10 @@ def render_agent_tools_panel(app: App) -> None:
if app.ui_tool_filter_category != "All" and app.ui_tool_filter_category != cat_name: continue if app.ui_tool_filter_category != "All" and app.ui_tool_filter_category != cat_name: continue
if imgui.tree_node(cat_name): if imgui.tree_node(cat_name):
for tool in tools: for tool in tools:
if tool.weight >= 5: imgui.text_colored(vec4(255, 100, 100), "[HIGH]"); imgui.same_line() if tool.weight >= 5: imgui.text_colored(theme.get_color("status_error"), "[HIGH]"); imgui.same_line()
elif tool.weight == 4: imgui.text_colored(vec4(255, 255, 100), "[PREF]"); imgui.same_line() elif tool.weight == 4: imgui.text_colored(theme.get_color("status_warning"), "[PREF]"); imgui.same_line()
elif tool.weight == 2: imgui.text_colored(vec4(255, 150, 50), "[REJECT]"); imgui.same_line() elif tool.weight == 2: imgui.text_colored(theme.get_color("status_warning"), "[REJECT]"); imgui.same_line()
elif tool.weight <= 1: imgui.text_colored(vec4(180, 180, 180), "[LOW]"); imgui.same_line() elif tool.weight <= 1: imgui.text_colored(theme.get_color("text_disabled"), "[LOW]"); imgui.same_line()
imgui.text(tool.name); imgui.same_line(180) imgui.text(tool.name); imgui.same_line(180)
@@ -2197,9 +2198,9 @@ def render_base_prompt_diff_modal(app: App) -> None:
else: else:
imgui.begin_child("base_prompt_diff_scroll", imgui.ImVec2(800, 500), True) imgui.begin_child("base_prompt_diff_scroll", imgui.ImVec2(800, 500), True)
for line in diff: for line in diff:
if line.startswith("+++") or line.startswith("---") or line.startswith("@@"): imgui.text_colored(vec4(77, 178, 255), line.rstrip()) if line.startswith("+++") or line.startswith("---") or line.startswith("@@"): imgui.text_colored(theme.get_color("diff_header"), line.rstrip())
elif line.startswith("+"): imgui.text_colored(vec4(51, 230, 51), line.rstrip()) elif line.startswith("+"): imgui.text_colored(theme.get_color("diff_added"), line.rstrip())
elif line.startswith("-"): imgui.text_colored(vec4(230, 51, 51), line.rstrip()) elif line.startswith("-"): imgui.text_colored(theme.get_color("diff_removed"), line.rstrip())
else: imgui.text(line.rstrip()) else: imgui.text(line.rstrip())
imgui.end_child() imgui.end_child()
@@ -2740,9 +2741,9 @@ def render_files_and_media(app: App) -> None:
imgui.table_set_column_index(2) imgui.table_set_column_index(2)
if in_context: if in_context:
imgui.text_colored(imgui.ImVec4(0.3, 0.8, 0.3, 1), "Active") imgui.text_colored(theme.get_color("status_success"), "Active")
elif is_cached: elif is_cached:
imgui.text_colored(imgui.ImVec4(0.3, 0.8, 1, 1), "Cached") imgui.text_colored(theme.get_color("status_info"), "Cached")
else: else:
imgui.text_disabled(" - ") imgui.text_disabled(" - ")
imgui.end_table() imgui.end_table()
@@ -3048,23 +3049,23 @@ def render_ast_inspector_modal(app: App) -> None:
mode = 'hide' mode = 'hide'
if deepest_node: mode = getattr(f_item, 'ast_mask', {}).get(deepest_node['full_path'], 'hide') if deepest_node: mode = getattr(f_item, 'ast_mask', {}).get(deepest_node['full_path'], 'hide')
if mode == 'def': if mode == 'def':
draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + avail_width, pos.y + line_height), imgui.get_color_u32(imgui.ImVec4(0, 1.0, 0, 0.15))) draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + avail_width, pos.y + line_height), imgui.get_color_u32(theme.get_color("slice_auto", alpha=0.15)))
elif mode == 'sig': elif mode == 'sig':
draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + avail_width, pos.y + line_height), imgui.get_color_u32(imgui.ImVec4(0, 0, 1.0, 0.15))) draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + avail_width, pos.y + line_height), imgui.get_color_u32(theme.get_color("slice_selection", alpha=0.15)))
elif deepest_node and deepest_node['full_path'] == getattr(app, '_hovered_ast_node', None): elif deepest_node and deepest_node['full_path'] == getattr(app, '_hovered_ast_node', None):
draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + avail_width, pos.y + line_height), imgui.get_color_u32(imgui.ImVec4(1.0, 1.0, 0, 0.2))) draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + avail_width, pos.y + line_height), imgui.get_color_u32(theme.get_color("status_warning", alpha=0.2)))
# 2. Slice Highlight # 2. Slice Highlight
if hasattr(f_item, 'custom_slices'): if hasattr(f_item, 'custom_slices'):
is_auto = any(slc['start_line'] <= line_num <= slc['end_line'] for slc in f_item.custom_slices if slc.get('tag') == 'auto-ast') is_auto = any(slc['start_line'] <= line_num <= slc['end_line'] for slc in f_item.custom_slices if slc.get('tag') == 'auto-ast')
is_man = any(slc['start_line'] <= line_num <= slc['end_line'] for slc in f_item.custom_slices if slc.get('tag') != 'auto-ast') is_man = any(slc['start_line'] <= line_num <= slc['end_line'] for slc in f_item.custom_slices if slc.get('tag') != 'auto-ast')
if is_man: draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + avail_width, pos.y + line_height), imgui.get_color_u32(imgui.ImVec4(1.0, 0.65, 0, 0.2))) if is_man: draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + avail_width, pos.y + line_height), imgui.get_color_u32(theme.get_color("slice_manual", alpha=0.2)))
elif is_auto and mode == 'hide': draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + avail_width, pos.y + line_height), imgui.get_color_u32(imgui.ImVec4(0, 1.0, 0, 0.1))) elif is_auto and mode == 'hide': draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + avail_width, pos.y + line_height), imgui.get_color_u32(theme.get_color("slice_auto", alpha=0.1)))
# 3. Active Selection Highlight # 3. Active Selection Highlight
if getattr(app, '_slice_sel_start', -1) != -1 and getattr(app, '_slice_sel_end', -1) != -1: if getattr(app, '_slice_sel_start', -1) != -1 and getattr(app, '_slice_sel_end', -1) != -1:
s, e = min(app._slice_sel_start, app._slice_sel_end), max(app._slice_sel_start, app._slice_sel_end) s, e = min(app._slice_sel_start, app._slice_sel_end), max(app._slice_sel_start, app._slice_sel_end)
if s <= line_num <= e: draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + avail_width, pos.y + line_height), imgui.get_color_u32(imgui.ImVec4(0.4, 0.4, 1.0, 0.3))) if s <= line_num <= e: draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + avail_width, pos.y + line_height), imgui.get_color_u32(theme.get_color("slice_selection", alpha=0.3)))
imgui.selectable(f"{line_num:4} | {line_text}##ln{line_num}", False) imgui.selectable(f"{line_num:4} | {line_text}##ln{line_num}", False)
if imgui.is_item_clicked(): app._slice_sel_start = line_num; app._slice_sel_end = line_num if imgui.is_item_clicked(): app._slice_sel_start = line_num; app._slice_sel_end = line_num
@@ -3234,7 +3235,7 @@ def render_context_files_table(app: App) -> None:
imgui.end_popup() imgui.end_popup()
if hasattr(f_item, "custom_slices") and f_item.custom_slices: if hasattr(f_item, "custom_slices") and f_item.custom_slices:
imgui.same_line() imgui.same_line()
imgui.text_colored(imgui.ImVec4(1.0, 0.5, 0.0, 1.0), "[Slices Active]") imgui.text_colored(theme.get_color("status_warning"), "[Slices Active]")
def render_context_presets(app: App) -> None: def render_context_presets(app: App) -> None:
presets = app.controller.project.get('context_presets', {}) presets = app.controller.project.get('context_presets', {})
@@ -3389,7 +3390,7 @@ def render_thinking_trace(app: App, entry: dict, segments: list[dict], entry_ind
if not segments: if not segments:
return return
# Tint thinking trace background slightly differently # Tint thinking trace background slightly differently
with imscope.style_color(imgui.Col_.child_bg, imgui.ImVec4(0.15, 0.14, 0.10, 0.7)), \ with imscope.style_color(imgui.Col_.child_bg, theme.get_color("bubble_vendor", alpha=0.7)), \
theme.ai_text_style(): theme.ai_text_style():
with imscope.indent(): with imscope.indent():
show_content = True show_content = True
@@ -3458,7 +3459,7 @@ def render_discussion_entry(app: App, entry: dict, index: int) -> None:
if usage: if usage:
inp, out, cache = usage.get("input_tokens", 0), usage.get("output_tokens", 0), usage.get("cache_read_input_tokens", 0) inp, out, cache = usage.get("input_tokens", 0), usage.get("output_tokens", 0), usage.get("cache_read_input_tokens", 0)
u_str = f" in:{inp} out:{out}" + (f" cache:{cache}" if cache else "") u_str = f" in:{inp} out:{out}" + (f" cache:{cache}" if cache else "")
imgui.same_line(); imgui.text_colored(imgui.ImVec4(0.4, 0.6, 0.7, 1.0), u_str) imgui.same_line(); imgui.text_colored(theme.get_color("status_info"), u_str)
if collapsed: if collapsed:
imgui.same_line() imgui.same_line()
@@ -3584,10 +3585,10 @@ def render_session_insights_panel(app: App) -> None:
completed = insights.get('completed_tickets', 0) completed = insights.get('completed_tickets', 0)
efficiency = insights.get('efficiency', 0) efficiency = insights.get('efficiency', 0)
def render_prior_session_view(app: App) -> None: def render_prior_session_view(app: App) -> None:
with imscope.style_color(imgui.Col_.child_bg, vec4(50, 40, 20)): with imscope.style_color(imgui.Col_.child_bg, theme.get_color("bubble_vendor")):
if imgui.button("Exit Prior Session"): app.controller.cb_exit_prior_session(); app._comms_log_dirty = True if imgui.button("Exit Prior Session"): app.controller.cb_exit_prior_session(); app._comms_log_dirty = True
imgui.same_line() imgui.same_line()
imgui.text_colored(vec4(200, 180, 100), f"({len(app.prior_disc_entries)} entries)") imgui.text_colored(theme.get_color("status_warning"), f"({len(app.prior_disc_entries)} entries)")
imgui.separator() imgui.separator()
avail = imgui.get_content_region_avail() avail = imgui.get_content_region_avail()
with imscope.child("prior_scroll", imgui.ImVec2(avail.x, avail.y), imgui.WindowFlags_.horizontal_scrollbar): with imscope.child("prior_scroll", imgui.ImVec2(avail.x, avail.y), imgui.WindowFlags_.horizontal_scrollbar):
@@ -3597,12 +3598,12 @@ def render_prior_session_view(app: App) -> None:
if imgui.button("+" if collapsed else "-"): entry["collapsed"] = not collapsed if imgui.button("+" if collapsed else "-"): entry["collapsed"] = not collapsed
imgui.same_line(); role, ts = entry.get("role", "??"), entry.get("ts", "") imgui.same_line(); role, ts = entry.get("role", "??"), entry.get("ts", "")
imgui.text_colored(C_LBL, f"[{role}]") imgui.text_colored(C_LBL, f"[{role}]")
if ts: imgui.same_line(); imgui.text_colored(vec4(160, 160, 160), str(ts)) if ts: imgui.same_line(); imgui.text_colored(theme.get_color("text_disabled"), str(ts))
content = entry.get("content", "") content = entry.get("content", "")
if collapsed: if collapsed:
imgui.same_line(); preview = content.replace("\n", " ")[:80] imgui.same_line(); preview = content.replace("\n", " ")[:80]
if len(content) > 80: preview += "..." if len(content) > 80: preview += "..."
imgui.text_colored(vec4(180, 180, 180), preview) imgui.text_colored(theme.get_color("text_disabled"), preview)
else: else:
with theme.ai_text_style(): with theme.ai_text_style():
markdown_helper.render(content, context_id=f'prior_disc_{idx}') markdown_helper.render(content, context_id=f'prior_disc_{idx}')
@@ -3649,8 +3650,8 @@ def render_synthesis_panel(app: App) -> None:
def render_comms_history_panel(app: App) -> None: def render_comms_history_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_comms_history_panel") if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_comms_history_panel")
st_col = vec4(200, 220, 160) st_col = theme.get_color("text_disabled")
if theme.is_nerv_active(): st_col = vec4(80, 255, 80) # DATA_GREEN for status in NERV if theme.is_nerv_active(): st_col = theme.get_color("status_success") # DATA_GREEN for status in NERV
imgui.text_colored(st_col, f"Status: {app.ai_status}") imgui.text_colored(st_col, f"Status: {app.ai_status}")
imgui.same_line() imgui.same_line()
if imgui.button("Clear##comms"): if imgui.button("Clear##comms"):
@@ -3693,7 +3694,7 @@ def render_comms_history_panel(app: App) -> None:
# Row 1: #Idx TS DIR KIND Provider/Model [Tier] # Row 1: #Idx TS DIR KIND Provider/Model [Tier]
imgui.text_colored(C_LBL, f"#{i_display}"); imgui.same_line() imgui.text_colored(C_LBL, f"#{i_display}"); imgui.same_line()
imgui.text_colored(vec4(160, 160, 160), ts) imgui.text_colored(theme.get_color("text_disabled"), ts)
latency = entry.get("latency") or entry.get("metadata", {}).get("latency") latency = entry.get("latency") or entry.get("metadata", {}).get("latency")
if latency: if latency:
@@ -3703,7 +3704,7 @@ def render_comms_history_panel(app: App) -> None:
ticket_id = entry.get("mma_ticket_id") ticket_id = entry.get("mma_ticket_id")
if ticket_id: if ticket_id:
imgui.same_line() imgui.same_line()
imgui.text_colored(vec4(255, 120, 120), f"[{ticket_id}]") imgui.text_colored(theme.get_color("status_error"), f"[{ticket_id}]")
imgui.same_line() imgui.same_line()
d_col = DIR_COLORS.get(direction, C_VAL) d_col = DIR_COLORS.get(direction, C_VAL)
imgui.text_colored(d_col, direction); imgui.same_line() imgui.text_colored(d_col, direction); imgui.same_line()
@@ -3857,7 +3858,7 @@ def render_discussion_metadata(app: App) -> None:
total_out += entry["usage"].get("output_tokens", 0) total_out += entry["usage"].get("output_tokens", 0)
total_cache += entry["usage"].get("cache_read_input_tokens", 0) total_cache += entry["usage"].get("cache_read_input_tokens", 0)
if total_in > 0 or total_out > 0: if total_in > 0 or total_out > 0:
imgui.text_colored(vec4(100, 150, 180), f"Discussion Tokens: {total_in} In | {total_out} Out | {total_cache} Cache") imgui.text_colored(theme.get_color("status_info"), f"Discussion Tokens: {total_in} In | {total_out} Out | {total_cache} Cache")
imgui.separator() imgui.separator()
imgui.text_colored(C_LBL, "commit:"); imgui.same_line() imgui.text_colored(C_LBL, "commit:"); imgui.same_line()
@@ -4053,7 +4054,7 @@ def render_vendor_state(app: App) -> None:
imgui.table_setup_column("Value", imgui.TableColumnFlags_.width_stretch) imgui.table_setup_column("Value", imgui.TableColumnFlags_.width_stretch)
imgui.table_setup_column("State", imgui.TableColumnFlags_.width_fixed, 60) imgui.table_setup_column("State", imgui.TableColumnFlags_.width_fixed, 60)
imgui.table_headers_row() imgui.table_headers_row()
state_colors = {"ok": vec4(120, 220, 120), "warn": vec4(240, 200, 80), "error": vec4(240, 80, 80), "info": vec4(180, 180, 180)} state_colors = {"ok": theme.get_color("status_success"), "warn": theme.get_color("status_warning"), "error": theme.get_color("status_error"), "info": theme.get_color("text_disabled")}
for m in metrics: for m in metrics:
imgui.table_next_row() imgui.table_next_row()
imgui.table_next_column(); imgui.text(m.label) imgui.table_next_column(); imgui.text(m.label)
@@ -4061,7 +4062,7 @@ def render_vendor_state(app: App) -> None:
imgui.text(m.value) imgui.text(m.value)
if imgui.is_item_hovered(): imgui.set_tooltip(m.tooltip) if imgui.is_item_hovered(): imgui.set_tooltip(m.tooltip)
imgui.table_next_column() imgui.table_next_column()
imgui.text_colored(state_colors.get(m.state, vec4(180, 180, 180)), m.state) imgui.text_colored(state_colors.get(m.state, theme.get_color("text_disabled")), m.state)
imgui.end_table() imgui.end_table()
def render_message_panel(app: App) -> None: def render_message_panel(app: App) -> None:
@@ -4071,8 +4072,7 @@ def render_message_panel(app: App) -> None:
if is_live: if is_live:
val = math.sin(time.time() * 10 * math.pi) val = math.sin(time.time() * 10 * math.pi)
alpha = 1.0 if val > 0 else 0.0 alpha = 1.0 if val > 0 else 0.0
c = imgui.ImVec4(0.39, 1.0, 0.39, alpha) c = theme.get_color("status_success", alpha=alpha)
if theme.is_nerv_active(): c = vec4(80, 255, 80, alpha) # DATA_GREEN for LIVE in NERV
imgui.text_colored(c, "LIVE") imgui.text_colored(c, "LIVE")
imgui.separator() imgui.separator()
ch, app.ui_ai_input = imgui.input_text_multiline("##ai_in", app.ui_ai_input, imgui.ImVec2(-1, -40)) ch, app.ui_ai_input = imgui.input_text_multiline("##ai_in", app.ui_ai_input, imgui.ImVec2(-1, -40))
@@ -4323,12 +4323,12 @@ def render_text_viewer_window(app: App) -> None:
is_auto_sliced = any(slc['start_line'] <= line_num <= slc['end_line'] for slc in app.ui_editing_slices_file.custom_slices if slc.get('tag') == 'auto-ast') is_auto_sliced = any(slc['start_line'] <= line_num <= slc['end_line'] for slc in app.ui_editing_slices_file.custom_slices if slc.get('tag') == 'auto-ast')
is_manual_sliced = any(slc['start_line'] <= line_num <= slc['end_line'] for slc in app.ui_editing_slices_file.custom_slices if slc.get('tag') != 'auto-ast') is_manual_sliced = any(slc['start_line'] <= line_num <= slc['end_line'] for slc in app.ui_editing_slices_file.custom_slices if slc.get('tag') != 'auto-ast')
if is_manual_sliced: draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(vec4(255, 165, 0, 0.2))) if is_manual_sliced: draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(theme.get_color("slice_manual", alpha=0.2)))
elif is_auto_sliced: draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(vec4(0, 255, 0, 0.15))) elif is_auto_sliced: draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(theme.get_color("slice_auto", alpha=0.15)))
if app._slice_sel_start != -1 and app._slice_sel_end != -1: if app._slice_sel_start != -1 and app._slice_sel_end != -1:
s, e = min(app._slice_sel_start, app._slice_sel_end), max(app._slice_sel_start, app._slice_sel_end) s, e = min(app._slice_sel_start, app._slice_sel_end), max(app._slice_sel_start, app._slice_sel_end)
if s <= line_num <= e: draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(vec4(100, 100, 255, 0.3))) if s <= line_num <= e: draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(theme.get_color("slice_selection", alpha=0.3)))
imgui.selectable(f"{line_num:4} | {line_text}##ln{line_num}", False) imgui.selectable(f"{line_num:4} | {line_text}##ln{line_num}", False)
if imgui.is_item_clicked(): app._slice_sel_start = line_num; app._slice_sel_end = line_num if imgui.is_item_clicked(): app._slice_sel_start = line_num; app._slice_sel_end = line_num
if imgui.is_item_hovered(imgui.HoveredFlags_.allow_when_blocked_by_active_item) and imgui.is_mouse_down(0): app._slice_sel_end = line_num if imgui.is_item_hovered(imgui.HoveredFlags_.allow_when_blocked_by_active_item) and imgui.is_mouse_down(0): app._slice_sel_end = line_num
@@ -4340,7 +4340,7 @@ def render_text_viewer_window(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_text_viewer_ced") if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_text_viewer_ced")
app._text_viewer_editor.render(f"##ced_{app.text_viewer_title}", imgui.ImVec2(-1, -1)) app._text_viewer_editor.render(f"##ced_{app.text_viewer_title}", imgui.ImVec2(-1, -1))
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_text_viewer_ced") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_text_viewer_ced")
except Exception as e: imgui.text_colored(vec4(255, 100, 100), f"CED Error: {e}"); imgui.text_unformatted(app.text_viewer_content) except Exception as e: imgui.text_colored(theme.get_color("status_error"), f"CED Error: {e}"); imgui.text_unformatted(app.text_viewer_content)
else: else:
with imscope.child("tv_scroll", -1, -1, True): with imscope.child("tv_scroll", -1, -1, True):
if app.text_viewer_wrap: imgui.push_text_wrap_pos(imgui.get_content_region_avail().x) if app.text_viewer_wrap: imgui.push_text_wrap_pos(imgui.get_content_region_avail().x)
@@ -4358,23 +4358,23 @@ def render_patch_modal(app: App) -> None:
p_max = imgui.ImVec2(p_min.x + imgui.get_window_size().x, p_min.y + imgui.get_window_size().y) p_max = imgui.ImVec2(p_min.x + imgui.get_window_size().x, p_min.y + imgui.get_window_size().y)
shaders.draw_soft_shadow(imgui.get_background_draw_list(), p_min, p_max, imgui.ImVec4(0, 0, 0, 0.6), 25.0, 6.0) shaders.draw_soft_shadow(imgui.get_background_draw_list(), p_min, p_max, imgui.ImVec4(0, 0, 0, 0.6), 25.0, 6.0)
imgui.text_colored(vec4(255, 230, 77), "Tier 4 QA Generated a Patch") imgui.text_colored(theme.get_color("status_warning"), "Tier 4 QA Generated a Patch")
imgui.separator() imgui.separator()
if app._pending_patch_files: if app._pending_patch_files:
imgui.text("Files to modify:") imgui.text("Files to modify:")
for f in app._pending_patch_files: imgui.text(f" - {f}") for f in app._pending_patch_files: imgui.text(f" - {f}")
imgui.separator() imgui.separator()
if app._patch_error_message: if app._patch_error_message:
imgui.text_colored(vec4(255, 77, 77), f"Error: {app._patch_error_message}") imgui.text_colored(theme.get_color("status_error"), f"Error: {app._patch_error_message}")
imgui.separator() imgui.separator()
imgui.text("Diff Preview:") imgui.text("Diff Preview:")
imgui.begin_child("patch_diff_scroll", imgui.ImVec2(-1, 280), True) imgui.begin_child("patch_diff_scroll", imgui.ImVec2(-1, 280), True)
if app._pending_patch_text: if app._pending_patch_text:
diff_lines = app._pending_patch_text.split("\n") diff_lines = app._pending_patch_text.split("\n")
for line in diff_lines: for line in diff_lines:
if line.startswith("+++") or line.startswith("---") or line.startswith("@@"): imgui.text_colored(vec4(77, 178, 255), line) if line.startswith("+++") or line.startswith("---") or line.startswith("@@"): imgui.text_colored(theme.get_color("diff_header"), line)
elif line.startswith("+"): imgui.text_colored(vec4(51, 230, 51), line) elif line.startswith("+"): imgui.text_colored(theme.get_color("diff_added"), line)
elif line.startswith("-"): imgui.text_colored(vec4(230, 51, 51), line) elif line.startswith("-"): imgui.text_colored(theme.get_color("diff_removed"), line)
else: imgui.text(line) else: imgui.text(line)
imgui.end_child() imgui.end_child()
imgui.separator() imgui.separator()
@@ -4454,7 +4454,7 @@ def render_approve_script_modal(app: App) -> None:
if not dlg: imgui.close_current_popup() if not dlg: imgui.close_current_popup()
else: else:
imgui.text("The AI wants to run the following PowerShell script:") imgui.text("The AI wants to run the following PowerShell script:")
imgui.text_colored(vec4(200, 200, 100), f"base_dir: {dlg._base_dir}") imgui.text_colored(theme.get_color("status_warning"), f"base_dir: {dlg._base_dir}")
imgui.separator() imgui.separator()
# Checkbox to toggle full preview inside modal # Checkbox to toggle full preview inside modal
if not hasattr(app, 'ui_approve_modal_preview'): app.ui_approve_modal_preview = False if not hasattr(app, 'ui_approve_modal_preview'): app.ui_approve_modal_preview = False
@@ -4632,13 +4632,13 @@ def render_error_tint(app: App) -> None:
if not HotReloader.is_error_state: return if not HotReloader.is_error_state: return
draw_list = imgui.get_background_draw_list() draw_list = imgui.get_background_draw_list()
display_size = imgui.get_io().display_size display_size = imgui.get_io().display_size
# Translucent red: (1.0, 0.0, 0.0, 0.2) # Translucent red
tint_col = imgui.get_color_u32(imgui.ImVec4(1.0, 0.0, 0.0, 0.2)) tint_col = imgui.get_color_u32(theme.get_color("status_error", alpha=0.2))
draw_list.add_rect_filled(imgui.ImVec2(0, 0), display_size, tint_col) draw_list.add_rect_filled(imgui.ImVec2(0, 0), display_size, tint_col)
if app.perf_profiling_enabled: if app.perf_profiling_enabled:
imgui.set_next_window_pos(imgui.ImVec2(10, 50)) imgui.set_next_window_pos(imgui.ImVec2(10, 50))
with imscope.window("Hot Reload Error", None, imgui.WindowFlags_.always_auto_resize | imgui.WindowFlags_.no_title_bar): with imscope.window("Hot Reload Error", None, imgui.WindowFlags_.always_auto_resize | imgui.WindowFlags_.no_title_bar):
imgui.text_colored(imgui.ImVec4(1, 0, 0, 1), "HOT RELOAD ERROR") imgui.text_colored(theme.get_color("status_error"), "HOT RELOAD ERROR")
imgui.text_wrapped(HotReloader.last_error or "Unknown error") imgui.text_wrapped(HotReloader.last_error or "Unknown error")
@@ -4651,14 +4651,14 @@ def render_project_stale_tint(app: App) -> None:
if not app.controller.is_project_stale(): return if not app.controller.is_project_stale(): return
draw_list = imgui.get_background_draw_list() draw_list = imgui.get_background_draw_list()
display_size = imgui.get_io().display_size display_size = imgui.get_io().display_size
tint_col = imgui.get_color_u32(imgui.ImVec4(1.0, 0.85, 0.2, 0.15)) tint_col = imgui.get_color_u32(theme.get_color("status_warning", alpha=0.15))
draw_list.add_rect_filled(imgui.ImVec2(0, 0), display_size, tint_col) draw_list.add_rect_filled(imgui.ImVec2(0, 0), display_size, tint_col)
pending = app.controller._project_switch_pending_path or app.controller.active_project_path pending = app.controller._project_switch_pending_path or app.controller.active_project_path
imgui.set_next_window_pos(imgui.ImVec2(10, 50)) imgui.set_next_window_pos(imgui.ImVec2(10, 50))
with imscope.window("Project Stale", None, with imscope.window("Project Stale", None,
imgui.WindowFlags_.always_auto_resize | imgui.WindowFlags_.no_title_bar | imgui.WindowFlags_.always_auto_resize | imgui.WindowFlags_.no_title_bar |
imgui.WindowFlags_.no_resize | imgui.WindowFlags_.no_move): imgui.WindowFlags_.no_resize | imgui.WindowFlags_.no_move):
imgui.text_colored(imgui.ImVec4(1.0, 0.85, 0.2, 1.0), "PROJECT SWITCHING") imgui.text_colored(theme.get_color("status_warning"), "PROJECT SWITCHING")
imgui.text_wrapped(f"Loading: {Path(pending).stem if pending else '?'}") imgui.text_wrapped(f"Loading: {Path(pending).stem if pending else '?'}")
imgui.text_wrapped("UI is read-only until the switch completes. You can still browse tabs.") imgui.text_wrapped("UI is read-only until the switch completes. You can still browse tabs.")
@@ -4701,7 +4701,7 @@ def render_mma_dashboard(app: App) -> None:
render_mma_focus_selector(app) render_mma_focus_selector(app)
imgui.separator() imgui.separator()
if app.is_viewing_prior_session: if app.is_viewing_prior_session:
c = vec4(255, 152, 48) if theme.is_nerv_active() else vec4(255, 200, 100) c = theme.get_color("status_warning") if theme.is_nerv_active() else theme.get_color("status_warning")
imgui.text_colored(c, "HISTORICAL VIEW - READ ONLY") imgui.text_colored(c, "HISTORICAL VIEW - READ ONLY")
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_mma_dashboard") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_mma_dashboard")
return return
@@ -4739,7 +4739,7 @@ def render_mma_modals(app: App) -> None:
if not app._pending_ask_dialog or app._ask_tool_data is None: imgui.close_current_popup() if not app._pending_ask_dialog or app._ask_tool_data is None: imgui.close_current_popup()
else: else:
tool_name = app._ask_tool_data.get("tool", "unknown"); tool_args = app._ask_tool_data.get("args", {}) tool_name = app._ask_tool_data.get("tool", "unknown"); tool_args = app._ask_tool_data.get("args", {})
imgui.text("The AI wants to execute a tool:"); imgui.text_colored(vec4(200, 200, 100), f"Tool: {tool_name}"); imgui.separator() imgui.text("The AI wants to execute a tool:"); imgui.text_colored(theme.get_color("status_warning"), f"Tool: {tool_name}"); imgui.separator()
imgui.text("Arguments:"); imgui.begin_child("ask_args_child", imgui.ImVec2(400, 200), True); imgui.text_unformatted(json.dumps(tool_args, indent=2)); imgui.end_child() imgui.text("Arguments:"); imgui.begin_child("ask_args_child", imgui.ImVec2(400, 200), True); imgui.text_unformatted(json.dumps(tool_args, indent=2)); imgui.end_child()
imgui.separator() imgui.separator()
if imgui.button("Approve", imgui.ImVec2(120, 0)): app._handle_approve_ask(); imgui.close_current_popup() if imgui.button("Approve", imgui.ImVec2(120, 0)): app._handle_approve_ask(); imgui.close_current_popup()
@@ -4796,7 +4796,7 @@ def render_mma_modals(app: App) -> None:
imgui.end_popup() imgui.end_popup()
# Cycle Detection # Cycle Detection
if imgui.begin_popup_modal("Cycle Detected!", None, imgui.WindowFlags_.always_auto_resize)[0]: if imgui.begin_popup_modal("Cycle Detected!", None, imgui.WindowFlags_.always_auto_resize)[0]:
imgui.text_colored(imgui.ImVec4(1, 0.3, 0.3, 1), "The dependency graph contains a cycle!") imgui.text_colored(theme.get_color("status_error", alpha=1), "The dependency graph contains a cycle!")
imgui.text("Please remove the circular dependency.") imgui.text("Please remove the circular dependency.")
if imgui.button("OK"): imgui.close_current_popup() if imgui.button("OK"): imgui.close_current_popup()
imgui.end_popup() imgui.end_popup()
@@ -4812,16 +4812,16 @@ def render_mma_track_summary(app: App) -> None:
total_cost = sum(cost_tracker.estimate_cost(u.get('model','unknown'), u.get('input',0), u.get('output',0)) for u in app.mma_tier_usage.values()) total_cost = sum(cost_tracker.estimate_cost(u.get('model','unknown'), u.get('input',0), u.get('output',0)) for u in app.mma_tier_usage.values())
imgui.text("Track:"); imgui.same_line(); imgui.text_colored(C_VAL, track_name); imgui.same_line(); imgui.text(" | Status:"); imgui.same_line() imgui.text("Track:"); imgui.same_line(); imgui.text_colored(C_VAL, track_name); imgui.same_line(); imgui.text(" | Status:"); imgui.same_line()
if app.mma_status == "paused": if app.mma_status == "paused":
imgui.text_colored(vec4(255, 152, 48) if is_nerv else imgui.ImVec4(1, 0.5, 0, 1), "PIPELINE PAUSED"); imgui.same_line() imgui.text_colored(theme.get_color("status_warning") if is_nerv else theme.get_color("status_warning"), "PIPELINE PAUSED"); imgui.same_line()
status_col = imgui.ImVec4(1, 1, 1, 1) status_col = imgui.ImVec4(1, 1, 1, 1)
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")
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_success") if is_nerv else theme.get_color("status_warning")
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")
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") if is_nerv else theme.get_color("status_error")
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")
imgui.text_colored(status_col, app.mma_status.upper()); imgui.same_line(); imgui.text(" | Cost:"); imgui.same_line(); imgui.text_colored(imgui.ImVec4(0, 1, 0, 1), f"${total_cost:,.4f}") imgui.text_colored(status_col, app.mma_status.upper()); imgui.same_line(); imgui.text(" | Cost:"); imgui.same_line(); imgui.text_colored(theme.get_color("status_success"), f"${total_cost:,.4f}")
perc = track_stats["percentage"] / 100.0 perc = track_stats["percentage"] / 100.0
p_color = imgui.ImVec4(1, 0, 0, 1) if track_stats["percentage"] < 33 else (imgui.ImVec4(1, 1, 0, 1) if track_stats["percentage"] < 66 else imgui.ImVec4(0, 1, 0, 1)) p_color = theme.get_color("status_error") if track_stats["percentage"] < 33 else (theme.get_color("status_warning") if track_stats["percentage"] < 66 else theme.get_color("status_success"))
imgui.push_style_color(imgui.Col_.plot_histogram, p_color); imgui.progress_bar(perc, imgui.ImVec2(-1, 0), f"{track_stats['percentage']:.1f}%"); imgui.pop_style_color() imgui.push_style_color(imgui.Col_.plot_histogram, p_color); imgui.progress_bar(perc, imgui.ImVec2(-1, 0), f"{track_stats['percentage']:.1f}%"); imgui.pop_style_color()
if imgui.begin_table("ticket_stats_breakdown", 4): if imgui.begin_table("ticket_stats_breakdown", 4):
for lbl, val in [("Completed:", track_stats["completed"]), ("In Progress:", track_stats["in_progress"]), ("Blocked:", track_stats["blocked"]), ("Todo:", track_stats["todo"])]: for lbl, val in [("Completed:", track_stats["completed"]), ("In Progress:", track_stats["in_progress"]), ("Blocked:", track_stats["blocked"]), ("Todo:", track_stats["todo"])]:
@@ -4857,10 +4857,10 @@ def render_mma_track_browser(app: App) -> None:
for track in app.tracks: for track in app.tracks:
imgui.table_next_row(); imgui.table_next_column(); imgui.text(track.get("title", "Untitled")); imgui.table_next_column() imgui.table_next_row(); imgui.table_next_column(); imgui.text(track.get("title", "Untitled")); imgui.table_next_column()
status = track.get("status", "unknown").lower() status = track.get("status", "unknown").lower()
c = imgui.ImVec4(0.7, 0.7, 0.7, 1) if status == "new" else (vec4(80, 255, 80) if status == "active" and theme.is_nerv_active() else (imgui.ImVec4(1, 1, 0, 1) if status == "active" else (imgui.ImVec4(0, 1, 0, 1) if status == "done" else (imgui.ImVec4(1, 0, 0, 1) if status == "blocked" else imgui.ImVec4(1, 1, 1, 1))))) c = theme.get_color("text_disabled") if status == "new" else (theme.get_color("status_success") if status == "active" and theme.is_nerv_active() else (theme.get_color("status_warning") if status == "active" else (theme.get_color("status_success") if status == "done" else (theme.get_color("status_error") if status == "blocked" else imgui.ImVec4(1, 1, 1, 1)))))
imgui.text_colored(c, status.upper()); imgui.table_next_column() imgui.text_colored(c, status.upper()); imgui.table_next_column()
prog = track.get("progress", 0.0) prog = track.get("progress", 0.0)
p_c = imgui.ImVec4(1, 0, 0, 1) if prog < 0.33 else (imgui.ImVec4(1, 1, 0, 1) if prog < 0.66 else imgui.ImVec4(0, 1, 0, 1)) p_c = theme.get_color("status_error") if prog < 0.33 else (theme.get_color("status_warning") if prog < 0.66 else theme.get_color("status_success"))
imgui.push_style_color(imgui.Col_.plot_histogram, p_c); imgui.progress_bar(prog, imgui.ImVec2(-1, 0), f"{int(prog*100)}%"); imgui.pop_style_color(); imgui.table_next_column() imgui.push_style_color(imgui.Col_.plot_histogram, p_c); imgui.progress_bar(prog, imgui.ImVec2(-1, 0), f"{int(prog*100)}%"); imgui.pop_style_color(); imgui.table_next_column()
if imgui.button(f"Load##{track.get('id')}"): app._cb_load_track(str(track.get("id") or "")) if imgui.button(f"Load##{track.get('id')}"): app._cb_load_track(str(track.get("id") or ""))
imgui.end_table() imgui.end_table()
@@ -4902,9 +4902,9 @@ def render_mma_global_controls(app: App) -> None:
if imgui.button("Reload GUI"): if imgui.button("Reload GUI"):
success = app._trigger_hot_reload() success = app._trigger_hot_reload()
if success: if success:
imgui.text_colored(imgui.ImVec4(0, 1, 0, 1), "Reloaded!") imgui.text_colored(theme.get_color("status_success"), "Reloaded!")
else: else:
imgui.text_colored(imgui.ImVec4(1, 0, 0, 1), f"Error: {app._hot_reload_error or 'Unknown'}") imgui.text_colored(theme.get_color("status_error"), f"Error: {app._hot_reload_error or 'Unknown'}")
imgui.same_line(); imgui.text_disabled("(Ctrl+Alt+R)") imgui.same_line(); imgui.text_disabled("(Ctrl+Alt+R)")
def render_mma_usage_section(app: App) -> None: def render_mma_usage_section(app: App) -> None:
@@ -4999,7 +4999,7 @@ def render_tier_stream_panel(app: App, tier_key: str, stream_key: str | None) ->
""" """
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_tier_stream_panel") if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_tier_stream_panel")
if app.is_viewing_prior_session: if app.is_viewing_prior_session:
imgui.text_colored(vec4(255, 200, 100), "HISTORICAL VIEW - READ ONLY") imgui.text_colored(theme.get_color("status_warning"), "HISTORICAL VIEW - READ ONLY")
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_tier_stream_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_tier_stream_panel")
return return
if stream_key is not None: if stream_key is not None:
@@ -5024,11 +5024,11 @@ def render_tier_stream_panel(app: App, tier_key: str, stream_key: str | None) ->
ticket_id = key.split(": ", 1)[-1] if ": " in key else key ticket_id = key.split(": ", 1)[-1] if ": " in key else key
status = worker_status.get(key, "unknown") status = worker_status.get(key, "unknown")
if status == "running": if status == "running":
imgui.text_colored(imgui.ImVec4(1, 1, 0, 1), f"{ticket_id} [{status}]") imgui.text_colored(theme.get_color("status_warning"), f"{ticket_id} [{status}]")
elif status == "completed": elif status == "completed":
imgui.text_colored(imgui.ImVec4(0, 1, 0, 1), f"{ticket_id} [{status}]") imgui.text_colored(theme.get_color("status_success"), f"{ticket_id} [{status}]")
elif status == "failed": elif status == "failed":
imgui.text_colored(imgui.ImVec4(1, 0, 0, 1), f"{ticket_id} [{status}]") imgui.text_colored(theme.get_color("status_error"), f"{ticket_id} [{status}]")
else: else:
imgui.text(f"{ticket_id} [{status}]") imgui.text(f"{ticket_id} [{status}]")
imgui.begin_child(f"##tier3_{ticket_id}_scroll", imgui.ImVec2(-1, 150), True) imgui.begin_child(f"##tier3_{ticket_id}_scroll", imgui.ImVec2(-1, 150), True)
@@ -5156,9 +5156,9 @@ def render_ticket_queue(app: App) -> None:
imgui.table_next_column() imgui.table_next_column()
prio = t.get('priority', 'medium') prio = t.get('priority', 'medium')
p_col = vec4(180, 180, 180) # gray p_col = theme.get_color("text_disabled") # gray
if prio == 'high': _col = vec4(255, 100, 100) # red if prio == 'high': _col = theme.get_color("status_error") # red
elif prio == 'medium': p_col = vec4(255, 255, 100) # yellow elif prio == 'medium': p_col = theme.get_color("status_warning") # yellow
imgui.push_style_color(imgui.Col_.text, p_col) imgui.push_style_color(imgui.Col_.text, p_col)
if imgui.begin_combo(f"##prio_{tid}", prio, imgui.ComboFlags_.height_small): if imgui.begin_combo(f"##prio_{tid}", prio, imgui.ComboFlags_.height_small):
@@ -5184,7 +5184,7 @@ def render_ticket_queue(app: App) -> None:
# Status # Status
imgui.table_next_column() imgui.table_next_column()
status = t.get('status', 'todo') status = t.get('status', 'todo')
if t.get('model_override'): imgui.text_colored(imgui.ImVec4(1, 0.5, 0, 1), f"{status} [{t.get('model_override')}]") if t.get('model_override'): imgui.text_colored(theme.get_color("status_warning"), f"{status} [{t.get('model_override')}]")
else: imgui.text(t.get('status', 'todo')) else: imgui.text(t.get('status', 'todo'))
# Description # Description
@@ -5223,14 +5223,14 @@ def render_task_dag_panel(app: App) -> None: # 4. Task DAG Visualizer
int_id = abs(hash(tid)) int_id = abs(hash(tid))
ed.begin_node(ed.NodeId(int_id)) ed.begin_node(ed.NodeId(int_id))
if getattr(app, "ui_project_execution_mode", "native") == "beads": if getattr(app, "ui_project_execution_mode", "native") == "beads":
imgui.text_colored(imgui.ImVec4(0, 1, 1, 1), "[B] ") imgui.text_colored(theme.get_color("status_info"), "[B] ")
imgui.same_line() imgui.same_line()
imgui.text_colored(C_KEY, f"Ticket: {tid}") imgui.text_colored(C_KEY, f"Ticket: {tid}")
status = t.get('status', 'todo') status = t.get('status', 'todo')
s_col = C_VAL s_col = C_VAL
if status == 'done' or status == 'complete': s_col = C_IN if status == 'done' or status == 'complete': s_col = C_IN
elif status == 'in_progress' or status == 'running': s_col = C_OUT elif status == 'in_progress' or status == 'running': s_col = C_OUT
elif status == 'error': s_col = imgui.ImVec4(1, 0.4, 0.4, 1) elif status == 'error': s_col = theme.get_color("status_error")
imgui.text("Status: ") imgui.text("Status: ")
imgui.same_line() imgui.same_line()
imgui.text_colored(s_col, status) imgui.text_colored(s_col, status)
@@ -5359,7 +5359,7 @@ def render_beads_tab(app: App) -> None:
missing = [] missing = []
if not dolt_path: missing.append("'dolt'") if not dolt_path: missing.append("'dolt'")
if not bd_path: missing.append("'bd'") if not bd_path: missing.append("'bd'")
imgui.text_colored(imgui.ImVec4(1, 0.5, 0, 1), f"Warning: {', '.join(missing)} not found in PATH.") imgui.text_colored(theme.get_color("status_warning"), f"Warning: {', '.join(missing)} not found in PATH.")
imgui.text_wrapped("Beads mode requires Dolt and the Beads (bd) CLI tools.") imgui.text_wrapped("Beads mode requires Dolt and the Beads (bd) CLI tools.")
if getattr(app, "ui_project_execution_mode", "native") == "beads": if getattr(app, "ui_project_execution_mode", "native") == "beads":
@@ -5385,7 +5385,7 @@ def render_beads_tab(app: App) -> None:
imgui.text(str(b.title)) imgui.text(str(b.title))
imgui.end_table() imgui.end_table()
except Exception as e: except Exception as e:
imgui.text_colored(imgui.ImVec4(1, 0, 0, 1), f"Error loading beads: {e}") imgui.text_colored(theme.get_color("status_error"), f"Error loading beads: {e}")
def render_mma_focus_selector(app: App) -> None: def render_mma_focus_selector(app: App) -> None:
""" """
@@ -5409,7 +5409,7 @@ def render_empty_context_modal(app: App) -> None:
app.show_empty_context_modal = False app.show_empty_context_modal = False
if imgui.begin_popup_modal("Empty Context Warning", True, imgui.WindowFlags_.always_auto_resize)[0]: if imgui.begin_popup_modal("Empty Context Warning", True, imgui.WindowFlags_.always_auto_resize)[0]:
imgui.text_colored(imgui.ImVec4(1.0, 1.0, 0.0, 1.0), "WARNING: Empty Context Composition") imgui.text_colored(theme.get_color("status_warning"), "WARNING: Empty Context Composition")
imgui.text("You are attempting to generate a response without any files selected.") imgui.text("You are attempting to generate a response without any files selected.")
imgui.text("This may result in poor AI performance or loss of project context.") imgui.text("This may result in poor AI performance or loss of project context.")
imgui.separator() imgui.separator()
@@ -5436,7 +5436,7 @@ def render_context_modals(app: App) -> None:
imgui.separator() imgui.separator()
imgui.begin_child("missing_files_list", imgui.ImVec2(0, 150), True) imgui.begin_child("missing_files_list", imgui.ImVec2(0, 150), True)
for f in app.missing_context_files: for f in app.missing_context_files:
imgui.text_colored(imgui.ImVec4(1, 0.4, 0.4, 1), f) imgui.text_colored(theme.get_color("status_error"), f)
imgui.end_child() imgui.end_child()
imgui.separator() imgui.separator()
+101 -81
View File
@@ -10,6 +10,7 @@ Scale uses imgui.get_style().font_scale_main.
from imgui_bundle import imgui, hello_imgui from imgui_bundle import imgui, hello_imgui
from typing import Any, Optional from typing import Any, Optional
import typing
from contextlib import nullcontext from contextlib import nullcontext
from src import imgui_scopes as imscope from src import imgui_scopes as imscope
import src.theme_nerv import src.theme_nerv
@@ -22,22 +23,12 @@ from src.theme_models import ThemeFile, load_themes_from_dir, load_themes_from_t
# Each palette maps imgui color enum values to (R, G, B, A) floats [0..1]. # Each palette maps imgui color enum values to (R, G, B, A) floats [0..1].
# Only keys that differ from the ImGui dark defaults need to be listed. # Only keys that differ from the ImGui dark defaults need to be listed.
# ------------------------------------------------------------------ internal helpers
def _c(r: int, g: int, b: int, a: int = 255) -> tuple[float, float, float, float]: def _c(r: int, g: int, b: int, a: int = 255) -> tuple[float, float, float, float]:
""" """Convert 0-255 RGBA to 0.0-1.0 floats."""
Convert 0-255 RGBA to 0.0-1.0 floats.
[C: src/theme_nerv.py:module]
"""
return (r / 255.0, g / 255.0, b / 255.0, a / 255.0) return (r / 255.0, g / 255.0, b / 255.0, a / 255.0)
_BUILTIN_PALETTES: dict[str, dict[int, tuple]] = {
"ImGui Dark": {}, # empty = use imgui dark defaults
"NERV": {},
}
_TOML_PALETTES: dict[str, ThemeFile] = {}
_TOML_COLOUR_CACHE: dict[str, dict[int, tuple[float, float, float, float]]] = {}
def _hex(rgb: tuple[int, int, int]) -> tuple[float, float, float, float]: def _hex(rgb: tuple[int, int, int]) -> tuple[float, float, float, float]:
return (rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0, 1.0) return (rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0, 1.0)
@@ -52,19 +43,26 @@ def _build_imgui_colour_dict(theme: ThemeFile) -> dict[int, tuple[float, float,
out[enum_val] = _hex(rgb) out[enum_val] = _hex(rgb)
return out return out
def _build_semantic_colour_dict(theme: ThemeFile) -> dict[str, tuple[float, float, float, float]]:
def get_palette_names() -> list[str]: out: dict[str, tuple[float, float, float, float]] = {}
"""Returns a list of all available palettes, including hello_imgui built-ins palette_dict = theme.palette.to_dict()
and TOML-loaded themes.""" from imgui_bundle import imgui
names = list(_BUILTIN_PALETTES.keys()) for col_name, rgb in palette_dict.items():
names.extend(sorted(_TOML_PALETTES.keys())) if not hasattr(imgui.Col_, col_name):
hi_themes = [name for name in dir(hello_imgui.ImGuiTheme_) if not name.startswith('_') and name != 'count'] out[col_name] = _hex(rgb)
hi_themes = [n for n in hi_themes if not hasattr(int, n)] return out
names.extend(sorted(hi_themes))
return names
# ------------------------------------------------------------------ state # ------------------------------------------------------------------ state
_BUILTIN_PALETTES: dict[str, dict[int, tuple]] = {
"ImGui Dark": {},
"NERV": {},
}
_TOML_PALETTES: dict[str, ThemeFile] = {}
_TOML_COLOUR_CACHE: dict[str, dict[int, tuple[float, float, float, float]]] = {}
_TOML_SEMANTIC_CACHE: dict[str, dict[str, tuple[float, float, float, float]]] = {}
_current_palette: str = "10x Dark" _current_palette: str = "10x Dark"
_current_font_path: str = "fonts/Inter-Regular.ttf" _current_font_path: str = "fonts/Inter-Regular.ttf"
_current_font_size: float = 16.0 _current_font_size: float = 16.0
@@ -141,13 +139,50 @@ def set_child_transparency(val: float) -> None:
_child_transparency = val _child_transparency = val
apply(_current_palette) apply(_current_palette)
def apply(palette_name: str) -> None: def get_color(name: str, alpha: float = 1.0) -> imgui.ImVec4:
""" """Return a tone-mapped semantic color from the current palette."""
palette_name = _current_palette
if palette_name in _TOML_SEMANTIC_CACHE:
d = _TOML_SEMANTIC_CACHE[palette_name]
if name in d:
rgba = list(d[name])
rgba[3] = alpha
return imgui.ImVec4(*_tone_map(tuple(rgba), palette_name))
# Hardcoded fallbacks if not in TOML (matches ThemePalette defaults)
Apply a named palette by setting all ImGui style colors and applying global professional styling. fallbacks = {
[C: tests/test_theme.py:test_theme_apply_sets_rounding_and_padding] "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),
}
if name in fallbacks:
rgb = fallbacks[name]
return imgui.ImVec4(*_tone_map((rgb[0]/255.0, rgb[1]/255.0, rgb[2]/255.0, alpha), palette_name))
return imgui.ImVec4(1, 1, 1, alpha)
def get_role_tint(role: str) -> imgui.ImVec4:
"""Returns a subtle background tint color based on the message role."""
mapping = {
"User": "bubble_user",
"AI": "bubble_ai",
"Vendor API": "bubble_vendor",
}
return get_color(mapping.get(role, "bubble_system"), alpha=0.6)
def apply(palette_name: str) -> None:
"""Apply a named palette by setting all ImGui style colors and professional styling."""
global _current_palette global _current_palette
_current_palette = palette_name _current_palette = palette_name
if palette_name == 'NERV': if palette_name == 'NERV':
@@ -175,13 +210,10 @@ def apply(palette_name: str) -> None:
elif hasattr(hello_imgui.ImGuiTheme_, palette_name): elif hasattr(hello_imgui.ImGuiTheme_, palette_name):
theme_enum = getattr(hello_imgui.ImGuiTheme_, palette_name) theme_enum = getattr(hello_imgui.ImGuiTheme_, palette_name)
hello_imgui.apply_theme(theme_enum) hello_imgui.apply_theme(theme_enum)
# hello_imgui doesn't expose the underlying dict easily to tone-map after-the-fact
# without re-reading every enum. For now, BUILTIN and TOML themes get full TM.
else: else:
# Fallback
imgui.style_colors_dark() imgui.style_colors_dark()
# 2. Apply our "Subtle Rounding" professional tweaks on top of ANY theme # 2. Professional tweaks
style = imgui.get_style() style = imgui.get_style()
style.window_rounding = 6.0 style.window_rounding = 6.0
style.child_rounding = 4.0 style.child_rounding = 4.0
@@ -194,44 +226,50 @@ def apply(palette_name: str) -> None:
style.frame_border_size = 1.0 style.frame_border_size = 1.0
style.popup_border_size = 1.0 style.popup_border_size = 1.0
# Apply transparency to WindowBg
win_bg = style.color_(imgui.Col_.window_bg) win_bg = style.color_(imgui.Col_.window_bg)
win_bg.w = _transparency win_bg.w = _transparency
style.set_color_(imgui.Col_.window_bg, win_bg) style.set_color_(imgui.Col_.window_bg, win_bg)
# Apply child/frame transparency
for col_idx in [imgui.Col_.child_bg, imgui.Col_.frame_bg, imgui.Col_.popup_bg]: for col_idx in [imgui.Col_.child_bg, imgui.Col_.frame_bg, imgui.Col_.popup_bg]:
c = style.color_(col_idx) c = style.color_(col_idx)
c.w = _child_transparency c.w = _child_transparency
style.set_color_(col_idx, c) style.set_color_(col_idx, c)
# Spacing & Padding
style.window_padding = imgui.ImVec2(8.0, 8.0) style.window_padding = imgui.ImVec2(8.0, 8.0)
style.frame_padding = imgui.ImVec2(8.0, 4.0) style.frame_padding = imgui.ImVec2(8.0, 4.0)
style.item_spacing = imgui.ImVec2(8.0, 4.0) style.item_spacing = imgui.ImVec2(8.0, 4.0)
style.item_inner_spacing = imgui.ImVec2(4.0, 4.0) style.item_inner_spacing = imgui.ImVec2(4.0, 4.0)
style.scrollbar_size = 14.0 style.scrollbar_size = 14.0
# Rendering anti-aliasing (Shaders/Quality)
style.anti_aliased_lines = True style.anti_aliased_lines = True
style.anti_aliased_fill = True style.anti_aliased_fill = True
style.anti_aliased_lines_use_tex = True style.anti_aliased_lines_use_tex = True
# 3. Sync syntax palette and clear markdown render cache # 3. Sync syntax palette and clear markdown render cache
apply_syntax_palette(get_syntax_palette_for_theme(palette_name)) apply_syntax_palette(get_syntax_palette_for_theme(palette_name))
import src.markdown_helper try:
src.markdown_helper.get_renderer().clear_cache() import src.markdown_helper
src.markdown_helper.get_renderer().clear_cache()
except (ImportError, AttributeError):
pass
def set_scale(factor: float) -> None: def apply_current() -> None:
"""Set the global font/UI scale factor.""" """Apply the loaded palette and scale."""
global _current_scale apply(_current_palette)
_current_scale = factor set_scale(_current_scale)
style = imgui.get_style()
style.font_scale_main = factor def get_palette_names() -> list[str]:
"""Returns a list of all available palettes."""
names = list(_BUILTIN_PALETTES.keys())
names.extend(sorted(_TOML_PALETTES.keys()))
hi_themes = [name for name in dir(hello_imgui.ImGuiTheme_) if not name.startswith('_') and name != 'count']
hi_themes = [n for n in hi_themes if not hasattr(int, n)]
names.extend(sorted(hi_themes))
return names
# ------------------------------------------------------------------ persistence
def save_to_config(config: dict) -> None: def save_to_config(config: dict) -> None:
"""Persist theme settings into the config dict under [theme].""" """Persist theme settings into the config dict."""
import sys
config.setdefault("theme", {}) config.setdefault("theme", {})
config["theme"]["palette"] = _current_palette config["theme"]["palette"] = _current_palette
config["theme"]["font_path"] = _current_font_path config["theme"]["font_path"] = _current_font_path
@@ -239,7 +277,6 @@ def save_to_config(config: dict) -> None:
config["theme"]["scale"] = _current_scale config["theme"]["scale"] = _current_scale
config["theme"]["transparency"] = _transparency config["theme"]["transparency"] = _transparency
config["theme"]["child_transparency"] = _child_transparency config["theme"]["child_transparency"] = _child_transparency
# Tone mapping
tm = {} tm = {}
for p in set(list(_brightness.keys()) + list(_contrast.keys()) + list(_gamma.keys())): for p in set(list(_brightness.keys()) + list(_contrast.keys()) + list(_gamma.keys())):
tm[p] = { tm[p] = {
@@ -248,49 +285,37 @@ def save_to_config(config: dict) -> None:
"gamma": _gamma.get(p, 1.0) "gamma": _gamma.get(p, 1.0)
} }
config["theme"]["tone_mapping"] = tm config["theme"]["tone_mapping"] = tm
sys.stderr.write(f"[DEBUG theme_2] save_to_config: palette={_current_palette}\n")
sys.stderr.flush()
def load_from_config(config: dict) -> None: def load_from_config(config: dict) -> None:
"""Read [theme] from config. Font is handled separately at startup.""" """Read [theme] from config."""
import sys import sys
global _current_font_path, _current_font_size, _current_scale, _current_palette, _transparency, _child_transparency, _brightness, _contrast, _gamma global _current_font_path, _current_font_size, _current_scale, _current_palette, _transparency, _child_transparency, _brightness, _contrast, _gamma
t = config.get("theme", {}) t = config.get("theme", {})
_current_palette = t.get("palette", "10x Dark") _current_palette = t.get("palette", "10x Dark")
if _current_palette in ("", "DPG Default"): if _current_palette in ("", "DPG Default"):
_current_palette = "10x Dark" _current_palette = "10x Dark"
_current_font_path = t.get("font_path", "fonts/Inter-Regular.ttf") _current_font_path = t.get("font_path", "fonts/Inter-Regular.ttf")
_current_font_size = float(t.get("font_size", 16.0)) _current_font_size = float(t.get("font_size", 16.0))
_current_scale = float(t.get("scale", 1.0)) _current_scale = float(t.get("scale", 1.0))
_transparency = float(t.get("transparency", 1.0)) _transparency = float(t.get("transparency", 1.0))
_child_transparency = float(t.get("child_transparency", 1.0)) _child_transparency = float(t.get("child_transparency", 1.0))
# Tone mapping
tm = t.get("tone_mapping", {}) tm = t.get("tone_mapping", {})
_brightness = {p: float(v.get("brightness", 1.0)) for p, v in tm.items()} _brightness = {p: float(v.get("brightness", 1.0)) for p, v in tm.items()}
_contrast = {p: float(v.get("contrast", 1.0)) for p, v in tm.items()} _contrast = {p: float(v.get("contrast", 1.0)) for p, v in tm.items()}
_gamma = {p: float(v.get("gamma", 1.0)) for p, v in tm.items()} _gamma = {p: float(v.get("gamma", 1.0)) for p, v in tm.items()}
sys.stderr.write(f"[DEBUG theme_2] load_from_config: palette={_current_palette}\n")
sys.stderr.flush()
def apply_current() -> None:
"""Apply the loaded palette and scale. Call after imgui context exists."""
apply(_current_palette)
set_scale(_current_scale)
# ------------------------------------------------------------------ external themes
def load_themes_from_disk() -> None: def load_themes_from_disk() -> None:
"""Load all themes from the global themes directory. Each *.toml file """Load all themes from the global themes directory."""
in the directory is one theme. Idempotent - safe to call repeatedly. global _TOML_PALETTES, _TOML_COLOUR_CACHE, _TOML_SEMANTIC_CACHE
Broken entries are logged and skipped."""
global _TOML_PALETTES, _TOML_COLOUR_CACHE
themes_dir = get_global_themes_path() themes_dir = get_global_themes_path()
loaded: dict[str, ThemeFile] = {} loaded: dict[str, ThemeFile] = {}
if themes_dir.exists() and themes_dir.is_dir(): if themes_dir.exists() and themes_dir.is_dir():
loaded.update(load_themes_from_dir(themes_dir, scope="global")) loaded.update(load_themes_from_dir(themes_dir, scope="global"))
_TOML_PALETTES = loaded _TOML_PALETTES = loaded
_TOML_COLOUR_CACHE = {name: _build_imgui_colour_dict(t) for name, t in loaded.items()} _TOML_COLOUR_CACHE = {name: _build_imgui_colour_dict(t) for name, t in loaded.items()}
_TOML_SEMANTIC_CACHE = {name: _build_semantic_colour_dict(t) for name, t in loaded.items()}
def get_syntax_palette_for_theme(theme_name: str) -> str: def get_syntax_palette_for_theme(theme_name: str) -> str:
"""Return the syntax palette name (one of dark/light/mariana/retro_blue) """Return the syntax palette name (one of dark/light/mariana/retro_blue)
@@ -300,7 +325,6 @@ def get_syntax_palette_for_theme(theme_name: str) -> str:
return _TOML_PALETTES[theme_name].syntax_palette return _TOML_PALETTES[theme_name].syntax_palette
return "dark" return "dark"
def apply_syntax_palette(palette_name: str) -> None: def apply_syntax_palette(palette_name: str) -> None:
"""Set the default imgui_color_text_edit palette. palette_name must """Set the default imgui_color_text_edit palette. palette_name must
be one of: dark, light, mariana, retro_blue. No-op for unknown names.""" be one of: dark, light, mariana, retro_blue. No-op for unknown names."""
@@ -312,46 +336,42 @@ def apply_syntax_palette(palette_name: str) -> None:
return return
ed.TextEditor.set_default_palette(palette_id) ed.TextEditor.set_default_palette(palette_id)
# ------------------------------------------------------------------ font & scaling
load_themes_from_disk() def set_scale(factor: float) -> None:
global _current_scale
_current_scale = factor
imgui.get_style().font_scale_main = factor
def get_font_loading_params() -> tuple[str, float]: def get_font_loading_params() -> tuple[str, float]:
"""Return (font_path, font_size) for use during hello_imgui font loading callback."""
return _current_font_path, _current_font_size return _current_font_path, _current_font_size
def get_tweaked_theme() -> hello_imgui.ImGuiTweakedTheme: def get_tweaked_theme() -> hello_imgui.ImGuiTweakedTheme:
"""Returns an ImGuiTweakedTheme object reflecting the current state."""
tt = hello_imgui.ImGuiTweakedTheme() tt = hello_imgui.ImGuiTweakedTheme()
if hasattr(hello_imgui.ImGuiTheme_, _current_palette): if hasattr(hello_imgui.ImGuiTheme_, _current_palette):
tt.theme = getattr(hello_imgui.ImGuiTheme_, _current_palette) tt.theme = getattr(hello_imgui.ImGuiTheme_, _current_palette)
else: else:
tt.theme = hello_imgui.ImGuiTheme_.imgui_colors_dark tt.theme = hello_imgui.ImGuiTheme_.imgui_colors_dark
# Sync tweaks
tt.tweaks.rounding = 6.0 tt.tweaks.rounding = 6.0
return tt return tt
# ------------------------------------------------------------------ specialized colors
def ai_text_color() -> imgui.ImVec4: def ai_text_color() -> imgui.ImVec4:
"""Returns DATA_GREEN if NERV is active, otherwise standard text color."""
if is_nerv_active(): if is_nerv_active():
return imgui.ImVec4(*DATA_GREEN) return imgui.ImVec4(*DATA_GREEN)
return imgui.get_style().color_(imgui.Col_.text) return imgui.get_style().color_(imgui.Col_.text)
def ai_text_style(): def ai_text_style():
"""Context manager for AI response text styling."""
return imscope.style_color(imgui.Col_.text, ai_text_color()) return imscope.style_color(imgui.Col_.text, ai_text_color())
def get_role_tint(role: str) -> imgui.ImVec4:
"""Returns a subtle background tint color based on the message role.""" """Returns a subtle background tint color based on the message role."""
# Deep, low-saturation tints for distinct role hierarchy
if role == "User": return imgui.ImVec4(0.12, 0.18, 0.30, 0.6) # Midnight Blue
elif role == "AI": return imgui.ImVec4(0.14, 0.25, 0.18, 0.6) # Forest Green
elif role == "Vendor API": return imgui.ImVec4(0.25, 0.22, 0.12, 0.5) # Bronze
return imgui.ImVec4(0.1, 0.1, 0.1, 0.4) # Neutral System
def render_post_fx(width: float, height: float, ai_status: str, crt_enabled: bool) -> None: def render_post_fx(width: float, height: float, ai_status: str, crt_enabled: bool) -> None:
"""Updates and renders the alert and CRT filters.""" """Updates and renders the alert and CRT filters."""
_alert_pulsing.update(ai_status) _alert_pulsing.update(ai_status)
_alert_pulsing.render(width, height) _alert_pulsing.render(width, height)
_crt_filter.enabled = crt_enabled _crt_filter.enabled = crt_enabled
_crt_filter.render(width, height) _crt_filter.render(width, height)
# ------------------------------------------------------------------ init
load_themes_from_disk()
+18 -1
View File
@@ -9,7 +9,6 @@ from typing import Any
VALID_SYNTAX_PALETTES: tuple[str, ...] = ("dark", "light", "mariana", "retro_blue") VALID_SYNTAX_PALETTES: tuple[str, ...] = ("dark", "light", "mariana", "retro_blue")
@dataclass
@dataclass @dataclass
class ThemePalette: class ThemePalette:
window_bg: tuple[int, int, int] = (0, 0, 0) window_bg: tuple[int, int, int] = (0, 0, 0)
@@ -75,6 +74,24 @@ class ThemePalette:
tree_lines: tuple[int, int, int] = (60, 60, 60) tree_lines: tuple[int, int, int] = (60, 60, 60)
unsaved_marker: tuple[int, int, int] = (200, 200, 200) unsaved_marker: tuple[int, int, int] = (200, 200, 200)
# Semantic colors
status_success: tuple[int, int, int] = (80, 255, 80)
status_warning: tuple[int, int, int] = (255, 152, 48)
status_error: tuple[int, int, int] = (255, 72, 64)
status_info: tuple[int, int, int] = (0, 255, 255)
bubble_user: tuple[int, int, int] = (30, 45, 75)
bubble_ai: tuple[int, int, int] = (35, 65, 45)
bubble_vendor: tuple[int, int, int] = (65, 55, 30)
bubble_system: tuple[int, int, int] = (25, 25, 25)
slice_manual: tuple[int, int, int] = (255, 165, 0)
slice_auto: tuple[int, int, int] = (0, 255, 0)
slice_selection: tuple[int, int, int] = (100, 100, 255)
# Diff colors
diff_added: tuple[int, int, int] = (51, 230, 51)
diff_removed: tuple[int, int, int] = (230, 51, 51)
diff_header: tuple[int, int, int] = (77, 178, 255)
@classmethod @classmethod
def from_dict(cls, data: dict[str, Any]) -> ThemePalette: def from_dict(cls, data: dict[str, Any]) -> ThemePalette:
kwargs: dict[str, Any] = {} kwargs: dict[str, Any] = {}
+48 -34
View File
@@ -3,53 +3,67 @@ syntax_palette = "dark"
description = "10x Dark Theme" description = "10x Dark Theme"
[colors] [colors]
window_bg = [ 34, 32, 28]
child_bg = [ 30, 28, 24]
popup_bg = [ 35, 30, 20]
border = [ 60, 55, 50] border = [ 60, 55, 50]
border_shadow = [ 0, 0, 0] border_shadow = [ 0, 0, 0]
bubble_ai = [ 35, 65, 45]
bubble_system = [ 25, 25, 25]
bubble_user = [ 30, 45, 75]
bubble_vendor = [ 65, 55, 30]
button = [ 83, 76, 60]
button_active = [115, 90, 70]
button_hovered = [126, 78, 14]
check_mark = [194, 164, 74]
child_bg = [ 30, 28, 24]
diff_added = [ 51, 230, 51]
diff_header = [ 77, 178, 255]
diff_removed = [230, 51, 51]
docking_empty_bg = [ 20, 20, 20]
docking_preview = [126, 78, 14]
frame_bg = [ 45, 42, 38] frame_bg = [ 45, 42, 38]
frame_bg_hovered = [ 60, 56, 50]
frame_bg_active = [ 75, 70, 62] frame_bg_active = [ 75, 70, 62]
title_bg = [ 40, 35, 25] frame_bg_hovered = [ 60, 56, 50]
title_bg_active = [ 60, 45, 15] header = [ 83, 76, 60]
title_bg_collapsed = [ 30, 27, 20] header_active = [115, 90, 70]
header_hovered = [126, 78, 14]
menu_bar_bg = [ 35, 30, 20] menu_bar_bg = [ 35, 30, 20]
modal_window_dim_bg = [ 10, 10, 10]
nav_cursor = [126, 78, 14]
nav_windowing_dim_bg = [ 20, 20, 20]
nav_windowing_highlight = [194, 164, 74]
popup_bg = [ 35, 30, 20]
resize_grip = [ 60, 55, 44]
resize_grip_active = [194, 164, 74]
resize_grip_hovered = [126, 78, 14]
scrollbar_bg = [ 30, 28, 24] scrollbar_bg = [ 30, 28, 24]
scrollbar_grab = [ 80, 78, 72] scrollbar_grab = [ 80, 78, 72]
scrollbar_grab_hovered = [100, 100, 92]
scrollbar_grab_active = [120, 118, 110] scrollbar_grab_active = [120, 118, 110]
check_mark = [194, 164, 74] scrollbar_grab_hovered = [100, 100, 92]
separator = [ 70, 65, 55]
separator_active = [194, 164, 74]
separator_hovered = [126, 78, 14]
slice_auto = [ 0, 255, 0]
slice_manual = [255, 165, 0]
slice_selection = [100, 100, 255]
slider_grab = [126, 78, 14] slider_grab = [126, 78, 14]
slider_grab_active = [194, 140, 30] slider_grab_active = [194, 140, 30]
button = [ 83, 76, 60] status_error = [255, 72, 64]
button_hovered = [126, 78, 14] status_info = [ 0, 255, 255]
button_active = [115, 90, 70] status_success = [ 80, 255, 80]
header = [ 83, 76, 60] status_warning = [255, 152, 48]
header_hovered = [126, 78, 14]
header_active = [115, 90, 70]
separator = [ 70, 65, 55]
separator_hovered = [126, 78, 14]
separator_active = [194, 164, 74]
resize_grip = [ 60, 55, 44]
resize_grip_hovered = [126, 78, 14]
resize_grip_active = [194, 164, 74]
tab = [ 83, 83, 70] tab = [ 83, 83, 70]
tab_hovered = [126, 77, 25]
tab_selected = [126, 77, 25]
tab_dimmed = [ 60, 58, 50] tab_dimmed = [ 60, 58, 50]
tab_dimmed_selected = [ 90, 80, 55] tab_dimmed_selected = [ 90, 80, 55]
docking_preview = [126, 78, 14] tab_hovered = [126, 77, 25]
docking_empty_bg = [ 20, 20, 20] tab_selected = [126, 77, 25]
table_border_light = [ 50, 47, 42]
table_border_strong = [ 70, 65, 55]
table_header_bg = [ 55, 50, 38]
table_row_bg = [ 0, 0, 0]
table_row_bg_alt = [ 40, 38, 34]
text = [200, 200, 200] text = [200, 200, 200]
text_disabled = [130, 130, 120] text_disabled = [130, 130, 120]
text_selected_bg = [ 59, 86, 142] text_selected_bg = [ 59, 86, 142]
table_header_bg = [ 55, 50, 38] title_bg = [ 40, 35, 25]
table_border_strong = [ 70, 65, 55] title_bg_active = [ 60, 45, 15]
table_border_light = [ 50, 47, 42] title_bg_collapsed = [ 30, 27, 20]
table_row_bg = [ 0, 0, 0] window_bg = [ 34, 32, 28]
table_row_bg_alt = [ 40, 38, 34]
nav_cursor = [126, 78, 14]
nav_windowing_highlight = [194, 164, 74]
nav_windowing_dim_bg = [ 20, 20, 20]
modal_window_dim_bg = [ 10, 10, 10]
+51 -37
View File
@@ -3,40 +3,54 @@ syntax_palette = "light"
description = "Binks Theme" description = "Binks Theme"
[colors] [colors]
text = [ 0, 0, 0] border = [ 0, 0, 0]
text_disabled = [153, 153, 153] border_shadow = [255, 255, 255]
window_bg = [240, 240, 240] bubble_ai = [220, 255, 220]
child_bg = [ 0, 0, 0] bubble_system = [240, 240, 240]
popup_bg = [255, 255, 255] bubble_user = [220, 230, 255]
border = [ 0, 0, 0] bubble_vendor = [255, 240, 200]
border_shadow = [255, 255, 255] button = [ 66, 150, 250]
frame_bg = [255, 255, 255] button_active = [ 15, 135, 250]
frame_bg_hovered = [ 66, 150, 250] button_hovered = [ 66, 150, 250]
frame_bg_active = [ 66, 150, 250] check_mark = [ 66, 150, 250]
title_bg = [245, 245, 245] child_bg = [ 0, 0, 0]
title_bg_collapsed = [255, 255, 255] diff_added = [ 40, 180, 40]
title_bg_active = [209, 209, 209] diff_header = [ 40, 100, 200]
menu_bar_bg = [219, 219, 219] diff_removed = [200, 40, 40]
scrollbar_bg = [250, 250, 250] frame_bg = [255, 255, 255]
scrollbar_grab = [176, 176, 176] frame_bg_active = [ 66, 150, 250]
scrollbar_grab_hovered = [150, 150, 150] frame_bg_hovered = [ 66, 150, 250]
scrollbar_grab_active = [125, 125, 125] header = [ 66, 150, 250]
check_mark = [ 66, 150, 250] header_active = [ 66, 150, 250]
slider_grab = [ 61, 133, 224] header_hovered = [ 66, 150, 250]
slider_grab_active = [ 66, 150, 250] menu_bar_bg = [219, 219, 219]
button = [ 66, 150, 250] modal_window_dim_bg = [ 51, 51, 51]
button_hovered = [ 66, 150, 250] plot_histogram = [230, 178, 0]
button_active = [ 15, 135, 250] plot_histogram_hovered = [255, 153, 0]
header = [ 66, 150, 250] plot_lines = [ 99, 99, 99]
header_hovered = [ 66, 150, 250] plot_lines_hovered = [255, 110, 89]
header_active = [ 66, 150, 250] popup_bg = [255, 255, 255]
separator = [100, 100, 100] resize_grip = [255, 255, 255]
resize_grip = [255, 255, 255] resize_grip_active = [ 66, 150, 250]
resize_grip_hovered = [ 66, 150, 250] resize_grip_hovered = [ 66, 150, 250]
resize_grip_active = [ 66, 150, 250] scrollbar_bg = [250, 250, 250]
plot_lines = [ 99, 99, 99] scrollbar_grab = [176, 176, 176]
plot_lines_hovered = [255, 110, 89] scrollbar_grab_active = [125, 125, 125]
plot_histogram = [230, 178, 0] scrollbar_grab_hovered = [150, 150, 150]
plot_histogram_hovered = [255, 153, 0] separator = [100, 100, 100]
text_selected_bg = [ 66, 150, 250] slice_auto = [ 80, 255, 80]
modal_window_dim_bg = [ 51, 51, 51] slice_manual = [255, 200, 0]
slice_selection = [180, 180, 255]
slider_grab = [ 61, 133, 224]
slider_grab_active = [ 66, 150, 250]
status_error = [200, 40, 40]
status_info = [ 40, 100, 200]
status_success = [ 40, 180, 40]
status_warning = [200, 140, 0]
text = [ 0, 0, 0]
text_disabled = [153, 153, 153]
text_selected_bg = [ 66, 150, 250]
title_bg = [245, 245, 245]
title_bg_active = [209, 209, 209]
title_bg_collapsed = [255, 255, 255]
window_bg = [240, 240, 240]
+47 -33
View File
@@ -1,38 +1,52 @@
# Gruvbox Dark — Pavel Pertsev's warm retro palette name = "gruvbox_dark"
syntax_palette = "retro_blue" syntax_palette = "retro_blue"
description = "Gruvbox Dark by Pavel Pertsev (github.com/morhetz/gruvbox)" description = "Gruvbox Dark by Pavel Pertsev (github.com/morhetz/gruvbox)"
[colors] [colors]
window_bg = [ 40, 40, 40] # bg border = [ 60, 56, 54]
child_bg = [ 50, 48, 47] # bg1 bubble_ai = [ 35, 65, 45]
popup_bg = [ 50, 48, 47] bubble_system = [ 25, 25, 25]
border = [ 60, 56, 54] bubble_user = [ 30, 45, 75]
frame_bg = [ 50, 48, 47] bubble_vendor = [ 65, 55, 30]
frame_bg_hovered = [ 80, 80, 80] button = [ 60, 56, 54]
frame_bg_active = [ 90, 90, 90] button_active = [200, 140, 0]
title_bg = [ 40, 40, 40] button_hovered = [180, 120, 40]
title_bg_active = [ 80, 80, 80] check_mark = [184, 187, 38]
menu_bar_bg = [ 40, 40, 40] child_bg = [ 50, 48, 47]
scrollbar_bg = [ 40, 40, 40] diff_added = [ 51, 230, 51]
scrollbar_grab = [ 80, 80, 80] diff_header = [ 77, 178, 255]
scrollbar_grab_hovered = [251, 241, 199] diff_removed = [230, 51, 51]
frame_bg = [ 50, 48, 47]
frame_bg_active = [ 90, 90, 90]
frame_bg_hovered = [ 80, 80, 80]
header = [ 60, 56, 54]
header_active = [251, 73, 52]
header_hovered = [180, 120, 40]
menu_bar_bg = [ 40, 40, 40]
popup_bg = [ 50, 48, 47]
scrollbar_bg = [ 40, 40, 40]
scrollbar_grab = [ 80, 80, 80]
scrollbar_grab_active = [251, 241, 199] scrollbar_grab_active = [251, 241, 199]
button = [ 60, 56, 54] scrollbar_grab_hovered = [251, 241, 199]
button_hovered = [180, 120, 40] # orange separator = [ 80, 80, 80]
button_active = [200, 140, 0] # bright orange separator_active = [251, 241, 199]
header = [ 60, 56, 54] separator_hovered = [180, 120, 40]
header_hovered = [180, 120, 40] slice_auto = [ 0, 255, 0]
header_active = [251, 73, 52] # red slice_manual = [255, 165, 0]
separator = [ 80, 80, 80] slice_selection = [100, 100, 255]
separator_hovered = [180, 120, 40] slider_grab = [184, 187, 38]
separator_active = [251, 241, 199] slider_grab_active = [184, 187, 38]
tab = [ 60, 56, 54] status_error = [255, 72, 64]
tab_hovered = [180, 120, 40] status_info = [ 0, 255, 255]
tab_selected = [ 80, 80, 80] status_success = [ 80, 255, 80]
text = [251, 241, 199] # fg status_warning = [255, 152, 48]
text_disabled = [146, 131, 116] # comment tab = [ 60, 56, 54]
text_selected_bg = [180, 120, 40] tab_hovered = [180, 120, 40]
check_mark = [184, 187, 38] # green tab_selected = [ 80, 80, 80]
slider_grab = [184, 187, 38] table_header_bg = [ 60, 56, 54]
slider_grab_active = [184, 187, 38] text = [251, 241, 199]
table_header_bg = [ 60, 56, 54] text_disabled = [146, 131, 116]
text_selected_bg = [180, 120, 40]
title_bg = [ 40, 40, 40]
title_bg_active = [ 80, 80, 80]
window_bg = [ 40, 40, 40]
+62 -48
View File
@@ -3,51 +3,65 @@ syntax_palette = "dark"
description = "Monokai Theme" description = "Monokai Theme"
[colors] [colors]
window_bg = [ 39, 40, 34] border = [ 60, 61, 52]
child_bg = [ 34, 35, 29] border_shadow = [ 0, 0, 0]
popup_bg = [ 39, 40, 34] bubble_ai = [ 35, 65, 45]
border = [ 60, 61, 52] bubble_system = [ 25, 25, 25]
border_shadow = [ 0, 0, 0] bubble_user = [ 30, 45, 75]
frame_bg = [ 50, 51, 44] bubble_vendor = [ 65, 55, 30]
frame_bg_hovered = [ 65, 67, 56] button = [ 73, 72, 62]
frame_bg_active = [ 80, 82, 68] button_active = [198, 30, 92]
title_bg = [ 39, 40, 34] button_hovered = [249, 38, 114]
title_bg_active = [ 73, 72, 62] check_mark = [166, 226, 46]
title_bg_collapsed = [ 30, 31, 26] child_bg = [ 34, 35, 29]
menu_bar_bg = [ 50, 51, 44] diff_added = [ 51, 230, 51]
scrollbar_bg = [ 34, 35, 29] diff_header = [ 77, 178, 255]
scrollbar_grab = [ 80, 80, 72] diff_removed = [230, 51, 51]
scrollbar_grab_hovered = [102, 217, 39] docking_empty_bg = [ 20, 20, 18]
scrollbar_grab_active = [166, 226, 46] docking_preview = [249, 38, 114]
check_mark = [166, 226, 46] frame_bg = [ 50, 51, 44]
slider_grab = [102, 217, 39] frame_bg_active = [ 80, 82, 68]
slider_grab_active = [166, 226, 46] frame_bg_hovered = [ 65, 67, 56]
button = [ 73, 72, 62] header = [ 73, 72, 62]
button_hovered = [249, 38, 114] header_active = [198, 30, 92]
button_active = [198, 30, 92] header_hovered = [249, 38, 114]
header = [ 73, 72, 62] menu_bar_bg = [ 50, 51, 44]
header_hovered = [249, 38, 114] modal_window_dim_bg = [ 10, 10, 8]
header_active = [198, 30, 92] nav_cursor = [166, 226, 46]
separator = [ 60, 61, 52] popup_bg = [ 39, 40, 34]
separator_hovered = [249, 38, 114] resize_grip = [ 73, 72, 62]
separator_active = [166, 226, 46] resize_grip_active = [166, 226, 46]
resize_grip = [ 73, 72, 62] resize_grip_hovered = [249, 38, 114]
resize_grip_hovered = [249, 38, 114] scrollbar_bg = [ 34, 35, 29]
resize_grip_active = [166, 226, 46] scrollbar_grab = [ 80, 80, 72]
tab = [ 73, 72, 62] scrollbar_grab_active = [166, 226, 46]
tab_hovered = [249, 38, 114] scrollbar_grab_hovered = [102, 217, 39]
tab_selected = [249, 38, 114] separator = [ 60, 61, 52]
tab_dimmed = [ 50, 51, 44] separator_active = [166, 226, 46]
tab_dimmed_selected = [ 90, 88, 76] separator_hovered = [249, 38, 114]
docking_preview = [249, 38, 114] slice_auto = [ 0, 255, 0]
docking_empty_bg = [ 20, 20, 18] slice_manual = [255, 165, 0]
text = [248, 248, 242] slice_selection = [100, 100, 255]
text_disabled = [117, 113, 94] slider_grab = [102, 217, 39]
text_selected_bg = [249, 38, 114] slider_grab_active = [166, 226, 46]
table_header_bg = [ 60, 61, 52] status_error = [255, 72, 64]
table_border_strong = [ 73, 72, 62] status_info = [ 0, 255, 255]
table_border_light = [ 55, 56, 48] status_success = [ 80, 255, 80]
table_row_bg = [ 0, 0, 0] status_warning = [255, 152, 48]
table_row_bg_alt = [ 50, 51, 44] tab = [ 73, 72, 62]
nav_cursor = [166, 226, 46] tab_dimmed = [ 50, 51, 44]
modal_window_dim_bg = [ 10, 10, 8] tab_dimmed_selected = [ 90, 88, 76]
tab_hovered = [249, 38, 114]
tab_selected = [249, 38, 114]
table_border_light = [ 55, 56, 48]
table_border_strong = [ 73, 72, 62]
table_header_bg = [ 60, 61, 52]
table_row_bg = [ 0, 0, 0]
table_row_bg_alt = [ 50, 51, 44]
text = [248, 248, 242]
text_disabled = [117, 113, 94]
text_selected_bg = [249, 38, 114]
title_bg = [ 39, 40, 34]
title_bg_active = [ 73, 72, 62]
title_bg_collapsed = [ 30, 31, 26]
window_bg = [ 39, 40, 34]
+47 -33
View File
@@ -1,38 +1,52 @@
# Moss — green-tinted dark theme name = "moss"
syntax_palette = "mariana" syntax_palette = "mariana"
description = "Moss — green-tinted dark theme" description = "Moss — green-tinted dark theme"
[colors] [colors]
window_bg = [ 40, 47, 49] # green-gray border = [ 60, 80, 90]
child_bg = [ 24, 32, 30] bubble_ai = [ 35, 65, 45]
popup_bg = [ 20, 35, 35] bubble_system = [ 25, 25, 25]
border = [ 60, 80, 90] bubble_user = [ 30, 45, 75]
frame_bg = [ 50, 70, 80] bubble_vendor = [ 65, 55, 30]
frame_bg_hovered = [ 60, 90, 100] button = [ 60, 80, 90]
frame_bg_active = [ 70, 100, 110] button_active = [120, 80, 200]
title_bg = [ 40, 47, 49] button_hovered = [105, 101, 255]
title_bg_active = [ 42, 77, 50] # mossy green check_mark = [120, 160, 130]
menu_bar_bg = [ 40, 47, 49] child_bg = [ 24, 32, 30]
scrollbar_bg = [ 40, 47, 49] diff_added = [ 51, 230, 51]
scrollbar_grab = [ 80, 80, 80] diff_header = [ 77, 178, 255]
scrollbar_grab_hovered = [100, 100, 100] diff_removed = [230, 51, 51]
frame_bg = [ 50, 70, 80]
frame_bg_active = [ 70, 100, 110]
frame_bg_hovered = [ 60, 90, 100]
header = [ 60, 80, 90]
header_active = [ 42, 77, 50]
header_hovered = [120, 160, 130]
menu_bar_bg = [ 40, 47, 49]
popup_bg = [ 20, 35, 35]
scrollbar_bg = [ 40, 47, 49]
scrollbar_grab = [ 80, 80, 80]
scrollbar_grab_active = [120, 120, 120] scrollbar_grab_active = [120, 120, 120]
button = [ 60, 80, 90] scrollbar_grab_hovered = [100, 100, 100]
button_hovered = [105, 101, 255] # blue accent separator = [ 60, 80, 90]
button_active = [120, 80, 200] separator_active = [105, 101, 255]
header = [ 60, 80, 90] separator_hovered = [120, 160, 130]
header_hovered = [120, 160, 130] # green slice_auto = [ 0, 255, 0]
header_active = [ 42, 77, 50] # mossy green slice_manual = [255, 165, 0]
separator = [ 60, 80, 90] slice_selection = [100, 100, 255]
separator_hovered = [120, 160, 130] slider_grab = [120, 160, 130]
separator_active = [105, 101, 255] slider_grab_active = [120, 160, 130]
tab = [ 60, 80, 90] status_error = [255, 72, 64]
tab_hovered = [ 80, 100, 110] status_info = [ 0, 255, 255]
tab_selected = [ 42, 77, 50] # mossy green status_success = [ 80, 255, 80]
text = [255, 255, 255] status_warning = [255, 152, 48]
text_disabled = [208, 208, 208] tab = [ 60, 80, 90]
text_selected_bg = [105, 101, 255] tab_hovered = [ 80, 100, 110]
check_mark = [120, 160, 130] # green tab_selected = [ 42, 77, 50]
slider_grab = [120, 160, 130] table_header_bg = [ 50, 70, 80]
slider_grab_active = [120, 160, 130] text = [255, 255, 255]
table_header_bg = [ 50, 70, 80] text_disabled = [208, 208, 208]
text_selected_bg = [105, 101, 255]
title_bg = [ 40, 47, 49]
title_bg_active = [ 42, 77, 50]
window_bg = [ 40, 47, 49]
+62 -48
View File
@@ -3,51 +3,65 @@ syntax_palette = "dark"
description = "Nord Dark Theme" description = "Nord Dark Theme"
[colors] [colors]
window_bg = [ 36, 41, 49] border = [ 59, 66, 82]
child_bg = [ 30, 34, 42] border_shadow = [ 0, 0, 0]
popup_bg = [ 36, 41, 49] bubble_ai = [ 35, 65, 45]
border = [ 59, 66, 82] bubble_system = [ 25, 25, 25]
border_shadow = [ 0, 0, 0] bubble_user = [ 30, 45, 75]
frame_bg = [ 46, 52, 64] bubble_vendor = [ 65, 55, 30]
frame_bg_hovered = [ 59, 66, 82] button = [ 59, 66, 82]
frame_bg_active = [ 67, 76, 94] button_active = [129, 161, 193]
title_bg = [ 36, 41, 49] button_hovered = [ 94, 129, 172]
title_bg_active = [ 59, 66, 82] check_mark = [136, 192, 208]
title_bg_collapsed = [ 30, 34, 42] child_bg = [ 30, 34, 42]
menu_bar_bg = [ 46, 52, 64] diff_added = [ 51, 230, 51]
scrollbar_bg = [ 30, 34, 42] diff_header = [ 77, 178, 255]
scrollbar_grab = [ 76, 86, 106] diff_removed = [230, 51, 51]
scrollbar_grab_hovered = [ 94, 129, 172] docking_empty_bg = [ 20, 22, 28]
scrollbar_grab_active = [129, 161, 193] docking_preview = [ 94, 129, 172]
check_mark = [136, 192, 208] frame_bg = [ 46, 52, 64]
slider_grab = [ 94, 129, 172] frame_bg_active = [ 67, 76, 94]
slider_grab_active = [129, 161, 193] frame_bg_hovered = [ 59, 66, 82]
button = [ 59, 66, 82] header = [ 59, 66, 82]
button_hovered = [ 94, 129, 172] header_active = [129, 161, 193]
button_active = [129, 161, 193] header_hovered = [ 94, 129, 172]
header = [ 59, 66, 82] menu_bar_bg = [ 46, 52, 64]
header_hovered = [ 94, 129, 172] modal_window_dim_bg = [ 10, 12, 16]
header_active = [129, 161, 193] nav_cursor = [136, 192, 208]
separator = [ 59, 66, 82] popup_bg = [ 36, 41, 49]
separator_hovered = [ 94, 129, 172] resize_grip = [ 59, 66, 82]
separator_active = [136, 192, 208] resize_grip_active = [136, 192, 208]
resize_grip = [ 59, 66, 82] resize_grip_hovered = [ 94, 129, 172]
resize_grip_hovered = [ 94, 129, 172] scrollbar_bg = [ 30, 34, 42]
resize_grip_active = [136, 192, 208] scrollbar_grab = [ 76, 86, 106]
tab = [ 46, 52, 64] scrollbar_grab_active = [129, 161, 193]
tab_hovered = [ 94, 129, 172] scrollbar_grab_hovered = [ 94, 129, 172]
tab_selected = [ 76, 86, 106] separator = [ 59, 66, 82]
tab_dimmed = [ 36, 41, 49] separator_active = [136, 192, 208]
tab_dimmed_selected = [ 59, 66, 82] separator_hovered = [ 94, 129, 172]
docking_preview = [ 94, 129, 172] slice_auto = [ 0, 255, 0]
docking_empty_bg = [ 20, 22, 28] slice_manual = [255, 165, 0]
text = [216, 222, 233] slice_selection = [100, 100, 255]
text_disabled = [116, 128, 150] slider_grab = [ 94, 129, 172]
text_selected_bg = [ 94, 129, 172] slider_grab_active = [129, 161, 193]
table_header_bg = [ 59, 66, 82] status_error = [255, 72, 64]
table_border_strong = [ 76, 86, 106] status_info = [ 0, 255, 255]
table_border_light = [ 59, 66, 82] status_success = [ 80, 255, 80]
table_row_bg = [ 0, 0, 0] status_warning = [255, 152, 48]
table_row_bg_alt = [ 46, 52, 64] tab = [ 46, 52, 64]
nav_cursor = [136, 192, 208] tab_dimmed = [ 36, 41, 49]
modal_window_dim_bg = [ 10, 12, 16] tab_dimmed_selected = [ 59, 66, 82]
tab_hovered = [ 94, 129, 172]
tab_selected = [ 76, 86, 106]
table_border_light = [ 59, 66, 82]
table_border_strong = [ 76, 86, 106]
table_header_bg = [ 59, 66, 82]
table_row_bg = [ 0, 0, 0]
table_row_bg_alt = [ 46, 52, 64]
text = [216, 222, 233]
text_disabled = [116, 128, 150]
text_selected_bg = [ 94, 129, 172]
title_bg = [ 36, 41, 49]
title_bg_active = [ 59, 66, 82]
title_bg_collapsed = [ 30, 34, 42]
window_bg = [ 36, 41, 49]
+47 -33
View File
@@ -1,38 +1,52 @@
# Solarized Dark — Ethan Schoonover's canonical dark palette name = "solarized_dark"
syntax_palette = "dark" syntax_palette = "dark"
description = "Solarized Dark by Ethan Schoonover" description = "Solarized Dark by Ethan Schoonover"
[colors] [colors]
window_bg = [ 0, 43, 54] # base03 border = [ 88, 110, 117]
child_bg = [ 7, 54, 66] # base02 bubble_ai = [ 35, 65, 45]
popup_bg = [ 0, 43, 54] bubble_system = [ 25, 25, 25]
border = [ 88, 110, 117] # base01 bubble_user = [ 30, 45, 75]
frame_bg = [ 7, 54, 66] bubble_vendor = [ 65, 55, 30]
frame_bg_hovered = [ 88, 110, 117] button = [ 7, 54, 66]
frame_bg_active = [101, 123, 131] button_active = [ 38, 139, 210]
title_bg = [ 7, 54, 66] button_hovered = [ 38, 139, 210]
title_bg_active = [ 88, 110, 117] check_mark = [ 38, 139, 210]
menu_bar_bg = [ 0, 43, 54] child_bg = [ 7, 54, 66]
scrollbar_bg = [ 7, 54, 66] diff_added = [ 51, 230, 51]
scrollbar_grab = [ 88, 110, 117] diff_header = [ 77, 178, 255]
scrollbar_grab_hovered = [131, 148, 150] diff_removed = [230, 51, 51]
frame_bg = [ 7, 54, 66]
frame_bg_active = [101, 123, 131]
frame_bg_hovered = [ 88, 110, 117]
header = [ 7, 54, 66]
header_active = [ 38, 139, 210]
header_hovered = [ 38, 139, 210]
menu_bar_bg = [ 0, 43, 54]
popup_bg = [ 0, 43, 54]
scrollbar_bg = [ 7, 54, 66]
scrollbar_grab = [ 88, 110, 117]
scrollbar_grab_active = [253, 246, 227] scrollbar_grab_active = [253, 246, 227]
button = [ 7, 54, 66] scrollbar_grab_hovered = [131, 148, 150]
button_hovered = [ 38, 139, 210] # blue separator = [ 88, 110, 117]
button_active = [ 38, 139, 210] separator_active = [203, 75, 22]
header = [ 7, 54, 66] separator_hovered = [ 38, 139, 210]
header_hovered = [ 38, 139, 210] slice_auto = [ 0, 255, 0]
header_active = [ 38, 139, 210] slice_manual = [255, 165, 0]
separator = [ 88, 110, 117] slice_selection = [100, 100, 255]
separator_hovered = [ 38, 139, 210] slider_grab = [ 38, 139, 210]
separator_active = [203, 75, 22] # orange slider_grab_active = [ 38, 139, 210]
tab = [ 7, 54, 66] status_error = [255, 72, 64]
tab_hovered = [ 38, 139, 210] status_info = [ 0, 255, 255]
tab_selected = [ 88, 110, 117] status_success = [ 80, 255, 80]
text = [147, 161, 161] # base1 status_warning = [255, 152, 48]
text_disabled = [ 88, 110, 117] # base01 tab = [ 7, 54, 66]
text_selected_bg = [ 38, 139, 210] tab_hovered = [ 38, 139, 210]
check_mark = [ 38, 139, 210] tab_selected = [ 88, 110, 117]
slider_grab = [ 38, 139, 210] table_header_bg = [ 7, 54, 66]
slider_grab_active = [ 38, 139, 210] text = [147, 161, 161]
table_header_bg = [ 7, 54, 66] text_disabled = [ 88, 110, 117]
text_selected_bg = [ 38, 139, 210]
title_bg = [ 7, 54, 66]
title_bg_active = [ 88, 110, 117]
window_bg = [ 0, 43, 54]
+47 -33
View File
@@ -1,38 +1,52 @@
# Solarized Light — Ethan Schoonover's canonical light palette name = "solarized_light"
syntax_palette = "light" syntax_palette = "light"
description = "Solarized Light by Ethan Schoonover" description = "Solarized Light by Ethan Schoonover"
[colors] [colors]
window_bg = [238, 232, 213] # base2 border = [147, 161, 161]
child_bg = [253, 246, 227] # base3 bubble_ai = [220, 255, 220]
popup_bg = [253, 246, 227] bubble_system = [240, 240, 240]
border = [147, 161, 161] # base1 bubble_user = [220, 230, 255]
frame_bg = [253, 246, 227] bubble_vendor = [255, 240, 200]
frame_bg_hovered = [238, 232, 213] button = [253, 246, 227]
frame_bg_active = [238, 232, 213] button_active = [ 38, 139, 210]
title_bg = [238, 232, 213] button_hovered = [ 38, 139, 210]
title_bg_active = [147, 161, 161] check_mark = [ 38, 139, 210]
menu_bar_bg = [238, 232, 213] child_bg = [253, 246, 227]
scrollbar_bg = [238, 232, 213] diff_added = [ 40, 180, 40]
scrollbar_grab = [147, 161, 161] diff_header = [ 40, 100, 200]
scrollbar_grab_hovered = [131, 148, 150] diff_removed = [200, 40, 40]
frame_bg = [253, 246, 227]
frame_bg_active = [238, 232, 213]
frame_bg_hovered = [238, 232, 213]
header = [253, 246, 227]
header_active = [ 38, 139, 210]
header_hovered = [ 38, 139, 210]
menu_bar_bg = [238, 232, 213]
popup_bg = [253, 246, 227]
scrollbar_bg = [238, 232, 213]
scrollbar_grab = [147, 161, 161]
scrollbar_grab_active = [ 7, 54, 66] scrollbar_grab_active = [ 7, 54, 66]
button = [253, 246, 227] scrollbar_grab_hovered = [131, 148, 150]
button_hovered = [ 38, 139, 210] # blue separator = [147, 161, 161]
button_active = [ 38, 139, 210] separator_active = [203, 75, 22]
header = [253, 246, 227] separator_hovered = [181, 137, 0]
header_hovered = [ 38, 139, 210] slice_auto = [ 80, 255, 80]
header_active = [ 38, 139, 210] slice_manual = [255, 200, 0]
separator = [147, 161, 161] slice_selection = [180, 180, 255]
separator_hovered = [181, 137, 0] # yellow slider_grab = [ 38, 139, 210]
separator_active = [203, 75, 22] # orange slider_grab_active = [ 38, 139, 210]
tab = [238, 232, 213] status_error = [200, 40, 40]
tab_hovered = [ 38, 139, 210] status_info = [ 40, 100, 200]
tab_selected = [147, 161, 161] status_success = [ 40, 180, 40]
text = [ 7, 54, 66] # base02 status_warning = [200, 140, 0]
text_disabled = [147, 161, 161] # base1 tab = [238, 232, 213]
text_selected_bg = [ 38, 139, 210] tab_hovered = [ 38, 139, 210]
check_mark = [ 38, 139, 210] tab_selected = [147, 161, 161]
slider_grab = [ 38, 139, 210] table_header_bg = [238, 232, 213]
slider_grab_active = [ 38, 139, 210] text = [ 7, 54, 66]
table_header_bg = [238, 232, 213] text_disabled = [147, 161, 161]
text_selected_bg = [ 38, 139, 210]
title_bg = [238, 232, 213]
title_bg_active = [147, 161, 161]
window_bg = [238, 232, 213]