feat: re-organise files dir

This commit is contained in:
2026-02-13 14:59:25 +00:00
parent 6461654bff
commit faf3bf2dfc
13 changed files with 3 additions and 5 deletions

281
home/.zsh/prompt.zsh Normal file
View File

@@ -0,0 +1,281 @@
# Prompt
(( ${+PROMPT_MIN_DURATION} )) || typeset -gi PROMPT_MIN_DURATION=2 # show duration after N seconds
(( ${+PROMPT_FLASH_DELAY} )) || typeset -gi PROMPT_FLASH_DELAY=4 # flash prompt for N centiseconds
typeset -gi _dots_prompt_cmd_start=0
typeset -gi _dots_prompt_cmd_ran=0
typeset -gi _dots_prompt_flashing=0
typeset -g _dots_prompt_symbol="λ"
typeset -g _dots_prompt_base=""
typeset -gA _dots_pc
_dots_init_colors() {
if [[ "$COLORTERM" == (truecolor|24bit) ]]; then
_dots_pc=(
teal $'%{\e[38;2;44;180;148m%}'
orange $'%{\e[38;2;248;140;20m%}'
red $'%{\e[38;2;244;4;4m%}'
grey $'%{\e[38;2;114;144;184m%}'
)
elif [[ "$TERM" == *256color* ]]; then
_dots_pc=(
teal $'%{\e[38;5;43m%}'
orange $'%{\e[38;5;208m%}'
red $'%{\e[38;5;196m%}'
grey $'%{\e[38;5;103m%}'
)
else
_dots_pc=(
teal $'%{\e[36m%}'
orange $'%{\e[33m%}'
red $'%{\e[31m%}'
grey $'%{\e[34m%}'
)
fi
_dots_pc[reset]=$'%{\e[0m%}'
_dots_pc[bold]=$'%{\e[1m%}'
}
_dots_abbrev_path() {
local dir="${PWD/#$HOME/~}"
local -a parts=( "${(@s:/:)dir}" )
local count=${#parts[@]}
(( count <= 3 )) && { print -r -- "$dir"; return }
local result=""
local i
for (( i=1; i <= count-3; i++ )); do
result+="${parts[i][1]}/"
done
print -r -- "${result}${parts[-3]}/${parts[-2]}/${parts[-1]}"
}
_dots_session() {
[[ -n "$CODESPACE_NAME" ]] && { print -r -- "$CODESPACE_NAME"; return }
[[ -n "$SSH_CONNECTION" || -n "$SSH_CLIENT" || -n "$SSH_TTY" ]] && { print -r -- "%n@%m"; return }
[[ -f /.dockerenv ]] && { print -r -- "${DEVCONTAINER_ID:-$(</etc/hostname)}"; return }
}
_dots_git_info_sync() {
# Use git status --short with awk for better performance
# Avoids shell loops and uses compiled awk for parsing
local output
output=$(git status --short --branch --ignore-submodules=dirty 2>/dev/null) || return
local branch="" ahead=0 behind=0 staged=0 unstaged=0 untracked=0
# Parse efficiently: branch info from first line, counts from rest
local first_line="${output%%$'\n'*}"
# Extract branch from "## branch...remote [ahead N, behind M]"
if [[ "$first_line" == "## "* ]]; then
branch="${first_line#\#\# }"
# Handle detached HEAD
if [[ "$branch" == "HEAD (no branch)" || "$branch" == [0-9a-f]##* ]]; then
branch="${branch:0:7}"
else
# Remove tracking info
branch="${branch%%...*}"
branch="${branch%% *}"
fi
# Extract ahead/behind
[[ "$first_line" =~ "ahead ([0-9]+)" ]] && ahead="${match[1]}"
[[ "$first_line" =~ "behind ([0-9]+)" ]] && behind="${match[1]}"
fi
[[ -z "$branch" ]] && return
# Count file status with awk (fast, no shell loops)
local -a counts
counts=( $(awk '
NR == 1 { next } # Skip branch line
/^\?\?/ { untracked++; next }
{
x = substr($0, 1, 1)
y = substr($0, 2, 1)
if (x != " " && x != "?") staged++
if (y != " " && y != "?") unstaged++
}
END { print staged+0, unstaged+0, untracked+0 }
' <<< "$output") )
staged=${counts[1]:-0}
unstaged=${counts[2]:-0}
untracked=${counts[3]:-0}
local info="${_dots_pc[grey]}(${branch})${_dots_pc[reset]}"
local dirty=""
local sep=""
if (( staged )); then
dirty+="${_dots_pc[teal]}+${staged}${_dots_pc[reset]}"
sep=" "
fi
if (( unstaged )); then
dirty+="${sep}${_dots_pc[orange]}~${unstaged}${_dots_pc[reset]}"
sep=" "
fi
if (( untracked )); then
dirty+="${sep}${_dots_pc[grey]}?${untracked}${_dots_pc[reset]}"
fi
local arrows=""
sep=""
if (( ahead )); then
arrows+="${_dots_pc[teal]}${ahead}${_dots_pc[reset]}"
sep=" "
fi
if (( behind )); then
arrows+="${sep}${_dots_pc[orange]}${behind}${_dots_pc[reset]}"
fi
if [[ -n "$dirty" || -n "$arrows" ]]; then
info+=" "
[[ -n "$dirty" ]] && info+="$dirty"
[[ -n "$dirty" && -n "$arrows" ]] && info+=" "
[[ -n "$arrows" ]] && info+="$arrows"
fi
print -r -- "$info"
}
# Async git info
typeset -g _dots_git_info_result=""
typeset -g _dots_git_info_pwd=""
typeset -g _dots_git_async_fd=""
_dots_git_async_callback() {
local fd=$1
local result=""
# Use sysread for efficient non-blocking read from fd
if [[ -n "$fd" ]] && sysread -i "$fd" result 2>/dev/null; then
result="${result%$'\n'}" # trim trailing newline
_dots_git_info_result="$result"
_dots_build_dots_prompt_base
PROMPT="$_dots_prompt_base"
zle && zle reset-prompt
fi
# Clean up
exec {fd}<&-
zle -F "$fd" 2>/dev/null
_dots_git_async_fd=""
}
_dots_git_async_start() {
# Check if we're in a git repo
local git_dir
git_dir=$(git rev-parse --git-dir 2>/dev/null) || return
# Cancel any pending async job (reuse single worker)
if [[ -n "$_dots_git_async_fd" ]]; then
exec {_dots_git_async_fd}<&- 2>/dev/null
zle -F "$_dots_git_async_fd" 2>/dev/null
_dots_git_async_fd=""
fi
# Start background job
exec {_dots_git_async_fd}< <(
_dots_git_info_sync
)
zle -F "$_dots_git_async_fd" _dots_git_async_callback
}
_dots_build_dots_prompt_base() {
local dir_path="$(_dots_abbrev_path)"
local symbol="${_dots_pc[grey]}${_dots_prompt_symbol}${_dots_pc[reset]}"
(( EUID == 0 )) && symbol="${_dots_pc[orange]}${_dots_pc[bold]}#${_dots_pc[reset]}"
local line1="${_dots_pc[teal]}${dir_path}${_dots_pc[reset]}"
[[ -n "$_dots_git_info_result" ]] && line1+=" ${_dots_git_info_result}"
_dots_prompt_base=$'\n'"${line1}"$'\n'"${symbol} "
}
_dots_preexec() {
_dots_prompt_cmd_start=$EPOCHSECONDS
_dots_prompt_cmd_ran=1
}
_dots_precmd() {
local e=$? d=0
# Only show exit code if a command actually ran
(( _dots_prompt_cmd_ran )) || e=0
_dots_prompt_cmd_ran=0
# First prompt should never show error from shell init
[[ -z "$_dots_first_prompt" ]] && { _dots_first_prompt=1; e=0; }
# Build RPROMPT: time, error, host
local rp_parts=()
if (( _dots_prompt_cmd_start )); then
d=$(( EPOCHSECONDS - _dots_prompt_cmd_start ))
_dots_prompt_cmd_start=0
if (( d >= PROMPT_MIN_DURATION )); then
(( d >= 60 )) && rp_parts+=("${_dots_pc[grey]}($(( d/60 ))m$(( d%60 ))s)${_dots_pc[reset]}") \
|| rp_parts+=("${_dots_pc[grey]}(${d}s)${_dots_pc[reset]}")
fi
fi
(( e )) && rp_parts+=("${_dots_pc[red]}[${e}]${_dots_pc[reset]}")
local session="$(_dots_session)"
[[ -n "$session" ]] && rp_parts+=("${_dots_pc[orange]}[${session}]${_dots_pc[reset]}")
RPROMPT="${(j: :)rp_parts}"
# On directory change, clear git info until async refreshes
if [[ "$PWD" != "$_dots_git_info_pwd" ]]; then
_dots_git_info_pwd="$PWD"
_dots_git_info_result=""
fi
_dots_build_dots_prompt_base
_dots_git_async_start
PROMPT="$_dots_prompt_base"
}
TRAPINT() {
# Only customize when ZLE is active (at prompt, not during command)
if [[ -o zle ]] && [[ -o interactive ]] && (( ${+WIDGET} )); then
if [[ -z "$BUFFER" ]] && (( ! _dots_prompt_flashing )); then
# Empty buffer: flash the prompt symbol
_dots_prompt_flashing=1
local git_part=""
[[ -n "$_dots_git_info_result" ]] && git_part=" ${_dots_git_info_result}"
local flash_prompt=$'\n'"${_dots_pc[teal]}$(_dots_abbrev_path)${_dots_pc[reset]}${git_part}"$'\n'$'%{\e[48;2;248;140;20m\e[30m%}'"${_dots_prompt_symbol}"$' %{\e[0m%}'
PROMPT="$flash_prompt"
zle reset-prompt
zselect -t $PROMPT_FLASH_DELAY
_dots_prompt_flashing=0
PROMPT="$_dots_prompt_base"
zle reset-prompt
return 0
elif [[ -n "$BUFFER" ]]; then
# Buffer has content: clear autosuggest, then default behavior
zle autosuggest-clear 2>/dev/null
fi
fi
# Propagate signal: use special return code -1 to let zsh handle normally
return $((128 + ${1:-2}))
}
_dots_prompt_init() {
zmodload zsh/datetime 2>/dev/null
zmodload zsh/zselect 2>/dev/null
zmodload zsh/system 2>/dev/null
_dots_init_colors
_dots_build_dots_prompt_base
setopt PROMPT_SUBST EXTENDED_HISTORY INC_APPEND_HISTORY_TIME
autoload -Uz add-zsh-hook
add-zsh-hook preexec _dots_preexec
add-zsh-hook precmd _dots_precmd
add-zsh-hook chpwd _dots_build_dots_prompt_base
PROMPT="$_dots_prompt_base" RPROMPT=""
}
_dots_prompt_init

295
home/.zsh/widgets.zsh Normal file
View File

@@ -0,0 +1,295 @@
_dots_load_keybindings() {
bindkey -e
stty -ixon 2>/dev/null
# Ctrl+J: zoxide jump
_dots_zoxide_widget() {
local result
result="$(zoxide query -i -- 2>&1)" || { zle reset-prompt; return; }
BUFFER="cd ${(q)result}"
zle reset-prompt
zle accept-line
}
zle -N _dots_zoxide_widget
bindkey '^J' _dots_zoxide_widget
# Ctrl+B: git branch checkout
_dots_git_branch_widget() {
local branch
branch="$(git branch --all --sort=-committerdate --format='%(refname:short)' 2>/dev/null \
| fzf --preview 'git log --oneline --color -20 {}')" || { zle reset-prompt; return; }
branch="${branch#origin/}"
BUFFER="git checkout ${(q)branch}"
zle reset-prompt
zle accept-line
}
zle -N _dots_git_branch_widget
bindkey '^B' _dots_git_branch_widget
# Ctrl+E: edit file
_dots_edit_widget() {
local file
file="$(rg --files --hidden --glob '!.git' 2>/dev/null \
| fzf --preview 'head -100 {}')" || { zle reset-prompt; return; }
BUFFER="${EDITOR:-vim} ${(q)file}"
zle reset-prompt
zle accept-line
}
zle -N _dots_edit_widget
bindkey '^E' _dots_edit_widget
# Ctrl+G: remote connect
if [[ -z "${CODESPACES:-}" ]]; then
_dots_ssh_hosts() {
local ssh_log="${XDG_DATA_HOME:-$HOME/.local/share}/ssh/log"
local cs_cache="$_dots_cache_dir/codespaces"
# Refresh codespace cache in background if stale (>5 min)
if [[ ! -f "$cs_cache" ]] || [[ -n "$(find "$cs_cache" -mmin +5 2>/dev/null)" ]]; then
{ gh cs list --json name -q '.[].name' 2>/dev/null | sed 's/^/cs:/' > "$cs_cache.tmp" && mv "$cs_cache.tmp" "$cs_cache"; } &!
fi
{
if [[ -f "$ssh_log" ]]; then
awk '{c[$2]++; t[$2]=$1} END {for(h in c) print c[h]*1000+t[h], h}' "$ssh_log" | sort -rn | awk '{print $2}'
fi
awk '/^Host / && !/\*/ {print $2}' ~/.ssh/config ~/.ssh/config.d/* 2>/dev/null
awk '{print $1}' ~/.ssh/known_hosts 2>/dev/null | tr ',' '\n' | sed 's/\[//;s/\]:.*//'
[[ -f "$cs_cache" ]] && cat "$cs_cache"
} | awk '!seen[$0]++'
}
_dots_ssh_widget() {
local target
target="$(_dots_ssh_hosts | fzf)" || { zle reset-prompt; return; }
if [[ "$target" == cs:* ]]; then
BUFFER="cs ${target#cs:}"
else
BUFFER="ssh $target"
fi
zle reset-prompt
zle accept-line
}
zle -N _dots_ssh_widget
bindkey '^G' _dots_ssh_widget
else
bindkey -r '^G'
fi
# Ctrl+F: find in files
_dots_find_in_files_widget() {
local selection
selection="$(rg --color=always --line-number --no-heading --hidden --glob '!.git' '' 2>/dev/null \
| fzf --ansi --delimiter=: \
--preview 'head -n $((({2}+30))) {1} | tail -n 60' \
--preview-window='right:60%')" || { zle -I; zle reset-prompt; return; }
local file="${selection%%:*}"
local line="${${selection#*:}%%:*}"
BUFFER="${EDITOR:-vim} +${line} ${(q)file}"
zle reset-prompt
zle accept-line
}
zle -N _dots_find_in_files_widget
bindkey '^F' _dots_find_in_files_widget
# Ctrl+A: git log browser
_dots_git_log_widget() {
local commit
commit="$(git log --oneline --color --decorate -50 2>/dev/null \
| fzf --ansi --no-sort \
--preview 'git show --color=always {1}' \
--preview-window='right:60%')" || { zle reset-prompt; return; }
BUFFER="git show ${commit%% *}"
zle reset-prompt
zle accept-line
}
zle -N _dots_git_log_widget
bindkey '^A' _dots_git_log_widget
# Ctrl+K: command help lookup
_dots_help_widget() {
local cmd
cmd="$(print -l ${(ko)commands} | fzf --preview 'tldr {1} 2>/dev/null || man -P cat {1} 2>/dev/null | head -80')" \
|| { zle reset-prompt; return; }
if command -v tldr &>/dev/null; then
BUFFER="tldr ${(q)cmd}"
else
BUFFER="man ${(q)cmd}"
fi
zle reset-prompt
zle accept-line
}
zle -N _dots_help_widget
bindkey '^K' _dots_help_widget
# Ctrl+N: tmux session
_dots_tmux_widget() {
[[ -n "${CODESPACES:-}" ]] && { zle reset-prompt; return; }
local sessions
sessions="$(tmux list-sessions -F '#{session_name}' 2>/dev/null)"
if [[ -z "$sessions" ]]; then
BUFFER="tmux new-session"
elif [[ -n "$TMUX" ]]; then
local session
session="$({ echo '+ new session'; echo "$sessions"; } \
| fzf --preview 'case {} in "+ new session") echo "Create a new tmux session";; *) tmux list-windows -t {} -F " #{window_index}: #{window_name} #{pane_current_command}";; esac')" \
|| { zle reset-prompt; return; }
if [[ "$session" == "+ new session" ]]; then
BUFFER="tmux new-session -d && tmux switch-client -n"
else
BUFFER="tmux switch-client -t ${(q)session}"
fi
else
local session
session="$(echo "$sessions" \
| fzf --preview 'tmux list-windows -t {} -F " #{window_index}: #{window_name} #{pane_current_command}"')" \
|| { zle reset-prompt; return; }
BUFFER="tmux attach -t ${(q)session}"
fi
zle reset-prompt
zle accept-line
}
zle -N _dots_tmux_widget
bindkey '^N' _dots_tmux_widget
# Ctrl+O: open in browser
_dots_open_widget() {
local choice
choice="$(printf 'repo\npr\nissues\nactions' | fzf)" || { zle reset-prompt; return; }
case "$choice" in
repo) BUFFER="gh browse" ;;
pr) BUFFER="gh pr view --web" ;;
issues) BUFFER="gh browse --issues" ;;
actions) BUFFER="gh browse --actions" ;;
esac
zle reset-prompt
zle accept-line
}
zle -N _dots_open_widget
bindkey '^O' _dots_open_widget
# Ctrl+P: project switch
_dots_project_widget() {
local result
result="$(zoxide query -l 2>/dev/null | grep "${WORKSPACE:-$HOME/Workspace}" \
| fzf --preview 'ls -1 {}')" || { zle reset-prompt; return; }
BUFFER="cd ${(q)result}"
zle reset-prompt
zle accept-line
}
zle -N _dots_project_widget
bindkey '^P' _dots_project_widget
# Ctrl+S: copilot sessions
_dots_copilot_session_widget() {
local session_dir="$HOME/.copilot/session-state"
[[ -d "$session_dir" ]] || { zle reset-prompt; return; }
local session
session="$(python3 -c "
import os, json, glob
sd = os.path.expanduser('~/.copilot/session-state')
home = os.path.expanduser('~')
entries = []
for ws in glob.glob(os.path.join(sd, '*/workspace.yaml')):
try:
d = {}
with open(ws) as f:
for l in f:
for k in ('updated_at:','cwd:','id:','summary:','repository:'):
if l.startswith(k): d[k[:-1]] = l.split(': ',1)[1].strip()
sid = d.get('id','')
if not sid: continue
ts = d.get('updated_at','?')[:16]
repo = d.get('repository','').split('/')[-1] if d.get('repository') else ''
summary = d.get('summary','')
msg = ''
ev = os.path.join(os.path.dirname(ws), 'events.jsonl')
if os.path.exists(ev):
with open(ev) as f:
for l in f:
if '\"user.message\"' in l:
try: msg = json.loads(l)['data']['content'].strip().split(chr(10))[0][:60]
except: pass
break
if not msg: continue
ctx = repo or d.get('cwd','?').replace(home,'~')
label = f'{ctx} \u00b7 {summary}' if summary else f'{ctx} \u00b7 {msg}'
entries.append((ts, sid, label))
except: pass
for jf in glob.glob(os.path.join(sd, '*.jsonl')):
try:
sid = os.path.basename(jf).replace('.jsonl','')
with open(jf) as f:
ts = json.loads(f.readline())['data']['startTime'][:16]
msg = ''
with open(jf) as f:
for l in f:
if '\"user.message\"' in l:
try: msg = json.loads(l)['data']['content'].strip().split(chr(10))[0][:60]
except: pass
break
if not msg: continue
entries.append((ts, sid, msg))
except: pass
entries.sort(key=lambda x: x[0], reverse=True)
for ts, sid, label in entries:
print(f'{ts} | {sid} | {label}')
" 2>/dev/null | fzf --preview '
id=$(echo {} | cut -d"|" -f2 | tr -d " ")
sd="'"$session_dir"'"
f="$sd/$id/events.jsonl"
[[ -f "$f" ]] || f="$sd/${id}.jsonl"
[[ -f "$f" ]] || exit 0
grep "\"user.message\"" "$f" | python3 -c "
import sys,json
for line in sys.stdin:
try:
msg=json.loads(line)[\"data\"][\"content\"].strip().split(chr(10))[0][:100]
print(\">\", msg)
except: pass
" 2>/dev/null
' --header 'enter=colby | ctrl-r=restricted' \
--expect=ctrl-r)" || { zle reset-prompt; return; }
local key=$(echo "$session" | head -1)
local line=$(echo "$session" | tail -1)
local id=$(echo "$line" | cut -d'|' -f2 | tr -d ' ')
if [[ "$key" == "ctrl-r" ]]; then
BUFFER="gh copilot --resume $id"
else
BUFFER="copilot --allow-all-tools --allow-all-paths --resume $id"
fi
zle reset-prompt
zle accept-line
}
zle -N _dots_copilot_session_widget
bindkey '^S' _dots_copilot_session_widget
# Ctrl+X: process manager
_dots_process_widget() {
local proc
proc="$(ps -eo pid,user,%cpu,%mem,command 2>/dev/null \
| fzf --header-lines=1 --preview 'ps -p {1} -o pid,ppid,stat,start,time,command 2>/dev/null' \
--preview-window='down:4:wrap')" || { zle reset-prompt; return; }
local pid="${${proc## #}%% *}"
BUFFER="kill $pid"
zle reset-prompt
zle accept-line
}
zle -N _dots_process_widget
bindkey '^X' _dots_process_widget
# Ctrl+Y: git stash browser
_dots_stash_widget() {
local stash
stash="$(git stash list --color=always 2>/dev/null \
| fzf --ansi --no-sort \
--preview 'git stash show -p --color=always $(echo {} | cut -d: -f1)' \
--preview-window='right:60%')" || { zle reset-prompt; return; }
local ref="${stash%%:*}"
BUFFER="git stash apply $ref"
zle reset-prompt
zle accept-line
}
zle -N _dots_stash_widget
bindkey '^Y' _dots_stash_widget
}
_dots_load_keybindings