diff --git a/assets/default-keybindings.json b/assets/default-keybindings.json index c942ec4c..fff2891d 100644 --- a/assets/default-keybindings.json +++ b/assets/default-keybindings.json @@ -3,6 +3,10 @@ "command": "system:toggleDeveloperTools", "keys": ["Cmd:Option:i"] }, + { + "command": "system:hideWindow", + "keys": ["Cmd:m"] + }, { "command": "generic:cancel", "keys": ["Escape"] @@ -32,7 +36,7 @@ "keys": ["PageDown"] }, { - "command": "app:openHistory", + "command": "app:openHistoryView", "keys": ["Cmd:h"] }, { @@ -41,11 +45,13 @@ }, { "command": "app:openConnectionsView", - "keys": [] + "keys": [], + "commandStr": "/mainview connections" }, { "command": "app:openSettingsView", - "keys": [] + "keys": [], + "commandStr": "/mainview clientsettings" }, { "command": "app:newTab", @@ -125,39 +131,48 @@ }, { "command": "app:selectWorkspace-1", - "keys": ["Cmd:Ctrl:1"] + "keys": ["Cmd:Ctrl:1"], + "commandStr": "/session 1" }, { "command": "app:selectWorkspace-2", - "keys": ["Cmd:Ctrl:2"] + "keys": ["Cmd:Ctrl:2"], + "commandStr": "/session 2" }, { "command": "app:selectWorkspace-3", - "keys": ["Cmd:Ctrl:3"] + "keys": ["Cmd:Ctrl:3"], + "commandStr": "/session 3" }, { "command": "app:selectWorkspace-4", - "keys": ["Cmd:Ctrl:4"] + "keys": ["Cmd:Ctrl:4"], + "commandStr": "/session 4" }, { "command": "app:selectWorkspace-5", - "keys": ["Cmd:Ctrl:5"] + "keys": ["Cmd:Ctrl:5"], + "commandStr": "/session 5" }, { "command": "app:selectWorkspace-6", - "keys": ["Cmd:Ctrl:6"] + "keys": ["Cmd:Ctrl:6"], + "commandStr": "/session 6" }, { "command": "app:selectWorkspace-7", - "keys": ["Cmd:Ctrl:7"] + "keys": ["Cmd:Ctrl:7"], + "commandStr": "/session 7" }, { "command": "app:selectWorkspace-8", - "keys": ["Cmd:Ctrl:8"] + "keys": ["Cmd:Ctrl:8"], + "commandStr": "/session 8" }, { "command": "app:selectWorkspace-9", - "keys": ["Cmd:Ctrl:9"] + "keys": ["Cmd:Ctrl:9"], + "commandStr": "/session 9" }, { "command": "app:toggleSidebar", @@ -168,8 +183,9 @@ "keys": ["Cmd:d"] }, { - "command": "app:bookmarkActiveLine", - "keys": ["Cmd:b"] + "command": "app:openBookmarksView", + "keys": ["Cmd:b"], + "commandStr": "/bookmarks:show" }, { "command": "bookmarks:edit", @@ -213,7 +229,8 @@ }, { "command": "cmdinput:openHistory", - "keys": ["Ctrl:r"] + "keys": ["Ctrl:r"], + "commandStr": "/history" }, { "command": "cmdinput:openAIChat", diff --git a/src/electron/emain.ts b/src/electron/emain.ts index 5aff80c4..be4f429f 100644 --- a/src/electron/emain.ts +++ b/src/electron/emain.ts @@ -263,7 +263,12 @@ const menuTemplate: Electron.MenuItemConstructorOptions[] = [ { type: "separator" }, { role: "services" }, { type: "separator" }, - { role: "hide" }, + { + label: "Hide", + click: () => { + app.hide(); + }, + }, { role: "hideOthers" }, { type: "separator" }, { role: "quit" }, @@ -513,6 +518,13 @@ electron.ipcMain.on("toggle-developer-tools", (event) => { event.returnValue = true; }); +electron.ipcMain.on("hide-window", (event) => { + if (MainWindow != null) { + MainWindow.hide(); + } + event.returnValue = true; +}); + electron.ipcMain.on("get-id", (event) => { event.returnValue = instanceId + ":" + event.processId; }); diff --git a/src/electron/preload.js b/src/electron/preload.js index 14be4eb1..1b5f1a63 100644 --- a/src/electron/preload.js +++ b/src/electron/preload.js @@ -1,6 +1,7 @@ let { contextBridge, ipcRenderer } = require("electron"); contextBridge.exposeInMainWorld("api", { + hideWindow: () => ipc.Renderer.send("hide-window"), toggleDeveloperTools: () => ipcRenderer.send("toggle-developer-tools"), getId: () => ipcRenderer.sendSync("get-id"), getPlatform: () => ipcRenderer.sendSync("get-platform"), diff --git a/src/models/model.ts b/src/models/model.ts index 15af1ae5..a1d0069a 100644 --- a/src/models/model.ts +++ b/src/models/model.ts @@ -143,7 +143,7 @@ class Model { this.runUpdate(message, interactive); }); this.ws.reconnect(); - this.keybindManager = new KeybindManager(); + this.keybindManager = new KeybindManager(this); this.readConfigKeybindings(); this.initSystemKeybindings(); this.initAppKeybindings(); @@ -222,46 +222,31 @@ class Model { getApi().toggleDeveloperTools(); return true; }); + this.keybindManager.registerKeybinding("system", "electron", "system:minimizeWindow", (waveEvent) => { + getApi().hideWindow(); + return true; + }); } initAppKeybindings() { for (let index = 1; index <= 9; index++) { - this.keybindManager.registerKeybinding("app", "model", "app:selectWorkspace-" + index, (waveEvent) => { - this.onSwitchSessionCmd(index); - return true; - }); + this.keybindManager.registerKeybinding("app", "model", "app:selectWorkspace-" + index, null); } - this.keybindManager.registerKeybinding("app", "model", "app:focusCmdInput", (waveEvent) => { - console.log("focus cmd input callback"); this.onFocusCmdInputPressed(); return true; }); - - this.keybindManager.registerKeybinding("app", "model", "app:bookmarkActiveLine", (waveEvent) => { - this.onBookmarkViewPressed(); - return true; - }); - - this.keybindManager.registerKeybinding("app", "model", "app:openHistory", (waveEvent) => { + this.keybindManager.registerKeybinding("app", "model", "app:openBookmarksView", null); + this.keybindManager.registerKeybinding("app", "model", "app:openHistoryView", (waveEvent) => { this.onOpenHistoryPressed(); return true; }); - this.keybindManager.registerKeybinding("app", "model", "app:openTabSearchModal", (waveEvent) => { this.onOpenTabSearchModalPressed(); return true; }); - - this.keybindManager.registerKeybinding("app", "model", "app:openConnectionsView", (waveEvent) => { - this.onOpenConnectionsViewPressed(); - return true; - }); - - this.keybindManager.registerKeybinding("app", "model", "app:openSettingsView", (waveEvent) => { - this.onOpenSettingsViewPressed(); - return true; - }); + this.keybindManager.registerKeybinding("app", "model", "app:openConnectionsView", null); + this.keybindManager.registerKeybinding("app", "model", "app:openSettingsView", null); } static getInstance(): Model { @@ -1023,6 +1008,12 @@ class Model { console.warn("invalid bookmarksview in update:", update.mainview); } break; + case "clientsettings": + this.activeMainView.set("clientsettings"); + break; + case "connections": + this.activeMainView.set("connections"); + break; case "plugins": this.pluginsModel.showPluginsView(); break; diff --git a/src/types/custom.d.ts b/src/types/custom.d.ts index 20630fdf..08e424ce 100644 --- a/src/types/custom.d.ts +++ b/src/types/custom.d.ts @@ -880,6 +880,7 @@ declare global { }; type ElectronApi = { + hideWindow: () => void; toggleDeveloperTools: () => void; getId: () => string; getIsDev: () => boolean; diff --git a/src/util/keyutil.ts b/src/util/keyutil.ts index 705335b9..4da4fecf 100644 --- a/src/util/keyutil.ts +++ b/src/util/keyutil.ts @@ -4,7 +4,7 @@ import * as electron from "electron"; import { parse } from "node:path"; import { v4 as uuidv4 } from "uuid"; import defaultKeybindingsFile from "../../assets/default-keybindings.json"; -const defaultKeybindings: KeybindConfig = defaultKeybindingsFile; +const defaultKeybindings: KeybindConfigArray = defaultKeybindingsFile; type KeyPressDecl = { mods: { @@ -24,12 +24,18 @@ const KeyTypeKey = "key"; const KeyTypeCode = "code"; type KeybindCallback = (event: WaveKeyboardEvent) => boolean; -type KeybindConfig = Array<{ command: string; keys: Array }>; +type KeybindConfigArray = Array; +type KeybindConfig = { command: string; keys: Array; commandStr?: string }; + +const Callback = "callback"; +const Command = "command"; type Keybind = { domain: string; keybinding: string; + action: string; callback: KeybindCallback; + commandStr: string; }; const KeybindLevels = ["system", "modal", "app", "pane", "plugin"]; @@ -38,11 +44,12 @@ class KeybindManager { domainCallbacks: Map; levelMap: Map>; levelArray: Array; - keyDescriptionsMap: Map>; - userKeybindings: KeybindConfig; + keyDescriptionsMap: Map; + userKeybindings: KeybindConfigArray; userKeybindingError: OV; + globalModel: any; - constructor() { + constructor(GlobalModel: any) { this.levelMap = new Map(); this.domainCallbacks = new Map(); this.levelArray = KeybindLevels; @@ -53,6 +60,7 @@ class KeybindManager { this.userKeybindingError = mobx.observable.box(null, { name: "keyutil-userKeybindingError", }); + this.globalModel = GlobalModel; this.initKeyDescriptionsMap(); } @@ -63,7 +71,7 @@ class KeybindManager { let newKeyDescriptions = new Map(); for (let index = 0; index < defaultKeybindings.length; index++) { let curKeybind = defaultKeybindings[index]; - newKeyDescriptions.set(curKeybind.command, curKeybind.keys); + newKeyDescriptions.set(curKeybind.command, curKeybind); } let curUserCommand = ""; if (this.userKeybindings != null && this.userKeybindings instanceof Array) { @@ -85,7 +93,15 @@ class KeybindManager { throw new Error("invalid keybind key"); } } - newKeyDescriptions.set(curKeybind.command, curKeybind.keys); + let defaultCmd = this.keyDescriptionsMap.get(curKeybind.command); + if ( + defaultCmd != null && + defaultCmd.commandStr != null && + (curKeybind.commandStr == null || curKeybind.commandStr == "") + ) { + curKeybind.commandStr = this.keyDescriptionsMap.get(curKeybind.command).commandStr; + } + newKeyDescriptions.set(curKeybind.command, curKeybind); } } catch (e) { let userError = `${curUserCommand} is invalid: error: ${e}`; @@ -98,16 +114,48 @@ class KeybindManager { this.keyDescriptionsMap = newKeyDescriptions; } + runSlashCommand(curKeybind: Keybind): boolean { + let curConfigKeybind = this.keyDescriptionsMap.get(curKeybind.keybinding); + if (curConfigKeybind == null || curConfigKeybind.commandStr == null || curKeybind.commandStr == "") { + return false; + } + let commandsList = curConfigKeybind.commandStr.trim().split(";"); + this.runIndividualSlashCommand(commandsList); + return true; + } + + runIndividualSlashCommand(commandsList: Array): boolean { + if (commandsList.length == 0) { + return true; + } + let curCommand = commandsList.shift(); + console.log("running: ", curCommand); + let prtn = this.globalModel.submitRawCommand(curCommand, false, false); + prtn.then((rtn) => { + if (!rtn.success) { + console.log("error running command ", curCommand); + return false; + } + return this.runIndividualSlashCommand(commandsList); + }).catch((error) => { + console.log("caught error running command ", curCommand, ": ", error); + return false; + }); + } + processLevel(nativeEvent: any, event: WaveKeyboardEvent, keybindsArray: Array): boolean { // iterate through keybinds in backwards order for (let index = keybindsArray.length - 1; index >= 0; index--) { let curKeybind = keybindsArray[index]; if (this.checkKeyPressed(event, curKeybind.keybinding)) { let shouldReturn = false; + let shouldRunCommand = true; if (curKeybind.callback != null) { shouldReturn = curKeybind.callback(event); + shouldRunCommand = false; } if (!shouldReturn && this.domainCallbacks.has(curKeybind.domain)) { + shouldRunCommand = false; let curDomainCallback = this.domainCallbacks.get(curKeybind.domain); if (curDomainCallback != null) { shouldReturn = curDomainCallback(event); @@ -115,6 +163,9 @@ class KeybindManager { console.log("domain callback for ", curKeybind.domain, " is null. This should never happen"); } } + if (shouldRunCommand) { + shouldReturn = this.runSlashCommand(curKeybind); + } if (shouldReturn) { nativeEvent.preventDefault(); nativeEvent.stopPropagation(); @@ -269,7 +320,7 @@ class KeybindManager { if (!this.keyDescriptionsMap.has(keyDescription)) { return false; } - let keyPressArray = this.keyDescriptionsMap.get(keyDescription); + let keyPressArray = this.keyDescriptionsMap.get(keyDescription).keys; for (let index = 0; index < keyPressArray.length; index++) { let curKeyPress = keyPressArray[index]; let pressed = checkKeyPressed(event, curKeyPress); diff --git a/wavesrv/pkg/cmdrunner/cmdrunner.go b/wavesrv/pkg/cmdrunner/cmdrunner.go index 5d4bd471..0d674efb 100644 --- a/wavesrv/pkg/cmdrunner/cmdrunner.go +++ b/wavesrv/pkg/cmdrunner/cmdrunner.go @@ -171,6 +171,9 @@ func init() { registerCmdFn("reset:cwd", ResetCwdCommand) registerCmdFn("signal", SignalCommand) registerCmdFn("sync", SyncCommand) + registerCmdFn("sleep", SleepCommand) + + registerCmdFn("mainview", MainViewCommand) registerCmdFn("session", SessionCommand) registerCmdFn("session:open", SessionOpenCommand) @@ -3560,6 +3563,46 @@ func SessionSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (s return update, nil } +func SleepCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.UpdatePacket, error) { + sleepTimeLimit := 10000 + if len(pk.Args) < 1 { + return nil, fmt.Errorf("no argument found - usage: /sleep [ms]") + } + sleepArg := pk.Args[0] + sleepArgInt, err := strconv.Atoi(sleepArg) + if err != nil { + return nil, fmt.Errorf("couldn't parse sleep arg: %v", err) + } + if sleepArgInt > sleepTimeLimit { + return nil, fmt.Errorf("sleep arg is too long, max value is %v", sleepTimeLimit) + } + time.Sleep(time.Duration(sleepArgInt) * time.Millisecond) + update := scbus.MakeUpdatePacket() + return update, nil +} + +func MainViewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.UpdatePacket, error) { + if len(pk.Args) < 1 { + return nil, fmt.Errorf("no argument found - usage: /mainview [view]") + } + update := scbus.MakeUpdatePacket() + mainViewArg := pk.Args[0] + if mainViewArg == sstore.MainViewSession { + update.AddUpdate(&sstore.MainViewUpdate{MainView: sstore.MainViewSession}) + } else if mainViewArg == sstore.MainViewConnections { + update.AddUpdate(&sstore.MainViewUpdate{MainView: sstore.MainViewConnections}) + } else if mainViewArg == sstore.MainViewSettings { + update.AddUpdate(&sstore.MainViewUpdate{MainView: sstore.MainViewSettings}) + } else if mainViewArg == sstore.MainViewHistory { + return nil, fmt.Errorf("use /history instead") + } else if mainViewArg == sstore.MainViewBookmarks { + return nil, fmt.Errorf("use /bookmarks instead") + } else { + return nil, fmt.Errorf("unrecognized main view") + } + return update, nil +} + func SessionCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.UpdatePacket, error) { ids, err := resolveUiIds(ctx, pk, 0) if err != nil { diff --git a/wavesrv/pkg/sstore/sstore.go b/wavesrv/pkg/sstore/sstore.go index ba07baa4..29459435 100644 --- a/wavesrv/pkg/sstore/sstore.go +++ b/wavesrv/pkg/sstore/sstore.go @@ -71,9 +71,11 @@ const ( ) const ( - MainViewSession = "session" - MainViewBookmarks = "bookmarks" - MainViewHistory = "history" + MainViewSession = "session" + MainViewBookmarks = "bookmarks" + MainViewHistory = "history" + MainViewConnections = "connections" + MainViewSettings = "clientsettings" ) const (