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;