Capture: Enter-to-start + on-board notice; dismiss discards a new note
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:
@@ -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"] },
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user