import { isElectron } from "~/env";import { isMacPlatform, isWindowsPlatform, normalizeSearchText } from "~/lib/utils"; export type SettingsPath = | "/settings/general" | "/settings/appearance" | "/settings/keybindings" | "/settings/providers" | "/settings/integrations" | "/settings/source-control" | "/settings/connections" | "/settings/archived"; export interface SettingsSearchItem { readonly id: string; readonly title: string; readonly to: SettingsPath; readonly targetId?: string; /** Descriptions, option labels, and aliases people may remember instead of the title. */ readonly searchTerms?: ReadonlyArray<string>; // Its row only renders in the desktop app, so a browser result would land on // an anchor that isn't there. readonly desktopOnly?: boolean; readonly macOnly?: boolean; // Its row only renders on Windows desktop, so other desktop platforms must // not expose a result that points to a missing anchor. readonly windowsOnly?: boolean; readonly cloudOnly?: boolean; readonly primaryOnly?: boolean; readonly providerSettingsOnly?: boolean; readonly localBackendManagementOnly?: boolean; readonly wslAvailableOnly?: boolean; readonly requiresThreadAutoSettlement?: boolean;} export interface SettingsSearchAvailability { readonly hasCloudPublicConfig: boolean; readonly hasPrimaryEnvironment: boolean; readonly hasProviderSettingsEnvironment: boolean; readonly canManageLocalBackend: boolean; readonly isWslSettingsRowVisible: boolean; readonly hasThreadAutoSettlement: boolean;} /** * Section labels in sidebar order. The sidebar nav and the search-result * subtitles both render from this record, so each label exists once. */export const SETTINGS_SECTION_LABELS: Readonly<Record<SettingsPath, string>> = { "/settings/general": "General", "/settings/appearance": "Appearance", "/settings/keybindings": "Keybindings", "/settings/providers": "Providers", "/settings/integrations": "Integrations", "/settings/source-control": "Source Control", "/settings/connections": "Connections", "/settings/archived": "Archive",}; /** * Searchable settings and stable destinations, in result order. Rows with a * dedicated anchor render their id and title via `searchableSetting`; items * that may not be mounted point at their nearest stable section instead. */export const SETTINGS_SEARCH_ITEMS = [ { id: "color-scheme", title: "Color scheme", to: "/settings/appearance", searchTerms: ["appearance light dark system mode"], // The scheme tiles sit at the top of the Appearance section. targetId: "appearance", }, { id: "theme", title: "Themes", to: "/settings/appearance", searchTerms: ["appearance colors palette custom import"], // Theme cards live directly under the scheme tiles; the section is the // stable scroll destination for both. targetId: "appearance", }, { // Prefixed because the slider control already owns the `appearance-contrast` id. id: "setting-appearance-contrast", title: "Contrast", to: "/settings/appearance", searchTerms: ["colors borders interface"], }, { // Prefixed because the slider control already owns the `glass-opacity` id. id: "setting-glass-opacity", title: "Glass opacity", to: "/settings/appearance", searchTerms: ["transparent transparency solid menus dialogs composer"], }, { id: "environment-identification", title: "Environment identification", to: "/settings/appearance", searchTerms: ["dev nightly artwork pill label hide none"], // The setting is stage-dependent, so its parent section is the stable destination. targetId: "appearance", }, { id: "interface-font", title: "Interface font", to: "/settings/appearance", searchTerms: ["typography family size system sans"], }, { id: "prompt-font", title: "Prompt font", to: "/settings/appearance", searchTerms: ["typography family size composer input"], }, { id: "code-font", title: "Code font", to: "/settings/appearance", searchTerms: ["typography family size monospace code blocks diffs file previews"], }, { id: "terminal-font", title: "Terminal font", to: "/settings/appearance", searchTerms: ["typography family size monospace output"], }, { id: "font-smoothing", title: "Font smoothing", to: "/settings/appearance", searchTerms: ["typography text grayscale anti aliasing macos thin"], macOnly: true, }, { id: "word-wrap", title: "Word wrap", to: "/settings/appearance", searchTerms: ["long lines code blocks tables diffs file previews"], }, { id: "project-grouping", title: "Project grouping", to: "/settings/general", searchTerms: ["combine matching repositories environments sidebar"], }, { id: "auto-settle-inactive-threads", title: "Auto-settle inactive threads", to: "/settings/general", searchTerms: ["sidebar inactivity days no activity automatically"], requiresThreadAutoSettlement: true, }, { id: "auto-settle-merged-threads", title: "Auto-settle merged threads", to: "/settings/general", searchTerms: ["pull request merge closed automatically sidebar"], requiresThreadAutoSettlement: true, }, { id: "days-before-auto-settle", title: "Days of inactivity before auto-settle", to: "/settings/general", targetId: "auto-settle-inactive-threads", searchTerms: ["thread timeout activity sidebar"], requiresThreadAutoSettlement: true, }, { id: "time-format", title: "Time format", to: "/settings/general", searchTerms: ["timestamp clock locale system browser os 12 hour 24 hour"], }, { id: "hide-whitespace-changes", title: "Hide whitespace changes", to: "/settings/general", searchTerms: ["diff ignore spaces edits default"], }, { id: "skills-in-slash-menu", title: "Show skills in slash menu", to: "/settings/general", searchTerms: ["command menu dollar $ slash /"], }, { id: "provider-update-checks", title: "Provider update checks", to: "/settings/general", searchTerms: ["installed cli versions newer available codex claude cursor grok opencode"], }, { id: "background-activity", title: "Background activity", to: "/settings/general", searchTerms: [ "balanced performance battery saver advanced git fetch provider health refresh host power monitor idle policy", ], }, { id: "new-threads", title: "New threads", to: "/settings/general", searchTerms: ["default workspace mode draft local worktree"], }, { id: "start-from-origin", title: "Start from origin", to: "/settings/general", targetId: "new-threads", searchTerms: ["new worktrees latest matching remote branch local"], }, { id: "add-project-starts-in", title: "Add project starts in", to: "/settings/general", searchTerms: ["base directory folder browser path home"], }, { id: "unpin-confirmation", title: "Unpin confirmation", to: "/settings/general", searchTerms: ["ask before thread pinned section"], }, { id: "archive-confirmation", title: "Archive confirmation", to: "/settings/general", searchTerms: ["ask before thread second click inline action"], }, { id: "delete-confirmation", title: "Delete confirmation", to: "/settings/general", searchTerms: ["ask before thread chat history"], }, { id: "quit-confirmation", title: "Hold to quit", to: "/settings/general", searchTerms: ["confirmation shortcut desktop app exit"], desktopOnly: true, }, { id: "text-generation-model", title: "Text generation model", to: "/settings/general", searchTerms: ["generated thread titles source control content default provider"], }, { id: "diagnostics", title: "Diagnostics", to: "/settings/general", searchTerms: ["logs traces processes resource history failures spans cpu memory"], }, { id: "legacy-plan-mode", title: "Plan mode (legacy)", to: "/settings/general", searchTerms: ["build plan composer old"], }, { id: "legacy-token-streaming", title: "Stream token by token (legacy)", to: "/settings/general", searchTerms: ["response output old compatibility"], }, { id: "legacy-sidebar", title: "Sidebar (legacy)", to: "/settings/general", searchTerms: ["project thread tree old flat list"], }, { id: "keybindings", title: "Keybindings", to: "/settings/keybindings", searchTerms: ["keyboard shortcuts hotkeys commands bindings json"], }, { id: "providers", title: "Providers", to: "/settings/providers", searchTerms: [ "agents cli codex claude cursor grok opencode instances authentication api key models configuration binary path config directory endpoint arguments environment variables display name accent color custom favorite hidden auto compact", ], }, { id: "provider-health-check-interval", title: "Health check interval", to: "/settings/providers", searchTerms: ["refresh availability versions auth state models background probes seconds off"], providerSettingsOnly: true, }, { id: "agent-browser-access", title: "Agent browser access", to: "/settings/integrations", searchTerms: ["allow open drive preview tools sessions"], }, { id: "browser-default-viewport", title: "Default browser viewport", to: "/settings/integrations", searchTerms: ["preview size width height device desktop mobile rotate"], }, { id: "browser-default-zoom", title: "Default browser zoom", to: "/settings/integrations", searchTerms: ["preview page scale tabs percent"], }, { id: "browser-default-appearance", title: "Default browser appearance", to: "/settings/integrations", searchTerms: ["preview color scheme light dark system os"], }, { id: "browser-recording-frame-rate", title: "Browser recording frame rate", to: "/settings/integrations", }, { id: "browser-auto-show-floating-preview", title: "Auto-show floating preview", to: "/settings/integrations", searchTerms: ["agent opens browser pop into view hide"], }, { id: "source-control", title: "Source control", to: "/settings/source-control", searchTerms: [ "version control git github gitlab bitbucket azure devops hosting integrations credentials scan server environment", ], }, { id: "git-fetch-interval", title: "Git fetch interval", to: "/settings/source-control", searchTerms: [ "automatic remote branch refresh background credentials security keys seconds off", ], primaryOnly: true, }, { id: "source-control-writing-style", title: "Source control writing style", to: "/settings/source-control", searchTerms: [ "repository conventions conventional commits custom instructions change descriptions request titles", ], primaryOnly: true, }, { id: "follow-change-request-templates", title: "Follow change request templates", to: "/settings/source-control", searchTerms: ["repository pr pull request description structure"], primaryOnly: true, }, { id: "source-control-writer-model", title: "Source control writer model", to: "/settings/source-control", searchTerms: [ "override generated commit change request pr titles descriptions branch bookmark", ], primaryOnly: true, }, { id: "network-access", title: "Network access", to: "/settings/connections", targetId: "connections-environment", searchTerms: ["expose backend remote pairing local machine interfaces host restart"], localBackendManagementOnly: true, }, { id: "tailscale-https", title: "Tailscale HTTPS", to: "/settings/connections", targetId: "connections-environment", searchTerms: ["serve magicdns endpoint remote secure network"], desktopOnly: true, localBackendManagementOnly: true, }, { id: "wsl-backend", title: "WSL backend", to: "/settings/connections", searchTerms: [ "windows subsystem linux distro second server projects stop windows backend restart", ], desktopOnly: true, windowsOnly: true, localBackendManagementOnly: true, wslAvailableOnly: true, }, { id: "t3-connect", title: "T3 Connect", to: "/settings/connections", targetId: "connections-environment", searchTerms: ["managed tunnel cloud other devices remote"], desktopOnly: true, cloudOnly: true, }, { id: "publish-agent-activity", title: "Publish agent activity", to: "/settings/connections", targetId: "connections-environment", searchTerms: ["mobile push notifications live activities cloud tunnel"], cloudOnly: true, }, { id: "connections-environment", title: "This environment", to: "/settings/connections", searchTerms: [ "connections server backend local remote access administrative permissions scope pairing links qr code authorized clients sessions revoke endpoint", ], }, { id: "remote-environments", title: "Remote environments", to: "/settings/connections", searchTerms: ["add pair backend host code ssh config agent tunnel saved t3 connect"], }, { id: "archive", title: "Archived threads", to: "/settings/archived", searchTerms: ["restore reopen deleted history projects"], },] as const satisfies ReadonlyArray<SettingsSearchItem>; export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"]; const SEARCH_ITEMS_BY_ID = new Map(SETTINGS_SEARCH_ITEMS.map((item) => [item.id, item] as const)); /** * `id` and `title` props for the element a search item anchors to. Panels * spread (or pick from) this instead of restating the strings, so the catalog * and the rendered settings cannot drift apart. */export function searchableSetting(id: SettingsSearchItemId): { readonly id: string; readonly title: string;} { const { id: anchorId, title } = SEARCH_ITEMS_BY_ID.get(id)!; return { id: anchorId, title };} export function filterAvailableSettingsSearchItems( availability: SettingsSearchAvailability,): ReadonlyArray<SettingsSearchItem> { const items: ReadonlyArray<SettingsSearchItem> = SETTINGS_SEARCH_ITEMS; return items.filter( (item) => (!item.cloudOnly || availability.hasCloudPublicConfig) && (!item.primaryOnly || availability.hasPrimaryEnvironment) && (!item.providerSettingsOnly || availability.hasProviderSettingsEnvironment) && (!item.localBackendManagementOnly || availability.canManageLocalBackend) && (!item.wslAvailableOnly || availability.isWslSettingsRowVisible) && (!item.requiresThreadAutoSettlement || availability.hasThreadAutoSettlement), );} export function searchSettings( query: string, items: ReadonlyArray<SettingsSearchItem> = SETTINGS_SEARCH_ITEMS,): ReadonlyArray<SettingsSearchItem> { const normalizedQuery = normalizeSearchText(query); if (normalizedQuery.length === 0) return []; const queryTokens = normalizedQuery.split(" "); const platform = typeof navigator === "undefined" ? "" : navigator.platform; return items .flatMap((item, index) => { if (!isElectron && item.desktopOnly === true) return []; if (item.macOnly && !isMacPlatform(platform)) return []; if (item.windowsOnly && !isWindowsPlatform(platform)) return []; const title = normalizeSearchText(item.title); const fields = [ title, normalizeSearchText(SETTINGS_SECTION_LABELS[item.to]), ...(item.searchTerms ?? []).map(normalizeSearchText), ]; if (!queryTokens.every((token) => fields.some((field) => field.includes(token)))) return []; const exactPhraseField = fields.findIndex((field) => field.includes(normalizedQuery)); const rank = title === normalizedQuery ? 5Lines 1–500 of 514. Load more
import { isElectron } from "~/env"; import { isMacPlatform, isWindowsPlatform, normalizeSearchText } from "~/lib/utils"; export type SettingsPath = | "/settings/general" | "/settings/appearance" | "/settings/keybindings" | "/settings/providers" | "/settings/integrations" | "/settings/source-control" | "/settings/connections" | "/settings/archived"; export interface SettingsSearchItem { readonly id: string; readonly title: string; readonly to: SettingsPath; readonly targetId?: string; /** Descriptions, option labels, and aliases people may remember instead of the title. */ readonly searchTerms?: ReadonlyArray<string>; // Its row only renders in the desktop app, so a browser result would land on // an anchor that isn't there. readonly desktopOnly?: boolean; readonly macOnly?: boolean; // Its row only renders on Windows desktop, so other desktop platforms must // not expose a result that points to a missing anchor. readonly windowsOnly?: boolean; readonly cloudOnly?: boolean; readonly primaryOnly?: boolean; readonly providerSettingsOnly?: boolean; readonly localBackendManagementOnly?: boolean; readonly wslAvailableOnly?: boolean; readonly requiresThreadAutoSettlement?: boolean; } export interface SettingsSearchAvailability { readonly hasCloudPublicConfig: boolean; readonly hasPrimaryEnvironment: boolean; readonly hasProviderSettingsEnvironment: boolean; readonly canManageLocalBackend: boolean; readonly isWslSettingsRowVisible: boolean; readonly hasThreadAutoSettlement: boolean; } /** * Section labels in sidebar order. The sidebar nav and the search-result * subtitles both render from this record, so each label exists once. */ export const SETTINGS_SECTION_LABELS: Readonly<Record<SettingsPath, string>> = { "/settings/general": "General", "/settings/appearance": "Appearance", "/settings/keybindings": "Keybindings", "/settings/providers": "Providers", "/settings/integrations": "Integrations", "/settings/source-control": "Source Control", "/settings/connections": "Connections", "/settings/archived": "Archive", }; /** * Searchable settings and stable destinations, in result order. Rows with a * dedicated anchor render their id and title via `searchableSetting`; items * that may not be mounted point at their nearest stable section instead. */ export const SETTINGS_SEARCH_ITEMS = [ { id: "color-scheme", title: "Color scheme", to: "/settings/appearance", searchTerms: ["appearance light dark system mode"], // The scheme tiles sit at the top of the Appearance section. targetId: "appearance", }, { id: "theme", title: "Themes", to: "/settings/appearance", searchTerms: ["appearance colors palette custom import"], // Theme cards live directly under the scheme tiles; the section is the // stable scroll destination for both. targetId: "appearance", }, { // Prefixed because the slider control already owns the `appearance-contrast` id. id: "setting-appearance-contrast", title: "Contrast", to: "/settings/appearance", searchTerms: ["colors borders interface"], }, { // Prefixed because the slider control already owns the `glass-opacity` id. id: "setting-glass-opacity", title: "Glass opacity", to: "/settings/appearance", searchTerms: ["transparent transparency solid menus dialogs composer"], }, { id: "environment-identification", title: "Environment identification", to: "/settings/appearance", searchTerms: ["dev nightly artwork pill label hide none"], // The setting is stage-dependent, so its parent section is the stable destination. targetId: "appearance", }, { id: "interface-font", title: "Interface font", to: "/settings/appearance", searchTerms: ["typography family size system sans"], }, { id: "prompt-font", title: "Prompt font", to: "/settings/appearance", searchTerms: ["typography family size composer input"], }, { id: "code-font", title: "Code font", to: "/settings/appearance", searchTerms: ["typography family size monospace code blocks diffs file previews"], }, { id: "terminal-font", title: "Terminal font", to: "/settings/appearance", searchTerms: ["typography family size monospace output"], }, { id: "font-smoothing", title: "Font smoothing", to: "/settings/appearance", searchTerms: ["typography text grayscale anti aliasing macos thin"], macOnly: true, }, { id: "word-wrap", title: "Word wrap", to: "/settings/appearance", searchTerms: ["long lines code blocks tables diffs file previews"], }, { id: "project-grouping", title: "Project grouping", to: "/settings/general", searchTerms: ["combine matching repositories environments sidebar"], }, { id: "auto-settle-inactive-threads", title: "Auto-settle inactive threads", to: "/settings/general", searchTerms: ["sidebar inactivity days no activity automatically"], requiresThreadAutoSettlement: true, }, { id: "auto-settle-merged-threads", title: "Auto-settle merged threads", to: "/settings/general", searchTerms: ["pull request merge closed automatically sidebar"], requiresThreadAutoSettlement: true, }, { id: "days-before-auto-settle", title: "Days of inactivity before auto-settle", to: "/settings/general", targetId: "auto-settle-inactive-threads", searchTerms: ["thread timeout activity sidebar"], requiresThreadAutoSettlement: true, }, { id: "time-format", title: "Time format", to: "/settings/general", searchTerms: ["timestamp clock locale system browser os 12 hour 24 hour"], }, { id: "hide-whitespace-changes", title: "Hide whitespace changes", to: "/settings/general", searchTerms: ["diff ignore spaces edits default"], }, { id: "skills-in-slash-menu", title: "Show skills in slash menu", to: "/settings/general", searchTerms: ["command menu dollar $ slash /"], }, { id: "provider-update-checks", title: "Provider update checks", to: "/settings/general", searchTerms: ["installed cli versions newer available codex claude cursor grok opencode"], }, { id: "background-activity", title: "Background activity", to: "/settings/general", searchTerms: [ "balanced performance battery saver advanced git fetch provider health refresh host power monitor idle policy", ], }, { id: "new-threads", title: "New threads", to: "/settings/general", searchTerms: ["default workspace mode draft local worktree"], }, { id: "start-from-origin", title: "Start from origin", to: "/settings/general", targetId: "new-threads", searchTerms: ["new worktrees latest matching remote branch local"], }, { id: "add-project-starts-in", title: "Add project starts in", to: "/settings/general", searchTerms: ["base directory folder browser path home"], }, { id: "unpin-confirmation", title: "Unpin confirmation", to: "/settings/general", searchTerms: ["ask before thread pinned section"], }, { id: "archive-confirmation", title: "Archive confirmation", to: "/settings/general", searchTerms: ["ask before thread second click inline action"], }, { id: "delete-confirmation", title: "Delete confirmation", to: "/settings/general", searchTerms: ["ask before thread chat history"], }, { id: "quit-confirmation", title: "Hold to quit", to: "/settings/general", searchTerms: ["confirmation shortcut desktop app exit"], desktopOnly: true, }, { id: "text-generation-model", title: "Text generation model", to: "/settings/general", searchTerms: ["generated thread titles source control content default provider"], }, { id: "diagnostics", title: "Diagnostics", to: "/settings/general", searchTerms: ["logs traces processes resource history failures spans cpu memory"], }, { id: "legacy-plan-mode", title: "Plan mode (legacy)", to: "/settings/general", searchTerms: ["build plan composer old"], }, { id: "legacy-token-streaming", title: "Stream token by token (legacy)", to: "/settings/general", searchTerms: ["response output old compatibility"], }, { id: "legacy-sidebar", title: "Sidebar (legacy)", to: "/settings/general", searchTerms: ["project thread tree old flat list"], }, { id: "keybindings", title: "Keybindings", to: "/settings/keybindings", searchTerms: ["keyboard shortcuts hotkeys commands bindings json"], }, { id: "providers", title: "Providers", to: "/settings/providers", searchTerms: [ "agents cli codex claude cursor grok opencode instances authentication api key models configuration binary path config directory endpoint arguments environment variables display name accent color custom favorite hidden auto compact", ], }, { id: "provider-health-check-interval", title: "Health check interval", to: "/settings/providers", searchTerms: ["refresh availability versions auth state models background probes seconds off"], providerSettingsOnly: true, }, { id: "agent-browser-access", title: "Agent browser access", to: "/settings/integrations", searchTerms: ["allow open drive preview tools sessions"], }, { id: "browser-default-viewport", title: "Default browser viewport", to: "/settings/integrations", searchTerms: ["preview size width height device desktop mobile rotate"], }, { id: "browser-default-zoom", title: "Default browser zoom", to: "/settings/integrations", searchTerms: ["preview page scale tabs percent"], }, { id: "browser-default-appearance", title: "Default browser appearance", to: "/settings/integrations", searchTerms: ["preview color scheme light dark system os"], }, { id: "browser-recording-frame-rate", title: "Browser recording frame rate", to: "/settings/integrations", }, { id: "browser-auto-show-floating-preview", title: "Auto-show floating preview", to: "/settings/integrations", searchTerms: ["agent opens browser pop into view hide"], }, { id: "source-control", title: "Source control", to: "/settings/source-control", searchTerms: [ "version control git github gitlab bitbucket azure devops hosting integrations credentials scan server environment", ], }, { id: "git-fetch-interval", title: "Git fetch interval", to: "/settings/source-control", searchTerms: [ "automatic remote branch refresh background credentials security keys seconds off", ], primaryOnly: true, }, { id: "source-control-writing-style", title: "Source control writing style", to: "/settings/source-control", searchTerms: [ "repository conventions conventional commits custom instructions change descriptions request titles", ], primaryOnly: true, }, { id: "follow-change-request-templates", title: "Follow change request templates", to: "/settings/source-control", searchTerms: ["repository pr pull request description structure"], primaryOnly: true, }, { id: "source-control-writer-model", title: "Source control writer model", to: "/settings/source-control", searchTerms: [ "override generated commit change request pr titles descriptions branch bookmark", ], primaryOnly: true, }, { id: "network-access", title: "Network access", to: "/settings/connections", targetId: "connections-environment", searchTerms: ["expose backend remote pairing local machine interfaces host restart"], localBackendManagementOnly: true, }, { id: "tailscale-https", title: "Tailscale HTTPS", to: "/settings/connections", targetId: "connections-environment", searchTerms: ["serve magicdns endpoint remote secure network"], desktopOnly: true, localBackendManagementOnly: true, }, { id: "wsl-backend", title: "WSL backend", to: "/settings/connections", searchTerms: [ "windows subsystem linux distro second server projects stop windows backend restart", ], desktopOnly: true, windowsOnly: true, localBackendManagementOnly: true, wslAvailableOnly: true, }, { id: "t3-connect", title: "T3 Connect", to: "/settings/connections", targetId: "connections-environment", searchTerms: ["managed tunnel cloud other devices remote"], desktopOnly: true, cloudOnly: true, }, { id: "publish-agent-activity", title: "Publish agent activity", to: "/settings/connections", targetId: "connections-environment", searchTerms: ["mobile push notifications live activities cloud tunnel"], cloudOnly: true, }, { id: "connections-environment", title: "This environment", to: "/settings/connections", searchTerms: [ "connections server backend local remote access administrative permissions scope pairing links qr code authorized clients sessions revoke endpoint", ], }, { id: "remote-environments", title: "Remote environments", to: "/settings/connections", searchTerms: ["add pair backend host code ssh config agent tunnel saved t3 connect"], }, { id: "archive", title: "Archived threads", to: "/settings/archived", searchTerms: ["restore reopen deleted history projects"], }, ] as const satisfies ReadonlyArray<SettingsSearchItem>; export type SettingsSearchItemId = (typeof SETTINGS_SEARCH_ITEMS)[number]["id"]; const SEARCH_ITEMS_BY_ID = new Map(SETTINGS_SEARCH_ITEMS.map((item) => [item.id, item] as const)); /** * `id` and `title` props for the element a search item anchors to. Panels * spread (or pick from) this instead of restating the strings, so the catalog * and the rendered settings cannot drift apart. */ export function searchableSetting(id: SettingsSearchItemId): { readonly id: string; readonly title: string; } { const { id: anchorId, title } = SEARCH_ITEMS_BY_ID.get(id)!; return { id: anchorId, title }; } export function filterAvailableSettingsSearchItems( availability: SettingsSearchAvailability, ): ReadonlyArray<SettingsSearchItem> { const items: ReadonlyArray<SettingsSearchItem> = SETTINGS_SEARCH_ITEMS; return items.filter( (item) => (!item.cloudOnly || availability.hasCloudPublicConfig) && (!item.primaryOnly || availability.hasPrimaryEnvironment) && (!item.providerSettingsOnly || availability.hasProviderSettingsEnvironment) && (!item.localBackendManagementOnly || availability.canManageLocalBackend) && (!item.wslAvailableOnly || availability.isWslSettingsRowVisible) && (!item.requiresThreadAutoSettlement || availability.hasThreadAutoSettlement), ); } export function searchSettings( query: string, items: ReadonlyArray<SettingsSearchItem> = SETTINGS_SEARCH_ITEMS, ): ReadonlyArray<SettingsSearchItem> { const normalizedQuery = normalizeSearchText(query); if (normalizedQuery.length === 0) return []; const queryTokens = normalizedQuery.split(" "); const platform = typeof navigator === "undefined" ? "" : navigator.platform; return items .flatMap((item, index) => { if (!isElectron && item.desktopOnly === true) return []; if (item.macOnly && !isMacPlatform(platform)) return []; if (item.windowsOnly && !isWindowsPlatform(platform)) return []; const title = normalizeSearchText(item.title); const fields = [ title, normalizeSearchText(SETTINGS_SECTION_LABELS[item.to]), ...(item.searchTerms ?? []).map(normalizeSearchText), ]; if (!queryTokens.every((token) => fields.some((field) => field.includes(token)))) return []; const exactPhraseField = fields.findIndex((field) => field.includes(normalizedQuery)); const rank = title === normalizedQuery ? 5