FIX: sửa lỗi hiển thị số bar bị sai
This commit is contained in:
+131
-97
@@ -13937,6 +13937,26 @@ const App = () => {
|
||||
const [beginBar, setBeginBar] = useState(0);
|
||||
const [endBar, setEndBar] = useState(0);
|
||||
const [numberBar, setNumberBar] = useState(1);
|
||||
// Quét chọn (sweep — tempo track/timeline) → đồng bộ bars hiển thị dưới
|
||||
// statusbar: beginBar = bar chứa selectionStart, endBar = bar chứa
|
||||
// selectionEnd, numberBar = số bar được chọn. Trước đây beginBar/endBar/
|
||||
// numberBar CHỈ đổi khi nhập tay input — quét không cập nhật → statusbar
|
||||
// luôn hiển 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 KẾT THÚC vùng quét (chạm vạch) — KHÔNG trừ 1: quét 8 bars
|
||||
// từ bar 0 → hiển thị "0 đến 8" (user yêu cầu) — số bars = ceil − floor
|
||||
// (8 − 0 = 8 — KHÔNG cộng 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 = vạch bar b (không +1 bar) — khớp hiển thị endBar
|
||||
// (quét 8 bars hiển thị "0 đến 8"): nhập 8 → selection tới vạch 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 = vạch bar b (không +1 bar) — khớp hiển thị endBar
|
||||
// (quét 8 bars hiển thị "0 đến 8"): nhập 8 → selection tới vạch 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),
|
||||
|
||||
Reference in New Issue
Block a user