@pm-2001/shellup 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +96 -0
- package/dist/cli.js +883 -0
- package/package.json +57 -0
- package/runtime/aliases.zsh +46 -0
- package/runtime/functions.zsh +56 -0
- package/runtime/keybindings.zsh +45 -0
- package/runtime/options.zsh +59 -0
- package/runtime/prompt.zsh +161 -0
- package/runtime/themes/minimal.zsh +29 -0
- package/runtime/themes/neon.zsh +29 -0
- package/runtime/themes/powerline.zsh +36 -0
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pm-2001/shellup",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Level up your terminal in 60 seconds — a beautiful prompt, modern tool swaps, and sane defaults, installed by one command and reversible by another.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"terminal",
|
|
7
|
+
"zsh",
|
|
8
|
+
"prompt",
|
|
9
|
+
"cli",
|
|
10
|
+
"dotfiles",
|
|
11
|
+
"shell",
|
|
12
|
+
"developer-experience",
|
|
13
|
+
"theme",
|
|
14
|
+
"productivity"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://github.com/pm-2001/shell-up#readme",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/pm-2001/shell-up.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/pm-2001/shell-up/issues"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"author": "pm-2001",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"bin": {
|
|
28
|
+
"shellup": "dist/cli.js"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"runtime",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsup",
|
|
44
|
+
"dev": "tsup --watch",
|
|
45
|
+
"typecheck": "tsc --noEmit",
|
|
46
|
+
"prepublishOnly": "npm run typecheck && npm run build"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@clack/prompts": "^1.7.0",
|
|
50
|
+
"picocolors": "^1.1.1"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^26.4.0",
|
|
54
|
+
"tsup": "^8.5.1",
|
|
55
|
+
"typescript": "^7.0.2"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# shellup — aliases
|
|
2
|
+
# Only aliases that are safe without any extra binary installed. Anything that
|
|
3
|
+
# depends on eza/bat/lazygit lives in generated/tools.zsh behind a guard.
|
|
4
|
+
|
|
5
|
+
[[ -o interactive ]] || return 0
|
|
6
|
+
|
|
7
|
+
# ── Navigation ─────────────────────────────────────────────────────────────
|
|
8
|
+
alias ..='cd ..'
|
|
9
|
+
alias ...='cd ../..'
|
|
10
|
+
alias ....='cd ../../..'
|
|
11
|
+
alias -- -='cd -'
|
|
12
|
+
|
|
13
|
+
# ── Safety rails ───────────────────────────────────────────────────────────
|
|
14
|
+
alias cp='cp -i'
|
|
15
|
+
alias mv='mv -i'
|
|
16
|
+
alias mkdir='mkdir -p'
|
|
17
|
+
|
|
18
|
+
# ── git ────────────────────────────────────────────────────────────────────
|
|
19
|
+
alias g='git'
|
|
20
|
+
alias gs='git status --short --branch'
|
|
21
|
+
alias ga='git add'
|
|
22
|
+
alias gaa='git add --all'
|
|
23
|
+
alias gc='git commit'
|
|
24
|
+
alias gcm='git commit -m'
|
|
25
|
+
alias gca='git commit --amend'
|
|
26
|
+
alias gco='git checkout'
|
|
27
|
+
alias gsw='git switch'
|
|
28
|
+
alias gb='git branch'
|
|
29
|
+
alias gd='git diff'
|
|
30
|
+
alias gds='git diff --staged'
|
|
31
|
+
alias gp='git push'
|
|
32
|
+
alias gpl='git pull'
|
|
33
|
+
alias gf='git fetch --all --prune'
|
|
34
|
+
alias gst='git stash'
|
|
35
|
+
alias gl='git log --oneline --graph --decorate -20'
|
|
36
|
+
alias gll='git log --oneline --graph --decorate --all'
|
|
37
|
+
alias gundo='git reset --soft HEAD~1'
|
|
38
|
+
alias gwip='git add -A && git commit -m "wip" --no-verify'
|
|
39
|
+
|
|
40
|
+
# ── Shell housekeeping ─────────────────────────────────────────────────────
|
|
41
|
+
alias reload='exec zsh'
|
|
42
|
+
alias path='print -l $path'
|
|
43
|
+
alias now='date "+%Y-%m-%d %H:%M:%S"'
|
|
44
|
+
alias week='date +%V'
|
|
45
|
+
alias ports='lsof -iTCP -sTCP:LISTEN -P -n'
|
|
46
|
+
alias myip='curl -s https://api.ipify.org && echo'
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# shellup — functions
|
|
2
|
+
# The handful of things that are genuinely awkward to type out every time.
|
|
3
|
+
|
|
4
|
+
[[ -o interactive ]] || return 0
|
|
5
|
+
|
|
6
|
+
# Make a directory and step into it.
|
|
7
|
+
mkcd() {
|
|
8
|
+
[[ -z $1 ]] && { print -u2 "usage: mkcd <dir>"; return 2 }
|
|
9
|
+
mkdir -p -- "$1" && builtin cd -- "$1"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
# One command for every archive format, instead of remembering nine flag sets.
|
|
13
|
+
extract() {
|
|
14
|
+
[[ -z $1 ]] && { print -u2 "usage: extract <archive>"; return 2 }
|
|
15
|
+
[[ -f $1 ]] || { print -u2 "extract: no such file: $1"; return 1 }
|
|
16
|
+
case $1 in
|
|
17
|
+
*.tar.bz2|*.tbz2) tar xjf -- "$1" ;;
|
|
18
|
+
*.tar.gz|*.tgz) tar xzf -- "$1" ;;
|
|
19
|
+
*.tar.xz) tar xJf -- "$1" ;;
|
|
20
|
+
*.tar.zst) tar --zstd -xf -- "$1" ;;
|
|
21
|
+
*.tar) tar xf -- "$1" ;;
|
|
22
|
+
*.bz2) bunzip2 -- "$1" ;;
|
|
23
|
+
*.gz) gunzip -- "$1" ;;
|
|
24
|
+
*.zip) unzip -- "$1" ;;
|
|
25
|
+
*.7z) 7z x "$1" ;;
|
|
26
|
+
*.rar) unrar x "$1" ;;
|
|
27
|
+
*.Z) uncompress -- "$1" ;;
|
|
28
|
+
*) print -u2 "extract: don't know how to open $1"; return 1 ;;
|
|
29
|
+
esac
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Serve the current directory over HTTP. `serve 3000` to pick a port.
|
|
33
|
+
serve() {
|
|
34
|
+
local port=${1:-8000}
|
|
35
|
+
print "serving $PWD on http://localhost:$port"
|
|
36
|
+
python3 -m http.server "$port"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# What is holding this port, and what do I kill?
|
|
40
|
+
onport() {
|
|
41
|
+
[[ -z $1 ]] && { print -u2 "usage: onport <port>"; return 2 }
|
|
42
|
+
lsof -nP -iTCP:"$1" -sTCP:LISTEN
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Timestamped copy, for when you're about to edit something risky.
|
|
46
|
+
bak() {
|
|
47
|
+
[[ -z $1 ]] && { print -u2 "usage: bak <file>"; return 2 }
|
|
48
|
+
cp -a -- "$1" "$1.$(date +%Y%m%d-%H%M%S).bak" && print "backed up $1"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Jump to the root of the current git repo.
|
|
52
|
+
cdr() {
|
|
53
|
+
local root
|
|
54
|
+
root=$(git rev-parse --show-toplevel 2>/dev/null) || { print -u2 "cdr: not in a git repo"; return 1 }
|
|
55
|
+
builtin cd -- "$root"
|
|
56
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# shellup — keybindings
|
|
2
|
+
# Emacs bindings plus the two things people miss most: prefix-aware history
|
|
3
|
+
# search on the arrow keys, and word-wise movement that works over ssh.
|
|
4
|
+
|
|
5
|
+
[[ -o interactive ]] || return 0
|
|
6
|
+
|
|
7
|
+
bindkey -e
|
|
8
|
+
|
|
9
|
+
# Type a few characters, press Up: cycles only through matching history.
|
|
10
|
+
autoload -U up-line-or-beginning-search down-line-or-beginning-search
|
|
11
|
+
zle -N up-line-or-beginning-search
|
|
12
|
+
zle -N down-line-or-beginning-search
|
|
13
|
+
bindkey '^[[A' up-line-or-beginning-search # Up
|
|
14
|
+
bindkey '^[[B' down-line-or-beginning-search # Down
|
|
15
|
+
bindkey '^[OA' up-line-or-beginning-search # Up (application mode)
|
|
16
|
+
bindkey '^[OB' down-line-or-beginning-search # Down (application mode)
|
|
17
|
+
|
|
18
|
+
bindkey '^[[1;5C' forward-word # Ctrl-Right
|
|
19
|
+
bindkey '^[[1;5D' backward-word # Ctrl-Left
|
|
20
|
+
bindkey '^[[1;3C' forward-word # Alt-Right
|
|
21
|
+
bindkey '^[[1;3D' backward-word # Alt-Left
|
|
22
|
+
bindkey '^[f' forward-word
|
|
23
|
+
bindkey '^[b' backward-word
|
|
24
|
+
|
|
25
|
+
bindkey '^[[H' beginning-of-line # Home
|
|
26
|
+
bindkey '^[[F' end-of-line # End
|
|
27
|
+
bindkey '^[[3~' delete-char # Delete
|
|
28
|
+
bindkey '^[[3;5~' kill-word # Ctrl-Delete
|
|
29
|
+
|
|
30
|
+
bindkey '^U' backward-kill-line # match bash: kill to start, not whole line
|
|
31
|
+
bindkey '^[^?' backward-kill-word # Alt-Backspace
|
|
32
|
+
|
|
33
|
+
# Treat path separators as word boundaries so Ctrl-W deletes one path segment.
|
|
34
|
+
WORDCHARS='*?_-.[]~&;!#$%^(){}<>'
|
|
35
|
+
|
|
36
|
+
# Ctrl-Z again to foreground the job you just suspended.
|
|
37
|
+
_shellup_fg() {
|
|
38
|
+
if [[ $#BUFFER -eq 0 ]]; then
|
|
39
|
+
fg >/dev/null 2>&1 && zle redisplay
|
|
40
|
+
else
|
|
41
|
+
zle push-input
|
|
42
|
+
fi
|
|
43
|
+
}
|
|
44
|
+
zle -N _shellup_fg
|
|
45
|
+
bindkey '^Z' _shellup_fg
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# shellup — zsh defaults
|
|
2
|
+
# Stock zsh forgets history across panes, re-asks for completions it already
|
|
3
|
+
# knows, and makes you type `cd`. These are the settings most people end up
|
|
4
|
+
# copying from a blog post eventually.
|
|
5
|
+
|
|
6
|
+
[[ -o interactive ]] || return 0
|
|
7
|
+
|
|
8
|
+
# ── History ────────────────────────────────────────────────────────────────
|
|
9
|
+
HISTFILE="${HISTFILE:-$HOME/.zsh_history}"
|
|
10
|
+
HISTSIZE=100000
|
|
11
|
+
SAVEHIST=100000
|
|
12
|
+
setopt append_history # never truncate another shell's writes
|
|
13
|
+
setopt inc_append_history # write as you go, not just on exit
|
|
14
|
+
setopt share_history # new panes see what you just ran
|
|
15
|
+
setopt hist_ignore_dups
|
|
16
|
+
setopt hist_ignore_all_dups
|
|
17
|
+
setopt hist_ignore_space # a leading space keeps it out of history
|
|
18
|
+
setopt hist_reduce_blanks
|
|
19
|
+
setopt hist_verify # expand !! for review instead of running it
|
|
20
|
+
setopt extended_history
|
|
21
|
+
|
|
22
|
+
# ── Navigation ─────────────────────────────────────────────────────────────
|
|
23
|
+
setopt auto_cd # `../..` instead of `cd ../..`
|
|
24
|
+
setopt auto_pushd # every cd builds a stack; `cd -<TAB>` to browse
|
|
25
|
+
setopt pushd_ignore_dups
|
|
26
|
+
setopt pushd_silent
|
|
27
|
+
DIRSTACKSIZE=20
|
|
28
|
+
|
|
29
|
+
# ── Globbing & misc ────────────────────────────────────────────────────────
|
|
30
|
+
setopt extended_glob
|
|
31
|
+
setopt no_case_glob
|
|
32
|
+
setopt interactive_comments # `# note` is legal at the prompt
|
|
33
|
+
setopt no_beep
|
|
34
|
+
setopt no_flow_control # frees Ctrl-S / Ctrl-Q for real bindings
|
|
35
|
+
unsetopt correct_all # autocorrect guesses wrong more than it helps
|
|
36
|
+
|
|
37
|
+
# ── Completion ─────────────────────────────────────────────────────────────
|
|
38
|
+
# compinit rebuilds its cache on every launch unless told otherwise; checking
|
|
39
|
+
# the dump's age once a day keeps startup fast without going stale.
|
|
40
|
+
autoload -Uz compinit
|
|
41
|
+
() {
|
|
42
|
+
local dump="${ZDOTDIR:-$HOME}/.zcompdump"
|
|
43
|
+
if [[ -n ${dump}(#qN.mh+24) ]]; then
|
|
44
|
+
compinit -d "$dump"
|
|
45
|
+
else
|
|
46
|
+
compinit -C -d "$dump"
|
|
47
|
+
fi
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
zstyle ':completion:*' menu select
|
|
51
|
+
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*'
|
|
52
|
+
zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}"
|
|
53
|
+
zstyle ':completion:*' group-name ''
|
|
54
|
+
zstyle ':completion:*:descriptions' format '%F{242}%d%f'
|
|
55
|
+
zstyle ':completion:*:warnings' format '%F{red}no matches%f'
|
|
56
|
+
zstyle ':completion:*' use-cache on
|
|
57
|
+
zstyle ':completion:*' cache-path "${XDG_CACHE_HOME:-$HOME/.cache}/zsh/compcache"
|
|
58
|
+
zstyle ':completion:*' special-dirs true
|
|
59
|
+
zstyle ':completion:*:*:kill:*:processes' list-colors '=(#b) #([0-9]#)*=0=01;31'
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# shellup — prompt engine
|
|
2
|
+
#
|
|
3
|
+
# The expensive part of a git prompt is `git status` in a large repo. Doing that
|
|
4
|
+
# synchronously is what makes themed prompts feel sluggish, so here the only
|
|
5
|
+
# blocking work per prompt is a single `git rev-parse`; counts, divergence and
|
|
6
|
+
# stash state are computed in a background process that repaints the prompt when
|
|
7
|
+
# it lands. Themes never touch any of this — they read the SHELLUP_* variables
|
|
8
|
+
# and define shellup_theme_render to format them.
|
|
9
|
+
|
|
10
|
+
[[ -o interactive ]] || return 0
|
|
11
|
+
|
|
12
|
+
autoload -Uz add-zsh-hook
|
|
13
|
+
setopt prompt_subst
|
|
14
|
+
|
|
15
|
+
# Overridden by whichever theme file loads next. Defined here so a missing or
|
|
16
|
+
# broken theme degrades to a plain prompt instead of erroring on every line.
|
|
17
|
+
shellup_theme_render() { SHELLUP_GIT_PROMPT=""; }
|
|
18
|
+
|
|
19
|
+
typeset -g SHELLUP_GIT_ROOT="" SHELLUP_GIT_BRANCH="" SHELLUP_GIT_ACTION=""
|
|
20
|
+
typeset -gi SHELLUP_GIT_STAGED=0 SHELLUP_GIT_UNSTAGED=0 SHELLUP_GIT_UNTRACKED=0
|
|
21
|
+
typeset -gi SHELLUP_GIT_CONFLICTED=0 SHELLUP_GIT_AHEAD=0 SHELLUP_GIT_BEHIND=0
|
|
22
|
+
typeset -gi SHELLUP_GIT_STASH=0 SHELLUP_GIT_LOADING=0
|
|
23
|
+
typeset -g SHELLUP_GIT_PROMPT="" SHELLUP_DURATION="" SHELLUP_VENV=""
|
|
24
|
+
typeset -gi _shellup_async_fd=0 _shellup_timer=0
|
|
25
|
+
|
|
26
|
+
: ${SHELLUP_SLOW_THRESHOLD:=3}
|
|
27
|
+
|
|
28
|
+
_shellup_git_reset() {
|
|
29
|
+
SHELLUP_GIT_ROOT=""; SHELLUP_GIT_BRANCH=""; SHELLUP_GIT_ACTION=""
|
|
30
|
+
SHELLUP_GIT_STAGED=0; SHELLUP_GIT_UNSTAGED=0; SHELLUP_GIT_UNTRACKED=0
|
|
31
|
+
SHELLUP_GIT_CONFLICTED=0; SHELLUP_GIT_AHEAD=0; SHELLUP_GIT_BEHIND=0
|
|
32
|
+
SHELLUP_GIT_STASH=0; SHELLUP_GIT_LOADING=0; SHELLUP_GIT_PROMPT=""
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Runs in a background process. Prints one space-separated line; "-" means empty,
|
|
36
|
+
# so every field stays at a fixed position and the reader needs no parsing rules.
|
|
37
|
+
_shellup_git_worker() {
|
|
38
|
+
builtin cd -q "$1" 2>/dev/null || return
|
|
39
|
+
local line xy branch="-" action="-" rest
|
|
40
|
+
local -i staged=0 unstaged=0 untracked=0 conflicted=0 ahead=0 behind=0 stash=0
|
|
41
|
+
|
|
42
|
+
while IFS= read -r line; do
|
|
43
|
+
case $line in
|
|
44
|
+
'# branch.head '*) branch=${line#'# branch.head '} ;;
|
|
45
|
+
'# branch.ab '*)
|
|
46
|
+
rest=${line#'# branch.ab '}
|
|
47
|
+
ahead=${${rest%% *}#+}
|
|
48
|
+
behind=${${rest##* }#-}
|
|
49
|
+
;;
|
|
50
|
+
'? '*) (( untracked += 1 )) ;;
|
|
51
|
+
'u '*) (( conflicted += 1 )) ;;
|
|
52
|
+
[12]' '*)
|
|
53
|
+
xy=${line[3,4]} # porcelain v2: X=index, Y=worktree
|
|
54
|
+
[[ ${xy[1]} != '.' ]] && (( staged += 1 ))
|
|
55
|
+
[[ ${xy[2]} != '.' ]] && (( unstaged += 1 ))
|
|
56
|
+
;;
|
|
57
|
+
esac
|
|
58
|
+
done < <(command git status --porcelain=v2 --branch --untracked-files=normal 2>/dev/null)
|
|
59
|
+
|
|
60
|
+
stash=$(command git rev-list --walk-reflogs --count refs/stash 2>/dev/null) || stash=0
|
|
61
|
+
[[ -z $stash ]] && stash=0
|
|
62
|
+
|
|
63
|
+
local gitdir
|
|
64
|
+
gitdir=$(command git rev-parse --git-dir 2>/dev/null)
|
|
65
|
+
if [[ -n $gitdir ]]; then
|
|
66
|
+
if [[ -d $gitdir/rebase-merge || -d $gitdir/rebase-apply ]]; then action="rebase"
|
|
67
|
+
elif [[ -f $gitdir/MERGE_HEAD ]]; then action="merge"
|
|
68
|
+
elif [[ -f $gitdir/CHERRY_PICK_HEAD ]]; then action="cherry-pick"
|
|
69
|
+
elif [[ -f $gitdir/REVERT_HEAD ]]; then action="revert"
|
|
70
|
+
elif [[ -f $gitdir/BISECT_LOG ]]; then action="bisect"
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
[[ $branch == '(detached)' ]] && branch="@$(command git rev-parse --short HEAD 2>/dev/null)"
|
|
75
|
+
print -r -- "$staged $unstaged $untracked $conflicted $ahead $behind $stash $action $branch"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
_shellup_async_stop() {
|
|
79
|
+
(( _shellup_async_fd )) || return 0
|
|
80
|
+
zle -F $_shellup_async_fd 2>/dev/null
|
|
81
|
+
exec {_shellup_async_fd}<&- 2>/dev/null
|
|
82
|
+
_shellup_async_fd=0
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Fired by zle when the worker has written its line (or hung up).
|
|
86
|
+
_shellup_async_callback() {
|
|
87
|
+
local fd=$1 data
|
|
88
|
+
IFS= read -r data <&$fd
|
|
89
|
+
zle -F $fd 2>/dev/null
|
|
90
|
+
exec {fd}<&- 2>/dev/null
|
|
91
|
+
_shellup_async_fd=0
|
|
92
|
+
SHELLUP_GIT_LOADING=0
|
|
93
|
+
|
|
94
|
+
if [[ -n $data ]]; then
|
|
95
|
+
local -a f=( ${=data} )
|
|
96
|
+
SHELLUP_GIT_STAGED=$f[1]; SHELLUP_GIT_UNSTAGED=$f[2]
|
|
97
|
+
SHELLUP_GIT_UNTRACKED=$f[3]; SHELLUP_GIT_CONFLICTED=$f[4]
|
|
98
|
+
SHELLUP_GIT_AHEAD=$f[5]; SHELLUP_GIT_BEHIND=$f[6]
|
|
99
|
+
SHELLUP_GIT_STASH=$f[7]
|
|
100
|
+
[[ $f[8] != '-' ]] && SHELLUP_GIT_ACTION=$f[8] || SHELLUP_GIT_ACTION=""
|
|
101
|
+
[[ $f[9] != '-' ]] && SHELLUP_GIT_BRANCH=$f[9]
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
shellup_theme_render
|
|
105
|
+
zle && zle reset-prompt
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
_shellup_preexec() {
|
|
109
|
+
_shellup_timer=$EPOCHSECONDS
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
_shellup_precmd() {
|
|
113
|
+
# Command duration, surfaced only when it was long enough to care about.
|
|
114
|
+
SHELLUP_DURATION=""
|
|
115
|
+
if (( _shellup_timer )); then
|
|
116
|
+
local -i elapsed=$(( EPOCHSECONDS - _shellup_timer ))
|
|
117
|
+
if (( elapsed >= SHELLUP_SLOW_THRESHOLD )); then
|
|
118
|
+
if (( elapsed < 60 )); then SHELLUP_DURATION="${elapsed}s"
|
|
119
|
+
elif (( elapsed < 3600 )); then SHELLUP_DURATION="$(( elapsed / 60 ))m$(( elapsed % 60 ))s"
|
|
120
|
+
else SHELLUP_DURATION="$(( elapsed / 3600 ))h$(( (elapsed % 3600) / 60 ))m"
|
|
121
|
+
fi
|
|
122
|
+
fi
|
|
123
|
+
_shellup_timer=0
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
SHELLUP_VENV=""
|
|
127
|
+
[[ -n $VIRTUAL_ENV ]] && SHELLUP_VENV=${VIRTUAL_ENV:t}
|
|
128
|
+
|
|
129
|
+
_shellup_async_stop
|
|
130
|
+
|
|
131
|
+
# The one synchronous git call: cheap even in a huge repo.
|
|
132
|
+
local -a info
|
|
133
|
+
info=( ${(f)"$(command git rev-parse --show-toplevel --abbrev-ref HEAD 2>/dev/null)"} )
|
|
134
|
+
if (( ${#info} < 2 )); then
|
|
135
|
+
_shellup_git_reset
|
|
136
|
+
shellup_theme_render
|
|
137
|
+
return
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
local root=$info[1] branch=$info[2]
|
|
141
|
+
# rev-parse still prints the toplevel on an unborn branch but resolves HEAD to
|
|
142
|
+
# the literal string "HEAD"; a detached checkout does the same. Git forbids a
|
|
143
|
+
# branch actually named HEAD, so this is an unambiguous signal to look closer.
|
|
144
|
+
if [[ $branch == HEAD ]]; then
|
|
145
|
+
branch=$(command git symbolic-ref --short HEAD 2>/dev/null) \
|
|
146
|
+
|| branch="@$(command git rev-parse --short HEAD 2>/dev/null)"
|
|
147
|
+
fi
|
|
148
|
+
# Leaving a repo must clear counts, or the old repo's state bleeds into the new prompt.
|
|
149
|
+
[[ $root != $SHELLUP_GIT_ROOT ]] && _shellup_git_reset
|
|
150
|
+
SHELLUP_GIT_ROOT=$root
|
|
151
|
+
SHELLUP_GIT_BRANCH=$branch
|
|
152
|
+
SHELLUP_GIT_LOADING=1
|
|
153
|
+
shellup_theme_render
|
|
154
|
+
|
|
155
|
+
exec {_shellup_async_fd}< <( _shellup_git_worker "$root" )
|
|
156
|
+
zle -F $_shellup_async_fd _shellup_async_callback
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
zmodload zsh/datetime 2>/dev/null
|
|
160
|
+
add-zsh-hook preexec _shellup_preexec
|
|
161
|
+
add-zsh-hook precmd _shellup_precmd
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# shellup theme: minimal
|
|
2
|
+
# Deliberately glyph-free — every character here exists in any monospace font,
|
|
3
|
+
# so this is the theme that is always safe to fall back to.
|
|
4
|
+
|
|
5
|
+
typeset -g SHELLUP_CTX_PROMPT=""
|
|
6
|
+
|
|
7
|
+
shellup_theme_render() {
|
|
8
|
+
local s=""
|
|
9
|
+
if [[ -n $SHELLUP_GIT_BRANCH ]]; then
|
|
10
|
+
s=" %F{242}on%f %F{magenta}${SHELLUP_GIT_BRANCH}%f"
|
|
11
|
+
(( SHELLUP_GIT_STAGED )) && s+=" %F{green}+${SHELLUP_GIT_STAGED}%f"
|
|
12
|
+
(( SHELLUP_GIT_UNSTAGED )) && s+=" %F{yellow}!${SHELLUP_GIT_UNSTAGED}%f"
|
|
13
|
+
(( SHELLUP_GIT_UNTRACKED )) && s+=" %F{blue}?${SHELLUP_GIT_UNTRACKED}%f"
|
|
14
|
+
(( SHELLUP_GIT_CONFLICTED )) && s+=" %F{red}x${SHELLUP_GIT_CONFLICTED}%f"
|
|
15
|
+
(( SHELLUP_GIT_STASH )) && s+=" %F{cyan}*${SHELLUP_GIT_STASH}%f"
|
|
16
|
+
(( SHELLUP_GIT_AHEAD )) && s+=" %F{cyan}^${SHELLUP_GIT_AHEAD}%f"
|
|
17
|
+
(( SHELLUP_GIT_BEHIND )) && s+=" %F{cyan}v${SHELLUP_GIT_BEHIND}%f"
|
|
18
|
+
[[ -n $SHELLUP_GIT_ACTION ]] && s+=" %F{red}(${SHELLUP_GIT_ACTION})%f"
|
|
19
|
+
fi
|
|
20
|
+
SHELLUP_GIT_PROMPT=$s
|
|
21
|
+
|
|
22
|
+
local ctx=""
|
|
23
|
+
[[ -n $SHELLUP_VENV ]] && ctx=" %F{yellow}(${SHELLUP_VENV})%f"
|
|
24
|
+
SHELLUP_CTX_PROMPT=$ctx
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
PROMPT='%F{39}%(4~|…/%3~|%~)%f${SHELLUP_GIT_PROMPT}${SHELLUP_CTX_PROMPT}
|
|
28
|
+
%(?.%F{green}.%F{red})>%f '
|
|
29
|
+
RPROMPT='%F{242}${SHELLUP_DURATION}%f%(?.. %F{red}[%?]%f)'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# shellup theme: neon
|
|
2
|
+
# Bright two-line prompt drawn with box characters and common Unicode arrows —
|
|
3
|
+
# no patched font required, so it works next to minimal on a bare system.
|
|
4
|
+
|
|
5
|
+
typeset -g SHELLUP_CTX_PROMPT=""
|
|
6
|
+
|
|
7
|
+
shellup_theme_render() {
|
|
8
|
+
local s=""
|
|
9
|
+
if [[ -n $SHELLUP_GIT_BRANCH ]]; then
|
|
10
|
+
s=" %F{240}·%f %F{213}⌥ ${SHELLUP_GIT_BRANCH}%f"
|
|
11
|
+
(( SHELLUP_GIT_STAGED )) && s+=" %F{82}●${SHELLUP_GIT_STAGED}%f"
|
|
12
|
+
(( SHELLUP_GIT_UNSTAGED )) && s+=" %F{220}✚${SHELLUP_GIT_UNSTAGED}%f"
|
|
13
|
+
(( SHELLUP_GIT_UNTRACKED )) && s+=" %F{45}◌${SHELLUP_GIT_UNTRACKED}%f"
|
|
14
|
+
(( SHELLUP_GIT_CONFLICTED )) && s+=" %F{196}✖${SHELLUP_GIT_CONFLICTED}%f"
|
|
15
|
+
(( SHELLUP_GIT_STASH )) && s+=" %F{141}⚑${SHELLUP_GIT_STASH}%f"
|
|
16
|
+
(( SHELLUP_GIT_AHEAD )) && s+=" %F{51}⇡${SHELLUP_GIT_AHEAD}%f"
|
|
17
|
+
(( SHELLUP_GIT_BEHIND )) && s+=" %F{51}⇣${SHELLUP_GIT_BEHIND}%f"
|
|
18
|
+
[[ -n $SHELLUP_GIT_ACTION ]] && s+=" %F{196}⚡${SHELLUP_GIT_ACTION}%f"
|
|
19
|
+
fi
|
|
20
|
+
SHELLUP_GIT_PROMPT=$s
|
|
21
|
+
|
|
22
|
+
local ctx=""
|
|
23
|
+
[[ -n $SHELLUP_VENV ]] && ctx=" %F{240}·%f %F{184}🐍${SHELLUP_VENV}%f"
|
|
24
|
+
SHELLUP_CTX_PROMPT=$ctx
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
PROMPT='%F{240}╭─%f %F{51}%(4~|…/%3~|%~)%f${SHELLUP_GIT_PROMPT}${SHELLUP_CTX_PROMPT}
|
|
28
|
+
%F{240}╰─%f%(?.%F{213}.%F{196})❯%f '
|
|
29
|
+
RPROMPT='%F{240}${SHELLUP_DURATION}%f%(?.. %F{196}✖ %?%f)'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# shellup theme: powerline
|
|
2
|
+
# Requires a Nerd Font — the separators and icons below are private-use glyphs
|
|
3
|
+
# and render as tofu boxes without one. `shellup doctor` checks for this.
|
|
4
|
+
|
|
5
|
+
typeset -g SHELLUP_CTX_PROMPT=""
|
|
6
|
+
|
|
7
|
+
shellup_theme_render() {
|
|
8
|
+
local s=""
|
|
9
|
+
if [[ -n $SHELLUP_GIT_BRANCH ]]; then
|
|
10
|
+
local body=" ${SHELLUP_GIT_BRANCH}"
|
|
11
|
+
(( SHELLUP_GIT_STAGED )) && body+=" +${SHELLUP_GIT_STAGED}"
|
|
12
|
+
(( SHELLUP_GIT_UNSTAGED )) && body+=" !${SHELLUP_GIT_UNSTAGED}"
|
|
13
|
+
(( SHELLUP_GIT_UNTRACKED )) && body+=" ?${SHELLUP_GIT_UNTRACKED}"
|
|
14
|
+
(( SHELLUP_GIT_CONFLICTED )) && body+=" ${SHELLUP_GIT_CONFLICTED}"
|
|
15
|
+
(( SHELLUP_GIT_STASH )) && body+=" ${SHELLUP_GIT_STASH}"
|
|
16
|
+
(( SHELLUP_GIT_AHEAD )) && body+=" ⇡${SHELLUP_GIT_AHEAD}"
|
|
17
|
+
(( SHELLUP_GIT_BEHIND )) && body+=" ⇣${SHELLUP_GIT_BEHIND}"
|
|
18
|
+
[[ -n $SHELLUP_GIT_ACTION ]] && body+=" ${SHELLUP_GIT_ACTION}"
|
|
19
|
+
|
|
20
|
+
# Dirty tree turns the segment amber; clean stays green.
|
|
21
|
+
local bg=71
|
|
22
|
+
(( SHELLUP_GIT_UNSTAGED + SHELLUP_GIT_UNTRACKED + SHELLUP_GIT_CONFLICTED )) && bg=178
|
|
23
|
+
s="%K{$bg}%F{24}%f%F{233}${body} %f%k%F{$bg}%f"
|
|
24
|
+
else
|
|
25
|
+
s="%F{24}%f"
|
|
26
|
+
fi
|
|
27
|
+
SHELLUP_GIT_PROMPT=$s
|
|
28
|
+
|
|
29
|
+
local ctx=""
|
|
30
|
+
[[ -n $SHELLUP_VENV ]] && ctx=" %F{184} ${SHELLUP_VENV}%f"
|
|
31
|
+
SHELLUP_CTX_PROMPT=$ctx
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
PROMPT='%K{24}%F{233} %(4~|…/%3~|%~) %f%k${SHELLUP_GIT_PROMPT}${SHELLUP_CTX_PROMPT}
|
|
35
|
+
%(?.%F{71}.%F{160})❯%f '
|
|
36
|
+
RPROMPT='%F{240}${SHELLUP_DURATION}%f%(?.. %F{160} %?%f)'
|