commit 0d80e3262c9f6b0ed4e5d7d1659358576de413df
parent d9c39bdde4535d8be00dcc575ba0238ec9f23864
Author: DAEMON <zer0sec.xp@icloud.com>
Date: Thu, 8 Oct 2026 04:26:01 +0100
added new tools
Diffstat:
15 files changed, 1967 insertions(+), 18 deletions(-)
diff --git a/README.md b/README.md
@@ -73,12 +73,15 @@ NixDaemon/
│ ├── modules/ssh.nix the ssh key (private half from sops) and ~/.ssh/config
│ ├── modules/gpg.nix the GPG main key: public in home/gpg/, secret from sops, gpg.conf, trust
│ ├── modules/git.nix git identity, signing with the main key, aliases, delta
+│ ├── modules/media.nix mpd user service, rmpc (full config, Rosé Pine, lyrics), mpv (gpu-next, uosc, thumbfast)
+│ ├── modules/yazi.nix yazi: inline image/video/pdf previews, openers, Rosé Pine, git + full-border plugins
│ ├── modules/gtk.nix Yaru-purple icons, cursor, prefer-dark
│ ├── hypr/*.lua omarchy (shim) · core · looknfeel · defaults · bindings · lid · caelestia
│ ├── kitty/scrollback.lua kitty copy mode as a Neovim buffer
│ ├── caelestia/ scheme.json (dark) · scheme-dawn.json · rose-pine-{dark,dawn}.txt · shell-tokens.json
│ ├── cheats/*.md the cards: nix.md, gpg.md
-│ └── gpg/daemon-main.pub.asc the main key's public half
+│ ├── gpg/daemon-main.pub.asc the main key's public half
+│ ├── rmpc/, mpv/shaders/, yazi/flavors/ the carried rmpc config and theme, the CfL shader, the Rosé Pine flavor
└── modules/workstation.nix Claude Code, Claude desktop, Obsidian, gh, glab
```
diff --git a/home/default.nix b/home/default.nix
@@ -16,6 +16,8 @@
./modules/ssh.nix # the ssh key (sops) and ~/.ssh/config
./modules/gpg.nix # the GPG main key (public in-repo, secret via sops) and gpg.conf
./modules/git.nix # git identity, signing with the main key, delta
+ ./modules/media.nix # mpd + rmpc, mpv
+ ./modules/yazi.nix # yazi with previews, Rosé Pine, plugins
./modules/gtk.nix # Yaru-purple icons, cursor, prefer-dark
];
diff --git a/home/modules/dotfiles.nix b/home/modules/dotfiles.nix
@@ -13,6 +13,7 @@
# .config/nvim the AstroNvim tree; Neovim is nvf now (home/modules/neovim.nix)
# .config/starship.toml, .config/fastfetch the Omarchy-era prompt and fetch; both are
# Nix-managed now (home/modules/prompt.nix)
+# .config/mpd, rmpc, mpv, yazi Nix-managed (home/modules/media.nix, yazi.nix), carried from the checkout
# .config/omarchy*, Omacom, hyprmoncfg Omarchy's own trees; caelestia.nix reads the
# wallpaper directory straight from the checkout
# .config/systemd/user Omarchy-era units; caelestia-shell.service there would
@@ -80,7 +81,6 @@ in
"mise"
# tools
"btop"
- "yazi"
"lazygit"
"jj"
"emacs"
@@ -93,9 +93,6 @@ in
"bg-pasticcio"
"libvirt"
# media
- "mpd"
- "rmpc"
- "mpv"
"cava"
"imv"
"zathura"
diff --git a/home/modules/fan.nix b/home/modules/fan.nix
@@ -16,7 +16,10 @@
# Reads need no root at all: k10temp and the uniwill hwmon are world-readable.
{ pkgs, lib, ... }:
let
- bin = lib.makeBinPath [ pkgs.coreutils pkgs.gawk pkgs.gnugrep pkgs.gnused pkgs.systemd pkgs.libnotify pkgs.sudo ];
+ bin = lib.makeBinPath [ pkgs.coreutils pkgs.gawk pkgs.gnugrep pkgs.gnused pkgs.systemd pkgs.libnotify ];
+ # NixOS's setuid sudo lives in /run/wrappers; the store copy is not setuid
+ # and refuses to run ("must be owned by uid 0 and have the setuid bit set").
+ sudo = "/run/wrappers/bin/sudo";
# shared read-only sampler, sourced by both scripts
lib-sh = pkgs.writeText "fan-lib.sh" ''
@@ -45,7 +48,7 @@ let
clamped() { # 1 when busy yet no core above TRIP_MHZ
local b=$1 mx=$2; [ "$b" -ge "$BUSY_MIN" ] && [ "$mx" -gt 0 ] && [ "$mx" -lt "$TRIP_MHZ" ] && echo 1 || echo 0
}
- ec_mode() { sudo -n /run/current-system/sw/bin/fan-ec mode 2>/dev/null || echo "?"; }
+ ec_mode() { ${sudo} -n /run/current-system/sw/bin/fan-ec mode 2>/dev/null || echo "?"; }
status_line() {
local b mx avg; b=$(busy); read -r avg mx <<< "$(clocks)"
local cl; cl=$(clamped "$b" "$mx")
@@ -66,7 +69,7 @@ let
while :; do
b=$(busy); read -r _ mx <<< "$(clocks)"
if [ "$(clamped "$b" "$mx")" = 1 ]; then
- out=$(sudo -n /run/current-system/sw/bin/fan-ec auto 2>&1)
+ out=$(${sudo} -n /run/current-system/sw/bin/fan-ec auto 2>&1)
log "CLAMP: load $b %, max core $mx MHz -> fan-ec auto: $out"
notify-send -a fan -u critical "Fans back to auto" "EC clamp hit (load $b %, cores at $mx MHz). Manual fan mode released."
exit 0
@@ -90,15 +93,20 @@ let
}
disarm() { systemctl --user stop "$UNIT" 2>/dev/null || true; }
+ if [ "$(id -u)" = 0 ]; then
+ echo "fan: run this as yourself, not under sudo (it needs your user session for the watchdog; root access is handled inside)" >&2
+ exit 1
+ fi
+
case "''${1:-}" in
""|status) status_line ;;
watch)
iv=''${2:-2}; busy >/dev/null; sleep "$iv"
while :; do status_line; sleep "$iv"; done ;;
- max) sudo -n "$EC" max && arm max ;;
- auto) disarm; sudo -n "$EC" auto ;;
- [0-9]*) p=''${1%\%}; sudo -n "$EC" "$p" && arm "$p %" ;;
- ec) sudo -n "$EC" status ;;
+ max) ${sudo} -n "$EC" max && arm max ;;
+ auto) disarm; ${sudo} -n "$EC" auto ;;
+ [0-9]*) p=''${1%\%}; ${sudo} -n "$EC" "$p" && arm "$p %" ;;
+ ec) ${sudo} -n "$EC" status ;;
log) [ -r "$LOG" ] && tail -n ''${2:-20} "$LOG" || echo "no watchdog log yet" ;;
-h|--help|help)
echo "usage: fan [status] | watch [sec] | max | <30-100> | auto | ec | log [n]"
diff --git a/home/modules/media.nix b/home/modules/media.nix
@@ -0,0 +1,115 @@
+# home/modules/media.nix — music and video: mpd + rmpc, and mpv.
+#
+# mpd runs as a user service (starts at login), library ~/Music (the layout
+# contract is ~/Music/AGENTS.md), state in ~/.local/share/mpd, PipeWire
+# output plus the FIFO rmpc's visualiser reads. It listens on the socket
+# $XDG_RUNTIME_DIR/mpd/socket and on 127.0.0.1:6600.
+#
+# rmpc: the dotfiles' full config (home/rmpc/config.ron: tabs with album art,
+# lyrics and cava panes, vim keys, 1-0 tab switching), the Rosé Pine theme
+# (home/rmpc/themes/rose-pine.ron) and the LRCLIB lyrics fetcher that runs on
+# song change. The Discord presence script was not carried (1.4k lines of
+# Python with its own deps; say so if wanted).
+#
+# mpv: the dotfiles' gpu-next/Vulkan profile with the CfL chroma shader, plus
+# uosc (the on-screen UI), thumbfast (seek thumbnails) and mpris (media keys,
+# Caelestia's player widget).
+{ config, pkgs, ... }:
+let
+ rt = "/run/user/1000";
+in
+{
+ services.mpd = {
+ enable = true;
+ musicDirectory = "${config.home.homeDirectory}/Music";
+ playlistDirectory = "${config.xdg.dataHome}/mpd/playlists";
+ network = {
+ listenAddress = "${rt}/mpd/socket";
+ port = 6600;
+ startWhenNeeded = false;
+ };
+ extraConfig = ''
+ bind_to_address "127.0.0.1"
+ auto_update "yes"
+ restore_paused "yes"
+ audio_output {
+ type "pipewire"
+ name "PipeWire"
+ }
+ audio_output {
+ type "fifo"
+ name "Visualizer FIFO"
+ path "${rt}/mpd/fifo"
+ format "44100:16:2"
+ }
+ '';
+ };
+ # the socket's directory, created by systemd before mpd starts
+ systemd.user.services.mpd.Service.RuntimeDirectory = "mpd";
+
+ programs.rmpc = {
+ enable = true;
+ config = builtins.readFile ../rmpc/config.ron;
+ };
+ xdg.configFile = {
+ "rmpc/themes/rose-pine.ron".source = ../rmpc/themes/rose-pine.ron;
+ "rmpc/scripts/fetch-lyrics" = {
+ source = ../rmpc/scripts/fetch-lyrics;
+ executable = true;
+ };
+ "mpv/shaders".source = ../mpv/shaders;
+ };
+
+ programs.mpv = {
+ enable = true;
+ scripts = with pkgs.mpvScripts; [ uosc thumbfast mpris ];
+ config = {
+ vo = "gpu-next";
+ gpu-api = "vulkan";
+ gpu-context = "waylandvk";
+ profile = "high-quality";
+ hwdec = "auto";
+ scale = "ewa_lanczos4sharpest";
+ glsl-shader = "~~/shaders/CfL_Prediction.glsl";
+ cscale = "ewa_lanczossharp";
+ cscale-antiring = 0.65;
+ dscale = "mitchell";
+ correct-downscaling = true;
+ linear-downscaling = true;
+ sigmoid-upscaling = true;
+ deband = true;
+ deband-iterations = 2;
+ deband-threshold = 32;
+ deband-range = 12;
+ deband-grain = 16;
+ dither = "error-diffusion";
+ error-diffusion = "burkes";
+ dither-depth = 8;
+ temporal-dither = false;
+ video-sync = "display-resample";
+ interpolation = true;
+ tscale = "oversample";
+ target-colorspace-hint = "auto";
+ target-colorspace-hint-mode = "target";
+ target-prim = "bt.709";
+ target-trc = "srgb";
+ gamut-mapping-mode = "perceptual";
+ tone-mapping = "auto";
+ hdr-compute-peak = true;
+ contrast = 4;
+ saturation = 5;
+ screenshot-format = "png";
+ screenshot-high-bit-depth = true;
+ screenshot-tag-colorspace = true;
+ screenshot-directory = "~/Pictures/mpv";
+ osc = false; # uosc replaces it
+ border = false;
+ save-position-on-quit = true;
+ keep-open = true;
+ sub-auto = "fuzzy";
+ slang = "en,eng";
+ alang = "ja,jpn,en,eng";
+ ytdl-format = "bestvideo[height<=?1440]+bestaudio/best";
+ };
+ };
+}
diff --git a/home/modules/sops.nix b/home/modules/sops.nix
@@ -12,7 +12,7 @@
# secretspec (home/modules/shell.nix) is the complement: per-project runtime
# secrets pulled from the keyring at `secretspec run`, declared next to the
# project in secretspec.toml, not in this repo.
-{ config, inputs, pkgs, ... }:
+{ config, inputs, pkgs, lib, ... }:
{
imports = [ inputs.sops-nix.homeManagerModules.sops ];
@@ -28,4 +28,16 @@
age
ssh-to-age # turn an ssh-ed25519 key into an age recipient when adding another machine
];
+
+ # sops-nix's activation step restarts the sops-nix user unit. That unit is a
+ # home-manager file (~/.config/systemd/user/sops-nix.service), linked by the
+ # linkGeneration step, and the user systemd manager only sees new unit
+ # files after a daemon-reload, which home-manager runs at the very end. With
+ # the extra steps this config adds, the DAG happened to order sops-nix
+ # before linkGeneration, so the first switch died with "Unit
+ # sops-nix.service not found". This entry pins the order: linkGeneration →
+ # daemon-reload → sops-nix.
+ home.activation.reloadUserUnitsForSops = lib.hm.dag.entryBetween [ "sops-nix" ] [ "linkGeneration" "installPackages" ] ''
+ ${pkgs.systemd}/bin/systemctl --user daemon-reload 2>/dev/null || true
+ '';
}
diff --git a/home/modules/tools.nix b/home/modules/tools.nix
@@ -60,10 +60,6 @@ in
yt-dlp
atomicparsley
gallery-dl
- mpd
- mpc
- rmpc
- mpv
imagemagick
img2pdf
resvg
@@ -91,7 +87,6 @@ in
uv # PEP-723 scripts in ~/.local/bin (dcx, dorkforge); managed Pythons work via nix-ld
nodejs
btop
- yazi
lazygit
lazydocker
tealdeer # `tldr`
diff --git a/home/modules/yazi.nix b/home/modules/yazi.nix
@@ -0,0 +1,130 @@
+# home/modules/yazi.nix — yazi, the terminal file manager, with previews.
+#
+# Pictures preview inline in kitty (its graphics protocol; yazi draws them
+# itself, nothing else needed), video frames via ffmpeg, PDFs via poppler,
+# SVG via resvg, archives via 7z, JSON via jq, code with syntax colours, and
+# the rest as text. Enter / l on a file opens it: images in imv, video and
+# audio in mpv, PDFs and books in zathura, text in $EDITOR, anything else via
+# xdg-open; o shows every opener. The toolbox's zimg/zbook/comx/gamex/dux
+# are offered where they apply (they live in ~/.local/bin).
+#
+# y start yazi and cd to where you left it (zsh wrapper)
+# T maximise the preview pane (toggle-pane) ! shell here
+# <C-e>/<C-y> scroll the preview . toggle hidden
+# l / Enter smart-enter: open a file, enter a directory
+# Theme: Rosé Pine (home/yazi/flavors). Git status marks via the git plugin.
+{ pkgs, ... }:
+{
+ programs.yazi = {
+ enable = true;
+ enableZshIntegration = true;
+ shellWrapperName = "y";
+ extraPackages = with pkgs; [
+ ffmpeg
+ poppler-utils
+ imagemagick
+ resvg
+ p7zip
+ jq
+ fd
+ ripgrep
+ fzf
+ zoxide
+ file
+ mediainfo
+ imv
+ ];
+
+ settings = {
+ mgr = {
+ show_hidden = false;
+ show_symlink = true;
+ sort_by = "natural";
+ sort_sensitive = false;
+ sort_dir_first = true;
+ linemode = "size";
+ scrolloff = 5;
+ };
+ preview = {
+ max_width = 1600;
+ max_height = 1600;
+ image_delay = 20;
+ image_filter = "lanczos3";
+ image_quality = 90;
+ wrap = "yes";
+ };
+ opener = {
+ edit = [ { run = ''${EDITOR:-nvim} "$@"''; desc = "Edit"; block = true; for = "unix"; } ];
+ open = [ { run = ''xdg-open "$1"''; desc = "Open (system default)"; orphan = true; for = "unix"; } ];
+ view-image = [
+ { run = ''imv "$@"''; desc = "View (imv)"; orphan = true; for = "unix"; }
+ { run = ''zimg "$1"''; desc = "View in zathura (zimg)"; orphan = true; for = "unix"; }
+ { run = ''xdg-open "$1"''; desc = "Open (system default)"; orphan = true; for = "unix"; }
+ ];
+ play = [
+ { run = ''mpv "$@"''; desc = "Play (mpv)"; orphan = true; for = "unix"; }
+ { run = ''mpv --no-video "$@"''; desc = "Play audio only (mpv)"; block = true; for = "unix"; }
+ ];
+ open-pdf = [
+ { run = ''zathura "$@"''; desc = "Open (zathura)"; orphan = true; for = "unix"; }
+ { run = ''xdg-open "$1"''; desc = "Open (system default)"; orphan = true; for = "unix"; }
+ ];
+ open-book = [
+ { run = ''zbook "$1"''; desc = "Read in zathura (zbook)"; orphan = true; for = "unix"; }
+ { run = ''zathura "$@"''; desc = "Open (zathura)"; orphan = true; for = "unix"; }
+ ];
+ open-comic = [
+ { run = ''comx "$1"''; desc = "Guided view (comx)"; orphan = true; for = "unix"; }
+ { run = ''zbook "$1"''; desc = "Panels in zathura (zbook)"; orphan = true; for = "unix"; }
+ { run = ''7z x -y "$1"''; desc = "Extract here (7z)"; block = true; for = "unix"; }
+ ];
+ extract = [ { run = ''7z x -y "$1"''; desc = "Extract here (7z)"; for = "unix"; } ];
+ run-exe = [
+ { run = ''gamex run "$1"''; desc = "Run (gamex, dGPU)"; block = true; for = "unix"; }
+ { run = ''gamex proton "$1"''; desc = "Run with Proton-GE"; block = true; for = "unix"; }
+ ];
+ };
+ open = {
+ rules = [
+ { mime = "image/gif"; use = [ "view-image" "open" ]; }
+ { mime = "image/*"; use = [ "view-image" "open" ]; }
+ { mime = "video/*"; use = [ "play" "open" ]; }
+ { mime = "audio/*"; use = [ "play" "open" ]; }
+ { mime = "application/pdf"; use = [ "open-pdf" "open" ]; }
+ { mime = "application/epub+zip"; use = [ "open-book" "open" ]; }
+ { name = "*.cbz"; use = [ "open-comic" "extract" ]; }
+ { name = "*.cbr"; use = [ "open-comic" "extract" ]; }
+ { name = "*.exe"; use = [ "run-exe" "open" ]; }
+ { mime = "application/{zip,gzip,x-tar,x-bzip*,x-7z-compressed,x-rar,x-xz,zstd}"; use = [ "extract" "open" ]; }
+ { mime = "inode/directory"; use = [ "edit" "open" ]; }
+ { mime = "text/*"; use = [ "edit" "open" ]; }
+ { mime = "application/{json,toml,x-ndjson,javascript,x-sh,x-shellscript,xml}"; use = [ "edit" "open" ]; }
+ { mime = "*"; use = [ "open" "edit" ]; }
+ ];
+ };
+ };
+
+ keymap.mgr.prepend_keymap = [
+ { on = [ "!" ]; run = ''shell "$SHELL" --block''; desc = "Open a shell here"; }
+ { on = [ "<C-e>" ]; run = "seek 5"; desc = "Scroll preview down"; }
+ { on = [ "<C-y>" ]; run = "seek -5"; desc = "Scroll preview up"; }
+ { on = [ "l" ]; run = "plugin smart-enter"; desc = "Enter directory or open file"; }
+ { on = [ "<Enter>" ]; run = "plugin smart-enter"; desc = "Enter directory or open file"; }
+ { on = [ "T" ]; run = "plugin toggle-pane max-preview"; desc = "Maximise the preview"; }
+ { on = [ "u" "d" ]; run = "shell -- dux %h %s"; desc = "Copy file(s) to the clipboard as a paste-able file"; }
+ ];
+
+ plugins = with pkgs.yaziPlugins; {
+ inherit full-border git smart-enter toggle-pane;
+ };
+ flavors.rose-pine = ../yazi/flavors/rose-pine.yazi;
+ theme.flavor = {
+ dark = "rose-pine";
+ light = "rose-pine";
+ };
+ initLua = ''
+ require("full-border"):setup()
+ require("git"):setup()
+ '';
+ };
+}
diff --git a/home/mpv/shaders/CfL_Prediction.LICENSE b/home/mpv/shaders/CfL_Prediction.LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 João Chrisóstomo
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/home/mpv/shaders/CfL_Prediction.glsl b/home/mpv/shaders/CfL_Prediction.glsl
@@ -0,0 +1,363 @@
+// MIT License
+
+// Copyright (c) 2023 João Chrisóstomo
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+//!PARAM chroma_offset_x
+//!TYPE float
+0.0
+
+//!PARAM chroma_offset_y
+//!TYPE float
+0.0
+
+//!HOOK CHROMA
+//!BIND LUMA
+//!BIND CHROMA
+//!SAVE LUMA_LR
+//!WIDTH CHROMA.w
+//!HEIGHT LUMA.h
+//!WHEN CHROMA.w LUMA.w <
+//!DESC Chroma From Luma Prediction (Hermite 1st step, Downscaling Luma)
+
+float comp_wd(vec2 v) {
+ float x = min(length(v), 1.0);
+ return smoothstep(0.0, 1.0, 1.0 - x);
+}
+
+vec4 hook() {
+ vec2 luma_pos = LUMA_pos;
+ luma_pos.x += chroma_offset_x / LUMA_size.x;
+ float start = ceil((luma_pos.x - (1.0 / CHROMA_size.x)) * LUMA_size.x - 0.5);
+ float end = floor((luma_pos.x + (1.0 / CHROMA_size.x)) * LUMA_size.x - 0.5);
+
+ float wt = 0.0;
+ float luma_sum = 0.0;
+ vec2 pos = luma_pos;
+
+ for (float dx = start.x; dx <= end.x; dx++) {
+ pos.x = LUMA_pt.x * (dx + 0.5);
+ vec2 dist = (pos - luma_pos) * CHROMA_size;
+ float wd = comp_wd(dist);
+ float luma_pix = LUMA_tex(pos).x;
+ luma_sum += wd * luma_pix;
+ wt += wd;
+ }
+
+ vec4 output_pix = vec4(luma_sum /= wt, 0.0, 0.0, 1.0);
+ return clamp(output_pix, 0.0, 1.0);
+}
+
+//!HOOK CHROMA
+//!BIND LUMA_LR
+//!BIND CHROMA
+//!BIND LUMA
+//!SAVE LUMA_LR
+//!WIDTH CHROMA.w
+//!HEIGHT CHROMA.h
+//!WHEN CHROMA.w LUMA.w <
+//!DESC Chroma From Luma Prediction (Hermite 2nd step, Downscaling Luma)
+
+float comp_wd(vec2 v) {
+ float x = min(length(v), 1.0);
+ return smoothstep(0.0, 1.0, 1.0 - x);
+}
+
+vec4 hook() {
+ vec2 luma_pos = LUMA_LR_pos;
+ luma_pos.y += chroma_offset_y / LUMA_LR_size.y;
+ float start = ceil((luma_pos.y - (1.0 / CHROMA_size.y)) * LUMA_LR_size.y - 0.5);
+ float end = floor((luma_pos.y + (1.0 / CHROMA_size.y)) * LUMA_LR_size.y - 0.5);
+
+ float wt = 0.0;
+ float luma_sum = 0.0;
+ vec2 pos = luma_pos;
+
+ for (float dy = start; dy <= end; dy++) {
+ pos.y = LUMA_LR_pt.y * (dy + 0.5);
+ vec2 dist = (pos - luma_pos) * CHROMA_size;
+ float wd = comp_wd(dist);
+ float luma_pix = LUMA_LR_tex(pos).x;
+ luma_sum += wd * luma_pix;
+ wt += wd;
+ }
+
+ vec4 output_pix = vec4(luma_sum /= wt, 0.0, 0.0, 1.0);
+ return clamp(output_pix, 0.0, 1.0);
+}
+
+//!HOOK CHROMA
+//!BIND HOOKED
+//!BIND LUMA
+//!BIND LUMA_LR
+//!WHEN CHROMA.w LUMA.w <
+//!WIDTH LUMA.w
+//!HEIGHT LUMA.h
+//!OFFSET ALIGN
+//!DESC Chroma From Luma Prediction (Upscaling Chroma)
+
+#define USE_12_TAP_REGRESSION 1
+#define USE_8_TAP_REGRESSIONS 1
+#define DEBUG 0
+
+float comp_wd(vec2 v) {
+ float d = min(length(v), 2.0);
+ float d2 = d * d;
+ float d3 = d2 * d;
+
+ if (d < 1.0) {
+ return 1.25 * d3 - 2.25 * d2 + 1.0;
+ } else {
+ return -0.75 * d3 + 3.75 * d2 - 6.0 * d + 3.0;
+ }
+}
+
+vec4 hook() {
+ float ar_strength = 0.8;
+ vec2 mix_coeff = vec2(0.8);
+ vec2 corr_exponent = vec2(4.0);
+
+ vec4 output_pix = vec4(0.0, 0.0, 0.0, 1.0);
+ float luma_zero = LUMA_texOff(0.0).x;
+
+ vec2 pp = HOOKED_pos * HOOKED_size - vec2(0.5);
+ vec2 fp = floor(pp);
+ pp -= fp;
+
+#ifdef HOOKED_gather
+ vec2 quad_idx[4] = {{0.0, 0.0}, {2.0, 0.0}, {0.0, 2.0}, {2.0, 2.0}};
+
+ vec4 luma_quads[4];
+ vec4 chroma_quads[4][2];
+
+ for (int i = 0; i < 4; i++) {
+ luma_quads[i] = LUMA_LR_gather(vec2((fp + quad_idx[i]) * HOOKED_pt), 0);
+ chroma_quads[i][0] = HOOKED_gather(vec2((fp + quad_idx[i]) * HOOKED_pt), 0);
+ chroma_quads[i][1] = HOOKED_gather(vec2((fp + quad_idx[i]) * HOOKED_pt), 1);
+ }
+
+ vec2 chroma_pixels[16];
+ chroma_pixels[0] = vec2(chroma_quads[0][0].w, chroma_quads[0][1].w);
+ chroma_pixels[1] = vec2(chroma_quads[0][0].z, chroma_quads[0][1].z);
+ chroma_pixels[2] = vec2(chroma_quads[1][0].w, chroma_quads[1][1].w);
+ chroma_pixels[3] = vec2(chroma_quads[1][0].z, chroma_quads[1][1].z);
+ chroma_pixels[4] = vec2(chroma_quads[0][0].x, chroma_quads[0][1].x);
+ chroma_pixels[5] = vec2(chroma_quads[0][0].y, chroma_quads[0][1].y);
+ chroma_pixels[6] = vec2(chroma_quads[1][0].x, chroma_quads[1][1].x);
+ chroma_pixels[7] = vec2(chroma_quads[1][0].y, chroma_quads[1][1].y);
+ chroma_pixels[8] = vec2(chroma_quads[2][0].w, chroma_quads[2][1].w);
+ chroma_pixels[9] = vec2(chroma_quads[2][0].z, chroma_quads[2][1].z);
+ chroma_pixels[10] = vec2(chroma_quads[3][0].w, chroma_quads[3][1].w);
+ chroma_pixels[11] = vec2(chroma_quads[3][0].z, chroma_quads[3][1].z);
+ chroma_pixels[12] = vec2(chroma_quads[2][0].x, chroma_quads[2][1].x);
+ chroma_pixels[13] = vec2(chroma_quads[2][0].y, chroma_quads[2][1].y);
+ chroma_pixels[14] = vec2(chroma_quads[3][0].x, chroma_quads[3][1].x);
+ chroma_pixels[15] = vec2(chroma_quads[3][0].y, chroma_quads[3][1].y);
+
+ float luma_pixels[16];
+ luma_pixels[0] = luma_quads[0].w;
+ luma_pixels[1] = luma_quads[0].z;
+ luma_pixels[2] = luma_quads[1].w;
+ luma_pixels[3] = luma_quads[1].z;
+ luma_pixels[4] = luma_quads[0].x;
+ luma_pixels[5] = luma_quads[0].y;
+ luma_pixels[6] = luma_quads[1].x;
+ luma_pixels[7] = luma_quads[1].y;
+ luma_pixels[8] = luma_quads[2].w;
+ luma_pixels[9] = luma_quads[2].z;
+ luma_pixels[10] = luma_quads[3].w;
+ luma_pixels[11] = luma_quads[3].z;
+ luma_pixels[12] = luma_quads[2].x;
+ luma_pixels[13] = luma_quads[2].y;
+ luma_pixels[14] = luma_quads[3].x;
+ luma_pixels[15] = luma_quads[3].y;
+#else
+ vec2 pix_idx[16] = {{-0.5,-0.5}, {0.5,-0.5}, {1.5,-0.5}, {2.5,-0.5},
+ {-0.5, 0.5}, {0.5, 0.5}, {1.5, 0.5}, {2.5, 0.5},
+ {-0.5, 1.5}, {0.5, 1.5}, {1.5, 1.5}, {2.5, 1.5},
+ {-0.5, 2.5}, {0.5, 2.5}, {1.5, 2.5}, {2.5, 2.5}};
+
+ float luma_pixels[16];
+ vec2 chroma_pixels[16];
+
+ for (int i = 0; i < 16; i++) {
+ luma_pixels[i] = LUMA_LR_tex(vec2((fp + pix_idx[i]) * HOOKED_pt)).x;
+ chroma_pixels[i] = HOOKED_tex(vec2((fp + pix_idx[i]) * HOOKED_pt)).xy;
+ }
+#endif
+
+#if (DEBUG == 1)
+ vec2 chroma_spatial = vec2(0.5);
+ mix_coeff = vec2(1.0);
+#else
+ float wd[16];
+ float wt = 0.0;
+ vec2 ct = vec2(0.0);
+
+ vec2 chroma_min = min(min(min(chroma_pixels[5], chroma_pixels[6]), chroma_pixels[9]), chroma_pixels[10]);
+ vec2 chroma_max = max(max(max(chroma_pixels[5], chroma_pixels[6]), chroma_pixels[9]), chroma_pixels[10]);
+
+ const int dx[16] = {-1, 0, 1, 2, -1, 0, 1, 2, -1, 0, 1, 2, -1, 0, 1, 2};
+ const int dy[16] = {-1, -1, -1, -1, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2};
+
+ for (int i = 0; i < 16; i++) {
+ wd[i] = comp_wd(vec2(dx[i], dy[i]) - pp);
+ wt += wd[i];
+ ct += wd[i] * chroma_pixels[i];
+ }
+
+ vec2 chroma_spatial = ct / wt;
+ chroma_spatial = clamp(mix(chroma_spatial, clamp(chroma_spatial, chroma_min, chroma_max), ar_strength), 0.0, 1.0);
+#endif
+
+#if (USE_12_TAP_REGRESSION == 1 || USE_8_TAP_REGRESSIONS == 1)
+ const int i12[12] = {1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14};
+ const int i4y[4] = {1, 2, 13, 14};
+ const int i4x[4] = {4, 7, 8, 11};
+ const int i4[4] = {5, 6, 9, 10};
+
+ float luma_sum_4 = 0.0;
+ float luma_sum_4y = 0.0;
+ float luma_sum_4x = 0.0;
+ vec2 chroma_sum_4 = vec2(0.0);
+ vec2 chroma_sum_4y = vec2(0.0);
+ vec2 chroma_sum_4x = vec2(0.0);
+
+ for (int i = 0; i < 4; i++) {
+ luma_sum_4 += luma_pixels[i4[i]];
+ luma_sum_4y += luma_pixels[i4y[i]];
+ luma_sum_4x += luma_pixels[i4x[i]];
+ chroma_sum_4 += chroma_pixels[i4[i]];
+ chroma_sum_4y += chroma_pixels[i4y[i]];
+ chroma_sum_4x += chroma_pixels[i4x[i]];
+ }
+
+ float luma_avg_12 = (luma_sum_4 + luma_sum_4y + luma_sum_4x) / 12.0;
+ float luma_min_12 = luma_pixels[i12[0]];
+ float luma_max_12 = luma_pixels[i12[0]];
+ float luma_var_12 = 0.0;
+ vec2 chroma_avg_12 = (chroma_sum_4 + chroma_sum_4y + chroma_sum_4x) / 12.0;
+ vec2 chroma_var_12 = vec2(0.0);
+ vec2 luma_chroma_cov_12 = vec2(0.0);
+
+ for (int i = 0; i < 12; i++) {
+ luma_min_12 = min(luma_min_12, luma_pixels[i12[i]]);
+ luma_max_12 = max(luma_max_12, luma_pixels[i12[i]]);
+ luma_var_12 += pow(luma_pixels[i12[i]] - luma_avg_12, 2.0);
+ chroma_var_12 += pow(chroma_pixels[i12[i]] - chroma_avg_12, vec2(2.0));
+ luma_chroma_cov_12 += (luma_pixels[i12[i]] - luma_avg_12) * (chroma_pixels[i12[i]] - chroma_avg_12);
+ }
+
+ vec2 corr = clamp(abs(luma_chroma_cov_12 / max(sqrt(luma_var_12 * chroma_var_12), 1e-6)), 0.0, 1.0);
+ mix_coeff = pow(corr, corr_exponent) * mix_coeff;
+
+ float luma_range_12 = max(luma_max_12 - luma_min_12, 1e-6);
+ float extrapolation = max(max(luma_zero - luma_max_12, luma_min_12 - luma_zero), 0.0) / luma_range_12;
+ mix_coeff *= clamp((8.0 - extrapolation) / 4.0, 0.0, 1.0);
+#endif
+
+#if (USE_12_TAP_REGRESSION == 1)
+ vec2 alpha_12 = luma_chroma_cov_12 / max(luma_var_12, 1e-6);
+ vec2 beta_12 = chroma_avg_12 - alpha_12 * luma_avg_12;
+ vec2 chroma_pred_12 = clamp(alpha_12 * luma_zero + beta_12, 0.0, 1.0);
+#endif
+
+#if (USE_8_TAP_REGRESSIONS == 1)
+ const int i8y[8] = {1, 2, 5, 6, 9, 10, 13, 14};
+ const int i8x[8] = {4, 5, 6, 7, 8, 9, 10, 11};
+
+ float luma_avg_8y = (luma_sum_4 + luma_sum_4y) / 8.0;
+ float luma_avg_8x = (luma_sum_4 + luma_sum_4x) / 8.0;
+ float luma_var_8y = 0.0;
+ float luma_var_8x = 0.0;
+ vec2 chroma_avg_8y = (chroma_sum_4 + chroma_sum_4y) / 8.0;
+ vec2 chroma_avg_8x = (chroma_sum_4 + chroma_sum_4x) / 8.0;
+ vec2 luma_chroma_cov_8y = vec2(0.0);
+ vec2 luma_chroma_cov_8x = vec2(0.0);
+
+ for (int i = 0; i < 8; i++) {
+ luma_var_8y += pow(luma_pixels[i8y[i]] - luma_avg_8y, 2.0);
+ luma_var_8x += pow(luma_pixels[i8x[i]] - luma_avg_8x, 2.0);
+ luma_chroma_cov_8y += (luma_pixels[i8y[i]] - luma_avg_8y) * (chroma_pixels[i8y[i]] - chroma_avg_8y);
+ luma_chroma_cov_8x += (luma_pixels[i8x[i]] - luma_avg_8x) * (chroma_pixels[i8x[i]] - chroma_avg_8x);
+ }
+
+ vec2 alpha_8y = luma_chroma_cov_8y / max(luma_var_8y, 1e-6);
+ vec2 alpha_8x = luma_chroma_cov_8x / max(luma_var_8x, 1e-6);
+ vec2 beta_8y = chroma_avg_8y - alpha_8y * luma_avg_8y;
+ vec2 beta_8x = chroma_avg_8x - alpha_8x * luma_avg_8x;
+ vec2 chroma_pred_8y = clamp(alpha_8y * luma_zero + beta_8y, 0.0, 1.0);
+ vec2 chroma_pred_8x = clamp(alpha_8x * luma_zero + beta_8x, 0.0, 1.0);
+ vec2 chroma_pred_8 = mix(chroma_pred_8y, chroma_pred_8x, 0.5);
+#endif
+
+#if (USE_12_TAP_REGRESSION == 1 && USE_8_TAP_REGRESSIONS == 1)
+ output_pix.xy = mix(chroma_spatial, mix(chroma_pred_12, chroma_pred_8, 0.5), mix_coeff);
+#elif (USE_12_TAP_REGRESSION == 1 && USE_8_TAP_REGRESSIONS == 0)
+ output_pix.xy = mix(chroma_spatial, chroma_pred_12, mix_coeff);
+#elif (USE_12_TAP_REGRESSION == 0 && USE_8_TAP_REGRESSIONS == 1)
+ output_pix.xy = mix(chroma_spatial, chroma_pred_8, mix_coeff);
+#else
+ output_pix.xy = chroma_spatial;
+#endif
+
+ output_pix.xy = clamp(output_pix.xy, 0.0, 1.0);
+ return output_pix;
+}
+
+//!PARAM distance_coeff
+//!TYPE float
+//!MINIMUM 0.0
+2.0
+
+//!PARAM intensity_coeff
+//!TYPE float
+//!MINIMUM 0.0
+128.0
+
+//!HOOK CHROMA
+//!BIND CHROMA
+//!BIND LUMA
+//!BIND LUMA_LR
+//!DESC Chroma From Luma Prediction (Smoothing Chroma)
+
+float comp_w(vec2 spatial_distance, float intensity_distance) {
+ return max(100.0 * exp(-distance_coeff * pow(length(spatial_distance), 2.0) - intensity_coeff * pow(intensity_distance, 2.0)), 1e-32);
+}
+
+vec4 hook() {
+ vec4 output_pix = vec4(0.0, 0.0, 0.0, 1.0);
+ float luma_zero = LUMA_texOff(0).x;
+ float wt = 0.0;
+ vec2 ct = vec2(0.0);
+
+ for (int i = -1; i < 2; i++) {
+ for (int j = -1; j < 2; j++) {
+ vec2 chroma_pixels = CHROMA_texOff(vec2(i, j)).xy;
+ float luma_pixels = LUMA_texOff(vec2(i, j)).x;
+ float w = comp_w(vec2(i, j), luma_zero - luma_pixels);
+ wt += w;
+ ct += w * chroma_pixels;
+ }
+ }
+
+ output_pix.xy = clamp(ct / wt, 0.0, 1.0);
+ return output_pix;
+}
diff --git a/home/rmpc/config.ron b/home/rmpc/config.ron
@@ -0,0 +1,316 @@
+#![enable(implicit_some)]
+#![enable(unwrap_newtypes)]
+#![enable(unwrap_variant_newtypes)]
+(
+ address: "/run/user/1000/mpd/socket",
+ password: None,
+ theme: "rose-pine",
+ cache_dir: None,
+ on_song_change: ["~/.config/rmpc/scripts/fetch-lyrics"],
+ lyrics_dir: "~/.local/share/rmpc/lyrics",
+ volume_step: 5,
+ max_fps: 60,
+ scrolloff: 3,
+ wrap_navigation: false,
+ enable_mouse: true,
+ scroll_amount: 1,
+ enable_config_hot_reload: true,
+ enable_lyrics_hot_reload: true,
+ status_update_interval_ms: 500,
+ rewind_to_start_sec: None,
+ keep_state_on_song_change: true,
+ reflect_changes_to_playlist: false,
+ select_current_song_on_change: false,
+ ignore_leading_the: false,
+ browser_song_sort: [Disc, Track, Artist, Title],
+ directories_sort: SortFormat(group_by_type: true, reverse: false),
+ auto_open_downloads: true,
+ album_art: (
+ method: Auto,
+ max_size_px: (width: 1200, height: 1200),
+ disabled_protocols: ["http://", "https://"],
+ vertical_align: Center,
+ horizontal_align: Center,
+ ),
+ cava: (
+ framerate: 60,
+ autosens: true,
+ input: (
+ method: Fifo,
+ source: "/run/user/1000/mpd/fifo",
+ sample_rate: 44100,
+ channels: 2,
+ sample_bits: 16,
+ ),
+ smoothing: (
+ noise_reduction: 77,
+ monstercat: true,
+ waves: false,
+ ),
+ ),
+ keybinds: (
+ global: {
+ "q": Quit,
+ "?": ShowHelp,
+ ":": CommandMode,
+ "oI": ShowCurrentSongInfo,
+ "oo": ShowOutputs,
+ "op": ShowDecoders,
+ "od": ShowDownloads,
+ "oP": Partition(),
+ "z": ToggleRepeat,
+ "x": ToggleRandom,
+ "c": ToggleConsume,
+ "v": ToggleSingle,
+ "p": TogglePause,
+ "s": Stop,
+ ">": NextTrack,
+ "<": PreviousTrack,
+ "f": SeekForward,
+ "b": SeekBack,
+ ".": VolumeUp,
+ ",": VolumeDown,
+ "<Tab>": NextTab,
+ "gt": NextTab,
+ "<S-Tab>": PreviousTab,
+ "gT": PreviousTab,
+ "1": SwitchToTab("Queue"),
+ "2": SwitchToTab("Now Playing"),
+ "3": SwitchToTab("Directories"),
+ "4": SwitchToTab("Artists"),
+ "5": SwitchToTab("Album Artists"),
+ "6": SwitchToTab("Albums"),
+ "7": SwitchToTab("Genre"),
+ "8": SwitchToTab("Playlists"),
+ "9": SwitchToTab("Search"),
+ "0": SwitchToTab("Lyrics"),
+ "L": SwitchToTab("Lyrics"),
+ "<C-u>": Update,
+ "<C-U>": Rescan,
+ "R": AddRandom,
+ },
+ navigation: {
+ "<C-c>": Close,
+ "<Esc>": Close,
+ "<CR>": Confirm,
+ "k": Up,
+ "<Up>": Up,
+ "j": Down,
+ "<Down>": Down,
+ "h": Left,
+ "<Left>": Left,
+ "l": Right,
+ "<Right>": Right,
+ "<C-w>k": PaneUp,
+ "<C-Up>": PaneUp,
+ "<C-w>j": PaneDown,
+ "<C-Down>": PaneDown,
+ "<C-w>h": PaneLeft,
+ "<C-Left>": PaneLeft,
+ "<C-w>l": PaneRight,
+ "<C-Right>": PaneRight,
+ "K": MoveUp,
+ "J": MoveDown,
+ "<C-u>": UpHalf,
+ "<C-d>": DownHalf,
+ "<C-b>": PageUp,
+ "<PageUp>": PageUp,
+ "<C-f>": PageDown,
+ "<PageDown>": PageDown,
+ "gg": Top,
+ "G": Bottom,
+ "<Space>": Select,
+ "<C-Space>": InvertSelection,
+ "/": EnterSearch,
+ "n": NextResult,
+ "N": PreviousResult,
+ "a": Add,
+ "A": AddAll,
+ "D": Delete,
+ "<C-r>": Rename,
+ "i": FocusInput,
+ "oi": ShowInfo,
+ "<C-z>": ContextMenu(),
+ "<C-s>s": Save(kind: Modal(all: false, duplicates_strategy: Ask)),
+ "<C-s>a": Save(kind: Modal(all: true, duplicates_strategy: Ask)),
+ "r": Rate(),
+ },
+ queue: {
+ "d": Delete,
+ "D": DeleteAll,
+ "<CR>": Play,
+ "C": JumpToCurrent,
+ "X": Shuffle,
+ },
+ ),
+ search: (
+ case_sensitive: false,
+ ignore_diacritics: false,
+ search_button: false,
+ mode: Contains,
+ tags: [
+ (value: "any", label: "Any Tag"),
+ (value: "artist", label: "Artist"),
+ (value: "album", label: "Album"),
+ (value: "albumartist", label: "Album Artist"),
+ (value: "title", label: "Title"),
+ (value: "filename", label: "Filename"),
+ (value: "genre", label: "Genre"),
+ ],
+ ),
+ artists: (
+ album_display_mode: SplitByDate,
+ album_sort_by: Date,
+ album_date_tags: [Date],
+ ),
+ tabs: [
+ (
+ name: "Queue",
+ pane: Split(
+ direction: Horizontal,
+ panes: [
+ (
+ size: "40%",
+ borders: "ALL",
+ border_symbols: Rounded,
+ pane: Pane(AlbumArt),
+ ),
+ (
+ size: "60%",
+ pane: Split(
+ direction: Vertical,
+ panes: [
+ (
+ size: "3",
+ borders: "ALL",
+ border_symbols: Inherited(parent: Rounded, bottom_left: "├", bottom_right: "┤"),
+ pane: Split(
+ direction: Horizontal,
+ panes: [
+ (size: "1", pane: Pane(Empty())),
+ (size: "100%", pane: Pane(QueueHeader())),
+ ],
+ ),
+ ),
+ (
+ size: "100%",
+ borders: "LEFT | RIGHT | BOTTOM",
+ border_symbols: Rounded,
+ pane: Split(
+ direction: Horizontal,
+ panes: [
+ (size: "1", pane: Pane(Empty())),
+ (size: "100%", pane: Pane(Queue)),
+ ],
+ ),
+ ),
+ (
+ size: "35%",
+ borders: "ALL",
+ border_symbols: Rounded,
+ border_title: [(kind: Text(" Visualiser "))],
+ border_title_alignment: Right,
+ pane: Pane(Cava),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ (
+ name: "Now Playing",
+ pane: Split(
+ direction: Horizontal,
+ panes: [
+ (
+ size: "42%",
+ borders: "ALL",
+ border_symbols: Rounded,
+ pane: Pane(AlbumArt),
+ ),
+ (
+ size: "58%",
+ pane: Split(
+ direction: Vertical,
+ panes: [
+ (
+ size: "65%",
+ borders: "ALL",
+ border_symbols: Rounded,
+ border_title: [(kind: Text(" Synced Lyrics "))],
+ border_title_alignment: Center,
+ pane: Pane(Lyrics),
+ ),
+ (
+ size: "35%",
+ borders: "ALL",
+ border_symbols: Rounded,
+ border_title: [(kind: Text(" Visualiser "))],
+ border_title_alignment: Right,
+ pane: Pane(Cava),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ (
+ name: "Directories",
+ pane: Split(
+ direction: Vertical,
+ panes: [(pane: Pane(Directories), size: "100%", borders: "ALL", border_symbols: Rounded)],
+ ),
+ ),
+ (
+ name: "Artists",
+ pane: Split(
+ direction: Vertical,
+ panes: [(pane: Pane(Artists), size: "100%", borders: "ALL", border_symbols: Rounded)],
+ ),
+ ),
+ (
+ name: "Album Artists",
+ pane: Split(
+ direction: Vertical,
+ panes: [(pane: Pane(AlbumArtists), size: "100%", borders: "ALL", border_symbols: Rounded)],
+ ),
+ ),
+ (
+ name: "Albums",
+ pane: Split(
+ direction: Vertical,
+ panes: [(pane: Pane(Albums), size: "100%", borders: "ALL", border_symbols: Rounded)],
+ ),
+ ),
+ (
+ name: "Genre",
+ pane: Split(
+ direction: Vertical,
+ panes: [(pane: Pane(Browser(root_tag: "genre")), size: "100%", borders: "ALL", border_symbols: Rounded)],
+ ),
+ ),
+ (
+ name: "Playlists",
+ pane: Split(
+ direction: Vertical,
+ panes: [(pane: Pane(Playlists), size: "100%", borders: "ALL", border_symbols: Rounded)],
+ ),
+ ),
+ (
+ name: "Search",
+ pane: Split(
+ direction: Vertical,
+ panes: [(pane: Pane(Search), size: "100%", borders: "ALL", border_symbols: Rounded)],
+ ),
+ ),
+ (
+ name: "Lyrics",
+ pane: Split(
+ direction: Vertical,
+ panes: [(pane: Pane(Lyrics), size: "100%", borders: "ALL", border_symbols: Rounded)],
+ ),
+ ),
+ ],
+)
diff --git a/home/rmpc/scripts/fetch-lyrics b/home/rmpc/scripts/fetch-lyrics
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+# rmpc on_song_change hook: fetch synced lyrics from LRCLIB when missing.
+#
+# rmpc provides (among others): $ARTIST $TITLE $ALBUM $DURATION $FILE
+# $HAS_LRC $LRC_FILE $PID $VERSION
+# Writes to $LRC_FILE (under lyrics_dir), then hot-indexes it via
+# `rmpc remote indexlrc`. Never blocks rmpc: all work happens in a
+# backgrounded subshell; all failures are silent (logged to cache dir).
+
+CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/rmpc-lyrics"
+MISS_DIR="$CACHE_DIR/misses"
+LOG_FILE="$CACHE_DIR/fetch.log"
+LRCLIB="https://lrclib.net"
+MISS_TTL_DAYS=14
+
+main() {
+ mkdir -p "$MISS_DIR"
+
+ # Nothing to do without metadata or if lyrics already resolved.
+ [ "${HAS_LRC:-true}" = "false" ] || exit 0
+ [ -n "${ARTIST:-}" ] && [ -n "${TITLE:-}" ] || exit 0
+ [ -n "${LRC_FILE:-}" ] || exit 0
+ [ -s "$LRC_FILE" ] && exit 0
+
+ # Negative cache: don't hammer LRCLIB for known misses.
+ local key miss
+ key=$(printf '%s\t%s\t%s' "$ARTIST" "$TITLE" "${ALBUM:-}" | sha1sum | cut -d' ' -f1)
+ miss="$MISS_DIR/$key"
+ if [ -e "$miss" ] && [ -z "$(find "$miss" -mtime +"$MISS_TTL_DAYS" 2>/dev/null)" ]; then
+ exit 0
+ fi
+
+ # Serialize concurrent hook invocations (fast track skipping).
+ exec 9>"$CACHE_DIR/lock"
+ flock -n 9 || exit 0
+
+ # DURATION may be seconds ("312.43") or "MM:SS"; normalise to integer seconds.
+ local dur_s=""
+ case "${DURATION:-}" in
+ *:*) dur_s=$(( $(echo "$DURATION" | awk -F: '{print $(NF-1)*60 + int($NF)}') )) ;;
+ *[0-9]*) dur_s=$(printf '%.0f' "$DURATION" 2>/dev/null || true) ;;
+ esac
+
+ # Try /api/get with maximum specificity first, then progressively relax.
+ local resp="" synced="" instrumental=""
+ for attempt in full noalbum bare; do
+ local args=(-sG --max-time 15 -H "Lrclib-Client: rmpc-${VERSION:-unknown}"
+ --data-urlencode "artist_name=$ARTIST"
+ --data-urlencode "track_name=$TITLE")
+ case $attempt in
+ full)
+ [ -n "${ALBUM:-}" ] && args+=(--data-urlencode "album_name=$ALBUM")
+ [ -n "$dur_s" ] && args+=(--data-urlencode "duration=$dur_s")
+ ;;
+ noalbum)
+ [ -n "$dur_s" ] && args+=(--data-urlencode "duration=$dur_s")
+ ;;
+ esac
+ resp=$(curl "${args[@]}" "$LRCLIB/api/get" 2>>"$LOG_FILE") || resp=""
+ synced=$(printf '%s' "$resp" | jq -r '.syncedLyrics // empty' 2>/dev/null)
+ instrumental=$(printf '%s' "$resp" | jq -r '.instrumental // false' 2>/dev/null)
+ [ -n "$synced" ] && break
+ [ "$instrumental" = "true" ] && break
+ sleep 1 # be polite between retries
+ done
+
+ if [ -z "$synced" ]; then
+ # Instrumental or genuinely not found: remember the miss, stay silent.
+ touch "$miss"
+ printf '%s MISS %s - %s\n' "$(date -Iseconds)" "$ARTIST" "$TITLE" >>"$LOG_FILE"
+ exit 0
+ fi
+
+ # Write the .lrc with metadata tags so rmpc's index matches it to the
+ # song regardless of file path. UTF-8 passes through untouched.
+ mkdir -p "$(dirname "$LRC_FILE")"
+ local tmp
+ tmp=$(mktemp "$(dirname "$LRC_FILE")/.fetch.XXXXXX") || exit 0
+ {
+ printf '[ar:%s]\n' "$ARTIST"
+ [ -n "${ALBUM:-}" ] && printf '[al:%s]\n' "$ALBUM"
+ printf '[ti:%s]\n' "$TITLE"
+ printf '%s\n' "$synced" | sed -E '/^\[(ar|al|ti|length):/d'
+ } >"$tmp"
+ mv -f "$tmp" "$LRC_FILE"
+ rm -f "$miss"
+ printf '%s HIT %s - %s -> %s\n' "$(date -Iseconds)" "$ARTIST" "$TITLE" "$LRC_FILE" >>"$LOG_FILE"
+
+ # Tell the running rmpc instance about the new file (hot reload).
+ if [ -n "${PID:-}" ]; then
+ rmpc remote --pid "$PID" indexlrc --path "$LRC_FILE" 2>>"$LOG_FILE" || true
+ rmpc remote --pid "$PID" status "Lyrics downloaded: $ARTIST - $TITLE" --level info 2>>"$LOG_FILE" || true
+ fi
+}
+
+# Never block rmpc's hook runner.
+main >/dev/null 2>&1 &
+exit 0
diff --git a/home/rmpc/themes/rose-pine.ron b/home/rmpc/themes/rose-pine.ron
@@ -0,0 +1,320 @@
+#![enable(implicit_some)]
+#![enable(unwrap_newtypes)]
+#![enable(unwrap_variant_newtypes)]
+// "rose-pine" — Rosé Pine (main) theme for rmpc
+// palette: base #191724 / text #e0def4 / subtle #908caa / hl-med #403d52
+// iris #c4a7e7 / rose #ebbcba / love #eb6f92 / foam #9ccfd8 / gold #f6c177
+(
+ default_album_art_path: None,
+ format_tag_separator: " | ",
+ browser_column_widths: [20, 38, 42],
+ background_color: "#191724",
+ text_color: "#e0def4",
+ header_background_color: "#191724",
+ modal_background_color: "#1f1d2e",
+ modal_backdrop: true,
+ preview_label_style: (fg: "#c4a7e7"),
+ preview_metadata_group_style: (fg: "#c4a7e7", modifiers: "Bold"),
+ highlighted_item_style: (fg: "#ebbcba", modifiers: "Bold"),
+ current_item_style: (fg: "#191724", bg: "#c4a7e7", modifiers: "Bold"),
+ borders_style: (fg: "#403d52"),
+ highlight_border_style: (fg: "#c4a7e7"),
+ symbols: (
+ song: "♪",
+ dir: "▸",
+ playlist: "≡",
+ marker: "▌",
+ ellipsis: "…",
+ song_style: (fg: "#908caa"),
+ dir_style: (fg: "#c4a7e7"),
+ playlist_style: (fg: "#ebbcba"),
+ ),
+ level_styles: (
+ info: (fg: "#c4a7e7", bg: "#191724"),
+ warn: (fg: "#f6c177", bg: "#191724"),
+ error: (fg: "#eb6f92", bg: "#191724"),
+ debug: (fg: "#9ccfd8", bg: "#191724"),
+ trace: (fg: "#ebbcba", bg: "#191724"),
+ ),
+ progress_bar: (
+ symbols: ["━", "━", "●", "─", "─"],
+ track_style: (fg: "#403d52"),
+ elapsed_style: (fg: "#c4a7e7"),
+ thumb_style: (fg: "#ebbcba"),
+ use_track_when_empty: true,
+ ),
+ scrollbar: (
+ symbols: ["│", "█", "▲", "▼"],
+ track_style: (fg: "#403d52"),
+ ends_style: (fg: "#403d52"),
+ thumb_style: (fg: "#c4a7e7"),
+ ),
+ tab_bar: (
+ active_style: (fg: "#191724", bg: "#c4a7e7", modifiers: "Bold"),
+ inactive_style: (fg: "#908caa"),
+ ),
+ lyrics: (
+ timestamp: false,
+ ),
+ cava: (
+ bar_width: 2,
+ bar_spacing: 1,
+ bar_color: Gradient({
+ 0: "#eb6f92",
+ 45: "#c4a7e7",
+ 100: "#9ccfd8",
+ }),
+ ),
+ browser_song_format: [
+ (
+ kind: Group([
+ (kind: Property(Track)),
+ (kind: Text(" ")),
+ ])
+ ),
+ (
+ kind: Group([
+ (kind: Property(Artist)),
+ (kind: Text(" - ")),
+ (kind: Property(Title)),
+ ]),
+ default: (kind: Property(Filename))
+ ),
+ ],
+ song_table_format: [
+ (
+ prop: (kind: Property(Artist), style: (fg: "#ebbcba"),
+ default: (kind: Text("Unknown"), style: (fg: "#908caa"))
+ ),
+ label_prop: (kind: Text("Artist"), style: (fg: "#c4a7e7", modifiers: "Bold")),
+ width: "22%",
+ ),
+ (
+ prop: (kind: Property(Title), style: (fg: "#e0def4", modifiers: "Bold"),
+ default: (kind: Text("Unknown"), style: (fg: "#908caa"))
+ ),
+ label_prop: (kind: Text("Title"), style: (fg: "#c4a7e7", modifiers: "Bold")),
+ width: "38%",
+ ),
+ (
+ prop: (kind: Property(Album), style: (fg: "#908caa"),
+ default: (kind: Text("Unknown Album"), style: (fg: "#908caa"))
+ ),
+ label_prop: (kind: Text("Album"), style: (fg: "#c4a7e7", modifiers: "Bold")),
+ width: "30%",
+ ),
+ (
+ prop: (kind: Property(Duration),
+ default: (kind: Text("-"))
+ ),
+ label_prop: (kind: Text("Length"), style: (fg: "#c4a7e7", modifiers: "Bold")),
+ width: "10%",
+ alignment: Right,
+ ),
+ ],
+ layout: Split(
+ direction: Vertical,
+ panes: [
+ (
+ size: "4",
+ pane: Split(
+ direction: Horizontal,
+ panes: [
+ (
+ size: "35",
+ borders: "LEFT | TOP | BOTTOM",
+ border_symbols: Inherited(parent: Rounded, bottom_left: "├"),
+ pane: Component("header_left")
+ ),
+ (
+ size: "100%",
+ borders: "ALL",
+ border_symbols: Inherited(parent: Rounded, top_left: "┬", top_right: "┬", bottom_left: "┴", bottom_right: "┴"),
+ pane: Component("header_center")
+ ),
+ (
+ size: "35",
+ borders: "RIGHT | TOP | BOTTOM",
+ border_symbols: Inherited(parent: Rounded, bottom_right: "┤"),
+ pane: Component("header_right")
+ ),
+ ]
+ )
+ ),
+ (
+ pane: Pane(Tabs),
+ borders: "RIGHT | LEFT | BOTTOM",
+ border_symbols: Rounded,
+ size: "2",
+ ),
+ (
+ pane: Pane(TabContent),
+ size: "100%",
+ ),
+ (
+ size: "3",
+ pane: Split(
+ direction: Horizontal,
+ panes: [
+ (
+ size: "12",
+ borders: "ALL",
+ border_symbols: Inherited(parent: Rounded, top_right: "┬", bottom_right: "┴"),
+ pane: Component("input_mode")
+ ),
+ (
+ size: "100%",
+ borders: "TOP | BOTTOM | RIGHT",
+ border_symbols: Rounded,
+ border_title: [(kind: Text(" ")), (kind: Property(Status(QueueLength()))), (kind: Text(" songs / ")), (kind: Property(Status(QueueTimeTotal()))), (kind: Text(" total time "))],
+ border_title_alignment: Right,
+ pane: Component("progress_bar"),
+ ),
+ ]
+ ),
+ ),
+ ],
+ ),
+ components: {
+ "state": Pane(Property(
+ content: [
+ (kind: Text("["), style: (fg: "#9ccfd8", modifiers: "Bold")),
+ (kind: Property(Status(StateV2( ))), style: (fg: "#9ccfd8", modifiers: "Bold")),
+ (kind: Text("]"), style: (fg: "#9ccfd8", modifiers: "Bold")),
+ ], align: Left,
+ )),
+ "title": Pane(Property(
+ content: [
+ (kind: Property(Song(Title)), style: (fg: "#e0def4", modifiers: "Bold"),
+ default: (kind: Text("No Song"), style: (fg: "#908caa", modifiers: "Bold"))),
+ ], align: Center, scroll_speed: 1
+ )),
+ "volume": Split(
+ direction: Horizontal,
+ panes: [
+ (size: "1", pane: Pane(Property(content: [(kind: Text(""))]))),
+ (size: "100%", pane: Pane(Volume(kind: Slider(symbols: (filled: "─", thumb: "●", track: "─"))))),
+ (size: "3", pane: Pane(Property(content: [(kind: Property(Status(Volume)), style: (fg: "#c4a7e7"))], align: Right))),
+ (size: "2", pane: Pane(Property(content: [(kind: Text("%"), style: (fg: "#c4a7e7"))]))),
+ ]
+ ),
+ "elapsed_and_bitrate": Pane(Property(
+ content: [
+ (kind: Property(Status(Elapsed)), style: (fg: "#e0def4")),
+ (kind: Text(" / "), style: (fg: "#908caa")),
+ (kind: Property(Status(Duration)), style: (fg: "#e0def4")),
+ (kind: Group([
+ (kind: Text(" ("), style: (fg: "#908caa")),
+ (kind: Property(Status(Bitrate)), style: (fg: "#908caa")),
+ (kind: Text(" kbps)"), style: (fg: "#908caa")),
+ ])),
+ ],
+ align: Left,
+ )),
+ "artist_and_album": Pane(Property(
+ content: [
+ (kind: Property(Song(Artist)), style: (fg: "#eb6f92", modifiers: "Bold"),
+ default: (kind: Text("Unknown"), style: (fg: "#908caa", modifiers: "Bold"))),
+ (kind: Text(" — "), style: (fg: "#908caa")),
+ (kind: Property(Song(Album)), style: (fg: "#ebbcba"),
+ default: (kind: Text("Unknown Album"), style: (fg: "#908caa"))),
+ ], align: Center, scroll_speed: 1
+ )),
+ "states": Split(
+ direction: Horizontal,
+ panes: [
+ (
+ size: "1",
+ pane: Pane(Empty())
+ ),
+ (
+ size: "100%",
+ pane: Pane(Property(content: [(kind: Property(Status(InputBuffer())), style: (fg: "#c4a7e7"), align: Left)]))
+ ),
+ (
+ size: "6",
+ pane: Pane(Property(content: [
+ (kind: Text("["), style: (fg: "#c4a7e7", modifiers: "Bold")),
+ (kind: Property(Status(RepeatV2(
+ on_label: "z",
+ off_label: "z",
+ on_style: (fg: "#9ccfd8", modifiers: "Bold"),
+ off_style: (fg: "#403d52"),
+ )))),
+ (kind: Property(Status(RandomV2(
+ on_label: "x",
+ off_label: "x",
+ on_style: (fg: "#9ccfd8", modifiers: "Bold"),
+ off_style: (fg: "#403d52"),
+ )))),
+ (kind: Property(Status(ConsumeV2(
+ on_label: "c",
+ off_label: "c",
+ oneshot_label: "c",
+ on_style: (fg: "#9ccfd8", modifiers: "Bold"),
+ off_style: (fg: "#403d52"),
+ oneshot_style: (fg: "#eb6f92", modifiers: "Bold"),
+ )))),
+ (kind: Property(Status(SingleV2(
+ on_label: "v",
+ off_label: "v",
+ oneshot_label: "v",
+ on_style: (fg: "#9ccfd8", modifiers: "Bold"),
+ off_style: (fg: "#403d52"),
+ oneshot_style: (fg: "#eb6f92", modifiers: "Bold"),
+ )))),
+ (kind: Text("]"), style: (fg: "#c4a7e7", modifiers: "Bold")),
+ ],
+ align: Right
+ ))
+ ),
+ ]
+ ),
+ "input_mode": Pane(Property(
+ content: [
+ (kind: Transform(Replace(content: (kind: Property(Status(InputMode()))), replacements: [
+ (match: "Normal", replace: (kind: Text(" NORMAL "), style: (fg: "#191724", bg: "#c4a7e7"))),
+ (match: "Insert", replace: (kind: Text(" INSERT "), style: (fg: "#191724", bg: "#9ccfd8"))),
+ ])))
+ ], align: Center
+ )),
+ "header_left": Split(
+ direction: Vertical,
+ panes: [
+ (size: "1", pane: Component("state")),
+ (size: "1", pane: Component("elapsed_and_bitrate")),
+ ]
+ ),
+ "header_center": Split(
+ direction: Vertical,
+ panes: [
+ (size: "1", pane: Component("title")),
+ (size: "1", pane: Component("artist_and_album")),
+ ]
+ ),
+ "header_right": Split(
+ direction: Vertical,
+ panes: [
+ (size: "1", pane: Component("volume")),
+ (size: "1", pane: Component("states")),
+ ]
+ ),
+ "progress_bar": Split(
+ direction: Horizontal,
+ panes: [
+ (
+ size: "1",
+ pane: Pane(Empty())
+ ),
+ (
+ size: "100%",
+ pane: Pane(ProgressBar)
+ ),
+ (
+ size: "1",
+ pane: Pane(Empty())
+ ),
+ ]
+ )
+ },
+)
diff --git a/home/yazi/flavors/rose-pine.yazi/flavor.toml b/home/yazi/flavors/rose-pine.yazi/flavor.toml
@@ -0,0 +1,253 @@
+# vim:fileencoding=utf-8:foldmethod=marker
+
+# : App {{{
+
+[app]
+overall = { bg = "#191724" }
+
+# : }}}
+
+
+# : Manager {{{
+
+[mgr]
+cwd = { fg = "#9ccfd8" }
+
+# Find
+find_keyword = { fg = "#f6c177", bold = true, italic = true, underline = true }
+find_position = { fg = "#c4a7e7", bg = "reset", bold = true, italic = true }
+
+# Symlink
+symlink_target = { italic = true }
+
+# Marker
+marker_copied = { fg = "#31748f", bg = "#31748f" }
+marker_cut = { fg = "#eb6f92", bg = "#eb6f92" }
+marker_marked = { fg = "#ebbcba", bg = "#ebbcba" }
+marker_selected = { fg = "#f6c177", bg = "#f6c177" }
+
+# Count
+count_copied = { fg = "#e0def4", bg = "#31748f" }
+count_cut = { fg = "#e0def4", bg = "#eb6f92" }
+count_selected = { fg = "#191724", bg = "#f6c177" }
+
+# Border
+border_symbol = "│"
+border_style = { fg = "#524f67" }
+
+# Highlighting
+syntect_theme = ""
+
+# : }}}
+
+
+# : Tabs {{{
+
+[tabs]
+active = { fg = "#e0def4", bg = "#31748f", bold = true }
+inactive = { fg = "#31748f", bg = "#e0def4" }
+
+# Separator
+sep_inner = { open = "", close = "" }
+sep_outer = { open = "", close = "" }
+
+# : }}}
+
+
+# : Mode {{{
+
+[mode]
+normal_main = { fg = "#191724", bg = "#9ccfd8", bold = true }
+normal_alt = { fg = "#191724", bg = "#e0def4", bold = true }
+
+# Select mode
+select_main = { fg = "#191724", bg = "#eb6f92", bold = true }
+select_alt = { fg = "#191724", bg = "#e0def4", bold = true }
+
+# Unset mode
+unset_main = { fg = "#191724", bg = "#eb6f92", bold = true }
+unset_alt = { fg = "#191724", bg = "#e0def4", bold = true }
+
+# : }}}
+
+
+# : Indicator of hovered file {{{
+
+[indicator]
+parent = { reversed = true }
+current = { reversed = true }
+preview = { underline = true }
+padding = { open = "", close = "" }
+
+# : }}}
+
+
+# : Status bar {{{
+
+[status]
+overall = { bg = "#191724" }
+sep_left = { open = "", close = "" }
+sep_right = { open = "", close = "" }
+
+# Permissions
+perm_sep = { fg = "#6e6a86" }
+perm_type = { fg = "#c4a7e7" }
+perm_read = { fg = "#f6c177" }
+perm_write = { fg = "#eb6f92" }
+perm_exec = { fg = "#9ccfd8" }
+
+# Progress
+progress_label = { fg = "#e0def4", bold = true }
+progress_normal = { fg = "#31748f", bg = "#21202e" }
+progress_error = { fg = "#eb6f92", bg = "#21202e" }
+
+# : }}}
+
+
+# : Which {{{
+
+[which]
+cols = 3
+mask = { bg = "#1f1d2e" }
+cand = { fg = "#9ccfd8" }
+rest = { fg = "#6e6a86" }
+desc = { fg = "#f6c177" }
+separator = " "
+separator_style = { fg = "#6e6a86" }
+
+# : }}}
+
+
+# : Confirmation {{{
+
+[confirm]
+border = { fg = "#524f67" }
+title = { fg = "#eb6f92", bold = true }
+body = { fg = "#e0def4" }
+list = { fg = "#e0def4" }
+btn_yes = { fg = "#191724", bg = "#31748f", bold = true }
+btn_no = { fg = "#191724", bg = "#e0def4", bold = true }
+btn_labels = [ " [y]es ", " (n)o " ]
+
+# : }}}
+
+
+# : Spotter {{{
+
+[spot]
+border = { fg = "#524f67" }
+title = { fg = "#9ccfd8", bold = true }
+
+# Table
+tbl_col = { fg = "#9ccfd8" }
+tbl_cell = { fg = "#191724", bg = "#f6c177", bold = true }
+
+# : }}}
+
+
+# : Notification {{{
+
+[notify]
+title_info = { fg = "#31748f" }
+title_warn = { fg = "#f6c177" }
+title_error = { fg = "#eb6f92" }
+
+# Icons
+icon_info = ""
+icon_warn = ""
+icon_error = ""
+
+# : }}}
+
+
+# : Picker {{{
+
+[pick]
+border = { fg = "#31748f" }
+active = { fg = "#c4a7e7", bold = true }
+inactive = { fg = "#908caa" }
+
+# : }}}
+
+
+# : Input {{{
+
+[input]
+border = { fg = "#31748f" }
+title = { bold = true }
+value = { fg = "#e0def4" }
+selected = { reversed = true }
+
+# : }}}
+
+
+# : Completion {{{
+
+[cmp]
+border = { fg = "#31748f" }
+active = { bg = "#e0def4", fg = "#191724", bold = true }
+inactive = { fg = "#908caa" }
+
+# Icons
+icon_file = ""
+icon_folder = ""
+icon_command = ""
+
+# : }}}
+
+
+# : Task manager {{{
+
+[tasks]
+border = { fg = "#31748f" }
+title = { bold = true }
+hovered = { fg = "#c4a7e7", bold = true, reversed = true }
+
+# : }}}
+
+
+# : Help menu {{{
+
+[help]
+on = { fg = "#9ccfd8" }
+run = { fg = "#c4a7e7" }
+desc = { fg = "#f6c177" }
+hovered = { reversed = true, bold = true }
+footer = { fg = "#191724", bg = "#e0def4" }
+
+# : }}}
+
+
+# : File-specific styles {{{
+
+[filetype]
+rules = [
+ # Images
+ { mime = "image/*", fg = "#c4a7e7" },
+
+ # Media
+ { mime = "{audio,video}/*", fg = "#f6c177" },
+
+ # Archives
+ { mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "#eb6f92" },
+
+ # Documents
+ { mime = "application/{pdf,doc,rtf}", fg = "#ebbcba" },
+
+ # Empty files
+ # { mime = "inode/empty", fg = "#eb6f92" },
+
+ # Special files
+ { url = "*", is = "orphan", fg = "#191724", bg = "#eb6f92" },
+ { url = "*", is = "exec", fg = "#9ccfd8" },
+
+ # Dummy files
+ { url = "*", is = "dummy", fg = "#191724", bg = "#eb6f92" },
+ { url = "*/", is = "dummy", fg = "#191724", bg = "#eb6f92" },
+
+ # Fallback
+ { url = "*", fg = "#e0def4" },
+ { url = "*/", fg = "#31748f" }
+]
+
+# : }}}
diff --git a/home/yazi/flavors/rose-pine.yazi/tmtheme.xml b/home/yazi/flavors/rose-pine.yazi/tmtheme.xml
@@ -0,0 +1,316 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>name</key>
+ <string>Rosé Pine</string>
+ <key>settings</key>
+ <array>
+ <dict>
+ <key>settings</key>
+ <dict>
+ <key>background</key>
+ <string>#191724</string>
+ <key>caret</key>
+ <string>#524f67</string>
+ <key>foreground</key>
+ <string>#e0def4</string>
+ <key>invisibles</key>
+ <string>#1f1d2e</string>
+ <key>lineHighlight</key>
+ <string>#21202e</string>
+ <key>selection</key>
+ <string>#403d52</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Comment</string>
+ <key>scope</key>
+ <string>comment</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string>italic</string>
+ <key>foreground</key>
+ <string>#908caa</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>String</string>
+ <key>scope</key>
+ <string>string, punctuation.definition.string</string>
+ <key>settings</key>
+ <dict>
+ <key>foreground</key>
+ <string>#f6c177</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Number</string>
+ <key>scope</key>
+ <string>constant.numeric</string>
+ <key>settings</key>
+ <dict>
+ <key>foreground</key>
+ <string>#f6c177</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Built-in constant</string>
+ <key>scope</key>
+ <string>constant.language</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string>bold</string>
+ <key>foreground</key>
+ <string>#f6c177</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>User-defined constant</string>
+ <key>scope</key>
+ <string>constant.character, constant.other</string>
+ <key>settings</key>
+ <dict>
+ <key>foreground</key>
+ <string>#f6c177</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Variable</string>
+ <key>scope</key>
+ <string>variable</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string>italic</string>
+ <key>foreground</key>
+ <string>#e0def4</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Keyword</string>
+ <key>scope</key>
+ <string>keyword</string>
+ <key>settings</key>
+ <dict>
+ <key>foreground</key>
+ <string>#31748f</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Storage</string>
+ <key>scope</key>
+ <string>storage</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string></string>
+ <key>foreground</key>
+ <string>#9ccfd8</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Storage type</string>
+ <key>scope</key>
+ <string>storage.type</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string></string>
+ <key>foreground</key>
+ <string>#9ccfd8</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Class name</string>
+ <key>scope</key>
+ <string>entity.name.class</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string>bold</string>
+ <key>foreground</key>
+ <string>#31748f</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Inherited class</string>
+ <key>scope</key>
+ <string>entity.other.inherited-class</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string>italic </string>
+ <key>foreground</key>
+ <string>#31748f</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Function name</string>
+ <key>scope</key>
+ <string>entity.name.function</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string>italic</string>
+ <key>foreground</key>
+ <string>#ebbcba</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Function argument</string>
+ <key>scope</key>
+ <string>variable.parameter</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string></string>
+ <key>foreground</key>
+ <string>#c4a7e7</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Tag name</string>
+ <key>scope</key>
+ <string>entity.name.tag</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string>bold</string>
+ <key>foreground</key>
+ <string>#31748f</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Tag attribute</string>
+ <key>scope</key>
+ <string>entity.other.attribute-name</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string></string>
+ <key>foreground</key>
+ <string>#c4a7e7</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Library function</string>
+ <key>scope</key>
+ <string>support.function</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string>bold</string>
+ <key>foreground</key>
+ <string>#ebbcba</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Library constant</string>
+ <key>scope</key>
+ <string>support.constant</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string>bold</string>
+ <key>foreground</key>
+ <string>#f6c177</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Library class/type</string>
+ <key>scope</key>
+ <string>support.type, support.class</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string>bold</string>
+ <key>foreground</key>
+ <string>#9ccfd8</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Library variable</string>
+ <key>scope</key>
+ <string>support.other.variable</string>
+ <key>settings</key>
+ <dict>
+ <key>fontStyle</key>
+ <string>bold</string>
+ <key>foreground</key>
+ <string>#eb6f92</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Invalid</string>
+ <key>scope</key>
+ <string>invalid</string>
+ <key>settings</key>
+ <dict>
+ <key>background</key>
+ <string>#eb6f92</string>
+ <key>fontStyle</key>
+ <string></string>
+ <key>foreground</key>
+ <string>#e0def4</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Invalid deprecated</string>
+ <key>scope</key>
+ <string>invalid.deprecated</string>
+ <key>settings</key>
+ <dict>
+ <key>background</key>
+ <string>#c4a7e7</string>
+ <key>foreground</key>
+ <string>#e0def4</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>name</key>
+ <string>Punctuation, Operators</string>
+ <key>scope</key>
+ <string>punctuation, keyword.operator</string>
+ <key>settings</key>
+ <dict>
+ <key>foreground</key>
+ <string>#908caa</string>
+ </dict>
+ </dict>
+ </array>
+ <key>colorSpaceName</key>
+ <string>sRGB</string>
+ <key>semanticClass</key>
+ <string>theme.dark.rosé-pine</string>
+ <key>author</key>
+ <string>arrrgi</string>
+ <key>comment</key>
+ <string>All natural pine, faux fur and a bit of soho vibes for the classy minimalist</string>
+ <key>uuid</key>
+ <string>14991673-80EB-41A2-BEFF-03216A233730</string>
+</dict>
+</plist>