Files
MujicaUI/git/commit_list.go
T
ZacharyZhang-NYandClaude Opus 5.5 bcce34cda1 Upgrade MyGo to v0.5.5
MyGo v0.5.5 returns ui.Element as a checked value handle instead of a
pointer. Migrated with `mygo migrate-ui`, then fixed what the codemod
leaves to review:

- Element nil checks become .Valid(); absent elements are ui.Element{}.
- Input handlers and paint callbacks run outside the build pass, where
  elements expire and Context methods are inert: read IsDisabled into a
  local during the build, keep a ui.Handle for Focus, use c.Services()
  for clipboard access, create ui.Local state during the build.
- Add core.AutoFocus: v0.5.5 records an overlay's opener after its
  content builds, so same-frame autofocus lost focus restore on close.
  It focuses one frame later; cascader rows defer their focus likewise.
- core.OverlayLayer identifies build passes by the root element, not
  c.Now(), which repeats across passes and under the Tester clock.
- Context menus survive the opening press counted as PressedOutside;
  split button tooltip no longer reorders its open menu; disabled
  menubar rows pass presses to the panel; Form defers blur-started
  async checks so the growing message no longer moves Submit away
  from the release; Combobox drops its disabled copy now that MyGo
  blocks typing under a disabled wrapper (canary flipped to a guard).
- The Tester clock is virtual: tests use Advance/Now instead of
  time.Sleep/time.Now and read element state inside the view. The web
  mirror advances the clock to wall time before each frame and input.
- AGENTS.md and docs describe the value Element API and the new pin.

Snapshot goldens are darwin-only and were not re-verified here.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 01:37:54 -04:00

108 lines
3.3 KiB
Go

package git
import (
"log/slog"
"time"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/data"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
)
// Commit is one commit of a history, newest first.
type Commit struct {
Hash string
Parents []string
Author string
When time.Time
Subject string
// Refs are branch and tag names pointing at the commit.
Refs []string
}
// GitListResult is a built version-control list and its events.
type GitListResult struct {
Element ui.Element
changed, submitted bool
action, item string
}
// Changed reports a new selection.
func (r GitListResult) Changed() bool { return r.changed }
// Submitted reports Enter or a double click on a row.
func (r GitListResult) Submitted() bool { return r.submitted }
// Action returns this frame's action ID and its item.
func (r GitListResult) Action() (action, item string, ok bool) {
return r.action, r.item, r.action != ""
}
// CommitListOptions configure a CommitList.
type CommitListOptions struct {
// TimeFormat lays out commit times; empty means "2006-01-02 15:04".
TimeFormat string
Status data.DataStatus
Error string
}
// CommitList shows commits to choose from; give it a height.
func CommitList(c *ui.Context, s *data.ListState[string], commits []Commit, opts CommitListOptions) GitListResult {
layout := gitTimeLayout(opts.TimeFormat)
v := data.List(c, s, len(commits), data.ListOptions[string]{
Key: func(i int) string { return gitCommitHash(commits[i]) },
Label: func(i int) string { return commits[i].Subject },
Status: opts.Status, Error: opts.Error, Empty: core.Msg(c, "commit_list.empty"),
}, func(i int) { commitListRow(c, commits[i], layout) })
r := GitListResult{Element: v.Element, changed: v.Changed(), submitted: v.Submitted()}
if r.changed || r.submitted {
key, _ := s.Selected()
slog.Debug("mujica: commit list choose", "hash", key, "open", r.submitted)
}
return r
}
func commitListRow(c *ui.Context, cm Commit, layout string) {
k := core.Tokens(c)
gitRefs(c, cm.Refs)
ui.Text(c, cm.Subject).SingleLine().Grow(1).MinWidth(0)
small := core.FontSize(c, theme.CaptionSize)
ui.Text(c, cm.Author).FontSize(small).TextColor(k.TextMuted).SingleLine().MaxWidth(140).Shrink(0)
ui.Text(c, gitShortHash(cm.Hash)).Font(theme.MonoFont).FontSize(small).TextColor(k.AccentText).Shrink(0)
ui.Text(c, cm.When.Format(layout)).FontSize(small).FontFeatures("tnum").TextColor(k.TextMuted).Shrink(0)
}
// gitRefs shows branch and tag names as small outlined pills.
func gitRefs(c *ui.Context, refs []string) {
k := core.Tokens(c)
u := core.Density(c).Unit()
for _, ref := range refs {
ui.Text(c, ref).FontSize(core.FontSize(c, theme.CaptionSize)).FontWeight(600).TextColor(k.AccentText).SingleLine().
Padding(0, u*1.5).Radius(theme.ControlRadius).Border(theme.BorderWidth, k.AccentText).Shrink(0).MaxWidth(160)
}
}
func gitCommitHash(cm Commit) string {
if cm.Hash == "" {
panic("mujica: Commit needs a Hash")
}
return cm.Hash
}
// gitShortHash is the first seven characters of a hash.
func gitShortHash(h string) string {
if len(h) > 7 {
return h[:7]
}
return h
}
func gitTimeLayout(f string) string {
if f == "" {
return "2006-01-02 15:04"
}
return f
}