Frontend Architecture
The frontend is a React 19 single-page application. It does not use React Router — navigation is driven entirely by a Zustand store. All communication with the native backend goes through the Tauri IPC bridge (invoke and listen).
Application entry point
Section titled “Application entry point”src/App.tsx is the root component. On mount it:
- Detects whether the current window is the system tray panel (
isTrayPanel()). If so, it renders the lightweightTrayPanelAppinstead of the full shell. - Calls
is_first_launch()via IPC to detect a fresh install or post-factory-reset state. - Shows either the onboarding flow (
FirstLaunch) or the main app shell (AppShell). - Registers all global effect hooks (event listeners, periodic syncs, shortcut interceptors).
The AppShell component wraps the router logic and renders the correct page based on the active route in useAppStore.
Custom router
Section titled “Custom router”Navigation is a Route string union stored in useAppStore:
"dashboard" | "session" | "settings" | "extensions" | "projects" |"sessions" | "changelog" | "project-settings" | "showcase" |"workspaces" | "workspace-create" | "workspace-settings" |"workspace-home" | "workspace-sessions"AppShell reads the current route and conditionally renders the corresponding page component. There are no URLs, no history API, and no link-based navigation — all transitions happen through useAppStore actions (openSession, goHome, openSettings, etc.).
Zustand stores
Section titled “Zustand stores”State is split into focused slices, each in src/store/. Stores that hold large or complex state are further split into sub-slices within their own subdirectory.
Core app state
Section titled “Core app state”| Store | Description |
|---|---|
useAppStore | Active route, active session ID, sidebar filters, view mode, command palette state |
useMagiaStore | All discovered sessions (sliced: session CRUD, pin/unpin, title sync, tools/dirs, branch info) |
useDiscoveryStore | Filesystem scan state — triggers and coordinates discovery of provider session directories |
useDiscoveryCacheStore | Persistent cache of previously scanned sessions; avoids rescanning unchanged directories |
useWorkspacesStore | Named workspaces (groups of projects) and active workspace selection |
Agent runtime
Section titled “Agent runtime”| Store | Description |
|---|---|
useAgentEventsStore | Live event stream per provider session ID — events, projections, dedup logic |
useSessionTasksStore | Task list extracted from agent events for the active session |
useSessionContextStore | Per-session context extracted from the codebase (file tree, symbols, etc.) |
useSessionHijackStore | Detects when another process has taken over a session |
useSessionKeepAliveStore | Heartbeat pings to prevent idle sessions from being cleaned up |
Provider / account state
Section titled “Provider / account state”| Store | Description |
|---|---|
useProvidersStore | Detected provider CLIs, connected accounts, active provider selection |
useProviderProfilesStore | User profile and subscription info fetched from provider APIs |
useConnectedProvidersStore | Which provider accounts are currently connected and authenticated |
useAuthStore | Authentication state (Magia cloud account, tokens) |
UI / peripheral state
Section titled “UI / peripheral state”| Store | Description |
|---|---|
useSettingsStore | All user preferences (sliced by domain: UI, editor, terminal, voice, telemetry, permissions, notifications, observations, provider settings) |
useOnboardingStore | Onboarding completion flag and step tracking |
useEditorStore | Editor layout: open files, split tree, active pane (sliced: file-slice, layout-slice, split-tree) |
useSessionUIStore | Per-session UI state: scroll position, input draft, panel layout |
useInputDraftStore | Persisted input drafts per session |
useTerminalPanelStore | Terminal panel open/closed state and active terminal |
useTrayPanelStore | Tray panel window state |
Feature / system stores
Section titled “Feature / system stores”| Store | Description |
|---|---|
usePluginsStore | Installed MCP plugins and their enabled/disabled state |
useKeybindingsStore + useShortcutStore | Custom keybinding definitions and the runtime interceptor |
useThemesStore | Custom theme definitions loaded from ~/.magia/themes/ |
useOtelConfigStore | OpenTelemetry collector configuration (port, socket path) |
useMetricsStore | Per-session token usage and cost metrics from OTel |
useProjectStatsStore | Aggregated per-project statistics |
useDashboardDataStore | Cached data for the dashboard view (recent activity, stats) |
useClaudeStatusStore | Claude.ai operational status feed |
useClaudeUsageStore | Claude API usage counters |
useFeedbackStore | In-app feedback dialog state |
useUpdaterStore | Auto-updater state and pending update info |
useLicenseStore | License / entitlement state for cloud features |
useIdeIntegrationStore | IDE integration (Claude Code VS Code extension) connection state |
useHookConfigStore | User-defined hook configurations |
Store patterns
Section titled “Store patterns”Sliced stores. Stores with many fields are split into sub-slices under a dedicated subdirectory (e.g. src/store/magia/, src/store/settings/, src/store/editor/). Each slice exports a partial state and actions, combined into a single create() call in the slice’s index.ts.
Persist middleware. Long-lived client state (settings, onboarding, discovery cache, keybindings, session UI, input drafts) uses Zustand’s persist middleware backed by localStorage. Persisted stores carry a version number and a migrate function to handle schema changes.
No derived state in selectors. Selectors must return stable references. Never return [] or {} literals from a selector — doing so creates a new reference on every render and causes infinite re-render loops. Compute derived values outside the selector or use a separate useMemo.
Component organisation
Section titled “Component organisation”src/components/├── app/ AppShell, SplashScreen, WhatsNew, ErrorBoundary├── auth/ AuthProvider, login/callback screens├── chat/ Chat input, message list, message renderers├── dashboard/ Dashboard page and widgets├── editor/ CodeMirror editor wrapper and panels├── feedback/ Feedback dialog├── hub/ Hub/marketplace UI├── onboarding/ FirstLaunch flow steps├── session/ Session sidebar, session cards, session header├── sessions/ Sessions list page├── settings/ Settings pages (general, editor, terminal, providers, etc.)├── terminal/ xterm.js wrapper and terminal panel├── tray/ Tray panel lightweight UI├── ui/ shadcn/ui primitives (button, dialog, input, etc.)└── workspace/ Workspace management pagesPage-level components live directly under src/components/<area>/. Shared UI primitives are in src/components/ui/ and are generated/managed by the shadcn CLI.
Styling
Section titled “Styling”- Tailwind v4 — utility-first CSS with JIT.
- shadcn/ui — component primitives (radix-ui based). The theme uses a Stone base with Rose accent, dark mode, Noto Sans font, and a small border radius.
- CSS variables — design tokens (colors, radius, spacing) are defined as CSS custom properties in
src/styles/globals.cssso custom themes can override them.
Every flex child that should not overflow its container needs min-w-0. overflow-hidden alone does not prevent flex items from growing beyond their container.
Testing
Section titled “Testing”Unit and integration tests use Vitest. Test files live alongside their source files (e.g. app.test.ts next to app.ts). Store tests typically exercise the store in isolation without a running Tauri backend by mocking invoke.
Run tests:
pnpm test