summaryrefslogtreecommitdiffhomepage
path: root/packages/tui/internal/status
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-05-30 20:47:56 -0400
committerDax Raad <[email protected]>2025-05-30 20:48:36 -0400
commitf3da73553c45f17e04b1e77cb13eb0fca714d1bd (patch)
treea24317a19e1ab2a89da50db669dc6894f15d00d1 /packages/tui/internal/status
parent9a26b3058ffc1023e5c7e54b6d571c903d15888e (diff)
downloadopencode-f3da73553c45f17e04b1e77cb13eb0fca714d1bd.tar.gz
opencode-f3da73553c45f17e04b1e77cb13eb0fca714d1bd.zip
sync
Diffstat (limited to 'packages/tui/internal/status')
-rw-r--r--packages/tui/internal/status/status.go142
1 files changed, 142 insertions, 0 deletions
diff --git a/packages/tui/internal/status/status.go b/packages/tui/internal/status/status.go
new file mode 100644
index 000000000..3648a64ae
--- /dev/null
+++ b/packages/tui/internal/status/status.go
@@ -0,0 +1,142 @@
+package status
+
+import (
+ "context"
+ "fmt"
+ "log/slog"
+ "sync"
+ "time"
+
+ "github.com/sst/opencode/internal/pubsub"
+)
+
+type Level string
+
+const (
+ LevelInfo Level = "info"
+ LevelWarn Level = "warn"
+ LevelError Level = "error"
+ LevelDebug Level = "debug"
+)
+
+type StatusMessage struct {
+ Level Level `json:"level"`
+ Message string `json:"message"`
+ Timestamp time.Time `json:"timestamp"`
+ Critical bool `json:"critical"`
+ Duration time.Duration `json:"duration"`
+}
+
+// StatusOption is a function that configures a status message
+type StatusOption func(*StatusMessage)
+
+// WithCritical marks a status message as critical, causing it to be displayed immediately
+func WithCritical(critical bool) StatusOption {
+ return func(msg *StatusMessage) {
+ msg.Critical = critical
+ }
+}
+
+// WithDuration sets a custom display duration for a status message
+func WithDuration(duration time.Duration) StatusOption {
+ return func(msg *StatusMessage) {
+ msg.Duration = duration
+ }
+}
+
+const (
+ EventStatusPublished pubsub.EventType = "status_published"
+)
+
+type Service interface {
+ pubsub.Subscriber[StatusMessage]
+
+ Info(message string, opts ...StatusOption)
+ Warn(message string, opts ...StatusOption)
+ Error(message string, opts ...StatusOption)
+ Debug(message string, opts ...StatusOption)
+}
+
+type service struct {
+ broker *pubsub.Broker[StatusMessage]
+ mu sync.RWMutex
+}
+
+var globalStatusService *service
+
+func InitService() error {
+ if globalStatusService != nil {
+ return fmt.Errorf("status service already initialized")
+ }
+ broker := pubsub.NewBroker[StatusMessage]()
+ globalStatusService = &service{
+ broker: broker,
+ }
+ return nil
+}
+
+func GetService() Service {
+ if globalStatusService == nil {
+ panic("status service not initialized. Call status.InitService() at application startup.")
+ }
+ return globalStatusService
+}
+
+func (s *service) Info(message string, opts ...StatusOption) {
+ s.publish(LevelInfo, message, opts...)
+ slog.Info(message)
+}
+
+func (s *service) Warn(message string, opts ...StatusOption) {
+ s.publish(LevelWarn, message, opts...)
+ slog.Warn(message)
+}
+
+func (s *service) Error(message string, opts ...StatusOption) {
+ s.publish(LevelError, message, opts...)
+ slog.Error(message)
+}
+
+func (s *service) Debug(message string, opts ...StatusOption) {
+ s.publish(LevelDebug, message, opts...)
+ slog.Debug(message)
+}
+
+func (s *service) publish(level Level, messageText string, opts ...StatusOption) {
+ statusMsg := StatusMessage{
+ Level: level,
+ Message: messageText,
+ Timestamp: time.Now(),
+ }
+
+ // Apply all options
+ for _, opt := range opts {
+ opt(&statusMsg)
+ }
+
+ s.broker.Publish(EventStatusPublished, statusMsg)
+}
+
+func (s *service) Subscribe(ctx context.Context) <-chan pubsub.Event[StatusMessage] {
+ return s.broker.Subscribe(ctx)
+}
+
+func Info(message string, opts ...StatusOption) {
+ GetService().Info(message, opts...)
+}
+
+func Warn(message string, opts ...StatusOption) {
+ GetService().Warn(message, opts...)
+}
+
+func Error(message string, opts ...StatusOption) {
+ GetService().Error(message, opts...)
+}
+
+func Debug(message string, opts ...StatusOption) {
+ GetService().Debug(message, opts...)
+}
+
+func Subscribe(ctx context.Context) <-chan pubsub.Event[StatusMessage] {
+ return GetService().Subscribe(ctx)
+}