Der Chat oeffnet dort, wo das Neue anfaengt -- bei allen sechs Rollen

Filipe: "wenn ich in den chat rein gehe und es neue kommentare gibt,
will ich dass mein chat sich da öffnet wo die neuen nachrichten anfangen
die ich noch nicht gesehen hab bitte und nicht immer ganz unten. sonst
muss man immer hoch scrollen um die neuen zu lesen und das ist scheisse.
perfektionier das bei allen rollen."

DIE FUNKTION GAB ES SEIT DEM 09.09.2026 -- eine Linie „Ab hier neu" und
einen Sprung darauf. Sie hat trotzdem nicht funktioniert, und zwar aus
DREI Gruenden, die sich gegenseitig verdeckt haben. Gefunden hat sie
keine Ueberlegung, sondern eine Messung in Pixeln: Wie weit ist die
Linie vom oberen Rand entfernt? Ein negativer Wert heisst „darueber",
also unsichtbar.

1. DER SPRUNG RECHNETE GEGEN DEN FALSCHEN PUNKT.
   `linie.offsetTop` ist der Abstand zum `offsetParent` -- und das ist
   nur dann der Verlauf, wenn dieser `position: relative` traegt. Tut
   er nicht. Gemessen auf 390 px landete die Linie 23 px OBERHALB des
   sichtbaren Bereichs: Man musste genau das tun, was Filipe nicht mehr
   tun wollte. Am Rechner stimmte es zufaellig, weil dort weniger
   dazwischenliegt -- deshalb ist es nie aufgefallen.
   Jetzt: Oberkante der Linie minus Oberkante des Verlaufs plus dessen
   Bildlaufposition. Das gilt immer, egal wer wessen offsetParent ist.

2. JEDES NACHLADEN LOESCHTE DIE LINIE.
   `gelesenBeimOeffnen` wurde bei JEDEM Aufruf gesetzt, auch beim
   sanften Nachladen. Sanft laedt der Verlauf staendig nach -- vor
   allem, wenn der Ereignisstrom sich verbindet. Die Seite laedt,
   zeichnet, meldet „gelesen bis hier", der Strom verbindet sich, laedt
   sanft nach -- und jetzt steht in `gelesen_bis` schon die letzte
   Nachricht. Keine Linie mehr, Sprung ans Ende.
   DAS IST DER FEHLER, DEN FILIPE GESEHEN HAT. Und er wuerfelte: Kommt
   die Verbindung vor dem ersten Zeichnen, passiert nichts; danach ist
   die Linie weg. Ueber sechs Rollen gemessen waren mal drei rot, mal
   zwei, mal andere -- bei unveraendertem Code.

3. UND EIN EINZIGER SPRUNG REICHT NICHT.
   Zwischen Sprung und fertigem Bild waechst die Hoehe noch: Schriften
   kommen an und setzen den Text um, Bilder melden ihre Groesse. Jetzt
   wird nachgezogen -- nach den Schriften, nach jedem Bild, nach zwei
   Bildwiederholungen -- und die Stelle haelt, bis der Mensch selbst
   scrollt. „Ich habe dich an die neue Stelle gesetzt" ist eine Zusage;
   sie beim naechsten Nachladen zu brechen waere schlimmer, als sie nie
   gegeben zu haben.

GEMESSEN -- server/pruef-chat-neu-stelle.mjs (neu), 63 Pruefungen,
0 Fehler, zweimal hintereinander mit demselben Ergebnis:

  Sechs Rollen in beiden Haeusern (DogFather, rechte Hand, linke Hand,
  Modi auf crew.; Manager und Creator auf workspace.), je auf Handy
  (390 px) und Rechner (1280 px). Je Blick: Steht die Linie im
  Verlauf? Ist sie zu SEHEN? Steht Zusammenhang darueber? Und ist der
  Verlauf NICHT am Ende?

  Ergebnis: 115-118 px unter dem oberen Rand am Handy, 150-153 px am
  Rechner -- darueber jeweils die letzte alte Nachricht.

  Dazu drei Gegenproben: Ohne Ungelesenes gibt es keine Linie und der
  Verlauf steht am Ende (sonst laendete man grundlos mitten im
  Verlauf); und die Messung erkennt „nicht sichtbar" auch wirklich
  (-1403 px an einem absichtlich nach unten gescrollten Verlauf) --
  sonst waere jede gruene Zeile darueber wertlos.

DIE PRUEFUNG SELBST HAT ZWEIMAL DAS FALSCHE GEMESSEN, bevor sie das
Richtige maass, und beides steht als Begruendung darin: Sie schickte
`/gelesen` ohne `bis` (die Route verlangt eine Nummer und lehnt sonst
ab -- der Lesestand blieb null, die Linie entstand nie), und sie
benutzte einen Raum je Rolle fuer zwei Blicke, was einen Wettlauf mit
der „gelesen"-Meldung erzeugte. Jetzt bekommt jeder Blick seinen
eigenen Raum: mehr Aufbau, dafuer immer dasselbe Ergebnis.

pruef-chat 63/0, pruef-chat-optik 62/0, pruef-chat-kanaele 81/0,
pruef-treffchat 110/0, pruef-chat-neu 36/0, pruef-pin-fuer-mich 37/0,
pruef-erwaehnung 129/0, pruef-gifs 17/0.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-25 14:40:01 +02:00
co-authored by Claude Opus 5
parent 9d219e17ce
commit cdb6c46f8b
44 changed files with 1420 additions and 638 deletions
+148 -7
View File
@@ -1473,6 +1473,17 @@
weiter unten schon einmal behoben wurde ("Wer liest, bleibt
stehen"). */
let zurNeuLinie = false;
/* ==== WIRD NOCH NACHGEZOGEN? (25.09.2026) ======================
Steht hier oben und nicht im Block darunter, obwohl es nur dort
benutzt wird -- und zwar mit Grund: Der Zuhoerer, der das
Nachziehen beendet, wird EINMAL an den Verlauf gebunden und
ueberlebt jeden Raumwechsel. Haelt er eine lokale Variable fest,
stoppt er beim zweiten Gespraech die falsche (die vom ersten),
und im neuen wird noch zwei Sekunden lang weitergezogen, waehrend
der Mensch schon scrollt. Eine Variable an dieser Stelle kann das
nicht passieren. */
let nochNachziehen = false;
let antwortAuf = null;
/* ---------- In Nachrichten suchen (09.09.2026, Punkt 15) ----------------- */
@@ -1567,9 +1578,37 @@
weiter unten setzt ihn gleich auf die letzte Nachricht -- die
Linie muss also aus dem Wert gezogen werden, der beim OEFFNEN
galt. Danach wuerde sie nie irgendwo stehen. */
gelesenBeimOeffnen = daten.gelesen_bis ?? 0;
/* Ab hier darf EINMAL gesprungen werden -- siehe oben. */
zurNeuLinie = true;
/* ==== NUR BEIM ECHTEN OEFFNEN, NICHT BEI JEDEM NACHLADEN ====
Filipe: „wenn ich in den chat rein gehe und es neue kommentare
gibt, will ich dass mein chat sich da öffnet wo die neuen
nachrichten anfangen ... und nicht immer ganz unten."
HIER STAND DIE ZUWEISUNG OHNE BEDINGUNG -- und damit lief sie
auch bei jedem SANFTEN Nachladen. Sanft laedt der Verlauf
staendig nach: wenn der Ereignisstrom sich verbindet
(`strom.addEventListener('open', ...)`), wenn jemand etwas
anheftet, nach einer eigenen Nachricht.
DAS ERSTE DAVON IST DER FEHLER, DEN FILIPE GESEHEN HAT. Die
Seite laedt, zeichnet, meldet „gelesen bis hier" -- und dann
verbindet sich der Strom und laedt sanft nach. Jetzt steht in
`gelesen_bis` schon die LETZTE Nachricht, die Linie entsteht
nicht mehr, und der Verlauf springt ans Ende.
WARUM ES FLATTERTE: Kommt die Strom-Verbindung vor dem ersten
Zeichnen, passiert nichts; kommt sie danach, ist die Linie
weg. Gemessen ueber sechs Rollen waren mal drei rot, mal
zwei, mal andere -- bei unveraendertem Code. Ein Fehler, der
wuerfelt, sieht wie „geht manchmal" aus und wird nie gemeldet.
`sanft` HEISST: dasselbe Gespraech, nur frisch geholt. Dann
gehoert die Linie genau dorthin, wo sie schon stand. */
if (!sanft) {
gelesenBeimOeffnen = daten.gelesen_bis ?? 0;
/* Ab hier darf EINMAL gesprungen werden -- siehe oben. */
zurNeuLinie = true;
}
antwortAuf = null;
} catch { return; }
@@ -2442,13 +2481,115 @@
auch, wie viel unten wartet) und damit eine eintreffende
Nachricht einen nicht nach unten reisst. */
if (zurNeuLinie) {
zurNeuLinie = false;
const linie = ziel.querySelector('.chat-neu');
if (linie) {
ziel.scrollTop = Math.max(0, linie.offsetTop - ziel.clientHeight * 0.25);
unten = amEnde(ziel);
/* ==== EINMAL SPRINGEN REICHT NICHT (25.09.2026) ============
Filipe: „wenn ich in den chat rein gehe und es neue
kommentare gibt, will ich dass mein chat sich da öffnet wo
die neuen nachrichten anfangen ... sonst muss man immer
hoch scrollen um die neuen zu lesen und das ist scheisse."
DER SPRUNG STAND SEIT DEM 09.09. HIER -- und er sass
trotzdem falsch. Gemessen auf 390 px: Die Linie landete
23 px OBERHALB des sichtbaren Bereichs, man musste also
genau das tun, was Filipe nicht mehr tun wollte. Am Rechner
stimmte es (74 px unter dem Rand); deshalb ist es nie
aufgefallen.
DER GRUND IST DAS NACHWACHSEN. Zwischen dem Sprung und dem
fertigen Bild aendert sich die Hoehe noch: Schriften kommen
an und setzen den Text neu um, Bilder melden ihre Groesse,
Sprachnachrichten bekommen ihre Leiste. Gemessen sind das
auf dem Handy 45 px in einem Verlauf von 5000 -- und das
reicht, weil dort jede Zeile dreimal umbricht. Am Rechner
faellt derselbe Zuwachs nicht auf.
ALSO WIRD NACHGEZOGEN, statt die Hoehe zu raten: nach dem
naechsten Bild, nach den Schriften, nach zwei
Bildwiederholungen. Jedes Mal an dieselbe Stelle -- die
Rechnung ist billig, und sie ist nach jedem Zuwachs wieder
richtig.
UND NUR, SOLANGE NIEMAND SELBST SCROLLT. Wer nach dem
Oeffnen sofort weiterliest, darf nicht zurueckgerissen
werden; das waere derselbe Fehler wie der, den die Regel
„nur nach unten springen, wenn man unten war" verhindert. */
/* ==== DIE STELLE HAELT, BIS DER MENSCH ETWAS TUT ===========
Zuerst wurde `zurNeuLinie` beim ersten Zeichnen auf false
gesetzt -- einmal springen, fertig. Das reicht nicht: Der
Verlauf wird staendig SANFT nachgeladen (der Ereignisstrom
verbindet sich, jemand heftet etwas an, eine Nachricht
kommt). Jedes Nachladen baut die Zeilen neu auf, und die
Bildlaufposition ist danach nicht mehr die von vorhin.
Gemessen landete der Verlauf dann doch am Ende -- 2016 px
unter der Linie.
JETZT BLEIBT DER MERKER STEHEN, bis einer von zwei Dingen
passiert: Der Mensch scrollt selbst (dann hat er die Stelle
gesehen und will woanders hin), oder der Raum wechselt.
Bis dahin findet jedes Neuzeichnen dieselbe Stelle wieder.
DAS IST AUCH INHALTLICH RICHTIG. „Ich habe dich an die neue
Stelle gesetzt" ist eine Zusage; sie beim naechsten
Nachladen zu brechen, waere schlimmer, als sie nie gegeben
zu haben. */
nochNachziehen = true;
const ansZiel = () => {
if (!nochNachziehen) return;
const l = ziel.querySelector('.chat-neu');
if (!l) return;
/* ==== `offsetTop` MISST GEGEN DEN FALSCHEN PUNKT ==========
Hier stand `l.offsetTop`. Das ist der Abstand zum
`offsetParent` -- und der ist nur dann der Verlauf selbst,
wenn dieser `position: relative` traegt. Tut er nicht;
gemessen landete die Linie damit 23 px OBERHALB des
sichtbaren Bereichs, also genau dort, wo man sie nicht
sieht. Die Rechnung war um 140 px daneben, und am Rechner
fiel das nicht auf, weil dort weniger dazwischenliegt.
DER ABSTAND ZWEIER KAESTEN IST IMMER RICHTIG: die Oberkante
der Linie minus die Oberkante des Verlaufs, plus dessen
aktuelle Bildlaufposition. Das gilt unabhaengig davon, wer
gerade wessen `offsetParent` ist -- und es ist dieselbe
Rechnung, mit der die Messung den Fehler gefunden hat. */
const bezug = ziel.getBoundingClientRect().top;
const wo = l.getBoundingClientRect().top - bezug + ziel.scrollTop;
ziel.scrollTop = Math.max(0, wo - ziel.clientHeight * 0.25);
unten = amEnde(ziel);
springKnopfZeigen();
};
/* WER SELBST SCROLLT, HAT ENTSCHIEDEN. Ab da wird nicht
mehr nachgezogen und auch beim naechsten Nachladen nicht
mehr gesprungen -- sonst risse es einen aus dem Text, den
man gerade liest. Die Zuhoerer haengen am Verlauf und
werden nur einmal gesetzt (`einmalGebunden`), sonst kaemen
bei jedem Neuzeichnen neue dazu. */
const schluss = () => { nochNachziehen = false; zurNeuLinie = false; };
if (!ziel.dataset.neuWache) {
ziel.dataset.neuWache = 'ja';
for (const art of ['wheel', 'touchstart', 'keydown', 'pointerdown']) {
ziel.addEventListener(art, schluss, { passive: true });
}
}
/* Nach spaetestens zwei Sekunden wird nicht mehr NACHGEZOGEN
-- der Merker bleibt aber stehen. Nachziehen faengt das
Nachwachsen von Schriften und Bildern ab; das ist nach zwei
Sekunden vorbei. Ein Zuhoerer, der fuer immer zieht, holte
den Verlauf beim naechsten Bild in zehn Minuten wieder an
eine Stelle, die niemand mehr sucht. */
setTimeout(() => { nochNachziehen = false; }, 2000);
ansZiel();
requestAnimationFrame(() => requestAnimationFrame(ansZiel));
document.fonts?.ready?.then(ansZiel).catch(() => {});
for (const bild of ziel.querySelectorAll('img')) {
if (!bild.complete) bild.addEventListener('load', ansZiel, { once: true });
}
neuUnten = 0;
springKnopfZeigen();
return;
}
/* Keine Linie heisst: nichts Neues seit dem letzten Mal. Dann