diff --git a/src/components/tools/ToolsTable.test.tsx b/src/components/tools/ToolsTable.test.tsx
index bf2f867..a481218 100644
--- a/src/components/tools/ToolsTable.test.tsx
+++ b/src/components/tools/ToolsTable.test.tsx
@@ -170,7 +170,7 @@ describe("ToolsTable", () => {
const tools = [createMockTool(1)];
render();
- const copyButton = screen.getByLabelText("Copy tool ID");
+ const copyButton = screen.getByLabelText("Copy tool ID for Display Name 1");
expect(copyButton).toBeInTheDocument();
});
@@ -179,7 +179,7 @@ describe("ToolsTable", () => {
const tools = [createMockTool(1, { id: "tool-abc-123" })];
render();
- const copyButtons = screen.getAllByLabelText("Copy tool ID");
+ const copyButtons = screen.getAllByLabelText("Copy tool ID for Display Name 1");
await user.click(copyButtons[0]);
expect(gatewayUtils.copyToClipboard).toHaveBeenCalledWith("tool-abc-123");
diff --git a/src/components/tools/ToolsTable.tsx b/src/components/tools/ToolsTable.tsx
index 9db952e..da3685b 100644
--- a/src/components/tools/ToolsTable.tsx
+++ b/src/components/tools/ToolsTable.tsx
@@ -113,7 +113,10 @@ export function ToolsTable({
diff --git a/src/components/ui/code-block.test.tsx b/src/components/ui/code-block.test.tsx
index 0ea98f4..0294637 100644
--- a/src/components/ui/code-block.test.tsx
+++ b/src/components/ui/code-block.test.tsx
@@ -10,13 +10,13 @@ describe("CodeBlock", () => {
expect(screen.getByText('"a"')).toBeInTheDocument();
});
- it("copies the code to the clipboard and shows the copied tooltip when the copy button is clicked", async () => {
+ it("copies the code to the clipboard and announces the copied status when the copy button is clicked", async () => {
const user = userEvent.setup();
const writeText = vi.spyOn(navigator.clipboard, "writeText").mockResolvedValue();
render();
await user.click(screen.getByRole("button", { name: /bash/i }));
expect(writeText).toHaveBeenCalledWith("hello world");
- expect(screen.getByRole("tooltip")).toHaveTextContent("Copied!");
+ expect(screen.getByRole("status")).toHaveTextContent("Copied!");
});
it("hides the copy button when hideCopy is set", () => {
diff --git a/src/components/ui/code-block.tsx b/src/components/ui/code-block.tsx
index 0d8475f..067ec49 100644
--- a/src/components/ui/code-block.tsx
+++ b/src/components/ui/code-block.tsx
@@ -1,4 +1,5 @@
import { Highlight, themes, type Language } from "prism-react-renderer";
+import { useIntl } from "react-intl";
import { CopyButton } from "@/components/ui/copy-button";
import { cn } from "@/lib/utils";
@@ -8,12 +9,10 @@ export type CodeBlockLanguage = "bash" | "json" | "python" | "tsx" | "markdown"
export interface CodeBlockProps {
code: string;
language: CodeBlockLanguage;
- /** Label for the Copy button, used as both its aria-label and idle tooltip. */
+ /** Accessible label for the Copy button. Defaults to a generic "Copy code". */
copyLabel?: string;
/** Hide the built-in Copy affordance. */
hideCopy?: boolean;
- /** aria-label fallback when `copyLabel` is not supplied. */
- copyAriaLabel?: string;
className?: string;
/** Pre-element padding override (defaults to p-4). */
padding?: string;
@@ -45,12 +44,12 @@ export function CodeBlock({
language,
copyLabel,
hideCopy = false,
- copyAriaLabel,
className,
padding = "p-4",
}: CodeBlockProps) {
+ const intl = useIntl();
const prismLanguage = TOKEN_LANGUAGE[language];
- const ariaLabel = copyLabel ?? copyAriaLabel ?? "Copy code";
+ const ariaLabel = copyLabel ?? intl.formatMessage({ id: "common.copyCode" });
return (
diff --git a/src/components/ui/copy-button.test.tsx b/src/components/ui/copy-button.test.tsx
index f1b0626..c38b6f9 100644
--- a/src/components/ui/copy-button.test.tsx
+++ b/src/components/ui/copy-button.test.tsx
@@ -9,13 +9,30 @@ describe("CopyButton", () => {
vi.clearAllMocks();
});
- it("keeps the idle label as both aria-label and hover tooltip before any click", async () => {
+ it("keeps aria-label fixed and shows no bubble on hover", async () => {
const user = userEvent.setup();
render();
const button = screen.getByRole("button", { name: "Copy resource ID" });
await user.hover(button);
- expect(await screen.findByRole("tooltip")).toHaveTextContent("Copy resource ID");
+
+ expect(screen.queryByText("Copy resource ID")).not.toBeInTheDocument();
+ expect(screen.getByRole("status")).toHaveTextContent("");
+ });
+
+ it("shows no bubble on focus", () => {
+ render();
+
+ const button = screen.getByRole("button", { name: "Copy resource ID" });
+ button.focus();
+
+ expect(screen.getByRole("status")).toHaveTextContent("");
+ });
+
+ it("mounts the status region before any copy has happened", () => {
+ render();
+ expect(screen.getAllByRole("status")).toHaveLength(1);
+ expect(screen.getByRole("status")).toHaveTextContent("");
});
it("copies the value, shows the Check icon, and announces the copied label once", async () => {
@@ -34,6 +51,17 @@ describe("CopyButton", () => {
expect(screen.getByRole("status")).toHaveTextContent("Copied!");
});
+ it("shows the confirmation bubble on click, hidden from assistive tech", async () => {
+ const user = userEvent.setup();
+ vi.spyOn(navigator.clipboard, "writeText").mockResolvedValue();
+ render();
+
+ await user.click(screen.getByRole("button", { name: "Copy resource ID" }));
+
+ const bubble = screen.getByText("Copied!", { selector: "span[aria-hidden='true']" });
+ expect(bubble).toBeInTheDocument();
+ });
+
it("stops the click from bubbling to an ancestor (e.g. a clickable table row)", async () => {
const user = userEvent.setup();
vi.spyOn(navigator.clipboard, "writeText").mockResolvedValue();
diff --git a/src/components/ui/copy-button.tsx b/src/components/ui/copy-button.tsx
index b393c22..e6d5b02 100644
--- a/src/components/ui/copy-button.tsx
+++ b/src/components/ui/copy-button.tsx
@@ -1,33 +1,34 @@
-import { useState } from "react";
+import { createPortal } from "react-dom";
import { Check, Copy } from "lucide-react";
import { useIntl } from "react-intl";
+import { useFloating, offset, flip, shift } from "@floating-ui/react-dom";
import type { VariantProps } from "class-variance-authority";
import { Button, buttonVariants } from "@/components/ui/button";
-import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { useCopyToClipboard } from "@/hooks/useCopyToClipboard";
import { cn } from "@/lib/utils";
export interface CopyButtonProps {
/** The value written to the clipboard. */
value: string;
- /** Accessible label and idle hover-tooltip text, e.g. "Copy resource ID". */
+ /** Accessible label, e.g. "Copy resource ID". Not shown on hover or focus. */
label: string;
size?: VariantProps["size"];
/** Icon size class. Defaults to the size most call sites use. */
iconClassName?: string;
className?: string;
- side?: "top" | "right" | "bottom" | "left";
}
/**
* Icon button that copies `value` to the clipboard and confirms it happened.
*
- * The tooltip stays uncontrolled on hover/focus (so the ordinary "Copy X"
- * hint keeps working) but is force-opened on copy, so touch users — who never
- * fire a hover event — still see the confirmation. `aria-label` stays fixed
- * at `label`; a single `role="status"` region announces the transient
- * copied/failed state instead, so the two don't double-announce.
+ * The confirmation bubble appears on click only — hover and focus show
+ * nothing, so it never fights a dialog's Escape handling (see
+ * react-dismissable-layer). It's a plain `aria-hidden` span positioned with
+ * `@floating-ui/react-dom` and portaled to `document.body` so it can't clip
+ * inside a scrolling table or dialog. `aria-label` stays fixed at `label`;
+ * a single always-mounted `role="status"` region is the one non-visual
+ * announcement channel for the transient copied/failed state.
*/
export function CopyButton({
value,
@@ -35,46 +36,55 @@ export function CopyButton({
size = "icon-xs",
iconClassName = "size-3.5",
className,
- side = "top",
}: CopyButtonProps) {
const intl = useIntl();
- const [hoverOpen, setHoverOpen] = useState(false);
const { status, copy } = useCopyToClipboard();
const copied = status === "copied";
const failed = status === "error";
const copiedLabel = intl.formatMessage({ id: "common.copied" });
const failedLabel = intl.formatMessage({ id: "common.copyFailed" });
- const tooltipLabel = copied ? copiedLabel : failed ? failedLabel : label;
+
+ const { refs, floatingStyles } = useFloating({
+ placement: "top",
+ middleware: [offset(6), flip(), shift({ padding: 8 })],
+ });
return (
<>
-
-
-
-
- {tooltipLabel}
-
- {(copied || failed) && (
-
- {copied ? copiedLabel : failedLabel}
-
- )}
+ {copied ? copiedLabel : failedLabel}
+ ,
+ document.body,
+ )}
+ {/* Always mounted, so the text change on copy is what gets announced. */}
+
+ {copied ? copiedLabel : failed ? failedLabel : ""}
+
>
);
}
diff --git a/src/i18n/locales/en-US/common.json b/src/i18n/locales/en-US/common.json
index adc2a1e..4ea9766 100644
--- a/src/i18n/locales/en-US/common.json
+++ b/src/i18n/locales/en-US/common.json
@@ -11,6 +11,7 @@
"common.button.back": "Back",
"common.button.submitting": "Submitting...",
"common.copyValue": "Copy {label}",
+ "common.copyCode": "Copy code",
"common.copied": "Copied!",
"common.copyFailed": "Copy failed",
"common.loading": "Loading...",
diff --git a/src/i18n/locales/en-US/gateways.json b/src/i18n/locales/en-US/gateways.json
index d958d75..e2087ce 100644
--- a/src/i18n/locales/en-US/gateways.json
+++ b/src/i18n/locales/en-US/gateways.json
@@ -113,6 +113,9 @@
"gateways.details.component.tools": "tool",
"gateways.details.component.resources": "resource",
"gateways.details.component.prompts": "prompt",
+ "gateways.details.component.copyName.tools": "Copy tool name for {name}",
+ "gateways.details.component.copyName.resources": "Copy URI for {name}",
+ "gateways.details.component.copyName.prompts": "Copy prompt name for {name}",
"gateways.details.noComponentsFound": "No components found.",
"gateways.details.noFilteredComponentsFound": "No {filter} found.",
"gateways.details.close": "Close virtual server details",
@@ -132,6 +135,7 @@
"gateways.details.lastModified": "Last modified",
"gateways.details.copyLabel": "Copy {label}",
"gateways.details.serverIdCopyLabel": "server ID",
+ "gateways.details.urlCopyLabel": "URL",
"gateways.details.actionsFor": "Actions for {name}",
"gateways.details.notAvailable": "N/A",
"gateways.details.tagFallback": "Tag"
diff --git a/src/i18n/locales/en-US/mcpServer.json b/src/i18n/locales/en-US/mcpServer.json
index 0c398f3..0eaa16d 100644
--- a/src/i18n/locales/en-US/mcpServer.json
+++ b/src/i18n/locales/en-US/mcpServer.json
@@ -43,6 +43,14 @@
"mcpServer.table.components.resources": "{count, plural, one {# resource} other {# resources}}",
"mcpServer.table.components.prompts": "{count, plural, one {# prompt} other {# prompts}}",
"mcpServer.table.copyUuid": "Copy UUID for {name}",
+ "mcpServer.details.uuid": "UUID",
+ "mcpServer.details.url": "URL",
+ "mcpServer.details.component.tools": "tool",
+ "mcpServer.details.component.resources": "resource",
+ "mcpServer.details.component.prompts": "prompt",
+ "mcpServer.details.component.copyName.tools": "Copy tool name for {name}",
+ "mcpServer.details.component.copyName.resources": "Copy URI for {name}",
+ "mcpServer.details.component.copyName.prompts": "Copy prompt name for {name}",
"mcpServer.table.actions.label": "Actions for {name}",
"mcpServer.table.actions.openMenu": "Open menu for {name}",
"mcpServer.table.actions.viewDetails": "View details",
diff --git a/src/i18n/locales/en-US/resources.json b/src/i18n/locales/en-US/resources.json
index e8b431a..9092264 100644
--- a/src/i18n/locales/en-US/resources.json
+++ b/src/i18n/locales/en-US/resources.json
@@ -57,8 +57,8 @@
"resources.table.resource": "Resource",
"resources.table.uri": "URI",
"resources.table.resourceId": "Resource ID",
- "resources.table.copyUri": "Copy {uri}",
- "resources.table.copyResourceId": "Copy resource ID",
+ "resources.table.copyUri": "Copy URI for {name}",
+ "resources.table.copyResourceId": "Copy resource ID for {name}",
"resources.table.moreOptionsFor": "More options for {name}",
"resources.table.edit": "Edit",
"resources.table.delete": "Delete",
@@ -98,6 +98,7 @@
"resources.details.preview.openInNewTab": "Open in new tab",
"resources.details.preview.downloadRaw": "Download raw",
"resources.details.preview.imageAlt": "Previewed {mimeType} image, {size}",
+ "resources.details.preview.contentFallback": "content",
"resources.details.label.status": "Status",
"resources.details.label.visibility": "Visibility",
"resources.details.label.type": "Type",
diff --git a/src/i18n/locales/en-US/tools.json b/src/i18n/locales/en-US/tools.json
index da298c4..7dbdde5 100644
--- a/src/i18n/locales/en-US/tools.json
+++ b/src/i18n/locales/en-US/tools.json
@@ -21,8 +21,10 @@
"tools.table.toolId": "Tool ID",
"tools.table.schema": "Schema",
"tools.table.copyName": "Copy {name}",
- "tools.table.copyToolId": "Copy tool ID",
+ "tools.table.copyToolId": "Copy tool ID for {name}",
"tools.table.viewSchema": "View schema",
+ "tools.schemaDialog.input": "Input",
+ "tools.schemaDialog.output": "Output",
"tools.table.moreOptions": "More options",
"tools.table.edit": "Edit",
"tools.table.activate": "Activate",
diff --git a/src/i18n/locales/es-ES/common.json b/src/i18n/locales/es-ES/common.json
index 20d4251..a698c6a 100644
--- a/src/i18n/locales/es-ES/common.json
+++ b/src/i18n/locales/es-ES/common.json
@@ -11,6 +11,7 @@
"common.button.back": "Volver",
"common.button.submitting": "Enviando...",
"common.copyValue": "Copiar {label}",
+ "common.copyCode": "Copiar código",
"common.copied": "¡Copiado!",
"common.copyFailed": "Error al copiar",
"common.loading": "Cargando...",
diff --git a/src/i18n/locales/es-ES/gateways.json b/src/i18n/locales/es-ES/gateways.json
index 1ed9476..f17825a 100644
--- a/src/i18n/locales/es-ES/gateways.json
+++ b/src/i18n/locales/es-ES/gateways.json
@@ -113,6 +113,9 @@
"gateways.details.component.tools": "herramienta",
"gateways.details.component.resources": "recurso",
"gateways.details.component.prompts": "prompt",
+ "gateways.details.component.copyName.tools": "Copiar nombre de la herramienta de {name}",
+ "gateways.details.component.copyName.resources": "Copiar URI de {name}",
+ "gateways.details.component.copyName.prompts": "Copiar nombre del prompt de {name}",
"gateways.details.noComponentsFound": "No se encontraron componentes.",
"gateways.details.noFilteredComponentsFound": "No se encontraron {filter}.",
"gateways.details.close": "Cerrar detalles del servidor virtual",
@@ -132,6 +135,7 @@
"gateways.details.lastModified": "Última modificación",
"gateways.details.copyLabel": "Copiar {label}",
"gateways.details.serverIdCopyLabel": "ID del servidor",
+ "gateways.details.urlCopyLabel": "URL",
"gateways.details.actionsFor": "Acciones para {name}",
"gateways.details.notAvailable": "N/D",
"gateways.details.tagFallback": "Etiqueta"
diff --git a/src/i18n/locales/es-ES/mcpServer.json b/src/i18n/locales/es-ES/mcpServer.json
index 28b9edb..adaeccb 100644
--- a/src/i18n/locales/es-ES/mcpServer.json
+++ b/src/i18n/locales/es-ES/mcpServer.json
@@ -43,6 +43,14 @@
"mcpServer.table.components.resources": "{count, plural, one {# recurso} other {# recursos}}",
"mcpServer.table.components.prompts": "{count, plural, one {# prompt} other {# prompts}}",
"mcpServer.table.copyUuid": "Copiar UUID de {name}",
+ "mcpServer.details.uuid": "UUID",
+ "mcpServer.details.url": "URL",
+ "mcpServer.details.component.tools": "herramienta",
+ "mcpServer.details.component.resources": "recurso",
+ "mcpServer.details.component.prompts": "prompt",
+ "mcpServer.details.component.copyName.tools": "Copiar nombre de la herramienta de {name}",
+ "mcpServer.details.component.copyName.resources": "Copiar URI de {name}",
+ "mcpServer.details.component.copyName.prompts": "Copiar nombre del prompt de {name}",
"mcpServer.table.actions.label": "Acciones para {name}",
"mcpServer.table.actions.openMenu": "Abrir menú de {name}",
"mcpServer.table.actions.viewDetails": "Ver detalles",
diff --git a/src/i18n/locales/es-ES/resources.json b/src/i18n/locales/es-ES/resources.json
index e404905..dcc34df 100644
--- a/src/i18n/locales/es-ES/resources.json
+++ b/src/i18n/locales/es-ES/resources.json
@@ -57,8 +57,8 @@
"resources.table.resource": "Recurso",
"resources.table.uri": "URI",
"resources.table.resourceId": "ID del recurso",
- "resources.table.copyUri": "Copiar {uri}",
- "resources.table.copyResourceId": "Copiar ID del recurso",
+ "resources.table.copyUri": "Copiar URI de {name}",
+ "resources.table.copyResourceId": "Copiar ID del recurso de {name}",
"resources.table.moreOptionsFor": "Más opciones para {name}",
"resources.table.edit": "Editar",
"resources.table.delete": "Eliminar",
@@ -98,6 +98,7 @@
"resources.details.preview.openInNewTab": "Abrir en una pestaña nueva",
"resources.details.preview.downloadRaw": "Descargar sin procesar",
"resources.details.preview.imageAlt": "Imagen {mimeType} obtenida en la vista previa, {size}",
+ "resources.details.preview.contentFallback": "contenido",
"resources.details.label.status": "Estado",
"resources.details.label.visibility": "Visibilidad",
"resources.details.label.type": "Tipo",
diff --git a/src/i18n/locales/es-ES/tools.json b/src/i18n/locales/es-ES/tools.json
index 652aba0..46ed52c 100644
--- a/src/i18n/locales/es-ES/tools.json
+++ b/src/i18n/locales/es-ES/tools.json
@@ -21,8 +21,10 @@
"tools.table.toolId": "ID de herramienta",
"tools.table.schema": "Esquema",
"tools.table.copyName": "Copiar {name}",
- "tools.table.copyToolId": "Copiar ID de herramienta",
+ "tools.table.copyToolId": "Copiar ID de herramienta de {name}",
"tools.table.viewSchema": "Ver esquema",
+ "tools.schemaDialog.input": "Entrada",
+ "tools.schemaDialog.output": "Salida",
"tools.table.moreOptions": "Más opciones",
"tools.table.edit": "Editar",
"tools.table.activate": "Activar",
diff --git a/src/i18n/locales/pt-BR/common.json b/src/i18n/locales/pt-BR/common.json
index 55dcda2..2840541 100644
--- a/src/i18n/locales/pt-BR/common.json
+++ b/src/i18n/locales/pt-BR/common.json
@@ -11,6 +11,7 @@
"common.button.back": "Voltar",
"common.button.submitting": "Enviando...",
"common.copyValue": "Copiar {label}",
+ "common.copyCode": "Copiar código",
"common.copied": "Copiado!",
"common.copyFailed": "Falha ao copiar",
"common.loading": "Carregando...",
diff --git a/src/i18n/locales/pt-BR/gateways.json b/src/i18n/locales/pt-BR/gateways.json
index bc3d130..ababdcf 100644
--- a/src/i18n/locales/pt-BR/gateways.json
+++ b/src/i18n/locales/pt-BR/gateways.json
@@ -113,6 +113,9 @@
"gateways.details.component.tools": "ferramenta",
"gateways.details.component.resources": "recurso",
"gateways.details.component.prompts": "prompt",
+ "gateways.details.component.copyName.tools": "Copiar nome da ferramenta de {name}",
+ "gateways.details.component.copyName.resources": "Copiar URI de {name}",
+ "gateways.details.component.copyName.prompts": "Copiar nome do prompt de {name}",
"gateways.details.noComponentsFound": "Nenhum componente encontrado.",
"gateways.details.noFilteredComponentsFound": "Nenhum {filter} encontrado.",
"gateways.details.close": "Fechar detalhes do servidor virtual",
@@ -132,6 +135,7 @@
"gateways.details.lastModified": "Última modificação",
"gateways.details.copyLabel": "Copiar {label}",
"gateways.details.serverIdCopyLabel": "ID do servidor",
+ "gateways.details.urlCopyLabel": "URL",
"gateways.details.actionsFor": "Ações para {name}",
"gateways.details.notAvailable": "N/D",
"gateways.details.tagFallback": "Tag"
diff --git a/src/i18n/locales/pt-BR/mcpServer.json b/src/i18n/locales/pt-BR/mcpServer.json
index 0555bdf..e6a1fac 100644
--- a/src/i18n/locales/pt-BR/mcpServer.json
+++ b/src/i18n/locales/pt-BR/mcpServer.json
@@ -43,6 +43,14 @@
"mcpServer.table.components.resources": "{count, plural, one {# recurso} other {# recursos}}",
"mcpServer.table.components.prompts": "{count, plural, one {# prompt} other {# prompts}}",
"mcpServer.table.copyUuid": "Copiar UUID de {name}",
+ "mcpServer.details.uuid": "UUID",
+ "mcpServer.details.url": "URL",
+ "mcpServer.details.component.tools": "ferramenta",
+ "mcpServer.details.component.resources": "recurso",
+ "mcpServer.details.component.prompts": "prompt",
+ "mcpServer.details.component.copyName.tools": "Copiar nome da ferramenta de {name}",
+ "mcpServer.details.component.copyName.resources": "Copiar URI de {name}",
+ "mcpServer.details.component.copyName.prompts": "Copiar nome do prompt de {name}",
"mcpServer.table.actions.label": "Ações para {name}",
"mcpServer.table.actions.openMenu": "Abrir menu de {name}",
"mcpServer.table.actions.viewDetails": "Ver detalhes",
diff --git a/src/i18n/locales/pt-BR/resources.json b/src/i18n/locales/pt-BR/resources.json
index 875e9e8..2de4eb4 100644
--- a/src/i18n/locales/pt-BR/resources.json
+++ b/src/i18n/locales/pt-BR/resources.json
@@ -57,8 +57,8 @@
"resources.table.resource": "Recurso",
"resources.table.uri": "URI",
"resources.table.resourceId": "ID do recurso",
- "resources.table.copyUri": "Copiar {uri}",
- "resources.table.copyResourceId": "Copiar ID do recurso",
+ "resources.table.copyUri": "Copiar URI de {name}",
+ "resources.table.copyResourceId": "Copiar ID do recurso de {name}",
"resources.table.moreOptionsFor": "Mais opções para {name}",
"resources.table.edit": "Editar",
"resources.table.delete": "Excluir",
@@ -98,6 +98,7 @@
"resources.details.preview.openInNewTab": "Abrir em nova aba",
"resources.details.preview.downloadRaw": "Baixar bruto",
"resources.details.preview.imageAlt": "Imagem {mimeType} da pré-visualização, {size}",
+ "resources.details.preview.contentFallback": "conteúdo",
"resources.details.label.status": "Status",
"resources.details.label.visibility": "Visibilidade",
"resources.details.label.type": "Tipo",
diff --git a/src/i18n/locales/pt-BR/tools.json b/src/i18n/locales/pt-BR/tools.json
index 06b9819..59bbf4d 100644
--- a/src/i18n/locales/pt-BR/tools.json
+++ b/src/i18n/locales/pt-BR/tools.json
@@ -21,8 +21,10 @@
"tools.table.toolId": "ID da ferramenta",
"tools.table.schema": "Esquema",
"tools.table.copyName": "Copiar {name}",
- "tools.table.copyToolId": "Copiar ID da ferramenta",
+ "tools.table.copyToolId": "Copiar ID da ferramenta de {name}",
"tools.table.viewSchema": "Ver esquema",
+ "tools.schemaDialog.input": "Entrada",
+ "tools.schemaDialog.output": "Saída",
"tools.table.moreOptions": "Mais opções",
"tools.table.edit": "Editar",
"tools.table.activate": "Ativar",