Zwei Nachbesserungen an Post 6: eine Slide mehr und Zeichnungen, die nicht mehr flach aussehen. DIE FUENFTE SLIDE KOMMT VON DER WEBSITE Die App gibt es noch nicht, und die Website sagt das bereits. In components/sections/ChatDemoSection.tsx steht unter der Fahne comingSoon genau dieser Zustand: ein Handy-Mockup, verwischt, und darueber ein Absperrband aus dem Bau -- gedreht um -13 Grad, in accent-yellow F1B729 mit Kanten in 171106, darauf immer wieder Schutzhelm und BALD VERFUEGBAR. Ein Zwischenstand hatte hier einen selbst erfundenen gelben Aufkleber "In Arbeit". Das war eine zweite Bildsprache neben einer vorhandenen. Uebernommen ist jetzt die der Website, samt Winkel, Farben und Wortlaut. Auch der Inhalt des Schirms kommt von dort und nicht aus dem Pitch-Paket: Heizungsprojekt Schulz, "4 Mitglieder · 2 online", die vier Nachrichten aus seedMessages, die Blasenfarben 0D6E6E fuer eigene und FFFFFF mit Rand E9E7E5 fuer fremde, der warme Grund hsl(30 17% 95.5%). Ueberschrift und Fliesstext ebenso: "Der Chat fuer unterwegs wird gerade gebaut." Das Geraet selbst ist eine bewusste Abweichung. Die Website zeichnet nur einen hellen Umriss, border 4px D1D5DB; auf einer Slide, die selbst hell ist, faellt der in sich zusammen und sieht nach Platzhalter aus. Hier steht ein richtiges Geraet: dunkler Korpus, heller Grat, Schirm eingelassen, Insel und Tasten. Die Abweichung betrifft den Rahmen, nicht den Inhalt. Verwischt wird schwaecher als dort -- auf der Website liegt das Handy gross neben Fliesstext und darf unlesbar sein, hier ist es klein, und bei voller Staerke blieb Brei. DIE TIEFE Zwei Anlaeufe waren falsch. Der erste legte graue Flaechen hinter die Elemente: das nimmt den Zeichnungen ihre Durchsichtigkeit und setzt einen Kasten auf den Punktgrund, der dort nicht hingehoert. Wieder raus, die Zeichnungen sind wieder durchsichtig. Der zweite waren bessere Schatten allein. Richtig, aber zu wenig. Die Schatten des Produkts sind in ui/theme.css gestaffelt und nicht einlagig: --shadow-md 0 4px 16px -2px + 0 2px 4px -1px --shadow-lg 0 12px 32px -4px + 0 4px 8px -2px Zwei Lagen, weil eine allein nicht beides kann: die enge dunkle Kante setzt das Element auf die Flaeche, die weite helle hebt es davon ab. In SVG zwei feDropShadow im selben Filter. Vorher stand dort einer mit dy 7. Gebracht hat es der Stapel. post1.html hat ihn als Bildrahmen im CSS, fuer Screenshots; hier ist er in SVG nachgebaut, mit denselben Werten: .sheet-mid rotate(-2.4deg) translate(-2.2%, 3%) scale(.975) .sheet-back rotate( 3.2deg) translate( 3%, 5.5%) scale(.95) Er gibt Tiefe, die ein Schatten allein nicht schafft, und sagt, dass hinter der einen Ansicht weitere liegen. Die hinteren Blaetter sind leer -- Andeutung, keine Kopie. Auf Slide 1 und 2, wo ein Bildschirm zu sehen ist; nicht auf Slide 3, wo ein Ausschnitt einer Unterhaltung steht, und nicht auf Slide 4, die eine Ueberlagerung ist. AUS DER QUELLE NACHGEHOLT Beim gruendlichen Durchgang durch GeneralTeamChatPage.tsx sind drei Dinge aufgefallen, die vorher fehlten oder falsch waren: - Die Datumsmarke "Heute · 30.07.2026" ueber dem ersten Beitrag und die Zeile "Maike Berger schreibt gerade ..." ueber dem Eingabefeld. Beides gibt der Unterhaltung Struktur, ohne eine Flaeche zu behaupten. - Der offene Kanal ist grau hinterlegt mit Rand und shadow-sm, nicht violett. - Die Blase muss so breit sein, wie ihr Anhang es braucht. Vorher richtete sie sich nur nach dem Fliesstext, und "Materialliste pruefen · faellig 31.07.2026" wurde abgeschnitten -- an einer Stelle, an der der Titel die Aussage traegt. ZWEI FALLEN Gedrehte Blaetter ragen aus dem Kasten. Das hinterste steht um 3,2 Grad gedreht und um 3 und 5,5 Prozent verschoben; seine Ecken lagen bis zu 24 px ausserhalb der 92-px-Schutzzone. Der Ueberstand wird jetzt gerechnet: stapelRand() schickt die vier Ecken durch dieselbe Kette wie das Blatt. getBBox() kennt keine Beschneidung. Das Absperrband war erst auf die 175 % der Website gezogen und am Geraet abgeschnitten -- im Bild richtig, in der Messung ein Ueberstand von 146 px, den es nicht gab. Es ist jetzt nur so lang, wie es bei -13 Grad quer ueber das Geraet sein muss. GEPRUEFT Raender 93 / 227 / 110 / 140 / 350, Luft 116 / 79 / 190 / 141 / 77 px, alle Ueberschriften innerhalb der Schutzzone, 214 CSS-Regeln, Konsole ohne Fehler, fuenf PNGs aus dem echten Ausgabeweg bei 2160 x 2700. Downloads blieb leer, der Projektstand unveraendert. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
12 KiB
Post 6 · Team-Chat
Fünfter Beitrag der Produktreihe, Termin Mi 26.08. laut Redaktionsplan (von Mo 24.08. verschoben). Stichwort dort: „Ein Kanal pro Projekt, Foto rein, Aufgabe raus."
Hell, Theme modern, Punktgrund, Text zentriert — dieselbe Anlage wie Post 4
und 5. Datei post6.html, Stand in state/post6.json, erreichbar über das
Projektmenü jeder Seite und über uebersicht.html.
Fünf Slides. DSGVO und deutsche Server hätten einen sechsten Punkt ergeben, dafür gibt es aber keinen Bildschirm. Der steht in der Bildunterschrift, wo eine Aussage ohne Bild hingehört.
1. Die vier Slides
| # | Tag | Überschrift | Was zu sehen ist |
|---|---|---|---|
| 1 | Team-Chat | Büro und Baustelle, endlich im selben Chat. | Kanalliste und Verlauf nebeneinander, mit Filterzeile und Eingabefeld |
| 2 | Die Kanäle | Ein Kanal pro Projekt. | Die Chatliste groß: Projektkanäle, Direktnachrichten, Gruppe |
| 3 | Die Aufgabe | Foto rein, Aufgabe raus. | Baustellenfotos, die Aufgabe daraus, die Bestätigung im Projekt |
| 4 | Das Pluszeichen | Nicht nur Text. | Das Menü hinter dem Plus im Eingabefeld |
| 5 | Bald verfügbar | Der Chat für unterwegs wird gerade gebaut. | Das Handy-Mockup der Website, verwischt, mit Absperrband |
Slide 3 ist der Kern. Der Redaktionsplan verlangt „Foto rein, Aufgabe raus", und genau das liegt im Werkzeug als Abfolge vor: Tobias schickt die Baustellenfotos, darauf folgt eine Nachricht mit angehängter Aufgabe, darauf eine Systemzeile „Aufgabe „Materialliste prüfen" im Projekt erstellt". Die letzte Zeile macht den Unterschied: die Aufgabe liegt danach im Projekt und nicht nur im Verlauf.
Slide 1 trägt den ganzen Chat, weil sie im Profilraster eine Kachel von rund 150 px ist. Dort ist kein Wort lesbar; was ankommt, sind zwei Spalten mit Blasen.
2. Die Bildunterschrift
Im Handwerk läuft die halbe Baustelle über WhatsApp. Das Foto von letzter Woche findet trotzdem keiner wieder.
In HiveGrid hat jedes Projekt seinen eigenen Kanal, mit Projektnummer. Daneben stehen Direktnachrichten und Gruppen, aber eben nicht alles in einem Verlauf.
Ein Foto von der Baustelle geht rein, alle sehen es sofort. Aus „mach das mal“ wird mit einem Klick eine echte Aufgabe: mit Frist, mit Namen, im Projekt.
Dokument, Sprachnachricht, Kontakt, Termin. Was sonst im Chat-Verlauf verschwindet, hängt hier am Projekt.
Kundendaten gehören nicht in eine WhatsApp-Gruppe. Bei HiveGrid liegen sie verschlüsselt auf deutschen Servern.
#handwerk #shk #handwerksbetrieb #digitalisierungimhandwerk #teamchat #baustelle #kommunikation #hivegrid
| Vorgabe | Ist |
|---|---|
| Haken in den ersten 125 Zeichen | erster Absatz, 110 |
| 300 bis 800 Zeichen | 644 |
| 5 bis 10 Hashtags, unten abgesetzt | 8 |
| Suchwörter zusätzlich im Fließtext | Handwerk, Baustelle, Projekt, Kanal, Aufgabe, Chat, Kontakt, Termin |
Der letzte Absatz steht wörtlich so als Merkmal „DSGVO-konform" in
lib/produkt-daten.ts der Website. „Aus „mach das mal" wird mit einem Klick
eine echte Aufgabe" ebenfalls, als Merkmal „Aufgaben-Integration".
3. Woher die Angaben kommen
Quelle ist components/general/GeneralTeamChatPage.tsx aus dem Pitch-Paket
(fertig bombo · Kundentermin 30.07.2026).
| Was | Woher |
|---|---|
| Alle acht Kanäle: Titel, Vorschau, Zeit, Zuordnung, Nadel, Ungelesen | CHATS, wörtlich |
| Der Verlauf: Autoren, Texte, Zeiten, Anhänge | INITIAL_MESSAGES.waermepumpe, wörtlich |
| Aufbau einer Listenzeile (drei Zeilen) | die Zeilen 170 bis 172 der Quelle |
| Aufbau einer Nachricht, Anhangkarte, Systemzeile | MessageBubble und AttachmentCard |
| Sinnbild je Kanalsorte (Koffer, Personen, Kürzel) | ChatAvatar |
| Kopfzeile „P-2026-0042 · 8 Mitglieder · aktiv" | ChatHeader |
| Filterzeile Alle / Projekte / Direkt / Ungelesen | der Typ ChatFilter |
| Das Menü hinter dem Plus, samt Trenner | Composer |
| Eingabefeld „Nachricht schreiben …" | derselbe Ort |
| Farben, Ränder, Plättchentöne | ui/theme.css |
| Alle Symbole | @subframe/core, Pfade aus dem Paket kopiert |
| „Büro und Baustelle, endlich im selben Chat." | Überschrift in lib/produkt-daten.ts |
Vom Chat gibt es nur eine Fassung. Anders als bei der Pipeline, die mit
Board, Liste, Detail und Schablone vier eigene SHK-Bildschirme hat, liegt der
Chat allein unter /allgemein/team-chat. Die Inhalte darin sind trotzdem aus
dem Gewerk: Wärmepumpe Müller, Montagetermin, Baustellenfotos.
4. Wo bewusst abgewichen wird
Slide 1 zeigt drei von sechs Nachrichten, Slide 2 fünf von acht Kanälen. Was nicht ins Bild passt, fällt weg; ein Chat und eine Liste scrollen beide. Slide 2 behält alle drei Sorten: Projektkanal, Direktnachricht, Gruppe.
Die Bildergalerie ist eine Anhangkarte, kein Bild. Im Werkzeug zeigt
GalleryPreview zwei Fotos, geladen von Unsplash. Fremde Fotos in einen
Beitrag zu setzen, der von der eigenen Baustelle spricht, wäre eine Behauptung
über Material, das wir nicht haben. Stattdessen steht dort die Karte
„2 Baustellenfotos · Galerie · heute" — der Text ist der der Quelle.
5. Stand der Dinge
| Slides | 5 |
| Format | 1080 × 1350, Ausgabe zweifach: 2160 × 2700 |
| Luft zwischen Text und Zeichnung | 116 / 79 / 190 / 141 / 77 px (Grenze 40) |
| Zeichnung ragt über die Seite | nein, alle innerhalb der 92-px-Schutzzone |
| Breiteste Überschriftzeile | 818 px von 896 |
| Stylesheet vollständig | 214 Regeln, [data-edit] vorhanden |
| Konsole | keine Fehler |
| Ausgabeweg | geprüft, 01-chat-hell.png bis 05-handy-hell.png |
Geprüft wird mit den Hilfsskripten aus dem Sitzungsordner: pruef.mjs misst
über getBBox() die tatsächlich gezeichnete Fläche je Slide und die Breite
jeder Überschriftzeile, zählt die Regeln im Stylesheet und liest die Konsole
mit; schuss.mjs rendert einzelne Slides; export.mjs fährt den echten
Ausgabeweg und fängt /save ab.
5a. Slide 5 kommt von der Website, nicht von mir
Das Gerät ist eine bewusste Abweichung. Die Website zeichnet nur einen
hellen Umriss (border: 4px solid #D1D5DB). Auf einer Slide, die selbst hell
ist, fällt der in sich zusammen und sieht nach Platzhalter aus. Hier steht
deshalb ein richtiges Gerät: dunkler Korpus, heller Grat, Schirm eingelassen,
Tasten an der Seite. Die Abweichung betrifft den Rahmen, nicht den Inhalt. Der
Schirm wird auch schwächer verwischt als dort: auf der Website liegt das Handy
groß neben Fließtext und darf unlesbar sein, hier ist es klein, und bei voller
Stärke blieb nur Brei.
Die App gibt es noch nicht, und die Website sagt das bereits. In
components/sections/ChatDemoSection.tsx steht unter der Fahne comingSoon
genau dieser Zustand: ein Handy-Mockup, das mit blur(3px) saturate(0.92)
verwischt wird, und darüber ein Absperrband aus dem Bau, gedreht um
-13°, in --accent-yellow #F1B729 mit Kanten in #171106, darauf immer
wieder Schutzhelm und BALD VERFÜGBAR.
Ein Zwischenstand hatte hier einen selbst erfundenen gelben Aufkleber „In Arbeit". Das war eine eigene Bildsprache neben einer vorhandenen. Übernommen ist jetzt die der Website, samt Winkel, Farben und Wortlaut.
Auch der Inhalt des Schirms kommt von dort und nicht aus dem Pitch-Paket:
Heizungsprojekt Schulz, „4 Mitglieder · 2 online", die vier Nachrichten aus
seedMessages, die Blasenfarben #0D6E6E für eigene und #FFFFFF mit Rand
#E9E7E5 für fremde, der warme Grund hsl(30 17% 95.5%). Auf der Website
steht dieses Handy für den mobilen Chat, also zeigt es hier dasselbe.
Überschrift und Fließtext sind ebenfalls die der Website: „Der Chat für
unterwegs wird gerade gebaut." und „Die mobile App legen wir gerade auf die
Baustelle. Am Desktop bist du schon voll dabei, die Version fürs Handy folgt
in Kürze." Der Em-Dash der Vorlage ist durch ein Komma ersetzt, rules.md §4.
5b. Woher die Tiefe kommt
Zwei Anläufe waren falsch, bevor es saß.
Erster Versuch: graue Flächen hinter die Elemente. Das nimmt den Zeichnungen ihre Durchsichtigkeit und setzt einen Kasten auf den Punktgrund, der dort nicht hingehört. Wieder raus.
Zweiter Versuch: nur bessere Schatten. Richtig, aber zu wenig. Die
Schatten des Produkts sind in ui/theme.css gestaffelt und nicht
einlagig, und das war tatsächlich ein Fehler im ersten Nachbau:
--shadow-sm 0 1px 2px rgb(0 0 0 / .08)
--shadow-md 0 4px 16px -2px + 0 2px 4px -1px
--shadow-lg 0 12px 32px -4px + 0 4px 8px -2px
Zwei Lagen, weil eine allein nicht beides kann: die enge dunkle Kante setzt
das Element auf die Fläche, die weite helle hebt es davon ab. In SVG zwei
feDropShadow im selben Filter. Vorher stand dort einer mit dy 7 und
stdDeviation 10.
Was es gebracht hat: der Stapel. post1.html hat ihn als Bildrahmen
stapel im CSS, für Screenshots. Hier ist er in SVG nachgebaut, mit genau
denselben Werten, damit die Reihe zusammenpasst:
.sheet-mid rotate(-2.4deg) translate(-2.2%, 3%) scale(.975)
.sheet-back rotate( 3.2deg) translate( 3%, 5.5%) scale(.95)
Er trägt zwei Aufgaben: er gibt Tiefe, die ein Schatten allein nicht schafft, und er sagt, dass hinter der einen Ansicht weitere liegen. Die hinteren Blätter sind deshalb leer — Andeutung, keine Kopie.
Auf Slide 1 und 2 liegt er, weil dort ein Bildschirm zu sehen ist. Auf Slide 3
nicht: ein Ausschnitt aus einer Unterhaltung ist kein Blatt, dort tragen die
Blasen ihre eigene Erhebung. Slide 4 ist eine Überlagerung und trägt
--shadow-overlay.
Dazu zwei Dinge aus der Quelle, die der Unterhaltung Struktur geben, ohne eine
Fläche zu behaupten: die Datumsmarke Heute · 30.07.2026 über dem ersten
Beitrag und die Zeile „Maike Berger schreibt gerade …" über dem
Eingabefeld. Beides steht so in GeneralTeamChatPage.tsx.
Nebenbei richtiggestellt: der offene Kanal ist grau hinterlegt, nicht
violett. Im Original steht dort bg-surface-neutral-primary mit Rand und
shadow-sm.
6. Fünf Fallen aus diesem Bau
Die Listenzeile hat drei Zeilen, nicht zwei. Der erste Anlauf setzte Titel
und Vorschau untereinander und schob Nadel und Ungelesen-Punkt rechts neben
die Vorschau. Dort stießen sie an den Text — und die Zuordnung fehlte ganz.
Im Original steht sie als dritte Zeile: P-2026-0042 links, die Nadel rechts.
Das war nicht nur ein Layoutfehler: der Fließtext behauptete „Jeder Kanal hängt
an seiner Projektnummer", und die Nummer war nirgends zu sehen.
Eine feste Obergrenze für die Blasenbreite hat ein Zitat gekürzt. Für die Titelslide musste sie klein sein, damit die Unterhaltung neben die Liste passt. Auf Slide 3 stand deshalb „Materialliste prüfen · fälli…" statt „… · fällig 31.07.2026". Die Grenze hängt jetzt an der Spalte, in der die Blase steht.
Beinahe hätte ich ein Zitat ohne Grund gekürzt. Die Website-Überschrift
lautet „Büro und Baustelle, endlich im selben Chat." Ich hatte das „endlich"
gestrichen mit der Begründung, die Zeile laufe sonst über die Schutzzone, und
diese Begründung sogar mit einer Zahl versehen, die ich nicht gemessen hatte.
Nachgemessen sind es 818 px von 896 erlaubten: sie passt. Das Wort ist wieder
drin. Dafür gibt es jetzt breite.mjs, das eine Formulierung misst, bevor sie
in die Datei kommt.
Gedrehte Blätter ragen aus dem Kasten. Der Stapel dreht das hinterste
Blatt um 3,2° und verschiebt es um 3 % und 5,5 % der eigenen Größe. Die Ecken
stehen damit über das vordere Blatt hinaus, und die Zeichnung lag bis zu 24 px
außerhalb der 92-px-Schutzzone. Der Überstand wird jetzt gerechnet statt
geschätzt: stapelRand() schickt die vier Ecken durch dieselbe Kette wie das
Blatt und nimmt den größten Wert. stapelKasten() macht den Kasten
entsprechend größer.
getBBox() kennt keine Beschneidung. Das Absperrband war zuerst auf die
175 % der Website gezogen und per clip-path am Gerät abgeschnitten. Im Bild
stimmte das, die Randmessung meldete aber einen Überstand von 146 px, den es
gar nicht gab: getBBox() liefert die Geometrie, nicht das Sichtbare. Das
Band ist jetzt nur so lang, wie es bei -13° quer über das Gerät sein muss,
also B / cos(13°) plus Zugabe. Messung und Bild sagen wieder dasselbe.
Das Prüfskript nahm eine leere Slide auf. schuss.mjs stellte für eine
einzelne Slide ein Fenster von 1400 px. Im durchgehenden Blatt liegt Seite 3
aber bei x = 2160, und captureBeyondViewport fing das nicht zuverlässig ab.
Die Bühne steht jetzt immer auf 7000 px.