@janga/norna 0.7.9 → 0.7.11

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 (287) hide show
  1. package/README.md +4 -2
  2. package/bin/norna-cli.mjs +3 -0
  3. package/docs/README.md +16 -12
  4. package/docs/commands.md +6 -0
  5. package/docs/configuration.md +10 -54
  6. package/docs/content.md +20 -21
  7. package/docs/design/norna-diagram-design.md +2 -0
  8. package/docs/design/site-examples-structure.md +35 -24
  9. package/docs/engine-development.md +13 -7
  10. package/docs/getting-started.md +56 -19
  11. package/docs/routes.md +8 -6
  12. package/docs/site-structure.md +11 -6
  13. package/docs/sitewide-content.md +74 -0
  14. package/docs/theme.md +125 -140
  15. package/docs/typography.md +72 -45
  16. package/examples/README.md +41 -0
  17. package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/site.schema.json +59 -0
  18. package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/sitewide.schema.json +138 -0
  19. package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/theme.schema.json +755 -0
  20. package/examples/complete-sites/dog-shelter-multi-page/.astro/content-modules.mjs +1 -0
  21. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/content.d.ts +8 -0
  22. package/examples/complete-sites/dog-shelter-multi-page/.astro/data-store.json +1 -0
  23. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/dev-local.json +2 -2
  24. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/dev.json +2 -2
  25. package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.log +53 -0
  26. package/examples/complete-sites/dog-shelter-multi-page/.astro/settings.json +5 -0
  27. package/examples/complete-sites/dog-shelter-multi-page/README.md +9 -0
  28. package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/_metadata.json +13 -7
  29. package/examples/complete-sites/dog-shelter-multi-page/site/.norna/generated-images.json +18 -0
  30. package/examples/complete-sites/dog-shelter-multi-page/site/config.mjs +30 -0
  31. package/examples/complete-sites/dog-shelter-multi-page/site/content.md +12 -0
  32. package/examples/complete-sites/dog-shelter-multi-page/site/public/favicon.svg +10 -0
  33. package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rex.svg +19 -0
  34. package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rover.svg +19 -0
  35. package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/route-content.md +30 -0
  36. package/examples/complete-sites/dog-shelter-multi-page/site/routes/020-adopt/route-content.md +12 -0
  37. package/examples/complete-sites/dog-shelter-multi-page/site/sitewide-content.md +6 -0
  38. package/examples/complete-sites/dog-shelter-multi-page/site/theme.md +25 -0
  39. package/examples/complete-sites/dog-shelter-single-page/README.md +9 -0
  40. package/examples/complete-sites/dog-shelter-single-page/site/.norna/generated-images.json +18 -0
  41. package/examples/complete-sites/dog-shelter-single-page/site/config.mjs +30 -0
  42. package/examples/complete-sites/dog-shelter-single-page/site/content.md +38 -0
  43. package/examples/complete-sites/dog-shelter-single-page/site/images/what-we-do/dog-house.svg +25 -0
  44. package/examples/complete-sites/dog-shelter-single-page/site/images/you-can-help/heart.svg +13 -0
  45. package/examples/complete-sites/dog-shelter-single-page/site/public/favicon.svg +10 -0
  46. package/examples/complete-sites/dog-shelter-single-page/site/public/robots.txt +2 -0
  47. package/examples/complete-sites/dog-shelter-single-page/site/sitewide-content.md +6 -0
  48. package/examples/complete-sites/dog-shelter-single-page/site/theme.md +25 -0
  49. package/examples/feature-demos/media-and-surfaces/.astro/collections/site.schema.json +59 -0
  50. package/examples/feature-demos/media-and-surfaces/.astro/collections/sitewide.schema.json +138 -0
  51. package/examples/feature-demos/media-and-surfaces/.astro/collections/theme.schema.json +755 -0
  52. package/examples/feature-demos/media-and-surfaces/.astro/content-modules.mjs +1 -0
  53. package/examples/feature-demos/media-and-surfaces/.astro/content.d.ts +183 -0
  54. package/examples/feature-demos/media-and-surfaces/.astro/data-store.json +1 -0
  55. package/examples/feature-demos/media-and-surfaces/.astro/dev.log +11 -0
  56. package/examples/feature-demos/media-and-surfaces/.astro/settings.json +5 -0
  57. package/examples/feature-demos/media-and-surfaces/.astro/types.d.ts +2 -0
  58. package/examples/feature-demos/media-and-surfaces/README.md +15 -0
  59. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/_metadata.json +74 -0
  60. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro-B3JHJyiv.js +434 -0
  61. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro-B3JHJyiv.js.map +1 -0
  62. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
  63. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  64. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
  65. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  66. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
  67. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  68. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
  69. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  70. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/audit-C3SVr7dT.js +1534 -0
  71. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/audit-C3SVr7dT.js.map +1 -0
  72. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/highlight-BLtvqtzO.js +48 -0
  73. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +1 -0
  74. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/icons-C92i61-2.js +42 -0
  75. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/icons-C92i61-2.js.map +1 -0
  76. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/settings-v5GgKks_.js +190 -0
  77. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/settings-v5GgKks_.js.map +1 -0
  78. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/toolbar-BFvJVp26.js +458 -0
  79. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +1 -0
  80. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/ui-library-Cd35oAvo.js +1016 -0
  81. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +1 -0
  82. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/window-vo3A6TMn.js +32 -0
  83. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/window-vo3A6TMn.js.map +1 -0
  84. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/xray-DwhzGckb.js +111 -0
  85. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/xray-DwhzGckb.js.map +1 -0
  86. package/examples/feature-demos/media-and-surfaces/site/.norna/generated-images.json +66 -0
  87. package/examples/feature-demos/media-and-surfaces/site/config.mjs +30 -0
  88. package/examples/feature-demos/media-and-surfaces/site/content.md +64 -0
  89. package/examples/feature-demos/media-and-surfaces/site/public/robots.txt +2 -0
  90. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/cards/card-carousel.svg +11 -0
  91. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/cards/card-stack.svg +9 -0
  92. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/cards/card-surfaces.svg +8 -0
  93. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/carousel/carousel-one.svg +8 -0
  94. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/carousel/carousel-three.svg +13 -0
  95. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/carousel/carousel-two.svg +8 -0
  96. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/stack/stack-one.svg +8 -0
  97. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/stack/stack-two.svg +9 -0
  98. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/route-content.md +85 -0
  99. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/theme.md +3 -0
  100. package/examples/feature-demos/media-and-surfaces/site/routes/020-surfaces/route-content.md +44 -0
  101. package/examples/feature-demos/media-and-surfaces/site/routes/020-surfaces/theme.md +8 -0
  102. package/examples/feature-demos/media-and-surfaces/site/sitewide-content.md +6 -0
  103. package/examples/feature-demos/media-and-surfaces/site/theme.md +3 -0
  104. package/examples/{typography-presets/.astro/collections/theme.schema.json → feature-demos/theme-presets/.astro/collections/routeTheme.schema.json} +143 -65
  105. package/examples/feature-demos/theme-presets/.astro/collections/site.schema.json +59 -0
  106. package/examples/feature-demos/theme-presets/.astro/collections/sitewide.schema.json +138 -0
  107. package/examples/feature-demos/theme-presets/.astro/collections/theme.schema.json +755 -0
  108. package/examples/feature-demos/theme-presets/.astro/content-assets.mjs +4 -0
  109. package/examples/feature-demos/theme-presets/.astro/content-modules.mjs +1 -0
  110. package/examples/feature-demos/theme-presets/.astro/content.d.ts +183 -0
  111. package/examples/feature-demos/theme-presets/.astro/data-store.json +1 -0
  112. package/examples/feature-demos/theme-presets/.astro/dev-local.json +8 -0
  113. package/examples/feature-demos/theme-presets/.astro/dev.json +15 -0
  114. package/examples/feature-demos/theme-presets/.astro/dev.log +127 -0
  115. package/examples/feature-demos/theme-presets/.astro/types.d.ts +2 -0
  116. package/examples/feature-demos/theme-presets/README.md +14 -0
  117. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/embla-carousel.js +1389 -0
  118. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/embla-carousel.js.map +1 -0
  119. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/package.json +3 -0
  120. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
  121. package/examples/feature-demos/theme-presets/site/.norna/generated-images.json +290 -0
  122. package/examples/{typography-presets → feature-demos/theme-presets}/site/config.mjs +4 -16
  123. package/examples/feature-demos/theme-presets/site/content.md +17 -0
  124. package/examples/feature-demos/theme-presets/site/routes/010-portfolio/route-content.md +34 -0
  125. package/examples/feature-demos/theme-presets/site/routes/010-portfolio/theme.md +3 -0
  126. package/examples/feature-demos/theme-presets/site/routes/020-documentation/route-content.md +33 -0
  127. package/examples/feature-demos/theme-presets/site/routes/020-documentation/theme.md +3 -0
  128. package/examples/feature-demos/theme-presets/site/routes/030-project/route-content.md +34 -0
  129. package/examples/feature-demos/theme-presets/site/routes/030-project/theme.md +3 -0
  130. package/examples/feature-demos/theme-presets/site/routes/040-statement/route-content.md +34 -0
  131. package/examples/feature-demos/theme-presets/site/routes/040-statement/theme.md +3 -0
  132. package/examples/feature-demos/theme-presets/site/sitewide-content.md +14 -0
  133. package/examples/feature-demos/theme-presets/site/theme.md +3 -0
  134. package/fixtures/basic/site/config.mjs +0 -13
  135. package/fixtures/basic/site/sitewide-content.md +12 -0
  136. package/fixtures/basic/site/theme.md +2 -2
  137. package/fixtures/content-model-v2/site/content.md +2 -5
  138. package/fixtures/content-model-v2/site/theme.md +1 -3
  139. package/package.json +9 -4
  140. package/scripts/build-pages.mjs +71 -0
  141. package/scripts/check-config.mjs +23 -1
  142. package/scripts/export-theme-preset.mjs +31 -0
  143. package/scripts/init-site.mjs +1 -0
  144. package/scripts/lib/example-sites.mjs +34 -0
  145. package/scripts/lib/frontmatter-yaml.mjs +28 -0
  146. package/scripts/lib/logo-assets.mjs +35 -0
  147. package/scripts/lib/norna-markdown-blocks.mjs +166 -0
  148. package/scripts/lib/presentation.mjs +106 -47
  149. package/scripts/lib/project-config.mjs +68 -105
  150. package/scripts/lib/site-content.mjs +26 -118
  151. package/scripts/lib/site-paths.mjs +2 -0
  152. package/scripts/lib/sitewide-config.mjs +34 -0
  153. package/scripts/lib/theme-config.mjs +75 -8
  154. package/scripts/lib/theme-presets.mjs +209 -0
  155. package/scripts/lib/typography.mjs +20 -26
  156. package/scripts/show-typography.mjs +43 -149
  157. package/scripts/sync-content-sections.mjs +19 -17
  158. package/scripts/test-content-check.mjs +59 -27
  159. package/scripts/test-content-model-v2.mjs +154 -0
  160. package/scripts/test-engine-commands.mjs +4 -1
  161. package/scripts/test-examples.mjs +15 -0
  162. package/scripts/test-navigation.mjs +73 -45
  163. package/scripts/test-package-check.mjs +66 -24
  164. package/scripts/test-temporary-visibility.mjs +19 -2
  165. package/scripts/test-theme-presets.mjs +151 -0
  166. package/src/components/CardList.astro +5 -2
  167. package/src/components/GalleryGrid.astro +37 -16
  168. package/src/components/SiteBanners.astro +100 -0
  169. package/src/components/SiteNavigation.astro +48 -31
  170. package/src/components/SitePage.astro +55 -13
  171. package/src/components/SiteSection.astro +193 -35
  172. package/src/content.config.ts +80 -48
  173. package/src/layouts/BaseLayout.astro +64 -14
  174. package/src/lib/routeThemes.ts +61 -0
  175. package/src/lib/sectionContent.ts +235 -51
  176. package/src/lib/sitePublicAssets.ts +1 -0
  177. package/src/styles/global.css +284 -93
  178. package/starters/basic/.github/workflows/deploy.yml +1 -1
  179. package/starters/basic/README.md +7 -6
  180. package/starters/basic/package.json +1 -0
  181. package/starters/basic/site/config.mjs +0 -13
  182. package/starters/basic/site/content.md +1 -1
  183. package/starters/basic/site/sitewide-content.md +14 -0
  184. package/starters/basic/site/theme.md +13 -24
  185. package/starters/project/.github/workflows/deploy.yml +1 -1
  186. package/starters/project/README.md +10 -8
  187. package/starters/project/package.json +1 -0
  188. package/starters/project/site/config.mjs +0 -13
  189. package/starters/project/site/sitewide-content.md +14 -0
  190. package/starters/project/site/theme.md +3 -11
  191. package/examples/dog-gallery/site/.norna/generated-images.json +0 -226
  192. package/examples/dog-gallery/site/config.mjs +0 -67
  193. package/examples/dog-gallery/site/content.md +0 -128
  194. package/examples/dog-gallery/site/images/black-dogs/black-puppy-meadow.png +0 -0
  195. package/examples/dog-gallery/site/images/black-dogs/photo-of-a-black-dog.jpg +0 -0
  196. package/examples/dog-gallery/site/images/brown-dogs/brown-dog.jpg +0 -0
  197. package/examples/dog-gallery/site/images/brown-dogs/dog-accompanies-master.jpg +0 -0
  198. package/examples/dog-gallery/site/images/golden-dogs/golden-retriever.jpg +0 -0
  199. package/examples/dog-gallery/site/images/golden-dogs/toller-puppy.jpg +0 -0
  200. package/examples/dog-gallery/site/images/white-dogs/white-cute-dog.jpg +0 -0
  201. package/examples/dog-gallery/site/images/white-dogs/white-puppy-garden.png +0 -0
  202. package/examples/dog-gallery/site/public/favicon.svg +0 -7
  203. package/examples/dog-gallery/site/routes/010-dog-care/route-content.md +0 -38
  204. package/examples/dog-gallery/site/theme.md +0 -112
  205. package/examples/typography-presets/.astro/collections/site.schema.json +0 -776
  206. package/examples/typography-presets/.astro/data-store.json +0 -1
  207. package/examples/typography-presets/.astro/dev.log +0 -24
  208. package/examples/typography-presets/site/.norna/generated-images.json +0 -290
  209. package/examples/typography-presets/site/content.md +0 -134
  210. package/examples/typography-presets/site/theme.md +0 -43
  211. /package/examples/{typography-presets/.astro/content-modules.mjs → complete-sites/dog-shelter-multi-page/.astro/content-assets.mjs} +0 -0
  212. /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/types.d.ts +0 -0
  213. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -0
  214. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -0
  215. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_aria-query.js +0 -0
  216. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -0
  217. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_axobject-query.js +0 -0
  218. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -0
  219. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_html-escaper.js +0 -0
  220. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -0
  221. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -0
  222. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -0
  223. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -0
  224. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -0
  225. /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/embla-carousel.js +0 -0
  226. /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/embla-carousel.js.map +0 -0
  227. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -0
  228. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -0
  229. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/icons-RyebKi45.js +0 -0
  230. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -0
  231. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/package.json +0 -0
  232. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -0
  233. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -0
  234. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -0
  235. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/toolbar-63PA619H.js +0 -0
  236. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -0
  237. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -0
  238. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -0
  239. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/window-jPkpKVcy.js +0 -0
  240. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -0
  241. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -0
  242. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -0
  243. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/site/public/robots.txt +0 -0
  244. /package/examples/{typography-presets → feature-demos/media-and-surfaces}/.astro/content-assets.mjs +0 -0
  245. /package/examples/{typography-presets → feature-demos/media-and-surfaces}/node_modules/.vite/deps/package.json +0 -0
  246. /package/examples/{typography-presets → feature-demos/media-and-surfaces}/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -0
  247. /package/examples/{typography-presets → feature-demos/theme-presets}/.astro/settings.json +0 -0
  248. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/_metadata.json +0 -0
  249. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -0
  250. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -0
  251. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_aria-query.js +0 -0
  252. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -0
  253. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_axobject-query.js +0 -0
  254. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -0
  255. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_html-escaper.js +0 -0
  256. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -0
  257. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -0
  258. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -0
  259. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -0
  260. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -0
  261. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -0
  262. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -0
  263. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/icons-RyebKi45.js +0 -0
  264. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -0
  265. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -0
  266. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -0
  267. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/toolbar-63PA619H.js +0 -0
  268. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -0
  269. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -0
  270. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -0
  271. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/window-jPkpKVcy.js +0 -0
  272. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -0
  273. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -0
  274. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -0
  275. /package/examples/{typography-presets → feature-demos/theme-presets}/site/public/robots.txt +0 -0
  276. /package/examples/{typography-presets/site/images/quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio}/puppy-portrait.jpg +0 -0
  277. /package/examples/{typography-presets/site/images/quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio}/shepherd-portrait.jpg +0 -0
  278. /package/examples/{typography-presets/site/images/quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio}/white-puppy.jpg +0 -0
  279. /package/examples/{typography-presets/site/images/compact-gallery → feature-demos/theme-presets/site/routes/020-documentation/images/documentation}/black-schnauzer.jpg +0 -0
  280. /package/examples/{typography-presets/site/images/compact-gallery → feature-demos/theme-presets/site/routes/020-documentation/images/documentation}/retriever-portrait.jpg +0 -0
  281. /package/examples/{typography-presets/site/images/compact-gallery → feature-demos/theme-presets/site/routes/020-documentation/images/documentation}/schnauzer-portrait.jpg +0 -0
  282. /package/examples/{typography-presets/site/images/text-forward → feature-demos/theme-presets/site/routes/030-project/images/project}/boxer-portrait.jpg +0 -0
  283. /package/examples/{typography-presets/site/images/text-forward → feature-demos/theme-presets/site/routes/030-project/images/project}/grass-puppy.jpg +0 -0
  284. /package/examples/{typography-presets/site/images/text-forward → feature-demos/theme-presets/site/routes/030-project/images/project}/grey-street-dog.jpg +0 -0
  285. /package/examples/{typography-presets/site → feature-demos/theme-presets/site/routes/040-statement}/images/statement/black-pedigree.jpg +0 -0
  286. /package/examples/{typography-presets/site → feature-demos/theme-presets/site/routes/040-statement}/images/statement/sheepdog-grass.jpg +0 -0
  287. /package/examples/{typography-presets/site → feature-demos/theme-presets/site/routes/040-statement}/images/statement/sheepdog-wide.jpg +0 -0
@@ -0,0 +1,61 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { parseYamlMapping } from '../../scripts/lib/frontmatter-yaml.mjs';
4
+ import {
5
+ splitSiteFile,
6
+ validateFrontmatterIndentation,
7
+ validateRouteThemeFrontmatterStructure,
8
+ } from '../../scripts/lib/site-content.mjs';
9
+ import { siteRoutesDir, siteRoutesLabel, sitewideContentLabel } from '../../scripts/lib/site-paths.mjs';
10
+ import { themeVisualSchema } from '../content.config';
11
+
12
+ type RouteTheme = {
13
+ id: string;
14
+ data: Record<string, unknown>;
15
+ };
16
+
17
+ export const getRouteTheme = async (routeDirectory: string | null): Promise<RouteTheme | null> => {
18
+ if (!routeDirectory) return null;
19
+
20
+ const segments = routeDirectory.split(/[\\/]/).filter(Boolean);
21
+ for (let length = segments.length; length > 0; length -= 1) {
22
+ const themeSegments = segments.slice(0, length);
23
+ const themePath = path.join(siteRoutesDir, ...themeSegments, 'theme.md');
24
+ const themeLabel = `${siteRoutesLabel}/${themeSegments.join('/')}/theme.md`;
25
+ const source = await readFile(themePath, 'utf8').catch((error) => {
26
+ if (error?.code === 'ENOENT') return null;
27
+ throw error;
28
+ });
29
+ if (!source) continue;
30
+
31
+ const { frontmatter, frontmatterBody } = splitSiteFile(source, themeLabel);
32
+ const issues: Array<{ message: string }> = [];
33
+ validateFrontmatterIndentation(frontmatter, (issue) => issues.push(issue));
34
+ validateRouteThemeFrontmatterStructure(frontmatter, (issue) => issues.push(issue));
35
+ if (issues.length > 0) {
36
+ throw new Error([
37
+ `${themeLabel} has invalid frontmatter.`,
38
+ ...issues.map((issue) => `- ${issue.message}`),
39
+ ].join('\n'));
40
+ }
41
+
42
+ const data = parseYamlMapping(frontmatterBody);
43
+ if (Object.hasOwn(data, 'navigation')) {
44
+ throw new Error(`${themeLabel} may not define navigation. Brand and logo belong in ${sitewideContentLabel}.`);
45
+ }
46
+ const parsed = themeVisualSchema.safeParse(data);
47
+ if (!parsed.success) {
48
+ throw new Error([
49
+ `${themeLabel} has invalid values.`,
50
+ ...parsed.error.issues.map((issue) => `- ${issue.path.join('.') || '(root)'}: ${issue.message}`),
51
+ ].join('\n'));
52
+ }
53
+
54
+ return {
55
+ id: `routes/${themeSegments.join('/')}/theme`,
56
+ data: parsed.data,
57
+ };
58
+ }
59
+
60
+ return null;
61
+ };
@@ -1,6 +1,8 @@
1
1
  import type { CollectionEntry } from 'astro:content';
2
+ import { markdownToHtml } from 'satteri';
2
3
  import projectConfig from '../../scripts/lib/project-config.mjs';
3
4
  import {
5
+ extractInlineNoteDiagnostics,
4
6
  extractNornaMarkdownBlocks,
5
7
  splitNornaMarkdownBlockMarkers,
6
8
  splitNornaRenderedCodeBlocks,
@@ -10,8 +12,6 @@ import type { SitePage } from './sitePages';
10
12
 
11
13
  type SiteSectionMetadataMap = CollectionEntry<'site'>['data']['sections'];
12
14
  type SiteSectionMetadata = SiteSectionMetadataMap[string];
13
- type ThemePresentation = CollectionEntry<'theme'>['data']['presentation'];
14
- type InlineStyles = NonNullable<NonNullable<ThemePresentation>['inlineStyles']>;
15
15
  type GalleryImage = {
16
16
  image: string;
17
17
  src: string;
@@ -30,11 +30,18 @@ type CardListLayout = 'image-top' | 'image-left' | 'image-right';
30
30
  type CardListFlow = 'grid' | 'stack';
31
31
  type CardListSize = 's' | 'm' | 'l' | 'xl';
32
32
  type CardListWidth = 'text' | 'narrow' | 'normal' | 'wide';
33
+ type InlineNote = {
34
+ markdown: string;
35
+ number: number;
36
+ id: string;
37
+ referenceId: string;
38
+ };
33
39
  type SectionContentBlock =
34
40
  | { type: 'html'; html: string }
35
41
  | { type: 'image-stack'; images: GalleryImage[] }
36
42
  | { type: 'image-carousel'; images: GalleryImage[] }
37
- | { type: 'card-list'; layout: CardListLayout; flow: CardListFlow; size: CardListSize; width: CardListWidth; cards: CardListItem[] };
43
+ | { type: 'card-list'; layout: CardListLayout; flow: CardListFlow; size: CardListSize; width: CardListWidth; cards: CardListItem[] }
44
+ | { type: 'note'; html: string; number?: number; id?: string; referenceId?: string };
38
45
  export type ResolvedSection = SiteSectionMetadata & {
39
46
  id: string;
40
47
  title: string;
@@ -42,9 +49,13 @@ export type ResolvedSection = SiteSectionMetadata & {
42
49
  contentBlocks: SectionContentBlock[];
43
50
  };
44
51
 
52
+ export type SectionNavigation = {
53
+ id: string;
54
+ title: string;
55
+ };
56
+
45
57
  const headingRegex = /<h2\b([^>]*)>([\s\S]*?)<\/h2>/gi;
46
58
  const explicitHeadingIdRegex = /\s*\{#([a-z0-9-]+)\}\s*$/;
47
- const inlineStyleReferenceRegex = /\[([\s\S]*?)\]\{\.([a-z][a-z0-9-]*)\}/g;
48
59
  const markdownH2Regex = /^##\s+.*$/gm;
49
60
  const imageProvenanceCommentRegex = /<!--\s*norna-image-provenance:[\s\S]*?-->/gi;
50
61
 
@@ -82,34 +93,144 @@ const getHeadingId = (attributes: string, headingHtml: string) => {
82
93
  const getExplicitHeadingId = (headingHtml: string) =>
83
94
  decodeHtmlEntities(stripTags(headingHtml)).trim().match(explicitHeadingIdRegex)?.[1];
84
95
 
85
- const stripInlineStyleMarkers = (value: string) =>
86
- value.replace(inlineStyleReferenceRegex, '$1');
87
-
88
96
  const getHeadingTitle = (headingHtml: string) =>
89
- stripInlineStyleMarkers(decodeHtmlEntities(stripTags(headingHtml)).replace(explicitHeadingIdRegex, '').trim());
97
+ decodeHtmlEntities(stripTags(headingHtml)).replace(explicitHeadingIdRegex, '').trim();
90
98
 
91
- const getHeadingTitleHtml = (headingHtml: string, inlineStyles: InlineStyles | undefined) =>
92
- prepareContentHtml(headingHtml.replace(explicitHeadingIdRegex, '').trim(), inlineStyles);
99
+ const getHeadingTitleHtml = (headingHtml: string) =>
100
+ prepareContentHtml(headingHtml.replace(explicitHeadingIdRegex, '').trim());
93
101
 
94
- const applyInlineStyles = (html: string, inlineStyles: InlineStyles | undefined) =>
95
- html.replace(inlineStyleReferenceRegex, (_match, text: string, styleName: string) => {
96
- const style = inlineStyles?.[styleName];
102
+ export const getSectionNavigation = (html: string, sectionMetadata: SiteSectionMetadataMap): SectionNavigation[] => {
103
+ const matches = Array.from(html.matchAll(headingRegex));
104
+ const sectionIds = new Set<string>();
105
+ const sections = matches.map((match) => {
106
+ const attributes = match[1] ?? '';
107
+ const headingHtml = match[2] ?? '';
108
+ const explicitId = getExplicitHeadingId(headingHtml);
109
+ const id = getHeadingId(attributes, headingHtml);
110
+ const title = getHeadingTitle(headingHtml);
111
+
112
+ if (!explicitId) {
113
+ throw new Error(`Section heading "${title}" is missing an explicit id. Write it as: ## ${title} {#${id}}`);
114
+ }
97
115
 
98
- if (!style) {
99
- throw new Error(`Markdown uses inline style ".${styleName}", but theme.md presentation.inlineStyles.${styleName} is not defined.`);
116
+ if (sectionIds.has(id)) {
117
+ throw new Error(`Duplicate Markdown section heading id: ${id}`);
100
118
  }
101
119
 
102
- return `<span class="inline-style inline-style-${styleName}" style="--inline-style-color: ${style.color}">${text}</span>`;
120
+ sectionIds.add(id);
121
+ return { id, title };
103
122
  });
104
123
 
124
+ for (const id of Object.keys(sectionMetadata)) {
125
+ if (!sectionIds.has(id)) {
126
+ throw new Error(
127
+ `Section metadata "${id}" does not match any Markdown section. Add "## Heading {#${id}}" or remove sections.${id}.`,
128
+ );
129
+ }
130
+ }
131
+
132
+ return sections;
133
+ };
134
+
105
135
  const stripImageProvenanceComments = (html: string) => html.replace(imageProvenanceCommentRegex, '');
106
136
 
107
- const prepareContentHtml = (html: string, inlineStyles: InlineStyles | undefined) =>
137
+ const prepareContentHtml = (html: string) =>
108
138
  applyBasePathToHtml(
109
139
  projectConfig.site.basePath,
110
- stripImageProvenanceComments(applyInlineStyles(html, inlineStyles)),
140
+ stripImageProvenanceComments(html),
111
141
  );
112
142
 
143
+ const htmlVoidElements = new Set([
144
+ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link',
145
+ 'meta', 'param', 'source', 'track', 'wbr',
146
+ ]);
147
+
148
+ // Keep notes aligned with the Markdown block immediately before them instead of
149
+ // aligning them with a whole run of paragraphs rendered as one HTML fragment.
150
+ const splitHtmlIntoBlocks = (html: string) => {
151
+ const tagRegex = /<!--[\s\S]*?-->|<\/?([a-z][\w:-]*)(?:\s[^<>]*?)?\/?>/gi;
152
+ const blocks: string[] = [];
153
+ let blockStart = -1;
154
+ let depth = 0;
155
+ let cursor = 0;
156
+
157
+ for (const match of html.matchAll(tagRegex)) {
158
+ const start = match.index ?? 0;
159
+ const token = match[0] ?? '';
160
+ const tagName = match[1]?.toLowerCase();
161
+ if (!tagName) continue;
162
+
163
+ const isClosing = /^<\//.test(token);
164
+ const isSelfClosing = /\/\s*>$/.test(token) || htmlVoidElements.has(tagName);
165
+
166
+ if (isClosing) {
167
+ if (depth > 0) depth -= 1;
168
+ if (depth === 0 && blockStart >= 0) {
169
+ blocks.push(html.slice(blockStart, start + token.length));
170
+ cursor = start + token.length;
171
+ blockStart = -1;
172
+ }
173
+ continue;
174
+ }
175
+
176
+ if (depth === 0) {
177
+ if (blockStart < 0) blockStart = start;
178
+ if (isSelfClosing) {
179
+ blocks.push(html.slice(blockStart, start + token.length));
180
+ cursor = start + token.length;
181
+ blockStart = -1;
182
+ }
183
+ }
184
+
185
+ if (!isSelfClosing) depth += 1;
186
+ }
187
+
188
+ if (blockStart >= 0) {
189
+ blocks.push(html.slice(blockStart).trim());
190
+ cursor = html.length;
191
+ }
192
+
193
+ const prefix = html.slice(0, blocks.length > 0 ? html.indexOf(blocks[0]!) : html.length).trim();
194
+ const suffix = html.slice(cursor).trim();
195
+ return [prefix, ...blocks, suffix].filter(Boolean);
196
+ };
197
+
198
+ const inlineNoteMarkerRegex = /^\s*<norna-inline-note\s+data-note-index="(\d+)"\s*><\/norna-inline-note>\s*$/i;
199
+ const noteDeclarationParagraphRegex = /<p>\s*\{note:\s*[\s\S]*?\}\s*<\/p>/gi;
200
+ const htmlCodeRegionRegex = /<pre\b[\s\S]*?<\/pre>|<code\b[\s\S]*?<\/code>/gi;
201
+
202
+ const replaceHtmlOutsideCode = (html: string, transform: (value: string) => string) => {
203
+ let result = '';
204
+ let cursor = 0;
205
+
206
+ for (const match of html.matchAll(htmlCodeRegionRegex)) {
207
+ const start = match.index ?? 0;
208
+ result += transform(html.slice(cursor, start));
209
+ result += match[0];
210
+ cursor = start + match[0].length;
211
+ }
212
+
213
+ return result + transform(html.slice(cursor));
214
+ };
215
+
216
+ const applyInlineNoteMarkup = (html: string, notes: InlineNote[]) => {
217
+ let referenceIndex = 0;
218
+ const withReferences = replaceHtmlOutsideCode(html, (value) => value.replace(/\{note-ref\}/g, () => {
219
+ const note = notes[referenceIndex];
220
+ if (!note) return '{note-ref}';
221
+
222
+ referenceIndex += 1;
223
+ return `\u2060<sup class="section-note-ref"><a id="${note.referenceId}" href="#${note.id}" aria-label="Note ${note.number}" aria-describedby="${note.id}" data-note-id="${note.id}">${note.number}</a></sup>`;
224
+ }));
225
+
226
+ let noteIndex = 0;
227
+ return withReferences.replace(noteDeclarationParagraphRegex, () => {
228
+ const marker = `<norna-inline-note data-note-index="${noteIndex}"></norna-inline-note>`;
229
+ noteIndex += 1;
230
+ return marker;
231
+ });
232
+ };
233
+
113
234
  const getRawMarkdownSections = (markdown: string) => {
114
235
  const matches = Array.from(markdown.matchAll(markdownH2Regex));
115
236
  const sections = new Map<string, string>();
@@ -135,61 +256,103 @@ const getImageSourceKey = (page: SitePage, sectionId: string, image: string) =>
135
256
  : `images/${sectionId}/${image}`
136
257
  );
137
258
 
138
- const resolveContentBlocks = (
259
+ const renderInlineNoteMarkdown = async (markdown: string) => {
260
+ const result = await markdownToHtml(markdown, {
261
+ features: {
262
+ gfm: true,
263
+ smartPunctuation: true,
264
+ },
265
+ });
266
+
267
+ if (/<img\b/i.test(result.html)) {
268
+ throw new Error('Inline notes cannot contain images. Use a Norna image block with a caption instead.');
269
+ }
270
+
271
+ return prepareContentHtml(result.html);
272
+ };
273
+
274
+ const resolveContentBlocks = async (
139
275
  html: string,
140
276
  rawMarkdown: string,
141
277
  page: SitePage,
142
278
  sectionId: string,
143
- inlineStyles: InlineStyles | undefined,
279
+ inlineNotes: InlineNote[] = [],
144
280
  ) => {
145
281
  const rawBlocks = extractNornaMarkdownBlocks(rawMarkdown);
282
+ const renderedHtml = applyInlineNoteMarkup(html, inlineNotes);
146
283
  const splitBlocks = rawBlocks.length > 0
147
- ? splitNornaRenderedCodeBlocks(html, rawBlocks)
148
- : splitNornaMarkdownBlockMarkers(html);
284
+ ? splitNornaRenderedCodeBlocks(renderedHtml, rawBlocks)
285
+ : splitNornaMarkdownBlockMarkers(renderedHtml);
286
+ const hasNotes = inlineNotes.length > 0;
287
+ const contentBlocks = hasNotes
288
+ ? splitBlocks.flatMap((block) => block.type === 'html'
289
+ ? splitHtmlIntoBlocks(block.html).map((htmlBlock) => ({ type: 'html' as const, html: htmlBlock }))
290
+ : [block])
291
+ : splitBlocks;
292
+ const resolvedBlocks: SectionContentBlock[] = [];
149
293
 
150
- return splitBlocks.map((block): SectionContentBlock => {
294
+ for (const block of contentBlocks) {
151
295
  if (block.type === 'html') {
152
- return {
153
- type: 'html',
154
- html: prepareContentHtml(block.html, inlineStyles),
155
- };
296
+ const inlineNoteMarker = block.html.match(inlineNoteMarkerRegex);
297
+ if (inlineNoteMarker) {
298
+ const note = inlineNotes[Number(inlineNoteMarker[1])];
299
+ if (!note) {
300
+ throw new Error('Norna inline note markup could not be matched to its note text.');
301
+ }
302
+
303
+ resolvedBlocks.push({
304
+ type: 'note',
305
+ html: await renderInlineNoteMarkdown(note.markdown),
306
+ number: note.number,
307
+ id: note.id,
308
+ referenceId: note.referenceId,
309
+ });
310
+ continue;
311
+ }
312
+
313
+ resolvedBlocks.push({ type: 'html', html: prepareContentHtml(block.html) });
314
+ continue;
315
+ }
316
+
317
+ if (block.type === 'card-list') {
318
+ resolvedBlocks.push({
319
+ type: 'card-list',
320
+ layout: block.layout,
321
+ flow: block.flow,
322
+ size: block.size,
323
+ width: block.width,
324
+ cards: block.cards.map((card: CardListItem) => ({
325
+ ...card,
326
+ ...(card.image ? { src: getImageSourceKey(page, sectionId, card.image) } : {}),
327
+ })),
328
+ });
329
+ continue;
156
330
  }
157
331
 
158
- return {
332
+ resolvedBlocks.push({
159
333
  type: block.type,
160
- ...(block.type === 'card-list'
161
- ? {
162
- layout: block.layout,
163
- flow: block.flow,
164
- size: block.size,
165
- width: block.width,
166
- cards: block.cards.map((card: CardListItem) => ({
167
- ...card,
168
- ...(card.image ? { src: getImageSourceKey(page, sectionId, card.image) } : {}),
169
- })),
170
- }
171
- : {
172
- images: block.images.map((image: { image: string; alt?: string; caption?: string }) => ({
173
- ...image,
174
- src: getImageSourceKey(page, sectionId, image.image),
175
- })),
176
- }),
177
- };
178
- });
334
+ images: block.images.map((image: { image: string; alt?: string; caption?: string }) => ({
335
+ ...image,
336
+ src: getImageSourceKey(page, sectionId, image.image),
337
+ })),
338
+ });
339
+ }
340
+
341
+ return resolvedBlocks;
179
342
  };
180
343
 
181
- export const getSectionsContent = (
344
+ export const getSectionsContent = async (
182
345
  html: string,
183
346
  rawMarkdown: string,
184
347
  sectionMetadata: SiteSectionMetadataMap,
185
348
  page: SitePage,
186
- inlineStyles?: InlineStyles,
187
349
  ) => {
188
350
  const matches = Array.from(html.matchAll(headingRegex));
189
351
  const rawSections = getRawMarkdownSections(rawMarkdown);
190
352
  const metadataIds = new Set(Object.keys(sectionMetadata));
191
353
  const sections: ResolvedSection[] = [];
192
354
  const sectionIds = new Set<string>();
355
+ let nextNoteNumber = 1;
193
356
 
194
357
  for (let index = 0; index < matches.length; index += 1) {
195
358
  const match = matches[index];
@@ -212,12 +375,33 @@ export const getSectionsContent = (
212
375
  }
213
376
 
214
377
  sectionIds.add(id);
378
+ const rawSection = rawSections.get(id) ?? '';
379
+ const inlineNoteDiagnostics = extractInlineNoteDiagnostics(rawSection, {
380
+ label: 'Markdown section',
381
+ });
382
+ if (inlineNoteDiagnostics.errors.length > 0) {
383
+ throw new Error(inlineNoteDiagnostics.errors[0].message);
384
+ }
385
+
386
+ const inlineNotes = inlineNoteDiagnostics.notes.map((note) => {
387
+ const number = nextNoteNumber;
388
+ nextNoteNumber += 1;
389
+ const pageKey = page.routeId || 'home';
390
+ const noteKey = `${pageKey}-${id}-${number}`;
391
+ return {
392
+ ...note,
393
+ number,
394
+ id: `note-${noteKey}`,
395
+ referenceId: `note-ref-${noteKey}`,
396
+ };
397
+ });
398
+
215
399
  sections.push({
216
400
  ...(sectionMetadata[id] ?? {}),
217
401
  id,
218
402
  title,
219
- titleHtml: getHeadingTitleHtml(headingHtml, inlineStyles),
220
- contentBlocks: resolveContentBlocks(content, rawSections.get(id) ?? '', page, id, inlineStyles),
403
+ titleHtml: getHeadingTitleHtml(headingHtml),
404
+ contentBlocks: await resolveContentBlocks(content, rawSection, page, id, inlineNotes),
221
405
  });
222
406
  }
223
407
 
@@ -2,6 +2,7 @@ import { existsSync } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { sitePublicDir } from '../../scripts/lib/site-paths.mjs';
4
4
  import projectConfig from '../../scripts/lib/project-config.mjs';
5
+ export { getLogoAsset } from '../../scripts/lib/logo-assets.mjs';
5
6
  import { withBasePath } from './basePath';
6
7
 
7
8
  type IconLink = {