Expand display websocket window bridge
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 19:45:31 +00:00
parent f2eb9f03c4
commit 8db26398af
2 changed files with 241 additions and 0 deletions

View file

@ -281,6 +281,147 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) {
case "browser:open-file":
path, _ := msg.Data["path"].(string)
result, handled, err = s.Core().PERFORM(browser.TaskOpenFile{Path: path})
case "window:list":
result, handled, err = s.Core().QUERY(window.QueryWindowList{})
case "window:get":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
result, handled, err = s.Core().QUERY(window.QueryWindowByName{Name: name})
case "window:focused":
result, handled, err = s.GetFocusedWindow(), true, nil
case "window:create":
var opts CreateWindowOptions
encoded, _ := json.Marshal(msg.Data)
if err := json.Unmarshal(encoded, &opts); err != nil {
return nil, false, fmt.Errorf("ws: invalid window create options: %w", err)
}
info, createErr := s.CreateWindow(opts)
if createErr != nil {
return nil, false, createErr
}
result, handled, err = info, true, nil
case "window:close":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
result, handled, err = nil, true, s.CloseWindow(name)
case "window:position":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
x, _ := msg.Data["x"].(float64)
y, _ := msg.Data["y"].(float64)
result, handled, err = nil, true, s.SetWindowPosition(name, int(x), int(y))
case "window:size":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
width, _ := msg.Data["width"].(float64)
height, _ := msg.Data["height"].(float64)
result, handled, err = nil, true, s.SetWindowSize(name, int(width), int(height))
case "window:bounds":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
x, _ := msg.Data["x"].(float64)
y, _ := msg.Data["y"].(float64)
width, _ := msg.Data["width"].(float64)
height, _ := msg.Data["height"].(float64)
result, handled, err = nil, true, s.SetWindowBounds(name, int(x), int(y), int(width), int(height))
case "window:maximize":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
result, handled, err = nil, true, s.MaximizeWindow(name)
case "window:minimize":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
result, handled, err = nil, true, s.MinimizeWindow(name)
case "window:restore":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
result, handled, err = nil, true, s.RestoreWindow(name)
case "window:focus":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
result, handled, err = nil, true, s.FocusWindow(name)
case "focus:set":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
result, handled, err = nil, true, s.FocusSet(name)
case "window:visibility":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
visible, _ := msg.Data["visible"].(bool)
result, handled, err = nil, true, s.SetWindowVisibility(name, visible)
case "window:title-set":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
title, e := wsRequire(msg.Data, "title")
if e != nil {
return nil, false, e
}
result, handled, err = nil, true, s.SetWindowTitle(name, title)
case "window:title-get":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
title, titleErr := s.GetWindowTitle(name)
if titleErr != nil {
return nil, false, titleErr
}
result, handled, err = title, true, nil
case "window:always-on-top":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
alwaysOnTop, _ := msg.Data["alwaysOnTop"].(bool)
result, handled, err = nil, true, s.SetWindowAlwaysOnTop(name, alwaysOnTop)
case "window:background-colour":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
red, _ := msg.Data["red"].(float64)
green, _ := msg.Data["green"].(float64)
blue, _ := msg.Data["blue"].(float64)
alpha, _ := msg.Data["alpha"].(float64)
result, handled, err = nil, true, s.SetWindowBackgroundColour(name, uint8(red), uint8(green), uint8(blue), uint8(alpha))
case "window:opacity":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
opacity, _ := msg.Data["opacity"].(float64)
result, handled, err = nil, true, s.SetWindowOpacity(name, float32(opacity))
case "window:fullscreen":
name, e := wsRequire(msg.Data, "name")
if e != nil {
return nil, false, e
}
fullscreen, _ := msg.Data["fullscreen"].(bool)
result, handled, err = nil, true, s.SetWindowFullscreen(name, fullscreen)
case "dock:show":
result, handled, err = s.Core().PERFORM(dock.TaskShowIcon{})
case "dock:hide":

View file

@ -950,6 +950,106 @@ func TestHandleWSMessage_Extended_Good(t *testing.T) {
assert.Equal(t, "side-by-side", suggestion.Mode)
})
t.Run("window list", func(t *testing.T) {
result, handled, err := svc.handleWSMessage(WSMessage{Action: "window:list"})
require.NoError(t, err)
assert.True(t, handled)
windows, ok := result.([]window.WindowInfo)
require.True(t, ok)
assert.GreaterOrEqual(t, len(windows), 2)
})
t.Run("window get", func(t *testing.T) {
result, handled, err := svc.handleWSMessage(WSMessage{
Action: "window:get",
Data: map[string]any{"name": "editor"},
})
require.NoError(t, err)
assert.True(t, handled)
info, ok := result.(*window.WindowInfo)
require.True(t, ok)
require.NotNil(t, info)
assert.Equal(t, "editor", info.Name)
})
t.Run("window focused", func(t *testing.T) {
require.NoError(t, svc.FocusWindow("assistant"))
result, handled, err := svc.handleWSMessage(WSMessage{Action: "window:focused"})
require.NoError(t, err)
assert.True(t, handled)
assert.Equal(t, "assistant", result)
})
t.Run("window title get", func(t *testing.T) {
result, handled, err := svc.handleWSMessage(WSMessage{
Action: "window:title-get",
Data: map[string]any{"name": "editor"},
})
require.NoError(t, err)
assert.True(t, handled)
assert.Equal(t, "Editor", result)
})
t.Run("window position and bounds", func(t *testing.T) {
_, handled, err := svc.handleWSMessage(WSMessage{
Action: "window:position",
Data: map[string]any{
"name": "assistant",
"x": float64(40),
"y": float64(50),
},
})
require.NoError(t, err)
assert.True(t, handled)
_, handled, err = svc.handleWSMessage(WSMessage{
Action: "window:bounds",
Data: map[string]any{
"name": "assistant",
"x": float64(60),
"y": float64(70),
"width": float64(800),
"height": float64(640),
},
})
require.NoError(t, err)
assert.True(t, handled)
info, err := svc.GetWindowInfo("assistant")
require.NoError(t, err)
require.NotNil(t, info)
assert.Equal(t, 60, info.X)
assert.Equal(t, 70, info.Y)
assert.Equal(t, 800, info.Width)
assert.Equal(t, 640, info.Height)
})
t.Run("window create and close", func(t *testing.T) {
result, handled, err := svc.handleWSMessage(WSMessage{
Action: "window:create",
Data: map[string]any{
"name": "ws-new",
"title": "WS New",
"url": "/ws-new",
"width": float64(500),
"height": float64(350),
},
})
require.NoError(t, err)
assert.True(t, handled)
created, ok := result.(*window.WindowInfo)
require.True(t, ok)
require.NotNil(t, created)
assert.Equal(t, "ws-new", created.Name)
_, handled, err = svc.handleWSMessage(WSMessage{
Action: "window:close",
Data: map[string]any{"name": "ws-new"},
})
require.NoError(t, err)
assert.True(t, handled)
})
t.Run("layout stack", func(t *testing.T) {
_, handled, err := svc.handleWSMessage(WSMessage{
Action: "layout:stack",