diff --git a/packages/tui/src/component/command-palette.tsx b/packages/tui/src/component/command-palette.tsx
index d9db1a1cd39..e801a8093a6 100644
--- a/packages/tui/src/component/command-palette.tsx
+++ b/packages/tui/src/component/command-palette.tsx
@@ -2,6 +2,7 @@ import { createMemo } from "solid-js"
import { DialogSelect, type DialogSelectRef } from "../ui/dialog-select"
import { type DialogContext } from "../ui/dialog"
import { COMMAND_PALETTE_COMMAND, Keymap, type KeymapCommand } from "../context/keymap"
+import { DialogConfig, settingID, settings } from "./dialog-config"
function isSuggestedPaletteCommand(command: KeymapCommand) {
const suggested = command.suggested
@@ -16,11 +17,14 @@ export function CommandPaletteDialog() {
const options = createMemo(() =>
commands().flatMap((command) => {
if (!command.id || !command.palette || command.id === COMMAND_PALETTE_COMMAND) return []
+ const footer = shortcuts.all(command.id)
return {
title: command.title ?? command.id,
description: command.description,
category: command.group,
- footer: shortcuts.all(command.id),
+ searchText: [command.id, command.description].filter(Boolean).join(" "),
+ searchFooter: [command.group, footer].filter(Boolean).join(" · "),
+ footer,
value: command.id,
suggested: isSuggestedPaletteCommand(command),
onSelect: (dialog: DialogContext) => {
@@ -30,10 +34,20 @@ export function CommandPaletteDialog() {
}
}),
)
+ const settingOptions = settings.map((setting) => ({
+ title: setting.title,
+ category: setting.category,
+ searchText: setting.keywords?.join(" "),
+ searchFooter: `Settings · ${setting.category}`,
+ value: `setting:${settingID(setting)}`,
+ onSelect: (dialog: DialogContext) => {
+ dialog.replace(() => )
+ },
+ }))
let ref: DialogSelectRef
const list = () => {
- if (ref?.filter) return options()
+ if (ref?.filter) return [...options(), ...settingOptions]
return [
...options()
.filter((option) => option.suggested)
@@ -46,5 +60,7 @@ export function CommandPaletteDialog() {
]
}
- return (ref = value)} title="Commands" options={list()} />
+ return (
+ (ref = value)} title="Commands" options={list()} flat={true} filterThreshold={0.7} />
+ )
}
diff --git a/packages/tui/src/component/dialog-config.tsx b/packages/tui/src/component/dialog-config.tsx
index 1ca585ec137..0cf13f88ac8 100644
--- a/packages/tui/src/component/dialog-config.tsx
+++ b/packages/tui/src/component/dialog-config.tsx
@@ -15,14 +15,16 @@ type Setting = {
min?: number
max?: number
format?: (value: unknown) => string
+ keywords?: readonly string[]
}
-const settings: Setting[] = [
+export const settings: Setting[] = [
{
title: "Theme",
category: "Appearance",
path: ["theme", "name"],
default: "opencode",
+ keywords: ["color scheme", "colors"],
},
{
title: "Color mode",
@@ -30,6 +32,7 @@ const settings: Setting[] = [
path: ["theme", "mode"],
default: "system",
values: ["system", "dark", "light"],
+ keywords: ["dark mode", "light mode", "system theme"],
},
{
title: "Animations",
@@ -38,6 +41,7 @@ const settings: Setting[] = [
default: false,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["motion", "effects"],
},
{
title: "Onboarding",
@@ -46,6 +50,7 @@ const settings: Setting[] = [
default: true,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["hints", "getting started", "guidance"],
},
{
title: "Sidebar",
@@ -53,6 +58,7 @@ const settings: Setting[] = [
path: ["session", "sidebar"],
default: "auto",
values: ["hide", "auto"],
+ keywords: ["side panel"],
},
{
title: "Scrollbar",
@@ -61,6 +67,7 @@ const settings: Setting[] = [
default: false,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["scroll bar"],
},
{
title: "Thinking",
@@ -68,6 +75,7 @@ const settings: Setting[] = [
path: ["session", "thinking"],
default: "hide",
values: ["hide", "show"],
+ keywords: ["reasoning", "chain of thought"],
},
{
title: "Markdown",
@@ -75,6 +83,7 @@ const settings: Setting[] = [
path: ["session", "markdown"],
default: "rendered",
values: ["source", "rendered"],
+ keywords: ["syntax", "concealment", "rendering"],
},
{
title: "Grouping",
@@ -82,6 +91,7 @@ const settings: Setting[] = [
path: ["session", "grouping"],
default: "auto",
values: ["none", "auto"],
+ keywords: ["transcript", "messages"],
},
{
title: "Layout",
@@ -89,6 +99,7 @@ const settings: Setting[] = [
path: ["diffs", "view"],
default: "auto",
values: ["auto", "split", "unified"],
+ keywords: ["diff layout", "split diff", "unified diff"],
},
{
title: "Wrapping",
@@ -96,6 +107,7 @@ const settings: Setting[] = [
path: ["diffs", "wrap"],
default: "word",
values: ["none", "word"],
+ keywords: ["diff wrap", "word wrap", "line wrap"],
},
{
title: "File tree",
@@ -104,6 +116,7 @@ const settings: Setting[] = [
default: true,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["diff files"],
},
{
title: "Single patch",
@@ -112,6 +125,7 @@ const settings: Setting[] = [
default: false,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["one file", "selected file"],
},
{
title: "Scroll speed",
@@ -122,6 +136,7 @@ const settings: Setting[] = [
min: 0.25,
max: 10,
format: (value) => Number(value).toFixed(2),
+ keywords: ["scrolling"],
},
{
title: "Acceleration",
@@ -130,6 +145,7 @@ const settings: Setting[] = [
default: false,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["scroll acceleration"],
},
{
title: "Mouse",
@@ -138,6 +154,7 @@ const settings: Setting[] = [
default: true,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["mouse capture"],
},
{
title: "Editor context",
@@ -146,6 +163,7 @@ const settings: Setting[] = [
default: true,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["file context", "prompt context", "editor selection"],
},
{
title: "Large pastes",
@@ -153,6 +171,7 @@ const settings: Setting[] = [
path: ["prompt", "paste"],
default: "compact",
values: ["compact", "full"],
+ keywords: ["paste summary", "clipboard", "pasted content"],
},
{
title: "Leader timeout",
@@ -163,6 +182,7 @@ const settings: Setting[] = [
min: 250,
max: 10000,
format: (value) => `${value} ms`,
+ keywords: ["leader key", "shortcut timeout"],
},
{
title: "Attention",
@@ -171,6 +191,7 @@ const settings: Setting[] = [
default: false,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["alerts"],
},
{
title: "Notifications",
@@ -179,6 +200,7 @@ const settings: Setting[] = [
default: true,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["system notifications", "desktop notifications", "alerts"],
},
{
title: "Sounds",
@@ -187,6 +209,7 @@ const settings: Setting[] = [
default: true,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["audio", "sound effects"],
},
{
title: "Volume",
@@ -197,6 +220,7 @@ const settings: Setting[] = [
min: 0,
max: 1,
format: (value) => `${Math.round(Number(value) * 100)}%`,
+ keywords: ["sound volume", "audio volume"],
},
{
title: "Window title",
@@ -205,6 +229,7 @@ const settings: Setting[] = [
default: true,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["terminal title", "tab title"],
},
{
title: "Copy on select",
@@ -213,6 +238,7 @@ const settings: Setting[] = [
default: process.platform !== "win32",
values: [false, true],
labels: ["off", "on"],
+ keywords: ["selection", "clipboard"],
},
{
title: "DevTools",
@@ -221,6 +247,7 @@ const settings: Setting[] = [
default: false,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["debug bar", "developer tools"],
},
{
title: "Turn token usage",
@@ -229,14 +256,23 @@ const settings: Setting[] = [
default: false,
values: [false, true],
labels: ["off", "on"],
+ keywords: ["tokens", "usage", "debug"],
},
]
-export function DialogConfig() {
+export function settingID(setting: Setting) {
+ return setting.path.join(".")
+}
+
+export function DialogConfig(props: { current?: string }) {
const config = useConfig()
const toast = useToast()
const themeState = useTheme()
- const [selected, setSelected] = createSignal(0)
+ const current = Math.max(
+ 0,
+ settings.findIndex((setting) => settingID(setting) === props.current),
+ )
+ const [selected, setSelected] = createSignal(current)
const [saving, setSaving] = createSignal(false)
const value = (setting: Setting) => {
@@ -261,6 +297,7 @@ export function DialogConfig() {
settings.map((setting, index) => ({
title: setting.title,
category: setting.category,
+ searchText: setting.keywords?.join(" "),
footer: display(setting),
value: index,
})),
@@ -292,6 +329,8 @@ export function DialogConfig() {
setSelected(option.value)}
onSelect={(option) => void change(1, option.value)}
footerHints={[{ title: "←/→", label: "change" }]}
diff --git a/packages/tui/src/ui/dialog-select.tsx b/packages/tui/src/ui/dialog-select.tsx
index 88248c92d34..be97539db59 100644
--- a/packages/tui/src/ui/dialog-select.tsx
+++ b/packages/tui/src/ui/dialog-select.tsx
@@ -22,6 +22,7 @@ export interface DialogSelectProps {
noMatchView?: JSX.Element
options: DialogSelectOption[]
flat?: boolean
+ filterThreshold?: number
ref?: (ref: DialogSelectRef) => void
onMove?: (option: DialogSelectOption) => void
onFilter?: (query: string) => void
@@ -64,6 +65,8 @@ export interface DialogSelectOption {
titleView?: JSX.Element
value: T
description?: string
+ searchText?: string
+ searchFooter?: JSX.Element | string
details?: string[]
detailsColor?: RGBA
detailsWrap?: boolean
@@ -164,12 +167,13 @@ export function DialogSelect(props: DialogSelectProps) {
)
if (!needle) return options
- // prioritize title matches (weight: 2) over category matches (weight: 1).
+ // prioritize title matches (weight: 2) over category and supplemental search text matches (weight: 1).
// users typically search by the item name, and not its category.
const result = fuzzysort
.go(needle, options, {
- keys: ["title", "category"],
- scoreFn: (r) => r[0].score * 2 + r[1].score,
+ keys: ["title", "category", "searchText"],
+ scoreFn: (r) => r[0].score * 2 + r[1].score + r[2].score,
+ threshold: props.filterThreshold,
})
.map((x) => x.obj)
@@ -704,7 +708,9 @@ export function DialogSelect(props: DialogSelectProps) {