This file shows how the app hangs together for real key presses.
For the common names used here, read docs/ABSTRACTIONS.md.
The basic rule is:
key press
-> internal/tui/update.go decides what the key means
-> internal/tui/actions.go or internal/tui/load.go starts background work
-> internal/git runs git or gh
-> a message comes back to Update
-> internal/tui/view.go draws the new screen
Names that appear in most flows:
Model: the current UI state.Runner: the Git command wrapper stored onModelasm.runner.Config: the loaded user settings stored onModelasm.config.repoLoadedMsg,branchesLoadedMsg,gitActionFinishedMsg: messages sent back toUpdatewhen background work finishes.
main.go
-> config.Load()
-> git.NewRunner("")
-> tui.New(runner, cfg)
-> tea.NewProgram(...).Run()
Important files:
What happens after startup:
Model.Init()
-> tickSplash()
-> updateSplash()
-> loadDefault()
-> repoLoadedMsg
-> handleRepoLoaded()
-> View()
key: s
-> update.go: updateKey()
-> update.go: updateDashboardKey()
-> selectedFile()
-> FileStatus.GitPaths()
-> action(...)
-> git.Runner.StageOutput()
-> git add -- <path...>
-> gitActionFinishedMsg
-> handleGitActionFinished()
-> loadCurrent()
-> repoLoadedMsg
-> handleRepoLoaded()
-> View()
Files to read:
internal/tui/update.gointernal/tui/actions.gointernal/git/changes.gointernal/tui/load.gointernal/tui/view.go
FileStatus.GitPaths() matters for renames because Git needs both old and new
paths for some operations. The UI still shows DisplayPath() so users see
old/path -> new/path.
key: b
-> update.go: updateKey()
-> update.go: updateDashboardKey()
-> loadBranches()
-> git.Runner.Branches()
-> branchesLoadedMsg
-> handleBranchesLoaded()
-> branchesView()
-> View()
Then pressing enter inside the branch picker:
key: enter
-> update.go: updateKey()
-> update.go: updateFocusedMode()
-> update.go: updateBranches()
-> action(...)
-> git.Runner.SwitchBranchOutput()
-> git switch <branch>
-> gitActionFinishedMsg
-> handleGitActionFinished()
-> loadCurrent()
-> View()
Files to read:
key: c
-> update.go: updateKey()
-> update.go: updateDashboardKey()
-> mode becomes "commit"
-> header() shows the commit text input
Then typing a message and pressing enter:
key: enter
-> update.go: updateKey()
-> update.go: updateFocusedMode()
-> update.go: updateCommit()
-> action(...)
-> git.Runner.CommitOutput()
-> git commit -m <message>
-> gitActionFinishedMsg
-> handleGitActionFinished()
-> loadCurrent()
-> View()
Files to read:
key: r
-> update.go: updateKey()
-> update.go: updateDashboardKey()
-> pull request options view
Then pressing g in the pull request options:
key: g
-> actions.go: pullRequestAction()
-> git.Runner.PullRequestOutput()
-> require current branch to already be pushed
-> gh pr view, or generate title/body and gh pr create
-> gitActionFinishedMsg
-> handleGitActionFinished()
-> View()
Then pressing m in the pull request options:
key: m
-> actions.go: manualPullRequestAction()
-> gh pr create
-> gitActionFinishedMsg
-> handleGitActionFinished()
-> View()
Files to read:
The app uses a few custom Bubble Tea messages:
repoLoadedMsg: repo info, changed files, and viewer text finished loading.branchesLoadedMsg: branch list finished loading.gitActionFinishedMsg: a Git command finished, with output or an error.
These are defined in internal/tui/model.go and
handled in internal/tui/update.go.