Skip to content

Commit 747b989

Browse files
committed
feat(changelog): add changelog page with styling and markdown rendering
- Implemented a new changelog page with a responsive layout. - Added CSS styles for the changelog components, including headers, entries, and badges. - Created a ReleaseCard component to display individual release details. - Introduced a markdown rendering function to parse and display release notes. - Included functionality for expanding and collapsing release body content. - Integrated release data from a JSON file for dynamic content rendering.
1 parent cd76db4 commit 747b989

8 files changed

Lines changed: 576 additions & 0 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Update Releases Data
2+
3+
on:
4+
release:
5+
types: [published, edited, deleted]
6+
workflow_dispatch: {}
7+
8+
jobs:
9+
update-releases-json:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: write
13+
steps:
14+
- uses: actions/checkout@v4
15+
with:
16+
token: ${{ secrets.GITHUB_TOKEN }}
17+
persist-credentials: true
18+
19+
- uses: actions/setup-node@v4
20+
with:
21+
node-version: 20
22+
23+
- name: Fetch latest GitHub releases
24+
run: node scripts/fetch-releases.js
25+
env:
26+
DOCUSAURUS_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27+
28+
- name: Commit updated releases.json
29+
run: |
30+
git config user.name "github-actions[bot]"
31+
git config user.email "github-actions[bot]@users.noreply.github.com"
32+
git add src/data/releases.json
33+
git diff --cached --quiet && echo "No changes to commit" || git commit -m "chore: update releases.json for /updates page"
34+
git push

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
"docusaurus": "docusaurus",
77
"dev": "docusaurus start",
88
"start": "docusaurus start",
9+
"prestart": "node scripts/fetch-releases.js",
10+
"prebuild": "node scripts/fetch-releases.js",
11+
"fetch:releases": "node scripts/fetch-releases.js",
912
"build": "docusaurus build",
1013
"swizzle": "docusaurus swizzle",
1114
"deploy": "docusaurus deploy",

scripts/fetch-releases.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Fetches GitHub Releases for recodehive/recode-website and writes them to
3+
* src/data/releases.json, consumed statically by the /updates page.
4+
* Run via `npm run fetch:releases` (also wired into prestart/prebuild).
5+
*/
6+
const fs = require("fs");
7+
const path = require("path");
8+
9+
const REPO = "recodehive/recode-website";
10+
const OUTPUT_PATH = path.join(__dirname, "..", "src", "data", "releases.json");
11+
12+
async function fetchReleases() {
13+
const headers = {
14+
Accept: "application/vnd.github+json",
15+
"User-Agent": "recode-website-updates-page",
16+
};
17+
const token = process.env.DOCUSAURUS_GIT_TOKEN;
18+
if (token) {
19+
headers.Authorization = `Bearer ${token}`;
20+
}
21+
22+
const res = await fetch(
23+
`https://api.github.com/repos/${REPO}/releases?per_page=50`,
24+
{ headers },
25+
);
26+
27+
if (!res.ok) {
28+
throw new Error(`GitHub API request failed: ${res.status} ${res.statusText}`);
29+
}
30+
31+
const releases = await res.json();
32+
33+
return releases
34+
.filter((release) => !release.draft)
35+
.map((release) => ({
36+
id: release.id,
37+
name: release.name || release.tag_name,
38+
tagName: release.tag_name,
39+
publishedAt: release.published_at,
40+
htmlUrl: release.html_url,
41+
body: release.body || "",
42+
prerelease: release.prerelease,
43+
author: {
44+
login: release.author?.login ?? "unknown",
45+
htmlUrl: release.author?.html_url ?? `https://github.com/${release.author?.login ?? ""}`,
46+
},
47+
}));
48+
}
49+
50+
async function main() {
51+
try {
52+
const releases = await fetchReleases();
53+
fs.mkdirSync(path.dirname(OUTPUT_PATH), { recursive: true });
54+
fs.writeFileSync(OUTPUT_PATH, JSON.stringify(releases, null, 2) + "\n");
55+
console.log(`Fetched ${releases.length} release(s) -> ${path.relative(process.cwd(), OUTPUT_PATH)}`);
56+
} catch (err) {
57+
console.warn(`fetch-releases: ${err.message}`);
58+
if (!fs.existsSync(OUTPUT_PATH)) {
59+
fs.mkdirSync(path.dirname(OUTPUT_PATH), { recursive: true });
60+
fs.writeFileSync(OUTPUT_PATH, "[]\n");
61+
}
62+
console.warn("fetch-releases: keeping existing src/data/releases.json (build not blocked)");
63+
}
64+
}
65+
66+
main();

src/data/releases.json

Lines changed: 80 additions & 0 deletions
Large diffs are not rendered by default.

src/pages/changelog/index.css

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
.changelog__page {
2+
max-width: 860px;
3+
margin: 0 auto;
4+
padding: 3rem 1.5rem 5rem;
5+
}
6+
7+
.changelog__header {
8+
text-align: center;
9+
margin-bottom: 3rem;
10+
}
11+
12+
.changelog__header h1 {
13+
font-family:
14+
"Space Grotesk",
15+
Inter,
16+
-apple-system,
17+
BlinkMacSystemFont,
18+
"Segoe UI",
19+
sans-serif;
20+
font-weight: 700;
21+
font-size: 2.75rem;
22+
letter-spacing: -0.03em;
23+
margin-bottom: 0.75rem;
24+
}
25+
26+
.changelog__header-brand {
27+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
28+
color: #16a34a;
29+
font-weight: 700;
30+
}
31+
32+
[data-theme="dark"] .changelog__header-brand {
33+
color: #4ade80;
34+
}
35+
36+
.changelog__header p {
37+
font-family: var(--font-family);
38+
color: var(--ifm-color-emphasis-600);
39+
font-size: 1.05rem;
40+
}
41+
42+
.changelog__timeline {
43+
position: relative;
44+
padding-left: 1.5rem;
45+
border-left: 2px solid var(--ifm-color-emphasis-300);
46+
}
47+
48+
.changelog__entry {
49+
position: relative;
50+
margin-bottom: 2rem;
51+
}
52+
53+
.changelog__entry:last-child {
54+
margin-bottom: 0;
55+
}
56+
57+
.changelog__dot {
58+
position: absolute;
59+
left: -1.6rem;
60+
top: 1.6rem;
61+
width: 10px;
62+
height: 10px;
63+
border-radius: 50%;
64+
background: var(--ifm-color-emphasis-800);
65+
border: 2px solid var(--ifm-background-color);
66+
}
67+
68+
.changelog__card {
69+
border: 1px solid var(--ifm-color-emphasis-300);
70+
border-radius: 12px;
71+
background: var(--ifm-background-surface-color);
72+
padding: 1.5rem 1.75rem;
73+
}
74+
75+
.changelog__card-header {
76+
display: flex;
77+
align-items: center;
78+
gap: 0.75rem;
79+
margin-bottom: 0.9rem;
80+
}
81+
82+
.changelog__badge {
83+
display: inline-flex;
84+
align-items: center;
85+
gap: 0.35rem;
86+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
87+
font-size: 0.7rem;
88+
font-weight: 700;
89+
letter-spacing: 0.03em;
90+
color: #16a34a;
91+
background: var(--ifm-color-success-contrast-background);
92+
border: 1px solid var(--ifm-color-success-light);
93+
border-radius: 6px;
94+
padding: 0.2rem 0.55rem;
95+
}
96+
97+
[data-theme="dark"] .changelog__badge {
98+
color: #4ade80;
99+
}
100+
101+
.changelog__date {
102+
font-size: 0.85rem;
103+
color: var(--ifm-color-emphasis-600);
104+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
105+
}
106+
107+
.changelog__title {
108+
font-family:
109+
"Space Grotesk",
110+
Inter,
111+
-apple-system,
112+
BlinkMacSystemFont,
113+
"Segoe UI",
114+
sans-serif;
115+
font-size: 1.35rem;
116+
font-weight: 700;
117+
letter-spacing: -0.01em;
118+
margin-bottom: 0.75rem;
119+
}
120+
121+
.changelog__title a {
122+
color: var(--ifm-font-color-base);
123+
text-decoration: none;
124+
}
125+
126+
.changelog__title a:hover {
127+
color: var(--ifm-color-primary);
128+
}
129+
130+
.changelog__body-wrap--clamped {
131+
max-height: 9rem;
132+
overflow: hidden;
133+
position: relative;
134+
mask-image: linear-gradient(to bottom, black 60%, transparent 100%);
135+
}
136+
137+
.changelog__body-heading {
138+
font-family:
139+
"Space Grotesk",
140+
Inter,
141+
-apple-system,
142+
BlinkMacSystemFont,
143+
"Segoe UI",
144+
sans-serif;
145+
font-size: 1rem;
146+
font-weight: 700;
147+
margin: 0.75rem 0 0.4rem;
148+
}
149+
150+
.changelog__body-text,
151+
.changelog__body-list {
152+
font-family: var(--font-family);
153+
color: var(--ifm-color-emphasis-700);
154+
font-size: 0.95rem;
155+
line-height: 1.6;
156+
}
157+
158+
.changelog__body-list {
159+
margin: 0 0 0.5rem;
160+
padding-left: 1.2rem;
161+
}
162+
163+
.changelog__toggle {
164+
font-family: var(--font-family);
165+
background: none;
166+
border: none;
167+
color: var(--ifm-color-primary);
168+
font-size: 0.85rem;
169+
font-weight: 600;
170+
cursor: pointer;
171+
padding: 0.4rem 0;
172+
}
173+
174+
.changelog__toggle:hover {
175+
text-decoration: underline;
176+
}
177+
178+
.changelog__divider {
179+
margin: 1rem 0 0.75rem;
180+
border: none;
181+
border-top: 1px solid var(--ifm-color-emphasis-300);
182+
}
183+
184+
.changelog__author {
185+
font-family: var(--font-family);
186+
font-size: 0.85rem;
187+
color: var(--ifm-color-emphasis-600);
188+
margin: 0;
189+
}
190+
191+
.changelog__empty {
192+
font-family: var(--font-family);
193+
text-align: center;
194+
color: var(--ifm-color-emphasis-600);
195+
}
196+
197+
@media screen and (max-width: 768px) {
198+
.changelog__header h1 {
199+
font-size: 2rem;
200+
}
201+
}

0 commit comments

Comments
 (0)