Pad/internal/browser/manager.go
Greg Pomerantz beae5fc026 Fix directory navigation and path initialization
- Initialize browser path to the actual startup directory instead of '/'
- Improve '..' navigation to be relative to the filesystem root
- Fix path duplication issues by avoiding redundant absolute path joining
2026-06-05 13:43:27 -04:00

168 lines
4.5 KiB
Go

package browser
import (
"fmt"
"path/filepath"
"time"
"pad/internal/io/pool"
"pad/internal/io/pool/types"
)
var ErrDirNotFound = fmt.Errorf("directory not found")
type BrowserManager struct {
state *BrowserState
workerPool *pool.WorkerPool
fs pool.FileSystem
}
func NewBrowserManager(state *BrowserState, wp *pool.WorkerPool, fs pool.FileSystem) (*BrowserManager, error) {
return &BrowserManager{
state: state,
workerPool: wp,
fs: fs,
}, nil
}
func (bm *BrowserManager) NavigateTo(dirPath string) {
fmt.Printf("NavigateTo: dirPath=%s, state.CurrentPath=%s\n", dirPath, bm.state.CurrentPath)
bm.state.CurrentPath = 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.state.CurrentPath, 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) {
bm.state.Loading = false
// In the mock implementation, BuildIndexTask returns []types.DirEntry.
// We need to convert these to browser.Entry and build the SortIndex.
entries := result.Data.([]types.DirEntry)
var browserEntries []Entry
// Add ".." entry if not at root
// We check against "/" specifically. "." is just a relative path for current dir,
// which still has a parent.
if bm.state.CurrentPath != "/" && bm.state.CurrentPath != "" {
browserEntries = append(browserEntries, Entry{
Path: filepath.Dir(bm.state.CurrentPath),
Name: "..",
Size: 0,
ModTime: time.Now(),
IsDir: true,
})
}
for _, e := range entries {
info, _ := e.Info()
fmt.Printf("Adding entry: %s\n", e.Name())
browserEntries = append(browserEntries, Entry{
Path: filepath.Join(bm.state.CurrentPath, 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.state.CurrentPath,
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=%d, 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.state.CurrentPath {
bm.state.Reset()
}
}