diff --git a/app/web/src/components/Workspace/CommandModal.vue b/app/web/src/components/Workspace/CommandModal.vue
index d69c909a92..5033e397f7 100644
--- a/app/web/src/components/Workspace/CommandModal.vue
+++ b/app/web/src/components/Workspace/CommandModal.vue
@@ -22,6 +22,8 @@
<socket>
[Q]ueue a <component> <action>
+ [G]oto a <view>
+ [N]ew <view>
@@ -78,6 +80,50 @@ class Pan implements Command {
}
}
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+interface GotoView extends Command {}
+class GotoView implements Command {
+ name = "Goto View";
+ shortcut = "G";
+ expects: CommandArg[] = ["view"];
+ constructor() {
+ this.choices = [];
+ }
+ execute() {
+ const viewId = this.choices[0]?.value;
+ if (!viewId) throw new Error("ViewId Expected");
+ viewStore.selectView(viewId);
+ }
+ // I can't make this static because the instance won't have a reference to it
+ // eslint-disable-next-line class-methods-use-this
+ factory() {
+ return new GotoView();
+ }
+}
+
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+interface NewView extends Command {}
+class NewView implements Command {
+ name = "New View";
+ shortcut = "N";
+ expects: CommandArg[] = ["stringInput"];
+ constructor() {
+ this.choices = [];
+ }
+ execute() {
+ const name = this.choices[0]?.value;
+ if (!name) throw new Error("Expected name");
+ viewStore.CREATE_VIEW(name).then((resp) => {
+ if (resp.result.success) viewStore.selectView(resp.result.data.id);
+ });
+ }
+ // I can't make this static because the instance won't have a reference to it
+ // eslint-disable-next-line class-methods-use-this
+ factory() {
+ return new NewView();
+ }
+}
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface Queue extends Command {}
class Queue implements Command {
@@ -141,7 +187,13 @@ class Connect implements Command {
return new Connect();
}
}
-const Commands: Command[] = [new Pan(), new Queue(), new Connect()];
+const Commands: Command[] = [
+ new Pan(),
+ new Queue(),
+ new Connect(),
+ new GotoView(),
+ new NewView(),
+];
export interface ActionBindingWithPrototype extends BindingWithDisplayName {
actionPrototypeId: NonNullable;
@@ -154,6 +206,15 @@ const setDropDown = (
const source = cmd.value?.expects[cmd.value?.choices.length];
if (!source) dropDownOptions.value = [];
switch (source) {
+ case "stringInput":
+ dropDownOptions.value = [];
+ break;
+ case "view":
+ dropDownOptions.value = Object.values(viewStore.viewList).map((c) => ({
+ label: c.name,
+ value: c.id,
+ }));
+ break;
case "component":
dropDownOptions.value = Object.values(
componentStore.rawComponentsById,
diff --git a/app/web/src/shared/CommandBar.vue b/app/web/src/shared/CommandBar.vue
index 2d18d2a434..bdd1e6cbed 100644
--- a/app/web/src/shared/CommandBar.vue
+++ b/app/web/src/shared/CommandBar.vue
@@ -219,9 +219,10 @@ const runGo = async () => {
}
};
-const input = (event?: Event) => {
+const input = (event?: KeyboardEvent) => {
event?.preventDefault();
const str = commandInput.value;
+ const numChoices = lastCmd.value?.choices.length ?? 0;
// am i setting a choice for an arg?
if (lastCmd.value) {
@@ -233,7 +234,12 @@ const input = (event?: Event) => {
if (!maybeDone.value) {
props.setDropDown(lastCmd, dropDownOptions);
}
- } else if (lastCmd.value.expects.length !== lastCmd.value.choices.length) {
+ } else if (lastCmd.value.expects.at(numChoices) === "stringInput") {
+ // typing free text, not a dropdown choice
+ if (event?.key === "Enter") {
+ if (str) lastCmd.value.choices.push({ label: str, value: str });
+ }
+ } else if (lastCmd.value.expects.length !== numChoices) {
let choice = dropDownOptions.value.find((o) => o.label === str);
if (!choice) choice = dropDownOptions.value.find((o) => o.value === str);
if (choice) lastCmd.value.choices.push(structuredClone(choice));
diff --git a/app/web/src/shared/command.ts b/app/web/src/shared/command.ts
index 403b281dc1..552f203c5f 100644
--- a/app/web/src/shared/command.ts
+++ b/app/web/src/shared/command.ts
@@ -8,7 +8,9 @@ export type CommandArg =
| "outputSocket"
| "inputSocket"
| "schema"
- | "action";
+ | "view"
+ | "action"
+ | "stringInput";
export interface Command {
readonly name: string;