diff --git a/zebra-report/zebra-spaces.html b/zebra-report/zebra-spaces.html
index 9f75c10..8c9351a 100644
--- a/zebra-report/zebra-spaces.html
+++ b/zebra-report/zebra-spaces.html
@@ -429,7 +429,10 @@ function logLine(kind, msg){
* Go side does with ed25519.PublicKey.
* ================================================================== */
const ID_KEY = 'zebra-spaces-id-v1';
-const HANDLE_KEY = 'zebra-spaces-handle-v1';
+const HANDLE_KEY = 'zebra-spaces-handle-v1';
+const MUSIC_MODE_KEY = 'zebra-spaces-music-mode-v1';
+const MIC_DEV_KEY = 'zebra-spaces-mic-device-v1';
+const CAM_DEV_KEY = 'zebra-spaces-cam-device-v1';
const VAULT_PREFIX = 'zspc-id-v1|';
const PBKDF2_ITER = 600000;
@@ -578,6 +581,14 @@ $('handle').addEventListener('input', (e) => {
await loadOrCreateIdentity();
myHandle = localStorage.getItem(HANDLE_KEY) || '';
$('handle').value = myHandle;
+/* preferences that should outlive a reload — music-mode toggle and the
+ * last-picked mic + camera deviceIds. They're applied here before any
+ * device enumeration / mic acquisition so the very first getUserMedia
+ * call uses the right device + constraints. */
+try { musicMode = localStorage.getItem(MUSIC_MODE_KEY) === '1'; } catch(_){}
+try { micDeviceId = localStorage.getItem(MIC_DEV_KEY) || ''; } catch(_){}
+try { cameraDeviceId = localStorage.getItem(CAM_DEV_KEY) || ''; } catch(_){}
+if ($('music-mode')) $('music-mode').checked = musicMode;
renderIdentity();
logLine('', 'pubkey '+myKeys.pubHex);
@@ -1735,6 +1746,7 @@ $('btn-camera-share').addEventListener('click', () => { sfuPublishCamera().catch
$('btn-camera-stop').addEventListener('click', () => { sfuUnpublishCamera(); });
$('camera-select').addEventListener('change', async (e) => {
cameraDeviceId = e.target.value;
+ try { localStorage.setItem(CAM_DEV_KEY, cameraDeviceId); } catch(_){}
if (sfuCameraPC){
/* restart with the new device — getUserMedia must be re-called with
* the new deviceId; replaceTrack on a sender from a different device
@@ -1976,10 +1988,12 @@ $('btn-mute').addEventListener('click', () => {
});
$('mic-select').addEventListener('change', async (e) => {
micDeviceId = e.target.value;
+ try { localStorage.setItem(MIC_DEV_KEY, micDeviceId); } catch(_){}
if (micStream){ try { await applyMicMode(); } catch(err){ logLine('err','input switch failed: '+err.message); await refreshMicList(); } }
});
$('music-mode').addEventListener('change', async (e) => {
musicMode = e.target.checked;
+ try { localStorage.setItem(MUSIC_MODE_KEY, musicMode ? '1' : '0'); } catch(_){}
logLine('', 'mic mode: '+(musicMode?'MUSIC':'VOICE'));
if (micStream){ try { await applyMicMode(); } catch(err){ logLine('err','mic mode switch failed: '+err.message); } }
});
@@ -2057,8 +2071,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');