import gleam/dict import gleam/list import gleam/option import kielet.{gettext as g_} import lustre/attribute.{attribute, href, id, name, rel, role, type_} import lustre/element.{type Element, text} import lustre/element/html.{ a, body, footer, h1, head, header, html, li, link, main, meta, nav, p, section, title, ul, } import scriptorium/config.{type Configuration} import scriptorium/models/database.{type Database} import scriptorium/models/menu.{type MenuItem} import scriptorium/rendering/views/archives import scriptorium/rendering/views/tags /// The base view pre-renders some content once, so that it can be reused for every render. pub type PreRendered { PreRendered(tags: Element(Nil), archives: Element(Nil)) } pub fn generate(db: Database, config: Configuration) { let pre_rendered = pre_render(db, config, tags.view, archives.view) fn(inner: Element(Nil), extra_meta: List(Element(Nil)), title_prefix: String) { view(db, config, pre_rendered, inner, extra_meta, title_prefix) } } /// Pre-render the tag and archive elements using the given tags and archives /// components. pub fn pre_render( db: Database, config: Configuration, tags: fn(Database, Configuration) -> Element(Nil), archives: fn(Database, Configuration) -> Element(Nil), ) -> PreRendered { PreRendered(tags: tags(db, config), archives: archives(db, config)) } fn view( db: Database, config: Configuration, pre_rendered: PreRendered, inner: Element(Nil), extra_meta: List(Element(Nil)), title_prefix: String, ) { let title_text = case title_prefix { "" -> config.blog_name prefix -> prefix <> " · " <> config.blog_name } html([attribute("lang", config.l10n.language)], [ head([], [ meta([attribute("charset", "utf-8")]), meta([ name("viewport"), attribute("content", "width=device-width, initial-scale=1"), ]), title([], title_text), link([href(config.paths.root <> "/css/normalize.css"), rel("stylesheet")]), link([href(config.paths.root <> "/css/magick.css"), rel("stylesheet")]), link([href(config.paths.root <> "/css/custom.css"), rel("stylesheet")]), link([ href(config.paths.root <> config.paths.feed), rel("alternate"), type_("application/atom+xml"), ]), case config.author.url { option.Some(url) -> link([rel("me"), href(url)]) _ -> element.none() }, ..extra_meta ]), body([], [ header([id("title"), role("banner")], [ h1([], [ a([href(config.paths.html(config.paths.root <> config.paths.index))], [ text(config.blog_name), ]), ]), case database.menu(db) { [] -> element.none() m -> menu(m) }, ]), main([], [inner]), section([id("sidebar")], [ case dict.size(database.tags(db)) { 0 -> element.none() _ -> nav( [ id("tags"), attribute("aria-label", g_(config.l10n.context, "Tags")), ], [pre_rendered.tags], ) }, nav( [ id("archives"), attribute("aria-label", g_(config.l10n.context, "Archives")), ], [pre_rendered.archives], ), ]), footer([], [ p([], [ text(config.rendering.copyright), text(" · "), text("Powered by: "), a([href("https://gleam.run/")], [text("Gleam")]), text(", "), a([href("https://hexdocs.pm/lustre")], [text("Lustre")]), text(", "), a([href("https://gitlab.com/Nicd/scriptorium")], [text("Scriptorium")]), ]), ]), ]), ]) } fn menu(menu: List(MenuItem)) { nav([], [ ul( [], list.map(menu, fn(item) { li([], [a([href(item.url)], [text(item.name)])]) }), ), ]) }