astro-accelerator 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +6 -0
  3. package/package.json +52 -0
  4. package/public/js/main.js +45 -0
  5. package/public/js/modules/animation.js +69 -0
  6. package/public/js/modules/click-blocks.js +41 -0
  7. package/public/js/modules/code-blocks.js +59 -0
  8. package/public/js/modules/events.js +19 -0
  9. package/public/js/modules/external-links.js +20 -0
  10. package/public/js/modules/figures.js +37 -0
  11. package/public/js/modules/focus.js +76 -0
  12. package/public/js/modules/nav-mobile.js +122 -0
  13. package/public/js/modules/nav-sticky.js +54 -0
  14. package/public/js/modules/query.js +41 -0
  15. package/public/js/modules/resizing.js +43 -0
  16. package/public/js/modules/string.js +66 -0
  17. package/public/js/modules/youtube.js +39 -0
  18. package/public/js/search.js +249 -0
  19. package/src/data/footer.ts +43 -0
  20. package/src/data/image-size.mjs +5 -0
  21. package/src/data/images.mjs +4 -0
  22. package/src/data/navigation.ts +64 -0
  23. package/src/layouts/Author.astro +15 -0
  24. package/src/layouts/Default.astro +15 -0
  25. package/src/layouts/Redirect.astro +15 -0
  26. package/src/layouts/Search.astro +15 -0
  27. package/src/pages/articles/feed.xml.ts +52 -0
  28. package/src/pages/report/missing-banner.astro +42 -0
  29. package/src/pages/report/missing-meta.astro +42 -0
  30. package/src/pages/report/oldest-content.astro +41 -0
  31. package/src/pages/report/taxonomy.astro +47 -0
  32. package/src/pages/search.json.ts +43 -0
  33. package/src/pages/sitemap.xml.ts +36 -0
  34. package/src/themes/accelerator/components/ArticleList.astro +67 -0
  35. package/src/themes/accelerator/components/Authors.astro +57 -0
  36. package/src/themes/accelerator/components/AuthorsMini.astro +37 -0
  37. package/src/themes/accelerator/components/Breadcrumbs.astro +33 -0
  38. package/src/themes/accelerator/components/Footer.astro +37 -0
  39. package/src/themes/accelerator/components/FooterItem.astro +29 -0
  40. package/src/themes/accelerator/components/Header.astro +46 -0
  41. package/src/themes/accelerator/components/HtmlHead.astro +49 -0
  42. package/src/themes/accelerator/components/Navigation.astro +31 -0
  43. package/src/themes/accelerator/components/NavigationBar.astro +31 -0
  44. package/src/themes/accelerator/components/NavigationItem.astro +37 -0
  45. package/src/themes/accelerator/components/Paging.astro +33 -0
  46. package/src/themes/accelerator/components/Related.astro +88 -0
  47. package/src/themes/accelerator/components/SkipLinks.astro +25 -0
  48. package/src/themes/accelerator/components/TableOfContents.astro +32 -0
  49. package/src/themes/accelerator/components/Taxonomy.astro +49 -0
  50. package/src/themes/accelerator/layouts/Author.astro +30 -0
  51. package/src/themes/accelerator/layouts/Default.astro +62 -0
  52. package/src/themes/accelerator/layouts/Redirect.astro +19 -0
  53. package/src/themes/accelerator/layouts/Search.astro +43 -0
  54. package/src/themes/accelerator/utilities/Authors.astro +48 -0
  55. package/src/themes/accelerator/utilities/Breadcrumbs.astro +27 -0
  56. package/src/themes/accelerator/utilities/Cache.astro +42 -0
  57. package/src/themes/accelerator/utilities/DateFormat.astro +25 -0
  58. package/src/themes/accelerator/utilities/Footer.astro +176 -0
  59. package/src/themes/accelerator/utilities/Languages.astro +14 -0
  60. package/src/themes/accelerator/utilities/Markdown.astro +27 -0
  61. package/src/themes/accelerator/utilities/NavPage.astro +65 -0
  62. package/src/themes/accelerator/utilities/Navigation.astro +81 -0
  63. package/src/themes/accelerator/utilities/NavigationTypes.astro +20 -0
  64. package/src/themes/accelerator/utilities/PageLinks.astro +72 -0
  65. package/src/themes/accelerator/utilities/PageQueries.astro +71 -0
  66. package/src/themes/accelerator/utilities/PageTypeFilters.astro +69 -0
  67. package/src/themes/accelerator/utilities/Taxonomy.astro +112 -0
  68. package/src/themes/accelerator/utilities/Url.astro +19 -0
  69. package/src/themes/accelerator/utilities/custom-markdown.mjs +104 -0
  70. package/src/themes/accelerator/utilities/img.mjs +152 -0
  71. package/src/themes/accelerator/utilities/language.json +111 -0
@@ -0,0 +1,29 @@
1
+ ---
2
+ import t from '@util/language.json';
3
+ import { Lang } from '@util/Languages.astro';
4
+
5
+ import type { NavPage } from '@util/NavigationTypes.astro';
6
+
7
+ // Properties
8
+ type Props = {
9
+ lang: string;
10
+ page: NavPage
11
+ };
12
+ const { lang, page } = Astro.props as Props;
13
+
14
+ // Language
15
+ const _ = Lang(lang);
16
+
17
+ // Logic
18
+ // -
19
+ ---
20
+ <div class="footer-column">
21
+ <h2>{ page.title }</h2>
22
+ <ul>
23
+ { page.children.sort((a, b) => a.order - b.order).map((child) => (
24
+ <li>
25
+ <a href={ child.url } aria-current={ child.ariaCurrent } rel={ child.rel }>{ child.title }</a>
26
+ </li>
27
+ ))}
28
+ </ul>
29
+ </div>
@@ -0,0 +1,46 @@
1
+ ---
2
+ import type { Frontmatter } from '@config';
3
+
4
+ import t from '@util/language.json';
5
+ import { Lang } from '@util/Languages.astro';
6
+
7
+ import { SITE } from '@config';
8
+ import { getPages } from '@util/PageQueries.astro';
9
+ import { addSlashToAddress } from '@util/Url.astro';
10
+ import { isSearch } from '@util/PageTypeFilters.astro';
11
+
12
+ // Properties
13
+ type Props = {
14
+ lang: string;
15
+ frontmatter: Frontmatter
16
+ headings: { depth: number; slug: string; text: string; }[];
17
+ };
18
+ const { lang } = Astro.props as Props;
19
+
20
+ // Language
21
+ const _ = Lang(lang);
22
+
23
+ // Logic
24
+ const search = (await getPages(isSearch))[0] ?? null;
25
+
26
+ ---
27
+ <header class="site-header">
28
+ <a href="#site-nav" class="navigation-icon" title={ _(t.header.open_menu) }><svg xmlns="http://www.w3.org/2000/svg"
29
+ width="40" height="40" viewBox="0 0 24 24" stroke-width="1.5"
30
+ fill="none" stroke-linecap="round" stroke-linejoin="round">
31
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
32
+ <line x1="4" y1="6" x2="20" y2="6" />
33
+ <line x1="4" y1="12" x2="20" y2="12" />
34
+ <line x1="4" y1="18" x2="20" y2="18" />
35
+ </svg></a>
36
+ <a href={ (SITE.subfolder ?? '') + '/' } class="site-title" translate="no">{ SITE.title }</a>
37
+ {search != null &&
38
+ <a href={ addSlashToAddress(search.url) } class="search-icon" title={ _(t.header.open_search) }><svg xmlns="http://www.w3.org/2000/svg"
39
+ width="40" height="40" viewBox="0 0 24 24" stroke-width="1"
40
+ fill="none" stroke-linecap="round" stroke-linejoin="round">
41
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
42
+ <circle cx="10" cy="10" r="7" />
43
+ <line x1="21" y1="21" x2="15" y2="15" />
44
+ </svg></a>
45
+ }
46
+ </header>
@@ -0,0 +1,49 @@
1
+ ---
2
+ import { SITE, OPEN_GRAPH, HEADER_SCRIPTS, Frontmatter } from '@config';
3
+ import { addSlashToUrl } from '@util/Url.astro';
4
+
5
+ // Properties
6
+ type Props = {
7
+ lang: string;
8
+ frontmatter: Frontmatter
9
+ headings: { depth: number; slug: string; text: string; }[];
10
+ };
11
+ const { frontmatter } = Astro.props;
12
+
13
+ // Logic
14
+ const imageSrc = frontmatter.bannerImage?.src ?? OPEN_GRAPH.image.src;
15
+ const imageAlt = frontmatter.bannerImage?.alt ?? OPEN_GRAPH.image.alt;
16
+ const robots = frontmatter.robots ?? 'index, follow';
17
+ const canonicalImageSrc = new URL(imageSrc, Astro.site);
18
+ const canonicalURL = addSlashToUrl(new URL(Astro.url.pathname, Astro.site + SITE.subfolder));
19
+ ---
20
+ <head>
21
+ <meta charset="utf-8" />
22
+ <title>{ frontmatter.title ? `${ frontmatter.title } | ${ SITE.title }` : SITE.title }</title>
23
+ <meta name="robots" content={ 'max-image-preview:large, ' + robots }>
24
+ <meta name="viewport" content="width=device-width, initial-scale=1">
25
+ <meta name="keywords" content={ frontmatter.keywords }>
26
+ <meta name="description" content={ frontmatter.description }>
27
+ <meta name="theme-color" content={ SITE.themeColor } />
28
+ <link rel="stylesheet" href={ SITE.subfolder + '/css/vars.css' } />
29
+ <link rel="stylesheet" href={ SITE.subfolder + '/css/main.css' } />
30
+ <link rel="canonical" href={ canonicalURL } />
31
+ {SITE.feedUrl &&
32
+ <link rel="alternate" type="application/atom+xml" title={ SITE.title } href={ SITE.feedUrl } />
33
+ }
34
+ <link rel="alternate" href={ SITE.subfolder + '/sitemap.xml' } type="application/rss+xml" />
35
+ <link rel="shortcut icon" href={ SITE.subfolder + '/icons/favicon.ico' } type="image/x-icon" />
36
+ <link rel="apple-touch-icon" href={ SITE.subfolder + '/icons/apple-touch-icon.png' } />
37
+ <meta property="og:type" content="website" />
38
+ <meta property="og:url" content={ canonicalURL } />
39
+ <meta property="og:title" content={ frontmatter.title } />
40
+ <meta property="og:description" content={ frontmatter.description } />
41
+ <meta property="og:image" content={ canonicalImageSrc } />
42
+ <meta property="og:image:alt" content={ imageAlt } />
43
+ <meta name="twitter:card" content="summary_large_image">
44
+ <meta name="twitter:title" content={ frontmatter.title }>
45
+ <meta name="twitter:description" content={ frontmatter.description }>
46
+ <meta name="twitter:image" content={ canonicalImageSrc }>
47
+ <meta name="twitter:image:alt" content={ imageAlt }>
48
+ <Fragment set:html={ HEADER_SCRIPTS }></Fragment>
49
+ </head>
@@ -0,0 +1,31 @@
1
+ ---
2
+ import type { Frontmatter } from '@config';
3
+
4
+ import t from '@util/language.json';
5
+ import { Lang } from '@util/Languages.astro';
6
+
7
+ import { getMenu } from '@util/Navigation.astro';
8
+
9
+ import NavigationItem from '@components/NavigationItem.astro';
10
+
11
+ // Properties
12
+ type Props = {
13
+ lang: string;
14
+ };
15
+ const { lang } = Astro.props as Props;
16
+
17
+ // Language
18
+ const _ = Lang(lang);
19
+
20
+ // Logic
21
+ const currentUrl = new URL(Astro.request.url);
22
+ const pages = await getMenu(currentUrl);
23
+ ---
24
+ <nav class="site-nav" id="site-nav" aria-label={ _(t.aria.site_navigation) }>
25
+ <h2 class="site-nav-title">{ _(t.navigation.title) }</h2>
26
+ <ul>
27
+ {pages.sort((a, b) => a.order - b.order).map((page) => (
28
+ <NavigationItem lang={ lang } page={ page } />
29
+ ))}
30
+ </ul>
31
+ </nav>
@@ -0,0 +1,31 @@
1
+ ---
2
+ import type { Frontmatter } from '@config';
3
+
4
+ import t from '@util/language.json';
5
+ import { Lang } from '@util/Languages.astro';
6
+
7
+ import { getMenu } from '@util/Navigation.astro';
8
+
9
+ import NavigationItem from '@components/NavigationItem.astro';
10
+
11
+ // Properties
12
+ type Props = {
13
+ lang: string;
14
+ };
15
+ const { lang } = Astro.props as Props;
16
+
17
+ // Language
18
+ const _ = Lang(lang);
19
+
20
+ // Logic
21
+ const currentUrl = new URL(Astro.request.url);
22
+ const pages = await getMenu(currentUrl);
23
+ ---
24
+ <nav class="site-nav-bar" id="site-nav" aria-label={ _(t.aria.site_navigation) }>
25
+ <h2 class="site-nav-title">{ _(t.navigation.title) }</h2>
26
+ <ul>
27
+ {pages.sort((a, b) => a.order - b.order).map((page) => (
28
+ <li><a href={ page.url }>{ page.title }</a></li>
29
+ ))}
30
+ </ul>
31
+ </nav>
@@ -0,0 +1,37 @@
1
+ ---
2
+ import t from '@util/language.json';
3
+ import { Lang } from '@util/Languages.astro';
4
+
5
+ import type { NavPage } from '@util/NavigationTypes.astro';
6
+ import { addSlashToAddress } from '@util/Url.astro';
7
+
8
+ // Properties
9
+ type Props = {
10
+ lang: string;
11
+ page: NavPage
12
+ };
13
+ const { lang, page } = Astro.props as Props;
14
+
15
+ // Language
16
+ const _ = Lang(lang);
17
+
18
+ // Logic
19
+ // -
20
+ ---
21
+ {(page.children.length == 0) && (
22
+ <li>
23
+ <a href={ addSlashToAddress(page.url) } aria-current={ page.ariaCurrent } rel={ page.rel }>{ page.title }</a>
24
+ </li>
25
+ )}
26
+ {(page.children.length > 0) && (
27
+ <li class="has-children">
28
+ <details class="sub-nav" open={ page.isOpen }>
29
+ <summary><span>{ page.section }</span></summary>
30
+ <ul>
31
+ { page.children.sort((a, b) => a.order - b.order).map((page) => (
32
+ <Astro.self lang={ lang } page={ page } />
33
+ ))}
34
+ </ul>
35
+ </details>
36
+ </li>
37
+ )}
@@ -0,0 +1,33 @@
1
+ ---
2
+ import type { MarkdownInstance, Page } from 'astro';
3
+ import type { Link } from '@util/PageLinks.astro';
4
+
5
+ import t from '@util/language.json';
6
+ import { Lang } from '@util/Languages.astro';
7
+ import { addSlashToAddress } from '@util/Url.astro';
8
+
9
+ // Properties
10
+ type Props = {
11
+ lang: string;
12
+ page: Page<MarkdownInstance<Record<string, any>>>,
13
+ pageLinks: Link[],
14
+ };
15
+ const { lang, page, pageLinks } = Astro.props as Props;
16
+
17
+ // Language
18
+ const _ = Lang(lang);
19
+
20
+ // Logic
21
+ // -
22
+ ---
23
+ <nav class="post-paging" aria-label={ _(t.aria.paging) }>
24
+ {page.url.prev
25
+ ? <a href={ addSlashToAddress(page.url.prev) }>{ _(t.articles.previous) }</a>
26
+ : <span>{ _(t.articles.previous) }</span>}
27
+ {pageLinks.map((link) => (
28
+ <a href={ addSlashToAddress(link.url) } aria-current={ link.ariaCurrent } class={ link.class }>{ link.title }</a>
29
+ ))}
30
+ {page.url.next
31
+ ? <a href={ addSlashToAddress(page.url.next) }>{ _(t.articles.next) }</a>
32
+ : <span>{ _(t.articles.next) }</span>}
33
+ </nav>
@@ -0,0 +1,88 @@
1
+ ---
2
+ import type { Frontmatter } from '@config';
3
+ import type { MarkdownInstance } from 'astro';
4
+
5
+ import t from '@util/language.json';
6
+ import { Lang } from '@util/Languages.astro';
7
+ import { addSlashToAddress } from '@util/Url.astro';
8
+ import { getImageInfo } from '@util/custom-markdown.mjs'
9
+ import { getPages } from '@util/PageQueries.astro';
10
+ import { isListable, sortByPubDateDesc } from '@util/PageTypeFilters.astro';
11
+
12
+ import AuthorsMini from '@components/AuthorsMini.astro';
13
+
14
+ import { SITE } from '@config';
15
+
16
+ // Properties
17
+ type Props = {
18
+ lang: string;
19
+ frontmatter: Frontmatter
20
+ headings: { depth: number; slug: string; text: string; }[];
21
+ };
22
+ const { lang, frontmatter, headings } = Astro.props as Props;
23
+
24
+ // Language
25
+ const _ = Lang(lang);
26
+
27
+ // Logic
28
+ let posts: MarkdownInstance<Record<string, any>>[] = [];
29
+ const parentCagory = (frontmatter.categories ?? [null])[0];
30
+
31
+ if (parentCagory != null) {
32
+ const allPages = await getPages(isListable)
33
+ const allPosts = allPages
34
+ .filter(p => p.frontmatter.title != frontmatter.title && p.frontmatter.categories && p.frontmatter.categories.includes(parentCagory))
35
+ .sort(sortByPubDateDesc)
36
+
37
+ // One "Most Recent" Post
38
+ if (allPosts.length > 0) {
39
+ posts.push(allPosts[0])
40
+ }
41
+
42
+ const olderPost = allPosts.slice(1).filter(p => p.frontmatter.pubDate < frontmatter.pubDate)
43
+
44
+ // One "Older than current" Post
45
+ if (olderPost.length > 0) {
46
+ posts.push(olderPost[0])
47
+ } else if (allPosts.length > 1) {
48
+ posts.push(allPosts[1])
49
+ }
50
+ }
51
+
52
+ const articles = posts.map(p => {
53
+ return {
54
+ url: p.url,
55
+ frontmatter: p.frontmatter,
56
+ img: p.frontmatter.bannerImage
57
+ ? getImageInfo(p.frontmatter.bannerImage.src, '', SITE.images.listerSize)
58
+ : null
59
+ };
60
+ });
61
+ ---
62
+ {articles.length > 0 &&
63
+ <ul class="post-list anim-show-parent">
64
+ {articles.map((post) => (
65
+ <li class="list-item" data-destination={ addSlashToAddress(post.url) } data-image={ (post.frontmatter.bannerImage?.src.length > 0).toString() }>
66
+ <article>
67
+ <div class="list-item-image">
68
+ {post.img && (
69
+ <img
70
+ srcset={ post.img.srcset }
71
+ sizes={ post.img.sizes }
72
+ src={ post.img.src }
73
+ alt={ post.frontmatter.bannerImage.alt }
74
+ class={ post.img.class }
75
+ loading="lazy" />
76
+ )}
77
+ </div>
78
+ <div class="list-item-content">
79
+ <h3>
80
+ <a href={ addSlashToAddress(post.url) }>{ post.frontmatter.title }</a>
81
+ </h3>
82
+ <AuthorsMini lang={ lang } frontmatter={ post.frontmatter as Frontmatter } />
83
+ </div>
84
+ </article>
85
+ </li>
86
+ ))}
87
+ </ul>
88
+ }
@@ -0,0 +1,25 @@
1
+ ---
2
+ import type { Frontmatter } from '@config';
3
+
4
+ import t from '@util/language.json';
5
+ import { Lang } from '@util/Languages.astro';
6
+
7
+ // Properties
8
+ type Props = {
9
+ lang: string;
10
+ frontmatter: Frontmatter
11
+ headings: { depth: number; slug: string; text: string; }[];
12
+ };
13
+ const { lang } = Astro.props as Props;
14
+
15
+ // Language
16
+ const _ = Lang(lang);
17
+
18
+ // Logic
19
+ // -
20
+ ---
21
+ <nav class="skip-links" aria-label={ _(t.aria.skiplinks) } id="site-top">
22
+ <a href="#site-nav">{ _(t.skiplinks.skip_to_navigation) }</a>
23
+ <a href="#site-main">{ _(t.skiplinks.skip_to_content) }</a>
24
+ <a href="#site-footer">{ _(t.skiplinks.skip_to_footer) }</a>
25
+ </nav>
@@ -0,0 +1,32 @@
1
+ ---
2
+ import { SITE, Frontmatter } from '@config';
3
+
4
+ import t from '@util/language.json';
5
+ import { Lang } from '@util/Languages.astro';
6
+
7
+ // Properties
8
+ type Props = {
9
+ lang: string;
10
+ frontmatter: Frontmatter;
11
+ headings: { depth: number; slug: string; text: string; }[];
12
+ };
13
+ const { lang, frontmatter, headings } = Astro.props as Props;
14
+
15
+ // Language
16
+ const _ = Lang(lang);
17
+
18
+ // Logic
19
+ // ...
20
+ ---
21
+ {headings?.length > 0 &&
22
+ <nav class="page-toc" aria-label={ _(t.aria.toc) }>
23
+ <details>
24
+ <summary>{ _(t.toc.title) }</summary>
25
+ <ol>
26
+ {headings.map((heading) =>(
27
+ <li class={ 'toc-lev-' + heading.depth }><a href={ '#' + heading.slug }>{ heading.text }</a></li>
28
+ ))}
29
+ </ol>
30
+ </details>
31
+ </nav>
32
+ }
@@ -0,0 +1,49 @@
1
+ ---
2
+ import type { Frontmatter } from '@config';
3
+
4
+ import t from '@util/language.json';
5
+ import { Lang } from '@util/Languages.astro';
6
+
7
+ import { taxonomyLinks } from '@util/Taxonomy.astro';
8
+
9
+ // Properties
10
+ type Props = {
11
+ lang: string;
12
+ frontmatter: Frontmatter;
13
+ };
14
+ const { lang, frontmatter } = Astro.props as Props;
15
+
16
+ // Language
17
+ const _ = Lang(lang);
18
+
19
+ // Logic
20
+ const categories = frontmatter.categories ?? [];
21
+ const tags = frontmatter.tags ?? [];
22
+
23
+ const links = taxonomyLinks(_);
24
+ const hasTaxonomy = (categories.length + tags.length) > 0;
25
+ ---
26
+ {hasTaxonomy && (
27
+ <div class="post-taxonomy">
28
+ {tags.length > 0 && (
29
+ <div class="post-tag">
30
+ <h2>{ _(t.articles.tag_title) }: </h2>
31
+ <ul itemprop="keywords">
32
+ {tags.map(tag =>
33
+ <li><a href={ links.getTagLink(tag) }>{ tag }</a></li>
34
+ )}
35
+ </ul>
36
+ </div>
37
+ )}
38
+ {categories.length > 0 && (
39
+ <div class="post-cat">
40
+ <h2>{ _(t.articles.category_title) }: </h2>
41
+ <ul>
42
+ {categories.map(category =>
43
+ <li><a href={ links.getCategoryLink(category) } itemprop="articleSection">{ category }</a></li>
44
+ )}
45
+ </ul>
46
+ </div>
47
+ )}
48
+ </div>
49
+ )}
@@ -0,0 +1,30 @@
1
+ ---
2
+ import { SITE, Frontmatter } from '@config';
3
+
4
+ import t from '@util/language.json';
5
+ import { Lang } from '@util/Languages.astro';
6
+
7
+ import { getPages } from '@util/PageQueries.astro';
8
+
9
+ import Default from './Default.astro';
10
+ import ArticleList from '@components/ArticleList.astro';
11
+
12
+ // Properties
13
+ type Props = {
14
+ frontmatter: Frontmatter
15
+ headings: { depth: number; slug: string; text: string; }[];
16
+ }
17
+ const { frontmatter, headings } = Astro.props as Props;
18
+ const lang = frontmatter.lang ?? SITE.default.lang;
19
+
20
+ // Language
21
+ const _ = Lang(lang);
22
+
23
+ // Logic
24
+ const authorPages = await getPages(p => p.frontmatter.authors?.indexOf(frontmatter.id) > -1)
25
+ ---
26
+ <Default frontmatter={ frontmatter } headings={ headings }>
27
+ <slot />
28
+ <h2>{ _(t.author.recent_articles) }</h2>
29
+ <ArticleList lang={ lang } posts={ authorPages } />
30
+ </Default>
@@ -0,0 +1,62 @@
1
+ ---
2
+ import { SITE, Frontmatter } from '@config';
3
+ import { getInlineHtmlFrom } from '@util/Markdown.astro';
4
+
5
+ import Head from '@components/HtmlHead.astro';
6
+ import Header from '@components/Header.astro';
7
+ import Footer from '@components/Footer.astro';
8
+ import SkipLinks from '@components/SkipLinks.astro';
9
+ import Breadcrumbs from '@components/Breadcrumbs.astro';
10
+ import Navigation from '@components/Navigation.astro';
11
+ import TableOfContents from '@components/TableOfContents.astro';
12
+ import Authors from '@components/Authors.astro';
13
+ import Taxonomy from '@components/Taxonomy.astro';
14
+ import Related from '@components/Related.astro';
15
+
16
+ type Props = {
17
+ frontmatter: Frontmatter
18
+ headings: { depth: number; slug: string; text: string; }[];
19
+ }
20
+ const { frontmatter, headings } = Astro.props as Props;
21
+
22
+ const lang = frontmatter.lang ?? SITE.default.lang;
23
+ const textDirection = frontmatter.dir ?? SITE.default.dir;
24
+
25
+ const title = await getInlineHtmlFrom(frontmatter.title ?? SITE.title);
26
+ const subtitle = frontmatter.subtitle
27
+ ? await getInlineHtmlFrom(frontmatter.subtitle)
28
+ : null;
29
+
30
+ const features = "<script>const site_url = '" + SITE.url + "'; const site_features = '" + JSON.stringify(SITE.featureFlags) + "';</script>";
31
+ ---
32
+ <html dir={ textDirection } lang={ lang } class="initial">
33
+ <Head frontmatter={ frontmatter } headings={ headings } lang={ lang } />
34
+ <body>
35
+ <SkipLinks frontmatter={ frontmatter } headings={ headings } lang={ lang } />
36
+ <Header frontmatter={ frontmatter } headings={ headings } lang={ lang } />
37
+ <Breadcrumbs frontmatter={ frontmatter } headings={ headings } lang={ lang } />
38
+ <div class="content-group">
39
+ <Navigation lang={ lang } />
40
+ <main id="site-main">
41
+ <article itemscope itemtype="https://schema.org/Article">
42
+ <header>
43
+ <h1 itemprop="name headline"><Fragment set:html={ title } /></h1>
44
+ {subtitle && (
45
+ <p><Fragment set:html={ subtitle } /></p>
46
+ )}
47
+ </header>
48
+ <div class="page-content anim-show-parent" itemprop="articleBody">
49
+ <TableOfContents frontmatter={ frontmatter } headings={ headings } lang={ lang } />
50
+ <Authors frontmatter={ frontmatter } lang={ lang } />
51
+ <slot />
52
+ <Taxonomy frontmatter={ frontmatter } lang={ lang } />
53
+ <Related frontmatter={ frontmatter } headings={ headings } lang={ lang } />
54
+ </div>
55
+ </article>
56
+ </main>
57
+ </div>
58
+ <Footer frontmatter={ frontmatter } headings={ headings } lang={ lang } />
59
+ <Fragment is:html="true" set:html={ features } />
60
+ <script src={ SITE.subfolder + '/js/main.js' } type="module" async></script>
61
+ </body>
62
+ </html>
@@ -0,0 +1,19 @@
1
+ ---
2
+ import type { Frontmatter } from '@config';
3
+
4
+ // Properties
5
+ type Props = {
6
+ frontmatter: Frontmatter
7
+ headings: { depth: number; slug: string; text: string; }[];
8
+ }
9
+ const { frontmatter, headings } = Astro.props as Props;
10
+
11
+ const destination = frontmatter.redirect;
12
+ const metaContent = `0; URL=${ destination }`;
13
+ ---
14
+ <html>
15
+ <meta charset="utf-8">
16
+ <title>Redirecting to { destination }</title>
17
+ <meta http-equiv="refresh" content={ metaContent }>
18
+ <link rel="canonical" href={ destination }>
19
+ </html>
@@ -0,0 +1,43 @@
1
+ ---
2
+ import { SITE, Frontmatter } from '@config';
3
+
4
+ import t from '@util/language.json';
5
+ import { Lang } from '@util/Languages.astro';
6
+
7
+ import Default from './Default.astro';
8
+
9
+ // Properties
10
+ type Props = {
11
+ frontmatter: Frontmatter
12
+ headings: { depth: number; slug: string; text: string; }[];
13
+ }
14
+ const { frontmatter, headings } = Astro.props as Props;
15
+ const lang = frontmatter.lang ?? SITE.default.lang;
16
+
17
+ // Language
18
+ const _ = Lang(lang);
19
+
20
+ // Logic
21
+ const siteUrl = Astro.site ? Astro.site.href : '';
22
+ ---
23
+ <Default frontmatter={ frontmatter } headings={ headings }>
24
+ <slot />
25
+ <form method="GET" action={ SITE.search.fallbackUrl ?? 'https://www.google.com/search' } role="search" id="site-search" class="site-search note" autocomplete="off" data-sourcedata={ SITE.subfolder + '/search.json' }>
26
+ <fieldset>
27
+ <input type="hidden" name={ SITE.search.fallbackSite ?? 'q' } value={ 'site:' + siteUrl } />
28
+ <div>
29
+ <label for="site-search-query">{ _(t.search.search_for) }</label>
30
+ <input
31
+ type="text"
32
+ name={ SITE.search.fallbackSite ?? 'q' }
33
+ id="site-search-query"
34
+ spellcheck="true"
35
+ autocomplete="off" />
36
+ <button id="site-search-button" type="submit">{ _(t.search.submit) }</button>
37
+ </div>
38
+ </fieldset>
39
+ </form>
40
+ <div id="site-search-results" data-title={ _(t.search.results_title) } data-emptytitle={ _(t.search.no_results_title) }>
41
+ </div>
42
+ <script src={ SITE.subfolder + '/js/search.js' } type="module" async></script>
43
+ </Default>
@@ -0,0 +1,48 @@
1
+ ---
2
+ import type { Frontmatter } from '@config';
3
+ import type { MarkdownInstance } from 'astro';
4
+
5
+ import { getPages } from "@util/PageQueries.astro";
6
+ import { isAuthor } from "@util/PageTypeFilters.astro";
7
+
8
+ type BannerImage = { src: string; alt: string } | null;
9
+ type AuthorList = {
10
+ image: BannerImage | null;
11
+ writers: MarkdownInstance<Record<string, any>>[];
12
+ };
13
+
14
+ export async function getAuthors () {
15
+ const authors = await getPages(isAuthor);
16
+ return authors;
17
+ }
18
+
19
+ export async function getAuthorList (frontmatter: Frontmatter) {
20
+ const authors = await getAuthors();
21
+
22
+ const result: AuthorList = {
23
+ image: null,
24
+ writers: [],
25
+ };
26
+
27
+ (frontmatter.authors ?? []).forEach((a) => {
28
+ const matches = authors.filter((x) => x.frontmatter.id == a);
29
+
30
+ if (matches.length == 0) {
31
+ console.warn("Unknown author", a);
32
+ }
33
+
34
+ if (matches.length > 1) {
35
+ console.warn("Multiple authors with id", a);
36
+ }
37
+
38
+ if (matches.length == 1) {
39
+ result.writers.push(matches[0]);
40
+ if (result.image == null) {
41
+ result.image = matches[0].frontmatter.bannerImage;
42
+ }
43
+ }
44
+ });
45
+
46
+ return result;
47
+ }
48
+ ---