Focus restoration:
* editor.js closeModal(): blur activeElement after the close so board
shortcuts (arrows, bare keys) work immediately without clicking.
* detail.js closeDetail(): same blur, skipped when openedFromSearch
(search input should keep focus in that case).
* search.js closeSearch(): blur after removing .open.
* calendar.js closeCalendar(): blur after closeDayPopup.
All four check activeElement !== body before blurring to avoid no-ops.
Test shield (loader.js):
* ensureProgressBar() now also creates #test-shield — a transparent
fixed overlay (z-index one below the progress bar) covering the
viewport below the bar. Blocks all real mouse interaction while
tests run; synthetic events from the test suite still work because
they bypass pointer-events. cursor:not-allowed as visual feedback.
Removed by hideProgress() alongside the progress bar.
ESC closes test results (runner.js):
* The existing keydown listener now also handles Escape when the
test-panel is visible. stopPropagation prevents the app's Escape
chain from firing a second close underneath.
Tests (board.test.js):
* New describe "Board: Focus restoration after overlay close":
- closing the editor returns focus to body
- closing the detail view returns focus to body
- closing search returns focus to body
- arrow keys work after editor round-trip (enter → enter → escape)
— the exact flow the user reported as broken. Dispatches
ArrowDown → Enter (detail) → Enter (editor) → Escape (close) →
asserts body focus → ArrowDown → asserts .card-focused exists.
* Fixed .not.toBe() → (x !== y).toBe(true) since the custom runner
doesn't have a .not chain.
chore: bump version to 0.9.14
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds the cursor-blink class to "no X" / "select a Y" placeholders across
core (calendar popup, board preview, search preview, detail split, notifs)
and plugins (chat, email, gitea, slideshow). Visual consistency with the
rest of the pixel-font UI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>