Keep terminal scrolled to bottom
This commit is contained in:
parent
9e8a275831
commit
5597502a8c
1 changed files with 4 additions and 74 deletions
|
|
@ -38,6 +38,7 @@
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
|
|
@ -230,11 +231,6 @@
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-to-bottom-btn.hidden {
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.terminal {
|
.terminal {
|
||||||
font-family: "SF Mono", Monaco, "Cascadia Code", "Courier New", monospace;
|
font-family: "SF Mono", Monaco, "Cascadia Code", "Courier New", monospace;
|
||||||
font-size: var(--font-size);
|
font-size: var(--font-size);
|
||||||
|
|
@ -1032,7 +1028,6 @@
|
||||||
idle_since: null,
|
idle_since: null,
|
||||||
git_branch: null,
|
git_branch: null,
|
||||||
git_files_json: null,
|
git_files_json: null,
|
||||||
autoScroll: true,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
renderSessions();
|
renderSessions();
|
||||||
|
|
@ -1088,10 +1083,6 @@
|
||||||
idle_since: null,
|
idle_since: null,
|
||||||
git_branch: null,
|
git_branch: null,
|
||||||
git_files_json: null,
|
git_files_json: null,
|
||||||
// Auto-scroll state is per-session and ephemeral - not persisted to localStorage.
|
|
||||||
// This is intentional: users may want different auto-scroll settings for different
|
|
||||||
// sessions, and persisting would require tracking state by session ID which is complex.
|
|
||||||
autoScroll: true,
|
|
||||||
});
|
});
|
||||||
renderSessions();
|
renderSessions();
|
||||||
});
|
});
|
||||||
|
|
@ -1371,7 +1362,7 @@
|
||||||
</div>
|
</div>
|
||||||
${renderStatsWidget(s)}
|
${renderStatsWidget(s)}
|
||||||
<div class="session-output" id="session-output-${s.id}">
|
<div class="session-output" id="session-output-${s.id}">
|
||||||
<button class="scroll-to-bottom-btn ${s.autoScroll ? 'hidden' : ''}"
|
<button class="scroll-to-bottom-btn"
|
||||||
onclick="scrollToBottom(${s.id}); event.stopPropagation();"
|
onclick="scrollToBottom(${s.id}); event.stopPropagation();"
|
||||||
title="Scroll to bottom">
|
title="Scroll to bottom">
|
||||||
↓
|
↓
|
||||||
|
|
@ -1381,13 +1372,6 @@
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
||||||
// Attach scroll listeners after rendering
|
|
||||||
sessionsToRender.forEach(s => {
|
|
||||||
const $output = document.getElementById(`session-output-${s.id}`);
|
|
||||||
if ($output) {
|
|
||||||
attachScrollListener(s.id, $output);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSessionOutput(sessionId) {
|
function renderSessionOutput(sessionId) {
|
||||||
|
|
@ -1404,13 +1388,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const $outputContainer = document.getElementById(`session-output-${sessionId}`);
|
const $outputContainer = document.getElementById(`session-output-${sessionId}`);
|
||||||
if ($outputContainer) {
|
if ($outputContainer && session.expanded) {
|
||||||
// Only auto-scroll if session is expanded and autoScroll is enabled
|
$outputContainer.scrollTop = $outputContainer.scrollHeight;
|
||||||
if (session.expanded && session.autoScroll) {
|
|
||||||
$outputContainer.scrollTop = $outputContainer.scrollHeight;
|
|
||||||
}
|
|
||||||
// Re-attach scroll listener after DOM update
|
|
||||||
attachScrollListener(sessionId, $outputContainer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2029,9 +2008,6 @@
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-scroll control
|
|
||||||
const scrollListeners = new Map();
|
|
||||||
|
|
||||||
// Viewport-based PTY resize
|
// Viewport-based PTY resize
|
||||||
let resizeDebounceTimer = null;
|
let resizeDebounceTimer = null;
|
||||||
|
|
||||||
|
|
@ -2100,59 +2076,13 @@
|
||||||
}, 300); // Debounce for 300ms
|
}, 300); // Debounce for 300ms
|
||||||
}
|
}
|
||||||
|
|
||||||
function attachScrollListener(sessionId, $outputContainer) {
|
|
||||||
// Remove existing listener if present
|
|
||||||
const existing = scrollListeners.get(sessionId);
|
|
||||||
if (existing) {
|
|
||||||
$outputContainer.removeEventListener('scroll', existing);
|
|
||||||
}
|
|
||||||
|
|
||||||
const listener = () => {
|
|
||||||
const session = state.sessions.get(sessionId);
|
|
||||||
if (!session) return;
|
|
||||||
|
|
||||||
const { scrollTop, clientHeight, scrollHeight } = $outputContainer;
|
|
||||||
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 50;
|
|
||||||
|
|
||||||
if (session.autoScroll !== isAtBottom) {
|
|
||||||
session.autoScroll = isAtBottom;
|
|
||||||
updateScrollButton(sessionId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$outputContainer.addEventListener('scroll', listener);
|
|
||||||
scrollListeners.set(sessionId, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.scrollToBottom = (sessionId) => {
|
window.scrollToBottom = (sessionId) => {
|
||||||
const session = state.sessions.get(sessionId);
|
|
||||||
if (!session) return;
|
|
||||||
|
|
||||||
// Scroll to bottom
|
|
||||||
const $outputContainer = document.getElementById(`session-output-${sessionId}`);
|
const $outputContainer = document.getElementById(`session-output-${sessionId}`);
|
||||||
if ($outputContainer) {
|
if ($outputContainer) {
|
||||||
$outputContainer.scrollTop = $outputContainer.scrollHeight;
|
$outputContainer.scrollTop = $outputContainer.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-enable auto-scroll
|
|
||||||
session.autoScroll = true;
|
|
||||||
updateScrollButton(sessionId);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function updateScrollButton(sessionId) {
|
|
||||||
const session = state.sessions.get(sessionId);
|
|
||||||
if (!session) return;
|
|
||||||
|
|
||||||
const $btn = document.querySelector(`#session-output-${sessionId} .scroll-to-bottom-btn`);
|
|
||||||
if ($btn) {
|
|
||||||
if (session.autoScroll) {
|
|
||||||
$btn.classList.add('hidden');
|
|
||||||
} else {
|
|
||||||
$btn.classList.remove('hidden');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
loadSettings();
|
loadSettings();
|
||||||
applySettings();
|
applySettings();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue