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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
{ depot, pkgs, lib, ... }:

let
  goDeps = import ../go-deps.nix { inherit depot pkgs; };

  # The editor's JavaScript is embedded into the binary with go:embed, so the
  # sources have to sit next to the .go files in one directory for the compiler
  # to see them. buildGo takes individual file paths from the store, which would
  # otherwise place each in its own directory.
  src = pkgs.runCommandLocal "blocks-src" { } ''
    mkdir -p $out/static
    cp ${./main.go}   $out/main.go
    cp ${./schema.go} $out/schema.go
    cp ${./model.go}  $out/model.go
    cp ${./asset.go}  $out/asset.go
    cp ${./render.go} $out/render.go
    cp ${./exif.go}   $out/exif.go
    cp ${./image.go}  $out/image.go
    cp ${./stl.go}    $out/stl.go
    cp ${./upload.go} $out/upload.go
    cp ${./serve.go}  $out/serve.go
    cp ${./page.go}   $out/page.go
    cp ${./static}/*  $out/static/
  '';

  unwrapped = depot.nix.buildGo.program {
    name = "blocks";
    srcs = [
      "${src}/main.go"
      "${src}/schema.go"
      "${src}/model.go"
      "${src}/asset.go"
      "${src}/render.go"
      "${src}/exif.go"
      "${src}/image.go"
      "${src}/stl.go"
      "${src}/upload.go"
      "${src}/serve.go"
      "${src}/page.go"
    ];
    deps = [
      goDeps.modernc-sqlite
      # x/image supplies the WebP *decoder* (there is none in the standard
      # library) and the resampling used to build the derivatives.
      goDeps.golang-x-image.draw
      goDeps.golang-x-image.webp
      goDeps.alecthomas-chroma-v2
      goDeps.alecthomas-chroma-v2.formatters.html
      goDeps.alecthomas-chroma-v2.lexers
      goDeps.alecthomas-chroma-v2.styles
      goDeps.yuin-goldmark
      goDeps.yuin-goldmark.extension
      goDeps.yuin-goldmark.parser
      goDeps.yuin-goldmark-highlighting-v2
    ];
  };
in
pkgs.symlinkJoin {
  name = "blocks";
  outputs = [ "out" "man" ];
  paths = [ unwrapped ];
  buildInputs = [ pkgs.makeWrapper ];

  postBuild = ''
    # cwebp encodes every image the tool serves. Neither the Go standard
    # library nor x/image can *write* WebP, and the pure-Go encoders are
    # lossless-only, which for photographs produces files larger than the JPEG
    # they came from. Putting libwebp on PATH here makes the dependency part of
    # the closure rather than something the user has to have installed.
    wrapProgram $out/bin/blocks \
      --prefix PATH : ${lib.makeBinPath [ pkgs.libwebp ]}

    mkdir -p $man/share/man/man1
    cp ${./blocks.1} $man/share/man/man1/blocks.1
  '';

  meta = {
    description = "Block-based authoring for blog posts, stored in SQLite";
    longDescription = ''
      A local web app for writing posts as an ordered list of typed blocks —
      markdown, code, images and 3D models — in the manner of a notebook.
      Files dropped onto the page become new blocks after the drop point.

      Everything lives in one SQLite file: the text, the uploaded originals and
      their derived renditions. Images are always served as WebP at two widths;
      STL models are parsed server-side into a packed vertex buffer and shown
      with a small WebGL viewer that has no JavaScript dependencies.
    '';
  };
}