chore: website

This commit is contained in:
Nathan Flurry 2026-01-28 01:44:19 -08:00
parent d1cbd20b83
commit 745c64149e
46 changed files with 2173 additions and 12 deletions

BIN
agent-diagram.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

View file

@ -0,0 +1 @@
export default new Map();

View file

@ -0,0 +1 @@
export default new Map();

View file

@ -0,0 +1,199 @@
declare module 'astro:content' {
export interface RenderResult {
Content: import('astro/runtime/server/index.js').AstroComponentFactory;
headings: import('astro').MarkdownHeading[];
remarkPluginFrontmatter: Record<string, any>;
}
interface Render {
'.md': Promise<RenderResult>;
}
export interface RenderedContent {
html: string;
metadata?: {
imagePaths: Array<string>;
[key: string]: unknown;
};
}
}
declare module 'astro:content' {
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
export type CollectionKey = keyof AnyEntryMap;
export type CollectionEntry<C extends CollectionKey> = Flatten<AnyEntryMap[C]>;
export type ContentCollectionKey = keyof ContentEntryMap;
export type DataCollectionKey = keyof DataEntryMap;
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
type ValidContentEntrySlug<C extends keyof ContentEntryMap> = AllValuesOf<
ContentEntryMap[C]
>['slug'];
export type ReferenceDataEntry<
C extends CollectionKey,
E extends keyof DataEntryMap[C] = string,
> = {
collection: C;
id: E;
};
export type ReferenceContentEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}) = string,
> = {
collection: C;
slug: E;
};
export type ReferenceLiveEntry<C extends keyof LiveContentConfig['collections']> = {
collection: C;
id: string;
};
/** @deprecated Use `getEntry` instead. */
export function getEntryBySlug<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
collection: C,
// Note that this has to accept a regular string too, for SSR
entrySlug: E,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
/** @deprecated Use `getEntry` instead. */
export function getDataEntryById<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C]>(
collection: C,
entryId: E,
): Promise<CollectionEntry<C>>;
export function getCollection<C extends keyof AnyEntryMap, E extends CollectionEntry<C>>(
collection: C,
filter?: (entry: CollectionEntry<C>) => entry is E,
): Promise<E[]>;
export function getCollection<C extends keyof AnyEntryMap>(
collection: C,
filter?: (entry: CollectionEntry<C>) => unknown,
): Promise<CollectionEntry<C>[]>;
export function getLiveCollection<C extends keyof LiveContentConfig['collections']>(
collection: C,
filter?: LiveLoaderCollectionFilterType<C>,
): Promise<
import('astro').LiveDataCollectionResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>
>;
export function getEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
entry: ReferenceContentEntry<C, E>,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof DataEntryMap,
E extends keyof DataEntryMap[C] | (string & {}),
>(
entry: ReferenceDataEntry<C, E>,
): E extends keyof DataEntryMap[C]
? Promise<DataEntryMap[C][E]>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof ContentEntryMap,
E extends ValidContentEntrySlug<C> | (string & {}),
>(
collection: C,
slug: E,
): E extends ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof DataEntryMap,
E extends keyof DataEntryMap[C] | (string & {}),
>(
collection: C,
id: E,
): E extends keyof DataEntryMap[C]
? string extends keyof DataEntryMap[C]
? Promise<DataEntryMap[C][E]> | undefined
: Promise<DataEntryMap[C][E]>
: Promise<CollectionEntry<C> | undefined>;
export function getLiveEntry<C extends keyof LiveContentConfig['collections']>(
collection: C,
filter: string | LiveLoaderEntryFilterType<C>,
): Promise<import('astro').LiveDataEntryResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>>;
/** Resolve an array of entry references from the same collection */
export function getEntries<C extends keyof ContentEntryMap>(
entries: ReferenceContentEntry<C, ValidContentEntrySlug<C>>[],
): Promise<CollectionEntry<C>[]>;
export function getEntries<C extends keyof DataEntryMap>(
entries: ReferenceDataEntry<C, keyof DataEntryMap[C]>[],
): Promise<CollectionEntry<C>[]>;
export function render<C extends keyof AnyEntryMap>(
entry: AnyEntryMap[C][string],
): Promise<RenderResult>;
export function reference<C extends keyof AnyEntryMap>(
collection: C,
): import('astro/zod').ZodEffects<
import('astro/zod').ZodString,
C extends keyof ContentEntryMap
? ReferenceContentEntry<C, ValidContentEntrySlug<C>>
: ReferenceDataEntry<C, keyof DataEntryMap[C]>
>;
// Allow generic `string` to avoid excessive type errors in the config
// if `dev` is not running to update as you edit.
// Invalid collection names will be caught at build time.
export function reference<C extends string>(
collection: C,
): import('astro/zod').ZodEffects<import('astro/zod').ZodString, never>;
type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
type InferEntrySchema<C extends keyof AnyEntryMap> = import('astro/zod').infer<
ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
>;
type ContentEntryMap = {
};
type DataEntryMap = {
};
type AnyEntryMap = ContentEntryMap & DataEntryMap;
type ExtractLoaderTypes<T> = T extends import('astro/loaders').LiveLoader<
infer TData,
infer TEntryFilter,
infer TCollectionFilter,
infer TError
>
? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError }
: { data: never; entryFilter: never; collectionFilter: never; error: never };
type ExtractDataType<T> = ExtractLoaderTypes<T>['data'];
type ExtractEntryFilterType<T> = ExtractLoaderTypes<T>['entryFilter'];
type ExtractCollectionFilterType<T> = ExtractLoaderTypes<T>['collectionFilter'];
type ExtractErrorType<T> = ExtractLoaderTypes<T>['error'];
type LiveLoaderDataType<C extends keyof LiveContentConfig['collections']> =
LiveContentConfig['collections'][C]['schema'] extends undefined
? ExtractDataType<LiveContentConfig['collections'][C]['loader']>
: import('astro/zod').infer<
Exclude<LiveContentConfig['collections'][C]['schema'], undefined>
>;
type LiveLoaderEntryFilterType<C extends keyof LiveContentConfig['collections']> =
ExtractEntryFilterType<LiveContentConfig['collections'][C]['loader']>;
type LiveLoaderCollectionFilterType<C extends keyof LiveContentConfig['collections']> =
ExtractCollectionFilterType<LiveContentConfig['collections'][C]['loader']>;
type LiveLoaderErrorType<C extends keyof LiveContentConfig['collections']> = ExtractErrorType<
LiveContentConfig['collections'][C]['loader']
>;
export type ContentConfig = typeof import("../src/content.config.mjs");
export type LiveContentConfig = never;
}

View file

@ -0,0 +1 @@
[["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.16.15","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[]},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false,\"svgo\":false},\"legacy\":{\"collections\":false}}"]

View file

@ -0,0 +1,5 @@
{
"_variables": {
"lastUpdateCheck": 1769576078224
}
}

View file

@ -0,0 +1 @@
/// <reference types="astro/client" />

View file

@ -2,21 +2,20 @@ FROM node:22-alpine AS build
WORKDIR /app
RUN npm install -g pnpm
# Copy package files for all workspaces
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY frontend/packages/website/package.json ./frontend/packages/website/
# Copy website package
COPY frontend/packages/website/package.json ./
# Install dependencies
RUN pnpm install --filter @sandbox-agent/website...
RUN pnpm install
# Copy website source
COPY frontend/packages/website ./frontend/packages/website
COPY frontend/packages/website/ .
# Build
RUN pnpm --filter @sandbox-agent/website build
RUN pnpm build
FROM caddy:alpine
COPY --from=build /app/frontend/packages/website/dist /srv
COPY --from=build /app/dist /srv
RUN cat > /etc/caddy/Caddyfile <<'EOF'
:80 {
root * /srv

View file

@ -0,0 +1,11 @@
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import tailwind from '@astrojs/tailwind';
export default defineConfig({
output: 'static',
integrations: [
react(),
tailwind()
]
});

View file

@ -1,15 +1,29 @@
{
"name": "@sandbox-agent/website",
"private": true,
"version": "0.0.0",
"version": "0.0.1",
"license": "Apache-2.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/react": "^4.2.0",
"@astrojs/tailwind": "^6.0.0",
"astro": "^5.1.0",
"framer-motion": "^12.0.0",
"lucide-react": "^0.469.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwindcss": "^3.4.0"
},
"devDependencies": {
"vite": "^5.4.7"
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"typescript": "^5.7.0"
}
}

View file

@ -0,0 +1 @@
<svg width="128" height="128" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" width="126" height="126" rx="44" fill="#0F0F0F"/><rect x="18.25" y="18.25" width="91.5" height="91.5" rx="25.75" stroke="#F0F0F0" stroke-width="8.5"/><path fill-rule="evenodd" clip-rule="evenodd" d="M57.694 43.098c0-.622-.505-1.126-1.127-1.126h-8.444a5.114 5.114 0 0 0-5.112 5.111v33.824a5.114 5.114 0 0 0 5.112 5.112h8.444c.622 0 1.127-.505 1.127-1.127V43.098Zm24.424 27.869c-1.238-2.222-4.047-4.026-6.27-4.026H62.923c-.684 0-.93.555-.549 1.239l7.703 13.822c1.239 2.223 4.048 4.026 6.27 4.026h12.927c.683 0 .93-.555.548-1.239l-7.703-13.822Zm.538-18.718c0-5.672-4.605-10.277-10.277-10.277H63.31a1.21 1.21 0 0 0-1.209 1.209v18.137c0 .667.542 1.209 1.21 1.209h9.068c5.672 0 10.277-4.605 10.277-10.278Z" fill="#F0F0F0"/></svg>

After

Width:  |  Height:  |  Size: 818 B

View file

@ -0,0 +1 @@
<svg viewBox="0 0 281 124" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M236.014 0C260.431 9.71106e-05 280.602 17.4115 280.603 44.7432C280.602 73.5337 260.065 94.1657 233.52 94.166C224.158 94.166 215.639 92.4222 208.63 88.4902C202.886 85.2698 198.203 80.6054 194.919 74.3379L188.115 121.822L187.946 123.016H174.214L174.448 121.423L191.772 2.49414H205.372L203.937 11.3369C212.143 3.86078 223.2 0.000153635 236.014 0ZM47.082 0.154297C56.4435 0.154297 65.0012 1.8991 72.0488 5.84863C77.8222 9.08305 82.5323 13.7713 85.8271 20.085L88.1201 3.69238L88.2861 2.49316H101.863L89.1611 90.6328L88.9873 91.8262H75.4092L76.7227 82.8555C68.5854 90.4564 57.3981 94.3231 44.5889 94.3232C20.1709 94.3232 0.000167223 76.9087 0 49.5771C0.000149745 20.7854 20.54 0.154871 47.082 0.154297ZM116.234 90.6357L116.061 91.8271H102.485L115.351 3.68555L115.521 2.49414H129.083L116.234 90.6357ZM140.673 90.6357L140.499 91.8271H126.924L139.789 3.68555L139.96 2.49414H153.521L140.673 90.6357ZM177.958 2.49414L165.108 90.6357L164.935 91.8271H151.36L164.225 3.68555L164.396 2.49414H177.958ZM48.4854 11.9844C27.8638 11.985 14.0133 28.3799 14.0127 48.9521C14.0127 57.7907 16.8094 66.1771 22.3145 72.334C27.7973 78.4657 36.0631 82.4932 47.2402 82.4932C67.8534 82.4925 81.7122 65.9487 81.7129 45.3682C81.7129 35.4076 78.2493 27.0792 72.4131 21.2441C66.5794 15.4088 58.2871 11.9844 48.4854 11.9844ZM233.362 11.8291C212.749 11.8297 198.89 28.3716 198.89 48.9521C198.89 58.9123 202.356 67.2403 208.189 73.0742C214.023 78.9107 222.315 82.3358 232.116 82.3359C252.738 82.3355 266.589 65.9407 266.59 45.3682C266.59 36.5296 263.795 28.1424 258.29 21.9863C252.807 15.8551 244.542 11.8291 233.362 11.8291Z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by Pixelmator Pro 3.6.17 -->
<svg width="1200" height="1200" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg">
<g id="g314">
<path id="path147" fill="#d97757" stroke="none" d="M 233.959793 800.214905 L 468.644287 668.536987 L 472.590637 657.100647 L 468.644287 650.738403 L 457.208069 650.738403 L 417.986633 648.322144 L 283.892639 644.69812 L 167.597321 639.865845 L 54.926208 633.825623 L 26.577238 627.785339 L 3.3e-05 592.751709 L 2.73832 575.27533 L 26.577238 559.248352 L 60.724873 562.228149 L 136.187973 567.382629 L 249.422867 575.194763 L 331.570496 580.026978 L 453.261841 592.671082 L 472.590637 592.671082 L 475.328857 584.859009 L 468.724915 580.026978 L 463.570557 575.194763 L 346.389313 495.785217 L 219.543671 411.865906 L 153.100723 363.543762 L 117.181267 339.060425 L 99.060455 316.107361 L 91.248367 266.01355 L 123.865784 230.093994 L 167.677887 233.073853 L 178.872513 236.053772 L 223.248367 270.201477 L 318.040283 343.570496 L 441.825592 434.738342 L 459.946411 449.798706 L 467.194672 444.64447 L 468.080597 441.020203 L 459.946411 427.409485 L 392.617493 305.718323 L 320.778564 181.932983 L 288.80542 130.630859 L 280.348999 99.865845 C 277.369171 87.221436 275.194641 76.590698 275.194641 63.624268 L 312.322174 13.20813 L 332.8591 6.604126 L 382.389313 13.20813 L 403.248352 31.328979 L 434.013519 101.71814 L 483.865753 212.537048 L 561.181274 363.221497 L 583.812134 407.919434 L 595.892639 449.315491 L 600.40271 461.959839 L 608.214783 461.959839 L 608.214783 454.711609 L 614.577271 369.825623 L 626.335632 265.61084 L 637.771851 131.516846 L 641.718201 93.745117 L 660.402832 48.483276 L 697.530334 24.000122 L 726.52356 37.852417 L 750.362549 72 L 747.060486 94.067139 L 732.886047 186.201416 L 705.100708 330.52356 L 686.979919 427.167847 L 697.530334 427.167847 L 709.61084 415.087341 L 758.496704 350.174561 L 840.644348 247.490051 L 876.885925 206.738342 L 919.167847 161.71814 L 946.308838 140.29541 L 997.61084 140.29541 L 1035.38269 196.429626 L 1018.469849 254.416199 L 965.637634 321.422852 L 921.825562 378.201538 L 859.006714 462.765259 L 819.785278 530.41626 L 823.409424 535.812073 L 832.75177 534.92627 L 974.657776 504.724915 L 1051.328979 490.872559 L 1142.818848 475.167786 L 1184.214844 494.496582 L 1188.724854 514.147644 L 1172.456421 554.335693 L 1074.604126 578.496765 L 959.838989 601.449829 L 788.939636 641.879272 L 786.845764 643.409485 L 789.261841 646.389343 L 866.255127 653.637634 L 899.194702 655.409424 L 979.812134 655.409424 L 1129.932861 666.604187 L 1169.154419 692.537109 L 1192.671265 724.268677 L 1188.724854 748.429688 L 1128.322144 779.194641 L 1046.818848 759.865845 L 856.590759 714.604126 L 791.355774 698.335754 L 782.335693 698.335754 L 782.335693 703.731567 L 836.69812 756.885986 L 936.322205 846.845581 L 1061.073975 962.81897 L 1067.436279 991.490112 L 1051.409424 1014.120911 L 1034.496704 1011.704712 L 924.885986 929.234924 L 882.604126 892.107544 L 786.845764 811.48999 L 780.483276 811.48999 L 780.483276 819.946289 L 802.550415 852.241699 L 919.087341 1027.409424 L 925.127625 1081.127686 L 916.671204 1098.604126 L 886.469849 1109.154419 L 853.288696 1103.114136 L 785.073914 1007.355835 L 714.684631 899.516785 L 657.906067 802.872498 L 650.979858 806.81897 L 617.476624 1167.704834 L 601.771851 1186.147705 L 565.530212 1200 L 535.328857 1177.046997 L 519.302124 1139.919556 L 535.328857 1066.550537 L 554.657776 970.792053 L 570.362488 894.68457 L 584.536926 800.134277 L 592.993347 768.724976 L 592.429626 766.630859 L 585.503479 767.516968 L 514.22821 865.369263 L 405.825531 1011.865906 L 320.053711 1103.677979 L 299.516815 1111.812256 L 263.919525 1093.369263 L 267.221497 1060.429688 L 287.114136 1031.114136 L 405.825531 880.107361 L 477.422913 786.52356 L 523.651062 732.483276 L 523.328918 724.671265 L 520.590698 724.671265 L 205.288605 929.395935 L 149.154434 936.644409 L 124.993355 914.01355 L 127.973183 876.885986 L 139.409409 864.80542 L 234.201385 799.570435 L 233.879227 799.8927 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View file

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="90 125 270 70" fill="none">
<path fill="currentColor" d="m126.103 130-15.679 15.678v-6.703h-6.335v15.31h6.335v-8.607l4.48 4.48 15.678-15.679-4.479-4.479Zm-4.592 13.726h21.117v6.335h-21.117v-6.335ZM96.7 165.899h9.244L94 153.955l4.48-4.48 12.041 12.041-4.383 4.383h9.04v6.335H96.701v-6.335Zm16.967 17.172 12.597-12.597v9.679h6.335v-19.006h-6.335v9.136l-4.384-4.384-12.692 12.692 4.479 4.48Z"/>
<path fill="currentColor" d="m126.263 154.541 14.185 14.185 4.48-4.479-14.185-14.186-4.48 4.48Z"/>
<path fill="currentColor" fill-rule="evenodd" d="M168.882 176.693h9.245c5.23.08 9.161-.212 11.956-1.414 2.834-1.243 4.831-3.308 5.988-6.194 1.198-2.887 1.797-7.056 1.797-12.508 0-5.052-.499-9.243-1.497-12.089-.998-2.847-2.695-4.952-5.091-6.315-2.355-1.403-5.709-2.325-10.061-2.766-1.996-.2-3.932-.3-5.809-.3a152.116 152.116 0 0 0-4.093-.01c-.784.005-1.598.009-2.435.01v41.586Zm18.266-7.187c-1.437.922-3.433 1.463-5.989 1.623-.758.08-1.577.1-2.455.06-.838-.04-1.897-.1-3.174-.18v-29.887c1.677 0 3.513.1 5.51.3 2.675.321 4.731.963 6.168 1.925 1.437.962 2.455 2.445 3.054 4.45.599 1.964.899 4.73.899 8.298 0 3.769-.32 6.716-.959 8.84-.598 2.085-1.617 3.608-3.054 4.571Zm16.454 6.675c1.518.561 3.654.841 6.408.841 2.436 0 4.292-.3 5.57-.902 1.278-.641 2.455-1.804 3.533-3.487h.479v4.091h5.869v-21.29c0-2.927-.379-5.172-1.137-6.735-.759-1.604-2.057-2.726-3.893-3.368-1.797-.681-4.352-1.022-7.666-1.022-3.274 0-5.809.301-7.606.902-1.756.561-2.994 1.543-3.713 2.947-.718 1.363-1.2 3.332-1.2 5.893h6.65c0-1.684.399-2.826 1.198-3.428.838-.601 2.395-.902 4.671-.902 1.717 0 3.055.181 4.013.541.958.361 1.637.942 2.036 1.744s.599 1.945.599 3.428v.842h-5.091c-3.872 0-6.867.341-8.983 1.022-2.116.642-3.613 1.784-4.492 3.428-.878 1.603-1.317 3.869-1.317 6.795 0 2.486.299 4.41.898 5.773.639 1.363 1.697 2.325 3.174 2.887Zm12.517-5.593c-1.118.601-2.735.902-4.851.902-2.076 0-3.493-.321-4.252-.962-.759-.642-1.138-1.824-1.138-3.548 0-1.403.22-2.466.659-3.187.439-.762 1.158-1.303 2.156-1.624 1.038-.321 2.495-.501 4.372-.541l6.288-.06c-.08 2.606-.359 4.61-.839 6.013-.479 1.403-1.277 2.406-2.395 3.007Zm71.836 6.434c-3.753 0-6.647-.481-8.684-1.443-2.036-.962-3.473-2.606-4.311-4.931-.839-2.325-1.258-5.632-1.258-9.922 0-4.33.419-7.657 1.258-9.983.838-2.325 2.275-3.968 4.311-4.931 2.037-1.002 4.931-1.503 8.684-1.503 3.753 0 6.628.501 8.624 1.503 2.036.963 3.474 2.606 4.312 4.931.878 2.326 1.318 5.653 1.318 9.983 0 4.29-.44 7.597-1.318 9.922-.838 2.285-2.276 3.929-4.312 4.931-2.036.962-4.911 1.443-8.624 1.443Zm0-5.532c2.076 0 3.653-.321 4.731-.962 1.118-.642 1.917-1.724 2.396-3.247.479-1.524.719-3.709.719-6.555 0-2.887-.24-5.092-.719-6.615-.479-1.564-1.278-2.666-2.396-3.307-1.078-.682-2.655-1.023-4.731-1.023s-3.673.341-4.791 1.023c-1.118.641-1.916 1.743-2.395 3.307-.479 1.523-.719 3.728-.719 6.615 0 2.846.24 5.031.719 6.555.479 1.523 1.277 2.605 2.395 3.247 1.118.641 2.715.962 4.791.962Z" clip-rule="evenodd"/>
<path fill="currentColor" d="M304.407 144.606h6.408v4.935h.479c.998-2.045 2.116-3.428 3.353-4.15 1.278-.721 3.115-1.082 5.51-1.082 2.875 0 5.091.381 6.648 1.143 1.557.721 2.675 1.944 3.353 3.668.679 1.724 1.019 4.189 1.019 7.396v20.208h-6.408v-20.328c0-1.644-.18-2.926-.539-3.849-.36-.922-.979-1.563-1.857-1.924s-2.136-.541-3.773-.541c-2.036 0-3.613.301-4.731.902-1.078.601-1.857 1.624-2.336 3.067-.479 1.443-.718 3.488-.718 6.134v16.539h-6.408v-32.118Z"/>
<path fill="currentColor" fill-rule="evenodd" d="M344.149 177.022c-2.755 0-4.891-.28-6.408-.841-1.477-.562-2.535-1.524-3.174-2.887-.599-1.363-.899-3.287-.899-5.773 0-2.926.44-5.192 1.318-6.795.878-1.644 2.376-2.786 4.492-3.428 2.116-.681 5.11-1.022 8.983-1.022h5.09v-.842c0-1.483-.199-2.626-.599-3.428-.399-.802-1.078-1.383-2.036-1.744-.958-.36-2.295-.541-4.012-.541-2.276 0-3.833.301-4.672.902-.798.602-1.197 1.744-1.197 3.428h-6.517c0-2.804.348-4.53 1.067-5.893.719-1.404 1.956-2.386 3.713-2.947 1.797-.601 4.332-.902 7.606-.902 3.314 0 5.869.341 7.665 1.022 1.837.642 3.135 1.764 3.893 3.368.759 1.563 1.138 3.808 1.138 6.735v21.29h-5.869v-4.091h-.479c-1.078 1.683-2.256 2.846-3.534 3.487-1.277.602-3.134.902-5.569.902Zm1.258-5.532c2.116 0 3.733-.301 4.85-.902 1.118-.601 1.917-1.604 2.396-3.007.479-1.403.759-3.407.838-6.013l-6.288.06c-1.876.04-3.334.22-4.372.541-.998.321-1.716.862-2.156 1.624-.439.721-.658 1.784-.658 3.187 0 1.724.379 2.906 1.137 3.548.759.641 2.176.962 4.253.962Z" clip-rule="evenodd"/>
<path fill="currentColor" d="M253.349 144.54h-6.169l-6.058 26.414h-2.205l-5.816-26.414h-6.058l5.147 26.192a7.27 7.27 0 0 0 7.134 5.869h1.168l-1.309 4.111c-.065.168-.125.336-.185.503-.526 1.46-1.018 2.825-3.867 2.825h-3.969v5.211l7.355.076c2.218-.038 5.005-1.652 7.077-8.615l7.755-36.172Zm15.285 32.264c-2.741 0-4.818-.363-6.229-1.089-1.37-.725-2.298-1.814-2.781-3.265-.484-1.492-.726-3.528-.726-6.108v-16.449h-3.744l1.09-5.322h2.896v-6.773h5.866v6.773h7.31v5.322h-7.31V166.1c0 1.33.121 2.358.363 3.084.282.686.826 1.21 1.632 1.573.807.362 2.016.544 3.629.544h1.686v5.503h-3.682Z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -0,0 +1,6 @@
<svg width="676" height="232" viewBox="0 0 676 232" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M545.35 202V30H608.499C627.501 30 642.162 34.0952 652.482 42.2857C662.802 50.3124 667.962 61.3695 667.962 75.4571C667.962 85.7771 665.259 94.0495 659.853 100.274C654.611 106.335 647.485 110.594 638.476 113.051C645.356 114.198 651.499 116.328 656.905 119.44C662.31 122.552 666.569 126.893 669.682 132.463C672.958 137.869 674.596 144.667 674.596 152.857C674.596 168.091 669.108 180.131 658.133 188.977C647.322 197.659 631.514 202 610.71 202H545.35ZM573.607 178.166H610.956C622.259 178.166 630.859 175.954 636.756 171.531C642.653 166.945 645.602 160.638 645.602 152.611C645.602 144.257 642.571 137.787 636.51 133.2C630.449 128.613 621.931 126.32 610.956 126.32H573.607V178.166ZM573.607 102.977H609.236C619.064 102.977 626.6 100.766 631.842 96.3429C637.084 91.92 639.705 85.8591 639.705 78.16C639.705 70.461 637.084 64.4819 631.842 60.2229C626.6 55.8 619.064 53.5886 609.236 53.5886H573.607V102.977Z" fill="currentColor"/>
<path d="M405.588 202V179.851L472.534 122.86C477.843 118.381 482.571 113.984 486.719 109.67C490.867 105.357 494.102 100.877 496.425 96.2316C498.748 91.4202 499.909 86.277 499.909 80.8019C499.909 70.8472 496.84 63.2153 490.701 57.9061C484.728 52.597 476.764 49.9424 466.81 49.9424C456.855 49.9424 448.808 53.0947 442.67 59.3993C436.531 65.704 433.461 74.2484 433.461 85.0326V88.019H405.837V83.5394C405.837 72.4234 408.326 62.6346 413.303 54.1731C418.281 45.5457 425.332 38.7434 434.457 33.766C443.748 28.7887 454.532 26.3 466.81 26.3C479.917 26.3 490.95 28.5398 499.909 33.0195C509.034 37.4991 515.919 43.7207 520.565 51.6845C525.376 59.6482 527.782 68.9392 527.782 79.5576C527.782 88.1849 526.206 95.8998 523.054 102.702C519.901 109.339 515.505 115.643 509.864 121.616C504.389 127.423 498.084 133.313 490.95 139.286L444.66 177.86H529.026V202H405.588Z" fill="currentColor"/>
<path d="M274 202V30H386.292V55.0629H302.257V102.731H371.549V127.057H302.257V176.937H389.24V202H274Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M188.212 157.998C186.672 157.998 185.71 159.665 186.48 160.998L202.585 188.894C203.476 190.437 202.056 192.287 200.335 191.826L151.491 178.737C149.357 178.165 147.163 179.432 146.592 181.566L133.504 230.411C133.042 232.132 130.731 232.436 129.84 230.893L113.732 202.992C112.962 201.659 111.037 201.659 110.268 202.992L94.1595 230.893C93.2686 232.436 90.9568 232.132 90.4956 230.411L77.4075 181.566C76.8357 179.432 74.6423 178.165 72.5085 178.737L23.664 191.826C21.9429 192.287 20.5234 190.437 21.4143 188.894L37.5192 160.998C38.289 159.665 37.3267 157.998 35.7871 157.998L3.57893 157.998C1.79713 157.998 0.904821 155.844 2.16476 154.584L37.9218 118.827C39.484 117.265 39.484 114.733 37.9218 113.171L2.16478 77.4133C0.904844 76.1533 1.7972 73.999 3.57902 73.9991L35.7837 73.9995C37.3233 73.9995 38.2856 72.3328 37.5158 70.9995L21.4143 43.11C20.5234 41.5669 21.9429 39.717 23.664 40.1781L72.5085 53.2665C74.6423 53.8383 76.8357 52.572 77.4075 50.4381L90.4956 1.59292C90.9568 -0.128187 93.2686 -0.432531 94.1595 1.11058L110.267 29.0111C111.037 30.3445 112.962 30.3445 113.732 29.0111L129.84 1.11058C130.73 -0.432532 133.042 -0.128189 133.503 1.59292L146.592 50.4381C147.163 52.572 149.357 53.8383 151.491 53.2665L200.335 40.1781C202.056 39.717 203.476 41.5669 202.585 43.11L186.483 70.9995C185.713 72.3328 186.676 73.9995 188.215 73.9995L220.421 73.9991C222.203 73.999 223.095 76.1533 221.835 77.4133L186.078 113.171C184.516 114.733 184.516 117.265 186.078 118.827L221.835 154.584C223.095 155.844 222.203 157.998 220.421 157.998L188.212 157.998ZM175.919 81.3306C177.366 79.8837 175.963 77.4549 173.987 77.9845L130.491 89.6396C128.357 90.2114 126.164 88.9451 125.592 86.8112L113.931 43.293C113.402 41.3166 110.597 41.3166 110.068 43.293L98.4069 86.8112C97.8351 88.9451 95.6418 90.2114 93.5079 89.6396L50.0136 77.9849C48.0371 77.4553 46.6348 79.8841 48.0817 81.331L79.9216 113.171C81.4837 114.733 81.4837 117.266 79.9216 118.828L48.0742 150.675C46.6273 152.122 48.0296 154.55 50.0061 154.021L93.5079 142.364C95.6418 141.792 97.8351 143.059 98.4069 145.192L110.068 188.711C110.597 190.687 113.402 190.687 113.931 188.711L125.592 145.192C126.164 143.059 128.357 141.792 130.491 142.364L173.994 154.021C175.971 154.551 177.373 152.122 175.926 150.675L144.079 118.828C142.516 117.266 142.516 114.733 144.079 113.171L175.919 81.3306Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#10A37F" width="800px" height="800px" viewBox="0 0 24 24" role="img" xmlns="http://www.w3.org/2000/svg"><title>OpenAI icon</title><path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1 @@
<svg width='32' height='40' viewBox='0 0 32 40' fill='none' xmlns='http://www.w3.org/2000/svg'><g clip-path='url(#clip0_1311_94973)'><path d='M24 32H8V16H24V32Z' fill='#4B4646'/><path d='M24 8H8V32H24V8ZM32 40H0V0H32V40Z' fill='#F1ECEC'/></g><defs><clipPath id='clip0_1311_94973'><rect width='32' height='40' fill='white'/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 347 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,3 @@
<svg viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M24 22.525H0l12-21.05 12 21.05z"/>
</svg>

After

Width:  |  Height:  |  Size: 134 B

View file

@ -0,0 +1 @@
<svg width="128" height="128" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" width="126" height="126" rx="44" fill="#0F0F0F"/><rect x="18.25" y="18.25" width="91.5" height="91.5" rx="25.75" stroke="#F0F0F0" stroke-width="8.5"/><path fill-rule="evenodd" clip-rule="evenodd" d="M57.694 43.098c0-.622-.505-1.126-1.127-1.126h-8.444a5.114 5.114 0 0 0-5.112 5.111v33.824a5.114 5.114 0 0 0 5.112 5.112h8.444c.622 0 1.127-.505 1.127-1.127V43.098Zm24.424 27.869c-1.238-2.222-4.047-4.026-6.27-4.026H62.923c-.684 0-.93.555-.549 1.239l7.703 13.822c1.239 2.223 4.048 4.026 6.27 4.026h12.927c.683 0 .93-.555.548-1.239l-7.703-13.822Zm.538-18.718c0-5.672-4.605-10.277-10.277-10.277H63.31a1.21 1.21 0 0 0-1.209 1.209v18.137c0 .667.542 1.209 1.21 1.209h9.068c5.672 0 10.277-4.605 10.277-10.278Z" fill="#F0F0F0"/></svg>

After

Width:  |  Height:  |  Size: 818 B

View file

@ -0,0 +1,9 @@
<svg width="204" height="68" viewBox="0 0 204 68" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="3" width="62" height="62" rx="17.55" stroke="white" stroke-width="6"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.9979 19.7671C28.9979 19.3436 28.6541 19 28.2308 19H22.4809C20.5597 19 19 20.5597 19 22.4807V45.5125C19 47.4336 20.5597 48.9935 22.4809 48.9935H28.2308C28.6541 48.9935 28.9979 48.6496 28.9979 48.2263V19.7671ZM45.6293 38.7439C44.7861 37.231 42.8732 36.0028 41.3604 36.0028H32.5577C32.0922 36.0028 31.9249 36.3807 32.1843 36.8462L37.4298 46.2586C38.273 47.7717 40.1858 49 41.6987 49H50.5012C50.9667 49 51.1342 48.6221 50.8745 48.1563L45.6293 38.7439ZM45.9963 25.9983C45.9963 22.1359 42.8604 19 38.9977 19H32.8227C32.3682 19 31.9994 19.3688 31.9994 19.8233V32.1734C31.9994 32.6278 32.3682 32.9969 32.8227 32.9969H38.9977C42.8604 32.9969 45.9963 29.861 45.9963 25.9983Z" fill="white"/>
<path d="M197.177 18.564C197.485 18.564 197.691 18.7698 197.691 19.0784V25.8685C197.691 26.1257 197.845 26.28 198.103 26.28H202.372C202.681 26.28 202.886 26.4858 202.886 26.7944V31.2697C202.886 31.5783 202.681 31.7841 202.372 31.7841H198.103C197.845 31.7841 197.691 31.9384 197.691 32.1956V51.4856C197.691 51.7942 197.485 52 197.177 52H191.415C191.107 52 190.901 51.7942 190.901 51.4856V32.1956C190.901 31.9384 190.747 31.7841 190.489 31.7841H185.808C185.5 31.7841 185.294 31.5783 185.294 31.2697V26.7944C185.294 26.4858 185.5 26.28 185.808 26.28H190.489C190.747 26.28 190.901 26.1257 190.901 25.8685V19.0784C190.901 18.7698 191.107 18.564 191.415 18.564H197.177Z" fill="white"/>
<path d="M172.602 52.6173C165.143 52.6173 159.021 46.4959 159.021 38.8828C159.021 31.3211 164.628 25.457 172.036 25.457C178.311 25.457 183.558 30.0866 184.021 35.6421C184.073 35.8993 183.918 36.105 183.713 36.2594L168.281 45.2614C168.023 45.4157 167.972 45.6729 168.229 45.8786C169.567 47.0103 171.213 47.2675 172.602 47.2675C175.791 47.2675 177.386 45.7758 178.723 43.5638C178.877 43.3066 179.083 43.2038 179.34 43.2552L183.713 44.0268C184.021 44.0782 184.227 44.2326 184.176 44.4383C183.25 48.1934 179.186 52.6173 172.602 52.6173ZM165.503 40.1174L165.606 40.426C165.709 40.7346 165.966 40.7861 166.172 40.6318L176.717 34.1503C176.923 33.996 177.025 33.8417 176.871 33.5845C175.945 32.0413 174.042 31.0639 171.676 31.3211C168.229 31.7326 164.114 35.8993 165.503 40.1174Z" fill="white"/>
<path d="M132.755 26.8973C132.601 26.5372 132.806 26.28 133.166 26.28H139.288C139.545 26.28 139.751 26.3829 139.854 26.6915L145.718 42.2264C145.821 42.5865 146.129 42.5865 146.232 42.2264L152.148 26.6915C152.251 26.3829 152.456 26.28 152.714 26.28H158.835C159.195 26.28 159.401 26.5372 159.247 26.8973L149.113 51.5885C149.01 51.8971 148.804 52 148.547 52H143.403C143.146 52 142.94 51.8971 142.837 51.5885L132.755 26.8973Z" fill="white"/>
<path d="M123.506 52C123.198 52 122.992 51.7942 122.992 51.4856V26.7944C122.992 26.4858 123.198 26.28 123.506 26.28H129.267C129.576 26.28 129.782 26.4858 129.782 26.7944V51.4856C129.782 51.7942 129.576 52 129.267 52H123.506ZM122.375 19.7986C122.375 17.5352 124.175 15.7348 126.387 15.7348C128.65 15.7348 130.399 17.5352 130.399 19.7986C130.399 22.0105 128.599 23.8109 126.387 23.8109C124.175 23.8109 122.375 22.0105 122.375 19.7986Z" fill="white"/>
<path d="M105.23 15.992C112.895 15.992 118.296 20.8274 118.296 28.4405C118.296 33.1215 116.393 36.568 112.74 38.5742C112.483 38.7285 112.483 38.9342 112.74 39.14C116.855 41.9692 118.604 47.2675 118.759 51.4856C118.759 51.7942 118.553 52 118.244 52H112.997C112.689 52 112.483 51.8457 112.483 51.4856C112.277 47.6276 109.602 41.3519 102.864 40.426C102.349 40.426 101.784 40.3746 101.218 40.3746C100.961 40.3231 100.806 40.4774 100.806 40.7346V51.4856C100.806 51.7942 100.6 52 100.292 52H94.5305C94.2219 52 94.0161 51.7942 94.0161 51.4856V16.5064C94.0161 16.1978 94.2219 15.992 94.5305 15.992H105.23ZM100.806 33.996C100.806 34.2532 100.961 34.4075 101.218 34.4075H105.076C114.283 34.4075 114.283 22.2162 105.539 22.2162H101.218C100.961 22.2162 100.806 22.3706 100.806 22.6278V33.996Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -0,0 +1,114 @@
'use client';
import { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { ArrowRight, Terminal, Check } from 'lucide-react';
const CTA_TITLES = [
'Control any coding agent with one SDK.',
'Claude Code, Codex, OpenCode, Amp — unified.',
'Swap agents without refactoring.',
'Universal events. Universal sessions.',
'Stream, store, and replay agent transcripts.',
'Human-in-the-loop, built in.',
'One SDK. Every coding agent.',
'Deploy anywhere. Same API everywhere.',
];
function AnimatedCTATitle() {
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCurrentIndex(prev => (prev + 1) % CTA_TITLES.length);
}, 3000);
return () => clearInterval(interval);
}, []);
return (
<h2 className='min-h-[1.2em] text-4xl font-medium tracking-tight text-white md:text-5xl'>
<AnimatePresence mode='wait'>
<motion.span
key={currentIndex}
initial={{ opacity: 0, y: 5 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -5 }}
transition={{ duration: 0.1 }}
style={{ display: 'block' }}
>
{CTA_TITLES[currentIndex]}
</motion.span>
</AnimatePresence>
</h2>
);
}
const CopyInstallButton = () => {
const [copied, setCopied] = useState(false);
const installCommand = 'curl -sSL https://sandboxagent.dev/install | sh';
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(installCommand);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
console.error('Failed to copy:', err);
}
};
return (
<button
onClick={handleCopy}
className='inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-white/10 bg-white/5 px-4 py-2 text-sm text-white subpixel-antialiased shadow-sm transition-colors hover:border-white/20'
>
{copied ? <Check className='h-4 w-4' /> : <Terminal className='h-4 w-4' />}
{installCommand}
</button>
);
};
export function CTASection() {
return (
<section className='relative overflow-hidden border-t border-white/10 px-6 py-32 text-center'>
<div className='absolute inset-0 z-0 bg-gradient-to-b from-black to-zinc-900/50' />
<motion.div
animate={{ opacity: [0.3, 0.5, 0.3] }}
transition={{ duration: 4, repeat: Infinity }}
className='pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-zinc-500/10 via-transparent to-transparent opacity-50'
/>
<div className='relative z-10 mx-auto max-w-3xl'>
<div className='mb-8'>
<AnimatedCTATitle />
</div>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.1 }}
className='mb-10 text-lg leading-relaxed text-zinc-400'
>
Universal SDK for coding agents. <br className='hidden md:block' />
Control Claude Code, Codex, OpenCode, and Amp with one API.
</motion.p>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.2 }}
className='flex flex-col items-center justify-center gap-4 sm:flex-row'
>
<a
href='/docs'
className='inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-white/10 bg-white px-4 py-2 text-sm text-black subpixel-antialiased shadow-sm transition-colors hover:bg-zinc-200'
>
Read the Docs
<ArrowRight className='h-4 w-4' />
</a>
<CopyInstallButton />
</motion.div>
</div>
</section>
);
}

View file

@ -0,0 +1,89 @@
'use client';
import { useState } from 'react';
import { ChevronDown } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
const faqs = [
{
question: 'Does this replace the Vercel AI SDK?',
answer:
"No, they're complementary. AI SDK is for building chat interfaces and calling LLMs. This SDK is for controlling autonomous coding agents that write code and run commands. Use AI SDK for your UI, use this when you need an agent to actually code.",
},
{
question: 'Which coding agents are supported?',
answer:
'Claude Code, Codex, OpenCode, and Amp. The SDK normalizes their APIs so you can swap between them without changing your code.',
},
{
question: 'How is session data persisted?',
answer:
"Events stream in a universal JSON schema. Persist them anywhere. We have adapters for Postgres and ClickHouse, or use <a href='https://rivet.gg' target='_blank' rel='noopener noreferrer' class='text-orange-400 hover:underline'>Rivet Actors</a> for managed stateful storage.",
},
{
question: 'Can I run this locally or does it require a sandbox provider?',
answer:
"Both. Run locally for development, deploy to E2B, Daytona, Vercel, or Docker for production.",
},
{
question: 'Is this open source?',
answer:
"Yes, MIT licensed. Code is on GitHub.",
},
];
function FAQItem({ question, answer }: { question: string; answer: string }) {
const [isOpen, setIsOpen] = useState(false);
return (
<div className="border-b border-white/5">
<button
onClick={() => setIsOpen(!isOpen)}
className="flex w-full items-center justify-between py-5 text-left"
>
<span className="text-base font-medium text-white pr-4">{question}</span>
<ChevronDown
className={`h-5 w-5 shrink-0 text-zinc-500 transition-transform duration-200 ${
isOpen ? 'rotate-180' : ''
}`}
/>
</button>
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.2 }}
className="overflow-hidden"
>
<p className="pb-5 text-sm leading-relaxed text-zinc-400" dangerouslySetInnerHTML={{ __html: answer }} />
</motion.div>
)}
</AnimatePresence>
</div>
);
}
export function FAQ() {
return (
<section className="relative overflow-hidden border-t border-white/5 py-24">
<div className="mx-auto max-w-3xl px-6">
<div className="mb-12 text-center">
<h2 className="mb-4 text-3xl font-medium tracking-tight text-white">
Frequently Asked Questions
</h2>
<p className="text-zinc-400">
Common questions about the Coding Agent SDK.
</p>
</div>
<div className="divide-y divide-white/5 rounded-2xl border border-white/5 bg-zinc-900/30 px-6">
{faqs.map((faq, index) => (
<FAQItem key={index} question={faq.question} answer={faq.answer} />
))}
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,329 @@
'use client';
import { Workflow, Server, Database, Download, Globe } from 'lucide-react';
import { FeatureIcon } from './ui/FeatureIcon';
import { CopyButton } from './ui/CopyButton';
function AgentLogo({ name, color, src }: { name: string; color: string; src?: string }) {
return (
<div className="flex items-center gap-2 px-2 py-1 rounded bg-zinc-800/50 border border-white/5">
{src ? (
<img src={src} alt={name} className="w-4 h-4" style={{ filter: 'brightness(0) invert(1)' }} />
) : (
<div
className="w-4 h-4 rounded-sm flex items-center justify-center text-[8px] font-bold"
style={{ backgroundColor: `${color}20`, color }}
>
{name[0]}
</div>
)}
<span className="text-[10px] text-zinc-400">{name}</span>
</div>
);
}
function ProviderLogo({ name, src }: { name: string; src?: string }) {
return (
<div className="flex items-center gap-2 px-2 py-1 rounded bg-zinc-800/50 border border-white/5">
{src ? (
<img src={src} alt={name} className="h-3 w-auto" style={{ filter: 'brightness(0) invert(1)' }} />
) : (
<div className="w-4 h-4 rounded-sm flex items-center justify-center text-[8px] font-bold bg-blue-500/20 text-blue-400">
D
</div>
)}
<span className="text-[10px] text-zinc-400">{name}</span>
</div>
);
}
export function FeatureGrid() {
return (
<section id="features" className="relative overflow-hidden border-t border-white/5 py-32">
<div className="relative z-10 mx-auto max-w-7xl px-6">
<div className="mb-16">
<h2 className="mb-4 text-3xl font-medium tracking-tight text-white md:text-5xl">
Full feature coverage.
</h2>
<p className="text-lg leading-relaxed text-zinc-400">
Available as an HTTP API or TypeScript SDK.
</p>
</div>
<div className="grid grid-cols-12 gap-4">
{/* Universal Agent API - Span 7 cols */}
<div className="col-span-12 lg:col-span-7 row-span-2 group relative flex flex-col gap-4 overflow-hidden rounded-2xl border border-white/5 bg-zinc-900/30 p-6 backdrop-blur-sm transition-colors duration-500 hover:bg-zinc-900/50 min-h-[400px]">
{/* Top Shine Highlight */}
<div className="absolute left-0 right-0 top-0 z-10 h-[1px] bg-gradient-to-r from-transparent via-white/20 to-transparent" />
{/* Top Left Reflection/Glow */}
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(255,79,0,0.15)_0%,transparent_50%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100" />
{/* Sharp Edge Highlight */}
<div className="pointer-events-none absolute left-0 top-0 z-20 h-24 w-24 rounded-tl-2xl border-l border-t border-orange-500 opacity-0 transition-opacity duration-500 [mask-image:linear-gradient(135deg,black_0%,transparent_50%)] group-hover:opacity-100" />
<div className="relative z-10 flex flex-col gap-4">
<div className="relative z-10 mb-2 flex items-center gap-3">
<FeatureIcon
icon={Workflow}
color="text-orange-400"
bgColor="bg-orange-500/10"
hoverBgColor="group-hover:bg-orange-500/20"
glowShadow="group-hover:shadow-[0_0_15px_rgba(255,79,0,0.5)]"
/>
<h4 className="text-sm font-medium uppercase tracking-wider text-white">Universal Agent API</h4>
</div>
<p className="text-zinc-400 leading-relaxed text-lg max-w-md">
Claude Code, Codex, OpenCode, and Amp each have different APIs. We provide a single,
unified interface to control them all.
</p>
</div>
<div className="mt-auto relative z-10 bg-black/50 rounded-xl border border-white/5 p-5 overflow-hidden">
<div className="relative w-full aspect-[16/9] bg-[#050505] rounded-xl border border-white/10 overflow-hidden flex items-center justify-center">
{/* Subtle Background Grid */}
<div className="absolute inset-0 opacity-[0.03] pointer-events-none"
style={{ backgroundImage: 'linear-gradient(#fff 1px, transparent 1px), linear-gradient(90deg, #fff 1px, transparent 1px)', backgroundSize: '40px 40px' }} />
<svg viewBox="0 0 800 450" className="w-full h-full relative z-10">
<defs>
{/* Glow effect for active lines */}
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="2.5" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
</defs>
{/* Define curved paths with their respective brand colors */}
{(() => {
const curvedPaths = [
{ d: "M480 225 C540 225, 560 90, 620 90", label: "Claude", color: "#d97757" },
{ d: "M480 225 C540 225, 560 180, 620 180", label: "OpenAI", color: "#ffffff" },
{ d: "M480 225 C540 225, 560 270, 620 270", label: "OpenCode", color: "#10B981" },
{ d: "M480 225 C540 225, 560 360, 620 360", label: "Amp", color: "#F59E0B" }
];
return (
<>
{/* Connection Lines */}
<g className="stroke-zinc-800" fill="none" strokeWidth="1.5">
{/* App -> Agent (Straight) */}
<path d="M180 225 L320 225" strokeDasharray="4 4" />
{/* Agent -> Providers (Curved) */}
{curvedPaths.map((path, i) => (
<path key={i} d={path.d} strokeDasharray="4 4" />
))}
</g>
{/* High-Performance Tracers */}
{/* Blue Tracer: App to SDK */}
<circle r="2.5" fill="#3B82F6" filter="url(#glow)">
<animateMotion path="M180 225 L320 225" dur="1.2s" repeatCount="indefinite" />
<animate attributeName="opacity" values="0;1;0" dur="1.2s" repeatCount="indefinite" />
</circle>
{/* Colored Tracers: SDK to Providers (following curves and matching brand colors) */}
{curvedPaths.map((path, i) => (
<circle key={i} r="2.5" fill={path.color} filter="url(#glow)">
<animateMotion path={path.d} dur="2s" begin={`${i * 0.4}s`} repeatCount="indefinite" />
<animate attributeName="opacity" values="0;1;0" dur="2s" begin={`${i * 0.4}s`} repeatCount="indefinite" />
</circle>
))}
</>
);
})()}
{/* Nodes */}
{/* App Node */}
<g transform="translate(80, 190)">
<rect width="100" height="70" rx="12" fill="#111" stroke="#333" strokeWidth="1" />
<text x="50" y="42" fill="#999" textAnchor="middle" fontSize="14" fontWeight="600" className="uppercase tracking-tighter">Client App</text>
</g>
{/* Central SDK Node */}
<g transform="translate(320, 180)">
<rect width="160" height="90" rx="14" fill="#18181B" stroke="#3B82F6" strokeWidth="2" />
<text x="80" y="52" fill="white" textAnchor="middle" fontSize="14" fontWeight="800">Sandbox Agent SDK</text>
</g>
{/* Provider Nodes with Logos - Vertical Layout (centered) */}
{/* Claude */}
<g transform="translate(620, 50)">
<rect width="140" height="80" rx="10" fill="#111" stroke="#222" strokeWidth="1" />
<foreignObject x="0" y="10" width="140" height="32">
<div className="flex justify-center">
<img src="/logos/claude.svg" alt="Claude" className="h-8 w-8" />
</div>
</foreignObject>
<text x="70" y="62" fill="#999" textAnchor="middle" fontSize="11" fontWeight="600">Claude Code</text>
</g>
{/* Codex */}
<g transform="translate(620, 140)">
<rect width="140" height="80" rx="10" fill="#111" stroke="#222" strokeWidth="1" />
<foreignObject x="0" y="10" width="140" height="32">
<div className="flex justify-center">
<svg className="h-8 w-8" viewBox="0 0 24 24" fill="none">
<path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" fill="#ffffff" />
</svg>
</div>
</foreignObject>
<text x="70" y="62" fill="#999" textAnchor="middle" fontSize="11" fontWeight="600">Codex</text>
</g>
{/* OpenCode */}
<g transform="translate(620, 230)">
<rect width="140" height="80" rx="10" fill="#111" stroke="#222" strokeWidth="1" />
<foreignObject x="0" y="10" width="140" height="32">
<div className="flex justify-center">
<svg className="h-8 w-auto" viewBox="0 0 32 40" fill="none">
<path d="M24 32H8V16H24V32Z" fill="#4B4646"/>
<path d="M24 8H8V32H24V8ZM32 40H0V0H32V40Z" fill="#F1ECEC"/>
</svg>
</div>
</foreignObject>
<text x="70" y="62" fill="#999" textAnchor="middle" fontSize="11" fontWeight="600">OpenCode</text>
</g>
{/* Amp */}
<g transform="translate(620, 320)">
<rect width="140" height="80" rx="10" fill="#111" stroke="#222" strokeWidth="1" />
<foreignObject x="0" y="12" width="140" height="28">
<div className="flex justify-center">
<img src="/logos/amp.svg" alt="Amp" className="h-6 w-auto" style={{ filter: 'brightness(0) invert(1)' }} />
</div>
</foreignObject>
<text x="70" y="62" fill="#999" textAnchor="middle" fontSize="11" fontWeight="600">Amp</text>
</g>
</svg>
</div>
</div>
</div>
{/* Server Mode - Span 5 cols */}
<div className="col-span-12 lg:col-span-5 group relative flex flex-col gap-4 overflow-hidden rounded-2xl border border-white/5 bg-zinc-900/30 p-6 backdrop-blur-sm transition-colors duration-500 hover:bg-zinc-900/50">
{/* Top Shine Highlight */}
<div className="absolute left-0 right-0 top-0 z-10 h-[1px] bg-gradient-to-r from-transparent via-white/20 to-transparent" />
{/* Top Left Reflection/Glow */}
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(34,197,94,0.15)_0%,transparent_50%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100" />
{/* Sharp Edge Highlight */}
<div className="pointer-events-none absolute left-0 top-0 z-20 h-24 w-24 rounded-tl-2xl border-l border-t border-green-500 opacity-0 transition-opacity duration-500 [mask-image:linear-gradient(135deg,black_0%,transparent_50%)] group-hover:opacity-100" />
<div className="relative z-10 mb-2 flex items-center gap-3">
<FeatureIcon
icon={Server}
color="text-green-400"
bgColor="bg-green-500/10"
hoverBgColor="group-hover:bg-green-500/20"
glowShadow="group-hover:shadow-[0_0_15px_rgba(34,197,94,0.5)]"
/>
<h4 className="text-sm font-medium uppercase tracking-wider text-white">Server Mode</h4>
</div>
<p className="text-zinc-400 text-sm leading-relaxed">
Run as an HTTP server anywhere. One command to bridge coding agents to your
application.
</p>
<div className="mt-auto relative z-10 p-3 bg-black/40 rounded-lg border border-white/5 font-mono text-xs text-green-400">
$ sandbox-agent server
</div>
</div>
{/* Universal Schema - Span 5 cols */}
<div className="col-span-12 lg:col-span-5 group relative flex flex-col gap-4 overflow-hidden rounded-2xl border border-white/5 bg-zinc-900/30 p-6 backdrop-blur-sm transition-colors duration-500 hover:bg-zinc-900/50">
{/* Top Shine Highlight */}
<div className="absolute left-0 right-0 top-0 z-10 h-[1px] bg-gradient-to-r from-transparent via-white/20 to-transparent" />
{/* Top Left Reflection/Glow */}
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(168,85,247,0.15)_0%,transparent_50%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100" />
{/* Sharp Edge Highlight */}
<div className="pointer-events-none absolute left-0 top-0 z-20 h-24 w-24 rounded-tl-2xl border-l border-t border-purple-500 opacity-0 transition-opacity duration-500 [mask-image:linear-gradient(135deg,black_0%,transparent_50%)] group-hover:opacity-100" />
<div className="relative z-10 mb-2 flex items-center gap-3">
<FeatureIcon
icon={Database}
color="text-purple-400"
bgColor="bg-purple-500/10"
hoverBgColor="group-hover:bg-purple-500/20"
glowShadow="group-hover:shadow-[0_0_15px_rgba(168,85,247,0.5)]"
/>
<h4 className="text-sm font-medium uppercase tracking-wider text-white">Universal Schema</h4>
</div>
<p className="text-zinc-400 text-sm leading-relaxed">
Standardized session schema that covers all features of all agents. Includes tool calls, permission requests, file edits, etc.
</p>
</div>
{/* Automatic Agent Installation - Span 8 cols */}
<div className="col-span-12 md:col-span-8 group relative flex flex-col gap-4 overflow-hidden rounded-2xl border border-white/5 bg-zinc-900/30 p-6 backdrop-blur-sm transition-colors duration-500 hover:bg-zinc-900/50">
{/* Top Shine Highlight */}
<div className="absolute left-0 right-0 top-0 z-10 h-[1px] bg-gradient-to-r from-transparent via-white/20 to-transparent" />
{/* Top Left Reflection/Glow */}
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(245,158,11,0.15)_0%,transparent_50%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100" />
{/* Sharp Edge Highlight */}
<div className="pointer-events-none absolute left-0 top-0 z-20 h-24 w-24 rounded-tl-2xl border-l border-t border-amber-500 opacity-0 transition-opacity duration-500 [mask-image:linear-gradient(135deg,black_0%,transparent_50%)] group-hover:opacity-100" />
<div className="flex flex-col gap-4 relative z-10">
<div className="relative z-10 mb-2 flex items-center gap-3">
<FeatureIcon
icon={Download}
color="text-amber-400"
bgColor="bg-amber-500/10"
hoverBgColor="group-hover:bg-amber-500/20"
glowShadow="group-hover:shadow-[0_0_15px_rgba(245,158,11,0.5)]"
/>
<h4 className="text-sm font-medium uppercase tracking-wider text-white">Automatic Agent Installation</h4>
</div>
<p className="text-zinc-400 text-sm leading-relaxed">
Agents are automatically installed on first use. No manual setup required.
</p>
</div>
<div className="mt-auto w-full relative z-10">
<div className="flex flex-wrap gap-2">
{['Claude Code', 'Codex', 'OpenCode', 'Amp'].map((agent) => (
<span
key={agent}
className="px-3 py-1.5 rounded-md bg-zinc-800/50 border border-white/5 text-xs font-mono text-zinc-400"
>
{agent}
</span>
))}
</div>
</div>
</div>
{/* Provider Agnostic - Span 4 cols */}
<div className="col-span-12 md:col-span-4 group relative flex flex-col gap-4 overflow-hidden rounded-2xl border border-white/5 bg-zinc-900/30 p-6 backdrop-blur-sm transition-colors duration-500 hover:bg-zinc-900/50">
{/* Top Shine Highlight */}
<div className="absolute left-0 right-0 top-0 z-10 h-[1px] bg-gradient-to-r from-transparent via-white/20 to-transparent" />
{/* Top Left Reflection/Glow */}
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(59,130,246,0.15)_0%,transparent_50%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100" />
{/* Sharp Edge Highlight */}
<div className="pointer-events-none absolute left-0 top-0 z-20 h-24 w-24 rounded-tl-2xl border-l border-t border-blue-500 opacity-0 transition-opacity duration-500 [mask-image:linear-gradient(135deg,black_0%,transparent_50%)] group-hover:opacity-100" />
<div className="relative z-10 mb-2 flex items-center gap-3">
<FeatureIcon
icon={Globe}
color="text-blue-400"
bgColor="bg-blue-500/10"
hoverBgColor="group-hover:bg-blue-500/20"
glowShadow="group-hover:shadow-[0_0_15px_rgba(59,130,246,0.5)]"
/>
<h4 className="text-sm font-medium uppercase tracking-wider text-white">Provider Agnostic</h4>
</div>
<p className="text-zinc-500 text-sm leading-relaxed">
Run locally, in Docker, or deploy to E2B, Daytona, and Vercel. Same SDK everywhere.
</p>
<div className="mt-auto flex flex-wrap gap-2">
{['Local', 'Docker', 'E2B', 'Daytona', 'Vercel', 'Netlify'].map((provider) => (
<span
key={provider}
className="px-2 py-1 rounded-md bg-zinc-800/50 border border-white/5 text-[10px] font-mono text-zinc-400"
>
{provider}
</span>
))}
</div>
</div>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,135 @@
'use client';
const footer = {
products: [
{ name: 'Actors', href: 'https://rivet.dev/docs/actors' },
{ name: 'Sandbox Agent SDK', href: '/docs' },
],
developers: [
{ name: 'Documentation', href: '/docs' },
{ name: 'Changelog', href: '/changelog' },
{ name: 'Blog', href: 'https://rivet.dev/blog' },
],
legal: [
{ name: 'Terms', href: 'https://rivet.dev/terms' },
{ name: 'Privacy Policy', href: 'https://rivet.dev/privacy' },
{ name: 'Acceptable Use', href: 'https://rivet.dev/acceptable-use' },
],
social: [
{
name: 'Discord',
href: 'https://discord.gg/sandbox-agent',
icon: (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
</svg>
),
},
{
name: 'GitHub',
href: 'https://github.com/rivet-dev/sandbox-agent',
icon: (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
),
},
{
name: 'Twitter',
href: 'https://x.com/rivet_dev',
icon: (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
</svg>
),
},
],
};
export function Footer() {
return (
<footer className="border-t border-white/10 bg-zinc-950">
<div className="mx-auto max-w-7xl px-6 py-12 lg:py-16">
<div className="xl:grid xl:grid-cols-12 xl:gap-16">
{/* Logo & Social */}
<div className="space-y-6 xl:col-span-4">
<img src="/rivet-logo-text-white.svg" alt="Rivet" className="h-6 w-auto" />
<p className="text-sm leading-6 text-zinc-400">
Build and scale stateful workloads
</p>
<div className="flex space-x-4">
{footer.social.map((item) => (
<a
key={item.name}
href={item.href}
className="text-zinc-500 hover:text-white transition-colors"
target="_blank"
rel="noopener noreferrer"
>
<span className="sr-only">{item.name}</span>
{item.icon}
</a>
))}
</div>
</div>
{/* Links */}
<div className="mt-12 grid grid-cols-2 gap-8 md:grid-cols-3 xl:col-span-8 xl:mt-0">
<div>
<h3 className="text-sm font-semibold leading-6 text-white">Products</h3>
<ul role="list" className="mt-4 space-y-3">
{footer.products.map((item) => (
<li key={item.name}>
<a
href={item.href}
className="text-sm leading-6 text-zinc-400 hover:text-white transition-colors"
>
{item.name}
</a>
</li>
))}
</ul>
</div>
<div>
<h3 className="text-sm font-semibold leading-6 text-white">Developers</h3>
<ul role="list" className="mt-4 space-y-3">
{footer.developers.map((item) => (
<li key={item.name}>
<a
href={item.href}
className="text-sm leading-6 text-zinc-400 hover:text-white transition-colors"
>
{item.name}
</a>
</li>
))}
</ul>
</div>
<div>
<h3 className="text-sm font-semibold leading-6 text-white">Legal</h3>
<ul role="list" className="mt-4 space-y-3">
{footer.legal.map((item) => (
<li key={item.name}>
<a
href={item.href}
className="text-sm leading-6 text-zinc-400 hover:text-white transition-colors"
>
{item.name}
</a>
</li>
))}
</ul>
</div>
</div>
</div>
{/* Bottom */}
<div className="mt-12 border-t border-white/10 pt-8">
<p className="text-xs text-zinc-500 text-center">
&copy; {new Date().getFullYear()} Rivet Gaming, Inc. All rights reserved.
</p>
</div>
</div>
</footer>
);
}

View file

@ -0,0 +1,230 @@
'use client';
import { Code, Server, GitBranch } from 'lucide-react';
import { CopyButton } from './ui/CopyButton';
const sdkCodeRaw = `import { SandboxAgent } from "sandbox-agent";
const client = await SandboxAgent.start();
await client.createSession("my-session", {
agent: "claude-code",
});
await client.postMessage("my-session", {
message: "Hello, world!",
});
for await (const event of client.streamEvents("my-session")) {
console.log(event.type, event.data);
}`;
function SdkCodeHighlighted() {
return (
<pre className="overflow-x-auto p-3 font-mono text-[11px] leading-relaxed">
<code>
<span className="text-purple-400">import</span>
<span className="text-zinc-300">{" { "}</span>
<span className="text-white">SandboxAgent</span>
<span className="text-zinc-300">{" } "}</span>
<span className="text-purple-400">from</span>
<span className="text-zinc-300"> </span>
<span className="text-green-400">"sandbox-agent"</span>
<span className="text-zinc-300">;</span>
{"\n\n"}
<span className="text-purple-400">const</span>
<span className="text-zinc-300"> client = </span>
<span className="text-purple-400">await</span>
<span className="text-zinc-300"> SandboxAgent.</span>
<span className="text-blue-400">start</span>
<span className="text-zinc-300">();</span>
{"\n\n"}
<span className="text-purple-400">await</span>
<span className="text-zinc-300"> client.</span>
<span className="text-blue-400">createSession</span>
<span className="text-zinc-300">(</span>
<span className="text-green-400">"my-session"</span>
<span className="text-zinc-300">{", {"}</span>
{"\n"}
<span className="text-zinc-300">{" agent: "}</span>
<span className="text-green-400">"claude-code"</span>
<span className="text-zinc-300">,</span>
{"\n"}
<span className="text-zinc-300">{"});"}</span>
{"\n\n"}
<span className="text-purple-400">await</span>
<span className="text-zinc-300"> client.</span>
<span className="text-blue-400">postMessage</span>
<span className="text-zinc-300">(</span>
<span className="text-green-400">"my-session"</span>
<span className="text-zinc-300">{", {"}</span>
{"\n"}
<span className="text-zinc-300">{" message: "}</span>
<span className="text-green-400">"Hello, world!"</span>
<span className="text-zinc-300">,</span>
{"\n"}
<span className="text-zinc-300">{"});"}</span>
{"\n\n"}
<span className="text-purple-400">for await</span>
<span className="text-zinc-300"> (</span>
<span className="text-purple-400">const</span>
<span className="text-zinc-300"> event </span>
<span className="text-purple-400">of</span>
<span className="text-zinc-300"> client.</span>
<span className="text-blue-400">streamEvents</span>
<span className="text-zinc-300">(</span>
<span className="text-green-400">"my-session"</span>
<span className="text-zinc-300">{")) {"}</span>
{"\n"}
<span className="text-zinc-300">{" console."}</span>
<span className="text-blue-400">log</span>
<span className="text-zinc-300">(event.type, event.data);</span>
{"\n"}
<span className="text-zinc-300">{"}"}</span>
</code>
</pre>
);
}
const sandboxCommand = `curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh`;
const sourceCommands = `git clone https://github.com/rivet-dev/sandbox-agent
cd sandbox-agent
cargo run -p sandbox-agent --release`;
export function GetStarted() {
return (
<section id="get-started" className="relative overflow-hidden border-t border-white/5 py-32">
<div className="relative z-10 mx-auto max-w-7xl px-6">
<div className="mb-16 text-center">
<h2 className="mb-4 text-3xl font-medium tracking-tight text-white md:text-5xl">
Get Started
</h2>
<p className="text-lg text-zinc-400">
Choose the installation method that works best for your use case.
</p>
</div>
<div className="grid grid-cols-1 gap-6 md:grid-cols-3">
{/* Option 1: SDK */}
<div className="group relative flex flex-col overflow-hidden rounded-2xl border border-white/5 bg-zinc-900/30 p-6 backdrop-blur-sm transition-colors duration-500 hover:bg-zinc-900/50">
<div className="absolute left-0 right-0 top-0 z-10 h-[1px] bg-gradient-to-r from-transparent via-white/20 to-transparent" />
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(59,130,246,0.15)_0%,transparent_50%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100" />
<div className="pointer-events-none absolute left-0 top-0 z-20 h-24 w-24 rounded-tl-2xl border-l border-t border-blue-500 opacity-0 transition-opacity duration-500 [mask-image:linear-gradient(135deg,black_0%,transparent_50%)] group-hover:opacity-100" />
<div className="relative z-10 mb-4 flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-blue-500/10 text-blue-400 transition-all duration-300 group-hover:bg-blue-500/20 group-hover:shadow-[0_0_15px_rgba(59,130,246,0.5)]">
<Code className="h-5 w-5" />
</div>
<div>
<h3 className="text-lg font-semibold text-white">TypeScript SDK</h3>
<p className="text-xs text-zinc-500">Embed in your application</p>
</div>
</div>
<p className="relative z-10 mb-4 text-sm leading-relaxed text-zinc-400 min-h-[4.5rem]">
Import the TypeScript SDK directly into your Node or browser application. Full type safety and streaming support.
</p>
<div className="relative z-10 flex-1 flex flex-col">
<div className="overflow-hidden rounded-lg border border-white/5 bg-black/50 flex-1 flex flex-col">
<div className="flex items-center justify-between border-b border-white/5 bg-white/5 px-3 py-2">
<span className="text-[10px] font-medium text-zinc-500">example.ts</span>
<CopyButton text={sdkCodeRaw} />
</div>
<SdkCodeHighlighted />
</div>
</div>
</div>
{/* Option 2: Sandbox */}
<div className="group relative flex flex-col overflow-hidden rounded-2xl border border-white/5 bg-zinc-900/30 p-6 backdrop-blur-sm transition-colors duration-500 hover:bg-zinc-900/50">
<div className="absolute left-0 right-0 top-0 z-10 h-[1px] bg-gradient-to-r from-transparent via-white/20 to-transparent" />
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(34,197,94,0.15)_0%,transparent_50%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100" />
<div className="pointer-events-none absolute left-0 top-0 z-20 h-24 w-24 rounded-tl-2xl border-l border-t border-green-500 opacity-0 transition-opacity duration-500 [mask-image:linear-gradient(135deg,black_0%,transparent_50%)] group-hover:opacity-100" />
<div className="relative z-10 mb-4 flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-green-500/10 text-green-400 transition-all duration-300 group-hover:bg-green-500/20 group-hover:shadow-[0_0_15px_rgba(34,197,94,0.5)]">
<Server className="h-5 w-5" />
</div>
<div>
<h3 className="text-lg font-semibold text-white">HTTP API</h3>
<p className="text-xs text-zinc-500">Run as a server</p>
</div>
</div>
<p className="relative z-10 mb-4 text-sm leading-relaxed text-zinc-400 min-h-[4.5rem]">
Run as an HTTP server and connect from any language. Deploy to E2B, Daytona, Vercel, or your own infrastructure.
</p>
<div className="relative z-10 flex-1 flex flex-col">
<div className="overflow-hidden rounded-lg border border-white/5 bg-black/50 flex-1 flex flex-col">
<div className="flex items-center justify-between border-b border-white/5 bg-white/5 px-3 py-2">
<span className="text-[10px] font-medium text-zinc-500">terminal</span>
<CopyButton text={sandboxCommand} />
</div>
<pre className="overflow-x-auto p-3 font-mono text-[11px] leading-relaxed flex-1">
<code>
<span className="text-zinc-500">$ </span>
<span className="text-zinc-300">curl -fsSL \</span>
{"\n"}
<span className="text-zinc-300">{" "}</span>
<span className="text-green-400">https://releases.rivet.dev/sandbox-agent/latest/install.sh</span>
<span className="text-zinc-300"> | </span>
<span className="text-blue-400">sh</span>
</code>
</pre>
</div>
</div>
</div>
{/* Option 3: Build from Source */}
<div className="group relative flex flex-col overflow-hidden rounded-2xl border border-white/5 bg-zinc-900/30 p-6 backdrop-blur-sm transition-colors duration-500 hover:bg-zinc-900/50">
<div className="absolute left-0 right-0 top-0 z-10 h-[1px] bg-gradient-to-r from-transparent via-white/20 to-transparent" />
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(245,158,11,0.15)_0%,transparent_50%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100" />
<div className="pointer-events-none absolute left-0 top-0 z-20 h-24 w-24 rounded-tl-2xl border-l border-t border-amber-500 opacity-0 transition-opacity duration-500 [mask-image:linear-gradient(135deg,black_0%,transparent_50%)] group-hover:opacity-100" />
<div className="relative z-10 mb-4 flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-amber-500/10 text-amber-400 transition-all duration-300 group-hover:bg-amber-500/20 group-hover:shadow-[0_0_15px_rgba(245,158,11,0.5)]">
<GitBranch className="h-5 w-5" />
</div>
<div>
<h3 className="text-lg font-semibold text-white">Open Source</h3>
<p className="text-xs text-zinc-500">Full control</p>
</div>
</div>
<p className="relative z-10 mb-4 text-sm leading-relaxed text-zinc-400 min-h-[4.5rem]">
Clone the repo and build with Cargo. Customize, contribute, or embed directly in your Rust project.
</p>
<div className="relative z-10 flex-1 flex flex-col">
<div className="overflow-hidden rounded-lg border border-white/5 bg-black/50 flex-1 flex flex-col">
<div className="flex items-center justify-between border-b border-white/5 bg-white/5 px-3 py-2">
<span className="text-[10px] font-medium text-zinc-500">terminal</span>
<CopyButton text={sourceCommands} />
</div>
<pre className="overflow-x-auto p-3 font-mono text-[11px] leading-relaxed flex-1">
<code>
<span className="text-zinc-500">$ </span>
<span className="text-blue-400">git clone</span>
<span className="text-zinc-300"> </span>
<span className="text-green-400">https://github.com/rivet-dev/sandbox-agent</span>
{"\n"}
<span className="text-zinc-500">$ </span>
<span className="text-blue-400">cd</span>
<span className="text-zinc-300"> sandbox-agent</span>
{"\n"}
<span className="text-zinc-500">$ </span>
<span className="text-blue-400">cargo run</span>
<span className="text-zinc-300"> -p sandbox-agent --release</span>
</code>
</pre>
</div>
</div>
</div>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,78 @@
'use client';
import { useEffect, useState } from 'react';
interface GitHubStarsProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
repo?: string;
}
function formatNumber(num: number): string {
if (num >= 1000) {
return `${(num / 1000).toFixed(1)}k`;
}
return num.toString();
}
export function GitHubStars({
repo = 'rivet-dev/sandbox-agent',
className,
...props
}: GitHubStarsProps) {
const [stars, setStars] = useState<number | null>(null);
useEffect(() => {
const cacheKey = `github-stars-${repo}`;
const cachedData = sessionStorage.getItem(cacheKey);
if (cachedData) {
const { stars: cachedStars, timestamp } = JSON.parse(cachedData);
// Check if cache is less than 5 minutes old
if (Date.now() - timestamp < 5 * 60 * 1000) {
setStars(cachedStars);
return;
}
}
fetch(`https://api.github.com/repos/${repo}`)
.then((response) => {
if (!response.ok) throw new Error('Failed to fetch');
return response.json();
})
.then((data) => {
const newStars = data.stargazers_count;
setStars(newStars);
sessionStorage.setItem(
cacheKey,
JSON.stringify({
stars: newStars,
timestamp: Date.now(),
}),
);
})
.catch((err) => {
console.error('Failed to fetch stars', err);
});
}, [repo]);
return (
<a
href={`https://github.com/${repo}`}
target="_blank"
rel="noreferrer"
className={className}
{...props}
>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
<span className="hidden md:inline">
{stars ? `${formatNumber(stars)} Stars` : 'GitHub'}
</span>
</a>
);
}

View file

@ -0,0 +1,136 @@
'use client';
import { useState } from 'react';
import { Terminal, Check, ArrowRight } from 'lucide-react';
const CopyInstallButton = () => {
const [copied, setCopied] = useState(false);
const installCommand = 'npx skills add https://sandboxagent.dev/docs';
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(installCommand);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
console.error('Failed to copy:', err);
}
};
return (
<button
onClick={handleCopy}
className='inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-white/10 bg-white/5 px-4 py-2 text-sm text-white subpixel-antialiased shadow-sm transition-colors hover:border-white/20'
>
{copied ? <Check className='h-4 w-4' /> : <Terminal className='h-4 w-4' />}
{installCommand}
</button>
);
};
export function Hero() {
return (
<section className="relative pt-44 pb-24 overflow-hidden">
<div className="max-w-7xl mx-auto px-6 relative z-10">
<div className="flex flex-col lg:flex-row items-center gap-16">
<div className="flex-1 text-center lg:text-left">
<h1 className="mb-6 text-5xl font-medium leading-[1.1] tracking-tighter text-white md:text-7xl">
Universal API for <br />
Coding Agents
</h1>
<p className="mt-8 text-xl text-zinc-400 leading-relaxed max-w-2xl mx-auto lg:mx-0">
One SDK to control Claude Code, Codex, OpenCode, and Amp. Unified events, session management, and human-in-the-loop. Swap agents with zero refactoring.
</p>
<div className="mt-10 flex flex-col items-center gap-4 sm:flex-row sm:justify-center lg:justify-start">
<a
href="/docs"
className='inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-white/10 bg-white px-4 py-2 text-sm text-black subpixel-antialiased shadow-sm transition-colors hover:bg-zinc-200'
>
Read the Docs
<ArrowRight className='h-4 w-4' />
</a>
<CopyInstallButton />
</div>
</div>
<div className="flex-1 w-full max-w-xl">
<div className="relative group">
<div className="absolute -inset-1 rounded-xl bg-gradient-to-r from-zinc-700 to-zinc-800 opacity-20 blur" />
<div className="group relative overflow-hidden rounded-xl border border-white/10 bg-zinc-900/50 shadow-2xl backdrop-blur-xl">
<div className="flex items-center justify-between border-b border-white/5 bg-white/5 px-4 py-3">
<div className="flex items-center gap-2">
<div className="h-3 w-3 rounded-full border border-zinc-500/50 bg-zinc-500/20" />
<div className="h-3 w-3 rounded-full border border-zinc-500/50 bg-zinc-500/20" />
<div className="h-3 w-3 rounded-full border border-zinc-500/50 bg-zinc-500/20" />
</div>
<div className="font-mono text-xs text-zinc-500">example_agent.ts</div>
</div>
<pre className="overflow-x-auto p-6 font-mono text-sm leading-relaxed">
<code>
<span className="text-purple-400">const</span>
<span className="text-zinc-300"> agents = </span>
<span className="text-purple-400">await</span>
<span className="text-zinc-300"> client.</span>
<span className="text-blue-400">listAgents</span>
<span className="text-zinc-300">();</span>
{"\n\n"}
<span className="text-purple-400">await</span>
<span className="text-zinc-300"> client.</span>
<span className="text-blue-400">createSession</span>
<span className="text-zinc-300">(</span>
<span className="text-green-400">"demo"</span>
<span className="text-zinc-300">{", {"}</span>
{"\n"}
<span className="text-zinc-300">{" agent: "}</span>
<span className="text-green-400">"codex"</span>
<span className="text-zinc-300">,</span>
{"\n"}
<span className="text-zinc-300">{" agentMode: "}</span>
<span className="text-green-400">"default"</span>
<span className="text-zinc-300">,</span>
{"\n"}
<span className="text-zinc-300">{" permissionMode: "}</span>
<span className="text-green-400">"plan"</span>
<span className="text-zinc-300">,</span>
{"\n"}
<span className="text-zinc-300">{"});"}</span>
{"\n\n"}
<span className="text-purple-400">await</span>
<span className="text-zinc-300"> client.</span>
<span className="text-blue-400">postMessage</span>
<span className="text-zinc-300">(</span>
<span className="text-green-400">"demo"</span>
<span className="text-zinc-300">{", { message: "}</span>
<span className="text-green-400">"Hello from the SDK."</span>
<span className="text-zinc-300">{" });"}</span>
{"\n\n"}
<span className="text-purple-400">for await</span>
<span className="text-zinc-300"> (</span>
<span className="text-purple-400">const</span>
<span className="text-zinc-300"> event </span>
<span className="text-purple-400">of</span>
<span className="text-zinc-300"> client.</span>
<span className="text-blue-400">streamEvents</span>
<span className="text-zinc-300">(</span>
<span className="text-green-400">"demo"</span>
<span className="text-zinc-300">{", { offset: "}</span>
<span className="text-amber-400">0</span>
<span className="text-zinc-300">{" })) {"}</span>
{"\n"}
<span className="text-zinc-300">{" console."}</span>
<span className="text-blue-400">log</span>
<span className="text-zinc-300">(event.type, event.data);</span>
{"\n"}
<span className="text-zinc-300">{"}"}</span>
</code>
</pre>
</div>
</div>
</div>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,35 @@
'use client';
const integrations = [
'Daytona',
'E2B',
'AI SDK',
'Anthropic',
'OpenAI',
'Docker',
'Fly.io',
'AWS Nitro',
'Postgres',
'ClickHouse',
'Rivet',
];
export function Integrations() {
return (
<section id="integrations" className="py-24 bg-zinc-900/20 border-t border-white/5 relative overflow-hidden">
<div className="max-w-4xl mx-auto px-6 text-center">
<h2 className="text-3xl font-bold text-white mb-6">Works with your stack</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{integrations.map((item) => (
<div
key={item}
className="h-16 flex items-center justify-center rounded-xl border border-white/5 bg-zinc-900/50 text-zinc-300 font-mono text-sm hover:border-accent/40 hover:text-accent transition-all cursor-default"
>
{item}
</div>
))}
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,144 @@
'use client';
import { useState, useEffect } from 'react';
import { Menu, X } from 'lucide-react';
import { GitHubStars } from './GitHubStars';
function NavItem({ href, children }: { href: string; children: React.ReactNode }) {
return (
<div className="px-2.5 py-2 opacity-60 hover:opacity-100 transition-all duration-200">
<a href={href} className="text-white text-sm">
{children}
</a>
</div>
);
}
export function Navigation() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [isScrolled, setIsScrolled] = useState(false);
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 20);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return (
<div className="fixed top-0 z-50 w-full max-w-[1200px] md:left-1/2 md:top-4 md:-translate-x-1/2 md:px-8">
<div
className={`relative before:pointer-events-none before:absolute before:inset-[-1px] before:z-20 before:hidden before:rounded-2xl before:border before:content-[''] before:transition-colors before:duration-300 before:ease-in-out md:before:block ${
isScrolled ? "before:border-white/10" : "before:border-transparent"
}`}
>
<div
className={`absolute inset-0 -z-[1] hidden overflow-hidden rounded-2xl transition-all duration-300 ease-in-out md:block ${
isScrolled
? "bg-black/80 backdrop-blur-lg"
: "bg-black backdrop-blur-none"
}`}
/>
<header
className={`bg-black/60 border-b-transparent sticky top-0 z-10 flex flex-col items-center border-b backdrop-blur pt-2 pb-2 md:static md:bg-transparent md:rounded-2xl md:max-w-[1200px] md:border-transparent md:backdrop-none md:backdrop-blur-none transition-all hover:opacity-100 ${
isScrolled ? "opacity-100" : "opacity-80"
}`}
>
<div className="flex w-full items-center justify-between px-3">
{/* Left side: Logo + Nav */}
<div className="flex items-center gap-4">
<a href="/" className="flex items-center gap-3">
<img src="/rivet-icon.svg" alt="Rivet" className="size-8" />
<span className="text-white/30">|</span>
<img src="/logos/sanboxagent.svg" alt="Sandbox Agent SDK" className="h-6 w-auto" />
</a>
{/* Desktop Nav */}
<div className="hidden md:flex items-center">
<NavItem href="/docs">Docs</NavItem>
<NavItem href="/changelog">Changelog</NavItem>
</div>
</div>
{/* Right side */}
<div className="hidden md:flex flex-row items-center">
<a
href="https://discord.gg/sandbox-agent"
className="inline-flex items-center justify-center whitespace-nowrap rounded-md border border-white/10 px-4 py-2 h-10 text-sm mr-2 hover:border-white/20 text-white/90 hover:text-white transition-colors"
aria-label="Discord"
>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
</svg>
</a>
<GitHubStars
repo="rivet-dev/sandbox-agent"
className="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-white/10 bg-white/5 px-4 py-2 h-10 text-sm text-white shadow-sm hover:border-white/20 transition-colors"
/>
</div>
{/* Mobile menu button */}
<button
className="md:hidden text-zinc-400 hover:text-white p-2"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
>
{mobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
</button>
</div>
</header>
</div>
{/* Mobile menu */}
{mobileMenuOpen && (
<div className="md:hidden border border-white/10 bg-black/95 backdrop-blur-lg rounded-2xl mt-2 mx-2">
<div className="px-4 py-4 space-y-2">
<a
href="/docs"
className="block py-2 px-3 text-white/80 hover:text-white hover:bg-white/5 rounded-lg transition-colors"
onClick={() => setMobileMenuOpen(false)}
>
Docs
</a>
<a
href="/changelog"
className="block py-2 px-3 text-white/80 hover:text-white hover:bg-white/5 rounded-lg transition-colors"
onClick={() => setMobileMenuOpen(false)}
>
Changelog
</a>
<div className="border-t border-white/10 pt-2 mt-2 space-y-2">
<a
href="https://discord.gg/sandbox-agent"
className="flex items-center gap-2 py-2 px-3 text-white/80 hover:text-white hover:bg-white/5 rounded-lg transition-colors"
onClick={() => setMobileMenuOpen(false)}
aria-label="Discord"
>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
</svg>
<span>Discord</span>
</a>
<GitHubStars
repo="rivet-dev/sandbox-agent"
className="flex items-center gap-2 py-2 px-3 text-white/80 hover:text-white hover:bg-white/5 rounded-lg transition-colors"
onClick={() => setMobileMenuOpen(false)}
/>
</div>
</div>
</div>
)}
</div>
);
}

View file

@ -0,0 +1,217 @@
'use client';
import { motion } from 'framer-motion';
const frictions = [
{
number: '01',
title: 'Fragmented Agent Scaffolds',
description:
'Claude Code, Codex, OpenCode, and Amp each have proprietary APIs. Swapping agents means rewriting your entire integration.',
solution: 'Unified control plane for all agent engines.',
visual: (
<div className="mt-6 space-y-3">
<div className="flex items-center gap-3">
<div className="h-px flex-1 bg-gradient-to-r from-red-500/50 to-transparent" />
<span className="text-xs text-zinc-500">Claude Bridge</span>
</div>
<div className="flex items-center gap-3">
<div className="h-px flex-1 bg-gradient-to-r from-red-500/50 to-transparent" />
<span className="text-xs text-zinc-500">Amp Bridge</span>
</div>
<div className="flex items-center gap-3 mt-4">
<div className="h-px flex-1 bg-gradient-to-r from-green-500 to-green-500/20" />
<span className="text-xs text-green-400 font-medium">API</span>
</div>
<div className="mt-4 bg-black/60 rounded-lg border border-white/5 p-3 font-mono text-xs">
<div className="text-zinc-500">
<span className="text-zinc-600">01</span>{' '}
<span className="text-purple-400">agent</span>
<span className="text-zinc-400">.</span>
<span className="text-blue-400">spawn</span>
<span className="text-zinc-400">(</span>
<span className="text-green-400">"claude-code"</span>
<span className="text-zinc-400">)</span>
</div>
<div className="text-zinc-500">
<span className="text-zinc-600">02</span>{' '}
<span className="text-purple-400">agent</span>
<span className="text-zinc-400">.</span>
<span className="text-blue-400">spawn</span>
<span className="text-zinc-400">(</span>
<span className="text-green-400">"amp"</span>
<span className="text-zinc-400">)</span>
</div>
<div className="text-zinc-600 mt-2">// Exactly same methods</div>
</div>
</div>
),
accentColor: 'orange',
},
{
number: '02',
title: 'Deploy Anywhere',
description:
"Whether you're running locally, in Docker, or with E2B, Daytona, and Vercel — you shouldn't need different integration code for each.",
solution: 'One SDK, any environment. Deploy locally or to any cloud provider with a single config change.',
visual: (
<div className="mt-6">
<div className="flex items-center justify-between gap-2 text-xs">
<div className="flex-1 text-center py-2 px-3 rounded-lg bg-zinc-800/50 border border-white/5 text-zinc-400">
E2B
</div>
<div className="text-zinc-500">+</div>
<div className="flex-1 text-center py-2 px-3 rounded-lg bg-zinc-800/50 border border-white/5 text-zinc-400">
Daytona
</div>
<div className="text-zinc-500">+</div>
<div className="flex-1 text-center py-2 px-3 rounded-lg bg-zinc-800/50 border border-white/5 text-zinc-400">
Vercel
</div>
</div>
<div className="mt-4 bg-black/60 rounded-lg border border-white/5 p-3 font-mono text-xs">
<div className="text-zinc-500 mb-1"># Works with all providers</div>
<div>
<span className="text-green-400">SANDBOX_PROVIDER</span>
<span className="text-zinc-400">=</span>
<span className="text-amber-400">"daytona"</span>
</div>
</div>
</div>
),
accentColor: 'purple',
},
{
number: '03',
title: 'Transient State',
description:
'Transcripts and session data are usually lost when the agent process ends. Debugging and replay become impossible.',
solution: 'Standardized session JSON. Stream events to your own storage in real-time.',
visual: (
<div className="mt-6">
<div className="bg-black/60 rounded-lg border border-white/5 p-3 font-mono text-xs overflow-hidden">
<div className="text-zinc-500 mb-2"># Session persisted automatically</div>
<div className="space-y-1">
<div>
<span className="text-blue-400">"events"</span>
<span className="text-zinc-400">: [</span>
</div>
<div className="pl-4">
<span className="text-zinc-400">{'{ '}</span>
<span className="text-blue-400">"type"</span>
<span className="text-zinc-400">: </span>
<span className="text-green-400">"tool_call"</span>
<span className="text-zinc-400">{' }'}</span>
</div>
<div className="pl-4">
<span className="text-zinc-400">{'{ '}</span>
<span className="text-blue-400">"type"</span>
<span className="text-zinc-400">: </span>
<span className="text-green-400">"message"</span>
<span className="text-zinc-400">{' }'}</span>
</div>
<div className="text-zinc-400">]</div>
</div>
<div className="mt-3 flex items-center gap-2 text-zinc-500">
<span className="w-1.5 h-1.5 rounded-full bg-green-500 animate-pulse" />
<span>Streaming to Rivet Actors</span>
</div>
</div>
</div>
),
accentColor: 'blue',
},
];
const accentStyles = {
orange: {
gradient: 'from-orange-500/20',
border: 'border-orange-500/30',
glow: 'rgba(255,79,0,0.15)',
number: 'text-orange-500',
},
purple: {
gradient: 'from-purple-500/20',
border: 'border-purple-500/30',
glow: 'rgba(168,85,247,0.15)',
number: 'text-purple-500',
},
blue: {
gradient: 'from-blue-500/20',
border: 'border-blue-500/30',
glow: 'rgba(59,130,246,0.15)',
number: 'text-blue-500',
},
};
export function PainPoints() {
return (
<section className="relative overflow-hidden border-t border-white/5 py-32">
<div className="mx-auto max-w-7xl px-6">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="mb-16"
>
<h2 className="mb-6 text-3xl font-medium tracking-tight text-white md:text-5xl">
Integrating coding agents is hard.
</h2>
<p className="max-w-2xl text-lg leading-relaxed text-zinc-400">
Every agent has its own API, event format, and session model. Swapping agents means
rewriting your entire integration.
</p>
</motion.div>
<div className="grid gap-6 md:grid-cols-3">
{frictions.map((friction, index) => {
const styles = accentStyles[friction.accentColor as keyof typeof accentStyles];
return (
<motion.div
key={friction.number}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }}
className="group relative flex flex-col overflow-hidden rounded-2xl border border-white/5 bg-zinc-900/30 p-6 backdrop-blur-sm transition-colors duration-500 hover:bg-zinc-900/50"
>
{/* Top shine */}
<div className="absolute left-0 right-0 top-0 z-10 h-[1px] bg-gradient-to-r from-transparent via-white/20 to-transparent" />
{/* Hover glow */}
<div
className="pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-500 group-hover:opacity-100"
style={{
background: `radial-gradient(circle at top left, ${styles.glow} 0%, transparent 50%)`,
}}
/>
{/* Corner highlight */}
<div
className={`pointer-events-none absolute left-0 top-0 z-20 h-24 w-24 rounded-tl-2xl border-l border-t ${styles.border} opacity-0 transition-opacity duration-500 [mask-image:linear-gradient(135deg,black_0%,transparent_50%)] group-hover:opacity-100`}
/>
<div className="relative z-10">
{/* Title */}
<h3 className="mb-3 text-xl font-medium text-white">{friction.title}</h3>
{/* Description */}
<p className="text-sm leading-relaxed text-zinc-500">{friction.description}</p>
{/* Solution */}
<div className="mt-4 border-t border-white/5 pt-4">
<p className="text-sm font-medium text-zinc-300">{friction.solution}</p>
</div>
{/* Visual */}
{friction.visual}
</div>
</motion.div>
);
})}
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,53 @@
'use client';
import { Workflow, Database, Server } from 'lucide-react';
import { FeatureIcon } from './ui/FeatureIcon';
const problems = [
{
title: 'Universal Agent API',
desc: 'Claude Code, Codex, OpenCode, and Amp each have different APIs. We provide a single interface to control them all.',
icon: Workflow,
color: 'text-accent',
},
{
title: 'Universal Transcripts',
desc: 'Every agent has its own event format. Our universal schema normalizes them all — stream, store, and replay with ease.',
icon: Database,
color: 'text-purple-400',
},
{
title: 'Run Anywhere',
desc: 'Lightweight Rust daemon runs locally or in any environment. One command to bridge coding agents to your system.',
icon: Server,
color: 'text-green-400',
},
];
export function ProblemsSolved() {
return (
<section id="features" className="py-24 bg-zinc-950 border-y border-white/5">
<div className="max-w-7xl mx-auto px-6">
<div className="text-center mb-16">
<h2 className="text-3xl font-bold text-white mb-4">Why Coding Agent SDK?</h2>
<p className="text-zinc-400 max-w-xl mx-auto">
Solving the three fundamental friction points of agentic software development.
</p>
</div>
<div className="grid md:grid-cols-3 gap-8">
{problems.map((item, idx) => (
<div
key={idx}
className="group p-8 rounded-2xl bg-zinc-900/40 border border-white/5 hover:border-accent/30 transition-all duration-300"
>
<FeatureIcon icon={item.icon} color={item.color} />
<h3 className="text-xl font-bold text-white mb-3">{item.title}</h3>
<p className="text-zinc-400 text-sm leading-relaxed">{item.desc}</p>
</div>
))}
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,11 @@
interface BadgeProps {
children: React.ReactNode;
}
export function Badge({ children }: BadgeProps) {
return (
<span className="inline-flex px-3 py-1 rounded-full bg-accent/10 border border-accent/20 text-accent text-xs font-mono font-medium">
{children}
</span>
);
}

View file

@ -0,0 +1,49 @@
import type { ReactNode } from 'react';
interface ButtonProps {
children: ReactNode;
variant?: 'primary' | 'secondary' | 'ghost';
size?: 'sm' | 'md' | 'lg';
href?: string;
onClick?: () => void;
className?: string;
}
export function Button({
children,
variant = 'primary',
size = 'md',
href,
onClick,
className = ''
}: ButtonProps) {
const baseStyles = 'inline-flex items-center justify-center font-bold rounded-lg transition-all';
const variants = {
primary: 'bg-white text-black hover:bg-zinc-200',
secondary: 'bg-zinc-900 border border-white/10 text-white hover:bg-zinc-800',
ghost: 'text-zinc-400 hover:text-white',
};
const sizes = {
sm: 'h-9 px-4 text-sm',
md: 'h-12 px-8 text-sm',
lg: 'h-14 px-10 text-base',
};
const classes = `${baseStyles} ${variants[variant]} ${sizes[size]} ${className}`;
if (href) {
return (
<a href={href} className={classes}>
{children}
</a>
);
}
return (
<button onClick={onClick} className={classes}>
{children}
</button>
);
}

View file

@ -0,0 +1,32 @@
'use client';
import { useState } from 'react';
import { Copy, CheckCircle2 } from 'lucide-react';
interface CopyButtonProps {
text: string;
}
export function CopyButton({ text }: CopyButtonProps) {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<button
onClick={handleCopy}
className="p-2 hover:bg-white/10 rounded-md transition-colors text-zinc-500 hover:text-white"
aria-label="Copy to clipboard"
>
{copied ? (
<CheckCircle2 className="w-4 h-4 text-green-400" />
) : (
<Copy className="w-4 h-4" />
)}
</button>
);
}

View file

@ -0,0 +1,23 @@
import type { LucideIcon } from 'lucide-react';
interface FeatureIconProps {
icon: LucideIcon;
color?: string;
bgColor?: string;
hoverBgColor?: string;
glowShadow?: string;
}
export function FeatureIcon({
icon: Icon,
color = 'text-accent',
bgColor = 'bg-accent/10',
hoverBgColor = 'group-hover:bg-accent/20',
glowShadow = 'group-hover:shadow-[0_0_15px_rgba(59,130,246,0.5)]'
}: FeatureIconProps) {
return (
<div className={`rounded ${bgColor} p-2 ${color} transition-all duration-500 ${hoverBgColor} ${glowShadow}`}>
<Icon className="h-4 w-4" />
</div>
);
}

View file

@ -0,0 +1,39 @@
---
interface Props {
title: string;
description?: string;
}
const { title, description = "Universal SDK for coding agents. Control Claude Code, Codex, OpenCode, and Amp with unified events and sessions." } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content={description} />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Open+Sans:wght@400;500;600;700&display=swap" rel="stylesheet" />
<title>{title}</title>
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:type" content="website" />
<meta property="og:image" content="/logos/sanboxagent.svg" />
<meta property="og:image:width" content="570" />
<meta property="og:image:height" content="98" />
<meta property="og:image:alt" content="Sandbox Agent SDK Logo" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="/logos/sanboxagent.svg" />
</head>
<body class="min-h-screen">
<slot />
</body>
</html>
<style is:global>
@import '../styles/global.css';
</style>

View file

@ -0,0 +1,22 @@
---
import Layout from '../layouts/Layout.astro';
import { Navigation } from '../components/Navigation';
import { Hero } from '../components/Hero';
import { FeatureGrid } from '../components/FeatureGrid';
import { GetStarted } from '../components/GetStarted';
import { FAQ } from '../components/FAQ';
import { Footer } from '../components/Footer';
---
<Layout title="Coding Agent SDK - Universal SDK for Coding Agents">
<div class="min-h-screen bg-black text-white selection:bg-accent/30">
<Navigation client:load />
<main>
<Hero client:load />
<FeatureGrid client:visible />
<GetStarted client:visible />
<FAQ client:visible />
</main>
<Footer client:visible />
</div>
</Layout>

View file

@ -0,0 +1,75 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
body {
@apply bg-black text-white antialiased;
font-family: 'Open Sans', system-ui, sans-serif;
}
::selection {
@apply bg-accent/30 text-white;
}
/* Firefox scrollbar */
* {
scrollbar-width: thin;
scrollbar-color: #3f3f46 transparent;
}
/* Webkit scrollbar */
::-webkit-scrollbar {
@apply w-2 h-2;
}
::-webkit-scrollbar-track {
@apply bg-transparent;
}
::-webkit-scrollbar-thumb {
@apply bg-zinc-700 rounded-full;
}
::-webkit-scrollbar-thumb:hover {
@apply bg-zinc-600;
}
::-webkit-scrollbar-corner {
@apply bg-transparent;
}
/* Code block scrollbars */
pre, code, .overflow-x-auto {
scrollbar-width: thin;
scrollbar-color: #52525b #18181b;
}
pre::-webkit-scrollbar,
code::-webkit-scrollbar,
.overflow-x-auto::-webkit-scrollbar {
@apply h-2;
}
pre::-webkit-scrollbar-track,
code::-webkit-scrollbar-track,
.overflow-x-auto::-webkit-scrollbar-track {
@apply bg-zinc-900;
}
pre::-webkit-scrollbar-thumb,
code::-webkit-scrollbar-thumb,
.overflow-x-auto::-webkit-scrollbar-thumb {
@apply bg-zinc-600 rounded-full;
}
}
@layer components {
.glass {
@apply bg-white/[0.02] backdrop-blur-md border border-white/10;
}
.glass-hover {
@apply hover:bg-white/[0.04] hover:border-white/20 transition-all;
}
}

View file

@ -0,0 +1,26 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {
colors: {
accent: '#FF4500',
},
fontFamily: {
sans: ['Open Sans', 'system-ui', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
animation: {
'fade-in-up': 'fade-in-up 0.6s ease-out forwards',
'pulse-slow': 'pulse 3s ease-in-out infinite',
},
keyframes: {
'fade-in-up': {
'0%': { opacity: '0', transform: 'translateY(20px)' },
'100%': { opacity: '1', transform: 'translateY(0)' },
},
},
},
},
plugins: [],
};

View file

@ -0,0 +1,7 @@
{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react"
}
}

BIN
og.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB