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": "bar", "vitals": "bar", "bar": "bar", "controlCenter": "control-center", "datetime": "datetime", "battery": "power", "idleBattery": "power", "power": "power", "typography": "appearance", "themes": "appearance", "titlebar": "appearance", "windows": "appearance", "effects": "appearance", "wallpaper": "appearance", "lockAppearance": "appearance", "dock": "dock", // Focus modes moved off the Notifications page onto their own tab, so // the group that owns them routes there now. "focus": "focus", "display": "displays", "nightLight": "displays", "idle": "power", "accessibility": "accessibility", "input": "shortcuts", "pointer": "mouse", "touchpad": "mouse", "multitasking": "tiling", "workspaces": "workspaces", "edges": "tiling", "master": "tiling", "notices": "tiling", "weather": "home", "notifications": "notifications", "capture": "screen-intelligence", "gaming": "gaming", "search": "applications", "sound": "sound" }) // 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. // // An entry may name a `section` as well as a page. A page with tabs opens // on the tab holding the thing that was searched for, rather than on // whichever tab it opens on by default -- see SettingsSidebar. Sections are // only worth naming for a page that consumes one (ShellState.takeSettings- // Section); everything else leaves it off and routes as before. readonly property var extraEntries: [ { label: "Manual", detail: "How this desktop works, in chapters", page: "manual" }, { label: "Getting started", detail: "Coming from GNOME, macOS or Windows", page: "manual" }, { label: "Troubleshooting", detail: "When something breaks", page: "manual" }, { label: "Help", detail: "The manual", page: "manual" }, { label: "Timezone", detail: "Set the system timezone", page: "datetime" }, { label: "Network time", detail: "Synchronize the clock with a time server", page: "datetime" }, // Connections. These said "Managed by GNOME Settings" for as long as // that was true; it stopped being true when the page grew VPN, hotspot, // proxy and per-connection details, and a search result that hands the // user to another application for something this page now does is worse // than no result at all. The Printers entry here was also a second copy // of the one that routes to the Printers page. { label: "Wi-Fi", detail: "Join a network, see the one you are on, and share it", page: "connectivity" }, { label: "Bluetooth", detail: "Pair and connect devices", page: "connectivity" }, { label: "VPN", detail: "Turn a tunnel on, and see which one is up", page: "connectivity" }, { label: "Import a VPN", detail: "Add a WireGuard or OpenVPN profile from a file", page: "connectivity" }, { label: "Hotspot", detail: "Share this machine's connection over Wi-Fi", page: "connectivity" }, { label: "Airplane mode", detail: "Turn every radio off at once", page: "connectivity" }, { label: "Network proxy", detail: "Send traffic through a proxy, or a PAC file", page: "connectivity" }, { label: "IP address", detail: "The address, gateway, DNS servers, and hardware address of a connection", page: "connectivity" }, { label: "Forget a Wi-Fi network", detail: "Remove a saved network so it stops connecting on its own", page: "connectivity" }, { label: "Enterprise Wi-Fi", detail: "Join a network that asks for an identity and a password", page: "connectivity" }, // Tier 2 network truths. Each of these was a reason to open a terminal // or GNOME's panel, and none of them is the label of a preference: a // static address is a profile property, a saved network is a file // NetworkManager keeps, and metered is a flag on both. { label: "Saved networks", detail: "Every network this machine remembers, including the ones nowhere near you, and forgetting one", page: "connectivity" }, { label: "Join a hidden network", detail: "A network that does not broadcast its name — type the name and its security", page: "connectivity" }, { label: "Hidden network", detail: "Join a Wi-Fi network that does not announce itself", page: "connectivity" }, { label: "Metered connection", detail: "Mark a connection as costing money by the byte, so updates and large downloads wait", page: "connectivity" }, { label: "Static IP address", detail: "Set a manual IPv4 or IPv6 address, gateway and DNS for one connection", page: "connectivity" }, { label: "Manual IP address", detail: "Turn off DHCP for a connection and enter the address yourself", page: "connectivity" }, { label: "DNS servers", detail: "The nameservers a connection uses, and replacing the ones it is handed", page: "connectivity" }, { label: "Show the Wi-Fi password", detail: "A QR code a phone can scan, so nobody has to read the password out", page: "connectivity" }, // The two words people type for the same socket. Neither appears in the // page's own labels, which say "Wired" once and "Ethernet" once. { label: "Wired network", detail: "The Ethernet connection, its link speed, and its addresses", page: "connectivity" }, { label: "Ethernet", detail: "The wired connection: turn it off, or open its addresses", page: "connectivity" }, { label: "Default applications", detail: "Browser, mail, files", page: "applications" }, // The Applications tab manages applications now, rather than only // pointing file types at them, so the things people come looking for — // what is installed, removing one, what it is allowed to reach — are // findable by their own names. { label: "Installed applications", detail: "Everything installed here, where it came from, and how much room it takes", page: "applications" }, { label: "Uninstall an application", detail: "Remove a Flatpak application, or see the command for a system package", page: "applications" }, { label: "Application permissions", detail: "What a Flatpak application may reach: files, camera, microphone, network", page: "applications" }, { label: "Install applications", detail: "Browse the catalog Panama curates, by category", page: "applications" }, { label: "Autostart", detail: "Which applications start with your session", page: "applications" }, { label: "File associations", detail: "Which application opens each kind of file", 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" }, // Users owns fingerprint enrollment now rather than handing it to // GNOME's panel, and it owns the other-account verbs it used to bury // inside an expanded row. Each of those is something people come // looking for by name, so each gets a name to be found by. { label: "Fingerprint", detail: "Unlock and authorize with the reader on this machine", page: "users" }, { label: "Enroll a fingerprint", detail: "Record a finger, one touch at a time", page: "users" }, { label: "Delete a user", detail: "Remove an account, keeping or destroying its files", page: "users" }, { label: "Account type", detail: "Whether an account may administer this machine", page: "users" }, { label: "Reset a password", detail: "They set a new one at their next sign-in", 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" }, // The page makes keys now rather than only listing them, so the verbs // people arrive with — make one, unload one, fix the mode ssh refuses — // each have a name to be found by. { label: "Generate an SSH key", detail: "Create an ed25519 key with a passphrase, without opening a terminal", page: "ssh-keys" }, { label: "Fix key permissions", detail: "Make a private key readable only by you, which is what ssh insists on", page: "ssh-keys" }, { label: "Remove from agent", detail: "Stop a key being offered for the rest of this session", 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" }, // Power & Lock. The sliders come from the schema, but the subjects // people arrive with are the physical ones — a button, a lid, sleep — // and none of those is the label of a preference. Hibernate is the // sharpest case: it has no control at all, only an honest row saying // why this machine will not do it, and a search that found nothing // would read as the desktop having no opinion. { label: "Hibernate", detail: "Whether this machine can hibernate, and why zram swap means it does not", page: "power" }, { label: "Power profile", detail: "Power saver, balanced, or performance, applied system-wide", page: "power" }, { label: "Power button", detail: "What pressing it does: the power menu, suspend, power off, or nothing", page: "power" }, { label: "Lid", detail: "What closing the lid does, and why an external display changes it", page: "power" }, { label: "Suspend", detail: "When the machine sleeps on its own, on wall power and on battery", page: "power" }, { label: "Sleep", detail: "The idle timeline: screen off, then lock, then suspend", page: "power" }, { 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: "Snapshot retention", detail: "How many hourly, daily, and weekly snapshots to keep", 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: "Clean up storage", detail: "Caches, trash, and unused runtimes, each itemized and sized before you remove it", page: "storage" }, { label: "Application caches", detail: "What applications have left in your cache folder, and clearing it", 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: "Balance", detail: "Shift the output between the left and right speaker", page: "sound" }, { label: "Speaker test", detail: "Play a tone through each channel to find which speaker is which", page: "sound" }, { label: "Microphone test", detail: "Record a few seconds and play it straight back", page: "sound" }, { label: "Alert sound", detail: "The sound theme alerts and Panama's notification chime use", page: "sound" }, { label: "Applications using the microphone", detail: "What is listening right now, and muting it", page: "sound" }, { label: "Move an application's audio", detail: "Send one application to a different output device", page: "sound" }, { label: "Device profiles", detail: "Switch a sound card between stereo, surround, and headset modes", 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" }, // Privacy stopped handing file history to GNOME and stopped listing two // devices where the portal arbitrates six tables, so the subjects it // now actually owns are findable by their own names. "Application // permissions" appears twice on purpose: the Applications page answers // what a Flatpak's sandbox exposes, this one answers what the portal // recorded, and they are different questions with the same name. { label: "Background applications", detail: "Which applications may keep running after you close them", page: "privacy" }, { label: "Screen sharing permission", detail: "Take back a screen-capture grant an application was given", page: "privacy" }, { label: "Remote desktop permission", detail: "Take back a grant to control this desktop's pointer and keyboard", page: "privacy" }, { label: "Clear recent files", detail: "Empty the list of documents this desktop remembers you opening", page: "privacy" }, { label: "Thumbnails", detail: "The cached previews of your pictures and videos, and clearing them", page: "privacy" }, { label: "Application permissions", detail: "What the desktop portal has recorded: camera, microphone, screen, background", page: "privacy" }, // Region & Language stopped being a tab of its own and became the // Language & formats card on Date, Time & Region: a date format and the // clock that shows it are one subject, and splitting them across two // tabs meant changing how a date is written on a page that never showed // one. These route to the merged tab, and `region` itself is a retired // id that SettingsRoutes still resolves. { label: "Language", detail: "The system language, applied to programs started afterwards", page: "datetime" }, { label: "Regional formats", detail: "How dates, times, and numbers are written", page: "datetime" }, { label: "Date format", detail: "The locale that decides how dates and times are written", page: "datetime" }, { label: "Number format", detail: "Which locale's decimal and thousands separators are used", page: "datetime" }, { label: "Currency", detail: "The locale that decides how amounts of money are written", page: "datetime" }, { label: "Measurement units", detail: "Metric or imperial, for programs that ask the system", page: "datetime" }, { label: "Paper size", detail: "A4 or Letter, for programs that ask the system", page: "datetime" }, { label: "First day of the week", detail: "Whether the week starts on Sunday or Monday, as your formats say", page: "datetime" }, { label: "Set the clock by hand", detail: "Type a date and time, once network time is off", page: "datetime" }, { label: "Online accounts", detail: "Sign in to mail, calendar, and contacts", page: "accounts" }, // Adding an account is the thing people search for, and they search for // it by the name of the service. Nextcloud and mail are added on the // page itself; Google still goes through the provider's own sign-in, // which is a result worth having rather than a dead end. { label: "Nextcloud", detail: "Add a Nextcloud server for files, calendar, and contacts", page: "accounts" }, { label: "Google account", detail: "Sign in to Google for mail, calendar, and contacts", page: "accounts" }, { label: "Add a mail account", detail: "Connect a mailbox by its IMAP and SMTP servers", page: "accounts" }, { label: "Remove an account", detail: "Sign out and take an online account off this machine", page: "accounts" }, { label: "Home Assistant", detail: "Connect the desktop to a Home Assistant server", page: "my-home" }, { label: "Lights", detail: "Toggle and dim lights, grouped by room", page: "my-home" }, { label: "Control Center lights", detail: "Choose the accessories on your shelf", page: "my-home" }, { label: "Phone", detail: "Battery, signal, and reaching your phone", page: "phone" }, { label: "Ring your phone", detail: "Find it, even when silenced", page: "phone" }, { label: "Send clipboard to phone", detail: "Hand off what you just copied", page: "phone" }, { label: "Send a file to phone", detail: "Lands in the phone's downloads", page: "phone" }, { label: "iMessage", detail: "Opens BlueBubbles", page: "phone" }, { label: "System information", detail: "Kernel, distribution, and hardware", page: "about" }, { label: "Desktop version", detail: "Which Hyprland and Quickshell this session runs", page: "about" }, // About answers "what is this machine". Each fact on it is something // people arrive looking for by its own name -- a hostname to hand to // somebody, a serial number for a warranty claim, a kernel version for // a bug report -- and none of them is the label of a preference, so // none was findable before. { label: "Hostname", detail: "The name this machine answers to", page: "about" }, { label: "Kernel version", detail: "The Linux kernel this session is running", page: "about" }, { label: "Device model", detail: "The manufacturer and model of this machine", page: "about" }, { label: "Installed memory", detail: "How much RAM this machine has", page: "about" }, { label: "Uptime", detail: "How long this machine has been running since it last started", page: "about" }, { label: "Serial number", detail: "The number on the machine, for a warranty or support call", page: "about" }, { label: "BIOS version", detail: "The firmware version this machine boots with", page: "about" }, { label: "Restore defaults", detail: "Return every Panama setting to its shipped value", page: "sync" }, { label: "Carry settings to another machine", detail: "Export, preview, and import a settings file", page: "sync" }, // The verbs, not just the sentence. People arrive knowing they want to // export or import, and searching either of those words used to find // nothing at all. { label: "Export settings", detail: "Write your preferences to a file you can carry elsewhere", page: "sync" }, { label: "Import settings", detail: "Preview a settings file from another machine, then apply it", page: "sync" }, { label: "Settings backups", detail: "Snapshots of your preferences, restorable any time", page: "sync" }, { label: "Pinned applications", detail: "Reorder the dock by dragging, here or on the dock itself", page: "dock" }, { label: "Bar text", detail: "Keep the bar legible on any wallpaper", page: "bar" }, { label: "Control Center sections", detail: "Choose what the panel offers", page: "control-center" }, { label: "Do Not Disturb tile", detail: "The Control Center switch that holds banners back, beside Presentation", page: "control-center" }, // Signing out has no preference anywhere: it is a power-menu verb, and // "log out" returned nothing at all on a desktop that plainly does it. { label: "Log out", detail: "Sign out of this session from the power menu — the same menu that restarts and powers off", page: "power" }, { label: "Sign out", detail: "End this session and return to the login screen", page: "power" }, { label: "Do Not Disturb", detail: "Hold banners back until you turn it off", page: "notifications" }, { label: "Quiet hours", detail: "The schedule the Sleep focus mode keeps", page: "notifications" }, { label: "Critical alerts break through", detail: "Let urgent notifications past Do Not Disturb", page: "notifications" }, { label: "Application notification rules", detail: "Which applications may notify you, and how", page: "notifications" }, { label: "Forget an application's notifications", detail: "Remove its rule; it returns on its next notification", page: "notifications" }, { label: "Per-application notification sound", detail: "Turn the chime off for one application", page: "notifications" }, { label: "Banners or history", detail: "Send one application straight to history without a popup", page: "notifications" }, { label: "Focus session duration", detail: "How long a focus session runs before it ends itself", page: "focus" }, { label: "Keyboard shortcuts", detail: "Every shortcut the compositor has bound", page: "shortcuts" }, // The rebinding flow has no schema entry of its own — it is a button on // a row — so searching for the thing people actually want to do would // otherwise find only the shortcut it is being done to. { label: "Rebind a shortcut", detail: "Change the keys an action answers to, or put them back", page: "shortcuts" }, // Tier 2. A custom shortcut, an app rule and a gesture assignment are // all the same thing wearing three hats -- a named action -- and none // of the three is a preference with a label to find it by. { label: "Custom shortcut", detail: "Bind your own keys to an application, a shell action, or a window action", page: "shortcuts" }, { label: "Add a shortcut", detail: "Press the chord you want, then pick what it should do", page: "shortcuts" }, { label: "Launch an application with a shortcut", detail: "Give an application its own key combination", page: "shortcuts" }, { label: "Application window rules", detail: "Per-application window rules: float, center, size, workspace, and no dimming", page: "tiling" }, { label: "Window rules", detail: "Make one application always float, open on a workspace, or skip the animations", page: "tiling" }, { label: "Always float a window", detail: "A per-application rule, so one application stops being tiled", page: "tiling" }, { label: "Gestures", detail: "Three-finger swipes as shipped, and four-finger swipes you assign yourself", page: "mouse" }, { label: "Touchpad gestures", detail: "What swiping with three or four fingers does", page: "mouse" }, { label: "Four-finger swipe", detail: "Assign an application or a shell action to each direction", page: "mouse" }, { label: "Pointer test area", detail: "Scribble and scroll to feel a pointer change before keeping it", page: "mouse" }, { label: "Connected input devices", detail: "The keyboards, mice, and touchpad this machine can see", page: "mouse" }, // Accessibility. The schema covers the switches by their own labels, so // these are the things people arrive with that the schema does not say: // the verb rather than the noun ("zoom in", not "Magnifier"), the // application's name rather than its category ("Orca", not "Screen // reader"), and — the one worth having most — sticky keys, which this // desktop does not offer at all. A search that found nothing there // would read as the desktop having no opinion, when in fact the page // carries a paragraph explaining exactly why the switch is absent. { label: "Magnifier zoom", detail: "Magnify the screen around the pointer, drawn by the compositor itself", page: "accessibility" }, { label: "Zoom in and out", detail: "Step the magnifier from anywhere with a shortcut; the OSD shows the level", page: "accessibility" }, { label: "Reduce motion", detail: "Still the windows, the workspaces, and the shell's own bar, dock and panels", page: "accessibility" }, { label: "Visual alerts", detail: "Flash the screen edges once when a notification arrives, instead of relying on the bell", page: "accessibility" }, { label: "Screen reader", detail: "Start Orca and see whether the accessibility bus is up", page: "accessibility" }, { label: "Orca", detail: "The screen reader: whether it is running, and starting or stopping it", page: "accessibility" }, { label: "Sticky keys", detail: "Why sticky, slow and bounce keys are not offered in this session", page: "accessibility" }, // The color filter is a compositor shader rather than a switch, so it // is findable by the condition rather than only by the word "filter". { label: "Color filter", detail: "A whole-screen filter the compositor renders: grayscale, or one for each kind of color blindness", page: "accessibility" }, { label: "Grayscale", detail: "Drain the color out of the whole screen", page: "accessibility" }, { label: "Color blindness", detail: "Protanopia, deuteranopia and tritanopia filters applied to the whole screen", page: "accessibility" }, { label: "Dictation", detail: "Speech to text with Super+D, typed where the cursor is", page: "dictation" }, { label: "Speech to text", detail: "Set up the local speech server and model", page: "dictation" }, { 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", section: "background" }, { label: "Password field", detail: "Choose whether the empty lock-screen field stays visible", page: "appearance", section: "background" }, { label: "Per-display wallpaper", detail: "Assign a different image to each connected display", page: "appearance", section: "background" }, { 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" }, { label: "Resolution", detail: "The mode each display runs, with its refresh rate", page: "displays" }, { label: "Refresh rate", detail: "How many times a second the display redraws", page: "displays" }, { label: "Scale", detail: "How large everything is drawn on a display", page: "displays" }, { label: "Rotation", detail: "Turn a display a quarter, half, or three-quarter turn", page: "displays" }, { label: "HDR", detail: "High dynamic range for a display that supports it", page: "displays" }, { label: "Color profile", detail: "Automatic, sRGB, wide gamut, or HDR per display", page: "displays" }, { label: "Bit depth", detail: "8-bit or 10-bit color, per display", page: "displays" }, { label: "10-bit", detail: "Deeper color with less gradient banding", page: "displays" }, { label: "SDR brightness", detail: "How bright ordinary content sits inside HDR", page: "displays" }, { label: "Mirror displays", detail: "Show the same picture on a second display", page: "displays" }, { label: "Variable refresh rate", detail: "Override the gaming policy for one display", page: "displays" }, { label: "Monitor brightness", detail: "The monitor's own backlight, over DDC", page: "displays" }, { label: "Theme profiles", detail: "Switch between Moon, Moon Rose, Day, and saved themes", page: "appearance", section: "themes" }, { label: "Themes", detail: "Light and dark mode, and the theme for each", page: "appearance", section: "themes" }, { label: "Dark mode", detail: "Flip the desktop to your dark theme", page: "appearance", section: "themes" }, { label: "Light mode", detail: "Flip the desktop to your light theme", page: "appearance", section: "themes" }, { label: "Theme editor", detail: "Build your own theme — colors, saturation, and effects", page: "appearance", section: "editor" }, { label: "Catppuccin", detail: "Mocha and Latte, in the theme galleries", page: "appearance", section: "themes" }, { label: "Nord", detail: "The arctic dark theme, in the gallery", page: "appearance", section: "themes" }, { label: "Gruvbox", detail: "Dark and light, in the theme galleries", page: "appearance", section: "themes" }, { label: "Everforest", detail: "Dark and light, in the theme galleries", page: "appearance", section: "themes" }, { label: "Tokyo Night", detail: "Moon and Day, the shipped defaults", page: "appearance", section: "themes" }, { label: "Video wallpaper", detail: "A looping video as the desktop background", page: "appearance", section: "background" }, { label: "Advanced accent", detail: "Adjust primary and secondary hue, saturation, and value", page: "appearance", section: "editor" }, { label: "Pick color from screen", detail: "Sample an accent color with hyprpicker", page: "appearance" } ] function pageFor(group: string): string { return root.groupPages[group] ?? "home"; } // Both sides of a comparison, in the one spelling. // // The hyphens go because they are a typographic choice rather than a word // boundary, and the desktop's most-searched noun is the worst case: // everything here spells it "Wi-Fi" and nobody types it that way, so "wifi" // matched nothing at all. Applied to the query and the index alike, so the // rule is a spelling equivalence rather than a special case for one word. function flatten(text: string): string { return String(text).trim().toLowerCase().replace(/-/g, ""); } // [{ label, detail, page, kind, section }] for a query. Empty query yields // nothing: the sidebar shows its normal navigation in that case. // // The needle is split on whitespace and every token has to appear somewhere // in the haystack -- an AND over words rather than one contiguous // substring. The old shape matched the whole query as typed, so "wifi // password", "log out" and "metered" returned nothing on a settings app // that has all three: the words are all present, just not adjacent and not // in that order. Order was the accidental part, and it was doing the most // damage. function search(query: string): var { const needle = root.flatten(query); if (needle === "") return []; const tokens = needle.split(/\s+/).filter(token => token !== ""); if (tokens.length === 0) return []; function hit(haystack) { const text = root.flatten(haystack); return tokens.every(token => text.indexOf(token) >= 0); } const results = []; const seen = {}; function add(label, detail, page, kind, section) { const dedupe = `${kind}:${label}:${page}`; if (seen[dedupe]) return; seen[dedupe] = true; results.push({ label: label, detail: detail, page: page, kind: kind, // "" means "the page's own first tab", which is every result // that does not name one. See SettingsSidebar. section: String(section ?? "") }); } 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}`; if (hit(haystack)) add(entry.label, entry.detail ?? "", root.pageFor(entry.group), "setting", ""); } for (const entry of root.extraEntries) { if (hit(`${entry.label} ${entry.detail}`)) add(entry.label, entry.detail, entry.page, "setting", entry.section ?? ""); } for (const bind of Keybinds.binds) { if (hit(bind.description)) 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. // // Between those two comes the tokenized rule: a row whose LABEL holds // every word of the query beats one that only holds some of them, or // holds them in its explanation. Without it, "wifi password" would rank // every row whose detail happens to say "password" alongside the rows // that are actually about the Wi-Fi password. 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 => root.flatten(option.label) === needle); const bo = (bSpec?.options ?? []).some(option => root.flatten(option.label) === needle); if (ao !== bo) return ao ? -1 : 1; const al = root.flatten(a.label); const bl = root.flatten(b.label); const at = tokens.every(token => al.indexOf(token) >= 0) ? 0 : 1; const bt = tokens.every(token => bl.indexOf(token) >= 0) ? 0 : 1; if (at !== bt) return at - bt; 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); } }