all repos — llm_aggregator @ 6e3abd0665fe86906baf2db7652595d393f2ffc3

A CLI tool to aggregate RSS feeds and summarise them with LLMs

internal/tui/tui.go (view raw)

  1package tui
  2
  3import (
  4	"context"
  5	"fmt"
  6	"time"
  7
  8	"llm_aggregator/internal/progress"
  9	"llm_aggregator/internal/runtime"
 10
 11	tea "github.com/charmbracelet/bubbletea"
 12)
 13
 14// TUIProgress implements the progress.Progress interface for TUI
 15type TUIProgress struct {
 16	program *tea.Program
 17	model   *Model
 18}
 19
 20// NewTUIProgress creates a new TUIProgress
 21func NewTUIProgress() *TUIProgress {
 22	model := New()
 23	p := tea.NewProgram(model, tea.WithAltScreen())
 24	return &TUIProgress{
 25		program: p,
 26		model:   model,
 27	}
 28}
 29
 30// Logf logs a message to the TUI
 31func (tp *TUIProgress) Logf(format string, args ...interface{}) {
 32	tp.program.Send(StepWithCounts(tp.model.currentStep, fmt.Sprintf(format, args...), 
 33		tp.model.articlesCount, tp.model.processedCount))
 34}
 35
 36// Warningf logs a warning to the TUI
 37func (tp *TUIProgress) Warningf(format string, args ...interface{}) {
 38	tp.Logf("Warning: "+format, args...)
 39}
 40
 41// Debugf logs a debug message to the TUI
 42func (tp *TUIProgress) Debugf(format string, args ...interface{}) {
 43	// Debug messages not shown in TUI by default
 44}
 45
 46// SetStage sets the current stage
 47func (tp *TUIProgress) SetStage(stage string) {
 48	tp.program.Send(Step(tp.model.currentStep, stage))
 49}
 50
 51// SetSubStage sets the sub-stage status
 52func (tp *TUIProgress) SetSubStage(status string) {
 53	tp.program.Send(Step(tp.model.currentStep, status))
 54}
 55
 56// SetArticleCount sets the article counts
 57func (tp *TUIProgress) SetArticleCount(total, processed int) {
 58	tp.model.articlesCount = total
 59	tp.model.processedCount = processed
 60}
 61
 62// Run runs the TUI
 63func (tp *TUIProgress) Run() error {
 64	_, err := tp.program.Run()
 65	return err
 66}
 67
 68// Context returns a progress.Context for the TUI
 69func (tp *TUIProgress) Context() *progress.Context {
 70	return progress.NewContext(tp)
 71}
 72
 73// RunWithRuntime runs the TUI with a runtime
 74func RunWithRuntime(rt *runtime.Runtime) error {
 75	// Create TUI progress
 76	tp := NewTUIProgress()
 77	
 78	// Run TUI in goroutine
 79	tuiDone := make(chan error)
 80	go func() {
 81		tuiDone <- tp.Run()
 82	}()
 83
 84	// Run aggregation in another goroutine
 85	runDone := make(chan error)
 86	go func() {
 87		// Create context for cancellation
 88		ctx, cancel := context.WithCancel(context.Background())
 89		defer cancel()
 90
 91		// Update TUI status
 92		tp.program.Send(Step(1, fmt.Sprintf("Aggregating feeds from: %s", rt.FeedsFile)))
 93
 94		// TODO: Configure runtime with progress context
 95		
 96		// Execute the runtime
 97		err := rt.Execute(ctx)
 98		if err != nil {
 99			tp.program.Send(Error(err.Error()))
100			runDone <- err
101			return
102		}
103
104		// Update TUI status
105		tp.program.Send(StepWithCounts(5, "Formatting output", len(rt.Articles), len(rt.Articles)))
106
107		// Write output
108		if rt.OutputFile != "" {
109			if err := rt.WriteOutputToFile(); err != nil {
110				tp.program.Send(Error(fmt.Sprintf("Error writing output: %v", err)))
111				runDone <- err
112				return
113			}
114			tp.program.Send(Step(6, fmt.Sprintf("Output written to: %s", rt.OutputFile)))
115		} else {
116			if err := rt.WriteOutputToFile(); err != nil {
117				tp.program.Send(Error(fmt.Sprintf("Error writing output: %v", err)))
118				runDone <- err
119				return
120			}
121			tp.program.Send(Step(6, "Output complete"))
122		}
123
124		// Mark as done
125		tp.program.Send(Step(7, "Processing completed successfully"))
126		time.Sleep(2 * time.Second) // Show success message briefly
127		tp.program.Send(tea.Quit())
128		runDone <- nil
129	}()
130
131	// Wait for either TUI or run to complete
132	select {
133	case err := <-tuiDone:
134		return err
135	case err := <-runDone:
136		if err != nil {
137			return err
138		}
139		// Wait for TUI to quit gracefully
140		select {
141		case err := <-tuiDone:
142			return err
143		case <-time.After(5 * time.Second):
144			return fmt.Errorf("TUI did not exit gracefully")
145		}
146	}
147}