poka.sh

Docs

How a preview is produced

explanation

A Poka preview is the result of a short pipeline. This page explains the shape of that pipeline and why it is built this way. It does not repeat the contracts that parser and viewer authors work to: those live with the code that owns them.

Three kinds of module

Everything that is not handled by the host itself is a wasm32-wasip1 module with a plugin.json manifest. There are three kinds:

Kind plugin.json Reads on stdin Writes on stdout
Monolithic no viewer field PreviewRequest a full PreviewResponse
Parser (composed) "viewer": "poka-<shape>-viewer" PreviewRequest one *Ast message
Viewer (shared) "file_extension": "" a *Ast message UTF-8 HTML

A composed parser knows nothing about HTML. It decodes the file into an *Ast (a table, a tree, a metadata card, a 3D scene, and so on). The host pipes that message into the viewer named in the manifest, takes the viewer's HTML, and wraps it in an HtmlPreview. A viewer is looked up by its id, never by file extension, so one viewer serves many parsers.

The reason for the split is cost. A parser is typically a few dozen to a few hundred lines of format logic and ships no HTML or CSS, so the look of every table, tree or card is decided once, in one viewer, and follows the host theme. A monolithic module is the right choice only when a format needs a response that no shared viewer draws (an SVG, a decoded image, a CAD drawing).

The two modules never call each other. Both speak only the protobuf schema core/proto/preview.proto, which is why the same .wasm runs unchanged on Windows, Linux, macOS and in the browser.

Who gets the file first

When you preview a file, the host picks a handler in this order:

  1. Host-native formats, drawn by the host with no extension: inline SVG, the images a webview decodes itself (JPEG, PNG, GIF, WebP, BMP, AVIF), TIFF (decoded and re-encoded to PNG), Markdown (rendered to HTML), and a long list of source-code and configuration extensions shown as text. One exception: when a parser for one of those raster formats is installed, it gets the file instead and the host passes the bytes on only with nothing installed. The browser engine has no native image arm, so this is what keeps the two hosts showing the same preview: the parser supplies the metadata card (EXIF, frame and loop counts) and its own 8 MiB cap (poka-interfaces#511). A multi-page TIFF already works this way.
  2. A wasm extension. The route table maps the lowercased file name to exactly one module: first an exact file name, then a whole extension, then the longest dotted suffix. Content signatures can route a file whose name is missing or ambiguous (an .xml is routed by its root element, for example). The module runs in the sandbox below.
  3. Nothing installed. The desktop app shows a card with the extension, the expected extension id and the file size, and offers to install it. The web app, which ships every route, reports that no extension handles the file.

The precise routing rule is one rule on both hosts and is described in the parity contract.

The sandbox

Modules run under Wasmtime on the desktop and in a Web Worker with a JavaScript WASI shim in the browser. The limits the manifest declares are enforced by the host, not promised by the module:

  • Filesystem: only the directory of the previewed file, read-only, mounted at /input. Sibling files are visible, which is how multi-file formats (OBJ with MTL, glTF with its buffers) find their companions.
  • Memory is capped at memory_mb (desktop), time at max_time_ms (an epoch watchdog traps the module), and the instance count at five.
  • Output: the desktop host caps stdout at 64 MB.
  • No network, no environment, no threads.

The two hosts differ in a few small ways (for example the browser shim cannot cap memory.grow, and the browser gives a module max(30 s, 3 x max_time_ms)). Those are listed, with the reasons, in the parity contract.

Where each piece lives

Piece Repository
The schema, the desktop host, the web app and the mobile shell poka-interfaces
Parsers, one crate per format poka-parsers
Shared viewers poka-viewers
The real-file corpus the tests run against poka-samples

How a new format moves through all of them is in Add a format end to end.