@janga/norna 0.7.16 → 0.7.18
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.
- package/README.md +18 -9
- package/astro.config.mjs +35 -3
- package/bin/norna-cli.mjs +1 -1
- package/docs/README.md +16 -14
- package/docs/commands.md +2 -2
- package/docs/configuration.md +25 -2
- package/docs/content.md +92 -103
- package/docs/design/navigation-and-theme-plan.md +186 -0
- package/docs/design/norna-diagram-design.md +9 -9
- package/docs/design/site-examples-structure.md +15 -11
- package/docs/engine-development.md +19 -22
- package/docs/getting-started.md +21 -16
- package/docs/images-and-metadata.md +12 -9
- package/docs/pages.md +190 -0
- package/docs/public-files.md +49 -36
- package/docs/publishing.md +19 -3
- package/docs/site-files.md +46 -38
- package/docs/sitewide-content.md +20 -36
- package/docs/theme.md +109 -143
- package/docs/typography.md +19 -31
- package/examples/README.md +10 -4
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/site.schema.json +17 -41
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/sitewide.schema.json +26 -67
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/theme.schema.json +185 -88
- package/examples/complete-sites/dog-shelter-multi-page/.astro/data-store.json +1 -1
- package/examples/complete-sites/dog-shelter-multi-page/.astro/dev-local.json +4 -4
- package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.json +5 -5
- package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.log +33 -23
- package/examples/complete-sites/dog-shelter-multi-page/README.md +1 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/_metadata.json +8 -8
- package/examples/complete-sites/dog-shelter-multi-page/site/.norna/generated-images.json +20 -20
- package/examples/complete-sites/dog-shelter-multi-page/site/{content.md → pages/000-home/content.md} +3 -4
- package/examples/complete-sites/dog-shelter-multi-page/site/{routes → pages}/010-dogs/content.md +3 -4
- package/examples/complete-sites/dog-shelter-multi-page/site/{routes → pages}/020-adopt/content.md +3 -4
- package/examples/complete-sites/dog-shelter-multi-page/site/sitewide-content.yaml +2 -4
- package/examples/complete-sites/dog-shelter-single-page/.astro/collections/site.schema.json +17 -41
- package/examples/complete-sites/dog-shelter-single-page/.astro/collections/sitewide.schema.json +26 -67
- package/examples/complete-sites/dog-shelter-single-page/.astro/collections/theme.schema.json +185 -88
- package/examples/complete-sites/dog-shelter-single-page/.astro/data-store.json +1 -1
- package/examples/complete-sites/dog-shelter-single-page/.astro/dev-local.json +8 -0
- package/examples/complete-sites/dog-shelter-single-page/.astro/dev.json +5 -5
- package/examples/complete-sites/dog-shelter-single-page/.astro/dev.log +16 -12
- package/examples/complete-sites/dog-shelter-single-page/README.md +1 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/_metadata.json +8 -8
- package/examples/complete-sites/dog-shelter-single-page/site/.norna/generated-images.json +12 -12
- package/examples/complete-sites/dog-shelter-single-page/site/{content.md → pages/000-home/content.md} +5 -2
- package/examples/complete-sites/dog-shelter-single-page/site/sitewide-content.yaml +2 -4
- package/examples/feature-demos/media-and-surfaces/.astro/collections/site.schema.json +17 -41
- package/examples/feature-demos/media-and-surfaces/.astro/collections/sitewide.schema.json +26 -67
- package/examples/feature-demos/media-and-surfaces/.astro/collections/theme.schema.json +180 -91
- package/examples/feature-demos/media-and-surfaces/.astro/data-store.json +1 -1
- package/examples/feature-demos/media-and-surfaces/.astro/dev.log +4 -14
- package/examples/feature-demos/media-and-surfaces/README.md +2 -2
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/_metadata.json +9 -9
- package/examples/feature-demos/media-and-surfaces/site/.norna/generated-images.json +16 -16
- package/examples/feature-demos/media-and-surfaces/site/{content.md → pages/000-home/content.md} +4 -7
- package/examples/feature-demos/media-and-surfaces/site/{routes → pages}/010-media/content.md +6 -5
- package/examples/feature-demos/media-and-surfaces/site/{routes → pages}/020-surfaces/content.md +9 -7
- package/examples/feature-demos/media-and-surfaces/site/pages/020-surfaces/theme.yaml +2 -0
- package/examples/feature-demos/media-and-surfaces/site/sitewide-content.yaml +0 -2
- package/examples/feature-demos/sitewide-content/README.md +3 -3
- package/examples/feature-demos/sitewide-content/site/pages/000-home/content.md +17 -0
- package/examples/feature-demos/sitewide-content/site/pages/010-logo-and-navigation/content.md +17 -0
- package/examples/feature-demos/sitewide-content/site/{routes → pages}/020-notices/content.md +7 -6
- package/examples/feature-demos/sitewide-content/site/{routes → pages}/030-footer/content.md +6 -5
- package/examples/feature-demos/sitewide-content/site/sitewide-content.yaml +4 -13
- package/examples/feature-demos/theme-preset-documentation/README.md +16 -0
- package/examples/feature-demos/theme-preset-documentation/site/.norna/generated-images.json +74 -0
- package/examples/feature-demos/{theme-presets/site/routes/020-documentation → theme-preset-documentation/site/pages/000-home}/content.md +4 -7
- package/examples/feature-demos/theme-preset-documentation/site/sitewide-content.yaml +3 -0
- package/examples/feature-demos/theme-preset-portfolio/README.md +16 -0
- package/examples/feature-demos/theme-preset-portfolio/site/.norna/generated-images.json +74 -0
- package/examples/feature-demos/theme-preset-portfolio/site/config.yaml +1 -0
- package/examples/feature-demos/{theme-presets/site/routes/010-portfolio → theme-preset-portfolio/site/pages/000-home}/content.md +4 -7
- package/examples/feature-demos/theme-preset-portfolio/site/public/robots.txt +2 -0
- package/examples/feature-demos/theme-preset-portfolio/site/sitewide-content.yaml +3 -0
- package/examples/feature-demos/theme-preset-project/README.md +16 -0
- package/examples/feature-demos/theme-preset-project/site/.norna/generated-images.json +74 -0
- package/examples/feature-demos/theme-preset-project/site/config.yaml +1 -0
- package/examples/feature-demos/{theme-presets/site/routes/030-project → theme-preset-project/site/pages/000-home}/content.md +4 -7
- package/examples/feature-demos/theme-preset-project/site/public/robots.txt +2 -0
- package/examples/feature-demos/theme-preset-project/site/sitewide-content.yaml +3 -0
- package/examples/feature-demos/theme-preset-statement/README.md +16 -0
- package/examples/feature-demos/theme-preset-statement/site/.norna/generated-images.json +74 -0
- package/examples/feature-demos/theme-preset-statement/site/config.yaml +1 -0
- package/examples/feature-demos/{theme-presets/site/routes/040-statement → theme-preset-statement/site/pages/000-home}/content.md +4 -7
- package/examples/feature-demos/theme-preset-statement/site/public/robots.txt +2 -0
- package/examples/feature-demos/theme-preset-statement/site/sitewide-content.yaml +3 -0
- package/fixtures/basic/site/{content.md → pages/000-home/content.md} +5 -6
- package/fixtures/basic/site/sitewide-content.yaml +1 -8
- package/fixtures/basic/site/theme.yaml +1 -8
- package/fixtures/content-model-v2/site/{content.md → pages/000-home/content.md} +11 -13
- package/fixtures/content-model-v2/site/pages/010-guide/content.md +18 -0
- package/fixtures/nested-pages/.astro/collections/site.schema.json +35 -0
- package/fixtures/nested-pages/.astro/collections/sitewide.schema.json +97 -0
- package/fixtures/nested-pages/.astro/collections/theme.schema.json +844 -0
- package/fixtures/nested-pages/.astro/content-assets.mjs +4 -0
- package/fixtures/nested-pages/.astro/content-modules.mjs +1 -0
- package/fixtures/nested-pages/.astro/content.d.ts +183 -0
- package/fixtures/nested-pages/.astro/data-store.json +1 -0
- package/fixtures/nested-pages/.astro/dev-local.json +8 -0
- package/fixtures/nested-pages/.astro/dev.log +1128 -0
- package/fixtures/nested-pages/.astro/settings.json +5 -0
- package/fixtures/nested-pages/.astro/types.d.ts +2 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/_metadata.json +74 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro-D_GIpJIE.js +434 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro-D_GIpJIE.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/audit-DM9Hkl5B.js +1534 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/highlight-DnZ1ULOL.js +48 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/icons-RyebKi45.js +42 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/icons-RyebKi45.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/package.json +3 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/settings-DaU-gNnK.js +190 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/settings-DaU-gNnK.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/toolbar-63PA619H.js +458 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/toolbar-63PA619H.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/ui-library--bkcNSYz.js +1016 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/window-jPkpKVcy.js +32 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/window-jPkpKVcy.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/xray-BXCHpEJP.js +111 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/xray-BXCHpEJP.js.map +1 -0
- package/fixtures/nested-pages/site/.norna/generated-images.json +10 -0
- package/fixtures/nested-pages/site/config.yaml +3 -0
- package/fixtures/nested-pages/site/pages/000-home/content.md +15 -0
- package/fixtures/nested-pages/site/pages/010-guides/content.md +15 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/content.md +21 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/images/diagram.svg +21 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/pages/010-macos/content.md +18 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/theme.yaml +2 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/020-workflows/content.md +11 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/030-release-notes/content.md +9 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/040-configuration/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/050-content/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/060-themes/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/070-images/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/080-cards/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/090-notes/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/100-validation/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/110-synchronization/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/120-building/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/130-publishing/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/140-troubleshooting/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/150-migration/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/160-accessibility/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/170-performance/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/180-security/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/190-localization/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/200-upgrading/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/210-faq/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/theme.yaml +7 -0
- package/fixtures/nested-pages/site/pages/020-reference/content.md +10 -0
- package/fixtures/nested-pages/site/pages/020-reference/pages/010-installation/content.md +11 -0
- package/fixtures/nested-pages/site/theme.yaml +1 -0
- package/package.json +8 -4
- package/schemas/config.schema.json +46 -5
- package/schemas/content-frontmatter.schema.json +28 -63
- package/schemas/manifest.json +4 -2
- package/schemas/page-theme.schema.json +192 -0
- package/schemas/sitewide-content.schema.json +58 -132
- package/schemas/theme.schema.json +434 -152
- package/scripts/check-config.mjs +8 -6
- package/scripts/deploy-site.mjs +6 -8
- package/scripts/doctor.mjs +2 -2
- package/scripts/generate-images.mjs +15 -29
- package/scripts/generate-schemas.mjs +7 -218
- package/scripts/lib/base-path-redirect.mjs +9 -0
- package/scripts/lib/editor-language-service.mjs +125 -79
- package/scripts/lib/example-sites.mjs +15 -3
- package/scripts/lib/heading-ids.mjs +161 -0
- package/scripts/lib/navigation-model.mjs +51 -0
- package/scripts/lib/norna-markdown-blocks.mjs +3 -4
- package/scripts/lib/page-model.mjs +102 -0
- package/scripts/lib/presentation.mjs +25 -17
- package/scripts/lib/project-config.mjs +46 -8
- package/scripts/lib/schema-definitions.mjs +78 -55
- package/scripts/lib/schema-editor-metadata.mjs +519 -0
- package/scripts/lib/schema-value-definitions.mjs +20 -6
- package/scripts/lib/site-content.mjs +159 -97
- package/scripts/lib/site-conventions.mjs +1 -0
- package/scripts/lib/site-paths.mjs +11 -5
- package/scripts/lib/theme-config.mjs +12 -18
- package/scripts/lib/theme-presets.mjs +55 -14
- package/scripts/release.mjs +1 -1
- package/scripts/show-typography.mjs +4 -30
- package/scripts/sync-content-sections.mjs +107 -68
- package/scripts/{test-temporary-visibility.mjs → test-banner-visibility.mjs} +8 -25
- package/scripts/test-client-javascript.mjs +50 -46
- package/scripts/test-content-check.mjs +153 -69
- package/scripts/test-content-model-v2.mjs +566 -285
- package/scripts/test-documentation.mjs +10 -3
- package/scripts/test-editor-language-service.mjs +172 -25
- package/scripts/test-engine-commands.mjs +6 -5
- package/scripts/test-examples.mjs +30 -1
- package/scripts/test-heading-ids.mjs +68 -0
- package/scripts/test-navigation-model.mjs +63 -0
- package/scripts/test-navigation.mjs +11 -2
- package/scripts/test-nested-pages.mjs +125 -0
- package/scripts/test-package-check.mjs +55 -95
- package/scripts/test-page-model.mjs +108 -0
- package/scripts/test-project-config.mjs +22 -3
- package/scripts/test-schemas.mjs +82 -15
- package/scripts/test-theme-presets.mjs +57 -42
- package/src/components/NavigationPageTree.astro +157 -0
- package/src/components/PageContentsLinks.astro +40 -0
- package/src/components/SectionNavigationScript.astro +117 -5
- package/src/components/SiteBreadcrumbs.astro +32 -0
- package/src/components/SiteNavigation.astro +97 -68
- package/src/components/SitePage.astro +93 -55
- package/src/components/SiteSection.astro +29 -27
- package/src/components/SiteTreeNavigation.astro +35 -0
- package/src/components/TreeNavigationScript.astro +83 -0
- package/src/content.config.ts +8 -10
- package/src/layouts/BaseLayout.astro +21 -20
- package/src/lib/pageThemes.ts +44 -0
- package/src/lib/sectionContent.ts +106 -107
- package/src/lib/siteNavigation.ts +49 -0
- package/src/lib/sitePages.ts +96 -38
- package/src/pages/{[slug].astro → [...slug].astro} +2 -2
- package/src/pages/index.astro +2 -1
- package/src/styles/global.css +743 -57
- package/starters/basic/README.md +9 -11
- package/starters/basic/site/{content.md → pages/000-home/content.md} +7 -4
- package/starters/basic/site/sitewide-content.yaml +1 -10
- package/starters/basic/site/theme.yaml +1 -105
- package/starters/project/README.md +14 -14
- package/starters/project/site/{content.md → pages/000-home/content.md} +5 -4
- package/starters/project/site/{routes → pages}/010-guide/content.md +5 -4
- package/starters/project/site/sitewide-content.yaml +1 -10
- package/starters/project/site/theme.yaml +1 -31
- package/docs/routes.md +0 -121
- package/examples/feature-demos/media-and-surfaces/site/routes/020-surfaces/theme.yaml +0 -3
- package/examples/feature-demos/sitewide-content/site/content.md +0 -16
- package/examples/feature-demos/sitewide-content/site/routes/010-identity/content.md +0 -20
- package/examples/feature-demos/theme-presets/README.md +0 -18
- package/examples/feature-demos/theme-presets/site/.norna/generated-images.json +0 -290
- package/examples/feature-demos/theme-presets/site/content.md +0 -17
- package/examples/feature-demos/theme-presets/site/sitewide-content.yaml +0 -12
- package/examples/feature-demos/theme-presets/site/theme.yaml +0 -1
- package/fixtures/content-model-v2/site/routes/010-guide/content.md +0 -17
- package/scripts/lib/route-model.mjs +0 -17
- package/scripts/test-route-model.mjs +0 -32
- package/src/lib/routeThemes.ts +0 -41
- /package/examples/complete-sites/dog-shelter-multi-page/site/{images/intro → pages/000-home/images}/shelter-dog.jpg +0 -0
- /package/examples/complete-sites/dog-shelter-multi-page/site/{routes/010-dogs/images/dogs → pages/010-dogs/images}/rex.jpg +0 -0
- /package/examples/complete-sites/dog-shelter-multi-page/site/{routes/010-dogs/images/dogs → pages/010-dogs/images}/rover.jpg +0 -0
- /package/examples/complete-sites/dog-shelter-multi-page/site/{routes/020-adopt/images/adopt → pages/020-adopt/images}/adoption-dog.jpg +0 -0
- /package/examples/complete-sites/dog-shelter-single-page/site/{images/you-can-help → pages/000-home/images}/foster-dog.jpg +0 -0
- /package/examples/complete-sites/dog-shelter-single-page/site/{images/what-we-do → pages/000-home/images}/rescue-dog.jpg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/cards → pages/010-media/images}/card-carousel.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/cards → pages/010-media/images}/card-stack.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/cards → pages/010-media/images}/card-surfaces.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/carousel → pages/010-media/images}/carousel-one.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/carousel → pages/010-media/images}/carousel-three.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/carousel → pages/010-media/images}/carousel-two.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/stack → pages/010-media/images}/stack-one.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/stack → pages/010-media/images}/stack-two.svg +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-documentation}/site/config.yaml +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/020-documentation/images/documentation → theme-preset-documentation/site/pages/000-home/images}/black-schnauzer.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/020-documentation/images/documentation → theme-preset-documentation/site/pages/000-home/images}/retriever-portrait.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/020-documentation/images/documentation → theme-preset-documentation/site/pages/000-home/images}/schnauzer-portrait.jpg +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-documentation}/site/public/robots.txt +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/020-documentation → theme-preset-documentation/site}/theme.yaml +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/010-portfolio/images/portfolio → theme-preset-portfolio/site/pages/000-home/images}/puppy-portrait.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/010-portfolio/images/portfolio → theme-preset-portfolio/site/pages/000-home/images}/shepherd-portrait.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/010-portfolio/images/portfolio → theme-preset-portfolio/site/pages/000-home/images}/white-puppy.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/010-portfolio → theme-preset-portfolio/site}/theme.yaml +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/collections/routeTheme.schema.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/collections/site.schema.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/collections/sitewide.schema.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/collections/theme.schema.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/content-assets.mjs +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/content-modules.mjs +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/content.d.ts +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/data-store.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/dev-local.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/dev.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/dev.log +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/settings.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/types.d.ts +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/_metadata.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_n_aria-query.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_n_axobject-query.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_n_html-escaper.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/embla-carousel.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/embla-carousel.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/icons-RyebKi45.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/package.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/toolbar-63PA619H.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/window-jPkpKVcy.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/030-project/images/project → theme-preset-project/site/pages/000-home/images}/boxer-portrait.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/030-project/images/project → theme-preset-project/site/pages/000-home/images}/grass-puppy.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/030-project/images/project → theme-preset-project/site/pages/000-home/images}/grey-street-dog.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/030-project → theme-preset-project/site}/theme.yaml +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/040-statement/images/statement → theme-preset-statement/site/pages/000-home/images}/black-pedigree.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/040-statement/images/statement → theme-preset-statement/site/pages/000-home/images}/sheepdog-grass.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/040-statement/images/statement → theme-preset-statement/site/pages/000-home/images}/sheepdog-wide.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/040-statement → theme-preset-statement/site}/theme.yaml +0 -0
- /package/fixtures/basic/site/{images/work → pages/000-home/images}/.gitkeep +0 -0
- /package/fixtures/content-model-v2/site/{images/card-section → pages/000-home/images}/adopt.svg +0 -0
- /package/fixtures/content-model-v2/site/{images/intro → pages/000-home/images}/detail.jpg +0 -0
- /package/fixtures/content-model-v2/site/{images/intro → pages/000-home/images}/duplicate.jpg +0 -0
- /package/fixtures/content-model-v2/site/{images/card-section → pages/000-home/images}/foster.svg +0 -0
- /package/fixtures/content-model-v2/site/{images/intro → pages/000-home/images}/hero.jpg +0 -0
- /package/fixtures/content-model-v2/site/{images/plain → pages/000-home/images}/image.jpg +0 -0
- /package/fixtures/content-model-v2/site/{images/timed → pages/000-home/images}/slide-one.jpg +0 -0
- /package/fixtures/content-model-v2/site/{images/timed → pages/000-home/images}/slide-three.jpg +0 -0
- /package/fixtures/content-model-v2/site/{images/timed → pages/000-home/images}/slide-two.jpg +0 -0
- /package/fixtures/content-model-v2/site/{routes/010-guide/images/intro → pages/010-guide/images}/duplicate.jpg +0 -0
- /package/starters/basic/site/{images/work → pages/000-home/images}/.gitkeep +0 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { homePageDirectory } from './site-conventions.mjs';
|
|
2
|
+
|
|
3
|
+
export const pageDirectoryPattern = /^(\d{3})-([a-z0-9]+(?:-[a-z0-9]+)*)$/;
|
|
4
|
+
|
|
5
|
+
const pageEntryPathSeparator = '--';
|
|
6
|
+
|
|
7
|
+
export const parsePageDirectory = (pageDirectory, label = 'page directory') => {
|
|
8
|
+
const match = pageDirectory.match(pageDirectoryPattern);
|
|
9
|
+
|
|
10
|
+
if (!match) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`Invalid ${label} "${pageDirectory}". Page directories must use the form NNN-page-id, for example 010-getting-started. Page ids may contain only lowercase letters, numbers, and single hyphens.`,
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
if (match[1] === '000' && pageDirectory !== homePageDirectory) {
|
|
16
|
+
throw new Error(`Invalid ${label} "${pageDirectory}". The 000 prefix is reserved for ${homePageDirectory}.`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
pageDirectory,
|
|
21
|
+
pageOrder: Number.parseInt(match[1], 10),
|
|
22
|
+
pageId: match[2],
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const normalizePageDirectoryPath = (pageDirectoryPath) => pageDirectoryPath.replaceAll('\\', '/');
|
|
27
|
+
|
|
28
|
+
export const parsePageDirectoryPath = (pageDirectoryPath, label = 'page directory path') => {
|
|
29
|
+
const normalizedPath = normalizePageDirectoryPath(pageDirectoryPath);
|
|
30
|
+
const segments = normalizedPath.split('/').filter(Boolean);
|
|
31
|
+
|
|
32
|
+
if (!normalizedPath || normalizedPath.startsWith('/') || normalizedPath.endsWith('/') || segments.length % 2 === 0) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Invalid ${label} "${pageDirectoryPath}". Nested pages must use the form NNN-page-id/pages/NNN-child-id.`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const pageDirectories = [];
|
|
39
|
+
for (const [index, segment] of segments.entries()) {
|
|
40
|
+
if (index % 2 === 1) {
|
|
41
|
+
if (segment !== 'pages') {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Invalid ${label} "${pageDirectoryPath}". Use a pages directory between nested page directories.`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
pageDirectories.push(parsePageDirectory(segment, `${label} segment ${segment}`));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const pageIds = pageDirectories.map(({ pageId }) => pageId);
|
|
53
|
+
const pageOrders = pageDirectories.map(({ pageOrder }) => pageOrder);
|
|
54
|
+
const currentPage = pageDirectories.at(-1);
|
|
55
|
+
const homeIndex = pageDirectories.findIndex(({ pageDirectory }) => pageDirectory === homePageDirectory);
|
|
56
|
+
if (homeIndex > 0) {
|
|
57
|
+
throw new Error(`Invalid ${label} "${pageDirectoryPath}". ${homePageDirectory} is allowed only as a top-level page.`);
|
|
58
|
+
}
|
|
59
|
+
if (homeIndex === 0 && pageDirectories.length > 1) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Invalid ${label} "${pageDirectoryPath}". ${homePageDirectory} is the homepage and cannot contain child pages. Place the page beside it under site/pages/, or below another non-home page.`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
const isHome = homeIndex === 0;
|
|
65
|
+
const logicalPageIds = isHome ? [] : pageIds;
|
|
66
|
+
const logicalPageOrders = isHome ? [] : pageOrders;
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
pageDirectory: normalizedPath,
|
|
70
|
+
pageDirectories: pageDirectories.map(({ pageDirectory }) => pageDirectory),
|
|
71
|
+
pageId: currentPage.pageId,
|
|
72
|
+
pageIds: logicalPageIds,
|
|
73
|
+
pageOrder: currentPage.pageOrder,
|
|
74
|
+
pageOrders: logicalPageOrders,
|
|
75
|
+
pagePath: logicalPageIds.join('/'),
|
|
76
|
+
parentPagePath: logicalPageIds.length > 1
|
|
77
|
+
? logicalPageIds.slice(0, -1).join('/')
|
|
78
|
+
: null,
|
|
79
|
+
depth: pageDirectories.length,
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const encodePageDirectoryPath = (pageDirectoryPath) => {
|
|
84
|
+
const { pageDirectories } = parsePageDirectoryPath(pageDirectoryPath);
|
|
85
|
+
return pageDirectories.join(pageEntryPathSeparator);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const decodePageDirectoryPath = (pageEntryPath, label = 'page entry path') => {
|
|
89
|
+
const pageDirectories = pageEntryPath.split(pageEntryPathSeparator);
|
|
90
|
+
if (pageDirectories.some((pageDirectory) => !pageDirectory)) {
|
|
91
|
+
throw new Error(`Invalid ${label} "${pageEntryPath}".`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return pageDirectories.join('/pages/');
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const getPageDirectoryAncestors = (pageDirectoryPath) => {
|
|
98
|
+
const { pageDirectories } = parsePageDirectoryPath(pageDirectoryPath);
|
|
99
|
+
return pageDirectories.map((_, index) => (
|
|
100
|
+
pageDirectories.slice(0, index + 1).join('/pages/')
|
|
101
|
+
));
|
|
102
|
+
};
|
|
@@ -5,7 +5,11 @@ import {
|
|
|
5
5
|
import { resolveThemeConfig } from './theme-presets.mjs';
|
|
6
6
|
|
|
7
7
|
export const presentationPaletteNames = ['dark', 'light', 'paper'];
|
|
8
|
-
const
|
|
8
|
+
const sectionBackgroundPatterns = Object.freeze({
|
|
9
|
+
uniform: ['base'],
|
|
10
|
+
alternating: ['base', 'soft'],
|
|
11
|
+
cycling: ['base', 'soft', 'emphasis'],
|
|
12
|
+
});
|
|
9
13
|
|
|
10
14
|
const presentationPalettes = Object.freeze({
|
|
11
15
|
dark: {
|
|
@@ -92,34 +96,30 @@ export const getPresentationCssVariables = (presentation) => {
|
|
|
92
96
|
};
|
|
93
97
|
};
|
|
94
98
|
|
|
95
|
-
const getSectionSurfaces = (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
for (const surface of value) {
|
|
101
|
-
if (!sectionSurfaceNames.includes(surface)) {
|
|
102
|
-
throw new Error(`Unknown section surface "${surface}" in ${sourceLabel}. Use one of: ${sectionSurfaceNames.join(', ')}.`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (new Set(value).size !== value.length) {
|
|
107
|
-
throw new Error(`Each section surface may appear only once in ${sourceLabel}.`);
|
|
99
|
+
const getSectionSurfaces = (pattern = 'uniform', sourceLabel = 'theme.yaml') => {
|
|
100
|
+
const surfaces = sectionBackgroundPatterns[pattern];
|
|
101
|
+
if (!surfaces) {
|
|
102
|
+
throw new Error(`sections.backgroundPattern must be one of ${Object.keys(sectionBackgroundPatterns).join(', ')} in ${sourceLabel}.`);
|
|
108
103
|
}
|
|
109
104
|
|
|
110
|
-
return
|
|
105
|
+
return surfaces;
|
|
111
106
|
};
|
|
112
107
|
|
|
113
108
|
export const resolveThemePresentation = (theme, sourceLabel = 'theme.yaml') => {
|
|
114
109
|
const normalizedTheme = resolveThemeConfig(theme, sourceLabel);
|
|
115
110
|
const paletteName = normalizedTheme.palette ?? 'dark';
|
|
116
111
|
const palette = getPresentationPalette(paletteName);
|
|
112
|
+
const typography = resolveTypographyConfig(normalizedTheme.typography ?? defaultTypography);
|
|
113
|
+
const textWidth = normalizedTheme.layout?.textWidth;
|
|
114
|
+
if (textWidth !== undefined) {
|
|
115
|
+
typography.values.body.width = textWidth;
|
|
116
|
+
}
|
|
117
117
|
|
|
118
118
|
return {
|
|
119
119
|
paletteName,
|
|
120
120
|
palette,
|
|
121
|
-
sectionSurfaces: getSectionSurfaces(normalizedTheme.
|
|
122
|
-
typography
|
|
121
|
+
sectionSurfaces: getSectionSurfaces(normalizedTheme.sections?.backgroundPattern, sourceLabel),
|
|
122
|
+
typography,
|
|
123
123
|
};
|
|
124
124
|
};
|
|
125
125
|
|
|
@@ -129,6 +129,14 @@ export const resolvePagePresentation = (theme, sourceLabel) => {
|
|
|
129
129
|
return resolvedThemePresentation;
|
|
130
130
|
};
|
|
131
131
|
|
|
132
|
+
const textWidthCssValues = Object.freeze({
|
|
133
|
+
narrow: 'min(60ch, var(--text-width))',
|
|
134
|
+
normal: 'min(72ch, var(--text-width))',
|
|
135
|
+
wide: 'min(72ch, var(--image-layout-width))',
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
export const getTextWidthCssValue = (textWidth) => textWidthCssValues[textWidth];
|
|
139
|
+
|
|
132
140
|
export const resolveSectionSurface = (pagePresentation, sectionIndex) => {
|
|
133
141
|
const surfaceName = pagePresentation.sectionSurfaces[sectionIndex % pagePresentation.sectionSurfaces.length];
|
|
134
142
|
|
|
@@ -2,6 +2,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
import {
|
|
3
3
|
validateConfigYamlStructure,
|
|
4
4
|
} from './site-content.mjs';
|
|
5
|
+
import { navigationModeNames } from './navigation-model.mjs';
|
|
5
6
|
import {
|
|
6
7
|
siteConfigLabel,
|
|
7
8
|
siteConfigPath,
|
|
@@ -182,19 +183,27 @@ const readSiteUrl = (config) => {
|
|
|
182
183
|
|
|
183
184
|
const localeLabels = Object.freeze({
|
|
184
185
|
en: Object.freeze({
|
|
186
|
+
breadcrumb: 'Breadcrumb',
|
|
185
187
|
dismissBanner: 'Dismiss notice',
|
|
188
|
+
built: 'Built',
|
|
186
189
|
images: 'Images',
|
|
190
|
+
navigationChildren: 'Child pages',
|
|
191
|
+
navigationMenu: 'Menu',
|
|
187
192
|
note: 'Note',
|
|
188
|
-
pageNavigation: '
|
|
193
|
+
pageNavigation: 'Page contents',
|
|
189
194
|
siteBanners: 'Site notices',
|
|
190
195
|
siteNavigation: 'Pages',
|
|
191
196
|
skipToContent: 'Skip to content',
|
|
192
197
|
}),
|
|
193
198
|
sv: Object.freeze({
|
|
199
|
+
breadcrumb: 'Brödsmulor',
|
|
194
200
|
dismissBanner: 'Stäng meddelande',
|
|
201
|
+
built: 'Byggd',
|
|
195
202
|
images: 'Bilder',
|
|
203
|
+
navigationChildren: 'Undersidor',
|
|
204
|
+
navigationMenu: 'Meny',
|
|
196
205
|
note: 'Not',
|
|
197
|
-
pageNavigation: '
|
|
206
|
+
pageNavigation: 'Sidinnehåll',
|
|
198
207
|
siteBanners: 'Meddelanden',
|
|
199
208
|
siteNavigation: 'Sidor',
|
|
200
209
|
skipToContent: 'Hoppa till innehållet',
|
|
@@ -228,8 +237,8 @@ const siteUrl = readSiteUrl(rawConfig);
|
|
|
228
237
|
const scrollBehaviorNames = ['instant', 'smooth'];
|
|
229
238
|
|
|
230
239
|
const defaultFontFamily = "Arial, 'Helvetica Neue', Helvetica, sans-serif";
|
|
231
|
-
const
|
|
232
|
-
const
|
|
240
|
+
const contentSpacingNames = ['compact', 'normal', 'spacious'];
|
|
241
|
+
const contentSpacingProfiles = Object.freeze({
|
|
233
242
|
compact: Object.freeze({
|
|
234
243
|
blockGap: Object.freeze({
|
|
235
244
|
desktop: '1.25em',
|
|
@@ -282,7 +291,7 @@ const layoutDensityProfiles = Object.freeze({
|
|
|
282
291
|
mobile: '1.5rem',
|
|
283
292
|
}),
|
|
284
293
|
}),
|
|
285
|
-
|
|
294
|
+
spacious: Object.freeze({
|
|
286
295
|
blockGap: Object.freeze({
|
|
287
296
|
desktop: '1.75em',
|
|
288
297
|
mobile: '1.5em',
|
|
@@ -315,12 +324,19 @@ export const resolveThemeVisualConfig = (theme, sourceLabel = siteThemeLabel) =>
|
|
|
315
324
|
const rawLayoutSpacingConfig = assertObject(rawLayoutConfig.spacing ?? {}, 'layout.spacing', sourceLabel);
|
|
316
325
|
const rawImagesConfig = assertObject(rawThemeConfig.images ?? {}, 'images', sourceLabel);
|
|
317
326
|
const rawTypographyConfig = assertObject(rawThemeConfig.typography ?? {}, 'typography', sourceLabel);
|
|
318
|
-
const
|
|
319
|
-
const resolvedLayoutSpacingDefaults =
|
|
327
|
+
const resolvedContentSpacing = readEnum(rawLayoutConfig, 'contentSpacing', 'layout', contentSpacingNames, 'normal', sourceLabel);
|
|
328
|
+
const resolvedLayoutSpacingDefaults = contentSpacingProfiles[resolvedContentSpacing];
|
|
329
|
+
const shape = readEnum(rawThemeConfig, 'shape', 'theme', ['square', 'soft'], 'soft', sourceLabel);
|
|
320
330
|
|
|
321
331
|
return Object.freeze({
|
|
322
332
|
layout: Object.freeze({
|
|
323
|
-
|
|
333
|
+
contentSpacing: resolvedContentSpacing,
|
|
334
|
+
textWidth: readEnum(rawLayoutConfig, 'textWidth', 'layout', ['narrow', 'normal', 'wide'], 'normal', sourceLabel),
|
|
335
|
+
localNavigationGap: readCssLengthValue(
|
|
336
|
+
rawLayoutConfig.localNavigationGap ?? 'clamp(1.5rem, 3vw, 3rem)',
|
|
337
|
+
'layout.localNavigationGap',
|
|
338
|
+
sourceLabel,
|
|
339
|
+
),
|
|
324
340
|
gutter: readResponsiveCssLength(rawLayoutConfig, 'gutter', 'layout', Object.freeze({
|
|
325
341
|
desktop: 'clamp(1.25rem, 4vw, 3rem)',
|
|
326
342
|
mobile: '1rem',
|
|
@@ -349,6 +365,27 @@ export const resolveThemeVisualConfig = (theme, sourceLabel = siteThemeLabel) =>
|
|
|
349
365
|
typography: Object.freeze({
|
|
350
366
|
fontFamily: readFontFamily(rawTypographyConfig, 'fontFamily', 'typography', defaultFontFamily, sourceLabel),
|
|
351
367
|
}),
|
|
368
|
+
shape: Object.freeze({
|
|
369
|
+
name: shape,
|
|
370
|
+
radiusSmall: shape === 'square' ? '0' : '2px',
|
|
371
|
+
radiusMedium: shape === 'square' ? '0' : '6px',
|
|
372
|
+
radiusLarge: shape === 'square' ? '0' : '8px',
|
|
373
|
+
}),
|
|
374
|
+
});
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
export const resolveNavigationConfig = (config, sourceLabel = siteConfigLabel) => {
|
|
378
|
+
const rawNavigation = assertObject(config.navigation ?? {}, 'navigation', sourceLabel);
|
|
379
|
+
|
|
380
|
+
return Object.freeze({
|
|
381
|
+
mode: readEnum(
|
|
382
|
+
rawNavigation,
|
|
383
|
+
'mode',
|
|
384
|
+
'navigation',
|
|
385
|
+
navigationModeNames,
|
|
386
|
+
'automatic',
|
|
387
|
+
sourceLabel,
|
|
388
|
+
),
|
|
352
389
|
});
|
|
353
390
|
};
|
|
354
391
|
|
|
@@ -359,6 +396,7 @@ export const projectConfig = Object.freeze({
|
|
|
359
396
|
}),
|
|
360
397
|
...resolveThemeVisualConfig(rawTheme, siteThemeLabel),
|
|
361
398
|
navigation: Object.freeze({
|
|
399
|
+
...resolveNavigationConfig(rawConfig, siteConfigLabel),
|
|
362
400
|
scrollBehavior: readEnum(
|
|
363
401
|
rawConfig,
|
|
364
402
|
'scrollBehavior',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'astro/zod';
|
|
2
|
+
import { navigationModeNames } from './navigation-model.mjs';
|
|
2
3
|
import { themePresetNames } from './theme-presets.mjs';
|
|
3
4
|
|
|
4
5
|
const isDateOnly = (value) => {
|
|
@@ -10,13 +11,16 @@ const isDateOnly = (value) => {
|
|
|
10
11
|
|
|
11
12
|
const textAlign = z.enum(['left', 'center', 'right']).describe('Text alignment. Omit an override to keep the active typography profile.');
|
|
12
13
|
const textSize = z.enum(['small', 'medium', 'large', 'xlarge']).describe('Text size from the active typography system. Omit an override to keep the profile value.');
|
|
13
|
-
const textWidth = z.enum(['narrow', 'normal', 'wide']).describe('Maximum width of body text. Omit
|
|
14
|
+
const textWidth = z.enum(['narrow', 'normal', 'wide']).describe('Maximum width of body text. Omit this to keep the inherited page setting.');
|
|
14
15
|
const headingWeight = z.union([z.literal(400), z.literal(500), z.literal(600), z.literal(700)]).describe('CSS font weight.');
|
|
15
16
|
const typographyProfile = z.enum(['restrained', 'dense', 'reading', 'statement']).describe('Coordinated typography defaults. Omit this to use the selected preset.');
|
|
16
17
|
const themePreset = z.enum(themePresetNames).describe('Complete Norna visual preset. Start here and add overrides only when needed.');
|
|
17
18
|
const presentationPalette = z.enum(['dark', 'light', 'paper']).describe('Coordinated site color palette. Omit this to use the selected preset.');
|
|
18
|
-
const sectionSurface = z.enum(['base', 'soft', 'emphasis']).describe('Semantic section surface from the active palette.');
|
|
19
19
|
const spacingDensity = z.enum(['compact', 'normal', 'airy']).describe('Coordinated spacing density. Omit this to use the selected preset.');
|
|
20
|
+
const contentSpacing = z.enum(['compact', 'normal', 'spacious']).describe('Vertical spacing between page sections and structured content blocks.');
|
|
21
|
+
const backgroundPattern = z.enum(['uniform', 'alternating', 'cycling']).describe('How sections cycle through the active palette surfaces.');
|
|
22
|
+
const shapeProfile = z.enum(['square', 'soft']).describe('Site-wide corner treatment for interface and content surfaces.');
|
|
23
|
+
const navigationMode = z.enum(navigationModeNames).describe('Site-wide navigation model. Automatic selects from the site structure.');
|
|
20
24
|
const lineHeight = z.number()
|
|
21
25
|
.min(1, 'Use a unitless line height of at least 1.')
|
|
22
26
|
.max(3, 'Use a unitless line height of at most 3.')
|
|
@@ -71,7 +75,6 @@ const headingLevelsPresentationOverride = z.object({
|
|
|
71
75
|
}).strict().describe('Heading overrides by Markdown level.');
|
|
72
76
|
const bodyPresentationOverride = z.object({
|
|
73
77
|
...commonTextPresentationOverride,
|
|
74
|
-
width: textWidth.optional().describe('Maximum body-text line length.'),
|
|
75
78
|
paragraphSpacing: cssLength.optional().describe('Vertical space between body paragraphs.'),
|
|
76
79
|
}).strict().describe('Focused overrides for body text.');
|
|
77
80
|
const captionPresentationOverride = z.object({
|
|
@@ -119,95 +122,115 @@ const themeLayoutSpacing = z.object({
|
|
|
119
122
|
sectionGap: responsiveCssLength.optional().describe('Vertical separation between page sections.'),
|
|
120
123
|
}).strict().describe('Fine-grained spacing overrides applied after layout density.');
|
|
121
124
|
const themeLayout = z.object({
|
|
122
|
-
|
|
125
|
+
contentSpacing: contentSpacing.optional().describe('Page content spacing. Omit this to keep the selected preset or inherited page setting.'),
|
|
126
|
+
textWidth: textWidth.optional().describe('Body-text line length. Omit this to keep the selected preset or inherited page setting.'),
|
|
123
127
|
pageWidth: visualCssLength.optional().describe('Maximum width of the site layout. Omit this to keep the selected preset.'),
|
|
124
128
|
gutter: responsiveCssLength.optional().describe('Horizontal page gutter as one value or separate desktop and mobile values.'),
|
|
125
129
|
spacing: themeLayoutSpacing.optional().describe('Fine-grained spacing overrides.'),
|
|
126
130
|
}).strict().describe('Optional layout overrides applied after the preset.');
|
|
131
|
+
const pageThemeLayout = z.object({
|
|
132
|
+
contentSpacing: contentSpacing.optional().describe('Page content spacing. Descendant pages inherit this value.'),
|
|
133
|
+
textWidth: textWidth.optional().describe('Body-text line length. Descendant pages inherit this value.'),
|
|
134
|
+
}).strict().refine(
|
|
135
|
+
(value) => value.contentSpacing !== undefined || value.textWidth !== undefined,
|
|
136
|
+
'Specify contentSpacing, textWidth, or both.',
|
|
137
|
+
).describe('Page-local layout settings inherited by descendant pages.');
|
|
127
138
|
const themeImages = z.object({
|
|
128
139
|
width: visualCssLength.optional().describe('Maximum managed-image width. Omit this to keep the selected preset.'),
|
|
129
140
|
maxAvailableWidthPercent: responsivePercent.optional().describe('Maximum percentage of available horizontal space used by managed images.'),
|
|
130
141
|
maxAvailableHeightPercent: responsivePercent.optional().describe('Maximum percentage of viewport height used by managed images.'),
|
|
131
142
|
}).strict().describe('Optional defaults for managed image presentation.');
|
|
132
|
-
const
|
|
133
|
-
(
|
|
134
|
-
|
|
135
|
-
|
|
143
|
+
const configNavigation = z.object({
|
|
144
|
+
mode: navigationMode.optional().describe('Navigation model. Omit this to let Norna choose from the site structure.'),
|
|
145
|
+
}).strict().describe('Site-wide navigation behavior.');
|
|
146
|
+
const themeSections = z.object({
|
|
147
|
+
backgroundPattern: backgroundPattern.optional().describe('Section background sequence. Omit this to keep the selected preset or inherited page setting.'),
|
|
148
|
+
}).strict().describe('Defaults for page section presentation.');
|
|
149
|
+
const pageThemeSections = z.object({
|
|
150
|
+
backgroundPattern: backgroundPattern.optional().describe('Section background sequence inherited by descendant pages.'),
|
|
151
|
+
}).strict().refine(
|
|
152
|
+
(value) => value.backgroundPattern !== undefined,
|
|
153
|
+
'Specify backgroundPattern.',
|
|
154
|
+
).describe('Page-local section presentation inherited by descendant pages.');
|
|
136
155
|
const pageNavigation = z.object({
|
|
137
|
-
|
|
138
|
-
label: z.string().optional().describe('Navigation label when it should differ from the page title.'),
|
|
156
|
+
listed: z.boolean().optional().default(true).describe('List this page in site navigation. The page remains public when false.'),
|
|
139
157
|
}).strict();
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}).strict().describe('
|
|
158
|
+
const pageMetadata = z.object({
|
|
159
|
+
description: z.string().min(1).optional().describe('Optional page-specific meta description.'),
|
|
160
|
+
}).strict().describe('Optional metadata for this homepage or additional page. The Markdown H1 supplies the page title.');
|
|
161
|
+
const sitewideLogo = z.object({
|
|
162
|
+
height: visualCssLength.optional().describe('Displayed logo height. Width follows the intrinsic aspect ratio.'),
|
|
163
|
+
}).strict().describe('Optional display settings for a convention-based navigation logo.');
|
|
146
164
|
|
|
147
|
-
const sectionMetadata = z.object({
|
|
148
|
-
visible: visibilityWindow.optional().describe('Optional date window for this section.'),
|
|
149
|
-
}).strict().describe('Metadata keyed by the explicit Markdown section id. Markdown order remains authoritative.');
|
|
150
165
|
const banner = z.object({
|
|
151
166
|
id: z.string().regex(/^[a-z0-9-]+$/).describe('Stable banner identifier used for dismissal state.'),
|
|
152
167
|
tone: z.enum(['warning']).default('warning').describe('Semantic banner tone.'),
|
|
153
168
|
visible: visibilityWindow.optional().describe('Optional date window for the banner.'),
|
|
154
169
|
title: z.string().min(1).describe('Short banner heading.'),
|
|
155
170
|
text: z.string().min(1).describe('Concise banner message.'),
|
|
156
|
-
}).strict().describe('Dismissible
|
|
171
|
+
}).strict().describe('Dismissible site-wide warning shown above page content for important temporary information.');
|
|
157
172
|
const banners = z.array(banner).optional().default([]).refine(
|
|
158
173
|
(values) => new Set(values.map((value) => value.id)).size === values.length,
|
|
159
174
|
'Banner ids must be unique.',
|
|
160
175
|
);
|
|
161
|
-
const dateTimeFormat = z.object({
|
|
162
|
-
locale: z.string().min(1).describe('Intl locale such as en-GB or sv-SE.'),
|
|
163
|
-
timeZone: z.string().min(1).describe('IANA time zone such as Europe/Stockholm.'),
|
|
164
|
-
dateStyle: z.enum(['short', 'medium', 'long', 'full']).describe('Intl dateStyle for the build timestamp.'),
|
|
165
|
-
timeStyle: z.enum(['short', 'medium', 'long', 'full']).describe('Intl timeStyle for the build timestamp.'),
|
|
166
|
-
}).strict().refine((value) => {
|
|
167
|
-
try {
|
|
168
|
-
new Intl.DateTimeFormat(value.locale, {
|
|
169
|
-
dateStyle: value.dateStyle,
|
|
170
|
-
timeStyle: value.timeStyle,
|
|
171
|
-
timeZone: value.timeZone,
|
|
172
|
-
});
|
|
173
|
-
return true;
|
|
174
|
-
} catch {
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
|
-
}, 'Use a valid Intl.DateTimeFormat configuration.');
|
|
178
176
|
const sitewideFooter = z.object({
|
|
179
177
|
copyrightMessage: z.string().min(1).optional().describe('Copyright or ownership text shown in the site footer.'),
|
|
180
|
-
buildInfo: z.
|
|
181
|
-
enabled: z.boolean().default(true).describe('Show generated build information in the footer.'),
|
|
182
|
-
text: z.string().min(1).describe('Label shown before the generated build timestamp.'),
|
|
183
|
-
dateTimeFormat: dateTimeFormat.describe('Formatting used for the generated build timestamp.'),
|
|
184
|
-
}).strict().optional().describe('Optional generated build timestamp.'),
|
|
178
|
+
buildInfo: z.boolean().optional().default(false).describe('Show a localized generated build timestamp in the footer.'),
|
|
185
179
|
}).strict().describe('Site-wide footer content.');
|
|
186
180
|
|
|
187
|
-
|
|
181
|
+
const configShape = {
|
|
188
182
|
url: z.string().url().describe('Absolute public URL for the built site.'),
|
|
189
183
|
language: z.string().regex(/^(?:en|sv)(?:-[a-zA-Z0-9]+)*$/).optional().describe('Site language tag using Norna\'s English or Swedish interface text; the default is en.'),
|
|
184
|
+
navigation: configNavigation.optional(),
|
|
190
185
|
scrollBehavior: z.enum(['instant', 'smooth']).optional().default('instant').describe('Use instant anchors by default or the browser\'s native smooth scrolling.'),
|
|
191
|
-
}
|
|
186
|
+
};
|
|
192
187
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
description: z.string().describe('Page description used in metadata.'),
|
|
188
|
+
const siteShape = {
|
|
189
|
+
page: pageMetadata.optional(),
|
|
196
190
|
navigation: pageNavigation.optional().describe('Optional navigation metadata for this page.'),
|
|
197
|
-
|
|
198
|
-
}).strict().describe('Frontmatter for a homepage or route content.md file.');
|
|
191
|
+
};
|
|
199
192
|
|
|
200
|
-
|
|
193
|
+
const themeVisualShape = {
|
|
201
194
|
preset: themePreset.optional().describe('Complete visual starting point. Add only the overrides the site actually needs.'),
|
|
195
|
+
shape: shapeProfile.optional().describe('Site-wide shape profile. Omit this to use the selected preset.'),
|
|
202
196
|
layout: themeLayout.optional(),
|
|
203
197
|
images: themeImages.optional(),
|
|
204
198
|
typography: themeTypography.optional(),
|
|
205
199
|
palette: presentationPalette.optional(),
|
|
206
|
-
|
|
207
|
-
}
|
|
200
|
+
sections: themeSections.optional(),
|
|
201
|
+
};
|
|
208
202
|
|
|
209
|
-
|
|
210
|
-
|
|
203
|
+
const pageThemeShape = {
|
|
204
|
+
layout: pageThemeLayout.optional(),
|
|
205
|
+
images: themeImages.optional(),
|
|
206
|
+
sections: pageThemeSections.optional(),
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const sitewideShape = {
|
|
210
|
+
logo: sitewideLogo.optional(),
|
|
211
211
|
banners: banners.describe('Site-wide dismissible notices.'),
|
|
212
212
|
footer: sitewideFooter.optional().describe('Site-wide footer content.'),
|
|
213
|
-
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
export const schemaTopLevelKeys = Object.freeze({
|
|
216
|
+
config: Object.freeze(Object.keys(configShape)),
|
|
217
|
+
content: Object.freeze(Object.keys(siteShape)),
|
|
218
|
+
sitewide: Object.freeze(Object.keys(sitewideShape)),
|
|
219
|
+
theme: Object.freeze(Object.keys(themeVisualShape)),
|
|
220
|
+
pageTheme: Object.freeze(Object.keys(pageThemeShape)),
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
export const configSchema = z.object(configShape).strict()
|
|
224
|
+
.describe('Technical settings for one Norna site. Additional pages cannot provide technical configuration.');
|
|
225
|
+
export const siteSchema = z.object(siteShape).strict()
|
|
226
|
+
.describe('Frontmatter for a homepage or additional page content.md file.');
|
|
227
|
+
export const themeVisualSchema = z.object(themeVisualShape).strict()
|
|
228
|
+
.describe('Site-wide visual identity and default page presentation. Presets may be selected only in the root theme.');
|
|
229
|
+
export const pageThemeSchema = z.object(pageThemeShape).strict()
|
|
230
|
+
.refine(
|
|
231
|
+
(value) => value.layout !== undefined || value.images !== undefined || value.sections !== undefined,
|
|
232
|
+
'Specify layout, images, sections, or a combination of them.',
|
|
233
|
+
)
|
|
234
|
+
.describe('Limited page presentation overrides inherited by descendant pages. Site colors, shapes, typography and navigation remain global.');
|
|
235
|
+
export const sitewideSchema = z.object(sitewideShape).strict()
|
|
236
|
+
.describe('Editorial content and optional navigation logo display settings shared by every page.');
|