From 957bb9e0e74b7be86dbb2b1dd486d1862ce95be4 Mon Sep 17 00:00:00 2001 From: SinachPat Date: Wed, 29 Apr 2026 04:43:29 +0100 Subject: [PATCH] improved a lot of things --- .claude/settings.local.json | 3 +- packages/app/public/marketing.html | 109 ++++++++++++++++- packages/app/src/components/canvas/Canvas.tsx | 7 +- .../app/src/components/chrome/AppChrome.tsx | 9 +- .../app/src/components/chrome/Toolbar.tsx | 93 ++++++-------- .../src/components/inspector/Inspector.tsx | 88 +++++++------- .../navigator/ArtboardNavigator.tsx | 113 ++++++++++-------- packages/app/src/store/canvasTheme.ts | 104 ++++++++++++++++ packages/app/tsconfig.tsbuildinfo | 2 +- 9 files changed, 362 insertions(+), 166 deletions(-) create mode 100644 packages/app/src/store/canvasTheme.ts diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 868f7ce..55ba25a 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -150,7 +150,8 @@ "Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); print\\(json.dumps\\(d.get\\('dependencies',{}\\), indent=2\\)\\)\")", "Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); print\\('version:', d.get\\('version'\\)\\)\")", "Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); print\\('type:', d.get\\('type'\\)\\); print\\('error:', d.get\\('error',{}\\).get\\('message','none'\\)[:200]\\)\")", - "Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); text=next\\(\\(b['text'] for b in d.get\\('content',[]\\) if b['type']=='text'\\),''\\); print\\('STATUS:', d.get\\('type','?'\\)\\); print\\('ERROR:', d.get\\('error',{}\\).get\\('message','none'\\)[:300]\\); print\\('TEXT:', text[:300]\\)\")" + "Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); text=next\\(\\(b['text'] for b in d.get\\('content',[]\\) if b['type']=='text'\\),''\\); print\\('STATUS:', d.get\\('type','?'\\)\\); print\\('ERROR:', d.get\\('error',{}\\).get\\('message','none'\\)[:300]\\); print\\('TEXT:', text[:300]\\)\")", + "Bash(perl -pi -e 's/color: '\\\\''rgba\\\\\\(255,255,255,0\\\\.22\\\\\\)'\\\\''/color: T.dim/g;' -e 's/color: '\\\\''rgba\\\\\\(255,255,255,0\\\\.18\\\\\\)'\\\\''/color: T.dim/g;' -e 's/color: '\\\\''rgba\\\\\\(255,255,255,0\\\\.28\\\\\\)'\\\\''/color: T.key/g;' -e 's/color: '\\\\''rgba\\\\\\(255,255,255,0\\\\.32\\\\\\)'\\\\''/color: T.key/g;' -e 's/color: '\\\\''rgba\\\\\\(255,255,255,0\\\\.45\\\\\\)'\\\\''/color: T.fgMuted/g;' -e 's/color: '\\\\''rgba\\\\\\(255,255,255,0\\\\.5\\\\\\)'\\\\''/color: T.fgMuted/g;' -e 's/color: '\\\\''rgba\\\\\\(255,255,255,0\\\\.62\\\\\\)'\\\\''/color: T.fgMuted/g;' -e 's/color: '\\\\''rgba\\\\\\(255,255,255,0\\\\.85\\\\\\)'\\\\''/color: T.fg/g;' -e 's/color: '\\\\''rgba\\\\\\(255,255,255,0\\\\.88\\\\\\)'\\\\''/color: T.fg/g;' /Users/USER/Desktop/originmain/packages/app/src/components/inspector/Inspector.tsx)" ] } } diff --git a/packages/app/public/marketing.html b/packages/app/public/marketing.html index c496cab..db3d663 100644 --- a/packages/app/public/marketing.html +++ b/packages/app/public/marketing.html @@ -69,6 +69,45 @@ --mono: 'JetBrains Mono', 'SF Mono', ui-monospace, 'Cascadia Code', monospace; } +/* ════════════════════════════════════════════════════════════ + DARK MODE — flip every semantic token + ════════════════════════════════════════════════════════════ */ +[data-theme="dark"] { + --bg: #0A0A0A; + --bg-subtle: #0F0F11; + --bg-muted: #161618; + --bg-strong: #1C1C1E; + --bg-inv: #F4F4F5; + --bg-inv-2: #E4E4E7; + --bg-inv-3: #D4D4D8; + + --fg: rgba(255,255,255,0.92); + --fg-2: rgba(255,255,255,0.58); + --fg-3: rgba(255,255,255,0.34); + --fg-4: rgba(255,255,255,0.16); + --fg-inv: #09090B; + --fg-inv-2: rgba(0,0,0,0.62); + --fg-inv-3: rgba(0,0,0,0.35); + + --border: rgba(255,255,255,0.07); + --border-2: rgba(255,255,255,0.12); + --border-inv: rgba(0,0,0,0.08); + --border-inv-2:rgba(0,0,0,0.14); + + --sh-xs: 0 1px 2px rgba(0,0,0,0.3); + --sh-sm: 0 1px 3px rgba(0,0,0,0.35), 0 1px 2px rgba(0,0,0,0.25); + --sh-md: 0 4px 8px rgba(0,0,0,0.32), 0 2px 4px rgba(0,0,0,0.22); + --sh-lg: 0 10px 24px rgba(0,0,0,0.38), 0 4px 8px rgba(0,0,0,0.26); + --sh-xl: 0 20px 40px rgba(0,0,0,0.42), 0 8px 16px rgba(0,0,0,0.28); + --sh-2xl:0 32px 64px rgba(0,0,0,0.48), 0 16px 32px rgba(0,0,0,0.32); + --sh-product: 0 48px 96px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.05); +} + +/* Nav glass adapts in dark mode */ +[data-theme="dark"] #nav.scrolled { + background: rgba(10,10,10,0.88); +} + /* ════════════════════════════════════════════════════════════ RESET ════════════════════════════════════════════════════════════ */ @@ -276,6 +315,22 @@ button { font-family: inherit; cursor: pointer; border: none; background: none; } .nav-links a:hover { color: var(--fg); } .nav-actions { display: flex; align-items: center; gap: 8px; } +.nav-theme-btn { + display: flex; + align-items: center; + justify-content: center; + width: 34px; + height: 34px; + border-radius: var(--r-2); + background: none; + border: 1px solid var(--border-2); + color: var(--fg-3); + font-size: 0.9rem; + transition: background 0.12s, color 0.12s, border-color 0.12s; + cursor: pointer; + flex-shrink: 0; +} +.nav-theme-btn:hover { background: var(--bg-muted); color: var(--fg); border-color: var(--border-2); } /* ── Hamburger ── */ .nav-burger { @@ -1555,6 +1610,7 @@ footer { } .nav-links { display: none; } .nav-actions .btn { display: none; } + .nav-actions .nav-theme-btn { display: none; } .nav-burger { display: flex; } .section { padding: 80px 0; } .feat-block { padding: 80px 24px; } @@ -1625,6 +1681,9 @@ footer {
  • Docs
  • @@ -2609,21 +2671,58 @@ annualBtn.addEventListener('click', () => { function safeLS(op, key, val) { try { return op === 'get' ? localStorage.getItem(key) : localStorage.setItem(key, val); } catch(e) {} } + +// Read from either the app Zustand key (set by the React app) or the landing-page key. +// Zustand stores { state: { mode: "dark" } } under 'originmain:theme'. +function readStoredTheme() { + try { + const zustand = localStorage.getItem('originmain:theme'); + if (zustand) { + const parsed = JSON.parse(zustand); + if (parsed?.state?.mode) return parsed.state.mode; + } + } catch(e) {} + return safeLS('get', 'om-theme') || 'light'; +} + +// Write to both keys so switching on the landing page carries through to the app. +function writeTheme(t) { + safeLS('set', 'om-theme', t); + try { + localStorage.setItem('originmain:theme', JSON.stringify({ state: { mode: t }, version: 0 })); + } catch(e) {} +} + const root = document.documentElement; -let theme = safeLS('get', 'om-theme') || 'light'; +let theme = readStoredTheme(); + const themeBtn = document.getElementById('themeToggle'); const themeIcon = document.getElementById('themeIcon'); const themeLabel = document.getElementById('themeLabel'); +const navThemeBtn = document.getElementById('navThemeBtn'); +const navThemeIcon = document.getElementById('navThemeIcon'); +const mobThemeBtn = document.getElementById('mobThemeBtn'); +const mobThemeIcon = document.getElementById('mobThemeIcon'); function applyTheme(t) { theme = t; root.setAttribute('data-theme', t); - safeLS('set', 'om-theme', t); - themeIcon.textContent = t === 'dark' ? '☀' : '☾'; - themeLabel.textContent = t === 'dark' ? 'Light mode' : 'Dark mode'; + writeTheme(t); + const isDark = t === 'dark'; + // Footer toggle + if (themeIcon) themeIcon.textContent = isDark ? '☀' : '☾'; + if (themeLabel) themeLabel.textContent = isDark ? 'Light mode' : 'Dark mode'; + // Nav toggle (desktop) + if (navThemeIcon) navThemeIcon.textContent = isDark ? '☀' : '☾'; + // Nav toggle (mobile drawer) + if (mobThemeIcon) mobThemeIcon.textContent = isDark ? '☀' : '☾'; } + applyTheme(theme); -themeBtn.addEventListener('click', () => applyTheme(theme === 'light' ? 'dark' : 'light')); + +if (themeBtn) themeBtn.addEventListener('click', () => applyTheme(theme === 'light' ? 'dark' : 'light')); +if (navThemeBtn) navThemeBtn.addEventListener('click', () => applyTheme(theme === 'light' ? 'dark' : 'light')); +if (mobThemeBtn) mobThemeBtn.addEventListener('click', () => applyTheme(theme === 'light' ? 'dark' : 'light')); // ── Feat cell reveal highlight (Fluent reveal effect) ───── document.querySelectorAll('.feat-cell').forEach(cell => { diff --git a/packages/app/src/components/canvas/Canvas.tsx b/packages/app/src/components/canvas/Canvas.tsx index 8e243d7..81ae6b5 100644 --- a/packages/app/src/components/canvas/Canvas.tsx +++ b/packages/app/src/components/canvas/Canvas.tsx @@ -5,9 +5,11 @@ import { useQueryClient } from '@tanstack/react-query'; import { useViewport } from '@/store/viewport'; import { useCanvas } from '@/store/canvas'; import { useArtboards, createArtboardMutation } from '@/hooks/useArtboards'; +import { useCanvasTheme } from '@/store/canvasTheme'; import { Artboard } from './Artboard'; export function Canvas() { + const T = useCanvasTheme(); const containerRef = useRef(null); const panX = useViewport((s) => s.panX); const panY = useViewport((s) => s.panY); @@ -151,7 +153,8 @@ export function Canvas() { gridRow: 2, position: 'relative', overflow: 'hidden', - background: '#0C0C10', + background: T.canvasBg, + transition: 'background 0.2s', cursor, }} onMouseDown={onMouseDown} @@ -163,7 +166,7 @@ export function Canvas() { style={{ position: 'absolute', inset: 0, - backgroundImage: 'radial-gradient(circle, rgba(255,255,255,0.055) 1px, transparent 1px)', + backgroundImage: `radial-gradient(circle, ${T.dotColor} 1px, transparent 1px)`, backgroundSize: `${gridSpacing}px ${gridSpacing}px`, backgroundPosition: `${panX % gridSpacing}px ${panY % gridSpacing}px`, pointerEvents: 'none', diff --git a/packages/app/src/components/chrome/AppChrome.tsx b/packages/app/src/components/chrome/AppChrome.tsx index 93a9b2c..68d1217 100644 --- a/packages/app/src/components/chrome/AppChrome.tsx +++ b/packages/app/src/components/chrome/AppChrome.tsx @@ -11,6 +11,7 @@ import { useHistory } from '@/store/history'; import { useCanvas, type Tool } from '@/store/canvas'; import { useViewport } from '@/store/viewport'; import { useTheme } from '@/store/theme'; +import { useCanvasTheme } from '@/store/canvasTheme'; interface AppChromeProps { workspaceId?: string; @@ -24,6 +25,7 @@ export function AppChrome({ workspaceId, projectId, workspaceName, projectName } const setContext = useCanvas((s) => s.setContext); const setActiveTool = useCanvas((s) => s.setActiveTool); const { mode: themeMode, toggle: toggleTheme } = useTheme(); + const CT = useCanvasTheme(); // Push workspace/project IDs into the store so Canvas and Navigator can read them. useEffect(() => { @@ -99,7 +101,8 @@ export function AppChrome({ workspaceId, projectId, workspaceName, projectName } flexDirection: 'column', height: '100dvh', overflow: 'hidden', - background: '#0C0C10', + background: CT.canvasBg, + transition: 'background 0.2s', fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif", }} > @@ -108,8 +111,8 @@ export function AppChrome({ workspaceId, projectId, workspaceName, projectName }
    s.zoom); const setZoom = useViewport((s) => s.setZoom); @@ -53,6 +38,7 @@ export function Toolbar() { gap: 2, height: 44, userSelect: 'none', + transition: 'background 0.2s, border-color 0.2s', }} > {/* Wordmark */} @@ -66,54 +52,55 @@ export function Toolbar() { padding: '0 10px 0 4px', marginRight: 2, flexShrink: 0, + transition: 'color 0.2s', }} > Om - + {/* Selection tools */} - setActiveTool('select')}> + setActiveTool('select')}> - setActiveTool('pan')}> + setActiveTool('pan')}> - + {/* Create tools */} - setActiveTool('artboard')}> + setActiveTool('artboard')}> - setActiveTool('zone')}> + setActiveTool('zone')}> - + {/* View tools */} - {}}> + {}}> - {}}> + {}}> @@ -122,7 +109,7 @@ export function Toolbar() { {/* Right side */}
    - setZoom(zoom * 0.8)}> + setZoom(zoom * 0.8)}> @@ -130,7 +117,7 @@ export function Toolbar() { - setZoom(zoom * 1.25)}> + setZoom(zoom * 1.25)}> - + {/* Live status pill */}
    -
    +
    ); } function ToolGroup({ children }: { children: React.ReactNode }) { - return ( -
    - {children} -
    - ); + return
    {children}
    ; } function TBtn({ + T, active, onClick, children, }: { + T: CanvasTokens; active: boolean; onClick: () => void; children: React.ReactNode; @@ -237,12 +217,11 @@ function TBtn({