paste-free.openapps.xyz

Source

This app’s code and revision history are public. Download a copy or explore the files below.

Public
The app’s code and revision history are public.
Owned app · rev 01fdb2b1f66e
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 6eca480d6447d873e2e5eed9
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
← All files
src/Code.tsx
// A paste's text with line numbers, colored by highlight.js once it loads. The numbers are drawn by CSS, so they are
// never copied with the text. When the paste allows it, a number is a link: clicking it marks the line and puts
// #L<n> in the address (and copies that link), and opening an address with #L<n> scrolls to that line.
import { useEffect, useMemo, useRef, useState, type CSSProperties, type MouseEvent } from "react";
import { highlight, splitLines, escapeHtml } from "./lib/highlight";
import { copy } from "./ui";

const lineFromHash = () => Number(/^#L(\d+)$/.exec(location.hash)?.[1]) || 0;

export function CodeView({ code, language, wrap, linkable }: { code: string; language: string; wrap: boolean; linkable: boolean }) {
  const [html, setHtml] = useState<string | null>(null);
  const [line, setLine] = useState(() => (linkable ? lineFromHash() : 0));
  const pre = useRef<HTMLPreElement>(null);
  const scrolled = useRef(false);

  useEffect(() => {
    let live = true;
    setHtml(null);
    highlight(code, language).then((h) => { if (live) setHtml(h); }, () => {});
    return () => { live = false; };
  }, [code, language]);

  const lines = useMemo(() => {
    const list = html ? splitLines(html) : code.split("\n").map(escapeHtml);
    if (list.length > 1 && list[list.length - 1].replace(/<[^>]+>/g, "") === "") list.pop();
    return list;
  }, [html, code]);
  const body = useMemo(() => lines.map((l, i) => linkable
    ? `<span class="ln" id="L${i + 1}"><a class="lnum" href="#L${i + 1}" data-n="${i + 1}" tabindex="-1" aria-hidden="true"></a><span class="lc">${l}</span></span>`
    : `<span class="ln"><span class="lnum" data-n="${i + 1}"></span><span class="lc">${l}</span></span>`).join(""), [lines, linkable]);

  // Re-mark the chosen line whenever the lines are redrawn (highlighting arrives after the plain text).
  useEffect(() => {
    if (!linkable) return;
    pre.current?.querySelector(".ln.marked")?.classList.remove("marked");
    const el = line ? pre.current?.querySelector(`#L${line}`) : null;
    el?.classList.add("marked");
    if (el && !scrolled.current) {
      scrolled.current = true;
      el.scrollIntoView({ block: "center" });
    }
  }, [body, line, linkable]);

  useEffect(() => {
    if (!linkable) return;
    const onHash = () => { scrolled.current = false; setLine(lineFromHash()); };
    window.addEventListener("hashchange", onHash);
    return () => window.removeEventListener("hashchange", onHash);
  }, [linkable]);

  const onClick = (e: MouseEvent) => {
    const a = (e.target as Element).closest<HTMLAnchorElement>("a.lnum");
    if (!a) return;
    e.preventDefault();
    const n = Number(a.dataset.n);
    const next = n === line ? 0 : n;
    scrolled.current = true;
    history.replaceState(history.state, "", next ? `#L${next}` : location.pathname + location.search);
    setLine(next);
    if (next) copy(`${location.origin}${location.pathname}#L${next}`, `Link to line ${next} copied`);
  };

  return (
    <pre ref={pre} className={`code-view${wrap ? " wrapped" : ""}`} style={{ "--digits": String(lines.length).length } as CSSProperties} onClick={onClick}>
      <code dangerouslySetInnerHTML={{ __html: body }} />
    </pre>
  );
}

Change history

Quality pass: password lock, year expiry, line links, text/plain API
01fdb2b1f6 · 4 hours ago
Bounded source sync
fe5788c2d2 · 1 day ago
Bounded source sync
ac6f7dbc95 · 1 day ago
Bounded source sync
0a45926293 · 1 day ago