From 1df70c53bd8e15ced9a3dc5fd16744b9009a7aa4 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 23 Jul 2026 14:11:11 -0400 Subject: [PATCH] Capture: Enter-to-start + on-board notice; dismiss discards a new note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live-pass feedback: make the "waiting for input" state visible and starting a note more deliberate, and let dismiss cancel an accidental note. - New notes are now a confirm-to-keep dialog: Esc OR click-away DISCARD a brand-new, not-yet-persisted note (so an accidental keystroke / type-to- compose never litters); Ctrl/Cmd+Enter, the footer button, or Shift+Enter commit it. A compose already persisted by a rich action, and any existing note, still close-and-save on dismiss. The compose footer button is now a filled "Add note" so the save path is unmistakable. - Enter (board, nothing focused) starts a new note; a subtle dashed on-board notice — "Press Enter or start typing to add a note" — makes capture discoverable instead of silent (click it to compose too). Type-to-compose stays as the fast path. - Empty-state copy + shortcuts help updated (Enter/c for a new note). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm --- frontend/src/components/AppShell.vue | 2 +- frontend/src/components/NoteEditor.vue | 25 ++++++++++++++++++++----- frontend/src/views/BoardView.vue | 21 +++++++++++++++++++-- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/AppShell.vue b/frontend/src/components/AppShell.vue index eeee2d6..05042d4 100644 --- a/frontend/src/components/AppShell.vue +++ b/frontend/src/components/AppShell.vue @@ -42,7 +42,7 @@ let searchTimer: ReturnType | undefined; const shortcuts = [ { label: "Command palette", keys: ["⌘/Ctrl", "K"] }, - { label: "New note (or just start typing)", keys: ["c"] }, + { label: "New note (or just start typing)", keys: ["Enter", "c"] }, { label: "Search", keys: ["/"] }, { label: "Go to Board", keys: ["g", "b"] }, { label: "Go to Graph", keys: ["g", "g"] }, diff --git a/frontend/src/components/NoteEditor.vue b/frontend/src/components/NoteEditor.vue index c7b2e6e..03b78a3 100644 --- a/frontend/src/components/NoteEditor.vue +++ b/frontend/src/components/NoteEditor.vue @@ -178,15 +178,26 @@ async function close(): Promise { await flush(); emit("close"); } +// Esc / click-away DISMISS. A brand-new, not-yet-persisted note is DISCARDED — so an +// accidental keystroke or type-to-compose never litters the board. To keep a new note, +// commit it explicitly (Done, Ctrl/Cmd+Enter, or Shift+Enter). An existing note, or a +// compose already persisted by a rich action, closes normally (saving its text). +async function dismiss(): Promise { + if (isCreate.value) { + emit("close"); + return; + } + await close(); +} function onEsc(): void { - void close(); + void dismiss(); } function onMetaEnter(): void { - // Ctrl/Cmd+Enter = finish & close (matches email/chat "send"). + // Ctrl/Cmd+Enter = finish & close, an explicit commit (matches email/chat "send"). void close(); } function onBackdropMousedown(): void { - void close(); + void dismiss(); } async function loadBacklinks(): Promise { @@ -875,11 +886,15 @@ function revPreview(rev: NoteRevision): string { diff --git a/frontend/src/views/BoardView.vue b/frontend/src/views/BoardView.vue index d42dfbc..6128467 100644 --- a/frontend/src/views/BoardView.vue +++ b/frontend/src/views/BoardView.vue @@ -94,7 +94,14 @@ function onBoardKey(e: KeyboardEvent) { moveFocus(-1); return; } - if (!browsing) return; // resting → let AppShell's type-to-compose take the key + if (!browsing) { + // Resting: Enter starts a new note; other keys fall to AppShell's type-to-compose. + if (key === "Enter") { + e.preventDefault(); + ui.requestCompose(); + } + return; + } if (key === "Escape") { e.preventDefault(); focusedIndex.value = -1; // leave browse mode, back to type-to-capture @@ -135,7 +142,7 @@ const emptyState = computed(() => { if (currentView.value === "archived") return { title: "Nothing archived", subtitle: "Archived notes are tucked away here." }; if (currentLabel.value) return { title: "No notes with this label", subtitle: "Tag a note to see it here." }; - return { title: "No notes yet", subtitle: "Hit + New — or just start typing — to capture a thought." }; + return { title: "No notes yet", subtitle: "Your captured thoughts will show up here." }; }); async function reload() { @@ -199,6 +206,16 @@ async function onDrop(target: Note) {