- Container-level ResizeObserver created feedback loop with fitAddon.fit()
- Removed it, kept initialization-time dimension check only
- Rely on window resize listener for viewport changes
- xterm docs require parent to have dimensions when open() is called
- Added ResizeObserver to wait for non-zero dimensions before initializing
- Added container ResizeObserver to handle resizes (header hide, keyboard)
- Fixed cleanup to properly disconnect observers and handle uninitialized ws
- Don't reconnect when server closes old connection with code 4000
- Code 4000 means new connection was established, not an error
- Prevents infinite reconnection loop between old/new connections
Refs: terminal switching between 4000 error and connected
- Measure parent dimensions and set them on container before term.open()
- Ensures FitAddon gets correct dimensions on initialization
- Prevents 1-row/1-col calculation that breaks scrolling and sizing
- Add docs/api/terminal.md with WebSocket protocol and reset endpoint
- Add docs/features/terminal.md with user guide for persistent sessions
- Add docs/features/terminal-troubleshooting.md with diagnostic steps
- Mark tasks 8.1-8.3 complete
Refs: persistent-terminal-sessions tasks 8.x
TerminalManager was trying to create an asyncio task at module import time,
but no event loop exists yet during import. This caused RuntimeError on startup.
Changes:
- _start_idle_check() now checks if event loop is running before creating task
- If no loop exists, silently skips (will be started lazily)
- Added lazy start call in get_or_create_session() when websocket connects
- Backend: Send ping every 30s from WebSocket endpoint
- Frontend: Respond to pings with pongs, detect missed pings (60s timeout)
- Update type definitions to include 'resetting' status
Refs: persistent-terminal-sessions task 6.4
TypeScript build failed because 'resetting' status was not included
in the onTerminalReady callback type definition.
Updated types in:
- TerminalComponent props
- MobileTerminalWrapper state and callback
- MobileTerminalHeader props
Replace flexbox chains with CSS Grid to give content area definite height:
- grid-template-rows: auto 1fr auto for header/content/keys
- Use 100dvh for proper mobile viewport handling
- Terminal fills content area with position: absolute
- Remove mobile-terminal-shell wrapper (redundant)
- Content area gets real height from grid, fixing FitAddon calculations
- Terminal sessions now persist across WebSocket disconnections
- Added circular output buffer (10KB) for replay on reconnect
- Added idle timeout cleanup (30 minutes)
- Added reset functionality via WebSocket message and HTTP endpoint
- Concurrent connections close old WebSocket when new one connects
- Frontend: Added reset button with confirmation dialog
- Frontend: Handle resetting status and reconnection
Refs: persistent-terminal-sessions
- Make .terminal-container position: relative with overflow: hidden
- Make xterm element absolutely positioned to fill container
- This ensures xterm.js always has concrete dimensions for fitAddon
- Remove conflicting height: 100% !important overrides
- Terminal now properly fills available space and calculates correct rows
The ResizeObserver triggered fit() which changed canvas dimensions,
triggering the observer again in an infinite loop. We already have
window resize handling and delayed fit() calls, so the observer was
redundant.
- Change .terminal-wrapper.mobile from height:100% to flex:1 for proper flex behavior
- Add explicit width/height to xterm-viewport and xterm-screen to prevent overflow
- Add delayed fit() at 4s to resize after mobile header auto-hides
- Remove min-height:100% which caused overflow issues
- Add SessionCard component with status indicators, actions, and confirmation dialogs
- Add SessionList component with grouping (active/recent) and filtering
- Refactor dashboard.tsx to use unified components
- Refactor sessions.tsx to use unified components
- Remove duplicated session rendering logic from both pages
Refs: session-list-overhaul tasks 1-4
- After changing font size and calling fit(), send resize message to WebSocket
- OpenCode now receives correct terminal dimensions after font size adjustment
- Fixes issue where OpenCode UI didn't fill available space after font resize
- Remove ResizeObserver that was causing infinite resize loop
- Add display: flex to terminal-container for proper child sizing
- Use flex: 1 on .xterm element instead of height: 100%
- Remove explicit height/width from xterm-viewport and xterm-screen
- Let flexbox handle the layout naturally
- Add ResizeObserver to watch terminal container and trigger fit() on size changes
- Add delayed second fit() call 500ms after initialization
- Remove initial setTimeout resize in favor of ResizeObserver
- Ensure resizeObserver is cleaned up on unmount
- Change from named volume instance_data to host bind mount
- Consistent with docker-compose.yml fix for clone mode
Refs: clone mode repo files not visible in containers
- Use double requestAnimationFrame before initial fitAddon.fit() to ensure DOM is settled
- Add display: flex to mobile-terminal-content for proper child sizing
- Add width: 100% to terminal-wrapper.mobile
- Ensure terminal fills parent container both horizontally and vertically
- Add width: 100% to xterm, xterm-viewport, and xterm-screen
- Add explicit canvas display: block for proper sizing
- Remove padding from terminal-container on mobile
- Add min-height: 100% to terminal-wrapper.mobile
- Ensure xterm.js internal elements fill parent container
- Change .terminal-page height from 100vh to 100% to fit within shell layout
- Add display: flex and min-height: 0 to .shell-content to allow flex children to expand
- Terminal container now properly fills available vertical space
- Reduce MIN_FONT_SIZE from 16 to 10 for better range
- Remove calculateFontSize from useEffect dependencies to prevent
terminal re-initialization when font size changes
- Font size changes now update xterm options directly without
disposing/recreating the terminal (no WebSocket reconnection)
- Add null checks and try/catch around fitAddon.fit() to prevent viewport errors
- Use requestAnimationFrame to ensure DOM is stable before fitting
- Remove isMobile condition from font size buttons in TerminalComponent
- Font size controls now visible on both mobile and desktop terminals
- Add safety check after rendering compose template to ensure REPO_PATH is mounted
- If compose template lacks volume mount, auto-add default mount to /workspace
- Add cloned repo verification to catch empty clone directories
Refs: clone mode repo not appearing in container workspace
- TerminalComponent: expose changeFontSize via onTerminalReady callback
- MobileTerminalWrapper: pass changeFontSize to header
- MobileTerminalHeader: add A- and A+ font size buttons
- CSS: collapse header height/padding/margin/border when hidden to reclaim space
Backend:
- Add created_at to get_user_sessions response
Frontend:
- Hide tunnel error badges, probe output, and 'Recreate Tunnel' button for terminal-only sessions
- Show session start time (created_at) in active sessions list
- Show repository configuration (clone_mode, branch) for each session
- Skip health check polling for terminal-only sessions
- Update Session type to include created_at field