document.addEventListener('DOMContentLoaded', () => { const navLinks = document.querySelectorAll('.nav-link'); const views = document.querySelectorAll('.view'); const filterSection = document.querySelector('.filter-section'); const themeSelect = document.getElementById('theme-select'); const themeLink = document.getElementById('theme-stylesheet'); const btnExport = document.getElementById('btn-export'); const btnApply = document.getElementById('btn-apply'); const btnSave = document.getElementById('btn-save'); const btnReset = document.getElementById('btn-reset'); const presetSelect = document.getElementById('preset-select'); const tuningGrid = document.getElementById('tuning-grid'); const fretsInput = document.getElementById('frets-input'); const fingersInput = document.getElementById('fingers-input'); const loading = document.getElementById('chord-loading'); const allNotes = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B']; // --- Audio --- const noteToSemitone = {C:0,'C#':1,D:2,'D#':3,E:4,F:5,'F#':6,G:7,'G#':8,A:9,'A#':10,B:11}; const standardMIDI = [40, 45, 50, 55, 59, 64]; function tuningNamesToMIDI(names) { return names.map((n, i) => { const pc = noteToSemitone[n]; if (pc === undefined) return standardMIDI[i] || 40; const std = standardMIDI[i] || 40; for (let m = std - 6; m <= std + 6; m++) { if (((m % 12) + 12) % 12 === pc) return m; } return std; }); } window.currentTuningMIDI = standardMIDI.slice(); let polySynth = null; window.playChord = function(midiNotes) { Tone.start(); if (!polySynth) { polySynth = new Tone.PolySynth(Tone.Synth, {maxPolyphony: 8}).toDestination(); polySynth.set({ oscillator: {type: 'triangle'}, envelope: {attack: 0.01, decay: 0.3, sustain: 0.4, release: 1.0} }); } polySynth.releaseAll(); const now = Tone.now(); midiNotes.forEach((m, i) => { const freq = 440 * Math.pow(2, (m - 69) / 12); polySynth.triggerAttackRelease(freq, '1.5s', now + i * 0.03); }); }; const presets = { 'Standard': ['E','A','D','G','B','E'], 'Drop D': ['D','A','D','G','B','E'], 'DADGAD': ['D','A','D','G','A','D'], 'Open G': ['D','G','B','D','G','B'], 'Open D': ['D','A','D','F#','A','D'], 'Open C': ['C','G','C','G','C','E'], 'Half Step Down': ['D#','G#','C#','F#','A#','D#'], 'Full Step Down': ['D','G','C','F','A','D'], 'Custom': null }; const shapesSection = document.getElementById('shapes-section'); let currentConfig = null; let chordsLoaded = false; let shapesInited = false; // --- Navigation --- navLinks.forEach(link => { link.addEventListener('click', () => { const target = link.dataset.view; navLinks.forEach(l => l.classList.remove('active')); views.forEach(v => v.classList.remove('active')); link.classList.add('active'); document.getElementById('view-' + target).classList.add('active'); filterSection.style.display = target === 'chords' ? '' : 'none'; shapesSection.style.display = target === 'shapes' ? '' : 'none'; if (target === 'chords' && !chordsLoaded) { loadChords(); } if (target === 'shapes' && !shapesInited) { shapesInited = true; if (window.initShapeExplorer) window.initShapeExplorer(); } }); }); // --- Theme --- themeSelect.addEventListener('change', () => { themeLink.href = 'chords-' + themeSelect.value + '.css'; }); // --- PDF Export --- btnExport.addEventListener('click', () => window.print()); // --- Config Panel --- function populatePresets() { presetSelect.innerHTML = ''; for (const name of Object.keys(presets)) { const opt = document.createElement('option'); opt.value = name; opt.textContent = name; presetSelect.appendChild(opt); } } function buildTuningGrid(tuning) { tuningGrid.innerHTML = ''; tuning.forEach((note, i) => { const sel = document.createElement('select'); allNotes.forEach(n => { const opt = document.createElement('option'); opt.value = n; opt.textContent = n; if (n === note) opt.selected = true; sel.appendChild(opt); }); sel.addEventListener('change', () => { presetSelect.value = 'Custom'; }); tuningGrid.appendChild(sel); }); } function readTuningFromGrid() { return Array.from(tuningGrid.querySelectorAll('select')).map(s => s.value); } function syncConfigPanel(cfg) { fretsInput.value = cfg.frets; fingersInput.value = cfg.max_fingers; buildTuningGrid(cfg.tuning); let matched = false; for (const [name, notes] of Object.entries(presets)) { if (notes && notes.length === cfg.tuning.length && notes.every((n, i) => n === cfg.tuning[i])) { presetSelect.value = name; matched = true; break; } } if (!matched) presetSelect.value = 'Custom'; } presetSelect.addEventListener('change', () => { const notes = presets[presetSelect.value]; if (notes) { buildTuningGrid(notes); } }); btnApply.addEventListener('click', () => { if (!window.go) return; const cfg = { instrument: currentConfig ? currentConfig.instrument : 'guitar', tuning: readTuningFromGrid(), frets: parseInt(fretsInput.value) || 4, max_fingers: parseInt(fingersInput.value) || 4 }; loading.style.display = ''; loading.textContent = 'Regenerating chords...'; window.go.main.App.UpdateConfig(cfg).then(chords => { currentConfig = cfg; window.currentTuningMIDI = tuningNamesToMIDI(cfg.tuning); loading.style.display = 'none'; chordsLoaded = true; if (window.buildChordCards) { window.buildChordCards(chords || [], cfg.frets, cfg.tuning.length); } }).catch(err => { loading.textContent = 'Error: ' + err; }); }); btnSave.addEventListener('click', () => { if (!window.go) return; window.go.main.App.SaveConfig().then(() => { btnSave.textContent = 'Saved'; setTimeout(() => { btnSave.textContent = 'Save'; }, 1500); }).catch(err => { btnSave.textContent = 'Error'; setTimeout(() => { btnSave.textContent = 'Save'; }, 1500); }); }); btnReset.addEventListener('click', () => { if (!window.go) return; window.go.main.App.ResetConfig().then(cfg => { currentConfig = cfg; window.currentTuningMIDI = tuningNamesToMIDI(cfg.tuning); syncConfigPanel(cfg); loadChords(); }); }); // --- Load Chords --- function loadChords() { if (!window.go) { loading.textContent = 'Wails runtime not available.'; return; } loading.style.display = ''; loading.textContent = 'Loading chord fingerings...'; window.go.main.App.FindChordFingerings().then(chords => { loading.style.display = 'none'; chordsLoaded = true; if (window.buildChordCards) { window.buildChordCards(chords || [], currentConfig.frets, currentConfig.tuning.length); } }); } // --- Init --- populatePresets(); if (window.go && window.go.main && window.go.main.App) { window.go.main.App.GetConfig().then(cfg => { currentConfig = cfg; window.currentTuningMIDI = tuningNamesToMIDI(cfg.tuning); syncConfigPanel(cfg); }); } else { loading.textContent = 'Wails runtime not available.'; } });