Просмотр исходного кода

feat(dialog-select-server): add icon button for server removal (#8053)

OpeOginni 1 месяц назад
Родитель
Сommit
b3a1360ad9
2 измененных файлов с 42 добавлено и 17 удалено
  1. 31 13
      packages/app/src/components/dialog-select-server.tsx
  2. 11 4
      packages/ui/src/hooks/use-filtered-list.tsx

+ 31 - 13
packages/app/src/components/dialog-select-server.tsx

@@ -5,6 +5,7 @@ import { Dialog } from "@opencode-ai/ui/dialog"
 import { List } from "@opencode-ai/ui/list"
 import { List } from "@opencode-ai/ui/list"
 import { TextField } from "@opencode-ai/ui/text-field"
 import { TextField } from "@opencode-ai/ui/text-field"
 import { Button } from "@opencode-ai/ui/button"
 import { Button } from "@opencode-ai/ui/button"
+import { IconButton } from "@opencode-ai/ui/icon-button"
 import { normalizeServerUrl, serverDisplayName, useServer } from "@/context/server"
 import { normalizeServerUrl, serverDisplayName, useServer } from "@/context/server"
 import { usePlatform } from "@/context/platform"
 import { usePlatform } from "@/context/platform"
 import { createOpencodeClient } from "@opencode-ai/sdk/v2/client"
 import { createOpencodeClient } from "@opencode-ai/sdk/v2/client"
@@ -116,6 +117,10 @@ export function DialogSelectServer() {
     select(value, true)
     select(value, true)
   }
   }
 
 
+  async function handleRemove(url: string) {
+      server.remove(url)
+  }
+
   return (
   return (
     <Dialog title="Servers" description="Switch which OpenCode server this app connects to.">
     <Dialog title="Servers" description="Switch which OpenCode server this app connects to.">
       <div class="flex flex-col gap-4 pb-4">
       <div class="flex flex-col gap-4 pb-4">
@@ -130,20 +135,33 @@ export function DialogSelectServer() {
           }}
           }}
         >
         >
           {(i) => (
           {(i) => (
-            <div
-              class="flex items-center gap-2 min-w-0 flex-1"
-              classList={{ "opacity-50": store.status[i]?.healthy === false }}
-            >
+            <div class="flex items-center gap-2 min-w-0 flex-1 group/item">
               <div
               <div
-                classList={{
-                  "size-1.5 rounded-full shrink-0": true,
-                  "bg-icon-success-base": store.status[i]?.healthy === true,
-                  "bg-icon-critical-base": store.status[i]?.healthy === false,
-                  "bg-border-weak-base": store.status[i] === undefined,
-                }}
-              />
-              <span class="truncate">{serverDisplayName(i)}</span>
-              <span class="text-text-weak">{store.status[i]?.version}</span>
+                class="flex items-center gap-2 min-w-0 flex-1"
+                classList={{ "opacity-50": store.status[i]?.healthy === false }}
+              >
+                <div
+                  classList={{
+                    "size-1.5 rounded-full shrink-0": true,
+                    "bg-icon-success-base": store.status[i]?.healthy === true,
+                    "bg-icon-critical-base": store.status[i]?.healthy === false,
+                    "bg-border-weak-base": store.status[i] === undefined,
+                  }}
+                />
+                <span class="truncate">{serverDisplayName(i)}</span>
+                <span class="text-text-weak">{store.status[i]?.version}</span>
+              </div>
+              <Show when={current() !== i && server.list.includes(i)}>
+                <IconButton
+                  icon="circle-x"
+                  variant="ghost"
+                  class="bg-transparent transition-opacity shrink-0 hover:scale-110"
+                  onClick={(e) => {
+                    e.stopPropagation()
+                    handleRemove(i)
+                  }}
+                />
+              </Show>
             </div>
             </div>
           )}
           )}
         </List>
         </List>

+ 11 - 4
packages/ui/src/hooks/use-filtered-list.tsx

@@ -22,10 +22,17 @@ export function useFilteredList<T>(props: FilteredListProps<T>) {
   const empty: Group[] = []
   const empty: Group[] = []
 
 
   const [grouped, { refetch }] = createResource(
   const [grouped, { refetch }] = createResource(
-    () => ({
-      filter: store.filter,
-      items: typeof props.items === "function" ? undefined : props.items,
-    }),
+    () => {
+      // When items is a function (not async filter function), call it to track changes
+      const itemsValue = typeof props.items === "function" 
+        ? (props.items as () => T[])() // Call synchronous function to track it
+        : props.items
+      
+      return {
+        filter: store.filter,
+        items: itemsValue,
+      }
+    },
     async ({ filter, items }) => {
     async ({ filter, items }) => {
       const needle = filter?.toLowerCase()
       const needle = filter?.toLowerCase()
       const all = (items ?? (await (props.items as (filter: string) => T[] | Promise<T[]>)(needle))) || []
       const all = (items ?? (await (props.items as (filter: string) => T[] | Promise<T[]>)(needle))) || []