spb/zyquo-cloud-web Public MIT
Zyquo Cloud Web — every cloud model, one beautiful chat, entirely in your browser.
TypeScript 81.9%
CSS 8.9%
JavaScript 7.5%
Shell 1.1%
HTML 0.6%
1<!--2 PLAN.md3 Zyquo Cloud Web45 Author: Simon-Pierre Boucher6 Mail: contact@spboucher.ai7-->89# Zyquo Cloud Web — Build Plan & Phase Log1011Strict phase order 0 → 8. Each phase gets a checklist here, checked off as work12lands, plus a 3–5 line summary at its checkpoint.1314---1516## Phase 0 — Research + Zyquo Cloud study1718- [x] Locate native Zyquo Cloud repo (`~/Desktop/zyquo-cloud`)19- [x] Study provider layer (ProviderID, ProviderRegistry, OpenAICompatibleClient, AnthropicClient, StreamingService/SSEParser, ProviderProtocol)20- [x] Study AIModel / capabilities / pricing / ParameterSupport model21- [x] Study ZyquoTheme design tokens22- [x] Extract complete model catalog (170 models, 12 providers — port from ModelCatalogData.swift, NOT the drifted catalog-summary.md)23- [x] Study native UX (views, model picker, personas, prompt library, brand glyph) → `docs/NATIVE-UX.md`24- [x] Empirical CORS probes, all 12 providers (preflight + real POST with Origin)25- [x] Anthropic browser header confirmed (`anthropic-dangerous-direct-browser-access: true`)26- [x] Web research: documented CORS stance per provider (cited in CORS-MATRIX)27- [x] Write `docs/CORS-MATRIX.md`28- [x] Write `docs/PROVIDER-REUSE.md`2930**Phase 0 summary.** Native repo studied end-to-end: only two wire formats31(OpenAI chat/completions ×11 incl. Gemini-compat; Anthropic Messages ×1), one32SSE parser, 170-model catalog, ZyquoTheme tokens extracted. Empirical CORS33probes with real keys: **all 12 providers callable directly from the browser**34(Anthropic needs `anthropic-dangerous-direct-browser-access: true`); documented35stances researched and cited. Proxy/Zyquo Router override still ships since36only Anthropic documents CORS officially. Gate satisfied: PROVIDER-REUSE.md +37CORS-MATRIX.md complete.3839---4041## Phase 1 — Project setup (React + TS + Vite)4243- [x] Vite + React 19 + TypeScript strict (project references, `noUncheckedIndexedAccess`)44- [x] Styling: CSS variables implementing ZyquoTheme (`src/design/tokens.css` + `global.css`) — no Tailwind (dependency minimalism = smaller XSS surface)45- [x] State: Zustand installed46- [x] ESLint (flat config) + Prettier; scripts `dev/build/preview/lint/typecheck/check`47- [x] PWA scaffold (vite-plugin-pwa): app-shell precache only, NO API/key caching, manifest (light theme-color)48- [x] Strict CSP meta in `index.html`: `connect-src` = exact 12 provider origins + localhost (Zyquo Router); no inline/third-party scripts49- [x] Phase checkpoint: `npm run check` green (typecheck, lint, build)5051**Phase 1 summary.** Hand-scaffolded Vite/React/TS-strict app with mandatory52headers everywhere, ZyquoTheme tokens as CSS variables (light default + dark +535 accents), a locked-down CSP whose connect-src is exactly the 12 provider54origins plus localhost for Zyquo Router, and an app-shell-only PWA. Build,55lint, and typecheck all green on first checkpoint.5657## Phase 2 — Architecture + browser provider layer5859- [x] `src/types/` — Provider, WireFormat, AIModel, capabilities/pricing/ParameterSupport, TokenUsage, Citation, Attachment, Message (+variants), Conversation, Settings60- [x] `providers/types.ts` — ProviderClient interface, ChatRequest/ChatEvent, ProviderError (typed, human messages, CORS-shaped network hint)61- [x] `providers/registry.ts` — 12-provider metadata (base URLs, auth, wire format, stream_options wants, /models support) + clientFor()62- [x] `providers/sse.ts` — SSEParser state machine + fetch/ReadableStream event stream + retrying requestJSON (429/5xx backoff, Retry-After) + AbortController cancellation63- [x] `providers/openaiCompatible.ts` — all native quirks ported (param gating, Mistral effort mapping + array-content reasoning, Qwen enable_thinking, Together choices[].text, Perplexity citations, Gemini models/ prefix, requiresStreaming aggregation, vision parts, text-file fencing)64- [x] `providers/anthropic.ts` — Messages API (mandatory max_tokens, block content, named SSE events, thinking toggle, browser CORS header)65- [x] `providers/catalog.ts` — ALL 170 models transcribed 1:1 (counts verified: 27/11/5/10/14/32/2/12/4/16/34/3) + rank/cheapest/default/find helpers66- [x] `storage/keys.ts` — namespaced localStorage key map (gate prerequisite)67- [x] PHASE GATE: real token-by-token streaming in a real Chromium browser via `#stream-test` — 26 incremental snapshots, usage `15→256 tok`, `finished (length)`, Stop → `cancelled: Generation stopped.` (scripts/gate-phase2.mjs)6869**Phase 2 summary.** The whole native provider layer now runs on browser70fetch + ReadableStream: one OpenAI-compatible client (11 providers + custom),71one Anthropic Messages client, one SSE parser for both stream shapes, and the72complete 170-model catalog with per-model parameter gating. Gate passed against73live OpenAI from headless Chromium: token-by-token rendering, usage events, and74AbortController cancellation all verified. Playwright added (dev-only) as the75browser harness that Phase 7 will reuse for the full matrix.7677## Phase 3 — Local persistence7879- [x] 3.A `storage/keys.ts` — namespaced localStorage map, get/set/remove, masked display, in-memory overlay for vault mode; `providers/testKey.ts` Test action (/models or min completion, ms latency)80- [x] 3.A `storage/vault.ts` — opt-in passphrase lock: PBKDF2 (310k, SHA-256) → AES-GCM; enable/unlock/lock/disable; honest framing (obfuscation-at-rest, not a secure vault)81- [x] 3.B `storage/db.ts` (dependency-free IndexedDB promise wrapper) + `storage/conversations.ts` — CRUD, updatedAt sorting, schema versioning + migrate(), title+message search82- [x] 3.C `storage/settings.ts` — theme (light default), default model, per-provider proxy URLs, UI prefs83- [x] 3.C `storage/backup.ts` — export (keys opt-in with warning) / import / clear-everything84- [x] Dev harness (`features/devHarness.ts`, DEV-only) exposing storage for browser gate scripts85- [x] PHASE GATE: 13/13 checks in real Chromium (scripts/gate-phase3.mjs) — reload survival, **browser-restart survival** (persistent context), export→clear→import exact restore, vault encrypt-at-rest / wrong-pass rejection / unlock / disable8687**Phase 3 summary.** All state is now local and durable: keys in88`zyquo.cloud.web.keys`, conversations in IndexedDB with schema versioning,89settings in `zyquo.cloud.web.settings`. Export/import is the backup story;90clear-data wipes everything. The optional passphrase lock encrypts the key map91with WebCrypto AES-GCM and is honestly framed. Gate ran in a real browser with92a persistent profile: reload AND restart survival, exact export round-trip, and93vault behavior all green.9495## Phase 4 — Design system & UI9697- [x] 4.1 Light flagship theme via tokens (Phase 1 CSS vars) — light default, dark opt-in, 5 accents, reduced-motion respected98- [x] 4.2 Shell: sidebar (wordmark, search ⌘F, New Chat ⌘N, Pinned/Today/Yesterday/7 Days/Older groups, hover pin/delete, usage footer) + chat area (header 52px with editable title, centered model chip, compare/export/info actions)99- [x] 4.2.1 Model Menu: ⌘K command-palette style — fuzzy search, capability filter chips (Vision/Tools/Reasoning/JSON/Long context/Cheapest), Aliases + Favorites + Recents groups, keyed-providers-first sections, capability/context/price badges, star favorites, "Add key" affordance, keyboard nav (↑↓/↵/⌘↵ default/esc), footer hints100- [x] Transcript: centered 760px column, user right accent-subtle / assistant left surface+avatar, GFM markdown + tables + zebra rows + syntax-highlighted code (language label, copy, wrap), collapsible reasoning, citation chips, streaming caret, scroll-lock + jump-to-latest pill, hover metadata (time, tokens, cost) + actions (copy/edit-resend/regenerate/quote/branch/read-aloud/delete), variant switcher101- [x] Input bar: floating card, auto-grow, image+text-file attach (click/drag/paste), thumbnails, params popover (support-gated controls with enable checkboxes), circular send ⌘↩ / Stop, context-usage bar + token/cost HUD102- [x] Settings modal: Providers & Keys (masked, Test + latency + status dot, per-provider proxy URL, honesty notice), Models (catalog browser, favorites, set-default), Appearance (theme/accent/font-size/density + live preview), Data (export incl.-keys warning / import / confirm-twice clear), Advanced (default system prompt, passphrase lock, streaming toggle)103- [x] First-run honest welcome + root empty state (glyph, greeting, model chip, 4 suggestion cards — self-contained ones send immediately, input-expecting ones seed the draft)104- [x] Toasts with undo (conversation delete)105- [x] 4.3 Responsive: sidebar drawer <768px with scrim, full-width transcript, code blocks scroll internally, no horizontal overflow; motion tokens (80ms hover, 150ms message-in, blinking caret)106- [x] 4.4 Quality gate: screenshotted desktop+phone, light+dark, model menu, settings — reviewed and fixed (filter-chip clipping, cost precision, draft seeding)107108**Phase 4 summary.** The full chat UI is live and matches the family design:109tokens-only styling, light flagship default, dark derived, responsive down to110phone width with a drawer sidebar. The Model Menu is the headline surface —111fuzzy-searchable, filterable, priced, favorited, keyboard-driven. Verified112visually in headless Chromium at 1240px and 390px in both themes; found and113fixed three issues in the review pass. `npm run check` green.114115## Phase 5 — Brand assets116117- [x] App icon reused from native (`assets/icon/zyquo-cloud.svg` — same squircle/gradient/Z-caret) → rasterized `icon-192/512.png`, `icon-512-maskable.png` (80% safe zone on brand gradient), `apple-touch-icon.png`118- [x] `favicon.svg` (native small mark); manifest icons wired in vite.config PWA manifest; `theme-color` light `#FAFBFD`119- [x] Wordmark: crisp inline `ZyquoGlyph` SVG component (currentColor, light/dark via tokens)120- [x] OG/social meta + generated 1200×630 `og-image.png` (light canvas, icon + wordmark + tagline + domain)121122**Phase 5 summary.** The web app now carries the exact native Zyquo Cloud123icon (Big Sur squircle, sky gradient, white Z with block caret) as its PWA124identity, matching the Dock/home-screen look of the macOS app. Favicon,125apple-touch, maskable, OG image and social meta are all in place.126127## Phase 6 — Features128129- [x] Personas: all 8 native personas ported verbatim (`features/personasData.ts`); apply via palette or slash menu (system prompt + personaID per conversation)130- [x] Prompt Library: all 56 native templates ported verbatim (8 categories × 7, `features/promptLibraryData.ts`), `{{input}}` substitution131- [x] Slash commands: `/` in the input → menu with quick tools (/summarize /translate /rewrite /explain), full template library, personas; keyboard-driven132- [x] Command palette (⌘K): actions (new chat, compare, switch model, settings, focus mode, summarize, duplicate), jump-to-conversation, models, personas, templates133- [x] Multi-model compare: 2–4 columns, per-column model chip/stream/stop/copy/regenerate, broadcast prompt, **promote answer into thread**134- [x] Branching/variants (Phase 4 store): regenerate keeps prior answer as a variant with ‹ › switcher; branch-from-message forks a conversation; edit & fork135- [x] Rich rendering: GFM + tables + task lists (Phase 4) + **KaTeX math** + **Mermaid diagrams** (lazy-loaded ~1MB chunk, strict security level)136- [x] Read-aloud (Web Speech TTS) + dictation (SpeechRecognition, mic button)137- [x] Continue generation (last assistant message action), Stop, live elapsed + tok/s138- [x] Token/cost HUD + context bar (Phase 4) + **auto-trim oldest turns** at 90% of the window (system prompt kept, toast informs)139- [x] Conversation tools: summarize (cheapest model), duplicate, export MD/JSON, undo delete, pin, rename, search140- [x] QoL: retry w/ backoff (Phase 2), human error toasts naming the provider + CORS hint, autosave everywhere, PWA shell precache trimmed 4.7MB → 1.15MB (mermaid/katex runtime-cached)141- [x] Coherence sweep #2: headers ✓, storage/network layering ✓, accent hexes moved to design/accents.ts142143**Phase 6 summary.** Full feature parity plus the web-specific extras: the144native personas and 56-template library are ported verbatim and surfaced145through both the ⌘K palette and a `/` slash menu; compare mode broadcasts to146up to four independently streaming columns with promote-to-thread; math and147diagrams render (lazily); reading aloud, dictation, auto-trim, and conversation148tools round out the spec. Sweep clean, `npm run check` green.149150## Phase 7 — Verification151152- [x] Provider matrix with real keys from a real browser: **169/169 tested models PASS streaming** (sonar-deep-research skipped by design); 12/12 non-streaming; 12/12 Stop/abort; vision on all 9 vision-capable providers; Perplexity citations (19); reasoning deltas on all thinking models → `docs/VERIFICATION.md`153- [x] Persistence: Phase 3 gate 13/13 (reload + browser restart + export/import + vault)154- [x] Security/privacy: network audit = 12 provider origins + localhost only; CSP blocks foreign origins; first-run notice; no dev harness in prod build; keys never committed155- [x] Cross-browser/responsive: 15/15 on Chromium + WebKit + Firefox, desktop + phone, light default156157**Phase 7 summary.** The whole catalog was exercised live from a browser158context using the user's keys: every one of the 169 tested models streamed159token-by-token through the ported provider layer, with non-streaming,160cancellation, vision, reasoning, and citations verified per provider, and the161network audit confirming not a single byte leaves the page except toward the162chosen providers. Three first-pass failures were all test-harness artifacts163(tiny token budgets on reasoning models, an abort timer slower than Cerebras)164and retested green.165166## Phase 8 — Build & deployment167168- [x] Production build: `npm run check` green (0 type/lint errors), code-split (mermaid/katex lazy), hashed assets, PWA precache trimmed to app shell (1.15 MB)169- [x] Zero-dependency production server (`scripts/server.mjs`): CSP (exact 12 provider origins + localhost), HSTS preload, nosniff, Referrer-Policy no-referrer, Permissions-Policy, immutable cache for hashed assets / no-cache HTML, SPA fallback, `/healthz`170- [x] Deployed on cluster node **M3U96a** (per user instruction; M2U64 stays prod-reserved): rsync → `~/zyquo-cloud-web`, PM2 `zyquo-cloud-web` (node server.mjs 8080 dist) + `zyquo-cloud-ngrok` (`ngrok http --url=www.zyquo.cloud 8080`), `pm2 save`171- [x] Registered in `~/Desktop/cluster-skill/cluster-deployments.json`172- [x] Post-deploy verification: **https://www.zyquo.cloud** serves HTTP/2 200 with all security headers; manifest + service worker registered (PWA installable); **light theme default**; first-run notice; **real streaming completion from the deployed origin** (CSP allows providers) — `finished`, token-by-token173- [x] Self-hosting documented in README (static bundle + server.mjs or any static host; Zyquo Router pairing)174- [x] Final sweeps: every code file carries the Author/Mail header; storage/network layering intact175- [ ] ⚠ Apex `zyquo.cloud` → `www` redirect: the apex currently has **no DNS record** — needs a registrar-level ALIAS/forward to `www.zyquo.cloud` (requires registrar access; the only item outside this environment's reach)176177**Phase 8 summary.** Zyquo Cloud Web is live at **https://www.zyquo.cloud**,178served as static assets from cluster node M3U96a through the user's ngrok179domain, under PM2 with the process list saved. Security headers verified on180the wire, the PWA installs, light loads by default, and a live provider stream181was rendered from the deployed origin. The single open item is the apex DNS182record (registrar access required).183