mirror of
https://github.com/anomalyco/opencode.git
synced 2026-04-26 07:44:56 +00:00
87 lines
2.6 KiB
TypeScript
87 lines
2.6 KiB
TypeScript
import { createMemo, createSignal } from "solid-js"
|
|
import { useLocal } from "@tui/context/local"
|
|
import { useSync } from "@tui/context/sync"
|
|
import { map, pipe, entries, sortBy } from "remeda"
|
|
import { DialogSelect, type DialogSelectRef, type DialogSelectOption } from "@tui/ui/dialog-select"
|
|
import { useTheme } from "../context/theme"
|
|
import { Keybind } from "@/util/keybind"
|
|
import { TextAttributes } from "@opentui/core"
|
|
import { useSDK } from "@tui/context/sdk"
|
|
|
|
function Status(props: { enabled: boolean; loading: boolean }) {
|
|
const { theme } = useTheme()
|
|
if (props.loading) {
|
|
return <span style={{ fg: theme.textMuted }}>⋯ Loading</span>
|
|
}
|
|
if (props.enabled) {
|
|
return <span style={{ fg: theme.success, attributes: TextAttributes.BOLD }}>✓ Enabled</span>
|
|
}
|
|
return <span style={{ fg: theme.textMuted }}>○ Disabled</span>
|
|
}
|
|
|
|
export function DialogMcp() {
|
|
const local = useLocal()
|
|
const sync = useSync()
|
|
const sdk = useSDK()
|
|
const [, setRef] = createSignal<DialogSelectRef<unknown>>()
|
|
const [loading, setLoading] = createSignal<string | null>(null)
|
|
|
|
const options = createMemo(() => {
|
|
// Track sync data and loading state to trigger re-render when they change
|
|
const mcpData = sync.data.mcp
|
|
const loadingMcp = loading()
|
|
|
|
return pipe(
|
|
mcpData ?? {},
|
|
entries(),
|
|
sortBy(([name]) => name),
|
|
map(([name, status]) => ({
|
|
value: name,
|
|
title: name,
|
|
description: status.status === "failed" ? "failed" : status.status,
|
|
footer: <Status enabled={local.mcp.isEnabled(name)} loading={loadingMcp === name} />,
|
|
category: undefined,
|
|
})),
|
|
)
|
|
})
|
|
|
|
const keybinds = createMemo(() => [
|
|
{
|
|
keybind: Keybind.parse("space")[0],
|
|
title: "toggle",
|
|
onTrigger: async (option: DialogSelectOption<string>) => {
|
|
// Prevent toggling while an operation is already in progress
|
|
if (loading() !== null) return
|
|
|
|
setLoading(option.value)
|
|
try {
|
|
await local.mcp.toggle(option.value)
|
|
// Refresh MCP status from server
|
|
const status = await sdk.client.mcp.status()
|
|
if (status.data) {
|
|
sync.set("mcp", status.data)
|
|
} else {
|
|
console.error("Failed to refresh MCP status: no data returned")
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to toggle MCP:", error)
|
|
} finally {
|
|
setLoading(null)
|
|
}
|
|
},
|
|
},
|
|
])
|
|
|
|
return (
|
|
<DialogSelect
|
|
ref={setRef}
|
|
title="MCPs"
|
|
options={options()}
|
|
keybind={keybinds()}
|
|
onSelect={(option) => {
|
|
// Don't close on select, only on escape
|
|
}}
|
|
/>
|
|
)
|
|
}
|