Files
FND 1d30b973c4 feat(editor): Mod+E shortcut to enter and leave Edit mode in place (#1492)
* feat(editor): Mod+E toggles markdown edit mode in place (#1479)

* fix(editor): scope the edit-mode exit chord and wire its tests into CI

- Early-return the dedicated Mod+E exit listener when the event target is
  an INPUT/TEXTAREA outside .cm-editor, so native text fields (Ask AI box,
  annotation textareas) keep their own Mod+E instead of committing and
  exiting the edit session underneath them; match the key case-insensitively
  so Caps Lock cannot dead-key the exit. CodeMirror's content DOM is
  contenteditable, so the editor path is untouched. Also note in code that
  mounting atomic-editor's selectionToolbar() would claim Mod-e and dead-key
  the chord.
- New DOM test pinning the ownership split (foreign textarea keeps the
  session open; the chord from inside CodeMirror still exits),
  revert-verified against the guard.
- De-flake "never discards an unsaved source-backed buffer silently":
  the single setTimeout(0) after a contentEditable DOM mutation raced
  CodeMirror's MutationObserver, which happy-dom intermittently never
  re-delivers after CM's stop()/start() observer cycling — the buffer is
  now dirtied through the editor's own dispatch, with a bounded poll for
  the toolstrip's Save readout. 8/8 stable under CI's exact invocation
  preceded by another App suite (previously ~3/8 failures).
- Add App.editModeShortcut.test.tsx to the DOM-gated file enumeration in
  .github/workflows/test.yml so the suite actually runs in CI.

---------

Co-authored-by: Michael Ramos <mdramos8@gmail.com>
2026-09-10 16:43:47 -07:00

40 lines
1.7 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { scrollableEditSurface } from "./editScroll";
const scrollElement = (scrollHeight: number, clientHeight: number): HTMLElement =>
({ scrollHeight, clientHeight }) as unknown as HTMLElement;
describe("scrollableEditSurface", () => {
// The desktop layout: CodeMirror grows to full content height inside the
// document viewport, so `.cm-scroller` has nothing to scroll. Restoring the
// offset into it would silently lose the reader's place (#1479).
test("falls back to the document viewport when the editor cannot scroll", () => {
const viewport = scrollElement(8_204, 1_055);
const scroller = scrollElement(8_106, 8_106);
expect(scrollableEditSurface(scroller, viewport, true)).toBe(viewport);
});
// A height-bounded shell (compact touch) bounds the editor instead, so the
// offset lives inside CodeMirror.
test("uses the editor scroller when the shell bounds the editor", () => {
const viewport = scrollElement(700, 700);
const scroller = scrollElement(2_000, 600);
expect(scrollableEditSurface(scroller, viewport, true)).toBe(scroller);
});
// Capturing on the way in happens while the viewer is still mounted: the
// editor's scroller is stale there, and the viewer's offset is the one to keep.
test("always reads the document viewport while not editing", () => {
const viewport = scrollElement(8_204, 1_055);
const scroller = scrollElement(2_000, 600);
expect(scrollableEditSurface(scroller, viewport, false)).toBe(viewport);
});
test("stays null when the viewport is unavailable", () => {
expect(scrollableEditSurface(null, null, true)).toBeNull();
});
});