{"id":324,"date":"2026-09-05T18:17:40","date_gmt":"2026-09-05T18:17:40","guid":{"rendered":"https:\/\/www.fabricioruch.ch\/?p=324"},"modified":"2026-09-05T18:17:40","modified_gmt":"2026-09-05T18:17:40","slug":"eine-tui-ist-mehr-als-farbiges-write-host","status":"publish","type":"post","link":"https:\/\/www.fabricioruch.ch\/?p=324","title":{"rendered":"Eine TUI ist mehr als farbiges Write-Host"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Terminalanwendungen erleben seit einigen Jahren eine kleine Renaissance.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Viele moderne Entwicklerwerkzeuge laufen wieder dort, wo Softwareentwicklung ohnehin h\u00e4ufig stattfindet: im Terminal. Paketmanager, Git-Werkzeuge, Cloud-CLIs und zunehmend auch KI-Assistenten zeigen, dass eine textbasierte Oberfl\u00e4che weder altmodisch noch zwangsl\u00e4ufig primitiv sein muss.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Moderne Terminalanwendungen verwenden Farben, Panels, Tabellen, Spinner, Fortschrittsanzeigen und dynamische Statusinformationen. Manche f\u00fchlen sich eher wie kleine Anwendungen als wie klassische Kommandozeilenprogramme an.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das verleitet zu einer einfachen Schlussfolgerung:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Ein paar ANSI-Farben, ein Spinner und etwas ASCII-Art \u2013 fertig ist die TUI.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Genau das ist der Fehler.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine gute Terminal User Interface ist keine Sammlung h\u00fcbscher <code>Write-Host<\/code>-Aufrufe.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie ist eine Benutzeroberfl\u00e4che.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Und damit gelten erstaunlich viele der gleichen Architekturprinzipien wie bei einer GUI.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">CLI und TUI sind nicht dasselbe<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Zun\u00e4chst lohnt sich eine begriffliche Trennung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine klassische CLI kann beispielsweise so aussehen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&gt; tool process\n\nConnecting...\nFound 184 items.\nProcessing...\n184 items processed.\nDone.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das Programm schreibt Informationen nacheinander auf <code>stdout<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Jede neue Ausgabe erweitert das bisherige Terminalprotokoll.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine TUI funktioniert anders.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie behandelt das Terminal zumindest teilweise als <strong>Darstellungsfl\u00e4che<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 PROCESSING                                           \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Current folder   Archive \/ 2025                      \u2502\n\u2502                                                      \u2502\n\u2502 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591  63%           \u2502\n\u2502                                                      \u2502\n\u2502 Processed        116                                 \u2502\n\u2502 Remaining         68                                 \u2502\n\u2502 Failed             0                                 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Beim n\u00e4chsten Update soll nicht zwangsl\u00e4ufig ein weiterer solcher Block darunter erscheinen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Stattdessen ver\u00e4ndert sich der bestehende Screen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Damit entsteht ein v\u00f6llig anderes Problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Es geht nicht mehr nur darum, <strong>was ausgegeben wird<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Es geht darum:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>wo etwas dargestellt wird,<\/li>\n\n\n\n<li>wann es aktualisiert wird,<\/li>\n\n\n\n<li>was stehen bleibt,<\/li>\n\n\n\n<li>was gel\u00f6scht werden muss,<\/li>\n\n\n\n<li>welche Informationen zusammengeh\u00f6ren,<\/li>\n\n\n\n<li>wie Zustandswechsel aussehen,<\/li>\n\n\n\n<li>was passiert, wenn sich die Terminalgr\u00f6\u00dfe \u00e4ndert.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Damit betreten wir UI-Territorium.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><code>Write-Host<\/code> ist kein UI-Framework<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">PowerShell macht den Einstieg besonders einfach:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Write-Host \"Connecting...\" -ForegroundColor Cyan\nWrite-Host \"Success\" -ForegroundColor Green\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das funktioniert hervorragend f\u00fcr normale Konsolenausgaben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann kommt vielleicht ein Rahmen dazu:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Write-Host \"\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\"\nWrite-Host \"\u2502     PROCESSING     \u2502\"\nWrite-Host \"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann ein Spinner:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$frames = @('\u280b','\u2819','\u2839','\u2838','\u283c')\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann ANSI-Farben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann <code>Clear-Host<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann Cursorsteuerung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Und pl\u00f6tzlich enth\u00e4lt der Code \u00fcberall Dinge wie:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Write-Host ...\nWrite-Raw ...\nSetCursorPosition(...)\nClear-Host\nStart-Sleep ...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das Ergebnis kann optisch beeindruckend sein.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Architektonisch ist es trotzdem h\u00e4ufig problematisch.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Denn jetzt entscheidet die Gesch\u00e4ftslogik gleichzeitig:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Was passiert?\n+\nWas soll angezeigt werden?\n+\nWo soll es angezeigt werden?\n+\nWie soll es aussehen?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Verantwortlichkeiten sollten getrennt werden.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Der wichtigste Perspektivwechsel<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine TUI sollte nicht prim\u00e4r aus Ausgabebefehlen bestehen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie sollte einen <strong>Anwendungszustand darstellen<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Statt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Write-Host \"Processed: $processed\"\nWrite-Host \"Remaining: $remaining\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">sollte man gedanklich zuerst diesen Zustand haben:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$state = &#91;pscustomobject]@{\n    Phase       = 'Processing'\n    CurrentItem = 'Archive \/ 2025'\n    Processed   = 116\n    Total       = 184\n    Failed      = 0\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die TUI bekommt diesen Zustand und entscheidet, wie er aussieht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Konzeptionell:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Application\n    \u2502\n    \u2502 State\n    \u25bc\nRenderer\n    \u2502\n    \u25bc\nTerminal\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die Gesch\u00e4ftslogik kennt damit nicht mehr:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Cyan\nGreen\nCursor Position\nBox Drawing\nSpinner Frame\nTerminal Width\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Sie kennt nur ihren eigenen Zustand.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist der zentrale Architekturgedanke hinter einer stabilen TUI.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Eine TUI ist ein Renderer f\u00fcr eine Zustandsmaschine<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Viele Werkzeuge haben ohnehin nat\u00fcrliche Zust\u00e4nde.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein API-basiertes Verarbeitungstool k\u00f6nnte beispielsweise folgende Phasen besitzen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>INITIALIZING\n      \u2193\nCONNECTING\n      \u2193\nDISCOVERING\n      \u2193\nAUDITING\n      \u2193\nCONFIRMING\n      \u2193\nPROCESSING\n      \u2193\nVERIFYING\n      \u2193\nCOMPLETED\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Zus\u00e4tzlich k\u00f6nnen Zust\u00e4nde wie diese auftreten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>RETRYING\nCANCELLED\nFAILED\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Zust\u00e4nde existieren unabh\u00e4ngig davon, ob wir sie modellieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn wir sie nicht explizit modellieren, verstecken sie sich irgendwann in Variablen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$isConnected = $true\n$isProcessing = $false\n$isVerifying = $true\n$hasFailed = $false\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Oder noch schlimmer: Der Zustand ergibt sich nur daraus, welche UI-Funktion gerade aufgerufen wird.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Besser ist:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Application State\n        \u2193\nScreen Selection\n        \u2193\nRendering\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>switch ($State.Phase) {\n    'Connecting' {\n        Show-ConnectingScreen $State\n    }\n\n    'Processing' {\n        Show-ProcessingScreen $State\n    }\n\n    'Verifying' {\n        Show-VerificationScreen $State\n    }\n\n    'Completed' {\n        Show-CompletedScreen $State\n    }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Jetzt ist klar:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Screen ist eine <strong>Darstellung eines Zustands<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht der Zustand selbst.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Screens statt verstreuter Ausgaben<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine hilfreiche Abstraktion ist das Konzept eines Screens.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Boot Screen\nConnection Screen\nAudit Screen\nConfirmation Screen\nProcessing Screen\nVerification Screen\nCompletion Screen\nError Screen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Jeder Screen beantwortet eine bestimmte Frage.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Connection Screen:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Mit welchem System verbindet sich die Anwendung gerade?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Der Audit Screen:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Was wurde gefunden?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Der Confirmation Screen:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Was wird ver\u00e4ndert, wenn ich fortfahre?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Der Processing Screen:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Wie weit ist die Operation?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Der Completion Screen:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Was ist tats\u00e4chlich passiert?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Das klingt nach UX-Theorie, hat aber einen sehr praktischen Effekt:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Man vermeidet Informationschaos.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Eine gute TUI braucht Informationshierarchie<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Terminalplatz ist begrenzt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb sollte nicht alles gleich wichtig aussehen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Processing Screen k\u00f6nnte beispielsweise so strukturiert sein:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>FILE PROCESSOR\nPROCESSING\n\nCurrent\nArchive \/ Customers \/ 2025\n\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591  68%\n\n125 processed\n 59 remaining\n  0 failed\n\nElapsed 00:01:42\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die Hierarchie ist offensichtlich:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Zustand<\/li>\n\n\n\n<li>aktuelles Objekt<\/li>\n\n\n\n<li>Fortschritt<\/li>\n\n\n\n<li>Kennzahlen<\/li>\n\n\n\n<li>sekund\u00e4re Information<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Schlechter w\u00e4re:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Status: Processing\nAccount: example@example.com\nMode: Write\nFolder: Archive \/ Customers \/ 2025\nProcessed: 125\nTotal: 184\nRemaining: 59\nFailed: 0\nElapsed: 00:01:42\nAPI: Microsoft Graph\nBatch size: 20\nRetries: 1\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die Informationen k\u00f6nnen alle korrekt sein.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber die Oberfl\u00e4che zwingt den Benutzer, selbst herauszufinden, was gerade wichtig ist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine gute TUI reduziert diese kognitive Arbeit.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Layout sollte aus Primitives entstehen<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein h\u00e4ufiger Fehler ist, Screens direkt mit Leerzeichen zusammenzubauen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Write-Host \"Processed:       $processed\"\nWrite-Host \"Remaining:       $remaining\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das funktioniert so lange, bis:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Zahlen gr\u00f6\u00dfer werden,<\/li>\n\n\n\n<li>Beschriftungen l\u00e4nger werden,<\/li>\n\n\n\n<li>Unicode verwendet wird,<\/li>\n\n\n\n<li>das Terminal schmaler wird,<\/li>\n\n\n\n<li>unterschiedliche Inhalte dargestellt werden.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Stattdessen lohnt sich eine kleine Menge wiederverwendbarer Layout-Primitives.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Header\nPanel\nKeyValue\nProgressBar\nStatusChip\nTable\nFooter\nRule\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Screen wird daraus zusammengesetzt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Screen\n\u251c\u2500\u2500 Header\n\u251c\u2500\u2500 StatusChip\n\u251c\u2500\u2500 Panel\n\u2502   \u251c\u2500\u2500 Current Item\n\u2502   \u2514\u2500\u2500 ProgressBar\n\u251c\u2500\u2500 KeyValue Statistics\n\u2514\u2500\u2500 Footer\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die einzelnen Komponenten wissen, wie breit sie sein d\u00fcrfen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Screen entscheidet nur, welche Komponenten vorkommen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Terminalbreite ist Teil des Layouts<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine feste Breite funktioniert auf dem eigenen Rechner erstaunlich lange.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bis jemand das Terminal verkleinert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann passiert:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Current folder: Archive \/ Customers \/ Europe \/ Switzerland \/ ...\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Text l\u00e4uft aus dem Rahmen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb sollte die Terminalbreite einmal zentral bestimmt werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$width = &#91;Console]::WindowWidth\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Darauf basierend kann eine sinnvolle Content-Breite berechnet werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Terminal: 120 Zeichen\nUI:        90 Zeichen\n\nTerminal: 80 Zeichen\nUI:        76 Zeichen\n\nTerminal: 55 Zeichen\nUI:        51 Zeichen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wichtig ist nicht die konkrete Formel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wichtig ist:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Komponenten sollten ihre verf\u00fcgbare Breite kennen.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Dann k\u00f6nnen sie Inhalte:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>abschneiden,<\/li>\n\n\n\n<li>umbrechen,<\/li>\n\n\n\n<li>verk\u00fcrzen,<\/li>\n\n\n\n<li>anders anordnen.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Sichtbare L\u00e4nge ist nicht immer String-L\u00e4nge<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Sobald ANSI-Sequenzen verwendet werden, entsteht ein weiteres Problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dieser String:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ESC&#91;32mSUCCESS ESC&#91;0m\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">enth\u00e4lt mehr Zeichen als das Wort:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SUCCESS\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Auf dem Bildschirm sind aber nur sieben Zeichen sichtbar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein simples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$text.Length\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">liefert deshalb f\u00fcr Layoutberechnungen die falsche Information.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine TUI braucht gegebenenfalls zwei Vorstellungen von L\u00e4nge:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>String Length\nVisible Length\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">ANSI-Sequenzen m\u00fcssen bei der sichtbaren L\u00e4nge ignoriert werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sonst entstehen typische Fehler:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 SUCCESS                    \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die Berechnung glaubt, der Text sei l\u00e4nger als er tats\u00e4chlich dargestellt wird.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Unicode ist eine Pipeline<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Moderne Terminal-UIs profitieren stark von Unicode.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u2713\n\u25c6\n\u25c7\n\u2500\n\u2502\n\u250c\n\u2510\n\u2514\n\u2518\n\u280b\n\u2819\n\u2839\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn solche Zeichen nicht korrekt dargestellt werden, wird schnell der Font verd\u00e4chtigt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das kann stimmen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Es gibt aber mehrere m\u00f6gliche Ebenen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Quelldatei\n    \u2193\nPowerShell\n    \u2193\nEncoding\n    \u2193\nConsole API\n    \u2193\nTerminal\n    \u2193\nFont\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb sollte man Unicode-Probleme isolieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein einfacher Test:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Write-Host \"\u280b \u2819 \u2839 \u2838 \u283c \u25c7 \u25c6 \u2713\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn dieser funktioniert, kann das Terminal die Zeichen grunds\u00e4tzlich darstellen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann liegt das Problem wahrscheinlich nicht beim Font.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Art von Minimaltest ist oft wertvoller als sofort den Renderer umzubauen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Codepoints k\u00f6nnen robuster sein<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Gerade bei Skripten, die durch unterschiedliche Editoren oder Encoding-Pipelines laufen, kann es sinnvoll sein, kritische UI-Zeichen zur Laufzeit zu erzeugen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Statt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$spinner = @('\u280b','\u2819','\u2839')\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$spinner = @(\n    &#91;char]0x280B\n    &#91;char]0x2819\n    &#91;char]0x2839\n)\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das l\u00f6st nicht jedes Unicode-Problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Es verhindert aber, dass bestimmte Glyphen bereits beim Speichern oder Kopieren des Skripts besch\u00e4digt werden.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Der schwierigste Teil: Redraw<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Sobald sich ein Screen ver\u00e4ndert, muss entschieden werden, wie neu gerendert wird.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die einfachste Variante:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Clear Screen\nDraw Screen\nWait\nClear Screen\nDraw Screen\nWait\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Sie ist leicht zu implementieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Und h\u00e4ufig h\u00e4sslich.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Terminal flackert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Besonders bei kurzen Refresh-Intervallen wirkt die Anwendung nerv\u00f6s.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Warum permanentes <code>Clear-Host<\/code> flackert<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein kompletter Clear erzeugt vereinfacht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>alter Frame\n   \u2193\nleerer Bildschirm\n   \u2193\nneuer Frame\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das menschliche Auge kann den leeren Zwischenzustand wahrnehmen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bei mehreren Updates pro Sekunde entsteht Flackern.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die intuitive Reaktion lautet dann:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Nicht mehr l\u00f6schen.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Damit entsteht das n\u00e4chste Problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn der neue Frame k\u00fcrzer ist als der alte:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>OLD:\nProcessing folder with a very long name\n\nNEW:\nDone\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">kann zur\u00fcckbleiben:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Doneessing folder with a very long name\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Jetzt haben wir Ghosting statt Flicker.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Zwei verschiedene Arten von Redraw<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Die L\u00f6sung besteht darin, zwei F\u00e4lle zu unterscheiden.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Zustandswechsel<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>AUDIT\n  \u2193\nCONFIRMATION\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Hier ver\u00e4ndert sich der gesamte Informationskontext.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein kompletter Screenwechsel ist sinnvoll:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Clear\n\u2193\nDraw Confirmation Screen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Eine sehr kurze Pause kann den Wechsel sogar verst\u00e4ndlicher machen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Live-Update innerhalb desselben Zustands<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Processing 41%\n\u2193\nProcessing 42%\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Hier sollte der Bildschirm nicht komplett gel\u00f6scht werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Stattdessen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Cursor Home\n\u2193\nFrame neu zeichnen\n\u2193\n\u00fcbrig gebliebenen Rest l\u00f6schen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ergibt ein wesentlich ruhigeres Bild.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Das grundlegende Rendering-Muster<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Damit entsteht ein sehr brauchbares Modell:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Programmstart\n    \u2193\nFull Clear\n\nZustandswechsel\n    \u2193\nFull Clear\n    \u2193\nneuen Screen zeichnen\n\nLive Update\n    \u2193\nCursor Home\n    \u2193\nScreen neu zeichnen\n    \u2193\nRest unterhalb l\u00f6schen\n\nProgrammende\n    \u2193\nstatischen Final Screen anzeigen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Allein diese Trennung beseitigt einen gro\u00dfen Teil typischer TUI-Probleme.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Nicht jedes Event braucht einen Frame<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Nehmen wir an, eine API verarbeitet 1&#8217;000 Elemente.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn nach jedem Element die komplette TUI neu gezeichnet wird, entstehen 1&#8217;000 Renderings.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist meistens unn\u00f6tig.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Verarbeitung kann deutlich schneller sein als die Wahrnehmung des Benutzers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb sollte Rendering gedrosselt werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Engine Events:\n1\n2\n3\n4\n5\n6\n7\n8\n9\n...\n\nRenderer:\n      Frame\n            Frame\n                  Frame\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Eine Aktualisierung ungef\u00e4hr alle 100 bis 200 Millisekunden reicht f\u00fcr viele Terminal-UIs bereits aus.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Engine darf schneller arbeiten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die UI muss nicht jeden einzelnen Zwischenzustand darstellen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Progress ist nicht Animation<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Spinner und eine Progressbar beantworten unterschiedliche Fragen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Spinner:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u2839 Connecting...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">sagt:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Das Programm arbeitet noch.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Er sagt nicht:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">37 Prozent sind erledigt.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Eine Progressbar:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591 58%\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">sagt:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Wir kennen Gesamtmenge und Fortschritt.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>unbekannte Dauer\n\u2192 Spinner\n\nbekannte Gesamtmenge\n\u2192 Progressbar\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Beides gleichzeitig ist nur selten notwendig.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Animation braucht eine Bedeutung<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Animation sollte einen Zustand kommunizieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sinnvolle Beispiele:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Spinner\n\u2192 Arbeit l\u00e4uft\n\nProgressbar\n\u2192 messbarer Fortschritt\n\nkurzer \u00dcbergang\n\u2192 neuer Programmzustand\n\nPulse\n\u2192 tempor\u00e4re Aktivit\u00e4t\n\nCompletion Effect\n\u2192 Operation abgeschlossen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Problematisch wird Animation, wenn sie nur existiert, weil sie technisch m\u00f6glich ist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann bekommt man:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Boot Animation\n\u2193\nLoading Animation\n\u2193\nTransition Animation\n\u2193\nSpinner\n\u2193\nProgress Animation\n\u2193\nCompletion Animation\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das Tool beginnt, sich selbst zu inszenieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bei einem Werkzeug, das regelm\u00e4\u00dfig benutzt wird, wird das schnell l\u00e4stig.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine gute TUI f\u00fchlt sich schnell an.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht unbedingt, weil jede Operation schnell ist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sondern weil sie unn\u00f6tige Wartezeit nicht k\u00fcnstlich verl\u00e4ngert.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><code>-NoAnimation<\/code> ist kein unn\u00f6tiges Feature<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Sobald Animation vorhanden ist, sollte sie deaktivierbar sein.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\\tool.ps1 -NoAnimation\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist aus mehreren Gr\u00fcnden sinnvoll:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Remote-Terminals<\/li>\n\n\n\n<li>Screenreader<\/li>\n\n\n\n<li>CI<\/li>\n\n\n\n<li>Logging<\/li>\n\n\n\n<li>langsamere Terminalumgebungen<\/li>\n\n\n\n<li>pers\u00f6nliche Pr\u00e4ferenz<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Auch das ist ein Hinweis darauf, dass Animation eine Darstellungseigenschaft sein sollte.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht Teil der Gesch\u00e4ftslogik.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Sound geh\u00f6rt ebenfalls zur Darstellung<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Das Gleiche gilt f\u00fcr akustisches Feedback.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Abschluss-Beep kann charmant sein.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Er sollte aber nicht tief in der Verarbeitung stecken:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Invoke-ApiOperation\n&#91;Console]::Beep()\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Besser:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Operation Completed\n        \u2193\nUI entscheidet \u00fcber\n        \u2193\nvisual completion\nsound\nnothing\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Damit kann beispielsweise ein <code>-NoSound<\/code>-Modus problemlos existieren.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Die TUI darf die Engine nicht besitzen<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist wahrscheinlich die wichtigste technische Grenze.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Schlecht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>function Show-ProcessingScreen {\n    $response = Invoke-MgGraphRequest ...\n    ...\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Jetzt f\u00fchrt der Screen die Gesch\u00e4ftsoperation aus.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das erzeugt eine starke Kopplung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Besser:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$result = Invoke-Processing\n\nShow-ProcessingScreen -State $result\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Noch besser f\u00fcr Live-Updates ist ein Event- oder Callback-Modell:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Engine\n  \u2502\n  \u251c\u2500\u2500 ProgressChanged\n  \u251c\u2500\u2500 ItemCompleted\n  \u251c\u2500\u2500 RetryStarted\n  \u2514\u2500\u2500 ProcessingCompleted\n        \u2502\n        \u25bc\n       UI\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die Engine meldet, was passiert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die TUI entscheidet, was davon sichtbar wird.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Dasselbe Programm sollte ohne TUI funktionieren<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist ein hervorragender Architekturtest.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Angenommen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\\tool.ps1\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">zeigt eine interaktive TUI.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dann sollte beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\\tool.ps1 -Quiet\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">dieselbe Operation ausf\u00fchren k\u00f6nnen und stattdessen etwas wie dieses liefern:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"status\": \"completed\",\n  \"processed\": 184,\n  \"failed\": 0,\n  \"remaining\": 0\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn das problemlos m\u00f6glich ist, sind Engine und UI wahrscheinlich ausreichend getrennt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn <code>-Quiet<\/code> bedeutet, dass an 47 Stellen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if (-not $Quiet) {\n    Write-Host ...\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">eingebaut werden muss, ist das ein Warnsignal.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Human Interface und Machine Interface<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein gutes Kommandozeilenwerkzeug kann zwei Zielgruppen haben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Den Menschen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 CASE CLOSED                                \u2502\n\u2502                                            \u2502\n\u2502 184 items processed                        \u2502\n\u2502 0 failures                                 \u2502\n\u2502 0 remaining                                \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Und andere Software:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"processed\": 184,\n  \"failed\": 0,\n  \"remaining\": 0\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Beide Oberfl\u00e4chen verwenden dieselbe Engine.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Damit entsteht:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>               Core\n                 \u2502\n        \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n        \u2502                 \u2502\n       TUI               JSON\n        \u2502                 \u2502\n      Human            Automation\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist ein \u00fcberraschend guter Ma\u00dfstab f\u00fcr die Qualit\u00e4t einer TUI-Architektur.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\"><code>stdout<\/code> ist nicht einfach eine Leinwand<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Gerade bei CLI-Tools sollte au\u00dferdem bewusst entschieden werden, wohin welche Ausgabe geh\u00f6rt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Maschinenlesbare Ausgabe auf <code>stdout<\/code> verliert ihren Wert, wenn dazwischen pl\u00f6tzlich steht:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Connecting...\n\u2839 Processing...\nSuccess!\n{\"processed\":184}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ein JSON-Modus sollte wirklich JSON liefern.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keine Spinner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keine Banner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keine ANSI-Sequenzen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keine Statusmeldungen davor.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist besonders relevant f\u00fcr Pipelines:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$result = .\\tool.ps1 -Quiet | ConvertFrom-Json\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Eine saubere TUI-Architektur sch\u00fctzt damit gleichzeitig die Automatisierbarkeit des Werkzeugs.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Farbe sollte Bedeutung haben<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine weitere Versuchung ist, m\u00f6glichst viele Farben zu verwenden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Besser ist eine kleine semantische Palette.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zum Beispiel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>neutral\n\u2192 normale Information\n\naccent\n\u2192 aktiver Zustand\n\nsuccess\n\u2192 erfolgreich\n\nwarning\n\u2192 Aufmerksamkeit\n\nerror\n\u2192 Fehler\n\nmuted\n\u2192 sekund\u00e4re Information\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dann bedeutet Gr\u00fcn immer ungef\u00e4hr dasselbe.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rot ebenfalls.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn jede \u00dcberschrift eine andere Farbe bekommt, wird Farbe zur Dekoration statt zur Information.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Farbe darf nicht die einzige Information sein<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Fehler sollte nicht nur rot sein.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Also nicht nur:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Connection failed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">in roter Schrift.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sondern beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;ERROR] Connection failed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Damit bleibt die Bedeutung erhalten:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>ohne ANSI<\/li>\n\n\n\n<li>bei Farbsehschw\u00e4che<\/li>\n\n\n\n<li>in Logs<\/li>\n\n\n\n<li>bei Copy &amp; Paste<\/li>\n\n\n\n<li>in Terminals mit eingeschr\u00e4nkter Farbdarstellung<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Das Gleiche gilt f\u00fcr Erfolg:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;OK] Connected\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">statt nur Gr\u00fcn.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">ANSI ist m\u00e4chtig \u2013 und eine Abh\u00e4ngigkeit<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">ANSI-Escape-Sequenzen erm\u00f6glichen unter anderem:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Farben<\/li>\n\n\n\n<li>Cursorbewegung<\/li>\n\n\n\n<li>Screen Clearing<\/li>\n\n\n\n<li>Line Clearing<\/li>\n\n\n\n<li>Cursor Visibility<\/li>\n\n\n\n<li>Terminal Title<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Damit lassen sich erstaunlich komplexe Oberfl\u00e4chen bauen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber ANSI-Unterst\u00fctzung sollte nicht blind vorausgesetzt werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein robustes Tool sollte sich fragen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Ist die Ausgabe interaktiv?\nUnterst\u00fctzt das Terminal ANSI?\nIst Output redirected?\nIst Quiet Mode aktiv?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Daraus kann ein Rendering-Modus entstehen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Interactive\nPlain\nMachine\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist h\u00e4ufig sinnvoller als ein einzelnes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>UI = true\/false\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Der Cursor geh\u00f6rt zum Ressourcenmanagement<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine kleine, aber wichtige Lektion:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn eine TUI den Cursor versteckt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;Console]::CursorVisible = $false\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">muss sie ihn garantiert wiederherstellen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Auch bei Fehlern.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Also:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>try {\n    ...\n}\nfinally {\n    &#91;Console]::CursorVisible = $true\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dasselbe gilt f\u00fcr:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Terminaltitel<\/li>\n\n\n\n<li>Farben<\/li>\n\n\n\n<li>Buffer-Zustand<\/li>\n\n\n\n<li>alternative Screens<\/li>\n\n\n\n<li>andere ver\u00e4nderte Terminaleigenschaften<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Eine TUI sollte das Terminal so sauber verlassen, wie sie es vorgefunden hat.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Fehler-Screens sind Teil der TUI<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Fehlerbehandlung ist nicht nur:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>catch {\n    Write-Host $_ -ForegroundColor Red\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Fehler ist ebenfalls ein Anwendungszustand.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Also kann er einen eigenen Screen besitzen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PROCESSING FAILED\n\nOperation\nMark items as processed\n\nReason\nHTTP 403 Forbidden\n\nCompleted\n142 \/ 184\n\nCheckpoint\nAvailable\n\nThe operation can be resumed.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist wesentlich hilfreicher als ein roter Stacktrace.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr Entwickler kann der Stacktrace trotzdem verf\u00fcgbar bleiben:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-Verbose\n-Debug\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">oder in einem Log.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Benutzeroberfl\u00e4che sollte aber zuerst erkl\u00e4ren, <strong>was der Fehler f\u00fcr den aktuellen Vorgang bedeutet<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Completion ist ebenfalls ein Zustand<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Dasselbe gilt f\u00fcr Erfolg.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Tool sollte nicht einfach nach langer dynamischer Ausgabe mit:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Done.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">enden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der finale Screen ist die Quittung des Vorgangs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Er sollte beantworten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Was wurde getan?\nWie viel?\nGab es Fehler?\nWurde das Ergebnis verifiziert?\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Beispielsweise:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CASE CLOSED\n\n184 items processed\n0 failed\n0 remaining\n\nPost-run verification successful.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Gerade bei mutierenden Operationen ist das wertvoll.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Der Renderer sollte keine Wahrheit erfinden<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine besonders wichtige Regel:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Die TUI darf nur Zust\u00e4nde darstellen, die die Engine tats\u00e4chlich kennt.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn die API 184 erfolgreiche Requests meldet, darf die TUI sagen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>184 operations completed\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Sie darf daraus aber nicht automatisch machen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Everything verified successfully\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Daf\u00fcr braucht es eine tats\u00e4chliche Verifikation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">UI-Texte sind Teil der Semantik des Systems.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein gr\u00fcner:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SUCCESS\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">ist technisch gesehen eine Behauptung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie sollte stimmen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Eine Progressbar braucht ebenfalls ehrliche Daten<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Dasselbe gilt f\u00fcr:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 100%\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">100 Prozent k\u00f6nnen unterschiedliche Dinge bedeuten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>alle Requests gesendet\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">oder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>alle Requests beantwortet\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">oder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>alle Elemente erfolgreich verarbeitet\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">oder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gew\u00fcnschter Endzustand verifiziert\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Dinge sind nicht identisch.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die UI sollte klar definieren, was ihr Fortschritt misst.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">TUI-Entwicklung ist Software Engineering<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">An diesem Punkt wird deutlich, warum eine gute TUI \u00fcberraschend viel Architektur ben\u00f6tigt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nicht weil Terminalausgabe kompliziert sein muss.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sondern weil eine dynamische Benutzeroberfl\u00e4che zwangsl\u00e4ufig Fragen erzeugt zu:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>State\nRendering\nLifecycle\nLayout\nEvents\nError Handling\nAccessibility\nOutput Contracts\nResource Cleanup\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das sind klassische Software-Engineering-Themen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nur eben im Terminal.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Eine sinnvolle Architektur<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr ein mittelgro\u00dfes PowerShell-Werkzeug k\u00f6nnte die Struktur beispielsweise so aussehen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Tool.ps1\n\nTool.psm1\n\nPublic\/\n    Invoke-Tool.ps1\n\nPrivate\/\n    Api.ps1\n    State.ps1\n\n    UI.Terminal.ps1\n    UI.Screens.ps1\n\nTests\/\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>Api.ps1<\/code> wei\u00df nichts \u00fcber die Darstellung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>State.ps1<\/code> beschreibt den Zustand.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>UI.Terminal.ps1<\/code> enth\u00e4lt technische Rendering-Primitives:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Clear Screen\nCursor Home\nVisible Length\nPanel\nProgressbar\nColor\nTerminal Width\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>UI.Screens.ps1<\/code> baut daraus semantische Screens:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Audit\nConfirmation\nProcessing\nVerification\nCompletion\nError\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Und <code>Invoke-Tool.ps1<\/code> orchestriert den Lebenszyklus.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Damit entsteht ungef\u00e4hr:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>              Orchestrator\n                    \u2502\n          \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n          \u2502                   \u2502\n        Engine               State\n          \u2502                   \u2502\n          \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                    \u2502\n                 Renderer\n                    \u2502\n               \u250c\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2510\n               \u2502         \u2502\n              TUI       JSON\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist f\u00fcr viele Tools bereits vollkommen ausreichend.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Man braucht daf\u00fcr kein gro\u00dfes UI-Framework.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Was ich heute anders bauen w\u00fcrde<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Bei einer neuen PowerShell-TUI w\u00fcrde ich nicht mit Farben beginnen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Auch nicht mit dem Banner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Und definitiv nicht mit dem Spinner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Reihenfolge w\u00e4re:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1. Operation\n2. State\n3. Zustands\u00fcberg\u00e4nge\n4. Output Contract\n5. Screens\n6. Layout-Primitives\n7. Rendering\n8. Progress\n9. Animation\n10. Farben und Effekte\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das wirkt zun\u00e4chst weniger befriedigend.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die ersten Versionen sehen langweilig aus.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber sobald die Grundlage steht, lassen sich visuelle Ideen wesentlich leichter hinzuf\u00fcgen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vor allem, ohne dabei die Gesch\u00e4ftslogik zu besch\u00e4digen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Der Spinner kommt zuletzt<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Das ist vielleicht die einfachste Zusammenfassung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bei einer TUI ist der Spinner das Sichtbarste.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deshalb ist er verf\u00fchrerisch.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Architektonisch ist er aber fast das Unwichtigste.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Unter ihm liegen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>State\nLifecycle\nEvents\nRendering\nLayout\nTerminal Capabilities\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn diese Dinge sauber sind, ist ein Spinner trivial.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn sie nicht sauber sind, wird selbst ein Spinner pl\u00f6tzlich zum Problem.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Fazit<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Eine moderne Terminaloberfl\u00e4che kann erstaunlich viel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sie kann interaktiv, informativ und visuell hochwertig sein, ohne das Terminal zu verlassen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber eine gute TUI entsteht nicht dadurch, dass normale Konsolenausgabe dekoriert wird.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der entscheidende Schritt ist ein anderer:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Nicht mehr dar\u00fcber nachdenken, welche Zeile als N\u00e4chstes ausgegeben werden soll, sondern welchen Zustand die Anwendung gerade hat und wie dieser Zustand dargestellt werden soll.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Aus:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Print\nPrint\nClear\nPrint\nSleep\nPrint\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">wird:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Application\n     \u2193\n   State\n     \u2193\n Renderer\n     \u2193\n  Screen\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Danach bekommen auch Cursorbewegungen, ANSI, Unicode, Progressbars und Animationen einen klaren Platz.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die wichtigste Erkenntnis ist deshalb:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Eine gute TUI ist ein Renderer f\u00fcr eine Zustandsmaschine \u2013 keine Sammlung farbiger <code>Write-Host<\/code>-Statements.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Und vielleicht ist genau das der Punkt, an dem aus einem h\u00fcbschen Terminalskript eine echte Benutzeroberfl\u00e4che wird.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Terminalanwendungen erleben seit einigen Jahren eine kleine Renaissance. Viele moderne Entwicklerwerkzeuge laufen wieder dort, wo Softwareentwicklung ohnehin h\u00e4ufig stattfindet: im Terminal. Paketmanager, Git-Werkzeuge, Cloud-CLIs und zunehmend auch KI-Assistenten zeigen, dass eine textbasierte Oberfl\u00e4che weder altmodisch noch zwangsl\u00e4ufig primitiv sein muss. Moderne Terminalanwendungen verwenden Farben, Panels, Tabellen, Spinner, Fortschrittsanzeigen und dynamische Statusinformationen. Manche f\u00fchlen sich eher [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":335,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[60,27,8,25,59],"tags":[],"class_list":["post-324","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-automation","category-programming-principles","category-software-architecture","category-software-engineering","category-system-engineering"],"_links":{"self":[{"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/posts\/324","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=324"}],"version-history":[{"count":1,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/posts\/324\/revisions"}],"predecessor-version":[{"id":325,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/posts\/324\/revisions\/325"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=\/wp\/v2\/media\/335"}],"wp:attachment":[{"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=324"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=324"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fabricioruch.ch\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=324"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}