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>
74 lines
6.1 KiB
Markdown
74 lines
6.1 KiB
Markdown
# AGENTS.md
|
||
|
||
MujicaUI: court-gothic components for MyGo native UI. Go library, module `git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI`, one package per component category.
|
||
Progress truth: `TASKS.md`. Requirements: `PRD.md` and `docs/prd/ext-*.md` (101–500).
|
||
Planned: once 001–500 land, the root package splits into category sub-packages (TASKS T22). Until then everything stays in the root package.
|
||
|
||
## Stack
|
||
|
||
| Item | Value |
|
||
| --- | --- |
|
||
| Go | 1.27.1 (MyGo's minimum) |
|
||
| MyGo | `github.com/egoist/mygo v0.5.5` (commit 4d8673c), pinned in go.mod |
|
||
| Icons | Lucide 1.52.0, ISC, copied into `icons/svg` |
|
||
| Web mirror | `examples/gallery -web :8080`: Go stdlib server drives the Gallery through `ui.NewTester` and streams frames to a canvas. The browser shows pixels; Go runs the components. |
|
||
|
||
Read MyGo source before using an API: `go env GOMODCACHE`/github.com/egoist/mygo@v0.5.5 (`ui/*.go`, `docs/ui/*.md`, migration guide `docs/ui/migration.md`). Never guess.
|
||
|
||
## Layout
|
||
|
||
```text
|
||
core/ Use, Tokens, Density, Motion, FontSize, Msg; shared result types and helpers;
|
||
messages_<group>.go built-in copy (en-US, zh-CN) via addMessages; docs tests
|
||
<category>/ one component per file, package = folder name:
|
||
layout display input datetime navigation data overlay feedback chart
|
||
finance chat agent git code media project files messaging devtools account
|
||
<category>/*_test.go tests beside the component; example_<group>_test.go compiled doc examples
|
||
theme/ tokens, density, type, motion, contrast
|
||
icons/ embedded Lucide icons: icons.Must("check")
|
||
internal/ focus ring, ornaments; internal/snaptest runs snapshot goldens
|
||
examples/gallery/ native Gallery; pages_<group>.go per PRD 4.x group
|
||
docs/components/ NNN-name.md, one page per component
|
||
testdata/snapshots/ <os>/<name>-<light|dark>.png, shared by every package
|
||
```
|
||
|
||
Gallery categories map to packages: Inputs, Choices, Forms, Essentials -> input; Date & Time, Calendar -> datetime; Feedback, Agent Ops & Motion -> feedback; Charts I, Charts II, Market Charts -> chart; Trading, Markets -> finance; Chat Messages, Chat Input, Chat Sessions -> chat; Terminal & Code -> code; Projects -> project; Files & Collaboration -> files; Messaging & Mail -> messaging; Dashboards & Dev Tools -> devtools; Settings & Account -> account. Imports point down only: core imports no category; a piece two categories need moves to core or internal.
|
||
|
||
## Component rules
|
||
|
||
- Signature: `func Name(c *ui.Context, value *T, opts NameOptions) ui.Element`. Composites may return a named struct.
|
||
- Result structs expose the element as a named field `Element ui.Element`, never embedded; element plus changed is `ChoiceResult`.
|
||
- Shared helpers: `iconAction` (borderless icon button), `overlayStyle(c, panel, overlayEnter(c, panel))` (popup surface and entry fade), `internal.RuleDiamond` / `internal.Frame` (ornaments), `colorFade` (state color transition).
|
||
- Zero `Options` gives the default look. No `map[string]any` config.
|
||
- Explicit font sizes: `FontSize(c, theme.CaptionSize)`; it applies the desktop text scale.
|
||
- Colors: `k := core.Tokens(c)`. Metrics: `theme.*` consts and `Density(c).Unit()` / `.ControlHeight()`.
|
||
- Build on MyGo Bases (`ui.ButtonBase`, `ui.CheckboxBase`, `ui.SelectBase`, ...) or MyGo widgets restyled through the theme `Use` sets. Never reach into MyGo internals.
|
||
- Focus: `internal.Ring(e, k.Focus, radius)` for the 2 DIP ring with 2 DIP gap.
|
||
- State marks (checked, selected, progress) use `OnAccent` on `Accent`, or `AccentText`, never `Accent` alone on dark surfaces (2.44:1).
|
||
- Ornament color only for dividers, corners, panel titles. No gradients, glow, glass or texture.
|
||
- Animated paths: pass coordinates through `internal.Snap(p, v)`. MyGo caches path masks by quarter-pixel keys (ui/path.go); unsnapped motion reuses near-miss masks and breaks snapshots.
|
||
- Durations: `core.Motion(c, theme.StateDuration)`; popups use `theme.PopupDuration`.
|
||
- Disabled blocks input before handling it (`Disabled(true)`); MyGo dims disabled elements, do not add opacity.
|
||
- Copy shown by a component comes from `core.Msg(c, key)`; business copy comes from the caller.
|
||
- `ui.Element` is a handle valid only during its build pass: no cross-frame caching, no goroutines touching elements, no element methods inside `HandleInput` or other callbacks (read state such as `IsDisabled` into a local during the build). Keep a `ui.Handle` for persistent identity and `c.Services()` for clipboard or URLs from callbacks. The absent element is `ui.Element{}`; test it with `.Valid()`.
|
||
- Overlay content takes initial focus with `core.AutoFocus(c, e)`, not `e.AutoFocus()`: MyGo v0.5.5 records an overlay's opener after its content builds, so same-frame autofocus loses focus restore on close.
|
||
- Tests run on the Tester's virtual clock: `tt.Advance(d)` and `tt.Now()`, never `time.Sleep` or `time.Now()`; read element state inside the view.
|
||
- Fail fast: panic on programmer errors (bad options); never swallow errors.
|
||
- Log state transitions in composites with `slog.Debug("mujica: <component> <event>", ...)`. Never log passwords.
|
||
- Files under 500 lines. Comments: one line, only when needed.
|
||
|
||
## Tests
|
||
|
||
- `ui.NewTester` drives click, keys, typing. Assert values, not existence.
|
||
- Every component: one `snapshot(t, name, w, h, view)` call (light and dark; each package wraps `internal/snaptest`). Refresh with `go test -run <Name> -update ./<category>`
|
||
- Run `go vet ./... && go test ./...` before reporting done.
|
||
- Each snapshot test renders in a fresh child process (snapshot_test.go), because MyGo's path-mask cache is process-wide. Order and neighbours never change pixels; `-run <Name> -update` is safe.
|
||
|
||
## Gallery page
|
||
|
||
Each component adds one `page` to its group slice in `examples/gallery/pages_<group>.go`: ID, Name, ZhName, Category, Demo, Code, Keys. Demo state lives in `ui.Local(c.Root(), "<id>", ...)`. Demos call `log` for every event.
|
||
|
||
## Review
|
||
|
||
Each TASKS.md item is reviewed by `codex exec -m gpt-6.1-sol -c model_reasoning_effort=high` (max 3 rounds). This session runs outside Herdr (`HERDR_ENV` unset), so reviews run as direct `codex exec`.
|