zz_

app/tui/fullscreen.ts

zzmq.dev@gmail.com/zz / app / tui / fullscreen.ts @ 2a73adc2 Download
Ladder Summary
  • file The module provides two exported functions, `enterFullscreen()` and `exitFullscreen()`, that toggle the terminal into and out of an alternate‑screen “full‑screen” mode: on entry it switches to the alternate buffer, clears the screen, hides the cursor, and enables mouse‑click/wheel reporting (SGR encoding), while also installing one‑time process‑exit/SIGINT/SIGTERM handlers that invoke `exitFullscreen()`. `exitFullscreen()` reverses those changes—disabling mouse reporting, showing the cursor, and restoring the normal screen buffer—ensuring the shell’s display is left clean whenever the app quits or is interrupted.
 // Puts the terminal into its alternate screen buffer for the life of
 // the process, the way a real full-screen app (less, vim, Textual's own
 // apps) does -- so the TUI occupies a stable viewport instead of
 // printing into (and growing) the shell's normal scrollback. Restores
 // the original screen on any exit path so the shell is left clean.
 //
 // Also enables mouse reporting (click + wheel, SGR encoding) so the
 // scroll wheel can drive the same scroll actions as the arrow keys (see
 // useMouseWheel.ts) -- the one real tradeoff this introduces is that a
 // plain click-drag no longer starts the terminal's own text selection,
 // since button presses now go to the app instead. Hold Option (macOS)
 // or Shift (most other terminals) while clicking/dragging to force the
 // terminal to select text natively anyway -- a standard convention, not
 // a workaround specific to this app.
 let active = false;
 // Separate from `active` because enterFullscreen can now be called more
 // than once per process (AssistApp's ctrl+t shell toggle leaves and
 // re-enters it) -- the exit/SIGINT/SIGTERM handlers only need
 // registering the first time, not once per re-entry, or they'd pile up
 // across however many times someone hops in and out of a shell.
 let listenersAttached = false;
 
 export function enterFullscreen(): void {
  if (active) return;
  active = true;
  process.stdout.write("\x1B[?1049h"); // switch to alternate screen buffer
  process.stdout.write("\x1B[2J\x1B[H"); // clear it, cursor to home
  process.stdout.write("\x1B[?25l"); // hide cursor
  process.stdout.write("\x1B[?1000h"); // enable mouse reporting (click + wheel)
  process.stdout.write("\x1B[?1006h"); // SGR extended mouse encoding
  if (listenersAttached) return;
  listenersAttached = true;
  const restore = () => exitFullscreen();
  process.on("exit", restore);
  process.on("SIGINT", () => {
  restore();
  process.exit(0);
  });
  process.on("SIGTERM", () => {
  restore();
  process.exit(0);
  });
 }
 
 export function exitFullscreen(): void {
  if (!active) return;
  active = false;
  process.stdout.write("\x1B[?1006l");
  process.stdout.write("\x1B[?1000l");
  process.stdout.write("\x1B[?25h"); // show cursor
  process.stdout.write("\x1B[?1049l"); // restore the normal screen buffer
 }