fix: filter hidden sessions in browser indicators + add federation plan

This commit is contained in:
Brian Krabach
2026-04-08 20:32:19 -07:00
parent e7379c46c9
commit 783611d143
5 changed files with 1490 additions and 3 deletions
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -1091,7 +1091,8 @@ function _drawFaviconBadge() {
* Delegates drawing to _drawFaviconBadge which manages the cached Image object.
*/
function updateFaviconBadge() {
var hasActivity = _currentSessions && _currentSessions.some(function (s) {
var visible = getVisibleSessions(_currentSessions);
var hasActivity = visible.length > 0 && visible.some(function (s) {
return s.bell && s.bell.unseen_count > 0;
});
@@ -1122,7 +1123,8 @@ function updatePageTitle() {
var hostname = (_serverSettings && _serverSettings.device_name) ||
(typeof location !== 'undefined' ? location.hostname : null) ||
'muxplex';
var count = (_currentSessions || []).filter(function(s) {
var visible = getVisibleSessions(_currentSessions);
var count = visible.filter(function(s) {
return s.bell && s.bell.unseen_count > 0;
}).length;
var prefix = count > 0 ? '(' + count + ') ' : '';
@@ -2547,6 +2549,7 @@ if (typeof module !== 'undefined' && module.exports) {
renderSheetList,
updateSessionPill,
updatePageTitle,
updateFaviconBadge,
// ANSI color rendering
ansiToHtml,
ansiParamsToStyle,
+60
View File
@@ -4641,3 +4641,63 @@ test('getVisibleSessions hides local session (remoteId null) but keeps remote se
assert.strictEqual(visible[0].remoteId, 0, 'the visible session must be the remote one with remoteId=0');
app._setServerSettings(null);
});
// --- task-2: updatePageTitle and updateFaviconBadge filter hidden sessions ---
test('updatePageTitle excludes hidden sessions from bell count', () => {
// Set up server settings with hidden_sessions
app._setServerSettings({ hidden_sessions: ['hidden-build'] });
// Create two sessions: visible-dev (unseen_count 2) and hidden-build (unseen_count 5)
app._setCurrentSessions([
{ name: 'visible-dev', bell: { unseen_count: 2 } },
{ name: 'hidden-build', bell: { unseen_count: 5 } },
]);
// Call updatePageTitle
app.updatePageTitle();
// Assert title starts with '(1)' — only visible-dev counts, not hidden-build
assert.ok(
document.title.startsWith('(1)'),
`title must start with '(1)' when only 1 visible session has bells, got '${document.title}'`
);
// Clean up
app._setServerSettings(null);
app._setCurrentSessions([]);
});
test('updateFaviconBadge does not show activity for only-hidden sessions with bells', () => {
// Set up server settings with hidden_sessions
app._setServerSettings({ hidden_sessions: ['hidden-build'] });
// Create only a hidden session with bell activity
app._setCurrentSessions([
{ name: 'hidden-build', bell: { unseen_count: 5 } },
]);
// Mock document.querySelector to return a fake link element
const origQS = globalThis.document.querySelector;
const faviconHref = 'http://localhost/favicon.ico';
const fakeLink = { href: faviconHref };
globalThis.document.querySelector = (sel) => {
if (sel && sel.includes('icon')) return fakeLink;
return null;
};
// Call updateFaviconBadge — should NOT draw badge since no visible sessions have bells
app.updateFaviconBadge();
// favicon href must not change — no badge should be applied
assert.strictEqual(
fakeLink.href,
faviconHref,
'favicon href must not change when only hidden sessions have bell activity'
);
// Restore mocks and state
globalThis.document.querySelector = origQS;
app._setServerSettings(null);
app._setCurrentSessions([]);
});
+33
View File
@@ -2733,3 +2733,36 @@ def test_get_visible_sessions_uses_null_check_not_falsy() -> None:
"getVisibleSessions must use 's.remoteId == null' (or '=== null') to correctly treat "
"remoteId=0 as a real remote while matching null/undefined as local sessions."
)
# ─── task-2: updatePageTitle and updateFaviconBadge filter hidden sessions ─────
def test_update_page_title_filters_through_visible_sessions() -> None:
"""updatePageTitle() must count bell activity from getVisibleSessions(), not all sessions."""
match = re.search(
r"function updatePageTitle\s*\(\s*\)\s*\{(.*?)(?=\nfunction |\n// ─|\n/\*\*)",
_JS,
re.DOTALL,
)
assert match, "updatePageTitle function not found in app.js"
body = match.group(1)
assert "getVisibleSessions" in body, (
"updatePageTitle must filter sessions through getVisibleSessions() "
"to exclude hidden sessions from the bell count"
)
def test_update_favicon_badge_filters_through_visible_sessions() -> None:
"""updateFaviconBadge() must check bell activity from getVisibleSessions(), not all sessions."""
match = re.search(
r"function updateFaviconBadge\s*\(\s*\)\s*\{(.*?)(?=\nfunction |\n// ─|\n/\*\*)",
_JS,
re.DOTALL,
)
assert match, "updateFaviconBadge function not found in app.js"
body = match.group(1)
assert "getVisibleSessions" in body, (
"updateFaviconBadge must filter sessions through getVisibleSessions() "
"to exclude hidden sessions from the bell activity check"
)
Generated
+1 -1
View File
@@ -332,7 +332,7 @@ wheels = [
[[package]]
name = "muxplex"
version = "0.2.0"
version = "0.2.1"
source = { editable = "." }
dependencies = [
{ name = "aiofiles" },