@heroiclands/package-build 23.13.0 → 24.0.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 (236) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +2 -1
  3. package/assets/preview/gitea.ini +12 -7
  4. package/assets/styles/content-base.css +150 -1
  5. package/bin/content-commands.mjs +356 -210
  6. package/bin/package-build.mjs +3 -2
  7. package/ci/check-banners.mjs +7 -7
  8. package/config.mjs +21 -6
  9. package/content-config.mjs +199 -80
  10. package/docs/api.md +93 -72
  11. package/docs/authoring/links-and-markup.md +316 -78
  12. package/docs/commands.md +248 -80
  13. package/docs/configuration.md +210 -102
  14. package/docs/content-format.md +2 -2
  15. package/docs/diagnostics.md +2 -2
  16. package/docs/getting-started.md +1 -1
  17. package/docs/guides/icons.md +1 -1
  18. package/docs/guides/module-project.md +5 -8
  19. package/docs/guides/release-and-deploy.md +2 -2
  20. package/docs/guides/site.md +155 -23
  21. package/docs/guides/system-project.md +1 -1
  22. package/docs/guides/writing-in-invented-scripts.md +1247 -0
  23. package/docs/index.md +1 -0
  24. package/docs/project-setup.md +28 -30
  25. package/docs/reference/format-details.md +589 -308
  26. package/docs/reference/note-types.md +40 -10
  27. package/docs/reference/theme-pages.md +127 -108
  28. package/docs/source-preview.md +15 -1
  29. package/engine/asset-shipping.mjs +22 -10
  30. package/engine/base-compiler.mjs +62 -104
  31. package/engine/changelog-lint.mjs +1 -1
  32. package/engine/content-address.mjs +1 -1
  33. package/engine/content-alerts.mjs +8 -4
  34. package/engine/content-blocks.mjs +26 -11
  35. package/engine/content-embeds.mjs +1 -1
  36. package/engine/content-expand.mjs +376 -0
  37. package/engine/content-figures.mjs +13 -6
  38. package/engine/content-format.yaml +8 -0
  39. package/engine/content-glyphs.mjs +213 -0
  40. package/engine/content-images.mjs +6 -7
  41. package/engine/content-links.mjs +3 -3
  42. package/engine/content-lint.mjs +1 -1
  43. package/engine/content-render.mjs +453 -0
  44. package/engine/content-scripts.mjs +326 -27
  45. package/engine/content-spans.mjs +4 -1
  46. package/engine/content-styles.mjs +91 -0
  47. package/engine/content-symbols.mjs +186 -0
  48. package/engine/content-verse.mjs +6 -18
  49. package/engine/core-report.mjs +169 -0
  50. package/engine/core-vocabulary.mjs +208 -0
  51. package/engine/dictionary-html.mjs +119 -0
  52. package/engine/field-reference.mjs +1 -1
  53. package/engine/frontmatter-lint.mjs +1 -1
  54. package/engine/heading-attributes.mjs +89 -0
  55. package/engine/helpers.mjs +61 -101
  56. package/engine/homepage.mjs +17 -29
  57. package/engine/html-minify.mjs +70 -0
  58. package/engine/index.mjs +8 -5
  59. package/engine/language-rows.mjs +8 -2
  60. package/engine/language-terms.mjs +115 -14
  61. package/engine/language-words.mjs +12 -0
  62. package/engine/logographic.mjs +255 -0
  63. package/engine/map-lexicon.mjs +221 -25
  64. package/engine/map-places.mjs +5 -4
  65. package/engine/markdown-expressions.mjs +0 -2
  66. package/engine/markdownlint-run.mjs +9 -3
  67. package/engine/markup-validation.mjs +21 -0
  68. package/engine/metre-declaration.mjs +134 -0
  69. package/engine/note-matter.mjs +2 -2
  70. package/engine/note-vocabulary.mjs +187 -3
  71. package/engine/pdf-build.mjs +68 -133
  72. package/engine/pdf-render.mjs +102 -44
  73. package/engine/post-fields.mjs +157 -0
  74. package/engine/preview-index.mjs +130 -0
  75. package/engine/preview-source.mjs +450 -0
  76. package/engine/project-init.mjs +5 -12
  77. package/engine/prose-config.mjs +3 -3
  78. package/engine/run-words.mjs +3 -2
  79. package/engine/script-emphasis-terms.mjs +38 -0
  80. package/engine/script-emphasis.mjs +295 -0
  81. package/engine/script-font-delivery.mjs +4 -4
  82. package/engine/site-build.mjs +265 -313
  83. package/engine/site-config.mjs +26 -238
  84. package/engine/site-files.mjs +175 -0
  85. package/engine/site-index.mjs +9 -4
  86. package/engine/site-preview.mjs +50 -523
  87. package/engine/site-reach.mjs +213 -0
  88. package/engine/site-render.mjs +668 -0
  89. package/engine/site-root.mjs +90 -39
  90. package/engine/site-search.mjs +161 -0
  91. package/engine/site-serve.mjs +274 -0
  92. package/engine/site-theme-banner.mjs +154 -0
  93. package/engine/site-theme-urls.mjs +125 -0
  94. package/engine/site-theme.mjs +939 -0
  95. package/engine/sql-tables.mjs +9 -6
  96. package/engine/text-maps.mjs +57 -10
  97. package/engine/typographer.mjs +248 -0
  98. package/engine/verse-metre.mjs +357 -0
  99. package/engine/wikilink-syntax.mjs +29 -47
  100. package/engine/wikilinks.mjs +758 -279
  101. package/engine/word-forms.mjs +39 -24
  102. package/engine/word-rows.mjs +12 -2
  103. package/engine/word-terms.mjs +29 -4
  104. package/manifest.mjs +3 -4
  105. package/package.json +10 -7
  106. package/{hugo-theme → site-theme}/data/banners.yaml +6 -10
  107. package/site-theme/layouts/base.hbs +31 -0
  108. package/site-theme/layouts/blog-list.hbs +30 -0
  109. package/site-theme/layouts/blog-post.hbs +40 -0
  110. package/site-theme/layouts/home.hbs +35 -0
  111. package/site-theme/layouts/list.hbs +20 -0
  112. package/site-theme/layouts/not-found.hbs +31 -0
  113. package/site-theme/layouts/page.hbs +7 -0
  114. package/site-theme/partials/copy-buttons.hbs +37 -0
  115. package/site-theme/partials/draft-notice.hbs +5 -0
  116. package/site-theme/partials/exturl.hbs +2 -0
  117. package/{hugo-theme/layouts/partials/footer.html → site-theme/partials/footer.hbs} +10 -7
  118. package/site-theme/partials/header.hbs +44 -0
  119. package/site-theme/partials/hero-banner.hbs +11 -0
  120. package/site-theme/partials/infobox-entry.hbs +2 -0
  121. package/site-theme/partials/infobox-section.hbs +31 -0
  122. package/site-theme/partials/infobox-value.hbs +2 -0
  123. package/site-theme/partials/infobox.hbs +18 -0
  124. package/site-theme/partials/list-members.hbs +9 -0
  125. package/site-theme/partials/nav-links.hbs +5 -0
  126. package/site-theme/partials/page-body.hbs +48 -0
  127. package/site-theme/partials/page-list.hbs +14 -0
  128. package/site-theme/partials/related.hbs +24 -0
  129. package/site-theme/partials/search-meta.hbs +6 -0
  130. package/site-theme/partials/search-names.hbs +2 -0
  131. package/site-theme/partials/search.hbs +52 -0
  132. package/site-theme/partials/tags.hbs +4 -0
  133. package/site-theme/partials/toc.hbs +7 -0
  134. package/{hugo-theme → site-theme}/static/css/style.css +14 -0
  135. package/stage.mjs +1 -2
  136. package/types/config.d.mts +8 -4
  137. package/types/content-config.d.mts +15 -15
  138. package/types/engine/asset-shipping.d.mts +5 -2
  139. package/types/engine/base-compiler.d.mts +20 -2
  140. package/types/engine/content-address.d.mts +1 -1
  141. package/types/engine/content-alerts.d.mts +8 -2
  142. package/types/engine/content-blocks.d.mts +11 -4
  143. package/types/engine/content-expand.d.mts +288 -0
  144. package/types/engine/content-figures.d.mts +11 -3
  145. package/types/engine/content-glyphs.d.mts +85 -0
  146. package/types/engine/content-images.d.mts +6 -7
  147. package/types/engine/content-links.d.mts +1 -1
  148. package/types/engine/content-render.d.mts +199 -0
  149. package/types/engine/content-scripts.d.mts +62 -0
  150. package/types/engine/content-spans.d.mts +1 -1
  151. package/types/engine/content-styles.d.mts +33 -0
  152. package/types/engine/content-symbols.d.mts +35 -0
  153. package/types/engine/core-report.d.mts +45 -0
  154. package/types/engine/core-vocabulary.d.mts +53 -0
  155. package/types/engine/dictionary-html.d.mts +8 -0
  156. package/types/engine/field-reference.d.mts +1 -1
  157. package/types/engine/heading-attributes.d.mts +20 -0
  158. package/types/engine/helpers.d.mts +3 -10
  159. package/types/engine/homepage.d.mts +11 -24
  160. package/types/engine/html-minify.d.mts +9 -0
  161. package/types/engine/index.d.mts +2 -1
  162. package/types/engine/language-terms.d.mts +24 -2
  163. package/types/engine/logographic.d.mts +45 -0
  164. package/types/engine/map-lexicon.d.mts +48 -8
  165. package/types/engine/map-places.d.mts +8 -0
  166. package/types/engine/markdown-expressions.d.mts +0 -2
  167. package/types/engine/metre-declaration.d.mts +39 -0
  168. package/types/engine/pdf-render.d.mts +6 -2
  169. package/types/engine/post-fields.d.mts +44 -0
  170. package/types/engine/preview-index.d.mts +72 -0
  171. package/types/engine/preview-source.d.mts +51 -0
  172. package/types/engine/script-emphasis-terms.d.mts +21 -0
  173. package/types/engine/script-emphasis.d.mts +84 -0
  174. package/types/engine/script-font-delivery.d.mts +4 -4
  175. package/types/engine/site-build.d.mts +71 -113
  176. package/types/engine/site-config.d.mts +14 -134
  177. package/types/engine/site-files.d.mts +73 -0
  178. package/types/engine/site-index.d.mts +11 -3
  179. package/types/engine/site-preview.d.mts +15 -19
  180. package/types/engine/site-reach.d.mts +76 -0
  181. package/types/engine/site-render.d.mts +173 -0
  182. package/types/engine/site-root.d.mts +14 -4
  183. package/types/engine/site-search.d.mts +54 -0
  184. package/types/engine/site-serve.d.mts +44 -0
  185. package/types/engine/site-theme-banner.d.mts +51 -0
  186. package/types/engine/site-theme-urls.d.mts +60 -0
  187. package/types/engine/site-theme.d.mts +127 -0
  188. package/types/engine/text-maps.d.mts +17 -3
  189. package/types/engine/typographer.d.mts +11 -0
  190. package/types/engine/verse-metre.d.mts +131 -0
  191. package/types/engine/wikilink-syntax.d.mts +30 -48
  192. package/types/engine/wikilinks.d.mts +330 -14
  193. package/types/engine/word-forms.d.mts +3 -2
  194. package/types/manifest.d.mts +3 -4
  195. package/types/stage.d.mts +1 -2
  196. package/types/vendor/markdownlint.d.mts +8 -0
  197. package/vendor/markdownlint.mjs +20388 -0
  198. package/engine/web-wikilinks.mjs +0 -594
  199. package/hugo-theme/layouts/404.html +0 -85
  200. package/hugo-theme/layouts/_default/baseof.html +0 -67
  201. package/hugo-theme/layouts/_default/list.html +0 -139
  202. package/hugo-theme/layouts/_default/single.html +0 -4
  203. package/hugo-theme/layouts/blog/list.html +0 -54
  204. package/hugo-theme/layouts/blog-post/single.html +0 -36
  205. package/hugo-theme/layouts/index.html +0 -75
  206. package/hugo-theme/layouts/partials/banner-url.html +0 -141
  207. package/hugo-theme/layouts/partials/cdn-url.html +0 -36
  208. package/hugo-theme/layouts/partials/dictionary-lists.html +0 -58
  209. package/hugo-theme/layouts/partials/draft-notice.html +0 -41
  210. package/hugo-theme/layouts/partials/draft-state.html +0 -26
  211. package/hugo-theme/layouts/partials/header.html +0 -35
  212. package/hugo-theme/layouts/partials/hero-banner.html +0 -85
  213. package/hugo-theme/layouts/partials/infobox/entry.html +0 -18
  214. package/hugo-theme/layouts/partials/infobox/items.html +0 -26
  215. package/hugo-theme/layouts/partials/infobox/section.html +0 -56
  216. package/hugo-theme/layouts/partials/infobox/value.html +0 -43
  217. package/hugo-theme/layouts/partials/infobox.html +0 -57
  218. package/hugo-theme/layouts/partials/page-body.html +0 -152
  219. package/hugo-theme/layouts/partials/related.html +0 -138
  220. package/hugo-theme/layouts/partials/search.html +0 -84
  221. package/hugo-theme/layouts/partials/site-url.html +0 -32
  222. package/hugo-theme/layouts/partials/tags.html +0 -30
  223. package/hugo-theme/layouts/partials/toc.html +0 -28
  224. package/hugo-theme/layouts/partials/warn-missing-banner.html +0 -3
  225. package/hugo-theme/layouts/shortcodes/cartouche.html +0 -64
  226. package/hugo-theme/layouts/shortcodes/exturl.html +0 -12
  227. package/hugo-theme/layouts/shortcodes/glyph.html +0 -43
  228. package/hugo-theme/layouts/shortcodes/pagelist.html +0 -63
  229. package/hugo-theme/theme.toml +0 -11
  230. package/types/engine/web-wikilinks.d.mts +0 -107
  231. /package/{hugo-theme → site-theme}/data/banner-kinds.yaml +0 -0
  232. /package/{hugo-theme → site-theme}/static/css/sohl-icons.css +0 -0
  233. /package/{hugo-theme → site-theme}/static/fonts/game-icons-kb.woff2 +0 -0
  234. /package/{hugo-theme → site-theme}/static/img/banners/takheperu-hieroglyphs.svg +0 -0
  235. /package/{hugo-theme → site-theme}/static/img/cartouches/takheperu.svg +0 -0
  236. /package/{hugo-theme → site-theme}/static/js/inline-spoilers.js +0 -0
@@ -1,67 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>{{ if not .IsHome }}{{ .Title }} — {{ end }}{{ .Site.Title }}</title>
7
- <meta name="description" content="{{ with .Params.description }}{{ . }}{{ else }}{{ .Site.Params.description }}{{ end }}">
8
- <link rel="preconnect" href="https://fonts.googleapis.com">
9
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
- <link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Lora:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600&display=swap" rel="stylesheet">
11
- <link rel="stylesheet" href="{{ "css/style.css" | relURL }}">
12
- {{/* Script fonts: the site build sets params.scriptFonts only when it has
13
- copied them. */}}
14
- {{ with .Site.Params.scriptFonts }}<link rel="stylesheet" href="{{ . | relURL }}">{{ end }}
15
- {{/* Icon glyphs. SoHL documentation writes icons as <i class="fa-solid …">
16
- or <i class="ginf-…">, the same markup the Foundry client renders, so a
17
- page reads identically in a journal and here. Font Awesome's FREE set is
18
- enough — the system deliberately uses no Pro-only icons — and the
19
- game-icons webfont is subset to just the glyphs the docs reference
20
- (~3 KB), generated by the SoHL repo. */}}
21
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.3.1/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer">
22
- <link rel="stylesheet" href="{{ "css/sohl-icons.css" | relURL }}">
23
- </head>
24
- <body>
25
- {{ partial "header.html" . }}
26
- <main>
27
- {{ block "main" . }}{{ end }}
28
- </main>
29
- {{ partial "footer.html" . }}
30
- <script>
31
- // Auto-enhance <span data-copy="URL"> elements with a clipboard copy button
32
- document.querySelectorAll('[data-copy]').forEach(function(el) {
33
- var url = el.getAttribute('data-copy');
34
- el.classList.add('exturl');
35
-
36
- // Wrap existing text in a code element if not already
37
- if (!el.querySelector('.exturl-text')) {
38
- var code = document.createElement('code');
39
- code.className = 'exturl-text';
40
- code.textContent = el.textContent;
41
- el.textContent = '';
42
- el.appendChild(code);
43
- }
44
-
45
- var btn = document.createElement('button');
46
- btn.className = 'exturl-copy';
47
- btn.title = 'Copy URL to clipboard';
48
- btn.setAttribute('aria-label', 'Copy URL to clipboard');
49
- btn.innerHTML =
50
- '<svg class="exturl-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' +
51
- '<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>' +
52
- '<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/>' +
53
- '</svg>' +
54
- '<svg class="exturl-check" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">' +
55
- '<polyline points="20 6 9 17 4 12"/>' +
56
- '</svg>';
57
- btn.addEventListener('click', function() {
58
- navigator.clipboard.writeText(url).then(function() {
59
- btn.classList.add('copied');
60
- setTimeout(function() { btn.classList.remove('copied'); }, 1500);
61
- });
62
- });
63
- el.appendChild(btn);
64
- });
65
- </script>
66
- </body>
67
- </html>
@@ -1,139 +0,0 @@
1
- {{- /*
2
- Default list layout: hero, the authored body when there is one,
3
- and an auto-list of the section's members.
4
-
5
- Minimal by design: a package-build consumer disables Hugo's `section`,
6
- `taxonomy` and `term` kinds, so this layout never runs for one — the home
7
- page and its pages are all it renders through `index.html` and
8
- `_default/single.html`. A site that keeps those kinds enabled — one that
9
- is not a package-build consumer, publishing a hand-curated section of its
10
- own — still needs a landing for them, which is what this file is for.
11
-
12
- Content rows render "Name + description", inline rather than through a
13
- shared partial: this is the only caller left, and a description is emitted
14
- only when present, so a page carrying none renders a plain title row.
15
- */ -}}
16
- {{ define "main" }}
17
- {{ partial "hero-banner.html" . }}
18
- <article class="single">
19
- <div class="single-content">
20
- {{ if .Content }}
21
- <div class="single-body" data-pagefind-body>
22
- {{ partial "dictionary-lists.html" .Content }}
23
- </div>
24
- {{ end }}
25
-
26
- {{- /*
27
- A curated landing is the best navigation — a rules section groups its
28
- pages under editorial headings like "Key Concepts", which no hierarchy
29
- can infer. But gating the auto-list on "has no body" meant authoring an
30
- intro silently replaced the section's ONLY navigation, and hand-curated
31
- lists drift: one guide reached 41 pages linking 1, and another had
32
- quietly orphaned a page.
33
-
34
- So the auto-list is a GAP-FILLER: a section with a body lists only the
35
- pages that are not reachable from that body by following links. A landing
36
- whose hierarchy covers everything shows nothing extra, and a page can
37
- never become unreachable — a newly added page appears under "Orphaned
38
- Pages" until someone files it into the curated index.
39
-
40
- Taxonomy and term pages keep their own listing shapes below.
41
- */ -}}
42
- {{- $showAutoList :=
43
- or (eq .Kind "taxonomy")
44
- (eq .Kind "term")
45
- (eq .Kind "section")
46
- -}}
47
- {{- $members := .Pages -}}
48
- {{- /*
49
- Pages reachable from the authored body, matched on RelPermalink so the
50
- comparison survives aliases and wikilink resolution.
51
-
52
- The needle deliberately stops after the URL rather than closing the quote,
53
- so a link that carries a fragment counts. A rules landing reaches its
54
- Characters chapter as `…/characters-introduction/#body-structure`, and
55
- demanding the closing quote made that chapter read as unlinked. A trailing
56
- slash ends every RelPermalink, so the open-ended match cannot bleed into a
57
- sibling (`/rules/fate/` never prefixes `/rules/fate-points/`).
58
-
59
- Reachability is TRANSITIVE, because a curated corpus is a hierarchy rather
60
- than a flat index: a rules landing links ten chapter introductions, and
61
- each of those links its own pages — up to four hops deep (root → Trauma →
62
- Body → Injury → Bleeding). Testing only what the landing links DIRECTLY
63
- counted every page below the first hop as a leftover. So a page counts as
64
- listed when the body links it, or when any page already counted as listed
65
- links it.
66
-
67
- The walk runs to a fixed point: each pass appends the newly reached pages'
68
- content to the haystack, and the first pass that reaches nothing new stops
69
- it. Twelve passes is far past the depth of any corpus here (the rules need
70
- four), and the `$growing` guard makes the surplus passes free.
71
- */ -}}
72
- {{- $unlisted := $members -}}
73
- {{- if and (eq .Kind "section") .Content -}}
74
- {{- $haystack := .Content -}}
75
- {{- $reached := slice -}}
76
- {{- $growing := true -}}
77
- {{- range seq 12 -}}
78
- {{- if $growing -}}
79
- {{- $growing = false -}}
80
- {{- range $members -}}
81
- {{- if not (in $reached .RelPermalink) -}}
82
- {{- if in $haystack (printf "href=\"%s" .RelPermalink) -}}
83
- {{- $reached = $reached | append .RelPermalink -}}
84
- {{- $haystack = print $haystack .Content -}}
85
- {{- $growing = true -}}
86
- {{- end -}}
87
- {{- end -}}
88
- {{- end -}}
89
- {{- end -}}
90
- {{- end -}}
91
- {{- $unlisted = where $members "RelPermalink" "not in" $reached -}}
92
- {{- end -}}
93
- {{- /*
94
- Label the leftovers only when a curated body precedes them. With
95
- reachability transitive, anything still here is genuinely unreachable
96
- by reading — an orphan — so the heading says so rather than claiming to be
97
- the section's full contents.
98
- */ -}}
99
- {{- $labelList := and (eq .Kind "section") .Content -}}
100
- {{- if $showAutoList -}}
101
- {{- with $unlisted -}}
102
- {{- if eq $.Kind "taxonomy" -}}
103
- <ul class="page-list">
104
- {{- range . }}
105
- <li class="page-list-item">
106
- <a class="page-list-title" href="{{ .RelPermalink }}">{{ .Title }}</a>
107
- <span class="page-list-count">{{ len .Pages }}</span>
108
- </li>
109
- {{- end }}
110
- </ul>
111
- {{- else -}}
112
- {{- if $labelList }}
113
- <h2 class="page-list-heading">Orphaned Pages</h2>
114
- {{- end }}
115
- <ul class="page-list">
116
- {{- range . }}
117
- <li class="page-list-item">
118
- <a class="page-list-title" href="{{ .RelPermalink }}">{{ .Title }}</a>
119
- {{- with .Description }}
120
- <span class="page-list-desc">{{ . }}</span>
121
- {{- end }}
122
- </li>
123
- {{- end }}
124
- </ul>
125
- {{- end -}}
126
- {{- else -}}
127
- {{- /*
128
- "Nothing here yet" is the right answer for an empty auto-listed section,
129
- and the wrong one for a curated landing — there a leftover list of zero
130
- means the hierarchy covers everything, so nothing should be said at all.
131
- */ -}}
132
- {{- if not $labelList -}}
133
- <p class="page-list-empty">Nothing here yet.</p>
134
- {{- end -}}
135
- {{- end -}}
136
- {{- end }}
137
- </div>
138
- </article>
139
- {{ end }}
@@ -1,4 +0,0 @@
1
- {{ define "main" }}
2
- {{ partial "hero-banner.html" . }}
3
- {{ partial "page-body.html" . }}
4
- {{ end }}
@@ -1,54 +0,0 @@
1
- {{ define "main" }}
2
- {{ partial "hero-banner.html" . }}
3
- <article class="single">
4
- <div class="single-content">
5
- {{ if .Content }}
6
- <div class="single-body">
7
- {{ partial "dictionary-lists.html" .Content }}
8
- </div>
9
- {{ end }}
10
-
11
- {{- /* Blog list: walk every descendant page in any /blog/YYYY/MM/
12
- date bucket, filter to actual blog posts, sort newest-first,
13
- and emit a list of entries with date + series + title +
14
- description. The default list.html skips auto-listing for
15
- hand-crafted section indexes (any _index.md), which is
16
- correct for most sections but wrong for a blog landing. */ -}}
17
- {{- $posts := where .Site.RegularPages "Section" "blog" -}}
18
- {{- $posts = where $posts "Type" "blog-post" -}}
19
- {{- $posts = $posts.ByDate.Reverse -}}
20
-
21
- {{- with $posts }}
22
- <ul class="blog-list">
23
- {{- range . }}
24
- <li class="blog-list-item">
25
- {{ if .Date }}
26
- <time class="blog-list-date" datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "January 2, 2006" }}</time>
27
- {{ end }}
28
- <a class="blog-list-title" href="{{ .RelPermalink }}">{{ .Title }}</a>
29
- {{- /* Auto-generate a ~100-word preview from the rendered body.
30
- `.Plain` strips HTML tags. `htmlUnescape` then decodes
31
- entities so smart quotes Goldmark emitted as `&rsquo;`
32
- etc. become real characters (otherwise the browser
33
- displays them literally after Hugo re-escapes the `&`).
34
- Collapse paragraph-break whitespace into single spaces,
35
- and append an ellipsis only if we actually truncated. */ -}}
36
- {{- $plain := .Plain | htmlUnescape -}}
37
- {{- $collapsed := trim (replaceRE `\s+` " " $plain) " " -}}
38
- {{- $words := split $collapsed " " -}}
39
- {{- $preview := $collapsed -}}
40
- {{- if gt (len $words) 100 -}}
41
- {{- $preview = printf "%s…" (delimit (first 100 $words) " ") -}}
42
- {{- end -}}
43
- {{- with $preview }}
44
- <p class="blog-list-desc">{{ . }}</p>
45
- {{- end }}
46
- </li>
47
- {{- end }}
48
- </ul>
49
- {{- else }}
50
- <p class="page-list-empty">No posts yet.</p>
51
- {{- end }}
52
- </div>
53
- </article>
54
- {{ end }}
@@ -1,36 +0,0 @@
1
- {{ define "main" }}
2
- {{ partial "hero-banner.html" . }}
3
-
4
- {{- /* Blog posts use the same content chrome as other pages but with
5
- a blog-specific meta line (series + date) and no right-rail
6
- sidebar or auto-generated TOC. If a post grows long enough to
7
- warrant a TOC, we can opt it in via a frontmatter flag later. */ -}}
8
-
9
- <article class="single">
10
- <div class="single-content">
11
- <div class="single-meta blog-meta">
12
- {{ with .Params.series }}
13
- <div class="blog-series">
14
- <span class="blog-series-label">Series</span>
15
- <span class="blog-series-name">{{ . }}</span>
16
- </div>
17
- {{ end }}
18
- {{ if .Params.date }}
19
- <time class="single-date" datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "January 2, 2006" }}</time>
20
- {{ end }}
21
- {{ partial "tags.html" . }}
22
- </div>
23
-
24
- <div class="single-body" data-pagefind-body>
25
- {{ partial "dictionary-lists.html" .Content }}
26
- </div>
27
-
28
- {{ partial "related.html" . }}
29
-
30
- <nav class="single-nav">
31
- {{ with .PrevInSection }}<a href="{{ .Permalink }}" class="nav-prev">&larr; {{ .Title }}</a>{{ end }}
32
- {{ with .NextInSection }}<a href="{{ .Permalink }}" class="nav-next">{{ .Title }} &rarr;</a>{{ end }}
33
- </nav>
34
- </div>
35
- </article>
36
- {{ end }}
@@ -1,75 +0,0 @@
1
- {{- /*
2
- Home layout. Two shapes share it, chosen by what the home page carries.
3
-
4
- **A package homepage** — the entry point to one published package, at its
5
- own prefix (/sohl/, /thalorna/, /kethira/, …), mounted as the package's
6
- `_index.md` and selected when it declares `type: homepage`. It renders
7
- through the same structure `_default/single.html` gives a note — the hero
8
- from its `banner`, then its body — through the shared `page-body.html`
9
- partial, so a homepage and a page look like one family. A package supplies
10
- only its own title, description, banner and body; the theme owns the
11
- arrangement.
12
-
13
- **The featured grid** — a site whose home page is a set of entry points into
14
- everything it publishes. This is the older shape and stays the default, so a
15
- consumer that has not adopted the homepage note keeps rendering exactly as
16
- before.
17
-
18
- The theme owns the arrangement; the consuming site owns every address and
19
- every word. The grid reads them from `params.home`:
20
-
21
- heroimage Banner behind the hero band. Resolved through cdn-url.html,
22
- so it may be absolute or relative to `params.cdnBaseURL`.
23
- herotitle Hero heading.
24
- herotagline Hero standfirst.
25
- headings Column headings across the top row of the featured grid.
26
- Each: `title`, and an optional `class`, appended as
27
- `home-section-title--<class>` for grid placement.
28
- cards The entry-point cards. Each: `title`, `url`, `text`, and
29
- optional `image`, `alt` (image alt text, defaulting to
30
- `title`) and `key` (appended as `home-card--<key>` for
31
- grid placement).
32
-
33
- Headings and cards render flat, as direct children of `.home-featured`,
34
- because they are the grid's items — the CSS places each one by its
35
- modifier class.
36
- */ -}}
37
- {{ define "main" }}
38
- {{- if eq (.Params.type | default "") "homepage" -}}
39
- {{- partial "hero-banner.html" . -}}
40
- {{- partial "page-body.html" . -}}
41
- {{- else -}}
42
- {{- with .Site.Params.home }}
43
- <section class="hero hero-with-image"{{ with .heroimage }} style="background-image: url('{{ partial "cdn-url.html" (dict "site" $.Site "path" .) }}');"{{ end }}>
44
- <div class="hero-inner" data-pagefind-ignore>
45
- <h1>{{ .herotitle }}</h1>
46
- {{- with .herotagline }}
47
- <p class="hero-tagline">{{ . }}</p>
48
- {{- end }}
49
- </div>
50
- </section>
51
-
52
- <section class="home-sections">
53
- <div class="home-featured">
54
- {{- range .headings }}
55
- <h2 class="home-section-title{{ with .class }} home-section-title--{{ . }}{{ end }}">{{ .title }}</h2>
56
- {{- end }}
57
- {{- range .cards }}
58
-
59
- <div class="home-card{{ with .key }} home-card--{{ . }}{{ end }}">
60
- {{- $url := .url }}{{ $title := .title }}{{ $alt := .alt | default .title }}
61
- {{- with .image }}
62
- <a href="{{ $url }}">
63
- <img src="{{ partial "cdn-url.html" (dict "site" $.Site "path" .) }}" alt="{{ $alt }}" class="card-image">
64
- </a>
65
- {{- end }}
66
- <h2><a href="{{ $url }}">{{ $title }}</a></h2>
67
- <p>{{ .text }}</p>
68
- <a href="{{ $url }}" class="card-button">More Info →</a>
69
- </div>
70
- {{- end }}
71
- </div>
72
- </section>
73
- {{- end }}
74
- {{- end }}
75
- {{ end }}
@@ -1,141 +0,0 @@
1
- {{- /*
2
- Resolve a `banner:` value to a URL, or to "" when there is none.
3
-
4
- Split out of `hero-banner.html` (issue #41): a single implementation is what
5
- keeps a second caller from drifting the way issue #36 already failed once —
6
- silently, behind a dead URL.
7
-
8
- Resolution, in order:
9
- 1. `data.banner` — a note's own hero image, which the content build has
10
- already resolved from an asset Address into the address its asset host
11
- serves. It is used exactly as given. A blank one is a note naming no
12
- hero image on purpose and declines an image.
13
- 2. `banner` — a hand-authored page's value. `none` (or `false`) declines an
14
- image and returns "". A full URL is used as-is; anything else is a
15
- fragment under `images/`.
16
- 3. `images/banners/{name}.webp`, when neither is set. The name is the
17
- `subtype` supplied, or, when the caller passes the page's `type`, the
18
- banner `data/banner-kinds.yaml` maps that `type` and `subType` to — a
19
- `category` for the types it lists as taking theirs from there. A type
20
- absent from the mapping keeps its own name. A caller that supplies
21
- neither gets "" instead, which is
22
- what a card wants: a section with no banner of its own renders as the
23
- text card it rendered before.
24
- 4. `images/banners/{fallback}` — whenever the name picked by 2 or 3 is not
25
- in `data/banners.yaml`'s declared inventory. Hugo cannot ask a remote
26
- host whether a URL exists, so the declaration stands in for the test;
27
- see `hero-banner.html` for why, and `data/banners.yaml` for how the
28
- list is kept honest.
29
-
30
- The check applies only to a relative path resolving directly inside the
31
- declared banner directory. An absolute URL, and a fragment pointing anywhere
32
- else under `images/`, pass through untouched: the theme has no inventory for
33
- either and must not second-guess an address it cannot know about. A step 1
34
- address is never checked either — the build resolved it against a real asset
35
- record and dropped one nothing answered, so there is no question left to
36
- ask.
37
-
38
- Whatever survives resolves through `cdn-url.html`, so the theme names a path
39
- and never a host.
40
-
41
- Usage:
42
- {{ partial "banner-url.html" (dict "site" .Site "banner" .Params.banner
43
- "subtype" $subtype "context" .RelPermalink) }}
44
-
45
- Params:
46
- site — the site context (.Site), for `params.cdnBaseURL`.
47
- data — the page's `data` map (`.Params.data`), read for the `banner`
48
- the content build resolved. Omit for a page that carries none.
49
- banner — the raw `banner:` value; may be unset, `none`/`false`, a
50
- fragment or an absolute URL.
51
- subtype — optional. The name to default to when `banner` is unset and no
52
- `type` is given. Omit for "no banner means no image".
53
- type — optional. The page's note `type`; with `subType` and `category`
54
- it selects the default name through `data/banner-kinds.yaml`.
55
- subType — optional. The page's note `subType`.
56
- category — optional. The page's `category`, for the types that take their
57
- banner name from it.
58
- context — optional. A page address, named in the missing-banner warning so
59
- the report says where the want came from.
60
- */ -}}
61
- {{- $site := .site -}}
62
- {{- $subtype := .subtype | default "" -}}
63
- {{- $context := .context | default "" -}}
64
- {{- $data := .data | default dict -}}
65
-
66
- {{- /* The default name for a page that states its `type`. A type maps to one
67
- name or to a table keyed by subType; one that the mapping does not know
68
- keeps its own name, so the missing-banner warning below still reports
69
- it. */ -}}
70
- {{- with .type -}}
71
- {{- $kinds := index (hugo.Data | default dict) "banner-kinds" | default dict -}}
72
- {{- $entry := index ($kinds.kinds | default dict) . -}}
73
- {{- if in ($kinds.categoryTypes | default slice) . -}}
74
- {{- $subtype = $.category | default "default" -}}
75
- {{- else if reflect.IsMap $entry -}}
76
- {{- $subtype = (index $entry (lower ($.subType | default ""))) | default . -}}
77
- {{- else -}}
78
- {{- $subtype = $entry | default . -}}
79
- {{- end -}}
80
- {{- end -}}
81
-
82
- {{- $banners := hugo.Data.banners | default dict -}}
83
- {{- $dir := $banners.dir | default "images/banners" -}}
84
- {{- $fallback := $banners.fallback | default "default.webp" -}}
85
- {{- $available := $banners.available | default slice -}}
86
-
87
- {{- /* `none`/`false` is a decision, not an absence, so it is read before the
88
- resolution order rather than as a step in it. Formatted rather than
89
- tested for truth, and deliberately not passed through `default`: both
90
- `false` and an unset banner are falsy, and only formatting tells them
91
- apart. */ -}}
92
- {{- $raw := printf "%v" .banner -}}
93
- {{- $declined := or (eq (lower $raw) "none") (eq $raw "false") -}}
94
-
95
- {{- /* A note's resolved banner, formatted for the same reason the decline
96
- above is: an unset key and an empty string are both falsy, and only
97
- formatting tells them apart. `<nil>` covers both an absent key and an
98
- explicit `null`, which the note format reads as *unset* — so the
99
- resolution carries on to the subtype default. */ -}}
100
- {{- $resolved := printf "%v" (index $data "banner") -}}
101
- {{- $fromBuild := ne $resolved "<nil>" -}}
102
-
103
- {{- $banner := "" -}}
104
- {{- $absolute := false -}}
105
- {{- if $fromBuild -}}
106
- {{- $banner = $resolved -}}
107
- {{- $absolute = true -}}
108
- {{- else if not $declined -}}
109
- {{- with .banner -}}
110
- {{- $banner = printf "images/%s" (strings.TrimPrefix "/" .) -}}
111
- {{- if or (hasPrefix . "http://") (hasPrefix . "https://") (hasPrefix . "//") -}}
112
- {{- $banner = . -}}
113
- {{- $absolute = true -}}
114
- {{- end -}}
115
- {{- else -}}
116
- {{- with $subtype -}}
117
- {{- $banner = printf "%s/%s.webp" $dir . -}}
118
- {{- end -}}
119
- {{- end -}}
120
-
121
- {{- /* The declared-existence check. Deduplicated by name, so a subtype with
122
- no artwork is reported once per build rather than once per page. */ -}}
123
- {{- if and $banner (not $absolute) (eq (path.Dir $banner) $dir) -}}
124
- {{- $name := strings.TrimSuffix (path.Ext $banner) (path.Base $banner) -}}
125
- {{- if not (in $available $name) -}}
126
- {{- partialCached "warn-missing-banner.html" (dict
127
- "name" $name
128
- "dir" $dir
129
- "fallback" $fallback
130
- "context" $context
131
- ) $name -}}
132
- {{- $banner = printf "%s/%s" $dir $fallback -}}
133
- {{- end -}}
134
- {{- end -}}
135
- {{- end -}}
136
-
137
- {{- $url := "" -}}
138
- {{- if $banner -}}
139
- {{- $url = partial "cdn-url.html" (dict "site" $site "path" $banner) -}}
140
- {{- end -}}
141
- {{- return $url -}}
@@ -1,36 +0,0 @@
1
- {{- /*
2
- Resolve an asset path against the consuming site's asset host.
3
-
4
- The theme carries layout, not addresses: it never names the host its
5
- imagery is served from. A consumer sets `params.cdnBaseURL` to the root of
6
- wherever it publishes shared artwork, and every image reference in the
7
- theme resolves through here.
8
-
9
- Resolution:
10
- 1. An already-absolute path (http://, https://, //) is returned as-is,
11
- so page frontmatter can point anywhere.
12
- 2. Otherwise the path is joined onto `params.cdnBaseURL`.
13
- 3. With no `params.cdnBaseURL` set, the path resolves against the site
14
- itself via relURL — correct under a bare root *and* under a baseURL
15
- carrying a path prefix.
16
-
17
- Usage:
18
- {{ partial "cdn-url.html" (dict "site" .Site "path" "images/x.webp") }}
19
-
20
- Params:
21
- site — the site context (.Site), for reading params.
22
- path — the asset path, absolute or relative to the asset root.
23
- */ -}}
24
- {{- $path := .path -}}
25
- {{- $url := "" -}}
26
- {{- if or (hasPrefix $path "http://") (hasPrefix $path "https://") (hasPrefix $path "//") -}}
27
- {{- $url = $path -}}
28
- {{- else -}}
29
- {{- $rel := strings.TrimPrefix "/" $path -}}
30
- {{- with .site.Params.cdnBaseURL -}}
31
- {{- $url = printf "%s/%s" (strings.TrimSuffix "/" .) $rel -}}
32
- {{- else -}}
33
- {{- $url = $rel | relURL -}}
34
- {{- end -}}
35
- {{- end -}}
36
- {{- return $url -}}
@@ -1,58 +0,0 @@
1
- {{- /* Group the final Goldmark HTML. Quote-aware tokens preserve attributes,
2
- literal examples, comments and raw blocks; a stack gives each nested list
3
- its own rows. Existing grouped lists keep their authored divs. */ -}}
4
- {{- /* One return, at the end: Hugo 0.163, which the CI runners use, fails on
5
- a `return` inside a block. */ -}}
6
- {{- $source := string . -}}
7
- {{- $result := $source -}}
8
- {{- if findRE `(?i)<dl(?:\s|>)` $source -}}
9
- {{- $pattern := `(?is)<!--.*?-->|<script\b(?:[^"'<>]|"[^"]*"|'[^']*')*>.*?</script\s*>|<style\b(?:[^"'<>]|"[^"]*"|'[^']*')*>.*?</style\s*>|<pre\b(?:[^"'<>]|"[^"]*"|'[^']*')*>.*?</pre\s*>|<textarea\b(?:[^"'<>]|"[^"]*"|'[^']*')*>.*?</textarea\s*>|<![^>]*>|</?[a-z][a-z0-9:-]*(?:[^"'<>]|"[^"]*"|'[^']*')*>|[^<]+|<` -}}
10
- {{- $parts := slice -}}
11
- {{- $lists := slice -}}
12
- {{- $elements := slice -}}
13
- {{- $tokens := findRE $pattern $source -}}
14
- {{- range $i, $token := $tokens -}}
15
- {{- if and (gt (len $lists) 0) (gt $i 0) (lt (add $i 1) (len $tokens)) (findRE `^\s+$` $token) (findRE `(?i)^</dt\s*>$` (index $tokens (sub $i 1))) (findRE `(?i)^<dd(?:\s|>)` (index $tokens (add $i 1))) -}}
16
- {{- $token = "" -}}
17
- {{- end -}}
18
- {{- if and (gt (len $lists) 0) (gt $i 0) (lt (add $i 1) (len $tokens)) (findRE `^\s+$` $token) (findRE `(?i)^<dd(?:\s[^>]*)?>$` (index $tokens (sub $i 1))) (findRE `(?i)^<p(?:\s|>)` (index $tokens (add $i 1))) -}}
19
- {{- $token = "" -}}
20
- {{- end -}}
21
- {{- $tag := "" -}}
22
- {{- $opaque := findRE `(?i)^<(?:!--|script\b|style\b|pre\b|textarea\b)` $token -}}
23
- {{- if and (not $opaque) (findRE `(?i)^</?[a-z]` $token) -}}
24
- {{- $tag = lower (replaceRE `(?is)^</?([a-z][a-z0-9:-]*).*` "$1" $token) -}}
25
- {{- $closing := hasPrefix $token "</" -}}
26
- {{- if and (eq $tag "dl") (not $closing) -}}
27
- {{- $list := newScratch -}}
28
- {{- $list.Set "row" false -}}
29
- {{- $lists = $lists | append $list -}}
30
- {{- if or (findRE `(?i)\sclass\s*=\s*["'](?:[^"']*\s)?dictionary(?:\s|["'])` $token) (findRE `(?i)\sclass\s*=\s*dictionary(?:\s|>)` $token) -}}
31
- {{- else if findRE `(?i)\sclass\s*=\s*["']` $token -}}
32
- {{- $token = replaceRE `(?i)(\sclass\s*=\s*["'])` "${1}dictionary " $token -}}
33
- {{- else if findRE `(?i)\sclass\s*=` $token -}}
34
- {{- $token = replaceRE `(?i)(\sclass\s*=\s*)([^\s>]+)` `${1}"dictionary ${2}"` $token -}}
35
- {{- else -}}
36
- {{- $token = replaceRE `(?i)^<dl` `<dl class="dictionary"` $token -}}
37
- {{- end -}}
38
- {{- else if and (eq $tag "dt") (not $closing) (gt (len $lists) 0) (eq (index $elements (sub (len $elements) 1)) "dl") -}}
39
- {{- $list := index $lists (sub (len $lists) 1) -}}
40
- {{- if $list.Get "row" -}}{{- $parts = $parts | append "</div>" -}}{{- end -}}
41
- {{- $parts = $parts | append "<div>" -}}
42
- {{- $list.Set "row" true -}}
43
- {{- else if and (eq $tag "dl") $closing (gt (len $lists) 0) -}}
44
- {{- $list := index $lists (sub (len $lists) 1) -}}
45
- {{- if $list.Get "row" -}}{{- $parts = $parts | append "</div>" -}}{{- end -}}
46
- {{- $lists = first (sub (len $lists) 1) $lists -}}
47
- {{- end -}}
48
- {{- if $closing -}}
49
- {{- if gt (len $elements) 0 -}}{{- $elements = first (sub (len $elements) 1) $elements -}}{{- end -}}
50
- {{- else if not (or (findRE `/\s*>$` $token) (in (slice "area" "base" "br" "col" "embed" "hr" "img" "input" "link" "meta" "param" "source" "track" "wbr") $tag)) -}}
51
- {{- $elements = $elements | append $tag -}}
52
- {{- end -}}
53
- {{- end -}}
54
- {{- $parts = $parts | append $token -}}
55
- {{- end -}}
56
- {{- $result = delimit $parts "" -}}
57
- {{- end -}}
58
- {{- return ($result | safeHTML) -}}
@@ -1,41 +0,0 @@
1
- {{- /*
2
- A page's statement that its own content is not settled.
3
-
4
- A draft page reads exactly like a finished one, and a reader who is not told
5
- assumes it is finished — so the page says so itself, at the head of the
6
- article, above the infobox and above the prose. A reader deciding whether to
7
- rely on an entry has to be told before they read it.
8
-
9
- The label alone will not do. "Draft" names the writing, so it reads as rough
10
- prose; what has to land is that the facts may change. The label and the
11
- sentence are one unit, and the sentence carries the meaning by itself — which
12
- is the floor this is built to, because the mark is a third-party font and the
13
- rule is a stylesheet, and either can be absent.
14
-
15
- The sentence is the theme's default and a consumer overrides it with
16
- `params.draftNotice`, the way `params.notfound` supplies its own wording. The
17
- theme states no consumer's name and no address.
18
-
19
- The mark is `circle-exclamation`: a notice, not a hazard. Draft-ness is a
20
- status, and a page in a corpus where one page in several carries one cannot
21
- wear a hazard mark without teaching a reader to discount it. `aria-hidden`
22
- keeps it out of a screen reader, where the label reads the same thing.
23
-
24
- The sentence sets in italic and the label in bold roman. Every other word on
25
- the page is the world describing itself; this sentence is the page speaking
26
- about its own state, and italic is how a reader is told the voice has
27
- changed. It is an `em`, so the italic arrives with the markup rather than
28
- with the stylesheet, and a bold italic beside the mark would be one emphasis
29
- too many.
30
-
31
- Silent-disappear: a settled page emits nothing here.
32
-
33
- Context: the page.
34
- */ -}}
35
- {{- if partial "draft-state.html" . -}}
36
- {{- $notice := .Site.Params.draftNotice | default "This entry is unfinished. What it states may change, and nothing in it is settled." -}}
37
- <aside class="draft-notice">
38
- <i class="fa-solid fa-circle-exclamation draft-notice-icon" aria-hidden="true"></i>
39
- <p class="draft-notice-text"><strong class="draft-notice-label">Draft.</strong> <em>{{ $notice }}</em></p>
40
- </aside>
41
- {{ end -}}