This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

tools.nnn: init wrapped file manager with previews

Colours stay inside the terminal's ANSI palette (NNN_FCOLORS, NNN_COLORS,
bat's ansi theme, mdcat), including nnn's nerd icon table which hardcodes
xterm-256 codes, so retheming the terminal also retints a session running
on the far side of ssh.

The image previewer asks the terminal which graphics protocol it speaks
instead of letting chafa guess from inherited KITTY_* variables, which
claim kitty even where the data cannot reach a terminal and leave blank
placeholder cells behind. Frames are rendered into memory and written
with signals blocked, so preview-tui killing the previewer on a selection
change cannot truncate an escape sequence.

+313
+213
tools/nnn/flake-module.nix
··· 1 + { inputs, ... }: 2 + { 3 + perSystem = 4 + { pkgs, lib, ... }: 5 + let 6 + # nnn's nerd icon table hardcodes xterm-256 colours, which would be the 7 + # one part of the UI that ignores the terminal theme. Remap them onto the 8 + # ANSI palette; 0 means "use the file's own colour". 9 + iconColors = { 10 + COLOR_ARCHIVE = 1; 11 + COLOR_AUDIO = 3; 12 + COLOR_AUDIO1 = 13; 13 + COLOR_C = 12; 14 + COLOR_CSS = 5; 15 + COLOR_DOCS = 9; 16 + COLOR_DOCUMENT = 0; 17 + COLOR_ELIXIR = 5; 18 + COLOR_FSHARP = 6; 19 + COLOR_IMAGE = 10; 20 + COLOR_JAVA = 4; 21 + COLOR_JAVASCRIPT = 3; 22 + COLOR_LUA = 4; 23 + COLOR_PYTHON = 11; 24 + COLOR_REACT = 14; 25 + COLOR_RUBY = 1; 26 + COLOR_SCALA = 9; 27 + COLOR_SHELL = 2; 28 + COLOR_VIDEO = 14; 29 + COLOR_VIDEO1 = 11; 30 + COLOR_VIM = 2; 31 + }; 32 + 33 + # Anything outside 0-15 left in the table means nnn grew an icon colour 34 + # we have not mapped, so fail the build instead of shipping a stray hue. 35 + unmapped = "COLOR_X\\(COLOR_[A-Z0-9_]+, *(1[6-9]|[2-9][0-9]|[0-9]{3})\\)"; 36 + 37 + nnn = (pkgs.nnn.override { withNerdIcons = true; }).overrideAttrs (old: { 38 + postPatch = (old.postPatch or "") + '' 39 + sed -i -E ${ 40 + lib.concatStringsSep " " ( 41 + lib.mapAttrsToList ( 42 + name: code: "-e 's/COLOR_X\\(${name},[^)]*\\)/COLOR_X(${name}, ${toString code})/'" 43 + ) iconColors 44 + ) 45 + } src/icons.h 46 + if grep -nE '${unmapped}' src/icons.h; then 47 + echo "nnn icon colours above are outside the ANSI palette" >&2 48 + exit 1 49 + fi 50 + ''; 51 + }); 52 + 53 + # Asks the terminal which graphics protocol it speaks. Run once per nnn 54 + # session so no image preview has to wait on a terminal round trip. 55 + term-caps = pkgs.writeShellApplication { 56 + name = "nnn-term-caps"; 57 + runtimeInputs = [ pkgs.coreutils ]; 58 + text = builtins.readFile ./term-caps.sh; 59 + }; 60 + 61 + imgpreview = pkgs.writeShellApplication { 62 + name = "nnn-imgpreview"; 63 + runtimeInputs = [ 64 + pkgs.chafa 65 + pkgs.coreutils 66 + ]; 67 + text = builtins.readFile ./imgpreview.sh; 68 + }; 69 + 70 + # Tools the preview/open plugins probe for at runtime. They are prefixed 71 + # onto $PATH rather than replacing it, so a tmux/kitty already in the 72 + # environment stays reachable. 73 + pluginTools = [ 74 + imgpreview 75 + pkgs.bat 76 + pkgs.coreutils 77 + pkgs.exiftool 78 + pkgs.fd 79 + pkgs.ffmpegthumbnailer 80 + pkgs.file 81 + pkgs.findutils 82 + pkgs.fzf 83 + pkgs.gnutar 84 + pkgs.jq 85 + pkgs.less 86 + pkgs.libarchive 87 + pkgs.librsvg 88 + pkgs.mdcat 89 + pkgs.mediainfo 90 + pkgs.ncurses 91 + pkgs.poppler-utils 92 + pkgs.procps 93 + pkgs.tree 94 + pkgs.unzip 95 + pkgs.viu 96 + pkgs.w3m 97 + ]; 98 + 99 + # nnn resolves bare plugin names inside $XDG_CONFIG_HOME/nnn/plugins, 100 + # which we do not own, so the plugins live here and are referenced by 101 + # absolute path from NNN_PLUG. 102 + plugins = 103 + let 104 + pathLine = ''export PATH="${lib.makeBinPath pluginTools}:$PATH"''; 105 + in 106 + pkgs.runCommand "nnn-plugins" { } '' 107 + mkdir -p $out/libexec/nnn 108 + cp ${nnn}/share/plugins/.nnn-plugin-helper $out/libexec/nnn/ 109 + for plugin in preview-tui fzcd fzopen nuke; do 110 + install -m755 ${nnn}/share/plugins/"$plugin" $out/libexec/nnn/"$plugin" 111 + sed -i '2i ${pathLine}' $out/libexec/nnn/"$plugin" 112 + done 113 + 114 + # glow renders markdown with a fixed hex palette and, as of 2.1.2, 115 + # emits no colour at all outside a tty. mdcat renders to plain ANSI 116 + # attributes, so markdown previews follow the terminal like the rest. 117 + substituteInPlace $out/libexec/nnn/preview-tui \ 118 + --replace-fail 'md) if exists glow; then' 'md) if exists mdcat; then' \ 119 + --replace-fail 'fifo_pager glow -s dark "$1"' 'fifo_pager mdcat --columns "$cols" "$1"' 120 + substituteInPlace $out/libexec/nnn/nuke \ 121 + --replace-fail 'if type glow >/dev/null 2>&1; then' 'if type mdcat >/dev/null 2>&1; then' \ 122 + --replace-fail 'glow -sdark "''${FPATH}" | eval "$PAGER"' 'mdcat "''${FPATH}" | eval "$PAGER"' 123 + 124 + # NNN_IMGFORMAT is decided before nnn starts, and preview-tui only 125 + # forwards the variables on this list into the preview pane. 126 + substituteInPlace $out/libexec/nnn/preview-tui \ 127 + --replace-fail \ 128 + '"NNN_PREVIEWIMGPROG=''${NNN_PREVIEWIMGPROG:-}"' \ 129 + '"NNN_PREVIEWIMGPROG=''${NNN_PREVIEWIMGPROG:-}" "NNN_IMGFORMAT=''${NNN_IMGFORMAT:-symbols}"' 130 + ''; 131 + 132 + # Every colour below is an xterm-256 index in 0x00-0x0f, i.e. an entry of 133 + # the terminal's own ANSI palette. Retheming the terminal - including 134 + # switch-theme repainting a local kitty while nnn runs on the far end of 135 + # an ssh session - recolours nnn without nnn or the remote host being 136 + # involved at all. 137 + # 138 + # Order is fixed, see nnn(1) NNN_FCOLORS. 139 + fileColors = lib.concatStrings [ 140 + "0b" # block device bright yellow 141 + "0b" # char device bright yellow 142 + "0c" # directory bright blue 143 + "0a" # executable bright green 144 + "00" # regular file terminal default 145 + "05" # hard link magenta 146 + "0e" # symlink bright cyan 147 + "08" # file details bright black 148 + "09" # orphaned symlink bright red 149 + "03" # fifo yellow 150 + "0d" # socket bright magenta 151 + "01" # unknown / empty red 152 + ]; 153 + 154 + # 8 contexts as 256-colour indices, with the 8-colour fallback after ';' 155 + # for terminals that never report 256 colour support. 156 + contextColors = "#0c0a0d0e0b090605;42563165"; 157 + in 158 + { 159 + packages.nnn = inputs.wrappers.lib.wrapPackage { 160 + inherit pkgs; 161 + package = nnn; 162 + aliases = [ "n" ]; 163 + runtimeInputs = [ term-caps ]; 164 + env = { 165 + NNN_FCOLORS = fileColors; 166 + NNN_COLORS = contextColors; 167 + # bat ships an "ansi" theme that only emits palette colours 168 + NNN_BATTHEME = "ansi"; 169 + NNN_BATSTYLE = "numbers"; 170 + NNN_OPENER = "${plugins}/libexec/nnn/nuke"; 171 + NNN_PREVIEWIMGPROG = lib.getExe imgpreview; 172 + NNN_PLUG = lib.concatStringsSep ";" [ 173 + "p:${plugins}/libexec/nnn/preview-tui" 174 + "f:${plugins}/libexec/nnn/fzcd" 175 + "o:${plugins}/libexec/nnn/fzopen" 176 + ]; 177 + }; 178 + preHook = '' 179 + # Ask the terminal once, on a tty nothing else is drawing on yet. 180 + NNN_IMGFORMAT=$(nnn-term-caps) 181 + export NNN_IMGFORMAT 182 + 183 + # nuke only reaches for graphical applications when a display exists. 184 + if [ -n "''${WAYLAND_DISPLAY:-}" ] || [ -n "''${DISPLAY:-}" ]; then 185 + export GUI=1 186 + fi 187 + 188 + # preview-tui splits the current pane; with no multiplexer and no 189 + # remote-controllable terminal it falls back to spawning $NNN_TERMINAL 190 + # (xterm), which is useless over ssh. Only auto-open the preview where 191 + # it can actually split. 192 + nnn_extra=() 193 + if [ -n "''${TMUX:-}" ] || [ -n "''${KITTY_LISTEN_ON:-}" ] || 194 + [ -n "''${WEZTERM_PANE:-}" ] || [ -n "''${ZELLIJ:-}" ]; then 195 + nnn_extra+=(-P p) 196 + fi 197 + ''; 198 + # -a: auto NNN_FIFO for the preview plugin, -c: NNN_OPENER is cli-only 199 + wrapper = 200 + { 201 + exePath, 202 + envString, 203 + preHook, 204 + ... 205 + }: 206 + '' 207 + ${envString} 208 + ${preHook} 209 + exec ${exePath} -a -c "''${nnn_extra[@]}" "$@" 210 + ''; 211 + }; 212 + }; 213 + }
+28
tools/nnn/imgpreview.sh
··· 1 + #!/usr/bin/env bash 2 + # Render an image for nnn's preview pane. 3 + # 4 + # preview-tui kills the previewer the instant the selection changes. Being 5 + # killed halfway through a kitty/sixel escape sequence leaves the terminal (and 6 + # tmux, when passthrough is in play) waiting for a terminator and swallowing 7 + # everything that follows, so the pane just goes blank and stays blank. Decode 8 + # into memory first, where a kill costs nothing, then write the finished frame 9 + # out in one go with the signals blocked. 10 + 11 + size=$(stty size </dev/tty 2>/dev/null) || size="24 80" 12 + rows=${size%% *} 13 + cols=${size##* } 14 + 15 + # The format is decided once per nnn session by term-caps; probing here would 16 + # put a terminal round trip inside the very window we are trying to protect. 17 + frame=$( 18 + chafa \ 19 + --probe off \ 20 + --format "${NNN_IMGFORMAT:-symbols}" \ 21 + --passthrough auto \ 22 + --animate off \ 23 + --size "${cols}x$((rows - 1))" \ 24 + -- "$1" 25 + ) || exit 0 26 + 27 + trap '' HUP INT TERM 28 + printf '%s' "$frame"
+72
tools/nnn/term-caps.sh
··· 1 + #!/usr/bin/env bash 2 + # Print the best graphics protocol this terminal speaks: kitty, sixels, iterm 3 + # or symbols. 4 + # 5 + # The terminal is asked directly instead of guessing from the environment, 6 + # because over ssh nothing on the remote host knows what is in front of the 7 + # user. Run this once, before a full-screen program takes over the tty: chafa 8 + # can probe on its own, but doing it per image races with nnn killing the 9 + # previewer mid-sequence. 10 + 11 + emit() { 12 + printf '%s\n' "$1" 13 + exit 0 14 + } 15 + 16 + case "${NNN_IMGFORMAT:-}" in 17 + kitty | sixels | iterm | symbols) emit "$NNN_IMGFORMAT" ;; 18 + esac 19 + 20 + # Terminals that answer no query but identify themselves in the environment. 21 + [ -n "${WEZTERM_PANE:-}" ] && emit iterm 22 + [ "${TERM_PROGRAM:-}" = "iTerm.app" ] && emit iterm 23 + 24 + query() { 25 + local saved 26 + local reply="" 27 + # a controlling terminal we can actually open, not just a device node; 28 + # bash reports a failing redirect before it applies 2>, hence the group 29 + { : <>/dev/tty; } 2>/dev/null || return 1 30 + saved=$(stty -g </dev/tty) || return 1 31 + stty raw -echo </dev/tty || return 1 32 + # Kitty graphics support, then primary device attributes (which reports 33 + # sixel as attribute 4). Inside tmux both are smuggled out to the real 34 + # terminal: tmux would answer DA1 itself, on its own behalf, and that says 35 + # nothing about what can actually be drawn. Getting no answer at all is 36 + # informative too - it means passthrough is disabled. 37 + if [ -n "${TMUX:-}" ]; then 38 + printf '\033Ptmux;\033\033_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\033\033\134\033\134' >/dev/tty 39 + printf '\033Ptmux;\033\033[c\033\134' >/dev/tty 40 + else 41 + printf '\033_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\033\134\033[c' >/dev/tty 42 + fi 43 + # terminals answer in order, so the device attributes reply comes last and 44 + # its trailing "c" ends the exchange; the timeout only bites when nothing 45 + # is listening 46 + IFS= read -r -s -t 1 -d'c' reply </dev/tty || true 47 + stty "$saved" </dev/tty 2>/dev/null || true 48 + printf '%sc' "$reply" 49 + } 50 + 51 + capabilities=$(query || true) 52 + 53 + # No answer to the graphics query inside tmux also means passthrough is off, 54 + # in which case kitty images would arrive as blank placeholder cells. 55 + case $capabilities in 56 + *'_Gi=31;OK'*) emit kitty ;; 57 + esac 58 + 59 + case $capabilities in 60 + *';4;'* | *';4c'* | *'?4;'* | *'?4c'*) 61 + # tmux advertises attribute 4 whenever it was built with sixel support, 62 + # regardless of what the attached client can actually draw 63 + if [ -z "${TMUX:-}" ]; then 64 + emit sixels 65 + fi 66 + case $(tmux display -p '#{client_termfeatures}' 2>/dev/null || true) in 67 + *sixel*) emit sixels ;; 68 + esac 69 + ;; 70 + esac 71 + 72 + emit symbols