Skip to content

Instantly share code, notes, and snippets.

@baldwindavid
Created October 28, 2025 20:18
Show Gist options
  • Save baldwindavid/4c4095ac09c663d9ace93f5efac247aa to your computer and use it in GitHub Desktop.
Save baldwindavid/4c4095ac09c663d9ace93f5efac247aa to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
# Wrapper around zellij run that adds move-or-create behavior for directional panes
# Usage: run.sh [OPTIONS] -- <command> [args...]
#
# Accepts all zellij run options. Key options:
# --floating Open in a floating pane
# --direction <dir> Open in direction (up, down, left, right)
# --close-on-exit Close pane when command exits
# --name <name> Name the pane
# --cwd <path> Set working directory
#
# For --direction, will reuse existing pane if one exists in that direction,
# otherwise creates a new pane.
#
# Caches the last command per-directory for rerun functionality.
set -e
# Cache the command for rerun functionality
CACHE_DIR="/tmp/helix_last_command_$USER"
mkdir -p "$CACHE_DIR"
# Use PWD hash for per-directory caching (portable across macOS and Linux)
if command -v md5sum &> /dev/null; then
PWD_HASH=$(echo -n "$PWD" | md5sum | cut -d' ' -f1)
elif command -v md5 &> /dev/null; then
PWD_HASH=$(echo -n "$PWD" | md5)
else
# Fallback: use base64-encoded path (less elegant but works everywhere)
PWD_HASH=$(echo -n "$PWD" | base64 | tr -d '=\n' | tr '/' '_')
fi
CACHE_FILE="$CACHE_DIR/$PWD_HASH"
# Save all args and PWD to cache file
cache_command() {
{
echo "PWD=$PWD"
echo -n "ARGS=("
printf '%q ' "$@"
echo ")"
} > "$CACHE_FILE"
}
# Cache the command with all original arguments
cache_command "$@"
# Parse args to find --floating or --direction
is_floating=false
direction=""
remaining_args=()
while [ "$#" -gt 0 ]; do
case "$1" in
--floating|-f)
is_floating=true
remaining_args+=("$1")
shift
;;
--direction|-d)
direction="$2"
remaining_args+=("$1" "$2")
shift 2
;;
*)
remaining_args+=("$1")
shift
;;
esac
done
# For floating, add default size/position if not specified, then delegate to zellij run
if [ "$is_floating" = true ]; then
# Check if user specified dimensions/position
has_width=false
has_height=false
has_x=false
has_y=false
for arg in "${remaining_args[@]}"; do
case "$arg" in
--width) has_width=true ;;
--height) has_height=true ;;
--x|-x) has_x=true ;;
--y|-y) has_y=true ;;
esac
done
# Build default args for large centered floating pane (90% width/height, centered at 5%/5%)
default_args=()
[ "$has_width" = false ] && default_args+=(--width "90%")
[ "$has_height" = false ] && default_args+=(--height "90%")
[ "$has_x" = false ] && default_args+=(--x "5%")
[ "$has_y" = false ] && default_args+=(--y "5%")
exec zellij run --cwd "$PWD" "${default_args[@]}" "${remaining_args[@]}"
fi
# If no direction specified, delegate to zellij run (it will pick best location)
if [ -z "$direction" ]; then
exec zellij run --cwd "$PWD" "${remaining_args[@]}"
fi
# For directional targets, implement move-or-create behavior
# Get current focused pane ID before moving
pane_before=$(zellij action list-clients | tail -n +2 | head -n 1 | awk '{print $2}')
# Try to move in the specified direction
zellij action move-focus "$direction"
# Get focused pane ID after moving
pane_after=$(zellij action list-clients | tail -n +2 | head -n 1 | awk '{print $2}')
# Remove --direction from remaining_args since we'll replace it
final_args=()
skip_next=false
for arg in "${remaining_args[@]}"; do
if [ "$skip_next" = true ]; then
skip_next=false
continue
fi
if [ "$arg" = "--direction" ] || [ "$arg" = "-d" ]; then
skip_next=true
continue
fi
final_args+=("$arg")
done
# If pane IDs are different, we moved to an existing pane - write command to it
# If same, create new pane in that direction
# NOTE: --close-on-exit and other zellij run flags only affect newly created panes.
# Existing panes retain their original behavior regardless of flags passed to this script.
if [ "$pane_before" != "$pane_after" ]; then
# Existing pane - extract the command after "--" and write it
# Find the command after "--"
found_separator=false
command_parts=()
for arg in "${final_args[@]}"; do
if [ "$found_separator" = true ]; then
command_parts+=("$arg")
elif [ "$arg" = "--" ]; then
found_separator=true
fi
done
# Build the command string
command_str="${command_parts[*]}"
# Write command to the existing pane
zellij action write-chars "cd '$PWD' && $command_str"
zellij action write 13 # Send enter
else
# No pane exists - create new one
exec zellij run --direction "$direction" --cwd "$PWD" "${final_args[@]}"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment