diff --git a/packages/app/e2e/regression/mcp-workspace-toggle.spec.ts b/packages/app/e2e/regression/mcp-workspace-toggle.spec.ts index 58d29aab1ef..812f5b97508 100644 --- a/packages/app/e2e/regression/mcp-workspace-toggle.spec.ts +++ b/packages/app/e2e/regression/mcp-workspace-toggle.spec.ts @@ -156,7 +156,7 @@ for (const surface of ["dialog"] as const) { // The dialog opens with its search focused and the first server highlighted. if (surface === "dialog") { - await expect(view.panel.getByRole("searchbox", { name: "Search", exact: true })).toBeFocused() + await expect(view.panel.getByRole("combobox", { name: "Search MCPs", exact: true })).toBeFocused() await expect(view.panel.getByRole("option")).toHaveAttribute("aria-selected", "true") } @@ -176,13 +176,13 @@ for (const surface of ["dialog"] as const) { ]) expect(view.requests.every((request) => request.directory === workspace)).toBe(true) - // Status is separate from the shared v2 button that opens the error. - const failed = view.panel.getByRole("button", { name: "Details for figma-desktop", exact: true }) + // The red dot is the status and the switch stays on; a ghost action beside it opens the error. + const failed = view.panel.getByRole("button", { name: "View error for figma-desktop", exact: true }) const details = page.getByRole("dialog", { name: "figma-desktop", exact: true }) - await expect(view.toggle).toHaveCount(0) - await expect(view.panel.getByText("Failed", { exact: true })).toHaveCount(1) - await expect(failed).toHaveText("Details") - await expect(failed).toHaveAttribute("data-component", "button-v2") + await expect(view.toggle).toBeChecked() + await expect(view.toggle).toHaveAccessibleDescription("Failed") + await expect(failed).toHaveText("View error") + await expect(failed).toHaveAttribute("data-variant", "ghost-muted") await failed.click() await expect(details).toHaveAccessibleDescription("Connection failed") await expect(details.getByText(error, { exact: true })).toBeVisible() @@ -220,6 +220,34 @@ for (const surface of ["dialog"] as const) { }) } +test("toggling an MCP from the dialog keeps it highlighted", async ({ page }) => { + const connected = new Set() + const names = ["alpha-mcp", "figma-desktop", "zeta-mcp"] + + const view = await open(page, { + surface: "dialog", + mcp: () => names.map((name) => ({ name, status: { status: connected.has(name) ? "connected" : "disabled" } })), + onMcpAction: (input) => + void (input.action === "connect" ? connected.add(input.server) : connected.delete(input.server)), + }) + + const row = view.panel.getByRole("option").filter({ hasText: "figma-desktop" }) + const toggle = row.getByRole("switch", { name: "figma-desktop", exact: true }) + + // From the keyboard, Enter toggles the highlighted server and the highlight stays on it. + await page.keyboard.press("ArrowDown") + await expect(row).toHaveAttribute("aria-selected", "true") + await page.keyboard.press("Enter") + await expect(toggle).toBeChecked() + await expect(row).toHaveAttribute("aria-selected", "true") + + // With the pointer, the switch toggles the row under it and the highlight stays there too. + await row.locator('[data-slot="switch-control"]').click() + await expect(toggle).not.toBeChecked() + await expect(toggle).toBeEnabled() + await expect(row).toHaveAttribute("aria-selected", "true") +}) + for (const surface of ["popover", "dialog"] as const) { test(`signs in to an auth-gated MCP from the ${surface}`, async ({ page, context }) => { const state = { signedIn: false } @@ -254,6 +282,7 @@ for (const surface of ["popover", "dialog"] as const) { }) await expect(signIn).toHaveAttribute("data-component", "button-v2") + await expect(signIn).toHaveAttribute("data-variant", "ghost-muted") await expect(signIn.locator("svg")).toHaveCount(0) await signIn.click() await expect(signIn).toHaveText("Signing in…") @@ -312,9 +341,9 @@ for (const surface of ["popover", "dialog"] as const) { .locator(surface === "dialog" ? '[role="option"]' : ".session-mcp-row") .filter({ hasText: name }) - // The popover's dot is the status, so the control beside the name must stay whole. + // The dot is the status, so the control beside the name must stay whole. const status = - surface === "dialog" || index === 3 + index === 3 ? row.getByText(["Connected", "Disabled", "Failed", "Sign in"][index], { exact: true }) : row.locator('[data-slot="switch-control"]') @@ -342,7 +371,7 @@ for (const surface of ["popover", "dialog"] as const) { await page.setViewportSize({ width: 390, height: 844 }) await expect(name).toHaveCSS("white-space", "normal") await expect.poll(() => name.evaluate((el) => el.scrollWidth <= el.clientWidth)).toBe(true) - await view.panel.getByRole("button", { name: `Details for ${names[2]}`, exact: true }).scrollIntoViewIfNeeded() - await expect(view.panel.getByRole("button", { name: `Details for ${names[2]}`, exact: true })).toBeInViewport() + await view.panel.getByRole("button", { name: `View error for ${names[2]}`, exact: true }).scrollIntoViewIfNeeded() + await expect(view.panel.getByRole("button", { name: `View error for ${names[2]}`, exact: true })).toBeInViewport() }) } diff --git a/packages/app/e2e/regression/session-summary.spec.ts b/packages/app/e2e/regression/session-summary.spec.ts index badb004d173..5579f52a636 100644 --- a/packages/app/e2e/regression/session-summary.spec.ts +++ b/packages/app/e2e/regression/session-summary.spec.ts @@ -603,7 +603,7 @@ test("MCP rows toggle once per action and keep sign-in, errors, and management a expect(writes).toHaveLength(6) await submenu.getByRole("button", { name: "Manage MCP servers", exact: true }).click() - await expect(page.getByRole("dialog").getByText("MCPs", { exact: true })).toBeVisible() + await expect(page.getByRole("dialog", { name: "MCPs", exact: true })).toBeVisible() await expect(submenu).toBeHidden() }) diff --git a/packages/app/src/providers/connect/mcp-dialog.tsx b/packages/app/src/providers/connect/mcp-dialog.tsx index fa53f305dbe..2a6adbb0dd2 100644 --- a/packages/app/src/providers/connect/mcp-dialog.tsx +++ b/packages/app/src/providers/connect/mcp-dialog.tsx @@ -1,19 +1,20 @@ -import { Component, createEffect, createMemo, For, on, Show } from "solid-js" +import { Component, createEffect, createMemo, on, Show } from "solid-js" import { createStore } from "solid-js/store" +import fuzzysort from "fuzzysort" import { useData } from "@/runtime/server/current" import { useWorkspaceLocation } from "@/workspaces/location" import { Button } from "@opencode/ui/button" -import { Dialog, DialogBody, DialogHeader, DialogTitleGroup } from "@opencode/ui/dialog" -import { useFilteredList } from "@opencode/ui/hooks" +import { Dialog, DialogBody, DialogHeader, DialogTitle, DialogTitleGroup } from "@opencode/ui/dialog" import { Icon } from "@opencode/ui/icon" import { ErrorDetails } from "@opencode/ui/error-details" +import { SearchList } from "@opencode/ui/search-list" import { Switch } from "@opencode/ui/switch" -import { TextInput } from "@opencode/ui/text-input" import { TruncatedText } from "@opencode/ui/truncated-text" import { useLanguage } from "@/runtime/i18n/language" import { useMcpToggle } from "@/providers/connect/mcp" import { usePlatform } from "@/runtime/platform/platform" import { showToast } from "@/shell/notifications/toast" +import "@/shell/commands/dialog.css" import "@/shell/search-list.css" const statusLabels = { @@ -37,7 +38,13 @@ export const DialogMcp: Component<{ directory: string; server?: string }> = (pro const platform = usePlatform() const directory = () => props.directory const toggle = useMcpToggle(directory) - const [store, setStore] = createStore<{ detail?: string; copied: boolean }>({ detail: props.server, copied: false }) + + const [store, setStore] = createStore<{ detail?: string; copied: boolean; query: string }>({ + detail: props.server, + copied: false, + query: "", + }) + const loaded = () => data.location.mcp.server.list({ directory: directory() }) const servers = createMemo(() => loaded() ?? []) @@ -49,7 +56,16 @@ export const DialogMcp: Component<{ directory: string; server?: string }> = (pro .sort((a, b) => a.name.localeCompare(b.name)), ) - const enabledCount = createMemo(() => items().filter((i) => i.status === "connected").length) + const results = createMemo(() => { + const query = store.query.trim() + + if (!query) return items() + + return fuzzysort + .go(query, items(), { keys: ["name", "status"] }) + .map((result) => result.obj) + .sort((a, b) => a.name.localeCompare(b.name)) + }) // Like the TUI, a failed server's row opens its error; retrying and disabling happen there. const failure = createMemo(() => { @@ -71,15 +87,10 @@ export const DialogMcp: Component<{ directory: string; server?: string }> = (pro requestAnimationFrame(() => backRef?.focus()) } + // The list stays mounted behind the error, so going back keeps the search, the highlight and the scroll. const back = () => { setStore("detail", undefined) - requestAnimationFrame(() => { - searchRef?.focus() - const active = list.active() - - if (active) - resultsRef?.querySelector(`[data-option-key="${CSS.escape(active)}"]`)?.scrollIntoView({ block: "nearest" }) - }) + requestAnimationFrame(() => listRef?.querySelector('[role="combobox"]')?.focus()) } const onDetailKeyDown = (event: KeyboardEvent) => { @@ -110,182 +121,18 @@ export const DialogMcp: Component<{ directory: string; server?: string }> = (pro ) } - // A toggle changes the server's status, which makes the list reset its highlight to the first row; keep the user's place. - let kept = "" - - const list = useFilteredList({ - items, - key: (x) => x.name, - filterKeys: ["name", "status"], - sortBy: (a, b) => a.name.localeCompare(b.name), - onSelect: (x) => { - if (!x) return - kept = x.name - select(x.name) - }, - }) - - createEffect( - on( - list.flat, - (items) => { - if (kept && items.some((item) => item.name === kept)) list.setActive(kept) - }, - { defer: true }, - ), - ) - - let resultsRef: HTMLDivElement | undefined - let searchRef: HTMLInputElement | undefined + let listRef: HTMLDivElement | undefined let backRef: HTMLButtonElement | undefined - const onKeyDown = (event: KeyboardEvent) => { - list.onKeyDown(event) - const active = list.active() - - if (!active) return - - if (kept) kept = active - resultsRef?.querySelector(`[data-option-key="${CSS.escape(active)}"]`)?.scrollIntoView({ block: "nearest" }) - } - return ( - + // Like the model picker and the TUI, the list has no visible header; the search field leads. + - - - - - -
(resultsRef = el)}> - 0} - fallback={ -
- {language.t(items().length > 0 ? "dialog.mcp.search.empty" : "dialog.mcp.empty")} -
- } - > -
- - {(item) => { - const server = () => servers().find((entry) => entry.name === item.name) - const status = () => server()?.status - // The switch would start sign-in anyway, so an auth-gated server shows that action instead. - const signIn = () => status()?.status === "needs_auth" && !!server()?.integrationID - const busy = () => toggle.isPending && toggle.variables === item.name - - const detail = () => { - const current = status() - - if (!current || current.status === "pending" || current.status === "failed" || signIn()) - return - - return language.t(statusLabels[current.status]) - } - - return ( -
{ - kept = item.name - list.setActive(item.name) - }} - onMouseLeave={() => { - kept = "" - list.setActive("") - }} - onMouseDown={(event) => event.preventDefault()} - onClick={() => select(item.name)} - > -
- -
-
event.stopPropagation()}> - toggleServer(item.name)} - > - {item.name} - - - } - > - - - } - > - {language.t("mcp.status.failed")} - - -
-
- ) - }} -
-
-
-
-
- +
+ {language.t("dialog.mcp.title")} +
} > {(current) => ( @@ -338,6 +185,95 @@ export const DialogMcp: Component<{ directory: string; server?: string }> = (pro )}
+
+ + setStore("query", query)} + placeholder={language.t("dialog.mcp.search.placeholder")} + items={results()} + key={(item) => item.name} + empty={language.t(items().length > 0 ? "dialog.mcp.search.empty" : "dialog.mcp.empty")} + onSelect={(item) => select(item.name)} + > + {(item, row) => { + const server = () => servers().find((entry) => entry.name === item().name) + const status = () => server()?.status + const error = () => { + const current = status() + + return current?.status === "failed" ? current.error : undefined + } + // The switch would start sign-in anyway, so an auth-gated server shows that action instead. + const signIn = () => status()?.status === "needs_auth" && !!server()?.integrationID + + const busy = () => { + const target = toggle.isPending ? toggle.variables : undefined + + return (typeof target === "string" ? target : target?.name) === item().name + } + + // The dot shows the status, as in the session's MCP list; the words stay for assistive technology. + const label = () => { + const current = status()?.status + + return current && current !== "pending" ? language.t(statusLabels[current]) : undefined + } + + return ( +
+ + +
event.stopPropagation()}> + + + + { + if (toggle.isPending) return + toggle.mutate({ name: item().name, enabled }) + }} + > + {item().name} + + } + > + + +
+
+ ) + }} +
+
+
) } diff --git a/packages/app/src/runtime/i18n/am.ts b/packages/app/src/runtime/i18n/am.ts index e0e396adf74..bf0d747de59 100644 --- a/packages/app/src/runtime/i18n/am.ts +++ b/packages/app/src/runtime/i18n/am.ts @@ -216,7 +216,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "ትዕዛዝ መላክ አልተሳካም", "prompt.toast.promptSendFailed.title": "ፕሮምፕት መላክ አልተሳካም", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} ከ{{total}} ነቅቷል", "dialog.mcp.empty": "ምንም MCPs አልተዋቀረም", "dialog.fork.empty": "አዲስ ቅርንጫፍ ለመፍጠር መልዕክት የለም", "dialog.directory.action.selectFile": "ፋይል ምረጥ", diff --git a/packages/app/src/runtime/i18n/ar.ts b/packages/app/src/runtime/i18n/ar.ts index 2d13d829b37..f7a73adaa19 100644 --- a/packages/app/src/runtime/i18n/ar.ts +++ b/packages/app/src/runtime/i18n/ar.ts @@ -229,7 +229,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "فشل إرسال الأمر", "prompt.toast.promptSendFailed.title": "فشل إرسال الموجه", "dialog.mcp.title": "خوادم MCP", - "dialog.mcp.description": "تم تفعيل {{enabled}} من أصل {{total}}", "dialog.mcp.empty": "لم يتم تكوين أي خوادم MCP", "dialog.fork.empty": "لا توجد رسائل للتفرع منها", "dialog.directory.action.selectFile": "اختيار ملف", diff --git a/packages/app/src/runtime/i18n/az.ts b/packages/app/src/runtime/i18n/az.ts index 3ee5a55aaaf..91ffba8d218 100644 --- a/packages/app/src/runtime/i18n/az.ts +++ b/packages/app/src/runtime/i18n/az.ts @@ -218,7 +218,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Əmr göndərilə bilmədi", "prompt.toast.promptSendFailed.title": "Prompt göndərilə bilmədi", "dialog.mcp.title": "MCP-lər", - "dialog.mcp.description": "{{total}}-dən {{enabled}} aktiv", "dialog.mcp.empty": "MCP konfiqurasiya edilməyib", "dialog.fork.empty": "Yeni sessiyaya ayırmaq üçün mesaj yoxdur", "dialog.directory.action.selectFile": "Fayl seçin", diff --git a/packages/app/src/runtime/i18n/bg.ts b/packages/app/src/runtime/i18n/bg.ts index acad278f509..127d58ffddf 100644 --- a/packages/app/src/runtime/i18n/bg.ts +++ b/packages/app/src/runtime/i18n/bg.ts @@ -217,7 +217,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Неуспешно изпращане на команда", "prompt.toast.promptSendFailed.title": "Неуспешно изпращане на подкана", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} от {{total}} активирани", "dialog.mcp.empty": "Няма конфигурирани MCP", "dialog.fork.empty": "Няма съобщения за разклонение", "dialog.directory.action.selectFile": "Изберете файл", diff --git a/packages/app/src/runtime/i18n/bn.ts b/packages/app/src/runtime/i18n/bn.ts index f5c71c7416f..5ce2375e0f4 100644 --- a/packages/app/src/runtime/i18n/bn.ts +++ b/packages/app/src/runtime/i18n/bn.ts @@ -221,7 +221,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "কমান্ড পাঠাতে ব্যর্থ হয়েছে", "prompt.toast.promptSendFailed.title": "প্রম্পট পাঠাতে ব্যর্থ হয়েছে৷", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{total}} এর {{enabled}} সক্ষম", "dialog.mcp.empty": "কোনো MCPs কনফিগার করা নেই", "dialog.fork.empty": "থেকে কাঁটাচামচ কোন বার্তা", "dialog.directory.action.selectFile": "ফাইল নির্বাচন করুন", diff --git a/packages/app/src/runtime/i18n/br.ts b/packages/app/src/runtime/i18n/br.ts index 0446f2119b3..ed80d01c476 100644 --- a/packages/app/src/runtime/i18n/br.ts +++ b/packages/app/src/runtime/i18n/br.ts @@ -433,7 +433,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Falha ao enviar comando", "prompt.toast.promptSendFailed.title": "Falha ao enviar prompt", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} de {{total}} ativados", "dialog.mcp.empty": "Nenhum MCP configurado", "dialog.fork.empty": "Nenhuma mensagem para bifurcar", "dialog.directory.action.selectFile": "Selecionar arquivo", diff --git a/packages/app/src/runtime/i18n/bs.ts b/packages/app/src/runtime/i18n/bs.ts index c1a9a83e6b7..7a455be8081 100644 --- a/packages/app/src/runtime/i18n/bs.ts +++ b/packages/app/src/runtime/i18n/bs.ts @@ -238,7 +238,6 @@ export const dict = { "prompt.toast.promptSendFailed.title": "Neuspješno slanje upita", "dialog.mcp.title": "MCP-ovi", - "dialog.mcp.description": "{{enabled}} od {{total}} omogućeno", "dialog.mcp.empty": "Nema konfigurisanih MCP-ova", diff --git a/packages/app/src/runtime/i18n/ca.ts b/packages/app/src/runtime/i18n/ca.ts index 0d02538d782..c7068a3ed90 100644 --- a/packages/app/src/runtime/i18n/ca.ts +++ b/packages/app/src/runtime/i18n/ca.ts @@ -216,7 +216,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "No s'ha pogut enviar l'ordre", "prompt.toast.promptSendFailed.title": "No s'ha pogut enviar la sol·licitud", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{enabled}} de {{total}} activat", "dialog.mcp.empty": "No s'ha configurat cap MCP", "dialog.fork.empty": "No hi ha missatges per bifurcar", "dialog.directory.action.selectFile": "Seleccioneu el fitxer", diff --git a/packages/app/src/runtime/i18n/cs.ts b/packages/app/src/runtime/i18n/cs.ts index 50917859dd4..806c6d99686 100644 --- a/packages/app/src/runtime/i18n/cs.ts +++ b/packages/app/src/runtime/i18n/cs.ts @@ -216,7 +216,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Odeslání příkazu se nezdařilo", "prompt.toast.promptSendFailed.title": "Odeslání výzvy se nezdařilo", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{enabled}} z {{total}} povoleno", "dialog.mcp.empty": "Nejsou nakonfigurovány žádné MCP", "dialog.fork.empty": "Žádné zprávy k rozvětvení", "dialog.directory.action.selectFile": "Vyberte soubor", diff --git a/packages/app/src/runtime/i18n/da.ts b/packages/app/src/runtime/i18n/da.ts index ab7aee36f15..5ff6f22f061 100644 --- a/packages/app/src/runtime/i18n/da.ts +++ b/packages/app/src/runtime/i18n/da.ts @@ -367,7 +367,6 @@ export const dict = { "prompt.toast.promptSendFailed.title": "Kunne ikke sende forespørgsel", "dialog.mcp.title": "MCP'er", - "dialog.mcp.description": "{{enabled}} af {{total}} aktiveret", "dialog.mcp.empty": "Ingen MCP'er konfigureret", diff --git a/packages/app/src/runtime/i18n/de.ts b/packages/app/src/runtime/i18n/de.ts index 4744a0d43b3..56b90c409c7 100644 --- a/packages/app/src/runtime/i18n/de.ts +++ b/packages/app/src/runtime/i18n/de.ts @@ -366,7 +366,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Befehl konnte nicht gesendet werden", "prompt.toast.promptSendFailed.title": "Eingabe konnte nicht gesendet werden", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} von {{total}} aktiviert", "dialog.mcp.empty": "Keine MCPs konfiguriert", "dialog.fork.empty": "Keine Nachrichten zum Abzweigen vorhanden", "dialog.directory.action.selectFile": "Datei auswählen", diff --git a/packages/app/src/runtime/i18n/dv.ts b/packages/app/src/runtime/i18n/dv.ts index e202be2a072..20b7da012fb 100644 --- a/packages/app/src/runtime/i18n/dv.ts +++ b/packages/app/src/runtime/i18n/dv.ts @@ -221,7 +221,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "ކޮމާންޑް ފޮނުވަން ނާކާމިޔާބުވިއެވެ", "prompt.toast.promptSendFailed.title": "ޕްރޮމްޕްޓް ފޮނުވަން ނާކާމިޔާބުވިއެވެ", "dialog.mcp.title": "MCPs އެވެ", - "dialog.mcp.description": "\u2068{{enabled}}\u2069 ގެ \u2068{{total}}\u2069 އެނެބަލްކޮށްފައިވެއެވެ", "dialog.mcp.empty": "އެއްވެސް MCPs ކޮންފިގްރޭޓް ކުރެވިފައެއް ނުވެއެވެ", "dialog.fork.empty": "ފޯކް ކުރާނެ މެސެޖެއް ނެތެވެ", "dialog.directory.action.selectFile": "ފައިލް ހޮވާށެވެ", diff --git a/packages/app/src/runtime/i18n/dz.ts b/packages/app/src/runtime/i18n/dz.ts index b77bb6a1c8e..079b88b458b 100644 --- a/packages/app/src/runtime/i18n/dz.ts +++ b/packages/app/src/runtime/i18n/dz.ts @@ -228,7 +228,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "བརྡ་བཀོད་གཏང་ནི་ལུ་འཐུས་ཤོར་འབྱུང་ཡོདཔ།", "prompt.toast.promptSendFailed.title": "བརྡ་སྟོན་གཏང་ནི་ལུ་འཐུས་ཤོར་འབྱུང་ཡོདཔ།", "dialog.mcp.title": "MCPs།", - "dialog.mcp.description": "{{enabled}} གི་ {{total}} ལྕོགས་ཅན་བཟོ་ཡོདཔ།", "dialog.mcp.empty": "MCPsརིམ་སྒྲིག་མ་འབད་བས།", "dialog.fork.empty": "ལས་ཕོརཀ་འབད་ནི་ལུ་འཕྲིན་དོན་མེད།", "dialog.directory.action.selectFile": "ཡིག་སྣོད་སེལ་འཐུ་འབད།", diff --git a/packages/app/src/runtime/i18n/el.ts b/packages/app/src/runtime/i18n/el.ts index 52ee542b52b..e77d29b287c 100644 --- a/packages/app/src/runtime/i18n/el.ts +++ b/packages/app/src/runtime/i18n/el.ts @@ -218,7 +218,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Αποτυχία αποστολής εντολής", "prompt.toast.promptSendFailed.title": "Αποτυχία αποστολής προτροπής", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} από {{total}} ενεργοποιημένα", "dialog.mcp.empty": "Δεν έχει διαμορφωθεί το MCPs", "dialog.fork.empty": "Δεν υπάρχουν μηνύματα για διακλάδωση", "dialog.directory.action.selectFile": "Επιλογή αρχείου", diff --git a/packages/app/src/runtime/i18n/en.ts b/packages/app/src/runtime/i18n/en.ts index 4fab4e05399..997a0195332 100644 --- a/packages/app/src/runtime/i18n/en.ts +++ b/packages/app/src/runtime/i18n/en.ts @@ -357,8 +357,8 @@ export const dict = { "prompt.toast.promptSendFailed.title": "Failed to send prompt", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} of {{total}} enabled", "dialog.mcp.empty": "No MCPs configured", + "dialog.mcp.search.placeholder": "Search MCPs", "dialog.mcp.search.empty": "No MCPs found", "dialog.mcp.error.title": "MCP server: {{name}}", "dialog.mcp.error.copy": "Copy details", @@ -368,6 +368,8 @@ export const dict = { "dialog.mcp.error.status": "Connection failed", "dialog.mcp.signingIn": "Signing in…", "dialog.mcp.error.actionLabel": "Details for {{name}}", + "dialog.mcp.error.view": "View error", + "dialog.mcp.error.viewLabel": "View error for {{name}}", "dialog.mcp.error.disable": "Disable", "dialog.mcp.signIn": "Sign in", "dialog.mcp.signIn.open": "Sign in to {{name}}", diff --git a/packages/app/src/runtime/i18n/es.ts b/packages/app/src/runtime/i18n/es.ts index 3507b7144c5..ecc93dbe673 100644 --- a/packages/app/src/runtime/i18n/es.ts +++ b/packages/app/src/runtime/i18n/es.ts @@ -453,7 +453,6 @@ export const dict = { "prompt.toast.promptSendFailed.title": "Fallo al enviar prompt", "dialog.mcp.title": "Servidores MCP", - "dialog.mcp.description": "{{enabled}} de {{total}} habilitados", "dialog.mcp.empty": "No hay servidores MCP configurados", diff --git a/packages/app/src/runtime/i18n/et.ts b/packages/app/src/runtime/i18n/et.ts index 259e6730e3b..0578cb504c6 100644 --- a/packages/app/src/runtime/i18n/et.ts +++ b/packages/app/src/runtime/i18n/et.ts @@ -216,7 +216,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Käsu saatmine ebaõnnestus", "prompt.toast.promptSendFailed.title": "Viipa saatmine ebaõnnestus", "dialog.mcp.title": "MCP-d", - "dialog.mcp.description": "{{enabled}} / {{total}} on lubatud", "dialog.mcp.empty": "MCP-sid pole konfigureeritud", "dialog.fork.empty": "Ühtegi sõnumit pole", "dialog.directory.action.selectFile": "Valige fail", diff --git a/packages/app/src/runtime/i18n/fa.ts b/packages/app/src/runtime/i18n/fa.ts index d301488338b..a9706fea374 100644 --- a/packages/app/src/runtime/i18n/fa.ts +++ b/packages/app/src/runtime/i18n/fa.ts @@ -226,7 +226,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "فرمان ارسال نشد", "prompt.toast.promptSendFailed.title": "درخواست ارسال نشد", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} از {{total}} فعال است", "dialog.mcp.empty": "MCPs پیکربندی نشده است", "dialog.fork.empty": "هیچ پیامی برای فورک وجود ندارد", "dialog.directory.action.selectFile": "فایل را انتخاب کنید", diff --git a/packages/app/src/runtime/i18n/fi.ts b/packages/app/src/runtime/i18n/fi.ts index 1bab7202be1..f2e02aec91b 100644 --- a/packages/app/src/runtime/i18n/fi.ts +++ b/packages/app/src/runtime/i18n/fi.ts @@ -349,7 +349,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Komennon lähettäminen epäonnistui", "prompt.toast.promptSendFailed.title": "Kehotteen lähettäminen epäonnistui", "dialog.mcp.title": "MCP:t", - "dialog.mcp.description": "{{enabled}}/{{total}} käytössä", "dialog.mcp.empty": "MCP:tä ei ole määritetty", "dialog.fork.empty": "Ei viestejä, joista voisi haarauttaa", "dialog.directory.action.selectFile": "Valitse tiedosto", diff --git a/packages/app/src/runtime/i18n/fo.ts b/packages/app/src/runtime/i18n/fo.ts index 6ca5f066639..d7987cbce20 100644 --- a/packages/app/src/runtime/i18n/fo.ts +++ b/packages/app/src/runtime/i18n/fo.ts @@ -216,7 +216,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Tað eydnaðist ikki at senda skipan", "prompt.toast.promptSendFailed.title": "Tað eydnaðist ikki at senda boð", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{enabled}} av {{total}} virkið", "dialog.mcp.empty": "Ongin MCP uppsettur", "dialog.fork.empty": "Eingi boð at greina frá", "dialog.directory.action.selectFile": "Vel fílu", diff --git a/packages/app/src/runtime/i18n/fr.ts b/packages/app/src/runtime/i18n/fr.ts index 1ee0a4d6b52..320aec6a329 100644 --- a/packages/app/src/runtime/i18n/fr.ts +++ b/packages/app/src/runtime/i18n/fr.ts @@ -437,7 +437,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Échec de l'envoi de la commande", "prompt.toast.promptSendFailed.title": "Échec de l'envoi de l'invite", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "MCP actifs : {{enabled}} sur {{total}}", "dialog.mcp.empty": "Aucun MCP configuré", "dialog.fork.empty": "Aucun message à partir duquel bifurquer", "dialog.directory.action.selectFile": "Sélectionner le fichier", diff --git a/packages/app/src/runtime/i18n/he.ts b/packages/app/src/runtime/i18n/he.ts index db1c5882f5a..a825396e71a 100644 --- a/packages/app/src/runtime/i18n/he.ts +++ b/packages/app/src/runtime/i18n/he.ts @@ -225,7 +225,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "שליחת הפקודה נכשלה", "prompt.toast.promptSendFailed.title": "שליחת הבקשה נכשלה", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} מתוך {{total}} מופעלים", "dialog.mcp.empty": "לא הוגדרו MCPs", "dialog.fork.empty": "אין הודעות שמהן אפשר ליצור הפעלה", "dialog.directory.action.selectFile": "בחר קובץ", diff --git a/packages/app/src/runtime/i18n/hi.ts b/packages/app/src/runtime/i18n/hi.ts index b9c844a360e..9faee636c97 100644 --- a/packages/app/src/runtime/i18n/hi.ts +++ b/packages/app/src/runtime/i18n/hi.ts @@ -222,7 +222,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "कमांड भेजने में विफल", "prompt.toast.promptSendFailed.title": "प्रॉम्प्ट भेजने में विफल", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{total}} में से {{enabled}} सक्षम", "dialog.mcp.empty": "कोई MCP कॉन्फ़िगर नहीं किया गया", "dialog.fork.empty": "फ़ोर्क करने के लिए कोई संदेश नहीं", "dialog.directory.action.selectFile": "फ़ाइल का चयन करें", diff --git a/packages/app/src/runtime/i18n/hr.ts b/packages/app/src/runtime/i18n/hr.ts index 7665b6731af..c3d223b0eef 100644 --- a/packages/app/src/runtime/i18n/hr.ts +++ b/packages/app/src/runtime/i18n/hr.ts @@ -229,7 +229,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Slanje naredbe nije uspjelo", "prompt.toast.promptSendFailed.title": "Slanje upita nije uspjelo", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} od {{total}} omogućen", "dialog.mcp.empty": "Nema konfiguriranih MCP-ova", "dialog.fork.empty": "Nema poruka za račvanje", "dialog.directory.action.selectFile": "Odaberite datoteku", diff --git a/packages/app/src/runtime/i18n/hu.ts b/packages/app/src/runtime/i18n/hu.ts index 3f230dda96a..2a52a2ffe8c 100644 --- a/packages/app/src/runtime/i18n/hu.ts +++ b/packages/app/src/runtime/i18n/hu.ts @@ -229,7 +229,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Nem sikerült elküldeni a parancsot", "prompt.toast.promptSendFailed.title": "Nem sikerült elküldeni a felszólítást", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}}/{{total}} engedélyezve", "dialog.mcp.empty": "Nincs konfigurálva MCP", "dialog.fork.empty": "Nincsenek üzenetek, ahonnan elágazhatna", "dialog.directory.action.selectFile": "Válassza ki a fájlt", diff --git a/packages/app/src/runtime/i18n/hy.ts b/packages/app/src/runtime/i18n/hy.ts index 6e5db7169d8..e33d02bf57b 100644 --- a/packages/app/src/runtime/i18n/hy.ts +++ b/packages/app/src/runtime/i18n/hy.ts @@ -217,7 +217,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Չհաջողվեց ուղարկել հրամանը", "prompt.toast.promptSendFailed.title": "Չհաջողվեց ուղարկել հուշումը", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}}-ը {{total}}-ից միացված է", "dialog.mcp.empty": "Կազմաձևված չէ MCPs", "dialog.fork.empty": "Չկան պատառաքաղված հաղորդագրություններ", "dialog.directory.action.selectFile": "Ընտրել ֆայլը", diff --git a/packages/app/src/runtime/i18n/id.ts b/packages/app/src/runtime/i18n/id.ts index 54ef379dca9..a908e44aac1 100644 --- a/packages/app/src/runtime/i18n/id.ts +++ b/packages/app/src/runtime/i18n/id.ts @@ -249,7 +249,6 @@ export const dict = { "prompt.toast.promptSendFailed.title": "Gagal mengirim prompt", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{enabled}} dari {{total}} diaktifkan", "dialog.mcp.empty": "Tidak ada MCP yang dikonfigurasi", diff --git a/packages/app/src/runtime/i18n/is.ts b/packages/app/src/runtime/i18n/is.ts index f1b4286f959..a089659a104 100644 --- a/packages/app/src/runtime/i18n/is.ts +++ b/packages/app/src/runtime/i18n/is.ts @@ -229,7 +229,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Mistókst að senda skipun", "prompt.toast.promptSendFailed.title": "Mistókst að senda tilkynningu", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} af {{total}} virkt", "dialog.mcp.empty": "Engar MCPs stilltar", "dialog.fork.empty": "Engin skilaboð til að punga frá", "dialog.directory.action.selectFile": "Veldu skrá", diff --git a/packages/app/src/runtime/i18n/it.ts b/packages/app/src/runtime/i18n/it.ts index c5a288191ee..8afa3f1a181 100644 --- a/packages/app/src/runtime/i18n/it.ts +++ b/packages/app/src/runtime/i18n/it.ts @@ -370,7 +370,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Impossibile inviare il comando", "prompt.toast.promptSendFailed.title": "Impossibile inviare il prompt", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{enabled}} di {{total}} abilitati", "dialog.mcp.empty": "Nessun MCP configurato", "dialog.fork.empty": "Nessun messaggio da cui effettuare il fork", "dialog.directory.action.selectFile": "Seleziona file", diff --git a/packages/app/src/runtime/i18n/ja.ts b/packages/app/src/runtime/i18n/ja.ts index 65219a6ba1c..d02823c5847 100644 --- a/packages/app/src/runtime/i18n/ja.ts +++ b/packages/app/src/runtime/i18n/ja.ts @@ -228,7 +228,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "コマンドの送信に失敗しました", "prompt.toast.promptSendFailed.title": "プロンプトの送信に失敗しました", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{total}}個中{{enabled}}個が有効", "dialog.mcp.empty": "MCPが設定されていません", "dialog.fork.empty": "フォーク元のメッセージがありません", "dialog.directory.action.selectFile": "ファイルを選択", diff --git a/packages/app/src/runtime/i18n/ka.ts b/packages/app/src/runtime/i18n/ka.ts index cdfe24d2396..37ed962ad0d 100644 --- a/packages/app/src/runtime/i18n/ka.ts +++ b/packages/app/src/runtime/i18n/ka.ts @@ -216,7 +216,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "ბრძანების გაგზავნა ვერ მოხერხდა", "prompt.toast.promptSendFailed.title": "მოთხოვნის გაგზავნა ვერ მოხერხდა", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} / {{total}}-დან ჩართულია", "dialog.mcp.empty": "არა MCPs კონფიგურირებული", "dialog.fork.empty": "განშტოებისთვის შეტყობინებები არ არის", "dialog.directory.action.selectFile": "აირჩიეთ ფაილი", diff --git a/packages/app/src/runtime/i18n/km.ts b/packages/app/src/runtime/i18n/km.ts index 04576db39ae..2843e1853a8 100644 --- a/packages/app/src/runtime/i18n/km.ts +++ b/packages/app/src/runtime/i18n/km.ts @@ -224,7 +224,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "បរាជ័យក្នុងការផ្ញើពាក្យបញ្ជា", "prompt.toast.promptSendFailed.title": "បរាជ័យក្នុងការផ្ញើប្រអប់បញ្ចូល", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} នៃ {{total}} ត្រូវបានបើក", "dialog.mcp.empty": "មិនបានកំណត់រចនាសម្ព័ន្ធ MCPs ទេ។", "dialog.fork.empty": "គ្មានសារដែលត្រូវបំបែកពី", "dialog.directory.action.selectFile": "ជ្រើសរើសឯកសារ", diff --git a/packages/app/src/runtime/i18n/ko.ts b/packages/app/src/runtime/i18n/ko.ts index c21b2f80194..3e1c9c8a13a 100644 --- a/packages/app/src/runtime/i18n/ko.ts +++ b/packages/app/src/runtime/i18n/ko.ts @@ -152,7 +152,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "명령 전송 실패", "prompt.toast.promptSendFailed.title": "프롬프트 전송 실패", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{total}}개 중 {{enabled}}개 활성화됨", "dialog.mcp.empty": "구성된 MCP 없음", "dialog.fork.empty": "분기할 메시지 없음", "dialog.directory.action.selectFile": "파일 선택", diff --git a/packages/app/src/runtime/i18n/lo.ts b/packages/app/src/runtime/i18n/lo.ts index bd4cd376c81..a7a4a8131de 100644 --- a/packages/app/src/runtime/i18n/lo.ts +++ b/packages/app/src/runtime/i18n/lo.ts @@ -224,7 +224,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "ລົ້ມເຫລວໃນການສົ່ງຄໍາສັ່ງ", "prompt.toast.promptSendFailed.title": "ສົ່ງ prompt ບໍ່ສຳເລັດ", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} ຂອງ {{total}} ຖືກເປີດໃຊ້", "dialog.mcp.empty": "ບໍ່ມີ MCPs ທີ່ຖືກຕັ້ງຄ່າ", "dialog.fork.empty": "ບໍ່ມີຂໍ້ຄວາມທີ່ຈະແຍກອອກຈາກ", "dialog.directory.action.selectFile": "ເລືອກໄຟລ໌", diff --git a/packages/app/src/runtime/i18n/lt.ts b/packages/app/src/runtime/i18n/lt.ts index 91874953153..3cfbf0af8bb 100644 --- a/packages/app/src/runtime/i18n/lt.ts +++ b/packages/app/src/runtime/i18n/lt.ts @@ -229,7 +229,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Nepavyko išsiųsti komandos", "prompt.toast.promptSendFailed.title": "Nepavyko išsiųsti raginimo", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "Įjungtas {{enabled}} arba {{total}}", "dialog.mcp.empty": "MCP nesukonfigūruota", "dialog.fork.empty": "Nėra pranešimų, iš kurių būtų galima išsišakoti", "dialog.directory.action.selectFile": "Pasirinkite failą", diff --git a/packages/app/src/runtime/i18n/lv.ts b/packages/app/src/runtime/i18n/lv.ts index 806778e1969..b63dad8f0b1 100644 --- a/packages/app/src/runtime/i18n/lv.ts +++ b/packages/app/src/runtime/i18n/lv.ts @@ -216,7 +216,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Neizdevās nosūtīt komandu", "prompt.toast.promptSendFailed.title": "Neizdevās nosūtīt pieprasījumu", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{enabled}} no {{total}} ieslēgti", "dialog.mcp.empty": "Nav konfigurētu MCP", "dialog.fork.empty": "Nav ziņu, no kurām atzarot", "dialog.directory.action.selectFile": "Izvēlieties failu", diff --git a/packages/app/src/runtime/i18n/mk.ts b/packages/app/src/runtime/i18n/mk.ts index aff55d69639..92b634c2d2d 100644 --- a/packages/app/src/runtime/i18n/mk.ts +++ b/packages/app/src/runtime/i18n/mk.ts @@ -217,7 +217,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Не успеа да се испрати командата", "prompt.toast.promptSendFailed.title": "Не успеа да се испрати потсетник", "dialog.mcp.title": "MCPс", - "dialog.mcp.description": "{{enabled}} од {{total}} е овозможено", "dialog.mcp.empty": "Не се конфигурирани MCPs", "dialog.fork.empty": "Нема пораки од кои може да се префрлите", "dialog.directory.action.selectFile": "Изберете датотека", diff --git a/packages/app/src/runtime/i18n/mn.ts b/packages/app/src/runtime/i18n/mn.ts index 843ab917ea9..a84fa71dc69 100644 --- a/packages/app/src/runtime/i18n/mn.ts +++ b/packages/app/src/runtime/i18n/mn.ts @@ -217,7 +217,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Командыг илгээж чадсангүй", "prompt.toast.promptSendFailed.title": "Промпт илгээж чадсангүй", "dialog.mcp.title": "MCPсек", - "dialog.mcp.description": "{{total}}-ийн {{enabled}}-г идэвхжүүлсэн", "dialog.mcp.empty": "Тохируулсан MCPс байхгүй", "dialog.fork.empty": "Салаалах зурвас алга", "dialog.directory.action.selectFile": "Файл сонгоно уу", diff --git a/packages/app/src/runtime/i18n/ms.ts b/packages/app/src/runtime/i18n/ms.ts index 912d0e448d3..d4d5f95bc39 100644 --- a/packages/app/src/runtime/i18n/ms.ts +++ b/packages/app/src/runtime/i18n/ms.ts @@ -225,7 +225,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Gagal menghantar arahan", "prompt.toast.promptSendFailed.title": "Gagal menghantar prompt", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{enabled}} daripada {{total}} diaktifkan", "dialog.mcp.empty": "Tiada MCP dikonfigurasi", "dialog.fork.empty": "Tiada mesej untuk dicabang", "dialog.directory.action.selectFile": "Pilih fail", diff --git a/packages/app/src/runtime/i18n/my.ts b/packages/app/src/runtime/i18n/my.ts index 1c8fe719ccb..7708fd5ce16 100644 --- a/packages/app/src/runtime/i18n/my.ts +++ b/packages/app/src/runtime/i18n/my.ts @@ -225,7 +225,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "အမိန့်ပေးပို့ရန် မအောင်မြင်ပါ။", "prompt.toast.promptSendFailed.title": "Prompt ပေးပို့ရန် မအောင်မြင်ပါ။", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} ၏ {{total}} ဖွင့်ထားသည်။", "dialog.mcp.empty": "MCP များကို ပြင်ဆင်သတ်မှတ်ထားခြင်းမရှိပါ။", "dialog.fork.empty": "လမ်းခွဲရန် မက်ဆေ့ချ်များ မရှိပါ။", "dialog.directory.action.selectFile": "ဖိုင်ကို ရွေးပါ။", diff --git a/packages/app/src/runtime/i18n/ne.ts b/packages/app/src/runtime/i18n/ne.ts index 89ad53fa523..805308ec6aa 100644 --- a/packages/app/src/runtime/i18n/ne.ts +++ b/packages/app/src/runtime/i18n/ne.ts @@ -221,7 +221,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "आदेश पठाउन असफल भयो", "prompt.toast.promptSendFailed.title": "प्रम्प्ट पठाउन असफल भयो", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} को {{total}} सक्षम गरियो", "dialog.mcp.empty": "कुनै पनि MCPs कन्फिगर गरिएको छैन", "dialog.fork.empty": "बाट फोर्क गर्न कुनै सन्देश छैन", "dialog.directory.action.selectFile": "फाइल चयन गर्नुहोस्", diff --git a/packages/app/src/runtime/i18n/nl.ts b/packages/app/src/runtime/i18n/nl.ts index 80ef3862cc6..6a483e6ff2f 100644 --- a/packages/app/src/runtime/i18n/nl.ts +++ b/packages/app/src/runtime/i18n/nl.ts @@ -421,7 +421,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Kan opdracht niet verzenden", "prompt.toast.promptSendFailed.title": "Kan de prompt niet verzenden", "dialog.mcp.title": "MCP's", - "dialog.mcp.description": "{{enabled}} of {{total}} ingeschakeld", "dialog.mcp.empty": "Geen MCP's geconfigureerd", "dialog.fork.empty": "Geen berichten om van af te splitsen", "dialog.directory.action.selectFile": "Selecteer bestand", diff --git a/packages/app/src/runtime/i18n/no.ts b/packages/app/src/runtime/i18n/no.ts index d8cb09fd1a3..a47446c204c 100644 --- a/packages/app/src/runtime/i18n/no.ts +++ b/packages/app/src/runtime/i18n/no.ts @@ -427,7 +427,6 @@ export const dict = { "prompt.toast.promptSendFailed.title": "Kunne ikke sende forespørsel", "dialog.mcp.title": "MCP-er", - "dialog.mcp.description": "{{enabled}} av {{total}} aktivert", "dialog.mcp.empty": "Ingen MCP-er konfigurert", diff --git a/packages/app/src/runtime/i18n/pa.ts b/packages/app/src/runtime/i18n/pa.ts index 433b2aef05c..60ec3e42382 100644 --- a/packages/app/src/runtime/i18n/pa.ts +++ b/packages/app/src/runtime/i18n/pa.ts @@ -224,7 +224,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "کمانڈ بھیجن چ ناکام رہیا", "prompt.toast.promptSendFailed.title": "پرامپٹ بھیجن چ ناکام رہیا", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "\u2068{{total}}\u2069 وچوں \u2068{{enabled}}\u2069 چالو", "dialog.mcp.empty": "کوئی MCP سیٹ نئیں کیتا گیا", "dialog.fork.empty": "فورک کرن لئی کوئی پیغام نئیں", "dialog.directory.action.selectFile": "فائل چنو", diff --git a/packages/app/src/runtime/i18n/pl.ts b/packages/app/src/runtime/i18n/pl.ts index 490d83708ee..e827f6d0594 100644 --- a/packages/app/src/runtime/i18n/pl.ts +++ b/packages/app/src/runtime/i18n/pl.ts @@ -220,7 +220,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Nie udało się wysłać polecenia", "prompt.toast.promptSendFailed.title": "Nie udało się wysłać zapytania", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "Włączone: {{enabled}} z {{total}}", "dialog.mcp.empty": "Brak skonfigurowanych MCP", "dialog.fork.empty": "Brak wiadomości do rozwidlenia", "dialog.directory.action.selectFile": "Wybierz plik", diff --git a/packages/app/src/runtime/i18n/ro.ts b/packages/app/src/runtime/i18n/ro.ts index 515438507b0..b349900b6f8 100644 --- a/packages/app/src/runtime/i18n/ro.ts +++ b/packages/app/src/runtime/i18n/ro.ts @@ -216,7 +216,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Nu s-a putut trimite comanda", "prompt.toast.promptSendFailed.title": "Nu s-a putut trimite promptul", "dialog.mcp.title": "MCP-uri", - "dialog.mcp.description": "{{enabled}} din {{total}} activate", "dialog.mcp.empty": "Niciun MCP configurat", "dialog.fork.empty": "Niciun mesaj de bifurcat", "dialog.directory.action.selectFile": "Selectează fișier", diff --git a/packages/app/src/runtime/i18n/ru.ts b/packages/app/src/runtime/i18n/ru.ts index 4e920a7e6e2..532849000b6 100644 --- a/packages/app/src/runtime/i18n/ru.ts +++ b/packages/app/src/runtime/i18n/ru.ts @@ -237,7 +237,6 @@ export const dict = { "prompt.toast.promptSendFailed.title": "Не удалось отправить запрос", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{enabled}} из {{total}} включено", "dialog.mcp.empty": "MCP не настроены", diff --git a/packages/app/src/runtime/i18n/si.ts b/packages/app/src/runtime/i18n/si.ts index c16b7823871..7deda282ebb 100644 --- a/packages/app/src/runtime/i18n/si.ts +++ b/packages/app/src/runtime/i18n/si.ts @@ -220,7 +220,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "විධානය යැවීමට අසමත් විය", "prompt.toast.promptSendFailed.title": "ප්‍රොම්ප්ට් එක යැවීමට අසමත් විය", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{total}} න් {{enabled}} සබලයි", "dialog.mcp.empty": "MCPs වින්‍යාස කර නැත", "dialog.fork.empty": "ෆෝක් කිරීමට පණිවිඩ නැත", "dialog.directory.action.selectFile": "ගොනුව තෝරන්න", diff --git a/packages/app/src/runtime/i18n/sk.ts b/packages/app/src/runtime/i18n/sk.ts index a4314068919..8f6290e8f99 100644 --- a/packages/app/src/runtime/i18n/sk.ts +++ b/packages/app/src/runtime/i18n/sk.ts @@ -242,7 +242,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Nepodarilo sa odoslať príkaz", "prompt.toast.promptSendFailed.title": "Nepodarilo sa odoslať výzvu", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{enabled}} z {{total}} povolených", "dialog.mcp.empty": "Nie sú nastavené žiadne MCP", "dialog.fork.empty": "Žiadne správy na vytvorenie vetvy", "dialog.directory.action.selectFile": "Vybrať súbor", diff --git a/packages/app/src/runtime/i18n/sl.ts b/packages/app/src/runtime/i18n/sl.ts index 8e54862efc2..8a90237c390 100644 --- a/packages/app/src/runtime/i18n/sl.ts +++ b/packages/app/src/runtime/i18n/sl.ts @@ -242,7 +242,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Ukaza ni bilo mogoče poslati", "prompt.toast.promptSendFailed.title": "Poziva ni bilo mogoče poslati", "dialog.mcp.title": "MCP-ji", - "dialog.mcp.description": "{{enabled}} od {{total}} omogočen", "dialog.mcp.empty": "MCP-ji niso konfigurirani", "dialog.fork.empty": "Ni sporočil za odcepitev", "dialog.directory.action.selectFile": "Izberite datoteko", diff --git a/packages/app/src/runtime/i18n/sq.ts b/packages/app/src/runtime/i18n/sq.ts index bc70e2a71df..a6bfc6c8c06 100644 --- a/packages/app/src/runtime/i18n/sq.ts +++ b/packages/app/src/runtime/i18n/sq.ts @@ -217,7 +217,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Dërgimi i komandës dështoi", "prompt.toast.promptSendFailed.title": "Dërgimi i kërkesës dështoi", "dialog.mcp.title": "MCP-të", - "dialog.mcp.description": "{{enabled}} nga {{total}} është aktivizuar", "dialog.mcp.empty": "Asnjë MCP nuk është konfiguruar", "dialog.fork.empty": "Nuk ka mesazhe për të marrë nga", "dialog.directory.action.selectFile": "Zgjidhni skedarin", diff --git a/packages/app/src/runtime/i18n/sr.ts b/packages/app/src/runtime/i18n/sr.ts index fc9dc3f98c9..188c2d43685 100644 --- a/packages/app/src/runtime/i18n/sr.ts +++ b/packages/app/src/runtime/i18n/sr.ts @@ -230,7 +230,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Слање команде није успело", "prompt.toast.promptSendFailed.title": "Слање упита није успело", "dialog.mcp.title": "MCPс", - "dialog.mcp.description": "{{enabled}} од {{total}} омогућено", "dialog.mcp.empty": "Нема конфигурисаних MCP", "dialog.fork.empty": "Нема порука од којих треба да се рачвате", "dialog.directory.action.selectFile": "Изаберите датотеку", diff --git a/packages/app/src/runtime/i18n/sv.ts b/packages/app/src/runtime/i18n/sv.ts index 1f6329a35f0..358ae6f9149 100644 --- a/packages/app/src/runtime/i18n/sv.ts +++ b/packages/app/src/runtime/i18n/sv.ts @@ -415,7 +415,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Det gick inte att skicka kommandot", "prompt.toast.promptSendFailed.title": "Det gick inte att skicka meddelandet", "dialog.mcp.title": "MCP:er", - "dialog.mcp.description": "{{enabled}} av {{total}} aktiverade", "dialog.mcp.empty": "Inga MCP:er konfigurerade", "dialog.fork.empty": "Inga meddelanden att förgrena från", "dialog.directory.action.selectFile": "Välj fil", diff --git a/packages/app/src/runtime/i18n/tg.ts b/packages/app/src/runtime/i18n/tg.ts index 1765a59b105..bd12a6589bc 100644 --- a/packages/app/src/runtime/i18n/tg.ts +++ b/packages/app/src/runtime/i18n/tg.ts @@ -217,7 +217,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Фармон фиристода нашуд", "prompt.toast.promptSendFailed.title": "Ирсоли промпт муяссар нашуд", "dialog.mcp.title": "MCPс", - "dialog.mcp.description": "{{enabled}} аз {{total}} фаъол", "dialog.mcp.empty": "Ягон MCP танзим карда нашудааст", "dialog.fork.empty": "Паёме барои сохтани шоха нест", "dialog.directory.action.selectFile": "Файлро интихоб кунед", diff --git a/packages/app/src/runtime/i18n/th.ts b/packages/app/src/runtime/i18n/th.ts index 9f9ee1d7d0c..068d84fa03c 100644 --- a/packages/app/src/runtime/i18n/th.ts +++ b/packages/app/src/runtime/i18n/th.ts @@ -246,7 +246,6 @@ export const dict = { "prompt.toast.promptSendFailed.title": "ไม่สามารถส่งพรอมต์", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "{{enabled}} จาก {{total}} ที่เปิดใช้งาน", "dialog.mcp.empty": "ไม่มี MCP ที่กำหนดค่า", diff --git a/packages/app/src/runtime/i18n/tk.ts b/packages/app/src/runtime/i18n/tk.ts index 5692582c441..d9ef14138cd 100644 --- a/packages/app/src/runtime/i18n/tk.ts +++ b/packages/app/src/runtime/i18n/tk.ts @@ -217,7 +217,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Buýruk iberilmedi", "prompt.toast.promptSendFailed.title": "Prompt iberilmedi", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{total}}-iň {{enabled}} açyk", "dialog.mcp.empty": "Hiç hili MCP düzülmedi", "dialog.fork.empty": "Şahalandyrmaly habar ýok", "dialog.directory.action.selectFile": "Faýly saýlaň", diff --git a/packages/app/src/runtime/i18n/tr.ts b/packages/app/src/runtime/i18n/tr.ts index c6bf9dd1ffd..3d93bb80107 100644 --- a/packages/app/src/runtime/i18n/tr.ts +++ b/packages/app/src/runtime/i18n/tr.ts @@ -250,7 +250,6 @@ export const dict = { "prompt.toast.promptSendFailed.title": "İstem gönderilemedi", "dialog.mcp.title": "MCP'ler", - "dialog.mcp.description": "Toplam {{total}} öğeden {{enabled}} tanesi etkin", "dialog.mcp.empty": "Yapılandırılmış MCP yok", diff --git a/packages/app/src/runtime/i18n/uk.ts b/packages/app/src/runtime/i18n/uk.ts index 59f1f92681c..9502cf735da 100644 --- a/packages/app/src/runtime/i18n/uk.ts +++ b/packages/app/src/runtime/i18n/uk.ts @@ -266,7 +266,6 @@ export const dict = { "prompt.toast.promptSendFailed.title": "Не вдалося надіслати запит", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{enabled}} з {{total}} увімкнено", "dialog.mcp.empty": "MCP не налаштовано", diff --git a/packages/app/src/runtime/i18n/ur.ts b/packages/app/src/runtime/i18n/ur.ts index 8525f9d34ce..d9ec8f05d06 100644 --- a/packages/app/src/runtime/i18n/ur.ts +++ b/packages/app/src/runtime/i18n/ur.ts @@ -225,7 +225,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "کمانڈ بھیجنے میں ناکام", "prompt.toast.promptSendFailed.title": "پرامپٹ بھیجنے میں ناکام", "dialog.mcp.title": "MCPs", - "dialog.mcp.description": "\u2068{{total}}\u2069 میں سے \u2068{{enabled}}\u2069 فعال", "dialog.mcp.empty": "کوئی MCPs کنفیگر نہیں ہے۔", "dialog.fork.empty": "کوئی پیغامات نہیں ہیں جن سے فورک کیا جائے۔", "dialog.directory.action.selectFile": "فائل منتخب کریں۔", diff --git a/packages/app/src/runtime/i18n/uz.ts b/packages/app/src/runtime/i18n/uz.ts index 799203e8116..cd044277688 100644 --- a/packages/app/src/runtime/i18n/uz.ts +++ b/packages/app/src/runtime/i18n/uz.ts @@ -217,7 +217,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Buyruq yuborilmadi", "prompt.toast.promptSendFailed.title": "Soʻrov yuborilmadi", "dialog.mcp.title": "MCPlar", - "dialog.mcp.description": "{{enabled}}, {{total}} yoqilgan", "dialog.mcp.empty": "Hech qanday MCP sozlanmagan", "dialog.fork.empty": "Hech qanday xabar yo'q", "dialog.directory.action.selectFile": "Faylni tanlang", diff --git a/packages/app/src/runtime/i18n/vi.ts b/packages/app/src/runtime/i18n/vi.ts index 742422e96ce..299958b3ae8 100644 --- a/packages/app/src/runtime/i18n/vi.ts +++ b/packages/app/src/runtime/i18n/vi.ts @@ -230,7 +230,6 @@ export const dict = { "prompt.toast.commandSendFailed.title": "Không gửi được lệnh", "prompt.toast.promptSendFailed.title": "Không gửi được lời nhắc", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "{{enabled}} trong số {{total}} đã bật", "dialog.mcp.empty": "Không có MCP nào được định cấu hình", "dialog.fork.empty": "Không có tin nhắn nào để phân nhánh", "dialog.directory.action.selectFile": "Chọn tệp", diff --git a/packages/app/src/runtime/i18n/zh.ts b/packages/app/src/runtime/i18n/zh.ts index 51dcfa20ce5..e78ea4fc1e3 100644 --- a/packages/app/src/runtime/i18n/zh.ts +++ b/packages/app/src/runtime/i18n/zh.ts @@ -264,7 +264,6 @@ export const dict = { "prompt.toast.promptSendFailed.title": "发送提示失败", "dialog.mcp.title": "MCP 服务器", - "dialog.mcp.description": "已启用 {{enabled}} / {{total}}", "dialog.mcp.empty": "未配置 MCP 服务器", diff --git a/packages/app/src/runtime/i18n/zht.ts b/packages/app/src/runtime/i18n/zht.ts index 84346eb60da..4bcc04226b6 100644 --- a/packages/app/src/runtime/i18n/zht.ts +++ b/packages/app/src/runtime/i18n/zht.ts @@ -248,7 +248,6 @@ export const dict = { "prompt.toast.promptSendFailed.title": "傳送提示失敗", "dialog.mcp.title": "MCP", - "dialog.mcp.description": "已啟用 {{enabled}} / {{total}}", "dialog.mcp.empty": "未設定 MCP", diff --git a/packages/app/src/shell/search-list.css b/packages/app/src/shell/search-list.css index e2d564521df..4e93343353e 100644 --- a/packages/app/src/shell/search-list.css +++ b/packages/app/src/shell/search-list.css @@ -115,24 +115,101 @@ letter-spacing: -0.04px; } -.mcp-dialog .search-list-row { +[data-component="dialog-v2"]:has(.mcp-dialog) [data-slot="dialog-container"] { + width: min(calc(100vw - 24px), 480px); +} + +.mcp-dialog [data-slot="search-list-row"] { gap: 16px; } -.mcp-dialog .search-list-row-text { +.mcp-dialog-lead { + display: flex; + min-width: 0; flex: 1; + align-items: center; + gap: 10px; } -.mcp-dialog .search-list-title { - flex: 1; +.mcp-dialog-name { + color: var(--v2-text-text-base); + font-size: 13px; + font-weight: 530; + line-height: var(--line-height-compact); + letter-spacing: -0.04px; +} + +.mcp-dialog [data-status="disabled"] .mcp-dialog-name { + color: var(--v2-text-text-muted); +} + +/* The session MCP list's status dot: filled while on, a ring when disabled. */ +.mcp-dialog-dot { + width: 6px; + height: 6px; + flex-shrink: 0; + border-radius: 50%; + background: currentColor; + color: var(--v2-icon-icon-faint); +} + +.mcp-dialog-dot[data-status="connected"] { + color: var(--icon-success-base); +} + +.mcp-dialog-dot[data-status="failed"] { + color: var(--icon-critical-base); +} + +.mcp-dialog-dot[data-status="needs_auth"] { + color: var(--icon-warning-base); +} + +.mcp-dialog-dot[data-status="disabled"] { + background: transparent; + box-shadow: inset 0 0 0 1px currentColor; +} + +.mcp-dialog-dot[data-status="pending"] { + animation: mcp-dialog-pulse 1s ease-in-out infinite; +} + +@keyframes mcp-dialog-pulse { + 50% { + opacity: 0.35; + } } .mcp-trailing { + display: flex; + flex-shrink: 0; + align-items: center; justify-content: flex-end; gap: 8px; white-space: nowrap; } +/* A quiet ghost action in the switch's place, small enough to weigh about as much as a switch. */ +.mcp-status-action[data-component="button-v2"][data-size="small"] { + height: 20px; + padding-inline: 5px; + font-size: 11px; + font-weight: 440; + line-height: 16px; + letter-spacing: 0.05px; +} + +/* At the row's end, the label lines up with the switches above and below it. */ +.mcp-status-action[data-component="button-v2"]:last-child { + margin-inline-end: -5px; +} + +@media (prefers-reduced-motion: reduce) { + .mcp-dialog-dot[data-status="pending"] { + animation: none; + } +} + .mcp-error-heading { display: grid; min-width: 0; @@ -160,14 +237,9 @@ } @media (max-width: 639px), (hover: none) { - .mcp-dialog .search-list-row { + .mcp-dialog [data-slot="search-list-row"] { height: auto; min-height: 44px; padding-block: 8px; } - - .mcp-dialog .search-list-title { - white-space: normal; - overflow-wrap: anywhere; - } } diff --git a/packages/gui-extensions/src/details/details.css b/packages/gui-extensions/src/details/details.css index 72d36a2d313..f12da4f62da 100644 --- a/packages/gui-extensions/src/details/details.css +++ b/packages/gui-extensions/src/details/details.css @@ -409,6 +409,17 @@ button.session-service-dot { white-space: nowrap; } +/* A quiet ghost action in the switch's place, as in the MCPs dialog. */ +.session-mcp-sign-in[data-component="button-v2"][data-size="small"] { + height: 20px; + margin-inline-end: -5px; + padding-inline: 5px; + font-size: 11px; + font-weight: 440; + line-height: 16px; + letter-spacing: 0.05px; +} + div.session-mcp-row { cursor: pointer; } diff --git a/packages/gui-extensions/src/details/server-panel.tsx b/packages/gui-extensions/src/details/server-panel.tsx index 8c718609226..c6ebf48bbd9 100644 --- a/packages/gui-extensions/src/details/server-panel.tsx +++ b/packages/gui-extensions/src/details/server-panel.tsx @@ -655,7 +655,8 @@ function useMcp(props: ServiceProps, close: () => void): ServiceSource { >