Skip to content

Commit 1304851

Browse files
committed
Add livestreams page
1 parent 6fd0fb0 commit 1304851

File tree

5 files changed

+702
-0
lines changed

5 files changed

+702
-0
lines changed

docs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
"tools-and-libraries",
9696
"tutorials",
9797
"newsletter",
98+
"livestreams",
9899
"forms/billing-support",
99100
"developer-terms"
100101
]

livestreams.mdx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
title: X API Livestreams
3+
sidebarTitle: Livestreams
4+
mode: wide
5+
icon: video
6+
---
7+
8+
import { BroadcastCarousel } from '/snippets/broadcast-carousel.mdx';
9+
10+
View recordings and replays of previous broadcasts about the X API, designed to help the developer community learn and build with our tools.
11+
12+
These include deep dives, getting started guides, and more.
13+
14+
## Past Broadcasts
15+
16+
<BroadcastCarousel broadcasts={[
17+
{
18+
url: 'https://x.com/i/broadcasts/1dRKZayWLvwxB',
19+
title: 'Community Notes AI Note Writer API',
20+
date: 'September 25, 2025'
21+
},
22+
{
23+
url: 'https://x.com/i/broadcasts/1vOxwdbkgMdKB',
24+
title: 'Real-time data streaming with the X API v2',
25+
date: 'August 21, 2025'
26+
},
27+
{
28+
url: 'https://x.com/i/broadcasts/1jMJgkmOONyJL',
29+
title: 'Account Activity API v2',
30+
date: 'July 24, 2025'
31+
},
32+
{
33+
url: 'https://x.com/i/broadcasts/1BdGYqOwPXyGX',
34+
title: 'Exploring the new X API v2 analytics endpoints',
35+
date: 'June 12, 2025'
36+
},
37+
{
38+
url: 'https://x.com/i/broadcasts/1yoJMoLQqdRKQ',
39+
title: 'Exploring the X API Developer Experience',
40+
date: 'April 9, 2025'
41+
},
42+
{
43+
url: 'https://x.com/i/broadcasts/1yoKMoQbjlXJQ',
44+
title: 'Getting started with the new Media Upload endpoint in X API v2',
45+
date: 'March 5, 2025'
46+
},
47+
{
48+
url: 'https://x.com/i/broadcasts/1BRJjPqnBqaKw',
49+
title: 'Migrating developer apps to the X API 2',
50+
date: 'May 14, 2024'
51+
}
52+
]} />

snippets/broadcast-card.mdx

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
export const BroadcastCard = ({ url, title, date }) => {
2+
if (!url) {
3+
return null;
4+
}
5+
6+
// Extract broadcast ID from URL for potential future use
7+
const broadcastId = url.match(/\/i\/broadcasts\/([^\/\?#]+)/)?.[1];
8+
9+
// Parse title and date if date is in parentheses in title
10+
let displayTitle = title;
11+
let displayDate = date;
12+
13+
if (title && !date) {
14+
const dateMatch = title.match(/\(([^)]+)\)$/);
15+
if (dateMatch) {
16+
displayDate = dateMatch[1];
17+
displayTitle = title.replace(/\s*\([^)]+\)$/, '').trim();
18+
}
19+
}
20+
21+
return (
22+
<a
23+
href={url}
24+
target="_blank"
25+
rel="noopener noreferrer"
26+
className="block not-prose text-inherit no-underline flex-shrink-0 w-[480px] max-w-[480px] rounded-xl overflow-hidden bg-black border border-gray-700 transition-transform hover:-translate-y-0.5 hover:shadow-lg"
27+
style={{ margin: 0 }}
28+
>
29+
{/* Preview container with 16:9 aspect ratio */}
30+
<div
31+
className="relative w-full overflow-hidden"
32+
style={{
33+
paddingTop: '56.25%', // 16:9 aspect ratio
34+
background: 'linear-gradient(135deg, #0f0f0f 0%, #050505 100%)',
35+
backgroundSize: 'cover',
36+
backgroundPosition: 'center'
37+
}}
38+
>
39+
{/* Logo container (offset position) */}
40+
<div
41+
className="absolute opacity-[0.15]"
42+
style={{
43+
bottom: '-14%',
44+
left: '-7%',
45+
width: '400px',
46+
height: '400px'
47+
}}
48+
>
49+
<svg
50+
width="400"
51+
height="400"
52+
viewBox="0 0 1200 1227"
53+
fill="none"
54+
xmlns="http://www.w3.org/2000/svg"
55+
>
56+
<path
57+
d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z"
58+
fill="white"
59+
/>
60+
</svg>
61+
</div>
62+
63+
{/* Overlay with play button */}
64+
<div
65+
className="absolute inset-0 flex flex-col items-center justify-center text-white cursor-pointer bg-black/30"
66+
>
67+
<div
68+
className="w-20 h-20 rounded-full bg-black/90 border-2 border-white/30 flex items-center justify-center mb-4 transition-transform hover:scale-110"
69+
>
70+
<div className="text-white text-3xl ml-1">▶</div>
71+
</div>
72+
</div>
73+
</div>
74+
75+
{/* Title container */}
76+
{displayTitle && (
77+
<div className="p-4 bg-black border-t border-gray-700">
78+
<div className="text-white text-base font-semibold leading-snug mb-1">
79+
{displayTitle}
80+
</div>
81+
{displayDate && (
82+
<div className="text-gray-400 text-sm font-normal leading-snug">
83+
{displayDate}
84+
</div>
85+
)}
86+
</div>
87+
)}
88+
</a>
89+
);
90+
};
91+

snippets/broadcast-carousel.mdx

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
export const BroadcastCarousel = ({ broadcasts }) => {
2+
if (!broadcasts) {
3+
return null;
4+
}
5+
if (broadcasts.length === 0) {
6+
return null;
7+
}
8+
const sorted = [...broadcasts].sort((a, b) => {
9+
const dateA = a.date ? new Date(a.date) : null;
10+
const dateB = b.date ? new Date(b.date) : null;
11+
if (!dateA && !dateB) return 0;
12+
if (!dateA) return 1;
13+
if (!dateB) return -1;
14+
if (isNaN(dateA.getTime()) || isNaN(dateB.getTime())) return 0;
15+
return dateB - dateA;
16+
});
17+
18+
return (
19+
<div className="flex overflow-x-auto overflow-y-hidden gap-5 py-5">
20+
{sorted.map((broadcast, index) => {
21+
if (!broadcast.url) return null;
22+
return (
23+
<a
24+
key={broadcast.url || index}
25+
href={broadcast.url}
26+
target="_blank"
27+
rel="noopener noreferrer"
28+
className="block not-prose text-inherit no-underline flex-shrink-0 w-[480px] max-w-[480px] rounded-xl overflow-hidden bg-black border border-gray-700 transition-transform hover:-translate-y-0.5 hover:shadow-lg"
29+
style={{ margin: 0 }}
30+
>
31+
<div
32+
className="relative w-full overflow-hidden"
33+
style={{
34+
paddingTop: '56.25%',
35+
background: 'linear-gradient(135deg, #0f0f0f 0%, #050505 100%)',
36+
backgroundSize: 'cover',
37+
backgroundPosition: 'center'
38+
}}
39+
>
40+
<div
41+
className="absolute opacity-[0.15]"
42+
style={{
43+
bottom: '-14%',
44+
left: '-7%',
45+
width: '400px',
46+
height: '400px'
47+
}}
48+
>
49+
<svg
50+
width="400"
51+
height="400"
52+
viewBox="0 0 1200 1227"
53+
fill="none"
54+
xmlns="http://www.w3.org/2000/svg"
55+
>
56+
<path
57+
d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z"
58+
fill="white"
59+
/>
60+
</svg>
61+
</div>
62+
<div
63+
className="absolute inset-0 flex items-center justify-center text-white cursor-pointer bg-black/30"
64+
>
65+
<div
66+
className="w-20 h-20 rounded-full bg-black/90 border-2 border-white/30 flex items-center justify-center transition-transform hover:scale-110"
67+
>
68+
<div className="text-white text-3xl ml-1">▶</div>
69+
</div>
70+
</div>
71+
</div>
72+
{broadcast.title && (
73+
<div className="p-4 bg-black border-t border-gray-700">
74+
<div className="text-white text-base font-semibold leading-snug mb-1">
75+
{broadcast.title}
76+
</div>
77+
{broadcast.date && (
78+
<div className="text-gray-400 text-sm font-normal leading-snug">
79+
{broadcast.date}
80+
</div>
81+
)}
82+
</div>
83+
)}
84+
</a>
85+
);
86+
})}
87+
</div>
88+
);
89+
};

0 commit comments

Comments
 (0)