Archived
feat: show project and workspace activity indicators
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { isSessionActive, sessionActivityLabel, isWorkspaceActivityActive } from "./activity";
|
||||
import type { SessionStatus, WorkspaceActivity } from "./apiTypes";
|
||||
|
||||
const idleStatus: SessionStatus = {
|
||||
sessionId: "s1",
|
||||
isStreaming: false,
|
||||
isCompacting: false,
|
||||
isBashRunning: false,
|
||||
pendingMessageCount: 0,
|
||||
queuedMessages: [],
|
||||
tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
cost: 0,
|
||||
};
|
||||
|
||||
describe("activity helpers", () => {
|
||||
it("detects and labels active session states consistently", () => {
|
||||
expect(isSessionActive(idleStatus)).toBe(false);
|
||||
expect(sessionActivityLabel(idleStatus)).toBeUndefined();
|
||||
|
||||
expect(isSessionActive({ ...idleStatus, isStreaming: true })).toBe(true);
|
||||
expect(sessionActivityLabel({ ...idleStatus, isStreaming: true })).toBe("streaming");
|
||||
|
||||
expect(isSessionActive({ ...idleStatus, pendingMessageCount: 2 })).toBe(true);
|
||||
expect(sessionActivityLabel({ ...idleStatus, pendingMessageCount: 2 })).toBe("2 pending");
|
||||
|
||||
expect(sessionActivityLabel(idleStatus, { sessionId: "s1", phase: "active", label: "running tool", detail: "read", at: "now" })).toBe("running tool: read");
|
||||
});
|
||||
|
||||
it("detects workspace activity presence without exposing details", () => {
|
||||
const idle: WorkspaceActivity = { cwd: "/repo", hasSessionActivity: false, hasTerminalActivity: false, updatedAt: "now" };
|
||||
expect(isWorkspaceActivityActive(idle)).toBe(false);
|
||||
expect(isWorkspaceActivityActive({ ...idle, hasSessionActivity: true })).toBe(true);
|
||||
expect(isWorkspaceActivityActive({ ...idle, hasTerminalActivity: true })).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { SessionActivity, SessionStatus, WorkspaceActivity } from "./apiTypes.js";
|
||||
|
||||
export function isSessionActive(status?: SessionStatus, activity?: SessionActivity): boolean {
|
||||
return activity?.phase === "active"
|
||||
|| status?.isStreaming === true
|
||||
|| status?.isBashRunning === true
|
||||
|| status?.isCompacting === true
|
||||
|| (status?.pendingMessageCount ?? 0) > 0;
|
||||
}
|
||||
|
||||
export function sessionActivityLabel(status?: SessionStatus, activity?: SessionActivity): string | undefined {
|
||||
if (activity?.phase === "active") return activity.detail !== undefined && activity.detail !== "" ? `${activity.label}: ${activity.detail}` : activity.label;
|
||||
if (status === undefined) return undefined;
|
||||
if (status.isCompacting) return "compacting";
|
||||
if (status.isBashRunning) return "bash";
|
||||
if (status.isStreaming) return "streaming";
|
||||
if (status.pendingMessageCount > 0) return `${String(status.pendingMessageCount)} pending`;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function isWorkspaceActivityActive(activity: WorkspaceActivity | undefined): boolean {
|
||||
return activity !== undefined && (activity.hasSessionActivity || activity.hasTerminalActivity);
|
||||
}
|
||||
+18
-1
@@ -107,6 +107,18 @@ export interface SessionStatus {
|
||||
contextUsage?: { tokens: number | null; contextWindow: number; percent: number | null };
|
||||
}
|
||||
|
||||
export interface WorkspaceActivity {
|
||||
cwd: string;
|
||||
hasSessionActivity: boolean;
|
||||
hasTerminalActivity: boolean;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface WorkspaceActivityResponse {
|
||||
workspaces: WorkspaceActivity[];
|
||||
generatedAt: string;
|
||||
}
|
||||
|
||||
export interface SlashCommand {
|
||||
name: string;
|
||||
description?: string;
|
||||
@@ -242,6 +254,11 @@ export type TerminalUiEvent =
|
||||
| { type: "terminal.exited"; terminal: TerminalInfo }
|
||||
| { type: "terminal.closed"; terminalId: string; cwd: string };
|
||||
|
||||
export interface WorkspaceActivityUiEvent {
|
||||
type: "workspace.activity";
|
||||
activity: WorkspaceActivity;
|
||||
}
|
||||
|
||||
export interface CommandOption {
|
||||
value: string;
|
||||
label: string;
|
||||
@@ -280,4 +297,4 @@ export type SessionUiEvent =
|
||||
| { type: "pi.event"; eventType: string };
|
||||
|
||||
export type GlobalSessionEvent = Extract<SessionUiEvent, { type: "status.update" | "activity.update" | "session.name" }>;
|
||||
export type RealtimeEvent = GlobalSessionEvent | TerminalUiEvent;
|
||||
export type RealtimeEvent = GlobalSessionEvent | TerminalUiEvent | WorkspaceActivityUiEvent;
|
||||
|
||||
Reference in New Issue
Block a user