diff --git a/models.html b/models.html index 9b9c807..3dffbe8 100644 --- a/models.html +++ b/models.html @@ -130,13 +130,43 @@ .model-row { display:flex; align-items:center; gap: 10px; flex-wrap: wrap; } .model-row label { color: rgba(255,255,255,0.7); font-size: 12px; } - select { - background: rgba(0,0,0,0.28); - border: 1px solid rgba(255,255,255,0.12); - color: rgba(255,255,255,0.9); + .model-toggle { + display: flex; + gap: 8px; + flex-wrap: wrap; + align-items: center; + } + .model-btn { + display: inline-flex; + align-items: center; + gap: 8px; padding: 10px 12px; - border-radius: 10px; - font-family: inherit; + border-radius: 999px; + border: 1px solid rgba(255,255,255,0.14); + background: rgba(0,0,0,0.18); + color: rgba(255,255,255,0.86); + font-size: 12px; + line-height: 1; + user-select: none; + } + .model-btn .dot { + width: 7px; + height: 7px; + border-radius: 999px; + background: rgba(255,255,255,0.35); + } + .model-btn:hover { border-color: rgba(255,255,255,0.22); box-shadow: 0 14px 40px rgba(0,0,0,0.35); } + .model-btn[aria-pressed="true"] { + border-color: rgba(0,255,136,0.35); + background: linear-gradient(90deg, rgba(0,255,136,0.16), rgba(255,60,245,0.10)); + } + .model-btn[aria-pressed="true"] .dot { + background: rgba(0,255,136,0.85); + box-shadow: 0 0 14px rgba(0,255,136,0.22); + } + .model-btn:focus-visible { + outline: none; + box-shadow: 0 0 0 3px rgba(0,255,136,0.25), 0 14px 40px rgba(0,0,0,0.35); } .ios-audio-banner { @@ -219,13 +249,22 @@
- - - Square/triangle/noise vibe, tight envelope. + +
+ + + +
+ Chip: square/triangle/saw flavor, tight envelope, crisp attacks.
diff --git a/src/models.ts b/src/models.ts index c6ba628..dc21925 100644 --- a/src/models.ts +++ b/src/models.ts @@ -36,8 +36,9 @@ class ModelsApp { private motifStopBtn!: HTMLButtonElement; private motifVolumeSlider!: HTMLInputElement; - private modelSelect!: HTMLSelectElement; private modelHint!: HTMLElement; + private currentModel: SynthModel = 'nes_gb'; + private modelButtons: HTMLButtonElement[] = []; private iosAudioBanner!: HTMLElement; private enableAudioBtn!: HTMLButtonElement; @@ -86,8 +87,12 @@ class ModelsApp { this.motifStopBtn = document.getElementById('motifStopBtn') as HTMLButtonElement; this.motifVolumeSlider = document.getElementById('motifVolume') as HTMLInputElement; - this.modelSelect = document.getElementById('modelSelect') as HTMLSelectElement; this.modelHint = document.getElementById('modelHint')!; + this.modelButtons = [ + document.getElementById('modelBtnPre8') as HTMLButtonElement, + document.getElementById('modelBtnNesGb') as HTMLButtonElement, + document.getElementById('modelBtnSnes') as HTMLButtonElement, + ]; this.nextResultBtn = document.getElementById('nextResultBtn') as HTMLButtonElement; @@ -116,7 +121,9 @@ class ModelsApp { this.testEngine?.setVolume(volume); }); - this.modelSelect.addEventListener('change', () => this.syncModelHint()); + for (const btn of this.modelButtons) { + btn.addEventListener('click', () => this.setModel((btn.dataset.model as SynthModel) || 'nes_gb')); + } this.nextResultBtn.addEventListener('click', () => this.handleNextResult()); @@ -126,9 +133,17 @@ class ModelsApp { } private getModel(): SynthModel { - const v = (this.modelSelect.value || 'nes_gb') as SynthModel; - if (v === 'pre8bit' || v === 'nes_gb' || v === 'snes_ish') return v; - return 'nes_gb'; + return this.currentModel; + } + + private setModel(model: SynthModel): void { + if (model !== 'pre8bit' && model !== 'nes_gb' && model !== 'snes_ish') return; + this.currentModel = model; + for (const btn of this.modelButtons) { + const isOn = btn.dataset.model === model; + btn.setAttribute('aria-pressed', isOn ? 'true' : 'false'); + } + this.syncModelHint(); } private syncModelHint(): void {