PaperStudio/PaperStudio Universe — Cell Providers
UsuarioSoporteDeveloper

PaperStudio Universe — Cell Providers

A universe papercell is the generalization of the template cell: instead of embedding a template by id, it names a provider that — given the studio context — returns an SVG.

That single indirection is the whole idea: one cell type, N providers, zero front-end code per provider. A new kind of cell (a map viewer, a route renderer, a statistical chart, a data inspector, a calculator) becomes an endpoint that returns SVG, not a change to PaperStudio.

The same SVG is used on the live canvas, in the Typst preview and in the exported PDF, so what you see is what you print.


The contract (v1)

A provider is an HTTPS endpoint that accepts POST with JSON and answers with an SVG.

Request

{
  "provider_id": "my-provider",
  "flavor": "thumb",
  "params": { "…": "whatever your provider defines" },
  "context": {
    "vars": { "…": "the studio's reactive data bag" },
    "size_mm": { "w": 90, "h": 60 },
    "locale": "es",
    "page": { "n": 2, "total": 8 },
    "studio": { "title": "Reporte trimestral" }
  }
}
Field Meaning
params Yours. Whatever the author edits in the cell (the editor shows a JSON field seeded from your params_example).
context.size_mm The cell box in millimetres. Size your SVG to this aspect (the convention is ~4px per mm).
context.vars The studio's reactive data (studio data + cell ports + _page/_studio). Use it if your provider is data-driven.
flavor thumb = the fast canvas preview · full = the final export. Return the same picture; use it to trade detail for speed if you want.

Response

Either the SVG directly:

200 OK
Content-Type: image/svg+xml

<svg xmlns="http://www.w3.org/2000/svg" width="360" height="240" viewBox="0 0 360 240">…</svg>

…or JSON with an svg key:

{ "svg": "<svg …>…</svg>" }

Rules your SVG must follow

  1. Self-contained. No external references. Images must be inlined as data: URIs — any <image>/<use> pointing at an http(s) URL is stripped (we will not let a document render pull a third-party URL).
  2. Give it dimensions. Include width and height on the root <svg>, not just viewBox. A viewBox-only SVG renders blank as an image source in Safari/Firefox and in Typst/resvg. (We add them if missing, but don't rely on it.)
  3. No scripts, no HTML. <script>, on* handlers, <foreignObject> and javascript: URLs are stripped. A document is a static artifact.
  4. Stay small and quick. Hard caps: 12 s timeout, 4 MB response.
  5. Be deterministic. The same request should give the same picture — the PDF is rendered separately from the canvas.

Errors

Return a non-2xx and the cell falls back to a placeholder — it never breaks the page. Prefer answering 200 with an SVG that says what's wrong (e.g. "missing lat/lng") — the author sees the problem in place. All the reference providers do this.


Security — what the platform does with your SVG

Your code runs on your infrastructure; we only call it and embed the result. So the result is treated as untrusted input:

  • HTTPS only, and the URL is checked against private/loopback/link-local ranges including a DNS resolve-then-check (a hostname that resolves to a private IP is refused).
  • Redirects are refused — a redirect could point somewhere the check already cleared.
  • Timeout + size caps (12 s / 4 MB), and the Content-Type must look like SVG/XML/JSON.
  • The returned SVG is sanitized (scripts, event handlers, foreignObject, javascript:, external refs) before it reaches the canvas or the PDF.

Trust tiers

Tier What it means
0 Static — a baked SVG/PNG, no computation.
1 Declarative — a spec rendered by our engine. The built-in providers are tier 1.
2 Bounded expressions — restricted, no I/O.
3 Full compute — external providers are tier 3 by nature: the code is not ours.

External providers are badged in the editor so an author always knows a third party renders that cell.

Getting listed

The registry is curated: the platform never calls an arbitrary URL. A provider becomes available when its entry is added to the catalog with an id, title, description, a params_example (what the editor seeds when the author picks it), kind: "external", its url and a tier. Being in the catalog is the allowlist.


A minimal provider (copy-paste)

Any language works — it's just HTTP. In Deno:

Deno.serve(async (req) => {
  const { params, context } = await req.json();
  const w = Math.round((context?.size_mm?.w ?? 80) * 4);
  const h = Math.round((context?.size_mm?.h ?? 50) * 4);
  const text = String(params?.text ?? "¡Hola, Universe!");
  const esc = (s: string) =>
    s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");

  const svg =
    `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">` +
    `<rect width="${w}" height="${h}" rx="8" fill="#eef2ff"/>` +
    `<text x="${w / 2}" y="${
      h / 2
    }" text-anchor="middle" dominant-baseline="middle" ` +
    `font-family="Inter,sans-serif" font-size="${
      Math.round(h * 0.16)
    }" fill="#4338ca">${esc(text)}</text>` +
    `</svg>`;

  return new Response(svg, { headers: { "Content-Type": "image/svg+xml" } });
});

That is a complete cell type. No PaperStudio code changed.


Reference providers (built in)

These ship with the platform and double as worked examples of the contract:

Provider What it renders Notable
plot Line / bar / area chart Series take data[] or a whitelisted fn (sin, cos, sqrt, log, square, linear) the provider samples — compute, not just drawing
scatter Scatter plot Optional linear regression (least squares) with the fitted equation and
gauge KPI arc gauge Colored threshold zones, needle, value + unit
timeline Timeline / gantt bars Accepts ISO dates or numbers for start/end
location A real map with a marker Reuses the GIS engine (basemap tiles + pin) from lat/lng
flowchart LR
    C["celda universe<br/>{ provider_id, params }"] -->|"contexto: vars · size_mm · locale"| P["proveedor (HTTPS)<br/>tuyo o built-in"]
    P -->|"SVG (thumb | full)"| S["saneo + caps"]
    S --> R["canvas · Typst β · PDF"]

Related