This commit is contained in:
2025-12-21 22:39:18 +01:00
parent 4029f62f43
commit 30a52529d4
3 changed files with 135 additions and 55 deletions

View File

@@ -296,3 +296,49 @@
background-color: rgba(var(--hue-primary), 0.1); background-color: rgba(var(--hue-primary), 0.1);
transform: translateY(-2px); transform: translateY(-2px);
} }
.zen-button {
background-color: transparent;
border: 2px solid var(--color-text-muted);
color: var(--color-text-muted);
padding: 0.5rem 1rem;
border-radius: 30px;
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: all 0.2s;
font-size: 0.9rem;
}
.zen-button:hover {
border-color: var(--color-text-main);
color: var(--color-text-main);
background-color: rgba(128, 128, 128, 0.1);
transform: translateY(-2px);
}
.exit-zen-button {
position: fixed;
bottom: 20px;
right: 20px;
padding: 10px 20px;
background: var(--color-surface);
border: 1px solid var(--color-text-muted);
border-radius: 20px;
color: var(--color-text-muted);
cursor: pointer;
opacity: 0.5;
transition: all 0.2s;
z-index: 100;
font-weight: 600;
box-shadow: var(--shadow-md);
}
.exit-zen-button:hover {
opacity: 1;
color: var(--color-text-main);
border-color: var(--color-text-main);
transform: scale(1.05);
}

View File

@@ -40,7 +40,8 @@ function App() {
autoAdvance: false, autoAdvance: false,
sound: true, sound: true,
volume: 0.5 volume: 0.5
} },
zenMode: false
}); });
const [matchStartTime, setMatchStartTime] = useState<number | null>(null); const [matchStartTime, setMatchStartTime] = useState<number | null>(null);
@@ -173,10 +174,12 @@ function App() {
}, [settings.showHint, targetMidi, currentTuning, currentInstrumentDef]); }, [settings.showHint, targetMidi, currentTuning, currentInstrumentDef]);
return ( return (
<div className="app-container"> <div className={`app-container ${settings.zenMode ? 'zen-mode' : ''}`}>
<header className="app-header"> {!settings.zenMode && (
<div className="logo">Sheet music trainer</div> <header className="app-header">
</header> <div className="logo">Sheet music trainer</div>
</header>
)}
<main className="main-stage"> <main className="main-stage">
<div className="card sheet-music-card"> <div className="card sheet-music-card">
@@ -190,13 +193,15 @@ function App() {
height={currentInstrumentDef.clefMode === 'grand' ? 300 : 250} height={currentInstrumentDef.clefMode === 'grand' ? 300 : 250}
/> />
<div className="feedback-area"> {!settings.zenMode && (
{feedbackMessage ? ( <div className="feedback-area">
<div className="success-message animate-pop">{feedbackMessage}</div> {feedbackMessage ? (
) : ( <div className="success-message animate-pop">{feedbackMessage}</div>
<div className="instruction-text">Play the note above</div> ) : (
)} <div className="instruction-text">Play the note above</div>
</div> )}
</div>
)}
{settings.showHint && ( {settings.showHint && (
<div className="hint-card"> <div className="hint-card">
@@ -211,53 +216,81 @@ function App() {
{error && <div className="error-message">{error}</div>} {error && <div className="error-message">{error}</div>}
<div className="action-row" style={{ marginTop: '24px', display: 'flex', justifyContent: 'center', gap: '16px' }}> {!settings.zenMode && (
<button <div className="action-row" style={{ marginTop: '24px', display: 'flex', justifyContent: 'center', gap: '16px' }}>
className={`hint-button ${settings.showHint ? 'active' : ''}`} <button
onClick={() => setSettings(s => ({ ...s, showHint: !s.showHint }))} className={`hint-button ${settings.showHint ? 'active' : ''}`}
> onClick={() => setSettings(s => ({ ...s, showHint: !s.showHint }))}
<HelpCircle size={18} /> >
{settings.showHint ? "Hide Hint" : "Show Hint"} <HelpCircle size={18} />
</button> {settings.showHint ? "Hide Hint" : "Show Hint"}
<button className="skip-button" onClick={generateNewNote}> </button>
<SkipForward size={18} /> <button className="skip-button" onClick={generateNewNote}>
Skip Note <SkipForward size={18} />
</button> Skip Note
</div> </button>
</div> <button
className="zen-button"
<div className="pitch-monitor-bar"> onClick={() => setSettings(s => ({ ...s, zenMode: true }))}
<button title="Zen Mode"
className={`mic-button ${listening ? 'listening' : ''}`} >
onClick={() => setListening(!listening)} <span>Zen</span>
aria-label={listening ? "Stop Listening" : "Start Listening"} </button>
>
{listening ? <Mic size={28} /> : <MicOff size={28} />}
</button>
{settings.showTuningMeter && pitchData ? (
<TuningMeter cents={pitchData.cents} noteName={pitchData.note} />
) : (
<div className={`pitch-readout ${pitchData ? 'active' : ''}`}>
{pitchData ? (
<>
<span className={`detected-note ${pitchData.midi === targetMidi ? 'match' : ''}`}>
{pitchData.note}
</span>
<span className="detected-hz">{Math.round(pitchData.frequency)} Hz</span>
</>
) : (
<span className="placeholder">{listening ? "Listening..." : "Mic Off"}</span>
)}
</div> </div>
)} )}
</div> </div>
</main>
<footer className="settings-footer">
<Controls settings={settings} onUpdateSettings={setSettings} /> {
</footer> !settings.zenMode && (
</div> <div className="pitch-monitor-bar">
<button
className={`mic-button ${listening ? 'listening' : ''}`}
onClick={() => setListening(!listening)}
aria-label={listening ? "Stop Listening" : "Start Listening"}
>
{listening ? <Mic size={28} /> : <MicOff size={28} />}
</button>
{settings.showTuningMeter && pitchData ? (
<TuningMeter cents={pitchData.cents} noteName={pitchData.note} />
) : (
<div className={`pitch-readout ${pitchData ? 'active' : ''}`}>
{pitchData ? (
<>
<span className={`detected-note ${pitchData.midi === targetMidi ? 'match' : ''}`}>
{pitchData.note}
</span>
<span className="detected-hz">{Math.round(pitchData.frequency)} Hz</span>
</>
) : (
<span className="placeholder">{listening ? "Listening..." : "Mic Off"}</span>
)}
</div>
)}
</div>
)
}
</main >
{!settings.zenMode && (
<footer className="settings-footer">
<Controls settings={settings} onUpdateSettings={setSettings} />
</footer>
)
}
{
settings.zenMode && (
<button
className="exit-zen-button"
onClick={() => setSettings(s => ({ ...s, zenMode: false }))}
>
Exit Zen Mode
</button>
)
}
</div >
); );
} }

View File

@@ -22,6 +22,7 @@ export interface AppSettings {
instrument: string; instrument: string;
showTuningMeter: boolean; showTuningMeter: boolean;
rhythm: RhythmSettings; rhythm: RhythmSettings;
zenMode: boolean;
} }
interface ControlsProps { interface ControlsProps {