QoL: Shift+Enter save-and-continue + autofocus quick-add on landing
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 5s
CI & Build / Python tests (push) Successful in 8s
CI & Build / Build & push image (push) Successful in 42s

- 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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
2026-07-19 21:31:48 -04:00
co-authored by Claude Opus 4.8
parent 2ae533e967
commit fdaf5c370c
2 changed files with 41 additions and 16 deletions
+40 -15
View File
@@ -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<void> {
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));
</script>
@@ -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()"
/>
<textarea
ref="bodyInput"
v-model="body"
rows="3"
placeholder="Take a note…"
placeholder="Take a note… (Shift+Enter saves &amp; starts a new one)"
class="w-full resize-none bg-transparent px-1 text-sm outline-none placeholder:text-neutral-400"
@keydown.enter.shift.prevent="commitAndContinue"
@keydown.esc="commit"
/>
<div class="flex items-center justify-between gap-2 pt-1">
+1 -1
View File
@@ -112,7 +112,7 @@ async function signOut() {
</header>
<main class="mx-auto w-full max-w-6xl flex-1 px-4 py-6">
<QuickAdd v-if="currentView === 'active'" class="mb-8" />
<QuickAdd v-if="currentView === 'active'" autofocus class="mb-8" />
<div v-if="notes.loading" class="py-24 text-center text-sm text-neutral-400">Loading</div>