Files
audio-ui/lib/utils/browser-compat.ts
Sebastian Krüger 908e6caaf8 feat: enhance mobile responsiveness with collapsible controls and automation/effects bars
Added comprehensive mobile support for Phase 15 (Polish & Optimization):

**Mobile Layout Enhancements:**
- Track controls now collapsible on mobile with two states:
  - Collapsed: minimal controls with expand chevron, R/M/S buttons, horizontal level meter
  - Expanded: full height fader, pan control, all buttons
- Track collapse buttons added to mobile view (left chevron for track collapse, right chevron for control collapse)
- Master controls collapse button hidden on desktop (lg:hidden)
- Automation and effects bars now available on mobile layout
- Both bars collapsible with eye/eye-off icons, horizontally scrollable when zoomed
- Mobile vertical stacking: controls → waveform → automation → effects per track

**Bug Fixes:**
- Fixed track controls and waveform container height matching on desktop
- Fixed Modal component prop: isOpen → open in all dialog components
- Fixed TypeScript null check for audioBuffer.duration
- Fixed keyboard shortcut category: 'help' → 'view'

**Technical Improvements:**
- Consistent height calculation using trackHeight variable
- Proper responsive breakpoints with Tailwind (sm:640px, lg:1024px)
- Progressive disclosure pattern for mobile controls

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 20:50:44 +01:00

134 lines
3.5 KiB
TypeScript

/**
* Browser compatibility checking utilities
*/
export interface BrowserCompatibility {
isSupported: boolean;
missingFeatures: string[];
warnings: string[];
}
/**
* Check if all required browser features are supported
*/
export function checkBrowserCompatibility(): BrowserCompatibility {
const missingFeatures: string[] = [];
const warnings: string[] = [];
// Check if running in browser
if (typeof window === 'undefined') {
return {
isSupported: true,
missingFeatures: [],
warnings: [],
};
}
// Check Web Audio API
if (!window.AudioContext && !(window as any).webkitAudioContext) {
missingFeatures.push('Web Audio API');
}
// Check IndexedDB
if (!window.indexedDB) {
missingFeatures.push('IndexedDB');
}
// Check localStorage
try {
localStorage.setItem('test', 'test');
localStorage.removeItem('test');
} catch (e) {
missingFeatures.push('LocalStorage');
}
// Check Canvas API
const canvas = document.createElement('canvas');
if (!canvas.getContext || !canvas.getContext('2d')) {
missingFeatures.push('Canvas API');
}
// Check MediaDevices API (for recording)
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
warnings.push('Microphone recording not supported (requires HTTPS or localhost)');
}
// Check File API
if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {
missingFeatures.push('File API');
}
// Check AudioWorklet support (optional)
if (window.AudioContext && !AudioContext.prototype.hasOwnProperty('audioWorklet')) {
warnings.push('AudioWorklet not supported (some features may have higher latency)');
}
// Check OfflineAudioContext
if (!window.OfflineAudioContext && !(window as any).webkitOfflineAudioContext) {
missingFeatures.push('OfflineAudioContext (required for audio processing)');
}
return {
isSupported: missingFeatures.length === 0,
missingFeatures,
warnings,
};
}
/**
* Get user-friendly browser name
*/
export function getBrowserInfo(): { name: string; version: string } {
// Check if running in browser
if (typeof window === 'undefined' || typeof navigator === 'undefined') {
return { name: 'Unknown', version: 'Unknown' };
}
const userAgent = navigator.userAgent;
let name = 'Unknown';
let version = 'Unknown';
if (userAgent.indexOf('Chrome') > -1 && userAgent.indexOf('Edg') === -1) {
name = 'Chrome';
const match = userAgent.match(/Chrome\/(\d+)/);
version = match ? match[1] : 'Unknown';
} else if (userAgent.indexOf('Edg') > -1) {
name = 'Edge';
const match = userAgent.match(/Edg\/(\d+)/);
version = match ? match[1] : 'Unknown';
} else if (userAgent.indexOf('Firefox') > -1) {
name = 'Firefox';
const match = userAgent.match(/Firefox\/(\d+)/);
version = match ? match[1] : 'Unknown';
} else if (userAgent.indexOf('Safari') > -1 && userAgent.indexOf('Chrome') === -1) {
name = 'Safari';
const match = userAgent.match(/Version\/(\d+)/);
version = match ? match[1] : 'Unknown';
}
return { name, version };
}
/**
* Check if browser version meets minimum requirements
*/
export function checkMinimumVersion(): boolean {
const { name, version } = getBrowserInfo();
const versionNum = parseInt(version, 10);
const minimumVersions: Record<string, number> = {
Chrome: 90,
Edge: 90,
Firefox: 88,
Safari: 14,
};
const minVersion = minimumVersions[name];
if (!minVersion) {
// Unknown browser, assume it's ok
return true;
}
return versionNum >= minVersion;
}