feat(ui): команды, snapshots, Fyne-окно и интеграция (--noui)
Some checks failed
CI / test (pull_request) Failing after 1m19s
CI / build-and-package (amd64, linux) (pull_request) Failing after 58s
CI / build-and-package (amd64, windows) (pull_request) Successful in 53s

Команды (спец 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).
This commit is contained in:
ki.sagidullin
2026-08-20 08:54:55 +05:00
parent baf2ad7147
commit c2272137b3
17 changed files with 1056 additions and 18 deletions

73
internal/ui/store.go Normal file
View File

@@ -0,0 +1,73 @@
package ui
import (
"context"
"github.com/kamelion/ratatoskr-go/internal/storage"
)
// Store — абстракция чтения-модели для UI (спец 12.5).
//
// Возвращает только копии (value-типы), никогда — живые указатели на
// разделяемые контейнеры core/БД. UI читает снимки и реагирует на события;
// мутирует состояние только Core.
type Store interface {
// ListTasks возвращает все задачи (копии), сортировка по updated_at desc.
ListTasks(ctx context.Context) ([]storage.Task, error)
// GetTask возвращает задачу по ID (копию).
GetTask(ctx context.Context, id int64) (storage.Task, error)
// GetHistory возвращает историю диалога задачи (копии).
GetHistory(ctx context.Context, id int64) ([]storage.HistoryMsg, error)
// GetTraces возвращает трассы задачи (копии).
GetTraces(ctx context.Context, id int64) ([]storage.Trace, error)
}
// DBStore — реализация Store поверх *storage.Storage (БД).
type DBStore struct {
s *storage.Storage
}
// NewDBStore создаёт DBStore на основе БД.
func NewDBStore(s *storage.Storage) *DBStore {
return &DBStore{s: s}
}
// ListTasks реализует Store: deref-копии записей, без указателей.
func (d *DBStore) ListTasks(ctx context.Context) ([]storage.Task, error) {
rows, err := d.s.ListTasks(ctx, storage.TaskFilter{Limit: 200})
if err != nil {
return nil, err
}
out := make([]storage.Task, 0, len(rows))
for _, r := range rows {
out = append(out, *r)
}
return out, nil
}
// GetTask реализует Store.
func (d *DBStore) GetTask(ctx context.Context, id int64) (storage.Task, error) {
t, err := d.s.GetTask(ctx, id)
if err != nil {
return storage.Task{}, err
}
return *t, nil
}
// GetHistory реализует Store.
func (d *DBStore) GetHistory(ctx context.Context, id int64) ([]storage.HistoryMsg, error) {
return d.s.GetHistory(ctx, id)
}
// GetTraces реализует Store.
func (d *DBStore) GetTraces(ctx context.Context, id int64) ([]storage.Trace, error) {
rows, err := d.s.GetTraces(ctx, id)
if err != nil {
return nil, err
}
out := make([]storage.Trace, 0, len(rows))
for _, r := range rows {
out = append(out, *r)
}
return out, nil
}