Buckets:
| import { Component, createMemo, Show } from "solid-js" | |
| import { useSync } from "@/context/sync" | |
| import { Dialog } from "@opencode-ai/ui/dialog" | |
| import { List } from "@opencode-ai/ui/list" | |
| import { Switch } from "@opencode-ai/ui/switch" | |
| import { useLanguage } from "@/context/language" | |
| import { useMcpToggle } from "@/context/mcp" | |
| const statusLabels = { | |
| connected: "mcp.status.connected", | |
| failed: "mcp.status.failed", | |
| needs_auth: "mcp.status.needs_auth", | |
| needs_client_registration: "mcp.status.needs_client_registration", | |
| disabled: "mcp.status.disabled", | |
| } as const | |
| export const DialogSelectMcp: Component = () => { | |
| const sync = useSync() | |
| const language = useLanguage() | |
| const items = createMemo(() => | |
| Object.entries(sync.data.mcp ?? {}) | |
| .map(([name, status]) => ({ name, status: status.status })) | |
| .sort((a, b) => a.name.localeCompare(b.name)), | |
| ) | |
| const toggle = useMcpToggle() | |
| const enabledCount = createMemo(() => items().filter((i) => i.status === "connected").length) | |
| const totalCount = createMemo(() => items().length) | |
| return ( | |
| <Dialog | |
| title={language.t("dialog.mcp.title")} | |
| description={language.t("dialog.mcp.description", { enabled: enabledCount(), total: totalCount() })} | |
| > | |
| <List | |
| class="px-3" | |
| search={{ placeholder: language.t("common.search.placeholder"), autofocus: true }} | |
| emptyMessage={language.t("dialog.mcp.empty")} | |
| key={(x) => x?.name ?? ""} | |
| items={items} | |
| filterKeys={["name", "status"]} | |
| sortBy={(a, b) => a.name.localeCompare(b.name)} | |
| onSelect={(x) => { | |
| if (!x || toggle.isPending) return | |
| toggle.mutate(x.name) | |
| }} | |
| > | |
| {(i) => { | |
| const mcpStatus = () => sync.data.mcp[i.name] | |
| const status = () => mcpStatus()?.status | |
| const statusLabel = () => { | |
| const key = status() ? statusLabels[status() as keyof typeof statusLabels] : undefined | |
| if (!key) return | |
| return language.t(key) | |
| } | |
| const error = () => { | |
| const s = mcpStatus() | |
| if (s?.status === "failed" || s?.status === "needs_client_registration") return s.error | |
| } | |
| const enabled = () => status() === "connected" | |
| return ( | |
| <div class="w-full flex items-center justify-between gap-x-3"> | |
| <div class="flex flex-col gap-0.5 min-w-0"> | |
| <div class="flex items-center gap-2"> | |
| <span class="truncate">{i.name}</span> | |
| <Show when={statusLabel()}> | |
| <span class="text-11-regular text-text-weaker">{statusLabel()}</span> | |
| </Show> | |
| </div> | |
| <Show when={error()}> | |
| <span class="text-11-regular text-text-weaker truncate">{error()}</span> | |
| </Show> | |
| </div> | |
| <div onClick={(e) => e.stopPropagation()}> | |
| <Switch | |
| checked={enabled()} | |
| disabled={toggle.isPending && toggle.variables === i.name} | |
| onChange={() => { | |
| if (toggle.isPending) return | |
| toggle.mutate(i.name) | |
| }} | |
| /> | |
| </div> | |
| </div> | |
| ) | |
| }} | |
| </List> | |
| </Dialog> | |
| ) | |
| } | |
Xet Storage Details
- Size:
- 3.36 kB
- Xet hash:
- 5582c294b5e9bc58797050941bbef8e1548a57e8730b1bbfc32985eeee92b202
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.