diff --git a/pkg/systray/menu.go b/pkg/systray/menu.go index 085678a..32ee360 100644 --- a/pkg/systray/menu.go +++ b/pkg/systray/menu.go @@ -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(), diff --git a/pkg/systray/messages.go b/pkg/systray/messages.go index cb51648..46da75d 100644 --- a/pkg/systray/messages.go +++ b/pkg/systray/messages.go @@ -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 } diff --git a/pkg/systray/mock_platform.go b/pkg/systray/mock_platform.go index 7d9b21b..7c126e0 100644 --- a/pkg/systray/mock_platform.go +++ b/pkg/systray/mock_platform.go @@ -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 { diff --git a/pkg/systray/platform.go b/pkg/systray/platform.go index 283f638..7c23929 100644 --- a/pkg/systray/platform.go +++ b/pkg/systray/platform.go @@ -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() diff --git a/pkg/systray/service.go b/pkg/systray/service.go index 66e2a00..fc232ca 100644 --- a/pkg/systray/service.go +++ b/pkg/systray/service.go @@ -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 } diff --git a/pkg/systray/tray.go b/pkg/systray/tray.go index 9cd9177..7d23b2a 100644 --- a/pkg/systray/tray.go +++ b/pkg/systray/tray.go @@ -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 } diff --git a/pkg/systray/wails.go b/pkg/systray/wails.go index 2ebc5e3..b9e46ac 100644 --- a/pkg/systray/wails.go +++ b/pkg/systray/wails.go @@ -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()} } diff --git a/pkg/window/layout.go b/pkg/window/layout.go index 6750c36..9d3e123 100644 --- a/pkg/window/layout.go +++ b/pkg/window/layout.go @@ -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 diff --git a/pkg/window/messages.go b/pkg/window/messages.go index 8ffdaf9..e517e0c 100644 --- a/pkg/window/messages.go +++ b/pkg/window/messages.go @@ -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"` diff --git a/pkg/window/mock_platform.go b/pkg/window/mock_platform.go index f257988..642de83 100644 --- a/pkg/window/mock_platform.go +++ b/pkg/window/mock_platform.go @@ -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 diff --git a/pkg/window/platform.go b/pkg/window/platform.go index be58dc0..37785ff 100644 --- a/pkg/window/platform.go +++ b/pkg/window/platform.go @@ -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 diff --git a/pkg/window/wails.go b/pkg/window/wails.go index 006cc6d..c4c3f0b 100644 --- a/pkg/window/wails.go +++ b/pkg/window/wails.go @@ -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))