diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index e615aae1f2..d4a8b4b043 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -144,6 +144,7 @@ jobs: DB_DATABASE=cipherbox_staging JWT_SECRET=${{ secrets.STAGING_JWT_SECRET }} WEB_APP_URL=${{ vars.STAGING_WEB_APP_URL }} + CORS_ALLOWED_ORIGINS=${{ vars.CORS_ALLOWED_ORIGINS }} IPFS_PROVIDER=local IPFS_LOCAL_API_URL=http://ipfs:5001 IPFS_LOCAL_GATEWAY_URL=http://ipfs:8080 diff --git a/.planning/STATE.md b/.planning/STATE.md index 1e0d56d040..0b0419cf35 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -12,7 +12,7 @@ See: .planning/PROJECT.md (updated 2026-02-11) Phase: 12 (first of 7 M2 phases: 11-17) Plan: Not yet planned Status: Ready to plan -Last activity: 2026-02-11 -- Completed quick task 009: Fix footer GitHub link +Last activity: 2026-02-11 -- Completed quick task 010: Matrix effect visibility Progress: [##########..........] 50% (M1 complete, M2 0/7 phases) @@ -67,9 +67,10 @@ Recent decisions affecting current work: ### Quick Tasks Completed -| # | Description | Date | Commit | Directory | -| --- | ---------------------- | ---------- | ------- | ----------------------------------------------------------------- | -| 009 | Fix footer GitHub link | 2026-02-11 | c13036d | [009-fix-footer-github-link](./quick/009-fix-footer-github-link/) | +| # | Description | Date | Commit | Directory | +| --- | ------------------------ | ---------- | ------- | --------------------------------------------------------------------- | +| 009 | Fix footer GitHub link | 2026-02-11 | c13036d | [009-fix-footer-github-link](./quick/009-fix-footer-github-link/) | +| 010 | Matrix effect visibility | 2026-02-11 | 74d27b5 | [010-matrix-effect-visibility](./quick/010-matrix-effect-visibility/) | ### Research Flags diff --git a/.planning/quick/010-matrix-effect-visibility/010-PLAN.md b/.planning/quick/010-matrix-effect-visibility/010-PLAN.md new file mode 100644 index 0000000000..0227bd7517 --- /dev/null +++ b/.planning/quick/010-matrix-effect-visibility/010-PLAN.md @@ -0,0 +1,235 @@ +--- +phase: quick-010 +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - apps/web/src/components/MatrixBackground.tsx + - apps/web/src/styles/layout.css + - apps/web/src/styles/modal.css + - apps/web/src/components/ui/Modal.tsx +autonomous: true + +must_haves: + truths: + - 'Matrix rain effect is visibly bleeding through header, sidebar, and footer backgrounds (NOT main content area)' + - 'Modal backdrop shows matrix rain effect behind the modal container' + - 'Text in header, sidebar, and footer remains readable against the semi-transparent backgrounds' + - 'Modal content remains readable with matrix backdrop' + artifacts: + - path: 'apps/web/src/components/MatrixBackground.tsx' + provides: 'Reusable matrix background with configurable opacity and className' + contains: 'opacity' + - path: 'apps/web/src/styles/layout.css' + provides: 'Reduced background opacities for app shell chrome' + contains: 'rgb(0 0 0' + - path: 'apps/web/src/styles/modal.css' + provides: 'Matrix-compatible modal backdrop styling' + contains: 'rgb(0 0 0' + - path: 'apps/web/src/components/ui/Modal.tsx' + provides: 'Modal with embedded MatrixBackground on backdrop' + contains: 'MatrixBackground' + key_links: + - from: 'apps/web/src/components/ui/Modal.tsx' + to: 'apps/web/src/components/MatrixBackground.tsx' + via: 'import and render inside backdrop div' + pattern: 'import.*MatrixBackground' +--- + + +Make the matrix rain canvas effect visually bleed through app shell chrome elements (header, sidebar, footer — NOT the main content area) and add it as a backdrop effect on modal overlays. + +Purpose: The matrix rain is currently barely visible because app shell backgrounds are too opaque (0.85) and the canvas opacity is too low (0.25). This change creates a more immersive terminal/cyberpunk aesthetic while keeping the main content area fully readable. + +Output: Updated MatrixBackground component with props, reduced layout opacities (except main), and matrix-enhanced modal backdrop. + + + +@./.claude/get-shit-done/workflows/execute-plan.md +@./.claude/get-shit-done/templates/summary.md + + + +@apps/web/src/components/MatrixBackground.tsx +@apps/web/src/styles/layout.css +@apps/web/src/styles/modal.css +@apps/web/src/components/ui/Modal.tsx +@apps/web/src/components/layout/AppShell.tsx +@apps/web/CLAUDE.md (CSS conventions: use modern `rgb(0 0 0 / 50%)` notation, never legacy `rgba()`) + + + + + + Task 1: Add props to MatrixBackground and reduce layout opacities + + apps/web/src/components/MatrixBackground.tsx + apps/web/src/styles/layout.css + + +**MatrixBackground.tsx** -- Add optional props for reuse in different contexts: + +1. Add props interface: + + ```tsx + interface MatrixBackgroundProps { + /** Canvas opacity. Default 0.4 */ + opacity?: number; + /** Additional CSS class name */ + className?: string; + } + ``` + +2. Update function signature: `export function MatrixBackground({ opacity = 0.4, className }: MatrixBackgroundProps)` + +3. Replace the inline `style` object on the canvas element with a CSS class approach. Keep `pointerEvents: 'none'` inline (it's behavioral). Move positioning/sizing to CSS: + + ```tsx + + + +1. `cd /Users/michael/Code/cipher-box && pnpm --filter web build` compiles without errors +2. Visual check: header, sidebar, footer, and main area show green matrix rain bleeding through their semi-transparent backgrounds + + + +- MatrixBackground accepts optional `opacity` and `className` props +- Default opacity is 0.4 (up from 0.25) +- Canvas positioning uses `.matrix-canvas` CSS class instead of inline styles +- All four layout element backgrounds reduced to 60-70% opacity using modern `rgb()` notation +- Matrix rain visibly shows through all app shell chrome elements + + + + + Task 2: Add matrix effect to modal backdrop + + apps/web/src/components/ui/Modal.tsx + apps/web/src/styles/modal.css + + +**Modal.tsx** -- Add a MatrixBackground instance inside the modal backdrop: + +1. Add import: `import { MatrixBackground } from '../MatrixBackground';` + +2. Inside the ``, render a MatrixBackground inside the backdrop div, BEFORE the modal-container div: + + ```tsx +
+ +
+ ``` + + The low opacity (0.15) keeps it subtle so modal content stays readable. The `className` prop adds `.matrix-canvas--modal` for modal-specific positioning. + +**modal.css** -- Update backdrop and add modal matrix positioning: + +1. Change `.modal-backdrop` background from `rgba(0, 0, 0, 0.8)` to `rgb(0 0 0 / 70%)` -- slightly more transparent to let matrix show. Keep `backdrop-filter: blur(4px)`. + +2. Add `.matrix-canvas--modal` override class (add after the Modal Backdrop section): + + ```css + .matrix-canvas--modal { + position: absolute; + z-index: 0; + } + ``` + + This overrides the `position: fixed` and `z-index: -1` from `.matrix-canvas` so the canvas sits inside the backdrop's stacking context rather than behind the entire page. + +3. Add `position: relative` and `overflow: hidden` to `.modal-backdrop` so the absolute-positioned matrix canvas is contained within it. The backdrop already has `position: fixed` and `inset: 0` so adding `overflow: hidden` is safe. + +4. Ensure `.modal-container` has `z-index: 1` (or `position: relative; z-index: 1`) so the modal content sits above the matrix canvas in the backdrop. Currently `.modal-container` has `position: relative` already -- just add `z-index: 1` to it. + +5. Fix the legacy `rgba()` in `.modal-container` box-shadow while here: + `box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5), var(--glow-green)` --> `box-shadow: 0 25px 50px -12px rgb(0 0 0 / 50%), var(--glow-green)` + +IMPORTANT: The MatrixBackground component creates a new canvas with its own animation loop. Since modals are not always open, this is fine -- the canvas only renders when the modal is open (Modal returns null when `open` is false). The animation loop starts on mount and cleans up on unmount via the existing useEffect cleanup. + + + +1. `cd /Users/michael/Code/cipher-box && pnpm --filter web build` compiles without errors +2. Open any modal (e.g., upload modal, settings) -- backdrop shows subtle green matrix rain behind the modal container +3. Close modal -- no console errors, no lingering animation frames +4. Modal content text remains fully readable + + + +- Modal backdrop renders a dedicated MatrixBackground instance at 0.15 opacity +- Matrix canvas is contained within the backdrop via absolute positioning +- Modal container sits above the matrix canvas via z-index +- Backdrop opacity reduced from 0.8 to 0.7 to let matrix show +- All legacy rgba() calls in modal.css converted to modern rgb() notation +- Matrix canvas cleans up properly when modal closes (existing useEffect cleanup) + + + + + + +1. `pnpm --filter web build` -- no TypeScript or build errors +2. Visual: App shell shows matrix rain bleeding through header, sidebar, main area, and footer +3. Visual: Opening a modal shows subtle matrix rain on the backdrop overlay +4. Visual: All text remains readable in header, sidebar, footer, main area, and modal content +5. No console errors or warnings related to MatrixBackground or Modal +6. Login page still shows matrix background (now at 0.4 opacity instead of 0.25) + + + + +- Matrix rain visually bleeds through all app shell chrome (header, sidebar, footer, main) +- Modal backdrops display a subtle matrix rain effect +- All text across the application remains readable +- No build errors, no runtime errors +- All CSS uses modern `rgb(0 0 0 / XX%)` notation (no legacy `rgba()`) + + + +After completion, create `.planning/quick/010-matrix-effect-visibility/010-SUMMARY.md` + diff --git a/.planning/quick/010-matrix-effect-visibility/010-SUMMARY.md b/.planning/quick/010-matrix-effect-visibility/010-SUMMARY.md new file mode 100644 index 0000000000..90407b582d --- /dev/null +++ b/.planning/quick/010-matrix-effect-visibility/010-SUMMARY.md @@ -0,0 +1,57 @@ +# Quick Task 010: Matrix Effect Visibility + +## Summary + +Made the matrix rain background effect visible through the app shell chrome (header, sidebar, footer) and added a dedicated matrix animation to modal backdrop overlays. The app-level animation pauses when a modal is open so only one canvas runs at a time (no performance cost). + +## Changes + +### MatrixBackground.tsx + +- Added `opacity`, `className`, and `paused` props +- Default opacity increased from 0.25 to 0.5 +- Animation frame rate increased from ~30fps to ~60fps +- Moved positioning styles from inline to `.matrix-canvas` CSS class +- Pause support: animation loop skips draw calls when `paused=true` + +### useModalOpen.ts (new) + +- Lightweight global open-modal counter using `useSyncExternalStore` +- `incrementModalCount()` / `decrementModalCount()` called by Modal on mount/unmount +- `useAnyModalOpen()` hook returns true when any modal is open + +### AppShell.tsx + +- Passes `paused={anyModalOpen}` to MatrixBackground +- App-level animation freezes when modal opens, resumes when it closes + +### Modal.tsx + +- Renders a dedicated `MatrixBackground` (opacity 0.35) inside the modal backdrop +- Calls `incrementModalCount` on mount, `decrementModalCount` on unmount +- Only one canvas animates at a time + +### layout.css + +- Added `.matrix-canvas` CSS class (position: fixed, z-index: -1) +- Removed solid `background-color` from `.app-shell` (was blocking the canvas) +- Reduced header/sidebar/footer backgrounds to `rgb(0 0 0 / 50%)` +- Main content area set to `rgb(0 0 0 / 90%)` (stays opaque) +- Converted all `rgba()` to modern `rgb()` notation + +### modal.css + +- Added `.matrix-canvas--modal` class for absolute positioning inside backdrop +- Reduced backdrop opacity to 40% for more visible matrix effect +- Removed `backdrop-filter: blur` (was obscuring the matrix) +- Added `z-index: 1` to `.modal-container` for proper stacking +- Converted legacy `rgba()` to modern `rgb()` notation + +## Verified + +- Build compiles without errors +- Matrix rain visible through header, sidebar, and footer +- Main content area remains opaque and readable +- Modal overlay shows dedicated matrix animation +- App-level canvas pauses when modal opens (single canvas performance) +- No console errors on modal open/close diff --git a/apps/api/.env.example b/apps/api/.env.example index 0597aed587..d545052b93 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -14,7 +14,10 @@ JWT_SECRET= # Pinata (IPFS pinning service) PINATA_JWT= -# CORS - allowed origins for web app (comma-separated for multiple) +# CORS - allowed origins (comma-separated, supports wildcards with *) +# Example: https://app.cipherbox.cc,https://cipher-box-pr-*.onrender.com +# Falls back to WEB_APP_URL if not set +CORS_ALLOWED_ORIGINS=http://localhost:5173 WEB_APP_URL=http://localhost:5173 # IPFS Provider: 'pinata' (default) or 'local' (Kubo node) diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index ccc42f65d5..6b327ca8bd 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -25,10 +25,28 @@ async function bootstrap() { ); app.use(cookieParser()); + + // CORS_ALLOWED_ORIGINS supports wildcards (e.g. https://cipher-box-pr-*.onrender.com) + // Falls back to WEB_APP_URL for backwards compatibility + const rawOrigins = process.env.CORS_ALLOWED_ORIGINS || process.env.WEB_APP_URL; + const originEntries = rawOrigins + ? rawOrigins.split(',').map((s) => s.trim()) + : ['http://localhost:5173', 'http://localhost:4173']; + const exactOrigins = originEntries.filter((o) => !o.includes('*')); + const wildcardPatterns = originEntries + .filter((o) => o.includes('*')) + .map((o) => new RegExp(`^${o.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*')}$`)); + app.enableCors({ - origin: process.env.WEB_APP_URL - ? process.env.WEB_APP_URL.split(',') - : ['http://localhost:5173', 'http://localhost:4173'], + origin: ( + origin: string | undefined, + callback: (err: Error | null, allow?: boolean) => void + ) => { + if (!origin) return callback(null, true); + if (exactOrigins.includes(origin)) return callback(null, true); + if (wildcardPatterns.some((re) => re.test(origin))) return callback(null, true); + callback(new Error(`Origin ${origin} not allowed by CORS`)); + }, credentials: true, }); diff --git a/apps/web/.env.example b/apps/web/.env.example index 1b8a5b9a3d..26bb27fba8 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -1,5 +1,5 @@ # Web3Auth -VITE_WEB3AUTH_CLIENT_ID=your-web3auth-client-id +VITE_WEB3AUTH_CLIENT_ID=BKBrLnxLHHqsis7NVyUhYoFB3RehqEziJRIWfKWFweEycHWaBduqQYZtgpEuKon4M2engmUgiax6k2xcNCbzP7s # API endpoint VITE_API_URL=http://localhost:3000 diff --git a/apps/web/src/App.css b/apps/web/src/App.css index e77b3a9417..3eb104c2aa 100644 --- a/apps/web/src/App.css +++ b/apps/web/src/App.css @@ -20,6 +20,17 @@ overflow: hidden; } +.login-panel { + position: relative; + z-index: 1; + display: flex; + flex-direction: column; + align-items: center; + padding: var(--spacing-xl) 48px; + background-color: rgb(0 0 0 / 60%); + border: 1px solid rgb(0 208 132 / 10%); +} + /* Logo: > CIPHERBOX with terminal prompt */ .login-container h1 { font-family: var(--font-family-mono); diff --git a/apps/web/src/components/MatrixBackground.tsx b/apps/web/src/components/MatrixBackground.tsx index e0d9c133c8..3aad80fb7e 100644 --- a/apps/web/src/components/MatrixBackground.tsx +++ b/apps/web/src/components/MatrixBackground.tsx @@ -1,13 +1,36 @@ import { useEffect, useRef } from 'react'; +interface MatrixBackgroundProps { + /** Canvas opacity. Default 0.5 */ + opacity?: number; + /** Additional CSS class name */ + className?: string; + /** Pause the animation loop */ + paused?: boolean; + /** Frame interval in ms. Default 16 (~60fps). Use 50 for slower effect. */ + frameInterval?: number; +} + /** - * Matrix rain background effect for login page. + * Matrix rain background effect. * Canvas-based animation with falling binary/hex characters. - * Performance-optimized: 30fps, low opacity, resize handling. + * Performance-optimized: 60fps, configurable opacity, resize handling. + * Supports pause/resume for coordination with modal overlays. */ -export function MatrixBackground() { +export function MatrixBackground({ + opacity = 0.5, + className, + paused = false, + frameInterval = 16, +}: MatrixBackgroundProps) { const canvasRef = useRef(null); const animationRef = useRef(0); + const pausedRef = useRef(paused); + + // Keep pausedRef in sync without restarting the effect + useEffect(() => { + pausedRef.current = paused; + }, [paused]); useEffect(() => { const canvas = canvasRef.current; @@ -21,7 +44,7 @@ export function MatrixBackground() { // Configuration const FONT_SIZE = 14; const COLUMN_WIDTH = 20; - const FRAME_INTERVAL = 33; // ~30fps + const FRAME_INTERVAL = frameInterval; const CHARACTERS = '01'; const PRIMARY_COLOR = '#00D084'; const DIM_COLOR = '#006644'; @@ -46,7 +69,13 @@ export function MatrixBackground() { // Draw frame function draw(timestamp: number) { - // Throttle to ~30fps + if (pausedRef.current) { + // Keep requesting frames so we resume instantly when unpaused + animationRef.current = requestAnimationFrame(draw); + return; + } + + // Throttle to ~60fps if (timestamp - lastFrameTime < FRAME_INTERVAL) { animationRef.current = requestAnimationFrame(draw); return; @@ -103,16 +132,10 @@ export function MatrixBackground() { return (