Add Bubble Tea TUI with interactive treemap
Cell canvas renderer with frame cache and a perceptual 256-colour quantizer; responsive geometry (large/medium/small/too-small); squarified treemap with nested previews, LOD grouping, category colouring and adaptive labels; spatial arrow navigation with stable NodeID selection; zoom and breadcrumbs; modal system (info, help, confirm, save); list, file-type, top-N, scan-info, duplicate and comparison views; live search/filter; mouse support; move-to-trash.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
package tui
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/andydixon/mapsize/internal/inventory"
|
||||
"github.com/andydixon/mapsize/internal/scan"
|
||||
)
|
||||
|
||||
// TestLiveScanRendering renders, navigates, filters and resizes while the
|
||||
// scanner mutates the inventory. Run with -race to verify the locking
|
||||
// discipline.
|
||||
func TestLiveScanRendering(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
for i := range 30 {
|
||||
d := filepath.Join(root, fmt.Sprintf("d%02d", i), "sub")
|
||||
if err := os.MkdirAll(d, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for j := range 40 {
|
||||
os.WriteFile(filepath.Join(d, fmt.Sprintf("f%d.log", j)), make([]byte, (i+1)*(j+1)*100), 0o644)
|
||||
}
|
||||
}
|
||||
start := func(ctx context.Context) (*scan.Scanner, *inventory.Tree, error) {
|
||||
s, err := scan.Start(ctx, root, scan.Options{Workers: 4, ChunkSize: 3})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return s, s.Tree, nil
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
s, tr, err := start(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m := newModel(Options{Start: start}, s, tr, cancel)
|
||||
sizes := [][2]int{{120, 40}, {80, 24}, {200, 60}, {45, 12}}
|
||||
deadline := time.After(20 * time.Second)
|
||||
for i := 0; ; i++ {
|
||||
send(m, size(sizes[i%len(sizes)][0], sizes[i%len(sizes)][1]), tickMsg(time.Now()))
|
||||
send(m, key([]string{"right", "down", "left", "up"}[i%4]))
|
||||
if i%5 == 0 {
|
||||
send(m, key("tab"))
|
||||
}
|
||||
if i == 3 {
|
||||
m.search.ed.set("ext = log")
|
||||
m.queryChanged()
|
||||
send(m, m.applyFilter()())
|
||||
}
|
||||
m.dirty = true
|
||||
m.View()
|
||||
select {
|
||||
case <-s.Done():
|
||||
send(m, scanDoneMsg{s})
|
||||
m.View()
|
||||
if m.scanning {
|
||||
t.Fatal("scan should be finished")
|
||||
}
|
||||
return
|
||||
case <-deadline:
|
||||
t.Fatal("scan did not finish")
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user