fix(web): reset a tool form by row identity, not by tool name

Copilot review round 4 on #2109. A `tools/list` may repeat a name (#1957) and
ToolsScreen already disambiguates those rows with a key of its own, but the
detail panel handed SchemaForm only the name — so switching between two
same-named tools looked like the same entity and left the first one's enlarged
fields (and its in-progress number/JSON text, which predates this PR) in place.

ToolDetailPanel now takes an optional `resetKey`, defaulting to the name for
callers whose list cannot repeat one, and ToolsScreen passes the row key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JTHVxSu8AUgHRLvo1ntZ8H
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-24 10:18:55 -04:00
commit d47071d535
2 changed files with 52 additions and 4 deletions

View file

@ -82,6 +82,41 @@ describe("ToolsScreen", () => {
).not.toBeInTheDocument();
});
it("drops an enlarged field when switching between duplicated tool names", async () => {
// The panel is reused across selections, so SchemaForm resets per-field
// state on `resetKey`. That key must be the ROW identity: two rows sharing
// a name would otherwise look like the same entity, and the first copy's
// enlarged field would stay enlarged over the second copy's (#2042/#1957).
const user = userEvent.setup();
const duplicated: Tool[] = [
{
name: "get_weather",
inputSchema: {
type: "object",
properties: { city: { type: "string", title: "City" } },
},
},
{
name: "get_weather",
inputSchema: {
type: "object",
properties: { city: { type: "string", title: "City" } },
},
},
];
renderWithMantine(<ControlledToolsScreen tools={duplicated} />);
const rows = screen.getAllByText("get_weather");
await user.click(rows[0] as HTMLElement);
await user.click(screen.getByRole("button", { name: "Enlarge City" }));
expect(screen.getByRole("textbox", { name: /City/ }).tagName).toBe(
"TEXTAREA",
);
await user.click(rows[1] as HTMLElement);
expect(screen.getByRole("textbox", { name: /City/ }).tagName).toBe("INPUT");
});
it("opens the second copy of a duplicated tool name and calls it by its protocol name", async () => {
// A `tools/list` may repeat a name with a different schema. Selection is
// keyed by row identity, so the later copy is reachable and the detail

View file

@ -258,6 +258,9 @@ export function ToolsScreen({
<ContentCard>
<ToolDetailPanel
tool={selectedTool}
// The row key, not the name — a server may list the same tool
// name twice (#1957), and the panel is reused across selections.
resetKey={selectedToolKey}
formValues={formValues}
isExecuting={isExecuting}
progress={callState?.progress}