diff --git a/frontend/src/shell/AppShell.session-mgmt.test.tsx b/frontend/src/shell/AppShell.session-mgmt.test.tsx index bc47645a..a8384491 100644 --- a/frontend/src/shell/AppShell.session-mgmt.test.tsx +++ b/frontend/src/shell/AppShell.session-mgmt.test.tsx @@ -99,9 +99,10 @@ test("WIP icon toggles the Model activity panel and shows the streamed text", as server.use(http.post("http://localhost:8787/sessions/:id/resume", () => new HttpResponse(null, { status: 204 }))); await userEvent.click(await screen.findByText("Attiva uno")); // opens docs panel await userEvent.click(await screen.findByRole("button", { name: /resume/i })); // active session - act(() => { useSessionStore.getState().applyEvent({ type: "text_delta", text: "FIRSTLINE alpha bravo charlie delta echo foxtrot golf hotel india juliet kilo" }); }); + const SIX_LINES = ["FIRSTLINE step one", "step two", "step three", "step four", "step five", "step six"].join("\n"); + act(() => { useSessionStore.getState().applyEvent({ type: "text_delta", text: SIX_LINES }); }); // The verbose stream stays in the (closed) left panel: its START is not shown centrally - // (the central working indicator shows only a short tail of last words, by design). + // (the central working indicator shows only the last 5 lines of tail, by design). expect(screen.queryByRole("heading", { name: /model activity/i })).not.toBeInTheDocument(); expect(screen.queryByText(/FIRSTLINE/)).not.toBeInTheDocument(); await userEvent.click(screen.getByRole("button", { name: /model activity/i })); diff --git a/frontend/src/shell/CentralStatus.tsx b/frontend/src/shell/CentralStatus.tsx index c45f8e78..a7e3318b 100644 --- a/frontend/src/shell/CentralStatus.tsx +++ b/frontend/src/shell/CentralStatus.tsx @@ -2,10 +2,14 @@ import { useEffect, useReducer, useState } from "react"; import { useSessionStore } from "../store/sessionStore"; import { WorkingSpinner } from "./WorkingSpinner"; -/** Last few words of a string — a compact "what the model is doing now" tail. */ -function lastWords(s: string, n = 8): string { - const w = s.trim().split(/\s+/).filter(Boolean); - return w.slice(-n).join(" "); +/** Last few non-blank lines of a string — a compact multi-line "what the model is + * doing now" tail (mirrors ModelActivityPanel's tail-line logic, at a smaller size). */ +function tailLines(s: string, n = 5): string { + const lines = s + .split("\n") + .map((l) => l.trimEnd()) + .filter((l) => l.trim() !== ""); + return lines.slice(-n).join("\n"); } /** The minimal central view: the user's last input/choice, a live "working" indicator @@ -41,7 +45,7 @@ export function CentralStatus({ if (!working && !lastUserEntry && stepMessages.length === 0) return null; const elapsed = startedAt ? Math.max(0, Math.floor((Date.now() - startedAt) / 1000)) : 0; - const tail = lastWords(transcript.at(-1)?.text ?? ""); + const tail = tailLines(transcript.at(-1)?.text ?? ""); return (
+ {tail} +
+ )}