CMD+shift+enter should create task without start (vibe-kanban) (#1317)

* Done! CMD+Shift+Enter will now create tasks without starting them.

**Changes made:**
1. Added `useKeySubmitTaskAlt` import
2. Added `forceCreateOnlyRef` to track create-only mode
3. Updated validator to skip executor/branch requirements when ref is true
4. Updated submit logic to check `autoStart && !forceCreateOnlyRef.current`
5. Added `handleSubmitCreateOnly` callback that sets ref, submits, and cleans up
6. Bound CMD+Shift+Enter with simpler validation (just needs title)

All type checks pass ✓

* Cleanup script changes for task attempt 2eb2be96-3b6c-4471-9964-3cff2dc9feef
This commit is contained in:
Louis Knight-Webb
2025-11-18 11:17:10 +00:00
committed by GitHub
parent 1dae217f1a
commit b04672d776

View File

@@ -38,7 +38,12 @@ import {
useImageUpload, useImageUpload,
useTaskMutations, useTaskMutations,
} from '@/hooks'; } from '@/hooks';
import { useKeySubmitTask, useKeyExit, Scope } from '@/keyboard'; import {
useKeySubmitTask,
useKeySubmitTaskAlt,
useKeyExit,
Scope,
} from '@/keyboard';
import { useHotkeysContext } from 'react-hotkeys-hook'; import { useHotkeysContext } from 'react-hotkeys-hook';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import type { import type {
@@ -96,6 +101,7 @@ const TaskFormDialogImpl = NiceModal.create<TaskFormDialogProps>((props) => {
const [showDiscardWarning, setShowDiscardWarning] = useState(false); const [showDiscardWarning, setShowDiscardWarning] = useState(false);
const imageUploadRef = useRef<ImageUploadSectionHandle>(null); const imageUploadRef = useRef<ImageUploadSectionHandle>(null);
const [pendingFiles, setPendingFiles] = useState<File[] | null>(null); const [pendingFiles, setPendingFiles] = useState<File[] | null>(null);
const forceCreateOnlyRef = useRef(false);
const { data: branches, isLoading: branchesLoading } = const { data: branches, isLoading: branchesLoading } =
useProjectBranches(projectId); useProjectBranches(projectId);
@@ -184,7 +190,8 @@ const TaskFormDialogImpl = NiceModal.create<TaskFormDialogProps>((props) => {
image_ids: imageIds, image_ids: imageIds,
shared_task_id: null, shared_task_id: null,
}; };
if (value.autoStart) { const shouldAutoStart = value.autoStart && !forceCreateOnlyRef.current;
if (shouldAutoStart) {
await createAndStart.mutateAsync( await createAndStart.mutateAsync(
{ {
task, task,
@@ -201,7 +208,11 @@ const TaskFormDialogImpl = NiceModal.create<TaskFormDialogProps>((props) => {
const validator = (value: TaskFormValues): string | undefined => { const validator = (value: TaskFormValues): string | undefined => {
if (!value.title.trim().length) return 'need title'; if (!value.title.trim().length) return 'need title';
if (value.autoStart && (!value.executorProfileId || !value.branch)) { if (
value.autoStart &&
!forceCreateOnlyRef.current &&
(!value.executorProfileId || !value.branch)
) {
return 'need executor profile or branch;'; return 'need executor profile or branch;';
} }
}; };
@@ -309,6 +320,26 @@ const TaskFormDialogImpl = NiceModal.create<TaskFormDialogProps>((props) => {
preventDefault: true, preventDefault: true,
}); });
const canSubmitAlt = useStore(
form.store,
(state) => state.values.title.trim().length > 0 && !state.isSubmitting
);
const handleSubmitCreateOnly = useCallback(() => {
forceCreateOnlyRef.current = true;
const promise = form.handleSubmit();
Promise.resolve(promise).finally(() => {
forceCreateOnlyRef.current = false;
});
}, [form]);
useKeySubmitTaskAlt(handleSubmitCreateOnly, {
enabled: modal.visible && canSubmitAlt && !showDiscardWarning,
scope: Scope.DIALOG,
enableOnFormTags: ['input', 'INPUT', 'textarea', 'TEXTAREA'],
preventDefault: true,
});
// Dialog close handling // Dialog close handling
const handleDialogClose = (open: boolean) => { const handleDialogClose = (open: boolean) => {
if (open) return; if (open) return;