SPB Git

spb/zyquo-cloud-web

Public MIT

Zyquo Cloud Web — every cloud model, one beautiful chat, entirely in your browser.

phase1: Vite + React 19 + TS strict scaffold, ZyquoTheme CSS tokens, strict CSP, PWA shell

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Jul 31, 2026) parent e17a7df Browse files

17 changed files +8,172 −1

added .prettierrc +6 −0
@@ -0,0 +1,6 @@
1 +{
2 + "semi": false,
3 + "singleQuote": true,
4 + "printWidth": 100,
5 + "trailingComma": "es5"
6 +}
modified docs/PLAN.md +15 −1
@@ -38,7 +38,21 @@ CORS-MATRIX.md complete.
38 38
39 39 ---
40 40
41 −## Phase 1 — Project setup (React + TS + Vite) — pending
41 +## Phase 1 — Project setup (React + TS + Vite)
42 +
43 +- [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 installed
46 +- [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 scripts
49 +- [x] Phase checkpoint: `npm run check` green (typecheck, lint, build)
50 +
51 +**Phase 1 summary.** Hand-scaffolded Vite/React/TS-strict app with mandatory
52 +headers everywhere, ZyquoTheme tokens as CSS variables (light default + dark +
53 +5 accents), a locked-down CSP whose connect-src is exactly the 12 provider
54 +origins plus localhost for Zyquo Router, and an app-shell-only PWA. Build,
55 +lint, and typecheck all green on first checkpoint.
42 56
43 57 ## Phase 2 — Architecture + browser provider layer — pending
44 58
added eslint.config.js +86 −0
@@ -0,0 +1,86 @@
1 +/*
2 + * eslint.config.js
3 + * Zyquo Cloud Web
4 + *
5 + * Author: Simon-Pierre Boucher
6 + * Mail: contact@spboucher.ai
7 + */
8 +
9 +import js from '@eslint/js'
10 +import tsParser from '@typescript-eslint/parser'
11 +import tsPlugin from '@typescript-eslint/eslint-plugin'
12 +import reactHooks from 'eslint-plugin-react-hooks'
13 +
14 +export default [
15 + js.configs.recommended,
16 + {
17 + files: ['src/**/*.{ts,tsx}'],
18 + languageOptions: {
19 + parser: tsParser,
20 + parserOptions: { ecmaVersion: 2022, sourceType: 'module', ecmaFeatures: { jsx: true } },
21 + globals: {
22 + window: 'readonly',
23 + document: 'readonly',
24 + navigator: 'readonly',
25 + console: 'readonly',
26 + fetch: 'readonly',
27 + localStorage: 'readonly',
28 + indexedDB: 'readonly',
29 + crypto: 'readonly',
30 + setTimeout: 'readonly',
31 + clearTimeout: 'readonly',
32 + setInterval: 'readonly',
33 + clearInterval: 'readonly',
34 + AbortController: 'readonly',
35 + TextDecoder: 'readonly',
36 + TextEncoder: 'readonly',
37 + TextDecoderStream: 'readonly',
38 + ReadableStream: 'readonly',
39 + Response: 'readonly',
40 + Request: 'readonly',
41 + Headers: 'readonly',
42 + FileReader: 'readonly',
43 + Blob: 'readonly',
44 + File: 'readonly',
45 + URL: 'readonly',
46 + URLSearchParams: 'readonly',
47 + IDBDatabase: 'readonly',
48 + IDBRequest: 'readonly',
49 + IDBTransaction: 'readonly',
50 + SpeechSynthesisUtterance: 'readonly',
51 + speechSynthesis: 'readonly',
52 + requestAnimationFrame: 'readonly',
53 + cancelAnimationFrame: 'readonly',
54 + matchMedia: 'readonly',
55 + HTMLElement: 'readonly',
56 + HTMLInputElement: 'readonly',
57 + HTMLTextAreaElement: 'readonly',
58 + HTMLDivElement: 'readonly',
59 + HTMLButtonElement: 'readonly',
60 + HTMLImageElement: 'readonly',
61 + KeyboardEvent: 'readonly',
62 + MouseEvent: 'readonly',
63 + ClipboardEvent: 'readonly',
64 + DragEvent: 'readonly',
65 + Event: 'readonly',
66 + CustomEvent: 'readonly',
67 + AbortSignal: 'readonly',
68 + DOMException: 'readonly',
69 + performance: 'readonly',
70 + atob: 'readonly',
71 + btoa: 'readonly',
72 + structuredClone: 'readonly',
73 + queueMicrotask: 'readonly',
74 + },
75 + },
76 + plugins: { '@typescript-eslint': tsPlugin, 'react-hooks': reactHooks },
77 + rules: {
78 + ...tsPlugin.configs.recommended.rules,
79 + ...reactHooks.configs.recommended.rules,
80 + 'no-unused-vars': 'off',
81 + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
82 + 'no-undef': 'off',
83 + },
84 + },
85 + { ignores: ['dist/', 'dev-dist/', 'node_modules/'] },
86 +]
added index.html +36 −0
@@ -0,0 +1,36 @@
1 +<!doctype html>
2 +<!--
3 + index.html
4 + Zyquo Cloud Web
5 +
6 + Author: Simon-Pierre Boucher
7 + Mail: contact@spboucher.ai
8 +-->
9 +<html lang="en">
10 + <head>
11 + <meta charset="UTF-8" />
12 + <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
13 + <!--
14 + Content-Security-Policy (defense-in-depth; the deployment also sends it
15 + as an HTTP header). connect-src is the exact set of the 12 provider API
16 + origins, plus localhost for a locally running Zyquo Router / user proxy.
17 + No third-party scripts, no inline scripts.
18 + -->
19 + <meta
20 + http-equiv="Content-Security-Policy"
21 + content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' https://api.openai.com https://api.anthropic.com https://api.x.ai https://api.mistral.ai https://generativelanguage.googleapis.com https://dashscope-intl.aliyuncs.com https://api.deepseek.com https://api.moonshot.ai https://api.perplexity.ai https://api.together.xyz https://api.deepinfra.com https://api.cerebras.ai http://localhost:* http://127.0.0.1:* ws://localhost:* ws://127.0.0.1:*; worker-src 'self'; object-src 'none'; base-uri 'self'; form-action 'none'; frame-src 'none'"
22 + />
23 + <meta name="theme-color" content="#FAFBFD" />
24 + <meta
25 + name="description"
26 + content="Zyquo Cloud Web — multi-provider AI chat that lives entirely in your browser. Your keys, your history, no backend."
27 + />
28 + <link rel="icon" type="image/svg+xml" href="/icons/favicon.svg" />
29 + <link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
30 + <title>Zyquo Cloud</title>
31 + </head>
32 + <body>
33 + <div id="root"></div>
34 + <script type="module" src="/src/main.tsx"></script>
35 + </body>
36 +</html>
added package-lock.json +7596 −0
Large diff (7,596 lines) is not loaded by default to keep the page fast.Load diff
added package.json +35 −0
Large diff (35 lines) is not loaded by default to keep the page fast.Load diff
added src/App.tsx +31 −0
Large diff (31 lines) is not loaded by default to keep the page fast.Load diff
added src/design/global.css +77 −0
Large diff (77 lines) is not loaded by default to keep the page fast.Load diff
added src/design/tokens.css +160 −0
Large diff (160 lines) is not loaded by default to keep the page fast.Load diff
added src/main.tsx +22 −0
Large diff (22 lines) is not loaded by default to keep the page fast.Load diff
added src/vite-env.d.ts +10 −0
Large diff (10 lines) is not loaded by default to keep the page fast.Load diff
added tsconfig.app.json +24 −0
Large diff (24 lines) is not loaded by default to keep the page fast.Load diff
added tsconfig.app.tsbuildinfo +1 −0
Large diff (2 lines) is not loaded by default to keep the page fast.Load diff
added tsconfig.json +4 −0
Large diff (4 lines) is not loaded by default to keep the page fast.Load diff
added tsconfig.node.json +17 −0
Large diff (17 lines) is not loaded by default to keep the page fast.Load diff
added tsconfig.node.tsbuildinfo +1 −0
Large diff (2 lines) is not loaded by default to keep the page fast.Load diff
added vite.config.ts +51 −0
Large diff (52 lines) is not loaded by default to keep the page fast.Load diff
Menu

Theme