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;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
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 {
|
.scroll-to-bottom-btn:hover {
|
||||||
|
|
@ -235,9 +242,10 @@
|
||||||
.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);
|
||||||
line-height: 1.5;
|
line-height: 1.3;
|
||||||
white-space: var(--wrap-mode);
|
white-space: var(--wrap-mode);
|
||||||
word-wrap: break-word;
|
word-wrap: normal;
|
||||||
|
overflow-wrap: normal;
|
||||||
color: #e0e0e0;
|
color: #e0e0e0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1012,7 +1020,7 @@
|
||||||
columns: 1, // 1, 2, 3, or 'fit'
|
columns: 1, // 1, 2, 3, or 'fit'
|
||||||
fontSize: 12, // px value
|
fontSize: 12, // px value
|
||||||
terminalHeight: 400, // 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();
|
const sessions = await res.json();
|
||||||
sessions.forEach(session => {
|
sessions.forEach(session => {
|
||||||
state.sessions.set(session.id, {
|
const existing = state.sessions.get(session.id);
|
||||||
|
const merged = {
|
||||||
id: session.id,
|
id: session.id,
|
||||||
cwd: session.cwd,
|
cwd: session.cwd,
|
||||||
command: session.command,
|
command: session.command,
|
||||||
output: '',
|
// Preserve any output already received via SSE before fetch completed
|
||||||
outputRenderedLength: 0,
|
output: existing?.output ?? '',
|
||||||
expanded: false,
|
outputRenderedLength: existing?.outputRenderedLength ?? 0,
|
||||||
state: 'ready',
|
// Preserve expansion state
|
||||||
prompts: 0,
|
expanded: existing?.expanded ?? false,
|
||||||
completions: 0,
|
state: existing?.state ?? 'ready',
|
||||||
tools: 0,
|
prompts: existing?.prompts ?? 0,
|
||||||
compressions: 0,
|
completions: existing?.completions ?? 0,
|
||||||
thinking_seconds: 0,
|
tools: existing?.tools ?? 0,
|
||||||
work_seconds: 0,
|
compressions: existing?.compressions ?? 0,
|
||||||
mode: 'normal',
|
thinking_seconds: existing?.thinking_seconds ?? 0,
|
||||||
model: null,
|
work_seconds: existing?.work_seconds ?? 0,
|
||||||
idle_since: null,
|
mode: existing?.mode ?? 'normal',
|
||||||
git_branch: null,
|
model: existing?.model ?? null,
|
||||||
git_files_json: 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();
|
renderSessions();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -1135,13 +1147,36 @@
|
||||||
|
|
||||||
es.addEventListener('initial_state', (e) => {
|
es.addEventListener('initial_state', (e) => {
|
||||||
const data = JSON.parse(e.data);
|
const data = JSON.parse(e.data);
|
||||||
const session = state.sessions.get(data.session_id);
|
let session = state.sessions.get(data.session_id);
|
||||||
if (session) {
|
if (!session) {
|
||||||
// Replace output with current terminal state
|
// Create a placeholder session so we don't drop the state
|
||||||
session.output = data.html;
|
session = {
|
||||||
session.outputRenderedLength = 0; // Force re-render
|
id: data.session_id,
|
||||||
renderSessionOutput(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) => {
|
es.addEventListener('output', (e) => {
|
||||||
|
|
@ -1226,6 +1261,10 @@
|
||||||
// Wait for DOM to update before measuring
|
// Wait for DOM to update before measuring
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
handleSessionResize(Number(sessionId));
|
handleSessionResize(Number(sessionId));
|
||||||
|
// After mount, ensure we start at bottom and initialize UI state
|
||||||
|
scrollToBottom(Number(sessionId));
|
||||||
|
updateScrollButton(Number(sessionId));
|
||||||
|
initSessionOutputUI(Number(sessionId));
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1426,6 +1465,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
||||||
|
// Initialize scroll handlers and button visibility
|
||||||
|
sessionsToRender.forEach(s => {
|
||||||
|
initSessionOutputUI(s.id);
|
||||||
|
updateScrollButton(s.id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSessionOutput(sessionId) {
|
function renderSessionOutput(sessionId) {
|
||||||
|
|
@ -1434,17 +1479,47 @@
|
||||||
|
|
||||||
const $output = document.getElementById(`output-${sessionId}`);
|
const $output = document.getElementById(`output-${sessionId}`);
|
||||||
if ($output) {
|
if ($output) {
|
||||||
// Always full replace since serializeAsHTML() returns full screen state
|
const $outputContainer = document.getElementById(`session-output-${sessionId}`);
|
||||||
$output.innerHTML = session.output;
|
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;
|
session.outputRenderedLength = session.output.length;
|
||||||
|
|
||||||
const $outputContainer = document.getElementById(`session-output-${sessionId}`);
|
if ($outputContainer && session.expanded && shouldStickToBottom) {
|
||||||
if ($outputContainer && session.expanded) {
|
|
||||||
$outputContainer.scrollTop = $outputContainer.scrollHeight;
|
$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) {
|
function updateSessionCard(sessionId) {
|
||||||
const session = state.sessions.get(sessionId);
|
const session = state.sessions.get(sessionId);
|
||||||
if (!session) return;
|
if (!session) return;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue