NoteDiscovery/backend/themes.py

98 lines
2.9 KiB
Python
Raw Normal View History

2025-11-05 16:48:41 +00:00
"""
Theme management for NoteDiscovery
"""
from pathlib import Path
from typing import List, Dict
import re
2025-11-17 16:29:00 +00:00
def parse_theme_metadata(theme_path: Path) -> Dict[str, str]:
"""Parse theme metadata from CSS file comments"""
metadata = {
"type": "dark" # Default to dark for backward compatibility
}
try:
with open(theme_path, 'r', encoding='utf-8') as f:
# Read first few lines to find metadata
for i, line in enumerate(f):
if i > 10: # Only check first 10 lines
break
# Look for @theme-type metadata
if '@theme-type:' in line:
# Extract the value (light or dark)
match = re.search(r'@theme-type:\s*(light|dark)', line)
if match:
metadata["type"] = match.group(1)
break
except Exception as e:
print(f"Error parsing theme metadata from {theme_path}: {e}")
return metadata
2025-11-05 16:48:41 +00:00
def get_available_themes(themes_dir: str) -> List[Dict[str, str]]:
"""Get all available themes from the themes directory"""
themes_path = Path(themes_dir)
themes = []
# Theme icons/emojis mapping
theme_icons = {
"light": "🌞",
"dark": "🌙",
"dracula": "🧛",
"nord": "❄️",
"monokai": "🎞️",
"vue-high-contrast": "💚",
"cobalt2": "🌊",
"vs-blue": "🔷",
"gruvbox-dark": "🟫",
2026-04-16 15:01:28 +00:00
"matcha-light": "🍵",
"hacker": "🖥️",
"ubuntu": "🟠"
2025-11-05 16:48:41 +00:00
}
# Load all themes from themes folder
2025-11-05 16:48:41 +00:00
if themes_path.exists():
for theme_file in themes_path.glob("*.css"):
theme_name = theme_file.stem.replace("-", " ").replace("_", " ").title()
icon = theme_icons.get(theme_file.stem, "🎨")
2025-11-17 16:29:00 +00:00
# Parse theme metadata
metadata = parse_theme_metadata(theme_file)
2025-11-05 16:48:41 +00:00
themes.append({
"id": theme_file.stem,
"name": f"{icon} {theme_name}",
2025-11-17 16:29:00 +00:00
"type": metadata["type"], # Add theme type (light/dark)
2025-11-05 16:48:41 +00:00
"builtin": False
})
return themes
def get_theme_css(themes_dir: str, theme_id: str) -> str:
"""Get the CSS content for a specific theme"""
2026-03-13 10:58:45 +00:00
import re
# Security: Validate theme_id to prevent path traversal
# Only allow alphanumeric, hyphens, and underscores
if not re.match(r'^[a-zA-Z0-9_-]+$', theme_id):
return ""
themes_path = Path(themes_dir)
theme_path = themes_path / f"{theme_id}.css"
# Security: Ensure resolved path is still within themes directory
if not theme_path.resolve().is_relative_to(themes_path.resolve()):
return ""
2025-11-05 16:48:41 +00:00
if not theme_path.exists():
return ""
with open(theme_path, 'r', encoding='utf-8') as f:
return f.read()