| import os |
|
|
| SUPPORTED_COLORS = {"blue", "purple", "red", "orange", "green", "pink"} |
|
|
| COLOR_RGB_MAP = { |
| "blue": (47, 108, 229), |
| "purple": (138, 47, 229), |
| "red": (229, 47, 47), |
| "orange": (229, 138, 47), |
| "green": (47, 229, 47), |
| "pink": (229, 47, 168), |
| } |
|
|
| DEFAULT_COLOR = "blue" |
|
|
|
|
| def get_assets_dir(): |
| """Returns the absolute path to the assets directory""" |
| current_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| return os.path.join(current_dir, "assets") |
|
|
| def get_valid_color(slide_colour: str) -> str: |
| """ |
| Returns the validated color name. Falls back to DEFAULT_COLOR if unsupported. |
| """ |
| color = slide_colour.lower() |
| return color if color in SUPPORTED_COLORS else DEFAULT_COLOR |
|
|
| def get_color_rgb(slide_colour: str) -> tuple: |
| """ |
| Returns the RGB color tuple based on the given slide color. |
| """ |
| color = get_valid_color(slide_colour) |
| return COLOR_RGB_MAP.get(color, COLOR_RGB_MAP[DEFAULT_COLOR]) |
|
|
| def get_background_image_path(slide_colour: str, first_slide: bool = False) -> str: |
| """ |
| Returns the path to the background image based on the slide color and type. |
| """ |
| color = get_valid_color(slide_colour) |
| suffix = "bg1" if first_slide else "bg2" |
| assets_dir = get_assets_dir() |
| image_path = os.path.join(assets_dir, f"{color}_{suffix}.png") |
| |
| if not os.path.exists(image_path): |
| print(f"Warning: Background image not found at {image_path}") |
| |
| image_path = os.path.join(assets_dir, f"{suffix}.png") |
| if not os.path.exists(image_path): |
| print(f"Warning: Fallback background image not found at {image_path}") |
| |
| return image_path |
|
|
| def get_code_rgb(slide_colour: str) -> tuple: |
| """ |
| Returns the RGB color used for code syntax highlighting. |
| """ |
| return get_color_rgb(slide_colour) |
|
|
| def get_title_rgb(slide_colour: str) -> tuple: |
| """ |
| Returns the RGB color used for the topic title text. |
| """ |
| return get_color_rgb(slide_colour) |
| |
| def sanitize_filename(name: str, replacement: str = "_", max_length: int = 200) -> str: |
| """ |
| Sanitize a string to be safe for use as a filesystem name. |
| Focuses on replacing problematic special characters with a safe replacement. |
| |
| Behavior: |
| - Normalize unicode (NFKC) |
| - Remove control characters |
| - Replace path separators and shell-sensitive punctuation with `replacement` |
| - Replace whitespace sequences with `replacement` |
| - Replace hyphens and dots with `replacement` (per config) |
| - Collapse repeated replacement characters |
| - Trim leading/trailing replacement characters |
| - Truncate to `max_length` characters |
| - Preserve non-Latin characters (Unicode) by default |
| """ |
| if not name: |
| return "" |
| import re |
| import unicodedata |
|
|
| |
| s = unicodedata.normalize("NFKC", str(name)) |
|
|
| |
| s = "".join(ch for ch in s if ord(ch) >= 32) |
|
|
| |
| |
| |
| |
| s = re.sub(r"[\\/\\:\\*\\?\"<>\\|,;@#%&\\$'`~()\[\]{}\\\\\+]", replacement, s) |
| s = re.sub(r"[-\\.]", replacement, s) |
|
|
| |
| s = re.sub(r"\s+", replacement, s) |
|
|
| |
| if replacement: |
| esc = re.escape(replacement) |
| s = re.sub(rf"{esc}+", replacement, s) |
|
|
| |
| s = s.strip(replacement + " ") |
|
|
| |
| if max_length and len(s) > max_length: |
| s = s[:max_length].rstrip(replacement) |
|
|
| |
| if not s: |
| return "topic" |
| return s |
|
|