diff --git a/web/src/ChatStream.test.tsx b/web/src/ChatStream.test.tsx index 0578c6a..00178a7 100644 --- a/web/src/ChatStream.test.tsx +++ b/web/src/ChatStream.test.tsx @@ -63,10 +63,7 @@ function stream(p: { describe("Bubble", () => { it("renders plain text per role class", () => { const { container } = render( - , + , ); expect(container.querySelector(".bubble-row.user")).not.toBeNull(); expect(container.textContent).toContain("hi there"); @@ -94,10 +91,7 @@ describe("Bubble", () => { it("toolResult collapses to a one-line preview, expands to full text", async () => { const long = `${"x".repeat(200)}`; render( - , + , ); const details = screen .getByText("result") @@ -160,9 +154,7 @@ describe("Bubble", () => { expect(screen.getByText("🛠 bash")).toBeInTheDocument(); expect(screen.getByText("finished")).toBeInTheDocument(); - const summary = screen - .getByText("🛠 bash") - .closest("summary") as HTMLElement; + const summary = screen.getByText("🛠 bash").closest("summary") as HTMLElement; const card = summary.closest("details") as HTMLDetailsElement; expect(card.open).toBe(false); await userEvent.click(summary); @@ -256,9 +248,7 @@ describe("ChatStream", () => { }), ); expect(container.querySelector(".typing")).toBeNull(); - rerender( - stream({ messages: [msg({ key: "a", text: "one" })], busy: false }), - ); + rerender(stream({ messages: [msg({ key: "a", text: "one" })], busy: false })); expect(container.querySelector(".typing")).toBeNull(); }); @@ -767,15 +757,34 @@ describe("copy button", () => { describe("pretty tool args", () => { const tool = (args: string): ToolState => ({ - id: "t1", name: "bash", args, running: false, isError: false, preview: "out", + id: "t1", + name: "bash", + args, + running: false, + isError: false, + preview: "out", }); it("bash command renders as the bare command, no JSON braces", () => { - render(); + render( + , + ); expect(document.querySelector(".tool-summary")?.textContent).toBe("ls -la"); - expect(document.querySelector(".tool-args")?.textContent).not.toContain('"command"'); + expect(document.querySelector(".tool-args")?.textContent).not.toContain( + '"command"', + ); }); it("priority ordering puts command first even when not first in JSON", () => { @@ -785,10 +794,24 @@ describe("pretty tool args", () => { }); it("single string arg renders label-less; multi-field keeps labels", () => { - render(); - expect(document.querySelector(".tool-summary")?.textContent).toBe("src/main.ts"); + render( + , + ); + expect(document.querySelector(".tool-summary")?.textContent).toBe( + "src/main.ts", + ); expect(document.querySelector(".tool-args .arg-k")).toBeNull(); }); @@ -839,7 +862,9 @@ describe("tool summary + diff", () => { tools={new Map([["t1", tool('{"path":"src/main.ts"}', "read")]])} />, ); - expect(document.querySelector(".tool-summary")?.textContent).toBe("src/main.ts"); + expect(document.querySelector(".tool-summary")?.textContent).toBe( + "src/main.ts", + ); }); it("long summary is truncated with ellipsis", () => { @@ -863,20 +888,28 @@ describe("tool summary + diff", () => { msg={msg({ toolCalls: [{ id: "t1", name: "edit", argsJson: "{}" }], })} - tools={new Map([ - [ - "t1", - tool( - '{"path":"a.ts","edits":[{"oldText":"const a = 1;\\nconst b = 2;","newText":"const a = 3;\\nconst b = 2;"}]}', - "edit", - ), - ], - ])} + tools={ + new Map([ + [ + "t1", + tool( + '{"path":"a.ts","edits":[{"oldText":"const a = 1;\\nconst b = 2;","newText":"const a = 3;\\nconst b = 2;"}]}', + "edit", + ), + ], + ]) + } />, ); - expect(document.querySelector(".diff-del")?.textContent).toContain("-const a = 1;"); - expect(document.querySelector(".diff-add")?.textContent).toContain("+const a = 3;"); - expect(document.querySelector(".diff-ctx")?.textContent).toContain(" const b = 2;"); + expect(document.querySelector(".diff-del")?.textContent).toContain( + "-const a = 1;", + ); + expect(document.querySelector(".diff-add")?.textContent).toContain( + "+const a = 3;", + ); + expect(document.querySelector(".diff-ctx")?.textContent).toContain( + " const b = 2;", + ); expect(document.querySelector(".tool-args")).toBeNull(); }); @@ -886,9 +919,11 @@ describe("tool summary + diff", () => { msg={msg({ toolCalls: [{ id: "t1", name: "write", argsJson: "{}" }], })} - tools={new Map([ - ["t1", tool('{"path":"new.ts","content":"hello\\nworld"}', "write")], - ])} + tools={ + new Map([ + ["t1", tool('{"path":"new.ts","content":"hello\\nworld"}', "write")], + ]) + } />, ); const adds = document.querySelectorAll(".diff-add"); diff --git a/web/src/index.css b/web/src/index.css index c0420fa..38905ec 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -25,8 +25,7 @@ --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.35); --shadow-2: 0 8px 28px rgba(0, 0, 0, 0.45); font-family: - ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Inter, - sans-serif; + ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Inter, sans-serif; } * { @@ -1445,24 +1444,37 @@ mark.hit { /* ---------- busy activity pip ---------- */ .busy-pip { - display: inline-block; - width: 8px; - height: 8px; - margin-left: 7px; - border-radius: 50%; - background: var(--accent); - animation: busy-pulse 1.1s ease-in-out infinite; - vertical-align: middle; + display: inline-block; + width: 8px; + height: 8px; + margin-left: 7px; + border-radius: 50%; + background: var(--accent); + animation: busy-pulse 1.1s ease-in-out infinite; + vertical-align: middle; } @keyframes busy-pulse { - 0%, 100% { transform: scale(0.55); opacity: 0.45; box-shadow: 0 0 0 0 rgba(108, 140, 255, 0.5); } - 50% { transform: scale(1); opacity: 1; box-shadow: 0 0 0 5px rgba(108, 140, 255, 0); } + 0%, + 100% { + transform: scale(0.55); + opacity: 0.45; + box-shadow: 0 0 0 0 rgba(108, 140, 255, 0.5); + } + 50% { + transform: scale(1); + opacity: 1; + box-shadow: 0 0 0 5px rgba(108, 140, 255, 0); + } } /* ---------- pretty tool args ---------- */ -.tool-args { margin-bottom: 10px; } -.tool-arg { margin-bottom: 6px; } +.tool-args { + margin-bottom: 10px; +} +.tool-arg { + margin-bottom: 6px; +} .arg-k { display: block; font-size: 10.5px;