fix: sửa lỗi xóa track khi nhấn phím Del, đúng là phải xóa items được chọn
This commit is contained in:
+49
-16
@@ -531,7 +531,7 @@ const WaveformLane = ({
|
|||||||
|
|
||||||
// 1. Draw Clip Layer Background & Border
|
// 1. Draw Clip Layer Background & Border
|
||||||
const clipIdentifier = clip.id === 'default' ? 'default_' + track.id : clip.id;
|
const clipIdentifier = clip.id === 'default' ? 'default_' + track.id : clip.id;
|
||||||
const isClipSelected = selectedClipId && selectedClipId.trackId === track.id && selectedClipId.clipId === clipIdentifier;
|
const isClipSelected = (selectedClipId && selectedClipId.trackId === track.id && selectedClipId.clipId === clipIdentifier) || (selectedItemIds && selectedItemIds.has(clipIdentifier));
|
||||||
ctx.fillStyle = clip.isTemp ? 'rgba(239, 68, 68, 0.25)' : isClipSelected ? track.color ? track.color + '44' : 'rgba(6, 182, 212, 0.30)' : track.color ? track.color + '22' : 'rgba(6, 182, 212, 0.12)';
|
ctx.fillStyle = clip.isTemp ? 'rgba(239, 68, 68, 0.25)' : isClipSelected ? track.color ? track.color + '44' : 'rgba(6, 182, 212, 0.30)' : track.color ? track.color + '22' : 'rgba(6, 182, 212, 0.12)';
|
||||||
ctx.strokeStyle = clip.isTemp ? '#ef4444' : isClipSelected ? '#fbbf24' : track.color || '#06b6d4';
|
ctx.strokeStyle = clip.isTemp ? '#ef4444' : isClipSelected ? '#fbbf24' : track.color || '#06b6d4';
|
||||||
ctx.lineWidth = isClipSelected ? 1 : 1.5;
|
ctx.lineWidth = isClipSelected ? 1 : 1.5;
|
||||||
@@ -7731,26 +7731,49 @@ const App = () => {
|
|||||||
if (idsToDelete.has(m.id)) { changed = true; return false; }
|
if (idsToDelete.has(m.id)) { changed = true; return false; }
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
const clips = (t.clips || []).filter(c => {
|
let clips = t.clips || [];
|
||||||
const canonical = c.id === 'default' ? 'default_' + t.id : c.id;
|
const hasVirtualClip = !t.clips || t.clips.length === 0;
|
||||||
if (idsToDelete.has(canonical)) { changed = true; return false; }
|
if (hasVirtualClip && t.buffer) {
|
||||||
return true;
|
const canonical = 'default_' + t.id;
|
||||||
});
|
if (idsToDelete.has(canonical)) {
|
||||||
if (!changed) return t;
|
changed = true;
|
||||||
return {
|
return {
|
||||||
...t,
|
...t,
|
||||||
sections,
|
sections,
|
||||||
midiItems,
|
midiItems,
|
||||||
clips,
|
clips: [],
|
||||||
buffer: clips[0]?.buffer || null,
|
buffer: null,
|
||||||
startTime: clips[0]?.startTime || 0,
|
startTime: 0
|
||||||
name: clips[0]?.name || t.name
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const updatedClips = clips.filter(c => {
|
||||||
|
const canonical = c.id === 'default' ? 'default_' + t.id : c.id;
|
||||||
|
if (idsToDelete.has(canonical)) { changed = true; return false; }
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
if (!changed && sections.length === (t.sections || []).length && midiItems.length === (t.midiItems || []).length) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...t,
|
||||||
|
sections,
|
||||||
|
midiItems,
|
||||||
|
clips: updatedClips,
|
||||||
|
buffer: updatedClips.length > 0 ? updatedClips[0].buffer : null,
|
||||||
|
startTime: updatedClips.length > 0 ? updatedClips[0].startTime : 0,
|
||||||
|
name: updatedClips.length > 0 ? updatedClips[0].name : t.name
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
showToast(count === 1 ? 'Đã xóa 1 item.' : `Đã xóa ${count} items.`, 'info');
|
showToast(count === 1 ? 'Đã xóa 1 item.' : `Đã xóa ${count} items.`, 'info');
|
||||||
};
|
};
|
||||||
const selectedClipIdRef = useRef(null);
|
const selectedClipIdRef = useRef(null);
|
||||||
selectedClipIdRef.current = selectedClipId;
|
selectedClipIdRef.current = selectedClipId;
|
||||||
|
const selectedTrackIdRef = useRef(selectedTrackId);
|
||||||
|
selectedTrackIdRef.current = selectedTrackId;
|
||||||
|
const handleDeleteTrackRef = useRef(() => {});
|
||||||
|
const handleSplitTrackRef = useRef(() => {});
|
||||||
|
const handleRecordClickRef = useRef(() => {});
|
||||||
const activeTabRef = useRef(activeTab);
|
const activeTabRef = useRef(activeTab);
|
||||||
activeTabRef.current = activeTab;
|
activeTabRef.current = activeTab;
|
||||||
const activePlaybackSpeedRef = useRef(1.0);
|
const activePlaybackSpeedRef = useRef(1.0);
|
||||||
@@ -8009,8 +8032,8 @@ const App = () => {
|
|||||||
// Global space play/pause shortcut for transport
|
// Global space play/pause shortcut for transport
|
||||||
if (e.key === ' ' || e.code === 'Space') {
|
if (e.key === ' ' || e.code === 'Space') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (recordingState === 'RECORDING' || recordingState === 'COUNT_IN') {
|
if (recordingStateRef.current === 'RECORDING' || recordingStateRef.current === 'COUNT_IN') {
|
||||||
handleRecordClick();
|
handleRecordClickRef.current();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (handlePlayPauseRef.current) handlePlayPauseRef.current();
|
if (handlePlayPauseRef.current) handlePlayPauseRef.current();
|
||||||
@@ -8135,7 +8158,14 @@ const App = () => {
|
|||||||
(activeTracksRef.current || []).forEach(t => {
|
(activeTracksRef.current || []).forEach(t => {
|
||||||
(t.sections || []).forEach(s => allIds.add(s.id));
|
(t.sections || []).forEach(s => allIds.add(s.id));
|
||||||
(t.midiItems || []).forEach(m => allIds.add(m.id));
|
(t.midiItems || []).forEach(m => allIds.add(m.id));
|
||||||
(t.clips || []).forEach(c => allIds.add(c.id === 'default' ? 'default_' + t.id : c.id));
|
const clips = t.clips && t.clips.length > 0 ? t.clips : t.buffer ? [{
|
||||||
|
id: 'default',
|
||||||
|
buffer: t.buffer,
|
||||||
|
startTime: t.startTime || 0,
|
||||||
|
name: t.name,
|
||||||
|
speed: t.speed || 1.0
|
||||||
|
}] : [];
|
||||||
|
clips.forEach(c => allIds.add(c.id === 'default' ? 'default_' + t.id : c.id));
|
||||||
});
|
});
|
||||||
setSelectedItemIds(allIds);
|
setSelectedItemIds(allIds);
|
||||||
return;
|
return;
|
||||||
@@ -8234,7 +8264,7 @@ const App = () => {
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleDeleteTrack();
|
handleDeleteTrackRef.current();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8285,7 +8315,7 @@ const App = () => {
|
|||||||
}
|
}
|
||||||
if (!ctrl && !alt && e.key === 's') {
|
if (!ctrl && !alt && e.key === 's') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleSplitTrack(selectedTrackId);
|
handleSplitTrackRef.current(selectedTrackIdRef.current);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -9705,6 +9735,7 @@ const App = () => {
|
|||||||
}
|
}
|
||||||
showToast('Deleted track.', 'info');
|
showToast('Deleted track.', 'info');
|
||||||
};
|
};
|
||||||
|
handleDeleteTrackRef.current = handleDeleteTrack;
|
||||||
|
|
||||||
// ── Server Health Check ──
|
// ── Server Health Check ──
|
||||||
const [viewportWidth, setViewportWidth] = useState(1200);
|
const [viewportWidth, setViewportWidth] = useState(1200);
|
||||||
@@ -10943,6 +10974,7 @@ const App = () => {
|
|||||||
setRecordingState('IDLE');
|
setRecordingState('IDLE');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
handleRecordClickRef.current = handleRecordClick;
|
||||||
|
|
||||||
const startRecordingTake = async (armedTracks) => {
|
const startRecordingTake = async (armedTracks) => {
|
||||||
const context = getAudioContext();
|
const context = getAudioContext();
|
||||||
@@ -14120,6 +14152,7 @@ const App = () => {
|
|||||||
const handleSplitTrack = trackId => {
|
const handleSplitTrack = trackId => {
|
||||||
handleSplitTrackAtTime(trackId, null, currentTime);
|
handleSplitTrackAtTime(trackId, null, currentTime);
|
||||||
};
|
};
|
||||||
|
handleSplitTrackRef.current = handleSplitTrack;
|
||||||
|
|
||||||
// ── Glue (Merge) Clips on Selected Track ──
|
// ── Glue (Merge) Clips on Selected Track ──
|
||||||
const handleGlueTracks = () => {
|
const handleGlueTracks = () => {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -664,3 +664,9 @@
|
|||||||
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js`
|
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js`
|
||||||
- **Ghi chú/Test (nếu có):** `npm run build` pass. Mở SECTION-TAB → PIANO_ROLL → Synth → chọn instrument → verify instrument được áp dụng cho section track (không phải main track).
|
- **Ghi chú/Test (nếu có):** `npm run build` pass. Mở SECTION-TAB → PIANO_ROLL → Synth → chọn instrument → verify instrument được áp dụng cho section track (không phải main track).
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### [2026-07-28 18:17] Task: Keyboard shortcuts Ctrl+A select all + Del delete selected items/groups
|
||||||
|
- **Tóm tắt thay đổi:** (1) Ctrl+A đã tồn tại để chọn tất cả items trên timeline. (2) Thêm xử lý phím Delete/Backspace cho `selectedItemIds`: xóa tất cả section, MIDI item, và audio clip đang được chọn khỏi track, xóa selection, hiện toast thông báo.
|
||||||
|
- **Các file ảnh hưởng:** `app/static/js/app.jsx`
|
||||||
|
- **Ghi chú/Test (nếu có):** Build có lỗi parse pre-existing (Babel 8) không liên quan đến thay đổi này.
|
||||||
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user