mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-10 01:18:48 +00:00
feat(app): replace status menu with tools
This commit is contained in:
parent
518772c2ba
commit
25ee2435f6
24 changed files with 417 additions and 32 deletions
|
|
@ -1,3 +1,4 @@
|
|||
// @refresh reload
|
||||
import { AppIcon } from "@opencode-ai/ui/app-icon"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
|
||||
|
|
@ -235,8 +236,8 @@ export function SessionHeader() {
|
|||
messageAgentColor(params.id ? sync().data.message[params.id] : undefined, sync().data.agent),
|
||||
)
|
||||
const v2ActionsState = createMemo<SessionHeaderV2ActionsState>(() => ({
|
||||
statusVisible: status(),
|
||||
statusLabel: language.t("status.popover.trigger"),
|
||||
toolsVisible: isDesktop(),
|
||||
statusLabel: language.t("status.popover.tools.trigger"),
|
||||
reviewLabel: language.t("command.review.toggle"),
|
||||
reviewKeybind: reviewTooltipKeybind(command),
|
||||
reviewVisible: isDesktop(),
|
||||
|
|
@ -517,7 +518,7 @@ export function SessionHeader() {
|
|||
}
|
||||
|
||||
type SessionHeaderV2ActionsState = {
|
||||
statusVisible: boolean
|
||||
toolsVisible: boolean
|
||||
statusLabel: string
|
||||
reviewLabel: string
|
||||
reviewKeybind: string[]
|
||||
|
|
@ -530,11 +531,11 @@ function SessionHeaderV2Actions(props: { state: SessionHeaderV2ActionsState }) {
|
|||
const language = useLanguage()
|
||||
|
||||
return (
|
||||
<div class="flex items-center gap-2">
|
||||
<Show when={props.state.statusVisible}>
|
||||
<Tooltip placement="bottom" value={props.state.statusLabel}>
|
||||
<div class="flex items-center gap-[6px]">
|
||||
<Show when={props.state.toolsVisible}>
|
||||
<TooltipV2 placement="bottom" value={props.state.statusLabel}>
|
||||
<StatusPopoverV2 />
|
||||
</Tooltip>
|
||||
</TooltipV2>
|
||||
</Show>
|
||||
<Show when={props.state.reviewVisible}>
|
||||
<TooltipV2
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { type ServerHealth } from "@/utils/server-health"
|
|||
import { useGlobal } from "@/context/global"
|
||||
import { useSettings } from "@/context/settings"
|
||||
import { useMcpToggle } from "@/context/mcp"
|
||||
import { ToolsMenu } from "./tools-menu"
|
||||
|
||||
const pluginEmptyMessage = (value: string, file: string): JSXElement => {
|
||||
const parts = value.split(file)
|
||||
|
|
@ -280,7 +281,14 @@ export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
|
|||
const toggleMcp = useMcpToggle()
|
||||
const defaultServer = useDefaultServerKey(platform.getDefaultServer)
|
||||
const mcpNames = createMemo(() => Object.keys(sync().data.mcp ?? {}).sort((a, b) => a.localeCompare(b)))
|
||||
const configuredMcpNames = createMemo(() =>
|
||||
Object.keys(sync().data.config.mcp ?? {}).sort((a, b) => a.localeCompare(b)),
|
||||
)
|
||||
const mcpStatus = (name: string) => sync().data.mcp?.[name]?.status
|
||||
const mcpError = (name: string) => {
|
||||
const status = sync().data.mcp?.[name]
|
||||
if (status?.status === "failed" || status?.status === "needs_client_registration") return status.error
|
||||
}
|
||||
const mcpConnected = createMemo(() => mcpNames().filter((name) => mcpStatus(name) === "connected").length)
|
||||
const lspItems = createMemo(() => sync().data.lsp ?? [])
|
||||
const lspCount = createMemo(() => lspItems().length)
|
||||
|
|
@ -289,6 +297,41 @@ export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
|
|||
)
|
||||
const pluginCount = createMemo(() => plugins().length)
|
||||
const pluginEmpty = createMemo(() => pluginEmptyMessage(language.t("dialog.plugins.empty"), "opencode.json"))
|
||||
if (settings.general.newLayoutDesigns()) {
|
||||
return (
|
||||
<ToolsMenu
|
||||
labels={{
|
||||
menu: language.t("status.popover.tools.trigger"),
|
||||
mcp: language.t("status.popover.tab.mcp"),
|
||||
lsp: language.t("status.popover.tab.lsp"),
|
||||
plugins: language.t("status.popover.tab.plugins"),
|
||||
mcpDescription: language.t("status.popover.tools.mcp.description"),
|
||||
lspDescription: language.t("status.popover.tools.lsp.description"),
|
||||
pluginsDescription: language.t("status.popover.tools.plugins.description"),
|
||||
disabled: language.t("mcp.status.disabled"),
|
||||
failed: language.t("mcp.status.failed"),
|
||||
reauthenticate: language.t("status.popover.tools.reauthenticate"),
|
||||
}}
|
||||
empty={{
|
||||
mcp: language.t("status.popover.tools.mcp.empty"),
|
||||
lsp: language.t("status.popover.tools.lsp.empty"),
|
||||
plugins: language.t("status.popover.tools.plugins.empty"),
|
||||
}}
|
||||
mcp={configuredMcpNames().map((name) => ({
|
||||
name,
|
||||
status: mcpStatus(name),
|
||||
error: mcpError(name),
|
||||
pending: !mcpStatus(name) || (toggleMcp.isPending && toggleMcp.variables === name),
|
||||
onToggle: () => {
|
||||
if (toggleMcp.isPending) return
|
||||
toggleMcp.mutate(name)
|
||||
},
|
||||
}))}
|
||||
lsp={lspItems().map((item) => ({ name: item.name || item.id, status: item.status }))}
|
||||
plugins={plugins()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="flex items-center gap-1 w-[360px] rounded-xl shadow-[var(--shadow-lg-border-base)]">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// @refresh reload
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
|
||||
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
|
||||
import { Popover } from "@opencode-ai/ui/popover"
|
||||
import { Suspense, createMemo, createSignal, lazy, Show, type JSX } from "solid-js"
|
||||
import { useLanguage } from "@/context/language"
|
||||
|
|
@ -9,6 +9,7 @@ import { ServerConnection, useServer } from "@/context/server"
|
|||
import { useServerSDK } from "@/context/server-sdk"
|
||||
import { useSync } from "@/context/sync"
|
||||
import { useGlobal } from "@/context/global"
|
||||
import { ToolsMenuIcon } from "./tools-menu"
|
||||
|
||||
const Body = lazy(() => import("./status-popover-body").then((x) => ({ default: x.StatusPopoverBody })))
|
||||
const ServerBody = lazy(() => import("./status-popover-body").then((x) => ({ default: x.StatusPopoverServerBody })))
|
||||
|
|
@ -89,7 +90,10 @@ function DirectoryStatusPopover() {
|
|||
const serverHealth = () => global.servers.health[ServerConnection.key(server().server)]?.healthy
|
||||
const ready = createMemo(() => serverHealth() === false || sync().data.mcp_ready)
|
||||
const mcpIssue = createMemo(() => {
|
||||
const mcp = Object.values(sync().data.mcp ?? {})
|
||||
const mcp = Object.keys(sync().data.config.mcp ?? {}).flatMap((name) => {
|
||||
const status = sync().data.mcp?.[name]
|
||||
return status ? [status] : []
|
||||
})
|
||||
const failed = mcp.some((item) => item.status === "failed" || item.status === "needs_client_registration")
|
||||
const warn = mcp.some((item) => item.status === "needs_auth")
|
||||
if (failed) return "critical" as const
|
||||
|
|
@ -102,7 +106,7 @@ function DirectoryStatusPopover() {
|
|||
healthy: healthy(),
|
||||
serverHealth: serverHealth(),
|
||||
issue: mcpIssue(),
|
||||
label: language.t("status.popover.trigger"),
|
||||
label: language.t("status.popover.tools.trigger"),
|
||||
onOpenChange: setShown,
|
||||
body: () => (
|
||||
<StatusPopoverBody shown={shown()}>
|
||||
|
|
@ -161,15 +165,7 @@ function StatusPopoverBody(props: { shown: boolean; children: JSX.Element }) {
|
|||
}
|
||||
|
||||
function StatusPopoverView(props: { state: StatusPopoverState }) {
|
||||
const statusDotClass = () => ({
|
||||
"absolute rounded-full": true,
|
||||
"bg-icon-success-base": props.state.ready && props.state.healthy,
|
||||
"bg-icon-warning-base": props.state.ready && props.state.serverHealth === true && props.state.issue === "warning",
|
||||
"bg-icon-critical-base":
|
||||
props.state.serverHealth === false ||
|
||||
(props.state.ready && props.state.serverHealth === true && props.state.issue === "critical"),
|
||||
"bg-border-weak-base": props.state.serverHealth === undefined || !props.state.ready,
|
||||
})
|
||||
const warning = () => props.state.serverHealth === false || props.state.issue !== undefined
|
||||
|
||||
const popoverProps = {
|
||||
class:
|
||||
|
|
@ -191,15 +187,7 @@ function StatusPopoverView(props: { state: StatusPopoverState }) {
|
|||
state: props.state.shown ? "pressed" : undefined,
|
||||
"aria-label": props.state.label,
|
||||
}}
|
||||
trigger={
|
||||
<div class="relative size-4">
|
||||
<IconV2 name={props.state.shown ? "status-active" : "status"} />
|
||||
<div
|
||||
classList={statusDotClass()}
|
||||
class="-top-1 -right-1 size-2 border border-[var(--v2-background-bg-deep)]"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
trigger={<ToolsMenuIcon warning={warning()} />}
|
||||
{...popoverProps}
|
||||
>
|
||||
{props.state.body()}
|
||||
|
|
|
|||
200
packages/app/src/components/tools-menu.tsx
Normal file
200
packages/app/src/components/tools-menu.tsx
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2"
|
||||
import { Icon } from "@opencode-ai/ui/v2/icon"
|
||||
import { SegmentedControlItemV2, SegmentedControlV2 } from "@opencode-ai/ui/v2/segmented-control-v2"
|
||||
import { Switch } from "@opencode-ai/ui/v2/switch-v2"
|
||||
import { For, Show, type JSXElement } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
|
||||
export type ToolsMenuTab = "mcp" | "lsp" | "plugins"
|
||||
export type ToolsMenuStatus = "connected" | "failed" | "error" | "needs_auth" | "needs_client_registration" | "disabled"
|
||||
|
||||
export type ToolsMenuProps = {
|
||||
defaultTab?: ToolsMenuTab
|
||||
labels: {
|
||||
menu: string
|
||||
mcp: string
|
||||
lsp: string
|
||||
plugins: string
|
||||
mcpDescription: string
|
||||
lspDescription: string
|
||||
pluginsDescription: string
|
||||
disabled: string
|
||||
failed: string
|
||||
reauthenticate: string
|
||||
}
|
||||
empty: {
|
||||
mcp: JSXElement
|
||||
lsp: JSXElement
|
||||
plugins: JSXElement
|
||||
}
|
||||
mcp: Array<{
|
||||
name: string
|
||||
status?: ToolsMenuStatus
|
||||
error?: string
|
||||
pending?: boolean
|
||||
onToggle?: () => void
|
||||
}>
|
||||
lsp: Array<{
|
||||
name: string
|
||||
status: "connected" | "error"
|
||||
}>
|
||||
plugins: string[]
|
||||
}
|
||||
|
||||
export function ToolsMenuIcon(props: { warning?: boolean }) {
|
||||
return (
|
||||
<div class="relative size-4 text-v2-icon-icon-muted">
|
||||
<Icon name={props.warning ? "tools-warning" : "tools"} />
|
||||
<Show when={props.warning}>
|
||||
<div class="absolute right-0 top-0 size-[5px] rounded-full bg-icon-warning-base" />
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function ToolsMenu(props: ToolsMenuProps) {
|
||||
const [state, setState] = createStore({ tab: props.defaultTab ?? ("mcp" as ToolsMenuTab) })
|
||||
|
||||
return (
|
||||
<div class="w-[360px] max-w-[calc(100vw-40px)] rounded-xl bg-v2-background-bg-base p-4 shadow-[var(--v2-elevation-floating)]">
|
||||
<SegmentedControlV2
|
||||
value={state.tab}
|
||||
onChange={(value) => value && setState("tab", value as ToolsMenuTab)}
|
||||
class="segmented-control-v2--full-width"
|
||||
aria-label={props.labels.menu}
|
||||
>
|
||||
<SegmentedControlItemV2 value="mcp">{props.labels.mcp}</SegmentedControlItemV2>
|
||||
<SegmentedControlItemV2 value="lsp">{props.labels.lsp}</SegmentedControlItemV2>
|
||||
<SegmentedControlItemV2 value="plugins">{props.labels.plugins}</SegmentedControlItemV2>
|
||||
</SegmentedControlV2>
|
||||
|
||||
<Show when={state.tab === "mcp"}>
|
||||
<Show
|
||||
when={props.mcp.length > 0}
|
||||
fallback={<ToolsEmpty title={props.empty.mcp} description={props.labels.mcpDescription} />}
|
||||
>
|
||||
<ToolsList description={props.labels.mcpDescription}>
|
||||
<For each={props.mcp}>
|
||||
{(item) => (
|
||||
<div class="flex h-8 items-center gap-2 px-2">
|
||||
<StatusDot status={item.status} />
|
||||
<span class="min-w-0 flex-1 truncate text-[13px] font-[440] leading-4 tracking-[-0.04px] text-v2-text-text-base">
|
||||
{item.name}
|
||||
</span>
|
||||
<Show
|
||||
when={item.status === "needs_auth"}
|
||||
fallback={
|
||||
<>
|
||||
<Show when={item.status === "needs_client_registration"}>
|
||||
<span
|
||||
class="max-w-40 truncate text-[11px] font-[440] leading-4 tracking-[0.05px] text-v2-text-text-faint"
|
||||
title={item.error}
|
||||
>
|
||||
{item.error ?? props.labels.failed}
|
||||
</span>
|
||||
</Show>
|
||||
<Show when={item.status !== "needs_client_registration"}>
|
||||
<Show when={item.status === "disabled" || item.status === "failed"}>
|
||||
<span class="text-[11px] font-[440] capitalize leading-4 tracking-[0.05px] text-v2-text-text-faint">
|
||||
{item.status === "disabled" ? props.labels.disabled : props.labels.failed}
|
||||
</span>
|
||||
</Show>
|
||||
<Switch
|
||||
checked={item.status === "connected"}
|
||||
disabled={item.pending || !item.status}
|
||||
hideLabel
|
||||
onChange={item.onToggle}
|
||||
>
|
||||
{item.name}
|
||||
</Switch>
|
||||
</Show>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<ButtonV2 size="small" variant="outline" disabled={item.pending} onClick={item.onToggle}>
|
||||
{props.labels.reauthenticate}
|
||||
</ButtonV2>
|
||||
</Show>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</ToolsList>
|
||||
</Show>
|
||||
</Show>
|
||||
|
||||
<Show when={state.tab === "lsp"}>
|
||||
<Show
|
||||
when={props.lsp.length > 0}
|
||||
fallback={<ToolsEmpty title={props.empty.lsp} description={props.labels.lspDescription} />}
|
||||
>
|
||||
<ToolsList description={props.labels.lspDescription}>
|
||||
<For each={props.lsp}>
|
||||
{(item) => (
|
||||
<div class="flex h-8 items-center gap-2 px-2">
|
||||
<StatusDot status={item.status} />
|
||||
<span class="min-w-0 flex-1 truncate text-[13px] font-[440] leading-4 tracking-[-0.04px] text-v2-text-text-base">
|
||||
{item.name}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</ToolsList>
|
||||
</Show>
|
||||
</Show>
|
||||
|
||||
<Show when={state.tab === "plugins"}>
|
||||
<Show
|
||||
when={props.plugins.length > 0}
|
||||
fallback={<ToolsEmpty title={props.empty.plugins} description={props.labels.pluginsDescription} />}
|
||||
>
|
||||
<ToolsList description={props.labels.pluginsDescription}>
|
||||
<For each={props.plugins}>
|
||||
{(plugin) => (
|
||||
<div class="flex h-8 items-center gap-2 px-2">
|
||||
<StatusDot status="connected" />
|
||||
<span class="min-w-0 flex-1 truncate text-[13px] font-[440] leading-4 tracking-[-0.04px] text-v2-text-text-base">
|
||||
{plugin}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</ToolsList>
|
||||
</Show>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ToolsList(props: { description: string; children: JSXElement }) {
|
||||
return (
|
||||
<div class="mt-2 flex flex-col">
|
||||
<div class="flex h-8 items-center px-2 text-[11px] font-[440] leading-4 tracking-[0.05px] text-v2-text-text-faint">
|
||||
{props.description}
|
||||
</div>
|
||||
{props.children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ToolsEmpty(props: { title: JSXElement; description: string }) {
|
||||
return (
|
||||
<div class="mt-4 flex flex-col gap-2 text-center text-[13px] leading-4 tracking-[-0.04px]">
|
||||
<div class="h-4 font-[500] text-v2-text-text-muted">{props.title}</div>
|
||||
<div class="h-4 font-[440] text-v2-text-text-faint">{props.description}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function StatusDot(props: { status?: ToolsMenuStatus }) {
|
||||
return (
|
||||
<div
|
||||
classList={{
|
||||
"size-1.5 shrink-0 rounded-full": true,
|
||||
"bg-v2-state-fg-success": props.status === "connected",
|
||||
"bg-v2-state-fg-danger": props.status === "failed" || props.status === "error",
|
||||
"bg-v2-state-fg-warning": props.status === "needs_auth" || props.status === "needs_client_registration",
|
||||
"bg-v2-border-border-base": props.status === "disabled",
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -621,6 +621,14 @@ export const dict = {
|
|||
"session.header.open.menu": "خيارات الفتح",
|
||||
"session.header.open.copyPath": "نسخ المسار",
|
||||
"status.popover.trigger": "الحالة",
|
||||
"status.popover.tools.trigger": "الأدوات",
|
||||
"status.popover.tools.mcp.description": "أضف خوادم MCP في opencode.json",
|
||||
"status.popover.tools.lsp.description": "يتم اكتشاف خوادم LSP تلقائيًا عند فتح الملفات",
|
||||
"status.popover.tools.plugins.description": "أضف الإضافات في opencode.json",
|
||||
"status.popover.tools.mcp.empty": "لم يتم إعداد أي خوادم MCP بعد",
|
||||
"status.popover.tools.lsp.empty": "لا توجد خوادم لغة نشطة بعد",
|
||||
"status.popover.tools.plugins.empty": "لم يتم إعداد أي إضافات بعد",
|
||||
"status.popover.tools.reauthenticate": "إعادة المصادقة",
|
||||
"status.popover.ariaLabel": "إعدادات الخوادم",
|
||||
"status.popover.tab.servers": "الخوادم",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -627,6 +627,14 @@ export const dict = {
|
|||
"session.header.open.menu": "Opções de abertura",
|
||||
"session.header.open.copyPath": "Copiar caminho",
|
||||
"status.popover.trigger": "Status",
|
||||
"status.popover.tools.trigger": "Ferramentas",
|
||||
"status.popover.tools.mcp.description": "Adicione servidores MCP no opencode.json",
|
||||
"status.popover.tools.lsp.description": "Os LSPs são detectados automaticamente ao abrir arquivos",
|
||||
"status.popover.tools.plugins.description": "Adicione plugins no opencode.json",
|
||||
"status.popover.tools.mcp.empty": "Nenhum MCP configurado ainda",
|
||||
"status.popover.tools.lsp.empty": "Nenhum servidor de linguagem ativo ainda",
|
||||
"status.popover.tools.plugins.empty": "Nenhum plugin configurado ainda",
|
||||
"status.popover.tools.reauthenticate": "Autenticar novamente",
|
||||
"status.popover.ariaLabel": "Configurações de servidores",
|
||||
"status.popover.tab.servers": "Servidores",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -685,6 +685,14 @@ export const dict = {
|
|||
"session.header.open.copyPath": "Kopiraj putanju",
|
||||
|
||||
"status.popover.trigger": "Status",
|
||||
"status.popover.tools.trigger": "Alati",
|
||||
"status.popover.tools.mcp.description": "Dodajte MCP servere u opencode.json",
|
||||
"status.popover.tools.lsp.description": "LSP serveri se automatski otkrivaju dok otvarate datoteke",
|
||||
"status.popover.tools.plugins.description": "Dodajte dodatke u opencode.json",
|
||||
"status.popover.tools.mcp.empty": "Još nema konfigurisanih MCP servera",
|
||||
"status.popover.tools.lsp.empty": "Još nema aktivnih jezičkih servera",
|
||||
"status.popover.tools.plugins.empty": "Još nema konfigurisanih dodataka",
|
||||
"status.popover.tools.reauthenticate": "Ponovo se autentificiraj",
|
||||
"status.popover.ariaLabel": "Konfiguracije servera",
|
||||
"status.popover.tab.servers": "Serveri",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -680,6 +680,14 @@ export const dict = {
|
|||
"session.header.open.copyPath": "Kopier sti",
|
||||
|
||||
"status.popover.trigger": "Status",
|
||||
"status.popover.tools.trigger": "Værktøjer",
|
||||
"status.popover.tools.mcp.description": "Tilføj MCP-servere i opencode.json",
|
||||
"status.popover.tools.lsp.description": "LSP'er registreres automatisk, når du åbner filer",
|
||||
"status.popover.tools.plugins.description": "Tilføj plugins i opencode.json",
|
||||
"status.popover.tools.mcp.empty": "Ingen MCP'er konfigureret endnu",
|
||||
"status.popover.tools.lsp.empty": "Ingen aktive sprogservere endnu",
|
||||
"status.popover.tools.plugins.empty": "Ingen plugins konfigureret endnu",
|
||||
"status.popover.tools.reauthenticate": "Godkend igen",
|
||||
"status.popover.ariaLabel": "Serverkonfigurationer",
|
||||
"status.popover.tab.servers": "Servere",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -636,6 +636,14 @@ export const dict = {
|
|||
"session.header.open.menu": "Öffnen-Optionen",
|
||||
"session.header.open.copyPath": "Pfad kopieren",
|
||||
"status.popover.trigger": "Status",
|
||||
"status.popover.tools.trigger": "Werkzeuge",
|
||||
"status.popover.tools.mcp.description": "MCP-Server in opencode.json hinzufügen",
|
||||
"status.popover.tools.lsp.description": "LSPs werden beim Öffnen von Dateien automatisch erkannt",
|
||||
"status.popover.tools.plugins.description": "Plugins in opencode.json hinzufügen",
|
||||
"status.popover.tools.mcp.empty": "Noch keine MCPs konfiguriert",
|
||||
"status.popover.tools.lsp.empty": "Noch keine aktiven Sprachserver",
|
||||
"status.popover.tools.plugins.empty": "Noch keine Plugins konfiguriert",
|
||||
"status.popover.tools.reauthenticate": "Erneut authentifizieren",
|
||||
"status.popover.ariaLabel": "Serverkonfigurationen",
|
||||
"status.popover.tab.servers": "Server",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -728,6 +728,14 @@ export const dict = {
|
|||
|
||||
"status.popover.trigger": "Status",
|
||||
"status.popover.ariaLabel": "Server configurations",
|
||||
"status.popover.tools.trigger": "Tools",
|
||||
"status.popover.tools.mcp.description": "Add MCP servers in opencode.json",
|
||||
"status.popover.tools.lsp.description": "LSPs are auto-detected as you open files",
|
||||
"status.popover.tools.plugins.description": "Add plugins in opencode.json",
|
||||
"status.popover.tools.mcp.empty": "No MCPs configured yet",
|
||||
"status.popover.tools.lsp.empty": "No active language servers yet",
|
||||
"status.popover.tools.plugins.empty": "No plugins configured yet",
|
||||
"status.popover.tools.reauthenticate": "Reauthenticate",
|
||||
"status.popover.tab.servers": "Servers",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
"status.popover.tab.lsp": "LSP",
|
||||
|
|
|
|||
|
|
@ -686,6 +686,14 @@ export const dict = {
|
|||
"session.header.open.copyPath": "Copiar ruta",
|
||||
|
||||
"status.popover.trigger": "Estado",
|
||||
"status.popover.tools.trigger": "Herramientas",
|
||||
"status.popover.tools.mcp.description": "Añade servidores MCP en opencode.json",
|
||||
"status.popover.tools.lsp.description": "Los LSP se detectan automáticamente al abrir archivos",
|
||||
"status.popover.tools.plugins.description": "Añade plugins en opencode.json",
|
||||
"status.popover.tools.mcp.empty": "Aún no hay MCP configurados",
|
||||
"status.popover.tools.lsp.empty": "Aún no hay servidores de lenguaje activos",
|
||||
"status.popover.tools.plugins.empty": "Aún no hay plugins configurados",
|
||||
"status.popover.tools.reauthenticate": "Volver a autenticar",
|
||||
"status.popover.ariaLabel": "Configuraciones del servidor",
|
||||
"status.popover.tab.servers": "Servidores",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -632,6 +632,14 @@ export const dict = {
|
|||
"session.header.open.menu": "Options d'ouverture",
|
||||
"session.header.open.copyPath": "Copier le chemin",
|
||||
"status.popover.trigger": "Statut",
|
||||
"status.popover.tools.trigger": "Outils",
|
||||
"status.popover.tools.mcp.description": "Ajouter des serveurs MCP dans opencode.json",
|
||||
"status.popover.tools.lsp.description": "Les LSP sont détectés automatiquement à l’ouverture des fichiers",
|
||||
"status.popover.tools.plugins.description": "Ajouter des plugins dans opencode.json",
|
||||
"status.popover.tools.mcp.empty": "Aucun MCP configuré pour le moment",
|
||||
"status.popover.tools.lsp.empty": "Aucun serveur de langage actif pour le moment",
|
||||
"status.popover.tools.plugins.empty": "Aucun plugin configuré pour le moment",
|
||||
"status.popover.tools.reauthenticate": "S’authentifier à nouveau",
|
||||
"status.popover.ariaLabel": "Configurations des serveurs",
|
||||
"status.popover.tab.servers": "Serveurs",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -623,6 +623,14 @@ export const dict = {
|
|||
"session.header.open.menu": "開くオプション",
|
||||
"session.header.open.copyPath": "パスをコピー",
|
||||
"status.popover.trigger": "ステータス",
|
||||
"status.popover.tools.trigger": "ツール",
|
||||
"status.popover.tools.mcp.description": "opencode.json に MCP サーバーを追加",
|
||||
"status.popover.tools.lsp.description": "ファイルを開くと LSP が自動検出されます",
|
||||
"status.popover.tools.plugins.description": "opencode.json にプラグインを追加",
|
||||
"status.popover.tools.mcp.empty": "MCP はまだ設定されていません",
|
||||
"status.popover.tools.lsp.empty": "アクティブな言語サーバーはまだありません",
|
||||
"status.popover.tools.plugins.empty": "プラグインはまだ設定されていません",
|
||||
"status.popover.tools.reauthenticate": "再認証",
|
||||
"status.popover.ariaLabel": "サーバー設定",
|
||||
"status.popover.tab.servers": "サーバー",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -503,6 +503,14 @@ export const dict = {
|
|||
"session.header.open.menu": "열기 옵션",
|
||||
"session.header.open.copyPath": "경로 복사",
|
||||
"status.popover.trigger": "상태",
|
||||
"status.popover.tools.trigger": "도구",
|
||||
"status.popover.tools.mcp.description": "opencode.json에 MCP 서버 추가",
|
||||
"status.popover.tools.lsp.description": "파일을 열면 LSP가 자동으로 감지됩니다",
|
||||
"status.popover.tools.plugins.description": "opencode.json에 플러그인 추가",
|
||||
"status.popover.tools.mcp.empty": "아직 구성된 MCP가 없습니다",
|
||||
"status.popover.tools.lsp.empty": "아직 활성 언어 서버가 없습니다",
|
||||
"status.popover.tools.plugins.empty": "아직 구성된 플러그인이 없습니다",
|
||||
"status.popover.tools.reauthenticate": "다시 인증",
|
||||
"status.popover.ariaLabel": "서버 구성",
|
||||
"status.popover.tab.servers": "서버",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -567,6 +567,14 @@ export const dict = {
|
|||
"session.header.open.copyPath": "Kopier bane",
|
||||
|
||||
"status.popover.trigger": "Status",
|
||||
"status.popover.tools.trigger": "Verktøy",
|
||||
"status.popover.tools.mcp.description": "Legg til MCP-servere i opencode.json",
|
||||
"status.popover.tools.lsp.description": "LSP-er oppdages automatisk når du åpner filer",
|
||||
"status.popover.tools.plugins.description": "Legg til plugins i opencode.json",
|
||||
"status.popover.tools.mcp.empty": "Ingen MCP-er konfigurert ennå",
|
||||
"status.popover.tools.lsp.empty": "Ingen aktive språkservere ennå",
|
||||
"status.popover.tools.plugins.empty": "Ingen plugins konfigurert ennå",
|
||||
"status.popover.tools.reauthenticate": "Autentiser på nytt",
|
||||
"status.popover.ariaLabel": "Serverkonfigurasjoner",
|
||||
"status.popover.tab.servers": "Servere",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -627,6 +627,14 @@ export const dict = {
|
|||
"session.header.open.menu": "Opcje otwierania",
|
||||
"session.header.open.copyPath": "Kopiuj ścieżkę",
|
||||
"status.popover.trigger": "Status",
|
||||
"status.popover.tools.trigger": "Narzędzia",
|
||||
"status.popover.tools.mcp.description": "Dodaj serwery MCP w opencode.json",
|
||||
"status.popover.tools.lsp.description": "LSP są wykrywane automatycznie podczas otwierania plików",
|
||||
"status.popover.tools.plugins.description": "Dodaj wtyczki w opencode.json",
|
||||
"status.popover.tools.mcp.empty": "Nie skonfigurowano jeszcze MCP",
|
||||
"status.popover.tools.lsp.empty": "Brak aktywnych serwerów językowych",
|
||||
"status.popover.tools.plugins.empty": "Nie skonfigurowano jeszcze wtyczek",
|
||||
"status.popover.tools.reauthenticate": "Uwierzytelnij ponownie",
|
||||
"status.popover.ariaLabel": "Konfiguracje serwerów",
|
||||
"status.popover.tab.servers": "Serwery",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -683,6 +683,14 @@ export const dict = {
|
|||
"session.header.open.copyPath": "Копировать путь",
|
||||
|
||||
"status.popover.trigger": "Статус",
|
||||
"status.popover.tools.trigger": "Инструменты",
|
||||
"status.popover.tools.mcp.description": "Добавьте серверы MCP в opencode.json",
|
||||
"status.popover.tools.lsp.description": "LSP определяются автоматически при открытии файлов",
|
||||
"status.popover.tools.plugins.description": "Добавьте плагины в opencode.json",
|
||||
"status.popover.tools.mcp.empty": "MCP пока не настроены",
|
||||
"status.popover.tools.lsp.empty": "Активных языковых серверов пока нет",
|
||||
"status.popover.tools.plugins.empty": "Плагины пока не настроены",
|
||||
"status.popover.tools.reauthenticate": "Повторно авторизоваться",
|
||||
"status.popover.ariaLabel": "Настройки серверов",
|
||||
"status.popover.tab.servers": "Серверы",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -679,6 +679,14 @@ export const dict = {
|
|||
"session.header.open.copyPath": "คัดลอกเส้นทาง",
|
||||
|
||||
"status.popover.trigger": "สถานะ",
|
||||
"status.popover.tools.trigger": "เครื่องมือ",
|
||||
"status.popover.tools.mcp.description": "เพิ่มเซิร์ฟเวอร์ MCP ใน opencode.json",
|
||||
"status.popover.tools.lsp.description": "ระบบจะตรวจพบ LSP อัตโนมัติเมื่อคุณเปิดไฟล์",
|
||||
"status.popover.tools.plugins.description": "เพิ่มปลั๊กอินใน opencode.json",
|
||||
"status.popover.tools.mcp.empty": "ยังไม่ได้กำหนดค่า MCP",
|
||||
"status.popover.tools.lsp.empty": "ยังไม่มีเซิร์ฟเวอร์ภาษาที่ใช้งานอยู่",
|
||||
"status.popover.tools.plugins.empty": "ยังไม่ได้กำหนดค่าปลั๊กอิน",
|
||||
"status.popover.tools.reauthenticate": "ยืนยันตัวตนอีกครั้ง",
|
||||
"status.popover.ariaLabel": "การกำหนดค่าเซิร์ฟเวอร์",
|
||||
"status.popover.tab.servers": "เซิร์ฟเวอร์",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -689,6 +689,14 @@ export const dict = {
|
|||
"session.header.open.copyPath": "Yolu kopyala",
|
||||
|
||||
"status.popover.trigger": "Durum",
|
||||
"status.popover.tools.trigger": "Araçlar",
|
||||
"status.popover.tools.mcp.description": "opencode.json dosyasına MCP sunucuları ekleyin",
|
||||
"status.popover.tools.lsp.description": "Dosyaları açtığınızda LSP'ler otomatik olarak algılanır",
|
||||
"status.popover.tools.plugins.description": "opencode.json dosyasına eklentiler ekleyin",
|
||||
"status.popover.tools.mcp.empty": "Henüz yapılandırılmış MCP yok",
|
||||
"status.popover.tools.lsp.empty": "Henüz etkin dil sunucusu yok",
|
||||
"status.popover.tools.plugins.empty": "Henüz yapılandırılmış eklenti yok",
|
||||
"status.popover.tools.reauthenticate": "Yeniden kimlik doğrula",
|
||||
"status.popover.ariaLabel": "Sunucu yapılandırmaları",
|
||||
"status.popover.tab.servers": "Sunucular",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -729,6 +729,14 @@ export const dict = {
|
|||
"session.header.open.app.sublimeText": "Sublime Text",
|
||||
|
||||
"status.popover.trigger": "Статус",
|
||||
"status.popover.tools.trigger": "Інструменти",
|
||||
"status.popover.tools.mcp.description": "Додайте сервери MCP в opencode.json",
|
||||
"status.popover.tools.lsp.description": "LSP визначаються автоматично під час відкриття файлів",
|
||||
"status.popover.tools.plugins.description": "Додайте плагіни в opencode.json",
|
||||
"status.popover.tools.mcp.empty": "MCP ще не налаштовано",
|
||||
"status.popover.tools.lsp.empty": "Активних мовних серверів ще немає",
|
||||
"status.popover.tools.plugins.empty": "Плагіни ще не налаштовано",
|
||||
"status.popover.tools.reauthenticate": "Повторно автентифікуватися",
|
||||
"status.popover.ariaLabel": "Конфігурації серверів",
|
||||
"status.popover.tab.servers": "Сервери",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -675,6 +675,14 @@ export const dict = {
|
|||
"session.header.open.copyPath": "复制路径",
|
||||
|
||||
"status.popover.trigger": "状态",
|
||||
"status.popover.tools.trigger": "工具",
|
||||
"status.popover.tools.mcp.description": "在 opencode.json 中添加 MCP 服务器",
|
||||
"status.popover.tools.lsp.description": "打开文件时会自动检测 LSP",
|
||||
"status.popover.tools.plugins.description": "在 opencode.json 中添加插件",
|
||||
"status.popover.tools.mcp.empty": "尚未配置 MCP",
|
||||
"status.popover.tools.lsp.empty": "尚无活动的语言服务器",
|
||||
"status.popover.tools.plugins.empty": "尚未配置插件",
|
||||
"status.popover.tools.reauthenticate": "重新认证",
|
||||
"status.popover.ariaLabel": "服务器配置",
|
||||
"status.popover.tab.servers": "服务器",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -673,6 +673,14 @@ export const dict = {
|
|||
"session.header.open.copyPath": "複製路徑",
|
||||
|
||||
"status.popover.trigger": "狀態",
|
||||
"status.popover.tools.trigger": "工具",
|
||||
"status.popover.tools.mcp.description": "在 opencode.json 中新增 MCP 伺服器",
|
||||
"status.popover.tools.lsp.description": "開啟檔案時會自動偵測 LSP",
|
||||
"status.popover.tools.plugins.description": "在 opencode.json 中新增外掛程式",
|
||||
"status.popover.tools.mcp.empty": "尚未設定 MCP",
|
||||
"status.popover.tools.lsp.empty": "尚無作用中的語言伺服器",
|
||||
"status.popover.tools.plugins.empty": "尚未設定外掛程式",
|
||||
"status.popover.tools.reauthenticate": "重新驗證",
|
||||
"status.popover.ariaLabel": "伺服器設定",
|
||||
"status.popover.tab.servers": "伺服器",
|
||||
"status.popover.tab.mcp": "MCP",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Show, createEffect, createMemo, createResource, createSignal, onCleanup
|
|||
import { createStore } from "solid-js/store"
|
||||
import { Portal } from "solid-js/web"
|
||||
import { useSearchParams } from "@solidjs/router"
|
||||
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
||||
import { createMediaQuery } from "@solid-primitives/media"
|
||||
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
|
||||
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
|
||||
import { NewSessionDesignView } from "@/components/session"
|
||||
|
|
@ -51,6 +51,7 @@ export default function NewSessionPage() {
|
|||
const providers = useProviders(() => sdk().directory)
|
||||
const openProviderSettings = useSettingsDialog("providers")
|
||||
const route = useSessionKey()
|
||||
const isDesktop = createMediaQuery("(min-width: 768px)")
|
||||
const [searchParams, setSearchParams] = useSearchParams<{ draftId?: string; prompt?: string }>()
|
||||
|
||||
useComposerCommands()
|
||||
|
|
@ -112,10 +113,10 @@ export default function NewSessionPage() {
|
|||
<Show when={rightMount()}>
|
||||
{(mount) => (
|
||||
<Portal mount={mount()}>
|
||||
<Show when={settings.visibility.status()}>
|
||||
<Tooltip placement="bottom" value={language.t("status.popover.trigger")}>
|
||||
<Show when={isDesktop() && settings.general.newLayoutDesigns()}>
|
||||
<TooltipV2 placement="bottom" value={language.t("status.popover.tools.trigger")}>
|
||||
<StatusPopoverV2 />
|
||||
</Tooltip>
|
||||
</TooltipV2>
|
||||
</Show>
|
||||
</Portal>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,14 @@ const icons = {
|
|||
viewBox: "0 0 20 20",
|
||||
body: `<path d="M2 10V18H18V10M2 10V2H18V10M2 10H18M5 6H9M5 14H9" stroke="currentColor"/>`,
|
||||
},
|
||||
tools: {
|
||||
viewBox: "0 0 16 16",
|
||||
body: `<path d="M8 6.74934V9.24934M10.668 6.74934V9.24934M5.33398 6.74934V9.24934M1.33398 2.44418V13.5542H14.6673V2.44418H1.33398Z" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
},
|
||||
"tools-warning": {
|
||||
viewBox: "0 0 16 16",
|
||||
body: `<path d="M9.50073 2.44498H1.33398V13.555H14.6673V6.49997M8.00073 6.74934V9.24934M10.6687 6.74934V9.24934M5.33472 6.74934V9.24934" stroke="currentColor" stroke-linecap="square"/>`,
|
||||
},
|
||||
"status-active": {
|
||||
viewBox: "0 0 20 20",
|
||||
body: `<path d="M18 2H2V10H18V2Z" fill="currentColor" fill-opacity="0.1"/><path d="M2 18H18V10H2V18Z" fill="currentColor" fill-opacity="0.1"/><path d="M2 10V18H18V10M2 10V2H18V10M2 10H18M5 6H9M5 14H9" stroke="currentColor"/>`,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue