diff --git a/server/public/home.html b/server/public/home.html
index a52f542..fdad267 100644
--- a/server/public/home.html
+++ b/server/public/home.html
@@ -1519,7 +1519,9 @@ function onChatMessage(m){
if(m.from!==ME.id && !isOpen && !isSys) it.unread=(it.unread||0)+1;
}
if(isOpen){
- THREAD.push(m); appendBubble(m);
+ // Dedup by id: the server echoes our own sent message back (multi-tab/device sync), and
+ // sendMessage already appended it optimistically — so skip if it's already in the thread.
+ if(!THREAD.some(x=>x.id===m.id)){ THREAD.push(m); appendBubble(m); }
if(m.from!==ME.id && !isSys){ if(it) it.unread=0; const body=JSON.stringify(kind==='group'?{group:rid}:{with:rid}); try{ fetch('/api/messages/read',{method:'POST',headers:{'Content-Type':'application/json'},body}); }catch(_){} }
} else if(m.from!==ME.id && !isSys && notifOn(kind)){
toast((m.fromName||'New message')+': '+(m.body?(m.body.length>60?m.body.slice(0,60)+'…':m.body):'📎 Attachment'));