项目文件夹

文件
wehub-resource-sync d25d482dc2
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
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

300 行
11 KiB
TypeScript

import type { CSSProperties } from 'react'
import { cn } from '@sim/emcn'
import { ThinkingLoader } from '@/components/ui'
import { FeatureGraphicShell } from '@/app/(landing)/enterprise/components/feature-graphics/feature-graphic-shell'
import styles from '@/app/(landing)/enterprise/components/feature-graphics/operations-teams-graphic.module.css'
/**
* Fixed pixel canvas the switchboard is drawn on, centered inside the
* shell. Kept to 280px wide (chips reaching only x 32–250) so every chip
* stays inside the tile's visible area even at the narrowest three-up
* tile width (~274px visible at 1024), and 248px tall — the tallest
* canvas that clears the slot's shortest inner box (~257px at 1024 after
* bottom-bleed compensation) so the staggered top pills never clip while
* the wires keep long vertical runs.
*/
const CANVAS = { WIDTH: 280, HEIGHT: 248 } as const
/**
* The ThinkingLoader's light-grey material (its dark-surface theme from
* `thinking-loader.module.css`), asserted inline exactly as the deploy
* tile's Deploy button does — the always-light landing would otherwise
* ink the loader dark, invisible on the dark tile ground showing through
* the outlined router hub.
*/
const ROUTER_LOADER_INK = {
'--tl-grad-inner': '#a7a7a7',
'--tl-grad-outer': '#d6d6d6',
'--tl-glow': 'rgba(255, 255, 255, 0.9)',
} as CSSProperties
interface Port {
/** Tailwind classes positioning the size-2 port dot, centered on the port's canvas x/y. */
dotClass: string
/** Tailwind classes placing the tag's center on the port's canvas x, above/below its dot. */
chipClass: string
/** Tool name rendered as an outlined tag beside the port. */
label: string
/** CSS-module animation class driving this tag's white connect-fill on its scheduled phases. */
fillClass: string
}
/**
* Inbound ops tools across the top, staggered like a constellation rather
* than a rank: Zendesk rides highest on the center axis (x 140), Gmail
* sits lowest at the left (x 56), Sheets between at the right (x 224).
*/
const SOURCES: readonly Port[] = [
{
dotClass: 'top-[48px] left-[52px]',
chipClass: 'top-[32px] left-[56px]',
label: 'Gmail',
fillClass: styles.fillSrcC,
},
{
dotClass: 'top-[28px] left-[136px]',
chipClass: 'top-[12px] left-[140px]',
label: 'Zendesk',
fillClass: styles.fillSrcA,
},
{
dotClass: 'top-[38px] left-[220px]',
chipClass: 'top-[22px] left-[224px]',
label: 'Sheets',
fillClass: styles.fillSrcB,
},
] as const
/**
* Outbound destinations mirrored below, staggered the same way: Slack at
* the left (x 56), Salesforce hanging lowest on the center axis (x 140),
* Jira highest at the right (x 224).
*/
const DESTINATIONS: readonly Port[] = [
{
dotClass: 'top-[192px] left-[52px]',
chipClass: 'top-[216px] left-[56px]',
label: 'Slack',
fillClass: styles.fillDstA,
},
{
dotClass: 'top-[212px] left-[136px]',
chipClass: 'top-[236px] left-[140px]',
label: 'Salesforce',
fillClass: styles.fillDstC,
},
{
dotClass: 'top-[202px] left-[220px]',
chipClass: 'top-[226px] left-[224px]',
label: 'Jira',
fillClass: styles.fillDstB,
},
] as const
/**
* Wires from each source port gathering into the router hub's top pole
* (140, 93) — all three terminate at the same point so they read as a
* tied bundle feeding the agent, with vertical tangents at both ends
* (the access tile's edge geometry) and long vertical runs so each curve
* travels before it arrives.
*/
const IN_PATHS = {
gmail: 'M 56 52 C 56 74 140 66 140 93',
zendesk: 'M 140 32 L 140 93',
sheets: 'M 224 42 C 224 70 140 64 140 93',
} as const
/**
* Wires fanning out from the router hub's bottom pole (140, 155) — the
* shared origin splitting to each destination, echoing the deploy tile's
* single pipeline branching.
*/
const OUT_PATHS = {
slack: 'M 140 155 C 140 182 56 174 56 196',
salesforce: 'M 140 155 L 140 216',
jira: 'M 140 155 C 140 184 224 178 224 206',
} as const
/** Faint-ink stroke for the resting wires (the deploy tile's guide-line grey, quieter). */
const QUIET_STROKE = 'color-mix(in srgb, var(--text-muted-inverse) 28%, transparent)'
/** Shared 1px outline ink for the tags, port dots, and router hub ring. */
const OUTLINE_INK = 'border-[color:color-mix(in_srgb,var(--text-muted-inverse)_45%,transparent)]'
/** Shared SVG props for a resting wire. */
const WIRE_PROPS = { stroke: QUIET_STROKE, strokeWidth: '1' } as const
/** Shared SVG props for a traveling white request-pulse overlay on a wire. */
const PULSE_PROPS = {
pathLength: 1,
stroke: 'var(--text-inverse)',
strokeWidth: '1.25',
strokeLinecap: 'round',
} as const
/** An outlined integration tag whose white connect-fill crossfades in on its scheduled phases. */
function PortTag({ port }: { port: Port }) {
return (
<span
className={cn('-translate-x-1/2 -translate-y-1/2 absolute whitespace-nowrap', port.chipClass)}
>
<span
className={cn(
'relative flex h-5 items-center overflow-hidden rounded-md border bg-transparent px-1.5 font-medium text-[var(--text-muted-inverse)] text-caption',
OUTLINE_INK
)}
>
{port.label}
<span
className={cn(
'absolute inset-0 flex items-center bg-[var(--white)] px-1.5 text-[var(--text-primary)] opacity-0',
port.fillClass
)}
>
{port.label}
</span>
</span>
</span>
)
}
/**
* Operations automation told as a vertical switchboard — the access tile's
* top-to-bottom composition (sources above, curved bezier edges flowing
* down to what they feed) in the dark tiles' ink: three ops tools wired in
* across the top (Gmail, Zendesk, Sheets), an outlined circular router hub
* at center — the agent — and three destinations wired out below (Slack,
* Salesforce, Jira). Both rows stagger their tag heights into a loose
* constellation, giving each wire its own long arc instead of ranking the
* tags into flat rows. Every element is drawn in the same outlined
* language: the six tool tags rest as transparent pills with 1px light
* hairline borders and light-grey ink, the hub is a transparent circle
* with the same hairline ring, and the wires are 1px curved SVG paths
* with vertical tangents (the access tile's edge geometry) gathering into
* the hub's top pole and fanning from its bottom pole — a tied bundle in,
* a split out.
*
* Inside the hub, the platform's gooey {@link ThinkingLoader} runs its
* default full morph cycle (no pinned variant, unlike the deploy button's
* `relay`) wearing the deploy button's exact light-grey ink override, so
* the page's two dark-tile loaders read as the same material — the agent
* visibly churning through work between dispatches. The loader stops
* cycling on its own under `prefers-reduced-motion`.
*
* Motion (from `operations-teams-graphic.module.css`, one shared 16s
* timeline of four 4s route phases — Zendesk→Slack, Sheets→Jira,
* Gmail→Salesforce, Zendesk→Jira — a precomputed shuffle so the routing
* feels varied): in each phase the source tag's white filled state
* crossfades in over its outline (ink flipping dark with it), a white
* pulse falls down its wire into the router — which blooms the family's
* ring pulse while the agent classifies — then out the bottom pole to
* the destination tag, whose fill crossfades in as the route connects.
* Both ends hold briefly, fade back to outlined, and the next pair
* begins. Everything is static and outlined under
* `prefers-reduced-motion`.
*
* The feature tile's visual slot bleeds `2rem` right and bottom (`1.5rem`
* under `max-lg`) but not left or top, so the canvas sits inside a
* matching right- and bottom-padded box to land on the tile's visible
* center. The fixed-size canvas is centered with a transform (`left-1/2`
* + `-translate-x-1/2`, the standards tile's approach) so at narrow tile
* widths it keeps its wiring geometry and overflows both edges equally
* instead of drifting. On the narrow grid bands where the tile drops
* below the canvas width (small two-column screens and the 3-up row just
* past `lg`) the canvas scales down via transform so the outer tool pills
* are never cropped.
*/
export function OperationsTeamsGraphic() {
return (
<FeatureGraphicShell>
<div aria-hidden='true' className='absolute inset-0 pr-8 pb-8 max-lg:pr-6 max-lg:pb-6'>
<div className='relative h-full'>
<div className='-translate-x-1/2 -translate-y-1/2 absolute top-1/2 left-1/2 h-[248px] w-[280px] max-sm:scale-100 max-md:scale-[0.85] lg:max-[1180px]:scale-[0.85]'>
<svg
className='absolute inset-0'
fill='none'
viewBox={`0 0 ${CANVAS.WIDTH} ${CANVAS.HEIGHT}`}
width={CANVAS.WIDTH}
height={CANVAS.HEIGHT}
>
<path d={IN_PATHS.gmail} {...WIRE_PROPS} />
<path d={IN_PATHS.zendesk} {...WIRE_PROPS} />
<path d={IN_PATHS.sheets} {...WIRE_PROPS} />
<path d={OUT_PATHS.slack} {...WIRE_PROPS} />
<path d={OUT_PATHS.salesforce} {...WIRE_PROPS} />
<path d={OUT_PATHS.jira} {...WIRE_PROPS} />
<path
d={IN_PATHS.zendesk}
className={cn(styles.pulse, styles.pulseInA)}
{...PULSE_PROPS}
/>
<path
d={IN_PATHS.sheets}
className={cn(styles.pulse, styles.pulseInB)}
{...PULSE_PROPS}
/>
<path
d={IN_PATHS.gmail}
className={cn(styles.pulse, styles.pulseInC)}
{...PULSE_PROPS}
/>
<path
d={OUT_PATHS.slack}
className={cn(styles.pulse, styles.pulseOutA)}
{...PULSE_PROPS}
/>
<path
d={OUT_PATHS.jira}
className={cn(styles.pulse, styles.pulseOutB)}
{...PULSE_PROPS}
/>
<path
d={OUT_PATHS.salesforce}
className={cn(styles.pulse, styles.pulseOutC)}
{...PULSE_PROPS}
/>
</svg>
{SOURCES.map((port) => (
<span key={port.label}>
<span
className={cn(
'absolute size-2 rounded-full border bg-[var(--text-secondary)]',
OUTLINE_INK,
port.dotClass
)}
/>
<PortTag port={port} />
</span>
))}
{DESTINATIONS.map((port) => (
<span key={port.label}>
<span
className={cn(
'absolute size-2 rounded-full border bg-[var(--text-secondary)]',
OUTLINE_INK,
port.dotClass
)}
/>
<PortTag port={port} />
</span>
))}
<div
className={cn(
'absolute top-[93px] left-[109px] flex size-[62px] items-center justify-center rounded-full border',
OUTLINE_INK,
styles.routerBloom
)}
>
<ThinkingLoader size={36} style={ROUTER_LOADER_INK} />
</div>
</div>
</div>
</div>
</FeatureGraphicShell>
)
}