Files
ainovel-clients/tui/app.go
shiyue 8900332910 fix: tmux 脱离后任务不再被中断
bubbletea TUI 退出时检查任务是否仍在运行,
若仍在运行则回退到无 UI 阻塞等待模式,
而不是调用 AbortSilent() 杀死 coordinator。
2026-03-18 13:37:26 +08:00

82 lines
2.1 KiB
Go

package tui
import (
"fmt"
"io"
"log"
"os"
"os/signal"
"path/filepath"
"syscall"
tea "github.com/charmbracelet/bubbletea"
"github.com/voocel/ainovel-cli/app"
"github.com/voocel/ainovel-cli/tools"
)
// Run 启动 TUI 模式。
func Run(cfg app.Config, refs tools.References, prompts app.Prompts, styles map[string]string) error {
rt, err := app.NewRuntime(cfg, refs, prompts, styles)
if err != nil {
return err
}
bridge := newAskUserBridge()
rt.AskUser().SetHandler(bridge.handler)
restoreLog := redirectLogger(rt.Dir())
defer restoreLog()
m := NewModel(rt, bridge)
p := tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseCellMotion())
_, err = p.Run()
// bubbletea 退出后,检查任务是否仍在运行
snap := rt.Snapshot()
if snap.IsRunning {
// 任务仍在运行(可能是 tmux detach 导致 TUI 退出),
// 不中断任务,回退到无 UI 阻塞等待模式
fmt.Println("\n[TUI 已退出,任务仍在后台运行中...]")
fmt.Printf("[小说: %s | 阶段: %s | 进度: %d/%d 章]\n",
snap.NovelName, snap.Phase, snap.CompletedCount, snap.TotalChapters)
fmt.Println("[等待任务完成,按 Ctrl+C 强制中断]")
// 监听中断信号
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
select {
case <-rt.Done():
fmt.Println("\n[任务已完成!]")
case <-sigCh:
fmt.Println("\n[收到中断信号,正在停止...]")
rt.Close()
}
return nil
}
// 任务未在运行,正常关闭
rt.Close()
return err
}
// redirectLogger 将标准日志重定向到文件,避免破坏 TUI 画面。
func redirectLogger(outputDir string) func() {
prev := log.Writer()
logPath := filepath.Join(outputDir, "meta", "tui.log")
if err := os.MkdirAll(filepath.Dir(logPath), 0o755); err != nil {
log.SetOutput(io.Discard)
return func() { log.SetOutput(prev) }
}
f, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
if err != nil {
log.SetOutput(io.Discard)
return func() { log.SetOutput(prev) }
}
log.SetOutput(f)
return func() {
log.SetOutput(prev)
_ = f.Close()
}
}