// js/main.js (ESM com import absoluto de socket.js + ROOM_NAME local) import { appState, resetProjectState } from "./state.js"; import { updateTransportLoop, restartAudioEditorIfPlaying, } from "./audio/audio_audio.js"; import { initializeAudioContext } from "./audio.js"; import { handleFileLoad, generateMmpFile } from "./file.js"; import { renderAll, loadAndRenderSampleBrowser, showOpenProjectModal, closeOpenProjectModal, } from "./ui.js"; import { renderAudioEditor } from "./audio/audio_ui.js"; import { adjustValue, enforceNumericInput } from "./utils.js"; import { ZOOM_LEVELS } from "./config.js"; // ⚠️ IMPORT ABSOLUTO para evitar 404/text/html quando a página estiver em /creation/ ou fora dela. // Ajuste o prefixo abaixo para o caminho real onde seus assets vivem no servidor: import { sendAction, joinRoom, setUserName } from "./socket.js"; // Descobre a sala pela URL (local ao main.js) e expõe no window para debug const ROOM_NAME = new URLSearchParams(window.location.search).get("room"); window.ROOM_NAME = ROOM_NAME; // ✅ NOVO: se tem sala na URL, entra já na sala (independe do áudio) if (ROOM_NAME) { // entra na sala para receber estado/broadcasts imediatamente joinRoom(); } // Função util para alternar estado dos botões de ferramenta function updateToolButtons() { const sliceToolBtn = document.getElementById("slice-tool-btn"); const trimToolBtn = document.getElementById("resize-tool-trim"); const stretchToolBtn = document.getElementById("resize-tool-stretch"); if (sliceToolBtn) sliceToolBtn.classList.toggle("active", appState.global.sliceToolActive); if (trimToolBtn) trimToolBtn.classList.toggle( "active", !appState.global.sliceToolActive && appState.global.resizeMode === "trim" ); if (stretchToolBtn) stretchToolBtn.classList.toggle( "active", !appState.global.sliceToolActive && appState.global.resizeMode === "stretch" ); document.body.classList.toggle( "slice-tool-active", appState.global.sliceToolActive ); } document.addEventListener("DOMContentLoaded", () => { // Botões e elementos const newProjectBtn = document.getElementById("new-project-btn"); const openMmpBtn = document.getElementById("open-mmp-btn"); const saveMmpBtn = document.getElementById("save-mmp-btn"); const uploadSampleBtn = document.getElementById("upload-sample-btn"); const addInstrumentBtn = document.getElementById("add-instrument-btn"); const removeInstrumentBtn = document.getElementById("remove-instrument-btn"); const playBtn = document.getElementById("play-btn"); const stopBtn = document.getElementById("stop-btn"); const audioEditorPlayBtn = document.getElementById("audio-editor-play-btn"); const audioEditorStopBtn = document.getElementById("audio-editor-stop-btn"); const audioEditorLoopBtn = document.getElementById("audio-editor-loop-btn"); const addAudioTrackBtn = document.getElementById("add-audio-track-btn"); const rewindBtn = document.getElementById("rewind-btn"); const metronomeBtn = document.getElementById("metronome-btn"); const sliceToolBtn = document.getElementById("slice-tool-btn"); const resizeToolTrimBtn = document.getElementById("resize-tool-trim"); const resizeToolStretchBtn = document.getElementById("resize-tool-stretch"); const createRoomBtn = document.getElementById("create-room-btn"); const mmpFileInput = document.getElementById("mmp-file-input"); const sampleFileInput = document.getElementById("sample-file-input"); const openProjectModal = document.getElementById("open-project-modal"); const openModalCloseBtn = document.getElementById("open-modal-close-btn"); const loadFromComputerBtn = document.getElementById("load-from-computer-btn"); const sidebarToggle = document.getElementById("sidebar-toggle"); const addBarBtn = document.getElementById("add-bar-btn"); const zoomInBtn = document.getElementById("zoom-in-btn"); const zoomOutBtn = document.getElementById("zoom-out-btn"); const deleteClipBtn = document.getElementById("delete-clip"); // ================================================================= // 👇 INÍCIO DA CORREÇÃO (Botão de Sincronia - Agora envia Ação) // ================================================================= const syncModeBtn = document.getElementById("sync-mode-btn"); // if (syncModeBtn) { // // Define o estado inicial (global por padrão) appState.global.syncMode = "global"; // syncModeBtn.classList.add("active"); // syncModeBtn.textContent = "Global"; // syncModeBtn.addEventListener("click", () => { // // 1. Determina qual será o *novo* modo const newMode = appState.global.syncMode === "global" ? "local" : "global"; // // 2. Envia a ação para sincronizar. O handleActionBroadcast // cuidará de atualizar o appState, o botão e mostrar o toast. sendAction({ type: "SET_SYNC_MODE", mode: newMode, }); // Lógica antiga removida daqui (movida para o handler) /* const isNowLocal = appState.global.syncMode === "global"; appState.global.syncMode = isNowLocal ? "local" : "global"; syncModeBtn.classList.toggle("active", !isNowLocal); syncModeBtn.textContent = isNowLocal ? "Local" : "Global"; showToast( `🎧 Modo de Playback: ${isNowLocal ? "Local" : "Global"}`, "info" ); */ }); // Esconde o botão se não estiver em uma sala (lógica movida do socket.js) if (!ROOM_NAME) { // //syncModeBtn.style.display = 'none'; // REMOVIDO PARA TESTE VISUAL } } // ================================================================= // 👆 FIM DA CORREÇÃO // ================================================================= // Excluir clipe if (deleteClipBtn) { deleteClipBtn.addEventListener("click", () => { initializeAudioContext(); const clipId = appState.global.selectedClipId; if (clipId) { sendAction({ type: "REMOVE_AUDIO_CLIP", clipId }); appState.global.selectedClipId = null; } const menu = document.getElementById("timeline-context-menu"); if (menu) menu.style.display = "none"; }); } // Delete/Backspace document.addEventListener("keydown", (e) => { if (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA") return; const clipId = appState.global.selectedClipId; if ((e.key === "Delete" || e.key === "Backspace") && clipId) { e.preventDefault(); sendAction({ type: "REMOVE_AUDIO_CLIP", clipId }); appState.global.selectedClipId = null; } }); // Fechar menu contexto document.addEventListener("click", (e) => { const menu = document.getElementById("timeline-context-menu"); if (menu && !e.target.closest("#timeline-context-menu")) { menu.style.display = "none"; } }); // Ações principais (broadcast) newProjectBtn?.addEventListener("click", () => { initializeAudioContext(); if ( (appState.pattern.tracks.length > 0 || appState.audio.clips.length > 0) && !confirm( "Você tem certeza? Isso irá resetar o projeto para TODOS na sala." ) ) return; sendAction({ type: "RESET_PROJECT" }); }); addBarBtn?.addEventListener("click", () => { const barsInput = document.getElementById("bars-input"); if (barsInput) { adjustValue(barsInput, 1); barsInput.dispatchEvent(new Event("change", { bubbles: true })); } }); openMmpBtn?.addEventListener("click", showOpenProjectModal); loadFromComputerBtn?.addEventListener("click", () => mmpFileInput?.click()); mmpFileInput?.addEventListener("change", (event) => { const file = event.target.files[0]; if (file) handleFileLoad(file).then(() => closeOpenProjectModal()); }); uploadSampleBtn?.addEventListener("click", () => sampleFileInput?.click()); saveMmpBtn?.addEventListener("click", generateMmpFile); addInstrumentBtn?.addEventListener("click", () => { initializeAudioContext(); sendAction({ type: "ADD_TRACK" }); }); removeInstrumentBtn?.addEventListener("click", () => { initializeAudioContext(); sendAction({ type: "REMOVE_LAST_TRACK" }); }); playBtn?.addEventListener("click", () => { initializeAudioContext(); sendAction({ type: "TOGGLE_PLAYBACK" }); }); stopBtn?.addEventListener("click", () => { initializeAudioContext(); sendAction({ type: "STOP_PLAYBACK" }); }); rewindBtn?.addEventListener("click", () => { initializeAudioContext(); sendAction({ type: "REWIND_PLAYBACK" }); }); metronomeBtn?.addEventListener("click", () => { initializeAudioContext(); appState.global.metronomeEnabled = !appState.global.metronomeEnabled; metronomeBtn.classList.toggle("active", appState.global.metronomeEnabled); }); // Ferramentas locais if (sliceToolBtn) { sliceToolBtn.addEventListener("click", () => { appState.global.sliceToolActive = !appState.global.sliceToolActive; updateToolButtons(); }); } if (resizeToolTrimBtn) { resizeToolTrimBtn.addEventListener("click", () => { appState.global.resizeMode = "trim"; appState.global.sliceToolActive = false; updateToolButtons(); }); } if (resizeToolStretchBtn) { resizeToolStretchBtn.addEventListener("click", () => { appState.global.resizeMode = "stretch"; appState.global.sliceToolActive = false; updateToolButtons(); }); } openModalCloseBtn?.addEventListener("click", closeOpenProjectModal); sidebarToggle?.addEventListener("click", () => { document.body.classList.toggle("sidebar-hidden"); const icon = sidebarToggle.querySelector("i"); if (icon) { icon.className = document.body.classList.contains("sidebar-hidden") ? "fa-solid fa-caret-right" : "fa-solid fa-caret-left"; } }); const inputs = document.querySelectorAll(".value-input"); inputs.forEach((input) => { input.addEventListener("input", (event) => { enforceNumericInput(event); if ( appState.global.isPlaying && (event.target.id.startsWith("compasso-") || event.target.id === "bars-input") ) { sendAction({ type: "STOP_PLAYBACK" }); } }); input.addEventListener("change", (event) => { const target = event.target; if (target.id === "bpm-input") { sendAction({ type: "SET_BPM", value: target.value }); } else if (target.id === "bars-input") { sendAction({ type: "SET_BARS", value: target.value }); } else if (target.id === "compasso-a-input") { sendAction({ type: "SET_TIMESIG_A", value: target.value }); } else if (target.id === "compasso-b-input") { sendAction({ type: "SET_TIMESIG_B", value: target.value }); } }); input.addEventListener("wheel", (event) => { event.preventDefault(); const step = event.deltaY < 0 ? 1 : -1; adjustValue(event.target, step); event.target.dispatchEvent(new Event("change", { bubbles: true })); }); }); const buttons = document.querySelectorAll(".adjust-btn"); buttons.forEach((button) => { button.addEventListener("click", () => { const targetId = button.dataset.target + "-input"; const targetInput = document.getElementById(targetId); const step = parseInt(button.dataset.step, 10) || 1; if (targetInput) { adjustValue(targetInput, step); targetInput.dispatchEvent(new Event("change", { bubbles: true })); } }); }); // Zoom local zoomInBtn?.addEventListener("click", () => { if (appState.global.zoomLevelIndex < ZOOM_LEVELS.length - 1) { appState.global.zoomLevelIndex++; renderAll(); } }); zoomOutBtn?.addEventListener("click", () => { if (appState.global.zoomLevelIndex > 0) { appState.global.zoomLevelIndex--; renderAll(); } }); // Editor de Áudio audioEditorPlayBtn?.addEventListener("click", () => { initializeAudioContext(); if (appState.global.isAudioEditorPlaying) { sendAction({ type: "STOP_AUDIO_PLAYBACK", rewind: false }); } else { sendAction({ type: "START_AUDIO_PLAYBACK", seekTime: appState.audio.audioEditorSeekTime, // Corrigido loopState: { isLoopActive: appState.global.isLoopActive, loopStartTime: appState.global.loopStartTime, loopEndTime: appState.global.loopEndTime, }, }); } }); audioEditorStopBtn?.addEventListener("click", () => { initializeAudioContext(); sendAction({ type: "STOP_AUDIO_PLAYBACK", rewind: true }); }); // Loop Button (agora envia ação) audioEditorLoopBtn?.addEventListener("click", () => { initializeAudioContext(); // Garante contexto const newLoopState = !appState.global.isLoopActive; sendAction({ type: "SET_LOOP_STATE", isLoopActive: newLoopState, loopStartTime: appState.global.loopStartTime, loopEndTime: appState.global.loopEndTime, }); }); if (addAudioTrackBtn) { addAudioTrackBtn.addEventListener("click", () => { initializeAudioContext(); sendAction({ type: "ADD_AUDIO_LANE" }); }); } // Navegador de Samples (local) loadAndRenderSampleBrowser(); const browserContent = document.getElementById("browser-content"); if (browserContent) { browserContent.addEventListener("click", function (event) { const folderName = event.target.closest(".folder-name"); if (folderName) { const folderItem = folderName.parentElement; folderItem.classList.toggle("open"); } }); } // Criar sala (gera link com ?room=...) if (createRoomBtn) { createRoomBtn.addEventListener("click", () => { initializeAudioContext(); const currentParams = new URLSearchParams(window.location.search); if (currentParams.has("room")) { alert( `Você já está na sala: ${currentParams.get( "room" )}\n\nCopie o link da barra de endereços para convidar.` ); return; } const defaultName = `sessao-${Math.random() .toString(36) .substring(2, 7)}`; const roomName = prompt( "Digite um nome para a sala compartilhada:", defaultName ); if (!roomName) return; const currentUrl = window.location.origin + window.location.pathname; const shareableLink = `${currentUrl}?room=${encodeURIComponent( roomName )}`; try { navigator.clipboard.writeText(shareableLink); alert( `Link da sala copiado para a área de transferência!\n\n${shareableLink}\n\nA página será recarregada agora para entrar na nova sala.` ); } catch (err) { alert( `Link da sala: ${shareableLink}\n\nA página será recarregada agora para entrar na nova sala.` ); } window.location.href = shareableLink; }); } // Modal “destravar áudio” + entrar na sala const audioUnlockModal = document.getElementById("audio-unlock-modal"); const audioUnlockBtn = document.getElementById("audio-unlock-btn"); if (ROOM_NAME && audioUnlockModal && audioUnlockBtn) { audioUnlockModal.style.display = "flex"; audioUnlockBtn.addEventListener("click", () => { const userName = prompt( "Qual o seu nome?", `Alicer-${Math.floor(Math.random() * 999)}` ); if (!userName) return; setUserName(userName); initializeAudioContext(); // joinRoom() já foi chamado no início se ROOM_NAME existe audioUnlockModal.style.display = "none"; }); } else { console.log("Modo local. Áudio será iniciado no primeiro clique."); // Comentado para permitir teste visual // if (syncModeBtn) syncModeBtn.style.display = "none"; } renderAll(); updateToolButtons(); });