diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 8cb704bf1d..007626bab0 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -21,7 +21,6 @@ import { focusTerminalById } from "@/pages/session/helpers" import { useSessionLayout } from "@/pages/session/session-layout" import { decode64 } from "@/utils/base64" import { Persist, persisted } from "@/utils/persist" -import { StatusPopover } from "../status-popover" const OPEN_APPS = [ "vscode", @@ -409,9 +408,6 @@ export function SessionHeader() {
- - - - sdk: ReturnType + get: () => + | { store: ReturnType["child"]>[0]; set: (...args: unknown[]) => void } + | undefined + client: () => ReturnType["createClient"]> | undefined language: ReturnType }) => { const [loading, setLoading] = createSignal(null) @@ -139,13 +141,18 @@ const useMcpToggle = (input: { if (loading()) return setLoading(name) + const child = input.get() + const cli = input.client() + if (!child || !cli) { + setLoading(null) + return + } + try { - const status = input.sync.data.mcp[name] - await (status?.status === "connected" - ? input.sdk.client.mcp.disconnect({ name }) - : input.sdk.client.mcp.connect({ name })) - const result = await input.sdk.client.mcp.status() - if (result.data) input.sync.set("mcp", result.data) + const status = child.store.mcp[name] + await (status?.status === "connected" ? cli.mcp.disconnect({ name }) : cli.mcp.connect({ name })) + const result = await cli.mcp.status() + if (result.data) child.set("mcp", result.data) } catch (err) { showToast({ variant: "error", @@ -160,15 +167,26 @@ const useMcpToggle = (input: { return { loading, toggle } } -export function StatusPopover() { - const sync = useSync() - const sdk = useSDK() +export function StatusPopover(props: { directory: string; placement?: "right-end" | "bottom-end" }) { + const globalSDK = useGlobalSDK() + const globalSync = useGlobalSync() const server = useServer() const platform = usePlatform() const dialog = useDialog() const language = useLanguage() const navigate = useNavigate() + const child = createMemo(() => { + if (!props.directory) return + const [store, set] = globalSync.child(props.directory) + return { store, set } + }) + + const client = createMemo(() => { + if (!props.directory) return + return globalSDK.createClient({ directory: props.directory, throwOnError: true }) + }) + const [shown, setShown] = createSignal(false) const servers = createMemo(() => { const current = server.current @@ -179,14 +197,18 @@ export function StatusPopover() { }) const health = useServerHealth(servers) const sortedServers = createMemo(() => listServersByHealth(servers(), server.key, health)) - const mcp = useMcpToggle({ sync, sdk, language }) + const mcp = useMcpToggle({ + language, + get: () => child(), + client: () => client(), + }) const defaultServer = useDefaultServerKey(platform.getDefaultServer) - const mcpNames = createMemo(() => Object.keys(sync.data.mcp ?? {}).sort((a, b) => a.localeCompare(b))) - const mcpStatus = (name: string) => sync.data.mcp?.[name]?.status + const mcpNames = createMemo(() => Object.keys(child()?.store.mcp ?? {}).sort((a, b) => a.localeCompare(b))) + const mcpStatus = (name: string) => child()?.store.mcp?.[name]?.status const mcpConnected = createMemo(() => mcpNames().filter((name) => mcpStatus(name) === "connected").length) - const lspItems = createMemo(() => sync.data.lsp ?? []) + const lspItems = createMemo(() => child()?.store.lsp ?? []) const lspCount = createMemo(() => lspItems().length) - const plugins = createMemo(() => sync.data.config.plugin ?? []) + const plugins = createMemo(() => child()?.store.config.plugin ?? []) const pluginCount = createMemo(() => plugins().length) const pluginEmpty = createMemo(() => pluginEmptyMessage(language.t("dialog.plugins.empty"), "opencode.json")) const overallHealthy = createMemo(() => { @@ -202,21 +224,24 @@ export function StatusPopover() { -
- -
+
+
( layout.sidebar.opened()} aimMove={aim.move} projects={projects} @@ -2199,6 +2200,7 @@ export default function Layout(props: ParentProps) { onOpenSettings={openSettings} helpLabel={() => language.t("sidebar.help")} onOpenHelp={() => platform.openLink("https://opencode.ai/desktop-feedback")} + statusLabel={() => language.t("status.popover.trigger")} renderPanel={() => mobile ? ( diff --git a/packages/app/src/pages/layout/sidebar-shell.tsx b/packages/app/src/pages/layout/sidebar-shell.tsx index ca36af2a42..b3b6d5c114 100644 --- a/packages/app/src/pages/layout/sidebar-shell.tsx +++ b/packages/app/src/pages/layout/sidebar-shell.tsx @@ -11,9 +11,11 @@ import { ConstrainDragXAxis } from "@/utils/solid-dnd" import { IconButton } from "@opencode-ai/ui/icon-button" import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip" import { type LocalProject } from "@/context/layout" +import { StatusPopover } from "@/components/status-popover" export const SidebarContent = (props: { mobile?: boolean + dir: string opened: Accessor aimMove: (event: MouseEvent) => void projects: Accessor @@ -30,6 +32,7 @@ export const SidebarContent = (props: { onOpenSettings: () => void helpLabel: Accessor onOpenHelp: () => void + statusLabel: Accessor renderPanel: () => JSX.Element }): JSX.Element => { const expanded = createMemo(() => !!props.mobile || props.opened()) @@ -90,6 +93,11 @@ export const SidebarContent = (props: {
+ + + + +