Files
dogfather-universe/workspace/chat.html
T
DogFatherGitandClaude Opus 5 2d14907d0d Chat: Sprachnachrichten -- und ein stiller Fehler, der jedes Foto betraf
Filipe: "dan will ich auch dass man im chat auch sprachnachrichten und
gifts reinschicken kann. also nur die modis rechte linke hand und
dogfather."

Dieser Commit macht die Sprachnachrichten. Die GIFs kommen als
naechstes -- ich lese "gifts" als GIFs und sage das ausdruecklich,
statt es stillschweigend anzunehmen.

AUFNEHMEN: TIPPEN, NICHT HALTEN
-------------------------------
Gedruecktbleiben ist der bekanntere Weg und der schlechtere: Wer beim
Sprechen verrutscht, verliert die ganze Aufnahme, und mit einer Hand am
Kaffee haelt niemand neunzig Sekunden still. Einmal tippen startet, das
Haekchen schickt, das Kreuz verwirft.

Drei Minuten Hoechstdauer, danach stoppt sie von selbst -- und schickt
NICHT von selbst ab. Wer die Grenze erreicht, hat gerade mitten im Satz
aufgehoert; das ungefragt zu verschicken waere die schlechteste Sekunde
dafuer.

Unter einer Sekunde wird gar nichts geschickt: Das ist ein Versehen,
kein Inhalt.

Die Aufnahmespur wird beim Beenden UND beim Verlassen der Seite
geschlossen. Ein Mikrofon, das offen bleibt, ist ein Vertrauensbruch,
auch wenn niemand zuhoert.

DREI BEHAELTER, WEIL DIE GERAETE DREI LIEFERN
---------------------------------------------
WebM/Opus (Chrome, Android), MP4/AAC (Safari, iPhone), Ogg/Opus
(Firefox). Wer nur einen nimmt, baut eine Funktion, die bei der Haelfte
des Teams stumm bleibt -- und zwar ohne Fehlermeldung.

DER BEHAELTER ALLEIN GENUEGT NICHT: WebM und MP4 tragen genauso gut
Video. Ein Video, das als "Ton" durchginge, landete in einem
Abspielgeraet ohne Bild -- es liefe, man hoerte etwas, und niemand
wuesste, dass die Haelfte fehlt. Die Erkennung sieht deshalb auf die
KENNUNG DER SPUR: Videospur -> abgelehnt, keine Tonspur -> abgelehnt.

Kein MP3: Kein Aufnahmegeraet im Browser erzeugt MP3. Es zuzulassen
hiesse, eine Tuer fuer Musikdateien aufzumachen, nach der niemand
gefragt hat.

"ALSO NUR" IST DER PUNKT
------------------------
Fotos und PDF darf jeder schicken, auch Creator -- das war eine
Entscheidung vom 09.09. und bleibt. Ton nicht. Geprueft wird in der
Route (istTeamDogi), nicht nur im Browser: Ein ausgeblendeter Knopf ist
eine Bitte, abgelehnt wird erst am Server.

Eigene Byte-Grenze fuer Ton (4 MB statt 12). Zwoelf Megabyte sind fuer
ein Foto richtig und fuer eine Sprachnachricht sinnlos -- das waeren
rund fuenfzig Minuten am Stueck.

Die Laenge kommt vom Absender und ist damit eine BEHAUPTUNG, keine
Messung; das steht so an der Spalte. Sie dient nur der Vorschau, bis
das Abspielgeraet die wahre Laenge kennt. Was schuetzt, sind die Bytes.

"FOTO"/"PDF" STAND AN VIER STELLEN
----------------------------------
Dieselbe Aufzaehlung in Gespraechsliste, Zitat, angehefteter Nachricht
und Benachrichtigung -- jede mit ihrem eigenen Fragezeichen-Doppelpunkt.
Mit der Sprachnachricht waeren es vier Aenderungen gewesen, und die
vierte ist die, die man vergisst. Jetzt anhangWort() an einer Stelle.

DER FUND: JEDES FOTO MELDETE "KEINE VERBINDUNG"
-----------------------------------------------
Beim Anfassen derselben Funktion aufgefallen: In `anhangSchicken` stand
seit dem 19.09. `const { nachricht } = e.daten;` -- ein `e`, das es
dort nicht gibt (gemeint war `ergebnis`). Die Zeile wirft, der `catch`
daneben faengt, und der Absender liest:

    "Keine Verbindung -- der Anhang wurde nicht geschickt."

Das Foto war aber da. Es kam Sekunden spaeter ueber den Ereignisstrom
in den Verlauf, mit einer roten Meldung darueber, die das Gegenteil
behauptet -- und der getippte Begleitsatz blieb im Feld stehen, also
schickt man es noch einmal.

WARUM DAS VIER TAGE UNBEMERKT BLIEB, und das ist der lehrreiche Teil:
pruef-chat-anhaenge war die ganze Zeit gruen. Sie schickt mit `fetch`
an die Route -- der bequeme Weg, und er prueft den Server gruendlich.
Sie prueft aber nicht den Weg, den ein Mensch geht. Der Fehler lag
hinter der Bueroklammer, und dort hat nie jemand hingesehen. Dazu
fuehlt er sich wie ein Netzproblem an -- und Netzprobleme sucht niemand
im Programm.

Jetzt geht ein Block durch das Dateifeld selbst und sieht auf das, was
der Mensch danach sieht: steht dort eine Meldung, und ist das Feld
leer? Gegenprobe gefahren -- mit dem alten Fehler wieder eingesetzt
werden genau diese zwei Zeilen rot, mit dem Wortlaut von oben.

Uebernommen wird eine frisch geschickte Nachricht jetzt an EINER
Stelle (nachrichtUebernehmen), fuer Anhang und Sprachnachricht. Zwei
Fassungen waeren zwei Gelegenheiten fuer denselben Fehler.

AUGENSCHONEND
-------------
Kein Blinkpunkt waehrend der Aufnahme -- der ist genau das, was die
Hausregel ausschliesst. Der Punkt atmet langsam (1,8 s, 45 bis 100 %)
und steht bei prefers-reduced-motion ganz still; die laufende Zeit
daneben traegt die Auskunft ohnehin.

Die Farben der Sprachnachricht-Blase kommen aus `--blase-leise`, der
fuer DIESE Blase ausgerechneten leisen Schrift. Eine feste Farbe waere
dieselbe Wette, die am 22.09. beim Loeschknopf 1,91:1 auf Babyblau
ergeben hat.

GEPRUEFT
--------
pruef-chat-anhaenge: 86 Pruefungen, 0 Fehler (vorher 60).
Aufgenommen wird mit MediaRecorder im echten Browser ueber den echten
Knopf, mit Chromiums erfundenem Mikrofon. Damit prueft der Abschnitt
nebenbei das, was keine gebastelte Datei pruefen koennte: ob die
Erkennung im Server versteht, was ein Browser wirklich erzeugt
(gemessen: 35 829 Bytes audio/webm, 2743 ms).

  * DogFather sieht den Mikrofonknopf, der Scout nicht
  * der Scout schickt dieselbe Aufnahme an der Oberflaeche vorbei:
    403, und nichts davon ist gespeichert
  * ein Video im Ton-Behaelter: 415
  * Laenge, Vorlesewort, Bedienbarkeit, preload=metadata, Breite
  * "Sprachnachricht" steht in der Gespraechsliste statt einer leeren
    Zeile

pruef-chat, pruef-chat-optik: unveraendert gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-23 01:51:56 +02:00

485 lines
25 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>Chat · 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?v=202609230151" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609230151" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609230151" />
<link rel="stylesheet" href="assets/css/start.css?v=202609230151" />
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
Dialog zu sehen bekam. -->
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609230151" />
<link rel="stylesheet" href="assets/css/chat.css?v=202609230151" />
<link rel="stylesheet" href="assets/css/module.css?v=202609230151" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609230151" />
</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> · Chat</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 chat-seite">
<!-- DIE GANZE SEITE IST EINE FLÄCHE, kein Dokument zum Scrollen.
Ein Chat ist kein Text, den man von oben nach unten liest --
man springt zwischen Gesprächen, und das Eingabefeld muss
immer erreichbar sein, ohne zu suchen. Deshalb: feste Höhe,
und gescrollt wird nur INNERHALB der beiden Spalten.
data-ansicht steuert am Handy, welche Spalte zu sehen ist:
"liste" oder "verlauf". Am Rechner stehen beide nebeneinander
und das Merkmal bewirkt nichts. -->
<div class="chat" id="chat" data-ansicht="liste">
<!-- ---------- Links: die Gespräche ---------- -->
<section class="chat__spalte chat__spalte--liste" aria-labelledby="liste-titel">
<div class="chat__kopf">
<h1 class="chat__titel" id="liste-titel">Gespräche</h1>
<!-- MEINE KACHEL (20.09.2026).
Filipe: "jeder account soll seine kachel personalisieren
koennen."
Der Knopf steht LINKS bei den Gespraechen und nicht rechts
im Verlauf: Es ist eine Einstellung ueber MICH, nicht ueber
dieses eine Gespraech. Rechts stehen ausserdem schon fuenf
Knoepfe, und ein sechster hat dort am Handy keinen Platz
mehr -- das war am 19.09. schon einmal der Fehler. -->
<button type="button" class="chat__kachelknopf" id="kachel-oeffnen"
aria-label="Meine Kachelfarbe wählen"
title="Meine Kachelfarbe">
<span class="chat__kachelknopf-probe" aria-hidden="true"></span>
</button>
<button type="button" class="knopf knopf--klein" id="neu-oeffnen">Neu</button>
</div>
<!-- Die Auswahl. Als <dialog>, wie die anderen Nachfragen im
Haus -- ein eigenes Aufklapp-Menue waere ein zweites Muster
fuer dieselbe Sache. -->
<dialog class="nachfrage chat-kachelwahl" id="kachel-wahl">
<h2 class="nachfrage__titel">Deine Kachel</h2>
<p class="nachfrage__text">
So sehen deine Nachrichten für alle anderen aus. Du hast schon
eine Farbe – hier suchst du dir eine andere aus. Alle Töne
leuchten gleich stark, damit die Schrift überall gleich gut
lesbar bleibt.
</p>
<div class="chat-kachelwahl__gitter" id="kachel-gitter" role="radiogroup"
aria-label="Kachelfarbe"></div>
<div class="nachfrage__knoepfe">
<button type="button" class="abmelden" id="kachel-zu">Fertig</button>
</div>
</dialog>
<!-- Suchen erst, wenn es etwas zu suchen gibt (chat.js blendet
es ab acht Gesprächen ein). Ein Suchfeld über drei Einträgen
ist Zierde. -->
<div class="chat__suche" id="suche-zeile" hidden>
<input type="search" id="suche" placeholder="Name oder Gruppe …"
aria-label="Gespräche durchsuchen" autocomplete="off" />
</div>
<ul class="chat__liste" id="raeume" aria-live="polite"></ul>
<!-- ---------- Fundstellen in Nachrichten (09.09.2026, Punkt 15)
Das Suchfeld darueber durchsuchte bisher nur die NAMEN der
Gespraeche. Das ist die kleinere Haelfte: Was man wirklich
sucht, ist ein Satz, den jemand vor drei Wochen geschrieben
hat.
Die Fundstellen stehen UNTER der Gespraechsliste und nicht
an ihrer Stelle -- beides ist eine Antwort auf dieselbe
Eingabe, und wer nach "Patrick" sucht, will vielleicht das
Gespraech und vielleicht die Nachricht. Ein Umschalter
zwischen zwei Ansichten haette ihn zwingen wollen, das
vorher zu wissen. -->
<div class="chat-funde" id="funde" hidden>
<p class="chat-funde__schild" id="funde-schild">In Nachrichten</p>
<ul class="chat-funde__liste" id="funde-liste"></ul>
</div>
<p class="chat__leer" id="liste-leer" hidden>
Noch keine Gespräche. Mit <strong>Neu</strong> schreibst du der
ersten Person.
</p>
</section>
<!-- ---------- Rechts: der Verlauf ---------- -->
<section class="chat__spalte chat__spalte--verlauf" aria-labelledby="verlauf-titel">
<!-- Der Kopf trägt am Handy den Zurück-Weg zur Liste. -->
<div class="chat__kopf chat__kopf--verlauf">
<button type="button" class="chat__zurueck" id="zur-liste" aria-label="Zurück zu den Gesprächen">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14.5 5.5 8 12l6.5 6.5" /></svg>
</button>
<div class="chat__kopf-text">
<h2 class="chat__titel" id="verlauf-titel">Wähle ein Gespräch</h2>
<p class="chat__unter" id="verlauf-unter"></p>
</div>
<!-- GESPRÄCH WEGRÄUMEN (09.09.2026).
Filipe: "man muss die chats auch gelöscht bekommen!!!"
Es verschwindet nur bei DIR -- das Gegenüber behält seinen
Verlauf. Der Knopf steht erst da, wenn auch ein Gespräch
offen ist; sonst wäre er ein Angebot ohne Gegenstand. -->
<!-- WER IST IM KANAL (10.09.2026, Kapitel 7.2).
Steht nur in einem Kanal da und nur bei der Teamleitung --
der Server entscheidet das, nicht diese Datei. -->
<!-- EIN BEHAELTER STATT DREIMAL "margin-left: auto" (11.09.2026).
Jeder Knopf schob sich vorher selbst nach rechts. Bei zwei
sichtbaren teilen sich zwei auto-Raender den freien Platz
und reissen sie auseinander -- und welche sichtbar sind,
entscheidet der Server, nicht diese Datei. Der Behaelter
schiebt einmal, die Knoepfe stehen beieinander, egal wie
viele es sind. -->
<div class="chat__kopf-knoepfe">
<button type="button" class="chat__weg chat__leute" id="kanal-leute" hidden
aria-label="Wer ist in diesem Kanal?"
title="Wer ist in diesem Kanal?">
<svg viewBox="0 0 24 24" aria-hidden="true">
<circle cx="9" cy="8.5" r="3" />
<path d="M3.5 19c0-3 2.5-5 5.5-5s5.5 2 5.5 5" />
<path d="M16 6.2a3 3 0 0 1 0 5.6M17.5 14.4c2 .7 3.3 2.4 3.3 4.6" />
</svg>
</button>
<!-- AUFLOESEN — FUER ALLE (11.09.2026).
Filipe: "die person die ihn oeffnet soll auch das recht
haben das zu loeschen und so dass es dan fuer jeden
geloescht ist. aber nur die person die es gruendet."
Bewusst ein ANDERES Zeichen als der Knopf daneben und eine
eigene Warnfarbe: Zwei gleich aussehende Papierkoerbe
nebeneinander, von denen einer nur bei mir und einer bei
allen loescht, waeren eine Falle. Der Server entscheidet,
ob er ueberhaupt dasteht. -->
<button type="button" class="chat__weg chat__weg--ganz" id="raum-aufloesen" hidden
aria-label="Diese Gruppe für alle auflösen"
title="Für alle auflösen – endgültig">
<svg viewBox="0 0 24 24" aria-hidden="true">
<circle cx="12" cy="12" r="8.2" />
<path d="M7.2 7.2 16.8 16.8" />
</svg>
</button>
<button type="button" class="chat__weg" id="gespraech-weg" hidden
aria-label="Dieses Gespräch wegräumen"
title="Gespräch wegräumen – nur bei dir">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M5 7h14M10 7V5h4v2M6.5 7l.8 12h9.4l.8-12M10 10.5v5.5M14 10.5v5.5" />
</svg>
</button>
</div>
</div>
<!-- aria-live="polite": Ein Vorleseprogramm sagt neue Nachrichten
an, ohne den Nutzer mitten im Satz zu unterbrechen. -->
<!-- ZUM NEUESTEN (09.09.2026). Vorher sprang der Verlauf bei jeder
neuen Nachricht ans Ende -- wer weiter oben nachlas, wurde
mitten im Satz weggerissen. Jetzt bleibt er stehen, und hier
steht, wie viel unten wartet. -->
<button type="button" class="chat__zum-neuesten" id="zum-neuesten" hidden></button>
<!-- HINEINZIEHEN (09.09.2026). Erscheint nur, solange wirklich
etwas über dem Fenster hängt. Eine dauerhaft sichtbare
Ablagefläche wäre jeden Tag im Weg für den Fall, dass man
sie vielleicht braucht. -->
<div class="chat__ablage" id="ablage" hidden aria-hidden="true">
<span>Loslassen – Foto oder PDF schicken</span>
</div>
<!-- ANGEHEFTETE ANKÜNDIGUNGEN (10.09.2026, Kapitel 7.2).
Sie stehen ÜBER dem Verlauf und rollen nicht mit: Eine
Ankündigung, die man erst suchen muss, ist keine. Leer bleibt
der Kasten verborgen – ein leerer Rahmen wäre jeden Tag im
Weg für den Fall, dass irgendwann etwas darin steht. -->
<div class="chat-pins" id="pins" hidden aria-label="Angeheftet"></div>
<div class="chat__verlauf" id="verlauf" role="log" aria-live="polite" aria-relevant="additions">
<p class="chat__leer" id="verlauf-leer">
Links ein Gespräch auswählen – oder mit <strong>Neu</strong> eines anfangen.
</p>
</div>
<!-- Das Eingabefeld. Ein <form>, damit Enter abschickt, ohne
dass dafür Code nötig wäre. -->
<!-- DIE ANTWORTLEISTE (09.09.2026, Punkt 15).
Sie steht UEBER dem Eingabefeld und innerhalb desselben
<form>: So sieht man beim Tippen, worauf man antwortet, und
kann es mit einem Griff wieder loswerden. Eine Leiste
ausserhalb waere beim Tippen ausserhalb des Blickfelds.
`hidden` als Vorgabe -- sie erscheint erst, wenn wirklich
auf etwas geantwortet wird. Ein leerer Kasten ueber dem
Eingabefeld waere jeden Tag im Weg fuer den Fall, dass man
ihn vielleicht braucht. -->
<!-- ==================================================================
DIE NACHTRUHE DES TREFFS (19.09.2026)
Filipe: "nach mitternacht bis morgens 6 uhr keiner schreiben
kann damit auch die privatsphaere beruecksichtig wird ueber
nacht."
SIE STEHT UEBER DEM EINGABEFELD, nicht darunter und nicht in
einer Fusszeile: Wer schreiben will, sieht zuerst, ob er es
kann. Ein Hinweis unter dem Feld liest man erst, nachdem man
getippt hat -- und dann ist die Arbeit schon gemacht.
ZWEI ZUSTAENDE, und der zweite ist der wichtigere:
zu Der Chat schlaeft. Das Feld ist aus.
gleich Es sind noch weniger als 15 Minuten. Das Feld
bleibt an, aber man weiss Bescheid. Ohne diese
Vorwarnung waere die Nachtruhe ein Abbruch mitten
im Satz.
=============================================================== -->
<p class="chat__nachtruhe" id="nachtruhe" hidden role="status" aria-live="polite">
<span class="chat__nachtruhe-zeichen" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M20 14.5A8.5 8.5 0 0 1 9.5 4 8.5 8.5 0 1 0 20 14.5z"/></svg>
</span>
<span class="chat__nachtruhe-text" id="nachtruhe-text"></span>
</p>
<!-- DER HOCHLADE-BALKEN (19.09.2026). Er steht UEBER der
Schreibleiste, nicht darunter: Am Handy sitzt die Leiste
direkt ueber der Tastatur, und alles darunter waere
verdeckt -- also genau dann unsichtbar, wenn man ihn
braucht. -->
<p class="hochlade" id="anhang-fortschritt" hidden></p>
<!-- DIE AUFNAHME LÄUFT (23.09.2026)
Über der Schreibzeile und nicht darunter -- aus demselben
Grund wie der Hochladebalken darüber: Am Handy sitzt die
Leiste direkt über der Tastatur, alles darunter wäre
verdeckt.
DIE ZEIT STEHT DA, GROß. Ohne sie weiß niemand, ob noch
aufgenommen wird — und man redet in eine Aufnahme, die
längst steht, oder hört auf, während sie läuft.
`aria-live` sagt sie auch dem vor, der nicht hinsieht;
bewusst nur alle fünf Sekunden (siehe chat.js), sonst
spricht das Vorleseprogramm im Sekundentakt. -->
<div class="tonleiste" id="tonleiste" hidden role="group"
aria-label="Sprachnachricht aufnehmen">
<span class="tonleiste__punkt" aria-hidden="true"></span>
<span class="tonleiste__zeit" id="ton-zeit">0:00</span>
<span class="tonleiste__wort" id="ton-wort" aria-live="polite">nimmt auf …</span>
<button type="button" class="tonleiste__weg" id="ton-weg"
aria-label="Aufnahme verwerfen" title="Verwerfen">&#10005;</button>
<button type="button" class="tonleiste__senden" id="ton-senden"
aria-label="Sprachnachricht abschicken" title="Abschicken">&#10003;</button>
</div>
<form class="chat__eingabe" id="schreiben" hidden>
<div class="antwort-leiste" id="antwort-leiste" hidden>
<span class="antwort-leiste__strich" aria-hidden="true"></span>
<span class="antwort-leiste__inhalt">
<span class="antwort-leiste__schild">Antwort an</span>
<span class="antwort-leiste__von" id="antwort-von"></span>
<span class="antwort-leiste__text" id="antwort-text"></span>
</span>
<button type="button" class="antwort-leiste__weg" id="antwort-weg"
aria-label="Antwort verwerfen">&#10005;</button>
</div>
<label class="chat__eingabe-schild" for="text">Nachricht</label>
<!-- KURZER PLATZHALTER. Hier stand der Hinweis "Enter schickt,
Umschalt+Enter macht eine neue Zeile" -- zwei Fehler auf
einmal: Am Handy brach er auf zwei Zeilen um und schob das
Feld halb unter den Bildschirmrand, UND er stimmte dort
nicht: Auf Geräten ohne Tastatur schickt Enter absichtlich
nicht, sonst käme man nie zu einer zweiten Zeile.
Die Tastenbelegung steht jetzt im title -- sie ist ein
Hinweis für Wiederkehrer, keine Aufforderung. -->
<!-- ANHANG (09.09.2026): "pdfs und fotos".
Das Feld selbst bleibt unsichtbar -- ein Datei-Auswahlfeld
sieht in jedem Browser anders aus und lässt sich nicht
gestalten. Bedient wird es über die Büroklammer daneben. -->
<input type="file" id="anhang-feld" class="chat__anhang-feld"
accept="image/png,image/jpeg,image/webp,image/gif,application/pdf"
tabindex="-1" aria-hidden="true" />
<button type="button" class="chat__anhang" id="anhang-auf"
aria-label="Foto oder PDF anhängen" title="Foto oder PDF anhängen">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M20 11.5 12.3 19.2a4.4 4.4 0 0 1-6.2-6.2l7.9-7.9a2.9 2.9 0 0 1 4.1 4.1l-7.9 7.9a1.5 1.5 0 0 1-2.1-2.1l7.2-7.2" />
</svg>
</button>
<!-- SPRACHNACHRICHT (23.09.2026)
Filipe: „dan will ich auch dass man im chat auch
sprachnachrichten und gifts reinschicken kann. also nur die
modis rechte linke hand und dogfather."
ER STEHT VON HAUS AUS AUF `hidden`. Ob er erscheint, sagt
der Server mit `darf_ton` beim Öffnen eines Gesprächs --
hier steht keine Rolle. Ein Knopf, der da ist und dann
abgelehnt wird, ist schlimmer als keiner.
TIPPEN, NICHT HALTEN. Gedrückthalten ist der bekanntere
Weg, aber der schlechtere: Wer beim Sprechen den Finger
verrutscht, verliert die ganze Aufnahme, und mit einer
Hand am Lenker, am Kind oder am Kaffee hält niemand
neunzig Sekunden still. Einmal tippen startet, der
Häkchen-Knopf schickt, das Kreuz verwirft. -->
<button type="button" class="chat__ton" id="ton-auf" hidden
aria-label="Sprachnachricht aufnehmen" title="Sprachnachricht aufnehmen">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 3.5a2.6 2.6 0 0 1 2.6 2.6v5.4a2.6 2.6 0 0 1-5.2 0V6.1A2.6 2.6 0 0 1 12 3.5Z" />
<path d="M6.4 11a5.6 5.6 0 0 0 11.2 0M12 16.6V20M9.2 20h5.6" />
</svg>
</button>
<textarea id="text" rows="1" maxlength="4000"
placeholder="Nachricht schreiben …"
title="Enter schickt ab, Umschalt+Enter macht eine neue Zeile, @ erwähnt jemanden"></textarea>
<!-- EMOJI-AUSWAHL (09.09.2026). Die Bildschirmtastatur am Handy
hat sie ohnehin -- am Rechner nicht, und dort schreibt die
Betreuung. Dreissig feste Zeichen statt einer nachgeladenen
Tafel: Das deckt den Alltag ab und laedt nichts. -->
<button type="button" class="chat__emoji" id="emoji-auf"
aria-expanded="false" aria-controls="emoji-tafel"
aria-label="Emoji einfügen" title="Emoji einfügen">☺</button>
<div class="emoji-tafel" id="emoji-tafel" role="group"
aria-label="Emoji auswählen" hidden></div>
<!-- WER IST GEMEINT? (20.09.2026)
Erscheint beim Tippen von "@" und zeigt die Leute DIESES
Gesprächs. Sie steht hier im Formular und nicht am
Seitenende, weil sie sich über der Schreibzeile öffnet --
ein absolut gesetzter Kasten braucht seinen Bezugspunkt in
der Nähe, sonst wandert er bei jedem Umbruch woanders hin.
`role=listbox` und `aria-activedescendant` am Textfeld:
Ein Vorleseprogramm sagt dann beim Blättern den Namen an,
statt nur "Bearbeitungsfeld". -->
<div class="erwaehn-liste" id="erwaehn-liste" role="listbox"
aria-label="Person erwähnen" hidden></div>
<!-- Erscheint erst, wenn es eng wird -- siehe chat.js. -->
<span class="chat__zeichen" id="zeichen" aria-live="polite" hidden></span>
<button type="submit" class="chat__senden" id="senden" aria-label="Abschicken">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M4 12l16-7-7 16-2.5-6.5z" />
</svg>
</button>
</form>
<p class="fehler" id="fehler" role="alert" aria-live="polite"></p>
</section>
</div>
</main>
<!-- Neues Gespräch. Beides in einem Dialog: Wer eine Person anklickt,
schreibt ihr; wer mehrere anhakt, legt eine Gruppe an. Zwei
getrennte Dialoge hätten die Frage "Einzeln oder Gruppe?" VOR die
eigentliche Absicht gestellt. -->
<dialog class="dialog" id="neu-dialog">
<form method="dialog" id="neu-form" class="neu neu--blank">
<h2 class="dialog__titel" id="neu-titel">Neues Gespräch</h2>
<p class="dialog__unter" id="neu-unter">
Eine Person antippen, um ihr zu schreiben. Mehrere auswählen ergibt
eine Gruppe.
</p>
<!-- ZWEI ARTEN, EIN DIALOG (10.09.2026, Kapitel 7.2).
Ein zweiter Dialog wäre dieselbe Personenwahl ein zweites Mal –
und die zweite Fassung wäre die, in der eine Regel fehlt.
Sichtbar nur, wenn der Server sagt, dass Kanäle erlaubt sind. -->
<div class="neu__art" id="neu-art" role="group" aria-label="Was soll es werden?" hidden>
<button type="button" data-art="gespraech" data-an="ja" aria-pressed="true">Gespräch</button>
<button type="button" data-art="kanal" aria-pressed="false">Kanal</button>
</div>
<!-- WAS IST DER UNTERSCHIED? (11.09.2026)
Filipe: "ich will eine bessere erklärung dafür bitte."
Der Umschalter stand da, und daneben stand ein Satz über das
ANTIPPEN von Personen -- also über den nächsten Schritt, nicht
über die Wahl, die gerade ansteht. Wer die beiden Wörter zum
ersten Mal sieht, erfährt nirgends, was sie bedeuten.
Der Unterschied ist nicht "wenige/viele Leute", sondern WONACH
der Raum benannt ist: ein Gespräch nach den Menschen darin, ein
Kanal nach einem Thema. Daran hängt alles Weitere -- dass es
einen Kanal je Zuständigkeit nur EINMAL gibt, dass sein Name
festliegt, und dass Leute darin wechseln können, ohne dass der
Raum ein anderer wird. Genau das steht hier.
Steht nur da, wenn auch der Umschalter dasteht. -->
<div class="neu__erklaerung" id="neu-erklaerung" hidden>
<p><strong>Gespräch</strong> – du wählst die <em>Leute</em>. Eine Person:
ihr beide. Mehrere: eine Gruppe mit eigenem Namen.</p>
<p><strong>Kanal</strong> – du wählst ein <em>Thema</em>. Eine Zuständigkeit,
ein fester Raum, den es genau einmal gibt. Sein Name kommt aus der
Zuständigkeit, die Teamleitung ist immer dabei, und wer sonst noch
dazugehört, lässt sich später ändern.</p>
</div>
<div class="neu__zeile" id="kanal-zeile" hidden>
<label class="feld-schild" for="kanal-kategorie">Zuständigkeit</label>
<select id="kanal-kategorie"></select>
<p class="neu__hinweis" id="kanal-alle" hidden>
Für jede Zuständigkeit gibt es schon einen Kanal.
</p>
</div>
<div class="chat-wahl" id="partner"></div>
<!-- Erscheint erst ab zwei Ausgewählten -- vorher wäre ein
Gruppenname eine Frage ohne Anlass. -->
<div class="neu__zeile" id="gruppenname-zeile" hidden>
<label class="feld-schild" for="gruppenname">Name der Gruppe</label>
<input id="gruppenname" maxlength="80" placeholder="z. B. Luna &amp; Betreuung" />
</div>
<p class="fehler" id="neu-fehler" role="alert" aria-live="polite"></p>
<div class="dialog__knoepfe">
<button type="submit" class="knopf knopf--klein" id="neu-los" disabled>Schreiben</button>
<button type="button" class="abmelden" id="neu-zu">Abbrechen</button>
</div>
</form>
</dialog>
<script src="assets/js/bereiche.js?v=202609230151"></script>
<!-- TEXTFORM VOR ALLEM ANDEREN (21.09.2026): chat.js und bereich.js
fragen beim Zeichnen nach `window.Textform`. Kaeme sie spaeter,
waere die erste gezeichnete Nachricht ohne Gestaltung -- und das
faellt nur dem auf, der genau hinsieht. KEIN `defer`, damit sie
wirklich vor den anderen dasteht. -->
<script src="assets/js/textform.js?v=202609230151"></script>
<script src="assets/js/meldung.js?v=202609230151" defer></script>
<script src="assets/js/hochladen.js?v=202609230151" defer></script>
<script src="assets/js/nachfrage.js?v=202609230151" defer></script>
<script src="assets/js/wahl.js?v=202609230151" defer></script>
<script src="assets/js/kopf.js?v=202609230151"></script>
<script src="assets/js/glocke.js?v=202609230151"></script>
<!-- ANRUF VOR CHAT (18.09.2026): anruf.js stellt `window.anrufStarten`
bereit, und chat.js baut die Knoepfe nur, wenn es das gibt.
Andersherum waere die Reihenfolge eine stille Abhaengigkeit --
die Knoepfe fehlten, und niemand wuesste warum. -->
<script src="assets/js/anruf.js?v=202609230151"></script>
<script src="assets/js/chat.js?v=202609230151"></script>
<script src="assets/js/installieren.js?v=202609230151" defer></script>
</body>
</html>