refactor(gui): align gui services with ax guidance
Some checks failed
Security Scan / security (push) Failing after 36s
Test / test (push) Successful in 1m15s

This commit is contained in:
Virgil 2026-04-02 14:13:58 +00:00
parent 973217ae54
commit c3361b7064
6 changed files with 87 additions and 37 deletions

View file

@ -3,21 +3,32 @@ package clipboard
import (
"context"
"fmt"
"forge.lthn.ai/core/go/pkg/core"
)
// Options holds configuration for the clipboard service.
// Options configures the clipboard service.
//
// Example:
//
// core.WithService(clipboard.Register(platform))
type Options struct{}
// Service is a core.Service managing clipboard operations via IPC.
// Service manages clipboard operations via Core queries and tasks.
//
// Example:
//
// svc := &clipboard.Service{}
type Service struct {
*core.ServiceRuntime[Options]
platform Platform
}
// Register creates a factory closure that captures the Platform adapter.
// Register creates a Core service factory for the clipboard backend.
//
// Example:
//
// core.New(core.WithService(clipboard.Register(platform)))
func Register(p Platform) func(*core.Core) (any, error) {
return func(c *core.Core) (any, error) {
return &Service{
@ -27,14 +38,18 @@ func Register(p Platform) func(*core.Core) (any, error) {
}
}
// OnStartup registers IPC handlers.
// OnStartup registers clipboard handlers with Core.
//
// Example:
//
// _ = svc.OnStartup(context.Background())
func (s *Service) OnStartup(ctx context.Context) error {
s.Core().RegisterQuery(s.handleQuery)
s.Core().RegisterTask(s.handleTask)
return nil
}
// HandleIPCEvents is auto-discovered by core.WithService.
// HandleIPCEvents satisfies Core's IPC hook.
func (s *Service) HandleIPCEvents(c *core.Core, msg core.Message) error {
return nil
}
@ -70,7 +85,7 @@ func (s *Service) handleTask(c *core.Core, t core.Task) (any, bool, error) {
if writer, ok := s.platform.(imageWriter); ok {
return writer.SetImage(t.Data), true, nil
}
return false, true, fmt.Errorf("clipboard image write not supported")
return false, true, core.E("clipboard.handleTask", "clipboard image write not supported", nil)
default:
return nil, false, nil
}

View file

@ -1562,7 +1562,7 @@ func (s *Service) ReadClipboard() (string, error) {
return "", err
}
if !handled {
return "", fmt.Errorf("clipboard service not available")
return "", core.E("display.ReadClipboard", "clipboard service not available", nil)
}
content, _ := result.(clipboard.ClipboardContent)
return content.Text, nil
@ -1575,10 +1575,10 @@ func (s *Service) WriteClipboard(text string) error {
return err
}
if !handled {
return fmt.Errorf("clipboard service not available")
return core.E("display.WriteClipboard", "clipboard service not available", nil)
}
if ok, _ := result.(bool); !ok {
return fmt.Errorf("clipboard write failed")
return core.E("display.WriteClipboard", "clipboard write failed", nil)
}
return nil
}
@ -1607,10 +1607,10 @@ func (s *Service) ClearClipboard() error {
return err
}
if !handled {
return fmt.Errorf("clipboard service not available")
return core.E("display.ClearClipboard", "clipboard service not available", nil)
}
if ok, _ := result.(bool); !ok {
return fmt.Errorf("clipboard clear failed")
return core.E("display.ClearClipboard", "clipboard clear failed", nil)
}
return nil
}
@ -1622,7 +1622,7 @@ func (s *Service) ReadClipboardImage() (clipboard.ClipboardImageContent, error)
return clipboard.ClipboardImageContent{}, err
}
if !handled {
return clipboard.ClipboardImageContent{}, fmt.Errorf("clipboard service not available")
return clipboard.ClipboardImageContent{}, core.E("display.ReadClipboardImage", "clipboard service not available", nil)
}
content, _ := result.(clipboard.ClipboardImageContent)
return content, nil
@ -1635,10 +1635,10 @@ func (s *Service) WriteClipboardImage(data []byte) error {
return err
}
if !handled {
return fmt.Errorf("clipboard service not available")
return core.E("display.WriteClipboardImage", "clipboard service not available", nil)
}
if ok, _ := result.(bool); !ok {
return fmt.Errorf("clipboard image write failed")
return core.E("display.WriteClipboardImage", "clipboard image write failed", nil)
}
return nil
}
@ -1652,7 +1652,7 @@ func (s *Service) ShowNotification(opts notification.NotificationOptions) error
return err
}
if !handled {
return fmt.Errorf("notification service not available")
return core.E("display.ShowNotification", "notification service not available", nil)
}
return nil
}

View file

@ -10,16 +10,28 @@ import (
"forge.lthn.ai/core/gui/pkg/dialog"
)
// Options holds configuration for the notification service.
// Options configures the notification service.
//
// Example:
//
// core.WithService(notification.Register(platform))
type Options struct{}
// Service is a core.Service managing notifications via IPC.
// Service manages notifications via Core tasks and queries.
//
// Example:
//
// svc := &notification.Service{}
type Service struct {
*core.ServiceRuntime[Options]
platform Platform
}
// Register creates a factory closure that captures the Platform adapter.
// Register creates a Core service factory for the notification backend.
//
// Example:
//
// core.New(core.WithService(notification.Register(platform)))
func Register(p Platform) func(*core.Core) (any, error) {
return func(c *core.Core) (any, error) {
return &Service{
@ -29,14 +41,18 @@ func Register(p Platform) func(*core.Core) (any, error) {
}
}
// OnStartup registers IPC handlers.
// OnStartup registers notification handlers with Core.
//
// Example:
//
// _ = svc.OnStartup(context.Background())
func (s *Service) OnStartup(ctx context.Context) error {
s.Core().RegisterQuery(s.handleQuery)
s.Core().RegisterTask(s.handleTask)
return nil
}
// HandleIPCEvents is auto-discovered by core.WithService.
// HandleIPCEvents satisfies Core's IPC hook.
func (s *Service) HandleIPCEvents(c *core.Core, msg core.Message) error {
return nil
}

View file

@ -1,12 +1,12 @@
// pkg/systray/menu.go
package systray
import "fmt"
import "forge.lthn.ai/core/go/pkg/core"
// SetMenu sets a dynamic menu on the tray from TrayMenuItem descriptors.
func (m *Manager) SetMenu(items []TrayMenuItem) error {
if m.tray == nil {
return fmt.Errorf("tray not initialised")
return core.E("systray.SetMenu", "tray not initialised", nil)
}
m.menuItems = append([]TrayMenuItem(nil), items...)
menu := m.buildMenu(items)

View file

@ -2,16 +2,23 @@ package systray
import (
"context"
"fmt"
"forge.lthn.ai/core/go/pkg/core"
"forge.lthn.ai/core/gui/pkg/notification"
)
// Options holds configuration for the systray service.
// Options configures the systray service.
//
// Example:
//
// core.WithService(systray.Register(platform))
type Options struct{}
// Service is a core.Service managing the system tray via IPC.
// Service manages system tray operations via Core tasks.
//
// Example:
//
// svc := &systray.Service{}
type Service struct {
*core.ServiceRuntime[Options]
manager *Manager
@ -19,7 +26,11 @@ type Service struct {
iconPath string
}
// OnStartup queries config and registers IPC handlers.
// OnStartup loads tray config and registers task handlers.
//
// Example:
//
// _ = svc.OnStartup(context.Background())
func (s *Service) OnStartup(ctx context.Context) error {
cfg, handled, _ := s.Core().QUERY(QueryConfig{})
if handled {
@ -45,7 +56,7 @@ func (s *Service) applyConfig(cfg map[string]any) {
}
}
// HandleIPCEvents is auto-discovered and registered by core.WithService.
// HandleIPCEvents satisfies Core's IPC hook.
func (s *Service) HandleIPCEvents(c *core.Core, msg core.Message) error {
return nil
}
@ -95,7 +106,7 @@ func (s *Service) taskShowMessage(title, message string) error {
}
tray := s.manager.Tray()
if tray == nil {
return fmt.Errorf("tray not initialised")
return core.E("systray.taskShowMessage", "tray not initialised", nil)
}
if messenger, ok := tray.(interface{ ShowMessage(title, message string) }); ok {
messenger.ShowMessage(title, message)

View file

@ -3,15 +3,19 @@ package systray
import (
_ "embed"
"fmt"
"sync"
"forge.lthn.ai/core/go/pkg/core"
)
//go:embed assets/apptray.png
var defaultIcon []byte
// Manager manages the system tray lifecycle.
// State that was previously in package-level vars is now on the Manager.
//
// Example:
//
// manager := systray.NewManager(platform)
type Manager struct {
platform Platform
tray PlatformTray
@ -25,6 +29,10 @@ type Manager struct {
}
// NewManager creates a systray Manager.
//
// Example:
//
// manager := systray.NewManager(platform)
func NewManager(platform Platform) *Manager {
return &Manager{
platform: platform,
@ -36,7 +44,7 @@ func NewManager(platform Platform) *Manager {
func (m *Manager) Setup(tooltip, label string) error {
m.tray = m.platform.NewTray()
if m.tray == nil {
return fmt.Errorf("platform returned nil tray")
return core.E("systray.Setup", "platform returned nil tray", nil)
}
m.tray.SetTemplateIcon(defaultIcon)
m.tray.SetTooltip(tooltip)
@ -50,7 +58,7 @@ func (m *Manager) Setup(tooltip, label string) error {
// SetIcon sets the tray icon.
func (m *Manager) SetIcon(data []byte) error {
if m.tray == nil {
return fmt.Errorf("tray not initialised")
return core.E("systray.SetIcon", "tray not initialised", nil)
}
m.tray.SetIcon(data)
m.hasIcon = len(data) > 0
@ -60,7 +68,7 @@ func (m *Manager) SetIcon(data []byte) error {
// SetTemplateIcon sets the template icon (macOS).
func (m *Manager) SetTemplateIcon(data []byte) error {
if m.tray == nil {
return fmt.Errorf("tray not initialised")
return core.E("systray.SetTemplateIcon", "tray not initialised", nil)
}
m.tray.SetTemplateIcon(data)
m.hasTemplateIcon = len(data) > 0
@ -70,7 +78,7 @@ func (m *Manager) SetTemplateIcon(data []byte) error {
// SetTooltip sets the tray tooltip.
func (m *Manager) SetTooltip(text string) error {
if m.tray == nil {
return fmt.Errorf("tray not initialised")
return core.E("systray.SetTooltip", "tray not initialised", nil)
}
m.tray.SetTooltip(text)
m.tooltip = text
@ -80,7 +88,7 @@ func (m *Manager) SetTooltip(text string) error {
// SetLabel sets the tray label.
func (m *Manager) SetLabel(text string) error {
if m.tray == nil {
return fmt.Errorf("tray not initialised")
return core.E("systray.SetLabel", "tray not initialised", nil)
}
m.tray.SetLabel(text)
m.label = text
@ -90,7 +98,7 @@ func (m *Manager) SetLabel(text string) error {
// AttachWindow attaches a panel window to the tray.
func (m *Manager) AttachWindow(w WindowHandle) error {
if m.tray == nil {
return fmt.Errorf("tray not initialised")
return core.E("systray.AttachWindow", "tray not initialised", nil)
}
m.tray.AttachWindow(w)
return nil