Skip to content

Commit faaf6a3

Browse files
committed
Generate extensions page at build time
1 parent c7accff commit faaf6a3

File tree

5 files changed

+172
-8
lines changed

5 files changed

+172
-8
lines changed

.github/workflows/deploy.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches:
66
- main
7+
workflow_dispatch:
78

89
permissions:
910
contents: read

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"@astrojs/check": "^0.9.4",
1414
"@astrojs/starlight": "^0.30.5",
1515
"astro": "^5.1.4",
16+
"marked": "^15.0.6",
1617
"sharp": "^0.32.5",
1718
"starlight-blog": "^0.16.1",
1819
"starlight-links-validator": "^0.14.1",

pnpm-lock.yaml

Lines changed: 10 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/content/docs/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ A common question we get is what moonlight does differently, and why it's worth
4343

4444
<CardGrid>
4545
<Card title="Ready to use extension list" icon="list-format">
46-
50+ extensions ready to use in a few clicks, made by community developers
46+
<a href="/extensions">50+ extensions</a> ready to use in a few clicks, made by community developers
4747
</Card>
4848
<Card title="Easy, persistent installation" icon="download">
4949
[Install into Discord](/using/install) and never think about it again, with automatic patching and update notifications

src/pages/extensions.astro

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
---
2+
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
3+
import {
4+
LinkButton,
5+
Tabs,
6+
TabItem,
7+
Badge
8+
} from "@astrojs/starlight/components";
9+
import { marked } from "marked";
10+
11+
// Copied from moonlight itself
12+
const apiLevel = 2;
13+
14+
enum ExtensionTag {
15+
Accessibility = "accessibility",
16+
Appearance = "appearance",
17+
Chat = "chat",
18+
Commands = "commands",
19+
ContextMenu = "contextMenu",
20+
DangerZone = "dangerZone",
21+
Development = "development",
22+
Fixes = "fixes",
23+
Fun = "fun",
24+
Markdown = "markdown",
25+
Voice = "voice",
26+
Privacy = "privacy",
27+
Profiles = "profiles",
28+
QualityOfLife = "qol",
29+
Library = "library"
30+
}
31+
const tagNames: Record<ExtensionTag, string> = {
32+
[ExtensionTag.Accessibility]: "Accessibility",
33+
[ExtensionTag.Appearance]: "Appearance",
34+
[ExtensionTag.Chat]: "Chat",
35+
[ExtensionTag.Commands]: "Commands",
36+
[ExtensionTag.ContextMenu]: "Context Menu",
37+
[ExtensionTag.DangerZone]: "Danger Zone",
38+
[ExtensionTag.Development]: "Development",
39+
[ExtensionTag.Fixes]: "Fixes",
40+
[ExtensionTag.Fun]: "Fun",
41+
[ExtensionTag.Markdown]: "Markdown",
42+
[ExtensionTag.Voice]: "Voice",
43+
[ExtensionTag.Privacy]: "Privacy",
44+
[ExtensionTag.Profiles]: "Profiles",
45+
[ExtensionTag.QualityOfLife]: "Quality of Life",
46+
[ExtensionTag.Library]: "Library"
47+
};
48+
49+
type ExtensionManifest = {
50+
id: string;
51+
version?: string;
52+
apiLevel?: number;
53+
54+
meta?: {
55+
name?: string;
56+
tagline?: string;
57+
description?: string;
58+
authors?: (string | { name: string })[];
59+
deprecated?: boolean;
60+
tags?: ExtensionTag[];
61+
source?: string;
62+
changelog?: string;
63+
};
64+
65+
dependencies?: string[];
66+
suggested?: string[];
67+
incompatible?: string[];
68+
69+
cors?: string[];
70+
blocked?: string[];
71+
72+
download: string;
73+
};
74+
75+
const extensions = (
76+
(await fetch(
77+
"https://moonlight-mod.github.io/extensions-dist/repo.json"
78+
).then((res) => res.json())) as ExtensionManifest[]
79+
)
80+
.filter((ext) => ext.apiLevel === apiLevel)
81+
.sort((a, b) => {
82+
const aName = a.meta?.name ?? a.id;
83+
const bName = b.meta?.name ?? b.id;
84+
return aName.localeCompare(bName);
85+
});
86+
---
87+
88+
<StarlightPage
89+
frontmatter={{
90+
title: "Extension list"
91+
}}
92+
headings={extensions.map((ext) => {
93+
return {
94+
text: ext.meta?.name || ext.id,
95+
slug: ext.id,
96+
depth: 2
97+
};
98+
})}
99+
>
100+
<p>
101+
This list is automatically generated from the official repository. It does
102+
not contain the extensions built into moonlight. This list may be out of
103+
date.
104+
</p>
105+
106+
{
107+
extensions.map((ext) => (
108+
<div id={ext.id}>
109+
<header>
110+
<h2>{ext.meta?.name || ext.id}</h2>
111+
</header>
112+
113+
<Tabs>
114+
<TabItem label="Info">
115+
{ext.meta?.authors && (
116+
<h6 class="noMargin">
117+
by
118+
{ext.meta.authors
119+
.map((author) =>
120+
typeof author === "string" ? author : author.name
121+
)
122+
.join(", ")}
123+
</h6>
124+
)}
125+
126+
{ext.meta?.tags && (
127+
<div class="noMargin">
128+
{ext.meta.tags.map((tag) => (
129+
<Badge text={tagNames[tag]} />
130+
))}
131+
</div>
132+
)}
133+
134+
{ext.meta?.tagline && <span>{ext.meta.tagline}</span>}
135+
</TabItem>
136+
137+
{ext.meta?.description && (
138+
<TabItem label="Description">
139+
<p set:html={marked(ext.meta.description)} />
140+
</TabItem>
141+
)}
142+
143+
<TabItem label="Links">
144+
{ext.meta?.source && (
145+
<LinkButton href={ext.meta.source}>Source code</LinkButton>
146+
)}
147+
<LinkButton href={ext.download}>Download .asar</LinkButton>
148+
</TabItem>
149+
</Tabs>
150+
</div>
151+
))
152+
}
153+
</StarlightPage>
154+
155+
<style>
156+
.noMargin {
157+
margin: 0 !important;
158+
}
159+
</style>

0 commit comments

Comments
 (0)