From 23b63bfdfb5ed2700441f13429cbd1cd0c2ea870 Mon Sep 17 00:00:00 2001
From: tv <tv@krebsco.de>
Date: Sat, 4 Feb 2023 21:52:14 +0100
Subject: [PATCH] flameshot-once: reinit with flameshot 12.1.0-pre

---
 krebs/5pkgs/simple/flameshot-once/config.nix  | 408 ++++++++++++++++++
 krebs/5pkgs/simple/flameshot-once/default.nix |  71 +++
 .../flameshot-once/flameshot/default.nix      |  15 +
 .../flameshot/flameshot-12.imgur.patch        |  43 ++
 lib/types.nix                                 |   5 +-
 tv/5pkgs/haskell/flameshot-once.nix           |  20 -
 tv/5pkgs/override/flameshot/default.nix       |  15 -
 .../flameshot/flameshot_imgur_0.10.2.patch    |  35 --
 tv/5pkgs/simple/flameshot-once/default.nix    |  28 --
 tv/5pkgs/simple/flameshot-once/profile.nix    | 235 ----------
 10 files changed, 541 insertions(+), 334 deletions(-)
 create mode 100644 krebs/5pkgs/simple/flameshot-once/config.nix
 create mode 100644 krebs/5pkgs/simple/flameshot-once/default.nix
 create mode 100644 krebs/5pkgs/simple/flameshot-once/flameshot/default.nix
 create mode 100644 krebs/5pkgs/simple/flameshot-once/flameshot/flameshot-12.imgur.patch
 delete mode 100644 tv/5pkgs/haskell/flameshot-once.nix
 delete mode 100644 tv/5pkgs/override/flameshot/default.nix
 delete mode 100644 tv/5pkgs/override/flameshot/flameshot_imgur_0.10.2.patch
 delete mode 100644 tv/5pkgs/simple/flameshot-once/default.nix
 delete mode 100644 tv/5pkgs/simple/flameshot-once/profile.nix

diff --git a/krebs/5pkgs/simple/flameshot-once/config.nix b/krebs/5pkgs/simple/flameshot-once/config.nix
new file mode 100644
index 000000000..817c700a1
--- /dev/null
+++ b/krebs/5pkgs/simple/flameshot-once/config.nix
@@ -0,0 +1,408 @@
+{ config, pkgs, ... }:
+with pkgs.stockholm.lib;
+
+let
+  # Encode integer to C-escaped string of bytes, little endian / LSB 0
+  le = rec {
+    x1 = i: let
+      i0 = mod i 16;
+      i1 = i / 16;
+    in
+      if i == 0 then
+        "\\0"
+      else if i < 16 then
+        "\\x${elemAt hexchars i0}"
+      else
+        "\\x${elemAt hexchars i1}${elemAt hexchars i0}";
+
+    x2 = i: let
+      i0 = mod i 256;
+      i1 = i / 256;
+    in
+      "${x1 i1}${x1 i0}";
+
+    x4 = i: let
+      i0 = mod i 65536;
+      i1 = i / 65536;
+    in
+      "${x2 i1}${x2 i0}";
+  };
+
+  toQList = t: xs:
+    assert t == "int";
+    "QList<${t}>${le.x4 0}${le.x1 (length xs)}${concatMapStrings le.x4 xs}";
+in
+
+{
+  options = {
+    imgur = mkOption {
+      default = {};
+      type = types.submodule {
+        options = {
+          enable = mkEnableOption "imgur";
+          createUrl = mkOption {
+            example = "http://p.r/image";
+            type = types.str;
+          };
+          deleteUrl = mkOption {
+            example = "http://p.r/image/delete/%1";
+            type = types.str;
+          };
+          xdg-open = mkOption {
+            default = {};
+            type = types.submodule {
+              options = {
+                enable = mkEnableOption "imgur.xdg-open" // {
+                  default = true;
+                };
+                browser = mkOption {
+                  default = "${pkgs.coreutils}/bin/false";
+                  type = types.str;
+                };
+                createPrefix = mkOption {
+                  default = config.imgur.createUrl;
+                  type = types.str;
+                };
+                deletePrefix = mkOption {
+                  default = removeSuffix "/%1" config.imgur.deleteUrl;
+                  type = types.str;
+                };
+              };
+            };
+          };
+        };
+      };
+    };
+    package = mkOption {
+      type = types.package;
+      default = import ./flameshot { inherit pkgs; };
+    };
+    settings = {
+      # Options without a description are not documented in flameshot's README.
+      General = mapAttrs (_: recursiveUpdate { default = null; }) {
+        allowMultipleGuiInstances = mkOption {
+          description = ''
+            Allow multiple instances of `flameshot gui` to run at the same time
+          '';
+          type = with types; nullOr bool;
+        };
+        antialiasingPinZoom = mkOption {
+          description = ''
+            Anti-aliasing image when zoom the pinned image
+          '';
+          type = with types; nullOr bool;
+        };
+        autoCloseIdleDaemon = mkOption {
+          description = ''
+            Automatically close daemon when it's not needed
+          '';
+          type = with types; nullOr bool;
+        };
+        buttons = let
+          buttonTypes = {
+            TYPE_PENCIL = 0;
+            TYPE_DRAWER = 1;
+            TYPE_ARROW = 2;
+            TYPE_SELECTION = 3;
+            TYPE_RECTANGLE = 4;
+            TYPE_CIRCLE = 5;
+            TYPE_MARKER = 6;
+            TYPE_SELECTIONINDICATOR = 7;
+            TYPE_MOVESELECTION = 8;
+            TYPE_UNDO = 9;
+            TYPE_COPY = 10;
+            TYPE_SAVE = 11;
+            TYPE_EXIT = 12;
+            TYPE_IMAGEUPLOADER = 13;
+            TYPE_OPEN_APP = 14;
+            TYPE_PIXELATE = 15;
+            TYPE_REDO = 16;
+            TYPE_PIN = 17;
+            TYPE_TEXT = 18;
+            TYPE_CIRCLECOUNT = 19;
+            TYPE_SIZEINCREASE = 20;
+            TYPE_SIZEDECREASE = 21;
+            TYPE_INVERT = 22;
+            TYPE_ACCEPT = 23;
+          };
+          iterableButtonTypes = [
+            "TYPE_ACCEPT"
+            "TYPE_ARROW"
+            "TYPE_CIRCLE"
+            "TYPE_CIRCLECOUNT"
+            "TYPE_COPY"
+            "TYPE_DRAWER"
+            "TYPE_EXIT"
+            "TYPE_IMAGEUPLOADER"
+            "TYPE_MARKER"
+            "TYPE_MOVESELECTION"
+            "TYPE_OPEN_APP"
+            "TYPE_PENCIL"
+            "TYPE_PIN"
+            "TYPE_PIXELATE"
+            "TYPE_RECTANGLE"
+            "TYPE_REDO"
+            "TYPE_SAVE"
+            "TYPE_SELECTION"
+            "TYPE_SIZEDECREASE"
+            "TYPE_SIZEINCREASE"
+            "TYPE_TEXT"
+            "TYPE_UNDO"
+          ];
+        in mkOption {
+          apply = names:
+            if names != null then let
+              values = map (name: buttonTypes.${name}) names;
+            in
+              ''@Variant(\0\0\0\x7f\0\0\0\v${toQList "int" values})''
+            else
+              null;
+          description = ''
+            Configure which buttons to show after drawing a selection
+          '';
+          type = with types; nullOr (listOf (enum iterableButtonTypes));
+        };
+        checkForUpdates = mkOption {
+          type = with types; nullOr bool;
+        };
+        contrastOpacity = mkOption {
+          description = ''
+            Opacity of area outside selection
+          '';
+          type = with types; nullOr (boundedInt 0 255);
+        };
+        contrastUiColor = mkOption {
+          description = ''
+            Contrast UI color
+          '';
+          type = with types; nullOr flameshot.color;
+        };
+        copyAndCloseAfterUpload = mkOption {
+          type = with types; nullOr bool;
+        };
+        copyOnDoubleClick = mkOption {
+          type = with types; nullOr bool;
+        };
+        copyPathAfterSave = mkOption {
+          description = ''
+            Copy path to image after save
+          '';
+          type = with types; nullOr bool;
+        };
+        copyURLAfterUpload = mkOption {
+          description = ''
+            On successful upload, close the dialog and copy URL to clipboard
+          '';
+          type = with types; nullOr bool;
+        };
+        disabledTrayIcon = mkOption {
+          description = ''
+            Whether the tray icon is disabled
+          '';
+          type = with types; nullOr bool;
+        };
+        drawColor = mkOption {
+          description = ''
+            Last used color
+          '';
+          type = with types; nullOr flameshot.color;
+        };
+        drawFontSize = mkOption {
+          type = with types; nullOr positive;
+        };
+        drawThickness = mkOption {
+          description = ''
+            Last used tool thickness
+          '';
+          type = with types; nullOr positive;
+        };
+        filenamePattern = mkOption {
+          description = ''
+            Filename pattern using C++ strftime formatting
+          '';
+          type =
+            # This is types.filename extended by [%:][%:+]*
+            with types;
+            nullOr (addCheck str (test "[%:0-9A-Za-z._][%:+0-9A-Za-z._-]*"));
+        };
+        fontFamily = mkOption {
+          type = with types; nullOr str;
+        };
+        historyConfirmationToDelete = mkOption {
+          type = with types; nullOr bool;
+        };
+        ignoreUpdateToVersion = mkOption {
+          description = ''
+            Ignore updates to versions less than this value
+          '';
+          type = with types; nullOr str;
+        };
+        keepOpenAppLauncher = mkOption {
+          description = ''
+            Keep the App Launcher open after selecting an app
+          '';
+          type = with types; nullOr bool;
+        };
+        predefinedColorPaletteLarge = mkOption {
+          description = ''
+            Use larger color palette as the default one
+          '';
+          type = with types; nullOr bool;
+        };
+        saveAfterCopy = mkOption {
+          description = ''
+            Save image after copy
+          '';
+          type = with types; nullOr bool;
+        };
+        saveAsFileExtension = mkOption {
+          description = ''
+            Default file extension for screenshots
+          '';
+          type = with types; nullOr (addCheck filename (hasPrefix "."));
+        };
+        safeLastRegion = mkOption {
+          type = with types; nullOr bool;
+        };
+        savePath = mkOption {
+          description = ''
+            Image Save Path
+          '';
+          type = with types; nullOr absolute-pathname;
+        };
+        savePathFixed = mkOption {
+          description = ''
+            Whether the savePath is a fixed path
+          '';
+          type = with types; nullOr bool;
+        };
+        showDesktopNotification = mkOption {
+          description = ''
+            Show desktop notifications
+          '';
+          type = with types; nullOr bool;
+        };
+        showHelp = mkOption {
+          description = ''
+            Show the help screen on startup
+          '';
+          type = with types; nullOr bool;
+        };
+        showMagnifier = mkOption {
+          type = with types; nullOr bool;
+        };
+        showSelectionGeometry = mkOption {
+          type = with types; nullOr (boundedInt 0 5);
+        };
+        showSelectionGeometryHideTime = mkOption {
+          type = with types; nullOr uint;
+        };
+        showSidePanelButton = mkOption {
+          description = ''
+            Show the side panel button
+          '';
+          type = with types; nullOr bool;
+        };
+        showStartupLaunchMessage = mkOption {
+          type = with types; nullOr bool;
+        };
+        squareMagnifier = mkOption {
+          type = with types; nullOr bool;
+        };
+        startupLaunch = mkOption {
+          description = ''
+            Launch at startup
+          '';
+          type = with types; nullOr bool;
+        };
+        uiColor = mkOption {
+          description = ''
+            Main UI color
+          '';
+          type = with types; nullOr flameshot.color;
+        };
+        undoLimit = mkOption {
+          type = with types; nullOr (boundedInt 0 999);
+        };
+        uploadClientSecret = mkOption {
+          type = with types; nullOr str;
+        };
+        uploadHistoryMax = mkOption {
+          type = with types; nullOr uint;
+        };
+        uploadWithoutConfirmation = mkOption {
+          description = ''
+            Upload to imgur without confirmation
+          '';
+          type = with types; nullOr bool;
+        };
+        useJpgForClipboard = mkOption {
+          description = ''
+            Use JPG format instead of PNG
+          '';
+          type = with types; nullOr bool;
+        };
+        userColors = mkOption {
+          apply = value:
+            if value != null then
+              concatStringsSep ", " value
+            else
+              null;
+          description = ''
+            List of colors for color picker
+            The colors are arranged counter-clockwise with the first being set
+            to the right of the cursor.  "picker" adds a custom color picker.
+          '';
+          type =
+            with types;
+            nullOr (listOf (either flameshot.color (enum ["picker"])));
+        };
+      };
+      Shortcuts = genAttrs [
+        "TYPE_ACCEPT"
+        "TYPE_ARROW"
+        "TYPE_CIRCLE"
+        "TYPE_CIRCLECOUNT"
+        "TYPE_COMMIT_CURRENT_TOOL"
+        "TYPE_COPY"
+        "TYPE_DELETE_CURRENT_TOOL"
+        "TYPE_DRAWER"
+        "TYPE_EXIT"
+        "TYPE_IMAGEUPLOADER"
+        "TYPE_INVERT"
+        "TYPE_MARKER"
+        "TYPE_MOVESELECTION"
+        "TYPE_MOVE_DOWN"
+        "TYPE_MOVE_LEFT"
+        "TYPE_MOVE_RIGHT"
+        "TYPE_MOVE_UP"
+        "TYPE_OPEN_APP"
+        "TYPE_PENCIL"
+        "TYPE_PIN"
+        "TYPE_PIXELATE"
+        "TYPE_RECTANGLE"
+        "TYPE_REDO"
+        "TYPE_RESIZE_DOWN"
+        "TYPE_RESIZE_LEFT"
+        "TYPE_RESIZE_RIGHT"
+        "TYPE_RESIZE_UP"
+        "TYPE_SAVE"
+        "TYPE_SELECTION"
+        "TYPE_SELECTIONINDICATOR"
+        "TYPE_SELECT_ALL"
+        "TYPE_SIZEDECREASE"
+        "TYPE_SIZEINCREASE"
+        "TYPE_SYM_RESIZE_DOWN"
+        "TYPE_SYM_RESIZE_LEFT"
+        "TYPE_SYM_RESIZE_RIGHT"
+        "TYPE_SYM_RESIZE_UP"
+        "TYPE_TEXT"
+        "TYPE_TOGGLE_PANEL"
+        "TYPE_UNDO"
+      ] (name: mkOption {
+        default = null;
+        type = with types; nullOr str;
+      });
+    };
+  };
+}
diff --git a/krebs/5pkgs/simple/flameshot-once/default.nix b/krebs/5pkgs/simple/flameshot-once/default.nix
new file mode 100644
index 000000000..92dd5311f
--- /dev/null
+++ b/krebs/5pkgs/simple/flameshot-once/default.nix
@@ -0,0 +1,71 @@
+{ name ? "flameshot-once", pkgs, ... }@args:
+with pkgs.stockholm.lib;
+
+let
+  # config cannot be declared in the input attribute set because that would
+  # cause callPackage to inject the wrong config.  Instead, get it from ...
+  # via args.
+  config = args.config or {};
+
+  cfg = evalModulesConfig (singleton {
+    _file = toString ./default.nix;
+    _module.args.pkgs = pkgs;
+    imports = [
+      config
+      ./config.nix
+    ];
+  });
+in
+
+pkgs.symlinkJoin {
+  inherit name;
+  paths = [
+    (pkgs.write "flameshot-once" {
+      "/bin/flameshot-once" = {
+        executable = true;
+        text = /* sh */ ''
+          #! ${pkgs.dash}/bin/dash
+          export PATH=${makeBinPath [
+            pkgs.qt5.qtbase
+          ]}:''${PATH+:$PATH}
+          ${optionalString (config != null) /* sh */ ''
+            export XDG_CONFIG_HOME=${placeholder "out"}/etc
+            ${optionalString cfg.imgur.enable /* sh */ ''
+              export IMGUR_CREATE_URL=${shell.escape cfg.imgur.createUrl}
+              export IMGUR_DELETE_URL=${shell.escape cfg.imgur.deleteUrl}
+              ${optionalString cfg.imgur.xdg-open.enable /* sh */ ''
+                export PATH=${placeholder "out"}/lib/imgur/bin''${PATH+:$PATH}
+              ''}
+            ''}
+          ''}
+          ${cfg.package}/bin/flameshot &
+          exec ${cfg.package}/bin/flameshot gui
+        '';
+      };
+      "/etc/flameshot/flameshot.ini".text =
+        lib.generators.toINI {} (stripAttr cfg.settings);
+      ${if cfg.imgur.enable then "/lib/imgur/bin/xdg-open" else null} = {
+        executable = true;
+        text = /* sh */ ''
+          #! ${pkgs.dash}/bin/dash
+          set -efu
+          uri=$1
+          prefix=$(${pkgs.coreutils}/bin/dirname "$uri")
+          case $prefix in
+            (${shell.escape cfg.imgur.xdg-open.createPrefix})
+              echo "opening image in browser: $uri" >&2
+              exec ${config.imgur.xdg-open.browser} "$uri"
+              ;;
+            (${shell.escape cfg.imgur.xdg-open.deletePrefix})
+              echo "deleting image: $uri" >&2
+              exec ${pkgs.curl}/bin/curl -fsS -X DELETE "$uri"
+              ;;
+            (*)
+              echo "don't know how to open URI: $uri" >&2
+              exit 1
+          esac
+        '';
+      };
+    })
+  ];
+}
diff --git a/krebs/5pkgs/simple/flameshot-once/flameshot/default.nix b/krebs/5pkgs/simple/flameshot-once/flameshot/default.nix
new file mode 100644
index 000000000..b129270eb
--- /dev/null
+++ b/krebs/5pkgs/simple/flameshot-once/flameshot/default.nix
@@ -0,0 +1,15 @@
+{ pkgs }:
+
+pkgs.flameshot.overrideAttrs (old: rec {
+  name = "flameshot-${version}";
+  version = "12.1.0-pre";
+  src = pkgs.fetchFromGitHub {
+    owner = "flameshot-org";
+    repo = "flameshot";
+    rev = "f7e41f4d708e50eeaec892408069da25a28e04a2";
+    hash = "sha256-fZquXY0xSaN1hJgCh16MocIlvxHe1c2Nt+fGF2NIOVw=";
+  };
+  patches = old.patches or [] ++ [
+    ./flameshot-12.imgur.patch
+  ];
+})
diff --git a/krebs/5pkgs/simple/flameshot-once/flameshot/flameshot-12.imgur.patch b/krebs/5pkgs/simple/flameshot-once/flameshot/flameshot-12.imgur.patch
new file mode 100644
index 000000000..b6c3f497a
--- /dev/null
+++ b/krebs/5pkgs/simple/flameshot-once/flameshot/flameshot-12.imgur.patch
@@ -0,0 +1,43 @@
+diff --git a/src/tools/imgupload/storages/imgur/imguruploader.cpp b/src/tools/imgupload/storages/imgur/imguruploader.cpp
+index d6748b5a..5bb8d7de 100644
+--- a/src/tools/imgupload/storages/imgur/imguruploader.cpp
++++ b/src/tools/imgupload/storages/imgur/imguruploader.cpp
+@@ -16,6 +16,7 @@
+ #include <QNetworkRequest>
+ #include <QShortcut>
+ #include <QUrlQuery>
++#include <stdlib.h>
+ 
+ ImgurUploader::ImgurUploader(const QPixmap& capture, QWidget* parent)
+   : ImgUploaderBase(capture, parent)
+@@ -70,7 +71,13 @@ void ImgurUploader::upload()
+     QString description = FileNameHandler().parsedPattern();
+     urlQuery.addQueryItem(QStringLiteral("description"), description);
+ 
+-    QUrl url(QStringLiteral("https://api.imgur.com/3/image"));
++    const char *IMGUR_CREATE_URL = secure_getenv("IMGUR_CREATE_URL");
++    QString createUrlPattern =
++      IMGUR_CREATE_URL != NULL
++        ? QString::fromUtf8(IMGUR_CREATE_URL)
++        : QStringLiteral("https://api.imgur.com/3/image")
++        ;
++    QUrl url(createUrlPattern);
+     url.setQuery(urlQuery);
+     QNetworkRequest request(url);
+     request.setHeader(QNetworkRequest::ContentTypeHeader,
+@@ -87,8 +94,14 @@ void ImgurUploader::deleteImage(const QString& fileName,
+                                 const QString& deleteToken)
+ {
+     Q_UNUSED(fileName)
++    const char *IMGUR_DELETE_URL = secure_getenv("IMGUR_DELETE_URL");
++    QString deleteImageURLPattern =
++      IMGUR_DELETE_URL != NULL
++        ? QString::fromUtf8(IMGUR_DELETE_URL)
++        : QStringLiteral("https://imgur.com/delete/%1")
++        ;
+     bool successful = QDesktopServices::openUrl(
+-      QUrl(QStringLiteral("https://imgur.com/delete/%1").arg(deleteToken)));
++      QUrl(deleteImageURLPattern.arg(deleteToken)));
+     if (!successful) {
+         notification()->showMessage(tr("Unable to open the URL."));
+     }
diff --git a/lib/types.nix b/lib/types.nix
index 4bb8c173f..5f01ccb52 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -7,7 +7,7 @@ let
     mkOption mkOptionType optional optionalAttrs optionals range splitString
     stringLength substring test testString typeOf;
   inherit (lib.types)
-    attrsOf bool either enum int lines listOf nullOr path str submodule;
+    addCheck attrsOf bool either enum int lines listOf nullOr path str submodule;
 in
 
 rec {
@@ -595,6 +595,9 @@ rec {
     };
   };
 
+  flameshot.color =
+    either (addCheck str (test "#[0-9A-Fa-f]{6}")) svg.color-keyword;
+
   file-mode = mkOptionType {
     name = "file mode";
     check = test "[0-7]{4}";
diff --git a/tv/5pkgs/haskell/flameshot-once.nix b/tv/5pkgs/haskell/flameshot-once.nix
deleted file mode 100644
index c8007ce9e..000000000
--- a/tv/5pkgs/haskell/flameshot-once.nix
+++ /dev/null
@@ -1,20 +0,0 @@
-{ mkDerivation, async, base, blessings, bytestring, dbus, fetchgit
-, iso8601-time, lib, process, random, text, time, unagi-chan, unix
-}:
-mkDerivation {
-  pname = "flameshot-once";
-  version = "1.4.0";
-  src = fetchgit {
-    url = "https://cgit.krebsco.de/flameshot-once";
-    sha256 = "13szgsiwn29aixm5xvs1m7128y5km5xss0ry5ii5y068rc2vysw8";
-    rev = "4475893c2081b3d9db4b7a54d0ce38d0914a17bf";
-    fetchSubmodules = true;
-  };
-  isLibrary = false;
-  isExecutable = true;
-  executableHaskellDepends = [
-    async base blessings bytestring dbus iso8601-time process random
-    text time unagi-chan unix
-  ];
-  license = lib.licenses.mit;
-}
diff --git a/tv/5pkgs/override/flameshot/default.nix b/tv/5pkgs/override/flameshot/default.nix
deleted file mode 100644
index 10154cc44..000000000
--- a/tv/5pkgs/override/flameshot/default.nix
+++ /dev/null
@@ -1,15 +0,0 @@
-self: super:
-
-super.flameshot.overrideAttrs (old: rec {
-  name = "flameshot-${version}";
-  version = "0.10.2";
-  src = self.fetchFromGitHub {
-    owner = "flameshot-org";
-    repo = "flameshot";
-    rev = "v${version}";
-    sha256 = "sha256-rZUiaS32C77tFJmEkw/9MGbVTVscb6LOCyWaWO5FyR4=";
-  };
-  patches = old.patches or [] ++ [
-    ./flameshot_imgur_0.10.2.patch
-  ];
-})
diff --git a/tv/5pkgs/override/flameshot/flameshot_imgur_0.10.2.patch b/tv/5pkgs/override/flameshot/flameshot_imgur_0.10.2.patch
deleted file mode 100644
index c4c0bf38a..000000000
--- a/tv/5pkgs/override/flameshot/flameshot_imgur_0.10.2.patch
+++ /dev/null
@@ -1,35 +0,0 @@
---- a/src/tools/imgur/imguruploader.cpp
-+++ b/src/tools/imgur/imguruploader.cpp
-@@ -31,6 +31,7 @@
- #include <QTimer>
- #include <QUrlQuery>
- #include <QVBoxLayout>
-+#include <stdlib.h>
- 
- ImgurUploader::ImgurUploader(const QPixmap& capture, QWidget* parent)
-   : QWidget(parent)
-@@ -79,8 +80,11 @@ void ImgurUploader::handleReply(QNetworkReply* reply)
-         m_imageURL.setUrl(data[QStringLiteral("link")].toString());
- 
-         auto deleteToken = data[QStringLiteral("deletehash")].toString();
-+        char *deleteImageURLPattern = secure_getenv("IMGUR_DELETE_URL");
-+        if (deleteImageURLPattern == NULL)
-+            deleteImageURLPattern = "https://imgur.com/delete/%1";
-         m_deleteImageURL.setUrl(
--          QStringLiteral("https://imgur.com/delete/%1").arg(deleteToken));
-+          QString::fromUtf8(deleteImageURLPattern).arg(deleteToken));
- 
-         // save history
-         QString imageName = m_imageURL.toString();
-@@ -133,7 +137,10 @@ void ImgurUploader::upload()
-     QString description = FileNameHandler().parsedPattern();
-     urlQuery.addQueryItem(QStringLiteral("description"), description);
- 
--    QUrl url(QStringLiteral("https://api.imgur.com/3/image"));
-+    char *createImageURLPattern = secure_getenv("IMGUR_CREATE_URL");
-+    if (createImageURLPattern == NULL)
-+        createImageURLPattern = "https://api.imgur.com/3/image";
-+    QUrl url(QString::fromUtf8(createImageURLPattern));
-     url.setQuery(urlQuery);
-     QNetworkRequest request(url);
-     request.setHeader(QNetworkRequest::ContentTypeHeader,
diff --git a/tv/5pkgs/simple/flameshot-once/default.nix b/tv/5pkgs/simple/flameshot-once/default.nix
deleted file mode 100644
index 0524c2cfa..000000000
--- a/tv/5pkgs/simple/flameshot-once/default.nix
+++ /dev/null
@@ -1,28 +0,0 @@
-{ pkgs, stockholm, ... }@args:
-with stockholm.lib;
-
-let
-  # config cannot be declared in the input attribute set because that would
-  # cause callPackage to inject the wrong config.  Instead, get it from ...
-  # via args.
-  config = args.config or {};
-in
-
-  pkgs.symlinkJoin {
-    name = "flameshot-once-wrapper";
-    paths = [
-      (pkgs.writeDashBin "flameshot-once" ''
-        export PATH=${makeBinPath [
-          pkgs.flameshot
-          pkgs.qt5.qtbase
-          pkgs.xclip
-          pkgs.xwaitforwindow
-        ]}
-        ${optionalString (config != null) /* sh */ ''
-          . ${import ./profile.nix { inherit config pkgs; }}
-        ''}
-        exec ${pkgs.haskellPackages.flameshot-once}/bin/flameshot-once "$@"
-      '')
-      pkgs.haskellPackages.flameshot-once
-    ];
-  }
diff --git a/tv/5pkgs/simple/flameshot-once/profile.nix b/tv/5pkgs/simple/flameshot-once/profile.nix
deleted file mode 100644
index 269f13a66..000000000
--- a/tv/5pkgs/simple/flameshot-once/profile.nix
+++ /dev/null
@@ -1,235 +0,0 @@
-{ config, pkgs }:
-with pkgs.stockholm.lib;
-with generators;
-let
-
-  # Refs https://github.com/lupoDharkael/flameshot/blob/master/src/widgets/capture/capturebutton.h
-  ButtonType = {
-    PENCIL             = 0;
-    DRAWER             = 1;
-    ARROW              = 2;
-    SELECTION          = 3;
-    RECTANGLE          = 4;
-    CIRCLE             = 5;
-    MARKER             = 6;
-    SELECTIONINDICATOR = 7;
-    MOVESELECTION      = 8;
-    UNDO               = 9;
-    COPY               = 10;
-    SAVE               = 11;
-    EXIT               = 12;
-    IMAGEUPLOADER      = 13;
-    OPEN_APP           = 14;
-    BLUR               = 15;
-    REDO               = 16;
-    PIN                = 17;
-    TEXT               = 18;
-    CIRCLECOUNT        = 19;
-  };
-
-  cfg = eval.config;
-
-  eval = evalModules {
-    modules = singleton {
-      _file = toString ./profile.nix;
-      imports = singleton config;
-      options = {
-        buttons = mkOption {
-          apply = map (name: ButtonType.${name});
-          default = [
-            "PENCIL"
-            "DRAWER"
-            "ARROW"
-            "SELECTION"
-            "RECTANGLE"
-            "CIRCLE"
-            "MARKER"
-            "SELECTIONINDICATOR"
-            "MOVESELECTION"
-            "UNDO"
-            "SAVE"
-            "EXIT"
-            "BLUR"
-            "CIRCLECOUNT"
-          ]
-          ++ optional cfg.imgur.enable "IMAGEUPLOADER"
-          ;
-          type = types.listOf (types.enum (attrNames ButtonType));
-        };
-        copyAndCloseAfterUpload = mkOption {
-          default = false;
-          type = types.bool;
-        };
-        disabledTrayIcon = mkOption {
-          default = true;
-          type = types.bool;
-        };
-        drawColor = mkOption {
-          default = "#ff0000";
-          type =
-            types.addCheck types.str (test "#[0-9A-Fa-f]{6}");
-        };
-        drawThickness = mkOption {
-          default = 8;
-          type = types.positive;
-        };
-        filenamePattern = mkOption {
-          default = "%FT%T%z_flameshot";
-          type =
-            # This is types.filename extended by [%:][%:+]*
-            types.addCheck types.str (test "[%:0-9A-Za-z._][%:+0-9A-Za-z._-]*");
-        };
-        imgur = mkOption {
-          default = {};
-          type = types.submodule {
-            options = {
-              enable = mkEnableOption "imgur";
-              createUrl = mkOption {
-                example = "http://p.r/image";
-                type = types.str;
-              };
-              deleteUrl = mkOption {
-                example = "http://p.r/image/delete/%1";
-                type = types.str;
-              };
-              xdg-open = mkOption {
-                default = {};
-                type = types.submodule {
-                  options = {
-                    enable = mkEnableOption "imgur.xdg-open" // {
-                      default = true;
-                    };
-                    browser = mkOption {
-                      default = "${pkgs.coreutils}/bin/false";
-                      type = types.str;
-                    };
-                    createPrefix = mkOption {
-                      default = cfg.imgur.createUrl;
-                      type = types.str;
-                    };
-                    deletePrefix = mkOption {
-                      default = removeSuffix "/%1" cfg.imgur.deleteUrl;
-                      type = types.str;
-                    };
-                  };
-                };
-              };
-            };
-          };
-        };
-        savePath = mkOption {
-          default = "/tmp";
-          type = types.absolute-pathname;
-        };
-        showDesktopNotification = mkOption {
-          default = false;
-          type = types.bool;
-        };
-        showHelp = mkOption {
-          default = false;
-          type = types.bool;
-        };
-        showSidePanelButton = mkOption {
-          default = false;
-          type = types.bool;
-        };
-        showStartupLaunchMessage = mkOption {
-          default = false;
-          type = types.bool;
-        };
-        timeout = mkOption {
-          default = 200;
-          description = ''
-            Maximum time in milliseconds allowed for the flameshot daemon to
-            react.
-          '';
-          type = types.positive;
-        };
-      };
-    };
-  };
-
-  hexchars = stringToCharacters "0123456789abcdef";
-
-  # Encode integer to C-escaped string of bytes, little endian / LSB 0
-  le = rec {
-    x1 = i: let
-      i0 = mod i 16;
-      i1 = i / 16;
-    in
-      "\\x${elemAt hexchars i1}${elemAt hexchars i0}";
-
-    x2 = i: let
-      i0 = mod i 256;
-      i1 = i / 256;
-    in
-      "${x1 i0}${x1 i1}";
-
-    x4 = i: let
-      i0 = mod i 65536;
-      i1 = i / 65536;
-    in
-      "${x2 i0}${x2 i1}";
-  };
-
-  toQList = t: xs:
-    assert t == "int";
-    "QList<${t}>${le.x4 0}${le.x4 (length xs)}${concatMapStrings le.x4 xs}";
-
-  XDG_CONFIG_HOME = pkgs.write "flameshot-config" {
-    "/flameshot/flameshot.ini".text =
-      toINI {} {
-        General = {
-          buttons = ''@Variant(\0\0\0\x7f\0\0\0\v${toQList "int" cfg.buttons})'';
-          disabledTrayIcon = cfg.disabledTrayIcon;
-          checkForUpdates = false;
-          copyAndCloseAfterUpload = cfg.copyAndCloseAfterUpload;
-          drawColor = cfg.drawColor;
-          drawThickness = cfg.drawThickness;
-          filenamePattern = cfg.filenamePattern;
-          savePath = cfg.savePath;
-          showDesktopNotification = cfg.showDesktopNotification;
-          showHelp = cfg.showHelp;
-          showSidePanelButton = cfg.showSidePanelButton;
-          showStartupLaunchMessage = cfg.showStartupLaunchMessage;
-          startupLaunch = false;
-        };
-        Shortcuts = {
-          TYPE_COPY = "Return";
-        };
-      };
-  };
-
-in
-
-  pkgs.writeDash "flameshot.profile" ''
-    export FLAMESHOT_CAPTURE_PATH=${cfg.savePath}
-    export FLAMESHOT_ONCE_TIMEOUT=${toString cfg.timeout}
-    export XDG_CONFIG_HOME=${XDG_CONFIG_HOME}
-    ${optionalString cfg.imgur.enable /* sh */ ''
-      export IMGUR_CREATE_URL=${shell.escape cfg.imgur.createUrl}
-      export IMGUR_DELETE_URL=${shell.escape cfg.imgur.deleteUrl}
-      ${optionalString cfg.imgur.xdg-open.enable /* sh */ ''
-        PATH=$PATH:${makeBinPath [
-          (pkgs.writeDashBin "xdg-open" ''
-            set -efu
-            uri=$1
-            prefix=$(${pkgs.coreutils}/bin/dirname "$uri")
-            case $prefix in
-              (${shell.escape cfg.imgur.xdg-open.createPrefix})
-                echo "opening image in browser: $uri" >&2
-                exec ${config.imgur.xdg-open.browser} "$uri"
-                ;;
-              (${shell.escape cfg.imgur.xdg-open.deletePrefix})
-                echo "deleting image: $uri" >&2
-                exec ${pkgs.curl}/bin/curl -fsS -X DELETE "$uri"
-                ;;
-              (*)
-                echo "don't know how to open URI: $uri" >&2
-                exit 1
-            esac
-          '')
-        ]}
-      ''}
-    ''}
-  ''