diff --git a/muxplex/frontend/app.js b/muxplex/frontend/app.js index d17bf3e..bbfc9ae 100644 --- a/muxplex/frontend/app.js +++ b/muxplex/frontend/app.js @@ -131,6 +131,7 @@ let _previewTimer = null; var _previewSessionName = null; // track by NAME, not DOM element // ─── Settings state ─────────────────────────────────────────────────────────── +let _sources = []; let _settingsOpen = false; let _serverSettings = null; const DISPLAY_SETTINGS_KEY = 'muxplex.display'; @@ -985,6 +986,36 @@ async function patchServerSetting(key, value) { } } +/** + * Build the list of session sources from server settings. + * Local source is always first with url: ''. + * Remote instances come from settings.remote_instances array. + * Trailing slashes are stripped from remote URLs. + * Default device name is 'This device' when device_name is empty. + * @param {object} settings - server settings object + * @returns {object[]} array of source objects with {url, name, type, status, backoffMs} + */ +function buildSources(settings) { + var localName = (settings && settings.device_name) || 'This device'; + var sources = [ + { url: '', name: localName, type: 'local', status: 'authenticated', backoffMs: 2000 }, + ]; + var remotes = (settings && settings.remote_instances) || []; + for (var i = 0; i < remotes.length; i++) { + var r = remotes[i]; + if (r && r.url) { + sources.push({ + url: r.url.replace(/\/+$/, ''), + name: r.name || r.url, + type: 'remote', + status: 'authenticated', + backoffMs: 2000, + }); + } + } + return sources; +} + // ─── Settings dialog ────────────────────────────────────────────────────────── /** @@ -1716,6 +1747,7 @@ if (typeof module !== 'undefined' && module.exports) { // Server settings loadServerSettings, patchServerSetting, + buildSources, // Fetch wrapper api, // Header + button with inline name input diff --git a/muxplex/frontend/tests/test_app.mjs b/muxplex/frontend/tests/test_app.mjs index a49acec..aaf70d8 100644 --- a/muxplex/frontend/tests/test_app.mjs +++ b/muxplex/frontend/tests/test_app.mjs @@ -2004,4 +2004,50 @@ test('createNewSession polls for session before auto-opening (not immediate setT ); }); +// --- buildSources --- + +test('buildSources returns only local source when no remote_instances', () => { + const sources = app.buildSources({ device_name: 'Laptop' }); + assert.strictEqual(sources.length, 1, 'should return exactly one source (local)'); + assert.strictEqual(sources[0].url, '', 'local source url should be empty string'); + assert.strictEqual(sources[0].name, 'Laptop', 'local source name should be device_name'); + assert.strictEqual(sources[0].type, 'local', 'local source type should be local'); + assert.strictEqual(sources[0].status, 'authenticated', 'local source status should be authenticated'); +}); + +test('buildSources returns local + remote sources from remote_instances', () => { + const sources = app.buildSources({ + device_name: 'Laptop', + remote_instances: [ + { url: 'https://server1.example.com', name: 'Server 1' }, + { url: 'https://server2.example.com', name: 'Server 2' }, + ], + }); + assert.strictEqual(sources.length, 3, 'should return 3 sources: 1 local + 2 remote'); + assert.strictEqual(sources[0].type, 'local', 'first source should be local'); + assert.strictEqual(sources[1].type, 'remote', 'second source should be remote'); + assert.strictEqual(sources[1].url, 'https://server1.example.com', 'remote source url should match'); + assert.strictEqual(sources[1].name, 'Server 1', 'remote source name should match'); + assert.strictEqual(sources[2].type, 'remote', 'third source should be remote'); + assert.strictEqual(sources[2].url, 'https://server2.example.com', 'remote source url should match'); +}); + +test('buildSources uses hostname fallback when device_name is empty', () => { + const sources = app.buildSources({}); + assert.strictEqual(sources.length, 1, 'should return one source'); + assert.strictEqual(sources[0].name, 'This device', 'local source name should fall back to This device'); +}); + +test('buildSources strips trailing slash from remote URLs', () => { + const sources = app.buildSources({ + device_name: 'Laptop', + remote_instances: [ + { url: 'https://server1.example.com/', name: 'Server 1' }, + { url: 'https://server2.example.com///', name: 'Server 2' }, + ], + }); + assert.strictEqual(sources[1].url, 'https://server1.example.com', 'trailing slash should be stripped from remote URL'); + assert.strictEqual(sources[2].url, 'https://server2.example.com', 'multiple trailing slashes should be stripped from remote URL'); +}); +