The frontend uses React, TanStack Router, TanStack Query, Zustand, shadcn/ui, and Rsbuild.

Route tree

Routes live in pinter-fe/src/routes. The app sidebar is the main navigation interface:
It exposes:
  • New chat.
  • Claim code redemption.
  • My usage.
  • Workspace items filtered by role level.
  • Conversation history.
  • User menu.

Data access seam

Frontend API access goes through one seam:
Use these exported functions instead of calling fetch directly: This module handles token refresh, 401 retry, logout on terminal auth failure, JSON errors, uploads, and streaming.

Conversation streaming

useAgentStream sends POST /api/conversations/{conversationId}/send through stream() with stream: true:
The backend defaults stream to false; the frontend opts in so it can consume SSE chunks with ReadableStream.getReader(). thinking events update the trace panel; text events update the answer bubble. The frontend also sends X-Pinter-Response-Format: blocks. It renders block events for complete one-line JSON blocks: html, flint_chart, image, and followup. Ordinary prose remains delta text. Do not split block JSON across SSE chunks or lines.

Domain packages

The canonical API is split into eight domains: identity, agents, conversations, knowledge, integrations, evaluations, governance, and content. Frontend domain code lives under src/apps; knowledge screens currently remain route-owned. A domain may import its own internals. Any other consumer must import from @/apps/<domain>, never @/apps/<domain>/...; ESLint enforces this boundary.

State modules

Role-aware UI

Workspace navigation uses role_level: Route guards live in:
The frontend guard hides routes and redirects users. Backend route permissions remain the source of truth for security.