Chapter 22: jev-ui — what the Jev sites' pages share

lmjtfy.fun and whiskers.lmjtfy.fun look alike on purpose: near-black on white, one pink band, ordered-dither dot fields and old operating system windows with a black title bar, after typesafe.ai. This crate is the one copy of that look, so the second site did not get a second copy of it.

It is the opposite of its neighbours in one way: nothing here talks to Jev. It is the page around whatever a site says.

What a site gets

FromWhat
assetThe files a page loads, embedded: ui.css, ui.js (the copy buttons and the dither fields), the three fonts and a fourth, a small pixel-emoji font. asset::find(path) for a fallback route, Asset::href() for a link named by the hash of its bytes.
shelldocument(head, body_attrs, body): the one head (title, preview tags, preloaded fonts, the shared sheet and script, and a <script> for Datastar when asked) and the body's open and close. Assets::Linked for a site whose policy forbids inline script, Assets::Inline for one that does not.
componentsnav, headline, window (and window_with_id), sheet, band, copy_line, footer, skip_link, the 12 by 12 pixel_icon, and SerenityOS's pixel emoji: PixelEmoji (play, pause, the two hourglasses), pixel_emoji and an hourglass that turns over (still under reduced motion).
previewPreview: Open Graph and Twitter tags that cannot be built wrong (an origin that is not https, a video of odd size, a card too small to be shown large), for a still card or a looping video, and missing(html, kind) to check a rendered page.
wireA call to Jev as a window: json prints a body from the raw text (whitespace added, nothing else), exchange is the folding request-and-response window (exchange_with when the title bar is markup or the body starts with something of the site's), bars is lmjtfy's probability rule with an optional decision tick, printed_json reads a panel back for a test.
flowspine: a flow drawn as one SVG from rows of steps, each with the ways it can end. Built from the data it is given, so a site that builds the rows from the code that runs the flow cannot draw a step the code lacks.
cardThe preview card's look as SVG: Chrome (the dither, the pink band, the window with its title bar, in lmjtfy's measures), and pixel text. A site composes what goes inside the window.

A page, in whiskers' shape:

use jev_ui::components::{Link, Nav, band, nav, sheet, window};
use jev_ui::shell::{Assets, Attr, Head, document};

let head = Head { title: "Whiskers", meta: preview_tags, style: SITE_CSS, assets: Assets::Linked,
                  datastar: true, icon: Some("/favicon.svg"), extra: maud::html! {} };
let page = document(&head, &[Attr::new("data-signals", "{}")], body);

and in the Worker, every asset the crate owns in one route:

// as the router's fallback; `path` and `query` are the request's
if let Some(asset) = jev_ui::asset::find(path) {
    // content-type: asset.content_type, cache-control: asset.cache_control(query), body: asset.body
}

and the script Datastar needs, from the Aldebaran crate that owns it:

// a route for `jev_ui::shell::DATASTAR_PATH`
// content-type: text/javascript; charset=utf-8, body: aldebaran_datastar::SCRIPT

What is a parameter and what is not

Everything a site differs in is a field: the title, the tags, the site's own stylesheet (it comes after the shared one), the nav's links, what sits between the name and the links, the footer's words, a picture's query. The look is not a parameter: there is one palette, light only, as typesafe.ai's is. A site that wants another look is not a Jev site.

Fonts are served by the site itself, from this crate (Inter Tight, JetBrains Mono and VT323, see assets/). A Jev site makes no request to another site for them.

Try it

cargo test -p jev-ui
cargo build -p jev-ui --target wasm32-unknown-unknown

lmjtfy on it

lmjtfy (~/lmjtfy) wears this crate: its head and body come from shell::document with Assets::Inline (its pages set no content security policy), its nav, headline, windows, sheet, band, footer and copy line from components, its icons from components::icons, its JSON panels, a choice's rule and its Jev, LLM and tool call windows from wire, and its fonts from /fonts/*, one route per asset::ALL entry (/{repo}/{*path} would otherwise take them). page.css holds only lmjtfy's own rules, after the shared sheet, and page.js only its own behaviour. Datastar's script is aldebaran_datastar::SCRIPT, served by each site at shell::DATASTAR_PATH.

Outstanding

  • lmjtfy's own Open Graph tags (view.rs's preview and view/code.rs's) are still its own: the home page's carries a canonical link and noindex that preview::Preview has no field for, and they should move onto it.
  • A score's bars (a tag in the label) and the budget's (a figure of its own, not a percentage) are still lmjtfy's; wire::Bar has neither a tag nor a figure.
  • packages/card draws lmjtfy's own card straight into a PNG with the same palette, dither and pixel text as card.rs; now that lmjtfy depends on this crate, one of the two should draw from the other.
  • lmjtfy's .rete diagram is Rete-specific and stays its own, but its CSS box language is what flow borrows.
  • Mermaid (lmjtfy's code pages) is still fetched from jsdelivr there; it is not part of this crate.

For the people who maintain it

In this folder

PathWhat
src/The code: Chapter 22½.
assets/The stylesheet, the script and the fonts: Chapter 22¾.
Cargo.tomlDependencies: maud and font8x8 only.
CLAUDE.mdInvariants for agents.

← Previous: Chapter 21½: inside jev-mock/src · Up: jevcrates · Next: Chapter 22½: inside jev-ui/src →