M7: editor keyboard + one-click card toolbars (live-pass fixes)
From the unified-editor live pass (task 1922): - Esc now returns to the base Notes board from any view (AppShell); an open editor consumes Esc first (its handler stops propagation) so it closes before navigating. - Ctrl/Cmd+Enter = finish & close in BOTH frames — it already closed the modal editor; now it also commits+collapses the board composer (matches the email/chat 'send' convention). Shift+Enter still saves & starts a new note. - Fix: the inline composer committed on document mousedown, collapsing and shifting the board before a click landed — so clicking another card's pin/archive/trash took two clicks. Commit on click (bubble phase) instead; the target's action fires first, then the composer closes. Pure frontend. 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:
@@ -36,9 +36,9 @@ const shortcuts = [
|
|||||||
{ label: "Move card focus", keys: ["j", "k"] },
|
{ label: "Move card focus", keys: ["j", "k"] },
|
||||||
{ label: "Open focused card", keys: ["Enter"] },
|
{ label: "Open focused card", keys: ["Enter"] },
|
||||||
{ label: "Pin / archive / trash card", keys: ["#", "e", "x"] },
|
{ label: "Pin / archive / trash card", keys: ["#", "e", "x"] },
|
||||||
{ label: "Save & close (editor)", keys: ["⌘/Ctrl", "Enter"] },
|
{ label: "Finish & close note", keys: ["⌘/Ctrl", "Enter"] },
|
||||||
{ label: "Save & new (composer)", keys: ["Shift", "Enter"] },
|
{ label: "Save & new (composer)", keys: ["Shift", "Enter"] },
|
||||||
{ label: "Dismiss / close", keys: ["Esc"] },
|
{ label: "Close / back to Notes", keys: ["Esc"] },
|
||||||
{ label: "This help", keys: ["?"] },
|
{ label: "This help", keys: ["?"] },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -84,6 +84,12 @@ function onKeydown(e: KeyboardEvent) {
|
|||||||
drawer.value = false;
|
drawer.value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Esc with nothing open = back to the base Notes board. (An open editor gets Esc
|
||||||
|
// first via its own handler, which stops propagation so it doesn't also navigate.)
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
if (route.name !== "board") void router.push("/");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
||||||
if (gPending) {
|
if (gPending) {
|
||||||
gPending = false;
|
gPending = false;
|
||||||
|
|||||||
@@ -192,12 +192,17 @@ function onEsc(): void {
|
|||||||
else void close();
|
else void close();
|
||||||
}
|
}
|
||||||
function onMetaEnter(): void {
|
function onMetaEnter(): void {
|
||||||
if (!props.inline) void close();
|
// Ctrl/Cmd+Enter = finish & close, in both frames (matches email/chat "send").
|
||||||
|
if (props.inline) void commitInline();
|
||||||
|
else void close();
|
||||||
}
|
}
|
||||||
function onBackdropMousedown(): void {
|
function onBackdropMousedown(): void {
|
||||||
if (!props.inline) void close();
|
if (!props.inline) void close();
|
||||||
}
|
}
|
||||||
function onDocMousedown(e: MouseEvent): void {
|
function onDocClick(e: MouseEvent): void {
|
||||||
|
// Commit the inline composer on an OUTSIDE *click* (bubble phase, not mousedown):
|
||||||
|
// the clicked target's own handler fires first, so clicking another card's toolbar
|
||||||
|
// performs its action, THEN the composer collapses — no wasted first click.
|
||||||
if (props.inline && expanded.value && root.value && !root.value.contains(e.target as Node)) {
|
if (props.inline && expanded.value && root.value && !root.value.contains(e.target as Node)) {
|
||||||
void commitInline();
|
void commitInline();
|
||||||
}
|
}
|
||||||
@@ -230,14 +235,14 @@ onMounted(async () => {
|
|||||||
void titles.load();
|
void titles.load();
|
||||||
void loadBacklinks();
|
void loadBacklinks();
|
||||||
if (props.inline) {
|
if (props.inline) {
|
||||||
document.addEventListener("mousedown", onDocMousedown);
|
document.addEventListener("click", onDocClick);
|
||||||
if (props.autofocus) void open();
|
if (props.autofocus) void open();
|
||||||
} else {
|
} else {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
bodyInput.value?.focus();
|
bodyInput.value?.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
onBeforeUnmount(() => document.removeEventListener("mousedown", onDocMousedown));
|
onBeforeUnmount(() => document.removeEventListener("click", onDocClick));
|
||||||
|
|
||||||
// ---- outgoing links (edit mode) ----
|
// ---- outgoing links (edit mode) ----
|
||||||
const outgoingLinks = computed(() => {
|
const outgoingLinks = computed(() => {
|
||||||
@@ -489,7 +494,7 @@ defineExpose({ open });
|
|||||||
"
|
"
|
||||||
:role="inline ? undefined : 'dialog'"
|
:role="inline ? undefined : 'dialog'"
|
||||||
:aria-modal="inline ? undefined : 'true'"
|
:aria-modal="inline ? undefined : 'true'"
|
||||||
@keydown.esc="onEsc"
|
@keydown.esc.stop="onEsc"
|
||||||
@keydown.enter.meta.prevent="onMetaEnter"
|
@keydown.enter.meta.prevent="onMetaEnter"
|
||||||
@keydown.enter.ctrl.prevent="onMetaEnter"
|
@keydown.enter.ctrl.prevent="onMetaEnter"
|
||||||
@paste="onPaste"
|
@paste="onPaste"
|
||||||
|
|||||||
Reference in New Issue
Block a user