From 9b74458a8cae34505ff50fb358e9f32f0892ffcc Mon Sep 17 00:00:00 2001 From: valentin gagarin Date: Wed, 13 Nov 2024 15:24:41 +0100 Subject: [PATCH] override page template for articles --- website/presentation/dom.nix | 70 ++++++++++++++++++++++------------- website/structure/article.nix | 22 ++++++----- 2 files changed, 57 insertions(+), 35 deletions(-) diff --git a/website/presentation/dom.nix b/website/presentation/dom.nix index 3b8d5f0..d64032b 100644 --- a/website/presentation/dom.nix +++ b/website/presentation/dom.nix @@ -9,6 +9,7 @@ let cfg = config; inherit (lib) mkOption types; + inherit (types) submodule; # https://html.spec.whatwg.org/multipage/dom.html#content-models # https://html.spec.whatwg.org/multipage/dom.html#kinds-of-content @@ -43,7 +44,7 @@ let # options with types for all the defined DOM elements element-types = lib.mapAttrs - (name: value: mkOption { type = types.submodule value; }) + (name: value: mkOption { type = submodule value; }) elements; # attrset of categories, where values are module options with the type of the @@ -52,7 +53,7 @@ let genAttrs content-categories (category: - (mapAttrs (_: e: mkOption { type = types.submodule e; }) + (mapAttrs (_: e: mkOption { type = submodule e; }) # HACK: don't evaluate the submodule types, just grab the config directly (filterAttrs (_: e: elem category (e { name = "dummy"; config = { }; }).config.categories) elements)) ); @@ -118,7 +119,7 @@ let mkAttrs = attrs: with lib; mkOption { - type = types.submodule { + type = submodule { options = global-attrs // attrs; }; default = { }; @@ -147,12 +148,21 @@ let print-element = name: attrs: content: with lib; + # TODO: be smarter about content to save some space and repetition at the call sites squash (trim '' <${name}${print-attrs attrs}> ${lib.indent " " content} ''); + toString-unwrap = e: + with lib; + if isAttrs e + then toString (head (attrValues e)) + else if isList e + then toString (map toString-unwrap e) + else e; + elements = rec { document = { ... }: { imports = [ element ]; @@ -206,7 +216,7 @@ let # https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_meta_tag#viewport_width_and_screen_width # this should not exist and no one should ever have to think about it meta.viewport = mkOption { - type = types.submodule ({ ... }: { + type = submodule ({ ... }: { # TODO: figure out how to render only non-default values options = { width = mkOption { @@ -422,21 +432,13 @@ let config.categories = [ ]; config.__toString = self: with lib; - print-element name self.attrs ( - join "\n" (map - (e: - if isAttrs e - then toString (lib.head (attrValues e)) - else e - ) - self.content) - ); + print-element name self.attrs (join "\n" (map toString-unwrap self.content)); }; section = { config, name, ... }: { imports = [ element ]; options = { - # setting to an attribute set will wrap the section in
+ # setting to an attribute set will wrap the section in `
` attrs = mkOption { type = with types; nullOr (submodule { options = global-attrs; }); default = null; @@ -450,14 +452,18 @@ let # such an outline is rather meaningless without headings for navigation, # which is why we enforce headings in sections. # arguably, and this is encoded here, a section *is defined* by its heading. - type = with types; submodule ({ ... }: { + type = with types; submodule ({ config, ... }: { imports = [ element ]; options = { attrs = mkAttrs { }; - # TODO: make `before`/`after` wrap the heading in `
` if non-empty + # setting to an attribute set will wrap the section in `
` + hgroup.attrs = mkOption { + type = with types; nullOr (submodule { options = global-attrs; }); + default = with lib; mkIf (!isNull config.before || !isNull config.after) { }; + }; # https://html.spec.whatwg.org/multipage/sections.html#the-hgroup-element before = mkOption { - type = with types; listOf (attrTag ({ inherit p; } // categories.scripting)); + type = with types; listOf (attrTag ({ inherit (element-types) p; } // categories.scripting)); default = [ ]; }; content = mkOption { @@ -466,7 +472,7 @@ let }; after = mkOption { type = with types; - listOf (attrTag ({ inherit p; } // categories.scripting)); + listOf (attrTag ({ inherit (element-types) p; } // categories.scripting)); default = [ ]; }; }; @@ -489,20 +495,32 @@ let __toString = self: with lib; let n = toString config.heading-level; - content = ''${self.heading.content} - '' + join "\n" (map - (e: - if isAttrs e - then toString (lib.head (attrValues e)) - else e - ) - self.content); + heading = ''${self.heading.content}''; + hgroup = with lib; print-element "hgroup" self.heading.hgroup.attrs (squash '' + ${optionalString (!isNull self.heading.before) (toString-unwrap self.heading.before)} + ${heading} + ${optionalString (!isNull self.heading.after) (toString-unwrap self.heading.after)} + ''); + content = if isNull self.heading.hgroup.attrs then heading else hgroup + + join "\n" (map toString-unwrap self.content); in if !isNull self.attrs then print-element name self.attrs content else content; }; }; + + p = { name, ... }: { + imports = [ element ]; + options = { + attrs = mkAttrs { }; + content = mkOption { + type = with types; either str (listOf (attrTag categories.phrasing)); + }; + }; + config.categories = [ "flow" "palpable" ]; + config.__toString = self: print-element name self.attrs (toString self.content); + }; }; in { diff --git a/website/structure/article.nix b/website/structure/article.nix index 04c6e65..bf4c6ab 100644 --- a/website/structure/article.nix +++ b/website/structure/article.nix @@ -27,17 +27,21 @@ in }; }; config.name = lib.slug config.title; - config.outputs.html = lib.mkForce (cfg.templates.html.dom { + config.outputs.html = lib.mkForce ((cfg.templates.html.page config).override { html = { - head = { - title.text = config.title; - meta.description = config.description; - meta.authors = if lib.isList config.author then config.author else [ config.author ]; - link.canonical = lib.head config.locations; - }; - body.content = [ + # TODO: make authors always a list + head.meta.authors = if lib.isList config.author then config.author else [ config.author ]; + body.content = lib.mkForce [ (cfg.menus.main.outputs.html config) - { section.heading.content = config.title; } + { + section.heading = { + # TODO: i18n support + # TODO: structured dates + before = [{ p.content = "Published ${config.date}"; }]; + content = config.title; + after = [{ p.content = "Written by ${config.author}"; }]; + }; + } (cfg.templates.html.markdown { inherit (config) name body; }) ]; };