Improve mobile UI (resizing)

This commit is contained in:
2025-12-30 20:11:08 +01:00
parent be7d994ca5
commit 7a272175d9
3 changed files with 103 additions and 38 deletions

View File

@@ -1,9 +1,11 @@
/* App.css */
.app-container {
display: flex;
flex-direction: column;
display: block;
/* Changed from flex to allow natural document flow */
min-height: 100vh;
background-color: var(--color-bg);
overflow-x: hidden;
max-width: 100vw;
}
.app-header {
@@ -38,13 +40,16 @@
}
.main-stage {
flex: 1;
/* Natural height - not flex to prevent shrinking when footer is present */
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
justify-content: flex-start;
padding: var(--spacing-md);
gap: var(--spacing-xl);
overflow-x: hidden;
max-width: 100%;
box-sizing: border-box;
}
.card {
@@ -58,6 +63,8 @@
width: 100%;
max-width: 600px;
transition: max-width 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), transform 0.2s ease;
box-sizing: border-box;
overflow-x: hidden;
}
.card.has-hint {
@@ -357,7 +364,8 @@ button {
background-color: transparent;
border: 2px solid var(--color-primary);
color: var(--color-primary);
padding: 0.5rem 1rem;
padding: 0.4rem 0.8rem;
/* Compact padding by default */
border-radius: 30px;
font-weight: 600;
cursor: pointer;
@@ -365,7 +373,8 @@ button {
align-items: center;
gap: 8px;
transition: all 0.2s;
font-size: 0.9rem;
font-size: 0.8rem;
/* Compact font size by default */
}
.hint-button:hover {
@@ -387,7 +396,8 @@ button {
background-color: transparent;
border: 2px solid var(--color-text-muted);
color: var(--color-text-muted);
padding: 0.5rem 1rem;
padding: 0.4rem 0.8rem;
/* Compact padding by default */
border-radius: 30px;
font-weight: 600;
cursor: pointer;
@@ -395,7 +405,8 @@ button {
align-items: center;
gap: 8px;
transition: all 0.2s;
font-size: 0.9rem;
font-size: 0.8rem;
/* Compact font size by default */
}
.zen-button:hover {
@@ -608,6 +619,20 @@ button {
.header-controls {
gap: 4px;
}
/* Fix action row overflow on narrow screens */
.action-row {
flex-wrap: wrap;
gap: 8px !important;
padding: 0 8px;
max-width: 100%;
box-sizing: border-box;
}
/* Reduce card padding on mobile */
.card {
padding: var(--spacing-md);
}
}
.toggle-option.active {
@@ -653,26 +678,29 @@ button {
.main-stage {
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
/* Center vertically */
justify-content: flex-start;
gap: 0;
/* Allow scrolling */
height: auto;
min-height: 100vh;
overflow-y: auto;
display: block;
height: 100vh;
overflow: hidden;
/* Prevent scroll - fit everything on one screen */
}
.card.sheet-music-card {
max-width: 100%;
width: 100%;
min-height: 100vh;
height: 100vh;
/* Fit within viewport */
max-height: 100vh;
margin: 0;
border-radius: 0;
align-items: stretch;
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
}
/* Staff Row: Horizontal Layout for Staff + Note Name */
@@ -701,10 +729,11 @@ button {
.sheet-music-container {
width: 100%;
flex: 0 0 auto;
min-height: 180px;
/* Increased from 150px */
transform: scale(1.1);
/* Scale UP */
min-height: 120px;
/* Reduced to allow more space for fretboard */
max-height: 180px;
transform: scale(1.0);
/* No scaling to prevent overflow */
transform-origin: center center;
justify-content: center;
margin-bottom: 0;
@@ -747,14 +776,17 @@ button {
transform: none;
max-width: none;
width: 100%;
flex-grow: 1;
flex: 1;
/* Take remaining space */
display: flex;
align-items: center;
justify-content: center;
background: #2a2a2a;
/* distinct bg for full width feel */
min-height: 200px;
padding: 10px 0;
min-height: 100px;
/* Reduced to fit on screen */
max-height: 50vh;
/* Limit height */
padding: 5px 0;
}
/* Hide Instruction Text ("Play note above") */
@@ -784,32 +816,33 @@ button {
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
/* Push settings way down so they don't interfere with main view */
/* Hide settings footer in landscape - accessible via settings modal */
.settings-footer {
margin-top: 50px;
padding-bottom: 50px;
display: none;
}
/* Make main stage scrollable to reach settings */
/* App container in landscape */
.app-container {
overflow-y: auto;
overflow: hidden;
height: 100vh;
display: block;
/* Break flex height lock */
}
.action-row {
margin-top: 20px !important;
margin-top: 5px !important;
width: 100%;
padding: 20px;
max-width: 100vw;
padding: 5px;
position: static;
/* Flow naturally below */
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
gap: 16px;
gap: 8px;
background: var(--color-bg);
pointer-events: auto;
box-sizing: border-box;
flex-shrink: 0;
/* Don't shrink */
}
.action-row button {

View File

@@ -75,6 +75,15 @@ function App() {
const [hoveredMidi, setHoveredMidi] = useState<number | null>(null);
const feedbackTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const [isFullscreen, setIsFullscreen] = useState(false);
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => {
setWindowWidth(window.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
useEffect(() => {
const handleFullscreenChange = () => {
@@ -485,7 +494,7 @@ function App() {
keySignature={settings.keySignature}
clef={activeClef}
transpose={activeTranspose}
width={Math.min(window.innerWidth - 40, 500)}
width={Math.min(windowWidth - 40, 500)}
height={activeClef === 'grand' ? 260 : 180}
hideTargetNote={settings.gameMode === 'ear_training' && !revealed}
hoverMidi={settings.showHint ? hoveredMidi : null}
@@ -708,6 +717,7 @@ function App() {
audioLevel={audioLevel}
debugInfo={debugInfo}
isListening={isListening}
onMicToggle={() => setListening(l => !l)}
/>
<OpenSourceModal

View File

@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { X, Volume2, VolumeX, ChevronDown, ChevronUp } from 'lucide-react';
import { X, Volume2, VolumeX, ChevronDown, ChevronUp, Mic, MicOff } from 'lucide-react';
import type { AppSettings } from './Controls';
import type { MicrophoneDebugInfo } from '../hooks/usePitchDetector';
@@ -11,6 +11,7 @@ interface SettingsModalProps {
audioLevel?: number;
debugInfo?: MicrophoneDebugInfo | null;
isListening?: boolean;
onMicToggle?: () => void;
}
// Level meter component
@@ -58,7 +59,8 @@ export const SettingsModal: React.FC<SettingsModalProps> = ({
onUpdateSettings,
audioLevel = 0,
debugInfo,
isListening = false
isListening = false,
onMicToggle
}) => {
const [showDebugInfo, setShowDebugInfo] = useState(false);
@@ -166,6 +168,26 @@ export const SettingsModal: React.FC<SettingsModalProps> = ({
<hr style={{ borderColor: 'rgba(255,255,255,0.1)', margin: '24px 0' }} />
{/* Microphone Toggle */}
<div className="control-group" style={{ marginBottom: '16px' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '8px' }}>
<label className="control-label" style={{ marginBottom: 0, fontSize: '16px', display: 'flex', alignItems: 'center', gap: '8px' }}>
{isListening ? <Mic size={18} /> : <MicOff size={18} />}
<span>Microphone</span>
</label>
<button
className={`switch-button ${isListening ? 'active' : ''}`}
onClick={onMicToggle}
title={isListening ? "Turn Off Microphone" : "Turn On Microphone"}
>
<div className="switch-thumb" />
</button>
</div>
<p style={{ fontSize: '12px', opacity: 0.7, margin: 0 }}>
{isListening ? 'Microphone is active and listening for notes.' : 'Enable microphone to detect your playing.'}
</p>
</div>
{/* Mic Sensitivity */}
<div className="control-group">
<label className="control-label" style={{ marginBottom: '12px', fontSize: '16px' }}>