// js/state.js import { initializePatternState } from "./pattern/pattern_state.js"; import { audioState, initializeAudioState, getAudioSnapshot, applyAudioSnapshot } from "./audio/audio_state.js"; import { DEFAULT_VOLUME, DEFAULT_PAN } from "./config.js"; import { generateMmpFile } from "./file.js"; // Vamos usar uma adaptação disto ou criar um helper import { parseMmpContent } from "./file.js"; // Importante para restaurar o XML // Estado global da aplicação const globalState = { sliceToolActive: false, isPlaying: false, isAudioEditorPlaying: false, playbackIntervalId: null, currentStep: 0, metronomeEnabled: false, originalXmlDoc: null, currentBeatBasslineName: "Novo Projeto", masterVolume: DEFAULT_VOLUME, masterPan: DEFAULT_PAN, zoomLevelIndex: 2, isLoopActive: false, loopStartTime: 0, loopEndTime: 8, resizeMode: "trim", selectedClipId: null, isRecording: false, clipboard: null, lastRulerClickTime: 0, justReset: false, // Configs Globais que devem persistir bpm: 140, compassoA: 4, compassoB: 4, bars: 1, syncMode: "global" // Adicionado para persistir a escolha }; const patternState = { tracks: [], activeTrackId: null, activePatternIndex: 0, }; // Combina todos os estados em um único objeto namespaced export let appState = { global: globalState, pattern: patternState, audio: audioState, }; window.appState = appState; export function resetProjectState() { console.log("Executando resetProjectState completo..."); Object.assign(appState.global, { sliceToolActive: false, isPlaying: false, isAudioEditorPlaying: false, playbackIntervalId: null, currentStep: 0, metronomeEnabled: false, originalXmlDoc: null, currentBeatBasslineName: "Novo Projeto", masterVolume: DEFAULT_VOLUME, masterPan: DEFAULT_PAN, zoomLevelIndex: 2, isLoopActive: false, loopStartTime: 0, loopEndTime: 8, resizeMode: "trim", selectedClipId: null, isRecording: false, clipboard: null, lastRulerClickTime: 0, justReset: false, }); Object.assign(appState.pattern, { tracks: [], activeTrackId: null, activePatternIndex: 0, }); initializeAudioState(); } // --- NOVA IMPLEMENTAÇÃO DE PERSISTÊNCIA --- /** * Helper: Gera a string XML do estado atual. * Precisamos "emprestar" a lógica do file.js sem baixar o arquivo. * A maneira mais limpa é importar generateXmlFromState de file.js, * mas como ela não é exportada lá, vamos usar uma estratégia via DOM * ou mover a lógica. Para simplificar sem quebrar o file.js, * vamos salvar apenas o "essencial" do patternState se o XML falhar, * mas o ideal é garantir que o XML seja gerado. * * NOTA: Para que isso funcione perfeitamente, certifique-se de exportar * 'generateXmlFromState' no seu file.js ou usar a lógica abaixo. */ import { generateXmlFromStateExported } from "./file.js"; // Vamos assumir que você vai exportar isso no file.js export function saveStateToSession() { if (!window.ROOM_NAME) return; // 1. Crie uma versão "limpa" dos tracks (PATTERN) const cleanTracks = appState.pattern.tracks.map((track) => { return { id: track.id, name: track.name, samplePath: track.samplePath, patterns: track.patterns, activePatternIndex: track.activePatternIndex, volume: track.volume, pan: track.pan, instrumentName: track.instrumentName, instrumentXml: track.instrumentXml, }; }); // 2. Construa o objeto de estado final para salvar const stateToSave = { pattern: { ...appState.pattern, tracks: cleanTracks, }, // 🔥 FIX: Salvando o estado de Áudio (Tracks e Clips) audio: { tracks: appState.audio.tracks || [], clips: appState.audio.clips || [], // Adicione outras props de áudio se necessário (ex: seekTime, etc) }, global: { bpm: document.getElementById("bpm-input").value, compassoA: document.getElementById("compasso-a-input").value, compassoB: document.getElementById("compasso-b-input").value, bars: document.getElementById("bars-input").value, syncMode: appState.global.syncMode, // É bom salvar isso também }, }; try { const roomName = window.ROOM_NAME || "default_room"; sessionStorage.setItem( `temp_state_${roomName}`, JSON.stringify(stateToSave) ); } catch (e) { console.error("Falha ao salvar estado na sessão:", e); } } export async function loadStateFromSession() { const roomName = window.ROOM_NAME; if (!roomName) return false; try { const raw = sessionStorage.getItem(`mmp_backup_${roomName}`); if (!raw) return false; const backup = JSON.parse(raw); console.log(`[Session] Recuperando backup de ${new Date(backup.timestamp).toLocaleTimeString()}`); // 1. Restaura Globais if (backup.global) { appState.global.bpm = backup.global.bpm; appState.global.compassoA = backup.global.compassoA; appState.global.compassoB = backup.global.compassoB; appState.global.bars = backup.global.bars; appState.global.syncMode = backup.global.syncMode || "global"; // Atualiza DOM imediatamente para evitar desincronia visual const bpmInput = document.getElementById("bpm-input"); if(bpmInput) bpmInput.value = backup.global.bpm; // ... (outros inputs se necessário, mas o renderAll cuida da maioria) } // 2. Restaura Pattern (XML é preferível) if (backup.xml) { await parseMmpContent(backup.xml); } else if (backup.patternRaw) { // Fallback simples (menos robusto que XML) console.warn("Restaurando de Pattern Raw (backup antigo/incompleto)"); Object.assign(appState.pattern, backup.patternRaw); } // 3. Restaura Áudio (Clipes + Gravações) if (backup.audio) { await applyAudioSnapshot(backup.audio); } return true; // Sucesso } catch (e) { console.error("Erro ao restaurar sessão:", e); return false; } }