From 5980a6c08022fc389d71f86a31610aea06f2f996 Mon Sep 17 00:00:00 2001 From: Brian Krabach Date: Mon, 30 Mar 2026 23:34:22 -0700 Subject: [PATCH] feat: add device badge and data attributes to buildTileHTML (task-9) - Show after tile name when _sources.length > 1 and session.deviceName is present (multi-source federation context) - Add data-session-key and data-source-url attributes to tile article element for unique session identification across sources - Tests: 3 new tests covering badge visibility, badge omission for single source, and presence of new data attributes --- muxplex/frontend/app.js | 4 ++-- muxplex/frontend/tests/test_app.mjs | 37 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/muxplex/frontend/app.js b/muxplex/frontend/app.js index f2eb97e..35f750a 100644 --- a/muxplex/frontend/app.js +++ b/muxplex/frontend/app.js @@ -468,9 +468,9 @@ function buildTileHTML(session, index, mobile) { const lastLines = snapshot.split('\n').slice(-20).join('\n'); return ( - `
` + + `
` + `
` + - `${escapeHtml(name)}` + + `${escapeHtml(name)}${_sources.length > 1 && session.deviceName ? '' + escapeHtml(session.deviceName) + '' : ''}` + `${bellHtml}${escapeHtml(timeStr)}` + `
` + `
${ansiToHtml(lastLines)}
` + diff --git a/muxplex/frontend/tests/test_app.mjs b/muxplex/frontend/tests/test_app.mjs index 339055a..be83e7a 100644 --- a/muxplex/frontend/tests/test_app.mjs +++ b/muxplex/frontend/tests/test_app.mjs @@ -2338,6 +2338,43 @@ test('getVisibleSessions hides local sessions by name but not remote sessions wi app._setServerSettings(null); }); +// --- buildTileHTML device badge (task-9) --- + +test('buildTileHTML shows device-badge when session has deviceName and multiple sources', () => { + app._setSources([ + { url: '', name: 'This device', type: 'local', status: 'authenticated', backoffMs: 2000 }, + { url: 'https://remote.example.com', name: 'Remote', type: 'remote', status: 'authenticated', backoffMs: 2000 }, + ]); + const session = { name: 'work', deviceName: 'Laptop', sourceUrl: '', sessionKey: '::work', snapshot: '' }; + const html = app.buildTileHTML(session, 0, false); + assert.ok(html.includes('device-badge'), 'should show device-badge when _sources.length > 1 and session has deviceName'); + assert.ok(html.includes('Laptop'), 'device-badge should contain the deviceName'); + app._setSources([]); +}); + +test('buildTileHTML omits device-badge when only one source configured', () => { + app._setSources([ + { url: '', name: 'This device', type: 'local', status: 'authenticated', backoffMs: 2000 }, + ]); + const session = { name: 'work', deviceName: 'Laptop', sourceUrl: '', sessionKey: '::work', snapshot: '' }; + const html = app.buildTileHTML(session, 0, false); + assert.ok(!html.includes('device-badge'), 'should NOT show device-badge when _sources.length is 1'); + app._setSources([]); +}); + +test('buildTileHTML includes data-session-key and data-source-url attributes on article element', () => { + const session = { + name: 'work', + deviceName: 'Laptop', + sourceUrl: 'https://remote.example.com', + sessionKey: 'https://remote.example.com::work', + snapshot: '', + }; + const html = app.buildTileHTML(session, 0, false); + assert.ok(html.includes('data-session-key='), 'article should have data-session-key attribute'); + assert.ok(html.includes('data-source-url='), 'article should have data-source-url attribute'); +}); + test('pollSessions sets unreachable and applies exponential backoff on network error', async () => { const mockStatusEl = { textContent: '', className: '' }; const mockGrid = { innerHTML: '' };