diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..d5fe1d7 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,222 @@ +# Kế hoạch phát triển SonicForge Studio + +> Nguyên tắc chung: **Giữ nguyên UI đã thiết kế**, chỉ bổ sung/bổ khuyết các thành phần còn thiếu. +> Mọi thay đổi phải tương thích với code hiện tại (backend FastAPI + frontend React/Babel trong `index.html`). + +--- + +## 0. Khảo sát hiện trạng (đã phân tích) + +| Hạng mục | Trạng thái | Ghi chú | +|---|---|---| +| Backend Auth (login/register/change-password/profile) | ✅ Sẵn sàng | `app/api/v1/auth.py` | +| Mock password admin | ✅ `seed_admin()` | Mật khẩu mặc định `admin123`, `must_change_password=1` | +| Quota / System Manager API | ✅ Sẵn sàng | `app/api/v1/admin.py`, `app/api/v1/projects.py` | +| Phân tích Stereo/Mono | ✅ Sẵn sàng | `audioEngine.analyzeAudioBufferChannels()` | +| Auth UI (AuthModal/ProfileModal/SystemManagerModal) | ✅ Sẵn sàng | `app/static/js/components/*` | +| Temp project (local + cloud) | ✅ Sẵn sàng | `storage.scheduleTempAutoSave()`, API `/projects/temp`, `/projects/cloud` | +| Export/Import `.sfs` | ⚠️ Có nhưng thiếu double-click mở lại | `storage.exportProjectToSFS/importProjectFromSFSFile` | +| Mock audioclip trong dự án | ❌ Cần xóa | `index.html:1876-1902` | +| Sub-tab: trục tọa độ channel/volume/panning + zoom rõ nét | ❌ Thiếu | Cần bổ sung theo ảnh đính kèm 1 | + +--- + +## 1. Xóa audioclip mock trong dự án + +**Mục tiêu:** Khởi tạo project trống, không có clip mẫu nào khi mở ứng dụng. + +**Thay đổi (`app/templates/index.html`):** +- Xóa hàm `createMockAudioBufferObj` (line ~1859) và biến `mockBuffer` (line ~1876) — chỉ giữ lại nếu dùng chỗ khác (hiện chỉ phục vụ mock clip). +- Sửa initial `tracks` state (line ~1880): Track 01 (`id:'1'`) để `buffer: null`, `name: 'Track 01'`, bỏ mảng `clips` mock. Giữ Track 02 rỗng như hiện tại. +- Đồng bộ `handleNewProject` (Ctrl+N, line ~2499 & 5319) đã dùng track rỗng — không đổi. +- Đảm bảo không còn tham chiếu `mockBuffer`/`createMockAudioBufferObj` nào khác (grep xác nhận trước khi xóa). + +**Kiểm chứng:** Mở app → 2 track trống, không có waveform mẫu, không có clip `Creak_DeepWood2.wav`. + +--- + +## 2. Phân tích Stereo/Mono khi import & chỉnh sửa đúng loại + +**Mục tiêu:** Khi import audio (main session hoặc sub-tab), tự động phát hiện Stereo/Mono và DSP (volume/panning/fade/stretch) phải hoạt động đúng số kênh thực tế. + +**Frontend (`app/templates/index.html`):** +- Hàm import audio (decoded bằng `window.SonicAudio.decodeAudioFile`) đã trả về `{ audioBuffer, channelInfo }` với `channelInfo = { channels, isStereo, label }`. +- Khi gán vào track / sub-tab: lưu `channelInfo` vào đối tượng track và sub-tab (`track.channelInfo`, `st.channelInfo`). Hiển thị badge `STEREO`/`MONO` trên TCP panel (giữ nguyên vị trí hiển thị hiện tại). +- **Sub-tab DSP:** + - Nếu `isStereo` → cho phép kéo Volume (L/R independent) và Panning (L100..R100) trên 2 kênh. + - Nếu `MONO` → ẩn/disable kênh đối xứng, chỉ 1 đường Volume, Panning khóa ở Center (vô hiệu hóa). Logic đã có sẵn trong `SubTabWaveform` (kiểm tra `channelInfo?.isStereo`) — bổ sung guard đầy đủ. +- Waveform render: vẽ đủ `numberOfChannels` kênh; với Mono chỉ vẽ 1 lane, Stereo vẽ 2 lane (L/R). + +**Backend (`app/core/sub_tab_dsp.py`):** giữ nguyên xử lý theo số kênh của buffer đầu vào (đã đúng). Chỉ đảm bảo API nhận buffer đa kênh. + +**Kiểm chứng:** Import file Mono → badge MONO, không thể pan; import Stereo → badge STEREO, pan L/R hoạt động. + +--- + +## 3. Sub-tab timeline: trục tọa độ + zoom rõ nét (theo ảnh 1) + +**Mục tiêu:** Trong sub-tab, track timeline hiển thị trục tọa độ thể hiện **Channel / Volume / Panning**, zoom in/out realtime mượt mà, cập nhật ngay khi click/thay đổi thông số. + +**Thay đổi (`app/templates/index.html` — khối sub-tab timeline, ~line 6152+):** +- Bổ sung **overlay trục tọa độ** vẽ trên canvas (giữ nguyên style UI): + - **Channel axis:** label `L` / `R` (stereo) hoặc `M` (mono) bên trái lane. + - **Volume axis:** thang dB dọc (`+3 / 0 / -15 / -30 dB`) căn chỉnh với đường `0dB` của graph volume. + - **Panning axis:** thang ngang (`L100 / C / R100`) căn chỉnh với graph panning. +- **Realtime zoom:** dùng `devicePixelRatio` (dpr) scale canvas như main timeline (`ctx.scale(dpr * (useW / timelineWidth), dpr)`) để nét khi zoom in. Đã có pattern ở `SubTabWaveform` (line ~751) — áp dụng đồng nhất cho trục tọa độ. +- Gán `requestAnimationFrame` / `useEffect` dependency `[zoom, buffer, volumeNodes, panningNodes, selectionStart, selectionEnd, currentTime]` để vẽ lại ngay khi tham số đổi (đã có sẵn trong `SubTabWaveform`, mở rộng vẽ thêm trục). +- Giữ nguyên ruler thời gian `0.00s` (đã sửa ở bước trước). + +**Kiểm chứng:** Zoom in → waveform + trục dB/pan sắc nét; kéo node volume/pan → trục cập nhật tức thì; click waveform → playhead + trục khớp. + +--- + +## 4. Quản lý người dùng & Menu File + +**Mục tiêu:** Admin đăng nhập (mock password), đổi mật khẩu, quản lý hệ thống; menu File có `Profile` (trên `Logout`) và `System Manager` (trong Profile). + +**Trạng thái đã có:** `currentUser`, `handleLogout`, `AuthModal`, `ProfileModal`, `SystemManagerModal`, `handleAuthSuccess` đã được bổ sung vào `App` (sửa lỗi `currentUser is not defined`). Backend `seed_admin` + `must_change_password` đã sẵn sàng. + +**Frontend (`index.html` — menu File, ~line 5319+ / 5728+):** +- Đảm bảo thứ tự menu File: `... → Profile → Logout`. (Đã đúng: Profile line 5729, Logout line 5730.) +- `Profile` mở `ProfileModal` (đổi password, xem quota) — đã render. +- `System Manager` hiển thị **chỉ khi `currentUser.role === 'admin'`** (đã có guard line 5728) → mở `SystemManagerModal` (quản lý user/quota). +- Auth flow bắt buộc khi login lần đầu (`isMandatoryLogin`) đã có trong `checkAuthStatus` effect. + +**Backend (`app/core/auth.py`):** `seed_admin()` dùng `DEFAULT_ADMIN_PASSWORD` (mặc định `admin123`), set `must_change_password=1`. Khi admin login → `AuthModal` mode `force_change` ép đổi pass. ✅ Không đổi. + +**Kiểm chứng:** Khởi chạy lần đầu → ép login admin/`admin123` → modal đổi mật khẩu → vào app. File menu hiện Profile (trên Logout); admin thấy thêm System Manager. + +--- + +## 5. Dự án tạm (Temp) & lưu cloud / `.sfs` + +**Mục tiêu:** Chưa lưu → auto-save tiến trình vào dự án tạm (local + server); cho phép đăng ký/đăng nhập/lưu cloud (quota); cho phép export `.sfs` về ổ cứng; double-click `.sfs` mở domain → login → tải lại dự án. + +**5.1 Temp auto-save (đã có, chuẩn hóa):** +- `storage.scheduleTempAutoSave()` lưu localStorage `sonic_temp_project` + gọi `API saveTempProject` nếu có token. ✅ Giữ nguyên. +- Đảm bảo mọi thay đổi (`tracks`, `subTabs`, `volumeNodes`, `panningNodes`, `fade*`, `speed`) đều nằm trong state được auto-save (serialize an toàn, không lưu `AudioBuffer` thô mà lưu metadata + `serverFileId`). + +**5.2 Lưu cloud (quota):** +- Backend `/projects/cloud` kiểm tra quota (`storage_limit_mb`, `max_tracks`). ✅ +- Frontend: menu File `Save to Cloud` → `handleSaveCloud` (đã có trong `app.js`) → port vào `App` trong `index.html` nếu chưa có, dùng `window.SonicAPI.saveCloudProject`. + +**5.3 Export / Import `.sfs`:** +- `exportProjectToSFS` (đã có) → download `.sfs`. ✅ +- **Bổ sung double-click mở lại:** + - Thêm vào `.sfs` JSON trường `domain` (đã có) và đăng ký MIME/association phía client: khi user double-click file `.sfs` trên máy, OS mở URL `domain/?sfs=` (hoặc protocol handler `sonicforge://open?file=...`). + - Tại `index.html` khởi tạo: đọc query param `?sfs=` → nếu có → yêu cầu login (nếu chưa) → `importProjectFromSFSFile` (đọc từ blob/server) → load tracks. + - Ghi chú: cơ chế double-click thực tế phụ thuộc OS (file association / protocol handler). Cung cấp hướng dẫn + nút "Mở dự án .sfs" trong UI làm fallback. + +**Kiểm chứng:** Sửa project → F5 → tiến trình còn (temp). Login → Save Cloud → quota đúng. Export `.sfs` → mở lại domain → login → project restored. + +--- + +## 6. Thứ tự thực hiện & kiểm thử + +1. **B1** — Xóa mock clip (§1). Chạy app, confirm trống. +2. **B2** — Stereo/Mono import + DSP (§2). Test Mono & Stereo file. +3. **B3** — Sub-tab trục tọa độ + zoom (§3). So sánh ảnh 1. +4. **B4** — User/Menu (§4). Test admin flow + role guard. +5. **B5** — Temp/Cloud/`.sfs` (§5). Test auto-save, quota, round-trip sfs. +6. **B6** — Lint/typecheck (nếu có script) + chạy `tests/` hiện có (`test_sub_tab_dsp.py`, `test_auth_and_quota.py`). + - ✅ Sửa `main.py`: thêm auth + admin + projects routers (thiếu từ đầu). + - ✅ 31/31 tests pass (auth + dsp_engine + sub_tab_dsp). + +**Không thay đổi:** Layout tổng thể, màu sắc, component giao diện đã design; chỉ bổ sung thành phần (trục, badge, modal, menu item) và sửa logic thiếu. + +--- + +## 7. Tối ưu hóa & Module hóa `index.html` (giảm latency) + +### 7.1 Hiện trạng & nguyên nhân latency + +| Vấn đề | Chi tiết | +|---|---| +| File `index.html` khổng lồ (~6784 dòng) chứa TOÀN BỘ UI inline trong 1 thẻ ` + + +