# Asset Browser Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add a file system asset browser panel to the editor that displays files/folders in a flat list with navigation. **Architecture:** `AssetBrowser` struct manages directory state (current path, entries cache). `asset_browser_panel` function renders the UI. Uses `std::fs::read_dir` for file listing. No external dependencies needed. **Tech Stack:** Rust, std::fs, voltex_editor (UiContext, Rect, LayoutState, widgets) **Spec:** `docs/superpowers/specs/2026-03-26-asset-browser-design.md` --- ### Task 1: AssetBrowser struct + logic + tests **Files:** - Create: `crates/voltex_editor/src/asset_browser.rs` - Modify: `crates/voltex_editor/src/lib.rs` - [ ] **Step 1: Write tests and implementation** Create `crates/voltex_editor/src/asset_browser.rs` with the full AssetBrowser implementation and tests. The struct handles directory scanning, navigation, and path safety. Key implementation details: - `new(root)` → canonicalize root, set current=root, refresh - `refresh()` → read_dir, collect into Vec, sort (dirs first, then by name) - `navigate_to(dir_name)` → join path, check starts_with(root) && is_dir, refresh - `go_up()` → parent path, check >= root, refresh - `relative_path()` → strip_prefix(root) display - `format_size(bytes)` → "B" / "KB" / "MB" formatting Tests using `std::fs` temp directories: - test_new_scans_entries - test_navigate_to - test_go_up - test_go_up_at_root - test_root_guard - test_entries_sorted (dirs before files) - test_format_size - [ ] **Step 2: Add module to lib.rs** ```rust pub mod asset_browser; pub use asset_browser::{AssetBrowser, asset_browser_panel}; ``` - [ ] **Step 3: Run tests** Run: `cargo test -p voltex_editor --lib asset_browser -- --nocapture` Expected: all tests PASS - [ ] **Step 4: Commit** ```bash git add crates/voltex_editor/src/asset_browser.rs crates/voltex_editor/src/lib.rs git commit -m "feat(editor): add AssetBrowser with directory navigation and file listing" ``` --- ### Task 2: asset_browser_panel UI function **Files:** - Modify: `crates/voltex_editor/src/asset_browser.rs` - [ ] **Step 1: Write test** ```rust #[test] fn test_panel_draws_commands() { let dir = std::env::temp_dir().join("voltex_ab_ui_test"); let _ = std::fs::create_dir_all(&dir); std::fs::write(dir.join("test.txt"), "hello").unwrap(); let mut browser = AssetBrowser::new(dir.clone()); let mut ui = UiContext::new(800.0, 600.0); let rect = Rect { x: 0.0, y: 0.0, w: 300.0, h: 400.0 }; ui.begin_frame(0.0, 0.0, false); asset_browser_panel(&mut ui, &mut browser, &rect); ui.end_frame(); assert!(ui.draw_list.commands.len() > 0); let _ = std::fs::remove_dir_all(&dir); } ``` - [ ] **Step 2: Implement asset_browser_panel** ```rust pub fn asset_browser_panel( ui: &mut UiContext, browser: &mut AssetBrowser, rect: &Rect, ) { ui.layout = LayoutState::new(rect.x + PADDING, rect.y + PADDING); // Current path let path_text = format!("Path: /{}", browser.relative_path()); ui.text(&path_text); // Go up button (not at root) if browser.current != browser.root { if ui.button("[..]") { browser.go_up(); return; // entries changed, redraw next frame } } // Entry list let gw = ui.font.glyph_width as f32; let gh = ui.font.glyph_height as f32; let line_h = gh + PADDING; let mut clicked_dir: Option = None; for entry in browser.entries() { let y = ui.layout.cursor_y; let x = rect.x + PADDING; // Highlight selected file if !entry.is_dir { if browser.selected_file.as_deref() == Some(&entry.name) { ui.draw_list.add_rect(rect.x, y, rect.w, line_h, COLOR_SELECTED); } } // Click detection if ui.mouse_clicked && ui.mouse_in_rect(rect.x, y, rect.w, line_h) { if entry.is_dir { clicked_dir = Some(entry.name.clone()); } else { browser.selected_file = Some(entry.name.clone()); } } // Draw label let label = if entry.is_dir { format!("[D] {}", entry.name) } else { format!(" {}", entry.name) }; let text_y = y + (line_h - gh) * 0.5; let mut cx = x; for ch in label.chars() { let (u0, v0, u1, v1) = ui.font.glyph_uv(ch); ui.draw_list.add_rect_uv(cx, text_y, gw, gh, u0, v0, u1, v1, COLOR_TEXT); cx += gw; } ui.layout.cursor_y += line_h; } // Navigate after iteration (avoids borrow conflict) if let Some(dir) = clicked_dir { browser.navigate_to(&dir); } // Selected file info if let Some(ref file_name) = browser.selected_file.clone() { ui.text("-- File Info --"); ui.text(&format!("Name: {}", file_name)); if let Some(entry) = browser.entries.iter().find(|e| e.name == *file_name) { ui.text(&format!("Size: {}", format_size(entry.size))); if let Some(ext) = file_name.rsplit('.').next() { if file_name.contains('.') { ui.text(&format!("Type: .{}", ext)); } } } } } ``` Note: `selected_file` needs to be `pub(crate)` or accessed via a method. Also `entries` field needs to be accessible. Simplest: make `selected_file` and `entries` pub. - [ ] **Step 3: Run tests** Run: `cargo test -p voltex_editor --lib asset_browser -- --nocapture` Expected: all tests PASS - [ ] **Step 4: Commit** ```bash git add crates/voltex_editor/src/asset_browser.rs git commit -m "feat(editor): add asset_browser_panel UI function" ``` --- ### Task 3: Integrate into editor_demo **Files:** - Modify: `examples/editor_demo/src/main.rs` - [ ] **Step 1: Replace Console panel with AssetBrowser** Add import: ```rust use voltex_editor::{..., AssetBrowser, asset_browser_panel}; ``` Add to AppState: ```rust asset_browser: AssetBrowser, ``` In `resumed`, initialize: ```rust let asset_browser = AssetBrowser::new(std::env::current_dir().unwrap_or_default()); ``` In the panel loop, replace panel 3 (Console): ```rust 3 => { asset_browser_panel(&mut state.ui, &mut state.asset_browser, rect); } ``` - [ ] **Step 2: Build and test** Run: `cargo build -p editor_demo` Run: `cargo test -p voltex_editor` - [ ] **Step 3: Commit** ```bash git add examples/editor_demo/src/main.rs git commit -m "feat(editor): integrate asset browser into editor_demo" ``` --- ### Task 4: Update docs **Files:** - Modify: `docs/STATUS.md` - Modify: `docs/DEFERRED.md` - [ ] **Step 1: Update STATUS.md** Add: `- voltex_editor: AssetBrowser (file system browsing, directory navigation)` Update test count. - [ ] **Step 2: Update DEFERRED.md** ``` - ~~**에셋 브라우저**~~ ✅ 파일 목록 + 디렉토리 탐색 완료. 에셋 로딩/프리뷰 미구현. ``` - [ ] **Step 3: Commit** ```bash git add docs/STATUS.md docs/DEFERRED.md git commit -m "docs: update STATUS.md and DEFERRED.md with asset browser" ```