@janga/norna 0.7.10 → 0.7.12

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 (476) hide show
  1. package/README.md +120 -106
  2. package/bin/norna-cli.mjs +4 -1
  3. package/docs/README.md +107 -74
  4. package/docs/assets/norna-dog-shelter.jpg +0 -0
  5. package/docs/commands.md +13 -4
  6. package/docs/configuration.md +86 -184
  7. package/docs/content.md +14 -14
  8. package/docs/design/command-organization.md +1 -1
  9. package/docs/design/norna-diagram-design.md +2 -1
  10. package/docs/design/site-examples-structure.md +38 -24
  11. package/docs/engine-development.md +28 -15
  12. package/docs/getting-started.md +141 -124
  13. package/docs/how-to/embedded-site.md +35 -0
  14. package/docs/publishing.md +47 -46
  15. package/docs/requirements.md +54 -0
  16. package/docs/routes.md +3 -2
  17. package/docs/site-structure.md +12 -9
  18. package/docs/sitewide-content.md +2 -1
  19. package/docs/theme.md +104 -34
  20. package/docs/typography.md +32 -17
  21. package/examples/README.md +43 -0
  22. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/collections/sitewide.schema.json +24 -0
  23. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/collections/theme.schema.json +9 -24
  24. package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/.astro/content.d.ts +9 -1
  25. package/examples/complete-sites/dog-shelter-multi-page/.astro/data-store.json +1 -0
  26. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/dev-local.json +2 -2
  27. package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.json +13 -0
  28. package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.log +26 -0
  29. package/examples/complete-sites/dog-shelter-multi-page/.astro/settings.json +5 -0
  30. package/examples/complete-sites/dog-shelter-multi-page/README.md +16 -0
  31. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/_metadata.json +74 -0
  32. package/examples/{typography-presets/node_modules/.vite/deps/astro-D_GIpJIE.js → complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro-B3JHJyiv.js} +4 -4
  33. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro-B3JHJyiv.js.map +1 -0
  34. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_aria-query.js +151 -151
  35. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  36. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_axobject-query.js +131 -131
  37. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  38. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_html-escaper.js +1 -1
  39. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  40. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +10 -10
  41. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  42. package/examples/{typography-presets/node_modules/.vite/deps/audit-DM9Hkl5B.js → complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/audit-C3SVr7dT.js} +10 -10
  43. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/audit-C3SVr7dT.js.map +1 -0
  44. package/examples/{typography-presets/node_modules/.vite/deps/highlight-DnZ1ULOL.js → complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/highlight-BLtvqtzO.js} +1 -1
  45. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +1 -0
  46. package/examples/{typography-presets/node_modules/.vite/deps/icons-RyebKi45.js → complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/icons-C92i61-2.js} +1 -1
  47. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/icons-C92i61-2.js.map +1 -0
  48. package/examples/{typography-presets/node_modules/.vite/deps/settings-DaU-gNnK.js → complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/settings-v5GgKks_.js} +2 -2
  49. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/settings-v5GgKks_.js.map +1 -0
  50. package/examples/{typography-presets/node_modules/.vite/deps/toolbar-63PA619H.js → complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/toolbar-BFvJVp26.js} +2 -2
  51. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +1 -0
  52. package/examples/{typography-presets/node_modules/.vite/deps/ui-library--bkcNSYz.js → complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/ui-library-Cd35oAvo.js} +10 -10
  53. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +1 -0
  54. package/examples/{typography-presets/node_modules/.vite/deps/window-jPkpKVcy.js → complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/window-vo3A6TMn.js} +1 -1
  55. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/window-vo3A6TMn.js.map +1 -0
  56. package/examples/{typography-presets/node_modules/.vite/deps/xray-BXCHpEJP.js → complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/xray-DwhzGckb.js} +3 -3
  57. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/xray-DwhzGckb.js.map +1 -0
  58. package/examples/complete-sites/dog-shelter-multi-page/site/.norna/generated-images.json +98 -0
  59. package/examples/complete-sites/dog-shelter-multi-page/site/config.md +3 -0
  60. package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-multi-page}/site/content.md +12 -0
  61. package/examples/complete-sites/dog-shelter-multi-page/site/public/logo.svg +9 -0
  62. package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/route-content.md +22 -0
  63. package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-multi-page}/site/routes/020-adopt/route-content.md +11 -0
  64. package/examples/complete-sites/dog-shelter-multi-page/site/sitewide-content.md +16 -0
  65. package/examples/complete-sites/dog-shelter-multi-page/site/theme.md +3 -0
  66. package/examples/complete-sites/dog-shelter-single-page/.astro/collections/site.schema.json +59 -0
  67. package/examples/complete-sites/dog-shelter-single-page/.astro/collections/sitewide.schema.json +138 -0
  68. package/examples/{dog-gallery → complete-sites/dog-shelter-single-page}/.astro/collections/theme.schema.json +151 -68
  69. package/examples/complete-sites/dog-shelter-single-page/.astro/content.d.ts +183 -0
  70. package/examples/complete-sites/dog-shelter-single-page/.astro/data-store.json +1 -0
  71. package/examples/complete-sites/dog-shelter-single-page/.astro/dev.json +13 -0
  72. package/examples/complete-sites/dog-shelter-single-page/.astro/dev.log +14 -0
  73. package/examples/complete-sites/dog-shelter-single-page/.astro/settings.json +5 -0
  74. package/examples/complete-sites/dog-shelter-single-page/README.md +16 -0
  75. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/_metadata.json +74 -0
  76. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro-B3JHJyiv.js +434 -0
  77. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro-B3JHJyiv.js.map +1 -0
  78. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
  79. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  80. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
  81. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  82. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
  83. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  84. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
  85. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  86. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/audit-C3SVr7dT.js +1534 -0
  87. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/audit-C3SVr7dT.js.map +1 -0
  88. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/highlight-BLtvqtzO.js +48 -0
  89. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +1 -0
  90. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/icons-C92i61-2.js +42 -0
  91. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/icons-C92i61-2.js.map +1 -0
  92. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/settings-v5GgKks_.js +190 -0
  93. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/settings-v5GgKks_.js.map +1 -0
  94. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/toolbar-BFvJVp26.js +458 -0
  95. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +1 -0
  96. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/ui-library-Cd35oAvo.js +1016 -0
  97. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +1 -0
  98. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/window-vo3A6TMn.js +32 -0
  99. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/window-vo3A6TMn.js.map +1 -0
  100. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/xray-DwhzGckb.js +111 -0
  101. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/xray-DwhzGckb.js.map +1 -0
  102. package/examples/complete-sites/dog-shelter-single-page/site/.norna/generated-images.json +50 -0
  103. package/examples/complete-sites/dog-shelter-single-page/site/config.md +3 -0
  104. package/examples/complete-sites/dog-shelter-single-page/site/content.md +34 -0
  105. package/examples/complete-sites/dog-shelter-single-page/site/public/logo.svg +9 -0
  106. package/examples/complete-sites/dog-shelter-single-page/site/sitewide-content.md +16 -0
  107. package/examples/complete-sites/dog-shelter-single-page/site/theme.md +3 -0
  108. package/examples/feature-demos/media-and-surfaces/.astro/collections/site.schema.json +59 -0
  109. package/examples/feature-demos/media-and-surfaces/.astro/collections/sitewide.schema.json +138 -0
  110. package/examples/feature-demos/media-and-surfaces/.astro/collections/theme.schema.json +755 -0
  111. package/examples/feature-demos/media-and-surfaces/.astro/content-assets.mjs +4 -0
  112. package/examples/feature-demos/media-and-surfaces/.astro/content-modules.mjs +1 -0
  113. package/examples/feature-demos/media-and-surfaces/.astro/content.d.ts +183 -0
  114. package/examples/feature-demos/media-and-surfaces/.astro/data-store.json +1 -0
  115. package/examples/feature-demos/media-and-surfaces/.astro/dev.log +24 -0
  116. package/examples/feature-demos/media-and-surfaces/.astro/settings.json +5 -0
  117. package/examples/feature-demos/media-and-surfaces/.astro/types.d.ts +2 -0
  118. package/examples/feature-demos/media-and-surfaces/README.md +21 -0
  119. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/_metadata.json +80 -0
  120. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro-B3JHJyiv.js +434 -0
  121. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro-B3JHJyiv.js.map +1 -0
  122. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
  123. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  124. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
  125. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  126. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
  127. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  128. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
  129. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  130. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/audit-C3SVr7dT.js +1534 -0
  131. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/audit-C3SVr7dT.js.map +1 -0
  132. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/embla-carousel.js +1389 -0
  133. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/embla-carousel.js.map +1 -0
  134. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/highlight-BLtvqtzO.js +48 -0
  135. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +1 -0
  136. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/icons-C92i61-2.js +42 -0
  137. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/icons-C92i61-2.js.map +1 -0
  138. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/package.json +3 -0
  139. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
  140. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/settings-v5GgKks_.js +190 -0
  141. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/settings-v5GgKks_.js.map +1 -0
  142. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/toolbar-BFvJVp26.js +458 -0
  143. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +1 -0
  144. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/ui-library-Cd35oAvo.js +1016 -0
  145. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +1 -0
  146. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/window-vo3A6TMn.js +32 -0
  147. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/window-vo3A6TMn.js.map +1 -0
  148. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/xray-DwhzGckb.js +111 -0
  149. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/xray-DwhzGckb.js.map +1 -0
  150. package/examples/{presentation → feature-demos/media-and-surfaces}/site/.norna/generated-images.json +16 -16
  151. package/examples/feature-demos/media-and-surfaces/site/config.md +4 -0
  152. package/examples/feature-demos/media-and-surfaces/site/content.md +18 -0
  153. package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/route-content.md +17 -24
  154. package/examples/{presentation/site/routes/030-surfaces → feature-demos/media-and-surfaces/site/routes/020-surfaces}/route-content.md +1 -1
  155. package/examples/{presentation/site/routes/030-surfaces → feature-demos/media-and-surfaces/site/routes/020-surfaces}/theme.md +1 -0
  156. package/examples/feature-demos/media-and-surfaces/site/sitewide-content.md +6 -0
  157. package/examples/feature-demos/media-and-surfaces/site/theme.md +3 -0
  158. package/examples/feature-demos/sitewide-content/.astro/collections/site.schema.json +59 -0
  159. package/examples/feature-demos/sitewide-content/.astro/collections/sitewide.schema.json +138 -0
  160. package/examples/feature-demos/sitewide-content/.astro/collections/theme.schema.json +755 -0
  161. package/examples/feature-demos/sitewide-content/.astro/content-assets.mjs +1 -0
  162. package/examples/feature-demos/sitewide-content/.astro/content-modules.mjs +1 -0
  163. package/examples/feature-demos/sitewide-content/.astro/content.d.ts +183 -0
  164. package/examples/feature-demos/sitewide-content/.astro/data-store.json +1 -0
  165. package/examples/feature-demos/sitewide-content/.astro/dev.json +13 -0
  166. package/examples/feature-demos/sitewide-content/.astro/dev.log +18 -0
  167. package/examples/feature-demos/sitewide-content/.astro/settings.json +5 -0
  168. package/examples/feature-demos/sitewide-content/.astro/types.d.ts +2 -0
  169. package/examples/feature-demos/sitewide-content/README.md +18 -0
  170. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/_metadata.json +74 -0
  171. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro-B3JHJyiv.js +434 -0
  172. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro-B3JHJyiv.js.map +1 -0
  173. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
  174. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  175. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
  176. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  177. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
  178. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  179. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
  180. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  181. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/audit-C3SVr7dT.js +1534 -0
  182. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/audit-C3SVr7dT.js.map +1 -0
  183. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/highlight-BLtvqtzO.js +48 -0
  184. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +1 -0
  185. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/icons-C92i61-2.js +42 -0
  186. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/icons-C92i61-2.js.map +1 -0
  187. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/package.json +3 -0
  188. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
  189. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/settings-v5GgKks_.js +190 -0
  190. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/settings-v5GgKks_.js.map +1 -0
  191. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/toolbar-BFvJVp26.js +458 -0
  192. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +1 -0
  193. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/ui-library-Cd35oAvo.js +1016 -0
  194. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +1 -0
  195. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/window-vo3A6TMn.js +32 -0
  196. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/window-vo3A6TMn.js.map +1 -0
  197. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/xray-DwhzGckb.js +111 -0
  198. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/xray-DwhzGckb.js.map +1 -0
  199. package/examples/feature-demos/sitewide-content/site/.norna/generated-images.json +1 -0
  200. package/examples/feature-demos/sitewide-content/site/config.md +3 -0
  201. package/examples/feature-demos/sitewide-content/site/content.md +16 -0
  202. package/examples/feature-demos/sitewide-content/site/public/logo.svg +6 -0
  203. package/examples/feature-demos/sitewide-content/site/routes/010-identity/route-content.md +20 -0
  204. package/examples/feature-demos/sitewide-content/site/routes/020-notices/route-content.md +22 -0
  205. package/examples/feature-demos/sitewide-content/site/routes/030-footer/route-content.md +17 -0
  206. package/examples/feature-demos/sitewide-content/site/sitewide-content.md +33 -0
  207. package/examples/feature-demos/sitewide-content/site/theme.md +3 -0
  208. package/examples/feature-demos/theme-presets/.astro/collections/site.schema.json +59 -0
  209. package/examples/feature-demos/theme-presets/.astro/collections/sitewide.schema.json +138 -0
  210. package/examples/feature-demos/theme-presets/.astro/collections/theme.schema.json +755 -0
  211. package/examples/feature-demos/theme-presets/.astro/content-assets.mjs +4 -0
  212. package/examples/feature-demos/theme-presets/.astro/content-modules.mjs +1 -0
  213. package/examples/{typography-presets → feature-demos/theme-presets}/.astro/data-store.json +1 -1
  214. package/examples/feature-demos/theme-presets/.astro/dev-local.json +8 -0
  215. package/examples/{typography-presets → feature-demos/theme-presets}/.astro/dev.json +2 -2
  216. package/examples/feature-demos/theme-presets/.astro/dev.log +164 -0
  217. package/examples/feature-demos/theme-presets/.astro/types.d.ts +2 -0
  218. package/examples/feature-demos/theme-presets/README.md +18 -0
  219. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/package.json +3 -0
  220. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
  221. package/examples/{typography-presets → feature-demos/theme-presets}/site/.norna/generated-images.json +45 -45
  222. package/examples/feature-demos/theme-presets/site/config.md +3 -0
  223. package/examples/feature-demos/theme-presets/site/content.md +17 -0
  224. package/examples/feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio/shepherd-portrait.jpg +0 -0
  225. package/examples/feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio/white-puppy.jpg +0 -0
  226. package/examples/{typography-presets/site/routes/010-quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio}/route-content.md +8 -8
  227. package/examples/feature-demos/theme-presets/site/routes/010-portfolio/theme.md +3 -0
  228. package/examples/feature-demos/theme-presets/site/routes/020-documentation/images/documentation/retriever-portrait.jpg +0 -0
  229. package/examples/{typography-presets/site/routes/020-compact-gallery → feature-demos/theme-presets/site/routes/020-documentation}/route-content.md +8 -8
  230. package/examples/feature-demos/theme-presets/site/routes/020-documentation/theme.md +3 -0
  231. package/examples/feature-demos/theme-presets/site/routes/030-project/images/project/boxer-portrait.jpg +0 -0
  232. package/examples/feature-demos/theme-presets/site/routes/030-project/images/project/grass-puppy.jpg +0 -0
  233. package/examples/{typography-presets/site/routes/030-text-forward → feature-demos/theme-presets/site/routes/030-project}/route-content.md +8 -8
  234. package/examples/feature-demos/theme-presets/site/routes/030-project/theme.md +3 -0
  235. package/examples/feature-demos/theme-presets/site/routes/040-statement/images/statement/sheepdog-grass.jpg +0 -0
  236. package/examples/{typography-presets → feature-demos/theme-presets}/site/routes/040-statement/route-content.md +3 -3
  237. package/examples/feature-demos/theme-presets/site/routes/040-statement/theme.md +3 -0
  238. package/examples/{typography-presets → feature-demos/theme-presets}/site/sitewide-content.md +1 -1
  239. package/examples/feature-demos/theme-presets/site/theme.md +3 -0
  240. package/fixtures/basic/site/config.md +3 -0
  241. package/fixtures/content-model-v2/site/config.md +3 -0
  242. package/package.json +17 -8
  243. package/scripts/build-pages.mjs +69 -0
  244. package/scripts/check-config.mjs +3 -11
  245. package/scripts/deploy-site.mjs +12 -8
  246. package/scripts/export-theme-preset.mjs +31 -0
  247. package/scripts/init-site.mjs +1 -0
  248. package/scripts/lib/example-sites.mjs +37 -0
  249. package/scripts/lib/github-repository.mjs +44 -0
  250. package/scripts/lib/norna-markdown-blocks.mjs +97 -10
  251. package/scripts/lib/presentation.mjs +6 -5
  252. package/scripts/lib/project-config.mjs +114 -124
  253. package/scripts/lib/site-content.mjs +9 -2
  254. package/scripts/lib/site-paths.mjs +6 -2
  255. package/scripts/lib/theme-config.mjs +3 -0
  256. package/scripts/lib/theme-presets.mjs +209 -0
  257. package/scripts/show-typography.mjs +6 -5
  258. package/scripts/test-client-javascript.mjs +236 -0
  259. package/scripts/test-content-check.mjs +103 -0
  260. package/scripts/test-content-model-v2.mjs +9 -13
  261. package/scripts/test-documentation.mjs +116 -0
  262. package/scripts/test-engine-commands.mjs +13 -3
  263. package/scripts/test-examples.mjs +24 -0
  264. package/scripts/test-navigation.mjs +73 -45
  265. package/scripts/test-package-check.mjs +17 -9
  266. package/scripts/test-project-config.mjs +130 -0
  267. package/scripts/test-temporary-visibility.mjs +3 -10
  268. package/scripts/test-theme-presets.mjs +146 -0
  269. package/scripts/watch-pages-deploy.mjs +15 -11
  270. package/src/components/GalleryGrid.astro +30 -230
  271. package/src/components/ImageCarousel.astro +184 -0
  272. package/src/components/MobileSectionNavigationScript.astro +14 -0
  273. package/src/components/NornaSmoothScrollScript.astro +250 -0
  274. package/src/components/SiteNavigation.astro +58 -341
  275. package/src/components/SiteNotesScript.astro +43 -0
  276. package/src/components/SitePage.astro +6 -3
  277. package/src/components/SiteSection.astro +4 -47
  278. package/src/content.config.ts +2 -0
  279. package/src/layouts/BaseLayout.astro +1 -1
  280. package/src/lib/galleryImages.ts +53 -0
  281. package/src/styles/global.css +83 -1
  282. package/starters/basic/.github/workflows/deploy.yml +1 -2
  283. package/starters/basic/README.md +10 -8
  284. package/starters/basic/package.json +1 -0
  285. package/starters/basic/site/config.md +3 -0
  286. package/starters/project/.github/workflows/deploy.yml +1 -2
  287. package/starters/project/README.md +3 -4
  288. package/starters/project/package.json +1 -0
  289. package/starters/project/site/config.md +3 -0
  290. package/examples/001-dog-shelter-single-page/README.md +0 -9
  291. package/examples/001-dog-shelter-single-page/site/config.mjs +0 -27
  292. package/examples/001-dog-shelter-single-page/site/content.md +0 -38
  293. package/examples/001-dog-shelter-single-page/site/images/what-we-do/dog-house.svg +0 -25
  294. package/examples/001-dog-shelter-single-page/site/images/you-can-help/heart.svg +0 -13
  295. package/examples/001-dog-shelter-single-page/site/sitewide-content.md +0 -6
  296. package/examples/001-dog-shelter-single-page/site/theme.md +0 -25
  297. package/examples/002-dog-shelter-multi-page/README.md +0 -9
  298. package/examples/002-dog-shelter-multi-page/site/config.mjs +0 -27
  299. package/examples/002-dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rex.svg +0 -19
  300. package/examples/002-dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rover.svg +0 -19
  301. package/examples/002-dog-shelter-multi-page/site/routes/010-dogs/route-content.md +0 -30
  302. package/examples/002-dog-shelter-multi-page/site/sitewide-content.md +0 -6
  303. package/examples/002-dog-shelter-multi-page/site/theme.md +0 -25
  304. package/examples/dog-gallery/.astro/collections/site.schema.json +0 -776
  305. package/examples/dog-gallery/.astro/data-store.json +0 -1
  306. package/examples/dog-gallery/.astro/dev-local.json +0 -8
  307. package/examples/dog-gallery/.astro/dev.json +0 -13
  308. package/examples/dog-gallery/.astro/dev.log +0 -95
  309. package/examples/dog-gallery/.astro/settings.json +0 -5
  310. package/examples/dog-gallery/dist/_astro/GalleryGrid.astro_astro_type_script_index_0_lang.B0kxDyGz.js +0 -1
  311. package/examples/dog-gallery/dist/_astro/SitePage.DicGh_5N.css +0 -1
  312. package/examples/dog-gallery/dist/dog-care/index.html +0 -15
  313. package/examples/dog-gallery/dist/dog-shelter-logo.svg +0 -14
  314. package/examples/dog-gallery/dist/favicon.svg +0 -7
  315. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1080.webp +0 -0
  316. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1440.webp +0 -0
  317. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1536.webp +0 -0
  318. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-480.webp +0 -0
  319. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-768.webp +0 -0
  320. package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-480.webp +0 -0
  321. package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-768.webp +0 -0
  322. package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-960.webp +0 -0
  323. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1080.webp +0 -0
  324. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1440.webp +0 -0
  325. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1920.webp +0 -0
  326. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-3939.webp +0 -0
  327. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-480.webp +0 -0
  328. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-768.webp +0 -0
  329. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1080.webp +0 -0
  330. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1440.webp +0 -0
  331. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1920.webp +0 -0
  332. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-2400.webp +0 -0
  333. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-480.webp +0 -0
  334. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-768.webp +0 -0
  335. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1080.webp +0 -0
  336. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1440.webp +0 -0
  337. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1600.webp +0 -0
  338. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-480.webp +0 -0
  339. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-768.webp +0 -0
  340. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1080.webp +0 -0
  341. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1440.webp +0 -0
  342. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1920.webp +0 -0
  343. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-2576.webp +0 -0
  344. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-480.webp +0 -0
  345. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-768.webp +0 -0
  346. package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-1080.webp +0 -0
  347. package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-1277.webp +0 -0
  348. package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-480.webp +0 -0
  349. package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-768.webp +0 -0
  350. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1080.webp +0 -0
  351. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1440.webp +0 -0
  352. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1536.webp +0 -0
  353. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-480.webp +0 -0
  354. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-768.webp +0 -0
  355. package/examples/dog-gallery/dist/index.html +0 -45
  356. package/examples/dog-gallery/node_modules/.vite/deps/_metadata.json +0 -74
  357. package/examples/dog-gallery/site/.norna/generated-images.json +0 -226
  358. package/examples/dog-gallery/site/config.mjs +0 -47
  359. package/examples/dog-gallery/site/content.md +0 -127
  360. package/examples/dog-gallery/site/images/black-dogs/black-puppy-meadow.png +0 -0
  361. package/examples/dog-gallery/site/images/black-dogs/photo-of-a-black-dog.jpg +0 -0
  362. package/examples/dog-gallery/site/images/brown-dogs/brown-dog.jpg +0 -0
  363. package/examples/dog-gallery/site/images/brown-dogs/dog-accompanies-master.jpg +0 -0
  364. package/examples/dog-gallery/site/images/golden-dogs/golden-retriever.jpg +0 -0
  365. package/examples/dog-gallery/site/images/golden-dogs/toller-puppy.jpg +0 -0
  366. package/examples/dog-gallery/site/images/white-dogs/white-cute-dog.jpg +0 -0
  367. package/examples/dog-gallery/site/images/white-dogs/white-puppy-garden.png +0 -0
  368. package/examples/dog-gallery/site/public/favicon.svg +0 -7
  369. package/examples/dog-gallery/site/public/logo.svg +0 -14
  370. package/examples/dog-gallery/site/routes/010-dog-care/route-content.md +0 -38
  371. package/examples/dog-gallery/site/sitewide-content.md +0 -16
  372. package/examples/dog-gallery/site/theme.md +0 -101
  373. package/examples/presentation/README.md +0 -15
  374. package/examples/presentation/site/config.mjs +0 -29
  375. package/examples/presentation/site/content.md +0 -67
  376. package/examples/presentation/site/public/robots.txt +0 -2
  377. package/examples/presentation/site/routes/010-presets/route-content.md +0 -63
  378. package/examples/presentation/site/routes/010-presets/theme.md +0 -4
  379. package/examples/presentation/site/routes/020-media/theme.md +0 -4
  380. package/examples/presentation/site/sitewide-content.md +0 -6
  381. package/examples/presentation/site/theme.md +0 -43
  382. package/examples/typography-presets/.astro/dev.log +0 -45
  383. package/examples/typography-presets/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -1
  384. package/examples/typography-presets/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -1
  385. package/examples/typography-presets/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -1
  386. package/examples/typography-presets/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -1
  387. package/examples/typography-presets/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -1
  388. package/examples/typography-presets/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -1
  389. package/examples/typography-presets/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -1
  390. package/examples/typography-presets/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -1
  391. package/examples/typography-presets/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -1
  392. package/examples/typography-presets/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -1
  393. package/examples/typography-presets/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -1
  394. package/examples/typography-presets/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -1
  395. package/examples/typography-presets/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -1
  396. package/examples/typography-presets/site/config.mjs +0 -29
  397. package/examples/typography-presets/site/content.md +0 -16
  398. package/examples/typography-presets/site/routes/010-quiet-gallery/theme.md +0 -4
  399. package/examples/typography-presets/site/routes/020-compact-gallery/theme.md +0 -4
  400. package/examples/typography-presets/site/routes/030-text-forward/theme.md +0 -4
  401. package/examples/typography-presets/site/routes/040-statement/theme.md +0 -4
  402. package/examples/typography-presets/site/theme.md +0 -43
  403. package/fixtures/basic/site/config.mjs +0 -27
  404. package/fixtures/content-model-v2/site/config.mjs +0 -11
  405. package/starters/basic/site/config.mjs +0 -27
  406. package/starters/project/site/config.mjs +0 -30
  407. /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/collections/site.schema.json +0 -0
  408. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/.astro/content-assets.mjs +0 -0
  409. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/.astro/content-modules.mjs +0 -0
  410. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/.astro/types.d.ts +0 -0
  411. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/package.json +0 -0
  412. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -0
  413. /package/examples/{typography-presets/site/routes/020-compact-gallery/images/compact-gallery/retriever-portrait.jpg → complete-sites/dog-shelter-multi-page/site/images/intro/shelter-dog.jpg} +0 -0
  414. /package/examples/{001-dog-shelter-single-page → complete-sites/dog-shelter-multi-page}/site/public/favicon.svg +0 -0
  415. /package/examples/{001-dog-shelter-single-page → complete-sites/dog-shelter-multi-page}/site/public/robots.txt +0 -0
  416. /package/examples/{typography-presets/site/routes/010-quiet-gallery/images/quiet-gallery/white-puppy.jpg → complete-sites/dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rex.jpg} +0 -0
  417. /package/examples/{typography-presets/site/routes/030-text-forward/images/text-forward/boxer-portrait.jpg → complete-sites/dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rover.jpg} +0 -0
  418. /package/examples/{typography-presets/site/routes/040-statement/images/statement/sheepdog-grass.jpg → complete-sites/dog-shelter-multi-page/site/routes/020-adopt/images/adopt/adoption-dog.jpg} +0 -0
  419. /package/examples/{typography-presets → complete-sites/dog-shelter-single-page}/.astro/content-assets.mjs +0 -0
  420. /package/examples/{typography-presets → complete-sites/dog-shelter-single-page}/.astro/content-modules.mjs +0 -0
  421. /package/examples/{typography-presets → complete-sites/dog-shelter-single-page}/.astro/types.d.ts +0 -0
  422. /package/examples/{typography-presets → complete-sites/dog-shelter-single-page}/node_modules/.vite/deps/package.json +0 -0
  423. /package/examples/{typography-presets → complete-sites/dog-shelter-single-page}/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -0
  424. /package/examples/{typography-presets/site/routes/030-text-forward/images/text-forward/grass-puppy.jpg → complete-sites/dog-shelter-single-page/site/images/what-we-do/rescue-dog.jpg} +0 -0
  425. /package/examples/{typography-presets/site/routes/010-quiet-gallery/images/quiet-gallery/shepherd-portrait.jpg → complete-sites/dog-shelter-single-page/site/images/you-can-help/foster-dog.jpg} +0 -0
  426. /package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-single-page}/site/public/favicon.svg +0 -0
  427. /package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-single-page}/site/public/robots.txt +0 -0
  428. /package/examples/{dog-gallery/dist → feature-demos/media-and-surfaces/site/public}/robots.txt +0 -0
  429. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/cards/card-carousel.svg +0 -0
  430. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/cards/card-stack.svg +0 -0
  431. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/cards/card-surfaces.svg +0 -0
  432. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/carousel/carousel-one.svg +0 -0
  433. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/carousel/carousel-three.svg +0 -0
  434. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/carousel/carousel-two.svg +0 -0
  435. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/stack/stack-one.svg +0 -0
  436. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/stack/stack-two.svg +0 -0
  437. /package/examples/{dog-gallery → feature-demos/sitewide-content}/site/public/robots.txt +0 -0
  438. /package/examples/{typography-presets → feature-demos/theme-presets}/.astro/collections/routeTheme.schema.json +0 -0
  439. /package/examples/{typography-presets → feature-demos/theme-presets}/.astro/content.d.ts +0 -0
  440. /package/examples/{typography-presets → feature-demos/theme-presets}/.astro/settings.json +0 -0
  441. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/_metadata.json +0 -0
  442. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -0
  443. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -0
  444. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_aria-query.js +0 -0
  445. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -0
  446. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_axobject-query.js +0 -0
  447. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -0
  448. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_html-escaper.js +0 -0
  449. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -0
  450. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -0
  451. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -0
  452. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -0
  453. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -0
  454. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/embla-carousel.js +0 -0
  455. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/embla-carousel.js.map +0 -0
  456. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -0
  457. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -0
  458. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/icons-RyebKi45.js +0 -0
  459. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -0
  460. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -0
  461. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -0
  462. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/toolbar-63PA619H.js +0 -0
  463. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -0
  464. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -0
  465. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -0
  466. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/window-jPkpKVcy.js +0 -0
  467. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -0
  468. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -0
  469. /package/examples/{dog-gallery → feature-demos/theme-presets}/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -0
  470. /package/examples/{typography-presets → feature-demos/theme-presets}/site/public/robots.txt +0 -0
  471. /package/examples/{typography-presets/site/routes/010-quiet-gallery/images/quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio}/puppy-portrait.jpg +0 -0
  472. /package/examples/{typography-presets/site/routes/020-compact-gallery/images/compact-gallery → feature-demos/theme-presets/site/routes/020-documentation/images/documentation}/black-schnauzer.jpg +0 -0
  473. /package/examples/{typography-presets/site/routes/020-compact-gallery/images/compact-gallery → feature-demos/theme-presets/site/routes/020-documentation/images/documentation}/schnauzer-portrait.jpg +0 -0
  474. /package/examples/{typography-presets/site/routes/030-text-forward/images/text-forward → feature-demos/theme-presets/site/routes/030-project/images/project}/grey-street-dog.jpg +0 -0
  475. /package/examples/{typography-presets → feature-demos/theme-presets}/site/routes/040-statement/images/statement/black-pedigree.jpg +0 -0
  476. /package/examples/{typography-presets → feature-demos/theme-presets}/site/routes/040-statement/images/statement/sheepdog-wide.jpg +0 -0
@@ -20,9 +20,10 @@ const h2Regex = /^##\s+.*$/gm;
20
20
  const explicitHeadingIdRegex = /\s*\{#([a-z0-9-]+)\}\s*$/;
21
21
  const deprecatedInlineStyleReferenceRegex = /\[[^\]\n]+\]\{\.([a-z][a-z0-9-]*)\}/g;
22
22
  const frontmatterDelimiterRegex = /^---\s*$/;
23
+ const knownConfigTopLevelFrontmatterKeys = new Set(['url', 'language', 'scrollBehavior']);
23
24
  const knownContentTopLevelFrontmatterKeys = new Set(['title', 'description', 'navigation', 'sections']);
24
- const knownThemeTopLevelFrontmatterKeys = new Set(['navigation', 'layout', 'gallery', 'typography', 'presentation']);
25
- const knownRouteThemeTopLevelFrontmatterKeys = new Set(['navigation', 'layout', 'gallery', 'typography', 'presentation']);
25
+ const knownThemeTopLevelFrontmatterKeys = new Set(['navigation', 'preset', 'layout', 'gallery', 'typography', 'presentation']);
26
+ const knownRouteThemeTopLevelFrontmatterKeys = new Set(['navigation', 'preset', 'layout', 'gallery', 'typography', 'presentation']);
26
27
  const knownSitewideTopLevelFrontmatterKeys = new Set(['navigation', 'banners', 'footer']);
27
28
  const knownNestedFrontmatterKeys = new Set([
28
29
  'align',
@@ -270,6 +271,12 @@ export const validateContentFrontmatterStructure = (frontmatter, addIssue) =>
270
271
  fileKind: 'content',
271
272
  });
272
273
 
274
+ export const validateConfigFrontmatterStructure = (frontmatter, addIssue) =>
275
+ validateFrontmatterStructure(frontmatter, addIssue, {
276
+ knownTopLevelFrontmatterKeys: knownConfigTopLevelFrontmatterKeys,
277
+ fileKind: 'config',
278
+ });
279
+
273
280
  export const validateThemeFrontmatterStructure = (frontmatter, addIssue) =>
274
281
  validateFrontmatterStructure(frontmatter, addIssue, {
275
282
  knownTopLevelFrontmatterKeys: knownThemeTopLevelFrontmatterKeys,
@@ -22,7 +22,10 @@ export const invocationRoot = configuredInvocationRoot
22
22
 
23
23
  const hasSiteFilesInDirectory = (siteDir) => {
24
24
  return (
25
- existsSync(path.join(siteDir, 'config.mjs'))
25
+ (
26
+ existsSync(path.join(siteDir, 'config.md'))
27
+ || existsSync(path.join(siteDir, 'config.mjs'))
28
+ )
26
29
  && existsSync(path.join(siteDir, 'content.md'))
27
30
  );
28
31
  };
@@ -114,7 +117,8 @@ const getPathLabel = (filePath) => {
114
117
  };
115
118
 
116
119
  export const siteDir = resolvedSitePaths.siteDir;
117
- export const siteConfigPath = path.join(siteDir, 'config.mjs');
120
+ export const siteConfigPath = path.join(siteDir, 'config.md');
121
+ export const legacySiteConfigPath = path.join(siteDir, 'config.mjs');
118
122
  export const siteThemePath = path.join(siteDir, 'theme.md');
119
123
  export const siteContentPath = path.join(siteDir, 'content.md');
120
124
  export const sitewideContentPath = path.join(siteDir, 'sitewide-content.md');
@@ -14,6 +14,7 @@ import {
14
14
  siteThemePath,
15
15
  sitewideContentLabel,
16
16
  } from './site-paths.mjs';
17
+ import { resolveThemeConfig } from './theme-presets.mjs';
17
18
 
18
19
  export const readThemeConfig = async () => {
19
20
  const themeFile = await readFile(siteThemePath, 'utf8').catch((error) => {
@@ -40,6 +41,7 @@ export const readThemeConfig = async () => {
40
41
  if (Object.hasOwn(config, 'navigation')) {
41
42
  throw new Error(`${siteThemeLabel} may not define navigation. Brand and logo belong in ${sitewideContentLabel}.`);
42
43
  }
44
+ resolveThemeConfig(config, siteThemeLabel);
43
45
 
44
46
  return config;
45
47
  };
@@ -92,6 +94,7 @@ export const validateRouteThemeFiles = async () => {
92
94
  if (Object.hasOwn(config, 'navigation')) {
93
95
  throw new Error(`${file.label} may not define navigation. Brand and logo belong in ${sitewideContentLabel}.`);
94
96
  }
97
+ resolveThemeConfig(config, file.label);
95
98
 
96
99
  configs.push({ ...file, config });
97
100
  }
@@ -0,0 +1,209 @@
1
+ import { mergeDeep } from './typography.mjs';
2
+
3
+ export const themePresetNames = [
4
+ 'portfolio',
5
+ 'documentation',
6
+ 'project',
7
+ 'statement',
8
+ ];
9
+
10
+ export const themePresets = Object.freeze({
11
+ portfolio: Object.freeze({
12
+ layout: {
13
+ density: 'normal',
14
+ pageWidth: '1240px',
15
+ gutter: {
16
+ desktop: 'clamp(1.25rem, 4vw, 3rem)',
17
+ mobile: '1rem',
18
+ },
19
+ },
20
+ gallery: {
21
+ width: '1000px',
22
+ maxAvailableWidthPercent: { desktop: 100, mobile: 100 },
23
+ maxAvailableHeightPercent: { desktop: 78, mobile: 68 },
24
+ },
25
+ typography: {
26
+ fontFamily: "'Helvetica Neue', Arial, sans-serif",
27
+ preset: 'quiet-gallery',
28
+ rhythm: 'normal',
29
+ },
30
+ presentation: {
31
+ palette: 'dark',
32
+ sectionSurfaces: {
33
+ mode: 'none',
34
+ sequence: ['base', 'soft', 'emphasis'],
35
+ },
36
+ },
37
+ }),
38
+ documentation: Object.freeze({
39
+ layout: {
40
+ density: 'compact',
41
+ pageWidth: '1240px',
42
+ gutter: {
43
+ desktop: 'clamp(1.25rem, 4vw, 3rem)',
44
+ mobile: '1rem',
45
+ },
46
+ },
47
+ gallery: {
48
+ width: '920px',
49
+ maxAvailableWidthPercent: { desktop: 100, mobile: 100 },
50
+ maxAvailableHeightPercent: { desktop: 74, mobile: 68 },
51
+ },
52
+ typography: {
53
+ fontFamily: "Georgia, 'Times New Roman', serif",
54
+ preset: 'text-forward',
55
+ rhythm: 'compact',
56
+ },
57
+ presentation: {
58
+ palette: 'paper',
59
+ sectionSurfaces: {
60
+ mode: 'cycle',
61
+ sequence: ['base', 'soft'],
62
+ },
63
+ },
64
+ }),
65
+ project: Object.freeze({
66
+ layout: {
67
+ density: 'compact',
68
+ pageWidth: '1120px',
69
+ gutter: {
70
+ desktop: 'clamp(1.25rem, 4vw, 3rem)',
71
+ mobile: '1rem',
72
+ },
73
+ },
74
+ gallery: {
75
+ width: '840px',
76
+ maxAvailableWidthPercent: { desktop: 100, mobile: 100 },
77
+ maxAvailableHeightPercent: { desktop: 70, mobile: 62 },
78
+ },
79
+ typography: {
80
+ fontFamily: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
81
+ preset: 'text-forward',
82
+ rhythm: 'compact',
83
+ },
84
+ presentation: {
85
+ palette: 'light',
86
+ sectionSurfaces: {
87
+ mode: 'cycle',
88
+ sequence: ['base', 'soft'],
89
+ },
90
+ },
91
+ }),
92
+ statement: Object.freeze({
93
+ layout: {
94
+ density: 'airy',
95
+ pageWidth: '1280px',
96
+ gutter: {
97
+ desktop: 'clamp(1.5rem, 5vw, 4rem)',
98
+ mobile: '1rem',
99
+ },
100
+ },
101
+ gallery: {
102
+ width: '1080px',
103
+ maxAvailableWidthPercent: { desktop: 100, mobile: 100 },
104
+ maxAvailableHeightPercent: { desktop: 80, mobile: 70 },
105
+ },
106
+ typography: {
107
+ fontFamily: "'Trebuchet MS', 'Helvetica Neue', Arial, sans-serif",
108
+ preset: 'statement',
109
+ rhythm: 'airy',
110
+ },
111
+ presentation: {
112
+ palette: 'paper',
113
+ sectionSurfaces: {
114
+ mode: 'cycle',
115
+ sequence: ['base', 'emphasis'],
116
+ },
117
+ },
118
+ }),
119
+ });
120
+
121
+ export const getThemePreset = (presetName, sourceLabel = 'theme.md') => {
122
+ const preset = themePresets[presetName];
123
+
124
+ if (!preset) {
125
+ throw new Error(`Unknown theme preset "${presetName}" in ${sourceLabel}. Use one of: ${themePresetNames.join(', ')}.`);
126
+ }
127
+
128
+ return structuredClone(preset);
129
+ };
130
+
131
+ export const resolveThemeConfig = (theme = {}, sourceLabel = 'theme.md') => {
132
+ const presetName = theme?.preset;
133
+ if (presetName === undefined) return structuredClone(theme ?? {});
134
+
135
+ const overrides = structuredClone(theme ?? {});
136
+ delete overrides.preset;
137
+
138
+ return {
139
+ preset: presetName,
140
+ ...mergeDeep(getThemePreset(presetName, sourceLabel), overrides),
141
+ };
142
+ };
143
+
144
+ const quote = (value) => JSON.stringify(value);
145
+ const responsiveValueLines = (label, value, indent = 2) => {
146
+ const prefix = ' '.repeat(indent);
147
+ return [
148
+ `${prefix}${label}:`,
149
+ `${prefix} desktop: ${value.desktop}`,
150
+ `${prefix} mobile: ${value.mobile}`,
151
+ ];
152
+ };
153
+
154
+ export const renderThemePresetReference = (presetName, sourceLabel = 'theme.md') => {
155
+ const preset = getThemePreset(presetName, sourceLabel);
156
+ const { layout, gallery, typography, presentation } = preset;
157
+
158
+ return [
159
+ '---',
160
+ `# Original values for Norna's "${presetName}" theme preset.`,
161
+ '# This is a reference file. Norna only loads theme.md.',
162
+ '# Keep the preset in theme.md and copy only the values you want to override.',
163
+ `# Available theme presets: ${themePresetNames.join(', ')}.`,
164
+ `preset: ${presetName}`,
165
+ '',
166
+ 'layout:',
167
+ ' # Alternatives: compact, normal, airy.',
168
+ ` density: ${layout.density}`,
169
+ ' # Any positive CSS length, including px, rem, em, %, or clamp(...).',
170
+ ` pageWidth: ${layout.pageWidth}`,
171
+ ' gutter:',
172
+ ` desktop: ${layout.gutter.desktop}`,
173
+ ` mobile: ${layout.gutter.mobile}`,
174
+ ' # Optional spacing overrides use blockGap, finalSectionBottom,',
175
+ ' # firstSectionTop, headingToBlock, imageGap, and sectionGap.',
176
+ '',
177
+ 'gallery:',
178
+ ' # width accepts a positive CSS length.',
179
+ ` width: ${gallery.width}`,
180
+ ' # Percent values must be greater than 0 and at most 100.',
181
+ ...responsiveValueLines('maxAvailableWidthPercent', gallery.maxAvailableWidthPercent),
182
+ ...responsiveValueLines('maxAvailableHeightPercent', gallery.maxAvailableHeightPercent),
183
+ '',
184
+ 'typography:',
185
+ ' # Any valid CSS font-family stack without semicolons.',
186
+ ` fontFamily: ${quote(typography.fontFamily)}`,
187
+ ' # Alternatives: quiet-gallery, compact-gallery, text-forward, statement.',
188
+ ` preset: ${typography.preset}`,
189
+ ' # Alternatives: compact, normal, airy.',
190
+ ` rhythm: ${typography.rhythm}`,
191
+ ' # Fine-grained overrides can target headings.h1-h4, body, and caption.',
192
+ ' # Example:',
193
+ ' # overrides:',
194
+ ' # headings:',
195
+ ' # h2:',
196
+ ' # size: large # small, medium, large, or xlarge',
197
+ '',
198
+ 'presentation:',
199
+ ' # Alternatives: dark, light, paper.',
200
+ ` palette: ${presentation.palette}`,
201
+ ' sectionSurfaces:',
202
+ ' # Alternatives: none, cycle.',
203
+ ` mode: ${presentation.sectionSurfaces.mode}`,
204
+ ' # Use one to three unique values from: base, soft, emphasis.',
205
+ ` sequence: [${presentation.sectionSurfaces.sequence.join(', ')}]`,
206
+ '---',
207
+ '',
208
+ ].join('\n');
209
+ };
@@ -1,6 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { findMap } from './lib/frontmatter-yaml.mjs';
3
+ import { parseYamlMapping } from './lib/frontmatter-yaml.mjs';
4
4
  import {
5
5
  defaultTypography,
6
6
  resolveTypographyConfig,
@@ -25,6 +25,7 @@ import {
25
25
  siteThemeLabel,
26
26
  siteThemePath,
27
27
  } from './lib/site-paths.mjs';
28
+ import { resolveThemeConfig } from './lib/theme-presets.mjs';
28
29
 
29
30
  const mode = process.argv[2] ?? 'show';
30
31
 
@@ -204,8 +205,8 @@ const readThemeTypography = async () => {
204
205
  ].join('\n'));
205
206
  }
206
207
 
207
- const themeLines = themeFrontmatterBody.split(/\r?\n/);
208
- const themeTypographyConfig = findMap(themeLines, 'typography', 0, themeLines.length, 0)?.value;
208
+ const themeConfig = resolveThemeConfig(parseYamlMapping(themeFrontmatterBody), siteThemeLabel);
209
+ const themeTypographyConfig = themeConfig.typography;
209
210
  return resolveAnnotatedTypographyConfig(themeTypographyConfig ?? defaultTypography, siteThemeLabel);
210
211
  };
211
212
 
@@ -231,8 +232,8 @@ const readRouteThemeTypography = async (contentFile, themeTypography) => {
231
232
  ].join('\n'));
232
233
  }
233
234
 
234
- const lines = frontmatterBody.split(/\r?\n/);
235
- const typographyConfig = findMap(lines, 'typography', 0, lines.length, 0)?.value;
235
+ const routeThemeConfig = resolveThemeConfig(parseYamlMapping(frontmatterBody), routeThemeLabel);
236
+ const typographyConfig = routeThemeConfig.typography;
236
237
  return resolveAnnotatedTypographyConfig(typographyConfig ?? defaultTypography, routeThemeLabel);
237
238
  };
238
239
 
@@ -0,0 +1,236 @@
1
+ import assert from 'node:assert/strict';
2
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
3
+ import { spawnSync } from 'node:child_process';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
8
+ const nornaBin = path.join(repoRoot, 'bin', 'norna.mjs');
9
+ const tempParent = path.join(repoRoot, 'node_modules', '.cache');
10
+ await mkdir(tempParent, { recursive: true });
11
+ const tempRoot = await mkdtemp(path.join(tempParent, 'norna-client-javascript-'));
12
+ const siteDir = path.join(tempRoot, 'site');
13
+ const routeDir = path.join(siteDir, 'routes', '010-details');
14
+ const configPath = path.join(siteDir, 'config.md');
15
+
16
+ const runBuild = () => {
17
+ const result = spawnSync(process.execPath, [nornaBin, 'build'], {
18
+ cwd: tempRoot,
19
+ encoding: 'utf8',
20
+ env: {
21
+ ...process.env,
22
+ NORNA_SITE_DIR: siteDir,
23
+ },
24
+ });
25
+
26
+ if (result.status !== 0) {
27
+ throw new Error([
28
+ `norna build exited with code ${result.status}.`,
29
+ result.stdout.trim(),
30
+ result.stderr.trim(),
31
+ ].filter(Boolean).join('\n'));
32
+ }
33
+ };
34
+
35
+ const readPage = (pathname = 'index.html') => readFile(path.join(tempRoot, 'dist', pathname), 'utf8');
36
+ const getScripts = (html) => html.match(/<script\b[^>]*>[\s\S]*?<\/script>/gi) ?? [];
37
+ const assertNoClientJavaScript = (html, label) => {
38
+ assert.deepEqual(getScripts(html), [], `${label} should not require client-side JavaScript.`);
39
+ };
40
+ const assertScrollBehavior = (html, behavior, label) => {
41
+ assert.match(
42
+ html,
43
+ new RegExp(`<html\\b[^>]*style="[^"]*scroll-behavior: ${behavior}(?:;|&quot;|\")`),
44
+ `${label} should render scroll-behavior: ${behavior}.`,
45
+ );
46
+ };
47
+ const writeConfig = (scrollBehavior) => writeFile(configPath, `---
48
+ url: https://example.com/
49
+ ${scrollBehavior ? `scrollBehavior: ${scrollBehavior}\n` : ''}---
50
+ `);
51
+ const writeSvg = (filePath, color) => writeFile(filePath, [
52
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 90">',
53
+ ` <rect width="160" height="90" fill="${color}"/>`,
54
+ '</svg>',
55
+ '',
56
+ ].join('\n'));
57
+
58
+ try {
59
+ await mkdir(path.join(siteDir, 'public'), { recursive: true });
60
+ await writeConfig();
61
+ await writeFile(path.join(siteDir, 'theme.md'), `---
62
+ typography:
63
+ preset: text-forward
64
+ presentation:
65
+ palette: paper
66
+ ---
67
+ `);
68
+ await writeFile(path.join(siteDir, 'sitewide-content.md'), `---
69
+ navigation:
70
+ brand: Test site
71
+ ---
72
+ `);
73
+ await writeFile(path.join(siteDir, 'content.md'), `---
74
+ title: Static single page
75
+ description: A single page without interactive features.
76
+ sections:
77
+ intro: {}
78
+ more: {}
79
+ ---
80
+ ## Intro {#intro}
81
+
82
+ Ordinary Markdown needs no client-side JavaScript.
83
+
84
+ ## More {#more}
85
+
86
+ Additional sections do not make the page interactive.
87
+ `);
88
+
89
+ runBuild();
90
+ const instantHtml = await readPage();
91
+ assertNoClientJavaScript(instantHtml, 'The default instant scroll mode');
92
+ assertScrollBehavior(instantHtml, 'auto', 'The default instant scroll mode');
93
+ assert.match(instantHtml, /<nav class="page-nav"/);
94
+ assert.match(instantHtml, /href="#intro"/);
95
+ assert.match(instantHtml, /href="#more"/);
96
+
97
+ await writeConfig('browser-smooth');
98
+ runBuild();
99
+ const browserSmoothHtml = await readPage();
100
+ assertNoClientJavaScript(browserSmoothHtml, 'The browser smooth scroll mode');
101
+ assertScrollBehavior(browserSmoothHtml, 'smooth', 'The browser smooth scroll mode');
102
+
103
+ await writeConfig('norna-smooth');
104
+ runBuild();
105
+ const nornaSmoothHtml = await readPage();
106
+ assert.equal(
107
+ getScripts(nornaSmoothHtml).length,
108
+ 1,
109
+ 'The Norna smooth scroll mode should load only its scroll implementation.',
110
+ );
111
+ assertScrollBehavior(nornaSmoothHtml, 'auto', 'The Norna smooth scroll mode');
112
+ assert.match(
113
+ getScripts(nornaSmoothHtml)[0],
114
+ /nornaScrollActive/,
115
+ 'The Norna smooth scroll mode should emit the Norna scroll implementation.',
116
+ );
117
+
118
+ await writeConfig();
119
+
120
+ await mkdir(path.join(routeDir, 'images', 'details'), { recursive: true });
121
+ await writeSvg(path.join(routeDir, 'images', 'details', 'first.svg'), '#7b8f78');
122
+ await writeSvg(path.join(routeDir, 'images', 'details', 'second.svg'), '#a7664b');
123
+ await writeFile(path.join(siteDir, 'content.md'), `---
124
+ title: Static baseline
125
+ description: A page without interactive features.
126
+ sections:
127
+ intro: {}
128
+ ---
129
+ ## Intro {#intro}
130
+
131
+ Ordinary Markdown needs no client-side JavaScript.
132
+ `);
133
+ await writeFile(path.join(routeDir, 'route-content.md'), `---
134
+ title: Details
135
+ description: Static managed images.
136
+ sections:
137
+ details: {}
138
+ ---
139
+ ## Details {#details}
140
+
141
+ Static image stacks remain static.
142
+
143
+ \`\`\`norna-image-stack
144
+ - image: first.svg
145
+ caption: First image.
146
+ - image: second.svg
147
+ caption: Second image.
148
+ \`\`\`
149
+
150
+ \`\`\`norna-card-list
151
+ - title: Static card
152
+ text: Cards do not require client-side behaviour.
153
+ \`\`\`
154
+ `);
155
+
156
+ runBuild();
157
+ assertNoClientJavaScript(await readPage(), 'A plain homepage in a multi-page site');
158
+ assertNoClientJavaScript(
159
+ await readPage(path.join('details', 'index.html')),
160
+ 'A page with managed image stacks and cards',
161
+ );
162
+
163
+ await writeFile(path.join(siteDir, 'content.md'), `---
164
+ title: Notes
165
+ description: A page with an enhanced sidenote.
166
+ sections:
167
+ intro: {}
168
+ ---
169
+ ## Intro {#intro}
170
+
171
+ This sentence has additional context.{note-ref}
172
+
173
+ {note: The note remains readable without JavaScript, while alignment is enhanced when JavaScript runs.}
174
+ `);
175
+ await writeFile(path.join(routeDir, 'route-content.md'), `---
176
+ title: Carousel
177
+ description: An interactive image carousel.
178
+ sections:
179
+ details: {}
180
+ ---
181
+ ## Details {#details}
182
+
183
+ \`\`\`norna-image-carousel
184
+ - image: first.svg
185
+ caption: First slide.
186
+ - image: second.svg
187
+ caption: Second slide.
188
+ \`\`\`
189
+ `);
190
+
191
+ runBuild();
192
+ const noteHtml = await readPage();
193
+ const carouselHtml = await readPage(path.join('details', 'index.html'));
194
+ assert.equal(getScripts(noteHtml).length, 1, 'A page with notes should load only the note enhancement.');
195
+ assert.match(noteHtml, /data-note-id=/);
196
+ assert.equal(getScripts(carouselHtml).length, 1, 'A carousel page should load only the carousel implementation.');
197
+ assert.match(carouselHtml, /data-carousel/);
198
+ assert.match(getScripts(carouselHtml)[0], /\ssrc=/, 'Carousel JavaScript should be emitted as a module asset.');
199
+
200
+ await writeFile(path.join(siteDir, 'content.md'), `---
201
+ title: Banner
202
+ description: A page with a dismissible banner.
203
+ sections:
204
+ intro: {}
205
+ ---
206
+ ## Intro {#intro}
207
+
208
+ The only interactive feature is the banner.
209
+ `);
210
+ await writeFile(path.join(routeDir, 'route-content.md'), `---
211
+ title: Details
212
+ description: A second plain page.
213
+ sections:
214
+ details: {}
215
+ ---
216
+ ## Details {#details}
217
+
218
+ Plain route content.
219
+ `);
220
+ await writeFile(path.join(siteDir, 'sitewide-content.md'), `---
221
+ banners:
222
+ - id: test-banner
223
+ title: Test banner
224
+ text: This banner can be dismissed.
225
+ ---
226
+ `);
227
+
228
+ runBuild();
229
+ const bannerHtml = await readPage();
230
+ assert.equal(getScripts(bannerHtml).length, 1, 'A dismissible banner should load only its dismissal script.');
231
+ assert.match(getScripts(bannerHtml)[0], /norna-banner:/);
232
+
233
+ console.log('Client JavaScript boundaries test passed.');
234
+ } finally {
235
+ await rm(tempRoot, { force: true, recursive: true });
236
+ }
@@ -239,6 +239,109 @@ test('content:check accepts valid inline notes and ignores note syntax in code f
239
239
  });
240
240
  });
241
241
 
242
+ test('content:check accepts wrapped and explicit multiline inline notes', async () => {
243
+ await withTempProject({
244
+ site: `---
245
+ title: Multiline inline notes
246
+ description: Fixture
247
+ ---
248
+ ## Wrapped {#wrapped}
249
+ This paragraph has a formatter-wrapped note.{note-ref}
250
+
251
+ {note: This note was wrapped by
252
+ an editor before its closing brace.}
253
+
254
+ ## Explicit {#explicit}
255
+ This paragraph has an explicit multiline note.{note-ref}
256
+
257
+ {note:
258
+ This note uses the explicit
259
+ multiline form.
260
+ }
261
+ `,
262
+ files: [],
263
+ }, async (root) => {
264
+ const result = runContentScript(root, ['--check']);
265
+ const output = getOutput(result);
266
+
267
+ assert.equal(result.status, 0, output);
268
+ assert.match(output, /Content check passed\./);
269
+ });
270
+ });
271
+
272
+ test('content:check reports an unclosed multiline inline note', async () => {
273
+ await withTempProject({
274
+ site: `---
275
+ title: Unclosed inline note
276
+ description: Fixture
277
+ ---
278
+ ## Intro {#intro}
279
+ This paragraph has a note reference.{note-ref}
280
+
281
+ {note: This note never closes
282
+ and continues to the end of the file.
283
+ `,
284
+ files: [],
285
+ }, async (root) => {
286
+ const result = runContentScript(root, ['--check']);
287
+ const output = getOutput(result);
288
+
289
+ assert.equal(result.status, 1, output);
290
+ assert.match(output, /The note starting on line \d+ is not closed\./);
291
+ assert.match(output, /End it with "\}" on its own line or at the end of the note text\./);
292
+ });
293
+ });
294
+
295
+ test('content:check rejects nested note syntax before a multiline note is closed', async () => {
296
+ await withTempProject({
297
+ site: `---
298
+ title: Nested inline note syntax
299
+ description: Fixture
300
+ ---
301
+ ## Nested note {#nested-note}
302
+ This paragraph has a note reference.{note-ref}
303
+
304
+ {note: The first note is still open
305
+ {note: A second note starts here.}
306
+
307
+ ## Nested reference {#nested-reference}
308
+ This paragraph has another note reference.{note-ref}
309
+
310
+ {note: This note is still open
311
+ and contains {note-ref} before closing.}
312
+ `,
313
+ files: [],
314
+ }, async (root) => {
315
+ const result = runContentScript(root, ['--check']);
316
+ const output = getOutput(result);
317
+
318
+ assert.equal(result.status, 1, output);
319
+ assert.match(output, /is not closed before another note starts on line \d+\./);
320
+ assert.match(output, /contains "\{note-ref\}" on line \d+\./);
321
+ });
322
+ });
323
+
324
+ test('content:check allows escaped and inline-code note syntax inside notes', async () => {
325
+ await withTempProject({
326
+ site: `---
327
+ title: Literal inline note syntax
328
+ description: Fixture
329
+ ---
330
+ ## Intro {#intro}
331
+ This paragraph has a note reference.{note-ref}
332
+
333
+ {note: Write \\{note: ... to show escaped text, or use \`{note-ref}\` as inline code.}
334
+ `,
335
+ files: [],
336
+ }, async (root) => {
337
+ const result = runContentScript(root, ['--check']);
338
+ const output = getOutput(result);
339
+
340
+ assert.equal(result.status, 0, output);
341
+ assert.match(output, /Content check passed\./);
342
+ });
343
+ });
344
+
242
345
  test('content:check rejects unpaired and repeated inline notes', async () => {
243
346
  await withTempProject({
244
347
  site: `---