summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--content/basics.html.nix26
-rw-r--r--content/default.nix18
-rw-r--r--content/education.html.nix39
-rw-r--r--content/experience.html.nix26
-rw-r--r--content/languages.html.nix10
-rw-r--r--content/publications.html.nix64
-rw-r--r--default.nix44
-rw-r--r--flake.lock90
-rw-r--r--flake.nix21
-rw-r--r--index.html.nix52
-rw-r--r--lib/default.nix5
-rw-r--r--lib/html.nix219
-rw-r--r--static/icon.pngbin0 -> 704 bytes
13 files changed, 614 insertions, 0 deletions
diff --git a/content/basics.html.nix b/content/basics.html.nix
new file mode 100644
index 0000000..d7f7a90
--- /dev/null
+++ b/content/basics.html.nix
@@ -0,0 +1,26 @@
+{ html, data, ... }:
+
+let basics = data.basics;
+in {
+ title = "About me";
+ priority = 0;
+ body = with html;
+ with data.basics;
+ lines [
+ (div { class = "row"; } [
+ (div { class = "col"; } [ (imgWith { src = avatar; }) ])
+ (div { class = "col"; } (dl [
+ (dt "${icon "las la-at"} e-mail")
+ (dd (for email (email: "${mailto email.address} (${email.name}) ${br}")))
+ (dt "${icon "las la-key"} keys")
+ (dd (for keys.pgp (name: path: href path name)))
+ (dt "${icon "las la-map-marker"} address")
+ (dd (with location; ''
+ ${number} ${street}${br}
+ ${postalCode} ${city}
+ ''))
+ ]))
+ ])
+ description
+ ];
+}
diff --git a/content/default.nix b/content/default.nix
new file mode 100644
index 0000000..0b0d0ca
--- /dev/null
+++ b/content/default.nix
@@ -0,0 +1,18 @@
+{ html, make, ... }:
+
+let
+ sectionTemplate = section: {
+ inherit (section) title priority;
+ body = html.section { id = section.title; } [
+ (html.h1 section.title)
+ section.body
+ ];
+ };
+ makeSection = path: sectionTemplate (make path { });
+in builtins.map makeSection [
+ ./basics.html.nix
+ ./education.html.nix
+ ./experience.html.nix
+ # ./languages.html.nix
+ ./publications.html.nix
+]
diff --git a/content/education.html.nix b/content/education.html.nix
new file mode 100644
index 0000000..e1027e3
--- /dev/null
+++ b/content/education.html.nix
@@ -0,0 +1,39 @@
+{ html, data, lib, ... }:
+
+let education = data.education;
+in {
+ title = "Education";
+ priority = 30;
+ body = with html;
+ dl (for (sort.reverse.byPath [ "date" "start" ] education) (item:
+ with item;
+ lines [
+ (dt [
+ (with institution; "${studyType} @ ${href url name}, ${location}")
+ br
+ (with date; small (timerange start end))
+ ])
+ (dd [
+ (lib.optionalString (lib.hasAttr "years" item) (lines
+ (for (sort.reverse.byPath [ "date" "start" ] years) (year:
+ with year;
+ details [
+ (summary [
+ (with program;
+ "${studyType} @ ${
+ href url (abbr { title = name; } acronym)
+ }")
+ br
+ (with date; small (timerange start end))
+ ])
+ description
+ (lines (for courses (category: list:
+ details [
+ (summary "${category} courses")
+ (lib.concatStringsSep " · " (lib.naturalSort list))
+ ])))
+ ]))))
+ description
+ ])
+ ]));
+}
diff --git a/content/experience.html.nix b/content/experience.html.nix
new file mode 100644
index 0000000..8637802
--- /dev/null
+++ b/content/experience.html.nix
@@ -0,0 +1,26 @@
+{ html, data, lib, ... }:
+
+let experience = data.experience;
+in {
+ title = "Experience";
+ priority = 20;
+ body = with html;
+ dl (for (sort.reverse.byPath [ "date" "start" ] experience) (item:
+ with item;
+ lines [
+ (dt [
+ (with institution; "${position} @ ${href url name}, ${location}")
+ br
+ (small (lib.concatStringsSep " · "
+ ([ (with date; timerange start end) ]
+ ++ lib.optional (lib.hasAttr "supervisors" item)
+ "supervised by ${
+ lib.concatStringsSep " "
+ (for supervisors (supervisor: with supervisor; href url name))
+ }" ++ lib.optional (lib.hasAttr "assets" item)
+ (lib.concatStringsSep " " (for assets
+ (asset: with asset; href "#Publications#${id}" "${icon "las la-paperclip"} ${name}"))))))
+ ])
+ (dd description)
+ ]));
+}
diff --git a/content/languages.html.nix b/content/languages.html.nix
new file mode 100644
index 0000000..36780db
--- /dev/null
+++ b/content/languages.html.nix
@@ -0,0 +1,10 @@
+{ html, data, ... }:
+
+let languages = data.languages;
+in {
+ title = "Languages";
+ priority = 40;
+ body = with html;
+ lines (for languages
+ (language: with language; "${icon} ${name} (${proficiency})"));
+}
diff --git a/content/publications.html.nix b/content/publications.html.nix
new file mode 100644
index 0000000..04bb502
--- /dev/null
+++ b/content/publications.html.nix
@@ -0,0 +1,64 @@
+{ html, data, lib, ... }:
+
+let
+ publications = data.publications;
+ attrValsOpt = attrs: attrSet:
+ lib.attrVals (builtins.filter (attr: lib.hasAttr attr attrSet) attrs)
+ attrSet;
+ format = publication:
+ with html;
+ with publication;
+ {
+ inherit id title url year abstract cite;
+ } // (let
+ authorsOther = lib.remove data.basics.name
+ (builtins.map (author: "${author.given} ${author.family}") author);
+ in lib.optionalAttrs (authorsOther != [ ]) {
+ authors = "With ${lib.concatStringsSep ", " authorsOther}";
+ }) // lib.optionalAttrs (publication ? container-title) {
+ published = "In ${em container-title}, " + lib.concatStringsSep ", "
+ (attrValsOpt [ "volume" "issue" "publisher" ] publication);
+ } // lib.optionalAttrs (publication ? ISBN) {
+ isbn = "${small "ISBN"}: ${ISBN}";
+ } // lib.optionalAttrs (publication ? ISSN) {
+ issn = "${small "ISSN"}: ${ISSN}";
+ } // lib.optionalAttrs (publication ? DOI) {
+ doi = "${small "DOI"}: ${href "https://doi.org/${DOI}" (code DOI)}";
+ };
+in {
+ title = "Publications";
+ priority = 10;
+ body = with html;
+ dl (for (sort.reverse.byPath [ "issued" "date-parts" ] publications)
+ (publication:
+ let formatted = format publication;
+ in with formatted;
+ lines [
+ (dt { id = "Publications#${id}"; }
+ "${href { target = "_blank"; } url title} (${year})")
+ (dd [
+ (lib.concatStringsSep ". "
+ (attrValsOpt [ "authors" "published" "isbn" "issn" "doi" ]
+ formatted))
+ (details [
+ (summary "More")
+ (dl [
+ (dt "Abstract.")
+ (dd (blockquote abstract))
+ (dt "Cite.")
+ (let
+ citeWith = title: attr:
+ details [
+ (summary title)
+ (pre (code (lib.getAttr attr cite)))
+ ];
+ in dd [
+ (citeWith "BibLaTeX" "biblatex")
+ (citeWith "BibTeX" "bibtex")
+ (citeWith "CSL JSON" "csljson")
+ ])
+ ])
+ ])
+ ])
+ ]));
+}
diff --git a/default.nix b/default.nix
new file mode 100644
index 0000000..7e7098a
--- /dev/null
+++ b/default.nix
@@ -0,0 +1,44 @@
+{ pkgs, html, data }:
+
+let
+ commonArgs = {
+ inherit data html make;
+ inherit (pkgs) lib;
+ };
+ make = path: overrides:
+ let f = import path;
+ in f ((builtins.intersectAttrs (builtins.functionArgs f) commonArgs)
+ // overrides);
+ lineAwesomeCSS = { fontsRelativeDirectory ? "./webfonts" }:
+ pkgs.stdenv.mkDerivation rec {
+ name = "line-awesome-css";
+ version = "v1.2.1";
+
+ src = pkgs.fetchurl {
+ url =
+ "https://raw.githubusercontent.com/icons8/line-awesome/${version}/dist/line-awesome/css/line-awesome.min.css";
+ sha256 = "sha256:zmGhjPCE8VADeYNABEZD8ymsX5AEWsstnneDaL15mFQ=";
+ };
+
+ phases = [ "installPhase" ];
+ installPhase = ''
+ cp $src $out
+ substituteInPlace $out --replace '../fonts' '${fontsRelativeDirectory}'
+ '';
+ };
+
+ webpage = { line-awesome, line-awesome-css ? lineAwesomeCSS
+ , source ? builtins.toFile "index.html" (make ./index.html.nix { })
+ , files ? data.files,
+ icon ? ./static/icon.png }:
+ pkgs.runCommand "webpage" { } ''
+ mkdir "$out"
+ ln -sT "${source}" "$out/index.html"
+ mkdir "$out/static"
+ ln -sT "${icon}" "$out/static/icon.png"
+ ln -sT "${files}" "$out/static/files"
+ mkdir -p "$out/static/css/fonts/line-awesome"
+ ln -sT "${line-awesome}/share/fonts/woff2" "$out/static/css/fonts/line-awesome/webfonts"
+ ln -sT "${line-awesome-css {}}" "$out/static/css/fonts/line-awesome/line-awesome.min.css"
+ '';
+in pkgs.callPackage webpage { }
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..c128a4a
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,90 @@
+{
+ "nodes": {
+ "data": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs"
+ },
+ "locked": {
+ "lastModified": 1668186470,
+ "narHash": "sha256-v1bDqQjsnwPpFgAS7LImXzbDKvydsxCeIeM0AsJgFAw=",
+ "owner": "qaristote",
+ "repo": "info",
+ "rev": "6bdc8dd0c582edcaed9295628956fa966d3c44e3",
+ "type": "github"
+ },
+ "original": {
+ "owner": "qaristote",
+ "repo": "info",
+ "type": "github"
+ }
+ },
+ "flake-utils": {
+ "locked": {
+ "lastModified": 1667395993,
+ "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "flake-utils_2": {
+ "locked": {
+ "lastModified": 1667395993,
+ "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1668086072,
+ "narHash": "sha256-msFoXI5ThCmhTTqgl27hpCXWhaeqxphBaleJAgD8JYM=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "72d8853228c9758820c39b8659415b6d89279493",
+ "type": "github"
+ },
+ "original": {
+ "id": "nixpkgs",
+ "type": "indirect"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1668086072,
+ "narHash": "sha256-msFoXI5ThCmhTTqgl27hpCXWhaeqxphBaleJAgD8JYM=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "72d8853228c9758820c39b8659415b6d89279493",
+ "type": "github"
+ },
+ "original": {
+ "id": "nixpkgs",
+ "type": "indirect"
+ }
+ },
+ "root": {
+ "inputs": {
+ "data": "data",
+ "flake-utils": "flake-utils_2",
+ "nixpkgs": "nixpkgs_2"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..570cde9
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,21 @@
+{
+ description = "Source code of Quentin Aristote's personal webpage.";
+
+ inputs = {
+ flake-utils.url = "github:numtide/flake-utils";
+ data.url = "github:qaristote/info";
+ };
+
+ outputs = { self, nixpkgs, flake-utils, data }:
+ { lib = import ./lib { inherit (nixpkgs) lib; }; } //
+ flake-utils.lib.eachDefaultSystem (system:
+ let pkgs = nixpkgs.legacyPackages.${system};
+ in rec {
+ packages.webpage = import ./default.nix {
+ inherit pkgs;
+ inherit (self.lib.pp) html;
+ data = data.formatWith."${system}" self.lib.pp.html;
+ };
+ defaultPackage = packages.webpage;
+ });
+}
diff --git a/index.html.nix b/index.html.nix
new file mode 100644
index 0000000..473c01a
--- /dev/null
+++ b/index.html.nix
@@ -0,0 +1,52 @@
+{ html, make, ... }:
+
+let sections = html.sort.byKey "priority" (make ./content { });
+in with html;
+html.html { lang = "en"; } [
+ (head [
+ # Basic page needs
+ (metaWith { charset = "utf-8"; })
+ (title "Quentin Aristote")
+ (metaWith {
+ name = "description";
+ content = "Personal webpage of Quentin Aristote";
+ })
+ (metaWith {
+ name = "author";
+ content = "Quentin Aristote";
+ })
+ (metaWith {
+ http-equiv = "x-ua-compatible";
+ content = "ie=edge";
+ })
+ # Mobile specific needs
+ (metaWith {
+ name = "viewport";
+ content = "width=device-width, initial-scale=1";
+ })
+ # Font
+ (linkWith {
+ rel = "stylesheet";
+ href = "/static/css/fonts/line-awesome/line-awesome.min.css";
+ })
+ # CSS
+ (linkWith {
+ rel = "stylesheet";
+ href = "https://classless.de/classless.css";
+ })
+ # Favicon
+ (linkWith {
+ rel = "icon";
+ type = "image/png";
+ href = "static/icon.png";
+ })
+ ])
+ (body [
+ (header [
+ (nav (ul ([ (li "Quentin Aristote") ] ++ for sections
+ (section: li (href "#${section.title}" section.title)))))
+ ])
+ (main { role = "main"; } (for sections (section: section.body)))
+ (footer [ ])
+ ])
+]
diff --git a/lib/default.nix b/lib/default.nix
new file mode 100644
index 0000000..2e3127d
--- /dev/null
+++ b/lib/default.nix
@@ -0,0 +1,5 @@
+{ lib }:
+
+{
+ pp.html = import ./html.nix { inherit lib; };
+}
diff --git a/lib/html.nix b/lib/html.nix
new file mode 100644
index 0000000..aa1c839
--- /dev/null
+++ b/lib/html.nix
@@ -0,0 +1,219 @@
+{ lib, ... }:
+
+let
+ comment = content: "<!-- ${content} -->";
+ lines = lib.concatStringsSep "\n";
+ sortByPath = cmp: keys:
+ lib.sort
+ (x: y: cmp (lib.getAttrFromPath keys x) (lib.getAttrFromPath keys y));
+ sortByKey = cmp: key: sortByPath cmp [ key ];
+ for = iterable: f:
+ if lib.isList iterable then
+ builtins.map f iterable
+ else
+ lib.mapAttrsToList f iterable;
+
+ setAttr = attr: value: ''${attr}="${value}"'';
+ tagWithAttrs = tag: attrs:
+ "<${tag}${
+ lib.concatMapStrings (x: " ${x}") (lib.mapAttrsToList setAttr attrs)
+ }>";
+ lineOrLines = f: content:
+ if lib.isList content then f (lines content) else f content;
+ tryOverride = f: arg:
+ if lib.isAttrs arg then
+ tryOverride (attrs: content: f (arg // attrs) content)
+ else
+ f { } arg;
+ container = tag:
+ tryOverride (attrs:
+ lineOrLines (content: "${tagWithAttrs tag attrs}${content}</${tag}>"));
+
+ empty = tagWithAttrs;
+
+ tagsContainer = [
+ # Main root
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#main_root
+ "html"
+ # Document metadata
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#document_metadata
+ "head"
+ "style"
+ "title"
+ # Sectioning root
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#sectioning_root
+ "body"
+ # Content sectioning
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#content_sectioning
+ "address"
+ "article"
+ "aside"
+ "footer"
+ "header"
+ "h1"
+ "h2"
+ "h3"
+ "h4"
+ "h5"
+ "h6"
+ "main"
+ "nav"
+ "section"
+ # Text content
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#text_content
+ "blockquote"
+ "dd"
+ "div"
+ "dl"
+ "dt"
+ "figcaption"
+ "figure"
+ "li"
+ "menu"
+ "ol"
+ "p"
+ "pre"
+ "ul"
+ # Inline text semantics
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#inline_text_semantics
+ "a"
+ "abbr"
+ "b"
+ "bdi"
+ "bdo"
+ "cite"
+ "code"
+ "data"
+ "dfn"
+ "em"
+ "i"
+ "kbd"
+ "mark"
+ "q"
+ "rp"
+ "rt"
+ "ruby"
+ "s"
+ "samp"
+ "small"
+ "span"
+ "strong"
+ "sub"
+ "time"
+ "u"
+ "var"
+ # Image and multimedia
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#image_and_multimedia
+ "audio"
+ "map"
+ "track"
+ "video"
+ # Embedded content
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#embedded_content
+ "iframe"
+ "object"
+ "picture"
+ "portal"
+ "source"
+ # SVG and MathML
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#svg_and_mathml
+ "svg"
+ "math"
+ # Scripting
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#scripting
+ "canvas"
+ "noscript"
+ "script"
+ # Demarcating edits
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#demarcating_edits
+ "del"
+ "ins"
+ # Table content
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#table_content
+ "caption"
+ "colgroup"
+ "table"
+ "tbody"
+ "td"
+ "tfoot"
+ "th"
+ "thead"
+ "tr"
+ # Forms
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#forms
+ "button"
+ "datalist"
+ "fieldset"
+ "form"
+ "label"
+ "legend"
+ "meter"
+ "optgroup"
+ "option"
+ "output"
+ "progress"
+ "select"
+ "textarea"
+ # Interactive elements
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#interactive_elements
+ "details"
+ "dialog"
+ "summary"
+ # Web components
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element#web_components
+ "slot"
+ "template"
+ ];
+ tagsContainerFuns =
+ builtins.foldl' (module: tag: module // { "${tag}" = container tag; }) { }
+ tagsContainer;
+
+ tagsEmpty = [
+ "area"
+ "base"
+ "br"
+ "col"
+ "embed"
+ "hr"
+ "img"
+ "input"
+ "keygen"
+ "link"
+ "meta"
+ "param"
+ "source"
+ "track"
+ "wbr"
+ ];
+ tagsEmptyFuns = builtins.foldl' (module: tag:
+ let tagWith = empty tag;
+ in module // {
+ "${tag}With" = tagWith;
+ "${tag}" = tagWith { };
+ }) { } tagsEmpty;
+
+ file = path: "/static/files/${path}";
+ href = tryOverride (attrs: url: content:
+ tagsContainerFuns.a ({ href = url; } // attrs) content);
+ icon =
+ tryOverride (attrs: id: tagsContainerFuns.i (attrs // { class = id; }) "");
+ mailto = tryOverride (attrs: address: href attrs "mailto:${address}" address);
+ timerange = start: end:
+ "${tagsContainerFuns.time { date = start; } start} - ${
+ tagsContainerFuns.time { date = end; } end
+ }";
+in tagsContainerFuns // tagsEmptyFuns // {
+ inherit for comment container empty file href icon lines mailto timerange;
+} // {
+ sort = let
+ lt = x: y: x < y;
+ gt = x: y: x > y;
+ in {
+ byKey = sortByKey lt;
+ byPath = sortByPath lt;
+ reverse = {
+ byKey = sortByKey gt;
+ byPath = sortByPath gt;
+ };
+ };
+}
diff --git a/static/icon.png b/static/icon.png
new file mode 100644
index 0000000..80dc5de
--- /dev/null
+++ b/static/icon.png
Binary files differ