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,6 @@
export {
PricingCard,
type PricingCardCta,
type PricingCardProps,
type PricingCardSection,
} from './pricing-card'
@@ -0,0 +1,138 @@
import { Check, ChipLink, ChipTag, cn } from '@sim/emcn'
import { SlackIcon } from '@/components/icons'
import type { CellValue } from '@/app/workspace/[workspaceId]/upgrade/components/comparison-table/comparison-data'
/** Maps a cell-icon identifier to its brand icon component. */
const CELL_ICONS = { slack: SlackIcon } as const
/** Resolved CTA for a pricing card - label, chip variant, and destination href. */
export interface PricingCardCta {
label: string
variant: 'primary' | 'border-shadow'
href: string
}
/** A labelled group of feature rows for one plan, transposed from the comparison data. */
export interface PricingCardSection {
/** Stable key for the section, used for React reconciliation. */
key: string
/** Section header, e.g. `"Rate limits (runs/min)"`. Omit to render the rows without a header. */
title?: string
/** Feature rows - this plan's value for each comparison row in the section. */
rows: { label: string; value: CellValue }[]
}
/**
* Props for {@link PricingCard}.
*/
export interface PricingCardProps {
/** Plan name, e.g. `"Pro"`. */
name: string
/** Headline price, e.g. `"$25"`, `"$0"`, or `"Custom"`. */
price: string
/** Small line under the price, e.g. `"per user/month, billed monthly"`. */
priceSubtext?: string
/** Optional discount pill next to the price, e.g. `"15% off"`. */
discountLabel?: string
/** Resolved CTA - label, variant, and destination href. */
cta: PricingCardCta
/** Full feature breakdown for this plan, grouped by section. */
sections: PricingCardSection[]
/** Extra outer classes. */
className?: string
}
/**
* Renders one plan's value for a feature row: `true` → check, `false` → em-dash,
* an icon reference → its brand icon, a string → right-aligned text.
*/
function FeatureValue({ value }: { value: CellValue }) {
if (value === true) {
return <Check className='size-[14px] flex-shrink-0 text-[var(--text-icon)]' />
}
if (value === false) {
return <span className='select-none text-[var(--text-muted)]'></span>
}
if (typeof value === 'object') {
const Icon = CELL_ICONS[value.icon]
return <Icon className='size-[14px] flex-shrink-0' />
}
return (
<span className='whitespace-nowrap text-right text-[var(--text-primary)] text-sm tabular-nums'>
{value}
</span>
)
}
/**
* Public pricing card - one plan rendered as a self-contained spec sheet: the
* header (name, price, CTA, segment) above the full comparison breakdown, grouped
* by section with a hairline rule under each section header.
*
* The radius (`rounded-lg`) matches the landing hero's visual panel so the cards
* line up with the rest of the landing chrome. Every card carries the identical
* row structure (only values differ) so a grid of them renders at exactly the
* same height. Feature rows are transposed from the shared comparison data, so
* the card can never drift from the platform.
*/
export function PricingCard({
name,
price,
priceSubtext,
discountLabel,
cta,
sections,
className,
}: PricingCardProps) {
return (
<article
className={cn(
'flex h-full flex-col gap-[22px] rounded-lg border border-[var(--border-1)] bg-[var(--surface-2)] p-5',
className
)}
>
<div className='flex flex-col gap-[22px]'>
<h2 className='text-[24px] text-[var(--text-primary)]'>{name}</h2>
<div className='flex flex-col'>
<div className='flex items-center gap-2'>
<span className='text-[20px] text-[var(--text-primary)] tabular-nums'>{price}</span>
{discountLabel && <ChipTag variant='mono'>{discountLabel}</ChipTag>}
</div>
<p className='text-[var(--text-muted)] text-base'>{priceSubtext ?? ' '}</p>
</div>
<ChipLink
href={cta.href}
variant={cta.variant}
fullWidth
flush
className='w-full justify-center'
>
{cta.label}
</ChipLink>
</div>
<div className='flex flex-col gap-5'>
{sections.map((section) => (
<div key={section.key} className='flex flex-col'>
{section.title && (
<>
<span className='text-[var(--text-primary)] text-small'>{section.title}</span>
<div className='mt-2 mb-2.5 h-px bg-[var(--border)]' />
</>
)}
<div className='flex flex-col gap-2.5'>
{section.rows.map((row) => (
<div key={row.label} className='flex items-center justify-between gap-3'>
<span className='text-[var(--text-body)] text-sm'>{row.label}</span>
<FeatureValue value={row.value} />
</div>
))}
</div>
</div>
))}
</div>
</article>
)
}
@@ -0,0 +1 @@
export { PricingPlans, type PricingPlansProps } from './pricing-plans'
@@ -0,0 +1,154 @@
'use client'
import type { ReactNode } from 'react'
import { useQueryStates } from 'nuqs'
import { getUpgradeCardCta, type PlanTier, type UpgradeCardId } from '@/lib/billing/client'
import { ANNUAL_DISCOUNT_RATE, CREDIT_TIERS } from '@/lib/billing/constants'
import { DEMO_HREF, SIGNUP_HREF } from '@/app/(landing)/constants'
import {
PricingCard,
type PricingCardCta,
type PricingCardSection,
} from '@/app/(landing)/pricing/components/pricing-card'
import { pricingParsers, pricingUrlKeys } from '@/app/(landing)/pricing/search-params'
import { BillingPeriodToggle } from '@/app/workspace/[workspaceId]/upgrade/components'
import {
type CellValue,
COMPARISON_SECTIONS,
} from '@/app/workspace/[workspaceId]/upgrade/components/comparison-table/comparison-data'
/**
* A public visitor has no subscription, so each card's canonical label and
* variant resolves against the `free` tier. On this page every self-serve CTA
* funnels to sign-up regardless of plan; the enterprise CTA routes to the
* demo-request form instead.
*/
const VISITOR_TIER: PlanTier = 'free'
/** This section's rows render on the public cards without their group header. */
const HEADERLESS_SECTION_TITLE = 'Credits & pricing'
/**
* Props for {@link PricingPlans}.
*/
export interface PricingPlansProps {
/**
* Server-rendered heading slot - the page `<h1>` and its sr-only GEO summary.
* Passed as a slot so the crawlable copy stays in the server payload.
*/
heading: ReactNode
}
/**
* Transpose the shared comparison data into one plan column's feature sections,
* so each card carries the full breakdown for its plan without duplicating it.
*/
function sectionsForColumn(col: number): PricingCardSection[] {
return COMPARISON_SECTIONS.map((section) => ({
key: section.title,
title: section.title === HEADERLESS_SECTION_TITLE ? undefined : section.title,
rows: section.rows.map((row) => ({ label: row.label, value: row.values[col] as CellValue })),
}))
}
/** The four card columns (Free, Pro, Max, Enterprise) transposed once at module load - the source data is static, so this never needs to be redone per render. */
const SECTIONS_BY_COLUMN = [0, 1, 2, 3].map(sectionsForColumn)
/** Round a dollar amount down to the annual-billing discounted price. */
function annualPrice(dollars: number): number {
return Math.round(dollars * (1 - ANNUAL_DISCOUNT_RATE))
}
/**
* Resolve a card's canonical label and variant from the free-tier matrix. A
* `sales` intent ("Talk to sales") routes to the demo-request form, matching
* every other "Contact sales" CTA on the landing site; every other intent
* funnels logged-out visitors to sign-up.
*/
function resolveCta(card: UpgradeCardId): PricingCardCta {
const cta = getUpgradeCardCta(VISITOR_TIER, card)
return {
label: cta.label,
variant: cta.variant,
href: cta.intent === 'sales' ? DEMO_HREF : SIGNUP_HREF,
}
}
const FREE_CTA: PricingCardCta = {
label: 'Get started',
variant: 'border-shadow',
href: SIGNUP_HREF,
}
/**
* The single interactive island of the public pricing page. It owns the lone
* piece of state - `isAnnual`, defaulting to monthly - which the billing-period
* toggle (centered under the heading) and the Pro/Max card prices read from one
* source.
*
* Each plan (Free, Pro, Max, Enterprise) renders as a full {@link PricingCard}
* spec sheet in a four-up grid that renders at exactly the same height. Prices
* and the discount come from {@link CREDIT_TIERS} / {@link ANNUAL_DISCOUNT_RATE},
* the feature breakdown is transposed from the shared `COMPARISON_SECTIONS`, and
* the CTA labels/variants from {@link getUpgradeCardCta}. Every CTA, Free
* included, funnels to `/signup`.
*/
export function PricingPlans({ heading }: PricingPlansProps) {
const [{ billing }, setParams] = useQueryStates(pricingParsers, pricingUrlKeys)
const isAnnual = billing === 'annual'
const setIsAnnual = (next: boolean) => setParams({ billing: next ? 'annual' : 'monthly' })
const discountPct = Math.round(ANNUAL_DISCOUNT_RATE * 100)
const proPrice = isAnnual ? annualPrice(CREDIT_TIERS[0].dollars) : CREDIT_TIERS[0].dollars
const maxPrice = isAnnual ? annualPrice(CREDIT_TIERS[1].dollars) : CREDIT_TIERS[1].dollars
const priceSubtext = isAnnual
? 'per user/month, billed annually'
: 'per user/month, billed monthly'
const discountLabel = isAnnual ? `${discountPct}% off` : undefined
const proCta = resolveCta('pro')
const maxCta = resolveCta('max')
const enterpriseCta = resolveCta('enterprise')
return (
<>
<div className='flex flex-col items-center gap-4'>
{heading}
<BillingPeriodToggle isAnnual={isAnnual} onChange={setIsAnnual} />
</div>
<div className='grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4'>
<PricingCard
name='Free'
price='$0'
priceSubtext='Free forever'
cta={FREE_CTA}
sections={SECTIONS_BY_COLUMN[0]}
/>
<PricingCard
name='Pro'
price={`$${proPrice}`}
discountLabel={discountLabel}
priceSubtext={priceSubtext}
cta={proCta}
sections={SECTIONS_BY_COLUMN[1]}
/>
<PricingCard
name='Max'
price={`$${maxPrice}`}
discountLabel={discountLabel}
priceSubtext={priceSubtext}
cta={maxCta}
sections={SECTIONS_BY_COLUMN[2]}
/>
<PricingCard
name='Enterprise'
price='Custom'
priceSubtext='Tailored to your team'
cta={enterpriseCta}
sections={SECTIONS_BY_COLUMN[3]}
/>
</div>
</>
)
}
@@ -0,0 +1 @@
export { PricingStructuredData } from './pricing-structured-data'
@@ -0,0 +1,95 @@
import { CREDIT_TIERS } from '@/lib/billing/constants'
import { SITE_URL } from '@/lib/core/utils/urls'
import { JsonLd } from '@/app/(landing)/components/json-ld'
import { COMPARISON_SECTIONS } from '@/app/workspace/[workspaceId]/upgrade/components/comparison-table/comparison-data'
const PAGE_URL = `${SITE_URL}/pricing`
/** Feature list derived from the comparison data the cards render, so it can't drift. */
const FEATURE_LIST = Array.from(
new Set(COMPARISON_SECTIONS.flatMap((section) => section.rows.map((row) => row.label)))
)
const PRICING_JSON_LD = {
'@context': 'https://schema.org',
'@graph': [
{
'@type': 'WebPage',
'@id': `${PAGE_URL}#webpage`,
url: PAGE_URL,
name: 'Pricing | Sim, the AI Workspace',
description:
'Pricing for Sim, the open-source AI workspace where teams build, deploy, and manage AI agents. Compare the Free, Pro, Max, and Enterprise plans.',
isPartOf: { '@id': `${SITE_URL}#website` },
about: { '@id': `${PAGE_URL}#application` },
breadcrumb: { '@id': `${PAGE_URL}#breadcrumb` },
inLanguage: 'en-US',
},
{
'@type': 'BreadcrumbList',
'@id': `${PAGE_URL}#breadcrumb`,
itemListElement: [
{ '@type': 'ListItem', position: 1, name: 'Home', item: SITE_URL },
{ '@type': 'ListItem', position: 2, name: 'Pricing', item: PAGE_URL },
],
},
{
'@type': 'WebApplication',
'@id': `${PAGE_URL}#application`,
name: 'Sim',
description:
'Sim is the open-source AI workspace where teams build, deploy, and manage AI agents, connecting 1,000+ integrations and every major LLM.',
applicationCategory: 'BusinessApplication',
operatingSystem: 'Web',
url: SITE_URL,
featureList: FEATURE_LIST,
offers: [
{
'@type': 'Offer',
name: 'Free',
price: '0',
priceCurrency: 'USD',
description: 'Start building AI agents for free.',
},
{
'@type': 'Offer',
name: 'Pro',
price: String(CREDIT_TIERS[0].dollars),
priceCurrency: 'USD',
description: 'For growing teams. Billed per user/month.',
},
{
'@type': 'Offer',
name: 'Max',
price: String(CREDIT_TIERS[1].dollars),
priceCurrency: 'USD',
description: 'For scaling businesses. Billed per user/month.',
},
{
'@type': 'Offer',
name: 'Enterprise',
priceCurrency: 'USD',
description: 'Custom limits and infrastructure for large organizations.',
},
],
},
],
}
/**
* JSON-LD for the public pricing page - a `WebPage` (about a `WebApplication`),
* a `BreadcrumbList`, and the `WebApplication` (Sim) carrying one `Offer` per
* plan tier. Rendered server-side before any visible content so crawlers and AI
* answer engines read the structured pricing first. Mirrors the platform/
* solutions structured-data shape so the landing family stays consistent.
*
* Everything is derived from the same shared sources that drive the visible cards
* - Pro/Max monthly prices from {@link CREDIT_TIERS} and the `featureList` from
* the shared `COMPARISON_SECTIONS` - so the structured data can never drift from
* the page. Free is `$0`; Enterprise is custom and intentionally ships no price.
*
* Server Component; no client cost.
*/
export function PricingStructuredData() {
return <JsonLd data={PRICING_JSON_LD} />
}