
Gallery.pudu
Pudu86 lines4.7 KB
1/** @Docgen.Site.Gallery — catalog pages of extensions shown as cards */2module PuduLangDocgen.Site.Gallery34import PuduLangDocgen as Docgen5import PuduLangDocgen.Markdown as Markdown6import PuduLangDocgen.Markdown.Phrase as Phrase7import PuduLangDocgen.Meta as Meta8import PuduLangDocgen.Paths as Paths910/** @Docgen.Site.Card — the rendered page of a catalog */11export type Rendered = { title: Str, body: Str, headings: Array[Docgen.Heading], links: Array[Docgen.Link], diagnostics: Array[Docgen.Diagnostic] }121314export const MARKER: Str = "YamlMime:Dashboard"151617const USAGE: Array[(Str, Str)] = [("install", "Install"), ("init", "Get"), ("command", "Command line"), ("config", "Configuration")]1819202122export fn render(value: &Docgen.Meta, context: &Phrase.Scope) -> Result[Rendered, Str] {23 let fields = match value {24 case Docgen.Fields(found) => found25 case _ => { return Err("catalog must be an object") }26 }27 let title = Meta.textOr(&fields, "title", "")28 if title.isEmpty() { return Err("catalog needs a title") }29 let items = match Meta.get(&fields, "items") {30 case Some(Docgen.Items(found)) => found31 case _ => { return Err("catalog needs an items list") }32 }33 var result = Rendered{title: title, body: "", headings: [Docgen.Heading{id: Paths.slug(title), title: title, level: 1}], links: [], diagnostics: []}34 var pieces = ["<h1 id=\"" + Paths.escape(Paths.slug(title)) + "\">" + Paths.escape(title) + "</h1>"]35 let introduction = Meta.textOr(&fields, "description", "")36 if !introduction.isEmpty() {37 let rendered = Markdown.fragment(introduction, context, 1)38 pieces = pieces.push(rendered.html)39 result = Rendered{..result, links: result.links.concat(rendered.links), diagnostics: result.diagnostics.concat(rendered.diagnostics)}40 }41 pieces = pieces.push("<div class=\"gallery\">")42 for item in items {43 let card = match item {44 case Docgen.Fields(found) => found45 case _ => { return Err("each catalog item must be an object") }46 }47 let name = Meta.textOr(&card, "name", "")48 if name.isEmpty() { return Err("each catalog item needs a name") }49 let id = Paths.slug(name)50 let homepage = safe(Meta.textOr(&card, "homepage", ""))51 let heading = if homepage.isEmpty() { Paths.escape(name) } else { "<a href=\"" + Paths.escape(homepage) + "\">" + Paths.escape(name) + "</a>" }52 var facts: Array[Str] = []53 for key in ["author", "version", "license"] {54 let written = Meta.textOr(&card, key, "")55 if !written.isEmpty() { facts = facts.push(Paths.escape(written)) }56 }57 let kind = Meta.textOr(&card, "type", "")58 var parts = ["<article class=\"card\" id=\"" + Paths.escape(id) + "\">"]59 let thumbnail = safe(Meta.textOr(&card, "thumbnail", ""))60 if !thumbnail.isEmpty() { parts = parts.push("<img class=\"card-image\" src=\"" + Paths.escape(thumbnail) + "\" alt=\"\" loading=\"lazy\">") }61 parts = parts.push("<div class=\"card-body\"><h2 class=\"card-title\">" + heading + "</h2>")62 parts = parts.push("<p class=\"card-meta\">" + (if kind.isEmpty() { "" } else { "<span class=\"badge\">" + Paths.escape(kind) + "</span> " }) + facts.join(" · ") + "</p>")63 let description = Meta.textOr(&card, "description", "")64 if !description.isEmpty() {65 let rendered = Markdown.fragment(description, context, 2)66 parts = parts.push(rendered.html)67 result = Rendered{..result, links: result.links.concat(rendered.links), diagnostics: result.diagnostics.concat(rendered.diagnostics)}68 }69 let usage = Meta.fieldsOf(&card, "usage")70 var entries: Array[Str] = []71 for (key, label) in USAGE {72 let written = Meta.textOr(&usage, key, "")73 if !written.isEmpty() { entries = entries.push("<dt>" + label + "</dt><dd><code>" + Paths.escape(written) + "</code></dd>") }74 }75 if !entries.isEmpty() { parts = parts.push("<dl class=\"card-usage\">" + entries.join("") + "</dl>") }76 let repository = safe(Meta.textOr(&Meta.fieldsOf(&card, "repository"), "url", ""))77 if !repository.isEmpty() { parts = parts.push("<p class=\"card-links\"><a class=\"external\" href=\"" + Paths.escape(repository) + "\">Source</a></p>") }78 pieces = pieces.push(parts.push("</div></article>").join(""))79 result = Rendered{..result, headings: result.headings.push(Docgen.Heading{id: id, title: name, level: 2})}80 }81 Ok(Rendered{..result, body: pieces.push("</div>").join("\n")})82}838485fn safe(destination: Str) -> Str { if destination.isEmpty() || !Paths.href(destination) { "" } else { destination } }86