Komponente aufs Formular, Active := True,
fertig. Die Anwendung selbst wird nicht geändert – kein Umbau,
keine zweite Oberfläche, kein zweiter Datenpfad.
Gewachsene Delphi-Anwendungen laufen zuverlässig, aber sie laufen auf genau einem Rechner, vor dem jemand stehen muss.
Irgendwann kommt die Frage: „Kann ich da auch vom Tablet draufschauen?“ Die üblichen Antworten sind teuer. Fernwartung überträgt Pixel und fühlt sich auch so an. Eine Neuentwicklung als Webanwendung bedeutet, zwanzig Jahre Fachlogik ein zweites Mal zu schreiben – und ab dann zwei Oberflächen zu pflegen, die auseinanderlaufen. Ein Umbau auf ein Web-Framework verlangt genau das, was es bei einer Anwendung aus dem letzten Jahrtausend nicht gibt: eine saubere Trennung von Oberfläche und Logik.
WebItTNG nimmt einen anderen Weg. Es liest das laufende Formular – die Controls, die ohnehin da sind, mit ihren Werten von gerade eben – und baut daraus im Browser echte HTML-Elemente. Was der Bediener dort tut, kommt als Wert oder Klick im Formular an, auf demselben Weg, den die VCL selbst benutzt. Die Anwendung merkt nicht, dass sie ferngesteuert wird.
Eine Komponente, eine Zeile. Alles Weitere ist optional.
procedure TfrmMain.FormCreate(Sender: TObject); begin Wit.Port := 8080; Wit.Active := True; end;
Der Browser zeigt danach dasselbe Formular, das auf dem Bildschirm steht. Wechselt die Anwendung das Formular oder öffnet sie einen modalen Dialog, wechselt der Browser mit.
Der Browser fragt nicht nach. Er hält eine Verbindung offen und bekommt nur, was sich geändert hat.
Alle 300 Millisekunden baut der Server eine Momentaufnahme des Formulars und vergleicht sie mit der vorigen. Was gleich geblieben ist, wird nicht gesendet – eine ruhende Maske kostet keine Bandbreite. Was sich geändert hat, geht als kleines JSON-Paket über einen Server-Sent-Events-Kanal an den Browser, der daraufhin genau die betroffenen Elemente ändert. Kein Neuaufbau, kein Flackern, keine verlorene Cursorposition.
Ein selbstgezeichnetes Control oder eine Fremdkomponente hat im Browser keine Entsprechung. Solche Controls reisen als Bild – aber nicht das Bild selbst, sondern nur eine Prüfsumme seiner Pixel. Das Bild holt der Browser unter einer URL, die diese Prüfsumme enthält. Ändert sich das Bild, ändert sich die URL; ändert es sich nicht, kostet ein erneuter Abruf gar nichts, weil der Browser die URL hart zwischenspeichern darf.
Übertragen wird nur, was von der Umgebung abweicht. Das Formular
trägt die Grundschrift, alles darunter erbt sie im Browser über
ganz normales CSS – in der Demo sind das zwei Schriftobjekte auf
neunzehn Knoten. Dasselbe gilt für die Hintergrundfarbe: kein Wert
heißt „der Elternknoten scheint durch“, genau wie
ParentColor in der VCL.
Ein Barcodescanner tippt seinen Code als schnelle Tastendrücke und
schließt mit einem Wagenrücklauf ab. Damit das funktioniert,
reicht es nicht, den Text zu übertragen: die Anwendung wartet auf
genau dieses Zeichen – in OnKeyPress, oder weil ein
Default-Button darauf reagiert. WebItTNG bildet die Reihenfolge nach, in
der die VCL eine Taste zustellt: erst die Tastenkürzel des Formulars,
dann der Dialogschlüssel, dann das fokussierte Control. Der
Default-Button klickt, als hätte jemand davorgesessen.
Dieses Paket wurde nicht von Hand geschrieben und anschließend von einem Assistenten aufgeräumt. Es ist vollständig im Gespräch entstanden – Claude Code als Agent, Kai als dessen Zugang zu RAD Studio. Wichtiger als diese Überschrift ist allerdings die Arbeitsweise.
RAD Studio 13 bleibt die ganze Zeit offen. Kai ist die Brücke hinein: eine Datei so lesen, wie sie gerade im Editorpuffer steht und nicht wie sie auf der Platte liegt, Änderungen in diesen Puffer schreiben, compilieren, das Projekt verwalten, den Debugger steuern. Claude Code läuft im Projektverzeichnis und erledigt den Rest – suchen, schreiben, die Testprogramme laufen lassen, die Demo starten, eine Seite vom laufenden Server abholen.
Die Aufgabenteilung, die sich dabei herausgebildet hat, ist erwähnenswert, weil sie nicht die naheliegende ist: Die IDE-Brücke ist für alles richtig, was Dateien betrifft, die im Editor offen sind – dort würde ein Schreibvorgang an der IDE vorbei ungespeicherte Arbeit verlieren oder still wieder überschrieben werden. Alles andere – eine Konsolen-Testsuite laufen lassen, Bytezahlen vergleichen, einen Browser steuern – geht außerhalb der IDE schneller. Diese Grenze falsch zu ziehen kostet echte Zeit: eine Änderung, die auf die Platte geschrieben wurde, während dieselbe Unit im Designer offen stand, war schlicht wieder überschrieben – und sah eine Weile lang wie ein Compilerproblem aus.
Fast nie als Spezifikation. Meist als zwei Sätze Fließtext, und sehr häufig als Beobachtung an der laufenden Anwendung:
„Hintergrundfarbe wird immer noch ignoriert. Eingestellt ist
Color = 1, also fast schwarz, angezeigt wird weiß. Dann:
Der Testdialog hat ein Panel in gelb, dadrauf ist ein Label mit schwarzem
Hintergrund. Dadurch bekommt das Label einen breiten gelben Rand. Im
Browser ist der Rand weiß.“
Das ist eine gute Fehlermeldung und eine schwierige: zwei Symptome, eines davon falsch zugeordnet. Daraus eine Änderung zu machen ist die eigentliche Arbeit, und dort steckt der größte Teil des Aufwands in diesem Projekt. Für Funktionswünsche gilt dasselbe: „Kann man die Verbindungsanzeige ausblenden, und kann der Browser sich selbst wieder verbinden?“ kam mit Vorschlägen für die Eigenschaftsnamen – und ein Teil der Antwort bestand darin, einen besseren Zuschnitt vorzuschlagen: ein Intervall, bei dem null „gar nicht“ heißt, statt eines Schalters neben einem Intervall, die sich widersprechen können.
Die Meldung oben ist ein durchgespieltes Beispiel der Methode – und dafür, warum Raten gescheitert wäre. Es brauchte fünf Schritte:
/state als JSON offen, und genau dazu ist das da: eine
Meldung durch einen Beweis zu ersetzen. Der Mitschnitt zeigte ein Panel,
das überhaupt keine Farbe meldete, und ein graues Härchen als
Rahmen, wo die Anwendung einen breiten gelben Streifen zeigt.BorderColor
gefüllt, BorderWidth Pixel breit, unabhängig von
beiden Rahmenstilen. Der Adapter behandelte die drei als
entweder/oder – und meldete deshalb für einen 30 Pixel breiten
gelben Streifen eine ein Pixel breite graue Linie.tblr:#9f9f9f aus – buchstäblich das
graue Härchen, auf das der Anwender geschaut hatte. Ein Test, der
nicht umfällt, wenn der Fehler zurückkommt, ist Dekoration.Das zweite Symptom entpuppte sich als eigener Fehler in derselben Meldung: ein Panel, das das ganze Formular abdeckt, durfte für dessen Farbe sprechen – richtig, solange die Formularfarbe die Vorgabe des Designers ist, falsch, sobald jemand sie absichtlich gesetzt hat. Beides wurde behoben, beides festgenagelt, und die Darstellung im Browser gegen eine Seite geprüft, die aus dem ausgelieferten Stylesheet gebaut wurde.
Breakpoints kamen sparsam und mit Absicht zum Einsatz. Eine lange Live-Debug-Sitzung gegen eine GUI-Anwendung, die nebenher HTTP bedient, ist fragil, und Schrittbetrieb beantwortet eine Frage langsam. Drei Techniken haben das fast vollständig ersetzt:
/state
liefert den ganzen Baum als JSON. Zwei Probeprogramme erzeugen echte
Controls und geben pro Zeile ein Urteil aus. Eine eigene Messung
beantwortet „zeichnet dieses Formular auch ohne angemeldeten
Benutzer?“ mit Pixeln statt mit einer Meinung./state-Mitschnitt durch den echten Browser-Client in einem
Teststand geschickt und die entstehenden Stile ausgegeben. Das trennt
„der Server irrt“ von „der Client irrt“ in einem
Lauf – ohne Debugger und ohne Zugriff auf den Rechner, auf dem es
passiert ist.Tests waren keine Phase am Ende. Jede Änderung ging zusammen mit ihrer Prüfung hinein, und danach lief der gesamte Satz – nicht der betroffene Teil, der gesamte Satz. Das dauert unter einer Minute, und genau das macht die Gewohnheit durchhaltbar.
Der Browser-Client verdient einen eigenen Hinweis. Er besteht aus erzeugten Pascal-Zeichenketten – also genau der Art Code, die sich dem Testen sonst entzieht. Deshalb gibt die Testsuite die Seite aus, die tatsächlich ausgeliefert wird, extrahiert das Skript und fährt es gegen einen nachgebauten DOM. Und weil die interessante Hälfte einer Einstellung diejenige ist, die etwas abschaltet, gibt sie eine zweite Seite mit allem abgeschaltet aus. Ein Client, der die Schalter schlicht ignoriert, bestünde sonst jede Prüfung.
Jede neue Zusicherung wurde gegengetestet: Korrektur zurückdrehen, das Umfallen bestätigen, wiederherstellen. Diese Gewohnheit hat Zusicherungen entlarvt, die bedeutsam aussahen und nichts prüften – und sie ist der Grund, warum die Zahlen oben überhaupt etwas wert sind.
Zwei Dokumente werden im selben Schritt wie der Code aktuell gehalten: ein README, das erklärt, wie die Sache funktioniert und warum jede Entscheidung so ausgefallen ist, und ein Übergabedokument für die nächste Sitzung – nicht verhandelbare Konventionen und eine wachsende Liste von Fehlern, die je einen Tag gekostet haben, damit sie sich nicht wiederholen.
Die Kommentare folgen derselben Regel. Sie sagen nicht, was die Zeile tut, sondern warum sie so ist – und nennen häufig den Fehlschlag, der sie verursacht hat. „Nur den Stil zu melden ergab ein graues Härchen, wo die Anwendung einen breiten Farbstreifen zeigt“ ist in einem halben Jahr mehr wert als jede Wiederholung des Codes.
Die Zwei-Compiler-Anforderung hat die Zusammenarbeit stärker geprägt als alles andere. RAD Studio 13 steht auf dem Entwicklungsrechner, Delphi 5 nicht. Jeder D5-Fehler kam deshalb als eingefügter Text zurück und musste allein daraus behoben werden.
Das funktioniert besser, als es klingt – mit einer harten Lektion: niemals einen Namen raten, den nur der andere Rechner kennt. Ein benötigter Package-Name wurde einmal geraten, was den Build auf einen Pfad schickte, der zwei weitere Fehlermeldungen und eine falsche Schlussfolgerung produzierte. Den richtigen Namen nachzusehen kostete den Anwender zehn Sekunden. „Das sehe ich von hier aus nicht, bitte nachschauen“ ist die schnellere Antwort als eine plausible Erfindung.
Konkret zu werden ist hier nützlicher als eine saubere Erzählung:
Nichts davon spricht gegen den Ansatz. Es ist der Grund, warum die Prüfungen genau die Form haben, die sie haben: jede einzelne ist ein konkreter Fehlschlag, der einmal passiert ist.
Jede Control-Klasse wird von einem Adapter beschrieben; gesucht wird der nächste registrierte Vorfahr. Eine Anwendung registriert für ihr eigenes Control acht Zeilen – und aus dem Bild wird ein richtiger Knopf. Das Paket muss dazu nicht angefasst werden.
Eins zu eins wie entworfen, waagerecht zentriert, mittig, oder untereinander fließend für schmale Geräte. Dazu unabhängig davon die Skalierung: Originalgröße, ins Fenster einpassen, auf Breite.
Dieselbe Exe startet als normales Fenster oder als Windows-Dienst. Dass ein VCL-Formular auch ohne angemeldeten Benutzer zeichnet, ist nachgemessen und nicht vermutet – die Messung steckt als Diagnosefunktion mit im Paket.
Drei Dinge, die es in der VCL so nicht gibt: eine Fläche zum Unterschreiben mit dem Finger, ein Link, der eine Datei herunterlädt, ein Bild, das sich selbst auffrischt. Die Anwendung beschreibt sie über einen Adapter.
Jeder Knoten trägt seine VCL-Klasse als CSS-Klasse mit sich. Eine Regel erreicht damit alle Buttons einer Bibliothek auf einmal. Und eine Anwendung kann eigene URLs bedienen, ohne einen zweiten Server zu starten.
Statt frmMain.pnlLinks.btnFreigabe reist auf Wunsch
ein 64-Bit-Hash des Pfades. Das ist Verschleierung, keine Sicherheit
– und die Dokumentation sagt das an genau der Stelle, an der man
es liest.
Ein Schild sagt, ob der Browser noch hängt. Bricht die Verbindung ab, versucht er sie in einstellbarem Abstand wieder aufzubauen und kann in der Zwischenzeit einen bildschirmfüllenden Hinweis zeigen – damit ein Terminal ohne Verbindung nicht bedienbar aussieht.
HTTP Basic gegen feste Zugangsdaten oder gegen einen eigenen Handler – dann entscheidet die Anwendung selbst, etwa gegen ihre Datenbank oder die Domäne.
Kein Indy, kein HTTP-Server von der Stange, keine JavaScript-Bibliothek. Der Transport geht direkt auf Winsock, der Browser-Client ist reines ES5. Was nicht da ist, kann auch nicht veralten.
Sieben Schichten, jede ohne Kenntnis der darüber liegenden. Nur die beiden obersten kennen die VCL.
Diese Trennung ist der Grund, warum das Paket prüfbar ist. Alles, was kein Formular braucht – Zustandsbaum, Delta-Bildung, HTTP, JSON, Prüfsummen, der ganze Browser-Client – wird in einem Konsolenprogramm getestet. Die VCL-Hälfte läuft über zwei Diagnoseprogramme, die echte Controls erzeugen und das Ergebnis Zeile für Zeile ausweisen.
Dieselben Dateien übersetzt Delphi 5 von 1999 und RAD Studio 13. Nicht zwei Zweige, nicht zwei Kopien.
Das ist keine Spielerei, sondern der Punkt: die Anwendungen, die diesen
Weg in den Browser am nötigsten haben, sind genau die alten. Der
Preis dafür ist Disziplin – keine Generics, keine inline
deklarierten Variablen, kein $IF, keine Klassenhelfer, und
ein byteorientierter Zeichenkettentyp, weil ein string auf
beiden Compilern etwas anderes bedeutet. Unterschieden wird an genau einer
Stelle, über zwei Schalter in einer Include-Datei.
Gemessen, nicht geschätzt.
| Format | dasselbe Control als Bild |
|---|---|
| BMP (Delphi 5) | 46 854 Byte |
| PNG (RAD Studio 13) | 460 Byte |
Was das Paket nicht kann und nicht sein will.
Nicht ans Internet. Das ist für das Firmennetz gebaut. Die Anmeldung ist HTTP Basic – das ist Kodierung, keine Verschlüsselung: wer die Verbindung mitlesen kann, liest die Zugangsdaten mit. Auf einem geschlossenen Netz ist das in Ordnung, darüber hinaus nicht.
Ein Formular, eine Anwendung: der Browser sieht, was auf dem Server-Bildschirm steht. Wer für jeden Benutzer eine eigene Sitzung braucht, braucht etwas anderes.