feat(run): wire Workspace to TerminalView, delete the stream-json Run feature
Combines three tasks that couldn't land as separate commits: the pre-commit hook's full test run crashes on any intermediate state where Workspace.tsx still imports the files being deleted, so the deletion (old RunConsole/useRunStream/run-spawner/stream-json-parser), the RunSetup/RunHistory type adjustments, and this file's own TerminalView wiring had to be staged together and committed as one hook-passable unit. - Delete RunConsole.tsx, useRunStream.ts, server/lib/run-spawner.js, server/lib/stream-json-parser.js and their tests (Task 8). - Adjust RunSetup.tsx/RunHistory.tsx to the tmux-backed RunHandle/ RunStartArgs/DashboardRunHistoryItem shapes, remove mode selection UI (Task 9). - Swap Workspace.tsx's chat-bubble run console for TerminalView (xterm.js over /ws-pty/:runId), drop the stream-json envelope plumbing, update Start/Resume to the new RunStartArgs payload. Create onStartFromSetup handler to work with RunSetup's new callback shape. Remove mode state and related plumbing. Remove send/followUp state (no longer using old RunConsole chat interface). - Add promptPlaceholderTerminal i18n key to support RunSetup's new placeholder text (Task 10). - Update Workspace.test.tsx to mock TerminalView component. - Regenerate screens.snapshot.test.tsx snapshot (only Workspace run panel changes: terminal container instead of chat bubbles).
This commit is contained in:
@@ -29,12 +29,10 @@ const activeRuns = {
|
||||
{
|
||||
id: LIVE_ID,
|
||||
sessionId: "sess-live",
|
||||
mode: "conversation",
|
||||
cwd: "/tmp/live",
|
||||
model: "claude-opus-5",
|
||||
status: "running",
|
||||
prompt: "the live prompt",
|
||||
startedAt: 3000,
|
||||
startedAt: "2000-01-01T00:50:00Z",
|
||||
endedAt: null,
|
||||
},
|
||||
],
|
||||
@@ -44,7 +42,6 @@ function historyItem(over: Partial<DashboardRunHistoryItem>): DashboardRunHistor
|
||||
return {
|
||||
id: PAST_ID,
|
||||
session_id: "sess-past",
|
||||
mode: "conversation",
|
||||
cwd: "/tmp/past",
|
||||
model: "sonnet",
|
||||
status: "completed",
|
||||
@@ -64,7 +61,6 @@ const PAST = historyItem({});
|
||||
const HEADLESS = historyItem({
|
||||
id: HEADLESS_ID,
|
||||
session_id: "sess-headless",
|
||||
mode: "headless",
|
||||
cwd: "/tmp/headless",
|
||||
prompt_preview: "the headless prompt",
|
||||
started_at: new Date(1000).toISOString(),
|
||||
@@ -95,10 +91,9 @@ function row(id: string, over: Partial<UnifiedRunRow> = {}): UnifiedRunRow {
|
||||
return {
|
||||
id,
|
||||
sessionId: `sess-${id}`,
|
||||
mode: "conversation",
|
||||
cwd: `/tmp/${id}`,
|
||||
model: "sonnet",
|
||||
status: "completed",
|
||||
status: "abandoned",
|
||||
promptPreview: `prompt of ${id}`,
|
||||
startedAt: 1000,
|
||||
endedAt: 2000,
|
||||
@@ -216,39 +211,31 @@ describe("RunsModal", () => {
|
||||
expect(spies.onAttach).toHaveBeenCalledWith(LIVE_ID);
|
||||
});
|
||||
|
||||
it("fires resume with the history item behind a finished conversation row", () => {
|
||||
it("fires resume with the history item behind a finished row", () => {
|
||||
const { spies } = renderModal([row(PAST_ID)]);
|
||||
fireEvent.click(screen.getByText(i18n.t("run:resume.resumeOption")));
|
||||
expect(spies.onResume).toHaveBeenCalledWith(PAST);
|
||||
expect(spies.onView).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("fires view — not resume — for a finished headless row", () => {
|
||||
const { spies } = renderModal([row(HEADLESS_ID, { mode: "headless" })]);
|
||||
expect(screen.queryByText(i18n.t("run:resume.resumeOption"))).toBeNull();
|
||||
it("fires view for a finished row", () => {
|
||||
const { spies } = renderModal([row(HEADLESS_ID)]);
|
||||
fireEvent.click(screen.getByText(i18n.t("run:runs.viewLabel")));
|
||||
expect(spies.onView).toHaveBeenCalledWith(HEADLESS);
|
||||
expect(spies.onResume).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("filters by status, by mode and by free text", () => {
|
||||
it("filters by status and by free text", () => {
|
||||
const rows = [
|
||||
row("a", { status: "running", isLive: true, promptPreview: "alpha" }),
|
||||
row("b", { status: "error", promptPreview: "bravo" }),
|
||||
row("c", { status: "completed", mode: "headless", promptPreview: "charlie" }),
|
||||
row("b", { status: "killed", promptPreview: "bravo" }),
|
||||
row("c", { status: "abandoned", promptPreview: "charlie" }),
|
||||
];
|
||||
renderModal(rows);
|
||||
|
||||
fireEvent.click(chip(i18n.t("run:status.error")));
|
||||
fireEvent.click(chip(i18n.t("run:status.killed")));
|
||||
expect(screen.getByText("bravo")).toBeTruthy();
|
||||
expect(screen.queryByText("alpha")).toBeNull();
|
||||
|
||||
fireEvent.click(allChip(0));
|
||||
fireEvent.click(chip(i18n.t("run:mode.headless")));
|
||||
expect(screen.getByText("charlie")).toBeTruthy();
|
||||
expect(screen.queryByText("bravo")).toBeNull();
|
||||
|
||||
fireEvent.click(allChip(1));
|
||||
fireEvent.change(
|
||||
screen.getByPlaceholderText(
|
||||
i18n.t("run:runs.searchPlaceholder", "Search prompt, cwd, model, or session id…")
|
||||
|
||||
Reference in New Issue
Block a user