Repository: kestra-io/kestra
Issue: Add CTRL-X shortcut to execute a flow
Labels: enhancement

### Feature description

While editing a flow, you can save it with CTRL-S but not execute it.
Add CTRL-X to execute it (CTRL-E is already used in Chrome), then make the execution form to be submitable with ENTER.
Whith both these, you can edit your flow then CTRL-S -> CTRL-X -> ENTER

Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/ui/src/components/flows/FlowRun.vue b/ui/src/components/flows/FlowRun.vue
index fdc17f460c..4ab793d556 100644
--- a/ui/src/components/flows/FlowRun.vue
+++ b/ui/src/components/flows/FlowRun.vue
@@ -5,7 +5,7 @@
             {{ $t('disabled flow desc') }}
         </el-alert>
 
-        <el-form label-position="top" :model="inputs" ref="form" @submit.prevent>
+        <el-form label-position="top" :model="inputs" ref="form" @submit.prevent="onSubmit($refs.form)">
             <el-form-item
                 v-for="input in flow.inputs || []"
                 :key="input.id"
@@ -96,7 +96,7 @@
                 </div>
                 <div class="right-align">
                     <el-form-item class="submit">
-                        <el-button :icon="Flash" class="flow-run-trigger-button" @click="onSubmit($refs.form)" type="primary" :disabled="flow.disabled || haveBadLabels">
+                        <el-button :icon="Flash" class="flow-run-trigger-button" @click="onSubmit($refs.form)" type="primary" native-type="submit" :disabled="flow.disabled || haveBadLabels">
                             {{ $t('launch execution') }}
                         </el-button>
                         <el-text v-if="haveBadLabels" type="danger" size="small">
diff --git a/ui/src/components/inputs/Editor.vue b/ui/src/components/inputs/Editor.vue
index 7cf274649b..af57dc0688 100644
--- a/ui/src/components/inputs/Editor.vue
+++ b/ui/src/components/inputs/Editor.vue
@@ -85,7 +85,7 @@
         components: {
             MonacoEditor,
         },
-        emits: ["save", "focusout", "tab", "update:modelValue", "cursor", "restartGuidedTour"],
+        emits: ["save", "execute", "focusout", "tab", "update:modelValue", "cursor", "restartGuidedTour"],
         editor: undefined,
         data() {
             return {
@@ -230,6 +230,19 @@
                     }
                 });
 
+                this.editor.addAction({
+                    id: "kestra-execute",
+                    label: "Execute the flow",
+                    keybindings: [
+                        KeyMod.CtrlCmd | KeyCode.KeyE,
+                    ],
+                    contextMenuGroupId: "navigation",
+                    contextMenuOrder: 1.5,
+                    run: (ed) => {
+                        this.$emit("execute", ed.getValue())
+                    }
+                });
+
                 if (this.input) {
                     this.editor.addCommand(KeyMod.CtrlCmd | KeyCode.KeyF, () => {
                     })
diff --git a/ui/src/components/inputs/EditorView.vue b/ui/src/components/inputs/EditorView.vue
index 1615b00103..4449dace58 100644
--- a/ui/src/components/inputs/EditorView.vue
+++ b/ui/src/components/inputs/EditorView.vue
@@ -121,6 +121,7 @@
     const isLoading = ref(false);
     const haveChange = ref(false)
     const flowYaml = ref("")
+    const triggerFlow = ref(null);
     const newTrigger = ref(null)
     const isNewTriggerOpen = ref(false)
     const newError = ref(null)
@@ -521,6 +522,13 @@
         })
     };
 
+    const execute = (_) => {
+        if (!triggerFlow.value) {
+            return;
+        }
+        triggerFlow.value.onClick();
+    };
+
     const canDelete = () => {
         return (
             user.isAllowed(
@@ -608,6 +616,7 @@
             :class="combinedEditor ? 'editor-combined' : ''"
             :style="combinedEditor ? {width: editorWidthPercentage} : {}"
             @save="save"
+            @execute="execute"
             v-model="flowYaml"
             schema-type="flow"
             lang="yaml"
@@ -793,6 +802,7 @@
             <li v-if="flow">
                 <trigger-flow
                     v-if="!props.isCreating"
+                    ref="triggerFlow"
                     type="default"
                     :disabled="flow.disabled"
                     :flow-id="flow.id"


Test patch: