package tui import ( "time" tea "github.com/charmbracelet/bubbletea" "github.com/voocel/ainovel-cli/app" ) // 消息类型 type ( eventMsg app.UIEvent snapshotMsg app.UISnapshot doneMsg struct{} askUserMsg askUserRequest startResultMsg struct{ err error } steerResultMsg struct{} spinnerTickMsg time.Time streamDeltaMsg string // 流式 token 增量 streamClearMsg struct{} // 清空流式缓冲(新消息开始) ) // --- Cmd 函数 --- func listenEvents(rt *app.Runtime) tea.Cmd { return func() tea.Msg { ev, ok := <-rt.Events() if !ok { return nil } return eventMsg(ev) } } func listenDone(rt *app.Runtime) tea.Cmd { return func() tea.Msg { <-rt.Done() return doneMsg{} } } func tickSnapshot(rt *app.Runtime) tea.Cmd { return tea.Tick(3*time.Second, func(t time.Time) tea.Msg { return snapshotMsg(rt.Snapshot()) }) } func fetchSnapshot(rt *app.Runtime) tea.Cmd { return func() tea.Msg { return snapshotMsg(rt.Snapshot()) } } func checkResume(rt *app.Runtime) tea.Cmd { return func() tea.Msg { label, err := rt.Resume() if err != nil { return startResultMsg{err: err} } if label != "" { return startResultMsg{err: nil} } return nil } } func startRuntime(rt *app.Runtime, prompt string) tea.Cmd { return func() tea.Msg { err := rt.Start(prompt) return startResultMsg{err: err} } } func steerRuntime(rt *app.Runtime, text string) tea.Cmd { return func() tea.Msg { rt.Steer(text) return steerResultMsg{} } } func tickSpinner() tea.Cmd { return tea.Tick(350*time.Millisecond, func(t time.Time) tea.Msg { return spinnerTickMsg(t) }) } func listenStream(rt *app.Runtime) tea.Cmd { return func() tea.Msg { delta, ok := <-rt.Stream() if !ok { return nil } return streamDeltaMsg(delta) } } func listenStreamClear(rt *app.Runtime) tea.Cmd { return func() tea.Msg { _, ok := <-rt.StreamClear() if !ok { return nil } return streamClearMsg{} } } func listenAskUser(bridge *askUserBridge) tea.Cmd { return func() tea.Msg { req, ok := <-bridge.requests if !ok { return nil } return askUserMsg(req) } }