diff --git a/pkg/contextmenu/messages.go b/pkg/contextmenu/messages.go index 32e7037..f7a78f3 100644 --- a/pkg/contextmenu/messages.go +++ b/pkg/contextmenu/messages.go @@ -1,8 +1,8 @@ package contextmenu -import "errors" +import core "dappco.re/go/core" -var ErrorMenuNotFound = errors.New("contextmenu: menu not found") +var ErrorMenuNotFound = core.E("contextmenu", "menu not found", nil) // QueryGet returns a named context menu definition. Result: *ContextMenuDef (nil if not found) type QueryGet struct { diff --git a/pkg/display/display.go b/pkg/display/display.go index e6af355..1a71337 100644 --- a/pkg/display/display.go +++ b/pkg/display/display.go @@ -2,9 +2,6 @@ package display import ( "context" - "encoding/json" - "os" - "path/filepath" "runtime" "forge.lthn.ai/core/config" @@ -331,9 +328,12 @@ func (s *Service) handleWSMessage(msg WSMessage) core.Result { return c.QUERY(dock.QueryVisible{}) case "contextmenu:add": name, _ := msg.Data["name"].(string) - menuJSON, _ := json.Marshal(msg.Data["menu"]) + marshalResult := core.JSONMarshal(msg.Data["menu"]) var menuDef contextmenu.ContextMenuDef - _ = json.Unmarshal(menuJSON, &menuDef) + if marshalResult.OK { + menuJSON, _ := marshalResult.Value.([]byte) + core.JSONUnmarshal(menuJSON, &menuDef) + } return c.Action("contextmenu.add").Run(ctx, core.NewOptions( core.Option{Key: "task", Value: contextmenu.TaskAdd{Name: name, Menu: menuDef}}, )) @@ -580,11 +580,11 @@ func (s *Service) handleTrayAction(actionID string) { } func guiConfigPath() string { - home, err := os.UserHomeDir() - if err != nil { - return filepath.Join(".core", "gui", "config.yaml") + home := core.Env("HOME") + if home == "" { + return core.JoinPath(".core", "gui", "config.yaml") } - return filepath.Join(home, ".core", "gui", "config.yaml") + return core.JoinPath(home, ".core", "gui", "config.yaml") } func (s *Service) loadConfig() { diff --git a/pkg/display/events.go b/pkg/display/events.go index 0dd2002..0498bb4 100644 --- a/pkg/display/events.go +++ b/pkg/display/events.go @@ -1,12 +1,12 @@ package display import ( - "encoding/json" "net/http" "strconv" "sync" "time" + core "dappco.re/go/core" "forge.lthn.ai/core/gui/pkg/window" "github.com/gorilla/websocket" ) @@ -134,10 +134,11 @@ func (em *WSEventManager) sendEvent(conn *websocket.Conn, event Event) { return } - data, err := json.Marshal(event) - if err != nil { + marshalResult := core.JSONMarshal(event) + if !marshalResult.OK { return } + data, _ := marshalResult.Value.([]byte) conn.SetWriteDeadline(time.Now().Add(10 * time.Second)) if err := conn.WriteMessage(websocket.TextMessage, data); err != nil { @@ -178,7 +179,7 @@ func (em *WSEventManager) handleMessages(conn *websocket.Conn) { EventTypes []EventType `json:"eventTypes,omitempty"` } - if err := json.Unmarshal(message, &msg); err != nil { + if unmarshalResult := core.JSONUnmarshal(message, &msg); !unmarshalResult.OK { continue } @@ -224,8 +225,10 @@ func (em *WSEventManager) subscribe(conn *websocket.Conn, id string, eventTypes "id": id, "eventTypes": eventTypes, } - data, _ := json.Marshal(response) - conn.WriteMessage(websocket.TextMessage, data) + if marshalResult := core.JSONMarshal(response); marshalResult.OK { + responseData, _ := marshalResult.Value.([]byte) + conn.WriteMessage(websocket.TextMessage, responseData) + } } // unsubscribe removes a subscription for a client. @@ -247,8 +250,10 @@ func (em *WSEventManager) unsubscribe(conn *websocket.Conn, id string) { "type": "unsubscribed", "id": id, } - data, _ := json.Marshal(response) - conn.WriteMessage(websocket.TextMessage, data) + if marshalResult := core.JSONMarshal(response); marshalResult.OK { + responseData, _ := marshalResult.Value.([]byte) + conn.WriteMessage(websocket.TextMessage, responseData) + } } // listSubscriptions sends a list of active subscriptions to a client. @@ -272,8 +277,10 @@ func (em *WSEventManager) listSubscriptions(conn *websocket.Conn) { "type": "subscriptions", "subscriptions": subs, } - data, _ := json.Marshal(response) - conn.WriteMessage(websocket.TextMessage, data) + if marshalResult := core.JSONMarshal(response); marshalResult.OK { + responseData, _ := marshalResult.Value.([]byte) + conn.WriteMessage(websocket.TextMessage, responseData) + } } // removeClient removes a client and its subscriptions. diff --git a/pkg/keybinding/messages.go b/pkg/keybinding/messages.go index 4d82713..4590604 100644 --- a/pkg/keybinding/messages.go +++ b/pkg/keybinding/messages.go @@ -1,9 +1,9 @@ package keybinding -import "errors" +import core "dappco.re/go/core" -var ErrorAlreadyRegistered = errors.New("keybinding: accelerator already registered") -var ErrorNotRegistered = errors.New("keybinding: accelerator not registered") +var ErrorAlreadyRegistered = core.E("keybinding", "accelerator already registered", nil) +var ErrorNotRegistered = core.E("keybinding", "accelerator not registered", nil) // BindingInfo describes a registered global key binding. type BindingInfo struct { diff --git a/pkg/mcp/tools_contextmenu.go b/pkg/mcp/tools_contextmenu.go index 9c8baf2..3413ede 100644 --- a/pkg/mcp/tools_contextmenu.go +++ b/pkg/mcp/tools_contextmenu.go @@ -3,7 +3,6 @@ package mcp import ( "context" - "encoding/json" core "dappco.re/go/core" coreerr "dappco.re/go/core/log" @@ -26,13 +25,15 @@ 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{}, coreerr.E("mcp.contextMenuAdd", "failed to marshal menu definition", err) + marshalResult := core.JSONMarshal(input.Menu) + if !marshalResult.OK { + return nil, ContextMenuAddOutput{}, coreerr.E("mcp.contextMenuAdd", "failed to marshal menu definition", marshalResult.Value.(error)) } + menuJSON := marshalResult.Value.([]byte) var menuDef contextmenu.ContextMenuDef - if err := json.Unmarshal(menuJSON, &menuDef); err != nil { - return nil, ContextMenuAddOutput{}, coreerr.E("mcp.contextMenuAdd", "failed to unmarshal menu definition", err) + unmarshalResult := core.JSONUnmarshal(menuJSON, &menuDef) + if !unmarshalResult.OK { + return nil, ContextMenuAddOutput{}, coreerr.E("mcp.contextMenuAdd", "failed to unmarshal menu definition", unmarshalResult.Value.(error)) } r := s.core.Action("contextmenu.add").Run(context.Background(), core.NewOptions( core.Option{Key: "task", Value: contextmenu.TaskAdd{Name: input.Name, Menu: menuDef}}, @@ -93,13 +94,15 @@ func (s *Subsystem) contextMenuGet(_ context.Context, _ *mcp.CallToolRequest, in 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{}, coreerr.E("mcp.contextMenuGet", "failed to marshal context menu", err) + marshalResult := core.JSONMarshal(menu) + if !marshalResult.OK { + return nil, ContextMenuGetOutput{}, coreerr.E("mcp.contextMenuGet", "failed to marshal context menu", marshalResult.Value.(error)) } + menuJSON := marshalResult.Value.([]byte) var menuMap map[string]any - if err := json.Unmarshal(menuJSON, &menuMap); err != nil { - return nil, ContextMenuGetOutput{}, coreerr.E("mcp.contextMenuGet", "failed to unmarshal context menu", err) + unmarshalResult := core.JSONUnmarshal(menuJSON, &menuMap) + if !unmarshalResult.OK { + return nil, ContextMenuGetOutput{}, coreerr.E("mcp.contextMenuGet", "failed to unmarshal context menu", unmarshalResult.Value.(error)) } return nil, ContextMenuGetOutput{Menu: menuMap}, nil } @@ -124,13 +127,15 @@ func (s *Subsystem) contextMenuList(_ context.Context, _ *mcp.CallToolRequest, _ return nil, ContextMenuListOutput{}, coreerr.E("mcp.contextMenuList", "unexpected result type", 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{}, coreerr.E("mcp.contextMenuList", "failed to marshal context menus", err) + marshalResult := core.JSONMarshal(menus) + if !marshalResult.OK { + return nil, ContextMenuListOutput{}, coreerr.E("mcp.contextMenuList", "failed to marshal context menus", marshalResult.Value.(error)) } + menusJSON := marshalResult.Value.([]byte) var menusMap map[string]any - if err := json.Unmarshal(menusJSON, &menusMap); err != nil { - return nil, ContextMenuListOutput{}, coreerr.E("mcp.contextMenuList", "failed to unmarshal context menus", err) + unmarshalResult := core.JSONUnmarshal(menusJSON, &menusMap) + if !unmarshalResult.OK { + return nil, ContextMenuListOutput{}, coreerr.E("mcp.contextMenuList", "failed to unmarshal context menus", unmarshalResult.Value.(error)) } return nil, ContextMenuListOutput{Menus: menusMap}, nil } diff --git a/pkg/window/layout.go b/pkg/window/layout.go index 9bdfc2b..373f4e8 100644 --- a/pkg/window/layout.go +++ b/pkg/window/layout.go @@ -2,12 +2,10 @@ package window import ( - "encoding/json" - "os" - "path/filepath" "sync" "time" + core "dappco.re/go/core" coreio "dappco.re/go/core/io" coreerr "dappco.re/go/core/log" ) @@ -40,9 +38,8 @@ func NewLayoutManager() *LayoutManager { lm := &LayoutManager{ layouts: make(map[string]Layout), } - configDir, err := os.UserConfigDir() - if err == nil { - lm.configDir = filepath.Join(configDir, "Core") + if configDir := core.Env("DIR_CONFIG"); configDir != "" { + lm.configDir = core.JoinPath(configDir, "Core") } lm.load() return lm @@ -60,7 +57,7 @@ func NewLayoutManagerWithDir(configDir string) *LayoutManager { } func (lm *LayoutManager) filePath() string { - return filepath.Join(lm.configDir, "layouts.json") + return core.JoinPath(lm.configDir, "layouts.json") } func (lm *LayoutManager) load() { @@ -73,7 +70,7 @@ func (lm *LayoutManager) load() { } lm.mu.Lock() defer lm.mu.Unlock() - _ = json.Unmarshal([]byte(content), &lm.layouts) + _ = core.JSONUnmarshalString(content, &lm.layouts) } func (lm *LayoutManager) save() { @@ -81,11 +78,12 @@ func (lm *LayoutManager) save() { return } lm.mu.RLock() - data, err := json.MarshalIndent(lm.layouts, "", " ") + result := core.JSONMarshal(lm.layouts) lm.mu.RUnlock() - if err != nil { + if !result.OK { return } + data := result.Value.([]byte) _ = coreio.Local.EnsureDir(lm.configDir) _ = coreio.Local.Write(lm.filePath(), string(data)) } diff --git a/pkg/window/state.go b/pkg/window/state.go index 2ce339b..358ada5 100644 --- a/pkg/window/state.go +++ b/pkg/window/state.go @@ -2,12 +2,10 @@ package window import ( - "encoding/json" - "os" - "path/filepath" "sync" "time" + core "dappco.re/go/core" coreio "dappco.re/go/core/io" ) @@ -38,9 +36,8 @@ func NewStateManager() *StateManager { sm := &StateManager{ states: make(map[string]WindowState), } - configDir, err := os.UserConfigDir() - if err == nil { - sm.configDir = filepath.Join(configDir, "Core") + if configDir := core.Env("DIR_CONFIG"); configDir != "" { + sm.configDir = core.JoinPath(configDir, "Core") } sm.load() return sm @@ -61,12 +58,12 @@ func (sm *StateManager) filePath() string { if sm.statePath != "" { return sm.statePath } - return filepath.Join(sm.configDir, "window_state.json") + return core.JoinPath(sm.configDir, "window_state.json") } func (sm *StateManager) dataDir() string { if sm.statePath != "" { - return filepath.Dir(sm.statePath) + return core.PathDir(sm.statePath) } return sm.configDir } @@ -96,7 +93,7 @@ func (sm *StateManager) load() { } sm.mu.Lock() defer sm.mu.Unlock() - _ = json.Unmarshal([]byte(content), &sm.states) + _ = core.JSONUnmarshalString(content, &sm.states) } func (sm *StateManager) save() { @@ -104,11 +101,12 @@ func (sm *StateManager) save() { return } sm.mu.RLock() - data, err := json.MarshalIndent(sm.states, "", " ") + result := core.JSONMarshal(sm.states) sm.mu.RUnlock() - if err != nil { + if !result.OK { return } + data := result.Value.([]byte) if dir := sm.dataDir(); dir != "" { _ = coreio.Local.EnsureDir(dir) }