@janga/norna 0.7.10 → 0.7.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/README.md +4 -2
  2. package/bin/norna-cli.mjs +3 -0
  3. package/docs/README.md +3 -2
  4. package/docs/commands.md +6 -0
  5. package/docs/configuration.md +1 -0
  6. package/docs/content.md +14 -14
  7. package/docs/design/site-examples-structure.md +35 -24
  8. package/docs/engine-development.md +13 -7
  9. package/docs/getting-started.md +44 -9
  10. package/docs/routes.md +2 -1
  11. package/docs/site-structure.md +8 -5
  12. package/docs/theme.md +86 -34
  13. package/docs/typography.md +32 -17
  14. package/examples/README.md +41 -0
  15. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/collections/sitewide.schema.json +24 -0
  16. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/collections/theme.schema.json +9 -24
  17. package/examples/complete-sites/dog-shelter-multi-page/.astro/data-store.json +1 -0
  18. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/dev-local.json +2 -2
  19. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/dev.json +2 -2
  20. package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.log +53 -0
  21. package/examples/complete-sites/dog-shelter-multi-page/.astro/settings.json +5 -0
  22. package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-multi-page}/README.md +1 -1
  23. package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/_metadata.json +13 -7
  24. package/examples/complete-sites/dog-shelter-multi-page/site/.norna/generated-images.json +18 -0
  25. package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-multi-page}/site/config.mjs +5 -2
  26. package/examples/{001-dog-shelter-single-page → complete-sites/dog-shelter-single-page}/README.md +1 -1
  27. package/examples/complete-sites/dog-shelter-single-page/site/.norna/generated-images.json +18 -0
  28. package/examples/{001-dog-shelter-single-page → complete-sites/dog-shelter-single-page}/site/config.mjs +5 -2
  29. package/examples/feature-demos/media-and-surfaces/.astro/collections/site.schema.json +59 -0
  30. package/examples/feature-demos/media-and-surfaces/.astro/collections/sitewide.schema.json +138 -0
  31. package/examples/{dog-gallery → feature-demos/media-and-surfaces}/.astro/collections/theme.schema.json +151 -68
  32. package/examples/feature-demos/media-and-surfaces/.astro/content-modules.mjs +1 -0
  33. package/examples/feature-demos/media-and-surfaces/.astro/content.d.ts +183 -0
  34. package/examples/feature-demos/media-and-surfaces/.astro/data-store.json +1 -0
  35. package/examples/feature-demos/media-and-surfaces/.astro/dev.log +11 -0
  36. package/examples/feature-demos/media-and-surfaces/.astro/settings.json +5 -0
  37. package/examples/feature-demos/media-and-surfaces/README.md +15 -0
  38. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/_metadata.json +74 -0
  39. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro-B3JHJyiv.js +434 -0
  40. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro-B3JHJyiv.js.map +1 -0
  41. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
  42. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  43. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
  44. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  45. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
  46. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  47. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
  48. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  49. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/audit-C3SVr7dT.js +1534 -0
  50. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/audit-C3SVr7dT.js.map +1 -0
  51. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/highlight-BLtvqtzO.js +48 -0
  52. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +1 -0
  53. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/icons-C92i61-2.js +42 -0
  54. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/icons-C92i61-2.js.map +1 -0
  55. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/settings-v5GgKks_.js +190 -0
  56. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/settings-v5GgKks_.js.map +1 -0
  57. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/toolbar-BFvJVp26.js +458 -0
  58. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +1 -0
  59. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/ui-library-Cd35oAvo.js +1016 -0
  60. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +1 -0
  61. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/window-vo3A6TMn.js +32 -0
  62. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/window-vo3A6TMn.js.map +1 -0
  63. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/xray-DwhzGckb.js +111 -0
  64. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/xray-DwhzGckb.js.map +1 -0
  65. package/examples/{presentation → feature-demos/media-and-surfaces}/site/.norna/generated-images.json +16 -16
  66. package/examples/{presentation → feature-demos/media-and-surfaces}/site/config.mjs +4 -3
  67. package/examples/{presentation → feature-demos/media-and-surfaces}/site/content.md +12 -15
  68. package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/route-content.md +11 -3
  69. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/theme.md +3 -0
  70. package/examples/{presentation/site/routes/030-surfaces → feature-demos/media-and-surfaces/site/routes/020-surfaces}/theme.md +1 -0
  71. package/examples/feature-demos/media-and-surfaces/site/sitewide-content.md +6 -0
  72. package/examples/feature-demos/media-and-surfaces/site/theme.md +3 -0
  73. package/examples/feature-demos/theme-presets/.astro/collections/site.schema.json +59 -0
  74. package/examples/feature-demos/theme-presets/.astro/collections/sitewide.schema.json +138 -0
  75. package/examples/feature-demos/theme-presets/.astro/collections/theme.schema.json +755 -0
  76. package/examples/feature-demos/theme-presets/.astro/content-modules.mjs +1 -0
  77. package/examples/{dog-gallery → feature-demos/theme-presets}/.astro/content.d.ts +8 -0
  78. package/examples/{typography-presets → feature-demos/theme-presets}/.astro/data-store.json +1 -1
  79. package/examples/feature-demos/theme-presets/.astro/dev-local.json +8 -0
  80. package/examples/feature-demos/theme-presets/.astro/dev.json +15 -0
  81. package/examples/feature-demos/theme-presets/.astro/dev.log +127 -0
  82. package/examples/feature-demos/theme-presets/.astro/types.d.ts +2 -0
  83. package/examples/feature-demos/theme-presets/README.md +14 -0
  84. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/embla-carousel.js +1389 -0
  85. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/embla-carousel.js.map +1 -0
  86. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/package.json +3 -0
  87. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
  88. package/examples/{typography-presets → feature-demos/theme-presets}/site/.norna/generated-images.json +45 -45
  89. package/examples/{typography-presets → feature-demos/theme-presets}/site/config.mjs +4 -3
  90. package/examples/feature-demos/theme-presets/site/content.md +17 -0
  91. package/examples/{typography-presets/site/routes/010-quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio}/route-content.md +8 -8
  92. package/examples/feature-demos/theme-presets/site/routes/010-portfolio/theme.md +3 -0
  93. package/examples/{typography-presets/site/routes/020-compact-gallery → feature-demos/theme-presets/site/routes/020-documentation}/route-content.md +8 -8
  94. package/examples/feature-demos/theme-presets/site/routes/020-documentation/theme.md +3 -0
  95. package/examples/{typography-presets/site/routes/030-text-forward → feature-demos/theme-presets/site/routes/030-project}/route-content.md +8 -8
  96. package/examples/feature-demos/theme-presets/site/routes/030-project/theme.md +3 -0
  97. package/examples/{typography-presets → feature-demos/theme-presets}/site/routes/040-statement/route-content.md +3 -3
  98. package/examples/feature-demos/theme-presets/site/routes/040-statement/theme.md +3 -0
  99. package/examples/{typography-presets → feature-demos/theme-presets}/site/sitewide-content.md +1 -1
  100. package/examples/feature-demos/theme-presets/site/theme.md +3 -0
  101. package/package.json +7 -3
  102. package/scripts/build-pages.mjs +71 -0
  103. package/scripts/check-config.mjs +2 -1
  104. package/scripts/export-theme-preset.mjs +31 -0
  105. package/scripts/init-site.mjs +1 -0
  106. package/scripts/lib/example-sites.mjs +34 -0
  107. package/scripts/lib/presentation.mjs +6 -5
  108. package/scripts/lib/project-config.mjs +2 -1
  109. package/scripts/lib/site-content.mjs +2 -2
  110. package/scripts/lib/theme-config.mjs +3 -0
  111. package/scripts/lib/theme-presets.mjs +209 -0
  112. package/scripts/show-typography.mjs +6 -5
  113. package/scripts/test-engine-commands.mjs +1 -0
  114. package/scripts/test-examples.mjs +15 -0
  115. package/scripts/test-navigation.mjs +73 -45
  116. package/scripts/test-theme-presets.mjs +151 -0
  117. package/src/components/SitePage.astro +2 -2
  118. package/src/content.config.ts +2 -0
  119. package/src/styles/global.css +2 -0
  120. package/starters/basic/package.json +1 -0
  121. package/starters/project/package.json +1 -0
  122. package/examples/dog-gallery/.astro/collections/site.schema.json +0 -776
  123. package/examples/dog-gallery/.astro/data-store.json +0 -1
  124. package/examples/dog-gallery/.astro/dev-local.json +0 -8
  125. package/examples/dog-gallery/.astro/dev.json +0 -13
  126. package/examples/dog-gallery/.astro/dev.log +0 -95
  127. package/examples/dog-gallery/.astro/settings.json +0 -5
  128. package/examples/dog-gallery/dist/_astro/GalleryGrid.astro_astro_type_script_index_0_lang.B0kxDyGz.js +0 -1
  129. package/examples/dog-gallery/dist/_astro/SitePage.DicGh_5N.css +0 -1
  130. package/examples/dog-gallery/dist/dog-care/index.html +0 -15
  131. package/examples/dog-gallery/dist/dog-shelter-logo.svg +0 -14
  132. package/examples/dog-gallery/dist/favicon.svg +0 -7
  133. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1080.webp +0 -0
  134. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1440.webp +0 -0
  135. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1536.webp +0 -0
  136. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-480.webp +0 -0
  137. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-768.webp +0 -0
  138. package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-480.webp +0 -0
  139. package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-768.webp +0 -0
  140. package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-960.webp +0 -0
  141. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1080.webp +0 -0
  142. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1440.webp +0 -0
  143. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1920.webp +0 -0
  144. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-3939.webp +0 -0
  145. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-480.webp +0 -0
  146. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-768.webp +0 -0
  147. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1080.webp +0 -0
  148. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1440.webp +0 -0
  149. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1920.webp +0 -0
  150. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-2400.webp +0 -0
  151. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-480.webp +0 -0
  152. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-768.webp +0 -0
  153. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1080.webp +0 -0
  154. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1440.webp +0 -0
  155. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1600.webp +0 -0
  156. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-480.webp +0 -0
  157. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-768.webp +0 -0
  158. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1080.webp +0 -0
  159. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1440.webp +0 -0
  160. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1920.webp +0 -0
  161. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-2576.webp +0 -0
  162. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-480.webp +0 -0
  163. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-768.webp +0 -0
  164. package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-1080.webp +0 -0
  165. package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-1277.webp +0 -0
  166. package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-480.webp +0 -0
  167. package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-768.webp +0 -0
  168. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1080.webp +0 -0
  169. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1440.webp +0 -0
  170. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1536.webp +0 -0
  171. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-480.webp +0 -0
  172. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-768.webp +0 -0
  173. package/examples/dog-gallery/dist/index.html +0 -45
  174. package/examples/dog-gallery/site/.norna/generated-images.json +0 -226
  175. package/examples/dog-gallery/site/config.mjs +0 -47
  176. package/examples/dog-gallery/site/content.md +0 -127
  177. package/examples/dog-gallery/site/images/black-dogs/black-puppy-meadow.png +0 -0
  178. package/examples/dog-gallery/site/images/black-dogs/photo-of-a-black-dog.jpg +0 -0
  179. package/examples/dog-gallery/site/images/brown-dogs/brown-dog.jpg +0 -0
  180. package/examples/dog-gallery/site/images/brown-dogs/dog-accompanies-master.jpg +0 -0
  181. package/examples/dog-gallery/site/images/golden-dogs/golden-retriever.jpg +0 -0
  182. package/examples/dog-gallery/site/images/golden-dogs/toller-puppy.jpg +0 -0
  183. package/examples/dog-gallery/site/images/white-dogs/white-cute-dog.jpg +0 -0
  184. package/examples/dog-gallery/site/images/white-dogs/white-puppy-garden.png +0 -0
  185. package/examples/dog-gallery/site/public/favicon.svg +0 -7
  186. package/examples/dog-gallery/site/public/logo.svg +0 -14
  187. package/examples/dog-gallery/site/public/robots.txt +0 -2
  188. package/examples/dog-gallery/site/routes/010-dog-care/route-content.md +0 -38
  189. package/examples/dog-gallery/site/sitewide-content.md +0 -16
  190. package/examples/dog-gallery/site/theme.md +0 -101
  191. package/examples/presentation/README.md +0 -15
  192. package/examples/presentation/site/public/robots.txt +0 -2
  193. package/examples/presentation/site/routes/010-presets/route-content.md +0 -63
  194. package/examples/presentation/site/routes/010-presets/theme.md +0 -4
  195. package/examples/presentation/site/routes/020-media/theme.md +0 -4
  196. package/examples/presentation/site/sitewide-content.md +0 -6
  197. package/examples/presentation/site/theme.md +0 -43
  198. package/examples/typography-presets/.astro/dev.log +0 -45
  199. package/examples/typography-presets/site/content.md +0 -16
  200. package/examples/typography-presets/site/routes/010-quiet-gallery/theme.md +0 -4
  201. package/examples/typography-presets/site/routes/020-compact-gallery/theme.md +0 -4
  202. package/examples/typography-presets/site/routes/030-text-forward/theme.md +0 -4
  203. package/examples/typography-presets/site/routes/040-statement/theme.md +0 -4
  204. package/examples/typography-presets/site/theme.md +0 -43
  205. /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/collections/site.schema.json +0 -0
  206. /package/examples/{dog-gallery/.astro/content-modules.mjs → complete-sites/dog-shelter-multi-page/.astro/content-assets.mjs} +0 -0
  207. /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/content-modules.mjs +0 -0
  208. /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/content.d.ts +0 -0
  209. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/.astro/types.d.ts +0 -0
  210. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -0
  211. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -0
  212. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_aria-query.js +0 -0
  213. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -0
  214. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_axobject-query.js +0 -0
  215. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -0
  216. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_html-escaper.js +0 -0
  217. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -0
  218. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -0
  219. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -0
  220. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -0
  221. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -0
  222. /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/embla-carousel.js +0 -0
  223. /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/embla-carousel.js.map +0 -0
  224. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -0
  225. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -0
  226. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/icons-RyebKi45.js +0 -0
  227. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -0
  228. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/package.json +0 -0
  229. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -0
  230. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -0
  231. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -0
  232. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/toolbar-63PA619H.js +0 -0
  233. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -0
  234. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -0
  235. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -0
  236. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/window-jPkpKVcy.js +0 -0
  237. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -0
  238. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -0
  239. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -0
  240. /package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-multi-page}/site/content.md +0 -0
  241. /package/examples/{001-dog-shelter-single-page → complete-sites/dog-shelter-multi-page}/site/public/favicon.svg +0 -0
  242. /package/examples/{001-dog-shelter-single-page → complete-sites/dog-shelter-multi-page}/site/public/robots.txt +0 -0
  243. /package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-multi-page}/site/routes/010-dogs/images/dogs/rex.svg +0 -0
  244. /package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-multi-page}/site/routes/010-dogs/images/dogs/rover.svg +0 -0
  245. /package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-multi-page}/site/routes/010-dogs/route-content.md +0 -0
  246. /package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-multi-page}/site/routes/020-adopt/route-content.md +0 -0
  247. /package/examples/{001-dog-shelter-single-page → complete-sites/dog-shelter-multi-page}/site/sitewide-content.md +0 -0
  248. /package/examples/{001-dog-shelter-single-page → complete-sites/dog-shelter-multi-page}/site/theme.md +0 -0
  249. /package/examples/{001-dog-shelter-single-page → complete-sites/dog-shelter-single-page}/site/content.md +0 -0
  250. /package/examples/{001-dog-shelter-single-page → complete-sites/dog-shelter-single-page}/site/images/what-we-do/dog-house.svg +0 -0
  251. /package/examples/{001-dog-shelter-single-page → complete-sites/dog-shelter-single-page}/site/images/you-can-help/heart.svg +0 -0
  252. /package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-single-page}/site/public/favicon.svg +0 -0
  253. /package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-single-page}/site/public/robots.txt +0 -0
  254. /package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-single-page}/site/sitewide-content.md +0 -0
  255. /package/examples/{002-dog-shelter-multi-page → complete-sites/dog-shelter-single-page}/site/theme.md +0 -0
  256. /package/examples/{dog-gallery → feature-demos/media-and-surfaces}/.astro/content-assets.mjs +0 -0
  257. /package/examples/{typography-presets → feature-demos/media-and-surfaces}/.astro/types.d.ts +0 -0
  258. /package/examples/{typography-presets → feature-demos/media-and-surfaces}/node_modules/.vite/deps/package.json +0 -0
  259. /package/examples/{typography-presets → feature-demos/media-and-surfaces}/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -0
  260. /package/examples/{dog-gallery/dist → feature-demos/media-and-surfaces/site/public}/robots.txt +0 -0
  261. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/cards/card-carousel.svg +0 -0
  262. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/cards/card-stack.svg +0 -0
  263. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/cards/card-surfaces.svg +0 -0
  264. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/carousel/carousel-one.svg +0 -0
  265. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/carousel/carousel-three.svg +0 -0
  266. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/carousel/carousel-two.svg +0 -0
  267. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/stack/stack-one.svg +0 -0
  268. /package/examples/{presentation/site/routes/020-media → feature-demos/media-and-surfaces/site/routes/010-media}/images/stack/stack-two.svg +0 -0
  269. /package/examples/{presentation/site/routes/030-surfaces → feature-demos/media-and-surfaces/site/routes/020-surfaces}/route-content.md +0 -0
  270. /package/examples/{typography-presets → feature-demos/theme-presets}/.astro/collections/routeTheme.schema.json +0 -0
  271. /package/examples/{typography-presets → feature-demos/theme-presets}/.astro/content-assets.mjs +0 -0
  272. /package/examples/{typography-presets → feature-demos/theme-presets}/.astro/settings.json +0 -0
  273. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/_metadata.json +0 -0
  274. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -0
  275. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -0
  276. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_aria-query.js +0 -0
  277. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -0
  278. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_axobject-query.js +0 -0
  279. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -0
  280. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_html-escaper.js +0 -0
  281. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -0
  282. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -0
  283. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -0
  284. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -0
  285. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -0
  286. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -0
  287. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -0
  288. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/icons-RyebKi45.js +0 -0
  289. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -0
  290. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -0
  291. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -0
  292. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/toolbar-63PA619H.js +0 -0
  293. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -0
  294. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -0
  295. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -0
  296. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/window-jPkpKVcy.js +0 -0
  297. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -0
  298. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -0
  299. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -0
  300. /package/examples/{typography-presets → feature-demos/theme-presets}/site/public/robots.txt +0 -0
  301. /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
  302. /package/examples/{typography-presets/site/routes/010-quiet-gallery/images/quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio}/shepherd-portrait.jpg +0 -0
  303. /package/examples/{typography-presets/site/routes/010-quiet-gallery/images/quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio}/white-puppy.jpg +0 -0
  304. /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
  305. /package/examples/{typography-presets/site/routes/020-compact-gallery/images/compact-gallery → feature-demos/theme-presets/site/routes/020-documentation/images/documentation}/retriever-portrait.jpg +0 -0
  306. /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
  307. /package/examples/{typography-presets/site/routes/030-text-forward/images/text-forward → feature-demos/theme-presets/site/routes/030-project/images/project}/boxer-portrait.jpg +0 -0
  308. /package/examples/{typography-presets/site/routes/030-text-forward/images/text-forward → feature-demos/theme-presets/site/routes/030-project/images/project}/grass-puppy.jpg +0 -0
  309. /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
  310. /package/examples/{typography-presets → feature-demos/theme-presets}/site/routes/040-statement/images/statement/black-pedigree.jpg +0 -0
  311. /package/examples/{typography-presets → feature-demos/theme-presets}/site/routes/040-statement/images/statement/sheepdog-grass.jpg +0 -0
  312. /package/examples/{typography-presets → feature-demos/theme-presets}/site/routes/040-statement/images/statement/sheepdog-wide.jpg +0 -0
@@ -1,15 +1,12 @@
1
- import { execFile, spawn } from 'node:child_process';
1
+ import { spawn } from 'node:child_process';
2
+ import net from 'node:net';
2
3
  import path from 'node:path';
3
4
  import { fileURLToPath } from 'node:url';
4
- import { promisify } from 'node:util';
5
-
6
- const execFileAsync = promisify(execFile);
7
5
 
8
6
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
9
7
  const cliPath = path.join(root, 'bin', 'norna.mjs');
10
- const dogGallerySiteDir = path.join(root, 'examples', 'dog-gallery', 'site');
11
- const url = 'http://localhost:4321/';
12
- const probeUrls = [url, 'http://127.0.0.1:4321/', 'http://[::1]:4321/'];
8
+ const navigationDemoSiteDir = path.join(root, 'examples', 'feature-demos', 'media-and-surfaces', 'site');
9
+ const host = '127.0.0.1';
13
10
  const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
14
11
  const testTargets = process.argv.slice(2);
15
12
  const playwrightTargets = testTargets.length > 0 ? testTargets : ['tests/navigation.spec.ts'];
@@ -18,33 +15,46 @@ const sleep = (milliseconds) => new Promise((resolve) => {
18
15
  setTimeout(resolve, milliseconds);
19
16
  });
20
17
 
21
- const isReachable = async () => {
22
- for (const probeUrl of probeUrls) {
23
- try {
24
- const response = await fetch(probeUrl, { signal: AbortSignal.timeout(1_000) });
25
- await response.arrayBuffer();
26
- return response.ok;
27
- } catch {
28
- // Try the next loopback address.
29
- }
30
- }
18
+ const getAvailablePort = () => new Promise((resolve, reject) => {
19
+ const server = net.createServer();
20
+ server.unref();
21
+ server.once('error', reject);
22
+ server.listen(0, host, () => {
23
+ const address = server.address();
24
+ const port = typeof address === 'object' && address ? address.port : null;
25
+ server.close((error) => {
26
+ if (error) {
27
+ reject(error);
28
+ return;
29
+ }
30
+ resolve(port);
31
+ });
32
+ });
33
+ });
34
+
35
+ const port = await getAvailablePort();
36
+ if (!port) throw new Error('Could not reserve a port for the navigation test server.');
37
+ const url = `http://${host}:${port}/`;
31
38
 
39
+ const isReachable = async () => {
32
40
  try {
33
- await execFileAsync('curl', ['-fsI', url], {
34
- cwd: root,
35
- maxBuffer: 1024 * 1024,
36
- });
37
- return true;
41
+ const response = await fetch(url, { signal: AbortSignal.timeout(1_000) });
42
+ await response.arrayBuffer();
43
+ return response.ok;
38
44
  } catch {
39
45
  return false;
40
46
  }
41
47
  };
42
48
 
43
- const waitForServer = async () => {
49
+ const waitForServer = async (serverProcess) => {
44
50
  const startedAt = Date.now();
45
51
  const timeoutMs = 30_000;
46
52
 
47
53
  while (Date.now() - startedAt < timeoutMs) {
54
+ if (serverProcess.exitCode !== null) {
55
+ throw new Error(`Navigation test server exited before ${url} became reachable.`);
56
+ }
57
+
48
58
  if (await isReachable()) {
49
59
  return;
50
60
  }
@@ -55,12 +65,6 @@ const waitForServer = async () => {
55
65
  throw new Error(`Timed out waiting for ${url}`);
56
66
  };
57
67
 
58
- const runCapture = async (command, args, options = {}) => execFileAsync(command, args, {
59
- cwd: root,
60
- maxBuffer: 1024 * 1024 * 10,
61
- ...options,
62
- });
63
-
64
68
  const runInherit = (command, args, options = {}) => new Promise((resolve, reject) => {
65
69
  const child = spawn(command, args, {
66
70
  cwd: root,
@@ -82,31 +86,57 @@ const runInherit = (command, args, options = {}) => new Promise((resolve, reject
82
86
  });
83
87
  });
84
88
 
85
- const ensureServer = async () => {
86
- await stopServer();
89
+ const startServer = async () => {
90
+ await runInherit(process.execPath, [cliPath, 'site:public'], {
91
+ cwd: navigationDemoSiteDir,
92
+ });
93
+ await runInherit(process.execPath, [cliPath, 'images'], {
94
+ cwd: navigationDemoSiteDir,
95
+ });
87
96
 
88
- await runInherit(process.execPath, [cliPath, 'dev:local'], {
89
- cwd: dogGallerySiteDir,
97
+ const serverProcess = spawn(process.execPath, [
98
+ cliPath,
99
+ 'astro',
100
+ 'dev',
101
+ '--ignore-lock',
102
+ '--host',
103
+ host,
104
+ '--port',
105
+ String(port),
106
+ ], {
107
+ cwd: navigationDemoSiteDir,
108
+ stdio: 'inherit',
90
109
  env: {
91
110
  ...process.env,
92
- WALDE_NO_OPEN: '1',
111
+ ASTRO_DEV_BACKGROUND: '0',
93
112
  },
94
113
  });
95
- await waitForServer();
114
+ serverProcess.once('error', (error) => {
115
+ throw error;
116
+ });
117
+ await waitForServer(serverProcess);
96
118
 
97
- return true;
119
+ return serverProcess;
98
120
  };
99
121
 
100
- const stopServer = async () => {
101
- await runCapture(process.execPath, [cliPath, 'dev:stop'], {
102
- cwd: dogGallerySiteDir,
103
- }).catch(() => {});
122
+ const stopServer = async (serverProcess) => {
123
+ if (!serverProcess || serverProcess.exitCode !== null) return;
124
+
125
+ serverProcess.kill('SIGTERM');
126
+ const stopped = await Promise.race([
127
+ new Promise((resolve) => serverProcess.once('exit', () => resolve(true))),
128
+ sleep(5_000).then(() => false),
129
+ ]);
130
+
131
+ if (!stopped && serverProcess.exitCode === null) {
132
+ serverProcess.kill('SIGKILL');
133
+ }
104
134
  };
105
135
 
106
- let startedServer = false;
136
+ let serverProcess;
107
137
 
108
138
  try {
109
- startedServer = await ensureServer();
139
+ serverProcess = await startServer();
110
140
  await runInherit(npmBin, ['exec', '--', 'playwright', 'test', ...playwrightTargets], {
111
141
  env: {
112
142
  ...process.env,
@@ -114,7 +144,5 @@ try {
114
144
  },
115
145
  });
116
146
  } finally {
117
- if (startedServer) {
118
- await stopServer();
119
- }
147
+ await stopServer(serverProcess);
120
148
  }
@@ -0,0 +1,151 @@
1
+ import assert from 'node:assert/strict';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { parseYamlMapping } from './lib/frontmatter-yaml.mjs';
7
+ import { splitSiteFile } from './lib/site-content.mjs';
8
+ import {
9
+ resolveThemeConfig,
10
+ themePresetNames,
11
+ themePresets,
12
+ } from './lib/theme-presets.mjs';
13
+
14
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
15
+ const cliPath = path.join(repoRoot, 'bin', 'norna.mjs');
16
+ const tempParent = path.join(repoRoot, 'node_modules', '.cache');
17
+ await mkdir(tempParent, { recursive: true });
18
+ const tempRoot = await mkdtemp(path.join(tempParent, 'norna theme presets-'));
19
+ const siteDir = path.join(tempRoot, 'site');
20
+
21
+ const runCli = (args) => spawnSync(process.execPath, [cliPath, '--site-dir', siteDir, ...args], {
22
+ cwd: repoRoot,
23
+ encoding: 'utf8',
24
+ });
25
+
26
+ try {
27
+ assert.deepEqual(Object.keys(themePresets), themePresetNames);
28
+ for (const presetName of themePresetNames) {
29
+ const resolved = resolveThemeConfig({ preset: presetName }, 'test theme');
30
+ assert.equal(resolved.preset, presetName);
31
+ assert.ok(resolved.layout?.density);
32
+ assert.ok(resolved.gallery?.width);
33
+ assert.ok(resolved.typography?.fontFamily);
34
+ assert.ok(resolved.typography?.preset);
35
+ assert.ok(resolved.presentation?.palette);
36
+ }
37
+
38
+ const overridden = resolveThemeConfig({
39
+ preset: 'documentation',
40
+ layout: { pageWidth: '1300px' },
41
+ presentation: { palette: 'dark' },
42
+ }, 'test theme');
43
+ assert.equal(overridden.layout.density, 'compact');
44
+ assert.equal(overridden.layout.pageWidth, '1300px');
45
+ assert.equal(overridden.gallery.width, '920px');
46
+ assert.equal(overridden.presentation.palette, 'dark');
47
+ assert.deepEqual(overridden.presentation.sectionSurfaces.sequence, ['base', 'soft']);
48
+ assert.throws(
49
+ () => resolveThemeConfig({ preset: 'unknown' }, 'test/theme.md'),
50
+ /Unknown theme preset "unknown" in test\/theme\.md.*portfolio, documentation, project, statement/,
51
+ );
52
+
53
+ await mkdir(path.join(siteDir, 'routes', '010-guide'), { recursive: true });
54
+ await writeFile(path.join(siteDir, 'config.mjs'), `export default {
55
+ site: { url: 'https://example.com/', basePath: '/' },
56
+ github: {
57
+ repo: 'owner/repo',
58
+ branch: 'main',
59
+ pagesWorkflow: 'Deploy to GitHub Pages',
60
+ },
61
+ };
62
+ `);
63
+ await writeFile(path.join(siteDir, 'content.md'), `---
64
+ title: Theme preset test
65
+ description: Root page
66
+ ---
67
+
68
+ ## Home {#home}
69
+
70
+ Root content.
71
+ `);
72
+ await writeFile(path.join(siteDir, 'theme.md'), `---
73
+ preset: documentation
74
+ layout:
75
+ pageWidth: 1300px
76
+ presentation:
77
+ palette: dark
78
+ ---
79
+ `);
80
+ await writeFile(path.join(siteDir, 'routes', '010-guide', 'route-content.md'), `---
81
+ title: Guide
82
+ description: Route page
83
+ navigation:
84
+ label: Guide
85
+ ---
86
+
87
+ ## Guide {#guide}
88
+
89
+ Route content.
90
+ `);
91
+ await writeFile(path.join(siteDir, 'routes', '010-guide', 'theme.md'), `---
92
+ preset: portfolio
93
+ layout:
94
+ pageWidth: 1010px
95
+ presentation:
96
+ palette: light
97
+ ---
98
+ `);
99
+
100
+ const buildResult = runCli(['build']);
101
+ assert.equal(buildResult.status, 0, buildResult.stderr || buildResult.stdout);
102
+ const rootHtml = await readFile(path.join(tempRoot, 'dist', 'index.html'), 'utf8');
103
+ const routeHtml = await readFile(path.join(tempRoot, 'dist', 'guide', 'index.html'), 'utf8');
104
+ assert.match(rootHtml, /--page-width: 1300px/);
105
+ assert.match(rootHtml, /--font-sans: Georgia, 'Times New Roman', serif/);
106
+ assert.match(rootHtml, /--color-page: #000000/);
107
+ assert.match(routeHtml, /--page-width: 1010px/);
108
+ assert.match(routeHtml, /--font-sans: 'Helvetica Neue', Arial, sans-serif/);
109
+ assert.match(routeHtml, /--color-page: #ffffff/);
110
+
111
+ const typographyResult = runCli(['typography', 'show']);
112
+ assert.equal(typographyResult.status, 0, typographyResult.stderr || typographyResult.stdout);
113
+ assert.match(typographyResult.stdout, /value: text-forward/);
114
+ assert.match(typographyResult.stdout, /value: quiet-gallery/);
115
+
116
+ const routeThemePath = path.join(siteDir, 'routes', '010-guide', 'theme.md');
117
+ const routeThemeSource = await readFile(routeThemePath, 'utf8');
118
+ await writeFile(routeThemePath, `---\npreset: unknown\n---\n`);
119
+ const invalidRoutePresetResult = runCli(['config:check']);
120
+ assert.notEqual(invalidRoutePresetResult.status, 0);
121
+ assert.match(invalidRoutePresetResult.stderr, /Unknown theme preset "unknown" in .*routes\/010-guide\/theme\.md/);
122
+ await writeFile(routeThemePath, routeThemeSource);
123
+
124
+ const exportResult = runCli(['theme:export', 'documentation']);
125
+ assert.equal(exportResult.status, 0, exportResult.stderr || exportResult.stdout);
126
+ assert.match(exportResult.stdout, /orig-documentation-theme\.md/);
127
+ const exportedPath = path.join(siteDir, 'orig-documentation-theme.md');
128
+ const exportedSource = await readFile(exportedPath, 'utf8');
129
+ assert.match(exportedSource, /This is a reference file\. Norna only loads theme\.md\./);
130
+ assert.match(exportedSource, /Available theme presets: portfolio, documentation, project, statement\./);
131
+ assert.match(exportedSource, /# Alternatives: dark, light, paper\./);
132
+ const { frontmatterBody } = splitSiteFile(exportedSource, 'exported theme reference');
133
+ const exportedConfig = parseYamlMapping(frontmatterBody);
134
+ assert.equal(exportedConfig.preset, 'documentation');
135
+ assert.equal(exportedConfig.layout.pageWidth, themePresets.documentation.layout.pageWidth);
136
+ assert.equal(exportedConfig.typography.fontFamily, themePresets.documentation.typography.fontFamily);
137
+ assert.deepEqual(exportedConfig.presentation.sectionSurfaces.sequence, ['base', 'soft']);
138
+
139
+ const exportAgainResult = runCli(['theme:export', 'documentation']);
140
+ assert.notEqual(exportAgainResult.status, 0);
141
+ assert.match(exportAgainResult.stderr, /already exists\. Norna will not overwrite it\./);
142
+ assert.equal(await readFile(exportedPath, 'utf8'), exportedSource);
143
+
144
+ const unknownExportResult = runCli(['theme:export', 'unknown']);
145
+ assert.notEqual(unknownExportResult.status, 0);
146
+ assert.match(unknownExportResult.stderr, /Unknown theme preset "unknown"/);
147
+
148
+ console.log('ok - complete theme presets resolve for root and route themes and export as protected references');
149
+ } finally {
150
+ await rm(tempRoot, { force: true, recursive: true });
151
+ }
@@ -57,8 +57,8 @@ const logo = logoAsset
57
57
  height: logoSettings?.height,
58
58
  }
59
59
  : undefined;
60
- const pagePresentation = resolvePagePresentation(themeData);
61
- const frameColors = resolveFrameColors({ theme: themeData });
60
+ const pagePresentation = resolvePagePresentation(themeData, themeLabel);
61
+ const frameColors = resolveFrameColors({ theme: themeData, sourceLabel: themeLabel });
62
62
  const sections = entry.data.sections;
63
63
  const siteHtml = entry.rendered?.html ?? '';
64
64
  const resolvedSections = await getSectionsContent(siteHtml, rawMarkdown, sections, currentPage);
@@ -15,6 +15,7 @@ const textSize = z.enum(['small', 'medium', 'large', 'xlarge']);
15
15
  const textWidth = z.enum(['narrow', 'normal', 'wide']);
16
16
  const headingWeight = z.union([z.literal(400), z.literal(500), z.literal(600), z.literal(700)]);
17
17
  const typographyPreset = z.enum(['quiet-gallery', 'compact-gallery', 'text-forward', 'statement']);
18
+ const themePreset = z.enum(['portfolio', 'documentation', 'project', 'statement']);
18
19
  const presentationPalette = z.enum(['dark', 'light', 'paper']);
19
20
  const sectionSurfaceMode = z.enum(['none', 'cycle']);
20
21
  const sectionSurface = z.enum(['base', 'soft', 'emphasis']);
@@ -197,6 +198,7 @@ const siteSchema = z.object({
197
198
  });
198
199
 
199
200
  export const themeVisualSchema = z.object({
201
+ preset: themePreset.optional(),
200
202
  layout: themeLayout.optional(),
201
203
  gallery: themeGallery.optional(),
202
204
  typography: themeTypography.optional(),
@@ -287,6 +287,8 @@ a {
287
287
  .site-banner {
288
288
  display: flex;
289
289
  align-items: flex-start;
290
+ width: 100%;
291
+ min-width: 0;
290
292
  gap: 1rem;
291
293
  border: 1px solid color-mix(in srgb, #ffd84d 42%, var(--color-border));
292
294
  border-inline-start: 0.35rem solid #ffd84d;
@@ -14,6 +14,7 @@
14
14
  "norna:config:check": "norna config:check",
15
15
  "norna:content:check": "norna content:check",
16
16
  "norna:sync": "norna content:sync",
17
+ "norna:theme:export": "norna theme:export",
17
18
  "norna:typography:presets": "norna typography presets",
18
19
  "norna:typography:show": "norna typography show",
19
20
  "norna:public": "norna site:public",
@@ -14,6 +14,7 @@
14
14
  "norna:config:check": "norna config:check",
15
15
  "norna:content:check": "norna content:check",
16
16
  "norna:sync": "norna content:sync",
17
+ "norna:theme:export": "norna theme:export",
17
18
  "norna:typography:presets": "norna typography presets",
18
19
  "norna:typography:show": "norna typography show",
19
20
  "norna:public": "norna site:public",