Files
dogfather-universe/workspace/leistung.html
T
DogFatherGitandClaude Opus 5 e28724564a Chat: sechs Ergaenzungen -- und ein Absturz, den nie jemand gesehen hat
Filipe: "die kategorie chat, boah ich will dass du die wirklich ueberall
perfektionierst. wirklich alles was gratis ist und hinzugefuegt werden
kann ohne problem will ich drin haben bitte. ich will das es hoch
profissionel und ultra krass geil ist."

Zuerst Bestand aufgenommen, nicht gebaut: Suche, Ungelesen-Zahlen, die
"ab hier neu"-Linie, Lesestand, Live-Strom, Antworten mit Zitat,
Zuruecknehmen und Enter-zum-Senden gab es schon. Dazugekommen ist, was
gefehlt hat und ohne fremde Bibliothek geht:

1. ADRESSEN SIND ANKLICKBAR. Gebaut als echte Knoten, nie ueber
   innerHTML -- ein Chat ist die eine Stelle, an der jeder schreiben
   darf. Erkannt wird bewusst wenig (http, https, www.): Wer mehr
   erkennt, macht irgendwann aus "z.b." einen Link ins Nichts.
   Satzzeichen am Ende bleiben beim Satz. rel="noopener noreferrer".
2. KOPIEREN je Nachricht, mit Rueckfall auf "Text markieren", wenn der
   Browser die Zwischenablage nicht hergibt.
3. DER VERLAUF REISST NIEMANDEN MEHR WEG. Vorher sprang er bei JEDEM
   Neuzeichnen ans Ende -- wer weiter oben nachlas und dabei eine
   Nachricht bekam, verlor die Stelle. Jetzt bleibt er stehen, und ein
   Knopf sagt, WIE VIEL unten wartet, nicht nur DASS etwas da ist.
4. ENTWUERFE JE GESPRAECH. Wer mitten im Satz wechselt, findet ihn
   wieder. Im localStorage, nicht auf dem Server: Ein Entwurf ist
   nichts, was jemand anders sehen soll. Abgeschickt heisst geloescht.
5. EMOJI-AUSWAHL, dreissig feste Zeichen, eingefuegt an der
   Cursorstelle. Am Handy hat die Tastatur sie ohnehin -- am Rechner
   nicht, und dort sitzt die Betreuung.
6. ZEICHENZAEHLER, sichtbar ab 400 Rest. Die Grenze steht NICHT im
   Skript, sondern kommt aus dem maxlength des Feldes -- zwei Stellen
   fuer dieselbe Zahl laufen auseinander.

--- DER FUND, um den es eigentlich geht ---

Die neue Pruefung hoert auf "pageerror". Damit kam sofort neun Mal
dieselbe Meldung: "Cannot set properties of null (setting 'hidden')",
raumOeffnen, Zeile 278.

Ursache: verlaufZeichnen() leerte den Verlaufskasten mit
`textContent = ''`. Darin liegt aber #verlauf-leer, der Absatz "Links
ein Gespraech auswaehlen". Nach dem ersten Zeichnen gab es ihn nicht
mehr, und raumOeffnen faellt sechs Zeilen weiter darueber.

Die Folge ist nicht die Fehlermeldung, sondern der Abbruch: Beim ZWEITEN
Aufruf liefen history.replaceState, gelesenMelden() und raeumeZeichnen()
nie. Der zweite Aufruf ist der Normalfall -- der Ereignisstrom ruft nach
jedem Verbindungsaufbau genau das, um nachzuholen, was waehrend der
Pause geschrieben wurde. Dieses Nachholen hat nie funktioniert. Von
aussen sah alles normal aus; nur die Konsole wusste Bescheid.

Behoben, indem nur noch die Nachrichten entfernt werden und der Absatz
stehenbleibt. Abschnitt 8 der Pruefung wechselt jetzt dreimal zwischen
zwei Gespraechen OHNE Neuladen und verlangt null Abstuerze.

--- UND EIN ZWEITER, der schon laenger drin war ---

Abschnitt 9 misst den Kontrast an der wirklichen Flaeche (Text kurz
unsichtbar, Flaeche fotografiert). Die Fusszeile jeder Nachricht stand
auf --text-still: auf der eigenen Blase 3,68:1, unter den 4,5:1 fuer
Text dieser Groesse. Aufgefallen ist es nie, weil niemand nachgemessen
hat. Jetzt traegt .chat-nachricht__fuss EINE Farbe (--text-leise,
4,81:1) und Uhrzeit, antworten, zuruecknehmen und kopieren erben sie --
statt vier eigener Angaben, die beim naechsten Mal auseinanderlaufen.

--- Pruefung ---

server/pruef-chat-ausbau.mjs, neu, 64 Pruefungen, alle gruen. Zu jedem
Punkt eine Gegenprobe: kein Link ohne Adresse, keine Auszeichnung aus
<b>/<img onerror>, kein Kopier-Knopf an zurueckgenommenen Nachrichten,
kein Sprungknopf fuer den, der schon unten steht, kein Entwurf nach dem
Abschicken, ein absichtlich zu dunkler Text faellt durch, und ein
absichtlich geworfener Fehler wird bemerkt (sonst waere Abschnitt 10
auch gruen, wenn niemand zuhoert).

Ausserdem gruen: pruef-css-klassen, pruef-chat, pruef-chat-optik.
Angesehen bei 1440 px und bei 390 px.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-09 12:13:24 +02:00

227 lines
9.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>Zahlen · Spicy & Dogi</title>
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<!-- Die Farbe der Fensterleiste, wenn der Workspace als App laeuft.
Stand auf #0674b9 (kraeftiges Blau) und stach damit als einziger
heller Balken ueber einer durchweg dunklen Seite. Jetzt derselbe
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609091211" />
<link rel="stylesheet" href="assets/css/start.css?v=202609091211" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609091211" />
<link rel="stylesheet" href="assets/css/leistung.css?v=202609091211" />
<link rel="stylesheet" href="assets/css/module.css?v=202609091211" />
</head>
<body class="start">
<div class="schleier" aria-hidden="true"></div>
<header class="kopfleiste">
<button type="button" class="zurueck-knopf" id="zurueck" hidden>
<svg class="zurueck-knopf__pfeil" viewBox="0 0 24 24" aria-hidden="true">
<path d="M14.5 5.5 8 12l6.5 6.5" />
</svg>
<span class="zurueck-knopf__text">Zurück</span>
</button>
<p class="marke"><span class="marke__text"><a class="zurueck" href="start.html">Creator&nbsp;Workspace</a> · Zahlen</span></p>
<div class="kopfleiste__rechts">
<span class="wer" id="wer">…</span>
<button type="button" class="abmelden" id="abmelden">Abmelden</button>
</div>
</header>
<main class="inhalt inhalt--breit">
<section class="kopf-zeile">
<div>
<p class="marke">Zahlen</p>
<h1 class="titel">Was wirklich passiert ist.</h1>
</div>
<div class="leistung__wahl" id="creator-wahl" hidden>
<label class="feld-schild" for="creator">Creator</label>
<select id="creator"></select>
</div>
</section>
<p class="fehler" id="fehler" role="alert" aria-live="polite"></p>
<!-- DIE WOCHE IN ZAHLEN.
Jede Karte trägt die drei Dinge, die eine Kennzahl braucht
(Recherche 31.08./06.09.2026): den WERT, den VERGLEICH zur
Vorwoche und den VERLAUF als kleine Linie. Eine Zahl ohne diese
drei ist eine Eitelkeitszahl -- sie sieht nach Auskunft aus, und
man kann nichts damit entscheiden. -->
<section class="leistung-woche" id="woche" hidden>
<div class="klapp-kopf">
<p class="feldschild">Diese Woche</p>
<p class="leistung__erfassung" id="erfassung"></p>
</div>
<div class="zahlkarten" id="zahlkarten"></div>
</section>
<!-- Die Ziele als Fortschritt, nicht als Satz. -->
<section class="leistung-ziele" id="ziele-block" hidden>
<div class="klapp-kopf">
<p class="feldschild">Ziele</p>
<button type="button" class="knopf-still" id="ziele-aendern" hidden>Ziele setzen</button>
</div>
<div class="zielbalken" id="zielbalken"></div>
</section>
<!-- Die Tageszeilen. Tastaturfreundlich: Eine Woche soll in unter
einer Minute eingetragen sein. -->
<section class="leistung-tage" id="tage-block" hidden>
<div class="klapp-kopf">
<p class="feldschild">Die letzten Tage</p>
<!-- Zwei Knöpfe rechts: erst der Schalter (er ordnet die Liste),
dann die Hauptaktion. Der Schalter trägt bewusst die stille
Ausführung -- er verändert nichts an den Daten. -->
<div class="klapp-kopf__knoepfe">
<button type="button" class="knopf-still" id="tage-schalter"
aria-expanded="false" aria-controls="tagetabelle" hidden>
<span id="tage-schalter-text">Alle zeigen</span>
<svg class="klapp-pfeil" viewBox="0 0 24 24" aria-hidden="true">
<path d="m6 9.5 6 6 6-6" />
</svg>
</button>
<button type="button" class="knopf knopf--klein" id="import-oeffnen">Aus Datei einlesen</button>
</div>
</div>
<div class="tagetabelle" id="tagetabelle"></div>
</section>
<p class="fuss">
Die Zahlen kommen aus dem offiziellen Export von TikTok LIVE Backstage –
entweder von Hand eingetragen oder als Datei eingelesen. Es wird nichts
automatisch abgerufen: Dafür gibt es keine offene Schnittstelle.
</p>
</main>
<!-- Einen Tag eintragen -->
<dialog class="dialog" id="tag-dialog">
<form method="dialog" id="tag-form" class="neu neu--blank">
<h2 class="dialog__titel" id="tag-titel">Tag eintragen</h2>
<p class="dialog__unter" id="tag-unter"></p>
<div class="neu__raster">
<div>
<label class="feld-schild" for="f-diamanten">Diamanten</label>
<input id="f-diamanten" type="number" min="0" inputmode="numeric" />
</div>
<div>
<label class="feld-schild" for="f-dauer">LIVE-Dauer <span class="leise">Minuten</span></label>
<input id="f-dauer" type="number" min="0" inputmode="numeric" />
</div>
<div>
<label class="feld-schild" for="f-verweildauer">Ø Verweildauer <span class="leise">Sekunden</span></label>
<input id="f-verweildauer" type="number" min="0" inputmode="numeric" />
</div>
<div>
<label class="feld-schild" for="f-zavg">Ø Zuschauer</label>
<input id="f-zavg" type="number" min="0" inputmode="numeric" />
</div>
<div>
<label class="feld-schild" for="f-zmax">Spitze</label>
<input id="f-zmax" type="number" min="0" inputmode="numeric" />
</div>
<div>
<label class="feld-schild" for="f-schenker">Schenker</label>
<input id="f-schenker" type="number" min="0" inputmode="numeric" />
</div>
<div>
<label class="feld-schild" for="f-follower">Neue Follower</label>
<input id="f-follower" type="number" min="0" inputmode="numeric" />
</div>
</div>
<!-- Der Kontext, ohne den Zahlen in die Irre führen. -->
<div class="neu__zeile">
<label class="feld-schild" for="f-notiz">
Was war an dem Tag besonders? <span class="leise">(optional)</span>
</label>
<input id="f-notiz" maxlength="500" placeholder="z. B. PK gegen Mia, krank, Technik ausgefallen" />
</div>
<p class="fehler" id="tag-fehler" role="alert" aria-live="polite"></p>
<div class="dialog__knoepfe">
<button type="submit" class="knopf knopf--klein" id="tag-speichern">Speichern</button>
<button type="button" class="abmelden" id="tag-zu">Abbrechen</button>
<button type="button" class="abmelden abmelden--warnung" id="tag-loeschen" hidden>Zeile löschen</button>
</div>
</form>
</dialog>
<!-- Ziele setzen -->
<dialog class="dialog" id="ziele-dialog">
<form method="dialog" id="ziele-form" class="neu neu--blank">
<h2 class="dialog__titel">Ziele je Woche</h2>
<p class="dialog__unter">
Aus „mehr Reichweite" wird eine prüfbare Größe. Leer lassen heißt: kein Ziel.
</p>
<div class="neu__raster">
<div>
<label class="feld-schild" for="z-diamanten">Diamanten pro Woche</label>
<input id="z-diamanten" type="number" min="0" inputmode="numeric" />
</div>
<div>
<label class="feld-schild" for="z-tage">LIVE-Tage pro Woche</label>
<input id="z-tage" type="number" min="0" max="7" inputmode="numeric" />
</div>
<div>
<label class="feld-schild" for="z-verweildauer">Ø Verweildauer <span class="leise">Sekunden</span></label>
<input id="z-verweildauer" type="number" min="0" inputmode="numeric" />
</div>
</div>
<p class="fehler" id="ziele-fehler" role="alert" aria-live="polite"></p>
<div class="dialog__knoepfe">
<button type="submit" class="knopf knopf--klein" id="ziele-speichern">Speichern</button>
<button type="button" class="abmelden" id="ziele-zu">Abbrechen</button>
</div>
</form>
</dialog>
<!-- Aus Datei einlesen. ERST VORSCHAU, DANN ÜBERNEHMEN -->
<dialog class="dialog dialog--breit" id="import-dialog">
<form method="dialog" id="import-form" class="neu neu--blank">
<h2 class="dialog__titel">Aus dem Export einlesen</h2>
<p class="dialog__unter">
Die CSV-Datei aus TikTok LIVE Backstage. Erst siehst du, was passieren
würde – geschrieben wird erst, wenn du bestätigst. Dieselbe Datei
zweimal einzulesen richtet keinen Schaden an.
</p>
<div class="neu__zeile">
<label class="feld-schild" for="datei">Datei wählen</label>
<input id="datei" type="file" accept=".csv,text/csv,text/plain" />
</div>
<div class="import-vorschau" id="vorschau" hidden></div>
<p class="fehler" id="import-fehler" role="alert" aria-live="polite"></p>
<div class="dialog__knoepfe">
<button type="submit" class="knopf knopf--klein" id="import-los" disabled>Übernehmen</button>
<button type="button" class="abmelden" id="import-zu">Abbrechen</button>
</div>
</form>
</dialog>
<script src="assets/js/wahl.js?v=202609091211"></script>
<script src="assets/js/bereiche.js?v=202609091211"></script>
<script src="assets/js/kopf.js?v=202609091211"></script>
<script src="assets/js/glocke.js?v=202609091211"></script>
<script src="assets/js/leistung.js?v=202609091211"></script>
</body>
</html>