Skip to content

Commit 038d86a

Browse files
committed
refactor(changelog): simplify presentation and validate model links
1 parent 6197069 commit 038d86a

15 files changed

Lines changed: 95 additions & 122 deletions

File tree

‎apps/sim/app/(landing)/changelog/archive/page.tsx‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ export default async function ChangelogArchivePage() {
2828
/>
2929
<ChangelogHeader
3030
title='All updates'
31-
lead='New capabilities, improvements, and fixes in Sim, with the details in one place.'
3231
actions={<BackLink href='/changelog' label='Back to changelog' />}
3332
/>
3433
<ChangelogList entries={entries} />

‎apps/sim/app/(landing)/changelog/changelog.tsx‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,9 @@ export default async function Changelog() {
1616
return (
1717
<ChangelogLayout>
1818
<JsonLd data={buildCollectionPageJsonLd(CHANGELOG_SECTION, visible)} />
19-
<ChangelogHeader
20-
title='Changelog'
21-
lead={CHANGELOG_SECTION.description}
22-
actions={<ChangelogActions />}
23-
/>
19+
<ChangelogHeader title='Changelog' actions={<ChangelogActions />} />
2420
<ChangelogList entries={visible} />
25-
<div className='flex flex-wrap gap-6 border-[var(--border)] border-t pt-6 text-[var(--text-secondary)] text-sm'>
21+
<div className='flex flex-wrap gap-6 text-[var(--text-secondary)] text-sm'>
2622
{firstOlderEntry ? (
2723
<Link
2824
href={`/changelog/archive#${firstOlderEntry.slug}`}

‎apps/sim/app/(landing)/changelog/components/changelog-article/changelog-article.tsx‎

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,31 +41,15 @@ export function ChangelogArticle({
4141
>
4242
{entry.title}
4343
</h1>
44-
<p
45-
itemProp='description'
46-
className={cn('text-pretty text-[var(--text-secondary)]', LANDING_TYPE.lead)}
47-
>
48-
{entry.description}
49-
</p>
50-
<p className='text-[var(--text-secondary)] text-sm'>
51-
By {entry.authors.map((author) => author.name).join(', ')}
52-
</p>
5344
</header>
54-
<section
55-
id='update-details'
56-
aria-labelledby='update-details-heading'
57-
className='mt-10 border-[var(--border)] border-t pt-4'
58-
>
45+
<section id='update-details' aria-labelledby='update-details-heading' className='mt-8'>
5946
<h2 id='update-details-heading' className='sr-only'>
6047
In this update
6148
</h2>
6249
<entry.Content />
6350
</section>
6451
<footer
65-
className={cn(
66-
'mt-10 flex flex-wrap gap-6 border-[var(--border)] border-t pt-6 text-[var(--text-secondary)]',
67-
HOME_TYPE.meta
68-
)}
52+
className={cn('mt-10 flex flex-wrap gap-6 text-[var(--text-secondary)]', HOME_TYPE.meta)}
6953
>
7054
{entry.release?.url ? (
7155
<a

‎apps/sim/app/(landing)/changelog/components/changelog-header/changelog-header.tsx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@ import { LANDING_TYPE } from '@/app/(landing)/components/landing-layout'
44

55
interface ChangelogHeaderProps {
66
title: string
7-
lead: string
7+
lead?: string
88
actions?: ReactNode
99
}
1010

1111
export function ChangelogHeader({ title, lead, actions }: ChangelogHeaderProps) {
1212
return (
1313
<header className='flex flex-col items-start gap-6'>
1414
<h1 className={cn('text-[var(--text-primary)]', LANDING_TYPE.h1)}>{title}</h1>
15-
<p className={cn('max-w-[640px] text-[var(--text-secondary)]', LANDING_TYPE.lead)}>{lead}</p>
15+
{lead ? (
16+
<p className={cn('max-w-[640px] text-[var(--text-secondary)]', LANDING_TYPE.lead)}>
17+
{lead}
18+
</p>
19+
) : null}
1620
{actions}
1721
</header>
1822
)
Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { cn } from '@sim/emcn'
1+
import { cn, Lightbox } from '@sim/emcn'
22
import Image from 'next/image'
33
import { LANDING_STAGE_RADIUS } from '@/app/(landing)/components/landing-layout'
44

@@ -14,25 +14,28 @@ interface ChangelogImageProps {
1414
export function ChangelogImage({ src, alt, width, height, caption }: ChangelogImageProps) {
1515
return (
1616
<figure className='my-6'>
17-
<Image
18-
src={src}
19-
alt={alt}
20-
width={width}
21-
height={height}
22-
sizes='(max-width: 767px) calc(100vw - 40px), 872px'
23-
className={cn('h-auto w-full border border-[var(--border)]', LANDING_STAGE_RADIUS)}
24-
/>
25-
<figcaption className='mt-3 text-[var(--text-secondary)] text-small'>
26-
{caption ? <p>{caption}</p> : null}
27-
<a
28-
href={src}
29-
target='_blank'
30-
rel='noopener noreferrer'
31-
className='mt-2 inline-block underline underline-offset-4'
17+
<Lightbox src={src} alt={alt}>
18+
<button
19+
type='button'
20+
aria-label={`Open ${alt} in media viewer`}
21+
className={cn(
22+
'block w-full cursor-zoom-in focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--text-primary)] focus-visible:outline-offset-4',
23+
LANDING_STAGE_RADIUS
24+
)}
3225
>
33-
View full-size image
34-
</a>
35-
</figcaption>
26+
<Image
27+
src={src}
28+
alt={alt}
29+
width={width}
30+
height={height}
31+
sizes='(max-width: 767px) calc(100vw - 40px), 872px'
32+
className={cn('h-auto w-full', LANDING_STAGE_RADIUS)}
33+
/>
34+
</button>
35+
</Lightbox>
36+
{caption ? (
37+
<figcaption className='mt-3 text-[var(--text-secondary)] text-small'>{caption}</figcaption>
38+
) : null}
3639
</figure>
3740
)
3841
}

‎apps/sim/app/(landing)/changelog/components/changelog-list/changelog-list.tsx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@ interface ChangelogListProps {
66
basePath?: string
77
}
88

9-
/** Chronological rows share the landing frame, with copy beside media and a stacked mobile layout. */
9+
/** A chronological index keeps demos and detailed copy on each update's page. */
1010
export function ChangelogList({ entries, basePath }: ChangelogListProps) {
1111
return (
1212
<section id='updates' aria-labelledby='updates-heading'>
1313
<h2 id='updates-heading' className='sr-only'>
1414
Product updates
1515
</h2>
1616
{entries.length > 0 ? (
17-
<ol className='divide-y divide-[var(--border)]'>
18-
{entries.map((entry, index) => (
19-
<li key={entry.slug} className='py-12 first:pt-0 last:pb-0 max-sm:py-8'>
20-
<ChangelogEntry entry={entry} priority={index === 0} basePath={basePath} />
17+
<ol className='space-y-10 max-sm:space-y-8'>
18+
{entries.map((entry) => (
19+
<li key={entry.slug}>
20+
<ChangelogEntry entry={entry} basePath={basePath} />
2121
</li>
2222
))}
2323
</ol>

‎apps/sim/app/(landing)/changelog/components/changelog-list/components/changelog-entry/changelog-entry.tsx‎

Lines changed: 14 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,41 @@
11
import { cn } from '@sim/emcn'
22
import { ArrowRight } from '@sim/emcn/icons'
3-
import Image from 'next/image'
43
import Link from 'next/link'
54
import { CHANGELOG_SECTION } from '@/lib/changelog/constants'
65
import type { ContentMeta } from '@/lib/content/schema'
76
import { formatPostDate } from '@/app/(landing)/components/content-utils'
8-
import { HOME_TYPE, LANDING_STAGE_RADIUS } from '@/app/(landing)/components/landing-layout'
7+
import { HOME_TYPE } from '@/app/(landing)/components/landing-layout'
98

109
interface ChangelogEntryProps {
1110
entry: ContentMeta
12-
priority: boolean
1311
basePath?: string
1412
}
1513

16-
const IMAGE_SIZES =
17-
'(min-width: 1728px) 663px, (min-width: 1280px) calc((100vw - 80px) * 5 / 12 - 24px), (min-width: 1024px) calc((100vw - 72px) * 5 / 12 - 24px), (min-width: 768px) calc(100vw - 64px), calc(100vw - 56px)'
18-
1914
export function ChangelogEntry({
2015
entry,
21-
priority,
2216
basePath = CHANGELOG_SECTION.basePath,
2317
}: ChangelogEntryProps) {
2418
return (
2519
<article id={entry.slug} aria-labelledby={`${entry.slug}-heading`} className='scroll-mt-28'>
2620
<Link
2721
href={`${basePath}/${entry.slug}`}
2822
aria-labelledby={`${entry.slug}-heading`}
29-
className={cn(
30-
'group grid min-w-0 grid-cols-2 items-center gap-12 focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--text-primary)] focus-visible:outline-offset-8 max-lg:grid-cols-1 max-lg:gap-6',
31-
LANDING_STAGE_RADIUS
32-
)}
23+
className='group grid min-w-0 grid-cols-[136px_1fr_auto] items-baseline gap-8 rounded-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--text-primary)] focus-visible:outline-offset-8 max-sm:grid-cols-[1fr_auto] max-sm:gap-x-4 max-sm:gap-y-3'
3324
>
34-
<div className='flex min-w-0 flex-col items-start gap-4'>
35-
<div className='flex flex-wrap items-center gap-x-4 gap-y-2 text-[var(--text-secondary)] text-sm'>
36-
<time dateTime={entry.date}>{formatPostDate(entry.date)}</time>
37-
{entry.draft ? <span>Draft</span> : null}
38-
</div>
39-
<h3
40-
id={`${entry.slug}-heading`}
41-
className={cn('text-balance text-[var(--text-primary)]', HOME_TYPE.h3)}
42-
>
43-
{entry.title}
44-
</h3>
45-
<p className={cn('text-pretty text-[var(--text-secondary)]', HOME_TYPE.body)}>
46-
{entry.description}
47-
</p>
48-
<span className='inline-flex items-center gap-2 text-[var(--text-primary)] text-sm'>
49-
Read update
50-
<ArrowRight
51-
aria-hidden='true'
52-
className='size-4 text-[var(--text-icon)] transition-transform duration-200 group-hover:translate-x-0.5 motion-reduce:transition-none'
53-
/>
54-
</span>
25+
<div className='flex flex-wrap items-center gap-x-4 gap-y-2 text-[var(--text-secondary)] text-sm max-sm:col-span-2'>
26+
<time dateTime={entry.date}>{formatPostDate(entry.date)}</time>
27+
{entry.draft ? <span>Draft</span> : null}
5528
</div>
56-
<div
57-
className={cn(
58-
'relative aspect-video overflow-hidden border border-[var(--border)] bg-[var(--surface-2)]',
59-
LANDING_STAGE_RADIUS
60-
)}
29+
<h3
30+
id={`${entry.slug}-heading`}
31+
className={cn('min-w-0 text-pretty text-[var(--text-primary)]', HOME_TYPE.h3)}
6132
>
62-
<Image
63-
src={entry.ogImage}
64-
alt={entry.ogAlt ?? entry.title}
65-
fill
66-
sizes={IMAGE_SIZES}
67-
priority={priority}
68-
className='object-contain'
69-
/>
70-
</div>
33+
{entry.title}
34+
</h3>
35+
<ArrowRight
36+
aria-hidden='true'
37+
className='size-4 shrink-0 text-[var(--text-icon)] transition-transform duration-200 group-hover:translate-x-0.5 motion-reduce:transition-none'
38+
/>
7139
</Link>
7240
</article>
7341
)

‎apps/sim/app/(landing)/changelog/components/changelog-video/changelog-video.tsx‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { LANDING_STAGE_RADIUS } from '@/app/(landing)/components/landing-layout'
55
interface ChangelogVideoProps {
66
src: string
77
poster: string
8+
/** Accessible description for the player; not repeated below the video. */
89
caption: string
910
captionsSrc?: string
1011
}
@@ -24,18 +25,14 @@ export function ChangelogVideo({ src, poster, caption, captionsSrc }: ChangelogV
2425
preload='none'
2526
poster={poster}
2627
aria-label={caption}
27-
className={cn(
28-
'aspect-video w-full border border-[var(--border)] bg-[var(--surface-2)]',
29-
LANDING_STAGE_RADIUS
30-
)}
28+
className={cn('aspect-video w-full bg-[var(--surface-2)]', LANDING_STAGE_RADIUS)}
3129
>
3230
<source src={src} type='video/mp4' />
3331
{captionsSrc ? (
3432
<track kind='captions' src={captionsSrc} srcLang='en' label='English' default />
3533
) : null}
3634
<a href={src}>Watch the demo</a>
3735
</video>
38-
<figcaption className='mt-3 text-[var(--text-secondary)] text-small'>{caption}</figcaption>
3936
</figure>
4037
)
4138
}

‎apps/sim/content/changelog/README.md‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Create `content/changelog/<slug>/index.mdx`. The slugs `archive` and `preview` are reserved. The website, archive, individual pages, sitemap, and RSS all read published entries from this directory through the shared content registry.
44

5-
The index shows up to 12 updates in chronological rows. When more exist, an Older updates link opens the archive at the first omitted entry. The archive links to every published update. Both lists read metadata only; the full article and its recordings are loaded on the individual update page. Rows use the local `ogImage` and `ogAlt`, so a reviewed demo poster also becomes the update's cover and share image. The page frame, typography, media radius, and row spacing use Sim's shared landing styles.
5+
The index shows up to 12 updates as dates and linked titles, without repeated summaries, thumbnails, or card borders. When more exist, an Older updates link opens the archive at the first omitted entry. The archive links to every published update. Both lists read metadata only; the full article and its media are loaded on the individual update page. Keep the concise description for search, sharing, and RSS. The reviewed poster supplies the share image; it does not need to repeat on the index. Article headers show the title and release date without a personal byline or another summary. The page frame, typography, media radius, and row spacing use Sim's shared landing styles.
66

77
Write for a team using Sim: what changed, what they can do with it, and where to find it. Group related PRs into one product update. Keep maintenance, refactors, and routine content edits in the technical GitHub release history.
88

@@ -44,13 +44,15 @@ Give readers the next useful step in the sentence that introduces a capability.
4444

4545
Use the guide for the exact surface being announced. Live Search connectors have their own `/search/<source>` guides; an ordinary workflow integration page can describe a different connection and permission model. Feature announcements should link to the feature guide, and API changes should link to the current generated operation page. Related Sim product pages and explanatory blog posts are useful when they help a reader use or understand the update.
4646

47-
Choose a few relevant links instead of repeating every destination in a separate resource list. Keep link text descriptive. The content audit checks integration destinations against the generated catalog and docs destinations against the MDX source and generated OpenAPI pages. Use current canonical URLs, including the operation ID's exact case. Confirm the linked page's instructions and any deep anchor in the deployment preview; a source file alone does not establish rollout or anchor correctness.
47+
For a model update, link the specific `/models/<provider>/<model>` page, or the provider catalog when discussing several models. Use the actual generated route, not a slug guessed from the model's display name. Keep the workflow setup link alongside it when useful.
48+
49+
Choose a few relevant links instead of repeating every destination in a separate resource list. Keep link text descriptive. The content audit checks integration and model destinations against their generated catalogs and docs destinations against the MDX source and generated OpenAPI pages. Use current canonical URLs, including the operation ID's exact case. Confirm the linked page's instructions and any deep anchor in the deployment preview; a source file alone does not establish rollout or anchor correctness.
4850

4951
## Search and answer-engine visibility
5052

5153
Keep each summary independently useful: name Sim and the feature, explain the new capability, and state material availability limits in the article. Put setup steps and demonstration explanations in server-rendered text alongside the media. Do not leave important facts only inside a screenshot or video.
5254

53-
The index and archive use real links, semantic lists, and CollectionPage/ItemList data matching the visible entries. Articles keep their own canonical URLs, BlogPosting data, author bylines, original publication dates, and substantive correction dates. Public articles appear in the sitemap and RSS; previews stay noindex and drafts stay out of public routes. Preserve these properties when changing the layout. If the archive eventually needs pagination, use server-rendered pages with real next/previous links and a self-canonical URL for each page; do not make older entries accessible only through a JavaScript button.
55+
The index and archive use real links, semantic lists, and CollectionPage/ItemList data matching the visible entries. Articles keep their own canonical URLs, BlogPosting data, authorship metadata, original publication dates, and substantive correction dates. Public articles appear in the sitemap and RSS; previews stay noindex and drafts stay out of public routes. Preserve these properties when changing the layout. If the archive eventually needs pagination, use server-rendered pages with real next/previous links and a self-canonical URL for each page; do not make older entries accessible only through a JavaScript button.
5456

5557
These are standard search foundations, not a guarantee of indexing or AI citations. [Google's AI search guidance](https://developers.google.com/search/docs/appearance/ai-features) calls for crawlable, useful text, relevant media, internal links, and structured data matching the visible content; it does not require special AI-only markup. Avoid hidden keyword blocks, invented FAQs, and schema for information that the page does not show.
5658

0 commit comments

Comments
 (0)