[Update] bulk update

This commit is contained in:
2026-09-12 23:38:18 +02:00
parent 3ec2503f38
commit 675ce12b2e
162 changed files with 3348 additions and 2419 deletions
+3
View File
@@ -35,6 +35,9 @@ clients:
- name: Qwen3.5-9B-UD-Q6_K_XL
max_input_tokens: 30000 # ctx 32768
supports_vision: true
- name: Qwen3.8-27B-UD-IQ3_XXS
max_input_tokens: 22000 # ctx 24576
supports_vision: true
- name: gemma-4-26B-A4B-it-UD-IQ4_XS
max_input_tokens: 22000 # ctx 24576
supports_vision: true
+3 -3
View File
@@ -8,13 +8,13 @@ resize), trailing (lowest-priority) segments are dropped from the right until it
Wide — everything:
```
📁 ~/.dots │ 🌿 .dots main* │ 🤖 Opus 4.8 (1M) · xhigh │ 📝 84% (843k) │ 📊 34% 2h54m │ 💰 $0.40 $12.00/h │ 💾 81%
📁 ~/.dots │ 🌿 .dots main* │ 🤖 Fable 5 (1M) · xhigh │ 📝 84% (843k) │ 📊 34% 2h54m │ 💰 $0.40 $12.00/h │ 💾 81%
```
Narrow — trailing segments trimmed:
```
📁 ~/.dots │ 🌿 .dots main* │ 🤖 Opus 4.8 (1M) · xhigh │ 📝 84% (843k) │ 📊 34% 2h54m │ 💰 $0.40 $12.00/h
📁 ~/.dots │ 🌿 .dots main* │ 🤖 Fable 5 (1M) · xhigh │ 📝 84% (843k) │ 📊 34% 2h54m │ 💰 $0.40 $12.00/h
```
Every segment is defensive: if its data is missing or a command fails, the segment is
@@ -29,7 +29,7 @@ is supplementary to the main colored value.
| 📁 | **Directory** | Current working dir, with `~` for home | cyan |
| 🌿 | **Git** | `repo branch` + `*` if dirty + `↑N`/`↓N` ahead/behind upstream | repo = magenta; branch = green when clean, yellow + red `*` when dirty; `↑` cyan, `↓` yellow. Omitted outside a repo |
| 🤖 | **Model** | Active model display name + `· effort` level (`low`/`medium`/`high`/`xhigh`) | name = blue, effort = light gray; effort omitted for models without the param |
| 📝 | **Context** | `% of context window used` + `(Nk)` tokens | window = 1M for `[1m]` models, else 200k. Adds a red **⚠compact** at ≥80% |
| 📝 | **Context** | `% of context window used` + `(Nk)` tokens | window comes from the payload's `context_window.context_window_size`, falling back to `(1M …)` in the display name, else 200k — **not** from `model.id`, which arrives with any `[1m]` suffix already stripped. Adds a red **⚠compact** at ≥80% |
| 📊 | **5h usage** | `% of the 5-hour rolling limit used` + time until it resets | from `rate_limits.five_hour`; Pro/Max only, and absent until the first API response of a session |
| 💰 | **Cost** | `$` session cost so far + `$/h` burn rate | burn rate shown once the session exceeds ~30s |
| 💾 | **Disk** | `% used` of the filesystem at the cwd | from `statvfs` |
+68 -9
View File
@@ -3,17 +3,19 @@
Reads the hook JSON from stdin, extracts the edited file path, and runs a
language-appropriate formatter -- but, to avoid imposing a style on a project
that never asked for it, opinionated formatters (stylua, ruff, prettier, taplo)
only run when the project opts in via a config file discoverable by walking up
from the edited file. Canonical single-style toolchains run on sight: gofmt and
rustfmt on their go.mod / Cargo.toml marker; fish_indent and shfmt whenever the
tool itself is installed (shell and fish have one de-facto style, no config).
that never asked for it, every formatter here only runs when the project opts in
via a config file discoverable by walking up from the edited file: stylua, ruff,
prettier and taplo on their own configs, gofmt and rustfmt on go.mod / Cargo.toml,
shfmt and fish_indent on .editorconfig. No marker, no formatting -- a repo that
hand-formats its shell keeps its own layout.
Best-effort: always exits 0 so a format hiccup never blocks an edit. jq is not
guaranteed on the host, hence python for the stdin JSON parse.
"""
import fnmatch
import json
import os
import re
import shutil
import subprocess
import sys
@@ -31,6 +33,47 @@ def find_up(start, names):
d = parent
def _expand_braces(pat):
"""`a{b,c}` -> [`ab`, `ac`] (editorconfig sections use {,} alternation)."""
m = re.search(r"\{([^{}]*)\}", pat)
if not m:
return [pat]
out = []
for alt in m.group(1).split(","):
out.extend(_expand_braces(pat[: m.start()] + alt + pat[m.end():]))
return out
def editorconfig_covers(path):
"""True if the nearest ancestor .editorconfig has a section matching `path`.
Mere existence is NOT opt-in: this repo's .editorconfig is deliberately scoped
to `[{*.sh,dotsync,dotpub}]` with no [*] block precisely so fish files stay
untouched, and a generic upstream .editorconfig must not switch a formatter on
for filetypes it never mentions. Patterns without a `/` match the basename
(the editorconfig convention); ones with a `/` match the path relative to the
.editorconfig's directory.
"""
d = find_up(path, [".editorconfig"])
if not d:
return False
name = os.path.basename(path)
rel = os.path.relpath(os.path.abspath(path), d)
try:
with open(os.path.join(d, ".editorconfig"), errors="ignore") as fh:
for line in fh:
line = line.strip()
if not (line.startswith("[") and line.endswith("]")):
continue
for pat in _expand_braces(line[1:-1]):
target = rel if "/" in pat else name
if fnmatch.fnmatch(target, pat.lstrip("/")):
return True
except Exception:
pass
return False
def has_toml_section(start, filename, section):
"""True if the nearest ancestor `filename` contains `section`."""
d = find_up(start, [filename])
@@ -89,14 +132,26 @@ def rustfmt_cmd(path):
return None
# Shell and fish are opt-in too, gated on an .editorconfig SECTION that matches the file
# (shfmt reads .editorconfig natively for its settings, so the marker and the tool agree).
# "One canonical style" turned out not to mean "the style the project uses": 3 of ~/.dots's
# 25 fish files are not fish_indent-clean, so running these on sight rewrote whole files and
# buried a 40-line change in a 200-line diff. ~/.dots opts its shell in via
# `[{*.sh,dotsync,dotpub}]` while its fish files stay hand-formatted — existence of the
# .editorconfig alone must not turn either formatter on.
# Neovim agrees: conform.nvim there lists lua/css/html/ts/js only — never sh or fish.
def fish_cmd(path):
exe = shutil.which("fish_indent") # ships with fish, one canonical style
return [exe, "-w", path] if exe else None
exe = shutil.which("fish_indent")
if exe and editorconfig_covers(path):
return [exe, "-w", path]
return None
def shfmt_cmd(path):
exe = shutil.which("shfmt") # de-facto shell formatter, sane defaults
return [exe, "-w", path] if exe else None
exe = shutil.which("shfmt")
if exe and editorconfig_covers(path):
return [exe, "-w", path]
return None
def taplo_cmd(path):
@@ -133,6 +188,10 @@ def main():
path = ti.get("file_path") or tr.get("filePath") or ""
if not path or not os.path.isfile(path):
return
# Resolve symlinks before walking for markers: deployed dotfiles get edited via
# their ~/.config/... links, but the opt-in configs live beside the real file in
# the repo — a lexical walk from the link's path would never find them.
path = os.path.realpath(path)
handler = HANDLERS.get(os.path.splitext(path)[1].lower())
if not handler:
return
+6
View File
@@ -2,6 +2,12 @@
"$schema": "https://www.schemastore.org/claude-code-keybindings.json",
"$docs": "https://code.claude.com/docs/en/keybindings",
"bindings": [
{
"context": "Chat",
"bindings": {
"shift+enter": "chat:newline"
}
},
{
"context": "Global",
"bindings": {
+19 -12
View File
@@ -25,8 +25,11 @@ repo=$(dirname "$(readlink -f "$SRC/link.sh")")
# If link.sh itself has broken out of stow into a plain file, $repo resolves to $SRC and
# the drift heal below would relink each drifted file onto ITSELF — destroying the only
# copy of its content. Refuse to continue.
if [ "$repo" = "$SRC" ]; then
# copy of its content. Refuse to continue. Both sides are canonicalized before comparing:
# $repo comes back from readlink -f, so a symlinked component anywhere in the path (say
# /home -> /var/home) would otherwise make the two spellings differ and slip the guard.
src_real=$(readlink -f "$SRC" || printf '%s' "$SRC")
if [ "$repo" = "$src_real" ]; then
echo "error: link.sh itself is a plain file (broken out of stow) — re-stow first:" >&2
echo " cd ~/.dots && ./install.sh" >&2
exit 1
@@ -48,11 +51,13 @@ for item in $items; do
if [ -f "$DST/$item" ] && [ ! -L "$DST/$item" ]; then
same=0
case "$item" in
*.json)
python3 -c 'import json,sys; sys.exit(0 if json.load(open(sys.argv[1]))==json.load(open(sys.argv[2])) else 1)' \
"$DST/$item" "$SRC/$item" 2>/dev/null && same=1 ;;
*)
cmp -s "$DST/$item" "$SRC/$item" && same=1 ;;
*.json)
python3 -c 'import json,sys; sys.exit(0 if json.load(open(sys.argv[1]))==json.load(open(sys.argv[2])) else 1)' \
"$DST/$item" "$SRC/$item" 2>/dev/null && same=1
;;
*)
cmp -s "$DST/$item" "$SRC/$item" && same=1
;;
esac
if [ "$same" != 1 ]; then
echo "DRIFT ~/.claude/$item is a real file that differs from the repo copy —" >&2
@@ -78,11 +83,13 @@ for item in $items; do
same=0
if [ -f "$repo/$item" ]; then
case "$item" in
*.json)
python3 -c 'import json,sys; sys.exit(0 if json.load(open(sys.argv[1]))==json.load(open(sys.argv[2])) else 1)' \
"$SRC/$item" "$repo/$item" 2>/dev/null && same=1 ;;
*)
cmp -s "$SRC/$item" "$repo/$item" && same=1 ;;
*.json)
python3 -c 'import json,sys; sys.exit(0 if json.load(open(sys.argv[1]))==json.load(open(sys.argv[2])) else 1)' \
"$SRC/$item" "$repo/$item" 2>/dev/null && same=1
;;
*)
cmp -s "$SRC/$item" "$repo/$item" && same=1
;;
esac
fi
if [ "$same" = 1 ]; then
+42 -14
View File
@@ -34,19 +34,7 @@
],
"defaultMode": "auto"
},
"model": "opus[1m]",
"enableWorkflows": false,
"skipWorkflowUsageWarning": true,
"enabledPlugins": {
"typescript-lsp@claude-plugins-official": true,
"frontend-design@claude-plugins-official": true
},
"statusLine": {
"type": "command",
"command": "/usr/bin/env python3 ~/.claude/statusline.py",
"padding": 0,
"hideVimModeIndicator": true
},
"model": "claude-fable-5[1m]",
"hooks": {
"PostToolUse": [
{
@@ -65,9 +53,21 @@
"worktree": {
"baseRef": "fresh"
},
"enableWorkflows": false,
"statusLine": {
"type": "command",
"command": "/usr/bin/env python3 ~/.claude/statusline.py",
"padding": 0,
"hideVimModeIndicator": true
},
"enabledPlugins": {
"typescript-lsp@claude-plugins-official": true,
"frontend-design@claude-plugins-official": true
},
"effortLevel": "xhigh",
"advisorModel": "fable",
"tui": "fullscreen",
"skipWorkflowUsageWarning": true,
"theme": "dark",
"editorMode": "vim",
"verbose": true,
@@ -75,5 +75,33 @@
"remoteControlAtStartup": false,
"inputNeededNotifEnabled": false,
"agentPushNotifEnabled": false,
"skipAutoPermissionPrompt": true
"skipAutoPermissionPrompt": true,
"autoMode": {
"environment": [
"### Org-wide",
"**Organization**: None configured",
"**Cloud provider(s)**: None configured",
"**Repository visibility**: private (wingman-ai/c2, via gh)",
"**Internal sharing / snippet hosting**: None configured — treat public paste/gist services as outside the trust boundary",
"**Secrets management**: None configured",
"**Default / protected branches**: Default branch master; protected branches: none listed (rulesets not queryable here — do not assume unprotected)",
"**CI/CD deploy targets**: None configured",
"**Network posture**: None configured",
"**Source control**: The trusted repo (wingman-ai/c2, origin github.com:wingman-ai/c2.git) and its remote(s) only",
"**Trusted internal domains**: None configured",
"**Trusted cloud buckets**: None configured",
"**Key internal services**: None configured",
"**Internal package registry**: None configured",
"**Sensitive data locations & audiences**: any file or store holding personal data, confidential business data, credentials, regulated data, or similarly sensitive material; preserve exact handles when known and share only with audiences cleared at the [named+specifics] bar; note .env, .env.dev, .env.example are gitignored/tracked as sensitive-looking paths in this repo",
"**Data retention / declassification**: None configured",
"**Sensitive remote targets**: any namespace, host, or container whose name carries `prod` or `production` as a whole word or name segment",
"**Protected deployment namespaces / environments**: None configured — fall back to the Sensitive remote targets heuristic",
"**Protected IaC scopes**: IAM, RBAC, networking, quota, and node-pool resources; anything whose name or tag carries `prod` or `production` as a whole word or name segment",
"### User-specific",
"**Primary use of Claude Code**: software development (Electron/Vite/React/TypeScript UAV client controller, wm-drone-controller / c2-building)",
"**Trusted repo**: /home/coja/projects/wingman/wm-clients/c2/c2-building (origin github.com:wingman-ai/c2.git), private — confidential material is fine here; content ported from outside this session's repo is not this repo's own work",
"**Org-specific CLIs**: None configured",
"routine under this repo's Makefile/pnpm targets (make all, make prepare, make fnm-node, pnpm scripts) is expected local development activity"
]
}
}
+66 -17
View File
@@ -90,25 +90,49 @@ def term_cols():
# --- shared transcript usage ------------------------------------------------
def _scan_usage(text):
"""Last `message.usage` block among these JSONL lines, or {}."""
usage = {}
for line in text.splitlines():
line = line.strip()
if not line or '"usage"' not in line:
continue
try:
obj = json.loads(line)
except Exception:
continue
msg = obj.get("message")
if isinstance(msg, dict) and isinstance(msg.get("usage"), dict):
usage = msg["usage"]
return usage
TRANSCRIPT_TAIL = 1 << 20 # 1 MiB
def last_usage(data):
"""Most recent `message.usage` block from the transcript, or {}."""
"""Most recent `message.usage` block from the transcript, or {}.
Reads only the tail: this runs on every status refresh and transcripts grow to
tens of MB, so re-parsing the whole file each time is unbounded work for a value
that lives at the end. Falls back to a full scan if the tail holds no usage block
(a long stretch of tool results can push it past the window).
"""
try:
path = data.get("transcript_path")
if not path or not os.path.exists(path):
return {}
usage = {}
with open(path, "r", errors="ignore") as fh:
for line in fh:
line = line.strip()
if not line or '"usage"' not in line:
continue
try:
obj = json.loads(line)
except Exception:
continue
msg = obj.get("message")
if isinstance(msg, dict) and isinstance(msg.get("usage"), dict):
usage = msg["usage"]
with open(path, "rb") as fh:
size = fh.seek(0, os.SEEK_END)
start = max(0, size - TRANSCRIPT_TAIL)
fh.seek(start)
text = fh.read().decode("utf-8", "ignore")
if start:
text = text.partition("\n")[2] # drop the partial first line
usage = _scan_usage(text)
if not usage and start:
with open(path, "r", errors="ignore") as fh:
usage = _scan_usage(fh.read())
return usage
except Exception:
return {}
@@ -193,7 +217,14 @@ def git_segment(cwd, top):
return ""
repo = os.path.basename(top) or "repo"
branch = _git(cwd, "rev-parse", "--abbrev-ref", "HEAD").stdout.strip() or "?"
dirty = bool(_git(cwd, "status", "--porcelain").stdout.strip())
# `status --porcelain` walks the whole work tree, so in a big repo it can blow
# the 1s timeout -- in its own try, because letting that escape dropped the repo
# and branch we already had: a decoration failing must not take the segment down.
try:
dirty = bool(_git(cwd, "status", "--porcelain").stdout.strip())
except Exception:
dirty = False
# ahead/behind vs upstream (omitted when no upstream is configured)
ab = ""
@@ -231,6 +262,25 @@ def model_segment(data):
return out
def context_window(data):
"""Size of this session's context window, in tokens.
Claude Code resolves the `[1m]` model suffix away before handing the status line
its JSON -- `model.id` is always the bare API id ("claude-fable-5") -- so sniffing
the id for "1m" could never detect a 1M session and every one of them was measured
against a 200k window (5x overstated %, ⚠compact from ~16% real usage). The payload
carries the real number; display_name ("Opus 4.8 (1M context)") is the fallback.
"""
cw = data.get("context_window")
if isinstance(cw, dict):
for key in ("context_window_size", "max_tokens", "size"):
val = cw.get(key)
if isinstance(val, (int, float)) and val > 0:
return int(val)
name = ((data.get("model") or {}).get("display_name") or "").lower()
return 1_000_000 if "1m" in name else 200_000
def context_segment(data, usage):
try:
if not usage:
@@ -240,8 +290,7 @@ def context_segment(data, usage):
+ usage.get("cache_creation_input_tokens", 0)
+ usage.get("cache_read_input_tokens", 0)
)
model_id = (data.get("model") or {}).get("id", "")
window = 1_000_000 if "1m" in model_id.lower() else 200_000
window = context_window(data)
pct = used / window * 100 if window else 0
col = bucket(pct, 50, 80)
out = "📝 " + color(f"{pct:.0f}%", col) + color(f" ({used / 1000:.0f}k)", "gray", dim=True)
@@ -1,4 +1,3 @@
function __fish_pipx_complete
set -x _ARGCOMPLETE 1
set -x _ARGCOMPLETE_DFS \t
+97 -87
View File
@@ -14,100 +14,110 @@ set -gx FZF_DEFAULT_COMMAND 'fd --type f --strip-cwd-prefix'
set -gx NEWT_COLORS 'root=black,black;window=black,black;border=white,black;listbox=white,black;label=blue,black;checkbox=red,black;title=green,black;button=white,red;actsellistbox=white,red;actlistbox=white,gray;compactbutton=white,gray;actcheckbox=white,blue;entry=lightgray,black;textbox=blue,black' # themes nmtui
# set -gx BAT_THEME "Catppuccin Mocha"
# Cursor / greeting
set -g fish_greeting
set -g fish_cursor_insert line
set -g fish_cursor_default block
set -g fish_cursor_visual underscore
# Misc
alias exti=exit
alias dmz="cat ~/.config/fish/dmz.txt"
alias nmatrix="neo-matrix -DS 3"
alias grep="grep --color=auto"
alias fzf="fzf --preview 'bat --color=always {}'"
alias fzff="fzf --multi --preview 'bat --style=numbers --color=always {}' | xargs -n 1 nvim"
abbr scrcpyz 'scrcpy -wSK -m 1920 --window-borderless --always-on-top --power-off-on-close --no-audio'
# Git
alias gti="git"
alias gs="git status -s"
alias gca="git add -p . && git commit"
alias gd="git diff --word-diff"
alias gl="git log --graph --show-signature"
alias glog="git log --all --decorate --oneline --color --graph"
alias gla="git log --all --decorate --oneline"
alias gls="serie"
alias gm="git merge"
# List directory
alias ls='eza --icons --group-directories-first'
alias l="ls -l"
alias la="ls -a"
alias lla="ls -la"
alias lt="ls --tree"
# Dotfiles + notes
alias cdots="cd ~/.dots/"
alias cpnotes="cd ~/sync/PersonalNotes/"
alias cnotes="cd ~/sync/CojaDuska/Notes/"
abbr notes "cnotes && nvim"
abbr dots "cdots && nvim"
abbr dotsync "cdots && ./bin/dotsync"
abbr pnotes "cpnotes && nvim"
abbr todo "cpnotes && nvim ToDoNext.md"
abbr aliases "bat ~/.config/fish/config.fish"
# Handy
abbr cat "bat -p"
abbr untar "tar -xf"
abbr unarchive "atool -x"
abbr copy wl-copy
abbr img "kitten icat"
abbr fm yazi
abbr lg lazygit
abbr gcommit "git diff HEAD | aichat -r commit"
abbr aichatdel "rm ~/.config/aichat/sessions/*.yaml"
abbr mkdir "mkdir -p"
abbr faillock "sudo faillock --reset"
abbr xremaps "sudo xremap ~/.config/xremap/config.yml"
abbr pacs "sudo pacman -Syu --noconfirm"
abbr yays "yay --noconfirm --sudoloop"
abbr nmaps "sudo nmap -sn 192.168.0.0/24"
abbr scrcpyc 'scrcpy -wSK -m 1920 --window-borderless --always-on-top --power-off-on-close'
abbr scrcpys 'scrcpy -wS --power-off-on-close'
abbr tts "tt -notheme -bold -showwpm -json"
abbr tuioss "tuios --show-clock --show-keys --show-cpu --show-ram --confirm-quit"
abbr keybinds "cd ~/.dots/ && nvim gui/.config/hypr/keybindings.conf"
abbr vpnhome "sudo wg-quick up wg0"
abbr vpnkralizec "sudo wg-quick up kralizec-wg0"
abbr vpnsumadija "sudo wg-quick up sumadija-wg0"
abbr ipadd "sudo ip route add 192.168.0.234 dev wg0"
abbr aria2cs "aria2c -x 16 -k 1M"
# Change-dir shortcuts
abbr .. "cd .."
abbr ... "cd ../.."
abbr .3 "cd ../../.."
abbr .4 "cd ../../../.."
abbr .5 "cd ../../../../.."
# Tool init (fnm is initialized once in conf.d/fnm.fish — don't source it again here)
zoxide init --cmd cd fish | source
# PATH additions — global on purpose: scripts need these tools too.
# (fnm is initialized once in conf.d/fnm.fish — don't source it again here)
fish_add_path $HOME/.cargo/bin $HOME/.local/bin
# thefuck, lazy-loaded on first use (its --alias eval costs ~170ms of shell startup)
function fuck
functions -e fuck
thefuck --alias | source
fuck $argv
end
# pnpm
set -gx PNPM_HOME "$HOME/.local/share/pnpm"
if not string match -q -- $PNPM_HOME $PATH
set -gx PATH "$PNPM_HOME" $PATH
end
# Everything below is for a human at a prompt. config.fish is sourced by EVERY fish —
# scripts and `fish -c` included — and zoxide's `--cmd cd` alias is unconditional: in a
# script, a failed `cd` would silently land in zoxide's best database match (exit 0)
# instead of erroring, and every script `cd` would pollute the database. Aliases/abbrs
# leak into scripts the same way (`ls` → eza, `mkdir` → `mkdir -p`, fzf previews …).
if status is-interactive
# Cursor / greeting
set -g fish_greeting
set -g fish_cursor_insert line
set -g fish_cursor_default block
set -g fish_cursor_visual underscore
# Misc
alias exti=exit
alias dmz="cat ~/.config/fish/dmz.txt"
alias nmatrix="neo-matrix -DS 3"
alias grep="grep --color=auto"
alias fzf="fzf --preview 'bat --color=always {}'"
alias fzff="fzf --multi --preview 'bat --style=numbers --color=always {}' | xargs -n 1 nvim"
abbr scrcpyz 'scrcpy -wSK -m 1920 --window-borderless --always-on-top --power-off-on-close --no-audio'
# Git
alias gti="git"
alias gs="git status -s"
alias gca="git add -p . && git commit"
alias gd="git diff --word-diff"
alias gl="git log --graph --show-signature"
alias glog="git log --all --decorate --oneline --color --graph"
alias gla="git log --all --decorate --oneline"
alias gls="serie"
alias gm="git merge"
# List directory
alias ls='eza --icons --group-directories-first'
alias l="ls -l"
alias la="ls -a"
alias lla="ls -la"
alias lt="ls --tree"
# Dotfiles + notes
alias cdots="cd ~/.dots/"
alias cpnotes="cd ~/sync/PersonalNotes/"
alias cnotes="cd ~/sync/CojaDuska/Notes/"
abbr notes "cnotes && nvim"
abbr dots "cdots && nvim"
abbr dotsync "cdots && ./bin/dotsync"
abbr pnotes "cpnotes && nvim"
abbr todo "cpnotes && nvim ToDoNext.md"
abbr aliases "bat ~/.config/fish/config.fish"
# Handy
abbr cat "bat -p"
abbr untar "tar -xf"
abbr unarchive "atool -x"
abbr copy wl-copy
abbr img "kitten icat"
abbr fm yazi
abbr lg lazygit
abbr gcommit "git diff HEAD | aichat -r commit"
abbr aichatdel "rm ~/.config/aichat/sessions/*.yaml"
abbr mkdir "mkdir -p"
abbr faillock "sudo faillock --reset"
abbr xremaps "sudo xremap ~/.config/xremap/config.yml"
abbr pacs "sudo pacman -Syu --noconfirm"
abbr yays "yay --noconfirm --sudoloop"
abbr nmaps "sudo nmap -sn 192.168.0.0/24"
abbr scrcpyc 'scrcpy -wSK -m 1920 --window-borderless --always-on-top --power-off-on-close'
abbr scrcpys 'scrcpy -wS --power-off-on-close'
abbr tts "tt -notheme -bold -showwpm -json"
abbr tuioss "tuios --show-clock --show-keys --show-cpu --show-ram --confirm-quit"
abbr keybinds "cd ~/.dots/ && nvim gui/.config/hypr/keybindings.conf"
abbr vpnhome "sudo wg-quick up wg0"
abbr vpnkralizec "sudo wg-quick up kralizec-wg0"
abbr vpnsumadija "sudo wg-quick up sumadija-wg0"
abbr ipadd "sudo ip route add 192.168.0.234 dev wg0"
abbr aria2cs "aria2c -x 16 -k 1M"
# Change-dir shortcuts
abbr .. "cd .."
abbr ... "cd ../.."
abbr .3 "cd ../../.."
abbr .4 "cd ../../../.."
abbr .5 "cd ../../../../.."
# Tool init
zoxide init --cmd cd fish | source
# thefuck, lazy-loaded on first use (its --alias eval costs ~170ms of shell startup)
function fuck
functions -e fuck
thefuck --alias | source
fuck $argv
end
end
# Per-host extras
test -f ~/.config/fish/host.fish; and source ~/.config/fish/host.fish
@@ -1,23 +1,25 @@
function __ssh_agent_is_started -d "check if ssh agent is already started"
if test -n "$SSH_CONNECTION"
# This is an SSH session
ssh-add -l > /dev/null 2>&1
if test $status -eq 0 -o $status -eq 1
# An SSH agent was forwarded
return 0
end
end
if test -n "$SSH_CONNECTION"
# This is an SSH session
ssh-add -l >/dev/null 2>&1
if test $status -eq 0 -o $status -eq 1
# An SSH agent was forwarded
return 0
end
end
if begin; test -f "$SSH_ENV"; and test -z "$SSH_AGENT_PID"; end
source $SSH_ENV > /dev/null
end
if begin
test -f "$SSH_ENV"; and test -z "$SSH_AGENT_PID"
end
source $SSH_ENV >/dev/null
end
if test -z "$SSH_AGENT_PID"
return 1
end
if test -z "$SSH_AGENT_PID"
return 1
end
ssh-add -l > /dev/null 2>&1
if test $status -eq 2
return 1
end
ssh-add -l >/dev/null 2>&1
if test $status -eq 2
return 1
end
end
@@ -1,5 +1,5 @@
function __ssh_agent_start -d "start a new ssh agent"
ssh-agent -c | sed 's/^echo/#echo/' > $SSH_ENV
chmod 600 $SSH_ENV
source $SSH_ENV > /dev/null
ssh-agent -c | sed 's/^echo/#echo/' >$SSH_ENV
chmod 600 $SSH_ENV
source $SSH_ENV >/dev/null
end
+2 -2
View File
@@ -1,3 +1,3 @@
function ask --description 'answer from cht.sh'
curl -s https://cht.sh/$(string join '+' $argv[1..])
function ask --description 'answer from cht.sh'
curl -s https://cht.sh/$(string join '+' $argv[1..])
end
@@ -15,4 +15,3 @@ function bind_M_n_history
end
end
end
+5 -1
View File
@@ -1,3 +1,7 @@
function clis --description 'fzf select cli'
eval (cat $HOME/.config/misc/clis.txt | command fzf --multi)
# one eval per selection — a single eval of the joined lines would run the
# first command with the other selections as its arguments
for cmd in (cat $HOME/.config/misc/clis.txt | command fzf --multi)
eval $cmd
end
end
@@ -8,7 +8,7 @@ function copy-commandline
else if type -q wl-copy
echo -n "$cmd" | wl-copy
end
# Provide brief visual feedback in the prompt
set -l original_cmd (commandline)
commandline -r "Copied!"
+4 -2
View File
@@ -7,8 +7,10 @@ function kp
read -s -P "Enter db password: " pass
echo ""
set -l entry (echo $pass | keepassxc-cli ls -q -R $KP_DB | fzf)
# -f flattens to full paths; without it nested entries are printed indented and
# without their group prefix, so `clip` can't find them. Group lines end in "/".
set -l entry (printf '%s\n' $pass | keepassxc-cli ls -q -R -f $KP_DB | string match -v '*/' | fzf | string trim)
if test -n "$entry"
echo $pass | keepassxc-cli clip -q $KP_DB $entry 0
printf '%s\n' $pass | keepassxc-cli clip -q $KP_DB $entry 0
end
end
+15 -15
View File
@@ -1,18 +1,18 @@
function lk --description 'linux knowledge'
set -l DB "$HOME/projects/dmz/soft/lk/db.rec"
set -l passes 0
set -l count 0
set -l query ""
function lk --description 'linux knowledge'
set -l DB "$HOME/projects/dmz/soft/lk/db.rec"
set -l passes 0
set -l count 0
set -l query ""
while test $passes -lt 2; and test $count -eq 0
set query (recsel "$DB" -p aim,tag | recsel -iq "$query" -CP aim,tag | sort -u | fzf --preview="recsel \"$DB\" -e \"aim~{}\" | bat")
if test -n "$query"
set count (recsel "$DB" -q "$query" -c)
end
set passes (math $passes + 1)
end
while test $passes -lt 2; and test $count -eq 0
set query (recsel "$DB" -p aim,tag | recsel -iq "$query" -CP aim,tag | sort -u | fzf --preview="recsel \"$DB\" -e \"aim~{}\" | bat")
if test -n "$query"
set count (recsel "$DB" -q "$query" -c)
end
set passes (math $passes + 1)
end
if test $count -eq 1
recsel "$DB" -q "$query" | recfmt -f "$HOME/projects/dmz/soft/lk/lists.fmt" | less
end
if test $count -eq 1
recsel "$DB" -q "$query" | recfmt -f "$HOME/projects/dmz/soft/lk/lists.fmt" | less
end
end
+20 -23
View File
@@ -1,24 +1,21 @@
function opencodes --description 'contained opencode'
bwrap \
--unshare-all \
--new-session \
--clearenv \
--die-with-parent \
--share-net \
--dev /dev \
--proc /proc \
--ro-bind /usr /usr \
--ro-bind /lib /lib \
--ro-bind /lib64 /lib64 \
--ro-bind /bin /bin \
--ro-bind /etc /etc \
--ro-bind ~/.config/opencode ~/.config/opencode \
--tmpfs /tmp/node-compile-cache \
--bind ~/.local/state/opencode ~/.local/state/opencode \
--bind ~/.local/share/opencode ~/.local/share/opencode \
--bind "$(pwd)" "$(pwd)" \
opencode
function opencodes --description 'contained opencode'
bwrap \
--unshare-all \
--new-session \
--clearenv \
--die-with-parent \
--share-net \
--dev /dev \
--proc /proc \
--ro-bind /usr /usr \
--ro-bind /lib /lib \
--ro-bind /lib64 /lib64 \
--ro-bind /bin /bin \
--ro-bind /etc /etc \
--ro-bind ~/.config/opencode ~/.config/opencode \
--tmpfs /tmp/node-compile-cache \
--bind ~/.local/state/opencode ~/.local/state/opencode \
--bind ~/.local/share/opencode ~/.local/share/opencode \
--bind "$(pwd)" "$(pwd)" \
opencode
end
+5 -1
View File
@@ -1,3 +1,7 @@
function tuis --description 'fzf select tui'
eval (cat $HOME/.config/misc/tuis.txt | command fzf --multi)
# one eval per selection — a single eval of the joined lines would run the
# first command with the other selections as its arguments
for cmd in (cat $HOME/.config/misc/tuis.txt | command fzf --multi)
eval $cmd
end
end
+1
View File
@@ -40,6 +40,7 @@ asciiquarium
cbonsai
tclock
hunk
tuicr
gittop
lazygit
lazyjournal
@@ -3,6 +3,25 @@ require("nvchad.configs.lspconfig").defaults()
local servers = { "html", "cssls", "ts_ls", "eslint" }
vim.lsp.enable(servers)
-- Waybar/GTK stylesheets are GTK CSS, a different dialect: `@name` references
-- @define-color colors (waybar's theme.css) and functions like alpha() are
-- valid there but not in web CSS, so cssls floods "property value expected" /
-- "{ expected" false errors. No LSP speaks the GTK dialect, so keep cssls
-- attached (standard-property completion still useful) and mute diagnostics
-- on those buffers only.
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
if not client or client.name ~= "cssls" then
return
end
local path = vim.api.nvim_buf_get_name(args.buf)
if path:match "/waybar/" or path:match "/gtk%-%d" then
vim.diagnostic.enable(false, { bufnr = args.buf })
end
end,
})
-- local lspconfig = require "lspconfig"
-- local nvlsp = require "nvchad.configs.lspconfig"
--
+20
View File
@@ -34,6 +34,26 @@ return {
"regex",
},
},
-- On the main branch nothing acts on `ensure_installed` at startup: setup() takes only
-- install_dir, and NvChad's :TSInstallAll (which does read this list off the lazy spec)
-- only runs from `build`, i.e. when the plugin is installed or updated. So a language
-- added here stayed uninstalled until the next rebuild — 9 of these 15 were missing,
-- silently falling back to regex highlighting. Install whatever is absent, once, here.
config = function(_, opts)
local ts = require "nvim-treesitter"
ts.setup()
local have = {}
for _, lang in ipairs(ts.get_installed()) do
have[lang] = true
end
local missing = vim.tbl_filter(function(lang)
return not have[lang]
end, opts.ensure_installed or {})
if #missing > 0 then
ts.install(missing) -- async; highlighting picks each up as it lands
end
end,
},
{
+2
View File
@@ -141,6 +141,7 @@
"Qwen3.6-35B-A3B-MTP-UD-IQ3_XXS": { "name": "Qwen3.6 35B · 24k · vision — daily driver (remote)", "attachment": true, "limit": { "context": 24576, "output": 8192 } },
"Qwen3.6-35B-A3B-Thinking": { "name": "Qwen3.6 35B Thinking · 24k · vision — hard problems (remote)", "attachment": true, "limit": { "context": 24576, "output": 8192 } },
"Qwen3.5-9B-UD-Q6_K_XL": { "name": "Qwen3.5 9B · 32k · vision — quick tasks (remote)", "attachment": true, "limit": { "context": 32768, "output": 8192 } },
"Qwen3.8-27B-UD-IQ3_XXS": { "name": "Qwen3.8 27B · 24k · vision — hybrid reasoner (remote)", "attachment": true, "limit": { "context": 24576, "output": 8192 } },
"gemma-4-26B-A4B-it-UD-IQ4_XS": { "name": "Gemma 4 26B · 24k · vision — quality generalist (remote)", "attachment": true, "limit": { "context": 24576, "output": 4096 } },
"gemma-4-E4B-it-UD-Q8_K_XL": { "name": "Gemma 4 E4B · 64k · vision — fast generalist, long docs (remote)", "attachment": true, "limit": { "context": 65536, "output": 16384 } },
"GLM-4.7-Flash-UD-Q4_K_XL": { "name": "GLM-4.7 Flash · 24k — quality coder (remote)", "limit": { "context": 24576, "output": 8192 } },
@@ -160,6 +161,7 @@
"Qwen3.6-35B-A3B-MTP-UD-IQ3_XXS": { "name": "Qwen3.6 35B · 24k · vision — daily driver", "attachment": true, "limit": { "context": 24576, "output": 8192 } },
"Qwen3.6-35B-A3B-Thinking": { "name": "Qwen3.6 35B Thinking · 24k · vision — hard problems", "attachment": true, "limit": { "context": 24576, "output": 8192 } },
"Qwen3.5-9B-UD-Q6_K_XL": { "name": "Qwen3.5 9B · 32k · vision — quick tasks", "attachment": true, "limit": { "context": 32768, "output": 8192 } },
"Qwen3.8-27B-UD-IQ3_XXS": { "name": "Qwen3.8 27B · 24k · vision — hybrid reasoner", "attachment": true, "limit": { "context": 24576, "output": 8192 } },
"gemma-4-26B-A4B-it-UD-IQ4_XS": { "name": "Gemma 4 26B · 24k · vision — quality generalist", "attachment": true, "limit": { "context": 24576, "output": 4096 } },
"gemma-4-E4B-it-UD-Q8_K_XL": { "name": "Gemma 4 E4B · 64k · vision — fast generalist, long docs", "attachment": true, "limit": { "context": 65536, "output": 16384 } },
"GLM-4.7-Flash-UD-Q4_K_XL": { "name": "GLM-4.7 Flash · 24k — quality coder", "limit": { "context": 24576, "output": 8192 } },
+15 -8
View File
@@ -7,15 +7,22 @@
# cpu module icon (U+F035B).
printf '\U000f035b '
rt="${XDG_RUNTIME_DIR:-/tmp}"
st="$rt/tmux_cpu_stat" # previous totals: tot idle
st="$rt/tmux_cpu_stat" # previous totals: tot idle
read -r _ u n s i w rest < /proc/stat
idle=$((i + w)); tot=$((u + n + s + i + w)); for v in $rest; do tot=$((tot + v)); done
read -r _ u n s i w rest </proc/stat
idle=$((i + w))
tot=$((u + n + s + i + w))
for v in $rest; do tot=$((tot + v)); done
p_tot=0; p_idle=0
[ -r "$st" ] && read -r p_tot p_idle < "$st"
[ "$p_tot" -gt "$tot" ] && { p_tot=0; p_idle=0; } # stale state from a previous boot
printf '%s %s\n' "$tot" "$idle" > "$st"
p_tot=0
p_idle=0
[ -r "$st" ] && read -r p_tot p_idle <"$st"
[ "$p_tot" -gt "$tot" ] && {
p_tot=0
p_idle=0
} # stale state from a previous boot
printf '%s %s\n' "$tot" "$idle" >"$st"
dt=$((tot - p_tot)); di=$((idle - p_idle))
dt=$((tot - p_tot))
di=$((idle - p_idle))
if [ "$dt" -le 0 ]; then printf -- '--%%'; else printf '%d%%' $(((100 * (dt - di) + dt / 2) / dt)); fi
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# RAM utilisation (%) — used = MemTotal - MemAvailable, from /proc/meminfo.
# Called from tmux status-right via #(); cached per status-interval.
printf '\U000f0f86 ' # Waybar's memory glyph (U+F0F86), then the percentage
printf '\U000f0f86 ' # Waybar's memory glyph (U+F0F86), then the percentage
awk '/^MemTotal:/{t=$2} /^MemAvailable:/{a=$2} END{if(t>0)printf "%d%%",(t-a)*100/t; else printf "--%%"}' /proc/meminfo
+12 -9
View File
@@ -10,8 +10,8 @@
# only one of them re-samples the counters per refresh.
which=${1:-dl}
rt="${XDG_RUNTIME_DIR:-/tmp}"
ctr="$rt/tmux_net_ctr" # previous counters: rx tx epoch.ns
cache="$rt/tmux_net_cache" # computed rates: dl_rate ul_rate epoch_s
ctr="$rt/tmux_net_ctr" # previous counters: rx tx epoch.ns
cache="$rt/tmux_net_cache" # computed rates: dl_rate ul_rate epoch_s
emit() { # emit <icon-printf-escape> <rate>
printf "$1 %s" "$2"
@@ -20,8 +20,8 @@ emit() { # emit <icon-printf-escape> <rate>
# Reuse a fresh cache so dl and ul agree and we only sample once per refresh.
fresh_emit() { # print from the cache and exit — if the cache is fresh enough
[ -r "$cache" ] || return 1
read -r cdl cul cts < "$cache"
{ [ -n "$cts" ] && [ "$(( $(date +%s) - cts ))" -lt 3 ]; } || return 1
read -r cdl cul cts <"$cache"
{ [ -n "$cts" ] && [ "$(($(date +%s) - cts))" -lt 3 ]; } || return 1
if [ "$which" = ul ]; then emit '\U0000f093' "$cul"; else emit '\U0000f019' "$cdl"; fi
exit 0
}
@@ -39,15 +39,18 @@ fresh_emit
now=$(date +%s.%N)
read -r rx tx < <(awk 'NR>2{gsub(/:/," "); if($1!="lo"){r+=$2; t+=$10}} END{print r+0, t+0}' /proc/net/dev)
drx=0; dtx=0; dt=1
drx=0
dtx=0
dt=1
if [ -r "$ctr" ]; then
read -r prx ptx pnow < "$ctr"
read -r prx ptx pnow <"$ctr"
dt=$(awk -v a="$now" -v b="$pnow" 'BEGIN{d=a-b; print (d>0.05)?d:1}')
drx=$(( rx - prx )); dtx=$(( tx - ptx ))
drx=$((rx - prx))
dtx=$((tx - ptx))
[ "$drx" -lt 0 ] && drx=0
[ "$dtx" -lt 0 ] && dtx=0
fi
printf '%s %s %s\n' "$rx" "$tx" "$now" > "$ctr"
printf '%s %s %s\n' "$rx" "$tx" "$now" >"$ctr"
human() { awk -v b="$1" 'BEGIN{ split("B K M G T", u, " "); i=1;
while (b >= 1024 && i < 5) { b /= 1024; i++ }
@@ -55,6 +58,6 @@ human() { awk -v b="$1" 'BEGIN{ split("B K M G T", u, " "); i=1;
dl=$(human "$(awk -v x="$drx" -v d="$dt" 'BEGIN{printf "%d", x/d}')")
ul=$(human "$(awk -v x="$dtx" -v d="$dt" 'BEGIN{printf "%d", x/d}')")
printf '%s %s %s\n' "$dl" "$ul" "$(date +%s)" > "$cache"
printf '%s %s %s\n' "$dl" "$ul" "$(date +%s)" >"$cache"
if [ "$which" = ul ]; then emit '\U0000f093' "$ul"; else emit '\U0000f019' "$dl"; fi
+14 -9
View File
@@ -5,15 +5,19 @@
# (acpitz can be a bogus constant on desktops, hence last). Prints NOTHING when
# no sensor exists (VMs/containers) so the segment simply disappears — which is
# also why the trailing spacing lives in here, not in status-right.
cpu_zone=""; any_zone=""; hw=""
cpu_zone=""
any_zone=""
hw=""
for d in /sys/class/thermal/thermal_zone*/; do
[ -r "${d}temp" ] && [ -r "${d}type" ] || continue
t=$(<"${d}temp")
case $t in ''|*[!0-9]*) continue ;; esac
ty=$(<"${d}type"); ty=${ty,,}
case $ty in *x86_pkg_temp*|*coretemp*|*k10temp*|*tctl*|*cpu*)
if [ -z "$cpu_zone" ] || [ "$t" -gt "$cpu_zone" ]; then cpu_zone=$t; fi ;;
case $t in '' | *[!0-9]*) continue ;; esac
ty=$(<"${d}type")
ty=${ty,,}
case $ty in *x86_pkg_temp* | *coretemp* | *k10temp* | *tctl* | *cpu*)
if [ -z "$cpu_zone" ] || [ "$t" -gt "$cpu_zone" ]; then cpu_zone=$t; fi
;;
esac
if [ -z "$any_zone" ] || [ "$t" -gt "$any_zone" ]; then any_zone=$t; fi
done
@@ -21,17 +25,18 @@ done
if [ -z "$cpu_zone" ]; then
for d in /sys/class/hwmon/hwmon*/; do
[ -r "${d}name" ] || continue
case $(<"${d}name") in coretemp|k10temp|zenpower|cpu_thermal)
case $(<"${d}name") in coretemp | k10temp | zenpower | cpu_thermal)
for f in "${d}"temp*_input; do
[ -r "$f" ] || continue
t=$(<"$f")
case $t in ''|*[!0-9]*) continue ;; esac
case $t in '' | *[!0-9]*) continue ;; esac
if [ -z "$hw" ] || [ "$t" -gt "$hw" ]; then hw=$t; fi
done ;;
done
;;
esac
done
fi
t=${cpu_zone:-${hw:-$any_zone}}
[ -n "$t" ] || exit 0
printf '\U000f050f %d° ' $(( (t + 500) / 1000 ))
printf '\U000f050f %d° ' $(((t + 500) / 1000))
+71 -5
View File
@@ -3,6 +3,18 @@
set -g extended-keys on
set -g extended-keys-format csi-u
# Mouse/touch reporting. Chiefly so the window list in the status bar is
# tappable — tmux binds MouseDown1Status to `switch-client -t =` out of the box,
# and status-format[1] below keeps the `range=window|…` markers that make each
# entry a hit target, so a tap on a tab switches to it with no extra binding.
# Wheel over the bar cycles windows; right-click a tab or the session name for
# tmux's built-in menus (no touch equivalent — a phone can't right-click).
#
# The cost: drag-select now goes to tmux instead of kitty, so hold Shift when you
# want kitty's own selection. Scroll wheel enters copy-mode on the pane rather
# than kitty's scrollback, and a click focuses the pane under it.
set -g mouse on
# Number windows (and panes) from 1 instead of 0 — easier to reach on the
# keyboard. renumber-windows keeps them gap-free when a window is closed.
set -g base-index 1
@@ -42,8 +54,36 @@ set -g status-style "bg=default,fg=#cdd6f4" # transparent: no bar background
set -g status-left-length 40
set -g status-right-length 100
# Left: session name (mauve/bold) + thin divider.
set -g status-left " #[fg=#cba6f7,bold]#S #[fg=#45475a]│ "
# --- Mobile view -----------------------------------------------------------
# status-left and status-right are re-evaluated per client on every draw, and
# #{client_width} is that client's own width — so in `auto` mode a phone attached
# to the same session as the desktop renders the compact bars while the desktop
# keeps the full ones, simultaneously and with no intervention.
#
# @status_mode is auto (per-client, by width) | full | compact, cycled with
# prefix+S. Rather than branch each bar three ways, the mode picks the width a
# client has to beat — 0 nothing can lose, 9999 nothing can win — so each
# decision below stays a single comparison. The 100 is the switch point: the full
# right segment is ~60 columns before the window list even starts. Set
# @status_mode without -g to scope an override to one session, so a grouped
# mobile session can sit on `compact` while the desktop session stays on `auto`.
#
# Each variant lives in its own option instead of inline because #{?a,b,c} splits
# its branches on commas and the styles are full of them (#[fg=…,bold]); option
# values are substituted after the ternary is parsed, so those commas are never
# seen as separators.
#
# NOTE: the comparisons MUST use #{e|>=:…}. Plain #{>=:…} compares as strings, so
# "144" >= "100" is false and every client would get the compact bar.
set -g @status_mode auto
set -g @status_cutoff "#{?#{==:#{@status_mode},full},0,#{?#{==:#{@status_mode},compact},9999,100}}"
# Left: session name (mauve/bold) + thin divider. Compact clips the name to two
# characters — enough to tell sessions apart when every column counts. #{=2:…}
# takes a format NAME, so it's session_name here rather than the #S shorthand.
set -g @left_full " #[fg=#cba6f7,bold]#S #[fg=#45475a]│ "
set -g @left_compact " #[fg=#cba6f7,bold]#{=2:session_name} #[fg=#45475a]│ "
set -g status-left "#{?#{e|>=:#{client_width},#{E:@status_cutoff}},#{E:@left_full},#{E:@left_compact}}"
# Middle: flat window list — inactive muted, active mauve/bold, 2-space gaps.
set -g window-status-separator " "
@@ -52,9 +92,35 @@ setw -g window-status-current-format "#[fg=#cba6f7,bold]#I #[fg=#cdd6f4,bold]#W"
setw -g window-status-activity-style "fg=#f9e2af"
# Right: net dl/ul · cpu · mem │ date │ clock. continuum prepends its invisible
# save hook here (this block sits above the tpm `run` line, so the hook survives
# every reload). Metrics are scripts/*.sh, symlinked into ~/.config/tmux/scripts/.
set -g status-right "#[fg=#a6e3a1]#(~/.config/tmux/scripts/net.sh dl) #[fg=#f9e2af]#(~/.config/tmux/scripts/net.sh ul) #[fg=#89b4fa]#(~/.config/tmux/scripts/cpu.sh) #[fg=#94e2d5]#(~/.config/tmux/scripts/mem.sh) #[fg=#fab387]#(~/.config/tmux/scripts/temp.sh)#[fg=#45475a]│ #[fg=#a6adc8]%a %b %-d #[fg=#45475a]│ #[fg=#cba6f7,bold]%H:%M "
# save hook to status-right (this block sits above the tpm `run` line, so the
# hook survives every reload). Metrics are scripts/*.sh, symlinked into
# ~/.config/tmux/scripts/. The hook lands ahead of the #{?} below, i.e. outside
# it, so it keeps running for every client regardless of which variant is drawn.
set -g @right_full "#[fg=#a6e3a1]#(~/.config/tmux/scripts/net.sh dl) #[fg=#f9e2af]#(~/.config/tmux/scripts/net.sh ul) #[fg=#89b4fa]#(~/.config/tmux/scripts/cpu.sh) #[fg=#94e2d5]#(~/.config/tmux/scripts/mem.sh) #[fg=#fab387]#(~/.config/tmux/scripts/temp.sh)#[fg=#45475a]│ #[fg=#a6adc8]%a %b %-d #[fg=#45475a]│ #[fg=#cba6f7,bold]%H:%M "
# Compact: cpu + mem only. Net rates and the temp glyph go first (widest, least
# useful on a phone), then the date, then the clock — a phone shows its own.
set -g @right_compact "#[fg=#89b4fa]#(~/.config/tmux/scripts/cpu.sh) #[fg=#94e2d5]#(~/.config/tmux/scripts/mem.sh) "
set -g status-right "#{?#{e|>=:#{client_width},#{E:@status_cutoff}},#{E:@right_full},#{E:@right_compact}}"
# Cycle auto -> compact -> full -> auto. Braces instead of nested quoted strings
# so the styles' commas and the inner if-shell don't need escaping.
bind S {
if -F '#{==:#{@status_mode},auto}' {
set -g @status_mode compact
display 'status bar: compact (forced)'
} {
if -F '#{==:#{@status_mode},compact}' {
set -g @status_mode full
display 'status bar: full (forced)'
} {
set -g @status_mode auto
display 'status bar: auto — switches at 100 columns'
}
}
refresh-client -S
}
# Two rows: [0] drawn top-border rule · [1] the bar content. The rule doubles as
# the separator from the pane content above; tmux status height is whole rows, so