Skip to content

Commit 07da8fd

Browse files
feat: updated changelog and addressed duplicate fns
1 parent 33e6b91 commit 07da8fd

File tree

8 files changed

+478
-238
lines changed

8 files changed

+478
-238
lines changed

apps/sim/app/(landing)/blog/[slug]/article-sidebar.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Image from 'next/image'
22
import Link from 'next/link'
33
import type { Author, BlogMeta } from '@/lib/blog/schema'
4+
import { formatDate } from '@/lib/core/utils/formatting'
45
import { TableOfContents } from '@/app/(landing)/blog/[slug]/table-of-contents'
56
import { getTagColor } from '@/app/(landing)/blog/tag-colors'
67

@@ -11,14 +12,6 @@ interface ArticleSidebarProps {
1112
related: BlogMeta[]
1213
}
1314

14-
function formatDate(iso: string) {
15-
return new Date(iso).toLocaleDateString('en-US', {
16-
month: 'short',
17-
day: 'numeric',
18-
year: 'numeric',
19-
})
20-
}
21-
2215
export function ArticleSidebar({ author, authors, headings, related }: ArticleSidebarProps) {
2316
const displayAuthors = authors.length > 0 ? authors : [author]
2417

@@ -91,7 +84,9 @@ export function ArticleSidebar({ author, authors, headings, related }: ArticleSi
9184
<h4 className='mb-1 text-[13px] font-[500] leading-tight text-[#ECECEC] transition-colors group-hover:text-[#FFCC02]'>
9285
{p.title}
9386
</h4>
94-
<div className='font-season text-[10px] text-[#666]'>{formatDate(p.date)}</div>
87+
<div className='font-season text-[10px] text-[#666]'>
88+
{formatDate(new Date(p.date))}
89+
</div>
9590
</Link>
9691
)
9792
})}

apps/sim/app/(landing)/blog/og/route.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import path from 'path'
33
import { ImageResponse } from 'next/og'
44
import type { NextRequest } from 'next/server'
55
import { getPostBySlug } from '@/lib/blog/registry'
6+
import { formatDate } from '@/lib/core/utils/formatting'
67
import { getPrimaryCategory } from '@/app/(landing)/blog/tag-colors'
78

89
function getTitleFontSize(title: string): number {
@@ -12,14 +13,6 @@ function getTitleFontSize(title: string): number {
1213
return 56
1314
}
1415

15-
function formatDate(iso: string): string {
16-
return new Date(iso).toLocaleDateString('en-US', {
17-
month: 'short',
18-
day: 'numeric',
19-
year: 'numeric',
20-
})
21-
}
22-
2316
export async function GET(request: NextRequest) {
2417
const slug = request.nextUrl.searchParams.get('slug')
2518

@@ -209,7 +202,7 @@ export async function GET(request: NextRequest) {
209202
}}
210203
/>
211204
<span style={{ fontSize: 14, color: '#666666', fontWeight: 500 }}>
212-
{formatDate(post.date)}
205+
{formatDate(new Date(post.date))}
213206
</span>
214207
</div>
215208
<span

apps/sim/app/(landing)/blog/post-grid.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { motion, useReducedMotion } from 'framer-motion'
44
import Image from 'next/image'
55
import Link from 'next/link'
66
import { Avatar, AvatarFallback, AvatarImage } from '@/components/emcn'
7+
import { formatDate } from '@/lib/core/utils/formatting'
78
import { getPrimaryCategory } from '@/app/(landing)/blog/tag-colors'
89

910
const EASE_OUT_QUINT = [0.23, 1, 0.32, 1] as const
@@ -57,14 +58,6 @@ interface PostGridProps {
5758
posts: Post[]
5859
}
5960

60-
function formatDate(iso: string) {
61-
return new Date(iso).toLocaleDateString('en-US', {
62-
month: 'short',
63-
day: 'numeric',
64-
year: 'numeric',
65-
})
66-
}
67-
6861
function PostCard({ post, priority = false }: { post: Post; priority?: boolean }) {
6962
const category = getPrimaryCategory(post.tags)
7063
const color = category.color
@@ -126,7 +119,7 @@ function PostCard({ post, priority = false }: { post: Post; priority?: boolean }
126119
</span>
127120
</div>
128121
<span className='h-1 w-1 bg-[#3d3d3d]' aria-hidden='true' />
129-
<time dateTime={post.date}>{formatDate(post.date)}</time>
122+
<time dateTime={post.date}>{formatDate(new Date(post.date))}</time>
130123
</div>
131124
</div>
132125
</article>
@@ -197,7 +190,7 @@ function FeaturedLeadCard({ post }: { post: Post }) {
197190
</span>
198191
</div>
199192
<span className='h-1 w-1 bg-[#3d3d3d]' aria-hidden='true' />
200-
<time dateTime={post.date}>{formatDate(post.date)}</time>
193+
<time dateTime={post.date}>{formatDate(new Date(post.date))}</time>
201194
</div>
202195
</div>
203196
</div>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { NextResponse } from 'next/server'
2+
import { env } from '@/lib/core/config/env'
3+
4+
const GITHUB_API_URL = 'https://api.github.com/repos/simstudioai/sim/releases'
5+
const PER_PAGE = 10
6+
7+
export async function GET(request: Request) {
8+
const { searchParams } = new URL(request.url)
9+
const page = Math.max(1, Number(searchParams.get('page') ?? '1'))
10+
11+
try {
12+
const token = env.GITHUB_TOKEN
13+
const response = await fetch(`${GITHUB_API_URL}?per_page=${PER_PAGE}&page=${page}`, {
14+
headers: {
15+
Accept: 'application/vnd.github+json',
16+
'X-GitHub-Api-Version': '2022-11-28',
17+
'User-Agent': 'Sim/1.0',
18+
...(token ? { Authorization: `Bearer ${token}` } : {}),
19+
},
20+
next: { revalidate: 3600 },
21+
})
22+
23+
if (!response.ok) {
24+
return NextResponse.json(
25+
{ error: `GitHub API returned ${response.status}` },
26+
{ status: response.status }
27+
)
28+
}
29+
30+
const data = await response.json()
31+
32+
if (!Array.isArray(data)) {
33+
return NextResponse.json({ error: 'Unexpected response from GitHub API' }, { status: 502 })
34+
}
35+
36+
const releases = data
37+
.filter((r: any) => !r.prerelease)
38+
.map((r: any) => ({
39+
tag: r.tag_name,
40+
title: r.name || r.tag_name,
41+
content: String(r.body || ''),
42+
date: r.published_at,
43+
url: r.html_url,
44+
}))
45+
46+
return NextResponse.json({ releases })
47+
} catch (error) {
48+
return NextResponse.json({ error: 'Failed to fetch releases' }, { status: 500 })
49+
}
50+
}
Lines changed: 36 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { BookOpen, Github, Rss } from 'lucide-react'
2-
import Link from 'next/link'
1+
import { env } from '@/lib/core/config/env'
2+
import { ChangelogHero } from '@/app/changelog/components/changelog-hero'
33
import ChangelogList from '@/app/changelog/components/timeline-list'
44

55
export interface ChangelogEntry {
@@ -21,76 +21,51 @@ export default async function ChangelogContent() {
2121
let entries: ChangelogEntry[] = []
2222

2323
try {
24+
const token = env.GITHUB_TOKEN
2425
const res = await fetch(
2526
'https://api.github.com/repos/simstudioai/sim/releases?per_page=10&page=1',
2627
{
27-
headers: { Accept: 'application/vnd.github+json' },
28+
headers: {
29+
Accept: 'application/vnd.github+json',
30+
'X-GitHub-Api-Version': '2022-11-28',
31+
'User-Agent': 'Sim/1.0',
32+
...(token ? { Authorization: `Bearer ${token}` } : {}),
33+
},
2834
next: { revalidate: 3600 },
2935
}
3036
)
31-
const releases: any[] = await res.json()
32-
entries = (releases || [])
33-
.filter((r) => !r.prerelease)
34-
.map((r) => ({
35-
tag: r.tag_name,
36-
title: r.name || r.tag_name,
37-
content: String(r.body || ''),
38-
date: r.published_at,
39-
url: r.html_url,
40-
contributors: extractMentions(String(r.body || '')),
41-
}))
37+
38+
if (!res.ok) {
39+
entries = []
40+
} else {
41+
const releases = await res.json()
42+
if (Array.isArray(releases)) {
43+
entries = releases
44+
.filter((r) => !r.prerelease)
45+
.map((r) => ({
46+
tag: r.tag_name,
47+
title: r.name || r.tag_name,
48+
content: String(r.body || ''),
49+
date: r.published_at,
50+
url: r.html_url,
51+
contributors: extractMentions(String(r.body || '')),
52+
}))
53+
}
54+
}
4255
} catch (err) {
4356
entries = []
4457
}
4558

4659
return (
47-
<div className='min-h-screen'>
48-
<div className='relative grid md:grid-cols-2'>
49-
{/* Left intro panel */}
50-
<div className='relative top-0 overflow-hidden border-[#2A2A2A] border-b px-6 py-16 sm:px-10 md:sticky md:h-dvh md:border-r md:border-b-0 md:px-12 md:py-24'>
51-
<div className='relative mx-auto h-full max-w-xl md:flex md:flex-col md:justify-center'>
52-
<h1 className='mt-6 font-[500] text-4xl tracking-tight sm:text-5xl'>Changelog</h1>
53-
<p className='mt-4 text-[#999] text-sm'>
54-
Stay up-to-date with the latest features, improvements, and bug fixes in Sim. All
55-
changes are documented here with detailed release notes.
56-
</p>
57-
<hr className='mt-6 border-[#2A2A2A]' />
58-
59-
<div className='mt-6 flex flex-wrap items-center gap-3 text-sm'>
60-
<Link
61-
href='https://github.com/simstudioai/sim/releases'
62-
target='_blank'
63-
rel='noopener noreferrer'
64-
className='inline-flex items-center gap-2 rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[9px] py-[5px] text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
65-
>
66-
<Github className='h-4 w-4' />
67-
View on GitHub
68-
</Link>
69-
<Link
70-
href='https://docs.sim.ai'
71-
className='inline-flex items-center gap-2 rounded-[5px] border border-[#3d3d3d] px-[9px] py-[5px] text-[#ECECEC] text-[13.5px] transition-colors hover:bg-[#2A2A2A]'
72-
>
73-
<BookOpen className='h-4 w-4' />
74-
Documentation
75-
</Link>
76-
<Link
77-
href='/changelog.xml'
78-
className='inline-flex items-center gap-2 rounded-[5px] border border-[#3d3d3d] px-[9px] py-[5px] text-[#ECECEC] text-[13.5px] transition-colors hover:bg-[#2A2A2A]'
79-
>
80-
<Rss className='h-4 w-4' />
81-
RSS Feed
82-
</Link>
83-
</div>
84-
</div>
85-
</div>
86-
87-
{/* Right timeline */}
88-
<div className='relative px-4 py-10 sm:px-6 md:px-8 md:py-12'>
89-
<div className='relative max-w-2xl pl-8'>
90-
<ChangelogList initialEntries={entries} />
91-
</div>
92-
</div>
93-
</div>
60+
<div className='flex flex-col'>
61+
<ChangelogHero />
62+
<main className='mx-auto w-full max-w-5xl px-6 py-12'>
63+
<h2 className='mb-8 flex items-center gap-2 font-season text-[11px] uppercase tracking-widest text-[#666]'>
64+
<span className='inline-block h-2 w-2 bg-[#FFCC02]' aria-hidden='true' />
65+
All Releases
66+
</h2>
67+
<ChangelogList initialEntries={entries} />
68+
</main>
9469
</div>
9570
)
9671
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
'use client'
2+
3+
import { motion, useReducedMotion } from 'framer-motion'
4+
import { BookOpen, Github, Rss } from 'lucide-react'
5+
import Link from 'next/link'
6+
7+
const EASE_OUT_QUINT = [0.23, 1, 0.32, 1] as const
8+
const STAGGER = 0.2
9+
const DURATION = 0.4
10+
const Y_OFFSET = 12
11+
12+
const containerVariants = {
13+
hidden: {},
14+
visible: {
15+
transition: { staggerChildren: STAGGER },
16+
},
17+
}
18+
19+
const itemVariants = {
20+
hidden: { opacity: 0, y: Y_OFFSET },
21+
visible: {
22+
opacity: 1,
23+
y: 0,
24+
transition: { duration: DURATION, ease: EASE_OUT_QUINT },
25+
},
26+
}
27+
28+
export function ChangelogHero() {
29+
const shouldReduceMotion = useReducedMotion()
30+
31+
return (
32+
<section className='relative overflow-hidden border-b border-[#2A2A2A] pb-10 pt-14'>
33+
<div
34+
className='pointer-events-none absolute inset-0 opacity-50'
35+
aria-hidden='true'
36+
style={{
37+
backgroundImage:
38+
'radial-gradient(circle at 2px 2px, rgba(255, 255, 255, 0.04) 1px, transparent 0)',
39+
backgroundSize: '32px 32px',
40+
}}
41+
/>
42+
<motion.div
43+
className='relative z-10 mx-auto max-w-5xl px-6'
44+
variants={containerVariants}
45+
initial={shouldReduceMotion ? 'visible' : 'hidden'}
46+
animate='visible'
47+
>
48+
<div className='flex flex-col items-start'>
49+
<motion.div
50+
variants={itemVariants}
51+
className='mb-6 inline-flex items-center gap-2 font-season text-[11px] uppercase tracking-widest text-[#999]'
52+
>
53+
<span className='inline-block h-2 w-2 flex-shrink-0 bg-[#FFCC02]' aria-hidden='true' />
54+
Sim / Changelog
55+
</motion.div>
56+
<motion.h1
57+
variants={itemVariants}
58+
className='mb-3 max-w-3xl text-balance font-[430] text-[40px] leading-[1.1] tracking-[-0.02em] text-[#ECECEC] sm:text-[56px] md:text-[64px]'
59+
>
60+
What&apos;s new in <span className='text-[#666]'>Sim.</span>
61+
</motion.h1>
62+
<motion.p
63+
variants={itemVariants}
64+
className='max-w-2xl text-[18px] leading-relaxed tracking-[0.02em] text-[#999]'
65+
>
66+
Stay up-to-date with the latest features, improvements, and bug fixes.
67+
</motion.p>
68+
<motion.div variants={itemVariants} className='mt-8 flex flex-wrap items-center gap-3'>
69+
<Link
70+
href='https://github.com/simstudioai/sim/releases'
71+
target='_blank'
72+
rel='noopener noreferrer'
73+
className='inline-flex items-center gap-2 rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[9px] py-[5px] text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
74+
>
75+
<Github className='h-4 w-4' />
76+
View on GitHub
77+
</Link>
78+
<Link
79+
href='https://docs.sim.ai'
80+
className='inline-flex items-center gap-2 rounded-[5px] border border-[#3d3d3d] px-[9px] py-[5px] text-[13.5px] text-[#ECECEC] transition-colors hover:bg-[#2A2A2A]'
81+
>
82+
<BookOpen className='h-4 w-4' />
83+
Documentation
84+
</Link>
85+
<Link
86+
href='/changelog.xml'
87+
className='inline-flex items-center gap-2 rounded-[5px] border border-[#3d3d3d] px-[9px] py-[5px] text-[13.5px] text-[#ECECEC] transition-colors hover:bg-[#2A2A2A]'
88+
>
89+
<Rss className='h-4 w-4' />
90+
RSS Feed
91+
</Link>
92+
</motion.div>
93+
</div>
94+
</motion.div>
95+
</section>
96+
)
97+
}

0 commit comments

Comments
 (0)