fix(mobile): cap in-bubble image height (kills the stretch balloon) + sharper spike diagnostic

Spike log showed bubbles ballooning to 3576-5684px (an image loading at natural ~2778px height before
constraint). att-img is capped 240 but some image path wasn't — added .convo-msgs img/.bubble img
max-height:340px as a blanket cap so no message can stretch the thread. Enhanced spike report to name
the tall bubble's img (natural size, rendered height, computed max-height) to confirm.
Build marker -> 2026-07-18-batch115.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 17:34:31 +05:30
parent 676612a12b
commit 7f7ff8ba1f
+5 -2
View File
@@ -645,6 +645,9 @@
.composer-row .sendbtn{flex:0 0 auto;align-self:flex-end;width:34px;height:34px;margin:1px;border:none;background:var(--brand);color:var(--ink);cursor:pointer;border-radius:50%;display:grid;place-items:center;} .composer-row .sendbtn{flex:0 0 auto;align-self:flex-end;width:34px;height:34px;margin:1px;border:none;background:var(--brand);color:var(--ink);cursor:pointer;border-radius:50%;display:grid;place-items:center;}
.composer-row .sendbtn:hover{background:var(--brand-d);} .composer-row .sendbtn:hover{background:var(--brand-d);}
.att-img{max-width:240px;max-height:240px;border-radius:8px;display:block;margin:.15rem 0;} .att-img{max-width:240px;max-height:240px;border-radius:8px;display:block;margin:.15rem 0;}
/* Belt-and-suspenders: cap ANY image inside a message bubble so a message can never momentarily balloon
the thread to thousands of px while a large picture loads at its natural size (the "stretch" glitch). */
.convo-msgs img,.bubble img{max-height:340px;max-width:100%;height:auto;}
.att-file{display:inline-flex;align-items:center;gap:.4rem;background:rgba(0,0,0,.06);border:1px solid var(--line);border-radius:8px;padding:.4rem .6rem;color:inherit;text-decoration:none;font-size:.85rem;margin:.15rem 0;max-width:240px;} .att-file{display:inline-flex;align-items:center;gap:.4rem;background:rgba(0,0,0,.06);border:1px solid var(--line);border-radius:8px;padding:.4rem .6rem;color:inherit;text-decoration:none;font-size:.85rem;margin:.15rem 0;max-width:240px;}
.att-file span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} .att-file span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.bubble.mine .att-file{background:rgba(255,255,255,.18);border-color:rgba(255,255,255,.3);} .bubble.mine .att-file{background:rgba(255,255,255,.18);border-color:rgba(255,255,255,.3);}
@@ -1123,7 +1126,7 @@
</head> </head>
<body> <body>
<script src="/icons.js?v=6"></script> <script src="/icons.js?v=6"></script>
<script>window.__BUILD='2026-07-18-batch114';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD); <script>window.__BUILD='2026-07-18-batch115';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
// Emoji are rendered with the OS's own (colour) emoji font — instant, zero network. // Emoji are rendered with the OS's own (colour) emoji font — instant, zero network.
// //
// We used to run Twemoji over every emoji, which swapped each one for an <img> pulled INDIVIDUALLY from // We used to run Twemoji over every emoji, which swapped each one for an <img> pulled INDIVIDUALLY from
@@ -2865,7 +2868,7 @@ async function openConvo(kind,id){
const _pinBox=document.getElementById('msgs'); if(_pinBox){ _pinBox.querySelectorAll('img').forEach((im)=>{ if(!im.complete) im.addEventListener('load', _pinNewest, {once:true}); }); } const _pinBox=document.getElementById('msgs'); if(_pinBox){ _pinBox.querySelectorAll('img').forEach((im)=>{ if(!im.complete) im.addEventListener('load', _pinNewest, {once:true}); }); }
setTimeout(()=>{ try{ const b=document.getElementById('msgs'); if(b && window.bzDbg){ let tall={h:0,c:''}; b.querySelectorAll('.bubble,img,.att-img').forEach(el=>{ const h=el.getBoundingClientRect().height; if(h>tall.h) tall={h:Math.round(h), c:(el.className||el.tagName||'').toString().slice(0,36)}; }); bzDbg('convoOpen',{count:THREAD.length, scrollTop:Math.round(b.scrollTop), scrollH:Math.round(b.scrollHeight), clientH:Math.round(b.clientHeight), atBottom:(b.scrollHeight-b.scrollTop-b.clientHeight)<8, newsep:!!b.querySelector('.new-sep'), tallH:tall.h, tallC:tall.c}); } }catch(_){} }, 3600); setTimeout(()=>{ try{ const b=document.getElementById('msgs'); if(b && window.bzDbg){ let tall={h:0,c:''}; b.querySelectorAll('.bubble,img,.att-img').forEach(el=>{ const h=el.getBoundingClientRect().height; if(h>tall.h) tall={h:Math.round(h), c:(el.className||el.tagName||'').toString().slice(0,36)}; }); bzDbg('convoOpen',{count:THREAD.length, scrollTop:Math.round(b.scrollTop), scrollH:Math.round(b.scrollHeight), clientH:Math.round(b.clientHeight), atBottom:(b.scrollHeight-b.scrollTop-b.clientHeight)<8, newsep:!!b.querySelector('.new-sep'), tallH:tall.h, tallC:tall.c}); } }catch(_){} }, 3600);
// Also watch for a transient scrollHeight balloon (the stretch/jump) and report what's tallest when it spikes. // Also watch for a transient scrollHeight balloon (the stretch/jump) and report what's tallest when it spikes.
if(window.bzDbg){ let _spk=0; const _watch=setInterval(()=>{ const b=document.getElementById('msgs'); if(!(selected&&selected.kind===kind&&selected.id===id)||!b){ clearInterval(_watch); return; } const per=b.scrollHeight/Math.max(1,THREAD.length); if(per>200 && _spk<2){ _spk++; let tall={h:0,c:''}; b.querySelectorAll('.bubble,img').forEach(el=>{ const h=el.getBoundingClientRect().height; if(h>tall.h) tall={h:Math.round(h), c:(el.className||el.tagName||'').toString().slice(0,36), src:(el.getAttribute&&el.getAttribute('src')||'').slice(-24)}; }); bzDbg('spike',{scrollH:Math.round(b.scrollHeight), per:Math.round(per), tallH:tall.h, tallC:tall.c, tallSrc:tall.src}); } }, 200); setTimeout(()=>clearInterval(_watch), 6000); } if(window.bzDbg){ let _spk=0; const _watch=setInterval(()=>{ const b=document.getElementById('msgs'); if(!(selected&&selected.kind===kind&&selected.id===id)||!b){ clearInterval(_watch); return; } const per=b.scrollHeight/Math.max(1,THREAD.length); if(per>200 && _spk<2){ _spk++; let tall={h:0}; b.querySelectorAll('.bubble').forEach(el=>{ const h=el.getBoundingClientRect().height; if(h>tall.h){ const im=el.querySelector('img'); tall={h:Math.round(h), c:(el.className||'').slice(0,24), img: im?((im.className||'')+' nat='+im.naturalWidth+'x'+im.naturalHeight+' rH='+Math.round(im.getBoundingClientRect().height)+' mh='+getComputedStyle(im).maxHeight):'no-img'}; } }); bzDbg('spike',{scrollH:Math.round(b.scrollHeight), per:Math.round(per), tallH:tall.h, tallC:tall.c, tallImg:tall.img}); } }, 200); setTimeout(()=>clearInterval(_watch), 6000); }
} }
// ---------- @mentions (group chat) ---------- // ---------- @mentions (group chat) ----------
let mentionItems=[], mentionIdx=0, mentionStart=-1; let mentionItems=[], mentionIdx=0, mentionStart=-1;