Команды (спец 12.2): - ui.Commands: действия UI → текстовые команды канала (start/cancel/skip/ retry/continue/approve/send), единый путь через chat.Channel. - ui.Window = chat.Channel + View; NilWindow для headless/тестов. Snapshots (спец 12.5): - ui.Store: чтение-модель, возвращает только копии (ListTasks/GetTask/ GetHistory/GetTraces); ui.DBStore поверх storage. Fyne-окно (internal/ui/desktop, build-tag cgo): - список задач слева, сплиты рабочей области и панели «Логи»/«Состояние», ввод+кнопки команд, тёмная тема, fullscreen, сохранение layout в Preferences, сворачивание при закрытии крестиком. - Колбэки View через fyne.Do (спец 12.4). Интеграция: - app.New(..., noUI); флаг --noui; UI собирается только с cgo (ui_cgo/ ui_noui фабрики), приложение headless без него. - Run: окно блокирует главную горутину; «Завершить» → cancel → graceful shutdown (спец 12.6). - go.mod: fyne.io/fyne/v2 v2.6.0 (direct).
53 lines
2.2 KiB
Go
53 lines
2.2 KiB
Go
package ui
|
||
|
||
import (
|
||
"github.com/kamelion/ratatoskr-go/internal/events"
|
||
)
|
||
|
||
// Controller — подписчик шин событий, мост к Fyne-представлению.
|
||
//
|
||
// Регистрирует Hub'ы на доменной и логовой шинах и диспатчит события в View
|
||
// (см. спец 12.4): колбэки View выполняются в горутинах Hub, поэтому
|
||
// реализация Fyne должна вызывать fyne.Do, чтобы обновить виджеты на главной
|
||
// горутине окна.
|
||
//
|
||
// Жизненный цикл независим от ядра: Close только отписывает Controller от шин
|
||
// (например, при сворачивании окна), но Core/worker продолжают работать.
|
||
type Controller struct {
|
||
domain *events.Hub
|
||
logs *events.Hub
|
||
view View
|
||
}
|
||
|
||
// New создаёт Controller, подписанный на доменную (domainBus) и логовую
|
||
// (logBus) шины, с представлением view.
|
||
func New(domainBus *events.Bus, logBus *events.LogBus, view View) *Controller {
|
||
c := &Controller{view: view}
|
||
|
||
c.domain = events.NewHub(domainBus)
|
||
events.On(c.domain, func(e events.TaskCreated) { view.OnTaskCreated(e) })
|
||
events.On(c.domain, func(e events.TaskUpdated) { view.OnTaskUpdated(e) })
|
||
events.On(c.domain, func(e events.TaskDeleted) { view.OnTaskDeleted(e) })
|
||
events.On(c.domain, func(e events.TaskStatusChanged) { view.OnTaskStatusChanged(e) })
|
||
events.On(c.domain, func(e events.HistoryAppended) { view.OnHistoryAppended(e) })
|
||
events.On(c.domain, func(e events.TraceAppended) { view.OnTraceAppended(e) })
|
||
events.On(c.domain, func(e events.AgentActivity) { view.OnAgentActivity(e) })
|
||
|
||
c.logs = events.NewHub(logBus.Bus)
|
||
events.On(c.logs, func(e events.LogLine) { view.OnLog(e) })
|
||
|
||
return c
|
||
}
|
||
|
||
// Start запускает подписку на обе шины.
|
||
func (c *Controller) Start() {
|
||
c.domain.Start()
|
||
c.logs.Start()
|
||
}
|
||
|
||
// Close останавливает подписки. Идемпотентен.
|
||
func (c *Controller) Close() {
|
||
c.domain.Close()
|
||
c.logs.Close()
|
||
}
|