FIX: sửa lỗi hiển thị số bar bị sai

This commit is contained in:
2026-08-07 15:07:06 +07:00
parent 173a1f227b
commit 58c6ec794d
4 changed files with 172 additions and 102 deletions
+131 -97
View File
@@ -13937,6 +13937,26 @@ const App = () => {
const [beginBar, setBeginBar] = useState(0);
const [endBar, setEndBar] = useState(0);
const [numberBar, setNumberBar] = useState(1);
// Quét chn (sweep tempo track/timeline) đng b bars hin th dưi
// statusbar: beginBar = bar cha selectionStart, endBar = bar cha
// selectionEnd, numberBar = s bar đưc chn. Trưc đây beginBar/endBar/
// numberBar CH đi khi nhp tay input quét không cp nht statusbar
// luôn hin th 0/0/1.
useEffect(() => {
if (selectionStart === null || selectionEnd === null || selectionEnd <= selectionStart) return;
const _beatDur = 60 / (parseInt(bpm) || 120);
const _secPerBar = _beatDur * 4;
const _b1 = Math.floor((selectionStart || 0) / _secPerBar);
// endBar = bar KT THÚC vùng quét (chm vch) KHÔNG tr 1: quét 8 bars
// t bar 0 hin th "0 đến 8" (user yêu cu) s bars = ceil floor
// (8 0 = 8 KHÔNG cng 1).
const _b2 = Math.ceil((selectionEnd || 0) / _secPerBar);
if (_b2 > _b1) {
setBeginBar(_b1);
setEndBar(_b2);
setNumberBar(_b2 - _b1);
}
}, [selectionStart, selectionEnd, bpm]);
const [subTabHeight, setSubTabHeight] = useState(96);
const [isExporting, setIsExporting] = useState(false);
const [projectName, setProjectName] = useState(() => localStorage.getItem('sonic_project_name') || '');
@@ -25880,97 +25900,18 @@ STRICT CONSTRAINTS:
}, "4")), /*#__PURE__*/React.createElement("div", {
className: "w-[1px] h-6 bg-zinc-800 mx-1.5"
}), /*#__PURE__*/React.createElement("span", {
className: "text-[14px] text-zinc-500 font-bold"
}, "Bars:"), /*#__PURE__*/React.createElement("input", {
type: "number",
min: "0",
value: beginBar,
onChange: e => {
const b = parseInt(e.target.value) || 0;
setBeginBar(b);
const beatDuration = 60 / parseInt(bpm || 120);
const t = b * beatDuration * 4;
clearLocalSelection();
setSelectionMode('global');
setSelectionStart(t);
setSelectionEnd(t + beatDuration * 4);
},
className: "w-14 bg-black text-white text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"
}), /*#__PURE__*/React.createElement("span", {
className: "text-[14px] text-zinc-500"
}, "-"), /*#__PURE__*/React.createElement("input", {
type: "number",
min: "0",
value: endBar,
onChange: e => {
const b = parseInt(e.target.value) || 0;
setEndBar(b);
const beatDuration = 60 / parseInt(bpm || 120);
const t = b * beatDuration * 4;
setSelectionEnd(t + beatDuration * 4);
setNumberBar(b - beginBar + 1);
},
className: "w-14 bg-black text-white text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"
}), /*#__PURE__*/React.createElement("span", {
className: "text-[14px] text-zinc-500"
}, "#"), /*#__PURE__*/React.createElement("input", {
type: "number",
min: "1",
value: numberBar,
readOnly: true,
className: "w-14 bg-black text-zinc-400 text-[14px] px-1 py-0.5 rounded border border-zinc-800 text-center font-mono"
}), /*#__PURE__*/React.createElement("button", {
onClick: () => setSelectionFollowsTempo(prev => !prev),
className: `px-1.5 py-0.5 text-[14px] rounded border font-bold ${selectionFollowsTempo ? 'bg-amber-700 text-white border-amber-500' : 'bg-zinc-800 text-zinc-400 border-zinc-700'}`,
title: selectionFollowsTempo ? "Selection theo tempo (đổi BPM → selection thay đổi)" : "Selection theo thời gian (cố định)"
}, selectionFollowsTempo ? "♪T" : "⏱T"), /*#__PURE__*/React.createElement("div", {
className: "w-[1px] h-6 bg-zinc-800 mx-1.5"
}), /*#__PURE__*/React.createElement("span", {
className: "text-[14px] text-zinc-500"
}, "Start:"), /*#__PURE__*/React.createElement("input", {
type: "text",
value: selLeft !== null ? formatTime(selLeft) : '',
onChange: e => {
const parts = e.target.value.split(/[:.]/);
if (parts.length === 3) {
const secs = parseInt(parts[0]) * 60 + parseInt(parts[1]) + parseFloat('0.' + (parts[2] || '0'));
clearLocalSelection();
setSelectionMode('global');
setSelectionStart(secs);
}
},
className: "w-20 bg-black text-zinc-200 text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"
}), /*#__PURE__*/React.createElement("span", {
className: "text-[14px] text-zinc-500"
}, "End:"), /*#__PURE__*/React.createElement("input", {
type: "text",
value: selRight !== null ? formatTime(selRight) : '',
onChange: e => {
const parts = e.target.value.split(/[:.]/);
if (parts.length === 3) {
const secs = parseInt(parts[0]) * 60 + parseInt(parts[1]) + parseFloat('0.' + (parts[2] || '0'));
setSelectionEnd(secs);
}
},
className: "w-20 bg-black text-zinc-200 text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"
}), /*#__PURE__*/React.createElement("span", {
className: "text-[14px] text-zinc-500"
}, "Len:"), /*#__PURE__*/React.createElement("input", {
type: "text",
value: selLeft !== null && selRight !== null ? formatTime(Math.abs(selRight - selLeft)) : '',
onChange: e => {
const parts = e.target.value.split(/[:.]/);
if (parts.length === 3 && selLeft !== null) {
const secs = parseInt(parts[0]) * 60 + parseInt(parts[1]) + parseFloat('0.' + (parts[2] || '0'));
setSelectionEnd(selLeft + secs);
}
},
className: "w-20 bg-black text-amber-300 text-[14px] px-1 py-0.5 rounded border border-zinc-700 text-center font-mono"
}), /*#__PURE__*/React.createElement("div", {
className: "text-[20px] text-zinc-500 font-bold ml-1"
}, "Time:"), /*#__PURE__*/React.createElement("span", {
className: "font-mono text-[20px] text-cyan-300 font-bold tabular-nums",
title: "Vị trí playhead"
}, formatTime(currentTime)), /*#__PURE__*/React.createElement("span", {
className: "text-[20px] text-zinc-600 font-mono"
}, "/"), /*#__PURE__*/React.createElement("span", {
className: "font-mono text-[20px] text-zinc-400 tabular-nums",
title: "Tổng thời gian dự án"
}, formatTime(projectEnd)), /*#__PURE__*/React.createElement("div", {
className: "flex-1"
}), /*#__PURE__*/React.createElement("div", {
className: "text-xs font-bold font-mono text-zinc-100"
}, formatTime(currentTime))), (() => {
})),(() => {
const dockPanels = {
top: [],
right: [],
@@ -26492,8 +26433,10 @@ STRICT CONSTRAINTS:
setEndBar(b);
const beatDuration = 60 / parseInt(bpm || 120);
const t = b * beatDuration * 4;
setSelectionEnd(t + beatDuration * 4);
setNumberBar(b - beginBar + 1);
// selectionEnd = vch bar b (không +1 bar) khp hin th endBar
// (quét 8 bars hin th "0 đến 8"): nhp 8 selection ti vch 8.
setSelectionEnd(t);
setNumberBar(Math.max(0, b - beginBar));
},
className: "w-full bg-[#242424] text-white text-center font-mono text-xs rounded py-0.5 border border-zinc-700 focus:outline-none"
})), /*#__PURE__*/React.createElement("div", {
@@ -27853,7 +27796,7 @@ STRICT CONSTRAINTS:
active: showMediaExplorer
}))));
})(), /*#__PURE__*/React.createElement("div", {
className: "h-6 bg-[#141414] border-t border-zinc-900 px-4 flex items-center justify-between text-xs text-zinc-500 select-none shrink-0"
className: "h-9 bg-[#141414] border-t border-zinc-900 px-4 flex items-center justify-between text-[18px] text-zinc-500 select-none shrink-0"
}, /*#__PURE__*/React.createElement("div", {
className: "flex items-center gap-4"
}, /*#__PURE__*/React.createElement("span", null, "Status: ", isPlaying ? 'Playing' : 'Stopped'), activeTab !== 'main' && /*#__PURE__*/React.createElement("span", {
@@ -27861,16 +27804,107 @@ STRICT CONSTRAINTS:
}, "Sub-Tab"), activeTab === 'main' && /*#__PURE__*/React.createElement("span", {
className: "text-cyan-400 font-semibold uppercase"
}, "Track: ID ", selectedTrackId), selectionMode === 'local' && /*#__PURE__*/React.createElement("span", {
className: "text-amber-400 font-semibold uppercase text-xs"
className: "text-amber-400 font-semibold uppercase text-[18px]"
}, "Local Sel"), selectionMode === 'global' && /*#__PURE__*/React.createElement("span", {
className: "text-purple-400 font-semibold uppercase text-xs"
className: "text-purple-400 font-semibold uppercase text-[18px]"
}, "Global Sel"), hasAnySolo && /*#__PURE__*/React.createElement("span", {
className: "text-amber-500 font-semibold"
}, "Solo: ", tracks.filter(t => t.solo).length, " track(s)"), isLoopingSelection && selectionMode === 'local' && /*#__PURE__*/React.createElement("span", {
className: "text-emerald-400 font-semibold uppercase text-xs"
className: "text-emerald-400 font-semibold uppercase text-[18px]"
}, "Solo Loop"), isLoopingSelection && selectionMode !== 'local' && /*#__PURE__*/React.createElement("span", {
className: "text-cyan-400 font-semibold uppercase text-xs"
className: "text-cyan-400 font-semibold uppercase text-[18px]"
}, "Master Loop")), /*#__PURE__*/React.createElement("div", {
className: "flex items-center gap-1.5 ml-3"
}, /*#__PURE__*/React.createElement("span", {
className: "text-[18px] font-bold text-zinc-400 uppercase"
}, "Bars:"), /*#__PURE__*/React.createElement("input", {
type: "number",
min: "0",
value: beginBar,
onChange: e => {
const b = parseInt(e.target.value) || 0;
setBeginBar(b);
const beatDuration = 60 / parseInt(bpm || 120);
const t = b * beatDuration * 4;
clearLocalSelection();
setSelectionMode('global');
setSelectionStart(t);
setSelectionEnd(t + beatDuration * 4);
},
className: "w-[60px] bg-black text-white text-[18px] px-1 py-0 rounded border border-zinc-700 text-center font-mono"
}), /*#__PURE__*/React.createElement("span", {
className: "text-[18px] text-zinc-500"
}, "-"), /*#__PURE__*/React.createElement("input", {
type: "number",
min: "0",
value: endBar,
onChange: e => {
const b = parseInt(e.target.value) || 0;
setEndBar(b);
const beatDuration = 60 / parseInt(bpm || 120);
const t = b * beatDuration * 4;
// selectionEnd = vch bar b (không +1 bar) khp hin th endBar
// (quét 8 bars hin th "0 đến 8"): nhp 8 selection ti vch 8.
setSelectionEnd(t);
setNumberBar(Math.max(0, b - beginBar));
},
className: "w-[60px] bg-black text-white text-[18px] px-1 py-0 rounded border border-zinc-700 text-center font-mono"
}), /*#__PURE__*/React.createElement("span", {
className: "text-[18px] text-zinc-500"
}, "#"), /*#__PURE__*/React.createElement("input", {
type: "number",
min: "1",
value: numberBar,
readOnly: true,
className: "w-[60px] bg-black text-zinc-400 text-[18px] px-1 py-0 rounded border border-zinc-800 text-center font-mono"
}), /*#__PURE__*/React.createElement("button", {
onClick: () => setSelectionFollowsTempo(prev => !prev),
className: `px-1.5 py-0 text-[18px] rounded border font-bold ${selectionFollowsTempo ? 'bg-amber-700 text-white border-amber-500' : 'bg-zinc-800 text-zinc-400 border-zinc-700'}`,
title: selectionFollowsTempo ? "Selection theo tempo (đổi BPM → selection thay đổi)" : "Selection theo thời gian (cố định)"
}, selectionFollowsTempo ? "♪T" : "⏱T"), /*#__PURE__*/React.createElement("div", {
className: "w-[1px] h-6 bg-zinc-800 mx-1"
}), /*#__PURE__*/React.createElement("span", {
className: "text-[18px] text-zinc-500"
}, "Start:"), /*#__PURE__*/React.createElement("input", {
type: "text",
value: selLeft !== null ? formatTime(selLeft) : '',
onChange: e => {
const parts = e.target.value.split(/[:.]/);
if (parts.length === 3) {
const secs = parseInt(parts[0]) * 60 + parseInt(parts[1]) + parseFloat('0.' + (parts[2] || '0'));
clearLocalSelection();
setSelectionMode('global');
setSelectionStart(secs);
}
},
className: "w-24 bg-black text-zinc-200 text-[18px] px-1 py-0 rounded border border-zinc-700 text-center font-mono"
}), /*#__PURE__*/React.createElement("span", {
className: "text-[18px] text-zinc-500"
}, "End:"), /*#__PURE__*/React.createElement("input", {
type: "text",
value: selRight !== null ? formatTime(selRight) : '',
onChange: e => {
const parts = e.target.value.split(/[:.]/);
if (parts.length === 3) {
const secs = parseInt(parts[0]) * 60 + parseInt(parts[1]) + parseFloat('0.' + (parts[2] || '0'));
setSelectionEnd(secs);
}
},
className: "w-24 bg-black text-zinc-200 text-[18px] px-1 py-0 rounded border border-zinc-700 text-center font-mono"
}), /*#__PURE__*/React.createElement("span", {
className: "text-[18px] text-zinc-500"
}, "Len:"), /*#__PURE__*/React.createElement("input", {
type: "text",
value: selLeft !== null && selRight !== null ? formatTime(Math.abs(selRight - selLeft)) : '',
onChange: e => {
const parts = e.target.value.split(/[:.]/);
if (parts.length === 3 && selLeft !== null) {
const secs = parseInt(parts[0]) * 60 + parseInt(parts[1]) + parseFloat('0.' + (parts[2] || '0'));
setSelectionEnd(selLeft + secs);
}
},
className: "w-24 bg-black text-amber-300 text-[18px] px-1 py-0 rounded border border-zinc-700 text-center font-mono"
})), /*#__PURE__*/React.createElement("div", {
className: "flex items-center gap-2"
}, /*#__PURE__*/React.createElement("button", {
onClick: () => setShowExportPanel(p => !p),