# Kế hoạch nâng cấp chức năng Piano Roll, quản lý tab và cơ chế Lưu (Save) cho Section/MIDI items

Tài liệu này đề xuất các chỉnh sửa lớn cho hệ thống **Piano Roll** và **Section Tabs** để tăng trải nghiệm soạn nhạc (DAW workflow):

---

## Các yêu cầu chức năng cần bổ sung

### 1. Quản lý Tab & Tên Tab
- **Gán tên tab**: Khi mở Piano Roll, gán tên tab hiển thị là `Piano Roll: [Tên MIDI Item]` để khớp chính xác.
- **Không mất dữ liệu khi mở Section Tab**: Khi mở thêm Section tab mới, trạng thái và các nốt nhạc đang sửa đổi trong tab Piano Roll phải được giữ nguyên trong React state (`subTabs`), không bị xoá hay reset.
- **Tạo tab con cho Section Tab**: Nếu mở MIDI item thuộc một Section-tab, hệ thống sẽ tạo một tab Piano Roll tương ứng (nhận `parent_tab_id` là Section tab đó) để quản lý độc lập.
- **Mở lại tab cũ**: Khi kích hoạt chỉnh sửa MIDI item đã có tab Piano Roll mở sẵn, chuyển đổi `activeTab` về tab đó mà không tạo thêm tab mới trùng lặp.

### 2. Cơ chế Lưu (Save) thủ công
- **Lưu nốt MIDI**: Thay vì tự động cập nhật nốt nhạc thời gian thực về track gốc làm nặng ứng dụng, các chỉnh sửa nốt nhạc trong Piano Roll sẽ lưu cục bộ trong tab. Khi nhấn nút **"Lưu"** (Save) trên Piano Roll tab, nốt nhạc mới được cập nhật vào MIDI item cha.
- **Lưu Section Tab**: Bổ sung nút **"Lưu Section"** trên thanh công cụ của Section Tab. Khi bấm, tên và thời lượng (duration) của Section Tab sẽ cập nhật ngược lại vào block Section item ở **Main Session**.

### 3. Cải tiến tương tác Chuột trong Piano Roll
- **Ctrl + Click**: Vẽ nhanh 1 nốt nhạc mới tại tọa độ click (áp dụng snap).
- **Nhấp & Kéo (Click & Drag) ở khoảng trống**: Quét khung chọn (marquee selection) để chọn nhiều nốt cùng lúc.
- **Kéo cạnh phải nốt**: Thay đổi thời lượng nốt nhạc (Resize).
- **Kéo phần thân nốt**: Di chuyển nốt nhạc theo cao độ (pitch) và thời gian (beat). Nếu có nhiều nốt đang được chọn, di chuyển toàn bộ các nốt được chọn cùng nhau (multi-note move).
- **Shift + Cuộn chuột (Scroll)**:
  - Nếu cuộn chuột trên 1 nốt nhạc: Tăng/giảm velocity của nốt đó (khoảng cách 0.05, giới hạn từ 0.1 đến 1.0).
  - Nếu cuộn chuột ngoài khoảng trống khi có các nốt được chọn: Tăng/giảm velocity của toàn bộ các nốt đang được chọn.
- **Click chuột phải**: Xóa nhanh note

### 4. Thêm Thanh thước nhịp (Bar Ruler)
- Bổ sung thanh thước nhịp nằm phía trên lưới Piano Roll, hiển thị nhịp số bắt đầu từ `Bar 0`, `Bar 1`, `Bar 2`,...
- Thanh thước nhịp sẽ tự động cuộn ngang đồng bộ với lưới Piano Roll khi người dùng cuộn chuột ngang.

---

## Đề xuất thay đổi mã nguồn

### [app.jsx](file:///home/locpham/SonicForgeStudio/app/static/js/app.jsx)

#### A. Cấu trúc lại `handleUpdateMidiNotes` và thêm `handleSaveMidiNotes`
- Tách biệt cập nhật cục bộ và lưu trữ về MIDI item gốc.

#### B. Thêm nút "Lưu Section" và "Lưu MIDI Notes"
- Hiển thị nút **Lưu Section** trên thanh công cụ khi `activeTab` là Section Tab.
- Hiển thị nút **Lưu** màu xanh lá trên thanh điều khiển của `PianoRollTabEditor`.

#### C. Viết lại `PianoRollTabEditor` hỗ trợ sự kiện chuột
- Quản lý trạng thái `selectedNoteIds` và `selectionMarquee`.
- Thêm sự kiện `wheel` hỗ trợ `Shift+Scroll` thay đổi velocity.
- Vẽ khung chọn `selectionMarquee` trên canvas.
- Thêm component / thẻ div Bar Ruler phía trên canvas và đồng bộ hóa cuộn (`onScroll` cập nhật `rulerScrollRef.current.scrollLeft`).

---

## Kế hoạch xác minh

### Kiểm tra tự động
- Chạy `PYTHONPATH=. pytest` để đảm bảo logic lưu trữ phía Python không bị ảnh hưởng.

### Kiểm tra thủ công
1. **Kiểm tra tab & lưu trữ**:
   - Bật Section Tab, tạo một MIDI item. Double-click để mở Piano Roll của nó.
   - Sửa đổi nốt trong Piano Roll, chuyển qua lại giữa Main Session, Section Tab và Piano Roll. Xác minh nốt không bị mất.
   - Bấm **Lưu** trong Piano Roll -> Nhìn ngoài timeline thấy MIDI item cập nhật nốt.
   - Bấm **Lưu Section** -> Nhìn ngoài Main Session thấy Section item cập nhật tên/độ dài.
2. **Kiểm tra thao tác chuột**:
   - Kiểm tra quét chuột chọn nhiều nốt, kéo di chuyển cả cụm.
   - Kiểm tra `Ctrl+Click` vẽ nốt.
   - Kiểm tra `Shift+Scroll` thay đổi độ đậm nhạt (velocity) của nốt.
   - Kiểm tra thanh Bar Ruler hiển thị `Bar 0, Bar 1...` cuộn mượt mà.
