feat: file manager — yazi (flagship TUI) + Thunar (GUI)
Legacy shipped only Thunar (and a stray nautilus extension, mid-migration). This pairs a keyboard-driven TUI flagship that fits the distro's identity with the GUI half for point-and-click + the "open folder" handler. yazi (modules/home/yazi.nix, nomarchy.yazi.enable, SUPER+E, `y` shell wrapper cd's on exit): themed from theme-state.json (mgr/mode/status mapped from the palette; merges over yazi's defaults so it tracks the theme) with a curated, correctly-wired plugin set — full-border, git (setup + prepend_fetchers), smart-enter (l/Enter), chmod (cm), mount (M), compress (caa), mediainfo (audio/subtitle only — images/videos keep native thumbnails), glow (markdown previews). Preview/tooling deps: ffmpegthumbnailer, poppler-utils, p7zip, jq, fd, ripgrep, fzf, chafa, mediainfo, glow. Thunar (modules/nixos/file-manager.nix, nomarchy.system.fileManager.enable): GTK file manager (Stylix-themed) + archive/volman/media-tags plugins + gvfs/tumbler/udisks2 backends + thumbnailers + exo. udisks2 also backs yazi's mount plugin. Verified: flake check green, HM generation builds (yazi TOML/init.lua render, all 8 plugins resolve), every package present in the live ISO closure (offline install draws from it — no source-build leak). Not verified: yazi's runtime preview/plugin behaviour needs a real session (TUI; can't drive in CI here). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
12
README.md
12
README.md
@@ -45,7 +45,9 @@ Flat on purpose. Two module trees, one options file each, no hidden layers.
|
|||||||
├── modules/
|
├── modules/
|
||||||
│ ├── nixos/ # the distro, system side
|
│ ├── nixos/ # the distro, system side
|
||||||
│ │ ├── default.nix # Hyprland session, Pipewire, greetd, fonts
|
│ │ ├── default.nix # Hyprland session, Pipewire, greetd, fonts
|
||||||
│ │ └── options.nix # nomarchy.system.* toggles
|
│ │ ├── options.nix # nomarchy.system.* toggles
|
||||||
|
│ │ ├── plymouth.nix # boot splash, tinted from the JSON
|
||||||
|
│ │ └── file-manager.nix # Thunar GUI + gvfs/tumbler/udisks2
|
||||||
│ └── home/ # the distro, user side
|
│ └── home/ # the distro, user side
|
||||||
│ ├── default.nix # entry point
|
│ ├── default.nix # entry point
|
||||||
│ ├── options.nix # nomarchy.* option surface
|
│ ├── options.nix # nomarchy.* option surface
|
||||||
@@ -57,7 +59,8 @@ Flat on purpose. Two module trees, one options file each, no hidden layers.
|
|||||||
│ ├── btop.nix
|
│ ├── btop.nix
|
||||||
│ ├── fuzzel.nix # launcher + nomarchy-menu (power, clip)
|
│ ├── fuzzel.nix # launcher + nomarchy-menu (power, clip)
|
||||||
│ ├── swaync.nix # notifications, same JSON
|
│ ├── swaync.nix # notifications, same JSON
|
||||||
│ └── idle.nix # hyprlock + hypridle, same JSON
|
│ ├── idle.nix # hyprlock + hypridle, same JSON
|
||||||
|
│ └── yazi.nix # flagship TUI file manager + plugins
|
||||||
├── hosts/
|
├── hosts/
|
||||||
│ ├── default/ # reference machine (thin: boot, user, hostname)
|
│ ├── default/ # reference machine (thin: boot, user, hostname)
|
||||||
│ └── live.nix # bootable live ISO (try the distro, no install)
|
│ └── live.nix # bootable live ISO (try the distro, no install)
|
||||||
@@ -172,11 +175,13 @@ examples: **[docs/OVERRIDES.md](docs/OVERRIDES.md)**.
|
|||||||
| `nomarchy.fuzzel.enable` | `true` | Themed fuzzel + `nomarchy-menu` (power menu) |
|
| `nomarchy.fuzzel.enable` | `true` | Themed fuzzel + `nomarchy-menu` (power menu) |
|
||||||
| `nomarchy.swaync.enable` | `true` | swaync notifications, themed |
|
| `nomarchy.swaync.enable` | `true` | swaync notifications, themed |
|
||||||
| `nomarchy.idle.enable` | `true` | hyprlock + hypridle (idle lock 5 min, display off 10, suspend 30) |
|
| `nomarchy.idle.enable` | `true` | hyprlock + hypridle (idle lock 5 min, display off 10, suspend 30) |
|
||||||
|
| `nomarchy.yazi.enable` | `true` | yazi TUI file manager, themed + curated plugins |
|
||||||
| `nomarchy.ghostty.enable` | `true` | Nomarchy's Ghostty |
|
| `nomarchy.ghostty.enable` | `true` | Nomarchy's Ghostty |
|
||||||
| `nomarchy.btop.enable` | `true` | btop with per-theme colors |
|
| `nomarchy.btop.enable` | `true` | btop with per-theme colors |
|
||||||
| `nomarchy.stylix.enable` | `true` | GTK/Qt/cursor theming |
|
| `nomarchy.stylix.enable` | `true` | GTK/Qt/cursor theming |
|
||||||
| `nomarchy.themesDir` | Nomarchy's `themes/` | Where per-theme app overrides are probed |
|
| `nomarchy.themesDir` | Nomarchy's `themes/` | Where per-theme app overrides are probed |
|
||||||
| `nomarchy.system.plymouth.enable` | `true` | Branded boot splash, background from the theme JSON (recolors on system rebuilds) |
|
| `nomarchy.system.plymouth.enable` | `true` | Branded boot splash, background from the theme JSON (recolors on system rebuilds) |
|
||||||
|
| `nomarchy.system.fileManager.enable` | `true` | Thunar GUI + gvfs/tumbler/udisks2 (the "open folder" handler) |
|
||||||
| `nomarchy.system.greeter.enable` | `true` | greetd/tuigreet |
|
| `nomarchy.system.greeter.enable` | `true` | greetd/tuigreet |
|
||||||
| `nomarchy.system.audio.enable` | `true` | Pipewire stack |
|
| `nomarchy.system.audio.enable` | `true` | Pipewire stack |
|
||||||
| `nomarchy.system.bluetooth.enable` | `true` | Bluetooth + blueman |
|
| `nomarchy.system.bluetooth.enable` | `true` | Bluetooth + blueman |
|
||||||
@@ -241,7 +246,8 @@ home-update # rebuild just the desktop layer
|
|||||||
|
|
||||||
Keybinds: `SUPER+Return` terminal · `SUPER+D` launcher · `SUPER+T` theme
|
Keybinds: `SUPER+Return` terminal · `SUPER+D` launcher · `SUPER+T` theme
|
||||||
picker · `SUPER+SHIFT+T` next wallpaper · `SUPER+X` power menu ·
|
picker · `SUPER+SHIFT+T` next wallpaper · `SUPER+X` power menu ·
|
||||||
`SUPER+N` notifications · `SUPER+CTRL+V` clipboard history · `SUPER+Q`
|
`SUPER+E` file manager (yazi) · `SUPER+N` notifications · `SUPER+CTRL+V`
|
||||||
|
clipboard history · `SUPER+Q`
|
||||||
close · `SUPER+1..9` workspaces · `Print` region screenshot.
|
close · `SUPER+1..9` workspaces · `Print` region screenshot.
|
||||||
|
|
||||||
## 6. Extending
|
## 6. Extending
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
./fuzzel.nix # launcher theming + the nomarchy-menu dispatcher
|
./fuzzel.nix # launcher theming + the nomarchy-menu dispatcher
|
||||||
./swaync.nix # notification daemon, themed from the same JSON
|
./swaync.nix # notification daemon, themed from the same JSON
|
||||||
./idle.nix # hyprlock + hypridle, themed from the same JSON
|
./idle.nix # hyprlock + hypridle, themed from the same JSON
|
||||||
|
./yazi.nix # flagship TUI file manager, themed + plugins
|
||||||
];
|
];
|
||||||
|
|
||||||
# Clipboard history (wl-paste watcher); browsed via the SUPER+CTRL+V
|
# Clipboard history (wl-paste watcher); browsed via the SUPER+CTRL+V
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ in
|
|||||||
bind = [
|
bind = [
|
||||||
"$mod, Return, exec, $terminal"
|
"$mod, Return, exec, $terminal"
|
||||||
"$mod, D, exec, fuzzel"
|
"$mod, D, exec, fuzzel"
|
||||||
|
"$mod, E, exec, $terminal -e yazi"
|
||||||
"$mod, Q, killactive"
|
"$mod, Q, killactive"
|
||||||
"$mod, F, fullscreen"
|
"$mod, F, fullscreen"
|
||||||
"$mod, V, togglefloating"
|
"$mod, V, togglefloating"
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
fuzzel.enable = lib.mkEnableOption "Nomarchy's themed fuzzel + the nomarchy-menu dispatcher (power menu)" // { default = true; };
|
fuzzel.enable = lib.mkEnableOption "Nomarchy's themed fuzzel + the nomarchy-menu dispatcher (power menu)" // { default = true; };
|
||||||
swaync.enable = lib.mkEnableOption "swaync notifications, themed from the state file" // { default = true; };
|
swaync.enable = lib.mkEnableOption "swaync notifications, themed from the state file" // { default = true; };
|
||||||
idle.enable = lib.mkEnableOption "hyprlock + hypridle (idle lock, display off, suspend)" // { default = true; };
|
idle.enable = lib.mkEnableOption "hyprlock + hypridle (idle lock, display off, suspend)" // { default = true; };
|
||||||
|
yazi.enable = lib.mkEnableOption "the yazi TUI file manager, themed with a curated plugin set" // { default = true; };
|
||||||
ghostty.enable = lib.mkEnableOption "Nomarchy's Ghostty configuration" // { default = true; };
|
ghostty.enable = lib.mkEnableOption "Nomarchy's Ghostty configuration" // { default = true; };
|
||||||
btop.enable = lib.mkEnableOption "btop with the per-theme nomarchy theme" // { default = true; };
|
btop.enable = lib.mkEnableOption "btop with the per-theme nomarchy theme" // { default = true; };
|
||||||
stylix.enable = lib.mkEnableOption "Stylix theming for the long tail of apps (GTK, Qt, cursors)" // { default = true; };
|
stylix.enable = lib.mkEnableOption "Stylix theming for the long tail of apps (GTK, Qt, cursors)" // { default = true; };
|
||||||
|
|||||||
126
modules/home/yazi.nix
Normal file
126
modules/home/yazi.nix
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# yazi — the flagship file manager: a fast, keyboard-driven TUI that
|
||||||
|
# fits the distro's identity (Ghostty's Kitty-graphics previews, fuzzel
|
||||||
|
# menus, everything from one JSON). Themed from theme-state.json and
|
||||||
|
# shipped with a curated plugin set. The GUI half (Thunar, "open folder"
|
||||||
|
# handler) is nomarchy.system.fileManager on the system side.
|
||||||
|
#
|
||||||
|
# `y` (the shell wrapper) cd's the shell to yazi's last directory on
|
||||||
|
# exit; SUPER+E opens it in a fresh terminal.
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.nomarchy;
|
||||||
|
t = cfg.theme;
|
||||||
|
c = t.colors;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = lib.mkIf cfg.yazi.enable {
|
||||||
|
# Preview + tooling deps yazi shells out to (thumbnails, search,
|
||||||
|
# archives, the plugins below). All small CLIs.
|
||||||
|
home.packages = with pkgs; [
|
||||||
|
ffmpegthumbnailer # video thumbnails
|
||||||
|
poppler-utils # PDF previews (pdftoppm)
|
||||||
|
p7zip # archive preview + the compress plugin
|
||||||
|
jq # JSON preview
|
||||||
|
fd ripgrep fzf # navigation / search integrations
|
||||||
|
chafa # image fallback when the terminal lacks graphics
|
||||||
|
mediainfo # mediainfo plugin (audio/subtitle metadata)
|
||||||
|
glow # glow plugin (markdown rendering)
|
||||||
|
];
|
||||||
|
|
||||||
|
programs.yazi = {
|
||||||
|
enable = true;
|
||||||
|
shellWrapperName = "y"; # `y` cd's on exit; `yazi` stays a no-cd launcher
|
||||||
|
enableBashIntegration = true;
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
mgr = {
|
||||||
|
show_hidden = false;
|
||||||
|
sort_by = "natural";
|
||||||
|
sort_dir_first = true;
|
||||||
|
linemode = "size";
|
||||||
|
};
|
||||||
|
preview = {
|
||||||
|
max_width = 1000;
|
||||||
|
max_height = 1000;
|
||||||
|
};
|
||||||
|
plugin = {
|
||||||
|
# git status in the linemode (fetcher registered before setup).
|
||||||
|
prepend_fetchers = [
|
||||||
|
{ id = "git"; url = "*"; run = "git"; }
|
||||||
|
{ id = "git"; url = "*/"; run = "git"; }
|
||||||
|
];
|
||||||
|
# Route only audio + subtitles to mediainfo's text metadata;
|
||||||
|
# images/videos keep yazi's native visual thumbnails. Markdown
|
||||||
|
# renders through glow.
|
||||||
|
prepend_previewers = [
|
||||||
|
{ name = "*.md"; run = "glow"; }
|
||||||
|
{ mime = "audio/*"; run = "mediainfo"; }
|
||||||
|
{ mime = "application/subrip"; run = "mediainfo"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
keymap.mgr.prepend_keymap = [
|
||||||
|
{ on = "l"; run = "plugin smart-enter"; desc = "Enter dir or open file"; }
|
||||||
|
{ on = "<Enter>"; run = "plugin smart-enter"; desc = "Enter dir or open file"; }
|
||||||
|
{ on = [ "c" "m" ]; run = "plugin chmod"; desc = "Chmod on selected files"; }
|
||||||
|
{ on = "M"; run = "plugin mount"; desc = "Mount/unmount manager"; }
|
||||||
|
{ on = [ "c" "a" "a" ]; run = "plugin compress"; desc = "Archive selected files"; }
|
||||||
|
];
|
||||||
|
|
||||||
|
# Curated plugins (nixpkgs yaziPlugins). setup = true emits the
|
||||||
|
# require(...):setup() call in init.lua; the rest are invoked via
|
||||||
|
# the keymap/previewer/fetcher wiring above.
|
||||||
|
plugins = {
|
||||||
|
full-border = { package = pkgs.yaziPlugins.full-border; setup = true; };
|
||||||
|
git = { package = pkgs.yaziPlugins.git; setup = true; };
|
||||||
|
smart-enter = { package = pkgs.yaziPlugins.smart-enter; };
|
||||||
|
chmod = { package = pkgs.yaziPlugins.chmod; };
|
||||||
|
mount = { package = pkgs.yaziPlugins.mount; };
|
||||||
|
compress = { package = pkgs.yaziPlugins.compress; };
|
||||||
|
mediainfo = { package = pkgs.yaziPlugins.mediainfo; };
|
||||||
|
glow = { package = pkgs.yaziPlugins.glow; };
|
||||||
|
};
|
||||||
|
|
||||||
|
# Theme from the palette. yazi merges this over its built-in theme,
|
||||||
|
# so only the accent-bearing UI is specified; everything else keeps
|
||||||
|
# yazi's defaults (which already follow Ghostty's ANSI colors).
|
||||||
|
theme = {
|
||||||
|
mgr = {
|
||||||
|
cwd = { fg = c.accent; };
|
||||||
|
hovered = { fg = c.base; bg = c.accent; };
|
||||||
|
preview_hovered = { underline = true; };
|
||||||
|
find_keyword = { fg = c.warn; italic = true; };
|
||||||
|
find_position = { fg = c.accentAlt; bg = "reset"; italic = true; };
|
||||||
|
marker_copied = { fg = c.good; bg = c.good; };
|
||||||
|
marker_cut = { fg = c.bad; bg = c.bad; };
|
||||||
|
marker_marked = { fg = c.accent; bg = c.accent; };
|
||||||
|
marker_selected = { fg = c.warn; bg = c.warn; };
|
||||||
|
count_copied = { fg = c.base; bg = c.good; };
|
||||||
|
count_cut = { fg = c.base; bg = c.bad; };
|
||||||
|
count_selected = { fg = c.base; bg = c.warn; };
|
||||||
|
border_symbol = "│";
|
||||||
|
border_style = { fg = c.muted; };
|
||||||
|
};
|
||||||
|
mode = {
|
||||||
|
normal_main = { fg = c.base; bg = c.accent; bold = true; };
|
||||||
|
normal_alt = { fg = c.accent; bg = c.surface; };
|
||||||
|
select_main = { fg = c.base; bg = c.good; bold = true; };
|
||||||
|
select_alt = { fg = c.good; bg = c.surface; };
|
||||||
|
unset_main = { fg = c.base; bg = c.accentAlt; bold = true; };
|
||||||
|
unset_alt = { fg = c.accentAlt; bg = c.surface; };
|
||||||
|
};
|
||||||
|
status = {
|
||||||
|
perm_type = { fg = c.accent; };
|
||||||
|
perm_read = { fg = c.good; };
|
||||||
|
perm_write = { fg = c.bad; };
|
||||||
|
perm_exec = { fg = c.warn; };
|
||||||
|
progress_label = { fg = c.text; bold = true; };
|
||||||
|
progress_normal = { fg = c.accent; bg = c.surface; };
|
||||||
|
progress_error = { fg = c.bad; bg = c.surface; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ let
|
|||||||
cfg = config.nomarchy.system;
|
cfg = config.nomarchy.system;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [ ./options.nix ./plymouth.nix ];
|
imports = [ ./options.nix ./plymouth.nix ./file-manager.nix ];
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
# The safe half of distro branding: distroName flows into
|
# The safe half of distro branding: distroName flows into
|
||||||
|
|||||||
35
modules/nixos/file-manager.nix
Normal file
35
modules/nixos/file-manager.nix
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# GUI file manager — Thunar (lightweight GTK, Stylix-themed) plus the
|
||||||
|
# backend services a standalone compositor needs for it to work fully:
|
||||||
|
# gvfs (trash/mount/network), tumbler (thumbnails), udisks2 (removable
|
||||||
|
# media — also what yazi's mount plugin drives). The keyboard-driven TUI
|
||||||
|
# flagship is yazi (modules/home/yazi.nix); this is the point-and-click
|
||||||
|
# half and the XDG handler for "open folder" / "show in files".
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.nomarchy.system;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = lib.mkIf cfg.fileManager.enable {
|
||||||
|
programs.thunar = {
|
||||||
|
enable = lib.mkDefault true;
|
||||||
|
plugins = with pkgs.xfce; [
|
||||||
|
thunar-archive-plugin # create/extract archives from the context menu
|
||||||
|
thunar-volman # auto-manage removable drives/media
|
||||||
|
thunar-media-tags-plugin # rename/inspect audio by tags
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.gvfs.enable = lib.mkDefault true; # trash, mounts, network shares
|
||||||
|
services.tumbler.enable = lib.mkDefault true; # thumbnail generation
|
||||||
|
services.udisks2.enable = lib.mkDefault true; # mount/unmount removable media
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
ffmpegthumbnailer # video thumbnails (Thunar + yazi)
|
||||||
|
libgsf # ODF thumbnails
|
||||||
|
poppler-utils # PDF thumbnails / pdftoppm (yazi PDF preview too)
|
||||||
|
xfce.exo # "Open Terminal Here" and default-app associations
|
||||||
|
shared-mime-info # MIME database
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -37,6 +37,12 @@
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fileManager.enable = lib.mkEnableOption ''
|
||||||
|
the Thunar GUI file manager + backend services (gvfs/tumbler/udisks2)
|
||||||
|
for point-and-click file management and the "open folder" handler.
|
||||||
|
The keyboard-driven TUI flagship (yazi) is the nomarchy.yazi.* home
|
||||||
|
option'' // { default = true; };
|
||||||
|
|
||||||
audio.enable = lib.mkEnableOption "the Pipewire audio stack" // { default = true; };
|
audio.enable = lib.mkEnableOption "the Pipewire audio stack" // { default = true; };
|
||||||
bluetooth.enable = lib.mkEnableOption "Bluetooth support with blueman" // { default = true; };
|
bluetooth.enable = lib.mkEnableOption "Bluetooth support with blueman" // { default = true; };
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user