@janga/norna 0.7.1 → 0.7.3
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 +32 -18
- package/astro.config.mjs +2 -0
- package/bin/norna-cli.mjs +175 -0
- package/bin/norna.mjs +149 -150
- package/docs/README.md +41 -22
- package/docs/commands.md +25 -12
- package/docs/configuration.md +38 -135
- package/docs/content.md +108 -265
- package/docs/{command-organization.md → design/command-organization.md} +64 -60
- package/docs/{site-examples-structure-note.md → design/site-examples-structure.md} +19 -24
- package/docs/engine-development.md +35 -7
- package/docs/getting-started.md +55 -26
- package/docs/images-and-metadata.md +1 -1
- package/docs/local-development.md +14 -1
- package/docs/publishing.md +24 -1
- package/docs/routes.md +90 -0
- package/docs/site-structure.md +18 -10
- package/docs/theme.md +292 -0
- package/docs/typography.md +144 -0
- package/examples/dog-gallery/.astro/collections/site.schema.json +802 -0
- package/examples/dog-gallery/.astro/collections/theme.schema.json +714 -0
- package/examples/dog-gallery/.astro/content-assets.mjs +4 -0
- package/examples/dog-gallery/.astro/content-modules.mjs +1 -0
- package/examples/dog-gallery/.astro/content.d.ts +175 -0
- package/examples/dog-gallery/.astro/data-store.json +1 -0
- package/examples/dog-gallery/.astro/dev-local.json +8 -0
- package/examples/dog-gallery/.astro/dev.json +13 -0
- package/examples/dog-gallery/.astro/dev.log +49 -0
- package/examples/dog-gallery/.astro/settings.json +5 -0
- package/examples/dog-gallery/.astro/types.d.ts +2 -0
- package/examples/dog-gallery/dist/_astro/GalleryGrid.astro_astro_type_script_index_0_lang.B0kxDyGz.js +1 -0
- package/examples/dog-gallery/dist/_astro/SitePage.BFHZm5UG.css +1 -0
- package/examples/dog-gallery/dist/dog-care/index.html +15 -0
- package/examples/dog-gallery/dist/favicon.svg +7 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1536.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-960.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1920.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-3939.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1920.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-2400.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1600.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1920.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-2576.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-1277.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1536.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-768.webp +0 -0
- package/examples/dog-gallery/dist/index.html +43 -0
- package/examples/dog-gallery/dist/robots.txt +2 -0
- package/examples/dog-gallery/node_modules/.vite/deps/_metadata.json +74 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro-D_GIpJIE.js +434 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro-D_GIpJIE.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/audit-DM9Hkl5B.js +1534 -0
- package/examples/dog-gallery/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/highlight-DnZ1ULOL.js +48 -0
- package/examples/dog-gallery/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/icons-RyebKi45.js +42 -0
- package/examples/dog-gallery/node_modules/.vite/deps/icons-RyebKi45.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/package.json +3 -0
- package/examples/dog-gallery/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
- package/examples/dog-gallery/node_modules/.vite/deps/settings-DaU-gNnK.js +190 -0
- package/examples/dog-gallery/node_modules/.vite/deps/settings-DaU-gNnK.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/toolbar-63PA619H.js +458 -0
- package/examples/dog-gallery/node_modules/.vite/deps/toolbar-63PA619H.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/ui-library--bkcNSYz.js +1016 -0
- package/examples/dog-gallery/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/window-jPkpKVcy.js +32 -0
- package/examples/dog-gallery/node_modules/.vite/deps/window-jPkpKVcy.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/xray-BXCHpEJP.js +111 -0
- package/examples/dog-gallery/node_modules/.vite/deps/xray-BXCHpEJP.js.map +1 -0
- package/examples/dog-gallery/site/.norna/generated-images.json +226 -0
- package/examples/dog-gallery/site/config.mjs +67 -0
- package/examples/dog-gallery/site/content.md +168 -0
- package/examples/dog-gallery/site/images/black-dogs/black-puppy-meadow.png +0 -0
- package/examples/dog-gallery/site/images/black-dogs/photo-of-a-black-dog.jpg +0 -0
- package/examples/dog-gallery/site/images/brown-dogs/brown-dog.jpg +0 -0
- package/examples/dog-gallery/site/images/brown-dogs/dog-accompanies-master.jpg +0 -0
- package/examples/dog-gallery/site/images/golden-dogs/golden-retriever.jpg +0 -0
- package/examples/dog-gallery/site/images/golden-dogs/toller-puppy.jpg +0 -0
- package/examples/dog-gallery/site/images/white-dogs/white-cute-dog.jpg +0 -0
- package/examples/dog-gallery/site/images/white-dogs/white-puppy-garden.png +0 -0
- package/examples/dog-gallery/site/public/favicon.svg +7 -0
- package/examples/dog-gallery/site/public/robots.txt +2 -0
- package/examples/dog-gallery/site/routes/dog-care/route-content.md +49 -0
- package/examples/dog-gallery/site/theme.md +114 -0
- package/fixtures/basic/site/config.mjs +3 -22
- package/fixtures/basic/site/content.md +1 -1
- package/fixtures/basic/site/theme.md +7 -3
- package/package.json +10 -9
- package/scripts/check-config.mjs +4 -3
- package/scripts/deploy-site.mjs +3 -3
- package/scripts/dev-local.mjs +64 -15
- package/scripts/init-site.mjs +11 -9
- package/scripts/lib/frontmatter-yaml.mjs +85 -0
- package/scripts/lib/presentation.mjs +10 -11
- package/scripts/lib/project-config.mjs +113 -45
- package/scripts/lib/site-content.mjs +21 -2
- package/scripts/lib/site-paths.mjs +18 -3
- package/scripts/lib/theme-config.mjs +34 -0
- package/scripts/lib/typography.mjs +103 -23
- package/scripts/show-typography.mjs +202 -75
- package/scripts/sync-content-sections.mjs +6 -6
- package/scripts/test-cli-discovery.mjs +124 -0
- package/scripts/test-content-check.mjs +3 -4
- package/scripts/test-engine-commands.mjs +20 -6
- package/scripts/test-navigation.mjs +10 -6
- package/scripts/test-package-check.mjs +40 -11
- package/scripts/test-temporary-visibility.mjs +2 -3
- package/src/components/SiteNavigation.astro +7 -5
- package/src/components/SitePage.astro +5 -3
- package/src/components/SiteSection.astro +63 -29
- package/src/content.config.ts +64 -2
- package/src/layouts/BaseLayout.astro +19 -1
- package/src/lib/basePath.ts +21 -0
- package/src/lib/generatedImages.ts +8 -4
- package/src/lib/sectionContent.ts +6 -1
- package/src/lib/sitePublicAssets.ts +8 -1
- package/src/styles/global.css +59 -55
- package/starters/basic/.github/workflows/deploy.yml +3 -3
- package/starters/basic/README.md +30 -14
- package/starters/basic/package.json +3 -3
- package/starters/basic/site/config.mjs +4 -24
- package/starters/basic/site/content.md +44 -9
- package/starters/basic/site/theme.md +113 -16
- package/starters/project/.github/workflows/deploy.yml +65 -0
- package/starters/project/README.md +72 -0
- package/starters/project/package-lock.json +4296 -0
- package/starters/project/package.json +35 -0
- package/starters/project/site/config.mjs +43 -0
- package/starters/project/site/content.md +88 -0
- package/starters/project/site/public/robots.txt +2 -0
- package/starters/project/site/routes/guide/route-content.md +58 -0
- package/starters/project/site/theme.md +15 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { mkdir, mkdtemp, realpath, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
|
+
const launcherPath = path.join(repoRoot, 'bin', 'norna.mjs');
|
|
10
|
+
const tempRoot = await mkdtemp(path.join(tmpdir(), 'norna cli discovery-'));
|
|
11
|
+
|
|
12
|
+
const runLauncher = (args, cwd) => spawnSync(process.execPath, [launcherPath, ...args], {
|
|
13
|
+
cwd,
|
|
14
|
+
encoding: 'utf8',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const writeJson = (filePath, value) => writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
18
|
+
|
|
19
|
+
const createFakeNornaInstall = async (projectRoot, version = '1.7.0') => {
|
|
20
|
+
const packageRoot = path.join(projectRoot, 'node_modules', '@janga', 'norna');
|
|
21
|
+
const binDir = path.join(packageRoot, 'bin');
|
|
22
|
+
await mkdir(binDir, { recursive: true });
|
|
23
|
+
await writeJson(path.join(packageRoot, 'package.json'), {
|
|
24
|
+
name: '@janga/norna',
|
|
25
|
+
version,
|
|
26
|
+
type: 'module',
|
|
27
|
+
bin: {
|
|
28
|
+
norna: 'bin/norna.mjs',
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
await writeFile(path.join(binDir, 'norna.mjs'), `#!/usr/bin/env node
|
|
32
|
+
const payload = {
|
|
33
|
+
marker: 'fake-local-norna',
|
|
34
|
+
version: ${JSON.stringify(version)},
|
|
35
|
+
argv: process.argv.slice(2),
|
|
36
|
+
cwd: process.cwd()
|
|
37
|
+
};
|
|
38
|
+
console.log(JSON.stringify(payload));
|
|
39
|
+
if (process.argv.includes('--exit-7')) process.exit(7);
|
|
40
|
+
`);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const createProject = async (directoryName, {
|
|
44
|
+
declareDependency = true,
|
|
45
|
+
installNorna = true,
|
|
46
|
+
version = '1.7.0',
|
|
47
|
+
} = {}) => {
|
|
48
|
+
const projectRoot = path.join(tempRoot, directoryName);
|
|
49
|
+
await mkdir(projectRoot, { recursive: true });
|
|
50
|
+
await writeJson(path.join(projectRoot, 'package.json'), {
|
|
51
|
+
name: directoryName.toLowerCase().replaceAll(/\s+/g, '-'),
|
|
52
|
+
private: true,
|
|
53
|
+
type: 'module',
|
|
54
|
+
...(declareDependency
|
|
55
|
+
? { dependencies: { '@janga/norna': version } }
|
|
56
|
+
: {}),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (installNorna) {
|
|
60
|
+
await createFakeNornaInstall(projectRoot, version);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return projectRoot;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const noProjectResult = runLauncher(['--help'], tempRoot);
|
|
68
|
+
assert.equal(noProjectResult.status, 0, noProjectResult.stderr || noProjectResult.stdout);
|
|
69
|
+
assert.match(noProjectResult.stdout, /Usage: norna <command>/);
|
|
70
|
+
assert.doesNotMatch(noProjectResult.stdout, /fake-local-norna/);
|
|
71
|
+
|
|
72
|
+
const undeclaredProject = await createProject('undeclared project', {
|
|
73
|
+
declareDependency: false,
|
|
74
|
+
installNorna: true,
|
|
75
|
+
});
|
|
76
|
+
const undeclaredResult = runLauncher(['--help'], undeclaredProject);
|
|
77
|
+
assert.equal(undeclaredResult.status, 0, undeclaredResult.stderr || undeclaredResult.stdout);
|
|
78
|
+
assert.match(undeclaredResult.stdout, /Usage: norna <command>/);
|
|
79
|
+
assert.doesNotMatch(undeclaredResult.stdout, /fake-local-norna/);
|
|
80
|
+
|
|
81
|
+
const localProject = await createProject('local project', { version: '1.7.0' });
|
|
82
|
+
const localResult = runLauncher(['build', '--flag', 'value with spaces'], localProject);
|
|
83
|
+
assert.equal(localResult.status, 0, localResult.stderr || localResult.stdout);
|
|
84
|
+
const localPayload = JSON.parse(localResult.stdout.trim());
|
|
85
|
+
assert.equal(localPayload.marker, 'fake-local-norna');
|
|
86
|
+
assert.equal(localPayload.version, '1.7.0');
|
|
87
|
+
assert.deepEqual(localPayload.argv, ['build', '--flag', 'value with spaces']);
|
|
88
|
+
assert.equal(await realpath(localPayload.cwd), await realpath(localProject));
|
|
89
|
+
|
|
90
|
+
const subdirectory = path.join(localProject, 'site', 'images', 'work');
|
|
91
|
+
await mkdir(subdirectory, { recursive: true });
|
|
92
|
+
const subdirectoryResult = runLauncher(['doctor'], subdirectory);
|
|
93
|
+
assert.equal(subdirectoryResult.status, 0, subdirectoryResult.stderr || subdirectoryResult.stdout);
|
|
94
|
+
const subdirectoryPayload = JSON.parse(subdirectoryResult.stdout.trim());
|
|
95
|
+
assert.equal(subdirectoryPayload.marker, 'fake-local-norna');
|
|
96
|
+
assert.deepEqual(subdirectoryPayload.argv, ['doctor']);
|
|
97
|
+
assert.equal(await realpath(subdirectoryPayload.cwd), await realpath(subdirectory));
|
|
98
|
+
|
|
99
|
+
const spacedProject = await createProject('project with spaces', { version: '1.9.0' });
|
|
100
|
+
const spacedResult = runLauncher(['engine:version'], spacedProject);
|
|
101
|
+
assert.equal(spacedResult.status, 0, spacedResult.stderr || spacedResult.stdout);
|
|
102
|
+
const spacedPayload = JSON.parse(spacedResult.stdout.trim());
|
|
103
|
+
assert.equal(spacedPayload.version, '1.9.0');
|
|
104
|
+
assert.equal(await realpath(spacedPayload.cwd), await realpath(spacedProject));
|
|
105
|
+
|
|
106
|
+
const exitResult = runLauncher(['build', '--exit-7'], localProject);
|
|
107
|
+
assert.equal(exitResult.status, 7, exitResult.stderr || exitResult.stdout);
|
|
108
|
+
const exitPayload = JSON.parse(exitResult.stdout.trim());
|
|
109
|
+
assert.deepEqual(exitPayload.argv, ['build', '--exit-7']);
|
|
110
|
+
|
|
111
|
+
const engineRepoResult = runLauncher(['--help'], repoRoot);
|
|
112
|
+
assert.equal(engineRepoResult.status, 0, engineRepoResult.stderr || engineRepoResult.stdout);
|
|
113
|
+
assert.match(engineRepoResult.stdout, /Usage: norna <command>/);
|
|
114
|
+
assert.doesNotMatch(engineRepoResult.stdout, /fake-local-norna/);
|
|
115
|
+
|
|
116
|
+
const engineStarterResult = runLauncher(['--help'], path.join(repoRoot, 'starters', 'basic'));
|
|
117
|
+
assert.equal(engineStarterResult.status, 0, engineStarterResult.stderr || engineStarterResult.stdout);
|
|
118
|
+
assert.match(engineStarterResult.stdout, /Usage: norna <command>/);
|
|
119
|
+
assert.doesNotMatch(engineStarterResult.stdout, /fake-local-norna/);
|
|
120
|
+
|
|
121
|
+
console.log('ok - cli launcher discovers local norna installations and avoids self-delegation');
|
|
122
|
+
} finally {
|
|
123
|
+
await rm(tempRoot, { force: true, recursive: true });
|
|
124
|
+
}
|
|
@@ -43,9 +43,8 @@ const makePngHeader = ({ width, height }) => {
|
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
const defaultTheme = `---
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
preset: quiet-gallery
|
|
46
|
+
typography:
|
|
47
|
+
preset: quiet-gallery
|
|
49
48
|
frame:
|
|
50
49
|
colors: presentation
|
|
51
50
|
---
|
|
@@ -114,7 +113,7 @@ test('content:check groups section issues, global issues, and unreferenced image
|
|
|
114
113
|
|
|
115
114
|
assert.equal(result.status, 1, output);
|
|
116
115
|
assert.match(output, /^Content check failed\./m);
|
|
117
|
-
assert.match(output, /Section
|
|
116
|
+
assert.match(output, /Section And Image Issues\n\n\[min-konst\]\n Errors:/);
|
|
118
117
|
assert.match(output, /Image "vav\.jpeg" is used here but is located in site\/images\/mitt-hem\/\./);
|
|
119
118
|
assert.match(output, /Image "missing\.jpeg" does not exist anywhere under site\/images\/\./);
|
|
120
119
|
assert.match(output, /Image "duplicate\.jpg" is referenced more than once in this section\./);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
|
-
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { mkdir, mkdtemp, readFile, realpath, rm, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
@@ -23,6 +23,18 @@ try {
|
|
|
23
23
|
assert.match(versionResult.stdout, new RegExp(`Installed norna: ${packageJson.version.replaceAll('.', '\\.')}`));
|
|
24
24
|
assert.match(versionResult.stdout, /Installed Astro: /);
|
|
25
25
|
|
|
26
|
+
const directSiteRoot = path.join(tempRoot, 'current-directory-site');
|
|
27
|
+
await mkdir(directSiteRoot, { recursive: true });
|
|
28
|
+
await writeFile(path.join(directSiteRoot, 'config.mjs'), 'export default { site: { url: "https://example.com/" } };\n');
|
|
29
|
+
await writeFile(path.join(directSiteRoot, 'content.md'), '---\ntitle: Direct Site\nsections: []\n---\n');
|
|
30
|
+
const directSiteProjectRoot = await realpath(tempRoot);
|
|
31
|
+
const directSiteDoctorResult = runCli(['doctor'], {
|
|
32
|
+
cwd: directSiteRoot,
|
|
33
|
+
});
|
|
34
|
+
assert.equal(directSiteDoctorResult.status, 0, directSiteDoctorResult.stderr || directSiteDoctorResult.stdout);
|
|
35
|
+
assert.ok(directSiteDoctorResult.stdout.includes(`Site project root: ${directSiteProjectRoot}`));
|
|
36
|
+
assert.match(directSiteDoctorResult.stdout, /Site directory: current-directory-site/);
|
|
37
|
+
|
|
26
38
|
const initializedSiteRoot = path.join(tempRoot, 'initialized-site');
|
|
27
39
|
const initResult = runCli(['init', initializedSiteRoot]);
|
|
28
40
|
assert.equal(initResult.status, 0, initResult.stderr || initResult.stdout);
|
|
@@ -35,16 +47,18 @@ try {
|
|
|
35
47
|
assert.equal(initializedPackageJson.scripts['engine:update'], undefined);
|
|
36
48
|
assert.equal(initializedPackageJson.scripts['engine:version'], undefined);
|
|
37
49
|
|
|
38
|
-
const presetsResult = runCli(['typography
|
|
50
|
+
const presetsResult = runCli(['typography', 'presets']);
|
|
39
51
|
assert.equal(presetsResult.status, 0, presetsResult.stderr || presetsResult.stdout);
|
|
40
52
|
assert.match(presetsResult.stdout, /quiet-gallery:/);
|
|
41
53
|
assert.match(presetsResult.stdout, /text-forward:/);
|
|
42
54
|
|
|
43
|
-
const showResult = runCli(['--site-dir', path.join(initializedSiteRoot, 'site'), 'typography
|
|
55
|
+
const showResult = runCli(['--site-dir', path.join(initializedSiteRoot, 'site'), 'typography', 'show']);
|
|
44
56
|
assert.equal(showResult.status, 0, showResult.stderr || showResult.stdout);
|
|
45
57
|
assert.match(showResult.stdout, /theme:/);
|
|
46
|
-
assert.match(showResult.stdout, /
|
|
47
|
-
assert.match(showResult.stdout,
|
|
58
|
+
assert.match(showResult.stdout, /pages:/);
|
|
59
|
+
assert.match(showResult.stdout, /\s+\/:/);
|
|
60
|
+
assert.match(showResult.stdout, /value: quiet-gallery/);
|
|
61
|
+
assert.match(showResult.stdout, /inherited: true/);
|
|
48
62
|
assert.match(showResult.stdout, /intro:/);
|
|
49
63
|
|
|
50
64
|
const initAgainResult = runCli(['init', initializedSiteRoot]);
|
|
@@ -55,7 +69,7 @@ try {
|
|
|
55
69
|
const customPureInitResult = runCli(['init', customPureSiteRoot, '--type', 'pure', '--site-dir', 'presentation']);
|
|
56
70
|
assert.equal(customPureInitResult.status, 0, customPureInitResult.stderr || customPureInitResult.stdout);
|
|
57
71
|
const customPurePackageJson = JSON.parse(await readFile(path.join(customPureSiteRoot, 'package.json'), 'utf8'));
|
|
58
|
-
assert.equal(customPurePackageJson.scripts.dev, 'npm run norna:dev');
|
|
72
|
+
assert.equal(customPurePackageJson.scripts.dev, 'npm run norna:dev --');
|
|
59
73
|
assert.equal(customPurePackageJson.scripts.build, 'npm run norna:build');
|
|
60
74
|
assert.equal(customPurePackageJson.scripts['norna:dev'], 'norna --site-dir presentation dev:local');
|
|
61
75
|
assert.equal(customPurePackageJson.scripts['norna:build'], 'norna --site-dir presentation build');
|
|
@@ -6,6 +6,8 @@ import { promisify } from 'node:util';
|
|
|
6
6
|
const execFileAsync = promisify(execFile);
|
|
7
7
|
|
|
8
8
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
|
+
const cliPath = path.join(root, 'bin', 'norna.mjs');
|
|
10
|
+
const dogGallerySiteDir = path.join(root, 'examples', 'dog-gallery', 'site');
|
|
9
11
|
const url = 'http://localhost:4321/';
|
|
10
12
|
const probeUrls = [url, 'http://127.0.0.1:4321/', 'http://[::1]:4321/'];
|
|
11
13
|
const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
@@ -53,9 +55,10 @@ const waitForServer = async () => {
|
|
|
53
55
|
throw new Error(`Timed out waiting for ${url}`);
|
|
54
56
|
};
|
|
55
57
|
|
|
56
|
-
const runCapture = async (command, args) => execFileAsync(command, args, {
|
|
58
|
+
const runCapture = async (command, args, options = {}) => execFileAsync(command, args, {
|
|
57
59
|
cwd: root,
|
|
58
60
|
maxBuffer: 1024 * 1024 * 10,
|
|
61
|
+
...options,
|
|
59
62
|
});
|
|
60
63
|
|
|
61
64
|
const runInherit = (command, args, options = {}) => new Promise((resolve, reject) => {
|
|
@@ -80,11 +83,10 @@ const runInherit = (command, args, options = {}) => new Promise((resolve, reject
|
|
|
80
83
|
});
|
|
81
84
|
|
|
82
85
|
const ensureServer = async () => {
|
|
83
|
-
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
+
await stopServer();
|
|
86
87
|
|
|
87
|
-
await runInherit(
|
|
88
|
+
await runInherit(process.execPath, [cliPath, 'dev:local'], {
|
|
89
|
+
cwd: dogGallerySiteDir,
|
|
88
90
|
env: {
|
|
89
91
|
...process.env,
|
|
90
92
|
WALDE_NO_OPEN: '1',
|
|
@@ -96,7 +98,9 @@ const ensureServer = async () => {
|
|
|
96
98
|
};
|
|
97
99
|
|
|
98
100
|
const stopServer = async () => {
|
|
99
|
-
await runCapture(
|
|
101
|
+
await runCapture(process.execPath, [cliPath, 'dev:stop'], {
|
|
102
|
+
cwd: dogGallerySiteDir,
|
|
103
|
+
}).catch(() => {});
|
|
100
104
|
};
|
|
101
105
|
|
|
102
106
|
let startedServer = false;
|
|
@@ -176,9 +176,23 @@ try {
|
|
|
176
176
|
packageJson.name = 'norna-package-check-site';
|
|
177
177
|
packageJson.dependencies['@janga/norna'] = tarballPath;
|
|
178
178
|
await writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
179
|
+
const packageCheckConfigPath = path.join(siteProjectRoot, 'site', 'config.mjs');
|
|
180
|
+
const packageCheckConfig = await readFile(packageCheckConfigPath, 'utf8');
|
|
181
|
+
await writeFile(
|
|
182
|
+
packageCheckConfigPath,
|
|
183
|
+
packageCheckConfig
|
|
184
|
+
.replace("url: 'https://example.com/'", "url: 'https://example.com/site/'")
|
|
185
|
+
.replace("basePath: '/'", "basePath: '/site/'"),
|
|
186
|
+
);
|
|
187
|
+
const packageCheckThemePath = path.join(siteProjectRoot, 'site', 'theme.md');
|
|
188
|
+
const packageCheckTheme = await readFile(packageCheckThemePath, 'utf8');
|
|
189
|
+
await writeFile(
|
|
190
|
+
packageCheckThemePath,
|
|
191
|
+
packageCheckTheme.replace('brand: Example Site', 'brand: Package Check Brand'),
|
|
192
|
+
);
|
|
179
193
|
await mkdir(path.join(siteProjectRoot, 'site', 'routes', 'about'), { recursive: true });
|
|
180
194
|
await writeFile(path.join(siteProjectRoot, 'site', 'routes', 'about', 'route-content.md'), `---
|
|
181
|
-
title: About the
|
|
195
|
+
title: About the site
|
|
182
196
|
description: Route used by package checks.
|
|
183
197
|
navigation:
|
|
184
198
|
label: About
|
|
@@ -221,7 +235,7 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
221
235
|
);
|
|
222
236
|
await assertFileIncludes(
|
|
223
237
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
224
|
-
'--section-heading-font-size-desktop: clamp(1.
|
|
238
|
+
'--section-heading-font-size-desktop: clamp(1.6rem, 1.22rem + 1.45vw, 2.75rem)',
|
|
225
239
|
);
|
|
226
240
|
await assertFileIncludes(
|
|
227
241
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
@@ -233,11 +247,15 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
233
247
|
);
|
|
234
248
|
await assertFileIncludes(
|
|
235
249
|
path.join(siteProjectRoot, 'dist', 'about', 'index.html'),
|
|
236
|
-
'<link rel="canonical" href="https://example.com/about/">',
|
|
250
|
+
'<link rel="canonical" href="https://example.com/site/about/">',
|
|
237
251
|
);
|
|
238
252
|
await assertFileIncludes(
|
|
239
253
|
path.join(siteProjectRoot, 'dist', 'about', 'index.html'),
|
|
240
|
-
'href="/about/" aria-current="page"',
|
|
254
|
+
'href="/site/about/" aria-current="page"',
|
|
255
|
+
);
|
|
256
|
+
await assertFileIncludes(
|
|
257
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
258
|
+
'<a class="site-brand" href="/site/">Package Check Brand</a>',
|
|
241
259
|
);
|
|
242
260
|
await assertFileIncludes(
|
|
243
261
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
@@ -288,7 +306,7 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
288
306
|
await assertFileExists(path.join(siteProjectRoot, 'dist', 'favicon.ico'));
|
|
289
307
|
await assertFileIncludes(
|
|
290
308
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
291
|
-
'<link rel="icon" sizes="any" href="/favicon.ico">',
|
|
309
|
+
'<link rel="icon" sizes="any" href="/site/favicon.ico">',
|
|
292
310
|
);
|
|
293
311
|
await assertFileExcludes(
|
|
294
312
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
@@ -298,12 +316,12 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
298
316
|
const siteContent = await readFile(siteContentPath, 'utf8');
|
|
299
317
|
const siteThemePath = path.join(siteProjectRoot, 'site', 'theme.md');
|
|
300
318
|
const siteTheme = await readFile(siteThemePath, 'utf8');
|
|
301
|
-
await writeFile(siteThemePath, siteTheme.replace('\n
|
|
319
|
+
await writeFile(siteThemePath, siteTheme.replace('\n preset: quiet-gallery', '\n preset: noisy-gallery'));
|
|
302
320
|
await runExpectFailure(
|
|
303
321
|
npxBin,
|
|
304
322
|
['norna', 'build'],
|
|
305
323
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
306
|
-
'
|
|
324
|
+
'typography.preset',
|
|
307
325
|
);
|
|
308
326
|
await writeFile(
|
|
309
327
|
siteThemePath,
|
|
@@ -372,11 +390,9 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
372
390
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
373
391
|
'sections.0.presentation.typography.preset',
|
|
374
392
|
);
|
|
375
|
-
const siteConfigPath = path.join(siteProjectRoot, 'site', 'config.mjs');
|
|
376
|
-
const siteConfig = await readFile(siteConfigPath, 'utf8');
|
|
377
393
|
await writeFile(
|
|
378
|
-
|
|
379
|
-
|
|
394
|
+
siteThemePath,
|
|
395
|
+
siteTheme.replace(
|
|
380
396
|
'fontFamily: "Arial, \'Helvetica Neue\', Helvetica, sans-serif"',
|
|
381
397
|
'fontFamily: "Arial; color: red"',
|
|
382
398
|
),
|
|
@@ -387,6 +403,19 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
387
403
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
388
404
|
'typography.fontFamily',
|
|
389
405
|
);
|
|
406
|
+
await writeFile(siteThemePath, siteTheme);
|
|
407
|
+
const siteConfigPath = path.join(siteProjectRoot, 'site', 'config.mjs');
|
|
408
|
+
const siteConfig = await readFile(siteConfigPath, 'utf8');
|
|
409
|
+
await writeFile(
|
|
410
|
+
siteConfigPath,
|
|
411
|
+
siteConfig.replace("basePath: '/site/'", "basePath: 'site'"),
|
|
412
|
+
);
|
|
413
|
+
await runExpectFailure(
|
|
414
|
+
npxBin,
|
|
415
|
+
['norna', 'config:check'],
|
|
416
|
+
{ cwd: siteProjectRoot, env: npmEnv },
|
|
417
|
+
'site.basePath',
|
|
418
|
+
);
|
|
390
419
|
|
|
391
420
|
console.log('Package check passed.');
|
|
392
421
|
} finally {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { CollectionEntry } from 'astro:content';
|
|
3
3
|
import projectConfig from '../../scripts/lib/project-config.mjs';
|
|
4
|
+
import { withBasePath } from '../lib/basePath';
|
|
4
5
|
import type { SitePage } from '../lib/sitePages';
|
|
5
6
|
|
|
6
7
|
type SiteSection = CollectionEntry<'site'>['data']['sections'][number];
|
|
@@ -11,13 +12,14 @@ type FrameColors = {
|
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
interface Props {
|
|
15
|
+
brandLabel: string;
|
|
14
16
|
currentPage: SitePage;
|
|
15
17
|
pages: SitePage[];
|
|
16
18
|
sections: ResolvedSection[];
|
|
17
19
|
frameColors: FrameColors;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
const { currentPage, pages, sections, frameColors } = Astro.props;
|
|
22
|
+
const { brandLabel, currentPage, pages, sections, frameColors } = Astro.props;
|
|
21
23
|
const smoothScrollConfig = projectConfig.navigation.smoothScroll;
|
|
22
24
|
const navigationStyle = [
|
|
23
25
|
`--site-top-background-color: ${frameColors.backgroundColor}`,
|
|
@@ -26,8 +28,8 @@ const navigationStyle = [
|
|
|
26
28
|
const showSiteNavigation = pages.length > 1;
|
|
27
29
|
const showPageNavigation = sections.length > 1;
|
|
28
30
|
const homePage = pages.find((page) => page.isHome) ?? currentPage;
|
|
29
|
-
const brandLabel = homePage.title;
|
|
30
31
|
const isCurrentPage = (page: SitePage) => page.pathname === currentPage.pathname;
|
|
32
|
+
const pageHref = (page: SitePage) => withBasePath(projectConfig.site.basePath, page.pathname);
|
|
31
33
|
---
|
|
32
34
|
|
|
33
35
|
<a class="skip-link" href="#main-content">{projectConfig.locale.labels.skipToContent}</a>
|
|
@@ -39,13 +41,13 @@ const isCurrentPage = (page: SitePage) => page.pathname === currentPage.pathname
|
|
|
39
41
|
>
|
|
40
42
|
{showSiteNavigation && (
|
|
41
43
|
<div class="site-nav-row">
|
|
42
|
-
<a class="site-brand" href={homePage
|
|
44
|
+
<a class="site-brand" href={pageHref(homePage)}>{brandLabel}</a>
|
|
43
45
|
|
|
44
46
|
<nav class="site-nav" aria-label={projectConfig.locale.labels.siteNavigation}>
|
|
45
47
|
<ul>
|
|
46
48
|
{pages.map((page) => (
|
|
47
49
|
<li>
|
|
48
|
-
<a href={page
|
|
50
|
+
<a href={pageHref(page)} aria-current={isCurrentPage(page) ? 'page' : undefined}>
|
|
49
51
|
{page.navigation.label}
|
|
50
52
|
</a>
|
|
51
53
|
</li>
|
|
@@ -63,7 +65,7 @@ const isCurrentPage = (page: SitePage) => page.pathname === currentPage.pathname
|
|
|
63
65
|
<ul>
|
|
64
66
|
{pages.map((page) => (
|
|
65
67
|
<li>
|
|
66
|
-
<a href={page
|
|
68
|
+
<a href={pageHref(page)} aria-current={isCurrentPage(page) ? 'page' : undefined}>
|
|
67
69
|
{page.navigation.label}
|
|
68
70
|
</a>
|
|
69
71
|
</li>
|
|
@@ -24,11 +24,12 @@ const allEntries = await getCollection('site');
|
|
|
24
24
|
const sitePages = getSitePages(allEntries);
|
|
25
25
|
const currentPage = getSitePage(entry);
|
|
26
26
|
const navigationPages = getNavigationPages(sitePages);
|
|
27
|
+
const homePage = sitePages.find((page) => page.isHome) ?? currentPage;
|
|
27
28
|
const themeData = theme?.data ?? {};
|
|
28
|
-
const
|
|
29
|
+
const brandLabel = themeData.navigation?.brand ?? homePage.title;
|
|
30
|
+
const pagePresentation = resolvePagePresentation(themeData, entry.data.presentation);
|
|
29
31
|
const frameColors = resolveFrameColors({
|
|
30
|
-
|
|
31
|
-
themeFrame: themeData.frame,
|
|
32
|
+
theme: themeData,
|
|
32
33
|
pagePresentation,
|
|
33
34
|
pageFrame: entry.data.frame,
|
|
34
35
|
});
|
|
@@ -50,6 +51,7 @@ if (visibleSections.length === 0) {
|
|
|
50
51
|
pathname={currentPage.pathname}
|
|
51
52
|
>
|
|
52
53
|
<SiteNavigation
|
|
54
|
+
brandLabel={brandLabel}
|
|
53
55
|
currentPage={currentPage}
|
|
54
56
|
pages={navigationPages}
|
|
55
57
|
sections={visibleSections}
|
|
@@ -32,58 +32,61 @@ interface Props {
|
|
|
32
32
|
|
|
33
33
|
const { section, pagePresentation, headingLevel, priorityGalleryImage = false } = Astro.props;
|
|
34
34
|
const HeadingTag = headingLevel === 1 ? 'h1' : 'h2';
|
|
35
|
+
// Sections are authored with Markdown ## headings even when the first one renders as HTML h1.
|
|
36
|
+
const sectionHeadingVisualLevel = 'h2';
|
|
37
|
+
type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4';
|
|
35
38
|
const headingSizeValues: Record<TextSize, ResponsiveValue<string>> = {
|
|
36
39
|
small: {
|
|
37
|
-
desktop: 'clamp(1.
|
|
38
|
-
mobile: 'clamp(1.
|
|
40
|
+
desktop: 'clamp(1.1rem, 1.02rem + 0.35vw, 1.35rem)',
|
|
41
|
+
mobile: 'clamp(1.12rem, 1rem + 0.85vw, 1.42rem)',
|
|
39
42
|
},
|
|
40
43
|
medium: {
|
|
41
|
-
desktop: 'clamp(1.
|
|
42
|
-
mobile: 'clamp(1.
|
|
44
|
+
desktop: 'clamp(1.35rem, 1.1rem + 0.9vw, 2rem)',
|
|
45
|
+
mobile: 'clamp(1.3rem, 1.05rem + 1.35vw, 1.75rem)',
|
|
43
46
|
},
|
|
44
47
|
large: {
|
|
45
|
-
desktop: 'clamp(1.
|
|
46
|
-
mobile: 'clamp(1.
|
|
48
|
+
desktop: 'clamp(1.6rem, 1.22rem + 1.45vw, 2.75rem)',
|
|
49
|
+
mobile: 'clamp(1.5rem, 1.12rem + 2vw, 2.25rem)',
|
|
47
50
|
},
|
|
48
51
|
xlarge: {
|
|
49
|
-
desktop: 'clamp(2rem,
|
|
50
|
-
mobile: 'clamp(
|
|
52
|
+
desktop: 'clamp(2rem, 1.35rem + 2.4vw, 3.75rem)',
|
|
53
|
+
mobile: 'clamp(1.8rem, 1.25rem + 3vw, 2.85rem)',
|
|
51
54
|
},
|
|
52
55
|
};
|
|
53
56
|
const bodySizeValues: Record<TextSize, ResponsiveValue<string>> = {
|
|
54
57
|
small: {
|
|
55
|
-
desktop: 'clamp(0.
|
|
56
|
-
mobile: 'clamp(0.
|
|
58
|
+
desktop: 'clamp(0.86rem, 0.84rem + 0.08vw, 0.92rem)',
|
|
59
|
+
mobile: 'clamp(0.86rem, 0.84rem + 0.08vw, 0.92rem)',
|
|
57
60
|
},
|
|
58
61
|
medium: {
|
|
59
|
-
desktop: 'clamp(
|
|
60
|
-
mobile: 'clamp(
|
|
62
|
+
desktop: 'clamp(0.96rem, 0.94rem + 0.08vw, 1rem)',
|
|
63
|
+
mobile: 'clamp(0.96rem, 0.94rem + 0.08vw, 1rem)',
|
|
61
64
|
},
|
|
62
65
|
large: {
|
|
63
|
-
desktop: 'clamp(1.
|
|
64
|
-
mobile: 'clamp(1.
|
|
66
|
+
desktop: 'clamp(1.05rem, 1rem + 0.16vw, 1.12rem)',
|
|
67
|
+
mobile: 'clamp(1.05rem, 1rem + 0.16vw, 1.12rem)',
|
|
65
68
|
},
|
|
66
69
|
xlarge: {
|
|
67
|
-
desktop: 'clamp(1.
|
|
68
|
-
mobile: 'clamp(1.
|
|
70
|
+
desktop: 'clamp(1.14rem, 1.06rem + 0.26vw, 1.25rem)',
|
|
71
|
+
mobile: 'clamp(1.14rem, 1.06rem + 0.26vw, 1.25rem)',
|
|
69
72
|
},
|
|
70
73
|
};
|
|
71
74
|
const captionSizeValues: Record<TextSize, ResponsiveValue<string>> = {
|
|
72
75
|
small: {
|
|
73
|
-
desktop: 'clamp(0.
|
|
74
|
-
mobile: 'clamp(0.
|
|
76
|
+
desktop: 'clamp(0.78rem, 0.76rem + 0.08vw, 0.84rem)',
|
|
77
|
+
mobile: 'clamp(0.78rem, 0.76rem + 0.08vw, 0.84rem)',
|
|
75
78
|
},
|
|
76
79
|
medium: {
|
|
77
|
-
desktop: 'clamp(0.
|
|
78
|
-
mobile: 'clamp(0.
|
|
80
|
+
desktop: 'clamp(0.86rem, 0.84rem + 0.08vw, 0.92rem)',
|
|
81
|
+
mobile: 'clamp(0.86rem, 0.84rem + 0.08vw, 0.92rem)',
|
|
79
82
|
},
|
|
80
83
|
large: {
|
|
81
|
-
desktop: 'clamp(
|
|
82
|
-
mobile: 'clamp(
|
|
84
|
+
desktop: 'clamp(0.96rem, 0.92rem + 0.12vw, 1rem)',
|
|
85
|
+
mobile: 'clamp(0.96rem, 0.92rem + 0.12vw, 1rem)',
|
|
83
86
|
},
|
|
84
87
|
xlarge: {
|
|
85
|
-
desktop: 'clamp(1.
|
|
86
|
-
mobile: 'clamp(1.
|
|
88
|
+
desktop: 'clamp(1.04rem, 0.98rem + 0.18vw, 1.12rem)',
|
|
89
|
+
mobile: 'clamp(1.04rem, 0.98rem + 0.18vw, 1.12rem)',
|
|
87
90
|
},
|
|
88
91
|
};
|
|
89
92
|
const sectionOverride = section.presentation as SectionPresentationOverride | undefined;
|
|
@@ -97,10 +100,26 @@ const textColor = sectionOverride?.textColor
|
|
|
97
100
|
?? pagePresentation.textColor;
|
|
98
101
|
const resolvedTypography = resolveTypographyOverride(pagePresentation.typography, sectionOverride?.typography);
|
|
99
102
|
const typography = resolvedTypography.values;
|
|
100
|
-
const
|
|
103
|
+
const heading = typography.headings[sectionHeadingVisualLevel] as {
|
|
104
|
+
align: ResponsiveValue<TextAlign>;
|
|
105
|
+
size: TextSize;
|
|
106
|
+
lineHeight: number;
|
|
107
|
+
spacing: string;
|
|
108
|
+
};
|
|
109
|
+
const markdownHeadingLevels = ['h3', 'h4'] as const;
|
|
110
|
+
const markdownHeadings = Object.fromEntries(markdownHeadingLevels.map((level) => [
|
|
111
|
+
level,
|
|
112
|
+
typography.headings[level] as {
|
|
113
|
+
align: ResponsiveValue<TextAlign>;
|
|
114
|
+
size: TextSize;
|
|
115
|
+
lineHeight: number;
|
|
116
|
+
spacing: string;
|
|
117
|
+
},
|
|
118
|
+
]));
|
|
119
|
+
const headingAlign = heading.align;
|
|
101
120
|
const bodyAlign = typography.body.align as ResponsiveValue<TextAlign>;
|
|
102
121
|
const captionAlign = typography.caption.align as ResponsiveValue<TextAlign>;
|
|
103
|
-
const headingFontSize = getSizeValue(headingSizeValues,
|
|
122
|
+
const headingFontSize = getSizeValue(headingSizeValues, heading.size);
|
|
104
123
|
const bodyFontSize = getSizeValue(bodySizeValues, typography.body.size as TextSize);
|
|
105
124
|
const captionFontSize = getSizeValue(captionSizeValues, typography.caption.size as TextSize);
|
|
106
125
|
const justifyByAlign: Record<TextAlign, string> = {
|
|
@@ -118,6 +137,19 @@ const getAlignedTextWidth = (
|
|
|
118
137
|
const headingWidth = getAlignedTextWidth(headingAlign, '760px');
|
|
119
138
|
const bodyWidth = getAlignedTextWidth(bodyAlign, 'var(--text-width)');
|
|
120
139
|
const hasGallery = section.gallery.length > 0;
|
|
140
|
+
const markdownHeadingStyle = (level: 'h3' | 'h4') => {
|
|
141
|
+
const headingConfig = markdownHeadings[level];
|
|
142
|
+
const fontSize = getSizeValue(headingSizeValues, headingConfig.size);
|
|
143
|
+
|
|
144
|
+
return [
|
|
145
|
+
`--section-markdown-${level}-align-desktop: ${headingConfig.align.desktop}`,
|
|
146
|
+
`--section-markdown-${level}-align-mobile: ${headingConfig.align.mobile}`,
|
|
147
|
+
`--section-markdown-${level}-font-size-desktop: ${fontSize.desktop}`,
|
|
148
|
+
`--section-markdown-${level}-font-size-mobile: ${fontSize.mobile}`,
|
|
149
|
+
`--section-markdown-${level}-line-height: ${headingConfig.lineHeight}`,
|
|
150
|
+
`--section-markdown-${level}-spacing: ${headingConfig.spacing}`,
|
|
151
|
+
];
|
|
152
|
+
};
|
|
121
153
|
const sectionStyle = [
|
|
122
154
|
`--section-background-color: ${backgroundColor}`,
|
|
123
155
|
`--section-text-color: ${textColor}`,
|
|
@@ -125,10 +157,12 @@ const sectionStyle = [
|
|
|
125
157
|
`--section-heading-align-mobile: ${headingAlign.mobile}`,
|
|
126
158
|
`--section-heading-font-size-desktop: ${headingFontSize.desktop}`,
|
|
127
159
|
`--section-heading-font-size-mobile: ${headingFontSize.mobile}`,
|
|
128
|
-
`--section-heading-line-height: ${
|
|
129
|
-
`--section-heading-spacing: ${
|
|
160
|
+
`--section-heading-line-height: ${heading.lineHeight}`,
|
|
161
|
+
`--section-heading-spacing: ${heading.spacing}`,
|
|
130
162
|
`--section-heading-width-desktop: ${headingWidth.desktop}`,
|
|
131
163
|
`--section-heading-width-mobile: ${headingWidth.mobile}`,
|
|
164
|
+
...markdownHeadingStyle('h3'),
|
|
165
|
+
...markdownHeadingStyle('h4'),
|
|
132
166
|
`--section-body-align-desktop: ${bodyAlign.desktop}`,
|
|
133
167
|
`--section-body-align-mobile: ${bodyAlign.mobile}`,
|
|
134
168
|
`--section-body-font-size-desktop: ${bodyFontSize.desktop}`,
|
|
@@ -150,7 +184,7 @@ const sectionStyle = [
|
|
|
150
184
|
|
|
151
185
|
<section
|
|
152
186
|
id={section.id}
|
|
153
|
-
class
|
|
187
|
+
class="site-section"
|
|
154
188
|
data-section-title={section.title}
|
|
155
189
|
style={sectionStyle}
|
|
156
190
|
>
|