diff options
| author | AlexanderCurl <alexc@alexc.hu> | 2026-04-18 17:46:06 +0100 |
|---|---|---|
| committer | AlexanderCurl <alexc@alexc.hu> | 2026-04-18 17:46:06 +0100 |
| commit | 70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69 (patch) | |
| tree | ab1123d4067c1b086dd6faa7ee4ea643236b565a /raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/translations/extract_settings_index.py | |
| parent | 5d94c0a7d44a2255b81815a52a7056a94a39842d (diff) | |
| download | RaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.tar.gz RaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.zip | |
Replaced file structures for themes in the correct format
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/translations/extract_settings_index.py')
| -rwxr-xr-x | raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/translations/extract_settings_index.py | 522 |
1 files changed, 522 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/translations/extract_settings_index.py b/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/translations/extract_settings_index.py new file mode 100755 index 0000000..8a7b935 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/translations/extract_settings_index.py @@ -0,0 +1,522 @@ +#!/usr/bin/env python3 +import json +import re +from collections import Counter +from pathlib import Path + +ABBREVIATIONS = { + "on-screen displays": ["osd"], + "on-screen display": ["osd"], + "do not disturb": ["dnd"], + "keyboard shortcuts": ["keybinds", "hotkeys", "bindings", "keys"], + "notifications": ["notif", "notifs", "alerts"], + "notification": ["notif", "alert"], + "wallpaper": ["background", "bg", "image", "picture", "desktop"], + "transparency": ["opacity", "alpha", "translucent", "transparent"], + "visibility": ["visible", "hide", "show", "hidden", "autohide", "auto-hide"], + "temperature": ["temp", "celsius", "fahrenheit"], + "configuration": ["config", "configure", "setup"], + "applications": ["apps", "programs"], + "application": ["app", "program"], + "animation": ["motion", "transition", "animate", "animations"], + "typography": ["font", "fonts", "text", "typeface"], + "workspaces": ["workspace", "desktops", "virtual"], + "workspace": ["desktop", "virtual"], + "bluetooth": ["bt"], + "network": ["wifi", "wi-fi", "ethernet", "internet", "connection", "wireless"], + "display": ["monitor", "screen", "output"], + "displays": ["monitors", "screens", "outputs"], + "brightness": ["bright", "dim", "backlight"], + "volume": ["audio", "sound", "speaker", "loudness"], + "battery": ["power", "charge", "charging"], + "clock": ["time", "watch"], + "calendar": ["date", "day", "month", "year"], + "launcher": ["app drawer", "app menu", "start menu", "applications"], + "dock": ["taskbar", "panel"], + "bar": ["panel", "taskbar", "topbar", "statusbar"], + "theme": ["appearance", "look", "style", "colors", "colour"], + "color": ["colour", "hue", "tint"], + "colors": ["colours", "palette"], + "dark": ["night", "dark mode"], + "light": ["day", "light mode"], + "lock screen": ["lockscreen", "login", "security"], + "power": ["shutdown", "reboot", "restart", "suspend", "hibernate", "sleep"], + "idle": ["afk", "inactive", "timeout", "screensaver"], + "gamma": ["color temperature", "night light", "blue light", "redshift"], + "media player": ["mpris", "music", "audio", "playback"], + "clipboard": ["copy", "paste", "cliphist", "history"], + "updater": ["updates", "upgrade", "packages"], + "plugins": ["extensions", "addons", "widgets"], + "spacing": ["gap", "gaps", "margin", "margins", "padding"], + "corner": ["corners", "rounded", "radius", "round"], + "matugen": ["dynamic", "wallpaper colors", "material"], + "running apps": ["taskbar", "windows", "active", "open"], + "weather": ["forecast", "temperature", "climate"], + "sounds": ["audio", "effects", "sfx"], + "printers": ["print", "cups", "printing"], + "widgets": ["components", "modules"], +} + +CATEGORY_KEYWORDS = { + "Personalization": ["customize", "custom", "personal", "appearance"], + "Time & Weather": ["clock", "forecast", "date"], + "Keyboard Shortcuts": ["keys", "bindings", "hotkey"], + "Dank Bar": ["panel", "topbar", "statusbar"], + "Workspaces": ["virtual desktops", "spaces"], + "Dock": ["taskbar", "launcher bar"], + "Network": ["connectivity", "online"], + "System": ["os", "linux"], + "Launcher": ["start", "menu", "drawer"], + "Theme & Colors": ["appearance", "look", "style", "scheme"], + "Lock Screen": ["security", "login", "password"], + "Plugins": ["extend", "addon"], + "About": ["info", "version", "credits", "help"], + "Typography & Motion": ["fonts", "animation", "text"], + "Sounds": ["audio", "sfx", "effects"], + "Media Player": ["music", "spotify", "mpris"], + "Notifications": ["alerts", "messages", "toast"], + "On-screen Displays": ["osd", "indicator", "popup"], + "Running Apps": ["windows", "tasks", "active"], + "System Updater": ["packages", "upgrade"], + "Power & Sleep": ["shutdown", "suspend", "energy"], + "Displays": ["monitor", "screen", "resolution"], + "Desktop Widgets": ["conky", "desktop clock"], + "Audio": ["sound", "volume", "speaker", "microphone", "headphones", "pipewire"], + "Window Rules": [ + "window", + "rules", + "matching", + "floating", + "fullscreen", + "opacity", + ], + "Locale": ["locale", "language", "country"], +} + +TAB_INDEX_MAP = { + "WallpaperTab.qml": 0, + "TimeWeatherTab.qml": 1, + "KeybindsTab.qml": 2, + "DankBarTab.qml": 3, + "WorkspacesTab.qml": 4, + "DockTab.qml": 5, + "NetworkTab.qml": 7, + "PrinterTab.qml": 8, + "LauncherTab.qml": 9, + "ThemeColorsTab.qml": 10, + "LockScreenTab.qml": 11, + "PluginsTab.qml": 12, + "AboutTab.qml": 13, + "TypographyMotionTab.qml": 14, + "SoundsTab.qml": 15, + "MediaPlayerTab.qml": 16, + "NotificationsTab.qml": 17, + "OSDTab.qml": 18, + "RunningAppsTab.qml": 19, + "SystemUpdaterTab.qml": 20, + "PowerSleepTab.qml": 21, + "WidgetsTab.qml": 22, + "ClipboardTab.qml": 23, + "DisplayConfigTab.qml": 24, + "GammaControlTab.qml": 25, + "DisplayWidgetsTab.qml": 26, + "DesktopWidgetsTab.qml": 27, + "WindowRulesTab.qml": 28, + "AudioTab.qml": 29, + "LocaleTab.qml": 30, +} + +TAB_CATEGORY_MAP = { + 0: "Personalization", + 1: "Time & Weather", + 2: "Keyboard Shortcuts", + 3: "Dank Bar", + 4: "Workspaces", + 5: "Dock", + 7: "Network", + 8: "System", + 9: "Launcher", + 10: "Theme & Colors", + 11: "Lock Screen", + 12: "Plugins", + 13: "About", + 14: "Typography & Motion", + 15: "Sounds", + 16: "Media Player", + 17: "Notifications", + 18: "On-screen Displays", + 19: "Running Apps", + 20: "System Updater", + 21: "Power & Sleep", + 22: "Dank Bar", + 23: "System", + 24: "Displays", + 25: "Displays", + 26: "Displays", + 27: "Desktop Widgets", + 28: "Window Rules", + 29: "Audio", + 30: "Locale", +} + +SEARCHABLE_COMPONENTS = [ + "SettingsCard", + "SettingsToggleRow", + "SettingsSliderCard", + "SettingsDropdownRow", + "SettingsButtonGroupRow", + "SettingsSliderRow", + "SettingsToggleCard", +] + +STOPWORDS = { + "the", + "and", + "for", + "with", + "from", + "this", + "that", + "are", + "was", + "will", + "can", + "has", + "have", + "been", + "when", + "your", + "use", + "used", + "using", + "instead", + "like", + "such", + "also", + "only", + "which", + "each", + "other", + "some", + "into", + "than", + "then", + "them", + "these", + "those", +} + + +def enrich_keywords(label, description, category, existing_tags): + keywords = set(existing_tags) + + label_lower = label.lower() + label_words = re.split(r"[\s\-_&/]+", label_lower) + keywords.update(w for w in label_words if len(w) > 2) + + for term, aliases in ABBREVIATIONS.items(): + if term in label_lower: + keywords.update(aliases) + + if description: + desc_lower = description.lower() + desc_words = re.split(r"[\s\-_&/,.]+", desc_lower) + keywords.update(w for w in desc_words if len(w) > 3 and w.isalpha()) + for term, aliases in ABBREVIATIONS.items(): + if term in desc_lower: + keywords.update(aliases) + + if category in CATEGORY_KEYWORDS: + keywords.update(CATEGORY_KEYWORDS[category]) + + cat_lower = category.lower() + cat_words = re.split(r"[\s\-_&/]+", cat_lower) + keywords.update(w for w in cat_words if len(w) > 2) + + keywords = {k for k in keywords if k not in STOPWORDS and len(k) > 1} + return sorted(keywords) + + +def extract_i18n_string(value): + match = re.search(r'I18n\.tr\(["\']([^"\']+)["\']', value) + if match: + return match.group(1) + match = re.search(r'^["\']([^"\']+)["\']$', value.strip()) + if match: + return match.group(1) + return None + + +def extract_tags(value): + match = re.search(r"\[([^\]]+)\]", value) + if not match: + return [] + content = match.group(1) + tags = re.findall(r'["\']([^"\']+)["\']', content) + return tags + + +def parse_component_block(content, start_pos, component_name): + brace_count = 0 + started = False + block_start = start_pos + + for i in range(start_pos, len(content)): + if content[i] == "{": + if not started: + block_start = i + started = True + brace_count += 1 + elif content[i] == "}": + brace_count -= 1 + if started and brace_count == 0: + return content[block_start : i + 1] + return "" + + +def extract_property(block, prop_name): + pattern = rf"{prop_name}\s*:\s*([^\n]+)" + match = re.search(pattern, block) + if match: + return match.group(1).strip() + return None + + +def find_settings_components(content, filename): + results = [] + tab_index = TAB_INDEX_MAP.get(filename, -1) + + if tab_index == -1: + return results + + for component in SEARCHABLE_COMPONENTS: + pattern = rf"\b{component}\s*\{{" + for match in re.finditer(pattern, content): + block = parse_component_block(content, match.start(), component) + if not block: + continue + + setting_key = extract_property(block, "settingKey") + if setting_key: + setting_key = setting_key.strip("\"'") + + if not setting_key: + continue + + title_raw = extract_property(block, "title") + text_raw = extract_property(block, "text") + label = None + if title_raw: + label = extract_i18n_string(title_raw) + if not label and text_raw: + label = extract_i18n_string(text_raw) + + if not label: + continue + + icon_raw = extract_property(block, "iconName") + icon = None + if icon_raw: + icon = icon_raw.strip("\"'") + if icon.startswith("{") or "?" in icon: + icon = None + + tags_raw = extract_property(block, "tags") + tags = [] + if tags_raw: + tags = extract_tags(tags_raw) + + desc_raw = extract_property(block, "description") + description = None + if desc_raw: + description = extract_i18n_string(desc_raw) + + visible_raw = extract_property(block, "visible") + condition_key = None + if visible_raw: + if "CompositorService.isNiri" in visible_raw: + condition_key = "isNiri" + elif "CompositorService.isHyprland" in visible_raw: + condition_key = "isHyprland" + elif "KeybindsService.available" in visible_raw: + condition_key = "keybindsAvailable" + elif "AudioService.soundsAvailable" in visible_raw: + condition_key = "soundsAvailable" + elif "CupsService.cupsAvailable" in visible_raw: + condition_key = "cupsAvailable" + elif "NetworkService.usingLegacy" in visible_raw: + condition_key = "networkNotLegacy" + elif "DMSService.isConnected" in visible_raw: + condition_key = "dmsConnected" + elif "Theme.matugenAvailable" in visible_raw: + condition_key = "matugenAvailable" + elif "CompositorService.isDwl" in visible_raw: + condition_key = "isDwl" + + category = TAB_CATEGORY_MAP.get(tab_index, "Settings") + enriched_keywords = enrich_keywords(label, description, category, tags) + + entry = { + "section": setting_key, + "label": label, + "tabIndex": tab_index, + "category": category, + "keywords": enriched_keywords, + } + + if icon: + entry["icon"] = icon + if description: + entry["description"] = description + if condition_key: + entry["conditionKey"] = condition_key + + results.append(entry) + + return results + + +def parse_tabs_from_sidebar(sidebar_file): + with open(sidebar_file, "r", encoding="utf-8") as f: + content = f.read() + + pattern = r'"text"\s*:\s*I18n\.tr\("([^"]+)"(?:,\s*"[^"]+")?\).*?"icon"\s*:\s*"([^"]+)".*?"tabIndex"\s*:\s*(\d+)' + tabs = [] + + for match in re.finditer(pattern, content, re.DOTALL): + label, icon, tab_idx = match.group(1), match.group(2), int(match.group(3)) + + before_text = content[: match.start()] + parent_match = re.search( + r'"text"\s*:\s*I18n\.tr\("([^"]+)"\)[^{]*"children"[^[]*\[[^{]*$', + before_text, + ) + parent = parent_match.group(1) if parent_match else None + + cond = None + after_pos = match.end() + snippet = content[match.start() : min(after_pos + 200, len(content))] + for qml_cond, key in [ + ("shortcutsOnly", "keybindsAvailable"), + ("soundsOnly", "soundsAvailable"), + ("cupsOnly", "cupsAvailable"), + ("dmsOnly", "dmsConnected"), + ("hyprlandNiriOnly", "isHyprlandOrNiri"), + ("clipboardOnly", "dmsConnected"), + ("niriOnly", "isNiri"), + ]: + if f'"{qml_cond}": true' in snippet: + cond = key + break + + tabs.append( + { + "tabIndex": tab_idx, + "label": label, + "icon": icon, + "parent": parent, + "conditionKey": cond, + } + ) + + return tabs + + +def generate_tab_entries(sidebar_file): + tabs = parse_tabs_from_sidebar(sidebar_file) + + label_counts = Counter([t["label"] for t in tabs]) + + entries = [] + for tab in tabs: + label = ( + f"{tab['parent']}: {tab['label']}" + if label_counts[tab["label"]] > 1 and tab["parent"] + else tab["label"] + ) + category = TAB_CATEGORY_MAP.get(tab["tabIndex"], "Settings") + + keywords = enrich_keywords(tab["label"], None, category, []) + + if tab["parent"]: + parent_keywords = [ + w for w in re.split(r"[\s\-_&/]+", tab["parent"].lower()) if len(w) > 2 + ] + keywords = sorted( + set( + keywords + + parent_keywords + + [k for p in parent_keywords for k in ABBREVIATIONS.get(p, [])] + ) + ) + + entry = { + "section": f"_tab_{tab['tabIndex']}", + "label": label, + "tabIndex": tab["tabIndex"], + "category": category, + "keywords": keywords, + "icon": tab["icon"], + } + if tab["conditionKey"]: + entry["conditionKey"] = tab["conditionKey"] + entries.append(entry) + + return entries + + +def extract_settings_index(root_dir): + settings_dir = Path(root_dir) / "Modules" / "Settings" + all_entries = [] + seen_keys = set() + + for qml_file in settings_dir.glob("*.qml"): + if not qml_file.name.endswith("Tab.qml"): + continue + + with open(qml_file, "r", encoding="utf-8") as f: + content = f.read() + + entries = find_settings_components(content, qml_file.name) + for entry in entries: + key = entry["section"] + if key not in seen_keys: + seen_keys.add(key) + all_entries.append(entry) + + return all_entries + + +def main(): + script_dir = Path(__file__).parent + root_dir = script_dir.parent + sidebar_file = root_dir / "Modals" / "Settings" / "SettingsSidebar.qml" + + print("Extracting settings search index...") + settings_entries = extract_settings_index(root_dir) + tab_entries = generate_tab_entries(sidebar_file) + + all_entries = tab_entries + settings_entries + + all_entries.sort(key=lambda x: (x["tabIndex"], x["label"])) + + output_path = script_dir / "settings_search_index.json" + with open(output_path, "w", encoding="utf-8") as f: + json.dump(all_entries, f, indent=2, ensure_ascii=False) + + print(f"Found {len(settings_entries)} searchable settings") + print(f"Found {len(tab_entries)} tab entries") + print(f"Total: {len(all_entries)} entries") + print(f"Output: {output_path}") + + conditions = set() + for entry in all_entries: + if "conditionKey" in entry: + conditions.add(entry["conditionKey"]) + + if conditions: + print(f"Condition keys found: {', '.join(sorted(conditions))}") + + +if __name__ == "__main__": + main() |