128 lines
7.3 KiB
PHP
128 lines
7.3 KiB
PHP
@props([
|
|
// Das Ticket, das der Browser einlöst. Dreißig Sekunden, genau einmal.
|
|
'ticket' => '',
|
|
// Was auf der Bühne steht, wenn die Seite aufgeht.
|
|
'title' => '',
|
|
'note' => '',
|
|
// Die Meldungen, die erst der Browser kennt (Schließcodes der Brücke).
|
|
'messages' => [],
|
|
// Ob ein neuer Versuch überhaupt etwas ändern kann.
|
|
'retry' => false,
|
|
'retryLabel' => '',
|
|
])
|
|
{{-- Schirm und Bühne für eine Terminalsitzung — geteilt von beiden Fenstern.
|
|
|
|
Das gab es zuerst nur einmal, für einen Host. Als das Terminal zum
|
|
CluPilot-Server dazukam, waren zwei Wege möglich: abschreiben oder
|
|
herausziehen. Abgeschrieben wäre der Vorspann zweimal da und würde einmal
|
|
gepflegt — und was hier steht, ist zum guten Teil AM BILDSCHIRM GEMESSEN
|
|
(Zellbreiten, Schriftgrößen, welche Klasse welche schlägt). Genau solche
|
|
Zeilen überleben eine Abschrift nicht.
|
|
|
|
Was NICHT hierher gehört: die Texte. Die kommen von der Seite, die diese
|
|
Bühne benutzt — ein Fenster spricht von einem Host, das andere vom Server,
|
|
und ein Bauteil, das beides selbst entscheidet, hätte einen Grund, ihre
|
|
Sprachdateien zu kennen.
|
|
|
|
resources/js/terminal.js bleibt davon unberührt: es hängt an den
|
|
data-Attributen unten, nicht an einer Seite. --}}
|
|
|
|
{{-- Die Bühne liegt ÜBER dem Terminal, nicht daneben.
|
|
|
|
Bis hierher waren es zwei Geschwister, von denen abwechselnd eines
|
|
`hidden` trug: erst der Vorspann, dann der Schirm. Das trug genau einmal —
|
|
beim Aufbau. Für alles danach fehlte die Rückfahrkarte, und was der
|
|
Betreiber beim Abbruch sah, war eine rote ANSI-Zeile mitten in der eigenen
|
|
Ausgabe: die einzige Stelle im Produkt, an der eine Meldung nicht gestaltet
|
|
war, sondern getippt.
|
|
|
|
Als Überlagerung kann dieselbe Bühne dreimal auftreten — beim Verbinden,
|
|
beim Abbruch, beim Ende — und die Sitzung darunter bleibt stehen, statt
|
|
gelöscht zu werden. Was auf ihr steht, kommt aus den Sprachdateien; die
|
|
Meldungen, die erst der Browser kennt, reisen als JSON in
|
|
`data-stage-messages` mit, damit auch sie dort stehen und nicht im Skript. --}}
|
|
<div class="relative flex h-screen w-screen flex-col overflow-hidden bg-ink"
|
|
data-terminal
|
|
data-ticket="{{ $ticket }}">
|
|
|
|
{{-- wire:ignore: xterms eigener DOM-Baum liegt in einem von Livewire
|
|
verwalteten Baum. Heute hat keine der beiden Komponenten eine Aktion,
|
|
die einen Re-Render auslöst — aber das erste `wire:`-irgendwas auf so
|
|
einer Seite würde xterm sonst beim nächsten Umlauf löschen. --}}
|
|
<div data-terminal-screen wire:ignore class="flex-1 overflow-hidden"></div>
|
|
|
|
<div data-terminal-stage
|
|
class="absolute inset-0 z-10 flex flex-col items-center justify-center gap-6 bg-ink px-6 text-center"
|
|
data-stage-messages="{{ json_encode($messages, JSON_UNESCAPED_UNICODE) }}">
|
|
|
|
{{-- Die Wortmarke als Schriftgrafik.
|
|
|
|
`role="img"` mit lesbarem Namen statt `aria-hidden`: ein
|
|
Screenreader soll „CluPilot" hören, nicht die einzelnen Zeichen der
|
|
Zeichnung.
|
|
|
|
Selbst gezeichnet, nicht aus einer Figlet-Schrift übernommen. Die
|
|
übernommene Fassung hatte im Bogen des `P` einen Überhang — eine
|
|
Zelle in der zweiten Zeile, unter der in der ersten nichts stand —
|
|
und der stand als loser Klotz mitten im Wort. Figlet-Schriften sind
|
|
für echte Terminalschriften gezeichnet und tragen hier nicht. Jeder
|
|
Buchstabe ist acht Zellen breit (das `I` zwei), mit einer Zelle
|
|
Abstand, und keine Zelle steht ohne Nachbarn darüber. Nicht von Hand
|
|
abgezählt, sondern aus einer Buchstabentabelle gesetzt — von Hand
|
|
hatte ich mich prompt verzählt, und die Buchstaben liefen ineinander.
|
|
|
|
`█` füllt seine Zelle ganz aus und stapelt deshalb in jeder Schrift
|
|
sauber — anders als `_ / \ |`, die auf Berührung zwischen den Zeilen
|
|
angewiesen sind. Bei enger Zeilenhöhe verschmierte das Wort damit,
|
|
bei weiter zerfiel es. Beides im Browser durchgemessen.
|
|
|
|
Immer Akzentfarbe, auch im Fehlerfall: Dunkelrot auf Fast-Schwarz
|
|
hatte kaum Kontrast, und eine Wortmarke, die ihre Farbe wechselt, ist
|
|
ohnehin keine Statuslampe — was los ist, sagt die Zeile darunter.
|
|
|
|
Acht Zellen pro Buchstabe, nicht sechs: bei sechs standen sie hoch
|
|
und schmal da. Acht trifft die Proportion der Wortmarke.
|
|
|
|
Zwei Größen: die Zeichnung ist 65 Zellen breit, und eine Zelle ist
|
|
gut 0,6 der Schriftgröße. 13px ergeben rund 500px — auf dem Schirm
|
|
eine Wortmarke, kein Etikett. Am Telefon sind es 7px und damit rund
|
|
270px, was neben den Rändern noch hineinpasst. --}}
|
|
<pre data-stage-mark
|
|
class="select-none font-mono text-[7px] leading-none text-accent sm:text-[13px]"
|
|
role="img" aria-label="CluPilot"> ██████ ██ ██ ██ ████████ ██ ██ ██████ ████████
|
|
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
██ ██ ██ ██ ████████ ██ ██ ██ ██ ██
|
|
██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
██████ ████████ ██████ ██ ██ ████████ ██████ ██ </pre>
|
|
|
|
{{-- aria-live: die Bühne tauscht ihren Text aus, ohne dass die Seite neu
|
|
lädt. Ohne diesen Hinweis erführe ein Screenreader vom Abbruch
|
|
nichts — für ihn stünde das Fenster weiter beim Vorspann. --}}
|
|
<div class="max-w-sm space-y-2" aria-live="polite">
|
|
<p data-stage-title class="text-sm font-semibold text-bg">{{ $title }}</p>
|
|
<p data-stage-note class="text-xs leading-relaxed text-bg opacity-70">{{ $note }}</p>
|
|
</div>
|
|
|
|
{{-- Neu laden statt neu verbinden: das Ticket gilt dreißig Sekunden und
|
|
genau einmal, ein zweiter Anlauf mit demselben wäre keiner. Der
|
|
Knopf ist beim Vorspann verborgen und kommt erst, wenn ein neuer
|
|
Versuch überhaupt etwas ändern kann — bei „kein Schlüssel
|
|
hinterlegt" ändert er nichts, dort bleibt er weg. Was er tut, hängt
|
|
terminal.js an; ein `onclick` im Markup wäre Verhalten an der
|
|
Stelle, an der sonst nur Gestalt steht.
|
|
|
|
`hidden` sitzt am Wrapper, nicht am Knopf. Nicht, weil es dort nicht
|
|
wirkte — Tailwind gibt `.hidden` als letzte der Display-Klassen aus
|
|
und schlägt `inline-flex` damit —, sondern weil die Klassen des
|
|
Knopfes aus einem geteilten Bauteil kommen und an dieser Stelle
|
|
niemandem gehören. Die Bühne selbst trägt `hidden` und `flex`
|
|
durchaus gemeinsam: das ist ein Element, das hier steht und hier
|
|
bleibt. --}}
|
|
<div data-stage-retry @class(['hidden' => ! $retry])>
|
|
<x-ui.button data-stage-retry-button variant="secondary" size="sm">
|
|
{{ $retryLabel }}
|
|
</x-ui.button>
|
|
</div>
|
|
</div>
|
|
</div>
|