MessageText erkennt http(s)-URLs auf Bilder (jpg/png/gif/webp/bmp/ico)
und rendert sie als <Image> unter dem Text. Markdown-Syntax
 wird durch dasselbe Regex erfasst weil die URL drin ist.
SVGs ausgespart — React Native Image kann SVG nicht ohne Extra-Lib.
Aspect-Ratio wird via Image.getSize ermittelt, gecapped auf 0.5..2.5
damit Panorama-/Streifen-Bilder die Bubble nicht sprengen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Frisch geclonte Repo / git pull nach .gitignore-Aenderungen lassen
USER.md (und andere Config-Files) fehlen — docker compose up failt
dann beim Bind-Mount. init.sh kopiert idempotent alle *.example zu
ihren Originalen wenn die noch nicht existieren.
Nach git clone und git pull empfohlen: bash init.sh
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
USER.md hatte Stefan-spezifische Infos (Gitea-URL hackersoft.de,
OpenCRM, STARFACE, RustDesk Tool-Stack). Hat im Repo nichts zu suchen.
USER.md ist jetzt in .gitignore, USER.md.example als Vorlage
eingecheckt. Lokale USER.md bleibt funktional unangetastet — nur
nicht mehr getrackt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ARIA setzt im Antworttext einen Marker `[FILE: /shared/uploads/aria_xxx.ext]`.
Bridge extrahiert ihn (Marker wird aus dem TTS-Text entfernt) und sendet
ein neues file_from_aria-Event ueber RVS an App + Diagnostic.
Diagnostic:
- Eigene Bubble mit Datei-Icon + Klick-Handler
- PDF/Bild → neuer Browser-Tab via /shared/* HTTP-Route
- Andere → Download via download-Attribut
App:
- Neues FileOpenerModule (Kotlin) — Intent.ACTION_VIEW mit FileProvider,
Android-Picker waehlt App nach MIME-Type
- file_paths.xml erweitert (cache + files + external)
- file_response liefert jetzt mimeType mit
- Klick auf ARIA-Anhang: lokal vorhanden → direkt oeffnen, sonst
file_request mit autoOpen-Flag → bei Empfang persistAttachment + open
Stefan muss noch im aria-core/OpenClaw System-Prompt einen Hinweis
einbauen: "Wenn du dem User eine Datei erstellt hast (Pfad in
/shared/uploads/), haenge am Ende deiner Antwort einmalig
[FILE: /shared/uploads/aria_<name>.<ext>] an. Der Marker wird aus dem
sichtbaren Text entfernt und als Anhang in App und Diagnostic angezeigt."
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Logs zeigen jetzt KEINEN haengenden RNSound-Focus mehr (Library-Version
oder Sound-Lifecycle hat sich geaendert). Der Kick mit AUDIOFOCUS_GAIN
(permanent) sagte Spotify "user hat manuell etwas anderes gestartet" →
Spotify resumed nicht automatisch.
Ohne Kick: unser Focus war AUDIOFOCUS_GAIN_TRANSIENT (USAGE_ASSISTANT) —
beim release bekommt Spotify einen sauberen GAIN nach TRANSIENT-Loss
und resumed automatisch.
Native kickReleaseMedia bleibt fuer den Fall dass es nochmal gebraucht
wird, wird aber nicht mehr gerufen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bisheriges Verhalten: Mute drueckt → stopPlayback. Mute zurueck → noch
eingehende chunks der gleichen Antwort starteten einen neuen Stream und
ARIA redete weiter wo sie war. Funktionierte nur 2x weil dann isFinal
schon kam und keine chunks mehr fluten.
Stefan: "Mund verbieten = Stop, fertig". Neue Antworten sollen normal
spielen.
Fix: _stoppedMessageId-Tracking. Bei Mute=true wird die aktuelle msgId
gemerkt — alle weiteren chunks dieser msgId bleiben silent, auch wenn
Mute zurueckgenommen wird. Reset bei neuer msgId.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stefan: ältere Nachrichten (deren Cache-WAV weg ist) gehen ueber
tts_request neu rendern → kommen als PCM-Stream zurueck → werden ueber
PcmStreamPlayer abgespielt. Beim Mute lief stopPlayback aber ohne den
Spotify-resume-Kick weil hadRnSound=false war (kein currentSound).
Jetzt: kickReleaseMedia immer in stopPlayback rufen — kostet nichts,
deckt PCM- und RNSound-Pfad ab.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Re-Renders / setInterval(loadSettings) triggern setMuted(true) oft
mehrfach hintereinander → jeder weitere stopPlayback rief erneut
kickReleaseMedia, Spotify pausierte+resumte mehrfach (Stefan: "spielt
kurz und pausiert dann wieder").
Fix: stopPlayback returnt sofort wenn nichts mehr aktiv ist.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Logs zeigten: react-native-sound requestet beim Sound.play() einen
EIGENEN AudioFocus mit USAGE_MEDIA, released den aber bei Sound.stop()/
release() NICHT (bekanntes RN-sound-Bug). Spotify sieht den haengenden
Media-Focus → bleibt pausiert.
Workaround: Native-Methode kickReleaseMedia() macht einen request+abandon-
Cycle mit USAGE_MEDIA, das System raeumt damit den Focus-Stack auf und
Spotify bekommt sauberen GAIN-Event. stopPlayback ruft das jetzt nach
Sound.release() wenn vorher ein RNSound aktiv war.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
console.log wird global stummgeschaltet wenn aus — spart adb-logcat-
Speicher wenn alles laeuft. console.warn/error bleiben immer aktiv.
Default an.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vorher: Button checkte nur ob audioPath gesetzt ist — auf eine geloeschte
Cache-Datei hat aber nichts geprueft. playFromPath warntete nur und
returnte stumm. Jetzt wird VOR playFromPath die Existenz geprueft, sonst
geht's ueber tts_request an die Bridge zum Neu-Rendern.
Plus: Logs in Sound.play-Callback und _releaseFocusDeferred fuer den
"Spotify resumed nicht nach Replay"-Bug.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- App-Start raeumt orphane aria_tts_*.wav (>5min) aus dem Cache —
Wiedergaben die durch Anruf/Mute/Barge-In abgebrochen wurden
hinterliessen sonst Files, weil der completion-Callback nicht feuert.
- Neuer Settings-Button "TTS-Cache leeren" mit Live-Groessenanzeige —
parallel zum bestehenden "Update-Cache leeren".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pauseForCall stoppte zwar currentSound + setzte ihn auf null, hat aber
isPlaying=true gelassen. Folge: nach dem Anruf war jeder weitere Play-
Button-Klick wirkungslos, weil playAudio bei isPlaying=true den
_playNext-Pfad ueberspringt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wenn ARIA's Resume-Pfad direkt nach Anruf-Ende den AudioFocus requestet,
kollidiert das mit Spotify's eigenem Auto-Resume. System haengt noch im
IN_CALL-Mode-Uebergang, Spotify sieht "Loss → Loss" und bleibt pausiert
statt kurz zu resumen.
Mit 800ms-Delay: Spotify schafft den Resume-Schritt, dann pausiert ARIA
wieder ordnungsgemaess. Wenn ARIA nichts pending hatte, bleibt Spotify
einfach weiter an.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Claude-Vision-API hat ~5MB Base64-Limit. Stefan's 4MB Foto via
Buroklammer (DocumentPicker) sprengte das, Claude lieferte leere
Antwort zurueck. Galerie-Pfad ging weil react-native-image-picker
schon clientseitig komprimiert.
Bridge resized jetzt JPEG/PNG/WebP/GIF >2MB auf max 1568px lange
Seite (Anthropic-Empfehlung), JPEG q=85. SVG, PDF, ZIP, Office-Docs
bleiben unangetastet — die laufen ueber Tools, nicht Vision.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wenn Claude-Vision das Bild silent ablehnt (z.B. zu gross), kommt
phase=end ohne Crash, aber chat:final ohne text. Bridge ignorierte das
nur mit Warning — App wartete ewig auf Antwort. Jetzt kommt eine
Hinweis-Bubble damit der User weiss dass was schief lief.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OpenClaw legt bei state=error den Text in errorMessage statt error.
Bridge ignorierte das und meldete generisches "[Fehler] Unbekannt" an
App + Diagnostic — der echte Text ("Process exited with code 1" etc)
ging nur in die Container-Logs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
stopPlayback stoppte bisher nur currentSound, nicht resumeSound — wenn
nach einem Anruf der Auto-Resume laeuft und der User Mute drueckt, bleibt
der Resume-Sound weiter spielen.
Plus Logs in setMuted/stopPlayback um zu sehen warum Stefans Mute beim
Replay nicht greift.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Logs zeigten: playFromPath setzt currentPlaybackMsgId='db710ff3-...', 9s
spaeter beim Anruf war captureInterruption msgId=(leer). Ursache:
_firePlaybackStarted setzt currentPlaybackMsgId blind aus pcmMessageId —
das ist beim Play-Button leer.
Jetzt nur noch setzen wenn ein PCM-Stream laeuft. Play-Button und Resume-
Sound setzen ihr Tracking selber im Caller.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Python websockets Default-Limit ist nur 1 MiB. Stefan's 4MB JPEG (5.8MB als
Base64) sprengte das, Bridge-Verbindung wurde silent gedroppt. App sah
nichts, Diagnostic kriegte kein file_saved, ARIA reagierte nicht — Kamera-
Bilder waren klein genug (<1MB) und gingen darum durch.
f5tts/whisper-bridges hatten max_size=50MB schon drin, nur aria_bridge
hatte's an beiden websockets.connect-Stellen vergessen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>