Files
superwork/tui/internal/tui/toolbar.go
T
2026-06-23 05:02:15 +08:00

145 lines
3.9 KiB
Go

package tui
import (
"context"
"fmt"
"path/filepath"
"strings"
tea "charm.land/bubbletea/v2"
"superwork-tui/internal/auth"
"superwork-tui/internal/cc"
"superwork-tui/internal/config"
"superwork-tui/internal/files"
"superwork-tui/internal/git"
"superwork-tui/internal/gitea"
"superwork-tui/internal/issue"
)
type branchSyncStatusMsg struct {
status git.BranchSyncStatus
err error
}
type branchSyncDoneMsg struct {
devBranch string
autoBuildBranch string
err error
}
type envLockDoneMsg struct {
locked bool
result files.EnvLockResult
err error
}
type prDiffResultMsg struct {
issueNumber int
prDiffFile string
err error
}
// branchSyncCheckCmd checks branch sync status and returns branchSyncStatusMsg.
func branchSyncCheckCmd() tea.Cmd {
return func() tea.Msg {
ctx := context.Background()
root, err := workspaceRootFn()
if err != nil {
return branchSyncStatusMsg{err: err}
}
settings, err := config.Load()
if err != nil {
return branchSyncStatusMsg{err: fmt.Errorf("load settings: %w", err)}
}
// Empty autoBuildBranch means "follow devBranch"; resolving here makes
// the check fall into the same-branch (Unavailable) path, matching TS.
autoBuildBranch := settings.AutoBuildBranch
if autoBuildBranch == "" {
autoBuildBranch = settings.DevBranch
}
status, err := git.CheckBranchSync(ctx, git.BranchSyncOpts{
WorkspaceRoot: root,
DevBranch: settings.DevBranch,
AutoBuildBranch: autoBuildBranch,
})
return branchSyncStatusMsg{status: status, err: err}
}
}
// branchSyncRunCmd pushes devBranch to autoBuildBranch, resolving workspace root internally.
func branchSyncRunCmd(devBranch, autoBuildBranch string) tea.Cmd {
return func() tea.Msg {
ctx := context.Background()
root, err := workspaceRootFn()
if err != nil {
return branchSyncDoneMsg{devBranch: devBranch, autoBuildBranch: autoBuildBranch, err: err}
}
err = git.RunBranchSync(ctx, git.BranchSyncOpts{
WorkspaceRoot: root,
DevBranch: devBranch,
AutoBuildBranch: autoBuildBranch,
})
return branchSyncDoneMsg{devBranch: devBranch, autoBuildBranch: autoBuildBranch, err: err}
}
}
// envLockToggleCmd locks or unlocks .env files based on current state.
func envLockToggleCmd(currentlyLocked bool) tea.Cmd {
return func() tea.Msg {
root, err := workspaceRootFn()
if err != nil {
return envLockDoneMsg{err: err}
}
var result files.EnvLockResult
if currentlyLocked {
result, err = files.UnlockEnvFiles(root)
return envLockDoneMsg{locked: false, result: result, err: err}
}
result, err = files.LockEnvFiles(root)
return envLockDoneMsg{locked: true, result: result, err: err}
}
}
// prDiffSummaryCmd asks Claude to summarize the PR diff and write it to docs/pr-diff/<branch>.md.
func prDiffSummaryCmd(iss issue.Issue) tea.Cmd {
return func() tea.Msg {
ctx := context.Background()
root, err := workspaceRootFn()
if err != nil {
return prDiffResultMsg{issueNumber: iss.Number, err: err}
}
safeBranch := strings.ReplaceAll(iss.Branch, "/", "-")
outFile := filepath.Join("docs", "pr-diff", safeBranch+".md")
prompt := fmt.Sprintf(
"请阅读 PR #%s(分支 %s)的变更,并将综合摘要写入 %s。"+
"摘要应包括:改动内容、改动原因、关键技术决策以及测试方案。",
iss.PR, iss.Branch, outFile,
)
_, spawnErr := cc.SpawnClaude(ctx, cc.Opts{
Prompt: prompt,
Cwd: root,
})
if spawnErr != nil {
return prDiffResultMsg{issueNumber: iss.Number, err: spawnErr}
}
// Persist prDiffFile to Gitea state JSON (best-effort).
remote, rErr := git.DetectRepo(root)
if rErr == nil {
token, tErr := auth.ResolveGiteaToken(remote.Host)
if tErr == nil {
client := gitea.New(remote.Host, token)
_ = issue.MergeStateJSON(ctx, client, remote.Owner, remote.Repo, iss.Number, map[string]any{
"prDiffFile": outFile,
})
}
}
return prDiffResultMsg{issueNumber: iss.Number, prDiffFile: outFile}
}
}