Files
headquarter/docs/architecture/frontend.md
T
alex a01e6252f5 docs: add responsive terminal documentation
- Add docs/features/terminal.md with user guide, connection states,
  keyboard shortcuts, protocol details, and troubleshooting
- Update docs/architecture/frontend.md with terminal component stack,
  connection hook behavior, and data flow diagrams
- Update docs/architecture/backend.md with terminal system architecture,
  protocol reference, message batching, and reconnect behavior
- Update docs/README.md to include terminal in feature list
2026-05-27 21:46:57 +02:00

10 KiB

Frontend Architecture

Overview

The Headquarter frontend is a React-based single-page application (SPA) built with modern tooling and designed for modularity and maintainability.

Tech Stack

Layer Technology Version
Framework React ^18.2.0
Router React Router ^6.20.0
Bundler Vite ^5.0.0
Language TypeScript ^5.3.0
Styling CSS3 with CSS Variables -
Testing Vitest + React Testing Library ^4.1.6

Directory Structure

apps/web/src/
├── api/                  # API clients
│   ├── auth.ts          # Authentication API
│   ├── projects.ts      # Project API
│   ├── git_repositories.ts  # Repository API
│   ├── ssh_keys.ts      # SSH key API
│   ├── tool_types.ts    # Tool type API
│   ├── users.ts         # User API
│   ├── sessions.ts      # Tool instance sessions API
│   └── settings.ts      # Settings API
├── components/           # Reusable components
│   ├── app-shell.tsx    # Main app layout
│   ├── terminal.tsx     # xterm.js terminal component
│   ├── protected-route.tsx  # Auth guard
│   └── [more...]
├── context/             # React contexts
│   └── auth.tsx         # Auth state management
├── hooks/               # Custom hooks
│   ├── use-auth.ts      # Auth hook
│   ├── use-theme.ts     # Theme hook
│   └── use-terminal-connection.ts  # Terminal WebSocket lifecycle
├── pages/               # Page components (routes)
│   ├── dashboard.tsx    # Dashboard
│   ├── projects.tsx     # Project list
│   ├── repo-workspace.tsx   # Repository workspace
│   ├── git-history.tsx  # Git history
│   ├── git-repositories.tsx # Repository management
│   ├── terminal.tsx     # Web terminal
│   ├── profile.tsx      # User profile
│   ├── settings.tsx     # User settings
│   ├── tool-types.tsx   # Tool types
│   ├── ssh-keys.tsx     # SSH keys
│   └── [more...]
├── styles/              # Global styles
│   ├── index.css        # Main stylesheet
│   └── [more...]
├── types.ts             # Shared TypeScript types
├── router.tsx           # Route definitions
└── main.tsx             # Entry point

Architecture Patterns

1. Component Architecture

Page Components: Top-level components mapped to routes

  • Own data fetching
  • Manage page-level state
  • Compose reusable components

Reusable Components: Shared UI elements

  • No data fetching
  • Receive data via props
  • Emit events via callbacks

Example:

// Page component
const RepoWorkspace = () => {
  const [files, setFiles] = useState([]);
  // ... fetch data, manage state
  return (
    <div className="workspace">
      <FileTree files={files} onFileClick={handleFileClick} />
      <FileViewer content={content} />
    </div>
  );
};

// Reusable component
const FileTree = ({ files, onFileClick }: FileTreeProps) => {
  return (
    <ul>
      {files.map(file => (
        <li onClick={() => onFileClick(file)}>{file.name}</li>
      ))}
    </ul>
  );
};

2. State Management

URL State: Shareable, bookmarkable state

// Sync selections to URL
const [searchParams, setSearchParams] = useSearchParams();
// ?repo=123&branch=main&path=src/main.py

React Context: Global auth state

// Auth context provides user, login, logout
const { user, isAuthenticated } = useAuth();

Local State: Component-specific state

const [isEditing, setIsEditing] = useState(false);

3. API Client Pattern

Centralized API clients with type safety:

// api/git_repositories.ts
export const getRepositories = async (projectId: string) => {
  const response = await fetch(`${API_BASE_URL}/projects/${projectId}/repositories`, {
    credentials: 'include',
  });
  return response.json();
};

// Usage in component
const repos = await getRepositories(projectId);

4. Authentication Flow

User clicks Login
  → Redirect to /auth/login (backend)
  → Backend redirects to Authentik OAuth
  → User authenticates with Authentik
  → Authentik redirects to /auth/callback
  → Backend creates session cookie
  → Backend redirects to frontend
  → Frontend checks /auth/me
  → User is authenticated!

Auth State:

interface AuthState {
  user: User | null;
  isAuthenticated: boolean;
  isLoading: boolean;
}

5. Routing Structure

// router.tsx
<Route path="/" element={<Dashboard />} />
<Route path="/projects" element={<ProjectsPage />} />
<Route path="/projects/:projectId" element={<RepoWorkspace />} />
<Route path="/projects/:projectId/repositories" element={<GitRepositories />} />
<Route path="/projects/:projectId/repositories/:repoId/history" element={<GitHistory />} />
<Route path="/terminal/:instanceId" element={<TerminalPage />} />
<Route path="/profile" element={<ProfilePage />} />
<Route path="/settings" element={<SettingsPage />} />
<Route path="/ssh-keys" element={<SSHKeysPage />} />
<Route path="/tool-types" element={<ToolTypesPage />} />

Data Flow

Repository Workspace Example

1. User clicks project
   → Navigate to /projects/:id
   
2. RepoWorkspace mounts
   → Fetch project repositories
   → Select first repo (or from URL)
   
3. Repo selected
   → Fetch branches
   → Fetch file tree (default branch)
   
4. User clicks file
   → Fetch file content
   → Display in viewer
   → Update URL: ?path=src/main.py
   
5. User switches branch
   → Fetch file tree for branch
   → Re-fetch current file if viewing
   → Update URL: ?branch=develop

Component Communication

┌─────────────────────────────────────┐
│           RepoWorkspace              │
│  ┌──────────┐    ┌──────────────┐  │
│  │ FileTree │───▶│  FileViewer  │  │
│  │          │    │              │  │
│  │ onFileClick  │  content     │  │
│  │          │    │  onEdit      │  │
│  └──────────┘    └──────────────┘  │
│       ▲                              │
│       │                              │
│  ┌──────────┐                       │
│  │ Branch   │───▶ fetch tree        │
│  │ Selector │                       │
│  └──────────┘                       │
└─────────────────────────────────────┘

Styling Strategy

CSS Variables (Design Tokens)

:root {
  --color-primary: #007bff;
  --color-bg: #ffffff;
  --color-text: #333333;
  --sidebar-width: 250px;
  --border-radius: 4px;
}

[data-theme="dark"] {
  --color-bg: #1a1a1a;
  --color-text: #e0e0e0;
}

Component Styles

  • Each page/component has scoped CSS
  • Global utilities in styles/index.css
  • No CSS-in-JS library (keep it simple)

Testing Strategy

Unit Tests (Vitest)

// Component test
import { render, screen } from '@testing-library/react';
import { FileTree } from './file-tree';

test('renders file list', () => {
  const files = [{ name: 'test.py', type: 'file' }];
  render(<FileTree files={files} onFileClick={() => {}} />);
  expect(screen.getByText('test.py')).toBeInTheDocument();
});

Test Coverage

  • Component rendering
  • User interactions
  • Auth state changes
  • API mocking

Performance Considerations

  1. Code Splitting: Vite handles automatic chunking
  2. Lazy Loading: React.lazy() for heavy pages
  3. Debouncing: URL updates debounced (300ms)
  4. Caching: Browser caches API responses (ETags)
  5. Optimistic UI: Immediate feedback before API response

Terminal Architecture

The web terminal is the most complex component in the frontend. It bridges a browser-based terminal emulator with a server-side PTY session.

Component Stack

TerminalPage (route)
└── TerminalComponent
    ├── Status bar (connection state, latency, actions)
    ├── Session-ended overlay (reconnect / go back)
    ├── Reconnect banner (spinner + countdown)
    └── xterm.js (terminal emulator)
        ├── FitAddon (auto-resize to container)
        ├── SerializeAddon (scrollback serialization)
        └── WebLinksAddon (clickable URLs)

Connection Hook

useTerminalConnection manages the full WebSocket lifecycle:

CONNECTING
  → onopen → CONNECTED → heartbeat every 15s
  → onclose (unexpected) → RECONNECTING
    → backoff: 1s → 2s → 4s → 8s → 16s → 30s max
    → up to 10 attempts
    → onopen → restore scrollback → CONNECTED
  → onclose (expected) → DISCONNECTED

Key behaviors:

  • Local echo: Printable ASCII chars appear instantly; server echo is deduplicated
  • Resize: Debounced 200ms, throttled to 1 message per 500ms
  • Scrollback: Serialized to sessionStorage on disconnect, restored on reconnect
  • Keyboard: Ctrl+Shift+R triggers manual reconnect

Data Flow

User types 'a'
  → xterm onData event
  → useTerminalConnection.sendInput('a')
  → local echo writes 'a' to xterm immediately
  → WebSocket sends 'a' to server
  → server PTY echoes 'a' back
  → client receives 'a' via binary frame
  → deduplicates against pending echo buffer
  → (no-op if matched, or writes remaining chars)

Future Improvements

  • Add React Query for server state management
  • Implement virtual scrolling for large file trees
  • Add service worker for offline support
  • Implement real-time updates (WebSocket) — Terminal done
  • Add error boundary components

Development Workflow

# Start dev server
cd apps/web && npm run dev

# Run tests
npm run test

# Type check
npm run typecheck

# Lint
npm run lint

# Build for production
npm run build