Files
Panama/config/dot/quickshell/services/SettingsSearch.qml
T

302 lines
23 KiB
QML

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",
"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.
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" },
{ 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" },
{ 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: "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" },
{ 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: "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" },
{ 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" },
{ 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", 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 app's notifications", detail: "Remove its rule; it returns on its next notification", page: "notifications" },
{ label: "Per-app 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" },
{ 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" },
{ 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" },
{ 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" },
{ 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 colour, per display", page: "displays" },
{ label: "10-bit", detail: "Deeper colour 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" },
{ label: "Themes", detail: "Light and dark mode, and the theme for each", page: "appearance" },
{ label: "Dark mode", detail: "Flip the desktop to your dark theme", page: "appearance" },
{ label: "Light mode", detail: "Flip the desktop to your light theme", page: "appearance" },
{ label: "Theme editor", detail: "Build your own theme — colors, saturation, and effects", page: "appearance" },
{ label: "Catppuccin", detail: "Mocha and Latte, in the theme galleries", page: "appearance" },
{ label: "Nord", detail: "The arctic dark theme, in the gallery", page: "appearance" },
{ label: "Gruvbox", detail: "Dark and light, in the theme galleries", page: "appearance" },
{ label: "Everforest", detail: "Dark and light, in the theme galleries", page: "appearance" },
{ label: "Tokyo Night", detail: "Moon and Day, the shipped defaults", page: "appearance" },
{ label: "Video wallpaper", detail: "A looping video as the desktop background", page: "appearance" },
{ label: "Advanced accent", detail: "Adjust primary and secondary hue, saturation, and value", page: "appearance" },
{ label: "Pick colour from screen", detail: "Sample an accent colour with hyprpicker", page: "appearance" }
]
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);
}
}