Zyquo Cloud # โšก Zyquo Cloud Web ### *Every cloud model. One beautiful chat. Entirely in your browser.* **The browser edition of Zyquo Cloud โ€” 12 AI providers, 170 models, zero backend. Bring your own keys; nothing ever leaves your device.**
[![Version](https://img.shields.io/badge/version-1.0.0-4E6AF0?style=for-the-badge)](https://github.com/spboucher-ai/zyquo-cloud-web/releases) [![License: MIT](https://img.shields.io/badge/license-MIT-2FA36B?style=for-the-badge)](LICENSE) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-D9822B?style=for-the-badge)](#-contributing) [![Maintained](https://img.shields.io/badge/maintained-yes-C94F7C?style=for-the-badge)](https://github.com/spboucher-ai/zyquo-cloud-web/commits/main) [![React 19](https://img.shields.io/badge/React-19-61DAFB?style=for-the-badge&logo=react&logoColor=black)](https://react.dev) [![TypeScript strict](https://img.shields.io/badge/TypeScript-strict-3178C6?style=for-the-badge&logo=typescript&logoColor=white)](https://www.typescriptlang.org/) [![Vite 7](https://img.shields.io/badge/Vite-7-646CFF?style=for-the-badge&logo=vite&logoColor=white)](https://vitejs.dev) [![Zustand](https://img.shields.io/badge/Zustand-state-593D88?style=for-the-badge&logo=react&logoColor=white)](https://zustand-demo.pmnd.rs/) [![PWA](https://img.shields.io/badge/PWA-installable-5A0FC8?style=for-the-badge&logo=pwa&logoColor=white)](#-features) [![Providers](https://img.shields.io/badge/AI_providers-12-FF6B6B?style=for-the-badge)](#-the-provider-fleet) [![Models](https://img.shields.io/badge/models-170-FFD93D?style=for-the-badge)](#-the-provider-fleet) [![Verified live](https://img.shields.io/badge/verified_live-169%2F169-6BCB77?style=for-the-badge)](docs/VERIFICATION.md) [![Backend](https://img.shields.io/badge/backend-none_๐ŸŽ‰-4D96FF?style=for-the-badge)](#-privacy--security-model) [![Core bundle](https://img.shields.io/badge/core_bundle-288_kB_gzip-9B5DE5?style=for-the-badge)](#-tech-stack) [![Personas](https://img.shields.io/badge/personas-8-F15BB5?style=for-the-badge)](#-features) [![Templates](https://img.shields.io/badge/prompt_templates-56-00BBF9?style=for-the-badge)](#-features) [![CI](https://img.shields.io/github/actions/workflow/status/spboucher-ai/zyquo-cloud-web/ci.yml?branch=main&style=for-the-badge&label=CI&color=2FA36B)](https://github.com/spboucher-ai/zyquo-cloud-web/actions/workflows/ci.yml) [![Pages](https://img.shields.io/github/actions/workflow/status/spboucher-ai/zyquo-cloud-web/deploy-pages.yml?branch=main&style=for-the-badge&label=Pages%20deploy&color=6D84F5)](https://github.com/spboucher-ai/zyquo-cloud-web/actions/workflows/deploy-pages.yml) [![Live](https://img.shields.io/badge/๐ŸŒ_live-www.zyquo.cloud-4E6AF0?style=for-the-badge)](https://www.zyquo.cloud) [![Last commit](https://img.shields.io/github/last-commit/spboucher-ai/zyquo-cloud-web?style=for-the-badge&color=E59A4D)](https://github.com/spboucher-ai/zyquo-cloud-web/commits/main) [![Repo size](https://img.shields.io/github/repo-size/spboucher-ai/zyquo-cloud-web?style=for-the-badge&color=43BD83)](https://github.com/spboucher-ai/zyquo-cloud-web)
**[๐Ÿš€ Open the app โ†’](https://www.zyquo.cloud)**
--- ## ๐Ÿ“š Table of Contents - [โœจ Features](#-features) - [๐Ÿ›ฐ The Provider Fleet](#-the-provider-fleet) - [๐ŸŒ Live Demo](#-live-demo) - [๐Ÿ“ธ Screenshots](#-screenshots) - [๐Ÿ”’ Privacy & Security Model](#-privacy--security-model) - [๐Ÿงฑ Tech Stack](#-tech-stack) - [๐Ÿš€ Getting Started](#-getting-started) - [๐Ÿ“œ Available Scripts](#-available-scripts) - [๐Ÿ—‚ Project Structure](#-project-structure) - [โ˜๏ธ Deployment](#๏ธ-deployment) - [โœ… Verification](#-verification) - [๐Ÿ—บ Roadmap](#-roadmap) - [๐Ÿค Contributing](#-contributing) - [๐Ÿ“„ License](#-license) - [๐Ÿ‘ค Author](#-author) --- ## โœจ Features Everything below is implemented and verified in this codebase โ€” no vaporware. ### ๐Ÿ’ฌ Chat engine - **Streaming & non-streaming chat** against 12 providers through two faithfully ported wire clients: one OpenAI `/chat/completions` client (11 providers + custom endpoints, every quirk handled) and one native **Anthropic Messages API** client โ€” over `fetch` + `ReadableStream` with a hand-rolled incremental **SSE parser** (`event:`/`data:` blocks, keep-alive comments, `[DONE]`, trailing-event flush). - **Token-by-token rendering** with a blinking caret, live **elapsed time + tok/s**, **Stop** (`AbortController` teardown) and **Continue**. - **Reasoning streams** normalized from every dialect: `reasoning_content`, `reasoning`, Mistral's array-of-thinking-chunks, DashScope `enable_thinking`, Anthropic `thinking` blocks โ€” shown in a collapsible *Thought process* panel. - **Perplexity citations** (`citations` + `search_results` merged) rendered as numbered, clickable chips. - **Auto-retry with backoff** on 429/5xx (honors `Retry-After`), typed human-readable errors that name the provider, and a CORS-shaped network error that explains the proxy/Zyquo Router fallback. - **Auto-titling** after the first exchange using the provider's cheapest non-reasoning model (native Zyquo Cloud behavior, ported verbatim). ### ๐ŸŽ› The Model Menu - Command-palette-style picker: **fuzzy search** across name / model ID / provider, **filter chips** (Vision ยท Tools ยท Reasoning ยท JSON ยท Long context ยท Cheapest), **Favorites โญ / Recents / Aliases** groups, keyed-providers first, full keyboard navigation (`โ†‘โ†“`, `โ†ต` use here, `โŒ˜โ†ต` set default). - Every row shows capability badges (๐Ÿ‘ ๐Ÿง  ๐Ÿ› ), **context window**, and **$/1M-token pricing** straight from the catalog; un-keyed providers get an inline **"Add key"** affordance. ### ๐Ÿงช Power features - **Multi-model compare** โ€” broadcast one prompt to **2โ€“4 columns**, each streaming independently with its own stop/copy/regenerate, then **promote the best answer into your thread**. - **Variants & branching** โ€” regenerate keeps the previous answer as a swipeable variant (`โ€น 2/3 โ€บ`); branch a whole conversation from any message; **edit & resend** forks from an earlier user turn. - **โŒ˜K command palette** โ€” actions (new chat, compare, focus mode, summarize, duplicate), jump-to-conversation, model switch, personas, templates. - **`/` slash commands** โ€” quick tools (`/summarize`, `/translate`, `/rewrite`, `/explain`) + the full library with `{{input}}` substitution. - **8 personas** and **56 prompt templates** (8 categories ร— 7), ported verbatim from native Zyquo Cloud. - **Vision attachments** (drag/drop/paste images โ†’ base64 parts) and **text file attachments** (injected as fenced context blocks), gated by each model's real capabilities. - **Read-aloud** (Web Speech TTS) and **dictation** (SpeechRecognition). - **Token & cost HUD** โ€” per-message and per-conversation token counts and cost estimates from catalog pricing, plus a **context-window usage bar** with **auto-trim** of the oldest turns at 90% capacity (system prompt kept). - **Per-conversation parameters** โ€” temperature, top-p, max tokens, penalties, reasoning effort, extended thinking โ€” the panel only shows controls the active model actually supports; unchecked = provider default, omitted from the request entirely. ### ๐Ÿ“ Rendering - GitHub-flavored **Markdown** (tables with zebra rows, task lists, blockquotes with accent bar), **syntax-highlighted code blocks** with language label, copy and wrap toggles, **KaTeX math**, and **Mermaid diagrams** (the ~1 MB renderer lazy-loads only when a diagram appears). ### ๐Ÿ’พ Local-first persistence - Conversations in **IndexedDB** (schema-versioned, dependency-free wrapper), keys and settings in namespaced `localStorage` (`zyquo.cloud.web.*`) โ€” **surviving reloads and full browser restarts** (gate-tested). - **Export / import everything** as JSON (keys strictly opt-in with a warning), per-conversation Markdown/JSON export, confirm-twice clear-data, **undo** for deletes. - Optional **passphrase lock**: the key map encrypted at rest with **AES-GCM** (PBKDF2, 310k iterations) โ€” honestly framed as at-rest protection, not a running-session vault. ### ๐ŸŽจ Design - The **ZyquoTheme** design system, ported token-for-token from the native macOS app โ€” light flagship theme (`#FAFBFD` / sky-indigo `#4E6AF0`), derived dark theme, **5 accent choices**, adjustable chat font size and density, focus mode, `prefers-reduced-motion` respected. - **Installable PWA** (app-shell precache, 1.15 MB) and fully **responsive** โ€” the sidebar becomes a drawer on phones, code blocks scroll internally, 44 pt touch targets. --- ## ๐Ÿ›ฐ The Provider Fleet | Provider | Models | Wire format | Streaming | Vision | Reasoning | Citations | |---|:-:|---|:-:|:-:|:-:|:-:| | ๐ŸŸข **OpenAI** | 27 | OpenAI compat | โœ… | โœ… | โœ… | โ€” | | ๐ŸŸ  **Anthropic** | 11 | **Messages API** | โœ… | โœ… | โœ… | โ€” | | โšซ **xAI** | 5 | OpenAI compat | โœ… | โœ… | โœ… | โ€” | | ๐Ÿ”ต **Mistral** | 10 | OpenAI compat | โœ… | โœ… | โœ… | โ€” | | ๐Ÿ”ด **Google Gemini** | 14 | OpenAI compat | โœ… | โœ… | โœ… | โ€” | | ๐ŸŸฃ **Alibaba Qwen** | 32 | OpenAI compat | โœ… | โœ… | โœ… | โ€” | | ๐ŸŒ€ **DeepSeek** | 2 | OpenAI compat | โœ… | โ€” | โœ… | โ€” | | ๐ŸŒ™ **Kimi (Moonshot)** | 12 | OpenAI compat | โœ… | โœ… | โœ… | โ€” | | ๐Ÿ” **Perplexity** | 4 | OpenAI compat | โœ… | โ€” | โœ… | โœ… | | ๐Ÿงฉ **Together AI** | 16 | OpenAI compat | โœ… | โ€” | โœ… | โ€” | | ๐Ÿ“ฆ **DeepInfra** | 34 | OpenAI compat | โœ… | โœ… | โœ… | โ€” | | โšก **Cerebras** | 3 | OpenAI compat | โœ… | โœ… | โœ… | โ€” | **All 12 call the provider APIs directly from your browser** โ€” CORS feasibility was empirically probed per provider (preflight + authenticated POST + streaming), documented with official sources in [`docs/CORS-MATRIX.md`](docs/CORS-MATRIX.md). Anthropic requires its documented `anthropic-dangerous-direct-browser-access: true` header, which the client sends. A **per-provider base-URL override** (Settings โ†’ Providers & Keys) lets you point any provider at your own proxy or a locally running **Zyquo Router** if a policy ever changes. --- ## ๐ŸŒ Live Demo | | URL | Notes | |---|---|---| | ๐Ÿš€ **Production** | **https://www.zyquo.cloud** | Primary deployment โ€” HTTPS, security headers, installable PWA | | ๐Ÿชž **GitHub Pages mirror** | https://spboucher-ai.github.io/zyquo-cloud-web/ | Auto-deployed from `main` by CI | > Open it, add any provider API key in **Settings โ†’ Providers & Keys** (it > stays in *your* browser), pick a model, chat. That's the whole onboarding. --- ## ๐Ÿ“ธ Screenshots
| Light (flagship) | Dark | |:-:|:-:| | ![Chat โ€” light](assets/screenshots/chat-light.png) | ![Chat โ€” dark](assets/screenshots/chat-dark.png) | | The Model Menu | โŒ˜K Command Palette | |:-:|:-:| | ![Model menu](assets/screenshots/model-menu.png) | ![Command palette](assets/screenshots/command-palette.png) | | ๐Ÿ“ฑ Mobile | |:-:| | Mobile |
--- ## ๐Ÿ”’ Privacy & Security Model **This app has no backend, no accounts, and no telemetry โ€” by design.** - ๐Ÿ”‘ **Bring-your-own-key:** your API keys live in `localStorage` on your device and are sent **only** to the provider you call, directly. - ๐Ÿงพ **Honest first-run notice:** browser storage is *not* a secure vault โ€” anyone with your browser profile (or a script running on the page) could read it. The app says so instead of pretending otherwise. - ๐Ÿ›ก **Strict CSP:** `connect-src` is locked to exactly the 12 provider origins + `localhost` (for Zyquo Router). No inline scripts, no third-party scripts, `object-src 'none'`, `frame-ancestors 'none'`. - ๐Ÿ•ต๏ธ **Audited:** during the full 169-model verification run, the page contacted the 12 provider origins and localhost โ€” **nothing else**. - ๐Ÿ” **Optional passphrase lock:** AES-GCM (WebCrypto) encryption of the key map at rest, PBKDF2-derived key, wrong-passphrase rejection tested. - ๐Ÿ“ฆ **Dependency-light on purpose:** every dependency is key-theft surface โ€” no icon packs, no CSS framework, no IndexedDB wrapper lib, no auth SDKs. --- ## ๐Ÿงฑ Tech Stack | Layer | Choice | Why | |---|---|---| | โš›๏ธ UI | **React 19** + **TypeScript (strict)** | `noUncheckedIndexedAccess`, project references, zero `any` leaks | | โšก Build | **Vite 7** | code-split (Mermaid/KaTeX lazy), hashed assets, **288 kB gzip** core bundle | | ๐Ÿ—ƒ State | **Zustand 5** | one store: conversations, streaming, settings, UI | | ๐ŸŽจ Styling | **CSS custom properties** (ZyquoTheme tokens) | no Tailwind/framework โ€” tokens ported 1:1 from the native macOS app | | ๐Ÿ“ Markdown | react-markdown + remark-gfm + rehype-highlight + **KaTeX** + **Mermaid** (lazy) | GFM, math, diagrams, Zyquo-mapped syntax theme | | ๐ŸŒŠ Networking | hand-rolled **SSE parser** over `fetch`/`ReadableStream` | one parser for OpenAI-style and Anthropic named events | | ๐Ÿ’พ Storage | **IndexedDB** (own 80-line wrapper) + namespaced `localStorage` | schema versioning, no dependency | | ๐Ÿ“ฒ PWA | vite-plugin-pwa (Workbox) | app-shell precache only โ€” **never** API responses or keys | | ๐Ÿงช Testing | **Playwright** (Chromium/WebKit/Firefox) | live gate scripts: streaming, persistence, full provider matrix | --- ## ๐Ÿš€ Getting Started ### Prerequisites - **Node.js โ‰ฅ 20** and **npm** - At least one provider API key (OpenAI, Anthropic, xAI, Mistral, Gemini, DashScope, DeepSeek, Moonshot, Perplexity, Together, DeepInfra or Cerebras) ### Install & run ```bash git clone https://github.com/spboucher-ai/zyquo-cloud-web.git cd zyquo-cloud-web npm install npm run dev # โ†’ http://localhost:5173 ``` Open the app, go to **Settings โ†’ Providers & Keys**, paste a key, hit **Test**, pick a model โ€” done. ### Environment variables The app itself needs **none** โ€” keys are entered in the UI and stored in the browser. Build-time only: | Variable | Purpose | Default | |---|---|---| | `VITE_BASE` | Base path for subpath hosting (e.g. GitHub Pages sets `/zyquo-cloud-web/`) | `/` | > โš ๏ธ Never put API keys in `.env` files here โ€” there is no server to hide > them in. The `.gitignore` blocks `.env*` and the local test-key file anyway. --- ## ๐Ÿ“œ Available Scripts | Script | What it does | |---|---| | `npm run dev` | Vite dev server on `:5173` | | `npm run build` | Type-check (project refs) + production build + PWA assets | | `npm run preview` | Serve the production build locally | | `npm run typecheck` | `tsc -b --noEmit` | | `npm run lint` | ESLint (flat config) over `src/` | | `npm run check` | typecheck + lint + build โ€” the CI gate | | `node scripts/server.mjs 8080 dist` | Zero-dependency production server (CSP/HSTS headers, SPA fallback, `/healthz`) | | `node scripts/gate-phase2.mjs` | Live streaming gate in headless Chromium | | `node scripts/gate-phase3.mjs` | 13-check persistence gate (reload + browser restart + vault) | | `node scripts/verify-matrix.mjs` | Full provider/model verification matrix (needs real keys in `.keys.local.json`) | | `node scripts/verify-browsers.mjs` | Chromium/WebKit/Firefox responsive smoke | --- ## ๐Ÿ—‚ Project Structure ``` zyquo-cloud-web/ โ”œโ”€โ”€ index.html # CSP meta, PWA/social meta โ”œโ”€โ”€ vite.config.ts # Vite + PWA (app-shell precache only) โ”œโ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ App.tsx # Shell: sidebar + chat, global shortcuts โ”‚ โ”œโ”€โ”€ types/ # Provider, AIModel, Conversation, Message, Settings โ”‚ โ”œโ”€โ”€ providers/ # ๐Ÿ”Œ ALL networking lives here โ”‚ โ”‚ โ”œโ”€โ”€ registry.ts # 12-provider metadata + client resolution โ”‚ โ”‚ โ”œโ”€โ”€ openaiCompatible.ts# one client, 11 providers + custom, every quirk โ”‚ โ”‚ โ”œโ”€โ”€ anthropic.ts # native Messages API + browser CORS header โ”‚ โ”‚ โ”œโ”€โ”€ sse.ts # fetch/ReadableStream SSE parser + retry โ”‚ โ”‚ โ”œโ”€โ”€ catalog.ts # the complete 170-model typed catalog โ”‚ โ”‚ โ””โ”€โ”€ testKey.ts # key validation (/models or min completion) โ”‚ โ”œโ”€โ”€ storage/ # ๐Ÿ’พ the ONLY place touching localStorage/IndexedDB โ”‚ โ”‚ โ”œโ”€โ”€ keys.ts # namespaced key map (+ vault overlay) โ”‚ โ”‚ โ”œโ”€โ”€ vault.ts # AES-GCM passphrase lock โ”‚ โ”‚ โ”œโ”€โ”€ db.ts # dependency-free IndexedDB wrapper โ”‚ โ”‚ โ”œโ”€โ”€ conversations.ts # CRUD + schema migration + search โ”‚ โ”‚ โ”œโ”€โ”€ settings.ts # theme, default model, proxy overrides โ”‚ โ”‚ โ””โ”€โ”€ backup.ts # export / import / clear-everything โ”‚ โ”œโ”€โ”€ state/store.ts # Zustand: send/stream/stop/regenerate/branch/title โ”‚ โ”œโ”€โ”€ features/ # personas, prompt library, compare, exporters, trim โ”‚ โ”œโ”€โ”€ components/ # Sidebar, ChatView, ModelMenu, CompareView, โ€ฆ โ”‚ โ””โ”€โ”€ design/ # ZyquoTheme tokens (CSS vars), components.css โ”œโ”€โ”€ scripts/ # prod server + live verification gates โ”œโ”€โ”€ docs/ # PLAN, PROVIDER-REUSE, CORS-MATRIX, VERIFICATION, NATIVE-UX โ””โ”€โ”€ .github/workflows/ # CI + GitHub Pages deploy ``` Two hard architectural invariants, enforced by sweep: **all network I/O is in `providers/`**, **all persistence is in `storage/`** โ€” components stay presentational. --- ## โ˜๏ธ Deployment The build is **100% static** โ€” host it anywhere. ### GitHub Pages (automated) Every push to `main` triggers [`deploy-pages.yml`](.github/workflows/deploy-pages.yml): build with `VITE_BASE=/zyquo-cloud-web/` โ†’ deploy to **https://spboucher-ai.github.io/zyquo-cloud-web/**. ### Self-host (any box, 30 seconds) ```bash npm run build node scripts/server.mjs 8080 dist ``` `server.mjs` is a zero-dependency Node server that adds the full security header set (CSP, HSTS preload, nosniff, Referrer-Policy, Permissions-Policy), immutable caching for hashed assets, SPA fallback, and `/healthz`. ### Production (www.zyquo.cloud) The primary instance runs the same `server.mjs` under **PM2** behind an **ngrok** custom domain: ```bash pm2 start server.mjs --name zyquo-cloud-web -- 8080 dist pm2 start "ngrok http --url=www.zyquo.cloud 8080" --name zyquo-cloud-ngrok pm2 save ``` ### Static hosts Cloudflare Pages / Netlify / Vercel: build command `npm run build`, output `dist/`. Add the security headers from `scripts/server.mjs` via your host's headers config for parity. --- ## โœ… Verification This app was verified **live, with real keys, from a real browser** โ€” full report in [`docs/VERIFICATION.md`](docs/VERIFICATION.md): | Check | Result | |---|---| | ๐ŸŒŠ Streaming, token-by-token | **169 / 169 tested models** โœ… | | ๐Ÿ“ฌ Non-streaming completion | 12 / 12 providers โœ… | | โน Stop / `AbortController` | 12 / 12 providers โœ… | | ๐Ÿ‘ Vision (base64 image parts) | 9 / 9 vision-capable providers โœ… | | ๐Ÿง  Reasoning deltas | all thinking models โœ… | | ๐Ÿ”— Perplexity citations | โœ… (19 chips on one query) | | ๐ŸŒ CORS direct-from-browser | 12 / 12 โœ… | | ๐Ÿ’พ Persistence (reload + restart + vault) | 13 / 13 checks โœ… | | ๐Ÿงญ Cross-browser (Chromium/WebKit/Firefox) | 15 / 15 checks โœ… | | ๐Ÿ•ต๏ธ Network audit | provider origins + localhost, **nothing else** โœ… | --- ## ๐Ÿ—บ Roadmap - [ ] Function calling / tools UI (the catalog already tracks `tools` capability) - [ ] Model aliases editor in Settings (aliases already surface in the Model Menu) - [ ] Client-side PDF export (Markdown/JSON exports ship today) - [ ] Transcript virtualization for 1,000+ message threads - [ ] Conversation tags & archive surfaces (data model ready) - [ ] i18n (FR first) --- ## ๐Ÿค Contributing Contributions are welcome! 1. **Fork** and create a branch: `git checkout -b feat/my-feature` 2. Keep the invariants: networking only in `providers/`, persistence only in `storage/`, design tokens only (no raw hex in components), and the file header on every code file. 3. `npm run check` must be green. 4. Open a PR with a clear description. > ๐Ÿ” Never commit keys. `.keys.local.json`, `.env*` and friends are > gitignored; CI contains a history secret-scan precedent. --- ## ๐Ÿ“„ License Released under the [MIT License](LICENSE) โ€” ยฉ 2026 Simon-Pierre Boucher. ---
## ๐Ÿ‘ค Author Zyquo **Simon-Pierre Boucher** ๐Ÿ“ซ **Contact:** [contact@spboucher.ai](mailto:contact@spboucher.ai) *Zyquo Cloud Web is part of the Zyquo family โ€” native **Zyquo Cloud** (macOS), **Zyquo Router** (local gateway), and friends.*
โญ **If this project is useful to you, a star makes my day.** โญ