package application import ( "sync" "chub/internal/domain" ) // EventBus is the in-process local event channel shared by the UI and CLI // adapters. Subscribers own cancellation; publishing never blocks a browser // process monitor on a slow presentation consumer. type EventBus struct { mu sync.RWMutex nextID uint64 clients map[uint64]chan domain.BrowserEvent } func NewEventBus() *EventBus { return &EventBus{clients: make(map[uint64]chan domain.BrowserEvent)} } func (b *EventBus) Subscribe(buffer int) (<-chan domain.BrowserEvent, func()) { if buffer < 1 { buffer = 1 } b.mu.Lock() b.nextID++ id := b.nextID ch := make(chan domain.BrowserEvent, buffer) b.clients[id] = ch b.mu.Unlock() return ch, func() { b.mu.Lock() if existing, ok := b.clients[id]; ok { delete(b.clients, id) close(existing) } b.mu.Unlock() } } func (b *EventBus) Publish(event domain.BrowserEvent) { b.mu.RLock() defer b.mu.RUnlock() for _, ch := range b.clients { select { case ch <- event: default: } } }