pragma Singleton // Search across every setting, not just page names. // // The sidebar's field used to filter the twelve page labels, so "gaps", // "wallpaper", and "repeat delay" all found nothing — which is precisely the // thing that makes a settings app feel smaller than it is. This indexes the // schema itself, so any setting is reachable by typing what it does, and a new // schema entry becomes searchable with no change here. // // Shortcuts are indexed too: "screenshot" should find the key that takes one. import Quickshell import QtQuick import qs.config Singleton { id: root // SettingsSearch is part of the always-constructed sidebar. Touching the // desktop-style service here gives application preferences their startup // replay even when Appearance is not the page that opens first. Component.onCompleted: DesktopStyle.ensureStarted() // Which page shows the settings in a given schema group. A group with no // entry here still appears in results and routes to Home rather than being // dropped, so adding a group can never make a setting unreachable. readonly property var groupPages: ({ "appearance": "appearance", "clock": "appearance", "vitals": "appearance", "typography": "appearance", "themes": "appearance", "titlebar": "appearance", "windows": "appearance", "effects": "appearance", "wallpaper": "appearance", "lockAppearance": "appearance", "dock": "desktop", "focus": "desktop", "display": "displays", "nightLight": "displays", "idle": "power", "accessibility": "accessibility", "input": "shortcuts", "pointer": "mouse", "touchpad": "mouse", "multitasking": "desktop", "edges": "desktop", "master": "desktop", "notices": "desktop", "weather": "home", "notifications": "notifications", "capture": "screen-intelligence", "gaming": "gaming" }) // Settings that are real but have no schema entry, because the system owns // them rather than Panama. Without these, searching "timezone" would fail // on a settings app that plainly has one. readonly property var extraEntries: [ { label: "Timezone", detail: "Set the system timezone", page: "datetime" }, { label: "Network time", detail: "Synchronize the clock with a time server", page: "datetime" }, { label: "Wi-Fi", detail: "Managed by GNOME Settings", page: "connectivity" }, { label: "Bluetooth", detail: "Managed by GNOME Settings", page: "connectivity" }, { label: "Printers", detail: "Managed by GNOME Settings", page: "connectivity" }, { label: "Default applications", detail: "Browser, mail, files", page: "applications" }, { label: "User account", detail: "Your name, picture, and password", page: "users" }, { label: "Profile picture", detail: "The avatar shown on the lock screen and in the Control Center", page: "users" }, { label: "Change password", detail: "Set a new password for signing in", page: "users" }, { label: "Add a user", detail: "Create another account on this machine", page: "users" }, { label: "Automatic login", detail: "Sign in without typing a password", page: "users" }, { label: "Administrator", detail: "Which accounts can manage this machine", page: "users" }, { label: "Printers", detail: "Add a printer and see what is queued", page: "printers" }, { label: "Print queue", detail: "What is waiting to print, and cancelling it", page: "printers" }, { label: "Add a printer", detail: "Find a printer on the network or enter its address", page: "printers" }, { label: "Default printer", detail: "Where applications print unless told otherwise", page: "printers" }, { label: "Firewall", detail: "What another machine can reach on this one", page: "firewall" }, { label: "Open ports", detail: "Which ports the firewall permits", page: "firewall" }, { label: "Firewall zones", detail: "Which rules apply to each network connection", page: "firewall" }, { label: "Exposed services", detail: "What is listening and reachable from the network", page: "firewall" }, { label: "Containers", detail: "Rootless podman stacks you run for development", page: "containers" }, { label: "SSH keys", detail: "The keys this machine signs in with", page: "ssh-keys" }, { label: "SSH agent", detail: "Which keys are held for this session", page: "ssh-keys" }, { label: "Known hosts", detail: "Machines this one has connected to before", page: "ssh-keys" }, { label: "Public key", detail: "Copy the half you paste into a server", page: "ssh-keys" }, { label: "Podman", detail: "Running containers, images and volumes", page: "containers" }, { label: "Container logs", detail: "Follow what a container is printing", page: "containers" }, { label: "Reclaim container space", detail: "Remove images and volumes nothing uses", page: "containers" }, { label: "Published ports", detail: "Which containers are reachable from the network", page: "containers" }, { label: "Remote login", detail: "Sign in to this machine over SSH", page: "sharing" }, { label: "Remote desktop", detail: "See and control this desktop from elsewhere", page: "sharing" }, { label: "Network name", detail: "The name other machines see", page: "sharing" }, { label: "File sharing", detail: "Share folders on the network", page: "sharing" }, { label: "Game Mode", detail: "What happens while a game is running", page: "gaming" }, { label: "Performance overlay", detail: "Frame rate and sensors on top of the game", page: "gaming" }, { label: "Proton", detail: "Compatibility tools available to Steam", page: "gaming" }, { label: "Graphics card", detail: "Temperature, power draw, and video memory", page: "gaming" }, { label: "Software update", detail: "Packages, applications, and firmware", page: "updates" }, { label: "Updates", detail: "What is waiting to be installed", page: "updates" }, { label: "Firmware", detail: "Updates for the hardware itself", page: "updates" }, { label: "Security updates", detail: "Packages that carry a security advisory", page: "updates" }, { label: "Automatic updates", detail: "Keep applications current in the background", page: "updates" }, { label: "Snapshots", detail: "Points in time you can go back to", page: "snapshots" }, { label: "Restore a file", detail: "Take a file or folder back out of a snapshot", page: "snapshots" }, { label: "Backups", detail: "Automatic snapshots of the system and your home folder", page: "snapshots" }, { label: "File history", detail: "Earlier versions of your files", page: "snapshots" }, { label: "Undo a change", detail: "Put back a file as it was at an earlier point", page: "snapshots" }, { label: "Free space", detail: "How full each drive and filesystem is", page: "storage" }, { label: "Disk usage", detail: "What is using the space on this machine", page: "storage" }, { label: "Drive health", detail: "Temperature, hours powered on, and reported warnings", page: "storage" }, { label: "Removable drives", detail: "Unmount a USB drive or memory card safely", page: "storage" }, { label: "Encryption", detail: "Whether the filesystem is encrypted", page: "storage" }, { label: "Output volume", detail: "Choose the output device and its level", page: "sound" }, { label: "Input volume", detail: "Choose the microphone and its level", page: "sound" }, { label: "Per-application volume", detail: "Set the level of each application separately", page: "sound" }, { label: "Event sounds", detail: "Play alerts and interface event sounds", page: "sound" }, { label: "Saved passwords", detail: "The login keyring and what is stored in it", page: "privacy" }, { label: "Camera and microphone", detail: "Which applications may use them", page: "privacy" }, { label: "Screen sharing", detail: "Which applications may capture the screen", page: "privacy" }, { label: "File history and trash", detail: "What is remembered and when it is cleared", page: "privacy" }, { label: "Device security", detail: "Secure boot and firmware protections", page: "privacy" }, { label: "Language", detail: "The system language, applied to programs started afterwards", page: "region" }, { label: "Regional formats", detail: "How dates, times, and numbers are written", page: "region" }, { label: "Online accounts", detail: "Sign in to mail, calendar, and contacts", page: "accounts" }, { label: "Home Assistant", detail: "Connect the desktop to a Home Assistant server", page: "home-phone" }, { label: "Phone", detail: "Pair a phone for messages and notifications", page: "home-phone" }, { label: "System information", detail: "Kernel, distribution, and hardware", page: "about" }, { label: "Desktop version", detail: "Which Hyprland and Quickshell this session runs", page: "about" }, { label: "Restore defaults", detail: "Return every Panama setting to its shipped value", page: "desktop" }, { label: "Keyboard shortcuts", detail: "Every shortcut the compositor has bound", page: "shortcuts" }, { label: "System Health", detail: "Check Panama services, integrations, tools, and recovery actions", page: "services" }, { label: "Copy health report", detail: "Copy a redacted Panama doctor report", page: "services" }, { label: "Lock screen background", detail: "Choose a blurred desktop, wallpaper, or solid color", page: "appearance" }, { label: "Password field", detail: "Choose whether the empty lock-screen field stays visible", page: "appearance" }, { label: "Per-display wallpaper", detail: "Assign a different image to each connected display", page: "appearance" }, { label: "Arrange displays", detail: "Drag connected displays into their physical positions", page: "displays" }, { label: "Monitor position", detail: "Set where each display sits in the desktop", page: "displays" }, { label: "Primary display", detail: "Choose the display that anchors the desktop", page: "displays" } ] function pageFor(group: string): string { return root.groupPages[group] ?? "home"; } // [{ label, detail, page, kind }] for a query. Empty query yields nothing: // the sidebar shows its normal navigation in that case. function search(query: string): var { const needle = String(query).trim().toLowerCase(); if (needle === "") return []; const results = []; const seen = {}; function add(label, detail, page, kind) { const dedupe = `${kind}:${label}:${page}`; if (seen[dedupe]) return; seen[dedupe] = true; results.push({ label: label, detail: detail, page: page, kind: kind }); } for (const entry of PreferenceSchema.entries) { if (entry.internal) continue; const optionLabels = (entry.options ?? []).map(option => option.label).join(" "); const haystack = `${entry.label} ${entry.detail ?? ""} ${entry.group} ${optionLabels}`.toLowerCase(); if (haystack.indexOf(needle) >= 0) add(entry.label, entry.detail ?? "", root.pageFor(entry.group), "setting"); } for (const entry of root.extraEntries) { if (`${entry.label} ${entry.detail}`.toLowerCase().indexOf(needle) >= 0) add(entry.label, entry.detail, entry.page, "setting"); } for (const bind of Keybinds.binds) { if (bind.description.toLowerCase().indexOf(needle) >= 0) add(bind.description, bind.chord, "shortcuts", "shortcut"); } // Exact prefix matches first: typing "blur" should put "Blur" above // "Blur radius", and both above a setting that merely mentions blur in // its explanation. An exact enum option also leads: "slideshow" is a // mode choice, so Wallpaper mode belongs above the interval row that // merely explains it. return results.sort((a, b) => { const aSpec = PreferenceSchema.entries.find(entry => entry.label === a.label); const bSpec = PreferenceSchema.entries.find(entry => entry.label === b.label); const ao = (aSpec?.options ?? []).some(option => option.label.toLowerCase() === needle); const bo = (bSpec?.options ?? []).some(option => option.label.toLowerCase() === needle); if (ao !== bo) return ao ? -1 : 1; const al = a.label.toLowerCase(); const bl = b.label.toLowerCase(); const ap = al === needle ? 0 : (al.indexOf(needle) === 0 ? 1 : 2); const bp = bl === needle ? 0 : (bl.indexOf(needle) === 0 ? 1 : 2); return ap !== bp ? ap - bp : al.localeCompare(bl); }).slice(0, 40); } }