feat(export): modal tien trinh export rieng (khong dung statusbar) - dong modal settings ngay khi bam Export + bao loi that khi khong ghi duoc file

This commit is contained in:
2026-08-24 18:43:50 +07:00
parent 8c972c3df2
commit 43b68200aa
3 changed files with 63 additions and 22 deletions
+48 -11
View File
@@ -12628,16 +12628,6 @@ const ExportModal = ({ open, onClose, exportSettings, setExportSettings, isExpor
</div>
</div>
<div className="p-4 pt-3 border-t border-slate-700 space-y-2">
{isExporting && (
<div className="bg-[#141414] border border-zinc-800 rounded px-2 py-1.5">
<div className="h-1.5 bg-zinc-800 rounded-full overflow-hidden">
<div className="h-full bg-cyan-500 transition-all duration-200" style={{ width: (exportProgress >= 0 ? exportProgress : 25) + '%' }} />
</div>
<div className="text-[8px] text-zinc-500 mt-1">
{exportProgress >= 0 ? 'Đang xuất... ' + Math.round(exportProgress) + '%' : 'Đang xử lý...'}
</div>
</div>
)}
<div className="grid grid-cols-2 gap-2">
<button onClick={onExport} disabled={isExporting}
className="w-full py-1 bg-cyan-700 hover:bg-cyan-600 text-zinc-100 font-bold rounded text-xs flex items-center justify-center gap-1">
@@ -12654,6 +12644,38 @@ const ExportModal = ({ open, onClose, exportSettings, setExportSettings, isExpor
);
};
// ── Export progress modal (RIÊNG, không dùng statusbar — vị trí đó cho tools) ──
// Settings modal đóng ngay khi bấm Export; modal này hiện tiến trình render +
// encode + ghi file, tự đóng khi xong (success/error qua toast).
const ExportProgressModal = ({ open, exportProgress, isExporting, onClose }) => {
if (!open) return null;
return (
<div className="fixed inset-0 z-[310] bg-black/70 backdrop-blur-sm flex items-center justify-center p-6">
<div className="w-full max-w-md bg-slate-900 border border-slate-700 rounded-2xl shadow-2xl overflow-hidden">
<div className="h-9 bg-slate-800/80 border-b border-slate-700 flex items-center justify-between px-3 select-none">
<span className="text-xs font-bold text-cyan-300 font-mono flex items-center gap-1.5">
<span className="w-3 h-3 border-2 border-cyan-300 border-t-transparent rounded-full animate-spin" />
ĐANG XUẤT ÂM THANH
</span>
</div>
<div className="p-5 space-y-3">
<div className="h-2.5 bg-zinc-800 rounded-full overflow-hidden">
<div className="h-full bg-cyan-500 transition-all duration-200" style={{ width: (exportProgress >= 0 ? exportProgress : 25) + '%' }} />
</div>
<div className="text-xs text-zinc-400 text-center">
{exportProgress >= 0 ? 'Đang xuất... ' + Math.round(exportProgress) + '%' : 'Đang xử lý...'}
</div>
{!isExporting && (
<button onClick={onClose} className="w-full py-1.5 bg-zinc-700 hover:bg-zinc-600 text-zinc-100 font-bold rounded text-xs">
Đóng
</button>
)}
</div>
</div>
</div>
);
};
// --- Embedded VST GUI (PLAN_MASTERBUS_VST_GUI_EMBED.md) --------------------
// Frame-capture + input relay: hiển thị native GUI qua HTTP /frame (~10fps),
// gửi pointer/key về bridge qua /input. Dùng chung MasteringModal + FXRackModal.
@@ -18100,6 +18122,7 @@ const App = () => {
const lastTempCompileTimeRef = useRef(0);
const nextMetronomeBeatRef = useRef(0);
const [showExportPanel, setShowExportPanel] = useState(false);
const [showExportProgress, setShowExportProgress] = useState(false); // modal tiến trình export RIÊNG
React.useEffect(() => {
if (showExportPanel) setTimeout(() => { try { window.lucide && window.lucide.createIcons(); } catch (e) { } }, 80);
}, [showExportPanel]);
@@ -28041,6 +28064,8 @@ const App = () => {
if (!exportSettings.loop && allOnServer && serverStatus === 'connected') {
// Use server-side export
setIsExporting(true);
setShowExportPanel(false);
setShowExportProgress(true);
setExportProgress(-1);
showToast("Đang gửi yêu cầu xuất âm thanh đến máy chủ...", "info");
try {
@@ -28113,6 +28138,7 @@ const App = () => {
clientSideExport(exportTracks);
} finally {
setIsExporting(false);
setShowExportProgress(false);
}
} else {
// Client-side export (existing working code)
@@ -28177,6 +28203,8 @@ const App = () => {
if (!masterBus) { showToast("Hãy phát thử một lần để khởi tạo audio engine trước.", "warning"); return; }
if (isExporting) return;
setIsExporting(true);
setShowExportPanel(false);
setShowExportProgress(true);
const audioCtx = getAudioContext();
try {
const allTracks = (activeTracksRef.current && activeTracksRef.current.length) ? activeTracksRef.current : tracks;
@@ -28275,6 +28303,7 @@ const App = () => {
showToast("Lỗi bounce: " + err.message, "error");
} finally {
setIsExporting(false);
setShowExportProgress(false);
}
};
const handleSaveLocalProject = (name) => {
@@ -28483,6 +28512,8 @@ const App = () => {
};
const clientSideExport = async (activeTracks, loopInfo) => {
setIsExporting(true);
setShowExportPanel(false);
setShowExportProgress(true);
setExportProgress(5);
showToast("Đang trộn âm thanh đa kênh (Offline Mixdown)...", "info");
// Yield 1 frame → React vẽ thanh "Đang xử lý..." trước khi graph build +
@@ -28789,7 +28820,7 @@ const App = () => {
if (dres && dres.saved_path) {
showToast(`Xuất bản âm thanh hoàn tất → ${dres.saved_path}`, "success");
} else {
showToast("Xuất bản âm thanh hoàn tất!", "success");
showToast("Không thể ghi file export — engine không lưu được file. Kiểm tra kết nối engine và thư mục export.", "error");
}
}
// Export hoàn tất → tự đóng modal.
@@ -28822,6 +28853,7 @@ const App = () => {
} finally {
setExportProgress(-1);
setIsExporting(false);
setShowExportProgress(false);
}
};
@@ -34613,6 +34645,11 @@ STRICT CONSTRAINTS:
onNoteOn: vkbNoteOn,
onNoteOff: vkbNoteOff,
bpm: bpm
}), /*#__PURE__*/React.createElement(ExportProgressModal, {
open: showExportProgress,
exportProgress: exportProgress,
isExporting: isExporting,
onClose: () => setShowExportProgress(false)
}), /*#__PURE__*/React.createElement(ExportModal, {
open: showExportPanel,
onClose: () => setShowExportPanel(false),
File diff suppressed because one or more lines are too long
+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=202608240110" defer></script>
<script src="/static/js/app.precompiled.js?v=202608231800" defer></script>
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
<style>
:root {