Capture: Enter-to-start + on-board notice; dismiss discards a new note
CI & Build / Python lint (push) Successful in 4s
CI & Build / TypeScript typecheck (push) Successful in 6s
CI & Build / Python tests (push) Successful in 8s
CI & Build / Build & push image (push) Successful in 33s

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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
2026-07-23 14:11:11 -04:00
co-authored by Claude Opus 4.8
parent 675b3aa248
commit 1df70c53bd
3 changed files with 40 additions and 8 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ let searchTimer: ReturnType<typeof setTimeout> | 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"] },
+20 -5
View File
@@ -178,15 +178,26 @@ async function close(): Promise<void> {
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<void> {
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<void> {
@@ -875,11 +886,15 @@ function revPreview(rev: NoteRevision): string {
</template>
<button
type="button"
class="rounded-md px-3 py-1.5 text-sm font-semibold text-neutral-700 hover:bg-neutral-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand disabled:opacity-60 dark:text-neutral-200 dark:hover:bg-neutral-800"
:class="
isCreate
? 'rounded-md bg-brand px-3 py-1.5 text-sm font-semibold text-neutral-900 hover:brightness-95 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand disabled:opacity-60'
: 'rounded-md px-3 py-1.5 text-sm font-semibold text-neutral-700 hover:bg-neutral-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand disabled:opacity-60 dark:text-neutral-200 dark:hover:bg-neutral-800'
"
:disabled="saving"
@click="close()"
>
{{ isCreate ? "Done" : "Close" }}
{{ isCreate ? "Add note" : "Close" }}
</button>
</div>
</div>
+19 -2
View File
@@ -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) {
<template>
<div class="mx-auto w-full max-w-6xl px-4 py-6">
<button
v-if="isMainBoard"
type="button"
class="mx-auto mb-4 block w-full max-w-xl rounded-xl border border-dashed border-neutral-300 px-4 py-2.5 text-center text-sm text-neutral-400 transition hover:border-neutral-400 hover:text-neutral-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand dark:border-neutral-700 dark:hover:border-neutral-500"
@click="ui.requestCompose()"
>
Press
<kbd class="mx-0.5 rounded border border-neutral-300 px-1 text-xs dark:border-neutral-600">Enter</kbd>
or start typing to add a note
</button>
<FilterBar v-if="isMainBoard" />
<div v-if="notes.loading" class="py-24 text-center text-sm text-neutral-400">Loading</div>