Files
2026-07-13 13:33:09 +08:00

158 lines
3.0 KiB
Go

package streamtui
import (
"fmt"
"log"
"os"
"plandex-cli/term"
"sync"
shared "plandex-shared"
tea "github.com/charmbracelet/bubbletea"
"github.com/fatih/color"
)
var ui *tea.Program
var mu sync.Mutex
var wg sync.WaitGroup
var prestartReply string
var prestartErr *shared.ApiError
var prestartAbort bool
func StartStreamUI(prompt string, buildOnly, canSendToBg bool) error {
if prestartErr != nil {
log.Println("stream UI - prestart error: ", prestartErr)
term.HandleApiError(prestartErr)
}
if prestartAbort {
fmt.Println("🛑 Stopped early")
os.Exit(0)
}
log.Println("Starting stream UI")
initial := initialModel(prestartReply, prompt, buildOnly, canSendToBg)
mu.Lock()
ui = tea.NewProgram(initial, tea.WithAltScreen())
mu.Unlock()
log.Println("Running bubbletea program")
wg.Add(1)
m, err := ui.Run()
log.Println("Bubbletea program finished")
wg.Done()
log.Println("Stream UI finished")
if err != nil {
return fmt.Errorf("error running stream UI: %v", err)
}
var mod *streamUIModel
c, ok := m.(*streamUIModel)
if ok {
mod = c
} else {
c := m.(streamUIModel)
mod = &c
}
fmt.Println()
if !mod.buildOnly {
fmt.Println(mod.mainDisplay)
}
if len(mod.finishedByPath) > 0 || len(mod.tokensByPath) > 0 {
fmt.Println(mod.renderStaticBuild())
}
if mod.err != nil {
log.Println("stream UI - error: ", mod.err)
fmt.Println()
term.OutputErrorAndExit(mod.err.Error())
}
if mod.apiErr != nil {
log.Println("stream UI - api error: ", mod.apiErr)
fmt.Println()
term.HandleApiError(mod.apiErr)
}
if mod.stopped {
fmt.Println()
color.New(color.BgBlack, color.Bold, color.FgHiRed).Println(" 🛑 Stopped early ")
fmt.Println()
term.PrintCmds("", "log", "rewind", "tell")
os.Exit(0)
} else if mod.background {
fmt.Println()
color.New(color.BgBlack, color.Bold, color.FgHiGreen).Println(" ✅ Plan is active in the background ")
fmt.Println()
term.PrintCmds("", "ps", "connect", "stop")
os.Exit(0)
}
if os.Getenv("PLANDEX_REPL") != "" && os.Getenv("PLANDEX_REPL_OUTPUT_FILE") != "" {
// write output to file
err := os.WriteFile(os.Getenv("PLANDEX_REPL_OUTPUT_FILE"), []byte(mod.reply), 0644)
if err != nil {
log.Println("stream UI - error writing output to repl temp file: ", err)
}
}
return nil
}
func Quit() {
if ui == nil {
log.Println("stream UI is nil, can't quit")
return
}
mu.Lock()
if ui != nil {
ui.Quit()
}
mu.Unlock()
wg.Wait() // Wait for the UI to fully terminate
}
func Send(msg shared.StreamMessage) {
if ui == nil {
log.Println("stream ui is nil")
if msg.Type == shared.StreamMessageError {
prestartErr = msg.Error
} else if msg.Type == shared.StreamMessageAborted {
} else if msg.Type == shared.StreamMessageReply {
prestartReply += msg.ReplyChunk
}
return
}
mu.Lock()
defer mu.Unlock()
ui.Send(msg)
}
func ToggleVisibility(hide bool) {
if ui == nil {
return
}
mu.Lock()
defer mu.Unlock()
if hide {
ui.Send(tea.ExitAltScreen())
} else {
ui.Send(tea.EnterAltScreen())
}
}