summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/components
diff options
context:
space:
mode:
authorRahul A Mistry <[email protected]>2026-01-21 22:05:15 +0530
committerGitHub <[email protected]>2026-01-21 10:35:15 -0600
commitb8a0e420f8e8c3abb8ceceecb3e511236e2e9735 (patch)
tree47e1a374574b326cc959ffb3d75528b5ef21f25f /packages/app/src/components
parentbfbcbc88632869f1a6b86b05a3fff0c7a46e5863 (diff)
downloadopencode-b8a0e420f8e8c3abb8ceceecb3e511236e2e9735.tar.gz
opencode-b8a0e420f8e8c3abb8ceceecb3e511236e2e9735.zip
feat(app): search on settings shortcuts (#9850)
Diffstat (limited to 'packages/app/src/components')
-rw-r--r--packages/app/src/components/settings-keybinds.tsx90
1 files changed, 83 insertions, 7 deletions
diff --git a/packages/app/src/components/settings-keybinds.tsx b/packages/app/src/components/settings-keybinds.tsx
index 13a0042ff..3f1f34e5f 100644
--- a/packages/app/src/components/settings-keybinds.tsx
+++ b/packages/app/src/components/settings-keybinds.tsx
@@ -1,6 +1,10 @@
import { Component, For, Show, createMemo, createSignal, onCleanup, onMount } from "solid-js"
import { Button } from "@opencode-ai/ui/button"
+import { Icon } from "@opencode-ai/ui/icon"
+import { IconButton } from "@opencode-ai/ui/icon-button"
+import { TextField } from "@opencode-ai/ui/text-field"
import { showToast } from "@opencode-ai/ui/toast"
+import fuzzysort from "fuzzysort"
import { formatKeybind, parseKeybind, useCommand } from "@/context/command"
import { useLanguage } from "@/context/language"
import { useSettings } from "@/context/settings"
@@ -108,6 +112,7 @@ export const SettingsKeybinds: Component = () => {
const settings = useSettings()
const [active, setActive] = createSignal<string | null>(null)
+ const [filter, setFilter] = createSignal("")
const stop = () => {
if (!active()) return
@@ -197,6 +202,45 @@ export const SettingsKeybinds: Component = () => {
return out
})
+ const filtered = createMemo(() => {
+ const query = filter().toLowerCase().trim()
+ if (!query) return grouped()
+
+ const map = list()
+ const out = new Map<KeybindGroup, string[]>()
+
+ for (const group of GROUPS) out.set(group, [])
+
+ const items = Array.from(map.entries()).map(([id, meta]) => ({
+ id,
+ title: meta.title,
+ group: meta.group,
+ keybind: command.keybind(id) || "",
+ }))
+
+ const results = fuzzysort.go(query, items, {
+ keys: ["title", "keybind"],
+ threshold: -10000,
+ })
+
+ for (const result of results) {
+ const item = result.obj
+ const ids = out.get(item.group)
+ if (!ids) continue
+ ids.push(item.id)
+ }
+
+ return out
+ })
+
+ const hasResults = createMemo(() => {
+ for (const group of GROUPS) {
+ const ids = filtered().get(group) ?? []
+ if (ids.length > 0) return true
+ }
+ return false
+ })
+
const used = createMemo(() => {
const map = new Map<string, { id: string; title: string }[]>()
@@ -313,22 +357,43 @@ export const SettingsKeybinds: Component = () => {
"linear-gradient(to bottom, var(--surface-raised-stronger-non-alpha) calc(100% - 24px), transparent)",
}}
>
- <div class="flex items-center justify-between gap-4 pt-6 pb-8 max-w-[720px]">
- <h2 class="text-16-medium text-text-strong">{language.t("settings.shortcuts.title")}</h2>
- <Button size="small" variant="secondary" onClick={resetAll} disabled={!hasOverrides()}>
- {language.t("settings.shortcuts.reset.button")}
- </Button>
+ <div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]">
+ <div class="flex items-center justify-between gap-4">
+ <h2 class="text-16-medium text-text-strong">{language.t("settings.shortcuts.title")}</h2>
+ <Button size="small" variant="secondary" onClick={resetAll} disabled={!hasOverrides()}>
+ {language.t("settings.shortcuts.reset.button")}
+ </Button>
+ </div>
+
+ <div class="flex items-center gap-2 px-3 py-2 rounded-lg bg-surface-base">
+ <Icon name="magnifying-glass" class="text-icon-weak-base flex-shrink-0" />
+ <TextField
+ variant="ghost"
+ type="text"
+ value={filter()}
+ onChange={setFilter}
+ placeholder={language.t("settings.shortcuts.search.placeholder")}
+ spellcheck={false}
+ autocorrect="off"
+ autocomplete="off"
+ autocapitalize="off"
+ class="flex-1"
+ />
+ <Show when={filter()}>
+ <IconButton icon="circle-x" variant="ghost" onClick={() => setFilter("")} />
+ </Show>
+ </div>
</div>
</div>
<div class="flex flex-col gap-8 max-w-[720px]">
<For each={GROUPS}>
{(group) => (
- <Show when={(grouped().get(group) ?? []).length > 0}>
+ <Show when={(filtered().get(group) ?? []).length > 0}>
<div class="flex flex-col gap-1">
<h3 class="text-14-medium text-text-strong pb-2">{language.t(groupKey[group])}</h3>
<div class="bg-surface-raised-base px-4 rounded-lg">
- <For each={grouped().get(group) ?? []}>
+ <For each={filtered().get(group) ?? []}>
{(id) => (
<div class="flex items-center justify-between gap-4 py-3 border-b border-border-weak-base last:border-none">
<span class="text-14-regular text-text-strong">{title(id)}</span>
@@ -357,6 +422,17 @@ export const SettingsKeybinds: Component = () => {
</Show>
)}
</For>
+
+ <Show when={filter() && !hasResults()}>
+ <div class="flex flex-col items-center justify-center py-12 text-center">
+ <span class="text-14-regular text-text-weak">
+ {language.t("settings.shortcuts.search.empty")}
+ </span>
+ <Show when={filter()}>
+ <span class="text-14-regular text-text-strong mt-1">"{filter()}"</span>
+ </Show>
+ </div>
+ </Show>
</div>
</div>
)