Skip to content

Conversation

@Shironex
Copy link
Collaborator

@Shironex Shironex commented Jan 13, 2026

Summary

  • Add the ability to view dev server logs in a dedicated panel with real-time streaming
  • Implements scrollback buffer (50KB) for log history replay on reconnect
  • ANSI color support using xterm.js for proper terminal output rendering
  • Output throttling to prevent UI flooding under heavy load

Changes

Server

  • Enhanced DevServerService with scrollback buffer and WebSocket event emission
  • Added GET /api/worktree/dev-server-logs endpoint for fetching buffered logs
  • Added event types: dev-server:started, dev-server:output, dev-server:stopped

UI

  • Added reusable XtermLogViewer component for rendering terminal output
  • Added DevServerLogsPanel dialog accessible via "View Logs" in worktree dropdown
  • Added useDevServerLogs hook for WebSocket subscription and state management

Test plan

  • Start a dev server from the worktree dropdown
  • Click "View Logs" to open the log panel
  • Verify logs stream in real-time with proper ANSI colors
  • Close and reopen the panel - verify scrollback buffer replays history
  • Stop the dev server and verify the panel shows stopped state

Closes #462

Preview

image

Summary by CodeRabbit

New Features

  • Development server log viewer with real-time streaming, terminal UI, and ANSI color support for better readability
  • "View Logs" menu action for running dev servers accessible from the worktree panel
  • Dev server status display showing running/stopped state, port, and start time
  • Auto-scrolling capability for logs with manual scroll-to-bottom control

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 13, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This pull request introduces comprehensive dev server log viewing functionality across the application stack. The backend adds log buffering with configurable scrollback history, throttled output emission via EventEmitter, and new API endpoints to fetch buffered logs and stream live output. The frontend introduces an XtermLogViewer component for terminal-style rendering, a DevServerLogsPanel modal for interactive log viewing, and a useDevServerLogs hook managing log state and WebSocket event subscriptions. Supporting API layers add typed interfaces for dev-server events and extend the Electron/HTTP client API surface.

Changes

Cohort / File(s) Summary
Backend Server Integration
apps/server/src/index.ts, apps/server/src/middleware/validate-paths.ts, apps/server/src/routes/worktree/index.ts
Initializes DevServerService at startup; extends path validation to support query-based parameters (GET requests); wires new GET /dev-server-logs route with handler factory.
Dev Server Service & Handler
apps/server/src/services/dev-server-service.ts, apps/server/src/routes/worktree/routes/dev-server-logs.ts
Adds scrollback buffer, output batching/throttling, and EventEmitter integration for log streaming. Introduces getServerLogs() API and lifecycle event emissions (dev-server:started/output/stopped). New HTTP handler validates worktreePath and returns buffered logs or 404.
Frontend Terminal Component
apps/ui/src/components/ui/xterm-log-viewer.tsx
New React component rendering read-only terminal using xterm.js with lazy-loaded addons (FitAddon, WebglAddon), ANSI color support, theme detection, auto-scroll control, resize handling, and imperative content management (append, clear, write).
Dev Server Logs Panel
apps/ui/src/components/views/board-view/worktree-panel/components/dev-server-logs-panel.tsx, apps/ui/src/components/views/board-view/worktree-panel/components/index.ts
New modal panel component integrating XtermLogViewer, real-time log streaming via useDevServerLogs, header with status/branch/port/start-time, action controls (refresh, open URL, stop server), and error display. Exported in component index.
Worktree Panel Integration
apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx, apps/ui/src/components/views/board-view/worktree-panel/components/worktree-tab.tsx, apps/ui/src/components/views/board-view/worktree-panel/worktree-panel.tsx
Threads onViewDevServerLogs prop through component hierarchy; adds "View Logs" menu item in WorktreeActionsDropdown; manages log panel state (open/worktree selection) in main WorktreePanel.
Dev Server Logs Hook
apps/ui/src/components/views/board-view/worktree-panel/hooks/use-dev-server-logs.ts, apps/ui/src/components/views/board-view/worktree-panel/hooks/index.ts
New hook manages log state (logs, running, loading, errors, port, startedAt, exitCode). Fetches buffered logs via Electron API; subscribes to WebSocket dev-server events (started/output/stopped); handles auto-append and state resets. Exported in hooks index.
API & Type Definitions
apps/ui/src/lib/http-api-client.ts, apps/ui/src/lib/electron.ts, apps/ui/src/types/electron.d.ts, libs/types/src/event.ts
Adds DevServerLogEvent union type and payload interfaces (DevServerStartedEvent, DevServerOutputEvent, DevServerStoppedEvent); extends HttpApiClient with getDevServerLogs() and onDevServerLogEvent(); adds mock Electron API stubs; expands EventType and WorktreeAPI interface with dev-server methods.

Sequence Diagrams

sequenceDiagram
    participant Client as UI Client
    participant Server as Express Server
    participant Service as DevServerService
    participant DB as Scrollback Buffer

    Client->>Server: GET /dev-server-logs?worktreePath=...
    Server->>Service: getServerLogs(worktreePath)
    Service->>DB: Retrieve scrollback buffer
    DB-->>Service: Return buffered logs
    Service-->>Server: Return logs + metadata
    Server-->>Client: 200 {logs, port, startedAt}
Loading
sequenceDiagram
    participant Child as Child Process
    participant Service as DevServerService
    participant Emitter as EventEmitter
    participant Client as UI Client

    Child->>Service: stdout/stderr data
    Service->>Service: handleProcessOutput()
    Service->>Service: Append to scrollback + buffer
    Service->>Service: Schedule throttled flush
    Service->>Emitter: flushOutput() emits batch
    Emitter-->>Client: WebSocket dev-server:output
    Client->>Client: useDevServerLogs appends
    Client->>Client: XtermLogViewer renders
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested labels

Enhancement, Testers-Requested

Poem

🐰 Logs were hidden in the dark,
Now they stream with vibrant spark!
Xterm glows with ANSI cheer,
Dev server whispers we can hear!
From buffer to the browser screen,
The finest logs you've ever seen! 🎆

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main feature being added: a dev server log panel with real-time streaming capability.
Linked Issues check ✅ Passed The PR fully implements the requirement from #462 to provide a way for users to view logs from the dev server by surfacing the output in the application UI.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the dev server logs feature: backend log streaming infrastructure, UI components for log display, API endpoints, and event handling.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Shironex, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers a significant enhancement by providing a comprehensive dev server log panel. Users can now monitor their development server's output in real-time, complete with rich ANSI color formatting. The system intelligently buffers historical logs and manages output flow to ensure a smooth and responsive user experience, making it easier to debug and observe server behavior directly within the application.

Highlights

  • Real-time Log Streaming: Introduced the ability to view dev server logs in a dedicated panel with real-time streaming capabilities.
  • Scrollback Buffer: Implemented a 50KB scrollback buffer for log history replay, ensuring logs are available even after reconnecting to the panel.
  • ANSI Color Support: Integrated xterm.js to provide proper rendering of ANSI escape codes, allowing for colored terminal output.
  • Output Throttling: Added output throttling mechanisms to prevent the UI from being overwhelmed and flooding under heavy log loads.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This is a great feature addition, enabling real-time log viewing for dev servers. The implementation is solid, with good attention to performance on both the server (output throttling) and client (xterm.js with WebGL, dynamic imports). The new components and hooks on the UI side are well-structured and provide a good user experience. I have a few suggestions to improve maintainability and type safety.

Comment on lines +20 to +26
if (!worktreePath) {
res.status(400).json({
success: false,
error: 'worktreePath query parameter is required',
});
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This worktreePath validation appears to be redundant. The route is already protected by the validatePathParams('worktreePath') middleware in apps/server/src/routes/worktree/index.ts, which should handle this check. Removing this block will make the handler cleaner and centralize the validation logic in the middleware.

Comment on lines 408 to 455
devProcess.on('error', (error) => {
logger.error(`Process error:`, error);
status.error = error.message;

// Clean up flush timeout
if (serverInfo.flushTimeout) {
clearTimeout(serverInfo.flushTimeout);
serverInfo.flushTimeout = null;
}

// Emit stopped event with error (only if not already stopping)
if (this.emitter && !serverInfo.stopping) {
this.emitter.emit('dev-server:stopped', {
worktreePath,
port,
exitCode: null,
error: error.message,
timestamp: new Date().toISOString(),
});
}

this.allocatedPorts.delete(port);
this.runningServers.delete(worktreePath);
});

devProcess.on('exit', (code) => {
logger.info(`Process for ${worktreePath} exited with code ${code}`);
status.exited = true;

// Clean up flush timeout
if (serverInfo.flushTimeout) {
clearTimeout(serverInfo.flushTimeout);
serverInfo.flushTimeout = null;
}

// Emit stopped event (only if not already stopping - prevents duplicate events)
if (this.emitter && !serverInfo.stopping) {
this.emitter.emit('dev-server:stopped', {
worktreePath,
port,
exitCode: code,
timestamp: new Date().toISOString(),
});
}

this.allocatedPorts.delete(port);
this.runningServers.delete(worktreePath);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The cleanup logic for when a process ends (clearing timeouts, releasing ports, and removing from tracking) is duplicated in both the error and exit event handlers. This can be refactored into a single helper function to reduce duplication and improve maintainability.

    const cleanupAndEmitStop = (code: number | null, error?: string) => {
      if (serverInfo.flushTimeout) {
        clearTimeout(serverInfo.flushTimeout);
        serverInfo.flushTimeout = null;
      }

      if (this.emitter && !serverInfo.stopping) {
        this.emitter.emit('dev-server:stopped', {
          worktreePath,
          port,
          exitCode: code,
          error,
          timestamp: new Date().toISOString(),
        });
      }

      this.allocatedPorts.delete(port);
      this.runningServers.delete(worktreePath);
    };

    devProcess.on('error', (error) => {
      logger.error(`Process error:`, error);
      status.error = error.message;
      cleanupAndEmitStop(null, error.message);
    });

    devProcess.on('exit', (code) => {
      logger.info(`Process for ${worktreePath} exited with code ${code}`);
      status.exited = true;
      cleanupAndEmitStop(code);
    });

Comment on lines 994 to 1016
onDevServerLogEvent: (
callback: (event: {
type: 'dev-server:started' | 'dev-server:output' | 'dev-server:stopped';
payload: {
worktreePath: string;
port?: number;
url?: string;
content?: string;
exitCode?: number | null;
error?: string;
timestamp: string;
};
}) => void
) => () => void;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The type definition for the onDevServerLogEvent payload is a bit loose, with many optional properties. To improve type safety and provide better autocompletion for consumers of this API, you can use a discriminated union for the event payload. This ensures that each event type has exactly the properties it's supposed to have.

  onDevServerLogEvent: (
    callback: (
      event:
        | { type: 'dev-server:started'; payload: { worktreePath: string; port: number; url: string; timestamp: string } }
        | { type: 'dev-server:output'; payload: { worktreePath: string; content: string; timestamp: string } }
        | { type: 'dev-server:stopped'; payload: { worktreePath: string; port: number; exitCode: number | null; error?: string; timestamp: string } }
    ) => void
  ) => () => void;

@Shironex Shironex force-pushed the feat/dev-server-log-panel branch from 6d239be to 1bd803f Compare January 13, 2026 19:46
@Shironex
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
apps/server/src/middleware/validate-paths.ts (1)

67-70: Consider adding explicit handling for required non-string parameters.

For required parameters (no ? suffix), if the value exists but is not a string (e.g., an array or object from query string parsing), the validation silently passes without calling validatePath. While this matches the prior behavior and downstream handlers would typically fail on malformed input, consider logging or returning an error for type mismatches on required params.

💡 Optional: Explicit type validation for required params
         // Handle regular parameters
         const value = getParamValue(req, paramName);
-        if (value && typeof value === 'string') {
+        if (value !== undefined) {
+          if (typeof value !== 'string') {
+            res.status(400).json({
+              success: false,
+              error: `Parameter '${paramName}' must be a string`,
+            });
+            return;
+          }
           validatePath(value);
         }
apps/ui/src/types/electron.d.ts (1)

981-1007: Well-structured API additions.

The type definitions are clear and follow the existing patterns in this file. The event callback signature with discriminated union on type enables type-safe event handling.

Consider using discriminated unions for the payload to provide stronger type guarantees per event type (e.g., port is only present on started, content only on output, exitCode only on stopped), though the current approach with optional fields is pragmatic and consistent with other event types in this file.

apps/ui/src/components/views/board-view/worktree-panel/components/dev-server-logs-panel.tsx (1)

271-277: Consider using the Button component for consistency.

The "Scroll to bottom" button uses a native <button> element while other buttons in the header use the Button component. For visual and behavioral consistency, consider using the Button component here.

♻️ Suggested change
-          <button
+          <Button
+            variant="ghost"
+            size="sm"
             onClick={scrollToBottom}
-            className="inline-flex items-center gap-1.5 px-2 py-1 rounded hover:bg-muted transition-colors text-primary"
+            className="h-auto px-2 py-1 text-xs text-primary"
           >
             <ArrowDown className="w-3 h-3" />
             Scroll to bottom
-          </button>
+          </Button>
apps/ui/src/components/views/board-view/worktree-panel/hooks/use-dev-server-logs.ts (1)

71-118: Consider adding stale response protection.

If worktreePath changes while a fetch is in-flight, the old response could update state with logs from the previous worktree. This could cause a brief flash of incorrect data before the new fetch completes.

♻️ Suggested fix with abort controller or stale check
 const fetchLogs = useCallback(async () => {
   if (!worktreePath) return;
+  const currentPath = worktreePath; // Capture for stale check

   setState((prev) => ({ ...prev, isLoading: true, error: null }));

   try {
     const api = getElectronAPI();
     if (!api?.worktree?.getDevServerLogs) {
       setState((prev) => ({
         ...prev,
         isLoading: false,
         error: 'Dev server logs API not available',
       }));
       return;
     }

     const result = await api.worktree.getDevServerLogs(worktreePath);

+    // Check if worktreePath changed during fetch
+    if (currentPath !== worktreePath) return;
+
     if (result.success && result.result) {

Note: Since worktreePath is captured in the closure, you'd need to use a ref to track the current path, or restructure to use useRef for the latest path value.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9311f2e and 1bd803f.

📒 Files selected for processing (17)
  • apps/server/src/index.ts
  • apps/server/src/middleware/validate-paths.ts
  • apps/server/src/routes/worktree/index.ts
  • apps/server/src/routes/worktree/routes/dev-server-logs.ts
  • apps/server/src/services/dev-server-service.ts
  • apps/ui/src/components/ui/xterm-log-viewer.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/components/dev-server-logs-panel.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/components/index.ts
  • apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/components/worktree-tab.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/hooks/index.ts
  • apps/ui/src/components/views/board-view/worktree-panel/hooks/use-dev-server-logs.ts
  • apps/ui/src/components/views/board-view/worktree-panel/worktree-panel.tsx
  • apps/ui/src/lib/electron.ts
  • apps/ui/src/lib/http-api-client.ts
  • apps/ui/src/types/electron.d.ts
  • libs/types/src/event.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always import from shared packages (@automaker/*), never from old relative paths

Files:

  • apps/ui/src/components/views/board-view/worktree-panel/hooks/index.ts
  • apps/server/src/routes/worktree/routes/dev-server-logs.ts
  • apps/ui/src/components/views/board-view/worktree-panel/components/index.ts
  • apps/ui/src/types/electron.d.ts
  • apps/ui/src/components/ui/xterm-log-viewer.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/hooks/use-dev-server-logs.ts
  • apps/server/src/index.ts
  • apps/ui/src/components/views/board-view/worktree-panel/components/dev-server-logs-panel.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/components/worktree-tab.tsx
  • libs/types/src/event.ts
  • apps/ui/src/lib/electron.ts
  • apps/server/src/routes/worktree/index.ts
  • apps/server/src/middleware/validate-paths.ts
  • apps/server/src/services/dev-server-service.ts
  • apps/ui/src/lib/http-api-client.ts
  • apps/ui/src/components/views/board-view/worktree-panel/worktree-panel.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use resolveModelString() from @automaker/model-resolver to convert model aliases (haiku, sonnet, opus) to full model names

Files:

  • apps/ui/src/components/views/board-view/worktree-panel/hooks/index.ts
  • apps/server/src/routes/worktree/routes/dev-server-logs.ts
  • apps/ui/src/components/views/board-view/worktree-panel/components/index.ts
  • apps/ui/src/types/electron.d.ts
  • apps/ui/src/components/ui/xterm-log-viewer.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/hooks/use-dev-server-logs.ts
  • apps/server/src/index.ts
  • apps/ui/src/components/views/board-view/worktree-panel/components/dev-server-logs-panel.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/components/worktree-tab.tsx
  • libs/types/src/event.ts
  • apps/ui/src/lib/electron.ts
  • apps/server/src/routes/worktree/index.ts
  • apps/server/src/middleware/validate-paths.ts
  • apps/server/src/services/dev-server-service.ts
  • apps/ui/src/lib/http-api-client.ts
  • apps/ui/src/components/views/board-view/worktree-panel/worktree-panel.tsx
apps/server/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use createEventEmitter() from lib/events.ts for all server operations to emit events that stream to frontend via WebSocket

Files:

  • apps/server/src/routes/worktree/routes/dev-server-logs.ts
  • apps/server/src/index.ts
  • apps/server/src/routes/worktree/index.ts
  • apps/server/src/middleware/validate-paths.ts
  • apps/server/src/services/dev-server-service.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: AutoMaker-Org/automaker PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T05:07:48.147Z
Learning: Applies to apps/server/src/**/*.{ts,tsx} : Use `createEventEmitter()` from `lib/events.ts` for all server operations to emit events that stream to frontend via WebSocket
📚 Learning: 2025-12-28T05:07:48.147Z
Learnt from: CR
Repo: AutoMaker-Org/automaker PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T05:07:48.147Z
Learning: Applies to apps/server/src/**/*.{ts,tsx} : Use `createEventEmitter()` from `lib/events.ts` for all server operations to emit events that stream to frontend via WebSocket

Applied to files:

  • apps/ui/src/types/electron.d.ts
  • apps/ui/src/components/views/board-view/worktree-panel/hooks/use-dev-server-logs.ts
  • apps/server/src/index.ts
  • libs/types/src/event.ts
  • apps/server/src/services/dev-server-service.ts
  • apps/ui/src/lib/http-api-client.ts
📚 Learning: 2025-12-30T01:02:07.114Z
Learnt from: illia1f
Repo: AutoMaker-Org/automaker PR: 324
File: apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx:122-131
Timestamp: 2025-12-30T01:02:07.114Z
Learning: Tailwind CSS v4 uses postfix syntax for the important modifier: append ! at the end of the utility class (e.g., backdrop-blur-[0px]! or hover:bg-red-500!). The older v3 style used a prefix (!) at the start (e.g., !backdrop-blur-[0px]); prefer the postfix form for consistency across TSX files.

Applied to files:

  • apps/ui/src/components/ui/xterm-log-viewer.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/components/dev-server-logs-panel.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/components/worktree-tab.tsx
  • apps/ui/src/components/views/board-view/worktree-panel/worktree-panel.tsx
🧬 Code graph analysis (8)
apps/server/src/routes/worktree/routes/dev-server-logs.ts (1)
apps/server/src/services/dev-server-service.ts (1)
  • getDevServerService (659-664)
apps/ui/src/components/ui/xterm-log-viewer.tsx (1)
apps/ui/src/config/terminal-themes.ts (2)
  • getTerminalTheme (568-578)
  • DEFAULT_TERMINAL_FONT (60-60)
apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx (2)
libs/types/src/settings.ts (1)
  • WorktreeInfo (564-575)
apps/ui/src/components/views/board-view/worktree-panel/types.ts (1)
  • WorktreeInfo (9-18)
apps/ui/src/components/views/board-view/worktree-panel/hooks/use-dev-server-logs.ts (1)
apps/ui/src/lib/electron.ts (1)
  • getElectronAPI (909-918)
apps/server/src/index.ts (1)
apps/server/src/services/dev-server-service.ts (1)
  • getDevServerService (659-664)
apps/server/src/routes/worktree/index.ts (2)
apps/server/src/middleware/validate-paths.ts (1)
  • validatePathParams (38-87)
apps/server/src/routes/worktree/routes/dev-server-logs.ts (1)
  • createGetDevServerLogsHandler (13-52)
apps/server/src/services/dev-server-service.ts (1)
apps/server/src/lib/events.ts (1)
  • EventEmitter (13-16)
apps/ui/src/components/views/board-view/worktree-panel/worktree-panel.tsx (5)
apps/ui/src/types/electron.d.ts (1)
  • WorktreeInfo (625-630)
libs/types/src/settings.ts (1)
  • WorktreeInfo (564-575)
apps/ui/src/components/views/board-view/worktree-panel/types.ts (1)
  • WorktreeInfo (9-18)
apps/ui/src/components/views/board-view/worktree-panel/components/dev-server-logs-panel.tsx (1)
  • DevServerLogsPanel (44-289)
apps/ui/src/components/views/board-view/worktree-panel/components/index.ts (1)
  • DevServerLogsPanel (2-2)
🔇 Additional comments (42)
apps/ui/src/lib/electron.ts (1)

1763-1777: LGTM!

The mock implementations for getDevServerLogs and onDevServerLogEvent are well-structured and consistent with other mock methods in this file. The console logging for subscribe/unsubscribe actions aids development debugging, and the return types align with the expected API contract.

apps/server/src/middleware/validate-paths.ts (1)

13-23: LGTM!

The getParamValue helper cleanly abstracts parameter extraction with correct priority (body first for POST/PUT, then query for GET). The unknown return type appropriately enforces type checking at call sites.

apps/ui/src/components/views/board-view/worktree-panel/components/index.ts (1)

1-4: LGTM!

The new DevServerLogsPanel export is correctly added to the barrel file, maintaining alphabetical ordering and enabling clean imports from the parent module.

apps/ui/src/components/views/board-view/worktree-panel/hooks/index.ts (1)

1-7: LGTM!

The useDevServerLogs hook export is correctly added to the barrel file, positioned logically near the related useDevServers export.

apps/server/src/routes/worktree/index.ts (2)

101-105: LGTM!

The new GET route for /dev-server-logs is correctly configured:

  • Uses GET method appropriate for read operations
  • Validates worktreePath from query params (leveraging the updated middleware)
  • Grouped logically with other dev server routes

36-36: LGTM!

Import follows the established pattern for route handlers in this file.

apps/server/src/index.ts (2)

70-70: LGTM!

The import follows the existing pattern for service imports in this file.


180-182: DevServerService cleanup is already handled via built-in signal handlers.

The service registers its own SIGTERM/SIGINT handlers (lines 669–675 of dev-server-service.ts) that call stopAll(), so no additional cleanup is needed in the main shutdown handlers. Orphaned processes are automatically stopped when the service receives shutdown signals.

libs/types/src/event.ts (1)

45-48: LGTM!

The new event types follow the established namespace:action naming convention and align well with the existing event type patterns (e.g., worktree:init-*).

apps/ui/src/components/views/board-view/worktree-panel/components/worktree-tab.tsx (2)

1-1: Good improvement!

Replacing // @ts-nocheck with an explicit import type { JSX } from 'react' is a proper fix that maintains type safety.


48-48: LGTM!

The new onViewDevServerLogs prop is correctly added to the interface, destructured in the component, and forwarded to WorktreeActionsDropdown. This follows the established pattern for other action callbacks in this component.

Also applies to: 91-91, 342-342

apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx (3)

28-28: LGTM!

The ScrollText icon is a suitable choice for a "View Logs" action.


60-60: LGTM!

The new prop is correctly added to the interface and component signature, following the established pattern for action callbacks.

Also applies to: 88-88


153-156: LGTM!

The "View Logs" menu item is well-placed in the Dev Server Running section, following the established styling patterns (text-xs, icon sizing). It logically appears after "Open in Browser" and before "Stop Dev Server".

apps/server/src/routes/worktree/routes/dev-server-logs.ts (1)

13-51: Handler implementation looks correct.

The endpoint follows the established pattern in the codebase with proper error handling and response structure. The validatePathParams('worktreePath') middleware is applied to the route and correctly validates the path parameter passed in the query string, protecting against path traversal attacks.

apps/ui/src/components/views/board-view/worktree-panel/worktree-panel.tsx (4)

15-15: LGTM!

Clean import from the components barrel export, following the established pattern in this file.


87-90: LGTM!

State management for the log panel follows the established patterns. Using separate state for visibility and selected worktree allows the panel to maintain smooth close animations.


171-181: LGTM!

Well-implemented handlers with appropriate useCallback usage. The comment explaining why logPanelWorktree is preserved during close (for animation smoothness) is helpful.


324-332: LGTM!

The DevServerLogsPanel is correctly wired with all necessary props. Since it uses a Dialog component (which portals to the document body), its placement in the JSX tree doesn't affect rendering.

apps/ui/src/components/views/board-view/worktree-panel/components/dev-server-logs-panel.tsx (4)

1-18: LGTM!

Imports are well-organized, using the @/ alias paths as required by coding guidelines. The component pulls in the appropriate UI primitives and the custom hook for log management.


56-70: LGTM!

Smart conditional logic that only activates the hook's API calls and WebSocket subscriptions when the panel is open, preventing unnecessary resource usage.


72-98: LGTM!

The effect logic handles all edge cases correctly:

  • Worktree changes trigger a full rewrite
  • Log clearing (shorter logs) triggers a full rewrite
  • New content is incrementally appended for efficiency

The ref-based tracking is a good pattern for avoiding unnecessary rewrites.


129-130: LGTM!

The line count calculation correctly handles the empty string case since '' is falsy in JavaScript, resulting in 0 lines.

apps/ui/src/components/ui/xterm-log-viewer.tsx (5)

1-4: LGTM!

Imports are correctly using @/ alias paths as required by coding guidelines. The component integrates with the app store for theme management.


88-171: Note: initialContent is only applied on initial mount.

The terminal initialization effect runs once (empty dependency array) and applies initialContent only during that first render. If initialContent changes after mount, it won't be reflected. This is likely intentional given the write method exists for updates, but worth noting for consumers of this component.

The lazy-loading pattern for xterm.js and graceful WebGL fallback are well implemented.


189-214: LGTM!

Robust resize handling with proper cleanup. The dimension check before fitting prevents errors when the container is hidden or has zero size.


216-238: LGTM with a minor caveat.

The scroll monitoring implementation is solid with passive listeners for performance. The 10px threshold for detecting "at bottom" is reasonable for handling sub-pixel rendering.

Note: The reliance on .xterm-viewport internal class name couples this to xterm.js implementation details, but this is a common pattern and unlikely to break.


266-276: LGTM!

The write method correctly uses reset() before writing to clear both the visible content and scrollback buffer, providing clean "replace all" semantics. The pending content queue ensures content isn't lost if methods are called before the terminal is ready.

apps/ui/src/components/views/board-view/worktree-panel/hooks/use-dev-server-logs.ts (4)

1-6: LGTM!

Imports correctly use shared packages (@automaker/utils/logger) and @/ alias paths as required by coding guidelines.


158-213: LGTM!

The WebSocket subscription is well-implemented:

  • Uses pathsEqual for cross-platform path comparison (handles Windows vs Unix paths)
  • Properly cleans up by returning the unsubscribe function
  • Event handling covers all three lifecycle states

The pattern of clearing logs on dev-server:started ensures a clean slate when the server restarts.


148-156: LGTM!

The effect correctly triggers log fetching when the worktree path changes or subscription is enabled, and clears state when disabled. The dependency array is complete.


215-220: LGTM!

The hook exposes a clean API with both state and action methods, following established React patterns. The returned fetchLogs allows manual refresh, clearLogs enables explicit reset, and appendLogs supports programmatic log injection if needed.

apps/ui/src/lib/http-api-client.ts (2)

513-559: LGTM! Well-structured event types and interfaces.

The new dev-server event types and interfaces are well-defined with appropriate discriminated union typing for DevServerLogEvent. The type definitions match the expected server-side payloads and follow the existing patterns in the codebase.


1759-1776: LGTM! Clean subscription pattern.

The implementation correctly follows the existing pattern used by onInitScriptEvent (lines 1788-1809). The combined unsubscribe function properly cleans up all three event subscriptions, preventing memory leaks.

apps/server/src/services/dev-server-service.ts (8)

66-131: Solid buffer management and throttling implementation.

The scrollback buffer correctly enforces the size limit by evicting oldest data. The output throttling at ~250fps with 4KB batches provides a good balance between responsiveness and preventing WebSocket flooding.

One minor note: outputBuffer has no explicit size limit and could theoretically grow large under extreme output conditions (e.g., a misbehaving dev server flooding stdout). However, given the 4ms flush interval, this window is very small and unlikely to cause issues in practice.


346-368: Good additions for livereload cleanup and ANSI color support.

The livereload port cleanup prevents port conflicts with HMR/livereload servers that use fixed ports. The FORCE_COLOR, COLORTERM, and TERM environment variables enable colored output for proper ANSI rendering in xterm.js.


380-406: LGTM! Clean handler setup with early serverInfo creation.

Creating serverInfo early allows the stdout/stderr handlers to reference it correctly. The handlers properly route through handleProcessOutput for buffer management and event emission.


408-455: Proper cleanup and event emission in error/exit handlers.

Both handlers correctly:

  1. Clear the flushTimeout to prevent memory leaks
  2. Check !serverInfo.stopping to prevent duplicate dev-server:stopped events
  3. Clean up allocated ports and running servers map

477-485: Correct timing for the started event.

The dev-server:started event is emitted only after verifying the process didn't fail immediately (checked at lines 460-472), ensuring clients receive accurate lifecycle notifications.


521-543: Well-implemented stop sequence.

The stop logic correctly:

  1. Sets stopping = true first to block further output events
  2. Clears the flush timeout and output buffer
  3. Emits dev-server:stopped immediately for responsive UI updates
  4. Then kills the process

The stopping flag prevents the exit handler from emitting a duplicate stopped event when the process terminates.


602-635: Clean implementation of log retrieval API.

The getServerLogs method correctly returns the scrollback buffer for replay on client reconnection, with appropriate error handling when no server is running.


15-60: Acceptable pattern for receiving the event emitter.

The service receives the EventEmitter via dependency injection rather than creating it internally. The emitter is created centrally using createEventEmitter() from lib/events.ts in apps/server/src/index.ts (line 165) and passed to the service during initialization via setEventEmitter() (line 182). This pattern satisfies the coding guideline requirement and maintains a single event bus for the application.

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>
@Shironex Shironex force-pushed the feat/dev-server-log-panel branch from 1bd803f to 073f6d5 Compare January 13, 2026 20:56
Resolved conflict in worktree-panel.tsx by combining imports:
- DevServerLogsPanel from this branch
- WorktreeMobileDropdown, WorktreeActionsDropdown, BranchSwitchDropdown from v0.11.0rc

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Shironex Shironex merged commit 32f6c6d into v0.11.0rc Jan 13, 2026
6 checks passed
@Shironex Shironex deleted the feat/dev-server-log-panel branch January 13, 2026 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants