fix(chat): restaure le collage des images dans le composer custom CLI (#184)

Le collage d'une image (ex. capture d'écran) échouait en environnement desktop : le presse-papier image n'est souvent pas exposé comme un File synchrone dans clipboardData.files, mais uniquement via l'Async Clipboard API. On ajoute donc un repli sur navigator.clipboard.read() (MIME image/*) quand l'événement de collage ne fournit aucun fichier. Le preventDefault() n'est émis que lorsqu'on a effectivement lu des fichiers issus de l'événement, afin de ne pas bloquer le collage texte quand le repli async ne remonte rien.

- refactor clipboardFiles(event) -> dataTransferFiles(DataTransfer) réutilisable
- dedupeKey robuste pour les images sans nom (mime + taille + contenu base64)
- ReplyChunk::UserPrompt porte désormais les labels d'attachments (DTO + accounting reply_chunk_bytes, skip_serializing_if Vec::is_empty)
- tests ciblés frontend (+219) et backend (chat_bridge/dto/attachments) verts

QA : tests ciblés verts. Réserve : environnement headless, pas de validation manuelle desktop — validation AppImage à confirmer en suivi.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 16:49:28 +02:00
parent d779b24916
commit d9f949c0f2
9 changed files with 339 additions and 25 deletions

View File

@ -145,7 +145,9 @@ impl ChatBridge {
fn reply_chunk_bytes(chunk: &ReplyChunk) -> usize {
match chunk {
ReplyChunk::UserPrompt { text } => text.len(),
ReplyChunk::UserPrompt { text, attachments } => {
text.len() + attachments.iter().map(String::len).sum::<usize>()
}
ReplyChunk::TextDelta { text } => text.len(),
ReplyChunk::ToolActivity { label } => label.len(),
ReplyChunk::Progress { progress } => {