refactor(ax): add atomic window bounds task
Some checks failed
Security Scan / security (push) Failing after 28s
Test / test (push) Successful in 1m21s

This commit is contained in:
Virgil 2026-03-31 08:23:59 +00:00
parent bd4dfa8027
commit 5ce1d67f70
8 changed files with 98 additions and 9 deletions

View file

@ -632,10 +632,9 @@ func (s *Service) SetWindowSize(name string, width, height int) error {
// SetWindowBounds sets both position and size of a window via IPC.
func (s *Service) SetWindowBounds(name string, x, y, width, height int) error {
if _, err := s.performWindowTask("display.SetWindowBounds", window.TaskSetPosition{Name: name, X: x, Y: y}); err != nil {
return err
}
_, err := s.performWindowTask("display.SetWindowBounds", window.TaskSetSize{Name: name, Width: width, Height: height})
_, err := s.performWindowTask("display.SetWindowBounds", window.TaskSetBounds{
Name: name, X: x, Y: y, Width: width, Height: height,
})
return err
}

View file

@ -252,6 +252,21 @@ func TestSetWindowSize_Good(t *testing.T) {
assert.Equal(t, 768, info.Height)
}
func TestSetWindowBounds_Good(t *testing.T) {
c := newTestConclave(t)
svc := core.MustServiceFor[*Service](c, "display")
_ = svc.OpenWindow(window.Window{Name: "bounds-win"})
err := svc.SetWindowBounds("bounds-win", 100, 200, 1024, 768)
assert.NoError(t, err)
info, _ := svc.GetWindowInfo("bounds-win")
assert.Equal(t, 100, info.X)
assert.Equal(t, 200, info.Y)
assert.Equal(t, 1024, info.Width)
assert.Equal(t, 768, info.Height)
}
func TestMaximizeWindow_Good(t *testing.T) {
c := newTestConclave(t)
svc := core.MustServiceFor[*Service](c, "display")

View file

@ -165,11 +165,9 @@ type WindowBoundsOutput struct {
}
func (s *Subsystem) windowBounds(_ context.Context, _ *mcp.CallToolRequest, input WindowBoundsInput) (*mcp.CallToolResult, WindowBoundsOutput, error) {
_, _, err := s.core.PERFORM(window.TaskSetPosition{Name: input.Name, X: input.X, Y: input.Y})
if err != nil {
return nil, WindowBoundsOutput{}, err
}
_, _, err = s.core.PERFORM(window.TaskSetSize{Name: input.Name, Width: input.Width, Height: input.Height})
_, _, err := s.core.PERFORM(window.TaskSetBounds{
Name: input.Name, X: input.X, Y: input.Y, Width: input.Width, Height: input.Height,
})
if err != nil {
return nil, WindowBoundsOutput{}, err
}

View file

@ -31,6 +31,12 @@ type TaskSetPosition struct {
X, Y int
}
type TaskSetBounds struct {
Name string
X, Y int
Width, Height int
}
type TaskSetSize struct {
Name string
Width, Height int

View file

@ -63,6 +63,20 @@ func TestStateManager_UpdateSize_Good(t *testing.T) {
assert.Equal(t, 200, got.Y)
}
func TestStateManager_UpdateBounds_Good(t *testing.T) {
sm := NewStateManagerWithDir(t.TempDir())
sm.SetState("win", WindowState{X: 100, Y: 200, Width: 800, Height: 600})
sm.UpdateBounds("win", 300, 400, 1280, 720)
got, ok := sm.GetState("win")
require.True(t, ok)
assert.Equal(t, 300, got.X)
assert.Equal(t, 400, got.Y)
assert.Equal(t, 1280, got.Width)
assert.Equal(t, 720, got.Height)
}
func TestStateManager_UpdateMaximized_Good(t *testing.T) {
sm := NewStateManagerWithDir(t.TempDir())
sm.SetState("win", WindowState{Width: 800, Height: 600, Maximized: false})

View file

@ -134,6 +134,8 @@ func (s *Service) handleTask(c *core.Core, t core.Task) (any, bool, error) {
return nil, true, s.taskCloseWindow(t.Name)
case TaskSetPosition:
return nil, true, s.taskSetPosition(t.Name, t.X, t.Y)
case TaskSetBounds:
return nil, true, s.taskSetBounds(t.Name, t.X, t.Y, t.Width, t.Height)
case TaskSetSize:
return nil, true, s.taskSetSize(t.Name, t.Width, t.Height)
case TaskMaximize:
@ -282,6 +284,17 @@ func (s *Service) taskSetPosition(name string, x, y int) error {
return nil
}
func (s *Service) taskSetBounds(name string, x, y, width, height int) error {
platformWindow, err := s.requireWindow(name, "window.Service.taskSetBounds")
if err != nil {
return err
}
platformWindow.SetPosition(x, y)
platformWindow.SetSize(width, height)
s.manager.State().UpdateBounds(name, x, y, width, height)
return nil
}
func (s *Service) taskSetSize(name string, width, height int) error {
platformWindow, err := s.requireWindow(name, "window.Service.taskSetSize")
if err != nil {

View file

@ -177,6 +177,36 @@ func TestTaskSetSize_Good(t *testing.T) {
assert.Equal(t, 600, info.Height)
}
func TestTaskSetBounds_Good(t *testing.T) {
_, c := newTestWindowService(t)
_, _, _ = c.PERFORM(TaskOpenWindow{Window: Window{Name: "test"}})
_, handled, err := c.PERFORM(TaskSetBounds{
Name: "test",
X: 100,
Y: 200,
Width: 800,
Height: 600,
})
require.NoError(t, err)
assert.True(t, handled)
result, _, _ := c.QUERY(QueryWindowByName{Name: "test"})
info := result.(*WindowInfo)
assert.Equal(t, 100, info.X)
assert.Equal(t, 200, info.Y)
assert.Equal(t, 800, info.Width)
assert.Equal(t, 600, info.Height)
states, _, _ := c.QUERY(QuerySavedWindowStates{})
saved := states.(map[string]WindowState)
require.Contains(t, saved, "test")
assert.Equal(t, 100, saved["test"].X)
assert.Equal(t, 200, saved["test"].Y)
assert.Equal(t, 800, saved["test"].Width)
assert.Equal(t, 600, saved["test"].Height)
}
func TestTaskMaximize_Good(t *testing.T) {
_, c := newTestWindowService(t)
_, _, _ = c.PERFORM(TaskOpenWindow{Window: Window{Name: "test"}})

View file

@ -164,6 +164,20 @@ func (sm *StateManager) UpdateSize(name string, width, height int) {
sm.scheduleSave()
}
// UpdateBounds updates position and size in one state write.
func (sm *StateManager) UpdateBounds(name string, x, y, width, height int) {
sm.mu.Lock()
s := sm.states[name]
s.X = x
s.Y = y
s.Width = width
s.Height = height
s.UpdatedAt = time.Now().UnixMilli()
sm.states[name] = s
sm.mu.Unlock()
sm.scheduleSave()
}
// UpdateMaximized updates the maximized flag.
func (sm *StateManager) UpdateMaximized(name string, maximized bool) {
sm.mu.Lock()