- you were sharing your screen before this refresh — tap to resume (browsers need a fresh click to grant screen capture)
-
-
-
your hand
@@ -1292,14 +1286,13 @@ const THEME_KEY = 'zebra-theme-v1';
* hard refresh auto-rejoins. Different tabs can sit in different
* spaces because sessionStorage doesn't bleed across tabs. */
const ACTIVE_CALL_KEY = 'zebra-spaces-active-call-v1';
+/* screen-share state is deliberately NOT tracked across reload. Even
+ * with a click banner the user has to re-pick the surface (browser
+ * security: getDisplayMedia always shows the picker; selection cannot
+ * be remembered across a reload). A re-pick is the same friction as
+ * just clicking 'share screen' again, so we keep the single canonical
+ * button instead of adding a banner that adds zero net clicks. */
const ACTIVE_CAM_KEY = 'zebra-spaces-active-cam-v1';
-/* screen-share resume — getDisplayMedia REQUIRES a fresh user gesture
- * (Chrome / Firefox block silent triggers after a hard refresh), so
- * unlike the camera we can't just re-acquire silently on welcome.
- * Instead we surface a one-click resume banner if the user was sharing
- * before the refresh — the click IS the gesture and counts toward the
- * getDisplayMedia call inside the same task. */
-const ACTIVE_SCREEN_KEY = 'zebra-spaces-active-screen-v1';
/* mute state for the current tab — '1' means muted. Survives a hard
* refresh via sessionStorage so the page reconnects in the same state
* the user last set. Cleared on explicit leave. NOT cleared on role
@@ -2127,7 +2120,13 @@ async function sfuPublishScreen(){
stream = await navigator.mediaDevices.getDisplayMedia({
video: { width:{ideal:1920}, height:{ideal:1080}, frameRate:{ideal:30} },
audio: { echoCancellation:false, noiseSuppression:false, autoGainControl:false,
- channelCount:2, sampleRate:48000 }
+ channelCount:2, sampleRate:48000 },
+ /* picker UX hints. Chrome respects these — biases toward the
+ * previously-shared surface family and shows tab audio toggle by
+ * default. Firefox / Safari currently ignore them. */
+ surfaceSwitching: 'include',
+ selfBrowserSurface: 'include',
+ systemAudio: 'include',
});
} catch(e){ logLine('err','screen share cancelled: '+e.message); return; }
sfuScreenStream = stream;
@@ -2188,8 +2187,6 @@ async function sfuPublishScreen(){
renderScreenTile(myKeys.pubHex, stream, { local: true });
$('btn-screen-share').classList.add('hidden');
$('btn-screen-stop').classList.remove('hidden');
- try { sessionStorage.setItem(ACTIVE_SCREEN_KEY, '1'); } catch(_){}
- $('sec-screen-resume').classList.add('hidden');
}
async function sfuUnpublishScreen(){
if (!sfuScreenPC && !sfuScreenStream) return;
@@ -2202,8 +2199,6 @@ async function sfuUnpublishScreen(){
}
$('btn-screen-share').classList.remove('hidden');
$('btn-screen-stop').classList.add('hidden');
- try { sessionStorage.removeItem(ACTIVE_SCREEN_KEY); } catch(_){}
- $('sec-screen-resume').classList.add('hidden');
logLine('', 'screen share stopped');
}
@@ -3011,20 +3006,8 @@ async function handleSignal(raw){
});
}, 250);
}
- /* screen — getDisplayMedia requires a fresh user gesture per
- * browser security policy, so we surface a one-click resume
- * banner instead. The click IS the gesture. */
- let wantScreen = false;
- try { wantScreen = sessionStorage.getItem(ACTIVE_SCREEN_KEY) === '1'; } catch(_){}
- if (wantScreen){
- $('sec-screen-resume').classList.remove('hidden');
- } else {
- $('sec-screen-resume').classList.add('hidden');
- }
} else {
try { sessionStorage.removeItem(ACTIVE_CAM_KEY); } catch(_){}
- try { sessionStorage.removeItem(ACTIVE_SCREEN_KEY); } catch(_){}
- $('sec-screen-resume').classList.add('hidden');
}
renderShareUrl();
onRoleEntered();
@@ -3514,17 +3497,6 @@ function renderRoom(){
$('btn-raise').addEventListener('click', () => { send({ type:'raise-hand' }); });
$('btn-lower').addEventListener('click', () => { send({ type:'lower-hand' }); });
$('btn-screen-share').addEventListener('click', () => { logLine('', 'click: btn-screen-share'); sfuPublishScreen().catch(e => logLine('err','screen share: '+e.message)); });
-$('btn-screen-resume').addEventListener('click', () => {
- /* The user gesture from this click is what unlocks getDisplayMedia
- * after a refresh — chained directly into sfuPublishScreen so the
- * browser counts it toward the same task. */
- logLine('', 'click: btn-screen-resume');
- sfuPublishScreen().catch(e => {
- logLine('err', 'screen resume: ' + e.message);
- try { sessionStorage.removeItem(ACTIVE_SCREEN_KEY); } catch(_){}
- $('sec-screen-resume').classList.add('hidden');
- });
-});
$('btn-screen-stop').addEventListener('click', () => {
/* click telemetry — the 'screen share stopped' line lower in
* sfuUnpublishScreen only fires AFTER the early-return, so logging
@@ -3943,8 +3915,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');