Sybil Web
Preact + Vite frontend for the Sybil backend.
Setup
- Copy env values:
cp .env.example .env
- Install and run:
npm install
npm run dev
Default dev URL: http://localhost:5173
Environment variables
VITE_API_BASE_URL: backend API base URL. Defaults tohttp://localhost:8787.VITE_ADMIN_TOKEN: optional. Pre-fills a token in the login form.
Authentication
- On startup, the app checks
GET /v1/auth/session. - If backend runs with
ADMIN_TOKEN, sign in using that token. - If backend runs in open mode (
ADMIN_TOKENunset), you can continue without a token. - The entered token is stored in browser
localStorageand can be cleared via Logout. When browser storage is blocked, authentication still works for the current page; sign in again after reloading.
UI
- Left panel: mixed list of chat conversations and Exa searches.
- Sidebar search uses
GET /v1/workspace-items?q=…to search all saved titles, conversation messages, and web-search queries/answers. It waits 300 ms while typing and cancels superseded requests. Results include a single-line matching excerpt with matching words in bold and middle truncation (or “Title match” for title-only chat matches), with up to 100 newest matches. Clear the field or press Escape to return to recent items. Matching uses word prefixes, soplanalso findsplanning. - Right panel:
- Chat mode: transcript + provider/model controls.
- Saved assistant replies show their received date/time in your local timezone and the provider/model used for that response, with Copy and Fork actions aligned beneath the reply on mobile and desktop. The model label stays with the response when you change models; older replies without a reliable record show “Model not recorded.” Copy preserves the message's Markdown; Fork starts a conversation through that reply.
- Search mode: top AI answer block + Google-style Exa results view.
- Composer adapts to the active item:
- Chat sends
POST /v1/chat-completions/stream(SSE). - Search sends
POST /v1/searches/:searchId/run/stream(SSE).
- Chat sends
- Keyboard shortcuts:
Cmd/Ctrl+J: start a new chat.Shift+Cmd/Ctrl+J: start a new search.Cmd/Ctrl+Up/Down: move through the sidebar list.
Client API contract docs:
../docs/api/rest.md../docs/api/streaming-chat.md
Routes
/: full Sybil app (sidebar + chat/search workspace)/lite: lightweight chat, web search, and Agent workspace for e-readers and older smartphones/search?q=bitcoin+price: standalone search page that renders only the search query box and results panel
Lightweight interface
Open /lite directly, or choose Lightweight view in the full workspace sidebar. Full interface switches back. Explicit switches (?ui=lite and ?ui=full) remember the choice in sybil:ui-mode; workspace IDs, query parameters, and hashes survive switching. A direct /lite visit always works even if Full was previously selected.
At /, an explicit or saved choice wins over automatic detection. Otherwise recognizable Kindle/Kobo/PocketBook user agents or navigator.connection.saveData === true select Lite. Silk/Kindle Fire alone do not identify an e-ink device. Screen size, CPU count, and reduced-motion preferences do not trigger a redirect. /search stays a standalone search unless explicitly switched; switching its q parameter into Lite prefills a search without starting a request.
Detection is intentionally limited: user agent identification is unreliable, and Save-Data is an explicit user preference available only in some browsers. Readers without recognizable browser data can bookmark /lite.
Lite follows the existing workspace layout: sidebar, compact conversation header, scrolling transcript, and bottom composer. It uses the same Inter font stack and SYBIL wordmark, the original chat bubble shapes, and the shared assistant metadata/Copy footer. Solid, high-contrast light surfaces replace gradients and translucent backgrounds; there are no character images, animations, or added welcome/tagline copy. Mobile uses an instant sidebar toggle, and pinch zoom remains available.
The route does not load the full workspace stylesheet or animated Agent presentation. Typography, bubble geometry, and assistant reply actions are shared with the full interface. Markdown tables and code scroll within the transcript; history and ordinary transcripts render in batches of 30. Images are not rendered automatically. Regular chats and searches consume the existing SSE APIs but publish completed responses, avoiding repeated token-by-token screen redraws. Refresh restores saved content and reconnects to selected active runs. Chat retries retain the same submission ID.
The Agent view loads only when opened and reuses the shared room hook, durable outbox, older history, and read tracking. It receives completed message updates, supports failed-delivery retries, and downloads attachments only when requested. Lite does not start notification presence polling or register a service worker. Existing browser workers/subscriptions are unaffected by switching views; sign-out keeps the shared revocation behavior.
The focused controls support reading and sending text, model selection (including custom model names), saved-history search, web searches, and Agent conversations. Uploads, chat settings, fork management, Quick Questions, and notification settings remain accessible through Full interface. Existing chat settings and attachments remain part of the shared conversation context.
Code boundaries
main.tsx,root-router.tsx, andlib/ui-mode.tschoose and lazily load one presentation before its UI assets download. Mode links perform document navigation to keep global styles isolated.pages/lite-route-page.tsxandpages/lite.cssown Lite's markup and appearance. Full UI components do not need Lite conditionals.typography.css,components/chat/message-bubble.css, andAssistantMessageFooterkeep fonts, bubble geometry, and reply actions consistent across the two presentations. Lite supplies flat color values through CSS variables.hooks/use-lite-workspace.tsadapts workspace state to completion-only rendering. Both presentations uselib/api.ts,lib/workspace-domain.ts(model preferences, history payloads, attachment context, URLs, and retry rules),useSessionAuth,useWorkspaceSearch, and sanitizedMarkdownContent.components/lite/lite-agent.tsxsupplies a simple presentation overuseAgentRoom; transport, delivery identities, attachments, and read-state semantics stay shared.
Production syntax targets Chrome 87, Firefox 78, and Safari 14. Browsers still need JavaScript modules, dynamic imports, Fetch/ReadableStream, AbortController, and BigInt; Agent additionally uses WebSocket and browser storage. A small feature-detected shim supplies Array.at, used by the Markdown dependency, and secure UUID creation falls back to crypto.getRandomValues. This is not an ES5 frontend or a guarantee for every Kindle firmware. Browsers without module support show an explanatory fallback. Physical Kindle testing remains necessary.
Verification
Run npm test and npm run build, then npm run preview -- --host 127.0.0.1 --port 5197. Browser fixtures mock all API calls and require Playwright Chromium (set PLAYWRIGHT_MODULE / CHROME_EXECUTABLE if installed elsewhere):
node tests/ui-mode.browser.mjs
node tests/lite-workspace.browser.mjs
node tests/lite-workspace-recovery.browser.mjs
node tests/lite-agent.browser.mjs
Set LITE_ARTIFACT_DIR=/tmp/sybil-lite-qa on the workspace fixture to save desktop and 320/390/600 px screenshots. Checks cover routing overrides, blocked storage, asset isolation, shared typography and bubble shapes, absence of added flavor text, composer placement, Markdown without native Array.at, chat submission/retry, search, saved-history navigation, active-run recovery, Agent delivery, and narrow-screen overflow. Chromium emulation does not reproduce e-ink refresh behavior or older engine implementations.
Mobile web
Installed Sybil uses standalone display, retaining the system status bar where the browser supports it. The viewport keeps pinch zoom disabled. Compact regular-chat replies use the transcript width and the active provider/model appears beneath the title; Agent keeps its existing layout.
Horizontal navigation swipes start on workspace chrome or empty space. Text, tables, quotations, code, controls, existing selections and nested horizontal scrollers keep native gestures, including at scroll edges. Long-press/right-click on transcript text and links uses the native browser menu; the reply footer provides Copy and Fork.
Agent notification settings include separate browser/server failure guidance and an expandable Android / GrapheneOS setup guide. See mobile QA results and limitations, including how to run the browser fixtures.