sync mode on
This commit is contained in:
1 parent
1818932244
commit
1174b328e5
415 files changed
+44294
-1182
No files matched your search
+368
-187
@@ -1,43 +1,65 @@
|
||||
// js/main.js
|
||||
// js/main.js (ESM com import absoluto de socket.js + ROOM_NAME local)
|
||||
|
||||
import { appState, resetProjectState } from "./state.js";
|
||||
import { addTrackToState, removeLastTrackFromState } from "./pattern/pattern_state.js";
|
||||
// --- CORREÇÃO AQUI ---
|
||||
import { toggleRecording } from "./recording.js";
|
||||
import { addAudioTrackLane, removeAudioClip } from "./audio/audio_state.js";
|
||||
import { updateTransportLoop } from "./audio/audio_audio.js";
|
||||
import {
|
||||
togglePlayback,
|
||||
stopPlayback,
|
||||
rewindPlayback,
|
||||
} from "./pattern/pattern_audio.js";
|
||||
import {
|
||||
startAudioEditorPlayback,
|
||||
stopAudioEditorPlayback,
|
||||
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 {
|
||||
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";
|
||||
|
||||
// --- NOVA FUNÇÃO ---
|
||||
// Atualiza a aparência dos botões de ferramenta
|
||||
// ⚠️ 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");
|
||||
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');
|
||||
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"
|
||||
);
|
||||
|
||||
// Desativa a ferramenta de corte se outra for selecionada
|
||||
document.body.classList.toggle("slice-tool-active", appState.global.sliceToolActive);
|
||||
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");
|
||||
@@ -53,12 +75,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const rewindBtn = document.getElementById("rewind-btn");
|
||||
const metronomeBtn = document.getElementById("metronome-btn");
|
||||
const sliceToolBtn = document.getElementById("slice-tool-btn");
|
||||
|
||||
// --- NOVOS BOTÕES ---
|
||||
const resizeToolTrimBtn = document.getElementById("resize-tool-trim");
|
||||
const resizeToolStretchBtn = document.getElementById("resize-tool-stretch");
|
||||
const recordBtn = document.getElementById("record-btn");
|
||||
|
||||
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");
|
||||
@@ -68,192 +87,354 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
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");
|
||||
|
||||
// --- LISTENERS ADICIONADOS (COM LÓGICA DE CONTROLLER) ---
|
||||
|
||||
// --- NOVO LISTENER PARA O BOTÃO DE GRAVAR ---
|
||||
if (recordBtn) {
|
||||
recordBtn.addEventListener("click", () => {
|
||||
// Garante que o contexto de áudio foi iniciado por um gesto do usuário
|
||||
initializeAudioContext();
|
||||
toggleRecording();
|
||||
// =================================================================
|
||||
// 👇 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,
|
||||
});
|
||||
}
|
||||
|
||||
// Listener para o botão "Excluir Clipe" no menu de contexto
|
||||
const deleteClipBtn = document.getElementById('delete-clip');
|
||||
if (deleteClipBtn) {
|
||||
deleteClipBtn.addEventListener('click', () => {
|
||||
const clipId = appState.global.selectedClipId; // 1. Lê o estado
|
||||
if (clipId) {
|
||||
if (removeAudioClip(clipId)) { // 2. Chama a função de state
|
||||
appState.global.selectedClipId = null; // 3. Atualiza o estado global
|
||||
renderAudioEditor(); // 4. Renderiza a mudança
|
||||
}
|
||||
}
|
||||
// Esconde o menu
|
||||
const menu = document.getElementById('timeline-context-menu');
|
||||
if (menu) menu.style.display = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
// Listener global para a tecla Delete/Backspace
|
||||
document.addEventListener('keydown', (e) => {
|
||||
// Ignora se estiver digitando em um input
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
|
||||
return;
|
||||
}
|
||||
|
||||
const clipId = appState.global.selectedClipId; // 1. Lê o estado
|
||||
// Verifica se há um clipe selecionado e a tecla pressionada é Delete ou Backspace
|
||||
if ((e.key === 'Delete' || e.key === 'Backspace') && clipId) {
|
||||
e.preventDefault(); // Impede o navegador de voltar a página (ação do Backspace)
|
||||
|
||||
if (removeAudioClip(clipId)) { // 2. Chama a função de state
|
||||
appState.global.selectedClipId = null; // 3. Atualiza o estado global
|
||||
renderAudioEditor(); // 4. Renderiza a mudança
|
||||
}
|
||||
}
|
||||
// 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" );
|
||||
*/
|
||||
});
|
||||
|
||||
// Listener global para fechar menu de contexto ou desselecionar clipe
|
||||
document.addEventListener('click', (e) => {
|
||||
// Esconde o menu de contexto se clicar fora dele
|
||||
const menu = document.getElementById('timeline-context-menu');
|
||||
if (menu && !e.target.closest('#timeline-context-menu')) {
|
||||
menu.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// --- FIM DOS LISTENERS ADICIONADOS ---
|
||||
// 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
|
||||
// =================================================================
|
||||
|
||||
newProjectBtn.addEventListener("click", () => {
|
||||
if ((appState.pattern.tracks.length > 0 || appState.audio.clips.length > 0) && !confirm("Você tem certeza? Alterações não salvas serão perdidas.")) return;
|
||||
resetProjectState();
|
||||
document.getElementById('bpm-input').value = 140;
|
||||
document.getElementById('bars-input').value = 1;
|
||||
document.getElementById('compasso-a-input').value = 4;
|
||||
document.getElementById('compasso-b-input').value = 4;
|
||||
const titleElement = document.getElementById('beat-bassline-title');
|
||||
if(titleElement) titleElement.textContent = 'Novo Projeto';
|
||||
renderAll();
|
||||
// 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;
|
||||
}
|
||||
});
|
||||
|
||||
addBarBtn.addEventListener("click", () => {
|
||||
// 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);
|
||||
});
|
||||
|
||||
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", addTrackToState);
|
||||
removeInstrumentBtn.addEventListener("click", removeLastTrackFromState);
|
||||
playBtn.addEventListener("click", togglePlayback);
|
||||
stopBtn.addEventListener("click", stopPlayback);
|
||||
rewindBtn.addEventListener("click", rewindPlayback);
|
||||
metronomeBtn.addEventListener("click", () => { initializeAudioContext(); appState.global.metronomeEnabled = !appState.global.metronomeEnabled; metronomeBtn.classList.toggle("active", appState.global.metronomeEnabled); });
|
||||
|
||||
// --- LISTENERS DE FERRAMENTAS ATUALIZADOS ---
|
||||
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";
|
||||
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')) { stopPlayback(); }
|
||||
if (event.target.id.startsWith("compasso-") || event.target.id === 'bars-input' || event.target.id === 'bpm-input') { renderAll(); }
|
||||
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 }));
|
||||
});
|
||||
input.addEventListener("wheel", (event) => { event.preventDefault(); const step = event.deltaY < 0 ? 1 : -1; adjustValue(event.target, step); });
|
||||
});
|
||||
|
||||
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); } }); });
|
||||
|
||||
if (zoomInBtn) {
|
||||
zoomInBtn.addEventListener("click", () => {
|
||||
if (appState.global.zoomLevelIndex < ZOOM_LEVELS.length - 1) {
|
||||
appState.global.zoomLevelIndex++;
|
||||
renderAll();
|
||||
}
|
||||
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 }));
|
||||
}
|
||||
});
|
||||
}
|
||||
if (zoomOutBtn) {
|
||||
zoomOutBtn.addEventListener("click", () => {
|
||||
if (appState.global.zoomLevelIndex > 0) {
|
||||
appState.global.zoomLevelIndex--;
|
||||
renderAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
audioEditorPlayBtn.addEventListener("click", () => {
|
||||
if (appState.global.isAudioEditorPlaying) {
|
||||
stopAudioEditorPlayback(false); // Pausa
|
||||
} else {
|
||||
startAudioEditorPlayback();
|
||||
}
|
||||
});
|
||||
audioEditorStopBtn.addEventListener("click", () => stopAudioEditorPlayback(true)); // Stop (rebobina)
|
||||
|
||||
audioEditorLoopBtn.addEventListener("click", () => {
|
||||
appState.global.isLoopActive = !appState.global.isLoopActive;
|
||||
appState.audio.isAudioEditorLoopEnabled = appState.global.isLoopActive;
|
||||
audioEditorLoopBtn.classList.toggle("active", appState.global.isLoopActive);
|
||||
updateTransportLoop();
|
||||
const loopArea = document.getElementById("loop-region");
|
||||
if (loopArea) {
|
||||
loopArea.classList.toggle("visible", appState.global.isLoopActive);
|
||||
|
||||
// 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();
|
||||
}
|
||||
restartAudioEditorIfPlaying();
|
||||
});
|
||||
|
||||
if (addAudioTrackBtn) { addAudioTrackBtn.addEventListener("click", () => { addAudioTrackLane(); renderAudioEditor(); }); }
|
||||
|
||||
// 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');
|
||||
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');
|
||||
}
|
||||
});
|
||||
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(); // Define o estado inicial dos botões
|
||||
});
|
||||
updateToolButtons();
|
||||
});
|
||||
Reference in new issue
Block a user