package browser import ( "fmt" "path/filepath" "time" "pad/internal/io/pool" "pad/internal/io/pool/mock" ) var ErrDirNotFound = fmt.Errorf("directory not found") type BrowserManager struct { state *BrowserState workerPool *pool.WorkerPool fs *mock.FileSystem dirPath string } func NewBrowserManager(state *BrowserState, wp *pool.WorkerPool, fs *mock.FileSystem) (*BrowserManager, error) { return &BrowserManager{ state: state, workerPool: wp, fs: fs, }, nil } func (bm *BrowserManager) NavigateTo(dirPath string) { fmt.Printf("NavigateTo: %s\n", dirPath) bm.dirPath = dirPath navigateToDirectory(bm.state, dirPath) bm.state.Loading = true // Dispatch BuildIndexTask to the worker pool. // In a real app, this would be a task that reads and sorts the directory. task := pool.NewBuildIndexTask(dirPath, bm.fs) bm.workerPool.Dispatch(task) } func (bm *BrowserManager) OnScroll() { if bm.state.TotalEntries == 0 { return } // Identify unloaded pages in the visible + prefetch range minPage, maxPage := computeVisiblePageRange(bm.state) var pagesToLoad []int for i := minPage; i <= maxPage; i++ { if page, ok := bm.state.Pages[i]; !ok || !page.Loaded { pagesToLoad = append(pagesToLoad, i) } } fmt.Printf("OnScroll: minPage=%d, maxPage=%d, pagesToLoad=%v\n", minPage, maxPage, pagesToLoad) // Dispatch LoadPagesTask for unloaded pages if len(pagesToLoad) > 0 { fmt.Printf("OnScroll: Dispatching LoadPagesTask for pages %v\n", pagesToLoad) task := pool.NewLoadPagesTask(bm.dirPath, pagesToLoad, bm.fs) bm.workerPool.Dispatch(task) } // Evict distant pages to save memory bm.state.EvictPages() } func (bm *BrowserManager) HandleResult(result pool.Result) { if !result.IsBrowserResult() { return } switch result.TaskType { case pool.TypeBuildIndex: if result.Success { bm.handleBuildIndexSuccess(result) } else { bm.handleError(result) } case pool.TypeLoadPages: if result.Success { bm.handleLoadPagesSuccess(result) } else { bm.handleError(result) } } } func (bm *BrowserManager) handleBuildIndexSuccess(result pool.Result) { fmt.Printf("handleBuildIndexSuccess: TotalEntries=%d\n", len(result.Data.([]mock.DirEntry))) bm.state.Loading = false // In the mock implementation, BuildIndexTask returns []mock.DirEntry. // We need to convert these to browser.Entry and build the SortIndex. entries := result.Data.([]mock.DirEntry) var browserEntries []Entry // Add ".." entry if not at root if bm.dirPath != "/" && bm.dirPath != "." { browserEntries = append(browserEntries, Entry{ Path: filepath.Dir(bm.dirPath), Name: "..", Size: 0, ModTime: time.Now(), IsDir: true, }) } for _, e := range entries { info, _ := e.Info() browserEntries = append(browserEntries, Entry{ Path: filepath.Join(bm.dirPath, e.Name()), Name: e.Name(), Size: info.Size(), ModTime: info.ModTime(), IsDir: e.IsDir(), }) } // Build the sorted index with position maps bm.state.SortIndex = &DirectoryIndex{ Path: bm.dirPath, EntryCount: len(browserEntries), Entries: browserEntries, SortOrders: make(map[string][]int), } // Compute position maps for all sort modes for mode := SortMode(0); mode < SortMode(modeCount()); mode++ { key := sortModeKey(mode) if key != "" { bm.state.SortIndex.SortOrders[key] = buildPositionMap(browserEntries, mode) } } bm.state.TotalEntries = len(browserEntries) fmt.Printf("handleBuildIndexSuccess: TotalEntries set to %d\n", bm.state.TotalEntries) // Trigger initial page load bm.OnScroll() } func (bm *BrowserManager) handleLoadPagesSuccess(result pool.Result) { // In a real implementation, LoadPagesTask would return the actual entry data. // In our current mock/placeholder, it just returns the page indices. // We'll load the data from the SortIndex. pageIndices := result.Data.([]int) for _, idx := range pageIndices { page := loadPageFromIndex(bm.state, idx) if page != nil { bm.state.Pages[idx] = page } } } func (bm *BrowserManager) handleError(result pool.Result) { fmt.Printf("handleError: TaskType=%s, Error=%v\n", result.TaskType, result.Error) bm.state.Loading = false // For now, just reset the state or log the error. // In production, we'd show a toast or error message. if bm.state.CurrentPath == bm.dirPath { bm.state.Reset() } }