fix(mobile): lock in device-independent keyboard (VisualViewport residual), remove debug

Debug confirmed resize:native shrinks the WebView by the keyboard height, so the composer only needs the
residual lift = clientHeight - visualViewport.height (46px on the test device — the prediction-bar sliver).
Tracked live via VisualViewport so it follows the keyboard. Zero per-device constants. Removed debug bar;
reverted Keyboard config to resize:native (which the logic relies on).
Build marker -> 2026-07-18-batch105.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 08:11:39 +05:30
parent 8867d0abfc
commit c775ca2740
2 changed files with 15 additions and 18 deletions
+1 -1
View File
@@ -17,7 +17,7 @@
"offsetForKeyboardInsetBug": false "offsetForKeyboardInsetBug": false
}, },
"Keyboard": { "Keyboard": {
"resize": "none" "resize": "native"
}, },
"SplashScreen": { "SplashScreen": {
"launchShowDuration": 900, "launchShowDuration": 900,
+14 -17
View File
@@ -1112,7 +1112,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-batch104';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD); <script>window.__BUILD='2026-07-18-batch105';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
@@ -1127,23 +1127,20 @@ function twemojify(_el){ /* native emoji: nothing to do */ }</script>
// juggling here — we only tag <html> so CSS can make small native-only tweaks if needed. // juggling here — we only tag <html> so CSS can make small native-only tweaks if needed.
(function(){ try{ var C=window.Capacitor; if(!C||!C.isNativePlatform||!C.isNativePlatform()) return; (function(){ try{ var C=window.Capacitor; if(!C||!C.isNativePlatform||!C.isNativePlatform()) return;
var p=(C.getPlatform&&C.getPlatform())||''; var r=document.documentElement; r.classList.add('native-app'); if(p) r.classList.add('native-'+p); var p=(C.getPlatform&&C.getPlatform())||''; var r=document.documentElement; r.classList.add('native-app'); if(p) r.classList.add('native-'+p);
// Keyboard — DEVICE-INDEPENDENT: the real keyboard overlap is (layout viewport height - visible viewport // Keyboard — DEVICE-INDEPENDENT & self-adjusting. resize:native already shrinks the WebView by the
// height), read from the VisualViewport API. No per-device factor. We prefer that; only if the WebView // keyboard height, so the composer only needs the RESIDUAL lift = (layout viewport height - visible
// doesn't reflect the keyboard in VisualViewport do we fall back to the plugin's reported height. // viewport height) — the sliver still under the keyboard's prediction/accessory bar. Read live from
function kbFromVV(){ var vv=window.visualViewport; if(!vv) return 0; return Math.max(0, Math.round(document.documentElement.clientHeight - vv.height - vv.offsetTop)); } // the VisualViewport API so the composer follows the keyboard smoothly. No per-device constants.
function applyKb(px){ px=Math.max(0,Math.round(px)); r.style.setProperty('--kb',px+'px'); document.body.classList.toggle('kb-open',px>4); if(px>4){ var b=document.getElementById('msgs'); if(b) b.scrollTop=b.scrollHeight; } } if(window.visualViewport){
// TEMP one-time debug readout (remove once logic is locked): shows the raw signals at the top of screen. var vv=window.visualViewport;
var dbg=document.createElement('div'); var lastRaw=null; var applyKb=function(){
dbg.style.cssText='position:fixed;left:0;right:0;top:0;z-index:2147483647;background:rgba(0,0,0,.82);color:#0f0;font:11px/1.35 monospace;padding:3px 6px;text-align:center;pointer-events:none;'; var kb=Math.max(0, Math.round(document.documentElement.clientHeight - vv.height - vv.offsetTop));
function rd(){ var vv=window.visualViewport; dbg.textContent='kbRaw='+(lastRaw==null?'-':Math.round(lastRaw))+' innerH='+window.innerHeight+' clientH='+document.documentElement.clientHeight+' vvH='+(vv?Math.round(vv.height):'-')+' vvTop='+(vv?Math.round(vv.offsetTop):'-')+' dpr='+window.devicePixelRatio+' -> vvKb='+kbFromVV(); } r.style.setProperty('--kb', kb+'px');
if(document.body){ document.body.appendChild(dbg); } else { document.addEventListener('DOMContentLoaded', function(){ document.body.appendChild(dbg); }); } document.body.classList.toggle('kb-open', kb>4);
if(window.visualViewport){ ['resize','scroll'].forEach(function(ev){ window.visualViewport.addEventListener(ev, function(){ var k=kbFromVV(); if(k>20) applyKb(k); rd(); }); }); } var b=document.getElementById('msgs'); if(b) b.scrollTop=b.scrollHeight;
var KB=C.Plugins&&C.Plugins.Keyboard; };
if(KB && KB.addListener){ vv.addEventListener('resize', applyKb); vv.addEventListener('scroll', applyKb);
KB.addListener('keyboardWillShow', function(info){ lastRaw=info&&info.keyboardHeight; rd(); setTimeout(function(){ var k=kbFromVV(); if(k>20){ applyKb(k); } else { var raw=lastRaw||0, dpr=window.devicePixelRatio||1; applyKb(raw>window.innerHeight?raw/dpr:raw); } rd(); }, 60); });
KB.addListener('keyboardWillHide', function(){ lastRaw=0; applyKb(0); rd(); });
} }
rd();
}catch(_){} })(); }catch(_){} })();
</script> </script>
<div class="loading" id="loading"><div class="ld-inner"><img src="/loaders/loader-orbit-dark.svg" alt="" width="72" height="72"><div class="ld-wm">Biz <b>Connect</b></div><span class="ld-sub">Loading…</span></div></div> <div class="loading" id="loading"><div class="ld-inner"><img src="/loaders/loader-orbit-dark.svg" alt="" width="72" height="72"><div class="ld-wm">Biz <b>Connect</b></div><span class="ld-sub">Loading…</span></div></div>