fix: restore default title when device name cleared, fix stale comments, set title at load

This commit is contained in:
Brian Krabach
2026-03-31 06:36:49 -07:00
parent bee74f5c1b
commit 4e78f6f5ac
23 changed files with 66 additions and 7 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+7 -7
View File
@@ -1792,9 +1792,7 @@ function openSettings() {
} }
// Update document.title from device_name setting // Update document.title from device_name setting
if (ss && ss.device_name) { document.title = (ss && ss.device_name) || 'muxplex';
document.title = ss.device_name;
}
// Multi-device enabled checkbox (with smart default: checked if remote_instances non-empty) // Multi-device enabled checkbox (with smart default: checked if remote_instances non-empty)
const multiDeviceEnabledEl = $('setting-multi-device-enabled'); const multiDeviceEnabledEl = $('setting-multi-device-enabled');
@@ -2351,7 +2349,7 @@ function bindStaticEventListeners() {
on($('setting-device-name'), 'input', function() { on($('setting-device-name'), 'input', function() {
clearTimeout(_deviceNameDebounceTimer); clearTimeout(_deviceNameDebounceTimer);
var val = this.value; var val = this.value;
if (val) document.title = val; document.title = val || 'muxplex';
_deviceNameDebounceTimer = setTimeout(function() { _deviceNameDebounceTimer = setTimeout(function() {
patchServerSetting('device_name', val).then(function() { patchServerSetting('device_name', val).then(function() {
_sources = buildSources(_serverSettings); _sources = buildSources(_serverSettings);
@@ -2359,7 +2357,7 @@ function bindStaticEventListeners() {
}, 500); }, 500);
}); });
// Sessions tab — add remote instance button // Multi-Device tab — add remote instance button
on($('add-remote-instance-btn'), 'click', function() { on($('add-remote-instance-btn'), 'click', function() {
var container = $('setting-remote-instances'); var container = $('setting-remote-instances');
if (container) { if (container) {
@@ -2367,7 +2365,7 @@ function bindStaticEventListeners() {
} }
}); });
// Sessions tab — delegated remove handler on remote instances container // Multi-Device tab — delegated remove handler on remote instances container
var remoteInstancesContainer = $('setting-remote-instances'); var remoteInstancesContainer = $('setting-remote-instances');
if (remoteInstancesContainer) { if (remoteInstancesContainer) {
remoteInstancesContainer.addEventListener('click', function(e) { remoteInstancesContainer.addEventListener('click', function(e) {
@@ -2519,7 +2517,9 @@ document.addEventListener('DOMContentLoaded', () => {
restoreState() restoreState()
.then(() => { .then(() => {
startPolling(); startPolling();
loadServerSettings(); loadServerSettings().then(function() {
document.title = _serverSettings.device_name || 'muxplex';
});
startHeartbeat(); startHeartbeat();
bindStaticEventListeners(); bindStaticEventListeners();
}) })
+59
View File
@@ -3564,3 +3564,62 @@ test('CSS style.css has scrollbar-width none for fit mode pre to hide scrollbar'
'style.css must have scrollbar-width: none for hidden scrollbar in fit mode pre' 'style.css must have scrollbar-width: none for hidden scrollbar in fit mode pre'
); );
}); });
// --- document.title quality fixes ---
test('device name input handler restores default title when value is cleared', () => {
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
// Must use fallback form, not the conditional-only form
assert.ok(
source.includes("document.title = val || 'muxplex'"),
"device name input handler must set document.title = val || 'muxplex' to restore default when cleared"
);
assert.ok(
!source.includes("if (val) document.title = val"),
"device name input handler must NOT use conditional 'if (val) document.title = val' (skips restore)"
);
});
test('openSettings restores default title unconditionally when device_name is absent', () => {
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
assert.ok(
source.includes("document.title = (ss && ss.device_name) || 'muxplex'"),
"openSettings must set document.title = (ss && ss.device_name) || 'muxplex' unconditionally"
);
assert.ok(
!source.includes("if (ss && ss.device_name) {\n document.title = ss.device_name;\n }"),
"openSettings must NOT use conditional block that skips restore when device_name is absent"
);
});
test('remote instance event listener comments say Multi-Device tab not Sessions tab', () => {
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
assert.ok(
!source.includes('// Sessions tab \u2014 add remote instance button'),
"comment for add-remote-instance-btn must say 'Multi-Device tab', not 'Sessions tab'"
);
assert.ok(
!source.includes('// Sessions tab \u2014 delegated remove handler'),
"comment for delegated remove handler must say 'Multi-Device tab', not 'Sessions tab'"
);
assert.ok(
source.includes('// Multi-Device tab \u2014 add remote instance button'),
"add-remote-instance-btn comment must say '// Multi-Device tab \u2014 add remote instance button'"
);
assert.ok(
source.includes('// Multi-Device tab \u2014 delegated remove handler'),
"delegated remove handler comment must say '// Multi-Device tab \u2014 delegated remove handler'"
);
});
test('DOMContentLoaded sets document.title from server settings at page load', () => {
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
// Find the DOMContentLoaded block
const domIdx = source.indexOf("document.addEventListener('DOMContentLoaded'");
assert.ok(domIdx !== -1, 'DOMContentLoaded handler must exist');
const domBlock = source.substring(domIdx, domIdx + 800);
assert.ok(
domBlock.includes("document.title = _serverSettings.device_name || 'muxplex'"),
"DOMContentLoaded must set document.title = _serverSettings.device_name || 'muxplex' after loadServerSettings resolves"
);
});