feat(www): introduce monorepo

This change brings Turborepo monorepo to independently handle the marketing website, the docs website and any other future use cases for mutli-platform support. They are using internal @katanemo package handlers for the design system and logic.
This commit is contained in:
Musa 2025-11-23 13:23:51 -08:00
parent 151cefd528
commit 0d9147456f
102 changed files with 3876 additions and 52 deletions

13
.gitignore vendored
View file

@ -139,3 +139,16 @@ crates/target/
build.log
archgw.log
# Next.js / Turborepo
.next/
out/
.turbo
*.tsbuildinfo
next-env.d.ts
apps/*/node_modules/
packages/*/node_modules/
apps/*/.next/
apps/*/out/
apps/*/dist/
./node_modules

101
README-MONOREPO.md Normal file
View file

@ -0,0 +1,101 @@
# 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

31
apps/docs/biome.json Normal file
View file

@ -0,0 +1,31 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
"useIgnoreFile": false
},
"files": {
"ignoreUnknown": false,
"ignore": []
},
"formatter": {
"enabled": true,
"indentStyle": "space"
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "double"
}
}
}

8
apps/docs/next.config.ts Normal file
View file

@ -0,0 +1,8 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;

34
apps/docs/package.json Normal file
View file

@ -0,0 +1,34 @@
{
"name": "@katanemo/docs",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p 3001",
"build": "next build",
"start": "next start -p 3001",
"lint": "biome check",
"format": "biome format --write",
"typecheck": "tsc --noEmit",
"clean": "rm -rf .next"
},
"dependencies": {
"@katanemo/shared-styles": "*",
"@katanemo/ui": "*",
"next": "16.0.0",
"react": "19.2.0",
"react-dom": "19.2.0"
},
"devDependencies": {
"@katanemo/tailwind-config": "*",
"@katanemo/tsconfig": "*",
"@biomejs/biome": "2.2.0",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"tailwindcss": "^4",
"tw-animate-css": "^1.4.0",
"typescript": "^5"
}
}

View file

@ -0,0 +1,8 @@
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

View file

@ -0,0 +1,3 @@
/* This file is kept for backwards compatibility but styles are imported from @katanemo/shared-styles */
@import "@katanemo/shared-styles/globals.css";

View file

@ -0,0 +1,28 @@
import type { Metadata } from "next";
import "./globals.css";
import "@katanemo/shared-styles/globals.css";
import { Navbar, Footer } from "@katanemo/ui";
export const metadata: Metadata = {
title: "Plano Documentation",
description: "Documentation for Plano - The AI-native network for agents",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className="antialiased">
<div className="min-h-screen">
<Navbar />
<main className="pt-20">{children}</main>
<Footer />
</div>
</body>
</html>
);
}

View file

@ -0,0 +1,17 @@
import React from "react";
export default function DocsPage() {
return (
<section className="px-4 sm:px-6 lg:px-8 py-12 sm:py-16 lg:py-24">
<div className="max-w-[81rem] mx-auto">
<h1 className="text-4xl sm:text-5xl lg:text-7xl font-normal leading-tight tracking-tighter text-black mb-6">
<span className="font-sans">Documentation</span>
</h1>
<p className="text-lg sm:text-xl lg:text-2xl font-sans font-[400] tracking-[-1.2px] text-black/70">
Coming soon...
</p>
</div>
</section>
);
}

20
apps/docs/tsconfig.json Normal file
View file

@ -0,0 +1,20 @@
{
"extends": "@katanemo/tsconfig/nextjs.json",
"compilerOptions": {
"jsx": "react-jsx",
"paths": {
"@/*": ["./src/*"],
"@katanemo/ui": ["../../packages/ui/src"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": ["node_modules"]
}

34
apps/www/package.json Normal file
View file

@ -0,0 +1,34 @@
{
"name": "@katanemo/www",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "biome check",
"format": "biome format --write",
"typecheck": "tsc --noEmit",
"clean": "rm -rf .next"
},
"dependencies": {
"@katanemo/shared-styles": "*",
"@katanemo/ui": "*",
"@vercel/analytics": "^1.5.0",
"next": "16.0.0",
"react": "19.2.0",
"react-dom": "19.2.0"
},
"devDependencies": {
"@katanemo/tailwind-config": "*",
"@katanemo/tsconfig": "*",
"@biomejs/biome": "2.2.0",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"tailwindcss": "^4",
"tw-animate-css": "^1.4.0",
"typescript": "^5"
}
}

View file

Before

Width:  |  Height:  |  Size: 317 KiB

After

Width:  |  Height:  |  Size: 317 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 186 KiB

After

Width:  |  Height:  |  Size: 186 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -0,0 +1,54 @@
<svg width="126" height="48" viewBox="0 0 126 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M33.723 43.0923V17.2768H38.0374V20.99H39.0276L38.0374 22.0156C38.0374 20.436 38.5089 19.1983 39.4519 18.3024C40.3949 17.3829 41.668 16.9232 43.2712 16.9232C45.228 16.9232 46.7958 17.5951 47.9746 18.9389C49.1534 20.2828 49.7427 22.0863 49.7427 24.3496V29.6188C49.7427 31.1276 49.4716 32.4479 48.9294 33.5795C48.4107 34.6876 47.6681 35.5481 46.7015 36.161C45.7349 36.774 44.5914 37.0805 43.2712 37.0805C41.668 37.0805 40.3949 36.6326 39.4519 35.7367C38.5089 34.8172 38.0374 33.5677 38.0374 31.9881L39.0276 33.0137H38.002L38.1435 37.6463V43.0923H33.723ZM41.7152 33.2612C42.8468 33.2612 43.7309 32.9312 44.3675 32.271C45.004 31.6109 45.3223 30.6679 45.3223 29.442V24.5618C45.3223 23.3358 45.004 22.3928 44.3675 21.7327C43.7309 21.0725 42.8468 20.7425 41.7152 20.7425C40.6071 20.7425 39.7348 21.0843 39.0983 21.768C38.4617 22.4282 38.1435 23.3594 38.1435 24.5618V29.442C38.1435 30.6443 38.4617 31.5873 39.0983 32.271C39.7348 32.9312 40.6071 33.2612 41.7152 33.2612ZM62.9549 36.7269C61.6819 36.7269 60.562 36.4675 59.5954 35.9489C58.6524 35.4302 57.9097 34.6994 57.3675 33.7563C56.8252 32.7897 56.5541 31.6817 56.5541 30.4321V14.9075H50.3301V10.9114H60.9746V30.4321C60.9746 31.1394 61.175 31.7052 61.5758 32.1296C62.0001 32.5304 62.5659 32.7308 63.2732 32.7308H69.1436V36.7269H62.9549ZM76.2025 37.0805C74.1986 37.0805 72.619 36.55 71.4638 35.4891C70.3086 34.4282 69.731 33.0019 69.731 31.2101C69.731 29.3005 70.3675 27.827 71.6406 26.7897C72.9137 25.7523 74.7172 25.2337 77.0512 25.2337H81.8961V23.5716C81.8961 22.6285 81.5896 21.8977 80.9766 21.379C80.3636 20.8368 79.5267 20.5657 78.4658 20.5657C77.4992 20.5657 76.6976 20.7779 76.0611 21.2022C75.4245 21.6266 75.0473 22.2042 74.9294 22.935H70.6151C70.8272 21.0961 71.6524 19.6344 73.0905 18.5499C74.5286 17.4655 76.3675 16.9232 78.6072 16.9232C80.9884 16.9232 82.8627 17.5244 84.2301 18.7268C85.621 19.9055 86.3165 21.5087 86.3165 23.5362V36.7269H82.0375V33.332H81.3302L82.0375 32.3771C82.0375 33.8153 81.5071 34.9587 80.4462 35.8074C79.3852 36.6561 77.9707 37.0805 76.2025 37.0805ZM77.6524 33.7563C78.9019 33.7563 79.9157 33.438 80.6937 32.8015C81.4953 32.165 81.8961 31.3398 81.8961 30.326V27.9567H77.122C76.2261 27.9567 75.507 28.216 74.9648 28.7347C74.4225 29.2533 74.1514 29.937 74.1514 30.7858C74.1514 31.7052 74.4579 32.4361 75.0709 32.9783C75.7074 33.497 76.5679 33.7563 77.6524 33.7563ZM88.8489 36.7269V17.2768H93.1633V20.99H94.3656L93.1633 22.0156C93.1633 20.4124 93.623 19.1629 94.5424 18.267C95.4855 17.3711 96.7704 16.9232 98.3971 16.9232C100.307 16.9232 101.827 17.5598 102.959 18.8328C104.114 20.1059 104.692 21.8152 104.692 23.9606V36.7269H100.271V24.4203C100.271 23.2415 99.9649 22.3339 99.3519 21.6973C98.7389 21.0608 97.8784 20.7425 96.7704 20.7425C95.6859 20.7425 94.8254 21.0725 94.1888 21.7327C93.5758 22.3928 93.2694 23.3358 93.2694 24.5618V36.7269H88.8489ZM115.11 37.0451C113.46 37.0451 112.022 36.7387 110.796 36.1257C109.594 35.4891 108.651 34.605 107.967 33.4734C107.307 32.3182 106.977 30.9626 106.977 29.4066V24.5971C106.977 23.0411 107.307 21.6973 107.967 20.5657C108.651 19.4105 109.594 18.5264 110.796 17.9134C112.022 17.2768 113.46 16.9586 115.11 16.9586C116.784 16.9586 118.222 17.2768 119.425 17.9134C120.627 18.5264 121.558 19.4105 122.218 20.5657C122.902 21.6973 123.244 23.0293 123.244 24.5618V29.4066C123.244 30.9626 122.902 32.3182 122.218 33.4734C121.558 34.605 120.627 35.4891 119.425 36.1257C118.222 36.7387 116.784 37.0451 115.11 37.0451ZM115.11 33.1905C116.289 33.1905 117.197 32.8722 117.833 32.2357C118.493 31.5756 118.823 30.6325 118.823 29.4066V24.5971C118.823 23.3476 118.493 22.4046 117.833 21.768C117.197 21.1315 116.289 20.8132 115.11 20.8132C113.955 20.8132 113.047 21.1315 112.387 21.768C111.727 22.4046 111.397 23.3476 111.397 24.5971V29.4066C111.397 30.6325 111.727 31.5756 112.387 32.2357C113.047 32.8722 113.955 33.1905 115.11 33.1905Z" fill="#7780D9" style="fill:#7780D9;fill:color(display-p3 0.4667 0.5020 0.8510);fill-opacity:1;"/>
<rect y="11" width="26" height="26" fill="#7780D9" style="fill:#7780D9;fill:color(display-p3 0.4667 0.5020 0.8510);fill-opacity:1;"/>
<rect x="2" y="13" width="22" height="22" fill="#B9BFFF" style="fill:#B9BFFF;fill:color(display-p3 0.7246 0.7499 1.0000);fill-opacity:1;"/>
<path d="M3 14H5V16H3V14Z" fill="#B0B7FF" style="fill:#B0B7FF;fill:color(display-p3 0.6897 0.7182 1.0000);fill-opacity:1;"/>
<path d="M3 17H5V19H3V17Z" fill="#B0B7FF" style="fill:#B0B7FF;fill:color(display-p3 0.6897 0.7182 1.0000);fill-opacity:1;"/>
<path d="M3 20H5V22H3V20Z" fill="#B0B7FF" style="fill:#B0B7FF;fill:color(display-p3 0.6897 0.7182 1.0000);fill-opacity:1;"/>
<path d="M3 23H5V25H3V23Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M3 26H5V28H3V26Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M3 29H5V31H3V29Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M3 32H5V34H3V32Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M12 14H14V16H12V14Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M12 17H14V19H12V17Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M12 20H14V22H12V20Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M12 23H14V25H12V23Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M12 26H14V28H12V26Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M12 29H14V31H12V29Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M12 32H14V34H12V32Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M6 14H8V16H6V14Z" fill="#B0B7FF" style="fill:#B0B7FF;fill:color(display-p3 0.6897 0.7182 1.0000);fill-opacity:1;"/>
<path d="M6 17H8V19H6V17Z" fill="#B0B7FF" style="fill:#B0B7FF;fill:color(display-p3 0.6897 0.7182 1.0000);fill-opacity:1;"/>
<path d="M6 20H8V22H6V20Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M6 23H8V25H6V23Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M6 26H8V28H6V26Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M6 29H8V31H6V29Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M6 32H8V34H6V32Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M15 14H17V16H15V14Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M15 17H17V19H15V17Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M15 20H17V22H15V20Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M15 23H17V25H15V23Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M15 26H17V28H15V26Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M15 29H17V31H15V29Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M15 32H17V34H15V32Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M9 14H11V16H9V14Z" fill="#B0B7FF" style="fill:#B0B7FF;fill:color(display-p3 0.6897 0.7182 1.0000);fill-opacity:1;"/>
<path d="M9 17H11V19H9V17Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M9 20H11V22H9V20Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M9 23H11V25H9V23Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M9 26H11V28H9V26Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M9 29H11V31H9V29Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M9 32H11V34H9V32Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M18 14H20V16H18V14Z" fill="#ABB2FA" style="fill:#ABB2FA;fill:color(display-p3 0.6706 0.6990 0.9799);fill-opacity:1;"/>
<path d="M18 17H20V19H18V17Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M18 20H20V22H18V20Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M18 23H20V25H18V23Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M18 26H20V28H18V26Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M18 29H20V31H18V29Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M18 32H20V34H18V32Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M21 14H23V16H21V14Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M21 17H23V19H21V17Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M21 20H23V22H21V20Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M21 23H23V25H21V23Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M21 26H23V28H21V26Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M21 29H23V31H21V29Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
<path d="M21 32H23V34H21V32Z" fill="#969FF4" style="fill:#969FF4;fill:color(display-p3 0.5882 0.6220 0.9566);fill-opacity:1;"/>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View file

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 391 B

After

Width:  |  Height:  |  Size: 391 B

Before After
Before After

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 874 B

After

Width:  |  Height:  |  Size: 874 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 128 B

After

Width:  |  Height:  |  Size: 128 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 385 B

Before After
Before After

View file

@ -0,0 +1,17 @@
import React from "react";
export default function DocsPage() {
return (
<section className="px-4 sm:px-6 lg:px-8 py-12 sm:py-16 lg:py-24">
<div className="max-w-[81rem] mx-auto">
<h1 className="text-4xl sm:text-5xl lg:text-7xl font-normal leading-tight tracking-tighter text-black mb-6">
<span className="font-sans">Documentation</span>
</h1>
<p className="text-lg sm:text-xl lg:text-2xl font-sans font-[400] tracking-[-1.2px] text-black/70">
Coming soon...
</p>
</div>
</section>
);
}

View file

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Before After
Before After

View file

@ -0,0 +1,2 @@
/* This file is kept for backwards compatibility but styles are imported from @katanemo/shared-styles */
@import "@katanemo/shared-styles/globals.css";

View file

@ -1,6 +1,7 @@
import type { Metadata } from "next";
import "./globals.css";
import "@katanemo/shared-styles/globals.css";
import { Analytics } from "@vercel/analytics/next";
import { Navbar, Footer } from "@katanemo/ui";
export const metadata: Metadata = {
title: "Plano - The AI-native network for agents",
@ -15,8 +16,14 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<body className="antialiased">{children}</body>
<Analytics />
<body className="antialiased">
<div className="min-h-screen">
<Navbar />
<main className="pt-20">{children}</main>
<Footer />
</div>
<Analytics />
</body>
</html>
);
}

View file

@ -1,7 +1,6 @@
"use client";
import React from "react";
import { Navbar } from "@/components/Navbar";
import { Hero } from "@/components/Hero";
import { IntroSection } from "@/components/IntroSection";
import { IdeaToAgentSection } from "@/components/IdeaToAgentSection";
@ -9,26 +8,21 @@ import { UseCasesSection } from "@/components/UseCasesSection";
import { VerticalCarouselSection } from "@/components/VerticalCarouselSection";
import { HowItWorksSection } from "@/components/HowItWorksSection";
import { UnlockPotentialSection } from "@/components/UnlockPotentialSection";
import { Footer } from "@/components/Footer";
import { LogoCloud } from "@/components/LogoCloud";
export default function Home() {
return (
<div className="min-h-screen">
<Navbar />
<main className="pt-20">
<Hero />
<LogoCloud />
<IntroSection />
<IdeaToAgentSection />
<UseCasesSection />
<VerticalCarouselSection />
<HowItWorksSection />
<UnlockPotentialSection variant="transparent" />
<>
<Hero />
<LogoCloud />
<IntroSection />
<IdeaToAgentSection />
<UseCasesSection />
<VerticalCarouselSection />
<HowItWorksSection />
<UnlockPotentialSection variant="transparent" />
{/* Rest of the sections will be refactored next */}
</main>
<Footer />
</div>
{/* Rest of the sections will be refactored next */}
</>
);
}

19
apps/www/tsconfig.json Normal file
View file

@ -0,0 +1,19 @@
{
"extends": "@katanemo/tsconfig/nextjs.json",
"compilerOptions": {
"jsx": "react-jsx",
"paths": {
"@/*": ["./src/*"],
"@katanemo/ui": ["../../packages/ui/src"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": ["node_modules"]
}

2661
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

25
package.json Normal file
View file

@ -0,0 +1,25 @@
{
"name": "archgw-monorepo",
"version": "0.1.0",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
],
"scripts": {
"dev": "turbo run dev",
"build": "turbo run build",
"lint": "turbo run lint",
"format": "turbo run format",
"typecheck": "turbo run typecheck",
"clean": "turbo run clean && rm -rf node_modules"
},
"devDependencies": {
"turbo": "^2.0.0"
},
"engines": {
"node": ">=18.0.0"
},
"packageManager": "npm@10.0.0"
}

View file

@ -1,4 +1,8 @@
@import "tailwindcss";
@source "../../apps/*/src/**/*.{js,ts,jsx,tsx,mdx}";
@source "../../packages/ui/src/**/*.{js,ts,jsx,tsx,mdx}";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
@ -163,6 +167,7 @@
/* Global letter-spacing removed - use Tailwind tracking utilities (tracking-tight, tracking-[-1.92px], etc.) instead */
/* Apply custom font to body by default */
body {
font-family: var(--font-sans);
}
@ -211,8 +216,10 @@ body {
}
body {
@apply text-foreground;
font-family: var(--font-sans);
background: linear-gradient(to top right, #ffffff, #dcdfff);
min-height: 100vh;
overflow-x: hidden;
}
}

View file

@ -0,0 +1,10 @@
{
"name": "@katanemo/shared-styles",
"version": "0.1.0",
"private": true,
"main": "./globals.css",
"files": [
"globals.css"
]
}

View file

@ -0,0 +1,13 @@
{
"name": "@katanemo/tailwind-config",
"version": "0.1.0",
"private": true,
"main": "index.js",
"files": [
"tailwind.config.ts"
],
"devDependencies": {
"tailwindcss": "^4"
}
}

View file

@ -0,0 +1,16 @@
import type { Config } from "tailwindcss";
const config: Config = {
content: [
"./src/**/*.{js,ts,jsx,tsx,mdx}",
"../../apps/*/src/**/*.{js,ts,jsx,tsx,mdx}",
"../../packages/ui/src/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {},
},
plugins: [],
};
export default config;

View file

@ -1,4 +1,5 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
@ -12,23 +13,8 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
"incremental": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": ["node_modules"]
}

View file

@ -0,0 +1,24 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"],
"@katanemo/ui": ["../../packages/ui/src"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
]
}

View file

@ -0,0 +1,10 @@
{
"name": "@katanemo/tsconfig",
"version": "0.1.0",
"private": true,
"files": [
"base.json",
"nextjs.json"
]
}

View file

@ -1,19 +1,16 @@
{
"name": "website",
"name": "@katanemo/ui",
"version": "0.1.0",
"private": true,
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "biome check",
"format": "biome format --write",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-slot": "^1.2.3",
"@vercel/analytics": "^1.5.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.23.24",
@ -25,12 +22,15 @@
},
"devDependencies": {
"@biomejs/biome": "2.2.0",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"tailwindcss": "^4",
"tw-animate-css": "^1.4.0",
"typescript": "^5"
},
"peerDependencies": {
"next": "^16.0.0",
"react": "^19.2.0",
"react-dom": "^19.2.0"
}
}

View file

@ -0,0 +1,101 @@
import React from "react";
import Link from "next/link";
import Image from "next/image";
const footerLinks = {
company: [
{ label: "Product", href: "/product" },
{ label: "Use Cases", href: "/use-cases" },
{ label: "Blog", href: "/blog" },
{ label: "Plano LLMs", href: "/llms" },
],
developerResources: [{ label: "Documentation", href: "/docs" }],
};
export function Footer() {
return (
<footer
className="relative overflow-hidden pt-20 px-6 lg:px-[102px] pb-48"
style={{ background: "linear-gradient(to top right, #ffffff, #dcdfff)" }}
>
<div className="max-w-[81rem] mx-auto relative z-10">
{/* Main Grid Layout */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-20">
{/* Left Column - Tagline and Copyright */}
<div className="flex flex-col">
<p className="font-sans font-normal text-lg sm:text-xl lg:text-2xl text-black tracking-[-1.2px] sm:tracking-[-1.5px] lg:tracking-[-1.7px]! leading-7 mb-6 sm:mb-8">
Plano is the powerful, intelligent platform that empowers teams to
seamlessly build, automate, and scale agentic systems with ease.
</p>
{/* Copyright */}
<div className="mt-auto">
<p className="font-sans text-sm sm:text-base text-black/63 tracking-[-0.6px] sm:tracking-[-0.8px]!">
© Katanemo Labs, Inc. 2025 / Plano by Katanemo Labs, Inc.
</p>
</div>
</div>
{/* Right Column - Navigation Links */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-8">
{/* Company Links */}
<div>
<h3 className="font-sans font-normal text-xl sm:text-2xl lg:text-3xl text-black tracking-[-1.2px] sm:tracking-[-1.4px] lg:tracking-[-1.6px]! mb-4 sm:mb-6">
Company
</h3>
<nav className="space-y-3 sm:space-y-4">
{footerLinks.company.map((link) => (
<Link
key={link.href}
href={link.href}
className="block font-sans font-normal text-sm sm:text-base lg:text-lg text-black tracking-[-0.8px] sm:tracking-[-0.9px] lg:tracking-[-1px]! hover:text-[var(--primary)] transition-colors"
>
{link.label}
</Link>
))}
</nav>
</div>
{/* Developer Resources */}
<div>
<h3 className="font-sans font-normal text-xl sm:text-2xl lg:text-3xl text-black tracking-[-1.2px] sm:tracking-[-1.4px] lg:tracking-[-1.6px]! mb-4 sm:mb-6">
Developer Resources
</h3>
<nav className="space-y-3 sm:space-y-4">
{footerLinks.developerResources.map((link) => (
<Link
key={link.href}
href={link.href}
className="block font-sans font-normal text-sm sm:text-base lg:text-lg text-black tracking-[-0.8px] sm:tracking-[-0.9px] lg:tracking-[-1px]! hover:text-[var(--primary)] transition-colors"
>
{link.label}
</Link>
))}
</nav>
</div>
</div>
</div>
</div>
{/* Half-Cut Plano Logo Background */}
<div className="absolute bottom-0 left-0 right-0 overflow-hidden pointer-events-none">
<div className="max-w-[81rem] mx-auto px-6 lg:px-[1px]">
<div className="relative w-full flex justify-start">
<Image
src="/LogoOutline.svg"
alt="Plano Logo"
width={1800}
height={200}
className="w-150 h-auto opacity-30 select-none"
style={{
transform: "translateY(0%)", // Push logo down more while showing top part
transformOrigin: "center bottom",
}}
/>
</div>
</div>
</div>
</footer>
);
}

View file

@ -0,0 +1,18 @@
import React from "react";
import Image from "next/image";
export function Logo() {
return (
<div className="flex items-center">
{/* LogoMarkSquare SVG */}
<Image
src="/Logomark.svg"
alt="Plano Logo"
width={90}
height={20}
className="flex-shrink-0"
/>
</div>
);
}

View file

@ -0,0 +1,291 @@
"use client";
import React, { useState, useEffect } from "react";
import Link from "next/link";
import { Logo } from "./Logo";
import { Button } from "./ui/button";
import { cn } from "../lib/utils";
import { motion, AnimatePresence } from "framer-motion";
import { X, Menu } from "lucide-react";
const navItems = [
{ href: "/start", label: "start locally" },
{ href: "/docs", label: "docs" },
{ href: "/model-research", label: "models research" },
{ href: "/blog", label: "blog" },
{ href: "/why", label: "why plano?" },
];
export function Navbar() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isDarkBackground, setIsDarkBackground] = useState(false);
// Detect background color behind dropdown menu
useEffect(() => {
if (!isMenuOpen) {
setIsDarkBackground(false);
return;
}
const detectBackground = () => {
// Small delay to ensure DOM is ready
setTimeout(() => {
const nav = document.querySelector("nav");
if (!nav) return;
const navRect = nav.getBoundingClientRect();
const dropdownBottom = navRect.bottom;
const checkY = dropdownBottom + 20; // Just below the dropdown
// First, try to find section elements directly
const main = document.querySelector("main");
if (main) {
const sections = main.querySelectorAll("section");
let foundDarkSection = false;
sections.forEach((section) => {
const rect = section.getBoundingClientRect();
// Check if this section is visible below the navbar
if (rect.top <= checkY && rect.bottom > checkY) {
// Check for dark background classes
const classList = Array.from(section.classList);
const hasDarkBg = classList.some(
(cls) =>
cls.includes("bg-[#1a1a1a]") ||
cls.includes("bg-black") ||
cls.includes("bg-gray-900") ||
cls.includes("bg-neutral-900") ||
cls.includes("dark"),
);
if (hasDarkBg) {
foundDarkSection = true;
setIsDarkBackground(true);
return;
}
// Also check computed background
const computed = window.getComputedStyle(section);
const bg = computed.backgroundColor;
if (bg && bg !== "rgba(0, 0, 0, 0)" && bg !== "transparent") {
const rgbMatch = bg.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
if (rgbMatch) {
const r = parseInt(rgbMatch[1]);
const g = parseInt(rgbMatch[2]);
const b = parseInt(rgbMatch[3]);
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
setIsDarkBackground(luminance < 0.5);
foundDarkSection = true;
return;
}
}
}
});
if (foundDarkSection) return;
}
// Fallback: Check element at point
const centerX = window.innerWidth / 2;
const elementBelow = document.elementFromPoint(centerX, checkY);
if (elementBelow) {
let current: HTMLElement | null = elementBelow as HTMLElement;
let backgroundColor = "";
// Walk up the DOM tree
let levels = 0;
while (
current &&
!backgroundColor &&
current !== document.body &&
levels < 15
) {
const computed = window.getComputedStyle(current);
const bg = computed.backgroundColor;
if (bg && bg !== "rgba(0, 0, 0, 0)" && bg !== "transparent") {
const rgbaMatch = bg.match(
/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/,
);
if (rgbaMatch) {
const alpha = rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1;
if (alpha > 0.1) {
backgroundColor = bg;
break;
}
} else {
backgroundColor = bg;
break;
}
}
current = current.parentElement;
levels++;
}
if (!backgroundColor) {
const bodyBg = window.getComputedStyle(
document.body,
).backgroundColor;
backgroundColor = bodyBg;
}
if (backgroundColor) {
const rgbMatch = backgroundColor.match(
/rgba?\((\d+),\s*(\d+),\s*(\d+)/,
);
if (rgbMatch) {
const r = parseInt(rgbMatch[1]);
const g = parseInt(rgbMatch[2]);
const b = parseInt(rgbMatch[3]);
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
setIsDarkBackground(luminance < 0.5);
} else {
const darkColors = [
"black",
"#000",
"#000000",
"rgb(0,0,0)",
"rgba(0,0,0",
"#1a1a1a",
];
const isDark = darkColors.some((color) =>
backgroundColor.toLowerCase().includes(color.toLowerCase()),
);
setIsDarkBackground(isDark);
}
}
}
}, 100);
};
// Detect on open and on scroll
detectBackground();
const scrollHandler = () => detectBackground();
const resizeHandler = () => detectBackground();
window.addEventListener("scroll", scrollHandler, { passive: true });
window.addEventListener("resize", resizeHandler);
return () => {
window.removeEventListener("scroll", scrollHandler);
window.removeEventListener("resize", resizeHandler);
};
}, [isMenuOpen]);
// Close menu when route changes
const handleLinkClick = () => {
setIsMenuOpen(false);
};
return (
<nav className="top-0 left-0 right-0 z-50 bg-gradient-to-b from-transparent to-white/5 backdrop-blur border-b border-neutral-200/5">
<div className="max-w-[85rem] mx-auto px-6 lg:px-8">
<div className="flex items-center justify-between h-20">
{/* Logo */}
<Link href="/" className="flex items-center">
<Logo />
</Link>
{/* Navigation Links and CTA - Far Right */}
<div className="hidden md:flex items-center justify-end gap-8">
{navItems.map((item) => (
<Link
key={item.href}
href={item.href}
className={cn(
"text-lg font-medium text-[var(--muted)]",
"hover:text-[var(--primary)] transition-colors",
"font-mono tracking-tighter",
)}
>
{item.label}
</Link>
))}
</div>
{/* Mobile Menu Button */}
<div className="md:hidden">
<button
onClick={(e) => {
e.stopPropagation();
setIsMenuOpen(!isMenuOpen);
}}
className="p-2 rounded-md text-[var(--muted)] hover:text-[var(--primary)] transition-colors"
aria-label="Toggle menu"
aria-expanded={isMenuOpen}
>
<AnimatePresence mode="wait" initial={false}>
{isMenuOpen ? (
<motion.div
key="close"
initial={{ opacity: 0, rotate: -90 }}
animate={{ opacity: 1, rotate: 0 }}
exit={{ opacity: 0, rotate: 90 }}
transition={{ duration: 0.2 }}
>
<X className="h-6 w-6" />
</motion.div>
) : (
<motion.div
key="menu"
initial={{ opacity: 0, rotate: 90 }}
animate={{ opacity: 1, rotate: 0 }}
exit={{ opacity: 0, rotate: -90 }}
transition={{ duration: 0.2 }}
>
<Menu className="h-6 w-6" />
</motion.div>
)}
</AnimatePresence>
</button>
</div>
</div>
</div>
{/* Mobile Dropdown Menu - Outside constrained container for full width */}
<AnimatePresence>
{isMenuOpen && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="md:hidden overflow-hidden bg-[#7580DF]/70"
>
<div className="max-w-[85rem] mx-auto px-6 lg:px-8 py-3">
<div className="flex flex-col gap-0.5">
{navItems.map((item, index) => (
<motion.div
key={item.href}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{
duration: 0.3,
delay: index * 0.05,
ease: [0.16, 1, 0.3, 1],
}}
>
<Link
href={item.href}
onClick={handleLinkClick}
className={cn(
"block px-0 py-1.5 border-b border-dashed transition-colors font-mono tracking-tighter",
"text-sm font-medium",
"text-white",
)}
>
{item.label}
</Link>
</motion.div>
))}
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</nav>
);
}

View file

@ -0,0 +1,66 @@
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "../../lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[7px] font-mono font-medium tracking-[-0.989px] transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-[#7780d9] border-[#4141b2] border-[1.562px] border-solid text-white hover:bg-[#7780d9]/90 text-base leading-[1.102]",
primary:
"bg-[#7780d9] border-[#4141b2] border-[1.562px] border-solid text-white hover:bg-[#7780d9]/90 text-base leading-[1.102]",
destructive:
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-[#edefff] border-[#d1d1d1] border-[1.562px] border-solid text-[#494949] hover:bg-[#edefff]/90 text-base leading-[1.102]",
secondaryDark:
"bg-neutral-600 border-[#d1d1d1]/20 border-[1.562px] border-solid text-white hover:bg-[#1a1a1a]/90 text-base leading-[1.102]",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-5 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-[7px] px-5 has-[>svg]:px-4",
icon: "size-9",
"icon-sm": "size-8",
"icon-lg": "size-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
function Button({
className,
variant,
size,
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean;
}) {
const Comp = asChild ? Slot : "button";
return (
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
);
}
export { Button, buttonVariants };

View file

@ -0,0 +1,144 @@
"use client";
import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react";
import { cn } from "../../lib/utils";
function Dialog({
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
}
function DialogTrigger({
...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
}
function DialogPortal({
...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
}
function DialogClose({
...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
}
function DialogOverlay({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
return (
<DialogPrimitive.Overlay
data-slot="dialog-overlay"
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className,
)}
{...props}
/>
);
}
function DialogContent({
className,
children,
showCloseButton = true,
...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean;
}) {
return (
<DialogPortal data-slot="dialog-portal">
<DialogOverlay />
<DialogPrimitive.Content
data-slot="dialog-content"
className={cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
className,
)}
{...props}
>
{children}
{showCloseButton && (
<DialogPrimitive.Close
data-slot="dialog-close"
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
>
<XIcon />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Content>
</DialogPortal>
);
}
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-header"
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
{...props}
/>
);
}
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-footer"
className={cn(
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className,
)}
{...props}
/>
);
}
function DialogTitle({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
return (
<DialogPrimitive.Title
data-slot="dialog-title"
className={cn("text-lg leading-none font-semibold", className)}
{...props}
/>
);
}
function DialogDescription({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
return (
<DialogPrimitive.Description
data-slot="dialog-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
);
}
export {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogTrigger,
};

23
packages/ui/src/index.ts Normal file
View file

@ -0,0 +1,23 @@
// Components
export { Logo } from "./components/Logo";
export { Footer } from "./components/Footer";
export { Navbar } from "./components/Navbar";
// UI Components
export { Button, buttonVariants } from "./components/ui/button";
export {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogTrigger,
} from "./components/ui/dialog";
// Utils
export { cn } from "./lib/utils";

11
packages/ui/tsconfig.json Normal file
View file

@ -0,0 +1,11 @@
{
"extends": "@katanemo/tsconfig/base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"jsx": "react-jsx"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

Some files were not shown because too many files have changed in this diff Show more