summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/dms/translations/extract_settings_index.py
diff options
context:
space:
mode:
Diffstat (limited to 'raveos-hyprland-theme/theme-data/dms/translations/extract_settings_index.py')
-rwxr-xr-xraveos-hyprland-theme/theme-data/dms/translations/extract_settings_index.py522
1 files changed, 522 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/dms/translations/extract_settings_index.py b/raveos-hyprland-theme/theme-data/dms/translations/extract_settings_index.py
new file mode 100755
index 0000000..8a7b935
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/dms/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()