fix: tempo track height 40px fixed + clamp selection >= 0
- TempoTrackLane canvas height fixed to 40px, wrapper h-10 shrink-0 overflow-hidden - selLeft/selRight clamped to Math.max(0, ...) - rulerAnchorRef/setSelectionStart/setSelectionEnd clamped >= 0
This commit is contained in:
+15
-12
@@ -1292,7 +1292,7 @@ const TempoTrackLane = ({
|
|||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
const dpr = window.devicePixelRatio || 1;
|
const dpr = window.devicePixelRatio || 1;
|
||||||
const scrollLeftVal = scrollLeft || 0;
|
const scrollLeftVal = scrollLeft || 0;
|
||||||
const height = canvas.parentElement ? canvas.parentElement.clientHeight : 40;
|
const height = 40;
|
||||||
canvas.width = Math.min(Math.round(drawWidth * dpr), 32768);
|
canvas.width = Math.min(Math.round(drawWidth * dpr), 32768);
|
||||||
canvas.height = Math.min(Math.round(height * dpr), 32768);
|
canvas.height = Math.min(Math.round(height * dpr), 32768);
|
||||||
ctx.scale(dpr, dpr);
|
ctx.scale(dpr, dpr);
|
||||||
@@ -8676,17 +8676,17 @@ const App = () => {
|
|||||||
const playheadLeftPos = useMemo(() => (currentTime + leadInMargin) * zoom, [currentTime, zoom, leadInMargin]);
|
const playheadLeftPos = useMemo(() => (currentTime + leadInMargin) * zoom, [currentTime, zoom, leadInMargin]);
|
||||||
const selLeft = useMemo(() => {
|
const selLeft = useMemo(() => {
|
||||||
if (selectionMode === 'local' && localSelectionStart !== null && localSelectionEnd !== null) {
|
if (selectionMode === 'local' && localSelectionStart !== null && localSelectionEnd !== null) {
|
||||||
return Math.min(localSelectionStart, localSelectionEnd);
|
return Math.max(0, Math.min(localSelectionStart, localSelectionEnd));
|
||||||
}
|
}
|
||||||
if (selectionStart === null || selectionEnd === null) return null;
|
if (selectionStart === null || selectionEnd === null) return null;
|
||||||
return Math.min(selectionStart, selectionEnd);
|
return Math.max(0, Math.min(selectionStart, selectionEnd));
|
||||||
}, [selectionStart, selectionEnd, selectionMode, localSelectionStart, localSelectionEnd]);
|
}, [selectionStart, selectionEnd, selectionMode, localSelectionStart, localSelectionEnd]);
|
||||||
const selRight = useMemo(() => {
|
const selRight = useMemo(() => {
|
||||||
if (selectionMode === 'local' && localSelectionStart !== null && localSelectionEnd !== null) {
|
if (selectionMode === 'local' && localSelectionStart !== null && localSelectionEnd !== null) {
|
||||||
return Math.max(localSelectionStart, localSelectionEnd);
|
return Math.max(0, Math.max(localSelectionStart, localSelectionEnd));
|
||||||
}
|
}
|
||||||
if (selectionStart === null || selectionEnd === null) return null;
|
if (selectionStart === null || selectionEnd === null) return null;
|
||||||
return Math.max(selectionStart, selectionEnd);
|
return Math.max(0, Math.max(selectionStart, selectionEnd));
|
||||||
}, [selectionStart, selectionEnd, selectionMode, localSelectionStart, localSelectionEnd]);
|
}, [selectionStart, selectionEnd, selectionMode, localSelectionStart, localSelectionEnd]);
|
||||||
|
|
||||||
const dspSelectionStats = useMemo(() => {
|
const dspSelectionStats = useMemo(() => {
|
||||||
@@ -10032,14 +10032,14 @@ const App = () => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
// Shift+click on ruler: lock existing anchor (or currentTime fallback) and extend global selection
|
// Shift+click on ruler: lock existing anchor (or currentTime fallback) and extend global selection
|
||||||
const anchor = rulerAnchorRef.current !== null && rulerAnchorRef.current !== undefined ? rulerAnchorRef.current : selectionStart !== null && selectionStart !== undefined ? selectionStart : currentTime;
|
const anchor = rulerAnchorRef.current !== null && rulerAnchorRef.current !== undefined ? rulerAnchorRef.current : selectionStart !== null && selectionStart !== undefined ? selectionStart : currentTime;
|
||||||
const selS = Math.min(anchor, time);
|
const selS = Math.max(0, Math.min(anchor, time));
|
||||||
const selE = Math.max(anchor, time);
|
const selE = Math.max(0, Math.max(anchor, time));
|
||||||
setSelectionStart(selS);
|
setSelectionStart(selS);
|
||||||
setSelectionEnd(selE);
|
setSelectionEnd(selE);
|
||||||
} else {
|
} else {
|
||||||
rulerAnchorRef.current = time;
|
rulerAnchorRef.current = Math.max(0, time);
|
||||||
setSelectionStart(time);
|
setSelectionStart(Math.max(0, time));
|
||||||
setSelectionEnd(time);
|
setSelectionEnd(Math.max(0, time));
|
||||||
}
|
}
|
||||||
handlePlayheadSet(time);
|
handlePlayheadSet(time);
|
||||||
};
|
};
|
||||||
@@ -14960,7 +14960,10 @@ const App = () => {
|
|||||||
onRulerMouseDown: handleRulerMouseDown,
|
onRulerMouseDown: handleRulerMouseDown,
|
||||||
scrollLeft: scrollLeft,
|
scrollLeft: scrollLeft,
|
||||||
canvasRedrawCount: canvasRedrawCount
|
canvasRedrawCount: canvasRedrawCount
|
||||||
}), /*#__PURE__*/React.createElement(TempoTrackLane, {
|
}), /*#__PURE__*/React.createElement("div", {
|
||||||
|
className: "h-10 shrink-0 relative overflow-hidden",
|
||||||
|
style: { height: '40px' }
|
||||||
|
}, /*#__PURE__*/React.createElement(TempoTrackLane, {
|
||||||
bpm: parseInt(bpm) || 120,
|
bpm: parseInt(bpm) || 120,
|
||||||
zoom: zoom,
|
zoom: zoom,
|
||||||
timelineWidth: timelineWidth,
|
timelineWidth: timelineWidth,
|
||||||
@@ -14970,7 +14973,7 @@ const App = () => {
|
|||||||
onRulerMouseDown: handleRulerMouseDown,
|
onRulerMouseDown: handleRulerMouseDown,
|
||||||
scrollLeft: scrollLeft,
|
scrollLeft: scrollLeft,
|
||||||
canvasRedrawCount: canvasRedrawCount
|
canvasRedrawCount: canvasRedrawCount
|
||||||
}), selectionMode === 'global' && selLeft !== null && selRight !== null && selRight > selLeft && /*#__PURE__*/React.createElement("div", {
|
})), selectionMode === 'global' && selLeft !== null && selRight !== null && selRight > selLeft && /*#__PURE__*/React.createElement("div", {
|
||||||
className: "absolute inset-0 pointer-events-none z-20",
|
className: "absolute inset-0 pointer-events-none z-20",
|
||||||
style: {
|
style: {
|
||||||
left: `${selLeft * zoom}px`,
|
left: `${selLeft * zoom}px`,
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -92,6 +92,12 @@
|
|||||||
- **Ghi chú/Test (nếu có):** `npm run build` — build passes.
|
- **Ghi chú/Test (nếu có):** `npm run build` — build passes.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### [2026-07-25 10:54] Task: Fix TempoTrackLane height + clamp selection at column 0
|
||||||
|
- **Tóm tắt thay đổi:** Fix TempoTrackLane canvas height = 40px cố định (thay vì parentElement.clientHeight gây tràn). Wrapper `h-10 shrink-0 overflow-hidden` để không đẩy track lanes lệch TCP. Clamp selLeft/selRight/ruler handlers >= 0 để selection không vào vùng âm.
|
||||||
|
- **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` — build passes.
|
||||||
|
---
|
||||||
|
|
||||||
### [2026-07-25 10:44] Task: Zero lead-in, time ruler + tempo track split layout
|
### [2026-07-25 10:44] Task: Zero lead-in, time ruler + tempo track split layout
|
||||||
- **Tóm tắt thay đổi:** Xóa leadInMargin (về 0) — time 0 tại mép trái, chỉ còn 8px PADDING_LEFT. TimelineRuler đơn giản thành time-only ruler (40px, chỉ hiển thị time labels). Thêm TempoTrackLane ngay dưới (40px, bars/beats/BPM). Selection overlay top từ 48→80px. Đồng bộ WaveformLane PADDING_LEFT=8/zoom.
|
- **Tóm tắt thay đổi:** Xóa leadInMargin (về 0) — time 0 tại mép trái, chỉ còn 8px PADDING_LEFT. TimelineRuler đơn giản thành time-only ruler (40px, chỉ hiển thị time labels). Thêm TempoTrackLane ngay dưới (40px, bars/beats/BPM). Selection overlay top từ 48→80px. Đồng bộ WaveformLane PADDING_LEFT=8/zoom.
|
||||||
- **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`
|
||||||
|
|||||||
Reference in New Issue
Block a user