pandorga 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +77 -0
  3. data/DESIGN.md +1434 -0
  4. data/LICENSE +21 -0
  5. data/README.md +81 -0
  6. data/_data/themes/dark.yml +97 -0
  7. data/_data/themes/light.yml +120 -0
  8. data/_data/themes/shared.yml +37 -0
  9. data/_data/translations.yml +85 -0
  10. data/_includes/chord-tab.html +38 -0
  11. data/_includes/content-runtime/00-config.html +22 -0
  12. data/_includes/content-runtime/10-utils.html +176 -0
  13. data/_includes/content-runtime/20-markdown.html +264 -0
  14. data/_includes/content-runtime/25-code-blocks.html +315 -0
  15. data/_includes/content-runtime/30-includes-bib.html +190 -0
  16. data/_includes/content-runtime/30-includes-xcite.html +122 -0
  17. data/_includes/content-runtime/30-includes.html +365 -0
  18. data/_includes/content-runtime/40-listing.html +225 -0
  19. data/_includes/content-runtime/41-listing-helpers.html +71 -0
  20. data/_includes/content-runtime/42-listing-filter.html +208 -0
  21. data/_includes/content-runtime/43-blocks.html +379 -0
  22. data/_includes/content-runtime/50-detail.html +861 -0
  23. data/_includes/content-runtime/55-toc.html +176 -0
  24. data/_includes/content-runtime/60-fragments.html +567 -0
  25. data/_includes/content-runtime/90-bootstrap.html +76 -0
  26. data/_includes/cut-in.html +10 -0
  27. data/_includes/cv/cv-box.html +18 -0
  28. data/_includes/dict-card.html +24 -0
  29. data/_includes/figure.html +36 -0
  30. data/_includes/home/band-index.html +43 -0
  31. data/_includes/home/band-listing.html +49 -0
  32. data/_includes/mathjax.html +10 -0
  33. data/_includes/ongoing-product.html +1 -0
  34. data/_includes/page/content-runtime.html +20 -0
  35. data/_includes/page/date_evolution.html +19 -0
  36. data/_includes/page/figure-lightbox.html +102 -0
  37. data/_includes/page/formatted_date.html +15 -0
  38. data/_includes/page/hero-default.html +18 -0
  39. data/_includes/page/listing-runtime-attrs.html +8 -0
  40. data/_includes/page/mermaid.html +101 -0
  41. data/_includes/page/network-graph.html +1253 -0
  42. data/_includes/page/page-header.html +41 -0
  43. data/_includes/page/professional-connections.html +2 -0
  44. data/_includes/page/site-footer.html +101 -0
  45. data/_includes/page/timeline-section.html +868 -0
  46. data/_includes/plotly.html +22 -0
  47. data/_includes/theme/dev-reexport.html +100 -0
  48. data/_includes/theme/favicons.html +4 -0
  49. data/_includes/theme/font-loader.html +55 -0
  50. data/_includes/theme/shell-theme-boot.html +115 -0
  51. data/_includes/theme/site-logo.html +6 -0
  52. data/_includes/theme/tailwind-theme-colors.json.liquid +65 -0
  53. data/_includes/theme/tailwind-theme-fonts.json.liquid +6 -0
  54. data/_includes/theme/theme-context.liquid +20 -0
  55. data/_includes/theme/theme-toggle.html +94 -0
  56. data/_includes/theme/theme-vars-block.liquid +161 -0
  57. data/_includes/theme/theme-vars.html +16 -0
  58. data/_includes/transcription.html +80 -0
  59. data/_includes/tweet.html +22 -0
  60. data/_includes/video.html +11 -0
  61. data/_includes/xcite.html +3 -0
  62. data/_includes/youtube-short.html +26 -0
  63. data/_includes/youtube.html +12 -0
  64. data/_layouts/article.html +27 -0
  65. data/_layouts/articles.html +534 -0
  66. data/_layouts/bibliography.html +417 -0
  67. data/_layouts/blog.html +544 -0
  68. data/_layouts/cv.html +393 -0
  69. data/_layouts/home.html +319 -0
  70. data/_layouts/network.html +9 -0
  71. data/_layouts/pandorga/articles.html +4 -0
  72. data/_layouts/pandorga/bibliography.html +4 -0
  73. data/_layouts/pandorga/blog.html +4 -0
  74. data/_layouts/pandorga/cv.html +4 -0
  75. data/_layouts/pandorga/media.html +4 -0
  76. data/_layouts/pandorga/network.html +4 -0
  77. data/_layouts/pandorga/portfolio.html +4 -0
  78. data/_layouts/post.html +7 -0
  79. data/_layouts/product.html +5 -0
  80. data/_layouts/project.html +27 -0
  81. data/_layouts/projects.html +534 -0
  82. data/_layouts/resources.html +387 -0
  83. data/_layouts/shell.html +203 -0
  84. data/_layouts/text.html +246 -0
  85. data/_plugins/bibliography_artifact.rb +57 -0
  86. data/_plugins/content_backend.rb +37 -0
  87. data/_plugins/content_collections.rb +51 -0
  88. data/_plugins/content_detail_paths.rb +58 -0
  89. data/_plugins/content_headers.rb +73 -0
  90. data/_plugins/content_keys.rb +310 -0
  91. data/_plugins/content_local_serve.rb +168 -0
  92. data/_plugins/content_site_adapter.rb +159 -0
  93. data/_plugins/content_validators_hook.rb +20 -0
  94. data/_plugins/content_writing_index.rb +52 -0
  95. data/_plugins/content_writing_paths.rb +55 -0
  96. data/_plugins/force_utf8_encoding.rb +13 -0
  97. data/_plugins/git_chronology.rb +211 -0
  98. data/_plugins/git_metadata.rb +43 -0
  99. data/_plugins/job_sections.rb +25 -0
  100. data/_plugins/lib/content_validators.rb +27 -0
  101. data/_plugins/lib/resource_validator.rb +56 -0
  102. data/_plugins/lib/tag_validator.rb +153 -0
  103. data/_plugins/lib/text_excerpt.rb +56 -0
  104. data/_plugins/lib/writing_entry.rb +107 -0
  105. data/_plugins/local_detail_rewrites.rb +194 -0
  106. data/_plugins/timeline.rb +507 -0
  107. data/_plugins/writing_slug_limits.rb +70 -0
  108. data/assets/css/article.css +1330 -0
  109. data/assets/css/base.css +1432 -0
  110. data/assets/css/cv.css +13 -0
  111. data/assets/css/global-link.css +94 -0
  112. data/assets/css/ledger/bibliography.css +307 -0
  113. data/assets/css/ledger/chrome.css +118 -0
  114. data/assets/css/ledger/cv.css +836 -0
  115. data/assets/css/ledger/detail.css +1270 -0
  116. data/assets/css/ledger/footer.css +298 -0
  117. data/assets/css/ledger/home.css +1490 -0
  118. data/assets/css/ledger/network.css +106 -0
  119. data/assets/css/ledger/projects.css +716 -0
  120. data/assets/css/ledger/rascunhos.css +379 -0
  121. data/assets/css/ledger/sources.css +327 -0
  122. data/assets/css/ledger/writing.css +754 -0
  123. data/assets/tailwind-play.js +15 -0
  124. data/examples/full/Gemfile +7 -0
  125. data/examples/full/_config.yml +92 -0
  126. data/examples/full/_redirects +3 -0
  127. data/examples/full/content/collections/articles/2026-01-01-welcome.md +9 -0
  128. data/examples/full/content/collections/data/contacts.yml +3 -0
  129. data/examples/full/content/collections/data/education.yml +4 -0
  130. data/examples/full/content/collections/jobs/research-fellow.md +12 -0
  131. data/examples/full/content/collections/posts/2026-01-02-note.md +9 -0
  132. data/examples/full/content/collections/products/ledger-tool.md +6 -0
  133. data/examples/full/content/collections/projects/demo-kit.md +10 -0
  134. data/examples/full/content/collections/resources/style-guide.md +7 -0
  135. data/examples/full/content/pages/cv/summary.md +1 -0
  136. data/examples/full/content/pages/headers.yml +20 -0
  137. data/examples/full/index.html +4 -0
  138. data/examples/minimal/Gemfile +7 -0
  139. data/examples/minimal/_config.yml +47 -0
  140. data/examples/minimal/_redirects +3 -0
  141. data/examples/minimal/content/collections/articles/2026-01-01-hello.md +10 -0
  142. data/examples/minimal/content/collections/articles/2026-01-02-second.md +9 -0
  143. data/examples/minimal/content/pages/headers.yml +6 -0
  144. data/examples/minimal/index.html +4 -0
  145. data/exe/pandorga +7 -0
  146. data/functions/api/studio/[[path]].js +562 -0
  147. data/functions/api/studio/_lib/auth.js +131 -0
  148. data/functions/api/studio/_lib/blocks.js +156 -0
  149. data/functions/api/studio/_lib/gemini.js +62 -0
  150. data/functions/api/studio/_lib/github.js +278 -0
  151. data/functions/api/studio/_lib/guidelines.js +143 -0
  152. data/functions/api/studio/_lib/paths.js +76 -0
  153. data/functions/api/studio/_lib/prompts.js +165 -0
  154. data/functions/api/studio/_lib/rate-limit.js +40 -0
  155. data/functions/api/studio/_lib/rewrite.js +136 -0
  156. data/functions/api/studio/_lib/sanitize.js +143 -0
  157. data/lib/pandorga/cli.rb +66 -0
  158. data/lib/pandorga/commands/doctor.rb +66 -0
  159. data/lib/pandorga/commands/export.rb +30 -0
  160. data/lib/pandorga/commands/install_functions.rb +41 -0
  161. data/lib/pandorga/commands/new.rb +113 -0
  162. data/lib/pandorga/commands/publish.rb +53 -0
  163. data/lib/pandorga/commands/serve.rb +34 -0
  164. data/lib/pandorga/commands/studio_schema.rb +309 -0
  165. data/lib/pandorga/jekyll/generator.rb +74 -0
  166. data/lib/pandorga/jekyll/plugins/bibliography_artifact.rb +57 -0
  167. data/lib/pandorga/jekyll/plugins/content_backend.rb +37 -0
  168. data/lib/pandorga/jekyll/plugins/content_collections.rb +51 -0
  169. data/lib/pandorga/jekyll/plugins/content_detail_paths.rb +58 -0
  170. data/lib/pandorga/jekyll/plugins/content_headers.rb +73 -0
  171. data/lib/pandorga/jekyll/plugins/content_keys.rb +310 -0
  172. data/lib/pandorga/jekyll/plugins/content_local_serve.rb +168 -0
  173. data/lib/pandorga/jekyll/plugins/content_site_adapter.rb +159 -0
  174. data/lib/pandorga/jekyll/plugins/content_validators_hook.rb +20 -0
  175. data/lib/pandorga/jekyll/plugins/content_writing_index.rb +52 -0
  176. data/lib/pandorga/jekyll/plugins/content_writing_paths.rb +55 -0
  177. data/lib/pandorga/jekyll/plugins/force_utf8_encoding.rb +13 -0
  178. data/lib/pandorga/jekyll/plugins/git_chronology.rb +211 -0
  179. data/lib/pandorga/jekyll/plugins/git_metadata.rb +43 -0
  180. data/lib/pandorga/jekyll/plugins/job_sections.rb +25 -0
  181. data/lib/pandorga/jekyll/plugins/lib/content_validators.rb +27 -0
  182. data/lib/pandorga/jekyll/plugins/lib/resource_validator.rb +56 -0
  183. data/lib/pandorga/jekyll/plugins/lib/tag_validator.rb +153 -0
  184. data/lib/pandorga/jekyll/plugins/lib/text_excerpt.rb +56 -0
  185. data/lib/pandorga/jekyll/plugins/lib/writing_entry.rb +107 -0
  186. data/lib/pandorga/jekyll/plugins/local_detail_rewrites.rb +194 -0
  187. data/lib/pandorga/jekyll/plugins/timeline.rb +507 -0
  188. data/lib/pandorga/jekyll/plugins/writing_slug_limits.rb +70 -0
  189. data/lib/pandorga/jekyll.rb +15 -0
  190. data/lib/pandorga/object_store_env.rb +51 -0
  191. data/lib/pandorga/registry/schema.yml +115 -0
  192. data/lib/pandorga/registry.rb +279 -0
  193. data/lib/pandorga/version.rb +5 -0
  194. data/lib/pandorga.rb +19 -0
  195. data/scripts/cdn-allowlist.json +19 -0
  196. data/scripts/content/backfill-content-keys.rb +99 -0
  197. data/scripts/content/band_art.rb +362 -0
  198. data/scripts/content/export-content-json.rb +741 -0
  199. data/scripts/content/flatten-ansi-capture.mjs +153 -0
  200. data/scripts/content/hero_portraits.rb +28 -0
  201. data/scripts/content/publish-content-to-object-store.sh +78 -0
  202. data/scripts/content/publish-content-to-r2.sh +5 -0
  203. data/scripts/content/sync-content-json-to-r2.rb +167 -0
  204. data/scripts/content/sync-content-redirects.rb +12 -0
  205. data/scripts/generate-configuration-doc.rb +127 -0
  206. data/scripts/lib/content_license.rb +50 -0
  207. data/scripts/lib/content_storage_config.rb +70 -0
  208. data/scripts/lib/normalize-display-math.mjs +52 -0
  209. data/scripts/lib/r2_json_sync.rb +76 -0
  210. data/scripts/lib/withdrawn_public_objects.rb +23 -0
  211. data/scripts/lib/writing_index_contract.rb +61 -0
  212. data/scripts/pdf/md-to-pdf.mjs +1400 -0
  213. data/scripts/pdf/md-to-pdf.sh +27 -0
  214. data/scripts/test/fixtures/dual-template/_config.yml +32 -0
  215. data/scripts/test/fixtures/dual-template/index.html +4 -0
  216. data/scripts/test/render-parity/article-note.json +5 -0
  217. data/scripts/test/render-parity/blockquote.json +5 -0
  218. data/scripts/test/render-parity/blocks/ansi-basic.json +7 -0
  219. data/scripts/test/render-parity/blocks/ansi-bold-is-bright.json +7 -0
  220. data/scripts/test/render-parity/blocks/ansi-drops-cursor-moves.json +7 -0
  221. data/scripts/test/render-parity/blocks/ansi-escapes-markup.json +7 -0
  222. data/scripts/test/render-parity/blocks/ansi-extended-colour.json +7 -0
  223. data/scripts/test/render-parity/blocks/ansi-real-control-byte.json +7 -0
  224. data/scripts/test/render-parity/blocks/archive-card-date-once.json +19 -0
  225. data/scripts/test/render-parity/blocks/archive-card-empty-foot.json +7 -0
  226. data/scripts/test/render-parity/blocks/archive-card-footer-dates.json +22 -0
  227. data/scripts/test/render-parity/blocks/archive-card-meta-parts.json +26 -0
  228. data/scripts/test/render-parity/blocks/archive-card.json +32 -0
  229. data/scripts/test/render-parity/blocks/cta-link.json +13 -0
  230. data/scripts/test/render-parity/blocks/lang-pill-pt.json +8 -0
  231. data/scripts/test/render-parity/blocks/lang-pill.json +8 -0
  232. data/scripts/test/render-parity/blocks/ledger-row-meta-parts.json +29 -0
  233. data/scripts/test/render-parity/blocks/ledger-row.json +35 -0
  234. data/scripts/test/render-parity/blocks/meta-strip.json +27 -0
  235. data/scripts/test/render-parity/blocks/poster-media-image.json +8 -0
  236. data/scripts/test/render-parity/blocks/poster-media-video.json +9 -0
  237. data/scripts/test/render-parity/blocks/section-banner.json +13 -0
  238. data/scripts/test/render-parity/blocks/system-card-custom-meta.json +22 -0
  239. data/scripts/test/render-parity/blocks/system-card.json +48 -0
  240. data/scripts/test/render-parity/blocks/tag-chips-links.json +12 -0
  241. data/scripts/test/render-parity/blocks/tag-chips.json +9 -0
  242. data/scripts/test/render-parity/cut-in-blank-line.json +9 -0
  243. data/scripts/test/render-parity/cut-in-right.json +9 -0
  244. data/scripts/test/render-parity/cut-in.json +8 -0
  245. data/scripts/test/render-parity/figure.json +5 -0
  246. data/scripts/test/render-parity/load-runtime.mjs +337 -0
  247. data/scripts/test/render-parity/ongoing-product.json +5 -0
  248. data/scripts/test/render-parity/plotly.json +5 -0
  249. data/scripts/test/render-parity/poem.json +5 -0
  250. data/scripts/test/render-parity/run-absent-field-checks.mjs +232 -0
  251. data/scripts/test/render-parity/run-block-fixture.mjs +86 -0
  252. data/scripts/test/render-parity/run-detail-parity-checks.mjs +123 -0
  253. data/scripts/test/render-parity/run-field-flow-checks.mjs +227 -0
  254. data/scripts/test/render-parity/run-fixture.mjs +159 -0
  255. data/scripts/test/render-parity/selectors.mjs +43 -0
  256. data/scripts/test/render-parity/video.json +5 -0
  257. data/scripts/test/render-parity/youtube-short.json +10 -0
  258. data/scripts/test/render-parity/youtube.json +8 -0
  259. data/scripts/test/test-absent-field-absent-element.rb +43 -0
  260. data/scripts/test/test-cdn-pins.rb +44 -0
  261. data/scripts/test/test-cloudflare-watch-paths-doc.rb +31 -0
  262. data/scripts/test/test-configuration-doc.rb +36 -0
  263. data/scripts/test/test-content-detail-urls.rb +346 -0
  264. data/scripts/test/test-content-keys.rb +110 -0
  265. data/scripts/test/test-content-license.rb +45 -0
  266. data/scripts/test/test-content-render-parity.rb +38 -0
  267. data/scripts/test/test-content-validators.rb +12 -0
  268. data/scripts/test/test-css-structure.rb +72 -0
  269. data/scripts/test/test-cv-export-contract.rb +158 -0
  270. data/scripts/test/test-detail-render-parity.rb +40 -0
  271. data/scripts/test/test-dev-reexport.rb +122 -0
  272. data/scripts/test/test-doctor.rb +39 -0
  273. data/scripts/test/test-dual-template-instances.rb +87 -0
  274. data/scripts/test/test-dual-template-runtime.rb +167 -0
  275. data/scripts/test/test-examples-build.rb +60 -0
  276. data/scripts/test/test-export-content-json-incremental.rb +138 -0
  277. data/scripts/test/test-export.rb +53 -0
  278. data/scripts/test/test-exported-fields-reach-the-view.rb +60 -0
  279. data/scripts/test/test-form-control-theming.rb +143 -0
  280. data/scripts/test/test-git-chronology.rb +414 -0
  281. data/scripts/test/test-hero-portraits.mjs +97 -0
  282. data/scripts/test/test-hero-portraits.rb +162 -0
  283. data/scripts/test/test-home-band-art.rb +135 -0
  284. data/scripts/test/test-install-functions.rb +39 -0
  285. data/scripts/test/test-language-export.rb +79 -0
  286. data/scripts/test/test-listing-filter-contract.rb +119 -0
  287. data/scripts/test/test-math-row-breaks.mjs +79 -0
  288. data/scripts/test/test-math-row-breaks.rb +18 -0
  289. data/scripts/test/test-new.rb +113 -0
  290. data/scripts/test/test-no-personal-data.rb +85 -0
  291. data/scripts/test/test-public-cv-export.rb +164 -0
  292. data/scripts/test/test-r2-config-consistency.rb +57 -0
  293. data/scripts/test/test-r2-json-sync.rb +193 -0
  294. data/scripts/test/test-registry-home.rb +34 -0
  295. data/scripts/test/test-runtime-js-syntax.rb +64 -0
  296. data/scripts/test/test-slim-writing-index.rb +126 -0
  297. data/scripts/test/test-studio-api-contract.rb +99 -0
  298. data/scripts/test/test-studio-content-files.rb +79 -0
  299. data/scripts/test/test-studio-liquid-roundtrip.mjs +179 -0
  300. data/scripts/test/test-studio-rate-limit.mjs +70 -0
  301. data/scripts/test/test-studio-rewrite-api-contract.rb +59 -0
  302. data/scripts/test/test-studio-rewrite-sanitize.mjs +210 -0
  303. data/scripts/test/test-studio-schema-composition.rb +126 -0
  304. data/scripts/test/test-studio-slug-config.rb +167 -0
  305. data/scripts/test/test-template-packages.rb +51 -0
  306. data/scripts/test/test-text-excerpt.rb +51 -0
  307. data/scripts/test/test-unwrap-media-src.mjs +39 -0
  308. data/scripts/test/test-unwrap-media-src.rb +19 -0
  309. data/scripts/test/test-withdrawn-content-export.rb +69 -0
  310. data/scripts/test/test-writing-index-export-slugs.rb +84 -0
  311. data/scripts/test/test-writing-slug-limits.rb +61 -0
  312. data/scripts/test/test-xcite-chronology.rb +106 -0
  313. data/scripts/validate.sh +74 -0
  314. data/scripts/visual/build-cv-pages-json.js +33 -0
  315. data/scripts/visual/build-site-pages-json.js +94 -0
  316. data/scripts/visual/publish-visual-inspection-to-r2.sh +42 -0
  317. data/scripts/visual/take-screenshots.js +88 -0
  318. data/scripts/visual/visual-inspection-pages.json +113 -0
  319. data/studio/assets/index-B_j0pMD5.css +1 -0
  320. data/studio/assets/index-Ch4YEv9g.js +285 -0
  321. data/studio/index.html +27 -0
  322. data/studio/schema.yml +869 -0
  323. data/studio-app/README.md +27 -0
  324. data/studio-app/index.html +26 -0
  325. data/studio-app/package-lock.json +2499 -0
  326. data/studio-app/package.json +26 -0
  327. data/studio-app/skills-lock.json +47 -0
  328. data/studio-app/src/api/client.js +71 -0
  329. data/studio-app/src/editor/body-editor.js +70 -0
  330. data/studio-app/src/editor/liquid-core.js +382 -0
  331. data/studio-app/src/editor/liquid.js +77 -0
  332. data/studio-app/src/lib/document.js +207 -0
  333. data/studio-app/src/lib/list-controls.js +317 -0
  334. data/studio-app/src/lib/schema.js +66 -0
  335. data/studio-app/src/lib/yaml-doc.js +100 -0
  336. data/studio-app/src/main.js +3693 -0
  337. data/studio-app/src/media/browser.js +578 -0
  338. data/studio-app/src/preview/render.js +430 -0
  339. data/studio-app/src/styles/studio.css +2826 -0
  340. data/studio-app/vite.config.js +25 -0
  341. data/templates/articles/README.md +40 -0
  342. data/templates/articles/fixtures/article.md +9 -0
  343. data/templates/articles/screenshots/README.md +6 -0
  344. data/templates/articles/studio.yml +73 -0
  345. data/templates/articles/template.yml +21 -0
  346. data/templates/bibliography/README.md +41 -0
  347. data/templates/bibliography/fixtures/references.json +9 -0
  348. data/templates/bibliography/screenshots/README.md +6 -0
  349. data/templates/bibliography/studio.yml +66 -0
  350. data/templates/bibliography/template.yml +18 -0
  351. data/templates/blog/README.md +38 -0
  352. data/templates/blog/fixtures/post.md +9 -0
  353. data/templates/blog/screenshots/README.md +6 -0
  354. data/templates/blog/studio.yml +69 -0
  355. data/templates/blog/template.yml +20 -0
  356. data/templates/cv/README.md +34 -0
  357. data/templates/cv/fixtures/job.md +12 -0
  358. data/templates/cv/screenshots/README.md +6 -0
  359. data/templates/cv/studio.yml +200 -0
  360. data/templates/cv/template.yml +25 -0
  361. data/templates/media/README.md +38 -0
  362. data/templates/media/fixtures/resource.md +7 -0
  363. data/templates/media/screenshots/README.md +6 -0
  364. data/templates/media/studio.yml +58 -0
  365. data/templates/media/template.yml +17 -0
  366. data/templates/network/README.md +34 -0
  367. data/templates/network/fixtures/sources.json +3 -0
  368. data/templates/network/screenshots/README.md +6 -0
  369. data/templates/network/studio.yml +28 -0
  370. data/templates/network/template.yml +10 -0
  371. data/templates/portfolio/README.md +39 -0
  372. data/templates/portfolio/fixtures/project.md +10 -0
  373. data/templates/portfolio/screenshots/README.md +6 -0
  374. data/templates/portfolio/studio.yml +89 -0
  375. data/templates/portfolio/template.yml +18 -0
  376. metadata +505 -0
@@ -0,0 +1,319 @@
1
+ ---
2
+ layout: shell
3
+ ---
4
+
5
+ {%- comment -%}
6
+ Home — composition from the page registry (PLT §5.4).
7
+
8
+ The hero reads identity plus content fragments. Decorative art is a slot
9
+ (`pandorga.home.hero_art`, default page/hero-default.html). Listing and
10
+ index bands come from site.data.pandorga_home_bands in registry order.
11
+ Band copy (kicker, title, note) arrives from content/pages/headers.yml
12
+ via hydrateHomeSections(). No Portuguese or owner-specific strings live
13
+ in this layout.
14
+ {%- endcomment -%}
15
+
16
+ {%- assign _identity_name = site.pandorga.identity.name | default: site.author -%}
17
+ {%- assign _name_parts = _identity_name | split: " " -%}
18
+ {%- assign _name_given = _name_parts | first -%}
19
+ {%- assign _name_family = _name_parts | slice: 1, 8 | join: " " -%}
20
+ {%- assign _cv_page = site.pages | where: "key", "cv" | first -%}
21
+ {%- assign _hero_art = site.pandorga.home.hero_art | default: "page/hero-default.html" -%}
22
+ {%- assign _home_bands = site.data.pandorga_home_bands -%}
23
+
24
+ <main class="home-ledger">
25
+
26
+ <section class="home-band home-band--hero">
27
+ <div class="home-hero-art home-hero-pandorga" aria-hidden="true">
28
+ {% include {{ _hero_art }} %}
29
+ </div>
30
+ <div class="home-band-inner">
31
+ <div class="home-hero">
32
+ <div class="home-hero-copy">
33
+ <div id="home-subtitle-content" class="home-hero-tagline font-label"
34
+ data-content-fragment="home/subtitle"></div>
35
+ <h1 class="home-hero-title">
36
+ <span class="home-hero-given">{{ _name_given }}</span>{%- if _name_family != "" %} <span class="home-hero-family">{{ _name_family }}</span>{%- endif %}
37
+ </h1>
38
+ <div id="home-cv-summary" class="home-hero-summary font-body"
39
+ data-content-fragment="cv/summary"></div>
40
+ <div class="home-hero-actions">
41
+ {%- if _cv_page -%}
42
+ <a class="ledger-cta ledger-cta--emphasis home-hero-cta" href="{{ _cv_page.url }}">
43
+ <span>{{ _cv_page.title | default: "Curriculum Vitæ" }}</span>
44
+ <span class="material-symbols-outlined ledger-cta-arrow" aria-hidden="true">chevron_right</span>
45
+ </a>
46
+ {%- endif -%}
47
+ <span data-content-hero-contacts></span>
48
+ </div>
49
+ </div>
50
+ <div class="home-hero-portrait">
51
+ <span class="shard-accent home-hero-shard" aria-hidden="true"></span>
52
+ <div class="home-hero-plate-wrap">
53
+ <span class="ledger-frame-crosshairs" aria-hidden="true"></span>
54
+ <figure class="home-hero-plate">
55
+ <img alt="" width="1200" height="800" data-content-hero-portrait />
56
+ <span class="ledger-frame-corners" aria-hidden="true"></span>
57
+ </figure>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ </section>
63
+
64
+ {%- for band in _home_bands -%}
65
+ {%- if band.kind == "index" -%}
66
+ {% include home/band-index.html band=band %}
67
+ {%- else -%}
68
+ {% include home/band-listing.html band=band %}
69
+ {%- endif -%}
70
+ {%- endfor -%}
71
+
72
+ </main>
73
+
74
+ <script>
75
+ {% raw %}
76
+ (function () {
77
+ 'use strict';
78
+
79
+ var runtime = window.ContentRuntime;
80
+ if (!runtime) return;
81
+
82
+ function asItem(entry) {
83
+ return {
84
+ slug: entry.slug,
85
+ key: entry.key,
86
+ url: entry.url,
87
+ collection: entry.collection,
88
+ date: entry.date,
89
+ last_updated: entry.last_updated,
90
+ front_matter: entry
91
+ };
92
+ }
93
+
94
+ function byDateDesc(a, b) {
95
+ return String(b.date || '').localeCompare(String(a.date || ''));
96
+ }
97
+
98
+ function tagHrefFrom(element) {
99
+ var base = element && element.getAttribute('data-tag-base');
100
+ if (!base) return null;
101
+ return function (tag) {
102
+ return base + '?tag=' + encodeURIComponent(tag);
103
+ };
104
+ }
105
+
106
+ function dropPlateOnError(scope, frame, illustratedClass) {
107
+ var image = frame && frame.querySelector('img');
108
+ if (!image) return;
109
+ image.addEventListener('error', function () {
110
+ frame.remove();
111
+ scope.classList.remove(illustratedClass);
112
+ });
113
+ }
114
+
115
+ function renderFeatured(entry, tagHref, locale) {
116
+ var href = entry.url || runtime.articleUrl(entry.slug);
117
+ var wrap = document.createElement('article');
118
+ var thumbnail = entry.thumbnail;
119
+ wrap.className = 'home-featured' + (thumbnail ? ' home-featured--illustrated' : '');
120
+ var summary = runtime.listingPreview(asItem(entry));
121
+ var meta = runtime.renderMetaStrip([runtime.formatDate(entry.date, locale || 'en')]);
122
+ var chips = runtime.renderTagChips(entry.tags, { hrefFor: tagHref });
123
+ var cta = runtime.renderCtaLink(href, 'Read', { className: 'home-featured-cta' });
124
+ wrap.innerHTML = [
125
+ '<span class="shard-accent home-featured-shard" aria-hidden="true"></span>',
126
+ '<span class="shard-accent shard-accent-beta home-featured-shard home-featured-shard--beta" aria-hidden="true"></span>',
127
+ '<div class="home-featured-lead">',
128
+ '<div class="home-featured-rail">',
129
+ '<span class="ledger-chip home-featured-badge">Featured</span>',
130
+ meta || '',
131
+ '</div>',
132
+ '<h2 class="card-title home-featured-title"><a href="',
133
+ runtime.escapeAttr(href), '">', runtime.escapeHtml(entry.title || entry.slug),
134
+ '</a></h2>',
135
+ summary ? '<p class="home-featured-copy font-body">' + runtime.escapeHtml(summary) + '</p>' : '',
136
+ (chips || cta) ? '<div class="home-featured-foot">' + chips + cta + '</div>' : '',
137
+ '</div>',
138
+ thumbnail ? runtime.renderPosterMedia(runtime.buildUrl(thumbnail), entry.title || entry.slug) : ''
139
+ ].join('');
140
+ if (thumbnail) {
141
+ dropPlateOnError(wrap, wrap.querySelector('.ledger-poster'), 'home-featured--illustrated');
142
+ }
143
+ return wrap;
144
+ }
145
+
146
+ function draftFolios(posts) {
147
+ var folios = Object.create(null);
148
+ posts.slice().sort(function (a, b) {
149
+ var byDate = String(a.date || '').localeCompare(String(b.date || ''));
150
+ return byDate !== 0 ? byDate : String(a.slug || '').localeCompare(String(b.slug || ''));
151
+ }).forEach(function (entry, index) {
152
+ folios[entry.slug] = index + 1;
153
+ });
154
+ return folios;
155
+ }
156
+
157
+ function renderDraftCard(entry, folio, locale) {
158
+ var href = entry.url || runtime.postUrl(entry.slug);
159
+ var f = asItem(entry);
160
+ var preview = runtime.listingPreview(f);
161
+ var metaParts = [];
162
+ if (folio) {
163
+ metaParts.push('<span class="code-technical">#' + String(folio).padStart(2, '0') + '</span>');
164
+ }
165
+ metaParts.push(runtime.formatDate(entry.date, locale || 'en'));
166
+ var meta = runtime.renderMetaStrip(metaParts);
167
+ var card = document.createElement('article');
168
+ card.className = 'ledger-card home-draft-card group';
169
+ card.innerHTML = [
170
+ runtime.renderPosterMedia(entry.thumbnail, entry.title || entry.slug),
171
+ '<div class="ledger-card-body">',
172
+ meta,
173
+ '<h3 class="card-title ledger-card-title"><a href="', runtime.escapeAttr(href), '">',
174
+ runtime.escapeHtml(entry.title || entry.slug), '</a></h3>',
175
+ preview ? '<p class="ledger-card-copy font-body">' +
176
+ runtime.escapeHtml(preview) + '</p>' : '',
177
+ '<div class="ledger-card-foot">', runtime.renderCtaLink(href, 'Read'), '</div>',
178
+ '</div>'
179
+ ].join('');
180
+ return card;
181
+ }
182
+
183
+ function fill(element, items, render, emptyMessage) {
184
+ if (!element) return;
185
+ if (!items.length) {
186
+ runtime.Listing.showErrorState(element, emptyMessage);
187
+ return;
188
+ }
189
+ var fragment = document.createDocumentFragment();
190
+ items.forEach(function (item) { fragment.appendChild(render(item)); });
191
+ element.innerHTML = '';
192
+ element.appendChild(fragment);
193
+ }
194
+
195
+ function fillBand(section, index) {
196
+ var kind = section.getAttribute('data-home-kind');
197
+ var collection = section.getAttribute('data-home-collection');
198
+ var limit = parseInt(section.getAttribute('data-home-limit') || '3', 10) || 3;
199
+ var featured = section.getAttribute('data-home-featured') === 'true';
200
+ var locale = section.getAttribute('data-home-language') || 'en';
201
+ var list = section.querySelector('[data-home-list]');
202
+ var featuredSlot = section.querySelector('[data-home-featured-slot]');
203
+ if (!collection || !list) return;
204
+
205
+ var entries = index.filter(function (e) { return e.collection === collection; });
206
+
207
+ if (kind === 'portfolio') {
208
+ entries = entries.sort(function (a, b) {
209
+ return Number(a.sort_order || 0) - Number(b.sort_order || 0);
210
+ });
211
+ var systemTagHref = tagHrefFrom(list);
212
+ fill(list, entries.slice(0, limit), function (entry) {
213
+ return runtime.renderSystemCard(asItem(entry), {
214
+ href: entry.url || runtime.projectUrl(entry.slug),
215
+ tagHref: systemTagHref
216
+ });
217
+ }, 'Projects could not be loaded right now.');
218
+ return;
219
+ }
220
+
221
+ entries = entries.sort(byDateDesc);
222
+
223
+ if (kind === 'blog') {
224
+ var folios = draftFolios(entries);
225
+ fill(list, entries.slice(0, limit), function (entry) {
226
+ return renderDraftCard(entry, folios[entry.slug], locale);
227
+ }, 'Posts could not be loaded right now.');
228
+ return;
229
+ }
230
+
231
+ /* articles and other writing templates */
232
+ var featuredSlug = null;
233
+ if (featured && featuredSlot) {
234
+ var featuredItem = runtime.pickFeatured(entries.map(asItem));
235
+ if (featuredItem) {
236
+ var featuredEntry = entries.filter(function (a) { return a.slug === featuredItem.slug; })[0];
237
+ featuredSlug = featuredEntry && featuredEntry.slug;
238
+ featuredSlot.innerHTML = '';
239
+ featuredSlot.appendChild(renderFeatured(featuredEntry, tagHrefFrom(featuredSlot), locale));
240
+ }
241
+ }
242
+ var rest = entries.filter(function (a) { return a.slug !== featuredSlug; });
243
+ var articleTagHref = tagHrefFrom(list);
244
+ fill(list, rest.slice(0, limit), function (entry) {
245
+ return runtime.renderArchiveCard(asItem(entry), {
246
+ href: entry.url || runtime.articleUrl(entry.slug),
247
+ locale: locale,
248
+ ctaLabel: 'Read',
249
+ tagHref: articleTagHref,
250
+ metaParts: [runtime.formatDate(entry.date, locale)]
251
+ });
252
+ }, 'Articles could not be loaded right now.');
253
+ }
254
+
255
+ var sections = Array.prototype.slice.call(
256
+ document.querySelectorAll('[data-home-band][data-home-kind]:not([data-home-kind="index"])')
257
+ );
258
+
259
+ runtime.loadJson('data/writing_index.json').then(function (entries) {
260
+ var index = Array.isArray(entries) ? entries : [];
261
+ sections.forEach(function (section) { fillBand(section, index); });
262
+ }).catch(function (error) {
263
+ console.error(error);
264
+ sections.forEach(function (section) {
265
+ var list = section.querySelector('[data-home-list]');
266
+ runtime.Listing.showErrorState(list, 'Content could not be loaded right now.');
267
+ });
268
+ });
269
+
270
+ var bibPlate = document.getElementById('home-bib-plate');
271
+ if (bibPlate) {
272
+ runtime.ensureBibliographyIndex().then(function (data) {
273
+ var counts = {};
274
+ var total = 0;
275
+ var years = [];
276
+ Object.keys(data || {}).forEach(function (k) {
277
+ var entry = data[k];
278
+ var type = (entry && entry.type) || 'misc';
279
+ counts[type] = (counts[type] || 0) + 1;
280
+ total++;
281
+ var y = parseInt((entry.fields || {}).year, 10);
282
+ if (y && !isNaN(y)) years.push(y);
283
+ });
284
+ if (!total) return;
285
+ years.sort();
286
+ var typeLabels = {
287
+ article: 'Academic Articles',
288
+ book: 'Books',
289
+ techreport: 'Tech Reports',
290
+ incollection: 'Chapters',
291
+ news: 'News & Magazines',
292
+ report: 'Reports & Data',
293
+ misc: 'Web & Misc'
294
+ };
295
+ var typeOrder = ['article', 'book', 'techreport', 'incollection', 'news', 'report', 'misc'];
296
+ var rows = typeOrder.filter(function (t) { return counts[t]; }).map(function (t) {
297
+ return '<tr>' +
298
+ '<td class="bib-stat-type">' + runtime.escapeHtml(typeLabels[t] || t) + '</td>' +
299
+ '<td class="bib-stat-count">' + counts[t] + '</td>' +
300
+ '</tr>';
301
+ }).join('');
302
+ var spanLabel = years.length
303
+ ? runtime.escapeHtml(String(years[0])) + '\u2013' + runtime.escapeHtml(String(years[years.length - 1]))
304
+ : '';
305
+ bibPlate.innerHTML =
306
+ '<figcaption class="home-index-plate-head">' +
307
+ '<span class="home-index-legend"><span class="home-index-key">' +
308
+ total + ' references</span></span>' +
309
+ (spanLabel ? '<span class="home-index-tally">' + spanLabel + '</span>' : '') +
310
+ '</figcaption>' +
311
+ '<table class="bib-stat-table">' +
312
+ '<tbody>' + rows + '</tbody>' +
313
+ '</table>';
314
+ bibPlate.hidden = false;
315
+ }).catch(function () {});
316
+ }
317
+ })();
318
+ {% endraw %}
319
+ </script>
@@ -0,0 +1,9 @@
1
+ ---
2
+ layout: shell
3
+ ---
4
+
5
+ <main class="relative max-w-7xl mx-auto px-8 md:px-16 pt-10 pb-16{% if page.accent and page.accent != 'blue' %} page-accent--{{ page.accent }}{% endif %}">
6
+ {% include page/page-header.html key=page.key listing_filter=true filter_search=false spacer_class="mb-8"
7
+ filter_label="Filter by type" %}
8
+ {% include page/network-graph.html %}
9
+ </main>
@@ -0,0 +1,4 @@
1
+ ---
2
+ layout: articles
3
+ ---
4
+ {{ content }}
@@ -0,0 +1,4 @@
1
+ ---
2
+ layout: bibliography
3
+ ---
4
+ {{ content }}
@@ -0,0 +1,4 @@
1
+ ---
2
+ layout: blog
3
+ ---
4
+ {{ content }}
@@ -0,0 +1,4 @@
1
+ ---
2
+ layout: cv
3
+ ---
4
+ {{ content }}
@@ -0,0 +1,4 @@
1
+ ---
2
+ layout: resources
3
+ ---
4
+ {{ content }}
@@ -0,0 +1,4 @@
1
+ ---
2
+ layout: network
3
+ ---
4
+ {{ content }}
@@ -0,0 +1,4 @@
1
+ ---
2
+ layout: projects
3
+ ---
4
+ {{ content }}
@@ -0,0 +1,7 @@
1
+ ---
2
+ layout: text
3
+ ---
4
+
5
+ {% comment %}Studio front-matter metadata only. Public post detail pages render via ContentRuntime on the blog listing layout.{% endcomment %}
6
+
7
+ {{ content }}
@@ -0,0 +1,5 @@
1
+ ---
2
+ layout: text
3
+ ---
4
+
5
+ {{ content }}
@@ -0,0 +1,27 @@
1
+ ---
2
+ layout: text
3
+ ---
4
+
5
+ {%- comment -%}
6
+ Project detail — private/studio build only.
7
+
8
+ The public project page is *not* this file. `content/` is excluded from the
9
+ build and the projects collection is `output: false`, so /projects/:slug/ is
10
+ a Cloudflare 200-rewrite onto the listing, which assembles the page in the
11
+ browser from `_layouts/projects.html`. That layout owns the portfolio
12
+ masthead, the dossier and the media treatment.
13
+
14
+ What is left for this file is the studio preview, which renders through
15
+ `_layouts/text.html` and therefore inherits the generic dossier chrome. The
16
+ wrapper below is the one hook that stylesheet-side parity needs: it lets
17
+ assets/css/ledger/projects.css give an editor's preview the same body
18
+ treatment the published page has — figures, release videos, Plotly frames,
19
+ Mermaid diagrams and tables on the full column rather than cropped to the
20
+ 45rem prose measure. The chrome around it still differs, deliberately: a
21
+ second full implementation of the masthead here would be a second thing to
22
+ keep in step with no way to render it.
23
+ {%- endcomment -%}
24
+
25
+ <div class="projects-studio">
26
+ {{ content }}
27
+ </div>