keyboard: board card navigation (j/k/arrows + actions)
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 6s
CI & Build / Python tests (push) Successful in 16s
CI & Build / Build & push image (push) Successful in 34s

Focused-card selection on the board: j/k or arrows move it, Enter opens,
e archives, # pins, x trashes the focused card (actions gated off the Trash
view). The selection is a brand ring on the card; it scrolls into view and
clamps as the list/view changes. A window keydown listener on BoardView,
ignored while typing, on a button/link, or while the editor modal is open.
Added the card keys to the ? cheat-sheet.

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-20 11:18:28 -04:00
co-authored by Claude Opus 4.8
parent b24316843e
commit 4c72dfea0b
3 changed files with 85 additions and 7 deletions
+13 -2
View File
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import { useNotesStore } from "../stores/notes";
import { NOTE_CARD_CLASSES, type NoteColor } from "../notes/colors";
import type { Note } from "../stores/notes";
@@ -7,7 +8,7 @@ import LinkedText from "./LinkedText.vue";
import NoteChecklist from "./NoteChecklist.vue";
import { formatReminder, isOverdue } from "../notes/datetime";
defineProps<{ note: Note; reorderable?: boolean }>();
const props = defineProps<{ note: Note; reorderable?: boolean; active?: boolean }>();
const emit = defineEmits<{
(e: "open", note: Note): void;
(e: "dragstart", note: Note): void;
@@ -15,6 +16,15 @@ const emit = defineEmits<{
}>();
const notes = useNotesStore();
// When this card becomes the keyboard-focused card, scroll it into view.
const root = ref<HTMLElement | null>(null);
watch(
() => props.active,
(a) => {
if (a) root.value?.scrollIntoView({ block: "nearest", behavior: "smooth" });
},
);
function cardClass(color: NoteColor): string {
return NOTE_CARD_CLASSES[color] ?? NOTE_CARD_CLASSES.default;
}
@@ -22,8 +32,9 @@ function cardClass(color: NoteColor): string {
<template>
<div
ref="root"
class="group relative mb-4 break-inside-avoid rounded-xl border p-3 shadow-sm transition hover:shadow-md"
:class="cardClass(note.color)"
:class="[cardClass(note.color), active ? 'ring-2 ring-brand' : '']"
:draggable="reorderable && !note.trashed"
@dragstart="emit('dragstart', note)"
@dragover.prevent