<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>emre şahin's digital garden 🍃 - tmux</title>
    <link>https://emresahin.net/tags/tmux/</link>
    <description>Posts in the tmux tag</description>
    <language>en</language>
    <managingEditor>contact@emresahin.net (Emre Şahin)</managingEditor>
    <lastBuildDate>Tue, 29 Sep 2026 14:57:43 +0000</lastBuildDate>
    <atom:link href="https://emresahin.net/tags/tmux/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>devlog 31</title>
      <published>2025-06-07T10:29:03+00:00</published>
      <updated>2025-06-07T10:29:03+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sat, 07 Jun 2025 10:29:03 +0000</pubDate>
      <link>https://emresahin.net/devlog-31/</link>
      <guid isPermaLink="true">https://emresahin.net/devlog-31/</guid>
      <description>🐢 I think sending Alt keys in Ghostty needs some love. Let’s start with it as an easy task for the day. 🐇 While checking macos-option-as-alt as a potential source of the problem, I spent time adjusting the Ghostty icon . I changed the app icon. This is how I spend my time being extremely producti...</description>
      <category>shell</category>
      <category>tmux</category>
      <category>ghostty</category>
      <category>productivity</category>
      <category>macos</category>
      <category>terminal</category>
      <content:encoded><![CDATA[<p>🐢 I think sending Alt keys in Ghostty needs some love. Let’s start with it as an easy task for the day.</p>
<p>🐇 While checking <a href="https://ghostty.org/docs/config/reference#macos-option-as-alt"><code>macos-option-as-alt</code></a> as a potential source of the problem, I spent time <a href="https://ghostty.org/docs/config/reference#macos-icon">adjusting the Ghostty icon</a>. I changed the app icon. This is how I spend my time being extremely productive.</p>
<p>🐢 Weird. The <code>Option as alt</code> key setting is fine.</p>
<p>🐇 Actually, the Alt key works; the reason we cannot make panes larger is that <code>Alt-Right</code> and <code>Alt-Left</code> are not bound to this.</p>
<p>🐢 You may be right, yes. Then, what’s the key to enlarge a pane?</p>
<p>🦊 List all the keys. Check the keys. You should have done this already.</p>
<pre><code class="language-shell">$ tmux list-keys | rg resize

bind-key    -T prefix       &gt;                      display-menu -T "#[align=centre]#{pane_index} (#{pane_id})" -x P -y P "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}" &lt; { send-keys -X history-top } "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}" &gt; { send-keys -X history-bottom } '' "#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}" C-r { if-shell -F "#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}" "copy-mode -t=" ; send-keys -X -t = search-backward "#{q:mouse_word}" } "#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}" C-y { copy-mode -q ; send-keys -l "#{q:mouse_word}" } "#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}" c { copy-mode -q ; set-buffer "#{q:mouse_word}" } "#{?mouse_line,Copy Line,}" l { copy-mode -q ; set-buffer "#{q:mouse_line}" } '' "#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}" C-h { copy-mode -q ; send-keys -l "#{q:mouse_hyperlink}" } "#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}" h { copy-mode -q ; set-buffer "#{q:mouse_hyperlink}" } '' "Horizontal Split" h { split-window -h } "Vertical Split" v { split-window -v } '' "#{?#{&gt;:#{window_panes},1},,-}Swap Up" u { swap-pane -U } "#{?#{&gt;:#{window_panes},1},,-}Swap Down" d { swap-pane -D } "#{?pane_marked_set,,-}Swap Marked" s { swap-pane } '' Kill X { kill-pane } Respawn R { respawn-pane -k } "#{?pane_marked,Unmark,Mark}" m { select-pane -m } "#{?#{&gt;:#{window_panes},1},,-}#{?window_zoomed_flag,Unzoom,Zoom}" z { resize-pane -Z }
bind-key    -T prefix       z                      resize-pane -Z
bind-key -r -T prefix       M-Up                   resize-pane -U 5
bind-key -r -T prefix       M-Down                 resize-pane -D 5
bind-key -r -T prefix       M-Left                 resize-pane -L 5
bind-key -r -T prefix       C-Up                   resize-pane -U
bind-key -r -T prefix       C-Down                 resize-pane -D
bind-key -r -T prefix       C-Left                 resize-pane -L
bind-key -r -T prefix       C-Right                resize-pane -R
bind-key    -T root         MouseDown3Pane         if-shell -F -t = "#{||:#{mouse_any_flag},#{&amp;&amp;:#{pane_in_mode},#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}}}" { select-pane -t = ; send-keys -M } { display-menu -T "#[align=centre]#{pane_index} (#{pane_id})" -t = -x M -y M "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}" &lt; { send-keys -X history-top } "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}" &gt; { send-keys -X history-bottom } '' "#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}" C-r { if-shell -F "#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}" "copy-mode -t=" ; send-keys -X -t = search-backward "#{q:mouse_word}" } "#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}" C-y { copy-mode -q ; send-keys -l "#{q:mouse_word}" } "#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}" c { copy-mode -q ; set-buffer "#{q:mouse_word}" } "#{?mouse_line,Copy Line,}" l { copy-mode -q ; set-buffer "#{q:mouse_line}" } '' "#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}" C-h { copy-mode -q ; send-keys -l "#{q:mouse_hyperlink}" } "#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}" h { copy-mode -q ; set-buffer "#{q:mouse_hyperlink}" } '' "Horizontal Split" h { split-window -h } "Vertical Split" v { split-window -v } '' "#{?#{&gt;:#{window_panes},1},,-}Swap Up" u { swap-pane -U } "#{?#{&gt;:#{window_panes},1},,-}Swap Down" d { swap-pane -D } "#{?pane_marked_set,,-}Swap Marked" s { swap-pane } '' Kill X { kill-pane } Respawn R { respawn-pane -k } "#{?pane_marked,Unmark,Mark}" m { select-pane -m } "#{?#{&gt;:#{window_panes},1},,-}#{?window_zoomed_flag,Unzoom,Zoom}" z { resize-pane -Z } }
bind-key    -T root         MouseDrag1Border       resize-pane -M
bind-key    -T root         M-MouseDown3Pane       display-menu -T "#[align=centre]#{pane_index} (#{pane_id})" -t = -x M -y M "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}" &lt; { send-keys -X history-top } "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}" &gt; { send-keys -X history-bottom } '' "#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}" C-r { if-shell -F "#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}" "copy-mode -t=" ; send-keys -X -t = search-backward "#{q:mouse_word}" } "#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}" C-y { copy-mode -q ; send-keys -l "#{q:mouse_word}" } "#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}" c { copy-mode -q ; set-buffer "#{q:mouse_word}" } "#{?mouse_line,Copy Line,}" l { copy-mode -q ; set-buffer "#{q:mouse_line}" } '' "#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}" C-h { copy-mode -q ; send-keys -l "#{q:mouse_hyperlink}" } "#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}" h { copy-mode -q ; set-buffer "#{q:mouse_hyperlink}" } '' "Horizontal Split" h { split-window -h } "Vertical Split" v { split-window -v } '' "#{?#{&gt;:#{window_panes},1},,-}Swap Up" u { swap-pane -U } "#{?#{&gt;:#{window_panes},1},,-}Swap Down" d { swap-pane -D } "#{?pane_marked_set,,-}Swap Marked" s { swap-pane } '' Kill X { kill-pane } Respawn R { respawn-pane -k } "#{?pane_marked,Unmark,Mark}" m { select-pane -m } "#{?#{&gt;:#{window_panes},1},,-}#{?window_zoomed_flag,Unzoom,Zoom}" z { resize-pane -Z }
</code></pre>
<p>🐇 <code>resize-pane</code> has keys, but they conflict with <code>C-Right</code>, etc., which are macOS display selection keys. I defined <code>M-S-Right</code>, etc., to resize and <code>M-C-Right</code>, etc., to swap the panes. Defining new keys with <code>M-S</code>…</p>
<pre><code class="language-tmux">bind-key -n "M-S-Right" resize-pane -R 10
bind-key -n "M-S-Left" resize-pane -L 10
bind-key -n "M-S-Up" resize-pane -U 10
bind-key -n "M-S-Down" resize-pane -D 10
</code></pre>
<p>🐢 We can set keys to swap windows too, maybe to <code>M-C-PageDown</code>, etc. <code>M-PageDown</code> now moves between windows.</p>
<p>🐇 Let’s take a look at the command options:</p>
<pre><code class="language-text">     swap-window [-d] [-s src-window] [-t dst-window]
                   (alias: swapw)
             This is similar to link-window, except the source and destination windows are swapped.  It is an error if no window exists at src-window.  If -d is given, the new window
             does not become the current window.

             If -s is omitted and a marked pane is present (see select-pane -m), the window containing the marked pane is used rather than the current window.
</code></pre>
<p>🐢 The use case will be limited, though.</p>
<p>🐇 Umm, right. Let’s stop procrastinating here.</p>
<p>🐢 It was productive procrastination, though. Now I can resize my tmux panes.</p>]]></content:encoded>
    </item>
    <item>
      <title>Nushell $env.PATH in tmux</title>
      <published>2025-05-30T16:09:29+00:00</published>
      <updated>2025-05-30T16:09:29+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Fri, 30 May 2025 16:09:29 +0000</pubDate>
      <link>https://emresahin.net/nushell--env-path-in-tmux/</link>
      <guid isPermaLink="true">https://emresahin.net/nushell--env-path-in-tmux/</guid>
      <description>I noticed that when I use: set -g default-shell nu in .tmux.conf to specify Nushell as the shell, it doesn’t load $env.PATH . It is necessary to specify the shell with: set -g default-command nu -l (using the full path to nu ) to ensure the shell reads your configuration file.</description>
      <category>Shell</category>
      <category>Nushell</category>
      <category>Tmux</category>
      <category>Terminal</category>
      <category>Configuration</category>
      <content:encoded><![CDATA[<p>I noticed that when I use:</p>
<pre><code class="language-tmux">set -g default-shell nu
</code></pre>
<p>in <code>.tmux.conf</code> to specify Nushell as the shell, it doesn’t load <code>$env.PATH</code>. It is necessary to specify the shell with:</p>
<pre><code class="language-tmux">set -g default-command nu -l
</code></pre>
<p>(using the full path to <code>nu</code>) to ensure the shell reads your configuration file.</p>]]></content:encoded>
    </item>
    <item>
      <title>Merging all Tmux windows in a single session</title>
      <published>2024-06-12T21:24:15+00:00</published>
      <updated>2024-06-12T21:24:15+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Wed, 12 Jun 2024 21:24:15 +0000</pubDate>
      <link>https://emresahin.net/merging-all-tmux-windows-in-a-single-session/</link>
      <guid isPermaLink="true">https://emresahin.net/merging-all-tmux-windows-in-a-single-session/</guid>
      <description>I use a new Tmux session for each day. Over time, I noticed that sessions proliferate, and I often need windows from earlier sessions in a central location. This script merges all windows from all Tmux sessions into a single session. You must specify the target session as a parameter. If you save...</description>
      <category>Shell</category>
      <category>Terminal</category>
      <category>Tmux</category>
      <category>Script</category>
      <category>Zsh</category>
      <category>Automation</category>
      <category>Productivity</category>
      <content:encoded><![CDATA[<p>I use a new Tmux session for each day. Over time, I noticed that sessions proliferate, and I often need windows from earlier sessions in a central location.</p>
<p>This script merges all windows from all Tmux sessions into a single session. You must specify the target session as a parameter. If you save the script as <code>merge-tmux-sessions.zsh</code>, you can run it like:</p>
<pre><code class="language-bash">merge-tmux-sessions.zsh my-current-session
</code></pre>
<p>It will then move all windows into the session you specify.</p>
<pre><code class="language-zsh">#!/bin/zsh

# set -vuex
#
#  Specify the target session

target_session=$1

# Get the list of all sessions

sessions=$(tmux list-sessions -F '#S')

# Iterate through each session

for session in ${(@f)sessions}; do
    if [ "$session" != "$target_session" ]; then
        # Get the list of windows in the current session
        windows=$(tmux list-windows -t $session -F '#I')

        # Move each window to the target session
        for window in ${(@f)windows}; do
            tmux move-window -s ${session}:${window} -t ${target_session}
        done
    fi

done
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>bits 6</title>
      <published>2024-05-23T16:33:49+00:00</published>
      <updated>2024-05-23T16:33:49+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Thu, 23 May 2024 16:33:49 +0000</pubDate>
      <link>https://emresahin.net/bits-6/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-6/</guid>
      <description>We’re using Graphite at work. Today, I configured a tmux shortcut to open a new pane and run gt create , which creates a new branch and commits all staged Git files. bind-key -n S-F12 split-window -h -c "#{pane_current_path}" "gt c" I just hit Shift-F12, and it opens a new pane in the current dir...</description>
      <category>bits</category>
      <category>tmux</category>
      <category>graphite</category>
      <category>cli</category>
      <category>git</category>
      <category>workflow</category>
      <category>productivity</category>
      <content:encoded><![CDATA[<p>We’re using <a href="https://graphite.dev">Graphite</a> at work. Today, I configured a tmux
shortcut to open a new pane and run <code>gt create</code>, which creates a new branch and
commits all staged Git files.</p>
<pre><code>bind-key -n S-F12 split-window -h -c "#{pane_current_path}" "gt c"
</code></pre>
<p>I just hit Shift-F12, and it opens a new pane in the current directory and runs the
command.</p>]]></content:encoded>
    </item>
    <item>
      <title>airmux</title>
      <published>2024-01-25T09:01:54+00:00</published>
      <updated>2024-01-25T09:01:54+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Thu, 25 Jan 2024 09:01:54 +0000</pubDate>
      <link>https://emresahin.net/airmux/</link>
      <guid isPermaLink="true">https://emresahin.net/airmux/</guid>
      <description>Airmux is a session manager for tmux that allows defining and managing multiple tmux sessions through project configuration files. Projects are defined in YAML or JSON files that specify settings such as commands to run, window/pane layouts, and other session settings. The start command loads and...</description>
      <category>tools</category>
      <category>tools</category>
      <category>tmux</category>
      <category>cli</category>
      <category>rust</category>
      <category>airmux</category>
      <category>terminal</category>
      <content:encoded><![CDATA[<ul>
<li><a href="https://github.com/dermoumi/airmux">Airmux</a> is a session manager for tmux that allows defining and managing multiple tmux sessions through project configuration files.</li>
<li>Projects are defined in YAML or JSON files that specify settings such as commands to run, window/pane layouts, and other session settings.</li>
<li>The <code>start</code> command loads and attaches to an existing project, while <code>edit</code> creates or modifies project files.</li>
<li>Projects support environment variable expansion, and command-line arguments passed to <code>start</code> are available as <code>$1</code>, <code>$2</code>, etc., in the project file.</li>
<li>Local project files can be used without specifying a name if they are found by searching parent directories.</li>
<li>Commands like <code>list</code>, <code>remove</code>, and <code>freeze</code> can manage existing projects.</li>
<li><code>Debug</code> prints the tmux commands to create a session without attaching, which is useful for troubleshooting.</li>
<li>Fields like <code>on_start</code> and <code>on_stop</code> allow running commands before or after session startup/shutdown.</li>
<li>Windows and panes have their own settings, such as layouts and commands to run, and they can be nested in the project file.</li>
<li>Airmux aims to provide an easy and organized way to define and manage multiple related tmux sessions through project files.</li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>Activate left window when the current one is closed in tmux</title>
      <published>2022-02-16T07:08:25+00:00</published>
      <updated>2022-02-16T07:08:25+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Wed, 16 Feb 2022 07:08:25 +0000</pubDate>
      <link>https://emresahin.net/Activate-left-window-when-the-current-one-is-closed-in-tmux/</link>
      <guid isPermaLink="true">https://emresahin.net/Activate-left-window-when-the-current-one-is-closed-in-tmux/</guid>
      <description>When I close a window in tmux, it typically activates the window to its right. I prefer the previously active window to be activated instead. I couldn’t find a direct setting for this, but tmux provides hooks for various events. By setting the window-unlinked hook to trigger last-window , I can a...</description>
      <category>CLI</category>
      <category>Tools</category>
      <category>tmux</category>
      <category>configuration</category>
      <category>hooks</category>
      <category>productivity</category>
      <content:encoded><![CDATA[<p>When I close a window in tmux, it typically activates the window to its right. I
prefer the previously active window to be activated instead. I couldn’t find a
direct setting for this, but tmux provides <a href="https://man7.org/linux/man-pages/man1/tmux.1.html#HOOKS">hooks</a>
for various events. By setting the <code>window-unlinked</code> hook to trigger <code>last-window</code>,
I can achieve the desired behavior.</p>
<p>Add the following to your <code>.tmux.conf</code>:</p>
<pre><code class="language-conf">set-hook -g window-unlinked 'last-window'
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Copying and pasting with XWindow clipboard from tmux</title>
      <published>2013-01-12T14:00:00+00:00</published>
      <updated>2013-01-12T14:00:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sat, 12 Jan 2013 14:00:00 +0000</pubDate>
      <link>https://emresahin.net/12235-5-1945/</link>
      <guid isPermaLink="true">https://emresahin.net/12235-5-1945/</guid>
      <description>tmux does not natively support XWindow’s clipboard. With two lines in .tmux.conf , you can configure two keys to send and retrieve clipboard content. Traditionally, applications use the PRIMARY selection, which uses the mouse selection for copying and pasting with the middle mouse button. However...</description>
      <category>Shell</category>
      <category>Linux</category>
      <category>tmux</category>
      <category>xwindow</category>
      <category>copy</category>
      <category>paste</category>
      <category>clipboard</category>
      <category>xsel</category>
      <content:encoded><![CDATA[<p><code>tmux</code> does not natively support XWindow’s clipboard. With two lines in
<code>.tmux.conf</code>, you can configure two keys to send and retrieve clipboard
content.</p>
<p>Traditionally, applications use the <code>PRIMARY</code> selection, which uses the mouse
selection for copying and pasting with the middle mouse button. However, this
is becoming less common, so I’ll configure the <code>CLIPBOARD</code>
selection used by most modern browsers, applications, and Emacs.</p>
<p>Add the following lines to <code>.tmux.conf</code>:</p>
<pre><code class="language-bash"># move x clipboard into tmux paste buffer
bind &lt; run "xsel -ob | tmux load-buffer - ; tmux paste-buffer "
# move tmux copy buffer into x clipboard
bind &gt; run "( tmux show-buffer | xsel -bi ) &amp;&amp; tmux display-message \"ok!\""
</code></pre>
<p>The first line sets <code>C-b &lt;</code> in <code>tmux</code> to retrieve the X clipboard contents
and paste them. The second line sets <code>C-b &gt;</code> to send the current <code>tmux</code> buffer to
the X clipboard for pasting into another application.</p>
<p>If you would still like to use the <code>PRIMARY</code> selection, you can drop the <code>b</code> options
from <code>xsel</code>, using <code>xsel -o</code> and <code>xsel -i</code> respectively.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
