Skip to content

Player

<astl-player> mounts a view, attaches the component elements, renders the chrome (pages, actions, results) and runs one attempt. It never holds answer logic: verdicts come from a checker.

Define it once with the elements you use (astrolabe.js and astrolabe-player.js do this for the built-ins):

import { definePlayer } from '@astrolabe/player';
import { builtInElements } from '@astrolabe/components/elements';
definePlayer({ elements: builtInElements }); // or only some, e.g. [gapElement]
definePlayer option Type and meaning
elements ElementEntry[]: component elements. A registration ({ manifest, element, messages }) is defined under its manifest tag right away; a LazyElement ({ tag, load }) is imported the first time a view contains its tag, and a load that fails shows load_failed (the next view tries again). The built-ins are lazy.
catalogs MessageCatalog[]: more or overriding messages, for example another UI locale
styleTemplates StyleTemplate[]: named looks that style-template picks on <astl-doc>, a component or the player: Looks
compileSource SourceCompiler: turns a light-DOM source into a view and a checker (HTML with answers); astrolabe.js registers one
urlPolicy UrlPolicy: which links, pictures and url attributes of a view stay; the engine's policy, or the player removes what it kept. Default isSafeUrl; one policy per page, the last call that passes one sets it. An address stays only when the policy answers true (URLs)
registry CustomElementRegistry: where the elements are defined. Default: the global customElements

Calling it again is safe: a tag already defined by the same class, or a lazy element with the same load, is skipped; a tag taken by another class, another loader or the other kind of entry throws.

The player loads the first of these that it finds:

  1. the view property (or load({ view }));
  2. an <astl-doc> in its light DOM: a compiled view plays directly; a source (with author ids and answers) goes through compileSource, and without one the player shows source_needs_compiler;
  3. the src attribute: fetched, checked against integrity, then played.
Attribute Values and meaning
locale A BCP 47 tag: the UI locale (buttons, messages, announcements). Matched against the available catalogs, with English as the fallback. Default: the player's lang, else the browser languages.
theme light, dark, hc or auto: the colour theme; auto (the default) follows prefers-color-scheme. Written to the player's data-theme.
src A URL: a view to fetch when no view is given.
integrity A lowercase hex SHA-256: the viewHash the fetched src must match; a mismatch (or an empty value) shows integrity_mismatch.
endpoint A URL: the grading endpoint of this attempt (protocol 1). Creates an httpChecker when no checker is set.
persist Boolean: keep snapshots in localStorage (key astl:snapshot:<viewHash>) and restore them on load. Off by default.
style-template A registered template name: the default look where neither the view's <astl-doc> nor a component names one (property styleTemplate): Looks.
Property Type and meaning
view string: the view HTML to play. Setting it loads it. Default ''.
policy AttemptPolicy: which buttons and rules apply. Default PRACTICE_POLICY. Changing it reloads the view and keeps the answers.
checker Checker | null: grades checks and submits. Default null: an HTTP checker for endpoint, if set.
snapshot Snapshot | null: restored on load when its viewHash matches the view (or is null). Default null.
locale, theme, src, integrity, endpoint, persist The attributes above.
state (read only) AttemptState: loading, ready, active, checking, submitting, submitted, review or error.
viewHash (read only) string | null: SHA-256 of the mounted view; null before mounting or without WebCrypto.
page (read only) number: the 0-based index of the current page.
Method Meaning
load(options): Promise<void> Plays a view: options is { view, policy?, checker?, snapshot? }, and omitted fields keep the current properties. Resolves when it is mounted (astl-ready); rejects with the error code when it cannot.
check(): Promise<CheckResponse | null> Practice: grades and shows verdicts. Failures are shown and emitted as astl-error, never thrown.
submit(): Promise<SubmitResponse | null> Hands the answers in (final) and moves the focus to the result. Failures are shown and emitted, never thrown.
goTo(index): boolean Goes to a page; false when not allowed (linear navigation, unknown page).
next(): boolean, previous(): boolean One page forward or back.
reset(): boolean Clears answers and results and goes to the first page; not after a hand-in.
getAnswers(): Answers The current answers, by slot id.
setAnswers(answers): void Sets answers without change events ('' clears). Only what the elements accept is kept.
getSnapshot(): Snapshot | null Everything needed to restore the attempt later.

All events bubble and are composed (they cross shadow roots). Names are in PlayerEvent of @astrolabe/core.

Event Detail When
astl-ready ReadyDetail: { viewHash: string; pages: number; slots: number } the view is mounted and every element has rendered
astl-change ChangeDetail: { answers: Answers } (changed slots only, '' = cleared) a student changed an answer (sent by the element)
astl-page PageDetail: { index: number; count: number; id: string } the page changed
astl-state StateDetail: { state: AttemptState; previous: AttemptState } the attempt state changed
astl-result CheckResponse | SubmitResponse a check or a hand-in finished
astl-snapshot Snapshot there is progress worth saving (debounced, 300 ms)
astl-error ErrorDetail: { code: string; message: string } loading, checking or handing in failed; message is in the UI locale
import { PlayerEvent, type Snapshot } from '@astrolabe/core';
const player = document.querySelector('astl-player')!;
player.addEventListener(PlayerEvent.snapshot, (event) => {
const snapshot = (event as CustomEvent<Snapshot>).detail;
void fetch('/api/drafts/123', { method: 'PUT', body: JSON.stringify(snapshot) });
});

astl-error and the player's own notices use these codes; each has a message error.<code> in the astl.player catalog.

Code Meaning
invalid_request, unauthorized, check_not_allowed, checks_exhausted, not_found, method_not_allowed, attempt_closed, payload_too_large, internal_error a problem from the checker: the grading protocol codes
no_checker there is nothing to grade with (no checker, no endpoint, no compiled source)
load_failed src could not be fetched, or a lazy element the view uses did not load
view_invalid the view is not one <astl-doc>
integrity_mismatch the fetched view does not match integrity
source_needs_compiler a source with answers is in the light DOM, but no compileSource is registered (use astrolabe.js)
source_invalid the source does not compile; its errors are listed under the notice for the author
Policy Buttons Notes
practice "Check"; "Start over" after a result "x checks left" when maxChecks is set; "Check" is aria-disabled when no check is possible
test "Hand in" asks "Hand in anyway?" when answers are empty; shows what reveal allows
review none read only

With navigation: 'linear' there is no "Back" button and the page progress is not clickable. With a deadline, the header shows a countdown, the live region announces 5 and 1 minutes left, and the player hands in when it reaches zero (the server stays authoritative).

player, header, canvas, result, footer, problem-details. See Looks.

Component elements talk to the player only through PlayerElementApi (@astrolabe/core): slotIds, getAnswers(), setAnswers(), feedback, solution, disabled, units() and attach(context); an element graded in parts also lists its groupIds and gets their verdicts in feedback.groups. The base classes AstlElement and SingleSlotElement in @astrolabe/player implement most of it; see Write your own component and the API reference.