How to install xdg-desktop-portal-termfilechooser?

uhh… so i gave it a proper go this time…

with the way i installed it, it:

  1. did not start as a service
  2. waits for a mysterious .toml config that it did not generate…

and i tried many things. i “quickly” realised that this portal is ALSO a cli command, so i found that it requires a config path (which is actually a file, not a path). there was no example template of a .toml that it expects, i tried manually starting it with systemd.services., i tried adding it to xdg.portal.config. (which it should do by itself, according to the repo)… with the latter making it actually override the gtk portal, but it just cant do anything (as in, nothing happens lol)… i tried literally testing zenity --file-selection, but nothing happened, obviously… i also tried placing those two nushell shebangs that i found in the repo into $XDG_CONFIG_HOME/xdg-desktop-portal-termfilepickers/ which i have done something similar previously with termfilechooser, but… .toml is missing, still!

that is all without the home manager. i mean, i always try everything without the home manager first, but this time its a rather steep skill check… or its just too raw atm :crazy_face: oh, i also did not try whatever that example is… all what i EXPECT from it is to have this portal just work, yknow, when you place the flake input to the xdg.portal.extraPortals… like literally everything else. oh, yeah, and options for declarative configuration, which will also require one to put its input to modules… i hope this makes sense, im really bad at explaining anything nix, but i just know, trust :sunglasses: (ill share the setup here if itll work out)

all in all, i had a better experience with termfilechooser, though… i literally just deleted termfilechooser config by “accident”, so… im left with gtk yet again. ffs. okay. ill be back with a nicer TUI open/save setup. but yes pls pls im patiently waiting for the documentation on this portal made in rust aaaaah!!

and nevermind abt cachix! it just kinda scared me when it started building for every platform again…

I had a look at your issue, and I can confirm the NixOS module is not working out of the box. I am currently investigating. But the HM module works correctly on my side, though

1 Like

Hey, maintainer of termfilechooser here. Just wanted to say that almost all of those errors/warnings are fine except for the filechooser: failed to open ... line.

That specific error is kind of generic since multiple things can cause it. Some examples being incorrect permissions or the wrapper script failing (usually this one).

As for only firefox/librewolf working, that is unique…
If you’re still willing to troubleshoot, feel free to open an issue on the repo (GitHub - hunkyburrito/xdg-desktop-portal-termfilechooser: xdg-desktop-portal backend for choosing files with your favorite file chooser) and I’ll help to the best of my abililties :slight_smile:

2 Likes

(bruh discourse deleted my concept message so im writing this anew…)

alright! time to test termfilechooser AND termfilepickers for the 9000th time! (cos it didnt really work last time for me…)

configuration.nix

  xdg.portal.extraPortals = [
    pkgs.xdg-desktop-portal-termfilechooser
  ];

expectation: should work out of the box! :smiley_cat:

reality: the portal service doesnt even start! :crying_cat_face:

EDIT: not until i added this:

  xdg.portal.config.common."org.freedesktop.impl.portal.FileChooser" = [ "termfilechooser" ];

anyway, to make it work, i also had to MANUALLY create configs:

$HOME/.config/xdg-desktop-portal-termfilechooser/config

  [filechooser]
  cmd=yazi-wrapper.sh
  default_dir=$HOME
  ; Uncomment to skip creating destination save files with instructions in them
  ; create_help_file=0

  ; Mode must be one of 'suggested', 'default', or 'last'.
  open_mode=suggested
  save_mode=last

i removed the env=TERMCMD=foot because see below:

$HOME/.config/xdg-desktop-portal-termfilechooser/yazi-wrapper.sh

    #!/usr/bin/env sh
    # This wrapper script is invoked by xdg-desktop-portal-termfilechooser.
    #
    # For more information about input/output arguments read `xdg-desktop-portal-termfilechooser(5)`

    set -e

    if [ "$6" -ge 4 ]; then
      set -x
    fi

    multiple="$1"
    directory="$2"
    save="$3"
    path="$4"
    out="$5"

    cmd="yazi"
    termcmd="wezterm start --always-new-process"

    if [ "$save" = "1" ]; then
      # save a file
      set -- --chooser-file="$out" "$path"
    elif [ "$directory" = "1" ]; then
      # upload files from a directory
      set -- --chooser-file="$out" --cwd-file="$out"".1" "$path"
    elif [ "$multiple" = "1" ]; then
      # upload multiple files
      set -- --chooser-file="$out" "$path"
    else
      # upload only 1 file
      set -- --chooser-file="$out" "$path"
    fi

    command="$termcmd $cmd"
    for arg in "$@"; do
      # escape double quotes
      escaped=$(printf "%s" "$arg" | sed 's/"/\\"/g')
      # escape special
      case "$termcmd" in
      *"ghostty"*)
        command="$command \"\\\"$escaped\\\"\"";;
      *)
        command="$command \"$escaped\"";;
      esac
    done

    sh -c "$command"

    if [ "$directory" = "1" ]; then
      if [ ! -s "$out" ] && [ -s "$out"".1" ]; then
        cat "$out"".1" > "$out"
        rm "$out"".1"
      else
        rm "$out"".1"
      fi
    fi

termcmd with ${TERMCMD} was spitting errors, saying it is “deprecated” on rebulid (i am using nix-maid). anyway, i am also using pkgs.wezterm! i dont care about the terminal title, but one could use wezterm cli set-window-title TITLE, just remember the --always-new-process!

make it executable!

chmod +x yazi-wrapper.sh

finally, remember the about:config (firefox/librewolf) property:

widget.use-xdg-desktop-portal.file-picker = 1 # https://searchfox.org/firefox-main/source/modules/libpref/init/StaticPrefList.yaml#18949

and NOW it WORKS! but not everywhere… some apps use their own portals? there might be a way to change this, but i either forgor the variable, or it’s way too jank and probably not worth it.

but is it scary to use it! IT IS! for instance, when you download something and pick the location, you MANUALLY pick a name for the file you want to download. no file extension provided, you do it yourself, so make sure you know what kind of… file extension you are downloading - though, you can use another compatible extension, for example, if its a .png, but you create a file called haha.jpeg - it will still work.

anyway, the way it does things:

  • A) pick the example file which automatically generates with the name and extension which would’ve been used were you to use a GUI, like via browser or
  • B) YOU manually create a file with the extension that you type in (via your TUI file manager or beforehand somewhere else) or
  • C) you OVERWRITE an existing file, completely, with the new downloaded contents.

which sounds a bit scary, especially when working with multiple files! ESPECIALLY because TUI file managers can select DIRECTORIES. what if you overwrite A DIRECTORY???!!! but still, if one never wanna leave their trusty terminal, this is good, i think? :innocent:

sucks that it doesnt create the config files (and wrappers!) itself though… that’s a few points taken away! :pouting_cat:


…moving on to termfilepickers! (plural?)

it is a flake btw, and it has a module (options for configuration.nix), which is pretty good (ive talked about it a few posts above, btw and sry, i was a bit dumb, but im learning things!)

configuration.nix

  services.xdg-desktop-portal-termfilepickers = { # flake!
    enable = true; # https://github.com/Guekka/xdg-desktop-portal-termfilepickers
    package = inputs.xdg-desktop-portal-termfilepickers.packages.${pkgs.system}.default;
    # desktopEnvironments = [ "common" ]; # "*"
    config = {
      # open_file_script_path = "$HOME/.local/share/wrappers/yazi-open-file.nu";
      # save_file_script_path = "$HOME/.local/share/wrappers/yazi-save-file.nu"; # singular
      # save_files_script_path = "$HOME/.local/share/wrappers/yazi-save-file.nu"; # multiple
      terminal_command = [ (lib.getExe pkgs.wezterm) "start" "--always-new-process" ];
    };
  };

EDIT: uhm. apparently the paths are not of type absolute path? hmm…

i also (just in case) put the (only!) two nushell wrappers in $HOME/.config/xdg-desktop-portal-termfilepickers/ and made them executable, ofc

rebuilt, rebooted, and the xdg-desktop-portal-termfilepickers.service is up and running! doing good so far… BUT SUDDENLY!

testing zenity --file-selection

Unable to spawn yazi because:
No viable candidates found in PATH "/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0/bin:/nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12/bin:/nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9/bin:/nix/store/acjdidq41qig9khxcm7gx1d7brzjs249-systemd-257.8/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/sbin:/nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0/sbin:/nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12/sbin:/nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9/sbin:/nix/store/acjdidq41qig9khxcm7gx1d7brzjs249-systemd-257.8/sbin"
⚠️  Process "yazi --chooser-file /tmp/.tmpEW6gQk" in domain "local" didn't exit cleanly
Exited with code 1.
This message is shown because exit_behavior="CloseOnCleanExit"

testing downloading a random image from librewolf browser:

Unable to spawn yazi because:
No viable candidates found in PATH "/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0/bin:/nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12/bin:/nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9/bin:/nix/store/acjdidq41qig9khxcm7gx1d7brzjs249-systemd-257.8/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/sbin:/nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0/sbin:/nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12/sbin:/nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9/sbin:/nix/store/acjdidq41qig9khxcm7gx1d7brzjs249-systemd-257.8/sbin"
⚠️  Process "yazi --chooser-file /tmp/.tmpnfolUw /home/deck/Downloads/test.png" in domain "local" didn't exit cleanly
Exited with code 1.
This message is shown because exit_behavior="CloseOnCleanExit"

yeah. dead end. SPECULATION TIME:

  1. nushell moment! maybe this helps (the part where they talk about the $PATH?)
  2. i am using (lib.hiPrio pkgs.uutils-coreutils-noprefix) btw, does termfilepickers only works with GNU pkgs.coreutils/pkgs.findutils? surely not, right? im too lazy to check!

well… it kinda works but… only with pkgs.nushell (by default). so that’s gonna be… tsk… sigh… a few points taken away. :triumph:

dont get me wrong, i LOVE nushell, but i am right now in a scenario where i dont have one set up, at all.

or am i dumb again? idk. sorry, i tried making it work… :sob:

Does it start automatically on startup/reboot?

termfilechooser doesn’t make configs/wrappers in the configuration directory by default, but it should be making example files in whatever the data directory is for NixOS (e.g. /usr/share/xdg-desktop-portal-termfilechooser is the default on Arch). It uses those by default unless it finds your own user configs/wrappers.

Honestly not sure on this one; that’s just normal shell parameter substitution. If you can send over the errors it’s spitting out I can look more into how to fix that.

Yeah, unfortunately there are many apps that don’t use portals by default, so you have to set environment variables that tell them to use the portal. For example:

  • GDK_DEBUG=portals: for newer GTK apps
  • GTK_USE_PORTAL=1: for older GTK apps
  • QT_QPA_PLATFORMTHEME=flatpak or QT_QPA_PLATFORMTHEME=xdgdesktopportal for Qt apps (most don’t need this, but some like Telegram do for instance)

Generally, it’s easier to just cut/copy the helper file it creates to another location and overwrite it. That way you don’t have to worry about file extensions, typing out the name, etc. You can of course just make new files, but like you said, you run the risk of confusing yourself later if the extension doesn’t match what the file actually is.

Good point; at the moment it usually tries but fails, so data loss shouldn’t ever happen. Now that you bring that up though I think I’ll add some checks to make sure that it fails immediately if you select a directory for saving a file.

1 Like

UGHH! i COMPLETELY forgot! im gonna edit that in…

not until i added this:

  xdg.portal.config.common."org.freedesktop.impl.portal.FileChooser" = [ "termfilechooser" ];

my bad.

WHOOPS, wrong one! lol

uhh… well, it’s probably just nix screaming, oh, im sure it will work imperatively, but i am placing it inside a nix file, so… perhaps something happens at evaluation or SOMETHING, right?

if i change that termcmd line, well, back to MATCH the default:

termcmd="${TERMCMD:-wezterm start --always-new-process}"

it will say this (again, im pretty sure it’s no one’s fault):

error:
       … while calling the 'seq' builtin
         at /nix/store/9rfm3gjhphdvi7qnzdsi7piw1gi4c8vp-source/lib/modules.nix:361:18:
          360|         options = checked options;
          361|         config = checked (removeAttrs config [ "_module" ]);
             |                  ^
          362|         _module = checked (config._module);

       … while evaluating an expression to select '_module.check' on it
         at /nix/store/9rfm3gjhphdvi7qnzdsi7piw1gi4c8vp-source/lib/modules.nix:297:12:
          296|       checkUnmatched =
          297|         if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [ ] then
             |            ^
          298|           let

       (stack trace truncated; use '--show-trace' to show the full trace)

       error: URL literals are deprecated, allow using them with --extra-deprecated-features url-literals
       at /nix/store/wjm8w6hfwhm9zhmmbhs6hciz2kv7y0bg-source/configuration.nix:6885:16:
         6884|     cmd="yazi"
         6885|     termcmd="${TERMCMD:-wezterm start --always-new-process}"
             |                ^
         6886|

...

Error:
   0: Failed to build configuration
   1: Command exited with status Exited(1)

i think it’s nix-maid doing something weird here. i probably should report this to viperML, but i dont have a github, so… im not actually sure where that is from, even??? but dont worry about it!

EDIT: MAYBE i could like… do something like this?

termcmd="\$\{TERMCMD\:-wezterm start --always-new-process\}"

i had a similar problem when i was writing an entire .zshrc into the programs.zsh.shellInit

im not ashamed, here’s one of the problematic lines i have inside my configuration.nix:

zstyle ':completion:*:default' list-colors \$\{\(s\.\:\.\)LS_COLORS\}

anyway, i have no clue lol

btw, thanks, very useful! thats a weird one - xdgdesktopportal

From that error message it seems that nix thinks it’s a url to a website, due to the ‘:’ I’m guessing? I don’t know enough about Nix to know how to properly escape that tbh. Anyway, you could just change it to termcmd="$TERMCMD" instead and then set the $TERMCMD environment variable globally to whatever terminal you use.

1 Like

I’ve gotten a terminal filepicker working globally through yazi or ranger a couple times on wayland/sway, and it had worked across all gui apps and all websites in chromium and firefox that I tried.

Except for https://web.whatsapp.com/ in chromium (I forgot to try it in firefox)

Trying to upload a file into a convo summons the terminal filepicker, but selecting a file ends up no-oping even though dbus showed it sending the right file paths.

I reverted my config (since I can never get past this issue) so I can’t share it. But I’m curious if anyone using a terminal filepicker can try it out in https://web.whatsapp.com/ (in chromium at least).

The default GTK file picker, on the other hand, did work.

I can return with actual config once I try again, but it’s not much different that what I’m seeing in this thread.

I’ve been tryna make it work but still can’t for some reasons even after reading this thread, maybe i missed sum idk

this is how i configured it btw

{ pkgs, ... }:

{
  xdg = {
    enable = true;

    portal = {
      enable = true;
      extraPortals = with pkgs; [
        xdg-desktop-portal-hyprland
        xdg-desktop-portal-termfilechooser
        xdg-desktop-portal-gtk
      ];
      # configPackages = [ pkgs.hyprland ];
    };
  };

  xdg.portal.config = { 
    common = {
      "org.freedesktop.impl.portal.FileChooser" = "termfilechooser";
    };
    hyprland = {
      default = [
        "hyprland"
        "gtk"
      ];
    "org.freedesktop.impl.portal.FileChooser" = [ "termfilechooser" ];
    };
  };

  xdg.configFile = {
    "xdg-desktop-portal-termfilechooser/config" = {
      force = true;
      text = ''
        [filechooser]
        cmd=yazi-wrapper.sh
        default_dir=$HOME
        env=TERMCMD=ghostty --title="terminal-filechooser" -e
        open_mode = suggested
        save_mode = last
      '';
    };

    "xdg-desktop-portal-termfilechooser/yazi-wrapper.sh" = {
      source = ./yazi-wrapper.sh;
    };
  };
}

Anyone successfully using it with yazi?

I managed to get it working with niri and foot using this:

Nixos config:

xdg = {
  portal = {
    enable = true;

    extraPortals = with pkgs; [
      xdg-desktop-portal-gtk
      xdg-desktop-portal-termfilechooser
    ];

    config.common."org.freedesktop.impl.portal.FileChooser" = [ "termfilechooser" ];
  };
};

Home-manager config(Nixos Module):

xdg.configFile."xdg-desktop-portal-termfilechooser/config" = {
  enable = true;
  text = ''
    [filechooser]
    cmd=${pkgs.xdg-desktop-portal-termfilechooser}/share/xdg-desktop-portal-termfilechooser/yazi-wrapper.sh
    default_dir=$HOME
    env=TERMCMD='foot -T "terminal filechooser"'
    open_mode=suggested
    save_mode=last
  '';
};

And in Firefox

widget.use-xdg-desktop-portal.file-picker = 1
2 Likes

this worked for me but just now it decided to just die like ok ig :wilted_flower::wilted_flower:

For everything ? It’s not working for some apps, like Thunderbird. I’m guessing more configuration is needed

yea for everything, im starting to think it might be related to xdp itselfcuz i can’t even open the default gtk one anymore

which i just fixed by inversing my portal orde in my niri portal config

Should I have both of these lines in NixOS and Home Manager modules?

It worked for chrome, gtk apps.
How do you set it for Qt apps like Telegram?

Also, the thing with this one is that xdg-desktop-portal-gnome doesn’t launch now, and because of this, screen sharing stops working.

That’s how I made Telegram work.

environment.sessionVariables = {
    8  ┊ ┊ WLR_RENDERER = "vulkan";
    9  ┊ ┊ # WAYLAND_DISPLAY = "wayland-1";
   10  ┊ ┊ QT_QPA_PLATFORM = "wayland";
   11  ┊ ┊ QT_QPA_PLATFORMTHEME = "xdgdesktopportal";
   12  ┊ ┊ TDESKTOP_USE_GTK_FILE_DIALOG = 1;
   13  ┊ ┊ QT_WAYLAND_DISABLE_WINDOWDECORATION = 1;
   14  ┊ ┊ QT_SCALE_FACTOR = 1;
   15  ┊ ┊ QT_AUTO_SCREEN_SCALE_FACTOR = 1;
   16  ┊ ┊ GDK_BACKEND = "wayland";
   17  ┊ ┊ GTK_USE_PORTAL = 1;
   18  ┊ ┊ GDK_DEBUG = "portals";
   19  ┊ ┊ GDK_SCALE = 1;
   20  ┊ ┊ XDG_SESSION_TYPE = "wayland";
   21  ┊ ┊ XDG_SESSION_DESKTOP = "niri";
   22  ┊ ┊ XDG_CURRENT_DESKTOP = "niri";
   23  ┊ ┊ WINIT_UNIX_BACKEND = "wayland";
   24  ┊ ┊ #If your cursor becomes invisible
   25  ┊ ┊ # WLR_NO_HARDWARE_CURSORS = "1";
   26  ┊ ┊ #Hint electron apps to use wayland
   27  ┊ ┊ NIXOS_OZONE_WL = 1;
   28  ┊ ┊ # NIXOS_NO_CHECK = 1;
   29  ┊ ┊ # NIXOS_XDG_OPEN_USE_PORTAL = 1;
   30  ┊ ┊ EDITOR = "hx";
   31  ┊ ┊ SYSTEMD_EDITOR = "hx";
   32  ┊ ┊ VISUAL = "hx";
   33  ┊ ┊ BROWSER = "firefox";
   34  ┊ ┊ TERMINAL = "ghostty";
   35  ┊ ┊ MOZ_ENABLE_WAYLAND = 1;
   36  ┊ ┊ # MOZ_DISABLE_RDD_SANDBOX = "1";
   37  ┊ };

Home-manager module or system.

  xdg = {
    enable = true;
    configFile = {
      "xdg-desktop-portal-termfilechooser/config" = {
        enable = true;
        force = true;
        text = ''
          [filechooser]
          cmd=${pkgs.xdg-desktop-portal-termfilechooser}/share/xdg-desktop-portal-termfilechooser/yazi-wrapper.sh
          default_dir=$HOME/Downloads
          env=TERMCMD=ghostty --title="terminal-filechooser" -e
          open_mode=suggested
          save_mode=last
        '';
      };
    };
    terminal-exec = {
      enable = true;
      settings = {
        default = [
          "com.mitchellh.ghostty.desktop"
          # "wezterm.desktop"
        ];
      };
    };
    portal = {
      enable = true;
      # xdgOpenUsePortal = true;
      extraPortals = with pkgs; [
        xdg-desktop-portal-termfilechooser
      ];
      config = {
        common = {
          default = [
            "niri"
            "gtk"
            "gnome"
            "kde"
          ];
          "org.freedesktop.impl.portal.FileChooser" = [ "termfilechooser" ];
        };
        niri = lib.mkForce {
          default = [
            "kde"
            "gnome"
            "gtk"
            "niri"
          ];
          "org.freedesktop.impl.portal.FileChooser" = [ "termfilechooser" ];
          "org.freedesktop.impl.portal.Access" = "gtk";
          "org.freedesktop.impl.portal.Notification" = "gtk";
          "org.freedesktop.impl.portal.Secret" = "gnome-keyring";

        };
      };

    };

Which lines are you referring to ?

I had this issue yesterday, my updated code (For niri)

nixos:

xdg.portal = {
  enable = true;
  xdgOpenUsePortal = true;
  extraPortals = with pkgs; [
    xdg-desktop-portal-termfilechooser
    xdg-desktop-portal-gtk
    xdg-desktop-portal-gnome
  ];

  config.common = {
    "org.freedesktop.impl.portal.FileChooser" = "termfilechooser";
    "org.freedesktop.impl.portal.ScreenCast" = "gnome";
    "org.freedesktop.impl.portal.Screenshot" = "gnome";
  };
};

I mean both. Do I have to have both of them to have a working termfilechooser?