Skip to content

Commit 0516273

Browse files
committed
added the live activity changes
1 parent 9321473 commit 0516273

11 files changed

Lines changed: 385 additions & 12 deletions

File tree

plugins/blog-plugin.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Wraps @docusaurus/plugin-content-blog to expose the latest posts as
3+
* global data, so homepage components can render them via usePluginData.
4+
* Registered in docusaurus.config.ts in place of the preset's blog
5+
* (preset blog is set to false) with the same options.
6+
*/
7+
const path = require("path");
8+
const fs = require("fs");
9+
const blogPluginExports = require("@docusaurus/plugin-content-blog");
10+
11+
const defaultBlogPlugin = blogPluginExports.default;
12+
13+
// Posts don't set an `image` frontmatter, so fall back to the first
14+
// image referenced in the post body for the card cover
15+
function extractFirstImage(content) {
16+
if (!content) return undefined;
17+
const markdownImage = content.match(/!\[[^\]]*\]\(\s*([^)\s]+)/);
18+
if (markdownImage) return markdownImage[1];
19+
const htmlImage = content.match(/<img[^>]+src=["']([^"']+)["']/i);
20+
return htmlImage ? htmlImage[1] : undefined;
21+
}
22+
23+
// Covers referenced relative to the post folder (e.g. ./assets/x.png) aren't
24+
// served outside the post page, so copy them into static/ and return that URL.
25+
// Generated files live in static/img/blog-covers/ (gitignored).
26+
function resolveCover(post, siteDir) {
27+
const raw =
28+
post.metadata.frontMatter?.image || extractFirstImage(post.content);
29+
if (!raw) return undefined;
30+
if (/^(https?:)?\/\//i.test(raw) || raw.startsWith("/")) return raw;
31+
32+
try {
33+
const sourcePath = post.metadata.source.replace(/^@site[\\/]/, "");
34+
const postDir = path.dirname(path.join(siteDir, sourcePath));
35+
const imagePath = path.resolve(postDir, raw);
36+
if (!fs.existsSync(imagePath)) return undefined;
37+
38+
const coversDir = path.join(siteDir, "static", "img", "blog-covers");
39+
const fileName = `${path.basename(postDir)}-${path.basename(imagePath)}`;
40+
fs.mkdirSync(coversDir, { recursive: true });
41+
fs.copyFileSync(imagePath, path.join(coversDir, fileName));
42+
return `/img/blog-covers/${fileName}`;
43+
} catch {
44+
return undefined;
45+
}
46+
}
47+
48+
async function blogPluginEnhanced(context, options) {
49+
const blogPlugin = await defaultBlogPlugin(context, options);
50+
51+
return {
52+
...blogPlugin,
53+
async contentLoaded(args) {
54+
await blogPlugin.contentLoaded?.(args);
55+
56+
const { content, actions } = args;
57+
const recentPosts = [...content.blogPosts]
58+
.sort(
59+
(a, b) =>
60+
new Date(b.metadata.date).getTime() -
61+
new Date(a.metadata.date).getTime(),
62+
)
63+
.slice(0, 4)
64+
.map((post) => {
65+
const author = post.metadata.authors?.[0];
66+
return {
67+
title: post.metadata.title,
68+
permalink: post.metadata.permalink,
69+
date: post.metadata.date,
70+
readingTime: post.metadata.readingTime,
71+
image: resolveCover(post, context.siteDir),
72+
author: author
73+
? {
74+
name: author.name,
75+
handle: author.key || author.name,
76+
imageURL: author.imageURL,
77+
}
78+
: null,
79+
};
80+
});
81+
82+
actions.setGlobalData({ recentPosts });
83+
},
84+
};
85+
}
86+
87+
module.exports = {
88+
...blogPluginExports,
89+
default: blogPluginEnhanced,
90+
};

src/components/FloatingContributors/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,9 +497,11 @@ const FloatingContributors: React.FC<FloatingContributorsProps> = ({
497497
}
498498
};
499499

500-
// Bots are filtered at fetch time; this also guards stale cached state
500+
// Bots are filtered at fetch time; this also guards stale cached state.
501+
// Unrecognized event types ("other" → ACTIVE badge) are hidden from the feed.
501502
const visibleActivities = activities.filter(
502-
(activity) => !isBotAccount(activity.contributor.login),
503+
(activity) =>
504+
!isBotAccount(activity.contributor.login) && activity.action !== "other",
503505
);
504506
const visibleContributors = contributors.filter(
505507
(contributor) => !isBotAccount(contributor.login),
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/* From the Blog — full-width band, theme tokens switched by [data-theme] */
2+
.from-blog {
3+
--fb-bg: #f2f3ef;
4+
--fb-card-bg: #ffffff;
5+
--fb-cover-bg: #f6f7f4;
6+
--fb-border: rgba(12, 24, 16, 0.1);
7+
--fb-text: #111512;
8+
--fb-muted: rgba(17, 21, 18, 0.62);
9+
--fb-green: #129450;
10+
11+
background: var(--fb-bg);
12+
border-top: 1px solid var(--fb-border);
13+
border-bottom: 1px solid var(--fb-border);
14+
padding: 96px 24px;
15+
font-family: "Instrument Sans", Inter, -apple-system, BlinkMacSystemFont,
16+
sans-serif;
17+
}
18+
19+
[data-theme="dark"] .from-blog {
20+
--fb-bg: #0f110f;
21+
--fb-card-bg: #131513;
22+
--fb-cover-bg: #181b18;
23+
--fb-border: rgba(255, 255, 255, 0.09);
24+
--fb-text: #f4f6f3;
25+
--fb-muted: rgba(244, 246, 243, 0.6);
26+
--fb-green: #37d67a;
27+
}
28+
29+
.from-blog__inner {
30+
max-width: 1180px;
31+
margin: 0 auto;
32+
}
33+
34+
.from-blog__eyebrow {
35+
margin: 0 0 14px;
36+
font-size: 13px;
37+
font-weight: 600;
38+
text-transform: uppercase;
39+
letter-spacing: 0.14em;
40+
color: var(--fb-green);
41+
}
42+
43+
.from-blog__header {
44+
display: flex;
45+
align-items: flex-end;
46+
justify-content: space-between;
47+
gap: 24px;
48+
margin-bottom: 40px;
49+
}
50+
51+
.from-blog__title {
52+
margin: 0;
53+
font-family: "Space Grotesk", Inter, sans-serif;
54+
font-weight: 700;
55+
font-size: 42px;
56+
letter-spacing: -0.03em;
57+
color: var(--fb-text);
58+
}
59+
60+
.from-blog__subtitle {
61+
margin: 10px 0 0;
62+
font-size: 16px;
63+
color: var(--fb-muted);
64+
}
65+
66+
.from-blog__viewall {
67+
flex-shrink: 0;
68+
font-size: 14px;
69+
font-weight: 600;
70+
color: var(--fb-green);
71+
}
72+
73+
.from-blog__viewall:hover {
74+
color: var(--fb-green);
75+
text-decoration: underline;
76+
}
77+
78+
.from-blog__grid {
79+
display: grid;
80+
grid-template-columns: repeat(4, 1fr);
81+
gap: 16px;
82+
}
83+
84+
.from-blog__card {
85+
display: flex;
86+
flex-direction: column;
87+
border-radius: 16px;
88+
border: 1px solid var(--fb-border);
89+
background: var(--fb-card-bg);
90+
overflow: hidden;
91+
transition: border-color 0.2s ease;
92+
}
93+
94+
.from-blog__card:hover {
95+
border-color: var(--fb-green);
96+
text-decoration: none;
97+
}
98+
99+
.from-blog__cover {
100+
aspect-ratio: 16 / 9;
101+
background: var(--fb-cover-bg);
102+
}
103+
104+
.from-blog__cover img {
105+
width: 100%;
106+
height: 100%;
107+
object-fit: cover;
108+
display: block;
109+
}
110+
111+
.from-blog__body {
112+
display: flex;
113+
flex-direction: column;
114+
flex: 1;
115+
padding: 18px;
116+
}
117+
118+
.from-blog__post-title {
119+
margin: 0;
120+
font-size: 15.5px;
121+
font-weight: 600;
122+
line-height: 1.4;
123+
color: var(--fb-text);
124+
}
125+
126+
.from-blog__meta {
127+
display: flex;
128+
align-items: center;
129+
gap: 8px;
130+
margin-top: auto;
131+
padding-top: 16px;
132+
}
133+
134+
.from-blog__avatar {
135+
width: 20px;
136+
height: 20px;
137+
border-radius: 50%;
138+
flex-shrink: 0;
139+
}
140+
141+
.from-blog__meta-text {
142+
font-size: 12.5px;
143+
color: var(--fb-muted);
144+
}
145+
146+
@media (max-width: 1024px) {
147+
.from-blog__grid {
148+
grid-template-columns: repeat(2, 1fr);
149+
}
150+
}
151+
152+
@media (max-width: 640px) {
153+
.from-blog {
154+
padding: 64px 16px;
155+
}
156+
157+
.from-blog__header {
158+
flex-direction: column;
159+
align-items: flex-start;
160+
gap: 12px;
161+
margin-bottom: 28px;
162+
}
163+
164+
.from-blog__title {
165+
font-size: 32px;
166+
}
167+
168+
.from-blog__grid {
169+
grid-template-columns: 1fr;
170+
}
171+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import React from "react";
2+
import Link from "@docusaurus/Link";
3+
import { usePluginData } from "@docusaurus/useGlobalData";
4+
import "./FromTheBlog.css";
5+
6+
interface RecentPost {
7+
title: string;
8+
permalink: string;
9+
date: string;
10+
readingTime?: number;
11+
image?: string;
12+
author: {
13+
name: string;
14+
handle: string;
15+
imageURL?: string;
16+
} | null;
17+
}
18+
19+
interface BlogGlobalData {
20+
recentPosts?: RecentPost[];
21+
}
22+
23+
const FromTheBlog: React.FC = () => {
24+
const { recentPosts = [] } =
25+
(usePluginData("docusaurus-plugin-content-blog") as BlogGlobalData) ?? {};
26+
27+
if (recentPosts.length === 0) {
28+
return null;
29+
}
30+
31+
return (
32+
<section className="from-blog">
33+
<div className="from-blog__inner">
34+
<p className="from-blog__eyebrow">✦ Blog</p>
35+
<div className="from-blog__header">
36+
<div>
37+
<h2 className="from-blog__title">From the Blog</h2>
38+
<p className="from-blog__subtitle">
39+
Latest articles from our contributors
40+
</p>
41+
</div>
42+
<Link to="/blogs" className="from-blog__viewall">
43+
View all →
44+
</Link>
45+
</div>
46+
47+
<div className="from-blog__grid">
48+
{recentPosts.map((post) => (
49+
<Link
50+
key={post.permalink}
51+
to={post.permalink}
52+
className="from-blog__card"
53+
>
54+
<div className="from-blog__cover">
55+
{post.image && (
56+
<img src={post.image} alt={post.title} loading="lazy" />
57+
)}
58+
</div>
59+
<div className="from-blog__body">
60+
<h3 className="from-blog__post-title">{post.title}</h3>
61+
{post.author && (
62+
<div className="from-blog__meta">
63+
{post.author.imageURL && (
64+
<img
65+
className="from-blog__avatar"
66+
src={post.author.imageURL}
67+
alt={post.author.name}
68+
loading="lazy"
69+
/>
70+
)}
71+
<span className="from-blog__meta-text">
72+
@{post.author.handle}
73+
{post.readingTime
74+
? ` · ${Math.ceil(post.readingTime)} min read`
75+
: ""}
76+
</span>
77+
</div>
78+
)}
79+
</div>
80+
</Link>
81+
))}
82+
</div>
83+
</div>
84+
</section>
85+
);
86+
};
87+
88+
export default FromTheBlog;

src/components/blogCarousel/blogCarousel.css

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@
9595

9696
/* ── Card image ── */
9797
.card-image {
98-
height: 200px;
98+
/* Blog covers are ~1.9:1 (1730x909); matching it shows the full image */
99+
aspect-ratio: 1.9;
99100
overflow: hidden;
100101
position: relative;
101102
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
@@ -129,7 +130,7 @@
129130
display: block;
130131
width: 100%;
131132
height: 100%;
132-
object-fit: cover;
133+
object-fit: contain;
133134
transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1),
134135
filter 0.45s ease;
135136
}
@@ -662,10 +663,6 @@
662663

663664
/* ── Responsive ── */
664665
@media (max-width: 768px) {
665-
.card-image {
666-
height: 160px;
667-
}
668-
669666
.card-content {
670667
padding: 16px 18px 14px;
671668
}

0 commit comments

Comments
 (0)