From fdaf5c370c13e23f9d080ffea20c631f0429bd7d Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sun, 19 Jul 2026 21:31:48 -0400 Subject: [PATCH] QoL: Shift+Enter save-and-continue + autofocus quick-add on landing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Shift+Enter in the quick-add title/body saves the current note and immediately starts a fresh one, staying focused for rapid capture. Plain Enter is still a newline (body) / title→body (title). Placeholder hints the shortcut. - Landing on the board auto-opens + focuses the note input (QuickAdd autofocus prop, passed from BoardView) so you can start typing on arrival. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm --- frontend/src/components/QuickAdd.vue | 55 ++++++++++++++++++++-------- frontend/src/views/BoardView.vue | 2 +- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/frontend/src/components/QuickAdd.vue b/frontend/src/components/QuickAdd.vue index bac9092..7fa3724 100644 --- a/frontend/src/components/QuickAdd.vue +++ b/frontend/src/components/QuickAdd.vue @@ -4,6 +4,8 @@ import { useNotesStore } from "../stores/notes"; import ColorPicker from "./ColorPicker.vue"; import type { NoteColor } from "../notes/colors"; +const props = withDefaults(defineProps<{ autofocus?: boolean }>(), { autofocus: false }); + const notes = useNotesStore(); const expanded = ref(false); @@ -20,24 +22,41 @@ async function open() { bodyInput.value?.focus(); } -function reset() { - expanded.value = false; +function hasContent(): boolean { + return title.value.trim() !== "" || body.value.trim() !== ""; +} + +function clearFields() { title.value = ""; body.value = ""; color.value = "default"; } -async function commit() { - const hasContent = title.value.trim() !== "" || body.value.trim() !== ""; - if (hasContent) { - saving.value = true; - try { - await notes.create({ title: title.value, body: body.value, color: color.value }); - } finally { - saving.value = false; - } +async function persist(): Promise { + if (!hasContent()) return; + saving.value = true; + try { + await notes.create({ title: title.value, body: body.value, color: color.value }); + } finally { + saving.value = false; } - reset(); +} + +// Close the composer, saving anything typed. +async function commit() { + await persist(); + clearFields(); + expanded.value = false; +} + +// Shift+Enter: save the current note and immediately start a fresh one, staying +// open + focused for rapid-fire capture. +async function commitAndContinue() { + if (!hasContent()) return; + await persist(); + clearFields(); + await nextTick(); + bodyInput.value?.focus(); } function onDocumentMousedown(e: MouseEvent) { @@ -46,7 +65,11 @@ function onDocumentMousedown(e: MouseEvent) { } } -onMounted(() => document.addEventListener("mousedown", onDocumentMousedown)); +onMounted(() => { + document.addEventListener("mousedown", onDocumentMousedown); + // Landing on the board drops the cursor straight into the note input. + if (props.autofocus) void open(); +}); onBeforeUnmount(() => document.removeEventListener("mousedown", onDocumentMousedown)); @@ -68,14 +91,16 @@ onBeforeUnmount(() => document.removeEventListener("mousedown", onDocumentMoused type="text" placeholder="Title" class="w-full bg-transparent px-1 text-sm font-semibold outline-none placeholder:text-neutral-400" - @keydown.enter.prevent="bodyInput?.focus()" + @keydown.enter.shift.prevent="commitAndContinue" + @keydown.enter.exact.prevent="bodyInput?.focus()" />