diff options
| -rw-r--r-- | packages/app/src/components/dialog-select-server.tsx | 44 | ||||
| -rw-r--r-- | packages/ui/src/hooks/use-filtered-list.tsx | 15 |
2 files changed, 42 insertions, 17 deletions
diff --git a/packages/app/src/components/dialog-select-server.tsx b/packages/app/src/components/dialog-select-server.tsx index 7e2bcc181..3e4ee8883 100644 --- a/packages/app/src/components/dialog-select-server.tsx +++ b/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 { TextField } from "@opencode-ai/ui/text-field" import { Button } from "@opencode-ai/ui/button" +import { IconButton } from "@opencode-ai/ui/icon-button" import { normalizeServerUrl, serverDisplayName, useServer } from "@/context/server" import { usePlatform } from "@/context/platform" import { createOpencodeClient } from "@opencode-ai/sdk/v2/client" @@ -116,6 +117,10 @@ export function DialogSelectServer() { select(value, true) } + async function handleRemove(url: string) { + server.remove(url) + } + return ( <Dialog title="Servers" description="Switch which OpenCode server this app connects to."> <div class="flex flex-col gap-4 pb-4"> @@ -130,20 +135,33 @@ export function DialogSelectServer() { }} > {(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 - 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> )} </List> diff --git a/packages/ui/src/hooks/use-filtered-list.tsx b/packages/ui/src/hooks/use-filtered-list.tsx index 1b3be4b4c..11bc35548 100644 --- a/packages/ui/src/hooks/use-filtered-list.tsx +++ b/packages/ui/src/hooks/use-filtered-list.tsx @@ -22,10 +22,17 @@ export function useFilteredList<T>(props: FilteredListProps<T>) { const empty: Group[] = [] 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 }) => { const needle = filter?.toLowerCase() const all = (items ?? (await (props.items as (filter: string) => T[] | Promise<T[]>)(needle))) || [] |
