gadget/gadget-code/docs/ui-design-guide.md
Rob Colbert e155a7ffcf Phase 2: SubProcess observability in DroneManager and DroneInspector
Adds real-time subprocess monitoring to the IDE via the callback-chain and
subscribe/broadcast socket patterns:

Shared types (packages/api):
- New subprocess.ts message types: SubProcessStat, RequestProcessStatsMessage,
  SubscribeDroneMessage, UnsubscribeDroneMessage
- New socket events in socket.ts: requestProcessStats, subscribeDrone,
  unsubscribeDrone (ClientToServer); drone:log, drone:status (ServerToClient)

Drone (gadget-drone):
- onRequestProcessStats handler calls SubProcessService.ps() + summarize(),
  returns typed SubProcessStat[] with status detection (exitCode/killed)

Backend routing (gadget-code):
- SocketService: getDroneSessionByRegistrationId(), droneMonitorIndex map,
  addDroneMonitor()/removeDroneMonitor()/broadcastToMonitors()
- CodeSession: requestProcessStats proxy, subscribeDrone/unsubscribeDrone
- DroneSession: broadcast drone:log + drone:status to monitor subscribers
  in addition to existing chat-session routing

Frontend socket layer:
- SocketClient: requestProcessStats(), subscribeDrone(), unsubscribeDrone()
- drone:log + drone:status events forwarded to event bus

Frontend components:
- LogRenderer — reusable log rendering core extracted from LogPanel
- SubProcessTable — btop-style process table with status dots
- DroneMonitorGauge — canvas oscilloscope waveform (studio-equipment aesthetic)
- DroneMonitor — 3-gauge container (CPU/red, NETWORK/cyan, FILE I/O/green)

Frontend pages:
- DroneManager: live log streaming, SubProcessTable, DroneMonitor gauges,
  1-second polling, auto-scroll log with pause-on-scroll-up
- Home/DroneInspector: process count summary, mini LogRenderer, navigate-to-manager link

Documentation:
- Updated gadget-drone/docs/subprocess.md Phase 2 section
- Updated gadget-code/docs/ui-design-guide.md Phase 2 section
- Updated docs/socket-protocol.md with new events, sequences, and patterns
2026-05-14 13:52:59 -04:00

19 KiB

Gadget Code IDE Style Guide

Gadget Code is an Agentic Integrated Development Environment (AIDE). It is a tool for professional software developers and project managers to use in the creation of software applications and solutions, to include: code, documentation, configuration, etc.

The brand color is #c20600, a rich red. It is to be used when printing Gadget Code.

The Gadget Code IDE (hereafter: IDE) is an HTML5 web app building using the latest stable ReactJS 19 and Tailwind CSS 4. The IDE delivers only industrial and purposeful dark theme that uses mostly blacks, near-black, and dark gray with light gray and brand color highlights.

Information-dense status and property panels. Tight margins and padding.

This is NOT a consumer news blog with giant bloated hero sections, etc. We prefer flat material design with thoughtful borders that help the eye find the information and focus on it - not distract from it.

Theme Colors

The IDE uses the following color palette (CSS custom properties):

--color-brand: #c20600
--color-bg-primary: #0a0a0a (main background - pure black)
--color-bg-secondary: #121212 (panels, sidebars)
--color-bg-tertiary: #1a1a1a (cards, dropdowns)
--color-bg-elevated: #202020 (hover states)
--color-text-primary: #d4d4d4 (main text)
--color-text-secondary: #a3a3a3 (muted text)
--color-text-muted: #737373 (disabled, hints)
--color-border-subtle: #1a1a1a (subtle dividers)
--color-border-default: #2a2a2a (default borders)
--color-border-highlight: #3a3a3a (emphasis borders)

Font stack:

  • Primary: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif
  • Code/Retro: Courier New, Courier, monospace

View Layout (Whole Application)

The root element is given fixed positioning to fill the browser view entirely, and presents a full-width/full-height Flex column that spans the entire view.

#root {
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

The top row is the header bar (48px). The 2nd row is the content area (flex-1). The 3rd/bottom row is the status bar (32px).

Header Bar

The header bar is basically our "window title bar" like for a desktop application:

GADGET CODE v1.0.0                                          [Username ▼]
  • Left: Application title + version using Courier New font
  • Right: User dropdown menu (display name, click to open)
    • Settings (placeholder)
    • Logout

Implementation: frontend/src/components/Header.tsx

Status Bar

The status bar displays:

Ready.                                                   my-project | BUILD | ●
  • Left: Status message ("Ready." default)
  • Center-right: Active project slug (when selected), Session mode (PLAN/BUILD/TEST/SHIP/DEV)
  • Right: Connection indicator (● = connected, animates when healthy)

Implementation: frontend/src/components/StatusBar.tsx

Content Area

Between the header and status bars is the Content Area. It uses React Router for URL-based navigation:

Route View
/ Home (authenticated dashboard or unauthenticated)
/projects Project Manager (list view)
/projects/:slug Project Manager (project selected)
/projects/new New project form
/sign-in Sign in page
/sign-out Signs out and redirects to /

Unauthenticated Home View

The logged-out home view displays a "System Ready" prompt dialog.

  • Font: Courier New (monospace)
  • Background: #0a0a0a (pure black)
  • Boxed with 2px border using border-default color
  • Sign In button

Users do not "sign up" for Gadget Code - accounts are administered via CLI (pnpm cli).

Implementation: frontend/src/pages/Home.tsx - SystemReady component

Authenticated Home View (Dashboard)

The authenticated home view presents:

[Welcome, Username!]
Your dashboard is under construction.
Select a project or chat session from the sidebar to get started.

[Open Project Manager]

+---------------------------+-----------------------+
|                           | Clock (AM/PM)         |
| Reserved for future       | Date                  |
| dashboard content         +-----------------------+
|                           | Projects    [+]       |
|                           | - project-1           |
|                           | - project-2           |
|                           +-----------------------+
|                           | Drones                |
|                           | - drone-alpha ●       |
|                           | - drone-beta ○        |
|                           +-----------------------+
|                           | Recent Chats          |
|                           | (loading...)          |
+---------------------------+-----------------------+

Components:

  1. Clock - local time in AM/PM format, current date
  2. Projects list - links to /projects/:slug, [+]/link navigates to /projects
  3. Drones list - status indicator (green=available, yellow=busy, gray=offline)
  4. Recent Chats - loading placeholder (stubbed, requires ChatSession API)

Implementation: frontend/src/pages/Home.tsx - DashboardSidebar component

Drone Inspector (Dashboard Inline)

When a user clicks a drone in the sidebar's Drones list, the main content area switches to the Drone Inspector:

+-----------------------------------------------------+
| Drone Inspector                        ← Back to    |
|                                         Dashboard   |
+-----------------------------------------------------+
| +-------------------------------------------------+ |
| | Hostname                                        | |
| | drone-alpha (mono)                              | |
| +-------------------------------------------------+ |
| | Workspace                                       | |
| | /path/to/workspace (mono)                       | |
| +-------------------------------------------------+ |
| | Status                                          | |
| | ● available (or ● busy, ● offline)              | |
| +-------------------------------------------------+ |
| | Registered                                      | |
| | 5/14/2026, 10:00:00 AM                          | |
| +-------------------------------------------------+ |
+-----------------------------------------------------+

Implementation: frontend/src/pages/Home.tsx - DroneInspector component (lines 36-93)

The Drone Inspector is a simple read-only card view. For full drone operations (terminate, logs, monitoring), users navigate to the Drone Manager at /drones via the gear icon in the Drones sidebar header.

Drone Manager View

Route: /drones

The Drone Manager is a full-page view for detailed drone inspection and operations. It replaces the main content area entirely.

Layout:

+----------------------------------+----------------------------------------+
| Drone Manager                    | Drone Details              [Terminate] |
+----------------------------------+----------------------------------------+
| Online Drones (N)                | +-----------+ +-----------+            |
|                                  | | Hostname  | | Status    |            |
| +----------------------------+   | | drone-1   | | ● busy    |            |
| | ● drone-1     available    |   | +-----------+ +-----------+            |
| | /path/to/workspace         |   | +-----------+ +-----------+            |
| +----------------------------+   | | Workspace | | Registered|            |
| +----------------------------+   | | /path/... | | 5/14/2026 |            |
| | ● drone-2     busy         |   | +-----------+ +-----------+            |
| | /path/to/workspace         |   |                                          |
| +----------------------------+   | Drone Monitor                            |
|                                  | +--------------------------------------+ |
| Offline Drones (N)              | | Monitor charts coming soon.           | |
|                                  | | Memory usage, AI operations, and log  | |
| +----------------------------+   | | production metrics will be displayed  | |
| | ○ drone-3     offline      |   | | here.                                | |
| | /path/to/workspace         |   | +--------------------------------------+ |
| +----------------------------+   |                                          |
|                                  | Drone Log (Live)                        |
|                                  | +--------------------------------------+ |
|                                  | | [10:00:01] [PLACEHOLDER] Log entry 1 | |
|                                  | | [10:00:02] [PLACEHOLDER] Log entry 2 | |
|                                  | | [10:00:03] [PLACEHOLDER] Log entry 3 | |
|                                  | | ...                                  | |
|                                  | +--------------------------------------+ |
+----------------------------------+----------------------------------------+

Current features (Phase 1):

  • Drone list (left sidebar): Split into Online and Offline sections. Each list item shows status dot, hostname, and workspace path. Click to inspect.
  • Drone details (right panel, top): 2x2 grid showing hostname, status with dot, workspace, registration date. Terminate button (red) for non-offline drones.
  • Drone Monitor (right panel, middle): Placeholder for future monitoring charts.
  • Drone Log (right panel, bottom): Collapsible panel with auto-scrolling log viewer. Log entries show timestamp and message. Auto-scroll pauses when user scrolls up, resumes when scrolled to bottom. Max 200 entries shown. Placeholder entries currently used, awaiting live log streaming.

Implementation: frontend/src/pages/DroneManager.tsx

Phase 2: SubProcess Monitor (Complete)

Phase 2 was implemented on the feature/process-management branch. The Drone Manager and Drone Inspector now show live SubProcess data and drone logs via Socket.IO.

Socket Protocol

Three new socket events added (defined in packages/api/src/messages/subprocess.ts):

Event Direction Purpose Mechanism
requestProcessStats IDE → Backend → Drone Request typed subprocess list Callback-chain (like fileTreeRequest)
subscribeDrone IDE → Backend Subscribe to live drone events (log, status) Registers socket in monitor index
unsubscribeDrone IDE → Backend Unsubscribe from live drone events Removes socket from monitor index

Broadcast events (backend → IDE, no request needed):

Event Payload When
drone:log { timestamp, level, component, message, metadata? } On every drone log emission
drone:status { timestamp, message } On every drone status emission

SubProcess Table

Rendered by frontend/src/components/SubProcessTable.tsx. Features:

  • Monospace table: PID, CMD, PROJECT, STATUS, UPDATED
  • Status dots: green for running, yellow for halted, red for error
  • Clickable rows with selection highlight
  • Header shows running count: "SubProcess Monitor (3 running)"
  • Empty state: "No managed subprocesses. The agent has not spawned any processes for this session."

Resource Gauges (Canvas)

Rendered by frontend/src/components/DroneMonitor.tsx + DroneMonitorGauge.tsx:

┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ CPU             │ │ NETWORK         │ │ FILE I/O         │
│   ▁▂▃▄▅▆▇█▇▆    │ │   ▁▂▃▄▅▆▇█▇▆    │ │   ▁▂▃▄▅▆▇█▇▆    │
│      42%        │ │      27%        │ │      15%        │
└─────────────────┘ └─────────────────┘ └─────────────────┘
  • Canvas-based waveform with glow effect (studio/scientific equipment aesthetic)
  • Brand red for CPU, cyan for NETWORK, green for FILE I/O
  • Grid lines, numeric readout, gradient fill under trace
  • Data is currently simulated (random walk) — placeholder for real metrics

Live Log Streaming

The Drone Log viewer now receives real log events via socket instead of placeholders:

  • Log events flow: droneDroneSession.onLog() → broadcast to monitor sessions via drone:log event
  • Uses the same LogRenderer component as the ChatSession LogPanel (refactored in Phase 2)
  • Auto-scroll with pause-on-scroll-up behavior preserved
  • Max 200 entries maintained

DroneInspector (Dashboard)

The inline Drone Inspector in the Home page (frontend/src/pages/Home.tsx):

  • Shows SubProcess count summary ("3 running" or "No managed processes")
  • Contains a compact LogRenderer (last 50 entries, max-h-48)
  • "Open in Drone Manager →" link navigates to /drones with route state for auto-selection

Polling Behavior

  • requestProcessStats polled every 1 second while a drone is selected (both DroneManager and DroneInspector)
  • Subscribe/unsubscribe lifecycle tied to component mount/unmount
  • All timers and socket listeners cleaned up on unmount or drone deselection

Supporting Components

Component File Purpose
SubProcessTable frontend/src/components/SubProcessTable.tsx Process list table
DroneMonitor frontend/src/components/DroneMonitor.tsx 3-gauge resource monitor container
DroneMonitorGauge frontend/src/components/DroneMonitorGauge.tsx Canvas waveform gauge
LogRenderer frontend/src/components/LogRenderer.tsx Reusable log rendering core

Backend Routing

  • SocketService.addDroneMonitor() / removeDroneMonitor() — manages monitor index (Map<registrationId, Set<socketId>>)
  • SocketService.broadcastToMonitors() — broadcasts events to all monitoring sockets
  • SocketService.getDroneSessionByRegistrationId() — lookup for non-chat-session drone routing
  • DroneSession.onLog() / onStatus() — extended to broadcast to monitors after chat-session routing
  • CodeSession.onRequestProcessStats() — proxies to drone with callback
  • CodeSession.onSubscribeDrone() / onUnsubscribeDrone() — registers/unregisters monitor

Project Manager View

The Project Manager presents:

+---------------------------+----------------------------------+
| [New Project]             | Project Inspector                |
|---------------------------|                                  |
| Projects (2)              | Select a project to view details |
| [project-one      ]       | or create a new project          |
| [project-two      ]       |                                  |
|                           |                                  |
+---------------------------+----------------------------------+

When a project is selected:

+---------------------------+---------------------------------+
| [New Project]             | Project Inspector               |
|---------------------------|                                 |
| Projects (2)              | Name: project-one               |
| [project-one ●]           | Slug: project-one               |
| [project-two   ]          | Git URL: https://github.com/... |
|                           | Status: active                  |
|                           | Created: 2026-04-28             |
|                           |                                 |
|                           | [Delete Project]                |
|                           +---------------------------------+
|                           | Chat Sessions                   |
|                           | (loading...)                    |
+---------------------------+---------------------------------+

Features:

  • Left sidebar: Project list with [+ New Project] button
  • Project Inspector: Shows name, slug, gitUrl, status, createdAt
  • Delete: Confirmation before deletion
  • Chat Sessions placeholder (requires ChatSession API)

URL-based: /projects (list), /projects/:slug (selected)

Implementation: frontend/src/pages/ProjectManager.tsx

New Project Form

Triggered by [New Project] button or /projects/new route:

  • Fields: Project Name*, Project Slug*, Git Repository URL
  • Slug tip: "Unique identifier for the project directory"
  • Submit: "Create Project" button (brand color)
  • Cancel: Returns to project list

Authentication

Frontend Token Management

  • Token stored in localStorage: dtp_auth_token
  • User stored in localStorage: dtp_user
  • Project stored in localStorage: dtp_current_project

API Requests

All API requests include the JWT in the Authorization header:

headers["Authorization"] = `Bearer ${token}`;

Backend Session Restoration

The backend restores user sessions from:

  1. Authorization: Bearer <token> header (JWT)
  2. Express session (fallback)

The requireUser() middleware ensures endpoints are authenticated.

Security

Password Field Handling

Password credentials are NEVER exposed:

  • Mongoose select: false on password fields in models
  • Population uses select: "-passwordSalt -password" to exclude from queries
  • Frontend User interface only includes: _id, email, displayName, flags

Chat Session View (Planned)

The Chat Session View presents:

Work Area                                     | Session Status
----------------------------------------------|---------------
Chat Messages                                 | Chat: name
                                              | ID: ...
                                              | Model: ...
----------------------------------------------|---------------
[Prompt input                  ][Expand][Send]| TC | FO | SA
----------------------------------------------|---------------
Log                                           | Files
                                              |

Implemented components:

  • Chat Messages (stubbed)
  • Prompt Input (stubbed)
  • Session Status sidebar (stubbed)
  • File Browser (stubbed)

Mobile Devices and Responsive Design

Nope. Gadget Code is a desktop workstation experience with one or more high-resolution displays, keyboard, mouse, webcam, microphone, and ample networking. There is no planned support for mobile devices.

Buttons are normal-sized, not bloated for finger use.

Accessibility

The IDE wants to be as accessible as possible, providing aria tags as much as possible. Build first with an eye toward accessibility, then take a 2nd pass after the build is accepted to add accessibility enhancements.

Tests

Unit Tests

Location: tests/**/*.test.ts (excluding tests/e2e/)

Run: pnpm test

E2E Tests

Location: tests/e2e/**/*.test.ts

Run: npx playwright test

Prerequisites: Running pnpm dev:backend and pnpm dev:frontend

Target: https://code-dev.g4dge7.com:5174

Build Commands

cd gadget-code
pnpm dev:backend    # Backend on https://localhost:3443
pnpm dev:frontend  # Frontend on https://localhost:5174
pnpm build         # Build all (backend -> dist/, frontend -> frontend/dist/)
pnpm test          # Unit tests
npx playwright test # E2E tests