sign-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 528326727848
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 1da91c033c738ab74538d13d
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/Pages.tsx
// Renders a PDF's pages as canvases at the container's width, with an overlay layer per page.
// Pages draw when they come near the screen and let go of their pixels when far away, so long documents stay light on phones.
import { useEffect, useRef, useState, type ReactNode, type MouseEvent } from "react";
import type { PdfDoc } from "./lib/pdfjs";
import { PagePlaceholder } from "./Skeleton";

interface PageSize { w: number; h: number }

export function Pages({ pdf, overlay, onPageClick, placing }: {
  pdf: PdfDoc;
  overlay?: (page: number) => ReactNode;
  onPageClick?: (page: number, fx: number, fy: number) => void;
  placing?: boolean;
}) {
  const [sizes, setSizes] = useState<PageSize[] | null>(null);
  useEffect(() => {
    let cancelled = false;
    Promise.all(Array.from({ length: pdf.numPages }, async (_, i) => {
      const vp = (await pdf.getPage(i + 1)).getViewport({ scale: 1 });
      return { w: vp.width, h: vp.height };
    })).then((out) => { if (!cancelled) setSizes(out); });
    return () => { cancelled = true; };
  }, [pdf]);

  if (!sizes) return <div className="pages"><PagePlaceholder /></div>;
  return (
    <div className={`pages${placing ? " placing" : ""}`}>
      {sizes.map((s, i) => (
        <Page key={i} pdf={pdf} index={i} size={s} overlay={overlay?.(i)} onClick={onPageClick} total={sizes.length} />
      ))}
    </div>
  );
}

function Page({ pdf, index, size, overlay, onClick, total }: {
  pdf: PdfDoc; index: number; size: PageSize; overlay?: ReactNode; total: number;
  onClick?: (page: number, fx: number, fy: number) => void;
}) {
  const wrap = useRef<HTMLDivElement>(null);
  const canvas = useRef<HTMLCanvasElement>(null);
  const [near, setNear] = useState(index < 2);
  const [width, setWidth] = useState(0);
  const [drawn, setDrawn] = useState(false);

  useEffect(() => {
    const el = wrap.current!;
    const io = new IntersectionObserver(([e]) => setNear(e.isIntersecting), { rootMargin: "1600px 0px" });
    io.observe(el);
    const ro = new ResizeObserver(([e]) => setWidth(Math.round(e.contentRect.width)));
    ro.observe(el);
    return () => { io.disconnect(); ro.disconnect(); };
  }, []);

  useEffect(() => {
    const c = canvas.current!;
    if (!near || !width) {
      // Far from the screen: free the pixels (phones cap total canvas memory).
      if (!near && c.width) { c.width = 0; c.height = 0; setDrawn(false); }
      return;
    }
    let task: { cancel: () => void; promise: Promise<unknown> } | null = null;
    let cancelled = false;
    (async () => {
      const page = await pdf.getPage(index + 1);
      if (cancelled) return;
      // Sharper on phones, where people pinch to zoom into the text.
      const dpr = Math.min(window.devicePixelRatio || 1, width < 600 ? 3 : 2);
      const vp = page.getViewport({ scale: (width / size.w) * dpr });
      const off = document.createElement("canvas");
      off.width = Math.floor(vp.width);
      off.height = Math.floor(vp.height);
      task = page.render({ canvasContext: off.getContext("2d")!, viewport: vp });
      const ok = await task.promise.then(() => true, () => false);
      if (ok && !cancelled) {
        // Swap in the finished drawing at once, so a resize never shows a blank page.
        c.width = off.width;
        c.height = off.height;
        c.getContext("2d")!.drawImage(off, 0, 0);
        setDrawn(true);
      }
      // Free the scratch canvas now: iOS counts every canvas toward a memory cap until it is collected.
      off.width = 0;
      off.height = 0;
    })();
    return () => { cancelled = true; task?.cancel(); };
  }, [near, width, pdf, index, size.w]);

  const click = (e: MouseEvent<HTMLDivElement>) => {
    if (!onClick || e.target !== e.currentTarget) return;
    const r = e.currentTarget.getBoundingClientRect();
    onClick(index, (e.clientX - r.left) / r.width, (e.clientY - r.top) / r.height);
  };

  return (
    <div className="page-wrap" data-page={index}>
      <div className="page" ref={wrap} style={{ aspectRatio: `${size.w} / ${size.h}` }}>
        <canvas ref={canvas} className={drawn ? "drawn" : undefined} aria-hidden="true" />
        <div className="page-layer" onClick={click}>{overlay}</div>
      </div>
      <p className="page-num">Page {index + 1} of {total}</p>
    </div>
  );
}

Change history

Quality pass: phone signing, edit signers, copy to send again
5283267278 · 5 hours ago
Bounded source sync
15f0fa2e27 · 1 day ago
Bounded source sync
d3aa1ca65b · 1 day ago
Bounded source sync
8e2ca0b773 · 1 day ago
Bounded source sync
5cfa84715c · 1 day ago
Bounded source sync
c849e7b286 · 1 day ago
Bounded source sync
68c1cabe03 · 1 day ago
Bounded source sync
62ae8944f0 · 1 day ago
Bounded source sync
7ed54e7381 · 1 day ago