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:
+18
-3
@@ -839,8 +839,20 @@ function renderGrid(sessions) {
|
||||
}
|
||||
|
||||
if (visible.length === 0) {
|
||||
if (grid) grid.innerHTML = '';
|
||||
if (emptyState) emptyState.classList.remove('hidden');
|
||||
// Build status tiles for non-authenticated sources even when no sessions exist
|
||||
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)
|
||||
if (filterBar) {
|
||||
if (_gridViewMode === 'filtered') {
|
||||
@@ -920,7 +932,10 @@ function _previewClickHandler(e) {
|
||||
e.stopPropagation();
|
||||
var name = _previewSessionName;
|
||||
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) {
|
||||
|
||||
@@ -606,6 +606,69 @@ test('renderGrid includes offline tile HTML when a source is unreachable', () =>
|
||||
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 ---
|
||||
|
||||
test('requestNotificationPermission is exported', () => {
|
||||
|
||||
Reference in New Issue
Block a user