// 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() { // Só salva se estiver em uma sala (contexto colaborativo/online) if (!window.ROOM_NAME) return; try { // 1. Snapshot do Áudio (inclui blobs Base64 das gravações) const audioSnap = getAudioSnapshot(); // 2. Snapshot do Pattern (XML) // Se não conseguirmos o XML exato, salvamos os dados brutos do pattern let xmlContent = null; try { // Tenta usar a função exportada (veja nota abaixo sobre file.js) if(typeof generateXmlFromStateExported === 'function') { xmlContent = generateXmlFromStateExported(); } } catch(e) { console.warn("Não foi possível gerar XML para backup:", e); } const stateToSave = { timestamp: Date.now(), xml: xmlContent, // Fallback: Salva o pattern bruto se o XML falhar patternRaw: !xmlContent ? appState.pattern : null, audio: audioSnap, global: { bpm: appState.global.bpm, // Pega do state, que deve estar syncado com o DOM compassoA: appState.global.compassoA, compassoB: appState.global.compassoB, bars: appState.global.bars, syncMode: appState.global.syncMode } }; const roomName = window.ROOM_NAME; sessionStorage.setItem(`mmp_backup_${roomName}`, JSON.stringify(stateToSave)); // console.log("Estado salvo na sessão local (F5 safe)."); } catch (e) { console.error("Falha crítica ao salvar sessão (Quota Excedida?):", 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; } }