From ec26a339958e01981367dc79fb9574d83527ab5a 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 14:49:07 -0400 Subject: [PATCH 1/2] WIP motion 1 --- bars_loader.go | 23 +++ blink_highlight.go | 81 ++++++++ cost_breakdown.go | 152 +++++++++++++++ count_up.go | 65 +++++++ dots_loader.go | 80 ++++++++ eval_result_table.go | 115 +++++++++++ interrupt_button.go | 61 ++++++ layout_transition.go | 41 ++++ loaders_test.go | 115 +++++++++++ messages_motion.go | 55 ++++++ orbit_loader.go | 26 +++ presence.go | 71 +++++++ pulse_loader.go | 21 ++ reorder_transition.go | 43 ++++ shimmer.go | 68 +++++++ shimmer_text.go | 43 ++++ stagger.go | 71 +++++++ .../snapshots/darwin/bars_loader-dark.png | Bin 0 -> 3286 bytes .../snapshots/darwin/bars_loader-light.png | Bin 0 -> 3167 bytes .../snapshots/darwin/dots_loader-dark.png | Bin 0 -> 4246 bytes .../snapshots/darwin/dots_loader-light.png | Bin 0 -> 4166 bytes .../snapshots/darwin/orbit_loader-dark.png | Bin 0 -> 4008 bytes .../snapshots/darwin/orbit_loader-light.png | Bin 0 -> 3788 bytes .../snapshots/darwin/pulse_loader-dark.png | Bin 0 -> 3809 bytes .../snapshots/darwin/pulse_loader-light.png | Bin 0 -> 3740 bytes .../snapshots/darwin/wave_loader-dark.png | Bin 0 -> 5486 bytes .../snapshots/darwin/wave_loader-light.png | Bin 0 -> 5371 bytes token_usage_chart.go | 65 +++++++ tool_registry_panel.go | 124 ++++++++++++ trace_viewer.go | 184 ++++++++++++++++++ typewriter.go | 86 ++++++++ wave_loader.go | 30 +++ 32 files changed, 1620 insertions(+) create mode 100644 bars_loader.go create mode 100644 blink_highlight.go create mode 100644 cost_breakdown.go create mode 100644 count_up.go create mode 100644 dots_loader.go create mode 100644 eval_result_table.go create mode 100644 interrupt_button.go create mode 100644 layout_transition.go create mode 100644 loaders_test.go create mode 100644 messages_motion.go create mode 100644 orbit_loader.go create mode 100644 presence.go create mode 100644 pulse_loader.go create mode 100644 reorder_transition.go create mode 100644 shimmer.go create mode 100644 shimmer_text.go create mode 100644 stagger.go create mode 100644 testdata/snapshots/darwin/bars_loader-dark.png create mode 100644 testdata/snapshots/darwin/bars_loader-light.png create mode 100644 testdata/snapshots/darwin/dots_loader-dark.png create mode 100644 testdata/snapshots/darwin/dots_loader-light.png create mode 100644 testdata/snapshots/darwin/orbit_loader-dark.png create mode 100644 testdata/snapshots/darwin/orbit_loader-light.png create mode 100644 testdata/snapshots/darwin/pulse_loader-dark.png create mode 100644 testdata/snapshots/darwin/pulse_loader-light.png create mode 100644 testdata/snapshots/darwin/wave_loader-dark.png create mode 100644 testdata/snapshots/darwin/wave_loader-light.png create mode 100644 token_usage_chart.go create mode 100644 tool_registry_panel.go create mode 100644 trace_viewer.go create mode 100644 typewriter.go create mode 100644 wave_loader.go diff --git a/bars_loader.go b/bars_loader.go new file mode 100644 index 0000000..fbdab80 --- /dev/null +++ b/bars_loader.go @@ -0,0 +1,23 @@ +package mujica + +import ( + "github.com/egoist/mygo/ui" + + "mujicaui/internal" +) + +// BarsLoader shows four bars rising and falling in turn. +func BarsLoader(c *ui.Context, opts LoaderOptions) *ui.Element { + return loaderMark(c, opts, 1.25, 1000, func(p *ui.Painter, r ui.Rect, t float64, col ui.Color, still bool) { + w := r.W / 7 + for i := range 4 { + h := float32(0.6) + if !still { + h = 0.3 + 0.7*loaderWave(t-float64(i)/8) + } + bh := internal.Snap(p, r.H*h) + x := r.X + float32(i)*w*2 + p.Fill(ui.Rect{X: x, Y: internal.Snap(p, r.Y+(r.H-bh)/2), W: w, H: bh}, col, 1) + } + }) +} diff --git a/blink_highlight.go b/blink_highlight.go new file mode 100644 index 0000000..31b351b --- /dev/null +++ b/blink_highlight.go @@ -0,0 +1,81 @@ +package mujica + +import ( + "log/slog" + "math" + "strconv" + "time" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" + "mujicaui/theme" +) + +// BlinkHighlightOptions configure a BlinkHighlight. +type BlinkHighlightOptions struct { + // Format writes the value; nil uses two decimals. + Format func(v float64) string + // Duration of the fade; zero means 800 ms. + Duration time.Duration +} + +type blinkState struct { + value float64 + dir int + at time.Time + set bool +} + +// BlinkHighlight shows value and tints it once when it changes, up in Success, down in Danger. +// An arrow marks the last direction, and stays as the only mark under reduced motion. +func BlinkHighlight(c *ui.Context, value float64, opts BlinkHighlightOptions) *ui.Element { + if math.IsNaN(value) || math.IsInf(value, 0) || opts.Duration < 0 { + panic("mujica: BlinkHighlight needs a finite value and a non-negative Duration") + } + d := opts.Duration + if d == 0 { + d = 800 * time.Millisecond + } + format := opts.Format + if format == nil { + format = func(v float64) string { return strconv.FormatFloat(v, 'f', 2, 64) } + } + k := Tokens(c) + u := Density(c).Unit() + row := ui.Row(c).AlignItems(ui.Center).Gap(u).Padding(0, u*1.5).Radius(theme.ControlRadius).Shrink(0) + st := ui.Local(row, "blink", func() blinkState { return blinkState{} }) + now := c.Now() + if st.set && value != st.value { + st.dir = 1 + if value < st.value { + st.dir = -1 + } + st.at = now + slog.Debug("mujica: blink highlight", "from", st.value, "to", value) + } + st.value, st.set = value, true + col := k.Success + icon, word := "arrow-up", "blink_highlight.up" + if st.dir < 0 { + col, icon, word = k.Danger, "arrow-down", "blink_highlight.down" + } + name := format(value) + if st.dir != 0 { + name += ", " + msg(c, word) + } + row.Label(name) + if dur := Motion(c, d); st.dir != 0 && dur > 0 { + if p := float32(now.Sub(st.at)) / float32(dur); p < 1 { + row.Background(col.Alpha(0.22 * (1 - p))) + c.AnimationFrame() + } + } + row.Children(func() { + ui.Text(c, format(value)).FontFeatures("tnum").SingleLine() + if st.dir != 0 { + ui.Icon(c, icons.Must(icon)).Size(12, 12).TextColor(col) + } + }) + return row +} diff --git a/cost_breakdown.go b/cost_breakdown.go new file mode 100644 index 0000000..b86296c --- /dev/null +++ b/cost_breakdown.go @@ -0,0 +1,152 @@ +package mujica + +import ( + "cmp" + "fmt" + "log/slog" + "math" + "strconv" + + "github.com/egoist/mygo/ui" + + "mujicaui/theme" +) + +// CostItem is the usage and cost of one call. +type CostItem struct { + Model, Tool, Step string + InputTokens int + OutputTokens int + Cost float64 +} + +// CostGroup chooses what a CostBreakdown sums by. +type CostGroup int + +const ( + CostByModel CostGroup = iota + CostByTool + CostByStep +) + +// CostBreakdownOptions configure a CostBreakdown. +type CostBreakdownOptions struct { + GroupBy CostGroup + // Currency precedes amounts; empty means "$". + Currency string + // Decimals of amounts; zero means 2. + Decimals int +} + +// CostBreakdownState holds the table's sort and selection and the sums. +type CostBreakdownState struct { + Table DataTableState[string] + rows []costRow + total costRow + src *CostItem + n int + group CostGroup + built bool +} + +type costRow struct { + name string + calls int + input, output int + cost float64 +} + +// CostBreakdown sums costs by model, tool or step in a sortable table with a total; give it a height. +func CostBreakdown(c *ui.Context, s *CostBreakdownState, items []CostItem, opts CostBreakdownOptions) DataTableView { + if opts.GroupBy < CostByModel || opts.GroupBy > CostByStep || opts.Decimals < 0 { + panic("mujica: CostBreakdown needs a known GroupBy and non-negative Decimals") + } + cur, dec := opts.Currency, opts.Decimals + if cur == "" { + cur = "$" + } + if dec == 0 { + dec = 2 + } + var first *CostItem + if len(items) > 0 { + first = &items[0] + } + if !s.built || s.src != first || s.n != len(items) || s.group != opts.GroupBy { + s.rows, s.total = costSum(items, opts.GroupBy, msg(c, "cost_breakdown.unnamed")) + s.src, s.n, s.group, s.built = first, len(items), opts.GroupBy, true + slog.Debug("mujica: cost breakdown sum", "items", len(items), "groups", len(s.rows)) + } + money := func(v float64) string { return cur + strconv.FormatFloat(v, 'f', dec, 64) } + count := func(n int) string { return strconv.Itoa(n) } + share := func(r costRow) float64 { + if s.total.cost == 0 { + return 0 + } + return r.cost / s.total.cost + } + k := Tokens(c) + u := Density(c).Unit() + groupTitle := msg(c, []string{"cost_breakdown.model", "cost_breakdown.tool", "cost_breakdown.step"}[opts.GroupBy]) + cols := []DataColumn[costRow]{ + {ID: "name", Title: groupTitle, Sortable: true, Compare: func(a, b costRow) int { return cmp.Compare(a.name, b.name) }, Text: func(r costRow) string { return r.name }}, + {ID: "calls", Title: msg(c, "cost_breakdown.calls"), Width: 80, Align: ui.End, Sortable: true, Compare: func(a, b costRow) int { return cmp.Compare(a.calls, b.calls) }, Text: func(r costRow) string { return count(r.calls) }}, + {ID: "input", Title: msg(c, "cost_breakdown.input"), Width: 110, Align: ui.End, Sortable: true, Compare: func(a, b costRow) int { return cmp.Compare(a.input, b.input) }, Text: func(r costRow) string { return count(r.input) }}, + {ID: "output", Title: msg(c, "cost_breakdown.output"), Width: 110, Align: ui.End, Sortable: true, Compare: func(a, b costRow) int { return cmp.Compare(a.output, b.output) }, Text: func(r costRow) string { return count(r.output) }}, + {ID: "cost", Title: msg(c, "cost_breakdown.cost"), Width: 160, Sortable: true, Compare: func(a, b costRow) int { return cmp.Compare(a.cost, b.cost) }, + Text: func(r costRow) string { return money(r.cost) }, + Cell: func(r costRow) { + ui.Row(c).Grow(1).Gap(u * 2).AlignItems(ui.Center).Children(func() { + f := share(r) + ui.Box(c).Height(6).Grow(1).Radius(1).Background(k.Border).Role(ui.RoleNone).Draw(func(p *ui.Painter, b ui.Rect) { + p.Fill(ui.Rect{X: b.X, Y: b.Y, W: b.W * float32(f), H: b.H}, k.AccentText, 1) + }) + ui.Text(c, money(r.cost)).FontFeatures("tnum").Shrink(0) + ui.Text(c, fmt.Sprintf("%.0f%%", 100*f)).FontFeatures("tnum").TextColor(k.TextMuted).FontSize(FontSize(c, theme.CaptionSize)).Width(36).TextAlign(ui.End).Shrink(0) + }) + }}, + } + var v DataTableView + col := ui.Column(c).AlignItems(ui.Stretch).Gap(u * 2) + col.Children(func() { + v = DataTable(c, &s.Table, s.rows, DataTableOptions[costRow, string]{Columns: cols, Key: func(r costRow) string { return r.name }, Empty: msg(c, "cost_breakdown.empty")}) + v.Element.Grow(1) + ui.Row(c).AlignItems(ui.Center).Gap(u*4).Padding(u*2, u*3).Background(k.Background).Radius(theme.ControlRadius).Role(ui.RoleStatus). + Label(fmt.Sprintf(msg(c, "cost_breakdown.total_name"), money(s.total.cost), s.total.calls)).Children(func() { + ui.Text(c, msg(c, "cost_breakdown.total")).Font(theme.SerifFont).FontWeight(600).Grow(1) + ui.Text(c, fmt.Sprintf(msg(c, "cost_breakdown.tokens"), s.total.input, s.total.output)).FontFeatures("tnum").TextColor(k.TextMuted) + ui.Text(c, money(s.total.cost)).FontWeight(700).FontFeatures("tnum") + }) + }) + v.Element = col + return v +} + +// costSum totals items per group, in order of first appearance. +func costSum(items []CostItem, by CostGroup, unnamed string) ([]costRow, costRow) { + var rows []costRow + at := map[string]int{} + var total costRow + for _, it := range items { + if it.InputTokens < 0 || it.OutputTokens < 0 || it.Cost < 0 || math.IsNaN(it.Cost) || math.IsInf(it.Cost, 0) { + panic("mujica: CostItem needs non-negative, finite counts and cost") + } + name := []string{it.Model, it.Tool, it.Step}[by] + if name == "" { + name = unnamed + } + i, ok := at[name] + if !ok { + i = len(rows) + at[name] = i + rows = append(rows, costRow{name: name}) + } + for _, r := range []*costRow{&rows[i], &total} { + r.calls++ + r.input += it.InputTokens + r.output += it.OutputTokens + r.cost += it.Cost + } + } + return rows, total +} diff --git a/count_up.go b/count_up.go new file mode 100644 index 0000000..71b75be --- /dev/null +++ b/count_up.go @@ -0,0 +1,65 @@ +package mujica + +import ( + "log/slog" + "math" + "strconv" + "time" + + "github.com/egoist/mygo/ui" +) + +// CountUpOptions configure a CountUp. +type CountUpOptions struct { + // Duration of a change; zero means 600 ms. + Duration time.Duration + // Decimals shown; Format overrides it. + Decimals int + // Format writes the shown value; nil uses Decimals. + Format func(v float64) string +} + +type countUpState struct { + from, to float64 + start time.Time + set bool +} + +// CountUp counts from the last value to value; reduced motion shows it at once. +func CountUp(c *ui.Context, value float64, opts CountUpOptions) *ui.Element { + if math.IsNaN(value) || math.IsInf(value, 0) { + panic("mujica: CountUp value must be finite") + } + if opts.Duration < 0 || opts.Decimals < 0 { + panic("mujica: CountUp Duration and Decimals must not be negative") + } + d := opts.Duration + if d == 0 { + d = 600 * time.Millisecond + } + format := opts.Format + if format == nil { + format = func(v float64) string { return strconv.FormatFloat(v, 'f', opts.Decimals, 64) } + } + dur := Motion(c, d) + now := c.Now() + row := ui.Row(c).Shrink(0).Label(format(value)) + st := ui.Local(row, "count", func() countUpState { return countUpState{from: value, to: value} }) + at := func() float64 { + if dur == 0 { + return st.to + } + p := min(1, float64(now.Sub(st.start))/float64(dur)) + return st.from + (st.to-st.from)*float64(ui.EaseOut(float32(p))) + } + if value != st.to { + from := at() + slog.Debug("mujica: count up", "from", from, "to", value) + st.from, st.to, st.start = from, value, now + } + shown := at() + if shown != st.to { + c.AnimationFrame() + } + return row.Children(func() { ui.Text(c, format(shown)).FontFeatures("tnum").SingleLine() }) +} diff --git a/dots_loader.go b/dots_loader.go new file mode 100644 index 0000000..b1cbb8a --- /dev/null +++ b/dots_loader.go @@ -0,0 +1,80 @@ +package mujica + +import ( + "math" + + "github.com/egoist/mygo/ui" + + "mujicaui/theme" +) + +// LoaderOptions configure the loaders: DotsLoader, BarsLoader, PulseLoader, OrbitLoader and WaveLoader. +type LoaderOptions struct { + // Size is the mark's height in DIPs; zero means 16. + Size float32 + // Label is read out, and shown with ShowLabel; empty means "Loading". + Label string + ShowLabel bool + // Color paints the mark; zero uses AccentText. + Color ui.Color +} + +// loaderDraw paints a loader at phase t in [0, 1); t is 0 and still is set under reduced motion. +type loaderDraw func(p *ui.Painter, r ui.Rect, t float64, col ui.Color, still bool) + +// loaderMark builds a loader of aspect w:1 that repaints only while painted and moving. +func loaderMark(c *ui.Context, opts LoaderOptions, aspect float32, period int64, draw loaderDraw) *ui.Element { + if !(opts.Size >= 0) || math.IsInf(float64(opts.Size), 1) { + panic("mujica: loader Size must be finite and not negative") + } + k := Tokens(c) + size := opts.Size + if size == 0 { + size = 16 + } + label := opts.Label + if label == "" { + label = msg(c, "loading") + } + col := opts.Color + if col.A == 0 { + col = k.AccentText + } + still := c.Preferences().ReduceMotion + row := ui.Row(c).Gap(Density(c).Unit() * 2).AlignItems(ui.Center).Shrink(0).Role(ui.RoleProgress).Label(label) + row.Children(func() { + ui.Box(c).Key(still).Size(size*aspect, size).Shrink(0).Role(ui.RoleNone).Draw(func(p *ui.Painter, r ui.Rect) { + t := 0.0 + if !still { + t = float64(p.Now().UnixMilli()%period) / float64(period) + p.AnimationFrame() + } + draw(p, r, t, col, still) + }) + // Reduced motion shows the words, as the mark stands still. + if opts.ShowLabel || still { + ui.Text(c, label).TextColor(k.TextMuted).FontSize(FontSize(c, theme.CaptionSize)).SingleLine() + } + }) + return row +} + +// loaderWave is a smooth 0..1 rise and fall over phase t. +func loaderWave(t float64) float32 { return float32(0.5 - 0.5*math.Cos(2*math.Pi*t)) } + +// DotsLoader shows three dots lighting up in turn. +func DotsLoader(c *ui.Context, opts LoaderOptions) *ui.Element { + return loaderMark(c, opts, 2.5, 1200, func(p *ui.Painter, r ui.Rect, t float64, col ui.Color, still bool) { + d := r.H * 0.36 + for i := range 3 { + a := float32(1) + if !still { + a = 0.3 + 0.7*loaderWave(t-float64(i)/6) + } + var dot ui.Path + cx := r.X + r.W*(float32(i)+0.5)/3 + dot.Circle(cx, r.Y+r.H/2, d/2) + p.FillPath(&dot, col.Alpha(a)) + } + }) +} diff --git a/eval_result_table.go b/eval_result_table.go new file mode 100644 index 0000000..915e0c7 --- /dev/null +++ b/eval_result_table.go @@ -0,0 +1,115 @@ +package mujica + +import ( + "cmp" + "fmt" + "log/slog" + "math" + "strconv" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" +) + +// EvalCase is one evaluation case and its outcome. +type EvalCase struct { + ID string + Name string + Score float64 + Passed bool +} + +// EvalResultTableOptions configure an EvalResultTable. +type EvalResultTableOptions struct { + // Decimals of scores; zero means 2. + Decimals int + Status DataStatus + Error string +} + +// EvalResultTableResult is a built EvalResultTable and its events. +type EvalResultTableResult struct { + Table DataTableView + diff string +} + +// Diff returns the case whose differences the user asked to see this frame. +func (r EvalResultTableResult) Diff() (string, bool) { return r.diff, r.diff != "" } + +// EvalResultTable lists eval cases with score and pass or fail, sortable, with a pass summary; give it a height. +func EvalResultTable(c *ui.Context, s *DataTableState[string], cases []EvalCase, opts EvalResultTableOptions) EvalResultTableResult { + if opts.Decimals < 0 { + panic("mujica: EvalResultTable Decimals is negative") + } + dec := opts.Decimals + if dec == 0 { + dec = 2 + } + passed := 0 + for _, ec := range cases { + if ec.ID == "" || math.IsNaN(ec.Score) || math.IsInf(ec.Score, 0) { + panic("mujica: EvalCase needs an ID and a finite Score") + } + if ec.Passed { + passed++ + } + } + k := Tokens(c) + u := Density(c).Unit() + var res EvalResultTableResult + verdict := func(ec EvalCase) string { + if ec.Passed { + return msg(c, "eval_result_table.pass") + } + return msg(c, "eval_result_table.fail") + } + cols := []DataColumn[EvalCase]{ + {ID: "name", Title: msg(c, "eval_result_table.case"), Sortable: true, Compare: func(a, b EvalCase) int { return cmp.Compare(a.Name, b.Name) }, Text: func(e EvalCase) string { return e.Name }}, + {ID: "score", Title: msg(c, "eval_result_table.score"), Width: 90, Align: ui.End, Sortable: true, Compare: func(a, b EvalCase) int { return cmp.Compare(a.Score, b.Score) }, + Text: func(e EvalCase) string { return strconv.FormatFloat(e.Score, 'f', dec, 64) }}, + {ID: "result", Title: msg(c, "eval_result_table.result"), Width: 110, Sortable: true, + Compare: func(a, b EvalCase) int { return cmp.Compare(evalRank(a), evalRank(b)) }, Text: verdict, + Cell: func(e EvalCase) { + icon, col := "circle-check", k.Success + if !e.Passed { + icon, col = "circle-x", k.Danger + } + ui.Row(c).Gap(u).AlignItems(ui.Center).TextColor(col).Children(func() { + ui.Icon(c, icons.Must(icon)).Size(14, 14) + ui.Text(c, verdict(e)).FontWeight(600) + }) + }}, + {ID: "diff", Title: "", Width: 48, Text: func(EvalCase) string { return "" }, + Cell: func(e EvalCase) { + b := iconAction(c, icons.Must("arrow-left-right"), fmt.Sprintf(msg(c, "eval_result_table.diff"), e.Name)).Tooltip(msg(c, "eval_result_table.view")) + if b.Clicked() && !b.IsDisabled() { + res.diff = e.ID + slog.Debug("mujica: eval result diff", "case", e.ID) + } + }}, + } + col := ui.Column(c).AlignItems(ui.Stretch).Gap(u * 2) + col.Children(func() { + if len(cases) > 0 { + ui.Row(c).Gap(u * 3).AlignItems(ui.Center).Role(ui.RoleStatus).Children(func() { + ui.Text(c, fmt.Sprintf(msg(c, "eval_result_table.summary"), passed, len(cases))).FontWeight(600) + ui.Box(c).Height(6).Width(160).Radius(1).Background(k.Danger.Mix(k.Surface, 0.6)).Role(ui.RoleNone).Draw(func(p *ui.Painter, r ui.Rect) { + p.Fill(ui.Rect{X: r.X, Y: r.Y, W: r.W * float32(passed) / float32(len(cases)), H: r.H}, k.Success, 1) + }) + }) + } + res.Table = DataTable(c, s, cases, DataTableOptions[EvalCase, string]{Columns: cols, Key: func(e EvalCase) string { return e.ID }, + Empty: msg(c, "eval_result_table.empty"), Status: opts.Status, Error: opts.Error}) + res.Table.Element.Grow(1) + }) + res.Table.Element = col + return res +} + +func evalRank(e EvalCase) int { + if e.Passed { + return 1 + } + return 0 +} diff --git a/interrupt_button.go b/interrupt_button.go new file mode 100644 index 0000000..fea58a9 --- /dev/null +++ b/interrupt_button.go @@ -0,0 +1,61 @@ +package mujica + +import ( + "log/slog" + + "github.com/egoist/mygo/ui" + + "mujicaui/icons" +) + +// InterruptButtonOptions configure an InterruptButton. +type InterruptButtonOptions struct { + // Confirm asks before interrupting. + Confirm bool + // Interrupting shows the stop in progress until the run ends. + Interrupting bool + // Label replaces the built-in "Stop". + Label string +} + +// InterruptButton stops a running agent; Changed reports the interrupt, confirmed when asked. +func InterruptButton(c *ui.Context, running bool, opts InterruptButtonOptions) ChoiceResult { + if opts.Interrupting && !running { + panic("mujica: InterruptButton cannot be Interrupting while not running") + } + label := opts.Label + if label == "" { + label = msg(c, "interrupt_button.stop") + } + shown := label + if opts.Interrupting { + shown = msg(c, "interrupt_button.stopping") + } + live := running && !opts.Interrupting + var res ChoiceResult + wrap := ui.Row(c).Shrink(0) + res.Element = wrap + wrap.Children(func() { + b := Button(c, shown, ButtonOptions{Variant: Danger, Icon: icons.Must("circle-x"), Loading: opts.Interrupting, Disabled: !running}) + open := ui.Local(wrap, "confirm", func() bool { return false }) + if !live || b.IsDisabled() { + *open = false + } + clicked := b.Clicked() && live + switch { + case clicked && opts.Confirm: + *open = true + case clicked: + res.changed = true + } + r := Popconfirm(c, b, open, PopconfirmOptions{Title: msg(c, "interrupt_button.confirm"), + Description: msg(c, "interrupt_button.detail"), ConfirmLabel: label, CancelLabel: msg(c, "interrupt_button.keep"), Danger: true}) + if r.Confirmed && live { + res.changed = true + } + }) + if res.changed { + slog.Debug("mujica: interrupt button interrupt") + } + return res +} diff --git a/layout_transition.go b/layout_transition.go new file mode 100644 index 0000000..3567d07 --- /dev/null +++ b/layout_transition.go @@ -0,0 +1,41 @@ +package mujica + +import ( + "time" + + "github.com/egoist/mygo/ui" +) + +// LayoutTransitionOptions configure a LayoutTransition. +type LayoutTransitionOptions struct { + // Duration of a change; zero means 200 ms. + Duration time.Duration + // Row lays the content in a row instead of a column. + Row bool + // Clip hides content outside the box while it resizes. + Clip bool +} + +// LayoutTransition holds content whose size and place animate from where they were (FLIP). +// Siblings that the change moves need a LayoutTransition of their own to move in step. +func LayoutTransition(c *ui.Context, opts LayoutTransitionOptions, content func()) *ui.Element { + if opts.Duration < 0 { + panic("mujica: LayoutTransition Duration is negative") + } + d := opts.Duration + if d == 0 { + d = 200 * time.Millisecond + } + box := ui.Column(c).AlignItems(ui.Stretch) + if opts.Row { + box.Row() + } + if opts.Clip { + box.Clip() + } + box.Transition(ui.ElementTransition{Duration: d, Position: true, Size: true}) + if content != nil { + box.Children(content) + } + return box +} diff --git a/loaders_test.go b/loaders_test.go new file mode 100644 index 0000000..0761011 --- /dev/null +++ b/loaders_test.go @@ -0,0 +1,115 @@ +package mujica + +import ( + "bytes" + "testing" + "time" + + "github.com/egoist/mygo/ui" +) + +// motionFrames builds view and returns its tester and a counter of built frames. +func motionFrames(view func(c *ui.Context), w, h int) (*ui.Tester, *int) { + n := new(int) + tt := ui.NewTester(func(c *ui.Context) { + *n++ + Use(c, Settings{}) + view(c) + }, w, h) + return tt, n +} + +// motionBusy reports whether one more frame keeps asking for frames. +func motionBusy(tt *ui.Tester, n *int) bool { + before := *n + tt.Frame() + return *n-before > 2 +} + +// motionCount counts texts and names equal to s. +func motionCount(tt *ui.Tester, s string) int { + n := 0 + for _, x := range tt.Texts() { + if x == s { + n++ + } + } + return n +} + +var loaderCases = map[string]func(c *ui.Context, o LoaderOptions) *ui.Element{ + "dots": DotsLoader, "bars": BarsLoader, "pulse": PulseLoader, "orbit": OrbitLoader, "wave": WaveLoader, +} + +func TestLoadersMoveStopAndLabel(t *testing.T) { + for name, loader := range loaderCases { + t.Run(name, func(t *testing.T) { + show := true + tt, n := motionFrames(func(c *ui.Context) { + if show { + loader(c, LoaderOptions{Size: 32, Label: "Thinking"}) + } + }, 200, 80) + if _, ok := tt.Find("Thinking"); !ok { + t.Fatal("loader not named") + } + if motionCount(tt, "Thinking") != 1 { + t.Fatal("label shown without ShowLabel") + } + a := encode(t, tt.Image()) + time.Sleep(120 * time.Millisecond) + tt.Frame() + if bytes.Equal(a, encode(t, tt.Image())) || !motionBusy(tt, n) { + t.Fatal("loader does not move") + } + tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1}) + if motionBusy(tt, n) || motionCount(tt, "Thinking") != 2 { + t.Fatalf("reduced motion: still repainting or no static label: %q", tt.Texts()) + } + a = encode(t, tt.Image()) + time.Sleep(120 * time.Millisecond) + tt.Frame() + if !bytes.Equal(a, encode(t, tt.Image())) { + t.Fatal("loader moved under reduced motion") + } + tt.SetPreferences(ui.Preferences{TextScale: 1}) + show = false + tt.Frame() + if motionBusy(tt, n) { + t.Fatal("hidden loader keeps repainting") + } + }) + } +} + +func TestLoadersInvalidSize(t *testing.T) { + for name, loader := range loaderCases { + func() { + defer func() { + if recover() == nil { + t.Errorf("%s: negative size did not panic", name) + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + loader(c, LoaderOptions{Size: -1}) + }, 100, 50, 1) + }() + } +} + +func loaderSnapshot(t *testing.T, name string, loader func(c *ui.Context, o LoaderOptions) *ui.Element) { + snapshot(t, name, 480, 90, func(c *ui.Context) { + ui.Row(c).Padding(20).Gap(24).AlignItems(ui.Center).Children(func() { + loader(c, LoaderOptions{}) + loader(c, LoaderOptions{Size: 28, Label: "Thinking", ShowLabel: true}) + loader(c, LoaderOptions{Size: 20, Color: Tokens(c).Info}) + }) + }) +} + +func TestDotsLoaderSnapshot(t *testing.T) { loaderSnapshot(t, "dots_loader", DotsLoader) } +func TestBarsLoaderSnapshot(t *testing.T) { loaderSnapshot(t, "bars_loader", BarsLoader) } +func TestPulseLoaderSnapshot(t *testing.T) { loaderSnapshot(t, "pulse_loader", PulseLoader) } +func TestOrbitLoaderSnapshot(t *testing.T) { loaderSnapshot(t, "orbit_loader", OrbitLoader) } +func TestWaveLoaderSnapshot(t *testing.T) { loaderSnapshot(t, "wave_loader", WaveLoader) } diff --git a/messages_motion.go b/messages_motion.go new file mode 100644 index 0000000..cc4cc5a --- /dev/null +++ b/messages_motion.go @@ -0,0 +1,55 @@ +package mujica + +func init() { + addMessages(map[string][2]string{ + "interrupt_button.stop": {"Stop", "停止"}, + "interrupt_button.stopping": {"Stopping", "正在停止"}, + "interrupt_button.confirm": {"Stop the agent?", "停止智能体?"}, + "interrupt_button.detail": {"Work in progress will be lost.", "进行中的工作将会丢失。"}, + "interrupt_button.keep": {"Keep running", "继续运行"}, + + "token_usage_chart.label": {"Token usage", "Token 用量"}, + "token_usage_chart.input": {"Input", "输入"}, + "token_usage_chart.output": {"Output", "输出"}, + + "cost_breakdown.model": {"Model", "模型"}, + "cost_breakdown.tool": {"Tool", "工具"}, + "cost_breakdown.step": {"Step", "步骤"}, + "cost_breakdown.calls": {"Calls", "调用"}, + "cost_breakdown.input": {"Input tokens", "输入 token"}, + "cost_breakdown.output": {"Output tokens", "输出 token"}, + "cost_breakdown.cost": {"Cost", "费用"}, + "cost_breakdown.empty": {"No costs yet", "暂无费用"}, + "cost_breakdown.unnamed": {"(unnamed)", "(未命名)"}, + "cost_breakdown.total": {"Total", "合计"}, + "cost_breakdown.total_name": {"Total %s over %d calls", "合计 %s,共 %d 次调用"}, + "cost_breakdown.tokens": {"%d in · %d out", "输入 %d · 输出 %d"}, + + "trace_viewer.span": {"Span", "跨度"}, + "trace_viewer.duration": {"Duration", "耗时"}, + "trace_viewer.timeline": {"Timeline", "时间轴"}, + "trace_viewer.start": {"Start", "开始"}, + "trace_viewer.failed": {"failed", "失败"}, + "trace_viewer.empty": {"No spans", "没有跨度"}, + + "eval_result_table.case": {"Case", "用例"}, + "eval_result_table.score": {"Score", "得分"}, + "eval_result_table.result": {"Result", "结果"}, + "eval_result_table.pass": {"Pass", "通过"}, + "eval_result_table.fail": {"Fail", "失败"}, + "eval_result_table.diff": {"View differences for %s", "查看 %s 的差异"}, + "eval_result_table.view": {"View differences", "查看差异"}, + "eval_result_table.summary": {"%d of %d passed", "通过 %d / %d"}, + "eval_result_table.empty": {"No eval cases", "没有评测用例"}, + + "tool_registry_panel.empty": {"No tools", "没有工具"}, + "tool_registry_panel.choose": {"Choose a tool to see its parameters", "选择工具查看参数"}, + "tool_registry_panel.no_params": {"No parameters", "无参数"}, + "tool_registry_panel.required": {"required", "必填"}, + + "typewriter.skip": {"Skip", "跳过"}, + + "blink_highlight.up": {"up", "上升"}, + "blink_highlight.down": {"down", "下降"}, + }) +} diff --git a/orbit_loader.go b/orbit_loader.go new file mode 100644 index 0000000..6a32103 --- /dev/null +++ b/orbit_loader.go @@ -0,0 +1,26 @@ +package mujica + +import ( + "math" + + "github.com/egoist/mygo/ui" + + "mujicaui/internal" +) + +// OrbitLoader moves a diamond around a ring of small diamonds, in Spinner's language. +func OrbitLoader(c *ui.Context, opts LoaderOptions) *ui.Element { + return loaderMark(c, opts, 1, 1600, func(p *ui.Painter, r ui.Rect, t float64, col ui.Color, still bool) { + cx, cy, rad := r.X+r.W/2, r.Y+r.H/2, r.W*0.36 + at := func(turn float64) (float32, float32) { + a := 2*math.Pi*turn - math.Pi/2 + return internal.Snap(p, cx+rad*float32(math.Cos(a))), internal.Snap(p, cy+rad*float32(math.Sin(a))) + } + for i := range 8 { + x, y := at(float64(i) / 8) + p.FillPath(internal.Diamond(x, y, r.W*0.12), col.Alpha(0.3)) + } + x, y := at(t) + p.FillPath(internal.Diamond(x, y, internal.Snap(p, r.W*0.28)), col) + }) +} diff --git a/presence.go b/presence.go new file mode 100644 index 0000000..2eacd5a --- /dev/null +++ b/presence.go @@ -0,0 +1,71 @@ +package mujica + +import ( + "log/slog" + "time" + + "github.com/egoist/mygo/ui" + + "mujicaui/theme" +) + +// PresenceKind is how content comes and goes. +type PresenceKind int + +const ( + // PresenceFade fades in and out. + PresenceFade PresenceKind = iota + // PresenceSlide fades while moving up into place and down out. + PresenceSlide + // PresenceCollapse grows open and shrinks shut along the parent's direction. + PresenceCollapse +) + +// PresenceOptions configure a Presence. +type PresenceOptions struct { + Kind PresenceKind + // Duration is PopupDuration when zero. + Duration time.Duration +} + +// presenceMotion is where content of kind comes from and goes to. +func presenceMotion(kind PresenceKind) *ui.Motion { + switch kind { + case PresenceFade: + return &ui.Motion{} + case PresenceSlide: + return &ui.Motion{Y: 8} + case PresenceCollapse: + return &ui.Motion{Collapse: true, Opacity: 1} + } + panic("mujica: unknown PresenceKind") +} + +type presenceState struct{ shown bool } + +// Presence builds content while show is true; it enters and exits with motion, removed once the exit ends. +func Presence(c *ui.Context, show bool, opts PresenceOptions, content func()) *ui.Element { + m := presenceMotion(opts.Kind) + if opts.Duration < 0 { + panic("mujica: Presence Duration is negative") + } + d := opts.Duration + if d == 0 { + d = theme.PopupDuration + } + holder := ui.Column(c).AlignItems(ui.Stretch).Shrink(0) + st := ui.Local(holder, "presence", func() presenceState { return presenceState{shown: show} }) + if st.shown != show { + st.shown = show + slog.Debug("mujica: presence", "show", show) + } + holder.Children(func() { + if !show { + return + } + box := ui.Column(c).AlignItems(ui.Stretch).Key("presence") + box.Transition(ui.ElementTransition{Duration: d, Enter: m, Exit: m}) + box.Children(content) + }) + return holder +} diff --git a/pulse_loader.go b/pulse_loader.go new file mode 100644 index 0000000..a500bcf --- /dev/null +++ b/pulse_loader.go @@ -0,0 +1,21 @@ +package mujica + +import ( + "github.com/egoist/mygo/ui" + + "mujicaui/internal" +) + +// PulseLoader shows one dot breathing between 60% and full size; never a halo. +func PulseLoader(c *ui.Context, opts LoaderOptions) *ui.Element { + return loaderMark(c, opts, 1, 1600, func(p *ui.Painter, r ui.Rect, t float64, col ui.Color, still bool) { + s, a := float32(1), float32(1) + if !still { + w := loaderWave(t) + s, a = 0.6+0.4*w, 0.55+0.45*w + } + var dot ui.Path + dot.Circle(r.X+r.W/2, r.Y+r.H/2, internal.Snap(p, r.H*0.4*s)) + p.FillPath(&dot, col.Alpha(a)) + }) +} diff --git a/reorder_transition.go b/reorder_transition.go new file mode 100644 index 0000000..9b27221 --- /dev/null +++ b/reorder_transition.go @@ -0,0 +1,43 @@ +package mujica + +import ( + "fmt" + "time" + + "github.com/egoist/mygo/ui" +) + +// ReorderTransitionOptions configure a ReorderTransition. +type ReorderTransitionOptions struct { + // Duration of a move; zero means 200 ms. + Duration time.Duration + // Gap between items in DIPs. + Gap float32 +} + +// ReorderTransition lays out keyed items in a column; when their order changes they slide to their new places. +// Items added fade in and items removed fade out. +func ReorderTransition[K comparable](c *ui.Context, keys []K, opts ReorderTransitionOptions, item func(key K)) *ui.Element { + if opts.Duration < 0 || opts.Gap < 0 { + panic("mujica: ReorderTransition Duration and Gap must not be negative") + } + d := opts.Duration + if d == 0 { + d = 200 * time.Millisecond + } + seen := make(map[K]bool, len(keys)) + for _, k := range keys { + if seen[k] { + panic(fmt.Sprintf("mujica: ReorderTransition duplicate key %v", k)) + } + seen[k] = true + } + move := ui.ElementTransition{Duration: d, Position: true, Enter: &ui.Motion{}, Exit: &ui.Motion{}} + col := ui.Column(c).AlignItems(ui.Stretch).Gap(opts.Gap) + col.Children(func() { + for _, k := range keys { + ui.Column(c).Key(k).AlignItems(ui.Stretch).Transition(move).Children(func() { item(k) }) + } + }) + return col +} diff --git a/shimmer.go b/shimmer.go new file mode 100644 index 0000000..b4df863 --- /dev/null +++ b/shimmer.go @@ -0,0 +1,68 @@ +package mujica + +import ( + "math" + + "github.com/egoist/mygo/ui" + + "mujicaui/internal" + "mujicaui/theme" +) + +// ShimmerOptions configure a Shimmer placeholder. +type ShimmerOptions struct { + // Width fills the parent when zero. + Width float32 + // Height is ControlHeight when zero. + Height float32 + // Label is read out while loading; empty means "Loading". + Label string +} + +// Shimmer shows a sweeping placeholder while loading, then builds content instead. +func Shimmer(c *ui.Context, loading bool, opts ShimmerOptions, content func()) *ui.Element { + if !(opts.Width >= 0) || !(opts.Height >= 0) || math.IsInf(float64(opts.Width+opts.Height), 1) { + panic("mujica: Shimmer size must be finite and not negative") + } + if !loading { + box := ui.Column(c).AlignItems(ui.Stretch) + if content != nil { + box.Children(content) + } + return box + } + k := Tokens(c) + h := opts.Height + if h == 0 { + h = Density(c).ControlHeight() + } + label := opts.Label + if label == "" { + label = msg(c, "loading") + } + fill := k.SurfaceHover + if k.Dark { + fill = k.Border + } + still := c.Preferences().ReduceMotion + e := ui.Box(c).Key(still).Height(h).Role(ui.RoleProgress).Label(label) + if opts.Width > 0 { + e.Width(opts.Width) + } else { + e.FillWidth() + } + e.Draw(func(p *ui.Painter, r ui.Rect) { + p.Fill(r, fill, theme.CardRadius) + if still { + return + } + p.AnimationFrame() + t := float32(p.Now().UnixMilli()%1600) / 1600 + band := max(r.W*0.25, 24) + x := internal.Snap(p, r.X-band+(r.W+band)*t) + p.Clip(r, theme.CardRadius, func() { + p.Fill(ui.Rect{X: x, Y: r.Y, W: band, H: r.H}, k.Surface.Alpha(0.55), 0) + }) + }) + return e +} diff --git a/shimmer_text.go b/shimmer_text.go new file mode 100644 index 0000000..1b7b9bd --- /dev/null +++ b/shimmer_text.go @@ -0,0 +1,43 @@ +package mujica + +import ( + "github.com/egoist/mygo/ui" +) + +// ShimmerTextOptions configure a ShimmerText. +type ShimmerTextOptions struct { + // Active sweeps the light; false shows plain muted text. + Active bool +} + +// ShimmerText shows a status such as "Thinking" with light sweeping across its letters. +func ShimmerText(c *ui.Context, text string, opts ShimmerTextOptions) *ui.Element { + if text == "" { + panic("mujica: ShimmerText needs text") + } + k := Tokens(c) + still := c.Preferences().ReduceMotion + if !opts.Active || still { + e := ui.Text(c, text).TextColor(k.TextMuted) + if opts.Active { + e.Role(ui.RoleStatus) + } + return e + } + runes := []rune(text) + size := c.Theme().FontSize + // The text keeps its layout and name; the sweep is painted over it. + e := ui.Text(c, text).TextColor(ui.Transparent).Role(ui.RoleStatus).SingleLine() + e.DrawOver(func(p *ui.Painter, r ui.Rect) { + p.AnimationFrame() + t := float32(p.Now().UnixMilli()%1800) / 1800 + at := t*float32(len(runes)+6) - 3 + spans := make([]ui.Span, len(runes)) + for i, ch := range runes { + d := max(0, 1-abs32(float32(i)-at)/3) + spans[i] = ui.Span{Text: string(ch), Size: size, Color: k.TextMuted.Mix(k.Text, d)} + } + p.RichText(r.X, r.Y, 0, spans...) + }) + return e +} diff --git a/stagger.go b/stagger.go new file mode 100644 index 0000000..1b39ab1 --- /dev/null +++ b/stagger.go @@ -0,0 +1,71 @@ +package mujica + +import ( + "log/slog" + "time" + + "github.com/egoist/mygo/ui" + + "mujicaui/theme" +) + +// StaggerOptions configure a Stagger. +type StaggerOptions struct { + // Interval between items; zero means 60 ms. + Interval time.Duration + // Duration of each item's entry; zero means PopupDuration. + Duration time.Duration + // Horizontal lays items in a row. + Horizontal bool +} + +type staggerState struct { + born time.Time + shown int + done bool +} + +// Stagger builds n items, each entering after the one before, once when it first appears. +// Items added after the sequence ends appear with their own entry. +func Stagger(c *ui.Context, n int, opts StaggerOptions, item func(i int)) *ui.Element { + if n < 0 || opts.Interval < 0 || opts.Duration < 0 { + panic("mujica: Stagger needs non-negative n, Interval and Duration") + } + every, d := opts.Interval, opts.Duration + if every == 0 { + every = 60 * time.Millisecond + } + if d == 0 { + d = theme.PopupDuration + } + box := ui.Column(c).AlignItems(ui.Stretch) + if opts.Horizontal { + box.Row().AlignItems(ui.Start) + } + st := ui.Local(box, "stagger", func() *staggerState { return &staggerState{born: c.Now()} }) + s := *st + upto := n + if !s.done && !c.Preferences().ReduceMotion { + upto = min(n, 1+int(c.Now().Sub(s.born)/every)) + if upto < n { + c.After(s.born.Add(time.Duration(upto) * every).Sub(c.Now())) + } else { + s.done = true + slog.Debug("mujica: stagger done", "items", n) + } + } + if c.Preferences().ReduceMotion { + s.done = true + } + enter := &ui.Motion{Y: 8} + if opts.Horizontal { + enter = &ui.Motion{X: 8} + } + box.Children(func() { + for i := range upto { + ui.Column(c).Key(i).AlignItems(ui.Stretch). + Transition(ui.ElementTransition{Duration: d, Enter: enter}).Children(func() { item(i) }) + } + }) + return box +} diff --git a/testdata/snapshots/darwin/bars_loader-dark.png b/testdata/snapshots/darwin/bars_loader-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..96707b3136bbc61215f8a3e535db997e5635b1ad GIT binary patch literal 3286 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

MKO8?`D3UFbU86`aSAdmrl2&ArZ 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 B-uwUn literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/dots_loader-light.png b/testdata/snapshots/darwin/dots_loader-light.png new file mode 100644 index 0000000000000000000000000000000000000000..c2c3c1351f7048837db60c45f59cae722307ce18 GIT binary patch literal 4166 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/orbit_loader-dark.png b/testdata/snapshots/darwin/orbit_loader-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..f80e8765a812d69764af9e4224f31536dde8dc27 GIT binary patch literal 4008 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 P0tXoCn&?zRonrn0Eb$y6 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/orbit_loader-light.png b/testdata/snapshots/darwin/orbit_loader-light.png new file mode 100644 index 0000000000000000000000000000000000000000..fdf0dcea11d882ce29e0b60fee1026ab0aeb60ed GIT binary patch literal 3788 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 Cu9f=$ literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/pulse_loader-dark.png b/testdata/snapshots/darwin/pulse_loader-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..715cc24bbf692730b06c1c22c907ce94b6d7f6ad GIT binary patch literal 3809 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 SwZPHDBS1$3u3n+~H0(cu$n@m^ literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/pulse_loader-light.png b/testdata/snapshots/darwin/pulse_loader-light.png new file mode 100644 index 0000000000000000000000000000000000000000..c971922ef90f5f3aa43ed58e2225e131bc97d6c5 GIT binary patch literal 3740 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 IN48=A12)qsqW}N^ literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/wave_loader-light.png b/testdata/snapshots/darwin/wave_loader-light.png new file mode 100644 index 0000000000000000000000000000000000000000..143110b9f71f844b1a2a2f2b96d240651d7da71a GIT binary patch literal 5371 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 literal 0 HcmV?d00001 diff --git a/token_usage_chart.go b/token_usage_chart.go new file mode 100644 index 0000000..11e139e --- /dev/null +++ b/token_usage_chart.go @@ -0,0 +1,65 @@ +package mujica + +import ( + "strconv" + + "github.com/egoist/mygo/ui" +) + +// TokenUsage is the tokens one session or step used. +type TokenUsage struct { + Label string + Input, Output int +} + +// TokenUsageChartOptions configure a TokenUsageChart. +type TokenUsageChartOptions struct { + // Frame names, sizes and states the chart; an empty Label uses the built-in name. + Frame ChartFrame + // Line draws input and output as lines instead of stacked bars. + Line bool +} + +type tokenUsageSeries struct { + src *TokenUsage + n int + labels []string + series []ChartSeries +} + +// TokenUsageChart shows input and output tokens per session or step, as stacked bars or lines. +func TokenUsageChart(c *ui.Context, usage []TokenUsage, opts TokenUsageChartOptions) ChartResult { + if opts.Frame.Label == "" { + opts.Frame.Label = msg(c, "token_usage_chart.label") + } + holder := ui.Column(c).AlignItems(ui.Stretch) + cache := ui.Local(holder, "series", func() *tokenUsageSeries { return &tokenUsageSeries{} }) + s := *cache + var first *TokenUsage + if len(usage) > 0 { + first = &usage[0] + } + if s.series == nil || s.src != first || s.n != len(usage) { + in, out := make([]float64, len(usage)), make([]float64, len(usage)) + s.labels = make([]string, len(usage)) + for i, u := range usage { + if u.Input < 0 || u.Output < 0 { + panic("mujica: TokenUsage counts must not be negative") + } + in[i], out[i], s.labels[i] = float64(u.Input), float64(u.Output), u.Label + } + s.series = []ChartSeries{{Name: msg(c, "token_usage_chart.input"), Values: in}, {Name: msg(c, "token_usage_chart.output"), Values: out}} + s.src, s.n = first, len(usage) + } + format := func(v float64) string { return strconv.FormatFloat(v, 'f', 0, 64) } + var res ChartResult + holder.Children(func() { + if opts.Line { + res = LineChart(c, s.series, LineChartOptions{Frame: opts.Frame, Format: format}) + return + } + res = BarChart(c, s.labels, s.series, BarChartOptions{Frame: opts.Frame, Stacked: true, Format: format}) + }) + res.Element = holder + return res +} diff --git a/tool_registry_panel.go b/tool_registry_panel.go new file mode 100644 index 0000000..93867ac --- /dev/null +++ b/tool_registry_panel.go @@ -0,0 +1,124 @@ +package mujica + +import ( + "log/slog" + + "github.com/egoist/mygo/ui" + + "mujicaui/theme" +) + +// ToolParam is a parameter in a tool's input schema. +type ToolParam struct { + Name string + Type string + Required bool + Description string +} + +// AgentTool is a tool an agent may call; the caller keeps Enabled. +type AgentTool struct { + Name string + Description string + Enabled bool + Params []ToolParam +} + +// ToolRegistryPanelOptions configure a ToolRegistryPanel. +type ToolRegistryPanelOptions struct { + Disabled bool +} + +// ToolRegistryResult is a built ToolRegistryPanel and its events. +type ToolRegistryResult struct { + Element *ui.Element + toggled string + on bool +} + +// Toggled returns the tool switched this frame and its new state. +func (r ToolRegistryResult) Toggled() (name string, on, ok bool) { + return r.toggled, r.on, r.toggled != "" +} + +// ToolRegistryPanel lists tools with on/off switches and previews the chosen tool's parameters. +func ToolRegistryPanel(c *ui.Context, s *ListState[string], tools []AgentTool, opts ToolRegistryPanelOptions) ToolRegistryResult { + seen := map[string]bool{} + for _, t := range tools { + if t.Name == "" || seen[t.Name] { + panic("mujica: AgentTool needs a unique Name") + } + seen[t.Name] = true + } + k := Tokens(c) + u := Density(c).Unit() + row := ui.Row(c).AlignItems(ui.Stretch).Gap(u * 3).Disabled(opts.Disabled) + res := ToolRegistryResult{Element: row} + row.Children(func() { + ui.Column(c).Width(300).Shrink(0).AlignItems(ui.Stretch).Children(func() { + List(c, s, len(tools), ListOptions[string]{ + Key: func(i int) string { return tools[i].Name }, Label: func(i int) string { return tools[i].Name }, + Empty: msg(c, "tool_registry_panel.empty"), + }, func(i int) { + t := tools[i] + ui.Column(c).Grow(1).MinWidth(0).Children(func() { + ui.Text(c, t.Name).Font(theme.MonoFont).FontWeight(600).SingleLine() + ui.Text(c, t.Description).FontSize(FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted).SingleLine() + }) + on := t.Enabled + Switch(c, &on, "", SwitchOptions{Description: t.Name}).Label(t.Name) + if on != t.Enabled && !row.IsDisabled() { + res.toggled, res.on = t.Name, on + slog.Debug("mujica: tool registry toggle", "tool", t.Name, "on", on) + } + }).Element.Grow(1) + }) + ui.Column(c).Grow(1).MinWidth(0).Gap(u*2).Padding(u*3, u*4).Background(k.Surface).Border(theme.BorderWidth, k.Border). + Radius(theme.CardRadius).Children(func() { + name, ok := s.Selected() + var tool *AgentTool + for i := range tools { + if ok && tools[i].Name == name { + tool = &tools[i] + } + } + if tool == nil { + ui.Text(c, msg(c, "tool_registry_panel.choose")).TextColor(k.TextMuted) + return + } + toolSchema(c, *tool) + }) + }) + return res +} + +// toolSchema previews a tool's parameters as a typed list. +func toolSchema(c *ui.Context, t AgentTool) { + k := Tokens(c) + u := Density(c).Unit() + small := FontSize(c, theme.CaptionSize) + ui.Text(c, t.Name).Font(theme.SerifFont).FontWeight(600).FontSize(FontSize(c, theme.H3Size)) + if t.Description != "" { + ui.Text(c, t.Description).TextColor(k.TextMuted) + } + ui.Box(c).Height(1).Background(k.Ornament).Role(ui.RoleNone) + if len(t.Params) == 0 { + ui.Text(c, msg(c, "tool_registry_panel.no_params")).TextColor(k.TextMuted) + return + } + for _, p := range t.Params { + ui.Column(c).Gap(2).Children(func() { + ui.Row(c).Gap(u * 2).AlignItems(ui.Center).Children(func() { + ui.Text(c, p.Name).Font(theme.MonoFont).FontWeight(600) + ui.Text(c, p.Type).Font(theme.MonoFont).FontSize(small).TextColor(k.Info) + if p.Required { + ui.Text(c, msg(c, "tool_registry_panel.required")).FontSize(small).TextColor(k.AccentText). + Padding(0, u).Border(theme.BorderWidth, k.AccentText).Radius(theme.ControlRadius) + } + }) + if p.Description != "" { + ui.Text(c, p.Description).FontSize(small).TextColor(k.TextMuted) + } + }) + } +} diff --git a/trace_viewer.go b/trace_viewer.go new file mode 100644 index 0000000..b562cd1 --- /dev/null +++ b/trace_viewer.go @@ -0,0 +1,184 @@ +package mujica + +import ( + "cmp" + "fmt" + "log/slog" + "time" + + "github.com/egoist/mygo/ui" + + "mujicaui/internal" + "mujicaui/theme" +) + +// TraceAttr is a key and value recorded on a span. +type TraceAttr struct{ Key, Value string } + +// TraceSpan is one timed call of a trace; Start and End are offsets from the trace start. +type TraceSpan struct { + ID string + Parent string + Name string + Start time.Duration + End time.Duration + Failed bool + Attrs []TraceAttr +} + +// TraceViewerState holds the tree's sort, open spans, selection and index. +type TraceViewerState struct { + Tree TreeTableState[string] + index traceIndex + src *TraceSpan + n int + built bool +} + +type traceIndex struct { + byID map[string]int + kids map[string][]string + roots []string + end time.Duration +} + +// TraceViewerOptions configure a TraceViewer. +type TraceViewerOptions struct { + Status DataStatus + Error string +} + +// TraceViewer shows a call tree with each span's time bar; the chosen span's attributes show below. +func TraceViewer(c *ui.Context, s *TraceViewerState, spans []TraceSpan, opts TraceViewerOptions) DataTableView { + var first *TraceSpan + if len(spans) > 0 { + first = &spans[0] + } + if !s.built || s.src != first || s.n != len(spans) { + s.index = traceBuild(spans) + s.src, s.n, s.built = first, len(spans), true + for _, id := range s.index.roots { + s.Tree.SetOpen(id, true) + } + slog.Debug("mujica: trace viewer index", "spans", len(spans), "roots", len(s.index.roots)) + } + ix := s.index + k := Tokens(c) + u := Density(c).Unit() + span := func(id string) TraceSpan { return spans[ix.byID[id]] } + cols := []TreeColumn[string]{ + {ID: "name", Title: msg(c, "trace_viewer.span"), Compare: func(a, b string) int { return cmp.Compare(span(a).Name, span(b).Name) }, + Cell: func(id string) { + sp := span(id) + t := ui.Text(c, sp.Name).SingleLine() + if sp.Failed { + t.TextColor(k.Danger).Label(sp.Name + ", " + msg(c, "trace_viewer.failed")) + } + }}, + {ID: "duration", Title: msg(c, "trace_viewer.duration"), Width: 96, Align: ui.End, + Compare: func(a, b string) int { return cmp.Compare(span(a).End-span(a).Start, span(b).End-span(b).Start) }, + Cell: func(id string) { + sp := span(id) + ui.Text(c, traceDuration(sp.End-sp.Start)).FontFeatures("tnum").TextColor(k.TextMuted) + }}, + {ID: "timeline", Title: msg(c, "trace_viewer.timeline"), Width: 260, Compare: func(a, b string) int { return cmp.Compare(span(a).Start, span(b).Start) }, + Cell: func(id string) { + sp := span(id) + col := k.AccentText + if sp.Failed { + col = k.Danger + } + ui.Box(c).Height(12).Grow(1).Role(ui.RoleNone).Draw(func(p *ui.Painter, r ui.Rect) { + p.Fill(ui.Rect{X: r.X, Y: r.Y + r.H/2, W: r.W, H: 1}, k.Border, 0) + if ix.end <= 0 { + return + } + x0 := r.X + r.W*float32(float64(sp.Start)/float64(ix.end)) + w := max(2, r.W*float32(float64(sp.End-sp.Start)/float64(ix.end))) + p.Fill(ui.Rect{X: internal.Snap(p, x0), Y: r.Y, W: internal.Snap(p, w), H: r.H}, col, 2) + }) + }}, + } + var v DataTableView + col := ui.Column(c).AlignItems(ui.Stretch).Gap(u * 2) + col.Children(func() { + v = TreeTable(c, &s.Tree, TreeTableOptions[string]{Columns: cols, Roots: ix.roots, + Children: func(id string) []string { return ix.kids[id] }, + Label: func(id string) string { return span(id).Name }, + Status: opts.Status, Error: opts.Error, Empty: msg(c, "trace_viewer.empty")}) + v.Element.Grow(1) + id, ok := s.Tree.Selected() + if _, known := ix.byID[id]; !ok || !known { + return + } + traceAttrs(c, span(id)) + }) + v.Element = col + return v +} + +// traceAttrs lists the chosen span's timing and attributes. +func traceAttrs(c *ui.Context, sp TraceSpan) { + k := Tokens(c) + u := Density(c).Unit() + ui.Column(c).Gap(u).Padding(u*3, u*4).Background(k.Background).Radius(theme.CardRadius).Shrink(0).Children(func() { + ui.Text(c, sp.Name).Font(theme.SerifFont).FontWeight(600) + row := func(key, val string) { + ui.Row(c).Gap(u * 3).Children(func() { + ui.Text(c, key).FontSize(FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted).Width(140).Shrink(0) + ui.Text(c, val).Font(theme.MonoFont).FontSize(FontSize(c, theme.CaptionSize)).Grow(1).MinWidth(0) + }) + } + row(msg(c, "trace_viewer.start"), traceDuration(sp.Start)) + row(msg(c, "trace_viewer.duration"), traceDuration(sp.End-sp.Start)) + for _, a := range sp.Attrs { + row(a.Key, a.Value) + } + }) +} + +// traceBuild indexes spans by ID and parent; unknown parents make roots. +func traceBuild(spans []TraceSpan) traceIndex { + ix := traceIndex{byID: make(map[string]int, len(spans)), kids: map[string][]string{}} + for i, sp := range spans { + if sp.ID == "" || sp.End < sp.Start || sp.Start < 0 { + panic(fmt.Sprintf("mujica: TraceSpan %q needs an ID and 0 <= Start <= End", sp.ID)) + } + if _, dup := ix.byID[sp.ID]; dup { + panic("mujica: duplicate TraceSpan ID " + sp.ID) + } + ix.byID[sp.ID] = i + ix.end = max(ix.end, sp.End) + } + for _, sp := range spans { + if _, ok := ix.byID[sp.Parent]; ok && sp.Parent != sp.ID { + ix.kids[sp.Parent] = append(ix.kids[sp.Parent], sp.ID) + } else { + ix.roots = append(ix.roots, sp.ID) + } + } + seen := 0 + var walk func(ids []string) + walk = func(ids []string) { + for _, id := range ids { + seen++ + walk(ix.kids[id]) + } + } + walk(ix.roots) + if seen != len(spans) { + panic("mujica: TraceSpan parents form a cycle") + } + return ix +} + +// traceDuration prints microseconds, milliseconds or seconds. +func traceDuration(d time.Duration) string { + switch { + case d < time.Millisecond: + return fmt.Sprintf("%d µs", d.Microseconds()) + case d < time.Second: + return fmt.Sprintf("%.1f ms", float64(d)/float64(time.Millisecond)) + } + return fmt.Sprintf("%.2f s", d.Seconds()) +} diff --git a/typewriter.go b/typewriter.go new file mode 100644 index 0000000..4d23dcb --- /dev/null +++ b/typewriter.go @@ -0,0 +1,86 @@ +package mujica + +import ( + "log/slog" + "strings" + "time" + + "github.com/egoist/mygo/ui" +) + +// TypewriterOptions configure a Typewriter. +type TypewriterOptions struct { + // Speed is characters per second; zero means 40. + Speed float64 + // Skippable adds a Skip button that shows the rest at once. + Skippable bool +} + +// TypewriterResult is a built Typewriter and its events. +type TypewriterResult struct { + Element *ui.Element + done bool +} + +// Finished reports the frame the whole text came into view. +func (r TypewriterResult) Finished() bool { return r.done } + +type typewriterState struct { + text []rune + shown int + last time.Time + skipped bool + done bool +} + +// Typewriter reveals text a character at a time; text that grows continues where it was. +func Typewriter(c *ui.Context, text string, opts TypewriterOptions) TypewriterResult { + speed := opts.Speed + if speed == 0 { + speed = 40 + } + if speed < 0 { + panic("mujica: Typewriter Speed is negative") + } + col := ui.Column(c).AlignItems(ui.Start).Gap(Density(c).Unit() * 2) + res := TypewriterResult{Element: col} + st := ui.Local(col, "typing", func() *typewriterState { return &typewriterState{last: c.Now()} }) + s := *st + runes := []rune(text) + if !strings.HasPrefix(text, string(s.text)) { + s.shown, s.skipped, s.done = 0, false, false + slog.Debug("mujica: typewriter restart") + } + if len(runes) > len(s.text) { + s.done = false + } + s.text = runes + now := c.Now() + if c.Preferences().ReduceMotion || s.skipped { + s.shown = len(runes) + } else if s.shown < len(runes) { + step := time.Duration(float64(time.Second) / speed) + if n := int(now.Sub(s.last) / step); n > 0 { + s.shown = min(len(runes), s.shown+n) + s.last = s.last.Add(time.Duration(n) * step) + } + if s.shown < len(runes) { + c.After(s.last.Add(step).Sub(now)) + } + } else { + s.last = now + } + col.Children(func() { + ui.Text(c, string(runes[:s.shown])).Label(text) + if opts.Skippable && s.shown < len(runes) { + if Button(c, msg(c, "typewriter.skip"), ButtonOptions{Variant: Ghost}).Clicked() { + s.skipped, s.shown = true, len(runes) + slog.Debug("mujica: typewriter skip") + } + } + }) + if s.shown == len(runes) && !s.done { + s.done, res.done = true, true + } + return res +} diff --git a/wave_loader.go b/wave_loader.go new file mode 100644 index 0000000..f82b161 --- /dev/null +++ b/wave_loader.go @@ -0,0 +1,30 @@ +package mujica + +import ( + "math" + + "github.com/egoist/mygo/ui" + + "mujicaui/internal" +) + +// WaveLoader draws a sine line flowing to the right. +func WaveLoader(c *ui.Context, opts LoaderOptions) *ui.Element { + return loaderMark(c, opts, 2.5, 1400, func(p *ui.Painter, r ui.Rect, t float64, col ui.Color, still bool) { + var path ui.Path + amp := r.H * 0.3 + const steps = 24 + for i := 0; i <= steps; i++ { + f := float64(i) / steps + x := r.X + r.W*float32(f) + y := r.Y + r.H/2 - amp*float32(math.Sin(2*math.Pi*(1.5*f-t))) + x, y = internal.Snap(p, x), internal.Snap(p, y) + if i == 0 { + path.MoveTo(x, y) + } else { + path.LineTo(x, y) + } + } + p.StrokePath(&path, max(1.5, r.H/10), col) + }) +} From eb6e0752d6c2e8354b39c30f0df89219e7534c87 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 14:56:58 -0400 Subject: [PATCH 2/2] WIP motion 2 --- blink_highlight_test.go | 71 ++++++ cost_breakdown.go | 4 +- cost_breakdown_test.go | 95 ++++++++ count_up_test.go | 85 +++++++ eval_result_table_test.go | 74 ++++++ interrupt_button_test.go | 76 +++++++ messages_motion.go | 1 + shimmer.go | 27 ++- shimmer_test.go | 101 +++++++++ .../snapshots/darwin/blink_highlight-dark.png | Bin 0 -> 3202 bytes .../darwin/blink_highlight-light.png | Bin 0 -> 3062 bytes .../snapshots/darwin/cost_breakdown-dark.png | Bin 0 -> 22253 bytes .../snapshots/darwin/cost_breakdown-light.png | Bin 0 -> 21403 bytes testdata/snapshots/darwin/count_up-dark.png | Bin 0 -> 4082 bytes testdata/snapshots/darwin/count_up-light.png | Bin 0 -> 3994 bytes .../darwin/eval_result_table-dark.png | Bin 0 -> 23119 bytes .../darwin/eval_result_table-light.png | Bin 0 -> 22733 bytes .../darwin/interrupt_button-dark.png | Bin 0 -> 5682 bytes .../darwin/interrupt_button-light.png | Bin 0 -> 5247 bytes .../darwin/layout_transition-dark.png | Bin 0 -> 3414 bytes .../darwin/layout_transition-light.png | Bin 0 -> 3296 bytes testdata/snapshots/darwin/presence-dark.png | Bin 0 -> 2656 bytes testdata/snapshots/darwin/presence-light.png | Bin 0 -> 2488 bytes .../darwin/reorder_transition-dark.png | Bin 0 -> 3656 bytes .../darwin/reorder_transition-light.png | Bin 0 -> 3479 bytes testdata/snapshots/darwin/shimmer-dark.png | Bin 0 -> 3344 bytes testdata/snapshots/darwin/shimmer-light.png | Bin 0 -> 3148 bytes .../snapshots/darwin/shimmer_text-dark.png | Bin 0 -> 2811 bytes .../snapshots/darwin/shimmer_text-light.png | Bin 0 -> 2648 bytes testdata/snapshots/darwin/stagger-dark.png | Bin 0 -> 5340 bytes testdata/snapshots/darwin/stagger-light.png | Bin 0 -> 5084 bytes .../darwin/token_usage_chart-dark.png | Bin 0 -> 15625 bytes .../darwin/token_usage_chart-light.png | Bin 0 -> 15074 bytes .../darwin/tool_registry_panel-dark.png | Bin 0 -> 22686 bytes .../darwin/tool_registry_panel-light.png | Bin 0 -> 22522 bytes .../snapshots/darwin/trace_viewer-dark.png | Bin 0 -> 25337 bytes .../snapshots/darwin/trace_viewer-light.png | Bin 0 -> 24909 bytes testdata/snapshots/darwin/typewriter-dark.png | Bin 0 -> 5182 bytes .../snapshots/darwin/typewriter-light.png | Bin 0 -> 5099 bytes token_usage_chart_test.go | 64 ++++++ tool_registry_panel.go | 3 +- tool_registry_panel_test.go | 89 ++++++++ trace_viewer.go | 13 +- trace_viewer_test.go | 85 +++++++ transitions_test.go | 210 ++++++++++++++++++ typewriter.go | 9 +- typewriter_test.go | 80 +++++++ 47 files changed, 1067 insertions(+), 20 deletions(-) create mode 100644 blink_highlight_test.go create mode 100644 cost_breakdown_test.go create mode 100644 count_up_test.go create mode 100644 eval_result_table_test.go create mode 100644 interrupt_button_test.go create mode 100644 shimmer_test.go create mode 100644 testdata/snapshots/darwin/blink_highlight-dark.png create mode 100644 testdata/snapshots/darwin/blink_highlight-light.png create mode 100644 testdata/snapshots/darwin/cost_breakdown-dark.png create mode 100644 testdata/snapshots/darwin/cost_breakdown-light.png create mode 100644 testdata/snapshots/darwin/count_up-dark.png create mode 100644 testdata/snapshots/darwin/count_up-light.png create mode 100644 testdata/snapshots/darwin/eval_result_table-dark.png create mode 100644 testdata/snapshots/darwin/eval_result_table-light.png create mode 100644 testdata/snapshots/darwin/interrupt_button-dark.png create mode 100644 testdata/snapshots/darwin/interrupt_button-light.png create mode 100644 testdata/snapshots/darwin/layout_transition-dark.png create mode 100644 testdata/snapshots/darwin/layout_transition-light.png create mode 100644 testdata/snapshots/darwin/presence-dark.png create mode 100644 testdata/snapshots/darwin/presence-light.png create mode 100644 testdata/snapshots/darwin/reorder_transition-dark.png create mode 100644 testdata/snapshots/darwin/reorder_transition-light.png create mode 100644 testdata/snapshots/darwin/shimmer-dark.png create mode 100644 testdata/snapshots/darwin/shimmer-light.png create mode 100644 testdata/snapshots/darwin/shimmer_text-dark.png create mode 100644 testdata/snapshots/darwin/shimmer_text-light.png create mode 100644 testdata/snapshots/darwin/stagger-dark.png create mode 100644 testdata/snapshots/darwin/stagger-light.png create mode 100644 testdata/snapshots/darwin/token_usage_chart-dark.png create mode 100644 testdata/snapshots/darwin/token_usage_chart-light.png create mode 100644 testdata/snapshots/darwin/tool_registry_panel-dark.png create mode 100644 testdata/snapshots/darwin/tool_registry_panel-light.png create mode 100644 testdata/snapshots/darwin/trace_viewer-dark.png create mode 100644 testdata/snapshots/darwin/trace_viewer-light.png create mode 100644 testdata/snapshots/darwin/typewriter-dark.png create mode 100644 testdata/snapshots/darwin/typewriter-light.png create mode 100644 token_usage_chart_test.go create mode 100644 tool_registry_panel_test.go create mode 100644 trace_viewer_test.go create mode 100644 transitions_test.go create mode 100644 typewriter_test.go diff --git a/blink_highlight_test.go b/blink_highlight_test.go new file mode 100644 index 0000000..ebc9e59 --- /dev/null +++ b/blink_highlight_test.go @@ -0,0 +1,71 @@ +package mujica + +import ( + "bytes" + "testing" + "time" + + "github.com/egoist/mygo/ui" +) + +func TestBlinkHighlightFlashesOnceWithDirection(t *testing.T) { + value := 10.0 + tt, n := motionFrames(func(c *ui.Context) { + BlinkHighlight(c, value, BlinkHighlightOptions{Duration: 200 * time.Millisecond}) + }, 200, 60) + if motionBusy(tt, n) { + t.Fatal("first value flashed") + } + if _, ok := tt.Find("10.00"); !ok { + t.Fatal("value not named") + } + value = 12 + tt.Frame() + a := encode(t, tt.Image()) + if _, ok := tt.Find("12.00, up"); !ok { + t.Fatalf("direction not named: %q", tt.Texts()) + } + time.Sleep(250 * time.Millisecond) + tt.Frame() + if bytes.Equal(a, encode(t, tt.Image())) || motionBusy(tt, n) { + t.Fatal("flash did not fade and stop") + } + value = 9 + tt.Frame() + if _, ok := tt.Find("9.00, down"); !ok { + t.Fatalf("down not named: %q", tt.Texts()) + } + tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1}) + value = 11 + tt.Frame() + if motionBusy(tt, n) { + t.Fatal("flash under reduced motion") + } + if _, ok := tt.Find("11.00, up"); !ok { + t.Fatal("static mark missing under reduced motion") + } +} + +func TestBlinkHighlightInvalid(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + BlinkHighlight(c, 1, BlinkHighlightOptions{Duration: -1}) + }, 100, 50, 1) +} + +func TestBlinkHighlightSnapshot(t *testing.T) { + snapshotAfter(t, "blink_highlight", 300, 100, func(c *ui.Context) { + v := ui.Local(c.Root(), "v", func() float64 { return 101.5 }) + w := ui.Local(c.Root(), "w", func() float64 { return 88.25 }) + ui.Column(c).Padding(16).Gap(8).AlignItems(ui.Start).Children(func() { + BlinkHighlight(c, *v, BlinkHighlightOptions{}) + BlinkHighlight(c, *w, BlinkHighlightOptions{}) + }) + *v, *w = 103.75, 86.5 + }, func(tt *ui.Tester) { tt.Frame() }) +} diff --git a/cost_breakdown.go b/cost_breakdown.go index b86296c..7b88a78 100644 --- a/cost_breakdown.go +++ b/cost_breakdown.go @@ -91,8 +91,8 @@ func CostBreakdown(c *ui.Context, s *CostBreakdownState, items []CostItem, opts cols := []DataColumn[costRow]{ {ID: "name", Title: groupTitle, Sortable: true, Compare: func(a, b costRow) int { return cmp.Compare(a.name, b.name) }, Text: func(r costRow) string { return r.name }}, {ID: "calls", Title: msg(c, "cost_breakdown.calls"), Width: 80, Align: ui.End, Sortable: true, Compare: func(a, b costRow) int { return cmp.Compare(a.calls, b.calls) }, Text: func(r costRow) string { return count(r.calls) }}, - {ID: "input", Title: msg(c, "cost_breakdown.input"), Width: 110, Align: ui.End, Sortable: true, Compare: func(a, b costRow) int { return cmp.Compare(a.input, b.input) }, Text: func(r costRow) string { return count(r.input) }}, - {ID: "output", Title: msg(c, "cost_breakdown.output"), Width: 110, Align: ui.End, Sortable: true, Compare: func(a, b costRow) int { return cmp.Compare(a.output, b.output) }, Text: func(r costRow) string { return count(r.output) }}, + {ID: "input", Title: msg(c, "cost_breakdown.input"), Width: 120, Align: ui.End, Sortable: true, Compare: func(a, b costRow) int { return cmp.Compare(a.input, b.input) }, Text: func(r costRow) string { return count(r.input) }}, + {ID: "output", Title: msg(c, "cost_breakdown.output"), Width: 130, Align: ui.End, Sortable: true, Compare: func(a, b costRow) int { return cmp.Compare(a.output, b.output) }, Text: func(r costRow) string { return count(r.output) }}, {ID: "cost", Title: msg(c, "cost_breakdown.cost"), Width: 160, Sortable: true, Compare: func(a, b costRow) int { return cmp.Compare(a.cost, b.cost) }, Text: func(r costRow) string { return money(r.cost) }, Cell: func(r costRow) { diff --git a/cost_breakdown_test.go b/cost_breakdown_test.go new file mode 100644 index 0000000..0445900 --- /dev/null +++ b/cost_breakdown_test.go @@ -0,0 +1,95 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" +) + +func costSample() []CostItem { + return []CostItem{ + {Model: "opus", Tool: "search", Step: "plan", InputTokens: 1000, OutputTokens: 200, Cost: 0.30}, + {Model: "haiku", Tool: "search", Step: "gather", InputTokens: 4000, OutputTokens: 500, Cost: 0.05}, + {Model: "opus", Tool: "write", Step: "write", InputTokens: 2000, OutputTokens: 3000, Cost: 1.15}, + } +} + +func TestCostSum(t *testing.T) { + rows, total := costSum(costSample(), CostByModel, "?") + if len(rows) != 2 || rows[0].name != "opus" || rows[0].calls != 2 || rows[0].input != 3000 || total.calls != 3 { + t.Fatalf("rows %+v total %+v", rows, total) + } + if d := total.cost - 1.5; d > 1e-9 || d < -1e-9 { + t.Fatalf("total cost %v", total.cost) + } + rows, _ = costSum([]CostItem{{Cost: 1}}, CostByTool, "(none)") + if rows[0].name != "(none)" { + t.Fatalf("unnamed group %q", rows[0].name) + } +} + +func TestCostBreakdownGroupsAndTotal(t *testing.T) { + var s CostBreakdownState + by := CostByModel + items := costSample() + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Height(400).Children(func() { + CostBreakdown(c, &s, items, CostBreakdownOptions{GroupBy: by}).Element.Grow(1) + }) + }, 800, 420) + for _, want := range []string{"opus", "haiku", "$1.45", "$0.05", "97%", "Total", "$1.50", "7000 in · 3700 out"} { + if !tt.HasText(want) { + t.Errorf("missing %q in %q", want, tt.Texts()) + } + } + if _, ok := tt.Find("Total $1.50 over 3 calls"); !ok { + t.Error("total not named") + } + rows := &s.rows[0] + tt.Frame() + if &s.rows[0] != rows { + t.Fatal("sums recomputed without a change") + } + by = CostByStep + tt.Frame() + if !tt.HasText("gather") || tt.HasText("haiku") { + t.Fatalf("group by step: %q", tt.Texts()) + } + if err := tt.Click("Cost"); err != nil { + t.Fatal(err) + } + if s.Table.Sort.Column != "cost" { + t.Fatalf("sort %+v", s.Table.Sort) + } +} + +func TestCostBreakdownInvalid(t *testing.T) { + for _, tc := range []struct { + items []CostItem + opts CostBreakdownOptions + }{{[]CostItem{{Cost: -1}}, CostBreakdownOptions{}}, {nil, CostBreakdownOptions{GroupBy: 7}}, {nil, CostBreakdownOptions{Decimals: -1}}} { + func() { + defer func() { + if recover() == nil { + t.Errorf("%+v did not panic", tc) + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + var s CostBreakdownState + CostBreakdown(c, &s, tc.items, tc.opts) + }, 300, 200, 1) + }() + } +} + +func TestCostBreakdownSnapshot(t *testing.T) { + items := costSample() + snapshot(t, "cost_breakdown", 760, 300, func(c *ui.Context) { + s := ui.Local(c.Root(), "s", func() *CostBreakdownState { return &CostBreakdownState{} }) + ui.Column(c).Padding(16).Height(300).Children(func() { + CostBreakdown(c, *s, items, CostBreakdownOptions{}).Element.Grow(1) + }) + }) +} diff --git a/count_up_test.go b/count_up_test.go new file mode 100644 index 0000000..a5e2bc9 --- /dev/null +++ b/count_up_test.go @@ -0,0 +1,85 @@ +package mujica + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/egoist/mygo/ui" +) + +func TestCountUpCountsAndSettles(t *testing.T) { + value := 100.0 + tt, n := motionFrames(func(c *ui.Context) { + CountUp(c, value, CountUpOptions{Duration: 300 * time.Millisecond}) + }, 200, 60) + if !tt.HasText("100") || motionBusy(tt, n) { + t.Fatalf("first value shown at once: %q", tt.Texts()) + } + value = 200 + tt.Frame() + time.Sleep(100 * time.Millisecond) + tt.Frame() + mid := "" + for _, s := range tt.Texts() { + if s != "200" { + mid = s + } + } + if mid == "" || mid == "100" { + t.Fatalf("no value between: %q", tt.Texts()) + } + time.Sleep(300 * time.Millisecond) + tt.Frame() + if motionCount(tt, "200") != 2 || motionBusy(tt, n) { + t.Fatalf("did not settle: %q", tt.Texts()) + } + tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1}) + value = 5 + tt.Frame() + if motionCount(tt, "5") != 2 { + t.Fatalf("reduced motion did not jump: %q", tt.Texts()) + } +} + +func TestCountUpFormatAndInvalid(t *testing.T) { + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + CountUp(c, 12.345, CountUpOptions{Decimals: 1}) + CountUp(c, 3, CountUpOptions{Format: func(v float64) string { return fmt.Sprintf("%.0f items", v) }}) + }, 300, 80) + if !tt.HasText("12.3") || !tt.HasText("3 items") { + t.Fatalf("format: %q", tt.Texts()) + } + for _, o := range []CountUpOptions{{Duration: -1}, {Decimals: -1}} { + func() { + defer func() { + if recover() == nil { + t.Errorf("%+v did not panic", o) + } + }() + ui.Render(func(c *ui.Context) { Use(c, Settings{}); CountUp(c, 1, o) }, 100, 50, 1) + }() + } + defer func() { + if r := recover(); r == nil || !strings.Contains(fmt.Sprint(r), "finite") { + t.Fatal("NaN did not panic") + } + }() + ui.Render(func(c *ui.Context) { Use(c, Settings{}); CountUp(c, nanValue(), CountUpOptions{}) }, 100, 50, 1) +} + +func nanValue() float64 { + zero := 0.0 + return zero / zero +} + +func TestCountUpSnapshot(t *testing.T) { + snapshot(t, "count_up", 300, 100, func(c *ui.Context) { + ui.Column(c).Padding(16).Gap(8).Children(func() { + CountUp(c, 12840, CountUpOptions{}).FontSize(FontSize(c, 28)).Font("Georgia, serif") + CountUp(c, 0.875, CountUpOptions{Format: func(v float64) string { return fmt.Sprintf("%.1f%%", v*100) }}) + }) + }) +} diff --git a/eval_result_table_test.go b/eval_result_table_test.go new file mode 100644 index 0000000..80051fd --- /dev/null +++ b/eval_result_table_test.go @@ -0,0 +1,74 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" +) + +func evalSample() []EvalCase { + return []EvalCase{ + {ID: "c1", Name: "Greets by title", Score: 0.98, Passed: true}, + {ID: "c2", Name: "Refuses forgery", Score: 0.41}, + {ID: "c3", Name: "Summarizes letter", Score: 0.87, Passed: true}, + } +} + +func TestEvalResultTableSummaryAndDiff(t *testing.T) { + var s DataTableState[string] + diff := "" + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Height(400).Children(func() { + r := EvalResultTable(c, &s, evalSample(), EvalResultTableOptions{}) + r.Table.Element.Grow(1) + if id, ok := r.Diff(); ok { + diff = id + } + }) + }, 700, 420) + for _, want := range []string{"2 of 3 passed", "0.41", "Fail", "Pass"} { + if !tt.HasText(want) { + t.Errorf("missing %q: %q", want, tt.Texts()) + } + } + if err := tt.Click("View differences for Refuses forgery"); err != nil { + t.Fatal(err) + } + if diff != "c2" { + t.Fatalf("diff %q", diff) + } + if err := tt.Click("Score"); err != nil { + t.Fatal(err) + } + if s.Sort.Column != "score" { + t.Fatalf("sort %+v", s.Sort) + } +} + +func TestEvalResultTableInvalid(t *testing.T) { + for _, cases := range [][]EvalCase{{{Name: "x"}}, {{ID: "a", Score: nanValue()}}} { + func() { + defer func() { + if recover() == nil { + t.Errorf("%+v did not panic", cases) + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + var s DataTableState[string] + EvalResultTable(c, &s, cases, EvalResultTableOptions{}) + }, 300, 200, 1) + }() + } +} + +func TestEvalResultTableSnapshot(t *testing.T) { + cases := evalSample() + snapshot(t, "eval_result_table", 640, 300, func(c *ui.Context) { + s := ui.Local(c.Root(), "s", func() *DataTableState[string] { return &DataTableState[string]{} }) + ui.Column(c).Padding(16).Height(300).Children(func() { + EvalResultTable(c, *s, cases, EvalResultTableOptions{}).Table.Element.Grow(1) + }) + }) +} diff --git a/interrupt_button_test.go b/interrupt_button_test.go new file mode 100644 index 0000000..d7cd259 --- /dev/null +++ b/interrupt_button_test.go @@ -0,0 +1,76 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" +) + +func TestInterruptButtonDirectAndConfirmed(t *testing.T) { + running, interrupting, confirm := true, false, false + stops := 0 + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Padding(20).Children(func() { + if InterruptButton(c, running, InterruptButtonOptions{Confirm: confirm, Interrupting: interrupting}).Changed() { + stops++ + } + }) + }, 400, 300) + if err := tt.Click("Stop"); err != nil { + t.Fatal(err) + } + if stops != 1 { + t.Fatalf("direct stop %d", stops) + } + confirm = true + tt.Frame() + _ = tt.Click("Stop") + if stops != 1 || !tt.HasText("Stop the agent?") { + t.Fatalf("confirm not asked: %q", tt.Texts()) + } + _ = tt.Click("Keep running") + if stops != 1 || tt.HasText("Stop the agent?") { + t.Fatal("cancel interrupted") + } + _ = tt.Click("Stop") + tt.Key(0, ui.KeyTab) // Danger focuses Cancel first; Tab reaches Stop + tt.Key(0, ui.KeyEnter) + if stops != 2 { + t.Fatalf("confirmed stop %d: %q", stops, tt.Texts()) + } + interrupting = true + tt.Frame() + _ = tt.Click("Stopping") + if stops != 2 || !tt.HasText("Stopping") { + t.Fatalf("interrupting: stops %d %q", stops, tt.Texts()) + } + interrupting, running = false, false + tt.Frame() + _ = tt.Click("Stop") + if stops != 2 { + t.Fatal("idle button interrupted") + } +} + +func TestInterruptButtonInvalid(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + InterruptButton(c, false, InterruptButtonOptions{Interrupting: true}) + }, 100, 50, 1) +} + +func TestInterruptButtonSnapshot(t *testing.T) { + snapshot(t, "interrupt_button", 460, 90, func(c *ui.Context) { + ui.Row(c).Padding(20).Gap(12).Children(func() { + InterruptButton(c, true, InterruptButtonOptions{}) + InterruptButton(c, true, InterruptButtonOptions{Interrupting: true}) + InterruptButton(c, false, InterruptButtonOptions{Label: "Cancel run"}) + }) + }) +} diff --git a/messages_motion.go b/messages_motion.go index cc4cc5a..8597473 100644 --- a/messages_motion.go +++ b/messages_motion.go @@ -45,6 +45,7 @@ func init() { "tool_registry_panel.empty": {"No tools", "没有工具"}, "tool_registry_panel.choose": {"Choose a tool to see its parameters", "选择工具查看参数"}, "tool_registry_panel.no_params": {"No parameters", "无参数"}, + "tool_registry_panel.enable": {"Enable %s", "启用 %s"}, "tool_registry_panel.required": {"required", "必填"}, "typewriter.skip": {"Skip", "跳过"}, diff --git a/shimmer.go b/shimmer.go index b4df863..174a960 100644 --- a/shimmer.go +++ b/shimmer.go @@ -45,23 +45,26 @@ func Shimmer(c *ui.Context, loading bool, opts ShimmerOptions, content func()) * fill = k.Border } still := c.Preferences().ReduceMotion - e := ui.Box(c).Key(still).Height(h).Role(ui.RoleProgress).Label(label) + e := ui.Column(c).Height(h).AlignItems(ui.Stretch).Role(ui.RoleProgress).Label(label) if opts.Width > 0 { e.Width(opts.Width) } else { e.FillWidth() } - e.Draw(func(p *ui.Painter, r ui.Rect) { - p.Fill(r, fill, theme.CardRadius) - if still { - return - } - p.AnimationFrame() - t := float32(p.Now().UnixMilli()%1600) / 1600 - band := max(r.W*0.25, 24) - x := internal.Snap(p, r.X-band+(r.W+band)*t) - p.Clip(r, theme.CardRadius, func() { - p.Fill(ui.Rect{X: x, Y: r.Y, W: band, H: r.H}, k.Surface.Alpha(0.55), 0) + // Keyed by motion so a flip repaints. + e.Children(func() { + ui.Box(c).Key(still).Grow(1).Role(ui.RoleNone).Draw(func(p *ui.Painter, r ui.Rect) { + p.Fill(r, fill, theme.CardRadius) + if still { + return + } + p.AnimationFrame() + t := float32(p.Now().UnixMilli()%1600) / 1600 + band := max(r.W*0.25, 24) + x := internal.Snap(p, r.X-band+(r.W+band)*t) + p.Clip(r, theme.CardRadius, func() { + p.Fill(ui.Rect{X: x, Y: r.Y, W: band, H: r.H}, k.Surface.Alpha(0.55), 0) + }) }) }) return e diff --git a/shimmer_test.go b/shimmer_test.go new file mode 100644 index 0000000..7479e93 --- /dev/null +++ b/shimmer_test.go @@ -0,0 +1,101 @@ +package mujica + +import ( + "bytes" + "testing" + "time" + + "github.com/egoist/mygo/ui" +) + +func TestShimmerOnlyWhileLoading(t *testing.T) { + loading := true + tt, n := motionFrames(func(c *ui.Context) { + Shimmer(c, loading, ShimmerOptions{Height: 40, Label: "Loading report"}, func() { ui.Text(c, "Report ready") }) + }, 300, 100) + if _, ok := tt.Find("Loading report"); !ok || tt.HasText("Report ready") { + t.Fatalf("loading: %q", tt.Texts()) + } + a := encode(t, tt.Image()) + time.Sleep(150 * time.Millisecond) + tt.Frame() + if bytes.Equal(a, encode(t, tt.Image())) || !motionBusy(tt, n) { + t.Fatal("shimmer does not sweep") + } + tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1}) + if motionBusy(tt, n) { + t.Fatal("shimmer repaints under reduced motion") + } + tt.SetPreferences(ui.Preferences{TextScale: 1}) + loading = false + tt.Frame() + if !tt.HasText("Report ready") || motionBusy(tt, n) { + t.Fatalf("loaded: %q", tt.Texts()) + } +} + +func TestShimmerInvalid(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + Shimmer(c, true, ShimmerOptions{Width: -1}, nil) + }, 100, 50, 1) +} + +func TestShimmerSnapshot(t *testing.T) { + snapshot(t, "shimmer", 400, 140, func(c *ui.Context) { + ui.Column(c).Padding(16).Gap(12).Children(func() { + Shimmer(c, true, ShimmerOptions{}, nil) + Shimmer(c, true, ShimmerOptions{Width: 160, Height: 24}, nil) + Shimmer(c, false, ShimmerOptions{}, func() { ui.Text(c, "Loaded content") }) + }) + }) +} + +func TestShimmerTextSweepsAndStops(t *testing.T) { + active := true + tt, n := motionFrames(func(c *ui.Context) { + ShimmerText(c, "Thinking", ShimmerTextOptions{Active: active}) + }, 200, 60) + if _, ok := tt.Find("Thinking"); !ok { + t.Fatal("text not named") + } + a := encode(t, tt.Image()) + time.Sleep(200 * time.Millisecond) + tt.Frame() + if bytes.Equal(a, encode(t, tt.Image())) || !motionBusy(tt, n) { + t.Fatal("no sweep") + } + active = false + tt.Frame() + if motionBusy(tt, n) || !tt.HasText("Thinking") { + t.Fatal("inactive text still sweeps") + } + active = true + tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1}) + if motionBusy(tt, n) { + t.Fatal("sweeps under reduced motion") + } + defer func() { + if recover() == nil { + t.Fatal("empty text did not panic") + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + ShimmerText(c, "", ShimmerTextOptions{}) + }, 100, 50, 1) +} + +func TestShimmerTextSnapshot(t *testing.T) { + snapshot(t, "shimmer_text", 300, 80, func(c *ui.Context) { + ui.Column(c).Padding(16).Gap(8).Children(func() { + ShimmerText(c, "Thinking…", ShimmerTextOptions{Active: true}) + ShimmerText(c, "思考中…", ShimmerTextOptions{}) + }) + }) +} diff --git a/testdata/snapshots/darwin/blink_highlight-dark.png b/testdata/snapshots/darwin/blink_highlight-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..c2c362426905b36f3d74428fa8dc26ce47300544 GIT binary patch literal 3202 zcmc&$_cz?z7XONn(S@j^6C|QU??efb=q(IFwCIc$M!967M;nYTK?tG-(V|Uah!{76 ziA0Mw+Kh4&jQ%F?Pk3wH^?o^L@3qc3>$A`L?EU#9o0%9e&~noP0Ki~msAmBH6t~E+ z8|V^wedk(;1OR#?BfUFTPm2k2UJk6^y07edwrEdVT}mP(fs4v_8v`B9DQqW~Y#PvEVA-+^uN}cA#u2_RHr*P1yYhO)7Xb)Mz=krq4@X!A0s+Klk|h*j1(ZD z-j~P`j2XGS5H(g@5Xd-Q8P4DDEOX(YyaHSm($Px-T@@74gC{Zn`-uxucUbsJfN1oI zJ?%csJ%u0JTKqdMFRwqE0FsRR`3(*+EmO?)Z>RpfX=%C<5o{_19!BIAxRVQDd`q7Chzb&G~Q&N?MA5HI3HFmSF26k!| zX;!*J?L`m2{}xOfy)I1VM;IG?=`F#?at%FnJ(JPe_t^eYf5~KsKApw(tmPMQJ@7L! z3d?xGOe^l{TA}jpgTV{V7&(=;{rkay**Fu#-o8xJ zueC*9c$t~A=?MRVuKV=trbOL);2qJpdN=dVV@ft5 zZa3P%@XF+r#`)7d?@ou%BkpjG=6Ygh$(*+AJYP^0w~Cd%$%J(H(yF*qdhZ;vqVc|Y z5DoCW?AEs{bwlgt8T_wMjVuc-h{*2JB93q*7B4KIb1hfgY~WqgGZq{H6(wJmp??5J=II zZ_|75I(`$nxkksvHnk%8-P_C0vB%f(;gkOMdLN3H&E_rckW6vm)U-Y!FC=0_kouQ2 z?fxOb^og5W|7xI*_m(Qr)X+R9&g_Ex9=fQGhY^1PlB9T4P6})VIbK@{a!oWqw@kRh%dW)CWS6Db6 zcBp7|?jCL8KR~-+{Q1)@9v*7mj33o?brnwVnM1k0ygVCdt@rhXoE1*&<5=h~T2Im~ zx9|`akV)CG|IhvyUukV$hr!ymnc+H_{08QR(sU`6^Q|LAUt~eS2wpM{rX%F-v*n4& zzr_qx{G7-Q2;n1*Y7?H=ZTxR~#T_5JA6l07CI zNqCMw>Wo^A)J3rZq}hPB?vvqRU33^VeEo1!Ap%S4>Owa<6j?d)zX%I&X<ZW_$Z%j{b1oZC`h3&>KR{4)#SW&Ir1B(e-y0;=f2o19$D;cpO>DyD3@RD&ak zp;Xxx?(5l1`0?&;-vOO3PJYiV^Y6{gRt!x^OivHfzuxV8 zkb9A8D^*kywsXG2U`ilf$+EOg?u?B7nyv6Ur9^}?!5kw=BvR+(TYDUS|aMU9Fest7-80D`!Vj#(h}CXOiCqc zcPl=AseFlr54oi=Haa$Cchm3SH|I&sj+jE*{-fC7OqcvB(T&!c()9Fp`%42u`@K4^ z&dJRf8k#X89V-Y;NQQ}@KaoG#V%JNk(7s06;UIZ1%hLby(fKEH;J}G*+o{yGzW!FW zf7RQCm4zS2X)gI{;rbUrL4~THP_BbyJ>h5JjyNB*1_Fb^ph0|4NvT&I9j$LV;8hz& ztZ37crA7Rvg_TuH$kDOZwUZS5$_f~?kIn$EuBzmfziEn2Y($HNo^ymG6}zO1iSe15 z+T>6_>^=s3s!2ai+Z@c;k- literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/blink_highlight-light.png b/testdata/snapshots/darwin/blink_highlight-light.png new file mode 100644 index 0000000000000000000000000000000000000000..8b685789000757acc1cc71d1ca88df1bbdb0aab8 GIT binary patch literal 3062 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 G_L$tWEp93B^a33_b@Q87~x+7 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/count_up-dark.png b/testdata/snapshots/darwin/count_up-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..aecbad54ff94285a8315eb627ab224c7fcd30917 GIT binary patch literal 4082 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 OO#_GOs+X%ifBSFuqN?iv literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/eval_result_table-dark.png b/testdata/snapshots/darwin/eval_result_table-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..6a3fdf03b2fb70edf88d09c439c63d591ef36735 GIT binary patch literal 23119 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 Mlr$BK6kZ4X3sti&1poj5 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/eval_result_table-light.png b/testdata/snapshots/darwin/eval_result_table-light.png new file mode 100644 index 0000000000000000000000000000000000000000..96502fdca466289c19dae1a7c5257ec2ed50e4d6 GIT binary patch literal 22733 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 Fe*i#uQF{OY literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/interrupt_button-dark.png b/testdata/snapshots/darwin/interrupt_button-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..6a1ddf431e188f17c31887bbe648d81327ee27ab GIT binary patch literal 5682 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 YU@OpLIISdc8G|dJuBxq4qhuTLU(#aOWB>pF literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/interrupt_button-light.png b/testdata/snapshots/darwin/interrupt_button-light.png new file mode 100644 index 0000000000000000000000000000000000000000..a4f5601dc3d9d048f9d3a1ae36f5b2f824c933e7 GIT binary patch literal 5247 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 Sc7O0sXNVhS230Vpn12C~{4k{e literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/layout_transition-light.png b/testdata/snapshots/darwin/layout_transition-light.png new file mode 100644 index 0000000000000000000000000000000000000000..b0f6b3e82d6ae5dbf1910a0680a2c4b61076451c GIT binary patch literal 3296 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/presence-dark.png b/testdata/snapshots/darwin/presence-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..dd58ee3502c86ce4372f795ef2ba6a9044b75e31 GIT binary patch literal 2656 zcmdT``8yMiA1CTFCs5mZ8Y9Bz`*}a_U*6~QdcV`$Tnb%?wi9v(rE1I)@Zu3%+az)iw?|F8AgcKVm#3&s*H!uL}H4&4XH=a%y#0Q<@J zpUVN?$HCIGbl^v;U-hJ_ik-;n=5uXfUDxD2;dai9hd241nixO7_;D%zaB2eO9?7mn zUGHaJ)yU3HMFEq+N#x{hbR}>~{Zd+;6M%=3cE4KVXL+m@`Tq~-g#K-<)W-5Jz9{<1 z;#SZG+GbjZyuxU7wXX?=A)RAmb%Hsjh@LIY>y+~$uR7U+|7P{=*pVa7q5{wQ*38pK zXJLj01}+;?ZP7a&2Xdt3_p`*rSl?@x}DZmUGZN_^*GF@t}yzEti{?1tT=fJ~<&)&RA?Ikej{rwFT zirj^<*#XmXZ`V(!vWN5)BDQy&Ff7xBk6$_BQ2t5^@COq(oHR%gEIYnBV z-cpq!9ZV!dZwhXpk3YPINd*Kuz&O8ujmwDZ(L0#Lw8hB@q?2UIkN|7ZbuIP1Yq(DC z2|8ruN1a2qVqt~CSWlT=9?`R>E+l$n8eC;`=!3RTqS9K-rq%Xtc94j{eJxh74^Zsa zx&0u5ygD##-+nQ~4(LJ-Xh=#02hz58${Uz#Z3)YC6XpJ)KGEFPM`0$vNn@e48YU(j z1^*qu*KM3}e5kMQVH`Y6&&cT0qcSRWY#8^f!s((bGk;EBjr}L(xh1i`|0MJvUPq_O zPJF1qjPko!bh_BE2Y9SEYTrue@)|?o3}$&N&0HMxV6cfzH{j(i`xV8On>zk%t0!57 zix<$v#VVBAT2BoLykufgr5XeRG0^)_T2`i&WvI2278BdN{d4lSedmu-^_R=!#H&Fa zZl+qwuPLu1BO~1fREyq5zm#XLjr;gJs_wyJz?u!Mc|!+3mAiB)hBlr6eR6oMwjHcn z?Z(e#dHy52Z_;C13*tHS@h1vG%yn}wNm-PWa6CAOTETSRubQ;E=M%HKhvTE~L9x3U z;U2w3+&ae2ORna7`o_DS_C9qIoaLAghf_Bhyw-|XJYaHSVgl-*o~a23mm%?X{F#Tx$=5Evw7*>K)Yr#!iWJ>8=t- zK7KqiXf$#$FK5z+@su`G_ouYlk26hD0kN?qCWJjnPX-YGg!JOY=*UPVtxy~!zLxDx zLAz_=)(WGQ#qw-+=`%Rc)f1AV_r4^NYO9$^_)T4s z+dJI{up%`2VppS%^un1=%=+`9d3R+Q{eIsj8Bd=|838*2hL(}#1as+HZI1cS6zD@Q zTLIYE@NFiAH}A;L$kjzZY8p?wJDmy5eJ@R_=zF#ChHCt>8F>*ETUK7rjZ{jhX7^=g z;U?Q(7g#DvnTD6he0$}%7^1)(y{Khw!DD%eFLemiHyTF(c&pV!Y2Rz-vRRo~onbUs z^R&h_TYBj_J_iEb*wQsL^c>{N^!Rw^P-$nlUA zM%=d7!0s-wR@$X?s;XXIlE%^=a!F^R_B6y41l_mnRNUkZtt~I9FMyV7pY0Bl**Gd6 zS%DzZ6#UCD^`8CIdEZtwgA3kKoUDA!?D7Nk2$Bs!O zrUoPC`5U9IR=)^4&OlGqOO*s*=mTw>oCTCcu)*R$7#;~>FVJNNvdQNLoop7t>3{>2 z+jD}OhPKWtfdBCzcRz5(l9q&EtO7W=iDnL-!Hv}`(1N>koiF?Ke9txB&nrv+e^i_QZ(dYRrRZIHEtMJw!s&+mX)cz&XA-xITrpgL0K0SxaWdKi zkjP%qyCG5{vtD1C?|WmtmE!Ubprq=n-pb2n$8LdRV&0w!jY4E@vH4c7EMVApQNKXy z5`h0scJ_H;Q9rI6IkM;eL%bl1K>BB3P5k z$p=qo6Iu(FZ(9PeS(XcH5w`-(6J=QjKG~U$`DA4HD0G*d1yT}2E6*&)NUVv^-?DCT zP2CY-St!kb{ty$0+~fKm#rklHn2M6E^@F=o0{^z`t$ix@Qv639yQX%XI8h!6&ifB$ Um8-+t&ymOBtP8B#`of)m0Nv56i~s-t literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/presence-light.png b/testdata/snapshots/darwin/presence-light.png new file mode 100644 index 0000000000000000000000000000000000000000..0f1422d54341e0d93c231cb4424b442ca239a83c GIT binary patch literal 2488 zcmdUx`8N~{7sn}wA=@Ndib%+k-NaL#ns88ef~7n>7#u+FGG!fYow!VD@w|Ggn1SJqB!U;U**|_Nwzo zxaRJ2fD7Y6#PseT$KhNM>5zsBplvUqo~#J4%+mX~Z$CeF45M-io|67ow2&navuZae zri_k`O8#2+^7WM?H_amL8*;J6_K}#H z^wk)4LwlBR){|{ww3`!PYkz^~79jo>Svv2>2Hexr3}Rs5PIO_eQ@7Cx3GvA##fC3( zYyy$efSr)t5rB#^r`-cyxguz&JWzLXhF?PG#hTj;A?GN3*{k~_m-c3=c)PY@@blcO zz}b#_xp~3g`(8$x^h?Jcb#!(fQR%RdWiP|--kOR^DQHlLxE4883ExAXMb10f)R+tm zmYW7~pZOEh+B*9De0EN@;h@LgxFjXv1*I3Wddh1rLM$jdsw1#NR>UQGx7uVOHnmx{Oqd~hqGf))s7LXvi)r@)3fNL0we-+#7l zNuKB-JX_$}1Jl#hg{RQSYN_eNjCtKn{QaUlYTNX5>(c@`e(@bnAUDTv)Ztg_JMjuH z`|g!@Ox5Z~(ulVqTAktc^Hra7RGqdNd415t*r+!;I7;VxEY<;7|H*#`Lg*DLHw%4f zG(d#jh43U4r3ZDVHg2|-OyP6#$OnBin$E`VQkH%GF{5V>vaE~hGo5SE)eR462qEU? z8t2`(dr2^C@RgqX>u5SFrPVoB5O65G@SUQup`?3#@<&gBAIGp08byB?1 z{k%Tahx{Cb!Sp`{%+(RxDn2r2PkoGBPcoW-qs*&u2I zh@of**d0kkWQozf_NZc~%(zZz=9-(t zN3{`)%eXm(MV`m)@5~SHHfeZ!c))#q-_SkNB|w<)8*kGF$=M5y)6aA6`+> z8X%8f;rs$>d{}MO#Km#I$@xoLOKT{fptUqQF$?T-8`1A>L2RtVvG7wWMR9Rp?4D7g zv!9=#?Zb{1kG&+n`-h)qW)oP}n#8|Yhu1uV(cp{qGyyxL;jDR{uf-8uJZW%_h8Oizi(c7yIMAc=7 zYYGa}wazJ6Z0{%ZQu8*WC1kb7Taq13+@Ba7J+XnGzd!P@+S$JRD7-N}=!o_w6LYuS zqgg0p*mU@-=Md^>g7KH|-%;U5v2hfED*JF za*E!XUvN-ILS!gNlNBz`gS}uSF~cWHk~b?VtEwWXqn|=Akzrko)nnKsl`12+#mC3j z(uKFE988{uJ6OV`b|rrW&z zV@NOuho=wxy4RY9)*}Qr8fo}Y1cDjlGZ6oZ+bKvcGQq<|+ BRoMUl literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/reorder_transition-dark.png b/testdata/snapshots/darwin/reorder_transition-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..888af78db903323783152ef45e31cbe7d04cd33f GIT binary patch literal 3656 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/reorder_transition-light.png b/testdata/snapshots/darwin/reorder_transition-light.png new file mode 100644 index 0000000000000000000000000000000000000000..466fc25bb99dee9f38cffacd14feafb2f38d6c6f GIT binary patch literal 3479 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̹H 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/stagger-light.png b/testdata/snapshots/darwin/stagger-light.png new file mode 100644 index 0000000000000000000000000000000000000000..ccb86b9dc64742e889171aa254ce4cc89680b505 GIT binary patch literal 5084 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/token_usage_chart-dark.png b/testdata/snapshots/darwin/token_usage_chart-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..71af2eec9eee18c386cae92cbac2710aaa58b06b GIT binary patch literal 15625 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

6-f*Z-r_-x%G37vjsthPUOCn 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 Ol$fxTP?>;^&wm4w$X9Lv literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/tool_registry_panel-dark.png b/testdata/snapshots/darwin/tool_registry_panel-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..0dc412fa52cd5851b7cd0f123cbfce59ed227b87 GIT binary patch literal 22686 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/tool_registry_panel-light.png b/testdata/snapshots/darwin/tool_registry_panel-light.png new file mode 100644 index 0000000000000000000000000000000000000000..8ed4916e875ad94ddfa4bf0ea5271ff28c173ee1 GIT binary patch literal 22522 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/trace_viewer-light.png b/testdata/snapshots/darwin/trace_viewer-light.png new file mode 100644 index 0000000000000000000000000000000000000000..a1f58631dc2c0574be233e247f168b881f45dcc4 GIT binary patch literal 24909 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/typewriter-dark.png b/testdata/snapshots/darwin/typewriter-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..4573c7fb1f143ab8506219f5f22b03feef127003 GIT binary patch literal 5182 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 literal 0 HcmV?d00001 diff --git a/testdata/snapshots/darwin/typewriter-light.png b/testdata/snapshots/darwin/typewriter-light.png new file mode 100644 index 0000000000000000000000000000000000000000..ee60d5323734273fd9eed0051a4dd431ebf38cd4 GIT binary patch literal 5099 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 literal 0 HcmV?d00001 diff --git a/token_usage_chart_test.go b/token_usage_chart_test.go new file mode 100644 index 0000000..4e6d21a --- /dev/null +++ b/token_usage_chart_test.go @@ -0,0 +1,64 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" +) + +func tokenUsageSample() []TokenUsage { + return []TokenUsage{{Label: "Plan", Input: 1200, Output: 300}, {Label: "Search", Input: 4800, Output: 650}, {Label: "Write", Input: 2100, Output: 2900}} +} + +func TestTokenUsageChartBarsKeyboard(t *testing.T) { + usage := tokenUsageSample() + h := &chartHarness{build: func(c *ui.Context) ChartResult { return TokenUsageChart(c, usage, TokenUsageChartOptions{}) }} + tt := ui.NewTester(h.view, 520, 420) + tabTo(t, tt, "Token usage") + want := []string{"Plan · Input 1200", "Plan · Output 300", "Search · Input 4800"} + for i, w := range want { + tt.Key(0, ui.KeyRight) + if got := chartReadout(tt); got != w { + t.Fatalf("mark %d readout %q, want %q", i, got, w) + } + } + if !tt.HasText("Input") || !tt.HasText("Output") { + t.Fatal("legend missing") + } +} + +func TestTokenUsageChartLineEmptyInvalid(t *testing.T) { + usage := tokenUsageSample() + h := &chartHarness{build: func(c *ui.Context) ChartResult { + return TokenUsageChart(c, usage, TokenUsageChartOptions{Line: true, Frame: ChartFrame{Label: "Tokens per step"}}) + }} + tt := ui.NewTester(h.view, 520, 420) + tabTo(t, tt, "Tokens per step") + tt.Key(0, ui.KeyRight) + if h.res.Point < 0 { + t.Fatal("line chart has no keyboard path") + } + usage = nil + tt.Frame() + if !tt.HasText("No data") { + t.Fatalf("empty: %q", tt.Texts()) + } + defer func() { + if recover() == nil { + t.Fatal("negative tokens did not panic") + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + TokenUsageChart(c, []TokenUsage{{Label: "x", Input: -1}}, TokenUsageChartOptions{}) + }, 300, 200, 1) +} + +func TestTokenUsageChartSnapshot(t *testing.T) { + usage := tokenUsageSample() + snapshot(t, "token_usage_chart", 520, 360, func(c *ui.Context) { + ui.Column(c).Padding(16).Children(func() { + TokenUsageChart(c, usage, TokenUsageChartOptions{}) + }) + }) +} diff --git a/tool_registry_panel.go b/tool_registry_panel.go index 93867ac..cafa661 100644 --- a/tool_registry_panel.go +++ b/tool_registry_panel.go @@ -1,6 +1,7 @@ package mujica import ( + "fmt" "log/slog" "github.com/egoist/mygo/ui" @@ -66,7 +67,7 @@ func ToolRegistryPanel(c *ui.Context, s *ListState[string], tools []AgentTool, o ui.Text(c, t.Description).FontSize(FontSize(c, theme.CaptionSize)).TextColor(k.TextMuted).SingleLine() }) on := t.Enabled - Switch(c, &on, "", SwitchOptions{Description: t.Name}).Label(t.Name) + Switch(c, &on, "", SwitchOptions{}).Label(fmt.Sprintf(msg(c, "tool_registry_panel.enable"), t.Name)) if on != t.Enabled && !row.IsDisabled() { res.toggled, res.on = t.Name, on slog.Debug("mujica: tool registry toggle", "tool", t.Name, "on", on) diff --git a/tool_registry_panel_test.go b/tool_registry_panel_test.go new file mode 100644 index 0000000..8e93561 --- /dev/null +++ b/tool_registry_panel_test.go @@ -0,0 +1,89 @@ +package mujica + +import ( + "testing" + + "github.com/egoist/mygo/ui" +) + +func toolSample() []AgentTool { + return []AgentTool{ + {Name: "web_search", Description: "Search the web", Enabled: true, Params: []ToolParam{ + {Name: "query", Type: "string", Required: true, Description: "What to look for"}, + {Name: "limit", Type: "integer"}, + }}, + {Name: "read_file", Description: "Read a local file"}, + } +} + +func TestToolRegistryPanelToggleAndPreview(t *testing.T) { + var s ListState[string] + tools := toolSample() + disabled := false + var toggled []string + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Height(400).Children(func() { + r := ToolRegistryPanel(c, &s, tools, ToolRegistryPanelOptions{Disabled: disabled}) + r.Element.Grow(1) + if name, on, ok := r.Toggled(); ok { + toggled = append(toggled, name) + for i := range tools { + if tools[i].Name == name { + tools[i].Enabled = on + } + } + } + }) + }, 800, 420) + if !tt.HasText("Choose a tool to see its parameters") { + t.Fatalf("empty preview: %q", tt.Texts()) + } + if err := tt.Click("Search the web"); err != nil { + t.Fatal(err) + } + if !tt.HasText("query") || !tt.HasText("required") || !tt.HasText("integer") { + t.Fatalf("schema: %q", tt.Texts()) + } + if err := tt.Click("read_file"); err != nil { + t.Fatal(err) + } + if !tt.HasText("No parameters") { + t.Fatalf("no params: %q", tt.Texts()) + } + if err := tt.Click("Enable read_file"); err != nil { + t.Fatal(err) + } + if len(toggled) != 1 || toggled[0] != "read_file" || !tools[1].Enabled { + t.Fatalf("toggle %q enabled %v", toggled, tools[1].Enabled) + } + disabled = true + tt.Frame() + _ = tt.Click("Enable read_file") + if len(toggled) != 1 { + t.Fatal("disabled panel toggled") + } +} + +func TestToolRegistryPanelInvalid(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("no panic") + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + var s ListState[string] + ToolRegistryPanel(c, &s, []AgentTool{{Name: "a"}, {Name: "a"}}, ToolRegistryPanelOptions{}) + }, 300, 200, 1) +} + +func TestToolRegistryPanelSnapshot(t *testing.T) { + tools := toolSample() + snapshotAfter(t, "tool_registry_panel", 760, 300, func(c *ui.Context) { + s := ui.Local(c.Root(), "s", func() *ListState[string] { return &ListState[string]{} }) + ui.Column(c).Padding(16).Height(300).Children(func() { + ToolRegistryPanel(c, *s, tools, ToolRegistryPanelOptions{}).Element.Grow(1) + }) + }, func(tt *ui.Tester) { _ = tt.Click("Search the web") }) +} diff --git a/trace_viewer.go b/trace_viewer.go index b562cd1..7304c72 100644 --- a/trace_viewer.go +++ b/trace_viewer.go @@ -8,6 +8,7 @@ import ( "github.com/egoist/mygo/ui" + "mujicaui/icons" "mujicaui/internal" "mujicaui/theme" ) @@ -70,6 +71,9 @@ func TraceViewer(c *ui.Context, s *TraceViewerState, spans []TraceSpan, opts Tra {ID: "name", Title: msg(c, "trace_viewer.span"), Compare: func(a, b string) int { return cmp.Compare(span(a).Name, span(b).Name) }, Cell: func(id string) { sp := span(id) + if sp.Failed { + ui.Icon(c, icons.Must("circle-x")).Size(14, 14).TextColor(k.Danger).Shrink(0).Margin(0, u, 0, 0) + } t := ui.Text(c, sp.Name).SingleLine() if sp.Failed { t.TextColor(k.Danger).Label(sp.Name + ", " + msg(c, "trace_viewer.failed")) @@ -95,7 +99,14 @@ func TraceViewer(c *ui.Context, s *TraceViewerState, spans []TraceSpan, opts Tra } x0 := r.X + r.W*float32(float64(sp.Start)/float64(ix.end)) w := max(2, r.W*float32(float64(sp.End-sp.Start)/float64(ix.end))) - p.Fill(ui.Rect{X: internal.Snap(p, x0), Y: r.Y, W: internal.Snap(p, w), H: r.H}, col, 2) + bar := ui.Rect{X: internal.Snap(p, x0), Y: r.Y, W: internal.Snap(p, w), H: r.H} + if sp.Failed { + // Failed spans are hollow, not told apart by color alone. + p.Fill(bar, k.Surface, 2) + p.Stroke(bar, col, 2, 1.5) + return + } + p.Fill(bar, col, 2) }) }}, } diff --git a/trace_viewer_test.go b/trace_viewer_test.go new file mode 100644 index 0000000..f6b3de1 --- /dev/null +++ b/trace_viewer_test.go @@ -0,0 +1,85 @@ +package mujica + +import ( + "testing" + "time" + + "github.com/egoist/mygo/ui" +) + +func traceSample() []TraceSpan { + ms := time.Millisecond + return []TraceSpan{ + {ID: "root", Name: "agent.run", Start: 0, End: 900 * ms, Attrs: []TraceAttr{{"model", "opus"}}}, + {ID: "plan", Parent: "root", Name: "llm.plan", Start: 10 * ms, End: 210 * ms, Attrs: []TraceAttr{{"tokens", "1200"}}}, + {ID: "search", Parent: "root", Name: "tool.search", Start: 220 * ms, End: 520 * ms, Failed: true}, + {ID: "fetch", Parent: "search", Name: "http.get", Start: 230 * ms, End: 500 * ms}, + {ID: "write", Parent: "root", Name: "llm.write", Start: 530 * ms, End: 880 * ms}, + } +} + +func TestTraceBuild(t *testing.T) { + ix := traceBuild(traceSample()) + if len(ix.roots) != 1 || ix.roots[0] != "root" || len(ix.kids["root"]) != 3 || ix.end != 900*time.Millisecond { + t.Fatalf("index %+v", ix) + } + for name, spans := range map[string][]TraceSpan{ + "dup": {{ID: "a"}, {ID: "a"}}, + "time": {{ID: "a", Start: 2, End: 1}}, + "cycle": {{ID: "a", Parent: "b"}, {ID: "b", Parent: "a"}}, + } { + func() { + defer func() { + if recover() == nil { + t.Errorf("%s did not panic", name) + } + }() + traceBuild(spans) + }() + } +} + +func TestTraceViewerSelectShowsAttributes(t *testing.T) { + var s TraceViewerState + spans := traceSample() + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + ui.Column(c).Height(600).Children(func() { + TraceViewer(c, &s, spans, TraceViewerOptions{}).Element.Grow(1) + }) + }, 900, 620) + if !tt.HasText("llm.plan") || !tt.HasText("200.0 ms") { + t.Fatalf("tree: %q", tt.Texts()) + } + if _, ok := tt.Find("tool.search, failed"); !ok { + t.Fatal("failed span not named") + } + if tt.HasText("http.get") { + t.Fatal("nested span shown before opening its parent") + } + if err := tt.Click("llm.plan"); err != nil { + t.Fatal(err) + } + if id, _ := s.Tree.Selected(); id != "plan" || !tt.HasText("tokens") || !tt.HasText("1200") { + t.Fatalf("attributes: %q", tt.Texts()) + } + tt.Key(0, ui.KeyDown) + tt.Key(0, ui.KeyRight) + if id, _ := s.Tree.Selected(); id != "search" || !tt.HasText("http.get") { + t.Fatalf("keyboard open: %q", tt.Texts()) + } +} + +func TestTraceViewerSnapshot(t *testing.T) { + spans := traceSample() + snapshot(t, "trace_viewer", 820, 420, func(c *ui.Context) { + s := ui.Local(c.Root(), "s", func() *TraceViewerState { + st := &TraceViewerState{} + st.Tree.SetOpen("search", true) + return st + }) + ui.Column(c).Padding(16).Height(420).Children(func() { + TraceViewer(c, *s, spans, TraceViewerOptions{}).Element.Grow(1) + }) + }) +} diff --git a/transitions_test.go b/transitions_test.go new file mode 100644 index 0000000..e42cfa3 --- /dev/null +++ b/transitions_test.go @@ -0,0 +1,210 @@ +package mujica + +import ( + "testing" + "time" + + "github.com/egoist/mygo/ui" +) + +func TestPresenceEntersExitsAndRemoves(t *testing.T) { + for _, kind := range []PresenceKind{PresenceFade, PresenceSlide, PresenceCollapse} { + show := false + tt, n := motionFrames(func(c *ui.Context) { + ui.Column(c).Children(func() { + Presence(c, show, PresenceOptions{Kind: kind}, func() { ui.Text(c, "Notice") }) + ui.Text(c, "Below") + }) + }, 300, 120) + if tt.HasText("Notice") { + t.Fatal("hidden content built") + } + show = true + before := *n + tt.Frame() + if !tt.HasText("Notice") || *n-before < 3 { + t.Fatalf("kind %d: no entry motion (%d frames)", kind, *n-before) + } + time.Sleep(250 * time.Millisecond) + tt.Frame() + show = false + before = *n + tt.Frame() + if *n-before < 3 { + t.Fatalf("kind %d: no exit motion", kind) + } + time.Sleep(250 * time.Millisecond) + tt.Frame() + if tt.HasText("Notice") || motionBusy(tt, n) { + t.Fatalf("kind %d: not removed after exit", kind) + } + } +} + +func TestPresenceReducedMotionAndInvalid(t *testing.T) { + show := false + tt, n := motionFrames(func(c *ui.Context) { + ui.Column(c).Children(func() { Presence(c, show, PresenceOptions{Kind: PresenceSlide}, func() { ui.Text(c, "Notice") }) }) + }, 300, 120) + tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1}) + show = true + if motionBusy(tt, n) || !tt.HasText("Notice") { + t.Fatal("reduced motion animated") + } + defer func() { + if recover() == nil { + t.Fatal("unknown kind did not panic") + } + }() + ui.Render(func(c *ui.Context) { Use(c, Settings{}); Presence(c, true, PresenceOptions{Kind: 9}, nil) }, 100, 50, 1) +} + +func TestStaggerRevealsInTurnOnce(t *testing.T) { + count := 5 + tt, n := motionFrames(func(c *ui.Context) { + ui.Column(c).Children(func() { + Stagger(c, count, StaggerOptions{Interval: 50 * time.Millisecond}, func(i int) { ui.Textf(c, "Row %d", i) }) + }) + }, 300, 240) + if !tt.HasText("Row 0") || tt.HasText("Row 4") { + t.Fatalf("first frame: %q", tt.Texts()) + } + time.Sleep(120 * time.Millisecond) + tt.Frame() + if !tt.HasText("Row 2") || tt.HasText("Row 4") { + t.Fatalf("after 120 ms: %q", tt.Texts()) + } + time.Sleep(300 * time.Millisecond) + tt.Frame() + time.Sleep(250 * time.Millisecond) + tt.Frame() + if !tt.HasText("Row 4") || motionBusy(tt, n) { + t.Fatalf("did not finish and settle: %q", tt.Texts()) + } + count = 6 + tt.Frame() + if !tt.HasText("Row 5") { + t.Fatal("item added after the sequence waited") + } +} + +func TestStaggerReducedMotionAndInvalid(t *testing.T) { + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + if c.Preferences().ReduceMotion { + Stagger(c, 3, StaggerOptions{}, func(i int) { ui.Textf(c, "Row %d", i) }) + } + }, 300, 200) + tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1}) + if !tt.HasText("Row 2") { + t.Fatalf("reduced motion staggered: %q", tt.Texts()) + } + defer func() { + if recover() == nil { + t.Fatal("negative n did not panic") + } + }() + ui.Render(func(c *ui.Context) { Use(c, Settings{}); Stagger(c, -1, StaggerOptions{}, nil) }, 100, 50, 1) +} + +func TestReorderTransitionSlides(t *testing.T) { + keys := []string{"a", "b", "c"} + tt, n := motionFrames(func(c *ui.Context) { + ReorderTransition(c, keys, ReorderTransitionOptions{Gap: 4}, func(k string) { + ui.Text(c, "Item "+k).Padding(8) + }) + }, 300, 200) + ra, _ := tt.Find("Item a") + keys = []string{"c", "b", "a"} + before := *n + tt.Frame() + if *n-before < 3 { + t.Fatal("reorder did not animate") + } + mid, _ := tt.Find("Item a") + time.Sleep(260 * time.Millisecond) + tt.Frame() + end, _ := tt.Find("Item a") + if !(mid.Y > ra.Y && mid.Y < end.Y) || motionBusy(tt, n) { + t.Fatalf("a moved %v → %v → %v", ra.Y, mid.Y, end.Y) + } + defer func() { + if recover() == nil { + t.Fatal("duplicate key did not panic") + } + }() + ui.Render(func(c *ui.Context) { + Use(c, Settings{}) + ReorderTransition(c, []int{1, 1}, ReorderTransitionOptions{}, func(int) {}) + }, 100, 50, 1) +} + +func TestLayoutTransitionResizes(t *testing.T) { + wide := false + var w float32 + tt, n := motionFrames(func(c *ui.Context) { + ui.Column(c).AlignItems(ui.Start).Children(func() { + box := LayoutTransition(c, LayoutTransitionOptions{Clip: true}, func() { + width := float32(100) + if wide { + width = 300 + } + ui.Box(c).Size(width, 40) + }) + w = box.Bounds().W + }) + }, 400, 120) + wide = true + before := *n + tt.Frame() + if *n-before < 3 { + t.Fatal("resize did not animate") + } + time.Sleep(260 * time.Millisecond) + tt.Frame() + if motionBusy(tt, n) || w != 300 { + t.Fatalf("settled width %v", w) + } + defer func() { + if recover() == nil { + t.Fatal("negative duration did not panic") + } + }() + ui.Render(func(c *ui.Context) { Use(c, Settings{}); LayoutTransition(c, LayoutTransitionOptions{Duration: -1}, nil) }, 100, 50, 1) +} + +func TestPresenceSnapshot(t *testing.T) { + snapshot(t, "presence", 300, 100, func(c *ui.Context) { + ui.Column(c).Padding(16).Children(func() { + Presence(c, true, PresenceOptions{}, func() { ui.Text(c, "The doors open.") }) + }) + }) +} + +func TestStaggerSnapshot(t *testing.T) { + snapshot(t, "stagger", 300, 140, func(c *ui.Context) { + ui.Column(c).Padding(16).Children(func() { + Stagger(c, 3, StaggerOptions{}, func(i int) { ui.Textf(c, "Guest %d arrives", i+1) }) + }) + }) +} + +func TestReorderTransitionSnapshot(t *testing.T) { + snapshot(t, "reorder_transition", 300, 140, func(c *ui.Context) { + ui.Column(c).Padding(16).Children(func() { + ReorderTransition(c, []string{"Sakiko", "Uika", "Mutsumi"}, ReorderTransitionOptions{Gap: 4}, func(k string) { + ui.Text(c, k).Padding(4, 8).Background(Tokens(c).Surface).Radius(4) + }) + }) + }) +} + +func TestLayoutTransitionSnapshot(t *testing.T) { + snapshot(t, "layout_transition", 300, 100, func(c *ui.Context) { + ui.Column(c).Padding(16).AlignItems(ui.Start).Children(func() { + LayoutTransition(c, LayoutTransitionOptions{}, func() { + ui.Text(c, "A panel that grows").Padding(12).Background(Tokens(c).Surface).Radius(4) + }) + }) + }) +} diff --git a/typewriter.go b/typewriter.go index 4d23dcb..b1bc810 100644 --- a/typewriter.go +++ b/typewriter.go @@ -42,7 +42,7 @@ func Typewriter(c *ui.Context, text string, opts TypewriterOptions) TypewriterRe if speed < 0 { panic("mujica: Typewriter Speed is negative") } - col := ui.Column(c).AlignItems(ui.Start).Gap(Density(c).Unit() * 2) + col := ui.Column(c).AlignItems(ui.Start).Gap(Density(c).Unit() * 2).Role(ui.RoleGroup).Label(text) res := TypewriterResult{Element: col} st := ui.Local(col, "typing", func() *typewriterState { return &typewriterState{last: c.Now()} }) s := *st @@ -51,8 +51,9 @@ func Typewriter(c *ui.Context, text string, opts TypewriterOptions) TypewriterRe s.shown, s.skipped, s.done = 0, false, false slog.Debug("mujica: typewriter restart") } - if len(runes) > len(s.text) { - s.done = false + if len(runes) > len(s.text) && s.done { + // New text continues from where it stood, at the typing speed. + s.done, s.skipped, s.last = false, false, c.Now() } s.text = runes now := c.Now() @@ -71,7 +72,7 @@ func Typewriter(c *ui.Context, text string, opts TypewriterOptions) TypewriterRe s.last = now } col.Children(func() { - ui.Text(c, string(runes[:s.shown])).Label(text) + ui.Text(c, string(runes[:s.shown])) if opts.Skippable && s.shown < len(runes) { if Button(c, msg(c, "typewriter.skip"), ButtonOptions{Variant: Ghost}).Clicked() { s.skipped, s.shown = true, len(runes) diff --git a/typewriter_test.go b/typewriter_test.go new file mode 100644 index 0000000..23f2103 --- /dev/null +++ b/typewriter_test.go @@ -0,0 +1,80 @@ +package mujica + +import ( + "testing" + "time" + + "github.com/egoist/mygo/ui" +) + +func TestTypewriterRevealsFinishesAndSkips(t *testing.T) { + text := "The court assembles at dusk." + finished := 0 + tt, n := motionFrames(func(c *ui.Context) { + if Typewriter(c, text, TypewriterOptions{Speed: 100, Skippable: true}).Finished() { + finished++ + } + }, 400, 120) + if tt.HasText(text) && motionCount(tt, text) == 2 { + t.Fatal("whole text shown at once") + } + time.Sleep(120 * time.Millisecond) + tt.Frame() + shown := 0 + for _, s := range tt.Texts() { + if s != text && s != "Skip" && len(s) > shown { + shown = len(s) + } + } + if shown < 5 || shown >= len(text) { + t.Fatalf("partial reveal %d of %d: %q", shown, len(text), tt.Texts()) + } + if err := tt.Click("Skip"); err != nil { + t.Fatal(err) + } + if motionCount(tt, text) != 2 || finished != 1 || tt.HasText("Skip") || motionBusy(tt, n) { + t.Fatalf("skip: finished %d texts %q", finished, tt.Texts()) + } + text += " The music begins." + tt.Frame() + if motionCount(tt, "The court assembles at dusk.") != 1 { + t.Fatalf("appended text restarted: %q", tt.Texts()) + } + time.Sleep(300 * time.Millisecond) + tt.Frame() + if motionCount(tt, text) != 2 || finished != 2 { + t.Fatalf("append: finished %d %q", finished, tt.Texts()) + } +} + +func TestTypewriterReducedMotionAndInvalid(t *testing.T) { + finished := false + tt := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + finished = Typewriter(c, "All at once", TypewriterOptions{}).Finished() || finished + }, 300, 80) + tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1}) + _ = tt + tt2 := ui.NewTester(func(c *ui.Context) { + Use(c, Settings{}) + finished = Typewriter(c, "Shown now", TypewriterOptions{}).Finished() || finished + }, 300, 80) + tt2.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1}) + if motionCount(tt2, "Shown now") != 2 || !finished { + t.Fatalf("reduced motion: %q", tt2.Texts()) + } + defer func() { + if recover() == nil { + t.Fatal("negative speed did not panic") + } + }() + ui.Render(func(c *ui.Context) { Use(c, Settings{}); Typewriter(c, "x", TypewriterOptions{Speed: -1}) }, 100, 50, 1) +} + +func TestTypewriterSnapshot(t *testing.T) { + snapshot(t, "typewriter", 420, 100, func(c *ui.Context) { + ui.Column(c).Padding(16).Children(func() { + Typewriter(c, "The court assembles at dusk; the music begins.", TypewriterOptions{Skippable: true}) + }) + }) +}