1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
{ depot, pkgs, lib, ... }:

# mastodon-alt-text — alt text for the clipboard image, with a choice of
# wordings.
#
# The binary is both the launcher and the server: bare invocation restarts the
# systemd user unit and opens a browser at it, `serve` is what the unit runs.
#
# xclip and imagemagick are pinned because they are load-bearing implementation
# details. The browser is *not* pinned: it is looked up on PATH (and overridable
# with -browser), so this uses the chromium the machine already has rather than
# dragging a second one into the closure.

let
  unwrapped = depot.nix.buildGo.program {
    name = "mastodon-alt-text";
    srcs = [
      ./main.go
      ./serve.go
      ./gemini.go
      ./clipboard.go
      ./templates.go
    ];
  };
in
pkgs.symlinkJoin {
  name = "mastodon-alt-text";
  outputs = [ "out" "man" ];
  paths = [ unwrapped ];
  buildInputs = [ pkgs.makeWrapper ];

  postBuild = ''
    # xclip reads the image and owns the resulting selections; magick does the
    # PNG→WebP conversion that keeps the upload small. pass(1) is intentionally
    # left to PATH, since it needs the user's own store and agent.
    wrapProgram $out/bin/mastodon-alt-text \
      --prefix PATH : ${lib.makeBinPath [ pkgs.xclip pkgs.imagemagick ]}

    mkdir -p $man/share/man/man1
    cp ${./mastodon-alt-text.1} $man/share/man/man1/mastodon-alt-text.1
  '';

  meta = {
    description = "Generate Mastodon alt text for the image in the clipboard";
    longDescription = ''
      Reads an image from the X11 CLIPBOARD selection, converts it to WebP and
      asks Gemini 2.5 Flash for three alt texts, generated concurrently at a
      raised temperature so the wordings actually differ. The results stream
      into a browser window as they arrive; clicking one copies it to both the
      PRIMARY and CLIPBOARD selections.

      The server runs as a systemd user unit and stops on its own once the
      window has been closed for a minute. Its idle timeout is monotonic, so
      suspending the machine does not cause it to disappear mid-sleep.

      The API key comes from $GEMINI_API_KEY or pass(1).
    '';
    mainProgram = "mastodon-alt-text";
  };
}