fix: renderGrid shows status tiles when no sessions; _previewClickHandler forwards sourceUrl

- renderGrid early-return now builds status tiles for auth_required and
  unreachable sources even when visible.length === 0. Hides empty-state
  when status tiles are present, shows it only when truly nothing to display.

- _previewClickHandler now looks up the session in _currentSessions to
  recover its sourceUrl before calling openSession, ensuring remote
  sessions opened via hover-preview connect to the correct instance
  instead of falling back to the local API path.

Tests added (3 new, 214 → 217 total):
- renderGrid shows auth tile and hides empty-state when no sessions but source is auth_required
- renderGrid shows offline tile and hides empty-state when no sessions but source is unreachable
- _previewClickHandler looks up sourceUrl from _currentSessions before calling openSession

Closes code-review Important issues #1 and #2.

Co-authored-by: Amplifier <amplifier@anthropic.com>
This commit is contained in:
Brian Krabach
2026-03-31 04:29:49 -07:00
parent 9befe9997e
commit c330f340ff
2 changed files with 81 additions and 3 deletions
+18 -3
View File
@@ -839,8 +839,20 @@ function renderGrid(sessions) {
} }
if (visible.length === 0) { if (visible.length === 0) {
if (grid) grid.innerHTML = ''; // Build status tiles for non-authenticated sources even when no sessions exist
if (emptyState) emptyState.classList.remove('hidden'); var statusTilesHtml = '';
if (typeof _sources !== 'undefined' && _sources) {
_sources.forEach(function(source) {
if (source.status === 'auth_required') statusTilesHtml += buildAuthTileHTML(source);
else if (source.status === 'unreachable') statusTilesHtml += buildOfflineTileHTML(source);
});
}
if (grid) grid.innerHTML = statusTilesHtml;
// Only show empty-state when there are truly no tiles at all
if (emptyState) {
if (statusTilesHtml) emptyState.classList.add('hidden');
else emptyState.classList.remove('hidden');
}
// Show filter bar even when filtered to empty (so user can switch back) // Show filter bar even when filtered to empty (so user can switch back)
if (filterBar) { if (filterBar) {
if (_gridViewMode === 'filtered') { if (_gridViewMode === 'filtered') {
@@ -920,7 +932,10 @@ function _previewClickHandler(e) {
e.stopPropagation(); e.stopPropagation();
var name = _previewSessionName; var name = _previewSessionName;
hidePreview(); hidePreview();
if (name) openSession(name); if (name) {
var session = _currentSessions && _currentSessions.find(function(s) { return s.name === name; });
openSession(name, { sourceUrl: session && session.sourceUrl || '' });
}
} }
function showPreview(name) { function showPreview(name) {
+63
View File
@@ -606,6 +606,69 @@ test('renderGrid includes offline tile HTML when a source is unreachable', () =>
app._setSources([]); app._setSources([]);
}); });
test('renderGrid shows auth tile and hides empty-state when no sessions but source is auth_required', () => {
const addedClasses = [];
const removedClasses = [];
const mockGrid = { innerHTML: '' };
const mockEmpty = { style: {}, classList: { add: (c) => addedClasses.push(c), remove: (c) => removedClasses.push(c) } };
const origGetById = globalThis.document.getElementById;
globalThis.document.getElementById = (id) => {
if (id === 'session-grid') return mockGrid;
if (id === 'empty-state') return mockEmpty;
return null;
};
app._setSources([
{ url: 'http://workstation:8088', name: 'Workstation', status: 'auth_required' },
]);
// No sessions — early-return path
app.renderGrid([]);
assert.ok(mockGrid.innerHTML.includes('source-tile--auth'), 'grid should show auth tile even when sessions list is empty');
assert.ok(addedClasses.includes('hidden'), 'empty-state should be hidden when status tiles are present');
assert.ok(!removedClasses.includes('hidden'), 'empty-state hidden class should NOT be removed when status tiles are present');
globalThis.document.getElementById = origGetById;
app._setSources([]);
});
test('renderGrid shows offline tile and hides empty-state when no sessions but source is unreachable', () => {
const addedClasses = [];
const mockGrid = { innerHTML: '' };
const mockEmpty = { style: {}, classList: { add: (c) => addedClasses.push(c), remove: () => {} } };
const origGetById = globalThis.document.getElementById;
globalThis.document.getElementById = (id) => {
if (id === 'session-grid') return mockGrid;
if (id === 'empty-state') return mockEmpty;
return null;
};
app._setSources([
{ url: 'http://devbox:8088', name: 'Dev Box', status: 'unreachable', lastSeenAt: Date.now() - 120000 },
]);
// No sessions — early-return path
app.renderGrid([]);
assert.ok(mockGrid.innerHTML.includes('source-tile--offline'), 'grid should show offline tile even when sessions list is empty');
assert.ok(addedClasses.includes('hidden'), 'empty-state should be hidden when status tiles are present');
globalThis.document.getElementById = origGetById;
app._setSources([]);
});
test('_previewClickHandler looks up sourceUrl from _currentSessions before calling openSession', () => {
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
const handlerIdx = source.indexOf('function _previewClickHandler');
assert.ok(handlerIdx >= 0, '_previewClickHandler must exist');
// Extract from function declaration to its closing brace
const handlerEnd = source.indexOf('\n}', handlerIdx) + 2;
const handlerBody = source.slice(handlerIdx, handlerEnd);
assert.ok(handlerBody.includes('_currentSessions'), '_previewClickHandler must look up session from _currentSessions to recover sourceUrl');
assert.ok(handlerBody.includes('sourceUrl'), '_previewClickHandler must forward sourceUrl when calling openSession');
});
// --- requestNotificationPermission --- // --- requestNotificationPermission ---
test('requestNotificationPermission is exported', () => { test('requestNotificationPermission is exported', () => {