Compare commits
4
Commits
9883af2bf3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcce34cda1
|
||
|
|
bc716d27be
|
||
|
|
8394eea44c
|
||
|
|
f7fdd2d0bd
|
No files matched your search
@@ -1,67 +0,0 @@
|
|||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ci-${{ github.ref }}
|
|
||||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: ${{ matrix.name }}
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
timeout-minutes: 30
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- { name: macOS, os: macos-latest, skip: "" }
|
|
||||||
# Snapshot goldens exist only for darwin; see docs/compat.md.
|
|
||||||
- { name: Linux, os: ubuntu-latest, skip: "-skip Snapshot" }
|
|
||||||
- { name: Windows, os: windows-latest, skip: "-skip Snapshot" }
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v7
|
|
||||||
- uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
- name: Format
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
run: test -z "$(gofmt -l .)" || { gofmt -l .; exit 1; }
|
|
||||||
- name: File size limit
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
run: |
|
|
||||||
big=$(git ls-files '*.go' '*.html' | xargs wc -l | awk '$2!="total" && $1>500')
|
|
||||||
test -z "$big" || { echo "$big"; exit 1; }
|
|
||||||
- name: Build and vet every platform
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
run: |
|
|
||||||
for os in darwin linux windows; do
|
|
||||||
GOOS=$os go build ./...
|
|
||||||
GOOS=$os go vet ./...
|
|
||||||
done
|
|
||||||
- name: Staticcheck
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
run: go run honnef.co/go/tools/cmd/staticcheck@v0.8.1 ./...
|
|
||||||
- name: Test
|
|
||||||
run: go test ${{ matrix.skip }} ./...
|
|
||||||
- name: Race
|
|
||||||
if: runner.os != 'Windows'
|
|
||||||
run: go test -race ${{ matrix.skip }} ./...
|
|
||||||
- name: README example matches examples/minimal
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
run: diff <(awk '/^```go$/{f=1;n++;next} /^```$/{f=0} f && n==1' README.md) examples/minimal/main.go
|
|
||||||
- name: Examples compile
|
|
||||||
run: go build ./examples/...
|
|
||||||
- name: Upload snapshot diffs
|
|
||||||
if: failure()
|
|
||||||
uses: actions/upload-artifact@v5
|
|
||||||
with:
|
|
||||||
name: snapshot-diffs-${{ matrix.name }}
|
|
||||||
path: testdata/snapshots/**/*.got.png
|
|
||||||
if-no-files-found: ignore
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# AGENTS.md
|
# AGENTS.md
|
||||||
|
|
||||||
MujicaUI: court-gothic components for MyGo native UI. Go library, module `github.com/ZacharyZhang-NY/MujicaUI`, one package per component category.
|
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).
|
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.
|
Planned: once 001–500 land, the root package splits into category sub-packages (TASKS T22). Until then everything stays in the root package.
|
||||||
|
|
||||||
@@ -9,11 +9,11 @@ Planned: once 001–500 land, the root package splits into category sub-packages
|
|||||||
| Item | Value |
|
| Item | Value |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Go | 1.27.1 (MyGo's minimum) |
|
| Go | 1.27.1 (MyGo's minimum) |
|
||||||
| MyGo | `github.com/egoist/mygo v0.2.10` (commit 459b512), pinned in go.mod |
|
| MyGo | `github.com/egoist/mygo v0.5.5` (commit 4d8673c), pinned in go.mod |
|
||||||
| Icons | Lucide 1.52.0, ISC, copied into `icons/svg` |
|
| 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. |
|
| 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.2.10 (`ui/*.go`, `docs/ui/*.md`). Never guess.
|
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
|
## Layout
|
||||||
|
|
||||||
@@ -36,8 +36,8 @@ Gallery categories map to packages: Inputs, Choices, Forms, Essentials -> input;
|
|||||||
|
|
||||||
## Component rules
|
## Component rules
|
||||||
|
|
||||||
- Signature: `func Name(c *ui.Context, value *T, opts NameOptions) *ui.Element`. Composites may return a named struct.
|
- 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`.
|
- 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).
|
- 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.
|
- Zero `Options` gives the default look. No `map[string]any` config.
|
||||||
- Explicit font sizes: `FontSize(c, theme.CaptionSize)`; it applies the desktop text scale.
|
- Explicit font sizes: `FontSize(c, theme.CaptionSize)`; it applies the desktop text scale.
|
||||||
@@ -50,7 +50,9 @@ Gallery categories map to packages: Inputs, Choices, Forms, Essentials -> input;
|
|||||||
- Durations: `core.Motion(c, theme.StateDuration)`; popups use `theme.PopupDuration`.
|
- 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.
|
- 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.
|
- Copy shown by a component comes from `core.Msg(c, key)`; business copy comes from the caller.
|
||||||
- No cross-frame `*ui.Element` caching, no goroutines touching elements.
|
- `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.
|
- 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.
|
- Log state transitions in composites with `slog.Debug("mujica: <component> <event>", ...)`. Never log passwords.
|
||||||
- Files under 500 lines. Comments: one line, only when needed.
|
- Files under 500 lines. Comments: one line, only when needed.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
go get github.com/ZacharyZhang-NY/MujicaUI
|
go get git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI
|
||||||
```
|
```
|
||||||
|
|
||||||
要求 Go 1.27.1 与 MyGo v0.2.10(见 [兼容表](docs/compat.md))。
|
要求 Go 1.27.1 与 MyGo v0.2.10(见 [兼容表](docs/compat.md))。
|
||||||
@@ -25,8 +25,8 @@ import (
|
|||||||
"github.com/egoist/mygo"
|
"github.com/egoist/mygo"
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
)
|
)
|
||||||
|
|
||||||
type app struct{ name string }
|
type app struct{ name string }
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccentColorPickerOptions configure an AccentColorPicker.
|
// AccentColorPickerOptions configure an AccentColorPicker.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccentColorPickerMarksLowContrast(t *testing.T) {
|
func TestAccentColorPickerMarksLowContrast(t *testing.T) {
|
||||||
|
|||||||
+15
-14
@@ -6,14 +6,14 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/data"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/display"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/overlay"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/overlay"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Account is a signed-in account a user can switch to.
|
// Account is a signed-in account a user can switch to.
|
||||||
@@ -29,7 +29,7 @@ type AccountSwitcherOptions struct {
|
|||||||
|
|
||||||
// AccountSwitcherResult reports account changes and addition requests.
|
// AccountSwitcherResult reports account changes and addition requests.
|
||||||
type AccountSwitcherResult struct {
|
type AccountSwitcherResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
changed bool
|
changed bool
|
||||||
add bool
|
add bool
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ func AccountSwitcher(c *ui.Context, current *string, accounts []Account, opts Ac
|
|||||||
if a.Email != "" {
|
if a.Email != "" {
|
||||||
label += " · " + a.Email
|
label += " · " + a.Email
|
||||||
}
|
}
|
||||||
ui.Column(c).Key(a.ID).Shrink(0).Children(func() {
|
ui.Column(c.Key(a.ID)).Shrink(0).Children(func() {
|
||||||
if m.Check(label, &on, overlay.PopupMenuItemOptions{}) && a.ID != *current && !b.IsDisabled() {
|
if m.Check(label, &on, overlay.PopupMenuItemOptions{}) && a.ID != *current && !b.IsDisabled() {
|
||||||
*current, r.changed = a.ID, true
|
*current, r.changed = a.ID, true
|
||||||
slog.Debug("mujica: account_switcher switched")
|
slog.Debug("mujica: account_switcher switched")
|
||||||
@@ -92,7 +92,7 @@ func AccountSwitcher(c *ui.Context, current *string, accounts []Account, opts Ac
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func accountSwitcherLarge(c *ui.Context, b *ui.Element, open *bool, current *string, accounts []Account, r *AccountSwitcherResult) {
|
func accountSwitcherLarge(c *ui.Context, b ui.Element, open *bool, current *string, accounts []Account, r *AccountSwitcherResult) {
|
||||||
overlay.Popover(c, b, open, overlay.PopoverOptions{Label: core.Msg(c, "account_switcher.accounts"), Width: 320}, func() {
|
overlay.Popover(c, b, open, overlay.PopoverOptions{Label: core.Msg(c, "account_switcher.accounts"), Width: 320}, func() {
|
||||||
cursor := ui.Local(b, "cursor", func() int { return -1 })
|
cursor := ui.Local(b, "cursor", func() int { return -1 })
|
||||||
rows := ui.Local(b, "list", func() ui.ListState { return ui.ListState{} })
|
rows := ui.Local(b, "list", func() ui.ListState { return ui.ListState{} })
|
||||||
@@ -100,7 +100,7 @@ func accountSwitcherLarge(c *ui.Context, b *ui.Element, open *bool, current *str
|
|||||||
rows.Label = func(i int) string { return accounts[i].Name }
|
rows.Label = func(i int) string { return accounts[i].Name }
|
||||||
rows.Selected = cursor
|
rows.Selected = cursor
|
||||||
restore := data.DataSelectTheme(c, 0)
|
restore := data.DataSelectTheme(c, 0)
|
||||||
list := ui.List(c, rows, len(accounts), func(i int) {
|
list := core.AutoFocus(c, ui.List(c, rows, len(accounts), func(i int) {
|
||||||
a := accounts[i]
|
a := accounts[i]
|
||||||
row := workspaceRow(c, Workspace{ID: a.ID, Name: a.Name, Detail: a.Email}, a.ID == *current, i == *cursor)
|
row := workspaceRow(c, Workspace{ID: a.ID, Name: a.Name, Detail: a.Email}, a.ID == *current, i == *cursor)
|
||||||
if row.Focused() && !b.IsDisabled() {
|
if row.Focused() && !b.IsDisabled() {
|
||||||
@@ -109,7 +109,8 @@ func accountSwitcherLarge(c *ui.Context, b *ui.Element, open *bool, current *str
|
|||||||
if row.Clicked() && !row.IsDisabled() && !b.IsDisabled() {
|
if row.Clicked() && !row.IsDisabled() && !b.IsDisabled() {
|
||||||
r.changed, *current, *open = a.ID != *current, a.ID, false
|
r.changed, *current, *open = a.ID != *current, a.ID, false
|
||||||
}
|
}
|
||||||
}).Height(280).MinHeight(0).AutoFocus()
|
}).Height(280).MinHeight(0))
|
||||||
|
|
||||||
restore()
|
restore()
|
||||||
internal.Ring(list, core.Tokens(c).Focus, theme.ControlRadius)
|
internal.Ring(list, core.Tokens(c).Focus, theme.ControlRadius)
|
||||||
if list.Changed() && !b.IsDisabled() {
|
if list.Changed() && !b.IsDisabled() {
|
||||||
@@ -129,7 +130,7 @@ func accountSwitcherLarge(c *ui.Context, b *ui.Element, open *bool, current *str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// accountTrigger displays an avatar, name and detail.
|
// accountTrigger displays an avatar, name and detail.
|
||||||
func accountTrigger(c *ui.Context, name, detail string, avatar *ui.Bitmap, disabled bool) *ui.Element {
|
func accountTrigger(c *ui.Context, name, detail string, avatar *ui.Bitmap, disabled bool) ui.Element {
|
||||||
k := core.Tokens(c)
|
k := core.Tokens(c)
|
||||||
d := core.Density(c)
|
d := core.Density(c)
|
||||||
b := ui.ButtonBase(c).Role(ui.RoleMenuButton).MinHeight(d.ControlHeight()+d.Unit()*2).Padding(d.Unit(), d.Unit()*3, d.Unit(), d.Unit()*2).
|
b := ui.ButtonBase(c).Role(ui.RoleMenuButton).MinHeight(d.ControlHeight()+d.Unit()*2).Padding(d.Unit(), d.Unit()*3, d.Unit(), d.Unit()*2).
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/feedback"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/feedback"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/overlay"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/overlay"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// APIKey contains public metadata and the visible prefix.
|
// APIKey contains public metadata and the visible prefix.
|
||||||
@@ -57,7 +57,7 @@ type ApiKeyManagerOptions struct {
|
|||||||
|
|
||||||
// ApiKeyManagerResult reports creation and revocation requests.
|
// ApiKeyManagerResult reports creation and revocation requests.
|
||||||
type ApiKeyManagerResult struct {
|
type ApiKeyManagerResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
create, revoke string
|
create, revoke string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ func ApiKeyManager(c *ui.Context, s *ApiKeyManagerState, keys []APIKey, opts Api
|
|||||||
}
|
}
|
||||||
accountRows(c, len(keys), func(i int) any { return keys[i].ID }, func(i int) {
|
accountRows(c, len(keys), func(i int) any { return keys[i].ID }, func(i int) {
|
||||||
key := keys[i]
|
key := keys[i]
|
||||||
ui.Row(c).Key(key.ID).MinHeight(d.ControlHeight()+d.Unit()*4).Gap(d.Unit()*3).AlignItems(ui.Center).
|
ui.Row(c.Key(key.ID)).MinHeight(d.ControlHeight()+d.Unit()*4).Gap(d.Unit()*3).AlignItems(ui.Center).
|
||||||
BorderWidth(0, 0, theme.BorderWidth, 0).BorderColor(k.Border).Children(func() {
|
BorderWidth(0, 0, theme.BorderWidth, 0).BorderColor(k.Border).Children(func() {
|
||||||
ui.Column(c).Grow(1).MinWidth(0).Gap(2).Children(func() {
|
ui.Column(c).Grow(1).MinWidth(0).Gap(2).Children(func() {
|
||||||
ui.Text(c, key.Name).FontWeight(600).SingleLine()
|
ui.Text(c, key.Name).FontWeight(600).SingleLine()
|
||||||
@@ -191,7 +191,7 @@ func apiKeyCreateDialog(c *ui.Context, s *ApiKeyManagerState, disabled bool) (st
|
|||||||
}
|
}
|
||||||
res := input.TextInput(c, &s.name, input.TextInputOptions{Label: core.Msg(c, "api_key_manager.name"), Placeholder: core.Msg(c, "api_key_manager.name"),
|
res := input.TextInput(c, &s.name, input.TextInputOptions{Label: core.Msg(c, "api_key_manager.name"), Placeholder: core.Msg(c, "api_key_manager.name"),
|
||||||
Disabled: s.pending, Error: s.err})
|
Disabled: s.pending, Error: s.err})
|
||||||
res.Input.AutoFocus()
|
core.AutoFocus(c, res.Input)
|
||||||
if res.Submitted() && strings.TrimSpace(s.name) != "" && !s.pending {
|
if res.Submitted() && strings.TrimSpace(s.name) != "" && !s.pending {
|
||||||
asked = true
|
asked = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,21 +6,21 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/overlay"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/overlay"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CoachmarkStep uses an anchor built in the current frame.
|
// CoachmarkStep uses an anchor built in the current frame.
|
||||||
type CoachmarkStep struct {
|
type CoachmarkStep struct {
|
||||||
Anchor *ui.Element
|
Anchor ui.Element
|
||||||
Title, Body string
|
Title, Body string
|
||||||
}
|
}
|
||||||
|
|
||||||
// CoachmarkResult reports tour completion or dismissal.
|
// CoachmarkResult reports tour completion or dismissal.
|
||||||
type CoachmarkResult struct {
|
type CoachmarkResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
finished, dismissed bool
|
finished, dismissed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ func Coachmark(c *ui.Context, open *bool, step *int, steps []CoachmarkStep) Coac
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
cur := steps[*step]
|
cur := steps[*step]
|
||||||
if cur.Anchor == nil || cur.Title == "" {
|
if !cur.Anchor.Valid() || cur.Title == "" {
|
||||||
panic("mujica: CoachmarkStep needs an Anchor and a Title")
|
panic("mujica: CoachmarkStep needs an Anchor and a Title")
|
||||||
}
|
}
|
||||||
if cur.Anchor.IsDisabled() {
|
if cur.Anchor.IsDisabled() {
|
||||||
@@ -76,7 +76,7 @@ func Coachmark(c *ui.Context, open *bool, step *int, steps []CoachmarkStep) Coac
|
|||||||
if last {
|
if last {
|
||||||
next = core.Msg(c, "coachmark.done")
|
next = core.Msg(c, "coachmark.done")
|
||||||
}
|
}
|
||||||
if input.Button(c, next, input.ButtonOptions{}).AutoFocus().Clicked() {
|
if core.AutoFocus(c, input.Button(c, next, input.ButtonOptions{})).Clicked() {
|
||||||
if last {
|
if last {
|
||||||
*open, r.finished = false, true
|
*open, r.finished = false, true
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
)
|
)
|
||||||
|
|
||||||
func coachmarkView(c *ui.Context, open *bool, step *int, r *CoachmarkResult) {
|
func coachmarkView(c *ui.Context, open *bool, step *int, r *CoachmarkResult) {
|
||||||
var a, b *ui.Element
|
var a, b ui.Element
|
||||||
ui.Row(c).Gap(40).Padding(20).Children(func() {
|
ui.Row(c).Gap(40).Padding(20).Children(func() {
|
||||||
a = input.Button(c, "Compose", input.ButtonOptions{})
|
a = input.Button(c, "Compose", input.ButtonOptions{})
|
||||||
b = input.Button(c, "Archive", input.ButtonOptions{Variant: input.Secondary})
|
b = input.Button(c, "Archive", input.ButtonOptions{Variant: input.Secondary})
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/account"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/account"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleSettingsLayout() {
|
func ExampleSettingsLayout() {
|
||||||
@@ -19,7 +19,7 @@ func ExampleSettingsLayout() {
|
|||||||
core.Use(c, core.Settings{})
|
core.Use(c, core.Settings{})
|
||||||
account.SettingsLayout(c, &page, pages, account.SettingsLayoutOptions{}, func(id string) {
|
account.SettingsLayout(c, &page, pages, account.SettingsLayoutOptions{}, func(id string) {
|
||||||
account.SettingsSection(c, "Notifications", func() {
|
account.SettingsSection(c, "Notifications", func() {
|
||||||
account.SettingsRow(c, "Sound", account.SettingsRowOptions{ID: "sound"}, func() *ui.Element {
|
account.SettingsRow(c, "Sound", account.SettingsRowOptions{ID: "sound"}, func() ui.Element {
|
||||||
return input.Switch(c, &sound, "", input.SwitchOptions{})
|
return input.Switch(c, &sound, "", input.SwitchOptions{})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/navigation"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/navigation"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FeedbackKind is what the feedback is about.
|
// FeedbackKind is what the feedback is about.
|
||||||
@@ -53,7 +53,7 @@ type FeedbackWidgetOptions struct {
|
|||||||
|
|
||||||
// FeedbackWidgetResult carries the submitted feedback.
|
// FeedbackWidgetResult carries the submitted feedback.
|
||||||
type FeedbackWidgetResult struct {
|
type FeedbackWidgetResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
feedback Feedback
|
feedback Feedback
|
||||||
submitted bool
|
submitted bool
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ func FeedbackWidget(c *ui.Context, s *FeedbackWidgetState, opts FeedbackWidgetOp
|
|||||||
return fmt.Sprintf(core.Msg(c, "feedback_widget.short"), minLen)
|
return fmt.Sprintf(core.Msg(c, "feedback_widget.short"), minLen)
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}}, func() *ui.Element {
|
}}, func() ui.Element {
|
||||||
return input.TextArea(c, &s.Draft.Text, input.TextAreaOptions{MinHeight: 96, MaxLength: 2000, Disabled: locked}).Input
|
return input.TextArea(c, &s.Draft.Text, input.TextAreaOptions{MinHeight: 96, MaxLength: 2000, Disabled: locked}).Input
|
||||||
})
|
})
|
||||||
if opts.Attachments != nil {
|
if opts.Attachments != nil {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFeedbackWidgetValidatesSubmitsOnce(t *testing.T) {
|
func TestFeedbackWidgetValidatesSubmitsOnce(t *testing.T) {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal/snaptest"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal/snaptest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// snapshot compares view in both themes with testdata/snapshots/<os>/<name>-<mode>.png.
|
// snapshot compares view in both themes with testdata/snapshots/<os>/<name>-<mode>.png.
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/feedback"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/feedback"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ShortcutGroup is a titled group of bindings in a KeyboardShortcutsList.
|
// ShortcutGroup is a titled group of bindings in a KeyboardShortcutsList.
|
||||||
@@ -35,7 +35,7 @@ type shortcutsListState struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// KeyboardShortcutsList displays searchable, grouped shortcuts using visible rows.
|
// KeyboardShortcutsList displays searchable, grouped shortcuts using visible rows.
|
||||||
func KeyboardShortcutsList(c *ui.Context, query *string, groups []ShortcutGroup, opts KeyboardShortcutsListOptions) *ui.Element {
|
func KeyboardShortcutsList(c *ui.Context, query *string, groups []ShortcutGroup, opts KeyboardShortcutsListOptions) ui.Element {
|
||||||
k := core.Tokens(c)
|
k := core.Tokens(c)
|
||||||
d := core.Density(c)
|
d := core.Density(c)
|
||||||
label := opts.Label
|
label := opts.Label
|
||||||
@@ -96,11 +96,11 @@ func shortcutsListPlan(st *shortcutsListState, groups []ShortcutGroup, q string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func accountRows(c *ui.Context, n int, key func(int) any, row func(int)) *ui.Element {
|
func accountRows(c *ui.Context, n int, key func(int) any, row func(int)) ui.Element {
|
||||||
if n <= 8 {
|
if n <= 8 {
|
||||||
return ui.Column(c).Children(func() {
|
return ui.Column(c).Children(func() {
|
||||||
for i := range n {
|
for i := range n {
|
||||||
ui.Column(c).Key(key(i)).Children(func() { row(i) })
|
ui.Column(c.Key(key(i))).Children(func() { row(i) })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LoginFormState holds the form; only OnSubmit sees the password.
|
// LoginFormState holds the form; only OnSubmit sees the password.
|
||||||
@@ -40,7 +40,7 @@ type LoginFormOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoginForm collects credentials; it never checks or stores them.
|
// LoginForm collects credentials; it never checks or stores them.
|
||||||
func LoginForm(c *ui.Context, s *LoginFormState, opts LoginFormOptions) *ui.Element {
|
func LoginForm(c *ui.Context, s *LoginFormState, opts LoginFormOptions) ui.Element {
|
||||||
if opts.OnSubmit == nil {
|
if opts.OnSubmit == nil {
|
||||||
panic("mujica: LoginForm needs OnSubmit")
|
panic("mujica: LoginForm needs OnSubmit")
|
||||||
}
|
}
|
||||||
@@ -61,11 +61,11 @@ func LoginForm(c *ui.Context, s *LoginFormState, opts LoginFormOptions) *ui.Elem
|
|||||||
submit := false
|
submit := false
|
||||||
var acc, pw input.InputResult
|
var acc, pw input.InputResult
|
||||||
e.Children(func() {
|
e.Children(func() {
|
||||||
input.FormField(c, accountLabel, input.FormFieldOptions{Required: true}, func() *ui.Element {
|
input.FormField(c, accountLabel, input.FormFieldOptions{Required: true}, func() ui.Element {
|
||||||
acc = input.TextInput(c, &s.Account, input.TextInputOptions{Disabled: disabled || s.submitting, Error: missing(s.Account)})
|
acc = input.TextInput(c, &s.Account, input.TextInputOptions{Disabled: disabled || s.submitting, Error: missing(s.Account)})
|
||||||
return acc.Input
|
return acc.Input
|
||||||
})
|
})
|
||||||
input.FormField(c, core.Msg(c, "login_form.password"), input.FormFieldOptions{Required: true}, func() *ui.Element {
|
input.FormField(c, core.Msg(c, "login_form.password"), input.FormFieldOptions{Required: true}, func() ui.Element {
|
||||||
pw = input.PasswordInput(c, &s.password, input.PasswordInputOptions{Disabled: disabled || s.submitting, Error: missing(s.password)})
|
pw = input.PasswordInput(c, &s.password, input.PasswordInputOptions{Disabled: disabled || s.submitting, Error: missing(s.password)})
|
||||||
return pw.Input
|
return pw.Input
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OAuthProvider is a sign-in provider; icons are plain, not trademarks.
|
// OAuthProvider is a sign-in provider; icons are plain, not trademarks.
|
||||||
@@ -25,7 +25,7 @@ type OAuthButtonsOptions struct {
|
|||||||
|
|
||||||
// OAuthButtonsResult carries the selected provider.
|
// OAuthButtonsResult carries the selected provider.
|
||||||
type OAuthButtonsResult struct {
|
type OAuthButtonsResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
chosen string
|
chosen string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ func OAuthButtons(c *ui.Context, providers []OAuthProvider, opts OAuthButtonsOpt
|
|||||||
panic("mujica: OAuthProvider needs an ID and a Name")
|
panic("mujica: OAuthProvider needs an ID and a Name")
|
||||||
}
|
}
|
||||||
label := fmt.Sprintf(core.Msg(c, "oauth_buttons.continue"), p.Name)
|
label := fmt.Sprintf(core.Msg(c, "oauth_buttons.continue"), p.Name)
|
||||||
ui.Column(c).Key(p.ID).AlignItems(ui.Stretch).Children(func() {
|
ui.Column(c.Key(p.ID)).AlignItems(ui.Stretch).Children(func() {
|
||||||
b := input.Button(c, label, input.ButtonOptions{Variant: input.Secondary, Icon: p.Icon, Loading: opts.Pending == p.ID,
|
b := input.Button(c, label, input.ButtonOptions{Variant: input.Secondary, Icon: p.Icon, Loading: opts.Pending == p.ID,
|
||||||
Disabled: opts.Pending != "" && opts.Pending != p.ID})
|
Disabled: opts.Pending != "" && opts.Pending != p.ID})
|
||||||
if b.Clicked() && !disabled && opts.Pending == "" {
|
if b.Clicked() && !disabled && opts.Pending == "" {
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/navigation"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/navigation"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WizardStep is one page of an OnboardingWizard.
|
// WizardStep is one page of an OnboardingWizard.
|
||||||
@@ -28,7 +28,7 @@ type OnboardingWizardOptions struct {
|
|||||||
|
|
||||||
// OnboardingWizardResult reports completion or skipping.
|
// OnboardingWizardResult reports completion or skipping.
|
||||||
type OnboardingWizardResult struct {
|
type OnboardingWizardResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
finished, skipped bool
|
finished, skipped bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ func OnboardingWizard(c *ui.Context, step *int, steps []WizardStep, opts Onboard
|
|||||||
at := *step
|
at := *step
|
||||||
navigation.Steps(c, &at, head, navigation.StepsOptions{Label: core.Msg(c, "onboarding_wizard.progress")})
|
navigation.Steps(c, &at, head, navigation.StepsOptions{Label: core.Msg(c, "onboarding_wizard.progress")})
|
||||||
cur := steps[*step]
|
cur := steps[*step]
|
||||||
ui.Column(c).Key(*step).Gap(d.Unit() * 3).Children(func() {
|
ui.Column(c.Key(*step)).Gap(d.Unit() * 3).Children(func() {
|
||||||
ui.Text(c, fmt.Sprintf(core.Msg(c, "onboarding_wizard.count"), *step+1, len(steps))).TextColor(k.TextMuted).FontSize(core.FontSize(c, theme.CaptionSize))
|
ui.Text(c, fmt.Sprintf(core.Msg(c, "onboarding_wizard.count"), *step+1, len(steps))).TextColor(k.TextMuted).FontSize(core.FontSize(c, theme.CaptionSize))
|
||||||
ui.Text(c, cur.Title).Font(theme.SerifFont).FontSize(core.FontSize(c, theme.H2Size)).FontWeight(600)
|
ui.Text(c, cur.Title).Font(theme.SerifFont).FontSize(core.FontSize(c, theme.H2Size)).FontWeight(600)
|
||||||
if cur.Description != "" {
|
if cur.Description != "" {
|
||||||
@@ -84,7 +84,7 @@ func OnboardingWizard(c *ui.Context, step *int, steps []WizardStep, opts Onboard
|
|||||||
if last {
|
if last {
|
||||||
next = core.Msg(c, "onboarding_wizard.finish")
|
next = core.Msg(c, "onboarding_wizard.finish")
|
||||||
}
|
}
|
||||||
if input.Button(c, next, input.ButtonOptions{Disabled: !ready}).AutoFocus().Clicked() && ready {
|
if core.AutoFocus(c, input.Button(c, next, input.ButtonOptions{Disabled: !ready})).Clicked() && ready {
|
||||||
if last {
|
if last {
|
||||||
r.finished = true
|
r.finished = true
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOnboardingWizardStepsSkipFinish(t *testing.T) {
|
func TestOnboardingWizardStepsSkipFinish(t *testing.T) {
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/display"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Profile is what a ProfileEditor edits.
|
// Profile is what a ProfileEditor edits.
|
||||||
@@ -34,7 +34,7 @@ type ProfileEditorOptions struct {
|
|||||||
|
|
||||||
// ProfileEditorResult reports save, avatar and discard requests.
|
// ProfileEditorResult reports save, avatar and discard requests.
|
||||||
type ProfileEditorResult struct {
|
type ProfileEditorResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
save, avatar, reset bool
|
save, avatar, reset bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,10 +73,10 @@ func ProfileEditor(c *ui.Context, p *Profile, opts ProfileEditorOptions) Profile
|
|||||||
r.avatar = true
|
r.avatar = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
input.FormField(c, core.Msg(c, "profile_editor.name"), input.FormFieldOptions{Required: true}, func() *ui.Element {
|
input.FormField(c, core.Msg(c, "profile_editor.name"), input.FormFieldOptions{Required: true}, func() ui.Element {
|
||||||
return input.TextInput(c, &p.Name, input.TextInputOptions{Disabled: locked, Error: nameErr}).Input
|
return input.TextInput(c, &p.Name, input.TextInputOptions{Disabled: locked, Error: nameErr}).Input
|
||||||
})
|
})
|
||||||
input.FormField(c, core.Msg(c, "profile_editor.bio"), input.FormFieldOptions{}, func() *ui.Element {
|
input.FormField(c, core.Msg(c, "profile_editor.bio"), input.FormFieldOptions{}, func() ui.Element {
|
||||||
return input.TextArea(c, &p.Bio, input.TextAreaOptions{MaxLength: limit, Disabled: locked}).Input
|
return input.TextArea(c, &p.Bio, input.TextAreaOptions{MaxLength: limit, Disabled: locked}).Input
|
||||||
})
|
})
|
||||||
ui.Row(c).Gap(d.Unit() * 3).AlignItems(ui.Center).Children(func() {
|
ui.Row(c).Gap(d.Unit() * 3).AlignItems(ui.Center).Children(func() {
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/display"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/overlay"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/overlay"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Session describes a device without carrying its authentication token.
|
// Session describes a device without carrying its authentication token.
|
||||||
@@ -30,7 +30,7 @@ type SessionListOptions struct {
|
|||||||
|
|
||||||
// SessionListResult reports individual and bulk sign-out requests.
|
// SessionListResult reports individual and bulk sign-out requests.
|
||||||
type SessionListResult struct {
|
type SessionListResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
revoke string
|
revoke string
|
||||||
others bool
|
others bool
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ func SessionList(c *ui.Context, sessions []Session, opts SessionListOptions) Ses
|
|||||||
ui.Column(c).Role(ui.RoleList).Label(core.Msg(c, "session_list.label")).Children(func() {
|
ui.Column(c).Role(ui.RoleList).Label(core.Msg(c, "session_list.label")).Children(func() {
|
||||||
accountRows(c, len(sessions), func(i int) any { return sessions[i].ID }, func(i int) {
|
accountRows(c, len(sessions), func(i int) any { return sessions[i].ID }, func(i int) {
|
||||||
s := sessions[i]
|
s := sessions[i]
|
||||||
ui.Row(c).Key(s.ID).MinHeight(d.ControlHeight()+d.Unit()*4).Gap(d.Unit()*3).AlignItems(ui.Center).
|
ui.Row(c.Key(s.ID)).MinHeight(d.ControlHeight()+d.Unit()*4).Gap(d.Unit()*3).AlignItems(ui.Center).
|
||||||
BorderWidth(0, 0, theme.BorderWidth, 0).BorderColor(k.Border).Role(ui.RoleListItem).Children(func() {
|
BorderWidth(0, 0, theme.BorderWidth, 0).BorderColor(k.Border).Role(ui.RoleListItem).Children(func() {
|
||||||
ui.Column(c).Grow(1).MinWidth(0).Gap(2).Children(func() {
|
ui.Column(c).Grow(1).MinWidth(0).Gap(2).Children(func() {
|
||||||
ui.Row(c).Gap(d.Unit() * 2).AlignItems(ui.Center).Children(func() {
|
ui.Row(c).Gap(d.Unit() * 2).AlignItems(ui.Center).Children(func() {
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/navigation"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/navigation"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SettingsPage is one page of a SettingsLayout's navigation.
|
// SettingsPage is one page of a SettingsLayout's navigation.
|
||||||
@@ -58,14 +58,14 @@ func SettingsLayout(c *ui.Context, page *string, pages []SettingsPage, opts Sett
|
|||||||
slog.Debug("mujica: settings_layout page", "page", *page)
|
slog.Debug("mujica: settings_layout page", "page", *page)
|
||||||
}
|
}
|
||||||
ui.Scroll(c).Grow(1).MinWidth(0).Padding(d.Unit()*6, d.Unit()*8).Children(func() {
|
ui.Scroll(c).Grow(1).MinWidth(0).Padding(d.Unit()*6, d.Unit()*8).Children(func() {
|
||||||
ui.Column(c).Key(*page).MaxWidth(720).Gap(d.Unit() * 8).Children(func() { content(*page) })
|
ui.Column(c.Key(*page)).MaxWidth(720).Gap(d.Unit() * 8).Children(func() { content(*page) })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// SettingsSection groups rows under a serif heading.
|
// SettingsSection groups rows under a serif heading.
|
||||||
func SettingsSection(c *ui.Context, title string, rows func()) *ui.Element {
|
func SettingsSection(c *ui.Context, title string, rows func()) ui.Element {
|
||||||
k := core.Tokens(c)
|
k := core.Tokens(c)
|
||||||
d := core.Density(c)
|
d := core.Density(c)
|
||||||
e := ui.Column(c).Gap(d.Unit() * 2).Role(ui.RoleGroup).Label(title)
|
e := ui.Column(c).Gap(d.Unit() * 2).Role(ui.RoleGroup).Label(title)
|
||||||
@@ -94,7 +94,7 @@ func settingsReveal(c *ui.Context, id string) {
|
|||||||
type settingsRowState struct{ at time.Time }
|
type settingsRowState struct{ at time.Time }
|
||||||
|
|
||||||
// SettingsRow associates a label and description with its control.
|
// SettingsRow associates a label and description with its control.
|
||||||
func SettingsRow(c *ui.Context, label string, opts SettingsRowOptions, control func() *ui.Element) *ui.Element {
|
func SettingsRow(c *ui.Context, label string, opts SettingsRowOptions, control func() ui.Element) ui.Element {
|
||||||
if label == "" || control == nil {
|
if label == "" || control == nil {
|
||||||
panic("mujica: SettingsRow needs a label and a control")
|
panic("mujica: SettingsRow needs a label and a control")
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ func SettingsRow(c *ui.Context, label string, opts SettingsRowOptions, control f
|
|||||||
}
|
}
|
||||||
st := ui.Local(row, "reveal", func() settingsRowState { return settingsRowState{} })
|
st := ui.Local(row, "reveal", func() settingsRowState { return settingsRowState{} })
|
||||||
reveal := ui.Local(c.Root(), settingsRevealKey{}, func() string { return "" })
|
reveal := ui.Local(c.Root(), settingsRevealKey{}, func() string { return "" })
|
||||||
var ctrl, text *ui.Element
|
var ctrl, text ui.Element
|
||||||
row.Children(func() {
|
row.Children(func() {
|
||||||
text = ui.Column(c).Grow(1).MinWidth(0).Gap(d.Unit())
|
text = ui.Column(c).Grow(1).MinWidth(0).Gap(d.Unit())
|
||||||
text.Children(func() {
|
text.Children(func() {
|
||||||
@@ -117,7 +117,7 @@ func SettingsRow(c *ui.Context, label string, opts SettingsRowOptions, control f
|
|||||||
})
|
})
|
||||||
ui.Row(c).Shrink(0).Children(func() { ctrl = control() })
|
ui.Row(c).Shrink(0).Children(func() { ctrl = control() })
|
||||||
})
|
})
|
||||||
if ctrl == nil {
|
if !ctrl.Valid() {
|
||||||
panic("mujica: SettingsRow " + label + ": control returned nil")
|
panic("mujica: SettingsRow " + label + ": control returned nil")
|
||||||
}
|
}
|
||||||
ctrl.Label(label).Description(opts.Description)
|
ctrl.Label(label).Description(opts.Description)
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
)
|
)
|
||||||
|
|
||||||
// accountTester runs a themed view inside a padded column.
|
// accountTester runs a themed view inside a padded column.
|
||||||
@@ -24,13 +24,13 @@ func settingsView(c *ui.Context, page *string, sound, track *bool, changes *int)
|
|||||||
switch id {
|
switch id {
|
||||||
case "general":
|
case "general":
|
||||||
SettingsSection(c, "Notifications", func() {
|
SettingsSection(c, "Notifications", func() {
|
||||||
SettingsRow(c, "Sound", SettingsRowOptions{ID: "sound", Description: "Chime on new messages."}, func() *ui.Element {
|
SettingsRow(c, "Sound", SettingsRowOptions{ID: "sound", Description: "Chime on new messages."}, func() ui.Element {
|
||||||
return input.Switch(c, sound, "", input.SwitchOptions{})
|
return input.Switch(c, sound, "", input.SwitchOptions{})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
case "privacy":
|
case "privacy":
|
||||||
SettingsSection(c, "Data", func() {
|
SettingsSection(c, "Data", func() {
|
||||||
SettingsRow(c, "Usage reports", SettingsRowOptions{ID: "track"}, func() *ui.Element {
|
SettingsRow(c, "Usage reports", SettingsRowOptions{ID: "track"}, func() ui.Element {
|
||||||
return input.Switch(c, track, "", input.SwitchOptions{})
|
return input.Switch(c, track, "", input.SwitchOptions{})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SettingEntry identifies a searchable setting and its destination.
|
// SettingEntry identifies a searchable setting and its destination.
|
||||||
@@ -32,7 +32,7 @@ type SettingsSearchOptions struct {
|
|||||||
|
|
||||||
// SettingsSearchResult carries the selected destination.
|
// SettingsSearchResult carries the selected destination.
|
||||||
type SettingsSearchResult struct {
|
type SettingsSearchResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
chosen *SettingEntry
|
chosen *SettingEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ func settingsSearchPlan(st *settingsSearchState, entries []SettingEntry, q strin
|
|||||||
func settingsSearchHit(c *ui.Context, e SettingEntry, q string, current bool) bool {
|
func settingsSearchHit(c *ui.Context, e SettingEntry, q string, current bool) bool {
|
||||||
k := core.Tokens(c)
|
k := core.Tokens(c)
|
||||||
d := core.Density(c)
|
d := core.Density(c)
|
||||||
b := ui.ButtonBase(c).Key(e.ID).MinHeight(d.ControlHeight()).Padding(d.Unit(), d.Unit()*3).Justify(ui.Start).
|
b := ui.ButtonBase(c.Key(e.ID)).MinHeight(d.ControlHeight()).Padding(d.Unit(), d.Unit()*3).Justify(ui.Start).
|
||||||
Radius(theme.ControlRadius).Role(ui.RoleListItem).Label(e.Label).Description(e.Description).Selected(current).Gap(d.Unit() * 2)
|
Radius(theme.ControlRadius).Role(ui.RoleListItem).Label(e.Label).Description(e.Description).Selected(current).Gap(d.Unit() * 2)
|
||||||
internal.Ring(b, k.Focus, theme.ControlRadius)
|
internal.Ring(b, k.Focus, theme.ControlRadius)
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
var settingEntries = []SettingEntry{
|
var settingEntries = []SettingEntry{
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/display"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Shortcut combines modifiers and a key; zero means unassigned.
|
// Shortcut combines modifiers and a key; zero means unassigned.
|
||||||
@@ -145,7 +145,7 @@ func ShortcutRecorder(c *ui.Context, value *Shortcut, opts ShortcutRecorderOptio
|
|||||||
}
|
}
|
||||||
if st.recording {
|
if st.recording {
|
||||||
b.HandleInput(func(ev ui.InputEvent) bool {
|
b.HandleInput(func(ev ui.InputEvent) bool {
|
||||||
if b.IsDisabled() {
|
if disabled {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return shortcutRecorderKey(st, ev)
|
return shortcutRecorderKey(st, ev)
|
||||||
@@ -178,7 +178,7 @@ func shortcutRecorderKey(st *shortcutRecorderState, ev ui.InputEvent) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// shortcutRecorderLook displays the shortcut or recording prompt.
|
// shortcutRecorderLook displays the shortcut or recording prompt.
|
||||||
func shortcutRecorderLook(c *ui.Context, b *ui.Element, st *shortcutRecorderState, value Shortcut, name string) {
|
func shortcutRecorderLook(c *ui.Context, b ui.Element, st *shortcutRecorderState, value Shortcut, name string) {
|
||||||
k := core.Tokens(c)
|
k := core.Tokens(c)
|
||||||
internal.Ring(b, k.Focus, theme.ControlRadius)
|
internal.Ring(b, k.Focus, theme.ControlRadius)
|
||||||
label := name + ": " + value.String()
|
label := name + ": " + value.String()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
)
|
)
|
||||||
|
|
||||||
func shortcutRecorderTester(v *Shortcut, opts *ShortcutRecorderOptions, n *int) *ui.Tester {
|
func shortcutRecorderTester(v *Shortcut, opts *ShortcutRecorderOptions, n *int) *ui.Tester {
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ThemeSelectorOptions configure a ThemeSelector.
|
// ThemeSelectorOptions configure a ThemeSelector.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestThemeSelectorPicksByPointerAndKeys(t *testing.T) {
|
func TestThemeSelectorPicksByPointerAndKeys(t *testing.T) {
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TwoFactorInputOptions configure a TwoFactorInput.
|
// TwoFactorInputOptions configure a TwoFactorInput.
|
||||||
@@ -25,7 +25,7 @@ type TwoFactorInputOptions struct {
|
|||||||
|
|
||||||
// TwoFactorResult reports completion and resend events.
|
// TwoFactorResult reports completion and resend events.
|
||||||
type TwoFactorResult struct {
|
type TwoFactorResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
completed, resend bool
|
completed, resend bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func TestTwoFactorInputPasteCompleteAndResend(t *testing.T) {
|
|||||||
if resent != 1 {
|
if resent != 1 {
|
||||||
t.Fatal("resend not reported")
|
t.Fatal("resend not reported")
|
||||||
}
|
}
|
||||||
opts.ResendAt = time.Now().Add(65 * time.Second)
|
opts.ResendAt = tt.Now().Add(65 * time.Second)
|
||||||
opts.Error = "That code has expired."
|
opts.Error = "That code has expired."
|
||||||
tt.Frame()
|
tt.Frame()
|
||||||
if !tt.HasText("Resend in 1:05") && !tt.HasText("Resend in 1:04") || !tt.HasText("That code has expired.") {
|
if !tt.HasText("Resend in 1:05") && !tt.HasText("Resend in 1:04") || !tt.HasText("That code has expired.") {
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/data"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// QuotaItem is one metered resource: Used of Limit, in Unit.
|
// QuotaItem is one metered resource: Used of Limit, in Unit.
|
||||||
@@ -29,7 +29,7 @@ type UsageQuotaOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UsageQuota displays usage bars and reset time.
|
// UsageQuota displays usage bars and reset time.
|
||||||
func UsageQuota(c *ui.Context, items []QuotaItem, opts UsageQuotaOptions) *ui.Element {
|
func UsageQuota(c *ui.Context, items []QuotaItem, opts UsageQuotaOptions) ui.Element {
|
||||||
warn := opts.Warn
|
warn := opts.Warn
|
||||||
if warn == 0 {
|
if warn == 0 {
|
||||||
warn = 0.8
|
warn = 0.8
|
||||||
@@ -54,7 +54,7 @@ func UsageQuota(c *ui.Context, items []QuotaItem, opts UsageQuotaOptions) *ui.El
|
|||||||
col, icon, note = k.Warning, "triangle-alert", core.Msg(c, "usage_quota.near")
|
col, icon, note = k.Warning, "triangle-alert", core.Msg(c, "usage_quota.near")
|
||||||
}
|
}
|
||||||
amount := fmt.Sprintf(core.Msg(c, "usage_quota.amount"), data.StatisticFormat(it.Used, 0), data.StatisticFormat(it.Limit, 0), it.Unit)
|
amount := fmt.Sprintf(core.Msg(c, "usage_quota.amount"), data.StatisticFormat(it.Used, 0), data.StatisticFormat(it.Limit, 0), it.Unit)
|
||||||
ui.Column(c).Key(i).Gap(d.Unit()).Role(ui.RoleMeter).Label(it.Label).
|
ui.Column(c.Key(i)).Gap(d.Unit()).Role(ui.RoleMeter).Label(it.Label).
|
||||||
Description(fmt.Sprintf("%s, %s%% %s", amount, data.StatisticFormat(share*100, 0), note)).Children(func() {
|
Description(fmt.Sprintf("%s, %s%% %s", amount, data.StatisticFormat(share*100, 0), note)).Children(func() {
|
||||||
ui.Row(c).Gap(d.Unit() * 2).AlignItems(ui.Center).Children(func() {
|
ui.Row(c).Gap(d.Unit() * 2).AlignItems(ui.Center).Children(func() {
|
||||||
ui.Text(c, it.Label).FontWeight(500).Grow(1)
|
ui.Text(c, it.Label).FontWeight(500).Grow(1)
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/display"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/overlay"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/overlay"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserStatus is the presence a user shows to others.
|
// UserStatus is the presence a user shows to others.
|
||||||
@@ -32,7 +32,7 @@ type UserMenuOptions struct {
|
|||||||
|
|
||||||
// UserMenuResult reports presence, settings and sign-out events.
|
// UserMenuResult reports presence, settings and sign-out events.
|
||||||
type UserMenuResult struct {
|
type UserMenuResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
changed, settings, leave bool
|
changed, settings, leave bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func TestUserMenuSnapshot(t *testing.T) {
|
|||||||
ui.Row(c).Padding(20).Gap(16).Children(func() {
|
ui.Row(c).Padding(20).Gap(16).Children(func() {
|
||||||
for s := UserStatusOnline; s <= UserStatusInvisible; s++ {
|
for s := UserStatusOnline; s <= UserStatusInvisible; s++ {
|
||||||
st := s
|
st := s
|
||||||
ui.Box(c).Key(int(s)).Children(func() { UserMenu(c, &st, UserMenuOptions{Name: "Sakiko Togawa"}) })
|
ui.Box(c.Key(int(s))).Children(func() { UserMenu(c, &st, UserMenuOptions{Name: "Sakiko Togawa"}) })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/display"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/display"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/overlay"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/overlay"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChangeKind tags a release note.
|
// ChangeKind tags a release note.
|
||||||
@@ -70,7 +70,7 @@ func WhatsNewDialog(c *ui.Context, open *bool, hide *bool, releases []Release, o
|
|||||||
r.Element = overlay.Dialog(c, open, overlay.DialogOptions{Title: title, Width: 480, Decorated: true, Actions: func() {
|
r.Element = overlay.Dialog(c, open, overlay.DialogOptions{Title: title, Width: 480, Decorated: true, Actions: func() {
|
||||||
input.Checkbox(c, hide, core.Msg(c, "whats_new_dialog.hide"), input.CheckboxOptions{})
|
input.Checkbox(c, hide, core.Msg(c, "whats_new_dialog.hide"), input.CheckboxOptions{})
|
||||||
ui.Box(c).Grow(1)
|
ui.Box(c).Grow(1)
|
||||||
if input.Button(c, core.Msg(c, "whats_new_dialog.close"), input.ButtonOptions{}).AutoFocus().Clicked() {
|
if core.AutoFocus(c, input.Button(c, core.Msg(c, "whats_new_dialog.close"), input.ButtonOptions{})).Clicked() {
|
||||||
*open = false
|
*open = false
|
||||||
}
|
}
|
||||||
}}, func() {
|
}}, func() {
|
||||||
@@ -78,7 +78,7 @@ func WhatsNewDialog(c *ui.Context, open *bool, hide *bool, releases []Release, o
|
|||||||
if rel.Version == "" {
|
if rel.Version == "" {
|
||||||
panic("mujica: Release needs a Version")
|
panic("mujica: Release needs a Version")
|
||||||
}
|
}
|
||||||
ui.Column(c).Key(i).Gap(d.Unit() * 2).Role(ui.RoleGroup).Label(rel.Version).Children(func() {
|
ui.Column(c.Key(i)).Gap(d.Unit() * 2).Role(ui.RoleGroup).Label(rel.Version).Children(func() {
|
||||||
ui.Row(c).Gap(d.Unit() * 2).AlignItems(ui.End).Children(func() {
|
ui.Row(c).Gap(d.Unit() * 2).AlignItems(ui.End).Children(func() {
|
||||||
ui.Text(c, rel.Version).Font(theme.SerifFont).FontSize(core.FontSize(c, theme.H3Size)).FontWeight(600)
|
ui.Text(c, rel.Version).Font(theme.SerifFont).FontSize(core.FontSize(c, theme.H3Size)).FontWeight(600)
|
||||||
if rel.Date != "" {
|
if rel.Date != "" {
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/overlay"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/overlay"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Workspace is a place a user can switch to.
|
// Workspace is a place a user can switch to.
|
||||||
@@ -72,7 +72,7 @@ func WorkspaceSwitcher(c *ui.Context, current *string, spaces []Workspace, opts
|
|||||||
overlay.Popover(c, b, &st.open, overlay.PopoverOptions{Width: 300, Label: core.Msg(c, "workspace_switcher.label")}, func() {
|
overlay.Popover(c, b, &st.open, overlay.PopoverOptions{Width: 300, Label: core.Msg(c, "workspace_switcher.label")}, func() {
|
||||||
res := input.SearchInput(c, &st.query, input.SearchInputOptions{Placeholder: core.Msg(c, "workspace_switcher.search"), Label: core.Msg(c, "workspace_switcher.search")})
|
res := input.SearchInput(c, &st.query, input.SearchInputOptions{Placeholder: core.Msg(c, "workspace_switcher.search"), Label: core.Msg(c, "workspace_switcher.search")})
|
||||||
in := res.Input
|
in := res.Input
|
||||||
in.AutoFocus()
|
core.AutoFocus(c, in)
|
||||||
q := strings.ToLower(strings.TrimSpace(st.query))
|
q := strings.ToLower(strings.TrimSpace(st.query))
|
||||||
if !st.ready || st.filtered != q || !slices.Equal(st.source, spaces) {
|
if !st.ready || st.filtered != q || !slices.Equal(st.source, spaces) {
|
||||||
st.source, st.filtered, st.ready = slices.Clone(spaces), q, true
|
st.source, st.filtered, st.ready = slices.Clone(spaces), q, true
|
||||||
@@ -115,10 +115,10 @@ func WorkspaceSwitcher(c *ui.Context, current *string, spaces []Workspace, opts
|
|||||||
}
|
}
|
||||||
|
|
||||||
// workspaceRow displays a clickable workspace with a selection mark.
|
// workspaceRow displays a clickable workspace with a selection mark.
|
||||||
func workspaceRow(c *ui.Context, w Workspace, current, cursor bool) *ui.Element {
|
func workspaceRow(c *ui.Context, w Workspace, current, cursor bool) ui.Element {
|
||||||
k := core.Tokens(c)
|
k := core.Tokens(c)
|
||||||
d := core.Density(c)
|
d := core.Density(c)
|
||||||
b := ui.ButtonBase(c).Key(w.ID).MinHeight(d.ControlHeight()).Padding(d.Unit(), d.Unit()*2).Gap(d.Unit() * 2).
|
b := ui.ButtonBase(c.Key(w.ID)).MinHeight(d.ControlHeight()).Padding(d.Unit(), d.Unit()*2).Gap(d.Unit() * 2).
|
||||||
Justify(ui.Start).Radius(theme.ControlRadius).Role(ui.RoleListItem).Label(w.Name).Selected(current)
|
Justify(ui.Start).Radius(theme.ControlRadius).Role(ui.RoleListItem).Label(w.Name).Selected(current)
|
||||||
internal.Ring(b, k.Focus, theme.ControlRadius)
|
internal.Ring(b, k.Focus, theme.ControlRadius)
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
+6
-6
@@ -5,10 +5,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileChange is a proposed edit; empty Old adds a file.
|
// FileChange is a proposed edit; empty Old adds a file.
|
||||||
@@ -52,7 +52,7 @@ func (dc *agentDiffCache) update(ch FileChange) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// agentDiffOf returns the cached diff of ch kept on holder.
|
// agentDiffOf returns the cached diff of ch kept on holder.
|
||||||
func agentDiffOf(holder *ui.Element, ch FileChange) *agentDiffCache {
|
func agentDiffOf(holder ui.Element, ch FileChange) *agentDiffCache {
|
||||||
dc := ui.Local(holder, "mujica.agent.diff", func() *agentDiffCache { return &agentDiffCache{} })
|
dc := ui.Local(holder, "mujica.agent.diff", func() *agentDiffCache { return &agentDiffCache{} })
|
||||||
(*dc).update(ch)
|
(*dc).update(ch)
|
||||||
return *dc
|
return *dc
|
||||||
@@ -98,7 +98,7 @@ func agentDiffRow(c *ui.Context, l internal.DiffLine) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// agentDiffView shows lines[from:to] in a capped well, virtualized when long.
|
// agentDiffView shows lines[from:to] in a capped well, virtualized when long.
|
||||||
func agentDiffView(c *ui.Context, lines []internal.DiffLine, maxHeight float32) *ui.Element {
|
func agentDiffView(c *ui.Context, lines []internal.DiffLine, maxHeight float32) ui.Element {
|
||||||
w := AgentWellRows(c, len(lines), func(i int) { agentDiffRow(c, lines[i]) }, maxHeight)
|
w := AgentWellRows(c, len(lines), func(i int) { agentDiffRow(c, lines[i]) }, maxHeight)
|
||||||
return w.Padding(core.Density(c).Unit(), 0)
|
return w.Padding(core.Density(c).Unit(), 0)
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-12
@@ -8,10 +8,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AgentState is an agent or step state; zero is pending.
|
// AgentState is an agent or step state; zero is pending.
|
||||||
@@ -67,7 +67,7 @@ func agentTone(k theme.Tokens, s AgentState) (ui.Color, *ui.SVG) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// agentMark draws s; running spins unless still.
|
// agentMark draws s; running spins unless still.
|
||||||
func agentMark(c *ui.Context, s AgentState, size float32, still bool) *ui.Element {
|
func agentMark(c *ui.Context, s AgentState, size float32, still bool) ui.Element {
|
||||||
tone, icon := agentTone(core.Tokens(c), s)
|
tone, icon := agentTone(core.Tokens(c), s)
|
||||||
if s == AgentRunning && !still {
|
if s == AgentRunning && !still {
|
||||||
return core.Spinner(c, core.SpinnerOptions{Size: size, Color: tone, Label: core.Msg(c, "agent_status.running")})
|
return core.Spinner(c, core.SpinnerOptions{Size: size, Color: tone, Label: core.Msg(c, "agent_status.running")})
|
||||||
@@ -95,7 +95,7 @@ func AgentDuration(d time.Duration) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AgentCard is the bordered surface every agent card sits on.
|
// AgentCard is the bordered surface every agent card sits on.
|
||||||
func AgentCard(c *ui.Context) *ui.Element {
|
func AgentCard(c *ui.Context) ui.Element {
|
||||||
k := core.Tokens(c)
|
k := core.Tokens(c)
|
||||||
u := core.Density(c).Unit()
|
u := core.Density(c).Unit()
|
||||||
return ui.Column(c).Shrink(0).Padding(u*3).Gap(u*2).Radius(theme.CardRadius).
|
return ui.Column(c).Shrink(0).Padding(u*3).Gap(u*2).Radius(theme.CardRadius).
|
||||||
@@ -103,12 +103,12 @@ func AgentCard(c *ui.Context) *ui.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AgentCaption is small muted text.
|
// AgentCaption is small muted text.
|
||||||
func AgentCaption(c *ui.Context, s string) *ui.Element {
|
func AgentCaption(c *ui.Context, s string) ui.Element {
|
||||||
return ui.Text(c, s).FontSize(core.FontSize(c, theme.CaptionSize)).TextColor(core.Tokens(c).TextMuted)
|
return ui.Text(c, s).FontSize(core.FontSize(c, theme.CaptionSize)).TextColor(core.Tokens(c).TextMuted)
|
||||||
}
|
}
|
||||||
|
|
||||||
// agentWell styles a sunken monospace well.
|
// agentWell styles a sunken monospace well.
|
||||||
func agentWell(c *ui.Context, l *ui.Element, maxHeight float32) *ui.Element {
|
func agentWell(c *ui.Context, l ui.Element, maxHeight float32) ui.Element {
|
||||||
k := core.Tokens(c)
|
k := core.Tokens(c)
|
||||||
u := core.Density(c).Unit()
|
u := core.Density(c).Unit()
|
||||||
return l.Shrink(1).MaxHeight(maxHeight).Padding(u*2, u*3).Radius(theme.ControlRadius).
|
return l.Shrink(1).MaxHeight(maxHeight).Padding(u*2, u*3).Radius(theme.ControlRadius).
|
||||||
@@ -117,12 +117,12 @@ func agentWell(c *ui.Context, l *ui.Element, maxHeight float32) *ui.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AgentWellRows builds n rows in a well.
|
// AgentWellRows builds n rows in a well.
|
||||||
func AgentWellRows(c *ui.Context, n int, row func(i int), maxHeight float32) *ui.Element {
|
func AgentWellRows(c *ui.Context, n int, row func(i int), maxHeight float32) ui.Element {
|
||||||
return agentWell(c, AgentRows(c, n, row), maxHeight)
|
return agentWell(c, AgentRows(c, n, row), maxHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AgentRows builds n scrolling rows, virtualized when many.
|
// AgentRows builds n scrolling rows, virtualized when many.
|
||||||
func AgentRows(c *ui.Context, n int, row func(i int)) *ui.Element {
|
func AgentRows(c *ui.Context, n int, row func(i int)) ui.Element {
|
||||||
if n > agentVirtualRows {
|
if n > agentVirtualRows {
|
||||||
return ui.List(c, nil, n, row)
|
return ui.List(c, nil, n, row)
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,7 @@ func agentCodeLine(c *ui.Context, language, line string, inBlock bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AgentCode shows highlighted text in a capped well.
|
// AgentCode shows highlighted text in a capped well.
|
||||||
func AgentCode(c *ui.Context, language, text string, maxHeight float32) *ui.Element {
|
func AgentCode(c *ui.Context, language, text string, maxHeight float32) ui.Element {
|
||||||
holder := ui.Column(c).Shrink(1)
|
holder := ui.Column(c).Shrink(1)
|
||||||
cc := ui.Local(holder, "mujica.agent.code", func() *agentCodeCache { return &agentCodeCache{} })
|
cc := ui.Local(holder, "mujica.agent.code", func() *agentCodeCache { return &agentCodeCache{} })
|
||||||
(*cc).update(language, text)
|
(*cc).update(language, text)
|
||||||
@@ -260,7 +260,7 @@ func agentAnsiSpans(k theme.Tokens, in []internal.AnsiSpan) []ui.Span {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// agentBar is a thin progress bar named label.
|
// agentBar is a thin progress bar named label.
|
||||||
func agentBar(c *ui.Context, frac float64, label string) *ui.Element {
|
func agentBar(c *ui.Context, frac float64, label string) ui.Element {
|
||||||
if frac < 0 || frac > 1 || frac != frac {
|
if frac < 0 || frac > 1 || frac != frac {
|
||||||
panic(fmt.Sprintf("mujica: progress %v outside 0..1", frac))
|
panic(fmt.Sprintf("mujica: progress %v outside 0..1", frac))
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -7,9 +7,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PlanItem is a plan task: pending, running, done or skipped.
|
// PlanItem is a plan task: pending, running, done or skipped.
|
||||||
@@ -72,7 +72,7 @@ func AgentPlan(c *ui.Context, items *[]PlanItem, opts AgentPlanOptions) core.Cho
|
|||||||
agentBar(c, frac, fmt.Sprintf(core.Msg(c, "agent_plan.progress"), done, counted))
|
agentBar(c, frac, fmt.Sprintf(core.Msg(c, "agent_plan.progress"), done, counted))
|
||||||
list := *items
|
list := *items
|
||||||
for i := range list {
|
for i := range list {
|
||||||
ui.Row(c).Key(list[i].ID).Gap(u * 2).AlignItems(ui.Center).MinHeight(core.Density(c).ControlHeight()).Children(func() {
|
ui.Row(c.Key(list[i].ID)).Gap(u * 2).AlignItems(ui.Center).MinHeight(core.Density(c).ControlHeight()).Children(func() {
|
||||||
if agentPlanRow(c, &list[i], locked, opts.Still) {
|
if agentPlanRow(c, &list[i], locked, opts.Still) {
|
||||||
r = core.NewChoiceResult(r.Element, true)
|
r = core.NewChoiceResult(r.Element, true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func agentPlanSample() []PlanItem {
|
func agentPlanSample() []PlanItem {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/feedback"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/feedback"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AgentProgressOptions configure an AgentProgress.
|
// AgentProgressOptions configure an AgentProgress.
|
||||||
@@ -19,7 +19,7 @@ type AgentProgressOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AgentProgress shows progress 0 to 1 and the current action.
|
// AgentProgress shows progress 0 to 1 and the current action.
|
||||||
func AgentProgress(c *ui.Context, value float64, opts AgentProgressOptions) *ui.Element {
|
func AgentProgress(c *ui.Context, value float64, opts AgentProgressOptions) ui.Element {
|
||||||
label := opts.Label
|
label := opts.Label
|
||||||
if label == "" {
|
if label == "" {
|
||||||
label = core.Msg(c, "agent_progress.label")
|
label = core.Msg(c, "agent_progress.label")
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAgentProgressValueAndAction(t *testing.T) {
|
func TestAgentProgressValueAndAction(t *testing.T) {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AgentStatusOptions configure an AgentStatus.
|
// AgentStatusOptions configure an AgentStatus.
|
||||||
@@ -16,7 +16,7 @@ type AgentStatusOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AgentStatus shows a state by icon shape and word.
|
// AgentStatus shows a state by icon shape and word.
|
||||||
func AgentStatus(c *ui.Context, state AgentState, opts AgentStatusOptions) *ui.Element {
|
func AgentStatus(c *ui.Context, state AgentState, opts AgentStatusOptions) ui.Element {
|
||||||
label := opts.Label
|
label := opts.Label
|
||||||
if label == "" {
|
if label == "" {
|
||||||
label = agentWord(c, state) // panics on an unknown state
|
label = agentWord(c, state) // panics on an unknown state
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAgentStatusWords(t *testing.T) {
|
func TestAgentStatusWords(t *testing.T) {
|
||||||
@@ -33,16 +33,14 @@ func TestAgentStatusAnimationStopsWhenStill(t *testing.T) {
|
|||||||
}, 300, 100)
|
}, 300, 100)
|
||||||
tt.Frame()
|
tt.Frame()
|
||||||
moving := string(tt.Image().Pix)
|
moving := string(tt.Image().Pix)
|
||||||
time.Sleep(80 * time.Millisecond)
|
tt.Advance(80 * time.Millisecond)
|
||||||
tt.Frame()
|
|
||||||
if moving == string(tt.Image().Pix) {
|
if moving == string(tt.Image().Pix) {
|
||||||
t.Fatal("running status did not animate")
|
t.Fatal("running status did not animate")
|
||||||
}
|
}
|
||||||
still = true
|
still = true
|
||||||
tt.Frame()
|
tt.Frame()
|
||||||
a := string(tt.Image().Pix)
|
a := string(tt.Image().Pix)
|
||||||
time.Sleep(80 * time.Millisecond)
|
tt.Advance(80 * time.Millisecond)
|
||||||
tt.Frame()
|
|
||||||
if a != string(tt.Image().Pix) {
|
if a != string(tt.Image().Pix) {
|
||||||
t.Fatal("still status kept moving")
|
t.Fatal("still status kept moving")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AgentStep is one step of an agent run.
|
// AgentStep is one step of an agent run.
|
||||||
@@ -30,7 +30,7 @@ type AgentStepListOptions struct {
|
|||||||
|
|
||||||
// AgentStepListResult reports this frame's events.
|
// AgentStepListResult reports this frame's events.
|
||||||
type AgentStepListResult struct {
|
type AgentStepListResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
// Retry is the failed step to retry, else "".
|
// Retry is the failed step to retry, else "".
|
||||||
Retry string
|
Retry string
|
||||||
// Toggled is the step whose detail toggled, else "".
|
// Toggled is the step whose detail toggled, else "".
|
||||||
@@ -55,7 +55,7 @@ func AgentStepList(c *ui.Context, steps []AgentStep, opts AgentStepListOptions)
|
|||||||
open := ui.Local(r.Element, "mujica.steps.open", func() map[string]bool { return map[string]bool{} })
|
open := ui.Local(r.Element, "mujica.steps.open", func() map[string]bool { return map[string]bool{} })
|
||||||
r.Element.Children(func() {
|
r.Element.Children(func() {
|
||||||
for i, s := range steps {
|
for i, s := range steps {
|
||||||
ui.Column(c).Key(s.ID).Children(func() {
|
ui.Column(c.Key(s.ID)).Children(func() {
|
||||||
if agentStepRow(c, s, i, len(steps), i == current, (*open)[s.ID], opts.Still, &r) {
|
if agentStepRow(c, s, i, len(steps), i == current, (*open)[s.ID], opts.Still, &r) {
|
||||||
(*open)[s.ID] = !(*open)[s.ID]
|
(*open)[s.ID] = !(*open)[s.ID]
|
||||||
r.Toggled = s.ID
|
r.Toggled = s.ID
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
var agentStepsSample = []AgentStep{
|
var agentStepsSample = []AgentStep{
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ArtifactKind is what an artifact holds; zero is code.
|
// ArtifactKind is what an artifact holds; zero is code.
|
||||||
@@ -42,7 +42,7 @@ type ArtifactPanelOptions struct {
|
|||||||
|
|
||||||
// ArtifactPanelResult reports this frame's events.
|
// ArtifactPanelResult reports this frame's events.
|
||||||
type ArtifactPanelResult struct {
|
type ArtifactPanelResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
// Copied reports the content went to the clipboard.
|
// Copied reports the content went to the clipboard.
|
||||||
Copied bool
|
Copied bool
|
||||||
// Exported asks the caller to export the artifact.
|
// Exported asks the caller to export the artifact.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
var artifactCodeSample = Artifact{Title: "seating.go", Language: "go", Content: "package court\n\n// Seats maps guests to tables.\nfunc Seats() map[string]int {\n\treturn map[string]int{\"Sakiko\": 1}\n}\n"}
|
var artifactCodeSample = Artifact{Title: "seating.go", Language: "go", Content: "package court\n\n// Seats maps guests to tables.\nfunc Seats() map[string]int {\n\treturn map[string]int{\"Sakiko\": 1}\n}\n"}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ArtifactVersion is one saved version of an artifact.
|
// ArtifactVersion is one saved version of an artifact.
|
||||||
@@ -23,7 +23,7 @@ type ArtifactVersionSwitcherOptions struct {
|
|||||||
|
|
||||||
// ArtifactVersionResult reports this frame's events.
|
// ArtifactVersionResult reports this frame's events.
|
||||||
type ArtifactVersionResult struct {
|
type ArtifactVersionResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
// Changed reports *current moved.
|
// Changed reports *current moved.
|
||||||
Changed bool
|
Changed bool
|
||||||
// Compare asks to diff current against the previous version.
|
// Compare asks to diff current against the previous version.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
var artifactVersionsSample = []ArtifactVersion{
|
var artifactVersionsSample = []ArtifactVersion{
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/overlay"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/overlay"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Checkpoint is a saved point to roll back to.
|
// Checkpoint is a saved point to roll back to.
|
||||||
@@ -91,7 +91,7 @@ func checkpointRow(c *ui.Context, p Checkpoint, current, locked bool) bool {
|
|||||||
d := core.Density(c)
|
d := core.Density(c)
|
||||||
u := d.Unit()
|
u := d.Unit()
|
||||||
asked := false
|
asked := false
|
||||||
row := ui.Row(c).Key(p.ID).Gap(u*3).AlignItems(ui.Center).MinHeight(d.ControlHeight()+u*2).Padding(u*2, u*3)
|
row := ui.Row(c.Key(p.ID)).Gap(u*3).AlignItems(ui.Center).MinHeight(d.ControlHeight()+u*2).Padding(u*2, u*3)
|
||||||
if current {
|
if current {
|
||||||
row.Background(k.Selection)
|
row.Background(k.Selection)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
var checkpointsSample = []Checkpoint{
|
var checkpointsSample = []Checkpoint{
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CommandRun is a command run; the caller appends Output.
|
// CommandRun is a command run; the caller appends Output.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func cmdTester(run *CommandRun, opts CommandExecutionCardOptions, aborts *int) *ui.Tester {
|
func cmdTester(run *CommandRun, opts CommandExecutionCardOptions, aborts *int) *ui.Tester {
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/agent"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/agent"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleToolCallCard() {
|
func ExampleToolCallCard() {
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileChangeCardOptions configure a FileChangeCard.
|
// FileChangeCardOptions configure a FileChangeCard.
|
||||||
@@ -20,7 +20,7 @@ type FileChangeCardOptions struct {
|
|||||||
|
|
||||||
// FileChangeResult reports this frame's events.
|
// FileChangeResult reports this frame's events.
|
||||||
type FileChangeResult struct {
|
type FileChangeResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
// Decided reports that *decision changed.
|
// Decided reports that *decision changed.
|
||||||
Decided bool
|
Decided bool
|
||||||
// OpenDiff asks the caller to show the full diff.
|
// OpenDiff asks the caller to show the full diff.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
var fileChangeSample = FileChange{Path: "court/ledger.md",
|
var fileChangeSample = FileChange{Path: "court/ledger.md",
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal/snaptest"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal/snaptest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// snapshot compares view in both themes with testdata/snapshots/<os>/<name>-<mode>.png.
|
// snapshot compares view in both themes with testdata/snapshots/<os>/<name>-<mode>.png.
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HumanInputRequestOptions configure a HumanInputRequest. Question is required.
|
// HumanInputRequestOptions configure a HumanInputRequest. Question is required.
|
||||||
@@ -28,7 +28,7 @@ type HumanInputRequestOptions struct {
|
|||||||
|
|
||||||
// HumanInputResult reports this frame's events.
|
// HumanInputResult reports this frame's events.
|
||||||
type HumanInputResult struct {
|
type HumanInputResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
Submitted bool
|
Submitted bool
|
||||||
// TimedOut reports the frame the deadline passed.
|
// TimedOut reports the frame the deadline passed.
|
||||||
TimedOut bool
|
TimedOut bool
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func humanTester(answer *string, opts *HumanInputRequestOptions, got *[]HumanInputResult) *ui.Tester {
|
func humanTester(answer *string, opts *HumanInputRequestOptions, got *[]HumanInputResult) *ui.Tester {
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MCPState is a server connection; zero is stopped.
|
// MCPState is a server connection; zero is stopped.
|
||||||
@@ -53,7 +53,7 @@ type MCPServerListOptions struct {
|
|||||||
|
|
||||||
// MCPServerListResult reports this frame's request.
|
// MCPServerListResult reports this frame's request.
|
||||||
type MCPServerListResult struct {
|
type MCPServerListResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
Action MCPAction
|
Action MCPAction
|
||||||
// Server is the ID the action is for.
|
// Server is the ID the action is for.
|
||||||
Server string
|
Server string
|
||||||
@@ -105,7 +105,7 @@ func mcpServerRow(c *ui.Context, s MCPServer, locked, still bool) MCPAction {
|
|||||||
words := [...]string{"stopped", "connecting", "connected", "error"}
|
words := [...]string{"stopped", "connecting", "connected", "error"}
|
||||||
word := core.Msg(c, "mcp_server_list."+words[s.State])
|
word := core.Msg(c, "mcp_server_list."+words[s.State])
|
||||||
act := MCPNone
|
act := MCPNone
|
||||||
ui.Row(c).Key(s.ID).Gap(u*3).AlignItems(ui.Center).MinHeight(d.ControlHeight()+u*2).Padding(u*2, u*3).
|
ui.Row(c.Key(s.ID)).Gap(u*3).AlignItems(ui.Center).MinHeight(d.ControlHeight()+u*2).Padding(u*2, u*3).
|
||||||
Label(s.Name + ", " + word).Children(func() {
|
Label(s.Name + ", " + word).Children(func() {
|
||||||
switch s.State {
|
switch s.State {
|
||||||
case MCPConnecting:
|
case MCPConnecting:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
var mcpServersSample = []MCPServer{
|
var mcpServersSample = []MCPServer{
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/overlay"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/overlay"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MemoryEntry is one memory; the caller persists it.
|
// MemoryEntry is one memory; the caller persists it.
|
||||||
@@ -30,7 +30,7 @@ type MemoryPanelOptions struct {
|
|||||||
|
|
||||||
// MemoryPanelResult reports this frame's in-place edits.
|
// MemoryPanelResult reports this frame's in-place edits.
|
||||||
type MemoryPanelResult struct {
|
type MemoryPanelResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
// Edited and Deleted are changed IDs, else "".
|
// Edited and Deleted are changed IDs, else "".
|
||||||
Edited string
|
Edited string
|
||||||
Deleted string
|
Deleted string
|
||||||
@@ -74,7 +74,7 @@ func MemoryPanel(c *ui.Context, entries *[]MemoryEntry, opts MemoryPanelOptions)
|
|||||||
}
|
}
|
||||||
AgentRows(c, len(st.shown), func(i int) {
|
AgentRows(c, len(st.shown), func(i int) {
|
||||||
idx := st.shown[i]
|
idx := st.shown[i]
|
||||||
ui.Column(c).Key((*entries)[idx].ID).Children(func() { memoryRow(c, entries, idx, st, locked, &r) })
|
ui.Column(c.Key((*entries)[idx].ID)).Children(func() { memoryRow(c, entries, idx, st, locked, &r) })
|
||||||
}).Shrink(1).MaxHeight(maxH).Dividers(theme.BorderWidth, core.Tokens(c).Border)
|
}).Shrink(1).MaxHeight(maxH).Dividers(theme.BorderWidth, core.Tokens(c).Border)
|
||||||
})
|
})
|
||||||
if r.Deleted != "" {
|
if r.Deleted != "" {
|
||||||
@@ -123,7 +123,7 @@ func memoryRow(c *ui.Context, entries *[]MemoryEntry, idx int, st *memoryPanelSt
|
|||||||
u := core.Density(c).Unit()
|
u := core.Density(c).Unit()
|
||||||
ui.Column(c).Gap(u).Padding(u*2, 0).Children(func() {
|
ui.Column(c).Gap(u).Padding(u*2, 0).Children(func() {
|
||||||
if st.editing == e.ID && !locked {
|
if st.editing == e.ID && !locked {
|
||||||
input.TextArea(c, &st.draft, input.TextAreaOptions{Label: core.Msg(c, "memory_panel.edit"), MinHeight: 60}).Input.AutoFocus()
|
core.AutoFocus(c, input.TextArea(c, &st.draft, input.TextAreaOptions{Label: core.Msg(c, "memory_panel.edit"), MinHeight: 60}).Input)
|
||||||
ui.Row(c).Gap(u * 2).Justify(ui.End).Children(func() {
|
ui.Row(c).Gap(u * 2).Justify(ui.End).Children(func() {
|
||||||
if input.Button(c, core.Msg(c, "memory_panel.cancel"), input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
if input.Button(c, core.Msg(c, "memory_panel.cancel"), input.ButtonOptions{Variant: input.Secondary}).Clicked() {
|
||||||
st.editing = ""
|
st.editing = ""
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func memorySample() []MemoryEntry {
|
func memorySample() []MemoryEntry {
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MultiFileDiffReviewOptions configure a MultiFileDiffReview.
|
// MultiFileDiffReviewOptions configure a MultiFileDiffReview.
|
||||||
@@ -79,7 +79,7 @@ func MultiFileDiffReview(c *ui.Context, decisions *[]FileDecision, files []FileC
|
|||||||
slog.Debug("mujica: multi_file_diff_review select", "path", files[i].Path)
|
slog.Debug("mujica: multi_file_diff_review select", "path", files[i].Path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var list *ui.Element
|
var list ui.Element
|
||||||
if len(files) > agentVirtualRows {
|
if len(files) > agentVirtualRows {
|
||||||
list = ui.List(c, nil, len(files), row)
|
list = ui.List(c, nil, len(files), row)
|
||||||
} else {
|
} else {
|
||||||
@@ -109,7 +109,7 @@ func MultiFileDiffReview(c *ui.Context, decisions *[]FileDecision, files []FileC
|
|||||||
func multiFileRow(c *ui.Context, f FileChange, d FileDecision, selected bool) bool {
|
func multiFileRow(c *ui.Context, f FileChange, d FileDecision, selected bool) bool {
|
||||||
k := core.Tokens(c)
|
k := core.Tokens(c)
|
||||||
u := core.Density(c).Unit()
|
u := core.Density(c).Unit()
|
||||||
row := ui.ButtonBase(c).Key(f.Path).Justify(ui.Start).Gap(u*2).MinHeight(core.Density(c).ControlHeight()).
|
row := ui.ButtonBase(c.Key(f.Path)).Justify(ui.Start).Gap(u*2).MinHeight(core.Density(c).ControlHeight()).
|
||||||
Padding(0, u*2).Radius(theme.ControlRadius).Label(f.Path)
|
Padding(0, u*2).Radius(theme.ControlRadius).Label(f.Path)
|
||||||
internal.Ring(row, k.Focus, theme.ControlRadius)
|
internal.Ring(row, k.Focus, theme.ControlRadius)
|
||||||
dc := agentDiffOf(row, f)
|
dc := agentDiffOf(row, f)
|
||||||
@@ -133,7 +133,7 @@ func multiFileRow(c *ui.Context, f FileChange, d FileDecision, selected bool) bo
|
|||||||
func fileChangeDecide(c *ui.Context, d *FileDecision, f FileChange, locked bool) bool {
|
func fileChangeDecide(c *ui.Context, d *FileDecision, f FileChange, locked bool) bool {
|
||||||
u := core.Density(c).Unit()
|
u := core.Density(c).Unit()
|
||||||
changed := false
|
changed := false
|
||||||
holder := ui.Column(c).Key(f.Path).Grow(1).Shrink(1).Gap(u * 2)
|
holder := ui.Column(c.Key(f.Path)).Grow(1).Shrink(1).Gap(u * 2)
|
||||||
dc := agentDiffOf(holder, f)
|
dc := agentDiffOf(holder, f)
|
||||||
holder.Children(func() {
|
holder.Children(func() {
|
||||||
ui.Row(c).Gap(u * 2).AlignItems(ui.Center).Children(func() {
|
ui.Row(c).Gap(u * 2).AlignItems(ui.Center).Children(func() {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func reviewFiles() []FileChange {
|
func reviewFiles() []FileChange {
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/overlay"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/overlay"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PermissionChoice answers a PermissionPrompt; zero is no answer.
|
// PermissionChoice answers a PermissionPrompt; zero is no answer.
|
||||||
@@ -32,7 +32,7 @@ type PermissionPromptOptions struct {
|
|||||||
|
|
||||||
// PermissionResult reports the choice made in this frame.
|
// PermissionResult reports the choice made in this frame.
|
||||||
type PermissionResult struct {
|
type PermissionResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
Choice PermissionChoice
|
Choice PermissionChoice
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ func PermissionPrompt(c *ui.Context, open *bool, opts PermissionPromptOptions) P
|
|||||||
r.Element = overlay.Dialog(c, open, overlay.DialogOptions{
|
r.Element = overlay.Dialog(c, open, overlay.DialogOptions{
|
||||||
Title: opts.Title, Description: opts.Description, KeepOnBackdrop: true, HideClose: true,
|
Title: opts.Title, Description: opts.Description, KeepOnBackdrop: true, HideClose: true,
|
||||||
Actions: func() {
|
Actions: func() {
|
||||||
if input.Button(c, core.Msg(c, "permission_prompt.deny"), input.ButtonOptions{Variant: input.Secondary}).AutoFocus().Clicked() {
|
if core.AutoFocus(c, input.Button(c, core.Msg(c, "permission_prompt.deny"), input.ButtonOptions{Variant: input.Secondary})).Clicked() {
|
||||||
r.Choice = PermissionDeny
|
r.Choice = PermissionDeny
|
||||||
}
|
}
|
||||||
if input.Button(c, core.Msg(c, "permission_prompt.always"), input.ButtonOptions{Variant: input.Outline}).Clicked() {
|
if input.Button(c, core.Msg(c, "permission_prompt.always"), input.ButtonOptions{Variant: input.Outline}).Clicked() {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
)
|
)
|
||||||
|
|
||||||
func permissionTester(open *bool, got *[]PermissionChoice) *ui.Tester {
|
func permissionTester(open *bool, got *[]PermissionChoice) *ui.Tester {
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SandboxState is a sandbox's lifecycle. The zero value is SandboxStarting.
|
// SandboxState is a sandbox's lifecycle. The zero value is SandboxStarting.
|
||||||
@@ -45,7 +45,7 @@ type SandboxStatusOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SandboxStatus shows sandbox state, resources and time left.
|
// SandboxStatus shows sandbox state, resources and time left.
|
||||||
func SandboxStatus(c *ui.Context, info SandboxInfo, opts SandboxStatusOptions) *ui.Element {
|
func SandboxStatus(c *ui.Context, info SandboxInfo, opts SandboxStatusOptions) ui.Element {
|
||||||
agent := map[SandboxState]AgentState{SandboxStarting: AgentPlanning, SandboxRunning: AgentRunning, SandboxStopped: AgentSkipped, SandboxFailed: AgentFailed}
|
agent := map[SandboxState]AgentState{SandboxStarting: AgentPlanning, SandboxRunning: AgentRunning, SandboxStopped: AgentSkipped, SandboxFailed: AgentFailed}
|
||||||
state, ok := agent[info.State]
|
state, ok := agent[info.State]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func sandboxSample(expires time.Time) SandboxInfo {
|
func sandboxSample(expires time.Time) SandboxInfo {
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ScreenshotFrame is one caller-supplied frame of the agent's screen.
|
// ScreenshotFrame is one caller-supplied frame of the agent's screen.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
// screenshotFrames makes n solid frames of shifting colors.
|
// screenshotFrames makes n solid frames of shifting colors.
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SubAgent is one agent in a hierarchy.
|
// SubAgent is one agent in a hierarchy.
|
||||||
@@ -74,7 +74,7 @@ func subAgentRow(c *ui.Context, a SubAgent, depth int, open, still bool) bool {
|
|||||||
d := core.Density(c)
|
d := core.Density(c)
|
||||||
u := d.Unit()
|
u := d.Unit()
|
||||||
parent := len(a.Children) > 0
|
parent := len(a.Children) > 0
|
||||||
row := ui.ButtonBase(c).Key(a.ID).Justify(ui.Start).Gap(u*2).MinHeight(d.ControlHeight()).
|
row := ui.ButtonBase(c.Key(a.ID)).Justify(ui.Start).Gap(u*2).MinHeight(d.ControlHeight()).
|
||||||
Padding(0, u*2, 0, u*2+float32(depth)*u*5).Radius(theme.ControlRadius).
|
Padding(0, u*2, 0, u*2+float32(depth)*u*5).Radius(theme.ControlRadius).
|
||||||
Label(a.Name + ", " + agentWord(c, a.State))
|
Label(a.Name + ", " + agentWord(c, a.State))
|
||||||
internal.Ring(row, k.Focus, theme.ControlRadius)
|
internal.Ring(row, k.Focus, theme.ControlRadius)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
var subAgentSample = []SubAgent{
|
var subAgentSample = []SubAgent{
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/overlay"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/overlay"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToolApprovalOptions configure a ToolApprovalDialog. Tool is required.
|
// ToolApprovalOptions configure a ToolApprovalDialog. Tool is required.
|
||||||
@@ -22,7 +22,7 @@ type ToolApprovalOptions struct {
|
|||||||
|
|
||||||
// ToolApprovalResult reports the choice made in this frame.
|
// ToolApprovalResult reports the choice made in this frame.
|
||||||
type ToolApprovalResult struct {
|
type ToolApprovalResult struct {
|
||||||
Element *ui.Element
|
Element ui.Element
|
||||||
Allowed bool
|
Allowed bool
|
||||||
Denied bool
|
Denied bool
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ func ToolApprovalDialog(c *ui.Context, open *bool, opts ToolApprovalOptions) Too
|
|||||||
Title: core.Msg(c, "tool_approval_dialog.title"), Description: opts.Reason, KeepOnBackdrop: true, HideClose: true,
|
Title: core.Msg(c, "tool_approval_dialog.title"), Description: opts.Reason, KeepOnBackdrop: true, HideClose: true,
|
||||||
Actions: func() {
|
Actions: func() {
|
||||||
// Deny first: a reflexive Enter never runs the tool.
|
// Deny first: a reflexive Enter never runs the tool.
|
||||||
if input.Button(c, core.Msg(c, "tool_approval_dialog.deny"), input.ButtonOptions{Variant: input.Secondary}).AutoFocus().Clicked() {
|
if core.AutoFocus(c, input.Button(c, core.Msg(c, "tool_approval_dialog.deny"), input.ButtonOptions{Variant: input.Secondary})).Clicked() {
|
||||||
r.Denied = true
|
r.Denied = true
|
||||||
}
|
}
|
||||||
if input.Button(c, core.Msg(c, "tool_approval_dialog.allow"), input.ButtonOptions{}).Clicked() {
|
if input.Button(c, core.Msg(c, "tool_approval_dialog.allow"), input.ButtonOptions{}).Clicked() {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
)
|
)
|
||||||
|
|
||||||
type approvalApp struct {
|
type approvalApp struct {
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/data"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToolCall is one tool invocation; the caller owns it.
|
// ToolCall is one tool invocation; the caller owns it.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
var toolCallSample = ToolCall{ID: "1", Name: "read_file", Args: `{"path":"court/ledger.md","limit":40}`,
|
var toolCallSample = ToolCall{ID: "1", Name: "read_file", Args: `{"path":"court/ledger.md","limit":40}`,
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/data"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToolCallGroupOptions configure a ToolCallGroup.
|
// ToolCallGroupOptions configure a ToolCallGroup.
|
||||||
@@ -64,7 +64,7 @@ func ToolCallGroup(c *ui.Context, open *bool, calls []ToolCall, opts ToolCallGro
|
|||||||
})
|
})
|
||||||
}}, func() {
|
}}, func() {
|
||||||
for _, call := range calls {
|
for _, call := range calls {
|
||||||
ui.Column(c).Key(call.ID).Children(func() { ToolCallCard(c, call, ToolCallCardOptions{Still: opts.Still}) })
|
ui.Column(c.Key(call.ID)).Children(func() { ToolCallCard(c, call, ToolCallCardOptions{Still: opts.Still}) })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
var toolCallGroupSample = []ToolCall{
|
var toolCallGroupSample = []ToolCall{
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func TestAreaChartStackedReadout(t *testing.T) {
|
|||||||
if got := chartReadout(tt); got != "0 · Pews 3 · Candles 5" {
|
if got := chartReadout(tt); got != "0 · Pews 3 · Candles 5" {
|
||||||
t.Fatalf("readout shows the series values, not the stack: %q", got)
|
t.Fatalf("readout shows the series values, not the stack: %q", got)
|
||||||
}
|
}
|
||||||
b := h.res.Element.Bounds()
|
b := h.box(tt)
|
||||||
tt.Move(b.X+52+(b.W-64)*0.5, b.Y+b.H/2)
|
tt.Move(b.X+52+(b.W-64)*0.5, b.Y+b.H/2)
|
||||||
if got := chartReadout(tt); got != "2 · Pews no value · Candles 6" || h.res.Point != 2 {
|
if got := chartReadout(tt); got != "2 · Pews no value · Candles 6" || h.res.Point != 2 {
|
||||||
t.Fatalf("hover %q", got)
|
t.Fatalf("hover %q", got)
|
||||||
|
|||||||
+3
-3
@@ -5,9 +5,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BarChartOptions configure a BarChart.
|
// BarChartOptions configure a BarChart.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
var barCats = []string{"Nave", "Choir", "Crypt"}
|
var barCats = []string{"Nave", "Choir", "Crypt"}
|
||||||
@@ -44,7 +44,7 @@ func TestBarChartHoverStackedHorizontal(t *testing.T) {
|
|||||||
tt := ui.NewTester(h.view, 520, 420)
|
tt := ui.NewTester(h.view, 520, 420)
|
||||||
tabTo(t, tt, "Tithes")
|
tabTo(t, tt, "Tithes")
|
||||||
found := map[string]bool{}
|
found := map[string]bool{}
|
||||||
b := h.res.Element.Bounds()
|
b := h.box(tt)
|
||||||
for y := b.Y + 30; y < b.Y+b.H-40; y += 4 {
|
for y := b.Y + 30; y < b.Y+b.H-40; y += 4 {
|
||||||
for x := b.X + 60; x < b.X+b.W-16; x += 8 {
|
for x := b.X + 60; x < b.X+b.W-16; x += 8 {
|
||||||
tt.Move(x, y)
|
tt.Move(x, y)
|
||||||
|
|||||||
+3
-3
@@ -7,9 +7,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BoxGroup is one named sample of a BoxPlot.
|
// BoxGroup is one named sample of a BoxPlot.
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func TestBoxPlotReadout(t *testing.T) {
|
|||||||
if got := chartReadout(tt); got != "Choir · median 5.5 · quartiles 3.25–7.75 · whiskers 1–9 · 1 outliers" {
|
if got := chartReadout(tt); got != "Choir · median 5.5 · quartiles 3.25–7.75 · whiskers 1–9 · 1 outliers" {
|
||||||
t.Fatalf("readout %q", got)
|
t.Fatalf("readout %q", got)
|
||||||
}
|
}
|
||||||
b := h.res.Element.Bounds()
|
b := h.box(tt)
|
||||||
tt.Key(0, ui.KeyEscape)
|
tt.Key(0, ui.KeyEscape)
|
||||||
tt.Move(b.X+52+(b.W-64)*0.75, b.Y+100)
|
tt.Move(b.X+52+(b.W-64)*0.75, b.Y+100)
|
||||||
if got := chartReadout(tt); got != "Nave · median 5 · quartiles 5–6 · whiskers 4–7 · 0 outliers" {
|
if got := chartReadout(tt); got != "Nave · median 5 · quartiles 5–6 · whiskers 4–7 · 0 outliers" {
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BulletChartOptions configure a BulletChart; Frame.Height 0 means 72 DIPs.
|
// BulletChartOptions configure a BulletChart; Frame.Height 0 means 72 DIPs.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBulletChartReadout(t *testing.T) {
|
func TestBulletChartReadout(t *testing.T) {
|
||||||
@@ -23,7 +23,7 @@ func TestBulletChartReadout(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tt.Key(0, ui.KeyEscape)
|
tt.Key(0, ui.KeyEscape)
|
||||||
b := h.res.Element.Bounds()
|
b := h.box(tt)
|
||||||
plotW := b.W - 24
|
plotW := b.W - 24
|
||||||
tt.Move(b.X+8+plotW*0.1, b.Y+6+21) // band 1, inside the value bar
|
tt.Move(b.X+8+plotW*0.1, b.Y+6+21) // band 1, inside the value bar
|
||||||
if got := chartReadout(tt); got != "Actual 270" {
|
if got := chartReadout(tt); got != "Actual 270" {
|
||||||
@@ -40,7 +40,7 @@ func TestBulletChartVerticalAndBad(t *testing.T) {
|
|||||||
return BulletChart(c, 40, BulletChartOptions{Frame: ChartFrame{Label: "Alms", Height: 200}, Vertical: true, Max: 100})
|
return BulletChart(c, 40, BulletChartOptions{Frame: ChartFrame{Label: "Alms", Height: 200}, Vertical: true, Max: 100})
|
||||||
}}
|
}}
|
||||||
tt := ui.NewTester(h.view, 520, 420)
|
tt := ui.NewTester(h.view, 520, 420)
|
||||||
b := h.res.Element.Bounds()
|
b := h.box(tt)
|
||||||
tt.Move(b.X+b.W/2, b.Y+8+184*0.8) // low on the scale: inside the bar
|
tt.Move(b.X+b.W/2, b.Y+8+184*0.8) // low on the scale: inside the bar
|
||||||
if got := chartReadout(tt); got != "Actual 40" {
|
if got := chartReadout(tt); got != "Actual 40" {
|
||||||
t.Fatalf("vertical hover %q", got)
|
t.Fatalf("vertical hover %q", got)
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/datetime"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/datetime"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/icons"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CalendarHeatmapOptions configure a CalendarHeatmap.
|
// CalendarHeatmapOptions configure a CalendarHeatmap.
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/datetime"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/datetime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func calendarValues() map[core.Date]float64 {
|
func calendarValues() map[core.Date]float64 {
|
||||||
@@ -65,7 +65,7 @@ func TestCalendarHeatmapWeekStartHover(t *testing.T) {
|
|||||||
return CalendarHeatmap(c, &year, calendarValues(), CalendarHeatmapOptions{Frame: ChartFrame{Label: "Prayers"}, WeekStart: tc.start})
|
return CalendarHeatmap(c, &year, calendarValues(), CalendarHeatmapOptions{Frame: ChartFrame{Label: "Prayers"}, WeekStart: tc.start})
|
||||||
}}
|
}}
|
||||||
tt := ui.NewTester(h.view, 560, 420)
|
tt := ui.NewTester(h.view, 560, 420)
|
||||||
b := h.res.Element.Bounds()
|
b := h.box(tt)
|
||||||
plotY := b.Y + b.H - 20 - 8 - 240 // above the readout line and gap
|
plotY := b.Y + b.H - 20 - 8 - 240 // above the readout line and gap
|
||||||
step := min((b.W-36)/53, float32(240-48)/7)
|
step := min((b.W-36)/53, float32(240-48)/7)
|
||||||
tt.Move(b.X+32+step/2, plotY+18+float32(tc.row)*step+step/2)
|
tt.Move(b.X+32+step/2, plotY+18+float32(tc.row)*step+step/2)
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CandleCountdownOptions configure a CandleCountdown.
|
// CandleCountdownOptions configure a CandleCountdown.
|
||||||
@@ -19,7 +19,7 @@ type CandleCountdownOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CandleCountdown displays closing time and schedules visible updates.
|
// CandleCountdown displays closing time and schedules visible updates.
|
||||||
func CandleCountdown(c *ui.Context, interval Interval, opts CandleCountdownOptions) *ui.Element {
|
func CandleCountdown(c *ui.Context, interval Interval, opts CandleCountdownOptions) ui.Element {
|
||||||
loc := opts.Location
|
loc := opts.Location
|
||||||
if loc == nil {
|
if loc == nil {
|
||||||
loc = time.UTC
|
loc = time.UTC
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCandleCountdownIntervalsAndClock(t *testing.T) {
|
func TestCandleCountdownIntervalsAndClock(t *testing.T) {
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/data"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/data"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChartType selects the price chart shape.
|
// ChartType selects the price chart shape.
|
||||||
@@ -67,6 +67,7 @@ type mktCore struct {
|
|||||||
candles []Candle
|
candles []Candle
|
||||||
view *MarketView
|
view *MarketView
|
||||||
from, to int
|
from, to int
|
||||||
|
act int // keyboard or click activation, mark plus one; zero is none
|
||||||
lo, hi float64
|
lo, hi float64
|
||||||
base float64 // percent mode divides by this close; 0 is prices
|
base float64 // percent mode divides by this close; 0 is prices
|
||||||
dec int
|
dec int
|
||||||
@@ -120,7 +121,7 @@ func CandlestickChart(c *ui.Context, candles []Candle, view *MarketView, opts Ca
|
|||||||
m.yDomain()
|
m.yDomain()
|
||||||
m.alertsCheck()
|
m.alertsCheck()
|
||||||
res, plot := ch.Build(m.spec())
|
res, plot := ch.Build(m.spec())
|
||||||
if plot != nil {
|
if plot.Valid() {
|
||||||
m.input(plot)
|
m.input(plot)
|
||||||
}
|
}
|
||||||
if s := opts.Sync; s != nil {
|
if s := opts.Sync; s != nil {
|
||||||
@@ -277,9 +278,7 @@ func (m *mktCore) spec() ChartSpec {
|
|||||||
}
|
}
|
||||||
if d := m.opts.Drawings; d != nil {
|
if d := m.opts.Drawings; d != nil {
|
||||||
spec.Activate = func(i int) {
|
spec.Activate = func(i int) {
|
||||||
if !d.Locked && d.Tool != ToolSelect {
|
m.act = i + 1
|
||||||
d.place(m, DrawingPoint{m.candles[i].Time, m.candles[i].Close})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spec.Readout = m.readout
|
spec.Readout = m.readout
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
// sampleCandles is a deterministic random walk, hourly from 2026-01-01.
|
// sampleCandles is a deterministic random walk, hourly from 2026-01-01.
|
||||||
@@ -33,13 +33,12 @@ func mktState0(r ChartResult) *mktState {
|
|||||||
func TestCandlestickChartDrawsOnlyVisible(t *testing.T) {
|
func TestCandlestickChartDrawsOnlyVisible(t *testing.T) {
|
||||||
candles := sampleCandles(100000)
|
candles := sampleCandles(100000)
|
||||||
var view MarketView
|
var view MarketView
|
||||||
var res ChartResult
|
var st *mktState
|
||||||
tt := ui.NewTester(func(c *ui.Context) {
|
tt := ui.NewTester(func(c *ui.Context) {
|
||||||
core.Use(c, core.Settings{})
|
core.Use(c, core.Settings{})
|
||||||
res = CandlestickChart(c, candles, &view, CandlestickOptions{Frame: ChartFrame{Label: "BTC"}})
|
st = mktState0(CandlestickChart(c, candles, &view, CandlestickOptions{Frame: ChartFrame{Label: "BTC"}}))
|
||||||
}, 900, 400)
|
}, 900, 400)
|
||||||
tt.Frame()
|
tt.Frame()
|
||||||
st := mktState0(res)
|
|
||||||
if st.drawn != 120 {
|
if st.drawn != 120 {
|
||||||
t.Fatalf("drew %d candles of 100000", st.drawn)
|
t.Fatalf("drew %d candles of 100000", st.drawn)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// paint draws axes, visible candles, layers, and the crosshair.
|
// paint draws axes, visible candles, layers, and the crosshair.
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChartNoteKind is what a ChartNote draws.
|
// ChartNoteKind is what a ChartNote draws.
|
||||||
@@ -123,7 +123,7 @@ type noteDrag struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Drag handles movement; plot uses element coordinates.
|
// Drag handles movement; plot uses element coordinates.
|
||||||
func (a ChartAnnotationPart) Drag(plotElement *ui.Element, plot ui.Rect) bool {
|
func (a ChartAnnotationPart) Drag(plotElement ui.Element, plot ui.Rect) bool {
|
||||||
if plotElement.IsDisabled() {
|
if plotElement.IsDisabled() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -185,7 +185,7 @@ func (a ChartAnnotationPart) Drag(plotElement *ui.Element, plot ui.Rect) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ChartAnnotationPart) keyboard(e *ui.Element) bool {
|
func (a ChartAnnotationPart) keyboard(e ui.Element) bool {
|
||||||
index := ui.Local(e, "mujica.notes.key", func() int { return 0 })
|
index := ui.Local(e, "mujica.notes.key", func() int { return 0 })
|
||||||
dir := 0
|
dir := 0
|
||||||
if e.Shortcut(ui.Alt, ui.KeyPageDown) {
|
if e.Shortcut(ui.Alt, ui.KeyPageDown) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestChartAnnotationDrag(t *testing.T) {
|
func TestChartAnnotationDrag(t *testing.T) {
|
||||||
@@ -19,7 +19,7 @@ func TestChartAnnotationDrag(t *testing.T) {
|
|||||||
}
|
}
|
||||||
h := &chartHarness{build: func(c *ui.Context) ChartResult { return partsChart(c, notes, false) }}
|
h := &chartHarness{build: func(c *ui.Context) ChartResult { return partsChart(c, notes, false) }}
|
||||||
tt := ui.NewTester(h.view, 520, 420)
|
tt := ui.NewTester(h.view, 520, 420)
|
||||||
b := h.res.Element.Bounds()
|
b := h.box(tt)
|
||||||
plot := ChartInset(ui.Rect{X: b.X, Y: b.Y, W: b.W, H: 240}, 52, 26)
|
plot := ChartInset(ui.Rect{X: b.X, Y: b.Y, W: b.W, H: 240}, 52, 26)
|
||||||
yAt := func(v float32) float32 { return plot.Y + plot.H*(1-v/100) }
|
yAt := func(v float32) float32 { return plot.Y + plot.H*(1-v/100) }
|
||||||
xAt := func(v float32) float32 { return plot.X + plot.W*v/10 }
|
xAt := func(v float32) float32 { return plot.X + plot.W*v/10 }
|
||||||
@@ -51,7 +51,7 @@ func TestChartAnnotationDisabledAndBad(t *testing.T) {
|
|||||||
notes := []ChartNote{{Kind: NoteHLine, Value: 50, Draggable: true}}
|
notes := []ChartNote{{Kind: NoteHLine, Value: 50, Draggable: true}}
|
||||||
h := &chartHarness{disabled: true, build: func(c *ui.Context) ChartResult { return partsChart(c, notes, false) }}
|
h := &chartHarness{disabled: true, build: func(c *ui.Context) ChartResult { return partsChart(c, notes, false) }}
|
||||||
tt := ui.NewTester(h.view, 520, 420)
|
tt := ui.NewTester(h.view, 520, 420)
|
||||||
b := h.res.Element.Bounds()
|
b := h.box(tt)
|
||||||
plot := ChartInset(ui.Rect{X: b.X, Y: b.Y, W: b.W, H: 240}, 52, 26)
|
plot := ChartInset(ui.Rect{X: b.X, Y: b.Y, W: b.W, H: 240}, 52, 26)
|
||||||
tt.Press(plot.X+100, plot.Y+plot.H/2)
|
tt.Press(plot.X+100, plot.Y+plot.H/2)
|
||||||
tt.Move(plot.X+100, plot.Y+20)
|
tt.Move(plot.X+100, plot.Y+20)
|
||||||
|
|||||||
+3
-3
@@ -7,9 +7,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChartAxisKind is how an axis maps values.
|
// ChartAxisKind is how an axis maps values.
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func axisIn(opts ChartAxisOptions) (a ChartAxisPart) {
|
func axisIn(opts ChartAxisOptions) (a ChartAxisPart) {
|
||||||
@@ -96,7 +96,7 @@ func partsChart(c *ui.Context, notes []ChartNote, crosshair bool) ChartResult {
|
|||||||
return hair.Snap(len(pts), func(i int) (float32, float32) { return at(r, i) }, px, py)
|
return hair.Snap(len(pts), func(i int) (float32, float32) { return at(r, i) }, px, py)
|
||||||
},
|
},
|
||||||
Anchor: at,
|
Anchor: at,
|
||||||
Input: func(plot *ui.Element, r ui.Rect) { notesPart.Drag(plot, plotOf(r)) },
|
Input: func(plot ui.Element, r ui.Rect) { notesPart.Drag(plot, plotOf(r)) },
|
||||||
Draw: func(p *ui.Painter, r ui.Rect, active int) {
|
Draw: func(p *ui.Painter, r ui.Rect, active int) {
|
||||||
plot := plotOf(r)
|
plot := plotOf(r)
|
||||||
grid.Draw(p, plot)
|
grid.Draw(p, plot)
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/input"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChartBrushOptions configure a ChartBrush.
|
// ChartBrushOptions configure a ChartBrush.
|
||||||
@@ -94,7 +94,7 @@ func ChartBrush(c *ui.Context, value *[2]float64, opts ChartBrushOptions) core.C
|
|||||||
track.Draw(func(p *ui.Painter, r ui.Rect) { brushPaint(p, r, *value, opts, k, preview) })
|
track.Draw(func(p *ui.Painter, r ui.Rect) { brushPaint(p, r, *value, opts, k, preview) })
|
||||||
track.Children(func() {
|
track.Children(func() {
|
||||||
x0, x1 := at(value[0]), at(value[1])
|
x0, x1 := at(value[0]), at(value[1])
|
||||||
end := func(i int, x float32) *ui.Element {
|
end := func(i int, x float32) ui.Element {
|
||||||
e := ui.Box(c).Absolute().Left(x-theme.MinHitSize/2).Top(0).Size(theme.MinHitSize, h).Focusable().
|
e := ui.Box(c).Absolute().Left(x-theme.MinHitSize/2).Top(0).Size(theme.MinHitSize, h).Focusable().
|
||||||
Role(ui.RoleSlider).Label(fmt.Sprintf(core.Msg(c, []string{"chart_brush.start", "chart_brush.end"}[i]), val(value[i])))
|
Role(ui.RoleSlider).Label(fmt.Sprintf(core.Msg(c, []string{"chart_brush.start", "chart_brush.end"}[i]), val(value[i])))
|
||||||
internal.Ring(e, k.Focus, theme.ControlRadius)
|
internal.Ring(e, k.Focus, theme.ControlRadius)
|
||||||
@@ -104,7 +104,7 @@ func ChartBrush(c *ui.Context, value *[2]float64, opts ChartBrushOptions) core.C
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Tab order runs start, window, end.
|
// Tab order runs start, window, end.
|
||||||
ends := [2]*ui.Element{end(0, x0)}
|
ends := [2]ui.Element{end(0, x0)}
|
||||||
window := ui.Box(c).Absolute().Left(x0 + theme.MinHitSize/2).Top(0).Width(max(x1-x0-theme.MinHitSize, theme.MinHitSize)).Height(h).Focusable().
|
window := ui.Box(c).Absolute().Left(x0 + theme.MinHitSize/2).Top(0).Width(max(x1-x0-theme.MinHitSize, theme.MinHitSize)).Height(h).Focusable().
|
||||||
Role(ui.RoleGroup).Label(fmt.Sprintf(core.Msg(c, "chart_brush.window"), opts.Label, val(value[0]), val(value[1])))
|
Role(ui.RoleGroup).Label(fmt.Sprintf(core.Msg(c, "chart_brush.window"), opts.Label, val(value[0]), val(value[1])))
|
||||||
internal.Ring(window, k.Focus, theme.ControlRadius)
|
internal.Ring(window, k.Focus, theme.ControlRadius)
|
||||||
@@ -149,7 +149,7 @@ func ChartBrush(c *ui.Context, value *[2]float64, opts ChartBrushOptions) core.C
|
|||||||
}
|
}
|
||||||
|
|
||||||
// brushKey handles arrow, Page, Home and End keys.
|
// brushKey handles arrow, Page, Home and End keys.
|
||||||
func brushKey(e *ui.Element, v float64, spec input.NumberInputSpec) (float64, bool) {
|
func brushKey(e ui.Element, v float64, spec input.NumberInputSpec) (float64, bool) {
|
||||||
if e.IsDisabled() {
|
if e.IsDisabled() {
|
||||||
return v, false
|
return v, false
|
||||||
}
|
}
|
||||||
@@ -171,10 +171,10 @@ func brushKey(e *ui.Element, v float64, spec input.NumberInputSpec) (float64, bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// brushPointer drags handles, the window, or a new range.
|
// brushPointer drags handles, the window, or a new range.
|
||||||
func brushPointer(track, window *ui.Element, ends [2]*ui.Element, drag *brushDrag, value *[2]float64, from func(float32) float64, set, move func(lo, hi float64)) {
|
func brushPointer(track, window ui.Element, ends [2]ui.Element, drag *brushDrag, value *[2]float64, from func(float32) float64, set, move func(lo, hi float64)) {
|
||||||
px, _, _ := track.PointerPosition()
|
px, _, _ := track.PointerPosition()
|
||||||
at := from(px)
|
at := from(px)
|
||||||
parts := []*ui.Element{ends[0], ends[1], window, track}
|
parts := []ui.Element{ends[0], ends[1], window, track}
|
||||||
held := -1
|
held := -1
|
||||||
for i, e := range parts {
|
for i, e := range parts {
|
||||||
if _, _, ok := e.Dragged(); ok && held < 0 {
|
if _, _, ok := e.Dragged(); ok && held < 0 {
|
||||||
|
|||||||
@@ -7,21 +7,21 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
type brushHarness struct {
|
type brushHarness struct {
|
||||||
value [2]float64
|
value [2]float64
|
||||||
changes int
|
changes int
|
||||||
disabled bool
|
disabled bool
|
||||||
el *ui.Element
|
bounds ui.Rect
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *brushHarness) view(c *ui.Context) {
|
func (h *brushHarness) view(c *ui.Context) {
|
||||||
core.Use(c, core.Settings{Mode: core.Light})
|
core.Use(c, core.Settings{Mode: core.Light})
|
||||||
ui.Column(c).Padding(10).Width(420).Children(func() {
|
ui.Column(c).Padding(10).Width(420).Children(func() {
|
||||||
r := ChartBrush(c, &h.value, ChartBrushOptions{Label: "Months", Min: 0, Max: 100, Step: 1, Disabled: h.disabled})
|
r := ChartBrush(c, &h.value, ChartBrushOptions{Label: "Months", Min: 0, Max: 100, Step: 1, Disabled: h.disabled})
|
||||||
h.el = r.Element
|
h.bounds = r.Element.Bounds()
|
||||||
if r.Changed() {
|
if r.Changed() {
|
||||||
h.changes++
|
h.changes++
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,8 @@ func TestChartBrushKeyboard(t *testing.T) {
|
|||||||
func TestChartBrushDrag(t *testing.T) {
|
func TestChartBrushDrag(t *testing.T) {
|
||||||
h := &brushHarness{value: [2]float64{20, 40}}
|
h := &brushHarness{value: [2]float64{20, 40}}
|
||||||
tt := ui.NewTester(h.view, 460, 200)
|
tt := ui.NewTester(h.view, 460, 200)
|
||||||
b := h.el.Bounds()
|
tt.Frame() // Bounds reads the previous frame
|
||||||
|
b := h.bounds
|
||||||
x := func(v float32) float32 { return b.X + 400*v/100 }
|
x := func(v float32) float32 { return b.X + 400*v/100 }
|
||||||
y := b.Y + 24
|
y := b.Y + 24
|
||||||
tt.Press(x(30), y) // inside the window
|
tt.Press(x(30), y) // inside the window
|
||||||
@@ -86,7 +87,8 @@ func TestChartBrushDrag(t *testing.T) {
|
|||||||
func TestChartBrushDisabledAndBad(t *testing.T) {
|
func TestChartBrushDisabledAndBad(t *testing.T) {
|
||||||
h := &brushHarness{value: [2]float64{20, 40}, disabled: true}
|
h := &brushHarness{value: [2]float64{20, 40}, disabled: true}
|
||||||
tt := ui.NewTester(h.view, 460, 200)
|
tt := ui.NewTester(h.view, 460, 200)
|
||||||
b := h.el.Bounds()
|
tt.Frame() // Bounds reads the previous frame
|
||||||
|
b := h.bounds
|
||||||
tt.Press(b.X+120, b.Y+24)
|
tt.Press(b.X+120, b.Y+24)
|
||||||
tt.Move(b.X+200, b.Y+24)
|
tt.Move(b.X+200, b.Y+24)
|
||||||
tt.Release(b.X+200, b.Y+24)
|
tt.Release(b.X+200, b.Y+24)
|
||||||
@@ -128,7 +130,8 @@ func TestChartBrushSnapshot(t *testing.T) {
|
|||||||
func TestChartBrushWindowDragPreservesSpanAtEdges(t *testing.T) {
|
func TestChartBrushWindowDragPreservesSpanAtEdges(t *testing.T) {
|
||||||
h := &brushHarness{value: [2]float64{20, 60}}
|
h := &brushHarness{value: [2]float64{20, 60}}
|
||||||
tt := ui.NewTester(h.view, 460, 200)
|
tt := ui.NewTester(h.view, 460, 200)
|
||||||
b := h.el.Bounds()
|
tt.Frame() // Bounds reads the previous frame
|
||||||
|
b := h.bounds
|
||||||
tt.Press(b.X+160, b.Y+24)
|
tt.Press(b.X+160, b.Y+24)
|
||||||
tt.Move(b.X+380, b.Y+24)
|
tt.Move(b.X+380, b.Y+24)
|
||||||
tt.Release(b.X+380, b.Y+24)
|
tt.Release(b.X+380, b.Y+24)
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import (
|
|||||||
|
|
||||||
"github.com/egoist/mygo/ui"
|
"github.com/egoist/mygo/ui"
|
||||||
|
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/core"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/internal"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal"
|
||||||
"github.com/ZacharyZhang-NY/MujicaUI/theme"
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChartCrosshairOptions configure a ChartCrosshair.
|
// ChartCrosshairOptions configure a ChartCrosshair.
|
||||||
|
|||||||
Loaded 100 of 1264 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user