-
-
Notifications
You must be signed in to change notification settings - Fork 0
2.2 chat
The Chat area is the reason 3F exists: a complete, streaming, multi-modal conversation UI that shows its work. Component root: src/components/chat/.
┌─ conversation-header ─ model, session, conversation controls (rename/delete/pin) ─┐
│ │
│ exchange-list ┌─ right-panel ────────────┐│
│ ├ chat-exchange (user) │ session metadata, ││
│ ├ chat-exchange (assistant) │ attachments, tools, ││
│ │ └ exchange-content │ playlist ││
│ │ ├ reasoning-area └──────────────────────────┘│
│ │ └ assistant-response (structured) │
│ └ pending-indicator (streaming state) │
├─ prompt-action-bar ─ input, images, submit, cancel, think ────┤
└─ toolbar · floating-player · floating-playlist (media) ────────┘
An exchange is one user request + the assistant's full response cycle, keyed by requestId and persisted per session/conversation (HarnessConversation.content). The exchange-list renders the history; each chat-exchange shows:
- the user prompt with its attached images;
- the assistant-response, rendered from the harness's structured schemas — comparisons, describe/OCR blocks, summaries, articles, product/news cards, image/video galleries, and stock-market answers render as UI elements, not raw prose; foreign-language articles/videos appear in an international coverage aside so they augment rather than displace primary-language content.
The exchange list supports two scroll modes, switchable per conversation from the SysCtl chat-navigation section and persisted in localStorage (app store):
-
Carousel (
mode: 'carousel') — a vertical carousel of full-height sections (vertical-carousel/). Each exchange section is exactly one viewport tall; the container snaps between them and scrolling crossfades the outgoing section into the incoming one (useCarouselBlendcomputes per-slide opacity from scroll position, so the blend reads as a seamless cut rather than a hard jump).useVerticalCarouselowns the auto-scroll/pin behavior: a new prompt snaps to the newest section, the bottom pin releases once the first response content arrives so the user can read from the top, and a manual scroll-away is preserved until the response finishes. -
Native (
mode: 'native') — a continuous scroll container with variable-height sections (useNativeScroll): auto-scroll-to-bottom while the user is at the bottom, active-section highlight, andscrollToSectionfor history-click navigation.
Both modes share ScrollableExchangeList (the container that renders either variant) and CarouselSection (one section = user exchange + its assistant exchanges). Switching modes preserves the section the user was reading instead of jumping to the bottom. The chat column is composed by ChatMainColumn; empty states render ExchangeEmptyState / NoConversationPanel.
stockmarketitem / stockmarketlist responses render with a D3 chart suite (templates/stockmarket-response/d3-charts/): D3UnifiedStockChart (OHLC/heatmap/HLC-area with crosshair tooltips, zoom, and range controls) and D3StackedAreaChart. Charts fetch cached end-of-day history from GET /api/v1/stock-data/history (see 1.1), paginate via use-stock-history-pager, and size their range controls from the coverage endpoint. The previous lightweight-charts implementation was removed.
Image galleries with more than a couple of items render as a carousel (sections/gallery-section/carousel/): a scroll-snap track with prev/next buttons, active-index tracking from scroll position, and a header. AssistantCarousel orchestrates the track; CarouselContent/CarouselHeader render it.
Models with thinking support stream their chain-of-thought as a distinct phase. The reasoning-area renders it collapsed-but-inspectable inside the exchange — you can watch the model think without letting it dominate the answer. use-chat-think wires the think parameter so thinking is a per-conversation choice.
The conversation header manages the thread's lifecycle directly:
-
Rename — inline title editing (
ConversationTitleEditor). - Delete — remove the conversation and its uploaded objects.
-
Pin / Unpin — toggle a conversation between temporary and persistent (
ConversationHeaderActions), so important threads survive session cleanup while throwaway ones stay ephemeral.
- Tokens land on the Socket.IO room as
text/streampayloads (see 1.4) and append live;pending-indicatorreflects the harness phase rather than a spinner-of-doom. -
use-frozen-read-snapshot+use-read-trackerfreeze the reading viewport while content grows, and release it precisely — no scroll fights mid-stream. -
Cancel posts
/api/v1/harness/canceland waits forcancel_result— in-flight generation actually stops (see 1.2).
The prompt action bar handles multimodal input: text plus one or more images (validated before upload), numCtx awareness (use-chat-context-size shows how much of the window the current thread consumes — the feature many proprietary chats hide), model picker fed by the live catalogue, and submit/cancel that maps 1:1 to the harness lifecycle.
Chat is not isolated: model + preprocessing + provider overrides configured in SysCtl apply to the next request; failed jobs surface in DLQ where they can be repaired and re-instated; PProc previews exactly which image variants the server will compute from your uploads.
components/debug and the debug store mirror the raw socket/API traffic. Every exchange can be traced client-side from submission through room events to the persisted record — the dashboard treats observability as a user feature.