feat: add dev server log panel with real-time streaming

Add the ability to view dev server logs in a dedicated panel with:
- Real-time log streaming via WebSocket events
- ANSI color support using xterm.js
- Scrollback buffer (50KB) for log history on reconnect
- Output throttling to prevent UI flooding
- "View Logs" option in worktree dropdown menu

Server changes:
- Add scrollback buffer and event emission to DevServerService
- Add GET /api/worktree/dev-server-logs endpoint
- Add dev-server:started, dev-server:output, dev-server:stopped events

UI changes:
- Add reusable XtermLogViewer component
- Add DevServerLogsPanel dialog component
- Add useDevServerLogs hook for WebSocket subscription

Closes #462

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Shirone
2026-01-13 20:42:57 +01:00
parent 7ef525effa
commit 073f6d5793
17 changed files with 1277 additions and 30 deletions

View File

@@ -33,6 +33,7 @@ import { createMigrateHandler } from './routes/migrate.js';
import { createStartDevHandler } from './routes/start-dev.js';
import { createStopDevHandler } from './routes/stop-dev.js';
import { createListDevServersHandler } from './routes/list-dev-servers.js';
import { createGetDevServerLogsHandler } from './routes/dev-server-logs.js';
import {
createGetInitScriptHandler,
createPutInitScriptHandler,
@@ -97,6 +98,11 @@ export function createWorktreeRoutes(events: EventEmitter): Router {
);
router.post('/stop-dev', createStopDevHandler());
router.post('/list-dev-servers', createListDevServersHandler());
router.get(
'/dev-server-logs',
validatePathParams('worktreePath'),
createGetDevServerLogsHandler()
);
// Init script routes
router.get('/init-script', createGetInitScriptHandler());

View File

@@ -0,0 +1,52 @@
/**
* GET /dev-server-logs endpoint - Get buffered logs for a worktree's dev server
*
* Returns the scrollback buffer containing historical log output for a running
* dev server. Used by clients to populate the log panel on initial connection
* before subscribing to real-time updates via WebSocket.
*/
import type { Request, Response } from 'express';
import { getDevServerService } from '../../../services/dev-server-service.js';
import { getErrorMessage, logError } from '../common.js';
export function createGetDevServerLogsHandler() {
return async (req: Request, res: Response): Promise<void> => {
try {
const { worktreePath } = req.query as {
worktreePath?: string;
};
if (!worktreePath) {
res.status(400).json({
success: false,
error: 'worktreePath query parameter is required',
});
return;
}
const devServerService = getDevServerService();
const result = devServerService.getServerLogs(worktreePath);
if (result.success && result.result) {
res.json({
success: true,
result: {
worktreePath: result.result.worktreePath,
port: result.result.port,
logs: result.result.logs,
startedAt: result.result.startedAt,
},
});
} else {
res.status(404).json({
success: false,
error: result.error || 'Failed to get dev server logs',
});
}
} catch (error) {
logError(error, 'Get dev server logs failed');
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}