Skip to content

feat: updated design for blog and blog details page w dynamic og generation#3625

Open
adithyaakrishna wants to merge 31 commits intosimstudioai:stagingfrom
adithyaakrishna:feat/studio-pages
Open

feat: updated design for blog and blog details page w dynamic og generation#3625
adithyaakrishna wants to merge 31 commits intosimstudioai:stagingfrom
adithyaakrishna:feat/studio-pages

Conversation

@adithyaakrishna
Copy link

@adithyaakrishna adithyaakrishna commented Mar 17, 2026

Summary

Complete redesign of the blog (/studio) pages with 2 column sidebar layout, redesigned post cards, a 3 column blog detail page and added feature for dynamic og image generation

Fixes #(issue)

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: Design overhaul / UX improvement

Testing

  • Check the Layout of /studio
  • Check the Layout of /studio/
  • Check the OG img generation by /studio/og?slug=<slug>

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

OG Image:

image

How it looks:

screen-capture.13.webm

@cursor
Copy link

cursor bot commented Mar 17, 2026

PR Summary

Medium Risk
Moderate risk due to a large UI/UX refactor across blog + changelog pages and new server routes that call GitHub and generate OG images; main risks are layout regressions and API/edge runtime issues.

Overview
Redesigns the blog experience into a “Studio” layout: a new 2-column page with a sticky sidebar for category filtering + search (tag/q query params), updated post cards (featured lead + animated grids), and refreshed layout styling (fonts, scrollbar theming, spacing).

Overhauls the blog post detail page with a new header (category + tags + share buttons), author cards, related articles, and an optional right-rail table-of-contents driven by generated h2/h3 headings; adds new prose/syntax CSS and TOC hover/scroll interactions.

Adds dynamic sharing/preview infrastructure: a new /blog/og Open Graph image route backed by post meta lookup, and SEO metadata now points OG/Twitter images to this dynamic endpoint. Also adds richer MDX rendering with Prism-based code blocks (copy button, more languages) and Mermaid diagram support.

Updates changelog fetching and UI: introduces /api/changelog/releases as a backend proxy (optional GitHub token), switches “load more” to use it, and refreshes changelog page/entries to a card-based, animated design with improved markdown cleanup.

Small follow-ups include moving hexToRgba into shared formatting utilities, adjusting a landing link to /blog/multiplayer, and adding a workspace knowledge page loading skeleton.

Written by Cursor Bugbot for commit 101a955. This will update automatically on new commits. Configure here.

@vercel
Copy link

vercel bot commented Mar 17, 2026

@adithyaakrishna is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 17, 2026

Greptile Summary

This PR is a comprehensive visual redesign of the Sim blog (/studio) — introducing a persistent two-column sidebar layout, redesigned post cards with category badges, a three-column article detail page (TOC + article + author sidebar), and dynamic per-post OG image generation via a new /studio/og route.

Key concerns identified:

  • Sidebar active state is never wired up (layout.tsx line 49): StudioSidebar is rendered without its activeTag prop, and Next.js layouts do not receive searchParams, so category highlights in the sidebar will never reflect the current filter.
  • Unhandled font-load errors in OG route (og/route.tsx lines 44–47): The fs.readFile calls for font assets are not inside a try-catch; a missing font file will produce an unhandled 500 error for all OG image requests.
  • Non-functional search input (sidebar.tsx lines 32–43): The search field renders and focuses but has no onChange, state, or search logic — it is purely decorative and may confuse users.
  • Tag filter URL semantics changed (page.tsx line 22): ?tag= now expects category IDs (e.g. announcements) rather than raw tag strings (e.g. Announcement). Existing bookmarks or sitemap entries using old tag URLs will silently show all posts instead of the expected results.
  • Untracked setTimeout handles (animated-blocks.tsx lines 53–79): Timers created inside startCycle are not tracked for cleanup, so they continue firing after unmount; a minor performance concern.

Confidence Score: 3/5

  • Merging is safe for the visual redesign, but two functional regressions (sidebar active state, OG font error handling) should be fixed before this ships to production.
  • The overall design work is solid and the new components are well-structured. However, the sidebar active-state feature is completely non-functional due to how Next.js layouts work (a logic issue, not a style one), and the OG image route will crash with 500 errors if the font files are missing. The tag URL schema change also silently breaks any existing filtered links without a migration path.
  • Pay close attention to apps/sim/app/(landing)/studio/layout.tsx (sidebar wiring), apps/sim/app/(landing)/studio/og/route.tsx (font error handling), and apps/sim/app/(landing)/studio/sidebar.tsx (search input placeholder UX).

Important Files Changed

Filename Overview
apps/sim/app/(landing)/studio/layout.tsx Adds StudioSidebar to a two-column layout and a metadata template; activeTag is never passed to the sidebar so category active-state highlighting is always broken.
apps/sim/app/(landing)/studio/og/route.tsx New dynamic OG image route using next/og and custom fonts; font file loading is not wrapped in error handling, so any filesystem issue returns an unhandled 500 to social crawlers.
apps/sim/app/(landing)/studio/[slug]/animated-blocks.tsx New decorative animation component; uses mounted guard correctly but creates untracked setTimeout handles inside startCycle that cannot be cancelled on unmount.
apps/sim/app/(landing)/studio/sidebar.tsx New left-rail sidebar with category counts and a search input; the search input is a non-functional placeholder and the activeTag prop it relies on is never supplied by the layout.
apps/sim/app/(landing)/studio/page.tsx Blog index page redesigned with hero, featured grid and category filtering; tag filter query param semantics changed from raw tag strings to category IDs, which breaks existing tag-based URLs.
apps/sim/app/(landing)/studio/post-grid.tsx Post grid refactored with new PostCard, FeaturedLeadCard, and FeaturedGrid components; category color badges and reading-time display added cleanly.
apps/sim/app/(landing)/studio/tag-colors.ts New config module mapping raw blog tags to five curated categories with colors; clean and well-organized with correct fallback to the "insights" category.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User visits /studio] --> B[StudioLayout]
    B --> C[StudioSidebar\nserver component]
    B --> D[main - children]
    
    C --> C1[getAllPostMeta]
    C1 --> C2[Count posts per category\nvia getPrimaryCategory]
    C2 --> C3[Render category links\nactiveTag always undefined ⚠️]

    D --> E{Route}
    E -->|/studio| F[StudioIndex page]
    E -->|/studio/slug| G[Article page]
    E -->|/studio/og?slug=| H[OG Image route]

    F --> F1[StudioHero]
    F --> F2[FeaturedGrid\nfeatured posts]
    F --> F3[PostGrid\nregular posts]
    F2 --> F4[FeaturedLeadCard]
    F2 --> F5[PostCard]

    G --> G1[AnimatedColorBlocks\nclient animation]
    G --> G2[Article MDX content\n+ prose-studio.css]
    G --> G3[ArticleSidebar\nauthor / TOC / tags / related]
    G --> G4[ShareButtons\nclient component]

    H --> H1{slug param?}
    H1 -->|No| H2[400 Bad Request]
    H1 -->|Yes| H3[getPostBySlug]
    H3 -->|Not found| H4[404]
    H3 -->|Found| H5[Read font files\nfs.readFile ⚠️ unguarded]
    H5 --> H6[ImageResponse\n1200x630 PNG]

    G3 --> G3a[TableOfContents\nclient - IntersectionObserver]
Loading

Last reviewed commit: 34d3d78

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

export async function getPostMetaBySlug(slug: string): Promise<BlogMeta | null> {
const meta = await scanFrontmatters()
return meta.find((m) => m.slug === slug) ?? null
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OG route exposes draft posts to public

Medium Severity

The new getPostMetaBySlug function searches scanFrontmatters() which includes draft posts, unlike getAllPostMeta() which filters them out. The OG image route at /blog/og?slug=<slug> uses getPostMetaBySlug, so anyone can generate OG images for draft/unpublished posts by guessing the slug, leaking titles, descriptions, and metadata of unpublished content.

Additional Locations (1)
Fix in Cursor Fix in Web

</div>
)}

<ArticleSidebar headings={post.headings ?? []} />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sidebar renders after main content instead of beside

Medium Severity

The ArticleSidebar is placed after the main content div in the flex container, but the parent uses xl:flex-row. The sidebar has xl:sticky xl:top-[76px] but on XL screens it will appear to the right of the article. Combined with the main content having mx-auto centering and max-w-5xl, the sidebar may be pushed off-screen or misaligned on certain viewport sizes because the main content's flex-grow and mx-auto conflict with the sidebar's expected positioning in a two-column layout.

Additional Locations (1)
Fix in Cursor Fix in Web

export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const post = await getPostBySlug(slug)
const [post, related] = await Promise.all([getPostBySlug(slug), getRelatedPosts(slug, 3)])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed revalidate may cause stale blog pages

Low Severity

The export const revalidate = 86400 was removed from the blog detail page and revalidate = 3600 from the blog index and author pages. Without explicit revalidation, these pages become fully static after build and won't pick up new or updated blog posts until the next deployment, changing the previous ISR behavior.

Additional Locations (1)
Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant