Auto-sync from catchthesethighs
This commit is contained in:
170
CHANGELOG_UPDATE.md
Normal file
170
CHANGELOG_UPDATE.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# Changelog Update for v1.2.0
|
||||
|
||||
## [1.2.0] - 2025-12-16
|
||||
|
||||
### Added
|
||||
|
||||
#### Python Project Templates
|
||||
- **`py-new`** - Create basic Python project with venv, tests, and structure
|
||||
- **`py-django`** - Django web application template with best practices
|
||||
- **`py-flask`** - Flask web application with blueprints and templates
|
||||
- **`py-fastapi`** - FastAPI REST API with automatic docs
|
||||
- **`py-data`** - Data science project with Jupyter, pandas, and proper data directory structure
|
||||
- **`py-cli`** - Command-line tool template using Click framework
|
||||
|
||||
#### Python Template Features
|
||||
- Automatic virtual environment creation
|
||||
- Poetry support (configurable via `PY_TEMPLATE_USE_POETRY`)
|
||||
- Pre-configured .gitignore for Python projects
|
||||
- README with setup instructions
|
||||
- Requirements.txt with common dependencies
|
||||
- Project structure following best practices
|
||||
- Optional git initialization
|
||||
- Quick aliases: `pynew`, `pydjango`, `pyflask`, `pyfast`, `pydata`, `pycli`
|
||||
- `venv` function to quickly activate virtual environments
|
||||
|
||||
### Changed
|
||||
|
||||
#### Alias System Cleanup
|
||||
- **Removed `stats` alias** - Forces explicit `dfstats` usage to avoid conflicts
|
||||
- Updated help text in `dotfiles-cli` to reflect removal
|
||||
- Clarified that `stats` removal is intentional in documentation
|
||||
|
||||
### Configuration
|
||||
|
||||
New Python template settings in `dotfiles.conf`:
|
||||
|
||||
```bash
|
||||
# Python Project Templates
|
||||
PY_TEMPLATE_BASE_DIR="$HOME/projects" # Where to create projects
|
||||
PY_TEMPLATE_PYTHON="python3" # Python executable
|
||||
PY_TEMPLATE_VENV_NAME="venv" # Virtual environment name
|
||||
PY_TEMPLATE_USE_POETRY="false" # Use Poetry instead of venv
|
||||
PY_TEMPLATE_GIT_INIT="true" # Auto-initialize git repos
|
||||
```
|
||||
|
||||
### Usage Examples
|
||||
|
||||
#### Basic Python Project
|
||||
```bash
|
||||
py-new myproject
|
||||
cd myproject
|
||||
source venv/bin/activate
|
||||
# Start coding!
|
||||
```
|
||||
|
||||
#### Django Project
|
||||
```bash
|
||||
py-django myblog
|
||||
cd myblog
|
||||
source venv/bin/activate
|
||||
python manage.py runserver
|
||||
# Visit: http://localhost:8000
|
||||
```
|
||||
|
||||
#### FastAPI Project
|
||||
```bash
|
||||
py-fastapi myapi
|
||||
cd myapi
|
||||
source venv/bin/activate
|
||||
python run.py
|
||||
# Docs at: http://localhost:8000/docs
|
||||
```
|
||||
|
||||
#### Data Science Project
|
||||
```bash
|
||||
py-data analysis
|
||||
cd analysis
|
||||
source venv/bin/activate
|
||||
jupyter notebook
|
||||
```
|
||||
|
||||
#### CLI Tool
|
||||
```bash
|
||||
py-cli mytool
|
||||
cd mytool
|
||||
pip install -e .
|
||||
mytool --help
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **`stats` alias removed** - Use `dfstats` instead
|
||||
- Reason: Potential conflicts with other tools/scripts
|
||||
- Migration: Replace `stats` with `dfstats` in any scripts or muscle memory
|
||||
|
||||
---
|
||||
|
||||
## File Changes
|
||||
|
||||
### New Files
|
||||
- `zsh/functions/python-templates.zsh` - Python project template functions
|
||||
|
||||
### Modified Files
|
||||
- `zsh/aliases.zsh` - Removed `stats` alias, added cleanup notes
|
||||
- `dotfiles.conf` - New Python template configuration section (optional)
|
||||
- `.zshrc` - Sources `python-templates.zsh` (needs manual addition)
|
||||
|
||||
### To Enable Python Templates
|
||||
|
||||
Add to your `.zshrc`:
|
||||
|
||||
```bash
|
||||
# Python project templates
|
||||
[[ -f "$_dotfiles_dir/zsh/functions/python-templates.zsh" ]] && \
|
||||
source "$_dotfiles_dir/zsh/functions/python-templates.zsh"
|
||||
```
|
||||
|
||||
Or add to deferred loading section:
|
||||
|
||||
```bash
|
||||
_deferred_load() {
|
||||
# ... existing code ...
|
||||
|
||||
# Python templates
|
||||
[[ -f "$_dotfiles_dir/zsh/functions/python-templates.zsh" ]] && \
|
||||
source "$_dotfiles_dir/zsh/functions/python-templates.zsh"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Documentation Updates Needed
|
||||
|
||||
### README.md
|
||||
- Add Python Templates section
|
||||
- Update aliases table (remove `stats`, add `dfstats`)
|
||||
- Add examples of template usage
|
||||
|
||||
### SETUP_GUIDE.md
|
||||
- Add Python project templates section
|
||||
- Document template configuration options
|
||||
|
||||
### New Documentation
|
||||
- Consider creating `docs/PYTHON_TEMPLATES.md` with detailed examples
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Test each template type creates correct structure
|
||||
- [ ] Verify virtual environment creation works
|
||||
- [ ] Test with both venv and Poetry modes
|
||||
- [ ] Confirm git initialization works
|
||||
- [ ] Check that `stats` alias is truly removed
|
||||
- [ ] Verify `dfstats` still works correctly
|
||||
- [ ] Test on fresh installation
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements (v1.3.0)
|
||||
|
||||
- Add `py-test` template for testing frameworks
|
||||
- Add `py-package` for PyPI package development
|
||||
- Add `py-ml` for machine learning projects (with more ML tools)
|
||||
- Add interactive template customization wizard
|
||||
- Support for different Python versions (pyenv integration)
|
||||
- Add GitHub Actions workflow templates
|
||||
- Add Docker support for projects
|
||||
328
CHANGELOG_v1.2.0.md
Normal file
328
CHANGELOG_v1.2.0.md
Normal file
@@ -0,0 +1,328 @@
|
||||
# Changelog - Version 1.2.0
|
||||
|
||||
## [1.2.0] - 2025-12-16
|
||||
|
||||
### Added
|
||||
|
||||
#### Python Project Templates
|
||||
- **`py-new`** - Basic Python project with venv, tests, and proper structure
|
||||
- **`py-django`** - Django web application template with best practices
|
||||
- **`py-flask`** - Flask web application with blueprints and templates
|
||||
- **`py-fastapi`** - FastAPI REST API with automatic documentation
|
||||
- **`py-data`** - Data science project with Jupyter, pandas, and structured data directories
|
||||
- **`py-cli`** - Command-line tool template using Click framework
|
||||
|
||||
**Features:**
|
||||
- Automatic virtual environment creation
|
||||
- Poetry support (configurable via `PY_TEMPLATE_USE_POETRY`)
|
||||
- Pre-configured .gitignore for Python projects
|
||||
- README with setup instructions
|
||||
- Requirements.txt with common dependencies
|
||||
- Project structure following best practices
|
||||
- Optional git initialization
|
||||
- Quick aliases: `pynew`, `pydjango`, `pyflask`, `pyfast`, `pydata`, `pycli`
|
||||
- `venv` function to quickly activate virtual environments
|
||||
|
||||
#### SSH Session Manager with Tmux Integration
|
||||
- **Save SSH connection profiles** with aliases for quick access
|
||||
- **Automatic tmux session attachment** on remote hosts
|
||||
- **Auto-create named sessions** per server connection
|
||||
- **Fuzzy search connections** with fzf integration
|
||||
- **Dotfiles sync** to remote servers
|
||||
- **Quick reconnect** to last used connection
|
||||
|
||||
**Commands:**
|
||||
- `ssh-save <n> <connection>` - Save connection profile
|
||||
- `ssh-connect <n>` - Connect with auto-tmux attach
|
||||
- `ssh-list` - List all saved profiles
|
||||
- `sshf` - Fuzzy search and connect
|
||||
- `ssh-reconnect` - Quick reconnect to last/specific connection
|
||||
- `ssh-sync-dotfiles <n>` - Deploy dotfiles to remote
|
||||
|
||||
**Aliases:**
|
||||
- `sshl`, `sshs`, `sshc`, `sshd`, `sshr`, `sshsync`
|
||||
|
||||
#### Tmux Workspace Manager
|
||||
- **Pre-configured workspace templates** for different workflows
|
||||
- **Quick workspace creation** from templates
|
||||
- **Session management** with persistence across disconnects
|
||||
- **Custom template creation** by saving current layouts
|
||||
- **Fuzzy search workspaces** with fzf
|
||||
- **Pane synchronization toggle** for multi-server commands
|
||||
|
||||
**Templates:**
|
||||
- `dev` - 3 panes: vim (50%), terminal (25%), logs (25%)
|
||||
- `ops` - 4 panes in grid for monitoring
|
||||
- `ssh-multi` - 4 panes for multi-server management
|
||||
- `debug` - 2 panes: main (70%), helper (30%)
|
||||
- `full` - Single full-screen pane
|
||||
- `review` - Side-by-side comparison
|
||||
|
||||
**Commands:**
|
||||
- `tw <n>` - Quick attach or create workspace
|
||||
- `tw-create <n> [template]` - Create from template
|
||||
- `tw-list` - List all workspaces
|
||||
- `tw-save <n>` - Save current layout as template
|
||||
- `tw-sync` - Toggle pane synchronization
|
||||
- `twf` - Fuzzy search workspaces
|
||||
|
||||
**Aliases:**
|
||||
- `twl`, `twc`, `twa`, `twd`, `tws`, `twt`, `twe`, `twf`
|
||||
|
||||
### Changed
|
||||
|
||||
#### Alias System Cleanup
|
||||
- **Removed `stats` alias** - Forces explicit `dfstats` usage to avoid conflicts with other tools
|
||||
- Updated help text in `dotfiles-cli` to reflect removal
|
||||
- Added clarifying comments in aliases.zsh
|
||||
|
||||
### Configuration
|
||||
|
||||
#### New Python Template Settings (dotfiles.conf)
|
||||
```bash
|
||||
# Python Project Templates
|
||||
PY_TEMPLATE_BASE_DIR="$HOME/projects" # Where to create projects
|
||||
PY_TEMPLATE_PYTHON="python3" # Python executable
|
||||
PY_TEMPLATE_VENV_NAME="venv" # Virtual environment name
|
||||
PY_TEMPLATE_USE_POETRY="false" # Use Poetry instead of venv
|
||||
PY_TEMPLATE_GIT_INIT="true" # Auto-initialize git repos
|
||||
```
|
||||
|
||||
#### New SSH Manager Settings (dotfiles.conf)
|
||||
```bash
|
||||
# SSH Session Manager
|
||||
SSH_AUTO_TMUX="true" # Auto-attach to tmux on connect
|
||||
SSH_TMUX_SESSION_PREFIX="ssh" # Tmux session prefix
|
||||
SSH_SYNC_DOTFILES="ask" # ask, true, or false
|
||||
```
|
||||
|
||||
#### New Tmux Workspace Settings (dotfiles.conf)
|
||||
```bash
|
||||
# Tmux Workspace Manager
|
||||
TW_SESSION_PREFIX="work" # Session name prefix
|
||||
TW_DEFAULT_TEMPLATE="dev" # Default template
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **`stats` alias removed** - Use `dfstats` instead
|
||||
- **Reason:** Potential conflicts with other tools/scripts
|
||||
- **Migration:** Replace `stats` with `dfstats` in any scripts or muscle memory
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
### New Files
|
||||
```
|
||||
zsh/functions/
|
||||
├── python-templates.zsh # Python project templates
|
||||
├── ssh-manager.zsh # SSH session manager
|
||||
└── tmux-workspaces.zsh # Tmux workspace manager
|
||||
|
||||
docs/
|
||||
└── SSH_TMUX_INTEGRATION.md # Complete integration guide
|
||||
|
||||
.ssh-profiles # SSH connection profiles (generated)
|
||||
.tmux-templates/ # Tmux workspace templates (generated)
|
||||
├── dev.tmux
|
||||
├── ops.tmux
|
||||
├── ssh-multi.tmux
|
||||
├── debug.tmux
|
||||
├── full.tmux
|
||||
└── review.tmux
|
||||
```
|
||||
|
||||
### Modified Files
|
||||
- `zsh/aliases.zsh` - Removed `stats` alias
|
||||
- `dotfiles.conf` - New configuration sections (optional)
|
||||
|
||||
---
|
||||
|
||||
## Integration Instructions
|
||||
|
||||
### 1. Add to .zshrc
|
||||
|
||||
Add to the deferred loading section in `.zshrc`:
|
||||
|
||||
```bash
|
||||
_deferred_load() {
|
||||
# ... existing code ...
|
||||
|
||||
# Python project templates
|
||||
[[ -f "$_dotfiles_dir/zsh/functions/python-templates.zsh" ]] && \
|
||||
source "$_dotfiles_dir/zsh/functions/python-templates.zsh"
|
||||
|
||||
# SSH Session Manager
|
||||
[[ -f "$_dotfiles_dir/zsh/functions/ssh-manager.zsh" ]] && \
|
||||
source "$_dotfiles_dir/zsh/functions/ssh-manager.zsh"
|
||||
|
||||
# Tmux Workspace Manager
|
||||
[[ -f "$_dotfiles_dir/zsh/functions/tmux-workspaces.zsh" ]] && \
|
||||
source "$_dotfiles_dir/zsh/functions/tmux-workspaces.zsh"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Reload Shell
|
||||
|
||||
```bash
|
||||
source ~/.zshrc
|
||||
# or
|
||||
exec zsh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Python Templates
|
||||
|
||||
**Basic project:**
|
||||
```bash
|
||||
py-new myproject
|
||||
cd myproject
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
**Django:**
|
||||
```bash
|
||||
py-django myblog
|
||||
cd myblog
|
||||
source venv/bin/activate
|
||||
python manage.py runserver
|
||||
```
|
||||
|
||||
**Data Science:**
|
||||
```bash
|
||||
py-data analysis
|
||||
cd analysis
|
||||
source venv/bin/activate
|
||||
jupyter notebook
|
||||
```
|
||||
|
||||
### SSH + Tmux Workflow
|
||||
|
||||
**Save and connect:**
|
||||
```bash
|
||||
# Save connection
|
||||
ssh-save prod user@prod.example.com 22 ~/.ssh/prod_key
|
||||
|
||||
# Connect (auto-attaches to tmux)
|
||||
ssh-connect prod
|
||||
```
|
||||
|
||||
**Multi-server monitoring:**
|
||||
```bash
|
||||
# Create workspace
|
||||
tw-create monitoring ssh-multi
|
||||
|
||||
# In each pane, connect to different server
|
||||
# Enable sync to run commands on all
|
||||
tw-sync
|
||||
```
|
||||
|
||||
### Tmux Workspaces
|
||||
|
||||
**Quick project setup:**
|
||||
```bash
|
||||
# One command creates workspace with dev template
|
||||
tw myproject
|
||||
|
||||
# Panes ready:
|
||||
# 1. Vim/editor
|
||||
# 2. Terminal
|
||||
# 3. Logs
|
||||
```
|
||||
|
||||
**Custom workflow:**
|
||||
```bash
|
||||
# Create with specific template
|
||||
tw-create backend ops
|
||||
|
||||
# Save current layout for reuse
|
||||
tw-save my-custom-template
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Python templates create correct structure
|
||||
- [ ] Virtual environments activate properly
|
||||
- [ ] SSH profiles save and load correctly
|
||||
- [ ] SSH auto-tmux attachment works on remote
|
||||
- [ ] Tmux templates create expected layouts
|
||||
- [ ] Workspace persistence across sessions
|
||||
- [ ] Fuzzy search works (requires fzf)
|
||||
- [ ] `stats` alias is removed
|
||||
- [ ] `dfstats` still works correctly
|
||||
- [ ] All new aliases function properly
|
||||
|
||||
---
|
||||
|
||||
## Documentation Updates
|
||||
|
||||
### Created
|
||||
- `docs/SSH_TMUX_INTEGRATION.md` - Complete guide for SSH and Tmux features
|
||||
|
||||
### Update Needed
|
||||
- `README.md` - Add Python Templates, SSH Manager, and Tmux Workspaces sections
|
||||
- `README.md` - Update aliases table (remove `stats`)
|
||||
- `SETUP_GUIDE.md` - Add integration instructions
|
||||
- `SETUP_GUIDE.md` - Document configuration options
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements (v1.3.0)
|
||||
|
||||
### Python Templates
|
||||
- Add `py-test` template for testing frameworks
|
||||
- Add `py-package` for PyPI package development
|
||||
- Add `py-ml` for ML projects with more ML tools
|
||||
- Interactive template customization wizard
|
||||
- Pyenv integration for version management
|
||||
- GitHub Actions workflow templates
|
||||
- Docker support for projects
|
||||
|
||||
### SSH & Tmux
|
||||
- SSH connection health monitoring
|
||||
- Auto-reconnect on network drop
|
||||
- Tmux session backup/restore
|
||||
- Remote tmux session discovery
|
||||
- Multi-hop SSH connections
|
||||
- SSH tunnel management
|
||||
- Tmux plugin recommendations
|
||||
|
||||
---
|
||||
|
||||
## Known Issues
|
||||
|
||||
None reported yet.
|
||||
|
||||
---
|
||||
|
||||
## Credits
|
||||
|
||||
- SSH Manager: Inspired by SSH config management tools
|
||||
- Tmux Workspaces: Inspired by tmuxinator and teamocil
|
||||
- Python Templates: Best practices from Python community
|
||||
|
||||
---
|
||||
|
||||
## Upgrade Notes
|
||||
|
||||
This is a **minor version** update with new features. No breaking changes except the intentional removal of the `stats` alias.
|
||||
|
||||
**Recommended upgrade path:**
|
||||
1. Pull latest dotfiles
|
||||
2. Review new configuration options in `dotfiles.conf`
|
||||
3. Add integration code to `.zshrc` (see above)
|
||||
4. Reload shell
|
||||
5. Test new features
|
||||
|
||||
**Optional:**
|
||||
- Customize Python template settings
|
||||
- Set up SSH profiles for your servers
|
||||
- Create custom tmux templates
|
||||
479
SSH_TMUX_INTEGRATION.md
Normal file
479
SSH_TMUX_INTEGRATION.md
Normal file
@@ -0,0 +1,479 @@
|
||||
# SSH & Tmux Integration Guide
|
||||
|
||||
Complete guide for integrating the new SSH Session Manager and Tmux Workspace Manager into your dotfiles.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Add to .zshrc
|
||||
|
||||
Add to the deferred loading section in `.zshrc`:
|
||||
|
||||
```bash
|
||||
_deferred_load() {
|
||||
# ... existing code ...
|
||||
|
||||
# SSH Session Manager
|
||||
[[ -f "$_dotfiles_dir/zsh/functions/ssh-manager.zsh" ]] && \
|
||||
source "$_dotfiles_dir/zsh/functions/ssh-manager.zsh"
|
||||
|
||||
# Tmux Workspace Manager
|
||||
[[ -f "$_dotfiles_dir/zsh/functions/tmux-workspaces.zsh" ]] && \
|
||||
source "$_dotfiles_dir/zsh/functions/tmux-workspaces.zsh"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Reload Shell
|
||||
|
||||
```bash
|
||||
source ~/.zshrc
|
||||
# or
|
||||
exec zsh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SSH Session Manager
|
||||
|
||||
### Basic Usage
|
||||
|
||||
**Save a connection:**
|
||||
```bash
|
||||
ssh-save prod user@prod.example.com
|
||||
ssh-save dev user@dev.example.com 2222 ~/.ssh/dev_key
|
||||
```
|
||||
|
||||
**Connect with auto-tmux:**
|
||||
```bash
|
||||
ssh-connect prod
|
||||
# Automatically attaches to or creates tmux session "ssh-prod"
|
||||
```
|
||||
|
||||
**List all profiles:**
|
||||
```bash
|
||||
ssh-list
|
||||
```
|
||||
|
||||
**Fuzzy search and connect:**
|
||||
```bash
|
||||
sshf
|
||||
# Requires fzf
|
||||
```
|
||||
|
||||
### Advanced Features
|
||||
|
||||
**With port forwarding:**
|
||||
```bash
|
||||
ssh-save vpn user@vpn.com 22 '' '-D 9090' 'VPN with SOCKS proxy'
|
||||
```
|
||||
|
||||
**Edit existing profile:**
|
||||
```bash
|
||||
ssh-edit prod
|
||||
```
|
||||
|
||||
**Quick reconnect:**
|
||||
```bash
|
||||
ssh-reconnect # Reconnects to last connection
|
||||
ssh-reconnect prod # Reconnect to specific profile
|
||||
```
|
||||
|
||||
**Sync dotfiles to remote:**
|
||||
```bash
|
||||
ssh-sync-dotfiles prod
|
||||
# Syncs ~/.dotfiles to remote host
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
Add to `dotfiles.conf`:
|
||||
|
||||
```bash
|
||||
# SSH Session Manager
|
||||
SSH_AUTO_TMUX="true" # Auto-attach to tmux on connect
|
||||
SSH_TMUX_SESSION_PREFIX="ssh" # Tmux session prefix
|
||||
SSH_SYNC_DOTFILES="ask" # ask, true, or false
|
||||
```
|
||||
|
||||
### Aliases
|
||||
|
||||
```bash
|
||||
sshl # ssh-list
|
||||
sshs # ssh-save
|
||||
sshc # ssh-connect
|
||||
sshd # ssh-delete
|
||||
sshr # ssh-reconnect
|
||||
sshsync # ssh-sync-dotfiles
|
||||
sshf # Fuzzy search
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tmux Workspace Manager
|
||||
|
||||
### Basic Usage
|
||||
|
||||
**Create a workspace:**
|
||||
```bash
|
||||
tw-create myproject # Uses default 'dev' template
|
||||
tw-create backend ops # Uses 'ops' template
|
||||
```
|
||||
|
||||
**Quick attach (or create if not exists):**
|
||||
```bash
|
||||
tw myproject
|
||||
```
|
||||
|
||||
**List workspaces:**
|
||||
```bash
|
||||
tw-list
|
||||
# or
|
||||
tw
|
||||
```
|
||||
|
||||
**Delete workspace:**
|
||||
```bash
|
||||
tw-delete myproject
|
||||
```
|
||||
|
||||
### Available Templates
|
||||
|
||||
**dev** - Development (3 panes)
|
||||
- Vim/editor (50% left)
|
||||
- Terminal (25% top-right)
|
||||
- Logs (25% bottom-right)
|
||||
|
||||
**ops** - Operations (4 panes in grid)
|
||||
- Perfect for monitoring multiple things
|
||||
|
||||
**ssh-multi** - Multi-server (4 panes)
|
||||
- Manage multiple SSH connections
|
||||
- Optional pane synchronization
|
||||
|
||||
**debug** - Debugging (2 panes)
|
||||
- Main pane (70%)
|
||||
- Helper pane (30%)
|
||||
|
||||
**full** - Single pane
|
||||
- Just one full-screen pane
|
||||
|
||||
**review** - Code review (2 equal panes)
|
||||
- Side-by-side comparison
|
||||
|
||||
### Working with Templates
|
||||
|
||||
**List available templates:**
|
||||
```bash
|
||||
tw-templates
|
||||
```
|
||||
|
||||
**Edit a template:**
|
||||
```bash
|
||||
tw-template-edit dev
|
||||
```
|
||||
|
||||
**Save current layout as template:**
|
||||
```bash
|
||||
# Inside tmux, arrange your panes how you want
|
||||
tw-save my-custom-layout
|
||||
```
|
||||
|
||||
### Advanced Features
|
||||
|
||||
**Fuzzy search workspaces:**
|
||||
```bash
|
||||
twf
|
||||
# Requires fzf
|
||||
```
|
||||
|
||||
**Rename workspace:**
|
||||
```bash
|
||||
tw-rename old-name new-name
|
||||
```
|
||||
|
||||
**Toggle pane synchronization:**
|
||||
```bash
|
||||
tw-sync
|
||||
# Sends same input to all panes - great for multi-server commands
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
Add to `dotfiles.conf`:
|
||||
|
||||
```bash
|
||||
# Tmux Workspace Manager
|
||||
TW_SESSION_PREFIX="work" # Session name prefix
|
||||
TW_DEFAULT_TEMPLATE="dev" # Default template
|
||||
```
|
||||
|
||||
### Aliases
|
||||
|
||||
```bash
|
||||
tw # Quick attach/create
|
||||
twl # tw-list
|
||||
twc # tw-create
|
||||
twa # tw-attach
|
||||
twd # tw-delete
|
||||
tws # tw-save
|
||||
twt # tw-templates
|
||||
twe # tw-template-edit
|
||||
twf # Fuzzy search
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### Combined Workflow
|
||||
|
||||
**1. Create a workspace for remote work:**
|
||||
```bash
|
||||
# Save SSH connection
|
||||
ssh-save backend-prod user@backend.prod.com 22 ~/.ssh/prod_key
|
||||
|
||||
# Create local workspace to track what you're doing
|
||||
tw-create backend-work dev
|
||||
|
||||
# Connect to remote with auto-tmux
|
||||
ssh-connect backend-prod
|
||||
# Now on remote server in tmux session "ssh-backend-prod"
|
||||
```
|
||||
|
||||
**2. Multi-server monitoring:**
|
||||
```bash
|
||||
# Create workspace for ops
|
||||
tw-create monitoring ops
|
||||
|
||||
# In each pane, connect to different server:
|
||||
# Pane 1: ssh-connect server1
|
||||
# Pane 2: ssh-connect server2
|
||||
# Pane 3: ssh-connect server3
|
||||
# Pane 4: local monitoring
|
||||
|
||||
# Enable synchronization for commands across all
|
||||
tw-sync
|
||||
```
|
||||
|
||||
**3. Development workflow:**
|
||||
```bash
|
||||
# Morning routine - one command:
|
||||
tw myproject
|
||||
|
||||
# If workspace exists: attaches
|
||||
# If not: creates with dev template
|
||||
|
||||
# Inside workspace:
|
||||
# - Pane 1: vim
|
||||
# - Pane 2: run dev server
|
||||
# - Pane 3: tail -f logs/development.log
|
||||
```
|
||||
|
||||
### Custom Template Example
|
||||
|
||||
Create a template for your specific workflow:
|
||||
|
||||
**File:** `~/.dotfiles/.tmux-templates/webdev.tmux`
|
||||
```tmux
|
||||
# Web development workspace
|
||||
# Vim (left) + Dev server (top-right) + Browser sync (bottom-right)
|
||||
|
||||
split-window -h -p 50
|
||||
split-window -v -p 50
|
||||
|
||||
# Auto-start commands
|
||||
send-keys -t 0 'vim' C-m
|
||||
send-keys -t 1 'npm run dev' C-m
|
||||
send-keys -t 2 'npm run watch' C-m
|
||||
|
||||
select-pane -t 0
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
tw-create my-webapp webdev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tips & Tricks
|
||||
|
||||
### SSH Manager
|
||||
|
||||
**1. Auto-sync dotfiles on first connect:**
|
||||
```bash
|
||||
ssh-save newserver user@new.com
|
||||
ssh-sync-dotfiles newserver
|
||||
ssh-connect newserver
|
||||
```
|
||||
|
||||
**2. Use descriptive names:**
|
||||
```bash
|
||||
ssh-save aws-prod-db "user@prod-db.amazonaws.com" 22 ~/.ssh/aws-prod.pem "" "Production Database"
|
||||
```
|
||||
|
||||
**3. Port forwarding shorthand:**
|
||||
```bash
|
||||
# Local port 8080 → Remote port 80
|
||||
ssh-save webapp "user@server.com" 22 "" "-L 8080:localhost:80"
|
||||
```
|
||||
|
||||
### Tmux Workspaces
|
||||
|
||||
**1. Project-specific setup:**
|
||||
Create `.tmux-project` in project root with workspace commands:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
tw-create ${PWD##*/} dev
|
||||
tw ${PWD##*/}
|
||||
```
|
||||
|
||||
**2. Quick workspace switching:**
|
||||
Add to your `.zshrc`:
|
||||
```bash
|
||||
# Switch to workspace by number
|
||||
alias tw1='tw project1'
|
||||
alias tw2='tw project2'
|
||||
alias tw3='tw project3'
|
||||
```
|
||||
|
||||
**3. Persistent sessions:**
|
||||
Workspaces survive reboots if you use `tmux-resurrect` or `tmux-continuum` plugins.
|
||||
|
||||
**4. Multi-pane commands:**
|
||||
```bash
|
||||
# Send command to all panes
|
||||
tw-sync # Enable sync
|
||||
echo "Running on all panes" # Typed in all
|
||||
tw-sync # Disable sync
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tmux Configuration Enhancements
|
||||
|
||||
Add to `~/.tmux.conf` for better integration:
|
||||
|
||||
```tmux
|
||||
# Better pane navigation (vim-style)
|
||||
bind h select-pane -L
|
||||
bind j select-pane -D
|
||||
bind k select-pane -U
|
||||
bind l select-pane -R
|
||||
|
||||
# Quick pane resizing
|
||||
bind -r H resize-pane -L 5
|
||||
bind -r J resize-pane -D 5
|
||||
bind -r K resize-pane -U 5
|
||||
bind -r L resize-pane -R 5
|
||||
|
||||
# Split panes using | and -
|
||||
bind | split-window -h
|
||||
bind - split-window -v
|
||||
|
||||
# Reload config
|
||||
bind r source-file ~/.tmux.conf \; display "Reloaded!"
|
||||
|
||||
# Enable mouse support
|
||||
set -g mouse on
|
||||
|
||||
# Status bar
|
||||
set -g status-position bottom
|
||||
set -g status-style 'bg=colour234 fg=colour137'
|
||||
set -g status-left '#[fg=colour233,bg=colour245,bold] #S '
|
||||
set -g status-right '#[fg=colour233,bg=colour245,bold] %d/%m %H:%M '
|
||||
|
||||
# Pane borders
|
||||
set -g pane-border-style 'fg=colour238'
|
||||
set -g pane-active-border-style 'fg=colour51'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### SSH Issues
|
||||
|
||||
**Connection fails:**
|
||||
```bash
|
||||
# Test connection directly
|
||||
ssh -v user@host
|
||||
|
||||
# Check profile
|
||||
ssh-list
|
||||
ssh-edit myprofile
|
||||
```
|
||||
|
||||
**Tmux not attaching on remote:**
|
||||
```bash
|
||||
# Check if tmux is installed on remote
|
||||
ssh user@host 'which tmux'
|
||||
|
||||
# Disable auto-tmux for specific connection
|
||||
SSH_AUTO_TMUX=false ssh-connect myprofile
|
||||
```
|
||||
|
||||
### Tmux Issues
|
||||
|
||||
**Workspace not found:**
|
||||
```bash
|
||||
# List all tmux sessions
|
||||
tmux ls
|
||||
|
||||
# Check session prefix
|
||||
echo $TW_SESSION_PREFIX
|
||||
```
|
||||
|
||||
**Template not working:**
|
||||
```bash
|
||||
# Validate template syntax
|
||||
cat ~/.dotfiles/.tmux-templates/dev.tmux
|
||||
|
||||
# Recreate default templates
|
||||
rm ~/.dotfiles/.tmux-templates/*
|
||||
source ~/.zshrc # Will regenerate
|
||||
```
|
||||
|
||||
**Panes not splitting correctly:**
|
||||
```bash
|
||||
# Check tmux version
|
||||
tmux -V
|
||||
|
||||
# Update tmux if < 3.0
|
||||
# Some split options may not work on older versions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration from Existing Setup
|
||||
|
||||
### If you already use SSH config:
|
||||
|
||||
Convert `~/.ssh/config` entries to profiles:
|
||||
|
||||
```bash
|
||||
# Old ~/.ssh/config:
|
||||
# Host prod
|
||||
# HostName prod.example.com
|
||||
# User ubuntu
|
||||
# Port 22
|
||||
# IdentityFile ~/.ssh/prod.pem
|
||||
|
||||
# New:
|
||||
ssh-save prod ubuntu@prod.example.com 22 ~/.ssh/prod.pem
|
||||
```
|
||||
|
||||
### If you already use tmux:
|
||||
|
||||
Existing sessions aren't affected. The workspace manager only manages sessions with the `work-` prefix (configurable).
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Save your most-used SSH connections
|
||||
2. Create workspaces for your projects
|
||||
3. Customize templates for your workflow
|
||||
4. Set up project-specific workspace scripts
|
||||
5. Add fuzzy search shortcuts to your workflow
|
||||
|
||||
Enjoy your enhanced terminal productivity!
|
||||
@@ -50,9 +50,8 @@ dfupdate() { _df_run dotfiles-update.sh "$@"; }
|
||||
dfv() { _df_run dotfiles-version.sh "$@"; }
|
||||
dfversion() { _df_run dotfiles-version.sh "$@"; }
|
||||
|
||||
# Stats - shell analytics
|
||||
# Stats - shell analytics (removed short 'stats' alias to force explicit usage)
|
||||
dfstats() { _df_run dotfiles-stats.sh "$@"; }
|
||||
stats() { _df_run dotfiles-stats.sh "$@"; }
|
||||
tophist() { _df_run dotfiles-stats.sh --top "$@"; }
|
||||
suggest() { _df_run dotfiles-stats.sh --suggest "$@"; }
|
||||
|
||||
@@ -108,6 +107,8 @@ dotfiles-cli() {
|
||||
echo
|
||||
echo "Aliases:"
|
||||
echo " dfd, dffix, dfs, dfpush, dfpull, dfu, dfv, dfstats, vault"
|
||||
echo
|
||||
echo "Note: 'stats' alias removed - use 'dfstats' instead"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
1446
zsh/functions/python-templates.zsh
Normal file
1446
zsh/functions/python-templates.zsh
Normal file
File diff suppressed because it is too large
Load Diff
462
zsh/functions/ssh-manager.zsh
Normal file
462
zsh/functions/ssh-manager.zsh
Normal file
@@ -0,0 +1,462 @@
|
||||
# ============================================================================
|
||||
# SSH Session Manager with Tmux Integration
|
||||
# ============================================================================
|
||||
# Manage SSH connections with automatic tmux session handling
|
||||
#
|
||||
# Usage:
|
||||
# ssh-save <name> <connection> # Save SSH connection
|
||||
# ssh-connect <name> # Connect and attach/create tmux session
|
||||
# ssh-list # List all saved connections
|
||||
# ssh-delete <name> # Delete saved connection
|
||||
# ssh-edit <name> # Edit connection details
|
||||
# sshf # Fuzzy search and connect
|
||||
#
|
||||
# Features:
|
||||
# - Automatic tmux session attach/create on remote host
|
||||
# - Named sessions per connection
|
||||
# - Connection profiles with SSH options
|
||||
# - Auto-reconnect support
|
||||
# - Dotfiles sync to remote (optional)
|
||||
#
|
||||
# Add to .zshrc:
|
||||
# source ~/.dotfiles/zsh/functions/ssh-manager.zsh
|
||||
# ============================================================================
|
||||
|
||||
# ============================================================================
|
||||
# Configuration
|
||||
# ============================================================================
|
||||
|
||||
typeset -g SSH_PROFILES_FILE="${SSH_PROFILES_FILE:-$HOME/.dotfiles/.ssh-profiles}"
|
||||
typeset -g SSH_AUTO_TMUX="${SSH_AUTO_TMUX:-true}"
|
||||
typeset -g SSH_TMUX_SESSION_PREFIX="${SSH_TMUX_SESSION_PREFIX:-ssh}"
|
||||
typeset -g SSH_SYNC_DOTFILES="${SSH_SYNC_DOTFILES:-ask}"
|
||||
|
||||
# Colors
|
||||
typeset -g SSH_GREEN=$'\033[0;32m'
|
||||
typeset -g SSH_BLUE=$'\033[0;34m'
|
||||
typeset -g SSH_YELLOW=$'\033[1;33m'
|
||||
typeset -g SSH_CYAN=$'\033[0;36m'
|
||||
typeset -g SSH_RED=$'\033[0;31m'
|
||||
typeset -g SSH_NC=$'\033[0m'
|
||||
|
||||
# ============================================================================
|
||||
# Helper Functions
|
||||
# ============================================================================
|
||||
|
||||
_ssh_print_step() {
|
||||
echo -e "${SSH_BLUE}==>${SSH_NC} $1"
|
||||
}
|
||||
|
||||
_ssh_print_success() {
|
||||
echo -e "${SSH_GREEN}✓${SSH_NC} $1"
|
||||
}
|
||||
|
||||
_ssh_print_error() {
|
||||
echo -e "${SSH_RED}✗${SSH_NC} $1"
|
||||
}
|
||||
|
||||
_ssh_print_info() {
|
||||
echo -e "${SSH_CYAN}ℹ${SSH_NC} $1"
|
||||
}
|
||||
|
||||
_ssh_init_profiles() {
|
||||
if [[ ! -f "$SSH_PROFILES_FILE" ]]; then
|
||||
mkdir -p "$(dirname "$SSH_PROFILES_FILE")"
|
||||
cat > "$SSH_PROFILES_FILE" << 'EOF'
|
||||
# SSH Connection Profiles
|
||||
# Format: name|user@host|port|key_file|options|description
|
||||
#
|
||||
# Example:
|
||||
# prod|user@prod.example.com|22|~/.ssh/prod_key|-L 8080:localhost:80|Production server
|
||||
# dev|user@dev.example.com|2222||ForwardAgent=yes|Development server
|
||||
EOF
|
||||
_ssh_print_success "Created SSH profiles file: $SSH_PROFILES_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
_ssh_parse_profile() {
|
||||
local name="$1"
|
||||
local line=$(grep "^${name}|" "$SSH_PROFILES_FILE" 2>/dev/null | head -1)
|
||||
|
||||
if [[ -z "$line" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Parse: name|connection|port|key|options|description
|
||||
IFS='|' read -r profile_name connection port key_file ssh_opts description <<< "$line"
|
||||
|
||||
echo "$connection|$port|$key_file|$ssh_opts|$description"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# SSH Profile Management
|
||||
# ============================================================================
|
||||
|
||||
ssh-save() {
|
||||
local name="$1"
|
||||
local connection="$2"
|
||||
local port="${3:-22}"
|
||||
local key_file="${4:-}"
|
||||
local options="${5:-}"
|
||||
local description="${6:-}"
|
||||
|
||||
_ssh_init_profiles
|
||||
|
||||
if [[ -z "$name" || -z "$connection" ]]; then
|
||||
echo "Usage: ssh-save <name> <user@host> [port] [key_file] [options] [description]"
|
||||
echo
|
||||
echo "Examples:"
|
||||
echo " ssh-save prod user@prod.com"
|
||||
echo " ssh-save dev user@dev.com 2222 ~/.ssh/dev_key"
|
||||
echo " ssh-save vpn user@vpn.com 22 '' '-D 9090' 'VPN server'"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if profile exists
|
||||
if grep -q "^${name}|" "$SSH_PROFILES_FILE" 2>/dev/null; then
|
||||
echo -e "${SSH_YELLOW}⚠${SSH_NC} Profile '$name' already exists"
|
||||
read -q "REPLY?Overwrite? [y/N]: "
|
||||
echo
|
||||
[[ ! "$REPLY" =~ ^[Yy]$ ]] && return 1
|
||||
|
||||
# Remove old entry
|
||||
grep -v "^${name}|" "$SSH_PROFILES_FILE" > "${SSH_PROFILES_FILE}.tmp"
|
||||
mv "${SSH_PROFILES_FILE}.tmp" "$SSH_PROFILES_FILE"
|
||||
fi
|
||||
|
||||
# Save new profile
|
||||
echo "${name}|${connection}|${port}|${key_file}|${options}|${description}" >> "$SSH_PROFILES_FILE"
|
||||
|
||||
_ssh_print_success "Saved SSH profile: $name"
|
||||
echo " Connection: $connection"
|
||||
[[ "$port" != "22" ]] && echo " Port: $port"
|
||||
[[ -n "$key_file" ]] && echo " Key: $key_file"
|
||||
[[ -n "$options" ]] && echo " Options: $options"
|
||||
[[ -n "$description" ]] && echo " Description: $description"
|
||||
}
|
||||
|
||||
ssh-list() {
|
||||
_ssh_init_profiles
|
||||
|
||||
echo -e "${SSH_BLUE}╔════════════════════════════════════════════════════════════╗${SSH_NC}"
|
||||
echo -e "${SSH_BLUE}║${SSH_NC} SSH Connection Profiles ${SSH_BLUE}║${SSH_NC}"
|
||||
echo -e "${SSH_BLUE}╚════════════════════════════════════════════════════════════╝${SSH_NC}"
|
||||
echo
|
||||
|
||||
local has_profiles=false
|
||||
while IFS='|' read -r name connection port key options description; do
|
||||
# Skip comments and empty lines
|
||||
[[ "$name" =~ ^# ]] && continue
|
||||
[[ -z "$name" ]] && continue
|
||||
|
||||
has_profiles=true
|
||||
|
||||
echo -e "${SSH_GREEN}●${SSH_NC} ${SSH_CYAN}$name${SSH_NC}"
|
||||
echo " Connection: $connection"
|
||||
[[ "$port" != "22" && -n "$port" ]] && echo " Port: $port"
|
||||
[[ -n "$key" ]] && echo " Key: $key"
|
||||
[[ -n "$options" ]] && echo " Options: $options"
|
||||
[[ -n "$description" ]] && echo " Description: $description"
|
||||
echo
|
||||
done < "$SSH_PROFILES_FILE"
|
||||
|
||||
if [[ "$has_profiles" != true ]]; then
|
||||
_ssh_print_info "No profiles saved yet"
|
||||
echo
|
||||
echo "Create a profile with:"
|
||||
echo " ssh-save myserver user@example.com"
|
||||
fi
|
||||
}
|
||||
|
||||
ssh-delete() {
|
||||
local name="$1"
|
||||
|
||||
if [[ -z "$name" ]]; then
|
||||
echo "Usage: ssh-delete <name>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_ssh_init_profiles
|
||||
|
||||
if ! grep -q "^${name}|" "$SSH_PROFILES_FILE" 2>/dev/null; then
|
||||
_ssh_print_error "Profile '$name' not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Remove profile
|
||||
grep -v "^${name}|" "$SSH_PROFILES_FILE" > "${SSH_PROFILES_FILE}.tmp"
|
||||
mv "${SSH_PROFILES_FILE}.tmp" "$SSH_PROFILES_FILE"
|
||||
|
||||
_ssh_print_success "Deleted profile: $name"
|
||||
}
|
||||
|
||||
ssh-edit() {
|
||||
local name="$1"
|
||||
|
||||
if [[ -z "$name" ]]; then
|
||||
# Edit entire file
|
||||
${EDITOR:-vim} "$SSH_PROFILES_FILE"
|
||||
return
|
||||
fi
|
||||
|
||||
_ssh_init_profiles
|
||||
|
||||
local profile_data=$(_ssh_parse_profile "$name")
|
||||
if [[ -z "$profile_data" ]]; then
|
||||
_ssh_print_error "Profile '$name' not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
IFS='|' read -r connection port key_file ssh_opts description <<< "$profile_data"
|
||||
|
||||
echo -e "${SSH_CYAN}Editing profile: $name${SSH_NC}"
|
||||
echo
|
||||
|
||||
read "new_connection?Connection [$connection]: "
|
||||
new_connection="${new_connection:-$connection}"
|
||||
|
||||
read "new_port?Port [$port]: "
|
||||
new_port="${new_port:-$port}"
|
||||
|
||||
read "new_key?Key file [$key_file]: "
|
||||
new_key="${new_key:-$key_file}"
|
||||
|
||||
read "new_opts?SSH options [$ssh_opts]: "
|
||||
new_opts="${new_opts:-$ssh_opts}"
|
||||
|
||||
read "new_desc?Description [$description]: "
|
||||
new_desc="${new_desc:-$description}"
|
||||
|
||||
# Remove old and add new
|
||||
grep -v "^${name}|" "$SSH_PROFILES_FILE" > "${SSH_PROFILES_FILE}.tmp"
|
||||
echo "${name}|${new_connection}|${new_port}|${new_key}|${new_opts}|${new_desc}" >> "${SSH_PROFILES_FILE}.tmp"
|
||||
mv "${SSH_PROFILES_FILE}.tmp" "$SSH_PROFILES_FILE"
|
||||
|
||||
_ssh_print_success "Updated profile: $name"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# SSH Connection with Tmux Integration
|
||||
# ============================================================================
|
||||
|
||||
ssh-connect() {
|
||||
local name="$1"
|
||||
local session_name="${2:-${SSH_TMUX_SESSION_PREFIX}-${name}}"
|
||||
|
||||
if [[ -z "$name" ]]; then
|
||||
echo "Usage: ssh-connect <profile_name> [tmux_session_name]"
|
||||
echo
|
||||
echo "Saved profiles:"
|
||||
ssh-list
|
||||
return 1
|
||||
fi
|
||||
|
||||
_ssh_init_profiles
|
||||
|
||||
# Parse profile
|
||||
local profile_data=$(_ssh_parse_profile "$name")
|
||||
if [[ -z "$profile_data" ]]; then
|
||||
_ssh_print_error "Profile '$name' not found"
|
||||
echo "Use 'ssh-save $name user@host' to create it"
|
||||
return 1
|
||||
fi
|
||||
|
||||
IFS='|' read -r connection port key_file ssh_opts description <<< "$profile_data"
|
||||
|
||||
_ssh_print_step "Connecting to: $name"
|
||||
[[ -n "$description" ]] && echo " $description"
|
||||
|
||||
# Build SSH command
|
||||
local ssh_cmd="ssh"
|
||||
|
||||
# Add port
|
||||
[[ -n "$port" && "$port" != "22" ]] && ssh_cmd="$ssh_cmd -p $port"
|
||||
|
||||
# Add key file
|
||||
[[ -n "$key_file" ]] && ssh_cmd="$ssh_cmd -i $key_file"
|
||||
|
||||
# Add custom options
|
||||
[[ -n "$ssh_opts" ]] && ssh_cmd="$ssh_cmd $ssh_opts"
|
||||
|
||||
# Add connection
|
||||
ssh_cmd="$ssh_cmd $connection"
|
||||
|
||||
# Tmux integration
|
||||
if [[ "$SSH_AUTO_TMUX" == "true" ]]; then
|
||||
_ssh_print_info "Attaching to tmux session: $session_name"
|
||||
|
||||
# SSH with tmux attach or create
|
||||
local tmux_cmd="tmux attach-session -t $session_name 2>/dev/null || tmux new-session -s $session_name"
|
||||
|
||||
# Execute
|
||||
eval "$ssh_cmd -t '$tmux_cmd'"
|
||||
else
|
||||
# Direct SSH without tmux
|
||||
eval "$ssh_cmd"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Fuzzy Search Integration (requires fzf)
|
||||
# ============================================================================
|
||||
|
||||
sshf() {
|
||||
if ! command -v fzf &>/dev/null; then
|
||||
_ssh_print_error "fzf not installed"
|
||||
echo "Install: git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf && ~/.fzf/install"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_ssh_init_profiles
|
||||
|
||||
# Build selection list
|
||||
local profiles=()
|
||||
while IFS='|' read -r name connection port key options description; do
|
||||
[[ "$name" =~ ^# ]] && continue
|
||||
[[ -z "$name" ]] && continue
|
||||
|
||||
local display="$name → $connection"
|
||||
[[ -n "$description" ]] && display="$display ($description)"
|
||||
|
||||
profiles+=("$name|$display")
|
||||
done < "$SSH_PROFILES_FILE"
|
||||
|
||||
if [[ ${#profiles[@]} -eq 0 ]]; then
|
||||
_ssh_print_info "No profiles saved"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Fuzzy select
|
||||
local selection=$(printf '%s\n' "${profiles[@]}" | \
|
||||
fzf --height=50% \
|
||||
--layout=reverse \
|
||||
--border=rounded \
|
||||
--prompt='SSH > ' \
|
||||
--preview='echo {}' \
|
||||
--preview-window=hidden \
|
||||
--delimiter='|' \
|
||||
--with-nth=2)
|
||||
|
||||
if [[ -n "$selection" ]]; then
|
||||
local profile_name="${selection%%|*}"
|
||||
ssh-connect "$profile_name"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Quick Reconnect
|
||||
# ============================================================================
|
||||
|
||||
ssh-reconnect() {
|
||||
local name="${1:-last}"
|
||||
|
||||
if [[ "$name" == "last" ]]; then
|
||||
# Get last connected profile from history
|
||||
local last_profile=$(grep "ssh-connect" "$HISTFILE" 2>/dev/null | tail -1 | awk '{print $2}')
|
||||
|
||||
if [[ -z "$last_profile" ]]; then
|
||||
_ssh_print_error "No previous connection found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
name="$last_profile"
|
||||
fi
|
||||
|
||||
_ssh_print_info "Reconnecting to: $name"
|
||||
ssh-connect "$name"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Dotfiles Sync to Remote
|
||||
# ============================================================================
|
||||
|
||||
ssh-sync-dotfiles() {
|
||||
local name="$1"
|
||||
|
||||
if [[ -z "$name" ]]; then
|
||||
echo "Usage: ssh-sync-dotfiles <profile_name>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local profile_data=$(_ssh_parse_profile "$name")
|
||||
if [[ -z "$profile_data" ]]; then
|
||||
_ssh_print_error "Profile '$name' not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
IFS='|' read -r connection port key_file ssh_opts description <<< "$profile_data"
|
||||
|
||||
local dotfiles_dir="${DOTFILES_DIR:-$HOME/.dotfiles}"
|
||||
|
||||
if [[ ! -d "$dotfiles_dir" ]]; then
|
||||
_ssh_print_error "Dotfiles directory not found: $dotfiles_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_ssh_print_step "Syncing dotfiles to: $connection"
|
||||
|
||||
# Build rsync command
|
||||
local rsync_cmd="rsync -avz --exclude='.git' --exclude='*.local'"
|
||||
|
||||
[[ -n "$port" && "$port" != "22" ]] && rsync_cmd="$rsync_cmd -e 'ssh -p $port'"
|
||||
[[ -n "$key_file" ]] && rsync_cmd="$rsync_cmd -e 'ssh -i $key_file'"
|
||||
|
||||
rsync_cmd="$rsync_cmd $dotfiles_dir/ $connection:.dotfiles/"
|
||||
|
||||
_ssh_print_info "Running: $rsync_cmd"
|
||||
|
||||
if eval "$rsync_cmd"; then
|
||||
_ssh_print_success "Dotfiles synced successfully"
|
||||
|
||||
# Optionally run install script on remote
|
||||
read -q "REPLY?Run install script on remote? [y/N]: "
|
||||
echo
|
||||
if [[ "$REPLY" =~ ^[Yy]$ ]]; then
|
||||
local ssh_cmd="ssh"
|
||||
[[ -n "$port" && "$port" != "22" ]] && ssh_cmd="$ssh_cmd -p $port"
|
||||
[[ -n "$key_file" ]] && ssh_cmd="$ssh_cmd -i $key_file"
|
||||
|
||||
eval "$ssh_cmd $connection 'cd .dotfiles && ./install.sh --skip-deps'"
|
||||
fi
|
||||
else
|
||||
_ssh_print_error "Failed to sync dotfiles"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Aliases
|
||||
# ============================================================================
|
||||
|
||||
alias sshl='ssh-list'
|
||||
alias sshs='ssh-save'
|
||||
alias sshc='ssh-connect'
|
||||
alias sshd='ssh-delete'
|
||||
alias sshr='ssh-reconnect'
|
||||
alias sshsync='ssh-sync-dotfiles'
|
||||
|
||||
# ============================================================================
|
||||
# Completion Helper
|
||||
# ============================================================================
|
||||
|
||||
_ssh_manager_profiles() {
|
||||
local profiles=()
|
||||
while IFS='|' read -r name rest; do
|
||||
[[ "$name" =~ ^# ]] && continue
|
||||
[[ -z "$name" ]] && continue
|
||||
profiles+=("$name")
|
||||
done < "$SSH_PROFILES_FILE" 2>/dev/null
|
||||
|
||||
echo "${profiles[@]}"
|
||||
}
|
||||
|
||||
# ZSH completion (if you want to add it)
|
||||
# compdef '_arguments "1:profile:($(_ssh_manager_profiles))"' ssh-connect
|
||||
# compdef '_arguments "1:profile:($(_ssh_manager_profiles))"' ssh-delete
|
||||
# compdef '_arguments "1:profile:($(_ssh_manager_profiles))"' ssh-edit
|
||||
|
||||
# ============================================================================
|
||||
# Initialization
|
||||
# ============================================================================
|
||||
|
||||
_ssh_init_profiles
|
||||
575
zsh/functions/tmux-workspaces.zsh
Normal file
575
zsh/functions/tmux-workspaces.zsh
Normal file
@@ -0,0 +1,575 @@
|
||||
# ============================================================================
|
||||
# Tmux Workspace Manager - Project Templates & Layouts
|
||||
# ============================================================================
|
||||
# Quick project workspace setup with pre-configured tmux layouts
|
||||
#
|
||||
# Usage:
|
||||
# tw-create <name> [template] # Create workspace from template
|
||||
# tw-attach <name> # Attach to workspace
|
||||
# tw-list # List all workspaces
|
||||
# tw-delete <name> # Delete workspace
|
||||
# tw-save <name> # Save current layout as template
|
||||
# tw <name> # Quick attach (or create if not exists)
|
||||
#
|
||||
# Templates:
|
||||
# dev - Vim (50%) + terminal (25%) + logs (25%)
|
||||
# ops - 4 panes: htop, logs, shell, monitoring
|
||||
# ssh-multi - 4 panes for managing multiple servers
|
||||
# debug - 2 panes: main (70%) + helper (30%)
|
||||
# full - Just one full pane
|
||||
#
|
||||
# Add to .zshrc:
|
||||
# source ~/.dotfiles/zsh/functions/tmux-workspaces.zsh
|
||||
# ============================================================================
|
||||
|
||||
# ============================================================================
|
||||
# Configuration
|
||||
# ============================================================================
|
||||
|
||||
typeset -g TW_TEMPLATES_DIR="${TW_TEMPLATES_DIR:-$HOME/.dotfiles/.tmux-templates}"
|
||||
typeset -g TW_SESSION_PREFIX="${TW_SESSION_PREFIX:-work}"
|
||||
typeset -g TW_DEFAULT_TEMPLATE="${TW_DEFAULT_TEMPLATE:-dev}"
|
||||
|
||||
# Colors
|
||||
typeset -g TW_GREEN=$'\033[0;32m'
|
||||
typeset -g TW_BLUE=$'\033[0;34m'
|
||||
typeset -g TW_YELLOW=$'\033[1;33m'
|
||||
typeset -g TW_CYAN=$'\033[0;36m'
|
||||
typeset -g TW_RED=$'\033[0;31m'
|
||||
typeset -g TW_NC=$'\033[0m'
|
||||
|
||||
# ============================================================================
|
||||
# Helper Functions
|
||||
# ============================================================================
|
||||
|
||||
_tw_print_step() {
|
||||
echo -e "${TW_BLUE}==>${TW_NC} $1"
|
||||
}
|
||||
|
||||
_tw_print_success() {
|
||||
echo -e "${TW_GREEN}✓${TW_NC} $1"
|
||||
}
|
||||
|
||||
_tw_print_error() {
|
||||
echo -e "${TW_RED}✗${TW_NC} $1"
|
||||
}
|
||||
|
||||
_tw_print_info() {
|
||||
echo -e "${TW_CYAN}ℹ${TW_NC} $1"
|
||||
}
|
||||
|
||||
_tw_check_tmux() {
|
||||
if ! command -v tmux &>/dev/null; then
|
||||
_tw_print_error "tmux not installed"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
_tw_init_templates() {
|
||||
mkdir -p "$TW_TEMPLATES_DIR"
|
||||
|
||||
# Create default templates if they don't exist
|
||||
if [[ ! -f "$TW_TEMPLATES_DIR/dev.tmux" ]]; then
|
||||
_tw_create_default_templates
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Default Template Definitions
|
||||
# ============================================================================
|
||||
|
||||
_tw_create_default_templates() {
|
||||
_tw_print_step "Creating default templates..."
|
||||
|
||||
# Development template - vim + terminal + logs
|
||||
cat > "$TW_TEMPLATES_DIR/dev.tmux" << 'EOF'
|
||||
# Development workspace
|
||||
# Usage: tw-create myproject dev
|
||||
|
||||
# Split vertically (vim on left 50%, rest on right)
|
||||
split-window -h -p 50
|
||||
|
||||
# Split right pane horizontally (terminal top, logs bottom)
|
||||
split-window -v -p 50
|
||||
|
||||
# Select the first pane (vim)
|
||||
select-pane -t 0
|
||||
|
||||
# Optional: Start vim in first pane
|
||||
# send-keys -t 0 'vim' C-m
|
||||
|
||||
# Optional: Set pane titles
|
||||
# select-pane -t 0 -T "Editor"
|
||||
# select-pane -t 1 -T "Terminal"
|
||||
# select-pane -t 2 -T "Logs"
|
||||
EOF
|
||||
|
||||
# Operations template - 4 panes for monitoring
|
||||
cat > "$TW_TEMPLATES_DIR/ops.tmux" << 'EOF'
|
||||
# Operations workspace
|
||||
# 4-pane layout for system monitoring
|
||||
|
||||
# Create 2x2 grid
|
||||
split-window -h -p 50
|
||||
split-window -v -p 50
|
||||
select-pane -t 0
|
||||
split-window -v -p 50
|
||||
|
||||
# Optional: Auto-start monitoring tools
|
||||
# send-keys -t 0 'htop' C-m
|
||||
# send-keys -t 1 'docker ps' C-m
|
||||
# send-keys -t 2 '' C-m
|
||||
# send-keys -t 3 'tail -f /var/log/syslog' C-m
|
||||
|
||||
select-pane -t 0
|
||||
EOF
|
||||
|
||||
# SSH multi-server template
|
||||
cat > "$TW_TEMPLATES_DIR/ssh-multi.tmux" << 'EOF'
|
||||
# Multi-server SSH workspace
|
||||
# 4 panes for managing multiple servers
|
||||
|
||||
# Create 2x2 grid
|
||||
split-window -h -p 50
|
||||
split-window -v -p 50
|
||||
select-pane -t 0
|
||||
split-window -v -p 50
|
||||
|
||||
# Enable pane synchronization (optional - uncomment to enable)
|
||||
# set-window-option synchronize-panes on
|
||||
|
||||
select-pane -t 0
|
||||
EOF
|
||||
|
||||
# Debug template - main + helper pane
|
||||
cat > "$TW_TEMPLATES_DIR/debug.tmux" << 'EOF'
|
||||
# Debug workspace
|
||||
# Main pane (70%) + helper pane (30%)
|
||||
|
||||
split-window -h -p 30
|
||||
|
||||
select-pane -t 0
|
||||
EOF
|
||||
|
||||
# Full template - single pane
|
||||
cat > "$TW_TEMPLATES_DIR/full.tmux" << 'EOF'
|
||||
# Full workspace
|
||||
# Single full-screen pane (default tmux behavior)
|
||||
EOF
|
||||
|
||||
# Code review template - side-by-side comparison
|
||||
cat > "$TW_TEMPLATES_DIR/review.tmux" << 'EOF'
|
||||
# Code Review workspace
|
||||
# Two equal panes side-by-side for comparison
|
||||
|
||||
split-window -h -p 50
|
||||
|
||||
select-pane -t 0
|
||||
EOF
|
||||
|
||||
_tw_print_success "Created default templates in: $TW_TEMPLATES_DIR"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Template Management
|
||||
# ============================================================================
|
||||
|
||||
tw-templates() {
|
||||
_tw_init_templates
|
||||
|
||||
echo -e "${TW_BLUE}╔════════════════════════════════════════════════════════════╗${TW_NC}"
|
||||
echo -e "${TW_BLUE}║${TW_NC} Available Tmux Templates ${TW_BLUE}║${TW_NC}"
|
||||
echo -e "${TW_BLUE}╚════════════════════════════════════════════════════════════╝${TW_NC}"
|
||||
echo
|
||||
|
||||
for template in "$TW_TEMPLATES_DIR"/*.tmux; do
|
||||
[[ ! -f "$template" ]] && continue
|
||||
|
||||
local name=$(basename "$template" .tmux)
|
||||
local description=$(grep "^#" "$template" | head -2 | tail -1 | sed 's/^# *//')
|
||||
|
||||
echo -e "${TW_GREEN}●${TW_NC} ${TW_CYAN}$name${TW_NC}"
|
||||
[[ -n "$description" ]] && echo " $description"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Create workspace: ${TW_CYAN}tw-create myproject dev${TW_NC}"
|
||||
echo "Quick attach: ${TW_CYAN}tw myproject${TW_NC}"
|
||||
}
|
||||
|
||||
tw-template-edit() {
|
||||
local template_name="$1"
|
||||
|
||||
if [[ -z "$template_name" ]]; then
|
||||
echo "Usage: tw-template-edit <template_name>"
|
||||
echo
|
||||
tw-templates
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_init_templates
|
||||
|
||||
local template_file="$TW_TEMPLATES_DIR/${template_name}.tmux"
|
||||
|
||||
${EDITOR:-vim} "$template_file"
|
||||
|
||||
_tw_print_success "Template edited: $template_name"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Workspace Management
|
||||
# ============================================================================
|
||||
|
||||
tw-create() {
|
||||
local workspace_name="$1"
|
||||
local template="${2:-$TW_DEFAULT_TEMPLATE}"
|
||||
|
||||
if [[ -z "$workspace_name" ]]; then
|
||||
echo "Usage: tw-create <workspace_name> [template]"
|
||||
echo
|
||||
tw-templates
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
_tw_init_templates
|
||||
|
||||
local session_name="${TW_SESSION_PREFIX}-${workspace_name}"
|
||||
|
||||
# Check if session already exists
|
||||
if tmux has-session -t "$session_name" 2>/dev/null; then
|
||||
_tw_print_error "Workspace '$workspace_name' already exists"
|
||||
echo "Use: ${TW_CYAN}tw $workspace_name${TW_NC} to attach"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if template exists
|
||||
local template_file="$TW_TEMPLATES_DIR/${template}.tmux"
|
||||
if [[ ! -f "$template_file" ]]; then
|
||||
_tw_print_error "Template '$template' not found"
|
||||
tw-templates
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_print_step "Creating workspace: $workspace_name (template: $template)"
|
||||
|
||||
# Create new tmux session (detached)
|
||||
tmux new-session -d -s "$session_name"
|
||||
|
||||
# Apply template
|
||||
_tw_print_step "Applying template: $template"
|
||||
tmux source-file "$template_file" -t "$session_name"
|
||||
|
||||
# Set working directory if we're in a git repo or specific directory
|
||||
if git rev-parse --git-dir &>/dev/null 2>&1; then
|
||||
local git_root=$(git rev-parse --show-toplevel)
|
||||
_tw_print_info "Setting workspace directory to: $git_root"
|
||||
tmux send-keys -t "$session_name:0" "cd $git_root" C-m
|
||||
fi
|
||||
|
||||
_tw_print_success "Workspace created: $workspace_name"
|
||||
|
||||
# Attach if not already in tmux
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
_tw_print_step "Attaching to workspace..."
|
||||
tmux attach-session -t "$session_name"
|
||||
else
|
||||
_tw_print_info "Switch with: ${TW_CYAN}tmux switch-client -t $session_name${TW_NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
tw-attach() {
|
||||
local workspace_name="$1"
|
||||
|
||||
if [[ -z "$workspace_name" ]]; then
|
||||
echo "Usage: tw-attach <workspace_name>"
|
||||
echo
|
||||
tw-list
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
local session_name="${TW_SESSION_PREFIX}-${workspace_name}"
|
||||
|
||||
if ! tmux has-session -t "$session_name" 2>/dev/null; then
|
||||
_tw_print_error "Workspace '$workspace_name' not found"
|
||||
echo
|
||||
echo "Create it with: ${TW_CYAN}tw-create $workspace_name${TW_NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Attach or switch
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
tmux attach-session -t "$session_name"
|
||||
else
|
||||
tmux switch-client -t "$session_name"
|
||||
fi
|
||||
}
|
||||
|
||||
tw-list() {
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
echo -e "${TW_BLUE}╔════════════════════════════════════════════════════════════╗${TW_NC}"
|
||||
echo -e "${TW_BLUE}║${TW_NC} Active Tmux Workspaces ${TW_BLUE}║${TW_NC}"
|
||||
echo -e "${TW_BLUE}╚════════════════════════════════════════════════════════════╝${TW_NC}"
|
||||
echo
|
||||
|
||||
local has_workspaces=false
|
||||
|
||||
# List all tmux sessions
|
||||
tmux list-sessions 2>/dev/null | while IFS=: read -r session_full rest; do
|
||||
# Only show sessions with our prefix
|
||||
if [[ "$session_full" == ${TW_SESSION_PREFIX}-* ]]; then
|
||||
has_workspaces=true
|
||||
local workspace_name="${session_full#${TW_SESSION_PREFIX}-}"
|
||||
local attached=""
|
||||
|
||||
# Check if currently attached
|
||||
if [[ -n "$TMUX" ]]; then
|
||||
local current_session=$(tmux display-message -p '#S')
|
||||
[[ "$current_session" == "$session_full" ]] && attached=" ${TW_GREEN}(current)${TW_NC}"
|
||||
fi
|
||||
|
||||
echo -e "${TW_GREEN}●${TW_NC} ${TW_CYAN}$workspace_name${TW_NC}$attached"
|
||||
echo " Session: $session_full"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$has_workspaces" != true ]]; then
|
||||
_tw_print_info "No active workspaces"
|
||||
echo
|
||||
echo "Create one with: ${TW_CYAN}tw-create myproject${TW_NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
tw-delete() {
|
||||
local workspace_name="$1"
|
||||
|
||||
if [[ -z "$workspace_name" ]]; then
|
||||
echo "Usage: tw-delete <workspace_name>"
|
||||
echo
|
||||
tw-list
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
local session_name="${TW_SESSION_PREFIX}-${workspace_name}"
|
||||
|
||||
if ! tmux has-session -t "$session_name" 2>/dev/null; then
|
||||
_tw_print_error "Workspace '$workspace_name' not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Kill session
|
||||
tmux kill-session -t "$session_name"
|
||||
|
||||
_tw_print_success "Deleted workspace: $workspace_name"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Save Current Layout as Template
|
||||
# ============================================================================
|
||||
|
||||
tw-save() {
|
||||
local template_name="$1"
|
||||
|
||||
if [[ -z "$template_name" ]]; then
|
||||
echo "Usage: tw-save <template_name>"
|
||||
echo
|
||||
echo "Saves the current tmux window layout as a reusable template"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
_tw_print_error "Must be run from inside tmux"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_init_templates
|
||||
|
||||
local template_file="$TW_TEMPLATES_DIR/${template_name}.tmux"
|
||||
|
||||
if [[ -f "$template_file" ]]; then
|
||||
read -q "REPLY?Template '$template_name' exists. Overwrite? [y/N]: "
|
||||
echo
|
||||
[[ ! "$REPLY" =~ ^[Yy]$ ]] && return 1
|
||||
fi
|
||||
|
||||
_tw_print_step "Saving current layout as template: $template_name"
|
||||
|
||||
# Get current window layout
|
||||
local layout=$(tmux display-message -p '#{window_layout}')
|
||||
local pane_count=$(tmux display-message -p '#{window_panes}')
|
||||
|
||||
# Create template with layout commands
|
||||
cat > "$template_file" << EOF
|
||||
# Custom template: $template_name
|
||||
# Saved: $(date)
|
||||
# Panes: $pane_count
|
||||
|
||||
# Note: This is a simplified layout recreation
|
||||
# You may need to adjust split percentages and commands
|
||||
|
||||
EOF
|
||||
|
||||
# Generate split commands based on pane count
|
||||
if (( pane_count == 2 )); then
|
||||
echo "split-window -h -p 50" >> "$template_file"
|
||||
elif (( pane_count == 3 )); then
|
||||
cat >> "$template_file" << 'EOF'
|
||||
split-window -h -p 50
|
||||
split-window -v -p 50
|
||||
EOF
|
||||
elif (( pane_count == 4 )); then
|
||||
cat >> "$template_file" << 'EOF'
|
||||
split-window -h -p 50
|
||||
split-window -v -p 50
|
||||
select-pane -t 0
|
||||
split-window -v -p 50
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo "" >> "$template_file"
|
||||
echo "select-pane -t 0" >> "$template_file"
|
||||
|
||||
_tw_print_success "Template saved: $template_name"
|
||||
echo " File: $template_file"
|
||||
echo " Edit: ${TW_CYAN}tw-template-edit $template_name${TW_NC}"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Quick Workspace (attach or create)
|
||||
# ============================================================================
|
||||
|
||||
tw() {
|
||||
local workspace_name="$1"
|
||||
local template="${2:-$TW_DEFAULT_TEMPLATE}"
|
||||
|
||||
if [[ -z "$workspace_name" ]]; then
|
||||
tw-list
|
||||
return 0
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
local session_name="${TW_SESSION_PREFIX}-${workspace_name}"
|
||||
|
||||
# If session exists, attach. Otherwise create.
|
||||
if tmux has-session -t "$session_name" 2>/dev/null; then
|
||||
tw-attach "$workspace_name"
|
||||
else
|
||||
_tw_print_info "Workspace doesn't exist. Creating with template: $template"
|
||||
tw-create "$workspace_name" "$template"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Fuzzy Search (requires fzf)
|
||||
# ============================================================================
|
||||
|
||||
twf() {
|
||||
if ! command -v fzf &>/dev/null; then
|
||||
_tw_print_error "fzf not installed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
# Get list of sessions
|
||||
local sessions=()
|
||||
tmux list-sessions 2>/dev/null | while IFS=: read -r session_full rest; do
|
||||
if [[ "$session_full" == ${TW_SESSION_PREFIX}-* ]]; then
|
||||
local workspace_name="${session_full#${TW_SESSION_PREFIX}-}"
|
||||
sessions+=("$workspace_name")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#sessions[@]} -eq 0 ]]; then
|
||||
_tw_print_info "No workspaces found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Fuzzy select
|
||||
local selection=$(printf '%s\n' "${sessions[@]}" | \
|
||||
fzf --height=40% \
|
||||
--layout=reverse \
|
||||
--border=rounded \
|
||||
--prompt='Workspace > ' \
|
||||
--preview='tmux list-windows -t work-{} 2>/dev/null || echo "No preview"')
|
||||
|
||||
if [[ -n "$selection" ]]; then
|
||||
tw-attach "$selection"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Pane Synchronization Toggle
|
||||
# ============================================================================
|
||||
|
||||
tw-sync() {
|
||||
if [[ -z "$TMUX" ]]; then
|
||||
_tw_print_error "Must be run from inside tmux"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local current=$(tmux show-window-option -v synchronize-panes 2>/dev/null)
|
||||
|
||||
if [[ "$current" == "on" ]]; then
|
||||
tmux set-window-option synchronize-panes off
|
||||
_tw_print_info "Pane synchronization: ${TW_RED}OFF${TW_NC}"
|
||||
else
|
||||
tmux set-window-option synchronize-panes on
|
||||
_tw_print_info "Pane synchronization: ${TW_GREEN}ON${TW_NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Rename Workspace
|
||||
# ============================================================================
|
||||
|
||||
tw-rename() {
|
||||
local old_name="$1"
|
||||
local new_name="$2"
|
||||
|
||||
if [[ -z "$old_name" || -z "$new_name" ]]; then
|
||||
echo "Usage: tw-rename <old_name> <new_name>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_tw_check_tmux || return 1
|
||||
|
||||
local old_session="${TW_SESSION_PREFIX}-${old_name}"
|
||||
local new_session="${TW_SESSION_PREFIX}-${new_name}"
|
||||
|
||||
if ! tmux has-session -t "$old_session" 2>/dev/null; then
|
||||
_tw_print_error "Workspace '$old_name' not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
tmux rename-session -t "$old_session" "$new_session"
|
||||
|
||||
_tw_print_success "Renamed: $old_name → $new_name"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Aliases
|
||||
# ============================================================================
|
||||
|
||||
alias twl='tw-list'
|
||||
alias twc='tw-create'
|
||||
alias twa='tw-attach'
|
||||
alias twd='tw-delete'
|
||||
alias tws='tw-save'
|
||||
alias twt='tw-templates'
|
||||
alias twe='tw-template-edit'
|
||||
|
||||
# ============================================================================
|
||||
# Initialization
|
||||
# ============================================================================
|
||||
|
||||
_tw_init_templates
|
||||
Reference in New Issue
Block a user