From 62ec735c10acccbf0fcdcbd5b3167297626fe46f Mon Sep 17 00:00:00 2001 From: Snider Date: Mon, 13 Apr 2026 09:32:01 +0100 Subject: [PATCH] =?UTF-8?q?refactor:=20AX=20compliance=20sweep=20=E2=80=94?= =?UTF-8?q?=20replace=20banned=20stdlib=20imports=20with=20core=20primitiv?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaced fmt, strings, sort, os, io, sync, encoding/json, path/filepath, errors, log, reflect with core.Sprintf, core.E, core.Contains, core.Trim, core.Split, core.Join, core.JoinPath, slices.Sort, c.Fs(), c.Lock(), core.JSONMarshal, core.ReadAll and other CoreGO v0.8.0 primitives. Framework boundary exceptions preserved where stdlib types are required by external interfaces (Gin, net/http, CGo, Wails, bubbletea). Co-Authored-By: Virgil --- go.sum | 2 + pkg/contextmenu/messages.go | 4 +- pkg/contextmenu/service.go | 6 +- pkg/display/display.go | 186 ++++++++++++++++--------------- pkg/display/display_test.go | 18 +-- pkg/display/events.go | 33 +++--- pkg/environment/service.go | 4 +- pkg/keybinding/messages.go | 4 +- pkg/keybinding/service.go | 8 +- pkg/mcp/mcp_test.go | 14 +-- pkg/mcp/tools_browser.go | 2 +- pkg/mcp/tools_clipboard.go | 14 +-- pkg/mcp/tools_contextmenu.go | 39 ++++--- pkg/mcp/tools_dialog.go | 14 +-- pkg/mcp/tools_dock.go | 2 +- pkg/mcp/tools_environment.go | 10 +- pkg/mcp/tools_keybinding.go | 2 +- pkg/mcp/tools_layout.go | 22 ++-- pkg/mcp/tools_lifecycle.go | 2 +- pkg/mcp/tools_notification.go | 8 +- pkg/mcp/tools_screen.go | 16 +-- pkg/mcp/tools_tray.go | 6 +- pkg/mcp/tools_webview.go | 32 +++--- pkg/mcp/tools_window.go | 12 +- pkg/notification/service.go | 6 +- pkg/notification/service_test.go | 2 +- pkg/systray/service.go | 2 +- pkg/webview/diagnostics.go | 15 +-- pkg/webview/service.go | 39 ++++--- pkg/webview/service_test.go | 2 +- pkg/window/layout.go | 19 ++-- pkg/window/service.go | 47 ++++---- pkg/window/service_test.go | 2 +- pkg/window/state.go | 17 +-- pkg/window/tiling.go | 12 +- pkg/window/window.go | 13 ++- 36 files changed, 324 insertions(+), 312 deletions(-) diff --git a/go.sum b/go.sum index f38e4fac..d4799104 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +dappco.re/go/core v0.3.3 h1:TaE7/SObQ3YEZj4ov1BAXWwLJEIttkrxzncVGewR3Bs= +dappco.re/go/core v0.3.3/go.mod h1:Cp4ac25pghvO2iqOu59t1GyngTKVOzKB5/VPdhRi9CQ= forge.lthn.ai/Snider/Borg v0.3.1/go.mod h1:Z7DJD0yHXsxSyM7Mjl6/g4gH1NBsIz44Bf5AFlV76Wg= forge.lthn.ai/core/config v0.1.8 h1:xP2hys7T94QGVF/OTh84/Zr5Dm/dL/0vzjht8zi+LOg= forge.lthn.ai/core/config v0.1.8/go.mod h1:8epZrkwoCt+5ayrqdinOUU/+w6UoxOyv9ZrdgVOgYfQ= diff --git a/pkg/contextmenu/messages.go b/pkg/contextmenu/messages.go index cb62e17a..c596a94b 100644 --- a/pkg/contextmenu/messages.go +++ b/pkg/contextmenu/messages.go @@ -1,11 +1,11 @@ // pkg/contextmenu/messages.go package contextmenu -import "errors" +import corego "dappco.re/go/core" // ErrMenuNotFound is returned when attempting to remove or get a menu // that does not exist in the registry. -var ErrMenuNotFound = errors.New("contextmenu: menu not found") +var ErrMenuNotFound = corego.NewError("contextmenu: menu not found") // --- Queries --- diff --git a/pkg/contextmenu/service.go b/pkg/contextmenu/service.go index 973346d2..8ff3a507 100644 --- a/pkg/contextmenu/service.go +++ b/pkg/contextmenu/service.go @@ -3,8 +3,8 @@ package contextmenu import ( "context" - "fmt" + corego "dappco.re/go/core" "forge.lthn.ai/core/go/pkg/core" ) @@ -92,7 +92,7 @@ func (s *Service) taskAdd(t TaskAdd) error { }) }) if err != nil { - return fmt.Errorf("contextmenu: platform add failed: %w", err) + return corego.Wrap(err, "contextmenu.add", "platform add failed") } s.menus[t.Name] = t.Menu @@ -106,7 +106,7 @@ func (s *Service) taskRemove(t TaskRemove) error { err := s.platform.Remove(t.Name) if err != nil { - return fmt.Errorf("contextmenu: platform remove failed: %w", err) + return corego.Wrap(err, "contextmenu.remove", "platform remove failed") } delete(s.menus, t.Name) diff --git a/pkg/display/display.go b/pkg/display/display.go index 75da55c2..0b8d5eaf 100644 --- a/pkg/display/display.go +++ b/pkg/display/display.go @@ -4,29 +4,27 @@ package display import ( "context" "encoding/base64" - "fmt" "os" - "path/filepath" "runtime" - "encoding/json" + corego "dappco.re/go/core" "forge.lthn.ai/core/config" "forge.lthn.ai/core/go/pkg/core" - "forge.lthn.ai/core/gui/pkg/browser" - "forge.lthn.ai/core/gui/pkg/clipboard" - "forge.lthn.ai/core/gui/pkg/contextmenu" - "forge.lthn.ai/core/gui/pkg/dialog" - "forge.lthn.ai/core/gui/pkg/dock" - "forge.lthn.ai/core/gui/pkg/environment" - "forge.lthn.ai/core/gui/pkg/keybinding" - "forge.lthn.ai/core/gui/pkg/lifecycle" - "forge.lthn.ai/core/gui/pkg/menu" - "forge.lthn.ai/core/gui/pkg/notification" - "forge.lthn.ai/core/gui/pkg/screen" - "forge.lthn.ai/core/gui/pkg/systray" - "forge.lthn.ai/core/gui/pkg/webview" - "forge.lthn.ai/core/gui/pkg/window" + "dappco.re/go/core/gui/pkg/browser" + "dappco.re/go/core/gui/pkg/clipboard" + "dappco.re/go/core/gui/pkg/contextmenu" + "dappco.re/go/core/gui/pkg/dialog" + "dappco.re/go/core/gui/pkg/dock" + "dappco.re/go/core/gui/pkg/environment" + "dappco.re/go/core/gui/pkg/keybinding" + "dappco.re/go/core/gui/pkg/lifecycle" + "dappco.re/go/core/gui/pkg/menu" + "dappco.re/go/core/gui/pkg/notification" + "dappco.re/go/core/gui/pkg/screen" + "dappco.re/go/core/gui/pkg/systray" + "dappco.re/go/core/gui/pkg/webview" + "dappco.re/go/core/gui/pkg/window" "github.com/wailsapp/wails/v3/pkg/application" ) @@ -259,7 +257,7 @@ type WSMessage struct { func wsRequire(data map[string]any, key string) (string, error) { v, _ := data[key].(string) if v == "" { - return "", fmt.Errorf("ws: missing required field %q", key) + return "", corego.NewError(corego.Sprintf("ws: missing required field %q", key)) } return v, nil } @@ -302,10 +300,11 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) { 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) + encodedR := corego.JSONMarshal(msg.Data) + if encodedR.OK { + _ = corego.JSONUnmarshal(encodedR.Value.([]byte), &opts) } + return nil, false, corego.Wrap(err, "display.ws", "ws: invalid window create options") info, createErr := s.CreateWindow(opts) if createErr != nil { return nil, false, createErr @@ -444,9 +443,11 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) { result, handled, err = s.Core().QUERY(dock.QueryVisible{}) case "contextmenu:add": name, _ := msg.Data["name"].(string) - menuJSON, _ := json.Marshal(msg.Data["menu"]) + menuR := corego.JSONMarshal(msg.Data["menu"]) var menuDef contextmenu.ContextMenuDef - _ = json.Unmarshal(menuJSON, &menuDef) + if menuR.OK { + _ = corego.JSONUnmarshal(menuR.Value.([]byte), &menuDef) + } result, handled, err = s.Core().PERFORM(contextmenu.TaskAdd{ Name: name, Menu: menuDef, }) @@ -768,7 +769,7 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) { } workflow, ok := window.ParseWorkflowLayout(workflowName) if !ok { - return nil, false, fmt.Errorf("ws: unknown workflow %q", workflowName) + return nil, false, corego.NewError(corego.Sprintf("ws: unknown workflow %q", workflowName)) } var names []string if raw, ok := msg.Data["windows"].([]any); ok { @@ -899,17 +900,19 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) { case "clipboard:write-image": data, ok := msg.Data["data"].(string) if !ok || data == "" { - return nil, false, fmt.Errorf("ws: missing required field %q", "data") + return nil, false, corego.NewError(corego.Sprintf("ws: missing required field %q", "data")) } decoded, decodeErr := base64.StdEncoding.DecodeString(data) if decodeErr != nil { - return nil, false, fmt.Errorf("ws: invalid base64 image data: %w", decodeErr) + return nil, false, corego.Wrap(decodeErr, "display.ws", "ws: invalid base64 image data") } result, handled, err = s.Core().PERFORM(clipboard.TaskSetImage{Data: decoded}) case "notification:show": var opts notification.NotificationOptions - encoded, _ := json.Marshal(msg.Data) - _ = json.Unmarshal(encoded, &opts) + encodedR := corego.JSONMarshal(msg.Data) + if encodedR.OK { + _ = corego.JSONUnmarshal(encodedR.Value.([]byte), &opts) + } result, handled, err = s.Core().PERFORM(notification.TaskSend{Opts: opts}) case "notification:info": title, e := wsRequire(msg.Data, "title") @@ -965,8 +968,10 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) { subtitle, _ := msg.Data["subtitle"].(string) actions := make([]notification.NotificationAction, 0) if raw, ok := msg.Data["actions"]; ok { - encoded, _ := json.Marshal(raw) - _ = json.Unmarshal(encoded, &actions) + encodedR := corego.JSONMarshal(raw) + if encodedR.OK { + _ = corego.JSONUnmarshal(encodedR.Value.([]byte), &actions) + } } result, handled, err = s.Core().PERFORM(notification.TaskSend{ Opts: notification.NotificationOptions{ @@ -1014,18 +1019,20 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) { } decoded, decodeErr := base64.StdEncoding.DecodeString(data) if decodeErr != nil { - return nil, false, fmt.Errorf("ws: invalid base64 tray icon data: %w", decodeErr) + return nil, false, corego.Wrap(decodeErr, "display.ws", "ws: invalid base64 tray icon data") } result, handled, err = s.Core().PERFORM(systray.TaskSetTrayIcon{Data: decoded}) case "tray:set-menu": raw, ok := msg.Data["items"] if !ok { - return nil, false, fmt.Errorf("ws: missing required field %q", "items") + return nil, false, corego.NewError(corego.Sprintf("ws: missing required field %q", "items")) } - encoded, _ := json.Marshal(raw) + encodedItemsR := corego.JSONMarshal(raw) var items []systray.TrayMenuItem - if err := json.Unmarshal(encoded, &items); err != nil { - return nil, false, fmt.Errorf("ws: invalid tray menu items: %w", err) + if encodedItemsR.OK { + if r := corego.JSONUnmarshal(encodedItemsR.Value.([]byte), &items); !r.OK { + return nil, false, corego.E("display.ws", "invalid tray menu items", nil) + } } result, handled, err = s.Core().PERFORM(systray.TaskSetTrayMenu{Items: items}) case "tray:info": @@ -1045,10 +1052,11 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) { result, handled, err = nil, true, s.SetTheme(isDark) case "dialog:open-file": var opts dialog.OpenFileOptions - encoded, _ := json.Marshal(msg.Data) - if err := json.Unmarshal(encoded, &opts); err != nil { - return nil, false, fmt.Errorf("ws: invalid open file options: %w", err) + encodedR := corego.JSONMarshal(msg.Data) + if encodedR.OK { + _ = corego.JSONUnmarshal(encodedR.Value.([]byte), &opts) } + return nil, false, corego.Wrap(err, "display.ws", "ws: invalid open file options") paths, openErr := s.OpenFileDialog(opts) if openErr != nil { return nil, false, openErr @@ -1056,10 +1064,11 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) { result, handled, err = paths, true, nil case "dialog:save-file": var opts dialog.SaveFileOptions - encoded, _ := json.Marshal(msg.Data) - if err := json.Unmarshal(encoded, &opts); err != nil { - return nil, false, fmt.Errorf("ws: invalid save file options: %w", err) + encodedR := corego.JSONMarshal(msg.Data) + if encodedR.OK { + _ = corego.JSONUnmarshal(encodedR.Value.([]byte), &opts) } + return nil, false, corego.Wrap(err, "display.ws", "ws: invalid save file options") path, saveErr := s.SaveFileDialog(opts) if saveErr != nil { return nil, false, saveErr @@ -1067,10 +1076,11 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) { result, handled, err = path, true, nil case "dialog:open-directory": var opts dialog.OpenDirectoryOptions - encoded, _ := json.Marshal(msg.Data) - if err := json.Unmarshal(encoded, &opts); err != nil { - return nil, false, fmt.Errorf("ws: invalid open directory options: %w", err) + encodedR := corego.JSONMarshal(msg.Data) + if encodedR.OK { + _ = corego.JSONUnmarshal(encodedR.Value.([]byte), &opts) } + return nil, false, corego.Wrap(err, "display.ws", "ws: invalid open directory options") path, dirErr := s.OpenDirectoryDialog(opts) if dirErr != nil { return nil, false, dirErr @@ -1135,7 +1145,7 @@ func (s *Service) handleTrayAction(actionID string) { result, handled, _ := s.Core().QUERY(environment.QueryInfo{}) if handled { info := result.(environment.EnvironmentInfo) - details := fmt.Sprintf("OS: %s\nArch: %s\nPlatform: %s %s", + details := corego.Sprintf("OS: %s\nArch: %s\nPlatform: %s %s", info.OS, info.Arch, info.Platform.Name, info.Platform.Version) _, _, _ = s.Core().PERFORM(dialog.TaskMessageDialog{ Opts: dialog.MessageDialogOptions{ @@ -1154,9 +1164,9 @@ func (s *Service) handleTrayAction(actionID string) { func guiConfigPath() string { home, err := os.UserHomeDir() if err != nil { - return filepath.Join(".core", "gui", "config.yaml") + return corego.JoinPath(".core", "gui", "config.yaml") } - return filepath.Join(home, ".core", "gui", "config.yaml") + return corego.JoinPath(home, ".core", "gui", "config.yaml") } func (s *Service) loadConfig() { @@ -1250,7 +1260,7 @@ func (s *Service) GetWindowInfo(name string) (*window.WindowInfo, error) { return nil, err } if !handled { - return nil, fmt.Errorf("window service not available") + return nil, corego.NewError(corego.Sprintf("window service not available")) } info, _ := result.(*window.WindowInfo) return info, nil @@ -1410,7 +1420,7 @@ func (s *Service) GetWindowTitle(name string) (string, error) { return "", err } if info == nil { - return "", fmt.Errorf("window not found: %s", name) + return "", corego.NewError(corego.Sprintf("window not found: %s", name)) } return info.Title, nil } @@ -1459,7 +1469,7 @@ type CreateWindowOptions struct { // Use: info, err := svc.CreateWindow(display.CreateWindowOptions{Name: "editor", URL: "/editor"}) func (s *Service) CreateWindow(opts CreateWindowOptions) (*window.WindowInfo, error) { if opts.Name == "" { - return nil, fmt.Errorf("window name is required") + return nil, corego.NewError(corego.Sprintf("window name is required")) } result, _, err := s.Core().PERFORM(window.TaskOpenWindow{ Window: &window.Window{ @@ -1488,7 +1498,7 @@ func (s *Service) CreateWindow(opts CreateWindowOptions) (*window.WindowInfo, er func (s *Service) SaveLayout(name string) error { ws := s.windowService() if ws == nil { - return fmt.Errorf("window service not available") + return corego.NewError(corego.Sprintf("window service not available")) } states := make(map[string]window.WindowState) for _, n := range ws.Manager().List() { @@ -1506,11 +1516,11 @@ func (s *Service) SaveLayout(name string) error { func (s *Service) RestoreLayout(name string) error { ws := s.windowService() if ws == nil { - return fmt.Errorf("window service not available") + return corego.NewError(corego.Sprintf("window service not available")) } layout, ok := ws.Manager().Layout().GetLayout(name) if !ok { - return fmt.Errorf("layout not found: %s", name) + return corego.NewError(corego.Sprintf("layout not found: %s", name)) } for wName, state := range layout.Windows { if pw, ok := ws.Manager().Get(wName); ok { @@ -1541,7 +1551,7 @@ func (s *Service) ListLayouts() []window.LayoutInfo { func (s *Service) DeleteLayout(name string) error { ws := s.windowService() if ws == nil { - return fmt.Errorf("window service not available") + return corego.NewError(corego.Sprintf("window service not available")) } ws.Manager().Layout().DeleteLayout(name) return nil @@ -1568,7 +1578,7 @@ func (s *Service) GetLayout(name string) *window.Layout { func (s *Service) TileWindows(mode window.TileMode, windowNames []string) error { ws := s.windowService() if ws == nil { - return fmt.Errorf("window service not available") + return corego.NewError(corego.Sprintf("window service not available")) } screenWidth, screenHeight := s.primaryScreenSize() return ws.Manager().TileWindows(mode, windowNames, screenWidth, screenHeight) @@ -1579,7 +1589,7 @@ func (s *Service) TileWindows(mode window.TileMode, windowNames []string) error func (s *Service) SnapWindow(name string, position window.SnapPosition) error { ws := s.windowService() if ws == nil { - return fmt.Errorf("window service not available") + return corego.NewError(corego.Sprintf("window service not available")) } screenWidth, screenHeight := s.primaryScreenSize() return ws.Manager().SnapWindow(name, position, screenWidth, screenHeight) @@ -1617,7 +1627,7 @@ func (s *Service) primaryScreenSize() (int, int) { func (s *Service) StackWindows(windowNames []string, offsetX, offsetY int) error { ws := s.windowService() if ws == nil { - return fmt.Errorf("window service not available") + return corego.NewError(corego.Sprintf("window service not available")) } return ws.Manager().StackWindows(windowNames, offsetX, offsetY) } @@ -1627,7 +1637,7 @@ func (s *Service) StackWindows(windowNames []string, offsetX, offsetY int) error func (s *Service) ApplyWorkflowLayout(workflow window.WorkflowLayout) error { ws := s.windowService() if ws == nil { - return fmt.Errorf("window service not available") + return corego.NewError(corego.Sprintf("window service not available")) } screenWidth, screenHeight := s.primaryScreenSize() return ws.Manager().ApplyWorkflow(workflow, ws.Manager().List(), screenWidth, screenHeight) @@ -1638,7 +1648,7 @@ func (s *Service) ApplyWorkflowLayout(workflow window.WorkflowLayout) error { func (s *Service) ArrangeWindowPair(first, second string) error { ws := s.windowService() if ws == nil { - return fmt.Errorf("window service not available") + return corego.NewError(corego.Sprintf("window service not available")) } screenWidth, screenHeight := s.primaryScreenSize() return ws.Manager().ArrangePair(first, second, screenWidth, screenHeight) @@ -1649,7 +1659,7 @@ func (s *Service) ArrangeWindowPair(first, second string) error { func (s *Service) FindSpace(width, height int) (window.SpaceInfo, error) { ws := s.windowService() if ws == nil { - return window.SpaceInfo{}, fmt.Errorf("window service not available") + return window.SpaceInfo{}, corego.NewError(corego.Sprintf("window service not available")) } screenWidth, screenHeight := s.primaryScreenSize() if width <= 0 { @@ -1673,7 +1683,7 @@ func (s *Service) SuggestLayout(windowCount, screenWidth, screenHeight int) (win return window.LayoutSuggestion{}, err } if !handled { - return window.LayoutSuggestion{}, fmt.Errorf("window service not available") + return window.LayoutSuggestion{}, corego.NewError(corego.Sprintf("window service not available")) } suggestion, _ := result.(window.LayoutSuggestion) return suggestion, nil @@ -1710,7 +1720,7 @@ func (s *Service) GetScreen(id string) (*screen.Screen, error) { return nil, err } if !handled { - return nil, fmt.Errorf("screen service not available") + return nil, corego.NewError(corego.Sprintf("screen service not available")) } scr, _ := result.(*screen.Screen) return scr, nil @@ -1724,7 +1734,7 @@ func (s *Service) GetPrimaryScreen() (*screen.Screen, error) { return nil, err } if !handled { - return nil, fmt.Errorf("screen service not available") + return nil, corego.NewError(corego.Sprintf("screen service not available")) } scr, _ := result.(*screen.Screen) return scr, nil @@ -1738,7 +1748,7 @@ func (s *Service) GetScreenAtPoint(x, y int) (*screen.Screen, error) { return nil, err } if !handled { - return nil, fmt.Errorf("screen service not available") + return nil, corego.NewError(corego.Sprintf("screen service not available")) } scr, _ := result.(*screen.Screen) return scr, nil @@ -1923,7 +1933,7 @@ func (s *Service) RequestNotificationPermission() (bool, error) { return false, err } if !handled { - return false, fmt.Errorf("notification service not available") + return false, corego.NewError(corego.Sprintf("notification service not available")) } granted, _ := result.(bool) return granted, nil @@ -1937,7 +1947,7 @@ func (s *Service) CheckNotificationPermission() (bool, error) { return false, err } if !handled { - return false, fmt.Errorf("notification service not available") + return false, corego.NewError(corego.Sprintf("notification service not available")) } status, _ := result.(notification.PermissionStatus) return status.Granted, nil @@ -1951,7 +1961,7 @@ func (s *Service) ClearNotifications() error { return err } if !handled { - return fmt.Errorf("notification service not available") + return corego.NewError(corego.Sprintf("notification service not available")) } return nil } @@ -1966,7 +1976,7 @@ func (s *Service) OpenFileDialog(opts dialog.OpenFileOptions) ([]string, error) return nil, err } if !handled { - return nil, fmt.Errorf("dialog service not available") + return nil, corego.NewError(corego.Sprintf("dialog service not available")) } paths, _ := result.([]string) return paths, nil @@ -1993,7 +2003,7 @@ func (s *Service) SaveFileDialog(opts dialog.SaveFileOptions) (string, error) { return "", err } if !handled { - return "", fmt.Errorf("dialog service not available") + return "", corego.NewError(corego.Sprintf("dialog service not available")) } path, _ := result.(string) return path, nil @@ -2007,7 +2017,7 @@ func (s *Service) OpenDirectoryDialog(opts dialog.OpenDirectoryOptions) (string, return "", err } if !handled { - return "", fmt.Errorf("dialog service not available") + return "", corego.NewError(corego.Sprintf("dialog service not available")) } path, _ := result.(string) return path, nil @@ -2028,7 +2038,7 @@ func (s *Service) ConfirmDialog(title, message string) (bool, error) { return false, err } if !handled { - return false, fmt.Errorf("dialog service not available") + return false, corego.NewError(corego.Sprintf("dialog service not available")) } button, _ := result.(string) return button == "Yes" || button == "OK", nil @@ -2060,7 +2070,7 @@ func (s *Service) PromptDialog(title, message string) (string, bool, error) { return "", false, err } if !handled { - return "", false, fmt.Errorf("dialog service not available") + return "", false, corego.NewError(corego.Sprintf("dialog service not available")) } button, _ := result.(string) return button, button == "OK", nil @@ -2075,27 +2085,27 @@ func (s *Service) promptViaWebView(title, message string) (string, bool, error) } } if windowName == "" { - return "", false, fmt.Errorf("no webview window available") + return "", false, corego.NewError(corego.Sprintf("no webview window available")) } - encodedTitle, err := json.Marshal(title) - if err != nil { - return "", false, err + encodedTitleR := corego.JSONMarshal(title) + if !encodedTitleR.OK { + return "", false, corego.E("display.showDialog", "failed to marshal title", nil) } - encodedMessage, err := json.Marshal(message) - if err != nil { - return "", false, err + encodedMessageR := corego.JSONMarshal(message) + if !encodedMessageR.OK { + return "", false, corego.E("display.showDialog", "failed to marshal message", nil) } result, handled, err := s.Core().PERFORM(webview.TaskEvaluate{ Window: windowName, - Script: "window.prompt(" + string(encodedTitle) + "," + string(encodedMessage) + ")", + Script: "window.prompt(" + string(encodedTitleR.Value.([]byte)) + "," + string(encodedMessageR.Value.([]byte)) + ")", }) if err != nil { return "", false, err } if !handled { - return "", false, fmt.Errorf("webview service not available") + return "", false, corego.NewError(corego.Sprintf("webview service not available")) } if result == nil { return "", false, nil @@ -2103,7 +2113,7 @@ func (s *Service) promptViaWebView(title, message string) (string, bool, error) if text, ok := result.(string); ok { return text, true, nil } - return fmt.Sprint(result), true, nil + return corego.Sprint(result), true, nil } // DialogMessage shows an informational, warning, or error message via the notification pipeline. @@ -2178,7 +2188,7 @@ func (s *Service) SetThemeMode(theme string) error { return err } if !handled { - return fmt.Errorf("environment service not available") + return corego.NewError(corego.Sprintf("environment service not available")) } return nil } @@ -2193,7 +2203,7 @@ func (s *Service) SetTrayIcon(data []byte) error { return err } if !handled { - return fmt.Errorf("systray service not available") + return corego.NewError(corego.Sprintf("systray service not available")) } return nil } @@ -2206,7 +2216,7 @@ func (s *Service) SetTrayTooltip(tooltip string) error { return err } if !handled { - return fmt.Errorf("systray service not available") + return corego.NewError(corego.Sprintf("systray service not available")) } return nil } @@ -2219,7 +2229,7 @@ func (s *Service) SetTrayLabel(label string) error { return err } if !handled { - return fmt.Errorf("systray service not available") + return corego.NewError(corego.Sprintf("systray service not available")) } return nil } @@ -2232,7 +2242,7 @@ func (s *Service) SetTrayMenu(items []systray.TrayMenuItem) error { return err } if !handled { - return fmt.Errorf("systray service not available") + return corego.NewError(corego.Sprintf("systray service not available")) } return nil } @@ -2255,7 +2265,7 @@ func (s *Service) ShowTrayMessage(title, message string) error { return err } if !handled { - return fmt.Errorf("systray service not available") + return corego.NewError(corego.Sprintf("systray service not available")) } return nil } diff --git a/pkg/display/display_test.go b/pkg/display/display_test.go index 84c8b693..384f345a 100644 --- a/pkg/display/display_test.go +++ b/pkg/display/display_test.go @@ -8,15 +8,15 @@ import ( "testing" "forge.lthn.ai/core/go/pkg/core" - "forge.lthn.ai/core/gui/pkg/clipboard" - "forge.lthn.ai/core/gui/pkg/dialog" - "forge.lthn.ai/core/gui/pkg/environment" - "forge.lthn.ai/core/gui/pkg/menu" - "forge.lthn.ai/core/gui/pkg/notification" - "forge.lthn.ai/core/gui/pkg/screen" - "forge.lthn.ai/core/gui/pkg/systray" - "forge.lthn.ai/core/gui/pkg/webview" - "forge.lthn.ai/core/gui/pkg/window" + "dappco.re/go/core/gui/pkg/clipboard" + "dappco.re/go/core/gui/pkg/dialog" + "dappco.re/go/core/gui/pkg/environment" + "dappco.re/go/core/gui/pkg/menu" + "dappco.re/go/core/gui/pkg/notification" + "dappco.re/go/core/gui/pkg/screen" + "dappco.re/go/core/gui/pkg/systray" + "dappco.re/go/core/gui/pkg/webview" + "dappco.re/go/core/gui/pkg/window" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/display/events.go b/pkg/display/events.go index bc5ba8e6..9c468a82 100644 --- a/pkg/display/events.go +++ b/pkg/display/events.go @@ -2,13 +2,12 @@ package display import ( - "encoding/json" - "fmt" "net/http" "sync" "time" - "forge.lthn.ai/core/gui/pkg/window" + "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/window" "github.com/gorilla/websocket" ) @@ -144,13 +143,13 @@ func (em *WSEventManager) sendEvent(conn *websocket.Conn, event Event) { return } - data, err := json.Marshal(event) - if err != nil { + r := core.JSONMarshal(event) + if !r.OK { return } conn.SetWriteDeadline(time.Now().Add(10 * time.Second)) - if err := conn.WriteMessage(websocket.TextMessage, data); err != nil { + if err := conn.WriteMessage(websocket.TextMessage, r.Value.([]byte)); err != nil { em.removeClient(conn) } } @@ -188,7 +187,7 @@ func (em *WSEventManager) handleMessages(conn *websocket.Conn) { EventTypes []EventType `json:"eventTypes,omitempty"` } - if err := json.Unmarshal(message, &msg); err != nil { + if r := core.JSONUnmarshal(message, &msg); !r.OK { continue } @@ -217,7 +216,7 @@ func (em *WSEventManager) subscribe(conn *websocket.Conn, id string, eventTypes if id == "" { em.mu.Lock() em.nextSubID++ - id = fmt.Sprintf("sub-%d", em.nextSubID) + id = core.Sprintf("sub-%d", em.nextSubID) em.mu.Unlock() } @@ -234,8 +233,10 @@ func (em *WSEventManager) subscribe(conn *websocket.Conn, id string, eventTypes "id": id, "eventTypes": eventTypes, } - data, _ := json.Marshal(response) - conn.WriteMessage(websocket.TextMessage, data) + r := core.JSONMarshal(response) + if r.OK { + conn.WriteMessage(websocket.TextMessage, r.Value.([]byte)) + } } // unsubscribe removes a subscription for a client. @@ -257,8 +258,10 @@ func (em *WSEventManager) unsubscribe(conn *websocket.Conn, id string) { "type": "unsubscribed", "id": id, } - data, _ := json.Marshal(response) - conn.WriteMessage(websocket.TextMessage, data) + r := core.JSONMarshal(response) + if r.OK { + conn.WriteMessage(websocket.TextMessage, r.Value.([]byte)) + } } // listSubscriptions sends a list of active subscriptions to a client. @@ -282,8 +285,10 @@ func (em *WSEventManager) listSubscriptions(conn *websocket.Conn) { "type": "subscriptions", "subscriptions": subs, } - data, _ := json.Marshal(response) - conn.WriteMessage(websocket.TextMessage, data) + r := core.JSONMarshal(response) + if r.OK { + conn.WriteMessage(websocket.TextMessage, r.Value.([]byte)) + } } // removeClient removes a client and its subscriptions. diff --git a/pkg/environment/service.go b/pkg/environment/service.go index bfb162b6..9dfd2812 100644 --- a/pkg/environment/service.go +++ b/pkg/environment/service.go @@ -3,8 +3,8 @@ package environment import ( "context" - "fmt" + corego "dappco.re/go/core" "forge.lthn.ai/core/go/pkg/core" ) @@ -104,7 +104,7 @@ func (s *Service) taskSetTheme(task TaskSetTheme) error { s.overrideDark = &isDark shouldApplyTheme = true default: - return fmt.Errorf("invalid theme mode: %s", task.Theme) + return corego.E("environment.setTheme", corego.Sprintf("invalid theme mode: %s", task.Theme), nil) } if shouldApplyTheme { diff --git a/pkg/keybinding/messages.go b/pkg/keybinding/messages.go index 7f037f3f..1bfc2c5c 100644 --- a/pkg/keybinding/messages.go +++ b/pkg/keybinding/messages.go @@ -1,11 +1,11 @@ // pkg/keybinding/messages.go package keybinding -import "errors" +import corego "dappco.re/go/core" // ErrAlreadyRegistered is returned when attempting to add a binding // that already exists. Callers must TaskRemove first to rebind. -var ErrAlreadyRegistered = errors.New("keybinding: accelerator already registered") +var ErrAlreadyRegistered = corego.NewError("keybinding: accelerator already registered") // BindingInfo describes a registered keyboard shortcut. type BindingInfo struct { diff --git a/pkg/keybinding/service.go b/pkg/keybinding/service.go index 048c259d..44fb4d32 100644 --- a/pkg/keybinding/service.go +++ b/pkg/keybinding/service.go @@ -3,8 +3,8 @@ package keybinding import ( "context" - "fmt" + corego "dappco.re/go/core" "forge.lthn.ai/core/go/pkg/core" ) @@ -75,7 +75,7 @@ func (s *Service) taskAdd(t TaskAdd) error { _ = s.Core().ACTION(ActionTriggered{Accelerator: t.Accelerator}) }) if err != nil { - return fmt.Errorf("keybinding: platform add failed: %w", err) + return corego.Wrap(err, "keybinding.add", "platform add failed") } s.bindings[t.Accelerator] = BindingInfo{ @@ -87,12 +87,12 @@ func (s *Service) taskAdd(t TaskAdd) error { func (s *Service) taskRemove(t TaskRemove) error { if _, exists := s.bindings[t.Accelerator]; !exists { - return fmt.Errorf("keybinding: not registered: %s", t.Accelerator) + return corego.E("keybinding.remove", corego.Sprintf("not registered: %s", t.Accelerator), nil) } err := s.platform.Remove(t.Accelerator) if err != nil { - return fmt.Errorf("keybinding: platform remove failed: %w", err) + return corego.Wrap(err, "keybinding.remove", "platform remove failed") } delete(s.bindings, t.Accelerator) diff --git a/pkg/mcp/mcp_test.go b/pkg/mcp/mcp_test.go index 0bf04324..5862d188 100644 --- a/pkg/mcp/mcp_test.go +++ b/pkg/mcp/mcp_test.go @@ -6,13 +6,13 @@ import ( "testing" "forge.lthn.ai/core/go/pkg/core" - "forge.lthn.ai/core/gui/pkg/clipboard" - "forge.lthn.ai/core/gui/pkg/display" - "forge.lthn.ai/core/gui/pkg/environment" - "forge.lthn.ai/core/gui/pkg/notification" - "forge.lthn.ai/core/gui/pkg/screen" - "forge.lthn.ai/core/gui/pkg/webview" - "forge.lthn.ai/core/gui/pkg/window" + "dappco.re/go/core/gui/pkg/clipboard" + "dappco.re/go/core/gui/pkg/display" + "dappco.re/go/core/gui/pkg/environment" + "dappco.re/go/core/gui/pkg/notification" + "dappco.re/go/core/gui/pkg/screen" + "dappco.re/go/core/gui/pkg/webview" + "dappco.re/go/core/gui/pkg/window" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/mcp/tools_browser.go b/pkg/mcp/tools_browser.go index 18f670c0..a14803ed 100644 --- a/pkg/mcp/tools_browser.go +++ b/pkg/mcp/tools_browser.go @@ -4,7 +4,7 @@ package mcp import ( "context" - "forge.lthn.ai/core/gui/pkg/browser" + "dappco.re/go/core/gui/pkg/browser" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/mcp/tools_clipboard.go b/pkg/mcp/tools_clipboard.go index 01e4b58f..f5732bbf 100644 --- a/pkg/mcp/tools_clipboard.go +++ b/pkg/mcp/tools_clipboard.go @@ -3,9 +3,9 @@ package mcp import ( "context" - "fmt" - "forge.lthn.ai/core/gui/pkg/clipboard" + corego "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/clipboard" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -23,7 +23,7 @@ func (s *Subsystem) clipboardRead(_ context.Context, _ *mcp.CallToolRequest, _ C } content, ok := result.(clipboard.ClipboardContent) if !ok { - return nil, ClipboardReadOutput{}, fmt.Errorf("unexpected result type from clipboard read query") + return nil, ClipboardReadOutput{}, corego.E("mcp.clipboard", "unexpected result type from clipboard read query", nil) } return nil, ClipboardReadOutput{Content: content.Text}, nil } @@ -44,7 +44,7 @@ func (s *Subsystem) clipboardWrite(_ context.Context, _ *mcp.CallToolRequest, in } success, ok := result.(bool) if !ok { - return nil, ClipboardWriteOutput{}, fmt.Errorf("unexpected result type from clipboard write task") + return nil, ClipboardWriteOutput{}, corego.E("mcp.clipboard", "unexpected result type from clipboard write task", nil) } return nil, ClipboardWriteOutput{Success: success}, nil } @@ -63,7 +63,7 @@ func (s *Subsystem) clipboardHas(_ context.Context, _ *mcp.CallToolRequest, _ Cl } content, ok := result.(clipboard.ClipboardContent) if !ok { - return nil, ClipboardHasOutput{}, fmt.Errorf("unexpected result type from clipboard has query") + return nil, ClipboardHasOutput{}, corego.E("mcp.clipboard", "unexpected result type from clipboard has query", nil) } return nil, ClipboardHasOutput{HasContent: content.HasContent}, nil } @@ -82,7 +82,7 @@ func (s *Subsystem) clipboardClear(_ context.Context, _ *mcp.CallToolRequest, _ } success, ok := result.(bool) if !ok { - return nil, ClipboardClearOutput{}, fmt.Errorf("unexpected result type from clipboard clear task") + return nil, ClipboardClearOutput{}, corego.E("mcp.clipboard", "unexpected result type from clipboard clear task", nil) } return nil, ClipboardClearOutput{Success: success}, nil } @@ -101,7 +101,7 @@ func (s *Subsystem) clipboardReadImage(_ context.Context, _ *mcp.CallToolRequest } image, ok := result.(clipboard.ClipboardImageContent) if !ok { - return nil, ClipboardReadImageOutput{}, fmt.Errorf("unexpected result type from clipboard image query") + return nil, ClipboardReadImageOutput{}, corego.E("mcp.clipboard", "unexpected result type from clipboard image query", nil) } return nil, ClipboardReadImageOutput{Image: image}, nil } diff --git a/pkg/mcp/tools_contextmenu.go b/pkg/mcp/tools_contextmenu.go index 74e9f8b5..c1112459 100644 --- a/pkg/mcp/tools_contextmenu.go +++ b/pkg/mcp/tools_contextmenu.go @@ -3,10 +3,9 @@ package mcp import ( "context" - "encoding/json" - "fmt" - "forge.lthn.ai/core/gui/pkg/contextmenu" + corego "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/contextmenu" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -25,13 +24,13 @@ type ContextMenuAddOutput struct { func (s *Subsystem) contextMenuAdd(_ context.Context, _ *mcp.CallToolRequest, input ContextMenuAddInput) (*mcp.CallToolResult, ContextMenuAddOutput, error) { // Convert map[string]any to ContextMenuDef via JSON round-trip - menuJSON, err := json.Marshal(input.Menu) - if err != nil { - return nil, ContextMenuAddOutput{}, fmt.Errorf("failed to marshal menu definition: %w", err) + r := corego.JSONMarshal(input.Menu) + if !r.OK { + return nil, ContextMenuAddOutput{}, corego.Wrap(r.Value.(error), "mcp.contextmenu", "failed to marshal menu definition") } var menuDef contextmenu.ContextMenuDef - if err := json.Unmarshal(menuJSON, &menuDef); err != nil { - return nil, ContextMenuAddOutput{}, fmt.Errorf("failed to unmarshal menu definition: %w", err) + if r2 := corego.JSONUnmarshal(r.Value.([]byte), &menuDef); !r2.OK { + return nil, ContextMenuAddOutput{}, corego.Wrap(r2.Value.(error), "mcp.contextmenu", "failed to unmarshal menu definition") } _, _, err = s.core.PERFORM(contextmenu.TaskAdd{Name: input.Name, Menu: menuDef}) if err != nil { @@ -73,19 +72,19 @@ func (s *Subsystem) contextMenuGet(_ context.Context, _ *mcp.CallToolRequest, in } menu, ok := result.(*contextmenu.ContextMenuDef) if !ok { - return nil, ContextMenuGetOutput{}, fmt.Errorf("unexpected result type from context menu get query") + return nil, ContextMenuGetOutput{}, corego.E("mcp.contextmenu", "unexpected result type from context menu get query", nil) } if menu == nil { return nil, ContextMenuGetOutput{}, nil } // Convert to map[string]any via JSON round-trip to avoid cyclic type in schema - menuJSON, err := json.Marshal(menu) - if err != nil { - return nil, ContextMenuGetOutput{}, fmt.Errorf("failed to marshal context menu: %w", err) + r := corego.JSONMarshal(menu) + if !r.OK { + return nil, ContextMenuGetOutput{}, corego.Wrap(r.Value.(error), "mcp.contextmenu", "failed to marshal context menu") } var menuMap map[string]any - if err := json.Unmarshal(menuJSON, &menuMap); err != nil { - return nil, ContextMenuGetOutput{}, fmt.Errorf("failed to unmarshal context menu: %w", err) + if r2 := corego.JSONUnmarshal(r.Value.([]byte), &menuMap); !r2.OK { + return nil, ContextMenuGetOutput{}, corego.Wrap(r2.Value.(error), "mcp.contextmenu", "failed to unmarshal context menu") } return nil, ContextMenuGetOutput{Menu: menuMap}, nil } @@ -104,16 +103,16 @@ func (s *Subsystem) contextMenuList(_ context.Context, _ *mcp.CallToolRequest, _ } menus, ok := result.(map[string]contextmenu.ContextMenuDef) if !ok { - return nil, ContextMenuListOutput{}, fmt.Errorf("unexpected result type from context menu list query") + return nil, ContextMenuListOutput{}, corego.E("mcp.contextmenu", "unexpected result type from context menu list query", nil) } // Convert to map[string]any via JSON round-trip to avoid cyclic type in schema - menusJSON, err := json.Marshal(menus) - if err != nil { - return nil, ContextMenuListOutput{}, fmt.Errorf("failed to marshal context menus: %w", err) + r := corego.JSONMarshal(menus) + if !r.OK { + return nil, ContextMenuListOutput{}, corego.Wrap(r.Value.(error), "mcp.contextmenu", "failed to marshal context menus") } var menusMap map[string]any - if err := json.Unmarshal(menusJSON, &menusMap); err != nil { - return nil, ContextMenuListOutput{}, fmt.Errorf("failed to unmarshal context menus: %w", err) + if r2 := corego.JSONUnmarshal(r.Value.([]byte), &menusMap); !r2.OK { + return nil, ContextMenuListOutput{}, corego.Wrap(r2.Value.(error), "mcp.contextmenu", "failed to unmarshal context menus") } return nil, ContextMenuListOutput{Menus: menusMap}, nil } diff --git a/pkg/mcp/tools_dialog.go b/pkg/mcp/tools_dialog.go index 06bdf668..a4d80b39 100644 --- a/pkg/mcp/tools_dialog.go +++ b/pkg/mcp/tools_dialog.go @@ -3,9 +3,9 @@ package mcp import ( "context" - "fmt" - "forge.lthn.ai/core/gui/pkg/dialog" + corego "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/dialog" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -33,7 +33,7 @@ func (s *Subsystem) dialogOpenFile(_ context.Context, _ *mcp.CallToolRequest, in } paths, ok := result.([]string) if !ok { - return nil, DialogOpenFileOutput{}, fmt.Errorf("unexpected result type from open file dialog") + return nil, DialogOpenFileOutput{}, corego.E("mcp.dialog", "unexpected result type from open file dialog", nil) } return nil, DialogOpenFileOutput{Paths: paths}, nil } @@ -62,7 +62,7 @@ func (s *Subsystem) dialogSaveFile(_ context.Context, _ *mcp.CallToolRequest, in } path, ok := result.(string) if !ok { - return nil, DialogSaveFileOutput{}, fmt.Errorf("unexpected result type from save file dialog") + return nil, DialogSaveFileOutput{}, corego.E("mcp.dialog", "unexpected result type from save file dialog", nil) } return nil, DialogSaveFileOutput{Path: path}, nil } @@ -87,7 +87,7 @@ func (s *Subsystem) dialogOpenDirectory(_ context.Context, _ *mcp.CallToolReques } path, ok := result.(string) if !ok { - return nil, DialogOpenDirectoryOutput{}, fmt.Errorf("unexpected result type from open directory dialog") + return nil, DialogOpenDirectoryOutput{}, corego.E("mcp.dialog", "unexpected result type from open directory dialog", nil) } return nil, DialogOpenDirectoryOutput{Path: path}, nil } @@ -115,7 +115,7 @@ func (s *Subsystem) dialogConfirm(_ context.Context, _ *mcp.CallToolRequest, inp } button, ok := result.(string) if !ok { - return nil, DialogConfirmOutput{}, fmt.Errorf("unexpected result type from confirm dialog") + return nil, DialogConfirmOutput{}, corego.E("mcp.dialog", "unexpected result type from confirm dialog", nil) } return nil, DialogConfirmOutput{Button: button}, nil } @@ -142,7 +142,7 @@ func (s *Subsystem) dialogPrompt(_ context.Context, _ *mcp.CallToolRequest, inpu } button, ok := result.(string) if !ok { - return nil, DialogPromptOutput{}, fmt.Errorf("unexpected result type from prompt dialog") + return nil, DialogPromptOutput{}, corego.E("mcp.dialog", "unexpected result type from prompt dialog", nil) } return nil, DialogPromptOutput{Button: button}, nil } diff --git a/pkg/mcp/tools_dock.go b/pkg/mcp/tools_dock.go index bff74fce..ef5c9c00 100644 --- a/pkg/mcp/tools_dock.go +++ b/pkg/mcp/tools_dock.go @@ -4,7 +4,7 @@ package mcp import ( "context" - "forge.lthn.ai/core/gui/pkg/dock" + "dappco.re/go/core/gui/pkg/dock" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/mcp/tools_environment.go b/pkg/mcp/tools_environment.go index 8853f473..09b7fcea 100644 --- a/pkg/mcp/tools_environment.go +++ b/pkg/mcp/tools_environment.go @@ -3,9 +3,9 @@ package mcp import ( "context" - "fmt" - "forge.lthn.ai/core/gui/pkg/environment" + corego "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/environment" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -23,7 +23,7 @@ func (s *Subsystem) themeGet(_ context.Context, _ *mcp.CallToolRequest, _ ThemeG } theme, ok := result.(environment.ThemeInfo) if !ok { - return nil, ThemeGetOutput{}, fmt.Errorf("unexpected result type from theme query") + return nil, ThemeGetOutput{}, corego.E("mcp.environment", "unexpected result type from theme query", nil) } return nil, ThemeGetOutput{Theme: theme}, nil } @@ -42,7 +42,7 @@ func (s *Subsystem) themeSystem(_ context.Context, _ *mcp.CallToolRequest, _ The } info, ok := result.(environment.EnvironmentInfo) if !ok { - return nil, ThemeSystemOutput{}, fmt.Errorf("unexpected result type from environment info query") + return nil, ThemeSystemOutput{}, corego.E("mcp.environment", "unexpected result type from environment info query", nil) } return nil, ThemeSystemOutput{Info: info}, nil } @@ -67,7 +67,7 @@ func (s *Subsystem) themeSet(_ context.Context, _ *mcp.CallToolRequest, input Th } theme, ok := result.(environment.ThemeInfo) if !ok { - return nil, ThemeSetOutput{}, fmt.Errorf("unexpected result type from theme query") + return nil, ThemeSetOutput{}, corego.E("mcp.environment", "unexpected result type from theme query", nil) } return nil, ThemeSetOutput{Theme: theme}, nil } diff --git a/pkg/mcp/tools_keybinding.go b/pkg/mcp/tools_keybinding.go index f8a5bf62..3ab84d85 100644 --- a/pkg/mcp/tools_keybinding.go +++ b/pkg/mcp/tools_keybinding.go @@ -4,7 +4,7 @@ package mcp import ( "context" - "forge.lthn.ai/core/gui/pkg/keybinding" + "dappco.re/go/core/gui/pkg/keybinding" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/mcp/tools_layout.go b/pkg/mcp/tools_layout.go index d53de75d..7942fd5b 100644 --- a/pkg/mcp/tools_layout.go +++ b/pkg/mcp/tools_layout.go @@ -3,11 +3,11 @@ package mcp import ( "context" - "fmt" + corego "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/screen" + "dappco.re/go/core/gui/pkg/window" "forge.lthn.ai/core/go/pkg/core" - "forge.lthn.ai/core/gui/pkg/screen" - "forge.lthn.ai/core/gui/pkg/window" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -59,7 +59,7 @@ func (s *Subsystem) layoutList(_ context.Context, _ *mcp.CallToolRequest, _ Layo } layouts, ok := result.([]window.LayoutInfo) if !ok { - return nil, LayoutListOutput{}, fmt.Errorf("unexpected result type from layout list query") + return nil, LayoutListOutput{}, corego.E("mcp.layout", "unexpected result type from layout list query", nil) } return nil, LayoutListOutput{Layouts: layouts}, nil } @@ -97,7 +97,7 @@ func (s *Subsystem) layoutGet(_ context.Context, _ *mcp.CallToolRequest, input L } layout, ok := result.(*window.Layout) if !ok { - return nil, LayoutGetOutput{}, fmt.Errorf("unexpected result type from layout get query") + return nil, LayoutGetOutput{}, corego.E("mcp.layout", "unexpected result type from layout get query", nil) } return nil, LayoutGetOutput{Layout: layout}, nil } @@ -176,7 +176,7 @@ func (s *Subsystem) layoutSuggest(_ context.Context, _ *mcp.CallToolRequest, inp } windows, ok := result.([]window.WindowInfo) if !ok { - return nil, LayoutSuggestOutput{}, fmt.Errorf("unexpected result type from window list query") + return nil, LayoutSuggestOutput{}, corego.E("mcp.layout", "unexpected result type from window list query", nil) } windowCount = len(windows) } @@ -193,11 +193,11 @@ func (s *Subsystem) layoutSuggest(_ context.Context, _ *mcp.CallToolRequest, inp return nil, LayoutSuggestOutput{}, err } if !handled { - return nil, LayoutSuggestOutput{}, fmt.Errorf("window service not available") + return nil, LayoutSuggestOutput{}, corego.E("mcp.layout", "window service not available", nil) } suggestion, ok := result.(window.LayoutSuggestion) if !ok { - return nil, LayoutSuggestOutput{}, fmt.Errorf("unexpected result type from layout suggestion query") + return nil, LayoutSuggestOutput{}, corego.E("mcp.layout", "unexpected result type from layout suggestion query", nil) } return nil, LayoutSuggestOutput{Suggestion: suggestion}, nil } @@ -227,11 +227,11 @@ func (s *Subsystem) screenFindSpace(_ context.Context, _ *mcp.CallToolRequest, i return nil, ScreenFindSpaceOutput{}, err } if !handled { - return nil, ScreenFindSpaceOutput{}, fmt.Errorf("window service not available") + return nil, ScreenFindSpaceOutput{}, corego.E("mcp.layout", "window service not available", nil) } space, ok := result.(window.SpaceInfo) if !ok { - return nil, ScreenFindSpaceOutput{}, fmt.Errorf("unexpected result type from find space query") + return nil, ScreenFindSpaceOutput{}, corego.E("mcp.layout", "unexpected result type from find space query", nil) } if space.ScreenWidth == 0 { space.ScreenWidth = screenW @@ -296,7 +296,7 @@ type LayoutWorkflowOutput struct { func (s *Subsystem) layoutWorkflow(_ context.Context, _ *mcp.CallToolRequest, input LayoutWorkflowInput) (*mcp.CallToolResult, LayoutWorkflowOutput, error) { workflow, ok := window.ParseWorkflowLayout(input.Workflow) if !ok { - return nil, LayoutWorkflowOutput{}, fmt.Errorf("unknown workflow: %s", input.Workflow) + return nil, LayoutWorkflowOutput{}, corego.E("mcp.layout", corego.Sprintf("unknown workflow: %s", input.Workflow), nil) } _, _, err := s.core.PERFORM(window.TaskApplyWorkflow{ Workflow: workflow, diff --git a/pkg/mcp/tools_lifecycle.go b/pkg/mcp/tools_lifecycle.go index 26715bdb..43dc62cb 100644 --- a/pkg/mcp/tools_lifecycle.go +++ b/pkg/mcp/tools_lifecycle.go @@ -4,7 +4,7 @@ package mcp import ( "context" - "forge.lthn.ai/core/gui/pkg/lifecycle" + "dappco.re/go/core/gui/pkg/lifecycle" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/mcp/tools_notification.go b/pkg/mcp/tools_notification.go index 5683efe6..f6c32c75 100644 --- a/pkg/mcp/tools_notification.go +++ b/pkg/mcp/tools_notification.go @@ -3,9 +3,9 @@ package mcp import ( "context" - "fmt" - "forge.lthn.ai/core/gui/pkg/notification" + corego "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/notification" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -71,7 +71,7 @@ func (s *Subsystem) notificationPermissionRequest(_ context.Context, _ *mcp.Call } granted, ok := result.(bool) if !ok { - return nil, NotificationPermissionRequestOutput{}, fmt.Errorf("unexpected result type from notification permission request") + return nil, NotificationPermissionRequestOutput{}, corego.E("mcp.notification", "unexpected result type from notification permission request", nil) } return nil, NotificationPermissionRequestOutput{Granted: granted}, nil } @@ -90,7 +90,7 @@ func (s *Subsystem) notificationPermissionCheck(_ context.Context, _ *mcp.CallTo } status, ok := result.(notification.PermissionStatus) if !ok { - return nil, NotificationPermissionCheckOutput{}, fmt.Errorf("unexpected result type from notification permission check") + return nil, NotificationPermissionCheckOutput{}, corego.E("mcp.notification", "unexpected result type from notification permission check", nil) } return nil, NotificationPermissionCheckOutput{Granted: status.Granted}, nil } diff --git a/pkg/mcp/tools_screen.go b/pkg/mcp/tools_screen.go index d8a06f73..5d076bf8 100644 --- a/pkg/mcp/tools_screen.go +++ b/pkg/mcp/tools_screen.go @@ -3,11 +3,11 @@ package mcp import ( "context" - "fmt" + corego "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/display" + "dappco.re/go/core/gui/pkg/screen" "forge.lthn.ai/core/go/pkg/core" - "forge.lthn.ai/core/gui/pkg/display" - "forge.lthn.ai/core/gui/pkg/screen" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -25,7 +25,7 @@ func (s *Subsystem) screenList(_ context.Context, _ *mcp.CallToolRequest, _ Scre } screens, ok := result.([]screen.Screen) if !ok { - return nil, ScreenListOutput{}, fmt.Errorf("unexpected result type from screen list query") + return nil, ScreenListOutput{}, corego.E("mcp.screen", "unexpected result type from screen list query", nil) } return nil, ScreenListOutput{Screens: screens}, nil } @@ -46,7 +46,7 @@ func (s *Subsystem) screenGet(_ context.Context, _ *mcp.CallToolRequest, input S } scr, ok := result.(*screen.Screen) if !ok { - return nil, ScreenGetOutput{}, fmt.Errorf("unexpected result type from screen get query") + return nil, ScreenGetOutput{}, corego.E("mcp.screen", "unexpected result type from screen get query", nil) } return nil, ScreenGetOutput{Screen: scr}, nil } @@ -65,7 +65,7 @@ func (s *Subsystem) screenPrimary(_ context.Context, _ *mcp.CallToolRequest, _ S } scr, ok := result.(*screen.Screen) if !ok { - return nil, ScreenPrimaryOutput{}, fmt.Errorf("unexpected result type from screen primary query") + return nil, ScreenPrimaryOutput{}, corego.E("mcp.screen", "unexpected result type from screen primary query", nil) } return nil, ScreenPrimaryOutput{Screen: scr}, nil } @@ -87,7 +87,7 @@ func (s *Subsystem) screenAtPoint(_ context.Context, _ *mcp.CallToolRequest, inp } scr, ok := result.(*screen.Screen) if !ok { - return nil, ScreenAtPointOutput{}, fmt.Errorf("unexpected result type from screen at point query") + return nil, ScreenAtPointOutput{}, corego.E("mcp.screen", "unexpected result type from screen at point query", nil) } return nil, ScreenAtPointOutput{Screen: scr}, nil } @@ -106,7 +106,7 @@ func (s *Subsystem) screenWorkAreas(_ context.Context, _ *mcp.CallToolRequest, _ } areas, ok := result.([]screen.Rect) if !ok { - return nil, ScreenWorkAreasOutput{}, fmt.Errorf("unexpected result type from screen work areas query") + return nil, ScreenWorkAreasOutput{}, corego.E("mcp.screen", "unexpected result type from screen work areas query", nil) } return nil, ScreenWorkAreasOutput{WorkAreas: areas}, nil } diff --git a/pkg/mcp/tools_tray.go b/pkg/mcp/tools_tray.go index f11f11d9..e0c95fa2 100644 --- a/pkg/mcp/tools_tray.go +++ b/pkg/mcp/tools_tray.go @@ -3,9 +3,9 @@ package mcp import ( "context" - "fmt" - "forge.lthn.ai/core/gui/pkg/systray" + corego "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/systray" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -74,7 +74,7 @@ func (s *Subsystem) trayInfo(_ context.Context, _ *mcp.CallToolRequest, _ TrayIn } config, ok := result.(map[string]any) if !ok { - return nil, TrayInfoOutput{}, fmt.Errorf("unexpected result type from tray config query") + return nil, TrayInfoOutput{}, corego.E("mcp.trayInfo", "unexpected result type from tray config query", nil) } return nil, TrayInfoOutput{Config: config}, nil } diff --git a/pkg/mcp/tools_webview.go b/pkg/mcp/tools_webview.go index 6da3dc3a..26b43611 100644 --- a/pkg/mcp/tools_webview.go +++ b/pkg/mcp/tools_webview.go @@ -3,9 +3,9 @@ package mcp import ( "context" - "fmt" - "forge.lthn.ai/core/gui/pkg/webview" + corego "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/webview" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -105,7 +105,7 @@ func (s *Subsystem) webviewScreenshot(_ context.Context, _ *mcp.CallToolRequest, } sr, ok := result.(webview.ScreenshotResult) if !ok { - return nil, WebviewScreenshotOutput{}, fmt.Errorf("unexpected result type from webview screenshot") + return nil, WebviewScreenshotOutput{}, corego.E("mcp.webview", "unexpected result type from webview screenshot", nil) } return nil, WebviewScreenshotOutput{Base64: sr.Base64, MimeType: sr.MimeType}, nil } @@ -129,7 +129,7 @@ func (s *Subsystem) webviewScreenshotElement(_ context.Context, _ *mcp.CallToolR } sr, ok := result.(webview.ScreenshotResult) if !ok { - return nil, WebviewScreenshotElementOutput{}, fmt.Errorf("unexpected result type from webview element screenshot") + return nil, WebviewScreenshotElementOutput{}, corego.E("mcp.webview", "unexpected result type from webview element screenshot", nil) } return nil, WebviewScreenshotElementOutput{Base64: sr.Base64, MimeType: sr.MimeType}, nil } @@ -272,7 +272,7 @@ func (s *Subsystem) webviewConsole(_ context.Context, _ *mcp.CallToolRequest, in } msgs, ok := result.([]webview.ConsoleMessage) if !ok { - return nil, WebviewConsoleOutput{}, fmt.Errorf("unexpected result type from webview console query") + return nil, WebviewConsoleOutput{}, corego.E("mcp.webview", "unexpected result type from webview console query", nil) } return nil, WebviewConsoleOutput{Messages: msgs}, nil } @@ -313,7 +313,7 @@ func (s *Subsystem) webviewErrors(_ context.Context, _ *mcp.CallToolRequest, inp } errors, ok := result.([]webview.ExceptionInfo) if !ok { - return nil, WebviewErrorsOutput{}, fmt.Errorf("unexpected result type from webview errors query") + return nil, WebviewErrorsOutput{}, corego.E("mcp.webview", "unexpected result type from webview errors query", nil) } return nil, WebviewErrorsOutput{Errors: errors}, nil } @@ -370,7 +370,7 @@ func (s *Subsystem) webviewQuery(_ context.Context, _ *mcp.CallToolRequest, inpu } el, ok := result.(*webview.ElementInfo) if !ok { - return nil, WebviewQueryOutput{}, fmt.Errorf("unexpected result type from webview query") + return nil, WebviewQueryOutput{}, corego.E("mcp.webview", "unexpected result type from webview query", nil) } return nil, WebviewQueryOutput{Element: el}, nil } @@ -399,7 +399,7 @@ func (s *Subsystem) webviewQueryAll(_ context.Context, _ *mcp.CallToolRequest, i } els, ok := result.([]*webview.ElementInfo) if !ok { - return nil, WebviewQueryAllOutput{}, fmt.Errorf("unexpected result type from webview query all") + return nil, WebviewQueryAllOutput{}, corego.E("mcp.webview", "unexpected result type from webview query all", nil) } return nil, WebviewQueryAllOutput{Elements: els}, nil } @@ -422,7 +422,7 @@ func (s *Subsystem) webviewDOMTree(_ context.Context, _ *mcp.CallToolRequest, in } html, ok := result.(string) if !ok { - return nil, WebviewDOMTreeOutput{}, fmt.Errorf("unexpected result type from webview DOM tree query") + return nil, WebviewDOMTreeOutput{}, corego.E("mcp.webview", "unexpected result type from webview DOM tree query", nil) } return nil, WebviewDOMTreeOutput{HTML: html}, nil } @@ -451,7 +451,7 @@ func (s *Subsystem) webviewComputedStyle(_ context.Context, _ *mcp.CallToolReque } style, ok := result.(map[string]string) if !ok { - return nil, WebviewComputedStyleOutput{}, fmt.Errorf("unexpected result type from webview computed style query") + return nil, WebviewComputedStyleOutput{}, corego.E("mcp.webview", "unexpected result type from webview computed style query", nil) } return nil, WebviewComputedStyleOutput{Style: style}, nil } @@ -473,7 +473,7 @@ func (s *Subsystem) webviewPerformance(_ context.Context, _ *mcp.CallToolRequest } metrics, ok := result.(webview.PerformanceMetrics) if !ok { - return nil, WebviewPerformanceOutput{}, fmt.Errorf("unexpected result type from webview performance query") + return nil, WebviewPerformanceOutput{}, corego.E("mcp.webview", "unexpected result type from webview performance query", nil) } return nil, WebviewPerformanceOutput{Metrics: metrics}, nil } @@ -495,7 +495,7 @@ func (s *Subsystem) webviewResources(_ context.Context, _ *mcp.CallToolRequest, } resources, ok := result.([]webview.ResourceEntry) if !ok { - return nil, WebviewResourcesOutput{}, fmt.Errorf("unexpected result type from webview resources query") + return nil, WebviewResourcesOutput{}, corego.E("mcp.webview", "unexpected result type from webview resources query", nil) } return nil, WebviewResourcesOutput{Resources: resources}, nil } @@ -518,7 +518,7 @@ func (s *Subsystem) webviewNetwork(_ context.Context, _ *mcp.CallToolRequest, in } requests, ok := result.([]webview.NetworkEntry) if !ok { - return nil, WebviewNetworkOutput{}, fmt.Errorf("unexpected result type from webview network query") + return nil, WebviewNetworkOutput{}, corego.E("mcp.webview", "unexpected result type from webview network query", nil) } return nil, WebviewNetworkOutput{Requests: requests}, nil } @@ -615,7 +615,7 @@ func (s *Subsystem) webviewPDF(_ context.Context, _ *mcp.CallToolRequest, input } pdf, ok := result.(webview.PDFResult) if !ok { - return nil, WebviewPDFOutput{}, fmt.Errorf("unexpected result type from webview pdf task") + return nil, WebviewPDFOutput{}, corego.E("mcp.webview", "unexpected result type from webview pdf task", nil) } return nil, WebviewPDFOutput{Base64: pdf.Base64, MimeType: pdf.MimeType}, nil } @@ -637,7 +637,7 @@ func (s *Subsystem) webviewURL(_ context.Context, _ *mcp.CallToolRequest, input } url, ok := result.(string) if !ok { - return nil, WebviewURLOutput{}, fmt.Errorf("unexpected result type from webview URL query") + return nil, WebviewURLOutput{}, corego.E("mcp.webview", "unexpected result type from webview URL query", nil) } return nil, WebviewURLOutput{URL: url}, nil } @@ -659,7 +659,7 @@ func (s *Subsystem) webviewTitle(_ context.Context, _ *mcp.CallToolRequest, inpu } title, ok := result.(string) if !ok { - return nil, WebviewTitleOutput{}, fmt.Errorf("unexpected result type from webview title query") + return nil, WebviewTitleOutput{}, corego.E("mcp.webview", "unexpected result type from webview title query", nil) } return nil, WebviewTitleOutput{Title: title}, nil } diff --git a/pkg/mcp/tools_window.go b/pkg/mcp/tools_window.go index a602a1f1..748eaadb 100644 --- a/pkg/mcp/tools_window.go +++ b/pkg/mcp/tools_window.go @@ -3,9 +3,9 @@ package mcp import ( "context" - "fmt" - "forge.lthn.ai/core/gui/pkg/window" + corego "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/window" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -23,7 +23,7 @@ func (s *Subsystem) windowList(_ context.Context, _ *mcp.CallToolRequest, _ Wind } windows, ok := result.([]window.WindowInfo) if !ok { - return nil, WindowListOutput{}, fmt.Errorf("unexpected result type from window list query") + return nil, WindowListOutput{}, corego.E("mcp.window", "unexpected result type from window list query", nil) } return nil, WindowListOutput{Windows: windows}, nil } @@ -44,7 +44,7 @@ func (s *Subsystem) windowGet(_ context.Context, _ *mcp.CallToolRequest, input W } info, ok := result.(*window.WindowInfo) if !ok { - return nil, WindowGetOutput{}, fmt.Errorf("unexpected result type from window get query") + return nil, WindowGetOutput{}, corego.E("mcp.window", "unexpected result type from window get query", nil) } return nil, WindowGetOutput{Window: info}, nil } @@ -63,7 +63,7 @@ func (s *Subsystem) windowFocused(_ context.Context, _ *mcp.CallToolRequest, _ W } windows, ok := result.([]window.WindowInfo) if !ok { - return nil, WindowFocusedOutput{}, fmt.Errorf("unexpected result type from window list query") + return nil, WindowFocusedOutput{}, corego.E("mcp.window", "unexpected result type from window list query", nil) } for _, w := range windows { if w.Focused { @@ -105,7 +105,7 @@ func (s *Subsystem) windowCreate(_ context.Context, _ *mcp.CallToolRequest, inpu } info, ok := result.(window.WindowInfo) if !ok { - return nil, WindowCreateOutput{}, fmt.Errorf("unexpected result type from window create task") + return nil, WindowCreateOutput{}, corego.E("mcp.window", "unexpected result type from window create task", nil) } return nil, WindowCreateOutput{Window: info}, nil } diff --git a/pkg/notification/service.go b/pkg/notification/service.go index 4941c50b..b15afe5d 100644 --- a/pkg/notification/service.go +++ b/pkg/notification/service.go @@ -3,11 +3,11 @@ package notification import ( "context" - "fmt" "time" + corego "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/dialog" "forge.lthn.ai/core/go/pkg/core" - "forge.lthn.ai/core/gui/pkg/dialog" ) // Options configures the notification service. @@ -76,7 +76,7 @@ func (s *Service) handleTask(c *core.Core, t core.Task) (any, bool, error) { func (s *Service) sendNotification(opts NotificationOptions) error { // Generate an ID when the caller does not provide one. if opts.ID == "" { - opts.ID = fmt.Sprintf("core-%d", time.Now().UnixNano()) + opts.ID = corego.Sprintf("core-%d", time.Now().UnixNano()) } if len(opts.Actions) > 0 { diff --git a/pkg/notification/service_test.go b/pkg/notification/service_test.go index cd51ab60..5cab891d 100644 --- a/pkg/notification/service_test.go +++ b/pkg/notification/service_test.go @@ -7,7 +7,7 @@ import ( "testing" "forge.lthn.ai/core/go/pkg/core" - "forge.lthn.ai/core/gui/pkg/dialog" + "dappco.re/go/core/gui/pkg/dialog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/systray/service.go b/pkg/systray/service.go index 47d71f21..a690c58d 100644 --- a/pkg/systray/service.go +++ b/pkg/systray/service.go @@ -5,7 +5,7 @@ import ( "context" "forge.lthn.ai/core/go/pkg/core" - "forge.lthn.ai/core/gui/pkg/notification" + "dappco.re/go/core/gui/pkg/notification" ) // Options configures the systray service. diff --git a/pkg/webview/diagnostics.go b/pkg/webview/diagnostics.go index 55c01137..2330df72 100644 --- a/pkg/webview/diagnostics.go +++ b/pkg/webview/diagnostics.go @@ -2,19 +2,16 @@ package webview import ( - "encoding/json" - "fmt" - "strings" + corego "dappco.re/go/core" ) func jsQuote(v string) string { - b, _ := json.Marshal(v) - return string(b) + return corego.JSONMarshalString(v) } func computedStyleScript(selector string) string { sel := jsQuote(selector) - return fmt.Sprintf(`(function(){ + return corego.Sprintf(`(function(){ const el = document.querySelector(%s); if (!el) return null; const style = window.getComputedStyle(el); @@ -33,7 +30,7 @@ func highlightScript(selector, colour string) string { colour = "#ff9800" } col := jsQuote(colour) - return fmt.Sprintf(`(function(){ + return corego.Sprintf(`(function(){ const el = document.querySelector(%s); if (!el) return false; if (el.__coreHighlightOrigOutline === undefined) { @@ -150,9 +147,9 @@ func networkLogScript(limit int) string { if limit <= 0 { return `(window.__coreNetworkLog || [])` } - return fmt.Sprintf(`(window.__coreNetworkLog || []).slice(-%d)`, limit) + return corego.Sprintf(`(window.__coreNetworkLog || []).slice(-%d)`, limit) } func normalizeWhitespace(s string) string { - return strings.TrimSpace(s) + return corego.Trim(s) } diff --git a/pkg/webview/service.go b/pkg/webview/service.go index 2ddc593c..0e8bb4d6 100644 --- a/pkg/webview/service.go +++ b/pkg/webview/service.go @@ -5,22 +5,20 @@ import ( "bytes" "context" "encoding/base64" - "encoding/json" - "fmt" "image" "image/draw" "image/png" "math" "reflect" "strconv" - "strings" "sync" "time" "unsafe" + corego "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/window" gowebview "forge.lthn.ai/core/go-webview" "forge.lthn.ai/core/go/pkg/core" - "forge.lthn.ai/core/gui/pkg/window" ) // connector abstracts go-webview for testing. The real implementation wraps @@ -108,7 +106,7 @@ func defaultNewConn(opts Options) func(string, string) (connector, error) { } var wsURL string for _, t := range targets { - if t.Type == "page" && (strings.Contains(t.Title, windowName) || strings.Contains(t.URL, windowName)) { + if t.Type == "page" && (corego.Contains(t.Title, windowName) || corego.Contains(t.URL, windowName)) { wsURL = t.WebSocketDebuggerURL break } @@ -473,7 +471,7 @@ func (s *Service) handleTask(_ *core.Core, t core.Task) (any, bool, error) { } pw, ok := ws.Manager().Get(t.Window) if !ok { - return nil, true, fmt.Errorf("window not found: %s", t.Window) + return nil, true, corego.E("webview", corego.Sprintf("window not found: %s", t.Window), nil) } pw.OpenDevTools() return nil, true, nil @@ -484,7 +482,7 @@ func (s *Service) handleTask(_ *core.Core, t core.Task) (any, bool, error) { } pw, ok := ws.Manager().Get(t.Window) if !ok { - return nil, true, fmt.Errorf("window not found: %s", t.Window) + return nil, true, corego.E("webview", corego.Sprintf("window not found: %s", t.Window), nil) } pw.CloseDevTools() return nil, true, nil @@ -550,12 +548,13 @@ func (s *Service) queryExceptions(windowName string, limit int) []ExceptionInfo func coerceJSON[T any](v any) (T, error) { var out T - raw, err := json.Marshal(v) - if err != nil { - return out, err + r := corego.JSONMarshal(v) + if !r.OK { + return out, r.Value.(error) } - if err := json.Unmarshal(raw, &out); err != nil { - return out, err + r2 := corego.JSONUnmarshal(r.Value.([]byte), &out) + if !r2.OK { + return out, r2.Value.(error) } return out, nil } @@ -586,7 +585,7 @@ type elementScreenshotBounds struct { func elementScreenshotScript(selector string) string { sel := jsQuote(selector) - return fmt.Sprintf(`(function(){ + return corego.Sprintf(`(function(){ const el = document.querySelector(%s); if (!el) return null; try { el.scrollIntoView({block: "center", inline: "center"}); } catch (e) {} @@ -607,14 +606,14 @@ func captureElementScreenshot(conn connector, selector string) ([]byte, error) { return nil, err } if result == nil { - return nil, fmt.Errorf("webview: element not found: %s", selector) + return nil, corego.E("webview", corego.Sprintf("element not found: %s", selector), nil) } bounds, err := coerceJSON[elementScreenshotBounds](result) if err != nil { return nil, err } if bounds.Width <= 0 || bounds.Height <= 0 { - return nil, fmt.Errorf("webview: element has no measurable bounds: %s", selector) + return nil, corego.E("webview", corego.Sprintf("element has no measurable bounds: %s", selector), nil) } raw, err := conn.Screenshot() if err != nil { @@ -648,7 +647,7 @@ func captureElementScreenshot(conn connector, selector string) ([]byte, error) { bottom = srcBounds.Max.Y } if right <= left || bottom <= top { - return nil, fmt.Errorf("webview: element is outside the captured screenshot: %s", selector) + return nil, corego.E("webview", corego.Sprintf("element is outside the captured screenshot: %s", selector), nil) } crop := image.NewRGBA(image.Rect(0, 0, right-left, bottom-top)) @@ -695,7 +694,7 @@ func (r *realConnector) PrintToPDF() ([]byte, error) { } data, ok := result["data"].(string) if !ok || data == "" { - return nil, fmt.Errorf("webview: missing PDF data") + return nil, corego.E("webview", "missing PDF data", nil) } return base64.StdEncoding.DecodeString(data) } @@ -703,17 +702,17 @@ func (r *realConnector) PrintToPDF() ([]byte, error) { func (r *realConnector) cdpClient() (*gowebview.CDPClient, error) { rv := reflect.ValueOf(r.wv) if rv.Kind() != reflect.Ptr || rv.IsNil() { - return nil, fmt.Errorf("webview: invalid connector") + return nil, corego.E("webview", "invalid connector", nil) } elem := rv.Elem() field := elem.FieldByName("client") if !field.IsValid() || field.IsNil() { - return nil, fmt.Errorf("webview: CDP client not available") + return nil, corego.E("webview", "CDP client not available", nil) } ptr := reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface() client, ok := ptr.(*gowebview.CDPClient) if !ok || client == nil { - return nil, fmt.Errorf("webview: unexpected CDP client type") + return nil, corego.E("webview", "unexpected CDP client type", nil) } return client, nil } diff --git a/pkg/webview/service_test.go b/pkg/webview/service_test.go index f3338333..23e93f78 100644 --- a/pkg/webview/service_test.go +++ b/pkg/webview/service_test.go @@ -12,7 +12,7 @@ import ( "testing" "forge.lthn.ai/core/go/pkg/core" - "forge.lthn.ai/core/gui/pkg/window" + "dappco.re/go/core/gui/pkg/window" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/window/layout.go b/pkg/window/layout.go index 9d3e1235..ca1f74ea 100644 --- a/pkg/window/layout.go +++ b/pkg/window/layout.go @@ -2,12 +2,11 @@ package window import ( - "encoding/json" - "fmt" "os" - "path/filepath" "sync" "time" + + corego "dappco.re/go/core" ) // Layout is a named window arrangement. @@ -44,7 +43,7 @@ func NewLayoutManager() *LayoutManager { } configDir, err := os.UserConfigDir() if err == nil { - lm.configDir = filepath.Join(configDir, "Core") + lm.configDir = corego.JoinPath(configDir, "Core") } lm.loadLayouts() return lm @@ -63,7 +62,7 @@ func NewLayoutManagerWithDir(configDir string) *LayoutManager { } func (lm *LayoutManager) layoutsFilePath() string { - return filepath.Join(lm.configDir, "layouts.json") + return corego.JoinPath(lm.configDir, "layouts.json") } func (lm *LayoutManager) loadLayouts() { @@ -76,7 +75,7 @@ func (lm *LayoutManager) loadLayouts() { } lm.mu.Lock() defer lm.mu.Unlock() - _ = json.Unmarshal(data, &lm.layouts) + _ = corego.JSONUnmarshal(data, &lm.layouts) } func (lm *LayoutManager) saveLayouts() { @@ -84,20 +83,20 @@ func (lm *LayoutManager) saveLayouts() { return } lm.mu.RLock() - data, err := json.MarshalIndent(lm.layouts, "", " ") + r := corego.JSONMarshal(lm.layouts) lm.mu.RUnlock() - if err != nil { + if !r.OK { return } _ = os.MkdirAll(lm.configDir, 0o755) - _ = os.WriteFile(lm.layoutsFilePath(), data, 0o644) + _ = os.WriteFile(lm.layoutsFilePath(), r.Value.([]byte), 0o644) } // SaveLayout creates or updates a named layout. // Use: _ = lm.SaveLayout("coding", windowStates) func (lm *LayoutManager) SaveLayout(name string, windowStates map[string]WindowState) error { if name == "" { - return fmt.Errorf("layout name cannot be empty") + return corego.E("layout.save", "layout name cannot be empty", nil) } now := time.Now().UnixMilli() lm.mu.Lock() diff --git a/pkg/window/service.go b/pkg/window/service.go index b6dbe1df..f41019f0 100644 --- a/pkg/window/service.go +++ b/pkg/window/service.go @@ -3,11 +3,10 @@ package window import ( "context" - "fmt" - "strings" + corego "dappco.re/go/core" + "dappco.re/go/core/gui/pkg/screen" "forge.lthn.ai/core/go/pkg/core" - "forge.lthn.ai/core/gui/pkg/screen" ) // Options holds configuration for the window service. @@ -280,7 +279,7 @@ func (s *Service) trackWindow(pw PlatformWindow) { func (s *Service) taskCloseWindow(name string) error { pw, ok := s.manager.Get(name) if !ok { - return fmt.Errorf("window not found: %s", name) + return corego.E("window.service", corego.Sprintf("window not found: %s", name), nil) } // Persist state BEFORE closing (spec requirement) s.manager.State().CaptureState(pw) @@ -292,7 +291,7 @@ func (s *Service) taskCloseWindow(name string) error { func (s *Service) taskSetPosition(name string, x, y int) error { pw, ok := s.manager.Get(name) if !ok { - return fmt.Errorf("window not found: %s", name) + return corego.E("window.service", corego.Sprintf("window not found: %s", name), nil) } pw.SetPosition(x, y) s.manager.State().UpdatePosition(name, x, y) @@ -302,7 +301,7 @@ func (s *Service) taskSetPosition(name string, x, y int) error { func (s *Service) taskSetSize(name string, width, height, fallbackWidth, fallbackHeight int) error { pw, ok := s.manager.Get(name) if !ok { - return fmt.Errorf("window not found: %s", name) + return corego.E("window.service", corego.Sprintf("window not found: %s", name), nil) } if width == 0 && height == 0 { width, height = fallbackWidth, fallbackHeight @@ -322,7 +321,7 @@ func (s *Service) taskSetSize(name string, width, height, fallbackWidth, fallbac func (s *Service) taskMaximise(name string) error { pw, ok := s.manager.Get(name) if !ok { - return fmt.Errorf("window not found: %s", name) + return corego.E("window.service", corego.Sprintf("window not found: %s", name), nil) } pw.Maximise() s.manager.State().UpdateMaximized(name, true) @@ -332,7 +331,7 @@ func (s *Service) taskMaximise(name string) error { func (s *Service) taskMinimise(name string) error { pw, ok := s.manager.Get(name) if !ok { - return fmt.Errorf("window not found: %s", name) + return corego.E("window.service", corego.Sprintf("window not found: %s", name), nil) } pw.Minimise() return nil @@ -341,7 +340,7 @@ func (s *Service) taskMinimise(name string) error { func (s *Service) taskFocus(name string) error { pw, ok := s.manager.Get(name) if !ok { - return fmt.Errorf("window not found: %s", name) + return corego.E("window.service", corego.Sprintf("window not found: %s", name), nil) } pw.Focus() return nil @@ -350,7 +349,7 @@ func (s *Service) taskFocus(name string) error { func (s *Service) taskRestore(name string) error { pw, ok := s.manager.Get(name) if !ok { - return fmt.Errorf("window not found: %s", name) + return corego.E("window.service", corego.Sprintf("window not found: %s", name), nil) } pw.Restore() s.manager.State().UpdateMaximized(name, false) @@ -360,7 +359,7 @@ func (s *Service) taskRestore(name string) error { func (s *Service) taskSetTitle(name, title string) error { pw, ok := s.manager.Get(name) if !ok { - return fmt.Errorf("window not found: %s", name) + return corego.E("window.service", corego.Sprintf("window not found: %s", name), nil) } pw.SetTitle(title) return nil @@ -369,7 +368,7 @@ func (s *Service) taskSetTitle(name, title string) error { func (s *Service) taskSetAlwaysOnTop(name string, alwaysOnTop bool) error { pw, ok := s.manager.Get(name) if !ok { - return fmt.Errorf("window not found: %s", name) + return corego.E("window.service", corego.Sprintf("window not found: %s", name), nil) } pw.SetAlwaysOnTop(alwaysOnTop) return nil @@ -378,7 +377,7 @@ func (s *Service) taskSetAlwaysOnTop(name string, alwaysOnTop bool) error { func (s *Service) taskSetBackgroundColour(name string, red, green, blue, alpha uint8) error { pw, ok := s.manager.Get(name) if !ok { - return fmt.Errorf("window not found: %s", name) + return corego.E("window.service", corego.Sprintf("window not found: %s", name), nil) } pw.SetBackgroundColour(red, green, blue, alpha) return nil @@ -386,11 +385,11 @@ func (s *Service) taskSetBackgroundColour(name string, red, green, blue, alpha u func (s *Service) taskSetOpacity(name string, opacity float32) error { if opacity < 0 || opacity > 1 { - return fmt.Errorf("opacity must be between 0 and 1") + return corego.E("window.setOpacity", "opacity must be between 0 and 1", nil) } pw, ok := s.manager.Get(name) if !ok { - return fmt.Errorf("window not found: %s", name) + return corego.E("window.service", corego.Sprintf("window not found: %s", name), nil) } pw.SetOpacity(opacity) return nil @@ -399,7 +398,7 @@ func (s *Service) taskSetOpacity(name string, opacity float32) error { func (s *Service) taskSetVisibility(name string, visible bool) error { pw, ok := s.manager.Get(name) if !ok { - return fmt.Errorf("window not found: %s", name) + return corego.E("window.service", corego.Sprintf("window not found: %s", name), nil) } pw.SetVisibility(visible) return nil @@ -408,7 +407,7 @@ func (s *Service) taskSetVisibility(name string, visible bool) error { func (s *Service) taskFullscreen(name string, fullscreen bool) error { pw, ok := s.manager.Get(name) if !ok { - return fmt.Errorf("window not found: %s", name) + return corego.E("window.service", corego.Sprintf("window not found: %s", name), nil) } if fullscreen { pw.Fullscreen() @@ -433,7 +432,7 @@ func (s *Service) taskSaveLayout(name string) error { func (s *Service) taskRestoreLayout(name string) error { layout, ok := s.manager.Layout().GetLayout(name) if !ok { - return fmt.Errorf("layout not found: %s", name) + return corego.E("window.restoreLayout", corego.Sprintf("layout not found: %s", name), nil) } for winName, state := range layout.Windows { pw, found := s.manager.Get(winName) @@ -465,7 +464,7 @@ var tileModeMap = map[string]TileMode{ func (s *Service) taskTileWindows(mode string, names []string) error { tm, ok := tileModeMap[mode] if !ok { - return fmt.Errorf("unknown tile mode: %s", mode) + return corego.E("window.tileWindows", corego.Sprintf("unknown tile mode: %s", mode), nil) } if len(names) == 0 { names = s.manager.List() @@ -485,7 +484,7 @@ var snapPosMap = map[string]SnapPosition{ func (s *Service) taskSnapWindow(name, position string) error { pos, ok := snapPosMap[position] if !ok { - return fmt.Errorf("unknown snap position: %s", position) + return corego.E("window.snapWindow", corego.Sprintf("unknown snap position: %s", position), nil) } screenW, screenH := s.primaryScreenSize() return s.manager.SnapWindow(name, pos, screenW, screenH) @@ -502,13 +501,13 @@ func (s *Service) taskBesideEditor(editorName, windowName string) error { editorName = s.detectEditorWindow() } if editorName == "" { - return fmt.Errorf("editor window not found") + return corego.E("window.besideEditor", "editor window not found", nil) } if windowName == "" { windowName = s.detectCompanionWindow(editorName) } if windowName == "" { - return fmt.Errorf("companion window not found") + return corego.E("window.besideEditor", "companion window not found", nil) } return s.manager.BesideEditor(editorName, windowName, screenW, screenH) } @@ -554,9 +553,9 @@ func looksLikeEditor(name, title string) bool { } func containsAny(value string, needles ...string) bool { - lower := strings.ToLower(value) + lower := corego.Lower(value) for _, needle := range needles { - if strings.Contains(lower, needle) { + if corego.Contains(lower, needle) { return true } } diff --git a/pkg/window/service_test.go b/pkg/window/service_test.go index 73de842e..36b10d8a 100644 --- a/pkg/window/service_test.go +++ b/pkg/window/service_test.go @@ -6,7 +6,7 @@ import ( "testing" "forge.lthn.ai/core/go/pkg/core" - "forge.lthn.ai/core/gui/pkg/screen" + "dappco.re/go/core/gui/pkg/screen" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/window/state.go b/pkg/window/state.go index f222f24d..08df4d4b 100644 --- a/pkg/window/state.go +++ b/pkg/window/state.go @@ -2,11 +2,11 @@ package window import ( - "encoding/json" "os" - "path/filepath" "sync" "time" + + corego "dappco.re/go/core" ) // WindowState holds the persisted position/size of a window. @@ -41,7 +41,7 @@ func NewStateManager() *StateManager { } configDir, err := os.UserConfigDir() if err == nil { - sm.configDir = filepath.Join(configDir, "Core") + sm.configDir = corego.JoinPath(configDir, "Core") } sm.load() return sm @@ -63,12 +63,12 @@ func (sm *StateManager) filePath() string { if sm.statePath != "" { return sm.statePath } - return filepath.Join(sm.configDir, "window_state.json") + return corego.JoinPath(sm.configDir, "window_state.json") } func (sm *StateManager) dataDir() string { if sm.statePath != "" { - return filepath.Dir(sm.statePath) + return corego.PathDir(sm.statePath) } return sm.configDir } @@ -100,7 +100,7 @@ func (sm *StateManager) load() { } sm.mu.Lock() defer sm.mu.Unlock() - _ = json.Unmarshal(data, &sm.states) + _ = corego.JSONUnmarshal(data, &sm.states) } func (sm *StateManager) save() { @@ -108,11 +108,12 @@ func (sm *StateManager) save() { return } sm.mu.RLock() - data, err := json.MarshalIndent(sm.states, "", " ") + r := corego.JSONMarshal(sm.states) sm.mu.RUnlock() - if err != nil { + if !r.OK { return } + data := r.Value.([]byte) _ = os.MkdirAll(sm.dataDir(), 0o755) _ = os.WriteFile(sm.filePath(), data, 0o644) } diff --git a/pkg/window/tiling.go b/pkg/window/tiling.go index caa845c6..186c868b 100644 --- a/pkg/window/tiling.go +++ b/pkg/window/tiling.go @@ -1,7 +1,7 @@ // pkg/window/tiling.go package window -import "fmt" +import corego "dappco.re/go/core" // normalizeWindowForLayout clears transient maximise/minimise state before // applying a new geometry. This keeps layout helpers effective even when a @@ -97,12 +97,12 @@ func (m *Manager) TileWindows(mode TileMode, names []string, screenW, screenH in for _, name := range names { pw, ok := m.Get(name) if !ok { - return fmt.Errorf("window %q not found", name) + return corego.E("window.tiling", corego.Sprintf("window %q not found", name), nil) } windows = append(windows, pw) } if len(windows) == 0 { - return fmt.Errorf("no windows to tile") + return corego.E("window.tiling", "no windows to tile", nil) } halfW, halfH := screenW/2, screenH/2 @@ -186,7 +186,7 @@ func (m *Manager) TileWindows(mode TileMode, names []string, screenW, screenH in func (m *Manager) SnapWindow(name string, pos SnapPosition, screenW, screenH int) error { pw, ok := m.Get(name) if !ok { - return fmt.Errorf("window %q not found", name) + return corego.E("window.tiling", corego.Sprintf("window %q not found", name), nil) } halfW, halfH := screenW/2, screenH/2 @@ -237,7 +237,7 @@ func (m *Manager) StackWindows(names []string, offsetX, offsetY int) error { for i, name := range names { pw, ok := m.Get(name) if !ok { - return fmt.Errorf("window %q not found", name) + return corego.E("window.tiling", corego.Sprintf("window %q not found", name), nil) } normalizeWindowForLayout(pw) pw.SetPosition(i*offsetX, i*offsetY) @@ -248,7 +248,7 @@ func (m *Manager) StackWindows(names []string, offsetX, offsetY int) error { // ApplyWorkflow arranges windows in a predefined workflow layout. func (m *Manager) ApplyWorkflow(workflow WorkflowLayout, names []string, screenW, screenH int) error { if len(names) == 0 { - return fmt.Errorf("no windows for workflow") + return corego.E("window.tiling", "no windows for workflow", nil) } switch workflow { diff --git a/pkg/window/window.go b/pkg/window/window.go index 65896463..3c26b43d 100644 --- a/pkg/window/window.go +++ b/pkg/window/window.go @@ -2,9 +2,10 @@ package window import ( - "fmt" "math" "sync" + + corego "dappco.re/go/core" ) // Window is CoreGUI's own window descriptor — NOT a Wails type alias. @@ -94,7 +95,7 @@ func (m *Manager) SetDefaultHeight(height int) { func (m *Manager) Open(opts ...WindowOption) (PlatformWindow, error) { w, err := ApplyOptions(opts...) if err != nil { - return nil, fmt.Errorf("window.Manager.Open: %w", err) + return nil, corego.Wrap(err, "window.Manager.Open", "failed to apply options") } return m.Create(w) } @@ -288,11 +289,11 @@ func (m *Manager) FindSpace(screenW, screenH, width, height int) SpaceInfo { func (m *Manager) ArrangePair(first, second string, screenW, screenH int) error { left, ok := m.Get(first) if !ok { - return fmt.Errorf("window %q not found", first) + return corego.E("window.ArrangePair", corego.Sprintf("window %q not found", first), nil) } right, ok := m.Get(second) if !ok { - return fmt.Errorf("window %q not found", second) + return corego.E("window.ArrangePair", corego.Sprintf("window %q not found", second), nil) } leftW := screenW / 2 @@ -309,11 +310,11 @@ func (m *Manager) ArrangePair(first, second string, screenW, screenH int) error func (m *Manager) BesideEditor(editorName, windowName string, screenW, screenH int) error { editor, ok := m.Get(editorName) if !ok { - return fmt.Errorf("window %q not found", editorName) + return corego.E("window.BesideEditor", corego.Sprintf("window %q not found", editorName), nil) } target, ok := m.Get(windowName) if !ok { - return fmt.Errorf("window %q not found", windowName) + return corego.E("window.BesideEditor", corego.Sprintf("window %q not found", windowName), nil) } editorW := screenW * 70 / 100