138 lines
7.6 KiB
PHP
138 lines
7.6 KiB
PHP
{{-- 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. --}}
|
|
@php
|
|
// Ein Grund, den schon der Server kennt (kein Schlüssel hinterlegt, Host
|
|
// noch nicht übernommen), steht sofort auf der Bühne. Sonst beginnt sie mit
|
|
// dem Vorspann. Derselbe Bau, anderer Text, anderer Ton.
|
|
$stageTitle = $problem !== ''
|
|
? __('hosts.terminal.problem.'.$problem.'.title')
|
|
: __('hosts.terminal.stage.connecting.title', ['host' => $host->name]);
|
|
|
|
$stageNote = $problem !== ''
|
|
? __('hosts.terminal.problem.'.$problem.'.note')
|
|
: __('hosts.terminal.stage.connecting.note');
|
|
|
|
// Was der Browser erst später braucht. Welcher Schlüssel wann gilt, steht
|
|
// in resources/js/terminal.js neben den Schließcodes der Brücke.
|
|
$messages = [
|
|
'ended' => [
|
|
'title' => __('hosts.terminal.stage.ended.title'),
|
|
'note' => __('hosts.terminal.stage.ended.note', ['host' => $host->name]),
|
|
],
|
|
'lost' => [
|
|
'title' => __('hosts.terminal.stage.lost.title'),
|
|
'note' => __('hosts.terminal.stage.lost.note', ['host' => $host->name]),
|
|
],
|
|
'rejected' => [
|
|
'title' => __('hosts.terminal.stage.rejected.title'),
|
|
'note' => __('hosts.terminal.stage.rejected.note'),
|
|
],
|
|
'unreachable' => [
|
|
'title' => __('hosts.terminal.stage.unreachable.title', ['host' => $host->name]),
|
|
'note' => __('hosts.terminal.stage.unreachable.note', ['host' => $host->name]),
|
|
],
|
|
'service_down' => [
|
|
'title' => __('hosts.terminal.stage.service_down.title'),
|
|
'note' => __('hosts.terminal.stage.service_down.note'),
|
|
],
|
|
'not_here' => [
|
|
'title' => __('hosts.terminal.stage.not_here.title'),
|
|
'note' => __('hosts.terminal.stage.not_here.note'),
|
|
],
|
|
];
|
|
@endphp
|
|
<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 die Komponente keine Aktion, die einen
|
|
Re-Render auslöst — aber das erste `wire:`-irgendwas auf dieser 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 sechs 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.
|
|
|
|
Zwei Größen: die Zeichnung ist 51 Zellen breit, und eine Zelle ist
|
|
gut 0,6 der Schriftgröße. 15px ergeben rund 460px — auf dem Schirm
|
|
eine Wortmarke, kein Etikett. Am Telefon sind es 10px und damit gut
|
|
300px, was neben den Rändern noch hineinpasst; bei 15px schöbe sie
|
|
die Seite zur Seite. --}}
|
|
<pre data-stage-mark
|
|
class="select-none font-mono text-[10px] leading-none text-accent sm:text-[15px]"
|
|
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">{{ $stageTitle }}</p>
|
|
<p data-stage-note class="text-xs leading-relaxed text-bg opacity-70">{{ $stageNote }}</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' => $problem !== 'unknown'])>
|
|
<x-ui.button data-stage-retry-button variant="secondary" size="sm">
|
|
{{ __('hosts.terminal.stage.retry') }}
|
|
</x-ui.button>
|
|
</div>
|
|
</div>
|
|
</div>
|