Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slash Commands for keybindings #441

Merged
merged 11 commits into from
Mar 14, 2024
42 changes: 28 additions & 14 deletions assets/default-keybindings.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,22 @@
},
{
"command": "app:openConnectionsView",
"keys": []
"keys": [],
"commandStr": "/mainview connections"
},
{
"command": "app:openSettingsView",
"keys": []
"keys": [],
"commandStr": "/mainview clientsettings"
},
{
"command": "app:newTab",
"keys": ["Cmd:t"]
},
{
"command": "app:focusCmdInput",
"keys": ["Cmd:i"]
"keys": ["Cmd:i"],
"commandStr":"/mainview session,/screen:set focus=cmd,/screen:set focus=input"
Copy link
Author

@MrStashley MrStashley Mar 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using , separator, whitespace or \n is allowed and ignored

},
{
"command": "app:focusSelectedLine",
Expand Down Expand Up @@ -125,39 +128,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",
Expand All @@ -169,7 +181,8 @@
},
{
"command": "app:bookmarkActiveLine",
"keys": ["Cmd:b"]
"keys": ["Cmd:b"],
"commandStr": "/bookmarks:show"
},
{
"command": "bookmarks:edit",
Expand Down Expand Up @@ -213,7 +226,8 @@
},
{
"command": "cmdinput:openHistory",
"keys": ["Ctrl:r"]
"keys": ["Ctrl:r"],
"commandStr": "/history"
},
{
"command": "cmdinput:openAIChat",
Expand Down
45 changes: 13 additions & 32 deletions src/models/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -226,42 +226,17 @@ class Model {

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.onOpenHistoryPressed();
return true;
});

this.keybindManager.registerKeybinding("app", "model", "app:focusCmdInput", null);
this.keybindManager.registerKeybinding("app", "model", "app:bookmarkActiveLine", null);
this.keybindManager.registerKeybinding("app", "model", "app:openHistory", null);
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 {
Expand Down Expand Up @@ -1023,6 +998,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;
Expand Down
67 changes: 59 additions & 8 deletions src/util/keyutil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -24,12 +24,18 @@ const KeyTypeKey = "key";
const KeyTypeCode = "code";

type KeybindCallback = (event: WaveKeyboardEvent) => boolean;
type KeybindConfig = Array<{ command: string; keys: Array<string> }>;
type KeybindConfigArray = Array<KeybindConfig>;
type KeybindConfig = { command: string; keys: Array<string>; 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"];
Expand All @@ -38,11 +44,12 @@ class KeybindManager {
domainCallbacks: Map<string, KeybindCallback>;
levelMap: Map<string, Array<Keybind>>;
levelArray: Array<string>;
keyDescriptionsMap: Map<string, Array<string>>;
userKeybindings: KeybindConfig;
keyDescriptionsMap: Map<string, KeybindConfig>;
userKeybindings: KeybindConfigArray;
userKeybindingError: OV<string>;
globalModel: any;

constructor() {
constructor(GlobalModel: any) {
this.levelMap = new Map();
this.domainCallbacks = new Map();
this.levelArray = KeybindLevels;
Expand All @@ -53,6 +60,7 @@ class KeybindManager {
this.userKeybindingError = mobx.observable.box(null, {
name: "keyutil-userKeybindingError",
});
this.globalModel = GlobalModel;
this.initKeyDescriptionsMap();
}

Expand All @@ -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) {
Expand All @@ -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}`;
Expand All @@ -98,23 +114,58 @@ 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<string>): 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<Keybind>): 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);
} else {
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();
Expand Down Expand Up @@ -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);
Expand Down
42 changes: 42 additions & 0 deletions wavesrv/pkg/cmdrunner/cmdrunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -3560,6 +3563,45 @@ func SessionSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (s
return update, nil
}

func SleepCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (scbus.UpdatePacket, error) {
Copy link
Author

@MrStashley MrStashley Mar 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this
For my slash command "scripts" it waits until the http call returns before running the next one, there is no update or anything, so for now at least, sleep has to lock the current process. I think this isn't too big a deal because we are already running inside of a goroutine? but either way I didn't end up using this so I can delete it if it seems sketchy
Added a max value of 1000 ms

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 > 1000 {
return nil, fmt.Errorf("sleep arg is too long, max value is 1000")
}
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 {
Expand Down
8 changes: 5 additions & 3 deletions wavesrv/pkg/sstore/sstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,11 @@ const (
)

const (
MainViewSession = "session"
MainViewBookmarks = "bookmarks"
MainViewHistory = "history"
MainViewSession = "session"
MainViewBookmarks = "bookmarks"
MainViewHistory = "history"
MainViewConnections = "connections"
MainViewSettings = "clientsettings"
)

const (
Expand Down
Loading