├── screenshots └── tmux-suspend-demo.gif ├── LICENSE ├── scripts ├── resume.sh └── suspend.sh ├── suspend.tmux └── README.md /screenshots/tmux-suspend-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MunifTanjim/tmux-suspend/HEAD/screenshots/tmux-suspend-demo.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Munif Tanjim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /scripts/resume.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | set_options_for_resumed_state() { 6 | local -r escaped_delim="${RANDOM}${RANDOM}${RANDOM}" 7 | 8 | local _options="${1#,}" 9 | _options="${_options//\\,/${escaped_delim}}" 10 | IFS=, read -ra options <<< "${_options}" 11 | 12 | local flags="" 13 | local name="" 14 | local value="" 15 | 16 | for item in "${options[@]}"; do 17 | name="$(echo "${item%%:*}" | xargs)" 18 | item="${item#*:}" 19 | flags="${item%%:*}" 20 | value="${item#*:}" 21 | value="${value//${escaped_delim}/,}" 22 | 23 | tmux set-option -q${flags} "${name}" "${value}" 24 | done 25 | } 26 | 27 | declare -r on_resume_command="${1}" 28 | declare -r resumed_options="$(tmux show-option -qv '@suspend_resumed_options')" 29 | 30 | declare -r prefix="$(tmux show-option -qv '@suspend_prefix')" 31 | declare prefix_flags="" 32 | if [[ -z ${prefix} ]]; then 33 | prefix_flags="u" 34 | fi 35 | 36 | eval "${on_resume_command}" 37 | 38 | set_options_for_resumed_state "${resumed_options}" 39 | 40 | tmux set-option -q${prefix_flags} prefix "${prefix}" \; set-option -u key-table 41 | 42 | tmux refresh-client -S 43 | -------------------------------------------------------------------------------- /suspend.tmux: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | declare -r CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | 7 | tmux_option() { 8 | local -r option=$(tmux show-option -gqv "$1") 9 | local -r fallback="$2" 10 | echo "${option:-$fallback}" 11 | } 12 | 13 | declare -r key_config='@suspend_key' 14 | declare -r suspended_options_config='@suspend_suspended_options' 15 | declare -r on_resume_command_config='@suspend_on_resume_command' 16 | declare -r on_suspend_command_config='@suspend_on_suspend_command' 17 | 18 | declare -r default_suspended_options=" \ 19 | @mode_indicator_custom_prompt:: ---- , \ 20 | @mode_indicator_custom_mode_style::bg=brightblack\,fg=black \ 21 | " 22 | declare -r default_on_resume_command="" 23 | declare -r default_on_suspend_command="" 24 | 25 | init_tmux_suspend() { 26 | local -r KEY=$(tmux_option "$key_config" "F12") 27 | local -r suspended_options=$(tmux_option "$suspended_options_config" "$default_suspended_options") 28 | local -r on_resume_command=$(tmux_option "$on_resume_command_config" "$default_on_resume_command") 29 | local -r on_suspend_command=$(tmux_option "$on_suspend_command_config" "$default_on_suspend_command") 30 | 31 | tmux bind -Troot "$KEY" run-shell "$CURRENT_DIR/scripts/suspend.sh \"$on_suspend_command\" \"$suspended_options\"" 32 | tmux bind -Tsuspended "$KEY" run-shell "$CURRENT_DIR/scripts/resume.sh \"$on_resume_command\"" 33 | } 34 | 35 | init_tmux_suspend 36 | -------------------------------------------------------------------------------- /scripts/suspend.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | set_options_for_suspended_state() { 6 | local -r escaped_delim="${RANDOM}${RANDOM}${RANDOM}" 7 | 8 | local _options="${1#,}" 9 | _options="${_options//\\,/${escaped_delim}}" 10 | IFS=, read -ra options <<< "${_options}" 11 | 12 | local flags="" 13 | local name="" 14 | local value="" 15 | 16 | local resumed_options="" 17 | for item in "${options[@]}"; do 18 | if [[ -z "$(echo "${item}" | xargs)" ]]; then 19 | continue 20 | fi 21 | 22 | name="$(echo "${item%%:*}" | xargs)" 23 | item="${item#*:}" 24 | flags="${item%%:*}" 25 | value="${item#*:}" 26 | value="${value//${escaped_delim}/,}" 27 | 28 | has_value="$(tmux show-options -qv${flags} "${name}" | wc -l | xargs)" 29 | preserved_flags="${flags}" 30 | if [[ "${has_value}" = "0" ]]; then 31 | preserved_flags="${preserved_flags}u" 32 | fi 33 | preserved_value="$(tmux show-options -qv${flags} "${name}")" 34 | resumed_options="${resumed_options},${name}:${preserved_flags}:${preserved_value//,/\\,}" 35 | 36 | tmux set-option -q${flags} "${name}" "${value}" 37 | done 38 | 39 | tmux set-option -q '@suspend_resumed_options' "${resumed_options}" 40 | } 41 | 42 | declare -r on_suspend_command="${1}" 43 | declare -r suspended_options="${2}" 44 | 45 | tmux set-option -q '@suspend_prefix' "$(tmux show-option -qv prefix)" 46 | 47 | tmux set-option -q prefix none \; set-option key-table suspended \; \ 48 | if-shell -F '#{pane_in_mode}' 'send-keys -X cancel' \; \ 49 | if-shell -F '#{pane_synchronized}' 'set synchronize-panes off' 50 | 51 | set_options_for_suspended_state "${suspended_options}" 52 | 53 | eval "${on_suspend_command}" 54 | 55 | tmux refresh-client -S 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tmux Suspend 2 | 3 | Plugin that lets you suspend **local tmux session**, so that you can work with 4 | **nested remote tmux session** painlessly. 5 | 6 | **Demo**: 7 | 8 | ![Tmux Suspend Demo GIF](screenshots/tmux-suspend-demo.gif) 9 | 10 | ## Usage 11 | 12 | With the default keybinding, 13 | 14 | Press `F12` to suspend your local tmux session. In suspeded state, you can only 15 | interact with the currently active pane (which will be running the remote tmux session). 16 | 17 | Press `F12` again in suspended state to resume local tmux session. 18 | 19 | _**Note**_: If you have [**tmux-mode-indicator**](https://github.com/MunifTanjim/tmux-mode-indicator) 20 | plugin installed, it'll automatically show indicator for the suspended state. 21 | 22 | ## Installation 23 | 24 | ### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) 25 | 26 | Add this repository as a TPM plugin in your `.tmux.conf` file: 27 | 28 | ```conf 29 | set -g @plugin 'MunifTanjim/tmux-suspend' 30 | ``` 31 | 32 | Press `prefix + I` in Tmux environment to install it. 33 | 34 | ### Manual Installation 35 | 36 | Clone this repository: 37 | 38 | ```bash 39 | git clone https://github.com/MunifTanjim/tmux-suspend.git ~/.tmux/plugins/tmux-suspend 40 | ``` 41 | 42 | Add this line in your `.tmux.conf` file: 43 | 44 | ```conf 45 | run-shell ~/.tmux/plugins/tmux-suspend/suspend.tmux 46 | ``` 47 | 48 | Reload Tmux configuration file with: 49 | 50 | ```sh 51 | tmux source-file ~/.tmux.conf 52 | ``` 53 | 54 | ## Configuration Options 55 | 56 | The following configuration options are available: 57 | 58 | ### `@suspend_key` 59 | 60 | Key used to suspend/resume local tmux session. This is not binded to any Prefix. 61 | 62 | ```conf 63 | set -g @suspend_key 'F12' 64 | ``` 65 | 66 | ### `@suspend_suspended_options` 67 | 68 | Comma-seperated list of items denoting options to set for suspended state. 69 | These options will be automatically reverted when session is resumed. 70 | 71 | ```conf 72 | set -g @suspend_suspended_options " \ 73 | @mode_indicator_custom_prompt:: ---- , \ 74 | @mode_indicator_custom_mode_style::bg=brightblack\\,fg=black, \ 75 | " 76 | ``` 77 | 78 | The syntax of each item is `#{option_name}:#{option_flags}:#{option_value}`. 79 | 80 | | Item Segment | Description | 81 | | ----------------- | -------------------------------------------------------------------------- | 82 | | `#{option_name}` | name of the option. | 83 | | `#{option_flags}` | flags accepted by `set-option`, can be left empty. | 84 | | `#{option_value}` | value of the option, commas (`,`) inside value need to be escaped as `\\,` | 85 | 86 | For example: 87 | 88 | ```conf 89 | # remove colors from status line for suspended state 90 | set -g @suspend_suspended_options " \ 91 | status-left-style::bg=brightblack\\,fg=black bold dim, \ 92 | window-status-current-style:gw:bg=brightblack\\,fg=black, \ 93 | window-status-last-style:gw:fg=brightblack, \ 94 | window-status-style:gw:bg=black\\,fg=brightblack, \ 95 | @mode_indicator_custom_prompt:: ---- , \ 96 | @mode_indicator_custom_mode_style::bg=brightblack\\,fg=black, \ 97 | " 98 | ``` 99 | 100 | ### `@suspend_on_suspend_command` and `@suspend_on_resume_command` 101 | 102 | These options can be set to arbritary commands to run when session is 103 | suspended (`@suspend_on_suspend_command`) or resumed (`@suspend_on_resume_command`). 104 | 105 | ```conf 106 | set -g @suspend_on_resume_command "" 107 | set -g @suspend_on_suspend_command "" 108 | ``` 109 | 110 | For example, you can do the same thing that the default value of `@suspend_suspended_options` 111 | does using these options instead: 112 | 113 | ```conf 114 | set -g @suspend_suspended_options "" 115 | 116 | set -g @suspend_on_resume_command "tmux \ 117 | set-option -uq '@mode_indicator_custom_prompt' \\; \ 118 | set-option -uq '@mode_indicator_custom_mode_style'" 119 | 120 | set -g @suspend_on_suspend_command "tmux \ 121 | set-option -q '@mode_indicator_custom_prompt' ' ---- ' \\; \ 122 | set-option -q '@mode_indicator_custom_mode_style' 'bg=brightblack,fg=black'" 123 | ``` 124 | 125 | As you can see, it's more convenient to use `@suspend_suspended_options` for setting 126 | and reverting options. 127 | 128 | ## License 129 | 130 | Licensed under the MIT License. Check the [LICENSE](./LICENSE) file for details. 131 | --------------------------------------------------------------------------------