Git: components 301-320

This commit is contained in:
ZacharyZhang-NY committed 2026-10-05 14:31:37 -04:00
1 parent 6f0c02d339
commit e789683ffa
121 files changed
+7031 -2

No files matched your search

+296 -1
View File
@@ -1,3 +1,298 @@
package main
var gitPages = []page{}
var gitPages = []page{
{
ID: "301", Name: "DiffViewer", ZhName: "差异查看器 比较 diff", Category: "Git",
Demo: gitDiffViewerDemo,
Code: `var state mujica.DiffViewerState
from := "func main() {\n\tprintln(\"hi\")\n}\n"
to := "func main() {\n\tprintln(\"hello\")\n}\n"
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.DiffViewer(c, &state, from, to, mujica.DiffViewerOptions{Language: "go", SideBySide: true}).Height(240)
}
ui.Render(view, 400, 300, 1)`,
Keys: "Tab focuses the list; arrows and Page Up/Down scroll. Fold buttons take Tab; Enter or Space shows the hidden lines.",
},
{
ID: "302", Name: "InlineDiff", ZhName: "行内差异 词级差异", Category: "Git",
Demo: gitInlineDiffDemo,
Code: `view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.InlineDiff(c, "The court meets at noon", "The court meets at dusk", mujica.InlineDiffOptions{})
}
ui.Render(view, 400, 300, 1)`,
Keys: "Not focusable; each line is read with its full text.",
},
{
ID: "303", Name: "ThreeWayMerge", ZhName: "三方合并 合并冲突", Category: "Git",
Demo: gitThreeWayMergeDemo,
Code: `var state mujica.ThreeWayMergeState
base, local, remote := "a\nb\nc", "a\nB1\nc", "a\nB2\nc"
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.ThreeWayMerge(c, &state, base, local, remote, mujica.ThreeWayMergeOptions{})
r.Element.Height(280)
if done, total := state.Resolved(); r.Changed() && done == total {
_ = state.Result() // write the merged file
}
}
ui.Render(view, 400, 300, 1)`,
Keys: "Tab reaches the take buttons of each conflict; Enter or Space chooses. The list scrolls with the arrows.",
},
{
ID: "304", Name: "ConflictResolver", ZhName: "冲突解决 冲突标记", Category: "Git",
Demo: gitConflictResolverDemo,
Code: `var state mujica.ConflictResolverState
text := "a\n<<<<<<< HEAD\nmine\n=======\ntheirs\n>>>>>>> feature\nb"
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
if mujica.ConflictResolver(c, &state, text, mujica.ConflictResolverOptions{}).Changed() {
_ = state.Result() // save the file once every block is resolved
}
}
ui.Render(view, 400, 300, 1)`,
Keys: "Tab moves through each block's buttons; Enter or Space accepts a side; Undo clears the choice.",
},
{
ID: "305", Name: "ChangesList", ZhName: "更改列表 暂存 未暂存", Category: "Git",
Demo: gitChangesListDemo,
Code: `var state mujica.ChangesListState
files := []mujica.ChangedFile{
{Path: "main.go", Status: mujica.GitModified, Staged: true},
{Path: "notes.txt", Status: mujica.GitUntracked},
}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.ChangesList(c, &state, files, mujica.ChangesListOptions{})
r.Element.Height(240)
if action, path, ok := r.Action(); ok {
_, _ = action, path // run git add or git restore --staged
}
}
ui.Render(view, 400, 300, 1)`,
Keys: "Up/Down choose a file, skipping group titles; typing jumps by name; stage buttons take Tab, Enter or Space.",
},
{
ID: "306", Name: "CommitInput", ZhName: "提交输入 提交信息", Category: "Git",
Demo: gitCommitInputDemo,
Code: `msg := mujica.CommitMessage{}
busy := false
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.CommitInput(c, &msg, mujica.CommitInputOptions{Busy: busy})
if r.Committed() || r.Amended() {
busy = true // run git commit, then clear busy
}
}
ui.Render(view, 400, 300, 1)`,
Keys: "Enter in the summary or Cmd/Ctrl+Enter in the description commits; Tab reaches Amend and Commit.",
},
{
ID: "307", Name: "CommitGraph", ZhName: "提交图谱 泳道 分支图", Category: "Git",
Demo: gitCommitGraphDemo,
Code: `var state mujica.CommitGraphState
commits := []mujica.Commit{
{Hash: "c3", Parents: []string{"c2", "b1"}, Subject: "Merge branch", Refs: []string{"main"}},
{Hash: "b1", Parents: []string{"c1"}, Subject: "Feature"},
{Hash: "c2", Parents: []string{"c1"}, Subject: "Fix"},
{Hash: "c1", Subject: "Initial commit"},
}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.CommitGraph(c, &state, commits, mujica.CommitGraphOptions{}).Element.Height(240)
}
ui.Render(view, 400, 300, 1)`,
Keys: "Up/Down/Home/End choose a commit; typing jumps by subject; Enter opens it.",
},
{
ID: "308", Name: "CommitList", ZhName: "提交列表 提交历史", Category: "Git",
Demo: gitCommitListDemo,
Code: `var state mujica.ListState[string]
commits := []mujica.Commit{{Hash: "a1b2c3d4", Author: "Sakiko", When: time.Now(), Subject: "Add the commit list"}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.CommitList(c, &state, commits, mujica.CommitListOptions{})
r.Element.Height(240)
if hash, ok := state.Selected(); ok && r.Submitted() {
_ = hash // show the commit
}
}
ui.Render(view, 400, 300, 1)`,
Keys: "Up/Down/Home/End choose a commit; typing jumps by subject; Enter opens it.",
},
{
ID: "309", Name: "BranchSelector", ZhName: "分支选择器 切换分支", Category: "Git",
Demo: gitBranchSelectorDemo,
Code: `branch := "main"
branches := []mujica.Branch{{Name: "main", Current: true}, {Name: "origin/main", Remote: true}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.BranchSelector(c, &branch, branches, mujica.BranchSelectorOptions{AllowCreate: true})
if name, ok := r.Created(); ok {
_ = name // run git switch -c name
}
}
ui.Render(view, 400, 300, 1)`,
Keys: "Enter or Space opens; type to filter; Up/Down move; Enter chooses or creates; Escape closes.",
},
{
ID: "310", Name: "BranchList", ZhName: "分支列表 上游 领先落后", Category: "Git",
Demo: gitBranchListDemo,
Code: `var state mujica.ListState[string]
branches := []mujica.Branch{{Name: "main", Current: true, Upstream: "origin/main", Ahead: 2}}
actions := []mujica.GitAction{{ID: "delete", Label: "Delete", Icon: "trash-2"}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.BranchList(c, &state, branches, mujica.BranchListOptions{Actions: actions})
r.Element.Height(200)
if action, name, ok := r.Action(); ok {
_, _ = action, name // run the action on the branch
}
}
ui.Render(view, 400, 300, 1)`,
Keys: "Up/Down choose a branch; row actions take Tab, Enter or Space.",
},
{
ID: "311", Name: "TagList", ZhName: "标签列表 tag", Category: "Git",
Demo: gitTagListDemo,
Code: `var state mujica.ListState[string]
tags := []mujica.GitTag{{Name: "v1.1.0", Target: "a1b2c3d4", Annotation: "Release"}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.TagList(c, &state, tags, mujica.TagListOptions{})
r.Element.Height(200)
if _, hash, ok := r.Action(); ok {
_ = hash // show the tagged commit
}
}
ui.Render(view, 400, 300, 1)`,
Keys: "Up/Down choose a tag; the jump button takes Tab, Enter or Space.",
},
{
ID: "312", Name: "StashList", ZhName: "贮藏列表 stash", Category: "Git",
Demo: gitStashListDemo,
Code: `var state mujica.ListState[string]
stashes := []mujica.Stash{{Ref: "stash@{0}", Message: "WIP on main", Branch: "main"}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.StashList(c, &state, stashes, mujica.StashListOptions{})
r.Element.Height(200)
if action, ref, ok := r.Action(); ok {
_, _ = action, ref // run git stash apply, pop or drop
}
}
ui.Render(view, 400, 300, 1)`,
Keys: "Up/Down choose a stash; Apply, Pop and Drop take Tab, Enter or Space.",
},
{
ID: "313", Name: "BlameView", ZhName: "逐行追溯 blame 作者", Category: "Git",
Demo: gitBlameViewDemo,
Code: `var state mujica.BlameViewState
lines := []mujica.BlameLine{
{Hash: "a1b2c3d4", Author: "Sakiko", When: time.Now(), Summary: "Add main", Text: "package main"},
{Hash: "a1b2c3d4", Author: "Sakiko", When: time.Now(), Summary: "Add main", Text: "func main() {}"},
}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.BlameView(c, &state, lines, mujica.BlameViewOptions{Language: "go"}).Height(240)
}
ui.Render(view, 400, 300, 1)`,
Keys: "Tab focuses the code; arrows scroll; typing jumps to a line starting with the text. Hover the left column for details.",
},
{
ID: "314", Name: "FileHistory", ZhName: "文件历史 单文件提交", Category: "Git",
Demo: gitFileHistoryDemo,
Code: `var state mujica.ListState[string]
commits := []mujica.Commit{{Hash: "a1b2c3d4", Author: "Sakiko", When: time.Now(), Subject: "Edit main.go"}}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.FileHistory(c, &state, "main.go", commits, mujica.FileHistoryOptions{})
r.Element.Height(240)
if _, hash, ok := r.Action(); ok {
_ = hash // open the diff of main.go at hash
}
}
ui.Render(view, 400, 300, 1)`,
Keys: "Up/Down choose a commit; View changes takes Tab, Enter or Space.",
},
{
ID: "315", Name: "PullRequestCard", ZhName: "拉取请求卡片 合并请求 PR", Category: "Git",
Demo: gitPullRequestCardDemo,
Code: `pr := mujica.PullRequest{Number: 42, Title: "Add git components", Author: "Sakiko", Head: "feature", Base: "main",
ChecksPassed: 3, Reviewers: []mujica.Reviewer{{Name: "Uika", State: mujica.ReviewApproved}}, Added: 120, Deleted: 8, Files: 6}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
if mujica.PullRequestCard(c, pr, mujica.PullRequestCardOptions{}).Changed() {
// open the pull request
}
}
ui.Render(view, 400, 300, 1)`,
Keys: "Tab reaches the title link; Enter or Space opens the pull request.",
},
{
ID: "316", Name: "ReviewComment", ZhName: "评审评论 代码评审", Category: "Git",
Demo: gitReviewCommentDemo,
Code: `thread := mujica.ReviewThread{File: "main.go", Line: 12, Messages: []mujica.ReviewMessage{
{Author: "Uika", When: time.Now(), Body: "Can this return early?"},
}}
draft := ""
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.ReviewComment(c, thread, &draft, mujica.ReviewCommentOptions{})
if text, ok := r.Replied(); ok {
thread.Messages = append(thread.Messages, mujica.ReviewMessage{Author: "Sakiko", When: time.Now(), Body: text})
}
if r.Resolved() || r.Reopened() {
thread.Resolved = !thread.Resolved
}
}
ui.Render(view, 400, 300, 1)`,
Keys: "Tab moves through the reply field and buttons; Cmd/Ctrl+Enter sends the reply.",
},
{
ID: "317", Name: "DiffStat", ZhName: "差异统计 增删行数", Category: "Git",
Demo: gitDiffStatDemo,
Code: `view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.DiffStat(c, 120, 8, mujica.DiffStatOptions{})
}
ui.Render(view, 400, 300, 1)`,
Keys: "Not focusable; read as additions and deletions.",
},
{
ID: "318", Name: "GitStatusBadge", ZhName: "Git 状态标记 文件状态", Category: "Git",
Demo: gitStatusBadgeDemo,
Code: `view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.GitStatusBadge(c, mujica.GitModified, mujica.GitStatusBadgeOptions{ShowLabel: true})
}
ui.Render(view, 400, 300, 1)`,
Keys: "Not focusable; each badge is read by its status name.",
},
{
ID: "319", Name: "CommandBlock", ZhName: "命令块 终端输出 退出码", Category: "Git",
Demo: gitCommandBlockDemo,
Code: `open := true
run := mujica.CommandRun{Command: "go test ./...", Output: "\x1b[32mok\x1b[0m example 0.4s", Duration: 400 * time.Millisecond}
view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
r := mujica.CommandBlock(c, run, &open, mujica.CommandBlockOptions{})
if what, ok := r.Copied(); ok {
_ = what // "command" or "output" is on the clipboard
}
}
ui.Render(view, 400, 300, 1)`,
Keys: "Tab reaches the header; Enter or Space folds the output; the copy buttons take Tab.",
},
{
ID: "320", Name: "AnsiText", ZhName: "ANSI 文本 终端颜色", Category: "Git",
Demo: gitAnsiTextDemo,
Code: `view := func(c *ui.Context) {
mujica.Use(c, mujica.Settings{})
mujica.AnsiText(c, "\x1b[1;32mPASS\x1b[0m 12 tests\n\x1b[31mFAIL\x1b[0m 1 test", mujica.AnsiTextOptions{})
}
ui.Render(view, 400, 300, 1)`,
Keys: "Not focusable; shows the text with its styles.",
},
}
+134
View File
@@ -0,0 +1,134 @@
package main
import (
"fmt"
"time"
"github.com/egoist/mygo/ui"
mujica "mujicaui"
)
func gitPullRequestCardDemo(c *ui.Context, log func(string)) {
type state struct {
st int
failed, disabled bool
}
s := ui.Local(c.Root(), "315", func() state { return state{} })
navigationDemo(c, func() {
mujica.SegmentedControl(c, &s.st, []mujica.Segment{{Label: "Open"}, {Label: "Draft"}, {Label: "Merged"}, {Label: "Closed"}}, mujica.SegmentedControlOptions{Label: "State"})
navigationCheck(c, &s.failed, "Failing check", log)
navigationCheck(c, &s.disabled, "Disabled", log)
}, func() {
pr := mujica.PullRequest{Number: 318, Title: "Git components 301–320", Author: "Sakiko", State: mujica.PullRequestState(s.st),
Head: "feature/git", Base: "main", ChecksPassed: 12, ChecksPending: 1,
Reviewers: []mujica.Reviewer{{Name: "Uika", State: mujica.ReviewApproved}, {Name: "Mutsumi", State: mujica.ReviewChangesRequested}, {Name: "Umiri"}},
Added: 2412, Deleted: 310, Files: 46}
if s.failed {
pr.ChecksFailed = 1
}
if mujica.PullRequestCard(c, pr, mujica.PullRequestCardOptions{Disabled: s.disabled}).Changed() {
log(fmt.Sprintf("Open #%d", pr.Number))
}
})
}
func gitReviewCommentDemo(c *ui.Context, log func(string)) {
type state struct {
thread mujica.ReviewThread
draft string
disabled bool
}
s := ui.Local(c.Root(), "316", func() state {
return state{thread: mujica.ReviewThread{File: "commit_graph.go", Line: 88, Messages: []mujica.ReviewMessage{
{Author: "Uika", When: gitDemoTime, Body: "Should merge lanes reuse the parent's color?"},
{Author: "Sakiko", When: gitDemoTime.Add(time.Hour), Body: "Yes, the first parent keeps it."},
}}}
})
navigationDemo(c, func() {
navigationCheck(c, &s.disabled, "Disabled", log)
}, func() {
r := mujica.ReviewComment(c, s.thread, &s.draft, mujica.ReviewCommentOptions{Disabled: s.disabled})
if text, ok := r.Replied(); ok {
log("Reply: " + text)
s.thread.Messages = append(s.thread.Messages, mujica.ReviewMessage{Author: "You", When: c.Now(), Body: text})
}
if r.Resolved() {
log("Resolved")
s.thread.Resolved = true
}
if r.Reopened() {
log("Reopened")
s.thread.Resolved = false
}
})
}
func gitDiffStatDemo(c *ui.Context, log func(string)) {
type state struct{ added, deleted, blocks float64 }
s := ui.Local(c.Root(), "317", func() state { return state{added: 120, deleted: 30, blocks: 5} })
navigationDemo(c, func() {
navigationSlider(c, &s.added, "Added", 0, 500, log)
navigationSlider(c, &s.deleted, "Deleted", 0, 500, log)
navigationSlider(c, &s.blocks, "Blocks", 1, 10, log)
}, func() {
mujica.DiffStat(c, int(s.added), int(s.deleted), mujica.DiffStatOptions{Blocks: int(s.blocks)})
})
}
func gitStatusBadgeDemo(c *ui.Context, log func(string)) {
type state struct{ labels bool }
s := ui.Local(c.Root(), "318", func() state { return state{} })
navigationDemo(c, func() {
navigationCheck(c, &s.labels, "Show labels", log)
}, func() {
ui.Row(c).Gap(16).Wrap().Children(func() {
for st := mujica.GitModified; st <= mujica.GitCopied; st++ {
mujica.GitStatusBadge(c, st, mujica.GitStatusBadgeOptions{ShowLabel: s.labels})
}
})
})
}
func gitCommandBlockDemo(c *ui.Context, log func(string)) {
type state struct {
open, failed, running bool
}
s := ui.Local(c.Root(), "319", func() state { return state{open: true} })
navigationDemo(c, func() {
navigationCheck(c, &s.failed, "Failed", log)
navigationCheck(c, &s.running, "Running", log)
}, func() {
run := mujica.CommandRun{Command: "go test -run Git ./...", Duration: 3214 * time.Millisecond,
Output: "\x1b[32mok\x1b[0m \tmujicaui\t3.214s\n\x1b[33mwarn\x1b[0m slow test: TestCommitGraphVirtualized"}
if s.failed {
run.ExitCode = 1
run.Output = "\x1b[31m--- FAIL\x1b[0m: TestDiffViewer (0.02s)\n\x1b[1mFAIL\x1b[0m\tmujicaui\t0.401s"
}
run.Running = s.running
r := mujica.CommandBlock(c, run, &s.open, mujica.CommandBlockOptions{})
if r.Toggled() {
log(fmt.Sprintf("Toggled: open=%v", s.open))
}
if what, ok := r.Copied(); ok {
log("Copied " + what)
}
})
}
func gitAnsiTextDemo(c *ui.Context, log func(string)) {
type state struct{ wrap bool }
s := ui.Local(c.Root(), "320", func() state { return state{} })
navigationDemo(c, func() {
navigationCheck(c, &s.wrap, "Wrap", log)
}, func() {
ui.Column(c).Padding(12).Background(mujica.Tokens(c).Surface).Radius(4).Children(func() {
mujica.AnsiText(c, "\x1b[1mBuilding\x1b[0m mujica \x1b[2m(v1.1)\x1b[0m\n"+
"\x1b[32m✓\x1b[0m 300 passed \x1b[31m✗\x1b[0m 2 failed \x1b[33m!\x1b[0m 1 skipped\n"+
"\x1b[30m0\x1b[31m1\x1b[32m2\x1b[33m3\x1b[34m4\x1b[35m5\x1b[36m6\x1b[37m7\x1b[90m8\x1b[91m9\x1b[92mA\x1b[93mB\x1b[94mC\x1b[95mD\x1b[96mE\x1b[97mF\x1b[0m\n"+
"\x1b[38;5;208m256-color\x1b[0m \x1b[38;2;125;32;52mtrue color\x1b[0m \x1b[4munderline\x1b[0m \x1b[3mitalic\x1b[0m \x1b[9mstruck\x1b[0m \x1b[7m inverse \x1b[0m\n"+
"A long line that only breaks when wrapping is on, so the terminal layout of columns stays intact as it was printed.\n"+
"Unknown escapes stay as text: \x1b[2K(cleared line)", mujica.AnsiTextOptions{Wrap: s.wrap})
})
})
}
+367
View File
@@ -0,0 +1,367 @@
package main
import (
"fmt"
"strings"
"time"
"github.com/egoist/mygo/ui"
mujica "mujicaui"
)
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) []mujica.Commit {
out := make([]mujica.Commit, n)
authors := []string{"Sakiko", "Mutsumi", "Uika", "Umiri", "Nyamu"}
for i := range out {
out[i] = mujica.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() []mujica.Commit {
cs := []mujica.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() []mujica.Branch {
return []mujica.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 mujica.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 = ""
}
mujica.DiffViewer(c, &s.viewer, gitDemoOld, gitDemoNew, mujica.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 mujica.TextInput(c, &s.from, mujica.TextInputOptions{Label: "Old line"}).Changed() {
log("Old: " + s.from)
}
if mujica.TextInput(c, &s.to, mujica.TextInputOptions{Label: "New line"}).Changed() {
log("New: " + s.to)
}
mujica.InlineDiff(c, s.from, s.to, mujica.InlineDiffOptions{Mono: s.mono})
})
}
func gitThreeWayMergeDemo(c *ui.Context, log func(string)) {
type state struct {
merge mujica.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 := mujica.ThreeWayMerge(c, &s.merge, base, ours, theirs, mujica.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(mujica.Tokens(c).TextMuted)
})
}
func gitConflictResolverDemo(c *ui.Context, log func(string)) {
type state struct {
res mujica.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 mujica.ConflictResolver(c, &s.res, text, mujica.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 mujica.ChangesListState
files []mujica.ChangedFile
disabled bool
}
s := ui.Local(c.Root(), "305", func() state {
return state{files: []mujica.ChangedFile{
{Path: "commit_graph.go", Status: mujica.GitAdded, Staged: true},
{Path: "internal/git.go", Status: mujica.GitModified},
{Path: "docs/old.md", Status: mujica.GitDeleted},
{Path: "notes.txt", Status: mujica.GitUntracked},
{Path: "icons/svg/tag.svg", Status: mujica.GitRenamed},
}}
})
navigationDemo(c, func() {
navigationCheck(c, &s.disabled, "Disabled", log)
}, func() {
r := mujica.ChangesList(c, &s.list, s.files, mujica.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 []mujica.ChangedFile, action, path string) []mujica.ChangedFile {
out := append([]mujica.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 mujica.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 = mujica.CommitMessage{}
log("Committed")
}
if s.busy {
c.After(s.until.Sub(c.Now()))
}
navigationDemo(c, func() {
navigationCheck(c, &s.disabled, "Disabled", log)
}, func() {
r := mujica.CommitInput(c, &s.msg, mujica.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 mujica.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 := mujica.CommitGraph(c, &s.graph, commits, mujica.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 mujica.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 := mujica.DataReady
if s.loading {
st = mujica.DataLoadingMore
}
r := mujica.CommitList(c, &s.list, commits, mujica.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 []mujica.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 := mujica.BranchSelector(c, &s.branch, s.branches, mujica.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([]mujica.Branch(nil), s.branches...), mujica.Branch{Name: name})
s.branch = name
}
})
}
func gitBranchListDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "310", func() mujica.ListState[string] { return mujica.ListState[string]{} })
actions := []mujica.GitAction{{ID: "checkout", Label: "Check out", Icon: "git-branch"}, {ID: "delete", Label: "Delete", Icon: "trash-2"}}
r := mujica.BranchList(c, s, gitDemoBranches(), mujica.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() mujica.ListState[string] { return mujica.ListState[string]{} })
tags := []mujica.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 := mujica.TagList(c, s, tags, mujica.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 mujica.ListState[string]
stashes []mujica.Stash
}
s := ui.Local(c.Root(), "312", func() state {
return state{stashes: []mujica.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 := mujica.StashList(c, &s.list, s.stashes, mujica.StashListOptions{})
r.Element.Height(240)
if a, ref, ok := r.Action(); ok {
log("Action: " + a + " " + ref)
if a != "apply" {
var keep []mujica.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() mujica.BlameViewState { return mujica.BlameViewState{} })
var lines []mujica.BlameLine
for i, l := range strings.Split(strings.TrimSuffix(gitDemoNew, "\n"), "\n") {
who := []mujica.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)
}
mujica.BlameView(c, s, lines, mujica.BlameViewOptions{Language: "go"}).Height(380)
ui.Text(c, "Hover the left column for commit details.").TextColor(mujica.Tokens(c).TextMuted)
}
func gitFileHistoryDemo(c *ui.Context, log func(string)) {
s := ui.Local(c.Root(), "314", func() mujica.ListState[string] { return mujica.ListState[string]{} })
r := mujica.FileHistory(c, s, "court/greet.go", gitDemoCommits(12), mujica.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])
}
}