Pages
Contents
Profile – Concept
Status: CONCEPT · Erstellt April 2026 Übergeordnet:
TOOL_chat_roadmap.mdVerwandt:TOOL_chat_concept-peer-sync.mdGeplant ab: v1.8.0
Warum ein eigenes Konzept?
Das Profil ist mehr als ein Login-Name. Ab v1.8.0 kann der User entscheiden wie er sich gegenüber seinen Peers präsentiert – und was davon überhaupt sichtbar ist. Dieses Konzept legt fest welche Felder existieren, was davon gesynct wird, und wie der User die Sichtbarkeit steuert.
Profil-Struktur
| { |
| // Technisch – nie gesynct, nie öffentlich |
| login: String, // interner Identifier, unveränderlich |
| clientId: String, // technische Peer-ID |
| createdAt: Number, // Unix ms – Zeitpunkt der Profil-Erstellung |
| // Sync-Basis |
| updatedAt: Number, // Unix ms – letzte Änderung, Grundlage für Sync-Vergleich |
| // Öffentliche Felder |
| displayName: String|null, // frei wählbarer Anzeigename ≠ Login |
| avatar: String|null, // Base64 Thumbnail |
| bio: String|null, // kurze Beschreibung – max. 500 Zeichen |
| mood: String|null, // Freitext – Motto / Current Mood – max. 250 Zeichen |
| pronouns: String|null, // Freitext – z.B. "he/him", "she/her", "they/them" |
| // UI zeigt Info-Hinweis mit Beispielen |
| // Status |
| status: String, // vom User wählbar: 'away' | 'busy' |
| // vom System gesetzt: 'online' | 'offline' |
| // Sichtbarkeitssteuerung – alle false by default |
| public: { |
| displayName: Boolean, // default: false |
| avatar: Boolean, // default: false |
| bio: Boolean, // default: false |
| mood: Boolean, // default: false |
| pronouns: Boolean, // default: false |
| status: Boolean, // default: false |
| }, |
| // Lokal – nie gesynct |
| preferences: { |
| notifications: Boolean, // Benachrichtigungen ein/aus |
| defaultStatus: String, // 'away' – gesetzt bei Tab-Unfocus + 5min Inaktivität |
| } |
| } |
Status
Prioritäts-Hierarchie
| System (höchste Priorität): |
| offline → immer wenn nicht verbunden – überschreibt alles |
| User (mittlere Priorität): |
| busy → manuell gesetzt – bleibt bis manuell geändert |
| away → manuell gesetzt – bleibt bis manuell geändert |
| System-Automatik (niedrigste Priorität): |
| away → Tab-Unfocus ODER 5min Inaktivität |
| → nur wenn kein User-Status gesetzt |
| online → Tab-Focus + Aktivität |
| → nur wenn kein User-Status gesetzt |
User-gesetzter Status gewinnt immer gegen System-Automatik:
- User setzt
busy→ bleibtbusy, egal ob Tab-Unfocus oder Inaktivität - User setzt nichts → System setzt automatisch
away/online - Verbindung weg → immer
offline, kein Override möglich
Vom System ermittelt – nicht wählbar
online– Peer ist verbunden und aktivoffline– Peer ist nicht verbunden
Vom User wählbar
away– "Bin kurz weg"busy– "Nicht stören"
Der User-Status wird nur angezeigt wenn der Peer online ist.
Ist der Peer offline, zeigt das System immer offline.
System-Automatik (`preferences.defaultStatus`)
| Tab verliert Fokus → Status: 'away' (wenn kein User-Status gesetzt) |
| 5min keine Aktivität → Status: 'away' (wenn kein User-Status gesetzt) |
| Tab bekommt Fokus → Status: 'online' (wenn kein User-Status gesetzt) |
Nicht stören (`busy`)
Wenn status === 'busy':
- Eingehende Notifications werden nicht gefeuert
- Der Peer sieht den Status und weiß dass Nachrichten nicht sofort gelesen werden
Sichtbarkeitssteuerung (`public`)
Jedes öffentliche Feld hat einen eigenen public-Flag. Der ProfileProvider
schaut beim Sync-Collect ausschließlich in public und baut daraus das
Sync-Payload – nur Felder mit true werden an den Peer übertragen.
| // Beispiel: nur displayName und status sind öffentlich |
| public: { |
| displayName: true, |
| avatar: false, |
| bio: false, |
| mood: true, |
| pronouns: false, |
| status: true, |
| } |
Der Peer sieht dann nur displayName, mood und status – alles andere
bleibt lokal.
Was nie gesynct wird
Unabhängig von public-Einstellungen werden folgende Felder niemals gesynct:
login– interner IdentifierclientId– technische Peer-IDcreatedAt– unnötig für den Peerpreferences– rein lokale Einstellungen
Comparator – Konfliktbehandlung
Beim Profile Sync gilt: updatedAt neuer → gewinnt.
| sync:response:profile empfangen → |
| eingehendes updatedAt > lokales updatedAt → übernehmen |
| eingehendes updatedAt < lokales updatedAt → ignorieren |
| eingehendes updatedAt === lokales updatedAt → lokaler Stand gewinnt |
Migration (altes Format)
| // Alt (vor v1.8.0) |
| { login, clientId, createdAt, preferences } |
| // Migration |
| updatedAt: createdAt // beste Annäherung |
| displayName: null |
| avatar: null |
| bio: null |
| mood: null |
| pronouns: null |
| status: 'online' // Default |
| public: { // alle false – User entscheidet bewusst |
| displayName: false, |
| avatar: false, |
| bio: false, |
| mood: false, |
| pronouns: false, |
| status: false |
| } |
| preferences: { |
| notifications: true, // bestehender Wert übernehmen |
| defaultStatus: 'away' // neuer Default |
| } |
Offene Punkte
| Thema | Stand |
|---|---|
| Avatar-Größenlimit (Base64, DataChannel ~256KB chunks) | TBD – relevant ab v1.8.0 |