diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx
index d5b2355..087273f 100644
--- a/app/static/js/app.jsx
+++ b/app/static/js/app.jsx
@@ -14925,6 +14925,24 @@ const App = () => {
const [selectionCleared, setSelectionCleared] = useState(false); // LOOP_EDITOR_2.md §4.2
+ // Detect MAIN/SECTION play vừa DỪNG (hết notes/projectEnd/stop): nếu đang ở
+ // PIANO ROLL tab (không play tab) → playhead về 0 — user: main play hết →
+ // không còn midi note → piano roll phải về đầu (không giữ local cuối).
+ const prevIsPlayingRef = useRef(false);
+ useEffect(() => {
+ const wasPlaying = prevIsPlayingRef.current;
+ prevIsPlayingRef.current = isPlaying;
+ if (wasPlaying && !isPlaying) {
+ const _tabId = activeTabRef.current;
+ if (_tabId && _tabId.startsWith('midi_')) {
+ const _prSt = subTabsRef.current.find(s => s.id === _tabId);
+ if (_prSt && !_prSt.isPlaying) {
+ setSubTabs(prev => prev.map(s => s.id === _tabId ? { ...s, currentTime: 0 } : s));
+ }
+ }
+ }
+ }, [isPlaying]);
+
const midiVuActivityRef = useRef({});
const triggerMidiVuActivity = (trackId, velocity) => {
if (!trackId) return;
diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js
index 68c04b5..7c445f6 100644
--- a/app/static/js/app.precompiled.js
+++ b/app/static/js/app.precompiled.js
@@ -717,7 +717,10 @@ if(activeTab==='main'&&prevSub.parent_tab_id&&prevSub.parent_tab_id.startsWith('
// roll — mainResumeRef vừa set bởi handleEditMidiInTab — clear nhầm ở
// đây làm resume chết → quay lại main/section bị CÂM).
mainResumeRef.current=null;}}},[activeTab]);const[selectionCleared,setSelectionCleared]=useState(false);// LOOP_EDITOR_2.md §4.2
-const midiVuActivityRef=useRef({});const triggerMidiVuActivity=(trackId,velocity)=>{if(!trackId)return;const velFactor=typeof velocity==='number'?velocity>1?velocity/127:velocity:0.8;const peak=Math.min(1.0,Math.max(0.15,velFactor));midiVuActivityRef.current[trackId]=peak;};window.triggerMidiVuActivity=triggerMidiVuActivity;// ── Temp Edit Tab (LOOP_EDITOR_2.md §1.2 Sub Tab) ──
+// Detect MAIN/SECTION play vừa DỪNG (hết notes/projectEnd/stop): nếu đang ở
+// PIANO ROLL tab (không play tab) → playhead về 0 — user: main play hết →
+// không còn midi note → piano roll phải về đầu (không giữ local cuối).
+const prevIsPlayingRef=useRef(false);useEffect(()=>{const wasPlaying=prevIsPlayingRef.current;prevIsPlayingRef.current=isPlaying;if(wasPlaying&&!isPlaying){const _tabId=activeTabRef.current;if(_tabId&&_tabId.startsWith('midi_')){const _prSt=subTabsRef.current.find(s=>s.id===_tabId);if(_prSt&&!_prSt.isPlaying){setSubTabs(prev=>prev.map(s=>s.id===_tabId?{...s,currentTime:0}:s));}}}},[isPlaying]);const midiVuActivityRef=useRef({});const triggerMidiVuActivity=(trackId,velocity)=>{if(!trackId)return;const velFactor=typeof velocity==='number'?velocity>1?velocity/127:velocity:0.8;const peak=Math.min(1.0,Math.max(0.15,velFactor));midiVuActivityRef.current[trackId]=peak;};window.triggerMidiVuActivity=triggerMidiVuActivity;// ── Temp Edit Tab (LOOP_EDITOR_2.md §1.2 Sub Tab) ──
const[tempTabActive,setTempTabActive]=useState(false);const[tempTabBuffer,setTempTabBuffer]=useState(null);const[tempTabTrackId,setTempTabTrackId]=useState(null);const[tempTabOrigStart,setTempTabOrigStart]=useState(0);const[tempTabOrigEnd,setTempTabOrigEnd]=useState(0);const tempTabCanvasRef=useRef(null);// Effect parameters for temp tab
const[tempTabEffects,setTempTabEffects]=useState({reverse:false,gainDb:0,fadeInMs:0,fadeOutMs:0});// ── Auth / User State ──
const[currentUser,setCurrentUser]=useState(null);const[authModalOpen,setAuthModalOpen]=useState(false);const[authMode,setAuthMode]=useState('login');// 'login' | 'register' | 'force_change'
diff --git a/app/templates/index.html b/app/templates/index.html
index 9a1cc99..c9d5ce3 100644
--- a/app/templates/index.html
+++ b/app/templates/index.html
@@ -24,7 +24,7 @@
-
+