zebra-report: zebra-audio mic icons (grid) + no button reflow
This commit is contained in:
parent
9a98ef18a5
commit
cb0e5454b1
1 changed files with 28 additions and 19 deletions
|
|
@ -51,8 +51,15 @@
|
|||
.status-line.ok { color: #060; }
|
||||
.status-line.err { color: #b00; }
|
||||
.note { font-size: 0.75rem; color: #555; line-height: 1.5; }
|
||||
.meter { height: 12px; border: 1px solid #000; background: #fff; position: relative; overflow: hidden; flex: 1; }
|
||||
.meter { height: 12px; border: 1px solid #000; background: #fff; position: relative; overflow: hidden; }
|
||||
.meter-fill { height: 100%; background: #000; width: 0%; transition: width 0.06s linear; }
|
||||
/* grid layouts so controls never reflow when a label/state changes */
|
||||
.controls { display: grid; grid-template-columns: auto 1fr auto auto; align-items: center; gap: 0.6rem; margin-bottom: 0.75rem; }
|
||||
#btn-mute { width: 6rem; text-align: center; } /* fixed width: 'mute'/'unmute' never shifts the row */
|
||||
.level { display: grid; grid-template-columns: 3rem 1.5rem 1fr; align-items: center; gap: 0.6rem; margin-bottom: 0.6rem; }
|
||||
.level .who { font-size: 0.8rem; color: #555; }
|
||||
.mic { width: 18px; height: 18px; }
|
||||
.mic svg { width: 18px; height: 18px; display: block; }
|
||||
.log { border: 1px solid #000; height: 150px; overflow-y: auto; padding: 0.5rem; font-size: 0.75rem; line-height: 1.5; background: #fafafa; }
|
||||
.log-line { margin-bottom: 0.2rem; word-wrap: break-word; }
|
||||
.log-line .ts { color: #888; }
|
||||
|
|
@ -71,10 +78,10 @@
|
|||
<input type="text" id="rdv-code" placeholder="rendezvous code — both type the same thing">
|
||||
<button id="btn-call" class="invert">call</button>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="controls">
|
||||
<div class="dot warn" id="dot-call"></div>
|
||||
<span id="call-status" class="status-line">idle</span>
|
||||
<button id="btn-mute" style="margin-left:auto" disabled>mute</button>
|
||||
<button id="btn-mute" disabled>mute</button>
|
||||
<button id="btn-hangup" disabled>hang up</button>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
|
@ -91,15 +98,15 @@
|
|||
|
||||
<section>
|
||||
<h2>levels</h2>
|
||||
<div class="row">
|
||||
<span class="note" style="min-width:3.5rem">you</span>
|
||||
<div class="level">
|
||||
<span class="who">you</span>
|
||||
<span class="mic" id="you-mic" title="your mic"></span>
|
||||
<div class="meter"><div class="meter-fill" id="meter-mic"></div></div>
|
||||
<span class="note" id="you-state" style="min-width:4.5rem; text-align:right"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="note" style="min-width:3.5rem">them</span>
|
||||
<div class="level">
|
||||
<span class="who">them</span>
|
||||
<span class="mic" id="them-mic" title="partner mic"></span>
|
||||
<div class="meter"><div class="meter-fill" id="meter-rem"></div></div>
|
||||
<span class="note" id="them-state" style="min-width:4.5rem; text-align:right"></span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -219,7 +226,7 @@ async function ensurePC(){
|
|||
const s = pc.connectionState;
|
||||
setStatus('call: '+s, s==='connected'?'ok':null);
|
||||
$('dot-call').className = 'dot ' + (s==='connected'?'ok':'warn');
|
||||
if (s==='connected'){ $('btn-mute').disabled=false; $('btn-hangup').disabled=false; setTimeout(reportPath,1200); setMicLabels(); sendMicState(); }
|
||||
if (s==='connected'){ $('btn-mute').disabled=false; $('btn-hangup').disabled=false; setTimeout(reportPath,1200); partnerKnown=true; setMicIcons(); sendMicState(); }
|
||||
else if (s==='failed'||s==='disconnected'||s==='closed'){ setPath('path: —', null); }
|
||||
};
|
||||
return pc;
|
||||
|
|
@ -273,7 +280,7 @@ async function startCall(){
|
|||
const code = $('rdv-code').value.trim();
|
||||
if (!code){ setStatus('enter a rendezvous code first','err'); return; }
|
||||
if (relayWS){ setStatus('already in a call — hang up first', null); return; }
|
||||
try { await getMic(); meterFor(micStream,'meter-mic'); }
|
||||
try { await getMic(); meterFor(micStream,'meter-mic'); setMicIcons(); }
|
||||
catch(e){ setStatus('microphone blocked: '+e.message,'err'); return; }
|
||||
await refreshTurnCred();
|
||||
relayKey = await deriveSignalKey(code);
|
||||
|
|
@ -300,7 +307,7 @@ async function startCall(){
|
|||
$('remote-audio').srcObject=null; $('meter-rem').style.width='0%';
|
||||
$('dot-call').className='dot warn'; setPath('path: —', null);
|
||||
$('btn-mute').disabled=true; muted=false; $('btn-mute').textContent='mute'; $('btn-mute').className='';
|
||||
setPartnerMic(false); setMicLabels();
|
||||
partnerKnown=false; partnerMuted=false; setMicIcons();
|
||||
setStatus('partner left — waiting for them to return', null);
|
||||
}
|
||||
else if (m.type==='sdp'){ try { await onSDP(m.kind, await aesDecrypt(relayKey, unb64(m.data))); } catch(e){ logLine('err','sdp failed (codes must match): '+e.message); } }
|
||||
|
|
@ -315,15 +322,17 @@ function hangup(){
|
|||
$('btn-mute').disabled=true; $('btn-hangup').disabled=true; $('btn-call').disabled=false;
|
||||
$('dot-call').className='dot warn'; setStatus('idle', null); setPath('path: —', null);
|
||||
$('meter-rem').style.width='0%';
|
||||
muted=false; partnerMuted=false; $('btn-mute').textContent='mute'; $('btn-mute').className=''; setMicLabels();
|
||||
muted=false; partnerMuted=false; partnerKnown=false; $('btn-mute').textContent='mute'; $('btn-mute').className=''; setMicIcons();
|
||||
logLine('', 'call ended');
|
||||
}
|
||||
let muted=false, partnerMuted=false;
|
||||
function setMicLabels(){
|
||||
$('you-state').textContent = muted ? 'muted' : '';
|
||||
$('them-state').textContent = partnerMuted ? 'muted' : '';
|
||||
let muted=false, partnerMuted=false, partnerKnown=false;
|
||||
const MIC_ON = '<svg viewBox="0 0 24 24" fill="none" stroke="#060" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>';
|
||||
const MIC_OFF = '<svg viewBox="0 0 24 24" fill="none" stroke="#b00" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="1" y1="1" x2="23" y2="23"/><path d="M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6"/><path d="M17 16.95A7 7 0 0 1 5 12v-2m14 0v2a7 7 0 0 1-.11 1.23"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>';
|
||||
function setMicIcons(){
|
||||
$('you-mic').innerHTML = micStream ? (muted ? MIC_OFF : MIC_ON) : '';
|
||||
$('them-mic').innerHTML = partnerKnown ? (partnerMuted ? MIC_OFF : MIC_ON) : '';
|
||||
}
|
||||
function setPartnerMic(m){ partnerMuted=m; setMicLabels(); logLine('', 'partner '+(m?'muted':'unmuted')); }
|
||||
function setPartnerMic(m){ partnerMuted=m; partnerKnown=true; setMicIcons(); logLine('', 'partner '+(m?'muted':'unmuted')); }
|
||||
/* advertise our mic state to the partner over the rendezvous channel, encrypted
|
||||
* with the room code so the relay never learns it */
|
||||
async function sendMicState(){
|
||||
|
|
@ -336,7 +345,7 @@ function toggleMute(){
|
|||
micStream.getAudioTracks().forEach(t=>t.enabled=!muted);
|
||||
$('btn-mute').textContent = muted?'unmute':'mute';
|
||||
$('btn-mute').className = muted?'invert':'';
|
||||
setMicLabels();
|
||||
setMicIcons();
|
||||
sendMicState();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue