fix(ios): status-bar inset via StatusBar plugin, hide nav in open chat, bulletproof h-scroll

Root cause from device screenshots: the iOS Capacitor WebView reports env(safe-area-inset-*)=0,
so CSS padding did nothing and content slid under the Dynamic Island (every page: home/share/
connect/login) and the bottom nav was clipped.
- Use the already-installed StatusBar plugin: setOverlaysWebView(false) pushes content below the
  status bar natively (no rebuild). Applied on all entry pages via a small native-chrome snippet.
- Hard-code the bottom home-indicator inset (--sab:34px on html.native-ios) since env() can't give it.
- #2 keyboard: hide the bottom tab bar inside an open conversation so the composer sits directly on
  the keyboard (was sandwiched between composer and keyboard).
- #3 h-scroll: clip every scroll surface + overflow-wrap:anywhere so long content can't widen the page.
Build marker -> 2026-07-17-batch91.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 17:19:37 +05:30
parent 95e13a707e
commit a609bfd2e0
4 changed files with 59 additions and 11 deletions
+9 -1
View File
@@ -2,8 +2,16 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Biz Connect — Agent Console</title>
<script>
// Native shell: push content below the status bar/notch (the Capacitor WebView reports safe-area insets
// as 0, so the back button/header would otherwise slide under the Dynamic Island).
(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 SB=C.Plugins&&C.Plugins.StatusBar; if(SB){ try{ SB.setOverlaysWebView({overlay:false}); }catch(_){}; try{ SB.setStyle({style:'DARK'}); }catch(_){}; try{ SB.setBackgroundColor({color:'#ffffff'}); }catch(_){} }
}catch(_){} })();
</script>
<meta name="theme-color" content="#1F3B73">
<link rel="icon" href="/favicon.ico?v=3" sizes="any">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png?v=3">
+33 -9
View File
@@ -23,6 +23,11 @@
bottom rail (the "gap"). overscroll-behavior:none pins the page; the rail's own height already
includes the home-indicator inset. */
html,body{height:100%;overscroll-behavior:none;}
/* Safe-area insets. The iOS Capacitor WebView reports env(safe-area-inset-*)=0, so on the native app
we (a) push content below the status bar/notch with StatusBar.setOverlaysWebView(false) — see the
native-chrome script — and (b) hard-code the bottom home-indicator inset here (env() can't give it). */
:root{--sat:env(safe-area-inset-top,0px);--sab:env(safe-area-inset-bottom,0px);}
html.native-ios{--sab:34px;}
body{font-family:'Segoe UI',system-ui,sans-serif;background:var(--bg);color:var(--ink);margin:0;display:flex;flex-direction:column;height:100vh;height:100dvh;overflow:hidden;position:fixed;inset:0;width:100%;}
/* ---- Top bar ---- */
@@ -1020,10 +1025,11 @@
relocated into the chat-list header (see placeHdrRight). The shell is pushed below the
device status bar so no view overlaps it. */
header{display:none;}
/* #3: kill any horizontal drift on phones (long content, 100vw children, safe-area math) — the page
should never pan left/right. */
html,body{overflow-x:hidden;max-width:100vw;}
.shell,.content,.chatcol,.panel,.convo,.convo-msgs,.chatlist{overflow-x:hidden;max-width:100%;}
/* #3: kill any horizontal drift on phones (long content, over-wide children, safe-area math) — the
page must never pan left/right. Clip every scroll surface and let long words/URLs wrap. */
html,body{overflow-x:hidden;}
.shell,.content,.chatcol,.panel,.convo,.convo-msgs,.chatlist,.side-head,.side-search-row,.chat-row{overflow-x:hidden;max-width:100%;min-width:0;}
.chat-main,.msg,.msg-body,.bubble{min-width:0;max-width:100%;overflow-wrap:anywhere;}
/* #1/#5: the top safe-area inset (notch / Dynamic Island) is applied PER SURFACE — the chat-list
column and each content panel — rather than on the whole shell, so nothing hides under the island
and there's no double-padding. Bottom inset is handled by the fixed nav + panel bottom below. */
@@ -1037,8 +1043,8 @@
indicator and drifted with the URL bar. Use border-box + a height that INCLUDES the inset, pin it
with a promoted layer, and use px-unit env() fallbacks (unitless 0 breaks calc() on Safari). */
.rail{position:fixed;left:0;right:0;bottom:0;top:auto;width:auto;box-sizing:border-box;
height:calc(60px + env(safe-area-inset-bottom,0px));
padding:0 .3rem env(safe-area-inset-bottom,0px) .3rem;
height:calc(60px + var(--sab));
padding:0 .3rem var(--sab) .3rem;
flex-direction:row;justify-content:space-around;align-items:center;gap:0;
border-right:none;border-top:1px solid var(--line);box-shadow:0 -3px 14px rgba(20,30,60,.08);
transform:translateZ(0)!important;-webkit-transform:translateZ(0)!important;z-index:1200;background:var(--card);}
@@ -1052,12 +1058,17 @@
/* Meeting room code: always fully visible, full-width, easy to read */
.meet-bar .code{flex:1 1 100%;font-size:.9rem;white-space:normal;word-break:break-word;text-align:center;background:var(--blue-soft);border-radius:8px;padding:.35rem .5rem;}
/* Chat: one pane at a time (list, then the open conversation) */
.chatcol{width:100%;flex:1 1 100%;padding-top:env(safe-area-inset-top,0px);padding-bottom:calc(60px + env(safe-area-inset-bottom,0px));}
.chatcol{width:100%;flex:1 1 100%;padding-top:var(--sat);padding-bottom:calc(60px + var(--sab));}
body.chat-open .chatcol{display:none;}
.content{display:none;}
body.chat-open .content, .shell:not(.is-chat) .content{display:block;}
.shell:not(.is-chat) .chatcol{display:none;}
.content .panel{top:env(safe-area-inset-top,0px);bottom:calc(60px + env(safe-area-inset-bottom,0px));} /* below the notch; clear the bottom nav + home indicator */
.content .panel{top:var(--sat);bottom:calc(60px + var(--sab));} /* below the notch; clear the bottom nav + home indicator */
/* #2 (mobile keyboard): inside an open conversation, hide the bottom tab bar so the composer sits
directly above the keyboard (no nav sandwiched between the text box and the keyboard). The panel
then only needs to clear the home indicator. */
body.chat-open .rail{display:none;}
body.chat-open .content .panel{bottom:var(--sab);}
/* Bigger touch targets */
.chat-row{padding:.7rem .85rem;}
.convo-head{padding:.7rem .85rem;}
@@ -1084,7 +1095,7 @@
</head>
<body>
<script src="/icons.js?v=6"></script>
<script>window.__BUILD='2026-07-17-batch90';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
<script>window.__BUILD='2026-07-17-batch91';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.
//
// We used to run Twemoji over every emoji, which swapped each one for an <img> pulled INDIVIDUALLY from
@@ -1093,6 +1104,19 @@
// ship full-colour emoji, so the native glyphs look right and cost nothing. Kept as a no-op so the
// existing call sites don't have to change.
function twemojify(_el){ /* native emoji: nothing to do */ }</script>
<script>
// Native shell chrome (iOS/Android). The Capacitor WebView reports env(safe-area-inset-*)=0, so content
// slides under the Dynamic Island / notch. We tag <html> (CSS then hard-codes the bottom home-indicator
// inset) and ask the StatusBar plugin to NOT overlay the WebView, which pushes the top of our content
// below the status bar. Runs on every entry page (home/share/connect/index) that includes this snippet.
(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 SB=C.Plugins&&C.Plugins.StatusBar;
if(SB){ try{ SB.setOverlaysWebView({overlay:false}); }catch(_){}; try{ SB.setStyle({style:'DARK'}); }catch(_){}; try{ SB.setBackgroundColor({color:'#ffffff'}); }catch(_){} }
}catch(_){} })();
</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>
<header>
+8
View File
@@ -4,6 +4,14 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Biz Connect</title>
<script>
// Native shell: push content below the status bar/notch (the Capacitor WebView reports safe-area insets
// as 0). Runs before render so headers never sit under the Dynamic Island.
(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 SB=C.Plugins&&C.Plugins.StatusBar; if(SB){ try{ SB.setOverlaysWebView({overlay:false}); }catch(_){}; try{ SB.setStyle({style:'DARK'}); }catch(_){}; try{ SB.setBackgroundColor({color:'#ffffff'}); }catch(_){} }
}catch(_){} })();
</script>
<meta name="theme-color" content="#1F3B73">
<link rel="icon" href="/favicon.ico?v=3" sizes="any">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png?v=3">
+9 -1
View File
@@ -2,8 +2,16 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Biz Connect — Share your screen</title>
<script>
// Native shell: push content below the status bar/notch (the Capacitor WebView reports safe-area insets
// as 0, so the back button/header would otherwise slide under the Dynamic Island).
(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 SB=C.Plugins&&C.Plugins.StatusBar; if(SB){ try{ SB.setOverlaysWebView({overlay:false}); }catch(_){}; try{ SB.setStyle({style:'DARK'}); }catch(_){}; try{ SB.setBackgroundColor({color:'#ffffff'}); }catch(_){} }
}catch(_){} })();
</script>
<meta name="theme-color" content="#1F3B73">
<link rel="icon" href="/favicon.ico?v=3" sizes="any">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png?v=3">