Skip to content

Commit 90c3c43

Browse files
authored
fix(blog): add back unoptimized tag, fix styling (#2461)
1 parent 83d813a commit 90c3c43

File tree

8 files changed

+159
-70
lines changed

8 files changed

+159
-70
lines changed

apps/sim/app/(landing)/studio/[slug]/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string
6464
sizes='(max-width: 768px) 100vw, 450px'
6565
priority
6666
itemProp='image'
67+
unoptimized
6768
/>
6869
</div>
6970
</div>
@@ -144,6 +145,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string
144145
className='h-[160px] w-full object-cover'
145146
sizes='(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw'
146147
loading='lazy'
148+
unoptimized
147149
/>
148150
<div className='p-3'>
149151
<div className='mb-1 text-gray-600 text-xs'>

apps/sim/app/(landing)/studio/authors/[id]/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export default async function AuthorPage({ params }: { params: Promise<{ id: str
3838
width={40}
3939
height={40}
4040
className='rounded-full'
41+
unoptimized
4142
/>
4243
) : null}
4344
<h1 className='font-medium text-[32px] leading-tight'>{author.name}</h1>
@@ -52,6 +53,7 @@ export default async function AuthorPage({ params }: { params: Promise<{ id: str
5253
width={600}
5354
height={315}
5455
className='h-[160px] w-full object-cover transition-transform group-hover:scale-[1.02]'
56+
unoptimized
5557
/>
5658
<div className='p-3'>
5759
<div className='mb-1 text-gray-600 text-xs'>

apps/sim/app/(landing)/studio/page.tsx

Lines changed: 2 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import Image from 'next/image'
21
import Link from 'next/link'
3-
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
42
import { getAllPostMeta } from '@/lib/blog/registry'
53
import { soehne } from '@/app/_styles/fonts/soehne/soehne'
4+
import { PostGrid } from '@/app/(landing)/studio/post-grid'
65

76
export const revalidate = 3600
87

@@ -18,7 +17,6 @@ export default async function StudioIndex({
1817
const all = await getAllPostMeta()
1918
const filtered = tag ? all.filter((p) => p.tags.includes(tag)) : all
2019

21-
// Sort to ensure featured post is first on page 1
2220
const sorted =
2321
pageNum === 1
2422
? filtered.sort((a, b) => {
@@ -63,68 +61,7 @@ export default async function StudioIndex({
6361
</div> */}
6462

6563
{/* Grid layout for consistent rows */}
66-
<div className='grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6 lg:grid-cols-3'>
67-
{posts.map((p, i) => {
68-
return (
69-
<Link key={p.slug} href={`/studio/${p.slug}`} className='group flex flex-col'>
70-
<div className='flex h-full flex-col overflow-hidden rounded-xl border border-gray-200 transition-colors duration-300 hover:border-gray-300'>
71-
<Image
72-
src={p.ogImage}
73-
alt={p.title}
74-
width={800}
75-
height={450}
76-
className='h-48 w-full object-cover'
77-
sizes='(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw'
78-
loading='lazy'
79-
/>
80-
<div className='flex flex-1 flex-col p-4'>
81-
<div className='mb-2 text-gray-600 text-xs'>
82-
{new Date(p.date).toLocaleDateString('en-US', {
83-
month: 'short',
84-
day: 'numeric',
85-
year: 'numeric',
86-
})}
87-
</div>
88-
<h3 className='shine-text mb-1 font-medium text-lg leading-tight'>{p.title}</h3>
89-
<p className='mb-3 line-clamp-3 flex-1 text-gray-700 text-sm'>{p.description}</p>
90-
<div className='flex items-center gap-2'>
91-
<div className='-space-x-1.5 flex'>
92-
{(p.authors && p.authors.length > 0 ? p.authors : [p.author])
93-
.slice(0, 3)
94-
.map((author, idx) => (
95-
<Avatar key={idx} className='size-4 border border-white'>
96-
<AvatarImage src={author?.avatarUrl} alt={author?.name} />
97-
<AvatarFallback className='border border-white bg-gray-100 text-[10px] text-gray-600'>
98-
{author?.name.slice(0, 2)}
99-
</AvatarFallback>
100-
</Avatar>
101-
))}
102-
</div>
103-
<span className='text-gray-600 text-xs'>
104-
{(p.authors && p.authors.length > 0 ? p.authors : [p.author])
105-
.slice(0, 2)
106-
.map((a) => a?.name)
107-
.join(', ')}
108-
{(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length > 2 && (
109-
<>
110-
{' '}
111-
and{' '}
112-
{(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length - 2}{' '}
113-
other
114-
{(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length - 2 >
115-
1
116-
? 's'
117-
: ''}
118-
</>
119-
)}
120-
</span>
121-
</div>
122-
</div>
123-
</div>
124-
</Link>
125-
)
126-
})}
127-
</div>
64+
<PostGrid posts={posts} />
12865

12966
{totalPages > 1 && (
13067
<div className='mt-10 flex items-center justify-center gap-3'>
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
'use client'
2+
3+
import Image from 'next/image'
4+
import Link from 'next/link'
5+
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
6+
7+
interface Author {
8+
id: string
9+
name: string
10+
avatarUrl?: string
11+
url?: string
12+
}
13+
14+
interface Post {
15+
slug: string
16+
title: string
17+
description: string
18+
date: string
19+
ogImage: string
20+
author: Author
21+
authors?: Author[]
22+
featured?: boolean
23+
}
24+
25+
export function PostGrid({ posts }: { posts: Post[] }) {
26+
return (
27+
<div className='grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6 lg:grid-cols-3'>
28+
{posts.map((p, index) => (
29+
<Link key={p.slug} href={`/studio/${p.slug}`} className='group flex flex-col'>
30+
<div className='flex h-full flex-col overflow-hidden rounded-xl border border-gray-200 transition-colors duration-300 hover:border-gray-300'>
31+
{/* Image container with fixed aspect ratio to prevent layout shift */}
32+
<div className='relative aspect-video w-full overflow-hidden'>
33+
<Image
34+
src={p.ogImage}
35+
alt={p.title}
36+
sizes='(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw'
37+
unoptimized
38+
priority={index < 6}
39+
loading={index < 6 ? undefined : 'lazy'}
40+
fill
41+
style={{ objectFit: 'cover' }}
42+
/>
43+
</div>
44+
<div className='flex flex-1 flex-col p-4'>
45+
<div className='mb-2 text-gray-600 text-xs'>
46+
{new Date(p.date).toLocaleDateString('en-US', {
47+
month: 'short',
48+
day: 'numeric',
49+
year: 'numeric',
50+
})}
51+
</div>
52+
<h3 className='shine-text mb-1 font-medium text-lg leading-tight'>{p.title}</h3>
53+
<p className='mb-3 line-clamp-3 flex-1 text-gray-700 text-sm'>{p.description}</p>
54+
<div className='flex items-center gap-2'>
55+
<div className='-space-x-1.5 flex'>
56+
{(p.authors && p.authors.length > 0 ? p.authors : [p.author])
57+
.slice(0, 3)
58+
.map((author, idx) => (
59+
<Avatar key={idx} className='size-4 border border-white'>
60+
<AvatarImage src={author?.avatarUrl} alt={author?.name} />
61+
<AvatarFallback className='border border-white bg-gray-100 text-[10px] text-gray-600'>
62+
{author?.name.slice(0, 2)}
63+
</AvatarFallback>
64+
</Avatar>
65+
))}
66+
</div>
67+
<span className='text-gray-600 text-xs'>
68+
{(p.authors && p.authors.length > 0 ? p.authors : [p.author])
69+
.slice(0, 2)
70+
.map((a) => a?.name)
71+
.join(', ')}
72+
{(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length > 2 && (
73+
<>
74+
{' '}
75+
and {(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length - 2}{' '}
76+
other
77+
{(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length - 2 > 1
78+
? 's'
79+
: ''}
80+
</>
81+
)}
82+
</span>
83+
</div>
84+
</div>
85+
</div>
86+
</Link>
87+
))}
88+
</div>
89+
)
90+
}

apps/sim/app/_styles/globals.css

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,3 +759,24 @@ input[type="search"]::-ms-clear {
759759
--surface-elevated: #202020;
760760
}
761761
}
762+
763+
/**
764+
* Remove backticks from inline code in prose (Tailwind Typography default)
765+
*/
766+
.prose code::before,
767+
.prose code::after {
768+
content: none !important;
769+
}
770+
771+
/**
772+
* Remove underlines from heading anchor links in prose
773+
*/
774+
.prose h1 a,
775+
.prose h2 a,
776+
.prose h3 a,
777+
.prose h4 a,
778+
.prose h5 a,
779+
.prose h6 a {
780+
text-decoration: none !important;
781+
color: inherit !important;
782+
}

apps/sim/lib/blog/code.tsx

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use client'
22

3+
import { useState } from 'react'
4+
import { Check, Copy } from 'lucide-react'
35
import { Code } from '@/components/emcn'
46

57
interface CodeBlockProps {
@@ -8,5 +10,36 @@ interface CodeBlockProps {
810
}
911

1012
export function CodeBlock({ code, language }: CodeBlockProps) {
11-
return <Code.Viewer code={code} showGutter={true} language={language} />
13+
const [copied, setCopied] = useState(false)
14+
15+
const handleCopy = () => {
16+
navigator.clipboard.writeText(code)
17+
setCopied(true)
18+
setTimeout(() => setCopied(false), 2000)
19+
}
20+
21+
return (
22+
<div className='dark w-full overflow-hidden rounded-md border border-[#2a2a2a] bg-[#1F1F1F] text-sm'>
23+
<div className='flex items-center justify-between border-[#2a2a2a] border-b px-4 py-1.5'>
24+
<span className='text-[#A3A3A3] text-xs'>{language}</span>
25+
<button
26+
onClick={handleCopy}
27+
className='text-[#A3A3A3] transition-colors hover:text-gray-300'
28+
title='Copy code'
29+
>
30+
{copied ? (
31+
<Check className='h-3 w-3' strokeWidth={2} />
32+
) : (
33+
<Copy className='h-3 w-3' strokeWidth={2} />
34+
)}
35+
</button>
36+
</div>
37+
<Code.Viewer
38+
code={code}
39+
showGutter
40+
language={language}
41+
className='[&_pre]:!pb-0 m-0 rounded-none border-0 bg-transparent'
42+
/>
43+
</div>
44+
)
1245
}

apps/sim/lib/blog/mdx.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const mdxComponents: MDXRemoteProps['components'] = {
1313
className={clsx('h-auto w-full rounded-lg', props.className)}
1414
sizes='(max-width: 768px) 100vw, 800px'
1515
loading='lazy'
16+
unoptimized
1617
/>
1718
),
1819
h2: (props: any) => (
@@ -66,7 +67,7 @@ export const mdxComponents: MDXRemoteProps['components'] = {
6667
a: (props: any) => {
6768
const isAnchorLink = props.className?.includes('anchor')
6869
if (isAnchorLink) {
69-
return <a {...props} />
70+
return <a {...props} className={clsx('text-inherit no-underline', props.className)} />
7071
}
7172
return (
7273
<a
@@ -112,7 +113,7 @@ export const mdxComponents: MDXRemoteProps['components'] = {
112113
const mappedLanguage = languageMap[language.toLowerCase()] || 'javascript'
113114

114115
return (
115-
<div className='my-6'>
116+
<div className='not-prose my-6'>
116117
<CodeBlock
117118
code={typeof codeContent === 'string' ? codeContent.trim() : String(codeContent)}
118119
language={mappedLanguage}
@@ -128,9 +129,10 @@ export const mdxComponents: MDXRemoteProps['components'] = {
128129
<code
129130
{...props}
130131
className={clsx(
131-
'rounded bg-gray-100 px-1.5 py-0.5 font-mono text-[0.9em] text-red-600',
132+
'rounded bg-gray-100 px-1.5 py-0.5 font-mono font-normal text-[0.9em] text-red-600',
132133
props.className
133134
)}
135+
style={{ fontWeight: 400 }}
134136
/>
135137
)
136138
}

apps/sim/lib/blog/registry.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ function slugify(text: string): string {
3838
}
3939

4040
async function scanFrontmatters(): Promise<BlogMeta[]> {
41-
if (cachedMeta) return cachedMeta
41+
if (cachedMeta) {
42+
return cachedMeta
43+
}
4244
await ensureContentDirs()
4345
const entries = await fs.readdir(BLOG_DIR).catch(() => [])
4446
const authorsMap = await loadAuthors()

0 commit comments

Comments
 (0)