📚 Docs / Frontend — React UI, stores, streaming & theming

Frontend — React UI, stores, streaming & theming

The frontend is a Vite + React 18 + TypeScript SPA with Tailwind CSS, Zustand state management, ReactFlow for the pipeline editor, and react-markdown for rendering answers. It talks only to the FastAPI backend at http://localhost:8765.

1. Layout (frontend/src/App.tsx)

BootLoader ──(health check /api/health)──▶ App
App
├── Sidebar                 (conversations, theme toggle, workspace toggle)
├── main
│   ├── ChatArea            (messages, streaming answer, statuses, thinking,
│   │                        tool calls, citations)
│   ├── ApprovalBar         (human-in-the-loop approve/reject, when pending)
│   └── InputBar            (send, attach files, folder pick, drag-and-drop)
└── Right panel (one at a time)
    ├── SourcesPanel        (sources, pipeline dashboard, execution log,
    │                        working set, memo export)
    ├── WorkspacePanel      (discovered folder files)
    └── PipelineEditor      (visual pipeline canvas, 560px panel)
Plus: CommandPalette (Cmd/Ctrl+K), SourceViewerModal (citation detail)

2. Chat flow (App.handleSend)

  1. Ensure a conversation exists; append the user message; start streaming.
  2. If pendingFiles exist → uploadFile() each (sequential, with status events), collect uploadedPaths/uploadedNames; if the user typed nothing, the query becomes a default batch-scan prompt ("Scan these documents for compliance issues: …").
  3. streamChat({query, mode, top_k, conversation_id, attached_file_paths, attached_filenames}) — an async generator parsing SSE lines.
  4. Event switch:
    • meta — ignored
    • intent — status line with icon + confidence
    • statusaddStreamingStatus
    • thinkingappendStreamingThinking (chain-of-thought)
    • tool_call / tool_result → streaming tool cards
    • plan → plan result line (the routing directive, humanized)
    • node_start / node_complete → execution log (pipeline nodes)
    • sourcessetSources
    • interruptedsetApprovalPending (shows ApprovalBar)
    • resumed → clear approval, continue
    • tokenappendStreamingToken
    • donefinalizeStreamingMessage(sources, execNodes)
  5. If the stream ends without done and no approval is pending, finalize anyway (interrupt fallback).

handleResume(approved) calls resumeChat(threadId, response, 5) and feeds the same event pipeline.


3. State management (frontend/src/store/chatStore.ts)

Zustand store, persisted per project: the storage key is freebuff-chat-storage plus a per-project hash suffix (freebuff-chat-storage:<project-hash>, re-pointed via projectStorage.ts when the workspace changes), and the same data is also saved server-side to the project's workspace/history.json (only conversations + activeConversationId + starred messages are persisted; streaming state is ephemeral; Date objects are rehydrated via rehydrateDates).

Key state:


4. API layer (frontend/src/services/api.ts)


5. Right-panel components

SourcesPanel (components/Sources/SourcesPanel.tsx, 360px)

WorkspacePanel

Shows files discovered from a selected folder (supported document extensions only, capped at 50) with per-file remove and clear-all.

ScanPanel (components/Scan/ScanPanel.tsx)

Chat components (components/Chat/)

ChatArea, Message, StreamingMessage (react-markdown + GFM), SkeletonMessage, TypingIndicator, InputBar (attachment chip list, drag- and-drop), ApprovalBar, ScanFlagsCard (compliance flags on the answer), ConfidenceMeter, AnswerTransparency, InlineCitation, BootLoader, ScanProgressBar.


6. Streaming UX


7. Theming (light & dark)


8. Tooling & scripts

Questions, answered

Short, self-contained answers about this guide.

How is the frontend organized?

Vite + React with Zustand stores for chat state, provider status, and working sets. Components live under frontend/src/components — Chat, Sources, Pipeline, Sidebar, Scan, and UI — with an Electron preload bridge for desktop APIs.

How does streaming rendering work?

The chat store consumes SSE events from the backend; streaming message components render tokens live while reasoning streams into a separate chain-of-thought panel, and a skeleton message shows while the pipeline boots.

Can I resize panels?

Yes — every major panel (sources, sidebar, working set, chat) has a drag handle, and sizes persist per session. The docs cover the panel storage helpers and right-panel tabs.