fix: trim trailing blank lines from tile/sidebar snapshot previews
Sessions with cursor near the top (e.g. just a prompt) have 18+ empty lines below. slice(-20) grabbed all blanks, making previews appear empty. Fix: pop trailing blank lines after slicing so the pre element contains only meaningful content. CSS bottom:0 anchors it correctly. Tests: 4 new tests cover content-at-top trimming and all-blank edge case.
This commit is contained in:
+15
-2
@@ -526,7 +526,14 @@ function buildTileHTML(session, index, mobile) {
|
|||||||
// Last N lines of snapshot — show more in fit mode so tall tiles fill
|
// Last N lines of snapshot — show more in fit mode so tall tiles fill
|
||||||
const snapshot = session.snapshot || '';
|
const snapshot = session.snapshot || '';
|
||||||
var _lineCount = (ds.viewMode === 'fit') ? -80 : -20;
|
var _lineCount = (ds.viewMode === 'fit') ? -80 : -20;
|
||||||
const lastLines = snapshot.split('\n').slice(_lineCount).join('\n');
|
var lines = snapshot.split('\n').slice(_lineCount);
|
||||||
|
// Trim trailing blank lines — sessions with cursor near the top have mostly empty
|
||||||
|
// lines at the bottom, making the preview appear blank. Removing them lets the
|
||||||
|
// CSS `position: absolute; bottom: 0` anchor the meaningful content correctly.
|
||||||
|
while (lines.length > 0 && lines[lines.length - 1].trim() === '') {
|
||||||
|
lines.pop();
|
||||||
|
}
|
||||||
|
const lastLines = lines.join('\n');
|
||||||
|
|
||||||
const sourceUrlAttr = session.sourceUrl ? ` data-source-url="${escapeHtml(session.sourceUrl)}"` : '';
|
const sourceUrlAttr = session.sourceUrl ? ` data-source-url="${escapeHtml(session.sourceUrl)}"` : '';
|
||||||
return (
|
return (
|
||||||
@@ -577,7 +584,13 @@ function buildSidebarHTML(session, currentSession) {
|
|||||||
|
|
||||||
// Last 20 lines of snapshot
|
// Last 20 lines of snapshot
|
||||||
const snapshot = session.snapshot || '';
|
const snapshot = session.snapshot || '';
|
||||||
const lastLines = snapshot.split('\n').slice(-20).join('\n');
|
var sidebarLines = snapshot.split('\n').slice(-20);
|
||||||
|
// Trim trailing blank lines — sessions with cursor near the top have mostly empty
|
||||||
|
// lines at the bottom, making the preview appear blank.
|
||||||
|
while (sidebarLines.length > 0 && sidebarLines[sidebarLines.length - 1].trim() === '') {
|
||||||
|
sidebarLines.pop();
|
||||||
|
}
|
||||||
|
const lastLines = sidebarLines.join('\n');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
`<article class="${classes}" data-session="${escapedName}" data-source-url="${escapeHtml(session.sourceUrl || '')}" tabindex="0" role="listitem">` +
|
`<article class="${classes}" data-session="${escapedName}" data-source-url="${escapeHtml(session.sourceUrl || '')}" tabindex="0" role="listitem">` +
|
||||||
|
|||||||
@@ -4287,3 +4287,70 @@ test('CSS style.css .tile-time has opacity transition for crossfade', () => {
|
|||||||
'style.css must have session-tile:hover .tile-time for crossfade'
|
'style.css must have session-tile:hover .tile-time for crossfade'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- Trailing blank line trimming in snapshot previews ---
|
||||||
|
|
||||||
|
test('buildTileHTML trims trailing blank lines so pre ends with last content line', () => {
|
||||||
|
// Simulates a session where cursor is near the top: 2 content lines, then 18 blank lines.
|
||||||
|
// slice(-20) grabs all 20 lines (2 content + 18 blanks).
|
||||||
|
// Without trimming, the <pre> ends with blank lines (last meaningful content is invisible).
|
||||||
|
// With trimming, the <pre> ends with the last non-blank line.
|
||||||
|
const contentLines = ['$ tunnel up', ' forwarding 8443...'];
|
||||||
|
const blankLines = new Array(18).fill('');
|
||||||
|
const snapshot = contentLines.concat(blankLines).join('\n');
|
||||||
|
|
||||||
|
const session = { name: 'tunnel', snapshot };
|
||||||
|
const html = app.buildTileHTML(session, 0, false);
|
||||||
|
|
||||||
|
// The pre element must contain the content lines
|
||||||
|
assert.ok(html.includes('tunnel up'), 'tile preview must include content line');
|
||||||
|
assert.ok(html.includes('forwarding 8443'), 'tile preview must include second content line');
|
||||||
|
|
||||||
|
// The pre should NOT end with blank lines (trailing whitespace trimmed)
|
||||||
|
const preMatch = html.match(/<pre>([\s\S]*?)<\/pre>/);
|
||||||
|
assert.ok(preMatch, '<pre> element must exist in tile HTML');
|
||||||
|
const preContent = preMatch[1];
|
||||||
|
const lastLine = preContent.split('\n').pop();
|
||||||
|
assert.ok(lastLine.trim() !== '',
|
||||||
|
'last line of pre content must not be blank after trimming trailing blank lines');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buildSidebarHTML trims trailing blank lines so pre ends with last content line', () => {
|
||||||
|
// Same scenario: 2 content lines at top, 18 blank lines below.
|
||||||
|
const contentLines = ['$ tunnel up', ' forwarding 8443...'];
|
||||||
|
const blankLines = new Array(18).fill('');
|
||||||
|
const snapshot = contentLines.concat(blankLines).join('\n');
|
||||||
|
|
||||||
|
const session = { name: 'tunnel', snapshot, bell: { unseen_count: 0 } };
|
||||||
|
const html = app.buildSidebarHTML(session, '');
|
||||||
|
|
||||||
|
assert.ok(html.includes('tunnel up'), 'sidebar preview must include content line');
|
||||||
|
assert.ok(html.includes('forwarding 8443'), 'sidebar preview must include second content line');
|
||||||
|
|
||||||
|
const preMatch = html.match(/<pre>([\s\S]*?)<\/pre>/);
|
||||||
|
assert.ok(preMatch, '<pre> element must exist in sidebar HTML');
|
||||||
|
const preContent = preMatch[1];
|
||||||
|
const lastLine = preContent.split('\n').pop();
|
||||||
|
assert.ok(lastLine.trim() !== '',
|
||||||
|
'last line of sidebar pre content must not be blank after trimming trailing blank lines');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buildTileHTML renders empty pre when snapshot is entirely blank lines', () => {
|
||||||
|
// Edge case: completely empty session — pre should be empty, not show garbage
|
||||||
|
const snapshot = new Array(30).fill('').join('\n');
|
||||||
|
const session = { name: 'empty-session', snapshot };
|
||||||
|
const html = app.buildTileHTML(session, 0, false);
|
||||||
|
const preMatch = html.match(/<pre>([\s\S]*?)<\/pre>/);
|
||||||
|
assert.ok(preMatch, '<pre> element must exist');
|
||||||
|
// pre content should be empty string (all blanks trimmed away)
|
||||||
|
assert.strictEqual(preMatch[1], '', 'pre should be empty when snapshot has only blank lines');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('buildSidebarHTML renders empty pre when snapshot is entirely blank lines', () => {
|
||||||
|
const snapshot = new Array(25).fill('').join('\n');
|
||||||
|
const session = { name: 'empty-session', snapshot, bell: { unseen_count: 0 } };
|
||||||
|
const html = app.buildSidebarHTML(session, '');
|
||||||
|
const preMatch = html.match(/<pre>([\s\S]*?)<\/pre>/);
|
||||||
|
assert.ok(preMatch, '<pre> element must exist');
|
||||||
|
assert.strictEqual(preMatch[1], '', 'sidebar pre should be empty when snapshot has only blank lines');
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user