Skip to content

Commit 1883595

Browse files
committed
feat: add SSH vs Local indicator in System Process Viewer
Session rows show an SSH/Local badge between the running count and session ID. SSH sessions display "SSH: {name}" with host tooltip; local sessions show "Local". Process rows show a compact "SSH" badge after the Running status for remote sessions.
1 parent 23268d6 commit 1883595

2 files changed

Lines changed: 98 additions & 0 deletions

File tree

src/__tests__/renderer/components/ProcessMonitor.test.tsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,69 @@ describe('ProcessMonitor', () => {
769769
});
770770
});
771771

772+
describe('SSH/Local indicator', () => {
773+
it('should show "Local" badge on session row for local sessions', async () => {
774+
const process = createActiveProcess({ sessionId: 'session-1-ai-tab-1' });
775+
getActiveProcessesMock().mockResolvedValue([process]);
776+
777+
const session = createSession();
778+
render(<ProcessMonitor theme={theme} sessions={[session]} groups={[]} onClose={onClose} />);
779+
780+
await waitFor(() => {
781+
expect(screen.getByTitle('Running locally')).toBeInTheDocument();
782+
expect(screen.getByText('Local')).toBeInTheDocument();
783+
});
784+
});
785+
786+
it('should show SSH badge on session row for SSH sessions', async () => {
787+
const process = createActiveProcess({ sessionId: 'session-1-ai-tab-1' });
788+
getActiveProcessesMock().mockResolvedValue([process]);
789+
790+
const session = createSession({
791+
sshRemote: { id: 'remote-1', name: 'dev-box', host: '192.168.1.100' },
792+
});
793+
render(<ProcessMonitor theme={theme} sessions={[session]} groups={[]} onClose={onClose} />);
794+
795+
await waitFor(() => {
796+
expect(screen.getByText('SSH: dev-box')).toBeInTheDocument();
797+
// Both session row and process row have SSH badges with this title
798+
const sshTitles = screen.getAllByTitle('SSH: dev-box (192.168.1.100)');
799+
expect(sshTitles.length).toBeGreaterThanOrEqual(1);
800+
});
801+
});
802+
803+
it('should show SSH badge on process row for SSH sessions', async () => {
804+
const process = createActiveProcess({ sessionId: 'session-1-ai-tab-1' });
805+
getActiveProcessesMock().mockResolvedValue([process]);
806+
807+
const session = createSession({
808+
sshRemote: { id: 'remote-1', name: 'prod-server', host: '10.0.0.5' },
809+
});
810+
render(<ProcessMonitor theme={theme} sessions={[session]} groups={[]} onClose={onClose} />);
811+
812+
await waitFor(() => {
813+
// Process row should have the SSH badge
814+
const sshBadges = screen.getAllByText('SSH');
815+
expect(sshBadges.length).toBeGreaterThanOrEqual(1);
816+
});
817+
});
818+
819+
it('should not show SSH badge on process row for local sessions', async () => {
820+
const process = createActiveProcess({ sessionId: 'session-1-ai-tab-1' });
821+
getActiveProcessesMock().mockResolvedValue([process]);
822+
823+
const session = createSession();
824+
render(<ProcessMonitor theme={theme} sessions={[session]} groups={[]} onClose={onClose} />);
825+
826+
await waitFor(() => {
827+
expect(screen.getByText('Test Session - AI Agent (claude-code)')).toBeInTheDocument();
828+
});
829+
830+
// No SSH badge should appear on process rows
831+
expect(screen.queryByText('SSH')).not.toBeInTheDocument();
832+
});
833+
});
834+
772835
describe('Expand/collapse', () => {
773836
it('should auto-expand all nodes on initial load', async () => {
774837
const process = createActiveProcess();

src/renderer/components/ProcessMonitor.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ interface ProcessNode {
7272
participantName?: string; // For group chat participant processes
7373
command?: string; // The command used to spawn this process
7474
args?: string[]; // The arguments passed to the command
75+
sshRemote?: { name: string; host: string }; // SSH remote info if running remotely
7576
}
7677

7778
// Format runtime in human readable format (e.g., "2m 30s", "1h 5m", "3d 2h")
@@ -358,13 +359,18 @@ export function ProcessMonitor(props: ProcessMonitorProps) {
358359

359360
// Build session node with active processes
360361
const buildSessionNode = (session: Session): ProcessNode => {
362+
const sshRemote = session.sshRemote
363+
? { name: session.sshRemote.name, host: session.sshRemote.host }
364+
: undefined;
365+
361366
const sessionNode: ProcessNode = {
362367
id: `session-${session.id}`,
363368
type: 'session',
364369
label: session.name,
365370
sessionId: session.id,
366371
expanded: expandedNodes.has(`session-${session.id}`),
367372
children: [],
373+
sshRemote,
368374
};
369375

370376
// Get active processes for this session
@@ -430,6 +436,7 @@ export function ProcessMonitor(props: ProcessMonitorProps) {
430436
isAutoRun,
431437
command: proc.command,
432438
args: proc.args,
439+
sshRemote,
433440
});
434441
});
435442

@@ -908,6 +915,22 @@ export function ProcessMonitor(props: ProcessMonitorProps) {
908915
{activeCount} running
909916
</span>
910917
)}
918+
<span
919+
className="px-1.5 py-0.5 rounded text-xs"
920+
style={{
921+
backgroundColor: node.sshRemote
922+
? `${theme.colors.accent}20`
923+
: `${theme.colors.textDim}15`,
924+
color: node.sshRemote ? theme.colors.accent : theme.colors.textDim,
925+
}}
926+
title={
927+
node.sshRemote
928+
? `SSH: ${node.sshRemote.name} (${node.sshRemote.host})`
929+
: 'Running locally'
930+
}
931+
>
932+
{node.sshRemote ? `SSH: ${node.sshRemote.name}` : 'Local'}
933+
</span>
911934
<span>Session: {node.sessionId?.substring(0, 8)}...</span>
912935
</span>
913936
{node.sessionId && onNavigateToSession && (
@@ -1131,6 +1154,18 @@ export function ProcessMonitor(props: ProcessMonitorProps) {
11311154
>
11321155
Running
11331156
</span>
1157+
{node.sshRemote && (
1158+
<span
1159+
className="text-xs px-1.5 py-0.5 rounded"
1160+
style={{
1161+
backgroundColor: `${theme.colors.accent}20`,
1162+
color: theme.colors.accent,
1163+
}}
1164+
title={`SSH: ${node.sshRemote.name} (${node.sshRemote.host})`}
1165+
>
1166+
SSH
1167+
</span>
1168+
)}
11341169
</div>
11351170
</div>
11361171
);

0 commit comments

Comments
 (0)