Files
MujicaUI/examples/gallery/pages_git_demos.go
T

371 lines
13 KiB
Go

package main
import (
"fmt"
"strings"
"time"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/data"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/git"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/input"
)
var gitDemoTime = time.Date(2026, 10, 5, 9, 30, 0, 0, time.UTC)
const gitDemoOld = "package court\n\nimport \"fmt\"\n\n// Greet welcomes a guest.\nfunc Greet(name string) {\n\tfmt.Println(\"Welcome,\", name)\n}\n\nfunc Leave() {}\n\nfunc Dance() {}\n\nfunc Bow() {}\n"
const gitDemoNew = "package court\n\nimport \"fmt\"\n\n// Greet welcomes a guest by title.\nfunc Greet(title, name string) {\n\tfmt.Println(\"Welcome,\", title, name)\n}\n\nfunc Leave() {}\n\nfunc Dance() {}\n\nfunc Bow() {}\n\nfunc Curtsy() {}\n"
func gitDemoCommits(n int) []git.Commit {
out := make([]git.Commit, n)
authors := []string{"Sakiko", "Mutsumi", "Uika", "Umiri", "Nyamu"}
for i := range out {
out[i] = git.Commit{Hash: fmt.Sprintf("%016x%024x", uint64(n-i)*0x9E3779B97F4A7C15, n-i), Author: authors[i%5],
When: gitDemoTime.Add(-time.Duration(i) * 37 * time.Minute), Subject: fmt.Sprintf("Chapter %d of the chronicle", n-i)}
if i+1 < n {
out[i].Parents = []string{fmt.Sprintf("%016x%024x", uint64(n-i-1)*0x9E3779B97F4A7C15, n-i-1)}
}
}
if n > 0 {
out[0].Refs = []string{"main", "origin/main"}
}
return out
}
func gitDemoGraph() []git.Commit {
cs := []git.Commit{
{Hash: "e7", Parents: []string{"e5", "f3"}, Subject: "Merge branch 'feature/lanes'", Refs: []string{"main"}},
{Hash: "f3", Parents: []string{"f2"}, Subject: "Color lanes by branch"},
{Hash: "e5", Parents: []string{"e4", "h1"}, Subject: "Merge hotfix"},
{Hash: "h1", Parents: []string{"e3"}, Subject: "Fix the overture timing", Refs: []string{"hotfix"}},
{Hash: "f2", Parents: []string{"f1"}, Subject: "Draw merge lines"},
{Hash: "e4", Parents: []string{"e3"}, Subject: "Tune the ballroom palette"},
{Hash: "f1", Parents: []string{"e3"}, Subject: "Start lane layout"},
{Hash: "e3", Parents: []string{"e2"}, Subject: "Add the commit list"},
{Hash: "e2", Parents: []string{"e1"}, Subject: "Add the blame view"},
{Hash: "e1", Subject: "Initial commit", Refs: []string{"v1.0.0"}},
}
for i := range cs {
cs[i].Author = []string{"Sakiko", "Mutsumi", "Uika"}[i%3]
cs[i].When = gitDemoTime.Add(-time.Duration(i) * 2 * time.Hour)
}
return cs
}
func gitDemoBranches() []git.Branch {
return []git.Branch{
{Name: "main", Current: true, Upstream: "origin/main", Ahead: 2, Behind: 1},
{Name: "feature/lanes", Upstream: "origin/feature/lanes", Ahead: 5},
{Name: "fix/blame"},
{Name: "origin/main", Remote: true},
{Name: "origin/feature/lanes", Remote: true},
{Name: "origin/release/1.1", Remote: true},
}
}
func gitDiffViewerDemo(c *ui.Context, log func(string)) {
type state struct {
viewer git.DiffViewerState
split, plain bool
context float64
}
s := ui.Local(c.Root(), "301", func() state { return state{context: 2} })
navigationDemo(c, func() {
navigationCheck(c, &s.split, "Side by side", log)
navigationCheck(c, &s.plain, "Plain text", log)
navigationSlider(c, &s.context, "Context", 1, 6, log)
}, func() {
lang := "go"
if s.plain {
lang = ""
}
git.DiffViewer(c, &s.viewer, gitDemoOld, gitDemoNew, git.DiffViewerOptions{SideBySide: s.split, Context: int(s.context), Language: lang}).Height(380)
})
}
func gitInlineDiffDemo(c *ui.Context, log func(string)) {
type state struct {
from, to string
mono bool
}
s := ui.Local(c.Root(), "302", func() state {
return state{from: "The court meets at noon in the east hall.", to: "The council meets at dusk in the east hall."}
})
navigationDemo(c, func() {
navigationCheck(c, &s.mono, "Monospace", log)
}, func() {
if input.TextInput(c, &s.from, input.TextInputOptions{Label: "Old line"}).Changed() {
log("Old: " + s.from)
}
if input.TextInput(c, &s.to, input.TextInputOptions{Label: "New line"}).Changed() {
log("New: " + s.to)
}
git.InlineDiff(c, s.from, s.to, git.InlineDiffOptions{Mono: s.mono})
})
}
func gitThreeWayMergeDemo(c *ui.Context, log func(string)) {
type state struct {
merge git.ThreeWayMergeState
disabled bool
}
s := ui.Local(c.Root(), "303", func() state { return state{} })
base := "title: Overture\ntempo: 96\nkey: D minor\nlength: 4:10\nhall: east"
ours := "title: Overture\ntempo: 104\nkey: D minor\nlength: 4:10\nhall: east\nencore: yes"
theirs := "title: Overture\ntempo: 92\nkey: D minor\nlength: 4:30\nhall: east"
navigationDemo(c, func() {
navigationCheck(c, &s.disabled, "Disabled", log)
}, func() {
r := git.ThreeWayMerge(c, &s.merge, base, ours, theirs, git.ThreeWayMergeOptions{Disabled: s.disabled})
r.Element.Height(360)
if r.Changed() {
done, total := s.merge.Resolved()
log(fmt.Sprintf("Changed: %d of %d resolved", done, total))
}
ui.Text(c, s.merge.Result()).Font("monospace").TextColor(core.Tokens(c).TextMuted)
})
}
func gitConflictResolverDemo(c *ui.Context, log func(string)) {
type state struct {
res git.ConflictResolverState
disabled bool
}
s := ui.Local(c.Root(), "304", func() state { return state{} })
text := "# Program\n<<<<<<< HEAD\nOpening: Overture in D minor\n=======\nOpening: Prelude in G major\n>>>>>>> feature/program\nInterval: 15 minutes\n<<<<<<< HEAD\nFinale: Masquerade\n=======\nFinale: Ave Mujica\n>>>>>>> feature/program"
navigationDemo(c, func() {
navigationCheck(c, &s.disabled, "Disabled", log)
}, func() {
if git.ConflictResolver(c, &s.res, text, git.ConflictResolverOptions{Disabled: s.disabled}).Changed() {
done, total := s.res.Resolved()
log(fmt.Sprintf("Changed: %d of %d resolved", done, total))
}
})
}
func gitChangesListDemo(c *ui.Context, log func(string)) {
type state struct {
list git.ChangesListState
files []git.ChangedFile
disabled bool
}
s := ui.Local(c.Root(), "305", func() state {
return state{files: []git.ChangedFile{
{Path: "commit_graph.go", Status: git.GitAdded, Staged: true},
{Path: "internal/git.go", Status: git.GitModified},
{Path: "docs/old.md", Status: git.GitDeleted},
{Path: "notes.txt", Status: git.GitUntracked},
{Path: "icons/svg/tag.svg", Status: git.GitRenamed},
}}
})
navigationDemo(c, func() {
navigationCheck(c, &s.disabled, "Disabled", log)
}, func() {
r := git.ChangesList(c, &s.list, s.files, git.ChangesListOptions{Disabled: s.disabled})
r.Element.Height(320)
if a, path, ok := r.Action(); ok {
log("Action: " + a + " " + path)
s.files = gitStage(s.files, a, path)
}
if r.Changed() && s.list.Selected() >= 0 {
log("Selected: " + s.files[s.list.Selected()].Path)
}
})
}
// gitStage applies a stage action to a copy of files.
func gitStage(files []git.ChangedFile, action, path string) []git.ChangedFile {
out := append([]git.ChangedFile(nil), files...)
for i := range out {
switch {
case action == "stage_all":
out[i].Staged = true
case action == "unstage_all":
out[i].Staged = false
case out[i].Path == path:
out[i].Staged = action == "stage"
}
}
return out
}
func gitCommitInputDemo(c *ui.Context, log func(string)) {
type state struct {
msg git.CommitMessage
busy, disabled bool
until time.Time
}
s := ui.Local(c.Root(), "306", func() state { return state{} })
if s.busy && c.Now().After(s.until) {
s.busy = false
s.msg = git.CommitMessage{}
log("Committed")
}
if s.busy {
c.After(s.until.Sub(c.Now()))
}
navigationDemo(c, func() {
navigationCheck(c, &s.disabled, "Disabled", log)
}, func() {
r := git.CommitInput(c, &s.msg, git.CommitInputOptions{Busy: s.busy, Disabled: s.disabled})
if r.Committed() || r.Amended() {
log(fmt.Sprintf("Submit: amend=%v title=%q", r.Amended(), s.msg.Title))
s.busy, s.until = true, c.Now().Add(1200*time.Millisecond)
}
})
}
func gitCommitGraphDemo(c *ui.Context, log func(string)) {
type state struct {
graph git.CommitGraphState
big bool
}
s := ui.Local(c.Root(), "307", func() state { return state{} })
small, big := gitDemoGraph(), gitDemoCommits(100000)
navigationDemo(c, func() {
navigationCheck(c, &s.big, "100,000 commits", log)
}, func() {
commits := small
if s.big {
commits = big
}
r := git.CommitGraph(c, &s.graph, commits, git.CommitGraphOptions{})
r.Element.Height(380)
if hash, ok := s.graph.List.Selected(); ok && (r.Changed() || r.Submitted()) {
log(fmt.Sprintf("Selected %s, open=%v", hash[:min(7, len(hash))], r.Submitted()))
}
})
}
func gitCommitListDemo(c *ui.Context, log func(string)) {
type state struct {
list data.ListState[string]
loading bool
}
s := ui.Local(c.Root(), "308", func() state { return state{} })
commits := gitDemoCommits(40)
navigationDemo(c, func() {
navigationCheck(c, &s.loading, "Loading more", log)
}, func() {
st := data.DataReady
if s.loading {
st = data.DataLoadingMore
}
r := git.CommitList(c, &s.list, commits, git.CommitListOptions{Status: st})
r.Element.Height(360)
if hash, ok := s.list.Selected(); ok && (r.Changed() || r.Submitted()) {
log(fmt.Sprintf("Selected %s, open=%v", hash[:7], r.Submitted()))
}
})
}
func gitBranchSelectorDemo(c *ui.Context, log func(string)) {
type state struct {
branch string
create, disabled bool
branches []git.Branch
}
s := ui.Local(c.Root(), "309", func() state { return state{branch: "main", create: true, branches: gitDemoBranches()} })
navigationDemo(c, func() {
navigationCheck(c, &s.create, "Allow create", log)
navigationCheck(c, &s.disabled, "Disabled", log)
}, func() {
r := git.BranchSelector(c, &s.branch, s.branches, git.BranchSelectorOptions{AllowCreate: s.create, Disabled: s.disabled})
if r.Changed() {
log("Changed: " + s.branch)
}
if name, ok := r.Created(); ok {
log("Create: " + name)
s.branches = append(append([]git.Branch(nil), s.branches...), git.Branch{Name: name})
s.branch = name
}
})
}
func gitBranchListDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "310", func() data.ListState[string] { return data.ListState[string]{} })
actions := []git.GitAction{{ID: "checkout", Label: "Check out", Icon: "git-branch"}, {ID: "delete", Label: "Delete", Icon: "trash-2"}}
r := git.BranchList(c, s, gitDemoBranches(), git.BranchListOptions{Actions: actions})
r.Element.Height(300)
if a, name, ok := r.Action(); ok {
log("Action: " + a + " " + name)
}
if key, ok := s.Selected(); ok && r.Changed() {
log("Selected: " + key)
}
}
func gitTagListDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "311", func() data.ListState[string] { return data.ListState[string]{} })
tags := []git.GitTag{
{Name: "v1.1.0", Target: "9e3779b97f4a7c15", Annotation: "Five hundred components"},
{Name: "v1.0.0", Target: "3c6ef372fe94f82a", Annotation: "The first hundred"},
{Name: "nightly", Target: "daa66d2c7ddf743f"},
}
r := git.TagList(c, s, tags, git.TagListOptions{})
r.Element.Height(220)
if _, hash, ok := r.Action(); ok {
log("Jump to " + hash[:7])
}
if key, ok := s.Selected(); ok && r.Changed() {
log("Selected: " + key)
}
}
func gitStashListDemo(c *ui.Context, log func(string)) {
type state struct {
list data.ListState[string]
stashes []git.Stash
}
s := ui.Local(c.Root(), "312", func() state {
return state{stashes: []git.Stash{
{Ref: "stash@{0}", Message: "WIP on main: lane colors", Branch: "main"},
{Ref: "stash@{1}", Message: "Try serif headings", Branch: "feature/type"},
{Ref: "stash@{2}", Message: "Blame hover experiment", Branch: "fix/blame"},
}}
})
r := git.StashList(c, &s.list, s.stashes, git.StashListOptions{})
r.Element.Height(240)
if a, ref, ok := r.Action(); ok {
log("Action: " + a + " " + ref)
if a != "apply" {
var keep []git.Stash
for _, st := range s.stashes {
if st.Ref != ref {
keep = append(keep, st)
}
}
s.stashes = keep
}
}
}
func gitBlameViewDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "313", func() git.BlameViewState { return git.BlameViewState{} })
var lines []git.BlameLine
for i, l := range strings.Split(strings.TrimSuffix(gitDemoNew, "\n"), "\n") {
who := []git.BlameLine{
{Hash: "9e3779b97f4a7c15", Author: "Sakiko", When: gitDemoTime, Summary: "Create the court package"},
{Hash: "3c6ef372fe94f82a", Author: "Uika", When: gitDemoTime.Add(-48 * time.Hour), Summary: "Greet guests by title"},
}[min(i/4%2, 1)]
who.Text = l
lines = append(lines, who)
}
git.BlameView(c, s, lines, git.BlameViewOptions{Language: "go"}).Height(380)
ui.Text(c, "Hover the left column for commit details.").TextColor(core.Tokens(c).TextMuted)
}
func gitFileHistoryDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "314", func() data.ListState[string] { return data.ListState[string]{} })
r := git.FileHistory(c, s, "court/greet.go", gitDemoCommits(12), git.FileHistoryOptions{})
r.Element.Height(360)
if _, hash, ok := r.Action(); ok {
log("Diff at " + hash[:7])
}
if key, ok := s.Selected(); ok && r.Changed() {
log("Selected: " + key[:7])
}
}