feat(editor): split display into writing column and side panel

Carve the panel into a ~60-col writing column and a right-hand side
panel (per CONTEXT.md screen regions); the divider sits right of the
x=396 driver seam. The mode indicator and pending-command echo move
into the panel, while the ':' command line stays in the bottom strip.

The panel also shows word count and words-this-session (a snapshot
refreshed on a typing pause / non-Insert action, never per keystroke,
so ordinary typing keeps the fast windowed refresh) and a "NO KBD"
flag while the keyboard is dropped.
This commit is contained in:
Julien Calixte
2026-07-08 21:33:41 +02:00
parent ddd9c387d3
commit 50f650fef6
2 changed files with 152 additions and 34 deletions

View File

@@ -66,6 +66,12 @@ fn main() -> anyhow::Result<()> {
let mut cursor_shown = true; // the initial render includes the caret
let mut last_activity = Instant::now();
// Keyboard attach/detach state drives the panel's disconnect flag; seed it
// (and the word-count snapshot) before the first render.
let mut last_kbd = usb_kbd::keyboard_present();
ed.set_keyboard_present(last_kbd);
ed.refresh_stats();
// First render is full (establishes the on-screen baseline for partials).
let mut shown = ed.draw(true);
epd.display_frame(shown.bytes())?;
@@ -79,14 +85,31 @@ fn main() -> anyhow::Result<()> {
keys += 1;
}
// Keyboard attach/detach feeds the panel's disconnect flag.
let kbd = usb_kbd::keyboard_present();
ed.set_keyboard_present(kbd);
let kbd_changed = kbd != last_kbd;
last_kbd = kbd;
if keys == 0 {
// A connect/disconnect while idle must still repaint the panel flag —
// no keystroke will arrive to trigger it otherwise.
if kbd_changed {
let f = ed.draw(true);
epd.display_frame_partial_window(f.bytes(), 0, epd::HEIGHT)?;
shown = f;
cursor_shown = true;
log::info!("keyboard {}", if kbd { "connected" } else { "disconnected" });
continue;
}
// Debounced caret, Insert mode only: once typing pauses, bring the
// bar caret back with a silent full-area partial (no flash).
// Normal/View already draw their caret on every action.
// bar caret back and refresh the panel word count with a silent
// full-area partial (no flash). Normal/View draw their caret on action.
if ed.mode() == Mode::Insert
&& !cursor_shown
&& last_activity.elapsed().as_millis() >= CURSOR_DEBOUNCE_MS
{
ed.refresh_stats();
let f = ed.draw(true);
epd.display_frame_partial_window(f.bytes(), 0, epd::HEIGHT)?;
shown = f;
@@ -99,6 +122,12 @@ fn main() -> anyhow::Result<()> {
}
last_activity = Instant::now();
// Non-Insert actions (Normal edits, mode switches) aren't rapid typing,
// so the panel word count can refresh immediately; in Insert the snapshot
// stays frozen until the typing-pause path above refreshes it.
if ed.mode() != Mode::Insert {
ed.refresh_stats();
}
// Suppress the Insert bar caret while typing (fast, no ghost); Normal
// and View render their caret regardless of this flag.
let insert_cursor_on = ed.mode() != Mode::Insert;