@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,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Local Dog Example
|
|
3
|
+
description: A small norna example with free dog images.
|
|
4
|
+
navigation:
|
|
5
|
+
label: Dog site!
|
|
6
|
+
order: 10
|
|
7
|
+
sections:
|
|
8
|
+
- id: black-dogs
|
|
9
|
+
presentation:
|
|
10
|
+
typography:
|
|
11
|
+
preset: statement
|
|
12
|
+
overrides:
|
|
13
|
+
headings:
|
|
14
|
+
h2:
|
|
15
|
+
size: xlarge
|
|
16
|
+
gallery:
|
|
17
|
+
- image: photo-of-a-black-dog.jpg
|
|
18
|
+
alt: "A black dog sitting outdoors and looking at the camera."
|
|
19
|
+
caption:
|
|
20
|
+
"Photo of a dog. ContaDeletada2906, CC0 1.0, via Wikimedia Commons."
|
|
21
|
+
- image: black-puppy-meadow.png
|
|
22
|
+
alt:
|
|
23
|
+
"A black puppy standing in a grassy meadow and looking at the camera."
|
|
24
|
+
caption: "AI-generated image."
|
|
25
|
+
- id: summer-dogs
|
|
26
|
+
visible:
|
|
27
|
+
from: "2026-08-01"
|
|
28
|
+
until: "2026-09-01"
|
|
29
|
+
presentation:
|
|
30
|
+
backgroundColor: "#181818"
|
|
31
|
+
typography:
|
|
32
|
+
preset: compact-gallery
|
|
33
|
+
gallery: []
|
|
34
|
+
- id: brown-dogs
|
|
35
|
+
presentation:
|
|
36
|
+
typography:
|
|
37
|
+
preset: text-forward
|
|
38
|
+
gallery:
|
|
39
|
+
- image: brown-dog.jpg
|
|
40
|
+
alt: "A brown dog looking directly at the camera."
|
|
41
|
+
caption: "DogBrown. Frank-3, CC0 1.0, via Wikimedia Commons."
|
|
42
|
+
- image: dog-accompanies-master.jpg
|
|
43
|
+
alt: "A man walks outdoors with a dog at his side."
|
|
44
|
+
caption:
|
|
45
|
+
"Dog accompanies his master. Steve Hillebrand, U.S. Fish and Wildlife
|
|
46
|
+
Service, public domain."
|
|
47
|
+
- id: golden-dogs
|
|
48
|
+
presentation:
|
|
49
|
+
typography:
|
|
50
|
+
overrides:
|
|
51
|
+
headings:
|
|
52
|
+
h2:
|
|
53
|
+
size: large
|
|
54
|
+
h3:
|
|
55
|
+
size: medium
|
|
56
|
+
body:
|
|
57
|
+
lineHeight: 1.58
|
|
58
|
+
paragraphSpacing: 1em
|
|
59
|
+
gallery:
|
|
60
|
+
- image: golden-retriever.jpg
|
|
61
|
+
alt: "A golden retriever standing outdoors."
|
|
62
|
+
caption: "Golden Retriever. Ribo, public domain, via Wikimedia Commons."
|
|
63
|
+
- image: toller-puppy.jpg
|
|
64
|
+
alt:
|
|
65
|
+
"A young Nova Scotia Duck Tolling Retriever puppy sitting on grass."
|
|
66
|
+
caption:
|
|
67
|
+
"Nova Scotia Duck Tolling Retriever puppy. RM, public domain, via
|
|
68
|
+
Wikimedia Commons."
|
|
69
|
+
- id: white-dogs
|
|
70
|
+
presentation:
|
|
71
|
+
typography:
|
|
72
|
+
overrides:
|
|
73
|
+
caption:
|
|
74
|
+
spacing: 0.35em
|
|
75
|
+
gallery:
|
|
76
|
+
- carousel:
|
|
77
|
+
- image: white-cute-dog.jpg
|
|
78
|
+
alt: "A white dog looking toward the camera."
|
|
79
|
+
caption:
|
|
80
|
+
"White cute dog. Neal Ziring, public domain, via Wikimedia
|
|
81
|
+
Commons."
|
|
82
|
+
- image: white-puppy-garden.png
|
|
83
|
+
alt: "A fluffy white puppy standing on grass in a garden."
|
|
84
|
+
caption: "AI-generated image."
|
|
85
|
+
- id: sources
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Black Dogs {#black-dogs}
|
|
89
|
+
|
|
90
|
+
Black-coated dogs from the local norna development collection. This
|
|
91
|
+
sentence contains [yellow highlighted text]{.highlight} for inline style
|
|
92
|
+
inspection.
|
|
93
|
+
|
|
94
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a
|
|
95
|
+
ante venenatis dapibus posuere velit aliquet.
|
|
96
|
+
|
|
97
|
+
### Coat notes
|
|
98
|
+
|
|
99
|
+
This `###` heading should follow `typography.headings.h3`.
|
|
100
|
+
|
|
101
|
+
#### Small detail
|
|
102
|
+
|
|
103
|
+
This `####` heading should follow `typography.headings.h4`.
|
|
104
|
+
|
|
105
|
+
Curabitur blandit tempus porttitor. Donec id elit non mi porta gravida at eget
|
|
106
|
+
metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.
|
|
107
|
+
|
|
108
|
+
## Summer Dogs {#summer-dogs}
|
|
109
|
+
|
|
110
|
+
This temporary section is visible during August 2026. It is here to demonstrate
|
|
111
|
+
date-controlled sections and [warm alert text]{.alert} in the local dog example.
|
|
112
|
+
|
|
113
|
+
Lorem ipsum dolor sit amet.
|
|
114
|
+
|
|
115
|
+
Vestibulum id ligula porta felis euismod semper. Maecenas faucibus mollis
|
|
116
|
+
interdum. Nullam quis risus eget urna mollis ornare vel eu leo.
|
|
117
|
+
|
|
118
|
+
## Brown Dogs {#brown-dogs}
|
|
119
|
+
|
|
120
|
+
Brown dogs provide a second image section for local navigation checks.
|
|
121
|
+
|
|
122
|
+
Donec ullamcorper nulla non metus auctor fringilla. Fusce dapibus, tellus ac
|
|
123
|
+
cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit
|
|
124
|
+
amet risus.
|
|
125
|
+
|
|
126
|
+
Maecenas sed diam eget risus varius blandit sit amet non magna. Morbi leo risus,
|
|
127
|
+
porta ac consectetur ac, vestibulum at eros. Sed posuere consectetur est at
|
|
128
|
+
lobortis.
|
|
129
|
+
|
|
130
|
+
## Golden Dogs {#golden-dogs}
|
|
131
|
+
|
|
132
|
+
Golden-coated retrievers add a third image section and more scrolling depth.
|
|
133
|
+
|
|
134
|
+
Cras justo odio, dapibus ac facilisis in, egestas eget quam.
|
|
135
|
+
|
|
136
|
+
### Training note
|
|
137
|
+
|
|
138
|
+
This section overrides `headings.h2` and `headings.h3`, while keeping the
|
|
139
|
+
site-wide `headings.h4` style.
|
|
140
|
+
|
|
141
|
+
Nulla vitae elit libero, a pharetra augue. Integer posuere erat a ante venenatis
|
|
142
|
+
dapibus posuere velit aliquet. Aenean eu leo quam. Pellentesque ornare sem
|
|
143
|
+
lacinia quam venenatis vestibulum.
|
|
144
|
+
|
|
145
|
+
## White Dogs {#white-dogs}
|
|
146
|
+
|
|
147
|
+
A white dog completes the colour-based image sequence.
|
|
148
|
+
|
|
149
|
+
Etiam porta sem malesuada magna mollis euismod. Donec id elit non mi porta
|
|
150
|
+
gravida at eget metus.
|
|
151
|
+
|
|
152
|
+
Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Lorem ipsum
|
|
153
|
+
dolor sit amet, consectetur adipiscing elit. Donec sed odio dui. Vestibulum id
|
|
154
|
+
ligula porta felis euismod semper.
|
|
155
|
+
|
|
156
|
+
## Sources {#sources}
|
|
157
|
+
|
|
158
|
+
Most images are sourced from Wikimedia Commons pages that list CC0 or
|
|
159
|
+
public-domain licensing:
|
|
160
|
+
|
|
161
|
+
- [Photo of a dog](https://commons.wikimedia.org/wiki/File:Photo_of_a_dog.jpg)
|
|
162
|
+
- [DogBrown](https://commons.wikimedia.org/wiki/File:DogBrown.jpg)
|
|
163
|
+
- [Dog accompanies his master](https://commons.wikimedia.org/wiki/File:Dog_accompanies_his_master.jpg)
|
|
164
|
+
- [Golden Retriever](https://commons.wikimedia.org/wiki/File:Golden_Retriever.jpg)
|
|
165
|
+
- [Toller pup7wks2](https://commons.wikimedia.org/wiki/File:Toller_pup7wks2.JPG)
|
|
166
|
+
- [White cute dog](https://commons.wikimedia.org/wiki/File:White_cute_dog.jpg)
|
|
167
|
+
|
|
168
|
+
The black and white puppy images are AI-generated.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
|
2
|
+
<rect width="128" height="128" rx="24" fill="#111111" />
|
|
3
|
+
<circle cx="40" cy="43" r="13" fill="#ffffff" />
|
|
4
|
+
<circle cx="64" cy="34" r="13" fill="#ffffff" />
|
|
5
|
+
<circle cx="88" cy="43" r="13" fill="#ffffff" />
|
|
6
|
+
<path fill="#ffffff" d="M31 83c0-18 15-31 33-31s33 13 33 31c0 12-9 18-20 14-7-3-19-3-26 0-11 4-20-2-20-14Z" />
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Dog care
|
|
3
|
+
description: A small route for testing multi-page norna navigation.
|
|
4
|
+
navigation:
|
|
5
|
+
label: Dog care
|
|
6
|
+
order: 20
|
|
7
|
+
sections:
|
|
8
|
+
- id: daily-rhythm
|
|
9
|
+
presentation:
|
|
10
|
+
typography:
|
|
11
|
+
overrides:
|
|
12
|
+
headings:
|
|
13
|
+
h2:
|
|
14
|
+
size: large
|
|
15
|
+
- id: calm-training
|
|
16
|
+
- id: media-notes
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Daily rhythm {#daily-rhythm}
|
|
20
|
+
|
|
21
|
+
Dogs often settle into a home when the day has a clear rhythm. Meals, walks,
|
|
22
|
+
rest and play do not need to be dramatic events, but they become easier to
|
|
23
|
+
understand when they happen in a predictable order.
|
|
24
|
+
|
|
25
|
+
Short routines also make a media page easier to scan. This route exists mainly
|
|
26
|
+
to test how page navigation and site navigation behave together.
|
|
27
|
+
|
|
28
|
+
### Morning checks
|
|
29
|
+
|
|
30
|
+
This `###` heading tests route-level body subheadings.
|
|
31
|
+
|
|
32
|
+
#### Quiet detail
|
|
33
|
+
|
|
34
|
+
This `####` heading should stay smaller than the `###` above it.
|
|
35
|
+
|
|
36
|
+
## Calm training {#calm-training}
|
|
37
|
+
|
|
38
|
+
Good training can be quiet. A short session with one clear task is often more
|
|
39
|
+
useful than a long session with many competing signals.
|
|
40
|
+
|
|
41
|
+
The same principle applies to navigation. The page should show where the visitor
|
|
42
|
+
is in the site first, and then offer a simple way to jump within the current
|
|
43
|
+
page.
|
|
44
|
+
|
|
45
|
+
## Media notes {#media-notes}
|
|
46
|
+
|
|
47
|
+
This section is intentionally text-only. Image routes can be added later, but a
|
|
48
|
+
first navigation test should make it easy to judge menus, active states and
|
|
49
|
+
anchor positions without layout noise from new media.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
# norna:start theme-help
|
|
3
|
+
# Site-wide visual defaults. Remove a value to use the engine default.
|
|
4
|
+
# Active values below are ordinary YAML; this marked comment block is only help text.
|
|
5
|
+
#
|
|
6
|
+
# Available structure:
|
|
7
|
+
# navigation:
|
|
8
|
+
# brand: site-wide brand/home text; defaults to the homepage title
|
|
9
|
+
# layout:
|
|
10
|
+
# pageWidth: CSS length, e.g. 2048px
|
|
11
|
+
# gutter:
|
|
12
|
+
# desktop: CSS length or clamp(), e.g. clamp(1.25rem, 4vw, 3rem)
|
|
13
|
+
# mobile: CSS length, e.g. 16px
|
|
14
|
+
# spacing:
|
|
15
|
+
# firstSectionTop:
|
|
16
|
+
# desktop: CSS length or clamp(), e.g. clamp(1.25rem, 2.5vw, 2rem)
|
|
17
|
+
# mobile: CSS length, e.g. 1rem
|
|
18
|
+
# sectionGap:
|
|
19
|
+
# desktop: CSS length or clamp(), e.g. clamp(1.8rem, 3vw, 2.75rem)
|
|
20
|
+
# mobile: CSS length, e.g. 1.65rem
|
|
21
|
+
# subheadingTop:
|
|
22
|
+
# desktop: CSS length, e.g. 1.35rem
|
|
23
|
+
# mobile: CSS length, e.g. 1.1rem
|
|
24
|
+
# gallery:
|
|
25
|
+
# width: CSS length, e.g. 55vw
|
|
26
|
+
# maxAvailableWidthPercent:
|
|
27
|
+
# desktop: number from 1 through 100
|
|
28
|
+
# mobile: number from 1 through 100
|
|
29
|
+
# maxAvailableHeightPercent:
|
|
30
|
+
# desktop: number from 1 through 100
|
|
31
|
+
# mobile: number from 1 through 100
|
|
32
|
+
# typography:
|
|
33
|
+
# fontFamily: CSS font-family stack
|
|
34
|
+
# preset: quiet-gallery | compact-gallery | text-forward | statement
|
|
35
|
+
# overrides:
|
|
36
|
+
# headings:
|
|
37
|
+
# h2:
|
|
38
|
+
# size: small | medium | large | xlarge
|
|
39
|
+
# h3:
|
|
40
|
+
# size: small | medium | large | xlarge
|
|
41
|
+
# h4:
|
|
42
|
+
# size: small | medium | large | xlarge
|
|
43
|
+
# presentation:
|
|
44
|
+
# backgroundColor: quoted hex color, e.g. "#000000"
|
|
45
|
+
# textColor: quoted hex color, e.g. "#f7f4ee"
|
|
46
|
+
# inlineStyles:
|
|
47
|
+
# style-name:
|
|
48
|
+
# color: quoted hex color
|
|
49
|
+
# frame:
|
|
50
|
+
# colors: presentation | theme
|
|
51
|
+
# colors:
|
|
52
|
+
# backgroundColor: quoted hex color
|
|
53
|
+
# textColor: quoted hex color
|
|
54
|
+
# norna:end theme-help
|
|
55
|
+
layout:
|
|
56
|
+
pageWidth: 2048px
|
|
57
|
+
gutter:
|
|
58
|
+
desktop: clamp(1.25rem, 4vw, 3rem)
|
|
59
|
+
mobile: 16px
|
|
60
|
+
spacing:
|
|
61
|
+
firstSectionTop:
|
|
62
|
+
desktop: clamp(1.25rem, 2.5vw, 2rem)
|
|
63
|
+
mobile: 1rem
|
|
64
|
+
sectionGap:
|
|
65
|
+
desktop: clamp(1.8rem, 3vw, 2.75rem)
|
|
66
|
+
mobile: 1.65rem
|
|
67
|
+
finalSectionBottom:
|
|
68
|
+
desktop: clamp(1.8rem, 3vw, 2.75rem)
|
|
69
|
+
mobile: 1.65rem
|
|
70
|
+
sectionHeadingToBody:
|
|
71
|
+
desktop: 0.55rem
|
|
72
|
+
mobile: 0.45rem
|
|
73
|
+
bodyToImages:
|
|
74
|
+
desktop: 1.25rem
|
|
75
|
+
mobile: 1rem
|
|
76
|
+
imageGap:
|
|
77
|
+
desktop: clamp(1.25rem, 2.8vw, 2rem)
|
|
78
|
+
mobile: 1.8rem
|
|
79
|
+
subheadingTop:
|
|
80
|
+
desktop: 1.35rem
|
|
81
|
+
mobile: 1.1rem
|
|
82
|
+
subheadingRuleTop:
|
|
83
|
+
desktop: 0.65rem
|
|
84
|
+
mobile: 0.55rem
|
|
85
|
+
gallery:
|
|
86
|
+
width: 55vw
|
|
87
|
+
maxAvailableWidthPercent:
|
|
88
|
+
desktop: 100
|
|
89
|
+
mobile: 100
|
|
90
|
+
maxAvailableHeightPercent:
|
|
91
|
+
desktop: 74
|
|
92
|
+
mobile: 68
|
|
93
|
+
typography:
|
|
94
|
+
fontFamily: "Arial, 'Helvetica Neue', Helvetica, sans-serif"
|
|
95
|
+
preset: quiet-gallery
|
|
96
|
+
overrides:
|
|
97
|
+
headings:
|
|
98
|
+
h3:
|
|
99
|
+
size: medium
|
|
100
|
+
spacing: 0.7rem
|
|
101
|
+
h4:
|
|
102
|
+
size: small
|
|
103
|
+
spacing: 0.55rem
|
|
104
|
+
presentation:
|
|
105
|
+
backgroundColor: "#550000"
|
|
106
|
+
textColor: "#ffffff"
|
|
107
|
+
inlineStyles:
|
|
108
|
+
highlight:
|
|
109
|
+
color: "#ffd84d"
|
|
110
|
+
alert:
|
|
111
|
+
color: "#ff9a3d"
|
|
112
|
+
frame:
|
|
113
|
+
colors: presentation
|
|
114
|
+
---
|
|
@@ -1,26 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
site: {
|
|
3
3
|
url: 'https://example.com/',
|
|
4
|
-
|
|
5
|
-
layout: {
|
|
6
|
-
pageWidth: '1180px',
|
|
7
|
-
gutter: {
|
|
8
|
-
desktop: 'clamp(1.25rem, 4vw, 3rem)',
|
|
9
|
-
mobile: '1rem',
|
|
10
|
-
},
|
|
11
|
-
},
|
|
12
|
-
gallery: {
|
|
13
|
-
maxAvailableWidthPercent: {
|
|
14
|
-
desktop: 100,
|
|
15
|
-
mobile: 100,
|
|
16
|
-
},
|
|
17
|
-
maxAvailableHeightPercent: {
|
|
18
|
-
desktop: 74,
|
|
19
|
-
mobile: 68,
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
typography: {
|
|
23
|
-
fontFamily: "Arial, 'Helvetica Neue', Helvetica, sans-serif",
|
|
4
|
+
basePath: '/',
|
|
24
5
|
},
|
|
25
6
|
navigation: {
|
|
26
7
|
smoothScroll: {
|
|
@@ -35,7 +16,7 @@ export default {
|
|
|
35
16
|
labels: {
|
|
36
17
|
skipToContent: 'Skip to content',
|
|
37
18
|
sectionNavigation: 'Sections',
|
|
38
|
-
gallery: '
|
|
19
|
+
gallery: 'Images',
|
|
39
20
|
},
|
|
40
21
|
},
|
|
41
22
|
footer: {
|
|
@@ -52,7 +33,7 @@ export default {
|
|
|
52
33
|
},
|
|
53
34
|
},
|
|
54
35
|
github: {
|
|
55
|
-
repo: 'owner/fixture-
|
|
36
|
+
repo: 'owner/fixture-site',
|
|
56
37
|
branch: 'main',
|
|
57
38
|
pagesWorkflow: 'Deploy to GitHub Pages',
|
|
58
39
|
},
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
layout:
|
|
3
|
+
pageWidth: 1180px
|
|
4
|
+
gallery:
|
|
5
|
+
width: 900px
|
|
6
|
+
typography:
|
|
7
|
+
fontFamily: "Arial, 'Helvetica Neue', Helvetica, sans-serif"
|
|
8
|
+
preset: quiet-gallery
|
|
5
9
|
frame:
|
|
6
10
|
colors: presentation
|
|
7
11
|
---
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@janga/norna",
|
|
3
3
|
"description": "A command-line publishing workflow for simple static sites.",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.7.
|
|
5
|
+
"version": "0.7.3",
|
|
6
6
|
"license": "GPL-3.0-only",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"astro.config.mjs",
|
|
26
26
|
"bin/",
|
|
27
27
|
"docs/",
|
|
28
|
+
"examples/",
|
|
28
29
|
"fixtures/",
|
|
29
30
|
"scripts/",
|
|
30
31
|
"src/",
|
|
@@ -40,8 +41,8 @@
|
|
|
40
41
|
"node": ">=22.12.0"
|
|
41
42
|
},
|
|
42
43
|
"scripts": {
|
|
43
|
-
"dev": "npm run dev:local",
|
|
44
|
-
"dev:local": "
|
|
44
|
+
"dev": "npm run dev:local --",
|
|
45
|
+
"dev:local": "node bin/norna.mjs dev:local",
|
|
45
46
|
"dev:lan": "node bin/norna.mjs dev:lan",
|
|
46
47
|
"dev:restart": "node bin/norna.mjs dev:restart",
|
|
47
48
|
"dev:status": "node bin/norna.mjs dev:status",
|
|
@@ -50,22 +51,22 @@
|
|
|
50
51
|
"config:check": "node bin/norna.mjs config:check",
|
|
51
52
|
"content:check": "node bin/norna.mjs content:check",
|
|
52
53
|
"content:sync": "node bin/norna.mjs content:sync",
|
|
53
|
-
"typography:presets": "node bin/norna.mjs typography
|
|
54
|
-
"typography:show": "node bin/norna.mjs typography
|
|
54
|
+
"typography:presets": "node bin/norna.mjs typography presets",
|
|
55
|
+
"typography:show": "node bin/norna.mjs typography show",
|
|
55
56
|
"site:public": "node bin/norna.mjs site:public",
|
|
56
57
|
"images": "node bin/norna.mjs images",
|
|
57
|
-
"build": "
|
|
58
|
+
"build": "node bin/norna.mjs build",
|
|
58
59
|
"build:local": "node bin/norna.mjs build:local",
|
|
59
60
|
"deploy": "node bin/norna.mjs deploy",
|
|
60
61
|
"deploy:commit": "node bin/norna.mjs deploy:commit",
|
|
61
62
|
"deploy:watch": "node bin/norna.mjs deploy:watch",
|
|
62
63
|
"doctor": "node bin/norna.mjs doctor",
|
|
63
|
-
"demo:
|
|
64
|
-
"
|
|
65
|
-
"test": "npm run test:content-check && npm run test:site-public && npm run test:engine-commands && npm run test:ci-lockfile && npm run test:temporary-visibility && npm run test:fixture:build && npm run demo:build && npm run package:check",
|
|
64
|
+
"demo:build": "node bin/norna.mjs --site-dir examples/dog-gallery/site build",
|
|
65
|
+
"test": "npm run test:content-check && npm run test:site-public && npm run test:cli-discovery && npm run test:engine-commands && npm run test:ci-lockfile && npm run test:temporary-visibility && npm run test:fixture:build && npm run package:check",
|
|
66
66
|
"test:fixture:build": "node bin/norna.mjs --site-dir fixtures/basic/site build",
|
|
67
67
|
"test:content-check": "node scripts/test-content-check.mjs",
|
|
68
68
|
"test:site-public": "node scripts/test-site-public.mjs",
|
|
69
|
+
"test:cli-discovery": "node scripts/test-cli-discovery.mjs",
|
|
69
70
|
"test:engine-commands": "node scripts/test-engine-commands.mjs",
|
|
70
71
|
"test:ci-lockfile": "node scripts/test-ci-lockfile.mjs",
|
|
71
72
|
"test:temporary-visibility": "node scripts/test-temporary-visibility.mjs",
|
package/scripts/check-config.mjs
CHANGED
|
@@ -20,11 +20,12 @@ try {
|
|
|
20
20
|
|
|
21
21
|
console.log('Config check passed.');
|
|
22
22
|
console.log(`Site URL: ${projectConfig.site.url}`);
|
|
23
|
+
console.log(`Base path: ${projectConfig.site.basePath}`);
|
|
23
24
|
console.log(`Page width: ${projectConfig.layout.pageWidth}`);
|
|
24
25
|
console.log(`Gutter: desktop ${projectConfig.layout.gutter.desktop}, mobile ${projectConfig.layout.gutter.mobile}`);
|
|
25
|
-
console.log(`
|
|
26
|
-
console.log(`
|
|
27
|
-
console.log(`
|
|
26
|
+
console.log(`Image area width: ${projectConfig.gallery.width}`);
|
|
27
|
+
console.log(`Image max available width: desktop ${projectConfig.gallery.maxAvailableWidthPercent.desktop}%, mobile ${projectConfig.gallery.maxAvailableWidthPercent.mobile}%`);
|
|
28
|
+
console.log(`Image max available height: desktop ${projectConfig.gallery.maxAvailableHeightPercent.desktop}%, mobile ${projectConfig.gallery.maxAvailableHeightPercent.mobile}%`);
|
|
28
29
|
console.log(`Font family: ${projectConfig.typography.fontFamily}`);
|
|
29
30
|
console.log(`Language: ${projectConfig.locale.lang}`);
|
|
30
31
|
console.log(`GitHub repo: ${projectConfig.github.repo}`);
|
package/scripts/deploy-site.mjs
CHANGED
|
@@ -196,7 +196,7 @@ const assertMainBranch = async () => {
|
|
|
196
196
|
|
|
197
197
|
const assertDeployableStatus = async (entries, expectedImagePaths) => {
|
|
198
198
|
if (entries.length === 0) {
|
|
199
|
-
fail('Refusing to deploy: no changes to commit after the
|
|
199
|
+
fail('Refusing to deploy: no changes to commit after the Norna build.');
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
const unexpectedUntracked = entries.filter((entry) => (
|
|
@@ -206,7 +206,7 @@ const assertDeployableStatus = async (entries, expectedImagePaths) => {
|
|
|
206
206
|
if (unexpectedUntracked.length > 0) {
|
|
207
207
|
fail([
|
|
208
208
|
'Refusing to deploy: unexpected untracked files are present.',
|
|
209
|
-
`Only new referenced
|
|
209
|
+
`Only new referenced images under ${siteImagesLabel}/<section-id>/ or ${siteRoutesLabel}/<route-folder>/images/<section-id>/ are staged automatically.`,
|
|
210
210
|
...unexpectedUntracked.map((entry) => `- ${formatEntry(entry)}`),
|
|
211
211
|
].join('\n'));
|
|
212
212
|
}
|
|
@@ -331,7 +331,7 @@ const deployCommittedMain = async () => {
|
|
|
331
331
|
await assertPushableBranch();
|
|
332
332
|
await runBuild();
|
|
333
333
|
await printStatusShort();
|
|
334
|
-
await assertCleanWorktree('Refusing to deploy: the
|
|
334
|
+
await assertCleanWorktree('Refusing to deploy: the Norna build produced uncommitted changes. Commit them before deploying.');
|
|
335
335
|
await fetchRemoteMain();
|
|
336
336
|
|
|
337
337
|
const relation = await assertPushableBranch();
|
package/scripts/dev-local.mjs
CHANGED
|
@@ -5,18 +5,26 @@ import { networkInterfaces } from 'node:os';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
7
|
import { getAstroArgs, runAstroInherit } from './lib/astro-command.mjs';
|
|
8
|
+
import projectConfig from './lib/project-config.mjs';
|
|
8
9
|
import { astroCacheDir, engineRoot, siteProjectRoot } from './lib/site-paths.mjs';
|
|
9
10
|
|
|
10
11
|
const execFileAsync = promisify(execFile);
|
|
11
12
|
|
|
12
13
|
const port = 4321;
|
|
13
|
-
const localHost = '
|
|
14
|
-
const localUrl = `http://${localHost}:${port}
|
|
15
|
-
const probeUrls = [
|
|
14
|
+
const localHost = '127.0.0.1';
|
|
15
|
+
const localUrl = `http://${localHost}:${port}${projectConfig.site.basePath}`;
|
|
16
|
+
const probeUrls = [
|
|
17
|
+
localUrl,
|
|
18
|
+
`http://localhost:${port}${projectConfig.site.basePath}`,
|
|
19
|
+
];
|
|
16
20
|
const skipOpen = process.env.WALDE_NO_OPEN === '1';
|
|
17
21
|
const statePath = path.join(astroCacheDir, 'dev-local.json');
|
|
18
22
|
const logPath = path.join(astroCacheDir, 'dev.log');
|
|
19
|
-
const
|
|
23
|
+
const args = process.argv.slice(2);
|
|
24
|
+
const knownCommands = new Set(['start', 'lan', 'status', 'logs', 'restart', 'stop']);
|
|
25
|
+
const command = args.find((arg) => knownCommands.has(arg)) ?? args.find((arg) => !arg.startsWith('-')) ?? 'start';
|
|
26
|
+
const shouldKillBlockingPort = args.includes('--kill');
|
|
27
|
+
const shouldFollowLogs = args.includes('--follow');
|
|
20
28
|
|
|
21
29
|
const runAstro = async (args, options = {}) => execFileAsync(process.execPath, getAstroArgs(args), {
|
|
22
30
|
cwd: siteProjectRoot,
|
|
@@ -57,7 +65,7 @@ const readState = async () => {
|
|
|
57
65
|
const getLanUrls = () => Object.values(networkInterfaces())
|
|
58
66
|
.flat()
|
|
59
67
|
.filter((network) => network?.family === 'IPv4' && !network.internal)
|
|
60
|
-
.map((network) => `http://${network.address}:${port}
|
|
68
|
+
.map((network) => `http://${network.address}:${port}${projectConfig.site.basePath}`);
|
|
61
69
|
|
|
62
70
|
const writeState = async (pid, host) => {
|
|
63
71
|
await mkdir(path.dirname(statePath), { recursive: true });
|
|
@@ -98,7 +106,7 @@ const isPortFreeOnHost = (loopbackHost) => new Promise((resolve, reject) => {
|
|
|
98
106
|
});
|
|
99
107
|
|
|
100
108
|
const isPortFree = async () => {
|
|
101
|
-
for (const loopbackHost of ['127.0.0.1'
|
|
109
|
+
for (const loopbackHost of ['127.0.0.1']) {
|
|
102
110
|
if (!(await isPortFreeOnHost(loopbackHost))) {
|
|
103
111
|
return false;
|
|
104
112
|
}
|
|
@@ -155,6 +163,35 @@ const waitForPidToStopListening = async (pid) => {
|
|
|
155
163
|
return false;
|
|
156
164
|
};
|
|
157
165
|
|
|
166
|
+
const killPortPids = async (pids) => {
|
|
167
|
+
for (const pid of pids) {
|
|
168
|
+
console.log(`Stopping process ${pid} on port ${port}.`);
|
|
169
|
+
try {
|
|
170
|
+
process.kill(pid, 'SIGTERM');
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if (error.code !== 'ESRCH') {
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
for (const pid of pids) {
|
|
179
|
+
if (await waitForPidToStopListening(pid)) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
console.log(`Process ${pid} did not stop; sending SIGKILL.`);
|
|
184
|
+
try {
|
|
185
|
+
process.kill(pid, 'SIGKILL');
|
|
186
|
+
} catch (error) {
|
|
187
|
+
if (error.code !== 'ESRCH') {
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
await waitForPidToStopListening(pid);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
158
195
|
const openBrowser = async () => {
|
|
159
196
|
if (skipOpen) {
|
|
160
197
|
console.log(`Browser open skipped. Open ${localUrl}`);
|
|
@@ -210,12 +247,22 @@ const stopServer = async ({ quiet = false } = {}) => {
|
|
|
210
247
|
}
|
|
211
248
|
};
|
|
212
249
|
|
|
213
|
-
const startServer = async ({ host = localHost, open = true } = {}) => {
|
|
250
|
+
const startServer = async ({ host = localHost, open = true, killBlockingPort = false } = {}) => {
|
|
214
251
|
await stopServer({ quiet: true });
|
|
215
252
|
|
|
216
|
-
|
|
253
|
+
let existingPids = await getPortPids();
|
|
217
254
|
if (existingPids.length > 0 || !(await isPortFree())) {
|
|
218
|
-
|
|
255
|
+
if (!killBlockingPort || existingPids.length === 0) {
|
|
256
|
+
throw new Error(`Port ${port} is already in use. Stop the process using it, then rerun the dev command, or pass --kill.`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
await killPortPids(existingPids);
|
|
260
|
+
await removeState();
|
|
261
|
+
existingPids = await getPortPids();
|
|
262
|
+
|
|
263
|
+
if (existingPids.length > 0 || !(await isPortFree())) {
|
|
264
|
+
throw new Error(`Port ${port} is still in use after trying to stop ${existingPids.join(', ')}.`);
|
|
265
|
+
}
|
|
219
266
|
}
|
|
220
267
|
|
|
221
268
|
await syncSitePublic();
|
|
@@ -271,9 +318,7 @@ const showStatus = async () => {
|
|
|
271
318
|
};
|
|
272
319
|
|
|
273
320
|
const showLogs = async () => {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (shouldFollow) {
|
|
321
|
+
if (shouldFollowLogs) {
|
|
277
322
|
const tail = spawn('tail', ['-n', '80', '-f', logPath], { stdio: 'inherit' });
|
|
278
323
|
await new Promise((resolve, reject) => {
|
|
279
324
|
tail.once('exit', resolve);
|
|
@@ -296,16 +341,20 @@ const showLogs = async () => {
|
|
|
296
341
|
};
|
|
297
342
|
|
|
298
343
|
if (command === 'start') {
|
|
299
|
-
await startServer({ open: !skipOpen });
|
|
344
|
+
await startServer({ open: !skipOpen, killBlockingPort: shouldKillBlockingPort });
|
|
300
345
|
} else if (command === 'lan') {
|
|
301
|
-
await startServer({ host: '0.0.0.0', open: !skipOpen });
|
|
346
|
+
await startServer({ host: '0.0.0.0', open: !skipOpen, killBlockingPort: shouldKillBlockingPort });
|
|
302
347
|
} else if (command === 'status') {
|
|
303
348
|
await showStatus();
|
|
304
349
|
} else if (command === 'logs') {
|
|
305
350
|
await showLogs();
|
|
306
351
|
} else if (command === 'restart') {
|
|
307
352
|
const state = await readState();
|
|
308
|
-
await startServer({
|
|
353
|
+
await startServer({
|
|
354
|
+
host: state?.host === '0.0.0.0' ? '0.0.0.0' : localHost,
|
|
355
|
+
open: false,
|
|
356
|
+
killBlockingPort: shouldKillBlockingPort,
|
|
357
|
+
});
|
|
309
358
|
} else if (command === 'stop') {
|
|
310
359
|
await stopServer();
|
|
311
360
|
} else {
|