这是indexloc提供的服务,不要输入任何密码
Skip to content

Conversation

@steipete
Copy link
Collaborator

Summary

  • Removes ResizeCoordinator for a simpler, more direct implementation
  • Fixes continuous resize loops on mobile devices that caused scrolling and performance issues
  • Mobile terminals now resize exactly once, 200ms after initialization

Problem

Based on debugging session findings, mobile devices were experiencing continuous resize loops due to:

  • Dynamic viewport height updates (browser chrome appearing/disappearing)
  • Multiple resize observers and event listeners
  • Terminal re-rendering entire scroll buffer on each resize
  • Cascading effects from viewport changes triggering more resize events

Solution

  • Mobile: Single resize 200ms after setup, then all resize events blocked
  • Desktop: Normal resize behavior with observers (unchanged)
  • Removed complex ResizeCoordinator in favor of simple state tracking
  • Added debug logging to track resize behavior

Test Plan

  • Test on iPhone Safari - verify no continuous scrolling
  • Test on Android Chrome - verify stable terminal view
  • Test orientation changes on mobile - should not trigger resize
  • Test on desktop - resize should work normally
  • Verify keyboard appearance on mobile doesn't cause resize loop

@github-actions
Copy link

github-actions bot commented Jul 11, 2025

🔍 Code Quality Report

This comment is automatically updated with linting results from CI.

Node.js Biome Formatting ✅ Status: Passed

Node.js Biome Linting ✅ Status: Passed

Node.js Test Coverage ✅ Status: Passed

Client Coverage:
• Lines: 0%
• Functions: 0%
• Branches: 0%
• Statements: 0%

Server Coverage:
• Lines: 8.75%
• Functions: 54.06%
• Branches: 72.29%
• Statements: 8.75%

Mac Formatting (SwiftFormat) ✅ Status: Passed

Mac Linting (SwiftLint) ✅ Status: Passed

macOS Test Coverage ❌ Status: Failed

• Coverage: 0%

@claude
Copy link

claude bot commented Jul 11, 2025

Claude review from 7/11/2025, 4:51:01 AM (outdated - click to expand)
Claude review from 7/11/2025, 4:51:01 AM (outdated - click to expand)
Claude review from 7/11/2025, 4:51:01 AM (outdated - click to expand)
Claude review from 7/11/2025, 4:51:01 AM (outdated - click to expand)
Claude review from 7/11/2025, 4:51:01 AM (outdated - click to expand)

Claude finished @steipete's task —— View job


Todo List:

  • Read terminal.ts to understand new resize implementation
  • Review deleted resize-coordinator.ts to understand what was removed
  • Review test changes in terminal.test.ts
  • Check other modified files for consistency
  • Review new documentation files
  • Provide comprehensive code review

📋 Summary

This PR addresses mobile terminal resize loops by removing the complex ResizeCoordinator and implementing a simpler, more direct resize strategy. The approach is pragmatic: mobile devices get a single resize after initialization, while desktop retains full dynamic resizing.

✅ Positive Aspects

  • Pragmatic solution: The "resize once on mobile" approach is simple and effective
  • Clear separation: Mobile vs desktop behavior is well-defined and easy to understand
  • Improved debugging: Added extensive debug logging to track resize behavior
  • Documentation: Included comprehensive debugging session notes explaining the problem and solution
  • Code simplification: Removing ResizeCoordinator reduces complexity and potential race conditions
  • Test coverage: Tests were updated to match the new behavior

🔍 Areas for Improvement

Code Quality

  • Magic numbers: The 200ms delay for mobile resize and 85vh fixed height could be constants with explanatory names
  • Repeated mobile detection: The window.innerWidth < 768 && 'ontouchstart' in window check appears multiple times
  • Debug logging: Consider using a debug flag constant instead of checking logger.debug availability each time

Potential Issues

  • Race condition: The 200ms timeout for mobile resize might fire before the terminal is fully initialized
  • Orientation changes: Mobile devices won't respond to orientation changes, which could leave users with suboptimal layouts
  • Tablet behavior: Devices with width >= 768px but with touch support will behave as desktop, which may not be ideal
  • Browser compatibility: The mobile detection using ontouchstart might miss some modern touch devices

Performance

  • The repeated isMobile checks on every resize request could be cached for the session
  • Multiple requestAnimationFrame calls could potentially queue up if resize is called rapidly

Testing

  • No explicit tests for the mobile-specific resize behavior
  • The 200ms delay makes testing timing-dependent

💡 Suggestions

  1. Extract constants for magic values:
// In terminal.ts
private static readonly MOBILE_RESIZE_DELAY_MS = 200;
private static readonly MOBILE_BREAKPOINT_WIDTH = 768;

// In session-view.ts
private static readonly MOBILE_TERMINAL_HEIGHT = '85vh';
  1. Create a shared mobile detection utility:
// utils/device-detection.ts
export function isMobileDevice(): boolean {
  return window.innerWidth < 768 && 'ontouchstart' in window;
}
  1. Add safeguard for the mobile resize timeout:
// In setupResize()
setTimeout(() => {
  if (!this.terminal || !this.container) {
    logger.warn('[Terminal] Mobile resize aborted - terminal not ready');
    return;
  }
  logger.debug('[Terminal] Mobile: Executing single resize now');
  this.fitTerminal('initial-mobile-only');
}, MOBILE_RESIZE_DELAY_MS);
  1. Consider adding a user-facing option for mobile users to manually trigger resize:

    • Could be useful for orientation changes or if the initial sizing is problematic
    • Perhaps a button in the mobile UI to "Refresh terminal size"
  2. Consider more robust mobile detection:

const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) || 
                 (window.innerWidth < 768 && navigator.maxTouchPoints > 0);

🎯 Priority Items

  1. Add error handling for the mobile resize timeout - ensure terminal is ready before resizing
  2. Extract mobile detection to a shared utility to ensure consistency
  3. Define constants for magic numbers (200ms, 768px, 85vh)
  4. Add tests specifically for mobile resize behavior
  5. Consider UX improvement for orientation changes - perhaps show a message to users that they need to refresh for optimal layout

Overall, this is a solid pragmatic fix for a complex problem. The simplification of the resize logic should improve stability significantly. The main improvements would be around code organization and edge case handling.


Reviewed commit: 91b40f9
Files changed: 7

cursor[bot]

This comment was marked as outdated.

@github-actions
Copy link

github-actions bot commented Jul 11, 2025

❌ CI Failed

View failed run

Failed Jobs:

  • Node.js CI / Build and Test
    • Failed at: Check test results

@claude
Copy link

claude bot commented Jul 11, 2025

Claude review from 7/11/2025, 5:02:46 AM (outdated - click to expand)
Claude review from 7/11/2025, 5:02:46 AM (outdated - click to expand)
Claude review from 7/11/2025, 5:02:46 AM (outdated - click to expand)
Claude review from 7/11/2025, 5:02:46 AM (outdated - click to expand)

Claude finished @steipete's task —— View job


📋 Summary

This PR effectively addresses the mobile terminal resize loop issue by removing the complex ResizeCoordinator and implementing a simpler, more direct approach. The solution is pragmatic: mobile devices get a single resize 200ms after initialization, while desktop retains full dynamic resizing. This directly fixes one of the three critical performance issues identified in the debugging session with Mario.

✅ Positive Aspects

  • Pragmatic solution: The "resize once on mobile" approach is simple and effective for the problem at hand
  • Clear separation: Mobile vs desktop behavior is well-defined with consistent detection logic
  • Improved debugging: Added extensive debug logging to track resize behavior
  • Documentation: Included comprehensive debugging notes explaining the problem and solution
  • Code simplification: Removing ResizeCoordinator eliminates ~119 lines of complex coordination logic
  • Test coverage: Tests were updated to verify the new resize optimization behavior
  • Performance fix: Directly addresses the exponential data growth issue from continuous re-rendering

🔍 Areas for Improvement

Code Quality Issues

  1. Inconsistent mobile detection: Different logic between files

    • terminal.ts: Uses window.innerWidth < 768 && 'ontouchstart' in window
    • index.html: Uses user agent sniffing and navigator.maxTouchPoints
    • This could cause edge cases where parts of the app disagree on whether a device is mobile
  2. Critical bug - Mobile resize never executes:

    • The isMobile class property is never set to true in setupResize()
    • Line 568 uses a local variable const isMobile instead of this.isMobile
    • This means shouldResize() at line 264 will never block subsequent resizes on mobile
    • The mobile resize timeout at line 576 is also not tracked for cleanup
  3. Magic numbers:

    • 200ms delay for mobile resize
    • 768px mobile breakpoint
    • 85vh fixed height for mobile terminals
    • These should be named constants
  4. Memory leak risk: The setTimeout in setupResize() for mobile is not stored or canceled in cleanup()

Potential Issues

  1. Race condition: The 200ms timeout might fire before the terminal is fully initialized
  2. Orientation changes: Mobile users can't adjust layout when rotating their device
  3. Tablet handling: Devices ≥768px width with touch are treated as desktop
  4. Fixed height limitation: The 85vh height might not be optimal for all mobile browsers/devices

💡 Suggestions

Priority Fix - Mobile Detection:

// In terminal.ts setupResize() - line 568
const isMobile = window.innerWidth < 768 && 'ontouchstart' in window;
this.isMobile = isMobile; // ADD THIS LINE to fix the bug

// Also store the timeout for cleanup
if (isMobile) {
  this.mobileResizeTimeout = setTimeout(() => {
    if (!this.terminal || !this.container) {
      logger.warn('[Terminal] Mobile resize aborted - terminal not ready');
      return;
    }
    logger.debug('[Terminal] Mobile: Executing single resize now');
    this.fitTerminal('initial-mobile-only');
  }, 200);
}

Extract Constants:

// At top of terminal.ts
private static readonly MOBILE_RESIZE_DELAY_MS = 200;
private static readonly MOBILE_BREAKPOINT_WIDTH = 768;

// In session-view.ts
private static readonly MOBILE_TERMINAL_HEIGHT = '85vh';

Unified Mobile Detection:

// utils/device-detection.ts
export function isMobileDevice(): boolean {
  const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
  const isNarrow = window.innerWidth < 768;
  const isMobileUA = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
  
  return (hasTouch && isNarrow) || isMobileUA;
}

Cleanup Improvements:

// Add to terminal.ts class properties
private mobileResizeTimeout: NodeJS.Timeout | null = null;

// In cleanup() method
if (this.mobileResizeTimeout) {
  clearTimeout(this.mobileResizeTimeout);
  this.mobileResizeTimeout = null;
}

🎯 Priority Items

  1. FIX IMMEDIATELY: Set this.isMobile = isMobile in setupResize() - without this, the mobile resize blocking doesn't work at all
  2. Add timeout tracking: Store and cleanup the mobile resize timeout to prevent memory leaks
  3. Unify mobile detection: Create a shared utility to ensure consistent behavior across the app
  4. Define constants: Replace magic numbers with named constants
  5. Add error handling: Ensure terminal is ready before the mobile resize executes

Additional Notes

The PR successfully addresses the resize loop issue identified in the debugging session. The approach of limiting mobile to a single resize is reasonable given the constraints of mobile browsers and their dynamic viewport behavior. However, the critical bug where this.isMobile is never set must be fixed for the solution to work as intended.

Consider adding a manual "refresh layout" button for mobile users who need to adjust after orientation changes, as suggested in the original review.


Reviewed commit: 91b40f9
Files changed: 7

cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

@claude
Copy link

claude bot commented Jul 11, 2025

Claude review from 7/11/2025, 5:26:18 AM (outdated - click to expand)
Claude review from 7/11/2025, 5:26:18 AM (outdated - click to expand)
Claude review from 7/11/2025, 5:26:18 AM (outdated - click to expand)

Claude finished @steipete's task —— View job


📋 Summary

This PR addresses the critical mobile terminal resize loop issue identified in Mario's debugging session. The solution removes the complex ResizeCoordinator class (~119 lines) and implements a simpler, more direct approach where mobile devices receive exactly one resize 200ms after initialization, with all subsequent resizes blocked. This directly fixes one of the three critical performance issues where continuous resize events caused exponential data growth from re-rendering the entire scroll buffer.

✅ Positive Aspects

  • Pragmatic solution: The "resize once on mobile" approach elegantly solves the resize loop problem without complex state management
  • Significant simplification: Removing ResizeCoordinator eliminates potential race conditions and coordination complexity
  • Clear separation of concerns: Mobile vs desktop behavior is cleanly separated with consistent detection logic
  • Improved debugging: Added extensive logging to track resize behavior across different sources
  • Documentation: Included comprehensive debugging notes (architecture-mario.md) explaining the problem and solution
  • Test coverage: Tests were updated to verify the new resize optimization behavior
  • Direct fix: Addresses the root cause of continuous scrolling and performance issues on mobile devices

🔍 Areas for Improvement

Code Quality

  1. Magic numbers without constants: Hard-coded values appear throughout:

    • 200ms delay for mobile resize (terminal.ts:586)
    • 768px mobile breakpoint (terminal.ts:240)
    • 85vh fixed height for mobile terminals (session-view.ts:1081)
  2. Inconsistent mobile detection: Critical discrepancy between files:

    • terminal.ts: Uses new detectMobile() method with user agent + maxTouchPoints
    • index.html: Uses similar logic but with different syntax
    • Previous logic used window.innerWidth < 768 && 'ontouchstart' which differs from current implementation
  3. Repeated mobile detection: The detectMobile() method is called multiple times during resize operations

Potential Issues

  1. Critical bug - Mobile resize blocking never activates:

    • The this.isMobile property is set in requestResize() (line 249)
    • However, on mobile, setupResize() directly calls fitTerminal() via setTimeout, bypassing requestResize()
    • This means this.isMobile may never be true when shouldResize() checks it, allowing continuous resizes
  2. Memory leak risk: The setTimeout in setupResize() (line 586) is not stored or canceled in cleanup()

  3. Race condition: The 200ms timeout might fire before the terminal is fully initialized

  4. Orientation changes: Mobile users cannot adjust layout when rotating their device

  5. Tablet edge cases: Devices ≥768px width with touch support are treated as desktop

Performance

  • Multiple calls to detectMobile() during resize could be optimized by caching the result
  • The mobile detection regex and navigator checks run on every resize request

Security

No security issues identified.

Testing

  • No explicit tests for the mobile-specific resize behavior (200ms delay, single resize)
  • The timing-dependent nature of the mobile resize makes it difficult to test reliably
  • Missing tests for orientation change scenarios

💡 Suggestions

1. Extract Constants

// terminal.ts
private static readonly MOBILE_RESIZE_DELAY_MS = 200;
private static readonly MOBILE_BREAKPOINT_WIDTH = 768;

// session-view.ts  
private static readonly MOBILE_TERMINAL_HEIGHT = '85vh';

2. Fix Mobile State Management

// In setupResize() around line 586
if (this.isMobile) {
  logger.debug('[Terminal] Mobile detected - scheduling single resize in 200ms');
  this.mobileResizeTimeout = setTimeout(() => {
    if (!this.terminal || !this.container) {
      logger.warn('[Terminal] Mobile resize aborted - terminal not ready');
      return;
    }
    logger.debug('[Terminal] Mobile: Executing single resize now');
    // Ensure we go through requestResize to set this.isMobile properly
    this.requestResize('initial-mobile-only');
  }, 200);
}

3. Cleanup Memory Leaks

// Add to terminal.ts class properties
private mobileResizeTimeout: NodeJS.Timeout | null = null;

// In cleanup() method
if (this.mobileResizeTimeout) {
  clearTimeout(this.mobileResizeTimeout);
  this.mobileResizeTimeout = null;
}

4. Cache Mobile Detection

private cachedIsMobile: boolean | null = null;

private detectMobile(): boolean {
  if (this.cachedIsMobile !== null) {
    return this.cachedIsMobile;
  }
  
  this.cachedIsMobile = (
    /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) ||
    (navigator.maxTouchPoints !== undefined && navigator.maxTouchPoints > 1)
  );
  
  return this.cachedIsMobile;
}

5. Consider Manual Resize Option

Add a button in the mobile UI to manually trigger a resize for orientation changes or layout issues.

🎯 Priority Items

  1. FIX CRITICAL: Ensure mobile resize goes through requestResize() instead of directly calling fitTerminal() so that this.isMobile is properly set
  2. Add timeout tracking: Store and cleanup the mobile resize timeout to prevent memory leaks
  3. Define constants: Replace magic numbers (200ms, 768px, 85vh) with named constants
  4. Add error handling: Ensure terminal is ready before the mobile resize executes
  5. Unify mobile detection: Consider creating a shared utility function for consistent mobile detection across the codebase
  6. Add tests: Create tests specifically for mobile resize behavior, including the 200ms delay and single-resize logic

Overall, this is a solid pragmatic fix for a complex problem. The simplification of the resize logic should significantly improve mobile stability. The main concern is ensuring the mobile state is properly managed so the resize blocking actually works as intended.


Reviewed commit: c815168
Files changed: 7

cursor[bot]

This comment was marked as outdated.

@github-actions github-actions bot deleted a comment from claude bot Jul 11, 2025
steipete added 6 commits July 11, 2025 07:38
- Remove ResizeCoordinator entirely for simpler implementation
- Mobile now gets exactly ONE resize 200ms after initialization
- All subsequent resize attempts on mobile are blocked
- Desktop retains normal resize behavior with observers
- Fixes continuous scrolling/re-rendering issue on mobile devices

The issue was that multiple resize triggers (viewport changes, keyboard, orientation) caused cascading resize events on mobile, leading to performance problems and visual glitches.
- Update action plan section with quotes from transcript
- Add emphasis on immediate priority to fix endless scrolling
- Improve formatting for better readability
- Update tests to expect fixed 85vh height on mobile
- Update tests to expect NO fitTerminal calls on mobile
- Mobile resize prevention is working as designed
- Unify mobile detection logic between index.html and terminal.ts
- Use consistent User Agent + maxTouchPoints detection everywhere
- Fix setupResize() to properly set this.isMobile class property
- Ensure mobile resize blocking works correctly on initial load

The AI was correct - there was a mismatch in mobile detection that could
cause unpredictable resize behavior. Now using the same detection logic
everywhere for consistency.
- Changed terminal resize logic to only block WIDTH changes on mobile
- Renamed mobileResizeComplete to mobileWidthResizeComplete for clarity
- Allow HEIGHT changes on mobile to support keyboard show/hide
- Updated session-view to allow dynamic height calculations on mobile
- Fixed tests to reflect new mobile resize behavior
- Terminal now properly handles keyboard appearance without WIDTH resize loops
- Added isMobile and isHeightOnlyChange flags to terminal-resize event
- Terminal lifecycle manager now skips sending height-only resizes on mobile
- Prevents server resize commands when keyboard appears/disappears
- Server only gets resize commands for actual WIDTH changes on mobile
@claude
Copy link

claude bot commented Jul 11, 2025

Claude review from 7/11/2025, 5:40:27 AM (outdated - click to expand)
Claude review from 7/11/2025, 5:40:27 AM (outdated - click to expand)

Claude finished @steipete's task —— View job


📋 Summary

This PR addresses the critical mobile terminal resize loop issue by removing the complex ResizeCoordinator class and implementing a simpler, more direct approach. Mobile devices now get exactly one resize 200ms after initialization (to set width), then only height changes are allowed (for keyboard events). This directly solves the resize loop problem identified in Mario's debugging session that caused exponential data growth from re-rendering the entire scroll buffer.

✅ Positive Aspects

  • Pragmatic solution: The "resize once on mobile" approach is simple and effective for solving the resize loop
  • Significant simplification: Removing ResizeCoordinator eliminates ~119 lines of coordination complexity
  • Clear mobile/desktop separation: Different resize strategies for different platforms are well-defined
  • Improved debugging: Added extensive debug logging throughout resize operations
  • Comprehensive documentation: Included Mario's detailed debugging notes explaining the root cause
  • Test coverage maintained: Tests were properly updated to reflect the new behavior
  • Mobile detection unified: Now uses consistent detection logic between index.html and terminal.ts

🔍 Areas for Improvement

Code Quality

  1. Magic numbers without constants:

    • 200ms delay for mobile resize (terminal.ts:624)
    • 768px mobile breakpoint (mentioned in old code, not used in new detection)
    • 250ms resize debounce in terminal-lifecycle-manager
  2. Potential memory leak: The setTimeout in setupResize() (line 624) is not stored or canceled in cleanup, which could cause:

    • Memory leaks if component is unmounted before timeout fires
    • Errors if fitTerminal is called on a disposed component
  3. Height-only change detection: The logic for isHeightOnlyChange in terminal.ts (line 570) depends on the order of operations and could be fragile

Potential Issues

  1. Race condition: The 200ms timeout might fire before the terminal is fully initialized

  2. Orientation changes: Mobile users cannot adjust layout when rotating their device, which may leave them with suboptimal terminal dimensions

  3. Tablet edge cases: The new mobile detection based on user agent and maxTouchPoints might have different behavior than the old width-based detection

  4. Height resizing on mobile: While the PR claims to block all resizes on mobile after initial, the code actually allows height changes for keyboard events, which could still trigger some resize operations

Performance

  • Multiple calls to detectMobile() could be optimized by caching the result
  • The detection logic runs regex test on every call

Security

No security issues identified.

Testing

  1. Missing mobile-specific tests: No explicit tests for the 200ms delay mobile resize behavior
  2. Timing-dependent tests: The 200ms delay makes testing more fragile
  3. No tests for orientation changes: Missing coverage for rotation scenarios on mobile

💡 Suggestions

1. Fix Memory Leak

// In terminal.ts, add class property:
private mobileResizeTimeout: NodeJS.Timeout | null = null;

// In setupResize():
if (this.isMobile) {
  logger.debug('[Terminal] Mobile detected - scheduling initial resize in 200ms');
  this.mobileResizeTimeout = setTimeout(() => {
    if (!this.terminal || !this.container) {
      logger.warn('[Terminal] Mobile resize aborted - terminal not ready');
      return;
    }
    logger.debug('[Terminal] Mobile: Executing initial resize');
    this.fitTerminal('initial-mobile-only');
    logger.debug('[Terminal] Mobile: Initial width set, future WIDTH resizes blocked');
  }, 200);
}

// In disconnectedCallback():
if (this.mobileResizeTimeout) {
  clearTimeout(this.mobileResizeTimeout);
  this.mobileResizeTimeout = null;
}

2. Extract Constants

// At top of terminal.ts
private static readonly MOBILE_RESIZE_DELAY_MS = 200;
private static readonly RESIZE_DEBOUNCE_MS = 250; // for terminal-lifecycle-manager

3. Cache Mobile Detection

private cachedIsMobile: boolean | null = null;

private detectMobile(): boolean {
  if (this.cachedIsMobile !== null) {
    return this.cachedIsMobile;
  }
  
  this.cachedIsMobile = (
    /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) ||
    (navigator.maxTouchPoints !== undefined && navigator.maxTouchPoints > 1)
  );
  
  return this.cachedIsMobile;
}

4. Consider Manual Resize Option

For mobile users who need to adjust after orientation changes, consider adding a button or gesture to manually trigger a resize.

🎯 Priority Items

  1. Fix memory leak: Store and cleanup the mobile resize timeout to prevent errors on component disposal
  2. Define constants: Replace magic numbers (200ms, 250ms) with named constants
  3. Add error handling: Ensure terminal is ready before the mobile resize executes
  4. Add mobile-specific tests: Test the 200ms delay behavior and width-blocking logic
  5. Document height resize behavior: The code allows height changes on mobile for keyboard events, but this isn't clearly documented in the PR description

Overall, this is a solid pragmatic fix that should significantly improve mobile stability by eliminating the resize loop issue. The simplification by removing ResizeCoordinator is a good architectural decision. The main concerns are around the memory leak and missing test coverage for the mobile-specific behavior.


Reviewed commit: c815168
Files changed: 8

cursor[bot]

This comment was marked as outdated.

@github-actions github-actions bot deleted a comment from claude bot Jul 11, 2025
@github-actions github-actions bot deleted a comment from claude bot Jul 11, 2025
steipete added 2 commits July 11, 2025 08:03
…-pty

- Rename vendored-pty directory to node-pty for consistency
- Add built-in SEA (Single Executable Application) support to unixTerminal.ts
- Remove patching logic from build-native.js as SEA support is now native
- Update macOS deployment target to 14.0 in binding.gyp
- Fix native executable build issues by using process.dlopen in SEA mode
CI needs the compiled JavaScript files since it doesn't build TypeScript
for node_modules. Remove lib/ from .gitignore and commit compiled files.
@claude
Copy link

claude bot commented Jul 11, 2025

Claude error from 7/11/2025, 6:08:13 AM (outdated - click to expand)
Claude error from 7/11/2025, 6:08:13 AM (outdated - click to expand)

Claude encountered an error —— View job


Claude finished @steipete's task —— View job


Todo List:

  • Read terminal.ts to understand new resize implementation
  • Review deleted resize-coordinator.ts to understand what was removed
  • Review test changes in terminal.test.ts
  • Check other modified files for consistency
  • Review new documentation files
  • Provide comprehensive code review

📋 Summary

This PR addresses the critical mobile terminal resize loop issue by removing the complex ResizeCoordinator class (~119 lines) and implementing a simpler, more direct approach. Mobile devices now get exactly one resize 200ms after initialization to set width, then only height changes are allowed (for keyboard events). This directly solves the resize loop problem identified in Mario's debugging session that caused exponential data growth from re-rendering the entire scroll buffer.

✅ Positive Aspects

  • Pragmatic solution: The "resize once on mobile" approach is simple and effective for solving the resize loop
  • Significant simplification: Removing ResizeCoordinator eliminates ~119 lines of coordination complexity
  • Clear mobile/desktop separation: Different resize strategies for different platforms are well-defined
  • Improved debugging: Added extensive debug logging throughout resize operations
  • Comprehensive documentation: Included Mario's detailed debugging notes explaining the root cause
  • Test coverage maintained: Tests were properly updated to reflect the new behavior
  • Mobile detection unified: Now uses consistent detection logic between index.html and terminal.ts

🔍 Areas for Improvement

Code Quality

  1. Magic numbers without constants:

    • 200ms delay for mobile resize (terminal.ts:624)
    • 768px mobile breakpoint (mentioned in old code, not used in new detection)
    • 250ms resize debounce in terminal-lifecycle-manager
  2. Potential memory leak: The setTimeout in setupResize() (line 624) is not stored or canceled in cleanup, which could cause:

    • Memory leaks if component is unmounted before timeout fires
    • Errors if fitTerminal is called on a disposed component
  3. Height-only change detection: The logic for isHeightOnlyChange in terminal.ts (line 570) depends on the order of operations and could be fragile

Potential Issues

  1. Race condition: The 200ms timeout might fire before the terminal is fully initialized

  2. Orientation changes: Mobile users cannot adjust layout when rotating their device, which may leave them with suboptimal terminal dimensions

  3. Tablet edge cases: The new mobile detection based on user agent and maxTouchPoints might have different behavior than the old width-based detection

  4. Height resizing on mobile: While the PR claims to block all resizes on mobile after initial, the code actually allows height changes for keyboard events, which could still trigger some resize operations

Performance

  • Multiple calls to detectMobile() could be optimized by caching the result
  • The detection logic runs regex test on every call

Security

No security issues identified.

Testing

  1. Missing mobile-specific tests: No explicit tests for the 200ms delay mobile resize behavior
  2. Timing-dependent tests: The 200ms delay makes testing more fragile
  3. No tests for orientation changes: Missing coverage for rotation scenarios on mobile

💡 Suggestions

1. Fix Memory Leak

// In terminal.ts, add class property:
private mobileResizeTimeout: NodeJS.Timeout | null = null;

// In setupResize():
if (this.isMobile) {
  logger.debug('[Terminal] Mobile detected - scheduling initial resize in 200ms');
  this.mobileResizeTimeout = setTimeout(() => {
    if (!this.terminal || !this.container) {
      logger.warn('[Terminal] Mobile resize aborted - terminal not ready');
      return;
    }
    logger.debug('[Terminal] Mobile: Executing initial resize');
    this.fitTerminal('initial-mobile-only');
    logger.debug('[Terminal] Mobile: Initial width set, future WIDTH resizes blocked');
  }, 200);
}

// In disconnectedCallback():
if (this.mobileResizeTimeout) {
  clearTimeout(this.mobileResizeTimeout);
  this.mobileResizeTimeout = null;
}

2. Extract Constants

// At top of terminal.ts
private static readonly MOBILE_RESIZE_DELAY_MS = 200;
private static readonly RESIZE_DEBOUNCE_MS = 250; // for terminal-lifecycle-manager

3. Cache Mobile Detection

private cachedIsMobile: boolean | null = null;

private detectMobile(): boolean {
  if (this.cachedIsMobile !== null) {
    return this.cachedIsMobile;
  }
  
  this.cachedIsMobile = (
    /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) ||
    (navigator.maxTouchPoints !== undefined && navigator.maxTouchPoints > 1)
  );
  
  return this.cachedIsMobile;
}

4. Consider Manual Resize Option

For mobile users who need to adjust after orientation changes, consider adding a button or gesture to manually trigger a resize.

🎯 Priority Items

  1. Fix memory leak: Store and cleanup the mobile resize timeout to prevent errors on component disposal
  2. Define constants: Replace magic numbers (200ms, 250ms) with named constants
  3. Add error handling: Ensure terminal is ready before the mobile resize executes
  4. Add mobile-specific tests: Test the 200ms delay behavior and width-blocking logic
  5. Document height resize behavior: The code allows height changes on mobile for keyboard events, but this isn't clearly documented in the PR description

Overall, this is a solid pragmatic fix that should significantly improve mobile stability by eliminating the resize loop issue. The simplification by removing ResizeCoordinator is a good architectural decision. The main concerns are around the memory leak and missing test coverage for the mobile-specific behavior.


Reviewed commit: 8e3d2de
Files changed: 50

cursor[bot]

This comment was marked as outdated.

@claude
Copy link

claude bot commented Jul 11, 2025

Claude finished @steipete's task —— View job


Todo List:

  • Read terminal.ts to understand new resize implementation
  • Review deleted resize-coordinator.ts to understand what was removed
  • Review test changes in terminal.test.ts
  • Check other modified files for consistency
  • Review new documentation files
  • Provide comprehensive code review

📋 Summary

This PR addresses the critical mobile terminal resize loop issue identified in Mario's debugging session by removing the complex ResizeCoordinator class (~119 lines) and implementing a simpler, more direct approach. Mobile devices now get exactly one resize 200ms after initialization to set width, then only height changes are allowed (for keyboard events). This directly solves the resize loop problem that caused exponential data growth from re-rendering the entire scroll buffer.

✅ Positive Aspects

  • Pragmatic solution: The "resize once on mobile" approach is simple and effective for solving the resize loop
  • Significant simplification: Removing ResizeCoordinator eliminates ~119 lines of coordination complexity
  • Clear mobile/desktop separation: Different resize strategies for different platforms are well-defined
  • Improved debugging: Added extensive debug logging throughout resize operations
  • Comprehensive documentation: Included Mario's detailed debugging notes explaining the root cause
  • Test coverage maintained: Tests were properly updated to reflect the new behavior
  • Mobile detection unified: Now uses consistent detection logic between index.html and terminal.ts

🔍 Areas for Improvement

Code Quality

  1. Magic numbers without constants:

    • 200ms delay for mobile resize (terminal.ts:630)
    • 250ms resize debounce in terminal-lifecycle-manager
    • 768px mobile breakpoint (mentioned in old code, not used in new detection)
  2. Potential memory leak: The setTimeout in setupResize() (line 630) is not stored or canceled in cleanup, which could cause:

    • Memory leaks if component is unmounted before timeout fires
    • Errors if fitTerminal is called on a disposed component
  3. Height-only change detection: The logic for isHeightOnlyChange in terminal.ts (line 576) depends on the order of operations and could be fragile

Potential Issues

  1. Race condition: The 200ms timeout might fire before the terminal is fully initialized

  2. Orientation changes: Mobile users cannot adjust layout when rotating their device, which may leave them with suboptimal terminal dimensions

  3. Tablet edge cases: The new mobile detection based on user agent and maxTouchPoints might have different behavior than the old width-based detection

  4. Height resizing on mobile: While the PR claims to block all resizes on mobile after initial, the code actually allows height changes for keyboard events, which could still trigger some resize operations

  5. State synchronization issue: The terminal's shouldResize method has an issue where this.lastCols and this.lastRows are not updated when width changes are rejected on mobile. This can lead to:

    • Persistent detection of width changes that have already been rejected
    • Incorrect handling of subsequent resize requests
    • State desynchronization between calculated and tracked dimensions

Performance

  • Multiple calls to detectMobile() could be optimized by caching the result
  • The detection logic runs regex test on every call

Security

No security issues identified.

Testing

  1. Missing mobile-specific tests: No explicit tests for the 200ms delay mobile resize behavior
  2. Timing-dependent tests: The 200ms delay makes testing more fragile
  3. No tests for orientation changes: Missing coverage for rotation scenarios on mobile

💡 Suggestions

1. Fix Memory Leak

// In terminal.ts, add class property:
private mobileResizeTimeout: NodeJS.Timeout | null = null;

// In setupResize():
if (this.isMobile) {
  logger.debug('[Terminal] Mobile detected - scheduling initial resize in 200ms');
  this.mobileResizeTimeout = setTimeout(() => {
    if (!this.terminal || !this.container) {
      logger.warn('[Terminal] Mobile resize aborted - terminal not ready');
      return;
    }
    logger.debug('[Terminal] Mobile: Executing initial resize');
    this.fitTerminal('initial-mobile-only');
    logger.debug('[Terminal] Mobile: Initial width set, future WIDTH resizes blocked');
  }, 200);
}

// In disconnectedCallback():
if (this.mobileResizeTimeout) {
  clearTimeout(this.mobileResizeTimeout);
  this.mobileResizeTimeout = null;
}

2. Extract Constants

// At top of terminal.ts
private static readonly MOBILE_RESIZE_DELAY_MS = 200;
private static readonly RESIZE_DEBOUNCE_MS = 250; // for terminal-lifecycle-manager

3. Cache Mobile Detection

private cachedIsMobile: boolean | null = null;

private detectMobile(): boolean {
  if (this.cachedIsMobile !== null) {
    return this.cachedIsMobile;
  }
  
  this.cachedIsMobile = (
    /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) ||
    (navigator.maxTouchPoints !== undefined && navigator.maxTouchPoints > 1)
  );
  
  return this.cachedIsMobile;
}

4. Fix State Synchronization

// In shouldResize() method, update tracking even when rejecting:
if (widthChanged) {
  logger.debug(`[Terminal] Preventing WIDTH resize on mobile (width already set)`);
  // Update lastCols to prevent repeated detection
  this.lastCols = cols;
  return false;
}

5. Consider Manual Resize Option

For mobile users who need to adjust after orientation changes, consider adding a button or gesture to manually trigger a resize.

🎯 Priority Items

  1. Fix memory leak: Store and cleanup the mobile resize timeout to prevent errors on component disposal
  2. Fix state synchronization: Update this.lastCols when rejecting width changes on mobile to prevent repeated detections
  3. Define constants: Replace magic numbers (200ms, 250ms) with named constants
  4. Add error handling: Ensure terminal is ready before the mobile resize executes
  5. Add mobile-specific tests: Test the 200ms delay behavior and width-blocking logic
  6. Document height resize behavior: The code allows height changes on mobile for keyboard events, but this isn't clearly documented in the PR description

Overall, this is a solid pragmatic fix that should significantly improve mobile stability by eliminating the resize loop issue. The simplification by removing ResizeCoordinator is a good architectural decision. The main concerns are around the memory leak, state synchronization, and missing test coverage for the mobile-specific behavior.


Reviewed commit: fef322a
Files changed: 50

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Stale State Causes Incorrect Resize Blocking

The mobile terminal resize logic in shouldResize suffers from inconsistent state management and incorrect blocking. When a width change is detected and prevented (after initial setup), this.lastCols is not updated to the new calculated width. This leaves the internal state stale, causing subsequent calls with the same dimensions to repeatedly detect a "width change" and incorrectly block legitimate height-only resizes (e.g., from keyboard events) if a width change is also present. The fix requires consistently updating this.lastCols and this.lastRows to the current calculated dimensions within shouldResize, regardless of whether the terminal's resize() method is ultimately called.

web/src/client/components/terminal.ts#L271-L312

private shouldResize(cols: number, rows: number): boolean {
// On mobile, prevent WIDTH changes after initial setup, but allow HEIGHT changes
if (this.isMobile && this.mobileWidthResizeComplete) {
// Check if only height changed (allow keyboard resizes)
const widthChanged = this.lastCols !== cols;
const heightChanged = this.lastRows !== rows;
if (widthChanged) {
logger.debug(`[Terminal] Preventing WIDTH resize on mobile (width already set)`);
return false;
}
if (heightChanged) {
logger.debug(
`[Terminal] Allowing HEIGHT resize on mobile: ${this.lastRows}${rows} rows`
);
this.lastRows = rows;
return true;
}
return false;
}
// Check if dimensions actually changed
const changed = this.lastCols !== cols || this.lastRows !== rows;
if (changed) {
logger.debug(
`[Terminal] Dimensions changed: ${this.lastCols}x${this.lastRows}${cols}x${rows}`
);
this.lastCols = cols;
this.lastRows = rows;
// Mark mobile WIDTH resize as complete after first resize
if (this.isMobile && !this.mobileWidthResizeComplete) {
this.mobileWidthResizeComplete = true;
logger.debug(`[Terminal] Mobile WIDTH resize complete - blocking future width changes`);
}
}
return changed;
}

Fix in CursorFix in Web


Was this report helpful? Give feedback by reacting with 👍 or 👎

@github-actions github-actions bot deleted a comment from claude bot Jul 11, 2025
@github-actions github-actions bot deleted a comment from claude bot Jul 11, 2025
@steipete steipete merged commit 0c617ae into main Jul 11, 2025
14 of 15 checks passed
@steipete steipete deleted the mobile-stabilize branch July 11, 2025 06:23
ferg-cod3s pushed a commit to ferg-cod3s/tunnelforge that referenced this pull request Sep 23, 2025
ferg-cod3s pushed a commit to ferg-cod3s/tunnelforge that referenced this pull request Oct 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants