Skip to main content

Frontend

The web UI is a Vite SPA built with React, React Router v7 for client-side routing, Tailwind CSS v4 for styling, and a lightweight useSession() hook backed by /api/auth/session. There is no Next.js, no App Router, and no NextAuth in the frontend.

Pages

RouteComponentDescription
/pages/Home.tsxHome / dashboard
/chatpages/Chat.tsxAI chat interface
/calendarpages/Calendar.tsxCalendar view
/todospages/Todos.tsxTask list
/filespages/Files.tsxFile browser
/mediapages/Media.tsxMedia library browser
/cameraspages/Cameras.tsxCamera list with status
/cameras/:idpages/CameraDetail.tsxCamera detail, events, test capture
/paired-devicespages/PairedDevices.tsxPhone / tablet / TV pairing
/devicespages/Devices.tsxIoT device management
/devices/:idpages/DeviceDetail.tsxIoT device detail
/eventspages/Events.tsxIoT event log
/automationpages/Automation.tsxJobs, webhooks, actions, skills
/memorypages/Memory.tsxMemory entries and raw file editor
/pluginspages/Plugins.tsxPlugin management
/settingspages/Settings.tsxApp settings
/settings/:slugpages/SettingsBySlug.tsxPer-plugin settings page
/setuppages/Setup.tsxFirst-run wizard
/auditpages/Audit.tsxAudit log viewer

Routing

Routes are defined in apps/web/client/routes.tsx using React Router v7's lazy loading pattern:

export const routes = [
{ path: "/", lazy: () => import("./pages/Home").then((m) => ({ Component: m.default })) },
{ path: "/chat", lazy: () => import("./pages/Chat").then((m) => ({ Component: m.default })) },
// ...
];

The router is bootstrapped in apps/web/client/main.tsx with <BrowserRouter>. Unknown paths are caught by the SPA's index.html fallback on the server.

Authentication

The useSession() hook in apps/web/client/lib/use-session.ts fetches /api/auth/session once on mount to retrieve the current browser session. The backend issues an HTTP-only cookie after Google OAuth completes.

import { useSession } from "@/lib/use-session";

function MyComponent() {
const { user, status, isAdmin } = useSession();
// user: { email, name, picture } | null
// status: "loading" | "authenticated" | "unauthenticated"
// isAdmin: true for any signed-in browser session
}

There is no SessionProvider wrapper — each component that needs session state calls the hook directly.

State Management

The frontend uses local React state — no global state library. Each page fetches its own data and manages its own state:

useEffect → fetch /api/... → useState → render

Common Patterns

  • useState for UI state (modals, forms, loading flags)
  • useCallback for memoized fetch functions
  • useEffect for initial data loading and side effects
  • useRef for scroll positioning, AbortControllers, timers

Key Page Details

Chat (/chat)

  • Full-screen chat interface with streaming responses
  • Conversation list sidebar
  • Messages rendered with react-markdown and highlight.js for code blocks
  • Supports conversation switching via URL query params
  • AbortController for canceling in-flight requests

Memory (/memory)

  • Two tabs: Entries (database records) and Raw (SOUL.md / MEMORY.md editors)
  • Entry list with category grouping
  • Create/edit/delete memory entries via modals
  • Direct text editor for personality (SOUL.md) file

Cameras (/cameras)

  • List view with connection status indicators
  • Detail page (/cameras/:id) includes configuration editor, event history with alert level badges, and "Test Capture" button

Media (/media)

  • Grid/list view with thumbnail previews
  • Filter by type (video, audio, image, document)
  • Pagination controls
  • Click-to-preview modal for images and video

Automation (/automation)

  • Four tabs: Jobs, Webhooks, Actions, Skills
  • Jobs: Create/edit cron jobs, view run history, manual execute
  • Webhooks: Create/manage webhooks, copy webhook URL + secret
  • Actions: Browse registered actions, test execute
  • Skills: View loaded skills and their manifests

Paired Devices (/paired-devices)

  • List of paired devices (phones, tablets, TVs) with live connection status
  • QR-code pairing flow — generates a one-time code; device scans and receives tokens
  • Device type badges and scope display

Styling

  • Tailwind CSS v4 with utility classes
  • @tailwindcss/typography plugin for prose styling (markdown rendering)
  • Dark mode via prefers-color-scheme
  • Design tokens from @genie/tokens

Key Files

FilePurpose
apps/web/client/main.tsxSPA entry point — mounts React, wraps <BrowserRouter>
apps/web/client/App.tsxRoot component — renders routes
apps/web/client/routes.tsxRoute definitions (React Router v7, lazy pages)
apps/web/client/lib/use-session.tsAuth hook (/api/auth/session)
apps/web/client/pages/Page components
apps/web/client/components/Shared components
apps/web/client/styles.cssTailwind imports, global styles