Souspli

Souspli / Docs / How

Writing a type

A type is one self-contained HTML file — inline CSS and JavaScript, no external references — that renders a letter from its args. In the code and on the wire it is called a program. The fifteen built-in types in samples/ are all ordinary programs; read samples/nametag.html first, it is the smallest with real state.

To try your own: New → New from HTML…, pick the file and any attachments.

The contract

A program runs in the sealed container and sees only the bridge:

<script>
  const { type, args, attachments, mode } = window.bridge.getArgs()  // mode: 'view' | 'edit'
  const { locale, colorScheme } = window.bridge.viewerInfo()
  img.src = window.bridge.getBlob('poster.webp')       // a thing:// URL, or null
  window.bridge.emit('draft', { type, args, blobs })   // while editing
</script>

A program supplies its own interface for creating new instances of itself. The client never composes args: it shows your program in Edit mode, your program streams emit('draft', …) as the person types, the client renders that draft as the live preview, and the header's Publish button signs exactly the latest one after the person confirms.

This is a convention the client cannot enforce. The expectation is that people converge on a library of well-made types and disfavour ones that hardcode state or cannot create their own instances.

Rules the samples live by

What the container allows

Inline scripts and styles; images, audio, video and fonts from your own attachments (plus data: and blob: where the policy says). Video seeks, because attachments are served with range support.

Not allowed, by design: any network request, frames, forms that submit, navigation, new windows, permissions, persistent storage, plugins. Consequences worth designing for:

Be honest in the interface

The samples are careful about the difference between what is proven and what is claimed, and a good type copies that:

A program silently ignores what it does not read

A program reads the args fields it names and ignores the rest. Writing headline at an article that reads title fails nowhere — it renders "Untitled". Check the State: args {…} comment at the top of the sample, and then open the letter; a manifest containing your field proves nothing.

Reference: the samples

File Shows
nametag.html Minimal state; the draft/preview/publish loop.
card.html, invite.html Scalar fields; hiding unset fields; viewerInfo() for locale.
todo.html Array state.
memo.html Enclosures, and honesty about what cannot be opened.
poster.html Multiple image attachments, carry, arrangements, backward compatibility with older letters.
article.html Block editor, media placement, footnotes, video, provenance.
invoice.html Integer-only money; derived totals.
comment.html, attestation.html, vote.html, join-request.html Letters about other letters; echoing the target on every emit.
contract.html, group.html, vouch.html Letters about keys: signatories, rosters, vouches.

Longer notes on each: samples/README.md.