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:31:56 +00:00
parent 0204540b20
commit d3443d4be9
13 changed files with 114 additions and 0 deletions

View file

@ -30,6 +30,7 @@ import (
)
// Options holds configuration for the display service.
// Use: svc, err := display.NewService()
type Options struct{}
// WindowInfo is an alias for window.WindowInfo (backward compatibility).
@ -41,6 +42,7 @@ type LayoutSuggestion = window.LayoutSuggestion
// Service manages windowing, dialogs, and other visual elements.
// It orchestrates sub-services (window, systray, menu) via IPC and bridges
// IPC actions to WebSocket events for TypeScript apps.
// Use: svc, err := display.NewService()
type Service struct {
*core.ServiceRuntime[Options]
wailsApp *application.App
@ -64,6 +66,7 @@ func NewService() (*Service, error) {
}
// Deprecated: use NewService.
// Use: svc, err := display.New()
func New() (*Service, error) {
return NewService()
}
@ -1436,6 +1439,7 @@ func (s *Service) GetSavedWindowStates() map[string]window.WindowState {
}
// CreateWindowOptions contains options for creating a new window.
// Use: opts := display.CreateWindowOptions{Name: "editor", URL: "/editor"}
type CreateWindowOptions struct {
Name string `json:"name"`
Title string `json:"title,omitempty"`

View file

@ -5,12 +5,14 @@ import "github.com/wailsapp/wails/v3/pkg/application"
// App abstracts the Wails application for the display orchestrator.
// The service uses Logger() for diagnostics and Quit() for shutdown.
// Use: var app display.App
type App interface {
Logger() Logger
Quit()
}
// Logger wraps Wails logging.
// Use: var logger display.Logger
type Logger interface {
Info(message string, args ...any)
}

View file

@ -4,14 +4,17 @@ package display
// ActionIDECommand is broadcast when a menu handler triggers an IDE command
// (save, run, build). Replaces direct s.app.Event().Emit("ide:*") calls.
// Listeners (e.g. editor windows) handle this via HandleIPCEvents.
// Use: _ = c.ACTION(display.ActionIDECommand{Command: "save"})
type ActionIDECommand struct {
Command string `json:"command"` // "save", "run", "build"
}
// EventIDECommand is the WS event type for IDE commands.
// Use: eventType := display.EventIDECommand
const EventIDECommand EventType = "ide.command"
// Theme is the display-level theme summary exposed by the service API.
// Use: theme := display.Theme{IsDark: true}
type Theme struct {
IsDark bool `json:"isDark"`
}

View file

@ -2,19 +2,25 @@
package screen
// QueryAll returns all screens. Result: []Screen
// Use: result, _, err := c.QUERY(screen.QueryAll{})
type QueryAll struct{}
// QueryPrimary returns the primary screen. Result: *Screen (nil if not found)
// Use: result, _, err := c.QUERY(screen.QueryPrimary{})
type QueryPrimary struct{}
// QueryByID returns a screen by ID. Result: *Screen (nil if not found)
// Use: result, _, err := c.QUERY(screen.QueryByID{ID: "display-1"})
type QueryByID struct{ ID string }
// QueryAtPoint returns the screen containing a point. Result: *Screen (nil if none)
// Use: result, _, err := c.QUERY(screen.QueryAtPoint{X: 100, Y: 100})
type QueryAtPoint struct{ X, Y int }
// QueryWorkAreas returns work areas for all screens. Result: []Rect
// Use: result, _, err := c.QUERY(screen.QueryWorkAreas{})
type QueryWorkAreas struct{}
// ActionScreensChanged is broadcast when displays change.
// Use: _ = c.ACTION(screen.ActionScreensChanged{Screens: screens})
type ActionScreensChanged struct{ Screens []Screen }

View file

@ -2,12 +2,14 @@
package screen
// Platform abstracts the screen/display backend.
// Use: var p screen.Platform
type Platform interface {
GetAll() []Screen
GetPrimary() *Screen
}
// Screen describes a display/monitor.
// Use: scr := screen.Screen{ID: "display-1"}
type Screen struct {
ID string `json:"id"`
Name string `json:"name"`
@ -20,6 +22,7 @@ type Screen struct {
}
// Rect represents a rectangle with position and dimensions.
// Use: rect := screen.Rect{X: 0, Y: 0, Width: 1920, Height: 1080}
type Rect struct {
X int `json:"x"`
Y int `json:"y"`
@ -28,6 +31,7 @@ type Rect struct {
}
// Size represents dimensions.
// Use: size := screen.Size{Width: 1920, Height: 1080}
type Size struct {
Width int `json:"width"`
Height int `json:"height"`

View file

@ -8,15 +8,18 @@ import (
)
// Options holds configuration for the screen service.
// Use: svc, err := screen.Register(platform)(core.New())
type Options struct{}
// Service is a core.Service providing screen/display queries via IPC.
// Use: svc, err := screen.Register(platform)(core.New())
type Service struct {
*core.ServiceRuntime[Options]
platform Platform
}
// Register creates a factory closure that captures the Platform adapter.
// Use: core.WithService(screen.Register(platform))
func Register(p Platform) func(*core.Core) (any, error) {
return func(c *core.Core) (any, error) {
return &Service{
@ -27,12 +30,14 @@ func Register(p Platform) func(*core.Core) (any, error) {
}
// OnStartup registers IPC handlers.
// Use: _ = svc.OnStartup(context.Background())
func (s *Service) OnStartup(ctx context.Context) error {
s.Core().RegisterQuery(s.handleQuery)
return nil
}
// HandleIPCEvents is auto-discovered by core.WithService.
// Use: _ = svc.HandleIPCEvents(core, msg)
func (s *Service) HandleIPCEvents(c *core.Core, msg core.Message) error {
return nil
}

View file

@ -49,6 +49,7 @@ type connector interface {
}
// Options holds configuration for the webview service.
// Use: svc, err := webview.Register()(core.New())
type Options struct {
DebugURL string // Chrome debug endpoint (default: "http://localhost:9222")
Timeout time.Duration // Operation timeout (default: 30s)
@ -56,6 +57,7 @@ type Options struct {
}
// Service is a core.Service managing webview interactions via IPC.
// Use: svc, err := webview.Register()(core.New())
type Service struct {
*core.ServiceRuntime[Options]
opts Options
@ -67,6 +69,7 @@ type Service struct {
}
// Register creates a factory closure with the given options.
// Use: core.WithService(webview.Register())
func Register(opts ...func(*Options)) func(*core.Core) (any, error) {
o := Options{
DebugURL: "http://localhost:9222",

View file

@ -2,6 +2,7 @@
package window
// WindowInfo contains information about a window.
// Use: info := window.WindowInfo{Name: "editor", Title: "Core Editor"}
type WindowInfo struct {
Name string `json:"name"`
Title string `json:"title"`
@ -16,6 +17,7 @@ type WindowInfo struct {
}
// Bounds describes the position and size of a window.
// Use: bounds := window.Bounds{X: 10, Y: 10, Width: 1280, Height: 800}
type Bounds struct {
X int `json:"x"`
Y int `json:"y"`
@ -26,19 +28,24 @@ type Bounds struct {
// --- Queries (read-only) ---
// QueryWindowList returns all tracked windows. Result: []WindowInfo
// Use: result, _, err := c.QUERY(window.QueryWindowList{})
type QueryWindowList struct{}
// QueryWindowByName returns a single window by name. Result: *WindowInfo (nil if not found)
// Use: result, _, err := c.QUERY(window.QueryWindowByName{Name: "editor"})
type QueryWindowByName struct{ Name string }
// QueryConfig requests this service's config section from the display orchestrator.
// Result: map[string]any
// Use: result, _, err := c.QUERY(window.QueryConfig{})
type QueryConfig struct{}
// QueryWindowBounds returns the current bounds for a window.
// Use: result, _, err := c.QUERY(window.QueryWindowBounds{Name: "editor"})
type QueryWindowBounds struct{ Name string }
// QueryFindSpace returns a suggested free placement for a new window.
// Use: result, _, err := c.QUERY(window.QueryFindSpace{Width: 1280, Height: 800})
type QueryFindSpace struct {
Width int
Height int
@ -47,6 +54,7 @@ type QueryFindSpace struct {
}
// QueryLayoutSuggestion returns a layout recommendation for the current screen.
// Use: result, _, err := c.QUERY(window.QueryLayoutSuggestion{WindowCount: 2})
type QueryLayoutSuggestion struct {
WindowCount int
ScreenWidth int
@ -56,6 +64,7 @@ type QueryLayoutSuggestion struct {
// --- Tasks (side-effects) ---
// TaskOpenWindow creates a new window. Result: WindowInfo
// Use: _, _, err := c.PERFORM(window.TaskOpenWindow{Opts: []window.WindowOption{window.WithName("editor")}})
type TaskOpenWindow struct {
Window *Window
Opts []WindowOption
@ -63,15 +72,18 @@ type TaskOpenWindow struct {
// TaskCloseWindow closes a window after persisting state.
// Platform close events emit ActionWindowClosed through the tracked window handler.
// Use: _, _, err := c.PERFORM(window.TaskCloseWindow{Name: "editor"})
type TaskCloseWindow struct{ Name string }
// TaskSetPosition moves a window.
// Use: _, _, err := c.PERFORM(window.TaskSetPosition{Name: "editor", X: 160, Y: 120})
type TaskSetPosition struct {
Name string
X, Y int
}
// TaskSetSize resizes a window.
// Use: _, _, err := c.PERFORM(window.TaskSetSize{Name: "editor", Width: 1280, Height: 800})
type TaskSetSize struct {
Name string
Width, Height int
@ -79,30 +91,37 @@ type TaskSetSize struct {
}
// TaskMaximise maximises a window.
// Use: _, _, err := c.PERFORM(window.TaskMaximise{Name: "editor"})
type TaskMaximise struct{ Name string }
// TaskMinimise minimises a window.
// Use: _, _, err := c.PERFORM(window.TaskMinimise{Name: "editor"})
type TaskMinimise struct{ Name string }
// TaskFocus brings a window to the front.
// Use: _, _, err := c.PERFORM(window.TaskFocus{Name: "editor"})
type TaskFocus struct{ Name string }
// TaskRestore restores a maximised or minimised window to its normal state.
// Use: _, _, err := c.PERFORM(window.TaskRestore{Name: "editor"})
type TaskRestore struct{ Name string }
// TaskSetTitle changes a window's title.
// Use: _, _, err := c.PERFORM(window.TaskSetTitle{Name: "editor", Title: "Core Editor"})
type TaskSetTitle struct {
Name string
Title string
}
// TaskSetAlwaysOnTop pins a window above others.
// Use: _, _, err := c.PERFORM(window.TaskSetAlwaysOnTop{Name: "editor", AlwaysOnTop: true})
type TaskSetAlwaysOnTop struct {
Name string
AlwaysOnTop bool
}
// TaskSetBackgroundColour updates the window background colour.
// Use: _, _, err := c.PERFORM(window.TaskSetBackgroundColour{Name: "editor", Red: 0, Green: 0, Blue: 0, Alpha: 0})
type TaskSetBackgroundColour struct {
Name string
Red uint8
@ -112,18 +131,21 @@ type TaskSetBackgroundColour struct {
}
// TaskSetOpacity updates the window opacity as a value between 0 and 1.
// Use: _, _, err := c.PERFORM(window.TaskSetOpacity{Name: "editor", Opacity: 0.85})
type TaskSetOpacity struct {
Name string
Opacity float32
}
// TaskSetVisibility shows or hides a window.
// Use: _, _, err := c.PERFORM(window.TaskSetVisibility{Name: "editor", Visible: false})
type TaskSetVisibility struct {
Name string
Visible bool
}
// TaskFullscreen enters or exits fullscreen mode.
// Use: _, _, err := c.PERFORM(window.TaskFullscreen{Name: "editor", Fullscreen: true})
type TaskFullscreen struct {
Name string
Fullscreen bool
@ -132,47 +154,57 @@ type TaskFullscreen struct {
// --- Layout Queries ---
// QueryLayoutList returns summaries of all saved layouts. Result: []LayoutInfo
// Use: result, _, err := c.QUERY(window.QueryLayoutList{})
type QueryLayoutList struct{}
// QueryLayoutGet returns a layout by name. Result: *Layout (nil if not found)
// Use: result, _, err := c.QUERY(window.QueryLayoutGet{Name: "coding"})
type QueryLayoutGet struct{ Name string }
// --- Layout Tasks ---
// TaskSaveLayout saves the current window arrangement as a named layout. Result: bool
// Use: _, _, err := c.PERFORM(window.TaskSaveLayout{Name: "coding"})
type TaskSaveLayout struct{ Name string }
// TaskRestoreLayout restores a saved layout by name.
// Use: _, _, err := c.PERFORM(window.TaskRestoreLayout{Name: "coding"})
type TaskRestoreLayout struct{ Name string }
// TaskDeleteLayout removes a saved layout by name.
// Use: _, _, err := c.PERFORM(window.TaskDeleteLayout{Name: "coding"})
type TaskDeleteLayout struct{ Name string }
// TaskTileWindows arranges windows in a tiling mode.
// Use: _, _, err := c.PERFORM(window.TaskTileWindows{Mode: "grid"})
type TaskTileWindows struct {
Mode string // "left-right", "grid", "left-half", "right-half", etc.
Windows []string // window names; empty = all
}
// TaskSnapWindow snaps a window to a screen edge/corner.
// Use: _, _, err := c.PERFORM(window.TaskSnapWindow{Name: "editor", Position: "left"})
type TaskSnapWindow struct {
Name string // window name
Position string // "left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right", "center"
}
// TaskArrangePair places two windows side-by-side in a balanced split.
// Use: _, _, err := c.PERFORM(window.TaskArrangePair{First: "editor", Second: "terminal"})
type TaskArrangePair struct {
First string
Second string
}
// TaskBesideEditor places a target window beside an editor/IDE window.
// Use: _, _, err := c.PERFORM(window.TaskBesideEditor{Editor: "editor", Window: "terminal"})
type TaskBesideEditor struct {
Editor string
Window string
}
// TaskStackWindows cascades windows with a shared offset.
// Use: _, _, err := c.PERFORM(window.TaskStackWindows{Windows: []string{"editor", "terminal"}})
type TaskStackWindows struct {
Windows []string
OffsetX int
@ -180,31 +212,47 @@ type TaskStackWindows struct {
}
// TaskApplyWorkflow applies a predefined workflow layout to windows.
// Use: _, _, err := c.PERFORM(window.TaskApplyWorkflow{Workflow: window.WorkflowCoding})
type TaskApplyWorkflow struct {
Workflow WorkflowLayout
Windows []string
}
// TaskSaveConfig persists this service's config section via the display orchestrator.
// Use: _, _, err := c.PERFORM(window.TaskSaveConfig{Value: map[string]any{"default_width": 1280}})
type TaskSaveConfig struct{ Value map[string]any }
// --- Actions (broadcasts) ---
// ActionWindowOpened is broadcast when a window is created.
// Use: _ = c.ACTION(window.ActionWindowOpened{Name: "editor"})
type ActionWindowOpened struct{ Name string }
// ActionWindowClosed is broadcast when a window is closed.
// Use: _ = c.ACTION(window.ActionWindowClosed{Name: "editor"})
type ActionWindowClosed struct{ Name string }
// ActionWindowMoved is broadcast when a window is moved.
// Use: _ = c.ACTION(window.ActionWindowMoved{Name: "editor", X: 160, Y: 120})
type ActionWindowMoved struct {
Name string
X, Y int
}
// ActionWindowResized is broadcast when a window is resized.
// Use: _ = c.ACTION(window.ActionWindowResized{Name: "editor", Width: 1280, Height: 800})
type ActionWindowResized struct {
Name string
Width, Height int
W, H int
}
// ActionWindowFocused is broadcast when a window gains focus.
// Use: _ = c.ACTION(window.ActionWindowFocused{Name: "editor"})
type ActionWindowFocused struct{ Name string }
// ActionWindowBlurred is broadcast when a window loses focus.
// Use: _ = c.ACTION(window.ActionWindowBlurred{Name: "editor"})
type ActionWindowBlurred struct{ Name string }
type ActionFilesDropped struct {
@ -214,6 +262,7 @@ type ActionFilesDropped struct {
}
// SpaceInfo describes a suggested empty area on the screen.
// Use: info := window.SpaceInfo{X: 160, Y: 120, Width: 1280, Height: 800}
type SpaceInfo struct {
X int `json:"x"`
Y int `json:"y"`
@ -225,6 +274,7 @@ type SpaceInfo struct {
}
// LayoutSuggestion describes a recommended layout for a screen.
// Use: suggestion := window.LayoutSuggestion{Mode: "side-by-side"}
type LayoutSuggestion struct {
Mode string `json:"mode"`
Columns int `json:"columns"`

View file

@ -2,12 +2,14 @@
package window
// Platform abstracts the windowing backend (Wails v3).
// Use: var p window.Platform
type Platform interface {
CreateWindow(opts PlatformWindowOptions) PlatformWindow
GetWindows() []PlatformWindow
}
// PlatformWindowOptions are the backend-specific options passed to CreateWindow.
// Use: opts := window.PlatformWindowOptions{Name: "editor"}
type PlatformWindowOptions struct {
Name string
Title string
@ -25,6 +27,7 @@ type PlatformWindowOptions struct {
}
// PlatformWindow is a live window handle from the backend.
// Use: var w window.PlatformWindow
type PlatformWindow interface {
// Identity
Name() string
@ -68,6 +71,7 @@ type PlatformWindow interface {
}
// WindowEvent is emitted by the backend for window state changes.
// Use: evt := window.WindowEvent{Type: "focus", Name: "editor"}
type WindowEvent struct {
Type string // "focus", "blur", "move", "resize", "close"
Name string // window name

View file

@ -4,6 +4,7 @@ import "forge.lthn.ai/core/go/pkg/core"
// Register creates a factory closure that captures the Platform adapter.
// The returned function has the signature WithService requires: func(*Core) (any, error).
// Use: core.WithService(window.Register(platform))
func Register(p Platform) func(*core.Core) (any, error) {
return func(c *core.Core) (any, error) {
return &Service{

View file

@ -11,11 +11,13 @@ import (
)
// Options holds configuration for the window service.
// Use: svc, err := window.Register(platform)(core.New())
type Options struct{}
// Service is a core.Service managing window lifecycle via IPC.
// Use: core.WithService(window.Register(window.NewMockPlatform()))
// It embeds ServiceRuntime for Core access and composes Manager for platform operations.
// Use: svc, err := window.Register(platform)(core.New())
type Service struct {
*core.ServiceRuntime[Options]
manager *Manager
@ -23,6 +25,7 @@ type Service struct {
}
// OnStartup queries config from the display orchestrator and registers IPC handlers.
// Use: _ = svc.OnStartup(context.Background())
func (s *Service) OnStartup(ctx context.Context) error {
// Query config — display registers its handler before us (registration order guarantee).
// If display is not registered, handled=false and we skip config.
@ -60,6 +63,7 @@ func (s *Service) applyConfig(cfg map[string]any) {
}
// HandleIPCEvents is auto-discovered and registered by core.WithService.
// Use: _ = svc.HandleIPCEvents(core, msg)
func (s *Service) HandleIPCEvents(c *core.Core, msg core.Message) error {
return nil
}

View file

@ -11,6 +11,7 @@ import (
// WindowState holds the persisted position/size of a window.
// JSON tags match existing window_state.json format for backward compat.
// Use: state := window.WindowState{X: 10, Y: 20, Width: 1280, Height: 800}
type WindowState struct {
X int `json:"x,omitempty"`
Y int `json:"y,omitempty"`
@ -23,6 +24,7 @@ type WindowState struct {
}
// StateManager persists window positions to ~/.config/Core/window_state.json.
// Use: sm := window.NewStateManager()
type StateManager struct {
configDir string
statePath string
@ -32,6 +34,7 @@ type StateManager struct {
}
// NewStateManager creates a StateManager loading from the default config directory.
// Use: sm := window.NewStateManager()
func NewStateManager() *StateManager {
sm := &StateManager{
states: make(map[string]WindowState),
@ -46,6 +49,7 @@ func NewStateManager() *StateManager {
// NewStateManagerWithDir creates a StateManager loading from a custom config directory.
// Useful for testing or when the default config directory is not appropriate.
// Use: sm := window.NewStateManagerWithDir(t.TempDir())
func NewStateManagerWithDir(configDir string) *StateManager {
sm := &StateManager{
configDir: configDir,
@ -70,6 +74,7 @@ func (sm *StateManager) dataDir() string {
}
// SetPath overrides the persisted state file path.
// Use: sm.SetPath(filepath.Join(t.TempDir(), "window_state.json"))
func (sm *StateManager) SetPath(path string) {
if path == "" {
return
@ -120,6 +125,7 @@ func (sm *StateManager) scheduleSave() {
}
// GetState returns the saved state for a window name.
// Use: state, ok := sm.GetState("editor")
func (sm *StateManager) GetState(name string) (WindowState, bool) {
sm.mu.RLock()
defer sm.mu.RUnlock()
@ -128,6 +134,7 @@ func (sm *StateManager) GetState(name string) (WindowState, bool) {
}
// SetState saves state for a window name (debounced disk write).
// Use: sm.SetState("editor", window.WindowState{Width: 1280, Height: 800})
func (sm *StateManager) SetState(name string, state WindowState) {
state.UpdatedAt = time.Now().UnixMilli()
sm.mu.Lock()
@ -137,6 +144,7 @@ func (sm *StateManager) SetState(name string, state WindowState) {
}
// UpdatePosition updates only the position fields.
// Use: sm.UpdatePosition("editor", 160, 120)
func (sm *StateManager) UpdatePosition(name string, x, y int) {
sm.mu.Lock()
s := sm.states[name]
@ -149,6 +157,7 @@ func (sm *StateManager) UpdatePosition(name string, x, y int) {
}
// UpdateSize updates only the size fields.
// Use: sm.UpdateSize("editor", 1280, 800)
func (sm *StateManager) UpdateSize(name string, width, height int) {
sm.mu.Lock()
s := sm.states[name]
@ -161,6 +170,7 @@ func (sm *StateManager) UpdateSize(name string, width, height int) {
}
// UpdateMaximized updates the maximized flag.
// Use: sm.UpdateMaximized("editor", true)
func (sm *StateManager) UpdateMaximized(name string, maximized bool) {
sm.mu.Lock()
s := sm.states[name]
@ -172,6 +182,7 @@ func (sm *StateManager) UpdateMaximized(name string, maximized bool) {
}
// CaptureState snapshots the current state from a PlatformWindow.
// Use: sm.CaptureState(pw)
func (sm *StateManager) CaptureState(pw PlatformWindow) {
x, y := pw.Position()
w, h := pw.Size()
@ -182,6 +193,7 @@ func (sm *StateManager) CaptureState(pw PlatformWindow) {
}
// ApplyState restores saved position/size to a Window descriptor.
// Use: sm.ApplyState(&window.Window{Name: "editor"})
func (sm *StateManager) ApplyState(w *Window) {
s, ok := sm.GetState(w.Name)
if !ok {
@ -198,6 +210,7 @@ func (sm *StateManager) ApplyState(w *Window) {
}
// ListStates returns all stored window names.
// Use: names := sm.ListStates()
func (sm *StateManager) ListStates() []string {
sm.mu.RLock()
defer sm.mu.RUnlock()
@ -209,6 +222,7 @@ func (sm *StateManager) ListStates() []string {
}
// Clear removes all stored states.
// Use: sm.Clear()
func (sm *StateManager) Clear() {
sm.mu.Lock()
sm.states = make(map[string]WindowState)
@ -217,6 +231,7 @@ func (sm *StateManager) Clear() {
}
// ForceSync writes state to disk immediately.
// Use: sm.ForceSync()
func (sm *StateManager) ForceSync() {
if sm.saveTimer != nil {
sm.saveTimer.Stop()

View file

@ -26,6 +26,7 @@ type Window struct {
}
// ToPlatformOptions converts a Window to PlatformWindowOptions for the backend.
// Use: opts := spec.ToPlatformOptions()
func (w *Window) ToPlatformOptions() PlatformWindowOptions {
return PlatformWindowOptions{
Name: w.Name, Title: w.Title, URL: w.URL,
@ -73,6 +74,7 @@ func NewManagerWithDir(platform Platform, configDir string) *Manager {
}
// SetDefaultWidth overrides the fallback width used when a window is created without one.
// Use: mgr.SetDefaultWidth(1280)
func (m *Manager) SetDefaultWidth(width int) {
if width > 0 {
m.defaultWidth = width
@ -80,6 +82,7 @@ func (m *Manager) SetDefaultWidth(width int) {
}
// SetDefaultHeight overrides the fallback height used when a window is created without one.
// Use: mgr.SetDefaultHeight(800)
func (m *Manager) SetDefaultHeight(height int) {
if height > 0 {
m.defaultHeight = height
@ -136,6 +139,7 @@ func (m *Manager) Create(w *Window) (PlatformWindow, error) {
}
// Get returns a tracked window by name.
// Use: pw, ok := mgr.Get("editor")
func (m *Manager) Get(name string) (PlatformWindow, bool) {
m.mu.RLock()
defer m.mu.RUnlock()
@ -144,6 +148,7 @@ func (m *Manager) Get(name string) (PlatformWindow, bool) {
}
// List returns all tracked window names.
// Use: names := mgr.List()
func (m *Manager) List() []string {
m.mu.RLock()
defer m.mu.RUnlock()
@ -155,6 +160,7 @@ func (m *Manager) List() []string {
}
// Remove stops tracking a window by name.
// Use: mgr.Remove("editor")
func (m *Manager) Remove(name string) {
m.mu.Lock()
delete(m.windows, name)
@ -162,21 +168,25 @@ func (m *Manager) Remove(name string) {
}
// Platform returns the underlying platform for direct access.
// Use: platform := mgr.Platform()
func (m *Manager) Platform() Platform {
return m.platform
}
// State returns the state manager for window persistence.
// Use: state := mgr.State()
func (m *Manager) State() *StateManager {
return m.state
}
// Layout returns the layout manager.
// Use: layouts := mgr.Layout()
func (m *Manager) Layout() *LayoutManager {
return m.layout
}
// SuggestLayout returns a simple layout recommendation for the given screen.
// Use: suggestion := mgr.SuggestLayout(1920, 1080, 2)
func (m *Manager) SuggestLayout(screenW, screenH, windowCount int) LayoutSuggestion {
if windowCount <= 1 {
return LayoutSuggestion{
@ -224,6 +234,7 @@ func (m *Manager) SuggestLayout(screenW, screenH, windowCount int) LayoutSuggest
}
// FindSpace returns a free placement suggestion for a new window.
// Use: info := mgr.FindSpace(1920, 1080, 1280, 800)
func (m *Manager) FindSpace(screenW, screenH, width, height int) SpaceInfo {
if width <= 0 {
width = screenW / 2
@ -273,6 +284,7 @@ func (m *Manager) FindSpace(screenW, screenH, width, height int) SpaceInfo {
}
// ArrangePair places two windows side-by-side with a balanced split.
// Use: _ = mgr.ArrangePair("editor", "terminal", 1920, 1080)
func (m *Manager) ArrangePair(first, second string, screenW, screenH int) error {
left, ok := m.Get(first)
if !ok {
@ -293,6 +305,7 @@ func (m *Manager) ArrangePair(first, second string, screenW, screenH int) error
}
// BesideEditor places a target window beside an editor window, using a 70/30 split.
// Use: _ = mgr.BesideEditor("editor", "terminal", 1920, 1080)
func (m *Manager) BesideEditor(editorName, windowName string, screenW, screenH int) error {
editor, ok := m.Get(editorName)
if !ok {