新增: 自动更新与一键自升级

This commit is contained in:
2026-05-18 18:05:53 +08:00
parent 401713fc5a
commit ff2d898f5c
3 changed files with 330 additions and 2 deletions

View File

@@ -30,6 +30,7 @@ type Model struct {
launched string
history HistoryState
pendingCmd string // Linux: 退出后执行的命令
update UpdateState
}
func NewModel() *Model {
@@ -40,7 +41,8 @@ func NewModel() *Model {
}
func (m *Model) Init() tea.Cmd {
return nil
m.update.Checking = true
return CheckUpdateCmd()
}
// totalTabs 包含 HISTORY 的总 Tab 数
@@ -71,6 +73,22 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case SummaryResultMsg:
m.applySummary(msg.SessionID, msg.Summary, msg.Completed, msg.Pending)
return m, m.nextSummaryCmd()
case UpdateAvailableMsg:
m.update.Checking = false
m.update.Available = true
m.update.NewVersion = msg.NewVersion
m.update.Changelog = msg.Changelog
m.update.DownloadURL = msg.DownloadURL
m.update.SHA256 = msg.SHA256
m.update.FileSize = msg.FileSize
case UpdateCompleteMsg:
m.update.Updating = false
m.update.Done = true
m.update.Available = false
case UpdateErrorMsg:
m.update.Updating = false
m.update.Error = msg.Err
m.update.Checking = false
}
return m, nil
}
@@ -82,6 +100,11 @@ func (m *Model) handleKey(s string) (*Model, tea.Cmd) {
switch s {
case "q", "ctrl+c":
return m, tea.Quit
case "u":
if m.update.Available && !m.update.Updating {
m.update.Updating = true
return m, SelfUpdateCmd(m.update.DownloadURL, m.update.SHA256, m.update.NewVersion)
}
}
// 数字键跳转 Tab
@@ -400,7 +423,7 @@ func (m *Model) render() string {
var b strings.Builder
// ── header: title + tabs ──
b.WriteString(style.TitleStyle.Render(" u-tabs "))
b.WriteString(style.TitleStyle.Render(" u-tabs v" + Version + " "))
sep := style.TabSep.Render(" | ")
// Workspace Tabs
@@ -455,6 +478,20 @@ func (m *Model) render() string {
b.WriteString(lipgloss.NewStyle().Foreground(style.Accent).Render(fmt.Sprintf(" ✓ %s", m.launched)))
m.launched = ""
}
// update status
if m.update.Done {
b.WriteString(lipgloss.NewStyle().Foreground(style.Success).Bold(true).
Render(fmt.Sprintf(" updated to v%s, restart to apply", m.update.NewVersion)))
} else if m.update.Error != nil {
b.WriteString(lipgloss.NewStyle().Foreground(style.Red).
Render(fmt.Sprintf(" update failed: %v", m.update.Error)))
} else if m.update.Updating {
b.WriteString(lipgloss.NewStyle().Foreground(style.Warning).
Render(" updating..."))
} else if m.update.Available {
b.WriteString(lipgloss.NewStyle().Foreground(style.Warning).Bold(true).
Render(fmt.Sprintf(" v%s available [u]update", m.update.NewVersion)))
}
b.WriteString("\n")
helpParts := m.renderHelp()