#!/usr/bin/env nu # Register a new Slidev deck as a site page. # # The projection REPLACES a page's body and PRESERVES its frontmatter — so the page has to exist # first, and it must carry title, slug, thumbnail and og_image. That is the one thing the # projection cannot invent, and the one step nobody remembers: without it, `just decks` fails with # "la página de destino no tiene frontmatter; no la sobrescribo a ciegas". # # This scaffolds those pages and prints the decks.ncl entry to add. It deliberately does NOT edit # decks.ncl: that file is the manifest a human curates (which deck, which slugs, which language), # and a script that rewrites a manifest is a script that decides what should be published. # # Usage (from outreach/): # nu scripts/decks/new-deck.nu rustikon.md rustikon-why-i-needed-rust \ # --title-es "Rustikon: por qué necesité Rust" --slug-es rustikon-por-que-necesite-rust \ # --title-en "Rustikon: Why I Needed Rust" --slug-en rustikon-why-i-needed-rust def outreach [] { $env.FILE_PWD | path dirname | path dirname } def main [ source: string # the Slidev md, relative to outreach/presentation/ id: string # the deck id: file name of the pages, and the asset dir --title-es: string = "" # omite el par title/slug de un idioma y ese idioma NO tendrá página --slug-es: string = "" --title-en: string = "" --slug-en: string = "" --source-en: string = "" # fuente PROPIA de la página EN cuando el deck es bilingüe nativo # (dos charlas reales, no una traducción): p. ej. rustikon.md (EN) # + rustikon_es.md (ES). Omítelo si una sola fuente sirve ambos idiomas. --date: string = "" # ISO; defaults to today --author: string = "Jesús Pérez" ] { let src = [(outreach) "presentation" $source] | path join if not ($src | path exists) { error make { msg: $"no existe el deck: ($src)" } } # A separate EN source must exist and must actually differ — a page's `source` equal to the # deck's is just noise in the manifest, and a non-existent one fails the projection later, so # catch both here where the message can name the file. if (not ($source_en | is-empty)) { let esrc = [(outreach) "presentation" $source_en] | path join if not ($esrc | path exists) { error make { msg: $"no existe la fuente EN: ($esrc)" } } if ($source_en == $source) { error make { msg: "--source-en es igual a la fuente del deck; omítelo" } } } let when = if ($date | is-empty) { (date now | format date "%Y-%m-%d") } else { $date } # `source` per page: EN uses --source-en when given, otherwise the deck's own. Only a source # that DIFFERS from the deck default is emitted in the manifest below. let pages = [ { lang: "es", title: $title_es, slug: $slug_es, source: $source } { lang: "en", title: $title_en, slug: $slug_en, source: (if ($source_en | is-empty) { $source } else { $source_en }) } ] # A language with no title/slug gets NO page. A deck that exists only in Spanish must not have # an English URL serving Spanish slides: a page that lies about its language is worse than a # page that does not exist. let wanted = ($pages | where {|p| not (($p.title | is-empty) or ($p.slug | is-empty)) }) if ($wanted | is-empty) { error make { msg: "necesitas al menos --title-es/--slug-es o --title-en/--slug-en" } } # The URL category is localized (decks.ncl `category`): ES → presentaciones, EN → decks. The # loader resolves the body by {lang}/{category}/{id}.md, so BOTH the directory and the # frontmatter category must be the localized value, or the ES page 404s. let cat = (^nickel export --format json ([(outreach) "scripts" "decks" "decks.ncl"] | path join) | from json | get category) for p in $wanted { let category = ($cat | get $p.lang) let dir = [(outreach) "site" "site" "content" "resources" $p.lang $category] | path join let file = [$dir $"($id).md"] | path join if ($file | path exists) { print $"(ansi yellow)ya existe, no lo toco: ($file)(ansi reset)" continue } mkdir $dir # Only the frontmatter. The body is the projection's, and it is fenced by its own markers — # anything written here below the frontmatter would be overwritten on the first `just decks`. $"--- id: \"($id)\" title: \"($p.title)\" slug: \"($p.slug)\" subtitle: \"\" excerpt: \"\" author: \"($author)\" date: \"($when)\" published: true featured: false category: \"($category)\" tags: [\"ontoref\", \"slides\"] # Card + share image. The webp carousel is no longer the CONTENT — the slides carry real text # now — but slide 0 still makes the best thumbnail. thumbnail: \"/images/decks/($id)/0.webp\" image_url: \"/images/decks/($id)/0.webp\" og_image: \"/images/og/decks-($id)-0.png\" sort_order: 1 --- " | save --force $file print $"(ansi green)creada:(ansi reset) ($file)" } # Emit `source = …` per page ONLY where it differs from the deck default — that is exactly the # bilingual-native case (rustikon.md + rustikon_es.md), and it is the line deck-new used to make # you add by hand. let entries = ($wanted | each {|p| if ($p.source == $source) { $" { lang = \"($p.lang)\", slug = \"($p.slug)\" }," } else { $" { lang = \"($p.lang)\", slug = \"($p.slug)\", source = \"($p.source)\" }," } } | str join "\n") print $" (ansi cyan)Añade esta entrada a scripts/decks/decks.ncl(ansi reset) — el manifiesto lo curas tú, no un script: qué deck se publica, con qué slug y en qué idioma es una decisión, no un derivado. { source = \"($source)\", id = \"($id)\", pages = [ ($entries) ], }, (ansi cyan)Y luego:(ansi reset) just decks # build + captura + proyección + puertas de artefacto just decks-served # las puertas sobre la PÁGINA servida " }