Changelog: Individual Image Pages

Images now link to a page featuring the image.

I've created individual image pages. Now all images will link to their own image page, which makes it easier to see the alt text and variant images. For example, this page for the picture of my dog on Rialto Beach.

Implementing this was pretty straightforward. My static site generator makes a distinction between "normal" pages, ones where the Page structure is constructible solely from the contents of a file (HTML or Markdown body with all the necessary metadata in a YAML frontmatter), and "synthetic" pages, where the Page structure is constructed in OCaml code inside the generator. These image pages are synthetic: the generator constructs one for each image original used on the site, generated alongside other synthetic pages like the tag pages or blog post archive pages.

let _make_metadata
  (image : ImageDb.image)
  (variants : Yaml.value list)
  (alt : string)
: Frontmatter.t =
  let title = Printf.sprintf "%s · Alex Leighton" image.id in
  let slug = Slug.of_string ("image-" ^ _slug_for_image image.id) in
  let now = Time.Datetime.now () in
  {
    title;
    created = now;
    posted = now;
    updated = None;
    slug;
    content_type = Frontmatter.HTML;
    template = Some "image-page-template";
    description = Some (if String.length alt > 0 then alt else title);
    tags = None;
    series = None;
    external_conversations = [];
    thumbnail = Some image.id;
    is_post = false;
    other =
      [
        ("image_id", `String image.id);
        ("alt", `String alt);
        ("variants", `A variants);
      ];
  }

The site generator will see a defined template in the frontmatter, and look up image-page-template in the templates directory, for use as the body of the page. The other bucket of attributes are added to the template language's variable environment (page_vars function below) when evaluating the page's template substitutions. So image-page-template.html contains references to {image_id}, {alt}, and {variants}.

let render_page_content
  ~(page : Page.t)
  ~(page_index : PageIndex.t)
  ~(series_index : SeriesIndex.t)
  ~(base_vars : Template.Vars.t)
  ~(templates : Templ.t Data.Strings.Map.t)
  ~(search_index : SearchIndex.state option)
: string * string =
  Log.debug (fun m -> m "Rendering page content: %s" page.path);
  let vars =
    Template.Vars.push_scope base_vars
    |> page_vars page page_index series_index
  in
  ...
  let templated_source = Template.apply page.content vars in
  ...

A couple adjustments to the img and pageurl builtin functions (to show an image without a link, and to create routing for the image pages), and the feature was complete.