fix: guest pre-join, post-admit lobby, audio device menu, RC keyboard (0.1.15/batch74)
1. Guest pre-join redesigned: brand backdrop, live camera preview, mic/cam toggles
applied on entry, initials avatar, name required, "host may admit you" note.
2. Post-admit bug: the guest stayed on "Waiting for the host…" forever — the lobby
screen had replaced the call UI and meeting-joined never re-rendered it. Now it
rebuilds the call on admission (_inLobby).
3. Audio devices: dropped the standalone headphones button. The MIC now has a ▾ caret
opening one Teams-style menu with Speaker + Microphone sections (radio-selected);
speaker uses setSinkId/LiveKit switchActiveDevice, mic switches the live input.
Mobile gets a speakerphone toggle that prefers a connected BT/headset when off.
4. Remote-control keyboard:
- Injector now maps the PHYSICAL key (KeyboardEvent.code) instead of the character,
so Shift+1 types "!" etc. Character mapping was why typing "performed differently".
- Keys reach the sharer ONLY while control is ENGAGED (window focused AND you clicked
their screen). Minimised/unfocused/chat typing stays local. Esc or clicking away
releases; modifiers are released on disengage so nothing sticks.
- Explicit control icons: viewer gets a Control ON/OFF button (green when engaged) +
an on-screen hint; the SHARER gets a control icon beside mic/chat to allow/stop
access at a glance, synced with the consent dialog and banner.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -226,7 +226,7 @@ function stopCustTranscription(){ crecogActive=false; if(crecog){ try{crecog.sto
|
||||
// after the user explicitly taps "Allow control", and only inside the desktop app. In a plain browser
|
||||
// there's no OS injection possible, so it stays view-only. The user can stop control at any time.
|
||||
let rcDesktop=false, rcAllowed=false, rcPrompted=false;
|
||||
try{ window.addEventListener('message',(e)=>{ if(e.origin!==location.origin) return; const d=e.data||{}; if(d.type==='rc-pong') rcDesktop=!!d.desktop; }); }catch(_){}
|
||||
try{ window.addEventListener('message',(e)=>{ if(e.origin!==location.origin) return; const d=e.data||{}; if(d.type==='rc-pong'){ rcDesktop=!!d.desktop; try{ updateRcBtn(); }catch(_){} } }); }catch(_){}
|
||||
try{ if(window.parent && window.parent!==window) window.parent.postMessage({type:'rc-ping'}, location.origin); }catch(_){}
|
||||
function rcPost(msg){ try{ if(window.parent && window.parent!==window) window.parent.postMessage(msg, location.origin); }catch(_){} }
|
||||
function rcOnInput(data){
|
||||
@@ -241,9 +241,22 @@ function showControlConsent(){
|
||||
const el=document.createElement('div'); el.id='rcConsent'; el.className='rc-consent';
|
||||
el.innerHTML='<div class="rc-card"><div class="rc-h">Allow remote control?</div><p>Your agent is asking to control your mouse & keyboard to help you. You stay in charge — stop anytime.</p><div class="rc-btns"><button id="rcDeny" class="rc-deny">Not now</button><button id="rcAllow" class="rc-allow">Allow control</button></div></div>';
|
||||
document.body.appendChild(el);
|
||||
el.querySelector('#rcAllow').onclick=()=>{ rcAllowed=true; rcPost({type:'rc-arm', on:true}); el.remove(); showControlBanner(); };
|
||||
el.querySelector('#rcAllow').onclick=()=>{ el.remove(); rcGrantControl(); };
|
||||
el.querySelector('#rcDeny').onclick=()=>{ el.remove(); };
|
||||
}
|
||||
// Grant / revoke control. Both the consent dialog and the bar's control icon route through here, so the
|
||||
// icon, banner and the shell's injection gate always agree.
|
||||
function rcGrantControl(){
|
||||
if(!rcDesktop){ setStatus('Remote control needs the Biz Connect desktop app. Your agent can still see your screen.'); return; }
|
||||
rcAllowed=true; rcPost({type:'rc-arm', on:true}); showControlBanner(); updateRcBtn();
|
||||
}
|
||||
function rcStopControl(){ rcAllowed=false; rcPrompted=false; rcPost({type:'rc-arm', on:false}); const b=document.getElementById('rcBanner'); if(b) b.remove(); updateRcBtn(); }
|
||||
function updateRcBtn(){
|
||||
const b=document.getElementById('rcBtn'); if(!b) return;
|
||||
b.style.background=rcAllowed?'#16a34a':'#6b7280';
|
||||
b.style.opacity=rcDesktop?'1':'.55';
|
||||
b.title=!rcDesktop ? 'Remote control needs the desktop app' : (rcAllowed?'Remote control is ON — tap to stop':'Remote control is OFF — tap to allow');
|
||||
}
|
||||
function showControlBanner(){
|
||||
if(document.getElementById('rcBanner')) return;
|
||||
const el=document.createElement('div'); el.id='rcBanner'; el.className='rc-banner';
|
||||
@@ -251,7 +264,6 @@ function showControlBanner(){
|
||||
document.body.appendChild(el);
|
||||
el.querySelector('#rcStop').onclick=rcStopControl;
|
||||
}
|
||||
function rcStopControl(){ rcAllowed=false; rcPrompted=false; rcPost({type:'rc-arm', on:false}); const b=document.getElementById('rcBanner'); if(b) b.remove(); }
|
||||
let recTimerInt=null, recStartTs=0;
|
||||
function fmtElapsed(ms){const s=Math.max(0,Math.floor(ms/1000));return String(Math.floor(s/60)).padStart(2,'0')+':'+String(s%60).padStart(2,'0');}
|
||||
function recNotice(on){
|
||||
@@ -293,10 +305,14 @@ function buildBar(){
|
||||
const bar=document.createElement('div'); bar.id='sessionBar';
|
||||
bar.style.cssText='position:fixed;right:18px;bottom:18px;z-index:2147483000;display:flex;gap:10px;align-items:center;background:rgba(15,23,42,.94);padding:8px 12px;border-radius:16px;box-shadow:0 10px 28px rgba(0,0,0,.35)';
|
||||
const mic=_btn('micBtn',SVG_MICOFF,'Muted','#6b7280');
|
||||
// Remote-control access, right next to mic/chat, so the customer can see AND flip it at a glance.
|
||||
const rcb=_btn('rcBtn',(window.ic?window.ic('monitor',18):''),'Remote control is OFF','#6b7280');
|
||||
const chat=_btn('chatBtn',SVG_CHAT,'Chat','#475569');
|
||||
const end=_btn('endBtn2',SVG_END,'End','#dc2626');
|
||||
bar.appendChild(mic);bar.appendChild(chat);bar.appendChild(end);
|
||||
bar.appendChild(mic);bar.appendChild(rcb);bar.appendChild(chat);bar.appendChild(end);
|
||||
document.body.appendChild(bar);
|
||||
rcb.onclick=()=>{ if(rcAllowed) rcStopControl(); else rcGrantControl(); };
|
||||
updateRcBtn();
|
||||
const setMic=(on)=>{mic.title=on?'Mute':'Unmute';mic.innerHTML='<span style="display:inline-flex">'+(on?SVG_MIC:SVG_MICOFF)+'</span>';mic.style.background=on?'#2563eb':'#6b7280';};
|
||||
mic.onclick=async()=>{
|
||||
if(!window.__mic){
|
||||
|
||||
Reference in New Issue
Block a user