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/Editor.tsx
// The landing's working parts: the editor in the hero, and "Your pastes" for returning owners.
// The Bounded SDK loads only when someone starts a paste, so the landing and paste pages stay light.
import { useEffect, useMemo, useRef, useState, type CSSProperties, type DragEvent, type FormEvent, type KeyboardEvent } from "react";
import { navigate } from "./lib/router";
import { newSecret, seal, sealWithPassword } from "./lib/crypto";
import { savePaste, savedPastes, forgetPaste, isExpired, type Saved } from "./lib/mine";
import { HOST, shareUrl, adminPath, adminLink, expiryText, explain } from "./lib/format";
import { copy, share, canShare, toast, Menu } from "./ui";
import {
  LANGUAGES, MAX_BYTES, EXPIRES, languageOf, detectLanguage, checkContent, checkTitle, byteLength, formatBytes,
} from "../functions/lib/paste";

const store = () => import("./lib/store");

type Expiry = keyof typeof EXPIRES | "burn";
const EXPIRY_LABELS: [Expiry, string][] = [
  ["never", "Never"], ["10m", "10 minutes"], ["1h", "1 hour"], ["1d", "1 day"], ["1w", "1 week"],
  ["1mo", "1 month"], ["1y", "1 year"], ["burn", "After the first view"],
];

export type Lock = "none" | "password" | "link";
const LOCK_LABELS: [Lock, string][] = [["none", "None"], ["password", "Password"], ["link", "Key in the link"]];

interface Fork { content: string; title: string; language: string; lock: Lock }
let pendingFork: Fork | null = null;

/** Opens a copy of a paste in the landing editor. */
export function forkIntoEditor(fork: Fork) {
  pendingFork = fork;
  window.dispatchEvent(new Event("paste:fork"));
  navigate("/");
}

/** A dropped file's language, from its extension. */
const byExtension = (name: string) => {
  const ext = name.split(".").pop()?.toLowerCase();
  return LANGUAGES.find((l) => l.ext === ext || (ext === "htm" && l.id === "html") || (ext === "yml" && l.id === "yaml"))?.id;
};

interface Made { id: string; ownerKey: string; secret?: string; password: boolean; burn: boolean; ttl: number; createdAt: number }

export function Editor() {
  const [content, setContent] = useState("");
  const [title, setTitle] = useState("");
  const [language, setLanguage] = useState("auto");
  const [expiry, setExpiry] = useState<Expiry>("never");
  const [lock, setLock] = useState<Lock>("none");
  const [password, setPassword] = useState("");
  const [detected, setDetected] = useState("text");
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<{ field: "content" | "password" | "form"; text: string } | null>(null);
  const [dragging, setDragging] = useState(false);
  const [made, setMade] = useState<Made | null>(null);
  const text = useRef<HTMLTextAreaElement>(null);
  const passwordInput = useRef<HTMLInputElement>(null);
  const finePointer = useMemo(() => matchMedia("(pointer: fine)").matches, []);

  useEffect(() => {
    const timer = window.setTimeout(() => setDetected(detectLanguage(content)), 250);
    return () => window.clearTimeout(timer);
  }, [content]);

  useEffect(() => {
    const onFork = () => {
      if (!pendingFork) return;
      setContent(pendingFork.content);
      setTitle(pendingFork.title);
      setLanguage(pendingFork.language || "auto");
      setLock(pendingFork.lock);
      setPassword("");
      setMade(null);
      setError(null);
      pendingFork = null;
      requestAnimationFrame(() => text.current?.focus({ preventScroll: true }));
    };
    window.addEventListener("paste:fork", onFork);
    return () => window.removeEventListener("paste:fork", onFork);
  }, []);

  const bytes = useMemo(() => byteLength(content), [content]);
  const tooBig = bytes > MAX_BYTES;

  const submit = async (e?: FormEvent) => {
    e?.preventDefault();
    if (busy) return;
    const c = checkContent(content);
    if ("error" in c) { setError({ field: "content", text: c.error }); text.current?.focus(); return; }
    const t = checkTitle(title);
    if ("error" in t) return setError({ field: "form", text: t.error });
    if (lock === "password" && !password) { setError({ field: "password", text: "Choose a password, or set Lock to None." }); passwordInput.current?.focus(); return; }
    const lang = language === "auto" ? detectLanguage(c.content) : language;
    const burn = expiry === "burn";
    // An unopened burn-after-reading paste expires after a week, so a forgotten secret does not stay forever.
    const ttl = burn ? EXPIRES["1w"] : EXPIRES[expiry];
    setBusy(true);
    setError(null);
    try {
      const { createPaste } = await store();
      let secret: string | undefined;
      let saved = { content: c.content, title: t.title, language: lang };
      if (lock !== "none") {
        if (lock === "link") secret = newSecret();
        const sealed = secret ? await seal(saved, secret) : await sealWithPassword(saved, password);
        saved = { content: sealed, title: "", language: "" };
      }
      const { id, key } = await createPaste({ ...saved, ttl, burn, encrypted: lock !== "none" });
      const createdAt = Math.floor(Date.now() / 1000);
      savePaste({ id, key, title: t.title, language: lang, createdAt, ttl, burn, secret, password: lock === "password" || undefined });
      setMade({ id, ownerKey: key, secret, password: lock === "password", burn, ttl, createdAt });
      setContent("");
      setTitle("");
      setLanguage("auto");
      setPassword("");
    } catch (err) {
      setError({ field: "form", text: explain(err) });
    } finally {
      setBusy(false);
    }
  };

  const onKey = (e: KeyboardEvent) => {
    if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); submit(); }
  };

  const onDrop = (e: DragEvent) => {
    const file = e.dataTransfer.files[0];
    setDragging(false);
    if (!file) return;
    e.preventDefault();
    if (file.size > MAX_BYTES) return setError({ field: "content", text: `${file.name} is ${formatBytes(file.size)}. A paste can hold up to 512 KB.` });
    file.text().then((body) => {
      if (body.includes("\u0000")) return setError({ field: "content", text: `${file.name} is not a text file. Paste holds text, code and logs.` });
      setContent(body);
      if (!title.trim()) setTitle(file.name);
      setLanguage(byExtension(file.name) ?? "auto");
      setError(null);
      text.current?.focus({ preventScroll: true });
    }, () => setError({ field: "content", text: `${file.name} could not be read.` }));
  };

  if (made) return <Result {...made} onAgain={() => { setMade(null); requestAnimationFrame(() => text.current?.focus()); }} />;

  const contentError = error?.field === "content" ? error.text : null;
  const passwordError = error?.field === "password" ? error.text : null;
  return (
    <form className={`editor card${dragging ? " dragging" : ""}`} onSubmit={submit} onKeyDown={onKey} noValidate aria-label="New paste">
      <div className="editor-top">
        <label className="sr-only" htmlFor="paste-title">Title</label>
        <input id="paste-title" className="editor-title" value={title} onChange={(e) => setTitle(e.target.value)} maxLength={200}
          placeholder="Title (optional)" autoComplete="off" enterKeyHint="next" />
        <label className="sr-only" htmlFor="paste-language">Language</label>
        <select id="paste-language" className="select editor-lang" value={language} onChange={(e) => setLanguage(e.target.value)}>
          <option value="auto">{content.trim() ? `Detect: ${languageOf(detected).name}` : "Detect language"}</option>
          {LANGUAGES.map((l) => <option key={l.id} value={l.id}>{l.name}</option>)}
        </select>
      </div>
      <label className="sr-only" htmlFor="paste-text">Text or code</label>
      <textarea id="paste-text" ref={text} className="editor-text" value={content}
        onChange={(e) => { setContent(e.target.value); if (contentError) setError(null); }}
        onFocus={() => { store().then((s) => s.warm()).catch(() => {}); }}
        onDragOver={(e) => { if (e.dataTransfer.types.includes("Files")) { e.preventDefault(); setDragging(true); } }}
        onDragLeave={() => setDragging(false)} onDrop={onDrop}
        aria-invalid={contentError ? true : undefined} aria-describedby={contentError ? "paste-text-error" : undefined}
        placeholder={finePointer ? "Paste or type text, code or a log here, or drop a text file." : "Paste or type text, code or a log here."}
        spellCheck={false} autoCapitalize="off" autoComplete="off" autoCorrect="off" />
      {contentError && <p className="field-error editor-error" id="paste-text-error" role="alert">{contentError}</p>}
      <div className="editor-foot">
        <div className="editor-options">
          <label className="option">
            <span>Expires</span>
            <select className="select" value={expiry} onChange={(e) => setExpiry(e.target.value as Expiry)}>
              {EXPIRY_LABELS.map(([k, label]) => <option key={k} value={k}>{label}</option>)}
            </select>
          </label>
          <label className="option">
            <span>Lock</span>
            <select className="select" value={lock} onChange={(e) => { setLock(e.target.value as Lock); setError(null); if (e.target.value === "password") requestAnimationFrame(() => passwordInput.current?.focus()); }}>
              {LOCK_LABELS.map(([k, label]) => <option key={k} value={k}>{label}</option>)}
            </select>
          </label>
          {lock === "password" && (
            <span className="option option-password">
              <label className="sr-only" htmlFor="paste-password">Password</label>
              <input id="paste-password" ref={passwordInput} className="input" value={password} onChange={(e) => { setPassword(e.target.value); if (passwordError) setError(null); }}
                placeholder="Password" autoComplete="off" autoCapitalize="off" autoCorrect="off" spellCheck={false}
                aria-invalid={passwordError ? true : undefined} aria-describedby={passwordError ? "paste-password-error" : undefined} />
            </span>
          )}
        </div>
        <div className="editor-submit">
          <span className={`size${tooBig ? " warn" : ""}`}>{content ? `${formatBytes(bytes)} of 512 KB` : "Up to 512 KB"}</span>
          <button className="btn btn-lg" type="submit" disabled={tooBig} aria-busy={busy || undefined}
            aria-keyshortcuts="Control+Enter Meta+Enter" title={finePointer ? "Ctrl+Enter or Cmd+Enter" : undefined}>
            <span>Create link</span>
          </button>
        </div>
      </div>
      {passwordError && <p className="field-error editor-error" id="paste-password-error" role="alert">{passwordError}</p>}
      <div className="editor-notes">
        {expiry === "burn" && <p>The first person to open the link sees the paste, and then it is deleted. If nobody opens it, it expires in a week.</p>}
        {lock === "password" && <p>Your browser locks the paste with this password before it is sent, so our server never sees the text. Send the password separately from the link. If it is lost, nobody can open the paste, not even us.</p>}
        {lock === "link" && <p>Your browser locks the paste before it is sent. The key is the part of the link after the # sign, and it never reaches our server. If you lose the link, nobody can open the paste, not even us.</p>}
        {error?.field === "form" && <p className="notice notice-error" role="alert">{error.text}</p>}
        <p className="editor-hint">Unlisted: only people with the link can open it. Nobody can browse or search pastes.</p>
      </div>
    </form>
  );
}

function Result({ id, ownerKey, secret, password, burn, ttl, createdAt, onAgain }: Made & { onAgain: () => void }) {
  const link = shareUrl(id, secret);
  const view = `/${id}${secret ? `#${secret}` : ""}`;
  const admin = adminPath({ id, key: ownerKey, secret });
  const card = useRef<HTMLDivElement>(null);
  useEffect(() => {
    const el = card.current;
    if (!el) return;
    if (el.getBoundingClientRect().top < 72) el.scrollIntoView({ block: "start", behavior: matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth" });
    el.focus({ preventScroll: true });
  }, []);
  return (
    <div className="result card enter" ref={card} tabIndex={-1} aria-labelledby="result-title">
      <p className="eyebrow" id="result-title" role="status">Your paste is ready</p>
      <div className="link-row">
        <a className="paste-link" href={view} target="_blank" rel="noopener">
          <span className="paste-host">{HOST}/</span><strong>{id}</strong>{secret && <span className="paste-secret">#{secret.slice(0, 6)}...</span>}
        </a>
        <button className="btn" type="button" onClick={() => share(link, "Paste", "Link copied")}>{canShare() ? "Share link" : "Copy link"}</button>
      </div>
      <p className="result-meta">
        {expiryText({ createdAt, ttl, burn })}. {password ? "Locked with a password." : secret ? "Encrypted in your browser." : "Unlisted."}
      </p>
      {burn && <p className="notice">Do not open it yourself: the first view deletes it. Send the link as it is.</p>}
      {password && <p className="notice">Send the password separately from the link. Our server never sees it, so if it is lost, nobody can open this paste.</p>}
      {secret && <p className="notice">The key is in the link, after the # sign. Our server never sees it, so if the link is lost, nobody can open this paste.</p>}
      <div className="edit-note">
        <p><strong>Your private admin link.</strong> Edit or delete the paste on any device. Anyone with it can change the paste, so keep it to yourself.</p>
        <div className="row">
          <button className="btn btn-sm btn-line" type="button" onClick={() => copy(adminLink({ id, key: ownerKey, secret }), "Admin link copied. Keep it somewhere safe.")}>Copy admin link</button>
          <a className="btn btn-sm btn-quiet" href={admin} onClick={(e) => { e.preventDefault(); navigate(admin); }}>Open admin page</a>
        </div>
      </div>
      <div className="row">
        {!burn && <a className="btn btn-line" href={view} onClick={(e) => { e.preventDefault(); navigate(view); }}>Open paste</a>}
        <button className="btn btn-quiet" type="button" onClick={onAgain}>New paste</button>
      </div>
    </div>
  );
}

export function YourPastes() {
  const [saved, setSaved] = useState(savedPastes);
  const [all, setAll] = useState(false);
  useEffect(() => {
    const onSaved = () => setSaved(savedPastes());
    window.addEventListener("paste:saved", onSaved);
    return () => window.removeEventListener("paste:saved", onSaved);
  }, []);
  const live = saved.filter((s) => !isExpired(s));
  if (!live.length) return null;
  const shown = all ? live : live.slice(0, 8);
  return (
    <div className="section-tight">
      <div className="wrap">
        <div className="spread">
          <h2 className="h2">Your pastes</h2>
          <span className="muted small">Saved in this browser</span>
        </div>
        <ul className="mine-list">
          {shown.map((s, i) => <PasteRow key={s.id} s={s} i={i} />)}
        </ul>
        {!all && live.length > shown.length && (
          <button className="btn btn-quiet more" type="button" onClick={() => setAll(true)}>Show all {live.length} pastes</button>
        )}
      </div>
    </div>
  );
}

function PasteRow({ s, i }: { s: Saved; i: number }) {
  const view = `/${s.id}${s.secret ? `#${s.secret}` : ""}`;
  const admin = adminPath(s);
  const lock = s.password ? "Password" : s.secret ? "Encrypted" : "";
  return (
    <li className="mine-row enter" style={{ "--i": i } as CSSProperties}>
      <div className="mine-main">
        {s.burn
          ? <span className="mine-title">{s.title || "Untitled paste"}</span>
          : <a className="mine-title" href={view} onClick={(e) => { e.preventDefault(); navigate(view); }}>{s.title || "Untitled paste"}</a>}
        <span className="mine-meta">{languageOf(s.language).name} · {expiryText(s)}{lock ? ` · ${lock}` : ""}</span>
      </div>
      <span className="mine-actions">
        <button className="btn btn-sm btn-line" type="button" onClick={() => share(shareUrl(s.id, s.secret), s.title || "Paste", "Link copied")}>{canShare() ? "Share" : "Copy link"}</button>
        <a className="btn btn-sm btn-line" href={admin} onClick={(e) => { e.preventDefault(); navigate(admin); }}>Manage</a>
        <Menu label={`More for ${s.title || "this paste"}`}>
          <button type="button" onClick={() => copy(adminLink(s), "Admin link copied. Keep it somewhere safe.")}>Copy admin link</button>
          <hr />
          <button type="button" className="danger" onClick={() => { forgetPaste(s.id); toast("Removed from this list. The paste still works.", { label: "Undo", run: () => savePaste(s) }); }}>Remove from this list</button>
        </Menu>
      </span>
    </li>
  );
}

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