chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
@@ -0,0 +1,38 @@
'use client'
import { ChipLink } from '@sim/emcn'
import { BookOpen, Github, Rss } from 'lucide-react'
/**
* Changelog hero actions - the GitHub / Documentation / RSS pill links shown
* beneath the changelog headline. A small client leaf because `ChipLink` is a
* Client Component and its `leftIcon` is a component reference that cannot cross
* the server→client boundary as a prop (same pattern as the platform pill CTA).
* GitHub is the primary filled chip; Docs and RSS are the default pills.
*/
export function ChangelogActions() {
return (
<div className='flex flex-wrap items-center gap-1'>
<ChipLink
variant='primary'
href='https://github.com/simstudioai/sim/releases'
target='_blank'
rel='noopener noreferrer'
leftIcon={Github}
>
View on GitHub
</ChipLink>
<ChipLink
href='https://docs.sim.ai'
target='_blank'
rel='noopener noreferrer'
leftIcon={BookOpen}
>
Documentation
</ChipLink>
<ChipLink href='/changelog.xml' leftIcon={Rss}>
RSS Feed
</ChipLink>
</div>
)
}
@@ -0,0 +1 @@
export { ChangelogActions } from './changelog-actions'
@@ -0,0 +1,220 @@
'use client'
import { type ReactNode, useRef, useState } from 'react'
import { Streamdown } from 'streamdown'
import 'streamdown/styles.css'
import { Avatar, AvatarFallback, AvatarImage, Chip, cn } from '@sim/emcn'
import { formatDate } from '@sim/utils/formatting'
import type { ChangelogEntry, GitHubRelease } from '@/app/(landing)/changelog/types'
import { mapReleases, releasesEndpoint } from '@/app/(landing)/changelog/utils'
/**
* The changelog timeline - the single client leaf of the changelog page. Renders
* each GitHub release as a `<section>` (an `<h2>` version tag + contributor
* avatars + cleaned markdown via {@link Streamdown}) and paginates further pages
* from the GitHub Releases API on demand. Re-authored from the prior dark
* timeline onto the platform light tokens; the fetch, markdown cleaning, and
* load-more behavior are preserved.
*/
interface ChangelogTimelineProps {
initialEntries: ChangelogEntry[]
}
function stripContributors(body: string): string {
let output = body
output = output.replace(
/(^|\n)#{1,6}\s*Contributors\s*\n[\s\S]*?(?=\n\s*\n|\n#{1,6}\s|$)/gi,
'\n'
)
output = output.replace(
/(^|\n)\s*(?:\*\*|__)?\s*Contributors\s*(?:\*\*|__)?\s*:?\s*\n[\s\S]*?(?=\n\s*\n|\n#{1,6}\s|$)/gi,
'\n'
)
output = output.replace(
/(^|\n)[-*+]\s*(?:@[A-Za-z0-9-]+(?:\s*,\s*|\s+))+@[A-Za-z0-9-]+\s*(?=\n)/g,
'\n'
)
output = output.replace(
/(^|\n)\s*(?:@[A-Za-z0-9-]+(?:\s*,\s*|\s+))+@[A-Za-z0-9-]+\s*(?=\n)/g,
'\n'
)
return output
}
function stripPrReferences(body: string): string {
return body.replace(/\s*\(\s*\[#\d+\]\([^)]*\)\s*\)/g, '').replace(/\s*\(\s*#\d+\s*\)/g, '')
}
function cleanMarkdown(body: string): string {
return stripPrReferences(stripContributors(body))
}
function isContributorsLabel(children: ReactNode): boolean {
return /^\s*contributors\s*:?\s*$/i.test(String(children))
}
export function ChangelogTimeline({ initialEntries }: ChangelogTimelineProps) {
const [entries, setEntries] = useState<ChangelogEntry[]>(initialEntries)
const [loading, setLoading] = useState<boolean>(false)
const [done, setDone] = useState<boolean>(false)
const pageRef = useRef(1)
const loadMore = async () => {
if (loading || done) return
setLoading(true)
try {
const nextPage = pageRef.current + 1
// boundary-raw-fetch: external GitHub Releases API (cross-origin), not a same-origin contract
const res = await fetch(releasesEndpoint(nextPage), {
headers: { Accept: 'application/vnd.github+json' },
})
const releases = (await res.json()) as GitHubRelease[]
const mapped = mapReleases(releases ?? [])
if (mapped.length === 0) {
setDone(true)
} else {
setEntries((prev) => [...prev, ...mapped])
pageRef.current = nextPage
}
} catch {
setDone(true)
} finally {
setLoading(false)
}
}
return (
<div className='flex flex-col gap-7'>
{entries.map((entry) => {
const headingId = `release-${entry.tag}-heading`
return (
<section key={entry.tag} aria-labelledby={headingId} className='flex flex-col'>
<div className='flex items-center justify-between gap-4'>
<div className='flex items-center gap-2'>
<h2 id={headingId} className='text-[18px] text-[var(--text-primary)] leading-[1.3]'>
{entry.tag}
</h2>
{entry.contributors.length > 0 ? (
<div className='flex'>
{entry.contributors.slice(0, 5).map((contributor, index) => (
<a
key={contributor}
href={`https://github.com/${contributor}`}
target='_blank'
rel='noopener noreferrer'
aria-label={`View @${contributor} on GitHub`}
title={`@${contributor}`}
className={index === 0 ? 'block' : '-ms-2 block'}
>
<Avatar className='size-6 ring-2 ring-[var(--bg)]'>
<AvatarImage
src={`https://avatars.githubusercontent.com/${contributor}`}
alt={`@${contributor}`}
className='hover:z-10'
/>
<AvatarFallback>{contributor.slice(0, 2).toUpperCase()}</AvatarFallback>
</Avatar>
</a>
))}
{entry.contributors.length > 5 ? (
<div className='-ms-2 relative flex size-6 items-center justify-center rounded-full bg-[var(--surface-2)] text-[10px] text-[var(--text-body)] ring-2 ring-[var(--bg)] hover:z-10'>
+{entry.contributors.length - 5}
</div>
) : null}
</div>
) : null}
</div>
<span className='text-[12px] text-[var(--text-muted)]'>
{formatDate(new Date(entry.date))}
</span>
</div>
<div aria-hidden='true' className='mt-[9px] mb-3 h-px bg-[var(--border)]' />
<div className='max-w-none'>
<Streamdown
mode='static'
components={{
h2: ({ children, ...props }) =>
isContributorsLabel(children) ? null : (
<h3
className='mt-5 mb-2 text-[14px] text-[var(--text-primary)] leading-[1.4]'
{...props}
>
{children}
</h3>
),
h3: ({ children, ...props }) =>
isContributorsLabel(children) ? null : (
<h4
className='mt-4 mb-1 text-[14px] text-[var(--text-primary)] leading-[1.4]'
{...props}
>
{children}
</h4>
),
ul: ({ children, ...props }) => (
<ul className='mt-2 mb-3 space-y-1.5' {...props}>
{children}
</ul>
),
li: ({ children, ...props }) =>
isContributorsLabel(children) ? null : (
<li
className='text-[13px] text-[var(--text-body)] leading-relaxed'
{...props}
>
{children}
</li>
),
p: ({ children, ...props }) =>
isContributorsLabel(children) ? null : (
<p
className='mb-3 text-[13px] text-[var(--text-body)] leading-relaxed'
{...props}
>
{children}
</p>
),
strong: ({ children, ...props }) => (
<strong className='text-[var(--text-primary)]' {...props}>
{children}
</strong>
),
inlineCode: ({ children }) => (
<code className='whitespace-normal rounded bg-[var(--surface-2)] px-1 py-0.5 font-mono text-[var(--text-primary)] not-italic'>
{children}
</code>
),
img: () => null,
a: ({ children, className, ...props }) => (
<a
{...props}
className={cn('text-[var(--text-primary)] underline', className)}
target='_blank'
rel='noopener noreferrer'
>
{children}
</a>
),
}}
>
{cleanMarkdown(entry.content)}
</Streamdown>
</div>
</section>
)
})}
{!done ? (
<div>
<Chip type='button' flush onClick={loadMore} disabled={loading}>
{loading ? 'Loading…' : 'Show more'}
</Chip>
</div>
) : null}
</div>
)
}
@@ -0,0 +1 @@
export { ChangelogTimeline } from './changelog-timeline'
@@ -0,0 +1,2 @@
export { ChangelogActions } from './changelog-actions'
export { ChangelogTimeline } from './changelog-timeline'