website

Personal website
git clone https://github.com/michaelneuper/website.git
README | Files | Log | Refs

website/src/scripts/emacs/motions.js (1651B)


// evil's motions, for reading: j and k scroll a few lines, d and u half a
// screen, gg goes to the top and G to the bottom. C-d and C-u work too on a
// Mac; elsewhere the browser keeps them (bookmark, view source).

import { reducedMotion } from "../util.js";
import { message, idle } from "./echo.js";

const LINE = 64; // px for j and k, about three lines of body text
const mac = /mac|iphone|ipad/i.test(navigator.userAgentData?.platform ?? navigator.platform);

let gAt = 0; // when g was pressed, for gg

// Handles e if it's a motion and returns true, so keys.js can stop there.
export function motion(e) {
  // held down, a key repeats faster than a smooth scroll finishes
  const behavior = reducedMotion.matches || e.repeat ? "instant" : "smooth";
  const by = (dy) => scrollBy({ top: dy, behavior });
  const half = innerHeight / 2;

  if (e.ctrlKey && !e.altKey && !e.metaKey) {
    if (!mac || (e.key !== "d" && e.key !== "u")) return false;
    e.preventDefault();
    by(e.key === "d" ? half : -half);
    return true;
  }
  if (e.ctrlKey || e.altKey || e.metaKey) return false;

  const was = gAt;
  gAt = 0;
  switch (e.key) {
    case "j": by(LINE); break;
    case "k": by(-LINE); break;
    case "d": by(half); break;
    case "u": by(-half); break;
    case "G": scrollTo({ top: document.documentElement.scrollHeight, behavior }); break;
    case "g":
      if (performance.now() - was < 800) {
        scrollTo({ top: 0, behavior });
        idle();
      } else {
        gAt = performance.now();
        message("g-", "", 800);
      }
      break;
    default:
      return false;
  }
  e.preventDefault();
  return true;
}