feat: AI-CLI workflow optimizations
- 200K scrollback, vi-mode copy, copy-on-select - Ctrl-A S captures pane to ~/logs/, Ctrl-A L toggles live logging - Status bar shows git branch + working directory - Pre-built session: code/git/run/logs windows - Aggressive resize + focus events for multi-device switching - Toolbar: added Paste (clipboard), Zoom (pane fullscreen), Save (capture) - All new windows inherit current working directory - Toolbar split into 2 rows for more buttons without clutter
This commit is contained in:
+57
-27
@@ -1,57 +1,87 @@
|
|||||||
# Sethian tmux config
|
# sethmux — Sethian tmux config for AI CLI workflows
|
||||||
|
# Dvorak-optimized, mobile-friendly, built for long-running AI sessions
|
||||||
|
|
||||||
# Remap prefix to Ctrl-a (easier on mobile)
|
# --- Prefix ---
|
||||||
unbind C-b
|
unbind C-b
|
||||||
set -g prefix C-a
|
set -g prefix C-a
|
||||||
bind C-a send-prefix
|
bind C-a send-prefix
|
||||||
|
|
||||||
# Easy tab management
|
# --- Tab/Window management ---
|
||||||
bind -n M-t new-window
|
bind -n M-t new-window -c "#{pane_current_path}"
|
||||||
bind -n M-w kill-window
|
bind -n M-w kill-window
|
||||||
bind -n M-1 select-window -t 0
|
bind -n M-1 select-window -t 1
|
||||||
bind -n M-2 select-window -t 1
|
bind -n M-2 select-window -t 2
|
||||||
bind -n M-3 select-window -t 2
|
bind -n M-3 select-window -t 3
|
||||||
bind -n M-4 select-window -t 3
|
bind -n M-4 select-window -t 4
|
||||||
bind -n M-5 select-window -t 4
|
bind -n M-5 select-window -t 5
|
||||||
bind -n M-Left previous-window
|
bind -n M-Left previous-window
|
||||||
bind -n M-Right next-window
|
bind -n M-Right next-window
|
||||||
|
bind c new-window -c "#{pane_current_path}"
|
||||||
|
|
||||||
# Mouse support (critical for mobile/touch)
|
# --- Dvorak-friendly splits ---
|
||||||
|
bind s split-window -v -c "#{pane_current_path}"
|
||||||
|
bind v split-window -h -c "#{pane_current_path}"
|
||||||
|
|
||||||
|
# --- Pane navigation ---
|
||||||
|
bind o select-pane -t :.+
|
||||||
|
bind z resize-pane -Z
|
||||||
|
|
||||||
|
# --- Mouse ---
|
||||||
set -g mouse on
|
set -g mouse on
|
||||||
|
|
||||||
# Scrollback
|
# --- Scrollback (huge for AI output) ---
|
||||||
set -g history-limit 50000
|
set -g history-limit 200000
|
||||||
|
|
||||||
# Start numbering at 1
|
# --- Copy mode (vi-style) ---
|
||||||
|
setw -g mode-keys vi
|
||||||
|
bind -T copy-mode-vi v send -X begin-selection
|
||||||
|
bind -T copy-mode-vi y send -X copy-selection-and-cancel
|
||||||
|
bind -T copy-mode-vi MouseDragEnd1Pane send -X copy-selection-and-cancel
|
||||||
|
|
||||||
|
# --- Capture & logging ---
|
||||||
|
# Ctrl-A S = save entire scrollback to file
|
||||||
|
bind S capture-pane -pS - \; save-buffer ~/logs/capture-#{session_name}-#{window_index}-#{pane_index}-#(date +%s).txt \; display "Pane captured to ~/logs/"
|
||||||
|
|
||||||
|
# Ctrl-A L = toggle pipe-pane logging
|
||||||
|
bind L pipe-pane -o "cat >> ~/logs/live-#{session_name}-#{window_index}-#(date +%s).log" \; display "Logging toggled"
|
||||||
|
|
||||||
|
# --- Clipboard ---
|
||||||
|
set -g set-clipboard on
|
||||||
|
# Copy on select (mouse)
|
||||||
|
bind -T copy-mode-vi MouseDragEnd1Pane send -X copy-pipe-and-cancel "cat"
|
||||||
|
|
||||||
|
# --- Numbering ---
|
||||||
set -g base-index 1
|
set -g base-index 1
|
||||||
setw -g pane-base-index 1
|
setw -g pane-base-index 1
|
||||||
|
|
||||||
# Renumber windows on close
|
|
||||||
set -g renumber-windows on
|
set -g renumber-windows on
|
||||||
|
|
||||||
# Status bar - Sethian dark + orange
|
# --- Adaptive resize ---
|
||||||
|
setw -g aggressive-resize on
|
||||||
|
set -g focus-events on
|
||||||
|
|
||||||
|
# --- Status bar: git branch + cwd ---
|
||||||
set -g status-style "bg=#1a1a1a,fg=#e0e0e0"
|
set -g status-style "bg=#1a1a1a,fg=#e0e0e0"
|
||||||
set -g status-left "#[bg=#D35400,fg=#0a0a0a,bold] #S #[bg=#1a1a1a] "
|
set -g status-left "#[bg=#D35400,fg=#0a0a0a,bold] #S #[bg=#1a1a1a] "
|
||||||
set -g status-right "#[fg=#D35400]%H:%M #[fg=#666666]| #[fg=#e0e0e0]%b %d"
|
set -g status-right "#[fg=#666666]#{pane_current_path} #[fg=#D35400]#{?#{==:#{pane_current_command},bash},#(cd #{pane_current_path} && git branch --show-current 2>/dev/null),} #[fg=#666666]| #[fg=#e0e0e0]%H:%M"
|
||||||
|
set -g status-right-length 80
|
||||||
set -g status-left-length 20
|
set -g status-left-length 20
|
||||||
set -g status-right-length 30
|
set -g status-interval 5
|
||||||
|
|
||||||
# Window status
|
# --- Window status ---
|
||||||
setw -g window-status-format " #[fg=#888888]#I:#W "
|
setw -g window-status-format " #[fg=#888888]#I:#W "
|
||||||
setw -g window-status-current-format "#[bg=#D35400,fg=#0a0a0a,bold] #I:#W "
|
setw -g window-status-current-format "#[bg=#D35400,fg=#0a0a0a,bold] #I:#W "
|
||||||
setw -g window-status-separator ""
|
setw -g window-status-separator ""
|
||||||
|
|
||||||
# Pane borders
|
# --- Pane borders ---
|
||||||
set -g pane-border-style "fg=#333333"
|
set -g pane-border-style "fg=#333333"
|
||||||
set -g pane-active-border-style "fg=#D35400"
|
set -g pane-active-border-style "fg=#D35400"
|
||||||
|
set -g pane-border-format " #{pane_current_command} "
|
||||||
|
set -g pane-border-status off
|
||||||
|
|
||||||
# Terminal settings
|
# --- Terminal ---
|
||||||
set -g default-terminal "tmux-256color"
|
set -g default-terminal "tmux-256color"
|
||||||
set -ga terminal-overrides ",xterm-256color:Tc"
|
set -ga terminal-overrides ",xterm-256color:Tc"
|
||||||
|
|
||||||
# Dvorak-friendly splits (s=horizontal, v=vertical)
|
|
||||||
bind s split-window -v
|
|
||||||
bind v split-window -h
|
|
||||||
|
|
||||||
# Reduce escape delay
|
|
||||||
set -sg escape-time 10
|
set -sg escape-time 10
|
||||||
|
|
||||||
|
# --- Session startup layout ---
|
||||||
|
# Windows created by sethmux-start.sh: code, git, run, logs
|
||||||
|
|||||||
Executable
+17
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Start sethmux session with AI-workflow windows
|
||||||
|
SESSION="sethmux"
|
||||||
|
|
||||||
|
# Kill stale session
|
||||||
|
tmux kill-session -t "$SESSION" 2>/dev/null
|
||||||
|
sleep 0.5
|
||||||
|
|
||||||
|
# Create session with named windows
|
||||||
|
tmux new-session -d -s "$SESSION" -n "code" -x 120 -y 40
|
||||||
|
tmux new-window -t "$SESSION" -n "git"
|
||||||
|
tmux new-window -t "$SESSION" -n "run"
|
||||||
|
tmux new-window -t "$SESSION" -n "logs"
|
||||||
|
tmux select-window -t "$SESSION:1"
|
||||||
|
|
||||||
|
# Ensure logs dir exists
|
||||||
|
mkdir -p ~/logs
|
||||||
+44
-8
@@ -5,16 +5,19 @@
|
|||||||
var css=document.createElement('style');
|
var css=document.createElement('style');
|
||||||
css.textContent=`
|
css.textContent=`
|
||||||
#mb{display:none;position:fixed;bottom:0;left:0;right:0;background:#111;
|
#mb{display:none;position:fixed;bottom:0;left:0;right:0;background:#111;
|
||||||
border-top:2px solid #D35400;padding:5px 4px;gap:4px;justify-content:center;
|
border-top:2px solid #D35400;padding:4px 3px;gap:3px;justify-content:center;
|
||||||
flex-wrap:wrap;z-index:99999}
|
flex-wrap:wrap;z-index:99999}
|
||||||
#mb button{background:#222;color:#ccc;border:1px solid #444;border-radius:5px;
|
#mb button{background:#222;color:#ccc;border:1px solid #444;border-radius:5px;
|
||||||
padding:10px 12px;font-size:14px;font-family:ui-monospace,monospace;
|
padding:9px 10px;font-size:13px;font-family:ui-monospace,monospace;
|
||||||
cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;
|
cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;
|
||||||
min-width:42px;text-align:center;user-select:none}
|
min-width:38px;text-align:center;user-select:none}
|
||||||
#mb button:active{background:#D35400;color:#0a0a0a;border-color:#D35400}
|
#mb button:active{background:#D35400;color:#0a0a0a;border-color:#D35400}
|
||||||
#mb button.hi{border-color:#D35400;color:#D35400}
|
#mb button.hi{border-color:#D35400;color:#D35400}
|
||||||
#mb button.on{background:#D35400;color:#0a0a0a;border-color:#D35400}
|
#mb button.on{background:#D35400;color:#0a0a0a;border-color:#D35400}
|
||||||
#mb .sep{width:1px;background:#333;margin:0 2px;align-self:stretch}
|
#mb button.grn{border-color:#4e9a06;color:#4e9a06}
|
||||||
|
#mb button.grn.on{background:#4e9a06;color:#0a0a0a}
|
||||||
|
#mb .sep{width:1px;background:#333;margin:0 1px;align-self:stretch}
|
||||||
|
#mb .row{display:flex;gap:3px;justify-content:center;width:100%}
|
||||||
@media(max-width:900px){#mb{display:flex}}
|
@media(max-width:900px){#mb{display:flex}}
|
||||||
body.selmode .xterm-screen{pointer-events:none!important;
|
body.selmode .xterm-screen{pointer-events:none!important;
|
||||||
user-select:text!important;-webkit-user-select:text!important}
|
user-select:text!important;-webkit-user-select:text!important}
|
||||||
@@ -24,6 +27,7 @@
|
|||||||
var bar=document.createElement('div');
|
var bar=document.createElement('div');
|
||||||
bar.id='mb';
|
bar.id='mb';
|
||||||
bar.innerHTML=
|
bar.innerHTML=
|
||||||
|
'<div class="row">'+
|
||||||
'<button class="hi" data-k="\\x01c">+Tab</button>'+
|
'<button class="hi" data-k="\\x01c">+Tab</button>'+
|
||||||
'<button data-k="\\x01n">Next</button>'+
|
'<button data-k="\\x01n">Next</button>'+
|
||||||
'<button data-k="\\x01p">Prev</button>'+
|
'<button data-k="\\x01p">Prev</button>'+
|
||||||
@@ -36,12 +40,18 @@
|
|||||||
'<button data-k="\\t">Tab</button>'+
|
'<button data-k="\\t">Tab</button>'+
|
||||||
'<button data-k="\\x1bOA">\u25B2</button>'+
|
'<button data-k="\\x1bOA">\u25B2</button>'+
|
||||||
'<button data-k="\\x1bOB">\u25BC</button>'+
|
'<button data-k="\\x1bOB">\u25BC</button>'+
|
||||||
'<div class="sep"></div>'+
|
'</div>'+
|
||||||
|
'<div class="row">'+
|
||||||
'<button class="hi" id="selbtn" data-sel="1">Sel</button>'+
|
'<button class="hi" id="selbtn" data-sel="1">Sel</button>'+
|
||||||
|
'<button class="hi" id="pastebtn" data-paste="1">Paste</button>'+
|
||||||
|
'<button data-k="\\x01z">Zoom</button>'+
|
||||||
|
'<button class="grn" data-save="1">Save</button>'+
|
||||||
|
'<div class="sep"></div>'+
|
||||||
'<button data-k="\\x01v">V.Spl</button>'+
|
'<button data-k="\\x01v">V.Spl</button>'+
|
||||||
'<button data-k="\\x01s">H.Spl</button>'+
|
'<button data-k="\\x01s">H.Spl</button>'+
|
||||||
'<button data-k="\\x01o">Pane</button>'+
|
'<button data-k="\\x01o">Pane</button>'+
|
||||||
'<button data-k="\\x01x">Kill</button>';
|
'<button data-k="\\x01x">Kill</button>'+
|
||||||
|
'</div>';
|
||||||
document.body.appendChild(bar);
|
document.body.appendChild(bar);
|
||||||
|
|
||||||
function send(k){
|
function send(k){
|
||||||
@@ -63,18 +73,44 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function doPaste(){
|
||||||
|
if(!navigator.clipboard||!navigator.clipboard.readText){
|
||||||
|
alert('Clipboard access not available (needs HTTPS)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigator.clipboard.readText().then(function(text){
|
||||||
|
if(text && window.term){
|
||||||
|
window.term.input(text);
|
||||||
|
window.term.focus();
|
||||||
|
}
|
||||||
|
}).catch(function(e){
|
||||||
|
alert('Clipboard read failed: '+e.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function doSave(){
|
||||||
|
// Trigger tmux capture-pane via the keybinding Ctrl-A S
|
||||||
|
send('\\x01S');
|
||||||
|
var btn=document.querySelector('[data-save]');
|
||||||
|
btn.classList.add('on');
|
||||||
|
btn.textContent='\u2713';
|
||||||
|
setTimeout(function(){btn.classList.remove('on');btn.textContent='Save';},1500);
|
||||||
|
}
|
||||||
|
|
||||||
bar.addEventListener('click',function(e){
|
bar.addEventListener('click',function(e){
|
||||||
var btn=e.target.closest('button');
|
var btn=e.target.closest('button');
|
||||||
if(!btn) return;
|
if(!btn) return;
|
||||||
if(btn.dataset.sel) return toggleSel();
|
if(btn.dataset.sel) return toggleSel();
|
||||||
|
if(btn.dataset.paste) return doPaste();
|
||||||
|
if(btn.dataset.save) return doSave();
|
||||||
if(btn.dataset.k) send(btn.dataset.k);
|
if(btn.dataset.k) send(btn.dataset.k);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Shrink terminal for toolbar on mobile
|
// Shrink terminal for toolbar on mobile (2 rows now)
|
||||||
var obs=new MutationObserver(function(){
|
var obs=new MutationObserver(function(){
|
||||||
var el=document.querySelector('.xterm');
|
var el=document.querySelector('.xterm');
|
||||||
if(el && window.innerWidth<=900){
|
if(el && window.innerWidth<=900){
|
||||||
el.style.height='calc(100vh - 54px)';
|
el.style.height='calc(100vh - 88px)';
|
||||||
if(window.term && window.term.fit) window.term.fit();
|
if(window.term && window.term.fit) window.term.fit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Type=simple
|
|||||||
User=rdp
|
User=rdp
|
||||||
Group=rdp
|
Group=rdp
|
||||||
|
|
||||||
ExecStartPre=/bin/bash -c "/usr/bin/tmux kill-session -t sethmux 2>/dev/null; sleep 0.5; /usr/bin/tmux new-session -d -s sethmux -x 120 -y 40"
|
ExecStartPre=/opt/sethmux/sethmux-start.sh
|
||||||
ExecStart=/usr/local/bin/ttyd \
|
ExecStart=/usr/local/bin/ttyd \
|
||||||
--port 7683 \
|
--port 7683 \
|
||||||
--interface 0.0.0.0 \
|
--interface 0.0.0.0 \
|
||||||
|
|||||||
Reference in New Issue
Block a user