debug: DIAG bang overlay textarea co nut Copy thay cho window.alert (text khong copy duoc)

This commit is contained in:
2026-08-25 07:01:38 +07:00
parent ba7c03ca5e
commit f741e1e1f1
3 changed files with 7 additions and 4 deletions
+4 -1
View File
@@ -33,6 +33,9 @@ window.__sfDiagDump = function () {
return (idx >= 0 ? arr.slice(idx) : arr.slice(-50)).join('\n');
} catch (e) { return String(e); }
};
window.__sfDiagShow = function () {
try { if (window.__sfDiagDump) { var ov = document.createElement('div'); ov.style.cssText = 'position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,.65);display:flex;align-items:center;justify-content:center;font-family:monospace'; var box = document.createElement('div'); box.style.cssText = 'background:#fff;color:#111;border-radius:8px;padding:12px;width:min(92vw,900px);max-height:85vh;display:flex;flex-direction:column;box-shadow:0 4px 24px rgba(0,0,0,.5)'; var ta = document.createElement('textarea'); ta.readOnly = true; ta.value = window.__sfDiagDump(); ta.style.cssText = 'flex:1;min-height:55vh;font-family:monospace;font-size:11px;white-space:pre;resize:none'; var bar = document.createElement('div'); bar.style.cssText = 'display:flex;gap:8px;justify-content:flex-end;margin-top:8px'; var bCopy = document.createElement('button'); bCopy.textContent = 'Copy'; bCopy.onclick = function () { try { ta.focus(); ta.select(); document.execCommand('copy'); bCopy.textContent = 'Đã copy!'; } catch (e) { bCopy.textContent = 'Copy thủ công (Ctrl+C)'; } }; var bClose = document.createElement('button'); bClose.textContent = 'Đóng'; bClose.onclick = function () { document.body.removeChild(ov); }; bar.appendChild(bCopy); bar.appendChild(bClose); box.appendChild(ta); box.appendChild(bar); ov.appendChild(box); document.body.appendChild(ov); try { ta.focus(); ta.select(); } catch (e) {} return; } } catch (e) { window.alert('DIAG:\n' + (window.__sfDiagDump ? window.__sfDiagDump() : '')); }
};
// ── Dedicated per-track MIDI channel allocation ──
// FluidSynth has 16 channels; if two tracks share a channel, arming one track
@@ -28371,7 +28374,7 @@ const App = () => {
const blob = new Blob([view], { type: 'audio/wav' });
await downloadBlobViaServer(blob, `sonicforge-bounce-${captureRate}hz-${bitDepth}bit.wav`).catch(() => {});
if (window.__sfDiag) window.__sfDiag('BOUNCE_END');
try { window.alert('DIAG:\n' + window.__sfDiagDump()); } catch (e) {}
try { window.__sfDiagShow(); } catch (e) {}
showToast("Bounce hoàn tất — file gồm MIDI + FX Rack + Mastering Chain!", "success");
} catch (err) {
showToast("Lỗi bounce: " + err.message, "error");
+2 -2
View File
@@ -1,6 +1,6 @@
const{useState,useRef,useEffect,useMemo,useCallback}=React;// ── FastAPI Backend Configuration ──
const API_BASE_URL=(window.API_BASE_URL||window.location.origin).replace(/\/+$/,'');const API_AUDIO=`${API_BASE_URL}/api/v1/audio`;const API_MULTITRACK=`${API_BASE_URL}/api/v1/multitrack`;const API_TASKS=`${API_BASE_URL}/api/v1/audio/tasks`;// ── DEBUG diag (tạm thời) ──
window.__sfDiag=function(){var arr=[];try{var s=localStorage.getItem('__sf_diag');if(s)arr=JSON.parse(s)||[];}catch(e){}return function(msg){try{arr.push(new Date().toISOString().slice(11,23)+' '+msg);if(arr.length>2000)arr=arr.slice(-2000);localStorage.setItem('__sf_diag',JSON.stringify(arr));}catch(e){}};}();window.__sfDiagDump=function(){try{var s=localStorage.getItem('__sf_diag');var arr=s?JSON.parse(s)||[]:[];var idx=-1;for(var i=arr.length-1;i>=0;i--){if(arr[i].indexOf('BOUNCE_START')!==-1){idx=i;break;}}return(idx>=0?arr.slice(idx):arr.slice(-50)).join('\n');}catch(e){return String(e);}};// ── Dedicated per-track MIDI channel allocation ──
window.__sfDiag=function(){var arr=[];try{var s=localStorage.getItem('__sf_diag');if(s)arr=JSON.parse(s)||[];}catch(e){}return function(msg){try{arr.push(new Date().toISOString().slice(11,23)+' '+msg);if(arr.length>2000)arr=arr.slice(-2000);localStorage.setItem('__sf_diag',JSON.stringify(arr));}catch(e){}};}();window.__sfDiagDump=function(){try{var s=localStorage.getItem('__sf_diag');var arr=s?JSON.parse(s)||[]:[];var idx=-1;for(var i=arr.length-1;i>=0;i--){if(arr[i].indexOf('BOUNCE_START')!==-1){idx=i;break;}}return(idx>=0?arr.slice(idx):arr.slice(-50)).join('\n');}catch(e){return String(e);}};window.__sfDiagShow=function(){try{if(window.__sfDiagDump){var ov=document.createElement('div');ov.style.cssText='position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,.65);display:flex;align-items:center;justify-content:center;font-family:monospace';var box=document.createElement('div');box.style.cssText='background:#fff;color:#111;border-radius:8px;padding:12px;width:min(92vw,900px);max-height:85vh;display:flex;flex-direction:column;box-shadow:0 4px 24px rgba(0,0,0,.5)';var ta=document.createElement('textarea');ta.readOnly=true;ta.value=window.__sfDiagDump();ta.style.cssText='flex:1;min-height:55vh;font-family:monospace;font-size:11px;white-space:pre;resize:none';var bar=document.createElement('div');bar.style.cssText='display:flex;gap:8px;justify-content:flex-end;margin-top:8px';var bCopy=document.createElement('button');bCopy.textContent='Copy';bCopy.onclick=function(){try{ta.focus();ta.select();document.execCommand('copy');bCopy.textContent='Đã copy!';}catch(e){bCopy.textContent='Copy thủ công (Ctrl+C)';}};var bClose=document.createElement('button');bClose.textContent='Đóng';bClose.onclick=function(){document.body.removeChild(ov);};bar.appendChild(bCopy);bar.appendChild(bClose);box.appendChild(ta);box.appendChild(bar);ov.appendChild(box);document.body.appendChild(ov);try{ta.focus();ta.select();}catch(e){}return;}}catch(e){window.alert('DIAG:\n'+(window.__sfDiagDump?window.__sfDiagDump():''));}};// ── Dedicated per-track MIDI channel allocation ──
// FluidSynth has 16 channels; if two tracks share a channel, arming one track
// re-selects the other track's program and its instrument changes. Every track
// gets its own stable, unique channel (0-15, skipping 9 which is the classic
@@ -2165,7 +2165,7 @@ const totalFrames=chunks.reduce((a,c)=>a+c.length/2,0);let stereoBuf=new Float32
if(loopDur>0){const sf=Math.max(0,Math.floor(loopStart*captureRate))*2;const ef=Math.min(stereoBuf.length,Math.floor((loopStart+loopDur)*captureRate)*2);if(ef>sf)stereoBuf=stereoBuf.subarray(sf,ef);}const frames=stereoBuf.length/2;const bytesPerSample=isFloatBit?4:bitDepth/8;const headerSize=44;const fileSizeBytes=headerSize+frames*bytesPerSample*outChannels;const view=new DataView(new ArrayBuffer(fileSizeBytes));const writeString=(o,s)=>{for(let i=0;i<s.length;i++)view.setUint8(o+i,s.charCodeAt(i));};writeString(0,'RIFF');view.setUint32(4,fileSizeBytes-8,true);writeString(8,'WAVE');writeString(12,'fmt ');view.setUint32(16,16,true);view.setUint16(20,fmtTag,true);view.setUint16(22,outChannels,true);view.setUint32(24,captureRate,true);view.setUint32(28,captureRate*bytesPerSample*outChannels,true);view.setUint16(32,bytesPerSample,true);view.setUint16(34,bitDepth,true);writeString(36,'data');view.setUint32(40,frames*bytesPerSample*outChannels,true);let o=44;const lsb=bitDepth===24?1/0x800000:bitDepth===8?1/128:1/0x8000;// 1 LSB in float
for(let i=0;i<frames;i++){for(let ch=0;ch<outChannels;ch++){const raw=stereoBuf[i*2+ch];if(isFloatBit){view.setFloat32(o,raw,true);// bit-exact, no clamp/quantize
}else{// Single-stage TPDF dither, then clamp, then quantize (final export only)
const s=Math.max(-1,Math.min(1,tpdfDither(raw,lsb)));if(bitDepth===24){const v=Math.floor(s<0?s*0x800000:s*0x7FFFFF)&0xFFFFFF;view.setUint8(o,v&0xFF);view.setUint8(o+1,v>>8&0xFF);view.setUint8(o+2,v>>16&0xFF);}else if(bitDepth===16)view.setInt16(o,Math.floor(s<0?s*0x8000:s*0x7FFF),true);else view.setUint8(o,Math.floor((s+1)*127.5),true);}o+=bytesPerSample;}}const blob=new Blob([view],{type:'audio/wav'});await downloadBlobViaServer(blob,`sonicforge-bounce-${captureRate}hz-${bitDepth}bit.wav`).catch(()=>{});if(window.__sfDiag)window.__sfDiag('BOUNCE_END');try{window.alert('DIAG:\n'+window.__sfDiagDump());}catch(e){}showToast("Bounce hoàn tất — file gồm MIDI + FX Rack + Mastering Chain!","success");}catch(err){showToast("Lỗi bounce: "+err.message,"error");}finally{setIsExporting(false);setShowExportProgress(false);}};const handleSaveLocalProject=name=>{const finalName=name||projectName||'Dự án mới';const localId=currentProjectId&&currentProjectId.startsWith('local_')?currentProjectId:'local_'+Date.now();const projectSchemaObj=serializeProjectToSchema(localId,finalName,bpm,tracks,subTabs,sessionTabs,masteringSettings,masterVolume,{volL:masterVolL,volR:masterVolR,link:masterLink,muted:masterMuted});const dataStr=JSON.stringify(projectSchemaObj);localStorage.setItem('sonic_local_project_data',dataStr);localStorage.setItem('sonic_project_id',localId);localStorage.setItem('sonic_project_name',finalName);setCurrentProjectId(localId);setProjectName(finalName);window.SonicStorage.exportProjectToSFS(projectSchemaObj);showToast(`Đã lưu dự án local "${finalName}" thành công!`,"success");// Đã đăng nhập → đồng bộ lên Cloud (fire-and-forget): project mở được từ
const s=Math.max(-1,Math.min(1,tpdfDither(raw,lsb)));if(bitDepth===24){const v=Math.floor(s<0?s*0x800000:s*0x7FFFFF)&0xFFFFFF;view.setUint8(o,v&0xFF);view.setUint8(o+1,v>>8&0xFF);view.setUint8(o+2,v>>16&0xFF);}else if(bitDepth===16)view.setInt16(o,Math.floor(s<0?s*0x8000:s*0x7FFF),true);else view.setUint8(o,Math.floor((s+1)*127.5),true);}o+=bytesPerSample;}}const blob=new Blob([view],{type:'audio/wav'});await downloadBlobViaServer(blob,`sonicforge-bounce-${captureRate}hz-${bitDepth}bit.wav`).catch(()=>{});if(window.__sfDiag)window.__sfDiag('BOUNCE_END');try{window.__sfDiagShow();}catch(e){}showToast("Bounce hoàn tất — file gồm MIDI + FX Rack + Mastering Chain!","success");}catch(err){showToast("Lỗi bounce: "+err.message,"error");}finally{setIsExporting(false);setShowExportProgress(false);}};const handleSaveLocalProject=name=>{const finalName=name||projectName||'Dự án mới';const localId=currentProjectId&&currentProjectId.startsWith('local_')?currentProjectId:'local_'+Date.now();const projectSchemaObj=serializeProjectToSchema(localId,finalName,bpm,tracks,subTabs,sessionTabs,masteringSettings,masterVolume,{volL:masterVolL,volR:masterVolR,link:masterLink,muted:masterMuted});const dataStr=JSON.stringify(projectSchemaObj);localStorage.setItem('sonic_local_project_data',dataStr);localStorage.setItem('sonic_project_id',localId);localStorage.setItem('sonic_project_name',finalName);setCurrentProjectId(localId);setProjectName(finalName);window.SonicStorage.exportProjectToSFS(projectSchemaObj);showToast(`Đã lưu dự án local "${finalName}" thành công!`,"success");// Đã đăng nhập → đồng bộ lên Cloud (fire-and-forget): project mở được từ
// máy khác / browser ẩn danh khi đăng nhập cùng tài khoản.
if(currentUser&&window.SonicAPI&&window.SonicAPI.saveCloudProject){window.SonicAPI.saveCloudProject(finalName,dataStr).then(function(res){if(res&&res.project_id){console.log('[Cloud] local project synced:',res.project_id);}}).catch(function(e){console.warn('Cloud sync local project failed:',e);});}};const handleSaveCloudProject=async(name,existingProjectId)=>{const finalName=name||projectName||'Dự án mới';var useProjectId=existingProjectId||currentProjectId;const projectSchemaObj=serializeProjectToSchema(useProjectId||'project_'+Date.now(),finalName,bpm,tracks,subTabs,sessionTabs,masteringSettings,masterVolume,{volL:masterVolL,volR:masterVolR,link:masterLink,muted:masterMuted});// Diagnostic: count source vs serialized items
var srcMidi=0,srcClip=0,srcSec=0;tracks.forEach(function(t){srcMidi+=(t.midiItems||[]).length;srcClip+=(t.clips||[]).length;srcSec+=(t.sections||[]).length;});var serItems=0;if(projectSchemaObj.main_session&&projectSchemaObj.main_session.tracks){projectSchemaObj.main_session.tracks.forEach(function(st){if(st.items)serItems+=st.items.length;});}Object.keys(projectSchemaObj.section_store||{}).forEach(function(sk){(projectSchemaObj.section_store[sk].tracks||[]).forEach(function(st){if(st.items)serItems+=st.items.length;});});const dataJson=JSON.stringify(projectSchemaObj);try{if(existingProjectId){await window.SonicAPI.updateCloudProject(existingProjectId,finalName,dataJson);setProjectName(finalName);setCurrentProjectId(existingProjectId);localStorage.setItem('sonic_project_name',finalName);localStorage.setItem('sonic_project_id',existingProjectId);showToast('Đã ghi đè Cloud "'+finalName+'" (src midi='+srcMidi+' clip='+srcClip+' sec='+srcSec+' | ser='+serItems+').','success');}else if(currentProjectId&&!currentProjectId.startsWith('local_')){await window.SonicAPI.updateCloudProject(currentProjectId,finalName,dataJson);setProjectName(finalName);localStorage.setItem('sonic_project_name',finalName);localStorage.setItem('sonic_project_id',currentProjectId);showToast('Đã lưu Cloud "'+finalName+'" (src midi='+srcMidi+' clip='+srcClip+' sec='+srcSec+' | ser='+serItems+' id='+currentProjectId+').',serItems===0?'warning':'success');}else{const res=await window.SonicAPI.saveCloudProject(finalName,dataJson);const newProjId=res.project_id;setProjectName(finalName);localStorage.setItem('sonic_project_name',finalName);if(newProjId){setCurrentProjectId(newProjId);localStorage.setItem('sonic_project_id',newProjId);}showToast('Đã lưu Cloud mới "'+finalName+'" (src midi='+srcMidi+' clip='+srcClip+' sec='+srcSec+' | ser='+serItems+').',serItems===0?'warning':'success');}}catch(err){showToast(err.message||"Lỗi lưu dự án lên Cloud","error");}};const handleSaveProject=async()=>{if(!currentProjectId){setSaveProjectModalOpen(true);return;}if(currentProjectId.startsWith('local_')){handleSaveLocalProject(projectName);}else{if(!currentUser){showToast('Cần đăng nhập để lưu Cloud. currentUser='+(currentUser?'OK':'NULL')+' token='+(localStorage.getItem('sonic_token')?'exists':'missing'),'warning');return;}handleSaveCloudProject(projectName);}};const handleSaveProjectRef=useRef(handleSaveProject);handleSaveProjectRef.current=handleSaveProject;// ── Ctrl-S (Save) toàn cục ──
+1 -1
View File
@@ -51,7 +51,7 @@
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
<script src="/static/js/services/chordTheory.js?v=202608211300"></script>
<script src="/static/js/app.precompiled.js?v=202608250106" defer></script>
<script src="/static/js/app.precompiled.js?v=202608250107" defer></script>
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
<style>
:root {