Add multiple fixes
This commit is contained in:
parent
0ac5eec30d
commit
116847ab58
1 changed files with 105 additions and 30 deletions
|
|
@ -220,6 +220,13 @@
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.session-output.scrolled-up .scroll-to-bottom-btn {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.scroll-to-bottom-btn:hover {
|
||||
|
|
@ -235,9 +242,10 @@
|
|||
.terminal {
|
||||
font-family: "SF Mono", Monaco, "Cascadia Code", "Courier New", monospace;
|
||||
font-size: var(--font-size);
|
||||
line-height: 1.5;
|
||||
line-height: 1.3;
|
||||
white-space: var(--wrap-mode);
|
||||
word-wrap: break-word;
|
||||
word-wrap: normal;
|
||||
overflow-wrap: normal;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
|
|
@ -1012,7 +1020,7 @@
|
|||
columns: 1, // 1, 2, 3, or 'fit'
|
||||
fontSize: 12, // px value
|
||||
terminalHeight: 400, // px value
|
||||
wrapText: true, // true = pre-wrap, false = pre (horizontal scroll)
|
||||
wrapText: false, // true = pre-wrap, false = pre (horizontal scroll)
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -1043,26 +1051,30 @@
|
|||
}
|
||||
const sessions = await res.json();
|
||||
sessions.forEach(session => {
|
||||
state.sessions.set(session.id, {
|
||||
const existing = state.sessions.get(session.id);
|
||||
const merged = {
|
||||
id: session.id,
|
||||
cwd: session.cwd,
|
||||
command: session.command,
|
||||
output: '',
|
||||
outputRenderedLength: 0,
|
||||
expanded: false,
|
||||
state: 'ready',
|
||||
prompts: 0,
|
||||
completions: 0,
|
||||
tools: 0,
|
||||
compressions: 0,
|
||||
thinking_seconds: 0,
|
||||
work_seconds: 0,
|
||||
mode: 'normal',
|
||||
model: null,
|
||||
idle_since: null,
|
||||
git_branch: null,
|
||||
git_files_json: null,
|
||||
});
|
||||
// Preserve any output already received via SSE before fetch completed
|
||||
output: existing?.output ?? '',
|
||||
outputRenderedLength: existing?.outputRenderedLength ?? 0,
|
||||
// Preserve expansion state
|
||||
expanded: existing?.expanded ?? false,
|
||||
state: existing?.state ?? 'ready',
|
||||
prompts: existing?.prompts ?? 0,
|
||||
completions: existing?.completions ?? 0,
|
||||
tools: existing?.tools ?? 0,
|
||||
compressions: existing?.compressions ?? 0,
|
||||
thinking_seconds: existing?.thinking_seconds ?? 0,
|
||||
work_seconds: existing?.work_seconds ?? 0,
|
||||
mode: existing?.mode ?? 'normal',
|
||||
model: existing?.model ?? null,
|
||||
idle_since: existing?.idle_since ?? null,
|
||||
git_branch: existing?.git_branch ?? null,
|
||||
git_files_json: existing?.git_files_json ?? null,
|
||||
};
|
||||
state.sessions.set(session.id, merged);
|
||||
});
|
||||
renderSessions();
|
||||
} catch (error) {
|
||||
|
|
@ -1135,13 +1147,36 @@
|
|||
|
||||
es.addEventListener('initial_state', (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
const session = state.sessions.get(data.session_id);
|
||||
if (session) {
|
||||
let session = state.sessions.get(data.session_id);
|
||||
if (!session) {
|
||||
// Create a placeholder session so we don't drop the state
|
||||
session = {
|
||||
id: data.session_id,
|
||||
cwd: '',
|
||||
command: 'claude',
|
||||
output: '',
|
||||
outputRenderedLength: 0,
|
||||
expanded: false,
|
||||
state: 'ready',
|
||||
prompts: 0,
|
||||
completions: 0,
|
||||
tools: 0,
|
||||
compressions: 0,
|
||||
thinking_seconds: 0,
|
||||
work_seconds: 0,
|
||||
mode: 'normal',
|
||||
model: null,
|
||||
idle_since: null,
|
||||
git_branch: null,
|
||||
git_files_json: null,
|
||||
};
|
||||
state.sessions.set(data.session_id, session);
|
||||
renderSessions();
|
||||
}
|
||||
// Replace output with current terminal state
|
||||
session.output = data.html;
|
||||
session.outputRenderedLength = 0; // Force re-render
|
||||
renderSessionOutput(data.session_id);
|
||||
}
|
||||
});
|
||||
|
||||
es.addEventListener('output', (e) => {
|
||||
|
|
@ -1226,6 +1261,10 @@
|
|||
// Wait for DOM to update before measuring
|
||||
setTimeout(() => {
|
||||
handleSessionResize(Number(sessionId));
|
||||
// After mount, ensure we start at bottom and initialize UI state
|
||||
scrollToBottom(Number(sessionId));
|
||||
updateScrollButton(Number(sessionId));
|
||||
initSessionOutputUI(Number(sessionId));
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -1426,6 +1465,12 @@
|
|||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
// Initialize scroll handlers and button visibility
|
||||
sessionsToRender.forEach(s => {
|
||||
initSessionOutputUI(s.id);
|
||||
updateScrollButton(s.id);
|
||||
});
|
||||
}
|
||||
|
||||
function renderSessionOutput(sessionId) {
|
||||
|
|
@ -1434,17 +1479,47 @@
|
|||
|
||||
const $output = document.getElementById(`output-${sessionId}`);
|
||||
if ($output) {
|
||||
// Always full replace since serializeAsHTML() returns full screen state
|
||||
const $outputContainer = document.getElementById(`session-output-${sessionId}`);
|
||||
const shouldStickToBottom =
|
||||
!!($outputContainer && session.expanded && isAtBottom($outputContainer));
|
||||
|
||||
// Only mutate DOM when expanded to reduce work
|
||||
if (session.expanded) {
|
||||
$output.innerHTML = session.output;
|
||||
}
|
||||
session.outputRenderedLength = session.output.length;
|
||||
|
||||
const $outputContainer = document.getElementById(`session-output-${sessionId}`);
|
||||
if ($outputContainer && session.expanded) {
|
||||
if ($outputContainer && session.expanded && shouldStickToBottom) {
|
||||
$outputContainer.scrollTop = $outputContainer.scrollHeight;
|
||||
}
|
||||
|
||||
// Update scroll-to-bottom button visibility
|
||||
updateScrollButton(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
function isAtBottom(el, threshold = 8) {
|
||||
return el.scrollTop + el.clientHeight >= el.scrollHeight - threshold;
|
||||
}
|
||||
|
||||
function updateScrollButton(sessionId) {
|
||||
const $outputContainer = document.getElementById(`session-output-${sessionId}`);
|
||||
if (!$outputContainer) return;
|
||||
const nearBottom = isAtBottom($outputContainer);
|
||||
if (nearBottom) {
|
||||
$outputContainer.classList.remove('scrolled-up');
|
||||
} else {
|
||||
$outputContainer.classList.add('scrolled-up');
|
||||
}
|
||||
}
|
||||
|
||||
function initSessionOutputUI(sessionId) {
|
||||
const $outputContainer = document.getElementById(`session-output-${sessionId}`);
|
||||
if (!$outputContainer) return;
|
||||
// Attach scroll listener to toggle the button visibility
|
||||
$outputContainer.addEventListener('scroll', () => updateScrollButton(sessionId));
|
||||
}
|
||||
|
||||
function updateSessionCard(sessionId) {
|
||||
const session = state.sessions.get(sessionId);
|
||||
if (!session) return;
|
||||
|
|
|
|||
Loading…
Reference in a new issue