MiniCut Core

Núcleo compartido para proyectos, pistas, historial, guardado automático, eventos y módulos futuros.

v0.1
Pistas 0
Elementos 0
Plugins 0
Historial 0

📁 Proyecto

🎞️ Crear pista

🎚️ Pistas del proyecto

🔌 Módulos registrados

🕘 Historial y eventos

Núcleo preparado.

Los próximos bloques deberán conectarse mediante window.MiniCutCore. Este núcleo guarda el estado en el navegador y evita que cada módulo trabaje por separado.

'; return; } state.tracks.forEach( function(track){ var item = document.createElement( "div" ); item.className = "item"; var title = document.createElement( "strong" ); title.textContent = trackIcon( track.type ) + " " + track.name; var info = document.createElement( "small" ); info.textContent = track.type + " · " + track.items.length + " elemento(s)" + ( track.muted ? " · silenciada" : "" ) + ( track.hidden ? " · oculta" : "" ) + ( track.locked ? " · bloqueada" : "" ); var mute = createSmallButton( track.muted ? "Activar sonido" : "Silenciar", "secondary", function(){ toggleTrackFlag( track.id, "muted" ); } ); var hide = createSmallButton( track.hidden ? "Mostrar" : "Ocultar", "secondary", function(){ toggleTrackFlag( track.id, "hidden" ); } ); hide.style.marginLeft = "6px"; var lock = createSmallButton( track.locked ? "Desbloquear" : "Bloquear", "secondary", function(){ toggleTrackFlag( track.id, "locked" ); } ); lock.style.marginLeft = "6px"; var remove = createSmallButton( "Eliminar", "danger", function(){ removeTrack( track.id ); } ); remove.style.marginLeft = "6px"; item.appendChild( title ); item.appendChild( info ); item.appendChild( mute ); item.appendChild( hide ); item.appendChild( lock ); item.appendChild( remove ); box.appendChild( item ); } ); } function renderPlugins(){ var box = document.getElementById( "mccPluginList" ); box.innerHTML = ""; if(!state.plugins.length){ box.innerHTML = '
No hay módulos registrados.
'; return; } state.plugins.forEach( function(plugin){ var item = document.createElement( "div" ); item.className = "item"; var title = document.createElement( "strong" ); title.textContent = "🔌 " + plugin.name + " v" + plugin.version; var info = document.createElement( "small" ); info.textContent = plugin.description || plugin.id; var remove = createSmallButton( "Eliminar", "danger", function(){ unregisterPlugin( plugin.id ); } ); item.appendChild( title ); item.appendChild( info ); item.appendChild( remove ); box.appendChild( item ); } ); } function renderHistory(){ var box = document.getElementById( "mccHistoryList" ); box.innerHTML = ""; if(!undoStack.length){ box.innerHTML = '
Todavía no hay cambios registrados.
'; return; } undoStack .slice() .reverse() .slice(0,12) .forEach( function(entry){ var item = document.createElement( "div" ); item.className = "item"; var title = document.createElement( "strong" ); title.textContent = "🕘 " + entry.label; var info = document.createElement( "small" ); info.textContent = new Date( entry.at ) .toLocaleString( "es-ES" ); item.appendChild( title ); item.appendChild( info ); box.appendChild( item ); } ); } function createSmallButton( text, className, handler ){ var button = document.createElement( "button" ); button.type = "button"; button.className = className; button.textContent = text; button.addEventListener( "click", handler ); return button; } function setStatus( message ){ document.getElementById( "mccStatus" ).textContent = message; } function logEvent( eventName, payload ){ var log = document.getElementById( "mccEventLog" ); var line = document.createElement( "div" ); var preview = ""; try{ preview = JSON.stringify(payload) .slice(0,180); }catch(error){ preview = "[datos no serializables]"; } line.textContent = new Date() .toLocaleTimeString( "es-ES" ) + " · " + eventName + " · " + preview; log.insertBefore( line, log.firstChild ); while( log.children.length > 40 ){ log.removeChild( log.lastChild ); } } function defaultTrackName( type ){ var names = { video:"Vídeo principal", audio:"Pista de audio", image:"Capa de imagen", text:"Pista de texto", subtitle:"Subtítulos", effects:"Efectos" }; return names[type] || "Nueva pista"; } function trackIcon( type ){ var icons = { video:"🎥", audio:"🎵", image:"🖼️", text:"✍️", subtitle:"💬", effects:"✨" }; return icons[type] || "🎞️"; } function debounce( fn, delay ){ var timer = null; return function(){ var args = arguments; clearTimeout( timer ); timer = setTimeout( function(){ fn.apply( null, args ); }, delay ); }; } renderAll(); bus.emit( "core:ready", { version:"0.1", state:snapshot() } ); logEvent( "core:ready", { version:"0.1" } ); }());

MiniCut Player

Reproductor y área de previsualización conectados al núcleo MiniCut Core.

Bloque 2 · v0.1
00:00:00 / 00:00:00

📁 Fuente de vídeo

Sin vídeo cargado.
Esperando un archivo de vídeo.

Este bloque necesita que el Bloque 1 esté colocado encima. Cuando añades el vídeo al núcleo, se crea automáticamente una pista de vídeo y un elemento con su duración.

MiniCut Timeline

Línea de tiempo multipista conectada al núcleo y al reproductor.

Bloque 3 · v0.1
Duración 0 s
Pistas 0
Elementos 0
Zoom 100%

🧩 Elemento seleccionado

⏱️ Tiempo y duración

Esperando datos del proyecto.

Puedes seleccionar elementos, moverlos, copiarlos, dividirlos y cambiar su duración. El cabezal rojo sigue la posición del reproductor del Bloque 2.

'; tracksBox.appendChild( empty ); return; } state.tracks.forEach( function(track){ var row = document.createElement( "div" ); row.className = "trackRow"; var header = document.createElement( "div" ); header.className = "trackHeader"; var title = document.createElement( "strong" ); title.textContent = trackIcon( track.type ) + " " + track.name; var info = document.createElement( "small" ); info.textContent = track.items.length + " elemento(s)" + ( track.muted ? " · silenciada" : "" ) + ( track.hidden ? " · oculta" : "" ) + ( track.locked ? " · bloqueada" : "" ); var buttons = document.createElement( "div" ); buttons.className = "trackButtons"; buttons.appendChild( createTrackButton( track.muted ? "🔊" : "🔇", function(){ core.toggleTrackFlag( track.id, "muted" ); } ) ); buttons.appendChild( createTrackButton( track.hidden ? "👁" : "🙈", function(){ core.toggleTrackFlag( track.id, "hidden" ); } ) ); buttons.appendChild( createTrackButton( track.locked ? "🔓" : "🔒", function(){ core.toggleTrackFlag( track.id, "locked" ); } ) ); header.appendChild( title ); header.appendChild( info ); header.appendChild( buttons ); var canvas = document.createElement( "div" ); canvas.className = "trackCanvas"; canvas.style.width = width + "px"; if(track.hidden){ canvas.style.opacity = ".4"; } track.items.forEach( function(item){ canvas.appendChild( createClip( track, item, pxPerSecond ) ); } ); row.appendChild( header ); row.appendChild( canvas ); tracksBox.appendChild( row ); } ); } function createClip( track, item, pxPerSecond ){ var clip = document.createElement( "div" ); clip.className = "clip " + track.type + ( track.locked ? " locked" : "" ) + ( selectedItem && selectedItem.item.id === item.id && selectedItem.trackId === track.id ? " selected" : "" ); clip.style.left = ( item.start * pxPerSecond ) + "px"; clip.style.width = Math.max( 36, item.duration * pxPerSecond ) + "px"; var title = document.createElement( "strong" ); title.textContent = item.name; var info = document.createElement( "small" ); info.textContent = item.start.toFixed(1) + "s · " + item.duration.toFixed(1) + "s"; clip.appendChild( title ); clip.appendChild( info ); clip.addEventListener( "click", function(event){ event.stopPropagation(); selectedItem = { trackId: track.id, item: JSON.parse( JSON.stringify( item ) ) }; selectedTrackId = track.id; trackSelect.value = track.id; renderAll(); } ); clip.addEventListener( "pointerdown", function(event){ if(track.locked){ return; } draggingItem = { trackId: track.id, itemId: item.id, element: clip }; dragStartX = event.clientX; dragOriginalStart = item.start; clip.setPointerCapture( event.pointerId ); event.preventDefault(); } ); clip.addEventListener( "pointermove", function(event){ if( !draggingItem || draggingItem.itemId !== item.id ){ return; } var delta = ( event.clientX - dragStartX ) / pxPerSecond; var nextStart = Math.max( 0, dragOriginalStart + delta ); clip.style.left = ( nextStart * pxPerSecond ) + "px"; } ); clip.addEventListener( "pointerup", function(event){ if( !draggingItem || draggingItem.itemId !== item.id ){ return; } var delta = ( event.clientX - dragStartX ) / pxPerSecond; var nextStart = Math.max( 0, dragOriginalStart + delta ); core.updateState( function(next){ var nextTrack = next.tracks.find( function(entry){ return entry.id === track.id; } ); if(!nextTrack){ return; } var nextItem = nextTrack.items.find( function(entry){ return entry.id === item.id; } ); if(nextItem){ nextItem.start = Math.round( nextStart * 100 ) / 100; } }, { label: "Mover elemento" } ); draggingItem = null; try{ clip.releasePointerCapture( event.pointerId ); }catch(error){ } } ); return clip; } function updateInspector(){ if(!selectedItem){ document.getElementById( "mctlSelectedName" ).value = ""; document.getElementById( "mctlSelectedStart" ).value = ""; document.getElementById( "mctlSelectedDuration" ).value = ""; return; } var state = core.getState(); var track = state.tracks.find( function(item){ return item.id === selectedItem.trackId; } ); if(!track){ selectedItem = null; updateInspector(); return; } var item = track.items.find( function(entry){ return entry.id === selectedItem.item.id; } ); if(!item){ selectedItem = null; updateInspector(); return; } selectedItem.item = JSON.parse( JSON.stringify( item ) ); document.getElementById( "mctlSelectedName" ).value = item.name; document.getElementById( "mctlSelectedStart" ).value = item.start; document.getElementById( "mctlSelectedDuration" ).value = item.duration; } function updatePlayhead( time ){ playhead.style.left = ( time * pixelsPerSecond() + 180 ) + "px"; } function pixelsPerSecond(){ return ( Number( zoomInput.value ) / 100 ) * 60; } function calculateDuration( state ){ var max = 0; state.tracks.forEach( function(track){ track.items.forEach( function(item){ max = Math.max( max, item.start + item.duration ); } ); } ); if( player && player.duration ){ max = Math.max( max, player.duration ); } return max; } function createTrackButton( text, handler ){ var button = document.createElement( "button" ); button.type = "button"; button.className = "secondary"; button.textContent = text; button.addEventListener( "click", handler ); return button; } function defaultItemName( type ){ var names = { video:"Clip de vídeo", audio:"Clip de audio", image:"Imagen", text:"Texto", subtitle:"Subtítulo", effects:"Efecto" }; return names[type] || "Elemento"; } function trackIcon( type ){ var icons = { video:"🎥", audio:"🎵", image:"🖼️", text:"✍️", subtitle:"💬", effects:"✨" }; return icons[type] || "🎞️"; } function formatTime( seconds ){ seconds = Math.max( 0, Number(seconds) || 0 ); var minutes = Math.floor( seconds / 60 ); var rest = Math.floor( seconds % 60 ); return minutes + ":" + String(rest) .padStart( 2, "0" ); } renderAll(); }());

MiniCut Clip Tools

Herramientas avanzadas para recortar, desplazar, duplicar, ajustar bordes y modificar la velocidad de los clips.

Bloque 4 · v0.1

🎯 Selección

No hay ningún elemento seleccionado.

📐 Ajuste de bordes

⚡ Velocidad y tiempo

🧬 Copias y división

📋 Elementos de la pista seleccionada

Esperando elementos del proyecto.

Este bloque modifica directamente los elementos almacenados en MiniCut Core. Los cambios aparecen automáticamente en la línea de tiempo del Bloque 3.

'; selectedItemId = ""; return; } var items = track.items.slice() .sort( function(a,b){ return a.start - b.start; } ); items.forEach( function(item,index){ var option = document.createElement( "option" ); option.value = item.id; option.textContent = ( index + 1 ) + ". " + item.name; itemSelect.appendChild( option ); var box = document.createElement( "div" ); box.className = "item" + ( item.id === selectedItemId ? " selected" : "" ); var title = document.createElement( "strong" ); title.textContent = item.name; var data = item.data || {}; var info = document.createElement( "small" ); info.textContent = "Inicio: " + item.start.toFixed(2) + " s · Duración: " + item.duration.toFixed(2) + " s · Velocidad: " + ( Number( data.speed ) || 1 ) + "×"; box.appendChild( title ); box.appendChild( info ); box.addEventListener( "click", function(){ selectedItemId = item.id; renderItems(); renderSelection(); } ); list.appendChild( box ); } ); if( selectedItemId && items.some( function(item){ return item.id === selectedItemId; } ) ){ itemSelect.value = selectedItemId; }else{ selectedItemId = items[0].id; itemSelect.value = selectedItemId; } } function renderSelection(){ var selection = getSelection( false ); var summary = document.getElementById( "mcctSummary" ); if(!selection){ summary.innerHTML = "No hay ningún elemento seleccionado."; return; } var data = selection.item.data || {}; summary.innerHTML = "" + escapeHtml( selection.item.name ) + "
" + "Pista: " + escapeHtml( selection.track.name ) + "
" + "Inicio: " + selection.item.start.toFixed(2) + " s · Fin: " + ( selection.item.start + selection.item.duration ).toFixed(2) + " s · Duración: " + selection.item.duration.toFixed(2) + " s
" + "Velocidad: " + ( Number( data.speed ) || 1 ) + "× · Desplazamiento de origen: " + ( Number( data.sourceOffset ) || 0 ).toFixed(2) + " s"; document.getElementById( "mcctStart" ).value = selection.item.start; document.getElementById( "mcctDuration" ).value = selection.item.duration; document.getElementById( "mcctSpeed" ).value = String( Number( data.speed ) || 1 ); } function getSelection( showError ){ if(showError === undefined){ showError = true; } var state = core.getState(); var track = state.tracks.find( function(item){ return item.id === selectedTrackId; } ); if(!track){ if(showError){ status.textContent = "Selecciona una pista."; } return null; } var item = track.items.find( function(entry){ return entry.id === selectedItemId; } ); if(!item){ if(showError){ status.textContent = "Selecciona un elemento."; } return null; } return{ track:track, item:item }; } function getPlayerTime(){ if(player){ return Number( player.currentTime ) || 0; } return 0; } function setPlayerTime( time ){ if(player){ player.currentTime = Math.max( 0, Number(time) || 0 ); } core.emit( "cliptools:seek", { time: Number(time) || 0 } ); } function trackIcon( type ){ var icons = { video:"🎥", audio:"🎵", image:"🖼️", text:"✍️", subtitle:"💬", effects:"✨" }; return icons[type] || "🎞️"; } function createId( prefix ){ return ( prefix + "_" + Date.now() .toString(36) + "_" + Math.random() .toString(36) .slice(2,8) ); } function round2( value ){ return Math.round( Number(value) * 100 ) / 100; } function escapeHtml( value ){ return String( value || "" ).replace( /[&<>"']/g, function(character){ return{ "&":"&", "<":"<", ">":">", '"':""", "'":"'" }[character]; } ); } renderAll(); status.textContent = "MiniCut Clip Tools conectado correctamente."; }());

MiniCut Layers

Gestor de capas de imagen y vídeo con posición, escala, opacidad, rotación, orden y tiempo de aparición.

Bloque 5 · v0.1

➕ Añadir capa

🎛️ Transformación

👁️ Vista previa de capas

📚 Capas del proyecto

Esperando capas.

Las capas se guardan como elementos de pistas de imagen o vídeo dentro de MiniCut Core. Los archivos locales se usan para la vista previa durante la sesión actual.

'; selectedTrackId = ""; selectedItemId = ""; return; } layers.forEach( function(entry){ var box = document.createElement( "div" ); box.className = "item" + ( entry.item.id === selectedItemId ? " selected" : "" ); var title = document.createElement( "strong" ); title.textContent = ( entry.item.type === "image" ? "🖼️ " : "🎥 " ) + entry.item.name; var data = entry.item.data || {}; var info = document.createElement( "small" ); info.textContent = "Inicio: " + entry.item.start.toFixed(1) + " s · Duración: " + entry.item.duration.toFixed(1) + " s · Escala: " + ( Number( data.scale ) || 35 ) + "% · Z: " + ( Number( data.zIndex ) || 1 ); box.appendChild( title ); box.appendChild( info ); box.addEventListener( "click", function(){ selectedTrackId = entry.track.id; selectedItemId = entry.item.id; renderAll(); } ); list.appendChild( box ); } ); if(!selectedItemId){ selectedTrackId = layers[0].track.id; selectedItemId = layers[0].item.id; } } function renderPreview(){ clearPreview(); var state = core.getState(); var entries = []; state.tracks.forEach( function(track){ track.items.forEach( function(item){ if( item.type === "image" || item.type === "video" ){ entries.push(item); } } ); } ); entries.sort( function(a,b){ return ( Number( a.data && a.data.zIndex ) || 0 ) - ( Number( b.data && b.data.zIndex ) || 0 ); } ); entries.forEach( function(item){ var data = item.data || {}; var layerId = data.layerId; var fileInfo = fileRegistry[layerId]; if(!layerId || !fileInfo){ return; } var element; if(item.type === "image"){ element = document.createElement( "img" ); element.src = fileInfo.url; }else{ element = document.createElement( "video" ); element.src = fileInfo.url; element.muted = true; element.playsInline = true; element.preload = "metadata"; } element.dataset.layerId = layerId; element.dataset.itemId = item.id; applyStyleToElement( element, data ); enableDrag( element, item ); preview.appendChild( element ); elementRegistry[layerId] = element; } ); preview.appendChild( grid ); updateVisibility(); syncPreviewVideos(); } function renderControls(){ var selection = getSelection( false ); if(!selection){ return; } var data = selection.item.data || {}; document.getElementById( "mclX" ).value = Number( data.x ) || 50; document.getElementById( "mclY" ).value = Number( data.y ) || 50; document.getElementById( "mclScale" ).value = Number( data.scale ) || 35; document.getElementById( "mclOpacity" ).value = data.opacity === undefined ? 1 : Number( data.opacity ); document.getElementById( "mclRotation" ).value = Number( data.rotation ) || 0; document.getElementById( "mclRadius" ).value = Number( data.radius ) || 0; } function applyStyleToElement( element, data ){ element.style.left = ( Number( data.x ) || 50 ) + "%"; element.style.top = ( Number( data.y ) || 50 ) + "%"; element.style.width = ( Number( data.scale ) || 35 ) + "%"; element.style.opacity = data.opacity === undefined ? "1" : String( data.opacity ); element.style.transform = "translate(-50%,-50%) rotate(" + ( Number( data.rotation ) || 0 ) + "deg)"; element.style.borderRadius = ( Number( data.radius ) || 0 ) + "%"; element.style.zIndex = Number( data.zIndex ) || 1; } function enableDrag( element, item ){ element.addEventListener( "pointerdown", function(event){ dragging = { element:element, itemId:item.id }; var rect = element.getBoundingClientRect(); dragOffsetX = event.clientX - rect.left - rect.width / 2; dragOffsetY = event.clientY - rect.top - rect.height / 2; element.setPointerCapture( event.pointerId ); selectedItemId = item.id; renderLayerList(); renderControls(); } ); element.addEventListener( "pointermove", function(event){ if( !dragging || dragging.element !== element ){ return; } var rect = preview.getBoundingClientRect(); var x = ( event.clientX - rect.left - dragOffsetX ) / rect.width * 100; var y = ( event.clientY - rect.top - dragOffsetY ) / rect.height * 100; x = Math.max( 0, Math.min( 100, x ) ); y = Math.max( 0, Math.min( 100, y ) ); element.style.left = x + "%"; element.style.top = y + "%"; document.getElementById( "mclX" ).value = x; document.getElementById( "mclY" ).value = y; } ); element.addEventListener( "pointerup", function(event){ if( !dragging || dragging.element !== element ){ return; } var x = Number( document.getElementById( "mclX" ).value ); var y = Number( document.getElementById( "mclY" ).value ); core.updateState( function(next){ next.tracks.forEach( function(track){ var nextItem = track.items.find( function(entry){ return entry.id === item.id; } ); if(nextItem){ nextItem.data = nextItem.data || {}; nextItem.data.x = x; nextItem.data.y = y; } } ); }, { label:"Mover capa" } ); dragging = null; try{ element.releasePointerCapture( event.pointerId ); }catch(error){ } } ); } function updateVisibility(){ var time = getCurrentTime(); var state = core.getState(); state.tracks.forEach( function(track){ track.items.forEach( function(item){ if( item.type !== "image" && item.type !== "video" ){ return; } var layerId = item.data && item.data.layerId; var element = layerId && elementRegistry[layerId]; if(!element){ return; } var visible = time >= item.start && time <= item.start + item.duration; element.style.display = visible ? "block" : "none"; } ); } ); } function syncPreviewVideos(){ var time = getCurrentTime(); var state = core.getState(); state.tracks.forEach( function(track){ track.items.forEach( function(item){ if(item.type !== "video"){ return; } var layerId = item.data && item.data.layerId; var element = layerId && elementRegistry[layerId]; if( !element || !element.duration ){ return; } var local = Math.max( 0, time - item.start ); local = Math.min( local, Math.max( 0, element.duration - .03 ) ); if( Math.abs( element.currentTime - local ) > .25 ){ try{ element.currentTime = local; }catch(error){ } } if( player && !player.paused && element.style.display !== "none" ){ element.play() .catch( function(){} ); }else{ element.pause(); } } ); } ); } function playPreviewVideos(){ Object.keys( elementRegistry ) .forEach( function(key){ var element = elementRegistry[key]; if( element.tagName === "VIDEO" && element.style.display !== "none" ){ element.play() .catch( function(){} ); } } ); } function pausePreviewVideos(){ Object.keys( elementRegistry ) .forEach( function(key){ var element = elementRegistry[key]; if(element.tagName === "VIDEO"){ element.pause(); } } ); } function clearPreview(){ Object.keys( elementRegistry ) .forEach( function(key){ var element = elementRegistry[key]; if( element && element.parentNode === preview ){ element.remove(); } } ); elementRegistry = {}; } function getSelection( showError ){ if(showError === undefined){ showError = true; } var state = core.getState(); var track = state.tracks.find( function(item){ return item.id === selectedTrackId; } ); var item = track && track.items.find( function(entry){ return entry.id === selectedItemId; } ); if(!track || !item){ if(showError){ status.textContent = "Selecciona una capa."; } return null; } return{ track:track, item:item }; } function getCurrentTime(){ return player ? Number( player.currentTime ) || 0 : 0; } function calculateNextZ(){ var state = core.getState(); var max = 0; state.tracks.forEach( function(track){ track.items.forEach( function(item){ max = Math.max( max, Number( item.data && item.data.zIndex ) || 0 ); } ); } ); return max + 1; } function createId( prefix ){ return ( prefix + "_" + Date.now() .toString(36) + "_" + Math.random() .toString(36) .slice(2,8) ); } renderAll(); status.textContent = "MiniCut Layers conectado correctamente."; }());

MiniCut Audio Engine

Motor base Web Audio para los siguientes módulos.

6.1-1 · v0.1
Estadoinactivo
Frecuencia
Fuentes0
Nivel0%

⚙️ Motor

🎛️ Procesamiento

🔗 Cadena

📦 Fuentes

Pulsa «Iniciar» para activar el motor.
').join(""); } function sourceList(){const ids=Object.keys(sources);$("aeSources").innerHTML=ids.length?ids.map(id=>'
🎵 '+esc(id)+'Bus de audio registrado
').join(""):'
No hay fuentes registradas.
'} function defaults(){$("aeLow").value=0;$("aeMid").value=0;$("aeHigh").value=0;$("aeThreshold").value=-24;$("aeRatio").value=4;eqOff=compOff=false;if(ctx){reconnect();apply()}status.textContent="Valores iniciales restaurados."} function esc(s){return String(s).replace(/[&<>"']/g,c=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]))} const api=window.MiniCutAudioEngine={version:"0.1",input:null,ensureContext:init,getContext:()=>ctx,getState:state,createSourceBus:bus,removeSourceBus:remove,listSources:()=>Object.keys(sources),decodeArrayBuffer:a=>{init();return ctx.decodeAudioData(a.slice(0))},connectNode:n=>{init();n.connect(input);return n},resume:()=>{init();return ctx.resume().then(ui)},suspend:()=>ctx?ctx.suspend().then(ui):Promise.resolve(),close}; $("aeInit").onclick=()=>{try{init();status.textContent="Motor iniciado."}catch(e){status.textContent=e.message}}; $("aeResume").onclick=()=>{init();ctx.resume().then(()=>{ui();status.textContent="Motor reanudado."})}; $("aeSuspend").onclick=()=>{if(ctx)ctx.suspend().then(()=>{ui();status.textContent="Motor suspendido."})}; $("aeClose").onclick=close;$("aeGain").oninput=()=>{if(ctx){master();nodes()}};$("aePan").oninput=()=>{if(ctx){master();nodes()}}; $("aeApply").onclick=()=>{init();apply();status.textContent="Procesamiento aplicado."}; $("aeBypassEq").onclick=function(){init();eqOff=!eqOff;reconnect();this.textContent=eqOff?"Activar EQ":"Bypass EQ";nodes()}; $("aeBypassComp").onclick=function(){init();compOff=!compOff;reconnect();this.textContent=compOff?"Activar comp.":"Bypass comp.";nodes()}; $("aeDefaults").onclick=defaults;$("aeTone").onclick=testTone;$("aeStopTone").onclick=()=>{stopTone();status.textContent="Tono detenido."}; $("aeReset").onclick=()=>{if(ctx){eqOff=compOff=false;reconnect();apply();master();status.textContent="Cadena reconstruida."}}; $("aeEmit").onclick=()=>{core.emit("audioengine:status",state());status.textContent="Estado emitido."}; $("aeAddSource").onclick=()=>{const id=$("aeSourceId").value.trim();if(!id)return status.textContent="Escribe un ID.";bus(id);status.textContent="Fuente registrada."}; $("aeRemoveSource").onclick=()=>{const id=$("aeSourceId").value.trim();status.textContent=remove(id)?"Fuente eliminada.":"La fuente no existe."}; $("aeClearSources").onclick=()=>{Object.keys(sources).forEach(remove);status.textContent="Registro limpiado."};$("aeRefresh").onclick=ui; core.on("project:new",()=>Object.keys(sources).forEach(remove)); core.registerPlugin({id:"minicut-audio-engine-v01",name:"MiniCut Audio Engine",version:"0.1",description:"Motor base Web Audio."}); ui(); })();