nematic 0.1.0

Portable smolweb rendering engine — Gemini, Gopher, Markdown, feeds, and the knot note format, lowered to the inker document model.
Documentation

nematic

nematic is the smolweb engine family for serval and its hosts. It has two products: nematic::blocks-style document engines (the default) that lower Gemini, Gopher, Spartan, Finger, plain text, Markdown, RSS/Atom, and the knot note format into inker's portable block model — the stored/authored lane (cards, clips, worker-shippable packets) — and, behind the views feature, nematic::views: native per-format xilem-serval views over errand's ASTs, the idiom-preserving live-render lane (real focusable links, per-format classes, per-site theming; no lowest-common-denominator lowering).

Home: mark-ik/serval, at components/nematic (adopted 2026-07). The former standalone repository is archived and links here.

For fullweb rendering (CSS, JS, embedded media), mere routes through Serval (the Servo/wgpu fork) or a system webview. Nematic does not own an HTML reader-mode lane — that's Serval's future "three-head Hekate" mode (smolweb extract / middlenet / fullweb negotiator for the same HTML input). Nematic stays for protocols whose grammar the engine can fully parse natively.

Naming

Nematic is borrowed from liquid-crystal physics: a nematic phase has orientational order without positional order; rod-shaped molecules all point the same way but otherwise flow freely. Light passes through aligned nematic crystals coherently, and that's the basis of LCDs.

If the web is a lenticular soup of pixels, then nematic is the engine that tries to align the molecules and let the light through.

Engines

Twelve concrete inker::Engine implementations, each spec-faithful to its source format. Use engines() to register all twelve in one call.

Engine ID Module Notes
Markdown nematic.markdown markdown CommonMark via pulldown-cmark
Gemtext nematic.gemtext gemtext Gemini's text/gemini line-oriented format
Gopher nematic.gopher gopher RFC 1436 menu parser; synthesised gopher:// URLs per RFC 4266
Feed nematic.feed feed RSS 2.0 + Atom 1.0 via quick-xml; emits FeedHeader + FeedEntry semantic blocks
Text nematic.text text Plain text with paragraph splitting
File nematic.file file Extension-based dispatch for file:// content (.md/.gmi/.gophermap/.xml/.knot/…)
Finger nematic.finger finger RFC 1288 finger responses; tags text/x-finger
Knot nematic.knot knot Mere's native note / clip format (frontmatter + polyglot markdown)
Scroll nematic.scroll scroll scroll.mozz.us body engine; delegates to gemtext or markdown by content-type
Misfin nematic.misfin misfin misfin.org gemini-style mail body
Nex nematic.nex nex Nex directory listings + plain text content
Guppy nematic.guppy guppy UDP-smolweb body (gemtext shape)

All engines populate EngineDocument.provenance with their own engine ID and the request address; trust state defaults to Unknown (the host overrides after transport verification).

Knot: the native note / clip format

Knot (nematic.knot) is Mere's polyglot note format and the load-bearing output of the clip workflow. A knot body is CommonMark with fenced code blocks whose language tag dispatches to a real engine:

---
title: Mixed Clip
source: https://blog.test/article
captured: 2026-05-08T14:23:00Z
trust: tofu
note_kind: clip
tags: [research, semantics]
---

User prose with [[wikilinks]] and #hashtags.

` ` `gemtext
=> gemini://capsule.test/ a capsule
* a bullet
` ` `

` ` `feed-entry
title: Linked article
url: https://blog.test/post
date: 2026-05-08
` ` `

` ` `gopher
0README<TAB>/readme.txt<TAB>example.org<TAB>70
` ` `
  • Frontmatter (YAML subset) populates provenance (source, captured, source_label), trust state, and emits note_kind / tags as MetadataRow blocks.
  • Fenced protocol blocks (gemtext, gopher, nex, feed-entry, feed-header, metadata-row, badge) are expanded into real semantic blocks by nematic::knot::expand. Unknown languages (e.g. python, rust) pass through as code blocks unchanged.
  • Wikilinks [[name]] rewrite to mere://node/<slug> (slug is lowercased, whitespace → -); display text preserves the original.
  • Hashtags #tag at word boundaries are extracted from paragraph text and emitted as Badge sibling blocks (so search / intelligence layers see them as semantic markers).
  • build_clip_knot(blocks, provenance, trust, note_kind) assembles a ready-to-save .knot string from selected blocks plus the source's provenance. The host's clip gesture wires up to this once the clip UI lands.
  • build_clip_knot_with_block_provenance(...) is the multi-source variant: takes an additional inker::BlockProvenanceMap sidecar and emits a block_sources: ["<index>|<uri>[|<anchor>]", ...] frontmatter list for blocks whose source differs from the document. Use this when composing a clip from heterogeneous sources (federated feed merge, citation overlay, multi-tab clip). Round-trip restoration through KnotEngine is gated on a concrete consumer; the producer side documents the shape so downstream readers can parse it directly.
  • Round-trip: EngineDocument::to_knot() (in inker) re-emits semantic blocks as fenced code blocks with their language tag, so a parsed knot serialises back into an equivalent knot.

See design_docs/nematic_docs/implementation_strategy/2026-05-08_polyglot_knot_design.md for the full design.

How it relates to other workspace crates

nematic is the engine that inker dispatches to for smolweb URI schemes; rendered output is presented through verso-core's surface contracts.

   inker.routing
      │ EngineRouteDecision
      │ engine_id ∈ { nematic.smolweb, nematic.file }
      ▼
   nematic
      │ rendered content
      ▼
   verso-core (CompositedTexture surface)
  • inker — references nematic by engine ID. The default policy routes gemini, gopher, finger, spartannematic.smolweb, and filenematic.file.
  • verso-core — nematic's output is presented as a CompositedTexture surface; verso-core owns the surface lifecycle.
  • mere — composes nematic into the product.

Status

Pre-1.0. Markdown lane shipped; smolweb (gemini/gopher), file, and feed lanes pending. Implementation is in progress within the mere workspace.

Fun Fact

My first idea for the crate's name was middlenet, intended to encapsulate the smolweb and well-structured web content. This notion of a browser that could manage whatever protocol it was offered calls to mind a quote from the game Elden Ring:

"Heresy is not native to the world; it is but a contrivance. All things can be conjoined."

Accordingly, another possible name was "miriel," and a fourth, "turtlepope." All protocols can be conjoined?

License

MPL-2.0.