Skip to content

Terminal Session Persistence and macOS Shell Optimization #2493

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

seidnerj
Copy link
Contributor

♻️ Current situation

Currently, when users navigate to the terminal page (/platform-tools/terminal), a new terminal process is spawned. However, when users navigate away from the terminal page, the terminal process is immediately terminated. This means:

  • Users lose their terminal session and command history when navigating away
  • Any long-running commands are interrupted when switching pages
  • Users must start fresh each time they return to the terminal page

Additionally, the shell selection logic was basic and didn't take advantage of modern shells available on different platforms.

💡 Proposed solution

This PR introduces Terminal Session Persistence as a configurable feature with the following enhancements:

1. Configurable Session Persistence

  • New setting: "Preserve Terminal Sessions" toggle in Settings → General
  • When OFF (default): Preserves current behavior - terminal sessions are terminated when navigating away
  • When ON: Terminal sessions persist across navigation, allowing users to resume exactly where they left off

2. Enhanced Navigation Protection

  • Warning dialogs when persistence is disabled and user tries to navigate away from active terminal
  • Browser protection against accidental tab closure/refresh when terminal is active
  • Configurable warnings with user preference to show/hide navigation confirmations

3. Smart Shell Selection

  • macOS optimization - Prefers /bin/zsh (default since macOS Catalina) when available
  • Cross-platform fallback - Falls back to bash → sh on other platforms
  • Consistent behavior across both regular and persistent terminal modes

4. Buffer Management

  • Configurable buffer size setting for persistent sessions (default 50KB)
  • Content restoration when reconnecting to existing sessions
  • Memory efficient with automatic buffer size limiting

⚙️ Release Notes

New Features

  • Terminal Session Persistence: Configure whether terminal sessions survive page navigation
  • Smart Shell Selection: Uses zsh on macOS when available, with intelligent fallbacks
  • Enhanced Navigation Protection: Configurable warnings before terminating active sessions

Settings

Three new settings have been added under Settings → General:

  • Preserve Terminal Sessions: Enable/disable session persistence across navigation
  • Show Terminal Warning: Control navigation warning dialogs (hidden when persistence is ON)
  • Terminal Buffer Size: Configure memory buffer for persistent sessions (50KB default)

User Experience Improvements

  • Seamless terminal experience with session continuity
  • Modern shell experience on macOS with zsh
  • Configurable protection against accidental session termination

➕ Additional Information

Architecture Changes

The implementation uses a singleton pattern for persistent terminals on the backend, with careful event listener management to prevent memory leaks. The frontend maintains WebSocket connections across navigation when persistence is enabled.

Backward Compatibility

All changes are fully backward compatible. The default behavior (persistence OFF) preserves the existing terminal experience. Users must explicitly enable persistence to access the new functionality.

Performance Considerations

  • Minimal memory overhead with configurable buffer limits
  • Efficient WebSocket connection reuse for persistent sessions
  • Proper cleanup when persistence is disabled

Testing

Manual Testing Performed

  • ✅ Terminal persistence ON/OFF behavior verification
  • ✅ Navigation protection with various browser actions (back, forward, refresh, URL change, tab close)
  • ✅ Shell selection verification on macOS (zsh preference) and other platforms
  • ✅ Buffer size configuration and content restoration
  • ✅ Settings UI integration and user preference persistence
  • ✅ Memory leak prevention with proper event listener cleanup

Edge Cases Covered

  • Rapid navigation between terminal and other pages
  • Multiple terminal windows/tabs behavior
  • Server restart scenarios with active persistent sessions
  • Buffer overflow handling with large terminal outputs
  • WebSocket disconnection and reconnection scenarios

Areas for Future Testing

  • Load testing with multiple concurrent persistent sessions
  • Extended session duration testing (hours/days)
  • Integration testing with various terminal applications and tools

Reviewer Nudging

Good entry points for review:

  1. Settings Integration - Start with /ui/src/app/modules/settings/settings.component.ts to understand the new configuration options
  2. Backend Core Logic - Review /src/modules/platform-tools/terminal/terminal.service.ts for the singleton pattern and session management
  3. Frontend Terminal Service - Examine /ui/src/app/core/terminal.service.ts for WebSocket connection management
  4. Navigation Guards - Check /ui/src/app/modules/platform-tools/terminal/terminal.component.ts for the protection mechanisms

Key areas to focus on:

  • Event listener management and memory leak prevention
  • WebSocket connection lifecycle in persistent vs non-persistent modes
  • Shell selection logic and cross-platform compatibility
  • Buffer management and size limiting implementation

@github-actions github-actions bot added the latest Related to Latest Branch label Jul 13, 2025
@seidnerj seidnerj force-pushed the feature/terminal-session-persistence branch from 8dc94d3 to fc04a88 Compare July 13, 2025 22:58
Copy link
Contributor

@bwp91 bwp91 left a comment

Choose a reason for hiding this comment

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

Hi @seidnerj thanks for taking the time to make this PR - this all looks really cool!

I've added a few questions/comments but they are really trivial things.

If you are able to clear these comments up then I think the best thing for really testing this would be to merge this into the beta branch so it can be published as a beta version to npm.

I'm really glad also that you have made this an opt-in feature with a configuration option, it means down the line we could hypothetically include this in a latest release much sooner as a 'beta feature'. So thanks for taking the extra time to do this too!

Another note - at some point we will need to copy the text strings into the other language files. There is a script you can run locally to do this (num run lang-sync) but it might be worth holding off on this for now, in case there any changes to the english file in the meantime. Maybe this was your thinking anyway

Looking forward to seeing this integrated into the UI! 🍾

@bwp91 bwp91 requested a review from Copilot July 14, 2025 00:02
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces terminal session persistence as a configurable feature, allowing users to maintain their terminal sessions when navigating away from the terminal page. It also enhances shell selection logic to prefer modern shells like zsh on macOS and adds comprehensive navigation protection mechanisms.

  • Configurable terminal session persistence with user settings for preservation, warnings, and buffer size
  • Enhanced shell selection logic that prefers /bin/zsh on macOS with intelligent cross-platform fallbacks
  • Navigation protection with warning dialogs and browser beforeunload events to prevent accidental session termination

Reviewed Changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
ui/src/i18n/en.json Adds internationalization strings for terminal persistence settings and warning dialogs
ui/src/app/modules/settings/settings.component.ts Implements settings UI logic for terminal persistence configuration with confirmation dialogs
ui/src/app/modules/settings/settings.component.html Adds HTML form controls for terminal persistence settings with conditional visibility
ui/src/app/modules/platform-tools/terminal/terminal.component.ts Implements navigation guards and session management logic for terminal persistence
ui/src/app/modules/platform-tools/terminal/terminal-routing.module.ts Adds canDeactivate guard to terminal routing for navigation protection
ui/src/app/core/terminal.service.ts Extensive refactoring to support session persistence, reconnection, and proper resource management
ui/src/app/core/settings.service.ts Adds terminal persistence settings to the settings service with default values
ui/src/app/core/settings.interfaces.ts Extends settings interface to include terminal persistence configuration options
ui/src/app/core/components/confirm/confirm.component.ts Adds customizable cancel button label support to confirmation dialogs
ui/src/app/core/components/confirm/confirm.component.html Updates template to use customizable cancel button labels
src/modules/platform-tools/terminal/terminal.service.ts Backend implementation of persistent terminal sessions using singleton pattern
src/modules/platform-tools/terminal/terminal.gateway.ts Adds WebSocket message handler for destroying persistent sessions
src/core/config/config.service.ts Integrates terminal persistence settings into the configuration service

@seidnerj
Copy link
Contributor Author

seidnerj commented Jul 14, 2025

Thank you :) this was a bit difficult to nail down, but I think I got it working properly. This specific thing has been driving me crazy as I would be running certain commands in the terminal only to realize I had navigated out of the page and things would get terminated!

I will take a look at all your comments and resolve them. Looking forward to seeing this released soon, it's a real pain :)

@bwp91
Copy link
Contributor

bwp91 commented Jul 14, 2025

also if you can rebase this onto the beta-5.0.1 branch, let me know if you need a hand with this!

seidnerj added 13 commits July 14, 2025 17:42
Implements configurable terminal session persistence with the following features:

Backend changes:
- Add terminal persistence setting to UI config interface
- Implement persistent terminal sessions using static class properties
- Add session cleanup endpoint for destroying persistent sessions
- Proper buffer management and content restoration
- Session termination warning and buffer size controls

Frontend changes:
- Add terminal persistence toggle in Settings → General
- Add warning control and buffer size settings with smart UI logic
- Implement navigation guards for both Angular routing and browser navigation
- Add confirmation dialogs with customizable button labels
- Session cleanup when disabling persistence with user confirmation
- Convert diagnostic logging to console.debug for cleaner production output

Translation updates:
- Add all necessary translation keys for settings and confirmation dialogs
- Support for HTML formatting in warning messages
- Add process-exit event handler to reconnectTerminal method
- Ensure auto-restart works on first navigation, not just after refresh
- Backend now emits process-exit events for persistent terminals
- Replace deprecated substr with substring
- Add getPreferredShell() method to select optimal shell per platform
- On macOS: prefer /bin/zsh if available, fallback to bash/sh
- Other platforms: use existing bash/sh fallback logic
- Apply consistent shell selection for both regular and persistent terminals
- Remove all console.debug statements from frontend terminal code
- Fix ESLint formatting issues (trailing spaces, empty blocks, etc.)
- Maintain functionality while meeting linting standards
- Add terminalBufferSize constant to globalDefaults.ts
- Update getPreferredShell() return type to union type for better type safety
- Replace hardcoded buffer size values with global constant
- Make cancelButtonLabel optional in confirm component
- Add conditional display for terminal settings based on terminal access
- Remove empty setTimeout block in terminal service
- Reduce verbose logging and change to debug level for performance
- Sanitize user input logging to prevent security risks
- Update Bootstrap 4 classes (pl-*) to Bootstrap 5 (ps-*)
- Replace persistentTerminal any type with IPty | null
- Replace dataDisposable any type with IDisposable | null
- Add proper typing for exit code parameter
- Import required types from node-pty and xterm packages
- Remove redundant globalThis.terminal.bufferSize fallback since config service already handles default
- Update navigation warning message to be more accurate about process termination
- Change "any running processes will be stopped" to "all foreground processes will be terminated"
- Better reflects the actual behavior where background processes may continue running
- Replace static variables with localStorage for cross-tab communication
- Add atomic check-and-set logic to prevent race conditions
- Implement "Terminal Already Open" modal when second tab tries to access terminal
- Add beforeunload cleanup to handle crashed/closed tabs
- Make confirm component buttons conditional to show only when labels provided
- Fix onExit callback parameter type to match IPty interface
- Remove debug console.log statements for production compliance
- Fix ESLint import ordering and spacing issues
- Clean up trailing whitespace and formatting
- Ensure all linting rules pass and builds complete successfully
@seidnerj seidnerj force-pushed the feature/terminal-session-persistence branch from fc04a88 to fad23c8 Compare July 14, 2025 14:45
@seidnerj
Copy link
Contributor Author

Okay, I fixed all of the above, this now contains:

  • Complete terminal session persistence implementation
  • Cross-tab coordination with race condition fixes
  • All PR feedback addressed and resolved
  • Ran lang-sync to generate text for the rest of the languages
  • Rebased onto beta-5.0.1

Let me know if you need anything else!

@bwp91 bwp91 changed the base branch from latest to beta-5.0.1 July 15, 2025 17:25
@bwp91
Copy link
Contributor

bwp91 commented Jul 15, 2025

Hi @seidnerj this is looking good! please continue to push fixes to this branch.

Some things we should work towards:

  • the option descriptions are not visible in dark mode:

    Screenshot 2025-07-15 at 18 37 00
  • there is a terminal widget for the home (status) screen that would be good to hook up to a persisted terminal if possible?

  • when the 'Show Terminal Termination Warning' is enabled, it appears even if i open the terminal page but don't type anything in

    • is it possible to detect whether a command is running in the terminal, and could we only show the warning modal if something is running?
    • if that isn't possible, would it be possible to simply determine if the user has typed anything into the terminal (and run it), and in this case only show the modal?
    • if one of these two things is possible, then I think we could look to lose the config option and just have this the default behaviour.
  • within this modal i mention above, maybe it would be good to mention that another configuration option exists to be able to activate a persisted terminal? so that they won't see this exit modal again?


these are just some comments i have for now, i will continue to comment here if and as i note more things.

if there are any that you disagree with or have other ideas for, be sure to let me know!

@bwp91 bwp91 force-pushed the beta-5.2.1 branch 3 times, most recently from eeacd0e to 1b12292 Compare July 21, 2025 01:51
@seidnerj
Copy link
Contributor Author

The reason I asked if you could look at the xterm update was because when i tried to do it, it introduced a tiny but annoying scroll behaviour.

I’ve noticed that it is back, at least it means I wasn’t doing anything wrong when I tried!

On the previous version of xterm (so replicable on the current stable version of UI, when you are looking at the logs on a mobile device, you are able to easily scroll up and down in the “terminal” just by swiping your finger anywhere in the ternimal box.

Now it is replicable on the beta, when you are trying to scroll up and down with your finger in the terminal box, it only scrolls nicely if you start in a part with no text. If your finger starts over any text, it will only scroll one line up or down no matter how far you try to scroll with your finger.

Something obviously has changed with xterm between the two versions, but I wondered if you would have any idea how we could get this nice scrolling behaviour back with the new version of xterm.

Sounds like a bug in xterm.js, have you/anyone else reported this? We could try downgrading to an earlier (but still newer than what we had) version and see when/if this bug gets introduced.

@bwp91 bwp91 force-pushed the beta-5.2.1 branch 3 times, most recently from afb4460 to 61ee610 Compare July 21, 2025 21:51
@bwp91 bwp91 moved this from In Progress to In Beta in Homebridge UI Jul 21, 2025
@bwp91 bwp91 force-pushed the beta-5.2.1 branch 4 times, most recently from cbe5b36 to 059c80d Compare July 23, 2025 19:48
@bwp91
Copy link
Contributor

bwp91 commented Jul 24, 2025

Hi @seidnerj
Do you have anything else that you want to add to this PR? If not then let me fix the conflicts and we can look to properly merge this into the beta version? What do you think?

@seidnerj
Copy link
Contributor Author

Nope, I think this is it. Sounds good to me!

@bwp91 bwp91 changed the base branch from beta-5.2.1 to beta-5.3.1 July 24, 2025 16:57
@bwp91
Copy link
Contributor

bwp91 commented Jul 24, 2025

happy for you to merge this into the beta-5.3.1 branch (which it is already based against in this PR)!

please use the 'squash and merge' option 😄

@seidnerj
Copy link
Contributor Author

I have squash merged the PR into the beta-5.3.1 branch, but I cannot push it upstream obviously.

@seidnerj
Copy link
Contributor Author

Took a quick look again, and I see I forgot to get rid of the two following string constants:
platform.terminal.already_open_message
platform.terminal.already_open_title

This was from an earlier iteration when I thought about limiting the the number of active terminals to only one at any given time, but then I realize I might as well just connect to all of them at the same time.

@bwp91
Copy link
Contributor

bwp91 commented Jul 24, 2025

that’s okay i have taken care of this in another commit 🙂

@bwp91 bwp91 merged commit 2262197 into homebridge:beta-5.3.1 Jul 24, 2025
2 of 3 checks passed
@github-project-automation github-project-automation bot moved this from In Beta to Done in Homebridge UI Jul 24, 2025
@seidnerj seidnerj deleted the feature/terminal-session-persistence branch July 24, 2025 18:00
@bwp91 bwp91 mentioned this pull request Jul 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
beta Related to Beta Branch latest Related to Latest Branch
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants