Add iOS audio unlock banner to play page
Shows "Tap to enable audio on iOS" banner on iOS devices. Hides automatically once audio is unlocked via button or play. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -294,6 +294,28 @@
|
|||||||
color: var(--gb-lightest);
|
color: var(--gb-lightest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* iOS audio unlock banner */
|
||||||
|
.ios-audio-banner {
|
||||||
|
margin-top: calc(var(--spacing-unit) * 2);
|
||||||
|
padding: calc(var(--spacing-unit) * 2);
|
||||||
|
border-top: 2px solid var(--gb-light);
|
||||||
|
display: none; /* toggled by JS */
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.ios-audio-banner p {
|
||||||
|
font-size: 8px;
|
||||||
|
color: var(--gb-light);
|
||||||
|
margin: 0 0 calc(var(--spacing-unit)) 0;
|
||||||
|
line-height: 2;
|
||||||
|
}
|
||||||
|
.ios-audio-banner button {
|
||||||
|
background: var(--gb-lightest);
|
||||||
|
color: var(--gb-darkest);
|
||||||
|
border: var(--pixel-border) solid var(--gb-light);
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Footer */
|
/* Footer */
|
||||||
.footer {
|
.footer {
|
||||||
margin-top: calc(var(--spacing-unit) * 4);
|
margin-top: calc(var(--spacing-unit) * 4);
|
||||||
@@ -338,6 +360,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- iOS Audio Unlock -->
|
||||||
|
<div id="iosAudioBanner" class="ios-audio-banner">
|
||||||
|
<p>Tap to enable audio on iOS</p>
|
||||||
|
<button id="enableAudioBtn" type="button">Enable Audio</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- CTA -->
|
<!-- CTA -->
|
||||||
<div class="cta-link-inline">
|
<div class="cta-link-inline">
|
||||||
<a id="generateOwn" href="/">Generate your own</a>
|
<a id="generateOwn" href="/">Generate your own</a>
|
||||||
|
|||||||
+29
-1
@@ -1,7 +1,7 @@
|
|||||||
import { MIDIService } from './services/MIDIService';
|
import { MIDIService } from './services/MIDIService';
|
||||||
import { MIDIParser } from './midi/MIDIParser';
|
import { MIDIParser } from './midi/MIDIParser';
|
||||||
import { MotifEngine } from './core/MotifEngine';
|
import { MotifEngine } from './core/MotifEngine';
|
||||||
import { unlockAudio } from './utils/audioUnlock';
|
import { unlockAudio, isAudioReady } from './utils/audioUnlock';
|
||||||
import type { NoteEvent } from './types';
|
import type { NoteEvent } from './types';
|
||||||
|
|
||||||
function qs(id: string): HTMLElement {
|
function qs(id: string): HTMLElement {
|
||||||
@@ -140,6 +140,33 @@ async function main(): Promise<void> {
|
|||||||
const currentTimeEl = qs('playCurrentTime') as HTMLElement;
|
const currentTimeEl = qs('playCurrentTime') as HTMLElement;
|
||||||
const durationEl = qs('playDuration') as HTMLElement;
|
const durationEl = qs('playDuration') as HTMLElement;
|
||||||
const timeRow = qs('playTimeRow') as HTMLElement;
|
const timeRow = qs('playTimeRow') as HTMLElement;
|
||||||
|
const iosAudioBanner = qs('iosAudioBanner') as HTMLElement;
|
||||||
|
const enableAudioBtn = qs('enableAudioBtn') as HTMLButtonElement;
|
||||||
|
|
||||||
|
// Detect iOS/Safari for audio unlock banner
|
||||||
|
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
||||||
|
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
|
||||||
|
|
||||||
|
function updateIOSBanner(): void {
|
||||||
|
if (isIOS && !isAudioReady()) {
|
||||||
|
iosAudioBanner.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
iosAudioBanner.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show banner on iOS if needed
|
||||||
|
updateIOSBanner();
|
||||||
|
|
||||||
|
// Handle enable audio button
|
||||||
|
enableAudioBtn.addEventListener('click', async () => {
|
||||||
|
try {
|
||||||
|
await unlockAudio();
|
||||||
|
updateIOSBanner();
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const { midiUrl: u, title } = buildMidiUrlFromParams();
|
const { midiUrl: u, title } = buildMidiUrlFromParams();
|
||||||
|
|
||||||
@@ -290,6 +317,7 @@ async function main(): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
// Wait for unlock to complete
|
// Wait for unlock to complete
|
||||||
await unlockPromise;
|
await unlockPromise;
|
||||||
|
updateIOSBanner(); // Hide banner once audio is unlocked
|
||||||
|
|
||||||
// First play generates the artifact (deterministically from MIDI structure).
|
// First play generates the artifact (deterministically from MIDI structure).
|
||||||
if (!isGenerated) {
|
if (!isGenerated) {
|
||||||
|
|||||||
Reference in New Issue
Block a user