feat: add session rename, shell mode, and UI improvements

- Add session name property with rename API endpoint
- Add shell session mode (plain bash/zsh without Claude)
- Add gear icon on session tabs for options (rename)
- Add mode indicator on tabs for shell sessions
- Rename "Quick Start" button to "Run Claude"
- Add "Shell" button to start plain shell sessions
- Replace cost counter with total token counter
- Add session options modal for renaming

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-01-19 01:26:38 +01:00
co-authored by Claude Opus 4.5
parent 4903c45ec9
commit dc5c874b11
5 changed files with 331 additions and 15 deletions
+43 -2
View File
@@ -96,9 +96,13 @@ export class WebServer extends EventEmitter {
this.app.get('/api/sessions', async () => this.getSessionsState());
this.app.post('/api/sessions', async (req): Promise<SessionResponse> => {
const body = req.body as CreateSessionRequest;
const body = req.body as CreateSessionRequest & { mode?: 'claude' | 'shell'; name?: string };
const workingDir = body.workingDir || process.cwd();
const session = new Session({ workingDir });
const session = new Session({
workingDir,
mode: body.mode || 'claude',
name: body.name || ''
});
this.sessions.set(session.id, session);
this.setupSessionListeners(session);
@@ -107,6 +111,21 @@ export class WebServer extends EventEmitter {
return { success: true, session: session.toDetailedState() };
});
// Rename a session
this.app.put('/api/sessions/:id/name', async (req) => {
const { id } = req.params as { id: string };
const body = req.body as { name: string };
const session = this.sessions.get(id);
if (!session) {
return { error: 'Session not found' };
}
session.name = body.name || '';
this.broadcast('session:updated', session.toDetailedState());
return { success: true, name: session.name };
});
this.app.delete('/api/sessions/:id', async (req): Promise<ApiResponse> => {
const { id } = req.params as { id: string };
const session = this.sessions.get(id);
@@ -226,6 +245,28 @@ export class WebServer extends EventEmitter {
}
});
// Start a plain shell session (no Claude)
this.app.post('/api/sessions/:id/shell', async (req) => {
const { id } = req.params as { id: string };
const session = this.sessions.get(id);
if (!session) {
return { error: 'Session not found' };
}
if (session.isBusy()) {
return { error: 'Session is busy' };
}
try {
await session.startShell();
this.broadcast('session:interactive', { id, mode: 'shell' });
return { success: true, message: 'Shell session started' };
} catch (err) {
return { error: (err as Error).message };
}
});
// Send input to interactive session
this.app.post('/api/sessions/:id/input', async (req): Promise<ApiResponse> => {
const { id } = req.params as { id: string };