daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

cursor.ts (10371B)


      1 /**
      2  * The dot cursor and its pill, framework-free. Ported from daemon-sec's
      3  * client/src/components/daemon/cursor.tsx so the sibling sites behave
      4  * the same way.
      5  *
      6  * A small disc trails the pointer and opens into a ring over anything
      7  * clickable. Over something with one clear action it becomes a pill
      8  * that says what a click will do:
      9  *   - an element with `data-cursor-label` (optional `data-cursor-icon`:
     10  *     view | play | pdf | download | read)
     11  *   - a video marked `data-pill`: Play / Pause (a click on the video body
     12  *     toggles it; the native control strip keeps its own clicks)
     13  *   - an image marked `data-zoomable`: View image / View GIF
     14  *   - a link to a PDF: Open PDF, or Download PDF if it downloads
     15  *   - a link the site counts as an article (`isArticle`): Read
     16  *
     17  * Fine pointers only; it never mounts on touch or under reduced motion,
     18  * so those visitors keep the native cursor. The trailing loop stops as
     19  * soon as the dot catches up, so an idle page costs nothing.
     20  *
     21  * Usage:
     22  *   mountCursor({ isArticle: (href) => href.startsWith("/sheets/") });
     23  */
     24 
     25 export interface CursorOptions {
     26   /** Which internal links read as articles and get the "Read" pill. */
     27   isArticle?: (href: string) => boolean;
     28   /** Finer control: the pill text for a link, or null for none. Takes
     29    *  precedence over `isArticle`. */
     30   linkLabel?: (href: string, link: HTMLAnchorElement) => string | null;
     31   /** Ancestors that are dark grounds, so the cursor re-tints over them. */
     32   darkSelector?: string;
     33   /** Finer control than `darkSelector`, for sites that nest light panels
     34    *  inside dark bands. */
     35   isDark?: (target: HTMLElement) => boolean;
     36   /** Override the reduced-motion gate. */
     37   enabled?: boolean;
     38 }
     39 
     40 type Mode = "view" | "play" | "read" | "pdf" | "label" | null;
     41 type IconName = "view" | "play" | "pause" | "pdf" | "download" | "read";
     42 
     43 /* lucide path data (ISC/MIT), so the pill needs no icon font. */
     44 const ICONS: Record<IconName, string> = {
     45   view: '<path d="M15 3h6v6"/><path d="m21 3-7 7"/><path d="m3 21 7-7"/><path d="M9 21H3v-6"/>',
     46   play: '<path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z"/>',
     47   pause: '<rect x="14" y="3" width="5" height="18" rx="1"/><rect x="5" y="3" width="5" height="18" rx="1"/>',
     48   pdf: '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/>',
     49   download: '<path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/>',
     50   read: '<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>',
     51 };
     52 
     53 const EASE = 0.28;
     54 const PILL_EASE = 0.16;
     55 const SETTLED = 0.1;
     56 const CONTROLS_STRIP = 48;
     57 const PDF = /\.pdf($|[?#])/i;
     58 const INTERACTIVE =
     59   "a, button, input, textarea, select, summary, label, [role='button'], [role='switch'], [role='tab']";
     60 
     61 /* Only ever fixed markup from ICONS: an unknown name falls back to the
     62    arrow rather than reaching the DOM. */
     63 function svg(name: IconName) {
     64   const body = ICONS[name] ?? ICONS.read;
     65   return `<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${body}</svg>`;
     66 }
     67 
     68 export function mountCursor(options: CursorOptions = {}): () => void {
     69   /* A site-level motion switch (`<html data-motion="reduce">`) counts the
     70      same as the OS setting. */
     71   const enabled =
     72     options.enabled ??
     73     (window.matchMedia("(pointer: fine)").matches &&
     74       !window.matchMedia("(prefers-reduced-motion: reduce)").matches &&
     75       document.documentElement.dataset.motion !== "reduce");
     76   if (!enabled) return () => {};
     77   const isArticle = options.isArticle ?? (() => false);
     78   const linkLabel =
     79     options.linkLabel ?? ((href: string) => (isArticle(href) ? "Read" : null));
     80   const darkSelector = options.darkSelector ?? "[data-ground='dark']";
     81   const isDark = options.isDark ?? ((t: HTMLElement) => Boolean(t.closest(darkSelector)));
     82 
     83   const el = document.createElement("div");
     84   el.className = "dmn-cursor";
     85   el.setAttribute("aria-hidden", "true");
     86   el.innerHTML = '<span class="dmn-cursor-pill"><span class="dmn-cursor-icon"></span><span class="dmn-cursor-label"></span></span>';
     87   const iconEl = el.querySelector<HTMLElement>(".dmn-cursor-icon")!;
     88   const labelEl = el.querySelector<HTMLElement>(".dmn-cursor-label")!;
     89   document.body.appendChild(el);
     90   const root = document.documentElement;
     91   root.classList.add("has-dmn-cursor");
     92 
     93   let x = window.innerWidth / 2;
     94   let y = window.innerHeight / 2;
     95   let tx = x;
     96   let ty = y;
     97   let raf = 0;
     98   let seen = false;
     99   let mode: Mode = null;
    100   let mediaEl: HTMLElement | null = null;
    101 
    102   const place = () => {
    103     el.style.transform = `translate(${x}px, ${y}px) translate(-50%, -50%)`;
    104   };
    105   const step = () => {
    106     const k = mode ? PILL_EASE : EASE;
    107     x += (tx - x) * k;
    108     y += (ty - y) * k;
    109     if (Math.abs(tx - x) < SETTLED && Math.abs(ty - y) < SETTLED) {
    110       x = tx;
    111       y = ty;
    112       place();
    113       raf = 0;
    114       return;
    115     }
    116     place();
    117     raf = requestAnimationFrame(step);
    118   };
    119   const wake = () => {
    120     if (!raf) raf = requestAnimationFrame(step);
    121   };
    122 
    123   const under = (target: HTMLElement): { mode: Mode; el: HTMLElement } | null => {
    124     const labelled = target.closest<HTMLElement>("[data-cursor-label]");
    125     if (labelled) return { mode: "label", el: labelled };
    126     const video = target.closest<HTMLVideoElement>("video[data-pill]");
    127     if (video) return { mode: "play", el: video };
    128     const img =
    129       target.closest<HTMLImageElement>("img[data-zoomable]") ??
    130       target.closest("button")?.querySelector<HTMLImageElement>("img[data-zoomable]") ??
    131       null;
    132     if (img) return { mode: "view", el: img };
    133     const link = target.closest<HTMLAnchorElement>("a[href]");
    134     if (link) {
    135       const href = link.getAttribute("href") ?? "";
    136       if (PDF.test(href)) return { mode: "pdf", el: link };
    137       if (linkLabel(href, link)) return { mode: "read", el: link };
    138     }
    139     return null;
    140   };
    141 
    142   const labelFor = (m: Mode, t: HTMLElement): { icon: IconName; text: string } => {
    143     if (m === "label") {
    144       return { icon: (t.dataset.cursorIcon as IconName) || "read", text: t.dataset.cursorLabel ?? "" };
    145     }
    146     if (m === "play") {
    147       const paused = (t as HTMLVideoElement).paused;
    148       return { icon: paused ? "play" : "pause", text: paused ? "Play" : "Pause" };
    149     }
    150     if (m === "pdf") {
    151       return t.hasAttribute("download")
    152         ? { icon: "download", text: "Download PDF" }
    153         : { icon: "pdf", text: "Open PDF" };
    154     }
    155     if (m === "read") {
    156       const a = t as HTMLAnchorElement;
    157       return { icon: "read", text: linkLabel(a.getAttribute("href") ?? "", a) ?? "Read" };
    158     }
    159     const src = (t as HTMLImageElement).currentSrc || (t as HTMLImageElement).src || "";
    160     return { icon: "view", text: /\.gif($|\?)/i.test(src) ? "View GIF" : "View image" };
    161   };
    162 
    163   const syncLabel = () => {
    164     if (!mode || !mediaEl) return;
    165     const { icon, text } = labelFor(mode, mediaEl);
    166     iconEl.innerHTML = svg(icon);
    167     labelEl.textContent = text;
    168   };
    169 
    170   const setMode = (next: Mode, target: HTMLElement | null) => {
    171     if (mediaEl && mediaEl !== target) {
    172       mediaEl.removeEventListener("play", syncLabel);
    173       mediaEl.removeEventListener("pause", syncLabel);
    174     }
    175     mode = next;
    176     mediaEl = target;
    177     if (mode && mediaEl) {
    178       el.dataset.mode = mode;
    179       if (mode === "play") {
    180         mediaEl.addEventListener("play", syncLabel);
    181         mediaEl.addEventListener("pause", syncLabel);
    182       }
    183       syncLabel();
    184     } else {
    185       delete el.dataset.mode;
    186     }
    187   };
    188 
    189   /* A viewer or modal on screen hides the pill. Sites often keep their
    190      search dialog in the DOM while closed, so only a visible one counts. */
    191   const dialogOpen = () =>
    192     Array.from(document.querySelectorAll<HTMLElement>("dialog[open], [role='dialog'][aria-modal='true']")).some(
    193       (d) => (d.checkVisibility ? d.checkVisibility() : d.offsetParent !== null),
    194     );
    195 
    196   const applyTarget = (target: HTMLElement | null) => {
    197     if (!target?.closest) return;
    198     el.classList.toggle("hot", Boolean(target.closest(INTERACTIVE)));
    199     el.classList.toggle("on-dark", isDark(target));
    200     const media = dialogOpen() ? null : under(target);
    201     setMode(media?.mode ?? null, media?.el ?? null);
    202   };
    203 
    204   const onMove = (e: MouseEvent) => {
    205     tx = e.clientX;
    206     ty = e.clientY;
    207     seen = true;
    208     wake();
    209   };
    210   const onOver = (e: MouseEvent) => applyTarget(e.target as HTMLElement | null);
    211   const onLeave = () => (el.style.opacity = "0");
    212   const onEnter = () => (el.style.opacity = "1");
    213 
    214   /* Content can move under a pointer that has not; look again while the
    215      page scrolls, once per frame. */
    216   let scrollRaf = 0;
    217   const onScroll = () => {
    218     if (!seen || scrollRaf) return;
    219     scrollRaf = requestAnimationFrame(() => {
    220       scrollRaf = 0;
    221       applyTarget(document.elementFromPoint(tx, ty) as HTMLElement | null);
    222     });
    223   };
    224 
    225   const onClick = (e: MouseEvent) => {
    226     const video = (e.target as HTMLElement | null)?.closest?.<HTMLVideoElement>("video[data-pill]");
    227     if (!video) return;
    228     const r = video.getBoundingClientRect();
    229     if (e.clientY > r.bottom - CONTROLS_STRIP) return;
    230     e.preventDefault();
    231     if (video.paused) void video.play().catch(() => {});
    232     else video.pause();
    233   };
    234 
    235   window.addEventListener("mousemove", onMove, { passive: true });
    236   window.addEventListener("mouseover", onOver, { passive: true });
    237   document.documentElement.addEventListener("mouseleave", onLeave);
    238   document.documentElement.addEventListener("mouseenter", onEnter);
    239   window.addEventListener("scroll", onScroll, { passive: true });
    240   window.addEventListener("click", onClick);
    241   place();
    242 
    243   return () => {
    244     if (raf) cancelAnimationFrame(raf);
    245     if (scrollRaf) cancelAnimationFrame(scrollRaf);
    246     setMode(null, null);
    247     window.removeEventListener("mousemove", onMove);
    248     window.removeEventListener("mouseover", onOver);
    249     document.documentElement.removeEventListener("mouseleave", onLeave);
    250     document.documentElement.removeEventListener("mouseenter", onEnter);
    251     window.removeEventListener("scroll", onScroll);
    252     window.removeEventListener("click", onClick);
    253     root.classList.remove("has-dmn-cursor");
    254     el.remove();
    255   };
    256 }