Frontend Architecture
Project structure
Section titled “Project structure”frontend/src/├── app.html # HTML shell├── app.css # Global styles├── app.d.ts # TypeScript declarations├── hooks.server.ts # Server hooks (Supabase SSR setup)├── lib/│ ├── api.ts # REST API client (apiFetch, api object)│ ├── ws.ts # WebSocket client (createChatWebSocket)│ ├── supabase.ts # Supabase client factories│ ├── index.ts # Barrel exports│ ├── stores/│ │ ├── auth.ts # Auth state (user, session, loading)│ │ ├── session.ts # Session/chat state (sessions, activeSession)│ │ └── models.ts # Available models store│ ├── components/│ │ ├── chat/│ │ │ ├── ChatArea.svelte # Main chat area (manages tabs)│ │ │ ├── ChatPanel.svelte # Single chat panel with messages│ │ │ ├── ChatInput.svelte # Message input with send button│ │ │ ├── ChatMessage.svelte # Single message bubble│ │ │ ├── ChatTab.svelte # Tab header for a chat│ │ │ ├── AddChatDialog.svelte # Dialog to add a new chat│ │ │ ├── ModelPicker.svelte # Model selection dropdown│ │ │ └── StreamingIndicator.svelte # Typing indicator│ │ ├── context/│ │ │ └── ContextPicker.svelte # Context item search and selection│ │ ├── layout/│ │ │ ├── DashboardLayout.svelte # Main layout with sidebar│ │ │ └── NavBar.svelte # Top navigation bar│ │ ├── session/│ │ │ └── SessionList.svelte # Sidebar session list│ │ └── ui/│ │ └── Toast.svelte # Notification toast│ └── assets/│ └── favicon.svg└── routes/ ├── +layout.svelte # Root layout (auth initialization) ├── +page.svelte # Landing/redirect page ├── (auth)/ │ ├── login/+page.svelte # Google OAuth login page │ └── callback/+page.server.ts # OAuth callback handler └── (app)/ └── dashboard/ ├── +page.server.ts # Auth guard, load sessions └── +page.svelte # Main dashboard pageRoute groups
Section titled “Route groups”The frontend uses SvelteKit route groups to separate auth and app concerns:
(auth)/— Login and OAuth callback pages. No auth guard.(app)/— Protected pages. The+page.server.tsload function verifies the Supabase session and redirects to login if not authenticated.
Svelte stores
Section titled “Svelte stores”auth store
Section titled “auth store”Holds the current Supabase user and session:
interface AuthState { user: User | null; session: Session | null; loading: boolean;}session store
Section titled “session store”Manages chat sessions and the active selection:
export const sessions = writable<ChatSession[]>([]);export const activeSession = writable<ChatSession | null>(null);export const activeTabIndex = writable<number>(0);A ChatSession contains an array of Chat objects, each with its own messages array and model_name.
models store
Section titled “models store”Lists available LLM models fetched from the backend:
export const availableModels = writable<ModelInfo[]>([]);REST API client
Section titled “REST API client”The api.ts module provides a typed wrapper around fetch:
export const api = { sessions: { list, create, delete, updateMaster }, chats: { add, remove, query, sync, import }, models: { list }, contextItems: { list, search, create }, suggestions: { search },};All methods accept a token parameter (the Supabase access token) which is sent as a Bearer token. The base URL is constructed from window.location.origin + the configured base path.
WebSocket client
Section titled “WebSocket client”The ws.ts module exports createChatWebSocket(sessionId, accessToken), which returns:
| Property | Type | Description |
|---|---|---|
sendQuery | (chatId, content) => void | Send a message to one chat |
sendQueryAll | (content, chatIds[]) => void | Send the same message to multiple chats |
tokens | Writable<{[chatId]: string}> | Accumulated streamed tokens per chat |
done | Writable<{[chatId]: boolean}> | Whether streaming is complete per chat |
errors | Writable<{[chatId]: string}> | Error messages per chat |
connected | Writable<boolean> | WebSocket connection status |
close | () => void | Close the connection |
resetChat | (chatId) => void | Clear tokens/done/error for a chat |
The client automatically reconnects after 3 seconds on disconnect.
Component hierarchy
Section titled “Component hierarchy”DashboardLayout├── NavBar├── SessionList (sidebar)└── ChatArea ├── ChatTab (one per chat, horizontal tabs) ├── AddChatDialog │ ├── ModelPicker │ └── ContextPicker └── ChatPanel (active chat) ├── ChatMessage (repeated) ├── StreamingIndicator └── ChatInputThe ChatArea component manages the tab bar and creates a ChatPanel for the active tab. When a user sends a message, it calls ws.sendQuery() for single-chat mode or ws.sendQueryAll() for multi-link mode, and subscribes to the tokens store to render the streaming response.