ifixkart-admin/scripts/fix_theme_contrast.py

108 lines
3.2 KiB
Python

#!/usr/bin/env python3
"""Rewrite hardcoded dark-only / light-only colors to semantic theme tokens."""
from __future__ import annotations
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
TARGETS = [
ROOT / "app",
ROOT / "components" / "ui",
]
SKIP_NAMES = {"Sidebar.tsx"}
TOKEN_MAP = {
"text-gray-300": "text-muted-foreground",
"text-gray-400": "text-muted-foreground",
"text-gray-500": "text-muted-foreground",
"text-gray-600": "text-muted-foreground",
"text-gray-700": "text-foreground",
"text-gray-800": "text-foreground",
"text-gray-900": "text-foreground",
"placeholder-gray-500": "placeholder:text-muted-foreground",
"placeholder:text-gray-400": "placeholder:text-muted-foreground",
"placeholder:text-gray-500": "placeholder:text-muted-foreground",
"bg-white/5": "bg-muted/50",
"bg-white/[0.02]": "bg-muted/30",
"bg-white/[0.01]": "bg-muted/20",
"hover:bg-white/5": "hover:bg-muted",
"hover:bg-white/10": "hover:bg-muted",
"border-white/10": "border-border",
"border-white/5": "border-border",
"border-white/20": "border-border",
"divide-white/5": "divide-border",
"divide-white/10": "divide-border",
"bg-zinc-900": "bg-card",
"bg-zinc-800": "bg-muted",
"border-white/30": "border-border",
}
COLORED_BG = re.compile(
r"bg-(red-|primary|green-|emerald-|blue-500|blue-600|blue-700|amber-500|navy|\[#|black)"
)
CLASS_ATTR = re.compile(
r"""(?:className|class)\s*=\s*(?:\{`([^`]*)`\}|"([^"]*)"|\{"([^"]*)"\})""",
re.MULTILINE,
)
def rewrite_class_string(raw: str) -> str:
tokens = raw.split()
has_colored = any(COLORED_BG.search(t) for t in tokens)
out = []
for token in tokens:
if token in TOKEN_MAP:
out.append(TOKEN_MAP[token])
continue
if token == "text-white" and not has_colored:
out.append("text-foreground")
continue
if token == "hover:text-white" and not has_colored:
out.append("hover:text-foreground")
continue
out.append(token)
return " ".join(out)
def rewrite_file(path: Path) -> bool:
original = path.read_text(encoding="utf-8")
updated = original
def repl(match: re.Match) -> str:
full = match.group(0)
body = match.group(1) or match.group(2) or match.group(3) or ""
new_body = rewrite_class_string(body)
if new_body == body:
return full
return full.replace(body, new_body, 1)
updated = CLASS_ATTR.sub(repl, updated)
# Bare replacements outside className (template fragments)
for old, new in TOKEN_MAP.items():
updated = updated.replace(old, new)
if updated != original:
path.write_text(updated, encoding="utf-8")
return True
return False
def main() -> None:
changed = []
for folder in TARGETS:
for path in folder.rglob("*.tsx"):
if path.name in SKIP_NAMES:
continue
if rewrite_file(path):
changed.append(str(path.relative_to(ROOT)))
print(f"updated {len(changed)} files")
for name in changed:
print(f" {name}")
if __name__ == "__main__":
main()