mirror of
https://github.com/katanemo/plano.git
synced 2026-06-17 15:25:17 +02:00
format(www)
This commit is contained in:
parent
a825ce9add
commit
64d45a7a00
27 changed files with 257 additions and 314 deletions
|
|
@ -1,101 +0,0 @@
|
|||
# ArchGW Monorepo
|
||||
|
||||
This is a Turborepo monorepo containing the Next.js applications and shared packages.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
.
|
||||
├── apps/
|
||||
│ ├── www/ # Marketing website
|
||||
│ └── docs/ # Documentation site
|
||||
├── packages/
|
||||
│ ├── ui/ # Shared UI components (Navbar, Footer, Logo, etc.)
|
||||
│ ├── shared-styles/ # Shared CSS and Tailwind configuration
|
||||
│ ├── tailwind-config/ # Tailwind configuration
|
||||
│ └── tsconfig/ # Shared TypeScript configurations
|
||||
└── turbo.json # Turborepo configuration
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
Run all apps in development mode:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Or run specific apps:
|
||||
|
||||
```bash
|
||||
# Marketing website (port 3000)
|
||||
cd apps/www && npm run dev
|
||||
|
||||
# Documentation (port 3001)
|
||||
cd apps/docs && npm run dev
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
Build all apps:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lint & Type Check
|
||||
|
||||
```bash
|
||||
npm run lint
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
## Shared Packages
|
||||
|
||||
### @archgw/ui
|
||||
|
||||
Shared React components including:
|
||||
- `Navbar` - Navigation bar component
|
||||
- `Footer` - Footer component
|
||||
- `Logo` - Logo component
|
||||
- UI components (Button, Dialog, etc.)
|
||||
|
||||
### @archgw/shared-styles
|
||||
|
||||
Shared CSS styles including:
|
||||
- Tailwind CSS configuration
|
||||
- Font definitions (IBM Plex Sans, JetBrains Mono)
|
||||
- CSS variables for theming
|
||||
|
||||
### @archgw/tailwind-config
|
||||
|
||||
Shared Tailwind CSS configuration.
|
||||
|
||||
### @archgw/tsconfig
|
||||
|
||||
Shared TypeScript configurations:
|
||||
- `base.json` - Base TypeScript config
|
||||
- `nextjs.json` - Next.js specific config
|
||||
|
||||
## Design System
|
||||
|
||||
Both apps share the same design system:
|
||||
- Same fonts (IBM Plex Sans, JetBrains Mono)
|
||||
- Same color palette
|
||||
- Same components (Navbar, Footer)
|
||||
- Same Tailwind configuration
|
||||
|
||||
## Notes
|
||||
|
||||
- Fonts are stored in each app's `public/fonts/` directory
|
||||
- Both apps use the same shared components and styles
|
||||
- The monorepo uses npm workspaces and Turborepo for build orchestration
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ const nextConfig: NextConfig = {
|
|||
"node_modules",
|
||||
"../../node_modules",
|
||||
];
|
||||
|
||||
|
||||
if (!isServer) {
|
||||
config.resolve.fallback = {
|
||||
...config.resolve.fallback,
|
||||
|
|
|
|||
|
|
@ -16,4 +16,4 @@ export default defineConfig({
|
|||
schema: {
|
||||
types: schemaTypes,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ export const runtime = "edge";
|
|||
function loadFont(fileName: string, baseUrl: string) {
|
||||
return fetch(new URL(`/fonts/${fileName}`, baseUrl)).then((res) => {
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to fetch font ${fileName}: ${res.status} ${res.statusText}`);
|
||||
throw new Error(
|
||||
`Failed to fetch font ${fileName}: ${res.status} ${res.statusText}`,
|
||||
);
|
||||
}
|
||||
return res.arrayBuffer();
|
||||
});
|
||||
|
|
@ -63,16 +65,20 @@ export async function GET(
|
|||
// Load fonts with error handling
|
||||
let fontData;
|
||||
try {
|
||||
const [ibmPlexSans, jetbrainsMonoRegular, jetbrainsMonoMedium, jetbrainsMonoBold] =
|
||||
await Promise.all([
|
||||
loadFont("IBMPlexSans-VariableFont_wdth,wght.otf", fontBaseUrl),
|
||||
loadFont("JetBrainsMono-Regular.otf", fontBaseUrl),
|
||||
loadFont("JetBrainsMono-Medium.otf", fontBaseUrl),
|
||||
loadFont("jetbrains-mono-bold.otf", fontBaseUrl),
|
||||
]).catch((error: Error) => {
|
||||
console.error("Error loading fonts:", error);
|
||||
throw new Error(`Failed to load fonts: ${error.message}`);
|
||||
});
|
||||
const [
|
||||
ibmPlexSans,
|
||||
jetbrainsMonoRegular,
|
||||
jetbrainsMonoMedium,
|
||||
jetbrainsMonoBold,
|
||||
] = await Promise.all([
|
||||
loadFont("IBMPlexSans-VariableFont_wdth,wght.otf", fontBaseUrl),
|
||||
loadFont("JetBrainsMono-Regular.otf", fontBaseUrl),
|
||||
loadFont("JetBrainsMono-Medium.otf", fontBaseUrl),
|
||||
loadFont("jetbrains-mono-bold.otf", fontBaseUrl),
|
||||
]).catch((error: Error) => {
|
||||
console.error("Error loading fonts:", error);
|
||||
throw new Error(`Failed to load fonts: ${error.message}`);
|
||||
});
|
||||
|
||||
fontData = {
|
||||
ibmPlexSans,
|
||||
|
|
@ -81,7 +87,8 @@ export async function GET(
|
|||
jetbrainsMonoBold,
|
||||
};
|
||||
} catch (error: unknown) {
|
||||
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "Unknown error";
|
||||
console.error("Font loading error:", errorMessage);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
|
|
@ -97,7 +104,9 @@ export async function GET(
|
|||
const post = await getBlogPost(slug);
|
||||
|
||||
if (!post) {
|
||||
return new Response(JSON.stringify({ error: "Post not found" }), { status: 404 });
|
||||
return new Response(JSON.stringify({ error: "Post not found" }), {
|
||||
status: 404,
|
||||
});
|
||||
}
|
||||
|
||||
// Get author image URL if available
|
||||
|
|
@ -114,8 +123,7 @@ export async function GET(
|
|||
: request.nextUrl.origin);
|
||||
const logoUrl = `${baseUrl}/Logomark.png`;
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
return new ImageResponse(
|
||||
<div
|
||||
style={{
|
||||
background: "linear-gradient(to top right, #ffffff, #dcdfff)",
|
||||
|
|
@ -259,53 +267,53 @@ export async function GET(
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
fonts: [
|
||||
{
|
||||
name: "IBM Plex Sans Regular",
|
||||
data: fontData.ibmPlexSans,
|
||||
style: "normal",
|
||||
weight: 400,
|
||||
},
|
||||
{
|
||||
name: "IBM Plex Sans Medium",
|
||||
data: fontData.ibmPlexSans,
|
||||
style: "normal",
|
||||
weight: 500,
|
||||
},
|
||||
{
|
||||
name: "IBM Plex Sans Bold",
|
||||
data: fontData.ibmPlexSans,
|
||||
style: "normal",
|
||||
weight: 700,
|
||||
},
|
||||
{
|
||||
name: "JetBrains Mono Regular",
|
||||
data: fontData.jetbrainsMonoRegular,
|
||||
style: "normal",
|
||||
weight: 400,
|
||||
},
|
||||
{
|
||||
name: "JetBrains Mono Medium",
|
||||
data: fontData.jetbrainsMonoMedium,
|
||||
style: "normal",
|
||||
weight: 500,
|
||||
},
|
||||
{
|
||||
name: "JetBrains Mono Bold",
|
||||
data: fontData.jetbrainsMonoBold,
|
||||
style: "normal",
|
||||
weight: 600,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
</div>,
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
fonts: [
|
||||
{
|
||||
name: "IBM Plex Sans Regular",
|
||||
data: fontData.ibmPlexSans,
|
||||
style: "normal",
|
||||
weight: 400,
|
||||
},
|
||||
{
|
||||
name: "IBM Plex Sans Medium",
|
||||
data: fontData.ibmPlexSans,
|
||||
style: "normal",
|
||||
weight: 500,
|
||||
},
|
||||
{
|
||||
name: "IBM Plex Sans Bold",
|
||||
data: fontData.ibmPlexSans,
|
||||
style: "normal",
|
||||
weight: 700,
|
||||
},
|
||||
{
|
||||
name: "JetBrains Mono Regular",
|
||||
data: fontData.jetbrainsMonoRegular,
|
||||
style: "normal",
|
||||
weight: 400,
|
||||
},
|
||||
{
|
||||
name: "JetBrains Mono Medium",
|
||||
data: fontData.jetbrainsMonoMedium,
|
||||
style: "normal",
|
||||
weight: 500,
|
||||
},
|
||||
{
|
||||
name: "JetBrains Mono Bold",
|
||||
data: fontData.jetbrainsMonoBold,
|
||||
style: "normal",
|
||||
weight: 600,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "Unknown error";
|
||||
console.error("Error generating image response:", error);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
|
|
@ -316,4 +324,3 @@ export async function GET(
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export async function generateMetadata({
|
|||
// Get baseUrl - use NEXT_PUBLIC_APP_URL if set, otherwise construct from VERCEL_URL
|
||||
// Restrict to allowed hosts: localhost:3000, archgw-tau.vercel.app, or plano.katanemo.com
|
||||
let baseUrl = "http://localhost:3000";
|
||||
|
||||
|
||||
if (process.env.NEXT_PUBLIC_APP_URL) {
|
||||
const url = process.env.NEXT_PUBLIC_APP_URL;
|
||||
if (
|
||||
|
|
@ -118,4 +118,3 @@ interface LayoutProps {
|
|||
export default async function Layout({ children, params }: LayoutProps) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,4 +33,3 @@ export default function NotFound() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -204,4 +204,3 @@ export default async function BlogPostPage({
|
|||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,4 +117,3 @@ export default async function BlogPage() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,15 @@
|
|||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { ResearchHero, ResearchGrid, ResearchTimeline, ResearchCTA, ResearchCapabilities, ResearchBenchmarks, ResearchFamily } from "@/components/research";
|
||||
import {
|
||||
ResearchHero,
|
||||
ResearchGrid,
|
||||
ResearchTimeline,
|
||||
ResearchCTA,
|
||||
ResearchCapabilities,
|
||||
ResearchBenchmarks,
|
||||
ResearchFamily,
|
||||
} from "@/components/research";
|
||||
import { UnlockPotentialSection } from "@/components/UnlockPotentialSection";
|
||||
|
||||
export default function ResearchPage() {
|
||||
|
|
|
|||
|
|
@ -9,4 +9,3 @@ export default function StudioLayout({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,4 +85,3 @@ export function BlogCard({ post, index = 0 }: BlogCardProps) {
|
|||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,4 +23,3 @@ export function BlogHeader() {
|
|||
</motion.section>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@
|
|||
export function BlogSectionHeader() {
|
||||
return (
|
||||
<h2 className="text-2xl sm:text-3xl lg:text-4xl font-normal leading-tight tracking-tighter text-black mb-12">
|
||||
<span className="font-sans">
|
||||
The latest and greatest from our blog.
|
||||
</span>
|
||||
<span className="font-sans">The latest and greatest from our blog.</span>
|
||||
</h2>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,7 @@
|
|||
import { usePathname } from "next/navigation";
|
||||
import { Navbar, Footer } from "@katanemo/ui";
|
||||
|
||||
export function ConditionalLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export function ConditionalLayout({ children }: { children: React.ReactNode }) {
|
||||
const pathname = usePathname();
|
||||
const isStudio = pathname?.startsWith("/studio");
|
||||
|
||||
|
|
@ -23,4 +19,3 @@ export function ConditionalLayout({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -115,4 +115,3 @@ export function FeaturedBlogCard({ post }: FeaturedBlogCardProps) {
|
|||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,9 +44,8 @@ export function Hero() {
|
|||
{/* Subheading with CTA Buttons */}
|
||||
<div className="max-w-7xl relative z-10">
|
||||
<p className="text-base sm:text-lg md:text-xl lg:text-[22px] font-sans font-[400] tracking-[-1.0px] sm:tracking-[-1.22px]! text-black max-w-76 sm:max-w-2xl mb-6">
|
||||
Build agents faster, and deliver them reliably to production -
|
||||
by offloading the critical plumbing work to Plano.
|
||||
|
||||
Build agents faster, and deliver them reliably to production - by
|
||||
offloading the critical plumbing work to Plano.
|
||||
</p>
|
||||
|
||||
{/* CTA Buttons */}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,9 @@ export function IdeaToAgentSection() {
|
|||
</div>
|
||||
|
||||
<Button asChild className="mt-6 sm:mt-8 w-full sm:w-auto">
|
||||
<Link href={carouselData[currentSlide].link}>Learn more</Link>
|
||||
<Link href={carouselData[currentSlide].link}>
|
||||
Learn more
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -20,15 +20,18 @@ export function IntroSection() {
|
|||
{/* Body Text */}
|
||||
<div className="text-white text-sm sm:text-base lg:text-lg max-w-[713px]">
|
||||
<p className="mb-0">
|
||||
Plano is a models-native proxy and dataplane for agents that handles
|
||||
critical plumbing work in AI - agent routing and orchestration, rich agentic
|
||||
traces, guardrail hooks, and smart model routing APIs for LLMs. Use any
|
||||
language, AI framework, and deliver agents to productions quickly with Plano.
|
||||
Plano is a models-native proxy and dataplane for agents that
|
||||
handles critical plumbing work in AI - agent routing and
|
||||
orchestration, rich agentic traces, guardrail hooks, and smart
|
||||
model routing APIs for LLMs. Use any language, AI framework, and
|
||||
deliver agents to productions quickly with Plano.
|
||||
</p>
|
||||
<p className="mb-0 mt-4">
|
||||
Developers can focus more on core product logic of agents.
|
||||
Product teams can accelerate feedback loops for reinforcement learning.
|
||||
Engineering teams can standardize policies and access controls across every agent and LLM for safer, more reliable scaling.
|
||||
Developers can focus more on core product logic of agents.
|
||||
Product teams can accelerate feedback loops for reinforcement
|
||||
learning. Engineering teams can standardize policies and access
|
||||
controls across every agent and LLM for safer, more reliable
|
||||
scaling.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -34,17 +34,17 @@ export function LogoCloud() {
|
|||
const isTMobile = index === 1; // T-Mobile is before HP
|
||||
const isHP = index === 2; // HP is in center
|
||||
const isSanDisk = index === 3; // SanDisk is after HP
|
||||
|
||||
|
||||
// Custom spacing for logos around HP on large screens
|
||||
let spacingClass = 'lg:mx-6 xl:mx-8'; // Default spacing
|
||||
let spacingClass = "lg:mx-6 xl:mx-8"; // Default spacing
|
||||
if (isTMobile) {
|
||||
spacingClass = 'lg:mr-3 xl:mr-4 lg:ml-6 xl:ml-8'; // Smaller gap to HP
|
||||
spacingClass = "lg:mr-3 xl:mr-4 lg:ml-6 xl:ml-8"; // Smaller gap to HP
|
||||
} else if (isHP) {
|
||||
spacingClass = 'lg:mx-3 xl:mx-4'; // Smaller gaps on both sides
|
||||
spacingClass = "lg:mx-3 xl:mx-4"; // Smaller gaps on both sides
|
||||
} else if (isSanDisk) {
|
||||
spacingClass = 'lg:ml-3 xl:ml-4 lg:mr-6 xl:mr-8'; // Smaller gap from HP
|
||||
spacingClass = "lg:ml-3 xl:ml-4 lg:mr-6 xl:mr-8"; // Smaller gap from HP
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
key={logo.name}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const squares = [
|
|||
{ x: 3, y: 15, color: "#ABB2FA", col: 0, row: 4 },
|
||||
{ x: 3, y: 18, color: "#ABB2FA", col: 0, row: 5 },
|
||||
{ x: 3, y: 21, color: "#969FF4", col: 0, row: 6 },
|
||||
|
||||
|
||||
// Column 2 (x=6)
|
||||
{ x: 6, y: 3, color: "#B0B7FF", col: 1, row: 0 },
|
||||
{ x: 6, y: 6, color: "#B0B7FF", col: 1, row: 1 },
|
||||
|
|
@ -22,7 +22,7 @@ const squares = [
|
|||
{ x: 6, y: 15, color: "#ABB2FA", col: 1, row: 4 },
|
||||
{ x: 6, y: 18, color: "#969FF4", col: 1, row: 5 },
|
||||
{ x: 6, y: 21, color: "#969FF4", col: 1, row: 6 },
|
||||
|
||||
|
||||
// Column 3 (x=9)
|
||||
{ x: 9, y: 3, color: "#B0B7FF", col: 2, row: 0 },
|
||||
{ x: 9, y: 6, color: "#ABB2FA", col: 2, row: 1 },
|
||||
|
|
@ -31,7 +31,7 @@ const squares = [
|
|||
{ x: 9, y: 15, color: "#969FF4", col: 2, row: 4 },
|
||||
{ x: 9, y: 18, color: "#969FF4", col: 2, row: 5 },
|
||||
{ x: 9, y: 21, color: "#969FF4", col: 2, row: 6 },
|
||||
|
||||
|
||||
// Column 4 (x=12)
|
||||
{ x: 12, y: 3, color: "#ABB2FA", col: 3, row: 0 },
|
||||
{ x: 12, y: 6, color: "#ABB2FA", col: 3, row: 1 },
|
||||
|
|
@ -40,7 +40,7 @@ const squares = [
|
|||
{ x: 12, y: 15, color: "#969FF4", col: 3, row: 4 },
|
||||
{ x: 12, y: 18, color: "#969FF4", col: 3, row: 5 },
|
||||
{ x: 12, y: 21, color: "#969FF4", col: 3, row: 6 },
|
||||
|
||||
|
||||
// Column 5 (x=15)
|
||||
{ x: 15, y: 3, color: "#ABB2FA", col: 4, row: 0 },
|
||||
{ x: 15, y: 6, color: "#ABB2FA", col: 4, row: 1 },
|
||||
|
|
@ -49,7 +49,7 @@ const squares = [
|
|||
{ x: 15, y: 15, color: "#969FF4", col: 4, row: 4 },
|
||||
{ x: 15, y: 18, color: "#969FF4", col: 4, row: 5 },
|
||||
{ x: 15, y: 21, color: "#969FF4", col: 4, row: 6 },
|
||||
|
||||
|
||||
// Column 6 (x=18)
|
||||
{ x: 18, y: 3, color: "#ABB2FA", col: 5, row: 0 },
|
||||
{ x: 18, y: 6, color: "#969FF4", col: 5, row: 1 },
|
||||
|
|
@ -58,7 +58,7 @@ const squares = [
|
|||
{ x: 18, y: 15, color: "#969FF4", col: 5, row: 4 },
|
||||
{ x: 18, y: 18, color: "#969FF4", col: 5, row: 5 },
|
||||
{ x: 18, y: 21, color: "#969FF4", col: 5, row: 6 },
|
||||
|
||||
|
||||
// Column 7 (x=21)
|
||||
{ x: 21, y: 3, color: "#969FF4", col: 6, row: 0 },
|
||||
{ x: 21, y: 6, color: "#969FF4", col: 6, row: 1 },
|
||||
|
|
@ -92,7 +92,7 @@ function getDeterministicValues(index: number) {
|
|||
const seed4 = index * 0.4;
|
||||
const seed5 = index * 0.5;
|
||||
const seed6 = index * 0.6;
|
||||
|
||||
|
||||
return {
|
||||
duration: roundToPrecision(3 + seededRandom(seed1) * 3, 10), // 3-6 seconds
|
||||
peakOpacity: roundToPrecision(0.7 + seededRandom(seed2) * 0.3, 10),
|
||||
|
|
@ -105,9 +105,9 @@ function getDeterministicValues(index: number) {
|
|||
|
||||
export function NetworkAnimation({ className }: NetworkAnimationProps) {
|
||||
// Generate unique IDs for gradient and mask to avoid conflicts when multiple instances exist
|
||||
const gradientId = useId().replace(/:/g, '-');
|
||||
const maskId = useId().replace(/:/g, '-');
|
||||
|
||||
const gradientId = useId().replace(/:/g, "-");
|
||||
const maskId = useId().replace(/:/g, "-");
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 pointer-events-none opacity-100">
|
||||
<motion.div
|
||||
|
|
@ -143,55 +143,61 @@ export function NetworkAnimation({ className }: NetworkAnimationProps) {
|
|||
<rect width="26" height="26" fill={`url(#${gradientId})`} />
|
||||
</mask>
|
||||
</defs>
|
||||
|
||||
|
||||
<g mask={`url(#${maskId})`}>
|
||||
{/* Outer border */}
|
||||
<rect width="26" height="26" fill="#7780D9" />
|
||||
|
||||
|
||||
{/* Inner background */}
|
||||
<rect x="2" y="2" width="22" height="22" fill="#B9BFFF" />
|
||||
|
||||
{/* Animated squares with wave effect */}
|
||||
{squares.map((square, index) => {
|
||||
// Use deterministic values based on index for SSR/client consistency
|
||||
const { duration, peakOpacity, baseOpacity, midOpacity, baseBrightness, peakBrightness } =
|
||||
getDeterministicValues(index);
|
||||
|
||||
return (
|
||||
<motion.path
|
||||
key={`square-${index}`}
|
||||
d={`M${square.x} ${square.y}H${square.x + 2}V${square.y + 2}H${square.x}V${square.y}Z`}
|
||||
fill={square.color}
|
||||
initial={{
|
||||
opacity: roundToPrecision(baseOpacity, 10),
|
||||
filter: `brightness(${roundToPrecision(baseBrightness, 10)})`,
|
||||
}}
|
||||
animate={{
|
||||
opacity: [
|
||||
roundToPrecision(baseOpacity, 10),
|
||||
roundToPrecision(midOpacity, 10),
|
||||
roundToPrecision(peakOpacity, 10),
|
||||
roundToPrecision(midOpacity, 10),
|
||||
roundToPrecision(baseOpacity, 10),
|
||||
],
|
||||
filter: [
|
||||
`brightness(${roundToPrecision(baseBrightness, 10)})`,
|
||||
`brightness(${roundToPrecision((baseBrightness + peakBrightness) / 2, 10)})`,
|
||||
`brightness(${roundToPrecision(peakBrightness, 10)})`,
|
||||
`brightness(${roundToPrecision((baseBrightness + peakBrightness) / 2, 10)})`,
|
||||
`brightness(${roundToPrecision(baseBrightness, 10)})`,
|
||||
],
|
||||
}}
|
||||
transition={{
|
||||
duration: roundToPrecision(duration, 10),
|
||||
delay: 0, // No delay - instant start
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
repeatDelay: 0, // No pause between cycles
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Animated squares with wave effect */}
|
||||
{squares.map((square, index) => {
|
||||
// Use deterministic values based on index for SSR/client consistency
|
||||
const {
|
||||
duration,
|
||||
peakOpacity,
|
||||
baseOpacity,
|
||||
midOpacity,
|
||||
baseBrightness,
|
||||
peakBrightness,
|
||||
} = getDeterministicValues(index);
|
||||
|
||||
return (
|
||||
<motion.path
|
||||
key={`square-${index}`}
|
||||
d={`M${square.x} ${square.y}H${square.x + 2}V${square.y + 2}H${square.x}V${square.y}Z`}
|
||||
fill={square.color}
|
||||
initial={{
|
||||
opacity: roundToPrecision(baseOpacity, 10),
|
||||
filter: `brightness(${roundToPrecision(baseBrightness, 10)})`,
|
||||
}}
|
||||
animate={{
|
||||
opacity: [
|
||||
roundToPrecision(baseOpacity, 10),
|
||||
roundToPrecision(midOpacity, 10),
|
||||
roundToPrecision(peakOpacity, 10),
|
||||
roundToPrecision(midOpacity, 10),
|
||||
roundToPrecision(baseOpacity, 10),
|
||||
],
|
||||
filter: [
|
||||
`brightness(${roundToPrecision(baseBrightness, 10)})`,
|
||||
`brightness(${roundToPrecision((baseBrightness + peakBrightness) / 2, 10)})`,
|
||||
`brightness(${roundToPrecision(peakBrightness, 10)})`,
|
||||
`brightness(${roundToPrecision((baseBrightness + peakBrightness) / 2, 10)})`,
|
||||
`brightness(${roundToPrecision(baseBrightness, 10)})`,
|
||||
],
|
||||
}}
|
||||
transition={{
|
||||
duration: roundToPrecision(duration, 10),
|
||||
delay: 0, // No delay - instant start
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
repeatDelay: 0, // No pause between cycles
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</g>
|
||||
</svg>
|
||||
</motion.div>
|
||||
|
|
|
|||
|
|
@ -11,22 +11,22 @@ const components = {
|
|||
types: {
|
||||
image: ({ value }: any) => {
|
||||
if (!value?.asset) return null;
|
||||
|
||||
|
||||
const imageUrl = urlFor(value);
|
||||
const asset = value.asset;
|
||||
|
||||
|
||||
// Get natural dimensions if available from metadata
|
||||
const dimensions = asset.metadata?.dimensions;
|
||||
const width = dimensions?.width || 1000;
|
||||
const height = dimensions?.height || 562;
|
||||
const aspectRatio = dimensions ? height / width : 0.5625; // Default to 16:9 if no dimensions
|
||||
|
||||
|
||||
return (
|
||||
<div className="my-6 lg:my-8">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<div className="relative w-full overflow-hidden rounded-lg bg-black/5">
|
||||
<div
|
||||
className="relative w-full"
|
||||
<div
|
||||
className="relative w-full"
|
||||
style={{ paddingBottom: `${aspectRatio * 100}%` }}
|
||||
>
|
||||
<Image
|
||||
|
|
@ -39,7 +39,9 @@ const components = {
|
|||
</div>
|
||||
</div>
|
||||
{value.alt && (
|
||||
<p className="mt-2 text-sm text-black/60 text-center">{value.alt}</p>
|
||||
<p className="mt-2 text-sm text-black/60 text-center">
|
||||
{value.alt}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -91,12 +93,8 @@ const components = {
|
|||
),
|
||||
},
|
||||
listItem: {
|
||||
bullet: (props: any) => (
|
||||
<li className="ml-4">{props.children}</li>
|
||||
),
|
||||
number: (props: any) => (
|
||||
<li className="ml-4">{props.children}</li>
|
||||
),
|
||||
bullet: (props: any) => <li className="ml-4">{props.children}</li>,
|
||||
number: (props: any) => <li className="ml-4">{props.children}</li>,
|
||||
},
|
||||
marks: {
|
||||
strong: ({ children }: { children: React.ReactNode }) => (
|
||||
|
|
@ -109,7 +107,11 @@ const components = {
|
|||
<a
|
||||
href={props.value?.href || "#"}
|
||||
target={props.value?.href?.startsWith("http") ? "_blank" : undefined}
|
||||
rel={props.value?.href?.startsWith("http") ? "noopener noreferrer" : undefined}
|
||||
rel={
|
||||
props.value?.href?.startsWith("http")
|
||||
? "noopener noreferrer"
|
||||
: undefined
|
||||
}
|
||||
className="text-[var(--secondary)] hover:underline font-medium"
|
||||
>
|
||||
{props.children}
|
||||
|
|
@ -121,4 +123,3 @@ const components = {
|
|||
export function PortableText({ content }: PortableTextProps) {
|
||||
return <SanityPortableText value={content} components={components} />;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -115,7 +115,9 @@ export function UseCasesSection() {
|
|||
What's possible with Plano
|
||||
</h2>
|
||||
<Button asChild className="hidden lg:block">
|
||||
<Link href="https://docs.plano.katanemo.com/getting-started/installation">Start building</Link>
|
||||
<Link href="https://docs.plano.katanemo.com/getting-started/installation">
|
||||
Start building
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -156,7 +158,9 @@ export function UseCasesSection() {
|
|||
{/* Start building button - Mobile only, appears last */}
|
||||
<div className="lg:hidden mt-8">
|
||||
<Button asChild className="w-full">
|
||||
<Link href="https://docs.plano.katanemo.com/getting-started/installation">Start building</Link>
|
||||
<Link href="https://docs.plano.katanemo.com/getting-started/installation">
|
||||
Start building
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -284,7 +288,10 @@ export function UseCasesSection() {
|
|||
</div>
|
||||
<div className="flex flex-col sm:flex-row gap-3 w-full sm:w-auto order-1">
|
||||
<Button asChild className="w-full sm:w-auto">
|
||||
<Link href="https://docs.plano.katanemo.com/getting-started/installation" className="flex items-center gap-2">
|
||||
<Link
|
||||
href="https://docs.plano.katanemo.com/getting-started/installation"
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
Start building
|
||||
<ArrowRightIcon className="w-4 h-4" />
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ const verticalCarouselData = [
|
|||
category: "PROGRAMMABLE ARCHITECTURE",
|
||||
title: "",
|
||||
description: [
|
||||
"As agent workloads move beyond prototypes, teams end up scattering critical logic across apps: compliance checks, context \"patches,\" provider-specific quirks, etc. That glue code gets duplicated across agents, is hard to audit, and slows iteration because every policy or workflow change requires touching application code and redeploying.",
|
||||
'As agent workloads move beyond prototypes, teams end up scattering critical logic across apps: compliance checks, context "patches," provider-specific quirks, etc. That glue code gets duplicated across agents, is hard to audit, and slows iteration because every policy or workflow change requires touching application code and redeploying.',
|
||||
"Plano keeps that logic in one place with a programmable Agent Filter Chain—hooks that can inspect, mutate, or terminate prompt traffic early, turning common steps (policy enforcement, jailbreak checks, context engineering, tool gating, routing hints) into reusable building blocks.",
|
||||
],
|
||||
diagram: "/PromptRouting.svg",
|
||||
|
|
@ -180,11 +180,23 @@ export function VerticalCarouselSection() {
|
|||
|
||||
{/* Description */}
|
||||
<div className="text-white text-sm sm:text-base lg:text-lg max-w-full lg:max-w-md -mt-0.5">
|
||||
{verticalCarouselData[activeSlide].description.map((paragraph, index) => (
|
||||
<p key={index} className={index < verticalCarouselData[activeSlide].description.length - 1 ? "mb-4" : "mb-0"}>
|
||||
{paragraph}
|
||||
</p>
|
||||
))}
|
||||
{verticalCarouselData[activeSlide].description.map(
|
||||
(paragraph, index) => (
|
||||
<p
|
||||
key={index}
|
||||
className={
|
||||
index <
|
||||
verticalCarouselData[activeSlide].description
|
||||
.length -
|
||||
1
|
||||
? "mb-4"
|
||||
: "mb-0"
|
||||
}
|
||||
>
|
||||
{paragraph}
|
||||
</p>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ export function ResearchBenchmarks() {
|
|||
|
||||
{/* Title */}
|
||||
<h2 className="text-4xl sm:text-4xl md:text-5xl lg:text-4xl font-medium leading-tight tracking-[-0.06em]! text-white">
|
||||
<span className="font-sans">Production excellence, outperforming frontier LLMs</span>
|
||||
<span className="font-sans">
|
||||
Production excellence, outperforming frontier LLMs
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -9,26 +9,34 @@ export function ResearchCTA() {
|
|||
<div className="max-w-4xl">
|
||||
{/* Main Heading */}
|
||||
<h1 className="text-4xl sm:text-4xl md:text-5xl lg:text-5xl font-medium leading-tight tracking-[-0.06em]! text-white -ml-1 mb-3 mt-4">
|
||||
<span className="font-sans">Meet Plano-Orchestrator. Our latest models.</span>
|
||||
<span className="font-sans">
|
||||
Meet Plano-Orchestrator. Our latest models.
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{/* Description with CTA Buttons */}
|
||||
<div className="max-w-5xl">
|
||||
<p className="leading-relaxed sm:text-lg md:text-lg lg:text-[18px] font-sans font-normal text-white/90 mb-6">
|
||||
Plano-Orchestrator is a family of state-of-the-art routing and orchestration models
|
||||
that decides which agent(s) or LLM(s) should handle each request, and in what sequence.
|
||||
Built for multi-agent orchestration systems, Plano-Orchestrator excels at analyzing
|
||||
user intent and conversation context to make precise routing and orchestration decisions.
|
||||
Plano-Orchestrator is a family of state-of-the-art routing and
|
||||
orchestration models that decides which agent(s) or LLM(s) should
|
||||
handle each request, and in what sequence. Built for multi-agent
|
||||
orchestration systems, Plano-Orchestrator excels at analyzing user
|
||||
intent and conversation context to make precise routing and
|
||||
orchestration decisions.
|
||||
</p>
|
||||
|
||||
{/* CTA Buttons */}
|
||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-start gap-3 sm:gap-4">
|
||||
<Button asChild className="w-full sm:w-auto">
|
||||
<Link href="https://huggingface.co/katanemo">Download Plano models</Link>
|
||||
<Link href="https://huggingface.co/katanemo">
|
||||
Download Plano models
|
||||
</Link>
|
||||
</Button>
|
||||
<Button variant="secondary" asChild className="w-full sm:w-auto">
|
||||
<Link href="https://docs.plano.katanemo.com">Get Started with Plano</Link>
|
||||
<Link href="https://docs.plano.katanemo.com">
|
||||
Get Started with Plano
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -52,14 +52,16 @@ export function ResearchCapabilities() {
|
|||
|
||||
{/* Title */}
|
||||
<h2 className="text-4xl sm:text-4xl md:text-5xl lg:text-4xl font-medium leading-tight tracking-[-0.06em]! text-white mb-4">
|
||||
<span className="font-sans">Accurately route with confidence with no compromise</span>
|
||||
<span className="font-sans">
|
||||
Accurately route with confidence with no compromise
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
<p className="text-white/90 w-full sm:w-[75%] text-sm sm:text-base leading-relaxed">
|
||||
Designed for real-world deployments, it delivers strong performance across general
|
||||
conversations, coding tasks, and long-context multi-turn conversations, while remaining
|
||||
efficient enough for low-latency production environments.
|
||||
|
||||
<p className="text-white/90 w-full sm:w-[75%] text-sm sm:text-base leading-relaxed">
|
||||
Designed for real-world deployments, it delivers strong performance
|
||||
across general conversations, coding tasks, and long-context
|
||||
multi-turn conversations, while remaining efficient enough for
|
||||
low-latency production environments.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -67,15 +69,18 @@ export function ResearchCapabilities() {
|
|||
<div className="lg:hidden grid grid-cols-1 gap-8">
|
||||
{capabilitiesData.map((capability) => {
|
||||
// Map each capability to its icon
|
||||
const iconMap: Record<number, React.ComponentType<{ className?: string }>> = {
|
||||
const iconMap: Record<
|
||||
number,
|
||||
React.ComponentType<{ className?: string }>
|
||||
> = {
|
||||
1: MessagesSquare, // Multi-turn Understanding
|
||||
2: GitFork, // Multi-Intent Detection
|
||||
3: Route, // Content-Dependency Routing
|
||||
4: RefreshCw, // Conversational-Flow Handling
|
||||
};
|
||||
|
||||
|
||||
const Icon = iconMap[capability.id];
|
||||
|
||||
|
||||
return (
|
||||
<div key={capability.id} className="flex flex-col">
|
||||
{/* Icon Card */}
|
||||
|
|
@ -84,9 +89,7 @@ export function ResearchCapabilities() {
|
|||
transition={{ duration: 0.2 }}
|
||||
className="bg-gradient-to-b from-[rgba(177,184,255,0.16)] to-[rgba(17,28,132,0.035)] border-2 border-[rgba(171,178,250,0.27)] rounded-md p-6 h-40 flex items-center justify-center mb-4"
|
||||
>
|
||||
{Icon && (
|
||||
<Icon className="w-24 h-24 text-[#9797ea]" />
|
||||
)}
|
||||
{Icon && <Icon className="w-24 h-24 text-[#9797ea]" />}
|
||||
</motion.div>
|
||||
|
||||
{/* Title */}
|
||||
|
|
@ -107,15 +110,18 @@ export function ResearchCapabilities() {
|
|||
<div className="hidden lg:grid lg:grid-cols-4 gap-6 mb-6">
|
||||
{capabilitiesData.map((capability) => {
|
||||
// Map each capability to its icon
|
||||
const iconMap: Record<number, React.ComponentType<{ className?: string }>> = {
|
||||
const iconMap: Record<
|
||||
number,
|
||||
React.ComponentType<{ className?: string }>
|
||||
> = {
|
||||
1: MessagesSquare, // Multi-turn Understanding
|
||||
2: GitFork, // Multi-Intent Detection
|
||||
3: Route, // Content-Dependency Routing
|
||||
4: RefreshCw, // Conversational-Flow Handling
|
||||
};
|
||||
|
||||
|
||||
const Icon = iconMap[capability.id];
|
||||
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={capability.id}
|
||||
|
|
@ -123,9 +129,7 @@ export function ResearchCapabilities() {
|
|||
transition={{ duration: 0.2 }}
|
||||
className="bg-gradient-to-b from-[rgba(177,184,255,0.16)] to-[rgba(17,28,132,0.035)] border-2 border-[rgba(171,178,250,0.27)] rounded-md p-6 h-52 flex items-center justify-center"
|
||||
>
|
||||
{Icon && (
|
||||
<Icon className="w-24 h-24 text-[#9797ea]" />
|
||||
)}
|
||||
{Icon && <Icon className="w-24 h-24 text-[#9797ea]" />}
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,7 @@ export function ResearchHero() {
|
|||
—
|
||||
</span>
|
||||
<span className="text-xs sm:text-sm font-[600] tracking-[-0.6px]! text-black leading-tight">
|
||||
<span className="">
|
||||
Plano Orchestrator models released
|
||||
</span>
|
||||
<span className="">Plano Orchestrator models released</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -40,16 +38,19 @@ export function ResearchHero() {
|
|||
{/* Description */}
|
||||
<div className="max-w-70 sm:max-w-2xl relative z-10">
|
||||
<p className="text-base sm:text-lg md:text-xl lg:text-[22px] font-sans font-normal tracking-[-1.0px] sm:tracking-[-1.22px]! text-black">
|
||||
Our applied research focuses on how to deliver agents safely, efficiently,
|
||||
and with improved real-world performance — critical for any AI application,
|
||||
but work that sits outside of any agent's core product logic.
|
||||
Our applied research focuses on how to deliver agents safely,
|
||||
efficiently, and with improved real-world performance — critical for
|
||||
any AI application, but work that sits outside of any agent's core
|
||||
product logic.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-start gap-3 sm:gap-4 mt-6 sm:mt-8 relative z-10">
|
||||
<Button asChild className="w-full sm:w-auto">
|
||||
<Link href="https://huggingface.co/katanemo">Available on Hugging Face</Link>
|
||||
</Button>
|
||||
<Button asChild className="w-full sm:w-auto">
|
||||
<Link href="https://huggingface.co/katanemo">
|
||||
Available on Hugging Face
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue