chore(gui): add AX usage examples
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run

This commit is contained in:
Virgil 2026-04-02 20:36:02 +00:00
parent d3443d4be9
commit 274a81689c
12 changed files with 70 additions and 17 deletions

View file

@ -4,6 +4,7 @@ package systray
import "forge.lthn.ai/core/go/pkg/core"
// SetMenu sets a dynamic menu on the tray from TrayMenuItem descriptors.
// Use: _ = m.SetMenu([]TrayMenuItem{{Label: "Quit", ActionID: "quit"}})
func (m *Manager) SetMenu(items []TrayMenuItem) error {
if m.tray == nil {
return core.E("systray.SetMenu", "tray not initialised", nil)
@ -54,6 +55,7 @@ func (m *Manager) buildMenuInto(menu PlatformMenu, items []TrayMenuItem) {
}
// RegisterCallback registers a callback for a menu action ID.
// Use: m.RegisterCallback("quit", func() { _ = app.Quit() })
func (m *Manager) RegisterCallback(actionID string, callback func()) {
m.mu.Lock()
m.callbacks[actionID] = callback
@ -61,6 +63,7 @@ func (m *Manager) RegisterCallback(actionID string, callback func()) {
}
// UnregisterCallback removes a callback.
// Use: m.UnregisterCallback("quit")
func (m *Manager) UnregisterCallback(actionID string) {
m.mu.Lock()
delete(m.callbacks, actionID)
@ -68,6 +71,7 @@ func (m *Manager) UnregisterCallback(actionID string) {
}
// GetCallback returns the callback for an action ID.
// Use: callback, ok := m.GetCallback("quit")
func (m *Manager) GetCallback(actionID string) (func(), bool) {
m.mu.RLock()
defer m.mu.RUnlock()
@ -76,6 +80,7 @@ func (m *Manager) GetCallback(actionID string) (func(), bool) {
}
// GetInfo returns tray status information.
// Use: info := m.GetInfo()
func (m *Manager) GetInfo() map[string]any {
return map[string]any{
"active": m.IsActive(),

View file

@ -2,41 +2,52 @@ package systray
// QueryConfig requests this service's config section from the display orchestrator.
// Result: map[string]any
// Use: result, _, err := c.QUERY(systray.QueryConfig{})
type QueryConfig struct{}
// --- Tasks ---
// TaskSetTrayIcon sets the tray icon.
// Use: _, _, err := c.PERFORM(systray.TaskSetTrayIcon{Data: iconBytes})
type TaskSetTrayIcon struct{ Data []byte }
// TaskSetTooltip updates the tray tooltip text.
// Use: _, _, err := c.PERFORM(systray.TaskSetTooltip{Tooltip: "Core is ready"})
type TaskSetTooltip struct{ Tooltip string }
// TaskSetLabel updates the tray label text.
// Use: _, _, err := c.PERFORM(systray.TaskSetLabel{Label: "Core"})
type TaskSetLabel struct{ Label string }
// TaskSetTrayMenu sets the tray menu items.
// Use: _, _, err := c.PERFORM(systray.TaskSetTrayMenu{Items: items})
type TaskSetTrayMenu struct{ Items []TrayMenuItem }
// TaskShowPanel shows the tray panel window.
// Use: _, _, err := c.PERFORM(systray.TaskShowPanel{})
type TaskShowPanel struct{}
// TaskHidePanel hides the tray panel window.
// Use: _, _, err := c.PERFORM(systray.TaskHidePanel{})
type TaskHidePanel struct{}
// TaskShowMessage shows a tray message or notification.
// Use: _, _, err := c.PERFORM(systray.TaskShowMessage{Title: "Core", Message: "Sync complete"})
type TaskShowMessage struct {
Title string `json:"title"`
Message string `json:"message"`
}
// TaskSaveConfig persists this service's config section via the display orchestrator.
// Use: _, _, err := c.PERFORM(systray.TaskSaveConfig{Value: map[string]any{"tooltip": "Core"}})
type TaskSaveConfig struct{ Value map[string]any }
// --- Actions ---
// ActionTrayClicked is broadcast when the tray icon is clicked.
// Use: _ = c.ACTION(systray.ActionTrayClicked{})
type ActionTrayClicked struct{}
// ActionTrayMenuItemClicked is broadcast when a tray menu item is clicked.
// Use: _ = c.ACTION(systray.ActionTrayMenuItemClicked{ActionID: "quit"})
type ActionTrayMenuItemClicked struct{ ActionID string }

View file

@ -1,11 +1,19 @@
package systray
// MockPlatform is an exported mock for cross-package integration tests.
// Use: platform := systray.NewMockPlatform()
type MockPlatform struct{}
// NewMockPlatform creates a tray platform mock.
// Use: platform := systray.NewMockPlatform()
func NewMockPlatform() *MockPlatform { return &MockPlatform{} }
// NewTray creates a mock tray handle for tests.
// Use: tray := platform.NewTray()
func (m *MockPlatform) NewTray() PlatformTray { return &exportedMockTray{} }
// NewMenu creates a mock tray menu for tests.
// Use: menu := platform.NewMenu()
func (m *MockPlatform) NewMenu() PlatformMenu { return &exportedMockMenu{} }
type exportedMockTray struct {

View file

@ -2,12 +2,14 @@
package systray
// Platform abstracts the system tray backend.
// Use: var p systray.Platform
type Platform interface {
NewTray() PlatformTray
NewMenu() PlatformMenu // Menu factory for building tray menus
}
// PlatformTray is a live tray handle from the backend.
// Use: var tray systray.PlatformTray
type PlatformTray interface {
SetIcon(data []byte)
SetTemplateIcon(data []byte)
@ -18,6 +20,7 @@ type PlatformTray interface {
}
// PlatformMenu is a tray menu built by the backend.
// Use: var menu systray.PlatformMenu
type PlatformMenu interface {
Add(label string) PlatformMenuItem
AddSeparator()
@ -25,6 +28,7 @@ type PlatformMenu interface {
}
// PlatformMenuItem is a single item in a tray menu.
// Use: var item systray.PlatformMenuItem
type PlatformMenuItem interface {
SetTooltip(text string)
SetChecked(checked bool)
@ -36,6 +40,7 @@ type PlatformMenuItem interface {
// WindowHandle is a cross-package interface for window operations.
// Defined locally to avoid circular imports (display imports systray).
// pkg/window.PlatformWindow satisfies this implicitly.
// Use: var w systray.WindowHandle
type WindowHandle interface {
Name() string
Show()

View file

@ -16,10 +16,7 @@ import (
type Options struct{}
// Service manages system tray operations via Core tasks.
//
// Example:
//
// svc := &systray.Service{}
// Use: svc := &systray.Service{}
type Service struct {
*core.ServiceRuntime[Options]
manager *Manager
@ -28,10 +25,7 @@ type Service struct {
}
// OnStartup loads tray config and registers task handlers.
//
// Example:
//
// _ = svc.OnStartup(context.Background())
// Use: _ = svc.OnStartup(context.Background())
func (s *Service) OnStartup(ctx context.Context) error {
cfg, handled, _ := s.Core().QUERY(QueryConfig{})
if handled {
@ -118,6 +112,7 @@ func (s *Service) showTrayMessage(title, message string) error {
}
// Manager returns the underlying systray Manager.
// Use: manager := svc.Manager()
func (s *Service) Manager() *Manager {
return s.manager
}

View file

@ -12,10 +12,7 @@ import (
var defaultIcon []byte
// Manager manages the system tray lifecycle.
//
// Example:
//
// manager := systray.NewManager(platform)
// Use: manager := systray.NewManager(platform)
type Manager struct {
platform Platform
tray PlatformTray
@ -30,10 +27,7 @@ type Manager struct {
}
// NewManager creates a systray Manager.
//
// Example:
//
// manager := systray.NewManager(platform)
// Use: manager := systray.NewManager(platform)
func NewManager(platform Platform) *Manager {
return &Manager{
platform: platform,
@ -42,6 +36,7 @@ func NewManager(platform Platform) *Manager {
}
// Setup creates the system tray with default icon and tooltip.
// Use: _ = manager.Setup("Core", "Core")
func (m *Manager) Setup(tooltip, label string) error {
m.tray = m.platform.NewTray()
if m.tray == nil {
@ -57,6 +52,7 @@ func (m *Manager) Setup(tooltip, label string) error {
}
// SetIcon sets the tray icon.
// Use: _ = manager.SetIcon(iconBytes)
func (m *Manager) SetIcon(data []byte) error {
if m.tray == nil {
return core.E("systray.SetIcon", "tray not initialised", nil)
@ -67,6 +63,7 @@ func (m *Manager) SetIcon(data []byte) error {
}
// SetTemplateIcon sets the template icon (macOS).
// Use: _ = manager.SetTemplateIcon(iconBytes)
func (m *Manager) SetTemplateIcon(data []byte) error {
if m.tray == nil {
return core.E("systray.SetTemplateIcon", "tray not initialised", nil)
@ -77,6 +74,7 @@ func (m *Manager) SetTemplateIcon(data []byte) error {
}
// SetTooltip sets the tray tooltip.
// Use: _ = manager.SetTooltip("Core is ready")
func (m *Manager) SetTooltip(text string) error {
if m.tray == nil {
return core.E("systray.SetTooltip", "tray not initialised", nil)
@ -87,6 +85,7 @@ func (m *Manager) SetTooltip(text string) error {
}
// SetLabel sets the tray label.
// Use: _ = manager.SetLabel("Core")
func (m *Manager) SetLabel(text string) error {
if m.tray == nil {
return core.E("systray.SetLabel", "tray not initialised", nil)
@ -97,6 +96,7 @@ func (m *Manager) SetLabel(text string) error {
}
// AttachWindow attaches a panel window to the tray.
// Use: _ = manager.AttachWindow(windowHandle)
func (m *Manager) AttachWindow(w WindowHandle) error {
if m.tray == nil {
return core.E("systray.AttachWindow", "tray not initialised", nil)
@ -109,6 +109,7 @@ func (m *Manager) AttachWindow(w WindowHandle) error {
}
// ShowPanel shows the attached tray panel window if one is configured.
// Use: _ = manager.ShowPanel()
func (m *Manager) ShowPanel() error {
m.mu.RLock()
w := m.panelWindow
@ -121,6 +122,7 @@ func (m *Manager) ShowPanel() error {
}
// HidePanel hides the attached tray panel window if one is configured.
// Use: _ = manager.HidePanel()
func (m *Manager) HidePanel() error {
m.mu.RLock()
w := m.panelWindow
@ -133,11 +135,13 @@ func (m *Manager) HidePanel() error {
}
// Tray returns the underlying platform tray for direct access.
// Use: tray := manager.Tray()
func (m *Manager) Tray() PlatformTray {
return m.tray
}
// IsActive returns whether a tray has been created.
// Use: active := manager.IsActive()
func (m *Manager) IsActive() bool {
return m.tray != nil
}

View file

@ -6,18 +6,25 @@ import (
)
// WailsPlatform implements Platform using Wails v3.
// Use: platform := systray.NewWailsPlatform(app)
type WailsPlatform struct {
app *application.App
}
// NewWailsPlatform creates a Wails-backed tray platform.
// Use: platform := systray.NewWailsPlatform(app)
func NewWailsPlatform(app *application.App) *WailsPlatform {
return &WailsPlatform{app: app}
}
// NewTray creates a Wails system tray handle.
// Use: tray := platform.NewTray()
func (wp *WailsPlatform) NewTray() PlatformTray {
return &wailsTray{tray: wp.app.SystemTray.New()}
}
// NewMenu creates a Wails tray menu handle.
// Use: menu := platform.NewMenu()
func (wp *WailsPlatform) NewMenu() PlatformMenu {
return &wailsTrayMenu{menu: wp.app.NewMenu()}
}

View file

@ -29,6 +29,7 @@ type LayoutInfo struct {
}
// LayoutManager persists named window arrangements to ~/.config/Core/layouts.json.
// Use: lm := window.NewLayoutManager()
type LayoutManager struct {
configDir string
layouts map[string]Layout

View file

@ -255,6 +255,8 @@ type ActionWindowFocused struct{ Name string }
// Use: _ = c.ACTION(window.ActionWindowBlurred{Name: "editor"})
type ActionWindowBlurred struct{ Name string }
// ActionFilesDropped is broadcast when files are dropped onto a window.
// Use: _ = c.ACTION(window.ActionFilesDropped{Name: "editor", Paths: []string{"/tmp/report.pdf"}})
type ActionFilesDropped struct {
Name string `json:"name"` // window name
Paths []string `json:"paths"`

View file

@ -1,15 +1,20 @@
package window
// MockPlatform is an exported mock for cross-package integration tests.
// Use: platform := window.NewMockPlatform()
// For internal tests, use the unexported mockPlatform in mock_test.go.
type MockPlatform struct {
Windows []*MockWindow
}
// NewMockPlatform creates a window platform mock.
// Use: platform := window.NewMockPlatform()
func NewMockPlatform() *MockPlatform {
return &MockPlatform{}
}
// CreateWindow creates an in-memory window for tests.
// Use: w := platform.CreateWindow(window.PlatformWindowOptions{Name: "editor"})
func (m *MockPlatform) CreateWindow(opts PlatformWindowOptions) PlatformWindow {
w := &MockWindow{
name: opts.Name, title: opts.Title, url: opts.URL,
@ -23,6 +28,8 @@ func (m *MockPlatform) CreateWindow(opts PlatformWindowOptions) PlatformWindow {
return w
}
// GetWindows returns all tracked mock windows.
// Use: windows := platform.GetWindows()
func (m *MockPlatform) GetWindows() []PlatformWindow {
out := make([]PlatformWindow, len(m.Windows))
for i, w := range m.Windows {
@ -31,6 +38,8 @@ func (m *MockPlatform) GetWindows() []PlatformWindow {
return out
}
// MockWindow is an in-memory window handle used by tests.
// Use: w := &window.MockWindow{}
type MockWindow struct {
name, title, url string
width, height, x, y int

View file

@ -9,7 +9,7 @@ type Platform interface {
}
// PlatformWindowOptions are the backend-specific options passed to CreateWindow.
// Use: opts := window.PlatformWindowOptions{Name: "editor"}
// Use: opts := window.PlatformWindowOptions{Name: "editor", URL: "/editor"}
type PlatformWindowOptions struct {
Name string
Title string

View file

@ -7,15 +7,19 @@ import (
)
// WailsPlatform implements Platform using Wails v3.
// Use: platform := window.NewWailsPlatform(app)
type WailsPlatform struct {
app *application.App
}
// NewWailsPlatform creates a Wails-backed Platform.
// Use: platform := window.NewWailsPlatform(app)
func NewWailsPlatform(app *application.App) *WailsPlatform {
return &WailsPlatform{app: app}
}
// CreateWindow opens a new Wails window from platform options.
// Use: w := wp.CreateWindow(window.PlatformWindowOptions{Name: "editor", URL: "/editor"})
func (wp *WailsPlatform) CreateWindow(opts PlatformWindowOptions) PlatformWindow {
wOpts := application.WebviewWindowOptions{
Name: opts.Name,
@ -40,6 +44,8 @@ func (wp *WailsPlatform) CreateWindow(opts PlatformWindowOptions) PlatformWindow
return &wailsWindow{w: w, title: opts.Title}
}
// GetWindows returns the live Wails windows.
// Use: windows := wp.GetWindows()
func (wp *WailsPlatform) GetWindows() []PlatformWindow {
all := wp.app.Window.GetAll()
out := make([]PlatformWindow, 0, len(all))