diff --git a/public/index.html b/public/index.html
index 4692d6e..c352e76 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1134,6 +1134,14 @@
// Reset rendered length when collapsing or expanding (element gets recreated)
session.outputRenderedLength = 0;
renderSessions();
+
+ // If expanding, resize the PTY to fit the viewport
+ if (session.expanded) {
+ // Wait for DOM to update before measuring
+ setTimeout(() => {
+ handleSessionResize(Number(sessionId));
+ }, 0);
+ }
}
};
@@ -1978,6 +1986,74 @@
// Auto-scroll control
const scrollListeners = new Map();
+ // Viewport-based PTY resize
+ let resizeDebounceTimer = null;
+
+ function measureTerminalCols() {
+ // Create a hidden span with monospace font to measure character width
+ const $measure = document.createElement('span');
+ $measure.style.fontFamily = '"SF Mono", Monaco, "Cascadia Code", "Courier New", monospace';
+ $measure.style.fontSize = `${state.settings.fontSize}px`;
+ $measure.style.lineHeight = '1.5';
+ $measure.style.visibility = 'hidden';
+ $measure.style.position = 'absolute';
+ $measure.textContent = 'X'.repeat(100); // Measure 100 chars
+ document.body.appendChild($measure);
+
+ const charWidth = $measure.offsetWidth / 100;
+ document.body.removeChild($measure);
+
+ // Get terminal container width (accounting for padding)
+ const $container = document.querySelector('.session-output');
+ if (!$container) {
+ return 80; // Default fallback
+ }
+
+ const containerWidth = $container.clientWidth - 32; // 16px padding on each side
+ const cols = Math.floor(containerWidth / charWidth);
+
+ // Clamp to reasonable bounds
+ return Math.max(40, Math.min(cols, 300));
+ }
+
+ async function resizeSessionPTY(sessionId, cols, rows) {
+ try {
+ const res = await fetch(`/api/sessions/${sessionId}/resize`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ cols, rows }),
+ });
+ if (!res.ok) {
+ console.error('Failed to resize session PTY:', await res.text());
+ }
+ } catch (error) {
+ console.error('Error resizing session PTY:', error);
+ }
+ }
+
+ function handleSessionResize(sessionId) {
+ const cols = measureTerminalCols();
+ const rows = 24; // Reasonable default row count
+ resizeSessionPTY(sessionId, cols, rows);
+ }
+
+ function handleWindowResize() {
+ if (resizeDebounceTimer) {
+ clearTimeout(resizeDebounceTimer);
+ }
+
+ resizeDebounceTimer = setTimeout(() => {
+ // Resize all expanded sessions
+ state.sessions.forEach((session) => {
+ if (session.expanded) {
+ handleSessionResize(session.id);
+ }
+ });
+ }, 300); // Debounce for 300ms
+ }
+
function attachScrollListener(sessionId, $outputContainer) {
// Remove existing listener if present
const existing = scrollListeners.get(sessionId);
@@ -2041,6 +2117,9 @@
loadSettings();
applySettings();
connectSSE();
+
+ // Set up window resize listener for PTY viewport resize
+ window.addEventListener('resize', handleWindowResize);