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,3 @@
/** Shared className for inline auth action links. */
export const AUTH_TEXT_LINK =
'font-medium text-[var(--brand-accent)] underline-offset-4 transition hover:text-[var(--brand-accent-hover)] hover:underline disabled:cursor-not-allowed disabled:opacity-50' as const
@@ -0,0 +1,21 @@
interface AuthDividerProps {
label: string
}
/**
* The "Or continue with" rule separating the email/password form from the
* social/SSO options. Light tokens only: a `--border` hairline with the label
* knocked out over the `--bg` canvas in `--text-muted`.
*/
export function AuthDivider({ label }: AuthDividerProps) {
return (
<div className='relative'>
<div className='absolute inset-0 flex items-center'>
<div className='w-full border-[var(--border)] border-t' />
</div>
<div className='relative flex justify-center'>
<span className='bg-[var(--bg)] px-4 text-[var(--text-muted)] text-sm'>{label}</span>
</div>
</div>
)
}
@@ -0,0 +1,41 @@
import type { ReactNode } from 'react'
import { Label } from '@sim/emcn'
interface AuthFieldProps {
/** Matches the `id` set on the control rendered as {@link children}. */
htmlFor: string
label: string
/** Validation messages to render beneath the control. */
errors?: string[]
/** Optional right-aligned action shown next to the label (e.g. Forgot password). */
action?: ReactNode
/** The field control — a {@link ChipInput}/{@link PasswordInput}. */
children: ReactNode
}
/**
* A labeled form field row: canonical {@link Label}, an optional inline label
* action, the control, and a validation-message list in the error token. The
* control drives its own invalid chrome through its `error` prop — this wrapper
* only owns the label row and the message list, so every auth field reads and
* spaces identically.
*/
export function AuthField({ htmlFor, label, errors, action, children }: AuthFieldProps) {
const hasErrors = Boolean(errors && errors.length > 0)
return (
<div className='space-y-2'>
<div className='flex items-center justify-between'>
<Label htmlFor={htmlFor}>{label}</Label>
{action}
</div>
{children}
{hasErrors && (
<div className='space-y-1 text-[var(--text-error)] text-caption' aria-live='polite'>
{errors?.map((error) => (
<p key={error}>{error}</p>
))}
</div>
)}
</div>
)
}
@@ -0,0 +1,28 @@
import type { ReactNode } from 'react'
import { cn } from '@sim/emcn'
interface AuthFormMessageProps {
type: 'error' | 'success'
align?: 'left' | 'center'
children: ReactNode
}
/**
* Form-level status copy (not tied to a single field) in the canonical tokens:
* errors in `--text-error`, success in `--brand-accent`. One place owns the
* auth message chrome so success/error states never drift to ad-hoc hex or
* `text-red-*`/`#4CAF50` colors.
*/
export function AuthFormMessage({ type, align = 'left', children }: AuthFormMessageProps) {
return (
<div
className={cn(
'space-y-1 text-caption',
align === 'center' && 'text-center',
type === 'error' ? 'text-[var(--text-error)]' : 'text-[var(--brand-accent)]'
)}
>
{children}
</div>
)
}
@@ -0,0 +1,21 @@
import type { ReactNode } from 'react'
interface AuthHeaderProps {
title: string
description: ReactNode
}
/**
* The centered heading + subcopy block shared by every auth page and status
* page. One source of truth for auth heading typography (light tokens, normal
* weight, no bespoke tracking — aligned with the landing scale, sized down for
* the single-column form).
*/
export function AuthHeader({ title, description }: AuthHeaderProps) {
return (
<div className='space-y-1 text-center'>
<h1 className='text-balance text-[32px] text-[var(--text-primary)] leading-[1.2]'>{title}</h1>
<p className='text-[var(--text-muted)] text-base leading-[1.5]'>{description}</p>
</div>
)
}
@@ -0,0 +1,20 @@
'use client'
import * as React from 'react'
import { ChipInput, type ChipInputProps, cn } from '@sim/emcn'
import { AUTH_CONTROL_HEIGHT } from '@/app/(auth)/components/constants'
/**
* The auth text field — a {@link ChipInput} raised to the auth control height
* ({@link AUTH_CONTROL_HEIGHT}) so every labeled field on the auth and invite
* surfaces shares one slightly-taller geometry. All chip props pass through
* (`error`, `endAdornment`, `icon`, …); only the height is owned here, and a
* caller's `className` (layout only) still composes on top.
*/
export const AuthInput = React.forwardRef<HTMLInputElement, ChipInputProps>(
({ className, ...props }, ref) => (
<ChipInput ref={ref} className={cn(AUTH_CONTROL_HEIGHT, className)} {...props} />
)
)
AuthInput.displayName = 'AuthInput'
@@ -0,0 +1,26 @@
import { AuthTextLink } from '@/app/(auth)/components/auth-text-link'
interface AuthLegalFooterProps {
/** The gerund describing the consent action, e.g. "signing in". */
action: string
}
/**
* The "By {action}, you agree to our Terms / Privacy" fine print shared by the
* login and signup pages. Restyled to muted light tokens with the legal links
* routed through {@link AuthTextLink}, so the consent copy has one source.
*/
export function AuthLegalFooter({ action }: AuthLegalFooterProps) {
return (
<p className='text-center text-[var(--text-muted)] text-caption leading-relaxed'>
By {action}, you agree to our{' '}
<AuthTextLink href='/terms' external>
Terms of Service
</AuthTextLink>{' '}
and{' '}
<AuthTextLink href='/privacy' external>
Privacy Policy
</AuthTextLink>
</p>
)
}
@@ -0,0 +1,27 @@
import { ChipLink } from '@sim/emcn'
interface AuthNavPromptProps {
/** Muted lead text before the link (e.g. "Don't have an account?"). */
prompt?: string
href: string
linkLabel: string
/** Side effect to run before navigation (e.g. clearing verification state). */
onNavigate?: () => void
}
/**
* The cross-page navigation row (Sign up / Sign in / Back to login) — an
* optional muted prompt followed by an outline {@link ChipLink} pill, matching
* the landing's secondary chip CTAs. Centralizes the auth nav affordance so the
* pill chrome is described by props, never restyled per page.
*/
export function AuthNavPrompt({ prompt, href, linkLabel, onNavigate }: AuthNavPromptProps) {
return (
<div className='flex items-center justify-center gap-1 text-sm'>
{prompt && <span className='text-[var(--text-muted)]'>{prompt}</span>}
<ChipLink href={href} onClick={onNavigate} className='border border-[var(--border-1)]'>
{linkLabel}
</ChipLink>
</div>
)
}
@@ -0,0 +1,40 @@
import type { ReactNode } from 'react'
import Link from 'next/link'
import { LogoMark, SimWordmark } from '@/app/(landing)/components/navbar/components'
interface AuthShellProps {
/** Centered content column (the form, status copy, etc.). */
children: ReactNode
/** Optional element pinned to the bottom of the shell (e.g. the support footer). */
footer?: ReactNode
}
/**
* The light auth/status page frame — the single source of truth for the shell
* every auth page and standalone status page wears.
*
* Mirrors the landing chrome: it pins the `light` token layer (so the platform's
* light-mode `var(--*)` tokens resolve regardless of the visitor's theme), uses
* the canvas/`--text-primary` surface, and renders a logo-only header that reuses
* the landing {@link LogoMark} + {@link SimWordmark} at the same nav gutters. The
* single content column is centered and capped for a calm single-form layout.
*/
export function AuthShell({ children, footer }: AuthShellProps) {
return (
<div className='light relative flex min-h-screen flex-col bg-[var(--bg)] text-[var(--text-primary)]'>
<header>
<nav className='mx-auto flex w-full max-w-[1446px] items-center px-12 py-4 max-sm:px-5 max-lg:px-8'>
<Link href='/' aria-label='Sim home' className='flex h-[30px] items-center'>
<LogoMark>
<SimWordmark />
</LogoMark>
</Link>
</nav>
</header>
<div className='flex flex-1 items-center justify-center px-4 pb-16'>
<div className='w-full max-w-[400px]'>{children}</div>
</div>
{footer}
</div>
)
}
@@ -0,0 +1,48 @@
import type { ReactNode } from 'react'
import { Chip, Loader } from '@sim/emcn'
import { AUTH_BUTTON_CLASS } from '@/app/(auth)/components/constants'
interface AuthSubmitButtonProps {
children: ReactNode
/** Label shown beside the spinner while the action is in flight. */
loadingLabel: string
loading?: boolean
disabled?: boolean
type?: 'submit' | 'button'
onClick?: () => void
}
/**
* The canonical full-width primary auth action — a `primary`-variant {@link Chip}
* with the shared in-flight spinner, so the primary CTA chrome lives in
* exactly one place.
*/
export function AuthSubmitButton({
children,
loadingLabel,
loading = false,
disabled = false,
type = 'submit',
onClick,
}: AuthSubmitButtonProps) {
return (
<Chip
variant='primary'
type={type}
onClick={onClick}
disabled={disabled || loading}
fullWidth
flush
className={AUTH_BUTTON_CLASS}
>
{loading ? (
<span className='flex items-center gap-2'>
<Loader className='size-4' animate />
{loadingLabel}
</span>
) : (
children
)}
</Chip>
)
}
@@ -0,0 +1,58 @@
'use client'
import type { ReactNode } from 'react'
import { cn } from '@sim/emcn'
import Link from 'next/link'
const AUTH_TEXT_LINK_CLASS =
'text-[var(--text-secondary)] underline-offset-4 transition-colors hover:text-[var(--text-primary)] hover:underline disabled:cursor-not-allowed disabled:opacity-50'
interface AuthTextLinkProps {
children: ReactNode
/** Renders a navigation link when set; otherwise renders an action button. */
href?: string
onClick?: () => void
/** Opens the link in a new tab with safe `rel` (e.g. Terms/Privacy). */
external?: boolean
disabled?: boolean
className?: string
}
/**
* The canonical inline text affordance for the auth pages — forgot-password,
* resend, and the legal links. Renders a {@link Link} when `href` is set and a
* `<button>` otherwise, both in one light-token style. Replaces the legacy dark
* `AUTH_TEXT_LINK` class string with a single props-driven source of truth.
*/
export function AuthTextLink({
children,
href,
onClick,
external = false,
disabled = false,
className,
}: AuthTextLinkProps) {
if (href) {
return (
<Link
href={href}
onClick={onClick}
className={cn(AUTH_TEXT_LINK_CLASS, className)}
{...(external ? { target: '_blank', rel: 'noopener noreferrer' } : {})}
>
{children}
</Link>
)
}
return (
<button
type='button'
onClick={onClick}
disabled={disabled}
className={cn(AUTH_TEXT_LINK_CLASS, className)}
>
{children}
</button>
)
}
@@ -0,0 +1,17 @@
/**
* Auth and invite surfaces use a slightly taller control than the 30px chip
* default, matching the landing `HeroCta` field family (the landing's own
* auth-adjacent CTA renders taller fields than in-app chips). Applied as the
* single source of truth for every auth field and button height so the inputs,
* submit, social, SSO, and invite action buttons stay on one line.
*/
export const AUTH_CONTROL_HEIGHT = 'h-9'
/**
* Shared layout for full-width auth/invite chip buttons (submit, social, SSO,
* invite actions). `[&>span]:flex-none` collapses the chip's stretching label
* span — which carries `flex-1` — so the icon + label cluster truly centers
* under `justify-center` (the landing `HeroCta` idiom). Height-only inputs use
* {@link AUTH_CONTROL_HEIGHT}; buttons compose this on top of it.
*/
export const AUTH_BUTTON_CLASS = `${AUTH_CONTROL_HEIGHT} w-full justify-center [&>span]:flex-none`
+14
View File
@@ -0,0 +1,14 @@
export { AuthDivider } from './auth-divider'
export { AuthField } from './auth-field'
export { AuthFormMessage } from './auth-form-message'
export { AuthHeader } from './auth-header'
export { AuthInput } from './auth-input'
export { AuthLegalFooter } from './auth-legal-footer'
export { AuthNavPrompt } from './auth-nav-prompt'
export { AuthShell } from './auth-shell'
export { AuthSubmitButton } from './auth-submit-button'
export { AuthTextLink } from './auth-text-link'
export { PasswordInput } from './password-input'
export { SocialLoginButtons } from './social-login-buttons'
export { SSOLoginButton } from './sso-login-button'
export { SupportFooter } from './support-footer'
@@ -0,0 +1,20 @@
import { env } from '@/lib/core/config/env'
import {
isGithubAuthDisabled,
isGoogleAuthDisabled,
isMicrosoftAuthDisabled,
isProd,
} from '@/lib/core/config/env-flags'
export async function getOAuthProviderStatus() {
const githubAvailable =
!!(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET) && !isGithubAuthDisabled
const googleAvailable =
!!(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET) && !isGoogleAuthDisabled
const microsoftAvailable =
!!(env.MICROSOFT_CLIENT_ID && env.MICROSOFT_CLIENT_SECRET) && !isMicrosoftAuthDisabled
return { githubAvailable, googleAvailable, microsoftAvailable, isProduction: isProd }
}
@@ -0,0 +1,37 @@
'use client'
import { useState } from 'react'
import { ChipInput, type ChipInputProps, cn } from '@sim/emcn'
import { Eye, EyeOff } from 'lucide-react'
import { AUTH_CONTROL_HEIGHT } from '@/app/(auth)/components/constants'
type PasswordInputProps = Omit<ChipInputProps, 'type' | 'icon' | 'endAdornment'>
/**
* A {@link ChipInput} that owns the password reveal toggle — the eye button is
* driven through the canonical `endAdornment` slot and the field's invalid state
* through the `error` prop, so no consumer hand-rolls the relative wrapper +
* absolutely positioned button the auth forms previously duplicated four times.
*/
export function PasswordInput({ error, className, ...props }: PasswordInputProps) {
const [visible, setVisible] = useState(false)
return (
<ChipInput
{...props}
className={cn(AUTH_CONTROL_HEIGHT, className)}
type={visible ? 'text' : 'password'}
error={error}
endAdornment={
<button
type='button'
onClick={() => setVisible((v) => !v)}
aria-label={visible ? 'Hide password' : 'Show password'}
className='flex shrink-0 text-[var(--text-icon)] transition-colors hover:text-[var(--text-primary)]'
>
{visible ? <EyeOff className='size-[14px]' /> : <Eye className='size-[14px]' />}
</button>
}
/>
)
}
@@ -0,0 +1,126 @@
'use client'
import { type ReactNode, useState } from 'react'
import { Chip, cn } from '@sim/emcn'
import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
import { GithubIcon, GoogleIcon, MicrosoftIcon } from '@/components/icons'
import { client } from '@/lib/auth/auth-client'
import { AUTH_BUTTON_CLASS } from '@/app/(auth)/components/constants'
const logger = createLogger('SocialLoginButtons')
interface SocialLoginButtonsProps {
githubAvailable: boolean
googleAvailable: boolean
microsoftAvailable: boolean
callbackURL?: string
isProduction: boolean
children?: ReactNode
}
export function SocialLoginButtons({
githubAvailable,
googleAvailable,
microsoftAvailable,
callbackURL = '/workspace',
isProduction,
children,
}: SocialLoginButtonsProps) {
const [isGithubLoading, setIsGithubLoading] = useState(false)
const [isGoogleLoading, setIsGoogleLoading] = useState(false)
const [isMicrosoftLoading, setIsMicrosoftLoading] = useState(false)
async function signInWithGithub() {
if (!githubAvailable) return
setIsGithubLoading(true)
try {
await client.signIn.social({ provider: 'github', callbackURL })
} catch (err) {
logger.error('GitHub sign-in failed', { error: getErrorMessage(err) })
} finally {
setIsGithubLoading(false)
}
}
async function signInWithGoogle() {
if (!googleAvailable) return
setIsGoogleLoading(true)
try {
await client.signIn.social({ provider: 'google', callbackURL })
} catch (err) {
logger.error('Google sign-in failed', { error: getErrorMessage(err) })
} finally {
setIsGoogleLoading(false)
}
}
async function signInWithMicrosoft() {
if (!microsoftAvailable) return
setIsMicrosoftLoading(true)
try {
await client.signIn.social({ provider: 'microsoft', callbackURL })
} catch (err) {
logger.error('Microsoft sign-in failed', { error: getErrorMessage(err) })
} finally {
setIsMicrosoftLoading(false)
}
}
const githubButton = (
<Chip
fullWidth
flush
leftIcon={GithubIcon}
className={cn(AUTH_BUTTON_CLASS, 'border border-[var(--border-1)]')}
disabled={!githubAvailable || isGithubLoading}
onClick={signInWithGithub}
>
{isGithubLoading ? 'Connecting…' : 'GitHub'}
</Chip>
)
const googleButton = (
<Chip
fullWidth
flush
leftIcon={GoogleIcon}
className={cn(AUTH_BUTTON_CLASS, 'border border-[var(--border-1)]')}
disabled={!googleAvailable || isGoogleLoading}
onClick={signInWithGoogle}
>
{isGoogleLoading ? 'Connecting…' : 'Google'}
</Chip>
)
const microsoftButton = (
<Chip
fullWidth
flush
leftIcon={MicrosoftIcon}
className={cn(AUTH_BUTTON_CLASS, 'border border-[var(--border-1)]')}
disabled={!microsoftAvailable || isMicrosoftLoading}
onClick={signInWithMicrosoft}
>
{isMicrosoftLoading ? 'Connecting…' : 'Microsoft'}
</Chip>
)
const hasAnyOAuthProvider = githubAvailable || googleAvailable || microsoftAvailable
if (!hasAnyOAuthProvider && !children) {
return null
}
return (
<div className='grid gap-3'>
{googleAvailable && googleButton}
{microsoftAvailable && microsoftButton}
{githubAvailable && githubButton}
{children}
</div>
)
}
@@ -0,0 +1,44 @@
'use client'
import { Chip, cn } from '@sim/emcn'
import { useRouter } from 'next/navigation'
import { getEnv, isTruthy } from '@/lib/core/config/env'
import { AUTH_BUTTON_CLASS } from '@/app/(auth)/components/constants'
interface SSOLoginButtonProps {
callbackURL?: string
className?: string
variant?: 'primary' | 'outline'
}
export function SSOLoginButton({
callbackURL,
className,
variant = 'outline',
}: SSOLoginButtonProps) {
const router = useRouter()
if (!isTruthy(getEnv('NEXT_PUBLIC_SSO_ENABLED'))) {
return null
}
const handleSSOClick = () => {
const ssoUrl = `/sso${callbackURL ? `?callbackUrl=${encodeURIComponent(callbackURL)}` : ''}`
router.push(ssoUrl)
}
return (
<Chip
variant={variant === 'primary' ? 'primary' : undefined}
fullWidth
flush
onClick={handleSSOClick}
className={cn(
AUTH_BUTTON_CLASS,
variant === 'outline' && 'border border-[var(--border-1)]',
className
)}
>
Sign in with SSO
</Chip>
)
}
@@ -0,0 +1,39 @@
'use client'
import { cn } from '@sim/emcn'
import { useBrandConfig } from '@/ee/whitelabeling'
export interface SupportFooterProps {
/**
* `fixed`/`absolute` pin the footer over the page (short, centered forms
* only — content must never render underneath it). `static` renders it in
* normal document flow after the content, which is required for pages with
* unbounded content height (e.g. the resume gate's HITL form): an
* absolutely-positioned footer with no reserved space is not pushed down by
* flow content, so it silently overlaps and eats clicks on whatever content
* ends up in its footprint.
*/
position?: 'fixed' | 'absolute' | 'static'
}
export function SupportFooter({ position = 'fixed' }: SupportFooterProps) {
const brandConfig = useBrandConfig()
return (
<div
className={cn(
'pb-8 text-center text-[var(--text-muted)] text-caption leading-relaxed',
position !== 'static' && 'right-0 bottom-0 left-0 z-50',
position
)}
>
Need help?{' '}
<a
href={`mailto:${brandConfig.supportEmail}`}
className='text-[var(--text-muted)] underline-offset-4 transition-colors hover:text-[var(--text-body)] hover:underline'
>
Contact support
</a>
</div>
)
}