From 03e974a1394e4081bcef46dee3c094e596786ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 5 Oct 2026 15:51:57 -0400 Subject: [PATCH] WIP components (as built) --- TASKS.md | 2 +- archive_viewer.go | 197 ++++++++++ archive_viewer_test.go | 187 +++++++++ code_shared.go | 8 - docs/components/381-file-explorer.md | 43 +++ docs/components/382-file-grid.md | 37 ++ docs/components/383-file-icon.md | 35 ++ docs/components/384-file-preview.md | 36 ++ docs/components/385-path-bar.md | 35 ++ docs/components/386-rename-inline.md | 36 ++ .../components/387-file-operation-progress.md | 36 ++ docs/components/388-transfer-queue.md | 37 ++ docs/components/389-storage-usage.md | 36 ++ docs/components/390-recent-files.md | 38 ++ docs/components/391-archive-viewer.md | 37 ++ docs/components/392-presence-avatars.md | 36 ++ docs/components/394-reactions.md | 36 ++ docs/components/395-share-dialog.md | 40 ++ docs/components/396-permission-select.md | 36 ++ docs/components/399-live-indicator.md | 35 ++ docs/components/400-remote-cursor.md | 40 ++ example_files_test.go | 177 +++++++++ examples/gallery/pages_files.go | 22 +- examples/gallery/pages_files_browser.go | 362 ++++++++++++++++++ examples/gallery/pages_files_code.go | 137 +++++++ examples/gallery/pages_files_collaboration.go | 195 ++++++++++ examples/gallery/pages_files_test.go | 116 ++++++ file_explorer.go | 254 ++++++++++++ file_explorer_test.go | 228 +++++++++++ file_grid.go | 129 +++++++ file_grid_test.go | 172 +++++++++ file_icon.go | 125 ++++++ file_icon_test.go | 71 ++++ file_operation_progress.go | 125 ++++++ file_operation_progress_test.go | 100 +++++ file_preview.go | 114 ++++++ file_preview_test.go | 127 ++++++ live_indicator.go | 96 +++++ live_indicator_test.go | 89 +++++ messages_files.go | 98 +++++ path_bar.go | 157 ++++++++ path_bar_test.go | 99 +++++ permission_select.go | 52 +++ permission_select_test.go | 92 +++++ presence_avatars.go | 148 +++++++ presence_avatars_test.go | 126 ++++++ reactions.go | 115 ++++++ reactions_test.go | 95 +++++ recent_files.go | 183 +++++++++ recent_files_test.go | 172 +++++++++ remote_cursor.go | 85 ++++ remote_cursor_test.go | 87 +++++ rename_inline.go | 197 ++++++++++ rename_inline_test.go | 169 ++++++++ select.go | 21 +- share_dialog.go | 142 +++++++ share_dialog_test.go | 192 ++++++++++ storage_usage.go | 150 ++++++++ storage_usage_test.go | 102 +++++ terminal.go | 6 +- .../snapshots/darwin/archive_viewer-dark.png | Bin 0 -> 28531 bytes .../snapshots/darwin/archive_viewer-light.png | Bin 0 -> 27577 bytes .../snapshots/darwin/file_explorer-dark.png | Bin 0 -> 13137 bytes .../snapshots/darwin/file_explorer-light.png | Bin 0 -> 12838 bytes testdata/snapshots/darwin/file_grid-dark.png | Bin 0 -> 13536 bytes testdata/snapshots/darwin/file_grid-light.png | Bin 0 -> 13170 bytes testdata/snapshots/darwin/file_icon-dark.png | Bin 0 -> 6364 bytes testdata/snapshots/darwin/file_icon-light.png | Bin 0 -> 6482 bytes .../darwin/file_operation_progress-dark.png | Bin 0 -> 22150 bytes .../darwin/file_operation_progress-light.png | Bin 0 -> 21468 bytes .../snapshots/darwin/file_preview-dark.png | Bin 0 -> 18371 bytes .../snapshots/darwin/file_preview-light.png | Bin 0 -> 17809 bytes .../snapshots/darwin/live_indicator-dark.png | Bin 0 -> 11436 bytes .../snapshots/darwin/live_indicator-light.png | Bin 0 -> 11059 bytes testdata/snapshots/darwin/path_bar-dark.png | Bin 0 -> 6634 bytes testdata/snapshots/darwin/path_bar-light.png | Bin 0 -> 6342 bytes .../darwin/permission_select-dark.png | Bin 0 -> 2412 bytes .../darwin/permission_select-light.png | Bin 0 -> 2308 bytes .../darwin/presence_avatars-dark.png | Bin 0 -> 4635 bytes .../darwin/presence_avatars-light.png | Bin 0 -> 4586 bytes testdata/snapshots/darwin/reactions-dark.png | Bin 0 -> 4062 bytes testdata/snapshots/darwin/reactions-light.png | Bin 0 -> 3924 bytes .../snapshots/darwin/recent_files-dark.png | Bin 0 -> 28297 bytes .../snapshots/darwin/recent_files-light.png | Bin 0 -> 27714 bytes .../snapshots/darwin/remote_cursor-dark.png | Bin 0 -> 7765 bytes .../snapshots/darwin/remote_cursor-light.png | Bin 0 -> 7420 bytes .../snapshots/darwin/rename_inline-dark.png | Bin 0 -> 4222 bytes .../snapshots/darwin/rename_inline-light.png | Bin 0 -> 4107 bytes .../snapshots/darwin/share_dialog-dark.png | Bin 0 -> 33062 bytes .../snapshots/darwin/share_dialog-light.png | Bin 0 -> 33953 bytes .../snapshots/darwin/storage_usage-dark.png | Bin 0 -> 20738 bytes .../snapshots/darwin/storage_usage-light.png | Bin 0 -> 19815 bytes .../snapshots/darwin/transfer_queue-dark.png | Bin 0 -> 22961 bytes .../snapshots/darwin/transfer_queue-light.png | Bin 0 -> 22293 bytes transfer_queue.go | 211 ++++++++++ transfer_queue_test.go | 123 ++++++ 96 files changed, 6468 insertions(+), 16 deletions(-) create mode 100644 archive_viewer.go create mode 100644 archive_viewer_test.go create mode 100644 docs/components/381-file-explorer.md create mode 100644 docs/components/382-file-grid.md create mode 100644 docs/components/383-file-icon.md create mode 100644 docs/components/384-file-preview.md create mode 100644 docs/components/385-path-bar.md create mode 100644 docs/components/386-rename-inline.md create mode 100644 docs/components/387-file-operation-progress.md create mode 100644 docs/components/388-transfer-queue.md create mode 100644 docs/components/389-storage-usage.md create mode 100644 docs/components/390-recent-files.md create mode 100644 docs/components/391-archive-viewer.md create mode 100644 docs/components/392-presence-avatars.md create mode 100644 docs/components/394-reactions.md create mode 100644 docs/components/395-share-dialog.md create mode 100644 docs/components/396-permission-select.md create mode 100644 docs/components/399-live-indicator.md create mode 100644 docs/components/400-remote-cursor.md create mode 100644 example_files_test.go create mode 100644 examples/gallery/pages_files_browser.go create mode 100644 examples/gallery/pages_files_code.go create mode 100644 examples/gallery/pages_files_collaboration.go create mode 100644 examples/gallery/pages_files_test.go create mode 100644 file_explorer.go create mode 100644 file_explorer_test.go create mode 100644 file_grid.go create mode 100644 file_grid_test.go create mode 100644 file_icon.go create mode 100644 file_icon_test.go create mode 100644 file_operation_progress.go create mode 100644 file_operation_progress_test.go create mode 100644 file_preview.go create mode 100644 file_preview_test.go create mode 100644 live_indicator.go create mode 100644 live_indicator_test.go create mode 100644 messages_files.go create mode 100644 path_bar.go create mode 100644 path_bar_test.go create mode 100644 permission_select.go create mode 100644 permission_select_test.go create mode 100644 presence_avatars.go create mode 100644 presence_avatars_test.go create mode 100644 reactions.go create mode 100644 reactions_test.go create mode 100644 recent_files.go create mode 100644 recent_files_test.go create mode 100644 remote_cursor.go create mode 100644 remote_cursor_test.go create mode 100644 rename_inline.go create mode 100644 rename_inline_test.go create mode 100644 share_dialog.go create mode 100644 share_dialog_test.go create mode 100644 storage_usage.go create mode 100644 storage_usage_test.go create mode 100644 testdata/snapshots/darwin/archive_viewer-dark.png create mode 100644 testdata/snapshots/darwin/archive_viewer-light.png create mode 100644 testdata/snapshots/darwin/file_explorer-dark.png create mode 100644 testdata/snapshots/darwin/file_explorer-light.png create mode 100644 testdata/snapshots/darwin/file_grid-dark.png create mode 100644 testdata/snapshots/darwin/file_grid-light.png create mode 100644 testdata/snapshots/darwin/file_icon-dark.png create mode 100644 testdata/snapshots/darwin/file_icon-light.png create mode 100644 testdata/snapshots/darwin/file_operation_progress-dark.png create mode 100644 testdata/snapshots/darwin/file_operation_progress-light.png create mode 100644 testdata/snapshots/darwin/file_preview-dark.png create mode 100644 testdata/snapshots/darwin/file_preview-light.png create mode 100644 testdata/snapshots/darwin/live_indicator-dark.png create mode 100644 testdata/snapshots/darwin/live_indicator-light.png create mode 100644 testdata/snapshots/darwin/path_bar-dark.png create mode 100644 testdata/snapshots/darwin/path_bar-light.png create mode 100644 testdata/snapshots/darwin/permission_select-dark.png create mode 100644 testdata/snapshots/darwin/permission_select-light.png create mode 100644 testdata/snapshots/darwin/presence_avatars-dark.png create mode 100644 testdata/snapshots/darwin/presence_avatars-light.png create mode 100644 testdata/snapshots/darwin/reactions-dark.png create mode 100644 testdata/snapshots/darwin/reactions-light.png create mode 100644 testdata/snapshots/darwin/recent_files-dark.png create mode 100644 testdata/snapshots/darwin/recent_files-light.png create mode 100644 testdata/snapshots/darwin/remote_cursor-dark.png create mode 100644 testdata/snapshots/darwin/remote_cursor-light.png create mode 100644 testdata/snapshots/darwin/rename_inline-dark.png create mode 100644 testdata/snapshots/darwin/rename_inline-light.png create mode 100644 testdata/snapshots/darwin/share_dialog-dark.png create mode 100644 testdata/snapshots/darwin/share_dialog-light.png create mode 100644 testdata/snapshots/darwin/storage_usage-dark.png create mode 100644 testdata/snapshots/darwin/storage_usage-light.png create mode 100644 testdata/snapshots/darwin/transfer_queue-dark.png create mode 100644 testdata/snapshots/darwin/transfer_queue-light.png create mode 100644 transfer_queue.go create mode 100644 transfer_queue_test.go diff --git a/TASKS.md b/TASKS.md index d3c27b5..9ca350d 100644 --- a/TASKS.md +++ b/TASKS.md @@ -28,7 +28,7 @@ Ordered by PRD sections. Check an item only after its codex review passes. Revie - [ ] T12.12 §4.23 Terminal & Code 321–340 (docs/prd) - [ ] T12.13 §4.24 Media 341–360 (docs/prd) - [ ] T12.14 §4.25 Projects 361–380 (docs/prd) -- [ ] T12.15 §4.26 Files & Collaboration 381–400 (docs/prd) +- [x] T12.15 §4.26 Files & Collaboration 381–400 — existing 17 sources only (user scope); missing 393/397/398 dropped; r1 FAIL 7, r2 FAIL 2, r3 PASS; 386 native stem selection and IME Enter remain documented MyGo API limits - [ ] T12.16 §4.27 Messaging & Mail 401–420 (docs/prd) - [ ] T12.17 §4.28 Calendar 421–440 (docs/prd) - [ ] T12.18 §4.29 Dashboards & Dev Tools 441–460 (docs/prd) diff --git a/archive_viewer.go b/archive_viewer.go new file mode 100644 index 0000000..ac764df --- /dev/null +++ b/archive_viewer.go @@ -0,0 +1,197 @@ +package mujica + +import ( + "cmp" + "fmt" + "log/slog" + "maps" + "math" + "path" + "slices" + "strings" + + "github.com/egoist/mygo/ui" +) + +// ArchiveEntry describes an archive member with a slash-separated Path. +type ArchiveEntry struct { + Path string + Dir bool + // Size is the unpacked size; Packed the compressed size. + Size, Packed int64 +} + +// ArchiveViewerState preserves folders, selection, sort and indexed entries. +type ArchiveViewerState struct { + table TreeTableState[string] + index archiveIndex + data *ArchiveEntry + n int + revision uint64 +} + +// archiveIndex is the tree built from the entries. +type archiveIndex struct { + byPath map[string]*ArchiveEntry + kids map[string][]string + roots []string + // sums totals a folder's files. + sums map[string][2]int64 + files int + size, pack int64 +} + +// ArchiveViewerOptions configure an ArchiveViewer. +type ArchiveViewerOptions struct { + // Revision changes when entries are edited in place. + Revision uint64 + // Status shows the caller's listing in progress or failed. + Status DataStatus + Error string +} + +// ArchiveViewerResult is the viewer and the entry opened this frame. +type ArchiveViewerResult struct { + Element *ui.Element + Opened *ArchiveEntry + Retried bool +} + +// ArchiveViewer virtualizes archive entries with sizes and compression ratios. +func ArchiveViewer(c *ui.Context, entries []ArchiveEntry, s *ArchiveViewerState, opts ArchiveViewerOptions) ArchiveViewerResult { + if s == nil { + panic("mujica: ArchiveViewer needs a state") + } + if s.revision != opts.Revision { + s.data = nil + s.index.byPath = nil + s.revision = opts.Revision + } + archiveBuild(entries, s) + ix := &s.index + u := Density(c).Unit() + size := func(p string) (int64, int64) { + if e := ix.byPath[p]; e != nil && !e.Dir { + return e.Size, e.Packed + } + t := ix.sums[p] + return t[0], t[1] + } + cell := func(text string) { codeMono(c, text).TextColor(Tokens(c).TextMuted) } + var res ArchiveViewerResult + res.Element = ui.Column(c).MinHeight(0).Gap(u * 2) + if res.Element.IsDisabled() { + copy := *s + copy.table.Selection = filesSelectionCopy(s.table.Selection) + copy.table.tree.outline.Open = filesSelectionCopy(s.table.tree.outline.Open) + copy.table.Layout = ui.TableLayout{Order: slices.Clone(s.table.Layout.Order), Widths: maps.Clone(s.table.Layout.Widths)} + copy.table.tree.outline.List.Columns = copy.table.Layout + s = © + } + res.Element.Children(func() { + codeCaption(c, fmt.Sprintf(msg(c, "archive_viewer.summary"), ix.files, processListBytes(uint64(ix.size)), + processListBytes(uint64(ix.pack)), archiveRatio(ix.size, ix.pack))) + v := TreeTable(c, &s.table, TreeTableOptions[string]{ + Roots: ix.roots, Children: func(p string) []string { return ix.kids[p] }, + Label: func(p string) string { return path.Base(p) }, Status: opts.Status, Error: opts.Error, + Empty: msg(c, "archive_viewer.empty"), + Columns: []TreeColumn[string]{ + {ID: "name", Title: msg(c, "archive_viewer.name"), Compare: strings.Compare, Cell: func(p string) { + ui.Row(c).Gap(u * 2).AlignItems(ui.Center).Children(func() { + FileIcon(c, p, FileIconOptions{Folder: ix.kids[p] != nil}) + ui.Text(c, path.Base(p)).SingleLine() + }) + }}, + {ID: "size", Title: msg(c, "archive_viewer.size"), Width: 96, Align: ui.End, + Compare: func(a, b string) int { x, _ := size(a); y, _ := size(b); return cmp.Compare(x, y) }, + Cell: func(p string) { n, _ := size(p); cell(processListBytes(uint64(n))) }}, + {ID: "packed", Title: msg(c, "archive_viewer.packed"), Width: 96, Align: ui.End, + Cell: func(p string) { _, n := size(p); cell(processListBytes(uint64(n))) }}, + {ID: "ratio", Title: msg(c, "archive_viewer.ratio"), Width: 72, Align: ui.End, + Cell: func(p string) { a, b := size(p); cell(archiveRatio(a, b)) }}, + }, + }) + v.Element.Grow(1) + res.Retried = v.Retried() + if p, ok := s.table.Selected(); ok && v.Submitted() && !res.Element.IsDisabled() { + if e := ix.byPath[p]; e != nil && !e.Dir { + open := *e + res.Opened = &open + slog.Debug("mujica: archive_viewer open") + } + } + }) + return res +} + +// archiveRatio formats the saved-size percentage. +func archiveRatio(size, packed int64) string { + if size <= 0 { + return "—" + } + return fmt.Sprintf("%.0f%%", 100*(1-float64(packed)/float64(size))) +} + +// archiveBuild caches entries and implied parent directories. +func archiveBuild(entries []ArchiveEntry, s *ArchiveViewerState) { + var first *ArchiveEntry + if len(entries) > 0 { + first = &entries[0] + } + if s.data == first && s.n == len(entries) && s.index.byPath != nil { + return + } + s.data, s.n = first, len(entries) + ix := archiveIndex{byPath: map[string]*ArchiveEntry{}, kids: map[string][]string{}, sums: map[string][2]int64{}} + var addDir func(p string) + link := func(p string) { + parent := path.Dir(p) + if parent == "." { + ix.roots = append(ix.roots, p) + return + } + addDir(parent) + ix.kids[parent] = append(ix.kids[parent], p) + } + addDir = func(p string) { + if e := ix.byPath[p]; e != nil && !e.Dir { + panic("mujica: ArchiveEntry file used as a folder: " + p) + } + if _, ok := ix.kids[p]; ok { + return + } + ix.kids[p] = []string{} + link(p) + } + for i := range entries { + e := &entries[i] + p := strings.Trim(e.Path, "/") + if p == "" || e.Size < 0 || e.Packed < 0 { + panic("mujica: ArchiveEntry needs a Path and sizes not negative") + } + if _, dup := ix.byPath[p]; dup { + panic("mujica: duplicate ArchiveEntry " + p) + } + if !e.Dir && ix.kids[p] != nil { + panic("mujica: ArchiveEntry folder used as a file: " + p) + } + ix.byPath[p] = e + if e.Dir { + addDir(p) + continue + } + link(p) + ix.files++ + if e.Size > math.MaxInt64-ix.size || e.Packed > math.MaxInt64-ix.pack { + panic("mujica: ArchiveEntry totals overflow") + } + ix.size += e.Size + ix.pack += e.Packed + for d := path.Dir(p); d != "."; d = path.Dir(d) { + t := ix.sums[d] + ix.sums[d] = [2]int64{t[0] + e.Size, t[1] + e.Packed} + } + } + s.index = ix + slog.Debug("mujica: archive_viewer index", "files", ix.files) +} diff --git a/archive_viewer_test.go b/archive_viewer_test.go new file mode 100644 index 0000000..7f15d59 --- /dev/null +++ b/archive_viewer_test.go @@ -0,0 +1,187 @@ +package mujica + +import ( + "fmt" + "strings" + "testing" + + "github.com/egoist/mygo/ui" +) + +func archiveSample() []ArchiveEntry { + return []ArchiveEntry{ + {Path: "court/seal.go", Size: 4000, Packed: 1000}, + {Path: "court/decrees/42.pdf", Size: 6000, Packed: 5000}, + {Path: "README.md", Size: 1000, Packed: 400}, + {Path: "empty/", Dir: true}, + } +} + +type archiveProbe struct { + entries []ArchiveEntry + s ArchiveViewerState + opts ArchiveViewerOptions + opened []string +} + +func (p *archiveProbe) view(c *ui.Context) { + Use(c, Settings{}) + r := ArchiveViewer(c, p.entries, &p.s, p.opts) + r.Element.Height(340) + if r.Opened != nil { + p.opened = append(p.opened, r.Opened.Path) + } +} + +func TestArchiveViewerIndex(t *testing.T) { + s := &ArchiveViewerState{} + archiveBuild(archiveSample(), s) + ix := s.index + if ix.files != 3 || ix.size != 11000 || ix.pack != 6400 { + t.Fatalf("totals %d %d %d", ix.files, ix.size, ix.pack) + } + if got := ix.sums["court"]; got != [2]int64{10000, 6000} { + t.Fatalf("court sums %v", got) + } + if len(ix.roots) != 3 || len(ix.kids["court"]) != 2 || ix.kids["empty"] == nil { + t.Fatalf("roots %v kids %v", ix.roots, ix.kids) + } + if archiveRatio(4000, 1000) != "75%" || archiveRatio(0, 0) != "—" { + t.Fatal("ratio") + } +} + +func TestArchiveViewerExpandAndOpen(t *testing.T) { + p := &archiveProbe{entries: archiveSample()} + tt := ui.NewTester(p.view, 620, 360) + if !tt.HasText("3 files, 11 KB unpacked, 6 KB packed (42% smaller)") { + t.Fatalf("summary: %q", tt.Texts()) + } + if !tt.HasText("40%") || tt.HasText("seal.go") { + t.Fatalf("court row: %q", tt.Texts()) + } + cr, _ := tt.Find("10 KB") + tt.ClickAt(cr.X, cr.Y+cr.H/2) + tt.Key(0, ui.KeyRight) + if !tt.HasText("seal.go") || !tt.HasText("75%") { + t.Fatalf("expand: %q", tt.Texts()) + } + r, _ := tt.Find("seal.go") + tt.ClickAt(r.X, r.Y+r.H/2) + tt.ClickAt(r.X, r.Y+r.H/2) + if len(p.opened) != 1 || p.opened[0] != "court/seal.go" { + t.Fatalf("opened %v", p.opened) + } +} + +func TestArchiveViewerStatusAndInvalid(t *testing.T) { + tt := ui.NewTester((&archiveProbe{opts: ArchiveViewerOptions{Status: DataFailed, Error: "bad archive"}}).view, 620, 360) + if !tt.HasText("bad archive") { + t.Fatalf("texts %q", tt.Texts()) + } + defer func() { + if recover() == nil { + t.Fatal("duplicate entry did not panic") + } + }() + archiveBuild([]ArchiveEntry{{Path: "a"}, {Path: "a"}}, &ArchiveViewerState{}) +} + +func TestArchiveViewerSnapshot(t *testing.T) { + snapshot(t, "archive_viewer", 600, 320, func(c *ui.Context) { + p := ui.Local(c.Root(), "snap", func() *archiveProbe { + p := &archiveProbe{entries: archiveSample()} + p.s.table.SetOpen("court", true) + return p + }) + ui.Column(c).Padding(12).Children(func() { + ArchiveViewer(c, (*p).entries, &(*p).s, ArchiveViewerOptions{}).Element.Height(296) + }) + }) +} + +func TestArchiveViewerRejectsFileParent(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("file used as a folder was accepted") + } + }() + archiveBuild([]ArchiveEntry{{Path: "court"}, {Path: "court/a.txt"}}, &ArchiveViewerState{}) +} +func TestArchiveViewerAncestorDisabled(t *testing.T) { + s := ArchiveViewerState{} + disabled, opened := false, 0 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Disabled(disabled).Children(func() { + r := ArchiveViewer(c, []ArchiveEntry{{Path: "a.txt", Size: 10}}, &s, ArchiveViewerOptions{}) + r.Element.Height(200) + if r.Opened != nil { + opened++ + } + }) + }, 500, 250) + if err := tt.Click("a.txt"); err != nil { + t.Fatal(err) + } + disabled = true + tt.Key(0, ui.KeyEnter) + if opened != 0 { + t.Fatal("disabled archive opened") + } +} + +func TestArchiveViewerLargeAndRevision(t *testing.T) { + entries := make([]ArchiveEntry, 100000) + for i := range entries { + entries[i] = ArchiveEntry{Path: fmt.Sprintf("f-%d.txt", i), Size: 10, Packed: 5} + } + s := ArchiveViewerState{} + revision := uint64(0) + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ArchiveViewer(c, entries, &s, ArchiveViewerOptions{Revision: revision}).Element.Height(220) + }, 600, 250) + n := 0 + for _, text := range tt.Texts() { + if strings.HasPrefix(text, "f-") { + n++ + } + } + if n == 0 || n > 60 { + t.Fatalf("built %d archive entries", n) + } + entries[0].Size = 20 + revision++ + tt.Frame() + if s.index.size != 1000010 { + t.Fatalf("revised size %d", s.index.size) + } +} + +func TestArchiveViewerTotalOverflow(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("overflowing archive total was accepted") + } + }() + archiveBuild([]ArchiveEntry{{Path: "a", Size: 1 << 62}, {Path: "b", Size: 1 << 62}}, &ArchiveViewerState{}) +} + +func TestArchiveViewerDisablingBeforeDeliveredKey(t *testing.T) { + s := ArchiveViewerState{} + disabled := false + entries := []ArchiveEntry{{Path: "a.txt", Size: 1}, {Path: "b.txt", Size: 1}} + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Disabled(disabled).Children(func() { ArchiveViewer(c, entries, &s, ArchiveViewerOptions{}).Element.Height(200) }) + }, 600, 250) + if err := tt.Click("a.txt"); err != nil { + t.Fatal(err) + } + disabled = true + tt.Key(0, ui.KeyDown) + if key, _ := s.table.Selected(); key != "a.txt" { + t.Fatalf("disabled selection %s", key) + } +} diff --git a/code_shared.go b/code_shared.go index 9a99563..b60a254 100644 --- a/code_shared.go +++ b/code_shared.go @@ -1,7 +1,6 @@ package mujica import ( - "math" "strings" "github.com/egoist/mygo/ui" @@ -162,13 +161,6 @@ func codeBadge(c *ui.Context, letter string, col ui.Color) { }) } -// codeRule draws a pixel-aligned 1 DIP line. -func codeRule(p *ui.Painter, x0, x1, y float32, c ui.Color) { - s := float64(p.Scale()) - yy := float32((math.Floor(float64(y)*s) + 0.5) / s) - p.Line(x0, yy, x1, yy, 1, c) -} - // codeTextWidth returns the width of s in the code font. func codeTextWidth(c *ui.Context, s string) float32 { w, _ := c.MeasureText(0, ui.Span{Text: s, Font: theme.MonoFont, Size: FontSize(c, codeSize)}) diff --git a/docs/components/381-file-explorer.md b/docs/components/381-file-explorer.md new file mode 100644 index 0000000..bc5ccf8 --- /dev/null +++ b/docs/components/381-file-explorer.md @@ -0,0 +1,43 @@ +# FileExplorer + +## 用途 + +组合文件夹树、文件列表、导航历史与多选。 + +## 最小示例 + +```go +var state mujica.FileExplorerState +opts := mujica.FileExplorerOptions{ + Roots: []string{"/court"}, + Folders: func(string) []string { return nil }, + Entries: func(string) []mujica.FileEntry { + return []mujica.FileEntry{{Path: "/court/seal.go", Name: "seal.go", Size: 1024}} + }, +} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FileExplorer(c, &state, opts).Element.Height(240) +} +ui.Render(view, 600, 300, 1) +``` + +## 参数 + +Roots、Folders、Entries 必填;Disabled 默认 false;Revision 用于原地更新条目。 + +## 状态 + +默认、焦点、禁用;空数据按组件内容展示。 + +## 事件 + +Navigated、Changed、Opened 返回本帧导航、选择和打开。 + +## 键盘操作 + +Tab 切换;方向键选择;Enter 打开;macOS Command+[ / ] / ↑,其他平台 Alt+← / → / ↑。 + +## 限制 + +文件及目录由调用方提供,读取与打开由调用方执行。条目 Path 必须唯一且非空;切片地址、长度或 Revision 变化时重建索引并清理已删除的选择。 diff --git a/docs/components/382-file-grid.md b/docs/components/382-file-grid.md new file mode 100644 index 0000000..f0af769 --- /dev/null +++ b/docs/components/382-file-grid.md @@ -0,0 +1,37 @@ +# FileGrid + +## 用途 + +通过缩略图插槽展示文件网格与多选。 + +## 最小示例 + +```go +entries := []mujica.FileEntry{{Path: "/court/seal.go", Name: "seal.go"}} +var state mujica.FileGridState +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FileGrid(c, entries, &state, mujica.FileGridOptions{}).Element.Height(240) +} +ui.Render(view, 400, 300, 1) +``` + +## 参数 + +ItemWidth 默认 128 DIP;Thumbnail 返回是否已绘制;Disabled 默认 false;Revision 用于原地更新条目。Path 顺序变化时重建范围选择锚点;相同 Path 顺序保留锚点与滚动位置。 + +## 状态 + +默认、焦点、禁用;空数据按组件内容展示。 + +## 事件 + +Changed 表示选择变化;Opened 表示打开请求。 + +## 键盘操作 + +方向键选择;Command/Ctrl+点击多选;Shift 扩展;Enter 打开。 + +## 限制 + +仅创建可见瓦片;缩略图解码由调用方完成。 diff --git a/docs/components/383-file-icon.md b/docs/components/383-file-icon.md new file mode 100644 index 0000000..155a18f --- /dev/null +++ b/docs/components/383-file-icon.md @@ -0,0 +1,35 @@ +# FileIcon + +## 用途 + +按扩展名和文件夹开合显示类型图标。 + +## 最小示例 + +```go +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FileIcon(c, "seal.go", mujica.FileIconOptions{Size: 32}) +} +ui.Render(view, 100, 80, 1) +``` + +## 参数 + +Size 默认 20 DIP;Folder 表示目录;Open 需要 Folder。 + +## 状态 + +文件、目录关闭、目录打开。 + +## 事件 + +展示组件。 + +## 键盘操作 + +展示组件,沿用父级焦点。 + +## 限制 + +扩展名大小写无关;未知扩展名显示通用文件图标。 diff --git a/docs/components/384-file-preview.md b/docs/components/384-file-preview.md new file mode 100644 index 0000000..9b8f407 --- /dev/null +++ b/docs/components/384-file-preview.md @@ -0,0 +1,36 @@ +# FilePreview + +## 用途 + +展示图片、文本、代码和不支持类型说明。 + +## 最小示例 + +```go +file := mujica.PreviewFile{Name: "seal.go", Text: "package court\nfunc Seal() {}"} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FilePreview(c, file, mujica.FilePreviewOptions{}).Element.Height(240) +} +ui.Render(view, 500, 300, 1) +``` + +## 参数 + +Name 必填;Image、Text、Language、Size 为调用方数据;Status、Error 描述加载状态。 + +## 状态 + +内容就绪、加载、失败、不支持类型。 + +## 事件 + +Closed、Opened、Retried 返回关闭、打开和重试请求。 + +## 键盘操作 + +Escape 关闭;Tab 到操作;文本支持选择与复制。 + +## 限制 + +读取和解码由调用方提供;预览作为内嵌面板布局。 diff --git a/docs/components/385-path-bar.md b/docs/components/385-path-bar.md new file mode 100644 index 0000000..173287d --- /dev/null +++ b/docs/components/385-path-bar.md @@ -0,0 +1,35 @@ +# PathBar + +## 用途 + +分段跳转,长路径折叠中间段。 + +## 最小示例 + +```go +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.PathBar(c, "/court/chronicles/seals", mujica.PathBarOptions{}) +} +ui.Render(view, 400, 100, 1) +``` + +## 参数 + +Separator 默认 /;RootLabel 默认分隔符;Disabled 默认 false。 + +## 状态 + +默认、焦点、禁用;空数据按组件内容展示。 + +## 事件 + +Navigated 返回目标祖先路径。 + +## 键盘操作 + +Tab 进入;左右移动;Enter 激活;Escape 关闭折叠菜单。 + +## 限制 + +路径必须至少包含一个分段;调用方处理导航。 diff --git a/docs/components/386-rename-inline.md b/docs/components/386-rename-inline.md new file mode 100644 index 0000000..b02d9c3 --- /dev/null +++ b/docs/components/386-rename-inline.md @@ -0,0 +1,36 @@ +# RenameInline + +## 用途 + +编辑文件名,校验后确认或取消。 + +## 最小示例 + +```go +name, editing := "seal.txt", true +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.RenameInline(c, &name, &editing, mujica.RenameInlineOptions{}) +} +ui.Render(view, 400, 100, 1) +``` + +## 参数 + +name、editing 必填;Folder 默认 false;Validate 返回错误文案;Disabled 默认 false。 + +## 状态 + +默认、焦点、禁用;空数据按组件内容展示。 + +## 事件 + +Renamed 表示已写回名称;Canceled 表示取消。 + +## 键盘操作 + +Enter 确认;Escape 取消;双击开始编辑;失焦确认有效名称。 + +## 限制 + +默认文件名主体选择采用视觉标记和首次输入替换。MyGo ui/editor.go:133 的选择接口为私有;剪贴板和撤销沿用原生编辑器的实际选区。组合输入期间 Enter 由 MyGo ui/editor.go:485、688 处理,组件无法查询组合状态;使用输入法提交文本后再确认。 diff --git a/docs/components/387-file-operation-progress.md b/docs/components/387-file-operation-progress.md new file mode 100644 index 0000000..6321e7f --- /dev/null +++ b/docs/components/387-file-operation-progress.md @@ -0,0 +1,36 @@ +# FileOperationProgress + +## 用途 + +显示复制、移动、删除进度、取消和冲突回答。 + +## 最小示例 + +```go +op := mujica.FileOperation{Kind: mujica.FileCopy, Done: 2, Total: 10, Current: "seal.go"} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FileOperationProgress(c, op, mujica.FileOperationOptions{}) +} +ui.Render(view, 500, 240, 1) +``` + +## 参数 + +Done、Total、BytesDone、BytesTotal 为计数;Conflict 为冲突名称;Disabled 默认 false。 + +## 状态 + +默认、焦点、禁用;空数据按组件内容展示。 + +## 事件 + +Canceled、Resolved、ApplyAll 返回操作意图。 + +## 键盘操作 + +Tab 到取消、全选规则和冲突选项;Enter / Space 激活。 + +## 限制 + +文件操作由调用方执行;非法计数或 Kind 会 panic。 diff --git a/docs/components/388-transfer-queue.md b/docs/components/388-transfer-queue.md new file mode 100644 index 0000000..8f92405 --- /dev/null +++ b/docs/components/388-transfer-queue.md @@ -0,0 +1,37 @@ +# TransferQueue + +## 用途 + +显示上传下载队列并回传暂停、继续、重试、移除。 + +## 最小示例 + +```go +items := []mujica.QueuedTransfer{{ID: "a", Name: "ledger.zip", Upload: true, Status: mujica.TransferActive, Done: 25, Total: 100}} +var state mujica.TransferQueueState +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.TransferQueue(c, items, &state, mujica.TransferQueueOptions{}).Element.Height(240) +} +ui.Render(view, 500, 300, 1) +``` + +## 参数 + +ID 唯一且必填;Status 默认为排队;Revision 原地更新数据时递增;Disabled 默认 false。 + +## 状态 + +默认、焦点、禁用;空数据按组件内容展示。 + +## 事件 + +Action 与 ID 返回对应传输操作。 + +## 键盘操作 + +Tab 在可见行操作间移动;Enter / Space 激活。 + +## 限制 + +网络由调用方执行;汇总仅在数据切片或 Revision 变化时重算。 diff --git a/docs/components/389-storage-usage.md b/docs/components/389-storage-usage.md new file mode 100644 index 0000000..75eae34 --- /dev/null +++ b/docs/components/389-storage-usage.md @@ -0,0 +1,36 @@ +# StorageUsage + +## 用途 + +存储分段条、分类明细及键盘读数。 + +## 最小示例 + +```go +segments := []mujica.StorageSegment{{Label: "Documents", Bytes: 24 << 30}, {Label: "Photos", Bytes: 40 << 30}} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.StorageUsage(c, segments, mujica.StorageUsageOptions{Capacity: 128 << 30, Label: "Court disk"}) +} +ui.Render(view, 500, 240, 1) +``` + +## 参数 + +Capacity 必须为正;Label 为业务名称;分类总量须处于容量内;原地更新时递增 Revision,分类明细采用虚拟列表。 + +## 状态 + +占用、剩余、焦点读数。 + +## 事件 + +方向键读数变化会朗读并记录状态。 + +## 键盘操作 + +Tab 聚焦分段条;左右循环读取分类与剩余空间。 + +## 限制 + +统计由调用方提供,组件展示分类大小与比例。 diff --git a/docs/components/390-recent-files.md b/docs/components/390-recent-files.md new file mode 100644 index 0000000..691f8a3 --- /dev/null +++ b/docs/components/390-recent-files.md @@ -0,0 +1,38 @@ +# RecentFiles + +## 用途 + +按今天、昨天、本周与更早分组展示最近文件。 + +## 最小示例 + +```go +now := time.Date(2026, 10, 5, 12, 0, 0, 0, time.UTC) +files := []mujica.RecentFile{{Path: "/court/seal.go", Opened: now}} +var state mujica.RecentFilesState +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.RecentFiles(c, files, &state, mujica.RecentFilesOptions{Now: now}).Element.Height(240) +} +ui.Render(view, 500, 300, 1) +``` + +## 参数 + +Now 默认帧时间;Revision 原地更新时递增;Disabled 默认 false。 + +## 状态 + +默认、焦点、禁用;空数据按组件内容展示。 + +## 事件 + +Opened、Removed、Cleared 为打开、移除、清空请求。 + +## 键盘操作 + +上下选择文件;Enter 打开;Tab 到清除和移除按钮。 + +## 限制 + +时间按 Now 时区分组;切片替换或 Revision 更新会重算排序。 diff --git a/docs/components/391-archive-viewer.md b/docs/components/391-archive-viewer.md new file mode 100644 index 0000000..0b11285 --- /dev/null +++ b/docs/components/391-archive-viewer.md @@ -0,0 +1,37 @@ +# ArchiveViewer + +## 用途 + +展示调用方条目树、大小和压缩率。 + +## 最小示例 + +```go +entries := []mujica.ArchiveEntry{{Path: "court/seal.go", Size: 4000, Packed: 1000}} +var state mujica.ArchiveViewerState +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.ArchiveViewer(c, entries, &state, mujica.ArchiveViewerOptions{}).Element.Height(240) +} +ui.Render(view, 600, 300, 1) +``` + +## 参数 + +Path 唯一;Size、Packed 非负;Status、Error 为加载状态;Revision 原地更新时递增。 + +## 状态 + +条目树、加载、失败、空列表。 + +## 事件 + +Opened 返回文件打开请求;Retried 返回重试请求。 + +## 键盘操作 + +上下选择;左右展开折叠;Enter 打开;Tab 到列操作。 + +## 限制 + +压缩包解码由调用方完成;祖先目录自动补齐,文件与目录身份冲突会 panic。 diff --git a/docs/components/392-presence-avatars.md b/docs/components/392-presence-avatars.md new file mode 100644 index 0000000..99a6fb1 --- /dev/null +++ b/docs/components/392-presence-avatars.md @@ -0,0 +1,36 @@ +# PresenceAvatars + +## 用途 + +显示协作者、溢出人数及跟随入口。 + +## 最小示例 + +```go +people := []mujica.Collaborator{{ID: "ada", Name: "Ada Lovelace"}} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.PresenceAvatars(c, people, mujica.PresenceAvatarsOptions{}) +} +ui.Render(view, 300, 100, 1) +``` + +## 参数 + +Max 默认 4;Size 默认 28;Following 为跟随 ID;Disabled 默认 false。 + +## 状态 + +默认、焦点、禁用;空数据按组件内容展示。 + +## 事件 + +Follow 返回 ID;Unfollow 请求停止跟随。 + +## 键盘操作 + +Tab 进入;左右移动;Enter 激活;Escape 关闭溢出名单。 + +## 限制 + +在线数据与跟随行为由调用方维护。 diff --git a/docs/components/394-reactions.md b/docs/components/394-reactions.md new file mode 100644 index 0000000..333e190 --- /dev/null +++ b/docs/components/394-reactions.md @@ -0,0 +1,36 @@ +# Reactions + +## 用途 + +显示回应计数、当前用户标记与添加选择器。 + +## 最小示例 + +```go +items := []mujica.Reaction{{Emoji: "\U0001f44d", Count: 3, Mine: true}} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.Reactions(c, items, mujica.ReactionsOptions{Choices: []string{"\U0001f44d", "\U0001f389"}}) +} +ui.Render(view, 400, 100, 1) +``` + +## 参数 + +Emoji 为显示字符;Count 必须为正;Choices 必填;People 用于提示;Disabled 默认 false。 + +## 状态 + +默认、焦点、禁用;空数据按组件内容展示。 + +## 事件 + +Toggled 为切换意图;Added 为选择器选项。 + +## 键盘操作 + +Tab 到回应与添加;Enter / Space 激活;选择器左右导航;Escape 关闭。 + +## 限制 + +计数更新与持久化由调用方执行;传入系统字体支持的表情。 diff --git a/docs/components/395-share-dialog.md b/docs/components/395-share-dialog.md new file mode 100644 index 0000000..606c262 --- /dev/null +++ b/docs/components/395-share-dialog.md @@ -0,0 +1,40 @@ +# ShareDialog + +## 用途 + +邀请成员、调整权限、移除成员与复制链接回调。 + +## 最小示例 + +```go +open := false +members := []mujica.ShareMember{{ID: "ada", Name: "Ada", Permission: mujica.PermissionEdit, Owner: true}} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + if mujica.Button(c, "Share", mujica.ButtonOptions{}).Clicked() { + open = true + } + mujica.ShareDialog(c, &open, &members, mujica.ShareDialogOptions{Title: "Ledger", Link: "https://court.org/ledger"}) +} +ui.Render(view, 600, 400, 1) +``` + +## 参数 + +Title 必填;Link 为空隐藏复制;OnCopy 可覆盖剪贴板动作;Disabled 或祖先禁用关闭弹层。 + +## 状态 + +默认、焦点、禁用;空数据按组件内容展示。 + +## 事件 + +Invited 为邀请请求;Changed、Removed 已应用成员变化;Copied 为复制请求完成。 + +## 键盘操作 + +Tab 在对话框内移动;Enter 邀请或激活操作;Escape 关闭并恢复焦点。 + +## 限制 + +权限和成员由调用方持有;网络与链接有效性校验由业务负责。 diff --git a/docs/components/396-permission-select.md b/docs/components/396-permission-select.md new file mode 100644 index 0000000..52daacf --- /dev/null +++ b/docs/components/396-permission-select.md @@ -0,0 +1,36 @@ +# PermissionSelect + +## 用途 + +选择可查看、可评论、可编辑。 + +## 最小示例 + +```go +value := mujica.PermissionView +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.PermissionSelect(c, &value, mujica.PermissionSelectOptions{}) +} +ui.Render(view, 300, 100, 1) +``` + +## 参数 + +PermissionView 为零值;Label 默认权限;Disabled 默认 false。 + +## 状态 + +默认、焦点、禁用;空数据按组件内容展示。 + +## 事件 + +ChoiceResult.Changed() 表示值已变化。 + +## 键盘操作 + +Enter 打开;上下移动;Enter 确认;Escape 关闭。 + +## 限制 + +权限枚举越界会 panic;授权操作由调用方处理。 diff --git a/docs/components/399-live-indicator.md b/docs/components/399-live-indicator.md new file mode 100644 index 0000000..0a368b9 --- /dev/null +++ b/docs/components/399-live-indicator.md @@ -0,0 +1,35 @@ +# LiveIndicator + +## 用途 + +显示实时同步状态、离线说明和重试。 + +## 最小示例 + +```go +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.LiveIndicator(c, mujica.LiveOffline, mujica.LiveIndicatorOptions{}) +} +ui.Render(view, 500, 100, 1) +``` + +## 参数 + +LastSynced 可选;Now 默认帧时间;Error 补充失败原因。 + +## 状态 + +已同步、同步中、重连、离线、失败。 + +## 事件 + +ChoiceResult.Changed() 表示用户请求重试。 + +## 键盘操作 + +Tab 到重试;Enter / Space 激活。 + +## 限制 + +同步由调用方执行;状态变化会朗读,减少动画使用静态说明。 diff --git a/docs/components/400-remote-cursor.md b/docs/components/400-remote-cursor.md new file mode 100644 index 0000000..131b437 --- /dev/null +++ b/docs/components/400-remote-cursor.md @@ -0,0 +1,40 @@ +# RemoteCursor + +## 用途 + +绘制远程光标、选区与姓名标签。 + +## 最小示例 + +```go +carets := []mujica.RemoteCaret{{ID: "ada", Name: "Ada", Offset: 4, Anchor: 4}} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + ui.Box(c).Size(300, 100).Children(func() { + mujica.RemoteCursor(c, carets, mujica.RemoteCursorOptions{ + Caret: func(offset int) ui.Rect { return ui.Rect{X: float32(offset) * 8, Y: 20, H: 20} }, + }) + }) +} +ui.Render(view, 320, 120, 1) +``` + +## 参数 + +ID 唯一且必填;Offset、Anchor 非负;Caret 必填;Selection 返回选区矩形。 + +## 状态 + +光标、选区、姓名;覆盖层输入穿透。 + +## 事件 + +展示组件。 + +## 键盘操作 + +输入穿透至下方编辑器。 + +## 限制 + +坐标映射、远程状态由调用方提供;覆盖层放在编辑器后方。 diff --git a/example_files_test.go b/example_files_test.go new file mode 100644 index 0000000..e9a2047 --- /dev/null +++ b/example_files_test.go @@ -0,0 +1,177 @@ +package mujica_test + +import ( + "github.com/egoist/mygo/ui" + mujica "mujicaui" + "time" +) + +func ExampleFileExplorer() { + var state mujica.FileExplorerState + opts := mujica.FileExplorerOptions{ + Roots: []string{"/court"}, + Folders: func(string) []string { return nil }, + Entries: func(string) []mujica.FileEntry { + return []mujica.FileEntry{{Path: "/court/seal.go", Name: "seal.go", Size: 1024}} + }, + } + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FileExplorer(c, &state, opts).Element.Height(240) + } + ui.Render(view, 600, 300, 1) +} + +func ExampleFileGrid() { + entries := []mujica.FileEntry{{Path: "/court/seal.go", Name: "seal.go"}} + var state mujica.FileGridState + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FileGrid(c, entries, &state, mujica.FileGridOptions{}).Element.Height(240) + } + ui.Render(view, 400, 300, 1) +} + +func ExampleFileIcon() { + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FileIcon(c, "seal.go", mujica.FileIconOptions{Size: 32}) + } + ui.Render(view, 100, 80, 1) +} + +func ExampleFilePreview() { + file := mujica.PreviewFile{Name: "seal.go", Text: "package court\nfunc Seal() {}"} + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FilePreview(c, file, mujica.FilePreviewOptions{}).Element.Height(240) + } + ui.Render(view, 500, 300, 1) +} + +func ExamplePathBar() { + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.PathBar(c, "/court/chronicles/seals", mujica.PathBarOptions{}) + } + ui.Render(view, 400, 100, 1) +} + +func ExampleRenameInline() { + name, editing := "seal.txt", true + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.RenameInline(c, &name, &editing, mujica.RenameInlineOptions{}) + } + ui.Render(view, 400, 100, 1) +} + +func ExampleFileOperationProgress() { + op := mujica.FileOperation{Kind: mujica.FileCopy, Done: 2, Total: 10, Current: "seal.go"} + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FileOperationProgress(c, op, mujica.FileOperationOptions{}) + } + ui.Render(view, 500, 240, 1) +} + +func ExampleTransferQueue() { + items := []mujica.QueuedTransfer{{ID: "a", Name: "ledger.zip", Upload: true, Status: mujica.TransferActive, Done: 25, Total: 100}} + var state mujica.TransferQueueState + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.TransferQueue(c, items, &state, mujica.TransferQueueOptions{}).Element.Height(240) + } + ui.Render(view, 500, 300, 1) +} + +func ExampleStorageUsage() { + segments := []mujica.StorageSegment{{Label: "Documents", Bytes: 24 << 30}, {Label: "Photos", Bytes: 40 << 30}} + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.StorageUsage(c, segments, mujica.StorageUsageOptions{Capacity: 128 << 30, Label: "Court disk"}) + } + ui.Render(view, 500, 240, 1) +} + +func ExampleRecentFiles() { + now := time.Date(2026, 10, 5, 12, 0, 0, 0, time.UTC) + files := []mujica.RecentFile{{Path: "/court/seal.go", Opened: now}} + var state mujica.RecentFilesState + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.RecentFiles(c, files, &state, mujica.RecentFilesOptions{Now: now}).Element.Height(240) + } + ui.Render(view, 500, 300, 1) +} + +func ExampleArchiveViewer() { + entries := []mujica.ArchiveEntry{{Path: "court/seal.go", Size: 4000, Packed: 1000}} + var state mujica.ArchiveViewerState + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.ArchiveViewer(c, entries, &state, mujica.ArchiveViewerOptions{}).Element.Height(240) + } + ui.Render(view, 600, 300, 1) +} + +func ExamplePresenceAvatars() { + people := []mujica.Collaborator{{ID: "ada", Name: "Ada Lovelace"}} + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.PresenceAvatars(c, people, mujica.PresenceAvatarsOptions{}) + } + ui.Render(view, 300, 100, 1) +} + +func ExampleReactions() { + items := []mujica.Reaction{{Emoji: "\U0001f44d", Count: 3, Mine: true}} + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.Reactions(c, items, mujica.ReactionsOptions{Choices: []string{"\U0001f44d", "\U0001f389"}}) + } + ui.Render(view, 400, 100, 1) +} + +func ExampleShareDialog() { + open := false + members := []mujica.ShareMember{{ID: "ada", Name: "Ada", Permission: mujica.PermissionEdit, Owner: true}} + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + if mujica.Button(c, "Share", mujica.ButtonOptions{}).Clicked() { + open = true + } + mujica.ShareDialog(c, &open, &members, mujica.ShareDialogOptions{Title: "Ledger", Link: "https://court.org/ledger"}) + } + ui.Render(view, 600, 400, 1) +} + +func ExamplePermissionSelect() { + value := mujica.PermissionView + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.PermissionSelect(c, &value, mujica.PermissionSelectOptions{}) + } + ui.Render(view, 300, 100, 1) +} + +func ExampleLiveIndicator() { + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.LiveIndicator(c, mujica.LiveOffline, mujica.LiveIndicatorOptions{}) + } + ui.Render(view, 500, 100, 1) +} + +func ExampleRemoteCursor() { + carets := []mujica.RemoteCaret{{ID: "ada", Name: "Ada", Offset: 4, Anchor: 4}} + view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + ui.Box(c).Size(300, 100).Children(func() { + mujica.RemoteCursor(c, carets, mujica.RemoteCursorOptions{ + Caret: func(offset int) ui.Rect { return ui.Rect{X: float32(offset) * 8, Y: 20, H: 20} }, + }) + }) + } + ui.Render(view, 320, 120, 1) +} diff --git a/examples/gallery/pages_files.go b/examples/gallery/pages_files.go index b5be09e..f2956bc 100644 --- a/examples/gallery/pages_files.go +++ b/examples/gallery/pages_files.go @@ -1,3 +1,23 @@ package main -var filesPages = []page{} +const filesCategory = "Files & Collaboration" + +var filesPages = []page{ + {ID: "381", Name: "FileExplorer", ZhName: "文件浏览器", Category: filesCategory, Demo: fileExplorerDemo, Code: filesFileExplorerCode, Keys: "Tab 切换;方向键选择;Enter 打开;macOS Command+[ / ] / ↑,其他平台 Alt+← / → / ↑。"}, + {ID: "382", Name: "FileGrid", ZhName: "文件网格", Category: filesCategory, Demo: fileGridDemo, Code: filesFileGridCode, Keys: "方向键选择;Command/Ctrl+点击多选;Shift 扩展;Enter 打开。"}, + {ID: "383", Name: "FileIcon", ZhName: "文件图标", Category: filesCategory, Demo: fileIconDemo, Code: filesFileIconCode, Keys: "展示组件,沿用父级焦点。"}, + {ID: "384", Name: "FilePreview", ZhName: "文件预览", Category: filesCategory, Demo: filePreviewDemo, Code: filesFilePreviewCode, Keys: "Escape 关闭;Tab 到操作;文本支持选择与复制。"}, + {ID: "385", Name: "PathBar", ZhName: "路径栏", Category: filesCategory, Demo: pathBarDemo, Code: filesPathBarCode, Keys: "Tab 进入;左右移动;Enter 激活;Escape 关闭折叠菜单。"}, + {ID: "386", Name: "RenameInline", ZhName: "行内重命名", Category: filesCategory, Demo: renameInlineDemo, Code: filesRenameInlineCode, Keys: "Enter 确认;Escape 取消;双击开始编辑;失焦确认有效名称。"}, + {ID: "387", Name: "FileOperationProgress", ZhName: "文件操作进度", Category: filesCategory, Demo: fileOperationProgressDemo, Code: filesFileOperationProgressCode, Keys: "Tab 到取消、全选规则和冲突选项;Enter / Space 激活。"}, + {ID: "388", Name: "TransferQueue", ZhName: "传输队列", Category: filesCategory, Demo: transferQueueDemo, Code: filesTransferQueueCode, Keys: "Tab 在可见行操作间移动;Enter / Space 激活。"}, + {ID: "389", Name: "StorageUsage", ZhName: "存储占用", Category: filesCategory, Demo: storageUsageDemo, Code: filesStorageUsageCode, Keys: "Tab 聚焦分段条;左右循环读取分类与剩余空间。"}, + {ID: "390", Name: "RecentFiles", ZhName: "最近文件", Category: filesCategory, Demo: recentFilesDemo, Code: filesRecentFilesCode, Keys: "上下选择文件;Enter 打开;Tab 到清除和移除按钮。"}, + {ID: "391", Name: "ArchiveViewer", ZhName: "压缩包浏览", Category: filesCategory, Demo: archiveViewerDemo, Code: filesArchiveViewerCode, Keys: "上下选择;左右展开折叠;Enter 打开;Tab 到列操作。"}, + {ID: "392", Name: "PresenceAvatars", ZhName: "协作头像", Category: filesCategory, Demo: presenceAvatarsDemo, Code: filesPresenceAvatarsCode, Keys: "Tab 进入;左右移动;Enter 激活;Escape 关闭溢出名单。"}, + {ID: "394", Name: "Reactions", ZhName: "表情回应", Category: filesCategory, Demo: reactionsDemo, Code: filesReactionsCode, Keys: "Tab 到回应与添加;Enter / Space 激活;选择器左右导航;Escape 关闭。"}, + {ID: "395", Name: "ShareDialog", ZhName: "分享对话框", Category: filesCategory, Demo: shareDialogDemo, Code: filesShareDialogCode, Keys: "Tab 在对话框内移动;Enter 邀请或激活操作;Escape 关闭并恢复焦点。"}, + {ID: "396", Name: "PermissionSelect", ZhName: "权限选择", Category: filesCategory, Demo: permissionSelectDemo, Code: filesPermissionSelectCode, Keys: "Enter 打开;上下移动;Enter 确认;Escape 关闭。"}, + {ID: "399", Name: "LiveIndicator", ZhName: "同步状态", Category: filesCategory, Demo: liveIndicatorDemo, Code: filesLiveIndicatorCode, Keys: "Tab 到重试;Enter / Space 激活。"}, + {ID: "400", Name: "RemoteCursor", ZhName: "远程光标", Category: filesCategory, Demo: remoteCursorDemo, Code: filesRemoteCursorCode, Keys: "输入穿透至下方编辑器。"}, +} diff --git a/examples/gallery/pages_files_browser.go b/examples/gallery/pages_files_browser.go new file mode 100644 index 0000000..901957f --- /dev/null +++ b/examples/gallery/pages_files_browser.go @@ -0,0 +1,362 @@ +package main + +import ( + "fmt" + "path" + "strings" + "time" + + "github.com/egoist/mygo/ui" + mujica "mujicaui" +) + +var filesFolders = map[string][]string{ + "/court": {"chronicles/", "decrees/", "seal.go", "notes.md"}, + "/court/chronicles": {"first.md", "second.md"}, + "/court/decrees": {"42.pdf", "crest.png"}, +} + +func filesEntries(dir string) []mujica.FileEntry { + var entries []mujica.FileEntry + for _, n := range filesFolders[dir] { + name := strings.TrimSuffix(n, "/") + entries = append(entries, mujica.FileEntry{Path: path.Join(dir, name), Name: name, Dir: name != n, Size: 2048}) + } + return entries +} + +func fileExplorerDemo(c *ui.Context, log func(string)) { + type state struct { + explorer mujica.FileExplorerState + disabled, empty bool + } + s := ui.Local(c.Root(), "381", func() state { return state{} }) + mediaCheck(c, log, &s.disabled, "Disabled") + mediaCheck(c, log, &s.empty, "Empty folder") + r := mujica.FileExplorer(c, &s.explorer, mujica.FileExplorerOptions{ + Roots: []string{"/court"}, Disabled: s.disabled, + Folders: func(dir string) []string { + var dirs []string + for _, e := range filesEntries(dir) { + if e.Dir { + dirs = append(dirs, e.Path) + } + } + return dirs + }, + Entries: func(dir string) []mujica.FileEntry { + if s.empty { + return nil + } + return filesEntries(dir) + }, + }) + r.Element.Height(320).FillWidth() + if r.Navigated { + log("Navigate: " + s.explorer.Current) + } + if r.Changed { + log(fmt.Sprintf("Selected: %v", s.explorer.Selected())) + } + if r.Opened != nil { + log("Open: " + r.Opened.Path) + } +} + +func fileGridDemo(c *ui.Context, log func(string)) { + type state struct { + grid mujica.FileGridState + entries []mujica.FileEntry + disabled, thumbs, empty bool + } + s := ui.Local(c.Root(), "382", func() state { + return state{thumbs: true, entries: append(filesEntries("/court"), filesEntries("/court/decrees")...)} + }) + mediaCheck(c, log, &s.disabled, "Disabled") + mediaCheck(c, log, &s.thumbs, "Thumbnails") + mediaCheck(c, log, &s.empty, "Empty") + entries := s.entries + if s.empty { + entries = nil + } + r := mujica.FileGrid(c, entries, &s.grid, mujica.FileGridOptions{Disabled: s.disabled, Thumbnail: func(e mujica.FileEntry) bool { + if !s.thumbs || mujica.FileKindOf(e.Name) != mujica.FileImage { + return false + } + mujica.Image(c, mediaBitmaps[0], mujica.ImageOptions{Alt: e.Name}).Element.Fill() + return true + }}) + r.Element.Height(320).FillWidth() + if r.Changed { + log(fmt.Sprintf("Selected: %v", s.grid.Selected())) + } + if r.Opened != nil { + log("Open: " + r.Opened.Path) + } +} + +func fileIconDemo(c *ui.Context, log func(string)) { + type state struct { + name string + folder, open bool + size float64 + } + s := ui.Local(c.Root(), "383", func() state { return state{name: "seal.go", size: 32} }) + if mujica.TextInput(c, &s.name, mujica.TextInputOptions{Label: "File name"}).Changed() { + log("File name changed") + } + mediaCheck(c, log, &s.folder, "Folder") + if s.folder { + mediaCheck(c, log, &s.open, "Open folder") + } + if mujica.NumberInput(c, &s.size, mujica.NumberInputOptions{Min: 20, Max: 64, Step: 4, Label: "Icon size"}).Changed() { + log(fmt.Sprintf("Size: %.0f", s.size)) + } + mujica.FileIcon(c, s.name, mujica.FileIconOptions{Folder: s.folder, Open: s.folder && s.open, Size: float32(s.size)}) + ui.Row(c).Gap(12).Children(func() { + for _, name := range []string{"seal.go", "notes.md", "crest.png", "deed.pdf", "ledger.zip", "hymn.mp3", "rite.mov", "blob"} { + mujica.FileIcon(c, name, mujica.FileIconOptions{Size: 32}) + } + }) +} + +func filePreviewDemo(c *ui.Context, log func(string)) { + type state struct { + kind int + status mujica.DataStatus + closed bool + } + s := ui.Local(c.Root(), "384", func() state { return state{} }) + if mujica.Select(c, &s.kind, []mujica.SelectOption[int]{{Value: 0, Label: "Code"}, {Value: 1, Label: "Text"}, {Value: 2, Label: "Image"}, {Value: 3, Label: "Unsupported"}}, mujica.SelectOptions{Label: "Preview type"}).Changed() { + log("Preview type changed") + s.closed = false + } + filesStatus(c, log, &s.status) + if s.closed { + if mujica.Button(c, "Show preview", mujica.ButtonOptions{}).Clicked() { + s.closed = false + log("Preview reopened") + } + return + } + f := []mujica.PreviewFile{{Name: "seal.go", Text: "package court\n\nfunc Seal() {}"}, {Name: "notes.md", Text: "The seal of the court."}, {Name: "crest.png", Image: mediaBitmaps[0]}, {Name: "deed.pdf", Size: 4096}}[s.kind] + r := mujica.FilePreview(c, f, mujica.FilePreviewOptions{Status: s.status, Error: "Reading failed"}) + r.Element.Height(280).FillWidth() + if r.Closed { + s.closed = true + log("Preview closed") + } + if r.Opened { + log("Open: " + f.Name) + } + if r.Retried { + s.status = mujica.DataReady + log("Read retried") + } +} + +func filesStatus(c *ui.Context, log func(string), value *mujica.DataStatus) { + if mujica.Select(c, value, []mujica.SelectOption[mujica.DataStatus]{{Value: mujica.DataReady, Label: "Ready"}, {Value: mujica.DataLoading, Label: "Loading"}, {Value: mujica.DataFailed, Label: "Failed"}}, mujica.SelectOptions{Label: "Load status"}).Changed() { + log(fmt.Sprintf("Status: %d", *value)) + } +} + +func pathBarDemo(c *ui.Context, log func(string)) { + type state struct { + path string + disabled bool + } + s := ui.Local(c.Root(), "385", func() state { return state{path: "/court/chronicles/royal/archives/seals"} }) + mediaCheck(c, log, &s.disabled, "Disabled") + if mujica.TextInput(c, &s.path, mujica.TextInputOptions{Label: "Path"}).Changed() { + log("Path changed") + } + if strings.Trim(s.path, "/") == "" { + ui.Text(c, "Enter a path") + return + } + if r := mujica.PathBar(c, s.path, mujica.PathBarOptions{Disabled: s.disabled}); r.Navigated != "" { + s.path = r.Navigated + log("Navigate: " + r.Navigated) + } +} + +func renameInlineDemo(c *ui.Context, log func(string)) { + type state struct { + name string + editing, disabled, folder bool + } + s := ui.Local(c.Root(), "386", func() state { return state{name: "seal.txt"} }) + mediaCheck(c, log, &s.disabled, "Disabled") + mediaCheck(c, log, &s.folder, "Folder") + if mujica.Button(c, "Rename", mujica.ButtonOptions{Disabled: s.disabled}).Clicked() { + s.editing = true + log("Rename started") + } + r := mujica.RenameInline(c, &s.name, &s.editing, mujica.RenameInlineOptions{Disabled: s.disabled, Folder: s.folder, Validate: func(name string) string { + if strings.ContainsAny(name, "/\\") { + return "Name contains a path separator" + } + return "" + }}) + if r.Renamed { + log("Renamed: " + s.name) + } + if r.Canceled { + log("Rename canceled") + } +} + +func fileOperationProgressDemo(c *ui.Context, log func(string)) { + type state struct { + op mujica.FileOperation + disabled, conflict bool + canceled bool + } + s := ui.Local(c.Root(), "387", func() state { + return state{op: mujica.FileOperation{Kind: mujica.FileCopy, Total: 10, Current: "seal.go"}} + }) + mediaCheck(c, log, &s.disabled, "Disabled") + mediaCheck(c, log, &s.conflict, "Conflict") + if mujica.Select(c, &s.op.Kind, []mujica.SelectOption[mujica.FileOperationKind]{{Value: mujica.FileCopy, Label: "Copy"}, {Value: mujica.FileMove, Label: "Move"}, {Value: mujica.FileDelete, Label: "Delete"}}, mujica.SelectOptions{Label: "Operation"}).Changed() { + log("Operation changed") + } + if mujica.Button(c, "Advance", mujica.ButtonOptions{Disabled: s.op.Done == s.op.Total}).Clicked() { + s.op.Done++ + log(fmt.Sprintf("Done: %d", s.op.Done)) + } + if mujica.Button(c, "Reset", mujica.ButtonOptions{}).Clicked() { + s.op.Done = 0 + s.canceled = false + log("Operation reset") + } + op := s.op + if s.conflict { + op.Conflict = "seal.go" + } + r := mujica.FileOperationProgress(c, op, mujica.FileOperationOptions{Disabled: s.disabled || s.canceled}) + if r.Canceled { + s.canceled = true + log("Operation canceled") + } + if r.Resolved != mujica.ConflictNone { + s.conflict = false + log(fmt.Sprintf("Conflict: %d; apply all: %v", r.Resolved, r.ApplyAll)) + } +} + +func filesTransfers() []mujica.QueuedTransfer { + return []mujica.QueuedTransfer{ + {ID: "a", Name: "ledger.zip", Upload: true, Status: mujica.TransferActive, Done: 30, Total: 120}, + {ID: "b", Name: "crest.png", Status: mujica.TransferPaused, Done: 1, Total: 4}, + {ID: "c", Name: "deed.pdf", Upload: true, Status: mujica.TransferFailed, Total: 2, Error: "Network lost"}, + } +} + +func transferQueueDemo(c *ui.Context, log func(string)) { + type state struct { + items []mujica.QueuedTransfer + queue mujica.TransferQueueState + disabled bool + revision uint64 + } + s := ui.Local(c.Root(), "388", func() state { return state{items: filesTransfers()} }) + mediaCheck(c, log, &s.disabled, "Disabled") + if mujica.Button(c, "Reset", mujica.ButtonOptions{}).Clicked() { + s.items = filesTransfers() + log("Queue reset") + } + r := mujica.TransferQueue(c, s.items, &s.queue, mujica.TransferQueueOptions{Disabled: s.disabled, Revision: s.revision}) + r.Element.Height(260).FillWidth() + if r.Action == mujica.TransferNone { + return + } + for i := range s.items { + if s.items[i].ID == r.ID { + switch r.Action { + case mujica.TransferPause: + s.items[i].Status = mujica.TransferPaused + case mujica.TransferResume, mujica.TransferRetry: + s.items[i].Status = mujica.TransferActive + case mujica.TransferRemove: + s.items = append(s.items[:i:i], s.items[i+1:]...) + } + break + } + } + s.revision++ + log(fmt.Sprintf("Transfer %s: %d", r.ID, r.Action)) +} + +func storageUsageDemo(c *ui.Context, log func(string)) { + type state struct{ photos float64 } + s := ui.Local(c.Root(), "389", func() state { return state{photos: 40} }) + if mujica.NumberInput(c, &s.photos, mujica.NumberInputOptions{Min: 0, Max: 92, Step: 1, Label: "Photos (GB)"}).Changed() { + log(fmt.Sprintf("Photos: %.0f GB", s.photos)) + } + mujica.StorageUsage(c, []mujica.StorageSegment{{Label: "Documents", Bytes: 24 << 30}, {Label: "Photos", Bytes: int64(s.photos) * (1 << 30)}, {Label: "Apps", Bytes: 12 << 30}}, mujica.StorageUsageOptions{Capacity: 128 << 30, Label: "Court disk"}).FillWidth() +} + +func filesRecent() []mujica.RecentFile { + now := time.Date(2026, 10, 5, 15, 0, 0, 0, time.UTC) + return []mujica.RecentFile{{Path: "/court/seal.go", Opened: now}, {Path: "/court/notes.md", Opened: now.AddDate(0, 0, -1)}, {Path: "/court/crest.png", Opened: now.AddDate(0, 0, -4)}, {Path: "/court/charter.pdf", Opened: now.AddDate(0, -1, 0)}} +} + +func recentFilesDemo(c *ui.Context, log func(string)) { + type state struct { + files []mujica.RecentFile + recent mujica.RecentFilesState + disabled bool + } + s := ui.Local(c.Root(), "390", func() state { return state{files: filesRecent()} }) + mediaCheck(c, log, &s.disabled, "Disabled") + if mujica.Button(c, "Reset", mujica.ButtonOptions{}).Clicked() { + s.files = filesRecent() + log("Recent files reset") + } + r := mujica.RecentFiles(c, s.files, &s.recent, mujica.RecentFilesOptions{Disabled: s.disabled, Now: time.Date(2026, 10, 5, 15, 0, 0, 0, time.UTC)}) + r.Element.Height(320).FillWidth() + if r.Opened != nil { + log("Open: " + r.Opened.Path) + } + if r.Cleared { + s.files = nil + log("Recent files cleared") + } + if r.Removed != "" { + for i, f := range s.files { + if f.Path == r.Removed { + s.files = append(s.files[:i:i], s.files[i+1:]...) + break + } + } + log("Removed: " + r.Removed) + } +} + +func archiveViewerDemo(c *ui.Context, log func(string)) { + type state struct { + archive mujica.ArchiveViewerState + entries []mujica.ArchiveEntry + status mujica.DataStatus + empty bool + } + s := ui.Local(c.Root(), "391", func() state { + return state{entries: []mujica.ArchiveEntry{{Path: "court/seal.go", Size: 4000, Packed: 1000}, {Path: "court/decrees/42.pdf", Size: 6000, Packed: 5000}, {Path: "README.md", Size: 1000, Packed: 400}}} + }) + filesStatus(c, log, &s.status) + mediaCheck(c, log, &s.empty, "Empty") + entries := s.entries + if s.empty { + entries = nil + } + r := mujica.ArchiveViewer(c, entries, &s.archive, mujica.ArchiveViewerOptions{Status: s.status, Error: "Invalid archive"}) + r.Element.Height(280).FillWidth() + if r.Opened != nil { + log("Open archive entry: " + r.Opened.Path) + } + if r.Retried { + s.status = mujica.DataReady + log("Archive read retried") + } +} diff --git a/examples/gallery/pages_files_code.go b/examples/gallery/pages_files_code.go new file mode 100644 index 0000000..7b1c108 --- /dev/null +++ b/examples/gallery/pages_files_code.go @@ -0,0 +1,137 @@ +package main + +const filesFileExplorerCode = `var state mujica.FileExplorerState +opts := mujica.FileExplorerOptions{ + Roots: []string{"/court"}, + Folders: func(string) []string { return nil }, + Entries: func(string) []mujica.FileEntry { + return []mujica.FileEntry{{Path: "/court/seal.go", Name: "seal.go", Size: 1024}} + }, +} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FileExplorer(c, &state, opts).Element.Height(240) +} +ui.Render(view, 600, 300, 1)` + +const filesFileGridCode = `entries := []mujica.FileEntry{{Path: "/court/seal.go", Name: "seal.go"}} +var state mujica.FileGridState +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FileGrid(c, entries, &state, mujica.FileGridOptions{}).Element.Height(240) +} +ui.Render(view, 400, 300, 1)` + +const filesFileIconCode = `view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FileIcon(c, "seal.go", mujica.FileIconOptions{Size: 32}) +} +ui.Render(view, 100, 80, 1)` + +const filesFilePreviewCode = `file := mujica.PreviewFile{Name: "seal.go", Text: "package court\nfunc Seal() {}"} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FilePreview(c, file, mujica.FilePreviewOptions{}).Element.Height(240) +} +ui.Render(view, 500, 300, 1)` + +const filesPathBarCode = `view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.PathBar(c, "/court/chronicles/seals", mujica.PathBarOptions{}) +} +ui.Render(view, 400, 100, 1)` + +const filesRenameInlineCode = `name, editing := "seal.txt", true +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.RenameInline(c, &name, &editing, mujica.RenameInlineOptions{}) +} +ui.Render(view, 400, 100, 1)` + +const filesFileOperationProgressCode = `op := mujica.FileOperation{Kind: mujica.FileCopy, Done: 2, Total: 10, Current: "seal.go"} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.FileOperationProgress(c, op, mujica.FileOperationOptions{}) +} +ui.Render(view, 500, 240, 1)` + +const filesTransferQueueCode = `items := []mujica.QueuedTransfer{{ID: "a", Name: "ledger.zip", Upload: true, Status: mujica.TransferActive, Done: 25, Total: 100}} +var state mujica.TransferQueueState +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.TransferQueue(c, items, &state, mujica.TransferQueueOptions{}).Element.Height(240) +} +ui.Render(view, 500, 300, 1)` + +const filesStorageUsageCode = `segments := []mujica.StorageSegment{{Label: "Documents", Bytes: 24 << 30}, {Label: "Photos", Bytes: 40 << 30}} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.StorageUsage(c, segments, mujica.StorageUsageOptions{Capacity: 128 << 30, Label: "Court disk"}) +} +ui.Render(view, 500, 240, 1)` + +const filesRecentFilesCode = `now := time.Date(2026, 10, 5, 12, 0, 0, 0, time.UTC) +files := []mujica.RecentFile{{Path: "/court/seal.go", Opened: now}} +var state mujica.RecentFilesState +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.RecentFiles(c, files, &state, mujica.RecentFilesOptions{Now: now}).Element.Height(240) +} +ui.Render(view, 500, 300, 1)` + +const filesArchiveViewerCode = `entries := []mujica.ArchiveEntry{{Path: "court/seal.go", Size: 4000, Packed: 1000}} +var state mujica.ArchiveViewerState +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.ArchiveViewer(c, entries, &state, mujica.ArchiveViewerOptions{}).Element.Height(240) +} +ui.Render(view, 600, 300, 1)` + +const filesPresenceAvatarsCode = `people := []mujica.Collaborator{{ID: "ada", Name: "Ada Lovelace"}} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.PresenceAvatars(c, people, mujica.PresenceAvatarsOptions{}) +} +ui.Render(view, 300, 100, 1)` + +const filesReactionsCode = `items := []mujica.Reaction{{Emoji: "\U0001f44d", Count: 3, Mine: true}} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.Reactions(c, items, mujica.ReactionsOptions{Choices: []string{"\U0001f44d", "\U0001f389"}}) +} +ui.Render(view, 400, 100, 1)` + +const filesShareDialogCode = `open := false +members := []mujica.ShareMember{{ID: "ada", Name: "Ada", Permission: mujica.PermissionEdit, Owner: true}} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + if mujica.Button(c, "Share", mujica.ButtonOptions{}).Clicked() { + open = true + } + mujica.ShareDialog(c, &open, &members, mujica.ShareDialogOptions{Title: "Ledger", Link: "https://court.org/ledger"}) +} +ui.Render(view, 600, 400, 1)` + +const filesPermissionSelectCode = `value := mujica.PermissionView +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.PermissionSelect(c, &value, mujica.PermissionSelectOptions{}) +} +ui.Render(view, 300, 100, 1)` + +const filesLiveIndicatorCode = `view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + mujica.LiveIndicator(c, mujica.LiveOffline, mujica.LiveIndicatorOptions{}) +} +ui.Render(view, 500, 100, 1)` + +const filesRemoteCursorCode = `carets := []mujica.RemoteCaret{{ID: "ada", Name: "Ada", Offset: 4, Anchor: 4}} +view := func(c *ui.Context) { + mujica.Use(c, mujica.Settings{}) + ui.Box(c).Size(300, 100).Children(func() { + mujica.RemoteCursor(c, carets, mujica.RemoteCursorOptions{ + Caret: func(offset int) ui.Rect { return ui.Rect{X: float32(offset) * 8, Y: 20, H: 20} }, + }) + }) +} +ui.Render(view, 320, 120, 1)` diff --git a/examples/gallery/pages_files_collaboration.go b/examples/gallery/pages_files_collaboration.go new file mode 100644 index 0000000..07df5a7 --- /dev/null +++ b/examples/gallery/pages_files_collaboration.go @@ -0,0 +1,195 @@ +package main + +import ( + "fmt" + "time" + + "github.com/egoist/mygo/ui" + mujica "mujicaui" + "mujicaui/theme" +) + +var filesPeople = []mujica.Collaborator{ + {ID: "a", Name: "Ada Lovelace"}, {ID: "b", Name: "Blaise Pascal", Away: true}, + {ID: "c", Name: "Carl Gauss"}, {ID: "d", Name: "Dora Maar"}, {ID: "e", Name: "Emmy Noether"}, +} + +func presenceAvatarsDemo(c *ui.Context, log func(string)) { + type state struct { + following string + disabled, empty bool + max float64 + } + s := ui.Local(c.Root(), "392", func() state { return state{max: 3} }) + mediaCheck(c, log, &s.disabled, "Disabled") + mediaCheck(c, log, &s.empty, "Empty") + if mujica.NumberInput(c, &s.max, mujica.NumberInputOptions{Min: 1, Max: 5, Step: 1, Label: "Visible avatars"}).Changed() { + log(fmt.Sprintf("Visible: %.0f", s.max)) + } + people := filesPeople + if s.empty { + people = nil + } + r := mujica.PresenceAvatars(c, people, mujica.PresenceAvatarsOptions{Following: s.following, Max: int(s.max), Disabled: s.disabled}) + if r.Follow != "" { + s.following = r.Follow + log("Follow: " + r.Follow) + } + if r.Unfollow { + s.following = "" + log("Following stopped") + } +} + +var filesReactionChoices = []string{"\U0001f44d", "\u2764\ufe0f", "\U0001f389", "\U0001f440", "\U0001f64f"} + +func reactionsDemo(c *ui.Context, log func(string)) { + type state struct { + items []mujica.Reaction + disabled bool + } + s := ui.Local(c.Root(), "394", func() state { + return state{items: []mujica.Reaction{{Emoji: filesReactionChoices[0], Count: 3, Mine: true}, {Emoji: filesReactionChoices[2], Count: 1}}} + }) + mediaCheck(c, log, &s.disabled, "Disabled") + if mujica.Button(c, "Clear reactions", mujica.ButtonOptions{}).Clicked() { + s.items = nil + log("Reactions cleared") + } + r := mujica.Reactions(c, s.items, mujica.ReactionsOptions{Choices: filesReactionChoices, Disabled: s.disabled}) + emoji := r.Toggled + if emoji == "" { + emoji = r.Added + } + if emoji == "" { + return + } + for i := range s.items { + if s.items[i].Emoji == emoji { + if s.items[i].Mine { + s.items[i].Mine = false + s.items[i].Count-- + } else { + s.items[i].Mine = true + s.items[i].Count++ + } + if s.items[i].Count == 0 { + s.items = append(s.items[:i:i], s.items[i+1:]...) + } + log("Reaction toggled") + return + } + } + s.items = append(s.items, mujica.Reaction{Emoji: emoji, Count: 1, Mine: true}) + log("Reaction added") +} + +func shareDialogDemo(c *ui.Context, log func(string)) { + type state struct { + members []mujica.ShareMember + open, disabled bool + next int + } + s := ui.Local(c.Root(), "395", func() state { + return state{members: []mujica.ShareMember{ + {ID: "owner", Name: "Ada Lovelace", Email: "ada@court.org", Owner: true, Permission: mujica.PermissionEdit}, + {ID: "b", Name: "Blaise Pascal", Email: "blaise@court.org", Permission: mujica.PermissionView}, + }} + }) + mediaCheck(c, log, &s.disabled, "Disabled") + if mujica.Button(c, "Share Ledger", mujica.ButtonOptions{Disabled: s.disabled}).Clicked() { + s.open = true + log("Share dialog opened") + } + was := s.open + r := mujica.ShareDialog(c, &s.open, &s.members, mujica.ShareDialogOptions{Title: "Ledger", Link: "https://court.org/ledger", Disabled: s.disabled, OnCopy: func(link string) { c.WriteClipboard(link); log("Copy callback") }}) + if r.Invited != nil { + s.next++ + s.members = append(s.members, mujica.ShareMember{ID: fmt.Sprintf("invite-%d", s.next), Name: r.Invited.Email, Email: r.Invited.Email, Permission: r.Invited.Permission}) + log("Member invited") + } + if r.Changed != "" { + log("Permission changed: " + r.Changed) + } + if r.Removed != "" { + log("Member removed: " + r.Removed) + } + if r.Copied { + log("Link copied") + } + if was && !s.open { + log("Share dialog closed") + } +} + +func permissionSelectDemo(c *ui.Context, log func(string)) { + type state struct { + value mujica.Permission + disabled bool + } + s := ui.Local(c.Root(), "396", func() state { return state{} }) + mediaCheck(c, log, &s.disabled, "Disabled") + if mujica.PermissionSelect(c, &s.value, mujica.PermissionSelectOptions{Disabled: s.disabled}).Changed() { + log(fmt.Sprintf("Permission: %d", s.value)) + } +} + +func liveIndicatorDemo(c *ui.Context, log func(string)) { + type state struct { + status mujica.LiveStatus + showTime bool + } + s := ui.Local(c.Root(), "399", func() state { return state{showTime: true} }) + if mujica.Select(c, &s.status, []mujica.SelectOption[mujica.LiveStatus]{{Value: mujica.LiveSynced, Label: "Synced"}, {Value: mujica.LiveSyncing, Label: "Syncing"}, {Value: mujica.LiveReconnecting, Label: "Reconnecting"}, {Value: mujica.LiveOffline, Label: "Offline"}, {Value: mujica.LiveError, Label: "Error"}}, mujica.SelectOptions{Label: "Sync status"}).Changed() { + log(fmt.Sprintf("Sync status: %d", s.status)) + } + mediaCheck(c, log, &s.showTime, "Last sync time") + now := time.Date(2026, 10, 5, 12, 0, 0, 0, time.UTC) + opts := mujica.LiveIndicatorOptions{Error: "Server unavailable", Now: now} + if s.showTime { + opts.LastSynced = now.Add(-3 * time.Minute) + } + if mujica.LiveIndicator(c, s.status, opts).Changed() { + s.status = mujica.LiveReconnecting + log("Sync retried") + } +} + +func remoteCursorDemo(c *ui.Context, log func(string)) { + type state struct { + offset float64 + selection, second bool + } + s := ui.Local(c.Root(), "400", func() state { return state{offset: 48, selection: true, second: true} }) + if mujica.NumberInput(c, &s.offset, mujica.NumberInputOptions{Min: 0, Max: 79, Step: 1, Label: "Remote text offset"}).Changed() { + log(fmt.Sprintf("Offset: %.0f", s.offset)) + } + mediaCheck(c, log, &s.selection, "Selection") + mediaCheck(c, log, &s.second, "Second collaborator") + carets := []mujica.RemoteCaret{{ID: "ada", Name: "Ada", Offset: int(s.offset), Anchor: int(s.offset)}} + if s.selection { + carets[0].Anchor = max(0, carets[0].Offset-5) + } + if s.second { + carets = append(carets, mujica.RemoteCaret{ID: "blaise", Name: "Blaise", Offset: 8, Anchor: 8}) + } + ui.Box(c).Height(140).FillWidth().Background(mujica.Tokens(c).Surface).Children(func() { + ui.Column(c).Children(func() { + for _, line := range []string{"The seal of the court is kept", "by the chancellor, who answers"} { + ui.Text(c, line).Font(theme.MonoFont).FontSize(mujica.FontSize(c, 13)).Height(20) + } + }) + at := func(o int) (float32, float32) { return float32(o%40) * 8, float32(o/40) * 20 } + mujica.RemoteCursor(c, carets, mujica.RemoteCursorOptions{ + Caret: func(o int) ui.Rect { x, y := at(o); return ui.Rect{X: x, Y: y, H: 20} }, + Selection: func(from, to int) []ui.Rect { + var out []ui.Rect + for o := from; o < to; o++ { + x, y := at(o) + out = append(out, ui.Rect{X: x, Y: y, W: 8, H: 20}) + } + return out + }, + }) + }) +} diff --git a/examples/gallery/pages_files_test.go b/examples/gallery/pages_files_test.go new file mode 100644 index 0000000..7d1b593 --- /dev/null +++ b/examples/gallery/pages_files_test.go @@ -0,0 +1,116 @@ +package main + +import ( + "fmt" + "strings" + "testing" + + "github.com/egoist/mygo/ui" + mujica "mujicaui" +) + +func filesDemoTester(t *testing.T, id string, mode mujica.Mode) (*ui.Tester, *[]string) { + t.Helper() + for _, p := range filesPages { + if p.ID == id { + events := &[]string{} + tt := ui.NewTester(func(c *ui.Context) { + mujica.Use(c, mujica.Settings{Mode: mode}) + ui.Scroll(c).Fill().Padding(24).Gap(12).Children(func() { p.Demo(c, func(s string) { *events = append(*events, s) }) }) + }, 900, 800) + tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1}) + tt.Frame() + return tt, events + } + } + t.Fatalf("missing files page %s", id) + return nil, nil +} + +func TestFilesGalleryExistingSet(t *testing.T) { + want := []string{"381", "382", "383", "384", "385", "386", "387", "388", "389", "390", "391", "392", "394", "395", "396", "399", "400"} + if len(filesPages) != len(want) { + t.Fatalf("files pages %d, want %d", len(filesPages), len(want)) + } + for i, p := range filesPages { + if p.ID != want[i] || p.Category != "Files & Collaboration" || p.Code == "" || p.Keys == "" { + t.Fatalf("page %s has incorrect metadata", p.ID) + } + } +} + +func TestFilesGalleryActions(t *testing.T) { + cases := []struct{ id, action, event string }{ + {"381", "Disabled", "Disabled = true"}, + {"382", "Empty", "Empty = true"}, + {"383", "Folder", "Folder = true"}, + {"384", "Close preview", "Preview closed"}, + {"385", "court", "Navigate: /court"}, + {"386", "Rename", "Rename started"}, + {"387", "Cancel", "Operation canceled"}, + {"388", "Pause ledger.zip", "Transfer a: 1"}, + {"390", "Clear", "Recent files cleared"}, + {"391", "Empty", "Empty = true"}, + {"392", "Follow Ada Lovelace", "Follow: a"}, + {"394", "Clear reactions", "Reactions cleared"}, + {"395", "Share Ledger", "Share dialog opened"}, + {"396", "Disabled", "Disabled = true"}, + {"399", "Last sync time", "Last sync time = false"}, + {"400", "Selection", "Selection = false"}, + } + for _, tc := range cases { + for _, mode := range []mujica.Mode{mujica.Light, mujica.Dark} { + t.Run(fmt.Sprintf("%s-%s", tc.id, mode), func(t *testing.T) { + tt, events := filesDemoTester(t, tc.id, mode) + if err := tt.Click(tc.action); err != nil { + t.Fatal(err) + } + found := false + for _, event := range *events { + if event == tc.event { + found = true + } + } + if !found { + t.Fatalf("events %v, want %s", *events, tc.event) + } + if tc.id == "395" { + if err := tt.Click("Copy link"); err != nil { + t.Fatal(err) + } + if tt.Clipboard() != "https://court.org/ledger" { + t.Fatal("share callback did not copy link") + } + } + }) + } + } +} + +func TestFilesGalleryStorageParameter(t *testing.T) { + tt, events := filesDemoTester(t, "389", mujica.Light) + if err := tt.Click("Photos (GB)"); err != nil { + t.Fatal(err) + } + tt.Key(ui.Cmd, ui.KeyA) + tt.Type("41") + tt.Key(0, ui.KeyEnter) + if !tt.HasText("77.0 GB of 128.0 GB used") || len(*events) == 0 { + t.Fatalf("storage parameter left %q; events %v", tt.Texts(), *events) + } +} + +func TestFilesGalleryGridRange(t *testing.T) { + tt, events := filesDemoTester(t, "382", mujica.Light) + if err := tt.Click("notes.md"); err != nil { + t.Fatal(err) + } + tt.Frame() + if err := tt.ClickWith(ui.Shift, "crest.png"); err != nil { + t.Fatal(err) + } + last := (*events)[len(*events)-1] + if strings.Contains(last, "/court/chronicles") || !strings.Contains(last, "/court/notes.md") || !strings.Contains(last, "/court/decrees/crest.png") { + t.Fatalf("range selection event %q", last) + } +} diff --git a/file_explorer.go b/file_explorer.go new file mode 100644 index 0000000..4910f89 --- /dev/null +++ b/file_explorer.go @@ -0,0 +1,254 @@ +package mujica + +import ( + "log/slog" + "path" + "runtime" + "strings" + "time" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/theme" +) + +// FileEntry identifies a file or folder by slash-separated Path. +type FileEntry struct { + Path, Name string + Dir bool + Size int64 + Modified time.Time +} + +// FileExplorerOptions configure a FileExplorer. +type FileExplorerOptions struct { + // Roots are the top folders of the tree. + Roots []string + // Folders returns subfolders; required, nil means a leaf. + Folders func(dir string) []string + // Entries returns a folder's files and folders; required. + Entries func(dir string) []FileEntry + Disabled bool + // Revision changes when entries are edited in place. + Revision uint64 +} + +// FileExplorerState keeps navigation, selection and scroll position. +type FileExplorerState struct { + // Current defaults to the first root. + Current string + tree TreeState[string] + list ListState[string] + back, forward []string + data *FileEntry + n int + revision uint64 +} + +// Selected returns the chosen entries' paths. +func (s *FileExplorerState) Selected() []string { + var out []string + for p := range s.list.Selection.All() { + out = append(out, p) + } + return out +} + +// Navigate records the current folder and shows dir. +func (s *FileExplorerState) Navigate(dir string) { + if dir == s.Current { + return + } + if s.Current != "" { + s.back = append(s.back, s.Current) + } + s.Current, s.forward = dir, nil + s.list.Selection.Clear() + slog.Debug("mujica: file_explorer navigate", "depth", strings.Count(dir, "/")) +} + +func (s *FileExplorerState) step(from, to *[]string) { + n := len(*from) + if n == 0 { + return + } + *to = append(*to, s.Current) + s.Current, *from = (*from)[n-1], (*from)[:n-1] + s.list.Selection.Clear() +} + +// FileExplorerResult is a FileExplorer and what happened in this frame. +type FileExplorerResult struct { + Element *ui.Element + // Navigated reports a new current folder; Changed a new selection. + Navigated, Changed bool + // Opened is a file the user opened. + Opened *FileEntry +} + +// FileExplorer combines folder navigation, history and file selection. +func FileExplorer(c *ui.Context, s *FileExplorerState, opts FileExplorerOptions) FileExplorerResult { + if s == nil || opts.Folders == nil || opts.Entries == nil || len(opts.Roots) == 0 { + panic("mujica: FileExplorer needs a state, Roots, Folders and Entries") + } + if s.Current == "" { + s.Current = opts.Roots[0] + } + k := Tokens(c) + u := Density(c).Unit() + var res FileExplorerResult + res.Element = ui.Column(c).MinHeight(0).Gap(u * 2).Disabled(opts.Disabled) + disabled := res.Element.IsDisabled() + if disabled { + copy := *s + copy.list.Selection = filesSelectionCopy(s.list.Selection) + copy.tree.Selection = filesSelectionCopy(s.tree.Selection) + copy.tree.tree.outline.Open = filesSelectionCopy(s.tree.tree.outline.Open) + s = © + } + at := s.Current + move := func(fn func()) { + if !disabled { + fn() + } + } + mac := runtime.GOOS == "darwin" + switch { + case mac && res.Element.Shortcut(ui.Super, ui.KeyBracketLeft), !mac && res.Element.Shortcut(ui.Alt, ui.KeyLeft): + move(func() { s.step(&s.back, &s.forward) }) + case mac && res.Element.Shortcut(ui.Super, ui.KeyBracketRight), !mac && res.Element.Shortcut(ui.Alt, ui.KeyRight): + move(func() { s.step(&s.forward, &s.back) }) + case mac && res.Element.Shortcut(ui.Super, ui.KeyUp), !mac && res.Element.Shortcut(ui.Alt, ui.KeyUp): + move(func() { s.Navigate(fileExplorerParent(s.Current, opts.Roots)) }) + } + res.Element.Children(func() { + ui.Row(c).Gap(u).AlignItems(ui.Center).Shrink(0).Children(func() { + for _, b := range []struct { + icon, key string + on bool + do func() + }{ + {"chevron-left", "file_explorer.back", len(s.back) > 0, func() { s.step(&s.back, &s.forward) }}, + {"chevron-right", "file_explorer.forward", len(s.forward) > 0, func() { s.step(&s.forward, &s.back) }}, + {"arrow-up", "file_explorer.up", fileExplorerParent(s.Current, opts.Roots) != s.Current, func() { s.Navigate(fileExplorerParent(s.Current, opts.Roots)) }}, + } { + a := iconAction(c, icons.Must(b.icon), msg(c, b.key)).Disabled(!b.on) + if a.Clicked() && b.on { + move(b.do) + } + } + if r := PathBar(c, s.Current, PathBarOptions{Disabled: opts.Disabled}); r.Navigated != "" { + move(func() { s.Navigate(r.Navigated) }) + } + }) + ui.Row(c).Grow(1).MinHeight(Density(c).ControlHeight() * 4).Gap(u * 2).AlignItems(ui.Stretch).Children(func() { + fileExplorerTree(c, s, opts, move) + ui.Column(c).Grow(1).MinWidth(0).Children(func() { + entries := opts.Entries(s.Current) + var first *FileEntry + if len(entries) > 0 { + first = &entries[0] + } + if s.data != first || s.n != len(entries) || s.revision != opts.Revision { + valid := make(map[string]bool, len(entries)) + for _, entry := range entries { + if entry.Path == "" || valid[entry.Path] { + panic("mujica: FileExplorer needs unique entry paths") + } + valid[entry.Path] = true + } + for key := range s.list.Selection.All() { + if !valid[key] { + s.list.Selection.Remove(key) + } + } + s.data, s.n, s.revision = first, len(entries), opts.Revision + } + v := List(c, &s.list, len(entries), ListOptions[string]{Multiple: true, + Key: func(i int) string { return entries[i].Path }, Label: func(i int) string { return entries[i].Name }, + Empty: msg(c, "file_explorer.empty")}, func(i int) { fileExplorerRow(c, k, entries[i]) }) + v.Element.Grow(1) + res.Changed = v.Changed() && !disabled + if key, ok := s.list.Selected(); ok && v.Submitted() && !disabled { + for _, e := range entries { + if e.Path != key { + continue + } + if e.Dir { + s.Navigate(e.Path) + } else { + open := e + res.Opened = &open + slog.Debug("mujica: file_explorer open") + } + } + } + }) + }) + }) + res.Navigated = s.Current != at + return res +} + +func filesSelectionCopy(src ui.Selection[string]) ui.Selection[string] { + var dst ui.Selection[string] + for key := range src.All() { + dst.Add(key) + } + return dst +} + +// fileExplorerTree shows the folders, choosing the current one. +func fileExplorerTree(c *ui.Context, s *FileExplorerState, opts FileExplorerOptions, move func(func())) { + for _, a := range fileExplorerAncestors(s.Current, opts.Roots) { + s.tree.SetOpen(a, true) + } + s.tree.tree.sel, s.tree.tree.has = s.Current, true + v := Tree(c, &s.tree, TreeOptions[string]{Roots: opts.Roots, Children: opts.Folders, + Label: func(p string) string { return path.Base(p) }}) + v.Element.Width(220).Shrink(0) + if v.Changed() { + if dir, ok := s.tree.Selected(); ok && dir != s.Current { + move(func() { s.Navigate(dir) }) + } + } +} + +// fileExplorerAncestors lists the folders above dir, inside its root. +func fileExplorerAncestors(dir string, roots []string) []string { + var out []string + for p := dir; ; { + parent := fileExplorerParent(p, roots) + if parent == p { + return out + } + out = append(out, parent) + p = parent + } +} + +// fileExplorerParent is dir's parent, or dir itself at a root. +func fileExplorerParent(dir string, roots []string) string { + for _, r := range roots { + if dir == r { + return dir + } + } + p := path.Dir(dir) + if p == "." || p == dir { + return dir + } + return p +} + +func fileExplorerRow(c *ui.Context, k theme.Tokens, e FileEntry) { + FileIcon(c, e.Name, FileIconOptions{Folder: e.Dir}) + ui.Text(c, e.Name).SingleLine().Grow(1).MinWidth(0) + if !e.Dir { + ui.Text(c, processListBytes(uint64(max(e.Size, 0)))).TextColor(k.TextMuted).FontSize(FontSize(c, theme.CaptionSize)).Shrink(0) + } + if !e.Modified.IsZero() { + ui.Text(c, e.Modified.Format("2006-01-02 15:04")).TextColor(k.TextMuted).FontSize(FontSize(c, theme.CaptionSize)).Shrink(0) + } +} diff --git a/file_explorer_test.go b/file_explorer_test.go new file mode 100644 index 0000000..5fb6f2b --- /dev/null +++ b/file_explorer_test.go @@ -0,0 +1,228 @@ +package mujica + +import ( + "fmt" + "path" + "runtime" + "strings" + "testing" + + "github.com/egoist/mygo/ui" +) + +// fileFS maps folders to entry names, with trailing directory slashes. +var fileFS = map[string][]string{ + "/court": {"chronicles/", "decrees/", "seal.go", "notes.md"}, + "/court/chronicles": {"first.md", "second.md"}, + "/court/decrees": {"old/", "42.pdf"}, + "/court/decrees/old": {"7.pdf"}, +} + +func fileFSOptions() FileExplorerOptions { + return FileExplorerOptions{ + Roots: []string{"/court"}, + Folders: func(dir string) []string { + var out []string + for _, n := range fileFS[dir] { + if strings.HasSuffix(n, "/") { + out = append(out, path.Join(dir, strings.TrimSuffix(n, "/"))) + } + } + return out + }, + Entries: func(dir string) []FileEntry { + var out []FileEntry + for _, n := range fileFS[dir] { + name := strings.TrimSuffix(n, "/") + out = append(out, FileEntry{Path: path.Join(dir, name), Name: name, Dir: name != n, Size: 2048}) + } + return out + }, + } +} + +type explorerProbe struct { + s FileExplorerState + opts FileExplorerOptions + nav int + opened []string +} + +func (p *explorerProbe) view(c *ui.Context) { + Use(c, Settings{}) + r := FileExplorer(c, &p.s, p.opts) + r.Element.Height(380) + if r.Navigated { + p.nav++ + } + if r.Opened != nil { + p.opened = append(p.opened, r.Opened.Path) + } +} + +func TestFileExplorerOpenFolderAndHistory(t *testing.T) { + p := &explorerProbe{opts: fileFSOptions()} + tt := ui.NewTester(p.view, 720, 400) + if p.s.Current != "/court" || !tt.HasText("seal.go") { + t.Fatalf("start %q", p.s.Current) + } + rows := 0 + for _, s := range tt.Texts() { + if s == "decrees" { + rows++ + } + } + if rows != 2 { + t.Fatalf("decrees shows %d times, want tree and list", rows) + } + // The list's decrees row is the one above seal.go. + seal, _ := tt.Find("seal.go") + tt.ClickAt(seal.X, seal.Y+seal.H/2-36) + tt.ClickAt(seal.X, seal.Y+seal.H/2-36) + if p.s.Current != "/court/decrees" || !tt.HasText("42.pdf") || p.nav != 1 { + t.Fatalf("open folder: current %q nav %d", p.s.Current, p.nav) + } + if err := tt.Click("Back"); err != nil { + t.Fatal(err) + } + if p.s.Current != "/court" { + t.Fatalf("back to %q", p.s.Current) + } + _ = tt.Click("Forward") + if p.s.Current != "/court/decrees" { + t.Fatalf("forward to %q", p.s.Current) + } + _ = tt.Click("Enclosing folder") + if p.s.Current != "/court" { + t.Fatalf("up to %q", p.s.Current) + } +} + +func TestFileExplorerTreeAndOpenFile(t *testing.T) { + p := &explorerProbe{opts: fileFSOptions()} + tt := ui.NewTester(p.view, 720, 400) + p.s.Navigate("/court/decrees/old") + tt.Frame() + if !tt.HasText("old") || !tt.HasText("7.pdf") { + t.Fatalf("tree did not open the ancestors: %q", tt.Texts()) + } + if sel, _ := p.s.tree.Selected(); sel != "/court/decrees/old" { + t.Fatalf("tree chose %q", sel) + } + r, _ := tt.Find("7.pdf") + tt.ClickAt(r.X, r.Y+r.H/2) + tt.ClickAt(r.X, r.Y+r.H/2) + if len(p.opened) != 1 || p.opened[0] != "/court/decrees/old/7.pdf" { + t.Fatalf("opened %v", p.opened) + } + if got := p.s.Selected(); len(got) != 1 { + t.Fatalf("selection %v", got) + } +} + +func TestFileExplorerShortcutsAndDisabled(t *testing.T) { + p := &explorerProbe{opts: fileFSOptions()} + tt := ui.NewTester(p.view, 720, 400) + p.s.Navigate("/court/chronicles") + tt.Frame() + tt.Key(0, ui.KeyTab) + back, up := ui.Modifiers(ui.Alt), ui.Modifiers(ui.Alt) + bk, uk := ui.KeyLeft, ui.KeyUp + if runtime.GOOS == "darwin" { + back, up, bk = ui.Super, ui.Super, ui.KeyBracketLeft + } + tt.Key(back, bk) + if p.s.Current != "/court" { + t.Fatalf("back shortcut gave %q", p.s.Current) + } + p.s.Navigate("/court/decrees/old") + tt.Frame() + tt.Key(up, uk) + if p.s.Current != "/court/decrees" { + t.Fatalf("up shortcut gave %q", p.s.Current) + } + p.opts.Disabled = true + tt.Frame() + _ = tt.Click("Back") + if p.s.Current != "/court/decrees" { + t.Fatal("disabled explorer moved") + } +} + +func TestFileExplorerSnapshot(t *testing.T) { + snapshot(t, "file_explorer", 640, 300, func(c *ui.Context) { + p := ui.Local(c.Root(), "snap", func() *explorerProbe { + p := &explorerProbe{opts: fileFSOptions()} + p.s.Navigate("/court/decrees") + return p + }) + ui.Column(c).Padding(12).Children(func() { + FileExplorer(c, &(*p).s, (*p).opts).Element.Height(276) + }) + }) +} + +func TestFileExplorerDisablingBeforeDeliveredKey(t *testing.T) { + p := &explorerProbe{opts: fileFSOptions()} + tt := ui.NewTester(p.view, 720, 400) + if err := tt.Click("seal.go"); err != nil { + t.Fatal(err) + } + before := p.s.Selected() + p.opts.Disabled = true + tt.Key(0, ui.KeyDown) + after := p.s.Selected() + if len(after) != len(before) || after[0] != before[0] { + t.Fatalf("disabled selection %v, want %v", after, before) + } +} + +func TestFileExplorerPrunesRemovedSelection(t *testing.T) { + entries := []FileEntry{{Path: "/court/a.txt", Name: "a.txt"}, {Path: "/court/b.txt", Name: "b.txt"}} + var s FileExplorerState + opts := FileExplorerOptions{Roots: []string{"/court"}, Folders: func(string) []string { return nil }, Entries: func(string) []FileEntry { return entries }} + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + FileExplorer(c, &s, opts).Element.Height(380) + }, 720, 400) + if err := tt.Click("b.txt"); err != nil { + t.Fatal(err) + } + entries = entries[:1] + tt.Frame() + if got := s.Selected(); len(got) != 0 { + t.Fatalf("removed selection %v", got) + } +} + +func TestFileExplorerLargeAndRevision(t *testing.T) { + entries := make([]FileEntry, 100000) + for i := range entries { + name := fmt.Sprintf("f-%d.txt", i) + entries[i] = FileEntry{Path: "/court/" + name, Name: name} + } + var s FileExplorerState + opts := FileExplorerOptions{Roots: []string{"/court"}, Folders: func(string) []string { return nil }, Entries: func(string) []FileEntry { return entries }} + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + FileExplorer(c, &s, opts).Element.Height(380) + }, 720, 400) + n := 0 + for _, text := range tt.Texts() { + if strings.HasPrefix(text, "f-") { + n++ + } + } + if n == 0 || n > 60 { + t.Fatalf("built %d file labels", n) + } + if err := tt.Click("f-0.txt"); err != nil { + t.Fatal(err) + } + entries[0].Path = "/court/replaced.txt" + opts.Revision++ + tt.Frame() + if got := s.Selected(); len(got) != 0 { + t.Fatalf("replaced selection %v", got) + } +} diff --git a/file_grid.go b/file_grid.go new file mode 100644 index 0000000..46217c8 --- /dev/null +++ b/file_grid.go @@ -0,0 +1,129 @@ +package mujica + +import ( + "log/slog" + + "github.com/egoist/mygo/ui" + + "mujicaui/theme" +) + +// FileGridOptions configure a FileGrid. +type FileGridOptions struct { + // Revision changes when entries are edited in place. + Revision uint64 + // Thumbnail builds a preview; nil or false shows FileIcon. + Thumbnail func(e FileEntry) bool + // ItemWidth defaults to 128 DIP, minimum 28. + ItemWidth float32 + Disabled bool +} + +// FileGridResult is a FileGrid and what happened in this frame. +type FileGridResult struct { + Element *ui.Element + // Changed reports a new selection; Opened is the entry opened. + Changed bool + Opened *FileEntry +} + +// FileGridState is a FileGrid's selection and place. +type FileGridState struct { + list ListState[string] + data *FileEntry + n int + revision uint64 + paths []string +} + +// Selected returns the chosen entries' paths. +func (s *FileGridState) Selected() []string { + var out []string + for p := range s.list.Selection.All() { + out = append(out, p) + } + return out +} + +// FileGrid virtualizes thumbnails, file selection and opening. +func FileGrid(c *ui.Context, entries []FileEntry, s *FileGridState, opts FileGridOptions) FileGridResult { + if s == nil { + panic("mujica: FileGrid needs a state") + } + if opts.ItemWidth < 0 { + panic("mujica: FileGrid ItemWidth is negative") + } + w := opts.ItemWidth + if w == 0 { + w = 128 + } + k := Tokens(c) + u := Density(c).Unit() + if w < theme.MinHitSize { + panic("mujica: FileGrid ItemWidth is below the minimum hit size") + } + thumb := max(0, w-4*u) + var res FileGridResult + res.Element = ui.Column(c).MinHeight(0).Disabled(opts.Disabled) + disabled := res.Element.IsDisabled() + if disabled { + copy := *s + copy.list.Selection = filesSelectionCopy(s.list.Selection) + s = © + } + var first *FileEntry + if len(entries) > 0 { + first = &entries[0] + } + if s.data != first || s.n != len(entries) || s.revision != opts.Revision { + valid := make(map[string]bool, len(entries)) + moved := len(s.paths) != len(entries) + for i, entry := range entries { + if entry.Path == "" || valid[entry.Path] { + panic("mujica: FileGrid needs unique entry paths") + } + valid[entry.Path] = true + if i >= len(s.paths) || s.paths[i] != entry.Path { + moved = true + } + } + for key := range s.list.Selection.All() { + if !valid[key] { + s.list.Selection.Remove(key) + } + } + if moved { + s.list.grid = ui.GridState{} + s.paths = make([]string, len(entries)) + for i, entry := range entries { + s.paths[i] = entry.Path + } + } + s.data, s.n, s.revision = first, len(entries), opts.Revision + } + res.Element.Children(func() { + v := List(c, &s.list, len(entries), ListOptions[string]{Multiple: true, Grid: true, ItemWidth: w, ItemHeight: thumb*0.75 + 56, + Key: func(i int) string { return entries[i].Path }, Label: func(i int) string { return entries[i].Name }, + Empty: msg(c, "file_grid.empty")}, func(i int) { + e := entries[i] + ui.Box(c).Height(thumb * 0.75).Shrink(0).Center().Clip().Radius(theme.ControlRadius).Background(k.Background).Children(func() { + if opts.Thumbnail == nil || !opts.Thumbnail(e) { + FileIcon(c, e.Name, FileIconOptions{Folder: e.Dir, Size: 40}) + } + }) + ui.Text(c, e.Name).SingleLine().TextAlign(ui.Center).FontSize(FontSize(c, theme.CaptionSize)) + }) + v.Element.Grow(1) + res.Changed = v.Changed() && !disabled + if key, ok := s.list.Selected(); ok && v.Submitted() && !res.Element.IsDisabled() { + for _, e := range entries { + if e.Path == key { + open := e + res.Opened = &open + slog.Debug("mujica: file_grid open", "dir", e.Dir) + } + } + } + }) + return res +} diff --git a/file_grid_test.go b/file_grid_test.go new file mode 100644 index 0000000..869ab03 --- /dev/null +++ b/file_grid_test.go @@ -0,0 +1,172 @@ +package mujica + +import ( + "fmt" + "slices" + "testing" + + "github.com/egoist/mygo/ui" +) + +func TestFileGridFreshSliceKeepsRangeAndScroll(t *testing.T) { + entries := fileGridEntries(6) + var s FileGridState + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + FileGrid(c, slices.Clone(entries), &s, FileGridOptions{}).Element.Height(320) + }, 600, 340) + if err := tt.Click("crest-3.png"); err != nil { + t.Fatal(err) + } + tt.Frame() + if err := tt.ClickWith(ui.Shift, "crest-5.png"); err != nil { + t.Fatal(err) + } + if got := s.Selected(); len(got) != 3 { + t.Fatalf("range selection %v", got) + } + entries = fileGridEntries(100000) + tt.Frame() + tt.Scroll(300, 200, 0, 10000) + tt.Frame() + if tt.HasText("sketches") { + t.Fatal("unchanged entries reset the scroll position") + } +} + +func fileGridEntries(n int) []FileEntry { + out := make([]FileEntry, n) + for i := range out { + name := fmt.Sprintf("crest-%d.png", i) + out[i] = FileEntry{Path: "/art/" + name, Name: name} + } + out[0] = FileEntry{Path: "/art/sketches", Name: "sketches", Dir: true} + return out +} + +type gridProbe struct { + entries []FileEntry + s FileGridState + disabled bool + thumbs int + opened []string +} + +func (p *gridProbe) view(c *ui.Context) { + Use(c, Settings{}) + r := FileGrid(c, p.entries, &p.s, FileGridOptions{Disabled: p.disabled, Thumbnail: func(e FileEntry) bool { + if FileKindOf(e.Name) != FileImage { + return false + } + p.thumbs++ + ui.Box(c).Fill().Background(Tokens(c).Info.Alpha(0.25)) + return true + }}) + r.Element.Height(320) + if r.Opened != nil { + p.opened = append(p.opened, r.Opened.Name) + } +} + +func TestFileGridMultiSelectAndOpen(t *testing.T) { + p := &gridProbe{entries: fileGridEntries(6)} + tt := ui.NewTester(p.view, 600, 340) + if err := tt.Click("crest-1.png"); err != nil { + t.Fatal(err) + } + if err := tt.ClickWith(ui.Cmd, "crest-3.png"); err != nil { + t.Fatal(err) + } + if got := p.s.Selected(); len(got) != 2 { + t.Fatalf("selection %v", got) + } + r, _ := tt.Find("sketches") + tt.ClickAt(r.X+r.W/2, r.Y+r.H/2) + tt.ClickAt(r.X+r.W/2, r.Y+r.H/2) + if len(p.opened) != 1 || p.opened[0] != "sketches" { + t.Fatalf("opened %v", p.opened) + } +} + +func TestFileGridThumbnailSlotAndVirtual(t *testing.T) { + p := &gridProbe{entries: fileGridEntries(100_000)} + tt := ui.NewTester(p.view, 600, 340) + p.thumbs = 0 + tt.Frame() + if p.thumbs == 0 || p.thumbs > 60 { + t.Fatalf("built %d thumbnails of 100000", p.thumbs) + } + if _, ok := tt.Find("Folder"); !ok { + t.Fatal("folder without thumbnail shows no icon") + } +} + +func TestFileGridKeysAndDisabled(t *testing.T) { + p := &gridProbe{entries: fileGridEntries(6)} + tt := ui.NewTester(p.view, 600, 340) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyDown) + tt.Key(0, ui.KeyEnter) + if len(p.opened) != 1 { + t.Fatalf("keyboard opened %v", p.opened) + } + p = &gridProbe{entries: fileGridEntries(6), disabled: true} + tt = ui.NewTester(p.view, 600, 340) + _ = tt.Click("crest-1.png") + if len(p.s.Selected()) != 0 { + t.Fatal("disabled grid selected") + } +} + +func TestFileGridSnapshot(t *testing.T) { + snapshot(t, "file_grid", 560, 340, func(c *ui.Context) { + p := ui.Local(c.Root(), "snap", func() *gridProbe { + p := &gridProbe{entries: append(fileGridEntries(3), FileEntry{Path: "/art/deed.pdf", Name: "deed.pdf"}, FileEntry{Path: "/art/x.zip", Name: "archive.zip"})} + p.s.list.Selection.Add("/art/crest-1.png") + return p + }) + ui.Column(c).Padding(12).Children(func() { FileGrid(c, (*p).entries, &(*p).s, FileGridOptions{}).Element.Height(316) }) + }) +} + +func TestFileGridRejectsSmallWidth(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("width below hit size was accepted") + } + }() + ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + FileGrid(c, fileGridEntries(3), &FileGridState{}, FileGridOptions{ItemWidth: 1}) + }, 400, 300) +} + +func TestFileGridDisablingBeforeDeliveredKey(t *testing.T) { + p := &gridProbe{entries: fileGridEntries(6)} + tt := ui.NewTester(p.view, 600, 340) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyDown) + before := p.s.Selected() + p.disabled = true + tt.Key(0, ui.KeyDown) + after := p.s.Selected() + if len(after) != len(before) || after[0] != before[0] { + t.Fatalf("disabled selection %v, want %v", after, before) + } +} + +func TestFileGridShrinkThenShiftSelect(t *testing.T) { + p := &gridProbe{entries: fileGridEntries(6)} + tt := ui.NewTester(p.view, 600, 340) + if err := tt.Click("crest-5.png"); err != nil { + t.Fatal(err) + } + p.entries = p.entries[:1] + tt.Frame() + if err := tt.ClickWith(ui.Shift, "sketches"); err != nil { + t.Fatal(err) + } + if got := p.s.Selected(); len(got) != 1 || got[0] != "/art/sketches" { + t.Fatalf("selection %v", got) + } +} diff --git a/file_icon.go b/file_icon.go new file mode 100644 index 0000000..97ddb73 --- /dev/null +++ b/file_icon.go @@ -0,0 +1,125 @@ +package mujica + +import ( + "path" + "strings" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/theme" +) + +// FileKind is the family a file belongs to. +type FileKind int + +const ( + FileOther FileKind = iota + FileFolder + FileImage + FileText + FileCode + FileDocument + FileArchive + FileAudio + FileVideo +) + +var fileKinds = map[string]FileKind{ + "png": FileImage, "jpg": FileImage, "jpeg": FileImage, "gif": FileImage, "svg": FileImage, "webp": FileImage, "bmp": FileImage, "heic": FileImage, + "txt": FileText, "md": FileText, "log": FileText, "csv": FileText, "rtf": FileText, + "go": FileCode, "js": FileCode, "ts": FileCode, "py": FileCode, "json": FileCode, "sh": FileCode, "sql": FileCode, "html": FileCode, "css": FileCode, "rs": FileCode, "c": FileCode, "h": FileCode, "java": FileCode, "yaml": FileCode, "yml": FileCode, "toml": FileCode, + "pdf": FileDocument, "doc": FileDocument, "docx": FileDocument, "xls": FileDocument, "xlsx": FileDocument, "ppt": FileDocument, "pptx": FileDocument, "pages": FileDocument, "key": FileDocument, + "zip": FileArchive, "tar": FileArchive, "gz": FileArchive, "tgz": FileArchive, "7z": FileArchive, "rar": FileArchive, "xz": FileArchive, + "mp3": FileAudio, "wav": FileAudio, "flac": FileAudio, "aac": FileAudio, "ogg": FileAudio, "m4a": FileAudio, + "mp4": FileVideo, "mov": FileVideo, "mkv": FileVideo, "avi": FileVideo, "webm": FileVideo, +} + +// FileKindOf classifies a filename by its extension. +func FileKindOf(name string) FileKind { + return fileKinds[fileExt(name)] +} + +// fileExt is the lower-case extension without the dot. +func fileExt(name string) string { + return strings.ToLower(strings.TrimPrefix(path.Ext(name), ".")) +} + +// FileIconOptions configure a FileIcon. +type FileIconOptions struct { + // Folder draws a folder; Open draws it opened. + Folder, Open bool + // Size is the icon's edge in DIPs; zero is 20. + Size float32 +} + +// fileFolderOpen is an opened folder glyph in Lucide's style. +var fileFolderOpen = ui.MustParseSVG([]byte(``)) + +// fileKindColor is a kind's tint, readable on Surface. +func fileKindColor(k theme.Tokens, kind FileKind) ui.Color { + switch kind { + case FileFolder: + return k.Warning + case FileImage, FileVideo: + return k.Info + case FileCode: + return k.AccentText + case FileArchive, FileAudio: + return k.Warning + case FileDocument: + return k.Success + } + return k.TextMuted +} + +// FileIcon shows file type or folder opening state. +func FileIcon(c *ui.Context, name string, opts FileIconOptions) *ui.Element { + if opts.Size < 0 { + panic("mujica: FileIcon Size is negative") + } + if opts.Open && !opts.Folder { + panic("mujica: FileIcon Open needs Folder") + } + k := Tokens(c) + size := opts.Size + if size == 0 { + size = 20 + } + kind := FileKindOf(name) + label := msg(c, "file_icon.file") + if ext := fileExt(name); ext != "" { + label = strings.ToUpper(ext) + " " + label + } + if opts.Folder { + kind, label = FileFolder, msg(c, "file_icon.folder") + if opts.Open { + label = msg(c, "file_icon.open") + } + } + col := fileKindColor(k, kind) + box := ui.Box(c).Size(size, size).Shrink(0).Role(ui.RoleImage).Label(label).TextColor(col) + return box.Children(func() { + switch { + case opts.Open: + ui.Icon(c, fileFolderOpen).Size(size, size) + case opts.Folder: + ui.Icon(c, icons.Must("folder")).Size(size, size) + case kind == FileImage: + ui.Icon(c, icons.Must("image")).Size(size, size) + case kind == FileText: + ui.Icon(c, icons.Must("file-text")).Size(size, size) + default: + ui.Icon(c, icons.Must("file")).Size(size, size) + ext := strings.ToUpper(fileExt(name)) + if ext != "" && size >= 20 { + // The extension tag sits across the page's lower half. + if len(ext) > 3 { + ext = ext[:3] + } + ui.Text(c, ext).Font(theme.MonoFont).FontSize(FontSize(c, size*0.3)).FontWeight(700).TextColor(k.Surface). + Background(col).Radius(1).Padding(0, 1).Absolute().Bottom(size * 0.12).Left(0).Role(ui.RoleNone) + } + } + }) +} diff --git a/file_icon_test.go b/file_icon_test.go new file mode 100644 index 0000000..5060fe3 --- /dev/null +++ b/file_icon_test.go @@ -0,0 +1,71 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" + + "mujicaui/theme" +) + +func TestFileKindOf(t *testing.T) { + for name, want := range map[string]FileKind{"a.PNG": FileImage, "main.go": FileCode, "notes.md": FileText, "x.tar.gz": FileArchive, + "song.flac": FileAudio, "film.mov": FileVideo, "deed.pdf": FileDocument, "README": FileOther, "blob.xyz": FileOther} { + if got := FileKindOf(name); got != want { + t.Errorf("%s: kind %d, want %d", name, got, want) + } + } +} + +func TestFileIconLabels(t *testing.T) { + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Row(c).Children(func() { + FileIcon(c, "main.go", FileIconOptions{}) + FileIcon(c, "src", FileIconOptions{Folder: true}) + FileIcon(c, "lib", FileIconOptions{Folder: true, Open: true, Size: 32}) + }) + }, 300, 100) + for _, s := range []string{"GO file", "Folder", "Open folder"} { + if _, ok := tt.Find(s); !ok { + t.Fatalf("no icon named %q", s) + } + } + if r, _ := tt.Find("Open folder"); r.W != 32 { + t.Fatalf("size %v", r.W) + } +} + +func TestFileIconColorsReadable(t *testing.T) { + for _, k := range []theme.Tokens{theme.Light(), theme.Dark()} { + for kind := FileOther; kind <= FileVideo; kind++ { + if r := theme.Contrast(fileKindColor(k, kind), k.Surface); r < 3 { + t.Errorf("dark=%v kind %d contrast %.2f", k.Dark, kind, r) + } + } + } +} + +func TestFileIconBadOptionsPanic(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("Open without Folder did not panic") + } + }() + ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + FileIcon(c, "a", FileIconOptions{Open: true}) + }, 100, 100) +} + +func TestFileIconSnapshot(t *testing.T) { + snapshot(t, "file_icon", 500, 70, func(c *ui.Context) { + ui.Row(c).Padding(16).Gap(14).Children(func() { + FileIcon(c, "src", FileIconOptions{Folder: true, Size: 32}) + FileIcon(c, "src", FileIconOptions{Folder: true, Open: true, Size: 32}) + for _, n := range []string{"seal.go", "notes.md", "crest.png", "deed.pdf", "ledger.zip", "hymn.mp3", "rite.mov", "blob"} { + FileIcon(c, n, FileIconOptions{Size: 32}) + } + }) + }) +} diff --git a/file_operation_progress.go b/file_operation_progress.go new file mode 100644 index 0000000..9f18ca2 --- /dev/null +++ b/file_operation_progress.go @@ -0,0 +1,125 @@ +package mujica + +import ( + "fmt" + "log/slog" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/theme" +) + +// FileOperationKind is what a file operation does. +type FileOperationKind int + +const ( + FileCopy FileOperationKind = iota + FileMove + FileDelete +) + +// FileOperation is the caller's running operation; counts are of items. +type FileOperation struct { + Kind FileOperationKind + Done, Total int + // BytesDone and BytesTotal show the amount when BytesTotal is set. + BytesDone, BytesTotal int64 + // Current is the item in progress. + Current string + // Conflict names an existing target awaiting a decision. + Conflict string +} + +// ConflictChoice answers a name conflict. +type ConflictChoice int + +const ( + ConflictNone ConflictChoice = iota + ConflictReplace + ConflictKeepBoth + ConflictSkip +) + +// FileOperationResult is the panel and the user's answer this frame. +type FileOperationResult struct { + Element *ui.Element + Canceled bool + // Resolved answers a conflict; ApplyAll extends that decision. + Resolved ConflictChoice + ApplyAll bool +} + +// FileOperationProgress displays progress, cancellation and conflict choices. +func FileOperationProgress(c *ui.Context, op FileOperation, opts FileOperationOptions) FileOperationResult { + if op.Total < 0 || op.Done < 0 || op.Done > op.Total || op.BytesTotal < 0 || op.BytesDone < 0 || op.BytesTotal > 0 && op.BytesDone > op.BytesTotal { + panic("mujica: FileOperation counts out of range") + } + if op.Kind < FileCopy || op.Kind > FileDelete { + panic("mujica: FileOperation kind out of range") + } + k := Tokens(c) + u := Density(c).Unit() + var res FileOperationResult + res.Element = codePanel(c).Padding(u * 4).Gap(u * 3).Disabled(opts.Disabled).Role(ui.RoleGroup) + all := ui.Local(res.Element, "file_operation_progress.all", func() bool { return false }) + disabled := res.Element.IsDisabled() + verb := msg(c, [...]string{"file_operation_progress.copying", "file_operation_progress.moving", "file_operation_progress.deleting"}[op.Kind]) + title := fmt.Sprintf(verb, op.Done, op.Total) + res.Element.Label(title) + frac := 0.0 + switch { + case op.BytesTotal > 0: + frac = float64(op.BytesDone) / float64(op.BytesTotal) + case op.Total > 0: + frac = float64(op.Done) / float64(op.Total) + } + res.Element.Children(func() { + Progress(c, frac, ProgressOptions{Label: title, ShowValue: true}) + ui.Row(c).Gap(u * 3).AlignItems(ui.Center).Children(func() { + ui.Column(c).Grow(1).MinWidth(0).Children(func() { + if op.Current != "" { + ui.Text(c, op.Current).FontSize(FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted).SingleLine() + } + if op.BytesTotal > 0 { + ui.Text(c, fmt.Sprintf(msg(c, "file_operation_progress.bytes"), processListBytes(uint64(op.BytesDone)), processListBytes(uint64(op.BytesTotal)))). + FontSize(FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted) + } + }) + if Button(c, msg(c, "file_operation_progress.cancel"), ButtonOptions{Variant: Secondary}).Clicked() && !disabled { + res.Canceled = true + } + }) + if op.Conflict == "" { + return + } + ui.Column(c).Gap(u*2).Padding(u*3).Radius(theme.ControlRadius).Border(theme.BorderWidth, k.Warning).Children(func() { + ui.Row(c).Gap(u * 2).AlignItems(ui.Center).TextColor(k.Warning).Children(func() { + ui.Icon(c, icons.Must("triangle-alert")).Size(16, 16) + ui.Text(c, fmt.Sprintf(msg(c, "file_operation_progress.conflict"), op.Conflict)).TextColor(k.Text).Role(ui.RoleStatus) + }) + Checkbox(c, all, msg(c, "file_operation_progress.all"), CheckboxOptions{Disabled: opts.Disabled}) + ui.Row(c).Gap(u * 2).Justify(ui.End).Children(func() { + for _, b := range []struct { + key string + v ButtonVariant + cho ConflictChoice + }{{"file_operation_progress.skip", Ghost, ConflictSkip}, {"file_operation_progress.keepBoth", Secondary, ConflictKeepBoth}, {"file_operation_progress.replace", Danger, ConflictReplace}} { + if Button(c, msg(c, b.key), ButtonOptions{Variant: b.v}).Clicked() && !disabled { + res.Resolved, res.ApplyAll = b.cho, *all + } + } + }) + }) + }) + if res.Canceled || res.Resolved != ConflictNone { + slog.Debug("mujica: file_operation_progress answer", "canceled", res.Canceled, "choice", res.Resolved, "all", res.ApplyAll) + *all = false + } + return res +} + +// FileOperationOptions configure a FileOperationProgress. +type FileOperationOptions struct { + Disabled bool +} diff --git a/file_operation_progress_test.go b/file_operation_progress_test.go new file mode 100644 index 0000000..018f78e --- /dev/null +++ b/file_operation_progress_test.go @@ -0,0 +1,100 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" +) + +type opProbe struct { + op FileOperation + disabled bool + canceled int + answers []ConflictChoice + all []bool +} + +func (p *opProbe) view(c *ui.Context) { + Use(c, Settings{}) + r := FileOperationProgress(c, p.op, FileOperationOptions{Disabled: p.disabled}) + r.Element.Width(480) + if r.Canceled { + p.canceled++ + } + if r.Resolved != ConflictNone { + p.answers = append(p.answers, r.Resolved) + p.all = append(p.all, r.ApplyAll) + } +} + +func TestFileOperationProgressShowsCounts(t *testing.T) { + p := &opProbe{op: FileOperation{Kind: FileMove, Done: 3, Total: 12, BytesDone: 12 << 20, BytesTotal: 48 << 20, Current: "decrees/42.pdf"}} + tt := ui.NewTester(p.view, 520, 300) + for _, s := range []string{"Moving 3 of 12 items", "25%", "12.0 MB of 48.0 MB", "decrees/42.pdf"} { + if !tt.HasText(s) { + t.Fatalf("missing %q in %q", s, tt.Texts()) + } + } + if err := tt.Click("Cancel"); err != nil { + t.Fatal(err) + } + if p.canceled != 1 { + t.Fatal("cancel not reported") + } +} + +func TestFileOperationProgressConflict(t *testing.T) { + p := &opProbe{op: FileOperation{Kind: FileCopy, Done: 1, Total: 4, Conflict: "seal.go"}} + tt := ui.NewTester(p.view, 520, 300) + if !tt.HasText("“seal.go” already exists here.") { + t.Fatalf("texts %q", tt.Texts()) + } + _ = tt.Click("Keep both") + _ = tt.Click("Do this for all conflicts") + _ = tt.Click("Replace") + if len(p.answers) != 2 || p.answers[0] != ConflictKeepBoth || p.all[0] || p.answers[1] != ConflictReplace || !p.all[1] { + t.Fatalf("answers %v all %v", p.answers, p.all) + } + // Keyboard: the answers are buttons in the Tab order. + for range 5 { + tt.Key(0, ui.KeyTab) + } + tt.Key(0, ui.KeyEnter) + if len(p.answers) != 3 { + t.Fatalf("keyboard answer missing: %v", p.answers) + } +} + +func TestFileOperationProgressDisabledAndInvalid(t *testing.T) { + p := &opProbe{op: FileOperation{Kind: FileDelete, Total: 2, Conflict: "a"}, disabled: true} + tt := ui.NewTester(p.view, 520, 300) + _ = tt.Click("Cancel") + _ = tt.Click("Skip") + if p.canceled != 0 || len(p.answers) != 0 { + t.Fatal("disabled panel answered") + } + defer func() { + if recover() == nil { + t.Fatal("Done above Total did not panic") + } + }() + ui.NewTester((&opProbe{op: FileOperation{Done: 5, Total: 2}}).view, 520, 300) +} + +func TestFileOperationProgressSnapshot(t *testing.T) { + snapshot(t, "file_operation_progress", 520, 300, func(c *ui.Context) { + ui.Column(c).Padding(12).Children(func() { + FileOperationProgress(c, FileOperation{Kind: FileCopy, Done: 4, Total: 10, BytesDone: 30 << 20, BytesTotal: 80 << 20, + Current: "chronicles/first.md", Conflict: "first.md"}, FileOperationOptions{}) + }) + }) +} + +func TestFileOperationProgressNegativeByteTotal(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("negative byte total was accepted") + } + }() + ui.NewTester((&opProbe{op: FileOperation{BytesTotal: -1}}).view, 520, 300) +} diff --git a/file_preview.go b/file_preview.go new file mode 100644 index 0000000..8ee8829 --- /dev/null +++ b/file_preview.go @@ -0,0 +1,114 @@ +package mujica + +import ( + "fmt" + "log/slog" + "strings" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/theme" +) + +// PreviewFile holds caller-provided preview content. +type PreviewFile struct { + Name string + Size int64 + // Image previews a picture; Text previews text or code. + Image ui.ImageSource + Text string + // Language highlights code; empty guesses it from the extension. + Language string +} + +// FilePreviewOptions configure a FilePreview. +type FilePreviewOptions struct { + // Status shows the caller's read in progress or failed. + Status DataStatus + Error string +} + +// FilePreviewResult exposes the panel and requested actions. +type FilePreviewResult struct { + Element *ui.Element + // Closed, Opened and Retried report requested actions. + Closed, Opened, Retried bool +} + +var filePreviewLanguages = map[string]string{"go": "go", "js": "javascript", "ts": "typescript", "py": "python", "json": "json", "sh": "shell", "sql": "sql"} + +type previewCodeState struct { + viewer CodeViewerState + text string + lines []string +} + +// FilePreview presents images, text, code or unsupported-type copy. +func FilePreview(c *ui.Context, f PreviewFile, opts FilePreviewOptions) FilePreviewResult { + if f.Name == "" { + panic("mujica: FilePreview needs a Name") + } + k := Tokens(c) + u := Density(c).Unit() + var res FilePreviewResult + res.Element = codePanel(c).Role(ui.RoleGroup).Label(fmt.Sprintf(msg(c, "file_preview.label"), f.Name)) + code := ui.Local(res.Element, "file_preview.code", func() previewCodeState { return previewCodeState{} }) + if res.Element.Shortcut(0, ui.KeyEscape) && !res.Element.IsDisabled() { + res.Closed = true + } + res.Element.Children(func() { + codeHeader(c, func() { + FileIcon(c, f.Name, FileIconOptions{}) + ui.Text(c, f.Name).FontWeight(600).SingleLine().MinWidth(0).Shrink(1) + if f.Size > 0 { + ui.Text(c, processListBytes(uint64(f.Size))).FontSize(FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted).Shrink(0) + } + ui.Spacer(c) + if Button(c, msg(c, "file_preview.open"), ButtonOptions{Variant: Ghost}).MinHeight(theme.MinHitSize).Clicked() { + res.Opened = true + } + if iconAction(c, icons.Must("x"), msg(c, "file_preview.close")).Clicked() { + res.Closed = true + } + }) + body := ui.Column(c).Grow(1).MinHeight(u * 40).Padding(u * 3) + body.Children(func() { + if opts.Status != DataReady { + res.Retried = dataPlaceholder(c, opts.Status, opts.Error, "", "") + return + } + lang := f.Language + if lang == "" { + lang = filePreviewLanguages[fileExt(f.Name)] + } + switch { + case f.Image != nil: + ui.Box(c).Grow(1).Center().Clip().Children(func() { + Image(c, f.Image, ImageOptions{Alt: f.Name}).Element.MaxWidthPercent(100).MaxHeightPercent(100) + }) + case lang != "" || FileKindOf(f.Name) == FileCode: + if code.lines == nil || code.text != f.Text { + code.text, code.lines = f.Text, strings.Split(f.Text, "\n") + } + if lang == "" { + lang = fileExt(f.Name) + } + CodeViewer(c, code.lines, &code.viewer, CodeViewerOptions{Language: lang, Label: f.Name}).Element.Grow(1) + case f.Text != "" || FileKindOf(f.Name) == FileText: + ScrollArea(c, nil, ScrollAreaOptions{Label: f.Name}, func() { + ui.Text(c, f.Text).Font(theme.MonoFont).FontSize(FontSize(c, codeSize)).Selectable() + }).Grow(1) + default: + ui.Column(c).Grow(1).Center().Gap(u * 2).Children(func() { + FileIcon(c, f.Name, FileIconOptions{Size: 48}) + ui.Text(c, fmt.Sprintf(msg(c, "file_preview.unsupported"), strings.ToUpper(fileExt(f.Name)))).TextColor(k.TextMuted).Role(ui.RoleStatus) + }) + } + }) + }) + if res.Closed || res.Opened { + slog.Debug("mujica: file_preview action", "closed", res.Closed, "opened", res.Opened) + } + return res +} diff --git a/file_preview_test.go b/file_preview_test.go new file mode 100644 index 0000000..347ff9d --- /dev/null +++ b/file_preview_test.go @@ -0,0 +1,127 @@ +package mujica + +import ( + "strings" + "testing" + + "github.com/egoist/mygo/ui" +) + +type previewProbe struct { + f PreviewFile + opts FilePreviewOptions + closed, opened, retried int +} + +func (p *previewProbe) view(c *ui.Context) { + Use(c, Settings{}) + r := FilePreview(c, p.f, p.opts) + r.Element.Size(480, 300) + for _, e := range []struct { + on bool + n *int + }{{r.Closed, &p.closed}, {r.Opened, &p.opened}, {r.Retried, &p.retried}} { + if e.on { + *e.n++ + } + } +} + +func TestFilePreviewKinds(t *testing.T) { + cases := []struct { + f PreviewFile + want string + }{ + {PreviewFile{Name: "seal.go", Text: "package court\nfunc Seal() {}"}, "func Seal() {}"}, + {PreviewFile{Name: "notes.md", Text: "# The court"}, "# The court"}, + {PreviewFile{Name: "deed.pdf", Size: 4096}, "No preview for PDF files"}, + {PreviewFile{Name: "crest.svg", Image: ui.MustParseSVG([]byte(``))}, "crest.svg"}, + } + for _, tc := range cases { + tt := ui.NewTester((&previewProbe{f: tc.f}).view, 500, 320) + if !tt.HasText(tc.want) { + t.Fatalf("%s: texts %q", tc.f.Name, tt.Texts()) + } + } + tt := ui.NewTester((&previewProbe{f: PreviewFile{Name: "deed.pdf", Size: 4096}}).view, 500, 320) + if !tt.HasText("4 KB") { + t.Fatal("size missing") + } +} + +func TestFilePreviewActions(t *testing.T) { + p := &previewProbe{f: PreviewFile{Name: "notes.md", Text: "text"}} + tt := ui.NewTester(p.view, 500, 320) + _ = tt.Click("Open") + _ = tt.Click("Close preview") + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyEscape) + if p.opened != 1 || p.closed != 2 { + t.Fatalf("opened %d closed %d", p.opened, p.closed) + } +} + +func TestFilePreviewStatusAndNoName(t *testing.T) { + p := &previewProbe{f: PreviewFile{Name: "big.log"}, opts: FilePreviewOptions{Status: DataFailed, Error: "read denied"}} + tt := ui.NewTester(p.view, 500, 320) + if !tt.HasText("read denied") { + t.Fatalf("texts %q", tt.Texts()) + } + _ = tt.Click("Retry") + if p.retried != 1 { + t.Fatal("retry not reported") + } + defer func() { + if recover() == nil { + t.Fatal("missing name did not panic") + } + }() + ui.NewTester((&previewProbe{}).view, 500, 320) +} + +func TestFilePreviewSnapshot(t *testing.T) { + snapshot(t, "file_preview", 500, 340, func(c *ui.Context) { + ui.Column(c).Padding(12).Children(func() { + FilePreview(c, PreviewFile{Name: "seal.go", Size: 912, Text: "package court\n\n// Seal stamps a decree.\nfunc Seal(d string) int {\n\treturn len(d)\n}"}, FilePreviewOptions{}).Element.Height(316) + }) + }) +} + +func TestFilePreviewEmptyCode(t *testing.T) { + for _, f := range []PreviewFile{{Name: "empty.go"}, {Name: "empty.html"}, {Name: "empty.custom", Language: "go"}} { + tt := ui.NewTester((&previewProbe{f: f}).view, 500, 320) + if tt.HasText("No preview for GO files") || tt.HasText("No preview for HTML files") || tt.HasText("No preview for CUSTOM files") { + t.Fatalf("empty code shows unsupported type: %s", f.Name) + } + } +} + +func TestFilePreviewLargeCodeCache(t *testing.T) { + file := PreviewFile{Name: "large.go", Text: strings.Repeat("var seal = 1\n", 100000)} + var state *previewCodeState + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + r := FilePreview(c, file, FilePreviewOptions{}) + r.Element.Size(480, 300) + state = ui.Local(r.Element, "file_preview.code", func() previewCodeState { return previewCodeState{} }) + }, 500, 320) + before := state.viewer.data + tt.Frame() + if before == nil || state.viewer.data != before || len(state.lines) != 100001 { + t.Fatal("unchanged preview rebuilt code cache") + } + n := 0 + for _, text := range tt.Texts() { + if text == "var seal = 1" { + n++ + } + } + if n == 0 || n > 60 { + t.Fatalf("built %d code lines", n) + } + file.Text = "package court" + tt.Frame() + if len(state.lines) != 1 || state.lines[0] != "package court" || state.viewer.data == before { + t.Fatal("changed preview retained code cache") + } +} diff --git a/live_indicator.go b/live_indicator.go new file mode 100644 index 0000000..03607f8 --- /dev/null +++ b/live_indicator.go @@ -0,0 +1,96 @@ +package mujica + +import ( + "fmt" + "log/slog" + "time" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/theme" +) + +// LiveStatus is the state of real-time sync. +type LiveStatus int + +const ( + LiveSynced LiveStatus = iota + LiveSyncing + LiveReconnecting + LiveOffline + LiveError +) + +// LiveIndicatorOptions configure a LiveIndicator. +type LiveIndicatorOptions struct { + // LastSynced dates the last good sync; zero hides it. + LastSynced time.Time + // Now measures LastSynced; zero uses the frame's time. + Now time.Time + // Error explains LiveError. + Error string +} + +// LiveIndicator displays sync status, announces changes and offers retries. +func LiveIndicator(c *ui.Context, status LiveStatus, opts LiveIndicatorOptions) ChoiceResult { + if status < LiveSynced || status > LiveError { + panic("mujica: LiveStatus out of range") + } + k := Tokens(c) + u := Density(c).Unit() + now := opts.Now + if now.IsZero() { + now = c.Now() + } + text := msg(c, [...]string{"live_indicator.synced", "live_indicator.syncing", "live_indicator.reconnecting", "live_indicator.offline", "live_indicator.error"}[status]) + if status == LiveError && opts.Error != "" { + text += ": " + opts.Error + } + var res ChoiceResult + res.Element = ui.Row(c).Gap(u * 2).AlignItems(ui.Center).MinHeight(theme.MinHitSize) + last := ui.Local(res.Element, "live_indicator.last", func() LiveStatus { return status }) + if *last != status { + *last = status + c.Announce(text) + slog.Debug("mujica: live_indicator status", "status", status) + } + res.Element.Children(func() { + box := ui.Row(c).Gap(u*2).AlignItems(ui.Center).Padding(u, u*3).Radius(theme.MinHitSize / 2).Role(ui.RoleStatus).Label(text) + col := [...]ui.Color{k.Success, k.TextMuted, k.Warning, k.Warning, k.Danger}[status] + box.Border(theme.BorderWidth, col.Mix(k.Surface, 0.5)).Background(k.Surface) + box.Children(func() { + switch status { + case LiveSyncing, LiveReconnecting: + Spinner(c, SpinnerOptions{Size: 14, Label: text}) + default: + name := [...]string{"circle-check", "", "", "wifi-off", "circle-alert"}[status] + ui.Icon(c, icons.Must(name)).Size(16, 16).Shrink(0).TextColor(col) + } + ui.Text(c, text).FontSize(FontSize(c, theme.CaptionSize)).SingleLine() + if !opts.LastSynced.IsZero() && status != LiveSyncing { + ui.Text(c, liveAgo(c, now.Sub(opts.LastSynced))).FontSize(FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted).SingleLine() + } + }) + if status == LiveOffline || status == LiveError { + if Button(c, msg(c, "live_indicator.retry"), ButtonOptions{Variant: Ghost}).MinHeight(theme.MinHitSize).Clicked() && !res.Element.IsDisabled() { + res.changed = true + slog.Debug("mujica: live_indicator retry") + } + } + }) + return res +} + +// liveAgo words how long ago the last sync was. +func liveAgo(c *ui.Context, d time.Duration) string { + switch { + case d < time.Minute: + return msg(c, "live_indicator.justNow") + case d < time.Hour: + return fmt.Sprintf(msg(c, "live_indicator.minutes"), int(d.Minutes())) + case d < 24*time.Hour: + return fmt.Sprintf(msg(c, "live_indicator.hours"), int(d.Hours())) + } + return fmt.Sprintf(msg(c, "live_indicator.days"), int(d.Hours()/24)) +} diff --git a/live_indicator_test.go b/live_indicator_test.go new file mode 100644 index 0000000..bffb04e --- /dev/null +++ b/live_indicator_test.go @@ -0,0 +1,89 @@ +package mujica + +import ( + "testing" + "time" + + "github.com/egoist/mygo/ui" +) + +var liveNow = time.Date(2026, 10, 5, 12, 0, 0, 0, time.UTC) + +type liveProbe struct { + status LiveStatus + err string + retried int +} + +func (p *liveProbe) view(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Padding(12).Children(func() { + if LiveIndicator(c, p.status, LiveIndicatorOptions{LastSynced: liveNow.Add(-3 * time.Minute), Now: liveNow, Error: p.err}).Changed() { + p.retried++ + } + }) +} + +func TestLiveIndicatorStates(t *testing.T) { + p := &liveProbe{} + tt := ui.NewTester(p.view, 500, 120) + if !tt.HasText("All changes saved") || !tt.HasText("3 min ago") || tt.HasText("Retry") { + t.Fatalf("synced: %q", tt.Texts()) + } + p.status = LiveOffline + tt.Frame() + if !tt.HasText("Offline, changes kept on this device") { + t.Fatalf("offline: %q", tt.Texts()) + } + ann := tt.Announcements() + if len(ann) == 0 || ann[len(ann)-1] != "Offline, changes kept on this device" { + t.Fatalf("not announced: %q", ann) + } + if err := tt.Click("Retry"); err != nil { + t.Fatal(err) + } + p.status, p.err = LiveError, "server refused" + tt.Frame() + if !tt.HasText("Sync failed: server refused") { + t.Fatalf("error: %q", tt.Texts()) + } + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyEnter) + if p.retried != 2 { + t.Fatalf("retried %d", p.retried) + } + p.status = LiveSyncing + tt.Frame() + if !tt.HasText("Saving…") || tt.HasText("Retry") { + t.Fatalf("syncing: %q", tt.Texts()) + } +} + +func TestLiveAgo(t *testing.T) { + var got []string + ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + got = []string{liveAgo(c, 10*time.Second), liveAgo(c, 5*time.Hour), liveAgo(c, 50*time.Hour)} + }, 10, 10) + if got[0] != "just now" || got[1] != "5 h ago" || got[2] != "2 days ago" { + t.Fatalf("ago %q", got) + } +} + +func TestLiveIndicatorInvalid(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("bad status did not panic") + } + }() + ui.NewTester((&liveProbe{status: 9}).view, 400, 100) +} + +func TestLiveIndicatorSnapshot(t *testing.T) { + snapshot(t, "live_indicator", 520, 150, func(c *ui.Context) { + ui.Column(c).Gap(4).Children(func() { + LiveIndicator(c, LiveSynced, LiveIndicatorOptions{LastSynced: liveNow.Add(-3 * time.Minute), Now: liveNow}) + LiveIndicator(c, LiveOffline, LiveIndicatorOptions{}) + }) + }) +} diff --git a/messages_files.go b/messages_files.go new file mode 100644 index 0000000..936f37a --- /dev/null +++ b/messages_files.go @@ -0,0 +1,98 @@ +package mujica + +func init() { + addMessages(map[string][2]string{ + "file_icon.file": {"file", "文件"}, + "file_icon.folder": {"Folder", "文件夹"}, + "file_icon.open": {"Open folder", "打开的文件夹"}, + "path_bar.label": {"Path", "路径"}, + "path_bar.more": {"Hidden folders", "折叠的文件夹"}, + "path_bar.current": {"Current folder", "当前文件夹"}, + "rename_inline.label": {"Name", "名称"}, + "rename_inline.empty": {"A name is required", "名称不能为空"}, + "file_explorer.back": {"Back", "后退"}, + "file_explorer.forward": {"Forward", "前进"}, + "file_explorer.up": {"Enclosing folder", "上级文件夹"}, + "file_explorer.empty": {"This folder is empty", "此文件夹为空"}, + "file_grid.empty": {"This folder is empty", "此文件夹为空"}, + "file_preview.label": {"Preview of %s", "%s 的预览"}, + "file_preview.open": {"Open", "打开"}, + "file_preview.close": {"Close preview", "关闭预览"}, + "file_preview.unsupported": {"No preview for %s files", "无法预览 %s 文件"}, + "file_operation_progress.copying": {"Copying %d of %d items", "正在复制 %d / %d 项"}, + "file_operation_progress.moving": {"Moving %d of %d items", "正在移动 %d / %d 项"}, + "file_operation_progress.deleting": {"Deleting %d of %d items", "正在删除 %d / %d 项"}, + "file_operation_progress.cancel": {"Cancel", "取消"}, + "file_operation_progress.bytes": {"%s of %s", "%s / %s"}, + "file_operation_progress.conflict": {"“%s” already exists here.", "此处已存在“%s”。"}, + "file_operation_progress.all": {"Do this for all conflicts", "对所有冲突执行此操作"}, + "file_operation_progress.skip": {"Skip", "跳过"}, + "file_operation_progress.keepBoth": {"Keep both", "保留两者"}, + "file_operation_progress.replace": {"Replace", "替换"}, + "transfer_queue.summary": {"%d in progress, %d failed", "%d 项进行中,%d 项失败"}, + "transfer_queue.empty": {"No transfers", "没有传输"}, + "transfer_queue.upload": {"Upload", "上传"}, + "transfer_queue.download": {"Download", "下载"}, + "transfer_queue.queued": {"Waiting", "等待中"}, + "transfer_queue.paused": {"Paused at %.0f%%", "已暂停于 %.0f%%"}, + "transfer_queue.failed": {"Failed: %s", "失败:%s"}, + "transfer_queue.done": {"Done", "已完成"}, + "transfer_queue.pause": {"Pause %s", "暂停 %s"}, + "transfer_queue.resume": {"Resume %s", "继续 %s"}, + "transfer_queue.retry": {"Retry %s", "重试 %s"}, + "transfer_queue.remove": {"Remove %s", "移除 %s"}, + "storage_usage.used": {"%s of %s used", "已用 %s,共 %s"}, + "storage_usage.free": {"Free: %s (%.1f%%)", "可用:%s(%.1f%%)"}, + "storage_usage.freeLabel": {"Free", "可用"}, + "recent_files.title": {"Recent", "最近使用"}, + "recent_files.clear": {"Clear", "清除"}, + "recent_files.empty": {"No recent files", "没有最近使用的文件"}, + "recent_files.remove": {"Remove %s from recent", "从最近使用中移除 %s"}, + "recent_files.today": {"Today", "今天"}, + "recent_files.yesterday": {"Yesterday", "昨天"}, + "recent_files.week": {"This week", "本周"}, + "recent_files.earlier": {"Earlier", "更早"}, + "archive_viewer.summary": {"%d files, %s unpacked, %s packed (%s smaller)", "%d 个文件,解压后 %s,压缩后 %s(减小 %s)"}, + "archive_viewer.empty": {"The archive is empty", "压缩包为空"}, + "archive_viewer.name": {"Name", "名称"}, + "archive_viewer.size": {"Size", "大小"}, + "archive_viewer.packed": {"Packed", "压缩后"}, + "archive_viewer.ratio": {"Saved", "压缩率"}, + "presence_avatars.label": {"%d people here", "%d 人在线"}, + "presence_avatars.more": {"%d more people", "另有 %d 人"}, + "presence_avatars.others": {"Others here", "其他在线者"}, + "presence_avatars.follow": {"Follow %s", "跟随 %s"}, + "presence_avatars.unfollow": {"Stop following %s", "停止跟随 %s"}, + "presence_avatars.away": {"Away", "离开"}, + "permission_select.view": {"Can view", "可查看"}, + "permission_select.comment": {"Can comment", "可评论"}, + "permission_select.edit": {"Can edit", "可编辑"}, + "permission_select.label": {"Permission", "权限"}, + "share_dialog.title": {"Share “%s”", "分享“%s”"}, + "share_dialog.placeholder": {"Add people by email", "通过邮箱添加成员"}, + "share_dialog.invite": {"Email to invite", "要邀请的邮箱"}, + "share_dialog.invitePermission": {"Permission for the invite", "邀请的权限"}, + "share_dialog.send": {"Invite", "邀请"}, + "share_dialog.people": {"People with access", "有权访问的成员"}, + "share_dialog.owner": {"Owner", "所有者"}, + "share_dialog.remove": {"Remove %s", "移除 %s"}, + "share_dialog.copy": {"Copy link", "复制链接"}, + "share_dialog.copied": {"Link copied", "链接已复制"}, + "reactions.label": {"Reactions", "表情回应"}, + "reactions.add": {"Add reaction", "添加回应"}, + "reactions.pick": {"Choose a reaction", "选择回应"}, + "reactions.count": {"%s %d", "%s %d"}, + "reactions.mine": {"including you", "包括你"}, + "live_indicator.synced": {"All changes saved", "所有更改已保存"}, + "live_indicator.syncing": {"Saving…", "正在保存…"}, + "live_indicator.reconnecting": {"Reconnecting…", "正在重新连接…"}, + "live_indicator.offline": {"Offline, changes kept on this device", "已离线,更改保存在本机"}, + "live_indicator.error": {"Sync failed", "同步失败"}, + "live_indicator.retry": {"Retry", "重试"}, + "live_indicator.justNow": {"just now", "刚刚"}, + "live_indicator.minutes": {"%d min ago", "%d 分钟前"}, + "live_indicator.hours": {"%d h ago", "%d 小时前"}, + "live_indicator.days": {"%d days ago", "%d 天前"}, + "remote_cursor.label": {"Also editing: %s", "同时编辑:%s"}, + }) +} diff --git a/path_bar.go b/path_bar.go new file mode 100644 index 0000000..8b5f6f7 --- /dev/null +++ b/path_bar.go @@ -0,0 +1,157 @@ +package mujica + +import ( + "log/slog" + "strings" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/internal" + "mujicaui/theme" +) + +// PathBarOptions configure a PathBar. +type PathBarOptions struct { + // Separator splits the path; empty is "/". + Separator string + // RootLabel defaults to the absolute path separator. + RootLabel string + Disabled bool +} + +// PathBarResult exposes the toolbar and requested path. +type PathBarResult struct { + Element *ui.Element + // Navigated is the chosen ancestor path, or empty. + Navigated string +} + +// pathSegment is a crumb and the path up to it. +type pathSegment struct{ label, path string } + +// pathSegments splits p into crumbs with cumulative paths. +func pathSegments(p, sep, root string) []pathSegment { + var out []pathSegment + acc := "" + if strings.HasPrefix(p, sep) { + label := root + if label == "" { + label = sep + } + out = append(out, pathSegment{label, sep}) + acc = sep + } + for _, part := range strings.Split(strings.Trim(p, sep), sep) { + if part == "" { + continue + } + if acc != "" && !strings.HasSuffix(acc, sep) { + acc += sep + } + acc += part + out = append(out, pathSegment{part, acc}) + } + return out +} + +// PathBar displays clickable segments and collapses overflowing middle segments. +func PathBar(c *ui.Context, p string, opts PathBarOptions) PathBarResult { + sep := opts.Separator + if sep == "" { + sep = "/" + } + segs := pathSegments(p, sep, opts.RootLabel) + if len(segs) == 0 { + panic("mujica: PathBar needs a path") + } + k := Tokens(c) + u := Density(c).Unit() + var res PathBarResult + e := ui.Row(c).Grow(1).MinWidth(0).Gap(u).AlignItems(ui.Center).Role(ui.RoleToolbar).Label(msg(c, "path_bar.label")). + FocusGroup(ui.Horizontal).Disabled(opts.Disabled) + res.Element = e + was := e.Bounds() + internal.Remeasure(c, e, was.W, was.H) + hidden := pathBarFold(c, segs, was.W) + disabled := e.IsDisabled() + open := ui.Local(e, "path_bar.open", func() bool { return false }) + *open = *open && hidden > 0 && !disabled + goTo := func(s pathSegment) { + if !disabled { + res.Navigated, *open = s.path, false + slog.Debug("mujica: path_bar navigate", "depth", strings.Count(s.path, sep)) + } + } + e.Children(func() { + for i, s := range segs { + if i > 0 && (i <= 1 || i > hidden) { + ui.Icon(c, icons.Must("chevron-right")).Size(12, 12).Shrink(0).TextColor(k.TextMuted) + } + if i >= 1 && i <= hidden { + if i == 1 { + more := pathBarCrumb(c, "…", false).Label(msg(c, "path_bar.more")) + if more.Clicked() && !disabled { + *open = !*open + } + Popover(c, more, open, PopoverOptions{Label: msg(c, "path_bar.more")}, func() { + for _, h := range segs[1 : hidden+1] { + if pathBarCrumb(c, h.label, false).Key(h.path).Clicked() { + goTo(h) + } + } + }) + } + continue + } + if pathBarCrumb(c, s.label, i == len(segs)-1).Key(s.path).Clicked() { + goTo(s) + } + } + }) + return res +} + +// pathBarFold counts middle segments to collapse into the available width. +func pathBarFold(c *ui.Context, segs []pathSegment, room float32) int { + if room <= 0 { + return 0 + } + u := Density(c).Unit() + width := func(s string) float32 { + w, _ := c.MeasureText(0, ui.Span{Text: s, Size: FontSize(c, theme.BodySize)}) + return max(w+4*u, theme.MinHitSize) + 12 + 2*u + } + total := float32(0) + for _, s := range segs { + total += width(s.label) + } + hidden := 0 + for total > room && hidden < len(segs)-2 { + hidden++ + total -= width(segs[hidden].label) + if hidden == 1 { + total += width("…") + } + } + return hidden +} + +// pathBarCrumb builds a button, emphasizing the current segment. +func pathBarCrumb(c *ui.Context, label string, current bool) *ui.Element { + k := Tokens(c) + d := Density(c) + b := ui.ButtonBase(c).MinHeight(theme.MinHitSize).MinWidth(theme.MinHitSize).Padding(0, d.Unit()*2). + Radius(theme.ControlRadius).Shrink(0).TextColor(k.TextMuted) + internal.Ring(b, k.Focus, theme.ControlRadius) + switch { + case b.Pressed(): + b.Background(k.Selection).TextColor(k.Text) + case b.Hovered(): + b.Background(k.SurfaceHover).TextColor(k.Text) + } + if current { + b.TextColor(k.Text).FontWeight(600).Description(msg(c, "path_bar.current")) + } + return b.Children(func() { ui.Text(c, label).SingleLine() }) +} diff --git a/path_bar_test.go b/path_bar_test.go new file mode 100644 index 0000000..3b5dc37 --- /dev/null +++ b/path_bar_test.go @@ -0,0 +1,99 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" +) + +func pathBarTester(p *string, opts PathBarOptions, w int, went *[]string) *ui.Tester { + return ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Row(c).Width(float32(w)).Children(func() { + if r := PathBar(c, *p, opts); r.Navigated != "" { + *went = append(*went, r.Navigated) + *p = r.Navigated + } + }) + }, w+20, 300) +} + +func TestPathSegments(t *testing.T) { + segs := pathSegments("/Users/ada/court", "/", "Disk") + want := []pathSegment{{"Disk", "/"}, {"Users", "/Users"}, {"ada", "/Users/ada"}, {"court", "/Users/ada/court"}} + if len(segs) != 4 || segs[0] != want[0] || segs[3] != want[3] { + t.Fatalf("segments %v", segs) + } + if s := pathSegments(`C:\Court\Seals`, `\`, ""); len(s) != 3 || s[2].path != `C:\Court\Seals` { + t.Fatalf("windows segments %v", s) + } +} + +func TestPathBarClickNavigates(t *testing.T) { + p := "/Users/ada/court/seals" + var went []string + tt := pathBarTester(&p, PathBarOptions{}, 600, &went) + if err := tt.Click("ada"); err != nil { + t.Fatal(err) + } + if len(went) != 1 || went[0] != "/Users/ada" || tt.HasText("seals") { + t.Fatalf("went %v", went) + } + if err := tt.Click("/"); err != nil { + t.Fatal(err) + } + if went[1] != "/" { + t.Fatalf("root went %v", went) + } +} + +func TestPathBarFoldsMiddle(t *testing.T) { + p := "/Users/ada/chronicles/of/the/royal/court/seals" + var went []string + tt := pathBarTester(&p, PathBarOptions{RootLabel: "Disk"}, 300, &went) + if !tt.HasText("Disk") || !tt.HasText("seals") || tt.HasText("chronicles") { + t.Fatalf("narrow bar shows %q", tt.Texts()) + } + if err := tt.Click("Hidden folders"); err != nil { + t.Fatal(err) + } + if err := tt.Click("chronicles"); err != nil { + t.Fatal(err) + } + if len(went) != 1 || went[0] != "/Users/ada/chronicles" { + t.Fatalf("went %v", went) + } + if !tt.HasText("chronicles") { + t.Fatal("shorter path still folded") + } +} + +func TestPathBarKeyboardAndDisabled(t *testing.T) { + p := "/a/b/c" + var went []string + tt := pathBarTester(&p, PathBarOptions{}, 600, &went) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyRight) + tt.Key(0, ui.KeyEnter) + if len(went) != 1 || went[0] != "/a" { + t.Fatalf("keyboard went %v", went) + } + p = "/a/b/c" + went = nil + tt = pathBarTester(&p, PathBarOptions{Disabled: true}, 600, &went) + _ = tt.Click("a") + if len(went) != 0 { + t.Fatal("disabled bar navigated") + } +} + +func TestPathBarSnapshot(t *testing.T) { + snapshot(t, "path_bar", 460, 110, func(c *ui.Context) { + ui.Column(c).Padding(12).Gap(12).Children(func() { + PathBar(c, "/Users/ada/court/seals", PathBarOptions{RootLabel: "Disk"}) + ui.Row(c).Width(300).Children(func() { + PathBar(c, "/Users/ada/chronicles/of/the/royal/court/seals", PathBarOptions{RootLabel: "Disk"}) + }) + }) + }) +} diff --git a/permission_select.go b/permission_select.go new file mode 100644 index 0000000..023b872 --- /dev/null +++ b/permission_select.go @@ -0,0 +1,52 @@ +package mujica + +import ( + "log/slog" + + "github.com/egoist/mygo/ui" +) + +// Permission defines access to a shared item. +type Permission int + +const ( + PermissionView Permission = iota + PermissionComment + PermissionEdit +) + +// PermissionSelectOptions configure a PermissionSelect. +type PermissionSelectOptions struct { + // Label names the select, as the person it is for. + Label string + Disabled bool +} + +// permissionName is a permission's built-in name. +func permissionName(c *ui.Context, p Permission) string { + if p < PermissionView || p > PermissionEdit { + panic("mujica: Permission out of range") + } + return msg(c, [...]string{"permission_select.view", "permission_select.comment", "permission_select.edit"}[p]) +} + +// PermissionSelect chooses can view, can comment or can edit. +func PermissionSelect(c *ui.Context, value *Permission, opts PermissionSelectOptions) ChoiceResult { + if value == nil { + panic("mujica: PermissionSelect needs a value") + } + permissionName(c, *value) + options := make([]SelectOption[Permission], 3) + for p := PermissionView; p <= PermissionEdit; p++ { + options[p] = SelectOption[Permission]{Value: p, Label: permissionName(c, p)} + } + label := opts.Label + if label == "" { + label = msg(c, "permission_select.label") + } + r := Select(c, value, options, SelectOptions{Label: label, Disabled: opts.Disabled}) + if r.Changed() { + slog.Debug("mujica: permission_select change", "permission", *value) + } + return r +} diff --git a/permission_select_test.go b/permission_select_test.go new file mode 100644 index 0000000..0e1c147 --- /dev/null +++ b/permission_select_test.go @@ -0,0 +1,92 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" +) + +func permissionSelectTester(v *Permission, disabled bool, changed *int) *ui.Tester { + return ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Padding(12).Children(func() { + if PermissionSelect(c, v, PermissionSelectOptions{Label: "Ada", Disabled: disabled}).Changed() { + *changed++ + } + }) + }, 360, 260) +} + +func TestPermissionSelectChoose(t *testing.T) { + v, n := PermissionView, 0 + tt := permissionSelectTester(&v, false, &n) + if err := tt.Click("Can view"); err != nil { + t.Fatal(err) + } + if err := tt.Click("Can edit"); err != nil { + t.Fatal(err) + } + if v != PermissionEdit || n != 1 { + t.Fatalf("value %d changed %d", v, n) + } +} + +func TestPermissionSelectKeyboard(t *testing.T) { + v, n := PermissionEdit, 0 + tt := permissionSelectTester(&v, false, &n) + tt.Key(0, ui.KeyTab) + if !tt.Focused("Ada") { + t.Fatal("Tab did not reach the select") + } + tt.Key(0, ui.KeyEnter) + tt.Key(0, ui.KeyUp) + tt.Key(0, ui.KeyEnter) + if v != PermissionComment { + t.Fatalf("keys chose %d", v) + } +} + +func TestPermissionSelectDisabledAndInvalid(t *testing.T) { + v, n := PermissionComment, 0 + tt := permissionSelectTester(&v, true, &n) + _ = tt.Click("Can comment") + _ = tt.Click("Can edit") + if v != PermissionComment || n != 0 { + t.Fatal("disabled select changed") + } + defer func() { + if recover() == nil { + t.Fatal("bad permission did not panic") + } + }() + bad := Permission(7) + permissionSelectTester(&bad, false, &n) +} + +func TestPermissionSelectSnapshot(t *testing.T) { + snapshot(t, "permission_select", 260, 70, func(c *ui.Context) { + v := PermissionComment + ui.Column(c).Padding(12).Children(func() { PermissionSelect(c, &v, PermissionSelectOptions{}) }) + }) +} + +func TestPermissionSelectDisabledClosesAndStaysClosed(t *testing.T) { + v, disabled := PermissionView, false + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Disabled(disabled).Children(func() { PermissionSelect(c, &v, PermissionSelectOptions{Label: "Permission"}) }) + }, 400, 300) + if err := tt.Click("Can view"); err != nil { + t.Fatal(err) + } + disabled = true + tt.Frame() + if tt.HasText("Can edit") { + t.Fatal("disabled permission popup stayed open") + } + disabled = false + tt.Frame() + if tt.HasText("Can edit") { + t.Fatal("reenabling permission reopened popup") + } +} diff --git a/presence_avatars.go b/presence_avatars.go new file mode 100644 index 0000000..9816c85 --- /dev/null +++ b/presence_avatars.go @@ -0,0 +1,148 @@ +package mujica + +import ( + "fmt" + "log/slog" + + "github.com/egoist/mygo/ui" + + "mujicaui/internal" + "mujicaui/theme" +) + +// Collaborator is someone working on the same document. +type Collaborator struct { + ID, Name string + Image *ui.Bitmap + // Away marks someone present but idle. + Away bool +} + +// PresenceAvatarsOptions configure PresenceAvatars. +type PresenceAvatarsOptions struct { + // Max avatars show before the "+N" button; zero is 4. + Max int + // Following identifies the followed collaborator, empty for none. + Following string + // Size is the avatar diameter; zero is 28. + Size float32 + Disabled bool +} + +// PresenceAvatarsResult is the avatars and the follow request this frame. +type PresenceAvatarsResult struct { + Element *ui.Element + // Follow identifies a collaborator; Unfollow requests stopping. + Follow string + Unfollow bool +} + +// PresenceAvatars displays collaborators, overflow count and follow actions. +func PresenceAvatars(c *ui.Context, people []Collaborator, opts PresenceAvatarsOptions) PresenceAvatarsResult { + if opts.Max < 0 || opts.Size < 0 { + panic("mujica: PresenceAvatars Max and Size must not be negative") + } + limit, size := opts.Max, opts.Size + if limit == 0 { + limit = 4 + } + if size == 0 { + size = 28 + } + u := Density(c).Unit() + var res PresenceAvatarsResult + res.Element = ui.Row(c).AlignItems(ui.Center).Gap(u).Role(ui.RoleGroup).Label(fmt.Sprintf(msg(c, "presence_avatars.label"), len(people))). + FocusGroup(ui.Horizontal).Disabled(opts.Disabled) + disabled := res.Element.IsDisabled() + open := ui.Local(res.Element, "presence_avatars.open", func() bool { return false }) + rows := ui.Local(res.Element, "presence_avatars.rows", func() ui.ListState { return ui.ListState{} }) + *open = *open && len(people) > limit && !disabled + pick := func(p Collaborator) { + if disabled { + return + } + if p.ID == opts.Following { + res.Unfollow = true + } else { + res.Follow = p.ID + } + *open = false + slog.Debug("mujica: presence_avatars follow", "id", p.ID, "unfollow", res.Unfollow) + } + res.Element.Children(func() { + for i, p := range people { + if i >= limit { + break + } + if presenceAvatar(c, p, size, p.ID == opts.Following).Clicked() { + pick(p) + } + } + if extra := len(people) - limit; extra > 0 { + more := ui.ButtonBase(c).Size(max(size+4, theme.MinHitSize), max(size+4, theme.MinHitSize)).Radius((size + 4) / 2).Label(fmt.Sprintf(msg(c, "presence_avatars.more"), extra)) + presenceStyle(c, more, size, false) + more.Children(func() { + ui.Text(c, fmt.Sprintf("+%d", extra)).FontSize(FontSize(c, theme.CaptionSize)).FontWeight(600) + }) + if more.Clicked() && !disabled { + *open = !*open + } + Popover(c, more, open, PopoverOptions{Label: msg(c, "presence_avatars.others")}, func() { + rows.Key = func(i int) any { return people[limit+i].ID } + ui.List(c, rows, extra, func(i int) { + p := people[limit+i] + ui.Row(c).Key(p.ID).Gap(u * 2).AlignItems(ui.Center).Children(func() { + if presenceAvatar(c, p, 24, p.ID == opts.Following).Clicked() { + pick(p) + } + ui.Text(c, p.Name).SingleLine() + }) + }).Width(260).Height(min(240, float32(extra)*Density(c).ControlHeight())) + }) + } + }) + return res +} + +// presenceAvatar builds a collaborator button with presence status. +func presenceAvatar(c *ui.Context, p Collaborator, size float32, following bool) *ui.Element { + if p.ID == "" { + panic("mujica: Collaborator needs an ID") + } + k := Tokens(c) + key := "presence_avatars.follow" + if following { + key = "presence_avatars.unfollow" + } + b := ui.ButtonBase(c).Key(p.ID).Size(max(size+4, theme.MinHitSize), max(size+4, theme.MinHitSize)).Radius((size + 4) / 2).Label(fmt.Sprintf(msg(c, key), p.Name)).Tooltip(p.Name) + if p.Away { + b.Description(msg(c, "presence_avatars.away")) + } + presenceStyle(c, b, size, following) + return b.Children(func() { + Avatar(c, p.Name, AvatarOptions{Image: p.Image, Size: size}) + dot := ui.Box(c).Size(9, 9).Radius(4.5).Absolute().Right(0).Bottom(0).Border(1.5, k.Surface).Role(ui.RoleNone) + if p.Away { + dot.Background(k.Surface).Border(1.5, k.TextMuted) + } else { + dot.Background(k.Success) + } + }) +} + +// presenceStyle styles hover, focus and following rings. +func presenceStyle(c *ui.Context, b *ui.Element, size float32, following bool) { + k := Tokens(c) + b.Shrink(0).Background(k.Surface).FocusRing(false) + if b.Hovered() { + b.Background(k.SurfaceHover) + } + b.DrawOver(func(p *ui.Painter, r ui.Rect) { + if following { + p.Stroke(r, k.AccentText, r.W/2, 2) + } + if b.FocusVisible() { + internal.FocusRing(p, r, r.W/2, k.Focus) + } + }) +} diff --git a/presence_avatars_test.go b/presence_avatars_test.go new file mode 100644 index 0000000..1099044 --- /dev/null +++ b/presence_avatars_test.go @@ -0,0 +1,126 @@ +package mujica + +import ( + "fmt" + "strings" + "testing" + + "github.com/egoist/mygo/ui" +) + +func presenceSample() []Collaborator { + return []Collaborator{{ID: "a", Name: "Ada Lovelace"}, {ID: "b", Name: "Blaise Pascal", Away: true}, + {ID: "c", Name: "Carl Gauss"}, {ID: "d", Name: "Dora Maar"}, {ID: "e", Name: "Emmy Noether"}} +} + +type presenceProbe struct { + people []Collaborator + following string + max int + disabled bool + events []string +} + +func (p *presenceProbe) view(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Padding(12).Children(func() { + r := PresenceAvatars(c, p.people, PresenceAvatarsOptions{Max: p.max, Following: p.following, Disabled: p.disabled}) + if r.Follow != "" { + p.following = r.Follow + p.events = append(p.events, "follow "+r.Follow) + } + if r.Unfollow { + p.following = "" + p.events = append(p.events, "unfollow") + } + }) +} + +func TestPresenceAvatarsFollow(t *testing.T) { + p := &presenceProbe{people: presenceSample(), max: 3} + tt := ui.NewTester(p.view, 400, 300) + if !tt.HasText("+2") { + t.Fatalf("overflow missing: %q", tt.Texts()) + } + if err := tt.Click("Follow Ada Lovelace"); err != nil { + t.Fatal(err) + } + if err := tt.Click("Stop following Ada Lovelace"); err != nil { + t.Fatal(err) + } + if err := tt.Click("2 more people"); err != nil { + t.Fatal(err) + } + if err := tt.Click("Follow Emmy Noether"); err != nil { + t.Fatal(err) + } + want := []string{"follow a", "unfollow", "follow e"} + if len(p.events) != 3 || p.events[2] != want[2] { + t.Fatalf("events %v", p.events) + } +} + +func TestPresenceAvatarsKeyboardAndDisabled(t *testing.T) { + p := &presenceProbe{people: presenceSample()} + tt := ui.NewTester(p.view, 400, 300) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyRight) + tt.Key(0, ui.KeyEnter) + if p.following != "b" { + t.Fatalf("keyboard followed %q", p.following) + } + p = &presenceProbe{people: presenceSample(), disabled: true} + tt = ui.NewTester(p.view, 400, 300) + _ = tt.Click("Follow Ada Lovelace") + if p.following != "" { + t.Fatal("disabled avatars followed") + } +} + +func TestPresenceAvatarsInvalid(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("missing ID did not panic") + } + }() + ui.NewTester((&presenceProbe{people: []Collaborator{{Name: "x"}}}).view, 400, 300) +} + +func TestPresenceAvatarsSnapshot(t *testing.T) { + snapshot(t, "presence_avatars", 300, 70, func(c *ui.Context) { + ui.Column(c).Padding(12).Children(func() { PresenceAvatars(c, presenceSample(), PresenceAvatarsOptions{Following: "c"}) }) + }) +} + +func TestPresenceAvatarsSmallHitArea(t *testing.T) { + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + PresenceAvatars(c, presenceSample(), PresenceAvatarsOptions{Size: 12, Max: 1}) + }, 300, 200) + for _, name := range []string{"Follow Ada Lovelace", "4 more people"} { + r, ok := tt.Find(name) + if !ok || r.W < 28 || r.H < 28 { + t.Fatalf("%s: hit area %v", name, r) + } + } +} + +func TestPresenceAvatarsLargeOverflow(t *testing.T) { + people := make([]Collaborator, 100000) + for i := range people { + people[i] = Collaborator{ID: fmt.Sprint(i), Name: fmt.Sprintf("Member %d", i)} + } + tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{}); PresenceAvatars(c, people, PresenceAvatarsOptions{Max: 1}) }, 400, 300) + if err := tt.Click("99999 more people"); err != nil { + t.Fatal(err) + } + n := 0 + for _, text := range tt.Texts() { + if strings.HasPrefix(text, "Member ") { + n++ + } + } + if n == 0 || n > 60 { + t.Fatalf("built %d overflow member labels", n) + } +} diff --git a/reactions.go b/reactions.go new file mode 100644 index 0000000..37fd785 --- /dev/null +++ b/reactions.go @@ -0,0 +1,115 @@ +package mujica + +import ( + "fmt" + "log/slog" + "strings" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/internal" + "mujicaui/theme" +) + +// Reaction is an emoji and who reacted with it. +type Reaction struct { + Emoji string + Count int + // Mine marks the user's own reaction. + Mine bool + // People names who reacted, for the tooltip. + People []string +} + +// ReactionsOptions configure Reactions. +type ReactionsOptions struct { + // Choices are the emoji the add picker offers; required. + Choices []string + Disabled bool +} + +// ReactionsResult exposes reactions and requested changes. +type ReactionsResult struct { + Element *ui.Element + // Toggled identifies a reaction; Added identifies a picker choice. + Toggled, Added string +} + +// Reactions displays counts, toggles and an add picker. +func Reactions(c *ui.Context, reactions []Reaction, opts ReactionsOptions) ReactionsResult { + if len(opts.Choices) == 0 { + panic("mujica: Reactions needs Choices") + } + u := Density(c).Unit() + var res ReactionsResult + res.Element = ui.Row(c).Wrap().Gap(u * 2).AlignItems(ui.Center).Role(ui.RoleGroup).Label(msg(c, "reactions.label")).Disabled(opts.Disabled) + disabled := res.Element.IsDisabled() + open := ui.Local(res.Element, "reactions.open", func() bool { return false }) + *open = *open && !disabled + res.Element.Children(func() { + for _, r := range reactions { + if r.Count < 1 || r.Emoji == "" { + panic("mujica: Reaction needs an Emoji and a positive Count") + } + if reactionChip(c, r).Clicked() && !disabled { + res.Toggled = r.Emoji + } + } + add := iconAction(c, icons.Must("plus"), msg(c, "reactions.add")) + if add.Clicked() && !disabled { + *open = !*open + } + Popover(c, add, open, PopoverOptions{Label: msg(c, "reactions.pick")}, func() { + ui.Grid(c).Columns(min(len(opts.Choices), 6)).Gap(u).FocusGroup(ui.Horizontal).Children(func() { + for _, e := range opts.Choices { + b := ui.ButtonBase(c).Key(e).Size(36, 36).Radius(theme.ControlRadius).Label(e) + internal.Ring(b, Tokens(c).Focus, theme.ControlRadius) + if b.Hovered() { + b.Background(Tokens(c).SurfaceHover) + } + b.Children(func() { ui.Text(c, e).FontSize(FontSize(c, 20)) }) + if b.Clicked() && !disabled { + res.Added, *open = e, false + } + } + }) + }) + }) + if res.Toggled != "" || res.Added != "" { + slog.Debug("mujica: reactions change", "toggled", res.Toggled != "", "added", res.Added != "") + } + return res +} + +// reactionChip frames and marks the current user's reaction. +func reactionChip(c *ui.Context, r Reaction) *ui.Element { + k := Tokens(c) + u := Density(c).Unit() + label := fmt.Sprintf(msg(c, "reactions.count"), r.Emoji, r.Count) + if r.Mine { + label += ", " + msg(c, "reactions.mine") + } + b := ui.ButtonBase(c).Key(r.Emoji).MinHeight(theme.MinHitSize).MinWidth(theme.MinHitSize).Padding(0, u*2).Gap(u). + Radius(theme.MinHitSize / 2).Role(ui.RoleToggleButton).Selected(r.Mine).Label(label).Transition(colorFade) + if len(r.People) > 0 { + b.Tooltip(strings.Join(r.People, ", ")) + } + internal.Ring(b, k.Focus, theme.MinHitSize/2) + bg, border := k.Surface, k.Border + if r.Mine { + bg, border = k.Selection, k.AccentText + } + if b.Hovered() && !r.Mine { + bg = k.SurfaceHover + } + b.Background(bg).Border(theme.BorderWidth, border) + return b.Children(func() { + ui.Text(c, r.Emoji) + fg := k.TextMuted + if r.Mine { + fg = k.AccentText + } + ui.Text(c, fmt.Sprint(r.Count)).FontFeatures("tnum").FontWeight(600).TextColor(fg) + }) +} diff --git a/reactions_test.go b/reactions_test.go new file mode 100644 index 0000000..d83a652 --- /dev/null +++ b/reactions_test.go @@ -0,0 +1,95 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" +) + +type reactionsProbe struct { + items []Reaction + disabled bool + toggled []string + added []string +} + +var reactionChoices = []string{"👍", "❤️", "🎉", "👀", "🙏", "😂", "🔥"} + +func (p *reactionsProbe) view(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Padding(12).Children(func() { + r := Reactions(c, p.items, ReactionsOptions{Choices: reactionChoices, Disabled: p.disabled}) + if r.Toggled != "" { + p.toggled = append(p.toggled, r.Toggled) + } + if r.Added != "" { + p.added = append(p.added, r.Added) + } + }) +} + +func newReactionsProbe() *reactionsProbe { + return &reactionsProbe{items: []Reaction{{Emoji: "👍", Count: 3, Mine: true, People: []string{"Ada", "Blaise", "you"}}, {Emoji: "🎉", Count: 1}}} +} + +func TestReactionsToggleAndAdd(t *testing.T) { + p := newReactionsProbe() + tt := ui.NewTester(p.view, 400, 300) + if err := tt.Click("👍 3, including you"); err != nil { + t.Fatal(err) + } + if err := tt.Click("🎉 1"); err != nil { + t.Fatal(err) + } + if err := tt.Click("Add reaction"); err != nil { + t.Fatal(err) + } + if err := tt.Click("🔥"); err != nil { + t.Fatal(err) + } + if len(p.toggled) != 2 || p.toggled[0] != "👍" || len(p.added) != 1 || p.added[0] != "🔥" { + t.Fatalf("toggled %v added %v", p.toggled, p.added) + } + if _, ok := tt.Find("❤️"); ok { + t.Fatal("picker stayed open") + } +} + +func TestReactionsKeyboard(t *testing.T) { + p := newReactionsProbe() + tt := ui.NewTester(p.view, 400, 300) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeySpace) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyEnter) + if !tt.HasText("❤️") { + t.Fatal("Enter on add did not open the picker") + } + if len(p.toggled) != 1 { + t.Fatalf("Space toggled %v", p.toggled) + } +} + +func TestReactionsDisabledAndInvalid(t *testing.T) { + p := newReactionsProbe() + p.disabled = true + tt := ui.NewTester(p.view, 400, 300) + _ = tt.Click("🎉 1") + _ = tt.Click("Add reaction") + if len(p.toggled) != 0 || tt.HasText("❤️") { + t.Fatal("disabled reactions acted") + } + defer func() { + if recover() == nil { + t.Fatal("zero count did not panic") + } + }() + ui.NewTester((&reactionsProbe{items: []Reaction{{Emoji: "x"}}}).view, 400, 300) +} + +func TestReactionsSnapshot(t *testing.T) { + snapshot(t, "reactions", 300, 70, func(c *ui.Context) { + ui.Column(c).Padding(12).Children(func() { Reactions(c, newReactionsProbe().items, ReactionsOptions{Choices: reactionChoices}) }) + }) +} diff --git a/recent_files.go b/recent_files.go new file mode 100644 index 0000000..1c67560 --- /dev/null +++ b/recent_files.go @@ -0,0 +1,183 @@ +package mujica + +import ( + "cmp" + "fmt" + "log/slog" + "path" + "slices" + "time" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/theme" +) + +// RecentFile is a file the user opened. +type RecentFile struct { + Path string + Opened time.Time +} + +// RecentFilesOptions configure a RecentFiles. +type RecentFilesOptions struct { + // Revision changes when files are edited in place. + Revision uint64 + // Now dates the groups; zero uses the frame's time. + Now time.Time + Disabled bool +} + +// RecentFilesState is a RecentFiles' choice and place. +type RecentFilesState struct { + rows ui.ListState + selected int + flat []recentRow + data *RecentFile + n int + day time.Time + started bool + revision uint64 +} + +// recentRow holds a group header or file index. +type recentRow struct{ group, file int } + +// RecentFilesResult is the list and what happened this frame. +type RecentFilesResult struct { + Element *ui.Element + Opened *RecentFile + // Removed identifies one file; Cleared requests clearing all files. + Removed string + Cleared bool +} + +// RecentFiles groups recent files by time and supports clearing. +func RecentFiles(c *ui.Context, files []RecentFile, s *RecentFilesState, opts RecentFilesOptions) RecentFilesResult { + if s == nil { + panic("mujica: RecentFiles needs a state") + } + if !s.started { + s.started, s.selected = true, -1 + } + now := opts.Now + if now.IsZero() { + now = c.Now() + } + if s.revision != opts.Revision { + s.day = time.Time{} + s.revision = opts.Revision + } + recentGroup(files, s, now) + k := Tokens(c) + d := Density(c) + var res RecentFilesResult + res.Element = codePanel(c).Disabled(opts.Disabled) + disabled := res.Element.IsDisabled() + if disabled { + copy := *s + s = © + } + res.Element.Children(func() { + codeHeader(c, func() { + ui.Text(c, msg(c, "recent_files.title")).Font(theme.SerifFont).FontWeight(600).Grow(1) + if len(files) > 0 && Button(c, msg(c, "recent_files.clear"), ButtonOptions{Variant: Ghost}).MinHeight(theme.MinHitSize).Clicked() && !disabled { + res.Cleared = true + slog.Debug("mujica: recent_files clear") + } + }) + if len(files) == 0 { + codeCaption(c, msg(c, "recent_files.empty")).Padding(d.Unit() * 4) + return + } + s.rows.Selected = &s.selected + s.rows.Key = func(i int) any { + r := s.flat[i] + if r.file < 0 { + return r.group + } + return files[r.file].Path + } + s.rows.Header = func(i int) bool { return s.flat[i].file < 0 } + s.rows.Label = func(i int) string { + if r := s.flat[i]; r.file >= 0 { + return path.Base(files[r.file].Path) + } + return recentGroupName(c, s.flat[i].group) + } + list := codeRows(c, &s.rows, len(s.flat), func(i int) { + r := s.flat[i] + if r.file < 0 { + ui.Text(c, recentGroupName(c, r.group)).Font(theme.SerifFont).FontSize(FontSize(c, theme.CaptionSize)). + TextColor(k.Ornament.Mix(k.Text, 0.2)).Grow(1) + return + } + f := files[r.file] + FileIcon(c, f.Path, FileIconOptions{}) + ui.Column(c).Grow(1).MinWidth(0).Children(func() { + ui.Text(c, path.Base(f.Path)).SingleLine() + codeCaption(c, path.Dir(f.Path)) + }) + when := f.Opened.Format("15:04") + if r.group >= 2 { + when = f.Opened.Format("Jan 2") + } + codeCaption(c, when).Shrink(0) + if iconAction(c, icons.Must("x"), fmt.Sprintf(msg(c, "recent_files.remove"), path.Base(f.Path))).Clicked() && !disabled { + res.Removed = f.Path + slog.Debug("mujica: recent_files remove") + } + }) + list.Background(k.Surface) + if list.Submitted() && !disabled && s.selected >= 0 && s.selected < len(s.flat) && s.flat[s.selected].file >= 0 { + f := files[s.flat[s.selected].file] + res.Opened = &f + slog.Debug("mujica: recent_files open") + } + }) + return res +} + +func recentGroupName(c *ui.Context, g int) string { + return msg(c, [...]string{"recent_files.today", "recent_files.yesterday", "recent_files.week", "recent_files.earlier"}[g]) +} + +// recentGroup caches files sorted into relative-day groups. +func recentGroup(files []RecentFile, s *RecentFilesState, now time.Time) { + var first *RecentFile + if len(files) > 0 { + first = &files[0] + } + today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) + if s.data == first && s.n == len(files) && s.day.Equal(today) { + return + } + s.data, s.n, s.day = first, len(files), today + idx := make([]int, len(files)) + for i := range idx { + idx[i] = i + } + slices.SortStableFunc(idx, func(a, b int) int { return cmp.Compare(files[b].Opened.UnixNano(), files[a].Opened.UnixNano()) }) + group := func(t time.Time) int { + switch { + case !t.Before(today): + return 0 + case !t.Before(today.AddDate(0, 0, -1)): + return 1 + case !t.Before(today.AddDate(0, 0, -6)): + return 2 + } + return 3 + } + s.flat = s.flat[:0] + last := -1 + for _, i := range idx { + g := group(files[i].Opened) + if g != last { + s.flat = append(s.flat, recentRow{g, -1}) + last = g + } + s.flat = append(s.flat, recentRow{g, i}) + } +} diff --git a/recent_files_test.go b/recent_files_test.go new file mode 100644 index 0000000..da63658 --- /dev/null +++ b/recent_files_test.go @@ -0,0 +1,172 @@ +package mujica + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/egoist/mygo/ui" +) + +var recentNow = time.Date(2026, 10, 5, 15, 0, 0, 0, time.UTC) + +func recentSample() []RecentFile { + return []RecentFile{ + {"/court/old/charter.pdf", recentNow.AddDate(0, -2, 0)}, + {"/court/seal.go", recentNow.Add(-2 * time.Hour)}, + {"/court/notes.md", recentNow.Add(-26 * time.Hour)}, + {"/court/crest.png", recentNow.AddDate(0, 0, -4)}, + {"/court/ledger.csv", recentNow.Add(-10 * time.Minute)}, + } +} + +type recentProbe struct { + files []RecentFile + s RecentFilesState + disabled bool + opened []string +} + +func (p *recentProbe) view(c *ui.Context) { + Use(c, Settings{}) + r := RecentFiles(c, p.files, &p.s, RecentFilesOptions{Now: recentNow, Disabled: p.disabled}) + r.Element.Height(420) + if r.Opened != nil { + p.opened = append(p.opened, r.Opened.Path) + } + if r.Removed != "" { + for i, f := range p.files { + if f.Path == r.Removed { + p.files = append(p.files[:i:i], p.files[i+1:]...) + break + } + } + } + if r.Cleared { + p.files = nil + } +} + +func TestRecentFilesGroups(t *testing.T) { + p := &recentProbe{files: recentSample()} + ui.NewTester(p.view, 480, 440) + want := []recentRow{{0, -1}, {0, 4}, {0, 1}, {1, -1}, {1, 2}, {2, -1}, {2, 3}, {3, -1}, {3, 0}} + if len(p.s.flat) != len(want) { + t.Fatalf("rows %v", p.s.flat) + } + for i := range want { + if p.s.flat[i] != want[i] { + t.Fatalf("rows %v, want %v", p.s.flat, want) + } + } +} + +func TestRecentFilesOpenRemoveClear(t *testing.T) { + p := &recentProbe{files: recentSample()} + tt := ui.NewTester(p.view, 480, 440) + for _, s := range []string{"Today", "Yesterday", "This week", "Earlier", "14:50", "Oct 1"} { + if !tt.HasText(s) { + t.Fatalf("missing %q in %q", s, tt.Texts()) + } + } + r, _ := tt.Find("seal.go") + tt.ClickAt(r.X, r.Y+r.H/2) + tt.ClickAt(r.X, r.Y+r.H/2) + if len(p.opened) != 1 || p.opened[0] != "/court/seal.go" { + t.Fatalf("opened %v", p.opened) + } + if err := tt.Click("Remove notes.md from recent"); err != nil { + t.Fatal(err) + } + if len(p.files) != 4 || tt.HasText("Yesterday") { + t.Fatalf("remove left %d files", len(p.files)) + } + _ = tt.Click("Clear") + if len(p.files) != 0 || !tt.HasText("No recent files") { + t.Fatal("clear did not empty the list") + } +} + +func TestRecentFilesKeyboardAndDisabled(t *testing.T) { + p := &recentProbe{files: recentSample()} + tt := ui.NewTester(p.view, 480, 440) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyDown) + tt.Key(0, ui.KeyEnter) + if len(p.opened) != 1 || p.opened[0] != "/court/ledger.csv" { + t.Fatalf("keyboard opened %v", p.opened) + } + p = &recentProbe{files: recentSample(), disabled: true} + tt = ui.NewTester(p.view, 480, 440) + _ = tt.Click("Clear") + if len(p.files) != 5 { + t.Fatal("disabled list cleared") + } +} + +func TestRecentFilesSnapshot(t *testing.T) { + snapshot(t, "recent_files", 460, 440, func(c *ui.Context) { + p := ui.Local(c.Root(), "snap", func() *recentProbe { return &recentProbe{files: recentSample()} }) + ui.Column(c).Padding(12).Children(func() { + RecentFiles(c, (*p).files, &(*p).s, RecentFilesOptions{Now: recentNow}).Element.Height(416) + }) + }) +} + +func TestRecentFilesLargeAndRevision(t *testing.T) { + files := make([]RecentFile, 100000) + for i := range files { + files[i] = RecentFile{Path: fmt.Sprintf("/f-%d.txt", i), Opened: recentNow} + } + s := RecentFilesState{} + revision := uint64(0) + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + RecentFiles(c, files, &s, RecentFilesOptions{Now: recentNow, Revision: revision}).Element.Height(220) + }, 500, 250) + n := 0 + for _, text := range tt.Texts() { + if strings.HasPrefix(text, "f-") { + n++ + } + } + if n == 0 || n > 60 { + t.Fatalf("built %d recent files", n) + } + files[0].Opened = recentNow.AddDate(0, -1, 0) + revision++ + tt.Frame() + if r := s.flat[len(s.flat)-1]; r.group != 3 || r.file != 0 { + t.Fatalf("revised last row %+v", r) + } +} + +func TestRecentFilesDisablingBeforeDeliveredKey(t *testing.T) { + p := &recentProbe{files: recentSample()} + tt := ui.NewTester(p.view, 480, 440) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyDown) + before := p.s.selected + p.disabled = true + tt.Key(0, ui.KeyDown) + if p.s.selected != before { + t.Fatalf("disabled row %d, want %d", p.s.selected, before) + } +} + +func TestRecentFilesRemovedSelectionDoesNotOpenAnother(t *testing.T) { + p := &recentProbe{files: []RecentFile{{Path: "a.txt", Opened: recentNow}, {Path: "b.txt", Opened: recentNow}}} + tt := ui.NewTester(p.view, 480, 440) + if err := tt.Click("b.txt"); err != nil { + t.Fatal(err) + } + p.files = p.files[:1] + tt.Frame() + tt.Key(0, ui.KeyEnter) + if len(p.opened) != 0 { + t.Fatalf("opened %v after removing selection", p.opened) + } +} diff --git a/remote_cursor.go b/remote_cursor.go new file mode 100644 index 0000000..1e1f5a7 --- /dev/null +++ b/remote_cursor.go @@ -0,0 +1,85 @@ +package mujica + +import ( + "fmt" + "hash/fnv" + "strings" + + "github.com/egoist/mygo/ui" + + "mujicaui/internal" + "mujicaui/theme" +) + +// RemoteCaret describes a collaborator's caret and selection offsets. +type RemoteCaret struct { + ID, Name string + // Offset and Anchor bound the selection, equal for a caret. + Offset, Anchor int +} + +// RemoteCursorOptions configure RemoteCursor. +type RemoteCursorOptions struct { + // Caret maps text offsets into overlay-relative rectangles; required. + Caret func(offset int) ui.Rect + // Selection maps offset ranges into overlay-relative rectangles; optional. + Selection func(from, to int) []ui.Rect +} + +// RemoteCursor overlays collaborator carets, selections and names; input passes through. +// Place it filling the editor's box, after the editor. +func RemoteCursor(c *ui.Context, carets []RemoteCaret, opts RemoteCursorOptions) *ui.Element { + if opts.Caret == nil { + panic("mujica: RemoteCursor needs Caret") + } + k := Tokens(c) + names := make([]string, len(carets)) + for i, rc := range carets { + if rc.ID == "" || rc.Offset < 0 || rc.Anchor < 0 { + panic("mujica: RemoteCaret needs an ID and offsets not negative") + } + names[i] = rc.Name + } + size := FontSize(c, theme.CaptionSize) + e := ui.Box(c).Absolute().Top(0).Left(0).Right(0).Bottom(0).PassThrough().Clip().Role(ui.RoleNone) + if len(carets) > 0 { + e.Role(ui.RoleStatus).Label(fmt.Sprintf(msg(c, "remote_cursor.label"), strings.Join(names, ", "))) + } + return e.Draw(func(p *ui.Painter, r ui.Rect) { + for _, rc := range carets { + col := remoteColor(k, rc.ID) + if opts.Selection != nil && rc.Anchor != rc.Offset { + for _, b := range opts.Selection(min(rc.Anchor, rc.Offset), max(rc.Anchor, rc.Offset)) { + p.Fill(remoteRect(p, ui.Rect{X: r.X + b.X, Y: r.Y + b.Y, W: b.W, H: b.H}), col.Alpha(0.22), 2) + } + } + cb := opts.Caret(rc.Offset) + x, y := internal.Snap(p, r.X+cb.X), internal.Snap(p, r.Y+cb.Y) + p.Fill(ui.Rect{X: x - 1, Y: y, W: 2, H: cb.H}, col, 0) + // Keep the name flag inside the overlay. + tw, th := p.MeasureText(0, ui.Span{Text: rc.Name, Size: size}) + flag := ui.Rect{X: x - 1, Y: y - th - 2, W: min(tw+8, r.W), H: th + 2} + if flag.Y < r.Y { + flag.Y = y + cb.H + } + flag.X = max(r.X, min(flag.X, r.X+r.W-flag.W)) + flag.Y = max(r.Y, min(flag.Y, r.Y+r.H-flag.H)) + flag = remoteRect(p, flag) + p.Fill(flag, col, 2) + p.Clip(flag, 2, func() { p.Text(flag.X+4, flag.Y+1, rc.Name, size, k.Surface) }) + } + }) +} + +func remoteRect(p *ui.Painter, r ui.Rect) ui.Rect { + x, y := internal.Snap(p, r.X), internal.Snap(p, r.Y) + return ui.Rect{X: x, Y: y, W: internal.Snap(p, r.X+r.W) - x, H: internal.Snap(p, r.Y+r.H) - y} +} + +// remoteColor assigns a palette color by collaborator ID. +func remoteColor(k theme.Tokens, id string) ui.Color { + h := fnv.New32a() + h.Write([]byte(id)) + pal := flamegraphPalette(k) + return pal[h.Sum32()%uint32(len(pal))] +} diff --git a/remote_cursor_test.go b/remote_cursor_test.go new file mode 100644 index 0000000..72ad2d1 --- /dev/null +++ b/remote_cursor_test.go @@ -0,0 +1,87 @@ +package mujica + +import ( + "image/color" + "testing" + + "github.com/egoist/mygo/ui" + + "mujicaui/theme" +) + +// remoteMono maps offsets onto 40-column, 8×20 DIP cells. +func remoteMono() RemoteCursorOptions { + at := func(o int) (float32, float32) { return float32(o%40) * 8, float32(o/40) * 20 } + return RemoteCursorOptions{ + Caret: func(o int) ui.Rect { x, y := at(o); return ui.Rect{X: x, Y: y, W: 2, H: 20} }, + Selection: func(from, to int) []ui.Rect { + var out []ui.Rect + for o := from; o < to; o++ { + x, y := at(o) + out = append(out, ui.Rect{X: x, Y: y, W: 8, H: 20}) + } + return out + }, + } +} + +func remoteTester(carets []RemoteCaret, button *int) *ui.Tester { + return ui.NewTester(func(c *ui.Context) { + Use(c, Settings{Mode: Light}) + ui.Box(c).Size(320, 120).Children(func() { + if Button(c, "Underneath", ButtonOptions{}).Size(320, 120).Clicked() { + *button++ + } + RemoteCursor(c, carets, remoteMono()) + }) + }, 340, 140) +} + +func TestRemoteCursorDrawCaretAndSelection(t *testing.T) { + carets := []RemoteCaret{{ID: "ada", Name: "Ada", Offset: 45, Anchor: 42}} + tt := remoteTester(carets, new(int)) + col := remoteColor(theme.Light(), "ada") + img := tt.Image() + // Offset 45 places the caret at x=40, y=20..40. + if got := img.RGBAAt(40, 35); got != (color.RGBA{col.R, col.G, col.B, 255}) { + t.Fatalf("caret pixel %v, want %v", got, col) + } + if got := img.RGBAAt(28, 35); got == (color.RGBA{col.R, col.G, col.B, 255}) || got == img.RGBAAt(100, 35) { + t.Fatalf("selection pixel %v not tinted", got) + } + if !tt.HasText("Also editing: Ada") { + t.Fatalf("texts %q", tt.Texts()) + } +} + +func TestRemoteCursorPassInput(t *testing.T) { + n := 0 + tt := remoteTester([]RemoteCaret{{ID: "b", Name: "Blaise", Offset: 3, Anchor: 3}}, &n) + tt.ClickAt(30, 10) + if n != 1 { + t.Fatal("the overlay took the click") + } +} + +func TestRemoteCursorInvalid(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("missing ID did not panic") + } + }() + remoteTester([]RemoteCaret{{Name: "x"}}, new(int)) +} + +func TestRemoteCursorSnapshot(t *testing.T) { + snapshot(t, "remote_cursor", 360, 120, func(c *ui.Context) { + k := Tokens(c) + ui.Box(c).Margin(12).Size(320, 80).Background(k.Surface).Children(func() { + ui.Column(c).Children(func() { + for _, l := range []string{"The seal of the court is kept", "by the chancellor, who answers"} { + ui.Text(c, l).Font(theme.MonoFont).FontSize(FontSize(c, 13)).Height(20) + } + }) + RemoteCursor(c, []RemoteCaret{{ID: "ada", Name: "Ada", Offset: 48, Anchor: 43}, {ID: "blaise", Name: "Blaise", Offset: 8, Anchor: 8}}, remoteMono()) + }) + }) +} diff --git a/rename_inline.go b/rename_inline.go new file mode 100644 index 0000000..3633525 --- /dev/null +++ b/rename_inline.go @@ -0,0 +1,197 @@ +package mujica + +import ( + "log/slog" + "path" + "strings" + + "github.com/egoist/mygo/ui" + + "mujicaui/theme" +) + +// RenameInlineOptions configure a RenameInline. +type RenameInlineOptions struct { + // Validate returns an error message, or empty for acceptance. + Validate func(name string) string + // Folder selects the whole name: folders have no extension. + Folder bool + Disabled bool +} + +// RenameInlineResult is a RenameInline and its outcome in this frame. +type RenameInlineResult struct { + Element *ui.Element + // Renamed reports a saved name; Canceled reports an abandoned edit. + Renamed, Canceled bool +} + +// renameState is one editing session. +type renameState struct { + active bool + draft string + // gen rebuilds the editor with its caret at the end. + gen int + // tail appends text next frame behind the caret. + tail string + shown bool + stemSel bool + focused bool + err string + folder bool +} + +// renameStem is the part of name before its extension. +func renameStem(name string, folder bool) string { + ext := path.Ext(name) + if folder || ext == name || ext == "" { + return name + } + return strings.TrimSuffix(name, ext) +} + +// RenameInline edits names with validation and initial stem replacement. +// Enter validates; Escape cancels; blur confirms valid names. +func RenameInline(c *ui.Context, name *string, editing *bool, opts RenameInlineOptions) RenameInlineResult { + if name == nil || editing == nil { + panic("mujica: RenameInline needs a name and editing") + } + var res RenameInlineResult + res.Element = ui.Row(c).MinWidth(0).MinHeight(theme.MinHitSize).AlignItems(ui.Center).Disabled(opts.Disabled) + st := ui.Local(res.Element, "rename_inline", func() renameState { return renameState{} }) + if res.Element.IsDisabled() && *editing { + *editing = false + res.Canceled = st.active + st.active = false + } + res.Element.Children(func() { + if !*editing { + st.active = false + t := ui.Text(c, *name).SingleLine().MinWidth(0) + if t.DoubleClicked() && !res.Element.IsDisabled() { + *editing = true + c.Invalidate() + } + return + } + stem := renameStem(*name, opts.Folder) + if !st.active { + // Start with the stem and its caret at the end. + *st = renameState{active: true, draft: stem, gen: st.gen + 1, tail: (*name)[len(stem):], stemSel: true, folder: opts.Folder} + slog.Debug("mujica: rename_inline start") + } + switch { + case st.tail != "" && st.shown: + st.draft, st.tail, st.shown = st.draft+st.tail, "", false + case st.tail != "": + // Show the head for one frame to place its caret. + st.shown = true + c.Invalidate() + } + finish := func(ok bool) { + if ok { + if why := renameCheck(c, st.draft, opts.Validate); why != "" { + st.err = why + return + } + res.Renamed = st.draft != *name + *name = st.draft + } else { + res.Canceled = true + } + *editing, st.active = false, false + slog.Debug("mujica: rename_inline end", "renamed", res.Renamed) + } + ui.Box(c).Key(st.gen).Grow(1).MinWidth(0).Children(func() { + in := TextInput(c, &st.draft, TextInputOptions{Label: msg(c, "rename_inline.label"), Error: st.err}) + in.Element.FillWidth() + in.Input.AutoFocus() + if in.Changed() { + st.err = "" + renameTyped(st, *name, opts.Folder) + } + renameSelection(c, in.Input, st) + switch { + case in.Input.Shortcut(0, ui.KeyEscape): + finish(false) + case in.Submitted(): + finish(true) + case in.Input.Focused(): + st.focused = true + case st.focused && st.tail == "": + // Confirm valid names when focus leaves. + finish(renameCheck(c, st.draft, opts.Validate) == "") + } + }) + }) + return res +} + +func renameCheck(c *ui.Context, name string, validate func(string) string) string { + if strings.TrimSpace(name) == "" { + return msg(c, "rename_inline.empty") + } + if validate != nil { + return validate(name) + } + return "" +} + +// renameSelection marks the stem for replacement on initial input. +func renameSelection(c *ui.Context, in *ui.Element, st *renameState) { + k := Tokens(c) + stem := renameStem(st.draft, st.folder) + if st.stemSel && st.tail == "" { + w, _ := c.MeasureText(0, ui.Span{Text: stem, Size: FontSize(c, theme.BodySize)}) + in.Draw(func(p *ui.Painter, r ui.Rect) { + p.Fill(ui.Rect{X: r.X, Y: r.Y + 2, W: w, H: r.H - 4}, k.Selection, 2) + }) + } + in.HandleInput(func(ev ui.InputEvent) bool { + if !st.stemSel || in.IsDisabled() { + return false + } + switch { + case ev.Kind == ui.InputKeyDown && ev.Mods == 0: + switch ev.Key { + case ui.KeyBackspace, ui.KeyDelete: + renameReplace(st, "") + return true + case ui.KeyLeft, ui.KeyHome, ui.KeyUp: + // An empty head clamps the caret to the start. + st.draft, st.tail, st.stemSel, st.shown = "", st.draft, false, false + return true + case ui.KeyRight: + st.stemSel = false + return true + } + } + if ev.Kind == ui.InputPointerDown || ev.Kind == ui.InputKeyDown && + (ev.Key == ui.KeyLeft || ev.Key == ui.KeyRight || ev.Key == ui.KeyHome || ev.Key == ui.KeyEnd || ev.Key == ui.KeyUp || ev.Key == ui.KeyDown || ev.Key == ui.KeyA && ev.Mods&ui.Cmd != 0) { + st.stemSel = false + } + return false + }) +} + +// renameReplace substitutes the stem and rebuilds the caret. +func renameReplace(st *renameState, text string) { + ext := st.draft[len(renameStem(st.draft, st.folder)):] + st.draft, st.tail, st.gen, st.stemSel, st.shown = text, ext, st.gen+1, false, false +} + +// renameTyped replaces the stem with newly inserted text. +func renameTyped(st *renameState, name string, folder bool) { + if !st.stemSel { + return + } + stem := renameStem(name, folder) + ext := name[len(stem):] + st.stemSel = false + if strings.HasPrefix(st.draft, stem) && strings.HasSuffix(st.draft, ext) && len(st.draft) >= len(name) { + typed := st.draft[len(stem) : len(st.draft)-len(ext)] + st.draft = stem + ext + renameReplace(st, typed) + slog.Debug("mujica: rename_inline replace stem") + } +} diff --git a/rename_inline_test.go b/rename_inline_test.go new file mode 100644 index 0000000..f530ee1 --- /dev/null +++ b/rename_inline_test.go @@ -0,0 +1,169 @@ +package mujica + +import ( + "strings" + "testing" + + "github.com/egoist/mygo/ui" +) + +type renameProbe struct { + name string + editing, disabled bool + folder bool + renamed, canceled int +} + +func (p *renameProbe) view(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Padding(12).Gap(8).Children(func() { + r := RenameInline(c, &p.name, &p.editing, RenameInlineOptions{Folder: p.folder, Disabled: p.disabled, + Validate: func(n string) string { + if strings.ContainsRune(n, '/') { + return "No slashes" + } + return "" + }}) + if r.Renamed { + p.renamed++ + } + if r.Canceled { + p.canceled++ + } + Button(c, "Elsewhere", ButtonOptions{}) + }) +} + +func TestRenameStem(t *testing.T) { + for in, want := range map[string]string{"seal.txt": "seal", "a.tar.gz": "a.tar", ".env": ".env", "README": "README"} { + if got := renameStem(in, false); got != want { + t.Errorf("%s: stem %q, want %q", in, got, want) + } + } + if renameStem("v1.2", true) != "v1.2" { + t.Fatal("folder stem dropped its dot part") + } +} + +func TestRenameInlineTypingReplacesStem(t *testing.T) { + p := &renameProbe{name: "seal.txt", editing: true} + tt := ui.NewTester(p.view, 400, 200) + if !tt.Focused("Name") { + t.Fatal("editor did not take the focus") + } + tt.Type("wax") + tt.Type("y") + tt.Key(0, ui.KeyEnter) + if p.name != "waxy.txt" || p.renamed != 1 || p.editing { + t.Fatalf("name %q renamed %d editing %v", p.name, p.renamed, p.editing) + } +} + +func TestRenameInlineKeysAroundSelection(t *testing.T) { + p := &renameProbe{name: "seal.txt", editing: true} + tt := ui.NewTester(p.view, 400, 200) + tt.Key(0, ui.KeyLeft) + tt.Type("royal ") + tt.Key(0, ui.KeyEnter) + if p.name != "royal seal.txt" { + t.Fatalf("Left then typing gave %q", p.name) + } + p.editing = true + tt.Frame() + tt.Key(0, ui.KeyBackspace) + tt.Type("crest") + tt.Key(0, ui.KeyEnter) + if p.name != "crest.txt" { + t.Fatalf("Backspace then typing gave %q", p.name) + } + p.editing = true + tt.Frame() + tt.Key(0, ui.KeyRight) + tt.Type("s") + tt.Key(0, ui.KeyEnter) + if p.name != "crests.txt" { + t.Fatalf("Right then typing gave %q", p.name) + } +} + +func TestRenameInlineValidationAndCancel(t *testing.T) { + p := &renameProbe{name: "seal.txt", editing: true} + tt := ui.NewTester(p.view, 400, 200) + tt.Type("a/b") + tt.Key(0, ui.KeyEnter) + if !p.editing || p.name != "seal.txt" || !tt.HasText("No slashes") { + t.Fatalf("invalid name accepted: %q %q", p.name, tt.Texts()) + } + tt.Key(0, ui.KeyEscape) + if p.editing || p.name != "seal.txt" || p.canceled != 1 { + t.Fatalf("Escape: name %q editing %v canceled %d", p.name, p.editing, p.canceled) + } +} + +func TestRenameInlineBlurCommitsAndFolder(t *testing.T) { + p := &renameProbe{name: "v1.2", editing: true, folder: true} + tt := ui.NewTester(p.view, 400, 200) + tt.Type("archive") + if err := tt.Click("Elsewhere"); err != nil { + t.Fatal(err) + } + if p.name != "archive" || p.editing { + t.Fatalf("blur gave %q editing %v", p.name, p.editing) + } +} + +func TestRenameInlineDoubleClickAndDisabled(t *testing.T) { + p := &renameProbe{name: "seal.txt"} + tt := ui.NewTester(p.view, 400, 200) + r, _ := tt.Find("seal.txt") + tt.ClickAt(r.X+4, r.Y+4) + tt.ClickAt(r.X+4, r.Y+4) + if !p.editing { + t.Fatal("double click did not start editing") + } + p.disabled = true + tt.Frame() + if p.editing || p.canceled != 1 { + t.Fatal("disabling did not end the edit") + } +} + +func TestRenameInlineSnapshot(t *testing.T) { + snapshot(t, "rename_inline", 360, 110, func(c *ui.Context) { + p := ui.Local(c.Root(), "snap", func() *renameProbe { return &renameProbe{name: "royal seal.txt", editing: true} }) + ui.Column(c).Padding(12).Gap(8).Children(func() { + RenameInline(c, &(*p).name, &(*p).editing, RenameInlineOptions{}) + name, off := "ledger.md", false + RenameInline(c, &name, &off, RenameInlineOptions{}) + }) + }) +} + +func TestRenameInlineCompositionCommit(t *testing.T) { + p := &renameProbe{name: "seal.txt", editing: true} + tt := ui.NewTester(p.view, 400, 200) + tt.Compose("印", 1) + tt.Type("印章") + tt.Key(0, ui.KeyEnter) + if p.name != "印章.txt" || p.renamed != 1 { + t.Fatalf("committed name %q", p.name) + } +} + +func TestRenameInlineNativeTypingAndPaste(t *testing.T) { + p := &renameProbe{name: "seal.txt", editing: true} + tt := ui.NewTester(p.view, 400, 200) + tt.TypeKey(0, ui.KeyW, "w") + tt.Key(0, ui.KeyEnter) + if p.name != "w.txt" { + t.Fatalf("native typing renamed %q", p.name) + } + p.editing = true + tt.Frame() + tt.SetClipboard("wax") + tt.Key(ui.Cmd, ui.KeyV) + tt.Key(0, ui.KeyEnter) + if p.name != "wax.txt" { + t.Fatalf("paste renamed %q", p.name) + } +} diff --git a/select.go b/select.go index 91e46ac..6f3de2a 100644 --- a/select.go +++ b/select.go @@ -41,6 +41,11 @@ type selectTypeahead struct { at time.Time } +type selectLifetime struct { + disabled bool + generation int +} + // Select chooses *value from options in a popup; typing on the closed select jumps to a match. func Select[T comparable](c *ui.Context, value *T, options []SelectOption[T], opts SelectOptions) ChoiceResult { k := Tokens(c) @@ -48,7 +53,15 @@ func Select[T comparable](c *ui.Context, value *T, options []SelectOption[T], op // SelectBase chooses into a copy as it is built; a choice counts once the option is known enabled. pick := old var s *ui.SelectParts[T] - ui.Row(c).Shrink(0).Disabled(opts.Disabled).Children(func() { s = ui.SelectBase(c, &pick) }) + site := ui.Row(c).Shrink(0).Disabled(opts.Disabled) + life := ui.Local(site, selectKey{"lifetime"}, func() selectLifetime { return selectLifetime{} }) + if disabled := site.IsDisabled(); disabled != life.disabled { + life.disabled = disabled + life.generation++ + } + site.Children(func() { + ui.Row(c).Key(life.generation).Children(func() { s = ui.SelectBase(c, &pick) }) + }) tr := s.Trigger commit := func() { if pick != *value && !tr.IsDisabled() && selectEnabled(options, pick) { @@ -62,6 +75,9 @@ func Select[T comparable](c *ui.Context, value *T, options []SelectOption[T], op } ta := ui.Local(tr, selectKey{"typeahead"}, func() selectTypeahead { return selectTypeahead{} }) tr.TextCaret(ui.Rect{W: 1, H: Density(c).ControlHeight()}).HandleInput(func(ev ui.InputEvent) bool { + if tr.IsDisabled() { + return false + } if ev.Kind != ui.InputText || strings.TrimSpace(ev.Text) == "" { return false } @@ -97,8 +113,7 @@ func Select[T comparable](c *ui.Context, value *T, options []SelectOption[T], op }) pick = *value lastHi := ui.Local(tr, selectKey{"shown"}, func() int { return -1 }) - if opts.Disabled { - // SelectParts has no way to close it (ui/base.go:310): a disabled select builds no popup. + if tr.IsDisabled() { return ChoiceResult{tr, false} } s.Popup(func(panel *ui.Element) { diff --git a/share_dialog.go b/share_dialog.go new file mode 100644 index 0000000..f8babc0 --- /dev/null +++ b/share_dialog.go @@ -0,0 +1,142 @@ +package mujica + +import ( + "fmt" + "log/slog" + "strings" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/theme" +) + +// ShareMember is someone the item is shared with. +type ShareMember struct { + ID, Name, Email string + Permission Permission + // Owner cannot be changed or removed. + Owner bool +} + +// ShareInvite is a new person to share with. +type ShareInvite struct { + Email string + Permission Permission +} + +// ShareDialogOptions configure a ShareDialog. +type ShareDialogOptions struct { + // Title names what is shared. + Title string + // Link is the item's address; empty hides the link row. + Link string + Disabled bool + // OnCopy handles copying the link; nil uses the clipboard. + OnCopy func(link string) +} + +// ShareDialogResult is what happened in the dialog this frame. +type ShareDialogResult struct { + Element *ui.Element + // Invited requests membership; Changed and Removed identify applied changes. + Invited *ShareInvite + Changed, Removed string + // Copied reports the link was written to the clipboard. + Copied bool +} + +type shareDraft struct { + email string + perm Permission + rows ui.ListState +} + +// ShareDialog manages invitations, permissions, members and copying links. +func ShareDialog(c *ui.Context, open *bool, members *[]ShareMember, opts ShareDialogOptions) ShareDialogResult { + if open == nil || members == nil || opts.Title == "" { + panic("mujica: ShareDialog needs open, members and a Title") + } + var res ShareDialogResult + site := ui.Box(c).Disabled(opts.Disabled) + if site.IsDisabled() { + *open = false + } + draft := ui.Local(site, "share_dialog.draft", func() shareDraft { return shareDraft{} }) + u := Density(c).Unit() + res.Element = Dialog(c, open, DialogOptions{Title: fmt.Sprintf(msg(c, "share_dialog.title"), opts.Title), Width: 480}, func() { + ui.Column(c).Gap(u * 3).Disabled(opts.Disabled).Children(func() { + ui.Row(c).Gap(u * 2).AlignItems(ui.Start).Children(func() { + in := TextInput(c, &draft.email, TextInputOptions{Placeholder: msg(c, "share_dialog.placeholder"), Label: msg(c, "share_dialog.invite")}) + in.Element.Grow(1).MinWidth(0) + PermissionSelect(c, &draft.perm, PermissionSelectOptions{Label: msg(c, "share_dialog.invitePermission")}) + send := Button(c, msg(c, "share_dialog.send"), ButtonOptions{Disabled: strings.TrimSpace(draft.email) == ""}) + if (send.Clicked() || in.Submitted()) && strings.TrimSpace(draft.email) != "" && !send.IsDisabled() { + res.Invited = &ShareInvite{Email: strings.TrimSpace(draft.email), Permission: draft.perm} + draft.email = "" + slog.Debug("mujica: share_dialog invite") + } + }) + ui.Text(c, msg(c, "share_dialog.people")).Font(theme.SerifFont).FontWeight(600) + shareMembers(c, members, &res, &draft.rows) + if opts.Link != "" { + Divider(c, DividerOptions{}) + ui.Row(c).Gap(u * 2).AlignItems(ui.Center).Children(func() { + ui.Icon(c, icons.Must("external-link")).Size(16, 16).Shrink(0).TextColor(Tokens(c).TextMuted) + ui.Text(c, opts.Link).SingleLine().Grow(1).MinWidth(0).Selectable() + b := Button(c, msg(c, "share_dialog.copy"), ButtonOptions{Variant: Secondary, Icon: icons.Must("copy")}) + if b.Clicked() && !b.IsDisabled() { + if opts.OnCopy != nil { + opts.OnCopy(opts.Link) + } else { + c.WriteClipboard(opts.Link) + } + res.Copied = true + c.Announce(msg(c, "share_dialog.copied")) + slog.Debug("mujica: share_dialog copy link") + } + }) + } + }) + }) + return res +} + +// shareMembers virtualizes membership permissions and removal actions. +func shareMembers(c *ui.Context, members *[]ShareMember, res *ShareDialogResult, rows *ui.ListState) { + k := Tokens(c) + u := Density(c).Unit() + remove := -1 + rows.Key = func(i int) any { return (*members)[i].ID } + _, h := c.Size() + ui.List(c, rows, len(*members), func(i int) { + m := &(*members)[i] + if m.ID == "" { + panic("mujica: ShareMember needs an ID") + } + ui.Row(c).Key(m.ID).Gap(u * 2).AlignItems(ui.Center).MinHeight(Density(c).ControlHeight()).Children(func() { + Avatar(c, m.Name, AvatarOptions{Size: 28}) + ui.Column(c).Grow(1).MinWidth(0).Children(func() { + ui.Text(c, m.Name).SingleLine() + if m.Email != "" { + ui.Text(c, m.Email).FontSize(FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted).SingleLine() + } + }) + if m.Owner { + ui.Text(c, msg(c, "share_dialog.owner")).TextColor(k.TextMuted) + return + } + if PermissionSelect(c, &m.Permission, PermissionSelectOptions{Label: m.Name}).Changed() { + res.Changed = m.ID + } + if b := iconAction(c, icons.Must("x"), fmt.Sprintf(msg(c, "share_dialog.remove"), m.Name)); b.Clicked() && !b.IsDisabled() { + remove = i + } + }) + }).Height(min(float32(len(*members))*(Density(c).ControlHeight()+u), max(28, h*0.4))).FillWidth() + if remove >= 0 { + res.Removed = (*members)[remove].ID + *members = append((*members)[:remove:remove], (*members)[remove+1:]...) + slog.Debug("mujica: share_dialog remove") + } +} diff --git a/share_dialog_test.go b/share_dialog_test.go new file mode 100644 index 0000000..dc427a7 --- /dev/null +++ b/share_dialog_test.go @@ -0,0 +1,192 @@ +package mujica + +import ( + "fmt" + "strings" + "testing" + + "github.com/egoist/mygo/ui" +) + +type shareProbe struct { + open bool + members []ShareMember + disabled bool + invites []ShareInvite + changed []string + removed []string + copied int +} + +func newShareProbe() *shareProbe { + return &shareProbe{open: true, members: []ShareMember{ + {ID: "o", Name: "Ada Lovelace", Email: "ada@court.org", Permission: PermissionEdit, Owner: true}, + {ID: "b", Name: "Blaise Pascal", Email: "blaise@court.org", Permission: PermissionView}, + }} +} + +func (p *shareProbe) view(c *ui.Context) { + Use(c, Settings{}) + r := ShareDialog(c, &p.open, &p.members, ShareDialogOptions{Title: "Ledger", Link: "https://court.org/ledger", Disabled: p.disabled}) + if r.Invited != nil { + p.invites = append(p.invites, *r.Invited) + } + if r.Changed != "" { + p.changed = append(p.changed, r.Changed) + } + if r.Removed != "" { + p.removed = append(p.removed, r.Removed) + } + if r.Copied { + p.copied++ + } +} + +func TestShareDialogInvite(t *testing.T) { + p := newShareProbe() + tt := ui.NewTester(p.view, 640, 520) + if !tt.HasText("Share “Ledger”") || !tt.HasText("Owner") { + t.Fatalf("texts %q", tt.Texts()) + } + for !tt.Focused("Email to invite") { + tt.Key(0, ui.KeyTab) + } + tt.Type("carl@court.org") + tt.Key(0, ui.KeyEnter) + if len(p.invites) != 1 || p.invites[0] != (ShareInvite{"carl@court.org", PermissionView}) { + t.Fatalf("invites %v", p.invites) + } +} + +func TestShareDialogChangeRemoveCopy(t *testing.T) { + p := newShareProbe() + tt := ui.NewTester(p.view, 640, 520) + if err := tt.Click("Copy link"); err != nil { + t.Fatal(err) + } + if p.copied != 1 || tt.Clipboard() != "https://court.org/ledger" { + t.Fatalf("copied %d clipboard %q", p.copied, tt.Clipboard()) + } + if err := tt.Click("Remove Blaise Pascal"); err != nil { + t.Fatal(err) + } + if len(p.removed) != 1 || len(p.members) != 1 || tt.HasText("Blaise Pascal") { + t.Fatalf("removed %v members %v", p.removed, p.members) + } + if tt.HasText("Remove Ada Lovelace") { + t.Fatal("owner can be removed") + } +} + +func TestShareDialogPermissionChange(t *testing.T) { + p := newShareProbe() + tt := ui.NewTester(p.view, 640, 520) + // Two "Can view" selects show: the invite's, then Blaise's. + inv, _ := tt.Find("Permission for the invite") + r, _ := tt.Find("Blaise Pascal") + tt.ClickAt(inv.X+inv.W/2, r.Y+r.H/2) + if err := tt.Click("Can comment"); err != nil { + t.Fatal(err) + } + if p.members[1].Permission != PermissionComment || len(p.changed) != 1 || p.changed[0] != "b" { + t.Fatalf("permission %d changed %v", p.members[1].Permission, p.changed) + } +} + +func TestShareDialogDisabled(t *testing.T) { + p := newShareProbe() + p.disabled = true + tt := ui.NewTester(p.view, 640, 520) + _ = tt.Click("Remove Blaise Pascal") + _ = tt.Click("Copy link") + if len(p.members) != 2 || p.copied != 0 { + t.Fatal("disabled dialog acted") + } +} + +func TestShareDialogSnapshot(t *testing.T) { + snapshot(t, "share_dialog", 560, 440, func(c *ui.Context) { + p := ui.Local(c.Root(), "snap", newShareProbe) + ShareDialog(c, &(*p).open, &(*p).members, ShareDialogOptions{Title: "Ledger", Link: "https://court.org/ledger"}) + }) +} + +func TestShareDialogAncestorDisabledCloses(t *testing.T) { + open, disabled := true, false + members := []ShareMember{{ID: "a", Name: "Ada"}} + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Disabled(disabled).Children(func() { + ShareDialog(c, &open, &members, ShareDialogOptions{Title: "Decree"}) + }) + }, 600, 400) + disabled = true + tt.Frame() + if open || tt.HasText("Share “Decree”") { + t.Fatal("disabled dialog stayed open") + } +} + +func TestShareDialogCopyCallback(t *testing.T) { + open := true + members := []ShareMember{} + var copied []string + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ShareDialog(c, &open, &members, ShareDialogOptions{Title: "Ledger", Link: "https://court.org/ledger", OnCopy: func(s string) { copied = append(copied, s) }}) + }, 600, 400) + if err := tt.Click("Copy link"); err != nil { + t.Fatal(err) + } + if len(copied) != 1 || copied[0] != "https://court.org/ledger" || tt.Clipboard() != "" { + t.Fatalf("callback count %d, clipboard %q", len(copied), tt.Clipboard()) + } +} +func TestShareDialogLargeList(t *testing.T) { + open := true + members := make([]ShareMember, 100000) + for i := range members { + members[i] = ShareMember{ID: fmt.Sprint(i), Name: fmt.Sprintf("Member %d", i)} + } + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ShareDialog(c, &open, &members, ShareDialogOptions{Title: "Roster"}) + }, 640, 520) + n := 0 + for _, text := range tt.Texts() { + if strings.HasPrefix(text, "Member ") { + n++ + } + } + if n == 0 || n > 60 { + t.Fatalf("built %d member labels", n) + } +} + +func TestShareDialogSameTitleIndependent(t *testing.T) { + openA, openB := true, false + membersA, membersB := []ShareMember{}, []ShareMember{} + var invited *ShareInvite + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Children(func() { + ShareDialog(c, &openA, &membersA, ShareDialogOptions{Title: "Ledger"}) + r := ShareDialog(c, &openB, &membersB, ShareDialogOptions{Title: "Ledger"}) + if r.Invited != nil { + invited = r.Invited + } + }) + }, 640, 520) + for i := 0; i < 10 && !tt.Focused("Email to invite"); i++ { + tt.Key(0, ui.KeyTab) + } + tt.Type("ada@court.org") + openA, openB = false, true + tt.Frame() + if err := tt.Click("Invite"); err != nil { + t.Fatal(err) + } + if invited != nil { + t.Fatal("second instance inherited invitation draft") + } +} diff --git a/storage_usage.go b/storage_usage.go new file mode 100644 index 0000000..9cfb8da --- /dev/null +++ b/storage_usage.go @@ -0,0 +1,150 @@ +package mujica + +import ( + "fmt" + "log/slog" + "sort" + + "github.com/egoist/mygo/ui" + + "mujicaui/internal" + "mujicaui/theme" +) + +// StorageSegment is one category of used space. +type StorageSegment struct { + Label string + Bytes int64 +} + +// StorageUsageOptions configure a StorageUsage. +type StorageUsageOptions struct { + // Capacity is the total space; required. + Capacity int64 + // Label names the volume. + Label string + // Revision changes when segments are edited in place. + Revision uint64 +} + +type storageUsageState struct { + data *StorageSegment + n int + revision uint64 + capacity, used int64 + ends []int64 + rows ui.ListState +} + +// StorageUsage displays segmented capacity with keyboard readouts and details. +func StorageUsage(c *ui.Context, segments []StorageSegment, opts StorageUsageOptions) *ui.Element { + if opts.Capacity <= 0 { + panic("mujica: StorageUsage needs a positive Capacity") + } + k := Tokens(c) + u := Density(c).Unit() + e := ui.Column(c).Gap(u * 3).Role(ui.RoleGroup).Label(opts.Label) + s := ui.Local(e, "storage_usage.data", func() storageUsageState { return storageUsageState{} }) + var first *StorageSegment + if len(segments) > 0 { + first = &segments[0] + } + if s.data != first || s.n != len(segments) || s.capacity != opts.Capacity || s.revision != opts.Revision { + s.used, s.ends = 0, s.ends[:0] + for _, seg := range segments { + if seg.Bytes < 0 || seg.Bytes > opts.Capacity-s.used { + panic("mujica: StorageUsage segment size out of range") + } + s.used += seg.Bytes + s.ends = append(s.ends, s.used) + } + s.data, s.n, s.capacity, s.revision = first, len(segments), opts.Capacity, opts.Revision + } + used := s.used + pal := flamegraphPalette(k) + pct := func(b int64) float64 { return 100 * float64(b) / float64(opts.Capacity) } + bytes := func(b int64) string { return processListBytes(uint64(b)) } + readout := func(i int) string { + if i < 0 || i >= len(segments) { + return fmt.Sprintf(msg(c, "storage_usage.free"), bytes(opts.Capacity-used), pct(opts.Capacity-used)) + } + return fmt.Sprintf("%s: %s (%.1f%%)", segments[i].Label, bytes(segments[i].Bytes), pct(segments[i].Bytes)) + } + summary := fmt.Sprintf(msg(c, "storage_usage.used"), bytes(used), bytes(opts.Capacity)) + focus := ui.Local(e, "storage_usage.focus", func() int { return 0 }) + *focus = min(*focus, len(segments)) + e.Children(func() { + ui.Row(c).Gap(u * 2).Children(func() { + if opts.Label != "" { + ui.Text(c, opts.Label).Font(theme.SerifFont).FontWeight(600).Grow(1) + } + ui.Text(c, summary).TextColor(k.TextMuted) + }) + bar := ui.Box(c).Height(14).Focusable().Role(ui.RoleMeter).Label(summary).Description(readout(*focus)) + n := len(segments) + 1 + moved := true + switch { + case bar.Shortcut(0, ui.KeyRight) && !bar.IsDisabled(): + *focus = (*focus + 1) % n + case bar.Shortcut(0, ui.KeyLeft) && !bar.IsDisabled(): + *focus = (*focus + n - 1) % n + default: + moved = false + } + if moved { + c.Announce(readout(*focus)) + slog.Debug("mujica: storage_usage focus", "segment", *focus) + } + internal.Ring(bar, k.Focus, theme.ControlRadius) + focused, at := bar.Focused(), *focus + bar.Draw(func(p *ui.Painter, r ui.Rect) { + p.Fill(r, k.Border.Mix(k.Surface, 0.4), 3) + if len(segments) > int(r.W) { + for px := float32(0); px < r.W; px++ { + b := int64(float64(px) / float64(r.W) * float64(opts.Capacity)) + i := sort.Search(len(s.ends), func(i int) bool { return s.ends[i] > b }) + if i == len(segments) { + break + } + p.Fill(ui.Rect{X: internal.Snap(p, r.X+px), Y: r.Y, W: min(1, r.W-px), H: r.H}, pal[i%len(pal)], 0) + } + return + } + x := r.X + for i, s := range segments { + w := internal.Snap(p, r.W*float32(s.Bytes)/float32(opts.Capacity)) + if w <= 0 { + continue + } + seg := ui.Rect{X: x, Y: r.Y, W: max(w-2, 1), H: r.H} + p.Fill(seg, pal[i%len(pal)], 2) + if focused && i == at { + p.Stroke(ui.Rect{X: seg.X - 1, Y: seg.Y - 1, W: seg.W + 2, H: seg.H + 2}, k.Text, 3, 1.5) + } + x += w + } + }) + if focused { + ui.Text(c, readout(at)).FontSize(FontSize(c, theme.CaptionSize)).Role(ui.RoleStatus) + } + ui.List(c, &s.rows, len(segments)+1, func(i int) { + if i == len(segments) { + storageLegend(c, k.Border.Mix(k.Surface, 0.4), msg(c, "storage_usage.freeLabel"), bytes(opts.Capacity-used), pct(opts.Capacity-used)) + return + } + seg := segments[i] + storageLegend(c, pal[i%len(pal)], seg.Label, bytes(seg.Bytes), pct(seg.Bytes)) + }).Height(min(float32(len(segments)+1)*Density(c).ControlHeight(), Density(c).ControlHeight()*6)) + }) + return e +} + +func storageLegend(c *ui.Context, col ui.Color, label, size string, pct float64) { + k := Tokens(c) + ui.Row(c).MinHeight(Density(c).ControlHeight()).Gap(Density(c).Unit() * 2).AlignItems(ui.Center).Children(func() { + ui.Box(c).Size(10, 10).Radius(2).Shrink(0).Background(col).Role(ui.RoleNone) + ui.Text(c, label).Grow(1).MinWidth(0).SingleLine() + ui.Text(c, size).TextColor(k.TextMuted).Shrink(0) + ui.Text(c, fmt.Sprintf("%.1f%%", pct)).TextColor(k.TextMuted).FontFeatures("tnum").Width(52).TextAlign(ui.End).Shrink(0) + }) +} diff --git a/storage_usage_test.go b/storage_usage_test.go new file mode 100644 index 0000000..da1d876 --- /dev/null +++ b/storage_usage_test.go @@ -0,0 +1,102 @@ +package mujica + +import ( + "fmt" + "strings" + "testing" + + "github.com/egoist/mygo/ui" +) + +func storageSample() []StorageSegment { + return []StorageSegment{{"Documents", 24 << 30}, {"Photos", 40 << 30}, {"Apps", 12 << 30}} +} + +func storageTester(segs []StorageSegment, capacity int64) *ui.Tester { + return ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + StorageUsage(c, segs, StorageUsageOptions{Capacity: capacity, Label: "Court disk"}).Width(420) + }, 460, 300) +} + +func TestStorageUsageLegend(t *testing.T) { + tt := storageTester(storageSample(), 128<<30) + for _, s := range []string{"76.0 GB of 128.0 GB used", "Photos", "40.0 GB", "31.2%", "Free", "52.0 GB", "40.6%"} { + if !tt.HasText(s) { + t.Fatalf("missing %q in %q", s, tt.Texts()) + } + } +} + +func TestStorageUsageKeyboardReadout(t *testing.T) { + tt := storageTester(storageSample(), 128<<30) + tt.Key(0, ui.KeyTab) + if !tt.HasText("Documents: 24.0 GB (18.8%)") { + t.Fatalf("focused readout missing: %q", tt.Texts()) + } + tt.Key(0, ui.KeyRight) + tt.Key(0, ui.KeyRight) + tt.Key(0, ui.KeyRight) + if !tt.HasText("Free: 52.0 GB (40.6%)") { + t.Fatalf("readout after three Rights: %q", tt.Texts()) + } + ann := tt.Announcements() + if len(ann) == 0 || ann[len(ann)-1] != "Free: 52.0 GB (40.6%)" { + t.Fatalf("announcements %q", ann) + } + tt.Key(0, ui.KeyRight) + if !tt.HasText("Documents: 24.0 GB (18.8%)") { + t.Fatal("Right did not wrap to the first segment") + } +} + +func TestStorageUsageInvalid(t *testing.T) { + for _, f := range []func(){ + func() { storageTester(storageSample(), 0) }, + func() { storageTester(storageSample(), 10) }, + func() { storageTester([]StorageSegment{{"x", -1}}, 10) }, + } { + func() { + defer func() { + if recover() == nil { + t.Error("invalid storage did not panic") + } + }() + f() + }() + } +} + +func TestStorageUsageSnapshot(t *testing.T) { + snapshot(t, "storage_usage", 460, 280, func(c *ui.Context) { + ui.Column(c).Padding(16).Children(func() { + StorageUsage(c, storageSample(), StorageUsageOptions{Capacity: 128 << 30, Label: "Court disk"}) + }) + }) +} + +func TestStorageUsageOverflow(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("overflowing storage was accepted") + } + }() + storageTester([]StorageSegment{{"a", 1 << 62}, {"b", 1 << 62}}, 1<<62) +} + +func TestStorageUsageLargeLegend(t *testing.T) { + segments := make([]StorageSegment, 100000) + for i := range segments { + segments[i] = StorageSegment{Label: fmt.Sprintf("Category %d", i), Bytes: 1} + } + tt := storageTester(segments, int64(len(segments))) + n := 0 + for _, text := range tt.Texts() { + if strings.HasPrefix(text, "Category ") { + n++ + } + } + if n == 0 || n > 60 { + t.Fatalf("built %d legend labels", n) + } +} diff --git a/terminal.go b/terminal.go index deee353..b9ee8cf 100644 --- a/terminal.go +++ b/terminal.go @@ -223,7 +223,7 @@ func terminalPaint(p *ui.Painter, r ui.Rect, s *TerminalState, k theme.Tokens, f if st.Strike { ly = y + ch/2 } - codeRule(p, x0, x0+float32(end-col)*cw, ly, fg) + internal.Rule(p, x0, x0+float32(end-col)*cw, ly, fg) } col = end } @@ -232,7 +232,7 @@ func terminalPaint(p *ui.Painter, r ui.Rect, s *TerminalState, k theme.Tokens, f continue } mr := ui.Rect{X: ox + float32(m.pos.Col)*cw, Y: y, W: float32(m.n) * cw, H: ch} - codeRule(p, mr.X, mr.X+mr.W, y+ch-1, k.AccentText) + internal.Rule(p, mr.X, mr.X+mr.W, y+ch-1, k.AccentText) if mi == s.current { p.Stroke(mr, k.AccentText, 1, 1.5) } @@ -248,7 +248,7 @@ func terminalPaint(p *ui.Painter, r ui.Rect, s *TerminalState, k theme.Tokens, f if s.preedit != "" { g := terminalShape(s, s.preedit, font) p.Glyphs(g, cr.X, cr.Y+base, k.AccentText) - codeRule(p, cr.X, cr.X+float32(len([]rune(s.preedit)))*cw, cr.Y+ch-1, k.AccentText) + internal.Rule(p, cr.X, cr.X+float32(len([]rune(s.preedit)))*cw, cr.Y+ch-1, k.AccentText) return } if !focused { diff --git a/testdata/snapshots/darwin/archive_viewer-dark.png b/testdata/snapshots/darwin/archive_viewer-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..e542032805d0880045b2df3c1837ee72b2f8713d GIT binary patch literal 28531 zcmdSAWpEtLwk>E7w3xwSW@eVvVrC|bnVD@d%VH*rnVD=cSj^1K&}y-!zw_=p_fEw8 zdT}NurmCa6tJKQMT)Fq!YbQh~DM%v0FJ1pO)&3!Xtl?rihssqjOV9 zERI{$Q&G`zsbL}vig6Ey|H>mXE{Y-%SNfKg&;IH^>DWe&OkRIMW$v;0e!9yJPV%|* zy7Zd3>^{sw7?+^|lAr>hKE2jN0^dHMegb?9{s8!D3&BaO*;dD&czU)nI>bE~%=979$Bo(S` z`C)EIZu{NIcgLxC5imV=P=j2Y-Yn%`{tnZs0)w&%&n^!S=+)`iRBbYabWR25M9Gq8 zEax7UT3iq!=d3{`ry7PGUKMRc$qXPq^eeARhS&&wp38~XtifXQ;GZ@<-S`Wg{0jkd z1C6frNeeHM57jYo&qEHWeAd_80WUu;StctM*XiF+_)8qnn%RMj2_; zD&|eQgcYu3jcG7tl%3$xudLJY@WE#iLUCaKmwM=iVnqVtz4PN7Ri&ks1qpL9m_EzR z*qX>l*?tgu@KnN)SDnLnhu`Iop>Hjl^VA>x$*Dr3WxFy&s5<>+tvFVOE^B->RCNs= z#eAZFX2*c;$MLDdpx%AoxX)RE200hG)%u*YrfY>$->?(YHMPWwloket2L5KAAMkB? zYcxv~hycVD5MyF9;=T8CTPqe@1NdSVZpmlihOW&V#w}QI2a4iGIKly>WWnKAieJ!- zaj{!yo}9b}%2BxOAD{Ra{rx__NF0R(Es%{}f(ZpYNI9u{Sp3(Api!bm{su@RyK`j< zzx|dml|DuVTK2ZqooZGo*>G^sB{R&^!iNCQrA*myFRQ}UE9MOYdcveqNA`1l+s+$$ z?B9n5Wc~a6KD|oDC%^4BQFt@Ojji9cI4sz`eCn%(0Cy_LU$skv3$*lpD2x~Cb3fZK ze62hX9;SZl^W$OAkTBOc?7$DH=kc!AtIkv5_o+e4CcPHINCYd zI@{a&=Fnk$4ujy*@X_xX^&yJ@uk&)4^`_s!fG0LrQ9ddSSEfK2DfPqW&(E(h3#wFB zu6(b?I^K{6h?*p7k)g>_41W5U9`RRBo~v3n2vr9gBRf7l*_+d(FT+E4{f3*((8ez- z+gx11slJ7tmR{KRMa@i|6KLsddDY7LX3JHPSL!*LUwCkUUH{;st*v9e(SEDDGH;jb z7tLpCVg~}RKL;O}ixj*Lt1ySl#YiA%Z-?!~mmnlr%ygTpfyE4R41C|14sC)9bh0wI&7i;vK&JU{U-U=|` z#aEi}OD5j}QJtM!!*keY7i83!?RY@5ZGX@B+Ye9AEDcq4Q+=OGp4%SxtbRt+eH*`7 z+DRCKjmu_PLNZuZQS@l>X?r}0{fSJ9iH=Evu0tUFdYg5OCKg#&NAkQ8lsoypFtg&$ z`??aiJX^G#mY7qvyJrQrU;vkveG`Dbq*e>Fp#!yf+zRfhPVR^bvw-pKCw+k&>& zfxMrDm~R*C*d%TAm?VGiS_P2y8?(wsH>FuLEC<21FU-wTQsuffwjjX4lc0woI*a43 zxNX(SF&WnJgbwX5X+<1Nqb?uxSn~jfJ<=@Z8pBt?L=;rHwbO|NWT(0VYZC(PCFe zXI0m$<>;57^*nAyH}_iRipn$7obSZEPQ4?!6A;r+cr0yJ=ybXKlcI4hJL!07q|+$O zR8#J6Zbpj;hgjQES6x|-j8EKeRHu-|E34rxvite-6A$mjQp4q70=Q#`gluI z^G0G?p<21d03+ldHXtw-g$z9WW`%kEnu7>hyNzJy=U<{G;)n3|+`@(dJvKk@QsTdD z#!Rdfzq_DVyjHuia@C^)S(Glhd~2ILSjlN*_r!v8flQXIMD_KF$Au@WZ%A=u&pD!T zd$y^i$Qit6AdCq6}5VD@5ZI4whu#kW5rU0=0)o6+_?%=*NCA!rRS_T%c zI77UioGdY4e+t#uQhvGGwe;bs_A?3Yt+ zHtL?O-_sbLsrSyjwlNX~DrNqU=Q9uu5u##cLJ0&w{D`5(*VvjNjY#Nti`5QmlGGkj z%U~rBVTe|^@f8I58aJ_RE2&s%i%Zg~Fg&*zX))K=s91L_HoJ0DoXD+p9@f6ZdxeNUC&n%=M2x&F;>7N5UMv%% zW7aa5{|U==n3iv8Zf;`Mz5U3ViVKO5Oy+-{bNv-GyvxFos8-TM8H#85{nS-;OO$9( zf#Zx-NHOvnDZOEFGN}w%{HA&IXxOL)waG;+EqN~W6+6=E1BrCT^+R4i^-NIEW2%|F5t6xX}K-_ydfSeSnP^{uwyc2E~6i_E}| z*HlXKyA-+f`_~0y=qTwZcj7WV-CDzV7v#<+VZr2QVp29;~wwJbWC(~<43-zd8R}(DRwl#hHhPB zW9N?J&&=1@|BgGETcye;`Ab%3Y`^X$cSCoi5uPr9cVeNpe z3yptC`UgG|OgP5Cp;&1J-U21MG5XxKZt(AMw-4!$*7LH3&TYnzD=1F#hy1N7MZHeG zm)ixBimyDTKW!ik$~#Ne{YLtu^7_fz&StXRr3O;@D5O*4ff- zt*^O8E41BwPF*4CUswl*cAey*J{jjJ_QabR|26Ek+zw@K=HlRjR8d_q_gv7m@m7IP z%YZpKEi>JH(D%R`#y+sIJ$68vGrA*^H& z`Z2v9P$H|iA0e`>%&JSw+T5B=(03;8aH7Ou9QEokH=r@avaIgAV!^Z;9ag9jlR|9& z{Y2;2 zu=T~?vV-E{SG_F3^>rDd)Gno8l7bAhMJlun!t--Ttm9o?jUhUgrNYOK8_H%EoEem9WU+>c-KC5hv*f_5{-dk2dS+WS^z+&3 z7M|D}1$2dfVPav@#b+=5cWt?y)#Gp@$hygUV5g-NQ^sGpZc+&aLOoQ2gOF6iu*Vr1 zSX$cJ?^8PDSd$-n@7_RG@>5eHU&I82otU#QCq(Bbz-}BltOC)_EUXlzTSj$6(EMmo zyAY+gP|E4#Ac^tp;ByaW)KC)2zqA~E10p&RYAt<0tGioeN@R`3Ri##(N;yhHU)=Ki zyQU3E8@_BO3V8(-eXr4Xw6nM3^&kbUjO#i~#?@nMYy7>Bi<1gO{QO?VUAMx-#ZCm5-NhjuQXR4%^C22m z6b2~^i+A7OUMP0g7CQ=HxU#Y$J2nO;mZqY&I)56s6*jzt2)rbj{oP0z;|-KWOV`vX z%N)ZCEBW)hkFUU>Pk5Ob7|4WGsZ#9rrbuACr{yQGsk4`rc1Rw1t6W!x5oNs|iw|Ga zNTt8Vt$y#vbFW73aPF4Ii={4yKA+3bu#hYvj}09PJ&pk6#F5e}jCaQ^RN#@Xbw*IO z-C6IW8Xe{7gdjh@4N|NNZ&n*SSR1$OTkDHu>TEO%zt>`v#q#+RNFVkT747o)^X@)X4$~^nl3g6^{hD}C zZ-?k9FZSZ6xGTHV5%fgG$WzL8i@Hmx`mQQ@$WuU>&90X)FuF1ZovM)B@|lO1EI1=0 zGczO8EmN5j&rMpJ_gX*L9To#vv76#*`pX4}iAZ+3dO|{Syw&NHi&gTqvH0HK{%y0x zw%k)C`FRI>+>lv6B1-!Tlp3I1F`wRIzd1hA(8#j*C_h_d=yu01FR!ofA$)}8uRTdk z23&_5mzRh36{;IW{`6xboP|DLin$EdiI*TmMY{55SLl*$iX7FVD;$6Hk!y4ynvE17 zpHNp=IK{d6G-Xa$MUG0eNTouJu5X!;OVBsLW>YBh?VdAk%*x7c7Rh2|p-#&=1jwA0 zr$XaO-Y!Q)je!^?lh1-1-F)xfg)&m67CFJ|r=KOHL)haw4!b@XYLu295us8<@wWB5 zN8Fuj0g_~IWxL@w#vu-M2)UB;Wj`Rs%N?mLVs!+R;~zDtO|GrORO_`{K0Xs4KOTaj zM@&1=i#|v2MKfDRHLJ=ulkT6pQcjj%*IvRXi;Q-I zhJyp71qEW=(<{N4XMZ3ybEcMwiH4-0i3+bv_&p+G05npu;DE^RD==4n$YYP$<mOsbFNov?n-T8oF~EGlgMgK2f&XA83rrs*&vAR5b$Ta+j3)g)JZ9AT z*H~NcyA^8SkAIHWKN4QqGXMF(w+G4ZSMSUGx244s9ML`8!D4Xp`KaL}G$iVGB=%^9 z8e-*v1>p$5UAB205GGC``+{m}x)HBBEuOOEfbqFgKOt_@6s>(JNhHsAyn%2v4i&qh zsytNbi!2KNX!jkJ;Z~tZa2WXc0t`p}{^GgO32t)?{(m~h-Gh5KYjFZe08j73lP$6i zhL;-6T*awt@((kw~BbAA#RdNqi$jmR1+}xqBX*L5j8?ty6en zUF^t_kdNLNNcwY$upeeWLym&aK!jNEs6>yQuFgCLy+kgiZK#xm=QLq6p?zw1hfC-tsLF@XmOY!Cw;yiQ85d( zL$4f*u@9tQ0jQsXAs-Ls-&sg68m4}dkc~cG{O*K~T4Iwapo#g#5jjKi&zJ6xMapfR zx|UK?n+v`T0{az^pDM<=pCJ>F{`=fMJp~Mj1w-H8)>L&p`(OEpHNKsWi(z76b^F|errDQFg!h-Zx)5^awxVqH)no$ zW)k*yti}V(oXU_SWjjn$rGcNC=X+C@Bk0@7?*$o9{;zg#BYe(xSey`;l%y;EVzIZs z|F)d_D}sic-pBm!r#^J$>+4J34tIaH89rCXcjpn{PAfBZ4Ud0kp`XvIr5lv1`( zWXI>DF&>LIIE*Ix#v%k{6>4w3XF?nJ`_x(y2QD0`3xa`BcsJd73wrz<^t>#kbFglYhDSk zczqx1d14NRE?sW<+)h{mHYx`NcZXtyl=U?HyMY^HnrK)GVx&QV#q!xo`hI@9n;uJ2 zI!xLenD@M@Q8f%<-+8-QBC6AHc@CmXDD&Gu#$L-RrseToW&Ut*^6M6l-1a~`g`=aZR9uQUqC*=bsvcAhhLPFZuFgSag1=S?X zin*RYO^UhRuR*h&oXp!SEWEoJi&M+UDO48jH3s#q4whd^=N!&1P}b;XVzTkj2=^S_ zXB#fL|8M_7CE*C1+N>!tn7rIblohjfGb=RKG1pKq*N8nv0Fpr(pjmVH`s!Veap2b7 z+1s}+PvcuCvDB@vTQ+B4%HWyN?M*?}Wl%DETp0pnS2V0`+0xP0uH)>R;yhZSyfziO zEbdlVU8=Hv2rJz>ijYn;d9ORoov<^VEugkvtzQ@@LRv-sUqti|A4#C5q?AE@vbTB4 zKsrsP9rZMrWLD5=;<3W2IaE!$#$TXsDXu{Sw3fL0mrvy2M2Iz*`}Si?FnIk_!`Zq52narM8NANsZ*1i%zLdR?cY?D z`AkBLrBE_%rv5cM< z&PQW2EHv@S2LV$1*qxwwVE#O$Ru5uD#{u@1Q=GVCIYUu0?&iMuT((zP!v42i3SRC;Kfzlw|9_seT!WhQDj z7khVBct)2vp+r#cj2pi_g~={neruqjWA(RZ7i|$Ii_fgM?QvLp+4SYC(_%n%1QTtx z)1qs)*iP?EFmq!2UWaHg*pp&&w#ieu&m27@<1Zop+f%OGT!T?;-g*ZzTX2bhJOzA{ z3;vmnI_Dvg#7k#r^NWiOvbQtVm)!SrTGn&;1Lnd1D`fpw?CN2P5fbps5%w$28qc%9 zYL}SijI366jrnDBr+$tX!a(-lUhrRG{{Q5o_Yek{Z7}IXkST(VI^@LHl{5ao<5zDg zbnv^;<==vp_6q@jNRX2r3+*zpjd&hmIgXB9*|ACTB8>F?x+Q3jRiN z=ZZl?HF)5vGy3tRmwq7}C+@2?1ZfLQaOc$(u3g zf5CQ66g)*9OhkGI$d?2N0*}yy<^=!mv)r2)s58f|p3hL7e#U~`e#YqsOk3j0xg{SV?e1g3i=lEo^obIukVZzP9U7#b?urL-?!}EJS3Kj+kfV;2rP+kX+&b)F6l|*KXgOKc83pTe zAL&>!jy7eGyoBWv?A9(repNlssU?z@k)-r9RaIME-?=(k2Kv(vB5PhgK1@L;5<0RZ z=REuvM&j(Zzm^b7*Y1(M_P3l8G!sTI4T?625YU z)1J8`m{;pN1>{a^osqw_tb$Zx=jU{iY&bTKe4c|JU5;FWmHDX zSDj@y^wX6snQbX7qUe{PO(IY2>3n+jP_%V)`hrKKZEtY8z5jT@4v(Y<5L7Aql>}TxSHObo$h%NfZeB9g(<& z3`iwB92&mIQjFZq?d?6_Mhwg56Lf1VD(S0AQfeDo+h10;-auwaR}bc1eLM!lB$5nh zJX~FuI_UYyD2CIA@H5j&pNj8KZ5F3Ho9#E!P%$XceB>46xtkjeY2Fh?M~rbp1#kC6 zm{E*GWl!7c;mhJE_}R&aHx&!!H_lET&t52P!1OxUIq5lcXi=d_eSWV&bMss_Q$C@u zc^`WGyxPc3OaeULO@e=hb!;uohC#rwap<(GaIO*ploVSO3MH&BuTCcWi99l3e<6C> z`yuH>K&(U~Za`Eva&6xGGLk!av#uu?9N3vvIZ|eY9rXpDGBG41&2jA;i>Egr*Z*cq zR`?w!5ia1~F`t3KQ==#&Eh9Dp5#{Rk6};Ka&dMoohVtphnP{S_su22gj->lH+yd1N zmx`MAY_I}TwD-8=XV{dWiM+*Q5H=358edN@%gej#BWY4ff|k}#3$}C^Oc{qdn%|R) zwp`3YQQEoY*<9PbyQINH0j`S9z)G2_FJJI)v7>U9QiZu#X)n8*7zNz|-k1_7!XyU; zf&Y9!mwS)10z1?8kRfaw8E`F3NUF_O^bzwc>|zh811+i7Egk% z#GFPDB;!mi%w0!2XmbamC3X)}>FhzEF&}TyPp<*aXX$w#gik2w-p4d8m{Tk)>_;ZY z*>Gced|&0)g5OVw4G)~!$P`fsxW{?}A^qYHB6hHHd+H7BgxFQ^k|qC%chHX?+EtZ1 z)>2B}e~*K%ehi@k$qP&tPw;wLZ?L2j)X+H!3A|ogE=hv*va$o-W_v^d**xmNguMy7 zV?e&g2LJN%JO(zwLjf_i-FjBLkjQWzI}7412wS+`4*^&TzW)F$b@=A!>ies`SfOY^ zYgfzhdv52mhB?Fsrosj&o4D~_eY*mlUR%~#A&qk!fBUEU@Hih2u|5(b&M;`L= zsxQv@y;zkM6!cXv_s}39I30FC+}sFv!L8!bBfvLz!1V2GXh3@`}vGE zWUn{|1}$uC->Pa-HUTJ7*RK5K(0v_0RAgkNT>9soy7f2$FXQ*@caeR;E&^nJAu4AJ zbC|^uILMst+hs@4D&HO;{|D)qWeJd%joZSD^lj;ZB5rJeMULzSBF+7-E#vp0UvAm! zeY*zc=1EY8?KyY~UrH7?87%V!dg0AOvi63{5u@h>JMrqKL(J>xre= zZEtIJSi5}j>Fvcfsj@SR;S`~7z<>xhbel&(ztJEmH#0dmvx&#B*fz(S(=7(Vl+QQL z+1XybkM%6+a@5qwo8wsAnWuOejJ0RPzHiBzjFUlH46<3KtnBPu@f43GC_>XG3|*#W zaZvwE>t8hx6N>`smSb>hq8nLG$j0 z{&FTa)qv#K1HUe5DHzRPWCAb4#MSe%3%S8$l5Y>wIubizR7K2ge~d@6~3QLG3d_& zxW^ZXJ2MXRqTzm9?IgrG1RBX2Nqo2 z6@Cok0(T3C+)EOj_Ff1nWqd~^0B9 zj@pkqAK`si%ISXlgD=l`oSER_ZtI3~Wd)$@mq;H!Fnn2sh?aARWl|1`cOX2(A~PtW zkS2GH$f^4b%QP)eNjJl z08~BxD>!#@VS$@9T}wsJ)mV?i?yYVi9U&EG_k#$!FSjO@d7C^Kgk3^4p!-HS#LilI zXZ&c`{g(14&%##b8id`ldFBn?qovyg{o*fJ&YrRPYh*>Z8TCsj7&T$5xMC z1D1HfwWXteaXl_3Xzk;2S=?XMUi|9gu(pPth!>hI+3^_R6=bcS(L*h zvr6tPqxu7|zJuN5ZuARRhXTquRreD-T_0lVmo$XWmA8mjbR=|&5wJ&K?f1^p7V|3a zg`nGXFt{2Nf)@P^iX4}5YC6$A=AV_gzZ3sRf8$SFp!o|hk^ry+)6jaOQO$z`j=qqF zqly8@pwI#VBEcj;)c@e+_9vWeIr4>OyXXN?NEJS$!De-6g@nb42D?z#WB+rT1lJT zXfA~gl-yRjWA!b(e~^1|r)g&(6!0g(Ca5lz3Y`y>aTBo-K5O1 z_ipFo<|mpy%b&Isd6V^iE|e*PCpDem1Ub9JgHWc9b^Hy8KIJ5Tw;1?l3BJ3FL4^{s zBHcRZ)S{{Fp5FL@fbjD6@a)Of*hIN5*!o?HBB3VrSw)x_!NoqcP`7O6yga{}GT9TCK@0ndOWAET}K_9H|5 z(?)R&T)K7}QcTYd1cgMco#XqL40YG`g|D%lsuV<3q>e z?cN@61Al;I@zEy?XnI4S{R4tOow{9D2=gbzAY?EyQPJ`I21`6MDr#B?JH%!C%xEzj zH1|+(wlp%Lz96L61;SfqM?T?`MY7KHU>;Qx{}=f z?mHc4V9#WF6W#4AMKM#{e|V&$lS}w0Q>78NFeT~p4j%8hloP%y;>x}_K8P0P?;%Yb zvB{0;cI&v!eEFoOy9K6p_EH);=TGKbbQEDHyt{T}d^#Sjo@?yNCFWw|sMYSrx$)|0 zZY;VG=HcO?sci-VyxMrF`z3ODZW)Cf6e4RoN)~&w@NqCW2Lg2s<$-{Req)a7-wb&L zeKH$wPm3jN@CeSritob<5-d6rOiWQ=|0fY65&MlEcYqp?(L zp&vg>!fw+$5q*3}qXq>0ZiYKXR%88>4SXN}BAo7S$Zv$AdOue!*2kXtWxaRQ{%Ka? zG9m9f+x0pQ>+TP6dJq`PBy7RU;HXMch}V*hy2o6^B=*IftOPW*=)1C<9`#>*0fv)= zpR{{x2lRyS;w&C;8;SgYRDhhfO|-ACwfW~|TvKvLLH2mdA$$bfxNd9&YvLask83ud z6kRfHl8n$~ph8K-e1>+Oj-VK6;(B&A`mQGN@Nk)pr!9hM16$2XosXfOoSIY~F^~p4 z*F&wtvfrn88Qy*cYXQxc-uS$0%B;93n_7J*ZT3gRKMeUILaS|}f639|#0TmJ`~;5E zI8WWAMBTE4?xs}!>@haB-e9(zw~a`nJ*7 zXpP3=Jm=0k#1X&`0=x8qBp12o*Ip@h-24!`V5bL?15?qUjO@%Fhm(QSlFw#uLexDY zH4Yh#xTKg`807uNlKh0&6@`_bQV&@rLSdARM9TmCN#&vl@89W_9bkV<$zA-%RHg`( zHNDJYdg2bh>p3-*!;qSeH>lAh>l;U8kN@himHK9Bb6dsG;G#GnF8Oxel$tIVT8&K4 z>JR$!H*4i-=#f%D7ip07J*MqS#p!rqJQEd|TO7c;iap0+^dJDig?-aE_hth!nzc_X zYw&XG;@%w9DWLD_=En67s-maVL<15BJSbR7eF(D)oXpILjLaxZedl6h`}H{gwJ0=< zaZK2!?rJCQfd_&^+QX?onK^;GL$2%XASl+ox6WQYiz~ZfvQIqa!K=fbBm&IPlJICVMyx9YD9&o}5;F)bF?2tK+dD-T^sm+%C?I zlfxsFfZl--<(YMMDj++uZoU!>?8Bgh$LY)TsRc!spw_#RPk>hfD{%MKUm4%e|zZyeCRJF<=a%* zIl8eSL3d8vKmt6{6PaE|5e}v=!ZN{z`mn{#PNE6La9v-|vYG9k*V>ph@@;rO&i`|H zd2@GbqW~Sl(jSd*n+!Y6lQ-^-M-s0?`w(E8*X$GRWXj}F%w$7(dDs19XU!O01vntS zNTpmai{SbXv}XXBXb^q~-X%U^ov)cEpnHP=dt1w}@pa*x+Uif87e3JPpsV_cwq!UZJVB*d20JetR=ukIbYHoW^rrYA;wUpfMN#TPV~-+5Wdsawi5 zZhgxtdva|?SvjV19~8FrAkuAWu}HD(B?pC@yM>FhiK1Fxlq{dAj>732PxNz8`2FVd%f$umgWUJ+bgcXevK0E{^3U45 zQada-fZyc`@!UUyL4gO?%w`vOl@Jh=n*^NSYyywZdz3nPs@ z=n;2M6Aho8L34dzI|izA;?+L5K&=MKmVm7v?>~8L6Hzwa6>$!Qr!D6c;vag1*EC&^ zErO?RrhbptbpCGehQ#gEWKHUxXD1I2W}xNr4CN)2!|dXsYDJigOwH4yx4qnodsk6a z?N)#HYZyCQ+%7`F-g!+}C=?Q(sT(RH>lAd1iBgB3w(bT#FY{F{Y zAD$PHKYr6l&j-A}>lRoo7g4GcatV;LJx)jUWlWtnUnG?4wqCKQ(Is13+r50`66k~# zLG|;SDVsm$3MBa*`0bCE$)9(VL%F;RzF8RGOL@T0uU}D7r@yk)w*`I=9Hk=prtS!( zEOyGqC(ch)p)gq!YjZkR-uhxmuO?pf)k&lZ;o_*3R5(d}p*9=<1%NZ9-g~1Cj*9I6 zxXO$4AjCZ6a{0XOv+qmcbyMZ|0oL&<7x~Q>ZHFf(UN1l0dX6oX`gs|(Hp_193Lr2XE4nbFa$`hOPVob5>T z)|RH{9v>fNbp}MyeEc6Jqee^(u6+{u*+d+8B1Kccg997O_X zMb!f^LE!`<{0TYH3CdXtWt!={T-*lr1P4L@1-?qtxyQ*zS zoIiZ%G+Mz-9V|XbxPd2|=GW2DA+oPt_JU><5DFye8y`QDY;Z=1;^X7X8jKU?E8BIH z8Kc`HBR3mZ||y+(G29-u_Zq9O~M0)a{tayaIeO6%JXoCe>wdET5J4NSuG|9mec zwivpGarN7g`@-4eR+L=>KUtxFBwQ;$l7PDCbB95PWPL=y}fIbi@Rr&IA8xs)wiF?Otp0xkr5uR z)jq{5A*gF|b9id#e=Z3bDw!^|(PYAk6hp}sYB6IK4YFvudcb6aWI1&4GA-x4Q!7*? z7qcX5dRe`P@3-C~uCx3Zn%B?0<%>?)BfMV!?|C-1gskOWvLyUd2&Ii1w$LbqQIU8999k zB=P3Ji~I#^Y;0qF8t&XbJ`ka=P3XsAzga!Lpb5M_V9^i8ktwcu(KfklXc0I;9mM#aAyXk$oQIKX*V#>CU@1X1{^(`-svhP=3?VfG# zW65MaDpWd;(^)&SrWX4bvN{TW{(#9IWCZc9I1M`dXCnJ3@&m{!^y-SkwYFPFjh~k( zc0RYa$;7Z|Q3vC9x28I}Daq=3UG9g*CvH1bA8^&QSS5ItVq^Ie@3tYkfc_$1H8p&3 z+j}T8w?~Lqg{)nFWR6^g3PkH0kH~^CvgM#4r36`--%tB>L}an0rI6cL8>S}Jp+Q+~ zaVXR1U>}%6EcA68V^TFLba8T(m3sJnzhY%A5C;%=bUKR)WrYDy#yt1WwzRQnX`$Es zaenTM9x9)~!bHYE!6kGpF1<{AwTYsKMx@ui63;6`+51q+{hA9;Fj;0A;6{}qb@v%h z9UOq-H`}{Al>0U?;#gN{r+qxn_5*-aY(@1 zDx3eiM!(0#ikFP#w=NCl`QruPy4It%g}4iBsM~LR4Z;UAe8H~J6nFQ(&L;z)cYnJG z1yzhKtM|NlkFBqJogTQM*X?<)2<~Wnz!iXc^jdVmYj8m*mUlvn?C;sfLZ!6+a`^3# z({6O2!fL?mtYu7VL8J8(@#(h;$pXR2QPLT_{_BTaCxW5coCdV?6!HoiYh}!A!Y%xx z(~#X(ER#Z+;MY-%$y37dnq2$AmDSjK<}7@a ztmq+(-IFtCa(P9KWE21)fp9H&2Pta z#_Fam{VgXsd31ym4MEgkVUEf0F)rDRY7hm@?=ax>lkVIiZ(9gWB+*jK`j~3l3!2E!+9}?eQ@oTaEoRKf=xD@A%AwHE z2^a`j!{!1QLs9S#zv1M1A`e|h>=I^vHg3aWnQ;!5*zHvD|74OUo%HO@7X?L31_s7E0?c8Twk53jrIKJhVs9AWc`R(d058Mu9wb*DGWo8m(_ArcSma zb7N`F*4(($d88KuZe$`BJPzM!K;s^z%xO(5ua;5w4iBdFt zO7Aou@{m)S_EANH{5qNm9u9KvxfnbarJvpHso>`m-j1wx9>;1<@l%+Q+4I0_Dy?ZKR%`YVPp3(6Pgv;NyZz9WEdUlqg z>vOiZ70B9?C{@Z%mNr70AYdfI(LzwV=c?R`(q!6fJ^%I`{~8cfBAzzF1n1;d@^b_? zGTZ0=axP}IyDKf}y?(|lNuF5~{PFW=zS+q5pPrCi)PbCd@oA2KHz#j<2W&tff_SOM z=T%YokWb>u9^8jd-m$4A{2{+U`|VY6xuywHh4`8ns8*A2ux#maWbuV6R@+hN%!)1Q z0Wr}r{5HmHuF`wH>hGd96?xUMyJrGFoe2rgBQOSQm(>aJ+Ad%}?ST7pj!aCQ z7k6sSqJWugjH%UNtDchk`}?KYh)l>T)zgHlBNJgaW)gPxFQbj7o~?@ZJI#&FO=zh$ z@O^*fK&5KYg)gYw?3_ldReYF)$@83w>ZsvNxa|hCF9urEabSk zx#f91LTI4A|M}e0l*}VWp#o{Ia#fNh4o~$Fk(JFB2W6J5ex2tZIpWEa3{60wSXFqx zQ`YRKW!(#Py+I~3O>{W3q!i6UgD&2&&7pO-1U;t7;n6nt(;P5I z%R}KUZ$I~UeiWh*=m4tJF-7WZ>Wk;Dyo2JtaFZsvFQ-X{*OCg;kJ*Kn58WxNox6I0 zU&#!%*&FJT1K6p@=6*Xm^gCmUce%e53@sE~E939XIt-O;QaN{hH#%A=-QPwyI61N$ z+rJr7Wb?QX8s5ETMSS(1)d>ht{M_EH$JFEccmRHmimdsrSIVA z>(zXoNPBzr(8Uq2re$J!r@WRVH|g8*8VdnV|0IQhZ0$w9FEE^6Xla6L{=cgG>Y%pT zuiLiJlHyRLxI>G(yO-iF1&Uhey5r1YnG z^5g}Bpy2!q*M}Z@a6EqC!!VXMJ31G!Y2S8ZLundX{=7GoEn_RDw!usLi&7>AE6(yl zyH5SMb?b+Usb<^uwtT75T3bB+quO)3HA#yY3*Bz!ctJ13bQ+3BM5aDFdcowbWUgxc zrvef!BTDRck$3NObix(sOGzj-F;E%tnsO?86Z^){2rCU+GA+%`n~qxp1O?+TKv93V z-R*e;^WGR5$FGz`E)fE#4hbR}s;)?owWc?JL8qk__?$=@kw4!g@&!2QRl<)JE=~M(us9l^TXDktDULrm} z{}uF6qKZ=YgbeLb%*TU#s;l|&>3vq&7i3T?Wlfk?AZmmuQY-Yhn zZNbIBK}54$r&6m-8}4Egnon3>z{rR?U@4OzW4=Ntp@-|9SVjBgHVZo}pBL0tKi#zr zzdW6pN`F-j0C#; zA>~IIX$u@_E2I8ZfzjGvdo=&Qb;SSO`S3sMg%Kkc7hz&V<44e|h|8{-a~BqzlRDkSrfkqPdo>KQV}0K2I1$bH5bM zH#}S)v4?}7vu0U-8~jqqPHcvh68bX33`+JW^K6_%EJ3JYi}34*I2BrAY|zGaPq!Cw z(yykZG$n4laKID8yJ;Jrz{A~baJfS^>}onQD6u9tmx{2^=B&|?E5}$*ONgIa`iFdE ze*SRw5r49-B!9=`k@*_GGPkA+d}n`GP+%4xqH}U%e|BhQYx>FFLLz2RfjMQde`rfL zbzx3-6Q6*O=R5+(+?KO>^&L(1cYSQ5AKo;kulRuH-fS_UZc$Yf)9nHl6!-n;#>P=W zibfrVTiPpFu4UjgX|X2i-w>=027NKJX{X|5e|t%o$6@(;Z_l5}H^b|Gqyh}|BB#OI ziyE&XL!S)dLRU~%fStt21LmA}C@IK=|_p>pmFn*+vTzM@Z4SHQgCoE zC|3_WN$Bl$w1~i{uCSHR)zE?YAXX7`9o2iv;Na$$2CoE?7?GXyvxK zpxMU4t-Lkb<zq+EI6owoYUUEcd$|A zzeH@cUCbivuVCBLuR()A3Yspv(BmGRm^e61f;Q#6@^4T5Y0hanN;>SLYi`xc!N>Iy zbYGNT%d~b(VmacPtJ>F$g_@tIT<76R9Rd_TE8(=bJ%$u@o~OQkB1=6zDeDZQ-=S|G zGD!C@*t}RS&bYb9;2yuLS6r))L~@?H^J<63=cK^wWP{f|)ohx@-*9vAQhO_qVqieU zX#tUd$t;>OvX&)$XU{sP&G9W4!|~3eq4e>s=ZO2v#BX_dh3dxo6nS&2K-_-HG-+oy zcl0Y`FKWStx;%wsD$;uPvmBfRM9l16_YvWW(7C!KebV(YyrX|y^uZk}PKl18r<6Y) zCif*Way+*v3zYjkWB1BEGrmKL4)>kw(DMM8#XVDR*vh)9s1kLdM@<9p?18iHs)f?+-$j>pff&wC3{;nB-ktsKG^{@O;S zrt=d8$B$OWFkeXMnP=sW>J!vGEJiZ>hGWUfr`4WP)|9YvmP6$&Y5!;e!P#LStLbjL z$W1MtQA5-2f*WoW1HftOfilOM$%$K+8H3)Xw=f{b{M*XxqB9RiLDlz|*P6c$U4-vo z{BcS0W}@Sv%K5jRql4aGM(ymA)|yt%=yL8^GJmPgeB=mz(8-R8WO-XjJFR61GWO%w z-cpXcf~>#tIEs@5;S(g=BM!{CNmuC5>bQBPT^ctCzXDuOEr*L#BR7b8i?$m%!w2W!EWyPvU*P6 z+gtGNuEi3X#Aet|^*TEOY_|_GWH87Xo|XhzY;L?SP!IXi*2W{*H#~LQn(q@X$Acs3 z-avXgb#HuqY_<6urTp%3BQX57J1q^Xf{xbX8WfR(^Gt18H#LnQ4d zLQdP}4m)c|DBWh>ve8QDx!v9#P%w(*op(90*71mMuNb+je@YP=>RV$r>d=yTL#+L2 z5g+$u(h9W!yvSsfFh_9KxhGUrt1-SMeZJKw`?`jruHK`sQN_-nHMgCWkNX5(yS)Sz zA|WC!8n*$U7vxo91Yz-Ft?VW$b%@a>GF2#}5IuIZoMi@GqC~N)`Alx<_zRS9ef^uW z0FUF%F5`F9CN(iU3mo2=l7*AuX`djlh95r!9~IQi%+7TmeA1ftn+?_~X^BXb=I>{) zSTn^$y=oX01I3q5Sm=G1E-FKYE?inXTnA;AQWDZG{t z_5Hx1J^wbFQ6$W1@K=gdOz9#~ZgQwz7k!2NZ#W~qG)PNprvGT?(h(TsLa@^hR~4vZ zf2a*I*)Ccsijh5-b)GyeE00ZXXdsARnV$WC$Z`i!Qs^k@INIh@NGNXk?UY>5JwHk((pQf3|&rUi^s9P5CM(; zscGIL*>lt5b2O@VrS!}Mlf6C_nvC{ar)?ru08*J4uAFfYxBh9$E^khB1(XCy z(ax!MRSh4^Zn?C6Y_|h;S;}%&Ga%G0=<0q@zF9!xXa;JCB#ji3eM5@)8+AR9PFrKD zB)y8X1PjJp7e1-SCQxwijn5GE6#J}5jTX`|R$m&4^EELYr_QpwF&rj{F#8eq!et5m zs6uRT|4j6Uzla4A-_6*stldVgwzlPLj`i}1n(dW^WUYEPHw=5Krtmx>< zvSJ@_R$=h^#CRGlIqdL21=2D7f;nBR@$ociwHF-nhkwMuZq)w^Vz7;QO-3Q}CWO|| z-hFIu^L5xunY^9Z9O4Z92nqZ}(NYLYwxxs6J)QoKkIQS%&%NPyjh`S(NjsNuC~1oA z37N`e(L+jw(mBzCY>Z?^lKqWb_d3jEq@*zdgmPtPKEXspiq!4oT(!_s_gs4J(Pff|crr*;#(Nw3GG7P(@m3kd! zV`~dS4jLDb2`zxi7OEFN3v{IR6U?fRE&OHA>6j6Nx=4@o{E}fBnacN&HCDN*T_hvP zaMs!$$|aa|9h4~I+S*RjS`}&i<4wNC9gH5kqXwQGFUT3Fqz5MzOneGinEAH*o!_?1z9*^Eg{{ziM|6hiv{P*HF zsD4syDNty31!l!%l=5VWoi)IFRw4bzO0@$AMG~liZ;^0FJhs!~zJzDXvqEzClU+Ej zU${T{kM}Bnjrh)xZe~dHs3mbEe@cF&s>0Ru64W?0u6(p@OB8o-%#;qjfeA5Pq3csQ4Jzxua}P zFZp6Z!m*v}YRSY#R{hob^S1~Var6xIfAjIK&i6gbP0AO^l6?gV#n<8~bLTatWFMO` zIG^KOj05@<+z*6e?2znw8v^$Btk;Rx&Yv4s+|FF?dt}>7f*vycO(#>9L}aA%Z3>Q7 z@+vCGdAM#**R>1KsQ7fY4Tai!mlC8W47EoV>%0h`zR#;~Xz+JmlU$Us@o&#wK-9Lk zKv_*j^89JA#T;ObWlc>VwEQ^GG6>#41_5h@4hU99zi75YJBQ2AYc~_`Levnmo}$Y8 z92r2G8gF3mV0>Lu-=HXze);V2*N|zMjpQdyl)p=-Olybj>StysGb>Mchv-w@TuhrFy-%+ z{}*{$vEz3$;H7t?!Zr?eZF~7&XGjkKU{GSW&+D+#>a-+wU|*i5#Z&3yJrtW$5C1uO z(~|JK0~6C@FfJz|qAIvk22?#iCjfDVZ;b$=!flMe*g=39NTv!o5_ISum(+Nb|A!LhPpKKp{%zb^e>c2w<>4?e`kbGHWH2gfk45ZANsm7uuNN5;0&$`h z5|O$!xBv91$m#^mqTUu#9B{|SY&x+NOn#_+Hunkdt z&-C8Va2UA6q%_V|A3T3Z6abUx;A`K&pA){!R~T4x`UWlhZ4K9|vMVwpYW!k2R1#2n zS;S&Z{_$FQWI`^G!G@L&bUF)@8`Ba1yohtt{Snv#Z+3~L;7h*|IrtoR{pvduqNTaE zFG~Qzk@O=RMlcf*5n($FF3%_yj9Ke90V;HAQwN5IFr4X(MR`!|WW zFODw1Rv5wzwR|evhd>z)(Z;YBfR( zzCxi;OA8CIg+;axOJ#MjfH3nLx0RHz9yz@^D=S4T!`pt8=4P*WgLz)C?%~mvFArb* zC?PFFcWG(q`cv_5kiw;6F(y1z&gOuIEFe^dQm-PVqteKEX+D)m=2q1=P>b`^ijg~f z1dJmYH&As583be9`Cw04!^44bg}kw0iH$jJZJXu*Pvi}wSD;sCFviR+AlQzM0_`Ts z6VQ*^TU1wk7^Nw)v@@f_jb6|_RIU_YR>&?b&NZ>JPj+V(GSS{BiC3?yz2hMtHPUZG`Z#lZG zmI@jwX1edgUXt5Y!Jv*YEO)UaHV&00L0(|H!qwFg;{St^p3#>bn>yq*{uCHI0-RP6 zp08f_7c~WFW@d%Q3poGvBu$ zfGyD-2uq9P2p@!>w9yhW^h(LaA?)?5(uz;fZgLzVtJUyOlc5E-P4$QIR+(*zdZ`)B z%`qxUV!)(E-_l%HD+I!hYq-5Nu>O*sJ|DYqU1CfA;>L^3=*y4OUPD0UX-VHhoxsmM zCg!ib(S{;JO1?5Pm>)aU?%s(N=gy0VYGEX~xJtv|>RQ*v&`gL2!GVX;V1xiafmMYl zPkO%y$)Zls%~0-*Ll^_cF$iJZi{(t4qF^&9_ukjmp3W9_!}O3I8ko-xP}4Mj9nZkd z!OO|Z>-&2V7YFD5cvvX%%e~(2&e*1NsQ8%AJuFjAPrW0RjL?hn67r?^QC9YaLBmlb z2&-o)*?sRK6cy?miA42#isxiWPgPZNF^h%89#AZl+cQtGLeq(-Y%-A~ckV-%>(^Yi zEB{(hA@H#=EjyIe{%p($d{~Ax9(w7nye1UM(!G6Mg1FU(c?pFNa`AF|Iy`1#t*Gc< zPTl?bd0+CDlR2bwGg(V>qK+F-wCJl0#H-E<%y0FM^t2S!-9ySWK)C=>8c_b=o0sw4 zP)ftfsxYwK=)O#TZL({ts~Z$LFtgBO%g4j};ft_`@jTA)J90B6CGMUggY@`eQI`tD z7|oLq^)zOhbJ~grD#PJnxas9JP(4YLyC&Y*KZS27P4RK#!0r8b#?68X3Y<9}!iSB` zC{Q!kd~OGxz=zB^``v;x{QUB(MwPs{&wsbnmp&`Jp&yE~tQ8<+GdxD=AmKYUYP!+% z|6M!n(R@GW11W*}I;xQ^?;NF+#D#tiY(wp_wTkEA(t~-C#Iv#Ua*vHe8|!P0e4$p#L3yP065_UAN^K`oSC3(mCftr_wj z)^<8zmIAWj7B@Z$s*R6Uou*92mFN7u&--1j3&~g*Eog_++V;iM+q1#a@_+h}sgfn% zMMRUcQwyiK5zqKT%sJEKX$c~`!m%jelyqK?Kjy>^-}lBMy%7{7rTTidTFAmOK&coF z_2R$mP2D+pDsi)0LPtPgR$DV_8a@H&Pa90V1`@lE0Y3FeMTArL&72CLF_g>&)Z-?- z=bCy2=tc}GSJV^k_Y6J>{r|KSAwtcGxHMk1q!6U#Ez)rFaJbjiCih#^yI}w`z;*V9 zX!j@stco9~+|v>e5-_<;hwTNlmjOUNHum6CS@6A|-a*`|N5{tpP9&d&VtzThv zf`qvuuDh5DKK>L2M*Om$4`!(1DjIVC`MMJt=t3P|%q6VU#i3?4rmC2$1L_{tmE&7o z@%7hG^H&lskxyGOj@3e7I)$z_)(IC<-t4J)<(F{Lp+S^kKhp1t#|TTg=1jY=8`=CLE>>K3 zjmusIv0S(!@9KUW5OrbQp3pKRJLDy3J&tV5y+hH|li5Vcxks7_Yiu}F>h4zK6LW69 zA4^y#>P5_mt28E0Eqjne=xNnDUwZU}y%28y%+KtxJFhY5+<3IH(x&t6uJfiaf_u6P z9fFdaQ)=ZjoRd~MuL3w=a7=b)Ka3svJ6F}}U8)TfXGU9%7?XeV@{H15Al=i>fkI!T zN$<5vy#%aLzWF=XQh2m(h*q@C<4~P?IhBek>|XOI`o${eD`l+}?g^V-n9i{V`-uzu zMdo28I?qDrHis578i8Bp8!*v7(4HCBqWlo7l4b&BSZr5Y3GbX9QPZu zUBLMd{ChS}g=DH;gQ5ffd&9qDO8MEbDX5CCREGB1$kXl5ET_E z=p*pa;MxCt=uHZx^rdT0^(f<58t?fmO-&WD;ViS+d6nW$1^yRGca#oxyjsZ=OWhB( zYn%5z8QEn@Zu%R{|6b>074$r}=k;Dt$U9K3y$#IGu^UA7N~-)TU)!S<;H76Dbf0Ls zi1RP}zWPFvSP6}a7SG+Z_X#J@ceUaTz%G%2P5UE7i}9Uj?|k2}j`I3hE5EN(e5H;E zTH(P%8rHl3^})aBj+12MRp1sU`&2&T58HIIBe}XHtY+(56_II84~t7CVI7Kf=i&HG zEm&j@mTav3?NT?cqUZMl4mi)Sr^Ge5f3Aci#i59eVF_- zq~_l%7dcfmdLi1HQLxBLKWdv%fROK`h`D6_HWADdlT%)c^LvOENO`@*{5bO>|7Yf& zbowq?H^-(uTU+k*l%~@C(#s4_->2a4TOz}AF7!AR&6#Px)>An^y5Eg9q!?2TkKem% z^EP$C5i&{~8d8KJSh^M$(S?9RqDXFwt≷z48m6c3Ae`ogbbNVMy#pASenj3_L3C z_gcx|n07Hl>+8FLOYCC#VE;#E8pLuOn|Zv!Lc_t$u8P$v!p>FD{*rJ|8F8GOX&mr) zl`)EraS zT|BtxTzIu^hAla+W@4uu5QKf#yCNp&`mr>AvA>kLovY;KY{NWxE zEdFhUPKcy53@V48JfM}5qMg#xlHRSWv3Mg4Bt>hSFN`UxxGAFj{`ucwC?GB7-&5m8 z@as+KyCGv1pxe#lK@7-?zb11WFEM43I2Z@AJ8ERk1d+!ogOxTDWz|uw>U%> zY|fS))DoNZl-+M2p}BD7joy3Jy1CY?DAW!nfR7Dts`-FEJg9~Q`sJIIzWUB5v@9OkE*}H1MKCS zoKyig;_cN$2NTFp&R-g4`w33X0Y=i!kMSokiw0ipBnBTV(*ETpn?mQ_rsm6s>#g>y z^riLQ0lxtu4+>`EYVEWtTib&zLD9|NQT+v7blIuK72mBLYaLxppg-~V;@J^=@cFaV z+|D$)_=9XR!sd;2Hsze=o5qM@08$t=d%vOrJoXO1iHoJDK&v#k?e!@$hWlHy;sc&z zgDzUlElr13&qRIM`n&s2fx$f>gW!|p9fFOFu18kwjb?hv*7`JWq;wE)z0kP$FEYdg zEV)ouh~Q*&ia6~t1A{UpHy4LdyXR9#?@G;j;WkCVP3^|PJ+Wlp!l2pq)iI7FP_P2! zo+`&7gV0bFzGeZ=2{yIte0KI`I2%%+;^NDZp5YAGOXLB?On?9=D8OIOBt4l`aow7E zZ!-BTIOra{(tNzRdo?(YNS1J)KJFWNhO}?@eOe6~+!JuwoXYTd(f`@@0y zxqon)hQ{&SeS3db@Dl0L(o$0d?b4F1CeH_-yo^Twb;qVjNhBT~?mqLgG-I|^0w9n+ z-T$`)*J&M*4_Gbg$pF&7BHpZ#_48_yEvxUtSot;SYPU0B;fGL4S6~)w_Y72>z2$rx zfn8Emv^O`zH0MEyL}hOM?U>VqG)YhDj%-f5Yjj`qRk3D?Cgaq2u8Jd(j-FP&?Zm{y z&hrvY?;~IEK^cr_XJ6EJR#khX%Mch2j>f5g?QWA!T9I%`qgoBl(g_6Df1IpWX z`uged=>}WFv9;CJndj5t%+tqyiO#Nd3-`x7d=3u;0wS6!D%WcT`=H#rFu*Y9N3#PE zAb>>i)|O)%d6y~Rl(&CHR(zw54dO;l=1c_$8Z&74a3&5bfybf1*X7Ga5til;%~7>C z543|pM@G~y2V{M*hqmY)C%VlGvj8d zPn*mHQavG$vCy+4@GgS+VJ)LlOetL~L!qFdR!BSGva+N^pNy;pkaZnyyer@=)|@vu zaw;ilJUeQ;N`LtJdFIwvVt%wYK;RAr$_)$q%S&`ZpI7LgQa$ZRoQMMUq@M;ehmdOF)Or=Hlf06?J5D$&M$)VW1zke%BYmsb|n8QwJgd1zc# zR{lNqy4LD=v*u%aAImQ6)BUc#gs^8w7sjXrI!Go&UZi3)JA3s)pO)#E7ApPTc z$Mp99+yW)201W&7hCRBXcxsB}SH0N1Ur0F_ zvkT2Dw-VyW3q(Mq@ZaQ69AOAdTovGVc5*`Q!YbdZ=`EYkeL||_K>yw#9pe1|wNTvZ e$&*zC#*=(QM~ahC`KiBMY42o|r7I*q2K_HLfdVi9 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/archive_viewer-light.png b/testdata/snapshots/darwin/archive_viewer-light.png new file mode 100644 index 0000000000000000000000000000000000000000..edce394032eb4d7d3e03744d1b2adc5c7b36f5c5 GIT binary patch literal 27577 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 XeddO}f3Jx03V^z@wo)z7I`V%3{tNox literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/file_explorer-light.png b/testdata/snapshots/darwin/file_explorer-light.png new file mode 100644 index 0000000000000000000000000000000000000000..88eaa9020660be82d80239a9036e9b12ab674567 GIT binary patch literal 12838 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 a19XokeJa|T8$@~q;GK+;bcLjG(EkE_&~wrN literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/file_grid-dark.png b/testdata/snapshots/darwin/file_grid-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..86e64481e40ccef65318f54110ebc63a7db581f5 GIT binary patch literal 13536 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/file_icon-dark.png b/testdata/snapshots/darwin/file_icon-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..2f44c3c655b78ccb3947c7d4047c2c0cc8b243b2 GIT binary patch literal 6364 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 F{13Gh;UWM4 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/file_operation_progress-dark.png b/testdata/snapshots/darwin/file_operation_progress-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..85d996f0246f1bf529a3f47ab231662154848351 GIT binary patch literal 22150 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 WQhg|PcE{k)FI~QL&A1wJBkn(13j&w` literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/file_preview-dark.png b/testdata/snapshots/darwin/file_preview-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..84402f91d09ce4e859d3a6af85cca5d5191f317c GIT binary patch literal 18371 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 G0sjZIB_SgK literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/file_preview-light.png b/testdata/snapshots/darwin/file_preview-light.png new file mode 100644 index 0000000000000000000000000000000000000000..9ecc700dc70bb74ca5d9aa3e18b4c2e3f7d482e9 GIT binary patch literal 17809 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/live_indicator-dark.png b/testdata/snapshots/darwin/live_indicator-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..22723159d735128bb2f0f2af9a87c2eccdc46fa9 GIT binary patch literal 11436 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 Z1*omO@;+uquKLe{#e}2%B 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 bvULJz)mS`;1I_mRGvQat+DbJF)=~cfL+@}Q literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/path_bar-dark.png b/testdata/snapshots/darwin/path_bar-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..da59905cb37e8d85a93234ae7595878dd57078ad GIT binary patch literal 6634 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/permission_select-light.png b/testdata/snapshots/darwin/permission_select-light.png new file mode 100644 index 0000000000000000000000000000000000000000..45c5631f298cf59d33a24e19bde8011d1162ed57 GIT binary patch literal 2308 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 O2cWNQq*b9|7yUm%k+}r` literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/presence_avatars-light.png b/testdata/snapshots/darwin/presence_avatars-light.png new file mode 100644 index 0000000000000000000000000000000000000000..2ec02347f201c3c7dbd89a223ec526d6d35f5132 GIT binary patch literal 4586 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/reactions-dark.png b/testdata/snapshots/darwin/reactions-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..8ee0655a8c5274735381c2d036be1a0244f12f12 GIT binary patch literal 4062 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/reactions-light.png b/testdata/snapshots/darwin/reactions-light.png new file mode 100644 index 0000000000000000000000000000000000000000..18490c970b90fcad190aee9d60567f5d696468ca GIT binary patch literal 3924 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 ZvXt}TWtw5W1g3=~E3Ndd0%Q{SzX0dgsD1zd literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/recent_files-light.png b/testdata/snapshots/darwin/recent_files-light.png new file mode 100644 index 0000000000000000000000000000000000000000..a9cd29c7d4888a8a5036aa367334e33bfac7e8ad GIT binary patch literal 27714 zcmd421z23$vpv`lEO>Btr?Ftc3BiK9ySuv+EI@$Z?g4_kI|O%k5ANP*Pv3igc{6XG z%>SGD-po+6oYVbv@2*{E?^>%?g)7KOpdb<;0ssJ%&yu1_007h|N^nzq+p8%m6TGTni@g+ zN`Yn}m5Nm;qfG!+l9nV59RZ#pkqC@TR3N|*4hkPX#SnEAnZ0%aP+7>dXWDf73+(>1 zO|_Lr&Mxi@mJeKWDQ(lMAs z>#1S|A|v(oRP~0|XPA24i3It5Uw*Y3GLj0Qhbn|g!7z|=ur_L=AIejp!#bKrq}P$$ z5<$dOYWm(2g2nv9fjk81fA*C1vvQN2@Sa4g`^jR#lm!Ag3CWpLa|U`^db+iT$H6rV z6)PU{QO`=G49(0jijUw%cX!qi6rc>x)xI;Ybig!?JIM6Yt>joyn835o+F7a;Cr#Jq zyJpjgLh}*J8Rx|?q|z@w5CIUk=M%G%y(!BsI%0AOBfCw%zk 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 IW!?q;4a2uQeVdr_@|6prB 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 brdJ6nwyVsrOMCWlw)wCBPBw z!^z>{+2!Q|TC<^vlH}j9s6Eu?-1XWNlNr9H1;dpv8sBWZU8~lp5wp^JID~-Iu~_YC zm8pL2xFEi_8N8+E1J*4C)09zNf4#4FFqwu4g9QDnW(=RmS=>LgC?!J&({ppG8i~rj z>8r&ApM8A~xHHf&k@x1aOa%2$8?guGX1N+Au3p(yR8?h*RHj3b4#_xX532>xTSa=X z^74S#z-w%bzM?|vAg+&}=ana@0`VZKA0c_Wt)Dw~TQ%YF7>oDqXS|~?NJc~ zuq$2*a)36lORT;+Db?0D{`azT;7|${;X>6lY&R~Kn^;A z4@NI_!nqjAl9Gz5;`w6K&VPAOzsfNcyS4@Rk~^*hs)$Vz;eo-pC@EudvH_oblE2l| zD8LMCcB|l1_QS(2<T9oy?Q;rUeavZ8Mp@LcU| zhQRABDsbu|C2wSh@AR|w-@kdp17yJ3-`~N)en$d|4OX?jc)BTvN?|U!Ad64tVnOe*aQ_gO(Fg>b3igZrjI zA!5^W3rfEC#nOx0LV?u~40I6<4GmsiPFT@V3}eb4;-REe%MY41BbJ(DYz3`<_j&t8 zh=J{EUo>}0`~ET)ibJTzZduQ{85*`lBI-C#aOi?VDPqyQ(w*J$=5&vLb3$D9pF;!y zRP{<3b-R;3O9|!wFyWZKBjxb&vrR>}PWso7%bKgT^=|Iu9pXEa zI2(0F7GvHFrTRC}wpvW}#BQRKWon@|8umMpxzdil-u!;!^fI22xuLq9>%(f4DA;<@ z7wbt$k0Vi;dl_Ocr(E#cG9?PCKGmKxOoV09AJJ>kMohHJUb{XPC#Iz}d-W~BV163O zbr4|(QwntximEKUr~UNi>(jsNmjJ8HcG+7JvX+9D0{VE^IOvE(-wqbCU8i|G4)4~P z+@HR@LgUY#ne>Q&a!D`EL*VMvTRPZ)WY-aR9&D_xX(g#T36rn?1u8AZoB#j- literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/remote_cursor-light.png b/testdata/snapshots/darwin/remote_cursor-light.png new file mode 100644 index 0000000000000000000000000000000000000000..554eaa96f9a63f10f6b8b78f4238345e75e9c483 GIT binary patch literal 7420 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/rename_inline-light.png b/testdata/snapshots/darwin/rename_inline-light.png new file mode 100644 index 0000000000000000000000000000000000000000..9aa29a01a5dbd379164983a1b71de9fec36469f1 GIT binary patch literal 4107 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 z%0D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 K#cM>3g8v7}KMHaH literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/share_dialog-light.png b/testdata/snapshots/darwin/share_dialog-light.png new file mode 100644 index 0000000000000000000000000000000000000000..5f98539d9d12b34146fb61510752aede07a58f81 GIT binary patch literal 33953 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/storage_usage-dark.png b/testdata/snapshots/darwin/storage_usage-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..b52993262ca88ea2d358156e3d1ee7a47e283f96 GIT binary patch literal 20738 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/storage_usage-light.png b/testdata/snapshots/darwin/storage_usage-light.png new file mode 100644 index 0000000000000000000000000000000000000000..a54baddb1fe02988b88f77e4ee8b86b02b08c7e6 GIT binary patch literal 19815 zcmdqJRa6{Nm@Z0!26xv$aCavV+}+*XT@&10f(Lg9?hxGFT^hH>8}HjQa~|$}xNFur zPiIbd^{TF2(zSQ}{(ZZnloh2=5D5{XprBA>q{UUCpgv7PKKKZqAuWcuDko4-3d}O% zqUzo`7ui1gYFg_fN}pkpf0Dz+Huc)RxrtrgvV;rv$=^vqI}iqK zVofXD3eEUThcxR_i;<%vz+{oCjL|^p{AB!&OOBKXJNnuDQ^Wt*O;6&6<%5)wB+V+U z7=aUa0n!AyqP4^;n#$?gt0F0pl{0tU8fMyAa2;897&?DSPF7IW(@=p zq$Hm0H)HLhIol>Bq2>cLmP^W+pWhQGkq6|dv3$(a)6#IBu~BH@hVRWBuN>9SLIwy2 zei!ui<_{by(_+tqpiJ&gmDK8GIg&=7VMcr>Cz%&`?D>Er7d!e5Z5J z=TSZjl_~~#bksD*3HHY+mi2Rd?NTVDg(p7Vo`jhC~NK#Ug7-I2?@p`RJCpV zo`Q;ga?|WGA(^WvMOtt%X&=(}6J2!_2X9&*ArmY4FT*dzvSZ7`Jb{3#xZbWkR#Fsn zv_Q7ru42JJIe%kkV-24ruZ$kI+{3eTpq%zpp3k@RV62! z)&&CjL5Dy`ac5(3H}YM~&8euwlwVs?4CK|DJ|H%PBHvg4!F7-toA_r4nNe`qh z9taM88yFC~>E@v4vH`}QuiVju-we_N%2tFI$_7IfqP}F0QleY?9!`Hy5T`uKbn3chK#Ltm@(W)NUx^-S+c#nxEO6|(@516XNiqB#E(PQ?RIRIrmbYG%K5A# z+Weh4g)9)nDr#kgX9fNqL>RZMykfkE8Z&i!|0s?C3j@1r2M%0ATS-ZNl)G!V8+0&* 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/transfer_queue-dark.png b/testdata/snapshots/darwin/transfer_queue-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..68ffed4ffd8551164b702cb0adfc384e72e46642 GIT binary patch literal 22961 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 WXaOp3TOuty+@-Fft^5vb^Xy+V!-RPN literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/transfer_queue-light.png b/testdata/snapshots/darwin/transfer_queue-light.png new file mode 100644 index 0000000000000000000000000000000000000000..26228f3948024773124784f2eef0e842c0df0688 GIT binary patch literal 22293 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 0 { + first = &items[0] + } + if !s.started || s.data != first || s.n != len(items) || s.revision != opts.Revision { + s.active, s.failed = 0, 0 + ids := make(map[string]bool, len(items)) + for _, t := range items { + if t.ID == "" || ids[t.ID] { + panic("mujica: Transfer needs a unique ID") + } + ids[t.ID] = true + if t.Status < TransferQueued || t.Status > TransferDone || t.Done < 0 || t.Total < 0 || t.Total > 0 && t.Done > t.Total { + panic("mujica: Transfer " + t.ID + " has an invalid status or size") + } + switch t.Status { + case TransferActive, TransferQueued: + s.active++ + case TransferFailed: + s.failed++ + } + } + s.data, s.n, s.revision, s.started = first, len(items), opts.Revision, true + } + d := Density(c) + var res TransferQueueResult + res.Element = codePanel(c).Disabled(opts.Disabled) + disabled := res.Element.IsDisabled() + res.Element.Children(func() { + codeHeader(c, func() { + ui.Text(c, fmt.Sprintf(msg(c, "transfer_queue.summary"), s.active, s.failed)).Role(ui.RoleStatus) + }) + if len(items) == 0 { + codeCaption(c, msg(c, "transfer_queue.empty")).Padding(d.Unit() * 4) + return + } + s.rows.Key = func(i int) any { return items[i].ID } + s.rows.Label = func(i int) string { return items[i].Name } + ui.List(c, &s.rows, len(items), func(i int) { + if a := transferRow(c, items[i]); a != TransferNone && !disabled { + res.Action, res.ID = a, items[i].ID + slog.Debug("mujica: transfer_queue action", "action", a, "id", items[i].ID) + } + }).Grow(1).MinHeight(d.ControlHeight() * 3) + }) + return res +} + +// transferRow builds one transfer and returns the action clicked. +func transferRow(c *ui.Context, t QueuedTransfer) TransferAction { + k := Tokens(c) + d := Density(c) + u := d.Unit() + action := TransferNone + ui.Row(c).MinHeight(d.ControlHeight()+2*u).Padding(u, u*3).Gap(u * 3).AlignItems(ui.Center).Children(func() { + FileIcon(c, t.Name, FileIconOptions{}) + ui.Column(c).Grow(1).MinWidth(0).Gap(u).Children(func() { + ui.Row(c).Gap(u).AlignItems(ui.Center).Children(func() { + dir, key := "arrow-down", "transfer_queue.download" + if t.Upload { + dir, key = "arrow-up", "transfer_queue.upload" + } + ui.Icon(c, icons.Must(dir)).Size(14, 14).Shrink(0).TextColor(k.TextMuted).Label(msg(c, key)).Role(ui.RoleImage) + ui.Text(c, t.Name).SingleLine().MinWidth(0) + }) + frac := float32(0) + if t.Total > 0 { + frac = float32(t.Done) / float32(t.Total) + } + if t.Status == TransferDone { + frac = 1 + } + ui.Box(c).Height(4).Shrink(0).Role(ui.RoleNone).Draw(func(p *ui.Painter, r ui.Rect) { + p.Fill(r, k.Border, 2) + fill := k.AccentText + if t.Status == TransferFailed { + fill = k.Danger + } + if frac > 0 { + p.Fill(ui.Rect{X: internal.Snap(p, r.X), Y: internal.Snap(p, r.Y), W: internal.Snap(p, max(r.W*frac, r.H)), H: r.H}, fill, 2) + } + }) + transferStatusText(c, t, frac) + }) + var acts []TransferAction + switch t.Status { + case TransferActive, TransferQueued: + acts = []TransferAction{TransferPause} + case TransferPaused: + acts = []TransferAction{TransferResume} + case TransferFailed: + acts = []TransferAction{TransferRetry} + } + acts = append(acts, TransferRemove) + for _, a := range acts { + icon, key := transferActionIcon(a) + if iconAction(c, icon, fmt.Sprintf(msg(c, key), t.Name)).Clicked() { + action = a + } + } + }) + return action +} + +func transferStatusText(c *ui.Context, t QueuedTransfer, frac float32) { + k := Tokens(c) + text, col := "", k.TextMuted + switch t.Status { + case TransferQueued: + text = msg(c, "transfer_queue.queued") + case TransferActive: + text = fmt.Sprintf("%.0f%% · %s / %s", frac*100, processListBytes(uint64(t.Done)), processListBytes(uint64(t.Total))) + case TransferPaused: + text = fmt.Sprintf(msg(c, "transfer_queue.paused"), frac*100) + case TransferFailed: + text, col = fmt.Sprintf(msg(c, "transfer_queue.failed"), t.Error), k.Danger + case TransferDone: + text, col = msg(c, "transfer_queue.done"), k.Success + } + ui.Text(c, text).FontSize(FontSize(c, theme.CaptionSize)).TextColor(col).SingleLine() +} + +var transferPauseIcon = ui.MustParseSVG([]byte(``)) +var transferPlayIcon = ui.MustParseSVG([]byte(``)) + +func transferActionIcon(a TransferAction) (*ui.SVG, string) { + switch a { + case TransferPause: + return transferPauseIcon, "transfer_queue.pause" + case TransferResume: + return transferPlayIcon, "transfer_queue.resume" + case TransferRetry: + return icons.Must("refresh-cw"), "transfer_queue.retry" + } + return icons.Must("x"), "transfer_queue.remove" +} diff --git a/transfer_queue_test.go b/transfer_queue_test.go new file mode 100644 index 0000000..a98e9f2 --- /dev/null +++ b/transfer_queue_test.go @@ -0,0 +1,123 @@ +package mujica + +import ( + "fmt" + "strings" + "testing" + + "github.com/egoist/mygo/ui" +) + +func transferSample() []QueuedTransfer { + return []QueuedTransfer{ + {ID: "a", Name: "ledger.zip", Upload: true, Status: TransferActive, Done: 30 << 20, Total: 120 << 20}, + {ID: "b", Name: "crest.png", Status: TransferPaused, Done: 1 << 20, Total: 4 << 20}, + {ID: "c", Name: "deed.pdf", Upload: true, Status: TransferFailed, Done: 0, Total: 2 << 20, Error: "network lost"}, + {ID: "d", Name: "hymn.mp3", Status: TransferDone, Done: 5 << 20, Total: 5 << 20}, + } +} + +type transferProbe struct { + items []QueuedTransfer + s TransferQueueState + disabled bool + got []string +} + +func (p *transferProbe) view(c *ui.Context) { + Use(c, Settings{}) + r := TransferQueue(c, p.items, &p.s, TransferQueueOptions{Disabled: p.disabled}) + r.Element.Height(380) + if r.Action != TransferNone { + p.got = append(p.got, fmt.Sprintf("%d:%s", r.Action, r.ID)) + } +} + +func TestTransferQueueActions(t *testing.T) { + p := &transferProbe{items: transferSample()} + tt := ui.NewTester(p.view, 560, 400) + for _, s := range []string{"1 in progress, 1 failed", "25% · 30.0 MB / 120.0 MB", "Paused at 25%", "Failed: network lost", "Done"} { + if !tt.HasText(s) { + t.Fatalf("missing %q in %q", s, tt.Texts()) + } + } + for _, name := range []string{"Pause ledger.zip", "Resume crest.png", "Retry deed.pdf", "Remove hymn.mp3"} { + if err := tt.Click(name); err != nil { + t.Fatal(err) + } + } + want := "1:a 2:b 3:c 4:d" + if strings.Join(p.got, " ") != want { + t.Fatalf("actions %v, want %s", p.got, want) + } + if tt.HasText("Pause hymn.mp3") || tt.HasText("Retry ledger.zip") { + t.Fatal("actions shown for the wrong status") + } +} + +func TestTransferQueueKeyboardAndDisabled(t *testing.T) { + p := &transferProbe{items: transferSample()} + tt := ui.NewTester(p.view, 560, 400) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyTab) + tt.Key(0, ui.KeyEnter) + if len(p.got) != 1 { + t.Fatalf("keyboard actions %v", p.got) + } + p = &transferProbe{items: transferSample(), disabled: true} + tt = ui.NewTester(p.view, 560, 400) + _ = tt.Click("Pause ledger.zip") + if len(p.got) != 0 { + t.Fatal("disabled queue acted") + } +} + +func TestTransferQueueEmptyAndInvalid(t *testing.T) { + tt := ui.NewTester((&transferProbe{}).view, 560, 400) + if !tt.HasText("No transfers") { + t.Fatalf("texts %q", tt.Texts()) + } + defer func() { + if recover() == nil { + t.Fatal("Done above Total did not panic") + } + }() + ui.NewTester((&transferProbe{items: []QueuedTransfer{{ID: "x", Done: 9, Total: 1}}}).view, 560, 400) +} + +func TestTransferQueueSnapshot(t *testing.T) { + snapshot(t, "transfer_queue", 520, 330, func(c *ui.Context) { + p := ui.Local(c.Root(), "snap", func() *transferProbe { return &transferProbe{items: transferSample()} }) + ui.Column(c).Padding(12).Children(func() { + TransferQueue(c, (*p).items, &(*p).s, TransferQueueOptions{}).Element.Height(306) + }) + }) +} + +func TestTransferQueueLargeAndRevision(t *testing.T) { + items := make([]QueuedTransfer, 100000) + for i := range items { + items[i] = QueuedTransfer{ID: fmt.Sprint(i), Name: fmt.Sprintf("f-%d.txt", i), Status: TransferActive, Total: 100} + } + s := TransferQueueState{} + revision := uint64(0) + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + TransferQueue(c, items, &s, TransferQueueOptions{Revision: revision}).Element.Height(220) + }, 500, 250) + n := 0 + for _, text := range tt.Texts() { + if strings.HasPrefix(text, "f-") { + n++ + } + } + if n == 0 || n > 60 { + t.Fatalf("built %d transfers", n) + } + items[0].Status = TransferFailed + revision++ + tt.Frame() + if !tt.HasText("99999 in progress, 1 failed") { + t.Fatalf("revised summary %d active %d failed", s.active, s.failed) + } +}