feat: F5.3b the editor on a project of server F

?project=<uuid> opens the shared document over /sync and the room over
/sim (RemoteSimHost: messages wait for the socket, a lost socket comes
back with what it watched, F's refusal or parting ends it); instead of
the editor, a word why when F refuses or lets go. F tells each
connection its access at once. Who only reads sees everything and
changes nothing: no palette or undo, a locked surface, the sheet's
fields disabled (tabs stay), no starting or stopping; thawed, the
document comes anew. The toolbar names who started the simulation.
Refusals carry no message, so Hocuspocus writes nothing outside F's
JSON log. A browser test runs three people against a real F.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-04 15:00:18 +00:00
co-authored by Claude Opus 5.5
parent 8cfc518f0c
commit f52852493a
28 changed files with 1142 additions and 58 deletions
+5
View File
@@ -260,6 +260,9 @@ Swift-Pfade relativ zu `filius-ipad/ios/FiliusPad/TopologyEditor/` (Stand
| `browser/touch.spec.ts`, `browser/fingers.ts`, `playwright.config.ts` (Projekte `ipad-portrait`, `ipad-landscape`), `package.json` (Playwright fest auf 1.62.1) | — | neu (F4.9): der Editor nur mit dem Finger, in Chromium am Desktop und in WebKit als iPad hoch und quer — die Abnahme von F4 als Ablauf ("zwei Rechnernetze" neu gebaut, Adressen im Blatt, Kabel per Werkzeug, Befehlszeile installiert, Ping quer über den Router), Zwei-Finger-Zoom vergrößert die Fläche und nie die Seite (M7, ohne Rahmen), jede Aktion ohne Rechtsklick (langes Drücken, Dreipunkt-Knopf, Menü der Dokumentation), alle Ziele mindestens 44 px, nichts über den Rand. Echte Gesten: Chromium über CDP, WebKit über `Input.dispatchTouchEvent` seiner Seitensitzung, die Playwright nur über die Verbindung im selben Prozess hergibt (Naht im Inneren von Playwright, Version deshalb fest). Ein gezogener Finger hält vor dem Loslassen 150 ms still: sonst nimmt Chromium die Geste als Schwung, und das nächste Antippen stoppt nur den Schwung |
| `src/sheet/ServicePanels.tsx`, `src/sheet/RowsEditor.tsx`, `src/sheet/SheetTabs.tsx`, `src/sheet/DeviceSheet.tsx` (Seiten) | Java `JDHCPKonfiguration` (`jdhcpkonfiguration_msg1`–`15`), `JPortForwardingDialog` (`jportforwarding_msg4`–`7`, `jgatewayconfiguration_msg19`) | neu (F4.5c): Javas Dialoge als Seiten des Blatts mit „Zurück“ — DHCP-Server (Grundeinstellungen, statische Adresszuweisung; beim Rechner, gesperrt während DHCP die Karte einrichtet, und auf dem LAN-Reiter des Heimrouters), Portfreigaben (Heimrouter); jedes Feld beim Verlassen übernommen statt mit Javas OK-Knopf. Die Zeilentabellen (Weiterleitung, Zuordnungen, Portfreigaben) teilen sich `RowsEditor`: eine neue Zeile, sobald alle Felder gültig sind, Zeile wählen, bearbeiten, löschen. Javas Tooltip zu „Manuelle Einstellungen“ als Text unter dem Kästchen (ein Finger hat kein Überfahren) |
| `src/sheet/RouterSheets.tsx`, `src/sheet/NameField.tsx`, `src/canvas/Canvas.tsx` (`highlightedLink`), `src/theme/tokens.css` (`--canvas-cable-active`) | Java `JVermittlungsrechnerKonfiguration`, `JGatewayConfiguration`, `JWeiterleitungsTabelle` (Wörter `jvermittlungsrechnerkonfiguration_msg6`–`26`, `jweiterleitungstabelle_msg3`–`6`), `JCablePanel.farbeBlinken`, `Kabel.setAktiv` | neu (F4.5b): Blatt des Vermittlungsrechners — Reiter Allgemein (Name, Gateway, Automatisches Routing, IP-Weiterleitung nur angezeigt), je Karte (Reiter mit ihrer Adresse, „Verbunden mit …“, Adresse, Maske, MAC) und Weiterleitungstabelle (alle Einträge oder nur die manuellen, Javas Zeilen grau, eine neue Zeile, die mit vier gültigen Feldern übernommen wird, Zelle bearbeiten, Eintrag löschen; bei RIP gesperrt); Blatt des Heimrouters mit Allgemein, LAN, WAN (DHCP sperrt die WAN-Adresse). Das Kabel der offenen Karte leuchtet grün wie bei Java. Javas „Eintrag Löschen“ klein geschrieben |
| `src/remote/remoteSimHost.ts`, `src/remote/sharedDoc.ts`, `src/remote/SharedProject.tsx`, `src/main.tsx` (`?project=`) | Plan §5, §6.1, §8.5; Hocuspocus' Provider 3.4.4 (MIT) | neu (F5.3b): der Editor auf einem Projekt von F — das gemeinsame Dokument über `/sync` (bereit mit Dokument, Lesestufe und ob eingefroren; abgewiesen; getrennt) und der Raum über `/sim` (`RemoteSimHost`: Nachrichten vor dem Öffnen warten, nach einem Verlust kommt der Socket wieder, mit doppelter Wartezeit bis 10 s und allen abonnierten Themen; Absage und Trennung von F beenden ihn); wer liest und wieder schreiben darf (aufgetaut), bekommt das Dokument neu; statt des Editors ein Wort, warum nicht |
| `src/Editor.tsx`, `src/canvas/Canvas.tsx` (`locked`), `src/sheet/SheetLock.tsx`, `src/sheet/DeviceSheet.tsx`, `src/sheet/RouterSheets.tsx`, `src/sheet/FirewallPanel.tsx`, `src/sheet/ServicePanels.tsx`, `src/design/DocumentationSidebar.tsx`, `src/simulation/ModeBar.tsx`, `src/simulation/useSimulation.ts`, `src/i18n/*.json` | Plan §6.2, §6.4, §9.3 | geändert (F5.3b): wer nur liest, sieht alles und ändert nichts — keine Palette, kein Rückgängig, nichts bewegt sich oder hat ein Menü, die Felder des Blatts sind gesperrt (die Reiter bleiben), keine neuen Doku-Elemente, die Simulation wird von ihm weder gestartet noch angehalten; Desktops und Datenaustausch öffnen zum Zuschauen; ein „nein“ des Raums wird gesagt; „Simulation gestartet von …“ und „Nur ansehen“ bzw. „Eingefroren – nur ansehen“ neben den Modusknöpfen |
| `test/remoteSimHost.test.ts`, `browser/shared.spec.ts`, `browser/serverF.ts` | — | neu (F5.3b): `RemoteSimHost` auf nachgebauten Sockets mit gestellter Uhr; drei Personen in drei Browsern an einem echten F (aus dem Bündel gestartet): verschieben, nur lesen, eine Simulation für alle mit Namen, ein Ping in einer Befehlszeile bei der anderen, Eintippen als Leser abgewiesen, eingefroren und aufgetaut, Zugang beendet; Absagen ohne Sitzung und ohne Projekt |
## packages/server
@@ -271,6 +274,8 @@ Swift-Pfade relativ zu `filius-ipad/ios/FiliusPad/TopologyEditor/` (Stand
| `src/sync.ts` (Anmeldung), `src/server.ts` (Wege, Herkunft) | Plan §8.5 | geändert (F5.2): `/sync` entscheidet nach der Berechtigung der Sitzung, die der Keks nennt — keine, Lesen, Schreiben; ein eingefrorenes Projekt lesen alle; jede Absage mit Grund und im Protokoll; ein WebSocket von einer Seite fremder Herkunft wird abgewiesen (eine Seite anderswo unter derselben Domain brächte den Keks mit, SameSite trennt nur Sites); unerwartete Fehler eines Wegs werden 500 und stehen im Protokoll |
| `src/service.ts`, `src/store.ts` (`freeze`, `remove`), `src/persistence.ts` (`discard`), `src/main.ts` | Dashboard `backend/modules/scratch/src/service.py`, Scratch-Editor `revoke`, `active_sessions` | neu (F5.2): `/svc/*` mit Dienstschlüssel (in konstanter Zeit verglichen): Projekt anlegen (leeres FILIUS-Projekt), löschen (erst nicht mehr gespeichert, dann weg, dann alle getrennt — wer wiederkommt, findet kein Projekt), einfrieren und auftauen (wer drin ist, liest bzw. schreibt wieder und erfährt es), Berechtigung einer Person an einem Projekt oder alle ihre Sitzungen beenden (wer verbunden ist, wird getrennt und beim Wiederkommen abgewiesen), die Sitzungen auflisten; ohne Geheimnisse startet der Dienst nicht |
| `src/simRooms.ts`, `src/simWorker.ts`, `src/simChannel.ts`, `src/server.ts` (`/sim`), `src/service.ts` (Räume) | Plan §6.1–§6.4, §8.2 | neu (F5.3a): die Simulationsräume auf F — einer je Projekt für alle darin, gehalten von wenigen `worker_threads` (der mit den wenigsten Räumen nimmt den nächsten), jeder Raum mit einer Kopie des Dokuments: Hocuspocus' direkte Verbindung hält das Dokument offen, solange der Raum lebt, jede Änderung geht in den Raum, was der Raum schreibt, kommt als seine Änderung zurück; `/sim?project=…` meldet an wie `/sync` (Keks, Berechtigung, Herkunft), Nachrichten vor der Anmeldung werden gehalten; ändern nur mit Schreibstufe; nach dem Letzten bleibt der Raum 60 s und hält dann, schreibt zurück und endet; Einfrieren hält eine laufende Simulation an (ihr Stand kommt noch ins Projekt), Löschen beendet den Raum ohne Zurückschreiben, Widerruf trennt auch im Raum; ein scheiternder Faden beendet seine Räume; beim Herunterfahren halten alle Räume und schreiben zurück; ein Socket ohne Antwort auf Pings wird getrennt; Nachrichten höchstens 1 MiB; `/healthz` zählt die Räume; der Build bündelt den Faden als `dist/simWorker.mjs` |
| `src/sync.ts` (Zugang gleich zu Beginn, stille Absage) | Plan §8.5 | geändert (F5.3b): jede Verbindung erfährt ihren Zugang sofort nach der Anmeldung (Nachricht `access`), damit der Editor weiß, ob er liest und warum; eine Absage trägt keinen Meldungstext — Hocuspocus schreibt sonst die Meldung eines gescheiterten Hooks auf die Konsole, außerhalb des JSON-Protokolls |
| `test/editorConnection.test.ts` | — | neu (F5.3b): `connectSharedDoc` des Editors gegen einen echten F — bereit mit Schreib- und Lesestufe, eingefroren und aufgetaut (neues Dokument, die verirrte Änderung ist weg, Schreiben geht wieder), eingefroren von Beginn an, Absage, Widerruf, Löschen, Loslassen |
| `test/sim.test.ts`, `test/harness.ts` (Faden) | — | neu (F5.3a): echte Sockets, der echte Faden (esbuild bündelt ihn wie der Build), Beispiel internetwork_komplett: ein Start für alle mit Namen, Ping in einer Befehlszeile bei der anderen, Zurückschreiben ins Dokument und eine Datei aus dem Dokument im Raum, Lesestufe, Absagen, Nachlauf, Einfrieren, Löschen, Widerruf, Wahl des Fadens, scheiternder Faden, Herunterfahren, Ping |
| `test/ticket.test.ts`, `test/tickets.ts`, `test/tickets.python.json` | `tools/ticket-oracle` | neu (F5.2): jede Karte des Python-Orakels liest Node wie Python (Nutzlast oder derselbe Grund der Ablehnung); in Node signierte Karten tragen dasselbe JSON zum selben Zeitpunkt (Nodes zlib packt anders, ungepackt byte-gleich); die Ansprüche |
| `test/access.test.ts`, `test/harness.ts` | — | neu (F5.2): `/__auth` (Keks, `next`, schlechte Karten, unbekanntes Projekt, zweites Projekt, nächste Person, Verlängerung), Absagen an `/sync` mit Grund, Herkunft, Dienstweg (Schlüssel, Anlegen, Fehlerantworten, Löschen, Einfrieren, Widerruf einzeln und ganz, Sitzungsliste, 500) |