cdn.si

rendered markdown titpetric

The front end’s shared markup

task-ui serves one stylesheet and one script, frontend/view/assets/task-ui.css and task-ui.js, to every page it draws, and a page of a module is one of those pages. Between them they carry behaviour a page gets by writing an attribute rather than by writing JavaScript, which is what makes a plugin’s page behave like the instance’s own without shipping a framework.

This chapter is the contract of that markup. A page, a partial, a Go template or a PHP module writes it and the script does the rest.

The run status label

A run’s state, live, anywhere in the interface:

<span class="state" data-run-id="01K8Z3Q0GJ9S2V7W4X5Y6Z7A8B"></span>

That is all of it. The script fills the span with the state the instance draws everywhere else — the coloured dot, the word, and the exit code of a run that ended badly — and rewrites it when the run ends. Every label on a page shares one event stream, so a page may carry fifty.

Attribute What it does
data-run-id the run to follow, a ULID. Required, and the span must be a span.
data-finished true for a run that was already over when the page was drawn: never hydrated, never subscribed.

A span the server leaves empty is filled once over GET /api/runs/{id}, because the stream carries a run when it starts and when it ends and says nothing in between: a page drawn in the middle of a run would otherwise show an empty label until the run finished. A span the server already filled is left alone until the stream has something newer. Either way, the script sets data-finished="true" on a label whose run has ended, and stops.

Write data-finished when you know. Most runs are over. A page that lists a hundred of them and says so for the ninety-nine that ended asks the server once instead of a hundred times, and subscribes to nothing. A Go template has the run in hand:

<span class="state" data-run-id="{{ .ID }}"{{ if ended .State }} data-finished="true"{{ end }}>{{ template "state" . }}</span>

A module has whatever its dispatch recorded. Task::run($note) answers the run a dispatch turned into, or "" for one that has not started yet, so the refinement module draws its label as:

{if $comment.run}<span class="state" data-run-id="{$comment.run}"></span>{/if}

What it is not

It is not the runs table. /runs keys its rows by tr[data-run-id] and updates each row whole — the state, the duration, the actions — from the same stream. The label claims span[data-run-id] and nothing else, so the two do not meet.

It is not a progress bar. A run reports two things over the stream, that it started and that it ended, and the label says which. A page that needs the output as it arrives attaches to the run’s terminal instead.

Where it comes from

onRuns(fn) in task-ui.js is the one subscription to /api/sse/runs; every handler on the page shares it. runLabels() is the label’s handler and the only thing a page has to know about is the markup above.


Contents · Modules · Development