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>
This commit is contained in:
1 parent
bc716d27be
commit
bcce34cda1
603 files changed
+1366
-1277
No files matched your search
@@ -42,7 +42,7 @@ type AlertListOptions struct {
|
||||
|
||||
// AlertListResult reports this frame's request.
|
||||
type AlertListResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Ack and Silence report requested alert IDs; empty means idle.
|
||||
Ack, Silence string
|
||||
}
|
||||
@@ -97,7 +97,7 @@ func alertRow(c *ui.Context, a AlertItem, locked bool) (ack, silence bool) {
|
||||
sev := [...]core.Severity{core.SeverityInfo, core.SeverityWarning, core.SeverityError}[a.Level]
|
||||
word := core.Msg(c, [...]string{"alert_list.info", "alert_list.warning", "alert_list.critical"}[a.Level])
|
||||
silenced := a.Silence.After(c.Now())
|
||||
row := ui.Row(c).Key(a.ID).Gap(u*3).AlignItems(ui.Center).MinHeight(d.ControlHeight()+u*2).Padding(u*2, u*3).
|
||||
row := ui.Row(c.Key(a.ID)).Gap(u*3).AlignItems(ui.Center).MinHeight(d.ControlHeight()+u*2).Padding(u*2, u*3).
|
||||
Label(word + ", " + a.Title)
|
||||
if tone, _ := core.SeverityLook(k, sev); a.Level == AlertCritical && !a.Acked {
|
||||
row.DrawOver(func(p *ui.Painter, r ui.Rect) { p.Fill(ui.Rect{X: r.X, Y: r.Y, W: 3, H: r.H}, tone, 0) })
|
||||
|
||||
@@ -36,7 +36,7 @@ type ApiRequestBuilderOptions struct {
|
||||
|
||||
// ApiRequestResult reports this frame's events.
|
||||
type ApiRequestResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Send asks the caller to send the request.
|
||||
Send bool
|
||||
Changed bool
|
||||
|
||||
@@ -27,6 +27,6 @@ func devtoolsSeverity(c *ui.Context, s core.Severity, word string) {
|
||||
}
|
||||
|
||||
// devtoolsTitle is a serif panel title.
|
||||
func devtoolsTitle(c *ui.Context, s string) *ui.Element {
|
||||
func devtoolsTitle(c *ui.Context, s string) ui.Element {
|
||||
return ui.Text(c, s).Font(theme.SerifFont).FontSize(core.FontSize(c, theme.H3Size)).FontWeight(600)
|
||||
}
|
||||
@@ -16,15 +16,14 @@ func snapshot(t *testing.T, name string, w, h int, view func(c *ui.Context)) {
|
||||
snaptest.Run(t, name, w, h, snapshotTheme, view, nil)
|
||||
}
|
||||
|
||||
// overlayUntil draws frames until cond holds, for at most 5 s of wall time.
|
||||
// overlayUntil advances the Tester clock until cond holds, for at most 5 s.
|
||||
func overlayUntil(tt *ui.Tester, cond func() bool) bool {
|
||||
end := time.Now().Add(5 * time.Second)
|
||||
end := tt.Now().Add(5 * time.Second)
|
||||
for !cond() {
|
||||
if time.Now().After(end) {
|
||||
if tt.Now().After(end) {
|
||||
return false
|
||||
}
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
tt.Frame()
|
||||
tt.Advance(2 * time.Millisecond)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ type Incident struct {
|
||||
}
|
||||
|
||||
// IncidentCard shows an incident's title, status, impact and update timeline.
|
||||
func IncidentCard(c *ui.Context, inc Incident) *ui.Element {
|
||||
func IncidentCard(c *ui.Context, inc Incident) ui.Element {
|
||||
if inc.Title == "" {
|
||||
panic("mujica: IncidentCard needs a Title")
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ type JsonEditorOptions struct {
|
||||
|
||||
// JsonEditorResult reports this frame's events and the parse state.
|
||||
type JsonEditorResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
Changed bool
|
||||
// Valid reports *text parses as one JSON value.
|
||||
Valid bool
|
||||
|
||||
@@ -43,7 +43,7 @@ type JsonViewerOptions struct {
|
||||
|
||||
// JsonViewerResult reports this frame's events.
|
||||
type JsonViewerResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Copied is the path written to the clipboard, else "".
|
||||
Copied string
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func KeyValueInput(c *ui.Context, rows *[]KeyValue, opts KeyValueInputOptions) c
|
||||
r.Element.Children(func() {
|
||||
agent.AgentRows(c, len(*rows), func(i int) {
|
||||
kv := &(*rows)[i]
|
||||
ui.Row(c).Key(i).Gap(u * 2).AlignItems(ui.Center).Children(func() {
|
||||
ui.Row(c.Key(i)).Gap(u * 2).AlignItems(ui.Center).Children(func() {
|
||||
name := fmt.Sprintf(core.Msg(c, "key_value_input.row"), i+1)
|
||||
on := kv.Enabled
|
||||
box := input.Checkbox(c, &on, "", input.CheckboxOptions{Disabled: locked}).Label(name)
|
||||
|
||||
@@ -23,7 +23,7 @@ type RefreshIntervalSelectorOptions struct {
|
||||
|
||||
// RefreshResult reports this frame's events.
|
||||
type RefreshResult struct {
|
||||
Element *ui.Element
|
||||
Element ui.Element
|
||||
// Refresh requests caller loading, manually or after the interval.
|
||||
Refresh bool
|
||||
// Changed reports a new interval.
|
||||
|
||||
@@ -31,8 +31,7 @@ func TestRefreshIntervalSelectorManualAndAuto(t *testing.T) {
|
||||
if refreshes != 1 {
|
||||
t.Fatalf("manual refreshes %d", refreshes)
|
||||
}
|
||||
time.Sleep(1100 * time.Millisecond)
|
||||
tt.Frame()
|
||||
tt.Advance(1100 * time.Millisecond)
|
||||
if refreshes != 1 {
|
||||
t.Fatal("refreshed while off")
|
||||
}
|
||||
@@ -41,11 +40,11 @@ func TestRefreshIntervalSelectorManualAndAuto(t *testing.T) {
|
||||
if interval != time.Second || changes != 1 {
|
||||
t.Fatalf("interval %v changes %d", interval, changes)
|
||||
}
|
||||
start := time.Now()
|
||||
start := tt.Now()
|
||||
if !overlayUntil(tt, func() bool { return refreshes == 2 }) {
|
||||
t.Fatal("no automatic refresh")
|
||||
}
|
||||
if time.Since(start) < 900*time.Millisecond {
|
||||
if tt.Now().Sub(start) < 900*time.Millisecond {
|
||||
t.Fatal("refreshed before the interval")
|
||||
}
|
||||
}
|
||||
@@ -56,15 +55,13 @@ func TestRefreshIntervalSelectorRefreshingAndDisabled(t *testing.T) {
|
||||
refreshes, changes := 0, 0
|
||||
tt := refreshTester(&interval, &opts, &refreshes, &changes)
|
||||
_ = tt.Click("Refresh")
|
||||
time.Sleep(1100 * time.Millisecond)
|
||||
tt.Frame()
|
||||
tt.Advance(1100 * time.Millisecond)
|
||||
if refreshes != 0 || !tt.HasText("Refresh") {
|
||||
t.Fatalf("refreshing: %d", refreshes)
|
||||
}
|
||||
opts = RefreshIntervalSelectorOptions{Intervals: []time.Duration{time.Second}, Disabled: true}
|
||||
tt.Frame()
|
||||
time.Sleep(1100 * time.Millisecond)
|
||||
tt.Frame()
|
||||
tt.Advance(1100 * time.Millisecond)
|
||||
_ = tt.Click("Refresh")
|
||||
if refreshes != 0 {
|
||||
t.Fatalf("disabled refreshed %d", refreshes)
|
||||
|
||||
@@ -66,7 +66,7 @@ func ResourceGauge(c *ui.Context, readings []ResourceReading, opts ResourceGauge
|
||||
panic(fmt.Sprintf("mujica: ResourceGauge reading %d needs a Name and 0 <= Used <= Total", i))
|
||||
}
|
||||
frac := r.Used / r.Total
|
||||
ui.Column(c).Key(r.Name).Width(200).Gap(u).AlignItems(ui.Center).Children(func() {
|
||||
ui.Column(c.Key(r.Name)).Width(200).Gap(u).AlignItems(ui.Center).Children(func() {
|
||||
ui.Text(c, r.Name).FontWeight(600)
|
||||
gauge := chart.Gauge(c, frac*100, chart.GaugeOptions{
|
||||
Frame: chart.ChartFrame{Label: r.Name, Height: h, Status: opts.Status, Error: opts.Error}, Min: 0, Max: 100, Unit: "%",
|
||||
|
||||
@@ -35,7 +35,7 @@ type ServiceStatusOptions struct {
|
||||
}
|
||||
|
||||
// ServiceStatus lists service health and an overall summary.
|
||||
func ServiceStatus(c *ui.Context, services []ServiceItem, opts ServiceStatusOptions) *ui.Element {
|
||||
func ServiceStatus(c *ui.Context, services []ServiceItem, opts ServiceStatusOptions) ui.Element {
|
||||
if opts.MaxHeight < 0 {
|
||||
panic("mujica: ServiceStatus MaxHeight is negative")
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func ServiceStatus(c *ui.Context, services []ServiceItem, opts ServiceStatusOpti
|
||||
agent.AgentRows(c, len(services), func(i int) {
|
||||
s := services[i]
|
||||
word := serviceWord(c, s.Health)
|
||||
ui.Row(c).Key(i).Gap(u*3).AlignItems(ui.Center).MinHeight(core.Density(c).ControlHeight()).Padding(u, u*3).
|
||||
ui.Row(c.Key(i)).Gap(u*3).AlignItems(ui.Center).MinHeight(core.Density(c).ControlHeight()).Padding(u, u*3).
|
||||
Label(s.Name + ", " + word).Children(func() {
|
||||
ui.Column(c).Grow(1).Shrink(1).Children(func() {
|
||||
ui.Text(c, s.Name).SingleLine()
|
||||
|
||||
@@ -56,7 +56,7 @@ func SystemMonitor(c *ui.Context, times []time.Time, metrics []MonitorMetric, op
|
||||
return
|
||||
}
|
||||
for _, m := range metrics {
|
||||
panel := ui.Column(c).Key(m.Name).Gap(u)
|
||||
panel := ui.Column(c.Key(m.Name)).Gap(u)
|
||||
series := ui.Local(panel, "mujica.monitor.series", func() []chart.ChartSeries { return []chart.ChartSeries{{Name: m.Name, Values: m.Samples}} })
|
||||
(*series)[0].Name, (*series)[0].Values = m.Name, m.Samples
|
||||
panel.Children(func() {
|
||||
|
||||
@@ -142,7 +142,7 @@ func traceSpanRow(c *ui.Context, s WaterfallSpan, depth int, total time.Duration
|
||||
k := core.Tokens(c)
|
||||
d := core.Density(c)
|
||||
u := d.Unit()
|
||||
row := ui.ButtonBase(c).Key(s.ID).Justify(ui.Start).Gap(u*2).MinHeight(d.ControlHeight()).Padding(0, u*2).
|
||||
row := ui.ButtonBase(c.Key(s.ID)).Justify(ui.Start).Gap(u*2).MinHeight(d.ControlHeight()).Padding(0, u*2).
|
||||
Label(fmt.Sprintf("%s, %s, %s", s.Name, s.Service, agent.AgentDuration(s.Duration)))
|
||||
internal.Ring(row, k.Focus, theme.ControlRadius)
|
||||
if selected {
|
||||
|
||||
Reference in new issue
Block a user