frontend: Pad einbetten — drei Ansichten mit Splitter, Spiegel bleibt

Das Einbetten ist möglich, ohne D25 zu opfern. Nachgemessen zuerst, ob der
Server es überhaupt zulässt: pad.hostsharing.net sendet kein
`X-Frame-Options` und keine CSP mit `frame-ancestors`, und im Versuch baute das
Pad in einem fremdstämmigen Rahmen seine inneren Editorframes auf
(`contentWindow.length === 2` = ace_outer/ace_inner), samt Werkzeugleiste,
heiler Einrückung und lebender Socket-Verbindung.

Einbetten heißt aber nicht Ersetzen: Ein cross-origin-iframe gibt keinen
DOM-Zugriff, ein ersetztes Textfeld kostete also beide Richtungen von D25
(Alt+Klick -> Zeile, Cursor-Zeile -> Knoten). Deshalb drei Ansichten, reihum
über einen Wähler in der Editor-Titelzeile:

- Pad und Text, geteilt durch einen eigenen Splitter (Mechanik und Idiom vom
  Legenden-Splitter aus D26: von der Spiegelseite gezogen, Doppelklick setzt
  zurück, `--pcol`/`--prow` getrennt gehalten und persistiert). Der Spiegel darf
  schmal werden und trägt weiter die Sprünge — die arbeiten auf unserem
  eigenen `<textarea>`.
- nur Pad — Spiegel ausgeblendet; `revealEditor()` schaltet bei einem Sprung
  auf „beide", sonst zeigte der Sprung ins Nichts.
- nur Text — wie bisher, kein Rahmen.

Ein Wähler statt dreier Knöpfe, weil die Titelzeile schon Dokument, Pad, Neu
laden, Kopieren, Legende und Fensterknöpfe trägt (mobil dreifach eng, D17) —
derselbe Reihum-Griff wie beim Modus-Wähler dort. Der Zustand steckt im Symbol
(geteilt / links gefüllt / rechts gefüllt), der Tooltip nennt ihn im Klartext.

Der Rahmen bekommt nur sichtbar ein `src`, sonst `about:blank`. Das ist kein
Geiz um Bytes: Ein geladenes Pad verbindet sich per Socket und macht dich in
dessen Anwesenden-Liste sichtbar. „Nur Text" ist damit die Ansicht, die nichts
von dir verrät.

Zwei Dinge, die beim Prüfen auffielen und behoben sind:
- Eine überzählige `}` ließ das Modul gar nicht laden. Die Messung sah trotzdem
  plausibel aus, weil die abgefragten Werte die statischen HTML-Vorgaben waren —
  seither prüft jede Messung zuerst, ob überhaupt Knoten gerendert sind, und
  `node --check` läuft vor dem Blick in den Browser.
- In „nur Pad" war der Spiegel weiterhin sichtbar (sogar breiter als das Pad):
  die Regel `.src-area.pv-pad>textarea{display:none}` fehlte.

Bleibender Preis: `Set-Cookie: token=…; SameSite=Lax`. Das Cookie ist Etherpads
Autoren-Identität und wird im fremdstämmigen Rahmen nicht mitgesendet — man ist
bei jedem Laden ein neuer Autor. Nur serverseitig zu beheben
(`cookie.sameSite: "None"`), nicht in Werkbaum. Steht in D31.

Auf kleinem Bildschirm nachgemessen statt geschätzt: bei 375x812 bleiben Pad
317 px und Spiegel 180 px, per Splitter verschiebbar — brauchbar, also keine
Sonderregel für Mobil.

SPEC §9 und D31 nachgezogen; 5 neue i18n-Schlüssel in allen 9 Sprachen.
This commit is contained in:
mhoennig
2026-07-30 13:14:40 +02:00
parent 793561c873
commit 0a08706877
7 changed files with 286 additions and 28 deletions
+40 -1
View File
@@ -208,6 +208,25 @@
Horizontal nebeneinander, vertikal (.side) untereinander. */
.editor-body{display:flex;align-items:stretch;flex:1 1 auto;min-height:0}
.editor-body textarea{flex:1 1 auto;min-width:0;min-height:0}
/* Pad-Rahmen | interner Spiegel (D31). Eigener Bereich, damit die
Legenden-Aufteilung (D26) unangetastet bleibt. Nur in der Ansicht „beide"
bekommt der Spiegel eine feste Größe (`--pcol`/`--prow`) — sonst füllt er
wie bisher den Bereich. Die 85 %-Grenze steht zusätzlich hier im CSS, weil
der gespeicherte Wert ein px-Wert ist und der Bereich später schrumpfen
kann (dieselbe Falle wie bei der Legende, D26). */
.src-area{display:flex;align-items:stretch;flex:1 1 auto;min-width:0;min-height:0}
.padframe{flex:1 1 auto;min-width:0;min-height:0;border:0;background:var(--card)}
.padframe[hidden]{display:none}
.src-area.pv-both>textarea{
flex:0 0 var(--pcol,300px);max-width:85%;
border-left:1px dashed rgba(36,52,71,.18);
}
.src-area.pv-pad>textarea{display:none}
.side .src-area,body.mobile .src-area{flex-direction:column}
.side .src-area.pv-both>textarea,body.mobile .src-area.pv-both>textarea{
flex:0 0 var(--prow,180px);max-width:none;max-height:85%;
border-left:0;border-top:1px dashed rgba(36,52,71,.18);
}
/* Legende: bewusst KEIN <details> mehr. Chrome legt den Details-Inhalt in
`::details-content`; dadurch war `.hint` kein Flex-Kind von `.agenda` mehr,
`flex:1/min-height:0` liefen ins Leere und der Inhalt wurde geclippt statt
@@ -259,6 +278,17 @@
.side .agenda.open{width:auto;max-width:none;height:var(--hrow,220px);max-height:85%}
.side .hint-gutter{cursor:row-resize}
.side .hint-gutter::before{width:38px;height:3px}
/* Splitter Pad|Spiegel — gleiches Idiom wie der Legenden-Splitter. */
.pad-gutter{
flex:0 0 10px;align-self:stretch;
cursor:col-resize;touch-action:none;
display:flex;align-items:center;justify-content:center;
}
.pad-gutter[hidden]{display:none}
.pad-gutter::before{content:'';width:3px;height:38px;border-radius:2px;background:rgba(36,52,71,.18)}
.pad-gutter:hover::before,.pad-gutter.dragging::before{background:var(--or)}
.side .pad-gutter,body.mobile .pad-gutter{cursor:row-resize}
.side .pad-gutter::before,body.mobile .pad-gutter::before{width:38px;height:3px}
.panel{
background:var(--card);
@@ -332,7 +362,16 @@
/* „im Pad bearbeiten" (D31) — nur bei einem ?etherpad=-Dokument sichtbar.
Das [hidden] braucht die eigene Regel, weil .copybtn ein display setzt. */
.padlink{text-decoration:none}
.padlink[hidden],.padrefresh[hidden]{display:none}
.padlink[hidden],.padrefresh[hidden],.padview[hidden]{display:none}
/* Der Ansichts-Wähler zeigt seinen Zustand als Position im Rahmen: geteilt,
links gefüllt (Pad) oder rechts gefüllt (Spiegel). Zwei Klassen, damit die
Regel die allgemeinere `.copybtn svg{display:block}` sicher schlägt. */
.copybtn.padview .ic-both,
.copybtn.padview .ic-pad,
.copybtn.padview .ic-text{display:none}
.copybtn.padview[data-view="both"] .ic-both,
.copybtn.padview[data-view="pad"] .ic-pad,
.copybtn.padview[data-view="text"] .ic-text{display:block}
/* Läuft ein Abruf, dreht das Symbol: bei gedrosselter Gegenseite (D31) können
das die vollen 20 s sein, und ein Knopf ohne Rückmeldung wirkt kaputt. */
/* Sperre nach einem Abruf (D31): sichtbar gedämpft, aber **klickbar** — der