فهرست منبع

feat(app): search on settings shortcuts (#9850)

Rahul A Mistry 1 ماه پیش
والد
کامیت
b8a0e420f8

+ 83 - 7
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>
   )

+ 2 - 0
packages/app/src/i18n/da.ts

@@ -466,6 +466,8 @@ export const dict = {
   "settings.shortcuts.conflict.description": "{{keybind}} er allerede tildelt til {{titles}}.",
   "settings.shortcuts.unassigned": "Ikke tildelt",
   "settings.shortcuts.pressKeys": "Tryk på taster",
+  "settings.shortcuts.search.placeholder": "Søg genveje",
+  "settings.shortcuts.search.empty": "Ingen genveje fundet",
 
   "settings.shortcuts.group.general": "Generelt",
   "settings.shortcuts.group.session": "Session",

+ 2 - 0
packages/app/src/i18n/de.ts

@@ -475,6 +475,8 @@ export const dict = {
   "settings.shortcuts.conflict.description": "{{keybind}} ist bereits {{titles}} zugewiesen.",
   "settings.shortcuts.unassigned": "Nicht zugewiesen",
   "settings.shortcuts.pressKeys": "Tasten drücken",
+  "settings.shortcuts.search.placeholder": "Tastenkürzel suchen",
+  "settings.shortcuts.search.empty": "Keine Tastenkürzel gefunden",
 
   "settings.shortcuts.group.general": "Allgemein",
   "settings.shortcuts.group.session": "Sitzung",

+ 2 - 0
packages/app/src/i18n/en.ts

@@ -465,6 +465,8 @@ export const dict = {
   "settings.shortcuts.conflict.description": "{{keybind}} is already assigned to {{titles}}.",
   "settings.shortcuts.unassigned": "Unassigned",
   "settings.shortcuts.pressKeys": "Press keys",
+  "settings.shortcuts.search.placeholder": "Search shortcuts",
+  "settings.shortcuts.search.empty": "No shortcuts found",
 
   "settings.shortcuts.group.general": "General",
   "settings.shortcuts.group.session": "Session",

+ 2 - 0
packages/app/src/i18n/es.ts

@@ -469,6 +469,8 @@ export const dict = {
   "settings.shortcuts.conflict.description": "{{keybind}} ya está asignado a {{titles}}.",
   "settings.shortcuts.unassigned": "Sin asignar",
   "settings.shortcuts.pressKeys": "Presiona teclas",
+  "settings.shortcuts.search.placeholder": "Buscar atajos",
+  "settings.shortcuts.search.empty": "No se encontraron atajos",
 
   "settings.shortcuts.group.general": "General",
   "settings.shortcuts.group.session": "Sesión",

+ 2 - 0
packages/app/src/i18n/fr.ts

@@ -475,6 +475,8 @@ export const dict = {
   "settings.shortcuts.conflict.description": "{{keybind}} est déjà assigné à {{titles}}.",
   "settings.shortcuts.unassigned": "Non assigné",
   "settings.shortcuts.pressKeys": "Appuyez sur les touches",
+  "settings.shortcuts.search.placeholder": "Rechercher des raccourcis",
+  "settings.shortcuts.search.empty": "Aucun raccourci trouvé",
 
   "settings.shortcuts.group.general": "Général",
   "settings.shortcuts.group.session": "Session",

+ 2 - 0
packages/app/src/i18n/ja.ts

@@ -464,6 +464,8 @@ export const dict = {
   "settings.shortcuts.conflict.description": "{{keybind}} は既に {{titles}} に割り当てられています。",
   "settings.shortcuts.unassigned": "未割り当て",
   "settings.shortcuts.pressKeys": "キーを押してください",
+  "settings.shortcuts.search.placeholder": "ショートカットを検索",
+  "settings.shortcuts.search.empty": "ショートカットが見つかりません",
 
   "settings.shortcuts.group.general": "一般",
   "settings.shortcuts.group.session": "セッション",

+ 2 - 0
packages/app/src/i18n/ko.ts

@@ -465,6 +465,8 @@ export const dict = {
   "settings.shortcuts.conflict.description": "{{keybind}}은(는) 이미 {{titles}}에 할당되어 있습니다.",
   "settings.shortcuts.unassigned": "할당되지 않음",
   "settings.shortcuts.pressKeys": "키 누르기",
+  "settings.shortcuts.search.placeholder": "단축키 검색",
+  "settings.shortcuts.search.empty": "단축키를 찾을 수 없습니다",
 
   "settings.shortcuts.group.general": "일반",
   "settings.shortcuts.group.session": "세션",

+ 2 - 0
packages/app/src/i18n/zh.ts

@@ -459,6 +459,8 @@ export const dict = {
   "settings.shortcuts.conflict.description": "{{keybind}} 已分配给 {{titles}}。",
   "settings.shortcuts.unassigned": "未设置",
   "settings.shortcuts.pressKeys": "按下按键",
+  "settings.shortcuts.search.placeholder": "搜索快捷键",
+  "settings.shortcuts.search.empty": "未找到快捷键",
 
   "settings.shortcuts.group.general": "通用",
   "settings.shortcuts.group.session": "会话",