mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 13:39:41 +02:00
feat(tui): add respawn status polling and display
- Poll session API every 2 seconds for respawn status - Display respawn state and cycle count in StatusBar - Show respawn status in magenta to match web UI - Format state (underscores to spaces) for readability Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+2
-1
@@ -45,6 +45,7 @@ export function App(): React.ReactElement {
|
||||
terminalOutput,
|
||||
innerLoopState,
|
||||
innerTodos,
|
||||
respawnStatus,
|
||||
} = useSessionManager();
|
||||
|
||||
// Calculate terminal height based on stdout dimensions
|
||||
@@ -255,7 +256,7 @@ export function App(): React.ReactElement {
|
||||
/>
|
||||
)}
|
||||
|
||||
<StatusBar session={activeSession} />
|
||||
<StatusBar session={activeSession} respawnStatus={respawnStatus} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,9 +13,16 @@ import React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import type { ScreenSession } from '../../types.js';
|
||||
|
||||
interface RespawnStatus {
|
||||
enabled: boolean;
|
||||
state: string;
|
||||
cycleCount: number;
|
||||
}
|
||||
|
||||
interface StatusBarProps {
|
||||
session: ScreenSession | null;
|
||||
inputMode?: boolean;
|
||||
respawnStatus?: RespawnStatus | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,7 +45,7 @@ function formatDuration(ms: number): string {
|
||||
/**
|
||||
* StatusBar component showing session information
|
||||
*/
|
||||
export function StatusBar({ session, inputMode = false }: StatusBarProps): React.ReactElement {
|
||||
export function StatusBar({ session, inputMode = false, respawnStatus }: StatusBarProps): React.ReactElement {
|
||||
if (!session) {
|
||||
return (
|
||||
<Box
|
||||
@@ -88,6 +95,15 @@ export function StatusBar({ session, inputMode = false }: StatusBarProps): React
|
||||
<Text color="yellow" bold>INPUT MODE</Text>
|
||||
</>
|
||||
)}
|
||||
{respawnStatus?.enabled && (
|
||||
<>
|
||||
<Text> | </Text>
|
||||
<Text color="magenta">
|
||||
respawn: {respawnStatus.state.replace(/_/g, ' ')}
|
||||
{respawnStatus.cycleCount > 0 && ` (${respawnStatus.cycleCount})`}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Right side: Keyboard hints */}
|
||||
|
||||
@@ -18,6 +18,12 @@ const SCREENS_FILE = join(homedir(), '.claudeman', 'screens.json');
|
||||
const INNER_STATE_FILE = join(homedir(), '.claudeman', 'state-inner.json');
|
||||
const OUTPUT_POLL_INTERVAL = 500; // Poll terminal output every 500ms
|
||||
|
||||
interface RespawnStatus {
|
||||
enabled: boolean;
|
||||
state: string;
|
||||
cycleCount: number;
|
||||
}
|
||||
|
||||
interface SessionManagerState {
|
||||
sessions: ScreenSession[];
|
||||
activeSessionId: string | null;
|
||||
@@ -25,6 +31,7 @@ interface SessionManagerState {
|
||||
terminalOutput: string;
|
||||
innerLoopState: InnerLoopState | null;
|
||||
innerTodos: InnerTodoItem[];
|
||||
respawnStatus: RespawnStatus | null;
|
||||
refreshSessions: () => void;
|
||||
selectSession: (sessionId: string) => void;
|
||||
createSession: () => Promise<string | null>;
|
||||
@@ -93,6 +100,7 @@ export function useSessionManager(): SessionManagerState {
|
||||
const [terminalOutput, setTerminalOutput] = useState<string>('');
|
||||
const [innerLoopState, setInnerLoopState] = useState<InnerLoopState | null>(null);
|
||||
const [innerTodos, setInnerTodos] = useState<InnerTodoItem[]>([]);
|
||||
const [respawnStatus, setRespawnStatus] = useState<RespawnStatus | null>(null);
|
||||
const outputBufferRef = useRef<string>('');
|
||||
|
||||
// Load sessions on mount and watch for changes
|
||||
@@ -153,6 +161,48 @@ export function useSessionManager(): SessionManagerState {
|
||||
};
|
||||
}, [activeSessionId]);
|
||||
|
||||
// Poll respawn status via API (when server is running)
|
||||
useEffect(() => {
|
||||
if (!activeSessionId) {
|
||||
setRespawnStatus(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const pollRespawnStatus = async () => {
|
||||
try {
|
||||
const response = await fetch(`http://localhost:3000/api/sessions/${activeSessionId}`);
|
||||
if (response.ok) {
|
||||
const data = await response.json() as {
|
||||
success?: boolean;
|
||||
session?: {
|
||||
respawnEnabled?: boolean;
|
||||
respawnState?: string;
|
||||
respawnCycleCount?: number;
|
||||
};
|
||||
};
|
||||
if (data.success && data.session) {
|
||||
setRespawnStatus({
|
||||
enabled: data.session.respawnEnabled || false,
|
||||
state: data.session.respawnState || 'stopped',
|
||||
cycleCount: data.session.respawnCycleCount || 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Server not running, that's ok
|
||||
setRespawnStatus(null);
|
||||
}
|
||||
};
|
||||
|
||||
// Poll less frequently than output (every 2 seconds)
|
||||
pollRespawnStatus();
|
||||
const intervalId = setInterval(pollRespawnStatus, 2000);
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}, [activeSessionId]);
|
||||
|
||||
// Poll terminal output for active session
|
||||
useEffect(() => {
|
||||
if (!activeSessionId || !activeSession) return;
|
||||
@@ -335,6 +385,7 @@ export function useSessionManager(): SessionManagerState {
|
||||
terminalOutput,
|
||||
innerLoopState,
|
||||
innerTodos,
|
||||
respawnStatus,
|
||||
refreshSessions,
|
||||
selectSession,
|
||||
createSession,
|
||||
|
||||
Reference in New Issue
Block a user