From aea2442fd6cf7beee71afe4cf6ce5dec7b42d6a3 Mon Sep 17 00:00:00 2001 From: Jens Mohrmann Date: Tue, 30 Dec 2025 20:17:35 +0100 Subject: [PATCH] mobile UI --- src/App.css | 4 ++- src/App.tsx | 14 ++++++++++- src/components/Controls.tsx | 1 + src/components/Fretboard.tsx | 15 +++++------ src/components/PianoKeys.tsx | 22 ++++++++-------- src/components/SettingsModal.tsx | 43 +++++++++++++++++++++++++++++++- src/styles/variables.css | 29 +++++++++++++++++---- 7 files changed, 102 insertions(+), 26 deletions(-) diff --git a/src/App.css b/src/App.css index 35c6db3..100dbd8 100644 --- a/src/App.css +++ b/src/App.css @@ -465,6 +465,8 @@ button { width: 90%; position: relative; border: 1px solid rgba(128, 128, 128, 0.1); + max-height: 85vh; + overflow-y: auto; } .help-item { @@ -781,7 +783,7 @@ button { display: flex; align-items: center; justify-content: center; - background: #2a2a2a; + /* background: #2a2a2a; Removed hardcoded background to respect theme */ min-height: 100px; /* Reduced to fit on screen */ max-height: 50vh; diff --git a/src/App.tsx b/src/App.tsx index 0887ce6..33fd018 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -61,7 +61,8 @@ function App() { virtualGuitarVolume: 0.5, virtualGuitarMute: false, micSensitivity: 0.5, - disableAnimation: false + disableAnimation: false, + theme: 'auto' }); const { pitchData, error, audioLevel, debugInfo, isListening } = usePitchDetector(listening, settings.micSensitivity); @@ -85,6 +86,17 @@ function App() { return () => window.removeEventListener('resize', handleResize); }, []); + // Theme support + useEffect(() => { + const root = document.documentElement; + const theme = settings.theme || 'auto'; + if (theme === 'auto') { + root.removeAttribute('data-theme'); + } else { + root.setAttribute('data-theme', theme); + } + }, [settings.theme]); + useEffect(() => { const handleFullscreenChange = () => { setIsFullscreen(!!document.fullscreenElement); diff --git a/src/components/Controls.tsx b/src/components/Controls.tsx index c0b5a49..a99ec65 100644 --- a/src/components/Controls.tsx +++ b/src/components/Controls.tsx @@ -35,6 +35,7 @@ export interface AppSettings { micSensitivity?: number; // 0.0 to 1.0 (0=least sensitive, 1=most) virtualGuitarMute?: boolean; disableAnimation?: boolean; + theme?: 'light' | 'dark' | 'auto'; } interface ControlsProps { diff --git a/src/components/Fretboard.tsx b/src/components/Fretboard.tsx index 31a3c01..e1103d8 100644 --- a/src/components/Fretboard.tsx +++ b/src/components/Fretboard.tsx @@ -99,7 +99,8 @@ export function Fretboard({ y={paddingY} width={width - (paddingX * 2) - nutWidth} height={height - (paddingY * 2)} - fill="#999999" + fill="var(--color-text-muted)" + fillOpacity={0.3} stroke="none" /> @@ -109,7 +110,7 @@ export function Fretboard({ y={paddingY} width={nutWidth} height={height - (paddingY * 2)} - fill="#333" + fill="var(--color-primary)" /> {/* Fret Markers (Dots) */} @@ -120,13 +121,13 @@ export function Fretboard({ if (isDouble) { return ( - - + + ); } return ( - + ); })} @@ -141,7 +142,7 @@ export function Fretboard({ y1={paddingY} x2={x} y2={height - paddingY} - stroke="#CCCCCC" + stroke="var(--color-text-muted)" strokeWidth={1} /> ); @@ -158,7 +159,7 @@ export function Fretboard({ y1={y} x2={width - paddingX} y2={y} - stroke="#EEEEEE" + stroke="var(--color-text-main)" strokeWidth={Math.max(1.5, visualThickness)} /> ); diff --git a/src/components/PianoKeys.tsx b/src/components/PianoKeys.tsx index 9ac4c11..1e20916 100644 --- a/src/components/PianoKeys.tsx +++ b/src/components/PianoKeys.tsx @@ -157,12 +157,12 @@ export function PianoKeys({ Range: {keys.list[0]?.midi} - {keys.list[keys.list.length - 1]?.midi} -
+

Settings

+ {/* Theme Settings */} +
+ +
+ {[ + { id: 'light', icon: Sun, label: 'Light' }, + { id: 'dark', icon: Moon, label: 'Dark' }, + { id: 'auto', icon: Monitor, label: 'Auto' } + ].map(mode => ( + + ))} +
+
+ +
+ {/* Animation Settings */}
diff --git a/src/styles/variables.css b/src/styles/variables.css index 647b2e8..bf803ef 100644 --- a/src/styles/variables.css +++ b/src/styles/variables.css @@ -9,17 +9,14 @@ --hue-text: 30; /* Warmer text */ + /* Default Light Theme */ --color-bg: hsl(30, 20%, 97%); /* Warm paper-like bg */ --color-surface: hsl(0, 0%, 100%); - - /* Neutral / Black Accent */ --color-primary: hsl(0, 0%, 20%); --color-primary-dark: hsl(0, 0%, 0%); - --color-text-main: hsl(var(--hue-text), 10%, 20%); --color-text-muted: hsl(var(--hue-text), 5%, 60%); - --color-success: hsl(var(--hue-success), 60%, 45%); --color-error: hsl(var(--hue-error), 70%, 55%); --color-warning: hsl(35, 90%, 50%); @@ -52,18 +49,40 @@ --radius-lg: 24px; } + + +/* Auto Dark Mode (System Preference) */ @media (prefers-color-scheme: dark) { :root { --color-bg: hsl(30, 20%, 10%); --color-surface: hsl(30, 20%, 15%); --color-text-main: hsl(30, 10%, 90%); --color-text-muted: hsl(30, 10%, 60%); - /* In dark mode, 'primary' (black) matches background too much, so we invert or use white */ --color-primary: hsl(0, 0%, 90%); --color-primary-dark: hsl(0, 0%, 100%); } } +/* Manual Dark Mode Override */ +html[data-theme="dark"] { + --color-bg: hsl(30, 20%, 10%); + --color-surface: hsl(30, 20%, 15%); + --color-text-main: hsl(30, 10%, 90%); + --color-text-muted: hsl(30, 10%, 60%); + --color-primary: hsl(0, 0%, 90%); + --color-primary-dark: hsl(0, 0%, 100%); +} + +/* Manual Light Mode Override */ +html[data-theme="light"] { + --color-bg: hsl(30, 20%, 97%); + --color-surface: hsl(0, 0%, 100%); + --color-primary: hsl(0, 0%, 20%); + --color-primary-dark: hsl(0, 0%, 0%); + --color-text-main: hsl(var(--hue-text), 10%, 20%); + --color-text-muted: hsl(var(--hue-text), 5%, 60%); +} + body { margin: 0; font-family: var(--font-family);