fix: fit view — reapply layout on session close, show more lines, bottom-anchor content
Bug 1: closeSession() didn't call applyFitLayout when returning to
dashboard — tiles kept old heights from before the overview was hidden.
Fix: add rAF call after overview.style.display = '' so the layout is
remeasured once the overview becomes visible again.
Bug 2: Only 20 lines shown in tiles — not enough for tall fit-mode
tiles, and content sat at the top. Fix:
- buildTileHTML shows 80 lines in fit mode (vs 20 in auto)
- renderGrid rAF sets pre.scrollTop = pre.scrollHeight after
applyFitLayout so content anchors at the bottom (like a real
terminal). Hidden scrollbar via scrollbar-width:none + ::-webkit.
Tests: 3 new source-scan tests added and verified RED before GREEN.
This commit is contained in:
+21
-3
@@ -400,9 +400,11 @@ function buildTileHTML(session, index, mobile) {
|
|||||||
bellHtml = `<span class="tile-bell">${countStr}</span>`;
|
bellHtml = `<span class="tile-bell">${countStr}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last 20 lines of snapshot
|
// Last N lines of snapshot — show more in fit mode so tall tiles fill
|
||||||
const snapshot = session.snapshot || '';
|
const snapshot = session.snapshot || '';
|
||||||
const lastLines = snapshot.split('\n').slice(-20).join('\n');
|
var _tileDs = loadDisplaySettings();
|
||||||
|
var _lineCount = (_tileDs.viewMode === 'fit') ? -80 : -20;
|
||||||
|
const lastLines = snapshot.split('\n').slice(_lineCount).join('\n');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
`<article class="${classes}" data-session="${escapedName}" tabindex="0" role="listitem" aria-label="${escapedName}">` +
|
`<article class="${classes}" data-session="${escapedName}" tabindex="0" role="listitem" aria-label="${escapedName}">` +
|
||||||
@@ -652,7 +654,13 @@ function renderGrid(sessions) {
|
|||||||
var currentMode = currentDs.viewMode || 'auto';
|
var currentMode = currentDs.viewMode || 'auto';
|
||||||
if (currentMode === 'fit' && grid) {
|
if (currentMode === 'fit' && grid) {
|
||||||
grid.classList.add('session-grid--fit');
|
grid.classList.add('session-grid--fit');
|
||||||
requestAnimationFrame(function() { applyFitLayout(grid); });
|
requestAnimationFrame(function() {
|
||||||
|
applyFitLayout(grid);
|
||||||
|
// Scroll each tile's pre to the bottom so content anchors at the bottom (like a real terminal)
|
||||||
|
grid.querySelectorAll('.tile-body pre').forEach(function(pre) {
|
||||||
|
pre.scrollTop = pre.scrollHeight;
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1003,6 +1011,16 @@ function closeSession() {
|
|||||||
}
|
}
|
||||||
if (overview) overview.style.display = ''; // overview uses view--active (no !important), style.display clears fine
|
if (overview) overview.style.display = ''; // overview uses view--active (no !important), style.display clears fine
|
||||||
|
|
||||||
|
// Reapply fit layout after overview becomes visible again
|
||||||
|
var _closDs = loadDisplaySettings();
|
||||||
|
if ((_closDs.viewMode || 'auto') === 'fit') {
|
||||||
|
var _closGrid = document.getElementById('session-grid');
|
||||||
|
if (_closGrid) {
|
||||||
|
_closGrid.classList.add('session-grid--fit');
|
||||||
|
requestAnimationFrame(function() { applyFitLayout(_closGrid); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const pill = $('session-pill');
|
const pill = $('session-pill');
|
||||||
if (pill) pill.classList.add('hidden');
|
if (pill) pill.classList.add('hidden');
|
||||||
|
|
||||||
|
|||||||
@@ -1380,10 +1380,16 @@ body {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In fit mode, stretch pre to fill the full tile body height (not just bottom-anchor) */
|
/* In fit mode, stretch pre to fill the full tile body height and anchor content to bottom */
|
||||||
.session-grid--fit .tile-body pre {
|
.session-grid--fit .tile-body pre {
|
||||||
top: 0; /* stretch to fill full tile body height in fit mode */
|
top: 0; /* stretch to fill full tile body height in fit mode */
|
||||||
overflow-y: auto; /* scroll if content exceeds the tile */
|
overflow-y: scroll; /* enable scrollTop positioning (content anchored via JS scrollTop=scrollHeight) */
|
||||||
|
scrollbar-width: none; /* Firefox: hide scrollbar */
|
||||||
|
-ms-overflow-style: none; /* IE/Edge: hide scrollbar */
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-grid--fit .tile-body pre::-webkit-scrollbar {
|
||||||
|
display: none; /* Chrome/Safari: hide scrollbar */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
|
|||||||
@@ -2212,3 +2212,32 @@ test('applyFitLayout is called via requestAnimationFrame for correct timing', ()
|
|||||||
'applyFitLayout must be deferred via requestAnimationFrame'
|
'applyFitLayout must be deferred via requestAnimationFrame'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- Fit view bug fixes: closeSession reapply, more lines, bottom-anchor ---
|
||||||
|
|
||||||
|
test('closeSession reapplies fit layout when returning to dashboard', () => {
|
||||||
|
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
|
||||||
|
const fnStart = source.indexOf('function closeSession');
|
||||||
|
assert.ok(fnStart !== -1, 'closeSession function must exist');
|
||||||
|
const fnBody = source.substring(fnStart, fnStart + 1500);
|
||||||
|
assert.ok(
|
||||||
|
fnBody.includes('applyFitLayout'),
|
||||||
|
'closeSession must call applyFitLayout for fit mode when returning to dashboard'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buildTileHTML shows up to 80 lines in fit mode', () => {
|
||||||
|
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
|
||||||
|
assert.ok(
|
||||||
|
source.includes('-80') || source.includes('(-80)'),
|
||||||
|
'app.js must use -80 slice for fit mode to show up to 80 lines'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('CSS style.css has scrollbar-width none for fit mode pre to hide scrollbar', () => {
|
||||||
|
const source = fs.readFileSync(new URL('../style.css', import.meta.url), 'utf8');
|
||||||
|
assert.ok(
|
||||||
|
source.includes('scrollbar-width: none'),
|
||||||
|
'style.css must have scrollbar-width: none for hidden scrollbar in fit mode pre'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user