gui/pkg/mcp/layout_helpers.go
Claude 8d3c0fb6d2
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run
feat(gui): implement chat-first UI and display primitives
- Replace provider dashboard with full chat UI (history, model selection, image attachments)
- Add chat settings/history/image queue/tool-call metadata persistence
- Add core://settings and core://store route handling in display package
- Add progressive assistant rendering, collapsible thinking/tool-call blocks
- Add markdown/code rendering with copy actions and lightbox image preview
- Add app mode detection (pkg/display/mode.go)
- Add chat backend coverage (pkg/display/chat_test.go)
- Add chat.service.ts frontend service
- AX sweep across pkg/mcp tools and pkg/window/webview/systray/notification

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-14 14:36:01 +01:00

195 lines
4.4 KiB
Go

package mcp
import (
"sort"
"dappco.re/go/core/gui/pkg/screen"
"dappco.re/go/core/gui/pkg/window"
"forge.lthn.ai/core/go/pkg/core"
)
func (s *Subsystem) allWindows() ([]window.WindowInfo, error) {
result, _, err := s.core.QUERY(window.QueryWindowList{})
if err != nil {
return nil, err
}
windows, _ := result.([]window.WindowInfo)
return windows, nil
}
func (s *Subsystem) allScreens() ([]screen.Screen, error) {
result, _, err := s.core.QUERY(screen.QueryAll{})
if err != nil {
return nil, err
}
screens, _ := result.([]screen.Screen)
return screens, nil
}
func (s *Subsystem) primaryScreen() (*screen.Screen, error) {
result, _, err := s.core.QUERY(screen.QueryPrimary{})
if err != nil {
return nil, err
}
scr, _ := result.(*screen.Screen)
return scr, nil
}
func (s *Subsystem) screenByID(id string) (*screen.Screen, error) {
if id == "" {
return nil, nil
}
result, _, err := s.core.QUERY(screen.QueryByID{ID: id})
if err != nil {
return nil, err
}
scr, _ := result.(*screen.Screen)
return scr, nil
}
func screenForWindowInfo(screens []screen.Screen, info window.WindowInfo) *screen.Screen {
cx := info.X + info.Width/2
cy := info.Y + info.Height/2
for i := range screens {
if screens[i].Bounds.Contains(cx, cy) {
return &screens[i]
}
}
return nil
}
func chooseScreenByIDOrPrimary(screens []screen.Screen, screenID string) *screen.Screen {
if screenID != "" {
for i := range screens {
if screens[i].ID == screenID {
return &screens[i]
}
}
}
for i := range screens {
if screens[i].IsPrimary {
return &screens[i]
}
}
if len(screens) == 0 {
return nil
}
return &screens[0]
}
func workAreaRect(scr *screen.Screen) screen.Rect {
if scr == nil {
return screen.Rect{}
}
if scr.WorkArea.Width > 0 && scr.WorkArea.Height > 0 {
return scr.WorkArea
}
return scr.Bounds
}
func uniqueSorted(values []int) []int {
sort.Ints(values)
if len(values) == 0 {
return values
}
out := values[:1]
for _, value := range values[1:] {
if value != out[len(out)-1] {
out = append(out, value)
}
}
return out
}
func clipRectToWorkArea(rect, workArea screen.Rect) (screen.Rect, bool) {
x1 := max(rect.X, workArea.X)
y1 := max(rect.Y, workArea.Y)
x2 := min(rect.X+rect.Width, workArea.X+workArea.Width)
y2 := min(rect.Y+rect.Height, workArea.Y+workArea.Height)
if x2 <= x1 || y2 <= y1 {
return screen.Rect{}, false
}
return screen.Rect{X: x1, Y: y1, Width: x2 - x1, Height: y2 - y1}, true
}
func findLargestFreeRect(workArea screen.Rect, occupied []screen.Rect, minWidth, minHeight int) (screen.Rect, bool) {
xs := []int{workArea.X, workArea.X + workArea.Width}
ys := []int{workArea.Y, workArea.Y + workArea.Height}
for _, rect := range occupied {
clipped, ok := clipRectToWorkArea(rect, workArea)
if !ok {
continue
}
xs = append(xs, clipped.X, clipped.X+clipped.Width)
ys = append(ys, clipped.Y, clipped.Y+clipped.Height)
}
xs = uniqueSorted(xs)
ys = uniqueSorted(ys)
bestArea := -1
best := screen.Rect{}
for xi := 0; xi < len(xs)-1; xi++ {
for xj := xi + 1; xj < len(xs); xj++ {
width := xs[xj] - xs[xi]
if width < minWidth {
continue
}
for yi := 0; yi < len(ys)-1; yi++ {
for yj := yi + 1; yj < len(ys); yj++ {
height := ys[yj] - ys[yi]
if height < minHeight {
continue
}
candidate := screen.Rect{X: xs[xi], Y: ys[yi], Width: width, Height: height}
if candidate.X < workArea.X || candidate.Y < workArea.Y ||
candidate.X+candidate.Width > workArea.X+workArea.Width ||
candidate.Y+candidate.Height > workArea.Y+workArea.Height {
continue
}
overlaps := false
for _, occ := range occupied {
if candidate.Overlaps(occ) {
overlaps = true
break
}
}
if overlaps {
continue
}
area := candidate.Width * candidate.Height
if area > bestArea || (area == bestArea && (candidate.Y < best.Y || (candidate.Y == best.Y && candidate.X < best.X))) {
bestArea = area
best = candidate
}
}
}
}
}
return best, bestArea >= 0
}
func applyRect(c *core.Core, windowName string, rect screen.Rect) error {
if _, _, err := c.PERFORM(window.TaskSetPosition{Name: windowName, X: rect.X, Y: rect.Y}); err != nil {
return err
}
_, _, err := c.PERFORM(window.TaskSetSize{Name: windowName, Width: rect.Width, Height: rect.Height})
return err
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func max(a, b int) int {
if a > b {
return a
}
return b
}