phase1: Vite + React 19 + TS strict scaffold, ZyquoTheme CSS tokens, strict CSP, PWA shell
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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