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),
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -24,7 +24,7 @@
|
||||
<script src="/static/js/services/midiExtractor.js?v=202607281052"></script>
|
||||
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
|
||||
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202608070520" defer></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202608070535" defer></script>
|
||||
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
|
||||
<style>
|
||||
:root {
|
||||
|
||||
@@ -2509,3 +2509,27 @@
|
||||
- **Hành vi:** LOOP TẮT → playhead chạy tới END ITEMS (22.59) rồi dừng (không cắt 8s content). LOOP BẬT → playhead tới projectEnd (content end ~8s) → LOOP LẠI từ 0 → play liên tục.
|
||||
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070520), `wiki.md`. Rebuild precompiled.
|
||||
- **Ghi chú/Test:** hard refresh → (1) LOOP TẮT → play MAIN (section 22.59s, content 8s) → play tới 22.59 mới dừng; (2) bấm LOOP → play → loop liên tục tại ~8s (log `[Stop] projectEnd reached — projectEnd≈8` + loop lại từ 0).
|
||||
|
||||
### [2026-08-07 05:25] Task: Tăng size Time display (toolbar) + tăng statusbar 150%
|
||||
- **Yêu cầu user:** tăng size ô hiển thị thời gian (playhead/tổng) trên toolbar; tăng kích thước status bar lên 150%.
|
||||
- **FIX (app.jsx):**
|
||||
(1) Toolbar — ô `Time: <playhead> / <total>`: `text-[14px]` → `text-[20px]` (4 spans: label, playhead, "/", total).
|
||||
(2) Statusbar — container `h-6` → `h-9` (36px = 150%), `text-xs` → `text-[18px]`; các badge trạng thái (Local Sel/Global Sel/Solo Loop/Master Loop) `text-xs` → `text-[18px]`; Bars selection (Bars/inputs/♪T) + Time selection (Start/End/Len): `text-xs` → `text-[18px]`, input `w-10` → `w-[60px]`, `w-16` → `w-24`, divider `h-4` → `h-6`.
|
||||
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070525), `wiki.md`. Rebuild precompiled (build PASS — text-[20px] ×4, text-[18px] ×18, w-[60px] ×7, h-9 ✓).
|
||||
- **Ghi chú/Test:** hard refresh → toolbar Time to rõ (20px); statusbar cao hơn + chữ/input 18px (150%).
|
||||
|
||||
### [2026-08-07 05:30] Task: Quét chọn (tempo track) không cập nhật bars hiển thị dưới statusbar
|
||||
- **Báo cáo user:** quét ở tempo track để select duration → vị trí quét + số bars được chọn KHÔNG hiển thị ở bars dưới status bar.
|
||||
- **Nguyên nhân:** beginBar/endBar/numberBar CHỈ được set từ onChange của input (nhập tay — dock panel cũ + statusbar mới) — KHÔNG có code cập nhật từ sweep/selection → statusbar luôn hiển thị 0/0/1 khi quét.
|
||||
- **FIX (app.jsx):** thêm effect `[selectionStart, selectionEnd, bpm]` — khi selection hợp lệ (end > start): beginBar = floor(start / secPerBar); endBar = ceil(end / secPerBar) − 1; numberBar = endBar − beginBar + 1 → quét xong bars statusbar hiển thị đúng vị trí + số bar.
|
||||
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070530), `wiki.md`. Rebuild precompiled (build PASS).
|
||||
- **Ghi chú/Test:** hard refresh → quét chọn trên tempo track (vd từ bar 2 tới bar 5) → statusbar hiển thị Bars: 2 - 5 # 4; xóa selection → giữ giá trị cuối (không tự reset).
|
||||
|
||||
### [2026-08-07 05:35] Task: Bars hiển thị sai chỉ số — quét 8 bars hiển thị "0 đến 7"
|
||||
- **Báo cáo user:** số bars đúng (8) nhưng chỉ số sai: quét bar 0→8 hiển thị "0 đến 7" — cần "0 đến 8".
|
||||
- **Nguyên nhân:** effect (0530) tính `endBar = ceil(end/spb) − 1` (bar cuối INCLUSIVE) → 7; numberBar = endBar − beginBar + 1 = 8.
|
||||
- **FIX (app.jsx):**
|
||||
(1) Effect: `endBar = ceil(end/spb)` (KHÔNG −1 — bar KẾT THÚC vùng quét/chạm vạch); `numberBar = ceil − floor` (KHÔNG +1) → quét 8 bars → "0 đến 8 # 8".
|
||||
(2) Input End Bar (dock 26436 + statusbar 27846): `setSelectionEnd(t + spb)` → `setSelectionEnd(t)` (selection tới vạch bar b — khớp hiển thị); `setNumberBar(b − beginBar)`.
|
||||
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/templates/index.html` (?v=202608070535), `wiki.md`. Rebuild precompiled (build PASS).
|
||||
- **Ghi chú/Test:** hard refresh → quét 8 bars từ 0 → BARS hiển thị "0 - 8 # 8"; nhập End Bar = 8 → selection tới vạch 8 (8 bars).
|
||||
|
||||
Reference in New Issue
Block a user