@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
@@ -12,7 +12,7 @@ import {
12
12
  extractNornaMarkdownBlocks,
13
13
  getNornaBlockImageReferences,
14
14
  } from './lib/norna-markdown-blocks.mjs';
15
- import { projectConfig } from './lib/project-config.mjs';
15
+ import { getGitHubRepositoryContext, githubPagesWorkflow } from './lib/github-repository.mjs';
16
16
  import {
17
17
  engineRoot,
18
18
  generatedImagesManifestLabel,
@@ -28,9 +28,9 @@ import {
28
28
  const execFileAsync = promisify(execFile);
29
29
 
30
30
  const root = siteProjectRoot;
31
- const branch = projectConfig.github.branch;
32
- const repo = projectConfig.github.repo;
33
- const pagesWorkflow = projectConfig.github.pagesWorkflow;
31
+ const pagesWorkflow = githubPagesWorkflow;
32
+ let branch;
33
+ let repo;
34
34
  const args = process.argv.slice(2);
35
35
  const mode = args[0] === 'commit' ? 'commit' : 'deploy';
36
36
  const modeArgs = mode === 'commit' ? args.slice(1) : args;
@@ -53,9 +53,9 @@ const failedConclusions = new Set(['action_required', 'cancelled', 'failure', 's
53
53
  const deployUsage = [
54
54
  'Usage: norna deploy',
55
55
  '',
56
- `Publishes an already committed ${branch} branch: builds, verifies a clean worktree,`,
57
- `pushes ${branch} when local ${branch} is ahead of origin/${branch}, and checks GitHub Pages.`,
58
- `If local ${branch} already matches origin/${branch}, it skips push and checks Pages.`,
56
+ 'Publishes an already committed default branch: builds, verifies a clean worktree,',
57
+ 'pushes it when local HEAD is ahead of origin, and checks GitHub Pages.',
58
+ 'If local HEAD already matches origin, it skips push and checks Pages.',
59
59
  '',
60
60
  'For the old build-and-commit convenience flow, use:',
61
61
  'norna deploy:commit "Commit message"',
@@ -63,7 +63,7 @@ const deployUsage = [
63
63
  const deployCommitUsage = [
64
64
  'Usage: norna deploy:commit "Commit message"',
65
65
  '',
66
- `Builds, stages only allowed site changes, commits, pushes ${branch},`,
66
+ 'Builds, stages only allowed site changes, commits, pushes the default branch,',
67
67
  'and checks GitHub Pages.',
68
68
  ].join('\n');
69
69
 
@@ -385,6 +385,10 @@ if (modeArgs.includes('--help') || modeArgs.includes('-h')) {
385
385
  }
386
386
 
387
387
  try {
388
+ const githubRepository = await getGitHubRepositoryContext({ cwd: root });
389
+ branch = githubRepository.branch;
390
+ repo = githubRepository.repo;
391
+
388
392
  if (mode === 'commit') {
389
393
  await deployWithCommit();
390
394
  } else {
@@ -0,0 +1,31 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import {
4
+ renderThemePresetReference,
5
+ themePresetNames,
6
+ } from './lib/theme-presets.mjs';
7
+ import { siteDir, siteThemeLabel } from './lib/site-paths.mjs';
8
+
9
+ const [presetName, ...extraArgs] = process.argv.slice(2);
10
+
11
+ if (!presetName || extraArgs.length > 0) {
12
+ throw new Error(`Usage: norna theme:export <preset>\nAvailable presets: ${themePresetNames.join(', ')}`);
13
+ }
14
+
15
+ const filename = `orig-${presetName}-theme.md`;
16
+ const outputPath = path.join(siteDir, filename);
17
+ const outputLabel = path.posix.join(path.posix.dirname(siteThemeLabel), filename);
18
+ const source = renderThemePresetReference(presetName, siteThemeLabel);
19
+
20
+ try {
21
+ await writeFile(outputPath, source, { encoding: 'utf8', flag: 'wx' });
22
+ } catch (error) {
23
+ if (error?.code === 'EEXIST') {
24
+ throw new Error(`${outputLabel} already exists. Norna will not overwrite it.`);
25
+ }
26
+
27
+ throw error;
28
+ }
29
+
30
+ console.log(`Exported the ${presetName} theme preset to ${outputLabel}.`);
31
+ console.log(`Norna still reads ${siteThemeLabel}; use the exported file as an override reference.`);
@@ -133,6 +133,7 @@ const galleryScripts = {
133
133
  'norna:config:check': cliCommand('config:check'),
134
134
  'norna:content:check': cliCommand('content:check'),
135
135
  'norna:sync': cliCommand('content:sync'),
136
+ 'norna:theme:export': cliCommand('theme:export'),
136
137
  'norna:typography:presets': cliCommand('typography presets'),
137
138
  'norna:typography:show': cliCommand('typography show'),
138
139
  'norna:public': cliCommand('site:public'),
@@ -0,0 +1,37 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readdir } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ export const exampleCategories = ['complete-sites', 'feature-demos'];
6
+
7
+ export const getExampleSites = async (root) => {
8
+ const examples = [];
9
+
10
+ for (const category of exampleCategories) {
11
+ const categoryDirectory = path.join(root, 'examples', category);
12
+ const entries = await readdir(categoryDirectory, { withFileTypes: true });
13
+
14
+ for (const entry of entries.filter((candidate) => candidate.isDirectory()).sort((a, b) => a.name.localeCompare(b.name))) {
15
+ const siteDirectory = path.join(categoryDirectory, entry.name, 'site');
16
+ if (!existsSync(path.join(siteDirectory, 'config.md'))) {
17
+ throw new Error(`Example ${path.relative(root, siteDirectory)} is missing config.md.`);
18
+ }
19
+ if (!existsSync(path.join(siteDirectory, 'content.md'))) {
20
+ throw new Error(`Example ${path.relative(root, siteDirectory)} is missing content.md.`);
21
+ }
22
+
23
+ examples.push({
24
+ category,
25
+ name: entry.name,
26
+ siteDirectory,
27
+ siteLabel: path.relative(root, siteDirectory),
28
+ });
29
+ }
30
+ }
31
+
32
+ if (examples.length === 0) {
33
+ throw new Error('No example sites were found.');
34
+ }
35
+
36
+ return examples;
37
+ };
@@ -0,0 +1,44 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+
4
+ const execFileAsync = promisify(execFile);
5
+
6
+ export const githubPagesWorkflow = 'deploy.yml';
7
+
8
+ export const getGitHubRepositoryContext = async ({ cwd, repo } = {}) => {
9
+ const args = [
10
+ 'repo',
11
+ 'view',
12
+ ...(repo ? [repo] : []),
13
+ '--json',
14
+ 'nameWithOwner,defaultBranchRef',
15
+ ];
16
+ let stdout;
17
+
18
+ try {
19
+ ({ stdout } = await execFileAsync('gh', args, {
20
+ cwd,
21
+ maxBuffer: 1024 * 1024,
22
+ }));
23
+ } catch (error) {
24
+ throw new Error([
25
+ 'Could not discover the GitHub repository and its default branch.',
26
+ 'Run this command in a Git repository with a GitHub remote and an authenticated gh CLI, or pass explicit deploy:watch options.',
27
+ error.stderr?.trim(),
28
+ error.message,
29
+ ].filter(Boolean).join('\n'));
30
+ }
31
+
32
+ const result = JSON.parse(stdout || '{}');
33
+ const defaultBranch = result.defaultBranchRef?.name;
34
+ const nameWithOwner = result.nameWithOwner;
35
+
36
+ if (!nameWithOwner || !defaultBranch) {
37
+ throw new Error('GitHub did not report a repository name and default branch.');
38
+ }
39
+
40
+ return Object.freeze({
41
+ branch: defaultBranch,
42
+ repo: nameWithOwner,
43
+ });
44
+ };
@@ -589,7 +589,8 @@ export const splitNornaMarkdownBlockMarkers = (html, options = {}) => {
589
589
  const normalizeBlockSource = (source) => source.replace(/\r\n?/g, '\n').trim();
590
590
 
591
591
  const inlineNoteReferenceRegex = /\{note-ref\}/g;
592
- const inlineNoteDeclarationRegex = /^\s*\{note:\s*(.*)\}\s*$/;
592
+ const inlineNoteDeclarationOpeningRegex = /^\s*\{note:\s*(.*)$/;
593
+ const inlineNoteDeclarationClosingRegex = /^(.*?)\}\s*$/;
593
594
  const inlineNoteDeclarationStartRegex = /\{note(?:\s*:|\s*\})/;
594
595
 
595
596
  const maskInlineCodeAndComments = (source) => source.replace(
@@ -605,7 +606,82 @@ const formatInlineNoteError = (message, options, line) => ({
605
606
  message: failMessage(message, { ...options, line }),
606
607
  });
607
608
 
608
- const getInlineNoteErrorMessage = () => 'Write the note on its own line as "{note: Your explanatory text.}".';
609
+ const getInlineNoteErrorMessage = () => 'Write the note on its own line as "{note: Short text.}", or end a multiline note with "}".';
610
+
611
+ const findUnescapedInlineNoteConstruct = (source) => {
612
+ const constructRegex = /\{note(?:-ref\}|\s*:|\s*\})/g;
613
+
614
+ for (const match of source.matchAll(constructRegex)) {
615
+ const index = match.index ?? 0;
616
+ let precedingBackslashes = 0;
617
+ for (let cursor = index - 1; cursor >= 0 && source[cursor] === '\\'; cursor -= 1) {
618
+ precedingBackslashes += 1;
619
+ }
620
+
621
+ if (precedingBackslashes % 2 === 0) {
622
+ return match[0].startsWith('{note-ref') ? 'reference' : 'note';
623
+ }
624
+ }
625
+
626
+ return null;
627
+ };
628
+
629
+ const collectInlineNoteDeclaration = (lines, maskedLines, startIndex) => {
630
+ const openingMatch = (lines[startIndex] ?? '').match(inlineNoteDeclarationOpeningRegex);
631
+ if (!openingMatch) return null;
632
+ const maskedOpeningMatch = (maskedLines[startIndex] ?? '').match(inlineNoteDeclarationOpeningRegex);
633
+
634
+ const textLines = [];
635
+ let currentLine = openingMatch[1] ?? '';
636
+ let currentMaskedLine = maskedOpeningMatch?.[1] ?? '';
637
+
638
+ for (let index = startIndex; index < lines.length; index += 1) {
639
+ const conflictType = findUnescapedInlineNoteConstruct(currentMaskedLine);
640
+ if (conflictType) {
641
+ return {
642
+ closed: false,
643
+ conflictIndex: index,
644
+ conflictType,
645
+ endIndex: index,
646
+ text: textLines.map((line) => line.trim()).filter(Boolean).join(' '),
647
+ };
648
+ }
649
+
650
+ const closingMatch = currentLine.match(inlineNoteDeclarationClosingRegex);
651
+ if (closingMatch) {
652
+ textLines.push(closingMatch[1]);
653
+ return {
654
+ closed: true,
655
+ endIndex: index,
656
+ text: textLines.map((line) => line.trim()).filter(Boolean).join(' '),
657
+ };
658
+ }
659
+
660
+ textLines.push(currentLine);
661
+ currentLine = lines[index + 1] ?? '';
662
+ currentMaskedLine = maskedLines[index + 1] ?? '';
663
+ }
664
+
665
+ return {
666
+ closed: false,
667
+ endIndex: lines.length - 1,
668
+ text: textLines.map((line) => line.trim()).filter(Boolean).join(' '),
669
+ };
670
+ };
671
+
672
+ const getUnclosedInlineNoteMessage = (noteDeclaration, startLine, lineOffset = 0) => {
673
+ if (noteDeclaration.conflictType === 'note') {
674
+ const conflictLine = lineOffset + noteDeclaration.conflictIndex + 1;
675
+ return `The note starting on line ${startLine} is not closed before another note starts on line ${conflictLine}. Close the first note with "}".`;
676
+ }
677
+
678
+ if (noteDeclaration.conflictType === 'reference') {
679
+ const conflictLine = lineOffset + noteDeclaration.conflictIndex + 1;
680
+ return `The note starting on line ${startLine} contains "{note-ref}" on line ${conflictLine}. Close the note before adding a note reference.`;
681
+ }
682
+
683
+ return `The note starting on line ${startLine} is not closed. End it with "}" on its own line or at the end of the note text.`;
684
+ };
609
685
 
610
686
  /**
611
687
  * Finds the deliberately small inline-note syntax while ignoring fenced and
@@ -640,8 +716,7 @@ export const extractInlineNoteDiagnostics = (markdown, options = {}) => {
640
716
  pendingParagraph = null;
641
717
  };
642
718
 
643
- const attachNote = (noteMatch, lineNumber) => {
644
- const text = noteMatch[1].trim();
719
+ const attachNote = (text, lineNumber) => {
645
720
  if (!text) {
646
721
  addError(`The note on line ${lineNumber} is empty. ${getInlineNoteErrorMessage()}`, lineNumber);
647
722
  pendingParagraph = null;
@@ -686,7 +761,10 @@ export const extractInlineNoteDiagnostics = (markdown, options = {}) => {
686
761
 
687
762
  const trimmed = line.trim();
688
763
  const maskedTrimmed = maskedLine.trim();
689
- const noteMatch = trimmed.match(inlineNoteDeclarationRegex);
764
+ const startsNoteDeclaration = inlineNoteDeclarationOpeningRegex.test(maskedLine);
765
+ const noteDeclaration = startsNoteDeclaration
766
+ ? collectInlineNoteDeclaration(lines, maskedLines, index)
767
+ : null;
690
768
  const looksLikeNoteDeclaration = inlineNoteDeclarationStartRegex.test(maskedTrimmed);
691
769
  const referenceCount = countInlineNoteReferences(maskedLine);
692
770
  const isHeading = /^#{1,6}(?:\s|$)/.test(trimmed);
@@ -698,8 +776,14 @@ export const extractInlineNoteDiagnostics = (markdown, options = {}) => {
698
776
  }
699
777
 
700
778
  if (pendingParagraph) {
701
- if (noteMatch) {
702
- attachNote(noteMatch, lineNumber);
779
+ if (noteDeclaration) {
780
+ if (!noteDeclaration.closed) {
781
+ addError(getUnclosedInlineNoteMessage(noteDeclaration, lineNumber, options.lineOffset), lineNumber);
782
+ pendingParagraph = null;
783
+ } else {
784
+ attachNote(noteDeclaration.text, lineNumber);
785
+ }
786
+ index = noteDeclaration.endIndex;
703
787
  continue;
704
788
  }
705
789
 
@@ -716,13 +800,16 @@ export const extractInlineNoteDiagnostics = (markdown, options = {}) => {
716
800
  noteJustSeen = false;
717
801
  }
718
802
 
719
- if (noteMatch || looksLikeNoteDeclaration) {
803
+ if (noteDeclaration || looksLikeNoteDeclaration) {
720
804
  addError(
721
- noteMatch
722
- ? `A "{note: ...}" requires a preceding paragraph containing "{note-ref}".`
805
+ noteDeclaration
806
+ ? noteDeclaration.closed
807
+ ? `A "{note: ...}" requires a preceding paragraph containing "{note-ref}".`
808
+ : getUnclosedInlineNoteMessage(noteDeclaration, lineNumber, options.lineOffset)
723
809
  : `Invalid note syntax on line ${lineNumber}. ${getInlineNoteErrorMessage()}`,
724
810
  lineNumber,
725
811
  );
812
+ if (noteDeclaration) index = noteDeclaration.endIndex;
726
813
  continue;
727
814
  }
728
815
 
@@ -2,6 +2,7 @@ import {
2
2
  defaultTypography,
3
3
  resolveTypographyConfig,
4
4
  } from './typography.mjs';
5
+ import { resolveThemeConfig } from './theme-presets.mjs';
5
6
 
6
7
  export const presentationPaletteNames = ['dark', 'light', 'paper'];
7
8
 
@@ -92,8 +93,8 @@ export const getPresentationCssVariables = (presentation) => {
92
93
  };
93
94
  };
94
95
 
95
- export const resolveThemePresentation = (theme) => {
96
- const normalizedTheme = theme ?? {};
96
+ export const resolveThemePresentation = (theme, sourceLabel = 'theme.md') => {
97
+ const normalizedTheme = resolveThemeConfig(theme, sourceLabel);
97
98
  const normalizedThemePresentation = normalizePresentation(normalizedTheme.presentation);
98
99
  const paletteName = normalizedThemePresentation.palette ?? 'dark';
99
100
  const palette = getPresentationPalette(paletteName);
@@ -109,8 +110,8 @@ export const resolveThemePresentation = (theme) => {
109
110
  };
110
111
  };
111
112
 
112
- export const resolvePagePresentation = (theme) => {
113
- const resolvedThemePresentation = resolveThemePresentation(theme);
113
+ export const resolvePagePresentation = (theme, sourceLabel) => {
114
+ const resolvedThemePresentation = resolveThemePresentation(theme, sourceLabel);
114
115
 
115
116
  return resolvedThemePresentation;
116
117
  };
@@ -126,4 +127,4 @@ export const resolveSectionSurface = (pagePresentation, sectionIndex) => {
126
127
  };
127
128
  };
128
129
 
129
- export const resolveFrameColors = ({ theme }) => resolveThemePresentation(theme).palette.frame;
130
+ export const resolveFrameColors = ({ theme, sourceLabel }) => resolveThemePresentation(theme, sourceLabel).palette.frame;
@@ -1,8 +1,55 @@
1
- import { pathToFileURL } from 'node:url';
2
- import { siteConfigLabel, siteConfigPath, siteThemeLabel } from './site-paths.mjs';
1
+ import { access, readFile } from 'node:fs/promises';
2
+ import { parseYamlMapping } from './frontmatter-yaml.mjs';
3
+ import {
4
+ splitSiteFile,
5
+ validateConfigFrontmatterStructure,
6
+ validateFrontmatterIndentation,
7
+ } from './site-content.mjs';
8
+ import {
9
+ legacySiteConfigPath,
10
+ siteConfigLabel,
11
+ siteConfigPath,
12
+ siteThemeLabel,
13
+ } from './site-paths.mjs';
3
14
  import { readThemeConfig } from './theme-config.mjs';
15
+ import { resolveThemeConfig } from './theme-presets.mjs';
4
16
 
5
- const { default: siteConfig } = await import(/* @vite-ignore */ pathToFileURL(siteConfigPath).href);
17
+ const fileExists = (filePath) => access(filePath).then(() => true, () => false);
18
+ const readSiteConfig = async () => {
19
+ const source = await readFile(siteConfigPath, 'utf8').catch(async (error) => {
20
+ if (error?.code === 'ENOENT' && await fileExists(legacySiteConfigPath)) {
21
+ throw new Error(`${siteConfigLabel} is required. Replace the obsolete config.mjs with a frontmatter-only config.md file.`);
22
+ }
23
+
24
+ if (error?.code === 'ENOENT') {
25
+ throw new Error(`${siteConfigLabel} is required. Create it before running Norna.`);
26
+ }
27
+
28
+ throw error;
29
+ });
30
+ const { body, frontmatter, frontmatterBody } = splitSiteFile(source, siteConfigLabel);
31
+ const issues = [];
32
+
33
+ validateFrontmatterIndentation(frontmatter, (issue) => issues.push(issue));
34
+ validateConfigFrontmatterStructure(frontmatter, (issue) => issues.push(issue));
35
+
36
+ if (body.trim()) {
37
+ issues.push({
38
+ message: `${siteConfigLabel} may contain YAML frontmatter only. Remove the Markdown body.`,
39
+ });
40
+ }
41
+
42
+ if (issues.length > 0) {
43
+ throw new Error([
44
+ `${siteConfigLabel} has invalid frontmatter.`,
45
+ ...issues.map((issue) => `- ${issue.message}`),
46
+ ].join('\n'));
47
+ }
48
+
49
+ return parseYamlMapping(frontmatterBody);
50
+ };
51
+
52
+ const siteConfig = await readSiteConfig();
6
53
  const themeConfig = await readThemeConfig();
7
54
 
8
55
  const cssLengthPattern = String.raw`(?:\d+|\d*\.\d+)(?:px|rem|em|vw|vh|vmin|vmax|ch|%)`;
@@ -27,16 +74,6 @@ const readEnum = (object, key, path, allowedValues, fallback, sourceLabel = site
27
74
  return value;
28
75
  };
29
76
 
30
- const readString = (object, key, path, sourceLabel = siteConfigLabel) => {
31
- const value = object[key];
32
-
33
- if (typeof value !== 'string' || value.trim() === '') {
34
- throw new Error(`${path}.${key} must be a non-empty string in ${sourceLabel}.`);
35
- }
36
-
37
- return value.trim();
38
- };
39
-
40
77
  const readFontFamily = (object, key, path, fallback, sourceLabel = siteConfigLabel) => {
41
78
  const value = object[key] ?? fallback;
42
79
 
@@ -137,123 +174,82 @@ const readResponsivePercent = (object, key, path, fallback, sourceLabel = siteCo
137
174
  });
138
175
  };
139
176
 
140
- const readBoolean = (object, key, path, fallback) => {
141
- const value = object[key] ?? fallback;
142
-
143
- if (typeof value !== 'boolean') {
144
- throw new Error(`${path}.${key} must be a boolean in ${siteConfigLabel}.`);
145
- }
146
-
147
- return value;
148
- };
149
-
150
- const readPositiveInteger = (object, key, path, fallback) => {
151
- const value = object[key] ?? fallback;
177
+ const readSiteUrl = (config) => {
178
+ const configuredValue = process.env.NORNA_SITE_URL ?? config.url;
152
179
 
153
- if (!Number.isInteger(value) || value <= 0) {
154
- throw new Error(`${path}.${key} must be a positive integer in ${siteConfigLabel}.`);
180
+ if (typeof configuredValue !== 'string' || configuredValue.trim() === '') {
181
+ throw new Error(`url must be a non-empty absolute URL in ${siteConfigLabel}.`);
155
182
  }
156
183
 
157
- return value;
158
- };
159
-
160
- const readPositiveNumber = (object, key, path, fallback) => {
161
- const value = object[key] ?? fallback;
162
-
163
- if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
164
- throw new Error(`${path}.${key} must be a positive number in ${siteConfigLabel}.`);
165
- }
166
-
167
- return value;
168
- };
169
-
170
- const readUrl = (object, key, path) => {
171
- const value = readString(object, key, path);
172
-
184
+ let url;
173
185
  try {
174
- return new URL(value).href;
186
+ url = new URL(configuredValue.trim());
175
187
  } catch {
176
- throw new Error(`${path}.${key} must be an absolute URL in ${siteConfigLabel}.`);
188
+ throw new Error(`url must be an absolute URL such as "https://example.com/" in ${siteConfigLabel}.`);
177
189
  }
178
- };
179
190
 
180
- const readBasePath = (object, key, path) => {
181
- const value = object[key] ?? '/';
182
-
183
- if (typeof value !== 'string' || value.trim() === '') {
184
- throw new Error(`${path}.${key} must be a non-empty URL path in ${siteConfigLabel}.`);
191
+ if (!['http:', 'https:'].includes(url.protocol)) {
192
+ throw new Error(`url must use http or https in ${siteConfigLabel}.`);
185
193
  }
186
-
187
- const normalizedValue = value.trim();
188
-
189
- if (
190
- !normalizedValue.startsWith('/')
191
- || !normalizedValue.endsWith('/')
192
- || normalizedValue.includes('//')
193
- || /[\s?#]/.test(normalizedValue)
194
- ) {
195
- throw new Error(`${path}.${key} must start and end with "/" and must not contain whitespace, "?", "#", or "//" in ${siteConfigLabel}.`);
194
+ if (url.search || url.hash) {
195
+ throw new Error(`url must not contain a query string or fragment in ${siteConfigLabel}.`);
196
+ }
197
+ if (!url.pathname.endsWith('/')) {
198
+ url.pathname = `${url.pathname}/`;
199
+ }
200
+ if (url.pathname.includes('//')) {
201
+ throw new Error(`url must not contain repeated slashes in its path in ${siteConfigLabel}.`);
196
202
  }
197
203
 
198
- return normalizedValue;
204
+ return url;
199
205
  };
200
206
 
201
- const readSmoothScroll = (navigation) => {
202
- const rawSmoothScroll = assertObject(navigation.smoothScroll ?? {}, 'navigation.smoothScroll');
203
- const minimumDurationMs = readPositiveInteger(rawSmoothScroll, 'minimumDurationMs', 'navigation.smoothScroll', 2_000);
204
- const maximumDurationMs = readPositiveInteger(rawSmoothScroll, 'maximumDurationMs', 'navigation.smoothScroll', 4_000);
207
+ const localeLabels = Object.freeze({
208
+ en: Object.freeze({
209
+ dismissBanner: 'Dismiss notice',
210
+ gallery: 'Images',
211
+ note: 'Note',
212
+ pageNavigation: 'On this page',
213
+ siteBanners: 'Site notices',
214
+ siteNavigation: 'Pages',
215
+ skipToContent: 'Skip to content',
216
+ }),
217
+ sv: Object.freeze({
218
+ dismissBanner: 'Stäng meddelande',
219
+ gallery: 'Bilder',
220
+ note: 'Not',
221
+ pageNavigation: 'På den här sidan',
222
+ siteBanners: 'Meddelanden',
223
+ siteNavigation: 'Sidor',
224
+ skipToContent: 'Hoppa till innehållet',
225
+ }),
226
+ });
205
227
 
206
- if (maximumDurationMs < minimumDurationMs) {
207
- throw new Error(`navigation.smoothScroll.maximumDurationMs must be greater than or equal to minimumDurationMs in ${siteConfigLabel}.`);
208
- }
228
+ const readLocale = (config) => {
229
+ const lang = config.language ?? 'en';
209
230
 
210
- return Object.freeze({
211
- durationPerPixelMs: readPositiveNumber(rawSmoothScroll, 'durationPerPixelMs', 'navigation.smoothScroll', 0.22),
212
- enabled: readBoolean(rawSmoothScroll, 'enabled', 'navigation.smoothScroll', true),
213
- maximumDurationMs,
214
- minimumDurationMs,
215
- });
216
- };
231
+ if (typeof lang !== 'string' || !/^[a-zA-Z]{2,3}(?:-[a-zA-Z0-9]+)*$/.test(lang.trim())) {
232
+ throw new Error(`language must be a valid language tag such as "en" or "sv" in ${siteConfigLabel}.`);
233
+ }
217
234
 
218
- const readLocale = (rawLocale) => {
219
- const locale = assertObject(rawLocale ?? {}, 'locale');
220
- const labels = assertObject(locale.labels ?? {}, 'locale.labels');
221
- const lang = locale.lang ?? 'en';
235
+ const normalizedLang = lang.trim();
236
+ const languageKey = normalizedLang.toLowerCase().split('-')[0];
237
+ const labels = localeLabels[languageKey];
222
238
 
223
- if (typeof lang !== 'string' || !/^[a-zA-Z]{2,3}(?:-[a-zA-Z0-9]+)*$/.test(lang.trim())) {
224
- throw new Error(`locale.lang must be a valid language tag such as "en" or "sv" in ${siteConfigLabel}.`);
239
+ if (!labels) {
240
+ throw new Error(`language "${normalizedLang}" has no built-in Norna UI text. Supported languages: ${Object.keys(localeLabels).join(', ')}.`);
225
241
  }
226
242
 
227
243
  return Object.freeze({
228
- lang: lang.trim(),
229
- labels: Object.freeze({
230
- closeMenu: readString({ closeMenu: labels.closeMenu ?? 'Close menu' }, 'closeMenu', 'locale.labels'),
231
- dismissBanner: readString({ dismissBanner: labels.dismissBanner ?? 'Dismiss notice' }, 'dismissBanner', 'locale.labels'),
232
- skipToContent: readString({ skipToContent: labels.skipToContent ?? 'Skip to content' }, 'skipToContent', 'locale.labels'),
233
- sectionNavigation: readString({ sectionNavigation: labels.sectionNavigation ?? 'Sections' }, 'sectionNavigation', 'locale.labels'),
234
- gallery: readString({ gallery: labels.gallery ?? 'Images' }, 'gallery', 'locale.labels'),
235
- note: readString({ note: labels.note ?? 'Note' }, 'note', 'locale.labels'),
236
- menu: readString({ menu: labels.menu ?? 'Menu' }, 'menu', 'locale.labels'),
237
- pageNavigation: readString({ pageNavigation: labels.pageNavigation ?? 'On this page' }, 'pageNavigation', 'locale.labels'),
238
- siteNavigation: readString({ siteNavigation: labels.siteNavigation ?? 'Pages' }, 'siteNavigation', 'locale.labels'),
239
- siteBanners: readString({ siteBanners: labels.siteBanners ?? 'Site notices' }, 'siteBanners', 'locale.labels'),
240
- }),
244
+ lang: normalizedLang,
245
+ labels,
241
246
  });
242
247
  };
243
248
 
244
- const rawConfig = assertObject(siteConfig, 'default export');
245
- const misplacedThemeKeys = ['layout', 'gallery', 'typography'].filter((key) => key in rawConfig);
246
- if (misplacedThemeKeys.length > 0) {
247
- throw new Error(`${misplacedThemeKeys.join(', ')} belong in ${siteThemeLabel}, not ${siteConfigLabel}.`);
248
- }
249
-
249
+ const rawConfig = assertObject(siteConfig, 'config frontmatter');
250
250
  const rawTheme = assertObject(themeConfig, 'theme frontmatter', siteThemeLabel);
251
- const rawSite = assertObject(rawConfig.site, 'site');
252
- const rawNavigation = assertObject(rawConfig.navigation ?? {}, 'navigation');
253
- const rawLocale = rawConfig.locale ?? {};
254
- const rawGithub = assertObject(rawConfig.github, 'github');
255
- const rawDeploy = assertObject(rawConfig.deploy ?? {}, 'deploy');
256
- const rawDeployWatch = assertObject(rawDeploy.watch ?? {}, 'deploy.watch');
251
+ const siteUrl = readSiteUrl(rawConfig);
252
+ const scrollBehaviorNames = ['instant', 'browser-smooth', 'norna-smooth'];
257
253
 
258
254
  const defaultFontFamily = "Arial, 'Helvetica Neue', Helvetica, sans-serif";
259
255
  const layoutDensityNames = ['compact', 'normal', 'airy'];
@@ -338,7 +334,7 @@ const layoutDensityProfiles = Object.freeze({
338
334
  }),
339
335
  });
340
336
  export const resolveThemeVisualConfig = (theme, sourceLabel = siteThemeLabel) => {
341
- const rawThemeConfig = assertObject(theme ?? {}, 'theme frontmatter', sourceLabel);
337
+ const rawThemeConfig = assertObject(resolveThemeConfig(theme, sourceLabel), 'theme frontmatter', sourceLabel);
342
338
  const rawLayoutConfig = assertObject(rawThemeConfig.layout ?? {}, 'layout', sourceLabel);
343
339
  const rawLayoutSpacingConfig = assertObject(rawLayoutConfig.spacing ?? {}, 'layout.spacing', sourceLabel);
344
340
  const rawGalleryConfig = assertObject(rawThemeConfig.gallery ?? {}, 'gallery', sourceLabel);
@@ -382,26 +378,20 @@ export const resolveThemeVisualConfig = (theme, sourceLabel = siteThemeLabel) =>
382
378
 
383
379
  export const projectConfig = Object.freeze({
384
380
  site: Object.freeze({
385
- basePath: readBasePath(rawSite, 'basePath', 'site'),
386
- url: readUrl(rawSite, 'url', 'site'),
381
+ basePath: siteUrl.pathname,
382
+ url: siteUrl.href,
387
383
  }),
388
384
  ...resolveThemeVisualConfig(rawTheme, siteThemeLabel),
389
385
  navigation: Object.freeze({
390
- smoothScroll: readSmoothScroll(rawNavigation),
391
- }),
392
- locale: readLocale(rawLocale),
393
- github: Object.freeze({
394
- repo: readString(rawGithub, 'repo', 'github'),
395
- branch: readString(rawGithub, 'branch', 'github'),
396
- pagesWorkflow: readString(rawGithub, 'pagesWorkflow', 'github'),
397
- }),
398
- deploy: Object.freeze({
399
- watch: Object.freeze({
400
- intervalMs: readPositiveInteger(rawDeployWatch, 'intervalMs', 'deploy.watch', 10_000),
401
- timeoutMs: readPositiveInteger(rawDeployWatch, 'timeoutMs', 'deploy.watch', 15 * 60_000),
402
- runLimit: readPositiveInteger(rawDeployWatch, 'runLimit', 'deploy.watch', 10),
403
- }),
386
+ scrollBehavior: readEnum(
387
+ rawConfig,
388
+ 'scrollBehavior',
389
+ 'config frontmatter',
390
+ scrollBehaviorNames,
391
+ 'instant',
392
+ ),
404
393
  }),
394
+ locale: readLocale(rawConfig),
405
395
  });
406
396
 
407
397
  export default projectConfig;