chore(gui): add AX usage examples
This commit is contained in:
parent
0204540b20
commit
d3443d4be9
13 changed files with 114 additions and 0 deletions
|
|
@ -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"`
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue