108 lines
3.2 KiB
Python
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()
|