@janga/norna 0.7.25 → 0.7.26
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 +3 -2
- package/astro.config.mjs +26 -12
- package/bin/norna-cli.mjs +12 -0
- package/package.json +14 -5
- package/schemas/card-list.schema.json +121 -0
- package/schemas/category.schema.json +2 -2
- package/schemas/config.schema.json +306 -32
- package/schemas/content-frontmatter.schema.json +7 -7
- package/schemas/image-carousel.schema.json +43 -0
- package/schemas/image-stack.schema.json +43 -0
- package/schemas/manifest.json +6 -1
- package/schemas/page-theme.schema.json +26 -26
- package/schemas/sitewide-content.schema.json +18 -18
- package/schemas/theme.schema.json +237 -262
- package/scripts/dev-local.mjs +24 -2
- package/scripts/generate-schemas.mjs +15 -3
- package/scripts/lib/category-destinations.mjs +48 -0
- package/scripts/lib/code-fence-metadata.mjs +14 -37
- package/scripts/lib/content-tabs.mjs +155 -0
- package/scripts/lib/details-headings.mjs +58 -0
- package/scripts/lib/docusaurus-migration-audit.mjs +603 -0
- package/scripts/lib/editor-language-service.mjs +238 -116
- package/scripts/lib/locale-registry.mjs +597 -0
- package/scripts/lib/locales/az-Latn.mjs +78 -0
- package/scripts/lib/locales/be.mjs +78 -0
- package/scripts/lib/locales/bg.mjs +78 -0
- package/scripts/lib/locales/bs.mjs +78 -0
- package/scripts/lib/locales/ca.mjs +78 -0
- package/scripts/lib/locales/cnr.mjs +78 -0
- package/scripts/lib/locales/cs.mjs +78 -0
- package/scripts/lib/locales/da.mjs +78 -0
- package/scripts/lib/locales/de.mjs +78 -0
- package/scripts/lib/locales/el.mjs +78 -0
- package/scripts/lib/locales/en.mjs +78 -0
- package/scripts/lib/locales/es.mjs +78 -0
- package/scripts/lib/locales/et.mjs +78 -0
- package/scripts/lib/locales/fi.mjs +78 -0
- package/scripts/lib/locales/fil.mjs +78 -0
- package/scripts/lib/locales/fr.mjs +78 -0
- package/scripts/lib/locales/ga.mjs +78 -0
- package/scripts/lib/locales/ha.mjs +78 -0
- package/scripts/lib/locales/hr.mjs +78 -0
- package/scripts/lib/locales/hu.mjs +78 -0
- package/scripts/lib/locales/id.mjs +78 -0
- package/scripts/lib/locales/is.mjs +78 -0
- package/scripts/lib/locales/it.mjs +78 -0
- package/scripts/lib/locales/jv.mjs +78 -0
- package/scripts/lib/locales/lb.mjs +78 -0
- package/scripts/lib/locales/lt.mjs +78 -0
- package/scripts/lib/locales/lv.mjs +78 -0
- package/scripts/lib/locales/mk.mjs +78 -0
- package/scripts/lib/locales/mt.mjs +78 -0
- package/scripts/lib/locales/nb.mjs +78 -0
- package/scripts/lib/locales/nl.mjs +78 -0
- package/scripts/lib/locales/nn.mjs +78 -0
- package/scripts/lib/locales/pcm.mjs +78 -0
- package/scripts/lib/locales/pl.mjs +78 -0
- package/scripts/lib/locales/pt.mjs +78 -0
- package/scripts/lib/locales/ro.mjs +78 -0
- package/scripts/lib/locales/ru.mjs +78 -0
- package/scripts/lib/locales/sk.mjs +78 -0
- package/scripts/lib/locales/sl.mjs +78 -0
- package/scripts/lib/locales/sq.mjs +78 -0
- package/scripts/lib/locales/sr-Cyrl.mjs +78 -0
- package/scripts/lib/locales/sr-Latn.mjs +78 -0
- package/scripts/lib/locales/sv.mjs +78 -0
- package/scripts/lib/locales/sw.mjs +78 -0
- package/scripts/lib/locales/tr.mjs +78 -0
- package/scripts/lib/locales/uk.mjs +78 -0
- package/scripts/lib/locales/vi.mjs +78 -0
- package/scripts/lib/markdown-links.mjs +4 -25
- package/scripts/lib/markdown-notes-render-plugin.mjs +75 -0
- package/scripts/lib/markdown-notes.mjs +165 -0
- package/scripts/lib/norna-markdown-blocks.mjs +32 -447
- package/scripts/lib/norna-markdown-render-plugin.mjs +4 -0
- package/scripts/lib/page-aliases.mjs +12 -1
- package/scripts/lib/page-markdown.mjs +63 -16
- package/scripts/lib/page-move-plan.mjs +22 -5
- package/scripts/lib/pagefind-translations.mjs +620 -0
- package/scripts/lib/presentation-contract.mjs +6 -1
- package/scripts/lib/presentation.mjs +29 -30
- package/scripts/lib/project-config.mjs +2 -173
- package/scripts/lib/schema-definitions.mjs +3 -10
- package/scripts/lib/schema-editor-metadata.mjs +22 -43
- package/scripts/lib/schema-value-definitions.mjs +4 -8
- package/scripts/lib/semantic-callouts.mjs +4 -2
- package/scripts/lib/site-content.mjs +4 -2
- package/scripts/lib/site-link-graph.mjs +16 -6
- package/scripts/lib/site-navigation-tree.mjs +0 -11
- package/scripts/lib/sitemap.mjs +5 -3
- package/scripts/lib/structured-blocks.mjs +108 -0
- package/scripts/lib/table-render-plugin.mjs +46 -3
- package/scripts/lib/table-row-headers.mjs +218 -0
- package/scripts/lib/theme-presets.mjs +3 -10
- package/scripts/lib/yaml-config.mjs +2 -6
- package/scripts/migrate-check.mjs +19 -0
- package/scripts/sync-content-sections.mjs +39 -10
- package/scripts/sync-site-public.mjs +7 -1
- package/src/components/CodeBlockCopyScript.astro +47 -5
- package/src/components/ContentBlocks.astro +39 -0
- package/src/components/ContentTabsScript.astro +60 -0
- package/src/components/DisplaySettings.astro +17 -19
- package/src/components/NavigationPageTree.astro +1 -1
- package/src/components/NoteNavigationScript.astro +4 -0
- package/src/components/PageAliasRedirect.astro +5 -4
- package/src/components/SearchNavigationScript.astro +4 -0
- package/src/components/SearchPage.astro +43 -2
- package/src/components/SectionNavigationScript.astro +43 -11
- package/src/components/SiteBreadcrumbs.astro +1 -1
- package/src/components/SiteNavigation.astro +29 -20
- package/src/components/SitePage.astro +40 -15
- package/src/components/SiteSection.astro +5 -81
- package/src/components/SiteTreeNavigation.astro +2 -2
- package/src/components/TableOverflowScript.astro +386 -58
- package/src/components/TopPageMenu.astro +47 -0
- package/src/components/TreeNavigationScript.astro +10 -3
- package/src/layouts/BaseLayout.astro +8 -12
- package/src/lib/navigationFollowing.ts +142 -0
- package/src/lib/noteNavigation.ts +50 -0
- package/src/lib/searchNavigation.ts +107 -0
- package/src/lib/sectionContent.ts +20 -90
- package/src/lib/siteNavigation.ts +0 -5
- package/src/lib/sitePages.ts +13 -2
- package/src/lib/table-sort-tooltip.ts +109 -0
- package/src/pages/[...slug].astro +16 -6
- package/src/styles/content.css +394 -132
- package/src/styles/foundations.css +9 -0
- package/src/styles/media.css +5 -0
- package/src/styles/navigation.css +63 -74
- package/src/styles/page-layout.css +7 -0
- package/src/styles/responsive.css +2 -7
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
3
3
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
|
4
4
|
import CodeBlockCopyScript from './CodeBlockCopyScript.astro';
|
|
5
|
+
import ContentTabsScript from './ContentTabsScript.astro';
|
|
6
|
+
import NoteNavigationScript from './NoteNavigationScript.astro';
|
|
5
7
|
import EditSourceLink from './EditSourceLink.astro';
|
|
6
8
|
import ImageStackEnhancement from './ImageStackEnhancement.astro';
|
|
7
9
|
import SiteBreadcrumbs from './SiteBreadcrumbs.astro';
|
|
@@ -13,7 +15,7 @@ import SiteSection from './SiteSection.astro';
|
|
|
13
15
|
import SiteTreeNavigation from './SiteTreeNavigation.astro';
|
|
14
16
|
import TableOverflowScript from './TableOverflowScript.astro';
|
|
15
17
|
import TreeNavigationScript from './TreeNavigationScript.astro';
|
|
16
|
-
import { getSectionsContent, type ResolvedSection } from '../lib/sectionContent';
|
|
18
|
+
import { flattenContentBlocks, getSectionsContent, type ResolvedSection } from '../lib/sectionContent';
|
|
17
19
|
import { getPageTheme } from '../lib/pageThemes';
|
|
18
20
|
import { getSiteModel, type SitePage } from '../lib/sitePages';
|
|
19
21
|
import {
|
|
@@ -36,15 +38,18 @@ import { mergePageThemeConfig } from '../../scripts/lib/theme-presets.mjs';
|
|
|
36
38
|
type SiteEntry = CollectionEntry<'site'>;
|
|
37
39
|
|
|
38
40
|
interface Props {
|
|
39
|
-
entry
|
|
41
|
+
entry?: SiteEntry;
|
|
42
|
+
categoryPath?: string;
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
const { entry } = Astro.props;
|
|
45
|
+
const { entry, categoryPath } = Astro.props;
|
|
43
46
|
const siteDirectory = (process.env.NORNA_SITE_DIR ?? 'site').trim();
|
|
44
47
|
const siteModel = await getSiteModel(await getCollection('site'));
|
|
45
48
|
const { nodes: siteNodes, pages: sitePages } = siteModel;
|
|
46
|
-
const
|
|
47
|
-
|
|
49
|
+
const categoryDestination = categoryPath ? siteModel.categoryDestinations.byPathname.get(categoryPath) : null;
|
|
50
|
+
const currentPage = categoryDestination?.category
|
|
51
|
+
?? sitePages.find((page) => page.entry.id === entry?.id);
|
|
52
|
+
if (!currentPage) throw new Error(`Page entry "${entry?.id ?? categoryPath}" is missing from the site page model.`);
|
|
48
53
|
const [siteTheme] = await getCollection('theme');
|
|
49
54
|
if (!siteTheme) {
|
|
50
55
|
throw new Error('site/theme.yaml is required. Add a root theme before building the site.');
|
|
@@ -93,17 +98,35 @@ const listedNavigationTree = getListedSiteNavigationTree(siteNavigation);
|
|
|
93
98
|
const renderedNavigationEntries = flattenSiteNavigationTree(listedNavigationTree);
|
|
94
99
|
const pageSequence = getSequentialPageNavigation(listedNavigationTree, currentPage.pagePath);
|
|
95
100
|
const childPages = getDirectChildPages(listedNavigationTree, currentPage.pagePath);
|
|
96
|
-
const siteHtml = entry
|
|
97
|
-
const resolvedSections =
|
|
98
|
-
|
|
101
|
+
const siteHtml = entry?.rendered?.html ?? '';
|
|
102
|
+
const resolvedSections: ResolvedSection[] = categoryDestination ? [{
|
|
103
|
+
id: null,
|
|
104
|
+
title: currentPage.title,
|
|
105
|
+
titleHtml: currentPage.title.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>'),
|
|
106
|
+
headingLevel: 1,
|
|
107
|
+
contentBlocks: [{
|
|
108
|
+
type: 'page-list',
|
|
109
|
+
pages: categoryDestination.children.map((node) => ({
|
|
110
|
+
title: node.title,
|
|
111
|
+
pathname: node.pathname,
|
|
112
|
+
description: node.kind === 'page' ? node.entry.data.page?.description : undefined,
|
|
113
|
+
})),
|
|
114
|
+
}],
|
|
115
|
+
}] : await getSectionsContent(siteHtml, currentPage, childPages);
|
|
116
|
+
const allContentBlocks = resolvedSections.flatMap((section) => flattenContentBlocks(section.contentBlocks));
|
|
117
|
+
const hasTabs = allContentBlocks.some((block) => block.type === 'tabs');
|
|
118
|
+
const hasNotes = allContentBlocks.some((block) => (
|
|
119
|
+
block.type === 'html' && /class="[^"]*\bsection-note-ref\b|\bdata-footnote-ref(?:="")?/.test(block.html)
|
|
120
|
+
));
|
|
121
|
+
const hasCodeBlocks = allContentBlocks.some((block) => (
|
|
99
122
|
block.type === 'html' && /<pre\b[^>]*>[\s\S]*?<code\b/i.test(block.html)
|
|
100
|
-
))
|
|
101
|
-
const hasTables =
|
|
123
|
+
));
|
|
124
|
+
const hasTables = allContentBlocks.some((block) => (
|
|
102
125
|
block.type === 'html' && /\bdata-table-frame(?:="")?/i.test(block.html)
|
|
103
|
-
))
|
|
104
|
-
const hasImageStack =
|
|
126
|
+
));
|
|
127
|
+
const hasImageStack = allContentBlocks.some((block) => (
|
|
105
128
|
block.type === 'image-stack'
|
|
106
|
-
))
|
|
129
|
+
));
|
|
107
130
|
const hasPrioritizableImage = (section: ResolvedSection) => section.contentBlocks.some((block) => (
|
|
108
131
|
block.type === 'image-stack'
|
|
109
132
|
|| block.type === 'image-carousel'
|
|
@@ -116,7 +139,7 @@ const renderedSections = resolvedSections.map((section, index) => ({
|
|
|
116
139
|
prioritizeFirstImage: index === firstImageSectionIndex,
|
|
117
140
|
}));
|
|
118
141
|
const hasPageSequence = Boolean(pageSequence.previous || pageSequence.next);
|
|
119
|
-
const editSourceTarget = projectConfig.editLink
|
|
142
|
+
const editSourceTarget = projectConfig.editLink && currentPage.kind === 'page'
|
|
120
143
|
? resolveEditSourceTarget({
|
|
121
144
|
...projectConfig.editLink,
|
|
122
145
|
development: import.meta.env.DEV,
|
|
@@ -184,7 +207,7 @@ const reserveBreadcrumbSpace = showTreeNavigation || showBreadcrumbs;
|
|
|
184
207
|
|
|
185
208
|
<BaseLayout
|
|
186
209
|
title={currentPage.title}
|
|
187
|
-
description={entry
|
|
210
|
+
description={entry?.data.page?.description}
|
|
188
211
|
presentation={pagePresentation}
|
|
189
212
|
visualTheme={visualTheme}
|
|
190
213
|
frameColors={frameColors}
|
|
@@ -259,6 +282,8 @@ const reserveBreadcrumbSpace = showTreeNavigation || showBreadcrumbs;
|
|
|
259
282
|
</div>
|
|
260
283
|
{hasTreeNavigation && <TreeNavigationScript />}
|
|
261
284
|
{hasCodeBlocks && <CodeBlockCopyScript />}
|
|
285
|
+
{hasTabs && <ContentTabsScript />}
|
|
286
|
+
{hasNotes && <NoteNavigationScript />}
|
|
262
287
|
{hasTables && <TableOverflowScript />}
|
|
263
288
|
{hasImageStack && <ImageStackEnhancement />}
|
|
264
289
|
</BaseLayout>
|
|
@@ -6,11 +6,8 @@ import {
|
|
|
6
6
|
getHeadingTypographySizeValue,
|
|
7
7
|
getTypographySizeValue,
|
|
8
8
|
} from '../../scripts/lib/typography.mjs';
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import ImageStack from './ImageStack.astro';
|
|
12
|
-
import PageList from './PageList.astro';
|
|
13
|
-
import type { ResolvedSection } from '../lib/sectionContent';
|
|
9
|
+
import ContentBlocks from './ContentBlocks.astro';
|
|
10
|
+
import { flattenContentBlocks, type ResolvedSection } from '../lib/sectionContent';
|
|
14
11
|
|
|
15
12
|
type ResponsiveValue<T> = { desktop: T; mobile: T };
|
|
16
13
|
type ResponsiveOverride<T> = { desktop?: T; mobile?: T };
|
|
@@ -110,42 +107,11 @@ const bodyWidth = {
|
|
|
110
107
|
desktop: bodyWidthValue,
|
|
111
108
|
mobile: bodyWidthValue,
|
|
112
109
|
};
|
|
113
|
-
const
|
|
114
|
-
block.type === 'image-stack'
|
|
115
|
-
|| block.type === 'image-carousel'
|
|
116
|
-
|| block.type === 'card-list'
|
|
117
|
-
|| block.type === 'page-list'
|
|
118
|
-
);
|
|
119
|
-
const hasMedia = section.contentBlocks.some((block) => (
|
|
110
|
+
const hasMedia = flattenContentBlocks(section.contentBlocks).some((block) => (
|
|
120
111
|
block.type === 'image-stack'
|
|
121
112
|
|| block.type === 'image-carousel'
|
|
122
113
|
|| (block.type === 'card-list' && block.cards.some((card) => card.image))
|
|
123
114
|
));
|
|
124
|
-
const contentGroups: Array<
|
|
125
|
-
| { type: 'reading'; blocks: ResolvedSection['contentBlocks'] }
|
|
126
|
-
| { type: 'structured'; block: Extract<ResolvedSection['contentBlocks'][number], { type: 'image-stack' | 'image-carousel' | 'card-list' | 'page-list' }> }
|
|
127
|
-
> = [];
|
|
128
|
-
let readingBlocks: ResolvedSection['contentBlocks'] = [];
|
|
129
|
-
const flushReadingBlocks = () => {
|
|
130
|
-
if (readingBlocks.length === 0) return;
|
|
131
|
-
contentGroups.push({ type: 'reading', blocks: readingBlocks });
|
|
132
|
-
readingBlocks = [];
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
for (const block of section.contentBlocks) {
|
|
136
|
-
if (isStructuredBlock(block)) {
|
|
137
|
-
flushReadingBlocks();
|
|
138
|
-
contentGroups.push({ type: 'structured', block });
|
|
139
|
-
} else {
|
|
140
|
-
readingBlocks = [...readingBlocks, block];
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
flushReadingBlocks();
|
|
145
|
-
const firstMediaGroupIndex = contentGroups.findIndex((group) => (
|
|
146
|
-
group.type === 'structured'
|
|
147
|
-
&& (group.block.type === 'image-stack' || group.block.type === 'image-carousel')
|
|
148
|
-
));
|
|
149
115
|
const markdownHeadingStyle = (level: 'h3' | 'h4') => {
|
|
150
116
|
const headingConfig = markdownHeadings[level];
|
|
151
117
|
const fontSize = getHeadingTypographySizeValue(level, headingConfig.size);
|
|
@@ -209,55 +175,13 @@ const sectionStyle = [
|
|
|
209
175
|
style={sectionStyle}
|
|
210
176
|
>
|
|
211
177
|
<div class="section-card">
|
|
178
|
+
<slot name="before-heading" />
|
|
212
179
|
<header class="section-header">
|
|
213
180
|
<HeadingTag id={headingId} set:html={section.titleHtml} />
|
|
214
181
|
</header>
|
|
215
182
|
|
|
216
183
|
<div class:list={['section-body', { 'section-body-has-media': hasMedia, 'section-body-text-only': !hasMedia }]}>
|
|
217
|
-
{
|
|
218
|
-
if (group.type === 'reading') {
|
|
219
|
-
return group.blocks.map((block) => block.type === 'html'
|
|
220
|
-
&& block.html
|
|
221
|
-
&& <div class="section-markdown" set:html={block.html} />);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const block = group.block;
|
|
225
|
-
if (block.type === 'image-carousel') {
|
|
226
|
-
return (
|
|
227
|
-
<ImageCarousel
|
|
228
|
-
title={section.title}
|
|
229
|
-
images={block.images}
|
|
230
|
-
maxAvailableWidthPercent={visualTheme.images.maxAvailableWidthPercent}
|
|
231
|
-
maxAvailableHeightPercent={visualTheme.images.maxAvailableHeightPercent}
|
|
232
|
-
priorityFirstImage={prioritizeFirstImage && groupIndex === firstMediaGroupIndex}
|
|
233
|
-
/>
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (block.type === 'card-list') {
|
|
238
|
-
return (
|
|
239
|
-
<CardList
|
|
240
|
-
cards={block.cards}
|
|
241
|
-
layout={block.layout}
|
|
242
|
-
flow={block.flow}
|
|
243
|
-
size={block.size}
|
|
244
|
-
width={block.width ?? pagePresentation.blocks.cardList.width}
|
|
245
|
-
/>
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
if (block.type === 'page-list') {
|
|
249
|
-
return <PageList pages={block.pages} />;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return (
|
|
253
|
-
<ImageStack
|
|
254
|
-
title={section.title}
|
|
255
|
-
images={block.images}
|
|
256
|
-
maxAvailableWidthPercent={visualTheme.images.maxAvailableWidthPercent}
|
|
257
|
-
priorityFirstImage={prioritizeFirstImage && groupIndex === firstMediaGroupIndex}
|
|
258
|
-
/>
|
|
259
|
-
);
|
|
260
|
-
})}
|
|
184
|
+
<ContentBlocks blocks={section.contentBlocks} title={section.title} images={visualTheme.images} cardWidth={pagePresentation.blocks.cardList.width} prioritizeFirstImage={prioritizeFirstImage} />
|
|
261
185
|
</div>
|
|
262
186
|
</div>
|
|
263
187
|
</section>
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
import projectConfig from '../../scripts/lib/project-config.mjs';
|
|
3
3
|
import { withBasePath } from '../lib/basePath';
|
|
4
4
|
import { getListedSiteNavigationTree, type SiteNavigationEntry } from '../lib/siteNavigation';
|
|
5
|
-
import type {
|
|
5
|
+
import type { SiteNode } from '../lib/sitePages';
|
|
6
6
|
import NavigationPageTree from './NavigationPageTree.astro';
|
|
7
7
|
import NavigationTreeControls from './NavigationTreeControls.astro';
|
|
8
8
|
|
|
9
9
|
interface Props {
|
|
10
|
-
currentPage:
|
|
10
|
+
currentPage: SiteNode;
|
|
11
11
|
navigationEntries: SiteNavigationEntry[];
|
|
12
12
|
integratePageSections?: boolean;
|
|
13
13
|
}
|